From 4c870bbaba1b011375b1f551c205a8538bf77230 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 20 May 2026 22:13:17 -0700 Subject: [PATCH 1/5] pinctrl: renesas: gpio: isolate function gpiochip from parent fwnode The sh-pfc driver registers two separate gpiochip instances: one for real GPIOs and another for function GPIOs. Since both share the same parent platform device, gpiolib's fallback logic causes both chips to share the same firmware node (fwnode). This causes ambiguity when using software nodes to describe GPIOs, as gpiolib may apply hogs meant for one chip to the other if they share the same node. Explicitly set gc->fwnode to ERR_PTR(-ENODEV) for the function GPIO chip. This satisfies gpiolib's check for an existing fwnode and prevents it from falling back to the parent device's node, while ensuring that no actual properties or hogs are found on the function chip unless explicitly assigned later. Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Acked-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/renesas/gpio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pinctrl/renesas/gpio.c b/drivers/pinctrl/renesas/gpio.c index 2293af642849..4e59dadb7364 100644 --- a/drivers/pinctrl/renesas/gpio.c +++ b/drivers/pinctrl/renesas/gpio.c @@ -278,6 +278,12 @@ static int gpio_function_setup(struct sh_pfc_chip *chip) gc->request = gpio_function_request; + /* + * Explicitly mask the parent's fwnode to prevent gpiolib from + * reusing it for function GPIOs. + */ + gc->fwnode = ERR_PTR(-ENODEV); + gc->label = pfc->info->name; gc->owner = THIS_MODULE; gc->base = pfc->nr_gpio_pins; From 62067bc0a083eb45b9f588745b3846cf366e2d35 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 20 May 2026 22:13:18 -0700 Subject: [PATCH 2/5] sh: pfc: attach software node to the GPIO chip With commit e5d527be7e69 ("gpio: swnode: don't use the swnode's name as the key for GPIO lookup") gpiolib requires that firmware nodes from the GPIO references to match firmware node in gpiochip structure. Define a software node for the pfc gpiochip so that it can be referenced by boards using static device properties. Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- arch/sh/include/cpu-common/cpu/pfc.h | 3 +++ arch/sh/kernel/cpu/pfc.c | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/arch/sh/include/cpu-common/cpu/pfc.h b/arch/sh/include/cpu-common/cpu/pfc.h index 879d2c9da537..d57e38c05bdb 100644 --- a/arch/sh/include/cpu-common/cpu/pfc.h +++ b/arch/sh/include/cpu-common/cpu/pfc.h @@ -11,6 +11,9 @@ #include struct resource; +struct software_node; + +extern const struct software_node pfc_gpiochip_node; int sh_pfc_register(const char *name, struct resource *resource, u32 num_resources); diff --git a/arch/sh/kernel/cpu/pfc.c b/arch/sh/kernel/cpu/pfc.c index 062056ede88d..5a8d804d607b 100644 --- a/arch/sh/kernel/cpu/pfc.c +++ b/arch/sh/kernel/cpu/pfc.c @@ -5,21 +5,29 @@ * Copyright (C) 2012 Renesas Solutions Corp. */ +#include #include #include +#include #include -static struct platform_device sh_pfc_device = { - .id = -1, +const struct software_node pfc_gpiochip_node = { + .name = "sh-pfc", }; int __init sh_pfc_register(const char *name, struct resource *resource, u32 num_resources) { - sh_pfc_device.name = name; - sh_pfc_device.num_resources = num_resources; - sh_pfc_device.resource = resource; + struct platform_device_info pdev_info = { + .name = name, + .id = PLATFORM_DEVID_NONE, + .res = resource, + .num_res = num_resources, + .swnode = &pfc_gpiochip_node, + }; + struct platform_device *pdev; - return platform_device_register(&sh_pfc_device); + pdev = platform_device_register_full(&pdev_info); + return PTR_ERR_OR_ZERO(pdev); } From 6905cdac0e51b067f0060b052724d696b4087f8a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 20 May 2026 22:13:19 -0700 Subject: [PATCH 3/5] sh: mach-rsk: rsk7203: use static device properties for LEDs and GPIO buttons Convert the board to use static device properties instead of platform data to describe LEDs and GPIO-connected buttons on the board, so that support for platform data can be removed from gpio-keys and other drivers, unifying their behavior. Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- arch/sh/boards/mach-rsk/devices-rsk7203.c | 221 ++++++++++++++-------- 1 file changed, 142 insertions(+), 79 deletions(-) diff --git a/arch/sh/boards/mach-rsk/devices-rsk7203.c b/arch/sh/boards/mach-rsk/devices-rsk7203.c index e6b05d4588b7..f8760a91e2f1 100644 --- a/arch/sh/boards/mach-rsk/devices-rsk7203.c +++ b/arch/sh/boards/mach-rsk/devices-rsk7203.c @@ -4,17 +4,19 @@ * * Copyright (C) 2008 - 2010 Paul Mundt */ +#include #include #include #include #include #include #include +#include #include -#include -#include +#include +#include #include -#include +#include #include static struct smsc911x_platform_config smsc911x_config = { @@ -37,92 +39,138 @@ static struct resource smsc911x_resources[] = { }, }; -static struct platform_device smsc911x_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(smsc911x_resources), - .resource = smsc911x_resources, - .dev = { - .platform_data = &smsc911x_config, +static const struct software_node rsk7203_gpio_leds_node = { + .name = "rsk7203-gpio-leds", +}; + +static const struct software_node rsk7203_green_led_node = { + .name = "green", + .parent = &rsk7203_gpio_leds_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_STRING("label", "green"), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PE10, GPIO_ACTIVE_LOW), + { } }, }; -static struct gpio_led rsk7203_gpio_leds[] = { +static const struct software_node rsk7203_orange_led_node = { + .name = "orange", + .parent = &rsk7203_gpio_leds_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_STRING("label", "orange"), + PROPERTY_ENTRY_STRING("linux,default-trigger", "nand-disk"), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PE12, GPIO_ACTIVE_LOW), + { } + }, +}; + +static const struct software_node rsk7203_red1_led_node = { + .name = "red:timer", + .parent = &rsk7203_gpio_leds_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_STRING("label", "red:timer"), + PROPERTY_ENTRY_STRING("linux,default-trigger", "timer"), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PC14, GPIO_ACTIVE_LOW), + { } + }, +}; + +static const struct software_node rsk7203_red2_led_node = { + .name = "red:heartbeat", + .parent = &rsk7203_gpio_leds_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_STRING("label", "red:heartbeat"), + PROPERTY_ENTRY_STRING("linux,default-trigger", "heartbeat"), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PE11, GPIO_ACTIVE_LOW), + { } + }, +}; + +static const struct software_node rsk7203_gpio_keys_node = { + .name = "rsk7203-gpio-keys", + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_U32("poll-interval", 50), + { } + }, +}; + +static const struct software_node rsk7203_sw1_key_node = { + .parent = &rsk7203_gpio_keys_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_U32("linux,code", BTN_0), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PB0, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_STRING("label", "SW1"), + { } + }, +}; + +static const struct software_node rsk7203_sw2_key_node = { + .parent = &rsk7203_gpio_keys_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_U32("linux,code", BTN_1), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PB1, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_STRING("label", "SW2"), + { } + }, +}; + +static const struct software_node rsk7203_sw3_key_node = { + .parent = &rsk7203_gpio_keys_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_U32("linux,code", BTN_2), + PROPERTY_ENTRY_GPIO("gpios", &pfc_gpiochip_node, + GPIO_PB2, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_STRING("label", "SW3"), + { } + }, +}; + +static const struct software_node * const rsk7203_swnodes[] __initconst = { + &rsk7203_gpio_leds_node, + &rsk7203_green_led_node, + &rsk7203_orange_led_node, + &rsk7203_red1_led_node, + &rsk7203_red2_led_node, + &rsk7203_gpio_keys_node, + &rsk7203_sw1_key_node, + &rsk7203_sw2_key_node, + &rsk7203_sw3_key_node, + NULL +}; + +static const struct platform_device_info rsk7203_devices[] __initconst = { { - .name = "green", - .gpio = GPIO_PE10, - .active_low = 1, - }, { - .name = "orange", - .default_trigger = "nand-disk", - .gpio = GPIO_PE12, - .active_low = 1, - }, { - .name = "red:timer", - .default_trigger = "timer", - .gpio = GPIO_PC14, - .active_low = 1, - }, { - .name = "red:heartbeat", - .default_trigger = "heartbeat", - .gpio = GPIO_PE11, - .active_low = 1, + .name = "smsc911x", + .id = PLATFORM_DEVID_NONE, + .res = smsc911x_resources, + .num_res = ARRAY_SIZE(smsc911x_resources), + .data = &smsc911x_config, + .size_data = sizeof(smsc911x_config), }, -}; - -static struct gpio_led_platform_data rsk7203_gpio_leds_info = { - .leds = rsk7203_gpio_leds, - .num_leds = ARRAY_SIZE(rsk7203_gpio_leds), -}; - -static struct platform_device led_device = { - .name = "leds-gpio", - .id = -1, - .dev = { - .platform_data = &rsk7203_gpio_leds_info, - }, -}; - -static struct gpio_keys_button rsk7203_gpio_keys_table[] = { { - .code = BTN_0, - .gpio = GPIO_PB0, - .active_low = 1, - .desc = "SW1", - }, { - .code = BTN_1, - .gpio = GPIO_PB1, - .active_low = 1, - .desc = "SW2", - }, { - .code = BTN_2, - .gpio = GPIO_PB2, - .active_low = 1, - .desc = "SW3", + .name = "leds-gpio", + .id = PLATFORM_DEVID_NONE, + .swnode = &rsk7203_gpio_leds_node, }, -}; - -static struct gpio_keys_platform_data rsk7203_gpio_keys_info = { - .buttons = rsk7203_gpio_keys_table, - .nbuttons = ARRAY_SIZE(rsk7203_gpio_keys_table), - .poll_interval = 50, /* default to 50ms */ -}; - -static struct platform_device keys_device = { - .name = "gpio-keys-polled", - .dev = { - .platform_data = &rsk7203_gpio_keys_info, + { + .name = "gpio-keys-polled", + .id = PLATFORM_DEVID_NONE, + .swnode = &rsk7203_gpio_keys_node, }, }; -static struct platform_device *rsk7203_devices[] __initdata = { - &smsc911x_device, - &led_device, - &keys_device, -}; - static int __init rsk7203_devices_setup(void) { + struct platform_device *pd; + int error; + int i; + /* Select pins for SCIF0 */ gpio_request(GPIO_FN_TXD0, NULL); gpio_request(GPIO_FN_RXD0, NULL); @@ -131,7 +179,22 @@ static int __init rsk7203_devices_setup(void) __raw_writel(0x36db0400, 0xfffc0008); /* CS1BCR */ gpio_request(GPIO_FN_IRQ0_PB, NULL); - return platform_add_devices(rsk7203_devices, - ARRAY_SIZE(rsk7203_devices)); + error = software_node_register_node_group(rsk7203_swnodes); + if (error) { + pr_err("failed to register software nodes: %d\n", error); + return error; + } + + for (i = 0; i < ARRAY_SIZE(rsk7203_devices); i++) { + pd = platform_device_register_full(&rsk7203_devices[i]); + error = PTR_ERR_OR_ZERO(pd); + if (error) { + pr_err("failed to create platform device %s: %d\n", + rsk7203_devices[i].name, error); + return error; + } + } + + return 0; } device_initcall(rsk7203_devices_setup); From c5b962b6023d3299cc9988e32288d5f2aeb206f1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 20 May 2026 22:13:20 -0700 Subject: [PATCH 4/5] pinctrl: renesas: gpio: support software nodes for function GPIOs This patch extends the sh-pfc GPIO driver to support software-node-based configuration for the secondary 'function' GPIO chip. While the primary GPIO chip typically uses the firmware node attached to the parent platform device, the secondary chip should target a specific child node to avoid ambiguity when defining GPIO hogs or properties. Update gpio_function_setup() to look for a child node named 'functions', but only when the parent is a software node. This ensures the behavior is restricted to legacy platforms being migrated to software nodes. Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/renesas/gpio.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/renesas/gpio.c b/drivers/pinctrl/renesas/gpio.c index 4e59dadb7364..b49a3e14da91 100644 --- a/drivers/pinctrl/renesas/gpio.c +++ b/drivers/pinctrl/renesas/gpio.c @@ -271,18 +271,40 @@ static int gpio_function_request(struct gpio_chip *gc, unsigned offset) return ret; } +static void sh_pfc_fwnode_put(void *data) +{ + fwnode_handle_put(data); +} + static int gpio_function_setup(struct sh_pfc_chip *chip) { struct sh_pfc *pfc = chip->pfc; struct gpio_chip *gc = &chip->gpio_chip; + struct fwnode_handle *fwnode = dev_fwnode(pfc->dev); gc->request = gpio_function_request; + if (is_software_node(fwnode)) { + fwnode = fwnode_get_named_child_node(fwnode, "functions"); + if (fwnode) { + int ret; + + ret = devm_add_action_or_reset(pfc->dev, + sh_pfc_fwnode_put, + fwnode); + if (ret) + return ret; + + gc->fwnode = fwnode; + } + } + /* - * Explicitly mask the parent's fwnode to prevent gpiolib from - * reusing it for function GPIOs. + * If we did not find 'functions' node, explicitly mask the parent's + * fwnode to prevent gpiolib from reusing it for function GPIOs. */ - gc->fwnode = ERR_PTR(-ENODEV); + if (!gc->fwnode) + gc->fwnode = ERR_PTR(-ENODEV); gc->label = pfc->info->name; gc->owner = THIS_MODULE; From 106dc49414c47497e9678b1a0a46e161ffeacb5b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 20 May 2026 22:13:21 -0700 Subject: [PATCH 5/5] sh: mach-rsk: rsk7203: convert pin configuration to using software nodes Replace legacy gpio_request() calls used to configure function pins (SCIF0 TXD/RXD and LAN9118 IRQ) with software nodes describing GPIO hogs. These hogs are attached to the PFC gpiochip node, allowing the GPIO subsystem to automatically configure these pins when the driver is registered. Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- arch/sh/boards/mach-rsk/devices-rsk7203.c | 97 ++++++++++++++++++++--- 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/arch/sh/boards/mach-rsk/devices-rsk7203.c b/arch/sh/boards/mach-rsk/devices-rsk7203.c index f8760a91e2f1..e8a8fc1d2ca9 100644 --- a/arch/sh/boards/mach-rsk/devices-rsk7203.c +++ b/arch/sh/boards/mach-rsk/devices-rsk7203.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -131,6 +130,56 @@ static const struct software_node rsk7203_sw3_key_node = { }, }; +/* The base of the function GPIOs in the flat enum */ +#define SH7203_FN_BASE GPIO_FN_PINT7_PB + +static const struct software_node rsk7203_pfc_functions_node = { + .name = "functions", + .parent = &pfc_gpiochip_node, +}; + +static const struct software_node rsk7203_txd0_hog_node = { + .name = "txd0-hog", + .parent = &rsk7203_pfc_functions_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_BOOL("gpio-hog"), + PROPERTY_ENTRY_U32_ARRAY("gpios", ((u32[]){ + GPIO_FN_TXD0 - SH7203_FN_BASE, GPIO_ACTIVE_HIGH + })), + PROPERTY_ENTRY_BOOL("input"), + PROPERTY_ENTRY_STRING("line-name", "TXD0"), + { } + }, +}; + +static const struct software_node rsk7203_rxd0_hog_node = { + .name = "rxd0-hog", + .parent = &rsk7203_pfc_functions_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_BOOL("gpio-hog"), + PROPERTY_ENTRY_U32_ARRAY("gpios", ((u32[]){ + GPIO_FN_RXD0 - SH7203_FN_BASE, GPIO_ACTIVE_HIGH + })), + PROPERTY_ENTRY_BOOL("input"), + PROPERTY_ENTRY_STRING("line-name", "RXD0"), + { } + }, +}; + +static const struct software_node rsk7203_irq0_hog_node = { + .name = "irq0-hog", + .parent = &rsk7203_pfc_functions_node, + .properties = (const struct property_entry[]) { + PROPERTY_ENTRY_BOOL("gpio-hog"), + PROPERTY_ENTRY_U32_ARRAY("gpios", ((u32[]){ + GPIO_FN_IRQ0_PB - SH7203_FN_BASE, GPIO_ACTIVE_HIGH + })), + PROPERTY_ENTRY_BOOL("input"), + PROPERTY_ENTRY_STRING("line-name", "IRQ0_PB"), + { } + }, +}; + static const struct software_node * const rsk7203_swnodes[] __initconst = { &rsk7203_gpio_leds_node, &rsk7203_green_led_node, @@ -141,6 +190,10 @@ static const struct software_node * const rsk7203_swnodes[] __initconst = { &rsk7203_sw1_key_node, &rsk7203_sw2_key_node, &rsk7203_sw3_key_node, + &rsk7203_pfc_functions_node, + &rsk7203_txd0_hog_node, + &rsk7203_rxd0_hog_node, + &rsk7203_irq0_hog_node, NULL }; @@ -165,25 +218,45 @@ static const struct platform_device_info rsk7203_devices[] __initconst = { }, }; +/* + * The pfc-sh7203 device is registered at arch_initcall level, and the + * sh-pfc driver (registered at postcore_initcall level) probes as soon + * as the device is created. + * + * We need to register our software nodes at postcore_initcall level so + * they are already present in the system when the driver probes and + * tries to apply GPIO hogs. + */ +static int __init rsk7203_sw_nodes_setup(void) +{ + int error; + + error = software_node_register(&pfc_gpiochip_node); + if (error && error != -EEXIST) { + pr_err("RSK7203: failed to register PFC software node: %d\n", + error); + return error; + } + + error = software_node_register_node_group(rsk7203_swnodes); + if (error) { + pr_err("RSK7203: failed to register board software nodes: %d\n", + error); + return error; + } + + return 0; +} +postcore_initcall(rsk7203_sw_nodes_setup); + static int __init rsk7203_devices_setup(void) { struct platform_device *pd; int error; int i; - /* Select pins for SCIF0 */ - gpio_request(GPIO_FN_TXD0, NULL); - gpio_request(GPIO_FN_RXD0, NULL); - /* Setup LAN9118: CS1 in 16-bit Big Endian Mode, IRQ0 at Port B */ __raw_writel(0x36db0400, 0xfffc0008); /* CS1BCR */ - gpio_request(GPIO_FN_IRQ0_PB, NULL); - - error = software_node_register_node_group(rsk7203_swnodes); - if (error) { - pr_err("failed to register software nodes: %d\n", error); - return error; - } for (i = 0; i < ARRAY_SIZE(rsk7203_devices); i++) { pd = platform_device_register_full(&rsk7203_devices[i]);