aboutsummaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorJohan Palsson <johan.palsson@stericsson.com>2011-01-27 16:29:51 +0100
committerJonas ABERG <jonas.aberg@stericsson.com>2011-01-31 08:33:41 +0100
commita28da6daa2d6ffa4aa8b007d66e5e59c07cdcd61 (patch)
tree603e6f27c9b527091d3cbcd3beed3535507e94b2 /drivers/power
parentce17b094b81c03cce9ab4c6f36d0b1a671b44c3c (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.c279
-rw-r--r--drivers/power/ab8500_fg.c8
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);