diff options
author | David Brownell <david-b@pacbell.net> | 2007-05-10 10:51:41 +0100 |
---|---|---|
committer | Richard Purdie <rpurdie@rpsys.net> | 2007-07-16 01:15:50 +0100 |
commit | 00852279af5ad26956bc7f4d0e86fdb40192e542 (patch) | |
tree | 8d8ef74de734d94c59ae2d83f19b446d4ef2ef73 | |
parent | 22e03f3b58dfcca30f0c8de185022132459638d1 (diff) |
leds: Teach leds-gpio to handle timer-unsafe GPIOs
Teach the new leds-gpio driver that some GPIOs can't be accessed from
timer callbacks ... which is how all today's standard triggers use them.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
-rw-r--r-- | drivers/leds/leds-gpio.c | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 431dcb61902..47d90db280c 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -13,14 +13,26 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> +#include <linux/workqueue.h> + #include <asm/gpio.h> struct gpio_led_data { struct led_classdev cdev; unsigned gpio; + struct work_struct work; + u8 new_level; + u8 can_sleep; u8 active_low; }; +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); +} static void gpio_led_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -37,7 +49,15 @@ static void gpio_led_set(struct led_classdev *led_cdev, if (led_dat->active_low) level = !level; - gpio_set_value(led_dat->gpio, level); + /* setting GPIOs with I2C/etc requires a preemptible task context */ + if (led_dat->can_sleep) { + if (preempt_count()) { + led_dat->new_level = level; + schedule_work(&led_dat->work); + } else + gpio_set_value_cansleep(led_dat->gpio, level); + } else + gpio_set_value(led_dat->gpio, level); } static int __init gpio_led_probe(struct platform_device *pdev) @@ -62,6 +82,7 @@ static int __init gpio_led_probe(struct platform_device *pdev) led_dat->cdev.name = cur_led->name; led_dat->cdev.default_trigger = cur_led->default_trigger; led_dat->gpio = cur_led->gpio; + led_dat->can_sleep = gpio_cansleep(cur_led->gpio); led_dat->active_low = cur_led->active_low; led_dat->cdev.brightness_set = gpio_led_set; led_dat->cdev.brightness = cur_led->active_low ? LED_FULL : LED_OFF; @@ -77,6 +98,8 @@ static int __init gpio_led_probe(struct platform_device *pdev) gpio_free(led_dat->gpio); goto err; } + + INIT_WORK(&led_dat->work, gpio_led_work); } platform_set_drvdata(pdev, leds_data); @@ -90,6 +113,8 @@ err: gpio_free(leds_data[i].gpio); } } + + flush_scheduled_work(); kfree(leds_data); return ret; |