diff options
author | Johan Palsson <johan.palsson@stericsson.com> | 2011-01-27 16:29:51 +0100 |
---|---|---|
committer | Jonas ABERG <jonas.aberg@stericsson.com> | 2011-01-31 08:33:41 +0100 |
commit | a28da6daa2d6ffa4aa8b007d66e5e59c07cdcd61 (patch) | |
tree | 603e6f27c9b527091d3cbcd3beed3535507e94b2 /drivers/power | |
parent | ce17b094b81c03cce9ab4c6f36d0b1a671b44c3c (diff) |
power: ab8500_bm: Support for measuring temperature on BAT_CTRL
Temperature is now measured using the internal current sources
in AB8500. This will enable us to measure temperature in the battery
with higher precision
ST-Ericsson ID: WP320571
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: I2ebbf60ae8a03f7a0ddd7a6dce778ee93d5559d7
Signed-off-by: Johan Palsson <johan.palsson@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/13637
Reviewed-by: QATOOLS
Reviewed-by: Johan GARDSMARK <johan.gardsmark@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/ab8500_btemp.c | 279 | ||||
-rw-r--r-- | drivers/power/ab8500_fg.c | 8 |
2 files changed, 233 insertions, 54 deletions
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index 058386170de..8acd5e598ff 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -32,6 +32,9 @@ #define BTEMP_THERMAL_HIGH_LIMIT_57 57 #define BTEMP_THERMAL_HIGH_LIMIT_62 62 +#define BTEMP_BATCTRL_CURR_SRC_7UA 7 +#define BTEMP_BATCTRL_CURR_SRC_20UA 20 + #define to_ab8500_btemp_device_info(x) container_of((x), \ struct ab8500_btemp, btemp_psy); @@ -65,6 +68,7 @@ struct ab8500_btemp_ranges { * struct ab8500_btemp - ab8500 BTEMP device information * @dev: Pointer to the structure device * @chip_id: Chip-Id of the AB8500 + * @curr_source: What current source we use, in uA * @bat_temp: Battery temperature in degree Celcius * @prev_bat_temp Last dispatched battery temperature * @parent: Pointer to the struct ab8500 @@ -79,6 +83,7 @@ struct ab8500_btemp_ranges { struct ab8500_btemp { struct device *dev; u8 chip_id; + int curr_source; int bat_temp; int prev_bat_temp; struct ab8500 *parent; @@ -103,15 +108,13 @@ static enum power_supply_property ab8500_btemp_props[] = { * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance * @di: pointer to the ab8500_btemp structure * @v_batctrl: measured batctrl voltage - * @v_fg: voltage drop over the FG resistor * * This function returns the battery resistance that is - * derived from the BATCTRL voltage. The calculated resistance is - * compensated with the voltage drop over the FG resistor. - * Returns value in mOhms. + * derived from the BATCTRL voltage. + * Returns value in Ohms. */ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, - int v_batctrl, int v_fg) + int v_batctrl) { int rbs; @@ -122,14 +125,22 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, * For ABB cut1.0 and 1.1 BAT_CTRL is internally * connected to 1.8V through a 450k resistor */ - rbs = (450000 * (v_batctrl)) / (1800 - v_batctrl - v_fg); + rbs = (450000 * (v_batctrl)) / (1800 - v_batctrl); break; default: - /* - * For ABB cut2.0 and onwards BAT_CTRL is internally - * connected to 1.8V through a 80k resistor - */ - rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl - v_fg); + if (di->bat->therm == THERM_BATTERY_PACK) { + /* + * If the battery has internal NTC, we use the current + * source to calculate the resistance, 7uA or 20uA + */ + rbs = v_batctrl * 1000 / di->curr_source; + } else { + /* + * BAT_CTRL is internally + * connected to 1.8V through a 80k resistor + */ + rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl); + } break; } @@ -164,36 +175,184 @@ static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di) } /** - * ab8500_btemp_read_batctrl_res() - get battery resistance + * ab8500_btemp_curr_source_enable() - enable/disable batctrl current source + * @di: pointer to the ab8500_btemp structure + * @enable: enable or disable the current source + * + * Enable or disable the current sources for the BatCtrl AD channel + */ +static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, + bool enable) +{ + int curr; + int ret = 0; + + /* + * BATCTRL current sources are included on AB8500 cut2.0 + * and future versions + */ + if (di->chip_id == AB8500_CUT1P0 || di->chip_id == AB8500_CUT1P1) + return 0; + + /* Only do this for batteries with internal NTC */ + if (di->bat->therm == THERM_BATTERY_PACK && enable) { + if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA) + curr = BAT_CTRL_7U_ENA; + else + curr = BAT_CTRL_20U_ENA; + + dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source); + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + FORCE_BAT_CTRL_CMP_HIGH, FORCE_BAT_CTRL_CMP_HIGH); + if (ret) { + dev_err(di->dev, "%s failed setting cmp_force\n", + __func__); + return ret; + } + + /* + * We have to wait one 32kHz cycle before enabling + * the current source, since ForceBatCtrlCmpHigh needs + * to be written in a separate cycle + */ + udelay(32); + + ret = abx500_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + FORCE_BAT_CTRL_CMP_HIGH | curr); + if (ret) { + dev_err(di->dev, "%s failed enabling current source\n", + __func__); + goto disable_curr_source; + } + } else if (di->bat->therm == THERM_BATTERY_PACK && !enable) { + dev_dbg(di->dev, "Disable BATCTRL curr source\n"); + + /* Write 0 to the curr bits */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, + ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); + if (ret) { + dev_err(di->dev, "%s failed disabling current source\n", + __func__); + goto disable_curr_source; + } + + /* Enable Pull-Up and comparator */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA, + BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA); + if (ret) { + dev_err(di->dev, "%s failed enabling PU and comp\n", + __func__); + goto enable_pu_comp; + } + + /* + * We have to wait one 32kHz cycle before disabling + * ForceBatCtrlCmpHigh since this needs to be written + * in a separate cycle + */ + udelay(32); + + /* Disable 'force comparator' */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH); + if (ret) { + dev_err(di->dev, "%s failed disabling force comp\n", + __func__); + goto disable_force_comp; + } + } + return ret; + + /* + * We have to try unsetting FORCE_BAT_CTRL_CMP_HIGH one more time + * if we got an error above + */ +disable_curr_source: + /* Write 0 to the curr bits */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, + ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); + if (ret) { + dev_err(di->dev, "%s failed disabling current source\n", + __func__); + return ret; + } +enable_pu_comp: + /* Enable Pull-Up and comparator */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA, + BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA); + if (ret) { + dev_err(di->dev, "%s failed enabling PU and comp\n", + __func__); + return ret; + } + +disable_force_comp: + /* + * We have to wait one 32kHz cycle before disabling + * ForceBatCtrlCmpHigh since this needs to be written + * in a separate cycle + */ + udelay(32); + + /* Disable 'force comparator' */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH); + if (ret) { + dev_err(di->dev, "%s failed disabling force comp\n", + __func__); + return ret; + } + + return ret; +} + +/** + * ab8500_btemp_get_batctrl_res() - get battery resistance * @di: pointer to the ab8500_btemp structure - * @curr_comp: compensate for current or not * * This function returns the battery pack identification resistance. - * Returns value in mOhms. + * Returns value in Ohms. */ -static int ab8500_btemp_read_batctrl_res(struct ab8500_btemp *di, - bool curr_comp) +static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di) { - int inst_curr; - int fg_drop; + int ret; int batctrl; int res; - if (di->bat->fg_ops.get_inst_curr && curr_comp) - inst_curr = di->bat->fg_ops.get_inst_curr(di->parent->fg); - else - inst_curr = 0; + /* + * BATCTRL current sources are included on AB8500 cut2.0 + * and future versions + */ + ret = ab8500_btemp_curr_source_enable(di, true); + if (ret) { + dev_err(di->dev, "%s curr source enabled failed\n", __func__); + return ret; + } - fg_drop = -inst_curr * di->bat->fg_res / 1000; batctrl = ab8500_btemp_read_batctrl_voltage(di); - res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, fg_drop); + res = ab8500_btemp_batctrl_volt_to_res(di, batctrl); + + ret = ab8500_btemp_curr_source_enable(di, false); + if (ret) { + dev_err(di->dev, "%s curr source disable failed\n", __func__); + return ret; + } - dev_dbg(di->dev, "%s inst_curr: %d fg_drop: %d batctrl: %d res: %d ", - __func__, - inst_curr, - fg_drop, - batctrl, - res); + dev_dbg(di->dev, "%s batctrl: %d res: %d ", + __func__, batctrl, res); return res; } @@ -237,11 +396,10 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di, /** * ab8500_btemp_measure_temp() - measure battery temperature * @di: pointer to the ab8500_btemp structure - * @curr_comp: compensate for current or not * * Returns battery temperature (on success) else the previous temperature */ -static int ab8500_btemp_measure_temp(struct ab8500_btemp *di, bool curr_comp) +static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) { int data; int temp; @@ -254,7 +412,17 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di, bool curr_comp) t = di->bat->batt_id; - rbat = ab8500_btemp_read_batctrl_res(di, curr_comp); + rbat = ab8500_btemp_get_batctrl_res(di); + if (rbat < 0) { + dev_err(di->dev, "%s get batctrl res failed\n", + __func__); + /* + * Return out-of-range temperature so that + * charging is stopped + */ + return BTEMP_THERMAL_LOW_LIMIT; + } + temp = ab8500_btemp_res_to_temp(di, di->bat->bat_type[t].r_to_t_tbl, di->bat->bat_type[t].n_temp_tbl_elements, rbat); @@ -278,6 +446,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di, bool curr_comp) temp = ab8500_btemp_res_to_temp(di, di->bat->pcb_ntc, di->bat->n_temp_tbl_elements, rntc); + prev = temp; } dev_dbg(di->dev, "Battery temperature is %d\n", temp); return temp; @@ -296,7 +465,14 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) int res; u8 i; - res = ab8500_btemp_read_batctrl_res(di, false); + di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA; + di->bat->batt_id = BATTERY_UNKNOWN; + + res = ab8500_btemp_get_batctrl_res(di); + if (res < 0) { + dev_err(di->dev, "%s get batctrl res failed\n", __func__); + return -ENXIO; + } /* BATTERY_UNKNOWN is defined on position 0, skip it! */ for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) { @@ -309,16 +485,28 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) di->bat->therm, di->bat->bat_type[i].resis_low, res, di->bat->bat_type[i].resis_high, i); - di->bat->batt_id = i; - return i; + + di->bat->batt_id = i; + break; } } - /* This is the unknown battery type to be used */ - dev_err(di->dev, "Battery identified as unknown" - ", resistance %d Ohm\n", res); - di->bat->batt_id = BATTERY_UNKNOWN; - return -ENXIO; + if (di->bat->batt_id == BATTERY_UNKNOWN) { + dev_warn(di->dev, "Battery identified as unknown" + ", resistance %d Ohm\n", res); + return -ENXIO; + } + + /* + * We only have to change current source if the + * detected type is Type 1, else we use the 7uA source + */ + if (di->bat->therm == THERM_BATTERY_PACK && di->bat->batt_id == 1) { + dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n"); + di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA; + } + + return di->bat->batt_id; } /** @@ -332,7 +520,7 @@ static void ab8500_btemp_periodic_work(struct work_struct *work) struct ab8500_btemp *di = container_of(work, struct ab8500_btemp, btemp_periodic_work.work); - di->bat_temp = ab8500_btemp_measure_temp(di, true); + di->bat_temp = ab8500_btemp_measure_temp(di); if (di->bat_temp != di->prev_bat_temp) { di->prev_bat_temp = di->bat_temp; @@ -796,9 +984,8 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) di->chip_id); /* Identify the battery */ - di->bat->batt_id = BATTERY_UNKNOWN; - if (ab8500_btemp_id(di) == -ENXIO) - dev_err(di->dev, "failed to identify the battery\n"); + if (ab8500_btemp_id(di) < 0) + dev_warn(di->dev, "failed to identify the battery\n"); /* Set BTEMP thermal limits. Low and Med are fixed */ di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT; @@ -827,7 +1014,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) } /* Measure temperature once initially */ - di->bat_temp = ab8500_btemp_measure_temp(di, false); + di->bat_temp = ab8500_btemp_measure_temp(di); /* Register BTEMP power supply class */ ret = power_supply_register(di->dev, &di->btemp_psy); diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 8fcd08d4aee..e3788345579 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -452,10 +452,6 @@ static int ab8500_fg_inst_curr(struct ab8500_fg *di) return val; } -static struct ab8500_fg_ops fg_ops = { - .get_inst_curr = ab8500_fg_inst_curr -}; - /** * ab8500_fg_acc_cur_work() - average battery current * @work: pointer to the work_struct structure @@ -1597,9 +1593,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) } di->bat = plat->battery; - /* Set up function pointers to be used by BTEMP */ - di->bat->fg_ops = fg_ops; - di->fg_psy.name = "ab8500_fg"; di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->fg_psy.properties = ab8500_fg_props; @@ -1675,7 +1668,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, di); - di->parent->fg = di; /* Run the FG algorithm */ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); |