From 98652efceab8f0b1a3a1b2750544a395ec056115 Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Sat, 17 Apr 2010 13:57:17 +0200 Subject: leds: 88pm860x - fix checking in probe function Improve device and platform data checks in probe function. Signed-off-by: Haojian Zhuang Signed-off-by: Christoph Fritz Signed-off-by: Richard Purdie --- drivers/leds/leds-88pm860x.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 16a60c06c96..b7677106cff 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -256,8 +256,10 @@ static int pm860x_led_probe(struct platform_device *pdev) if (pdev->dev.parent->platform_data) { pm860x_pdata = pdev->dev.parent->platform_data; pdata = pm860x_pdata->led; - } else - pdata = NULL; + } else { + dev_err(&pdev->dev, "missing platform data\n"); + return -EINVAL; + } data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL); if (data == NULL) @@ -268,8 +270,11 @@ static int pm860x_led_probe(struct platform_device *pdev) data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; data->iset = pdata->iset; data->port = __check_device(pdata, data->name); - if (data->port < 0) + if (data->port < 0) { + dev_err(&pdev->dev, "check device failed\n"); + kfree(data); return -EINVAL; + } data->current_brightness = 0; data->cdev.name = data->name; -- cgit v1.2.3-18-g5258 From 14e40f644b020d473415342461b7c62e3bb5e312 Mon Sep 17 00:00:00 2001 From: Bjarke Istrup Pedersen Date: Mon, 24 May 2010 12:15:56 -0700 Subject: leds: Add LED driver for the Soekris net5501 board It is based on the previously submitted code by Alessandro Zummo, but is changed to use the new GPIO driver with 2.6.33, and the driver has been moved to drivers/leds where it belongs. [akpm@linux-foundation.org: coding-style fixes] [randy.dunlap@oracle.com: fix net5501 kconfig] Signed-off-by: Bjarke Istrup Pedersen Signed-off-by: Randy Dunlap Signed-off-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 10 +++++ drivers/leds/Makefile | 1 + drivers/leds/leds-net5501.c | 94 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 drivers/leds/leds-net5501.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 505eb64c329..ecb6932c363 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -67,6 +67,16 @@ config LEDS_NET48XX This option enables support for the Soekris net4801 and net4826 error LED. +config LEDS_NET5501 + tristate "LED Support for Soekris net5501 series Error LED" + depends on LEDS_CLASS && LEDS_TRIGGERS + depends on LEDS_GPIO_PLATFORM && GPIO_CS5535 + select LEDS_TRIGGER_DEFAULT_ON + default n + help + Add support for the Soekris net5501 board (detection, error led + and GPIO). + config LEDS_FSG tristate "LED Support for the Freecom FSG-3" depends on MACH_FSG diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 0cd8b995738..77119f2e26b 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o +obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o diff --git a/drivers/leds/leds-net5501.c b/drivers/leds/leds-net5501.c new file mode 100644 index 00000000000..3063f591f0d --- /dev/null +++ b/drivers/leds/leds-net5501.c @@ -0,0 +1,94 @@ +/* + * Soekris board support code + * + * Copyright (C) 2008-2009 Tower Technologies + * Written by Alessandro Zummo + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +static struct gpio_led net5501_leds[] = { + { + .name = "error", + .gpio = 6, + .default_trigger = "default-on", + }, +}; + +static struct gpio_led_platform_data net5501_leds_data = { + .num_leds = ARRAY_SIZE(net5501_leds), + .leds = net5501_leds, +}; + +static struct platform_device net5501_leds_dev = { + .name = "leds-gpio", + .id = -1, + .dev.platform_data = &net5501_leds_data, +}; + +static void __init init_net5501(void) +{ + platform_device_register(&net5501_leds_dev); +} + +struct soekris_board { + u16 offset; + char *sig; + u8 len; + void (*init)(void); +}; + +static struct soekris_board __initdata boards[] = { + { 0xb7b, "net5501", 7, init_net5501 }, /* net5501 v1.33/1.33c */ + { 0xb1f, "net5501", 7, init_net5501 }, /* net5501 v1.32i */ +}; + +static int __init soekris_init(void) +{ + int i; + unsigned char *rombase, *bios; + + if (!is_geode()) + return 0; + + rombase = ioremap(0xffff0000, 0xffff); + if (!rombase) { + printk(KERN_INFO "Soekris net5501 LED driver failed to get rombase"); + return 0; + } + + bios = rombase + 0x20; /* null terminated */ + + if (strncmp(bios, "comBIOS", 7)) + goto unmap; + + for (i = 0; i < ARRAY_SIZE(boards); i++) { + unsigned char *model = rombase + boards[i].offset; + + if (strncmp(model, boards[i].sig, boards[i].len) == 0) { + printk(KERN_INFO "Soekris %s: %s\n", model, bios); + + if (boards[i].init) + boards[i].init(); + break; + } + } + +unmap: + iounmap(rombase); + return 0; +} + +arch_initcall(soekris_init); -- cgit v1.2.3-18-g5258 From 2146325df2c2640059a9e064890c30c6e259b458 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 22 May 2010 20:54:55 +1000 Subject: leds: leds-gpio: Change blink_set callback to be able to turn off blinking The leds-gpio blink_set() callback follows the same prototype as the main leds subsystem blink_set() one. The problem is that to stop blink, normally, a leds driver does it in the brightness_set() callback when asked to set a new fixed value. However, with leds-gpio, the platform has no hook to do so, as this later callback results in a standard GPIO manipulation. This changes the leds-gpio specific callback to take a new argument that indicates whether the LED should be blinking or not and in what state it should be set if not. We also update the dns323 platform which seems to be the only user of this so far. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Richard Purdie --- drivers/leds/leds-gpio.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 6d94b0b9979..26843dd6b85 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -26,7 +26,8 @@ struct gpio_led_data { u8 new_level; u8 can_sleep; u8 active_low; - int (*platform_gpio_blink_set)(unsigned gpio, + u8 blinking; + int (*platform_gpio_blink_set)(unsigned gpio, int state, unsigned long *delay_on, unsigned long *delay_off); }; @@ -35,7 +36,13 @@ static void gpio_led_work(struct work_struct *work) struct gpio_led_data *led_dat = container_of(work, struct gpio_led_data, work); - gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); + if (led_dat->blinking) { + led_dat->platform_gpio_blink_set(led_dat->gpio, + led_dat->new_level, + NULL, NULL); + led_dat->blinking = 0; + } else + gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); } static void gpio_led_set(struct led_classdev *led_cdev, @@ -60,8 +67,14 @@ static void gpio_led_set(struct led_classdev *led_cdev, if (led_dat->can_sleep) { led_dat->new_level = level; schedule_work(&led_dat->work); - } else - gpio_set_value(led_dat->gpio, level); + } else { + if (led_dat->blinking) { + led_dat->platform_gpio_blink_set(led_dat->gpio, level, + NULL, NULL); + led_dat->blinking = 0; + } else + gpio_set_value(led_dat->gpio, level); + } } static int gpio_blink_set(struct led_classdev *led_cdev, @@ -70,12 +83,14 @@ static int gpio_blink_set(struct led_classdev *led_cdev, struct gpio_led_data *led_dat = container_of(led_cdev, struct gpio_led_data, cdev); - return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off); + led_dat->blinking = 1; + return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, + delay_on, delay_off); } static int __devinit create_gpio_led(const struct gpio_led *template, struct gpio_led_data *led_dat, struct device *parent, - int (*blink_set)(unsigned, unsigned long *, unsigned long *)) + int (*blink_set)(unsigned, int, unsigned long *, unsigned long *)) { int ret, state; @@ -97,6 +112,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template, led_dat->gpio = template->gpio; led_dat->can_sleep = gpio_cansleep(template->gpio); led_dat->active_low = template->active_low; + led_dat->blinking = 0; if (blink_set) { led_dat->platform_gpio_blink_set = blink_set; led_dat->cdev.blink_set = gpio_blink_set; @@ -113,7 +129,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template, ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); if (ret < 0) goto err; - + INIT_WORK(&led_dat->work, gpio_led_work); ret = led_classdev_register(parent, &led_dat->cdev); @@ -234,6 +250,7 @@ static int __devinit of_gpio_leds_probe(struct of_device *ofdev, led.gpio = of_get_gpio_flags(child, 0, &flags); led.active_low = flags & OF_GPIO_ACTIVE_LOW; led.name = of_get_property(child, "label", NULL) ? : child->name; + led.blinking = 0; led.default_trigger = of_get_property(child, "linux,default-trigger", NULL); state = of_get_property(child, "default-state", NULL); -- cgit v1.2.3-18-g5258 From 569762ef3dcf8fc5aecdb059d8c0741b90fe1d17 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 17 May 2010 08:48:28 +0800 Subject: leds: led-class: set permissions on max_brightness file to 0444 max_brightness is not writable, thus set permissions to 0444. Signed-off-by: Axel Lin Signed-off-by: Richard Purdie --- drivers/leds/led-class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 69e7d86a514..26066007650 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -74,7 +74,7 @@ static ssize_t led_max_brightness_show(struct device *dev, static struct device_attribute led_class_attrs[] = { __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), - __ATTR(max_brightness, 0644, led_max_brightness_show, NULL), + __ATTR(max_brightness, 0444, led_max_brightness_show, NULL), #ifdef CONFIG_LEDS_TRIGGERS __ATTR(trigger, 0644, led_trigger_show, led_trigger_store), #endif -- cgit v1.2.3-18-g5258 From 7e1ce34f25c984a93dc0a2d8c217f7f78516b376 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 17 May 2010 17:47:48 +0800 Subject: leds: leds-lp3944: properly handle lp3944_configure fail in lp3944_probe In current implementation, lp3944_probe return 0 even if lp3944_configure fail. Therefore, led_classdev_unregister will be executed twice ( in error handling of lp3944_configure and lp3944_remove ). This patch properly handles lp3944_configure fail in lp3944_probe. Signed-off-by: Axel Lin Acked-by: Wolfram Sang Acked-by: Antonio Ospite Signed-off-by: Richard Purdie --- drivers/leds/leds-lp3944.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index 8d5ecceba18..932a58da76c 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -379,6 +379,7 @@ static int __devinit lp3944_probe(struct i2c_client *client, { struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data; struct lp3944_data *data; + int err; if (lp3944_pdata == NULL) { dev_err(&client->dev, "no platform data\n"); @@ -401,9 +402,13 @@ static int __devinit lp3944_probe(struct i2c_client *client, mutex_init(&data->lock); - dev_info(&client->dev, "lp3944 enabled\n"); + err = lp3944_configure(client, data, lp3944_pdata); + if (err < 0) { + kfree(data); + return err; + } - lp3944_configure(client, data, lp3944_pdata); + dev_info(&client->dev, "lp3944 enabled\n"); return 0; } -- cgit v1.2.3-18-g5258 From 1e653accf7d2b02e75af550963b261b3243ac20e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 17 May 2010 13:42:43 +0800 Subject: leds: leds-ss4200: fix led_classdev_unregister twice in error handling In current implementation, if device_create_file failed in register_nasgpio_led, led_classdev_unregister will be executed twice. ( in register_nasgpio_led it calls led_classdev_unregister before return and in nas_gpio_init out_err ) This patch fixes it by only unregistering those that were successfully registered in out_err. ( not including last failed register_nasgpio_led call ) Signed-off-by: Axel Lin Acked-by: Dave Hansen Signed-off-by: Richard Purdie --- drivers/leds/leds-ss4200.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 51477ec7139..a688293abd0 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -534,7 +534,7 @@ static int __init nas_gpio_init(void) set_power_light_amber_noblink(); return 0; out_err: - for (; i >= 0; i--) + for (i--; i >= 0; i--) unregister_nasgpio_led(i); pci_unregister_driver(&nas_gpio_pci_driver); return ret; -- cgit v1.2.3-18-g5258 From 7fdcef8a414eaeb367b3696005b25283d62d195d Mon Sep 17 00:00:00 2001 From: Philippe Rétornaz Date: Wed, 19 May 2010 09:24:31 +0200 Subject: leds: Add mc13783 LED support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This add basic led support for Freescale MC13783 PMIC. Signed-off-by: Philippe Rétornaz Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 7 + drivers/leds/Makefile | 1 + drivers/leds/leds-mc13783.c | 403 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/mc13783-core.c | 4 + 4 files changed, 415 insertions(+) create mode 100644 drivers/leds/leds-mc13783.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index ecb6932c363..f98d17a7108 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -295,6 +295,13 @@ config LEDS_DELL_NETBOOKS This adds support for the Latitude 2100 and similar notebooks that have an external LED. +config LEDS_MC13783 + tristate "LED Support for MC13783 PMIC" + depends on MFD_MC13783 + help + This option enable support for on-chip LED drivers found + on Freescale Semiconductor MC13783 PMIC. + config LEDS_TRIGGERS bool "LED Trigger support" help diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 77119f2e26b..2493de49937 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o +obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c new file mode 100644 index 00000000000..f05bb08d0f0 --- /dev/null +++ b/drivers/leds/leds-mc13783.c @@ -0,0 +1,403 @@ +/* + * LEDs driver for Freescale MC13783 + * + * Copyright (C) 2010 Philippe Rétornaz + * + * Based on leds-da903x: + * Copyright (C) 2008 Compulab, Ltd. + * Mike Rapoport + * + * Copyright (C) 2006-2008 Marvell International Ltd. + * Eric Miao + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct mc13783_led { + struct led_classdev cdev; + struct work_struct work; + struct mc13783 *master; + enum led_brightness new_brightness; + int id; +}; + +#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; + } + + mc13783_lock(led->master); + + mc13783_reg_rmw(led->master, reg, mask, value); + + mc13783_unlock(led->master); +} + +static void mc13783_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct mc13783_led *led; + + led = container_of(led_cdev, struct mc13783_led, cdev); + led->new_brightness = value; + schedule_work(&led->work); +} + +static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) +{ + int shift = 0; + int mask = 0; + int value = 0; + int reg = 0; + int ret, bank; + + 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; + 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: + 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; + break; + } + + mc13783_lock(led->master); + + ret = mc13783_reg_rmw(led->master, reg, mask << shift, + value << shift); + + mc13783_unlock(led->master); + return ret; +} + +static int __devinit mc13783_leds_prepare(struct platform_device *pdev) +{ + struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); + int ret = 0; + int reg = 0; + + mc13783_lock(dev); + + if (pdata->flags & MC13783_LED_TC1HALF) + reg |= MC13783_LED_C1_TC1HALF_BIT; + + if (pdata->flags & MC13783_LED_SLEWLIMTC) + reg |= MC13783_LED_Cx_SLEWLIM_BIT; + + ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg); + if (ret) + goto out; + + reg = (pdata->bl_period & MC13783_LED_Cx_PERIOD_MASK) << + MC13783_LED_Cx_PERIOD; + + if (pdata->flags & MC13783_LED_SLEWLIMBL) + reg |= MC13783_LED_Cx_SLEWLIM_BIT; + + ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg); + if (ret) + goto out; + + reg = (pdata->tc1_period & MC13783_LED_Cx_PERIOD_MASK) << + MC13783_LED_Cx_PERIOD; + + if (pdata->flags & MC13783_LED_TRIODE_TC1) + reg |= MC13783_LED_Cx_TRIODE_TC_BIT; + + ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg); + if (ret) + goto out; + + reg = (pdata->tc2_period & MC13783_LED_Cx_PERIOD_MASK) << + MC13783_LED_Cx_PERIOD; + + if (pdata->flags & MC13783_LED_TRIODE_TC2) + reg |= MC13783_LED_Cx_TRIODE_TC_BIT; + + ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg); + if (ret) + goto out; + + reg = (pdata->tc3_period & MC13783_LED_Cx_PERIOD_MASK) << + MC13783_LED_Cx_PERIOD; + + if (pdata->flags & MC13783_LED_TRIODE_TC3) + reg |= MC13783_LED_Cx_TRIODE_TC_BIT;; + + ret = mc13783_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 = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg); + +out: + mc13783_unlock(dev); + return ret; +} + +static int __devinit mc13783_led_probe(struct platform_device *pdev) +{ + struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13783_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; + } + + if (pdata->num_leds < 1 || pdata->num_leds > MC13783_LED_MAX) { + dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds); + return -EINVAL; + } + + led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); + if (led == NULL) { + dev_err(&pdev->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + ret = mc13783_leds_prepare(pdev); + if (ret) { + dev_err(&pdev->dev, "unable to init led driver\n"); + goto err_free; + } + + for (i = 0; i < pdata->num_leds; i++) { + led_dat = &led[i]; + led_cur = &pdata->led[i]; + + 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; + } + + if (init_led & (1 << led_cur->id)) { + dev_err(&pdev->dev, "led %d already initialized\n", + led_cur->id); + ret = -EINVAL; + goto err_register; + } + + 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); + + INIT_WORK(&led_dat->work, mc13783_led_work); + + 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; + } + + ret = mc13783_led_setup(led_dat, led_cur->max_current); + if (ret) { + dev_err(&pdev->dev, "unable to init led %d\n", + led_dat->id); + i++; + goto err_register; + } + } + + 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); + } + +err_free: + kfree(led); + return ret; +} + +static int __devexit mc13783_led_remove(struct platform_device *pdev) +{ + struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13783_led *led = platform_get_drvdata(pdev); + struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); + int i; + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&led[i].cdev); + cancel_work_sync(&led[i].work); + } + + mc13783_lock(dev); + + mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0); + mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0); + mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0); + mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0); + mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0); + mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0); + + mc13783_unlock(dev); + + kfree(led); + return 0; +} + +static struct platform_driver mc13783_led_driver = { + .driver = { + .name = "mc13783-led", + .owner = THIS_MODULE, + }, + .probe = mc13783_led_probe, + .remove = __devexit_p(mc13783_led_remove), +}; + +static int __init mc13783_led_init(void) +{ + return platform_driver_register(&mc13783_led_driver); +} +module_init(mc13783_led_init); + +static void __exit mc13783_led_exit(void) +{ + platform_driver_unregister(&mc13783_led_driver); +} +module_exit(mc13783_led_exit); + +MODULE_DESCRIPTION("LEDs driver for Freescale MC13783 PMIC"); +MODULE_AUTHOR("Philippe Retornaz "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mc13783-led"); diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c index 1f68ecadddc..fecf38a4f02 100644 --- a/drivers/mfd/mc13783-core.c +++ b/drivers/mfd/mc13783-core.c @@ -679,6 +679,10 @@ err_revision: if (pdata->flags & MC13783_USE_TOUCHSCREEN) mc13783_add_subdevice(mc13783, "mc13783-ts"); + if (pdata->flags & MC13783_USE_LED) + mc13783_add_subdevice_pdata(mc13783, "mc13783-led", + pdata->leds, sizeof(*pdata->leds)); + return 0; } -- cgit v1.2.3-18-g5258