]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
gpio: shared: call gpio_chip::of_xlate() if set
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Wed, 18 Mar 2026 14:00:53 +0000 (15:00 +0100)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 23 Mar 2026 08:54:24 +0000 (09:54 +0100)
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@nvidia.com>
Closes: https://lore.kernel.org/all/921ba8ce-b18e-4a99-966d-c763d22081e2@nvidia.com/
Reviewed-by: Linus Walleij <linusw@kernel.org>
Tested-by: Jon Hunter <jonathanh@nvidia.com>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Link: https://patch.msgid.link/20260318-gpio-shared-xlate-v2-1-0ce34c707e81@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpiolib-shared.c
drivers/gpio/gpiolib-shared.h
drivers/gpio/gpiolib.c

index 17a7128b6bd9bf6023deccee50b2453caebe3d9a..3a8db9bf456daaf021d3c691677a90fc6da15889 100644 (file)
@@ -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;
 
index 40568ef7364ccbf08b7f583e279a7d5b572af477..e11e260e1f590c46c5e575d3bb8f3b5a2240892d 100644 (file)
@@ -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;
 }
index f52d2d3efac4f0e13c2ea8444e48eb8ed9f66df6..d82fcf3fb458637447363e0871954bc47cf136fe 100644 (file)
@@ -1211,7 +1211,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;