power_supply: Charger-Manager: poll battery health in normal state
Hi Rafael,
As Mr. Kim Donggeun leaved the company as personal reason, I reply it about your comment based on following link because I haven't been included in mailing list.
- https://lkml.org/lkml/2012/2/29/506
Quoted 9 lines · Rafael J. Wysocki
Quoted 8 lines · Donggeun Kim
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.
Quoted 6 lines
Quoted 6 lines
Signed-off-by MyungJoo Ham <[email protected]>Signed-off-by Donggeun Kim <[email protected]>Signed-off-by Kyungmin Park <[email protected]>--- Documentation/power/charger-manager.txt | 25 ++++- drivers/power/charger-manager.c | 230
+++++++++++++++++++++++++++++++
Quoted 4 lines
Review
Quoted 4 lines
include/linux/power/charger-manager.h | 25 ++++ 3 files changed, 279 insertions(+), 1 deletions(-)(quoted diff) +0
-0
b/Documentation/power/charger-manager.txt
Quoted 24 lines
Quoted 24 lines
index fdcca99..47f7fdc 100644
Documentation/power/charger-manager.txt +7
-1@@ -44,6 +44,12 @@ Charger Manager supports the following: Normally, the platform will need to resume and suspend some devices that are used by Charger Manager. +* Support for premature full-battery event handling + If the battery voltage drops by "fullbatt_vchkdrop_uV" after + "fullbatt_vchkdrop_ms" from the full-battery event, the framework + restarts charging. This check is also performed while suspended by + setting wakeup time accordingly and using suspend_again. + 2. Global Charger-Manager Data related with suspend_again ======================================================== 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 multiple instances of Charger Manager share the same charger_global_desc and it will manage in-suspend monitoring for all instances of Charger Manager. -The user needs to provide all the two entries properly in order to activate +The user needs to provide all the three entries properly in order to activate in-suspend monitoring:
Quoted 15 lines
Documentation/power/charger-manager.txt bool (*rtc_only_wakeup)(void); · line 87
Quoted 10 lines
Documentation/power/charger-manager.txt +5
-0struct charger_global_desc {@@ -74,6 +80,11 @@ bool (*rtc_only_wakeup)(void); same struct. If there is any other wakeup source triggered the wakeup, it should return false. If the "rtc" is the only wakeup reason, it should return true. + +bool assume_timer_stops_in_suspend; + : if true, Charger Manager assumes that + the timer (CM uses jiffies as timer) stops during suspend. Then, CM + assumes that the suspend-duration is same as the alarm length.The description here is not 100% clear to me. Is it supposed to mean that if
assume_timer_stops_in_suspendis set, the charger manager will assume that there won't be timer events while suspended?
I think that jiffy timer event isn't happened after entered suspended state. Please let me know about your opinion.
Quoted 23 lines
Quoted 23 lines
Documentation/power/charger-manager.txt +10
-0}; 3. How to setup suspend_again@@ -111,6 +122,16 @@ enum polling_modes polling_mode; CM_POLL_CHARGING_ONLY: poll this battery if and only if the battery is being charged. +unsigned int fullbatt_vchkdrop_ms; +unsigned int fullbatt_vchkdrop_uV; + : If both have non-zero values, Charger Manager will check the + battery voltage drop fullbatt_vchkdrop_ms after the battery is fully + charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger + Manager will try to recharge the battery by disabling and enabling + chargers. Recharge with voltage drop condition only (without delay + condition) is needed to be implemented with hardware interrupts from + fuel gauges or charger devices/chips. + unsigned int fullbatt_uV; : If specified with a non-zero value, Charger Manager assumes that the battery is full (capacity = 100) if the battery is not being@@ -122,6 +143,8 @@ unsigned int polling_interval_ms; this battery every polling_interval_ms or more frequently.
Quoted 4 lines
Documentation/power/charger-manager.txt unsigned int polling_interval_ms; · line 146
Quoted 2 lines
Documentation/power/charger-manager.txt +1
-0enum data_source battery_present; + : CM_ASSUME_ALWAYS_TRUE: assume that the battery exists.I'd call that
CM_BATTERY_PRESENT
OK, I will modify it.
Quoted 4 lines
Documentation/power/charger-manager.txt unsigned int polling_interval_ms; · line 147
Quoted 2 lines
Documentation/power/charger-manager.txt +1
-0+ CM_ASSUME_ALWAYS_FALSE: assume that the battery does not exists.And that
CM_NO_BATTERY
OK.
Quoted 24 lines
Quoted 24 lines
Documentation/power/charger-manager.txt +0
-0CM_FUEL_GAUGE: get battery presence information from fuel gauge. CM_CHARGER_STAT: get battery presence from chargers.drivers/power/charger-manager.c +6
-0@@ -57,6 +57,12 @@ static bool cm_suspended;static bool cm_rtc_set;static unsigned long cm_suspend_duration_ms; +/* About normal (not suspended) monitoring */ +static unsigned long polling_jiffy =ULONG_MAX ;/* ULONG_MAX: no polling */ +static unsigned long next_polling;/* Next appointed polling time */ +static struct workqueue_struct *cm_wq;/* init at driver add */ +static struct delayed_work cm_monitor_work;/* init at driver add */ +/* Global charger-manager description */ static struct charger_global_desc *g_desc;/* init with setup_charger_manager */ @@ -71,6 +77,12 @@ static bool is_batt_present(struct charger_manager *cm)int i, ret;
Quoted 9 lines
drivers/power/charger-manager.c static bool is_batt_present(struct charger_manager *cm) · line 84
Quoted 6 lines
drivers/power/charger-manager.c +5
-0switch (cm->desc->battery_present) { +case CM_ASSUME_ALWAYS_TRUE : + present = true; +break ; +case CM_ASSUME_ALWAYS_FALSE : + present = false;You don't have to do preset = false here, because it's false already.
OK, I will modify it.
Quoted 34 lines
Quoted 34 lines
drivers/power/charger-manager.c +21
-0+break ;case CM_FUEL_GAUGE : ret = cm->fuel_gauge->get_property (cm->fuel_gauge,POWER_SUPPLY_PROP_PRESENT , &val);@@ -282,6 +294,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) }/** +* try_charger_restart - Restart charging. +* @cm: the Charger Manager representing the battery. +* +* Restart charging by turning off and on the charger. +*/ +static int try_charger_restart (struct charger_manager *cm) +{ +int err; + +if (cm->emergency_stop) +return -EAGAIN ; + + err =try_charger_enable (cm, false); +if (err) +return err; + +return try_charger_enable (cm, true); +} + +/** * uevent_notify - Let users know something has changed. * @cm: the Charger Manager representing the battery. * @event: the event string. @@ -339,6 +371,47 @@ static void uevent_notify(struct charger_manager *cm, const char *event) }
Quoted 15 lines
drivers/power/charger-manager.c static void uevent_notify(struct charger_manager *cm, const char *event) · line 385
Quoted 13 lines
drivers/power/charger-manager.c +12
-0/** +* fullbatt_vchk - Check voltage drop some times after "FULL" event. +* @work: the work_struct appointing the function +* +* If a user has designated "fullbatt_vchkdrop_ms/uV" values with +* charger_desc, Charger Manager checks voltage drop after the battery +* "FULL" event. It checks whether the voltage has dropped more than +* fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. +*/ +static void fullbatt_vchk (struct work_struct *work) +{ +struct delayed_work *dwork = + container_of(work, struct delayed_work, work);There's the
to_delayed_work()function for that.
OK, I will modify it.
Quoted 71 lines
Quoted 71 lines
drivers/power/charger-manager.c +63
-0+struct charger_manager *cm =container_of (dwork, + struct charger_manager, fullbatt_vchk_work); +struct charger_desc *desc = cm->desc; +int batt_uV, err, diff; + +/* remove the appointment for fullbatt_vchk */ + cm->fullbatt_vchk_jiffies_at =0 ; + +if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) +return ; + + err =get_batt_uV (cm, &batt_uV); +if (err) { +dev_err (cm->dev,"%s: get_batt_uV error(%d).\n" , __func__, err); +return ; + } + + diff = cm->fullbatt_vchk_uV; + diff -= batt_uV; + +dev_dbg (cm->dev,"VBATT dropped %duV after full-batt.\n" , diff); + +if (diff > desc->fullbatt_vchkdrop_uV) { +try_charger_restart (cm); +uevent_notify (cm,"Recharge" ); + } +} + +/** * _cm_monitor - Monitor the temperature and return true for exceptions. * @cm: the Charger Manager representing the battery. * @@ -395,6 +468,68 @@ static bool cm_monitor(void)return stop; } +/** +* _setup_polling - Setup the next instance of polling. +* @work: work_struct of the function _setup_polling. +*/ +static void _setup_polling (struct work_struct *work) +{ +unsigned long min =ULONG_MAX ; +struct charger_manager *cm; +bool keep_polling = false; +unsigned long _next_polling; + +mutex_lock (&cm_list_mtx); + +list_for_each_entry (cm, &cm_list, entry) { +if (is_polling_required (cm) && cm->desc->polling_interval_ms) { + keep_polling = true; + +if (min > cm->desc->polling_interval_ms) + min = cm->desc->polling_interval_ms; + } + } + + polling_jiffy =msecs_to_jiffies (min); +if (polling_jiffy <=CM_JIFFIES_SMALL ) + polling_jiffy =CM_JIFFIES_SMALL +1 ; + +if (!keep_polling) + polling_jiffy =ULONG_MAX ; +if (polling_jiffy ==ULONG_MAX ) + goto out; + +WARN (cm_wq ==NULL ,"charger-manager: workqueue not initialized" +". try it later. %s\n" , __func__); +
Quoted 8 lines
drivers/power/charger-manager.c static bool cm_monitor(void) · line 510
Quoted 6 lines
drivers/power/charger-manager.c +6
-0+ _next_polling = jiffies + polling_jiffy; + +if (!delayed_work_pending (&cm_monitor_work) || + (delayed_work_pending (&cm_monitor_work) && +time_after (next_polling, _next_polling))) { +cancel_delayed_work (&cm_monitor_work);Shouldn't that be
cancel_delayes_work_sync()?
You're right. When cm_monitor_work isn't pending state, use cancel_delayed_work_sync() to cancel 'cm_monitor_work' with running state for waiting completion of callback of 'cm_monitor_work'.
Quoted 29 lines
Quoted 29 lines
drivers/power/charger-manager.c +22
-0+ next_polling = jiffies + polling_jiffy; +queue_delayed_work (cm_wq, &cm_monitor_work, polling_jiffy); + } + +out: +mutex_unlock (&cm_list_mtx); +} +static DECLARE_WORK(setup_polling, _setup_polling); + +/** +* cm_monitor_poller - The Monitor / Poller. +* @work: work_struct of the function cm_monitor_poller +* +* During non-suspended state, cm_monitor_poller is used to poll and monitor +* the batteries. +*/ +static void cm_monitor_poller (struct work_struct *work) +{ +cm_monitor (); +schedule_work (&setup_polling); +} +static int charger_get_property (struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)@@ -616,6 +751,20 @@ static bool cm_setup_timer(void)mutex_lock (&cm_list_mtx);
Quoted 10 lines
drivers/power/charger-manager.c static bool cm_setup_timer(void) · line 760
Quoted 8 lines
drivers/power/charger-manager.c +7
-0list_for_each_entry (cm, &cm_list, entry) { +unsigned int fbchk_ms =0 ; + +/* fullbatt_vchk is required. setup timer for that */ +if (cm->fullbatt_vchk_jiffies_at) { + fbchk_ms =jiffies_to_msecs (cm->fullbatt_vchk_jiffies_at + - jiffies); + if (cm->fullbatt_vchk_jiffies_at <= jiffies
time_before_eq_jiffies()?
OK, I will modify it.
Quoted 16 lines
Quoted 16 lines
||
drivers/power/charger-manager.c +7
-0+ msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) { +fullbatt_vchk (&cm->fullbatt_vchk_work.work); + fbchk_ms =0 ; + } + } +CM_MIN_VALID (wakeup_ms, fbchk_ms); +/* Skip if polling is not required for this CM */ if (!is_polling_required (cm) && !cm->emergency_stop)continue ;@@ -675,6 +824,27 @@ static bool cm_setup_timer(void)return false; }
Quoted 3 lines
drivers/power/charger-manager.c static bool cm_setup_timer(void) · line 827
Quoted 1 line
drivers/power/charger-manager.c +1
-0+static bool _cm_fbchk_in_suspend (struct charger_manager *cm)Why do you need it to return bool?
OK, I will use 'void' type instead of 'bool' type.
Quoted 92 lines
Quoted 92 lines
drivers/power/charger-manager.c +45
-0+{ +unsigned long jiffy_now = jiffies; + +if (!cm->fullbatt_vchk_jiffies_at) +return false; + +if (g_desc && g_desc->assume_timer_stops_in_suspend) + jiffy_now +=msecs_to_jiffies (cm_suspend_duration_ms); + +/* Execute now if it's going to be executed not too long after */ + jiffy_now +=CM_JIFFIES_SMALL ; + +if (time_after_eq (jiffy_now, cm->fullbatt_vchk_jiffies_at)) { +fullbatt_vchk (&cm->fullbatt_vchk_work.work); +return true; + } + +return false; +} +/** * cm_suspend_again - Determine whether suspend again or not * @@ -696,6 +866,8 @@ bool cm_suspend_again(void) ret = true;mutex_lock (&cm_list_mtx);list_for_each_entry (cm, &cm_list, entry) { +_cm_fbchk_in_suspend (cm); +if (cm->status_save_ext_pwr_inserted !=is_ext_pwr_online (cm) || cm->status_save_batt !=is_batt_present (cm)) ret = false;@@ -797,6 +969,21 @@ static int charger_manager_probe(struct platform_device *pdev)memcpy (cm->desc, desc,sizeof (struct charger_desc )); cm->last_temp_mC =INT_MIN ;/* denotes "unmeasured, yet" */ +/* +* The following two do not need to be errors. +* Users may intentionally ignore those two features. +*/ +if (desc->fullbatt_uV ==0 ) { +dev_info (&pdev->dev,"Ignoring full-battery voltage threshold" +" as it is not supplied." ); + } +if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { +dev_info (&pdev->dev,"Disabling full-battery voltage drop " +"checking mechanism as it is not supplied." ); + desc->fullbatt_vchkdrop_ms =0 ; + desc->fullbatt_vchkdrop_uV =0 ; + } +if (!desc->charger_regulators || desc->num_charger_regulators <1 ) { ret = -EINVAL ;dev_err (&pdev->dev,"charger_regulators undefined.\n" );@@ -905,6 +1092,8 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy.num_properties++; } +INIT_DELAYED_WORK (&cm->fullbatt_vchk_work, fullbatt_vchk); + ret =power_supply_register (NULL , &cm->charger_psy);if (ret) { dev_err(&pdev->dev, "Cannot register charger-manager with"@@ -930,6 +1119,8 @@ static int charger_manager_probe(struct platform_device *pdev)list_add (&cm->entry, &cm_list);mutex_unlock (&cm_list_mtx); +schedule_work (&setup_polling); +return 0 ; err_chg_enable:@@ -961,10 +1152,14 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)list_del (&cm->entry);mutex_unlock (&cm_list_mtx); +schedule_work (&setup_polling); +if (desc->charger_regulators)regulator_bulk_free (desc->num_charger_regulators, desc->charger_regulators); +try_charger_enable (cm, false); +power_supply_unregister (&cm->charger_psy);kfree (cm->charger_psy.properties);kfree (cm->charger_stat);@@ -1007,6 +1202,7 @@ static int cm_suspend_prepare(struct device *dev) cm_suspended = true; }
Quoted 3 lines
drivers/power/charger-manager.c static int cm_suspend_prepare(struct device *dev) · line 1205
Quoted 1 line
drivers/power/charger-manager.c +1
-0+cancel_delayed_work (&cm->fullbatt_vchk_work);Why do you need to cancel it here?
cm->fullbatt_vchk_work check the health state of battery in normal state, so cm->fullbatt_vchk_work should be canceled before entering suspend. I will modify below code:
if (delayed_work_pending(&cm->fullbatt_vchk_work)
cancel_delayed_work(&cm->fullbatt_vchk_work);
Quoted 17 lines
drivers/power/charger-manager.c static void cm_suspend_complete(struct device *dev) · line 1238
Quoted 12 lines
drivers/power/charger-manager.c +4
-0cm->status_save_ext_pwr_inserted =is_ext_pwr_online (cm); cm->status_save_batt =is_batt_present (cm);@@ -1036,6 +1232,34 @@ static void cm_suspend_complete(struct device *dev) cm_rtc_set = false; } +/* Re-enqueue delayed work (fullbatt_vchk_work) */ +if (cm->fullbatt_vchk_jiffies_at) { +unsigned long delay =0 ; +unsigned long now = jiffies;I'd do
(quoted diff) +1
-0+ unsigned long now = jiffies + CM_JIFFIES_SMALL;
Quoted 2 lines
which would simplify the code below.
OK, I will modify it.
Quoted 9 lines
drivers/power/charger-manager.c static void cm_suspend_complete(struct device *dev) · line 1243
Quoted 5 lines
drivers/power/charger-manager.c +5
-0+ +if (time_after_eq (now +CM_JIFFIES_SMALL , + cm->fullbatt_vchk_jiffies_at)) { + delay = (unsigned long )((long )(now +CM_JIFFIES_SMALL ) + - (long )(cm->fullbatt_vchk_jiffies_at));I don't really think that handles the possible overflow correctly (if you get a negative result and then covert it to unsigned long, that still will produce a big positive number).
I think that now value is larger than cm->fullbatt_vchk_jiffies_at, so delay value is always positive value because of checking difference of between now and cm->fullbatt_vchk_jiffies_at before calculating delay value.
Quoted 111 lines
Quoted 111 lines
drivers/power/charger-manager.c +25
-0+ delay =jiffies_to_msecs (delay); + }else { + delay =0 ; + } + +/* +* Account for cm_suspend_duration_ms if +* assume_timer_stops_in_suspend is active +*/ +if (g_desc && g_desc->assume_timer_stops_in_suspend) { +if (delay > cm_suspend_duration_ms) + delay -= cm_suspend_duration_ms; +else + delay =0 ; + } + +queue_delayed_work (cm_wq, &cm->fullbatt_vchk_work, +msecs_to_jiffies (delay)); + }uevent_notify (cm,NULL ); }@@ -1057,12 +1281,18 @@ static struct platform_driver charger_manager_driver = {static int __init charger_manager_init (void ) { + cm_wq =create_freezable_workqueue ("charger_manager" ); +INIT_DELAYED_WORK (&cm_monitor_work, cm_monitor_poller); +return platform_driver_register (&charger_manager_driver); }late_initcall (charger_manager_init); static void __exit charger_manager_cleanup(void) { +destroy_workqueue (cm_wq); + cm_wq =NULL ; +platform_driver_unregister (&charger_manager_driver); }module_exit (charger_manager_cleanup);include/linux/power/charger-manager.h +21
-0@@ -18,6 +18,8 @@#include <linux/power_supply.h> enum data_source { +CM_ASSUME_ALWAYS_TRUE , +CM_ASSUME_ALWAYS_FALSE ,CM_FUEL_GAUGE ,CM_CHARGER_STAT , };@@ -38,11 +40,18 @@ enum polling_modes {* rtc_only_wakeup() returning false. * If the RTC given to CM is the only wakeup reason, * rtc_only_wakeup should return true. +* @assume_timer_stops_in_suspend: +* Assume that the jiffy timer stops in suspend-to-RAM. +* When enabled, CM does not rely on jiffies value in +* suspend_again and assumes that jiffies value does not +* change during suspend. */ struct charger_global_desc {char *rtc_name;bool (*rtc_only_wakeup)(void ); + +bool assume_timer_stops_in_suspend; };/** @@ -50,6 +59,11 @@ struct charger_global_desc {* @psy_name: the name of power-supply-class for charger manager * @polling_mode: * Determine which polling mode will be used +* @fullbatt_vchkdrop_ms: +* @fullbatt_vchkdrop_uV: +* Check voltage drop after the battery is fully charged. +* If it has dropped more than fullbatt_vchkdrop_uV after +* fullbatt_vchkdrop_ms, CM will restart charging. * @fullbatt_uV: voltage in microvolt * If it is not being charged and VBATT >= fullbatt_uV, * it is assumed to be full. @@ -76,6 +90,8 @@ struct charger_desc {enum polling_modes polling_mode;unsigned int polling_interval_ms; +unsigned int fullbatt_vchkdrop_ms; +unsigned int fullbatt_vchkdrop_uV;unsigned int fullbatt_uV;enum data_source battery_present;@@ -101,6 +117,11 @@ struct charger_desc {* @fuel_gauge: power_supply for fuel gauge * @charger_stat: array of power_supply for chargers * @charger_enabled: the state of charger +* @fullbatt_vchk_jiffies_at: +* jiffies at the time full battery check will occur. +* @fullbatt_vchk_uV: voltage in microvolt +* criteria for full battery +* @fullbatt_vchk_work: work queue for full battery check * @emergency_stop: * When setting true, stop charging * @last_temp_mC: the measured temperature in milli-Celsius @@ -121,6 +142,10 @@ struct charger_manager {bool charger_enabled;
Quoted 16 lines
include/linux/power/charger-manager.h struct charger_manager { · line 151
Quoted 8 lines
include/linux/power/charger-manager.h +4
-0+unsigned long fullbatt_vchk_jiffies_at; +unsigned int fullbatt_vchk_uV; +struct delayed_work fullbatt_vchk_work; +int emergency_stop;int last_temp_mC;Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Best Regards,
Chanwoo Choi