File Index
Files and directories
Sign in
FILE

led-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/leds/led-core.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * LED Class Core
  4 *
  5 * Copyright 2005-2006 Openedhand Ltd.
  6 *
  7 * Author: Richard Purdie <rpurdie@openedhand.com>
  8 */
  9
 10#include <linux/kernel.h>
 11#include <linux/led-class-multicolor.h>
 12#include <linux/leds.h>
 13#include <linux/list.h>
 14#include <linux/module.h>
 15#include <linux/mutex.h>
 16#include <linux/of.h>
 17#include <linux/property.h>
 18#include <linux/rwsem.h>
 19#include <linux/slab.h>
 20#include <uapi/linux/uleds.h>
 21#include "leds.h"
 22
 23DECLARE_RWSEM(leds_list_lock);
 24EXPORT_SYMBOL_GPL(leds_list_lock);
 25
 26LIST_HEAD(leds_list);
 27EXPORT_SYMBOL_GPL(leds_list);
 28
 29static const char * const led_colors[LED_COLOR_ID_MAX] = {
 30	[LED_COLOR_ID_WHITE] = "white",
 31	[LED_COLOR_ID_RED] = "red",
 32	[LED_COLOR_ID_GREEN] = "green",
 33	[LED_COLOR_ID_BLUE] = "blue",
 34	[LED_COLOR_ID_AMBER] = "amber",
 35	[LED_COLOR_ID_VIOLET] = "violet",
 36	[LED_COLOR_ID_YELLOW] = "yellow",
 37	[LED_COLOR_ID_IR] = "ir",
 38	[LED_COLOR_ID_MULTI] = "multicolor",
 39	[LED_COLOR_ID_RGB] = "rgb",
 40	[LED_COLOR_ID_PURPLE] = "purple",
 41	[LED_COLOR_ID_ORANGE] = "orange",
 42	[LED_COLOR_ID_PINK] = "pink",
 43	[LED_COLOR_ID_CYAN] = "cyan",
 44	[LED_COLOR_ID_LIME] = "lime",
 45};
 46
 47static int __led_set_brightness(struct led_classdev *led_cdev, unsigned int value)
 48{
 49	if (!led_cdev->brightness_set)
 50		return -ENOTSUPP;
 51
 52	led_cdev->brightness_set(led_cdev, value);
 53
 54	return 0;
 55}
 56
 57static int __led_set_brightness_blocking(struct led_classdev *led_cdev, unsigned int value)
 58{
 59	if (!led_cdev->brightness_set_blocking)
 60		return -ENOTSUPP;
 61
 62	return led_cdev->brightness_set_blocking(led_cdev, value);
 63}
 64
 65static void led_timer_function(struct timer_list *t)
 66{
 67	struct led_classdev *led_cdev = timer_container_of(led_cdev, t,
 68							   blink_timer);
 69	unsigned long brightness;
 70	unsigned long delay;
 71
 72	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
 73		led_set_brightness_nosleep(led_cdev, LED_OFF);
 74		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
 75		return;
 76	}
 77
 78	if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
 79			       &led_cdev->work_flags)) {
 80		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
 81		return;
 82	}
 83
 84	brightness = led_get_brightness(led_cdev);
 85	if (!brightness) {
 86		/* Time to switch the LED on. */
 87		if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
 88					&led_cdev->work_flags))
 89			brightness = led_cdev->new_blink_brightness;
 90		else
 91			brightness = led_cdev->blink_brightness;
 92		delay = led_cdev->blink_delay_on;
 93	} else {
 94		/* Store the current brightness value to be able
 95		 * to restore it when the delay_off period is over.
 96		 */
 97		led_cdev->blink_brightness = brightness;
 98		brightness = LED_OFF;
 99		delay = led_cdev->blink_delay_off;
100	}
101
102	led_set_brightness_nosleep(led_cdev, brightness);
103
104	/* Return in next iteration if led is in one-shot mode and we are in
105	 * the final blink state so that the led is toggled each delay_on +
106	 * delay_off milliseconds in worst case.
107	 */
108	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
109		if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
110			if (brightness)
111				set_bit(LED_BLINK_ONESHOT_STOP,
112					&led_cdev->work_flags);
113		} else {
114			if (!brightness)
115				set_bit(LED_BLINK_ONESHOT_STOP,
116					&led_cdev->work_flags);
117		}
118	}
119
120	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
121}
122
123static void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev,
124						  unsigned int value)
125{
126	int ret;
127
128	ret = __led_set_brightness(led_cdev, value);
129	if (ret == -ENOTSUPP) {
130		ret = __led_set_brightness_blocking(led_cdev, value);
131		if (ret == -ENOTSUPP)
132			/* No back-end support to set a fixed brightness value */
133			return;
134	}
135
136	/* LED HW might have been unplugged, therefore don't warn */
137	if (ret == -ENODEV && led_cdev->flags & LED_UNREGISTERING &&
138	    led_cdev->flags & LED_HW_PLUGGABLE)
139		return;
140
141	if (ret < 0)
142		dev_err(led_cdev->dev,
143			"Setting an LED's brightness failed (%d)\n", ret);
144}
145
146static void set_brightness_delayed(struct work_struct *ws)
147{
148	struct led_classdev *led_cdev =
149		container_of(ws, struct led_classdev, set_brightness_work);
150
151	if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
152		led_stop_software_blink(led_cdev);
153		set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
154	}
155
156	/*
157	 * Triggers may call led_set_brightness(LED_OFF),
158	 * led_set_brightness(LED_FULL) in quick succession to disable blinking
159	 * and turn the LED on. Both actions may have been scheduled to run
160	 * before this work item runs once. To make sure this works properly
161	 * handle LED_SET_BRIGHTNESS_OFF first.
162	 */
163	if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags)) {
164		set_brightness_delayed_set_brightness(led_cdev, LED_OFF);
165		/*
166		 * The consecutives led_set_brightness(LED_OFF),
167		 * led_set_brightness(LED_FULL) could have been executed out of
168		 * order (LED_FULL first), if the work_flags has been set
169		 * between LED_SET_BRIGHTNESS_OFF and LED_SET_BRIGHTNESS of this
170		 * work. To avoid ending with the LED turned off, turn the LED
171		 * on again.
172		 */
173		if (led_cdev->delayed_set_value != LED_OFF)
174			set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
175	}
176
177	if (test_and_clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags))
178		set_brightness_delayed_set_brightness(led_cdev, led_cdev->delayed_set_value);
179
180	if (test_and_clear_bit(LED_SET_BLINK, &led_cdev->work_flags)) {
181		unsigned long delay_on = led_cdev->delayed_delay_on;
182		unsigned long delay_off = led_cdev->delayed_delay_off;
183
184		led_blink_set(led_cdev, &delay_on, &delay_off);
185	}
186}
187
188static void led_set_software_blink(struct led_classdev *led_cdev,
189				   unsigned long delay_on,
190				   unsigned long delay_off)
191{
192	int current_brightness;
193
194	current_brightness = led_get_brightness(led_cdev);
195	if (current_brightness)
196		led_cdev->blink_brightness = current_brightness;
197	if (!led_cdev->blink_brightness)
198		led_cdev->blink_brightness = led_cdev->max_brightness;
199
200	led_cdev->blink_delay_on = delay_on;
201	led_cdev->blink_delay_off = delay_off;
202
203	/* never on - just set to off */
204	if (!delay_on) {
205		led_set_brightness_nosleep(led_cdev, LED_OFF);
206		return;
207	}
208
209	/* never off - just set to brightness */
210	if (!delay_off) {
211		led_set_brightness_nosleep(led_cdev,
212					   led_cdev->blink_brightness);
213		return;
214	}
215
216	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
217	mod_timer(&led_cdev->blink_timer, jiffies + 1);
218}
219
220
221static void led_blink_setup(struct led_classdev *led_cdev,
222		     unsigned long *delay_on,
223		     unsigned long *delay_off)
224{
225	if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
226	    led_cdev->blink_set &&
227	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
228		return;
229
230	/* blink with 1 Hz as default if nothing specified */
231	if (!*delay_on && !*delay_off)
232		*delay_on = *delay_off = 500;
233
234	led_set_software_blink(led_cdev, *delay_on, *delay_off);
235}
236
237void led_init_core(struct led_classdev *led_cdev)
238{
239	INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
240
241	timer_setup(&led_cdev->blink_timer, led_timer_function, 0);
242}
243EXPORT_SYMBOL_GPL(led_init_core);
244
245void led_blink_set(struct led_classdev *led_cdev,
246		   unsigned long *delay_on,
247		   unsigned long *delay_off)
248{
249	timer_delete_sync(&led_cdev->blink_timer);
250
251	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
252	clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
253	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
254
255	led_blink_setup(led_cdev, delay_on, delay_off);
256}
257EXPORT_SYMBOL_GPL(led_blink_set);
258
259void led_blink_set_oneshot(struct led_classdev *led_cdev,
260			   unsigned long *delay_on,
261			   unsigned long *delay_off,
262			   int invert)
263{
264	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
265	     timer_pending(&led_cdev->blink_timer))
266		return;
267
268	set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
269	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
270
271	if (invert)
272		set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
273	else
274		clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
275
276	led_blink_setup(led_cdev, delay_on, delay_off);
277}
278EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
279
280void led_blink_set_nosleep(struct led_classdev *led_cdev, unsigned long delay_on,
281			   unsigned long delay_off)
282{
283	/* If necessary delegate to a work queue task. */
284	if (led_cdev->blink_set && led_cdev->brightness_set_blocking) {
285		led_cdev->delayed_delay_on = delay_on;
286		led_cdev->delayed_delay_off = delay_off;
287		set_bit(LED_SET_BLINK, &led_cdev->work_flags);
288		queue_work(led_cdev->wq, &led_cdev->set_brightness_work);
289		return;
290	}
291
292	led_blink_set(led_cdev, &delay_on, &delay_off);
293}
294EXPORT_SYMBOL_GPL(led_blink_set_nosleep);
295
296void led_stop_software_blink(struct led_classdev *led_cdev)
297{
298	timer_delete_sync(&led_cdev->blink_timer);
299	led_cdev->blink_delay_on = 0;
300	led_cdev->blink_delay_off = 0;
301	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
302}
303EXPORT_SYMBOL_GPL(led_stop_software_blink);
304
305void led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness)
306{
307	/*
308	 * If software blink is active, delay brightness setting
309	 * until the next timer tick.
310	 */
311	if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
312		/*
313		 * If we need to disable soft blinking delegate this to the
314		 * work queue task to avoid problems in case we are called
315		 * from hard irq context.
316		 */
317		if (!brightness) {
318			set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
319			queue_work(led_cdev->wq, &led_cdev->set_brightness_work);
320		} else {
321			set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
322				&led_cdev->work_flags);
323			led_cdev->new_blink_brightness = brightness;
324		}
325		return;
326	}
327
328	led_set_brightness_nosleep(led_cdev, brightness);
329}
330EXPORT_SYMBOL_GPL(led_set_brightness);
331
332void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
333{
334	/* Use brightness_set op if available, it is guaranteed not to sleep */
335	if (!__led_set_brightness(led_cdev, value))
336		return;
337
338	/*
339	 * Brightness setting can sleep, delegate it to a work queue task.
340	 * value 0 / LED_OFF is special, since it also disables hw-blinking
341	 * (sw-blink disable is handled in led_set_brightness()).
342	 * To avoid a hw-blink-disable getting lost when a second brightness
343	 * change is done immediately afterwards (before the work runs),
344	 * it uses a separate work_flag.
345	 */
346	led_cdev->delayed_set_value = value;
347	/* Ensure delayed_set_value is seen before work_flags modification */
348	smp_mb__before_atomic();
349
350	if (value)
351		set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
352	else {
353		clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
354		clear_bit(LED_SET_BLINK, &led_cdev->work_flags);
355		set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
356	}
357
358	queue_work(led_cdev->wq, &led_cdev->set_brightness_work);
359}
360EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
361
362void led_set_brightness_nosleep(struct led_classdev *led_cdev, unsigned int value)
363{
364	led_cdev->brightness = min(value, led_cdev->max_brightness);
365
366	if (led_cdev->flags & LED_SUSPENDED)
367		return;
368
369	led_set_brightness_nopm(led_cdev, led_cdev->brightness);
370}
371EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
372
373int led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value)
374{
375	if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
376		return -EBUSY;
377
378	led_cdev->brightness = min(value, led_cdev->max_brightness);
379
380	if (led_cdev->flags & LED_SUSPENDED)
381		return 0;
382
383	return __led_set_brightness_blocking(led_cdev, led_cdev->brightness);
384}
385EXPORT_SYMBOL_GPL(led_set_brightness_sync);
386
387/*
388 * This is a led-core function because just like led_set_brightness()
389 * it is used in the kernel by e.g. triggers.
390 */
391void led_mc_set_brightness(struct led_classdev *led_cdev,
392			   unsigned int *intensity_value, unsigned int num_colors,
393			   unsigned int brightness)
394{
395	struct led_classdev_mc *mcled_cdev;
396	unsigned int i;
397
398	if (!(led_cdev->flags & LED_MULTI_COLOR)) {
399		dev_err_once(led_cdev->dev, "error not a multi-color LED\n");
400		return;
401	}
402
403	mcled_cdev = lcdev_to_mccdev(led_cdev);
404	if (num_colors != mcled_cdev->num_colors) {
405		dev_err_once(led_cdev->dev, "error num_colors mismatch %u != %u\n",
406			     num_colors, mcled_cdev->num_colors);
407		return;
408	}
409
410	for (i = 0; i < mcled_cdev->num_colors; i++)
411		mcled_cdev->subled_info[i].intensity = intensity_value[i];
412
413	led_set_brightness(led_cdev, brightness);
414}
415EXPORT_SYMBOL_GPL(led_mc_set_brightness);
416
417int led_update_brightness(struct led_classdev *led_cdev)
418{
419	int ret;
420
421	if (led_cdev->brightness_get) {
422		ret = led_cdev->brightness_get(led_cdev);
423		if (ret < 0)
424			return ret;
425
426		led_cdev->brightness = ret;
427	}
428
429	return 0;
430}
431EXPORT_SYMBOL_GPL(led_update_brightness);
432
433u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
434{
435	struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
436	u32 *pattern;
437	int count;
438
439	count = fwnode_property_count_u32(fwnode, "led-pattern");
440	if (count < 0)
441		return NULL;
442
443	pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
444	if (!pattern)
445		return NULL;
446
447	if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
448		kfree(pattern);
449		return NULL;
450	}
451
452	*size = count;
453
454	return pattern;
455}
456EXPORT_SYMBOL_GPL(led_get_default_pattern);
457
458/* Caller must ensure led_cdev->led_access held */
459void led_sysfs_disable(struct led_classdev *led_cdev)
460{
461	lockdep_assert_held(&led_cdev->led_access);
462
463	led_cdev->flags |= LED_SYSFS_DISABLE;
464}
465EXPORT_SYMBOL_GPL(led_sysfs_disable);
466
467/* Caller must ensure led_cdev->led_access held */
468void led_sysfs_enable(struct led_classdev *led_cdev)
469{
470	lockdep_assert_held(&led_cdev->led_access);
471
472	led_cdev->flags &= ~LED_SYSFS_DISABLE;
473}
474EXPORT_SYMBOL_GPL(led_sysfs_enable);
475
476static void led_parse_fwnode_props(struct device *dev,
477				   struct fwnode_handle *fwnode,
478				   struct led_properties *props)
479{
480	int ret;
481
482	if (!fwnode)
483		return;
484
485	if (fwnode_property_present(fwnode, "label")) {
486		ret = fwnode_property_read_string(fwnode, "label", &props->label);
487		if (ret)
488			dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
489		return;
490	}
491
492	if (fwnode_property_present(fwnode, "color")) {
493		ret = fwnode_property_read_u32(fwnode, "color", &props->color);
494		if (ret)
495			dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
496		else if (props->color >= LED_COLOR_ID_MAX)
497			dev_err(dev, "LED color identifier out of range\n");
498		else
499			props->color_present = true;
500	}
501
502
503	if (!fwnode_property_present(fwnode, "function"))
504		return;
505
506	ret = fwnode_property_read_string(fwnode, "function", &props->function);
507	if (ret) {
508		dev_err(dev,
509			"Error parsing 'function' property (%d)\n",
510			ret);
511	}
512
513	if (!fwnode_property_present(fwnode, "function-enumerator"))
514		return;
515
516	ret = fwnode_property_read_u32(fwnode, "function-enumerator",
517				       &props->func_enum);
518	if (ret) {
519		dev_err(dev,
520			"Error parsing 'function-enumerator' property (%d)\n",
521			ret);
522	} else {
523		props->func_enum_present = true;
524	}
525}
526
527int led_compose_name(struct device *dev, struct led_init_data *init_data,
528		     char *led_classdev_name)
529{
530	struct led_properties props = {};
531	struct fwnode_handle *fwnode = init_data->fwnode;
532	const char *devicename = init_data->devicename;
533	int n;
534
535	if (!led_classdev_name)
536		return -EINVAL;
537
538	led_parse_fwnode_props(dev, fwnode, &props);
539
540	if (props.label) {
541		/*
542		 * If init_data.devicename is NULL, then it indicates that
543		 * DT label should be used as-is for LED class device name.
544		 * Otherwise the label is prepended with devicename to compose
545		 * the final LED class device name.
546		 */
547		if (devicename) {
548			n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
549				     devicename, props.label);
550		} else {
551			n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s", props.label);
552		}
553	} else if (props.function || props.color_present) {
554		char tmp_buf[LED_MAX_NAME_SIZE];
555
556		if (props.func_enum_present) {
557			n = snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
558				     props.color_present ? led_colors[props.color] : "",
559				     props.function ?: "", props.func_enum);
560		} else {
561			n = snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
562				     props.color_present ? led_colors[props.color] : "",
563				     props.function ?: "");
564		}
565		if (n >= LED_MAX_NAME_SIZE)
566			return -E2BIG;
567
568		if (init_data->devname_mandatory) {
569			n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
570				     devicename, tmp_buf);
571		} else {
572			n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s", tmp_buf);
573		}
574	} else if (init_data->default_label) {
575		if (!devicename) {
576			dev_err(dev, "Legacy LED naming requires devicename segment");
577			return -EINVAL;
578		}
579		n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
580			     devicename, init_data->default_label);
581	} else if (is_of_node(fwnode)) {
582		n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s",
583			     to_of_node(fwnode)->name);
584	} else
585		return -EINVAL;
586
587	if (n >= LED_MAX_NAME_SIZE)
588		return -E2BIG;
589
590	return 0;
591}
592EXPORT_SYMBOL_GPL(led_compose_name);
593
594const char *led_get_color_name(u8 color_id)
595{
596	if (color_id >= ARRAY_SIZE(led_colors))
597		return NULL;
598
599	return led_colors[color_id];
600}
601EXPORT_SYMBOL_GPL(led_get_color_name);
602
603enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode)
604{
605	const char *state = NULL;
606
607	if (!fwnode_property_read_string(fwnode, "default-state", &state)) {
608		if (!strcmp(state, "keep"))
609			return LEDS_DEFSTATE_KEEP;
610		if (!strcmp(state, "on"))
611			return LEDS_DEFSTATE_ON;
612	}
613
614	return LEDS_DEFSTATE_OFF;
615}
616EXPORT_SYMBOL_GPL(led_init_default_state_get);
~/ Read-only file preview.