aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-05-27 11:34:20 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-05-27 11:34:20 -0700
commit3ddab4788d4980b1b3dc324fdd105adab3812418 (patch)
tree91b25590723d2a81ecd4d42753412cb8c4897bf0
parentd1e0fe252e1c410164127b3000613afeaf47e49f (diff)
parenta7cca8aec9d08231207503e4823e7b47bfa7e596 (diff)
Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds: leds: Add mx31moboard MC13783 led support leds: Add mc13783 LED support leds: leds-ss4200: fix led_classdev_unregister twice in error handling leds: leds-lp3944: properly handle lp3944_configure fail in lp3944_probe leds: led-class: set permissions on max_brightness file to 0444 leds: leds-gpio: Change blink_set callback to be able to turn off blinking leds: Add LED driver for the Soekris net5501 board leds: 88pm860x - fix checking in probe function
-rw-r--r--arch/arm/mach-mx3/mach-mx31moboard.c45
-rw-r--r--arch/arm/mach-orion5x/dns323-setup.c22
-rw-r--r--drivers/leds/Kconfig17
-rw-r--r--drivers/leds/Makefile2
-rw-r--r--drivers/leds/led-class.c2
-rw-r--r--drivers/leds/leds-88pm860x.c11
-rw-r--r--drivers/leds/leds-gpio.c31
-rw-r--r--drivers/leds/leds-lp3944.c9
-rw-r--r--drivers/leds/leds-mc13783.c403
-rw-r--r--drivers/leds/leds-net5501.c94
-rw-r--r--drivers/leds/leds-ss4200.c2
-rw-r--r--drivers/mfd/mc13783-core.c4
-rw-r--r--include/linux/leds.h12
-rw-r--r--include/linux/mfd/mc13783.h66
14 files changed, 691 insertions, 29 deletions
diff --git a/arch/arm/mach-mx3/mach-mx31moboard.c b/arch/arm/mach-mx3/mach-mx31moboard.c
index 33a8d35498a..62b5e40165d 100644
--- a/arch/arm/mach-mx3/mach-mx31moboard.c
+++ b/arch/arm/mach-mx3/mach-mx31moboard.c
@@ -220,11 +220,54 @@ static struct mc13783_regulator_init_data moboard_regulators[] = {
},
};
+static struct mc13783_led_platform_data moboard_led[] = {
+ {
+ .id = MC13783_LED_R1,
+ .name = "coreboard-led-4:red",
+ .max_current = 2,
+ },
+ {
+ .id = MC13783_LED_G1,
+ .name = "coreboard-led-4:green",
+ .max_current = 2,
+ },
+ {
+ .id = MC13783_LED_B1,
+ .name = "coreboard-led-4:blue",
+ .max_current = 2,
+ },
+ {
+ .id = MC13783_LED_R2,
+ .name = "coreboard-led-5:red",
+ .max_current = 3,
+ },
+ {
+ .id = MC13783_LED_G2,
+ .name = "coreboard-led-5:green",
+ .max_current = 3,
+ },
+ {
+ .id = MC13783_LED_B2,
+ .name = "coreboard-led-5:blue",
+ .max_current = 3,
+ },
+};
+
+static struct mc13783_leds_platform_data moboard_leds = {
+ .num_leds = ARRAY_SIZE(moboard_led),
+ .led = moboard_led,
+ .flags = MC13783_LED_SLEWLIMTC,
+ .abmode = MC13783_LED_AB_DISABLED,
+ .tc1_period = MC13783_LED_PERIOD_10MS,
+ .tc2_period = MC13783_LED_PERIOD_10MS,
+};
+
static struct mc13783_platform_data moboard_pmic = {
.regulators = moboard_regulators,
.num_regulators = ARRAY_SIZE(moboard_regulators),
+ .leds = &moboard_leds,
.flags = MC13783_USE_REGULATOR | MC13783_USE_RTC |
- MC13783_USE_ADC,
+ MC13783_USE_ADC | MC13783_USE_LED,
};
static struct spi_board_info moboard_spi_board_info[] __initdata = {
diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c
index 685f34a9634..fe0de1698ed 100644
--- a/arch/arm/mach-orion5x/dns323-setup.c
+++ b/arch/arm/mach-orion5x/dns323-setup.c
@@ -240,22 +240,23 @@ error_fail:
#define ORION_BLINK_HALF_PERIOD 100 /* ms */
-static int dns323_gpio_blink_set(unsigned gpio,
+static int dns323_gpio_blink_set(unsigned gpio, int state,
unsigned long *delay_on, unsigned long *delay_off)
{
- static int value = 0;
- if (!*delay_on && !*delay_off)
+ if (delay_on && delay_off && !*delay_on && !*delay_off)
*delay_on = *delay_off = ORION_BLINK_HALF_PERIOD;
- if (ORION_BLINK_HALF_PERIOD == *delay_on
- && ORION_BLINK_HALF_PERIOD == *delay_off) {
- value = !value;
- orion_gpio_set_blink(gpio, value);
- return 0;
+ switch(state) {
+ case GPIO_LED_NO_BLINK_LOW:
+ case GPIO_LED_NO_BLINK_HIGH:
+ orion_gpio_set_blink(gpio, 0);
+ gpio_set_value(gpio, state);
+ break;
+ case GPIO_LED_BLINK:
+ orion_gpio_set_blink(gpio, 1);
}
-
- return -EINVAL;
+ return 0;
}
static struct gpio_led dns323_leds[] = {
@@ -263,6 +264,7 @@ static struct gpio_led dns323_leds[] = {
.name = "power:blue",
.gpio = DNS323_GPIO_LED_POWER2,
.default_trigger = "timer",
+ .active_low = 1,
}, {
.name = "right:amber",
.gpio = DNS323_GPIO_LED_RIGHT_AMBER,
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 505eb64c329..f98d17a7108 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
@@ -285,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 0cd8b995738..2493de49937 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
@@ -35,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/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
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;
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);
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;
}
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 <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/slab.h>
+
+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 <philippe.retornaz@epfl.ch>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mc13783-led");
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 <a.zummo@towertech.it>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <asm/geode.h>
+
+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);
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;
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;
}
diff --git a/include/linux/leds.h b/include/linux/leds.h
index d8bf9665e70..ba6986a1166 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -149,14 +149,18 @@ struct gpio_led {
unsigned default_state : 2;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
};
-#define LEDS_GPIO_DEFSTATE_OFF 0
-#define LEDS_GPIO_DEFSTATE_ON 1
-#define LEDS_GPIO_DEFSTATE_KEEP 2
+#define LEDS_GPIO_DEFSTATE_OFF 0
+#define LEDS_GPIO_DEFSTATE_ON 1
+#define LEDS_GPIO_DEFSTATE_KEEP 2
struct gpio_led_platform_data {
int num_leds;
struct gpio_led *leds;
- int (*gpio_blink_set)(unsigned gpio,
+
+#define GPIO_LED_NO_BLINK_LOW 0 /* No blink GPIO state low */
+#define GPIO_LED_NO_BLINK_HIGH 1 /* No blink GPIO state high */
+#define GPIO_LED_BLINK 2 /* Plase, blink */
+ int (*gpio_blink_set)(unsigned gpio, int state,
unsigned long *delay_on,
unsigned long *delay_off);
};
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index 8895d9d8879..4a894f68854 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -64,6 +64,70 @@ static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq)
MC13783_ADC0_TSMOD1 | \
MC13783_ADC0_TSMOD2)
+struct mc13783_led_platform_data {
+#define MC13783_LED_MD 0
+#define MC13783_LED_AD 1
+#define MC13783_LED_KP 2
+#define MC13783_LED_R1 3
+#define MC13783_LED_G1 4
+#define MC13783_LED_B1 5
+#define MC13783_LED_R2 6
+#define MC13783_LED_G2 7
+#define MC13783_LED_B2 8
+#define MC13783_LED_R3 9
+#define MC13783_LED_G3 10
+#define MC13783_LED_B3 11
+#define MC13783_LED_MAX MC13783_LED_B3
+ int id;
+ const char *name;
+ const char *default_trigger;
+
+/* Three or two bits current selection depending on the led */
+ char max_current;
+};
+
+struct mc13783_leds_platform_data {
+ int num_leds;
+ struct mc13783_led_platform_data *led;
+
+#define MC13783_LED_TRIODE_MD (1 << 0)
+#define MC13783_LED_TRIODE_AD (1 << 1)
+#define MC13783_LED_TRIODE_KP (1 << 2)
+#define MC13783_LED_BOOST_EN (1 << 3)
+#define MC13783_LED_TC1HALF (1 << 4)
+#define MC13783_LED_SLEWLIMTC (1 << 5)
+#define MC13783_LED_SLEWLIMBL (1 << 6)
+#define MC13783_LED_TRIODE_TC1 (1 << 7)
+#define MC13783_LED_TRIODE_TC2 (1 << 8)
+#define MC13783_LED_TRIODE_TC3 (1 << 9)
+ int flags;
+
+#define MC13783_LED_AB_DISABLED 0
+#define MC13783_LED_AB_MD1 1
+#define MC13783_LED_AB_MD12 2
+#define MC13783_LED_AB_MD123 3
+#define MC13783_LED_AB_MD1234 4
+#define MC13783_LED_AB_MD1234_AD1 5
+#define MC13783_LED_AB_MD1234_AD12 6
+#define MC13783_LED_AB_MD1_AD 7
+ char abmode;
+
+#define MC13783_LED_ABREF_200MV 0
+#define MC13783_LED_ABREF_400MV 1
+#define MC13783_LED_ABREF_600MV 2
+#define MC13783_LED_ABREF_800MV 3
+ char abref;
+
+#define MC13783_LED_PERIOD_10MS 0
+#define MC13783_LED_PERIOD_100MS 1
+#define MC13783_LED_PERIOD_500MS 2
+#define MC13783_LED_PERIOD_2S 3
+ char bl_period;
+ char tc1_period;
+ char tc2_period;
+ char tc3_period;
+};
+
/* to be cleaned up */
struct regulator_init_data;
@@ -80,12 +144,14 @@ struct mc13783_regulator_platform_data {
struct mc13783_platform_data {
int num_regulators;
struct mc13783_regulator_init_data *regulators;
+ struct mc13783_leds_platform_data *leds;
#define MC13783_USE_TOUCHSCREEN (1 << 0)
#define MC13783_USE_CODEC (1 << 1)
#define MC13783_USE_ADC (1 << 2)
#define MC13783_USE_RTC (1 << 3)
#define MC13783_USE_REGULATOR (1 << 4)
+#define MC13783_USE_LED (1 << 5)
unsigned int flags;
};