diff options
-rw-r--r-- | drivers/power/power_supply_core.c | 38 | ||||
-rw-r--r-- | include/linux/power_supply.h | 3 |
2 files changed, 35 insertions, 6 deletions
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 3b2d5df45e7a..00e667296360 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c | |||
@@ -67,23 +67,42 @@ static int __power_supply_changed_work(struct device *dev, void *data) | |||
67 | 67 | ||
68 | static void power_supply_changed_work(struct work_struct *work) | 68 | static void power_supply_changed_work(struct work_struct *work) |
69 | { | 69 | { |
70 | unsigned long flags; | ||
70 | struct power_supply *psy = container_of(work, struct power_supply, | 71 | struct power_supply *psy = container_of(work, struct power_supply, |
71 | changed_work); | 72 | changed_work); |
72 | 73 | ||
73 | dev_dbg(psy->dev, "%s\n", __func__); | 74 | dev_dbg(psy->dev, "%s\n", __func__); |
74 | 75 | ||
75 | class_for_each_device(power_supply_class, NULL, psy, | 76 | spin_lock_irqsave(&psy->changed_lock, flags); |
76 | __power_supply_changed_work); | 77 | if (psy->changed) { |
77 | 78 | psy->changed = false; | |
78 | power_supply_update_leds(psy); | 79 | spin_unlock_irqrestore(&psy->changed_lock, flags); |
79 | 80 | class_for_each_device(power_supply_class, NULL, psy, | |
80 | kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); | 81 | __power_supply_changed_work); |
82 | power_supply_update_leds(psy); | ||
83 | kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); | ||
84 | spin_lock_irqsave(&psy->changed_lock, flags); | ||
85 | } | ||
86 | /* | ||
87 | * Dependent power supplies (e.g. battery) may have changed state | ||
88 | * as a result of this event, so poll again and hold the | ||
89 | * wakeup_source until all events are processed. | ||
90 | */ | ||
91 | if (!psy->changed) | ||
92 | pm_relax(psy->dev); | ||
93 | spin_unlock_irqrestore(&psy->changed_lock, flags); | ||
81 | } | 94 | } |
82 | 95 | ||
83 | void power_supply_changed(struct power_supply *psy) | 96 | void power_supply_changed(struct power_supply *psy) |
84 | { | 97 | { |
98 | unsigned long flags; | ||
99 | |||
85 | dev_dbg(psy->dev, "%s\n", __func__); | 100 | dev_dbg(psy->dev, "%s\n", __func__); |
86 | 101 | ||
102 | spin_lock_irqsave(&psy->changed_lock, flags); | ||
103 | psy->changed = true; | ||
104 | pm_stay_awake(psy->dev); | ||
105 | spin_unlock_irqrestore(&psy->changed_lock, flags); | ||
87 | schedule_work(&psy->changed_work); | 106 | schedule_work(&psy->changed_work); |
88 | } | 107 | } |
89 | EXPORT_SYMBOL_GPL(power_supply_changed); | 108 | EXPORT_SYMBOL_GPL(power_supply_changed); |
@@ -500,6 +519,11 @@ int power_supply_register(struct device *parent, struct power_supply *psy) | |||
500 | goto check_supplies_failed; | 519 | goto check_supplies_failed; |
501 | } | 520 | } |
502 | 521 | ||
522 | spin_lock_init(&psy->changed_lock); | ||
523 | rc = device_init_wakeup(dev, true); | ||
524 | if (rc) | ||
525 | goto wakeup_init_failed; | ||
526 | |||
503 | rc = kobject_set_name(&dev->kobj, "%s", psy->name); | 527 | rc = kobject_set_name(&dev->kobj, "%s", psy->name); |
504 | if (rc) | 528 | if (rc) |
505 | goto kobject_set_name_failed; | 529 | goto kobject_set_name_failed; |
@@ -529,6 +553,7 @@ create_triggers_failed: | |||
529 | register_cooler_failed: | 553 | register_cooler_failed: |
530 | psy_unregister_thermal(psy); | 554 | psy_unregister_thermal(psy); |
531 | register_thermal_failed: | 555 | register_thermal_failed: |
556 | wakeup_init_failed: | ||
532 | device_del(dev); | 557 | device_del(dev); |
533 | kobject_set_name_failed: | 558 | kobject_set_name_failed: |
534 | device_add_failed: | 559 | device_add_failed: |
@@ -546,6 +571,7 @@ void power_supply_unregister(struct power_supply *psy) | |||
546 | power_supply_remove_triggers(psy); | 571 | power_supply_remove_triggers(psy); |
547 | psy_unregister_cooler(psy); | 572 | psy_unregister_cooler(psy); |
548 | psy_unregister_thermal(psy); | 573 | psy_unregister_thermal(psy); |
574 | device_init_wakeup(psy->dev, false); | ||
549 | device_unregister(psy->dev); | 575 | device_unregister(psy->dev); |
550 | } | 576 | } |
551 | EXPORT_SYMBOL_GPL(power_supply_unregister); | 577 | EXPORT_SYMBOL_GPL(power_supply_unregister); |
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 804b90643a85..5c2600630dc9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h | |||
@@ -15,6 +15,7 @@ | |||
15 | 15 | ||
16 | #include <linux/workqueue.h> | 16 | #include <linux/workqueue.h> |
17 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
18 | #include <linux/spinlock.h> | ||
18 | 19 | ||
19 | struct device; | 20 | struct device; |
20 | 21 | ||
@@ -194,6 +195,8 @@ struct power_supply { | |||
194 | /* private */ | 195 | /* private */ |
195 | struct device *dev; | 196 | struct device *dev; |
196 | struct work_struct changed_work; | 197 | struct work_struct changed_work; |
198 | spinlock_t changed_lock; | ||
199 | bool changed; | ||
197 | #ifdef CONFIG_THERMAL | 200 | #ifdef CONFIG_THERMAL |
198 | struct thermal_zone_device *tzd; | 201 | struct thermal_zone_device *tzd; |
199 | struct thermal_cooling_device *tcd; | 202 | struct thermal_cooling_device *tcd; |