power_supply: Hold a wake_lock while power supply change notifications are pending
authorArve Hjønnevåg <arve@android.com>
Tue, 22 Sep 2009 00:26:47 +0000 (17:26 -0700)
committerColin Cross <ccross@android.com>
Thu, 30 Sep 2010 00:49:09 +0000 (17:49 -0700)
When connecting usb or the charger the device would often go back to sleep
before the charge led and screen turned on.

Change-Id: I01def6d86ddece0d4e31d2a91d176ed0975b6b9d
Signed-off-by: Arve Hjønnevåg <arve@android.com>
drivers/power/power_supply_core.c
include/linux/power_supply.h

index 91606bb55318110307e3997bcb61722cd247ee73..127617195629873befb2966799a8b17aae4221bd 100644 (file)
@@ -41,23 +41,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);
@@ -180,6 +197,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
                goto device_add_failed;
 
        INIT_WORK(&psy->changed_work, power_supply_changed_work);
+       spin_lock_init(&psy->changed_lock);
+       wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");
 
        rc = power_supply_create_triggers(psy);
        if (rc)
@@ -190,6 +209,7 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
        goto success;
 
 create_triggers_failed:
+       wake_lock_destroy(&psy->work_wake_lock);
        device_unregister(psy->dev);
 kobject_set_name_failed:
 device_add_failed:
@@ -203,6 +223,7 @@ void power_supply_unregister(struct power_supply *psy)
 {
        flush_scheduled_work();
        power_supply_remove_triggers(psy);
+       wake_lock_destroy(&psy->work_wake_lock);
        device_unregister(psy->dev);
 }
 EXPORT_SYMBOL_GPL(power_supply_unregister);
index 30083a896f3628418fa42114a582dc18396c66e9..f5a8453203f4a959c0a44eea9e242e7ccf46173f 100644 (file)
@@ -14,6 +14,7 @@
 #define __LINUX_POWER_SUPPLY_H__
 
 #include <linux/device.h>
+#include <linux/wakelock.h>
 #include <linux/workqueue.h>
 #include <linux/leds.h>
 
@@ -159,6 +160,9 @@ struct power_supply {
        /* private */
        struct device *dev;
        struct work_struct changed_work;
+       spinlock_t changed_lock;
+       bool changed;
+       struct wake_lock work_wake_lock;
 
 #ifdef CONFIG_LEDS_TRIGGERS
        struct led_trigger *charging_full_trig;