]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
5917112c SG |
2 | /* |
3 | * Copyright (c) 2015 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
5917112c SG |
5 | */ |
6 | ||
b953ec2b PD |
7 | #define LOG_CATEGORY UCLASS_LED |
8 | ||
5917112c SG |
9 | #include <dm.h> |
10 | #include <errno.h> | |
11 | #include <led.h> | |
d7a435a2 | 12 | #include <dm/device-internal.h> |
3bf0515a | 13 | #include <dm/lists.h> |
5917112c SG |
14 | #include <dm/root.h> |
15 | #include <dm/uclass-internal.h> | |
592b6f39 HS |
16 | #include <dt-bindings/leds/common.h> |
17 | ||
18 | static const char * const led_colors[LED_COLOR_ID_MAX] = { | |
19 | [LED_COLOR_ID_WHITE] = "white", | |
20 | [LED_COLOR_ID_RED] = "red", | |
21 | [LED_COLOR_ID_GREEN] = "green", | |
22 | [LED_COLOR_ID_BLUE] = "blue", | |
23 | [LED_COLOR_ID_AMBER] = "amber", | |
24 | [LED_COLOR_ID_VIOLET] = "violet", | |
25 | [LED_COLOR_ID_YELLOW] = "yellow", | |
26 | [LED_COLOR_ID_IR] = "ir", | |
27 | [LED_COLOR_ID_MULTI] = "multicolor", | |
28 | [LED_COLOR_ID_RGB] = "rgb", | |
29 | [LED_COLOR_ID_PURPLE] = "purple", | |
30 | [LED_COLOR_ID_ORANGE] = "orange", | |
31 | [LED_COLOR_ID_PINK] = "pink", | |
32 | [LED_COLOR_ID_CYAN] = "cyan", | |
33 | [LED_COLOR_ID_LIME] = "lime", | |
34 | }; | |
5917112c | 35 | |
3bf0515a RV |
36 | int led_bind_generic(struct udevice *parent, const char *driver_name) |
37 | { | |
38 | struct udevice *dev; | |
39 | ofnode node; | |
40 | int ret; | |
41 | ||
42 | dev_for_each_subnode(node, parent) { | |
43 | ret = device_bind_driver_to_node(parent, driver_name, | |
44 | ofnode_get_name(node), | |
45 | node, &dev); | |
46 | if (ret) | |
47 | return ret; | |
48 | } | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
5917112c SG |
53 | int led_get_by_label(const char *label, struct udevice **devp) |
54 | { | |
55 | struct udevice *dev; | |
56 | struct uclass *uc; | |
57 | int ret; | |
58 | ||
59 | ret = uclass_get(UCLASS_LED, &uc); | |
60 | if (ret) | |
61 | return ret; | |
62 | uclass_foreach_dev(dev, uc) { | |
caa4daa2 | 63 | struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); |
5917112c | 64 | |
fb8a5ffc SG |
65 | /* Ignore the top-level LED node */ |
66 | if (uc_plat->label && !strcmp(label, uc_plat->label)) | |
5917112c SG |
67 | return uclass_get_device_tail(dev, 0, devp); |
68 | } | |
69 | ||
fb8a5ffc | 70 | return -ENODEV; |
5917112c SG |
71 | } |
72 | ||
ddae9fcd | 73 | int led_set_state(struct udevice *dev, enum led_state_t state) |
5917112c SG |
74 | { |
75 | struct led_ops *ops = led_get_ops(dev); | |
76 | ||
ddae9fcd | 77 | if (!ops->set_state) |
5917112c SG |
78 | return -ENOSYS; |
79 | ||
b557f55e MP |
80 | if (IS_ENABLED(CONFIG_LED_SW_BLINK) && |
81 | led_sw_on_state_change(dev, state)) | |
82 | return 0; | |
83 | ||
ddae9fcd | 84 | return ops->set_state(dev, state); |
5917112c SG |
85 | } |
86 | ||
8f4b6123 SG |
87 | enum led_state_t led_get_state(struct udevice *dev) |
88 | { | |
89 | struct led_ops *ops = led_get_ops(dev); | |
90 | ||
91 | if (!ops->get_state) | |
92 | return -ENOSYS; | |
93 | ||
b557f55e MP |
94 | if (IS_ENABLED(CONFIG_LED_SW_BLINK) && |
95 | led_sw_is_blinking(dev)) | |
96 | return LEDST_BLINK; | |
97 | ||
8f4b6123 SG |
98 | return ops->get_state(dev); |
99 | } | |
100 | ||
53378dac SG |
101 | int led_set_period(struct udevice *dev, int period_ms) |
102 | { | |
2a15c676 | 103 | #ifdef CONFIG_LED_BLINK |
53378dac SG |
104 | struct led_ops *ops = led_get_ops(dev); |
105 | ||
2a15c676 MK |
106 | if (ops->set_period) |
107 | return ops->set_period(dev, period_ms); | |
108 | #endif | |
53378dac | 109 | |
b557f55e MP |
110 | if (IS_ENABLED(CONFIG_LED_SW_BLINK)) |
111 | return led_sw_set_period(dev, period_ms); | |
112 | ||
2a15c676 | 113 | return -ENOSYS; |
53378dac | 114 | } |
53378dac | 115 | |
914fd75a CM |
116 | #ifdef CONFIG_LED_BOOT |
117 | static int led_boot_get(struct udevice **devp, int *period_ms) | |
118 | { | |
119 | struct led_uc_priv *priv; | |
120 | struct uclass *uc; | |
121 | int ret; | |
122 | ||
123 | ret = uclass_get(UCLASS_LED, &uc); | |
124 | if (ret) | |
125 | return ret; | |
126 | ||
127 | priv = uclass_get_priv(uc); | |
128 | if (!priv->boot_led_label) | |
129 | return -ENOENT; | |
130 | ||
131 | if (period_ms) | |
132 | *period_ms = priv->boot_led_period; | |
133 | ||
134 | return led_get_by_label(priv->boot_led_label, devp); | |
135 | } | |
136 | ||
137 | int led_boot_on(void) | |
138 | { | |
139 | struct udevice *dev; | |
140 | int ret; | |
141 | ||
142 | ret = led_boot_get(&dev, NULL); | |
143 | if (ret) | |
144 | return ret; | |
145 | ||
146 | return led_set_state(dev, LEDST_ON); | |
147 | } | |
148 | ||
149 | int led_boot_off(void) | |
150 | { | |
151 | struct udevice *dev; | |
152 | int ret; | |
153 | ||
154 | ret = led_boot_get(&dev, NULL); | |
155 | if (ret) | |
156 | return ret; | |
157 | ||
158 | return led_set_state(dev, LEDST_OFF); | |
159 | } | |
160 | ||
161 | #if defined(CONFIG_LED_BLINK) || defined(CONFIG_LED_SW_BLINK) | |
162 | int led_boot_blink(void) | |
163 | { | |
164 | struct udevice *dev; | |
165 | int period_ms, ret; | |
166 | ||
167 | ret = led_boot_get(&dev, &period_ms); | |
168 | if (ret) | |
169 | return ret; | |
170 | ||
171 | ret = led_set_period(dev, period_ms); | |
172 | if (ret) { | |
173 | if (ret != -ENOSYS) | |
174 | return ret; | |
175 | ||
176 | /* fallback to ON with no set_period and no SW_BLINK */ | |
177 | return led_set_state(dev, LEDST_ON); | |
178 | } | |
179 | ||
180 | return led_set_state(dev, LEDST_BLINK); | |
181 | } | |
182 | #endif | |
183 | #endif | |
184 | ||
aad8cfa3 CM |
185 | #ifdef CONFIG_LED_ACTIVITY |
186 | static int led_activity_get(struct udevice **devp, int *period_ms) | |
187 | { | |
188 | struct led_uc_priv *priv; | |
189 | struct uclass *uc; | |
190 | int ret; | |
191 | ||
192 | ret = uclass_get(UCLASS_LED, &uc); | |
193 | if (ret) | |
194 | return ret; | |
195 | ||
196 | priv = uclass_get_priv(uc); | |
197 | if (!priv->activity_led_label) | |
198 | return -ENOENT; | |
199 | ||
200 | if (period_ms) | |
201 | *period_ms = priv->activity_led_period; | |
202 | ||
203 | return led_get_by_label(priv->activity_led_label, devp); | |
204 | } | |
205 | ||
206 | int led_activity_on(void) | |
207 | { | |
208 | struct udevice *dev; | |
209 | int ret; | |
210 | ||
211 | ret = led_activity_get(&dev, NULL); | |
212 | if (ret) | |
213 | return ret; | |
214 | ||
215 | return led_set_state(dev, LEDST_ON); | |
216 | } | |
217 | ||
218 | int led_activity_off(void) | |
219 | { | |
220 | struct udevice *dev; | |
221 | int ret; | |
222 | ||
223 | ret = led_activity_get(&dev, NULL); | |
224 | if (ret) | |
225 | return ret; | |
226 | ||
227 | return led_set_state(dev, LEDST_OFF); | |
228 | } | |
229 | ||
230 | #if defined(CONFIG_LED_BLINK) || defined(CONFIG_LED_SW_BLINK) | |
231 | int led_activity_blink(void) | |
232 | { | |
233 | struct udevice *dev; | |
234 | int period_ms, ret; | |
235 | ||
236 | ret = led_activity_get(&dev, &period_ms); | |
237 | if (ret) | |
238 | return ret; | |
239 | ||
240 | ret = led_set_period(dev, period_ms); | |
241 | if (ret) { | |
242 | if (ret != -ENOSYS) | |
243 | return ret; | |
244 | ||
245 | /* fallback to ON with no set_period and no SW_BLINK */ | |
246 | return led_set_state(dev, LEDST_ON); | |
247 | } | |
248 | ||
249 | return led_set_state(dev, LEDST_BLINK); | |
250 | } | |
251 | #endif | |
252 | #endif | |
253 | ||
592b6f39 HS |
254 | static const char *led_get_function_name(struct udevice *dev) |
255 | { | |
256 | struct led_uc_plat *uc_plat; | |
257 | const char *func; | |
258 | u32 color; | |
259 | u32 enumerator; | |
260 | int ret; | |
261 | int cp; | |
262 | ||
263 | if (!dev) | |
264 | return NULL; | |
265 | ||
266 | uc_plat = dev_get_uclass_plat(dev); | |
267 | if (!uc_plat) | |
268 | return NULL; | |
269 | ||
270 | if (uc_plat->label) | |
271 | return uc_plat->label; | |
272 | ||
273 | /* Now try to detect function label name */ | |
274 | func = dev_read_string(dev, "function"); | |
275 | cp = dev_read_u32(dev, "color", &color); | |
276 | if (cp == 0 || func) { | |
277 | ret = dev_read_u32(dev, "function-enumerator", &enumerator); | |
278 | if (!ret) { | |
279 | snprintf(uc_plat->name, LED_MAX_NAME_SIZE, | |
280 | "%s:%s-%d", | |
281 | cp ? "" : led_colors[color], | |
282 | func ? func : "", enumerator); | |
283 | } else { | |
284 | snprintf(uc_plat->name, LED_MAX_NAME_SIZE, | |
285 | "%s:%s", | |
286 | cp ? "" : led_colors[color], | |
287 | func ? func : ""); | |
288 | } | |
289 | uc_plat->label = uc_plat->name; | |
290 | } | |
291 | ||
292 | return uc_plat->label; | |
293 | } | |
294 | ||
295 | static const char *led_get_label(struct udevice *dev, ofnode node) | |
69542d06 CM |
296 | { |
297 | const char *label; | |
298 | ||
299 | label = ofnode_read_string(node, "label"); | |
592b6f39 HS |
300 | if (!label) |
301 | label = led_get_function_name(dev); | |
69542d06 CM |
302 | if (!label && !ofnode_read_string(node, "compatible")) |
303 | label = ofnode_get_name(node); | |
304 | ||
305 | return label; | |
306 | } | |
307 | ||
83c63f0d MV |
308 | static int led_post_bind(struct udevice *dev) |
309 | { | |
310 | struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); | |
72675b06 | 311 | const char *default_state; |
83c63f0d | 312 | |
5bf61f91 | 313 | if (!uc_plat->label) |
592b6f39 | 314 | uc_plat->label = led_get_label(dev, dev_ofnode(dev)); |
83c63f0d | 315 | |
72675b06 MV |
316 | uc_plat->default_state = LEDST_COUNT; |
317 | ||
318 | default_state = dev_read_string(dev, "default-state"); | |
319 | if (!default_state) | |
320 | return 0; | |
321 | ||
322 | if (!strncmp(default_state, "on", 2)) | |
323 | uc_plat->default_state = LEDST_ON; | |
324 | else if (!strncmp(default_state, "off", 3)) | |
325 | uc_plat->default_state = LEDST_OFF; | |
326 | else | |
327 | return 0; | |
328 | ||
f15e89ef MP |
329 | if (IS_ENABLED(CONFIG_LED_BLINK)) { |
330 | const char *trigger; | |
331 | ||
332 | trigger = dev_read_string(dev, "linux,default-trigger"); | |
333 | if (trigger && !strncmp(trigger, "pattern", 7)) | |
334 | uc_plat->default_state = LEDST_BLINK; | |
335 | } | |
336 | ||
72675b06 MV |
337 | /* |
338 | * In case the LED has default-state DT property, trigger | |
339 | * probe() to configure its default state during startup. | |
340 | */ | |
c438866b MV |
341 | dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); |
342 | ||
343 | return 0; | |
72675b06 MV |
344 | } |
345 | ||
346 | static int led_post_probe(struct udevice *dev) | |
347 | { | |
348 | struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); | |
f15e89ef MP |
349 | int default_period_ms = 1000; |
350 | int ret = 0; | |
351 | ||
352 | switch (uc_plat->default_state) { | |
353 | case LEDST_ON: | |
354 | case LEDST_OFF: | |
355 | ret = led_set_state(dev, uc_plat->default_state); | |
356 | break; | |
357 | case LEDST_BLINK: | |
358 | ret = led_set_period(dev, default_period_ms); | |
359 | if (!ret) | |
360 | ret = led_set_state(dev, uc_plat->default_state); | |
361 | break; | |
362 | default: | |
363 | break; | |
364 | } | |
72675b06 | 365 | |
f15e89ef | 366 | return ret; |
83c63f0d MV |
367 | } |
368 | ||
aad8cfa3 | 369 | #if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY) |
914fd75a CM |
370 | static int led_init(struct uclass *uc) |
371 | { | |
372 | struct led_uc_priv *priv = uclass_get_priv(uc); | |
69542d06 CM |
373 | ofnode led_node; |
374 | int ret; | |
914fd75a | 375 | |
aad8cfa3 | 376 | #ifdef CONFIG_LED_BOOT |
69542d06 CM |
377 | ret = ofnode_options_get_by_phandle("boot-led", &led_node); |
378 | if (!ret) | |
592b6f39 | 379 | priv->boot_led_label = led_get_label(NULL, led_node); |
69542d06 | 380 | priv->boot_led_period = ofnode_options_read_int("boot-led-period-ms", 250); |
aad8cfa3 CM |
381 | #endif |
382 | ||
383 | #ifdef CONFIG_LED_ACTIVITY | |
69542d06 CM |
384 | ret = ofnode_options_get_by_phandle("activity-led", &led_node); |
385 | if (!ret) | |
592b6f39 | 386 | priv->activity_led_label = led_get_label(NULL, led_node); |
69542d06 | 387 | priv->activity_led_period = ofnode_options_read_int("activity-led-period-ms", |
aad8cfa3 CM |
388 | 250); |
389 | #endif | |
914fd75a CM |
390 | |
391 | return 0; | |
392 | } | |
393 | #endif | |
394 | ||
5917112c SG |
395 | UCLASS_DRIVER(led) = { |
396 | .id = UCLASS_LED, | |
397 | .name = "led", | |
caa4daa2 | 398 | .per_device_plat_auto = sizeof(struct led_uc_plat), |
83c63f0d | 399 | .post_bind = led_post_bind, |
72675b06 | 400 | .post_probe = led_post_probe, |
aad8cfa3 | 401 | #if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY) |
914fd75a CM |
402 | .init = led_init, |
403 | .priv_auto = sizeof(struct led_uc_priv), | |
404 | #endif | |
5917112c | 405 | }; |