HID: i2c-hid: Remove runtime power management

Runtime power management in i2c-hid brings lots of issues, such as:
- When transitioning from display manager to desktop session, i2c-hid
was closed and opened, so the device was set to SLEEP and ON in a short
period. Vendors confirmed that their devices can't handle fast ON/SLEEP
command because Windows doesn't have this behavior.

- When rebooting, i2c-hid was closed, and the driver core put the device
back to full power before shutdown. This behavior also triggers a quick
SLEEP and ON commands that some devices can't handle, renders an
unusable touchpad after reboot.

- Most importantly, my power meter reports little to none energy saving
when i2c-hid is runtime suspended.

So let's remove runtime power management since there is no actual
benefit.

Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
This commit is contained in:
Kai-Heng Feng 2019-10-16 23:12:24 +08:00 committed by Benjamin Tissoires
parent 16ff7bf6db
commit 67b18dfb8c

View File

@ -26,7 +26,6 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/err.h> #include <linux/err.h>
@ -48,8 +47,6 @@
/* quirks to control the device */ /* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0) #define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1) #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
#define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2)
#define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3)
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4) #define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
/* flags */ /* flags */
@ -172,14 +169,7 @@ static const struct i2c_hid_quirks {
{ USB_VENDOR_ID_WEIDA, HID_ANY_ID, { USB_VENDOR_ID_WEIDA, HID_ANY_ID,
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV }, I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288, { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET | I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
I2C_HID_QUIRK_NO_RUNTIME_PM },
{ I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_4B33,
I2C_HID_QUIRK_DELAY_AFTER_SLEEP },
{ USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001,
I2C_HID_QUIRK_NO_RUNTIME_PM },
{ I2C_VENDOR_ID_GOODIX, I2C_DEVICE_ID_GOODIX_01F0,
I2C_HID_QUIRK_NO_RUNTIME_PM },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID, { USB_VENDOR_ID_ELAN, HID_ANY_ID,
I2C_HID_QUIRK_BOGUS_IRQ }, I2C_HID_QUIRK_BOGUS_IRQ },
{ 0, 0 } { 0, 0 }
@ -397,7 +387,6 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
{ {
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
int ret; int ret;
unsigned long now, delay;
i2c_hid_dbg(ihid, "%s\n", __func__); i2c_hid_dbg(ihid, "%s\n", __func__);
@ -415,22 +404,9 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
goto set_pwr_exit; goto set_pwr_exit;
} }
if (ihid->quirks & I2C_HID_QUIRK_DELAY_AFTER_SLEEP &&
power_state == I2C_HID_PWR_ON) {
now = jiffies;
if (time_after(ihid->sleep_delay, now)) {
delay = jiffies_to_usecs(ihid->sleep_delay - now);
usleep_range(delay, delay + 1);
}
}
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state, ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
0, NULL, 0, NULL, 0); 0, NULL, 0, NULL, 0);
if (ihid->quirks & I2C_HID_QUIRK_DELAY_AFTER_SLEEP &&
power_state == I2C_HID_PWR_SLEEP)
ihid->sleep_delay = jiffies + msecs_to_jiffies(20);
if (ret) if (ret)
dev_err(&client->dev, "failed to change power setting.\n"); dev_err(&client->dev, "failed to change power setting.\n");
@ -791,11 +767,6 @@ static int i2c_hid_open(struct hid_device *hid)
{ {
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
int ret = 0;
ret = pm_runtime_get_sync(&client->dev);
if (ret < 0)
return ret;
set_bit(I2C_HID_STARTED, &ihid->flags); set_bit(I2C_HID_STARTED, &ihid->flags);
return 0; return 0;
@ -807,27 +778,6 @@ static void i2c_hid_close(struct hid_device *hid)
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
clear_bit(I2C_HID_STARTED, &ihid->flags); clear_bit(I2C_HID_STARTED, &ihid->flags);
/* Save some power */
pm_runtime_put(&client->dev);
}
static int i2c_hid_power(struct hid_device *hid, int lvl)
{
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
switch (lvl) {
case PM_HINT_FULLON:
pm_runtime_get_sync(&client->dev);
break;
case PM_HINT_NORMAL:
pm_runtime_put(&client->dev);
break;
}
return 0;
} }
struct hid_ll_driver i2c_hid_ll_driver = { struct hid_ll_driver i2c_hid_ll_driver = {
@ -836,7 +786,6 @@ struct hid_ll_driver i2c_hid_ll_driver = {
.stop = i2c_hid_stop, .stop = i2c_hid_stop,
.open = i2c_hid_open, .open = i2c_hid_open,
.close = i2c_hid_close, .close = i2c_hid_close,
.power = i2c_hid_power,
.output_report = i2c_hid_output_report, .output_report = i2c_hid_output_report,
.raw_request = i2c_hid_raw_request, .raw_request = i2c_hid_raw_request,
}; };
@ -1104,9 +1053,6 @@ static int i2c_hid_probe(struct i2c_client *client,
i2c_hid_acpi_fix_up_power(&client->dev); i2c_hid_acpi_fix_up_power(&client->dev);
pm_runtime_get_noresume(&client->dev);
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
device_enable_async_suspend(&client->dev); device_enable_async_suspend(&client->dev);
/* Make sure there is something at this address */ /* Make sure there is something at this address */
@ -1114,16 +1060,16 @@ static int i2c_hid_probe(struct i2c_client *client,
if (ret < 0) { if (ret < 0) {
dev_dbg(&client->dev, "nothing at this address: %d\n", ret); dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
ret = -ENXIO; ret = -ENXIO;
goto err_pm; goto err_regulator;
} }
ret = i2c_hid_fetch_hid_descriptor(ihid); ret = i2c_hid_fetch_hid_descriptor(ihid);
if (ret < 0) if (ret < 0)
goto err_pm; goto err_regulator;
ret = i2c_hid_init_irq(client); ret = i2c_hid_init_irq(client);
if (ret < 0) if (ret < 0)
goto err_pm; goto err_regulator;
hid = hid_allocate_device(); hid = hid_allocate_device();
if (IS_ERR(hid)) { if (IS_ERR(hid)) {
@ -1154,9 +1100,6 @@ static int i2c_hid_probe(struct i2c_client *client,
goto err_mem_free; goto err_mem_free;
} }
if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
pm_runtime_put(&client->dev);
return 0; return 0;
err_mem_free: err_mem_free:
@ -1165,10 +1108,6 @@ static int i2c_hid_probe(struct i2c_client *client,
err_irq: err_irq:
free_irq(client->irq, ihid); free_irq(client->irq, ihid);
err_pm:
pm_runtime_put_noidle(&client->dev);
pm_runtime_disable(&client->dev);
err_regulator: err_regulator:
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
ihid->pdata.supplies); ihid->pdata.supplies);
@ -1181,12 +1120,6 @@ static int i2c_hid_remove(struct i2c_client *client)
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid; struct hid_device *hid;
if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
pm_runtime_get_sync(&client->dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
hid = ihid->hid; hid = ihid->hid;
hid_destroy_device(hid); hid_destroy_device(hid);
@ -1219,25 +1152,15 @@ static int i2c_hid_suspend(struct device *dev)
int wake_status; int wake_status;
if (hid->driver && hid->driver->suspend) { if (hid->driver && hid->driver->suspend) {
/*
* Wake up the device so that IO issues in
* HID driver's suspend code can succeed.
*/
ret = pm_runtime_resume(dev);
if (ret < 0)
return ret;
ret = hid->driver->suspend(hid, PMSG_SUSPEND); ret = hid->driver->suspend(hid, PMSG_SUSPEND);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
if (!pm_runtime_suspended(dev)) { /* Save some power */
/* Save some power */ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
disable_irq(client->irq); disable_irq(client->irq);
}
if (device_may_wakeup(&client->dev)) { if (device_may_wakeup(&client->dev)) {
wake_status = enable_irq_wake(client->irq); wake_status = enable_irq_wake(client->irq);
@ -1279,11 +1202,6 @@ static int i2c_hid_resume(struct device *dev)
wake_status); wake_status);
} }
/* We'll resume to full power */
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
enable_irq(client->irq); enable_irq(client->irq);
/* Instead of resetting device, simply powers the device on. This /* Instead of resetting device, simply powers the device on. This
@ -1304,30 +1222,8 @@ static int i2c_hid_resume(struct device *dev)
} }
#endif #endif
#ifdef CONFIG_PM
static int i2c_hid_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
disable_irq(client->irq);
return 0;
}
static int i2c_hid_runtime_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
enable_irq(client->irq);
i2c_hid_set_power(client, I2C_HID_PWR_ON);
return 0;
}
#endif
static const struct dev_pm_ops i2c_hid_pm = { static const struct dev_pm_ops i2c_hid_pm = {
SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume) SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
NULL)
}; };
static const struct i2c_device_id i2c_hid_id_table[] = { static const struct i2c_device_id i2c_hid_id_table[] = {