diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2012-05-05 09:24:10 -0400 |
---|---|---|
committer | Anton Vorontsov <anton.vorontsov@linaro.org> | 2012-05-05 22:48:50 -0400 |
commit | d829dc75bafb10754f35fb8895e5143d20267b04 (patch) | |
tree | 4cff2aa07dcf7c15ef931e3b9ab20a2d84fcf68d | |
parent | 34298d40e5853bc195c9db012fc1ddccac9b6f7f (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>
-rw-r--r-- | Documentation/power/charger-manager.txt | 25 | ||||
-rw-r--r-- | drivers/power/charger-manager.c | 229 | ||||
-rw-r--r-- | include/linux/power/charger-manager.h | 25 |
3 files changed, 278 insertions, 1 deletions
diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt index fdcca991df30..9b3863386e54 100644 --- a/Documentation/power/charger-manager.txt +++ b/Documentation/power/charger-manager.txt | |||
@@ -44,6 +44,12 @@ Charger Manager supports the following: | |||
44 | Normally, the platform will need to resume and suspend some devices | 44 | Normally, the platform will need to resume and suspend some devices |
45 | that are used by Charger Manager. | 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 | 2. Global Charger-Manager Data related with suspend_again | 53 | 2. Global Charger-Manager Data related with suspend_again |
48 | ======================================================== | 54 | ======================================================== |
49 | In order to setup Charger Manager with suspend-again feature | 55 | In order to setup Charger Manager with suspend-again feature |
@@ -55,7 +61,7 @@ if there are multiple batteries. If there are multiple batteries, the | |||
55 | multiple instances of Charger Manager share the same charger_global_desc | 61 | multiple instances of Charger Manager share the same charger_global_desc |
56 | and it will manage in-suspend monitoring for all instances of Charger Manager. | 62 | and it will manage in-suspend monitoring for all instances of Charger Manager. |
57 | 63 | ||
58 | The user needs to provide all the two entries properly in order to activate | 64 | The user needs to provide all the three entries properly in order to activate |
59 | in-suspend monitoring: | 65 | in-suspend monitoring: |
60 | 66 | ||
61 | struct charger_global_desc { | 67 | struct charger_global_desc { |
@@ -74,6 +80,11 @@ bool (*rtc_only_wakeup)(void); | |||
74 | same struct. If there is any other wakeup source triggered the | 80 | same struct. If there is any other wakeup source triggered the |
75 | wakeup, it should return false. If the "rtc" is the only wakeup | 81 | wakeup, it should return false. If the "rtc" is the only wakeup |
76 | reason, it should return true. | 82 | reason, it should return true. |
83 | |||
84 | bool assume_timer_stops_in_suspend; | ||
85 | : if true, Charger Manager assumes that | ||
86 | the timer (CM uses jiffies as timer) stops during suspend. Then, CM | ||
87 | assumes that the suspend-duration is same as the alarm length. | ||
77 | }; | 88 | }; |
78 | 89 | ||
79 | 3. How to setup suspend_again | 90 | 3. How to setup suspend_again |
@@ -111,6 +122,16 @@ enum polling_modes polling_mode; | |||
111 | CM_POLL_CHARGING_ONLY: poll this battery if and only if the | 122 | CM_POLL_CHARGING_ONLY: poll this battery if and only if the |
112 | battery is being charged. | 123 | battery is being charged. |
113 | 124 | ||
125 | unsigned int fullbatt_vchkdrop_ms; | ||
126 | unsigned int fullbatt_vchkdrop_uV; | ||
127 | : If both have non-zero values, Charger Manager will check the | ||
128 | battery voltage drop fullbatt_vchkdrop_ms after the battery is fully | ||
129 | charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger | ||
130 | Manager will try to recharge the battery by disabling and enabling | ||
131 | chargers. Recharge with voltage drop condition only (without delay | ||
132 | condition) is needed to be implemented with hardware interrupts from | ||
133 | fuel gauges or charger devices/chips. | ||
134 | |||
114 | unsigned int fullbatt_uV; | 135 | unsigned int fullbatt_uV; |
115 | : If specified with a non-zero value, Charger Manager assumes | 136 | : If specified with a non-zero value, Charger Manager assumes |
116 | that the battery is full (capacity = 100) if the battery is not being | 137 | that the battery is full (capacity = 100) if the battery is not being |
@@ -122,6 +143,8 @@ unsigned int polling_interval_ms; | |||
122 | this battery every polling_interval_ms or more frequently. | 143 | this battery every polling_interval_ms or more frequently. |
123 | 144 | ||
124 | enum data_source battery_present; | 145 | enum data_source battery_present; |
146 | : CM_BATTERY_PRESENT: assume that the battery exists. | ||
147 | CM_NO_BATTERY: assume that the battery does not exists. | ||
125 | CM_FUEL_GAUGE: get battery presence information from fuel gauge. | 148 | CM_FUEL_GAUGE: get battery presence information from fuel gauge. |
126 | CM_CHARGER_STAT: get battery presence from chargers. | 149 | CM_CHARGER_STAT: get battery presence from chargers. |
127 | 150 | ||
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; | |||
57 | static bool cm_rtc_set; | 57 | static bool cm_rtc_set; |
58 | static unsigned long cm_suspend_duration_ms; | 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 | /* Global charger-manager description */ | 66 | /* Global charger-manager description */ |
61 | static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ | 67 | static 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 | */ | ||
298 | static 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 | */ | ||
376 | static 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 | */ | ||
470 | static 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 | |||
510 | out: | ||
511 | mutex_unlock(&cm_list_mtx); | ||
512 | } | ||
513 | static 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 | */ | ||
522 | static void cm_monitor_poller(struct work_struct *work) | ||
523 | { | ||
524 | cm_monitor(); | ||
525 | schedule_work(&setup_polling); | ||
526 | } | ||
527 | |||
395 | static int charger_get_property(struct power_supply *psy, | 528 | static 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 | ||
823 | static 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 | ||
933 | err_chg_enable: | 1119 | err_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 | ||
1049 | static int __init charger_manager_init(void) | 1272 | static 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 | } |
1053 | late_initcall(charger_manager_init); | 1279 | late_initcall(charger_manager_init); |
1054 | 1280 | ||
1055 | static void __exit charger_manager_cleanup(void) | 1281 | static 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 | } |
1059 | module_exit(charger_manager_cleanup); | 1288 | module_exit(charger_manager_cleanup); |
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index 4f75e531c112..baa299a95e13 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/power_supply.h> | 18 | #include <linux/power_supply.h> |
19 | 19 | ||
20 | enum data_source { | 20 | enum data_source { |
21 | CM_BATTERY_PRESENT, | ||
22 | CM_NO_BATTERY, | ||
21 | CM_FUEL_GAUGE, | 23 | CM_FUEL_GAUGE, |
22 | CM_CHARGER_STAT, | 24 | CM_CHARGER_STAT, |
23 | }; | 25 | }; |
@@ -38,11 +40,18 @@ enum polling_modes { | |||
38 | * rtc_only_wakeup() returning false. | 40 | * rtc_only_wakeup() returning false. |
39 | * If the RTC given to CM is the only wakeup reason, | 41 | * If the RTC given to CM is the only wakeup reason, |
40 | * rtc_only_wakeup should return true. | 42 | * rtc_only_wakeup should return true. |
43 | * @assume_timer_stops_in_suspend: | ||
44 | * Assume that the jiffy timer stops in suspend-to-RAM. | ||
45 | * When enabled, CM does not rely on jiffies value in | ||
46 | * suspend_again and assumes that jiffies value does not | ||
47 | * change during suspend. | ||
41 | */ | 48 | */ |
42 | struct charger_global_desc { | 49 | struct charger_global_desc { |
43 | char *rtc_name; | 50 | char *rtc_name; |
44 | 51 | ||
45 | bool (*rtc_only_wakeup)(void); | 52 | bool (*rtc_only_wakeup)(void); |
53 | |||
54 | bool assume_timer_stops_in_suspend; | ||
46 | }; | 55 | }; |
47 | 56 | ||
48 | /** | 57 | /** |
@@ -50,6 +59,11 @@ struct charger_global_desc { | |||
50 | * @psy_name: the name of power-supply-class for charger manager | 59 | * @psy_name: the name of power-supply-class for charger manager |
51 | * @polling_mode: | 60 | * @polling_mode: |
52 | * Determine which polling mode will be used | 61 | * Determine which polling mode will be used |
62 | * @fullbatt_vchkdrop_ms: | ||
63 | * @fullbatt_vchkdrop_uV: | ||
64 | * Check voltage drop after the battery is fully charged. | ||
65 | * If it has dropped more than fullbatt_vchkdrop_uV after | ||
66 | * fullbatt_vchkdrop_ms, CM will restart charging. | ||
53 | * @fullbatt_uV: voltage in microvolt | 67 | * @fullbatt_uV: voltage in microvolt |
54 | * If it is not being charged and VBATT >= fullbatt_uV, | 68 | * If it is not being charged and VBATT >= fullbatt_uV, |
55 | * it is assumed to be full. | 69 | * it is assumed to be full. |
@@ -76,6 +90,8 @@ struct charger_desc { | |||
76 | enum polling_modes polling_mode; | 90 | enum polling_modes polling_mode; |
77 | unsigned int polling_interval_ms; | 91 | unsigned int polling_interval_ms; |
78 | 92 | ||
93 | unsigned int fullbatt_vchkdrop_ms; | ||
94 | unsigned int fullbatt_vchkdrop_uV; | ||
79 | unsigned int fullbatt_uV; | 95 | unsigned int fullbatt_uV; |
80 | 96 | ||
81 | enum data_source battery_present; | 97 | enum data_source battery_present; |
@@ -101,6 +117,11 @@ struct charger_desc { | |||
101 | * @fuel_gauge: power_supply for fuel gauge | 117 | * @fuel_gauge: power_supply for fuel gauge |
102 | * @charger_stat: array of power_supply for chargers | 118 | * @charger_stat: array of power_supply for chargers |
103 | * @charger_enabled: the state of charger | 119 | * @charger_enabled: the state of charger |
120 | * @fullbatt_vchk_jiffies_at: | ||
121 | * jiffies at the time full battery check will occur. | ||
122 | * @fullbatt_vchk_uV: voltage in microvolt | ||
123 | * criteria for full battery | ||
124 | * @fullbatt_vchk_work: work queue for full battery check | ||
104 | * @emergency_stop: | 125 | * @emergency_stop: |
105 | * When setting true, stop charging | 126 | * When setting true, stop charging |
106 | * @last_temp_mC: the measured temperature in milli-Celsius | 127 | * @last_temp_mC: the measured temperature in milli-Celsius |
@@ -121,6 +142,10 @@ struct charger_manager { | |||
121 | 142 | ||
122 | bool charger_enabled; | 143 | bool charger_enabled; |
123 | 144 | ||
145 | unsigned long fullbatt_vchk_jiffies_at; | ||
146 | unsigned int fullbatt_vchk_uV; | ||
147 | struct delayed_work fullbatt_vchk_work; | ||
148 | |||
124 | int emergency_stop; | 149 | int emergency_stop; |
125 | int last_temp_mC; | 150 | int last_temp_mC; |
126 | 151 | ||