leds: netxbig: Convert to use GPIO descriptors
authorLinus Walleij <linus.walleij@linaro.org>
Wed, 15 Apr 2020 14:51:39 +0000 (16:51 +0200)
committerPavel Machek <pavel@ucw.cz>
Mon, 27 Apr 2020 12:11:20 +0000 (14:11 +0200)
This converts the NetXbig LED driver to use GPIO descriptors
instead of using the legacy interfaces in <linux/of_gpio.h>
and <linux/gpio.h> to iteratively parse the device tree for
global GPIO numbers.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Simon Guinot <simon.guinot@sequanux.org>
drivers/leds/leds-netxbig.c

index 14ef4cc..ceceeb6 100644 (file)
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/leds.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 
 struct netxbig_gpio_ext {
-       unsigned int    *addr;
+       struct gpio_desc **addr;
        int             num_addr;
-       unsigned int    *data;
+       struct gpio_desc **data;
        int             num_data;
-       unsigned int    enable;
+       struct gpio_desc *enable;
 };
 
 enum netxbig_led_mode {
@@ -69,7 +70,7 @@ static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr)
        int pin;
 
        for (pin = 0; pin < gpio_ext->num_addr; pin++)
-               gpio_set_value(gpio_ext->addr[pin], (addr >> pin) & 1);
+               gpiod_set_value(gpio_ext->addr[pin], (addr >> pin) & 1);
 }
 
 static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data)
@@ -77,14 +78,14 @@ static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data)
        int pin;
 
        for (pin = 0; pin < gpio_ext->num_data; pin++)
-               gpio_set_value(gpio_ext->data[pin], (data >> pin) & 1);
+               gpiod_set_value(gpio_ext->data[pin], (data >> pin) & 1);
 }
 
 static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext)
 {
        /* Enable select is done on the raising edge. */
-       gpio_set_value(gpio_ext->enable, 0);
-       gpio_set_value(gpio_ext->enable, 1);
+       gpiod_set_value(gpio_ext->enable, 0);
+       gpiod_set_value(gpio_ext->enable, 1);
 }
 
 static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
@@ -99,41 +100,6 @@ static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
        spin_unlock_irqrestore(&gpio_ext_lock, flags);
 }
 
-static int gpio_ext_init(struct platform_device *pdev,
-                        struct netxbig_gpio_ext *gpio_ext)
-{
-       int err;
-       int i;
-
-       if (unlikely(!gpio_ext))
-               return -EINVAL;
-
-       /* Configure address GPIOs. */
-       for (i = 0; i < gpio_ext->num_addr; i++) {
-               err = devm_gpio_request_one(&pdev->dev, gpio_ext->addr[i],
-                                           GPIOF_OUT_INIT_LOW,
-                                           "GPIO extension addr");
-               if (err)
-                       return err;
-       }
-       /* Configure data GPIOs. */
-       for (i = 0; i < gpio_ext->num_data; i++) {
-               err = devm_gpio_request_one(&pdev->dev, gpio_ext->data[i],
-                                           GPIOF_OUT_INIT_LOW,
-                                           "GPIO extension data");
-               if (err)
-                       return err;
-       }
-       /* Configure "enable select" GPIO. */
-       err = devm_gpio_request_one(&pdev->dev, gpio_ext->enable,
-                                   GPIOF_OUT_INIT_LOW,
-                                   "GPIO extension enable");
-       if (err)
-               return err;
-
-       return 0;
-}
-
 /*
  * Class LED driver.
  */
@@ -347,15 +313,47 @@ static int create_netxbig_led(struct platform_device *pdev,
        return devm_led_classdev_register(&pdev->dev, &led_dat->cdev);
 }
 
-static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
-                                struct netxbig_gpio_ext *gpio_ext)
+/**
+ * netxbig_gpio_ext_remove() - Clean up GPIO extension data
+ * @data: managed resource data to clean up
+ *
+ * Since we pick GPIO descriptors from another device than the device our
+ * driver is probing to, we need to register a specific callback to free
+ * these up using managed resources.
+ */
+static void netxbig_gpio_ext_remove(void *data)
+{
+       struct netxbig_gpio_ext *gpio_ext = data;
+       int i;
+
+       for (i = 0; i < gpio_ext->num_addr; i++)
+               gpiod_put(gpio_ext->addr[i]);
+       for (i = 0; i < gpio_ext->num_data; i++)
+               gpiod_put(gpio_ext->data[i]);
+       gpiod_put(gpio_ext->enable);
+}
+
+/**
+ * netxbig_gpio_ext_get() - Obtain GPIO extension device data
+ * @dev: main LED device
+ * @gpio_ext_dev: the GPIO extension device
+ * @gpio_ext: the data structure holding the GPIO extension data
+ *
+ * This function walks the subdevice that only contain GPIO line
+ * handles in the device tree and obtains the GPIO descriptors from that
+ * device.
+ */
+static int netxbig_gpio_ext_get(struct device *dev,
+                               struct device *gpio_ext_dev,
+                               struct netxbig_gpio_ext *gpio_ext)
 {
-       int *addr, *data;
+       struct gpio_desc **addr, **data;
        int num_addr, num_data;
+       struct gpio_desc *gpiod;
        int ret;
        int i;
 
-       ret = of_gpio_named_count(np, "addr-gpios");
+       ret = gpiod_count(gpio_ext_dev, "addr");
        if (ret < 0) {
                dev_err(dev,
                        "Failed to count GPIOs in DT property addr-gpios\n");
@@ -366,16 +364,25 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
        if (!addr)
                return -ENOMEM;
 
+       /*
+        * We cannot use devm_ managed resources with these GPIO descriptors
+        * since they are associated with the "GPIO extension device" which
+        * does not probe any driver. The device tree parser will however
+        * populate a platform device for it so we can anyway obtain the
+        * GPIO descriptors from the device.
+        */
        for (i = 0; i < num_addr; i++) {
-               ret = of_get_named_gpio(np, "addr-gpios", i);
-               if (ret < 0)
-                       return ret;
-               addr[i] = ret;
+               gpiod = gpiod_get_index(gpio_ext_dev, "addr", i,
+                                       GPIOD_OUT_LOW);
+               if (IS_ERR(gpiod))
+                       return PTR_ERR(gpiod);
+               gpiod_set_consumer_name(gpiod, "GPIO extension addr");
+               addr[i] = gpiod;
        }
        gpio_ext->addr = addr;
        gpio_ext->num_addr = num_addr;
 
-       ret = of_gpio_named_count(np, "data-gpios");
+       ret = gpiod_count(gpio_ext_dev, "data");
        if (ret < 0) {
                dev_err(dev,
                        "Failed to count GPIOs in DT property data-gpios\n");
@@ -387,23 +394,26 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
                return -ENOMEM;
 
        for (i = 0; i < num_data; i++) {
-               ret = of_get_named_gpio(np, "data-gpios", i);
-               if (ret < 0)
-                       return ret;
-               data[i] = ret;
+               gpiod = gpiod_get_index(gpio_ext_dev, "data", i,
+                                       GPIOD_OUT_LOW);
+               if (IS_ERR(gpiod))
+                       return PTR_ERR(gpiod);
+               gpiod_set_consumer_name(gpiod, "GPIO extension data");
+               data[i] = gpiod;
        }
        gpio_ext->data = data;
        gpio_ext->num_data = num_data;
 
-       ret = of_get_named_gpio(np, "enable-gpio", 0);
-       if (ret < 0) {
+       gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW);
+       if (IS_ERR(gpiod)) {
                dev_err(dev,
                        "Failed to get GPIO from DT property enable-gpio\n");
-               return ret;
+               return PTR_ERR(gpiod);
        }
-       gpio_ext->enable = ret;
+       gpiod_set_consumer_name(gpiod, "GPIO extension enable");
+       gpio_ext->enable = gpiod;
 
-       return 0;
+       return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext);
 }
 
 static int netxbig_leds_get_of_pdata(struct device *dev,
@@ -411,6 +421,8 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
 {
        struct device_node *np = dev->of_node;
        struct device_node *gpio_ext_np;
+       struct platform_device *gpio_ext_pdev;
+       struct device *gpio_ext_dev;
        struct device_node *child;
        struct netxbig_gpio_ext *gpio_ext;
        struct netxbig_led_timer *timers;
@@ -426,13 +438,19 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
                dev_err(dev, "Failed to get DT handle gpio-ext\n");
                return -EINVAL;
        }
+       gpio_ext_pdev = of_find_device_by_node(gpio_ext_np);
+       if (!gpio_ext_pdev) {
+               dev_err(dev, "Failed to find platform device for gpio-ext\n");
+               return -ENODEV;
+       }
+       gpio_ext_dev = &gpio_ext_pdev->dev;
 
        gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL);
        if (!gpio_ext) {
                of_node_put(gpio_ext_np);
                return -ENOMEM;
        }
-       ret = gpio_ext_get_of_pdata(dev, gpio_ext_np, gpio_ext);
+       ret = netxbig_gpio_ext_get(dev, gpio_ext_dev, gpio_ext);
        of_node_put(gpio_ext_np);
        if (ret)
                return ret;
@@ -585,10 +603,6 @@ static int netxbig_led_probe(struct platform_device *pdev)
        if (!leds_data)
                return -ENOMEM;
 
-       ret = gpio_ext_init(pdev, pdata->gpio_ext);
-       if (ret < 0)
-               return ret;
-
        for (i = 0; i < pdata->num_leds; i++) {
                ret = create_netxbig_led(pdev, pdata,
                                         &leds_data[i], &pdata->leds[i]);