Skip to content

Commit 01714a6

Browse files
diandersJiri Kosina
authored andcommitted
HID: i2c-hid: Fix suspend/resume when already runtime suspended
On ACPI-based systems ACPI power domain code runtime resumes device before calling suspend method, which ensures that i2c-hid suspend code starts with device not in low-power state and with interrupts enabled. On other systems, especially if device is not a part of any power domain, we may end up calling driver's system-level suspend routine while the device is runtime-suspended (with controller in presumably low power state and interrupts disabled). This will result in interrupts being essentially disabled twice, and we will only re-enable them after both system resume and runtime resume methods complete. Unfortunately i2c_hid_resume() calls i2c_hid_hwreset() and that only works properly if interrupts are enabled. Also if device is runtime-suspended driver's suspend code may fail if it tries to issue I/O requests. Let's fix it by runtime-resuming the device if we need to run HID driver's suspend code and also disabling interrupts only if device is not already runtime-suspended. Also on resume we mark the device as running at full power (since that is what resetting will do to it). Reviewed-by: Benson Leung <[email protected]> Tested-by: Mika Westerberg <[email protected]> Acked-by: Benjamin Tissoires <[email protected]> Signed-off-by: Doug Anderson <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 1dcdde9 commit 01714a6

File tree

1 file changed

+31
-12
lines changed

1 file changed

+31
-12
lines changed

drivers/hid/i2c-hid/i2c-hid.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,13 +1108,30 @@ static int i2c_hid_suspend(struct device *dev)
11081108
struct i2c_client *client = to_i2c_client(dev);
11091109
struct i2c_hid *ihid = i2c_get_clientdata(client);
11101110
struct hid_device *hid = ihid->hid;
1111-
int ret = 0;
1111+
int ret;
11121112
int wake_status;
11131113

1114-
if (hid->driver && hid->driver->suspend)
1114+
if (hid->driver && hid->driver->suspend) {
1115+
/*
1116+
* Wake up the device so that IO issues in
1117+
* HID driver's suspend code can succeed.
1118+
*/
1119+
ret = pm_runtime_resume(dev);
1120+
if (ret < 0)
1121+
return ret;
1122+
11151123
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
1124+
if (ret < 0)
1125+
return ret;
1126+
}
1127+
1128+
if (!pm_runtime_suspended(dev)) {
1129+
/* Save some power */
1130+
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
1131+
1132+
disable_irq(ihid->irq);
1133+
}
11161134

1117-
disable_irq(ihid->irq);
11181135
if (device_may_wakeup(&client->dev)) {
11191136
wake_status = enable_irq_wake(ihid->irq);
11201137
if (!wake_status)
@@ -1124,10 +1141,7 @@ static int i2c_hid_suspend(struct device *dev)
11241141
wake_status);
11251142
}
11261143

1127-
/* Save some power */
1128-
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
1129-
1130-
return ret;
1144+
return 0;
11311145
}
11321146

11331147
static int i2c_hid_resume(struct device *dev)
@@ -1138,11 +1152,6 @@ static int i2c_hid_resume(struct device *dev)
11381152
struct hid_device *hid = ihid->hid;
11391153
int wake_status;
11401154

1141-
enable_irq(ihid->irq);
1142-
ret = i2c_hid_hwreset(client);
1143-
if (ret)
1144-
return ret;
1145-
11461155
if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
11471156
wake_status = disable_irq_wake(ihid->irq);
11481157
if (!wake_status)
@@ -1152,6 +1161,16 @@ static int i2c_hid_resume(struct device *dev)
11521161
wake_status);
11531162
}
11541163

1164+
/* We'll resume to full power */
1165+
pm_runtime_disable(dev);
1166+
pm_runtime_set_active(dev);
1167+
pm_runtime_enable(dev);
1168+
1169+
enable_irq(ihid->irq);
1170+
ret = i2c_hid_hwreset(client);
1171+
if (ret)
1172+
return ret;
1173+
11551174
if (hid->driver && hid->driver->reset_resume) {
11561175
ret = hid->driver->reset_resume(hid);
11571176
return ret;

0 commit comments

Comments
 (0)