gpio: pca954x: Add vcc regulator and enable it
authorPhil Reid <preid@electromag.com.au>
Fri, 29 Jul 2016 03:39:55 +0000 (11:39 +0800)
committerLinus Walleij <linus.walleij@linaro.org>
Thu, 11 Aug 2016 08:13:57 +0000 (10:13 +0200)
Some i2c gpio devices are connected to a switchable power supply
which needs to be enabled prior to probing the device. This patch
allows the drive to enable the devices vcc regulator prior to probing.

Signed-off-by: Phil Reid <preid@electromag.com.au>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpio-pca953x.c

index 02f2a56..cbe2824 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/unaligned.h>
 #include <linux/of_platform.h>
 #include <linux/acpi.h>
+#include <linux/regulator/consumer.h>
 
 #define PCA953X_INPUT          0
 #define PCA953X_OUTPUT         1
@@ -113,6 +114,7 @@ struct pca953x_chip {
        const char *const *names;
        int     chip_type;
        unsigned long driver_data;
+       struct regulator *regulator;
 };
 
 static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
@@ -746,6 +748,7 @@ static int pca953x_probe(struct i2c_client *client,
        int irq_base = 0;
        int ret;
        u32 invert = 0;
+       struct regulator *reg;
 
        chip = devm_kzalloc(&client->dev,
                        sizeof(struct pca953x_chip), GFP_KERNEL);
@@ -765,6 +768,20 @@ static int pca953x_probe(struct i2c_client *client,
 
        chip->client = client;
 
+       reg = devm_regulator_get(&client->dev, "vcc");
+       if (IS_ERR(reg)) {
+               ret = PTR_ERR(reg);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&client->dev, "reg get err: %d\n", ret);
+               return ret;
+       }
+       ret = regulator_enable(reg);
+       if (ret) {
+               dev_err(&client->dev, "reg en err: %d\n", ret);
+               return ret;
+       }
+       chip->regulator = reg;
+
        if (id) {
                chip->driver_data = id->driver_data;
        } else {
@@ -776,8 +793,10 @@ static int pca953x_probe(struct i2c_client *client,
                        chip->driver_data = (int)(uintptr_t)match->data;
                } else {
                        id = acpi_match_device(pca953x_acpi_ids, &client->dev);
-                       if (!id)
-                               return -ENODEV;
+                       if (!id) {
+                               ret = -ENODEV;
+                               goto err_exit;
+                       }
 
                        chip->driver_data = id->driver_data;
                }
@@ -797,15 +816,15 @@ static int pca953x_probe(struct i2c_client *client,
        else
                ret = device_pca957x_init(chip, invert);
        if (ret)
-               return ret;
+               goto err_exit;
 
        ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
        if (ret)
-               return ret;
+               goto err_exit;
 
        ret = pca953x_irq_setup(chip, irq_base);
        if (ret)
-               return ret;
+               goto err_exit;
 
        if (pdata && pdata->setup) {
                ret = pdata->setup(client, chip->gpio_chip.base,
@@ -816,6 +835,10 @@ static int pca953x_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, chip);
        return 0;
+
+err_exit:
+       regulator_disable(chip->regulator);
+       return ret;
 }
 
 static int pca953x_remove(struct i2c_client *client)
@@ -827,14 +850,14 @@ static int pca953x_remove(struct i2c_client *client)
        if (pdata && pdata->teardown) {
                ret = pdata->teardown(client, chip->gpio_chip.base,
                                chip->gpio_chip.ngpio, pdata->context);
-               if (ret < 0) {
+               if (ret < 0)
                        dev_err(&client->dev, "%s failed, %d\n",
                                        "teardown", ret);
-                       return ret;
-               }
        }
 
-       return 0;
+       regulator_disable(chip->regulator);
+
+       return ret;
 }
 
 /* convenience to stop overlong match-table lines */