From: Bartosz Golaszewski Date: Mon, 9 Mar 2026 12:42:38 +0000 (+0100) Subject: gpio: move hogs into GPIO core X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1d564ec4992945db853303dc2978256bce8c0b4;p=thirdparty%2Fkernel%2Flinux.git gpio: move hogs into GPIO core 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 Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260309-gpio-hog-fwnode-v2-2-4e61f3dbf06a@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index ced6375d1badf..09f860200a059 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -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); } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index b5610a847e726..2c923d17541f2 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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; } diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 2257f7a498a10..218cfe5bc4ac3 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -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; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bbb96e52197cf..4a57d98826006 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -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) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 8d1a762f9d11b..dc4cb61a93187 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -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);