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>
Tue, 14 Jun 2011 16:08:45 +0000 (09:08 -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 329b46b2327d4110eaea4438f87f2570f2587e88..03810ce5633faf03105d9690b4369cdf0a8950d3 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);
@@ -181,6 +198,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 = power_supply_create_triggers(psy);
        if (rc)
                goto create_triggers_failed;
@@ -190,6 +210,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_del(dev);
 kobject_set_name_failed:
 device_add_failed:
@@ -203,6 +224,7 @@ void power_supply_unregister(struct power_supply *psy)
 {
        cancel_work_sync(&psy->changed_work);
        power_supply_remove_triggers(psy);
+       wake_lock_destroy(&psy->work_wake_lock);
        device_unregister(psy->dev);
 }
 EXPORT_SYMBOL_GPL(power_supply_unregister);
index 204c18dfdc9e839a378cfd4200342784c22be5b7..2287c3214138dc556e0c26a8686e3be35364a338 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>
 
@@ -163,6 +164,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;