From c03c6a086d188dc5b52e7db2b2991ecead9bb669 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Mon, 8 Jun 2026 01:05:03 +0200 Subject: [PATCH 01/61] gpio: rockchip: use devm_platform_ioremap_resource() to map registers Currently, the driver retrieves the memory resource with of_address_to_resource() and maps it with devm_ioremap_resource(). Since the bank device is a platform_device, simplify and modernize the code by using devm_platform_ioremap_resource(). This also removes the need for the local struct resource variable. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260607230504.35392-3-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 9478a58f1caa..8647d006d103 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -647,15 +647,10 @@ static void rockchip_clk_put(void *data) static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) { - struct resource res; + struct platform_device *pdev = to_platform_device(bank->dev); int id = 0, ret; - if (of_address_to_resource(bank->of_node, 0, &res)) { - dev_err(bank->dev, "cannot find IO resource for bank\n"); - return -ENOENT; - } - - bank->reg_base = devm_ioremap_resource(bank->dev, &res); + bank->reg_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bank->reg_base)) return PTR_ERR(bank->reg_base); From c2e26f2408226de7464ba4cdcd86827e0a000db9 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Mon, 8 Jun 2026 01:05:04 +0200 Subject: [PATCH 02/61] gpio: rockchip: use platform_get_irq() to retrieve interrupt The driver currently uses irq_of_parse_and_map() to parse and map the GPIO bank interrupt from the device tree node. Since the bank device is represented by a platform_device, use the standard platform_get_irq() API instead. This integrates cleanly with the platform device framework and ensures proper error propagation (such as -EPROBE_DEFER). Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260607230504.35392-4-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 8647d006d103..77b239a9a601 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -654,9 +654,10 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) if (IS_ERR(bank->reg_base)) return PTR_ERR(bank->reg_base); - bank->irq = irq_of_parse_and_map(bank->of_node, 0); - if (!bank->irq) - return -EINVAL; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + bank->irq = ret; bank->clk = devm_clk_get_enabled(bank->dev, NULL); if (IS_ERR(bank->clk)) From 23cf763ac5d084f9459a10a9fece9cb1ddc023d9 Mon Sep 17 00:00:00 2001 From: Igor Putko Date: Thu, 18 Jun 2026 18:56:25 +0300 Subject: [PATCH 03/61] gpio: tb10x: use unsigned int instead of bare unsigned Fix the checkpatch.pl warning by using 'unsigned int' instead of the bare use of 'unsigned' for the offset parameter in tb10x_gpio_to_irq(). Signed-off-by: Igor Putko Link: https://patch.msgid.link/20260618155626.18751-3-igorpetindev@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tb10x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 705bfd80a8d0..d30524dbc841 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -51,7 +51,7 @@ static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs) return ioread32(gpio->base + offs); } -static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) { struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip); From 309f81de06c00c12a41e318633e1d1d0c9d67e2c Mon Sep 17 00:00:00 2001 From: Igor Putko Date: Thu, 18 Jun 2026 18:56:26 +0300 Subject: [PATCH 04/61] gpio: tb10x: remove unnecessary braces Fix the checkpatch.pl warning by removing unnecessary braces from a single-statement if-block in tb10x_gpio_probe(). Signed-off-by: Igor Putko Link: https://patch.msgid.link/20260618155626.18751-4-igorpetindev@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tb10x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index d30524dbc841..7fb8e6223bd1 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -167,9 +167,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev) tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev), tb10x_gpio->chip.gc.ngpio, &irq_generic_chip_ops, NULL); - if (!tb10x_gpio->domain) { + if (!tb10x_gpio->domain) return -ENOMEM; - } ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain, tb10x_gpio->chip.gc.ngpio, 1, tb10x_gpio->chip.gc.label, From 8f32808e1530b2229e07695fb39c54fee910bd4a Mon Sep 17 00:00:00 2001 From: Tzuyi Chang Date: Mon, 22 Jun 2026 17:23:32 +0800 Subject: [PATCH 05/61] dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio Add the device tree bindings for the Realtek DHC (Digital Home Center) RTD1625 GPIO controllers. The RTD1625 GPIO controller features a per-pin register architecture that differs significantly from previous generations. It utilizes separate register blocks for GPIO configuration and interrupt control. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Tzuyi Chang Signed-off-by: Yu-Chun Lin Link: https://patch.msgid.link/20260622092335.1166876-2-eleanor.lin@realtek.com Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/realtek,rtd1625-gpio.yaml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml b/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml new file mode 100644 index 000000000000..f13c910b73c6 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2023 Realtek Semiconductor Corporation +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/realtek,rtd1625-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek DHC RTD1625 GPIO controller + +maintainers: + - Tzuyi Chang + +description: | + GPIO controller for the Realtek RTD1625 SoC, featuring a per-pin register + architecture that differs significantly from earlier RTD series controllers. + Each GPIO has dedicated registers for configuration (direction, input/output + values, debounce), and interrupt control supporting edge and level detection + modes. + +properties: + compatible: + enum: + - realtek,rtd1625-iso-gpio + - realtek,rtd1625-isom-gpio + + reg: + maxItems: 1 + + interrupts: + items: + - description: Interrupt number of the assert GPIO interrupt, which is + triggered when there is a rising edge. + - description: Interrupt number of the deassert GPIO interrupt, which is + triggered when there is a falling edge. + - description: Interrupt number of the level-sensitive GPIO interrupt, + triggered by a configured logic level. + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + gpio-ranges: true + + gpio-controller: true + + "#gpio-cells": + const: 2 + +required: + - compatible + - reg + - gpio-ranges + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + gpio@89100 { + compatible = "realtek,rtd1625-isom-gpio"; + reg = <0x89100 0x30>; + interrupt-parent = <&iso_m_irq_mux>; + interrupts = <0>, <1>, <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&isom_pinctrl 0 0 4>; + gpio-controller; + #gpio-cells = <2>; + }; From b5d23fcdb12972c522e96f90ab48be8a0d971b0e Mon Sep 17 00:00:00 2001 From: Yu-Chun Lin Date: Mon, 22 Jun 2026 17:23:33 +0800 Subject: [PATCH 06/61] gpio: Replace "default y" with "default ARCH_REALTEK" in Kconfig Replace "default y" with "default ARCH_REALTEK" to avoid bloating the build for non-Realtek platforms when COMPILE_TEST is enabled on other platforms. Signed-off-by: Yu-Chun Lin Link: https://patch.msgid.link/20260622092335.1166876-3-eleanor.lin@realtek.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 28cf6d2e83c2..ed2bc3113374 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -646,7 +646,7 @@ config GPIO_ROCKCHIP config GPIO_RTD tristate "Realtek DHC GPIO support" depends on ARCH_REALTEK || COMPILE_TEST - default y + default ARCH_REALTEK select GPIOLIB_IRQCHIP help This option enables support for GPIOs found on Realtek DHC(Digital From a57e27c43b0315ee86c6896510d69be5257e093e Mon Sep 17 00:00:00 2001 From: Tzuyi Chang Date: Mon, 22 Jun 2026 17:23:34 +0800 Subject: [PATCH 07/61] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC Add support for the GPIO controller found on Realtek DHC RTD1625 SoCs. Unlike the existing Realtek GPIO driver (drivers/gpio/gpio-rtd.c), which manages pins via shared bank registers, the RTD1625 introduces a per-pin register architecture. Each GPIO line now has its own dedicated 32-bit control register to manage configuration independently, including direction, output value, input value, interrupt enable, and debounce. Therefore, this distinct hardware design requires a separate driver. Additionally, the RTD1625 GPIO controller has a specific hardware quirk: it fires both 'assert' and 'de-assert' interrupts simultaneously on any edge toggle. To handle this, we utilize the polarity register to route the requested edge (rising/falling) to the 'assert' IRQ line. The driver then filters out the unwanted 'de-assert' interrupt in the IRQ handler and pre-clears edge interrupts to prevent interrupt storms caused by unhandled dropped interrupts. Interrupt support is optional for this device, matching the dt-bindings. If the interrupts property is not provided, the driver simply skips IRQ initialization and operates purely as a basic GPIO controller. Reviewed-by: Linus Walleij Signed-off-by: Tzuyi Chang Co-developed-by: Yu-Chun Lin Signed-off-by: Yu-Chun Lin Link: https://patch.msgid.link/20260622092335.1166876-4-eleanor.lin@realtek.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 12 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-rtd1625.c | 611 ++++++++++++++++++++++++++++++++++++ 3 files changed, 624 insertions(+) create mode 100644 drivers/gpio/gpio-rtd1625.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ed2bc3113374..f03c05288376 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -656,6 +656,18 @@ config GPIO_RTD Say yes here to support GPIO functionality and GPIO interrupt on Realtek DHC SoCs. +config GPIO_RTD1625 + tristate "Realtek DHC RTD1625 GPIO support" + depends on ARCH_REALTEK || COMPILE_TEST + default ARCH_REALTEK + select GPIOLIB_IRQCHIP + help + This option enables support for the GPIO controller on Realtek + DHC (Digital Home Center) RTD1625 SoC. + + Say yes here to support both basic GPIO line functionality + and GPIO interrupt handling capabilities for this platform. + config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" depends on OF diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4d0e900402fc..fa14581e3995 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -160,6 +160,7 @@ obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o +obj-$(CONFIG_GPIO_RTD1625) += gpio-rtd1625.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o diff --git a/drivers/gpio/gpio-rtd1625.c b/drivers/gpio/gpio-rtd1625.c new file mode 100644 index 000000000000..409e540bf40b --- /dev/null +++ b/drivers/gpio/gpio-rtd1625.c @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Realtek DHC RTD1625 gpio driver + * + * Copyright (c) 2023-2026 Realtek Semiconductor Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTD1625_GPIO_DIR BIT(0) +#define RTD1625_GPIO_OUT BIT(2) +#define RTD1625_GPIO_IN BIT(4) +#define RTD1625_GPIO_EDGE_INT_DP BIT(6) +#define RTD1625_GPIO_EDGE_INT_EN BIT(8) +#define RTD1625_GPIO_LEVEL_INT_EN BIT(16) +#define RTD1625_GPIO_LEVEL_INT_DP BIT(18) +#define RTD1625_GPIO_DEBOUNCE GENMASK(30, 28) +#define RTD1625_GPIO_DEBOUNCE_WREN BIT(31) + +#define RTD1625_GPIO_WREN(x) ((x) << 1) + +/* Write-enable masks for all GPIO configs and reserved hardware bits */ +#define RTD1625_ISO_GPIO_WREN_ALL 0x8000aa8a +#define RTD1625_ISOM_GPIO_WREN_ALL 0x800aaa8a + +#define RTD1625_GPIO_DEBOUNCE_1US 0 +#define RTD1625_GPIO_DEBOUNCE_10US 1 +#define RTD1625_GPIO_DEBOUNCE_100US 2 +#define RTD1625_GPIO_DEBOUNCE_1MS 3 +#define RTD1625_GPIO_DEBOUNCE_10MS 4 +#define RTD1625_GPIO_DEBOUNCE_20MS 5 +#define RTD1625_GPIO_DEBOUNCE_30MS 6 +#define RTD1625_GPIO_DEBOUNCE_50MS 7 + +#define GPIO_CONTROL(gpio) ((gpio) * 4) + +enum rtd1625_irq_index { + RTD1625_IRQ_ASSERT, + RTD1625_IRQ_DEASSERT, + RTD1625_IRQ_LEVEL, + RTD1625_MAX_IRQS +}; + +/** + * struct rtd1625_gpio_info - Specific GPIO register information + * @num_gpios: The number of GPIOs + * @irq_type_support: Supported IRQ types + * @gpa_offset: Offset for GPIO assert interrupt status registers + * @gpda_offset: Offset for GPIO deassert interrupt status registers + * @level_offset: Offset of level interrupt status register + * @write_en_all: Write-enable mask for all configurable bits + */ +struct rtd1625_gpio_info { + unsigned int num_gpios; + unsigned int irq_type_support; + unsigned int base_offset; + unsigned int gpa_offset; + unsigned int gpda_offset; + unsigned int level_offset; + unsigned int write_en_all; +}; + +struct rtd1625_gpio { + struct gpio_chip gpio_chip; + const struct rtd1625_gpio_info *info; + void __iomem *base; + void __iomem *irq_base; + unsigned int irqs[RTD1625_MAX_IRQS]; + raw_spinlock_t lock; + unsigned int *save_regs; +}; + +static unsigned int rtd1625_gpio_gpa_offset(struct rtd1625_gpio *data, unsigned int offset) +{ + return data->info->gpa_offset + ((offset / 32) * 4); +} + +static unsigned int rtd1625_gpio_gpda_offset(struct rtd1625_gpio *data, unsigned int offset) +{ + return data->info->gpda_offset + ((offset / 32) * 4); +} + +static unsigned int rtd1625_gpio_level_offset(struct rtd1625_gpio *data, unsigned int offset) +{ + return data->info->level_offset + ((offset / 32) * 4); +} + +static int rtd1625_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, + unsigned int debounce) +{ + struct rtd1625_gpio *data = gpiochip_get_data(chip); + u8 deb_val; + u32 val; + + switch (debounce) { + case 1: + deb_val = RTD1625_GPIO_DEBOUNCE_1US; + break; + case 10: + deb_val = RTD1625_GPIO_DEBOUNCE_10US; + break; + case 100: + deb_val = RTD1625_GPIO_DEBOUNCE_100US; + break; + case 1000: + deb_val = RTD1625_GPIO_DEBOUNCE_1MS; + break; + case 10000: + deb_val = RTD1625_GPIO_DEBOUNCE_10MS; + break; + case 20000: + deb_val = RTD1625_GPIO_DEBOUNCE_20MS; + break; + case 30000: + deb_val = RTD1625_GPIO_DEBOUNCE_30MS; + break; + case 50000: + deb_val = RTD1625_GPIO_DEBOUNCE_50MS; + break; + default: + return -ENOTSUPP; + } + + val = FIELD_PREP(RTD1625_GPIO_DEBOUNCE, deb_val) | RTD1625_GPIO_DEBOUNCE_WREN; + + guard(raw_spinlock_irqsave)(&data->lock); + + writel_relaxed(val, data->base + GPIO_CONTROL(offset)); + + return 0; +} + +static int rtd1625_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + u32 debounce; + + if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) { + debounce = pinconf_to_config_argument(config); + return rtd1625_gpio_set_debounce(chip, offset, debounce); + } + + return gpiochip_generic_config(chip, offset, config); +} + +static int rtd1625_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct rtd1625_gpio *data = gpiochip_get_data(chip); + u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_OUT); + + if (value) + val |= RTD1625_GPIO_OUT; + + guard(raw_spinlock_irqsave)(&data->lock); + + writel_relaxed(val, data->base + GPIO_CONTROL(offset)); + + return 0; +} + +static int rtd1625_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct rtd1625_gpio *data = gpiochip_get_data(chip); + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + val = readl_relaxed(data->base + GPIO_CONTROL(offset)); + + if (val & RTD1625_GPIO_DIR) + return !!(val & RTD1625_GPIO_OUT); + else + return !!(val & RTD1625_GPIO_IN); +} + +static int rtd1625_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rtd1625_gpio *data = gpiochip_get_data(chip); + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + val = readl_relaxed(data->base + GPIO_CONTROL(offset)); + + if (val & RTD1625_GPIO_DIR) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int rtd1625_gpio_set_direction(struct gpio_chip *chip, unsigned int offset, bool out) +{ + struct rtd1625_gpio *data = gpiochip_get_data(chip); + u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_DIR); + + if (out) + val |= RTD1625_GPIO_DIR; + + guard(raw_spinlock_irqsave)(&data->lock); + + writel_relaxed(val, data->base + GPIO_CONTROL(offset)); + + return 0; +} + +static int rtd1625_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + return rtd1625_gpio_set_direction(chip, offset, false); +} + +static int rtd1625_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) +{ + rtd1625_gpio_set(chip, offset, value); + + return rtd1625_gpio_set_direction(chip, offset, true); +} + +static void rtd1625_gpio_irq_handle(struct irq_desc *desc) +{ + unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset); + struct rtd1625_gpio *data = irq_desc_get_handler_data(desc); + struct irq_domain *domain = data->gpio_chip.irq.domain; + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int irq = irq_desc_get_irq(desc); + unsigned long status; + unsigned int reg_offset, i, j; + unsigned int girq; + irq_hw_number_t hwirq; + u32 irq_type; + + if (irq == data->irqs[RTD1625_IRQ_ASSERT]) + get_reg_offset = &rtd1625_gpio_gpa_offset; + else if (irq == data->irqs[RTD1625_IRQ_DEASSERT]) + get_reg_offset = &rtd1625_gpio_gpda_offset; + else if (irq == data->irqs[2]) + get_reg_offset = &rtd1625_gpio_level_offset; + else + return; + + chained_irq_enter(chip, desc); + + for (i = 0; i < data->info->num_gpios; i += 32) { + reg_offset = get_reg_offset(data, i); + status = readl_relaxed(data->irq_base + reg_offset); + + /* + * Hardware quirk: The controller fires both "assert" and "de-assert" + * interrupts simultaneously on any edge toggle. + * We must pre-clear edge interrupts here. If we drop an unwanted + * de-assert interrupt below, it will never reach the IRQ core + * (generic_handle_domain_irq), meaning ->irq_ack() won't be called. + * Failing to clear it here leads to an interrupt storm. + */ + if (irq != data->irqs[RTD1625_IRQ_LEVEL]) + writel_relaxed(status, data->irq_base + reg_offset); + + for_each_set_bit(j, &status, 32) { + hwirq = i + j; + girq = irq_find_mapping(domain, hwirq); + irq_type = irq_get_trigger_type(girq); + + /* + * Filter out the hardware-forced de-assert interrupt unless + * the user explicitly requested IRQ_TYPE_EDGE_BOTH. + */ + if (irq == data->irqs[RTD1625_IRQ_DEASSERT] && + irq_type != IRQ_TYPE_EDGE_BOTH) + continue; + + generic_handle_domain_irq(domain, hwirq); + } + } + + chained_irq_exit(chip, desc); +} + +static void rtd1625_gpio_ack_irq(struct irq_data *d) +{ + struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 irq_type = irqd_get_trigger_type(d); + u32 bit_mask = BIT(hwirq % 32); + int reg_offset; + + if (irq_type & IRQ_TYPE_LEVEL_MASK) { + reg_offset = rtd1625_gpio_level_offset(data, hwirq); + writel_relaxed(bit_mask, data->irq_base + reg_offset); + } +} + +static void rtd1625_gpio_enable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) +{ + int gpda_reg_offset = rtd1625_gpio_gpda_offset(data, hwirq); + int gpa_reg_offset = rtd1625_gpio_gpa_offset(data, hwirq); + u32 clr_mask = BIT(hwirq % 32); + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + writel_relaxed(clr_mask, data->irq_base + gpa_reg_offset); + writel_relaxed(clr_mask, data->irq_base + gpda_reg_offset); + val = RTD1625_GPIO_EDGE_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN); + writel_relaxed(val, data->base + GPIO_CONTROL(hwirq)); +} + +static void rtd1625_gpio_disable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) +{ + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN); + writel_relaxed(val, data->base + GPIO_CONTROL(hwirq)); +} + +static void rtd1625_gpio_enable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) +{ + int level_reg_offset = rtd1625_gpio_level_offset(data, hwirq); + u32 clr_mask = BIT(hwirq % 32); + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + writel_relaxed(clr_mask, data->irq_base + level_reg_offset); + val = RTD1625_GPIO_LEVEL_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN); + writel_relaxed(val, data->base + GPIO_CONTROL(hwirq)); +} + +static void rtd1625_gpio_disable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) +{ + u32 val; + + guard(raw_spinlock_irqsave)(&data->lock); + + val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN); + writel_relaxed(val, data->base + GPIO_CONTROL(hwirq)); +} + +static void rtd1625_gpio_enable_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd1625_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 irq_type = irqd_get_trigger_type(d); + + gpiochip_enable_irq(gc, hwirq); + + if (irq_type & IRQ_TYPE_EDGE_BOTH) + rtd1625_gpio_enable_edge_irq(data, hwirq); + else if (irq_type & IRQ_TYPE_LEVEL_MASK) + rtd1625_gpio_enable_level_irq(data, hwirq); +} + +static void rtd1625_gpio_disable_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd1625_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 irq_type = irqd_get_trigger_type(d); + + if (irq_type & IRQ_TYPE_EDGE_BOTH) + rtd1625_gpio_disable_edge_irq(data, hwirq); + else if (irq_type & IRQ_TYPE_LEVEL_MASK) + rtd1625_gpio_disable_level_irq(data, hwirq); + + gpiochip_disable_irq(gc, hwirq); +} + +static int rtd1625_gpio_irq_set_level_type(struct irq_data *d, bool level) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd1625_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_DP); + + if (!(data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK)) + return -EINVAL; + + if (level) + val |= RTD1625_GPIO_LEVEL_INT_DP; + + scoped_guard(raw_spinlock_irqsave, &data->lock) + writel_relaxed(val, data->base + GPIO_CONTROL(hwirq)); + + irq_set_handler_locked(d, handle_level_irq); + + return 0; +} + +static int rtd1625_gpio_irq_set_edge_type(struct irq_data *d, bool polarity) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct rtd1625_gpio *data = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_DP); + + if (!(data->info->irq_type_support & IRQ_TYPE_EDGE_BOTH)) + return -EINVAL; + + if (polarity) + val |= RTD1625_GPIO_EDGE_INT_DP; + + scoped_guard(raw_spinlock_irqsave, &data->lock) + writel_relaxed(val, data->base + GPIO_CONTROL(hwirq)); + + irq_set_handler_locked(d, handle_edge_irq); + + return 0; +} + +static int rtd1625_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + return rtd1625_gpio_irq_set_edge_type(d, 1); + + case IRQ_TYPE_EDGE_FALLING: + return rtd1625_gpio_irq_set_edge_type(d, 0); + + case IRQ_TYPE_EDGE_BOTH: + return rtd1625_gpio_irq_set_edge_type(d, 1); + + case IRQ_TYPE_LEVEL_HIGH: + return rtd1625_gpio_irq_set_level_type(d, 0); + + case IRQ_TYPE_LEVEL_LOW: + return rtd1625_gpio_irq_set_level_type(d, 1); + + default: + return -EINVAL; + } +} + +static struct irq_chip rtd1625_iso_gpio_irq_chip = { + .name = "rtd1625-gpio", + .irq_ack = rtd1625_gpio_ack_irq, + .irq_mask = rtd1625_gpio_disable_irq, + .irq_unmask = rtd1625_gpio_enable_irq, + .irq_set_type = rtd1625_gpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_gpio *data) +{ + struct gpio_irq_chip *irq_chip; + unsigned int num_irqs; + int irq; + + /* + * Interrupt support is optional. All IRQs must be provided together. + * If index 0 is missing, we assume no interrupts are configured in DT + * and fall back to basic GPIO operation. + */ + irq = platform_get_irq_optional(pdev, 0); + if (irq == -ENXIO) + return 0; + if (irq < 0) + return irq; + + num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2; + data->irqs[RTD1625_IRQ_ASSERT] = irq; + + for (unsigned int i = 1; i < num_irqs; i++) { + irq = platform_get_irq(pdev, i); + if (irq < 0) + return irq; + data->irqs[i] = irq; + } + + irq_chip = &data->gpio_chip.irq; + irq_chip->handler = handle_bad_irq; + irq_chip->default_type = IRQ_TYPE_NONE; + irq_chip->parent_handler = rtd1625_gpio_irq_handle; + irq_chip->parent_handler_data = data; + irq_chip->num_parents = num_irqs; + irq_chip->parents = data->irqs; + + gpio_irq_chip_set_chip(irq_chip, &rtd1625_iso_gpio_irq_chip); + + return 0; +} + +static int rtd1625_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtd1625_gpio *data; + void __iomem *irq_base; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->info = device_get_match_data(dev); + if (!data->info) + return -EINVAL; + + raw_spin_lock_init(&data->lock); + + irq_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(irq_base)) + return PTR_ERR(irq_base); + + data->irq_base = irq_base; + data->base = irq_base + data->info->base_offset; + + data->save_regs = devm_kcalloc(dev, data->info->num_gpios, sizeof(*data->save_regs), + GFP_KERNEL); + if (!data->save_regs) + return -ENOMEM; + + data->gpio_chip.label = dev_name(dev); + data->gpio_chip.base = -1; + data->gpio_chip.ngpio = data->info->num_gpios; + data->gpio_chip.request = gpiochip_generic_request; + data->gpio_chip.free = gpiochip_generic_free; + data->gpio_chip.get_direction = rtd1625_gpio_get_direction; + data->gpio_chip.direction_input = rtd1625_gpio_direction_input; + data->gpio_chip.direction_output = rtd1625_gpio_direction_output; + data->gpio_chip.set = rtd1625_gpio_set; + data->gpio_chip.get = rtd1625_gpio_get; + data->gpio_chip.set_config = rtd1625_gpio_set_config; + data->gpio_chip.parent = dev; + + ret = rtd1625_gpio_setup_irq(pdev, data); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return devm_gpiochip_add_data(dev, &data->gpio_chip, data); +} + +static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = { + .num_gpios = 166, + .irq_type_support = IRQ_TYPE_EDGE_BOTH, + .base_offset = 0x100, + .gpa_offset = 0x000, + .gpda_offset = 0x020, + .write_en_all = RTD1625_ISO_GPIO_WREN_ALL, +}; + +static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = { + .num_gpios = 4, + .irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW | + IRQ_TYPE_LEVEL_HIGH, + .base_offset = 0x20, + .gpa_offset = 0x00, + .gpda_offset = 0x04, + .level_offset = 0x18, + .write_en_all = RTD1625_ISOM_GPIO_WREN_ALL, +}; + +static int rtd1625_gpio_suspend(struct device *dev) +{ + struct rtd1625_gpio *data = dev_get_drvdata(dev); + const struct rtd1625_gpio_info *info = data->info; + + for (unsigned int i = 0; i < info->num_gpios; i++) + data->save_regs[i] = readl_relaxed(data->base + GPIO_CONTROL(i)); + + return 0; +} + +static int rtd1625_gpio_resume(struct device *dev) +{ + struct rtd1625_gpio *data = dev_get_drvdata(dev); + const struct rtd1625_gpio_info *info = data->info; + + for (unsigned int i = 0; i < info->num_gpios; i++) + writel_relaxed(data->save_regs[i] | info->write_en_all, + data->base + GPIO_CONTROL(i)); + + return 0; +} + +static DEFINE_NOIRQ_DEV_PM_OPS(rtd1625_gpio_pm_ops, rtd1625_gpio_suspend, rtd1625_gpio_resume); + +static const struct of_device_id rtd1625_gpio_of_matches[] = { + { .compatible = "realtek,rtd1625-iso-gpio", .data = &rtd1625_iso_gpio_info }, + { .compatible = "realtek,rtd1625-isom-gpio", .data = &rtd1625_isom_gpio_info }, + { } +}; +MODULE_DEVICE_TABLE(of, rtd1625_gpio_of_matches); + +static struct platform_driver rtd1625_gpio_platform_driver = { + .driver = { + .name = "gpio-rtd1625", + .of_match_table = rtd1625_gpio_of_matches, + .pm = pm_sleep_ptr(&rtd1625_gpio_pm_ops), + }, + .probe = rtd1625_gpio_probe, +}; +module_platform_driver(rtd1625_gpio_platform_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Realtek Semiconductor Corporation"); +MODULE_DESCRIPTION("Realtek DHC SoC RTD1625 gpio driver"); From 415c63896b76fd98883e190bc7518383cb01779c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 13:37:44 +0800 Subject: [PATCH 08/61] gpio: pca9570: Use I2C match data The driver stores chip metadata in both the OF match table and the I2C id table, but probe currently reads it with device_get_match_data(). That helper only returns firmware-node match data, so an I2C-id/modalias match can leave chip_data NULL before the driver dereferences it to read ngpio. Use i2c_get_match_data() so the I2C id table driver_data is consumed when firmware match data is not present. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624053744.72612-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca9570.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index 7a47a9aa0414..db027c10494f 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -133,7 +133,7 @@ static int pca9570_probe(struct i2c_client *client) gpio->chip.get = pca9570_get; gpio->chip.set = pca9570_set; gpio->chip.base = -1; - gpio->chip_data = device_get_match_data(&client->dev); + gpio->chip_data = i2c_get_match_data(client); gpio->chip.ngpio = gpio->chip_data->ngpio; gpio->chip.can_sleep = true; From 069547f9d8f817d5d0ba7a8276b0ddefd22222ee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 10 Jun 2026 15:29:17 +0200 Subject: [PATCH 09/61] gpio: kunit: shrink initialization code We always start the swnode lookup test cases by registering the provider and consumer drivers. Factor it out into a common function and use the .init() callback of the kunit suite struct. Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260610-gpio-kunit-swnode-hog-v1-1-37b3bf37096c@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-kunit.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c index 380b68f879e5..d6a80ea03543 100644 --- a/drivers/gpio/gpiolib-kunit.c +++ b/drivers/gpio/gpiolib-kunit.c @@ -91,13 +91,8 @@ static struct platform_driver gpio_swnode_consumer_driver = { }, }; -static void gpio_swnode_lookup_by_primary(struct kunit *test) +static int gpio_swnode_register_drivers(struct kunit *test) { - struct gpio_swnode_consumer_pdata *pdata; - struct platform_device_info pdevinfo; - struct property_entry properties[2]; - struct platform_device *pdev; - bool bound = false; int ret; ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); @@ -106,6 +101,17 @@ static void gpio_swnode_lookup_by_primary(struct kunit *test) ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); KUNIT_ASSERT_EQ(test, ret, 0); + return 0; +} + +static void gpio_swnode_lookup_by_primary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *pdev; + bool bound = false; + pdevinfo = (struct platform_device_info){ .name = GPIO_TEST_PROVIDER, .id = PLATFORM_DEVID_NONE, @@ -149,7 +155,6 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test) struct fwnode_handle *primary; struct platform_device *pdev; bool bound = false; - int ret; /* * Can't live on the stack as it will still get referenced in cleanup @@ -158,12 +163,6 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test) primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); - ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); - KUNIT_ASSERT_EQ(test, ret, 0); - - ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); - KUNIT_ASSERT_EQ(test, ret, 0); - fwnode_init(primary, NULL); pdevinfo = (struct platform_device_info){ @@ -211,6 +210,7 @@ static struct kunit_case gpio_swnode_lookup_tests[] = { static struct kunit_suite gpio_swnode_lookup_test_suite = { .name = "gpio-swnode-lookup", .test_cases = gpio_swnode_lookup_tests, + .init = gpio_swnode_register_drivers, }; static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier); From a05cfd98c5eca766a4a127bde6278095fc367442 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 10 Jun 2026 15:29:18 +0200 Subject: [PATCH 10/61] gpio: kunit: add test cases for software node hogs Add a test suite containing cases verifying that GPIO hogs described with software nodes are correctly setup by GPIO core. Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260610-gpio-kunit-swnode-hog-v1-2-37b3bf37096c@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-kunit.c | 254 ++++++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c index d6a80ea03543..c9c5b4374820 100644 --- a/drivers/gpio/gpiolib-kunit.c +++ b/drivers/gpio/gpiolib-kunit.c @@ -18,19 +18,67 @@ #define GPIO_TEST_PROVIDER "gpio-test-provider" #define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" #define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" +#define GPIO_CONSUMER_NAME "gpio-swnode-consumer-test-device" + +#define GPIO_TEST_PROVIDER_NGPIO 4 + +/* + * The test provider tracks per-line direction and value so that lines can be + * driven as both inputs and outputs - this is needed to exercise input as well + * as output GPIO hogs. + */ +struct gpio_test_provider_data { + DECLARE_BITMAP(is_output, GPIO_TEST_PROVIDER_NGPIO); + DECLARE_BITMAP(values, GPIO_TEST_PROVIDER_NGPIO); +}; static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset) { - return GPIO_LINE_DIRECTION_OUT; + struct gpio_test_provider_data *data = gpiochip_get_data(gc); + + return test_bit(offset, data->is_output) ? + GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int gpio_test_provider_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_test_provider_data *data = gpiochip_get_data(gc); + + clear_bit(offset, data->is_output); + + return 0; +} + +static int gpio_test_provider_direction_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct gpio_test_provider_data *data = gpiochip_get_data(gc); + + set_bit(offset, data->is_output); + __assign_bit(offset, data->values, value); + + return 0; +} + +static int gpio_test_provider_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_test_provider_data *data = gpiochip_get_data(gc); + + return test_bit(offset, data->values); } static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value) { + struct gpio_test_provider_data *data = gpiochip_get_data(gc); + + __assign_bit(offset, data->values, value); + return 0; } static int gpio_test_provider_probe(struct platform_device *pdev) { + struct gpio_test_provider_data *data; struct device *dev = &pdev->dev; struct gpio_chip *gc; @@ -38,16 +86,26 @@ static int gpio_test_provider_probe(struct platform_device *pdev) if (!gc) return -ENOMEM; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* Lines start as outputs to preserve the default for lookup tests. */ + bitmap_fill(data->is_output, GPIO_TEST_PROVIDER_NGPIO); + gc->base = -1; - gc->ngpio = 4; - gc->label = "gpio-swnode-consumer-test-device"; + gc->ngpio = GPIO_TEST_PROVIDER_NGPIO; + gc->label = GPIO_CONSUMER_NAME; gc->parent = dev; gc->owner = THIS_MODULE; gc->get_direction = gpio_test_provider_get_direction; + gc->direction_input = gpio_test_provider_direction_input; + gc->direction_output = gpio_test_provider_direction_output; + gc->get = gpio_test_provider_get; gc->set = gpio_test_provider_set; - return devm_gpiochip_add_data(dev, gc, NULL); + return devm_gpiochip_add_data(dev, gc, data); } static struct platform_driver gpio_test_provider_driver = { @@ -63,10 +121,12 @@ static const struct software_node gpio_test_provider_swnode = { struct gpio_swnode_consumer_pdata { bool gpio_ok; + int errno; }; static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = { .gpio_ok = false, + .errno = 0, }; static int gpio_swnode_consumer_probe(struct platform_device *pdev) @@ -76,8 +136,10 @@ static int gpio_swnode_consumer_probe(struct platform_device *pdev) struct gpio_desc *desc; desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); - if (IS_ERR(desc)) + if (IS_ERR(desc)) { + pdata->errno = PTR_ERR(desc); return PTR_ERR(desc); + } pdata->gpio_ok = true; @@ -348,9 +410,191 @@ static struct kunit_suite gpio_unbind_with_consumers_test_suite = { .test_cases = gpio_unbind_with_consumers_tests, }; +/* + * GPIO line hogs are described by child software nodes of the provider + * carrying the "gpio-hog" property. They are picked up automatically when the + * gpiochip is registered. Each hog below sits on a distinct line of the + * provider. + */ +#define GPIO_HOG_OUTPUT_HIGH_OFFSET 0 +#define GPIO_HOG_OUTPUT_LOW_OFFSET 1 +#define GPIO_HOG_INPUT_OFFSET 2 + +static const u32 gpio_hog_output_high_gpios[] = { + GPIO_HOG_OUTPUT_HIGH_OFFSET, GPIO_ACTIVE_HIGH, +}; + +static const struct property_entry gpio_hog_output_high_properties[] = { + PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_output_high_gpios), + PROPERTY_ENTRY_STRING("line-name", "hog-output-high"), + PROPERTY_ENTRY_BOOL("output-high"), + PROPERTY_ENTRY_BOOL("gpio-hog"), + { } +}; + +static const struct software_node gpio_hog_output_high_swnode = + SOFTWARE_NODE("hog-output-high", gpio_hog_output_high_properties, + &gpio_test_provider_swnode); + +static const u32 gpio_hog_output_low_gpios[] = { + GPIO_HOG_OUTPUT_LOW_OFFSET, GPIO_ACTIVE_HIGH, +}; + +static const struct property_entry gpio_hog_output_low_properties[] = { + PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_output_low_gpios), + PROPERTY_ENTRY_STRING("line-name", "hog-output-low"), + PROPERTY_ENTRY_BOOL("output-low"), + PROPERTY_ENTRY_BOOL("gpio-hog"), + { } +}; + +static const struct software_node gpio_hog_output_low_swnode = + SOFTWARE_NODE("hog-output-low", gpio_hog_output_low_properties, + &gpio_test_provider_swnode); + +static const u32 gpio_hog_input_gpios[] = { + GPIO_HOG_INPUT_OFFSET, GPIO_ACTIVE_HIGH, +}; + +static const struct property_entry gpio_hog_input_properties[] = { + PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_input_gpios), + PROPERTY_ENTRY_STRING("line-name", "hog-input"), + PROPERTY_ENTRY_BOOL("input"), + PROPERTY_ENTRY_BOOL("gpio-hog"), + { } +}; + +static const struct software_node gpio_hog_input_swnode = + SOFTWARE_NODE("hog-input", gpio_hog_input_properties, + &gpio_test_provider_swnode); + +static const struct software_node *const gpio_hog_swnodes[] = { + &gpio_test_provider_swnode, + &gpio_hog_output_high_swnode, + &gpio_hog_output_low_swnode, + &gpio_hog_input_swnode, + NULL +}; + +/* + * Bring up the provider with a single hog child registered and verify both + * that the line was configured with the expected direction and that it is now + * exclusively owned (a consumer asking for the same line fails to bind). + * + * The provider node is referenced by the device through its fwnode rather than + * being handed to .swnode, so the device takes no software node reference of + * its own. Both the provider and the hog child are therefore test-managed and + * torn down (child first) once the test case completes. + */ +static void gpio_hog_assert(struct kunit *test, unsigned int offset, + int expected_direction) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *pdev; + struct fwnode_handle *fwnode; + struct gpio_desc *desc; + bool bound = true; + int ret; + + fwnode = software_node_fwnode(&gpio_test_provider_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .fwnode = fwnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + + /* The hog must have configured the line with the expected direction. */ + struct gpio_device *gdev __free(gpio_device_put) = + gpio_device_find_by_label(GPIO_CONSUMER_NAME); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gdev); + + desc = gpio_device_get_desc(gdev, offset); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + ret = gpiod_get_direction(desc); + KUNIT_ASSERT_EQ(test, ret, expected_direction); + + /* A hogged line is owned exclusively, so a consumer must fail to bind. */ + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + offset, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_FALSE(test, pdata->gpio_ok); + KUNIT_ASSERT_EQ(test, pdata->errno, -EBUSY); +} + +static void gpio_hog_output_high(struct kunit *test) +{ + gpio_hog_assert(test, GPIO_HOG_OUTPUT_HIGH_OFFSET, GPIO_LINE_DIRECTION_OUT); +} + +static void gpio_hog_output_low(struct kunit *test) +{ + gpio_hog_assert(test, GPIO_HOG_OUTPUT_LOW_OFFSET, GPIO_LINE_DIRECTION_OUT); +} + +static void gpio_hog_input(struct kunit *test) +{ + gpio_hog_assert(test, GPIO_HOG_INPUT_OFFSET, GPIO_LINE_DIRECTION_IN); +} + +static int gpio_hog_suite_init(struct kunit_suite *suite) +{ + return software_node_register_node_group(gpio_hog_swnodes); +} + +static void gpio_hog_suite_exit(struct kunit_suite *suite) +{ + software_node_unregister_node_group(gpio_hog_swnodes); +} + +static struct kunit_case gpio_swnode_hog_tests[] = { + KUNIT_CASE(gpio_hog_output_high), + KUNIT_CASE(gpio_hog_output_low), + KUNIT_CASE(gpio_hog_input), + { } +}; + +static struct kunit_suite gpio_swnode_hog_test_suite = { + .name = "gpio-swnode-hog", + .test_cases = gpio_swnode_hog_tests, + .suite_init = gpio_hog_suite_init, + .suite_exit = gpio_hog_suite_exit, + .init = gpio_swnode_register_drivers, +}; + kunit_test_suites( &gpio_swnode_lookup_test_suite, &gpio_unbind_with_consumers_test_suite, + &gpio_swnode_hog_test_suite, ); MODULE_DESCRIPTION("Test module for the GPIO subsystem"); From 45bd4e25f81f3afb550f04376a54f4c5e1d5ab11 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 18 Feb 2026 22:46:01 -0800 Subject: [PATCH 11/61] gpio: amd-fch: switch to guard() notation guard() is more concise and ensures that lock is released at the end of the scope. Signed-off-by: Dmitry Torokhov Reviewed-by: Linus Walleij Link: https://patch.msgid.link/aZarCgDvMUta4Viq@google.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-amd-fch.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c index 9f329938202b..9b9d75acf35a 100644 --- a/drivers/gpio/gpio-amd-fch.c +++ b/drivers/gpio/gpio-amd-fch.c @@ -48,13 +48,11 @@ static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv, static int amd_fch_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) { - unsigned long flags; struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); void __iomem *ptr = amd_fch_gpio_addr(priv, offset); - spin_lock_irqsave(&priv->lock, flags); + guard(spinlock_irqsave)(&priv->lock); writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr); - spin_unlock_irqrestore(&priv->lock, flags); return 0; } @@ -62,12 +60,11 @@ static int amd_fch_gpio_direction_input(struct gpio_chip *gc, static int amd_fch_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, int value) { - unsigned long flags; struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); u32 val; - spin_lock_irqsave(&priv->lock, flags); + guard(spinlock_irqsave)(&priv->lock); val = readl_relaxed(ptr); if (value) @@ -77,33 +74,28 @@ static int amd_fch_gpio_direction_output(struct gpio_chip *gc, writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; } static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) { int ret; - unsigned long flags; struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); - spin_lock_irqsave(&priv->lock, flags); + guard(spinlock_irqsave)(&priv->lock); ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION); - spin_unlock_irqrestore(&priv->lock, flags); return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { - unsigned long flags; struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); u32 mask; - spin_lock_irqsave(&priv->lock, flags); + guard(spinlock_irqsave)(&priv->lock); mask = readl_relaxed(ptr); if (value) @@ -112,22 +104,18 @@ static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) mask &= ~AMD_FCH_GPIO_FLAG_WRITE; writel_relaxed(mask, ptr); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; } static int amd_fch_gpio_get(struct gpio_chip *gc, unsigned int offset) { - unsigned long flags; u32 val; struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); void __iomem *ptr = amd_fch_gpio_addr(priv, offset); - spin_lock_irqsave(&priv->lock, flags); + guard(spinlock_irqsave)(&priv->lock); val = readl_relaxed(ptr); - spin_unlock_irqrestore(&priv->lock, flags); return FIELD_GET(AMD_FCH_GPIO_FLAG_READ, val); } From b67e64d4eb9765d23010f423e29a627a25f5ac2e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 28 Jun 2026 16:02:02 -0700 Subject: [PATCH 12/61] gpio: mvebu: add wake-up interrupt support Implement wake IRQ support for the mvebu GPIO controller: - Replace unused irqbase field with bank_irq[4] to store per-bank IRQ numbers for use in the wake-up callback. - Add mvebu_gpio_set_wake_irq() that forwards enable_irq_wake / disable_irq_wake to the correct parent IRQ based on hwirq. - Set IRQCHIP_SET_TYPE_MASKED and IRQCHIP_MASK_ON_SUSPEND flags on both level and edge chip types. - Set IRQ_GC_INIT_NESTED_LOCK for the nested irq domain. - Add missing include. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260628230202.1209991-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 40 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index c030d1f00abc..26092b223be5 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +115,7 @@ struct mvebu_gpio_chip { struct regmap *regs; u32 offset; struct regmap *percpu_regs; - int irqbase; + int bank_irq[4]; struct irq_domain *domain; int soc_variant; @@ -603,6 +604,34 @@ static const struct regmap_config mvebu_gpio_regmap_config = { .val_bits = 32, }; +/* + * Forward wake-up configuration to the parent bank IRQ. + * @d: interrupt data + * @enable: enable as wake-up if non-zero + * + * Return: 0 on success, or a negative error code. + */ +static int mvebu_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mvebu_gpio_chip *mvchip = gc->private; + int bank; + int irq; + + bank = d->hwirq / 8; + if (bank >= ARRAY_SIZE(mvchip->bank_irq)) + return -EINVAL; + + irq = mvchip->bank_irq[bank]; + if (irq <= 0) + return -EINVAL; + + if (enable) + return enable_irq_wake(irq); + + return disable_irq_wake(irq); +} + /* * Functions implementing the pwm_chip methods */ @@ -1249,7 +1278,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) err = irq_alloc_domain_generic_chips( mvchip->domain, ngpios, 2, np->name, handle_level_irq, - IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, 0); + IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, IRQ_GC_INIT_NESTED_LOCK); if (err) { dev_err(&pdev->dev, "couldn't allocate irq chips %s (DT).\n", mvchip->chip.label); @@ -1267,6 +1296,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ct->chip.irq_mask = mvebu_gpio_level_irq_mask; ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask; ct->chip.irq_set_type = mvebu_gpio_irq_set_type; + ct->chip.irq_set_wake = mvebu_gpio_set_wake_irq; + ct->chip.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; ct->chip.name = mvchip->chip.label; ct = &gc->chip_types[1]; @@ -1275,6 +1306,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ct->chip.irq_mask = mvebu_gpio_edge_irq_mask; ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask; ct->chip.irq_set_type = mvebu_gpio_irq_set_type; + ct->chip.irq_set_wake = mvebu_gpio_set_wake_irq; + ct->chip.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; ct->handler = handle_edge_irq; ct->chip.name = mvchip->chip.label; @@ -1283,13 +1316,14 @@ static int mvebu_gpio_probe(struct platform_device *pdev) * interrupt handlers, with each handler dealing with 8 GPIO * pins. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(mvchip->bank_irq); i++) { int irq = platform_get_irq_optional(pdev, i); if (irq < 0) continue; irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler, mvchip); + mvchip->bank_irq[i] = irq; } return 0; From 6dd9672a16bfbe59427aa0694fdb94553e7949f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Wed, 1 Jul 2026 18:56:58 +0200 Subject: [PATCH 13/61] gpio: nomadik: convert nmk_gpio_populate_chip() to goto cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove duplicate teardown code that is found in all error if statements. Replace by goto-based cleanup labels. Signed-off-by: Théo Lebrun Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260701-gpio-nomadik-silent-v1-1-644d10316cef@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-nomadik.c | 44 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index e22b713166d7..f25f251f4757 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -527,15 +527,15 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, if (device_property_read_u32(gpio_dev, "gpio-bank", &id)) { dev_err(dev, "populate: gpio-bank property not found\n"); - platform_device_put(gpio_pdev); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err_put_pdev; } #ifdef CONFIG_PINCTRL_NOMADIK if (id >= ARRAY_SIZE(nmk_gpio_chips)) { dev_err(dev, "populate: invalid id: %u\n", id); - platform_device_put(gpio_pdev); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err_put_pdev; } /* Already populated? */ nmk_chip = nmk_gpio_chips[id]; @@ -547,8 +547,8 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, nmk_chip = devm_kzalloc(dev, sizeof(*nmk_chip), GFP_KERNEL); if (!nmk_chip) { - platform_device_put(gpio_pdev); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_put_pdev; } if (device_property_read_u32(gpio_dev, "ngpios", &ngpio)) { @@ -569,16 +569,16 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) { - platform_device_put(gpio_pdev); - return ERR_CAST(base); + ret = PTR_ERR(base); + goto err_put_pdev; } nmk_chip->addr = base; /* NOTE: do not use devm_ here! */ clk = clk_get_optional(gpio_dev, NULL); if (IS_ERR(clk)) { - platform_device_put(gpio_pdev); - return ERR_CAST(clk); + ret = PTR_ERR(clk); + goto err_put_pdev; } clk_prepare(clk); nmk_chip->clk = clk; @@ -586,12 +586,9 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, /* NOTE: do not use devm_ here! */ reset = reset_control_get_optional_shared(gpio_dev, NULL); if (IS_ERR(reset)) { - clk_unprepare(clk); - clk_put(clk); - platform_device_put(gpio_pdev); - dev_err(dev, "failed getting reset control: %pe\n", - reset); - return ERR_CAST(reset); + dev_err(dev, "failed getting reset control: %pe\n", reset); + ret = PTR_ERR(reset); + goto err_unprepare_clk; } /* @@ -601,18 +598,23 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, */ ret = reset_control_deassert(reset); if (ret) { - reset_control_put(reset); - clk_unprepare(clk); - clk_put(clk); - platform_device_put(gpio_pdev); dev_err(dev, "failed reset deassert: %d\n", ret); - return ERR_PTR(ret); + goto err_put_reset; } #ifdef CONFIG_PINCTRL_NOMADIK nmk_gpio_chips[id] = nmk_chip; #endif return nmk_chip; + +err_put_reset: + reset_control_put(reset); +err_unprepare_clk: + clk_unprepare(clk); + clk_put(clk); +err_put_pdev: + platform_device_put(gpio_pdev); + return ERR_PTR(ret); } static void nmk_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) From 2123813511d495b99b605abe4db167793698e0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Wed, 1 Jul 2026 18:56:59 +0200 Subject: [PATCH 14/61] gpio: nomadik: add missing dev_err() call on chip populate failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All error paths of nmk_gpio_populate_chip() lead to logging errors but this one (ignoring the alloc or ioremap failures that must not log). Add the single missing dev_err() call. Signed-off-by: Théo Lebrun Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260701-gpio-nomadik-silent-v1-2-644d10316cef@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-nomadik.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index f25f251f4757..4a7db282bad8 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -578,6 +578,7 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, clk = clk_get_optional(gpio_dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); + dev_err(dev, "failed getting clock: %d\n", ret); goto err_put_pdev; } clk_prepare(clk); From b0901d4dc17e37edbc928c01dced21ea86da87f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Wed, 1 Jul 2026 18:57:00 +0200 Subject: [PATCH 15/61] gpio: nomadik: drop duplicate probe error line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all error codepaths in nmk_gpio_populate_chip() log an error, drop dev_err() call that is made on nmk_gpio_populate_chip() failure. Current boot log: [ 0.544230] nomadik-gpio 1400000.gpio: failed getting reset control: -EPROBE_DEFER [ 0.544274] nomadik-gpio 1400000.gpio: could not populate nmk chip struct The second line is always redundant (or is logged when we shouldn't log, like ioremap or alloc failures). Signed-off-by: Théo Lebrun Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260701-gpio-nomadik-silent-v1-3-644d10316cef@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-nomadik.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 4a7db282bad8..eba095eeb3d6 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -651,10 +651,8 @@ static int nmk_gpio_probe(struct platform_device *pdev) int ret; nmk_chip = nmk_gpio_populate_chip(dev_fwnode(dev), pdev); - if (IS_ERR(nmk_chip)) { - dev_err(dev, "could not populate nmk chip struct\n"); + if (IS_ERR(nmk_chip)) return PTR_ERR(nmk_chip); - } supports_sleepmode = device_property_read_bool(dev, "st,supports-sleepmode"); From 56716e37447a5bd4f9fbd6e314acacce191e313f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Wed, 1 Jul 2026 18:57:01 +0200 Subject: [PATCH 16/61] gpio: nomadik: use dev_err_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpio-nomadik depends on a few resources. In one case the reset is taking time to show up leading to a boot log containing: [ 0.544230] nomadik-gpio 1400000.gpio: failed getting reset control: -EPROBE_DEFER Fix by replacing all dev_err() calls that might be made at probe with dev_err_probe(). On nomadik platforms, the nmk_gpio_populate_chip() log calls might attach their reasons to the gpio or pinctrl device depending on boot order. Signed-off-by: Théo Lebrun Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260701-gpio-nomadik-silent-v1-4-644d10316cef@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-nomadik.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index eba095eeb3d6..1ee46f59d708 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -520,21 +520,22 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, gpio_dev = bus_find_device_by_fwnode(&platform_bus_type, fwnode); if (!gpio_dev) { - dev_err(dev, "populate \"%pfwP\": device not found\n", fwnode); - return ERR_PTR(-ENODEV); + ret = -ENODEV; + dev_err_probe(dev, ret, "populate \"%pfwP\": device not found\n", fwnode); + return ERR_PTR(ret); } gpio_pdev = to_platform_device(gpio_dev); if (device_property_read_u32(gpio_dev, "gpio-bank", &id)) { - dev_err(dev, "populate: gpio-bank property not found\n"); ret = -EINVAL; + dev_err_probe(dev, ret, "populate: gpio-bank property not found\n"); goto err_put_pdev; } #ifdef CONFIG_PINCTRL_NOMADIK if (id >= ARRAY_SIZE(nmk_gpio_chips)) { - dev_err(dev, "populate: invalid id: %u\n", id); ret = -EINVAL; + dev_err_probe(dev, ret, "populate: invalid id: %u\n", id); goto err_put_pdev; } /* Already populated? */ @@ -578,7 +579,7 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, clk = clk_get_optional(gpio_dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - dev_err(dev, "failed getting clock: %d\n", ret); + dev_err_probe(dev, ret, "failed getting clock\n"); goto err_put_pdev; } clk_prepare(clk); @@ -587,8 +588,8 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, /* NOTE: do not use devm_ here! */ reset = reset_control_get_optional_shared(gpio_dev, NULL); if (IS_ERR(reset)) { - dev_err(dev, "failed getting reset control: %pe\n", reset); ret = PTR_ERR(reset); + dev_err_probe(dev, ret, "failed getting reset control\n"); goto err_unprepare_clk; } @@ -599,7 +600,7 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, */ ret = reset_control_deassert(reset); if (ret) { - dev_err(dev, "failed reset deassert: %d\n", ret); + dev_err_probe(dev, ret, "failed reset deassert\n"); goto err_put_reset; } @@ -695,7 +696,7 @@ static int nmk_gpio_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, nmk_gpio_irq_handler, IRQF_SHARED, dev_name(dev), nmk_chip); if (ret) { - dev_err(dev, "failed requesting IRQ\n"); + dev_err_probe(dev, ret, "failed requesting IRQ\n"); return ret; } From edbcefc9b0d5925f8be4227fc180a92cd49d126e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Wed, 1 Jul 2026 18:57:02 +0200 Subject: [PATCH 17/61] gpio: nomadik: drop "chip registered" log on probe success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Successful driver probing should be silent. Drop unconditional dev_info() call that is done at nmk_gpio_probe() exit. Signed-off-by: Théo Lebrun Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260701-gpio-nomadik-silent-v1-5-644d10316cef@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-nomadik.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 1ee46f59d708..244331f468cc 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -712,8 +712,6 @@ static int nmk_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nmk_chip); - dev_info(dev, "chip registered\n"); - return 0; } From 9bb4c0b37d54fc7d61f2a21cfa635fa2e3a29ac5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 2 Jul 2026 14:42:54 +0200 Subject: [PATCH 18/61] regmap-irq: Provide IRQ resource request and release callbacks The users which rely on regmap IRQ to create the IRQ chip may also want to have an additional tracking of the IRQ requests and releases. Provide a callback for them. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260702130903.1790633-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 22 ++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 07234d415b51..99b55b1053ee 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -296,6 +296,26 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int regmap_irq_reqres(struct irq_data *data) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); + + if (d->chip->irq_reqres) + return d->chip->irq_reqres(d->chip->irq_drv_data, hwirq); + + return 0; +} + +static void regmap_irq_relres(struct irq_data *data) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); + + if (d->chip->irq_relres) + d->chip->irq_relres(d->chip->irq_drv_data, hwirq); +} + static const struct irq_chip regmap_irq_chip = { .irq_bus_lock = regmap_irq_lock, .irq_bus_sync_unlock = regmap_irq_sync_unlock, @@ -303,6 +323,8 @@ static const struct irq_chip regmap_irq_chip = { .irq_enable = regmap_irq_enable, .irq_set_type = regmap_irq_set_type, .irq_set_wake = regmap_irq_set_wake, + .irq_request_resources = regmap_irq_reqres, + .irq_release_resources = regmap_irq_relres, }; static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index df44cb30f53b..370baa19db87 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1770,6 +1770,8 @@ struct regmap_irq_chip { void *irq_drv_data); unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data, unsigned int base, int index); + int (*irq_reqres)(void *irq_drv_data, irq_hw_number_t hwirq); + void (*irq_relres)(void *irq_drv_data, irq_hw_number_t hwirq); void *irq_drv_data; }; From 83957567b1589bcaf5fafbc359cc17f79a59ed29 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:12 +0200 Subject: [PATCH 19/61] gpio: mt7621: unify naming style in driver code There is a mix of 'mediatek' and 'mt7621' mix of prefix in different function names along the code of the driver. Be consistent using 'mt7621' for all function prefixes. Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-5-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mt7621.c | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 1b0b5247d3c9..87086c322f08 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -68,7 +68,7 @@ mt7621_gpio_gc_to_priv(struct gpio_chip *gc) } static inline struct mtk_gc * -to_mediatek_gpio(struct gpio_chip *chip) +to_mt7621_gpio(struct gpio_chip *chip) { struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(chip); @@ -137,7 +137,7 @@ mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank) } static void -mediatek_gpio_irq_unmask(struct irq_data *d) +mt7621_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct mtk_gc *rg = gpiochip_get_data(gc); @@ -159,7 +159,7 @@ mediatek_gpio_irq_unmask(struct irq_data *d) } static void -mediatek_gpio_irq_mask(struct irq_data *d) +mt7621_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct mtk_gc *rg = gpiochip_get_data(gc); @@ -181,7 +181,7 @@ mediatek_gpio_irq_mask(struct irq_data *d) } static int -mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) +mt7621_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct mtk_gc *rg = gpiochip_get_data(gc); @@ -245,11 +245,11 @@ mt7621_gpio_irq_relres(struct irq_data *d) } static int -mediatek_gpio_xlate(struct gpio_chip *chip, +mt7621_gpio_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) { int gpio = spec->args[0]; - struct mtk_gc *rg = to_mediatek_gpio(chip); + struct mtk_gc *rg = to_mt7621_gpio(chip); if (rg->bank != gpio / MTK_BANK_WIDTH) return -EINVAL; @@ -264,10 +264,10 @@ static const struct irq_chip mt7621_irq_chip = { .name = "mt7621-gpio", .irq_request_resources = mt7621_gpio_irq_reqres, .irq_release_resources = mt7621_gpio_irq_relres, - .irq_mask_ack = mediatek_gpio_irq_mask, - .irq_mask = mediatek_gpio_irq_mask, - .irq_unmask = mediatek_gpio_irq_unmask, - .irq_set_type = mediatek_gpio_irq_type, + .irq_mask_ack = mt7621_gpio_irq_mask, + .irq_mask = mt7621_gpio_irq_mask, + .irq_unmask = mt7621_gpio_irq_unmask, + .irq_set_type = mt7621_gpio_irq_type, .flags = IRQCHIP_IMMUTABLE, }; @@ -380,7 +380,7 @@ mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) } static int -mediatek_gpio_bank_probe(struct device *dev, int bank) +mt7621_gpio_bank_probe(struct device *dev, int bank) { struct gpio_generic_chip_config config; struct mtk *mtk = dev_get_drvdata(dev); @@ -416,7 +416,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) } rg->chip.gc.of_gpio_n_cells = 2; - rg->chip.gc.of_xlate = mediatek_gpio_xlate; + rg->chip.gc.of_xlate = mt7621_gpio_xlate; rg->chip.gc.ngpio = MTK_BANK_WIDTH; rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", dev_name(dev), bank); @@ -443,7 +443,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) } static int -mediatek_gpio_probe(struct platform_device *pdev) +mt7621_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk *mtk; @@ -477,7 +477,7 @@ mediatek_gpio_probe(struct platform_device *pdev) return ret; for (i = 0; i < MTK_BANK_CNT; i++) { - ret = mediatek_gpio_bank_probe(dev, i); + ret = mt7621_gpio_bank_probe(dev, i); if (ret) return ret; } @@ -485,18 +485,18 @@ mediatek_gpio_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id mediatek_gpio_match[] = { +static const struct of_device_id mt7621_gpio_match[] = { { .compatible = "mediatek,mt7621-gpio" }, {}, }; -MODULE_DEVICE_TABLE(of, mediatek_gpio_match); +MODULE_DEVICE_TABLE(of, mt7621_gpio_match); -static struct platform_driver mediatek_gpio_driver = { - .probe = mediatek_gpio_probe, +static struct platform_driver mt7621_gpio_driver = { + .probe = mt7621_gpio_probe, .driver = { .name = "mt7621_gpio", - .of_match_table = mediatek_gpio_match, + .of_match_table = mt7621_gpio_match, }, }; -builtin_platform_driver(mediatek_gpio_driver); +builtin_platform_driver(mt7621_gpio_driver); From 44d75ed98ddde54edc55e9300bb70ea9a7c62f4b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 3 Jul 2026 09:09:46 +0700 Subject: [PATCH 20/61] gpio: cdev: Drop redundant nonseekable_open() return check nonseekable_open() never fails, so the error check is unnecessary. Remove the dead error handling path. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260703020947.8010-1-phucduc.bui@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 82f27db0b230..9f3b628d5793 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2682,15 +2682,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) file->private_data = cdev; cdev->fp = file; - ret = nonseekable_open(inode, file); - if (ret) - goto out_unregister_device_notifier; + return nonseekable_open(inode, file); - return ret; - -out_unregister_device_notifier: - blocking_notifier_chain_unregister(&gdev->device_notifier, - &cdev->device_unregistered_nb); out_unregister_line_notifier: scoped_guard(write_lock_irqsave, &gdev->line_state_lock) raw_notifier_chain_unregister(&gdev->line_state_notifier, From 1048d391664a5b56dc5db4a1944cde47d749c9a6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 4 Jul 2026 17:10:39 +0200 Subject: [PATCH 21/61] gpio: rcar: Drop unused FILONOFF macro The FILONOFF macro is never used in the driver, drop it. No functional change. Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20260704151055.211082-1-marek.vasut+renesas@mailbox.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 86777e097fd8..09bebde5c426 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -59,7 +59,6 @@ struct gpio_rcar_priv { #define MSKCLR 0x1c /* Interrupt Mask Clear Register */ #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ #define EDGLEVEL 0x24 /* Edge/level Select Register */ -#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ #define OUTDTSEL 0x40 /* Output Data Select Register */ #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ #define INEN 0x50 /* General Input Enable Register */ From 8fe6fa0f223f44b8b869319d1b9c383749aa147b Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:22:30 +0800 Subject: [PATCH 22/61] gpio: sifive: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704122230.75964-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sifive.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index 94ef2efbd14f..e9a992cd4b9c 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -260,6 +260,7 @@ static const struct of_device_id sifive_gpio_match[] = { { .compatible = "sifive,fu540-c000-gpio" }, { }, }; +MODULE_DEVICE_TABLE(of, sifive_gpio_match); static struct platform_driver sifive_gpio_driver = { .probe = sifive_gpio_probe, From 611fd6cfe13997245f1f1b59b81e957163491773 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 2 Jul 2026 09:51:00 +0200 Subject: [PATCH 23/61] gpio: swnode: remove deprecated lookup mechanism 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 abused 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 relied on GPIOLIB matching the label of the GPIO controller against the name of the software node rather than on a real firmware node link. All such users have now been coverted to using attached software nodes via the fwnode address lookup path and the kernel documentation has been updated to recommend it as the correct approach. This allows us to remove the old behavior. This will allow us to leverage the upcoming support for fw_devlink for software nodes in GPIO core. Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260702-gpio-swnode-drop-label-matching-v2-1-0838349eb644@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-swnode.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index 8d9591aa9304..304994c5c7d0 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -26,7 +26,6 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) { const struct software_node *gdev_node; - struct gpio_device *gdev; gdev_node = to_software_node(fwnode); if (!gdev_node) @@ -41,27 +40,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) return ERR_PTR(-ENOENT); fwnode_lookup: - gdev = gpio_device_find_by_fwnode(fwnode); - if (!gdev && gdev_node && gdev_node->name) - /* - * FIXME: We shouldn't need to compare the GPIO controller's - * label against the software node that is supposedly attached - * to it. However there are currently GPIO users that - knowing - * the expected label of the GPIO chip whose pins they want to - * control - set up dummy software nodes named after those GPIO - * controllers, which aren't actually attached to them. In this - * case gpio_device_find_by_fwnode() will fail as no device on - * the GPIO bus is actually associated with the fwnode we're - * looking for. - * - * As a fallback: continue checking the label if we have no - * match. However, the situation described above is an abuse - * of the software node API and should be phased out and the - * following line - eventually removed. - */ - gdev = gpio_device_find_by_label(gdev_node->name); - - return gdev ?: ERR_PTR(-EPROBE_DEFER); + return gpio_device_find_by_fwnode(fwnode) ?: ERR_PTR(-EPROBE_DEFER); } static int swnode_gpio_get_reference(const struct fwnode_handle *fwnode, From 975a1d61260cf74d467f34898fade63165c80735 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:21:23 +0800 Subject: [PATCH 24/61] gpio: vf610: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260704122123.73004-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-vf610.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index aa8586d8a787..adfa2c2d5873 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -83,6 +83,7 @@ static const struct of_device_id vf610_gpio_dt_ids[] = { { .compatible = "fsl,imx8ulp-gpio", .data = &imx8ulp_data, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, vf610_gpio_dt_ids); static inline void vf610_gpio_writel(u32 val, void __iomem *reg) { From fccb8d28a4e3b8dcfa8606c1a8d225299f20b64e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 11 Jul 2026 22:59:31 +0200 Subject: [PATCH 25/61] dt-bindings: gpio: pca95xx: Document Kinetic KTS1622 The Kinetic Technologies KTS1622 is a 16-bit general-purpose I/O expander via the I2C bus for microcontrollers when additional I/Os are needed while keeping interconnections to the minimum. Datasheet comparison suggests that it is compatible with TCAL6416, add the compatible string and TCAL6416 as a fallback compatible. Signed-off-by: Marek Vasut Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260711210131.236025-3-marex@nabladev.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 4f955f855e1a..4631388a7d91 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -22,6 +22,9 @@ properties: - items: - const: diodes,pi4ioe5v6534q - const: nxp,pcal6534 + - items: + - const: kinetic,kts1622 + - const: ti,tcal6416 - items: - enum: - exar,xra1202 From 57eb9d14b4f6dad9c340f88f767fc54609bcdba5 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 12 Jul 2026 11:31:48 +0200 Subject: [PATCH 26/61] gpio: sloppy-logic-analyzer: add a comment explaining the buffer init To avoid more false positive reports of "leaking memory" when fops_buf_size_set() returns an error. Signed-off-by: Wolfram Sang Link: https://patch.msgid.link/20260712093148.21446-2-wsa+renesas@sang-engineering.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sloppy-logic-analyzer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 2bbd308ca08e..6748ddce3c19 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -237,6 +237,7 @@ static int gpio_la_poll_probe(struct platform_device *pdev) if (ret) return ret; + /* Initially allocate a buffer. It currently is NULL */ fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); From 7cefedf0c4a2d943f96f440b433bf71a1a8261d7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 13 Jul 2026 16:29:16 -0700 Subject: [PATCH 27/61] gpiolib: remove trailing comma from sentinel in GPIO_LOOKUP_SINGLE The GPIO_LOOKUP_SINGLE() macro defines a lookup table with a single entry followed by an empty sentinel entry. The sentinel entry has a trailing comma which is unnecessary. Remove it. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/alV0wIgZAY_InGYV@google.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 5eb88f5d0630..1a141056716f 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -54,7 +54,7 @@ static struct gpiod_lookup_table _name = { \ .dev_id = _dev_id, \ .table = { \ GPIO_LOOKUP(_key, _chip_hwnum, _con_id, _flags), \ - {}, \ + { } \ }, \ } From a8e59e3ac9f7f7154e0e80f45933799da8b4dc89 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Jul 2026 18:02:58 +0100 Subject: [PATCH 28/61] gpio: bd72720: make read-only const arrays regs static Don't populate the read-only const arrays regs on the stack at run time, instead make them static. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20260714170258.186766-1-colin.i.king@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bd72720.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c index 306e23411209..c124d964e4cf 100644 --- a/drivers/gpio/gpio-bd72720.c +++ b/drivers/gpio/gpio-bd72720.c @@ -85,9 +85,11 @@ static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset) static int bd72720gpo_get(struct bd72720_gpio *bdgpio, unsigned int offset) { - const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, - BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, - BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL }; + static const int regs[] = { + BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, + BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, + BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL + }; int ret, val; ret = regmap_read(bdgpio->regmap, regs[offset], &val); @@ -111,9 +113,11 @@ static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset, int value) { struct bd72720_gpio *bdgpio = gpiochip_get_data(chip); - const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, - BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, - BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL }; + static const int regs[] = { + BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, + BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, + BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL + }; if (BIT(offset) & bdgpio->gpio_is_input) { dev_dbg(bdgpio->dev, "pin %d not output.\n", offset); @@ -132,9 +136,11 @@ static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) { struct bd72720_gpio *bdgpio = gpiochip_get_data(chip); - const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, - BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, - BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL }; + static const int regs[] = { + BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, + BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, + BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL + }; /* * We can only set the output mode, which makes sense only when output From 9fdbdb528488403146ee56d6904e96955edde860 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Jul 2026 13:14:44 +0200 Subject: [PATCH 29/61] kunit: provide a set of fwnode-oriented helpers Provide three new kunit-managed helpers for test cases that need to register/create dynamic software nodes. Reviewed-by: David Gow Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260713-swnode-fw-devlink-v4-1-d4f2dee27ad9@oss.qualcomm.com Signed-off-by: Danilo Krummrich --- include/kunit/fwnode.h | 29 ++++++++ lib/kunit/Makefile | 1 + lib/kunit/fwnode.c | 146 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 include/kunit/fwnode.h create mode 100644 lib/kunit/fwnode.c diff --git a/include/kunit/fwnode.h b/include/kunit/fwnode.h new file mode 100644 index 000000000000..e250bb87e144 --- /dev/null +++ b/include/kunit/fwnode.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * KUnit resource management helpers for firmware nodes. + * + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#ifndef _KUNIT_FWNODE_H +#define _KUNIT_FWNODE_H + +struct device; +struct fwnode_handle; +struct kunit; +struct property_entry; +struct software_node; + +struct fwnode_handle * +kunit_fwnode_create_software_node(struct kunit *test, + const struct property_entry *properties, + const struct fwnode_handle *parent); +struct fwnode_handle * +kunit_software_node_register(struct kunit *test, + const struct software_node *node); +int kunit_software_node_register_node_group(struct kunit *test, + const struct software_node *const *nodes); +int kunit_device_add_software_node(struct kunit *test, struct device *dev, + const struct software_node *node); + +#endif /* _KUNIT_FWNODE_H */ diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 2e8a6b71a2ab..204e02b10eba 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -11,6 +11,7 @@ kunit-objs += test.o \ attributes.o \ device.o \ platform.o \ + fwnode.o \ bug.o ifeq ($(CONFIG_KUNIT_DEBUGFS),y) diff --git a/lib/kunit/fwnode.c b/lib/kunit/fwnode.c new file mode 100644 index 000000000000..a58ce0984d76 --- /dev/null +++ b/lib/kunit/fwnode.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include +#include + +#include +#include + +KUNIT_DEFINE_ACTION_WRAPPER(fwnode_remove_software_node_wrapper, + fwnode_remove_software_node, + struct fwnode_handle *); + +/** + * kunit_fwnode_create_software_node() - Create a kunit-managed software node + * @test: Test context + * @properties: Properties to use to create the new software node + * @parent: Parent of this software node + * + * Create a test-managed software node and return its firmware node handle. + * The software node is removed after the test case completes. + * + * Returns: + * Firmware node handle of the newly created software node or IS_ERR() on + * failure. + */ +struct fwnode_handle * +kunit_fwnode_create_software_node(struct kunit *test, + const struct property_entry *properties, + const struct fwnode_handle *parent) +{ + struct fwnode_handle *fwnode; + int ret; + + fwnode = fwnode_create_software_node(properties, parent); + if (IS_ERR(fwnode)) + return fwnode; + + ret = kunit_add_action_or_reset(test, fwnode_remove_software_node_wrapper, + fwnode); + if (ret) + return ERR_PTR(ret); + + return fwnode; +} +EXPORT_SYMBOL_GPL(kunit_fwnode_create_software_node); + +KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper, + software_node_unregister, + const struct software_node *); + +/** + * kunit_software_node_register() - Register a kunit-managed software node + * @test: Test context + * @swnode: Software node to register + * + * Register a test-managed software node and return its firmware node handle. + * The software node is unregistered after the test case completes. + * + * Returns: + * Firmware node handle of the registered software node or IS_ERR() on failure. + */ +struct fwnode_handle * +kunit_software_node_register(struct kunit *test, + const struct software_node *swnode) +{ + struct fwnode_handle *fwnode; + int ret; + + ret = software_node_register(swnode); + if (ret) + return ERR_PTR(ret); + + fwnode = software_node_fwnode(swnode); + if (WARN_ON(!fwnode)) + return ERR_PTR(-ENOENT); + + ret = kunit_add_action_or_reset(test, software_node_unregister_wrapper, + (void *)swnode); + if (ret) + return ERR_PTR(ret); + + return fwnode; +} +EXPORT_SYMBOL_GPL(kunit_software_node_register); + +KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper, + software_node_unregister_node_group, + const struct software_node *const *); + +/** + * kunit_software_node_register_node_group() - Register a kunit-managed software node group + * @test: Test context + * @nodes: Software node group to register + * + * Register a test-managed software node group. The nodes are unregistered + * after the test case completes. + * + * Returns: + * 0 on success, negative error number on failure. + */ +int kunit_software_node_register_node_group(struct kunit *test, + const struct software_node *const *nodes) +{ + int ret; + + ret = software_node_register_node_group(nodes); + if (ret) + return ret; + + return kunit_add_action_or_reset(test, software_node_unregister_node_group_wrapper, + (void *)nodes); +} +EXPORT_SYMBOL_GPL(kunit_software_node_register_node_group); + +KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper, + device_remove_software_node, + struct device *); + +/** + * kunit_device_add_software_node() - Assign a kunit-managed software node to a device + * @test: Test context + * @dev: Device to assign the software node for + * @node: The software node to assign + * + * Make @node the secondary firmware node of @dev. If @dev has no primary + * firmware node, @node will become the primary node. The software node will + * be automatically removed from @dev when the test case completes. + * + * Returns: + * 0 on success, negative error number on failure. + */ +int kunit_device_add_software_node(struct kunit *test, struct device *dev, + const struct software_node *node) +{ + int ret; + + ret = device_add_software_node(dev, node); + if (ret) + return ret; + + return kunit_add_action_or_reset(test, device_remove_software_node_wrapper, dev); +} +EXPORT_SYMBOL_GPL(kunit_device_add_software_node); From 37e586675993a165ccc0ae84505f4ee5d0551d08 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Jul 2026 13:14:45 +0200 Subject: [PATCH 30/61] software node: add fw_devlink support Software nodes can be used to describe supplier-consumer relationships between devices they represent using reference property entries. Unlike for OF-nodes, driver core cannot yet use these references to create a probe order that avoids needless probe deferrals on missing providers. Implement software_node_add_links() modelled on of_fwnode_add_links(). For every DEV_PROP_REF property we resolve each referenced supplier and create an fwnode link from the node to it. The driver core later promotes these to device links and defers the consumer until the suppliers are ready. There's no allowlist like the one DT needs - devicetree phandles appear in plenty of non-supplier contexts, but a software node only carries a reference property when its author explicitly points at another node, so we treat every reference as an intentional supplier dependency and link all of them. Graph "remote-endpoint" references are skipped for now: they go 2-ways between endpoint nodes and would create graph cycles without the port-parent lifting DT does via get_con_dev(). References to suppliers that aren't registered yet and self-references are ignored. fw_devlink resolves the supplier device through fwnode->dev but the core only records the owning device on the primary fwnode. When the software node is a device's secondary fwnode, mirror the device pointer onto it in software_node_notify() so the consumer can actually find the supplier instead of deferring forever. While at it: purge the fwnode links in software_node_release() now that software nodes can own them. Acked-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260713-swnode-fw-devlink-v4-2-d4f2dee27ad9@oss.qualcomm.com Signed-off-by: Danilo Krummrich --- drivers/base/swnode.c | 79 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 869228a65cb3..1f2315858cc3 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -699,6 +699,62 @@ software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode, return 0; } +static int software_node_add_links(struct fwnode_handle *fwnode) +{ + const struct software_node_ref_args *ref, *ref_array; + struct swnode *swnode = to_swnode(fwnode); + const struct property_entry *prop; + struct fwnode_handle *refnode; + unsigned int count; + + if (!swnode || !swnode->node->properties) + return 0; + + /* + * Unlike Device Tree, where phandles appear in many non-supplier + * contexts and a curated allowlist is required, a software node only + * carries a DEV_PROP_REF property when the author explicitly describes + * a reference to another node. Every such reference is therefore an + * intentional supplier dependency, so we create fwnode links for all + * of them. + */ + for (prop = swnode->node->properties; prop->name; prop++) { + if (prop->type != DEV_PROP_REF || prop->is_inline) + continue; + + /* + * TODO: Graph "remote-endpoint" references go both ways + * between endpoint child nodes and would create endpoint + * cycles. Let's leave it out for now until we have potential + * users. + */ + if (!strcmp(prop->name, "remote-endpoint")) + continue; + + ref_array = prop->pointer; + count = prop->length / sizeof(*ref_array); + + for (unsigned int i = 0; i < count; i++) { + ref = &ref_array[i]; + + if (ref->swnode) + refnode = software_node_fwnode(ref->swnode); + else if (ref->fwnode) + refnode = ref->fwnode; + else + continue; + + /* Supplier not registered yet, or self-reference. */ + if (!refnode || refnode == &swnode->fwnode) + continue; + + fwnode_link_add(&swnode->fwnode, refnode, 0); + } + } + + return 0; +} + static const struct fwnode_operations software_node_ops = { .get = software_node_get, .put = software_node_put, @@ -716,6 +772,7 @@ static const struct fwnode_operations software_node_ops = { .graph_get_remote_endpoint = software_node_graph_get_remote_endpoint, .graph_get_port_parent = software_node_graph_get_port_parent, .graph_parse_endpoint = software_node_graph_parse_endpoint, + .add_links = software_node_add_links, }; /* -------------------------------------------------------------------------- */ @@ -787,6 +844,8 @@ static void software_node_release(struct kobject *kobj) { struct swnode *swnode = kobj_to_swnode(kobj); + fwnode_links_purge(&swnode->fwnode); + if (swnode->parent) { ida_free(&swnode->parent->child_ids, swnode->id); list_del(&swnode->entry); @@ -1105,6 +1164,17 @@ void software_node_notify(struct device *dev) if (!swnode) return; + /* + * When the software node is the device's secondary firmware node, + * the core only records the owning device on the primary fwnode + * (see device_add()). fw_devlink resolves a supplier device through + * fwnode->dev, so without this a consumer referencing the software + * node could never find the supplier device and would defer forever. + * Make fwnode.dev point to its owner in that case. + */ + if (!device_match_fwnode(dev, &swnode->fwnode) && !swnode->fwnode.dev) + swnode->fwnode.dev = dev; + swnode_get(swnode); ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node"); if (ret) @@ -1127,6 +1197,15 @@ void software_node_notify_remove(struct device *dev) sysfs_remove_link(&swnode->kobj, dev_name(dev)); sysfs_remove_link(&dev->kobj, "software_node"); + + /* + * Drop the device pointer mirrored onto a secondary software node in + * software_node_notify(). For a primary software node the core owns + * fwnode->dev and clears it in device_del(). + */ + if (!device_match_fwnode(dev, &swnode->fwnode) && swnode->fwnode.dev == dev) + swnode->fwnode.dev = NULL; + swnode_put(swnode); if (swnode->managed) { From eecba5978729f4a7a151f25d486fc7274fce7b8f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Jul 2026 13:14:46 +0200 Subject: [PATCH 31/61] software node: add kunit tests for fw_devlink support Add a kunit test suite for fw_devlink support for software nodes. Most cases call add_links() directly and inspect the resulting fwnode supplier/consumer lists: a single reference, multiple references, a reference to an unregistered node, a "remote-endpoint" reference and a reference array. The last case is end-to-end - it registers real consumer and supplier platform devices together with their drivers, adds the consumer first and checks that fw_devlink defers its probe until the supplier has been bound. Acked-by: Andy Shevchenko Tested-by: David Gow Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260713-swnode-fw-devlink-v4-3-d4f2dee27ad9@oss.qualcomm.com Signed-off-by: Danilo Krummrich --- MAINTAINERS | 1 + drivers/base/test/Kconfig | 5 + drivers/base/test/Makefile | 2 + drivers/base/test/swnode-devlink-test.c | 337 ++++++++++++++++++++++++ 4 files changed, 345 insertions(+) create mode 100644 drivers/base/test/swnode-devlink-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..ca605906a5f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25105,6 +25105,7 @@ L: linux-acpi@vger.kernel.org S: Maintained F: drivers/base/property.c F: drivers/base/swnode.c +F: drivers/base/test/swnode-devlink-test.c F: include/linux/fwnode.h F: include/linux/property.h diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 2756870615cc..1ecf0791241a 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -18,3 +18,8 @@ config DRIVER_PE_KUNIT_TEST tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS depends on KUNIT default KUNIT_ALL_TESTS + +config DRIVER_SWNODE_KUNIT_TEST + tristate "KUnit Tests for software node fw_devlink links" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index e321dfc7e922..9ced7bbd569f 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN) + +obj-$(CONFIG_DRIVER_SWNODE_KUNIT_TEST) += swnode-devlink-test.o diff --git a/drivers/base/test/swnode-devlink-test.c b/drivers/base/test/swnode-devlink-test.c new file mode 100644 index 000000000000..f53549e92611 --- /dev/null +++ b/drivers/base/test/swnode-devlink-test.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int swnode_count_suppliers(struct fwnode_handle *fwnode) +{ + struct fwnode_link *link; + unsigned int count = 0; + + /* + * The suppliers and consumers lists should typically only be accessed + * with the fwnode_link_lock taken but it's private to the driver core. + * + * These are tests and at this point nobody should be modifying them so + * let's just access the list. + */ + list_for_each_entry(link, &fwnode->suppliers, c_hook) + count++; + + return count; +} + +/* True if a supplier link con->sup exists, checked from both list ends. */ +static bool swnode_has_link(struct fwnode_handle *consumer, + struct fwnode_handle *supplier) +{ + bool from_con = false, from_sup = false; + struct fwnode_link *link; + + list_for_each_entry(link, &consumer->suppliers, c_hook) { + if (link->supplier == supplier && link->consumer == consumer) + from_con = true; + } + + list_for_each_entry(link, &supplier->consumers, s_hook) { + if (link->supplier == supplier && link->consumer == consumer) + from_sup = true; + } + + return from_con && from_sup; +} + +/* A single reference creates exactly one supplier link, on both list ends. */ +static void swnode_devlink_test_single_ref(struct kunit *test) +{ + static const struct software_node supp_swnode = { + .name = "swnode-devlink-test-supplier", + }; + + struct fwnode_handle *cons_fwnode, *supp_fwnode; + int ret; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("supplier", &supp_swnode), + { } + }; + + supp_fwnode = kunit_software_node_register(test, &supp_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode); + + cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode); + + ret = fwnode_call_int_op(cons_fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 1); + KUNIT_EXPECT_TRUE(test, swnode_has_link(cons_fwnode, supp_fwnode)); +} + +/* Multiple distinct references create multiple supplier links. */ +static void swnode_devlink_test_multiple_refs(struct kunit *test) +{ + static const struct software_node supp1_swnode = { + .name = "swnode-devlink-test-supplier-1", + }; + static const struct software_node supp2_swnode = { + .name = "swnode-devlink-test-supplier-2", + }; + static const struct software_node *supp_nodes[] = { + &supp1_swnode, &supp2_swnode, NULL + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("foo", &supp1_swnode), + PROPERTY_ENTRY_REF("bar", &supp2_swnode), + { } + }; + + struct fwnode_handle *fwnode; + int ret; + + ret = kunit_software_node_register_node_group(test, supp_nodes); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = fwnode_call_int_op(fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode))); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode))); +} + +/* A reference to an unregistered node creates no link (graceful skip). */ +static void swnode_devlink_test_unregistered_ref(struct kunit *test) +{ + static const struct software_node supp_swnode = { + .name = "swnode-devlink-test-supplier", + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("supplier", &supp_swnode), + { } + }; + + struct fwnode_handle *fwnode; + int ret; + + fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = fwnode_call_int_op(fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 0); +} + +/* Graph "remote-endpoint" references are excluded. */ +static void swnode_devlink_test_remote_endpoint_excluded(struct kunit *test) +{ + static const struct software_node ep_swnode = { + .name = "swnode-devlink-test-end-point" + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("remote-endpoint", &ep_swnode), + { } + }; + + struct fwnode_handle *cons_fwnode, *supp_fwnode; + int ret; + + supp_fwnode = kunit_software_node_register(test, &ep_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode); + + cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode); + + ret = fwnode_call_int_op(cons_fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 0); +} + +/* A reference array creates one link per registered element. */ +static void swnode_devlink_test_ref_array(struct kunit *test) +{ + static const struct software_node supp1_swnode = { + .name = "swnode-devlink-test-supplier-1", + }; + static const struct software_node supp2_swnode = { + .name = "swnode-devlink-test-supplier-2", + }; + static const struct software_node *supp_nodes[] = { + &supp1_swnode, &supp2_swnode, NULL + }; + static const struct software_node_ref_args refs[] = { + SOFTWARE_NODE_REFERENCE(&supp1_swnode), + SOFTWARE_NODE_REFERENCE(&supp2_swnode, 4, 2), + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF_ARRAY("suppliers", refs), + { } + }; + + struct fwnode_handle *fwnode; + int ret; + + ret = kunit_software_node_register_node_group(test, supp_nodes); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = fwnode_call_int_op(fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode))); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode))); +} + +/* + * End-to-end test: fw_devlink must defer a consumer's probe until its + * supplier has probed. + * + * The reference created by software_node_add_links() is only useful if the + * driver core promotes it to a real device_link and uses it to order probing. + * This test drives actual probing through the platform bus and asserts the + * supplier binds before the consumer. + */ + +#define SWNODE_DEVLINK_TEST_SUPPLIER "swnode-link-supplier" +#define SWNODE_DEVLINK_TEST_CONSUMER "swnode-link-consumer" +#define SWNODE_DEVLINK_TEST_TIMEOUT_MS (2 * MSEC_PER_SEC) + +struct swnode_test_probe_order { + /* Names in the order their drivers' .probe ran. */ + const char *probed[2]; + unsigned int count; + wait_queue_head_t wq; +}; + +static int swnode_test_record_probe(struct platform_device *pdev) +{ + struct swnode_test_probe_order *order = platform_get_drvdata(pdev); + + if (order && order->count < ARRAY_SIZE(order->probed)) { + order->probed[order->count++] = dev_name(&pdev->dev); + wake_up_interruptible(&order->wq); + } + + return 0; +} + +static struct platform_driver swnode_test_supplier_driver = { + .probe = swnode_test_record_probe, + .driver = { + .name = SWNODE_DEVLINK_TEST_SUPPLIER, + }, +}; + +static struct platform_driver swnode_test_consumer_driver = { + .probe = swnode_test_record_probe, + .driver = { + .name = SWNODE_DEVLINK_TEST_CONSUMER, + }, +}; + +static void swnode_devlink_test_probe_order(struct kunit *test) +{ + static const struct software_node supplier_swnode = { + .name = "swnode-devlink-test-supplier", + }; + + const struct property_entry consumer_props[] = { + PROPERTY_ENTRY_REF("supplier-ref", &supplier_swnode), + { } + }; + + struct platform_device *supplier, *consumer; + struct swnode_test_probe_order *order; + struct fwnode_handle *fwnode; + int ret; + + order = kunit_kzalloc(test, sizeof(*order), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, order); + init_waitqueue_head(&order->wq); + + fwnode = kunit_software_node_register(test, &supplier_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = kunit_platform_driver_register(test, &swnode_test_supplier_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_platform_driver_register(test, &swnode_test_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + supplier = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_SUPPLIER, + PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier); + consumer = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_CONSUMER, + PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer); + + platform_set_drvdata(supplier, order); + platform_set_drvdata(consumer, order); + + ret = kunit_device_add_software_node(test, &supplier->dev, &supplier_swnode); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = device_create_managed_software_node(&consumer->dev, + consumer_props, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_device_add(test, consumer); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_platform_device_add(test, supplier); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = wait_event_interruptible_timeout(order->wq, + order->count == 2, + msecs_to_jiffies(SWNODE_DEVLINK_TEST_TIMEOUT_MS)); + KUNIT_ASSERT_GT(test, ret, 0); + + KUNIT_EXPECT_STREQ(test, order->probed[0], SWNODE_DEVLINK_TEST_SUPPLIER); + KUNIT_EXPECT_STREQ(test, order->probed[1], SWNODE_DEVLINK_TEST_CONSUMER); + + /* Tear down the consumer (and its device link) before the supplier. */ + kunit_platform_device_unregister(test, consumer); +} + +static struct kunit_case swnode_test_cases[] = { + KUNIT_CASE(swnode_devlink_test_single_ref), + KUNIT_CASE(swnode_devlink_test_multiple_refs), + KUNIT_CASE(swnode_devlink_test_unregistered_ref), + KUNIT_CASE(swnode_devlink_test_remote_endpoint_excluded), + KUNIT_CASE(swnode_devlink_test_ref_array), + KUNIT_CASE(swnode_devlink_test_probe_order), + { } +}; + +static struct kunit_suite swnode_test_suite = { + .name = "software-node-links", + .test_cases = swnode_test_cases, +}; + +kunit_test_suite(swnode_test_suite); + +MODULE_DESCRIPTION("Test module for software node fw_devlink support"); +MODULE_AUTHOR("Bartosz Golaszewski "); +MODULE_LICENSE("GPL"); From 1226304062453754dd8fe7ec3f2656d600e9efe0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Jul 2026 13:14:47 +0200 Subject: [PATCH 32/61] MAINTAINERS: add myself as reviewer of software node support I've been working extensively on software nodes lately and introduced some changes. Add myself as reviewer so that I can help review any new proposed changes. Acked-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260713-swnode-fw-devlink-v4-4-d4f2dee27ad9@oss.qualcomm.com Signed-off-by: Danilo Krummrich --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ca605906a5f7..e30aa3b63eff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25101,6 +25101,7 @@ R: Andy Shevchenko R: Daniel Scally R: Heikki Krogerus R: Sakari Ailus +R: Bartosz Golaszewski L: linux-acpi@vger.kernel.org S: Maintained F: drivers/base/property.c From bbd8254da11c189072349003006091e8327e8d36 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 13 Jul 2026 16:28:10 -0700 Subject: [PATCH 33/61] gpio: bt8xx: use devm_ioremap_resource() Replace the open-coded devm_request_mem_region() plus devm_ioremap() sequence with a single devm_ioremap_resource() call on the BAR0 resource. This reserves the region and maps it in one step, and maps the full BAR length instead of a hardcoded 0x1000 (BAR0 on the bt848/878 is a 4KB register block, so the mapped size is unchanged). devm_ioremap_resource() returns an ERR_PTR() on failure, so check with IS_ERR() and propagate PTR_ERR(). Drop the now-redundant error message, as devm_ioremap_resource() already logs on every failure path. Since it can return -EPROBE_DEFER, place it early. Built for ARM (defconfig + CONFIG_GPIO_BT8XX) with LLVM=1; drivers/gpio/gpio-bt8xx.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260713232810.1144712-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bt8xx.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 324eeb77dbd5..f3a2321bc388 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -154,12 +154,18 @@ static int bt8xxgpio_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { struct bt8xxgpio *bg; + void __iomem *mmio; int err; + mmio = devm_ioremap_resource(&dev->dev, pci_resource_n(dev, 0)); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL); if (!bg) return -ENOMEM; + bg->mmio = mmio; bg->pdev = dev; spin_lock_init(&bg->lock); @@ -168,24 +174,9 @@ static int bt8xxgpio_probe(struct pci_dev *dev, dev_err(&dev->dev, "can't enable device.\n"); return err; } - if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0), - pci_resource_len(dev, 0), - "bt8xxgpio")) { - dev_warn(&dev->dev, "can't request iomem (0x%llx).\n", - (unsigned long long)pci_resource_start(dev, 0)); - err = -EBUSY; - goto err_disable; - } pci_set_master(dev); pci_set_drvdata(dev, bg); - bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000); - if (!bg->mmio) { - dev_err(&dev->dev, "ioremap() failed\n"); - err = -EIO; - goto err_disable; - } - /* Disable interrupts */ bgwrite(0, BT848_INT_MASK); From e8a40b3566887c1c83d4cb87058d4e98ec8b4a14 Mon Sep 17 00:00:00 2001 From: Swark Yang Date: Thu, 16 Jul 2026 20:51:09 -0700 Subject: [PATCH 34/61] dt-bindings: gpio: cdns: add Axiado AX3005 GPIO variant Add binding for Axiado AX3005 GPIO controller. So far, no changes are known, so it can fall back to the cdns,gpio-r1p02 compatible. Signed-off-by: Swark Yang Acked-by: Krzysztof Kozlowski Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260716-upstream-axiado-ax3005-upstream-v3-2-c429095143ec@axiado.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/cdns,gpio.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml b/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml index a84d60b39459..7eacf5af6554 100644 --- a/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml @@ -14,8 +14,11 @@ properties: oneOf: - const: cdns,gpio-r1p02 - items: - - enum: - - axiado,ax3000-gpio + - const: axiado,ax3000-gpio + - const: cdns,gpio-r1p02 + - items: + - const: axiado,ax3005-gpio + - const: axiado,ax3000-gpio - const: cdns,gpio-r1p02 reg: From fbd35c8cc6c6e817385f2bfa07fa4227370a3010 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 15 Jul 2026 18:39:44 +0200 Subject: [PATCH 35/61] gpio: pcf857x: Use le16_to_cpu() and cpu_to_le16() in IO accessors 16-bit expander data is an __le16, use le16_to_cpu() to read it. In the similar way use cpu_to_le16() to write __le16 value. Signed-off-by: Andy Shevchenko Fixes: and Cc: stable? Link: https://patch.msgid.link/20260715163944.1300616-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pcf857x.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 4196916c4f94..c1f5e10a3c20 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -18,6 +18,8 @@ #include #include +#include + static const struct i2c_device_id pcf857x_id[] = { { .name = "pcf8574", .driver_data = 8 }, { .name = "pcf8574a", .driver_data = 8 }, @@ -96,22 +98,23 @@ static int i2c_read_le8(struct i2c_client *client) static int i2c_write_le16(struct i2c_client *client, unsigned int word) { - u8 buf[2] = { word & 0xff, word >> 8, }; + __le16 buf = cpu_to_le16(word); int status; - status = i2c_master_send(client, buf, 2); + status = i2c_master_send(client, (char *)&buf, sizeof(buf)); return (status < 0) ? status : 0; } static int i2c_read_le16(struct i2c_client *client) { - u8 buf[2]; + __le16 buf; int status; - status = i2c_master_recv(client, buf, 2); + status = i2c_master_recv(client, (char *)&buf, sizeof(buf)); if (status < 0) return status; - return (buf[1] << 8) | buf[0]; + + return le16_to_cpu(buf); } /*-------------------------------------------------------------------------*/ From 9672edf75bc7ba3fb5b7d529936548de38b5908e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 20 Jul 2026 10:15:31 +0200 Subject: [PATCH 36/61] gpio: kunit: add test cases verifying swnode devlink support The software node fw_devlink support already has its own kunit suite, but that verifies the fwnode links in isolation. Add GPIO tests that prove the ordering works in a real-life use-case: a GPIO consumer that references its provider via a software node. The first suite registers the provider's software node, adds the consumer device first and checks that fw_devlink defers its probe until the provider has been added and bound. The second covers the fallback: with the provider's software node not yet registered no supplier link is created, so the consumer probes, devm_gpiod_get() returns -EPROBE_DEFER and the consumer only binds once the provider shows up. While at it: the existing gpio_unbind_with_consumers() test keeps the consumer bound while the provider goes away and then operates the orphaned descriptor. With software nodes now being covered by fw_devlink that would instead force-unbind the consumer along with the provider, so opt it out by setting FWNODE_FLAG_LINKS_ADDED. Acked-by: Andy Shevchenko Tested-by: David Gow Link: https://patch.msgid.link/20260720-swnode-fw-devlink-v5-1-ec250ae6af91@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-kunit.c | 291 ++++++++++++++++++++++++++++++++++- 1 file changed, 284 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c index c9c5b4374820..e6cb43a8df5b 100644 --- a/drivers/gpio/gpiolib-kunit.c +++ b/drivers/gpio/gpiolib-kunit.c @@ -3,6 +3,8 @@ * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries */ +#include +#include #include #include #include @@ -11,12 +13,16 @@ #include #include #include +#include +#include #include #include #define GPIO_TEST_PROVIDER "gpio-test-provider" #define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" +#define GPIO_PROBE_ORDER_TEST_CONSUMER "gpio-probe-order-test-consumer" +#define GPIO_PROBE_DEFER_TEST_CONSUMER "gpio-probe-defer-test-consumer" #define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" #define GPIO_CONSUMER_NAME "gpio-swnode-consumer-test-device" @@ -275,6 +281,265 @@ static struct kunit_suite gpio_swnode_lookup_test_suite = { .init = gpio_swnode_register_drivers, }; +static void gpio_swnode_unregister_swnode(void *data) +{ + software_node_unregister(data); +} + +struct gpio_probe_order_pdata { + unsigned int probe_count; + bool gpio_ok; +}; + +static const struct gpio_probe_order_pdata gpio_probe_order_pdata_template = { + .probe_count = 0, + .gpio_ok = false, +}; + +static int gpio_probe_order_consumer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_probe_order_pdata *pdata = dev_get_platdata(dev); + struct gpio_desc *desc; + + pdata->probe_count++; + + desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + pdata->gpio_ok = true; + + return 0; +} + +static struct platform_driver gpio_probe_order_consumer_driver = { + .probe = gpio_probe_order_consumer_probe, + .driver = { + .name = GPIO_PROBE_ORDER_TEST_CONSUMER, + }, +}; + +/* + * Verify that fw_devlink orders the probe of a GPIO consumer after its + * provider. The consumer references the provider through a software node and + * is registered first. fw_devlink must defer it before its driver's probe() + * is ever entered, so the consumer probes exactly once - only after the + * provider is added and bound. + */ +static void gpio_swnode_probe_order(struct kunit *test) +{ + struct property_entry properties[2] = { }; + struct gpio_probe_order_pdata *pdata; + struct platform_device_info pdevinfo; + struct platform_device *prvd, *cons; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_probe_order_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = software_node_register(&gpio_test_provider_swnode); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_add_action_or_reset(test, gpio_swnode_unregister_swnode, + (void *)&gpio_test_provider_swnode); + KUNIT_ASSERT_EQ(test, ret, 0); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_PROBE_ORDER_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_probe_order_pdata_template, + .size_data = sizeof(gpio_probe_order_pdata_template), + .properties = properties, + }; + + cons = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + pdata = dev_get_platdata(&cons->dev); + KUNIT_ASSERT_EQ(test, pdata->probe_count, 0); + KUNIT_ASSERT_FALSE(test, pdata->gpio_ok); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + prvd = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + wait_for_device_probe(); + + scoped_guard(device, &prvd->dev) + bound = device_is_bound(&prvd->dev); + KUNIT_ASSERT_TRUE(test, bound); + + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&cons->dev); + KUNIT_ASSERT_EQ(test, pdata->probe_count, 1); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +struct gpio_probe_defer_pdata { + unsigned int probe_count; + int gpio_err; +}; + +static const struct gpio_probe_defer_pdata gpio_probe_defer_pdata_template = { + .probe_count = 0, + .gpio_err = 0, +}; + +static int gpio_probe_defer_consumer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_probe_defer_pdata *pdata = dev_get_platdata(dev); + struct gpio_desc *desc; + + pdata->probe_count++; + + desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(desc)) { + pdata->gpio_err = PTR_ERR(desc); + return pdata->gpio_err; + } + + pdata->gpio_err = 0; + + return 0; +} + +static struct platform_driver gpio_probe_defer_consumer_driver = { + .probe = gpio_probe_defer_consumer_probe, + .driver = { + .name = GPIO_PROBE_DEFER_TEST_CONSUMER, + }, +}; + +/* + * Verify that a GPIO consumer referencing a provider whose software node is + * not registered yet, defers its probe instead of failing. + * + * The provider software node is deliberately left unregistered when the + * consumer is added. fw_devlink cannot resolve the reference, so it creates no + * supplier link and does not order the consumer - the consumer's probe() runs + * and reaches devm_gpiod_get(). The swnode GPIO lookup returns -ENOTCONN for a + * reference to an unregistered node, which gpiolib maps to -EPROBE_DEFER. Once + * the provider software node and device appear, the deferred consumer probes + * again and binds. + */ +static void gpio_swnode_probe_defer_on_unregistered(struct kunit *test) +{ + struct property_entry properties[2] = { }; + struct gpio_probe_defer_pdata *pdata; + struct platform_device_info pdevinfo; + struct platform_device *prvd, *cons; + struct fwnode_handle *fwnode; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_probe_defer_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_PROBE_DEFER_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_probe_defer_pdata_template, + .size_data = sizeof(gpio_probe_defer_pdata_template), + .properties = properties, + }; + + cons = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + pdata = dev_get_platdata(&cons->dev); + KUNIT_ASSERT_GT(test, pdata->probe_count, 0); + KUNIT_ASSERT_EQ(test, pdata->gpio_err, -EPROBE_DEFER); + + fwnode = kunit_software_node_register(test, &gpio_test_provider_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + prvd = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + wait_for_device_probe(); + + scoped_guard(device, &prvd->dev) + bound = device_is_bound(&prvd->dev); + KUNIT_ASSERT_TRUE(test, bound); + + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&cons->dev); + KUNIT_ASSERT_EQ(test, pdata->gpio_err, 0); + + /* Tear down the consumer before the provider to free the GPIO. */ + kunit_platform_device_unregister(test, cons); +} + +static int gpio_swnode_probe_order_test_init(struct kunit *test) +{ + /* + * A prior test may have left a managed device link teardown queued on + * the device_link_mq. Flush it so that software_node_register() + * doesn't spuriously see the node as registered and fail with -EEXIST. + */ + device_link_wait_removal(); + + return 0; +} + +static struct kunit_case gpio_swnode_probe_order_tests[] = { + KUNIT_CASE(gpio_swnode_probe_order), + KUNIT_CASE(gpio_swnode_probe_defer_on_unregistered), + { } +}; + +static struct kunit_suite gpio_swnode_probe_order_test_suite = { + .name = "gpio-swnode-probe-order", + .test_cases = gpio_swnode_probe_order_tests, + .init = gpio_swnode_probe_order_test_init, +}; + static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier); struct gpio_unbind_consumer_drvdata { @@ -372,15 +637,24 @@ static void gpio_unbind_with_consumers(struct kunit *test) 0, GPIO_ACTIVE_HIGH); properties[1] = (struct property_entry){ }; - pdevinfo = (struct platform_device_info){ - .name = GPIO_UNBIND_TEST_CONSUMER, - .id = PLATFORM_DEVID_NONE, - .properties = properties, - }; - - cons = kunit_platform_device_register_full(test, &pdevinfo); + /* + * This test deliberately keeps the consumer bound while the provider + * is unregistered. fw_devlink would force-unbind the consumer before + * the provider so use the FWNODE_FLAG_LINKS_ADDED flag to opt out of + * it as a workaround. + */ + cons = kunit_platform_device_alloc(test, GPIO_UNBIND_TEST_CONSUMER, + PLATFORM_DEVID_NONE); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + ret = device_create_managed_software_node(&cons->dev, properties, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode_set_flag(dev_fwnode(&cons->dev), FWNODE_FLAG_LINKS_ADDED); + + ret = kunit_platform_device_add(test, cons); + KUNIT_ASSERT_EQ(test, ret, 0); + wait_for_device_probe(); scoped_guard(device, &cons->dev) bound = device_is_bound(&cons->dev); @@ -408,6 +682,8 @@ static struct kunit_case gpio_unbind_with_consumers_tests[] = { static struct kunit_suite gpio_unbind_with_consumers_test_suite = { .name = "gpio-unbind-with-consumers", .test_cases = gpio_unbind_with_consumers_tests, + /* We need this here too to clean any left over links. */ + .init = gpio_swnode_probe_order_test_init, }; /* @@ -593,6 +869,7 @@ static struct kunit_suite gpio_swnode_hog_test_suite = { kunit_test_suites( &gpio_swnode_lookup_test_suite, + &gpio_swnode_probe_order_test_suite, &gpio_unbind_with_consumers_test_suite, &gpio_swnode_hog_test_suite, ); From bde4905c102f19151228c86f82a7fcf50f11b160 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 21 Jul 2025 21:56:49 +0200 Subject: [PATCH 37/61] Input: soc_button_array - select CONFIG_GPIOLIB_LEGACY This driver converts information from ACPI in x86 based tablets and laptops into platform_data for the gpio_keys driver, using the obsolete gpio number based interfaces. This should really be converted to some other method, but since the conversion is nontrivial, have this one select GPIOLIB_LEGACY for the time being. This enables turning GPIOLIB_LEGACY off by default on most kernel builds. Since the driver is only used on x86 portables, add a CONFIG_X86 dependency, which means non-x86 allmodconfig builds usuallly build without the legacy gpio support. Link: https://lore.kernel.org/all/ah-1z9LhVG0wtfBw@google.com/ Acked-by: Bartosz Golaszewski Acked-by: Dmitry Torokhov # for input Signed-off-by: Arnd Bergmann --- drivers/input/misc/Kconfig | 3 +++ drivers/input/misc/soc_button_array.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1f6c57dba030..9c66e3a67127 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -892,6 +892,9 @@ config INPUT_IDEAPAD_SLIDEBAR config INPUT_SOC_BUTTON_ARRAY tristate "Windows-compatible SoC Button Array" depends on KEYBOARD_GPIO && ACPI + depends on X86 + depends on GPIOLIB + select GPIOLIB_LEGACY help Say Y here if you have a SoC-based tablet that originally runs Windows 8 or a Microsoft Surface Book 2, Pro 5, Laptop 1 or later. diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index b8cad415c62c..a6c984205123 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -15,7 +15,6 @@ #include #include #include -#include #include static bool use_low_level_irq; From 0b333ae1a530e304a5677497c849179cacb9ea92 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 21 Jul 2025 21:54:21 +0200 Subject: [PATCH 38/61] Input: gpio-keys: make legacy gpiolib optional Most users of gpio-keys and gpio-keys-polled use modern gpiolib interfaces, but there are still number of ancient sh, arm32 and x86 machines that have never been converted. Add an #ifdef block for the parts of the driver that are only used on those legacy machines. Link: https://lore.kernel.org/all/b3c94552-c104-42e3-be15-7e8362e8039e@gmail.com/ Link: https://lore.kernel.org/all/afJXG4_rtaj3l2Dk@google.com/ Link: https://lore.kernel.org/all/ajQ-CtU131FAJ9ES@google.com/ Acked-by: Bartosz Golaszewski Acked-by: Dmitry Torokhov # for input Signed-off-by: Arnd Bergmann --- drivers/input/keyboard/gpio_keys.c | 7 ++++--- drivers/input/keyboard/gpio_keys_polled.c | 4 +++- include/linux/gpio_keys.h | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index e19617485679..129fc4212a5e 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -23,8 +23,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -528,6 +528,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, */ bdata->gpiod = NULL; } +#ifdef CONFIG_GPIOLIB_LEGACY } else if (gpio_is_valid(button->gpio)) { /* * Legacy GPIO number, so request the GPIO here and @@ -546,6 +547,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, if (button->active_low ^ gpiod_is_active_low(bdata->gpiod)) gpiod_toggle_active_low(bdata->gpiod); +#endif } if (bdata->gpiod) { @@ -583,8 +585,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, if (irq < 0) { error = irq; dev_err_probe(dev, error, - "Unable to get irq number for GPIO %d\n", - button->gpio); + "Unable to get irq number for GPIO\n"); return error; } bdata->irq = irq; diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index e6707d72210e..4e7a366ff05b 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include #include @@ -301,6 +301,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(bdata->gpiod), "failed to get gpio\n"); } +#ifdef CONFIG_GPIOLIB_LEGACY } else if (gpio_is_valid(button->gpio)) { /* * Legacy GPIO number so request the GPIO here and @@ -323,6 +324,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) if (button->active_low ^ gpiod_is_active_low(bdata->gpiod)) gpiod_toggle_active_low(bdata->gpiod); +#endif } bdata->last_state = -1; diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index 80fa930b04c6..e8d6dc290efb 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -25,7 +25,9 @@ struct device; */ struct gpio_keys_button { unsigned int code; +#ifdef CONFIG_GPIOLIB_LEGACY int gpio; +#endif int active_low; const char *desc; unsigned int type; From 9906ce2473f6f1db619208b25ce87273232b00e3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 21 Jul 2025 21:56:49 +0200 Subject: [PATCH 39/61] sh: select legacy gpiolib interface Many board files on sh reference the legacy gpiolib interfaces that are becoming optional. To ensure the boards can keep building, select CONFIG_GPIOLIB_LEGACY on each of the boards that have one of the hardcoded calls. Cc: John Paul Adrian Glaubitz Acked-by: Bartosz Golaszewski Acked-by: Dmitry Torokhov # for input Signed-off-by: Arnd Bergmann --- arch/sh/Kconfig | 1 + arch/sh/boards/Kconfig | 8 ++++++++ arch/sh/boards/mach-highlander/Kconfig | 1 + arch/sh/boards/mach-rsk/Kconfig | 3 +++ 4 files changed, 13 insertions(+) diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index d5795067befa..d60f1d5a94c0 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -462,6 +462,7 @@ config CPU_SUBTYPE_SHX3 select CPU_SHX3 select GENERIC_CLOCKEVENTS_BROADCAST if SMP select GPIOLIB + select GPIOLIB_LEGACY select PINCTRL # SH4AL-DSP Processor Support diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index 1af93be61b1f..d89b74177233 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -80,6 +80,7 @@ config SH_7724_SOLUTION_ENGINE select SOLUTION_ENGINE depends on CPU_SUBTYPE_SH7724 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR imply SND_SOC_AK4642 if SND_SIMPLE_CARD help @@ -199,6 +200,7 @@ config SH_SH7757LCR bool "SH7757LCR" depends on CPU_SUBTYPE_SH7757 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR config SH_SH7785LCR @@ -226,6 +228,7 @@ config SH_URQUELL bool "Urquell" depends on CPU_SUBTYPE_SH7786 select GPIOLIB + select GPIOLIB_LEGACY select HAVE_PCI select NO_IOPORT_MAP if !PCI @@ -233,6 +236,7 @@ config SH_MIGOR bool "Migo-R" depends on CPU_SUBTYPE_SH7722 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR help Select Migo-R if configuring for the SH7722 Migo-R platform @@ -242,6 +246,7 @@ config SH_AP325RXA bool "AP-325RXA" depends on CPU_SUBTYPE_SH7723 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR help Renesas "AP-325RXA" support. @@ -251,6 +256,7 @@ config SH_KFR2R09 bool "KFR2R09" depends on CPU_SUBTYPE_SH7724 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR help "Kit For R2R for 2009" support. @@ -259,6 +265,7 @@ config SH_ECOVEC bool "EcoVec" depends on CPU_SUBTYPE_SH7724 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR imply SND_SOC_DA7210 if SND_SIMPLE_CARD help @@ -329,6 +336,7 @@ config SH_MAGIC_PANEL_R2 bool "Magic Panel R2" depends on CPU_SUBTYPE_SH7720 select GPIOLIB + select GPIOLIB_LEGACY select REGULATOR_FIXED_VOLTAGE if REGULATOR help Select Magic Panel R2 if configuring for Magic Panel R2. diff --git a/arch/sh/boards/mach-highlander/Kconfig b/arch/sh/boards/mach-highlander/Kconfig index b0abd03cac4e..cd3a553ce30c 100644 --- a/arch/sh/boards/mach-highlander/Kconfig +++ b/arch/sh/boards/mach-highlander/Kconfig @@ -20,6 +20,7 @@ config SH_R7785RP bool "R7785RP board support" depends on CPU_SUBTYPE_SH7785 select GPIOLIB + select GPIOLIB_LEGACY endchoice diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig index f0299bc4416f..3810937aa5d4 100644 --- a/arch/sh/boards/mach-rsk/Kconfig +++ b/arch/sh/boards/mach-rsk/Kconfig @@ -12,16 +12,19 @@ config SH_RSK7201 config SH_RSK7203 bool "RSK7203" select GPIOLIB + select GPIOLIB_LEGACY depends on CPU_SUBTYPE_SH7203 config SH_RSK7264 bool "RSK2+SH7264" select GPIOLIB + select GPIOLIB_LEGACY depends on CPU_SUBTYPE_SH7264 config SH_RSK7269 bool "RSK2+SH7269" select GPIOLIB + select GPIOLIB_LEGACY depends on CPU_SUBTYPE_SH7269 endchoice From 6b5edc71aac6ff45f555172b9cdae10c4294707b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 24 Jul 2025 15:41:21 +0200 Subject: [PATCH 40/61] x86/olpc: select GPIOLIB_LEGACY The OLPC GPIO controller sets up a fixed number space that is used by at least two drivers: arch/x86/platform/olpc/olpc-xo1-sci.c: In function 'setup_ec_sci': arch/x86/platform/olpc/olpc-xo1-sci.c:358:13: error: implicit declaration of function 'gpio_request' [-Wimplicit-function-declaration] 358 | r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI"); | ^~~~~~~~~~~~ sound/pci/cs5535audio/cs5535audio_olpc.c: In function 'olpc_analog_input': sound/pci/cs5535audio/cs5535audio_olpc.c:41:9: error: implicit declaration of function 'gpio_set_value'; did you mean 'gpiod_set_value'? [-Wimplicit-function-declaration] 41 | gpio_set_value(OLPC_GPIO_MIC_AC, on); The AMD Geode platform that this is based on is now marked as 'Orphaned' in Kconfig, and it is likely that there are no XO1 users on modern kernels, but so far there is no consensus on removing it entirely. Select CONFIG_GPIOLIB_LEGACY for this platform and make sure the sound driver portion cannot be compiled without this. Acked-by: Borislav Petkov (AMD) Acked-by: Takashi Iwai Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Dmitry Torokhov # for input Signed-off-by: Arnd Bergmann --- arch/x86/Kconfig | 1 + arch/x86/platform/olpc/olpc-xo1-sci.c | 2 +- sound/pci/Kconfig | 1 + sound/pci/cs5535audio/cs5535audio_olpc.c | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index bdad90f210e4..4ad8a7bbd93d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2976,6 +2976,7 @@ config OLPC bool "One Laptop Per Child support" depends on !X86_PAE select GPIOLIB + select GPIOLIB_LEGACY select OF select OF_PROMTREE select IRQ_DOMAIN diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c index 30751b42d54e..a5b47960ba32 100644 --- a/arch/x86/platform/olpc/olpc-xo1-sci.c +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index e0996a9d90b0..6366f72b3667 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -300,6 +300,7 @@ config SND_CS5535AUDIO tristate "CS5535/CS5536 Audio" depends on X86_32 || MIPS || COMPILE_TEST depends on HAS_IOPORT + depends on GPIOLIB_LEGACY || !OLPC select SND_PCM select SND_AC97_CODEC help diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c index 122170a410d9..cfdcc5bf4341 100644 --- a/sound/pci/cs5535audio/cs5535audio_olpc.c +++ b/sound/pci/cs5535audio/cs5535audio_olpc.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include "cs5535audio.h" From 8fd460518c48056f1f5df32bf0bd3b4550262d6f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jun 2026 11:22:13 +0200 Subject: [PATCH 41/61] mips: replace linux/gpio.h inclusions linux/gpio.h should no longer be used, convert these instead to either linux/gpio/consumer.h or linux/gpio/legacy.h as needed. Acked-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann --- arch/mips/alchemy/board-xxs1500.c | 2 +- arch/mips/alchemy/devboards/db1000.c | 2 +- arch/mips/alchemy/devboards/db1200.c | 2 +- arch/mips/alchemy/devboards/db1550.c | 2 +- arch/mips/bcm47xx/workarounds.c | 2 +- arch/mips/bcm63xx/boards/board_bcm963xx.c | 1 + arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h | 2 +- 7 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/mips/alchemy/board-xxs1500.c b/arch/mips/alchemy/board-xxs1500.c index f175bce2987f..a03762dde4e7 100644 --- a/arch/mips/alchemy/board-xxs1500.c +++ b/arch/mips/alchemy/board-xxs1500.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c index 8fb24b220e3a..5eff34a8683b 100644 --- a/arch/mips/alchemy/devboards/db1000.c +++ b/arch/mips/alchemy/devboards/db1000.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c index de2a9083ed9a..539b311fc8af 100644 --- a/arch/mips/alchemy/devboards/db1200.c +++ b/arch/mips/alchemy/devboards/db1200.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c index b8295a5c2e9a..579fc8f1eaed 100644 --- a/arch/mips/alchemy/devboards/db1550.c +++ b/arch/mips/alchemy/devboards/db1550.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/mips/bcm47xx/workarounds.c b/arch/mips/bcm47xx/workarounds.c index 745c6228eb2c..dc9e5483347d 100644 --- a/arch/mips/bcm47xx/workarounds.c +++ b/arch/mips/bcm47xx/workarounds.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "bcm47xx_private.h" -#include +#include #include #include diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c index c5617b889b1c..71628dac6c26 100644 --- a/arch/mips/bcm63xx/boards/board_bcm963xx.c +++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include diff --git a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h index 830f53f28e3f..428cf4508f9a 100644 --- a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h +++ b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h @@ -3,7 +3,7 @@ #define BOARD_BCM963XX_H_ #include -#include +#include #include #include #include From 752ce1fecd55b507329689c928703e3591bc9c2f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jun 2026 11:22:13 +0200 Subject: [PATCH 42/61] sh: replace linux/gpio.h inclusions linux/gpio.h should no longer be used, convert these instead to linux/gpio/legacy.h for the sh boards using the legacy interfaces, or remove it where it is not needed at all. Acked-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann --- arch/sh/boards/board-magicpanelr2.c | 2 +- arch/sh/boards/board-sh7757lcr.c | 2 +- arch/sh/boards/board-urquell.c | 2 +- arch/sh/boards/mach-ap325rxa/setup.c | 2 +- arch/sh/boards/mach-ecovec24/setup.c | 2 +- arch/sh/boards/mach-highlander/pinmux-r7785rp.c | 2 +- arch/sh/boards/mach-kfr2r09/lcd_wqvga.c | 2 +- arch/sh/boards/mach-kfr2r09/setup.c | 2 +- arch/sh/boards/mach-migor/lcd_qvga.c | 2 +- arch/sh/boards/mach-migor/setup.c | 2 +- arch/sh/boards/mach-rsk/devices-rsk7269.c | 1 - arch/sh/boards/mach-se/7724/setup.c | 2 +- arch/sh/include/mach-common/mach/magicpanelr2.h | 2 -- arch/sh/kernel/cpu/sh4a/setup-shx3.c | 2 +- 14 files changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/sh/boards/board-magicpanelr2.c b/arch/sh/boards/board-magicpanelr2.c index 75de893152af..3f14118a1015 100644 --- a/arch/sh/boards/board-magicpanelr2.c +++ b/arch/sh/boards/board-magicpanelr2.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index bca54e489e11..6d18f59ef261 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/sh/boards/board-urquell.c b/arch/sh/boards/board-urquell.c index dad2b3b40735..1f73c04d341f 100644 --- a/arch/sh/boards/board-urquell.c +++ b/arch/sh/boards/board-urquell.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index bb5004a8ac02..17c3f568d92e 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index a641e26f8fdf..ca4b4dd1ddef 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/sh/boards/mach-highlander/pinmux-r7785rp.c b/arch/sh/boards/mach-highlander/pinmux-r7785rp.c index 689bd8732d9e..3a1057ee9ace 100644 --- a/arch/sh/boards/mach-highlander/pinmux-r7785rp.c +++ b/arch/sh/boards/mach-highlander/pinmux-r7785rp.c @@ -3,7 +3,7 @@ * Copyright (C) 2008 Paul Mundt */ #include -#include +#include #include #include diff --git a/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c b/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c index f6bbac106d13..68716660de34 100644 --- a/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c +++ b/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include