提交 572d3c64 编写于 作者: B Brian Norris 提交者: Jiri Kosina

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: NBrian Norris <briannorris@chromium.org>
Signed-off-by: NCaesar Wang <wxt@rock-chips.com>
Acked-by: NBenjamin Tissoires <benjamin.tissoires@redhat.com>
Reviewed-by: NDmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: linux-input@vger.kernel.org
Signed-off-by: NJiri Kosina <jkosina@suse.cz>
上级 cb5cfd91
...@@ -994,6 +994,11 @@ static int i2c_hid_of_probe(struct i2c_client *client, ...@@ -994,6 +994,11 @@ static int i2c_hid_of_probe(struct i2c_client *client,
} }
pdata->hid_descriptor_address = val; pdata->hid_descriptor_address = val;
ret = of_property_read_u32(dev->of_node, "post-power-on-delay-ms",
&val);
if (!ret)
pdata->post_power_delay_ms = val;
return 0; return 0;
} }
...@@ -1053,6 +1058,24 @@ static int i2c_hid_probe(struct i2c_client *client, ...@@ -1053,6 +1058,24 @@ static int i2c_hid_probe(struct i2c_client *client,
ihid->pdata = *platform_data; ihid->pdata = *platform_data;
} }
ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(ihid->pdata.supply)) {
ret = PTR_ERR(ihid->pdata.supply);
if (ret != -EPROBE_DEFER)
dev_err(&client->dev, "Failed to get regulator: %d\n",
ret);
return ret;
}
ret = regulator_enable(ihid->pdata.supply);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable regulator: %d\n",
ret);
goto err;
}
if (ihid->pdata.post_power_delay_ms)
msleep(ihid->pdata.post_power_delay_ms);
i2c_set_clientdata(client, ihid); i2c_set_clientdata(client, ihid);
ihid->client = client; ihid->client = client;
...@@ -1068,7 +1091,7 @@ static int i2c_hid_probe(struct i2c_client *client, ...@@ -1068,7 +1091,7 @@ static int i2c_hid_probe(struct i2c_client *client,
* real computation later. */ * real computation later. */
ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE); ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
if (ret < 0) if (ret < 0)
goto err; goto err_regulator;
pm_runtime_get_noresume(&client->dev); pm_runtime_get_noresume(&client->dev);
pm_runtime_set_active(&client->dev); pm_runtime_set_active(&client->dev);
...@@ -1125,6 +1148,9 @@ static int i2c_hid_probe(struct i2c_client *client, ...@@ -1125,6 +1148,9 @@ static int i2c_hid_probe(struct i2c_client *client,
pm_runtime_put_noidle(&client->dev); pm_runtime_put_noidle(&client->dev);
pm_runtime_disable(&client->dev); pm_runtime_disable(&client->dev);
err_regulator:
regulator_disable(ihid->pdata.supply);
err: err:
i2c_hid_free_buffers(ihid); i2c_hid_free_buffers(ihid);
kfree(ihid); kfree(ihid);
...@@ -1149,6 +1175,8 @@ static int i2c_hid_remove(struct i2c_client *client) ...@@ -1149,6 +1175,8 @@ static int i2c_hid_remove(struct i2c_client *client)
if (ihid->bufsize) if (ihid->bufsize)
i2c_hid_free_buffers(ihid); i2c_hid_free_buffers(ihid);
regulator_disable(ihid->pdata.supply);
kfree(ihid); kfree(ihid);
return 0; return 0;
...@@ -1199,6 +1227,10 @@ static int i2c_hid_suspend(struct device *dev) ...@@ -1199,6 +1227,10 @@ static int i2c_hid_suspend(struct device *dev)
else else
hid_warn(hid, "Failed to enable irq wake: %d\n", hid_warn(hid, "Failed to enable irq wake: %d\n",
wake_status); wake_status);
} else {
ret = regulator_disable(ihid->pdata.supply);
if (ret < 0)
hid_warn(hid, "Failed to disable supply: %d\n", ret);
} }
return 0; return 0;
...@@ -1212,7 +1244,13 @@ static int i2c_hid_resume(struct device *dev) ...@@ -1212,7 +1244,13 @@ static int i2c_hid_resume(struct device *dev)
struct hid_device *hid = ihid->hid; struct hid_device *hid = ihid->hid;
int wake_status; int wake_status;
if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) { if (!device_may_wakeup(&client->dev)) {
ret = regulator_enable(ihid->pdata.supply);
if (ret < 0)
hid_warn(hid, "Failed to enable supply: %d\n", ret);
if (ihid->pdata.post_power_delay_ms)
msleep(ihid->pdata.post_power_delay_ms);
} else if (ihid->irq_wake_enabled) {
wake_status = disable_irq_wake(client->irq); wake_status = disable_irq_wake(client->irq);
if (!wake_status) if (!wake_status)
ihid->irq_wake_enabled = false; ihid->irq_wake_enabled = false;
......
...@@ -14,9 +14,13 @@ ...@@ -14,9 +14,13 @@
#include <linux/types.h> #include <linux/types.h>
struct regulator;
/** /**
* struct i2chid_platform_data - used by hid over i2c implementation. * struct i2chid_platform_data - used by hid over i2c implementation.
* @hid_descriptor_address: i2c register where the HID descriptor is stored. * @hid_descriptor_address: i2c register where the HID descriptor is stored.
* @supply: regulator for powering on the device.
* @post_power_delay_ms: delay after powering on before device is usable.
* *
* Note that it is the responsibility of the platform driver (or the acpi 5.0 * Note that it is the responsibility of the platform driver (or the acpi 5.0
* driver, or the flattened device tree) to setup the irq related to the gpio in * driver, or the flattened device tree) to setup the irq related to the gpio in
...@@ -31,6 +35,8 @@ ...@@ -31,6 +35,8 @@
*/ */
struct i2c_hid_platform_data { struct i2c_hid_platform_data {
u16 hid_descriptor_address; u16 hid_descriptor_address;
struct regulator *supply;
int post_power_delay_ms;
}; };
#endif /* __LINUX_I2C_HID_H */ #endif /* __LINUX_I2C_HID_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册