Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: shared: call gpio_chip::of_xlate() if set

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>

+29 -4
+26 -1
drivers/gpio/gpiolib-shared.c
··· 506 506 auxiliary_device_uninit(adev); 507 507 } 508 508 509 - int gpio_device_setup_shared(struct gpio_device *gdev) 509 + int gpiochip_setup_shared(struct gpio_chip *gc) 510 510 { 511 + struct gpio_device *gdev = gc->gpiodev; 511 512 struct gpio_shared_entry *entry; 512 513 struct gpio_shared_ref *ref; 513 514 struct gpio_desc *desc; ··· 533 532 * exposing shared pins. Find them and create the proxy devices. 534 533 */ 535 534 list_for_each_entry(entry, &gpio_shared_list, list) { 535 + guard(mutex)(&entry->lock); 536 + 536 537 if (!device_match_fwnode(&gdev->dev, entry->fwnode)) 537 538 continue; 538 539 539 540 if (list_count_nodes(&entry->refs) <= 1) 540 541 continue; 542 + 543 + #if IS_ENABLED(CONFIG_OF) 544 + if (is_of_node(entry->fwnode) && gc->of_xlate) { 545 + /* 546 + * This is the earliest that we can tranlate the 547 + * devicetree offset to the chip offset. 548 + */ 549 + struct of_phandle_args gpiospec = { }; 550 + 551 + gpiospec.np = to_of_node(entry->fwnode); 552 + gpiospec.args_count = 2; 553 + gpiospec.args[0] = entry->offset; 554 + 555 + ret = gc->of_xlate(gc, &gpiospec, NULL); 556 + if (ret < 0) 557 + return ret; 558 + 559 + entry->offset = ret; 560 + } 561 + #endif /* CONFIG_OF */ 541 562 542 563 desc = &gdev->descs[entry->offset]; 543 564 ··· 598 575 struct gpio_shared_ref *ref; 599 576 600 577 list_for_each_entry(entry, &gpio_shared_list, list) { 578 + guard(mutex)(&entry->lock); 579 + 601 580 if (!device_match_fwnode(&gdev->dev, entry->fwnode)) 602 581 continue; 603 582
+2 -2
drivers/gpio/gpiolib-shared.h
··· 14 14 15 15 #if IS_ENABLED(CONFIG_GPIO_SHARED) 16 16 17 - int gpio_device_setup_shared(struct gpio_device *gdev); 17 + int gpiochip_setup_shared(struct gpio_chip *gc); 18 18 void gpio_device_teardown_shared(struct gpio_device *gdev); 19 19 int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, 20 20 unsigned long lflags); 21 21 22 22 #else 23 23 24 - static inline int gpio_device_setup_shared(struct gpio_device *gdev) 24 + static inline int gpiochip_setup_shared(struct gpio_chip *gc) 25 25 { 26 26 return 0; 27 27 }
+1 -1
drivers/gpio/gpiolib.c
··· 1211 1211 if (ret) 1212 1212 goto err_remove_irqchip_mask; 1213 1213 1214 - ret = gpio_device_setup_shared(gdev); 1214 + ret = gpiochip_setup_shared(gc); 1215 1215 if (ret) 1216 1216 goto err_remove_irqchip; 1217 1217