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>

authored by David Brownell and committed by Richard Purdie 00852279 22e03f3b

+26 -1
+26 -1
drivers/leds/leds-gpio.c
··· 13 13 #include <linux/init.h> 14 14 #include <linux/platform_device.h> 15 15 #include <linux/leds.h> 16 + #include <linux/workqueue.h> 17 + 16 18 #include <asm/gpio.h> 17 19 18 20 struct gpio_led_data { 19 21 struct led_classdev cdev; 20 22 unsigned gpio; 23 + struct work_struct work; 24 + u8 new_level; 25 + u8 can_sleep; 21 26 u8 active_low; 22 27 }; 23 28 29 + static void gpio_led_work(struct work_struct *work) 30 + { 31 + struct gpio_led_data *led_dat = 32 + container_of(work, struct gpio_led_data, work); 33 + 34 + gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); 35 + } 24 36 25 37 static void gpio_led_set(struct led_classdev *led_cdev, 26 38 enum led_brightness value) ··· 49 37 if (led_dat->active_low) 50 38 level = !level; 51 39 52 - gpio_set_value(led_dat->gpio, level); 40 + /* setting GPIOs with I2C/etc requires a preemptible task context */ 41 + if (led_dat->can_sleep) { 42 + if (preempt_count()) { 43 + led_dat->new_level = level; 44 + schedule_work(&led_dat->work); 45 + } else 46 + gpio_set_value_cansleep(led_dat->gpio, level); 47 + } else 48 + gpio_set_value(led_dat->gpio, level); 53 49 } 54 50 55 51 static int __init gpio_led_probe(struct platform_device *pdev) ··· 82 62 led_dat->cdev.name = cur_led->name; 83 63 led_dat->cdev.default_trigger = cur_led->default_trigger; 84 64 led_dat->gpio = cur_led->gpio; 65 + led_dat->can_sleep = gpio_cansleep(cur_led->gpio); 85 66 led_dat->active_low = cur_led->active_low; 86 67 led_dat->cdev.brightness_set = gpio_led_set; 87 68 led_dat->cdev.brightness = cur_led->active_low ? LED_FULL : LED_OFF; ··· 98 77 gpio_free(led_dat->gpio); 99 78 goto err; 100 79 } 80 + 81 + INIT_WORK(&led_dat->work, gpio_led_work); 101 82 } 102 83 103 84 platform_set_drvdata(pdev, leds_data); ··· 113 90 gpio_free(leds_data[i].gpio); 114 91 } 115 92 } 93 + 94 + flush_scheduled_work(); 116 95 kfree(leds_data); 117 96 118 97 return ret;