Merge tag 'gpio-updates-for-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 25 Mar 2022 19:28:23 +0000 (12:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 25 Mar 2022 19:28:23 +0000 (12:28 -0700)
Pull gpio updates from Bartosz Golaszewski:
 "Relatively few updates for this release cycle. We have a single new
  driver and some minor changes in drivers, more work on limiting the
  usage of of_node in drivers and DT updates:

   - new driver: gpio-en7523

   - dt-bindings: convertion of faraday,ftgpio010 to YAML, new
     compatible string in gpio-vf610 and a bugfix in an example

   - gpiolib core: several improvements and some code shrink

   - documentation: convert all public docs into kerneldoc format

   - set IRQ bus token in gpio-crystalcove (addresses a debugfs issue)

   - add a missing return value check for kstrdup() in gpio-merrifield

   - allow gpio-tps68470 to be built as module

   - more work on limiting usage of of_node in GPIO drivers

   - several sysfs interface improvements

   - use SDPX in gpio-ts4900"

* tag 'gpio-updates-for-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: ts4900: Use SPDX header
  gpiolib: Use list_first_entry()/list_last_entry()
  gpiolib: sysfs: Simplify edge handling in the code
  gpiolib: sysfs: Move kstrtox() calls outside of the mutex lock
  gpiolib: sysfs: Move sysfs_emit() calls outside of the mutex lock
  gpiolib: make struct comments into real kernel docs
  dt-bindings: gpio: convert faraday,ftgpio01 to yaml
  dt-bindings: gpio: gpio-vf610: Add imx93 compatible string
  gpiolib: Simplify error path in gpiod_get_index() when requesting GPIO
  gpiolib: Use short form of ternary operator in gpiod_get_index()
  gpiolib: Introduce for_each_gpio_desc_with_flag() macro
  gpio: Add support for Airoha EN7523 GPIO controller
  dt-bindings: arm: airoha: Add binding for Airoha GPIO controller
  dt-bindings: gpio: fix gpio-hog example
  gpio: tps68470: Allow building as module
  gpio: tegra: Get rid of duplicate of_node assignment
  gpio: altera-a10sr: Switch to use fwnode instead of of_node
  gpio: merrifield: check the return value of devm_kstrdup()
  gpio: crystalcove: Set IRQ domain bus token to DOMAIN_BUS_WIRED

19 files changed:
Documentation/devicetree/bindings/gpio/airoha,en7523-gpio.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/faraday,ftgpio010.txt [deleted file]
Documentation/devicetree/bindings/gpio/faraday,ftgpio010.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-vf610.yaml
Documentation/devicetree/bindings/gpio/gpio.txt
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-altera-a10sr.c
drivers/gpio/gpio-crystalcove.c
drivers/gpio/gpio-en7523.c [new file with mode: 0644]
drivers/gpio/gpio-merrifield.c
drivers/gpio/gpio-tegra.c
drivers/gpio/gpio-tps68470.c
drivers/gpio/gpio-ts4900.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib-sysfs.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
include/linux/gpio/consumer.h

diff --git a/Documentation/devicetree/bindings/gpio/airoha,en7523-gpio.yaml b/Documentation/devicetree/bindings/gpio/airoha,en7523-gpio.yaml
new file mode 100644 (file)
index 0000000..7c41d8e
--- /dev/null
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/airoha,en7523-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha EN7523 GPIO controller
+
+maintainers:
+  - John Crispin <john@phrozen.org>
+
+description: |
+  Airoha's GPIO controller on their ARM EN7523 SoCs consists of two banks of 32
+  GPIOs.
+
+properties:
+  $nodename:
+    pattern: "^gpio@[0-9a-f]+$"
+
+  compatible:
+    items:
+      - const: airoha,en7523-gpio
+
+  reg:
+    description: |
+      The first tuple points to the input register.
+      The second and third tuple point to the direction registers
+      The fourth tuple points to the output register
+    maxItems: 4
+
+  "#gpio-cells":
+    const: 2
+
+  gpio-controller: true
+
+required:
+  - compatible
+  - reg
+  - "#gpio-cells"
+  - gpio-controller
+
+additionalProperties: false
+
+examples:
+  - |
+    gpio0: gpio@1fbf0200 {
+        compatible = "airoha,en7523-gpio";
+        reg = <0x1fbf0204 0x4>,
+              <0x1fbf0200 0x4>,
+              <0x1fbf0220 0x4>,
+              <0x1fbf0214 0x4>;
+        gpio-controller;
+        #gpio-cells = <2>;
+    };
+
+    gpio1: gpio@1fbf0270 {
+        compatible = "airoha,en7523-gpio";
+        reg = <0x1fbf0270 0x4>,
+              <0x1fbf0260 0x4>,
+              <0x1fbf0264 0x4>,
+              <0x1fbf0278 0x4>;
+        gpio-controller;
+        #gpio-cells = <2>;
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.txt b/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.txt
deleted file mode 100644 (file)
index d042365..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-Faraday Technology FTGPIO010 GPIO Controller
-
-Required properties:
-
-- compatible : Should be one of
-  "cortina,gemini-gpio", "faraday,ftgpio010"
-  "moxa,moxart-gpio", "faraday,ftgpio010"
-  "faraday,ftgpio010"
-- reg : Should contain registers location and length
-- interrupts : Should contain the interrupt line for the GPIO block
-- gpio-controller : marks this as a GPIO controller
-- #gpio-cells : Should be 2, see gpio/gpio.txt
-- interrupt-controller : marks this as an interrupt controller
-- #interrupt-cells : a standard two-cell interrupt flag, see
-  interrupt-controller/interrupts.txt
-
-Example:
-
-gpio@4d000000 {
-       compatible = "cortina,gemini-gpio", "faraday,ftgpio010";
-       reg = <0x4d000000 0x100>;
-       interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
-       gpio-controller;
-       #gpio-cells = <2>;
-       interrupt-controller;
-       #interrupt-cells = <2>;
-};
diff --git a/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.yaml b/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.yaml
new file mode 100644 (file)
index 0000000..640da5b
--- /dev/null
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/faraday,ftgpio010.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Faraday Technology FTGPIO010 GPIO Controller
+
+maintainers:
+  - Linus Walleij <linus.walleij@linaro.org>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - const: cortina,gemini-gpio
+          - const: faraday,ftgpio010
+      - items:
+          - const: moxa,moxart-gpio
+          - const: faraday,ftgpio010
+      - const: faraday,ftgpio010
+
+  reg:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+    description: Should contain the interrupt line for the GPIO block
+
+  gpio-controller: true
+  "#gpio-cells":
+    const: 2
+
+  interrupt-controller: true
+  "#interrupt-cells":
+    const: 2
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#gpio-cells"
+  - interrupt-controller
+  - "#interrupt-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    gpio@4d000000 {
+      compatible = "cortina,gemini-gpio", "faraday,ftgpio010";
+      reg = <0x4d000000 0x100>;
+      interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
+      gpio-controller;
+      #gpio-cells = <2>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+    };
index e135939..d2c39db 100644 (file)
@@ -25,7 +25,9 @@ properties:
           - const: fsl,imx7ulp-gpio
           - const: fsl,vf610-gpio
       - items:
-          - const: fsl,imx8ulp-gpio
+          - enum:
+              - fsl,imx93-gpio
+              - fsl,imx8ulp-gpio
           - const: fsl,imx7ulp-gpio
 
   reg:
index a8895d3..5663e71 100644 (file)
@@ -213,7 +213,7 @@ Example of two SOC GPIO banks defined as gpio-controller nodes:
                gpio-controller;
                #gpio-cells = <2>;
 
-               line_b {
+               line_b-hog {
                        gpio-hog;
                        gpios = <6 0>;
                        output-low;
index 1c211b4..b2d313f 100644 (file)
@@ -247,6 +247,16 @@ config GPIO_EM
        help
          Say yes here to support GPIO on Renesas Emma Mobile SoCs.
 
+config GPIO_EN7523
+       tristate "Airoha GPIO support"
+       depends on ARCH_AIROHA
+       default ARCH_AIROHA
+       select GPIO_GENERIC
+       select GPIOLIB_IRQCHIP
+       help
+         Say Y or M here to support the GPIO controller block on the
+         Airoha EN7523 SoC. It supports two banks of 32 GPIOs.
+
 config GPIO_EP93XX
        def_bool y
        depends on ARCH_EP93XX
@@ -1380,7 +1390,7 @@ config GPIO_TPS65912
          This driver supports TPS65912 GPIO chip.
 
 config GPIO_TPS68470
-       bool "TPS68470 GPIO"
+       tristate "TPS68470 GPIO"
        depends on INTEL_SKL_INT3472
        help
          Select this option to enable GPIO driver for the TPS68470
@@ -1390,10 +1400,6 @@ config GPIO_TPS68470
          input or output as appropriate, the sensor related GPIOs
          are "output only" GPIOs.
 
-         This driver config is bool, as the GPIO functionality
-         of the TPS68470 must be available before dependent
-         drivers are loaded.
-
 config GPIO_TQMX86
        tristate "TQ-Systems QTMX86 GPIO"
        depends on MFD_TQMX86 || COMPILE_TEST
index edbaa3c..1a14e24 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_DLN2)                       += gpio-dln2.o
 obj-$(CONFIG_GPIO_DWAPB)               += gpio-dwapb.o
 obj-$(CONFIG_GPIO_EIC_SPRD)            += gpio-eic-sprd.o
 obj-$(CONFIG_GPIO_EM)                  += gpio-em.o
+obj-$(CONFIG_GPIO_EN7523)              += gpio-en7523.o
 obj-$(CONFIG_GPIO_EP93XX)              += gpio-ep93xx.o
 obj-$(CONFIG_GPIO_EXAR)                        += gpio-exar.o
 obj-$(CONFIG_GPIO_F7188X)              += gpio-f7188x.o
index 6af51fe..be1ed7e 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/gpio/driver.h>
 #include <linux/mfd/altera-a10sr.h>
 #include <linux/module.h>
+#include <linux/property.h>
 
 /**
  * struct altr_a10sr_gpio - Altera Max5 GPIO device private data structure
@@ -88,7 +89,7 @@ static int altr_a10sr_gpio_probe(struct platform_device *pdev)
 
        gpio->gp = altr_a10sr_gc;
        gpio->gp.parent = pdev->dev.parent;
-       gpio->gp.of_node = pdev->dev.of_node;
+       gpio->gp.fwnode = dev_fwnode(&pdev->dev);
 
        return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
 }
index 5a909f3..b55c74a 100644 (file)
@@ -370,7 +370,14 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
                return retval;
        }
 
-       return devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
+       retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
+       if (retval)
+               return retval;
+
+       /* Distuingish IRQ domain from others sharing (MFD) the same fwnode */
+       irq_domain_update_bus_token(cg->chip.irq.domain, DOMAIN_BUS_WIRED);
+
+       return 0;
 }
 
 static struct platform_driver crystalcove_gpio_driver = {
diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c
new file mode 100644 (file)
index 0000000..f836a8d
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/bits.h>
+#include <linux/gpio/driver.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define AIROHA_GPIO_MAX                32
+
+/**
+ * airoha_gpio_ctrl - Airoha GPIO driver data
+ * @gc: Associated gpio_chip instance.
+ * @data: The data register.
+ * @dir0: The direction register for the lower 16 pins.
+ * @dir1: The direction register for the higher 16 pins.
+ * @output: The output enable register.
+ */
+struct airoha_gpio_ctrl {
+       struct gpio_chip gc;
+       void __iomem *data;
+       void __iomem *dir[2];
+       void __iomem *output;
+};
+
+static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc)
+{
+       return container_of(gc, struct airoha_gpio_ctrl, gc);
+}
+
+static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio,
+                         int val, int out)
+{
+       struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc);
+       u32 dir = ioread32(ctrl->dir[gpio / 16]);
+       u32 output = ioread32(ctrl->output);
+       u32 mask = BIT((gpio % 16) * 2);
+
+       if (out) {
+               dir |= mask;
+               output |= BIT(gpio);
+       } else {
+               dir &= ~mask;
+               output &= ~BIT(gpio);
+       }
+
+       iowrite32(dir, ctrl->dir[gpio / 16]);
+
+       if (out)
+               gc->set(gc, gpio, val);
+
+       iowrite32(output, ctrl->output);
+
+       return 0;
+}
+
+static int airoha_dir_out(struct gpio_chip *gc, unsigned int gpio,
+                         int val)
+{
+       return airoha_dir_set(gc, gpio, val, 1);
+}
+
+static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+       return airoha_dir_set(gc, gpio, 0, 0);
+}
+
+static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc);
+       u32 dir = ioread32(ctrl->dir[gpio / 16]);
+       u32 mask = BIT((gpio % 16) * 2);
+
+       return (dir & mask) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int airoha_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct airoha_gpio_ctrl *ctrl;
+       int err;
+
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       ctrl->data = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ctrl->data))
+               return PTR_ERR(ctrl->data);
+
+       ctrl->dir[0] = devm_platform_ioremap_resource(pdev, 1);
+       if (IS_ERR(ctrl->dir[0]))
+               return PTR_ERR(ctrl->dir[0]);
+
+       ctrl->dir[1] = devm_platform_ioremap_resource(pdev, 2);
+       if (IS_ERR(ctrl->dir[1]))
+               return PTR_ERR(ctrl->dir[1]);
+
+       ctrl->output = devm_platform_ioremap_resource(pdev, 3);
+       if (IS_ERR(ctrl->output))
+               return PTR_ERR(ctrl->output);
+
+       err = bgpio_init(&ctrl->gc, dev, 4, ctrl->data, NULL,
+                        NULL, NULL, NULL, 0);
+       if (err)
+               return dev_err_probe(dev, err, "unable to init generic GPIO");
+
+       ctrl->gc.ngpio = AIROHA_GPIO_MAX;
+       ctrl->gc.owner = THIS_MODULE;
+       ctrl->gc.direction_output = airoha_dir_out;
+       ctrl->gc.direction_input = airoha_dir_in;
+       ctrl->gc.get_direction = airoha_get_dir;
+
+       return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
+}
+
+static const struct of_device_id airoha_gpio_of_match[] = {
+       { .compatible = "airoha,en7523-gpio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, airoha_gpio_of_match);
+
+static struct platform_driver airoha_gpio_driver = {
+       .driver = {
+               .name = "airoha-gpio",
+               .of_match_table = airoha_gpio_of_match,
+       },
+       .probe = airoha_gpio_probe,
+};
+module_platform_driver(airoha_gpio_driver);
+
+MODULE_DESCRIPTION("Airoha GPIO support");
+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
+MODULE_LICENSE("GPL v2");
index 42c4d9d..f3d1bae 100644 (file)
@@ -409,6 +409,9 @@ static int mrfld_gpio_add_pin_ranges(struct gpio_chip *chip)
        int retval;
 
        pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv);
+       if (!pinctrl_dev_name)
+               return -ENOMEM;
+
        for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
                range = &mrfld_gpio_ranges[i];
                retval = gpiochip_add_pin_range(&priv->chip, pinctrl_dev_name,
index 7f5bc10..ff2d2a1 100644 (file)
@@ -691,7 +691,6 @@ static int tegra_gpio_probe(struct platform_device *pdev)
        tgi->gc.base                    = 0;
        tgi->gc.ngpio                   = tgi->bank_count * 32;
        tgi->gc.parent                  = &pdev->dev;
-       tgi->gc.of_node                 = pdev->dev.of_node;
 
        tgi->ic.name                    = "GPIO";
        tgi->ic.irq_ack                 = tegra_gpio_irq_ack;
index 423b7bc..aaddcab 100644 (file)
@@ -154,5 +154,8 @@ static struct platform_driver tps68470_gpio_driver = {
        },
        .probe = tps68470_gpio_probe,
 };
+module_platform_driver(tps68470_gpio_driver);
 
-builtin_platform_driver(tps68470_gpio_driver)
+MODULE_ALIAS("platform:tps68470-gpio");
+MODULE_DESCRIPTION("GPIO driver for TPS68470 PMIC");
+MODULE_LICENSE("GPL v2");
index d918d2d..69854fd 100644 (file)
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Digital I/O driver for Technologic Systems I2C FPGA Core
  *
  * Copyright (C) 2015, 2018 Technologic Systems
  * Copyright (C) 2016 Savoir-Faire Linux
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether expressed or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License version 2 for more details.
  */
 
 #include <linux/gpio/driver.h>
index 91dcf2c..ae1ce31 100644 (file)
@@ -711,14 +711,12 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
 static void of_gpiochip_remove_hog(struct gpio_chip *chip,
                                   struct device_node *hog)
 {
-       struct gpio_desc *descs = chip->gpiodev->descs;
+       struct gpio_desc *desc;
        unsigned int i;
 
-       for (i = 0; i < chip->ngpio; i++) {
-               if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) &&
-                   descs[i].hog == hog)
-                       gpiochip_free_own_desc(&descs[i]);
-       }
+       for_each_gpio_desc_with_flag(i, chip, desc, FLAG_IS_HOGGED)
+               if (desc->hog == hog)
+                       gpiochip_free_own_desc(desc);
 }
 
 static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
index 44c1ad5..d44ffea 100644 (file)
@@ -13,6 +13,7 @@
 #include "gpiolib.h"
 #include "gpiolib-sysfs.h"
 
+#define GPIO_IRQF_TRIGGER_NONE         0
 #define GPIO_IRQF_TRIGGER_FALLING      BIT(0)
 #define GPIO_IRQF_TRIGGER_RISING       BIT(1)
 #define GPIO_IRQF_TRIGGER_BOTH         (GPIO_IRQF_TRIGGER_FALLING | \
@@ -61,17 +62,16 @@ static ssize_t direction_show(struct device *dev,
 {
        struct gpiod_data *data = dev_get_drvdata(dev);
        struct gpio_desc *desc = data->desc;
-       ssize_t                 status;
+       int value;
 
        mutex_lock(&data->mutex);
 
        gpiod_get_direction(desc);
-       status = sysfs_emit(buf, "%s\n",
-                           test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in");
+       value = !!test_bit(FLAG_IS_OUT, &desc->flags);
 
        mutex_unlock(&data->mutex);
 
-       return status;
+       return sysfs_emit(buf, "%s\n", value ? "out" : "in");
 }
 
 static ssize_t direction_store(struct device *dev,
@@ -108,12 +108,13 @@ static ssize_t value_show(struct device *dev,
        mutex_lock(&data->mutex);
 
        status = gpiod_get_value_cansleep(desc);
-       if (status >= 0)
-               status = sysfs_emit(buf, "%zd\n", status);
 
        mutex_unlock(&data->mutex);
 
-       return status;
+       if (status < 0)
+               return status;
+
+       return sysfs_emit(buf, "%zd\n", status);
 }
 
 static ssize_t value_store(struct device *dev,
@@ -121,24 +122,18 @@ static ssize_t value_store(struct device *dev,
 {
        struct gpiod_data *data = dev_get_drvdata(dev);
        struct gpio_desc *desc = data->desc;
-       ssize_t status = 0;
+       ssize_t status;
+       long value;
+
+       status = kstrtol(buf, 0, &value);
 
        mutex_lock(&data->mutex);
 
        if (!test_bit(FLAG_IS_OUT, &desc->flags)) {
                status = -EPERM;
-       } else {
-               long            value;
-
-               if (size <= 2 && isdigit(buf[0]) &&
-                   (size == 1 || buf[1] == '\n'))
-                       value = buf[0] - '0';
-               else
-                       status = kstrtol(buf, 0, &value);
-               if (status == 0) {
-                       gpiod_set_value_cansleep(desc, value);
-                       status = size;
-               }
+       } else if (status == 0) {
+               gpiod_set_value_cansleep(desc, value);
+               status = size;
        }
 
        mutex_unlock(&data->mutex);
@@ -224,54 +219,41 @@ static void gpio_sysfs_free_irq(struct device *dev)
        sysfs_put(data->value_kn);
 }
 
-static const struct {
-       const char *name;
-       unsigned char flags;
-} trigger_types[] = {
-       { "none",    0 },
-       { "falling", GPIO_IRQF_TRIGGER_FALLING },
-       { "rising",  GPIO_IRQF_TRIGGER_RISING },
-       { "both",    GPIO_IRQF_TRIGGER_BOTH },
+static const char * const trigger_names[] = {
+       [GPIO_IRQF_TRIGGER_NONE]        = "none",
+       [GPIO_IRQF_TRIGGER_FALLING]     = "falling",
+       [GPIO_IRQF_TRIGGER_RISING]      = "rising",
+       [GPIO_IRQF_TRIGGER_BOTH]        = "both",
 };
 
 static ssize_t edge_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        struct gpiod_data *data = dev_get_drvdata(dev);
-       ssize_t status = 0;
-       int i;
+       int flags;
 
        mutex_lock(&data->mutex);
 
-       for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
-               if (data->irq_flags == trigger_types[i].flags)
-                       break;
-       }
-       if (i < ARRAY_SIZE(trigger_types))
-               status = sysfs_emit(buf, "%s\n", trigger_types[i].name);
+       flags = data->irq_flags;
 
        mutex_unlock(&data->mutex);
 
-       return status;
+       if (flags >= ARRAY_SIZE(trigger_names))
+               return 0;
+
+       return sysfs_emit(buf, "%s\n", trigger_names[flags]);
 }
 
 static ssize_t edge_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
        struct gpiod_data *data = dev_get_drvdata(dev);
-       unsigned char flags;
        ssize_t status = size;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
-               if (sysfs_streq(trigger_types[i].name, buf))
-                       break;
-       }
-
-       if (i == ARRAY_SIZE(trigger_types))
-               return -EINVAL;
+       int flags;
 
-       flags = trigger_types[i].flags;
+       flags = sysfs_match_string(trigger_names, buf);
+       if (flags < 0)
+               return flags;
 
        mutex_lock(&data->mutex);
 
@@ -324,16 +306,15 @@ static ssize_t active_low_show(struct device *dev,
 {
        struct gpiod_data *data = dev_get_drvdata(dev);
        struct gpio_desc *desc = data->desc;
-       ssize_t                 status;
+       int value;
 
        mutex_lock(&data->mutex);
 
-       status = sysfs_emit(buf, "%d\n",
-                           !!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
+       value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags);
 
        mutex_unlock(&data->mutex);
 
-       return status;
+       return sysfs_emit(buf, "%d\n", value);
 }
 
 static ssize_t active_low_store(struct device *dev,
@@ -343,11 +324,13 @@ static ssize_t active_low_store(struct device *dev,
        ssize_t                 status;
        long                    value;
 
+       status = kstrtol(buf, 0, &value);
+       if (status)
+               return status;
+
        mutex_lock(&data->mutex);
 
-       status = kstrtol(buf, 0, &value);
-       if (status == 0)
-               status = gpio_sysfs_set_active_low(dev, value);
+       status = gpio_sysfs_set_active_low(dev, value);
 
        mutex_unlock(&data->mutex);
 
@@ -790,11 +773,8 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
        mutex_unlock(&sysfs_lock);
 
        /* unregister gpiod class devices owned by sysfs */
-       for (i = 0; i < chip->ngpio; i++) {
-               desc = &gdev->descs[i];
-               if (test_and_clear_bit(FLAG_SYSFS, &desc->flags))
-                       gpiod_free(desc);
-       }
+       for_each_gpio_desc_with_flag(i, chip, desc, FLAG_SYSFS)
+               gpiod_free(desc);
 }
 
 static int __init gpiolib_sysfs_init(void)
index 6630d92..e59884c 100644 (file)
@@ -262,14 +262,14 @@ static int gpiodev_add_to_list(struct gpio_device *gdev)
                return 0;
        }
 
-       next = list_entry(gpio_devices.next, struct gpio_device, list);
+       next = list_first_entry(&gpio_devices, struct gpio_device, list);
        if (gdev->base + gdev->ngpio <= next->base) {
                /* add before first entry */
                list_add(&gdev->list, &gpio_devices);
                return 0;
        }
 
-       prev = list_entry(gpio_devices.prev, struct gpio_device, list);
+       prev = list_last_entry(&gpio_devices, struct gpio_device, list);
        if (prev->base + prev->ngpio <= gdev->base) {
                /* add behind last entry */
                list_add_tail(&gdev->list, &gpio_devices);
@@ -3951,23 +3951,21 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
         * If a connection label was passed use that, else attempt to use
         * the device name as label
         */
-       ret = gpiod_request(desc, con_id ? con_id : devname);
+       ret = gpiod_request(desc, con_id ?: devname);
        if (ret) {
-               if (ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) {
-                       /*
-                        * This happens when there are several consumers for
-                        * the same GPIO line: we just return here without
-                        * further initialization. It is a bit if a hack.
-                        * This is necessary to support fixed regulators.
-                        *
-                        * FIXME: Make this more sane and safe.
-                        */
-                       dev_info(dev, "nonexclusive access to GPIO for %s\n",
-                                con_id ? con_id : devname);
-                       return desc;
-               } else {
+               if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
                        return ERR_PTR(ret);
-               }
+
+               /*
+                * This happens when there are several consumers for
+                * the same GPIO line: we just return here without
+                * further initialization. It is a bit of a hack.
+                * This is necessary to support fixed regulators.
+                *
+                * FIXME: Make this more sane and safe.
+                */
+               dev_info(dev, "nonexclusive access to GPIO for %s\n", con_id ?: devname);
+               return desc;
        }
 
        ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
@@ -4122,12 +4120,11 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
  */
 static void gpiochip_free_hogs(struct gpio_chip *gc)
 {
+       struct gpio_desc *desc;
        int id;
 
-       for (id = 0; id < gc->ngpio; id++) {
-               if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags))
-                       gpiochip_free_own_desc(&gc->gpiodev->descs[id]);
-       }
+       for_each_gpio_desc_with_flag(id, gc, desc, FLAG_IS_HOGGED)
+               gpiochip_free_own_desc(desc);
 }
 
 /**
@@ -4446,7 +4443,7 @@ static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
        if (list_is_last(&gdev->list, &gpio_devices))
                ret = NULL;
        else
-               ret = list_entry(gdev->list.next, struct gpio_device, list);
+               ret = list_first_entry(&gdev->list, struct gpio_device, list);
        spin_unlock_irqrestore(&gpio_lock, flags);
 
        s->private = "\n";
index c31f462..06f3faa 100644 (file)
@@ -37,6 +37,9 @@
  * or name of the IP component in a System on Chip.
  * @data: per-instance data assigned by the driver
  * @list: links gpio_device:s together for traversal
+ * @notifier: used to notify subscribers about lines being requested, released
+ *            or reconfigured
+ * @pin_ranges: range of pins served by the GPIO driver
  *
  * This state container holds most of the runtime variable data
  * for a GPIO device and can hold references and live on after the
@@ -72,6 +75,20 @@ struct gpio_device {
 /* gpio suffixes used for ACPI and device tree lookup */
 static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
 
+/**
+ * struct gpio_array - Opaque descriptor for a structure of GPIO array attributes
+ *
+ * @desc:              Array of pointers to the GPIO descriptors
+ * @size:              Number of elements in desc
+ * @chip:              Parent GPIO chip
+ * @get_mask:          Get mask used in fastpath
+ * @set_mask:          Set mask used in fastpath
+ * @invert_mask:       Invert mask used in fastpath
+ *
+ * This structure is attached to struct gpiod_descs obtained from
+ * gpiod_get_array() and can be passed back to get/set array functions in order
+ * to activate fast processing path if applicable.
+ */
 struct gpio_array {
        struct gpio_desc        **desc;
        unsigned int            size;
@@ -82,6 +99,13 @@ struct gpio_array {
 };
 
 struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
+
+#define for_each_gpio_desc_with_flag(i, gc, desc, flag)                \
+       for (i = 0, desc = gpiochip_get_desc(gc, i);            \
+            i < gc->ngpio;                                     \
+            i++, desc = gpiochip_get_desc(gc, i))              \
+               if (!test_bit(flag, &desc->flags)) {} else
+
 int gpiod_get_array_value_complex(bool raw, bool can_sleep,
                                  unsigned int array_size,
                                  struct gpio_desc **desc_array,
@@ -96,6 +120,23 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 extern spinlock_t gpio_lock;
 extern struct list_head gpio_devices;
 
+
+/**
+ * struct gpio_desc - Opaque descriptor for a GPIO
+ *
+ * @gdev:              Pointer to the parent GPIO device
+ * @flags:             Binary descriptor flags
+ * @label:             Name of the consumer
+ * @name:              Line name
+ * @hog:               Pointer to the device node that hogs this line (if any)
+ * @debounce_period_us:        Debounce period in microseconds
+ *
+ * These are obtained using gpiod_get() and are preferable to the old
+ * integer-based handles.
+ *
+ * Contrary to integers, a pointer to a &struct gpio_desc is guaranteed to be
+ * valid until the GPIO is released.
+ */
 struct gpio_desc {
        struct gpio_device      *gdev;
        unsigned long           flags;
index 3ad67b4..c3aa8b3 100644 (file)
@@ -8,27 +8,16 @@
 #include <linux/err.h>
 
 struct device;
-
-/**
- * Opaque descriptor for a GPIO. These are obtained using gpiod_get() and are
- * preferable to the old integer-based handles.
- *
- * Contrary to integers, a pointer to a gpio_desc is guaranteed to be valid
- * until the GPIO is released.
- */
 struct gpio_desc;
-
-/**
- * Opaque descriptor for a structure of GPIO array attributes.  This structure
- * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
- * passed back to get/set array functions in order to activate fast processing
- * path if applicable.
- */
 struct gpio_array;
 
 /**
- * Struct containing an array of descriptors that can be obtained using
- * gpiod_get_array().
+ * struct gpio_descs - Struct containing an array of descriptors that can be
+ *                     obtained using gpiod_get_array()
+ *
+ * @info:      Pointer to the opaque gpio_array structure
+ * @ndescs:    Number of held descriptors
+ * @desc:      Array of pointers to GPIO descriptors
  */
 struct gpio_descs {
        struct gpio_array *info;
@@ -43,8 +32,16 @@ struct gpio_descs {
 #define GPIOD_FLAGS_BIT_NONEXCLUSIVE   BIT(4)
 
 /**
- * Optional flags that can be passed to one of gpiod_* to configure direction
- * and output value. These values cannot be OR'd.
+ * enum gpiod_flags - Optional flags that can be passed to one of gpiod_* to
+ *                    configure direction and output value. These values
+ *                    cannot be OR'd.
+ *
+ * @GPIOD_ASIS:                        Don't change anything
+ * @GPIOD_IN:                  Set lines to input mode
+ * @GPIOD_OUT_LOW:             Set lines to output and drive them low
+ * @GPIOD_OUT_HIGH:            Set lines to output and drive them high
+ * @GPIOD_OUT_LOW_OPEN_DRAIN:  Set lines to open-drain output and drive them low
+ * @GPIOD_OUT_HIGH_OPEN_DRAIN: Set lines to open-drain output and drive them high
  */
 enum gpiod_flags {
        GPIOD_ASIS      = 0,