diff options
-rw-r--r-- | Documentation/power/charger-manager.txt | 16 | ||||
-rw-r--r-- | drivers/power/charger-manager.c | 163 | ||||
-rw-r--r-- | include/linux/power/charger-manager.h | 25 |
3 files changed, 195 insertions, 9 deletions
diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt index 9b3863386e54..b4f7f4b23f64 100644 --- a/Documentation/power/charger-manager.txt +++ b/Documentation/power/charger-manager.txt | |||
@@ -50,6 +50,10 @@ Charger Manager supports the following: | |||
50 | restarts charging. This check is also performed while suspended by | 50 | restarts charging. This check is also performed while suspended by |
51 | setting wakeup time accordingly and using suspend_again. | 51 | setting wakeup time accordingly and using suspend_again. |
52 | 52 | ||
53 | * Support for uevent-notify | ||
54 | With the charger-related events, the device sends | ||
55 | notification to users with UEVENT. | ||
56 | |||
53 | 2. Global Charger-Manager Data related with suspend_again | 57 | 2. Global Charger-Manager Data related with suspend_again |
54 | ======================================================== | 58 | ======================================================== |
55 | In order to setup Charger Manager with suspend-again feature | 59 | In order to setup Charger Manager with suspend-again feature |
@@ -174,7 +178,17 @@ bool measure_battery_temp; | |||
174 | the value of measure_battery_temp. | 178 | the value of measure_battery_temp. |
175 | }; | 179 | }; |
176 | 180 | ||
177 | 5. Other Considerations | 181 | 5. Notify Charger-Manager of charger events: cm_notify_event() |
182 | ========================================================= | ||
183 | If there is an charger event is required to notify | ||
184 | Charger Manager, a charger device driver that triggers the event can call | ||
185 | cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager. | ||
186 | In the function, psy is the charger driver's power_supply pointer, which is | ||
187 | associated with Charger-Manager. The parameter "type" | ||
188 | is the same as irq's type (enum cm_event_types). The event message "msg" is | ||
189 | optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS". | ||
190 | |||
191 | 6. Other Considerations | ||
178 | ======================= | 192 | ======================= |
179 | 193 | ||
180 | At the charger/battery-related events such as battery-pulled-out, | 194 | At the charger/battery-related events such as battery-pulled-out, |
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 959062d16bac..86935ec18954 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c | |||
@@ -23,6 +23,16 @@ | |||
23 | #include <linux/power/charger-manager.h> | 23 | #include <linux/power/charger-manager.h> |
24 | #include <linux/regulator/consumer.h> | 24 | #include <linux/regulator/consumer.h> |
25 | 25 | ||
26 | static const char * const default_event_names[] = { | ||
27 | [CM_EVENT_UNKNOWN] = "Unknown", | ||
28 | [CM_EVENT_BATT_FULL] = "Battery Full", | ||
29 | [CM_EVENT_BATT_IN] = "Battery Inserted", | ||
30 | [CM_EVENT_BATT_OUT] = "Battery Pulled Out", | ||
31 | [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach", | ||
32 | [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", | ||
33 | [CM_EVENT_OTHERS] = "Other battery events" | ||
34 | }; | ||
35 | |||
26 | /* | 36 | /* |
27 | * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for | 37 | * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for |
28 | * delayed works so that we can run delayed works with CM_JIFFIES_SMALL | 38 | * delayed works so that we can run delayed works with CM_JIFFIES_SMALL |
@@ -525,6 +535,69 @@ static void cm_monitor_poller(struct work_struct *work) | |||
525 | schedule_work(&setup_polling); | 535 | schedule_work(&setup_polling); |
526 | } | 536 | } |
527 | 537 | ||
538 | /** | ||
539 | * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL | ||
540 | * @cm: the Charger Manager representing the battery. | ||
541 | */ | ||
542 | static void fullbatt_handler(struct charger_manager *cm) | ||
543 | { | ||
544 | struct charger_desc *desc = cm->desc; | ||
545 | |||
546 | if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) | ||
547 | goto out; | ||
548 | |||
549 | if (cm_suspended) | ||
550 | device_set_wakeup_capable(cm->dev, true); | ||
551 | |||
552 | if (delayed_work_pending(&cm->fullbatt_vchk_work)) | ||
553 | cancel_delayed_work(&cm->fullbatt_vchk_work); | ||
554 | queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, | ||
555 | msecs_to_jiffies(desc->fullbatt_vchkdrop_ms)); | ||
556 | cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies( | ||
557 | desc->fullbatt_vchkdrop_ms); | ||
558 | |||
559 | if (cm->fullbatt_vchk_jiffies_at == 0) | ||
560 | cm->fullbatt_vchk_jiffies_at = 1; | ||
561 | |||
562 | out: | ||
563 | dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n"); | ||
564 | uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * battout_handler - Event handler for CM_EVENT_BATT_OUT | ||
569 | * @cm: the Charger Manager representing the battery. | ||
570 | */ | ||
571 | static void battout_handler(struct charger_manager *cm) | ||
572 | { | ||
573 | if (cm_suspended) | ||
574 | device_set_wakeup_capable(cm->dev, true); | ||
575 | |||
576 | if (!is_batt_present(cm)) { | ||
577 | dev_emerg(cm->dev, "Battery Pulled Out!\n"); | ||
578 | uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]); | ||
579 | } else { | ||
580 | uevent_notify(cm, "Battery Reinserted?"); | ||
581 | } | ||
582 | } | ||
583 | |||
584 | /** | ||
585 | * misc_event_handler - Handler for other evnets | ||
586 | * @cm: the Charger Manager representing the battery. | ||
587 | * @type: the Charger Manager representing the battery. | ||
588 | */ | ||
589 | static void misc_event_handler(struct charger_manager *cm, | ||
590 | enum cm_event_types type) | ||
591 | { | ||
592 | if (cm_suspended) | ||
593 | device_set_wakeup_capable(cm->dev, true); | ||
594 | |||
595 | if (!delayed_work_pending(&cm_monitor_work) && | ||
596 | is_polling_required(cm) && cm->desc->polling_interval_ms) | ||
597 | schedule_work(&setup_polling); | ||
598 | uevent_notify(cm, default_event_names[type]); | ||
599 | } | ||
600 | |||
528 | static int charger_get_property(struct power_supply *psy, | 601 | static int charger_get_property(struct power_supply *psy, |
529 | enum power_supply_property psp, | 602 | enum power_supply_property psp, |
530 | union power_supply_propval *val) | 603 | union power_supply_propval *val) |
@@ -1112,6 +1185,13 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1112 | list_add(&cm->entry, &cm_list); | 1185 | list_add(&cm->entry, &cm_list); |
1113 | mutex_unlock(&cm_list_mtx); | 1186 | mutex_unlock(&cm_list_mtx); |
1114 | 1187 | ||
1188 | /* | ||
1189 | * Charger-manager is capable of waking up the systme from sleep | ||
1190 | * when event is happend through cm_notify_event() | ||
1191 | */ | ||
1192 | device_init_wakeup(&pdev->dev, true); | ||
1193 | device_set_wakeup_capable(&pdev->dev, false); | ||
1194 | |||
1115 | schedule_work(&setup_polling); | 1195 | schedule_work(&setup_polling); |
1116 | 1196 | ||
1117 | return 0; | 1197 | return 0; |
@@ -1169,6 +1249,18 @@ static const struct platform_device_id charger_manager_id[] = { | |||
1169 | }; | 1249 | }; |
1170 | MODULE_DEVICE_TABLE(platform, charger_manager_id); | 1250 | MODULE_DEVICE_TABLE(platform, charger_manager_id); |
1171 | 1251 | ||
1252 | static int cm_suspend_noirq(struct device *dev) | ||
1253 | { | ||
1254 | int ret = 0; | ||
1255 | |||
1256 | if (device_may_wakeup(dev)) { | ||
1257 | device_set_wakeup_capable(dev, false); | ||
1258 | ret = -EAGAIN; | ||
1259 | } | ||
1260 | |||
1261 | return ret; | ||
1262 | } | ||
1263 | |||
1172 | static int cm_suspend_prepare(struct device *dev) | 1264 | static int cm_suspend_prepare(struct device *dev) |
1173 | { | 1265 | { |
1174 | struct charger_manager *cm = dev_get_drvdata(dev); | 1266 | struct charger_manager *cm = dev_get_drvdata(dev); |
@@ -1250,11 +1342,13 @@ static void cm_suspend_complete(struct device *dev) | |||
1250 | queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, | 1342 | queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, |
1251 | msecs_to_jiffies(delay)); | 1343 | msecs_to_jiffies(delay)); |
1252 | } | 1344 | } |
1345 | device_set_wakeup_capable(cm->dev, false); | ||
1253 | uevent_notify(cm, NULL); | 1346 | uevent_notify(cm, NULL); |
1254 | } | 1347 | } |
1255 | 1348 | ||
1256 | static const struct dev_pm_ops charger_manager_pm = { | 1349 | static const struct dev_pm_ops charger_manager_pm = { |
1257 | .prepare = cm_suspend_prepare, | 1350 | .prepare = cm_suspend_prepare, |
1351 | .suspend_noirq = cm_suspend_noirq, | ||
1258 | .complete = cm_suspend_complete, | 1352 | .complete = cm_suspend_complete, |
1259 | }; | 1353 | }; |
1260 | 1354 | ||
@@ -1287,6 +1381,75 @@ static void __exit charger_manager_cleanup(void) | |||
1287 | } | 1381 | } |
1288 | module_exit(charger_manager_cleanup); | 1382 | module_exit(charger_manager_cleanup); |
1289 | 1383 | ||
1384 | /** | ||
1385 | * find_power_supply - find the associated power_supply of charger | ||
1386 | * @cm: the Charger Manager representing the battery | ||
1387 | * @psy: pointer to instance of charger's power_supply | ||
1388 | */ | ||
1389 | static bool find_power_supply(struct charger_manager *cm, | ||
1390 | struct power_supply *psy) | ||
1391 | { | ||
1392 | int i; | ||
1393 | bool found = false; | ||
1394 | |||
1395 | for (i = 0; cm->charger_stat[i]; i++) { | ||
1396 | if (psy == cm->charger_stat[i]) { | ||
1397 | found = true; | ||
1398 | break; | ||
1399 | } | ||
1400 | } | ||
1401 | |||
1402 | return found; | ||
1403 | } | ||
1404 | |||
1405 | /** | ||
1406 | * cm_notify_event - charger driver notify Charger Manager of charger event | ||
1407 | * @psy: pointer to instance of charger's power_supply | ||
1408 | * @type: type of charger event | ||
1409 | * @msg: optional message passed to uevent_notify fuction | ||
1410 | */ | ||
1411 | void cm_notify_event(struct power_supply *psy, enum cm_event_types type, | ||
1412 | char *msg) | ||
1413 | { | ||
1414 | struct charger_manager *cm; | ||
1415 | bool found_power_supply = false; | ||
1416 | |||
1417 | if (psy == NULL) | ||
1418 | return; | ||
1419 | |||
1420 | mutex_lock(&cm_list_mtx); | ||
1421 | list_for_each_entry(cm, &cm_list, entry) { | ||
1422 | found_power_supply = find_power_supply(cm, psy); | ||
1423 | if (found_power_supply) | ||
1424 | break; | ||
1425 | } | ||
1426 | mutex_unlock(&cm_list_mtx); | ||
1427 | |||
1428 | if (!found_power_supply) | ||
1429 | return; | ||
1430 | |||
1431 | switch (type) { | ||
1432 | case CM_EVENT_BATT_FULL: | ||
1433 | fullbatt_handler(cm); | ||
1434 | break; | ||
1435 | case CM_EVENT_BATT_OUT: | ||
1436 | battout_handler(cm); | ||
1437 | break; | ||
1438 | case CM_EVENT_BATT_IN: | ||
1439 | case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP: | ||
1440 | misc_event_handler(cm, type); | ||
1441 | break; | ||
1442 | case CM_EVENT_UNKNOWN: | ||
1443 | case CM_EVENT_OTHERS: | ||
1444 | uevent_notify(cm, msg ? msg : default_event_names[type]); | ||
1445 | break; | ||
1446 | default: | ||
1447 | dev_err(cm->dev, "%s type not specified.\n", __func__); | ||
1448 | break; | ||
1449 | } | ||
1450 | } | ||
1451 | EXPORT_SYMBOL_GPL(cm_notify_event); | ||
1452 | |||
1290 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | 1453 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
1291 | MODULE_DESCRIPTION("Charger Manager"); | 1454 | MODULE_DESCRIPTION("Charger Manager"); |
1292 | MODULE_LICENSE("GPL"); | 1455 | MODULE_LICENSE("GPL"); |
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index baa299a95e13..241065c9ce51 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h | |||
@@ -31,6 +31,16 @@ enum polling_modes { | |||
31 | CM_POLL_CHARGING_ONLY, | 31 | CM_POLL_CHARGING_ONLY, |
32 | }; | 32 | }; |
33 | 33 | ||
34 | enum cm_event_types { | ||
35 | CM_EVENT_UNKNOWN = 0, | ||
36 | CM_EVENT_BATT_FULL, | ||
37 | CM_EVENT_BATT_IN, | ||
38 | CM_EVENT_BATT_OUT, | ||
39 | CM_EVENT_EXT_PWR_IN_OUT, | ||
40 | CM_EVENT_CHG_START_STOP, | ||
41 | CM_EVENT_OTHERS, | ||
42 | }; | ||
43 | |||
34 | /** | 44 | /** |
35 | * struct charger_global_desc | 45 | * struct charger_global_desc |
36 | * @rtc_name: the name of RTC used to wake up the system from suspend. | 46 | * @rtc_name: the name of RTC used to wake up the system from suspend. |
@@ -159,14 +169,13 @@ struct charger_manager { | |||
159 | #ifdef CONFIG_CHARGER_MANAGER | 169 | #ifdef CONFIG_CHARGER_MANAGER |
160 | extern int setup_charger_manager(struct charger_global_desc *gd); | 170 | extern int setup_charger_manager(struct charger_global_desc *gd); |
161 | extern bool cm_suspend_again(void); | 171 | extern bool cm_suspend_again(void); |
172 | extern void cm_notify_event(struct power_supply *psy, | ||
173 | enum cm_event_types type, char *msg); | ||
162 | #else | 174 | #else |
163 | static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd) | 175 | static inline int setup_charger_manager(struct charger_global_desc *gd) |
164 | { } | 176 | { return 0; } |
165 | 177 | static inline bool cm_suspend_again(void) { return false; } | |
166 | static bool __maybe_unused cm_suspend_again(void) | 178 | static inline void cm_notify_event(struct power_supply *psy, |
167 | { | 179 | enum cm_event_types type, char *msg) { } |
168 | return false; | ||
169 | } | ||
170 | #endif | 180 | #endif |
171 | |||
172 | #endif /* _CHARGER_MANAGER_H */ | 181 | #endif /* _CHARGER_MANAGER_H */ |