aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-led17
-rw-r--r--Documentation/leds/leds-class.txt15
-rw-r--r--drivers/leds/Kconfig9
-rw-r--r--drivers/leds/led-class.c76
-rw-r--r--include/linux/leds.h15
5 files changed, 132 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led
index 491cdeedc195..5f67f7ab277b 100644
--- a/Documentation/ABI/testing/sysfs-class-led
+++ b/Documentation/ABI/testing/sysfs-class-led
@@ -23,6 +23,23 @@ Description:
23 If the LED does not support different brightness levels, this 23 If the LED does not support different brightness levels, this
24 should be 1. 24 should be 1.
25 25
26What: /sys/class/leds/<led>/brightness_hw_changed
27Date: January 2017
28KernelVersion: 4.11
29Description:
30 Last hardware set brightness level for this LED. Some LEDs
31 may be changed autonomously by hardware/firmware. Only LEDs
32 where this happens and the driver can detect this, will have
33 this file.
34
35 This file supports poll() to detect when the hardware changes
36 the brightness.
37
38 Reading this file will return the last brightness level set
39 by the hardware, this may be different from the current
40 brightness. Reading this file when no hw brightness change
41 event has happened will return an ENODATA error.
42
26What: /sys/class/leds/<led>/trigger 43What: /sys/class/leds/<led>/trigger
27Date: March 2006 44Date: March 2006
28KernelVersion: 2.6.17 45KernelVersion: 2.6.17
diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt
index f1f7ec9f5cc5..836cb16d6f09 100644
--- a/Documentation/leds/leds-class.txt
+++ b/Documentation/leds/leds-class.txt
@@ -65,6 +65,21 @@ LED subsystem core exposes following API for setting brightness:
65 blinking, returns -EBUSY if software blink fallback is enabled. 65 blinking, returns -EBUSY if software blink fallback is enabled.
66 66
67 67
68LED registration API
69====================
70
71A driver wanting to register a LED classdev for use by other drivers /
72userspace needs to allocate and fill a led_classdev struct and then call
73[devm_]led_classdev_register. If the non devm version is used the driver
74must call led_classdev_unregister from its remove function before
75free-ing the led_classdev struct.
76
77If the driver can detect hardware initiated brightness changes and thus
78wants to have a brightness_hw_changed attribute then the LED_BRIGHT_HW_CHANGED
79flag must be set in flags before registering. Calling
80led_classdev_notify_brightness_hw_changed on a classdev not registered with
81the LED_BRIGHT_HW_CHANGED flag is a bug and will trigger a WARN_ON.
82
68Hardware accelerated blink of LEDs 83Hardware accelerated blink of LEDs
69================================== 84==================================
70 85
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index c621cbbb5768..275f467956ee 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -29,6 +29,15 @@ config LEDS_CLASS_FLASH
29 for the flash related features of a LED device. It can be built 29 for the flash related features of a LED device. It can be built
30 as a module. 30 as a module.
31 31
32config LEDS_BRIGHTNESS_HW_CHANGED
33 bool "LED Class brightness_hw_changed attribute support"
34 depends on LEDS_CLASS
35 help
36 This option enables support for the brightness_hw_changed attribute
37 for led sysfs class devices under /sys/class/leds.
38
39 See Documentation/ABI/testing/sysfs-class-led for details.
40
32comment "LED drivers" 41comment "LED drivers"
33 42
34config LEDS_88PM860X 43config LEDS_88PM860X
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 326ee6e925a2..f2b0a80a62b4 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -103,6 +103,68 @@ static const struct attribute_group *led_groups[] = {
103 NULL, 103 NULL,
104}; 104};
105 105
106#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
107static ssize_t brightness_hw_changed_show(struct device *dev,
108 struct device_attribute *attr, char *buf)
109{
110 struct led_classdev *led_cdev = dev_get_drvdata(dev);
111
112 if (led_cdev->brightness_hw_changed == -1)
113 return -ENODATA;
114
115 return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
116}
117
118static DEVICE_ATTR_RO(brightness_hw_changed);
119
120static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
121{
122 struct device *dev = led_cdev->dev;
123 int ret;
124
125 ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
126 if (ret) {
127 dev_err(dev, "Error creating brightness_hw_changed\n");
128 return ret;
129 }
130
131 led_cdev->brightness_hw_changed_kn =
132 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
133 if (!led_cdev->brightness_hw_changed_kn) {
134 dev_err(dev, "Error getting brightness_hw_changed kn\n");
135 device_remove_file(dev, &dev_attr_brightness_hw_changed);
136 return -ENXIO;
137 }
138
139 return 0;
140}
141
142static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
143{
144 sysfs_put(led_cdev->brightness_hw_changed_kn);
145 device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
146}
147
148void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev,
149 enum led_brightness brightness)
150{
151 if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
152 return;
153
154 led_cdev->brightness_hw_changed = brightness;
155 sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
156}
157EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
158#else
159static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
160{
161 return 0;
162}
163static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
164{
165}
166#endif
167
106/** 168/**
107 * led_classdev_suspend - suspend an led_classdev. 169 * led_classdev_suspend - suspend an led_classdev.
108 * @led_cdev: the led_classdev to suspend. 170 * @led_cdev: the led_classdev to suspend.
@@ -204,10 +266,21 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
204 dev_warn(parent, "Led %s renamed to %s due to name collision", 266 dev_warn(parent, "Led %s renamed to %s due to name collision",
205 led_cdev->name, dev_name(led_cdev->dev)); 267 led_cdev->name, dev_name(led_cdev->dev));
206 268
269 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
270 ret = led_add_brightness_hw_changed(led_cdev);
271 if (ret) {
272 device_unregister(led_cdev->dev);
273 return ret;
274 }
275 }
276
207 led_cdev->work_flags = 0; 277 led_cdev->work_flags = 0;
208#ifdef CONFIG_LEDS_TRIGGERS 278#ifdef CONFIG_LEDS_TRIGGERS
209 init_rwsem(&led_cdev->trigger_lock); 279 init_rwsem(&led_cdev->trigger_lock);
210#endif 280#endif
281#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
282 led_cdev->brightness_hw_changed = -1;
283#endif
211 mutex_init(&led_cdev->led_access); 284 mutex_init(&led_cdev->led_access);
212 /* add to the list of leds */ 285 /* add to the list of leds */
213 down_write(&leds_list_lock); 286 down_write(&leds_list_lock);
@@ -256,6 +329,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
256 329
257 flush_work(&led_cdev->set_brightness_work); 330 flush_work(&led_cdev->set_brightness_work);
258 331
332 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
333 led_remove_brightness_hw_changed(led_cdev);
334
259 device_unregister(led_cdev->dev); 335 device_unregister(led_cdev->dev);
260 336
261 down_write(&leds_list_lock); 337 down_write(&leds_list_lock);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index bb50d0151e75..38c0bd7ca107 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,6 +13,7 @@
13#define __LINUX_LEDS_H_INCLUDED 13#define __LINUX_LEDS_H_INCLUDED
14 14
15#include <linux/device.h> 15#include <linux/device.h>
16#include <linux/kernfs.h>
16#include <linux/list.h> 17#include <linux/list.h>
17#include <linux/mutex.h> 18#include <linux/mutex.h>
18#include <linux/rwsem.h> 19#include <linux/rwsem.h>
@@ -47,6 +48,7 @@ struct led_classdev {
47#define LED_DEV_CAP_FLASH (1 << 18) 48#define LED_DEV_CAP_FLASH (1 << 18)
48#define LED_HW_PLUGGABLE (1 << 19) 49#define LED_HW_PLUGGABLE (1 << 19)
49#define LED_PANIC_INDICATOR (1 << 20) 50#define LED_PANIC_INDICATOR (1 << 20)
51#define LED_BRIGHT_HW_CHANGED (1 << 21)
50 52
51 /* set_brightness_work / blink_timer flags, atomic, private. */ 53 /* set_brightness_work / blink_timer flags, atomic, private. */
52 unsigned long work_flags; 54 unsigned long work_flags;
@@ -111,6 +113,11 @@ struct led_classdev {
111 bool activated; 113 bool activated;
112#endif 114#endif
113 115
116#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
117 int brightness_hw_changed;
118 struct kernfs_node *brightness_hw_changed_kn;
119#endif
120
114 /* Ensures consistent access to the LED Flash Class device */ 121 /* Ensures consistent access to the LED Flash Class device */
115 struct mutex led_access; 122 struct mutex led_access;
116}; 123};
@@ -423,4 +430,12 @@ static inline void ledtrig_cpu(enum cpu_led_event evt)
423} 430}
424#endif 431#endif
425 432
433#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
434extern void led_classdev_notify_brightness_hw_changed(
435 struct led_classdev *led_cdev, enum led_brightness brightness);
436#else
437static inline void led_classdev_notify_brightness_hw_changed(
438 struct led_classdev *led_cdev, enum led_brightness brightness) { }
439#endif
440
426#endif /* __LINUX_LEDS_H_INCLUDED */ 441#endif /* __LINUX_LEDS_H_INCLUDED */