Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Chanwoo Choi and committed by
Anton Vorontsov
d829dc75 34298d40

+278 -1
+24 -1
Documentation/power/charger-manager.txt
··· 44 44 Normally, the platform will need to resume and suspend some devices 45 45 that are used by Charger Manager. 46 46 47 + * Support for premature full-battery event handling 48 + If the battery voltage drops by "fullbatt_vchkdrop_uV" after 49 + "fullbatt_vchkdrop_ms" from the full-battery event, the framework 50 + restarts charging. This check is also performed while suspended by 51 + setting wakeup time accordingly and using suspend_again. 52 + 47 53 2. Global Charger-Manager Data related with suspend_again 48 54 ======================================================== 49 55 In order to setup Charger Manager with suspend-again feature ··· 61 55 multiple instances of Charger Manager share the same charger_global_desc 62 56 and it will manage in-suspend monitoring for all instances of Charger Manager. 63 57 64 - The user needs to provide all the two entries properly in order to activate 58 + The user needs to provide all the three entries properly in order to activate 65 59 in-suspend monitoring: 66 60 67 61 struct charger_global_desc { ··· 80 74 same struct. If there is any other wakeup source triggered the 81 75 wakeup, it should return false. If the "rtc" is the only wakeup 82 76 reason, it should return true. 77 + 78 + bool assume_timer_stops_in_suspend; 79 + : if true, Charger Manager assumes that 80 + the timer (CM uses jiffies as timer) stops during suspend. Then, CM 81 + assumes that the suspend-duration is same as the alarm length. 83 82 }; 84 83 85 84 3. How to setup suspend_again ··· 122 111 CM_POLL_CHARGING_ONLY: poll this battery if and only if the 123 112 battery is being charged. 124 113 114 + unsigned int fullbatt_vchkdrop_ms; 115 + unsigned int fullbatt_vchkdrop_uV; 116 + : If both have non-zero values, Charger Manager will check the 117 + battery voltage drop fullbatt_vchkdrop_ms after the battery is fully 118 + charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger 119 + Manager will try to recharge the battery by disabling and enabling 120 + chargers. Recharge with voltage drop condition only (without delay 121 + condition) is needed to be implemented with hardware interrupts from 122 + fuel gauges or charger devices/chips. 123 + 125 124 unsigned int fullbatt_uV; 126 125 : If specified with a non-zero value, Charger Manager assumes 127 126 that the battery is full (capacity = 100) if the battery is not being ··· 143 122 this battery every polling_interval_ms or more frequently. 144 123 145 124 enum data_source battery_present; 125 + : CM_BATTERY_PRESENT: assume that the battery exists. 126 + CM_NO_BATTERY: assume that the battery does not exists. 146 127 CM_FUEL_GAUGE: get battery presence information from fuel gauge. 147 128 CM_CHARGER_STAT: get battery presence from chargers. 148 129
+229
drivers/power/charger-manager.c
··· 57 57 static bool cm_rtc_set; 58 58 static unsigned long cm_suspend_duration_ms; 59 59 60 + /* About normal (not suspended) monitoring */ 61 + static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */ 62 + static unsigned long next_polling; /* Next appointed polling time */ 63 + static struct workqueue_struct *cm_wq; /* init at driver add */ 64 + static struct delayed_work cm_monitor_work; /* init at driver add */ 65 + 60 66 /* Global charger-manager description */ 61 67 static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ 62 68 ··· 77 71 int i, ret; 78 72 79 73 switch (cm->desc->battery_present) { 74 + case CM_BATTERY_PRESENT: 75 + present = true; 76 + break; 77 + case CM_NO_BATTERY: 78 + break; 80 79 case CM_FUEL_GAUGE: 81 80 ret = cm->fuel_gauge->get_property(cm->fuel_gauge, 82 81 POWER_SUPPLY_PROP_PRESENT, &val); ··· 290 279 } 291 280 292 281 /** 282 + * try_charger_restart - Restart charging. 283 + * @cm: the Charger Manager representing the battery. 284 + * 285 + * Restart charging by turning off and on the charger. 286 + */ 287 + static int try_charger_restart(struct charger_manager *cm) 288 + { 289 + int err; 290 + 291 + if (cm->emergency_stop) 292 + return -EAGAIN; 293 + 294 + err = try_charger_enable(cm, false); 295 + if (err) 296 + return err; 297 + 298 + return try_charger_enable(cm, true); 299 + } 300 + 301 + /** 293 302 * uevent_notify - Let users know something has changed. 294 303 * @cm: the Charger Manager representing the battery. 295 304 * @event: the event string. ··· 362 331 kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); 363 332 364 333 dev_info(cm->dev, event); 334 + } 335 + 336 + /** 337 + * fullbatt_vchk - Check voltage drop some times after "FULL" event. 338 + * @work: the work_struct appointing the function 339 + * 340 + * If a user has designated "fullbatt_vchkdrop_ms/uV" values with 341 + * charger_desc, Charger Manager checks voltage drop after the battery 342 + * "FULL" event. It checks whether the voltage has dropped more than 343 + * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. 344 + */ 345 + static void fullbatt_vchk(struct work_struct *work) 346 + { 347 + struct delayed_work *dwork = to_delayed_work(work); 348 + struct charger_manager *cm = container_of(dwork, 349 + struct charger_manager, fullbatt_vchk_work); 350 + struct charger_desc *desc = cm->desc; 351 + int batt_uV, err, diff; 352 + 353 + /* remove the appointment for fullbatt_vchk */ 354 + cm->fullbatt_vchk_jiffies_at = 0; 355 + 356 + if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) 357 + return; 358 + 359 + err = get_batt_uV(cm, &batt_uV); 360 + if (err) { 361 + dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err); 362 + return; 363 + } 364 + 365 + diff = cm->fullbatt_vchk_uV; 366 + diff -= batt_uV; 367 + 368 + dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); 369 + 370 + if (diff > desc->fullbatt_vchkdrop_uV) { 371 + try_charger_restart(cm); 372 + uevent_notify(cm, "Recharge"); 373 + } 365 374 } 366 375 367 376 /** ··· 461 390 mutex_unlock(&cm_list_mtx); 462 391 463 392 return stop; 393 + } 394 + 395 + /** 396 + * _setup_polling - Setup the next instance of polling. 397 + * @work: work_struct of the function _setup_polling. 398 + */ 399 + static void _setup_polling(struct work_struct *work) 400 + { 401 + unsigned long min = ULONG_MAX; 402 + struct charger_manager *cm; 403 + bool keep_polling = false; 404 + unsigned long _next_polling; 405 + 406 + mutex_lock(&cm_list_mtx); 407 + 408 + list_for_each_entry(cm, &cm_list, entry) { 409 + if (is_polling_required(cm) && cm->desc->polling_interval_ms) { 410 + keep_polling = true; 411 + 412 + if (min > cm->desc->polling_interval_ms) 413 + min = cm->desc->polling_interval_ms; 414 + } 415 + } 416 + 417 + polling_jiffy = msecs_to_jiffies(min); 418 + if (polling_jiffy <= CM_JIFFIES_SMALL) 419 + polling_jiffy = CM_JIFFIES_SMALL + 1; 420 + 421 + if (!keep_polling) 422 + polling_jiffy = ULONG_MAX; 423 + if (polling_jiffy == ULONG_MAX) 424 + goto out; 425 + 426 + WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" 427 + ". try it later. %s\n", __func__); 428 + 429 + _next_polling = jiffies + polling_jiffy; 430 + 431 + if (!delayed_work_pending(&cm_monitor_work) || 432 + (delayed_work_pending(&cm_monitor_work) && 433 + time_after(next_polling, _next_polling))) { 434 + cancel_delayed_work_sync(&cm_monitor_work); 435 + next_polling = jiffies + polling_jiffy; 436 + queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); 437 + } 438 + 439 + out: 440 + mutex_unlock(&cm_list_mtx); 441 + } 442 + static DECLARE_WORK(setup_polling, _setup_polling); 443 + 444 + /** 445 + * cm_monitor_poller - The Monitor / Poller. 446 + * @work: work_struct of the function cm_monitor_poller 447 + * 448 + * During non-suspended state, cm_monitor_poller is used to poll and monitor 449 + * the batteries. 450 + */ 451 + static void cm_monitor_poller(struct work_struct *work) 452 + { 453 + cm_monitor(); 454 + schedule_work(&setup_polling); 464 455 } 465 456 466 457 static int charger_get_property(struct power_supply *psy, ··· 746 613 mutex_lock(&cm_list_mtx); 747 614 748 615 list_for_each_entry(cm, &cm_list, entry) { 616 + unsigned int fbchk_ms = 0; 617 + 618 + /* fullbatt_vchk is required. setup timer for that */ 619 + if (cm->fullbatt_vchk_jiffies_at) { 620 + fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at 621 + - jiffies); 622 + if (time_is_before_eq_jiffies( 623 + cm->fullbatt_vchk_jiffies_at) || 624 + msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) { 625 + fullbatt_vchk(&cm->fullbatt_vchk_work.work); 626 + fbchk_ms = 0; 627 + } 628 + } 629 + CM_MIN_VALID(wakeup_ms, fbchk_ms); 630 + 749 631 /* Skip if polling is not required for this CM */ 750 632 if (!is_polling_required(cm) && !cm->emergency_stop) 751 633 continue; ··· 820 672 return false; 821 673 } 822 674 675 + static void _cm_fbchk_in_suspend(struct charger_manager *cm) 676 + { 677 + unsigned long jiffy_now = jiffies; 678 + 679 + if (!cm->fullbatt_vchk_jiffies_at) 680 + return; 681 + 682 + if (g_desc && g_desc->assume_timer_stops_in_suspend) 683 + jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms); 684 + 685 + /* Execute now if it's going to be executed not too long after */ 686 + jiffy_now += CM_JIFFIES_SMALL; 687 + 688 + if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at)) 689 + fullbatt_vchk(&cm->fullbatt_vchk_work.work); 690 + } 691 + 823 692 /** 824 693 * cm_suspend_again - Determine whether suspend again or not 825 694 * ··· 858 693 ret = true; 859 694 mutex_lock(&cm_list_mtx); 860 695 list_for_each_entry(cm, &cm_list, entry) { 696 + _cm_fbchk_in_suspend(cm); 697 + 861 698 if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) || 862 699 cm->status_save_batt != is_batt_present(cm)) { 863 700 ret = false; ··· 962 795 } 963 796 memcpy(cm->desc, desc, sizeof(struct charger_desc)); 964 797 cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */ 798 + 799 + /* 800 + * The following two do not need to be errors. 801 + * Users may intentionally ignore those two features. 802 + */ 803 + if (desc->fullbatt_uV == 0) { 804 + dev_info(&pdev->dev, "Ignoring full-battery voltage threshold" 805 + " as it is not supplied."); 806 + } 807 + if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { 808 + dev_info(&pdev->dev, "Disabling full-battery voltage drop " 809 + "checking mechanism as it is not supplied."); 810 + desc->fullbatt_vchkdrop_ms = 0; 811 + desc->fullbatt_vchkdrop_uV = 0; 812 + } 965 813 966 814 if (!desc->charger_regulators || desc->num_charger_regulators < 1) { 967 815 ret = -EINVAL; ··· 1085 903 cm->charger_psy.num_properties++; 1086 904 } 1087 905 906 + INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); 907 + 1088 908 ret = power_supply_register(NULL, &cm->charger_psy); 1089 909 if (ret) { 1090 910 dev_err(&pdev->dev, "Cannot register charger-manager with" ··· 1111 927 mutex_lock(&cm_list_mtx); 1112 928 list_add(&cm->entry, &cm_list); 1113 929 mutex_unlock(&cm_list_mtx); 930 + 931 + schedule_work(&setup_polling); 1114 932 1115 933 return 0; 1116 934 ··· 1144 958 list_del(&cm->entry); 1145 959 mutex_unlock(&cm_list_mtx); 1146 960 961 + if (work_pending(&setup_polling)) 962 + cancel_work_sync(&setup_polling); 963 + if (delayed_work_pending(&cm_monitor_work)) 964 + cancel_delayed_work_sync(&cm_monitor_work); 965 + 1147 966 regulator_bulk_free(desc->num_charger_regulators, 1148 967 desc->charger_regulators); 1149 968 power_supply_unregister(&cm->charger_psy); 969 + 970 + try_charger_enable(cm, false); 971 + 1150 972 kfree(cm->charger_psy.properties); 1151 973 kfree(cm->charger_stat); 1152 974 kfree(cm->desc); ··· 1194 1000 cm_suspended = true; 1195 1001 } 1196 1002 1003 + if (delayed_work_pending(&cm->fullbatt_vchk_work)) 1004 + cancel_delayed_work(&cm->fullbatt_vchk_work); 1197 1005 cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); 1198 1006 cm->status_save_batt = is_batt_present(cm); 1199 1007 ··· 1223 1027 cm_rtc_set = false; 1224 1028 } 1225 1029 1030 + /* Re-enqueue delayed work (fullbatt_vchk_work) */ 1031 + if (cm->fullbatt_vchk_jiffies_at) { 1032 + unsigned long delay = 0; 1033 + unsigned long now = jiffies + CM_JIFFIES_SMALL; 1034 + 1035 + if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) { 1036 + delay = (unsigned long)((long)now 1037 + - (long)(cm->fullbatt_vchk_jiffies_at)); 1038 + delay = jiffies_to_msecs(delay); 1039 + } else { 1040 + delay = 0; 1041 + } 1042 + 1043 + /* 1044 + * Account for cm_suspend_duration_ms if 1045 + * assume_timer_stops_in_suspend is active 1046 + */ 1047 + if (g_desc && g_desc->assume_timer_stops_in_suspend) { 1048 + if (delay > cm_suspend_duration_ms) 1049 + delay -= cm_suspend_duration_ms; 1050 + else 1051 + delay = 0; 1052 + } 1053 + 1054 + queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, 1055 + msecs_to_jiffies(delay)); 1056 + } 1226 1057 uevent_notify(cm, NULL); 1227 1058 } 1228 1059 ··· 1271 1048 1272 1049 static int __init charger_manager_init(void) 1273 1050 { 1051 + cm_wq = create_freezable_workqueue("charger_manager"); 1052 + INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); 1053 + 1274 1054 return platform_driver_register(&charger_manager_driver); 1275 1055 } 1276 1056 late_initcall(charger_manager_init); 1277 1057 1278 1058 static void __exit charger_manager_cleanup(void) 1279 1059 { 1060 + destroy_workqueue(cm_wq); 1061 + cm_wq = NULL; 1062 + 1280 1063 platform_driver_unregister(&charger_manager_driver); 1281 1064 } 1282 1065 module_exit(charger_manager_cleanup);
+25
include/linux/power/charger-manager.h
··· 18 18 #include <linux/power_supply.h> 19 19 20 20 enum data_source { 21 + CM_BATTERY_PRESENT, 22 + CM_NO_BATTERY, 21 23 CM_FUEL_GAUGE, 22 24 CM_CHARGER_STAT, 23 25 }; ··· 40 38 * rtc_only_wakeup() returning false. 41 39 * If the RTC given to CM is the only wakeup reason, 42 40 * rtc_only_wakeup should return true. 41 + * @assume_timer_stops_in_suspend: 42 + * Assume that the jiffy timer stops in suspend-to-RAM. 43 + * When enabled, CM does not rely on jiffies value in 44 + * suspend_again and assumes that jiffies value does not 45 + * change during suspend. 43 46 */ 44 47 struct charger_global_desc { 45 48 char *rtc_name; 46 49 47 50 bool (*rtc_only_wakeup)(void); 51 + 52 + bool assume_timer_stops_in_suspend; 48 53 }; 49 54 50 55 /** ··· 59 50 * @psy_name: the name of power-supply-class for charger manager 60 51 * @polling_mode: 61 52 * Determine which polling mode will be used 53 + * @fullbatt_vchkdrop_ms: 54 + * @fullbatt_vchkdrop_uV: 55 + * Check voltage drop after the battery is fully charged. 56 + * If it has dropped more than fullbatt_vchkdrop_uV after 57 + * fullbatt_vchkdrop_ms, CM will restart charging. 62 58 * @fullbatt_uV: voltage in microvolt 63 59 * If it is not being charged and VBATT >= fullbatt_uV, 64 60 * it is assumed to be full. ··· 90 76 enum polling_modes polling_mode; 91 77 unsigned int polling_interval_ms; 92 78 79 + unsigned int fullbatt_vchkdrop_ms; 80 + unsigned int fullbatt_vchkdrop_uV; 93 81 unsigned int fullbatt_uV; 94 82 95 83 enum data_source battery_present; ··· 117 101 * @fuel_gauge: power_supply for fuel gauge 118 102 * @charger_stat: array of power_supply for chargers 119 103 * @charger_enabled: the state of charger 104 + * @fullbatt_vchk_jiffies_at: 105 + * jiffies at the time full battery check will occur. 106 + * @fullbatt_vchk_uV: voltage in microvolt 107 + * criteria for full battery 108 + * @fullbatt_vchk_work: work queue for full battery check 120 109 * @emergency_stop: 121 110 * When setting true, stop charging 122 111 * @last_temp_mC: the measured temperature in milli-Celsius ··· 141 120 struct power_supply **charger_stat; 142 121 143 122 bool charger_enabled; 123 + 124 + unsigned long fullbatt_vchk_jiffies_at; 125 + unsigned int fullbatt_vchk_uV; 126 + struct delayed_work fullbatt_vchk_work; 144 127 145 128 int emergency_stop; 146 129 int last_temp_mC;