[PATCH v8 5/5] gpio: shared: convert to using software nodes for GPIO shared proxy lookup
From: Bartosz Golaszewski
Date: Thu Oct 29 2026 - 12:35:27 EST
Automated management of shared GPIOs only allows to request shared pins
when they're assigned to a real struct device. Firmware node, no-device
lookup currently isn't supported and can't be because we're using
platform lookup tables to point the callers to the shared proxy GPIO
controllers.
Rework the GPIO sharing by using dummy software nodes assigned to the
proxy devices and stored in the reference structures which allow us -
once the correct struct gpio_shared_ref is found - to find the correct
target GPIO device by its firmware node.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/gpio/gpiolib-shared.c | 152 ++++++++++++++++++++++--------------------
drivers/gpio/gpiolib-shared.h | 15 ++---
drivers/gpio/gpiolib.c | 20 +-----
3 files changed, 90 insertions(+), 97 deletions(-)
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index c0e2a1207cccf16fe9fcf998eaf6ef744fe93eae..75529336c6cd9c97210d6afed9180f686014da8c 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -27,6 +27,12 @@
#include "gpiolib.h"
#include "gpiolib-shared.h"
+struct gpio_shared_proxy {
+ struct auxiliary_device aux;
+ struct software_node swnode;
+ struct fwnode_handle *fwnode;
+};
+
/* Represents a single reference to a GPIO pin. */
struct gpio_shared_ref {
struct list_head list;
@@ -35,11 +41,10 @@ struct gpio_shared_ref {
/* GPIO flags this consumer uses for the request. */
enum gpiod_flags flags;
char *con_id;
- /* Protects the auxiliary device struct and the lookup table. */
+ /* Protects the auxiliary device struct and fwnode. */
struct mutex lock;
+ struct gpio_shared_proxy *proxy;
struct lock_class_key lock_key;
- struct auxiliary_device *proxy_dev;
- struct gpiod_lookup_table *lookup;
bool is_reset_gpio;
};
@@ -304,43 +309,62 @@ static int gpio_shared_of_scan(void)
static void gpio_shared_proxy_dev_release(struct device *dev)
{
struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct gpio_shared_proxy *proxy = container_of(adev,
+ struct gpio_shared_proxy, aux);
ida_free(&gpio_shared_ida, adev->id);
- kfree(adev);
+ kfree(proxy);
}
static int gpio_shared_make_proxy_dev(struct gpio_device *gdev,
struct gpio_shared_entry *entry,
struct gpio_shared_ref *ref)
{
+ struct gpio_shared_proxy *proxy;
struct auxiliary_device *adev;
+ struct software_node *swnode;
+ struct device *dev;
int ret, id;
- adev = kzalloc_obj(*adev);
- if (!adev)
+ proxy = kzalloc_obj(*proxy);
+ if (!proxy)
return -ENOMEM;
+ adev = &proxy->aux;
+ swnode = &proxy->swnode;
+
id = ida_alloc(&gpio_shared_ida, GFP_KERNEL);
if (id < 0) {
- kfree(adev);
- return id;
+ ret = id;
+ goto err_free_proxy;
}
+ ret = software_node_register(swnode);
+ if (ret)
+ goto err_free_ida;
+
+ proxy->fwnode = software_node_fwnode(swnode);
+ if (!proxy->fwnode) {
+ ret = -ENODEV;
+ goto err_swnode_unreg;
+ }
+
+ dev = &adev->dev;
adev->id = id;
adev->name = "proxy";
- adev->dev.parent = gdev->dev.parent;
- adev->dev.platform_data = entry;
- adev->dev.release = gpio_shared_proxy_dev_release;
+ dev->parent = gdev->dev.parent;
+ dev->platform_data = entry;
+ dev->release = gpio_shared_proxy_dev_release;
+
+ device_set_node(dev, proxy->fwnode);
ret = auxiliary_device_init(adev);
- if (ret) {
- ida_free(&gpio_shared_ida, id);
- kfree(adev);
- return ret;
- }
+ if (ret)
+ goto err_swnode_unreg;
ret = auxiliary_device_add(adev);
if (ret) {
+ software_node_unregister(swnode);
auxiliary_device_uninit(adev);
return ret;
}
@@ -349,10 +373,19 @@ static int gpio_shared_make_proxy_dev(struct gpio_device *gdev,
dev_name(&adev->dev), gpio_device_get_label(gdev));
scoped_guard(mutex, &ref->lock) {
- ref->proxy_dev = adev;
+ ref->proxy = proxy;
}
return 0;
+
+err_swnode_unreg:
+ software_node_unregister(swnode);
+err_free_ida:
+ ida_free(&gpio_shared_ida, id);
+err_free_proxy:
+ kfree(proxy);
+
+ return ret;
}
#if IS_ENABLED(CONFIG_RESET_GPIO)
@@ -457,23 +490,19 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
}
#endif /* CONFIG_RESET_GPIO */
-int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode,
- const char *con_id, unsigned long lflags)
+struct gpio_desc *
+gpio_shared_get_proxy(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id)
{
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
- const char *dev_id;
-
- if (!consumer)
- return -EOPNOTSUPP;
-
- dev_id = dev_name(consumer);
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
guard(mutex)(&ref->lock);
- if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) {
+ if (!ref->fwnode && consumer &&
+ device_is_compatible(consumer, "reset-gpio")) {
if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue;
} else if (fwnode != ref->fwnode) {
@@ -484,55 +513,45 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *
(con_id && ref->con_id && strcmp(con_id, ref->con_id) != 0))
continue;
- /* We've already done that on a previous request. */
- if (ref->lookup)
- return 0;
-
- char *key __free(kfree) =
- kasprintf(GFP_KERNEL,
- KBUILD_MODNAME ".proxy.%u",
- ref->proxy_dev->id);
- if (!key)
- return -ENOMEM;
-
- struct gpiod_lookup_table *lookup __free(kfree) =
- kzalloc_flex(*lookup, table, 2);
- if (!lookup)
- return -ENOMEM;
-
- pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n",
- dev_id, key, ref->con_id ?: "none");
-
- lookup->dev_id = kstrdup(dev_id, GFP_KERNEL);
- if (!lookup->dev_id)
- return -ENOMEM;
-
- lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
- ref->con_id, lflags);
+ if (!ref->proxy || !ref->proxy->fwnode)
+ /*
+ * There's a matching shared reference but the
+ * associated auxiliary device has not been
+ * created yet.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
- ref->lookup = no_free_ptr(lookup);
- gpiod_add_lookup_table(ref->lookup);
+ struct gpio_device *gdev __free(gpio_device_put) =
+ gpio_device_find_by_fwnode(ref->proxy->fwnode);
+ if (!gdev)
+ /*
+ * There's a matching shared reference but the
+ * associated GPIO controller has not been
+ * registered yet.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
- return 0;
+ return gpio_device_get_desc(gdev, 0);
}
}
/* We warn here because this can only happen if the programmer borked. */
WARN_ON(1);
- return -ENOENT;
+ return ERR_PTR(-ENOENT);
}
static void gpio_shared_remove_proxy_dev(struct gpio_shared_ref *ref)
{
- struct auxiliary_device *proxy;
+ struct gpio_shared_proxy *proxy;
scoped_guard(mutex, &ref->lock) {
- proxy = ref->proxy_dev;
- ref->proxy_dev = NULL;
+ proxy = ref->proxy;
+ ref->proxy = NULL;
+ software_node_unregister(&proxy->swnode);
}
- auxiliary_device_delete(proxy);
- auxiliary_device_uninit(proxy);
+ auxiliary_device_delete(&proxy->aux);
+ auxiliary_device_uninit(&proxy->aux);
}
static void gpio_shared_teardown_entry(struct gpio_device *gdev,
@@ -543,17 +562,8 @@ static void gpio_shared_teardown_entry(struct gpio_device *gdev,
scoped_guard(mutex, &entry->lock)
gpiod_free_commit(&gdev->descs[entry->offset]);
- list_for_each_entry(ref, &entry->refs, list) {
- if (ref->lookup) {
- gpiod_remove_lookup_table(ref->lookup);
- kfree(ref->lookup->table[0].key);
- kfree(ref->lookup->dev_id);
- kfree(ref->lookup);
- ref->lookup = NULL;
- }
-
+ list_for_each_entry(ref, &entry->refs, list)
gpio_shared_remove_proxy_dev(ref);
- }
}
static int gpio_shared_setup_entry(struct gpio_chip *gc,
@@ -637,7 +647,7 @@ int gpiochip_setup_shared(struct gpio_chip *gc)
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
- if (ref->proxy_dev && gdev->dev.parent == &ref->proxy_dev->dev) {
+ if (ref->proxy && gdev->dev.parent == &ref->proxy->aux.dev) {
/*
* This is a shared GPIO proxy. Mark its
* descriptor as such and return here.
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
index 618756f6c6aafd087df267a4a32732ccc03af38e..27fd2a40c5cb77a19c258e3fbf429d3f6e7d4b32 100644
--- a/drivers/gpio/gpiolib-shared.h
+++ b/drivers/gpio/gpiolib-shared.h
@@ -14,9 +14,9 @@ struct fwnode_handle;
int gpiochip_setup_shared(struct gpio_chip *gc);
void gpio_device_teardown_shared(struct gpio_device *gdev);
-int gpio_shared_add_proxy_lookup(struct device *consumer,
- struct fwnode_handle *fwnode,
- const char *con_id, unsigned long lflags);
+struct gpio_desc *
+gpio_shared_get_proxy(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id);
#else
@@ -27,12 +27,11 @@ static inline int gpiochip_setup_shared(struct gpio_chip *gc)
static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
-static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
- struct fwnode_handle *fwnode,
- const char *con_id,
- unsigned long lflags)
+static inline struct gpio_desc *
+gpio_shared_get_proxy(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id)
{
- return 0;
+ return NULL;
}
#endif /* CONFIG_GPIO_SHARED */
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index fbdeda2f278713a47c2e3d2a6640f387f6d92227..402de2e106f19fd110d67eeecd3c07794b7a5cdd 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4841,24 +4841,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
scoped_guard(srcu, &gpio_devices_srcu) {
desc = gpiod_fwnode_lookup(fwnode, consumer, con_id, idx,
&flags, &lookupflags);
- if (!IS_ERR_OR_NULL(desc) &&
- test_bit(GPIOD_FLAG_SHARED, &desc->flags)) {
- /*
- * We're dealing with a GPIO shared by multiple
- * consumers. This is the moment to add the machine
- * lookup table for the proxy device as previously
- * we only knew the consumer's fwnode.
- */
- ret = gpio_shared_add_proxy_lookup(consumer, fwnode,
- con_id, lookupflags);
- if (ret)
- return ERR_PTR(ret);
-
- /* Trigger platform lookup for shared GPIO proxy. */
- desc = ERR_PTR(-ENOENT);
- /* Trigger it even for fwnode-only gpiod_get(). */
- platform_lookup_allowed = true;
- }
+ if (!IS_ERR_OR_NULL(desc) && test_bit(GPIOD_FLAG_SHARED, &desc->flags))
+ desc = gpio_shared_get_proxy(consumer, fwnode, con_id);
if (gpiod_not_found(desc) && platform_lookup_allowed) {
/*
--
2.47.3