Input: elants_i2c - wire up regulator support
authorDmitry Torokhov <dtor@chromium.org>
Fri, 17 Apr 2015 01:14:55 +0000 (18:14 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 7 Aug 2015 21:55:38 +0000 (14:55 -0700)
Elan touchscreen controllers use two power supplies, vcc33 and vccio,
and we need to enable them before trying to access the device. On X86
firmware usually does this, but on ARM it is usually left to the kernel.

Signed-off-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Scott Liu <scott.liu@emc.com.tw>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Documentation/devicetree/bindings/input/elants_i2c.txt
drivers/input/touchscreen/elants_i2c.c

index a765232e6446f8006a7a9ede6939e58ae4bb199d..8a71038f3489780c8b6730236ed47daea48f31d2 100644 (file)
@@ -13,6 +13,9 @@ Optional properties:
 - pinctrl-names: should be "default" (see pinctrl binding [1]).
 - pinctrl-0: a phandle pointing to the pin settings for the device (see
   pinctrl binding [1]).
+- reset-gpios: reset gpio the chip is connected to.
+- vcc33-supply: a phandle for the regulator supplying 3.3V power.
+- vccio-supply: a phandle for the regulator supplying IO power.
 
 [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
 [1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
index 42e43f14602eaff5634783298da4ab44db94373f..19122652e71b10833905e0e62dd9aab7d17e44ca 100644 (file)
@@ -38,6 +38,8 @@
 #include <linux/input/mt.h>
 #include <linux/acpi.h>
 #include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
 #include <asm/unaligned.h>
 
 /* Device, Driver information */
 /* calibration timeout definition */
 #define ELAN_CALI_TIMEOUT_MSEC 10000
 
+#define ELAN_POWERON_DELAY_USEC        500
+#define ELAN_RESET_DELAY_MSEC  20
+
 enum elants_state {
        ELAN_STATE_NORMAL,
        ELAN_WAIT_QUEUE_HEADER,
@@ -118,6 +123,10 @@ struct elants_data {
        struct i2c_client *client;
        struct input_dev *input;
 
+       struct regulator *vcc33;
+       struct regulator *vccio;
+       struct gpio_desc *reset_gpio;
+
        u16 fw_version;
        u8 test_version;
        u8 solution_version;
@@ -141,6 +150,7 @@ struct elants_data {
        u8 buf[MAX_PACKET_SIZE];
 
        bool wake_irq_enabled;
+       bool keep_power_in_suspend;
 };
 
 static int elants_i2c_send(struct i2c_client *client,
@@ -1058,6 +1068,67 @@ static void elants_i2c_remove_sysfs_group(void *_data)
        sysfs_remove_group(&ts->client->dev.kobj, &elants_attribute_group);
 }
 
+static int elants_i2c_power_on(struct elants_data *ts)
+{
+       int error;
+
+       /*
+        * If we do not have reset gpio assume platform firmware
+        * controls regulators and does power them on for us.
+        */
+       if (IS_ERR_OR_NULL(ts->reset_gpio))
+               return 0;
+
+       gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+       error = regulator_enable(ts->vcc33);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "failed to enable vcc33 regulator: %d\n",
+                       error);
+               goto release_reset_gpio;
+       }
+
+       error = regulator_enable(ts->vccio);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "failed to enable vccio regulator: %d\n",
+                       error);
+               regulator_disable(ts->vcc33);
+               goto release_reset_gpio;
+       }
+
+       /*
+        * We need to wait a bit after powering on controller before
+        * we are allowed to release reset GPIO.
+        */
+       udelay(ELAN_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+       gpiod_set_value_cansleep(ts->reset_gpio, 0);
+       if (error)
+               return error;
+
+       msleep(ELAN_RESET_DELAY_MSEC);
+
+       return 0;
+}
+
+static void elants_i2c_power_off(void *_data)
+{
+       struct elants_data *ts = _data;
+
+       if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+               /*
+                * Activate reset gpio to prevent leakage through the
+                * pin once we shut off power to the controller.
+                */
+               gpiod_set_value_cansleep(ts->reset_gpio, 1);
+               regulator_disable(ts->vccio);
+               regulator_disable(ts->vcc33);
+       }
+}
+
 static int elants_i2c_probe(struct i2c_client *client,
                            const struct i2c_device_id *id)
 {
@@ -1072,13 +1143,6 @@ static int elants_i2c_probe(struct i2c_client *client,
                return -ENXIO;
        }
 
-       /* Make sure there is something at this address */
-       if (i2c_smbus_xfer(client->adapter, client->addr, 0,
-                       I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
-               dev_err(&client->dev, "nothing at this address\n");
-               return -ENXIO;
-       }
-
        ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL);
        if (!ts)
                return -ENOMEM;
@@ -1089,6 +1153,70 @@ static int elants_i2c_probe(struct i2c_client *client,
        ts->client = client;
        i2c_set_clientdata(client, ts);
 
+       ts->vcc33 = devm_regulator_get(&client->dev, "vcc33");
+       if (IS_ERR(ts->vcc33)) {
+               error = PTR_ERR(ts->vcc33);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "Failed to get 'vcc33' regulator: %d\n",
+                               error);
+               return error;
+       }
+
+       ts->vccio = devm_regulator_get(&client->dev, "vccio");
+       if (IS_ERR(ts->vccio)) {
+               error = PTR_ERR(ts->vccio);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "Failed to get 'vccio' regulator: %d\n",
+                               error);
+               return error;
+       }
+
+       ts->reset_gpio = devm_gpiod_get(&client->dev, "reset");
+       if (IS_ERR(ts->reset_gpio)) {
+               error = PTR_ERR(ts->reset_gpio);
+
+               if (error == -EPROBE_DEFER)
+                       return error;
+
+               if (error != -ENOENT && error != -ENOSYS) {
+                       dev_err(&client->dev,
+                               "failed to get reset gpio: %d\n",
+                               error);
+                       return error;
+               }
+
+               ts->keep_power_in_suspend = true;
+       } else {
+               error = gpiod_direction_output(ts->reset_gpio, 0);
+               if (error) {
+                       dev_err(&client->dev,
+                               "failed to configure reset gpio as output: %d\n",
+                               error);
+                       return error;
+               }
+       }
+
+       error = elants_i2c_power_on(ts);
+       if (error)
+               return error;
+
+       error = devm_add_action(&client->dev, elants_i2c_power_off, ts);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to install power off action: %d\n", error);
+               elants_i2c_power_off(ts);
+               return error;
+       }
+
+       /* Make sure there is something at this address */
+       if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+                          I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+               dev_err(&client->dev, "nothing at this address\n");
+               return -ENXIO;
+       }
+
        error = elants_i2c_initialize(ts);
        if (error) {
                dev_err(&client->dev, "failed to initialize: %d\n", error);
@@ -1196,17 +1324,23 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev)
 
        disable_irq(client->irq);
 
-       for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
-               error = elants_i2c_send(client, set_sleep_cmd,
-                                       sizeof(set_sleep_cmd));
-               if (!error)
-                       break;
+       if (device_may_wakeup(dev) || ts->keep_power_in_suspend) {
+               for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+                       error = elants_i2c_send(client, set_sleep_cmd,
+                                               sizeof(set_sleep_cmd));
+                       if (!error)
+                               break;
 
-               dev_err(&client->dev, "suspend command failed: %d\n", error);
-       }
+                       dev_err(&client->dev,
+                               "suspend command failed: %d\n", error);
+               }
 
-       if (device_may_wakeup(dev))
-               ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+               if (device_may_wakeup(dev))
+                       ts->wake_irq_enabled =
+                                       (enable_irq_wake(client->irq) == 0);
+       } else {
+               elants_i2c_power_off(ts);
+       }
 
        return 0;
 }
@@ -1222,13 +1356,19 @@ static int __maybe_unused elants_i2c_resume(struct device *dev)
        if (device_may_wakeup(dev) && ts->wake_irq_enabled)
                disable_irq_wake(client->irq);
 
-       for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
-               error = elants_i2c_send(client, set_active_cmd,
-                                       sizeof(set_active_cmd));
-               if (!error)
-                       break;
+       if (ts->keep_power_in_suspend) {
+               for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+                       error = elants_i2c_send(client, set_active_cmd,
+                                               sizeof(set_active_cmd));
+                       if (!error)
+                               break;
 
-               dev_err(&client->dev, "resume command failed: %d\n", error);
+                       dev_err(&client->dev,
+                               "resume command failed: %d\n", error);
+               }
+       } else {
+               elants_i2c_power_on(ts);
+               elants_i2c_initialize(ts);
        }
 
        ts->state = ELAN_STATE_NORMAL;