Re: [PATCH] gpio: shared: call gpio_chip::of_xlate() if set

From: Jon Hunter

Date: Tue Mar 17 2026 - 06:13:50 EST


Hi Bartosz,

On 16/03/2026 13:52, Bartosz Golaszewski wrote:
OF-based GPIO controller drivers may provide a translation function that
calculates the real chip offset from whatever devicetree sources
provide. We need to take this into account in the shared GPIO management
and call of_xlate() if it's provided and adjust the entry->offset we
initially set when scanning the tree.

To that end: modify the shared GPIO API to take the GPIO chip as
argument on setup (to avoid having to rcu_dereference() it from the GPIO
device) and protect the access to entry->offset with the existing lock.

Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support")
Reported-by: Jon Hunter <jonathanh@xxxxxxxxxx>
Closes: https://lore.kernel.org/all/921ba8ce-b18e-4a99-966d-c763d22081e2@xxxxxxxxxx/
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/gpio/gpiolib-shared.c | 27 ++++++++++++++++++++++++++-
drivers/gpio/gpiolib-shared.h | 4 ++--
drivers/gpio/gpiolib.c | 2 +-
3 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 17a7128b6bd9bf6023deccee50b2453caebe3d9a..3a8db9bf456daaf021d3c691677a90fc6da15889 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -506,8 +506,9 @@ static void gpio_shared_remove_adev(struct auxiliary_device *adev)
auxiliary_device_uninit(adev);
}
-int gpio_device_setup_shared(struct gpio_device *gdev)
+int gpiochip_setup_shared(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
struct gpio_desc *desc;
@@ -532,12 +533,34 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
* exposing shared pins. Find them and create the proxy devices.
*/
list_for_each_entry(entry, &gpio_shared_list, list) {
+ guard(mutex)(&entry->lock);
+
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
if (list_count_nodes(&entry->refs) <= 1)
continue;
+#if IS_ENABLED(CONFIG_OF)
+ if (is_of_node(entry->fwnode) && gc->of_xlate) {
+ /*
+ * This is the earliest that we can tranlate the
+ * devicetree offset to the chip offset.
+ */
+ struct of_phandle_args gpiospec = { };
+
+ gpiospec.np = to_of_node(entry->fwnode);
+ gpiospec.args_count = 2;
+ gpiospec.args[0] = entry->offset;
+
+ ret = gc->of_xlate(gc, &gpiospec, NULL);
+ if (ret < 0)
+ return ret;
+
+ entry->offset = ret;
+ }
+#endif /* CONFIG_OF */
+
desc = &gdev->descs[entry->offset];
__set_bit(GPIOD_FLAG_SHARED, &desc->flags);
@@ -575,6 +598,8 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
struct gpio_shared_ref *ref;
list_for_each_entry(entry, &gpio_shared_list, list) {
+ guard(mutex)(&entry->lock);
+
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
index 40568ef7364ccbf08b7f583e279a7d5b572af477..e11e260e1f590c46c5e575d3bb8f3b5a2240892d 100644
--- a/drivers/gpio/gpiolib-shared.h
+++ b/drivers/gpio/gpiolib-shared.h
@@ -14,14 +14,14 @@ struct device;
#if IS_ENABLED(CONFIG_GPIO_SHARED)
-int gpio_device_setup_shared(struct gpio_device *gdev);
+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, const char *con_id,
unsigned long lflags);
#else
-static inline int gpio_device_setup_shared(struct gpio_device *gdev)
+static inline int gpiochip_setup_shared(struct gpio_chip *gc)
{
return 0;
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6e000ad58a11f7e3de85d8a0630150368acc53ce..1777efe1a986c941da464da92255c261f27a5a6b 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1223,7 +1223,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_remove_irqchip_mask;
- ret = gpio_device_setup_shared(gdev);
+ ret = gpiochip_setup_shared(gc);
if (ret)
goto err_remove_irqchip;


Thanks for sending this. However, I am seeing a different issue now ...

------------[ cut here ]------------
WARNING: drivers/gpio/gpiolib-shared.c:499 at gpio_shared_add_proxy_lookup+0x118/0x1d8, CPU#8: swapper/0/1
Modules linked in:
CPU: 8 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.0.0-rc3-next-20260309-00005-g02826fefa46f #14 PREEMPT
Hardware name: NVIDIA NVIDIA Jetson AGX Orin Developer Kit/Jetson, BIOS buildbrain-gcid-42974706 11/20/2025
pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : gpio_shared_add_proxy_lookup+0x118/0x1d8
lr : gpio_shared_add_proxy_lookup+0xfc/0x1d8
sp : ffff8000832bba30
x29: ffff8000832bba30 x28: ffff000080d01010 x27: ffffffffffffefff
x26: 0000000000000001 x25: ffff800082df0538 x24: ffff800082df0528
x23: 0000000000000000 x22: ffff00008012c158 x21: ffff000081455010
usb 1-3: new full-speed USB device number 2 using tegra-xusb
x20: ffff000080d5d430 x19: ffff00008012c158 x18: 00000000ffffffff
x17: ffff8000830786a8 x16: ffff800083078718 x15: ffff8000832bb880
x14: ffffffffffffffff x13: 0000000000000008 x12: 0101010101010101
x11: 7f7f7f7f7f7f7f7f x10: ffff8000827f20d0 x9 : 0000000000000003
x8 : 0101010101010101 x7 : 0080808080808000 x6 : 15151a0a59460209
x5 : 000000000000003c x4 : ffff8000832bb990 x3 : ffff0000800fe800
x2 : ffff0000801c2f40 x1 : ffff0000801c2f40 x0 : ffff800082df0538
Call trace:
gpio_shared_add_proxy_lookup+0x118/0x1d8 (P)
gpiod_find_and_request+0x1bc/0x548
devm_fwnode_gpiod_get_index+0x1c/0x6c
gpio_keys_probe+0x494/0x9fc
platform_probe+0x5c/0x98
really_probe+0xbc/0x2a8
__driver_probe_device+0x78/0x12c
driver_probe_device+0x3c/0x15c
__driver_attach+0x90/0x19c
bus_for_each_dev+0x78/0xd4
driver_attach+0x24/0x30
bus_add_driver+0xe4/0x208
driver_register+0x5c/0x124
__platform_driver_register+0x24/0x30
gpio_keys_init+0x1c/0x28
do_one_initcall+0x7c/0x1c0
kernel_init_freeable+0x204/0x2ec
kernel_init+0x24/0x1e0
ret_from_fork+0x10/0x20
---[ end trace 0000000000000000 ]---

I see the comment says ...

/* We warn here because this can only happen if the programmer borked. */
WARN_ON(1);

I will take a closer look, but let me know if you have any thoughts?

Jon

--
nvpublic