Hi Randy, On Mon, Aug 11, 2025 at 05:46:02PM -0700, Randy Dunlap wrote: > Hi, > > On 8/11/25 2:30 PM, Dmitry Torokhov wrote: > > Introduce documentation regarding use of software nodes to describe > > GPIOs on legacy boards that have not been converted to device tree. > > > > Thanks for the additional documentation. Thanks for the review. > > > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> > > --- > > Documentation/driver-api/gpio/board.rst | 64 ++++ > > Documentation/driver-api/gpio/index.rst | 1 + > > .../driver-api/gpio/legacy-boards.rst | 298 ++++++++++++++++++ > > 3 files changed, 363 insertions(+) > > > > diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst > > index 4fd1cbd8296e..0cf64e1f2623 100644 > > --- a/Documentation/driver-api/gpio/board.rst > > +++ b/Documentation/driver-api/gpio/board.rst > > @@ -94,6 +94,70 @@ with the help of _DSD (Device Specific Data), introduced in ACPI 5.1:: > > For more information about the ACPI GPIO bindings see > > Documentation/firmware-guide/acpi/gpio-properties.rst. > > > > +Software Nodes > > +-------------- > > +Software nodes allows to construct an in-memory, device-tree-like structure > > allow { drivers | modules | software | us} > > although "software" seems redundant. I changed it to "... allows board specific code ..." > > > +using ``struct software_node`` and ``struct property_entry``. This structure > > Quoting Jon (for a different struct): > Better to just say "struct list_head", and the automarkup logic should > take care of the rest. > > @Jon: ISTM that we need something in Documentation/doc-guide/sphinx.rst (?) about which > keywords are handled by automarkup logic. AFAIK, they are struct, union, enum, > and typedef (keywords) and function() as indicated by the "()". Unfortunately device properties/software nodes are not yet hooked to the documentations, so automatic markup/cross referencing does not work. I changed this to :c:type:`struct software_node <software_node>`. > > > > > +can then be associated with a platform device, allowing drivers to use the > > +standard device properties API to query configuration, just as they would on an > > +ACPI or device tree systems. > > system. OK. > > > + > > +Software-node-backed GPIOs are described using ``PROPERTY_ENTRY_GPIO()`` > > +macro, which ties a sotfware node representing GPIO controller with consumer > > software Yep, thanks. > > > +device. It allows consumers to use regular gpiolib APIs, such as ``gpiod_get()``, > > +``gpiod_get_optional()``. > > + > > +The software node representing GPIO controller need not be attached to the > > +GPIO controller device. The only requirement that the node must be registered > > requirement is that OK. > > > +and its name much match the GPIO controller's label. > > must OK. > > > + > > +For example, here is how to describe a single GPIO-connected LED. This is an > > +alternative to using platform_data on legacy systems. > > + > > +.. code-block:: c > > + > > + #include <linux/property.h> > > + #include <linux/gpio/machine.h> > > + #include <linux/gpio/property.h> > > + > > + /* > > + * 1. Define a node for the GPIO controller. Its .name must match the > > + * controller's label. > > + */ > > + static const struct software_node gpio_controller_node = { > > + .name = "gpio-foo", > > + }; > > + > > + /* 2. Define the properties for the LED device. */ > > + static const struct property_entry led_device_props[] = { > > + PROPERTY_ENTRY_STRING("label", "myboard:green:status"), > > + PROPERTY_ENTRY_STRING("linux,default-trigger", "heartbeat"), > > + PROPERTY_ENTRY_GPIO("gpios", &gpio_controller_node, 42, GPIO_ACTIVE_HIGH), > > + { } > > + }; > > + > > + /* 3. Define the software node for the LED device. */ > > + static const struct software_node led_device_swnode = { > > + .name = "status-led", > > + .properties = led_device_props, > > + }; > > + > > + /* > > + * 4. Register the software nodes and the platform device. > > + */ > > + const struct software_node *swnodes[] = { > > + &gpio_controller_node, > > + &led_device_swnode, > > + NULL > > + }; > > + software_node_register_node_group(swnodes); > > + > > + // Then register a platform_device for "leds-gpio" and associate > > + // it with &led_device_swnode via .fwnode. > > + > > +For a complete guide on converting board files to use software nodes, see > > +Documentation/driver-api/gpio/legacy-boards.rst. > > + > > Platform Data > > ------------- > > Finally, GPIOs can be bound to devices and functions using platform data. Board > > > > > diff --git a/Documentation/driver-api/gpio/legacy-boards.rst b/Documentation/driver-api/gpio/legacy-boards.rst > > new file mode 100644 > > index 000000000000..6700a2549220 > > --- /dev/null > > +++ b/Documentation/driver-api/gpio/legacy-boards.rst > > @@ -0,0 +1,298 @@ > > +Supporting Legacy Boards > > +======================== > > + > > +Many drivers in the kernel, such as ``leds-gpio`` and ``gpio-keys``, are > > +migrating away from using board-specific ``platform_data`` to a unified device > > +properties interface. This interface allows drivers to be simpler and more > > +generic, as they can query properties in a standardized way. > > + > > +On modern systems, these properties are provided via device tree. However, some > > +older platforms have not been converted to device tree and instead rely on > > +board files to describe their hardware configuration. To bridge this gap and > > +allow these legacy boards to work with modern, generic drivers, the kernel > > +provides a mechanism called **software nodes**. > > + > > +This document provides a guide on how to convert a legacy board file from using > > +``platform_data`` and ``gpiod_lookup_table`` to the modern software node > > +approach for describing GPIO-connected devices. > > + > > +The Core Idea: Software Nodes > > +----------------------------- > > + > > +Software nodes allows to construct an in-memory, device-tree-like structure > > allow {some object of the verb, as suggested above} Done. > > > +using ``struct software_node`` and ``struct property_entry``. This structure > > Please drop the "``" markups. They aren't needed. As I mentioned above some kind of markups currently is needed. > > > +can then be associated with a platform device, allowing drivers to use the > > +standard device properties API (e.g., ``device_property_read_u32()``, > > +``device_property_read_string()``) to query configuration, just as they would > > +on an ACPI or device tree systems. > > system. OK. > > > + > > +The gpiolib code has support for handling software nodes, so that if GPIO is > > +described properly, as detailed in the section below, then regular gpiolib APIs, > > +such as ``gpiod_get()``, ``gpiod_get_optional()`` and others will work. > > + > > +Requirements for GPIO Properties > > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > + > > +When using software nodes to describe GPIO connections, the following > > +requirements must be met for the GPIO core to correctly resolve the reference: > > + > > +1. **The GPIO controller's software node ``name`` must match the controller's > > + ``label``.** The gpiolib core uses this name to find the corresponding > > + ``struct gpio_chip`` at runtime. > > "``" not needed. Dropped. > > > + This software node has to be registered, but need not be attached to the > > + device representing GPIO controller that is providing GPIO in question. > > + It may be left as a "free floating" node. > > + > > +2. **The GPIO property must be a reference.** The ``PROPERTY_ENTRY_GPIO()`` > > + macro handles this as it is an alias for ``PROPERTY_ENTRY_REF()``. > > + > > +3. **The reference must have exactly two arguments:** > > + > > + - The first argument is the GPIO offset within the controller. > > + - The second argument is the flags for the GPIO line (e.g., > > + ``GPIO_ACTIVE_HIGH``, ``GPIO_ACTIVE_LOW``). > > + > > +The ``PROPERTY_ENTRY_GPIO()`` macro is the preferred way of defining GPIO > > +properties in software nodes. > > + > > +Conversion Example > > +------------------ > > + > > +Let's walk through an example of converting a board file that defines a GPIO- > > +connected LED and a button. > > + > > +Before: Using Platform Data > > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > + > > +A typical legacy board file might look like this: > > + > > +.. code-block:: c > > + > > + #include <linux/platform_device.h> > > + #include <linux/leds.h> > > + #include <linux/gpio_keys.h> > > + #include <linux/gpio/machine.h> > > + > > + #define MYBOARD_GPIO_CONTROLLER "gpio-foo" > > + > > + /* LED setup */ > > + static const struct gpio_led myboard_leds[] = { > > + { > > + .name = "myboard:green:status", > > + .default_trigger = "heartbeat", > > + }, > > + }; > > + > > + static const struct gpio_led_platform_data myboard_leds_pdata = { > > + .num_leds = ARRAY_SIZE(myboard_leds), > > + .leds = myboard_leds, > > + }; > > + > > + static struct gpiod_lookup_table myboard_leds_gpios = { > > + .dev_id = "leds-gpio", > > + .table = { > > + GPIO_LOOKUP_IDX(MYBOARD_GPIO_CONTROLLER, 42, NULL, 0, GPIO_ACTIVE_HIGH), > > + { }, > > + }, > > + }; > > + > > + /* Button setup */ > > + static struct gpio_keys_button myboard_buttons[] = { > > + { > > + .code = KEY_WPS_BUTTON, > > + .desc = "WPS Button", > > + .active_low = 1, > > + }, > > + }; > > + > > + static const struct gpio_keys_platform_data myboard_buttons_pdata = { > > + .buttons = myboard_buttons, > > + .nbuttons = ARRAY_SIZE(myboard_buttons), > > + }; > > + > > + static struct gpiod_lookup_table myboard_buttons_gpios = { > > + .dev_id = "gpio-keys", > > + .table = { > > + GPIO_LOOKUP_IDX(MYBOARD_GPIO_CONTROLLER, 15, NULL, 0, GPIO_ACTIVE_LOW), > > + { }, > > + }, > > + }; > > + > > + /* Device registration */ > > + static int __init myboard_init(void) > > + { > > + gpiod_add_lookup_table(&myboard_leds_gpios); > > + gpiod_add_lookup_table(&myboard_buttons_gpios); > > + > > + platform_device_register_data(NULL, "leds-gpio", -1, > > + &myboard_leds_pdata, sizeof(myboard_leds_pdata)); > > + platform_device_register_data(NULL, "gpio-keys", -1, > > + &myboard_buttons_pdata, sizeof(myboard_buttons_pdata)); > > + > > + return 0; > > + } > > + > > +After: Using Software Nodes > > +~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > + > > +Here is how the same configuration can be expressed using software nodes. > > + > > +Step 1: Define the GPIO Controller Node > > +*************************************** > > + > > +First, define a software node that represents the GPIO controller that the > > +LEDs and buttons are connected to. The ``name`` of this node must match the > > +name of the driver for the GPIO controller (e.g., "gpio-foo"). > > + > > +.. code-block:: c > > + > > + #include <linux/property.h> > > + #include <linux/gpio/property.h> > > + > > + #define MYBOARD_GPIO_CONTROLLER "gpio-foo" > > + > > + static const struct software_node myboard_gpio_controller_node = { > > + .name = MYBOARD_GPIO_CONTROLLER, > > + }; > > + > > +Step 2: Define Consumer Device Nodes and Properties > > +*************************************************** > > + > > +Next, define the software nodes for the consumer devices (the LEDs and buttons). > > +This involves creating a parent node for each device type and child nodes for > > +each individual LED or button. > > + > > +.. code-block:: c > > + > > + /* LED setup */ > > + static const struct software_node myboard_leds_node = { > > + .name = "myboard-leds", > > + }; > > + > > + static const struct property_entry myboard_status_led_props[] = { > > + PROPERTY_ENTRY_STRING("label", "myboard:green:status"), > > + PROPERTY_ENTRY_STRING("linux,default-trigger", "heartbeat"), > > + PROPERTY_ENTRY_GPIO("gpios", &myboard_gpio_controller_node, 42, GPIO_ACTIVE_HIGH), > > + { } > > + }; > > + > > + static const struct software_node myboard_status_led_swnode = { > > + .name = "status-led", > > + .parent = &myboard_leds_node, > > + .properties = myboard_status_led_props, > > + }; > > + > > + /* Button setup */ > > + static const struct software_node myboard_keys_node = { > > + .name = "myboard-keys", > > + }; > > + > > + static const struct property_entry myboard_wps_button_props[] = { > > + PROPERTY_ENTRY_STRING("label", "WPS Button"), > > + PROPERTY_ENTRY_U32("linux,code", KEY_WPS_BUTTON), > > + PROPERTY_ENTRY_GPIO("gpios", &myboard_gpio_controller_node, 15, GPIO_ACTIVE_LOW), > > + { } > > + }; > > + > > + static const struct software_node myboard_wps_button_swnode = { > > + .name = "wps-button", > > + .parent = &myboard_keys_node, > > + .properties = myboard_wps_button_props, > > + }; > > + > > + > > + > > +Step 3: Group and Register the Nodes > > +************************************ > > + > > +For maintainability, it is often beneficial to group all software nodes into a > > +single array and register them with one call. > > + > > +.. code-block:: c > > + > > + static const struct software_node * const myboard_swnodes[] __initconst = { > > + &myboard_gpio_controller_node, > > + &myboard_leds_node, > > + &myboard_status_led_swnode, > > + &myboard_keys_node, > > + &myboard_wps_button_swnode, > > + NULL > > + }; > > + > > + static int __init myboard_init(void) > > + { > > + int error; > > + > > + error = software_node_register_node_group(myboard_swnodes); > > + if (error) { > > + pr_err("Failed to register software nodes: %d\n", error); > > + return error; > > + } > > + > > + // ... platform device registration follows > > + } > > + > > +.. note:: > > + When splitting registration of nodes by devices that they represent, it is > > + essential that the software node representing the GPIO controller itself > > + is registered first, before any of the nodes that reference it. > > + > > +Step 4: Register Platform Devices with Software Nodes > > +***************************************************** > > + > > +Finally, register the platform devices and associate them with their respective > > +software nodes using the ``fwnode`` field in ``struct platform_device_info``. > > Drop the "``" on struct platform_device_info. It is not documented anywhere, so used :c:type:`....` for it. Thanks. -- Dmitry