]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
gpio: move hogs into GPIO core
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 9 Mar 2026 12:42:38 +0000 (13:42 +0100)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 16 Mar 2026 08:51:05 +0000 (09:51 +0100)
Refactor line hogging code by moving the parts duplicated in
gpiolib-acpi-core.c and gpiolib-of.c into gpiolib.c, leaving just the
OF-specific bits in the latter.

This makes fwnode the primary API for setting up hogs and allows to use
software nodes in addition to ACPI and OF nodes.

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20260309-gpio-hog-fwnode-v2-2-4e61f3dbf06a@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpiolib-acpi-core.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib-of.h
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h

index ced6375d1badf9e113e708ce4bc9f83071f9acca..09f860200a059b1d17c652b9aa66a49abea3cb4f 100644 (file)
@@ -1220,75 +1220,6 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
        }
 }
 
-static struct gpio_desc *
-acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip,
-                            struct fwnode_handle *fwnode,
-                            const char **name,
-                            unsigned long *lflags,
-                            enum gpiod_flags *dflags)
-{
-       struct gpio_chip *chip = achip->chip;
-       struct gpio_desc *desc;
-       u32 gpios[2];
-       int ret;
-
-       *lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
-       *dflags = GPIOD_ASIS;
-       *name = NULL;
-
-       ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios,
-                                            ARRAY_SIZE(gpios));
-       if (ret < 0)
-               return ERR_PTR(ret);
-
-       desc = gpiochip_get_desc(chip, gpios[0]);
-       if (IS_ERR(desc))
-               return desc;
-
-       if (gpios[1])
-               *lflags |= GPIO_ACTIVE_LOW;
-
-       if (fwnode_property_present(fwnode, "input"))
-               *dflags |= GPIOD_IN;
-       else if (fwnode_property_present(fwnode, "output-low"))
-               *dflags |= GPIOD_OUT_LOW;
-       else if (fwnode_property_present(fwnode, "output-high"))
-               *dflags |= GPIOD_OUT_HIGH;
-       else
-               return ERR_PTR(-EINVAL);
-
-       fwnode_property_read_string(fwnode, "line-name", name);
-
-       return desc;
-}
-
-static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip)
-{
-       struct gpio_chip *chip = achip->chip;
-
-       device_for_each_child_node_scoped(chip->parent, fwnode) {
-               unsigned long lflags;
-               enum gpiod_flags dflags;
-               struct gpio_desc *desc;
-               const char *name;
-               int ret;
-
-               if (!fwnode_property_present(fwnode, "gpio-hog"))
-                       continue;
-
-               desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name,
-                                                   &lflags, &dflags);
-               if (IS_ERR(desc))
-                       continue;
-
-               ret = gpiod_hog(desc, name, lflags, dflags);
-               if (ret) {
-                       dev_err(chip->parent, "Failed to hog GPIO\n");
-                       return;
-               }
-       }
-}
-
 void acpi_gpiochip_add(struct gpio_chip *chip)
 {
        struct acpi_gpio_chip *acpi_gpio;
@@ -1321,7 +1252,6 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
        }
 
        acpi_gpiochip_request_regions(acpi_gpio);
-       acpi_gpiochip_scan_gpios(acpi_gpio);
        acpi_dev_clear_dependencies(adev);
 }
 
index b5610a847e726add0331aaa1066a31d971a7fb40..2c923d17541f26586d531713d7f1fc917cdceaf4 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/fwnode.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -712,139 +713,26 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
        return desc;
 }
 
-/**
- * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
- * @np:                device node to get GPIO from
- * @chip:      GPIO chip whose hog is parsed
- * @idx:       Index of the GPIO to parse
- * @name:      GPIO line name
- * @lflags:    bitmask of gpio_lookup_flags GPIO_* values - returned from
- *             of_find_gpio() or of_parse_own_gpio()
- * @dflags:    gpiod_flags - optional GPIO initialization flags
- *
- * Returns:
- * GPIO descriptor to use with Linux GPIO API, or one of the errno
- * value on the error condition.
- */
-static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
-                                          struct gpio_chip *chip,
-                                          unsigned int idx, const char **name,
-                                          unsigned long *lflags,
-                                          enum gpiod_flags *dflags)
+int of_gpiochip_get_lflags(struct gpio_chip *chip,
+                          struct fwnode_reference_args *gpiospec,
+                          unsigned long *lflags)
 {
-       struct device_node *chip_np;
        enum of_gpio_flags xlate_flags;
-       struct of_phandle_args gpiospec;
+       struct of_phandle_args args;
        struct gpio_desc *desc;
-       unsigned int i;
-       u32 tmp;
-       int ret;
-
-       chip_np = dev_of_node(&chip->gpiodev->dev);
-       if (!chip_np)
-               return ERR_PTR(-EINVAL);
-
-       xlate_flags = 0;
-       *lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
-       *dflags = GPIOD_ASIS;
 
-       ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
-       if (ret)
-               return ERR_PTR(ret);
+       args.np = to_of_node(gpiospec->fwnode);
+       args.args_count = gpiospec->nargs;
 
-       gpiospec.np = chip_np;
-       gpiospec.args_count = tmp;
-
-       for (i = 0; i < tmp; i++) {
-               ret = of_property_read_u32_index(np, "gpios", idx * tmp + i,
-                                                &gpiospec.args[i]);
-               if (ret)
-                       return ERR_PTR(ret);
-       }
+       for (int i = 0; i < args.args_count; i++)
+               args.args[i] = gpiospec->args[i];
 
-       desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
+       desc = of_xlate_and_get_gpiod_flags(chip, &args, &xlate_flags);
        if (IS_ERR(desc))
-               return desc;
+               return PTR_ERR(desc);
 
        *lflags = of_convert_gpio_flags(xlate_flags);
 
-       if (of_property_read_bool(np, "input"))
-               *dflags |= GPIOD_IN;
-       else if (of_property_read_bool(np, "output-low"))
-               *dflags |= GPIOD_OUT_LOW;
-       else if (of_property_read_bool(np, "output-high"))
-               *dflags |= GPIOD_OUT_HIGH;
-       else {
-               pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n",
-                       desc_to_gpio(desc), np);
-               return ERR_PTR(-EINVAL);
-       }
-
-       if (name && of_property_read_string(np, "line-name", name))
-               *name = np->name;
-
-       return desc;
-}
-
-/**
- * of_gpiochip_add_hog - Add all hogs in a hog device node
- * @chip:      gpio chip to act on
- * @hog:       device node describing the hogs
- *
- * Returns:
- * 0 on success, or negative errno on failure.
- */
-static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
-{
-       enum gpiod_flags dflags;
-       struct gpio_desc *desc;
-       unsigned long lflags;
-       const char *name;
-       unsigned int i;
-       int ret;
-
-       for (i = 0;; i++) {
-               desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags);
-               if (IS_ERR(desc))
-                       break;
-
-               ret = gpiod_hog(desc, name, lflags, dflags);
-               if (ret < 0)
-                       return ret;
-
-#ifdef CONFIG_OF_DYNAMIC
-               WRITE_ONCE(desc->hog, hog);
-#endif
-       }
-
-       return 0;
-}
-
-/**
- * of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
- * @chip:      gpio chip to act on
- *
- * This is only used by of_gpiochip_add to request/set GPIO initial
- * configuration.
- *
- * Returns:
- * 0 on success, or negative errno on failure.
- */
-static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
-{
-       int ret;
-
-       for_each_available_child_of_node_scoped(dev_of_node(&chip->gpiodev->dev), np) {
-               if (!of_property_read_bool(np, "gpio-hog"))
-                       continue;
-
-               ret = of_gpiochip_add_hog(chip, np);
-               if (ret < 0)
-                       return ret;
-
-               of_node_set_flag(np, OF_POPULATED);
-       }
-
        return 0;
 }
 
@@ -899,7 +787,7 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
                if (!gdev)
                        return NOTIFY_DONE;     /* not for us */
 
-               ret = of_gpiochip_add_hog(gpio_device_get_chip(gdev), rd->dn);
+               ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn));
                if (ret < 0) {
                        pr_err("%s: failed to add hogs for %pOF\n", __func__,
                               rd->dn);
@@ -1178,9 +1066,10 @@ int of_gpiochip_add(struct gpio_chip *chip)
 
        of_node_get(np);
 
-       ret = of_gpiochip_scan_gpios(chip);
-       if (ret)
-               of_node_put(np);
+       for_each_available_child_of_node_scoped(np, child) {
+               if (of_property_read_bool(child, "gpio-hog"))
+                       of_node_set_flag(child, OF_POPULATED);
+       }
 
        return ret;
 }
index 2257f7a498a10d69980f0c8afd48d5b661632d87..218cfe5bc4ac31a7c48306a08b4feafc06c0ad55 100644 (file)
@@ -10,6 +10,7 @@
 
 struct device_node;
 struct fwnode_handle;
+struct fwnode_reference_args;
 
 struct gpio_chip;
 struct gpio_desc;
@@ -24,6 +25,9 @@ int of_gpiochip_add(struct gpio_chip *gc);
 void of_gpiochip_remove(struct gpio_chip *gc);
 bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index);
 int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id);
+int of_gpiochip_get_lflags(struct gpio_chip *chip,
+                          struct fwnode_reference_args *gpiospec,
+                          unsigned long *lflags);
 #else
 static inline struct gpio_desc *of_find_gpio(struct device_node *np,
                                             const char *con_id,
@@ -44,6 +48,12 @@ static inline int of_gpio_count(const struct fwnode_handle *fwnode,
 {
        return 0;
 }
+static inline int of_gpiochip_get_lflags(struct gpio_chip *chip,
+                                        struct fwnode_reference_args *gpiospec,
+                                        unsigned long *lflags)
+{
+       return -ENOENT;
+}
 #endif /* CONFIG_OF_GPIO */
 
 extern struct notifier_block gpio_of_notifier;
index bbb96e52197cfbdf61fb49432609d4ddc8d83df8..4a57d98826006484091b85286880c5d85e606321 100644 (file)
@@ -948,7 +948,7 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
                          __func__, gc->label, hog->chip_hwnum, rv);
 }
 
-static void machine_gpiochip_add(struct gpio_chip *gc)
+static void gpiochip_machine_hog_lines(struct gpio_chip *gc)
 {
        struct gpiod_hog *hog;
 
@@ -960,6 +960,98 @@ static void machine_gpiochip_add(struct gpio_chip *gc)
        }
 }
 
+int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
+{
+       struct fwnode_handle *gc_node = dev_fwnode(&gc->gpiodev->dev);
+       struct fwnode_reference_args gpiospec;
+       enum gpiod_flags dflags;
+       struct gpio_desc *desc;
+       unsigned long lflags;
+       const char *name;
+       int ret, argc;
+       u32 gpios[3]; /* We support up to three-cell bindings. */
+       u32 cells;
+
+       lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+       dflags = GPIOD_ASIS;
+       name = NULL;
+
+       argc = fwnode_property_count_u32(fwnode, "gpios");
+       if (argc < 0)
+               return argc;
+       if (argc > 3)
+               return -EINVAL;
+
+       ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, argc);
+       if (ret < 0)
+               return ret;
+
+       if (is_of_node(fwnode)) {
+               /*
+                * OF-nodes need some additional special handling for
+                * translating of devicetree flags.
+                */
+               ret = fwnode_property_read_u32(gc_node, "#gpio-cells", &cells);
+               if (ret)
+                       return ret;
+               if (!ret && argc != cells)
+                       return -EINVAL;
+
+               memset(&gpiospec, 0, sizeof(gpiospec));
+               gpiospec.fwnode = fwnode;
+               gpiospec.nargs = argc;
+
+               for (int i = 0; i < argc; i++)
+                       gpiospec.args[i] = gpios[i];
+
+               ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
+               if (ret)
+                       return ret;
+       } else {
+               /*
+                * GPIO_ACTIVE_LOW is currently the only lookup flag
+                * supported for non-OF firmware nodes.
+                */
+               if (gpios[1])
+                       lflags |= GPIO_ACTIVE_LOW;
+       }
+
+       if (fwnode_property_present(fwnode, "input"))
+               dflags |= GPIOD_IN;
+       else if (fwnode_property_present(fwnode, "output-low"))
+               dflags |= GPIOD_OUT_LOW;
+       else if (fwnode_property_present(fwnode, "output-high"))
+               dflags |= GPIOD_OUT_HIGH;
+       else
+               return -EINVAL;
+
+       fwnode_property_read_string(fwnode, "line-name", &name);
+
+       desc = gpiochip_get_desc(gc, gpios[0]);
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       return gpiod_hog(desc, name, lflags, dflags);
+}
+
+static int gpiochip_hog_lines(struct gpio_chip *gc)
+{
+       int ret;
+
+       device_for_each_child_node_scoped(&gc->gpiodev->dev, fwnode) {
+               if (!fwnode_property_present(fwnode, "gpio-hog"))
+                       continue;
+
+               ret = gpiochip_add_hog(gc, fwnode);
+               if (ret)
+                       return ret;
+       }
+
+       gpiochip_machine_hog_lines(gc);
+
+       return 0;
+}
+
 static void gpiochip_setup_devs(void)
 {
        struct gpio_device *gdev;
@@ -1209,7 +1301,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
 
        acpi_gpiochip_add(gc);
 
-       machine_gpiochip_add(gc);
+       ret = gpiochip_hog_lines(gc);
+       if (ret)
+               goto err_remove_of_chip;
 
        ret = gpiochip_irqchip_init_valid_mask(gc);
        if (ret)
index 8d1a762f9d11bfc29c9102be02d7b640aa7daad3..dc4cb61a93187659d943f4ce3622bc1755e9fd42 100644 (file)
@@ -23,6 +23,8 @@
 
 #define GPIOCHIP_NAME  "gpiochip"
 
+struct fwnode_handle;
+
 /**
  * struct gpio_device - internal state container for GPIO devices
  * @dev: the GPIO device struct
@@ -274,6 +276,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
 int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
 int gpiod_hog(struct gpio_desc *desc, const char *name,
                unsigned long lflags, enum gpiod_flags dflags);
+int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode);
 int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
 struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
 const char *gpiod_get_label(struct gpio_desc *desc);