Commit 2146325d authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Richard Purdie

leds: leds-gpio: Change blink_set callback to be able to turn off blinking

The leds-gpio blink_set() callback follows the same prototype as the
main leds subsystem blink_set() one.

The problem is that to stop blink, normally, a leds driver does it
in the brightness_set() callback when asked to set a new fixed value.

However, with leds-gpio, the platform has no hook to do so, as this
later callback results in a standard GPIO manipulation.

This changes the leds-gpio specific callback to take a new argument
that indicates whether the LED should be blinking or not and in what
state it should be set if not. We also update the dns323 platform
which seems to be the only user of this so far.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarRichard Purdie <rpurdie@linux.intel.com>
parent 14e40f64
...@@ -240,22 +240,23 @@ static int __init dns323_read_mac_addr(void) ...@@ -240,22 +240,23 @@ static int __init dns323_read_mac_addr(void)
#define ORION_BLINK_HALF_PERIOD 100 /* ms */ #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) 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; *delay_on = *delay_off = ORION_BLINK_HALF_PERIOD;
if (ORION_BLINK_HALF_PERIOD == *delay_on switch(state) {
&& ORION_BLINK_HALF_PERIOD == *delay_off) { case GPIO_LED_NO_BLINK_LOW:
value = !value; case GPIO_LED_NO_BLINK_HIGH:
orion_gpio_set_blink(gpio, value); orion_gpio_set_blink(gpio, 0);
return 0; gpio_set_value(gpio, state);
break;
case GPIO_LED_BLINK:
orion_gpio_set_blink(gpio, 1);
} }
return 0;
return -EINVAL;
} }
static struct gpio_led dns323_leds[] = { static struct gpio_led dns323_leds[] = {
...@@ -263,6 +264,7 @@ static struct gpio_led dns323_leds[] = { ...@@ -263,6 +264,7 @@ static struct gpio_led dns323_leds[] = {
.name = "power:blue", .name = "power:blue",
.gpio = DNS323_GPIO_LED_POWER2, .gpio = DNS323_GPIO_LED_POWER2,
.default_trigger = "timer", .default_trigger = "timer",
.active_low = 1,
}, { }, {
.name = "right:amber", .name = "right:amber",
.gpio = DNS323_GPIO_LED_RIGHT_AMBER, .gpio = DNS323_GPIO_LED_RIGHT_AMBER,
......
...@@ -26,7 +26,8 @@ struct gpio_led_data { ...@@ -26,7 +26,8 @@ struct gpio_led_data {
u8 new_level; u8 new_level;
u8 can_sleep; u8 can_sleep;
u8 active_low; 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); unsigned long *delay_on, unsigned long *delay_off);
}; };
...@@ -35,7 +36,13 @@ static void gpio_led_work(struct work_struct *work) ...@@ -35,7 +36,13 @@ static void gpio_led_work(struct work_struct *work)
struct gpio_led_data *led_dat = struct gpio_led_data *led_dat =
container_of(work, struct gpio_led_data, work); 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, static void gpio_led_set(struct led_classdev *led_cdev,
...@@ -60,8 +67,14 @@ 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) { if (led_dat->can_sleep) {
led_dat->new_level = level; led_dat->new_level = level;
schedule_work(&led_dat->work); schedule_work(&led_dat->work);
} else } else {
gpio_set_value(led_dat->gpio, level); 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, static int gpio_blink_set(struct led_classdev *led_cdev,
...@@ -70,12 +83,14 @@ 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 = struct gpio_led_data *led_dat =
container_of(led_cdev, struct gpio_led_data, cdev); 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, static int __devinit create_gpio_led(const struct gpio_led *template,
struct gpio_led_data *led_dat, struct device *parent, 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; int ret, state;
...@@ -97,6 +112,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template, ...@@ -97,6 +112,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
led_dat->gpio = template->gpio; led_dat->gpio = template->gpio;
led_dat->can_sleep = gpio_cansleep(template->gpio); led_dat->can_sleep = gpio_cansleep(template->gpio);
led_dat->active_low = template->active_low; led_dat->active_low = template->active_low;
led_dat->blinking = 0;
if (blink_set) { if (blink_set) {
led_dat->platform_gpio_blink_set = blink_set; led_dat->platform_gpio_blink_set = blink_set;
led_dat->cdev.blink_set = gpio_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, ...@@ -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); ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
if (ret < 0) if (ret < 0)
goto err; goto err;
INIT_WORK(&led_dat->work, gpio_led_work); INIT_WORK(&led_dat->work, gpio_led_work);
ret = led_classdev_register(parent, &led_dat->cdev); ret = led_classdev_register(parent, &led_dat->cdev);
...@@ -234,6 +250,7 @@ static int __devinit of_gpio_leds_probe(struct of_device *ofdev, ...@@ -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.gpio = of_get_gpio_flags(child, 0, &flags);
led.active_low = flags & OF_GPIO_ACTIVE_LOW; led.active_low = flags & OF_GPIO_ACTIVE_LOW;
led.name = of_get_property(child, "label", NULL) ? : child->name; led.name = of_get_property(child, "label", NULL) ? : child->name;
led.blinking = 0;
led.default_trigger = led.default_trigger =
of_get_property(child, "linux,default-trigger", NULL); of_get_property(child, "linux,default-trigger", NULL);
state = of_get_property(child, "default-state", NULL); state = of_get_property(child, "default-state", NULL);
......
...@@ -149,14 +149,18 @@ struct gpio_led { ...@@ -149,14 +149,18 @@ struct gpio_led {
unsigned default_state : 2; unsigned default_state : 2;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
}; };
#define LEDS_GPIO_DEFSTATE_OFF 0 #define LEDS_GPIO_DEFSTATE_OFF 0
#define LEDS_GPIO_DEFSTATE_ON 1 #define LEDS_GPIO_DEFSTATE_ON 1
#define LEDS_GPIO_DEFSTATE_KEEP 2 #define LEDS_GPIO_DEFSTATE_KEEP 2
struct gpio_led_platform_data { struct gpio_led_platform_data {
int num_leds; int num_leds;
struct gpio_led *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_on,
unsigned long *delay_off); unsigned long *delay_off);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment