Skip to content

Commit

Permalink
Merge tag 'leds_for_4.10' of git://git.kernel.org/pub/scm/linux/kerne…
Browse files Browse the repository at this point in the history
…l/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
  • Loading branch information
torvalds committed Dec 13, 2016
2 parents 20d5ba4 + 44b3e31 commit 1f0a53f
Show file tree
Hide file tree
Showing 32 changed files with 807 additions and 107 deletions.
14 changes: 11 additions & 3 deletions Documentation/ABI/testing/sysfs-class-led
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,32 @@ KernelVersion: 2.6.17
Contact: Richard Purdie <rpurdie@rpsys.net>
Description:
Set the brightness of the LED. Most LEDs don't
have hardware brightness support so will just be turned on for
have hardware brightness support, so will just be turned on for
non-zero brightness settings. The value is between 0 and
/sys/class/leds/<led>/max_brightness.

Writing 0 to this file clears active trigger.

Writing non-zero to this file while trigger is active changes the
top brightness trigger is going to use.

What: /sys/class/leds/<led>/max_brightness
Date: March 2006
KernelVersion: 2.6.17
Contact: Richard Purdie <rpurdie@rpsys.net>
Description:
Maximum brightness level for this led, default is 255 (LED_FULL).
Maximum brightness level for this LED, default is 255 (LED_FULL).

If the LED does not support different brightness levels, this
should be 1.

What: /sys/class/leds/<led>/trigger
Date: March 2006
KernelVersion: 2.6.17
Contact: Richard Purdie <rpurdie@rpsys.net>
Description:
Set the trigger for this LED. A trigger is a kernel based source
of led events.
of LED events.
You can change triggers in a similar manner to the way an IO
scheduler is chosen. Trigger specific parameters can appear in
/sys/class/leds/<led> once a given trigger is selected. For
Expand Down
3 changes: 3 additions & 0 deletions Documentation/devicetree/bindings/leds/pca963x.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Optional properties:
- nxp,totem-pole : use totem pole (push-pull) instead of open-drain (pca9632 defaults
to open-drain, newer chips to totem pole)
- nxp,hw-blink : use hardware blinking instead of software blinking
- nxp,period-scale : In some configurations, the chip blinks faster than expected.
This parameter provides a scaling ratio (fixed point, decimal divided
by 1000) to compensate, e.g. 1300=1.3x and 750=0.75x.

Each led is represented as a sub-node of the nxp,pca963x device.

Expand Down
4 changes: 2 additions & 2 deletions Documentation/leds/leds-lp5523.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ There are two ways to run LED patterns.
Control interface for the engines:
x is 1 .. 3
enginex_mode : disabled, load, run
enginex_load : microcode load (visible only in load mode)
enginex_leds : led mux control (visible only in load mode)
enginex_load : microcode load
enginex_leds : led mux control

cd /sys/class/leds/lp5523:channel2/device
echo "load" > engine3_mode
Expand Down
36 changes: 36 additions & 0 deletions Documentation/leds/uleds.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Userspace LEDs
==============

The uleds driver supports userspace LEDs. This can be useful for testing
triggers and can also be used to implement virtual LEDs.


Usage
=====

When the driver is loaded, a character device is created at /dev/uleds. To
create a new LED class device, open /dev/uleds and write a uleds_user_dev
structure to it (found in kernel public header file linux/uleds.h).

#define LED_MAX_NAME_SIZE 64

struct uleds_user_dev {
char name[LED_MAX_NAME_SIZE];
};

A new LED class device will be created with the name given. The name can be
any valid sysfs device node name, but consider using the LED class naming
convention of "devicename:color:function".

The current brightness is found by reading a single byte from the character
device. Values are unsigned: 0 to 255. Reading will block until the brightness
changes. The device node can also be polled to notify when the brightness value
changes.

The LED class device will be removed when the open file handle to /dev/uleds
is closed.

Multiple LED class devices are created by opening additional file handles to
/dev/uleds.

See tools/leds/uledmon.c for an example userspace program.
2 changes: 1 addition & 1 deletion arch/arm/configs/multi_v7_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ CONFIG_MFD_MAX8907=y
CONFIG_MFD_MAX8997=y
CONFIG_MFD_MAX8998=y
CONFIG_MFD_RK808=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8XXX=y
CONFIG_MFD_QCOM_RPM=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_MFD_SEC_CORE=y
Expand Down
1 change: 0 additions & 1 deletion arch/arm/configs/pxa_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ CONFIG_MFD_MAX77693=y
CONFIG_MFD_MAX8907=m
CONFIG_EZX_PCAP=y
CONFIG_UCB1400_CORE=m
CONFIG_MFD_PM8921_CORE=m
CONFIG_MFD_SEC_CORE=y
CONFIG_MFD_PALMAS=y
CONFIG_MFD_TPS65090=y
Expand Down
1 change: 0 additions & 1 deletion arch/arm/configs/qcom_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_MSM=y
CONFIG_THERMAL=y
CONFIG_MFD_PM8XXX=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_QCOM_RPM=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
Expand Down
21 changes: 20 additions & 1 deletion drivers/leds/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ config LEDS_VERSATILE

config LEDS_PM8058
tristate "LED Support for the Qualcomm PM8058 PMIC"
depends on MFD_PM8921_CORE
depends on MFD_PM8XXX
depends on LEDS_CLASS
help
Choose this option if you want to use the LED drivers in
Expand All @@ -659,6 +659,25 @@ config LEDS_MLXCPLD
This option enabled support for the LEDs on the Mellanox
boards. Say Y to enabled these.

config LEDS_USER
tristate "Userspace LED support"
depends on LEDS_CLASS
help
This option enables support for userspace LEDs. Say 'y' to enable this
support in kernel. To compile this driver as a module, choose 'm' here:
the module will be called uleds.

config LEDS_NIC78BX
tristate "LED support for NI PXI NIC78bx devices"
depends on LEDS_CLASS
depends on X86 && ACPI
help
This option enables support for the User1 and User2 LEDs on NI
PXI NIC78bx devices.

To compile this driver as a module, choose M here: the module
will be called leds-nic78bx.

comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"

Expand Down
4 changes: 4 additions & 0 deletions drivers/leds/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,13 @@ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o

# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o

# LED Userspace Drivers
obj-$(CONFIG_LEDS_USER) += uleds.o

# LED Triggers
obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
4 changes: 3 additions & 1 deletion drivers/leds/led-class.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <uapi/linux/uleds.h>
#include "leds.h"

static struct class *leds_class;
Expand Down Expand Up @@ -187,7 +188,7 @@ static int led_classdev_next_name(const char *init_name, char *name,
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
char name[64];
char name[LED_MAX_NAME_SIZE];
int ret;

ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
Expand All @@ -203,6 +204,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
dev_warn(parent, "Led %s renamed to %s due to name collision",
led_cdev->name, dev_name(led_cdev->dev));

led_cdev->work_flags = 0;
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
#endif
Expand Down
62 changes: 32 additions & 30 deletions drivers/leds/led-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,30 +53,30 @@ static void led_timer_function(unsigned long data)

if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
led_set_brightness_nosleep(led_cdev, LED_OFF);
led_cdev->flags &= ~LED_BLINK_SW;
clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
return;
}

if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
led_cdev->flags &= ~(LED_BLINK_ONESHOT_STOP | LED_BLINK_SW);
if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
&led_cdev->work_flags)) {
clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
return;
}

brightness = led_get_brightness(led_cdev);
if (!brightness) {
/* Time to switch the LED on. */
brightness = led_cdev->blink_brightness;
if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
&led_cdev->work_flags))
brightness = led_cdev->new_blink_brightness;
else
brightness = led_cdev->blink_brightness;
delay = led_cdev->blink_delay_on;
} else {
/* Store the current brightness value to be able
* to restore it when the delay_off period is over.
* Do it only if there is no pending blink brightness
* change, to avoid overwriting the new value.
*/
if (!(led_cdev->flags & LED_BLINK_BRIGHTNESS_CHANGE))
led_cdev->blink_brightness = brightness;
else
led_cdev->flags &= ~LED_BLINK_BRIGHTNESS_CHANGE;
led_cdev->blink_brightness = brightness;
brightness = LED_OFF;
delay = led_cdev->blink_delay_off;
}
Expand All @@ -87,13 +87,15 @@ static void led_timer_function(unsigned long data)
* the final blink state so that the led is toggled each delay_on +
* delay_off milliseconds in worst case.
*/
if (led_cdev->flags & LED_BLINK_ONESHOT) {
if (led_cdev->flags & LED_BLINK_INVERT) {
if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
if (brightness)
led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
set_bit(LED_BLINK_ONESHOT_STOP,
&led_cdev->work_flags);
} else {
if (!brightness)
led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
set_bit(LED_BLINK_ONESHOT_STOP,
&led_cdev->work_flags);
}
}

Expand All @@ -106,10 +108,9 @@ static void set_brightness_delayed(struct work_struct *ws)
container_of(ws, struct led_classdev, set_brightness_work);
int ret = 0;

if (led_cdev->flags & LED_BLINK_DISABLE) {
if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
led_cdev->delayed_set_value = LED_OFF;
led_stop_software_blink(led_cdev);
led_cdev->flags &= ~LED_BLINK_DISABLE;
}

ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
Expand Down Expand Up @@ -152,7 +153,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
return;
}

led_cdev->flags |= LED_BLINK_SW;
set_bit(LED_BLINK_SW, &led_cdev->work_flags);
mod_timer(&led_cdev->blink_timer, jiffies + 1);
}

Expand All @@ -161,7 +162,7 @@ static void led_blink_setup(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
if (!(led_cdev->flags & LED_BLINK_ONESHOT) &&
if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
led_cdev->blink_set &&
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
return;
Expand All @@ -188,8 +189,8 @@ void led_blink_set(struct led_classdev *led_cdev,
{
del_timer_sync(&led_cdev->blink_timer);

led_cdev->flags &= ~LED_BLINK_ONESHOT;
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);

led_blink_setup(led_cdev, delay_on, delay_off);
}
Expand All @@ -200,17 +201,17 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
unsigned long *delay_off,
int invert)
{
if ((led_cdev->flags & LED_BLINK_ONESHOT) &&
if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
timer_pending(&led_cdev->blink_timer))
return;

led_cdev->flags |= LED_BLINK_ONESHOT;
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);

if (invert)
led_cdev->flags |= LED_BLINK_INVERT;
set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
else
led_cdev->flags &= ~LED_BLINK_INVERT;
clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);

led_blink_setup(led_cdev, delay_on, delay_off);
}
Expand All @@ -221,7 +222,7 @@ void led_stop_software_blink(struct led_classdev *led_cdev)
del_timer_sync(&led_cdev->blink_timer);
led_cdev->blink_delay_on = 0;
led_cdev->blink_delay_off = 0;
led_cdev->flags &= ~LED_BLINK_SW;
clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
}
EXPORT_SYMBOL_GPL(led_stop_software_blink);

Expand All @@ -232,18 +233,19 @@ void led_set_brightness(struct led_classdev *led_cdev,
* If software blink is active, delay brightness setting
* until the next timer tick.
*/
if (led_cdev->flags & LED_BLINK_SW) {
if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
/*
* If we need to disable soft blinking delegate this to the
* work queue task to avoid problems in case we are called
* from hard irq context.
*/
if (brightness == LED_OFF) {
led_cdev->flags |= LED_BLINK_DISABLE;
set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
schedule_work(&led_cdev->set_brightness_work);
} else {
led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE;
led_cdev->blink_brightness = brightness;
set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
&led_cdev->work_flags);
led_cdev->new_blink_brightness = brightness;
}
return;
}
Expand Down
6 changes: 1 addition & 5 deletions drivers/leds/leds-cobalt-raq.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,4 @@ static struct platform_driver cobalt_raq_led_driver = {
},
};

static int __init cobalt_raq_led_init(void)
{
return platform_driver_register(&cobalt_raq_led_driver);
}
device_initcall(cobalt_raq_led_init);
builtin_platform_driver(cobalt_raq_led_driver);
1 change: 1 addition & 0 deletions drivers/leds/leds-lp3952.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ static const struct i2c_device_id lp3952_id[] = {
{LP3952_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, lp3952_id);

#ifdef CONFIG_ACPI
static const struct acpi_device_id lp3952_acpi_match[] = {
Expand Down
5 changes: 3 additions & 2 deletions drivers/leds/leds-mc13783.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ static int mc13xxx_led_set(struct led_classdev *led_cdev,
case MC13892_LED_MD:
case MC13892_LED_AD:
case MC13892_LED_KP:
reg = (led->id - MC13892_LED_MD) / 2;
shift = 3 + (led->id - MC13892_LED_MD) * 12;
off = led->id - MC13892_LED_MD;
reg = off / 2;
shift = 3 + (off - reg * 2) * 12;
break;
case MC13892_LED_R:
case MC13892_LED_G:
Expand Down
5 changes: 4 additions & 1 deletion drivers/leds/leds-mlxcpld.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,9 @@ static int __init mlxcpld_led_init(void)
struct platform_device *pdev;
int err;

if (!dmi_match(DMI_CHASSIS_VENDOR, "Mellanox Technologies Ltd."))
return -ENODEV;

pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
if (IS_ERR(pdev)) {
pr_err("Device allocation failed\n");
Expand All @@ -426,5 +429,5 @@ module_exit(mlxcpld_led_exit);

MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("Mellanox board LED driver");
MODULE_LICENSE("GPL v2");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:leds_mlxcpld");
Loading

0 comments on commit 1f0a53f

Please sign in to comment.