mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-21 05:14:52 +08:00
LED updates for 4.10 merge cycle.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJYToOYAAoJEL1qUBy3i3wmKL8QAKKJo17+3X+dWdmyqJ3sNa7r YiWvaOUv6OBnOwc2ETfzprk0YsKsvuHEUAehjoUBwqNBDs/lnEnfwv4EfiISmv6R sKQP38Q92RgfSnYfDGy/U8VgPgAMintA1/PRrxZTYpMiqX3QZa9HLRO2LmyC/Nuk Td0lVvAS/PpGCFqk4xwGfn/pVfgBjmdyCoQTVpJoh2fn64V3Jd56YzVekOQd6Koc 7eIyy64Bmh3gNWxOExqPFhzSotysPKvfAz6BHcv0IpI6Wio+Rek7E3OY//VlntaH Wnn086QcGzcAAT6irJ7AMLsU03j2Dr4zhPvVdiHTqsm+7YOfP7ugT+OCdCkbh3Y9 HYQoCFXL1uPt68Vcc+72ijEytUhNm1p3ri1+jKl8hlg93+0eZ2g/zNxgQZ6kLuQw 2s4P4FGC8TnaG213k6or/UaAocIfOhHWpf0ymMT7Hu6JSQJQTGjS29dqfY17YMCN UxvXyT/j+QHskJnvDBhmblrnXe516MpQiMxookuVUh1gDHTGdB+A23Hj4W57mA95 WpQm36MmDuD6ri4hywLv+fEVTc1FrbjZOdi8AoAsWDJEOY5K4QLfzreHk3QnCpDg S1HGIr9QcC4aMtXyE5o1ahKX0e2A3kgu22hQtk6w7eAnvwdS7PaB2F0eVEOoI85C Epxp4ni5Jgb7013Hoq2e =4k30 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
1f0a53f623
@ -4,16 +4,24 @@ 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
|
||||
@ -21,7 +29,7 @@ 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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
36
Documentation/leds/uleds.txt
Normal file
36
Documentation/leds/uleds.txt
Normal file
@ -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.
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -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"
|
||||
|
||||
|
@ -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/
|
||||
|
@ -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;
|
||||
@ -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));
|
||||
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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[] = {
|
||||
|
@ -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:
|
||||
|
@ -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");
|
||||
@ -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");
|
||||
|
@ -534,6 +534,7 @@ static const struct of_device_id of_netxbig_leds_match[] = {
|
||||
{ .compatible = "lacie,netxbig-leds", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_netxbig_leds_match);
|
||||
#else
|
||||
static inline int
|
||||
netxbig_leds_get_of_pdata(struct device *dev,
|
||||
|
209
drivers/leds/leds-nic78bx.c
Normal file
209
drivers/leds/leds-nic78bx.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2016 National Instruments Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define NIC78BX_USER1_LED_MASK 0x3
|
||||
#define NIC78BX_USER1_GREEN_LED BIT(0)
|
||||
#define NIC78BX_USER1_YELLOW_LED BIT(1)
|
||||
|
||||
#define NIC78BX_USER2_LED_MASK 0xC
|
||||
#define NIC78BX_USER2_GREEN_LED BIT(2)
|
||||
#define NIC78BX_USER2_YELLOW_LED BIT(3)
|
||||
|
||||
#define NIC78BX_LOCK_REG_OFFSET 1
|
||||
#define NIC78BX_LOCK_VALUE 0xA5
|
||||
#define NIC78BX_UNLOCK_VALUE 0x5A
|
||||
|
||||
#define NIC78BX_USER_LED_IO_SIZE 2
|
||||
|
||||
struct nic78bx_led_data {
|
||||
u16 io_base;
|
||||
spinlock_t lock;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
struct nic78bx_led {
|
||||
u8 bit;
|
||||
u8 mask;
|
||||
struct nic78bx_led_data *data;
|
||||
struct led_classdev cdev;
|
||||
};
|
||||
|
||||
static inline struct nic78bx_led *to_nic78bx_led(struct led_classdev *cdev)
|
||||
{
|
||||
return container_of(cdev, struct nic78bx_led, cdev);
|
||||
}
|
||||
|
||||
static void nic78bx_brightness_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct nic78bx_led *nled = to_nic78bx_led(cdev);
|
||||
unsigned long flags;
|
||||
u8 value;
|
||||
|
||||
spin_lock_irqsave(&nled->data->lock, flags);
|
||||
value = inb(nled->data->io_base);
|
||||
|
||||
if (brightness) {
|
||||
value &= ~nled->mask;
|
||||
value |= nled->bit;
|
||||
} else {
|
||||
value &= ~nled->bit;
|
||||
}
|
||||
|
||||
outb(value, nled->data->io_base);
|
||||
spin_unlock_irqrestore(&nled->data->lock, flags);
|
||||
}
|
||||
|
||||
static enum led_brightness nic78bx_brightness_get(struct led_classdev *cdev)
|
||||
{
|
||||
struct nic78bx_led *nled = to_nic78bx_led(cdev);
|
||||
unsigned long flags;
|
||||
u8 value;
|
||||
|
||||
spin_lock_irqsave(&nled->data->lock, flags);
|
||||
value = inb(nled->data->io_base);
|
||||
spin_unlock_irqrestore(&nled->data->lock, flags);
|
||||
|
||||
return (value & nled->bit) ? 1 : LED_OFF;
|
||||
}
|
||||
|
||||
static struct nic78bx_led nic78bx_leds[] = {
|
||||
{
|
||||
.bit = NIC78BX_USER1_GREEN_LED,
|
||||
.mask = NIC78BX_USER1_LED_MASK,
|
||||
.cdev = {
|
||||
.name = "nilrt:green:user1",
|
||||
.max_brightness = 1,
|
||||
.brightness_set = nic78bx_brightness_set,
|
||||
.brightness_get = nic78bx_brightness_get,
|
||||
}
|
||||
},
|
||||
{
|
||||
.bit = NIC78BX_USER1_YELLOW_LED,
|
||||
.mask = NIC78BX_USER1_LED_MASK,
|
||||
.cdev = {
|
||||
.name = "nilrt:yellow:user1",
|
||||
.max_brightness = 1,
|
||||
.brightness_set = nic78bx_brightness_set,
|
||||
.brightness_get = nic78bx_brightness_get,
|
||||
}
|
||||
},
|
||||
{
|
||||
.bit = NIC78BX_USER2_GREEN_LED,
|
||||
.mask = NIC78BX_USER2_LED_MASK,
|
||||
.cdev = {
|
||||
.name = "nilrt:green:user2",
|
||||
.max_brightness = 1,
|
||||
.brightness_set = nic78bx_brightness_set,
|
||||
.brightness_get = nic78bx_brightness_get,
|
||||
}
|
||||
},
|
||||
{
|
||||
.bit = NIC78BX_USER2_YELLOW_LED,
|
||||
.mask = NIC78BX_USER2_LED_MASK,
|
||||
.cdev = {
|
||||
.name = "nilrt:yellow:user2",
|
||||
.max_brightness = 1,
|
||||
.brightness_set = nic78bx_brightness_set,
|
||||
.brightness_get = nic78bx_brightness_get,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static int nic78bx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct nic78bx_led_data *led_data;
|
||||
struct resource *io_rc;
|
||||
int ret, i;
|
||||
|
||||
led_data = devm_kzalloc(dev, sizeof(*led_data), GFP_KERNEL);
|
||||
if (!led_data)
|
||||
return -ENOMEM;
|
||||
|
||||
led_data->pdev = pdev;
|
||||
platform_set_drvdata(pdev, led_data);
|
||||
|
||||
io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!io_rc) {
|
||||
dev_err(dev, "missing IO resources\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (resource_size(io_rc) < NIC78BX_USER_LED_IO_SIZE) {
|
||||
dev_err(dev, "IO region too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),
|
||||
KBUILD_MODNAME)) {
|
||||
dev_err(dev, "failed to get IO region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
led_data->io_base = io_rc->start;
|
||||
spin_lock_init(&led_data->lock);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) {
|
||||
nic78bx_leds[i].data = led_data;
|
||||
|
||||
ret = devm_led_classdev_register(dev, &nic78bx_leds[i].cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Unlock LED register */
|
||||
outb(NIC78BX_UNLOCK_VALUE,
|
||||
led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nic78bx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct nic78bx_led_data *led_data = platform_get_drvdata(pdev);
|
||||
|
||||
/* Lock LED register */
|
||||
outb(NIC78BX_LOCK_VALUE,
|
||||
led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id led_device_ids[] = {
|
||||
{"NIC78B3", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, led_device_ids);
|
||||
|
||||
static struct platform_driver led_driver = {
|
||||
.probe = nic78bx_probe,
|
||||
.remove = nic78bx_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.acpi_match_table = ACPI_PTR(led_device_ids),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(led_driver);
|
||||
|
||||
MODULE_DESCRIPTION("National Instruments PXI User LEDs driver");
|
||||
MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -369,7 +369,7 @@ static int pca9532_configure(struct i2c_client *client,
|
||||
led->state = pled->state;
|
||||
led->name = pled->name;
|
||||
led->ldev.name = led->name;
|
||||
led->ldev.default_trigger = led->default_trigger;
|
||||
led->ldev.default_trigger = pled->default_trigger;
|
||||
led->ldev.brightness = LED_OFF;
|
||||
led->ldev.brightness_set_blocking =
|
||||
pca9532_set_brightness;
|
||||
|
@ -40,6 +40,7 @@
|
||||
* bits the chip supports.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
@ -100,6 +101,15 @@ static const struct i2c_device_id pca955x_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca955x_id);
|
||||
|
||||
static const struct acpi_device_id pca955x_acpi_ids[] = {
|
||||
{ "PCA9550", pca9550 },
|
||||
{ "PCA9551", pca9551 },
|
||||
{ "PCA9552", pca9552 },
|
||||
{ "PCA9553", pca9553 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pca955x_acpi_ids);
|
||||
|
||||
struct pca955x {
|
||||
struct mutex lock;
|
||||
struct pca955x_led *leds;
|
||||
@ -250,7 +260,16 @@ static int pca955x_probe(struct i2c_client *client,
|
||||
struct led_platform_data *pdata;
|
||||
int i, err;
|
||||
|
||||
chip = &pca955x_chipdefs[id->driver_data];
|
||||
if (id) {
|
||||
chip = &pca955x_chipdefs[id->driver_data];
|
||||
} else {
|
||||
const struct acpi_device_id *acpi_id;
|
||||
|
||||
acpi_id = acpi_match_device(pca955x_acpi_ids, &client->dev);
|
||||
if (!acpi_id)
|
||||
return -ENODEV;
|
||||
chip = &pca955x_chipdefs[acpi_id->driver_data];
|
||||
}
|
||||
adapter = to_i2c_adapter(client->dev.parent);
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
@ -264,7 +283,7 @@ static int pca955x_probe(struct i2c_client *client,
|
||||
|
||||
dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at "
|
||||
"slave address 0x%02x\n",
|
||||
id->name, chip->bits, client->addr);
|
||||
client->name, chip->bits, client->addr);
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
|
||||
return -EIO;
|
||||
@ -358,6 +377,7 @@ static int pca955x_remove(struct i2c_client *client)
|
||||
static struct i2c_driver pca955x_driver = {
|
||||
.driver = {
|
||||
.name = "leds-pca955x",
|
||||
.acpi_match_table = ACPI_PTR(pca955x_acpi_ids),
|
||||
},
|
||||
.probe = pca955x_probe,
|
||||
.remove = pca955x_remove,
|
||||
|
@ -25,6 +25,7 @@
|
||||
* or by adding the 'nxp,hw-blink' property to the DTS.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
@ -59,6 +60,7 @@ struct pca963x_chipdef {
|
||||
u8 grpfreq;
|
||||
u8 ledout_base;
|
||||
int n_leds;
|
||||
unsigned int scaling;
|
||||
};
|
||||
|
||||
static struct pca963x_chipdef pca963x_chipdefs[] = {
|
||||
@ -95,6 +97,15 @@ static const struct i2c_device_id pca963x_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca963x_id);
|
||||
|
||||
static const struct acpi_device_id pca963x_acpi_ids[] = {
|
||||
{ "PCA9632", pca9633 },
|
||||
{ "PCA9633", pca9633 },
|
||||
{ "PCA9634", pca9634 },
|
||||
{ "PCA9635", pca9635 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pca963x_acpi_ids);
|
||||
|
||||
struct pca963x_led;
|
||||
|
||||
struct pca963x {
|
||||
@ -102,6 +113,7 @@ struct pca963x {
|
||||
struct mutex mutex;
|
||||
struct i2c_client *client;
|
||||
struct pca963x_led *leds;
|
||||
unsigned long leds_on;
|
||||
};
|
||||
|
||||
struct pca963x_led {
|
||||
@ -123,7 +135,6 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
|
||||
u8 mask = 0x3 << shift;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pca963x->chip->mutex);
|
||||
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
|
||||
switch (brightness) {
|
||||
case LED_FULL:
|
||||
@ -140,14 +151,13 @@ static int pca963x_brightness(struct pca963x_led *pca963x,
|
||||
PCA963X_PWM_BASE + pca963x->led_num,
|
||||
brightness);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_PWM << shift));
|
||||
break;
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -179,14 +189,49 @@ static void pca963x_blink(struct pca963x_led *pca963x)
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
}
|
||||
|
||||
static int pca963x_power_state(struct pca963x_led *pca963x)
|
||||
{
|
||||
unsigned long *leds_on = &pca963x->chip->leds_on;
|
||||
unsigned long cached_leds = pca963x->chip->leds_on;
|
||||
|
||||
if (pca963x->led_cdev.brightness)
|
||||
set_bit(pca963x->led_num, leds_on);
|
||||
else
|
||||
clear_bit(pca963x->led_num, leds_on);
|
||||
|
||||
if (!(*leds_on) != !cached_leds)
|
||||
return i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
PCA963X_MODE1, *leds_on ? 0 : BIT(4));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca963x_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct pca963x_led *pca963x;
|
||||
int ret;
|
||||
|
||||
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
|
||||
return pca963x_brightness(pca963x, value);
|
||||
mutex_lock(&pca963x->chip->mutex);
|
||||
|
||||
ret = pca963x_brightness(pca963x, value);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
ret = pca963x_power_state(pca963x);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int pca963x_period_scale(struct pca963x_led *pca963x,
|
||||
unsigned int val)
|
||||
{
|
||||
unsigned int scaling = pca963x->chip->chipdef->scaling;
|
||||
|
||||
return scaling ? DIV_ROUND_CLOSEST(val * scaling, 1000) : val;
|
||||
}
|
||||
|
||||
static int pca963x_blink_set(struct led_classdev *led_cdev,
|
||||
@ -207,14 +252,14 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
|
||||
time_off = 500;
|
||||
}
|
||||
|
||||
period = time_on + time_off;
|
||||
period = pca963x_period_scale(pca963x, time_on + time_off);
|
||||
|
||||
/* If period not supported by hardware, default to someting sane. */
|
||||
if ((period < PCA963X_BLINK_PERIOD_MIN) ||
|
||||
(period > PCA963X_BLINK_PERIOD_MAX)) {
|
||||
time_on = 500;
|
||||
time_off = 500;
|
||||
period = time_on + time_off;
|
||||
period = pca963x_period_scale(pca963x, 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -222,7 +267,7 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
|
||||
* (time_on / period) = (GDC / 256) ->
|
||||
* GDC = ((time_on * 256) / period)
|
||||
*/
|
||||
gdc = (time_on * 256) / period;
|
||||
gdc = (pca963x_period_scale(pca963x, time_on) * 256) / period;
|
||||
|
||||
/*
|
||||
* From manual: period = ((GFRQ + 1) / 24) in seconds.
|
||||
@ -294,6 +339,9 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
|
||||
else
|
||||
pdata->blink_type = PCA963X_SW_BLINK;
|
||||
|
||||
if (of_property_read_u32(np, "nxp,period-scale", &chip->scaling))
|
||||
chip->scaling = 1000;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
@ -322,7 +370,16 @@ static int pca963x_probe(struct i2c_client *client,
|
||||
struct pca963x_chipdef *chip;
|
||||
int i, err;
|
||||
|
||||
chip = &pca963x_chipdefs[id->driver_data];
|
||||
if (id) {
|
||||
chip = &pca963x_chipdefs[id->driver_data];
|
||||
} else {
|
||||
const struct acpi_device_id *acpi_id;
|
||||
|
||||
acpi_id = acpi_match_device(pca963x_acpi_ids, &client->dev);
|
||||
if (!acpi_id)
|
||||
return -ENODEV;
|
||||
chip = &pca963x_chipdefs[acpi_id->driver_data];
|
||||
}
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (!pdata) {
|
||||
@ -391,8 +448,8 @@ static int pca963x_probe(struct i2c_client *client,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Disable LED all-call address and set normal mode */
|
||||
i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00);
|
||||
/* Disable LED all-call address, and power down initially */
|
||||
i2c_smbus_write_byte_data(client, PCA963X_MODE1, BIT(4));
|
||||
|
||||
if (pdata) {
|
||||
/* Configure output: open-drain or totem pole (push-pull) */
|
||||
@ -426,6 +483,7 @@ static struct i2c_driver pca963x_driver = {
|
||||
.driver = {
|
||||
.name = "leds-pca963x",
|
||||
.of_match_table = of_match_ptr(of_pca963x_match),
|
||||
.acpi_match_table = ACPI_PTR(pca963x_acpi_ids),
|
||||
},
|
||||
.probe = pca963x_probe,
|
||||
.remove = pca963x_remove,
|
||||
|
@ -42,7 +42,7 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
|
||||
* @evt: CPU event to be emitted
|
||||
*
|
||||
* Emit a CPU event on a CPU core, which will trigger a
|
||||
* binded LED to turn on or turn off.
|
||||
* bound LED to turn on or turn off.
|
||||
*/
|
||||
void ledtrig_cpu(enum cpu_led_event ledevt)
|
||||
{
|
||||
|
235
drivers/leds/uleds.c
Normal file
235
drivers/leds/uleds.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Userspace driver for the LED subsystem
|
||||
*
|
||||
* Copyright (C) 2016 David Lechner <david@lechnology.com>
|
||||
*
|
||||
* Based on uinput.c: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <uapi/linux/uleds.h>
|
||||
|
||||
#define ULEDS_NAME "uleds"
|
||||
|
||||
enum uleds_state {
|
||||
ULEDS_STATE_UNKNOWN,
|
||||
ULEDS_STATE_REGISTERED,
|
||||
};
|
||||
|
||||
struct uleds_device {
|
||||
struct uleds_user_dev user_dev;
|
||||
struct led_classdev led_cdev;
|
||||
struct mutex mutex;
|
||||
enum uleds_state state;
|
||||
wait_queue_head_t waitq;
|
||||
int brightness;
|
||||
bool new_data;
|
||||
};
|
||||
|
||||
static struct miscdevice uleds_misc;
|
||||
|
||||
static void uleds_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct uleds_device *udev = container_of(led_cdev, struct uleds_device,
|
||||
led_cdev);
|
||||
|
||||
if (udev->brightness != brightness) {
|
||||
udev->brightness = brightness;
|
||||
udev->new_data = true;
|
||||
wake_up_interruptible(&udev->waitq);
|
||||
}
|
||||
}
|
||||
|
||||
static int uleds_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct uleds_device *udev;
|
||||
|
||||
udev = kzalloc(sizeof(*udev), GFP_KERNEL);
|
||||
if (!udev)
|
||||
return -ENOMEM;
|
||||
|
||||
udev->led_cdev.name = udev->user_dev.name;
|
||||
udev->led_cdev.brightness_set = uleds_brightness_set;
|
||||
|
||||
mutex_init(&udev->mutex);
|
||||
init_waitqueue_head(&udev->waitq);
|
||||
udev->state = ULEDS_STATE_UNKNOWN;
|
||||
|
||||
file->private_data = udev;
|
||||
nonseekable_open(inode, file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t uleds_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct uleds_device *udev = file->private_data;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
ret = mutex_lock_interruptible(&udev->mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (udev->state == ULEDS_STATE_REGISTERED) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (count != sizeof(struct uleds_user_dev)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(&udev->user_dev, buffer,
|
||||
sizeof(struct uleds_user_dev))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
name = udev->user_dev.name;
|
||||
if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") ||
|
||||
strchr(name, '/')) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (udev->user_dev.max_brightness <= 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
udev->led_cdev.max_brightness = udev->user_dev.max_brightness;
|
||||
|
||||
ret = devm_led_classdev_register(uleds_misc.this_device,
|
||||
&udev->led_cdev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
udev->new_data = true;
|
||||
udev->state = ULEDS_STATE_REGISTERED;
|
||||
ret = count;
|
||||
|
||||
out:
|
||||
mutex_unlock(&udev->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t uleds_read(struct file *file, char __user *buffer, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct uleds_device *udev = file->private_data;
|
||||
ssize_t retval;
|
||||
|
||||
if (count < sizeof(udev->brightness))
|
||||
return 0;
|
||||
|
||||
do {
|
||||
retval = mutex_lock_interruptible(&udev->mutex);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (udev->state != ULEDS_STATE_REGISTERED) {
|
||||
retval = -ENODEV;
|
||||
} else if (!udev->new_data && (file->f_flags & O_NONBLOCK)) {
|
||||
retval = -EAGAIN;
|
||||
} else if (udev->new_data) {
|
||||
retval = copy_to_user(buffer, &udev->brightness,
|
||||
sizeof(udev->brightness));
|
||||
udev->new_data = false;
|
||||
retval = sizeof(udev->brightness);
|
||||
}
|
||||
|
||||
mutex_unlock(&udev->mutex);
|
||||
|
||||
if (retval)
|
||||
break;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK))
|
||||
retval = wait_event_interruptible(udev->waitq,
|
||||
udev->new_data ||
|
||||
udev->state != ULEDS_STATE_REGISTERED);
|
||||
} while (retval == 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static unsigned int uleds_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct uleds_device *udev = file->private_data;
|
||||
|
||||
poll_wait(file, &udev->waitq, wait);
|
||||
|
||||
if (udev->new_data)
|
||||
return POLLIN | POLLRDNORM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uleds_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct uleds_device *udev = file->private_data;
|
||||
|
||||
if (udev->state == ULEDS_STATE_REGISTERED) {
|
||||
udev->state = ULEDS_STATE_UNKNOWN;
|
||||
devm_led_classdev_unregister(uleds_misc.this_device,
|
||||
&udev->led_cdev);
|
||||
}
|
||||
kfree(udev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations uleds_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = uleds_open,
|
||||
.release = uleds_release,
|
||||
.read = uleds_read,
|
||||
.write = uleds_write,
|
||||
.poll = uleds_poll,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice uleds_misc = {
|
||||
.fops = &uleds_fops,
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = ULEDS_NAME,
|
||||
};
|
||||
|
||||
static int __init uleds_init(void)
|
||||
{
|
||||
return misc_register(&uleds_misc);
|
||||
}
|
||||
module_init(uleds_init);
|
||||
|
||||
static void __exit uleds_exit(void)
|
||||
{
|
||||
misc_deregister(&uleds_misc);
|
||||
}
|
||||
module_exit(uleds_exit);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
|
||||
MODULE_DESCRIPTION("Userspace driver for the LED subsystem");
|
||||
MODULE_LICENSE("GPL");
|
@ -756,24 +756,20 @@ config UCB1400_CORE
|
||||
module will be called ucb1400_core.
|
||||
|
||||
config MFD_PM8XXX
|
||||
tristate
|
||||
|
||||
config MFD_PM8921_CORE
|
||||
tristate "Qualcomm PM8921 PMIC chip"
|
||||
tristate "Qualcomm PM8xxx PMIC chips driver"
|
||||
depends on (ARM || HEXAGON)
|
||||
select IRQ_DOMAIN
|
||||
select MFD_CORE
|
||||
select MFD_PM8XXX
|
||||
select REGMAP
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
built-in PM8921 PMIC chip.
|
||||
built-in PM8xxx PMIC chips.
|
||||
|
||||
This is required if your board has a PM8921 and uses its features,
|
||||
This is required if your board has a PM8xxx and uses its features,
|
||||
such as: MPPs, GPIOs, regulators, interrupts, and PWM.
|
||||
|
||||
Say M here if you want to include support for PM8921 chip as a module.
|
||||
This will build a module called "pm8921-core".
|
||||
Say M here if you want to include support for PM8xxx chips as a
|
||||
module. This will build a module called "pm8xxx-core".
|
||||
|
||||
config MFD_QCOM_RPM
|
||||
tristate "Qualcomm Resource Power Manager (RPM)"
|
||||
|
@ -172,7 +172,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
|
||||
|
||||
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
|
||||
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
|
||||
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
|
||||
obj-$(CONFIG_MFD_PM8XXX) += qcom-pm8xxx.o ssbi.o
|
||||
obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
|
||||
obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
|
||||
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
|
||||
|
@ -53,7 +53,7 @@
|
||||
#define REG_HWREV 0x002 /* PMIC4 revision */
|
||||
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
|
||||
|
||||
#define PM8921_NR_IRQS 256
|
||||
#define PM8XXX_NR_IRQS 256
|
||||
|
||||
struct pm_irq_chip {
|
||||
struct regmap *regmap;
|
||||
@ -308,22 +308,22 @@ static const struct regmap_config ssbi_regmap_config = {
|
||||
.reg_write = ssbi_reg_write
|
||||
};
|
||||
|
||||
static const struct of_device_id pm8921_id_table[] = {
|
||||
static const struct of_device_id pm8xxx_id_table[] = {
|
||||
{ .compatible = "qcom,pm8018", },
|
||||
{ .compatible = "qcom,pm8058", },
|
||||
{ .compatible = "qcom,pm8921", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pm8921_id_table);
|
||||
MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
|
||||
|
||||
static int pm8921_probe(struct platform_device *pdev)
|
||||
static int pm8xxx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
int irq, rc;
|
||||
unsigned int val;
|
||||
u32 rev;
|
||||
struct pm_irq_chip *chip;
|
||||
unsigned int nirqs = PM8921_NR_IRQS;
|
||||
unsigned int nirqs = PM8XXX_NR_IRQS;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
@ -384,46 +384,46 @@ static int pm8921_probe(struct platform_device *pdev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pm8921_remove_child(struct device *dev, void *unused)
|
||||
static int pm8xxx_remove_child(struct device *dev, void *unused)
|
||||
{
|
||||
platform_device_unregister(to_platform_device(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8921_remove(struct platform_device *pdev)
|
||||
static int pm8xxx_remove(struct platform_device *pdev)
|
||||
{
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
struct pm_irq_chip *chip = platform_get_drvdata(pdev);
|
||||
|
||||
device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
|
||||
device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child);
|
||||
irq_set_chained_handler_and_data(irq, NULL, NULL);
|
||||
irq_domain_remove(chip->irqdomain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pm8921_driver = {
|
||||
.probe = pm8921_probe,
|
||||
.remove = pm8921_remove,
|
||||
static struct platform_driver pm8xxx_driver = {
|
||||
.probe = pm8xxx_probe,
|
||||
.remove = pm8xxx_remove,
|
||||
.driver = {
|
||||
.name = "pm8921-core",
|
||||
.of_match_table = pm8921_id_table,
|
||||
.name = "pm8xxx-core",
|
||||
.of_match_table = pm8xxx_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pm8921_init(void)
|
||||
static int __init pm8xxx_init(void)
|
||||
{
|
||||
return platform_driver_register(&pm8921_driver);
|
||||
return platform_driver_register(&pm8xxx_driver);
|
||||
}
|
||||
subsys_initcall(pm8921_init);
|
||||
subsys_initcall(pm8xxx_init);
|
||||
|
||||
static void __exit pm8921_exit(void)
|
||||
static void __exit pm8xxx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pm8921_driver);
|
||||
platform_driver_unregister(&pm8xxx_driver);
|
||||
}
|
||||
module_exit(pm8921_exit);
|
||||
module_exit(pm8xxx_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("PMIC 8921 core driver");
|
||||
MODULE_DESCRIPTION("PMIC 8xxx core driver");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_ALIAS("platform:pm8921-core");
|
||||
MODULE_ALIAS("platform:pm8xxx-core");
|
@ -42,16 +42,20 @@ struct led_classdev {
|
||||
#define LED_UNREGISTERING (1 << 1)
|
||||
/* Upper 16 bits reflect control information */
|
||||
#define LED_CORE_SUSPENDRESUME (1 << 16)
|
||||
#define LED_BLINK_SW (1 << 17)
|
||||
#define LED_BLINK_ONESHOT (1 << 18)
|
||||
#define LED_BLINK_ONESHOT_STOP (1 << 19)
|
||||
#define LED_BLINK_INVERT (1 << 20)
|
||||
#define LED_BLINK_BRIGHTNESS_CHANGE (1 << 21)
|
||||
#define LED_BLINK_DISABLE (1 << 22)
|
||||
#define LED_SYSFS_DISABLE (1 << 23)
|
||||
#define LED_DEV_CAP_FLASH (1 << 24)
|
||||
#define LED_HW_PLUGGABLE (1 << 25)
|
||||
#define LED_PANIC_INDICATOR (1 << 26)
|
||||
#define LED_SYSFS_DISABLE (1 << 17)
|
||||
#define LED_DEV_CAP_FLASH (1 << 18)
|
||||
#define LED_HW_PLUGGABLE (1 << 19)
|
||||
#define LED_PANIC_INDICATOR (1 << 20)
|
||||
|
||||
/* set_brightness_work / blink_timer flags, atomic, private. */
|
||||
unsigned long work_flags;
|
||||
|
||||
#define LED_BLINK_SW 0
|
||||
#define LED_BLINK_ONESHOT 1
|
||||
#define LED_BLINK_ONESHOT_STOP 2
|
||||
#define LED_BLINK_INVERT 3
|
||||
#define LED_BLINK_BRIGHTNESS_CHANGE 4
|
||||
#define LED_BLINK_DISABLE 5
|
||||
|
||||
/* Set LED brightness level
|
||||
* Must not sleep. Use brightness_set_blocking for drivers
|
||||
@ -89,6 +93,7 @@ struct led_classdev {
|
||||
unsigned long blink_delay_on, blink_delay_off;
|
||||
struct timer_list blink_timer;
|
||||
int blink_brightness;
|
||||
int new_blink_brightness;
|
||||
void (*flash_resume)(struct led_classdev *led_cdev);
|
||||
|
||||
struct work_struct set_brightness_work;
|
||||
|
@ -426,6 +426,7 @@ header-y += udp.h
|
||||
header-y += uhid.h
|
||||
header-y += uinput.h
|
||||
header-y += uio.h
|
||||
header-y += uleds.h
|
||||
header-y += ultrasound.h
|
||||
header-y += un.h
|
||||
header-y += unistd.h
|
||||
|
24
include/uapi/linux/uleds.h
Normal file
24
include/uapi/linux/uleds.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Userspace driver support for the LED subsystem
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _UAPI__ULEDS_H_
|
||||
#define _UAPI__ULEDS_H_
|
||||
|
||||
#define LED_MAX_NAME_SIZE 64
|
||||
|
||||
struct uleds_user_dev {
|
||||
char name[LED_MAX_NAME_SIZE];
|
||||
int max_brightness;
|
||||
};
|
||||
|
||||
#endif /* _UAPI__ULEDS_H_ */
|
@ -17,6 +17,7 @@ help:
|
||||
@echo ' hv - tools used when in Hyper-V clients'
|
||||
@echo ' iio - IIO tools'
|
||||
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
|
||||
@echo ' leds - LEDs tools'
|
||||
@echo ' lguest - a minimal 32-bit x86 hypervisor'
|
||||
@echo ' net - misc networking tools'
|
||||
@echo ' perf - Linux performance measurement and analysis tool'
|
||||
@ -56,7 +57,7 @@ acpi: FORCE
|
||||
cpupower: FORCE
|
||||
$(call descend,power/$@)
|
||||
|
||||
cgroup firewire hv guest spi usb virtio vm net iio gpio objtool: FORCE
|
||||
cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE
|
||||
$(call descend,$@)
|
||||
|
||||
liblockdep: FORCE
|
||||
@ -126,7 +127,7 @@ acpi_clean:
|
||||
cpupower_clean:
|
||||
$(call descend,power/cpupower,clean)
|
||||
|
||||
cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean:
|
||||
cgroup_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:
|
||||
$(call descend,$(@:_clean=),clean)
|
||||
|
||||
liblockdep_clean:
|
||||
@ -164,6 +165,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_cle
|
||||
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
|
||||
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
|
||||
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
|
||||
gpio_clean objtool_clean
|
||||
gpio_clean objtool_clean leds_clean
|
||||
|
||||
.PHONY: FORCE
|
||||
|
1
tools/leds/.gitignore
vendored
Normal file
1
tools/leds/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
uledmon
|
13
tools/leds/Makefile
Normal file
13
tools/leds/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
# Makefile for LEDs tools
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CFLAGS = -Wall -Wextra -g -I../../include/uapi
|
||||
|
||||
all: uledmon
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) uledmon
|
||||
|
||||
.PHONY: all clean
|
63
tools/leds/uledmon.c
Normal file
63
tools/leds/uledmon.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* uledmon.c
|
||||
*
|
||||
* This program creates a new userspace LED class device and monitors it. A
|
||||
* timestamp and brightness value is printed each time the brightness changes.
|
||||
*
|
||||
* Usage: uledmon <device-name>
|
||||
*
|
||||
* <device-name> is the name of the LED class device to be created. Pressing
|
||||
* CTRL+C will exit.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/uleds.h>
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
struct uleds_user_dev uleds_dev;
|
||||
int fd, ret;
|
||||
int brightness;
|
||||
struct timespec ts;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Requires <device-name> argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
strncpy(uleds_dev.name, argv[1], LED_MAX_NAME_SIZE);
|
||||
uleds_dev.max_brightness = 100;
|
||||
|
||||
fd = open("/dev/uleds", O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror("Failed to open /dev/uleds");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = write(fd, &uleds_dev, sizeof(uleds_dev));
|
||||
if (ret == -1) {
|
||||
perror("Failed to write to /dev/uleds");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ret = read(fd, &brightness, sizeof(brightness));
|
||||
if (ret == -1) {
|
||||
perror("Failed to read from /dev/uleds");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
printf("[%ld.%09ld] %u\n", ts.tv_sec, ts.tv_nsec, brightness);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user