aboutsummaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig64
-rw-r--r--drivers/leds/Makefile5
-rw-r--r--drivers/leds/dell-led.c171
-rw-r--r--drivers/leds/led-class.c48
-rw-r--r--drivers/leds/led-core.c6
-rw-r--r--drivers/leds/led-triggers.c21
-rw-r--r--drivers/leds/leds-88pm860x.c10
-rw-r--r--drivers/leds/leds-adp5520.c12
-rw-r--r--drivers/leds/leds-asic3.c5
-rw-r--r--drivers/leds/leds-atmel-pwm.c5
-rw-r--r--drivers/leds/leds-bd2802.c6
-rw-r--r--drivers/leds/leds-blinkm.c6
-rw-r--r--drivers/leds/leds-clevo-mail.c7
-rw-r--r--drivers/leds/leds-cobalt-qube.c1
-rw-r--r--drivers/leds/leds-da903x.c7
-rw-r--r--drivers/leds/leds-da9052.c8
-rw-r--r--drivers/leds/leds-dac124s085.c3
-rw-r--r--drivers/leds/leds-fsg.c1
-rw-r--r--drivers/leds/leds-gpio.c23
-rw-r--r--drivers/leds/leds-hp6xx.c1
-rw-r--r--drivers/leds/leds-lm3530.c2
-rw-r--r--drivers/leds/leds-lm3533.c3
-rw-r--r--drivers/leds/leds-lm355x.c2
-rw-r--r--drivers/leds/leds-lm3642.c2
-rw-r--r--drivers/leds/leds-lp3944.c7
-rw-r--r--drivers/leds/leds-lp5521.c157
-rw-r--r--drivers/leds/leds-lp5523.c378
-rw-r--r--drivers/leds/leds-lp5562.c40
-rw-r--r--drivers/leds/leds-lp55xx-common.c78
-rw-r--r--drivers/leds/leds-lp55xx-common.h70
-rw-r--r--drivers/leds/leds-lp8501.c411
-rw-r--r--drivers/leds/leds-lt3593.c5
-rw-r--r--drivers/leds/leds-mc13783.c499
-rw-r--r--drivers/leds/leds-netxbig.c7
-rw-r--r--drivers/leds/leds-ns2.c6
-rw-r--r--drivers/leds/leds-ot200.c1
-rw-r--r--drivers/leds/leds-pca9532.c3
-rw-r--r--drivers/leds/leds-pca955x.c2
-rw-r--r--drivers/leds/leds-pca9633.c194
-rw-r--r--drivers/leds/leds-pca963x.c461
-rw-r--r--drivers/leds/leds-pwm.c213
-rw-r--r--drivers/leds/leds-regulator.c3
-rw-r--r--drivers/leds/leds-renesas-tpu.c337
-rw-r--r--drivers/leds/leds-s3c24xx.c11
-rw-r--r--drivers/leds/leds-ss4200.c8
-rw-r--r--drivers/leds/leds-sunfire.c8
-rw-r--r--drivers/leds/leds-tca6507.c209
-rw-r--r--drivers/leds/leds-versatile.c110
-rw-r--r--drivers/leds/leds-wm831x-status.c11
-rw-r--r--drivers/leds/leds-wm8350.c3
-rw-r--r--drivers/leds/trigger/ledtrig-backlight.c30
-rw-r--r--drivers/leds/trigger/ledtrig-cpu.c26
52 files changed, 2440 insertions, 1267 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ef992293598..a1b044e7eaa 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -194,11 +194,11 @@ config LEDS_LP3944
module will be called leds-lp3944.
config LEDS_LP55XX_COMMON
- tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
- depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
+ tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
+ depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
select FW_LOADER
help
- This option supports common operations for LP5521 and LP5523/55231
+ This option supports common operations for LP5521/5523/55231/5562/8501
devices.
config LEDS_LP5521
@@ -232,6 +232,18 @@ config LEDS_LP5562
Driver provides direct control via LED class and interface for
programming the engines.
+config LEDS_LP8501
+ tristate "LED Support for TI LP8501 LED driver chip"
+ depends on LEDS_CLASS && I2C
+ select LEDS_LP55XX_COMMON
+ help
+ If you say yes here you get support for TI LP8501 LED driver.
+ It is 9 channel chip with programmable engines.
+ Driver provides direct control via LED class and interface for
+ programming the engines.
+ It is similar as LP5523, but output power selection is available.
+ And register layout and engine program schemes are different.
+
config LEDS_LP8788
tristate "LED support for the TI LP8788 PMIC"
depends on LEDS_CLASS
@@ -279,13 +291,14 @@ config LEDS_PCA955X
LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
-config LEDS_PCA9633
- tristate "LED support for PCA9633 I2C chip"
+config LEDS_PCA963X
+ tristate "LED support for PCA963x I2C chip"
depends on LEDS_CLASS
depends on I2C
help
- This option enables support for LEDs connected to the PCA9633
- LED driver chip accessed via the I2C bus.
+ This option enables support for LEDs connected to the PCA963x
+ LED driver chip accessed via the I2C bus. Supported
+ devices include PCA9633 and PCA9634
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
@@ -388,20 +401,17 @@ config LEDS_DELL_NETBOOKS
notebooks that have an external LED.
config LEDS_MC13783
- tristate "LED Support for MC13783 PMIC"
+ tristate "LED Support for MC13XXX PMIC"
depends on LEDS_CLASS
- depends on MFD_MC13783
+ depends on MFD_MC13XXX
help
This option enable support for on-chip LED drivers found
- on Freescale Semiconductor MC13783 PMIC.
+ on Freescale Semiconductor MC13783/MC13892/MC34708 PMIC.
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
depends on LEDS_CLASS
- depends on MACH_NETSPACE_V2 || MACH_INETSPACE_V2 || \
- MACH_NETSPACE_MAX_V2 || MACH_D2NET_V2 || \
- MACH_NETSPACE_V2_DT || MACH_INETSPACE_V2_DT || \
- MACH_NETSPACE_MAX_V2_DT || MACH_NETSPACE_MINI_V2_DT
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
default y
help
This option enable support for the dual-GPIO LED found on the
@@ -410,8 +420,8 @@ config LEDS_NS2
config LEDS_NETXBIG
tristate "LED support for Big Network series LEDs"
- depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
depends on LEDS_CLASS
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
default y
help
This option enable support for LEDs found on the LaCie 2Big
@@ -429,18 +439,6 @@ config LEDS_ASIC3
cannot be used. This driver supports hardware blinking with an on+off
period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700.
-config LEDS_RENESAS_TPU
- bool "LED support for Renesas TPU"
- depends on LEDS_CLASS=y && HAVE_CLK && GPIOLIB
- help
- This option enables build of the LED TPU platform driver,
- suitable to drive any TPU channel on newer Renesas SoCs.
- The driver controls the GPIO pin connected to the LED via
- the GPIO framework and expects the LED to be connected to
- a pin that can be driven in both GPIO mode and using TPU
- pin function. The latter to support brightness control.
- Brightness control is supported but hardware blinking is not.
-
config LEDS_TCA6507
tristate "LED Support for TCA6507 I2C chip"
depends on LEDS_CLASS && I2C
@@ -466,11 +464,13 @@ config LEDS_LM355x
config LEDS_OT200
tristate "LED support for the Bachmann OT200"
- depends on LEDS_CLASS && HAS_IOMEM
+ depends on LEDS_CLASS && HAS_IOMEM && (X86_32 || COMPILE_TEST)
help
This option enables support for the LEDs on the Bachmann OT200.
Say Y to enable LEDs on the Bachmann OT200.
+comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
+
config LEDS_BLINKM
tristate "LED support for the BlinkM I2C RGB LED"
depends on LEDS_CLASS
@@ -479,6 +479,14 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
+config LEDS_VERSATILE
+ tristate "LED support for the ARM Versatile and RealView"
+ depends on ARCH_REALVIEW || ARCH_VERSATILE
+ depends on LEDS_CLASS
+ help
+ This option enabled support for the LEDs on the ARM Versatile
+ and RealView boards. Say Y to enabled these.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ac2897732b0..79c5155199a 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
+obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
@@ -34,7 +35,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_OT200) += leds-ot200.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
-obj-$(CONFIG_LEDS_PCA9633) += leds-pca9633.o
+obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
@@ -49,10 +50,10 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
-obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/dell-led.c b/drivers/leds/dell-led.c
index e5c57389efd..c36acaf566a 100644
--- a/drivers/leds/dell-led.c
+++ b/drivers/leds/dell-led.c
@@ -15,12 +15,15 @@
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/dell-led.h>
MODULE_AUTHOR("Louis Davis/Jim Dailey");
MODULE_DESCRIPTION("Dell LED Control Driver");
MODULE_LICENSE("GPL");
#define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
+#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
/* Error Result Codes: */
@@ -39,6 +42,149 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
#define CMD_LED_OFF 17
#define CMD_LED_BLINK 18
+struct app_wmi_args {
+ u16 class;
+ u16 selector;
+ u32 arg1;
+ u32 arg2;
+ u32 arg3;
+ u32 arg4;
+ u32 res1;
+ u32 res2;
+ u32 res3;
+ u32 res4;
+ char dummy[92];
+};
+
+#define GLOBAL_MIC_MUTE_ENABLE 0x364
+#define GLOBAL_MIC_MUTE_DISABLE 0x365
+
+struct dell_bios_data_token {
+ u16 tokenid;
+ u16 location;
+ u16 value;
+};
+
+struct __attribute__ ((__packed__)) dell_bios_calling_interface {
+ struct dmi_header header;
+ u16 cmd_io_addr;
+ u8 cmd_io_code;
+ u32 supported_cmds;
+ struct dell_bios_data_token damap[];
+};
+
+static struct dell_bios_data_token dell_mic_tokens[2];
+
+static int dell_wmi_perform_query(struct app_wmi_args *args)
+{
+ struct app_wmi_args *bios_return;
+ union acpi_object *obj;
+ struct acpi_buffer input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ u32 rc = -EINVAL;
+
+ input.length = 128;
+ input.pointer = args;
+
+ status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output);
+ if (!ACPI_SUCCESS(status))
+ goto err_out0;
+
+ obj = output.pointer;
+ if (!obj)
+ goto err_out0;
+
+ if (obj->type != ACPI_TYPE_BUFFER)
+ goto err_out1;
+
+ bios_return = (struct app_wmi_args *)obj->buffer.pointer;
+ rc = bios_return->res1;
+ if (rc)
+ goto err_out1;
+
+ memcpy(args, bios_return, sizeof(struct app_wmi_args));
+ rc = 0;
+
+ err_out1:
+ kfree(obj);
+ err_out0:
+ return rc;
+}
+
+static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy)
+{
+ struct dell_bios_calling_interface *calling_interface;
+ struct dell_bios_data_token *token;
+ int token_size = sizeof(struct dell_bios_data_token);
+ int i = 0;
+
+ if (dm->type == 0xda && dm->length > 17) {
+ calling_interface = container_of(dm,
+ struct dell_bios_calling_interface, header);
+
+ token = &calling_interface->damap[i];
+ while (token->tokenid != 0xffff) {
+ if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE)
+ memcpy(&dell_mic_tokens[0], token, token_size);
+ else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE)
+ memcpy(&dell_mic_tokens[1], token, token_size);
+
+ i++;
+ token = &calling_interface->damap[i];
+ }
+ }
+}
+
+static int dell_micmute_led_set(int state)
+{
+ struct app_wmi_args args;
+ struct dell_bios_data_token *token;
+
+ if (!wmi_has_guid(DELL_APP_GUID))
+ return -ENODEV;
+
+ if (state == 0 || state == 1)
+ token = &dell_mic_tokens[state];
+ else
+ return -EINVAL;
+
+ memset(&args, 0, sizeof(struct app_wmi_args));
+
+ args.class = 1;
+ args.arg1 = token->location;
+ args.arg2 = token->value;
+
+ dell_wmi_perform_query(&args);
+
+ return state;
+}
+
+int dell_app_wmi_led_set(int whichled, int on)
+{
+ int state = 0;
+
+ switch (whichled) {
+ case DELL_LED_MICMUTE:
+ state = dell_micmute_led_set(on);
+ break;
+ default:
+ pr_warn("led type %x is not supported\n", whichled);
+ break;
+ }
+
+ return state;
+}
+EXPORT_SYMBOL_GPL(dell_app_wmi_led_set);
+
+static int __init dell_micmute_led_init(void)
+{
+ memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2);
+ dmi_walk(find_micmute_tokens, NULL);
+
+ return 0;
+}
+
struct bios_args {
unsigned char length;
unsigned char result_code;
@@ -181,21 +327,32 @@ static int __init dell_led_init(void)
{
int error = 0;
- if (!wmi_has_guid(DELL_LED_BIOS_GUID))
+ if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID))
return -ENODEV;
- error = led_off();
- if (error != 0)
- return -ENODEV;
+ if (wmi_has_guid(DELL_APP_GUID))
+ error = dell_micmute_led_init();
- return led_classdev_register(NULL, &dell_led);
+ if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
+ error = led_off();
+ if (error != 0)
+ return -ENODEV;
+
+ error = led_classdev_register(NULL, &dell_led);
+ }
+
+ return error;
}
static void __exit dell_led_exit(void)
{
- led_classdev_unregister(&dell_led);
+ int error = 0;
- led_off();
+ if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
+ error = led_off();
+ if (error == 0)
+ led_classdev_unregister(&dell_led);
+ }
}
module_init(dell_led_init);
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index a20752f562b..f37d63cf726 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -29,7 +29,7 @@ static void led_update_brightness(struct led_classdev *led_cdev)
led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}
-static ssize_t led_brightness_show(struct device *dev,
+static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -40,7 +40,7 @@ static ssize_t led_brightness_show(struct device *dev,
return sprintf(buf, "%u\n", led_cdev->brightness);
}
-static ssize_t led_brightness_store(struct device *dev,
+static ssize_t brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -57,6 +57,7 @@ static ssize_t led_brightness_store(struct device *dev,
return size;
}
+static DEVICE_ATTR_RW(brightness);
static ssize_t led_max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -65,14 +66,35 @@ static ssize_t led_max_brightness_show(struct device *dev,
return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
+static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
-static struct device_attribute led_class_attrs[] = {
- __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
- __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS
- __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
+static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
+static struct attribute *led_trigger_attrs[] = {
+ &dev_attr_trigger.attr,
+ NULL,
+};
+static const struct attribute_group led_trigger_group = {
+ .attrs = led_trigger_attrs,
+};
#endif
- __ATTR_NULL,
+
+static struct attribute *led_class_attrs[] = {
+ &dev_attr_brightness.attr,
+ &dev_attr_max_brightness.attr,
+ NULL,
+};
+
+static const struct attribute_group led_group = {
+ .attrs = led_class_attrs,
+};
+
+static const struct attribute_group *led_groups[] = {
+ &led_group,
+#ifdef CONFIG_LEDS_TRIGGERS
+ &led_trigger_group,
+#endif
+ NULL,
};
static void led_timer_function(unsigned long data)
@@ -156,7 +178,7 @@ void led_classdev_resume(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_classdev_resume);
-static int led_suspend(struct device *dev, pm_message_t state)
+static int led_suspend(struct device *dev)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -176,6 +198,11 @@ static int led_resume(struct device *dev)
return 0;
}
+static const struct dev_pm_ops leds_class_dev_pm_ops = {
+ .suspend = led_suspend,
+ .resume = led_resume,
+};
+
/**
* led_classdev_register - register a new object of led_classdev class.
* @parent: The device to register.
@@ -252,9 +279,8 @@ static int __init leds_init(void)
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
- leds_class->suspend = led_suspend;
- leds_class->resume = led_resume;
- leds_class->dev_attrs = led_class_attrs;
+ leds_class->pm = &leds_class_dev_pm_ops;
+ leds_class->dev_groups = led_groups;
return 0;
}
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index ce8921a753a..71b40d3bf77 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -39,9 +39,11 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
led_cdev->blink_delay_on = delay_on;
led_cdev->blink_delay_off = delay_off;
- /* never on - don't blink */
- if (!delay_on)
+ /* never on - just set to off */
+ if (!delay_on) {
+ __led_set_brightness(led_cdev, LED_OFF);
return;
+ }
/* never off - just set to brightness */
if (!delay_off) {
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 3c972b2f989..c3734f10fdd 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
@@ -220,9 +219,12 @@ void led_trigger_unregister(struct led_trigger *trig)
{
struct led_classdev *led_cdev;
+ if (list_empty_careful(&trig->next_trig))
+ return;
+
/* Remove from the list of led triggers */
down_write(&triggers_list_lock);
- list_del(&trig->next_trig);
+ list_del_init(&trig->next_trig);
up_write(&triggers_list_lock);
/* Remove anyone actively using this trigger */
@@ -242,18 +244,14 @@ EXPORT_SYMBOL_GPL(led_trigger_unregister);
void led_trigger_event(struct led_trigger *trig,
enum led_brightness brightness)
{
- struct list_head *entry;
+ struct led_classdev *led_cdev;
if (!trig)
return;
read_lock(&trig->leddev_list_lock);
- list_for_each(entry, &trig->led_cdevs) {
- struct led_classdev *led_cdev;
-
- led_cdev = list_entry(entry, struct led_classdev, trig_list);
+ list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
led_set_brightness(led_cdev, brightness);
- }
read_unlock(&trig->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_event);
@@ -264,16 +262,13 @@ static void led_trigger_blink_setup(struct led_trigger *trig,
int oneshot,
int invert)
{
- struct list_head *entry;
+ struct led_classdev *led_cdev;
if (!trig)
return;
read_lock(&trig->leddev_list_lock);
- list_for_each(entry, &trig->led_cdevs) {
- struct led_classdev *led_cdev;
-
- led_cdev = list_entry(entry, struct led_classdev, trig_list);
+ list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
if (oneshot)
led_blink_set_oneshot(led_cdev, delay_on, delay_off,
invert);
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index f5b9ea31579..c2def5551ce 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
@@ -131,10 +130,9 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
struct device_node *nproot, *np;
int iset = 0;
- nproot = of_node_get(pdev->dev.parent->of_node);
- if (!nproot)
+ if (!pdev->dev.parent->of_node)
return -ENODEV;
- nproot = of_find_node_by_name(nproot, "leds");
+ nproot = of_get_child_by_name(pdev->dev.parent->of_node, "leds");
if (!nproot) {
dev_err(&pdev->dev, "failed to find leds node\n");
return -ENODEV;
@@ -157,7 +155,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
static int pm860x_led_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
- struct pm860x_led_pdata *pdata = pdev->dev.platform_data;
+ struct pm860x_led_pdata *pdata = dev_get_platdata(&pdev->dev);
struct pm860x_led *data;
struct resource *res;
int ret = 0;
@@ -204,7 +202,7 @@ static int pm860x_led_probe(struct platform_device *pdev)
sprintf(data->name, "led1-blue");
break;
}
- dev_set_drvdata(&pdev->dev, data);
+ platform_set_drvdata(pdev, data);
data->chip = chip;
data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
data->port = pdev->id;
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
index e8072abe76e..5036d7b4f82 100644
--- a/drivers/leds/leds-adp5520.c
+++ b/drivers/leds/leds-adp5520.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
@@ -87,7 +86,7 @@ static int adp5520_led_setup(struct adp5520_led *led)
static int adp5520_led_prepare(struct platform_device *pdev)
{
- struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = pdev->dev.parent;
int ret = 0;
@@ -103,7 +102,7 @@ static int adp5520_led_prepare(struct platform_device *pdev)
static int adp5520_led_probe(struct platform_device *pdev)
{
- struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct adp5520_led *led, *led_dat;
struct led_info *cur_led;
int ret, i;
@@ -121,13 +120,10 @@ static int adp5520_led_probe(struct platform_device *pdev)
led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds,
GFP_KERNEL);
- if (led == NULL) {
- dev_err(&pdev->dev, "failed to alloc memory\n");
+ if (!led)
return -ENOMEM;
- }
ret = adp5520_led_prepare(pdev);
-
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
return ret;
@@ -185,7 +181,7 @@ err:
static int adp5520_led_remove(struct platform_device *pdev)
{
- struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct adp5520_led *led;
int i;
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
index cf9efe421c2..70c74a7f0df 100644
--- a/drivers/leds/leds-asic3.c
+++ b/drivers/leds/leds-asic3.c
@@ -7,7 +7,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/slab.h>
@@ -94,7 +93,7 @@ static int blink_set(struct led_classdev *cdev,
static int asic3_led_probe(struct platform_device *pdev)
{
- struct asic3_led *led = pdev->dev.platform_data;
+ struct asic3_led *led = dev_get_platdata(&pdev->dev);
int ret;
ret = mfd_cell_enable(pdev);
@@ -127,7 +126,7 @@ out:
static int asic3_led_remove(struct platform_device *pdev)
{
- struct asic3_led *led = pdev->dev.platform_data;
+ struct asic3_led *led = dev_get_platdata(&pdev->dev);
led_classdev_unregister(led->cdev);
diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c
index 8a39c5b20f7..56cec8d6a2a 100644
--- a/drivers/leds/leds-atmel-pwm.c
+++ b/drivers/leds/leds-atmel-pwm.c
@@ -42,7 +42,7 @@ static int pwmled_probe(struct platform_device *pdev)
int i;
int status;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata || pdata->num_leds < 1)
return -ENODEV;
@@ -119,7 +119,7 @@ static int pwmled_remove(struct platform_device *pdev)
struct pwmled *leds;
unsigned i;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
leds = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++) {
@@ -129,7 +129,6 @@ static int pwmled_remove(struct platform_device *pdev)
pwm_channel_free(&led->pwmc);
}
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 2db04231a79..6078c15d345 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -678,13 +678,11 @@ static int bd2802_probe(struct i2c_client *client,
int ret, i;
led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
- if (!led) {
- dev_err(&client->dev, "failed to allocate driver data\n");
+ if (!led)
return -ENOMEM;
- }
led->client = client;
- pdata = led->pdata = client->dev.platform_data;
+ pdata = led->pdata = dev_get_platdata(&client->dev);
i2c_set_clientdata(client, led);
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */
diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c
index a502678cc7f..d0452b099ae 100644
--- a/drivers/leds/leds-blinkm.c
+++ b/drivers/leds/leds-blinkm.c
@@ -18,7 +18,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
@@ -161,13 +160,10 @@ static ssize_t show_color_common(struct device *dev, char *buf, int color)
switch (color) {
case RED:
return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red);
- break;
case GREEN:
return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green);
- break;
case BLUE:
return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue);
- break;
default:
return -EINVAL;
}
@@ -447,7 +443,7 @@ static void led_work(struct work_struct *work)
{
int ret;
struct blinkm_led *led;
- struct blinkm_data *data ;
+ struct blinkm_data *data;
struct blinkm_work *blm_work = work_to_blmwork(work);
led = blm_work->blinkm_led;
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
index 6a8405df76a..f58a354428e 100644
--- a/drivers/leds/leds-clevo-mail.c
+++ b/drivers/leds/leds-clevo-mail.c
@@ -19,7 +19,7 @@ MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
MODULE_DESCRIPTION("Clevo mail LED driver");
MODULE_LICENSE("GPL");
-static bool __initdata nodetect;
+static bool nodetect;
module_param_named(nodetect, nodetect, bool, 0);
MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
@@ -40,7 +40,7 @@ static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
* detected as working, but in reality it is not) as low as
* possible.
*/
-static struct dmi_system_id __initdata clevo_mail_led_dmi_table[] = {
+static struct dmi_system_id clevo_mail_led_dmi_table[] __initdata = {
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Clevo D410J",
@@ -153,7 +153,7 @@ static struct led_classdev clevo_mail_led = {
.flags = LED_CORE_SUSPENDRESUME,
};
-static int clevo_mail_led_probe(struct platform_device *pdev)
+static int __init clevo_mail_led_probe(struct platform_device *pdev)
{
return led_classdev_register(&pdev->dev, &clevo_mail_led);
}
@@ -165,7 +165,6 @@ static int clevo_mail_led_remove(struct platform_device *pdev)
}
static struct platform_driver clevo_mail_led_driver = {
- .probe = clevo_mail_led_probe,
.remove = clevo_mail_led_remove,
.driver = {
.name = KBUILD_MODNAME,
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c
index 8abcb66db01..910339d86ed 100644
--- a/drivers/leds/leds-cobalt-qube.c
+++ b/drivers/leds/leds-cobalt-qube.c
@@ -3,7 +3,6 @@
*
* Control the Cobalt Qube/RaQ front LED
*/
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/leds.h>
diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c
index c263a21db82..54b8b5216b8 100644
--- a/drivers/leds/leds-da903x.c
+++ b/drivers/leds/leds-da903x.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
@@ -93,7 +92,7 @@ static void da903x_led_set(struct led_classdev *led_cdev,
static int da903x_led_probe(struct platform_device *pdev)
{
- struct led_info *pdata = pdev->dev.platform_data;
+ struct led_info *pdata = dev_get_platdata(&pdev->dev);
struct da903x_led *led;
int id, ret;
@@ -109,10 +108,8 @@ static int da903x_led_probe(struct platform_device *pdev)
}
led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL);
- if (led == NULL) {
- dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id);
+ if (!led)
return -ENOMEM;
- }
led->cdev.name = pdata->name;
led->cdev.default_trigger = pdata->default_trigger;
diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c
index efec43344e9..e4da1f460ac 100644
--- a/drivers/leds/leds-da9052.c
+++ b/drivers/leds/leds-da9052.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
@@ -112,7 +111,7 @@ static int da9052_led_probe(struct platform_device *pdev)
int i;
da9052 = dev_get_drvdata(pdev->dev.parent);
- pdata = da9052->dev->platform_data;
+ pdata = dev_get_platdata(da9052->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data\n");
goto err;
@@ -127,8 +126,7 @@ static int da9052_led_probe(struct platform_device *pdev)
led = devm_kzalloc(&pdev->dev,
sizeof(struct da9052_led) * pled->num_leds,
GFP_KERNEL);
- if (led == NULL) {
- dev_err(&pdev->dev, "Failed to alloc memory\n");
+ if (!led) {
error = -ENOMEM;
goto err;
}
@@ -185,7 +183,7 @@ static int da9052_led_remove(struct platform_device *pdev)
int i;
da9052 = dev_get_drvdata(pdev->dev.parent);
- pdata = da9052->dev->platform_data;
+ pdata = dev_get_platdata(da9052->dev);
pled = pdata->pled;
for (i = 0; i < pled->num_leds; i++) {
diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c
index 1f9d8e62d37..db3ba8b4251 100644
--- a/drivers/leds/leds-dac124s085.c
+++ b/drivers/leds/leds-dac124s085.c
@@ -101,7 +101,6 @@ eledcr:
while (i--)
led_classdev_unregister(&dac->leds[i].ldev);
- spi_set_drvdata(spi, NULL);
return ret;
}
@@ -115,8 +114,6 @@ static int dac124s085_remove(struct spi_device *spi)
cancel_work_sync(&dac->leds[i].work);
}
- spi_set_drvdata(spi, NULL);
-
return 0;
}
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
index b4d5a44cc41..2b4dc738dcd 100644
--- a/drivers/leds/leds-fsg.c
+++ b/drivers/leds/leds-fsg.c
@@ -16,7 +16,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/module.h>
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index b02b679abf3..57ff20fecf5 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -11,16 +11,15 @@
*
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/module.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/err.h>
struct gpio_led_data {
@@ -171,11 +170,11 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
int count, ret;
/* count LEDs in this device, so we know how much to allocate */
- count = of_get_child_count(np);
+ count = of_get_available_child_count(np);
if (!count)
return ERR_PTR(-ENODEV);
- for_each_child_of_node(np, child)
+ for_each_available_child_of_node(np, child)
if (of_get_gpio(child, 0) == -EPROBE_DEFER)
return ERR_PTR(-EPROBE_DEFER);
@@ -184,7 +183,7 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
if (!priv)
return ERR_PTR(-ENOMEM);
- for_each_child_of_node(np, child) {
+ for_each_available_child_of_node(np, child) {
struct gpio_led led = {};
enum of_gpio_flags flags;
const char *state;
@@ -204,6 +203,9 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
led.default_state = LEDS_GPIO_DEFSTATE_OFF;
}
+ if (of_get_property(child, "retain-state-suspended", NULL))
+ led.retain_state_suspended = 1;
+
ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
&pdev->dev, NULL);
if (ret < 0) {
@@ -224,6 +226,8 @@ static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
{},
};
+
+MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
#else /* CONFIG_OF_GPIO */
static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
{
@@ -234,15 +238,10 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
static int gpio_led_probe(struct platform_device *pdev)
{
- struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct gpio_leds_priv *priv;
- struct pinctrl *pinctrl;
int i, ret = 0;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl))
- dev_warn(&pdev->dev,
- "pins are not configured from the driver\n");
if (pdata && pdata->num_leds) {
priv = devm_kzalloc(&pdev->dev,
@@ -282,8 +281,6 @@ static int gpio_led_remove(struct platform_device *pdev)
for (i = 0; i < priv->num_leds; i++)
delete_gpio_led(&priv->leds[i]);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c
index 366b6055e33..d61a98896c7 100644
--- a/drivers/leds/leds-hp6xx.c
+++ b/drivers/leds/leds-hp6xx.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <asm/hd64461.h>
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index a036a19040f..652368c2ea9 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -403,7 +403,7 @@ static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
static int lm3530_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lm3530_platform_data *pdata = client->dev.platform_data;
+ struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev);
struct lm3530_data *drvdata;
int err = 0;
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
index bbf24d038a7..e2c642c1169 100644
--- a/drivers/leds/leds-lm3533.c
+++ b/drivers/leds/leds-lm3533.c
@@ -12,7 +12,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/mfd/core.h>
#include <linux/mutex.h>
@@ -671,7 +670,7 @@ static int lm3533_led_probe(struct platform_device *pdev)
if (!lm3533)
return -EINVAL;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index d81a8e7afd6..591eb5e58ae 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -423,7 +423,7 @@ static const struct regmap_config lm355x_regmap = {
static int lm355x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lm355x_platform_data *pdata = client->dev.platform_data;
+ struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev);
struct lm355x_chip_data *chip;
int err;
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index f361bbef2de..ceb6b3cde6f 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -316,7 +316,7 @@ static const struct regmap_config lm3642_regmap = {
static int lm3642_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lm3642_platform_data *pdata = client->dev.platform_data;
+ struct lm3642_platform_data *pdata = dev_get_platdata(&client->dev);
struct lm3642_chip_data *chip;
int err;
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c
index 0c4386e656c..8e1abdcd4c9 100644
--- a/drivers/leds/leds-lp3944.c
+++ b/drivers/leds/leds-lp3944.c
@@ -289,7 +289,7 @@ static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
dev_dbg(&led->client->dev, "%s: %s, %d\n",
__func__, led_cdev->name, brightness);
- led->status = brightness;
+ led->status = !!brightness;
schedule_work(&led->work);
}
@@ -377,7 +377,8 @@ exit:
static int lp3944_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data;
+ struct lp3944_platform_data *lp3944_pdata =
+ dev_get_platdata(&client->dev);
struct lp3944_data *data;
int err;
@@ -413,7 +414,7 @@ static int lp3944_probe(struct i2c_client *client,
static int lp3944_remove(struct i2c_client *client)
{
- struct lp3944_platform_data *pdata = client->dev.platform_data;
+ struct lp3944_platform_data *pdata = dev_get_platdata(&client->dev);
struct lp3944_data *data = i2c_get_clientdata(client);
int i;
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 19752c928aa..8ca197af286 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -25,12 +25,12 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include "leds-lp55xx-common.h"
@@ -151,12 +151,26 @@ static void lp5521_load_engine(struct lp55xx_chip *chip)
lp5521_wait_opmode_done();
}
-static void lp5521_stop_engine(struct lp55xx_chip *chip)
+static void lp5521_stop_all_engines(struct lp55xx_chip *chip)
{
lp55xx_write(chip, LP5521_REG_OP_MODE, 0);
lp5521_wait_opmode_done();
}
+static void lp5521_stop_engine(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
+ u8 mask[] = {
+ [LP55XX_ENGINE_1] = LP5521_MODE_R_M,
+ [LP55XX_ENGINE_2] = LP5521_MODE_G_M,
+ [LP55XX_ENGINE_3] = LP5521_MODE_B_M,
+ };
+
+ lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], 0);
+
+ lp5521_wait_opmode_done();
+}
+
static void lp5521_run_engine(struct lp55xx_chip *chip, bool start)
{
int ret;
@@ -219,17 +233,11 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
};
unsigned cmd;
char c[3];
- int program_size;
int nrchars;
- int offset = 0;
int ret;
- int i;
-
- /* clear program memory before updating */
- for (i = 0; i < LP5521_PROGRAM_LENGTH; i++)
- lp55xx_write(chip, addr[idx] + i, 0);
+ int offset = 0;
+ int i = 0;
- i = 0;
while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) {
/* separate sscanfs because length is working only for %s */
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
@@ -249,11 +257,13 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
if (i % 2)
goto err;
- program_size = i;
- for (i = 0; i < program_size; i++)
- lp55xx_write(chip, addr[idx] + i, pattern[i]);
+ for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) {
+ ret = lp55xx_write(chip, addr[idx] + i, pattern[i]);
+ if (ret)
+ return -EINVAL;
+ }
- return 0;
+ return size;
err:
dev_err(&chip->cl->dev, "wrong pattern format\n");
@@ -364,6 +374,82 @@ static void lp5521_led_brightness_work(struct work_struct *work)
mutex_unlock(&chip->lock);
}
+static ssize_t show_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
+
+ switch (mode) {
+ case LP55XX_ENGINE_RUN:
+ return sprintf(buf, "run\n");
+ case LP55XX_ENGINE_LOAD:
+ return sprintf(buf, "load\n");
+ case LP55XX_ENGINE_DISABLED:
+ default:
+ return sprintf(buf, "disabled\n");
+ }
+}
+show_mode(1)
+show_mode(2)
+show_mode(3)
+
+static ssize_t store_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+
+ if (!strncmp(buf, "run", 3)) {
+ lp5521_run_engine(chip, true);
+ engine->mode = LP55XX_ENGINE_RUN;
+ } else if (!strncmp(buf, "load", 4)) {
+ lp5521_stop_engine(chip);
+ lp5521_load_engine(chip);
+ engine->mode = LP55XX_ENGINE_LOAD;
+ } else if (!strncmp(buf, "disabled", 8)) {
+ lp5521_stop_engine(chip);
+ engine->mode = LP55XX_ENGINE_DISABLED;
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+store_mode(1)
+store_mode(2)
+store_mode(3)
+
+static ssize_t store_engine_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+ lp5521_load_engine(chip);
+ ret = lp5521_update_program_memory(chip, buf, len);
+
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+store_load(1)
+store_load(2)
+store_load(3)
+
static ssize_t lp5521_selftest(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -380,9 +466,21 @@ static ssize_t lp5521_selftest(struct device *dev,
}
/* device attributes */
-static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
+static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
+static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
+static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
+static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
+static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
+static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
+static LP55XX_DEV_ATTR_RO(selftest, lp5521_selftest);
static struct attribute *lp5521_attributes[] = {
+ &dev_attr_engine1_mode.attr,
+ &dev_attr_engine2_mode.attr,
+ &dev_attr_engine3_mode.attr,
+ &dev_attr_engine1_load.attr,
+ &dev_attr_engine2_load.attr,
+ &dev_attr_engine3_load.attr,
&dev_attr_selftest.attr,
NULL
};
@@ -416,12 +514,20 @@ static int lp5521_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata = client->dev.platform_data;
-
- if (!pdata) {
- dev_err(&client->dev, "no platform data\n");
- return -EINVAL;
+ struct lp55xx_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+
+ if (!dev_get_platdata(&client->dev)) {
+ if (np) {
+ ret = lp55xx_of_populate_pdata(&client->dev, np);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
}
+ pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -471,7 +577,7 @@ static int lp5521_remove(struct i2c_client *client)
struct lp55xx_led *led = i2c_get_clientdata(client);
struct lp55xx_chip *chip = led->chip;
- lp5521_stop_engine(chip);
+ lp5521_stop_all_engines(chip);
lp55xx_unregister_sysfs(chip);
lp55xx_unregister_leds(led, chip);
lp55xx_deinit_device(chip);
@@ -485,9 +591,18 @@ static const struct i2c_device_id lp5521_id[] = {
};
MODULE_DEVICE_TABLE(i2c, lp5521_id);
+#ifdef CONFIG_OF
+static const struct of_device_id of_lp5521_leds_match[] = {
+ { .compatible = "national,lp5521", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_lp5521_leds_match);
+#endif
static struct i2c_driver lp5521_driver = {
.driver = {
.name = "lp5521",
+ .of_match_table = of_match_ptr(of_lp5521_leds_match),
},
.probe = lp5521_probe,
.remove = lp5521_remove,
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 229f734040a..9e1716f8098 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -1,5 +1,5 @@
/*
- * lp5523.c - LP5523 LED Driver
+ * lp5523.c - LP5523, LP55231 LED Driver
*
* Copyright (C) 2010 Nokia Corporation
* Copyright (C) 2012 Texas Instruments
@@ -25,16 +25,24 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/slab.h>
#include "leds-lp55xx-common.h"
-#define LP5523_PROGRAM_LENGTH 32
+#define LP5523_PROGRAM_LENGTH 32 /* bytes */
+/* Memory is used like this:
+ 0x00 engine 1 program
+ 0x10 engine 2 program
+ 0x20 engine 3 program
+ 0x30 engine 1 muxing info
+ 0x40 engine 2 muxing info
+ 0x50 engine 3 muxing info
+*/
#define LP5523_MAX_LEDS 9
/* Registers */
@@ -49,6 +57,9 @@
#define LP5523_REG_RESET 0x3D
#define LP5523_REG_LED_TEST_CTRL 0x41
#define LP5523_REG_LED_TEST_ADC 0x42
+#define LP5523_REG_CH1_PROG_START 0x4C
+#define LP5523_REG_CH2_PROG_START 0x4D
+#define LP5523_REG_CH3_PROG_START 0x4E
#define LP5523_REG_PROG_PAGE_SEL 0x4F
#define LP5523_REG_PROG_MEM 0x50
@@ -65,11 +76,15 @@
#define LP5523_RESET 0xFF
#define LP5523_ADC_SHORTCIRC_LIM 80
#define LP5523_EXT_CLK_USED 0x08
+#define LP5523_ENG_STATUS_MASK 0x07
/* Memory Page Selection */
#define LP5523_PAGE_ENG1 0
#define LP5523_PAGE_ENG2 1
#define LP5523_PAGE_ENG3 2
+#define LP5523_PAGE_MUX1 3
+#define LP5523_PAGE_MUX2 4
+#define LP5523_PAGE_MUX3 5
/* Program Memory Operations */
#define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */
@@ -94,11 +109,15 @@
#define LP5523_RUN_ENG2 0x08
#define LP5523_RUN_ENG3 0x02
+#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led)))
+
enum lp5523_chip_id {
LP5523,
LP55231,
};
+static int lp5523_init_program_engine(struct lp55xx_chip *chip);
+
static inline void lp5523_wait_opmode_done(void)
{
usleep_range(1000, 2000);
@@ -134,7 +153,11 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip)
if (ret)
return ret;
- return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
+ ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
+ if (ret)
+ return ret;
+
+ return lp5523_init_program_engine(chip);
}
static void lp5523_load_engine(struct lp55xx_chip *chip)
@@ -152,25 +175,45 @@ static void lp5523_load_engine(struct lp55xx_chip *chip)
[LP55XX_ENGINE_3] = LP5523_LOAD_ENG3,
};
+ lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]);
+
+ lp5523_wait_opmode_done();
+}
+
+static void lp5523_load_engine_and_select_page(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
u8 page_sel[] = {
[LP55XX_ENGINE_1] = LP5523_PAGE_ENG1,
[LP55XX_ENGINE_2] = LP5523_PAGE_ENG2,
[LP55XX_ENGINE_3] = LP5523_PAGE_ENG3,
};
- lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]);
-
- lp5523_wait_opmode_done();
+ lp5523_load_engine(chip);
lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]);
}
-static void lp5523_stop_engine(struct lp55xx_chip *chip)
+static void lp5523_stop_all_engines(struct lp55xx_chip *chip)
{
lp55xx_write(chip, LP5523_REG_OP_MODE, 0);
lp5523_wait_opmode_done();
}
+static void lp5523_stop_engine(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
+ u8 mask[] = {
+ [LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M,
+ [LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M,
+ [LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M,
+ };
+
+ lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], 0);
+
+ lp5523_wait_opmode_done();
+}
+
static void lp5523_turn_off_channels(struct lp55xx_chip *chip)
{
int i;
@@ -227,23 +270,75 @@ static void lp5523_run_engine(struct lp55xx_chip *chip, bool start)
lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec);
}
+static int lp5523_init_program_engine(struct lp55xx_chip *chip)
+{
+ int i;
+ int j;
+ int ret;
+ u8 status;
+ /* one pattern per engine setting LED MUX start and stop addresses */
+ static const u8 pattern[][LP5523_PROGRAM_LENGTH] = {
+ { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},
+ { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},
+ { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},
+ };
+
+ /* hardcode 32 bytes of memory for each engine from program memory */
+ ret = lp55xx_write(chip, LP5523_REG_CH1_PROG_START, 0x00);
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_CH2_PROG_START, 0x10);
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_CH3_PROG_START, 0x20);
+ if (ret)
+ return ret;
+
+ /* write LED MUX address space for each engine */
+ for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
+ chip->engine_idx = i;
+ lp5523_load_engine_and_select_page(chip);
+
+ for (j = 0; j < LP5523_PROGRAM_LENGTH; j++) {
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + j,
+ pattern[i - 1][j]);
+ if (ret)
+ goto out;
+ }
+ }
+
+ lp5523_run_engine(chip, true);
+
+ /* Let the programs run for couple of ms and check the engine status */
+ usleep_range(3000, 6000);
+ lp55xx_read(chip, LP5523_REG_STATUS, &status);
+ status &= LP5523_ENG_STATUS_MASK;
+
+ if (status != LP5523_ENG_STATUS_MASK) {
+ dev_err(&chip->cl->dev,
+ "cound not configure LED engine, status = 0x%.2x\n",
+ status);
+ ret = -1;
+ }
+
+out:
+ lp5523_stop_all_engines(chip);
+ return ret;
+}
+
static int lp5523_update_program_memory(struct lp55xx_chip *chip,
const u8 *data, size_t size)
{
u8 pattern[LP5523_PROGRAM_LENGTH] = {0};
unsigned cmd;
char c[3];
- int update_size;
int nrchars;
- int offset = 0;
int ret;
- int i;
-
- /* clear program memory before updating */
- for (i = 0; i < LP5523_PROGRAM_LENGTH; i++)
- lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0);
+ int offset = 0;
+ int i = 0;
- i = 0;
while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) {
/* separate sscanfs because length is working only for %s */
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
@@ -263,11 +358,13 @@ static int lp5523_update_program_memory(struct lp55xx_chip *chip,
if (i % 2)
goto err;
- update_size = i;
- for (i = 0; i < update_size; i++)
- lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]);
+ for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) {
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]);
+ if (ret)
+ return -EINVAL;
+ }
- return 0;
+ return size;
err:
dev_err(&chip->cl->dev, "wrong pattern format\n");
@@ -290,10 +387,198 @@ static void lp5523_firmware_loaded(struct lp55xx_chip *chip)
* 2) write firmware data into program memory
*/
- lp5523_load_engine(chip);
+ lp5523_load_engine_and_select_page(chip);
lp5523_update_program_memory(chip, fw->data, fw->size);
}
+static ssize_t show_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
+
+ switch (mode) {
+ case LP55XX_ENGINE_RUN:
+ return sprintf(buf, "run\n");
+ case LP55XX_ENGINE_LOAD:
+ return sprintf(buf, "load\n");
+ case LP55XX_ENGINE_DISABLED:
+ default:
+ return sprintf(buf, "disabled\n");
+ }
+}
+show_mode(1)
+show_mode(2)
+show_mode(3)
+
+static ssize_t store_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+
+ if (!strncmp(buf, "run", 3)) {
+ lp5523_run_engine(chip, true);
+ engine->mode = LP55XX_ENGINE_RUN;
+ } else if (!strncmp(buf, "load", 4)) {
+ lp5523_stop_engine(chip);
+ lp5523_load_engine(chip);
+ engine->mode = LP55XX_ENGINE_LOAD;
+ } else if (!strncmp(buf, "disabled", 8)) {
+ lp5523_stop_engine(chip);
+ engine->mode = LP55XX_ENGINE_DISABLED;
+ }
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+store_mode(1)
+store_mode(2)
+store_mode(3)
+
+static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len)
+{
+ u16 tmp_mux = 0;
+ int i;
+
+ len = min_t(int, len, LP5523_MAX_LEDS);
+
+ for (i = 0; i < len; i++) {
+ switch (buf[i]) {
+ case '1':
+ tmp_mux |= (1 << i);
+ break;
+ case '0':
+ break;
+ case '\n':
+ i = len;
+ break;
+ default:
+ return -1;
+ }
+ }
+ *mux = tmp_mux;
+
+ return 0;
+}
+
+static void lp5523_mux_to_array(u16 led_mux, char *array)
+{
+ int i, pos = 0;
+ for (i = 0; i < LP5523_MAX_LEDS; i++)
+ pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i));
+
+ array[pos] = '\0';
+}
+
+static ssize_t show_engine_leds(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ char mux[LP5523_MAX_LEDS + 1];
+
+ lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux);
+
+ return sprintf(buf, "%s\n", mux);
+}
+show_leds(1)
+show_leds(2)
+show_leds(3)
+
+static int lp5523_load_mux(struct lp55xx_chip *chip, u16 mux, int nr)
+{
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+ int ret;
+ u8 mux_page[] = {
+ [LP55XX_ENGINE_1] = LP5523_PAGE_MUX1,
+ [LP55XX_ENGINE_2] = LP5523_PAGE_MUX2,
+ [LP55XX_ENGINE_3] = LP5523_PAGE_MUX3,
+ };
+
+ lp5523_load_engine(chip);
+
+ ret = lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, mux_page[nr]);
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM , (u8)(mux >> 8));
+ if (ret)
+ return ret;
+
+ ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + 1, (u8)(mux));
+ if (ret)
+ return ret;
+
+ engine->led_mux = mux;
+ return 0;
+}
+
+static ssize_t store_engine_leds(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_engine *engine = &chip->engines[nr - 1];
+ u16 mux = 0;
+ ssize_t ret;
+
+ if (lp5523_mux_parse(buf, &mux, len))
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+ ret = -EINVAL;
+
+ if (engine->mode != LP55XX_ENGINE_LOAD)
+ goto leave;
+
+ if (lp5523_load_mux(chip, mux, nr))
+ goto leave;
+
+ ret = len;
+leave:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+store_leds(1)
+store_leds(2)
+store_leds(3)
+
+static ssize_t store_engine_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ chip->engine_idx = nr;
+ lp5523_load_engine_and_select_page(chip);
+ ret = lp5523_update_program_memory(chip, buf, len);
+
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+store_load(1)
+store_load(2)
+store_load(3)
+
static ssize_t lp5523_selftest(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -393,9 +678,27 @@ static void lp5523_led_brightness_work(struct work_struct *work)
mutex_unlock(&chip->lock);
}
-static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL);
+static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
+static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
+static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
+static LP55XX_DEV_ATTR_RW(engine1_leds, show_engine1_leds, store_engine1_leds);
+static LP55XX_DEV_ATTR_RW(engine2_leds, show_engine2_leds, store_engine2_leds);
+static LP55XX_DEV_ATTR_RW(engine3_leds, show_engine3_leds, store_engine3_leds);
+static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
+static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
+static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
+static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);
static struct attribute *lp5523_attributes[] = {
+ &dev_attr_engine1_mode.attr,
+ &dev_attr_engine2_mode.attr,
+ &dev_attr_engine3_mode.attr,
+ &dev_attr_engine1_load.attr,
+ &dev_attr_engine2_load.attr,
+ &dev_attr_engine3_load.attr,
+ &dev_attr_engine1_leds.attr,
+ &dev_attr_engine2_leds.attr,
+ &dev_attr_engine3_leds.attr,
&dev_attr_selftest.attr,
NULL,
};
@@ -429,12 +732,20 @@ static int lp5523_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata = client->dev.platform_data;
-
- if (!pdata) {
- dev_err(&client->dev, "no platform data\n");
- return -EINVAL;
+ struct lp55xx_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+
+ if (!dev_get_platdata(&client->dev)) {
+ if (np) {
+ ret = lp55xx_of_populate_pdata(&client->dev, np);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
}
+ pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -484,7 +795,7 @@ static int lp5523_remove(struct i2c_client *client)
struct lp55xx_led *led = i2c_get_clientdata(client);
struct lp55xx_chip *chip = led->chip;
- lp5523_stop_engine(chip);
+ lp5523_stop_all_engines(chip);
lp55xx_unregister_sysfs(chip);
lp55xx_unregister_leds(led, chip);
lp55xx_deinit_device(chip);
@@ -500,9 +811,20 @@ static const struct i2c_device_id lp5523_id[] = {
MODULE_DEVICE_TABLE(i2c, lp5523_id);
+#ifdef CONFIG_OF
+static const struct of_device_id of_lp5523_leds_match[] = {
+ { .compatible = "national,lp5523", },
+ { .compatible = "ti,lp55231", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_lp5523_leds_match);
+#endif
+
static struct i2c_driver lp5523_driver = {
.driver = {
.name = "lp5523x",
+ .of_match_table = of_match_ptr(of_lp5523_leds_match),
},
.probe = lp5523_probe,
.remove = lp5523_remove,
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index 513f2390ca2..ca85724ab13 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -13,10 +13,10 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/slab.h>
@@ -346,9 +346,9 @@ static void lp5562_write_program_memory(struct lp55xx_chip *chip,
/* check the size of program count */
static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
{
- return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
- ptn->size_g >= LP5562_PROGRAM_LENGTH ||
- ptn->size_b >= LP5562_PROGRAM_LENGTH);
+ return ptn->size_r >= LP5562_PROGRAM_LENGTH ||
+ ptn->size_g >= LP5562_PROGRAM_LENGTH ||
+ ptn->size_b >= LP5562_PROGRAM_LENGTH;
}
static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
@@ -477,8 +477,8 @@ static ssize_t lp5562_store_engine_mux(struct device *dev,
return len;
}
-static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
-static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
+static LP55XX_DEV_ATTR_WO(led_pattern, lp5562_store_pattern);
+static LP55XX_DEV_ATTR_WO(engine_mux, lp5562_store_engine_mux);
static struct attribute *lp5562_attributes[] = {
&dev_attr_led_pattern.attr,
@@ -515,12 +515,20 @@ static int lp5562_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata = client->dev.platform_data;
-
- if (!pdata) {
- dev_err(&client->dev, "no platform data\n");
- return -EINVAL;
+ struct lp55xx_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+
+ if (!dev_get_platdata(&client->dev)) {
+ if (np) {
+ ret = lp55xx_of_populate_pdata(&client->dev, np);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
}
+ pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -583,9 +591,19 @@ static const struct i2c_device_id lp5562_id[] = {
};
MODULE_DEVICE_TABLE(i2c, lp5562_id);
+#ifdef CONFIG_OF
+static const struct of_device_id of_lp5562_leds_match[] = {
+ { .compatible = "ti,lp5562", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
+#endif
+
static struct i2c_driver lp5562_driver = {
.driver = {
.name = "lp5562",
+ .of_match_table = of_match_ptr(of_lp5562_leds_match),
},
.probe = lp5562_probe,
.remove = lp5562_remove,
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index ba34199dc3d..88317b4f7bf 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -19,6 +19,9 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_data/leds-lp55xx.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include "leds-lp55xx-common.h"
@@ -164,6 +167,7 @@ static int lp55xx_init_led(struct lp55xx_led *led,
led->led_current = pdata->led_config[chan].led_current;
led->max_current = pdata->led_config[chan].max_current;
led->chan_nr = pdata->led_config[chan].chan_nr;
+ led->cdev.default_trigger = pdata->led_config[chan].default_trigger;
if (led->chan_nr >= max_channel) {
dev_err(dev, "Use channel numbers between 0 and %d\n",
@@ -206,6 +210,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
{
struct lp55xx_chip *chip = context;
struct device *dev = &chip->cl->dev;
+ enum lp55xx_engine_index idx = chip->engine_idx;
if (!fw) {
dev_err(dev, "firmware request failed\n");
@@ -215,6 +220,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
/* handling firmware data is chip dependent */
mutex_lock(&chip->lock);
+ chip->engines[idx - 1].mode = LP55XX_ENGINE_LOAD;
chip->fw = fw;
if (chip->cfg->firmware_cb)
chip->cfg->firmware_cb(chip);
@@ -405,18 +411,18 @@ int lp55xx_init_device(struct lp55xx_chip *chip)
if (!pdata || !cfg)
return -EINVAL;
- if (pdata->setup_resources) {
- ret = pdata->setup_resources();
+ if (gpio_is_valid(pdata->enable_gpio)) {
+ ret = devm_gpio_request_one(dev, pdata->enable_gpio,
+ GPIOF_DIR_OUT, "lp5523_enable");
if (ret < 0) {
- dev_err(dev, "setup resoure err: %d\n", ret);
+ dev_err(dev, "could not acquire enable gpio (err=%d)\n",
+ ret);
goto err;
}
- }
- if (pdata->enable) {
- pdata->enable(0);
+ gpio_set_value(pdata->enable_gpio, 0);
usleep_range(1000, 2000); /* Keep enable down at least 1ms */
- pdata->enable(1);
+ gpio_set_value(pdata->enable_gpio, 1);
usleep_range(1000, 2000); /* 500us abs min. */
}
@@ -457,11 +463,8 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip)
if (chip->clk)
clk_disable_unprepare(chip->clk);
- if (pdata->enable)
- pdata->enable(0);
-
- if (pdata->release_resources)
- pdata->release_resources();
+ if (gpio_is_valid(pdata->enable_gpio))
+ gpio_set_value(pdata->enable_gpio, 0);
}
EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
@@ -554,6 +557,57 @@ void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
+int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
+{
+ struct device_node *child;
+ struct lp55xx_platform_data *pdata;
+ struct lp55xx_led_config *cfg;
+ int num_channels;
+ int i = 0;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ num_channels = of_get_child_count(np);
+ if (num_channels == 0) {
+ dev_err(dev, "no LED channels\n");
+ return -EINVAL;
+ }
+
+ cfg = devm_kzalloc(dev, sizeof(*cfg) * num_channels, GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ pdata->led_config = &cfg[0];
+ pdata->num_channels = num_channels;
+
+ for_each_child_of_node(np, child) {
+ cfg[i].chan_nr = i;
+
+ of_property_read_string(child, "chan-name", &cfg[i].name);
+ of_property_read_u8(child, "led-cur", &cfg[i].led_current);
+ of_property_read_u8(child, "max-cur", &cfg[i].max_current);
+ cfg[i].default_trigger =
+ of_get_property(child, "linux,default-trigger", NULL);
+
+ i++;
+ }
+
+ of_property_read_string(np, "label", &pdata->label);
+ of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
+
+ pdata->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0);
+
+ /* LP8501 specific */
+ of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
+
+ dev->platform_data = pdata;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lp55xx_of_populate_pdata);
+
MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
MODULE_DESCRIPTION("LP55xx Common Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h
index fa6a078bf54..cceab483edd 100644
--- a/drivers/leds/leds-lp55xx-common.h
+++ b/drivers/leds/leds-lp55xx-common.h
@@ -20,8 +20,62 @@ enum lp55xx_engine_index {
LP55XX_ENGINE_1,
LP55XX_ENGINE_2,
LP55XX_ENGINE_3,
+ LP55XX_ENGINE_MAX = LP55XX_ENGINE_3,
};
+enum lp55xx_engine_mode {
+ LP55XX_ENGINE_DISABLED,
+ LP55XX_ENGINE_LOAD,
+ LP55XX_ENGINE_RUN,
+};
+
+#define LP55XX_DEV_ATTR_RW(name, show, store) \
+ DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show, store)
+#define LP55XX_DEV_ATTR_RO(name, show) \
+ DEVICE_ATTR(name, S_IRUGO, show, NULL)
+#define LP55XX_DEV_ATTR_WO(name, store) \
+ DEVICE_ATTR(name, S_IWUSR, NULL, store)
+
+#define show_mode(nr) \
+static ssize_t show_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_mode(dev, attr, buf, nr); \
+}
+
+#define store_mode(nr) \
+static ssize_t store_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_mode(dev, attr, buf, len, nr); \
+}
+
+#define show_leds(nr) \
+static ssize_t show_engine##nr##_leds(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_leds(dev, attr, buf, nr); \
+}
+
+#define store_leds(nr) \
+static ssize_t store_engine##nr##_leds(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_leds(dev, attr, buf, len, nr); \
+}
+
+#define store_load(nr) \
+static ssize_t store_engine##nr##_load(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_load(dev, attr, buf, len, nr); \
+}
+
struct lp55xx_led;
struct lp55xx_chip;
@@ -72,6 +126,16 @@ struct lp55xx_device_config {
};
/*
+ * struct lp55xx_engine
+ * @mode : Engine mode
+ * @led_mux : Mux bits for LED selection. Only used in LP5523
+ */
+struct lp55xx_engine {
+ enum lp55xx_engine_mode mode;
+ u16 led_mux;
+};
+
+/*
* struct lp55xx_chip
* @cl : I2C communication for access registers
* @pdata : Platform specific data
@@ -79,6 +143,7 @@ struct lp55xx_device_config {
* @num_leds : Number of registered LEDs
* @cfg : Device specific configuration data
* @engine_idx : Selected engine number
+ * @engines : Engine structure for the device attribute R/W interface
* @fw : Firmware data for running a LED pattern
*/
struct lp55xx_chip {
@@ -89,6 +154,7 @@ struct lp55xx_chip {
int num_leds;
struct lp55xx_device_config *cfg;
enum lp55xx_engine_index engine_idx;
+ struct lp55xx_engine engines[LP55XX_ENGINE_MAX];
const struct firmware *fw;
};
@@ -135,4 +201,8 @@ extern void lp55xx_unregister_leds(struct lp55xx_led *led,
extern int lp55xx_register_sysfs(struct lp55xx_chip *chip);
extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip);
+/* common device tree population function */
+extern int lp55xx_of_populate_pdata(struct device *dev,
+ struct device_node *np);
+
#endif /* _LEDS_LP55XX_COMMON_H */
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
new file mode 100644
index 00000000000..00f068b0fa6
--- /dev/null
+++ b/drivers/leds/leds-lp8501.c
@@ -0,0 +1,411 @@
+/*
+ * TI LP8501 9 channel LED Driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_data/leds-lp55xx.h>
+#include <linux/slab.h>
+
+#include "leds-lp55xx-common.h"
+
+#define LP8501_PROGRAM_LENGTH 32
+#define LP8501_MAX_LEDS 9
+
+/* Registers */
+#define LP8501_REG_ENABLE 0x00
+#define LP8501_ENABLE BIT(6)
+#define LP8501_EXEC_M 0x3F
+#define LP8501_EXEC_ENG1_M 0x30
+#define LP8501_EXEC_ENG2_M 0x0C
+#define LP8501_EXEC_ENG3_M 0x03
+#define LP8501_RUN_ENG1 0x20
+#define LP8501_RUN_ENG2 0x08
+#define LP8501_RUN_ENG3 0x02
+
+#define LP8501_REG_OP_MODE 0x01
+#define LP8501_MODE_ENG1_M 0x30
+#define LP8501_MODE_ENG2_M 0x0C
+#define LP8501_MODE_ENG3_M 0x03
+#define LP8501_LOAD_ENG1 0x10
+#define LP8501_LOAD_ENG2 0x04
+#define LP8501_LOAD_ENG3 0x01
+
+#define LP8501_REG_PWR_CONFIG 0x05
+#define LP8501_PWR_CONFIG_M 0x03
+
+#define LP8501_REG_LED_PWM_BASE 0x16
+
+#define LP8501_REG_LED_CURRENT_BASE 0x26
+
+#define LP8501_REG_CONFIG 0x36
+#define LP8501_PWM_PSAVE BIT(7)
+#define LP8501_AUTO_INC BIT(6)
+#define LP8501_PWR_SAVE BIT(5)
+#define LP8501_CP_AUTO 0x18
+#define LP8501_INT_CLK BIT(0)
+#define LP8501_DEFAULT_CFG \
+ (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO)
+
+#define LP8501_REG_RESET 0x3D
+#define LP8501_RESET 0xFF
+
+#define LP8501_REG_PROG_PAGE_SEL 0x4F
+#define LP8501_PAGE_ENG1 0
+#define LP8501_PAGE_ENG2 1
+#define LP8501_PAGE_ENG3 2
+
+#define LP8501_REG_PROG_MEM 0x50
+
+#define LP8501_ENG1_IS_LOADING(mode) \
+ ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1)
+#define LP8501_ENG2_IS_LOADING(mode) \
+ ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2)
+#define LP8501_ENG3_IS_LOADING(mode) \
+ ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3)
+
+static inline void lp8501_wait_opmode_done(void)
+{
+ usleep_range(1000, 2000);
+}
+
+static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current)
+{
+ led->led_current = led_current;
+ lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr,
+ led_current);
+}
+
+static int lp8501_post_init_device(struct lp55xx_chip *chip)
+{
+ int ret;
+ u8 val = LP8501_DEFAULT_CFG;
+
+ ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE);
+ if (ret)
+ return ret;
+
+ /* Chip startup time is 500 us, 1 - 2 ms gives some margin */
+ usleep_range(1000, 2000);
+
+ if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
+ val |= LP8501_INT_CLK;
+
+ ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
+ if (ret)
+ return ret;
+
+ /* Power selection for each output */
+ return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG,
+ LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel);
+}
+
+static void lp8501_load_engine(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
+ u8 mask[] = {
+ [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
+ [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
+ [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
+ };
+
+ u8 val[] = {
+ [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
+ [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
+ [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
+ };
+
+ u8 page_sel[] = {
+ [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
+ [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
+ [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
+ };
+
+ lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]);
+
+ lp8501_wait_opmode_done();
+
+ lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]);
+}
+
+static void lp8501_stop_engine(struct lp55xx_chip *chip)
+{
+ lp55xx_write(chip, LP8501_REG_OP_MODE, 0);
+ lp8501_wait_opmode_done();
+}
+
+static void lp8501_turn_off_channels(struct lp55xx_chip *chip)
+{
+ int i;
+
+ for (i = 0; i < LP8501_MAX_LEDS; i++)
+ lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0);
+}
+
+static void lp8501_run_engine(struct lp55xx_chip *chip, bool start)
+{
+ int ret;
+ u8 mode;
+ u8 exec;
+
+ /* stop engine */
+ if (!start) {
+ lp8501_stop_engine(chip);
+ lp8501_turn_off_channels(chip);
+ return;
+ }
+
+ /*
+ * To run the engine,
+ * operation mode and enable register should updated at the same time
+ */
+
+ ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode);
+ if (ret)
+ return;
+
+ ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec);
+ if (ret)
+ return;
+
+ /* change operation mode to RUN only when each engine is loading */
+ if (LP8501_ENG1_IS_LOADING(mode)) {
+ mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1;
+ exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1;
+ }
+
+ if (LP8501_ENG2_IS_LOADING(mode)) {
+ mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2;
+ exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2;
+ }
+
+ if (LP8501_ENG3_IS_LOADING(mode)) {
+ mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3;
+ exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3;
+ }
+
+ lp55xx_write(chip, LP8501_REG_OP_MODE, mode);
+ lp8501_wait_opmode_done();
+
+ lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec);
+}
+
+static int lp8501_update_program_memory(struct lp55xx_chip *chip,
+ const u8 *data, size_t size)
+{
+ u8 pattern[LP8501_PROGRAM_LENGTH] = {0};
+ unsigned cmd;
+ char c[3];
+ int update_size;
+ int nrchars;
+ int offset = 0;
+ int ret;
+ int i;
+
+ /* clear program memory before updating */
+ for (i = 0; i < LP8501_PROGRAM_LENGTH; i++)
+ lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0);
+
+ i = 0;
+ while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) {
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
+ if (ret != 1)
+ goto err;
+
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto err;
+
+ pattern[i] = (u8)cmd;
+ offset += nrchars;
+ i++;
+ }
+
+ /* Each instruction is 16bit long. Check that length is even */
+ if (i % 2)
+ goto err;
+
+ update_size = i;
+ for (i = 0; i < update_size; i++)
+ lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]);
+
+ return 0;
+
+err:
+ dev_err(&chip->cl->dev, "wrong pattern format\n");
+ return -EINVAL;
+}
+
+static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
+{
+ const struct firmware *fw = chip->fw;
+
+ if (fw->size > LP8501_PROGRAM_LENGTH) {
+ dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
+ fw->size);
+ return;
+ }
+
+ /*
+ * Program memory sequence
+ * 1) set engine mode to "LOAD"
+ * 2) write firmware data into program memory
+ */
+
+ lp8501_load_engine(chip);
+ lp8501_update_program_memory(chip, fw->data, fw->size);
+}
+
+static void lp8501_led_brightness_work(struct work_struct *work)
+{
+ struct lp55xx_led *led = container_of(work, struct lp55xx_led,
+ brightness_work);
+ struct lp55xx_chip *chip = led->chip;
+
+ mutex_lock(&chip->lock);
+ lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
+ led->brightness);
+ mutex_unlock(&chip->lock);
+}
+
+/* Chip specific configurations */
+static struct lp55xx_device_config lp8501_cfg = {
+ .reset = {
+ .addr = LP8501_REG_RESET,
+ .val = LP8501_RESET,
+ },
+ .enable = {
+ .addr = LP8501_REG_ENABLE,
+ .val = LP8501_ENABLE,
+ },
+ .max_channel = LP8501_MAX_LEDS,
+ .post_init_device = lp8501_post_init_device,
+ .brightness_work_fn = lp8501_led_brightness_work,
+ .set_led_current = lp8501_set_led_current,
+ .firmware_cb = lp8501_firmware_loaded,
+ .run_engine = lp8501_run_engine,
+};
+
+static int lp8501_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lp55xx_chip *chip;
+ struct lp55xx_led *led;
+ struct lp55xx_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+
+ if (!dev_get_platdata(&client->dev)) {
+ if (np) {
+ ret = lp55xx_of_populate_pdata(&client->dev, np);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+ }
+ pdata = dev_get_platdata(&client->dev);
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ led = devm_kzalloc(&client->dev,
+ sizeof(*led) * pdata->num_channels, GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ chip->cl = client;
+ chip->pdata = pdata;
+ chip->cfg = &lp8501_cfg;
+
+ mutex_init(&chip->lock);
+
+ i2c_set_clientdata(client, led);
+
+ ret = lp55xx_init_device(chip);
+ if (ret)
+ goto err_init;
+
+ dev_info(&client->dev, "%s Programmable led chip found\n", id->name);
+
+ ret = lp55xx_register_leds(led, chip);
+ if (ret)
+ goto err_register_leds;
+
+ ret = lp55xx_register_sysfs(chip);
+ if (ret) {
+ dev_err(&client->dev, "registering sysfs failed\n");
+ goto err_register_sysfs;
+ }
+
+ return 0;
+
+err_register_sysfs:
+ lp55xx_unregister_leds(led, chip);
+err_register_leds:
+ lp55xx_deinit_device(chip);
+err_init:
+ return ret;
+}
+
+static int lp8501_remove(struct i2c_client *client)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(client);
+ struct lp55xx_chip *chip = led->chip;
+
+ lp8501_stop_engine(chip);
+ lp55xx_unregister_sysfs(chip);
+ lp55xx_unregister_leds(led, chip);
+ lp55xx_deinit_device(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id lp8501_id[] = {
+ { "lp8501", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp8501_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_lp8501_leds_match[] = {
+ { .compatible = "ti,lp8501", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
+#endif
+
+static struct i2c_driver lp8501_driver = {
+ .driver = {
+ .name = "lp8501",
+ .of_match_table = of_match_ptr(of_lp8501_leds_match),
+ },
+ .probe = lp8501_probe,
+ .remove = lp8501_remove,
+ .id_table = lp8501_id,
+};
+
+module_i2c_driver(lp8501_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index ca48a7d5502..059f5b1f355 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -17,7 +17,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
@@ -135,7 +134,7 @@ static void delete_lt3593_led(struct lt3593_led_data *led)
static int lt3593_led_probe(struct platform_device *pdev)
{
- struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct lt3593_led_data *leds_data;
int i, ret = 0;
@@ -169,7 +168,7 @@ err:
static int lt3593_led_remove(struct platform_device *pdev)
{
int i;
- struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct lt3593_led_data *leds_data;
leds_data = platform_get_drvdata(pdev);
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index e942adaa750..f1db88e2513 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -1,5 +1,5 @@
/*
- * LEDs driver for Freescale MC13783
+ * LEDs driver for Freescale MC13783/MC13892/MC34708
*
* Copyright (C) 2010 Philippe Rétornaz
*
@@ -17,143 +17,56 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
+#include <linux/of.h>
#include <linux/workqueue.h>
#include <linux/mfd/mc13xxx.h>
-#include <linux/slab.h>
-struct mc13783_led {
+struct mc13xxx_led_devtype {
+ int led_min;
+ int led_max;
+ int num_regs;
+ u32 ledctrl_base;
+};
+
+struct mc13xxx_led {
struct led_classdev cdev;
struct work_struct work;
- struct mc13xxx *master;
enum led_brightness new_brightness;
int id;
+ struct mc13xxx_leds *leds;
};
-#define MC13783_REG_LED_CONTROL_0 51
-#define MC13783_LED_C0_ENABLE_BIT (1 << 0)
-#define MC13783_LED_C0_TRIODE_MD_BIT (1 << 7)
-#define MC13783_LED_C0_TRIODE_AD_BIT (1 << 8)
-#define MC13783_LED_C0_TRIODE_KP_BIT (1 << 9)
-#define MC13783_LED_C0_BOOST_BIT (1 << 10)
-#define MC13783_LED_C0_ABMODE_MASK 0x7
-#define MC13783_LED_C0_ABMODE 11
-#define MC13783_LED_C0_ABREF_MASK 0x3
-#define MC13783_LED_C0_ABREF 14
-
-#define MC13783_REG_LED_CONTROL_1 52
-#define MC13783_LED_C1_TC1HALF_BIT (1 << 18)
-
-#define MC13783_REG_LED_CONTROL_2 53
-#define MC13783_LED_C2_BL_P_MASK 0xf
-#define MC13783_LED_C2_MD_P 9
-#define MC13783_LED_C2_AD_P 13
-#define MC13783_LED_C2_KP_P 17
-#define MC13783_LED_C2_BL_C_MASK 0x7
-#define MC13783_LED_C2_MD_C 0
-#define MC13783_LED_C2_AD_C 3
-#define MC13783_LED_C2_KP_C 6
-
-#define MC13783_REG_LED_CONTROL_3 54
-#define MC13783_LED_C3_TC_P 6
-#define MC13783_LED_C3_TC_P_MASK 0x1f
-
-#define MC13783_REG_LED_CONTROL_4 55
-#define MC13783_REG_LED_CONTROL_5 56
-
-#define MC13783_LED_Cx_PERIOD 21
-#define MC13783_LED_Cx_PERIOD_MASK 0x3
-#define MC13783_LED_Cx_SLEWLIM_BIT (1 << 23)
-#define MC13783_LED_Cx_TRIODE_TC_BIT (1 << 23)
-#define MC13783_LED_Cx_TC_C_MASK 0x3
-
-static void mc13783_led_work(struct work_struct *work)
-{
- struct mc13783_led *led = container_of(work, struct mc13783_led, work);
- int reg = 0;
- int mask = 0;
- int value = 0;
- int bank, off, shift;
-
- switch (led->id) {
- case MC13783_LED_MD:
- reg = MC13783_REG_LED_CONTROL_2;
- mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_MD_P;
- value = (led->new_brightness >> 4) << MC13783_LED_C2_MD_P;
- break;
- case MC13783_LED_AD:
- reg = MC13783_REG_LED_CONTROL_2;
- mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_AD_P;
- value = (led->new_brightness >> 4) << MC13783_LED_C2_AD_P;
- break;
- case MC13783_LED_KP:
- reg = MC13783_REG_LED_CONTROL_2;
- mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_KP_P;
- value = (led->new_brightness >> 4) << MC13783_LED_C2_KP_P;
- break;
- case MC13783_LED_R1:
- case MC13783_LED_G1:
- case MC13783_LED_B1:
- case MC13783_LED_R2:
- case MC13783_LED_G2:
- case MC13783_LED_B2:
- case MC13783_LED_R3:
- case MC13783_LED_G3:
- case MC13783_LED_B3:
- off = led->id - MC13783_LED_R1;
- bank = off/3;
- reg = MC13783_REG_LED_CONTROL_3 + off/3;
- shift = (off - bank * 3) * 5 + MC13783_LED_C3_TC_P;
- value = (led->new_brightness >> 3) << shift;
- mask = MC13783_LED_C3_TC_P_MASK << shift;
- break;
- }
-
- mc13xxx_lock(led->master);
-
- mc13xxx_reg_rmw(led->master, reg, mask, value);
-
- mc13xxx_unlock(led->master);
-}
+struct mc13xxx_leds {
+ struct mc13xxx *master;
+ struct mc13xxx_led_devtype *devtype;
+ int num_leds;
+ struct mc13xxx_led *led;
+};
-static void mc13783_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
+static unsigned int mc13xxx_max_brightness(int id)
{
- struct mc13783_led *led;
+ if (id >= MC13783_LED_MD && id <= MC13783_LED_KP)
+ return 0x0f;
+ else if (id >= MC13783_LED_R1 && id <= MC13783_LED_B3)
+ return 0x1f;
- led = container_of(led_cdev, struct mc13783_led, cdev);
- led->new_brightness = value;
- schedule_work(&led->work);
+ return 0x3f;
}
-static int mc13783_led_setup(struct mc13783_led *led, int max_current)
+static void mc13xxx_led_work(struct work_struct *work)
{
- int shift = 0;
- int mask = 0;
- int value = 0;
- int reg = 0;
- int ret, bank;
+ struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work);
+ struct mc13xxx_leds *leds = led->leds;
+ unsigned int reg, bank, off, shift;
switch (led->id) {
case MC13783_LED_MD:
- shift = MC13783_LED_C2_MD_C;
- mask = MC13783_LED_C2_BL_C_MASK;
- value = max_current & MC13783_LED_C2_BL_C_MASK;
- reg = MC13783_REG_LED_CONTROL_2;
- break;
case MC13783_LED_AD:
- shift = MC13783_LED_C2_AD_C;
- mask = MC13783_LED_C2_BL_C_MASK;
- value = max_current & MC13783_LED_C2_BL_C_MASK;
- reg = MC13783_REG_LED_CONTROL_2;
- break;
case MC13783_LED_KP:
- shift = MC13783_LED_C2_KP_C;
- mask = MC13783_LED_C2_BL_C_MASK;
- value = max_current & MC13783_LED_C2_BL_C_MASK;
- reg = MC13783_REG_LED_CONTROL_2;
+ reg = 2;
+ shift = 9 + (led->id - MC13783_LED_MD) * 4;
break;
case MC13783_LED_R1:
case MC13783_LED_G1:
@@ -164,229 +77,261 @@ static int mc13783_led_setup(struct mc13783_led *led, int max_current)
case MC13783_LED_R3:
case MC13783_LED_G3:
case MC13783_LED_B3:
- bank = (led->id - MC13783_LED_R1)/3;
- reg = MC13783_REG_LED_CONTROL_3 + bank;
- shift = ((led->id - MC13783_LED_R1) - bank * 3) * 2;
- mask = MC13783_LED_Cx_TC_C_MASK;
- value = max_current & MC13783_LED_Cx_TC_C_MASK;
+ off = led->id - MC13783_LED_R1;
+ bank = off / 3;
+ reg = 3 + bank;
+ shift = (off - bank * 3) * 5 + 6;
+ break;
+ 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;
+ break;
+ case MC13892_LED_R:
+ case MC13892_LED_G:
+ case MC13892_LED_B:
+ off = led->id - MC13892_LED_R;
+ bank = off / 2;
+ reg = 2 + bank;
+ shift = (off - bank * 2) * 12 + 3;
break;
+ case MC34708_LED_R:
+ case MC34708_LED_G:
+ reg = 0;
+ shift = 3 + (led->id - MC34708_LED_R) * 12;
+ break;
+ default:
+ BUG();
}
- mc13xxx_lock(led->master);
+ mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg,
+ mc13xxx_max_brightness(led->id) << shift,
+ led->new_brightness << shift);
+}
- ret = mc13xxx_reg_rmw(led->master, reg, mask << shift,
- value << shift);
+static void mc13xxx_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct mc13xxx_led *led =
+ container_of(led_cdev, struct mc13xxx_led, cdev);
- mc13xxx_unlock(led->master);
- return ret;
+ led->new_brightness = value;
+ schedule_work(&led->work);
}
-static int mc13783_leds_prepare(struct platform_device *pdev)
+#ifdef CONFIG_OF
+static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
+ struct platform_device *pdev)
{
- struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
- int ret = 0;
- int reg = 0;
+ struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
+ struct mc13xxx_leds_platform_data *pdata;
+ struct device_node *parent, *child;
+ struct device *dev = &pdev->dev;
+ int i = 0, ret = -ENODATA;
- mc13xxx_lock(dev);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
- if (pdata->flags & MC13783_LED_TC1HALF)
- reg |= MC13783_LED_C1_TC1HALF_BIT;
+ of_node_get(dev->parent->of_node);
- if (pdata->flags & MC13783_LED_SLEWLIMTC)
- reg |= MC13783_LED_Cx_SLEWLIM_BIT;
+ parent = of_find_node_by_name(dev->parent->of_node, "leds");
+ if (!parent)
+ goto out_node_put;
- ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
+ ret = of_property_read_u32_array(parent, "led-control",
+ pdata->led_control,
+ leds->devtype->num_regs);
if (ret)
- goto out;
+ goto out_node_put;
- reg = (pdata->bl_period & MC13783_LED_Cx_PERIOD_MASK) <<
- MC13783_LED_Cx_PERIOD;
+ pdata->num_leds = of_get_child_count(parent);
- if (pdata->flags & MC13783_LED_SLEWLIMBL)
- reg |= MC13783_LED_Cx_SLEWLIM_BIT;
+ pdata->led = devm_kzalloc(dev, pdata->num_leds * sizeof(*pdata->led),
+ GFP_KERNEL);
+ if (!pdata->led) {
+ ret = -ENOMEM;
+ goto out_node_put;
+ }
- ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
- if (ret)
- goto out;
+ for_each_child_of_node(parent, child) {
+ const char *str;
+ u32 tmp;
- reg = (pdata->tc1_period & MC13783_LED_Cx_PERIOD_MASK) <<
- MC13783_LED_Cx_PERIOD;
+ if (of_property_read_u32(child, "reg", &tmp))
+ continue;
+ pdata->led[i].id = leds->devtype->led_min + tmp;
- if (pdata->flags & MC13783_LED_TRIODE_TC1)
- reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
+ if (!of_property_read_string(child, "label", &str))
+ pdata->led[i].name = str;
+ if (!of_property_read_string(child, "linux,default-trigger",
+ &str))
+ pdata->led[i].default_trigger = str;
- ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
- if (ret)
- goto out;
+ i++;
+ }
- reg = (pdata->tc2_period & MC13783_LED_Cx_PERIOD_MASK) <<
- MC13783_LED_Cx_PERIOD;
+ pdata->num_leds = i;
+ ret = i > 0 ? 0 : -ENODATA;
- if (pdata->flags & MC13783_LED_TRIODE_TC2)
- reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
+out_node_put:
+ of_node_put(parent);
- ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
- if (ret)
- goto out;
+ return ret ? ERR_PTR(ret) : pdata;
+}
+#else
+static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
+ struct platform_device *pdev)
+{
+ return ERR_PTR(-ENOSYS);
+}
+#endif
- reg = (pdata->tc3_period & MC13783_LED_Cx_PERIOD_MASK) <<
- MC13783_LED_Cx_PERIOD;
+static int __init mc13xxx_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(dev);
+ struct mc13xxx *mcdev = dev_get_drvdata(dev->parent);
+ struct mc13xxx_led_devtype *devtype =
+ (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data;
+ struct mc13xxx_leds *leds;
+ int i, id, ret = -ENODATA;
+ u32 init_led = 0;
+
+ leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
+ if (!leds)
+ return -ENOMEM;
- if (pdata->flags & MC13783_LED_TRIODE_TC3)
- reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
+ leds->devtype = devtype;
+ leds->master = mcdev;
+ platform_set_drvdata(pdev, leds);
- ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
- if (ret)
- goto out;
-
- reg = MC13783_LED_C0_ENABLE_BIT;
- if (pdata->flags & MC13783_LED_TRIODE_MD)
- reg |= MC13783_LED_C0_TRIODE_MD_BIT;
- if (pdata->flags & MC13783_LED_TRIODE_AD)
- reg |= MC13783_LED_C0_TRIODE_AD_BIT;
- if (pdata->flags & MC13783_LED_TRIODE_KP)
- reg |= MC13783_LED_C0_TRIODE_KP_BIT;
- if (pdata->flags & MC13783_LED_BOOST_EN)
- reg |= MC13783_LED_C0_BOOST_BIT;
-
- reg |= (pdata->abmode & MC13783_LED_C0_ABMODE_MASK) <<
- MC13783_LED_C0_ABMODE;
- reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) <<
- MC13783_LED_C0_ABREF;
-
- ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
-
-out:
- mc13xxx_unlock(dev);
- return ret;
-}
+ if (dev->parent->of_node) {
+ pdata = mc13xxx_led_probe_dt(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ } else if (!pdata)
+ return -ENODATA;
-static int mc13783_led_probe(struct platform_device *pdev)
-{
- struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13xxx_led_platform_data *led_cur;
- struct mc13783_led *led, *led_dat;
- int ret, i;
- int init_led = 0;
-
- if (pdata == NULL) {
- dev_err(&pdev->dev, "missing platform data\n");
- return -ENODEV;
- }
+ leds->num_leds = pdata->num_leds;
- if (pdata->num_leds < 1 || pdata->num_leds > (MC13783_LED_MAX + 1)) {
- dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds);
+ if ((leds->num_leds < 1) ||
+ (leds->num_leds > (devtype->led_max - devtype->led_min + 1))) {
+ dev_err(dev, "Invalid LED count %d\n", leds->num_leds);
return -EINVAL;
}
- led = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*led),
- GFP_KERNEL);
- if (led == NULL) {
- dev_err(&pdev->dev, "failed to alloc memory\n");
+ leds->led = devm_kzalloc(dev, leds->num_leds * sizeof(*leds->led),
+ GFP_KERNEL);
+ if (!leds->led)
return -ENOMEM;
- }
- ret = mc13783_leds_prepare(pdev);
- if (ret) {
- dev_err(&pdev->dev, "unable to init led driver\n");
- return ret;
+ for (i = 0; i < devtype->num_regs; i++) {
+ ret = mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i,
+ pdata->led_control[i]);
+ if (ret)
+ return ret;
}
- for (i = 0; i < pdata->num_leds; i++) {
- led_dat = &led[i];
- led_cur = &pdata->led[i];
+ for (i = 0; i < leds->num_leds; i++) {
+ const char *name, *trig;
- if (led_cur->id > MC13783_LED_MAX || led_cur->id < 0) {
- dev_err(&pdev->dev, "invalid id %d\n", led_cur->id);
- ret = -EINVAL;
- goto err_register;
- }
+ ret = -EINVAL;
- if (init_led & (1 << led_cur->id)) {
- dev_err(&pdev->dev, "led %d already initialized\n",
- led_cur->id);
- ret = -EINVAL;
- goto err_register;
+ id = pdata->led[i].id;
+ name = pdata->led[i].name;
+ trig = pdata->led[i].default_trigger;
+
+ if ((id > devtype->led_max) || (id < devtype->led_min)) {
+ dev_err(dev, "Invalid ID %i\n", id);
+ break;
}
- init_led |= 1 << led_cur->id;
- led_dat->cdev.name = led_cur->name;
- led_dat->cdev.default_trigger = led_cur->default_trigger;
- led_dat->cdev.brightness_set = mc13783_led_set;
- led_dat->cdev.brightness = LED_OFF;
- led_dat->id = led_cur->id;
- led_dat->master = dev_get_drvdata(pdev->dev.parent);
+ if (init_led & (1 << id)) {
+ dev_warn(dev, "LED %i already initialized\n", id);
+ break;
+ }
- INIT_WORK(&led_dat->work, mc13783_led_work);
+ init_led |= 1 << id;
+ leds->led[i].id = id;
+ leds->led[i].leds = leds;
+ leds->led[i].cdev.name = name;
+ leds->led[i].cdev.default_trigger = trig;
+ leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME;
+ leds->led[i].cdev.brightness_set = mc13xxx_led_set;
+ leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id);
- ret = led_classdev_register(pdev->dev.parent, &led_dat->cdev);
- if (ret) {
- dev_err(&pdev->dev, "failed to register led %d\n",
- led_dat->id);
- goto err_register;
- }
+ INIT_WORK(&leds->led[i].work, mc13xxx_led_work);
- ret = mc13783_led_setup(led_dat, led_cur->max_current);
+ ret = led_classdev_register(dev->parent, &leds->led[i].cdev);
if (ret) {
- dev_err(&pdev->dev, "unable to init led %d\n",
- led_dat->id);
- i++;
- goto err_register;
+ dev_err(dev, "Failed to register LED %i\n", id);
+ break;
}
}
- platform_set_drvdata(pdev, led);
- return 0;
-
-err_register:
- for (i = i - 1; i >= 0; i--) {
- led_classdev_unregister(&led[i].cdev);
- cancel_work_sync(&led[i].work);
- }
+ if (ret)
+ while (--i >= 0) {
+ led_classdev_unregister(&leds->led[i].cdev);
+ cancel_work_sync(&leds->led[i].work);
+ }
return ret;
}
-static int mc13783_led_remove(struct platform_device *pdev)
+static int mc13xxx_led_remove(struct platform_device *pdev)
{
- struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13783_led *led = platform_get_drvdata(pdev);
- struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < pdata->num_leds; i++) {
- led_classdev_unregister(&led[i].cdev);
- cancel_work_sync(&led[i].work);
+ for (i = 0; i < leds->num_leds; i++) {
+ led_classdev_unregister(&leds->led[i].cdev);
+ cancel_work_sync(&leds->led[i].work);
}
- mc13xxx_lock(dev);
+ return 0;
+}
- mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
- mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
- mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
- mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
- mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
- mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
+static const struct mc13xxx_led_devtype mc13783_led_devtype = {
+ .led_min = MC13783_LED_MD,
+ .led_max = MC13783_LED_B3,
+ .num_regs = 6,
+ .ledctrl_base = 51,
+};
- mc13xxx_unlock(dev);
+static const struct mc13xxx_led_devtype mc13892_led_devtype = {
+ .led_min = MC13892_LED_MD,
+ .led_max = MC13892_LED_B,
+ .num_regs = 4,
+ .ledctrl_base = 51,
+};
- platform_set_drvdata(pdev, NULL);
- return 0;
-}
+static const struct mc13xxx_led_devtype mc34708_led_devtype = {
+ .led_min = MC34708_LED_R,
+ .led_max = MC34708_LED_G,
+ .num_regs = 1,
+ .ledctrl_base = 54,
+};
+
+static const struct platform_device_id mc13xxx_led_id_table[] = {
+ { "mc13783-led", (kernel_ulong_t)&mc13783_led_devtype, },
+ { "mc13892-led", (kernel_ulong_t)&mc13892_led_devtype, },
+ { "mc34708-led", (kernel_ulong_t)&mc34708_led_devtype, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, mc13xxx_led_id_table);
-static struct platform_driver mc13783_led_driver = {
+static struct platform_driver mc13xxx_led_driver = {
.driver = {
- .name = "mc13783-led",
+ .name = "mc13xxx-led",
.owner = THIS_MODULE,
},
- .probe = mc13783_led_probe,
- .remove = mc13783_led_remove,
+ .remove = mc13xxx_led_remove,
+ .id_table = mc13xxx_led_id_table,
};
+module_platform_driver_probe(mc13xxx_led_driver, mc13xxx_led_probe);
-module_platform_driver(mc13783_led_driver);
-
-MODULE_DESCRIPTION("LEDs driver for Freescale MC13783 PMIC");
+MODULE_DESCRIPTION("LEDs driver for Freescale MC13XXX PMIC");
MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:mc13783-led");
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c
index c61c5ebcc08..e97f443a6e0 100644
--- a/drivers/leds/leds-netxbig.c
+++ b/drivers/leds/leds-netxbig.c
@@ -21,7 +21,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -306,7 +305,7 @@ create_netxbig_led(struct platform_device *pdev,
struct netxbig_led_data *led_dat,
const struct netxbig_led *template)
{
- struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
+ struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret;
spin_lock_init(&led_dat->lock);
@@ -354,7 +353,7 @@ create_netxbig_led(struct platform_device *pdev,
static int netxbig_led_probe(struct platform_device *pdev)
{
- struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
+ struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct netxbig_led_data *leds_data;
int i;
int ret;
@@ -391,7 +390,7 @@ err_free_leds:
static int netxbig_led_remove(struct platform_device *pdev)
{
- struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
+ struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct netxbig_led_data *leds_data;
int i;
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index 70137b1eecf..efa625883c8 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -23,13 +23,13 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_data/leds-kirkwood-ns2.h>
+#include <linux/of.h>
#include <linux/of_gpio.h>
/*
@@ -321,7 +321,7 @@ static inline int sizeof_ns2_led_priv(int num_leds)
static int ns2_led_probe(struct platform_device *pdev)
{
- struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
+ struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct ns2_led_priv *priv;
int i;
int ret;
@@ -374,8 +374,6 @@ static int ns2_led_remove(struct platform_device *pdev)
for (i = 0; i < priv->num_leds; i++)
delete_ns2_led(&priv->leds_data[i]);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c
index 98cae529373..c9d90609846 100644
--- a/drivers/leds/leds-ot200.c
+++ b/drivers/leds/leds-ot200.c
@@ -8,7 +8,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/leds.h>
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 0c597bdd23f..4a0e786b783 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -446,7 +446,8 @@ static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pca9532_data *data = i2c_get_clientdata(client);
- struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
+ struct pca9532_platform_data *pca9532_pdata =
+ dev_get_platdata(&client->dev);
if (!pca9532_pdata)
return -EIO;
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index edf485b773c..c3a08b60535 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -267,7 +267,7 @@ static int pca955x_probe(struct i2c_client *client,
chip = &pca955x_chipdefs[id->driver_data];
adapter = to_i2c_adapter(client->dev.parent);
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
/* Make sure the slave address / chip type combo given is possible */
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c
deleted file mode 100644
index 9aae5679ffb..00000000000
--- a/drivers/leds/leds-pca9633.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2011 bct electronic GmbH
- *
- * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
- *
- * Based on leds-pca955x.c
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
- * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
- *
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <linux/leds.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/workqueue.h>
-#include <linux/slab.h>
-#include <linux/platform_data/leds-pca9633.h>
-
-/* LED select registers determine the source that drives LED outputs */
-#define PCA9633_LED_OFF 0x0 /* LED driver off */
-#define PCA9633_LED_ON 0x1 /* LED driver on */
-#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */
-#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
-
-#define PCA9633_MODE1 0x00
-#define PCA9633_MODE2 0x01
-#define PCA9633_PWM_BASE 0x02
-#define PCA9633_LEDOUT 0x08
-
-static const struct i2c_device_id pca9633_id[] = {
- { "pca9633", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, pca9633_id);
-
-struct pca9633_led {
- struct i2c_client *client;
- struct work_struct work;
- enum led_brightness brightness;
- struct led_classdev led_cdev;
- int led_num; /* 0 .. 3 potentially */
- char name[32];
-};
-
-static void pca9633_led_work(struct work_struct *work)
-{
- struct pca9633_led *pca9633 = container_of(work,
- struct pca9633_led, work);
- u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
- int shift = 2 * pca9633->led_num;
- u8 mask = 0x3 << shift;
-
- switch (pca9633->brightness) {
- case LED_FULL:
- i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
- (ledout & ~mask) | (PCA9633_LED_ON << shift));
- break;
- case LED_OFF:
- i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
- ledout & ~mask);
- break;
- default:
- i2c_smbus_write_byte_data(pca9633->client,
- PCA9633_PWM_BASE + pca9633->led_num,
- pca9633->brightness);
- i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
- (ledout & ~mask) | (PCA9633_LED_PWM << shift));
- break;
- }
-}
-
-static void pca9633_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct pca9633_led *pca9633;
-
- pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
-
- pca9633->brightness = value;
-
- /*
- * Must use workqueue for the actual I/O since I2C operations
- * can sleep.
- */
- schedule_work(&pca9633->work);
-}
-
-static int pca9633_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct pca9633_led *pca9633;
- struct pca9633_platform_data *pdata;
- int i, err;
-
- pdata = client->dev.platform_data;
-
- if (pdata) {
- if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) {
- dev_err(&client->dev, "board info must claim at most 4 LEDs");
- return -EINVAL;
- }
- }
-
- pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
- if (!pca9633)
- return -ENOMEM;
-
- i2c_set_clientdata(client, pca9633);
-
- for (i = 0; i < 4; i++) {
- pca9633[i].client = client;
- pca9633[i].led_num = i;
-
- /* Platform data can specify LED names and default triggers */
- if (pdata && i < pdata->leds.num_leds) {
- if (pdata->leds.leds[i].name)
- snprintf(pca9633[i].name,
- sizeof(pca9633[i].name), "pca9633:%s",
- pdata->leds.leds[i].name);
- if (pdata->leds.leds[i].default_trigger)
- pca9633[i].led_cdev.default_trigger =
- pdata->leds.leds[i].default_trigger;
- } else {
- snprintf(pca9633[i].name, sizeof(pca9633[i].name),
- "pca9633:%d", i);
- }
-
- pca9633[i].led_cdev.name = pca9633[i].name;
- pca9633[i].led_cdev.brightness_set = pca9633_led_set;
-
- INIT_WORK(&pca9633[i].work, pca9633_led_work);
-
- err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
- if (err < 0)
- goto exit;
- }
-
- /* Disable LED all-call address and set normal mode */
- i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);
-
- /* Configure output: open-drain or totem pole (push-pull) */
- if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN)
- i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01);
-
- /* Turn off LEDs */
- i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);
-
- return 0;
-
-exit:
- while (i--) {
- led_classdev_unregister(&pca9633[i].led_cdev);
- cancel_work_sync(&pca9633[i].work);
- }
-
- return err;
-}
-
-static int pca9633_remove(struct i2c_client *client)
-{
- struct pca9633_led *pca9633 = i2c_get_clientdata(client);
- int i;
-
- for (i = 0; i < 4; i++) {
- led_classdev_unregister(&pca9633[i].led_cdev);
- cancel_work_sync(&pca9633[i].work);
- }
-
- return 0;
-}
-
-static struct i2c_driver pca9633_driver = {
- .driver = {
- .name = "leds-pca9633",
- .owner = THIS_MODULE,
- },
- .probe = pca9633_probe,
- .remove = pca9633_remove,
- .id_table = pca9633_id,
-};
-
-module_i2c_driver(pca9633_driver);
-
-MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
-MODULE_DESCRIPTION("PCA9633 LED driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c
new file mode 100644
index 00000000000..82589c0a568
--- /dev/null
+++ b/drivers/leds/leds-pca963x.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2011 bct electronic GmbH
+ * Copyright 2013 Qtechnology/AS
+ *
+ * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
+ * Author: Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ *
+ * Based on leds-pca955x.c
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
+ * LED driver for the PCA9634 I2C LED driver (7-bit slave address set by hw.)
+ *
+ * Note that hardware blinking violates the leds infrastructure driver
+ * interface since the hardware only supports blinking all LEDs with the
+ * same delay_on/delay_off rates. That is, only the LEDs that are set to
+ * blink will actually blink but all LEDs that are set to blink will blink
+ * in identical fashion. The delay_on/delay_off values of the last LED
+ * that is set to blink will be used for all of the blinking LEDs.
+ * Hardware blinking is disabled by default but can be enabled by setting
+ * the 'blink_type' member in the platform_data struct to 'PCA963X_HW_BLINK'
+ * or by adding the 'nxp,hw-blink' property to the DTS.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_data/leds-pca963x.h>
+
+/* LED select registers determine the source that drives LED outputs */
+#define PCA963X_LED_OFF 0x0 /* LED driver off */
+#define PCA963X_LED_ON 0x1 /* LED driver on */
+#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */
+#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
+
+#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */
+
+#define PCA963X_MODE1 0x00
+#define PCA963X_MODE2 0x01
+#define PCA963X_PWM_BASE 0x02
+
+enum pca963x_type {
+ pca9633,
+ pca9634,
+};
+
+struct pca963x_chipdef {
+ u8 grppwm;
+ u8 grpfreq;
+ u8 ledout_base;
+ int n_leds;
+};
+
+static struct pca963x_chipdef pca963x_chipdefs[] = {
+ [pca9633] = {
+ .grppwm = 0x6,
+ .grpfreq = 0x7,
+ .ledout_base = 0x8,
+ .n_leds = 4,
+ },
+ [pca9634] = {
+ .grppwm = 0xa,
+ .grpfreq = 0xb,
+ .ledout_base = 0xc,
+ .n_leds = 8,
+ },
+};
+
+/* Total blink period in milliseconds */
+#define PCA963X_BLINK_PERIOD_MIN 42
+#define PCA963X_BLINK_PERIOD_MAX 10667
+
+static const struct i2c_device_id pca963x_id[] = {
+ { "pca9632", pca9633 },
+ { "pca9633", pca9633 },
+ { "pca9634", pca9634 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca963x_id);
+
+enum pca963x_cmd {
+ BRIGHTNESS_SET,
+ BLINK_SET,
+};
+
+struct pca963x_led;
+
+struct pca963x {
+ struct pca963x_chipdef *chipdef;
+ struct mutex mutex;
+ struct i2c_client *client;
+ struct pca963x_led *leds;
+};
+
+struct pca963x_led {
+ struct pca963x *chip;
+ struct work_struct work;
+ enum led_brightness brightness;
+ struct led_classdev led_cdev;
+ int led_num; /* 0 .. 7 potentially */
+ enum pca963x_cmd cmd;
+ char name[32];
+ u8 gdc;
+ u8 gfrq;
+};
+
+static void pca963x_brightness_work(struct pca963x_led *pca963x)
+{
+ u8 ledout_addr = pca963x->chip->chipdef->ledout_base
+ + (pca963x->led_num / 4);
+ u8 ledout;
+ int shift = 2 * (pca963x->led_num % 4);
+ u8 mask = 0x3 << shift;
+
+ mutex_lock(&pca963x->chip->mutex);
+ ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
+ switch (pca963x->brightness) {
+ case LED_FULL:
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ (ledout & ~mask) | (PCA963X_LED_ON << shift));
+ break;
+ case LED_OFF:
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ ledout & ~mask);
+ break;
+ default:
+ i2c_smbus_write_byte_data(pca963x->chip->client,
+ PCA963X_PWM_BASE + pca963x->led_num,
+ pca963x->brightness);
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ (ledout & ~mask) | (PCA963X_LED_PWM << shift));
+ break;
+ }
+ mutex_unlock(&pca963x->chip->mutex);
+}
+
+static void pca963x_blink_work(struct pca963x_led *pca963x)
+{
+ u8 ledout_addr = pca963x->chip->chipdef->ledout_base +
+ (pca963x->led_num / 4);
+ u8 ledout;
+ u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client,
+ PCA963X_MODE2);
+ int shift = 2 * (pca963x->led_num % 4);
+ u8 mask = 0x3 << shift;
+
+ i2c_smbus_write_byte_data(pca963x->chip->client,
+ pca963x->chip->chipdef->grppwm, pca963x->gdc);
+
+ i2c_smbus_write_byte_data(pca963x->chip->client,
+ pca963x->chip->chipdef->grpfreq, pca963x->gfrq);
+
+ if (!(mode2 & PCA963X_MODE2_DMBLNK))
+ i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2,
+ mode2 | PCA963X_MODE2_DMBLNK);
+
+ mutex_lock(&pca963x->chip->mutex);
+ ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
+ if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift))
+ i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift));
+ mutex_unlock(&pca963x->chip->mutex);
+}
+
+static void pca963x_work(struct work_struct *work)
+{
+ struct pca963x_led *pca963x = container_of(work,
+ struct pca963x_led, work);
+
+ switch (pca963x->cmd) {
+ case BRIGHTNESS_SET:
+ pca963x_brightness_work(pca963x);
+ break;
+ case BLINK_SET:
+ pca963x_blink_work(pca963x);
+ break;
+ }
+}
+
+static void pca963x_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pca963x_led *pca963x;
+
+ pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
+
+ pca963x->cmd = BRIGHTNESS_SET;
+ pca963x->brightness = value;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca963x->work);
+}
+
+static int pca963x_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct pca963x_led *pca963x;
+ unsigned long time_on, time_off, period;
+ u8 gdc, gfrq;
+
+ pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
+
+ time_on = *delay_on;
+ time_off = *delay_off;
+
+ /* If both zero, pick reasonable defaults of 500ms each */
+ if (!time_on && !time_off) {
+ time_on = 500;
+ time_off = 500;
+ }
+
+ period = 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;
+ }
+
+ /*
+ * From manual: duty cycle = (GDC / 256) ->
+ * (time_on / period) = (GDC / 256) ->
+ * GDC = ((time_on * 256) / period)
+ */
+ gdc = (time_on * 256) / period;
+
+ /*
+ * From manual: period = ((GFRQ + 1) / 24) in seconds.
+ * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) ->
+ * GFRQ = ((period * 24 / 1000) - 1)
+ */
+ gfrq = (period * 24 / 1000) - 1;
+
+ pca963x->cmd = BLINK_SET;
+ pca963x->gdc = gdc;
+ pca963x->gfrq = gfrq;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca963x->work);
+
+ *delay_on = time_on;
+ *delay_off = time_off;
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static struct pca963x_platform_data *
+pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
+{
+ struct device_node *np = client->dev.of_node, *child;
+ struct pca963x_platform_data *pdata;
+ struct led_info *pca963x_leds;
+ int count;
+
+ count = of_get_child_count(np);
+ if (!count || count > chip->n_leds)
+ return ERR_PTR(-ENODEV);
+
+ pca963x_leds = devm_kzalloc(&client->dev,
+ sizeof(struct led_info) * chip->n_leds, GFP_KERNEL);
+ if (!pca963x_leds)
+ return ERR_PTR(-ENOMEM);
+
+ for_each_child_of_node(np, child) {
+ struct led_info led;
+ u32 reg;
+ int res;
+
+ res = of_property_read_u32(child, "reg", &reg);
+ if ((res != 0) || (reg >= chip->n_leds))
+ continue;
+ led.name =
+ of_get_property(child, "label", NULL) ? : child->name;
+ led.default_trigger =
+ of_get_property(child, "linux,default-trigger", NULL);
+ pca963x_leds[reg] = led;
+ }
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct pca963x_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->leds.leds = pca963x_leds;
+ pdata->leds.num_leds = chip->n_leds;
+
+ /* default to open-drain unless totem pole (push-pull) is specified */
+ if (of_property_read_bool(np, "nxp,totem-pole"))
+ pdata->outdrv = PCA963X_TOTEM_POLE;
+ else
+ pdata->outdrv = PCA963X_OPEN_DRAIN;
+
+ /* default to software blinking unless hardware blinking is specified */
+ if (of_property_read_bool(np, "nxp,hw-blink"))
+ pdata->blink_type = PCA963X_HW_BLINK;
+ else
+ pdata->blink_type = PCA963X_SW_BLINK;
+
+ return pdata;
+}
+
+static const struct of_device_id of_pca963x_match[] = {
+ { .compatible = "nxp,pca9632", },
+ { .compatible = "nxp,pca9633", },
+ { .compatible = "nxp,pca9634", },
+ {},
+};
+#else
+static struct pca963x_platform_data *
+pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
+static int pca963x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca963x *pca963x_chip;
+ struct pca963x_led *pca963x;
+ struct pca963x_platform_data *pdata;
+ struct pca963x_chipdef *chip;
+ int i, err;
+
+ chip = &pca963x_chipdefs[id->driver_data];
+ pdata = dev_get_platdata(&client->dev);
+
+ if (!pdata) {
+ pdata = pca963x_dt_init(client, chip);
+ if (IS_ERR(pdata)) {
+ dev_warn(&client->dev, "could not parse configuration\n");
+ pdata = NULL;
+ }
+ }
+
+ if (pdata && (pdata->leds.num_leds < 1 ||
+ pdata->leds.num_leds > chip->n_leds)) {
+ dev_err(&client->dev, "board info must claim 1-%d LEDs",
+ chip->n_leds);
+ return -EINVAL;
+ }
+
+ pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip),
+ GFP_KERNEL);
+ if (!pca963x_chip)
+ return -ENOMEM;
+ pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x),
+ GFP_KERNEL);
+ if (!pca963x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pca963x_chip);
+
+ mutex_init(&pca963x_chip->mutex);
+ pca963x_chip->chipdef = chip;
+ pca963x_chip->client = client;
+ pca963x_chip->leds = pca963x;
+
+ /* Turn off LEDs by default*/
+ i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00);
+ if (chip->n_leds > 4)
+ i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00);
+
+ for (i = 0; i < chip->n_leds; i++) {
+ pca963x[i].led_num = i;
+ pca963x[i].chip = pca963x_chip;
+
+ /* Platform data can specify LED names and default triggers */
+ if (pdata && i < pdata->leds.num_leds) {
+ if (pdata->leds.leds[i].name)
+ snprintf(pca963x[i].name,
+ sizeof(pca963x[i].name), "pca963x:%s",
+ pdata->leds.leds[i].name);
+ if (pdata->leds.leds[i].default_trigger)
+ pca963x[i].led_cdev.default_trigger =
+ pdata->leds.leds[i].default_trigger;
+ }
+ if (!pdata || i >= pdata->leds.num_leds ||
+ !pdata->leds.leds[i].name)
+ snprintf(pca963x[i].name, sizeof(pca963x[i].name),
+ "pca963x:%d:%.2x:%d", client->adapter->nr,
+ client->addr, i);
+
+ pca963x[i].led_cdev.name = pca963x[i].name;
+ pca963x[i].led_cdev.brightness_set = pca963x_led_set;
+
+ if (pdata && pdata->blink_type == PCA963X_HW_BLINK)
+ pca963x[i].led_cdev.blink_set = pca963x_blink_set;
+
+ INIT_WORK(&pca963x[i].work, pca963x_work);
+
+ err = led_classdev_register(&client->dev, &pca963x[i].led_cdev);
+ if (err < 0)
+ goto exit;
+ }
+
+ /* Disable LED all-call address and set normal mode */
+ i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00);
+
+ /* Configure output: open-drain or totem pole (push-pull) */
+ if (pdata && pdata->outdrv == PCA963X_OPEN_DRAIN)
+ i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x01);
+
+ return 0;
+
+exit:
+ while (i--) {
+ led_classdev_unregister(&pca963x[i].led_cdev);
+ cancel_work_sync(&pca963x[i].work);
+ }
+
+ return err;
+}
+
+static int pca963x_remove(struct i2c_client *client)
+{
+ struct pca963x *pca963x = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < pca963x->chipdef->n_leds; i++) {
+ led_classdev_unregister(&pca963x->leds[i].led_cdev);
+ cancel_work_sync(&pca963x->leds[i].work);
+ }
+
+ return 0;
+}
+
+static struct i2c_driver pca963x_driver = {
+ .driver = {
+ .name = "leds-pca963x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_pca963x_match),
+ },
+ .probe = pca963x_probe,
+ .remove = pca963x_remove,
+ .id_table = pca963x_id,
+};
+
+module_i2c_driver(pca963x_driver);
+
+MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
+MODULE_DESCRIPTION("PCA963X LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index faf52c005e8..d672bb4480f 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/fb.h>
@@ -66,9 +65,15 @@ static void led_pwm_set(struct led_classdev *led_cdev,
struct led_pwm_data *led_dat =
container_of(led_cdev, struct led_pwm_data, cdev);
unsigned int max = led_dat->cdev.max_brightness;
- unsigned int period = led_dat->period;
+ unsigned long long duty = led_dat->period;
- led_dat->duty = brightness * period / max;
+ duty *= brightness;
+ do_div(duty, max);
+
+ if (led_dat->active_low)
+ duty = led_dat->period - duty;
+
+ led_dat->duty = duty;
if (led_dat->can_sleep)
schedule_work(&led_dat->work);
@@ -82,140 +87,136 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
(sizeof(struct led_pwm_data) * num_leds);
}
-static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
+static void led_pwm_cleanup(struct led_pwm_priv *priv)
{
- struct device_node *node = pdev->dev.of_node;
- struct device_node *child;
- struct led_pwm_priv *priv;
- int count, ret;
+ while (priv->num_leds--) {
+ led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
+ if (priv->leds[priv->num_leds].can_sleep)
+ cancel_work_sync(&priv->leds[priv->num_leds].work);
+ }
+}
- /* count LEDs in this device, so we know how much to allocate */
- count = of_get_child_count(node);
- if (!count)
- return NULL;
+static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
+ struct led_pwm *led, struct device_node *child)
+{
+ struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
+ int ret;
+
+ led_data->active_low = led->active_low;
+ led_data->cdev.name = led->name;
+ led_data->cdev.default_trigger = led->default_trigger;
+ led_data->cdev.brightness_set = led_pwm_set;
+ led_data->cdev.brightness = LED_OFF;
+ led_data->cdev.max_brightness = led->max_brightness;
+ led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+ if (child)
+ led_data->pwm = devm_of_pwm_get(dev, child, NULL);
+ else
+ led_data->pwm = devm_pwm_get(dev, led->name);
+ if (IS_ERR(led_data->pwm)) {
+ ret = PTR_ERR(led_data->pwm);
+ dev_err(dev, "unable to request PWM for %s: %d\n",
+ led->name, ret);
+ return ret;
+ }
- priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count),
- GFP_KERNEL);
- if (!priv)
- return NULL;
+ if (child)
+ led_data->period = pwm_get_period(led_data->pwm);
- for_each_child_of_node(node, child) {
- struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
+ led_data->can_sleep = pwm_can_sleep(led_data->pwm);
+ if (led_data->can_sleep)
+ INIT_WORK(&led_data->work, led_pwm_work);
- led_dat->cdev.name = of_get_property(child, "label",
- NULL) ? : child->name;
+ led_data->period = pwm_get_period(led_data->pwm);
+ if (!led_data->period && (led->pwm_period_ns > 0))
+ led_data->period = led->pwm_period_ns;
- led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL);
- if (IS_ERR(led_dat->pwm)) {
- dev_err(&pdev->dev, "unable to request PWM for %s\n",
- led_dat->cdev.name);
- goto err;
- }
- /* Get the period from PWM core when n*/
- led_dat->period = pwm_get_period(led_dat->pwm);
+ ret = led_classdev_register(dev, &led_data->cdev);
+ if (ret == 0) {
+ priv->num_leds++;
+ } else {
+ dev_err(dev, "failed to register PWM led for %s: %d\n",
+ led->name, ret);
+ }
- led_dat->cdev.default_trigger = of_get_property(child,
- "linux,default-trigger", NULL);
- of_property_read_u32(child, "max-brightness",
- &led_dat->cdev.max_brightness);
+ return ret;
+}
+
+static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv)
+{
+ struct device_node *child;
+ struct led_pwm led;
+ int ret = 0;
+
+ memset(&led, 0, sizeof(led));
- led_dat->cdev.brightness_set = led_pwm_set;
- led_dat->cdev.brightness = LED_OFF;
- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ for_each_child_of_node(dev->of_node, child) {
+ led.name = of_get_property(child, "label", NULL) ? :
+ child->name;
- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
- if (led_dat->can_sleep)
- INIT_WORK(&led_dat->work, led_pwm_work);
+ led.default_trigger = of_get_property(child,
+ "linux,default-trigger", NULL);
+ led.active_low = of_property_read_bool(child, "active-low");
+ of_property_read_u32(child, "max-brightness",
+ &led.max_brightness);
- ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register for %s\n",
- led_dat->cdev.name);
+ ret = led_pwm_add(dev, priv, &led, child);
+ if (ret) {
of_node_put(child);
- goto err;
+ break;
}
- priv->num_leds++;
}
- return priv;
-err:
- while (priv->num_leds--)
- led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
-
- return NULL;
+ return ret;
}
static int led_pwm_probe(struct platform_device *pdev)
{
- struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct led_pwm_priv *priv;
- int i, ret = 0;
-
- if (pdata && pdata->num_leds) {
- priv = devm_kzalloc(&pdev->dev,
- sizeof_pwm_leds_priv(pdata->num_leds),
- GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- for (i = 0; i < pdata->num_leds; i++) {
- struct led_pwm *cur_led = &pdata->leds[i];
- struct led_pwm_data *led_dat = &priv->leds[i];
-
- led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name);
- if (IS_ERR(led_dat->pwm)) {
- ret = PTR_ERR(led_dat->pwm);
- dev_err(&pdev->dev,
- "unable to request PWM for %s\n",
- cur_led->name);
- goto err;
- }
-
- led_dat->cdev.name = cur_led->name;
- led_dat->cdev.default_trigger = cur_led->default_trigger;
- led_dat->active_low = cur_led->active_low;
- led_dat->period = cur_led->pwm_period_ns;
- led_dat->cdev.brightness_set = led_pwm_set;
- led_dat->cdev.brightness = LED_OFF;
- led_dat->cdev.max_brightness = cur_led->max_brightness;
- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
-
- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
- if (led_dat->can_sleep)
- INIT_WORK(&led_dat->work, led_pwm_work);
-
- ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
- if (ret < 0)
- goto err;
+ int count, i;
+ int ret = 0;
+
+ if (pdata)
+ count = pdata->num_leds;
+ else
+ count = of_get_child_count(pdev->dev.of_node);
+
+ if (!count)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (pdata) {
+ for (i = 0; i < count; i++) {
+ ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i],
+ NULL);
+ if (ret)
+ break;
}
- priv->num_leds = pdata->num_leds;
} else {
- priv = led_pwm_create_of(pdev);
- if (!priv)
- return -ENODEV;
+ ret = led_pwm_create_of(&pdev->dev, priv);
+ }
+
+ if (ret) {
+ led_pwm_cleanup(priv);
+ return ret;
}
platform_set_drvdata(pdev, priv);
return 0;
-
-err:
- while (i--)
- led_classdev_unregister(&priv->leds[i].cdev);
-
- return ret;
}
static int led_pwm_remove(struct platform_device *pdev)
{
struct led_pwm_priv *priv = platform_get_drvdata(pdev);
- int i;
- for (i = 0; i < priv->num_leds; i++) {
- led_classdev_unregister(&priv->leds[i].cdev);
- if (priv->leds[i].can_sleep)
- cancel_work_sync(&priv->leds[i].work);
- }
+ led_pwm_cleanup(priv);
return 0;
}
@@ -232,7 +233,7 @@ static struct platform_driver led_pwm_driver = {
.driver = {
.name = "leds_pwm",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(of_pwm_leds_match),
+ .of_match_table = of_pwm_leds_match,
},
};
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
index 4253a9b03db..358430db6e6 100644
--- a/drivers/leds/leds-regulator.c
+++ b/drivers/leds/leds-regulator.c
@@ -142,7 +142,8 @@ static void regulator_led_brightness_set(struct led_classdev *led_cdev,
static int regulator_led_probe(struct platform_device *pdev)
{
- struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
+ struct led_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
struct regulator_led *led;
struct regulator *vcc;
int ret = 0;
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
deleted file mode 100644
index 9483f1c1078..00000000000
--- a/drivers/leds/leds-renesas-tpu.c
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * LED control using Renesas TPU
- *
- * Copyright (C) 2011 Magnus Damm
- *
- * 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
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/printk.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/leds.h>
-#include <linux/platform_data/leds-renesas-tpu.h>
-#include <linux/gpio.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-#include <linux/workqueue.h>
-
-enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN };
-enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON };
-
-struct r_tpu_priv {
- struct led_classdev ldev;
- void __iomem *mapbase;
- struct clk *clk;
- struct platform_device *pdev;
- enum r_tpu_pin pin_state;
- enum r_tpu_timer timer_state;
- unsigned long min_rate;
- unsigned int refresh_rate;
- struct work_struct work;
- enum led_brightness new_brightness;
-};
-
-static DEFINE_SPINLOCK(r_tpu_lock);
-
-#define TSTR -1 /* Timer start register (shared register) */
-#define TCR 0 /* Timer control register (+0x00) */
-#define TMDR 1 /* Timer mode register (+0x04) */
-#define TIOR 2 /* Timer I/O control register (+0x08) */
-#define TIER 3 /* Timer interrupt enable register (+0x0c) */
-#define TSR 4 /* Timer status register (+0x10) */
-#define TCNT 5 /* Timer counter (+0x14) */
-#define TGRA 6 /* Timer general register A (+0x18) */
-#define TGRB 7 /* Timer general register B (+0x1c) */
-#define TGRC 8 /* Timer general register C (+0x20) */
-#define TGRD 9 /* Timer general register D (+0x24) */
-
-static inline unsigned short r_tpu_read(struct r_tpu_priv *p, int reg_nr)
-{
- struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
- void __iomem *base = p->mapbase;
- unsigned long offs = reg_nr << 2;
-
- if (reg_nr == TSTR)
- return ioread16(base - cfg->channel_offset);
-
- return ioread16(base + offs);
-}
-
-static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr,
- unsigned short value)
-{
- struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
- void __iomem *base = p->mapbase;
- unsigned long offs = reg_nr << 2;
-
- if (reg_nr == TSTR) {
- iowrite16(value, base - cfg->channel_offset);
- return;
- }
-
- iowrite16(value, base + offs);
-}
-
-static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start)
-{
- struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
- unsigned long flags, value;
-
- /* start stop register shared by multiple timer channels */
- spin_lock_irqsave(&r_tpu_lock, flags);
- value = r_tpu_read(p, TSTR);
-
- if (start)
- value |= 1 << cfg->timer_bit;
- else
- value &= ~(1 << cfg->timer_bit);
-
- r_tpu_write(p, TSTR, value);
- spin_unlock_irqrestore(&r_tpu_lock, flags);
-}
-
-static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness)
-{
- struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
- int prescaler[] = { 1, 4, 16, 64 };
- int k, ret;
- unsigned long rate, tmp;
-
- if (p->timer_state == R_TPU_TIMER_ON)
- return 0;
-
- /* wake up device and enable clock */
- pm_runtime_get_sync(&p->pdev->dev);
- ret = clk_enable(p->clk);
- if (ret) {
- dev_err(&p->pdev->dev, "cannot enable clock\n");
- return ret;
- }
-
- /* make sure channel is disabled */
- r_tpu_start_stop_ch(p, 0);
-
- /* get clock rate after enabling it */
- rate = clk_get_rate(p->clk);
-
- /* pick the lowest acceptable rate */
- for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--)
- if ((rate / prescaler[k]) >= p->min_rate)
- break;
-
- if (k < 0) {
- dev_err(&p->pdev->dev, "clock rate mismatch\n");
- goto err0;
- }
- dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n",
- rate, prescaler[k]);
-
- /* clear TCNT on TGRB match, count on rising edge, set prescaler */
- r_tpu_write(p, TCR, 0x0040 | k);
-
- /* output 0 until TGRA, output 1 until TGRB */
- r_tpu_write(p, TIOR, 0x0002);
-
- rate /= prescaler[k] * p->refresh_rate;
- r_tpu_write(p, TGRB, rate);
- dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate);
-
- tmp = (cfg->max_brightness - brightness) * rate;
- r_tpu_write(p, TGRA, tmp / cfg->max_brightness);
- dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness);
-
- /* PWM mode */
- r_tpu_write(p, TMDR, 0x0002);
-
- /* enable channel */
- r_tpu_start_stop_ch(p, 1);
-
- p->timer_state = R_TPU_TIMER_ON;
- return 0;
- err0:
- clk_disable(p->clk);
- pm_runtime_put_sync(&p->pdev->dev);
- return -ENOTSUPP;
-}
-
-static void r_tpu_disable(struct r_tpu_priv *p)
-{
- if (p->timer_state == R_TPU_TIMER_UNUSED)
- return;
-
- /* disable channel */
- r_tpu_start_stop_ch(p, 0);
-
- /* stop clock and mark device as idle */
- clk_disable(p->clk);
- pm_runtime_put_sync(&p->pdev->dev);
-
- p->timer_state = R_TPU_TIMER_UNUSED;
-}
-
-static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state,
- enum led_brightness brightness)
-{
- struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data;
-
- if (p->pin_state == new_state) {
- if (p->pin_state == R_TPU_PIN_GPIO)
- gpio_set_value(cfg->pin_gpio, brightness);
- return;
- }
-
- if (p->pin_state == R_TPU_PIN_GPIO)
- gpio_free(cfg->pin_gpio);
-
- if (p->pin_state == R_TPU_PIN_GPIO_FN)
- gpio_free(cfg->pin_gpio_fn);
-
- if (new_state == R_TPU_PIN_GPIO)
- gpio_request_one(cfg->pin_gpio, !!brightness ?
- GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
- cfg->name);
-
- if (new_state == R_TPU_PIN_GPIO_FN)
- gpio_request(cfg->pin_gpio_fn, cfg->name);
-
- p->pin_state = new_state;
-}
-
-static void r_tpu_work(struct work_struct *work)
-{
- struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work);
- enum led_brightness brightness = p->new_brightness;
-
- r_tpu_disable(p);
-
- /* off and maximum are handled as GPIO pins, in between PWM */
- if ((brightness == 0) || (brightness == p->ldev.max_brightness))
- r_tpu_set_pin(p, R_TPU_PIN_GPIO, brightness);
- else {
- r_tpu_set_pin(p, R_TPU_PIN_GPIO_FN, 0);
- r_tpu_enable(p, brightness);
- }
-}
-
-static void r_tpu_set_brightness(struct led_classdev *ldev,
- enum led_brightness brightness)
-{
- struct r_tpu_priv *p = container_of(ldev, struct r_tpu_priv, ldev);
- p->new_brightness = brightness;
- schedule_work(&p->work);
-}
-
-static int r_tpu_probe(struct platform_device *pdev)
-{
- struct led_renesas_tpu_config *cfg = pdev->dev.platform_data;
- struct r_tpu_priv *p;
- struct resource *res;
- int ret;
-
- if (!cfg) {
- dev_err(&pdev->dev, "missing platform data\n");
- return -ENODEV;
- }
-
- p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
- if (p == NULL) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
- return -ENOMEM;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get I/O memory\n");
- return -ENXIO;
- }
-
- /* map memory, let mapbase point to our channel */
- p->mapbase = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
- if (p->mapbase == NULL) {
- dev_err(&pdev->dev, "failed to remap I/O memory\n");
- return -ENXIO;
- }
-
- /* get hold of clock */
- p->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(p->clk)) {
- dev_err(&pdev->dev, "cannot get clock\n");
- return PTR_ERR(p->clk);
- }
-
- p->pdev = pdev;
- p->pin_state = R_TPU_PIN_UNUSED;
- p->timer_state = R_TPU_TIMER_UNUSED;
- p->refresh_rate = cfg->refresh_rate ? cfg->refresh_rate : 100;
- r_tpu_set_pin(p, R_TPU_PIN_GPIO, LED_OFF);
- platform_set_drvdata(pdev, p);
-
- INIT_WORK(&p->work, r_tpu_work);
-
- p->ldev.name = cfg->name;
- p->ldev.brightness = LED_OFF;
- p->ldev.max_brightness = cfg->max_brightness;
- p->ldev.brightness_set = r_tpu_set_brightness;
- p->ldev.flags |= LED_CORE_SUSPENDRESUME;
- ret = led_classdev_register(&pdev->dev, &p->ldev);
- if (ret < 0)
- goto err0;
-
- /* max_brightness may be updated by the LED core code */
- p->min_rate = p->ldev.max_brightness * p->refresh_rate;
-
- pm_runtime_enable(&pdev->dev);
- return 0;
-
- err0:
- r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
- return ret;
-}
-
-static int r_tpu_remove(struct platform_device *pdev)
-{
- struct r_tpu_priv *p = platform_get_drvdata(pdev);
-
- r_tpu_set_brightness(&p->ldev, LED_OFF);
- led_classdev_unregister(&p->ldev);
- cancel_work_sync(&p->work);
- r_tpu_disable(p);
- r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
-
- pm_runtime_disable(&pdev->dev);
-
- return 0;
-}
-
-static struct platform_driver r_tpu_device_driver = {
- .probe = r_tpu_probe,
- .remove = r_tpu_remove,
- .driver = {
- .name = "leds-renesas-tpu",
- }
-};
-
-module_platform_driver(r_tpu_device_driver);
-
-MODULE_AUTHOR("Magnus Damm");
-MODULE_DESCRIPTION("Renesas TPU LED Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index e1a0df63a37..785eb53a87f 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -12,16 +12,15 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_data/leds-s3c24xx.h>
-#include <mach/hardware.h>
#include <mach/regs-gpio.h>
-#include <linux/platform_data/leds-s3c24xx.h>
+#include <plat/gpio-cfg.h>
/* our context */
@@ -71,16 +70,14 @@ static int s3c24xx_led_remove(struct platform_device *dev)
static int s3c24xx_led_probe(struct platform_device *dev)
{
- struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;
+ struct s3c24xx_led_platdata *pdata = dev_get_platdata(&dev->dev);
struct s3c24xx_gpio_led *led;
int ret;
led = devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),
GFP_KERNEL);
- if (led == NULL) {
- dev_err(&dev->dev, "No memory for device\n");
+ if (!led)
return -ENOMEM;
- }
platform_set_drvdata(dev, led);
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
index 64e204e714f..2eb3ef62962 100644
--- a/drivers/leds/leds-ss4200.c
+++ b/drivers/leds/leds-ss4200.c
@@ -63,7 +63,7 @@ MODULE_LICENSE("GPL");
/*
* PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
*/
-static DEFINE_PCI_DEVICE_TABLE(ich7_lpc_pci_id) = {
+static const struct pci_device_id ich7_lpc_pci_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
@@ -78,7 +78,7 @@ static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id)
return 1;
}
-static bool __initdata nodetect;
+static bool nodetect;
module_param_named(nodetect, nodetect, bool, 0);
MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
@@ -91,7 +91,7 @@ MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
* detected as working, but in reality it is not) as low as
* possible.
*/
-static struct dmi_system_id __initdata nas_led_whitelist[] = {
+static struct dmi_system_id nas_led_whitelist[] __initdata = {
{
.callback = ss4200_led_dmi_callback,
.ident = "Intel SS4200-E",
@@ -197,7 +197,7 @@ static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
spin_unlock(&nasgpio_gpio_lock);
}
-u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
+static u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
{
struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
u32 gpio_in;
diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c
index 89792990088..0b8cc4a021a 100644
--- a/drivers/leds/leds-sunfire.c
+++ b/drivers/leds/leds-sunfire.c
@@ -135,10 +135,8 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
}
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
- if (!p) {
- dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n");
+ if (!p)
return -ENOMEM;
- }
for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {
struct led_classdev *lp = &p->leds[i].led_cdev;
@@ -159,14 +157,14 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
}
}
- dev_set_drvdata(&pdev->dev, p);
+ platform_set_drvdata(pdev, p);
return 0;
}
static int sunfire_led_generic_remove(struct platform_device *pdev)
{
- struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev);
+ struct sunfire_drvdata *p = platform_get_drvdata(pdev);
int i;
for (i = 0; i < NUM_LEDS_PER_BOARD; i++)
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 98fe021ba27..3d9e267a56c 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -4,77 +4,87 @@
* The TCA6507 is a programmable LED controller that can drive 7
* separate lines either by holding them low, or by pulsing them
* with modulated width.
- * The modulation can be varied in a simple pattern to produce a blink or
- * double-blink.
+ * The modulation can be varied in a simple pattern to produce a
+ * blink or double-blink.
*
- * This driver can configure each line either as a 'GPIO' which is out-only
- * (no pull-up) or as an LED with variable brightness and hardware-assisted
- * blinking.
+ * This driver can configure each line either as a 'GPIO' which is
+ * out-only (pull-up resistor required) or as an LED with variable
+ * brightness and hardware-assisted blinking.
*
- * Apart from OFF and ON there are three programmable brightness levels which
- * can be programmed from 0 to 15 and indicate how many 500usec intervals in
- * each 8msec that the led is 'on'. The levels are named MASTER, BANK0 and
- * BANK1.
+ * Apart from OFF and ON there are three programmable brightness
+ * levels which can be programmed from 0 to 15 and indicate how many
+ * 500usec intervals in each 8msec that the led is 'on'. The levels
+ * are named MASTER, BANK0 and BANK1.
*
- * There are two different blink rates that can be programmed, each with
- * separate time for rise, on, fall, off and second-off. Thus if 3 or more
- * different non-trivial rates are required, software must be used for the extra
- * rates. The two different blink rates must align with the two levels BANK0 and
- * BANK1.
- * This driver does not support double-blink so 'second-off' always matches
- * 'off'.
+ * There are two different blink rates that can be programmed, each
+ * with separate time for rise, on, fall, off and second-off. Thus if
+ * 3 or more different non-trivial rates are required, software must
+ * be used for the extra rates. The two different blink rates must
+ * align with the two levels BANK0 and BANK1. This driver does not
+ * support double-blink so 'second-off' always matches 'off'.
*
- * Only 16 different times can be programmed in a roughly logarithmic scale from
- * 64ms to 16320ms. To be precise the possible times are:
+ * Only 16 different times can be programmed in a roughly logarithmic
+ * scale from 64ms to 16320ms. To be precise the possible times are:
* 0, 64, 128, 192, 256, 384, 512, 768,
* 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320
*
- * Times that cannot be closely matched with these must be
- * handled in software. This driver allows 12.5% error in matching.
+ * Times that cannot be closely matched with these must be handled in
+ * software. This driver allows 12.5% error in matching.
*
- * This driver does not allow rise/fall rates to be set explicitly. When trying
- * to match a given 'on' or 'off' period, an appropriate pair of 'change' and
- * 'hold' times are chosen to get a close match. If the target delay is even,
- * the 'change' number will be the smaller; if odd, the 'hold' number will be
- * the smaller.
-
- * Choosing pairs of delays with 12.5% errors allows us to match delays in the
- * ranges: 56-72, 112-144, 168-216, 224-27504, 28560-36720.
- * 26% of the achievable sums can be matched by multiple pairings. For example
- * 1536 == 1536+0, 1024+512, or 768+768. This driver will always choose the
- * pairing with the least maximum - 768+768 in this case. Other pairings are
- * not available.
+ * This driver does not allow rise/fall rates to be set explicitly.
+ * When trying to match a given 'on' or 'off' period, an appropriate
+ * pair of 'change' and 'hold' times are chosen to get a close match.
+ * If the target delay is even, the 'change' number will be the
+ * smaller; if odd, the 'hold' number will be the smaller.
+
+ * Choosing pairs of delays with 12.5% errors allows us to match
+ * delays in the ranges: 56-72, 112-144, 168-216, 224-27504,
+ * 28560-36720.
+ * 26% of the achievable sums can be matched by multiple pairings.
+ * For example 1536 == 1536+0, 1024+512, or 768+768.
+ * This driver will always choose the pairing with the least
+ * maximum - 768+768 in this case. Other pairings are not available.
*
- * Access to the 3 levels and 2 blinks are on a first-come, first-served basis.
- * Access can be shared by multiple leds if they have the same level and
- * either same blink rates, or some don't blink.
- * When a led changes, it relinquishes access and tries again, so it might
- * lose access to hardware blink.
- * If a blink engine cannot be allocated, software blink is used.
- * If the desired brightness cannot be allocated, the closest available non-zero
- * brightness is used. As 'full' is always available, the worst case would be
- * to have two different blink rates at '1', with Max at '2', then other leds
- * will have to choose between '2' and '16'. Hopefully this is not likely.
+ * Access to the 3 levels and 2 blinks are on a first-come,
+ * first-served basis. Access can be shared by multiple leds if they
+ * have the same level and either same blink rates, or some don't
+ * blink. When a led changes, it relinquishes access and tries again,
+ * so it might lose access to hardware blink.
*
- * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the brightness
- * and LEDs using the blink. It can only be reprogrammed when the appropriate
- * counter is zero. The MASTER level has a single usage count.
+ * If a blink engine cannot be allocated, software blink is used. If
+ * the desired brightness cannot be allocated, the closest available
+ * non-zero brightness is used. As 'full' is always available, the
+ * worst case would be to have two different blink rates at '1', with
+ * Max at '2', then other leds will have to choose between '2' and
+ * '16'. Hopefully this is not likely.
*
- * Each Led has programmable 'on' and 'off' time as milliseconds. With each
- * there is a flag saying if it was explicitly requested or defaulted.
- * Similarly the banks know if each time was explicit or a default. Defaults
- * are permitted to be changed freely - they are not recognised when matching.
+ * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the
+ * brightness and LEDs using the blink. It can only be reprogrammed
+ * when the appropriate counter is zero. The MASTER level has a
+ * single usage count.
*
+ * Each LED has programmable 'on' and 'off' time as milliseconds.
+ * With each there is a flag saying if it was explicitly requested or
+ * defaulted. Similarly the banks know if each time was explicit or a
+ * default. Defaults are permitted to be changed freely - they are
+ * not recognised when matching.
*
- * An led-tca6507 device must be provided with platform data. This data
- * lists for each output: the name, default trigger, and whether the signal
- * is being used as a GPiO rather than an led. 'struct led_plaform_data'
- * is used for this. If 'name' is NULL, the output isn't used. If 'flags'
- * is TCA6507_MAKE_CPIO, the output is a GPO.
- * The "struct led_platform_data" can be embedded in a
- * "struct tca6507_platform_data" which adds a 'gpio_base' for the GPiOs,
- * and a 'setup' callback which is called once the GPiOs are available.
*
+ * An led-tca6507 device must be provided with platform data or
+ * configured via devicetree.
+ *
+ * The platform-data lists for each output: the name, default trigger,
+ * and whether the signal is being used as a GPIO rather than an LED.
+ * 'struct led_plaform_data' is used for this. If 'name' is NULL, the
+ * output isn't used. If 'flags' is TCA6507_MAKE_GPIO, the output is
+ * a GPO. The "struct led_platform_data" can be embedded in a "struct
+ * tca6507_platform_data" which adds a 'gpio_base' for the GPIOs, and
+ * a 'setup' callback which is called once the GPIOs are available.
+ *
+ * When configured via devicetree there is one child for each output.
+ * The "reg" determines the output number and "compatible" determines
+ * whether it is an LED or a GPIO. "linux,default-trigger" can set a
+ * default trigger.
*/
#include <linux/module.h>
@@ -192,17 +202,18 @@ MODULE_DEVICE_TABLE(i2c, tca6507_id);
static int choose_times(int msec, int *c1p, int *c2p)
{
/*
- * Choose two timecodes which add to 'msec' as near as possible.
- * The first returned is the 'on' or 'off' time. The second is to be
- * used as a 'fade-on' or 'fade-off' time. If 'msec' is even,
- * the first will not be smaller than the second. If 'msec' is odd,
- * the first will not be larger than the second.
- * If we cannot get a sum within 1/8 of 'msec' fail with -EINVAL,
- * otherwise return the sum that was achieved, plus 1 if the first is
- * smaller.
- * If two possibilities are equally good (e.g. 512+0, 256+256), choose
- * the first pair so there is more change-time visible (i.e. it is
- * softer).
+ * Choose two timecodes which add to 'msec' as near as
+ * possible. The first returned is the 'on' or 'off' time.
+ * The second is to be used as a 'fade-on' or 'fade-off' time.
+ * If 'msec' is even, the first will not be smaller than the
+ * second. If 'msec' is odd, the first will not be larger
+ * than the second.
+ * If we cannot get a sum within 1/8 of 'msec' fail with
+ * -EINVAL, otherwise return the sum that was achieved, plus 1
+ * if the first is smaller.
+ * If two possibilities are equally good (e.g. 512+0,
+ * 256+256), choose the first pair so there is more
+ * change-time visible (i.e. it is softer).
*/
int c1, c2;
int tmax = msec * 9 / 8;
@@ -255,8 +266,8 @@ static int choose_times(int msec, int *c1p, int *c2p)
}
/*
- * Update the register file with the appropriate 3-bit state for
- * the given led.
+ * Update the register file with the appropriate 3-bit state for the
+ * given led.
*/
static void set_select(struct tca6507_chip *tca, int led, int val)
{
@@ -274,9 +285,9 @@ static void set_select(struct tca6507_chip *tca, int led, int val)
}
}
-/* Update the register file with the appropriate 4-bit code for
- * one bank or other. This can be used for timers, for levels, or
- * for initialisation.
+/* Update the register file with the appropriate 4-bit code for one
+ * bank or other. This can be used for timers, for levels, or for
+ * initialization.
*/
static void set_code(struct tca6507_chip *tca, int reg, int bank, int new)
{
@@ -309,7 +320,7 @@ static void set_level(struct tca6507_chip *tca, int bank, int level)
tca->bank[bank].level = level;
}
-/* Record all relevant time code for a given bank */
+/* Record all relevant time codes for a given bank */
static void set_times(struct tca6507_chip *tca, int bank)
{
int c1, c2;
@@ -317,7 +328,8 @@ static void set_times(struct tca6507_chip *tca, int bank)
result = choose_times(tca->bank[bank].ontime, &c1, &c2);
dev_dbg(&tca->client->dev,
- "Chose on times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1],
+ "Chose on times %d(%d) %d(%d) for %dms\n",
+ c1, time_codes[c1],
c2, time_codes[c2], tca->bank[bank].ontime);
set_code(tca, TCA6507_FADE_ON, bank, c2);
set_code(tca, TCA6507_FULL_ON, bank, c1);
@@ -325,7 +337,8 @@ static void set_times(struct tca6507_chip *tca, int bank)
result = choose_times(tca->bank[bank].offtime, &c1, &c2);
dev_dbg(&tca->client->dev,
- "Chose off times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1],
+ "Chose off times %d(%d) %d(%d) for %dms\n",
+ c1, time_codes[c1],
c2, time_codes[c2], tca->bank[bank].offtime);
set_code(tca, TCA6507_FADE_OFF, bank, c2);
set_code(tca, TCA6507_FIRST_OFF, bank, c1);
@@ -373,7 +386,8 @@ static void led_release(struct tca6507_led *led)
static int led_prepare(struct tca6507_led *led)
{
- /* Assign this led to a bank, configuring that bank if necessary. */
+ /* Assign this led to a bank, configuring that bank if
+ * necessary. */
int level = TO_LEVEL(led->led_cdev.brightness);
struct tca6507_chip *tca = led->chip;
int c1, c2;
@@ -389,10 +403,10 @@ static int led_prepare(struct tca6507_led *led)
if (led->ontime == 0 || led->offtime == 0) {
/*
- * Just set the brightness, choosing first usable bank.
- * If none perfect, choose best.
- * Count backwards so we check MASTER bank first
- * to avoid wasting a timer.
+ * Just set the brightness, choosing first usable
+ * bank. If none perfect, choose best. Count
+ * backwards so we check MASTER bank first to avoid
+ * wasting a timer.
*/
int best = -1;/* full-on */
int diff = 15-level;
@@ -433,9 +447,9 @@ static int led_prepare(struct tca6507_led *led)
}
/*
- * We have on/off time so we need to try to allocate a timing bank.
- * First check if times are compatible with hardware and give up if
- * not.
+ * We have on/off time so we need to try to allocate a timing
+ * bank. First check if times are compatible with hardware
+ * and give up if not.
*/
if (choose_times(led->ontime, &c1, &c2) < 0)
return -EINVAL;
@@ -523,8 +537,8 @@ static int led_assign(struct tca6507_led *led)
err = led_prepare(led);
if (err) {
/*
- * Can only fail on timer setup. In that case we need to
- * re-establish as steady level.
+ * Can only fail on timer setup. In that case we need
+ * to re-establish as steady level.
*/
led->ontime = 0;
led->offtime = 0;
@@ -594,8 +608,8 @@ static void tca6507_gpio_set_value(struct gpio_chip *gc,
spin_lock_irqsave(&tca->lock, flags);
/*
- * 'OFF' is floating high, and 'ON' is pulled down, so it has the
- * inverse sense of 'val'.
+ * 'OFF' is floating high, and 'ON' is pulled down, so it has
+ * the inverse sense of 'val'.
*/
set_select(tca, tca->gpio_map[offset],
val ? TCA6507_LS_LED_OFF : TCA6507_LS_LED_ON);
@@ -638,6 +652,9 @@ static int tca6507_probe_gpios(struct i2c_client *client,
tca->gpio.direction_output = tca6507_gpio_direction_output;
tca->gpio.set = tca6507_gpio_set_value;
tca->gpio.dev = &client->dev;
+#ifdef CONFIG_OF_GPIO
+ tca->gpio.of_node = of_node_get(client->dev.of_node);
+#endif
err = gpiochip_add(&tca->gpio);
if (err) {
tca->gpio.ngpio = 0;
@@ -682,7 +699,7 @@ tca6507_led_dt_init(struct i2c_client *client)
return ERR_PTR(-ENODEV);
tca_leds = devm_kzalloc(&client->dev,
- sizeof(struct led_info) * count, GFP_KERNEL);
+ sizeof(struct led_info) * NUM_LEDS, GFP_KERNEL);
if (!tca_leds)
return ERR_PTR(-ENOMEM);
@@ -695,9 +712,11 @@ tca6507_led_dt_init(struct i2c_client *client)
of_get_property(child, "label", NULL) ? : child->name;
led.default_trigger =
of_get_property(child, "linux,default-trigger", NULL);
-
+ led.flags = 0;
+ if (of_property_match_string(child, "compatible", "gpio") >= 0)
+ led.flags |= TCA6507_MAKE_GPIO;
ret = of_property_read_u32(child, "reg", &reg);
- if (ret != 0)
+ if (ret != 0 || reg < 0 || reg >= NUM_LEDS)
continue;
tca_leds[reg] = led;
@@ -708,8 +727,10 @@ tca6507_led_dt_init(struct i2c_client *client)
return ERR_PTR(-ENOMEM);
pdata->leds.leds = tca_leds;
- pdata->leds.num_leds = count;
-
+ pdata->leds.num_leds = NUM_LEDS;
+#ifdef CONFIG_GPIOLIB
+ pdata->gpio_base = -1;
+#endif
return pdata;
}
@@ -737,7 +758,7 @@ static int tca6507_probe(struct i2c_client *client,
int i = 0;
adapter = to_i2c_adapter(client->dev.parent);
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
return -EIO;
diff --git a/drivers/leds/leds-versatile.c b/drivers/leds/leds-versatile.c
new file mode 100644
index 00000000000..80553022d66
--- /dev/null
+++ b/drivers/leds/leds-versatile.c
@@ -0,0 +1,110 @@
+/*
+ * Driver for the 8 user LEDs found on the RealViews and Versatiles
+ * Based on DaVinci's DM365 board code
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Linus Walleij <triad@df.lth.se>
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+
+struct versatile_led {
+ void __iomem *base;
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} versatile_leds[] = {
+ { "versatile:0", "heartbeat", },
+ { "versatile:1", "mmc0", },
+ { "versatile:2", "cpu0" },
+ { "versatile:3", "cpu1" },
+ { "versatile:4", "cpu2" },
+ { "versatile:5", "cpu3" },
+ { "versatile:6", },
+ { "versatile:7", },
+};
+
+static void versatile_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct versatile_led *led = container_of(cdev,
+ struct versatile_led, cdev);
+ u32 reg = readl(led->base);
+
+ if (b != LED_OFF)
+ reg |= led->mask;
+ else
+ reg &= ~led->mask;
+ writel(reg, led->base);
+}
+
+static enum led_brightness versatile_led_get(struct led_classdev *cdev)
+{
+ struct versatile_led *led = container_of(cdev,
+ struct versatile_led, cdev);
+ u32 reg = readl(led->base);
+
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
+}
+
+static int versatile_leds_probe(struct platform_device *dev)
+{
+ int i;
+ struct resource *res;
+ void __iomem *base;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&dev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* All off */
+ writel(0, base);
+ for (i = 0; i < ARRAY_SIZE(versatile_leds); i++) {
+ struct versatile_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->base = base;
+ led->cdev.name = versatile_leds[i].name;
+ led->cdev.brightness_set = versatile_led_set;
+ led->cdev.brightness_get = versatile_led_get;
+ led->cdev.default_trigger = versatile_leds[i].trigger;
+ led->mask = BIT(i);
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver versatile_leds_driver = {
+ .driver = {
+ .name = "versatile-leds",
+ },
+ .probe = versatile_leds_probe,
+};
+
+module_platform_driver(versatile_leds_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("ARM Versatile LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
index 6bd5c679d87..e72c974142d 100644
--- a/drivers/leds/leds-wm831x-status.c
+++ b/drivers/leds/leds-wm831x-status.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/leds.h>
@@ -230,9 +229,9 @@ static int wm831x_status_probe(struct platform_device *pdev)
int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
int ret;
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ res = platform_get_resource(pdev, IORESOURCE_REG, 0);
if (res == NULL) {
- dev_err(&pdev->dev, "No I/O resource\n");
+ dev_err(&pdev->dev, "No register resource\n");
ret = -EINVAL;
goto err;
}
@@ -241,13 +240,13 @@ static int wm831x_status_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- dev_set_drvdata(&pdev->dev, drvdata);
+ platform_set_drvdata(pdev, drvdata);
drvdata->wm831x = wm831x;
drvdata->reg = res->start;
- if (wm831x->dev->platform_data)
- chip_pdata = wm831x->dev->platform_data;
+ if (dev_get_platdata(wm831x->dev))
+ chip_pdata = dev_get_platdata(wm831x->dev);
else
chip_pdata = NULL;
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index 8a181d56602..4133ffe2901 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/err.h>
@@ -203,7 +202,7 @@ static int wm8350_led_probe(struct platform_device *pdev)
{
struct regulator *isink, *dcdc;
struct wm8350_led *led;
- struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
+ struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
int i;
if (pdata == NULL) {
diff --git a/drivers/leds/trigger/ledtrig-backlight.c b/drivers/leds/trigger/ledtrig-backlight.c
index 3c9c88a07eb..47e55aa9eef 100644
--- a/drivers/leds/trigger/ledtrig-backlight.c
+++ b/drivers/leds/trigger/ledtrig-backlight.c
@@ -36,26 +36,28 @@ static int fb_notifier_callback(struct notifier_block *p,
struct bl_trig_notifier, notifier);
struct led_classdev *led = n->led;
struct fb_event *fb_event = data;
- int *blank = fb_event->data;
- int new_status = *blank ? BLANK : UNBLANK;
+ int *blank;
+ int new_status;
- switch (event) {
- case FB_EVENT_BLANK:
- if (new_status == n->old_status)
- break;
+ /* If we aren't interested in this event, skip it immediately ... */
+ if (event != FB_EVENT_BLANK)
+ return 0;
- if ((n->old_status == UNBLANK) ^ n->invert) {
- n->brightness = led->brightness;
- __led_set_brightness(led, LED_OFF);
- } else {
- __led_set_brightness(led, n->brightness);
- }
+ blank = fb_event->data;
+ new_status = *blank ? BLANK : UNBLANK;
- n->old_status = new_status;
+ if (new_status == n->old_status)
+ return 0;
- break;
+ if ((n->old_status == UNBLANK) ^ n->invert) {
+ n->brightness = led->brightness;
+ __led_set_brightness(led, LED_OFF);
+ } else {
+ __led_set_brightness(led, n->brightness);
}
+ n->old_status = new_status;
+
return 0;
}
diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c
index 118335eccc5..aec0f02b6b3 100644
--- a/drivers/leds/trigger/ledtrig-cpu.c
+++ b/drivers/leds/trigger/ledtrig-cpu.c
@@ -26,6 +26,7 @@
#include <linux/percpu.h>
#include <linux/syscore_ops.h>
#include <linux/rwsem.h>
+#include <linux/cpu.h>
#include "../leds.h"
#define MAX_NAME_LEN 8
@@ -46,7 +47,7 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
*/
void ledtrig_cpu(enum cpu_led_event ledevt)
{
- struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig);
+ struct led_trigger_cpu *trig = this_cpu_ptr(&cpu_trig);
/* Locate the correct CPU LED */
switch (ledevt) {
@@ -92,6 +93,26 @@ static struct syscore_ops ledtrig_cpu_syscore_ops = {
.resume = ledtrig_cpu_syscore_resume,
};
+static int ledtrig_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_STARTING:
+ ledtrig_cpu(CPU_LED_START);
+ break;
+ case CPU_DYING:
+ ledtrig_cpu(CPU_LED_STOP);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static struct notifier_block ledtrig_cpu_nb = {
+ .notifier_call = ledtrig_cpu_notify,
+};
+
static int __init ledtrig_cpu_init(void)
{
int cpu;
@@ -113,6 +134,7 @@ static int __init ledtrig_cpu_init(void)
}
register_syscore_ops(&ledtrig_cpu_syscore_ops);
+ register_cpu_notifier(&ledtrig_cpu_nb);
pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n");
@@ -124,6 +146,8 @@ static void __exit ledtrig_cpu_exit(void)
{
int cpu;
+ unregister_cpu_notifier(&ledtrig_cpu_nb);
+
for_each_possible_cpu(cpu) {
struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);