diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index c701f5e6f327..e92ec55ced91 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -323,6 +323,14 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) if (enable) { if (cm->emergency_stop) return -EAGAIN; + + /* + * Save start time of charging to limit + * maximum possible charging time. + */ + cm->charging_start_time = ktime_to_ms(ktime_get()); + cm->charging_end_time = 0; + for (i = 0 ; i < desc->num_charger_regulators ; i++) { err = regulator_enable(desc->charger_regulators[i].consumer); if (err < 0) { @@ -332,6 +340,13 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) } } } else { + /* + * Save end time of charging to maintain fully charged state + * of battery after full-batt. + */ + cm->charging_start_time = 0; + cm->charging_end_time = ktime_to_ms(ktime_get()); + for (i = 0 ; i < desc->num_charger_regulators ; i++) { err = regulator_disable(desc->charger_regulators[i].consumer); if (err < 0) { @@ -474,10 +489,57 @@ static void fullbatt_vchk(struct work_struct *work) if (diff > desc->fullbatt_vchkdrop_uV) { try_charger_restart(cm); - uevent_notify(cm, "Recharge"); + uevent_notify(cm, "Recharging"); } } +/** + * check_charging_duration - Monitor charging/discharging duration + * @cm: the Charger Manager representing the battery. + * + * If whole charging duration exceed 'charging_max_duration_ms', + * cm stop charging to prevent overcharge/overheat. If discharging + * duration exceed 'discharging _max_duration_ms', charger cable is + * attached, after full-batt, cm start charging to maintain fully + * charged state for battery. + */ +static int check_charging_duration(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + u64 curr = ktime_to_ms(ktime_get()); + u64 duration; + int ret = false; + + if (!desc->charging_max_duration_ms && + !desc->discharging_max_duration_ms) + return ret; + + if (cm->charger_enabled) { + duration = curr - cm->charging_start_time; + + if (duration > desc->charging_max_duration_ms) { + dev_info(cm->dev, "Charging duration exceed %lldms", + desc->charging_max_duration_ms); + uevent_notify(cm, "Discharging"); + try_charger_enable(cm, false); + ret = true; + } + } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { + duration = curr - cm->charging_end_time; + + if (duration > desc->charging_max_duration_ms && + is_ext_pwr_online(cm)) { + dev_info(cm->dev, "DisCharging duration exceed %lldms", + desc->discharging_max_duration_ms); + uevent_notify(cm, "Recharing"); + try_charger_enable(cm, true); + ret = true; + } + } + + return ret; +} + /** * _cm_monitor - Monitor the temperature and return true for exceptions. * @cm: the Charger Manager representing the battery. @@ -510,6 +572,13 @@ static bool _cm_monitor(struct charger_manager *cm) uevent_notify(cm, "COLD"); } + /* + * Check whole charging duration and discharing duration + * after full-batt. + */ + } else if (!cm->emergency_stop && check_charging_duration(cm)) { + dev_dbg(cm->dev, + "Charging/Discharging duration is out of range"); /* * Check dropped voltage of battery. If battery voltage is more * dropped than fullbatt_vchkdrop_uV after fully charged state, @@ -1271,6 +1340,15 @@ static int charger_manager_probe(struct platform_device *pdev) goto err_chg_stat; } + if (!desc->charging_max_duration_ms || + !desc->discharging_max_duration_ms) { + dev_info(&pdev->dev, "Cannot limit charging duration " + "checking mechanism to prevent overcharge/overheat " + "and control discharging duration"); + desc->charging_max_duration_ms = 0; + desc->discharging_max_duration_ms = 0; + } + platform_set_drvdata(pdev, cm); memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index 76b37ef3c071..a7b388ea1588 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h @@ -162,6 +162,13 @@ struct charger_regulator { * @measure_battery_temp: * true: measure battery temperature * false: measure ambient temperature + * @charging_max_duration_ms: Maximum possible duration for charging + * If whole charging duration exceed 'charging_max_duration_ms', + * cm stop charging. + * @discharging_max_duration_ms: + * Maximum possible duration for discharging with charger cable + * after full-batt. If discharging duration exceed 'discharging + * max_duration_ms', cm start charging. */ struct charger_desc { char *psy_name; @@ -186,6 +193,9 @@ struct charger_desc { int (*temperature_out_of_range)(int *mC); bool measure_battery_temp; + + u64 charging_max_duration_ms; + u64 discharging_max_duration_ms; }; #define PSY_NAME_MAX 30 @@ -210,6 +220,8 @@ struct charger_desc { * saved status of external power before entering suspend-to-RAM * @status_save_batt: * saved status of battery before entering suspend-to-RAM + * @charging_start_time: saved start time of enabling charging + * @charging_end_time: saved end time of disabling charging */ struct charger_manager { struct list_head entry; @@ -232,6 +244,9 @@ struct charger_manager { bool status_save_ext_pwr_inserted; bool status_save_batt; + + u64 charging_start_time; + u64 charging_end_time; }; #ifdef CONFIG_CHARGER_MANAGER