Skip to content

Commit

Permalink
HID: i2c-hid: support regulator power on/off
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
computersforpeace authored and Jiri Kosina committed Mar 6, 2017
1 parent cb5cfd9 commit 572d3c6
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 2 deletions.
42 changes: 40 additions & 2 deletions drivers/hid/i2c-hid/i2c-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,11 @@ static int i2c_hid_of_probe(struct i2c_client *client,
}
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;
}

Expand Down Expand Up @@ -1053,6 +1058,24 @@ static int i2c_hid_probe(struct i2c_client *client,
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);

ihid->client = client;
Expand All @@ -1068,7 +1091,7 @@ static int i2c_hid_probe(struct i2c_client *client,
* real computation later. */
ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
if (ret < 0)
goto err;
goto err_regulator;

pm_runtime_get_noresume(&client->dev);
pm_runtime_set_active(&client->dev);
Expand Down Expand Up @@ -1125,6 +1148,9 @@ static int i2c_hid_probe(struct i2c_client *client,
pm_runtime_put_noidle(&client->dev);
pm_runtime_disable(&client->dev);

err_regulator:
regulator_disable(ihid->pdata.supply);

err:
i2c_hid_free_buffers(ihid);
kfree(ihid);
Expand All @@ -1149,6 +1175,8 @@ static int i2c_hid_remove(struct i2c_client *client)
if (ihid->bufsize)
i2c_hid_free_buffers(ihid);

regulator_disable(ihid->pdata.supply);

kfree(ihid);

return 0;
Expand Down Expand Up @@ -1199,6 +1227,10 @@ static int i2c_hid_suspend(struct device *dev)
else
hid_warn(hid, "Failed to enable irq wake: %d\n",
wake_status);
} else {
ret = regulator_disable(ihid->pdata.supply);
if (ret < 0)
hid_warn(hid, "Failed to disable supply: %d\n", ret);
}

return 0;
Expand All @@ -1212,7 +1244,13 @@ static int i2c_hid_resume(struct device *dev)
struct hid_device *hid = ihid->hid;
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);
if (!wake_status)
ihid->irq_wake_enabled = false;
Expand Down
6 changes: 6 additions & 0 deletions include/linux/i2c/i2c-hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@

#include <linux/types.h>

struct regulator;

/**
* struct i2chid_platform_data - used by hid over i2c implementation.
* @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
* driver, or the flattened device tree) to setup the irq related to the gpio in
Expand All @@ -31,6 +35,8 @@
*/
struct i2c_hid_platform_data {
u16 hid_descriptor_address;
struct regulator *supply;
int post_power_delay_ms;
};

#endif /* __LINUX_I2C_HID_H */

0 comments on commit 572d3c6

Please sign in to comment.