|
@@ -67,23 +67,42 @@ 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);
|
|
|
-
|
|
|
- power_supply_update_leds(psy);
|
|
|
-
|
|
|
- kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
|
|
|
+ spin_lock_irqsave(&psy->changed_lock, flags);
|
|
|
+ if (psy->changed) {
|
|
|
+ psy->changed = false;
|
|
|
+ spin_unlock_irqrestore(&psy->changed_lock, flags);
|
|
|
+ class_for_each_device(power_supply_class, NULL, psy,
|
|
|
+ __power_supply_changed_work);
|
|
|
+ power_supply_update_leds(psy);
|
|
|
+ kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
|
|
|
+ spin_lock_irqsave(&psy->changed_lock, flags);
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Dependent power supplies (e.g. battery) may have changed state
|
|
|
+ * as a result of this event, so poll again and hold the
|
|
|
+ * wakeup_source until all events are processed.
|
|
|
+ */
|
|
|
+ if (!psy->changed)
|
|
|
+ pm_relax(psy->dev);
|
|
|
+ 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;
|
|
|
+ pm_stay_awake(psy->dev);
|
|
|
+ spin_unlock_irqrestore(&psy->changed_lock, flags);
|
|
|
schedule_work(&psy->changed_work);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(power_supply_changed);
|
|
@@ -500,6 +519,11 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
|
|
|
goto check_supplies_failed;
|
|
|
}
|
|
|
|
|
|
+ spin_lock_init(&psy->changed_lock);
|
|
|
+ rc = device_init_wakeup(dev, true);
|
|
|
+ if (rc)
|
|
|
+ goto wakeup_init_failed;
|
|
|
+
|
|
|
rc = kobject_set_name(&dev->kobj, "%s", psy->name);
|
|
|
if (rc)
|
|
|
goto kobject_set_name_failed;
|
|
@@ -529,6 +553,7 @@ create_triggers_failed:
|
|
|
register_cooler_failed:
|
|
|
psy_unregister_thermal(psy);
|
|
|
register_thermal_failed:
|
|
|
+wakeup_init_failed:
|
|
|
device_del(dev);
|
|
|
kobject_set_name_failed:
|
|
|
device_add_failed:
|
|
@@ -546,6 +571,7 @@ void power_supply_unregister(struct power_supply *psy)
|
|
|
power_supply_remove_triggers(psy);
|
|
|
psy_unregister_cooler(psy);
|
|
|
psy_unregister_thermal(psy);
|
|
|
+ device_init_wakeup(psy->dev, false);
|
|
|
device_unregister(psy->dev);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(power_supply_unregister);
|