HID: i2c-hid-of: Add reset GPIO support to i2c-hid-of
authorHans de Goede <hdegoede@redhat.com>
Thu, 13 Apr 2023 09:36:25 +0000 (11:36 +0200)
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>
Thu, 13 Apr 2023 14:27:42 +0000 (16:27 +0200)
Add reset GPIO support to the generic i2c-hid-of driver

This is necessary to make the Wacom digitizer on the Lenovo Yoga Book 1
(yb1-x90f/l) work and this will also allow consolidating the 2 specialized
i2c-hid-of-elan.c and i2c-hid-of-goodix.c drivers into the generic
i2c-hid-of driver.

For now the new "post-reset-deassert-delay-ms" property is only used on
x86/ACPI (non devicetree) devs. IOW it is not used in actual devicetree
files and the same goes for the reset GPIO. The devicetree-bindings
maintainers have requested properties like these to not be added to
the devicetree-bindings, so the new property + GPIO are deliberately
not added to the existing devicetree-bindings.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Link: https://lore.kernel.org/r/20230413093625.71146-4-hdegoede@redhat.com
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
drivers/hid/i2c-hid/i2c-hid-of.c

index 385f746..855f530 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/gpio/consumer.h>
 #include <linux/hid.h>
 #include <linux/i2c.h>
 #include <linux/kernel.h>
@@ -35,8 +36,10 @@ struct i2c_hid_of {
        struct i2chid_ops ops;
 
        struct i2c_client *client;
+       struct gpio_desc *reset_gpio;
        struct regulator_bulk_data supplies[2];
        int post_power_delay_ms;
+       int post_reset_delay_ms;
 };
 
 static int i2c_hid_of_power_up(struct i2chid_ops *ops)
@@ -55,6 +58,10 @@ static int i2c_hid_of_power_up(struct i2chid_ops *ops)
        if (ihid_of->post_power_delay_ms)
                msleep(ihid_of->post_power_delay_ms);
 
+       gpiod_set_value_cansleep(ihid_of->reset_gpio, 0);
+       if (ihid_of->post_reset_delay_ms)
+               msleep(ihid_of->post_reset_delay_ms);
+
        return 0;
 }
 
@@ -62,6 +69,7 @@ static void i2c_hid_of_power_down(struct i2chid_ops *ops)
 {
        struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
 
+       gpiod_set_value_cansleep(ihid_of->reset_gpio, 1);
        regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies),
                               ihid_of->supplies);
 }
@@ -96,6 +104,19 @@ static int i2c_hid_of_probe(struct i2c_client *client)
        if (!device_property_read_u32(dev, "post-power-on-delay-ms", &val))
                ihid_of->post_power_delay_ms = val;
 
+       /*
+        * Note this is a kernel internal device-property set by x86 platform code,
+        * this MUST not be used in devicetree files without first adding it to
+        * the DT bindings.
+        */
+       if (!device_property_read_u32(dev, "post-reset-deassert-delay-ms", &val))
+               ihid_of->post_reset_delay_ms = val;
+
+       /* Start out with reset asserted */
+       ihid_of->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(ihid_of->reset_gpio))
+               return PTR_ERR(ihid_of->reset_gpio);
+
        ihid_of->supplies[0].supply = "vdd";
        ihid_of->supplies[1].supply = "vddl";
        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ihid_of->supplies),