Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 27 May 2010 18:34:20 +0000 (11:34 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 27 May 2010 18:34:20 +0000 (11:34 -0700)
* '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

14 files changed:
arch/arm/mach-mx3/mach-mx31moboard.c
arch/arm/mach-orion5x/dns323-setup.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-class.c
drivers/leds/leds-88pm860x.c
drivers/leds/leds-gpio.c
drivers/leds/leds-lp3944.c
drivers/leds/leds-mc13783.c [new file with mode: 0644]
drivers/leds/leds-net5501.c [new file with mode: 0644]
drivers/leds/leds-ss4200.c
drivers/mfd/mc13783-core.c
include/linux/leds.h
include/linux/mfd/mc13783.h

index 33a8d35..62b5e40 100644 (file)
@@ -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 = {
index 685f34a..fe0de16 100644 (file)
@@ -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,
index 505eb64..f98d17a 100644 (file)
@@ -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
index 0cd8b99..2493de4 100644 (file)
@@ -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
index 69e7d86..2606600 100644 (file)
@@ -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
index 16a60c0..b767710 100644 (file)
@@ -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;
index 6d94b0b..26843dd 100644 (file)
@@ -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);
index 8d5ecce..932a58d 100644 (file)
@@ -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 (file)
index 0000000..f05bb08
--- /dev/null
@@ -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 (file)
index 0000000..3063f59
--- /dev/null
@@ -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);
index 51477ec..a688293 100644 (file)
@@ -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;
index 1f68eca..fecf38a 100644 (file)
@@ -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;
 }
 
index d8bf966..ba6986a 100644 (file)
@@ -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);
 };
index 8895d9d..4a894f6 100644 (file)
@@ -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;
 };