summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>2011-07-20 20:58:00 +0530
committerSrinidhi KASAGAR <srinidhi.kasagar@stericsson.com>2011-10-03 07:01:20 +0200
commit6af7a3ed543e355b4345f8565f009c412fcf1bf2 (patch)
treef3aee7cc69b2b4f7caae7d3d2d2b151bf9020f8a
parent160cebfb0d9fcaa4fc9b5ee615a4923990072fca (diff)
u5500: leds: add ab5500v2 HVLED blink feature
This patch adds support for AB5500 v2.0 HVLED hardware blink feature in existing driver. ST-Ericsson Linux next: ER 336280 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson ID: ER 364964 Change-Id:I73e1caeac14774b7d3e03d1e7c5e4bd16fc7d06a Signed-off-by: Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27547 Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32746 Reviewed-by: Naga RADHESH Y <naga.radheshy@stericsson.com> Tested-by: Naga RADHESH Y <naga.radheshy@stericsson.com>
-rw-r--r--arch/arm/mach-ux500/board-u5500.c14
-rw-r--r--drivers/leds/leds-ab5500.c505
-rw-r--r--include/linux/leds-ab5500.h14
3 files changed, 461 insertions, 72 deletions
diff --git a/arch/arm/mach-ux500/board-u5500.c b/arch/arm/mach-ux500/board-u5500.c
index 7a1715ea4aa..b2056bd28bb 100644
--- a/arch/arm/mach-ux500/board-u5500.c
+++ b/arch/arm/mach-ux500/board-u5500.c
@@ -108,24 +108,30 @@ static struct lm3530_platform_data u5500_als_platform_data = {
/* leds-ab5500 */
static struct ab5500_hvleds_platform_data ab5500_hvleds_data = {
- .hw_blink = false,
+ .hw_fade = false,
.leds = {
[0] = {
.name = "red",
+ .led_on = true,
.led_id = 0,
- .status = AB5500_LED_ON,
+ .fade_hi = 255,
+ .fade_lo = 0,
.max_current = 10, /* wrong value may damage h/w */
},
[1] = {
.name = "green",
+ .led_on = true,
.led_id = 1,
- .status = AB5500_LED_ON,
+ .fade_hi = 255,
+ .fade_lo = 0,
.max_current = 10, /* wrong value may damage h/w */
},
[2] {
.name = "blue",
+ .led_on = true,
.led_id = 2,
- .status = AB5500_LED_ON,
+ .fade_hi = 255,
+ .fade_lo = 0,
.max_current = 10, /* wrong value may damage h/w */
},
},
diff --git a/drivers/leds/leds-ab5500.c b/drivers/leds/leds-ab5500.c
index 94db3a6feea..294551b1962 100644
--- a/drivers/leds/leds-ab5500.c
+++ b/drivers/leds/leds-ab5500.c
@@ -1,13 +1,47 @@
/*
+ * leds-ab5500.c - driver for High Voltage (HV) LED in ST-Ericsson AB5500 chip
+ *
* Copyright (C) 2011 ST-Ericsson SA.
*
* License Terms: GNU General Public License v2
*
- * Driver for LED in ST-Ericsson AB5500 v1.0 Analog baseband Controller
- *
* Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
*/
+/*
+ * Driver for HVLED in ST-Ericsson AB5500 analog baseband controller
+ *
+ * This chip can drive upto 3 leds, of upto 40mA of led sink current.
+ * These leds can be programmed to blink between two intensities with
+ * fading delay of half, one or two seconds.
+ *
+ * Leds can be controlled via sysfs entries in
+ * "/sys/class/leds/< red | green | blue >"
+ *
+ * For each led,
+ *
+ * Modes of operation:
+ * - manual: echo 0 > fade_auto (default, no auto blinking)
+ * - auto: echo 1 > fade_auto
+ *
+ * Soft scaling delay between two intensities:
+ * - 1/2 sec: echo 1 > fade_delay
+ * - 1 sec: echo 2 > fade_delay
+ * - 2 sec: echo 3 > fade_delay
+ *
+ * Possible sequence of operation:
+ * - continuous glow: set brightness (brt)
+ * - blink between LED_OFF and LED_FULL:
+ * set fade delay -> set fade auto
+ * - blink between previous two brightness (only for LED-1):
+ * set brt1 -> set brt2 -> set fade auto
+ *
+ * Delay can be set in any step, its affect will be seen on switching mode.
+ *
+ * Note: Blink/Fade feature is supported in AB5500 v2 onwards
+ *
+ */
+
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/device.h>
@@ -18,45 +52,67 @@
#include <linux/leds-ab5500.h>
#include <linux/types.h>
+#include <mach/hardware.h>
+
#define AB5500LED_NAME "ab5500-leds"
+#define AB5500_LED_MAX 0x03
/* Register offsets */
#define AB5500_LED_REG_ENABLE 0x03
-#define AB5500_LED_FADE_CTL 0x0D
+#define AB5500_LED_FADE_CTRL 0x0D
-/* LED-0 */
+/* LED-0 Register Addr. Offsets */
#define AB5500_LED0_PWM_DUTY 0x01
#define AB5500_LED0_PWMFREQ 0x02
#define AB5500_LED0_SINKCTL 0x0A
+#define AB5500_LED0_FADE_HI 0x11
+#define AB5500_LED0_FADE_LO 0x17
-/* LED-1 */
+/* LED-1 Register Addr. Offsets */
#define AB5500_LED1_PWM_DUTY 0x05
#define AB5500_LED1_PWMFREQ 0x06
#define AB5500_LED1_SINKCTL 0x0B
+#define AB5500_LED1_FADE_HI 0x13
+#define AB5500_LED1_FADE_LO 0x19
-/* LED-2 */
+/* LED-2 Register Addr. Offsets */
#define AB5500_LED2_PWM_DUTY 0x08
#define AB5500_LED2_PWMFREQ 0x09
#define AB5500_LED2_SINKCTL 0x0C
+#define AB5500_LED2_FADE_HI 0x15
+#define AB5500_LED2_FADE_LO 0x1B
+
+/* led-0/1/2 enable bit */
+#define AB5500_LED_ENABLE_MASK 0x04
-/* pwm duty cycle */
-#define AB5500_LED_PWMDUTY_OFF 0x0
-#define AB5500_LED_PWMDUTY_MAX 0x3FF
-#define AB5500_LED_PWMDUTY_STEP (AB5500_LED_PWMDUTY_MAX/LED_FULL)
+/* led intensity */
+#define AB5500_LED_INTENSITY_OFF 0x0
+#define AB5500_LED_INTENSITY_MAX 0x3FF
+#define AB5500_LED_INTENSITY_STEP (AB5500_LED_INTENSITY_MAX/LED_FULL)
/* pwm frequency */
-#define AB5500_LED_PWMFREQ_MAX 0x0F /* 373.39 @sysclk=26MHz */
+#define AB5500_LED_PWMFREQ_MAX 0x0F /* 373.39 @sysclk=26MHz */
#define AB5500_LED_PWMFREQ_SHIFT 4
/* LED sink current control */
-#define AB5500_LED_SINKCURR_MAX 0x0F /* 40mA */
+#define AB5500_LED_SINKCURR_MAX 0x0F /* 40mA MAX */
#define AB5500_LED_SINKCURR_SHIFT 4
+/* fade Control shift and masks */
+#define AB5500_FADE_DELAY_SHIFT 0x00
+#define AB5500_FADE_MODE_MASK 0x80
+#define AB5500_FADE_DELAY_MASK 0x03
+#define AB5500_FADE_START_MASK 0x04
+#define AB5500_FADE_ON_MASK 0x70
+#define AB5500_LED_FADE_ENABLE(ledid) (0x40 >> (ledid))
+
struct ab5500_led {
u8 id;
u8 max_current;
u16 brt_val;
- enum ab5500_led_status status;
+ u16 fade_hi;
+ u16 fade_lo;
+ bool led_on;
struct led_classdev led_cdev;
struct work_struct led_work;
};
@@ -66,26 +122,41 @@ struct ab5500_hvleds {
struct device *dev;
struct ab5500_hvleds_platform_data *pdata;
struct ab5500_led leds[AB5500_HVLEDS_MAX];
+ bool hw_fade;
+ bool fade_auto;
+ enum ab5500_fade_delay fade_delay;
};
-static u8 ab5500_led_pwmduty_reg[] = {
+static u8 ab5500_led_pwmduty_reg[AB5500_LED_MAX] = {
AB5500_LED0_PWM_DUTY,
AB5500_LED1_PWM_DUTY,
AB5500_LED2_PWM_DUTY,
};
-static u8 ab5500_led_pwmfreq_reg[] = {
+static u8 ab5500_led_pwmfreq_reg[AB5500_LED_MAX] = {
AB5500_LED0_PWMFREQ,
AB5500_LED1_PWMFREQ,
AB5500_LED2_PWMFREQ,
};
-static u8 ab5500_led_sinkctl_reg[] = {
+static u8 ab5500_led_sinkctl_reg[AB5500_LED_MAX] = {
AB5500_LED0_SINKCTL,
AB5500_LED1_SINKCTL,
AB5500_LED2_SINKCTL
};
+static u8 ab5500_led_fade_hi_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_FADE_HI,
+ AB5500_LED1_FADE_HI,
+ AB5500_LED2_FADE_HI,
+};
+
+static u8 ab5500_led_fade_lo_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_FADE_LO,
+ AB5500_LED1_FADE_LO,
+ AB5500_LED2_FADE_LO,
+};
+
#define to_led(_x) container_of(_x, struct ab5500_led, _x)
static inline struct ab5500_hvleds *led_to_hvleds(struct ab5500_led *led)
@@ -93,19 +164,74 @@ static inline struct ab5500_hvleds *led_to_hvleds(struct ab5500_led *led)
return container_of(led, struct ab5500_hvleds, leds[led->id]);
}
+static int ab5500_led_enable(struct ab5500_hvleds *hvleds,
+ unsigned int led_id)
+{
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id],
+ AB5500_LED_ENABLE_MASK,
+ AB5500_LED_ENABLE_MASK);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmduty_reg[led_id], ret);
+
+ return ret;
+
+}
+
+static int ab5500_led_start_manual(struct ab5500_hvleds *hvleds)
+{
+ int ret;
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_mask_and_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_FADE_CTRL, AB5500_FADE_START_MASK,
+ AB5500_FADE_START_MASK);
+ if (ret < 0)
+ dev_err(hvleds->dev, "update reg 0x%x failed - %d\n",
+ AB5500_LED_FADE_CTRL, ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_disable(struct ab5500_hvleds *hvleds,
+ unsigned int led_id)
+{
+ int ret;
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id] - 1, 0);
+ ret |= abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id], 0);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmduty_reg[led_id], ret);
+
+ return ret;
+}
+
static int ab5500_led_pwmduty_write(struct ab5500_hvleds *hvleds,
unsigned int led_id, u16 val)
{
int ret;
- int val_lsb = val & 0xFF;
- int val_msb = (val & 0x300) >> 8;
+ u8 val_lsb = val & 0xFF;
+ u8 val_msb = (val & 0x300) >> 8;
mutex_lock(&hvleds->lock);
dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val = %d\n"
- "reg[%d] w val = %d\n",
- ab5500_led_pwmduty_reg[led_id] - 1, val_lsb,
- ab5500_led_pwmduty_reg[led_id], val_msb);
+ "reg[%d] w val = %d\n",
+ ab5500_led_pwmduty_reg[led_id] - 1, val_lsb,
+ ab5500_led_pwmduty_reg[led_id], val_msb);
ret = abx500_set_register_interruptible(
hvleds->dev, AB5500_BANK_LED,
@@ -116,6 +242,7 @@ static int ab5500_led_pwmduty_write(struct ab5500_hvleds *hvleds,
if (ret < 0)
dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
ab5500_led_pwmduty_reg[led_id], ret);
+
mutex_unlock(&hvleds->lock);
return ret;
@@ -139,6 +266,7 @@ static int ab5500_led_pwmfreq_write(struct ab5500_hvleds *hvleds,
if (ret < 0)
dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
ab5500_led_pwmfreq_reg[led_id], ret);
+
mutex_unlock(&hvleds->lock);
return ret;
@@ -149,19 +277,58 @@ static int ab5500_led_sinkctl_write(struct ab5500_hvleds *hvleds,
{
int ret;
- val = (val & 0x0F) << AB5500_LED_SINKCURR_SHIFT;
+ if (val > AB5500_LED_SINKCURR_MAX)
+ val = AB5500_LED_SINKCURR_MAX;
- mutex_lock(&hvleds->lock);
+ val = (val << AB5500_LED_SINKCURR_SHIFT);
dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val=%d\n",
ab5500_led_sinkctl_reg[led_id], val);
+ mutex_lock(&hvleds->lock);
+
ret = abx500_set_register_interruptible(
hvleds->dev, AB5500_BANK_LED,
ab5500_led_sinkctl_reg[led_id], val);
if (ret < 0)
dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
ab5500_led_sinkctl_reg[led_id], ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_fade_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, bool on, u16 val)
+{
+ int ret;
+ int val_lsb = val & 0xFF;
+ int val_msb = (val & 0x300) >> 8;
+ u8 *fade_reg;
+
+ if (on)
+ fade_reg = ab5500_led_fade_hi_reg;
+ else
+ fade_reg = ab5500_led_fade_lo_reg;
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val = %d\n"
+ "reg[%d] w val = %d\n",
+ fade_reg[led_id] - 1, val_lsb,
+ fade_reg[led_id], val_msb);
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ fade_reg[led_id] - 1, val_lsb);
+ ret |= abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ fade_reg[led_id], val_msb);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ fade_reg[led_id], ret);
+
mutex_unlock(&hvleds->lock);
return ret;
@@ -174,6 +341,7 @@ static int ab5500_led_sinkctl_read(struct ab5500_hvleds *hvleds,
u8 val;
mutex_lock(&hvleds->lock);
+
ret = abx500_get_register_interruptible(
hvleds->dev, AB5500_BANK_LED,
ab5500_led_sinkctl_reg[led_id], &val);
@@ -183,7 +351,9 @@ static int ab5500_led_sinkctl_read(struct ab5500_hvleds *hvleds,
mutex_unlock(&hvleds->lock);
return ret;
}
+
val = (val & 0xF0) >> AB5500_LED_SINKCURR_SHIFT;
+
mutex_unlock(&hvleds->lock);
return val;
@@ -196,7 +366,8 @@ static void ab5500_led_brightness_set(struct led_classdev *led_cdev,
/* adjust LED_FULL to 10bit range */
brt_val &= LED_FULL;
- led->brt_val = brt_val * AB5500_LED_PWMDUTY_STEP;
+ led->brt_val = brt_val * AB5500_LED_INTENSITY_STEP;
+
schedule_work(&led->led_work);
}
@@ -205,8 +376,13 @@ static void ab5500_led_work(struct work_struct *led_work)
struct ab5500_led *led = to_led(led_work);
struct ab5500_hvleds *hvleds = led_to_hvleds(led);
- if (led->status == AB5500_LED_ON)
+ if (led->led_on == true) {
ab5500_led_pwmduty_write(hvleds, led->id, led->brt_val);
+ if (hvleds->hw_fade && led->brt_val) {
+ ab5500_led_enable(hvleds, led->id);
+ ab5500_led_start_manual(hvleds);
+ }
+ }
}
static ssize_t ab5500_led_show_current(struct device *dev,
@@ -248,9 +424,122 @@ static ssize_t ab5500_led_store_current(struct device *dev,
return len;
}
+static ssize_t ab5500_led_store_fade_auto(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ int ret;
+ u8 fade_ctrl = 0;
+ unsigned long fade_auto;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (strict_strtoul(buf, 0, &fade_auto))
+ return -EINVAL;
+
+ if (fade_auto > 1) {
+ dev_err(hvleds->dev, "invalid mode\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_get_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_FADE_CTRL, &fade_ctrl);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_FADE_CTRL, ret);
+ goto unlock_and_return;
+ }
+
+ /* manual mode */
+ if (fade_auto == false) {
+ fade_ctrl &= ~(AB5500_LED_FADE_ENABLE(led->id));
+ if (!(fade_ctrl & AB5500_FADE_ON_MASK))
+ fade_ctrl = 0;
+
+ ret = ab5500_led_disable(hvleds, led->id);
+ if (ret < 0)
+ goto unlock_and_return;
+ } else {
+ /* set led auto enable bit */
+ fade_ctrl |= AB5500_FADE_MODE_MASK;
+ fade_ctrl |= AB5500_LED_FADE_ENABLE(led->id);
+
+ /* set fade delay */
+ fade_ctrl &= ~AB5500_FADE_DELAY_MASK;
+ fade_ctrl |= hvleds->fade_delay << AB5500_FADE_DELAY_SHIFT;
+
+ /* set fade start manual */
+ fade_ctrl |= AB5500_FADE_START_MASK;
+
+ /* enble corresponding led */
+ ret = ab5500_led_enable(hvleds, led->id);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ }
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_FADE_CTRL, fade_ctrl);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_FADE_CTRL, ret);
+ goto unlock_and_return;
+ }
+
+ hvleds->fade_auto = fade_auto;
+
+ ret = len;
+
+unlock_and_return:
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static ssize_t ab5500_led_show_fade_auto(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ return sprintf(buf, "%d\n", hvleds->fade_auto);
+}
+
+static ssize_t ab5500_led_store_fade_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ unsigned long fade_delay;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (strict_strtoul(buf, 0, &fade_delay))
+ return -EINVAL;
+
+ if (fade_delay > AB5500_FADE_DELAY_TWOSEC) {
+ dev_err(hvleds->dev, "invalid mode\n");
+ return -EINVAL;
+ }
+
+ hvleds->fade_delay = fade_delay;
+
+ return len;
+}
+
/* led class device attributes */
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO,
ab5500_led_show_current, ab5500_led_store_current);
+static DEVICE_ATTR(fade_auto, S_IRUGO | S_IWUGO,
+ ab5500_led_show_fade_auto, ab5500_led_store_fade_auto);
+static DEVICE_ATTR(fade_delay, S_IRUGO | S_IWUGO,
+ NULL, ab5500_led_store_fade_delay);
static int ab5500_led_init_registers(struct ab5500_hvleds *hvleds)
{
@@ -258,30 +547,52 @@ static int ab5500_led_init_registers(struct ab5500_hvleds *hvleds)
unsigned int led_id;
/* fade - manual : dur mid : pwm duty mid */
- ret = abx500_set_register_interruptible(
+ if (!hvleds->hw_fade) {
+ ret = abx500_set_register_interruptible(
hvleds->dev, AB5500_BANK_LED,
AB5500_LED_REG_ENABLE, true);
- if (ret < 0) {
- dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
- AB5500_LED_REG_ENABLE, ret);
- return ret;
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_REG_ENABLE, ret);
+ return ret;
+ }
}
for (led_id = 0; led_id < AB5500_HVLEDS_MAX; led_id++) {
- /* Set pwm freq. and sink current to mid values */
- ret = ab5500_led_pwmfreq_write(
- hvleds, led_id, AB5500_LED_PWMFREQ_MAX);
- if (ret < 0)
- return ret;
+ if (hvleds->leds[led_id].led_on == false)
+ continue;
ret = ab5500_led_sinkctl_write(
- hvleds, led_id, AB5500_LED_SINKCURR_MAX);
+ hvleds, led_id,
+ hvleds->leds[led_id].max_current);
if (ret < 0)
return ret;
+ if (hvleds->hw_fade) {
+ ret = ab5500_led_pwmfreq_write(
+ hvleds, led_id,
+ AB5500_LED_PWMFREQ_MAX / 2);
+ if (ret < 0)
+ return ret;
+
+ /* fade high intensity */
+ ret = ab5500_led_fade_write(
+ hvleds, led_id, true,
+ hvleds->leds[led_id].fade_hi);
+ if (ret < 0)
+ return ret;
+
+ /* fade low intensity */
+ ret = ab5500_led_fade_write(
+ hvleds, led_id, false,
+ hvleds->leds[led_id].fade_lo);
+ if (ret < 0)
+ return ret;
+ }
+
/* init led off */
- ret = ab5500_led_pwmduty_write(
- hvleds, led_id, AB5500_LED_PWMDUTY_OFF);
+ ret |= ab5500_led_pwmduty_write(
+ hvleds, led_id, AB5500_LED_INTENSITY_OFF);
if (ret < 0)
return ret;
}
@@ -294,12 +605,18 @@ static int ab5500_led_register_leds(struct device *dev,
struct ab5500_hvleds *hvleds)
{
int i_led;
- int err;
+ int ret = 0;
struct ab5500_led_conf *pled;
struct ab5500_led *led;
hvleds->dev = dev;
hvleds->pdata = pdata;
+
+ if (abx500_get_chip_id(dev) == AB5500_2_0)
+ hvleds->hw_fade = true;
+ else
+ hvleds->hw_fade = false;
+
for (i_led = 0; i_led < AB5500_HVLEDS_MAX; i_led++) {
pled = &pdata->leds[i_led];
led = &hvleds->leds[i_led];
@@ -308,84 +625,137 @@ static int ab5500_led_register_leds(struct device *dev,
led->id = pled->led_id;
led->max_current = pled->max_current;
- led->status = pled->status;
+ led->led_on = pled->led_on;
led->led_cdev.name = pled->name;
led->led_cdev.brightness_set = ab5500_led_brightness_set;
- err = led_classdev_register(dev, &led->led_cdev);
- if (err < 0) {
- dev_err(dev, "Register led class failed: %d\n", err);
+ /* Provide interface only for enabled LEDs */
+ if (led->led_on == false)
+ continue;
+
+ if (hvleds->hw_fade) {
+ led->fade_hi = (pled->fade_hi & LED_FULL);
+ led->fade_hi *= AB5500_LED_INTENSITY_STEP;
+ led->fade_lo = (pled->fade_lo & LED_FULL);
+ led->fade_lo *= AB5500_LED_INTENSITY_STEP;
+ }
+
+ ret = led_classdev_register(dev, &led->led_cdev);
+ if (ret < 0) {
+ dev_err(dev, "Register led class failed: %d\n", ret);
goto bailout1;
}
- err = device_create_file(led->led_cdev.dev,
+ ret = device_create_file(led->led_cdev.dev,
&dev_attr_led_current);
- if (err < 0) {
- dev_err(dev, "sysfs device creation failed: %d\n", err);
+ if (ret < 0) {
+ dev_err(dev, "sysfs device creation failed: %d\n", ret);
goto bailout2;
}
+
+ if (hvleds->hw_fade) {
+ ret = device_create_file(led->led_cdev.dev,
+ &dev_attr_fade_auto);
+ if (ret < 0) {
+ dev_err(dev, "sysfs device "
+ "creation failed: %d\n", ret);
+ goto bailout3;
+ }
+
+ ret = device_create_file(led->led_cdev.dev,
+ &dev_attr_fade_delay);
+ if (ret < 0) {
+ dev_err(dev, "sysfs device "
+ "creation failed: %d\n", ret);
+ goto bailout4;
+ }
+ }
}
- return err;
+ return ret;
for (; i_led >= 0; i_led--) {
- device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ if (hvleds->leds[i_led].led_on == false)
+ continue;
+
+ if (hvleds->hw_fade) {
+ device_remove_file(hvleds->leds[i_led].led_cdev.dev,
+ &dev_attr_fade_delay);
+bailout4:
+ device_remove_file(hvleds->leds[i_led].led_cdev.dev,
+ &dev_attr_fade_auto);
+ }
+bailout3:
+ device_remove_file(hvleds->leds[i_led].led_cdev.dev,
+ &dev_attr_led_current);
bailout2:
led_classdev_unregister(&hvleds->leds[i_led].led_cdev);
bailout1:
cancel_work_sync(&hvleds->leds[i_led].led_work);
}
- return err;
+ return ret;
}
static int __devinit ab5500_hvleds_probe(struct platform_device *pdev)
{
struct ab5500_hvleds_platform_data *pdata = pdev->dev.platform_data;
struct ab5500_hvleds *hvleds = NULL;
- int err = 0, i;
+ int ret = 0, i;
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data required\n");
- err = -ENODEV;
+ ret = -ENODEV;
goto err_out;
}
hvleds = kzalloc(sizeof(struct ab5500_hvleds), GFP_KERNEL);
if (hvleds == NULL) {
- err = -ENOMEM;
+ ret = -ENOMEM;
goto err_out;
}
mutex_init(&hvleds->lock);
/* init leds data and register led_classdev */
- err = ab5500_led_register_leds(&pdev->dev, pdata, hvleds);
- if (err < 0) {
- dev_err(&pdev->dev, "leds registeration failed\n");
+ ret = ab5500_led_register_leds(&pdev->dev, pdata, hvleds);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "leds registration failed\n");
goto err_out;
}
/* init device registers and set initial led current */
- err = ab5500_led_init_registers(hvleds);
- if (err < 0) {
- dev_err(&pdev->dev, "reg init failed: %d\n", err);
+ ret = ab5500_led_init_registers(hvleds);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "reg init failed: %d\n", ret);
goto err_reg_init;
}
- dev_info(&pdev->dev, "enabled\n");
+ if (hvleds->hw_fade)
+ dev_info(&pdev->dev, "v2 enabled\n");
+ else
+ dev_info(&pdev->dev, "v1 enabled\n");
- return err;
+ return ret;
err_reg_init:
for (i = 0; i < AB5500_HVLEDS_MAX; i++) {
struct ab5500_led *led = &hvleds->leds[i];
- led_classdev_unregister(&led->led_cdev);
+ if (led->led_on == false)
+ continue;
+
device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ if (hvleds->hw_fade) {
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_auto);
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_delay);
+ }
+ led_classdev_unregister(&led->led_cdev);
cancel_work_sync(&led->led_work);
}
err_out:
kfree(hvleds);
- return err;
+ return ret;
}
static int __devexit ab5500_hvleds_remove(struct platform_device *pdev)
@@ -396,8 +766,17 @@ static int __devexit ab5500_hvleds_remove(struct platform_device *pdev)
for (i = 0; i < AB5500_HVLEDS_MAX; i++) {
struct ab5500_led *led = &hvleds->leds[i];
- led_classdev_unregister(&led->led_cdev);
+ if (led->led_on == false)
+ continue;
+
device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ if (hvleds->hw_fade) {
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_auto);
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_delay);
+ }
+ led_classdev_unregister(&led->led_cdev);
cancel_work_sync(&led->led_work);
}
kfree(hvleds);
diff --git a/include/linux/leds-ab5500.h b/include/linux/leds-ab5500.h
index 2db6ffd188e..9ba9ac61d90 100644
--- a/include/linux/leds-ab5500.h
+++ b/include/linux/leds-ab5500.h
@@ -13,19 +13,23 @@
#define AB5500_HVLED2 2
#define AB5500_HVLEDS_MAX 3
-enum ab5500_led_status {
- AB5500_LED_OFF = 0x00,
- AB5500_LED_ON,
+enum ab5500_fade_delay {
+ AB5500_FADE_DELAY_BYPASS = 0,
+ AB5500_FADE_DELAY_HALFSEC,
+ AB5500_FADE_DELAY_ONESEC,
+ AB5500_FADE_DELAY_TWOSEC
};
struct ab5500_led_conf {
char *name;
u8 led_id;
- enum ab5500_led_status status;
u8 max_current;
+ u8 fade_hi;
+ u8 fade_lo;
+ bool led_on;
};
struct ab5500_hvleds_platform_data {
- bool hw_blink;
+ bool hw_fade;
struct ab5500_led_conf leds[AB5500_HVLEDS_MAX];
};