charger-manager: Modify the way of checking battery's temperature

Charger-manager driver used to check battery temperature through the
callback function passed by platform data. Unfortunatley, without that
pre-defined callback function, charger-manager can't get battery's
temperature at all. Also passing callback function through platform data
ruins DT support for charger-manager.

This patch mondifies charger-manager driver to get temperature of battery
without pre-defined callback function. Now, charger-manager can use either
of battery thermometer in fuel-gauge and ouside of battery. It uses
thermal framework interface for outer thermometer if thermal fw is
enabled. Otherwise, it tries to use fuel-gauge's through the power supply
interface.

Signed-off-by: Jonghwa Lee <[email protected]>
Signed-off-by: Myungjoo Ham <[email protected]>
Signed-off-by: Anton Vorontsov <[email protected]>
This commit is contained in:
Jonghwa Lee 2013-12-18 15:42:34 +09:00 • committed by Anton Vorontsov
commit 5c49a6256b
2 changed files with 136 additions and 49 deletions

View file

@ -25,12 +25,22 @@
#include <linux/power/charger-manager.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
/*
* Default termperature threshold for charging.
* Every temperature units are in tenth of centigrade.
*/
#define CM_DEFAULT_RECHARGE_TEMP_DIFF 50
#define CM_DEFAULT_CHARGE_TEMP_MAX 500
static const char * const default_event_names[] = {
[CM_EVENT_UNKNOWN] = "Unknown",
[CM_EVENT_BATT_FULL] = "Battery Full",
[CM_EVENT_BATT_IN] = "Battery Inserted",
[CM_EVENT_BATT_OUT] = "Battery Pulled Out",
[CM_EVENT_BATT_OVERHEAT] = "Battery Overheat",
[CM_EVENT_BATT_COLD] = "Battery Cold",
[CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
[CM_EVENT_OTHERS] = "Other battery events"
@ -540,6 +550,60 @@ static int check_charging_duration(struct charger_manager *cm)
return ret;
}
static int cm_get_battery_temperature(struct charger_manager *cm,
int *temp)
{
int ret;
if (!cm->desc->measure_battery_temp)
return -ENODEV;
#ifdef CONFIG_THERMAL
ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
if (!ret)
/* Calibrate temperature unit */
*temp /= 100;
#else
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_TEMP,
(union power_supply_propval *)temp);
#endif
return ret;
}
static int cm_check_thermal_status(struct charger_manager *cm)
{
struct charger_desc *desc = cm->desc;
int temp, upper_limit, lower_limit;
int ret = 0;
ret = cm_get_battery_temperature(cm, &temp);
if (ret) {
/* FIXME:
* No information of battery temperature might
* occur hazadous result. We have to handle it
* depending on battery type.
*/
dev_err(cm->dev, "Failed to get battery temperature\n");
return 0;
}
upper_limit = desc->temp_max;
lower_limit = desc->temp_min;
if (cm->emergency_stop) {
upper_limit -= desc->temp_diff;
lower_limit += desc->temp_diff;
}
if (temp > upper_limit)
ret = CM_EVENT_BATT_OVERHEAT;
else if (temp < lower_limit)
ret = CM_EVENT_BATT_COLD;
return ret;
}
/**
* _cm_monitor - Monitor the temperature and return true for exceptions.
* @cm: the Charger Manager representing the battery.
@ -549,28 +613,22 @@ static int check_charging_duration(struct charger_manager *cm)
*/
static bool _cm_monitor(struct charger_manager *cm)
{
struct charger_desc *desc = cm->desc;
int temp = desc->temperature_out_of_range(&cm->last_temp_mC);
int temp_alrt;
dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n",
cm->last_temp_mC / 1000, cm->last_temp_mC % 1000);
temp_alrt = cm_check_thermal_status(cm);
/* It has been stopped already */
if (temp && cm->emergency_stop)
if (temp_alrt && 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)) {
if (temp > 0)
uevent_notify(cm, "OVERHEAT");
else
uevent_notify(cm, "COLD");
}
if (temp_alrt) {
cm->emergency_stop = temp_alrt;
if (!try_charger_enable(cm, false))
uevent_notify(cm, default_event_names[temp_alrt]);
/*
* Check whole charging duration and discharing duration
@ -802,21 +860,8 @@ static int charger_get_property(struct power_supply *psy,
POWER_SUPPLY_PROP_CURRENT_NOW, val);
break;
case POWER_SUPPLY_PROP_TEMP:
/* in thenth of centigrade */
if (cm->last_temp_mC == INT_MIN)
desc->temperature_out_of_range(&cm->last_temp_mC);
val->intval = cm->last_temp_mC / 100;
if (!desc->measure_battery_temp)
ret = -ENODEV;
break;
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
/* in thenth of centigrade */
if (cm->last_temp_mC == INT_MIN)
desc->temperature_out_of_range(&cm->last_temp_mC);
val->intval = cm->last_temp_mC / 100;
if (desc->measure_battery_temp)
ret = -ENODEV;
break;
return cm_get_battery_temperature(cm, &val->intval);
case POWER_SUPPLY_PROP_CAPACITY:
if (!cm->fuel_gauge) {
ret = -ENODEV;
@ -1439,6 +1484,50 @@ err:
return ret;
}
static int cm_init_thermal_data(struct charger_manager *cm)
{
struct charger_desc *desc = cm->desc;
union power_supply_propval val;
int ret;
/* Verify whether fuel gauge provides battery temperature */
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_TEMP, &val);
if (!ret) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP;
cm->charger_psy.num_properties++;
cm->desc->measure_battery_temp = true;
}
#ifdef CONFIG_THERMAL
cm->tzd_batt = cm->fuel_gauge->tzd;
if (ret && desc->thermal_zone) {
cm->tzd_batt =
thermal_zone_get_zone_by_name(desc->thermal_zone);
if (IS_ERR(cm->tzd_batt))
return PTR_ERR(cm->tzd_batt);
/* Use external thermometer */
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy.num_properties++;
cm->desc->measure_battery_temp = true;
ret = 0;
}
#endif
if (cm->desc->measure_battery_temp) {
/* NOTICE : Default allowable minimum charge temperature is 0 */
if (!desc->temp_max)
desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX;
if (!desc->temp_diff)
desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF;
}
return ret;
}
static int charger_manager_probe(struct platform_device *pdev)
{
struct charger_desc *desc = dev_get_platdata(&pdev->dev);
@ -1470,7 +1559,6 @@ static int charger_manager_probe(struct platform_device *pdev)
/* Basic Values. Unspecified are Null or 0 */
cm->dev = &pdev->dev;
cm->desc = desc;
cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
/*
* The following two do not need to be errors.
@ -1533,11 +1621,6 @@ static int charger_manager_probe(struct platform_device *pdev)
return -EINVAL;
}
if (!desc->temperature_out_of_range) {
dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
return -EINVAL;
}
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\n");
@ -1583,14 +1666,10 @@ static int charger_manager_probe(struct platform_device *pdev)
cm->charger_psy.num_properties++;
}
if (desc->measure_battery_temp) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP;
cm->charger_psy.num_properties++;
} else {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy.num_properties++;
ret = cm_init_thermal_data(cm);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize thermal data\n");
cm->desc->measure_battery_temp = false;
}
INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);