1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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 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.
|