power_supply: Hold a wake_lock while power supply change notifications are pending
[linux-3.10.git] / drivers / power / power_supply_core.c
index 1c517c3..0e210ab 100644 (file)
@@ -67,23 +67,40 @@ static int __power_supply_changed_work(struct device *dev, void *data)
 
 static void power_supply_changed_work(struct work_struct *work)
 {
+       unsigned long flags;
        struct power_supply *psy = container_of(work, struct power_supply,
                                                changed_work);
 
        dev_dbg(psy->dev, "%s\n", __func__);
 
-       class_for_each_device(power_supply_class, NULL, psy,
-                             __power_supply_changed_work);
+       spin_lock_irqsave(&psy->changed_lock, flags);
+       if (psy->changed) {
+               psy->changed = false;
+               spin_unlock_irqrestore(&psy->changed_lock, flags);
 
-       power_supply_update_leds(psy);
+               class_for_each_device(power_supply_class, NULL, psy,
+                                     __power_supply_changed_work);
 
-       kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
+               power_supply_update_leds(psy);
+
+               kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
+               spin_lock_irqsave(&psy->changed_lock, flags);
+       }
+       if (!psy->changed)
+               wake_unlock(&psy->work_wake_lock);
+       spin_unlock_irqrestore(&psy->changed_lock, flags);
 }
 
 void power_supply_changed(struct power_supply *psy)
 {
+       unsigned long flags;
+
        dev_dbg(psy->dev, "%s\n", __func__);
 
+       spin_lock_irqsave(&psy->changed_lock, flags);
+       psy->changed = true;
+       wake_lock(&psy->work_wake_lock);
+       spin_unlock_irqrestore(&psy->changed_lock, flags);
        schedule_work(&psy->changed_work);
 }
 EXPORT_SYMBOL_GPL(power_supply_changed);
@@ -504,6 +521,9 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
        if (rc)
                goto device_add_failed;
 
+       spin_lock_init(&psy->changed_lock);
+       wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");
+
        rc = psy_register_thermal(psy);
        if (rc)
                goto register_thermal_failed;
@@ -525,6 +545,7 @@ create_triggers_failed:
 register_cooler_failed:
        psy_unregister_thermal(psy);
 register_thermal_failed:
+       wake_lock_destroy(&psy->work_wake_lock);
        device_del(dev);
 kobject_set_name_failed:
 device_add_failed:
@@ -542,6 +563,7 @@ void power_supply_unregister(struct power_supply *psy)
        power_supply_remove_triggers(psy);
        psy_unregister_cooler(psy);
        psy_unregister_thermal(psy);
+       wake_lock_destroy(&psy->work_wake_lock);
        device_unregister(psy->dev);
 }
 EXPORT_SYMBOL_GPL(power_supply_unregister);