Documentation/driver-api/gpio/legacy-boards.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

Supporting Legacy Boards

Legacy platform_data와 gpiod lookup table을 software node property로 전환하는 절차입니다.

Source pathDocumentation/driver-api/gpio/legacy-boards.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

legacy-boards.rst:1-298

Device Tree가 없는 legacy board도 software node를 통해 modern generic GPIO driver를 사용할 수 있습니다. Controller node name은 gpio_chip label과 같아야 하고 GPIO property는 provider reference, offset, polarity flag를 정확히 담아야 합니다.

Controller node를 먼저 등록하고 consumer parent·child node를 묶은 뒤 platform device의 fwnode에 연결합니다. Error path에서는 platform device와 node group을 의존성의 역순으로 정리합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 Supporting Legacy Boards
2 ========================
3
4 Many drivers in the kernel, such as ``leds-gpio`` and ``gpio-keys``, are
5 migrating away from using board-specific ``platform_data`` to a unified device
6 properties interface. This interface allows drivers to be simpler and more
7 generic, as they can query properties in a standardized way.
8
9 On modern systems, these properties are provided via device tree. However, some
10 older platforms have not been converted to device tree and instead rely on
11 board files to describe their hardware configuration. To bridge this gap and
12 allow these legacy boards to work with modern, generic drivers, the kernel
13 provides a mechanism called **software nodes**.
14
15 This document provides a guide on how to convert a legacy board file from using
16 ``platform_data`` and ``gpiod_lookup_table`` to the modern software node
17 approach for describing GPIO-connected devices.
18
19 The Core Idea: Software Nodes
20 -----------------------------
21
22 Software nodes allow board-specific code to construct an in-memory,
23 device-tree-like structure using struct software_node and struct
24 property_entry. This structure can then be associated with a platform device,
25 allowing drivers to use the standard device properties API (e.g.,
26 device_property_read_u32(), device_property_read_string()) to query
27 configuration, just as they would on an ACPI or device tree system.
28
29 The gpiolib code has support for handling software nodes, so that if GPIO is
30 described properly, as detailed in the section below, then regular gpiolib APIs,
31 such as gpiod_get(), gpiod_get_optional(), and others will work.
32
33 Requirements for GPIO Properties
34 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35
36 When using software nodes to describe GPIO connections, the following
37 requirements must be met for the GPIO core to correctly resolve the reference:
38
39 1. **The GPIO controller's software node "name" must match the controller's
40 "label".** The gpiolib core uses this name to find the corresponding
41 struct gpio_chip at runtime.
42 This software node has to be registered, but need not be attached to the
43 device representing the GPIO controller that is providing the GPIO in
44 question. It may be left as a "free floating" node.
45
46 2. **The GPIO property must be a reference.** The ``PROPERTY_ENTRY_GPIO()``
47 macro handles this as it is an alias for ``PROPERTY_ENTRY_REF()``.
48
49 3. **The reference must have exactly two arguments:**
50
51 - The first argument is the GPIO offset within the controller.
52 - The second argument is the flags for the GPIO line (e.g.,
53 GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW).
54
55 The ``PROPERTY_ENTRY_GPIO()`` macro is the preferred way of defining GPIO
56 properties in software nodes.
57
58 Conversion Example
59 ------------------
60
61 Let's walk through an example of converting a board file that defines a GPIO-
62 connected LED and a button.
63
64 Before: Using Platform Data
65 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66
67 A typical legacy board file might look like this:
68
69 .. code-block:: c
70
71 #include <linux/platform_device.h>
72 #include <linux/leds.h>
73 #include <linux/gpio_keys.h>
74 #include <linux/gpio/machine.h>
75
76 #define MYBOARD_GPIO_CONTROLLER "gpio-foo"
77
78 /* LED setup */
79 static const struct gpio_led myboard_leds[] = {
80 {
81 .name = "myboard:green:status",
82 .default_trigger = "heartbeat",
83 },
84 };
85
86 static const struct gpio_led_platform_data myboard_leds_pdata = {
87 .num_leds = ARRAY_SIZE(myboard_leds),
88 .leds = myboard_leds,
89 };
90
91 static struct gpiod_lookup_table myboard_leds_gpios = {
92 .dev_id = "leds-gpio",
93 .table = {
94 GPIO_LOOKUP_IDX(MYBOARD_GPIO_CONTROLLER, 42, NULL, 0, GPIO_ACTIVE_HIGH),
95 { },
96 },
97 };
98
99 /* Button setup */
100 static struct gpio_keys_button myboard_buttons[] = {
101 {
102 .code = KEY_WPS_BUTTON,
103 .desc = "WPS Button",
104 .active_low = 1,
105 },
106 };
107
108 static const struct gpio_keys_platform_data myboard_buttons_pdata = {
109 .buttons = myboard_buttons,
110 .nbuttons = ARRAY_SIZE(myboard_buttons),
111 };
112
113 static struct gpiod_lookup_table myboard_buttons_gpios = {
114 .dev_id = "gpio-keys",
115 .table = {
116 GPIO_LOOKUP_IDX(MYBOARD_GPIO_CONTROLLER, 15, NULL, 0, GPIO_ACTIVE_LOW),
117 { },
118 },
119 };
120
121 /* Device registration */
122 static int __init myboard_init(void)
123 {
124 gpiod_add_lookup_table(&myboard_leds_gpios);
125 gpiod_add_lookup_table(&myboard_buttons_gpios);
126
127 platform_device_register_data(NULL, "leds-gpio", -1,
128 &myboard_leds_pdata, sizeof(myboard_leds_pdata));
129 platform_device_register_data(NULL, "gpio-keys", -1,
130 &myboard_buttons_pdata, sizeof(myboard_buttons_pdata));
131
132 return 0;
133 }
134
135 After: Using Software Nodes
136 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
137
138 Here is how the same configuration can be expressed using software nodes.
139
140 Step 1: Define the GPIO Controller Node
141 ***************************************
142
143 First, define a software node that represents the GPIO controller that the
144 LEDs and buttons are connected to. The ``name`` of this node must match the
145 name of the driver for the GPIO controller (e.g., "gpio-foo").
146
147 .. code-block:: c
148
149 #include <linux/property.h>
150 #include <linux/gpio/property.h>
151
152 #define MYBOARD_GPIO_CONTROLLER "gpio-foo"
153
154 static const struct software_node myboard_gpio_controller_node = {
155 .name = MYBOARD_GPIO_CONTROLLER,
156 };
157
158 Step 2: Define Consumer Device Nodes and Properties
159 ***************************************************
160
161 Next, define the software nodes for the consumer devices (the LEDs and buttons).
162 This involves creating a parent node for each device type and child nodes for
163 each individual LED or button.
164
165 .. code-block:: c
166
167 /* LED setup */
168 static const struct software_node myboard_leds_node = {
169 .name = "myboard-leds",
170 };
171
172 static const struct property_entry myboard_status_led_props[] = {
173 PROPERTY_ENTRY_STRING("label", "myboard:green:status"),
174 PROPERTY_ENTRY_STRING("linux,default-trigger", "heartbeat"),
175 PROPERTY_ENTRY_GPIO("gpios", &myboard_gpio_controller_node, 42, GPIO_ACTIVE_HIGH),
176 { }
177 };
178
179 static const struct software_node myboard_status_led_swnode = {
180 .name = "status-led",
181 .parent = &myboard_leds_node,
182 .properties = myboard_status_led_props,
183 };
184
185 /* Button setup */
186 static const struct software_node myboard_keys_node = {
187 .name = "myboard-keys",
188 };
189
190 static const struct property_entry myboard_wps_button_props[] = {
191 PROPERTY_ENTRY_STRING("label", "WPS Button"),
192 PROPERTY_ENTRY_U32("linux,code", KEY_WPS_BUTTON),
193 PROPERTY_ENTRY_GPIO("gpios", &myboard_gpio_controller_node, 15, GPIO_ACTIVE_LOW),
194 { }
195 };
196
197 static const struct software_node myboard_wps_button_swnode = {
198 .name = "wps-button",
199 .parent = &myboard_keys_node,
200 .properties = myboard_wps_button_props,
201 };
202
203
204
205 Step 3: Group and Register the Nodes
206 ************************************
207
208 For maintainability, it is often beneficial to group all software nodes into a
209 single array and register them with one call.
210
211 .. code-block:: c
212
213 static const struct software_node * const myboard_swnodes[] = {
214 &myboard_gpio_controller_node,
215 &myboard_leds_node,
216 &myboard_status_led_swnode,
217 &myboard_keys_node,
218 &myboard_wps_button_swnode,
219 NULL
220 };
221
222 static int __init myboard_init(void)
223 {
224 int error;
225
226 error = software_node_register_node_group(myboard_swnodes);
227 if (error) {
228 pr_err("Failed to register software nodes: %d\n", error);
229 return error;
230 }
231
232 // ... platform device registration follows
233 }
234
235 .. note::
236 When splitting registration of nodes by devices that they represent, it is
237 essential that the software node representing the GPIO controller itself
238 is registered first, before any of the nodes that reference it.
239
240 Step 4: Register Platform Devices with Software Nodes
241 *****************************************************
242
243 Finally, register the platform devices and associate them with their respective
244 software nodes using the ``fwnode`` field in struct platform_device_info.
245
246 .. code-block:: c
247
248 static struct platform_device *leds_pdev;
249 static struct platform_device *keys_pdev;
250
251 static int __init myboard_init(void)
252 {
253 struct platform_device_info pdev_info;
254 int error;
255
256 error = software_node_register_node_group(myboard_swnodes);
257 if (error)
258 return error;
259
260 memset(&pdev_info, 0, sizeof(pdev_info));
261 pdev_info.name = "leds-gpio";
262 pdev_info.id = PLATFORM_DEVID_NONE;
263 pdev_info.fwnode = software_node_fwnode(&myboard_leds_node);
264 leds_pdev = platform_device_register_full(&pdev_info);
265 if (IS_ERR(leds_pdev)) {
266 error = PTR_ERR(leds_pdev);
267 goto err_unregister_nodes;
268 }
269
270 memset(&pdev_info, 0, sizeof(pdev_info));
271 pdev_info.name = "gpio-keys";
272 pdev_info.id = PLATFORM_DEVID_NONE;
273 pdev_info.fwnode = software_node_fwnode(&myboard_keys_node);
274 keys_pdev = platform_device_register_full(&pdev_info);
275 if (IS_ERR(keys_pdev)) {
276 error = PTR_ERR(keys_pdev);
277 platform_device_unregister(leds_pdev);
278 goto err_unregister_nodes;
279 }
280
281 return 0;
282
283 err_unregister_nodes:
284 software_node_unregister_node_group(myboard_swnodes);
285 return error;
286 }
287
288 static void __exit myboard_exit(void)
289 {
290 platform_device_unregister(keys_pdev);
291 platform_device_unregister(leds_pdev);
292 software_node_unregister_node_group(myboard_swnodes);
293 }
294
295 With these changes, the generic ``leds-gpio`` and ``gpio-keys`` drivers will
296 be able to probe successfully and get their configuration from the properties
297 defined in the software nodes, removing the need for board-specific platform
298 data.
299

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Legacy board를 software node로 이전

1-18

문서 제목은 `Supporting Legacy Boards`입니다. `leds-gpio`, `gpio-keys` 같은 많은 kernel driver가 board별 `platform_data`에서 통합 device-properties interface로 이동하고 있습니다. Driver는 표준 방식으로 property를 조회할 수 있어 더 단순하고 generic해집니다.

Modern system은 이 property를 Device Tree로 제공합니다. 그러나 Device Tree로 전환되지 않은 오래된 platform은 board file에 hardware configuration을 기술합니다. Kernel은 이런 legacy board와 modern generic driver 사이의 간극을 메우기 위해 software node mechanism을 제공합니다.

이 문서는 GPIO-connected device를 기술하던 legacy board file의 `platform_data`와 `gpiod_lookup_table`을 modern software-node 방식으로 변환하는 절차를 설명합니다.

Legacy GPIO board migration
Legacy board fileplatform_data + gpiod_lookup_tablestruct software_node + property_entryPlatform device fwnode 연결Generic driver가 device_property API 사용

Board-specific data를 표준 property interface로 옮기는 방향입니다.

Software node의 핵심 개념

19-32

Software node를 사용하면 board-specific code가 `struct software_node`와 `struct property_entry`로 memory 안에 Device-Tree와 비슷한 구조를 만들 수 있습니다.

이 구조를 platform device와 연결하면 driver는 ACPI나 Device Tree system에서처럼 `device_property_read_u32()`, `device_property_read_string()` 같은 standard device-properties API로 configuration을 조회합니다.

Gpiolib은 software node를 처리합니다. 다음 절의 규칙에 맞게 GPIO를 기술하면 `gpiod_get()`, `gpiod_get_optional()` 등 일반 gpiolib API가 그대로 동작합니다.

Software node property 조회
Board code가 software_node 구성property_entry에 GPIO와 설정 기록Node를 platform device에 연결Generic driver probedevice_property_*()와 gpiod_get*()로 조회

Board code의 in-memory description이 generic driver에 전달됩니다.

GPIO property 참조 요구사항

33-57

Software node로 GPIO connection을 기술할 때 GPIO core가 reference를 올바르게 해석하려면 세 요구사항을 지켜야 합니다.

  • GPIO controller software node의 `name`은 controller의 `label`과 같아야 합니다. Gpiolib core가 runtime에 대응 `struct gpio_chip`을 찾는 key입니다. 이 node는 등록해야 하지만 GPIO controller device에 attach할 필요는 없고 free-floating node여도 됩니다.
  • GPIO property는 reference여야 합니다. `PROPERTY_ENTRY_GPIO()`는 `PROPERTY_ENTRY_REF()`의 alias이므로 이 형식을 자동으로 만듭니다.
  • Reference argument는 정확히 두 개여야 합니다. 첫째는 controller 내부 GPIO offset, 둘째는 `GPIO_ACTIVE_HIGH`·`GPIO_ACTIVE_LOW` 같은 line flag입니다.

Software node에서 GPIO property를 정의할 때는 `PROPERTY_ENTRY_GPIO()` macro 사용을 권장합니다.

Software-node GPIO reference 규칙
항목요구사항이유
Controller node namegpio_chip label과 일치Runtime controller lookup
Property typeReferenceGPIO provider node 연결
Argument 1Controller 내부 offsetGPIO line 선택
Argument 2GPIO_ACTIVE_HIGH/LOW flagLogical polarity 지정
권장 macroPROPERTY_ENTRY_GPIO()정확한 reference 형식 생성

Controller 식별과 reference argument를 정리했습니다.

변환 전: platform data와 lookup table

58-134

예제는 GPIO에 연결된 LED와 button을 정의하는 board file을 변환합니다. 전형적인 legacy board file은 다음과 같습니다.

.. 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;
  }

LED 쪽은 `gpio_led_platform_data`와 `gpiod_lookup_table`을 따로 만들고 controller `gpio-foo`의 offset 42를 `GPIO_ACTIVE_HIGH`로 연결합니다. Button 쪽은 `gpio_keys_platform_data`와 별도 lookup table을 만들고 offset 15를 `GPIO_ACTIVE_LOW`로 연결합니다.

초기화에서는 `gpiod_add_lookup_table()`을 두 번 호출한 뒤 `platform_device_register_data()`로 `leds-gpio`와 `gpio-keys`에 board-specific platform data를 전달합니다.

Legacy 예제의 GPIO 연결
ConsumerProperty dataController·offsetFlag
leds-gpiogpio_led_platform_datagpio-foo · 42GPIO_ACTIVE_HIGH
gpio-keysgpio_keys_platform_datagpio-foo · 15GPIO_ACTIVE_LOW

Platform data와 lookup table에 흩어진 LED·button 정보를 구조화했습니다.

1단계: GPIO controller node 정의

135-157

같은 configuration을 software node로 표현하는 첫 단계는 LED와 button이 연결된 GPIO controller를 나타내는 node를 정의하는 것입니다. Node의 `name`은 GPIO controller driver 이름, 예제에서는 `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,
  };

`linux/property.h`와 `linux/gpio/property.h`를 include하고 `myboard_gpio_controller_node`의 name에 `MYBOARD_GPIO_CONTROLLER`를 지정합니다.

Controller software node
GPIO controller label 확인같은 문자열로 software_node.name 설정Controller node 등록Node는 free-floating 가능PROPERTY_ENTRY_GPIO가 이 node를 reference

Gpiolib이 gpio_chip을 찾는 controller node 구성입니다.

2단계: Consumer node와 property 정의

158-204

다음으로 LED와 button consumer device의 software node를 정의합니다. Device type별 parent node를 만들고 각 LED 또는 button마다 child node를 둡니다.

.. 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,
  };

LED property는 `label`, `linux,default-trigger`, `gpios`를 가지며 GPIO reference는 controller node, offset 42, `GPIO_ACTIVE_HIGH`를 지정합니다. `status-led` child node는 `myboard-leds` parent와 이 property array를 연결합니다.

Button property는 `label`, `linux,code`, `gpios`를 가지며 controller node의 offset 15와 `GPIO_ACTIVE_LOW`를 참조합니다. `wps-button` child node는 `myboard-keys` parent에 속합니다.

Consumer software-node hierarchy
ParentChild주요 propertyGPIO reference
myboard-ledsstatus-ledlabel, linux,default-triggercontroller · 42 · ACTIVE_HIGH
myboard-keyswps-buttonlabel, linux,codecontroller · 15 · ACTIVE_LOW

Parent·child node와 GPIO property를 정리했습니다.

3단계: Node group 등록

205-239

유지보수를 위해 모든 software node를 하나의 array로 묶고 한 번의 호출로 등록하는 것이 좋습니다.

.. 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
  }

Array에는 controller node, LED parent와 child, key parent와 child를 순서대로 넣고 `NULL`로 끝냅니다. `software_node_register_node_group()`이 실패하면 error를 출력하고 그대로 반환합니다.

Device별로 node 등록을 나눈다면 GPIO controller를 나타내는 software node를 이를 reference하는 어느 node보다 먼저 등록해야 합니다.

Software-node 등록 순서
GPIO controller nodeLED parent nodeLED child nodeKey parent nodeKey child nodesoftware_node_register_node_group()

Reference target인 controller를 먼저 준비하는 의존 관계입니다.

4단계: Platform device와 software node 연결

240-298

마지막으로 platform device를 등록하면서 `struct platform_device_info`의 `fwnode` field로 각 software node를 연결합니다.

.. 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);
  }

먼저 node group을 등록한 뒤 `leds-gpio`의 `pdev_info.fwnode`에 `myboard_leds_node`의 fwnode를 지정하고 `platform_device_register_full()`을 호출합니다. 실패하면 node group을 unregister합니다.

이어서 `gpio-keys` device를 `myboard_keys_node`와 연결합니다. 두 번째 등록이 실패하면 먼저 등록한 LED platform device도 unregister한 뒤 node group을 정리합니다.

정상 종료 path에서는 key device, LED device, software-node group 순으로 unregister합니다. 이 변경으로 generic `leds-gpio`와 `gpio-keys` driver가 성공적으로 probe하고 software-node property에서 configuration을 얻으므로 board-specific platform data가 필요 없어집니다.

Platform device 등록과 rollback
Software-node group 등록leds-gpio + myboard_leds fwnode 등록gpio-keys + myboard_keys fwnode 등록성공하면 generic driver probe두 번째 실패 시 LED unregister마지막에 node group unregister

두 consumer device의 fwnode 연결과 error cleanup 순서입니다.

Legacy와 software-node 방식 비교
항목LegacySoftware node
GPIO mappinggpiod_lookup_tablePROPERTY_ENTRY_GPIO
Device configplatform_data structureproperty_entry array
Driver queryDriver별 pdata castdevice_property_* + gpiod_get*
Device associationplatform_device_register_dataplatform_device_info.fwnode
재사용성Board-specificGeneric firmware-node model

변환 전후 configuration 전달 방식을 비교합니다.