gfs2: Rename "gfs_recovery" workqueue to "gfs2_recovery"
[linux-2.6-microblaze.git] / drivers / leds / led-core.c
index 4a97cb7..b9b1295 100644 (file)
@@ -114,21 +114,14 @@ static void led_timer_function(struct timer_list *t)
        mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
 }
 
-static void set_brightness_delayed(struct work_struct *ws)
+static void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev,
+                                                 unsigned int value)
 {
-       struct led_classdev *led_cdev =
-               container_of(ws, struct led_classdev, set_brightness_work);
        int ret = 0;
 
-       if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
-               led_cdev->delayed_set_value = LED_OFF;
-               led_stop_software_blink(led_cdev);
-       }
-
-       ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
+       ret = __led_set_brightness(led_cdev, value);
        if (ret == -ENOTSUPP)
-               ret = __led_set_brightness_blocking(led_cdev,
-                                       led_cdev->delayed_set_value);
+               ret = __led_set_brightness_blocking(led_cdev, value);
        if (ret < 0 &&
            /* LED HW might have been unplugged, therefore don't warn */
            !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
@@ -137,6 +130,37 @@ static void set_brightness_delayed(struct work_struct *ws)
                        "Setting an LED's brightness failed (%d)\n", ret);
 }
 
+static void set_brightness_delayed(struct work_struct *ws)
+{
+       struct led_classdev *led_cdev =
+               container_of(ws, struct led_classdev, set_brightness_work);
+
+       if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
+               led_stop_software_blink(led_cdev);
+               set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
+       }
+
+       /*
+        * Triggers may call led_set_brightness(LED_OFF),
+        * led_set_brightness(LED_FULL) in quick succession to disable blinking
+        * and turn the LED on. Both actions may have been scheduled to run
+        * before this work item runs once. To make sure this works properly
+        * handle LED_SET_BRIGHTNESS_OFF first.
+        */
+       if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags))
+               set_brightness_delayed_set_brightness(led_cdev, LED_OFF);
+
+       if (test_and_clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags))
+               set_brightness_delayed_set_brightness(led_cdev, led_cdev->delayed_set_value);
+
+       if (test_and_clear_bit(LED_SET_BLINK, &led_cdev->work_flags)) {
+               unsigned long delay_on = led_cdev->delayed_delay_on;
+               unsigned long delay_off = led_cdev->delayed_delay_off;
+
+               led_blink_set(led_cdev, &delay_on, &delay_off);
+       }
+}
+
 static void led_set_software_blink(struct led_classdev *led_cdev,
                                   unsigned long delay_on,
                                   unsigned long delay_off)
@@ -229,6 +253,22 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
 }
 EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
 
+void led_blink_set_nosleep(struct led_classdev *led_cdev, unsigned long delay_on,
+                          unsigned long delay_off)
+{
+       /* If necessary delegate to a work queue task. */
+       if (led_cdev->blink_set && led_cdev->brightness_set_blocking) {
+               led_cdev->delayed_delay_on = delay_on;
+               led_cdev->delayed_delay_off = delay_off;
+               set_bit(LED_SET_BLINK, &led_cdev->work_flags);
+               schedule_work(&led_cdev->set_brightness_work);
+               return;
+       }
+
+       led_blink_set(led_cdev, &delay_on, &delay_off);
+}
+EXPORT_SYMBOL_GPL(led_blink_set_nosleep);
+
 void led_stop_software_blink(struct led_classdev *led_cdev)
 {
        del_timer_sync(&led_cdev->blink_timer);
@@ -271,8 +311,23 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
        if (!__led_set_brightness(led_cdev, value))
                return;
 
-       /* If brightness setting can sleep, delegate it to a work queue task */
-       led_cdev->delayed_set_value = value;
+       /*
+        * Brightness setting can sleep, delegate it to a work queue task.
+        * value 0 / LED_OFF is special, since it also disables hw-blinking
+        * (sw-blink disable is handled in led_set_brightness()).
+        * To avoid a hw-blink-disable getting lost when a second brightness
+        * change is done immediately afterwards (before the work runs),
+        * it uses a separate work_flag.
+        */
+       if (value) {
+               led_cdev->delayed_set_value = value;
+               set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
+       } else {
+               clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
+               clear_bit(LED_SET_BLINK, &led_cdev->work_flags);
+               set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
+       }
+
        schedule_work(&led_cdev->set_brightness_work);
 }
 EXPORT_SYMBOL_GPL(led_set_brightness_nopm);