[PATCH 9/9] gpio: shared: generalize most code to firmware nodes
From: Bartosz Golaszewski
Date: Wed Nov 04 2026 - 09:12:29 EST
Ahead of adding support for sharing of GPIOs for other types of firmware
nodes, convert as much code in gpiolib-shared.c as possible to using
fwnode APIs and guard the remaining OF-specific bits with CONFIG_OF
guards.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/gpio/gpiolib-shared.c | 241 +++++++++++++++++++++++-------------------
1 file changed, 131 insertions(+), 110 deletions(-)
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 8c9ac9d9330490b17fb3936b8758beb97a308a15..58ac049bb603e2946c81e67a047b4788d39213b4 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -23,6 +23,7 @@
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/string_helpers.h>
#include "gpiolib.h"
#include "gpiolib-shared.h"
@@ -63,7 +64,7 @@ struct gpio_shared_entry {
static LIST_HEAD(gpio_shared_list);
static DEFINE_IDA(gpio_shared_ida);
-#if IS_ENABLED(CONFIG_OF)
+
static struct gpio_shared_entry *
gpio_shared_find_entry(struct fwnode_handle *controller_node,
unsigned int offset)
@@ -133,158 +134,162 @@ static int gpio_shared_setup_reset_proxy(struct gpio_shared_entry *entry,
}
/* Handle all special nodes that we should ignore. */
-static bool gpio_shared_of_node_ignore(struct device_node *node)
+static bool gpio_shared_fwnode_ignore(struct fwnode_handle *fwnode)
{
/* Ignore disabled devices. */
- if (!of_device_is_available(node))
+ if (!fwnode_device_is_available(fwnode))
return true;
/*
- * __symbols__ is a special, internal node and should not be considered
- * when scanning for shared GPIOs.
+ * On OF-systems __symbols__ is a special, internal node and should not
+ * be considered when scanning for shared GPIOs.
*/
- if (of_node_name_eq(node, "__symbols__"))
+ if (fwnode_name_eq(fwnode, "__symbols__"))
return true;
/*
* GPIO hogs have a "gpios" property which is not a phandle and can't
* possibly refer to a shared GPIO.
*/
- if (of_property_present(node, "gpio-hog"))
+ if (fwnode_property_present(fwnode, "gpio-hog"))
return true;
return false;
}
-static int gpio_shared_of_traverse(struct device_node *curr)
+static int gpio_shared_fwnode_scan_property(struct fwnode_handle *curr,
+ const char *propname)
{
+ struct fwnode_reference_args args;
struct gpio_shared_entry *entry;
size_t con_id_len, suffix_len;
struct fwnode_handle *fwnode;
- struct of_phandle_args args;
struct gpio_shared_ref *ref;
- struct property *prop;
unsigned int offset;
const char *suffix;
- int ret, count, i;
+ int ret, i, count;
- if (gpio_shared_of_node_ignore(curr))
+ /*
+ * The standard name for a GPIO property is "foo-gpios"
+ * or "foo-gpio". Some bindings also use "gpios" or "gpio".
+ * There are some legacy device-trees which have a different
+ * naming convention and for which we have rename quirks in
+ * place in gpiolib-of.c. I don't think any of them require
+ * support for shared GPIOs so for now let's just ignore
+ * them. We can always just export the quirk list and
+ * iterate over it here.
+ */
+ if (!strends(propname, "-gpios") && !strends(propname, "-gpio") &&
+ strcmp(propname, "gpios") != 0 && strcmp(propname, "gpio") != 0)
return 0;
- for_each_property_of_node(curr, prop) {
- /*
- * The standard name for a GPIO property is "foo-gpios"
- * or "foo-gpio". Some bindings also use "gpios" or "gpio".
- * There are some legacy device-trees which have a different
- * naming convention and for which we have rename quirks in
- * place in gpiolib-of.c. I don't think any of them require
- * support for shared GPIOs so for now let's just ignore
- * them. We can always just export the quirk list and
- * iterate over it here.
- */
- if (!strends(prop->name, "-gpios") &&
- !strends(prop->name, "-gpio") &&
- strcmp(prop->name, "gpios") != 0 &&
- strcmp(prop->name, "gpio") != 0)
- continue;
-
- count = of_count_phandle_with_args(curr, prop->name,
- "#gpio-cells");
- if (count <= 0)
- continue;
+ count = fwnode_property_count_references(curr, propname, "#gpio-cells");
+ if (count <= 0)
+ return 0;
- for (i = 0; i < count; i++) {
- struct device_node *np __free(device_node) = NULL;
- char *con_id __free(kfree) = NULL;
+ for (i = 0; i < count; i++) {
+ struct fwnode_handle *ctrl __free(fwnode_handle) = NULL;
+ char *con_id __free(kfree) = NULL;
- ret = of_parse_phandle_with_args(curr, prop->name,
- "#gpio-cells", i,
+ ret = fwnode_property_get_reference_args(curr, propname,
+ "#gpio-cells", 0, i,
&args);
- if (ret)
- continue;
+ if (ret)
+ continue;
- np = args.np;
+ ctrl = args.fwnode;
- if (!of_property_present(np, "gpio-controller"))
- continue;
+ if (!fwnode_property_present(ctrl, "gpio-controller"))
+ continue;
- /*
- * We support 1, 2 and 3 cell GPIO bindings in the
- * kernel currently. There's only one old MIPS dts that
- * has a one-cell binding but there's no associated
- * consumer so it may as well be an error. There don't
- * seem to be any 3-cell users of non-exclusive GPIOs,
- * so we can skip this as well. Let's occupy ourselves
- * with the predominant 2-cell binding with the first
- * cell indicating the hardware offset of the GPIO and
- * the second defining the GPIO flags of the request.
- */
- if (args.args_count != 2)
- continue;
+ /*
+ * We support 1, 2 and 3 cell GPIO bindings in the
+ * kernel currently. There's only one old MIPS dts that
+ * has a one-cell binding but there's no associated
+ * consumer so it may as well be an error. There don't
+ * seem to be any 3-cell users of non-exclusive GPIOs,
+ * so we can skip this as well. Let's occupy ourselves
+ * with the predominant 2-cell binding with the first
+ * cell indicating the hardware offset of the GPIO and
+ * the second defining the GPIO flags of the request.
+ */
+ if (args.nargs != 2)
+ continue;
- fwnode = of_fwnode_handle(args.np);
- offset = args.args[0];
+ fwnode = ctrl;
+ offset = args.args[0];
- entry = gpio_shared_find_entry(fwnode, offset);
- if (!entry) {
- entry = kzalloc_obj(*entry);
- if (!entry)
- return -ENOMEM;
+ entry = gpio_shared_find_entry(fwnode, offset);
+ if (!entry) {
+ entry = kzalloc_obj(*entry);
+ if (!entry)
+ return -ENOMEM;
- entry->fwnode = fwnode_handle_get(fwnode);
- entry->offset = offset;
- entry->index = count;
- INIT_LIST_HEAD(&entry->refs);
- mutex_init(&entry->lock);
+ entry->fwnode = fwnode_handle_get(fwnode);
+ entry->offset = offset;
+ entry->index = count;
+ INIT_LIST_HEAD(&entry->refs);
+ mutex_init(&entry->lock);
- list_add_tail(&entry->list, &gpio_shared_list);
- }
+ list_add_tail(&entry->list, &gpio_shared_list);
+ }
- if (strends(prop->name, "gpios"))
- suffix = "-gpios";
- else if (strends(prop->name, "gpio"))
- suffix = "-gpio";
- else
- suffix = NULL;
- if (!suffix)
- continue;
+ if (strends(propname, "gpios"))
+ suffix = "-gpios";
+ else if (strends(propname, "gpio"))
+ suffix = "-gpio";
+ else
+ suffix = NULL;
+ if (!suffix)
+ continue;
- /* We only set con_id if there's actually one. */
- if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) {
- con_id = kstrdup(prop->name, GFP_KERNEL);
- if (!con_id)
- return -ENOMEM;
+ /* We only set con_id if there's actually one. */
+ if (strcmp(propname, "gpios") && strcmp(propname, "gpio")) {
+ con_id = kstrdup(propname, GFP_KERNEL);
+ if (!con_id)
+ return -ENOMEM;
- con_id_len = strlen(con_id);
- suffix_len = strlen(suffix);
+ con_id_len = strlen(con_id);
+ suffix_len = strlen(suffix);
- con_id[con_id_len - suffix_len] = '\0';
- }
+ con_id[con_id_len - suffix_len] = '\0';
+ }
- struct fwnode_handle *curr_fwnode =
- fwnode_handle_get(of_fwnode_handle(curr));
- ref = gpio_shared_make_ref(curr_fwnode, con_id, args.args[1]);
- if (!ref) {
- fwnode_handle_put(curr_fwnode);
- return -ENOMEM;
- }
+ ref = gpio_shared_make_ref(fwnode_handle_get(curr), con_id, args.args[1]);
+ if (!ref) {
+ fwnode_handle_put(curr);
+ return -ENOMEM;
+ }
- if (!list_empty(&entry->refs))
- pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n",
- entry->offset, fwnode_get_name(entry->fwnode));
+ if (!list_empty(&entry->refs))
+ pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n",
+ entry->offset, fwnode_get_name(entry->fwnode));
- list_add_tail(&ref->list, &entry->refs);
+ list_add_tail(&ref->list, &entry->refs);
- if (strcmp(prop->name, "reset-gpios") == 0) {
- ret = gpio_shared_setup_reset_proxy(entry, args.args[1]);
- if (ret)
- return ret;
- }
+ if (strcmp(propname, "reset-gpios") == 0) {
+ ret = gpio_shared_setup_reset_proxy(entry, args.args[1]);
+ if (ret)
+ return ret;
}
}
- for_each_child_of_node_scoped(curr, child) {
- ret = gpio_shared_of_traverse(child);
+ return 0;
+}
+
+static int gpio_shared_fwnode_scan_properties(struct fwnode_handle *curr)
+{
+ char **props;
+ int ret, i;
+
+ ret = fwnode_property_get_names(curr, &props);
+ if (ret <= 0)
+ return ret;
+
+ CLASS(strarray, names)(props, ret);
+
+ for (i = 0; i < names.size; i++) {
+ ret = gpio_shared_fwnode_scan_property(curr, names.array[i]);
if (ret)
return ret;
}
@@ -292,19 +297,35 @@ static int gpio_shared_of_traverse(struct device_node *curr)
return 0;
}
-static int gpio_shared_of_scan(void)
+static int gpio_shared_fwnode_traverse(struct fwnode_handle *curr)
{
- if (of_root)
- return gpio_shared_of_traverse(of_root);
+ int ret;
+
+ if (gpio_shared_fwnode_ignore(curr))
+ return 0;
+
+ ret = gpio_shared_fwnode_scan_properties(curr);
+ if (ret)
+ return ret;
+
+ fwnode_for_each_child_node_scoped(curr, child) {
+ ret = gpio_shared_fwnode_traverse(child);
+ if (ret)
+ return ret;
+ }
return 0;
}
-#else
+
static int gpio_shared_of_scan(void)
{
+#if IS_ENABLED(CONFIG_OF)
+ if (of_root)
+ return gpio_shared_fwnode_traverse(of_fwnode_handle(of_root));
+#endif
+
return 0;
}
-#endif /* CONFIG_OF */
static void gpio_shared_adev_release(struct device *dev)
{
--
2.47.3