diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/charger-manager.c | 163 |
1 files changed, 163 insertions, 0 deletions
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"); |