blob: edcdeee1e9a2a23e968d46eb8406141f7103afa1 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glasscce3aed2015-06-23 15:38:45 -06002/*
3 * Copyright (c) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glasscce3aed2015-06-23 15:38:45 -06005 */
6
Patrick Delaunay81313352021-04-27 11:02:19 +02007#define LOG_CATEGORY UCLASS_LED
8
Simon Glasscce3aed2015-06-23 15:38:45 -06009#include <dm.h>
10#include <errno.h>
11#include <led.h>
Patrick Delaunaya5bf9282018-07-27 16:37:07 +020012#include <dm/device-internal.h>
Rasmus Villemoes49ba9f02023-11-17 12:38:08 +010013#include <dm/lists.h>
Simon Glasscce3aed2015-06-23 15:38:45 -060014#include <dm/root.h>
15#include <dm/uclass-internal.h>
Heiko Schocher350d2522025-01-28 14:52:46 +010016#include <dt-bindings/leds/common.h>
17
18static 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};
Simon Glasscce3aed2015-06-23 15:38:45 -060035
Rasmus Villemoes49ba9f02023-11-17 12:38:08 +010036int 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
Simon Glasscce3aed2015-06-23 15:38:45 -060053int 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) {
Simon Glass71fa5b42020-12-03 16:55:18 -070063 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Simon Glasscce3aed2015-06-23 15:38:45 -060064
Simon Glassbf3ac8192015-07-06 12:54:33 -060065 /* Ignore the top-level LED node */
66 if (uc_plat->label && !strcmp(label, uc_plat->label))
Simon Glasscce3aed2015-06-23 15:38:45 -060067 return uclass_get_device_tail(dev, 0, devp);
68 }
69
Simon Glassbf3ac8192015-07-06 12:54:33 -060070 return -ENODEV;
Simon Glasscce3aed2015-06-23 15:38:45 -060071}
72
Simon Glass6ca19772017-04-10 11:34:54 -060073int led_set_state(struct udevice *dev, enum led_state_t state)
Simon Glasscce3aed2015-06-23 15:38:45 -060074{
75 struct led_ops *ops = led_get_ops(dev);
76
Simon Glass6ca19772017-04-10 11:34:54 -060077 if (!ops->set_state)
Simon Glasscce3aed2015-06-23 15:38:45 -060078 return -ENOSYS;
79
Michael Polyntsov353deb92024-07-19 13:12:12 +040080 if (IS_ENABLED(CONFIG_LED_SW_BLINK) &&
81 led_sw_on_state_change(dev, state))
82 return 0;
83
Simon Glass6ca19772017-04-10 11:34:54 -060084 return ops->set_state(dev, state);
Simon Glasscce3aed2015-06-23 15:38:45 -060085}
86
Simon Glassdc531662017-04-10 11:34:55 -060087enum 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
Michael Polyntsov353deb92024-07-19 13:12:12 +040094 if (IS_ENABLED(CONFIG_LED_SW_BLINK) &&
95 led_sw_is_blinking(dev))
96 return LEDST_BLINK;
97
Simon Glassdc531662017-04-10 11:34:55 -060098 return ops->get_state(dev);
99}
100
Simon Glass3bd0c462017-04-10 11:34:57 -0600101int led_set_period(struct udevice *dev, int period_ms)
102{
Mikhail Kshevetskiy8224f162024-07-19 13:12:11 +0400103#ifdef CONFIG_LED_BLINK
Simon Glass3bd0c462017-04-10 11:34:57 -0600104 struct led_ops *ops = led_get_ops(dev);
105
Mikhail Kshevetskiy8224f162024-07-19 13:12:11 +0400106 if (ops->set_period)
107 return ops->set_period(dev, period_ms);
108#endif
Simon Glass3bd0c462017-04-10 11:34:57 -0600109
Michael Polyntsov353deb92024-07-19 13:12:12 +0400110 if (IS_ENABLED(CONFIG_LED_SW_BLINK))
111 return led_sw_set_period(dev, period_ms);
112
Mikhail Kshevetskiy8224f162024-07-19 13:12:11 +0400113 return -ENOSYS;
Simon Glass3bd0c462017-04-10 11:34:57 -0600114}
Simon Glass3bd0c462017-04-10 11:34:57 -0600115
Christian Marangiea7387e2024-10-01 14:24:36 +0200116#ifdef CONFIG_LED_BOOT
117static 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
137int 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
149int 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)
162int 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
Christian Marangi8be34e82024-10-01 14:24:38 +0200185#ifdef CONFIG_LED_ACTIVITY
186static 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
206int 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
218int 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)
231int 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
Heiko Schocher350d2522025-01-28 14:52:46 +0100254static 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);
Heiko Schocher4603d052025-02-26 10:18:58 +0100276 /*
277 * prevent coverity scan error CID 541279: (TAINTED_SCALAR)
278 * only check the upper bound. No need to check the lower bound
279 * as color is from type u32 and never can be lower than 0.
280 */
281 if (color >= LED_COLOR_ID_MAX)
Heiko Schocher8b493a22025-02-12 10:10:55 +0100282 cp = -EINVAL;
283
Heiko Schocher350d2522025-01-28 14:52:46 +0100284 if (cp == 0 || func) {
285 ret = dev_read_u32(dev, "function-enumerator", &enumerator);
286 if (!ret) {
287 snprintf(uc_plat->name, LED_MAX_NAME_SIZE,
288 "%s:%s-%d",
289 cp ? "" : led_colors[color],
290 func ? func : "", enumerator);
291 } else {
292 snprintf(uc_plat->name, LED_MAX_NAME_SIZE,
293 "%s:%s",
294 cp ? "" : led_colors[color],
295 func ? func : "");
296 }
297 uc_plat->label = uc_plat->name;
298 }
299
300 return uc_plat->label;
301}
302
303static const char *led_get_label(struct udevice *dev, ofnode node)
Christian Marangi477c93b2024-11-10 12:50:26 +0100304{
305 const char *label;
306
307 label = ofnode_read_string(node, "label");
Heiko Schocher350d2522025-01-28 14:52:46 +0100308 if (!label)
309 label = led_get_function_name(dev);
Christian Marangi477c93b2024-11-10 12:50:26 +0100310 if (!label && !ofnode_read_string(node, "compatible"))
311 label = ofnode_get_name(node);
312
313 return label;
314}
315
Marek Vasutc2b7e982022-04-04 01:18:02 +0200316static int led_post_bind(struct udevice *dev)
317{
318 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Marek Vasut03cadb62022-04-04 01:23:27 +0200319 const char *default_state;
Marek Vasutc2b7e982022-04-04 01:18:02 +0200320
Rasmus Villemoesad3401f2023-11-17 12:38:06 +0100321 if (!uc_plat->label)
Heiko Schocher350d2522025-01-28 14:52:46 +0100322 uc_plat->label = led_get_label(dev, dev_ofnode(dev));
Marek Vasutc2b7e982022-04-04 01:18:02 +0200323
Marek Vasut03cadb62022-04-04 01:23:27 +0200324 uc_plat->default_state = LEDST_COUNT;
325
326 default_state = dev_read_string(dev, "default-state");
327 if (!default_state)
328 return 0;
329
330 if (!strncmp(default_state, "on", 2))
331 uc_plat->default_state = LEDST_ON;
332 else if (!strncmp(default_state, "off", 3))
333 uc_plat->default_state = LEDST_OFF;
334 else
335 return 0;
336
Michael Polyntsov56250a62024-07-19 13:12:13 +0400337 if (IS_ENABLED(CONFIG_LED_BLINK)) {
338 const char *trigger;
339
340 trigger = dev_read_string(dev, "linux,default-trigger");
341 if (trigger && !strncmp(trigger, "pattern", 7))
342 uc_plat->default_state = LEDST_BLINK;
343 }
344
Marek Vasut03cadb62022-04-04 01:23:27 +0200345 /*
346 * In case the LED has default-state DT property, trigger
347 * probe() to configure its default state during startup.
348 */
Marek Vasut1c2b81f2022-04-22 15:15:54 +0200349 dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
350
351 return 0;
Marek Vasut03cadb62022-04-04 01:23:27 +0200352}
353
354static int led_post_probe(struct udevice *dev)
355{
356 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Michael Polyntsov56250a62024-07-19 13:12:13 +0400357 int default_period_ms = 1000;
358 int ret = 0;
Marek Vasut03cadb62022-04-04 01:23:27 +0200359
Michael Polyntsov56250a62024-07-19 13:12:13 +0400360 switch (uc_plat->default_state) {
361 case LEDST_ON:
362 case LEDST_OFF:
363 ret = led_set_state(dev, uc_plat->default_state);
364 break;
365 case LEDST_BLINK:
366 ret = led_set_period(dev, default_period_ms);
367 if (!ret)
368 ret = led_set_state(dev, uc_plat->default_state);
369 break;
370 default:
371 break;
372 }
Marek Vasut03cadb62022-04-04 01:23:27 +0200373
Michael Polyntsov56250a62024-07-19 13:12:13 +0400374 return ret;
Marek Vasutc2b7e982022-04-04 01:18:02 +0200375}
Christian Marangiea7387e2024-10-01 14:24:36 +0200376
Christian Marangi8be34e82024-10-01 14:24:38 +0200377#if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY)
Christian Marangiea7387e2024-10-01 14:24:36 +0200378static int led_init(struct uclass *uc)
379{
380 struct led_uc_priv *priv = uclass_get_priv(uc);
Christian Marangi477c93b2024-11-10 12:50:26 +0100381 ofnode led_node;
382 int ret;
Christian Marangiea7387e2024-10-01 14:24:36 +0200383
Christian Marangi8be34e82024-10-01 14:24:38 +0200384#ifdef CONFIG_LED_BOOT
Christian Marangi477c93b2024-11-10 12:50:26 +0100385 ret = ofnode_options_get_by_phandle("boot-led", &led_node);
386 if (!ret)
Heiko Schocher350d2522025-01-28 14:52:46 +0100387 priv->boot_led_label = led_get_label(NULL, led_node);
Christian Marangi477c93b2024-11-10 12:50:26 +0100388 priv->boot_led_period = ofnode_options_read_int("boot-led-period-ms", 250);
Christian Marangi8be34e82024-10-01 14:24:38 +0200389#endif
390
391#ifdef CONFIG_LED_ACTIVITY
Christian Marangi477c93b2024-11-10 12:50:26 +0100392 ret = ofnode_options_get_by_phandle("activity-led", &led_node);
393 if (!ret)
Heiko Schocher350d2522025-01-28 14:52:46 +0100394 priv->activity_led_label = led_get_label(NULL, led_node);
Christian Marangi477c93b2024-11-10 12:50:26 +0100395 priv->activity_led_period = ofnode_options_read_int("activity-led-period-ms",
Christian Marangi8be34e82024-10-01 14:24:38 +0200396 250);
397#endif
Christian Marangiea7387e2024-10-01 14:24:36 +0200398
399 return 0;
400}
401#endif
Marek Vasutc2b7e982022-04-04 01:18:02 +0200402
Simon Glasscce3aed2015-06-23 15:38:45 -0600403UCLASS_DRIVER(led) = {
404 .id = UCLASS_LED,
405 .name = "led",
Simon Glass71fa5b42020-12-03 16:55:18 -0700406 .per_device_plat_auto = sizeof(struct led_uc_plat),
Marek Vasutc2b7e982022-04-04 01:18:02 +0200407 .post_bind = led_post_bind,
Marek Vasut03cadb62022-04-04 01:23:27 +0200408 .post_probe = led_post_probe,
Christian Marangi8be34e82024-10-01 14:24:38 +0200409#if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY)
Christian Marangiea7387e2024-10-01 14:24:36 +0200410 .init = led_init,
411 .priv_auto = sizeof(struct led_uc_priv),
412#endif
Simon Glasscce3aed2015-06-23 15:38:45 -0600413};