| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- 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 allow board-specific code to construct an in-memory,
- device-tree-like structure using struct software_node and struct
- property_entry. This structure 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 system.
- 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.
- This software node has to be registered, but need not be attached to the
- device representing the GPIO controller that is providing the 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[] = {
- &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.
- .. code-block:: c
- static struct platform_device *leds_pdev;
- static struct platform_device *keys_pdev;
- static int __init myboard_init(void)
- {
- struct platform_device_info pdev_info;
- int error;
- error = software_node_register_node_group(myboard_swnodes);
- if (error)
- return error;
- memset(&pdev_info, 0, sizeof(pdev_info));
- pdev_info.name = "leds-gpio";
- pdev_info.id = PLATFORM_DEVID_NONE;
- pdev_info.fwnode = software_node_fwnode(&myboard_leds_node);
- leds_pdev = platform_device_register_full(&pdev_info);
- if (IS_ERR(leds_pdev)) {
- error = PTR_ERR(leds_pdev);
- goto err_unregister_nodes;
- }
- memset(&pdev_info, 0, sizeof(pdev_info));
- pdev_info.name = "gpio-keys";
- pdev_info.id = PLATFORM_DEVID_NONE;
- pdev_info.fwnode = software_node_fwnode(&myboard_keys_node);
- keys_pdev = platform_device_register_full(&pdev_info);
- if (IS_ERR(keys_pdev)) {
- error = PTR_ERR(keys_pdev);
- platform_device_unregister(leds_pdev);
- goto err_unregister_nodes;
- }
- return 0;
- err_unregister_nodes:
- software_node_unregister_node_group(myboard_swnodes);
- return error;
- }
- static void __exit myboard_exit(void)
- {
- platform_device_unregister(keys_pdev);
- platform_device_unregister(leds_pdev);
- software_node_unregister_node_group(myboard_swnodes);
- }
- With these changes, the generic ``leds-gpio`` and ``gpio-keys`` drivers will
- be able to probe successfully and get their configuration from the properties
- defined in the software nodes, removing the need for board-specific platform
- data.
|