From: David Brownell Date: Thu, 10 May 2007 09:51:41 +0000 (+0100) Subject: leds: Teach leds-gpio to handle timer-unsafe GPIOs X-Git-Tag: v2.6.23-rc1~9^2~5 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=00852279af5ad26956bc7f4d0e86fdb40192e542;p=linux-2.6 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 Signed-off-by: Richard Purdie --- diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 431dcb6190..47d90db280 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -13,14 +13,26 @@ #include #include #include +#include + #include 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;