diff options
author | Brian Norris <briannorris@chromium.org> | 2017-02-09 21:03:57 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2017-03-06 07:48:44 -0500 |
commit | 572d3c6444979a6a49c6b464110563f578e8dece (patch) | |
tree | edfa44f72e6d60a0b4ca84705e39f6a3b8dd9812 | |
parent | cb5cfd91a7ae5ae355e657e5ce4931836b7665eb (diff) |
HID: i2c-hid: support regulator power on/off
On some boards, we need to enable a regulator before using the HID, and
it's also nice to save power in suspend by disabling it. Support an
optional "vdd-supply" and a companion initialization delay.
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Caesar Wang <wxt@rock-chips.com>
Acked-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Reviewed-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: linux-input@vger.kernel.org
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 42 | ||||
-rw-r--r-- | include/linux/i2c/i2c-hid.h | 6 |
2 files changed, 46 insertions, 2 deletions
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index ea3c3546cef7..a3f6daf0886b 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c | |||
@@ -994,6 +994,11 @@ static int i2c_hid_of_probe(struct i2c_client *client, | |||
994 | } | 994 | } |
995 | pdata->hid_descriptor_address = val; | 995 | pdata->hid_descriptor_address = val; |
996 | 996 | ||
997 | ret = of_property_read_u32(dev->of_node, "post-power-on-delay-ms", | ||
998 | &val); | ||
999 | if (!ret) | ||
1000 | pdata->post_power_delay_ms = val; | ||
1001 | |||
997 | return 0; | 1002 | return 0; |
998 | } | 1003 | } |
999 | 1004 | ||
@@ -1053,6 +1058,24 @@ static int i2c_hid_probe(struct i2c_client *client, | |||
1053 | ihid->pdata = *platform_data; | 1058 | ihid->pdata = *platform_data; |
1054 | } | 1059 | } |
1055 | 1060 | ||
1061 | ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd"); | ||
1062 | if (IS_ERR(ihid->pdata.supply)) { | ||
1063 | ret = PTR_ERR(ihid->pdata.supply); | ||
1064 | if (ret != -EPROBE_DEFER) | ||
1065 | dev_err(&client->dev, "Failed to get regulator: %d\n", | ||
1066 | ret); | ||
1067 | return ret; | ||
1068 | } | ||
1069 | |||
1070 | ret = regulator_enable(ihid->pdata.supply); | ||
1071 | if (ret < 0) { | ||
1072 | dev_err(&client->dev, "Failed to enable regulator: %d\n", | ||
1073 | ret); | ||
1074 | goto err; | ||
1075 | } | ||
1076 | if (ihid->pdata.post_power_delay_ms) | ||
1077 | msleep(ihid->pdata.post_power_delay_ms); | ||
1078 | |||
1056 | i2c_set_clientdata(client, ihid); | 1079 | i2c_set_clientdata(client, ihid); |
1057 | 1080 | ||
1058 | ihid->client = client; | 1081 | ihid->client = client; |
@@ -1068,7 +1091,7 @@ static int i2c_hid_probe(struct i2c_client *client, | |||
1068 | * real computation later. */ | 1091 | * real computation later. */ |
1069 | ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE); | 1092 | ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE); |
1070 | if (ret < 0) | 1093 | if (ret < 0) |
1071 | goto err; | 1094 | goto err_regulator; |
1072 | 1095 | ||
1073 | pm_runtime_get_noresume(&client->dev); | 1096 | pm_runtime_get_noresume(&client->dev); |
1074 | pm_runtime_set_active(&client->dev); | 1097 | pm_runtime_set_active(&client->dev); |
@@ -1125,6 +1148,9 @@ err_pm: | |||
1125 | pm_runtime_put_noidle(&client->dev); | 1148 | pm_runtime_put_noidle(&client->dev); |
1126 | pm_runtime_disable(&client->dev); | 1149 | pm_runtime_disable(&client->dev); |
1127 | 1150 | ||
1151 | err_regulator: | ||
1152 | regulator_disable(ihid->pdata.supply); | ||
1153 | |||
1128 | err: | 1154 | err: |
1129 | i2c_hid_free_buffers(ihid); | 1155 | i2c_hid_free_buffers(ihid); |
1130 | kfree(ihid); | 1156 | kfree(ihid); |
@@ -1149,6 +1175,8 @@ static int i2c_hid_remove(struct i2c_client *client) | |||
1149 | if (ihid->bufsize) | 1175 | if (ihid->bufsize) |
1150 | i2c_hid_free_buffers(ihid); | 1176 | i2c_hid_free_buffers(ihid); |
1151 | 1177 | ||
1178 | regulator_disable(ihid->pdata.supply); | ||
1179 | |||
1152 | kfree(ihid); | 1180 | kfree(ihid); |
1153 | 1181 | ||
1154 | return 0; | 1182 | return 0; |
@@ -1199,6 +1227,10 @@ static int i2c_hid_suspend(struct device *dev) | |||
1199 | else | 1227 | else |
1200 | hid_warn(hid, "Failed to enable irq wake: %d\n", | 1228 | hid_warn(hid, "Failed to enable irq wake: %d\n", |
1201 | wake_status); | 1229 | wake_status); |
1230 | } else { | ||
1231 | ret = regulator_disable(ihid->pdata.supply); | ||
1232 | if (ret < 0) | ||
1233 | hid_warn(hid, "Failed to disable supply: %d\n", ret); | ||
1202 | } | 1234 | } |
1203 | 1235 | ||
1204 | return 0; | 1236 | return 0; |
@@ -1212,7 +1244,13 @@ static int i2c_hid_resume(struct device *dev) | |||
1212 | struct hid_device *hid = ihid->hid; | 1244 | struct hid_device *hid = ihid->hid; |
1213 | int wake_status; | 1245 | int wake_status; |
1214 | 1246 | ||
1215 | if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) { | 1247 | if (!device_may_wakeup(&client->dev)) { |
1248 | ret = regulator_enable(ihid->pdata.supply); | ||
1249 | if (ret < 0) | ||
1250 | hid_warn(hid, "Failed to enable supply: %d\n", ret); | ||
1251 | if (ihid->pdata.post_power_delay_ms) | ||
1252 | msleep(ihid->pdata.post_power_delay_ms); | ||
1253 | } else if (ihid->irq_wake_enabled) { | ||
1216 | wake_status = disable_irq_wake(client->irq); | 1254 | wake_status = disable_irq_wake(client->irq); |
1217 | if (!wake_status) | 1255 | if (!wake_status) |
1218 | ihid->irq_wake_enabled = false; | 1256 | ihid->irq_wake_enabled = false; |
diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h index 7aa901d92058..1fb088239d12 100644 --- a/include/linux/i2c/i2c-hid.h +++ b/include/linux/i2c/i2c-hid.h | |||
@@ -14,9 +14,13 @@ | |||
14 | 14 | ||
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | 16 | ||
17 | struct regulator; | ||
18 | |||
17 | /** | 19 | /** |
18 | * struct i2chid_platform_data - used by hid over i2c implementation. | 20 | * struct i2chid_platform_data - used by hid over i2c implementation. |
19 | * @hid_descriptor_address: i2c register where the HID descriptor is stored. | 21 | * @hid_descriptor_address: i2c register where the HID descriptor is stored. |
22 | * @supply: regulator for powering on the device. | ||
23 | * @post_power_delay_ms: delay after powering on before device is usable. | ||
20 | * | 24 | * |
21 | * Note that it is the responsibility of the platform driver (or the acpi 5.0 | 25 | * Note that it is the responsibility of the platform driver (or the acpi 5.0 |
22 | * driver, or the flattened device tree) to setup the irq related to the gpio in | 26 | * driver, or the flattened device tree) to setup the irq related to the gpio in |
@@ -31,6 +35,8 @@ | |||
31 | */ | 35 | */ |
32 | struct i2c_hid_platform_data { | 36 | struct i2c_hid_platform_data { |
33 | u16 hid_descriptor_address; | 37 | u16 hid_descriptor_address; |
38 | struct regulator *supply; | ||
39 | int post_power_delay_ms; | ||
34 | }; | 40 | }; |
35 | 41 | ||
36 | #endif /* __LINUX_I2C_HID_H */ | 42 | #endif /* __LINUX_I2C_HID_H */ |