blob: 22f61d12d38ef470aa187bb51b76c1a14de118a6 [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 Schocher8b493a22025-02-12 10:10:55 +0100276 // prevent coverity scan error CID 541279: (TAINTED_SCALAR)
277 if (color < LED_COLOR_ID_WHITE || color >= LED_COLOR_ID_MAX)
278 cp = -EINVAL;
279
Heiko Schocher350d2522025-01-28 14:52:46 +0100280 if (cp == 0 || func) {
281 ret = dev_read_u32(dev, "function-enumerator", &enumerator);
282 if (!ret) {
283 snprintf(uc_plat->name, LED_MAX_NAME_SIZE,
284 "%s:%s-%d",
285 cp ? "" : led_colors[color],
286 func ? func : "", enumerator);
287 } else {
288 snprintf(uc_plat->name, LED_MAX_NAME_SIZE,
289 "%s:%s",
290 cp ? "" : led_colors[color],
291 func ? func : "");
292 }
293 uc_plat->label = uc_plat->name;
294 }
295
296 return uc_plat->label;
297}
298
299static const char *led_get_label(struct udevice *dev, ofnode node)
Christian Marangi477c93b2024-11-10 12:50:26 +0100300{
301 const char *label;
302
303 label = ofnode_read_string(node, "label");
Heiko Schocher350d2522025-01-28 14:52:46 +0100304 if (!label)
305 label = led_get_function_name(dev);
Christian Marangi477c93b2024-11-10 12:50:26 +0100306 if (!label && !ofnode_read_string(node, "compatible"))
307 label = ofnode_get_name(node);
308
309 return label;
310}
311
Marek Vasutc2b7e982022-04-04 01:18:02 +0200312static int led_post_bind(struct udevice *dev)
313{
314 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Marek Vasut03cadb62022-04-04 01:23:27 +0200315 const char *default_state;
Marek Vasutc2b7e982022-04-04 01:18:02 +0200316
Rasmus Villemoesad3401f2023-11-17 12:38:06 +0100317 if (!uc_plat->label)
Heiko Schocher350d2522025-01-28 14:52:46 +0100318 uc_plat->label = led_get_label(dev, dev_ofnode(dev));
Marek Vasutc2b7e982022-04-04 01:18:02 +0200319
Marek Vasut03cadb62022-04-04 01:23:27 +0200320 uc_plat->default_state = LEDST_COUNT;
321
322 default_state = dev_read_string(dev, "default-state");
323 if (!default_state)
324 return 0;
325
326 if (!strncmp(default_state, "on", 2))
327 uc_plat->default_state = LEDST_ON;
328 else if (!strncmp(default_state, "off", 3))
329 uc_plat->default_state = LEDST_OFF;
330 else
331 return 0;
332
Michael Polyntsov56250a62024-07-19 13:12:13 +0400333 if (IS_ENABLED(CONFIG_LED_BLINK)) {
334 const char *trigger;
335
336 trigger = dev_read_string(dev, "linux,default-trigger");
337 if (trigger && !strncmp(trigger, "pattern", 7))
338 uc_plat->default_state = LEDST_BLINK;
339 }
340
Marek Vasut03cadb62022-04-04 01:23:27 +0200341 /*
342 * In case the LED has default-state DT property, trigger
343 * probe() to configure its default state during startup.
344 */
Marek Vasut1c2b81f2022-04-22 15:15:54 +0200345 dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
346
347 return 0;
Marek Vasut03cadb62022-04-04 01:23:27 +0200348}
349
350static int led_post_probe(struct udevice *dev)
351{
352 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Michael Polyntsov56250a62024-07-19 13:12:13 +0400353 int default_period_ms = 1000;
354 int ret = 0;
Marek Vasut03cadb62022-04-04 01:23:27 +0200355
Michael Polyntsov56250a62024-07-19 13:12:13 +0400356 switch (uc_plat->default_state) {
357 case LEDST_ON:
358 case LEDST_OFF:
359 ret = led_set_state(dev, uc_plat->default_state);
360 break;
361 case LEDST_BLINK:
362 ret = led_set_period(dev, default_period_ms);
363 if (!ret)
364 ret = led_set_state(dev, uc_plat->default_state);
365 break;
366 default:
367 break;
368 }
Marek Vasut03cadb62022-04-04 01:23:27 +0200369
Michael Polyntsov56250a62024-07-19 13:12:13 +0400370 return ret;
Marek Vasutc2b7e982022-04-04 01:18:02 +0200371}
Christian Marangiea7387e2024-10-01 14:24:36 +0200372
Christian Marangi8be34e82024-10-01 14:24:38 +0200373#if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY)
Christian Marangiea7387e2024-10-01 14:24:36 +0200374static int led_init(struct uclass *uc)
375{
376 struct led_uc_priv *priv = uclass_get_priv(uc);
Christian Marangi477c93b2024-11-10 12:50:26 +0100377 ofnode led_node;
378 int ret;
Christian Marangiea7387e2024-10-01 14:24:36 +0200379
Christian Marangi8be34e82024-10-01 14:24:38 +0200380#ifdef CONFIG_LED_BOOT
Christian Marangi477c93b2024-11-10 12:50:26 +0100381 ret = ofnode_options_get_by_phandle("boot-led", &led_node);
382 if (!ret)
Heiko Schocher350d2522025-01-28 14:52:46 +0100383 priv->boot_led_label = led_get_label(NULL, led_node);
Christian Marangi477c93b2024-11-10 12:50:26 +0100384 priv->boot_led_period = ofnode_options_read_int("boot-led-period-ms", 250);
Christian Marangi8be34e82024-10-01 14:24:38 +0200385#endif
386
387#ifdef CONFIG_LED_ACTIVITY
Christian Marangi477c93b2024-11-10 12:50:26 +0100388 ret = ofnode_options_get_by_phandle("activity-led", &led_node);
389 if (!ret)
Heiko Schocher350d2522025-01-28 14:52:46 +0100390 priv->activity_led_label = led_get_label(NULL, led_node);
Christian Marangi477c93b2024-11-10 12:50:26 +0100391 priv->activity_led_period = ofnode_options_read_int("activity-led-period-ms",
Christian Marangi8be34e82024-10-01 14:24:38 +0200392 250);
393#endif
Christian Marangiea7387e2024-10-01 14:24:36 +0200394
395 return 0;
396}
397#endif
Marek Vasutc2b7e982022-04-04 01:18:02 +0200398
Simon Glasscce3aed2015-06-23 15:38:45 -0600399UCLASS_DRIVER(led) = {
400 .id = UCLASS_LED,
401 .name = "led",
Simon Glass71fa5b42020-12-03 16:55:18 -0700402 .per_device_plat_auto = sizeof(struct led_uc_plat),
Marek Vasutc2b7e982022-04-04 01:18:02 +0200403 .post_bind = led_post_bind,
Marek Vasut03cadb62022-04-04 01:23:27 +0200404 .post_probe = led_post_probe,
Christian Marangi8be34e82024-10-01 14:24:38 +0200405#if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY)
Christian Marangiea7387e2024-10-01 14:24:36 +0200406 .init = led_init,
407 .priv_auto = sizeof(struct led_uc_priv),
408#endif
Simon Glasscce3aed2015-06-23 15:38:45 -0600409};