From 3bb62e3f99a557d257e5f5a803200051b7de3afa Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 28 Apr 2026 21:52:39 -0500 Subject: [PATCH 01/12] gpiolib: acpi: Only trigger ActiveBoth interrupts on boot Commit ca876c7483b6 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") introduced logic to trigger edge-based GPIO interrupts during initialization to ensure proper initial state setup when firmware doesn't initialize it. However, according to the Microsoft GPIO documentation, triggering GPIO interrupts during initialization should only happen for interrupts marked as ActiveBoth (both IRQF_TRIGGER_RISING and IRQF_TRIGGER_FALLING) and only when the associated GPIO line is already asserted (logic level low). The current implementation incorrectly triggers: 1. Any edge-triggered interrupt (RISING-only or FALLING-only) 2. RISING interrupts when value is high and FALLING when value is low This causes problems at bootup for single-edge interrupts that don't follow the ActiveBoth pattern. Fix this by: - Only triggering when BOTH rising and falling edges are configured - Only triggering when the GPIO line is asserted (value == 0) Reported-by: Francesco Lauritano Closes: https://lore.kernel.org/all/6iFCwGH2vssb7NRUTWGpkubGMNbgIlBHSz40z8ZsezjxngXpoiiRiJaijviNvhiDAGIr43bfUmdxLmxYoHDjyft4DgwFc3Pnu5hzPguTa0s=@protonmail.com/ Tested-by: Marco Scardovi Fixes: ca876c7483b69 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") Link: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- Suggested-by: Armin Wolf Signed-off-by: Mario Limonciello Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi-core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 09f860200a05..eb8a40cfb7a9 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -233,12 +233,23 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, event->irq_requested = true; - /* Make sure we trigger the initial state of edge-triggered IRQs */ + /* + * Make sure we trigger the initial state of ActiveBoth IRQs. + * + * According to the Microsoft GPIO documentation, triggering GPIO + * interrupts marked as ActiveBoth during initialization is correct + * as long as the associated GPIO line is already "asserted" + * (logic level low). We should not trigger edge-based GPIO + * interrupts not marked as ActiveBoth. + * + * See: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- + * Section: "GPIO controllers and ActiveBoth interrupts" + */ if (acpi_gpio_need_run_edge_events_on_boot() && - (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { + ((event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) == + (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { value = gpiod_get_raw_value_cansleep(event->desc); - if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || - ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) + if (value == 0) event->handler(event->irq, event); } } From 4910aa198d25e5d1067236560ba34ab12bccc677 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:52:16 -0500 Subject: [PATCH 02/12] gpio: pisosr: Read "ngpios" as u32 The generic "ngpios" property is encoded as a normal uint32 cell. The pisosr driver stores it in the gpio_chip field, but reading it with a u16 helper does not match the DT property encoding. Read "ngpios" as u32 and keep the existing assignment to the chip field. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260612215216.1887485-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pisosr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index 7ec6a46ed600..2732ea8c16b7 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -112,6 +112,7 @@ static int pisosr_gpio_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct pisosr_gpio *gpio; + u32 ngpios; int ret; gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); @@ -120,7 +121,8 @@ static int pisosr_gpio_probe(struct spi_device *spi) gpio->chip = template_chip; gpio->chip.parent = dev; - of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio); + if (!of_property_read_u32(dev->of_node, "ngpios", &ngpios)) + gpio->chip.ngpio = ngpios; gpio->spi = spi; From 0482862a90169f4daaba0ed31a85d8304bf51e04 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 17:19:18 +0800 Subject: [PATCH 03/12] gpio: mlxbf3: fail probe if gpiochip registration fails mlxbf3_gpio_probe() logs a devm_gpiochip_add_data() failure but still returns success. That leaves the platform device bound even though the GPIO chip was not registered. Return the registration error so probe failure matches the missing gpiochip state. Fixes: cd33f216d241 ("gpio: mlxbf3: Add gpio driver support") Signed-off-by: Pengpeng Hou Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260615091918.43333-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mlxbf3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 4770578269ba..566326644a2c 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -255,7 +255,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(dev, gc, gs); if (ret) - dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); + return dev_err_probe(dev, ret, + "Failed adding memory mapped gpiochip\n"); return 0; } From dece79032f529d2c9fdbf63a9f2fc32244722775 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Wed, 10 Jun 2026 17:42:03 +0200 Subject: [PATCH 04/12] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources Ensure that GPIO pin resource arrays are safely bounded before accessing indices. Add explicit bounds checking in acpi_request_own_gpiod(), acpi_gpio_irq_is_wake(), and acpi_gpiochip_alloc_event() to prevent out-of-bounds array reads if the ACPI namespace provides malformed or empty pin tables. This change addresses potential safety issues arising from inconsistent or invalid ACPI pin tables. It does not alter functional behavior in well-formed tables. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260610154204.110379-2-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi-core.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 1a762a2988b7..b09f89832890 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -316,10 +316,17 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, unsigned int index, const char *label) { - int polarity = GPIO_ACTIVE_HIGH; - enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); - unsigned int pin = agpio->pin_table[index]; + enum gpiod_flags flags; struct gpio_desc *desc; + unsigned int pin; + int polarity; + + if (index >= agpio->pin_table_length) + return ERR_PTR(-EINVAL); + + pin = agpio->pin_table[index]; + polarity = GPIO_ACTIVE_HIGH; + flags = acpi_gpio_to_gpiod_flags(agpio, polarity); desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); if (IS_ERR(desc)) @@ -333,7 +340,12 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, static bool acpi_gpio_irq_is_wake(struct device *parent, const struct acpi_resource_gpio *agpio) { - unsigned int pin = agpio->pin_table[0]; + unsigned int pin; + + if (agpio->pin_table_length == 0) + return false; + + pin = agpio->pin_table[0]; if (agpio->wake_capable != ACPI_WAKE_CAPABLE) return false; @@ -363,6 +375,9 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, if (!acpi_gpio_get_irq_resource(ares, &agpio)) return AE_OK; + if (agpio->pin_table_length == 0) + return AE_OK; + handle = ACPI_HANDLE(chip->parent); pin = agpio->pin_table[0]; From ae9f812df3149729643d27d2af488c112f62af9a Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Wed, 10 Jun 2026 17:42:04 +0200 Subject: [PATCH 05/12] gpiolib: acpi: Prevent out-of-bounds pin access in OperationRegion handler The ACPI GPIO OperationRegion handler receives pin offsets as a 64-bit address. Previously, this value could be assigned to a pin index without validation, potentially causing out-of-bounds access if the ACPI table provides an invalid offset. This patch explicitly checks that the 64-bit address is less than agpio->pin_table_length before using it, returning AE_BAD_PARAMETER if the check fails. Additionally, it makes the length calculation overflow-safe and ensures proper unsigned types for loop counters. This corrects the commit message from v5 to accurately reflect the underlying issue, removing references to truncation or wrap-around, which do not occur in ACPICA. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260610154204.110379-3-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi-core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index b09f89832890..220f0ac4204e 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -1098,10 +1098,10 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_chip *chip = achip->chip; struct acpi_resource_gpio *agpio; struct acpi_resource *ares; - u16 pin_index = address; + unsigned int length; acpi_status status; - int length; - int i; + unsigned int i; + u16 pin_index; status = acpi_buffer_to_resource(achip->conn_info.connection, achip->conn_info.length, &ares); @@ -1121,7 +1121,14 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, return AE_BAD_PARAMETER; } - length = min(agpio->pin_table_length, pin_index + bits); + /* address represents GPIO pin index in connection table */ + if (address >= agpio->pin_table_length) { + ACPI_FREE(ares); + return AE_BAD_PARAMETER; + } + + pin_index = address; + length = min_t(unsigned int, agpio->pin_table_length, pin_index + bits); for (i = pin_index; i < length; ++i) { unsigned int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; From 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:34 +0800 Subject: [PATCH 06/12] gpio: sch: use raw_spinlock_t in the irq startup path sch_irq_unmask() enables the GPIO IRQ and then updates the controller state through sch_irq_mask_unmask(), which takes sch->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the SCH controller lock to raw_spinlock_t. The same lock is also used by the GPIO direction and value callbacks, but those critical sections only update MMIO-backed GPIO registers and do not contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. Fixes: 7a81638485c1 ("gpio: sch: Add edge event support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260617154035.1199948-2-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 966d16a6d515..5e361742a11a 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -39,7 +39,7 @@ struct sch_gpio { struct gpio_chip chip; void __iomem *regs; - spinlock_t lock; + raw_spinlock_t lock; unsigned short resume_base; /* GPE handling */ @@ -104,9 +104,9 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -122,9 +122,9 @@ static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -135,9 +135,9 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 0); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); /* * according to the datasheet, writing to the level register has no @@ -196,14 +196,14 @@ static int sch_irq_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTPE, rising); sch_gpio_reg_set(sch, gpio_num, GTNE, falling); irq_set_handler_locked(d, handle_edge_irq); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -215,9 +215,9 @@ static void sch_irq_ack(struct irq_data *d) irq_hw_number_t gpio_num = irqd_to_hwirq(d); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTS, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val) @@ -225,9 +225,9 @@ static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GGPE, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask(struct irq_data *d) @@ -268,12 +268,12 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) int offset; u32 ret; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status; for_each_set_bit(offset, &pending, sch->chip.ngpio) @@ -343,7 +343,7 @@ static int sch_gpio_probe(struct platform_device *pdev) sch->regs = regs; - spin_lock_init(&sch->lock); + raw_spin_lock_init(&sch->lock); sch->chip = sch_gpio_chip; sch->chip.label = dev_name(dev); sch->chip.parent = dev; From 90f0109019e6817eb40a486671b7722d1544ae29 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:35 +0800 Subject: [PATCH 07/12] gpio: eic-sprd: use raw_spinlock_t in the irq startup path sprd_eic_irq_unmask() enables the GPIO IRQ and then updates controller state through sprd_eic_update(), which takes sprd_eic->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sprd_eic_irq_unmask() -> sprd_eic_update() carrier and used the original spin_lock_irqsave(&sprd_eic->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sprd_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sprd_eic_update.constprop.0+0x48/0x90 [vuln_msv] sprd_eic_irq_unmask.constprop.0+0x35/0x50 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the Spreadtrum EIC controller lock to raw_spinlock_t. The locked section only serializes MMIO register updates and does not contain sleepable operations, so keeping it non-sleeping is appropriate for the irqchip callbacks. Fixes: 25518e024e3a ("gpio: Add Spreadtrum EIC driver support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260617154035.1199948-3-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-eic-sprd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 50fafeda8d7e..3b7ebcf12fe7 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -95,7 +95,7 @@ struct sprd_eic { struct notifier_block irq_nb; void __iomem *base[SPRD_EIC_MAX_BANK]; enum sprd_eic_type type; - spinlock_t lock; + raw_spinlock_t lock; int irq; }; @@ -149,7 +149,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, unsigned long flags; u32 tmp; - spin_lock_irqsave(&sprd_eic->lock, flags); + raw_spin_lock_irqsave(&sprd_eic->lock, flags); tmp = readl_relaxed(base + reg); if (val) @@ -158,7 +158,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, tmp &= ~BIT(SPRD_EIC_BIT(offset)); writel_relaxed(tmp, base + reg); - spin_unlock_irqrestore(&sprd_eic->lock, flags); + raw_spin_unlock_irqrestore(&sprd_eic->lock, flags); } static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg) @@ -628,7 +628,7 @@ static int sprd_eic_probe(struct platform_device *pdev) if (!sprd_eic) return -ENOMEM; - spin_lock_init(&sprd_eic->lock); + raw_spin_lock_init(&sprd_eic->lock); sprd_eic->type = pdata->type; sprd_eic->irq = platform_get_irq(pdev, 0); From 442d60df742a597dca7cca89a28a4843ce935f09 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 11 Jun 2026 09:59:27 +0200 Subject: [PATCH 08/12] x86/platform/geode: reference the real node of the cs5535 GPIO controller GPIO software node lookup should rely exclusively on matching the addresses of the referenced firmware nodes. Commit e5d527be7e69 ("gpio: swnode: don't use the swnode's name as the key for GPIO lookup") tried to enforce this but had to be reverted: it broke existing users who abuse the software node mechanism by creating "dummy" software nodes named after the device they want to get GPIOs from, without ever attaching them to the actual GPIO devices. Those users rely on GPIOLIB matching the label of the GPIO controller against the name of the software node rather than on a real firmware node link. Un-reverting e5d527be7e69 therefore requires converting all such users to real firmware node lookup. The geode board setup is one of them: it references the cs5535 GPIO controller through a locally-defined dummy node named "cs5535-gpio". The cs5535 MFD driver now exports the software node associated with its GPIO controller cell as cs5535_gpio_swnode. Use it as the target of the GPIO software node references in geode-common.c instead of the dummy node, so the lookup resolves by firmware node address. As the referenced node must exist at lookup time, make the cs5535 driver built-in for all boards selecting GEODE_COMMON (depend on GPIO_CS5535=y). The node is exported in the "CS5535" namespace, so import it in this module. Acked-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260611-cs5535-swnode-v3-1-2b0c517c0c03@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- arch/x86/Kconfig | 10 +++++----- arch/x86/platform/geode/geode-common.c | 12 +++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fa4f0079614c..bdad90f210e4 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -3026,7 +3026,7 @@ config GEODE_COMMON config ALIX bool "PCEngines ALIX System Support (LED setup)" - select GPIOLIB + depends on GPIO_CS5535=y select GEODE_COMMON help This option enables system support for the PCEngines ALIX. @@ -3034,21 +3034,21 @@ config ALIX ALIX2/3/6 boards. However, other system specific setup should get added here. - Note: You must still enable the drivers for GPIO and LED support - (GPIO_CS5535 & LEDS_GPIO) to actually use the LEDs + Note: You must still enable the drivers for LED support (LEDS_GPIO) + to actually use the LEDs Note: You have to set alix.force=1 for boards with Award BIOS. config NET5501 bool "Soekris Engineering net5501 System Support (LEDS, GPIO, etc)" - select GPIOLIB + depends on GPIO_CS5535=y select GEODE_COMMON help This option enables system support for the Soekris Engineering net5501. config GEOS bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)" - select GPIOLIB + depends on GPIO_CS5535=y select GEODE_COMMON depends on DMI help diff --git a/arch/x86/platform/geode/geode-common.c b/arch/x86/platform/geode/geode-common.c index 1843ae385e2d..679b4b07b790 100644 --- a/arch/x86/platform/geode/geode-common.c +++ b/arch/x86/platform/geode/geode-common.c @@ -9,15 +9,12 @@ #include #include #include +#include #include #include #include "geode-common.h" -static const struct software_node geode_gpiochip_node = { - .name = "cs5535-gpio", -}; - static const struct property_entry geode_gpio_keys_props[] = { PROPERTY_ENTRY_U32("poll-interval", 20), { } @@ -44,7 +41,6 @@ static const struct software_node geode_restart_key_node = { }; static const struct software_node *geode_gpio_keys_swnodes[] __initconst = { - &geode_gpiochip_node, &geode_gpio_keys_node, &geode_restart_key_node, NULL @@ -66,7 +62,7 @@ int __init geode_create_restart_key(unsigned int pin) struct platform_device *pd; int err; - geode_restart_gpio_ref = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, + geode_restart_gpio_ref = SOFTWARE_NODE_REFERENCE(&cs5535_gpio_swnode, pin, GPIO_ACTIVE_LOW); err = software_node_register_node_group(geode_gpio_keys_swnodes); @@ -143,7 +139,7 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, goto err_free_names; } - gpio_refs[i] = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, + gpio_refs[i] = SOFTWARE_NODE_REFERENCE(&cs5535_gpio_swnode, leds[i].pin, GPIO_ACTIVE_LOW); props[i * 3 + 0] = @@ -188,3 +184,5 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, kfree(swnodes); return err; } + +MODULE_IMPORT_NS("CS5535"); From 99dfa46baba29513d1094c8f30bc86c6ef88543a Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 23:53:19 +0800 Subject: [PATCH 09/12] gpiolib: initialize return value in gpiochip_set_multiple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpiochip_set_multiple() falls back to setting lines one by one when the chip does not provide set_multiple(). If the fallback path receives an empty mask, the loop is skipped and ret is returned without being initialized. Initialize ret to 0 so an empty mask is treated as a successful no-op. Fixes: 9b407312755f ("gpiolib: rework the wrapper around gpio_chip::set_multiple()") Signed-off-by: Ruoyu Wang Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260620155319.79994-1-ruoyuw560@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8a5ff78a1149..e5fb60111151 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3803,7 +3803,7 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { unsigned int i; - int ret; + int ret = 0; lockdep_assert_held(&gc->gpiodev->srcu); From 9068c631d5af20000d873e4f299fa0bac4e294d9 Mon Sep 17 00:00:00 2001 From: Igor Putko Date: Thu, 18 Jun 2026 18:56:24 +0300 Subject: [PATCH 10/12] gpio: tb10x: fix struct tb10x_gpio kernel-doc Fix build warning by adding the missing structure name and description to the kernel-doc comment block. Signed-off-by: Igor Putko Link: https://patch.msgid.link/20260618155626.18751-2-igorpetindev@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tb10x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 3c8fd322a713..705bfd80a8d0 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -33,6 +33,7 @@ /** + * struct tb10x_gpio - TB10x GPIO controller structure * @base: register base address * @domain: IRQ domain of GPIO generated interrupts managed by this controller * @irq: Interrupt line of parent interrupt controller From d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 23:24:39 +0800 Subject: [PATCH 11/12] gpio: tegra: do not call pinctrl for GPIO direction tegra_gpio_direction_input() and tegra_gpio_direction_output() already program the GPIO controller direction registers directly. The additional pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl operation, because the Tegra pinmux ops provide GPIO request/free handling but no gpio_set_direction hook. The extra call still enters the pinctrl core and takes pctldev->mutex. Shared GPIO users can call the direction path while holding their per-line spinlock, so this otherwise redundant pinctrl direction call can sleep in an atomic context. This was found by our static analysis tool and then confirmed by manual review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the Tegra pinctrl ops. The reviewed path has a default non-sleeping struct gpio_chip while the direction callback still enters the pinctrl mutex path. A directed runtime validation kept the same non-sleeping chip registration and drove: gpio_shared_proxy_direction_output() gpiod_direction_output_raw_commit() tegra_gpio_direction_output() pinctrl_gpio_direction_output() Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output() on the stack. Do not mark the whole chip as can_sleep to paper over this: can_sleep describes whether get()/set() may sleep, and Tegra value access is MMIO. Remove the redundant pinctrl direction calls and keep pinctrl involvement in the existing request/free path. Fixes: 11da90541283 ("gpio: tegra: Fix offset of pinctrl calls") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619152439.1239561-1-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index df06b56a2ade..fa6c8ee92093 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -172,18 +172,11 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_input(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl input direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_direction_output(struct gpio_chip *chip, @@ -191,19 +184,12 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_output(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl output direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_get_direction(struct gpio_chip *chip, From 4e8eb6952aa6749726c6c3763ae0032a6332c24f Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 10:31:06 +0800 Subject: [PATCH 12/12] gpio: davinci: fix IRQ domain leak on devm_kzalloc failure In davinci_gpio_irq_setup(), after successfully creating an IRQ domain with irq_domain_create_legacy(), a subsequent devm_kzalloc() failure in the bank loop causes the function to return -ENOMEM without removing the IRQ domain. Unlike devm-managed resources, irq_domain_create_legacy() does not auto-clean up on probe failure, so the domain is leaked. Fix by calling irq_domain_remove() before returning on allocation failure. Fixes: b5cf3fd827d2 ("gpio: davinci: Redesign driver to accommodate ngpios in one gpio chip") Signed-off-by: Qingshuang Fu Link: https://patch.msgid.link/20260623023106.117229-1-fffsqian@163.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 97780f27ce5b..270cd7c88812 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -568,8 +568,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) sizeof(struct davinci_gpio_irq_data), GFP_KERNEL); - if (!irqdata) + if (!irqdata) { + irq_domain_remove(chips->irq_domain); return -ENOMEM; + } irqdata->regs = g; irqdata->bank_num = bank;