diff options
author | Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com> | 2011-07-20 20:58:00 +0530 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:01:02 +0200 |
commit | 2ff7e0ff79b553e7077e456cb9c9e7ec48b887a6 (patch) | |
tree | cfbfa7b6ae70a2e7f699008eb318841321eac333 /drivers | |
parent | 9bac6a0efe0993a13822a3cf84371d6ff92e1abb (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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/leds/leds-ab5500.c | 505 |
1 files changed, 442 insertions, 63 deletions
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); |