1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
5 */
6
7 #define LOG_CATEGORY UCLASS_LED
8
9 #include <dm.h>
10 #include <errno.h>
11 #include <led.h>
12 #include <dm/device-internal.h>
13 #include <dm/lists.h>
14 #include <dm/root.h>
15 #include <dm/uclass-internal.h>
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 };
35
led_bind_generic(struct udevice * parent,const char * driver_name)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
led_get_by_label(const char * label,struct udevice ** devp)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) {
63 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
64
65 /* Ignore the top-level LED node */
66 if (uc_plat->label && !strcmp(label, uc_plat->label))
67 return uclass_get_device_tail(dev, 0, devp);
68 }
69
70 return -ENODEV;
71 }
72
led_set_state(struct udevice * dev,enum led_state_t state)73 int led_set_state(struct udevice *dev, enum led_state_t state)
74 {
75 struct led_ops *ops = led_get_ops(dev);
76
77 if (!ops->set_state)
78 return -ENOSYS;
79
80 if (IS_ENABLED(CONFIG_LED_SW_BLINK) &&
81 led_sw_on_state_change(dev, state))
82 return 0;
83
84 return ops->set_state(dev, state);
85 }
86
led_get_state(struct udevice * dev)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
94 if (IS_ENABLED(CONFIG_LED_SW_BLINK) &&
95 led_sw_is_blinking(dev))
96 return LEDST_BLINK;
97
98 return ops->get_state(dev);
99 }
100
led_set_period(struct udevice * dev,int period_ms)101 int led_set_period(struct udevice *dev, int period_ms)
102 {
103 #ifdef CONFIG_LED_BLINK
104 struct led_ops *ops = led_get_ops(dev);
105
106 if (ops->set_period)
107 return ops->set_period(dev, period_ms);
108 #endif
109
110 if (IS_ENABLED(CONFIG_LED_SW_BLINK))
111 return led_sw_set_period(dev, period_ms);
112
113 return -ENOSYS;
114 }
115
116 #ifdef CONFIG_LED_BOOT
led_boot_get(struct udevice ** devp,int * period_ms)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
led_boot_on(void)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
led_boot_off(void)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)
led_boot_blink(void)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
185 #ifdef CONFIG_LED_ACTIVITY
led_activity_get(struct udevice ** devp,int * period_ms)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
led_activity_on(void)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
led_activity_off(void)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)
led_activity_blink(void)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
led_get_function_name(struct udevice * dev)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 /*
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)
282 cp = -EINVAL;
283
284 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
led_get_label(struct udevice * dev,ofnode node)303 static const char *led_get_label(struct udevice *dev, ofnode node)
304 {
305 const char *label;
306
307 label = ofnode_read_string(node, "label");
308 if (!label)
309 label = led_get_function_name(dev);
310 if (!label && !ofnode_read_string(node, "compatible"))
311 label = ofnode_get_name(node);
312
313 return label;
314 }
315
led_post_bind(struct udevice * dev)316 static int led_post_bind(struct udevice *dev)
317 {
318 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
319 const char *default_state;
320
321 if (!uc_plat->label)
322 uc_plat->label = led_get_label(dev, dev_ofnode(dev));
323
324 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
337 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
345 /*
346 * In case the LED has default-state DT property, trigger
347 * probe() to configure its default state during startup.
348 */
349 dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
350
351 return 0;
352 }
353
led_post_probe(struct udevice * dev)354 static int led_post_probe(struct udevice *dev)
355 {
356 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
357 int default_period_ms = 1000;
358 int ret = 0;
359
360 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 }
373
374 return ret;
375 }
376
377 #if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY)
led_init(struct uclass * uc)378 static int led_init(struct uclass *uc)
379 {
380 struct led_uc_priv *priv = uclass_get_priv(uc);
381 ofnode led_node;
382 int ret;
383
384 #ifdef CONFIG_LED_BOOT
385 ret = ofnode_options_get_by_phandle("boot-led", &led_node);
386 if (!ret)
387 priv->boot_led_label = led_get_label(NULL, led_node);
388 priv->boot_led_period = ofnode_options_read_int("boot-led-period-ms", 250);
389 #endif
390
391 #ifdef CONFIG_LED_ACTIVITY
392 ret = ofnode_options_get_by_phandle("activity-led", &led_node);
393 if (!ret)
394 priv->activity_led_label = led_get_label(NULL, led_node);
395 priv->activity_led_period = ofnode_options_read_int("activity-led-period-ms",
396 250);
397 #endif
398
399 return 0;
400 }
401 #endif
402
403 UCLASS_DRIVER(led) = {
404 .id = UCLASS_LED,
405 .name = "led",
406 .per_device_plat_auto = sizeof(struct led_uc_plat),
407 .post_bind = led_post_bind,
408 .post_probe = led_post_probe,
409 #if defined(CONFIG_LED_BOOT) || defined(CONFIG_LED_ACTIVITY)
410 .init = led_init,
411 .priv_auto = sizeof(struct led_uc_priv),
412 #endif
413 };
414