aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrent Piepho <xyzzy@speakeasy.org>2009-05-12 18:33:12 -0400
committerRichard Purdie <rpurdie@linux.intel.com>2009-06-23 15:21:39 -0400
commited88bae6918fa990cbfe47316bd0f790121aaf00 (patch)
treeeba5d14d9db0f7361f9684170f9dd6e43bf54646
parent5054d39e327f76df022163a2ebd02e444c5d65f9 (diff)
leds: Add options to have GPIO LEDs start on or keep their state
There already is a "default-on" trigger but there are problems with it. For one, it's a inefficient way to do it and requires led trigger support to be compiled in. But the real reason is that is produces a glitch on the LED. The GPIO is allocate with the LED *off*, then *later* when the trigger runs it is turned back on. If the LED was already on via the GPIO's reset default or action of the firmware, this produces a glitch where the LED goes from on to off to on. While normally this is fast enough that it wouldn't be noticeable to a human observer, there are still serious problems. One is that there may be something else on the GPIO line, like a hardware alarm or watchdog, that is fast enough to notice the glitch. Another is that the kernel may panic before the LED is turned back on, thus hanging with the LED in the wrong state. This is not just speculation, but actually happened to me with an embedded system that has an LED which should turn off when the kernel finishes booting, which was left in the incorrect state due to a bug in the OF LED binding code. We also let GPIO LEDs get their initial value from whatever the current state of the GPIO line is. On some systems the LEDs are put into some state by the firmware or hardware before Linux boots, and it is desired to have them keep this state which is otherwise unknown to Linux. This requires that the underlying GPIO driver support reading the value of output GPIOs. Some drivers support this and some do not. The platform device binding gains a field in the platform data "default_state" that controls this. There are three constants defined to select from on, off, or keeping the current state. The OpenFirmware binding uses a property named "default-state" that can be set to "on", "off", or "keep". The default if the property isn't present is off. Signed-off-by: Trent Piepho <xyzzy@speakeasy.org> Acked-by: Grant Likely <grant.likely@secretlab.ca> Acked-by: Wolfram Sang <w.sang@pengutronix.de> Acked-by: Sean MacLennan <smaclennan@pikatech.com> Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
-rw-r--r--Documentation/powerpc/dts-bindings/gpio/led.txt17
-rw-r--r--drivers/leds/leds-gpio.c20
-rw-r--r--include/linux/leds.h9
3 files changed, 40 insertions, 6 deletions
diff --git a/Documentation/powerpc/dts-bindings/gpio/led.txt b/Documentation/powerpc/dts-bindings/gpio/led.txt
index 4fe14deedc0a..064db928c3c1 100644
--- a/Documentation/powerpc/dts-bindings/gpio/led.txt
+++ b/Documentation/powerpc/dts-bindings/gpio/led.txt
@@ -16,10 +16,17 @@ LED sub-node properties:
16 string defining the trigger assigned to the LED. Current triggers are: 16 string defining the trigger assigned to the LED. Current triggers are:
17 "backlight" - LED will act as a back-light, controlled by the framebuffer 17 "backlight" - LED will act as a back-light, controlled by the framebuffer
18 system 18 system
19 "default-on" - LED will turn on 19 "default-on" - LED will turn on, but see "default-state" below
20 "heartbeat" - LED "double" flashes at a load average based rate 20 "heartbeat" - LED "double" flashes at a load average based rate
21 "ide-disk" - LED indicates disk activity 21 "ide-disk" - LED indicates disk activity
22 "timer" - LED flashes at a fixed, configurable rate 22 "timer" - LED flashes at a fixed, configurable rate
23- default-state: (optional) The initial state of the LED. Valid
24 values are "on", "off", and "keep". If the LED is already on or off
25 and the default-state property is set the to same value, then no
26 glitch should be produced where the LED momentarily turns off (or
27 on). The "keep" setting will keep the LED at whatever its current
28 state is, without producing a glitch. The default is off if this
29 property is not present.
23 30
24Examples: 31Examples:
25 32
@@ -30,14 +37,22 @@ leds {
30 gpios = <&mcu_pio 0 1>; /* Active low */ 37 gpios = <&mcu_pio 0 1>; /* Active low */
31 linux,default-trigger = "ide-disk"; 38 linux,default-trigger = "ide-disk";
32 }; 39 };
40
41 fault {
42 gpios = <&mcu_pio 1 0>;
43 /* Keep LED on if BIOS detected hardware fault */
44 default-state = "keep";
45 };
33}; 46};
34 47
35run-control { 48run-control {
36 compatible = "gpio-leds"; 49 compatible = "gpio-leds";
37 red { 50 red {
38 gpios = <&mpc8572 6 0>; 51 gpios = <&mpc8572 6 0>;
52 default-state = "off";
39 }; 53 };
40 green { 54 green {
41 gpios = <&mpc8572 7 0>; 55 gpios = <&mpc8572 7 0>;
56 default-state = "on";
42 }; 57 };
43} 58}
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 76895e691042..6b06638eb5b4 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -76,7 +76,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
76 struct gpio_led_data *led_dat, struct device *parent, 76 struct gpio_led_data *led_dat, struct device *parent,
77 int (*blink_set)(unsigned, unsigned long *, unsigned long *)) 77 int (*blink_set)(unsigned, unsigned long *, unsigned long *))
78{ 78{
79 int ret; 79 int ret, state;
80 80
81 /* skip leds that aren't available */ 81 /* skip leds that aren't available */
82 if (!gpio_is_valid(template->gpio)) { 82 if (!gpio_is_valid(template->gpio)) {
@@ -99,11 +99,15 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
99 led_dat->cdev.blink_set = gpio_blink_set; 99 led_dat->cdev.blink_set = gpio_blink_set;
100 } 100 }
101 led_dat->cdev.brightness_set = gpio_led_set; 101 led_dat->cdev.brightness_set = gpio_led_set;
102 led_dat->cdev.brightness = LED_OFF; 102 if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
103 state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
104 else
105 state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
106 led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
103 if (!template->retain_state_suspended) 107 if (!template->retain_state_suspended)
104 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 108 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
105 109
106 ret = gpio_direction_output(led_dat->gpio, led_dat->active_low); 110 ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
107 if (ret < 0) 111 if (ret < 0)
108 goto err; 112 goto err;
109 113
@@ -223,12 +227,22 @@ static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
223 memset(&led, 0, sizeof(led)); 227 memset(&led, 0, sizeof(led));
224 for_each_child_of_node(np, child) { 228 for_each_child_of_node(np, child) {
225 enum of_gpio_flags flags; 229 enum of_gpio_flags flags;
230 const char *state;
226 231
227 led.gpio = of_get_gpio_flags(child, 0, &flags); 232 led.gpio = of_get_gpio_flags(child, 0, &flags);
228 led.active_low = flags & OF_GPIO_ACTIVE_LOW; 233 led.active_low = flags & OF_GPIO_ACTIVE_LOW;
229 led.name = of_get_property(child, "label", NULL) ? : child->name; 234 led.name = of_get_property(child, "label", NULL) ? : child->name;
230 led.default_trigger = 235 led.default_trigger =
231 of_get_property(child, "linux,default-trigger", NULL); 236 of_get_property(child, "linux,default-trigger", NULL);
237 state = of_get_property(child, "default-state", NULL);
238 if (state) {
239 if (!strcmp(state, "keep"))
240 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
241 else if(!strcmp(state, "on"))
242 led.default_state = LEDS_GPIO_DEFSTATE_ON;
243 else
244 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
245 }
232 246
233 ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++], 247 ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++],
234 &ofdev->dev, NULL); 248 &ofdev->dev, NULL);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index c7f0b148df06..62af62915cf7 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -143,9 +143,14 @@ struct gpio_led {
143 const char *name; 143 const char *name;
144 const char *default_trigger; 144 const char *default_trigger;
145 unsigned gpio; 145 unsigned gpio;
146 u8 active_low : 1; 146 unsigned active_low : 1;
147 u8 retain_state_suspended : 1; 147 unsigned retain_state_suspended : 1;
148 unsigned default_state : 2;
149 /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
148}; 150};
151#define LEDS_GPIO_DEFSTATE_OFF 0
152#define LEDS_GPIO_DEFSTATE_ON 1
153#define LEDS_GPIO_DEFSTATE_KEEP 2
149 154
150struct gpio_led_platform_data { 155struct gpio_led_platform_data {
151 int num_leds; 156 int num_leds;