aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2012-05-05 09:24:10 -0400
committerAnton Vorontsov <anton.vorontsov@linaro.org>2012-05-05 22:48:50 -0400
commitd829dc75bafb10754f35fb8895e5143d20267b04 (patch)
tree4cff2aa07dcf7c15ef931e3b9ab20a2d84fcf68d /drivers/power
parent34298d40e5853bc195c9db012fc1ddccac9b6f7f (diff)
charger-manager: Poll battery health in normal state
Charger-Manager needs to check battery health in normal state as well as suspend-to-RAM state. When the battery is fully charged, Charger-Manager needs to determine when the chargers restart charging. This patch allows Charger-Manager to monitor battery health in normal state and handle operation for chargers after battery is fully charged. Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/charger-manager.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 9eca9f1ff0ea..959062d16bac 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -57,6 +57,12 @@ static bool cm_suspended;
57static bool cm_rtc_set; 57static bool cm_rtc_set;
58static unsigned long cm_suspend_duration_ms; 58static unsigned long cm_suspend_duration_ms;
59 59
60/* About normal (not suspended) monitoring */
61static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
62static unsigned long next_polling; /* Next appointed polling time */
63static struct workqueue_struct *cm_wq; /* init at driver add */
64static struct delayed_work cm_monitor_work; /* init at driver add */
65
60/* Global charger-manager description */ 66/* Global charger-manager description */
61static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ 67static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
62 68
@@ -71,6 +77,11 @@ static bool is_batt_present(struct charger_manager *cm)
71 int i, ret; 77 int i, ret;
72 78
73 switch (cm->desc->battery_present) { 79 switch (cm->desc->battery_present) {
80 case CM_BATTERY_PRESENT:
81 present = true;
82 break;
83 case CM_NO_BATTERY:
84 break;
74 case CM_FUEL_GAUGE: 85 case CM_FUEL_GAUGE:
75 ret = cm->fuel_gauge->get_property(cm->fuel_gauge, 86 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
76 POWER_SUPPLY_PROP_PRESENT, &val); 87 POWER_SUPPLY_PROP_PRESENT, &val);
@@ -279,6 +290,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
279} 290}
280 291
281/** 292/**
293 * try_charger_restart - Restart charging.
294 * @cm: the Charger Manager representing the battery.
295 *
296 * Restart charging by turning off and on the charger.
297 */
298static int try_charger_restart(struct charger_manager *cm)
299{
300 int err;
301
302 if (cm->emergency_stop)
303 return -EAGAIN;
304
305 err = try_charger_enable(cm, false);
306 if (err)
307 return err;
308
309 return try_charger_enable(cm, true);
310}
311
312/**
282 * uevent_notify - Let users know something has changed. 313 * uevent_notify - Let users know something has changed.
283 * @cm: the Charger Manager representing the battery. 314 * @cm: the Charger Manager representing the battery.
284 * @event: the event string. 315 * @event: the event string.
@@ -334,6 +365,46 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
334} 365}
335 366
336/** 367/**
368 * fullbatt_vchk - Check voltage drop some times after "FULL" event.
369 * @work: the work_struct appointing the function
370 *
371 * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
372 * charger_desc, Charger Manager checks voltage drop after the battery
373 * "FULL" event. It checks whether the voltage has dropped more than
374 * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
375 */
376static void fullbatt_vchk(struct work_struct *work)
377{
378 struct delayed_work *dwork = to_delayed_work(work);
379 struct charger_manager *cm = container_of(dwork,
380 struct charger_manager, fullbatt_vchk_work);
381 struct charger_desc *desc = cm->desc;
382 int batt_uV, err, diff;
383
384 /* remove the appointment for fullbatt_vchk */
385 cm->fullbatt_vchk_jiffies_at = 0;
386
387 if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
388 return;
389
390 err = get_batt_uV(cm, &batt_uV);
391 if (err) {
392 dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
393 return;
394 }
395
396 diff = cm->fullbatt_vchk_uV;
397 diff -= batt_uV;
398
399 dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
400
401 if (diff > desc->fullbatt_vchkdrop_uV) {
402 try_charger_restart(cm);
403 uevent_notify(cm, "Recharge");
404 }
405}
406
407/**
337 * _cm_monitor - Monitor the temperature and return true for exceptions. 408 * _cm_monitor - Monitor the temperature and return true for exceptions.
338 * @cm: the Charger Manager representing the battery. 409 * @cm: the Charger Manager representing the battery.
339 * 410 *
@@ -392,6 +463,68 @@ static bool cm_monitor(void)
392 return stop; 463 return stop;
393} 464}
394 465
466/**
467 * _setup_polling - Setup the next instance of polling.
468 * @work: work_struct of the function _setup_polling.
469 */
470static void _setup_polling(struct work_struct *work)
471{
472 unsigned long min = ULONG_MAX;
473 struct charger_manager *cm;
474 bool keep_polling = false;
475 unsigned long _next_polling;
476
477 mutex_lock(&cm_list_mtx);
478
479 list_for_each_entry(cm, &cm_list, entry) {
480 if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
481 keep_polling = true;
482
483 if (min > cm->desc->polling_interval_ms)
484 min = cm->desc->polling_interval_ms;
485 }
486 }
487
488 polling_jiffy = msecs_to_jiffies(min);
489 if (polling_jiffy <= CM_JIFFIES_SMALL)
490 polling_jiffy = CM_JIFFIES_SMALL + 1;
491
492 if (!keep_polling)
493 polling_jiffy = ULONG_MAX;
494 if (polling_jiffy == ULONG_MAX)
495 goto out;
496
497 WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
498 ". try it later. %s\n", __func__);
499
500 _next_polling = jiffies + polling_jiffy;
501
502 if (!delayed_work_pending(&cm_monitor_work) ||
503 (delayed_work_pending(&cm_monitor_work) &&
504 time_after(next_polling, _next_polling))) {
505 cancel_delayed_work_sync(&cm_monitor_work);
506 next_polling = jiffies + polling_jiffy;
507 queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
508 }
509
510out:
511 mutex_unlock(&cm_list_mtx);
512}
513static DECLARE_WORK(setup_polling, _setup_polling);
514
515/**
516 * cm_monitor_poller - The Monitor / Poller.
517 * @work: work_struct of the function cm_monitor_poller
518 *
519 * During non-suspended state, cm_monitor_poller is used to poll and monitor
520 * the batteries.
521 */
522static void cm_monitor_poller(struct work_struct *work)
523{
524 cm_monitor();
525 schedule_work(&setup_polling);
526}
527
395static int charger_get_property(struct power_supply *psy, 528static int charger_get_property(struct power_supply *psy,
396 enum power_supply_property psp, 529 enum power_supply_property psp,
397 union power_supply_propval *val) 530 union power_supply_propval *val)
@@ -613,6 +746,21 @@ static bool cm_setup_timer(void)
613 mutex_lock(&cm_list_mtx); 746 mutex_lock(&cm_list_mtx);
614 747
615 list_for_each_entry(cm, &cm_list, entry) { 748 list_for_each_entry(cm, &cm_list, entry) {
749 unsigned int fbchk_ms = 0;
750
751 /* fullbatt_vchk is required. setup timer for that */
752 if (cm->fullbatt_vchk_jiffies_at) {
753 fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
754 - jiffies);
755 if (time_is_before_eq_jiffies(
756 cm->fullbatt_vchk_jiffies_at) ||
757 msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
758 fullbatt_vchk(&cm->fullbatt_vchk_work.work);
759 fbchk_ms = 0;
760 }
761 }
762 CM_MIN_VALID(wakeup_ms, fbchk_ms);
763
616 /* Skip if polling is not required for this CM */ 764 /* Skip if polling is not required for this CM */
617 if (!is_polling_required(cm) && !cm->emergency_stop) 765 if (!is_polling_required(cm) && !cm->emergency_stop)
618 continue; 766 continue;
@@ -672,6 +820,23 @@ static bool cm_setup_timer(void)
672 return false; 820 return false;
673} 821}
674 822
823static void _cm_fbchk_in_suspend(struct charger_manager *cm)
824{
825 unsigned long jiffy_now = jiffies;
826
827 if (!cm->fullbatt_vchk_jiffies_at)
828 return;
829
830 if (g_desc && g_desc->assume_timer_stops_in_suspend)
831 jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
832
833 /* Execute now if it's going to be executed not too long after */
834 jiffy_now += CM_JIFFIES_SMALL;
835
836 if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
837 fullbatt_vchk(&cm->fullbatt_vchk_work.work);
838}
839
675/** 840/**
676 * cm_suspend_again - Determine whether suspend again or not 841 * cm_suspend_again - Determine whether suspend again or not
677 * 842 *
@@ -693,6 +858,8 @@ bool cm_suspend_again(void)
693 ret = true; 858 ret = true;
694 mutex_lock(&cm_list_mtx); 859 mutex_lock(&cm_list_mtx);
695 list_for_each_entry(cm, &cm_list, entry) { 860 list_for_each_entry(cm, &cm_list, entry) {
861 _cm_fbchk_in_suspend(cm);
862
696 if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) || 863 if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
697 cm->status_save_batt != is_batt_present(cm)) { 864 cm->status_save_batt != is_batt_present(cm)) {
698 ret = false; 865 ret = false;
@@ -796,6 +963,21 @@ static int charger_manager_probe(struct platform_device *pdev)
796 memcpy(cm->desc, desc, sizeof(struct charger_desc)); 963 memcpy(cm->desc, desc, sizeof(struct charger_desc));
797 cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */ 964 cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
798 965
966 /*
967 * The following two do not need to be errors.
968 * Users may intentionally ignore those two features.
969 */
970 if (desc->fullbatt_uV == 0) {
971 dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
972 " as it is not supplied.");
973 }
974 if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
975 dev_info(&pdev->dev, "Disabling full-battery voltage drop "
976 "checking mechanism as it is not supplied.");
977 desc->fullbatt_vchkdrop_ms = 0;
978 desc->fullbatt_vchkdrop_uV = 0;
979 }
980
799 if (!desc->charger_regulators || desc->num_charger_regulators < 1) { 981 if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
800 ret = -EINVAL; 982 ret = -EINVAL;
801 dev_err(&pdev->dev, "charger_regulators undefined.\n"); 983 dev_err(&pdev->dev, "charger_regulators undefined.\n");
@@ -903,6 +1085,8 @@ static int charger_manager_probe(struct platform_device *pdev)
903 cm->charger_psy.num_properties++; 1085 cm->charger_psy.num_properties++;
904 } 1086 }
905 1087
1088 INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
1089
906 ret = power_supply_register(NULL, &cm->charger_psy); 1090 ret = power_supply_register(NULL, &cm->charger_psy);
907 if (ret) { 1091 if (ret) {
908 dev_err(&pdev->dev, "Cannot register charger-manager with" 1092 dev_err(&pdev->dev, "Cannot register charger-manager with"
@@ -928,6 +1112,8 @@ static int charger_manager_probe(struct platform_device *pdev)
928 list_add(&cm->entry, &cm_list); 1112 list_add(&cm->entry, &cm_list);
929 mutex_unlock(&cm_list_mtx); 1113 mutex_unlock(&cm_list_mtx);
930 1114
1115 schedule_work(&setup_polling);
1116
931 return 0; 1117 return 0;
932 1118
933err_chg_enable: 1119err_chg_enable:
@@ -958,9 +1144,17 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
958 list_del(&cm->entry); 1144 list_del(&cm->entry);
959 mutex_unlock(&cm_list_mtx); 1145 mutex_unlock(&cm_list_mtx);
960 1146
1147 if (work_pending(&setup_polling))
1148 cancel_work_sync(&setup_polling);
1149 if (delayed_work_pending(&cm_monitor_work))
1150 cancel_delayed_work_sync(&cm_monitor_work);
1151
961 regulator_bulk_free(desc->num_charger_regulators, 1152 regulator_bulk_free(desc->num_charger_regulators,
962 desc->charger_regulators); 1153 desc->charger_regulators);
963 power_supply_unregister(&cm->charger_psy); 1154 power_supply_unregister(&cm->charger_psy);
1155
1156 try_charger_enable(cm, false);
1157
964 kfree(cm->charger_psy.properties); 1158 kfree(cm->charger_psy.properties);
965 kfree(cm->charger_stat); 1159 kfree(cm->charger_stat);
966 kfree(cm->desc); 1160 kfree(cm->desc);
@@ -1000,6 +1194,8 @@ static int cm_suspend_prepare(struct device *dev)
1000 cm_suspended = true; 1194 cm_suspended = true;
1001 } 1195 }
1002 1196
1197 if (delayed_work_pending(&cm->fullbatt_vchk_work))
1198 cancel_delayed_work(&cm->fullbatt_vchk_work);
1003 cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); 1199 cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
1004 cm->status_save_batt = is_batt_present(cm); 1200 cm->status_save_batt = is_batt_present(cm);
1005 1201
@@ -1027,6 +1223,33 @@ static void cm_suspend_complete(struct device *dev)
1027 cm_rtc_set = false; 1223 cm_rtc_set = false;
1028 } 1224 }
1029 1225
1226 /* Re-enqueue delayed work (fullbatt_vchk_work) */
1227 if (cm->fullbatt_vchk_jiffies_at) {
1228 unsigned long delay = 0;
1229 unsigned long now = jiffies + CM_JIFFIES_SMALL;
1230
1231 if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
1232 delay = (unsigned long)((long)now
1233 - (long)(cm->fullbatt_vchk_jiffies_at));
1234 delay = jiffies_to_msecs(delay);
1235 } else {
1236 delay = 0;
1237 }
1238
1239 /*
1240 * Account for cm_suspend_duration_ms if
1241 * assume_timer_stops_in_suspend is active
1242 */
1243 if (g_desc && g_desc->assume_timer_stops_in_suspend) {
1244 if (delay > cm_suspend_duration_ms)
1245 delay -= cm_suspend_duration_ms;
1246 else
1247 delay = 0;
1248 }
1249
1250 queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
1251 msecs_to_jiffies(delay));
1252 }
1030 uevent_notify(cm, NULL); 1253 uevent_notify(cm, NULL);
1031} 1254}
1032 1255
@@ -1048,12 +1271,18 @@ static struct platform_driver charger_manager_driver = {
1048 1271
1049static int __init charger_manager_init(void) 1272static int __init charger_manager_init(void)
1050{ 1273{
1274 cm_wq = create_freezable_workqueue("charger_manager");
1275 INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
1276
1051 return platform_driver_register(&charger_manager_driver); 1277 return platform_driver_register(&charger_manager_driver);
1052} 1278}
1053late_initcall(charger_manager_init); 1279late_initcall(charger_manager_init);
1054 1280
1055static void __exit charger_manager_cleanup(void) 1281static void __exit charger_manager_cleanup(void)
1056{ 1282{
1283 destroy_workqueue(cm_wq);
1284 cm_wq = NULL;
1285
1057 platform_driver_unregister(&charger_manager_driver); 1286 platform_driver_unregister(&charger_manager_driver);
1058} 1287}
1059module_exit(charger_manager_cleanup); 1288module_exit(charger_manager_cleanup);