From 7d80e248e8fc4c70f8feac4989f3666878039565 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 3 Dec 2025 11:52:06 +0100 Subject: [PATCH 01/10] gpio: mmio: fix bad guard conversion A recent spinlock guard conversion consistently used the wrong guard so that interrupts are no longer disabled while holding the chip lock (which can cause deadlocks). Fixes: 7e061b462b3d ("gpio: mmio: use lock guards") Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20251203105206.24453-1-johan@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index b3a26a06260b..5daf962b0323 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -231,7 +231,7 @@ static int gpio_mmio_set(struct gpio_chip *gc, unsigned int gpio, int val) struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long mask = gpio_mmio_line2mask(gc, gpio); - guard(raw_spinlock)(&chip->lock); + guard(raw_spinlock_irqsave)(&chip->lock); if (val) chip->sdata |= mask; @@ -262,7 +262,7 @@ static int gpio_mmio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long mask = gpio_mmio_line2mask(gc, gpio); - guard(raw_spinlock)(&chip->lock); + guard(raw_spinlock_irqsave)(&chip->lock); if (val) chip->sdata |= mask; @@ -302,7 +302,7 @@ static void gpio_mmio_set_multiple_single_reg(struct gpio_chip *gc, struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long set_mask, clear_mask; - guard(raw_spinlock)(&chip->lock); + guard(raw_spinlock_irqsave)(&chip->lock); gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); @@ -391,7 +391,7 @@ static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - scoped_guard(raw_spinlock, &chip->lock) { + scoped_guard(raw_spinlock_irqsave, &chip->lock) { chip->sdir &= ~gpio_mmio_line2mask(gc, gpio); if (chip->reg_dir_in) @@ -431,7 +431,7 @@ static void gpio_mmio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - guard(raw_spinlock)(&chip->lock); + guard(raw_spinlock_irqsave)(&chip->lock); chip->sdir |= gpio_mmio_line2mask(gc, gpio); From 52721cfc78c76b09c66e092b52617006390ae96a Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Thu, 4 Dec 2025 18:13:04 +0800 Subject: [PATCH 02/10] gpio: regmap: Fix memleak in error path in gpio_regmap_register() Call gpiochip_remove() to free the resources allocated by gpiochip_add_data() in error path. Fixes: 553b75d4bfe9 ("gpio: regmap: Allow to allocate regmap-irq device") Fixes: ae495810cffe ("gpio: regmap: add the .fixed_direction_output configuration parameter") CC: stable@vger.kernel.org Co-developed-by: WangYuli Signed-off-by: WangYuli Signed-off-by: Wentao Guan Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20251204101303.30353-1-guanwentao@uniontech.com [Bartosz: reworked the commit message] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index e5ba38e65c10..9581bd5ca947 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -338,7 +338,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config config->regmap_irq_line, config->regmap_irq_flags, 0, config->regmap_irq_chip, &gpio->irq_chip_data); if (ret) - goto err_free_bitmap; + goto err_remove_gpiochip; irq_domain = regmap_irq_get_domain(gpio->irq_chip_data); } else From db9c67bfca8585bb7f00f289056e7b83502861c8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Dec 2025 10:48:47 +0100 Subject: [PATCH 03/10] gpio: qixis: select CONFIG_REGMAP_MMIO The regmap drivers need to be selected by each user, without that there can be configurations that fail to link: x86_64-linux-ld: drivers/gpio/gpio-qixis-fpga.o: in function `qixis_cpld_gpio_probe': gpio-qixis-fpga.c:(.text+0x13a): undefined reference to `__devm_regmap_init_mmio_clk' Fixes: e88500247dc3 ("gpio: add QIXIS FPGA GPIO controller") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20251204094928.1031494-1-arnd@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c74da29253e8..0fed90ef587a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1568,6 +1568,7 @@ config GPIO_QIXIS_FPGA tristate "NXP QIXIS FPGA GPIO support" depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST select GPIO_REGMAP + select REGMAP_MMIO help This enables support for the GPIOs found in the QIXIS FPGA which is integrated on some NXP Layerscape boards such as LX2160ARDB and From dd44d4d0c55a4ecf5eabf7856f96ed47e0684780 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 5 Dec 2025 10:54:09 +0100 Subject: [PATCH 04/10] gpio: tb10x: fix OF_GPIO dependency Selecting OF_GPIO is generally not allowed, it always gets enabled when both GPIOLIB and OF are turned on. The tb10x driver now warns about this after it was enabled for compile-testing: WARNING: unmet direct dependencies detected for OF_GPIO Depends on [n]: GPIOLIB [=y] && OF [=n] && HAS_IOMEM [=y] Selected by [y]: - GPIO_TB10X [=y] && GPIOLIB [=y] && HAS_IOMEM [=y] && (ARC_PLAT_TB10X || COMPILE_TEST [=y]) OF_GPIO is not required for compile-testing and is already enabled when the driver is usable, so just drop the 'select' line. Fixes: 682fbb18e14c ("gpio: tb10x: allow building the module with COMPILE_TEST=y") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20251205095429.1291866-1-arnd@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0fed90ef587a..bd185482a7fd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -737,7 +737,6 @@ config GPIO_TB10X depends on ARC_PLAT_TB10X || COMPILE_TEST select GPIO_GENERIC select GENERIC_IRQ_CHIP - select OF_GPIO config GPIO_TEGRA tristate "NVIDIA Tegra GPIO support" From 2d967310c49ed93ac11cef408a55ddf15c3dd52e Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Sat, 6 Dec 2025 18:04:13 +0000 Subject: [PATCH 05/10] gpiolib: acpi: Add quirk for Dell Precision 7780 Dell Precision 7780 often wakes up on its own from suspend. Sometimes wake up happens immediately (i. e. within 7 seconds), sometimes it happens after, say, 30 minutes. Fixes: 1796f808e4bb ("HID: i2c-hid: acpi: Stop setting wakeup_capable") Link: https://lore.kernel.org/linux-i2c/197ae95ffd8.dc819e60457077.7692120488609091556@zohomail.com/ Cc: stable@vger.kernel.org Reviewed-by: Andy Shevchenko Signed-off-by: Askar Safin Link: https://lore.kernel.org/r/20251206180414.3183334-2-safinaskar@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi-quirks.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c index 7b95d1b03361..a0116f004975 100644 --- a/drivers/gpio/gpiolib-acpi-quirks.c +++ b/drivers/gpio/gpiolib-acpi-quirks.c @@ -370,6 +370,28 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { .ignore_wake = "ASCP1A00:00@8", }, }, + { + /* + * Spurious wakeups, likely from touchpad controller + * Dell Precision 7780 + * Found in BIOS 1.24.1 + * + * Found in touchpad firmware, installed by Dell Touchpad Firmware Update Utility version 1160.4196.9, A01 + * ( Dell-Touchpad-Firmware-Update-Utility_VYGNN_WIN64_1160.4196.9_A00.EXE ), + * released on 11 Jul 2024 + * + * https://lore.kernel.org/linux-i2c/197ae95ffd8.dc819e60457077.7692120488609091556@zohomail.com/ + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Precision"), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7780"), + DMI_MATCH(DMI_BOARD_NAME, "0C6JVW"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "VEN_0488:00@355", + }, + }, {} /* Terminating entry */ }; From 9e7a40a2841483d7bf51b8d9a5e1f0633a5c7a26 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Dec 2025 10:23:09 +0100 Subject: [PATCH 06/10] gpio: shared: ignore disabled nodes when traversing the device-tree Don't consider disabled devices when traversing the device-tree looking for shared GPIOs. Even if they do share a phandle to a pin, they can never be instantiated and request it. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Link: https://lore.kernel.org/r/20251203092309.34737-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 8bdd107b1ad1..5d15675d61ea 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -77,6 +77,10 @@ gpio_shared_find_entry(struct fwnode_handle *controller_node, /* Handle all special nodes that we should ignore. */ static bool gpio_shared_of_node_ignore(struct device_node *node) { + /* Ignore disabled devices. */ + if (!of_device_is_available(node)) + return true; + /* * __symbols__ is a special, internal node and should not be considered * when scanning for shared GPIOs. From e2c4175b8d3b3ea65fc3801c190bd93fe8b7a7a9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 6 Dec 2025 12:53:53 +0100 Subject: [PATCH 07/10] gpio: shared: fix NULL-pointer dereference in teardown path We need to actually store the address of the GPIO lookup table in the reference struct before we try to free it or - worse - dereference its members. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Link: https://lore.kernel.org/r/20251206-gpio-shared-teardown-fixes-v1-1-35ac458cfce1@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 5d15675d61ea..17da15c1075f 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -399,7 +399,8 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0, ref->con_id, lflags); - gpiod_add_lookup_table(no_free_ptr(lookup)); + ref->lookup = no_free_ptr(lookup); + gpiod_add_lookup_table(ref->lookup); return 0; } From c904a0d8525d5f03529ae3176e99bd32466ece7b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 6 Dec 2025 12:53:54 +0100 Subject: [PATCH 08/10] gpio: shared: check if a reference is populated before cleaning its resources It's possible that not all proxy entries will be set up when the device gets removed so check if they are before trying to dereference members which are still NULL. This can happen if some consumers never requested their shared GPIOs. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Link: https://lore.kernel.org/r/20251206-gpio-shared-teardown-fixes-v1-2-35ac458cfce1@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 17da15c1075f..4084a28a953a 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -491,10 +491,13 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) continue; list_for_each_entry(ref, &entry->refs, list) { - gpiod_remove_lookup_table(ref->lookup); - kfree(ref->lookup->table[0].key); - kfree(ref->lookup); - ref->lookup = NULL; + if (ref->lookup) { + gpiod_remove_lookup_table(ref->lookup); + kfree(ref->lookup->table[0].key); + kfree(ref->lookup); + ref->lookup = NULL; + } + gpio_shared_remove_adev(&ref->adev); } } From d382c765d083ad871b4a053059351edd348a2442 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 6 Dec 2025 12:53:55 +0100 Subject: [PATCH 09/10] gpio: shared: fix auxiliary device cleanup order Dropping the last reference to the internal struct device should be the last thing we do so delete the device first and then uninit it which also involves the final put_device(). Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Link: https://lore.kernel.org/r/20251206-gpio-shared-teardown-fixes-v1-3-35ac458cfce1@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 4084a28a953a..2d3b0c3460e5 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -415,8 +415,8 @@ static void gpio_shared_remove_adev(struct auxiliary_device *adev) { lockdep_assert_held(&gpio_shared_lock); - auxiliary_device_uninit(adev); auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); } int gpio_device_setup_shared(struct gpio_device *gdev) From ea513dd3c066074b12e788114b45e0f2bda382cc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 6 Dec 2025 12:53:56 +0100 Subject: [PATCH 10/10] gpio: shared: make locking more fine-grained The global gpio_shared_lock has caused some issues when recursively removing GPIO shared proxies. The thing is: we don't really need it. Once created from an init-call, the shared GPIO data structures are never removed, there's no need to protect the linked lists of entries and references. All we need to protect is the shared GPIO descriptor (which we already do with a per-entry mutex) and the auxiliary device data in struct gpio_shared_ref. Remove the global gpio_shared_lock and use a per-reference mutex to protect shared references when adding/removing the auxiliary devices and their GPIO lookup entries. Link: https://lore.kernel.org/r/20251206-gpio-shared-teardown-fixes-v1-4-35ac458cfce1@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 2d3b0c3460e5..ba4b718d40a0 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -36,6 +36,8 @@ struct gpio_shared_ref { enum gpiod_flags flags; char *con_id; int dev_id; + /* Protects the auxiliary device struct and the lookup table. */ + struct mutex lock; struct auxiliary_device adev; struct gpiod_lookup_table *lookup; }; @@ -49,6 +51,7 @@ struct gpio_shared_entry { unsigned int offset; /* Index in the property value array. */ size_t index; + /* Synchronizes the modification of shared_desc. */ struct mutex lock; struct gpio_shared_desc *shared_desc; struct kref ref; @@ -56,7 +59,6 @@ struct gpio_shared_entry { }; static LIST_HEAD(gpio_shared_list); -static DEFINE_MUTEX(gpio_shared_lock); static DEFINE_IDA(gpio_shared_ida); #if IS_ENABLED(CONFIG_OF) @@ -187,6 +189,7 @@ static int gpio_shared_of_traverse(struct device_node *curr) ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr)); ref->flags = args.args[1]; + mutex_init(&ref->lock); if (strends(prop->name, "gpios")) suffix = "-gpios"; @@ -258,7 +261,7 @@ static int gpio_shared_make_adev(struct gpio_device *gdev, struct auxiliary_device *adev = &ref->adev; int ret; - lockdep_assert_held(&gpio_shared_lock); + guard(mutex)(&ref->lock); memset(adev, 0, sizeof(*adev)); @@ -373,14 +376,14 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) if (!lookup) return -ENOMEM; - guard(mutex)(&gpio_shared_lock); - list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(ref, &entry->refs, list) { if (!device_match_fwnode(consumer, ref->fwnode) && !gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) continue; + guard(mutex)(&ref->lock); + /* We've already done that on a previous request. */ if (ref->lookup) return 0; @@ -413,8 +416,6 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) static void gpio_shared_remove_adev(struct auxiliary_device *adev) { - lockdep_assert_held(&gpio_shared_lock); - auxiliary_device_delete(adev); auxiliary_device_uninit(adev); } @@ -426,8 +427,6 @@ int gpio_device_setup_shared(struct gpio_device *gdev) unsigned long *flags; int ret; - guard(mutex)(&gpio_shared_lock); - list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(ref, &entry->refs, list) { if (gdev->dev.parent == &ref->adev.dev) { @@ -484,13 +483,22 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) struct gpio_shared_entry *entry; struct gpio_shared_ref *ref; - guard(mutex)(&gpio_shared_lock); - list_for_each_entry(entry, &gpio_shared_list, list) { if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; + /* + * For some reason if we call synchronize_srcu() in GPIO core, + * descent here and take this mutex and then recursively call + * synchronize_srcu() again from gpiochip_remove() (which is + * totally fine) called after gpio_shared_remove_adev(), + * lockdep prints a false positive deadlock splat. Disable + * lockdep here. + */ + lockdep_off(); list_for_each_entry(ref, &entry->refs, list) { + guard(mutex)(&ref->lock); + if (ref->lookup) { gpiod_remove_lookup_table(ref->lookup); kfree(ref->lookup->table[0].key); @@ -500,6 +508,7 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) gpio_shared_remove_adev(&ref->adev); } + lockdep_on(); } } @@ -523,8 +532,6 @@ static void gpiod_shared_put(void *data) { struct gpio_shared_entry *entry = data; - lockdep_assert_not_held(&gpio_shared_lock); - kref_put(&entry->ref, gpio_shared_release); } @@ -562,8 +569,6 @@ struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev) struct gpio_shared_entry *entry; int ret; - lockdep_assert_not_held(&gpio_shared_lock); - entry = dev_get_platdata(dev); if (WARN_ON(!entry)) /* Programmer bug */ @@ -598,6 +603,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_shared_get); static void gpio_shared_drop_ref(struct gpio_shared_ref *ref) { list_del(&ref->list); + mutex_destroy(&ref->lock); kfree(ref->con_id); ida_free(&gpio_shared_ida, ref->dev_id); fwnode_handle_put(ref->fwnode);