aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-12-13 11:01:19 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-13 11:01:19 -0500
commit1f0a53f623b675e856554f2bb1d6b630ea78125d (patch)
tree7c0fdaac3a433ec812413bd5c1149aaae66c2595
parent20d5ba4928ceb79b919092c939ae4ef4d88807bd (diff)
parent44b3e31d540e917a4d2292b902ade63fa1748d9a (diff)
Merge tag 'leds_for_4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds
Pull LED updates from Jacek Anaszewski: - userspace LED class driver - it can be useful for testing triggers and can also be used to implement virtual LEDs - LED class driver for NIC78bx device - LED core fixes for preventing potential races while setting brightness when software blinking is enabled - improvements in LED documentation to mention semantics on changing brightness while trigger is active * tag 'leds_for_4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds: leds: pca955x: Add ACPI support leds: netxbig: fix module autoload for OF registration leds: pca963x: Add ACPI support leds: leds-cobalt-raq: use builtin_platform_driver led: core: Fix blink_brightness setting race led: core: Use atomic bit-field for the blink-flags leds: Add user LED driver for NIC78bx device leds: verify vendor and change license in mlxcpld driver leds: pca963x: enable low-power state leds: pca9532: Use default trigger value from platform data leds: pca963x: workaround group blink scaling issue cleanup LED documentation and make it match reality leds: lp3952: Export I2C module alias information for module autoload leds: mc13783: Fix MC13892 keypad led access ledtrig-cpu.c: fix english leds/leds-lp5523.txt: make documentation match reality tools/leds: Add uledmon program for monitoring userspace LEDs leds: Use macro for max device node name size leds: Introduce userspace LED class driver mfd: qcom-pm8xxx: Clean up PM8XXX namespace
-rw-r--r--Documentation/ABI/testing/sysfs-class-led14
-rw-r--r--Documentation/devicetree/bindings/leds/pca963x.txt3
-rw-r--r--Documentation/leds/leds-lp5523.txt4
-rw-r--r--Documentation/leds/uleds.txt36
-rw-r--r--arch/arm/configs/multi_v7_defconfig2
-rw-r--r--arch/arm/configs/pxa_defconfig1
-rw-r--r--arch/arm/configs/qcom_defconfig1
-rw-r--r--drivers/leds/Kconfig21
-rw-r--r--drivers/leds/Makefile4
-rw-r--r--drivers/leds/led-class.c4
-rw-r--r--drivers/leds/led-core.c62
-rw-r--r--drivers/leds/leds-cobalt-raq.c6
-rw-r--r--drivers/leds/leds-lp3952.c1
-rw-r--r--drivers/leds/leds-mc13783.c5
-rw-r--r--drivers/leds/leds-mlxcpld.c5
-rw-r--r--drivers/leds/leds-netxbig.c1
-rw-r--r--drivers/leds/leds-nic78bx.c209
-rw-r--r--drivers/leds/leds-pca9532.c2
-rw-r--r--drivers/leds/leds-pca955x.c24
-rw-r--r--drivers/leds/leds-pca963x.c80
-rw-r--r--drivers/leds/trigger/ledtrig-cpu.c2
-rw-r--r--drivers/leds/uleds.c235
-rw-r--r--drivers/mfd/Kconfig14
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/qcom-pm8xxx.c (renamed from drivers/mfd/pm8921-core.c)42
-rw-r--r--include/linux/leds.h25
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/uleds.h24
-rw-r--r--tools/Makefile7
-rw-r--r--tools/leds/.gitignore1
-rw-r--r--tools/leds/Makefile13
-rw-r--r--tools/leds/uledmon.c63
32 files changed, 807 insertions, 107 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led
index 86ace287d48b..491cdeedc195 100644
--- a/Documentation/ABI/testing/sysfs-class-led
+++ b/Documentation/ABI/testing/sysfs-class-led
@@ -4,16 +4,24 @@ KernelVersion: 2.6.17
4Contact: Richard Purdie <rpurdie@rpsys.net> 4Contact: Richard Purdie <rpurdie@rpsys.net>
5Description: 5Description:
6 Set the brightness of the LED. Most LEDs don't 6 Set the brightness of the LED. Most LEDs don't
7 have hardware brightness support so will just be turned on for 7 have hardware brightness support, so will just be turned on for
8 non-zero brightness settings. The value is between 0 and 8 non-zero brightness settings. The value is between 0 and
9 /sys/class/leds/<led>/max_brightness. 9 /sys/class/leds/<led>/max_brightness.
10 10
11 Writing 0 to this file clears active trigger.
12
13 Writing non-zero to this file while trigger is active changes the
14 top brightness trigger is going to use.
15
11What: /sys/class/leds/<led>/max_brightness 16What: /sys/class/leds/<led>/max_brightness
12Date: March 2006 17Date: March 2006
13KernelVersion: 2.6.17 18KernelVersion: 2.6.17
14Contact: Richard Purdie <rpurdie@rpsys.net> 19Contact: Richard Purdie <rpurdie@rpsys.net>
15Description: 20Description:
16 Maximum brightness level for this led, default is 255 (LED_FULL). 21 Maximum brightness level for this LED, default is 255 (LED_FULL).
22
23 If the LED does not support different brightness levels, this
24 should be 1.
17 25
18What: /sys/class/leds/<led>/trigger 26What: /sys/class/leds/<led>/trigger
19Date: March 2006 27Date: March 2006
@@ -21,7 +29,7 @@ KernelVersion: 2.6.17
21Contact: Richard Purdie <rpurdie@rpsys.net> 29Contact: Richard Purdie <rpurdie@rpsys.net>
22Description: 30Description:
23 Set the trigger for this LED. A trigger is a kernel based source 31 Set the trigger for this LED. A trigger is a kernel based source
24 of led events. 32 of LED events.
25 You can change triggers in a similar manner to the way an IO 33 You can change triggers in a similar manner to the way an IO
26 scheduler is chosen. Trigger specific parameters can appear in 34 scheduler is chosen. Trigger specific parameters can appear in
27 /sys/class/leds/<led> once a given trigger is selected. For 35 /sys/class/leds/<led> once a given trigger is selected. For
diff --git a/Documentation/devicetree/bindings/leds/pca963x.txt b/Documentation/devicetree/bindings/leds/pca963x.txt
index dafbe9931c2b..dfbdb123a9bf 100644
--- a/Documentation/devicetree/bindings/leds/pca963x.txt
+++ b/Documentation/devicetree/bindings/leds/pca963x.txt
@@ -7,6 +7,9 @@ Optional properties:
7- nxp,totem-pole : use totem pole (push-pull) instead of open-drain (pca9632 defaults 7- nxp,totem-pole : use totem pole (push-pull) instead of open-drain (pca9632 defaults
8 to open-drain, newer chips to totem pole) 8 to open-drain, newer chips to totem pole)
9- nxp,hw-blink : use hardware blinking instead of software blinking 9- nxp,hw-blink : use hardware blinking instead of software blinking
10- nxp,period-scale : In some configurations, the chip blinks faster than expected.
11 This parameter provides a scaling ratio (fixed point, decimal divided
12 by 1000) to compensate, e.g. 1300=1.3x and 750=0.75x.
10 13
11Each led is represented as a sub-node of the nxp,pca963x device. 14Each led is represented as a sub-node of the nxp,pca963x device.
12 15
diff --git a/Documentation/leds/leds-lp5523.txt b/Documentation/leds/leds-lp5523.txt
index 0dbbd279c9b9..0961a060fc4d 100644
--- a/Documentation/leds/leds-lp5523.txt
+++ b/Documentation/leds/leds-lp5523.txt
@@ -34,8 +34,8 @@ There are two ways to run LED patterns.
34 Control interface for the engines: 34 Control interface for the engines:
35 x is 1 .. 3 35 x is 1 .. 3
36 enginex_mode : disabled, load, run 36 enginex_mode : disabled, load, run
37 enginex_load : microcode load (visible only in load mode) 37 enginex_load : microcode load
38 enginex_leds : led mux control (visible only in load mode) 38 enginex_leds : led mux control
39 39
40 cd /sys/class/leds/lp5523:channel2/device 40 cd /sys/class/leds/lp5523:channel2/device
41 echo "load" > engine3_mode 41 echo "load" > engine3_mode
diff --git a/Documentation/leds/uleds.txt b/Documentation/leds/uleds.txt
new file mode 100644
index 000000000000..13e375a580f9
--- /dev/null
+++ b/Documentation/leds/uleds.txt
@@ -0,0 +1,36 @@
1Userspace LEDs
2==============
3
4The uleds driver supports userspace LEDs. This can be useful for testing
5triggers and can also be used to implement virtual LEDs.
6
7
8Usage
9=====
10
11When the driver is loaded, a character device is created at /dev/uleds. To
12create a new LED class device, open /dev/uleds and write a uleds_user_dev
13structure to it (found in kernel public header file linux/uleds.h).
14
15 #define LED_MAX_NAME_SIZE 64
16
17 struct uleds_user_dev {
18 char name[LED_MAX_NAME_SIZE];
19 };
20
21A new LED class device will be created with the name given. The name can be
22any valid sysfs device node name, but consider using the LED class naming
23convention of "devicename:color:function".
24
25The current brightness is found by reading a single byte from the character
26device. Values are unsigned: 0 to 255. Reading will block until the brightness
27changes. The device node can also be polled to notify when the brightness value
28changes.
29
30The LED class device will be removed when the open file handle to /dev/uleds
31is closed.
32
33Multiple LED class devices are created by opening additional file handles to
34/dev/uleds.
35
36See tools/leds/uledmon.c for an example userspace program.
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 11f37ed1dbff..4846de4c3357 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -489,7 +489,7 @@ CONFIG_MFD_MAX8907=y
489CONFIG_MFD_MAX8997=y 489CONFIG_MFD_MAX8997=y
490CONFIG_MFD_MAX8998=y 490CONFIG_MFD_MAX8998=y
491CONFIG_MFD_RK808=y 491CONFIG_MFD_RK808=y
492CONFIG_MFD_PM8921_CORE=y 492CONFIG_MFD_PM8XXX=y
493CONFIG_MFD_QCOM_RPM=y 493CONFIG_MFD_QCOM_RPM=y
494CONFIG_MFD_SPMI_PMIC=y 494CONFIG_MFD_SPMI_PMIC=y
495CONFIG_MFD_SEC_CORE=y 495CONFIG_MFD_SEC_CORE=y
diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig
index a016ecc0084b..e4314b1227a3 100644
--- a/arch/arm/configs/pxa_defconfig
+++ b/arch/arm/configs/pxa_defconfig
@@ -411,7 +411,6 @@ CONFIG_MFD_MAX77693=y
411CONFIG_MFD_MAX8907=m 411CONFIG_MFD_MAX8907=m
412CONFIG_EZX_PCAP=y 412CONFIG_EZX_PCAP=y
413CONFIG_UCB1400_CORE=m 413CONFIG_UCB1400_CORE=m
414CONFIG_MFD_PM8921_CORE=m
415CONFIG_MFD_SEC_CORE=y 414CONFIG_MFD_SEC_CORE=y
416CONFIG_MFD_PALMAS=y 415CONFIG_MFD_PALMAS=y
417CONFIG_MFD_TPS65090=y 416CONFIG_MFD_TPS65090=y
diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
index c2dff4fd5fc4..74e9cd759b99 100644
--- a/arch/arm/configs/qcom_defconfig
+++ b/arch/arm/configs/qcom_defconfig
@@ -119,7 +119,6 @@ CONFIG_POWER_RESET=y
119CONFIG_POWER_RESET_MSM=y 119CONFIG_POWER_RESET_MSM=y
120CONFIG_THERMAL=y 120CONFIG_THERMAL=y
121CONFIG_MFD_PM8XXX=y 121CONFIG_MFD_PM8XXX=y
122CONFIG_MFD_PM8921_CORE=y
123CONFIG_MFD_QCOM_RPM=y 122CONFIG_MFD_QCOM_RPM=y
124CONFIG_MFD_SPMI_PMIC=y 123CONFIG_MFD_SPMI_PMIC=y
125CONFIG_REGULATOR=y 124CONFIG_REGULATOR=y
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 7a628c6516f6..c621cbbb5768 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -645,7 +645,7 @@ config LEDS_VERSATILE
645 645
646config LEDS_PM8058 646config LEDS_PM8058
647 tristate "LED Support for the Qualcomm PM8058 PMIC" 647 tristate "LED Support for the Qualcomm PM8058 PMIC"
648 depends on MFD_PM8921_CORE 648 depends on MFD_PM8XXX
649 depends on LEDS_CLASS 649 depends on LEDS_CLASS
650 help 650 help
651 Choose this option if you want to use the LED drivers in 651 Choose this option if you want to use the LED drivers in
@@ -659,6 +659,25 @@ config LEDS_MLXCPLD
659 This option enabled support for the LEDs on the Mellanox 659 This option enabled support for the LEDs on the Mellanox
660 boards. Say Y to enabled these. 660 boards. Say Y to enabled these.
661 661
662config LEDS_USER
663 tristate "Userspace LED support"
664 depends on LEDS_CLASS
665 help
666 This option enables support for userspace LEDs. Say 'y' to enable this
667 support in kernel. To compile this driver as a module, choose 'm' here:
668 the module will be called uleds.
669
670config LEDS_NIC78BX
671 tristate "LED support for NI PXI NIC78bx devices"
672 depends on LEDS_CLASS
673 depends on X86 && ACPI
674 help
675 This option enables support for the User1 and User2 LEDs on NI
676 PXI NIC78bx devices.
677
678 To compile this driver as a module, choose M here: the module
679 will be called leds-nic78bx.
680
662comment "LED Triggers" 681comment "LED Triggers"
663source "drivers/leds/trigger/Kconfig" 682source "drivers/leds/trigger/Kconfig"
664 683
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3965070190f5..6b8273736478 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -71,9 +71,13 @@ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
71obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o 71obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
72obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o 72obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
73obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o 73obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
74obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o
74 75
75# LED SPI Drivers 76# LED SPI Drivers
76obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o 77obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
77 78
79# LED Userspace Drivers
80obj-$(CONFIG_LEDS_USER) += uleds.o
81
78# LED Triggers 82# LED Triggers
79obj-$(CONFIG_LEDS_TRIGGERS) += trigger/ 83obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index aa84e5b37593..326ee6e925a2 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -20,6 +20,7 @@
20#include <linux/slab.h> 20#include <linux/slab.h>
21#include <linux/spinlock.h> 21#include <linux/spinlock.h>
22#include <linux/timer.h> 22#include <linux/timer.h>
23#include <uapi/linux/uleds.h>
23#include "leds.h" 24#include "leds.h"
24 25
25static struct class *leds_class; 26static struct class *leds_class;
@@ -187,7 +188,7 @@ static int led_classdev_next_name(const char *init_name, char *name,
187 */ 188 */
188int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) 189int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
189{ 190{
190 char name[64]; 191 char name[LED_MAX_NAME_SIZE];
191 int ret; 192 int ret;
192 193
193 ret = led_classdev_next_name(led_cdev->name, name, sizeof(name)); 194 ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
@@ -203,6 +204,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
203 dev_warn(parent, "Led %s renamed to %s due to name collision", 204 dev_warn(parent, "Led %s renamed to %s due to name collision",
204 led_cdev->name, dev_name(led_cdev->dev)); 205 led_cdev->name, dev_name(led_cdev->dev));
205 206
207 led_cdev->work_flags = 0;
206#ifdef CONFIG_LEDS_TRIGGERS 208#ifdef CONFIG_LEDS_TRIGGERS
207 init_rwsem(&led_cdev->trigger_lock); 209 init_rwsem(&led_cdev->trigger_lock);
208#endif 210#endif
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 3bce44893021..ef1360445413 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -53,30 +53,30 @@ static void led_timer_function(unsigned long data)
53 53
54 if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { 54 if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
55 led_set_brightness_nosleep(led_cdev, LED_OFF); 55 led_set_brightness_nosleep(led_cdev, LED_OFF);
56 led_cdev->flags &= ~LED_BLINK_SW; 56 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
57 return; 57 return;
58 } 58 }
59 59
60 if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) { 60 if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
61 led_cdev->flags &= ~(LED_BLINK_ONESHOT_STOP | LED_BLINK_SW); 61 &led_cdev->work_flags)) {
62 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
62 return; 63 return;
63 } 64 }
64 65
65 brightness = led_get_brightness(led_cdev); 66 brightness = led_get_brightness(led_cdev);
66 if (!brightness) { 67 if (!brightness) {
67 /* Time to switch the LED on. */ 68 /* Time to switch the LED on. */
68 brightness = led_cdev->blink_brightness; 69 if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
70 &led_cdev->work_flags))
71 brightness = led_cdev->new_blink_brightness;
72 else
73 brightness = led_cdev->blink_brightness;
69 delay = led_cdev->blink_delay_on; 74 delay = led_cdev->blink_delay_on;
70 } else { 75 } else {
71 /* Store the current brightness value to be able 76 /* Store the current brightness value to be able
72 * to restore it when the delay_off period is over. 77 * to restore it when the delay_off period is over.
73 * Do it only if there is no pending blink brightness
74 * change, to avoid overwriting the new value.
75 */ 78 */
76 if (!(led_cdev->flags & LED_BLINK_BRIGHTNESS_CHANGE)) 79 led_cdev->blink_brightness = brightness;
77 led_cdev->blink_brightness = brightness;
78 else
79 led_cdev->flags &= ~LED_BLINK_BRIGHTNESS_CHANGE;
80 brightness = LED_OFF; 80 brightness = LED_OFF;
81 delay = led_cdev->blink_delay_off; 81 delay = led_cdev->blink_delay_off;
82 } 82 }
@@ -87,13 +87,15 @@ static void led_timer_function(unsigned long data)
87 * the final blink state so that the led is toggled each delay_on + 87 * the final blink state so that the led is toggled each delay_on +
88 * delay_off milliseconds in worst case. 88 * delay_off milliseconds in worst case.
89 */ 89 */
90 if (led_cdev->flags & LED_BLINK_ONESHOT) { 90 if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
91 if (led_cdev->flags & LED_BLINK_INVERT) { 91 if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
92 if (brightness) 92 if (brightness)
93 led_cdev->flags |= LED_BLINK_ONESHOT_STOP; 93 set_bit(LED_BLINK_ONESHOT_STOP,
94 &led_cdev->work_flags);
94 } else { 95 } else {
95 if (!brightness) 96 if (!brightness)
96 led_cdev->flags |= LED_BLINK_ONESHOT_STOP; 97 set_bit(LED_BLINK_ONESHOT_STOP,
98 &led_cdev->work_flags);
97 } 99 }
98 } 100 }
99 101
@@ -106,10 +108,9 @@ static void set_brightness_delayed(struct work_struct *ws)
106 container_of(ws, struct led_classdev, set_brightness_work); 108 container_of(ws, struct led_classdev, set_brightness_work);
107 int ret = 0; 109 int ret = 0;
108 110
109 if (led_cdev->flags & LED_BLINK_DISABLE) { 111 if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
110 led_cdev->delayed_set_value = LED_OFF; 112 led_cdev->delayed_set_value = LED_OFF;
111 led_stop_software_blink(led_cdev); 113 led_stop_software_blink(led_cdev);
112 led_cdev->flags &= ~LED_BLINK_DISABLE;
113 } 114 }
114 115
115 ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value); 116 ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
@@ -152,7 +153,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
152 return; 153 return;
153 } 154 }
154 155
155 led_cdev->flags |= LED_BLINK_SW; 156 set_bit(LED_BLINK_SW, &led_cdev->work_flags);
156 mod_timer(&led_cdev->blink_timer, jiffies + 1); 157 mod_timer(&led_cdev->blink_timer, jiffies + 1);
157} 158}
158 159
@@ -161,7 +162,7 @@ static void led_blink_setup(struct led_classdev *led_cdev,
161 unsigned long *delay_on, 162 unsigned long *delay_on,
162 unsigned long *delay_off) 163 unsigned long *delay_off)
163{ 164{
164 if (!(led_cdev->flags & LED_BLINK_ONESHOT) && 165 if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
165 led_cdev->blink_set && 166 led_cdev->blink_set &&
166 !led_cdev->blink_set(led_cdev, delay_on, delay_off)) 167 !led_cdev->blink_set(led_cdev, delay_on, delay_off))
167 return; 168 return;
@@ -188,8 +189,8 @@ void led_blink_set(struct led_classdev *led_cdev,
188{ 189{
189 del_timer_sync(&led_cdev->blink_timer); 190 del_timer_sync(&led_cdev->blink_timer);
190 191
191 led_cdev->flags &= ~LED_BLINK_ONESHOT; 192 clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
192 led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; 193 clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
193 194
194 led_blink_setup(led_cdev, delay_on, delay_off); 195 led_blink_setup(led_cdev, delay_on, delay_off);
195} 196}
@@ -200,17 +201,17 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
200 unsigned long *delay_off, 201 unsigned long *delay_off,
201 int invert) 202 int invert)
202{ 203{
203 if ((led_cdev->flags & LED_BLINK_ONESHOT) && 204 if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
204 timer_pending(&led_cdev->blink_timer)) 205 timer_pending(&led_cdev->blink_timer))
205 return; 206 return;
206 207
207 led_cdev->flags |= LED_BLINK_ONESHOT; 208 set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
208 led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; 209 clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
209 210
210 if (invert) 211 if (invert)
211 led_cdev->flags |= LED_BLINK_INVERT; 212 set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
212 else 213 else
213 led_cdev->flags &= ~LED_BLINK_INVERT; 214 clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
214 215
215 led_blink_setup(led_cdev, delay_on, delay_off); 216 led_blink_setup(led_cdev, delay_on, delay_off);
216} 217}
@@ -221,7 +222,7 @@ void led_stop_software_blink(struct led_classdev *led_cdev)
221 del_timer_sync(&led_cdev->blink_timer); 222 del_timer_sync(&led_cdev->blink_timer);
222 led_cdev->blink_delay_on = 0; 223 led_cdev->blink_delay_on = 0;
223 led_cdev->blink_delay_off = 0; 224 led_cdev->blink_delay_off = 0;
224 led_cdev->flags &= ~LED_BLINK_SW; 225 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
225} 226}
226EXPORT_SYMBOL_GPL(led_stop_software_blink); 227EXPORT_SYMBOL_GPL(led_stop_software_blink);
227 228
@@ -232,18 +233,19 @@ void led_set_brightness(struct led_classdev *led_cdev,
232 * If software blink is active, delay brightness setting 233 * If software blink is active, delay brightness setting
233 * until the next timer tick. 234 * until the next timer tick.
234 */ 235 */
235 if (led_cdev->flags & LED_BLINK_SW) { 236 if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
236 /* 237 /*
237 * If we need to disable soft blinking delegate this to the 238 * If we need to disable soft blinking delegate this to the
238 * work queue task to avoid problems in case we are called 239 * work queue task to avoid problems in case we are called
239 * from hard irq context. 240 * from hard irq context.
240 */ 241 */
241 if (brightness == LED_OFF) { 242 if (brightness == LED_OFF) {
242 led_cdev->flags |= LED_BLINK_DISABLE; 243 set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
243 schedule_work(&led_cdev->set_brightness_work); 244 schedule_work(&led_cdev->set_brightness_work);
244 } else { 245 } else {
245 led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE; 246 set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
246 led_cdev->blink_brightness = brightness; 247 &led_cdev->work_flags);
248 led_cdev->new_blink_brightness = brightness;
247 } 249 }
248 return; 250 return;
249 } 251 }
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c
index b316df4a8c1e..8d066facdc73 100644
--- a/drivers/leds/leds-cobalt-raq.c
+++ b/drivers/leds/leds-cobalt-raq.c
@@ -115,8 +115,4 @@ static struct platform_driver cobalt_raq_led_driver = {
115 }, 115 },
116}; 116};
117 117
118static int __init cobalt_raq_led_init(void) 118builtin_platform_driver(cobalt_raq_led_driver);
119{
120 return platform_driver_register(&cobalt_raq_led_driver);
121}
122device_initcall(cobalt_raq_led_init);
diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c
index a73c8ff08530..4847e89883a7 100644
--- a/drivers/leds/leds-lp3952.c
+++ b/drivers/leds/leds-lp3952.c
@@ -274,6 +274,7 @@ static const struct i2c_device_id lp3952_id[] = {
274 {LP3952_NAME, 0}, 274 {LP3952_NAME, 0},
275 {} 275 {}
276}; 276};
277MODULE_DEVICE_TABLE(i2c, lp3952_id);
277 278
278#ifdef CONFIG_ACPI 279#ifdef CONFIG_ACPI
279static const struct acpi_device_id lp3952_acpi_match[] = { 280static const struct acpi_device_id lp3952_acpi_match[] = {
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index a2e4c1792e17..2421cf104991 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -84,8 +84,9 @@ static int mc13xxx_led_set(struct led_classdev *led_cdev,
84 case MC13892_LED_MD: 84 case MC13892_LED_MD:
85 case MC13892_LED_AD: 85 case MC13892_LED_AD:
86 case MC13892_LED_KP: 86 case MC13892_LED_KP:
87 reg = (led->id - MC13892_LED_MD) / 2; 87 off = led->id - MC13892_LED_MD;
88 shift = 3 + (led->id - MC13892_LED_MD) * 12; 88 reg = off / 2;
89 shift = 3 + (off - reg * 2) * 12;
89 break; 90 break;
90 case MC13892_LED_R: 91 case MC13892_LED_R:
91 case MC13892_LED_G: 92 case MC13892_LED_G:
diff --git a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c
index 197ab9b29a9c..281482e1d50f 100644
--- a/drivers/leds/leds-mlxcpld.c
+++ b/drivers/leds/leds-mlxcpld.c
@@ -400,6 +400,9 @@ static int __init mlxcpld_led_init(void)
400 struct platform_device *pdev; 400 struct platform_device *pdev;
401 int err; 401 int err;
402 402
403 if (!dmi_match(DMI_CHASSIS_VENDOR, "Mellanox Technologies Ltd."))
404 return -ENODEV;
405
403 pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); 406 pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
404 if (IS_ERR(pdev)) { 407 if (IS_ERR(pdev)) {
405 pr_err("Device allocation failed\n"); 408 pr_err("Device allocation failed\n");
@@ -426,5 +429,5 @@ module_exit(mlxcpld_led_exit);
426 429
427MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 430MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
428MODULE_DESCRIPTION("Mellanox board LED driver"); 431MODULE_DESCRIPTION("Mellanox board LED driver");
429MODULE_LICENSE("GPL v2"); 432MODULE_LICENSE("Dual BSD/GPL");
430MODULE_ALIAS("platform:leds_mlxcpld"); 433MODULE_ALIAS("platform:leds_mlxcpld");
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c
index 4b88b93244be..f48b1aed9b4e 100644
--- a/drivers/leds/leds-netxbig.c
+++ b/drivers/leds/leds-netxbig.c
@@ -534,6 +534,7 @@ static const struct of_device_id of_netxbig_leds_match[] = {
534 { .compatible = "lacie,netxbig-leds", }, 534 { .compatible = "lacie,netxbig-leds", },
535 {}, 535 {},
536}; 536};
537MODULE_DEVICE_TABLE(of, of_netxbig_leds_match);
537#else 538#else
538static inline int 539static inline int
539netxbig_leds_get_of_pdata(struct device *dev, 540netxbig_leds_get_of_pdata(struct device *dev,
diff --git a/drivers/leds/leds-nic78bx.c b/drivers/leds/leds-nic78bx.c
new file mode 100644
index 000000000000..8d69e2b74a27
--- /dev/null
+++ b/drivers/leds/leds-nic78bx.c
@@ -0,0 +1,209 @@
1/*
2 * Copyright (C) 2016 National Instruments Corp.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/acpi.h>
16#include <linux/leds.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/spinlock.h>
20
21#define NIC78BX_USER1_LED_MASK 0x3
22#define NIC78BX_USER1_GREEN_LED BIT(0)
23#define NIC78BX_USER1_YELLOW_LED BIT(1)
24
25#define NIC78BX_USER2_LED_MASK 0xC
26#define NIC78BX_USER2_GREEN_LED BIT(2)
27#define NIC78BX_USER2_YELLOW_LED BIT(3)
28
29#define NIC78BX_LOCK_REG_OFFSET 1
30#define NIC78BX_LOCK_VALUE 0xA5
31#define NIC78BX_UNLOCK_VALUE 0x5A
32
33#define NIC78BX_USER_LED_IO_SIZE 2
34
35struct nic78bx_led_data {
36 u16 io_base;
37 spinlock_t lock;
38 struct platform_device *pdev;
39};
40
41struct nic78bx_led {
42 u8 bit;
43 u8 mask;
44 struct nic78bx_led_data *data;
45 struct led_classdev cdev;
46};
47
48static inline struct nic78bx_led *to_nic78bx_led(struct led_classdev *cdev)
49{
50 return container_of(cdev, struct nic78bx_led, cdev);
51}
52
53static void nic78bx_brightness_set(struct led_classdev *cdev,
54 enum led_brightness brightness)
55{
56 struct nic78bx_led *nled = to_nic78bx_led(cdev);
57 unsigned long flags;
58 u8 value;
59
60 spin_lock_irqsave(&nled->data->lock, flags);
61 value = inb(nled->data->io_base);
62
63 if (brightness) {
64 value &= ~nled->mask;
65 value |= nled->bit;
66 } else {
67 value &= ~nled->bit;
68 }
69
70 outb(value, nled->data->io_base);
71 spin_unlock_irqrestore(&nled->data->lock, flags);
72}
73
74static enum led_brightness nic78bx_brightness_get(struct led_classdev *cdev)
75{
76 struct nic78bx_led *nled = to_nic78bx_led(cdev);
77 unsigned long flags;
78 u8 value;
79
80 spin_lock_irqsave(&nled->data->lock, flags);
81 value = inb(nled->data->io_base);
82 spin_unlock_irqrestore(&nled->data->lock, flags);
83
84 return (value & nled->bit) ? 1 : LED_OFF;
85}
86
87static struct nic78bx_led nic78bx_leds[] = {
88 {
89 .bit = NIC78BX_USER1_GREEN_LED,
90 .mask = NIC78BX_USER1_LED_MASK,
91 .cdev = {
92 .name = "nilrt:green:user1",
93 .max_brightness = 1,
94 .brightness_set = nic78bx_brightness_set,
95 .brightness_get = nic78bx_brightness_get,
96 }
97 },
98 {
99 .bit = NIC78BX_USER1_YELLOW_LED,
100 .mask = NIC78BX_USER1_LED_MASK,
101 .cdev = {
102 .name = "nilrt:yellow:user1",
103 .max_brightness = 1,
104 .brightness_set = nic78bx_brightness_set,
105 .brightness_get = nic78bx_brightness_get,
106 }
107 },
108 {
109 .bit = NIC78BX_USER2_GREEN_LED,
110 .mask = NIC78BX_USER2_LED_MASK,
111 .cdev = {
112 .name = "nilrt:green:user2",
113 .max_brightness = 1,
114 .brightness_set = nic78bx_brightness_set,
115 .brightness_get = nic78bx_brightness_get,
116 }
117 },
118 {
119 .bit = NIC78BX_USER2_YELLOW_LED,
120 .mask = NIC78BX_USER2_LED_MASK,
121 .cdev = {
122 .name = "nilrt:yellow:user2",
123 .max_brightness = 1,
124 .brightness_set = nic78bx_brightness_set,
125 .brightness_get = nic78bx_brightness_get,
126 }
127 }
128};
129
130static int nic78bx_probe(struct platform_device *pdev)
131{
132 struct device *dev = &pdev->dev;
133 struct nic78bx_led_data *led_data;
134 struct resource *io_rc;
135 int ret, i;
136
137 led_data = devm_kzalloc(dev, sizeof(*led_data), GFP_KERNEL);
138 if (!led_data)
139 return -ENOMEM;
140
141 led_data->pdev = pdev;
142 platform_set_drvdata(pdev, led_data);
143
144 io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
145 if (!io_rc) {
146 dev_err(dev, "missing IO resources\n");
147 return -EINVAL;
148 }
149
150 if (resource_size(io_rc) < NIC78BX_USER_LED_IO_SIZE) {
151 dev_err(dev, "IO region too small\n");
152 return -EINVAL;
153 }
154
155 if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),
156 KBUILD_MODNAME)) {
157 dev_err(dev, "failed to get IO region\n");
158 return -EBUSY;
159 }
160
161 led_data->io_base = io_rc->start;
162 spin_lock_init(&led_data->lock);
163
164 for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) {
165 nic78bx_leds[i].data = led_data;
166
167 ret = devm_led_classdev_register(dev, &nic78bx_leds[i].cdev);
168 if (ret)
169 return ret;
170 }
171
172 /* Unlock LED register */
173 outb(NIC78BX_UNLOCK_VALUE,
174 led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
175
176 return ret;
177}
178
179static int nic78bx_remove(struct platform_device *pdev)
180{
181 struct nic78bx_led_data *led_data = platform_get_drvdata(pdev);
182
183 /* Lock LED register */
184 outb(NIC78BX_LOCK_VALUE,
185 led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
186
187 return 0;
188}
189
190static const struct acpi_device_id led_device_ids[] = {
191 {"NIC78B3", 0},
192 {"", 0},
193};
194MODULE_DEVICE_TABLE(acpi, led_device_ids);
195
196static struct platform_driver led_driver = {
197 .probe = nic78bx_probe,
198 .remove = nic78bx_remove,
199 .driver = {
200 .name = KBUILD_MODNAME,
201 .acpi_match_table = ACPI_PTR(led_device_ids),
202 },
203};
204
205module_platform_driver(led_driver);
206
207MODULE_DESCRIPTION("National Instruments PXI User LEDs driver");
208MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
209MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 09a7cffbc46f..06e63106ae1e 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -369,7 +369,7 @@ static int pca9532_configure(struct i2c_client *client,
369 led->state = pled->state; 369 led->state = pled->state;
370 led->name = pled->name; 370 led->name = pled->name;
371 led->ldev.name = led->name; 371 led->ldev.name = led->name;
372 led->ldev.default_trigger = led->default_trigger; 372 led->ldev.default_trigger = pled->default_trigger;
373 led->ldev.brightness = LED_OFF; 373 led->ldev.brightness = LED_OFF;
374 led->ldev.brightness_set_blocking = 374 led->ldev.brightness_set_blocking =
375 pca9532_set_brightness; 375 pca9532_set_brightness;
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 840401ae9a4e..78a7ce816a47 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -40,6 +40,7 @@
40 * bits the chip supports. 40 * bits the chip supports.
41 */ 41 */
42 42
43#include <linux/acpi.h>
43#include <linux/module.h> 44#include <linux/module.h>
44#include <linux/delay.h> 45#include <linux/delay.h>
45#include <linux/string.h> 46#include <linux/string.h>
@@ -100,6 +101,15 @@ static const struct i2c_device_id pca955x_id[] = {
100}; 101};
101MODULE_DEVICE_TABLE(i2c, pca955x_id); 102MODULE_DEVICE_TABLE(i2c, pca955x_id);
102 103
104static const struct acpi_device_id pca955x_acpi_ids[] = {
105 { "PCA9550", pca9550 },
106 { "PCA9551", pca9551 },
107 { "PCA9552", pca9552 },
108 { "PCA9553", pca9553 },
109 { }
110};
111MODULE_DEVICE_TABLE(acpi, pca955x_acpi_ids);
112
103struct pca955x { 113struct pca955x {
104 struct mutex lock; 114 struct mutex lock;
105 struct pca955x_led *leds; 115 struct pca955x_led *leds;
@@ -250,7 +260,16 @@ static int pca955x_probe(struct i2c_client *client,
250 struct led_platform_data *pdata; 260 struct led_platform_data *pdata;
251 int i, err; 261 int i, err;
252 262
253 chip = &pca955x_chipdefs[id->driver_data]; 263 if (id) {
264 chip = &pca955x_chipdefs[id->driver_data];
265 } else {
266 const struct acpi_device_id *acpi_id;
267
268 acpi_id = acpi_match_device(pca955x_acpi_ids, &client->dev);
269 if (!acpi_id)
270 return -ENODEV;
271 chip = &pca955x_chipdefs[acpi_id->driver_data];
272 }
254 adapter = to_i2c_adapter(client->dev.parent); 273 adapter = to_i2c_adapter(client->dev.parent);
255 pdata = dev_get_platdata(&client->dev); 274 pdata = dev_get_platdata(&client->dev);
256 275
@@ -264,7 +283,7 @@ static int pca955x_probe(struct i2c_client *client,
264 283
265 dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " 284 dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at "
266 "slave address 0x%02x\n", 285 "slave address 0x%02x\n",
267 id->name, chip->bits, client->addr); 286 client->name, chip->bits, client->addr);
268 287
269 if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) 288 if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
270 return -EIO; 289 return -EIO;
@@ -358,6 +377,7 @@ static int pca955x_remove(struct i2c_client *client)
358static struct i2c_driver pca955x_driver = { 377static struct i2c_driver pca955x_driver = {
359 .driver = { 378 .driver = {
360 .name = "leds-pca955x", 379 .name = "leds-pca955x",
380 .acpi_match_table = ACPI_PTR(pca955x_acpi_ids),
361 }, 381 },
362 .probe = pca955x_probe, 382 .probe = pca955x_probe,
363 .remove = pca955x_remove, 383 .remove = pca955x_remove,
diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c
index 407eba11e187..ded1e4dac36a 100644
--- a/drivers/leds/leds-pca963x.c
+++ b/drivers/leds/leds-pca963x.c
@@ -25,6 +25,7 @@
25 * or by adding the 'nxp,hw-blink' property to the DTS. 25 * or by adding the 'nxp,hw-blink' property to the DTS.
26 */ 26 */
27 27
28#include <linux/acpi.h>
28#include <linux/module.h> 29#include <linux/module.h>
29#include <linux/delay.h> 30#include <linux/delay.h>
30#include <linux/string.h> 31#include <linux/string.h>
@@ -59,6 +60,7 @@ struct pca963x_chipdef {
59 u8 grpfreq; 60 u8 grpfreq;
60 u8 ledout_base; 61 u8 ledout_base;
61 int n_leds; 62 int n_leds;
63 unsigned int scaling;
62}; 64};
63 65
64static struct pca963x_chipdef pca963x_chipdefs[] = { 66static struct pca963x_chipdef pca963x_chipdefs[] = {
@@ -95,6 +97,15 @@ static const struct i2c_device_id pca963x_id[] = {
95}; 97};
96MODULE_DEVICE_TABLE(i2c, pca963x_id); 98MODULE_DEVICE_TABLE(i2c, pca963x_id);
97 99
100static const struct acpi_device_id pca963x_acpi_ids[] = {
101 { "PCA9632", pca9633 },
102 { "PCA9633", pca9633 },
103 { "PCA9634", pca9634 },
104 { "PCA9635", pca9635 },
105 { }
106};
107MODULE_DEVICE_TABLE(acpi, pca963x_acpi_ids);
108
98struct pca963x_led; 109struct pca963x_led;
99 110
100struct pca963x { 111struct pca963x {
@@ -102,6 +113,7 @@ struct pca963x {
102 struct mutex mutex; 113 struct mutex mutex;
103 struct i2c_client *client; 114 struct i2c_client *client;
104 struct pca963x_led *leds; 115 struct pca963x_led *leds;
116 unsigned long leds_on;
105}; 117};
106 118
107struct pca963x_led { 119struct pca963x_led {
@@ -123,7 +135,6 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
123 u8 mask = 0x3 << shift; 135 u8 mask = 0x3 << shift;
124 int ret; 136 int ret;
125 137
126 mutex_lock(&pca963x->chip->mutex);
127 ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); 138 ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
128 switch (brightness) { 139 switch (brightness) {
129 case LED_FULL: 140 case LED_FULL:
@@ -140,14 +151,13 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
140 PCA963X_PWM_BASE + pca963x->led_num, 151 PCA963X_PWM_BASE + pca963x->led_num,
141 brightness); 152 brightness);
142 if (ret < 0) 153 if (ret < 0)
143 goto unlock; 154 return ret;
144 ret = i2c_smbus_write_byte_data(pca963x->chip->client, 155 ret = i2c_smbus_write_byte_data(pca963x->chip->client,
145 ledout_addr, 156 ledout_addr,
146 (ledout & ~mask) | (PCA963X_LED_PWM << shift)); 157 (ledout & ~mask) | (PCA963X_LED_PWM << shift));
147 break; 158 break;
148 } 159 }
149unlock: 160
150 mutex_unlock(&pca963x->chip->mutex);
151 return ret; 161 return ret;
152} 162}
153 163
@@ -179,14 +189,49 @@ static void pca963x_blink(struct pca963x_led *pca963x)
179 mutex_unlock(&pca963x->chip->mutex); 189 mutex_unlock(&pca963x->chip->mutex);
180} 190}
181 191
192static int pca963x_power_state(struct pca963x_led *pca963x)
193{
194 unsigned long *leds_on = &pca963x->chip->leds_on;
195 unsigned long cached_leds = pca963x->chip->leds_on;
196
197 if (pca963x->led_cdev.brightness)
198 set_bit(pca963x->led_num, leds_on);
199 else
200 clear_bit(pca963x->led_num, leds_on);
201
202 if (!(*leds_on) != !cached_leds)
203 return i2c_smbus_write_byte_data(pca963x->chip->client,
204 PCA963X_MODE1, *leds_on ? 0 : BIT(4));
205
206 return 0;
207}
208
182static int pca963x_led_set(struct led_classdev *led_cdev, 209static int pca963x_led_set(struct led_classdev *led_cdev,
183 enum led_brightness value) 210 enum led_brightness value)
184{ 211{
185 struct pca963x_led *pca963x; 212 struct pca963x_led *pca963x;
213 int ret;
186 214
187 pca963x = container_of(led_cdev, struct pca963x_led, led_cdev); 215 pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
188 216
189 return pca963x_brightness(pca963x, value); 217 mutex_lock(&pca963x->chip->mutex);
218
219 ret = pca963x_brightness(pca963x, value);
220 if (ret < 0)
221 goto unlock;
222 ret = pca963x_power_state(pca963x);
223
224unlock:
225 mutex_unlock(&pca963x->chip->mutex);
226 return ret;
227}
228
229static unsigned int pca963x_period_scale(struct pca963x_led *pca963x,
230 unsigned int val)
231{
232 unsigned int scaling = pca963x->chip->chipdef->scaling;
233
234 return scaling ? DIV_ROUND_CLOSEST(val * scaling, 1000) : val;
190} 235}
191 236
192static int pca963x_blink_set(struct led_classdev *led_cdev, 237static int pca963x_blink_set(struct led_classdev *led_cdev,
@@ -207,14 +252,14 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
207 time_off = 500; 252 time_off = 500;
208 } 253 }
209 254
210 period = time_on + time_off; 255 period = pca963x_period_scale(pca963x, time_on + time_off);
211 256
212 /* If period not supported by hardware, default to someting sane. */ 257 /* If period not supported by hardware, default to someting sane. */
213 if ((period < PCA963X_BLINK_PERIOD_MIN) || 258 if ((period < PCA963X_BLINK_PERIOD_MIN) ||
214 (period > PCA963X_BLINK_PERIOD_MAX)) { 259 (period > PCA963X_BLINK_PERIOD_MAX)) {
215 time_on = 500; 260 time_on = 500;
216 time_off = 500; 261 time_off = 500;
217 period = time_on + time_off; 262 period = pca963x_period_scale(pca963x, 1000);
218 } 263 }
219 264
220 /* 265 /*
@@ -222,7 +267,7 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
222 * (time_on / period) = (GDC / 256) -> 267 * (time_on / period) = (GDC / 256) ->
223 * GDC = ((time_on * 256) / period) 268 * GDC = ((time_on * 256) / period)
224 */ 269 */
225 gdc = (time_on * 256) / period; 270 gdc = (pca963x_period_scale(pca963x, time_on) * 256) / period;
226 271
227 /* 272 /*
228 * From manual: period = ((GFRQ + 1) / 24) in seconds. 273 * From manual: period = ((GFRQ + 1) / 24) in seconds.
@@ -294,6 +339,9 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
294 else 339 else
295 pdata->blink_type = PCA963X_SW_BLINK; 340 pdata->blink_type = PCA963X_SW_BLINK;
296 341
342 if (of_property_read_u32(np, "nxp,period-scale", &chip->scaling))
343 chip->scaling = 1000;
344
297 return pdata; 345 return pdata;
298} 346}
299 347
@@ -322,7 +370,16 @@ static int pca963x_probe(struct i2c_client *client,
322 struct pca963x_chipdef *chip; 370 struct pca963x_chipdef *chip;
323 int i, err; 371 int i, err;
324 372
325 chip = &pca963x_chipdefs[id->driver_data]; 373 if (id) {
374 chip = &pca963x_chipdefs[id->driver_data];
375 } else {
376 const struct acpi_device_id *acpi_id;
377
378 acpi_id = acpi_match_device(pca963x_acpi_ids, &client->dev);
379 if (!acpi_id)
380 return -ENODEV;
381 chip = &pca963x_chipdefs[acpi_id->driver_data];
382 }
326 pdata = dev_get_platdata(&client->dev); 383 pdata = dev_get_platdata(&client->dev);
327 384
328 if (!pdata) { 385 if (!pdata) {
@@ -391,8 +448,8 @@ static int pca963x_probe(struct i2c_client *client,
391 goto exit; 448 goto exit;
392 } 449 }
393 450
394 /* Disable LED all-call address and set normal mode */ 451 /* Disable LED all-call address, and power down initially */
395 i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00); 452 i2c_smbus_write_byte_data(client, PCA963X_MODE1, BIT(4));
396 453
397 if (pdata) { 454 if (pdata) {
398 /* Configure output: open-drain or totem pole (push-pull) */ 455 /* Configure output: open-drain or totem pole (push-pull) */
@@ -426,6 +483,7 @@ static struct i2c_driver pca963x_driver = {
426 .driver = { 483 .driver = {
427 .name = "leds-pca963x", 484 .name = "leds-pca963x",
428 .of_match_table = of_match_ptr(of_pca963x_match), 485 .of_match_table = of_match_ptr(of_pca963x_match),
486 .acpi_match_table = ACPI_PTR(pca963x_acpi_ids),
429 }, 487 },
430 .probe = pca963x_probe, 488 .probe = pca963x_probe,
431 .remove = pca963x_remove, 489 .remove = pca963x_remove,
diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c
index 22f0634dd3fa..9719caf7437c 100644
--- a/drivers/leds/trigger/ledtrig-cpu.c
+++ b/drivers/leds/trigger/ledtrig-cpu.c
@@ -42,7 +42,7 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
42 * @evt: CPU event to be emitted 42 * @evt: CPU event to be emitted
43 * 43 *
44 * Emit a CPU event on a CPU core, which will trigger a 44 * Emit a CPU event on a CPU core, which will trigger a
45 * binded LED to turn on or turn off. 45 * bound LED to turn on or turn off.
46 */ 46 */
47void ledtrig_cpu(enum cpu_led_event ledevt) 47void ledtrig_cpu(enum cpu_led_event ledevt)
48{ 48{
diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c
new file mode 100644
index 000000000000..5e9e8a1fdefb
--- /dev/null
+++ b/drivers/leds/uleds.c
@@ -0,0 +1,235 @@
1/*
2 * Userspace driver for the LED subsystem
3 *
4 * Copyright (C) 2016 David Lechner <david@lechnology.com>
5 *
6 * Based on uinput.c: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18#include <linux/fs.h>
19#include <linux/init.h>
20#include <linux/leds.h>
21#include <linux/miscdevice.h>
22#include <linux/module.h>
23#include <linux/poll.h>
24#include <linux/sched.h>
25#include <linux/slab.h>
26
27#include <uapi/linux/uleds.h>
28
29#define ULEDS_NAME "uleds"
30
31enum uleds_state {
32 ULEDS_STATE_UNKNOWN,
33 ULEDS_STATE_REGISTERED,
34};
35
36struct uleds_device {
37 struct uleds_user_dev user_dev;
38 struct led_classdev led_cdev;
39 struct mutex mutex;
40 enum uleds_state state;
41 wait_queue_head_t waitq;
42 int brightness;
43 bool new_data;
44};
45
46static struct miscdevice uleds_misc;
47
48static void uleds_brightness_set(struct led_classdev *led_cdev,
49 enum led_brightness brightness)
50{
51 struct uleds_device *udev = container_of(led_cdev, struct uleds_device,
52 led_cdev);
53
54 if (udev->brightness != brightness) {
55 udev->brightness = brightness;
56 udev->new_data = true;
57 wake_up_interruptible(&udev->waitq);
58 }
59}
60
61static int uleds_open(struct inode *inode, struct file *file)
62{
63 struct uleds_device *udev;
64
65 udev = kzalloc(sizeof(*udev), GFP_KERNEL);
66 if (!udev)
67 return -ENOMEM;
68
69 udev->led_cdev.name = udev->user_dev.name;
70 udev->led_cdev.brightness_set = uleds_brightness_set;
71
72 mutex_init(&udev->mutex);
73 init_waitqueue_head(&udev->waitq);
74 udev->state = ULEDS_STATE_UNKNOWN;
75
76 file->private_data = udev;
77 nonseekable_open(inode, file);
78
79 return 0;
80}
81
82static ssize_t uleds_write(struct file *file, const char __user *buffer,
83 size_t count, loff_t *ppos)
84{
85 struct uleds_device *udev = file->private_data;
86 const char *name;
87 int ret;
88
89 if (count == 0)
90 return 0;
91
92 ret = mutex_lock_interruptible(&udev->mutex);
93 if (ret)
94 return ret;
95
96 if (udev->state == ULEDS_STATE_REGISTERED) {
97 ret = -EBUSY;
98 goto out;
99 }
100
101 if (count != sizeof(struct uleds_user_dev)) {
102 ret = -EINVAL;
103 goto out;
104 }
105
106 if (copy_from_user(&udev->user_dev, buffer,
107 sizeof(struct uleds_user_dev))) {
108 ret = -EFAULT;
109 goto out;
110 }
111
112 name = udev->user_dev.name;
113 if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") ||
114 strchr(name, '/')) {
115 ret = -EINVAL;
116 goto out;
117 }
118
119 if (udev->user_dev.max_brightness <= 0) {
120 ret = -EINVAL;
121 goto out;
122 }
123 udev->led_cdev.max_brightness = udev->user_dev.max_brightness;
124
125 ret = devm_led_classdev_register(uleds_misc.this_device,
126 &udev->led_cdev);
127 if (ret < 0)
128 goto out;
129
130 udev->new_data = true;
131 udev->state = ULEDS_STATE_REGISTERED;
132 ret = count;
133
134out:
135 mutex_unlock(&udev->mutex);
136
137 return ret;
138}
139
140static ssize_t uleds_read(struct file *file, char __user *buffer, size_t count,
141 loff_t *ppos)
142{
143 struct uleds_device *udev = file->private_data;
144 ssize_t retval;
145
146 if (count < sizeof(udev->brightness))
147 return 0;
148
149 do {
150 retval = mutex_lock_interruptible(&udev->mutex);
151 if (retval)
152 return retval;
153
154 if (udev->state != ULEDS_STATE_REGISTERED) {
155 retval = -ENODEV;
156 } else if (!udev->new_data && (file->f_flags & O_NONBLOCK)) {
157 retval = -EAGAIN;
158 } else if (udev->new_data) {
159 retval = copy_to_user(buffer, &udev->brightness,
160 sizeof(udev->brightness));
161 udev->new_data = false;
162 retval = sizeof(udev->brightness);
163 }
164
165 mutex_unlock(&udev->mutex);
166
167 if (retval)
168 break;
169
170 if (!(file->f_flags & O_NONBLOCK))
171 retval = wait_event_interruptible(udev->waitq,
172 udev->new_data ||
173 udev->state != ULEDS_STATE_REGISTERED);
174 } while (retval == 0);
175
176 return retval;
177}
178
179static unsigned int uleds_poll(struct file *file, poll_table *wait)
180{
181 struct uleds_device *udev = file->private_data;
182
183 poll_wait(file, &udev->waitq, wait);
184
185 if (udev->new_data)
186 return POLLIN | POLLRDNORM;
187
188 return 0;
189}
190
191static int uleds_release(struct inode *inode, struct file *file)
192{
193 struct uleds_device *udev = file->private_data;
194
195 if (udev->state == ULEDS_STATE_REGISTERED) {
196 udev->state = ULEDS_STATE_UNKNOWN;
197 devm_led_classdev_unregister(uleds_misc.this_device,
198 &udev->led_cdev);
199 }
200 kfree(udev);
201
202 return 0;
203}
204
205static const struct file_operations uleds_fops = {
206 .owner = THIS_MODULE,
207 .open = uleds_open,
208 .release = uleds_release,
209 .read = uleds_read,
210 .write = uleds_write,
211 .poll = uleds_poll,
212 .llseek = no_llseek,
213};
214
215static struct miscdevice uleds_misc = {
216 .fops = &uleds_fops,
217 .minor = MISC_DYNAMIC_MINOR,
218 .name = ULEDS_NAME,
219};
220
221static int __init uleds_init(void)
222{
223 return misc_register(&uleds_misc);
224}
225module_init(uleds_init);
226
227static void __exit uleds_exit(void)
228{
229 misc_deregister(&uleds_misc);
230}
231module_exit(uleds_exit);
232
233MODULE_AUTHOR("David Lechner <david@lechnology.com>");
234MODULE_DESCRIPTION("Userspace driver for the LED subsystem");
235MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df6442ba2b..1ed0584f494e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -756,24 +756,20 @@ config UCB1400_CORE
756 module will be called ucb1400_core. 756 module will be called ucb1400_core.
757 757
758config MFD_PM8XXX 758config MFD_PM8XXX
759 tristate 759 tristate "Qualcomm PM8xxx PMIC chips driver"
760
761config MFD_PM8921_CORE
762 tristate "Qualcomm PM8921 PMIC chip"
763 depends on (ARM || HEXAGON) 760 depends on (ARM || HEXAGON)
764 select IRQ_DOMAIN 761 select IRQ_DOMAIN
765 select MFD_CORE 762 select MFD_CORE
766 select MFD_PM8XXX
767 select REGMAP 763 select REGMAP
768 help 764 help
769 If you say yes to this option, support will be included for the 765 If you say yes to this option, support will be included for the
770 built-in PM8921 PMIC chip. 766 built-in PM8xxx PMIC chips.
771 767
772 This is required if your board has a PM8921 and uses its features, 768 This is required if your board has a PM8xxx and uses its features,
773 such as: MPPs, GPIOs, regulators, interrupts, and PWM. 769 such as: MPPs, GPIOs, regulators, interrupts, and PWM.
774 770
775 Say M here if you want to include support for PM8921 chip as a module. 771 Say M here if you want to include support for PM8xxx chips as a
776 This will build a module called "pm8921-core". 772 module. This will build a module called "pm8xxx-core".
777 773
778config MFD_QCOM_RPM 774config MFD_QCOM_RPM
779 tristate "Qualcomm Resource Power Manager (RPM)" 775 tristate "Qualcomm Resource Power Manager (RPM)"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e669d985..7bb5a50127cb 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -172,7 +172,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
172 172
173obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o 173obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
174obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o 174obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
175obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o 175obj-$(CONFIG_MFD_PM8XXX) += qcom-pm8xxx.o ssbi.o
176obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o 176obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
177obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o 177obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
178obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o 178obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/qcom-pm8xxx.c
index 0e3a2ea25942..7f9620ec61e8 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/qcom-pm8xxx.c
@@ -53,7 +53,7 @@
53#define REG_HWREV 0x002 /* PMIC4 revision */ 53#define REG_HWREV 0x002 /* PMIC4 revision */
54#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */ 54#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
55 55
56#define PM8921_NR_IRQS 256 56#define PM8XXX_NR_IRQS 256
57 57
58struct pm_irq_chip { 58struct pm_irq_chip {
59 struct regmap *regmap; 59 struct regmap *regmap;
@@ -308,22 +308,22 @@ static const struct regmap_config ssbi_regmap_config = {
308 .reg_write = ssbi_reg_write 308 .reg_write = ssbi_reg_write
309}; 309};
310 310
311static const struct of_device_id pm8921_id_table[] = { 311static const struct of_device_id pm8xxx_id_table[] = {
312 { .compatible = "qcom,pm8018", }, 312 { .compatible = "qcom,pm8018", },
313 { .compatible = "qcom,pm8058", }, 313 { .compatible = "qcom,pm8058", },
314 { .compatible = "qcom,pm8921", }, 314 { .compatible = "qcom,pm8921", },
315 { } 315 { }
316}; 316};
317MODULE_DEVICE_TABLE(of, pm8921_id_table); 317MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
318 318
319static int pm8921_probe(struct platform_device *pdev) 319static int pm8xxx_probe(struct platform_device *pdev)
320{ 320{
321 struct regmap *regmap; 321 struct regmap *regmap;
322 int irq, rc; 322 int irq, rc;
323 unsigned int val; 323 unsigned int val;
324 u32 rev; 324 u32 rev;
325 struct pm_irq_chip *chip; 325 struct pm_irq_chip *chip;
326 unsigned int nirqs = PM8921_NR_IRQS; 326 unsigned int nirqs = PM8XXX_NR_IRQS;
327 327
328 irq = platform_get_irq(pdev, 0); 328 irq = platform_get_irq(pdev, 0);
329 if (irq < 0) 329 if (irq < 0)
@@ -384,46 +384,46 @@ static int pm8921_probe(struct platform_device *pdev)
384 return rc; 384 return rc;
385} 385}
386 386
387static int pm8921_remove_child(struct device *dev, void *unused) 387static int pm8xxx_remove_child(struct device *dev, void *unused)
388{ 388{
389 platform_device_unregister(to_platform_device(dev)); 389 platform_device_unregister(to_platform_device(dev));
390 return 0; 390 return 0;
391} 391}
392 392
393static int pm8921_remove(struct platform_device *pdev) 393static int pm8xxx_remove(struct platform_device *pdev)
394{ 394{
395 int irq = platform_get_irq(pdev, 0); 395 int irq = platform_get_irq(pdev, 0);
396 struct pm_irq_chip *chip = platform_get_drvdata(pdev); 396 struct pm_irq_chip *chip = platform_get_drvdata(pdev);
397 397
398 device_for_each_child(&pdev->dev, NULL, pm8921_remove_child); 398 device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child);
399 irq_set_chained_handler_and_data(irq, NULL, NULL); 399 irq_set_chained_handler_and_data(irq, NULL, NULL);
400 irq_domain_remove(chip->irqdomain); 400 irq_domain_remove(chip->irqdomain);
401 401
402 return 0; 402 return 0;
403} 403}
404 404
405static struct platform_driver pm8921_driver = { 405static struct platform_driver pm8xxx_driver = {
406 .probe = pm8921_probe, 406 .probe = pm8xxx_probe,
407 .remove = pm8921_remove, 407 .remove = pm8xxx_remove,
408 .driver = { 408 .driver = {
409 .name = "pm8921-core", 409 .name = "pm8xxx-core",
410 .of_match_table = pm8921_id_table, 410 .of_match_table = pm8xxx_id_table,
411 }, 411 },
412}; 412};
413 413
414static int __init pm8921_init(void) 414static int __init pm8xxx_init(void)
415{ 415{
416 return platform_driver_register(&pm8921_driver); 416 return platform_driver_register(&pm8xxx_driver);
417} 417}
418subsys_initcall(pm8921_init); 418subsys_initcall(pm8xxx_init);
419 419
420static void __exit pm8921_exit(void) 420static void __exit pm8xxx_exit(void)
421{ 421{
422 platform_driver_unregister(&pm8921_driver); 422 platform_driver_unregister(&pm8xxx_driver);
423} 423}
424module_exit(pm8921_exit); 424module_exit(pm8xxx_exit);
425 425
426MODULE_LICENSE("GPL v2"); 426MODULE_LICENSE("GPL v2");
427MODULE_DESCRIPTION("PMIC 8921 core driver"); 427MODULE_DESCRIPTION("PMIC 8xxx core driver");
428MODULE_VERSION("1.0"); 428MODULE_VERSION("1.0");
429MODULE_ALIAS("platform:pm8921-core"); 429MODULE_ALIAS("platform:pm8xxx-core");
diff --git a/include/linux/leds.h b/include/linux/leds.h
index ddfcb2df3656..569cb531094c 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -42,16 +42,20 @@ struct led_classdev {
42#define LED_UNREGISTERING (1 << 1) 42#define LED_UNREGISTERING (1 << 1)
43 /* Upper 16 bits reflect control information */ 43 /* Upper 16 bits reflect control information */
44#define LED_CORE_SUSPENDRESUME (1 << 16) 44#define LED_CORE_SUSPENDRESUME (1 << 16)
45#define LED_BLINK_SW (1 << 17) 45#define LED_SYSFS_DISABLE (1 << 17)
46#define LED_BLINK_ONESHOT (1 << 18) 46#define LED_DEV_CAP_FLASH (1 << 18)
47#define LED_BLINK_ONESHOT_STOP (1 << 19) 47#define LED_HW_PLUGGABLE (1 << 19)
48#define LED_BLINK_INVERT (1 << 20) 48#define LED_PANIC_INDICATOR (1 << 20)
49#define LED_BLINK_BRIGHTNESS_CHANGE (1 << 21) 49
50#define LED_BLINK_DISABLE (1 << 22) 50 /* set_brightness_work / blink_timer flags, atomic, private. */
51#define LED_SYSFS_DISABLE (1 << 23) 51 unsigned long work_flags;
52#define LED_DEV_CAP_FLASH (1 << 24) 52
53#define LED_HW_PLUGGABLE (1 << 25) 53#define LED_BLINK_SW 0
54#define LED_PANIC_INDICATOR (1 << 26) 54#define LED_BLINK_ONESHOT 1
55#define LED_BLINK_ONESHOT_STOP 2
56#define LED_BLINK_INVERT 3
57#define LED_BLINK_BRIGHTNESS_CHANGE 4
58#define LED_BLINK_DISABLE 5
55 59
56 /* Set LED brightness level 60 /* Set LED brightness level
57 * Must not sleep. Use brightness_set_blocking for drivers 61 * Must not sleep. Use brightness_set_blocking for drivers
@@ -89,6 +93,7 @@ struct led_classdev {
89 unsigned long blink_delay_on, blink_delay_off; 93 unsigned long blink_delay_on, blink_delay_off;
90 struct timer_list blink_timer; 94 struct timer_list blink_timer;
91 int blink_brightness; 95 int blink_brightness;
96 int new_blink_brightness;
92 void (*flash_resume)(struct led_classdev *led_cdev); 97 void (*flash_resume)(struct led_classdev *led_cdev);
93 98
94 struct work_struct set_brightness_work; 99 struct work_struct set_brightness_work;
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index cd2be1c8e9fb..9838a5c7b3e7 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -426,6 +426,7 @@ header-y += udp.h
426header-y += uhid.h 426header-y += uhid.h
427header-y += uinput.h 427header-y += uinput.h
428header-y += uio.h 428header-y += uio.h
429header-y += uleds.h
429header-y += ultrasound.h 430header-y += ultrasound.h
430header-y += un.h 431header-y += un.h
431header-y += unistd.h 432header-y += unistd.h
diff --git a/include/uapi/linux/uleds.h b/include/uapi/linux/uleds.h
new file mode 100644
index 000000000000..95186578c46e
--- /dev/null
+++ b/include/uapi/linux/uleds.h
@@ -0,0 +1,24 @@
1/*
2 * Userspace driver support for the LED subsystem
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#ifndef _UAPI__ULEDS_H_
15#define _UAPI__ULEDS_H_
16
17#define LED_MAX_NAME_SIZE 64
18
19struct uleds_user_dev {
20 char name[LED_MAX_NAME_SIZE];
21 int max_brightness;
22};
23
24#endif /* _UAPI__ULEDS_H_ */
diff --git a/tools/Makefile b/tools/Makefile
index daa8fb3e4363..00caacd3ed92 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -17,6 +17,7 @@ help:
17 @echo ' hv - tools used when in Hyper-V clients' 17 @echo ' hv - tools used when in Hyper-V clients'
18 @echo ' iio - IIO tools' 18 @echo ' iio - IIO tools'
19 @echo ' kvm_stat - top-like utility for displaying kvm statistics' 19 @echo ' kvm_stat - top-like utility for displaying kvm statistics'
20 @echo ' leds - LEDs tools'
20 @echo ' lguest - a minimal 32-bit x86 hypervisor' 21 @echo ' lguest - a minimal 32-bit x86 hypervisor'
21 @echo ' net - misc networking tools' 22 @echo ' net - misc networking tools'
22 @echo ' perf - Linux performance measurement and analysis tool' 23 @echo ' perf - Linux performance measurement and analysis tool'
@@ -56,7 +57,7 @@ acpi: FORCE
56cpupower: FORCE 57cpupower: FORCE
57 $(call descend,power/$@) 58 $(call descend,power/$@)
58 59
59cgroup firewire hv guest spi usb virtio vm net iio gpio objtool: FORCE 60cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE
60 $(call descend,$@) 61 $(call descend,$@)
61 62
62liblockdep: FORCE 63liblockdep: FORCE
@@ -126,7 +127,7 @@ acpi_clean:
126cpupower_clean: 127cpupower_clean:
127 $(call descend,power/cpupower,clean) 128 $(call descend,power/cpupower,clean)
128 129
129cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean: 130cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
130 $(call descend,$(@:_clean=),clean) 131 $(call descend,$(@:_clean=),clean)
131 132
132liblockdep_clean: 133liblockdep_clean:
@@ -164,6 +165,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_cle
164 perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ 165 perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
165 vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ 166 vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
166 freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ 167 freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
167 gpio_clean objtool_clean 168 gpio_clean objtool_clean leds_clean
168 169
169.PHONY: FORCE 170.PHONY: FORCE
diff --git a/tools/leds/.gitignore b/tools/leds/.gitignore
new file mode 100644
index 000000000000..ac96d9f53dfc
--- /dev/null
+++ b/tools/leds/.gitignore
@@ -0,0 +1 @@
uledmon
diff --git a/tools/leds/Makefile b/tools/leds/Makefile
new file mode 100644
index 000000000000..c03a79ebf9c8
--- /dev/null
+++ b/tools/leds/Makefile
@@ -0,0 +1,13 @@
1# Makefile for LEDs tools
2
3CC = $(CROSS_COMPILE)gcc
4CFLAGS = -Wall -Wextra -g -I../../include/uapi
5
6all: uledmon
7%: %.c
8 $(CC) $(CFLAGS) -o $@ $^
9
10clean:
11 $(RM) uledmon
12
13.PHONY: all clean
diff --git a/tools/leds/uledmon.c b/tools/leds/uledmon.c
new file mode 100644
index 000000000000..25cbc7acf50a
--- /dev/null
+++ b/tools/leds/uledmon.c
@@ -0,0 +1,63 @@
1/*
2 * uledmon.c
3 *
4 * This program creates a new userspace LED class device and monitors it. A
5 * timestamp and brightness value is printed each time the brightness changes.
6 *
7 * Usage: uledmon <device-name>
8 *
9 * <device-name> is the name of the LED class device to be created. Pressing
10 * CTRL+C will exit.
11 */
12
13#include <fcntl.h>
14#include <stdio.h>
15#include <string.h>
16#include <time.h>
17#include <unistd.h>
18
19#include <linux/uleds.h>
20
21int main(int argc, char const *argv[])
22{
23 struct uleds_user_dev uleds_dev;
24 int fd, ret;
25 int brightness;
26 struct timespec ts;
27
28 if (argc != 2) {
29 fprintf(stderr, "Requires <device-name> argument\n");
30 return 1;
31 }
32
33 strncpy(uleds_dev.name, argv[1], LED_MAX_NAME_SIZE);
34 uleds_dev.max_brightness = 100;
35
36 fd = open("/dev/uleds", O_RDWR);
37 if (fd == -1) {
38 perror("Failed to open /dev/uleds");
39 return 1;
40 }
41
42 ret = write(fd, &uleds_dev, sizeof(uleds_dev));
43 if (ret == -1) {
44 perror("Failed to write to /dev/uleds");
45 close(fd);
46 return 1;
47 }
48
49 while (1) {
50 ret = read(fd, &brightness, sizeof(brightness));
51 if (ret == -1) {
52 perror("Failed to read from /dev/uleds");
53 close(fd);
54 return 1;
55 }
56 clock_gettime(CLOCK_MONOTONIC, &ts);
57 printf("[%ld.%09ld] %u\n", ts.tv_sec, ts.tv_nsec, brightness);
58 }
59
60 close(fd);
61
62 return 0;
63}