diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-09-02 23:30:46 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-09-02 23:30:46 +0200 |
commit | 83ec91697412ae64d25dcca74597ed03029aa00d (patch) | |
tree | 8ee44ade455b790a24c2048977bf23d96d930195 /drivers/hid/i2c-hid/i2c-hid-of-goodix.c | |
parent | Merge tag 'pinctrl-v5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/... (diff) | |
parent | Merge branch 'for-5.15/apple' into for-linus (diff) | |
download | linux-83ec91697412ae64d25dcca74597ed03029aa00d.tar.xz linux-83ec91697412ae64d25dcca74597ed03029aa00d.zip |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina:
- Fix in i2c-hid driver for Elan touchpad quirk regression (Jim
Broadus)
- Quirk preventing ASUS Claymore from accidentally suspending whole
system (Luke D. Jones)
- Updates to the existing FW reporting mechanism, MP2 FW status checks,
adding proper power management support for amd-sfh (Basavaraj
Natikar)
- Regression fix for an issue in HID core that got uncovered by recent
USB core cleanup leading to issues when transfer_buffer_length is not
in line with wLength (Alan Stern)
- Memory leak fix in USB HID core (Anirudh Rayabharam)
- Improvement of stylus battery reporting (Dmitry Torokhov)
- Power management improvement for Goodix driver (Douglas Anderson)
- High-resolution scroll support for Magicmouse devices (José Expósito)
- Support for GHLive PS4 dongles (Daniel Nguyen)
- Support proper EV_MSC emissions to hid-apple (Vincent Lefevre)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (31 commits)
HID: usbhid: Simplify code in hid_submit_ctrl()
HID: usbhid: Fix warning caused by 0-length input reports
HID: usbhid: Fix flood of "control queue full" messages
HID: sony: Fix more ShanWan clone gamepads to not rumble when plugged in.
HID: sony: support for the ghlive ps4 dongles
HID: thrustmaster: clean up Makefile and adapt quirks
HID: i2c-hid: Fix Elan touchpad regression
HID: asus: Prevent Claymore sending suspend event
HID: amd_sfh: Add dyndbg prints for debugging
HID: amd_sfh: Add support for PM suspend and resume
HID: amd_sfh: Move hid probe after sensor is enabled
HID: amd_sfh: Add command response to check command status
HID: amd_sfh: Fix period data field to enable sensor
HID: logitech-hidpp: battery: provide CAPACITY property for newer devices
HID: thrustmaster: Fix memory leak in thrustmaster_interrupts()
HID: thrustmaster: Fix memory leak in remove
HID: thrustmaster: Fix memory leaks in probe
HID: elo: update the reference count of the usb device structure
HID: logitech-hidpp: Use 'atomic_inc_return' instead of hand-writing it
HID: apple: Add missing scan code event for keys handled by hid-apple
...
Diffstat (limited to 'drivers/hid/i2c-hid/i2c-hid-of-goodix.c')
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-of-goodix.c | 92 |
1 files changed, 79 insertions, 13 deletions
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c index ee0225982a82..52674149a275 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c @@ -26,28 +26,29 @@ struct i2c_hid_of_goodix { struct i2chid_ops ops; struct regulator *vdd; + struct notifier_block nb; + struct mutex regulator_mutex; struct gpio_desc *reset_gpio; const struct goodix_i2c_hid_timing_data *timings; }; -static int goodix_i2c_hid_power_up(struct i2chid_ops *ops) +static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix, + bool regulator_just_turned_on) { - struct i2c_hid_of_goodix *ihid_goodix = - container_of(ops, struct i2c_hid_of_goodix, ops); - int ret; - - ret = regulator_enable(ihid_goodix->vdd); - if (ret) - return ret; - - if (ihid_goodix->timings->post_power_delay_ms) + if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms) msleep(ihid_goodix->timings->post_power_delay_ms); gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0); if (ihid_goodix->timings->post_gpio_reset_delay_ms) msleep(ihid_goodix->timings->post_gpio_reset_delay_ms); +} - return 0; +static int goodix_i2c_hid_power_up(struct i2chid_ops *ops) +{ + struct i2c_hid_of_goodix *ihid_goodix = + container_of(ops, struct i2c_hid_of_goodix, ops); + + return regulator_enable(ihid_goodix->vdd); } static void goodix_i2c_hid_power_down(struct i2chid_ops *ops) @@ -55,20 +56,54 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops) struct i2c_hid_of_goodix *ihid_goodix = container_of(ops, struct i2c_hid_of_goodix, ops); - gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1); regulator_disable(ihid_goodix->vdd); } +static int ihid_goodix_vdd_notify(struct notifier_block *nb, + unsigned long event, + void *ignored) +{ + struct i2c_hid_of_goodix *ihid_goodix = + container_of(nb, struct i2c_hid_of_goodix, nb); + int ret = NOTIFY_OK; + + mutex_lock(&ihid_goodix->regulator_mutex); + + switch (event) { + case REGULATOR_EVENT_PRE_DISABLE: + gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1); + break; + + case REGULATOR_EVENT_ENABLE: + goodix_i2c_hid_deassert_reset(ihid_goodix, true); + break; + + case REGULATOR_EVENT_ABORT_DISABLE: + goodix_i2c_hid_deassert_reset(ihid_goodix, false); + break; + + default: + ret = NOTIFY_DONE; + break; + } + + mutex_unlock(&ihid_goodix->regulator_mutex); + + return ret; +} + static int i2c_hid_of_goodix_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_hid_of_goodix *ihid_goodix; - + int ret; ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix), GFP_KERNEL); if (!ihid_goodix) return -ENOMEM; + mutex_init(&ihid_goodix->regulator_mutex); + ihid_goodix->ops.power_up = goodix_i2c_hid_power_up; ihid_goodix->ops.power_down = goodix_i2c_hid_power_down; @@ -84,6 +119,37 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client, ihid_goodix->timings = device_get_match_data(&client->dev); + /* + * We need to control the "reset" line in lockstep with the regulator + * actually turning on an off instead of just when we make the request. + * This matters if the regulator is shared with another consumer. + * - If the regulator is off then we must assert reset. The reset + * line is active low and on some boards it could cause a current + * leak if left high. + * - If the regulator is on then we don't want reset asserted for very + * long. Holding the controller in reset apparently draws extra + * power. + */ + mutex_lock(&ihid_goodix->regulator_mutex); + ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify; + ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb); + if (ret) { + mutex_unlock(&ihid_goodix->regulator_mutex); + return dev_err_probe(&client->dev, ret, + "regulator notifier request failed\n"); + } + + /* + * If someone else is holding the regulator on (or the regulator is + * an always-on one) we might never be told to deassert reset. Do it + * now. Here we'll assume that someone else might have _just + * barely_ turned the regulator on so we'll do the full + * "post_power_delay" just in case. + */ + if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd)) + goodix_i2c_hid_deassert_reset(ihid_goodix, true); + mutex_unlock(&ihid_goodix->regulator_mutex); + return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001); } |