From dbb61fc7452632a4225b2fe3719b6c2d1306e257 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 27 Jul 2012 14:01:34 +0900 Subject: charger-manager: Disable regulator when charger cable is detached This patch disable regulator for charging when charger cable is detached before stopping charging forcibly on abnormal battery state and check return value of regulator_enable/disable() function to confirm correct operation of enabling or disabling regulator for charging. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/power/charger-manager.c') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 526e5c931294..240de49934f1 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -271,9 +271,24 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) if (enable) { if (cm->emergency_stop) return -EAGAIN; - for (i = 0 ; i < desc->num_charger_regulators ; i++) - regulator_enable(desc->charger_regulators[i].consumer); + for (i = 0 ; i < desc->num_charger_regulators ; i++) { + err = regulator_enable(desc->charger_regulators[i].consumer); + if (err < 0) { + dev_warn(cm->dev, + "Cannot enable %s regulator\n", + desc->charger_regulators[i].regulator_name); + } + } } else { + for (i = 0 ; i < desc->num_charger_regulators ; i++) { + err = regulator_disable(desc->charger_regulators[i].consumer); + if (err < 0) { + dev_warn(cm->dev, + "Cannot disable %s regulator\n", + desc->charger_regulators[i].regulator_name); + } + } + /* * Abnormal battery state - Stop charging forcibly, * even if charger was enabled at the other places -- cgit v1.2.3 From fd65ee5f1c21af9ff9f113842d513ca50749ad68 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 27 Jul 2012 14:01:37 +0900 Subject: charger-manager: Use replacement variable to check state of battery This patch remove unnecessary variable(cm->fullbatt_vchk_uV) by using 'desc->fullbatt_uV' field directly in fullbatt_handler() function to check the state of battery. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/charger-manager.c') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 240de49934f1..cdf29d2eb7a5 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -415,7 +415,7 @@ static void fullbatt_vchk(struct work_struct *work) return; } - diff = cm->fullbatt_vchk_uV; + diff = desc->fullbatt_uV; diff -= batt_uV; dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); -- cgit v1.2.3 From 2ed9e9b6530951b5b96185e6761119361a166d7a Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 21 Aug 2012 17:06:52 +0900 Subject: charger-manager: Check fully charged state of battery periodically This patch check periodically fully charged state of battery to protect overcharge and overheat. If battery is fully charged, stop charging and check droped voltage with 'fullbatt_vchkdrop_ms' period. When voltage of battery is more droped than 'fullbatt_vchkdrop_uV' voltage, charger-manager will restart charging for battery. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 161 +++++++++++++++++++++++++++++----------- 1 file changed, 116 insertions(+), 45 deletions(-) (limited to 'drivers/power/charger-manager.c') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index cdf29d2eb7a5..c701f5e6f327 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -226,6 +226,58 @@ static bool is_charging(struct charger_manager *cm) return charging; } +/** + * is_full_charged - Returns true if the battery is fully charged. + * @cm: the Charger Manager representing the battery. + */ +static bool is_full_charged(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + union power_supply_propval val; + int ret = 0; + int uV; + + /* If there is no battery, it cannot be charged */ + if (!is_batt_present(cm)) { + val.intval = 0; + goto out; + } + + if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) { + /* Not full if capacity of fuel gauge isn't full */ + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CHARGE_FULL, &val); + if (!ret && val.intval > desc->fullbatt_full_capacity) { + val.intval = 1; + goto out; + } + } + + /* Full, if it's over the fullbatt voltage */ + if (desc->fullbatt_uV > 0) { + ret = get_batt_uV(cm, &uV); + if (!ret && uV >= desc->fullbatt_uV) { + val.intval = 1; + goto out; + } + } + + /* Full, if the capacity is more than fullbatt_soc */ + if (cm->fuel_gauge && desc->fullbatt_soc > 0) { + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CAPACITY, &val); + if (!ret && val.intval >= desc->fullbatt_soc) { + val.intval = 1; + goto out; + } + } + + val.intval = 0; + +out: + return val.intval ? true : false; +} + /** * is_polling_required - Return true if need to continue polling for this CM. * @cm: the Charger Manager representing the battery. @@ -418,7 +470,7 @@ static void fullbatt_vchk(struct work_struct *work) diff = desc->fullbatt_uV; diff -= batt_uV; - dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); + dev_info(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); if (diff > desc->fullbatt_vchkdrop_uV) { try_charger_restart(cm); @@ -441,10 +493,14 @@ static bool _cm_monitor(struct charger_manager *cm) dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", cm->last_temp_mC / 1000, cm->last_temp_mC % 1000); - /* It has been stopped or charging already */ - if (!!temp == !!cm->emergency_stop) + /* It has been stopped already */ + if (temp && cm->emergency_stop) return false; + /* + * Check temperature whether overheat or cold. + * If temperature is out of range normal state, stop charging. + */ if (temp) { cm->emergency_stop = temp; if (!try_charger_enable(cm, false)) { @@ -453,10 +509,34 @@ static bool _cm_monitor(struct charger_manager *cm) else uevent_notify(cm, "COLD"); } + + /* + * Check dropped voltage of battery. If battery voltage is more + * dropped than fullbatt_vchkdrop_uV after fully charged state, + * charger-manager have to recharge battery. + */ + } else if (!cm->emergency_stop && is_ext_pwr_online(cm) && + !cm->charger_enabled) { + fullbatt_vchk(&cm->fullbatt_vchk_work.work); + + /* + * Check whether fully charged state to protect overcharge + * if charger-manager is charging for battery. + */ + } else if (!cm->emergency_stop && is_full_charged(cm) && + cm->charger_enabled) { + dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n"); + uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); + + try_charger_enable(cm, false); + + fullbatt_vchk(&cm->fullbatt_vchk_work.work); } else { cm->emergency_stop = 0; - if (!try_charger_enable(cm, true)) - uevent_notify(cm, "CHARGING"); + if (is_ext_pwr_online(cm)) { + if (!try_charger_enable(cm, true)) + uevent_notify(cm, "CHARGING"); + } } return true; @@ -719,47 +799,10 @@ static int charger_get_property(struct power_supply *psy, val->intval = 0; break; case POWER_SUPPLY_PROP_CHARGE_FULL: - if (cm->fuel_gauge) { - if (cm->fuel_gauge->get_property(cm->fuel_gauge, - POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0) - break; - } - - if (is_ext_pwr_online(cm)) { - /* Not full if it's charging. */ - if (is_charging(cm)) { - val->intval = 0; - break; - } - /* - * Full if it's powered but not charging andi - * not forced stop by emergency - */ - if (!cm->emergency_stop) { - val->intval = 1; - break; - } - } - - /* Full if it's over the fullbatt voltage */ - ret = get_batt_uV(cm, &uV); - if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && - !is_charging(cm)) { + if (is_full_charged(cm)) val->intval = 1; - break; - } - - /* Full if the cap is 100 */ - if (cm->fuel_gauge) { - ret = cm->fuel_gauge->get_property(cm->fuel_gauge, - POWER_SUPPLY_PROP_CAPACITY, val); - if (!ret && val->intval >= 100 && !is_charging(cm)) { - val->intval = 1; - break; - } - } - - val->intval = 0; + else + val->intval = 0; ret = 0; break; case POWER_SUPPLY_PROP_CHARGE_NOW: @@ -1049,7 +1092,26 @@ static int charger_extcon_notifier(struct notifier_block *self, struct charger_cable *cable = container_of(self, struct charger_cable, nb); + /* + * The newly state of charger cable. + * If cable is attached, cable->attached is true. + */ cable->attached = event; + + /* + * Setup monitoring to check battery state + * when charger cable is attached. + */ + if (cable->attached && is_polling_required(cable->cm)) { + if (work_pending(&setup_polling)) + cancel_work_sync(&setup_polling); + schedule_work(&setup_polling); + } + + /* + * Setup work for controlling charger(regulator) + * according to charger cable. + */ schedule_work(&cable->wq); return NOTIFY_DONE; @@ -1143,6 +1205,15 @@ static int charger_manager_probe(struct platform_device *pdev) desc->fullbatt_vchkdrop_ms = 0; desc->fullbatt_vchkdrop_uV = 0; } + if (desc->fullbatt_soc == 0) { + dev_info(&pdev->dev, "Ignoring full-battery soc(state of" + " charge) threshold as it is not" + " supplied."); + } + if (desc->fullbatt_full_capacity == 0) { + dev_info(&pdev->dev, "Ignoring full-battery full capacity" + " threshold as it is not supplied."); + } if (!desc->charger_regulators || desc->num_charger_regulators < 1) { ret = -EINVAL; -- cgit v1.2.3 From 8fcfe088e21aa0db9d62eaf565757def673efba6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 20 Sep 2012 21:20:05 -0700 Subject: charger-manager: Support limit of maximum possible This patch check maximum possible duration of charging/discharging. If whole charging duration exceed 'desc->charging_max_duration_ms', cm stop charging to prevent overcharge/overheat. And if discharging duration exceed, charger cable is attached, after full-batt, cm start charging to maintain fully charged state for battery. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 80 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) (limited to 'drivers/power/charger-manager.c') 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,8 +489,55 @@ 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; } /** @@ -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)); -- cgit v1.2.3 From 3950c7865cd7c963982a2c94457182b96732f4c9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 21 Sep 2012 18:49:37 +0900 Subject: charger-manager: Add support sysfs entry for charger This patch add support sysfs entry for each charger(regulator). Charger-manager use one or more chargers for charging battery but some charger isn't necessary on specific scenario. So, if some charger isn't needed, can disable specific charger through 'externally_control' entry while system is on state and confirm the information(name, state) of charger. The list of added sysfs entry - /sys/class/power_supply/battery/chargers/charger.[index]/name show name of charger(regulator) - /sys/class/power_supply/battery/chargers/charger.[index]/state show either enabled or disabled state of charger - /sys/class/power_supply/battery/chargers/charger.[index]/externally_control If 'externally_control' of specific charger is 1, Charger-manager cannot enable regulator for charging when charger cable is attached and charger must be maintained with disabled state. If 'externally_control' is zero, Charger-manager usually can control to enable/disable regulator. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 172 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) (limited to 'drivers/power/charger-manager.c') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index e92ec55ced91..92dfa5c64876 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -22,6 +22,7 @@ #include #include #include +#include static const char * const default_event_names[] = { [CM_EVENT_UNKNOWN] = "Unknown", @@ -332,6 +333,9 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) cm->charging_end_time = 0; for (i = 0 ; i < desc->num_charger_regulators ; i++) { + if (desc->charger_regulators[i].externally_control) + continue; + err = regulator_enable(desc->charger_regulators[i].consumer); if (err < 0) { dev_warn(cm->dev, @@ -348,6 +352,9 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) cm->charging_end_time = ktime_to_ms(ktime_get()); for (i = 0 ; i < desc->num_charger_regulators ; i++) { + if (desc->charger_regulators[i].externally_control) + continue; + err = regulator_disable(desc->charger_regulators[i].consumer); if (err < 0) { dev_warn(cm->dev, @@ -1217,12 +1224,101 @@ static int charger_extcon_init(struct charger_manager *cm, return ret; } +/* help function of sysfs node to control charger(regulator) */ +static ssize_t charger_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_regulator *charger + = container_of(attr, struct charger_regulator, attr_name); + + return sprintf(buf, "%s\n", charger->regulator_name); +} + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_regulator *charger + = container_of(attr, struct charger_regulator, attr_state); + int state = 0; + + if (!charger->externally_control) + state = regulator_is_enabled(charger->consumer); + + return sprintf(buf, "%s\n", state ? "enabled" : "disabled"); +} + +static ssize_t charger_externally_control_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_regulator *charger = container_of(attr, + struct charger_regulator, attr_externally_control); + + return sprintf(buf, "%d\n", charger->externally_control); +} + +static ssize_t charger_externally_control_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct charger_regulator *charger + = container_of(attr, struct charger_regulator, + attr_externally_control); + struct charger_manager *cm = charger->cm; + struct charger_desc *desc = cm->desc; + int i; + int ret; + int externally_control; + int chargers_externally_control = 1; + + ret = sscanf(buf, "%d", &externally_control); + if (ret == 0) { + ret = -EINVAL; + return ret; + } + + if (!externally_control) { + charger->externally_control = 0; + return count; + } + + for (i = 0; i < desc->num_charger_regulators; i++) { + if (&desc->charger_regulators[i] != charger && + !desc->charger_regulators[i].externally_control) { + /* + * At least, one charger is controlled by + * charger-manager + */ + chargers_externally_control = 0; + break; + } + } + + if (!chargers_externally_control) { + if (cm->charger_enabled) { + try_charger_enable(charger->cm, false); + charger->externally_control = externally_control; + try_charger_enable(charger->cm, true); + } else { + charger->externally_control = externally_control; + } + } else { + dev_warn(cm->dev, + "'%s' regulator should be controlled " + "in charger-manager because charger-manager " + "must need at least one charger for charging\n", + charger->regulator_name); + } + + return count; +} + static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = dev_get_platdata(&pdev->dev); struct charger_manager *cm; int ret = 0, i = 0; int j = 0; + int chargers_externally_control = 1; union power_supply_propval val; if (g_desc && !rtc_dev && g_desc->rtc_name) { @@ -1412,6 +1508,8 @@ static int charger_manager_probe(struct platform_device *pdev) for (i = 0 ; i < desc->num_charger_regulators ; i++) { struct charger_regulator *charger = &desc->charger_regulators[i]; + char buf[11]; + char *str; charger->consumer = regulator_get(&pdev->dev, charger->regulator_name); @@ -1421,6 +1519,7 @@ static int charger_manager_probe(struct platform_device *pdev) ret = -EINVAL; goto err_chg_get; } + charger->cm = cm; for (j = 0 ; j < charger->num_cables ; j++) { struct charger_cable *cable = &charger->cables[j]; @@ -1434,6 +1533,71 @@ static int charger_manager_probe(struct platform_device *pdev) cable->charger = charger; cable->cm = cm; } + + /* Create sysfs entry to control charger(regulator) */ + snprintf(buf, 10, "charger.%d", i); + str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); + if (!str) { + for (i--; i >= 0; i--) { + charger = &desc->charger_regulators[i]; + kfree(charger->attr_g.name); + } + ret = -ENOMEM; + + goto err_extcon; + } + strcpy(str, buf); + + charger->attrs[0] = &charger->attr_name.attr; + charger->attrs[1] = &charger->attr_state.attr; + charger->attrs[2] = &charger->attr_externally_control.attr; + charger->attrs[3] = NULL; + charger->attr_g.name = str; + charger->attr_g.attrs = charger->attrs; + + sysfs_attr_init(&charger->attr_name.attr); + charger->attr_name.attr.name = "name"; + charger->attr_name.attr.mode = 0444; + charger->attr_name.show = charger_name_show; + + sysfs_attr_init(&charger->attr_state.attr); + charger->attr_state.attr.name = "state"; + charger->attr_state.attr.mode = 0444; + charger->attr_state.show = charger_state_show; + + sysfs_attr_init(&charger->attr_externally_control.attr); + charger->attr_externally_control.attr.name + = "externally_control"; + charger->attr_externally_control.attr.mode = 0644; + charger->attr_externally_control.show + = charger_externally_control_show; + charger->attr_externally_control.store + = charger_externally_control_store; + + if (!desc->charger_regulators[i].externally_control || + !chargers_externally_control) { + chargers_externally_control = 0; + } + dev_info(&pdev->dev, "'%s' regulator's externally_control" + "is %d\n", charger->regulator_name, + charger->externally_control); + + ret = sysfs_create_group(&cm->charger_psy.dev->kobj, + &charger->attr_g); + if (ret < 0) { + dev_info(&pdev->dev, "Cannot create sysfs entry" + "of %s regulator\n", + charger->regulator_name); + } + } + + if (chargers_externally_control) { + dev_err(&pdev->dev, "Cannot register regulator because " + "charger-manager must need at least " + "one charger for charging battery\n"); + + ret = -EINVAL; + goto err_chg_enable; } ret = try_charger_enable(cm, true); @@ -1459,6 +1623,14 @@ static int charger_manager_probe(struct platform_device *pdev) return 0; err_chg_enable: + for (i = 0; i < desc->num_charger_regulators; i++) { + struct charger_regulator *charger; + + charger = &desc->charger_regulators[i]; + sysfs_remove_group(&cm->charger_psy.dev->kobj, + &charger->attr_g); + kfree(charger->attr_g.name); + } err_extcon: for (i = 0 ; i < desc->num_charger_regulators ; i++) { struct charger_regulator *charger -- cgit v1.2.3