diff options
author | Arun Murthy <arun.murthy@stericsson.com> | 2011-11-30 16:25:34 +0530 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:01:14 +0200 |
commit | 296369d9b4c1d3b5682675ef7dc353819ae4eaac (patch) | |
tree | 36781b36c78fe46a2c7c85b13fed82b325651f93 | |
parent | 67f7b9c9b957753ea710eeb765800159a215d93f (diff) |
power: ab5500-fg: battery capacity based on fuel gauge
On enabling reset_on_read in fuel gauge register with offset 0x0C, the
accumulator should be reset after reading. But it doesnt due to the hardware
issue reported in ER374581.
This patch adds a software workaround for the above mentione issue.
ST-Ericsson Linux next: NA
ST-Ericsson ID: 375503
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: I92e06e3429faa2b58318015c6d93647d67c31160
Signed-off-by: Arun Murthy <arun.murthy@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40341
Reviewed-by: Rajagopala VENKATARAVANAPPA X <rajagopala.v@stericsson.com>
Reviewed-by: QABUILD
Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
-rw-r--r-- | drivers/power/ab5500_fg.c | 70 |
1 files changed, 51 insertions, 19 deletions
diff --git a/drivers/power/ab5500_fg.c b/drivers/power/ab5500_fg.c index 5e3a4f46cd0..0f1dc4a13df 100644 --- a/drivers/power/ab5500_fg.c +++ b/drivers/power/ab5500_fg.c @@ -37,11 +37,13 @@ static LIST_HEAD(ab5500_fg_list); #define FG_ACC_RESET_ON_READ 0x08 #define EN_READOUT_MASK 0x01 #define EN_READOUT 0x01 +#define EN_ACC_RESET_ON_READ 0x08 +#define ACC_RESET_ON_READ 0x08 #define RESET 0x00 #define EOC_52_mA 0x04 #define MILLI_TO_MICRO 1000 #define FG_LSB_IN_MA 770 -#define QLSB_NANO_AMP_HOURS_X10 1129 +#define QLSB_NANO_AMP_HOURS_X100 5353 #define SEC_TO_SAMPLE(S) (S * 4) #define NBR_AVG_SAMPLES 20 #define LOW_BAT_CHECK_INTERVAL (2 * HZ) @@ -444,6 +446,7 @@ static void ab5500_fg_acc_cur_timer_expired(unsigned long data) queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work, 0); } +static int prev_samples, prev_val; /** * ab5500_fg_acc_cur_work() - average battery current * @work: pointer to the work_struct structure @@ -453,7 +456,7 @@ static void ab5500_fg_acc_cur_timer_expired(unsigned long data) */ static void ab5500_fg_acc_cur_work(struct work_struct *work) { - int val; + int val, raw_val, sample; int ret; u8 low, med, high, cnt_low, cnt_high; @@ -473,6 +476,11 @@ static void ab5500_fg_acc_cur_work(struct work_struct *work) goto exit; /* If charging read charging registers for accumulated values */ if (di->flags.charging) { + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A, + ACC_RESET_ON_READ, EN_ACC_RESET_ON_READ); + if (ret < 0) + goto exit; /* Read CC Sample conversion value Low and high */ ret = abx500_get_register_interruptible(di->dev, AB5500_BANK_FG_BATTCOM_ACC, @@ -500,9 +508,19 @@ static void ab5500_fg_acc_cur_work(struct work_struct *work) AB5500_FG_VAL_COUNT1, &cnt_high); if (ret < 0) goto exit; + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A, + ACC_RESET_ON_READ, RESET); + if (ret < 0) + goto exit; queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work, di->bat->interval_charging * HZ); } else { /* discharging */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A, + ACC_RESET_ON_READ, EN_ACC_RESET_ON_READ); + if (ret < 0) + goto exit; /* Read CC Sample conversion value Low and high */ ret = abx500_get_register_interruptible(di->dev, AB5500_BANK_FG_BATTCOM_ACC, @@ -530,19 +548,48 @@ static void ab5500_fg_acc_cur_work(struct work_struct *work) AB5500_FG_VAL_COUNT1, &cnt_high); if (ret < 0) goto exit; + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A, + ACC_RESET_ON_READ, RESET); + if (ret < 0) + goto exit; queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work, di->bat->interval_not_charging * HZ); } di->fg_samples = (cnt_low | (cnt_high << 8)); + /* + * TODO: Workaround due to the hardware issue that accumulator is not + * reset after setting reset_on_read bit and reading the accumulator + * Registers. + */ + if (prev_samples > di->fg_samples) { + /* overflow has occured */ + sample = (0xFFFF - prev_samples) + di->fg_samples; + } else + sample = di->fg_samples - prev_samples; + prev_samples = di->fg_samples; + di->fg_samples = sample; val = (low | (med << 8) | (high << 16)); + /* + * TODO: Workaround due to the hardware issue that accumulator is not + * reset after setting reset_on_read bit and reading the accumulator + * Registers. + */ + if (prev_val > val) + raw_val = (0xFFFFFF - prev_val) + val; + else + raw_val = val - prev_val; + prev_val = val; + val = raw_val; if (di->fg_samples) { - di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10)/10000; + di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X100)/100000; di->avg_curr = (val * FG_LSB_IN_MA) / (di->fg_samples * 1000); } else dev_err(di->dev, "samples is zero, using previous calculated average current\n"); di->flags.conv_done = true; + di->calib_state = AB5500_FG_CALIB_END; mutex_unlock(&di->cc_lock); @@ -1372,8 +1419,6 @@ static int ab5500_fg_get_property(struct power_supply *psy, union power_supply_propval *val) { struct ab5500_fg *di; - int i, tbl_size; - struct abx500_v_to_cap *tbl; di = to_ab5500_fg_device_info(psy); @@ -1432,20 +1477,7 @@ static int ab5500_fg_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: if (di->flags.batt_unknown && !di->bat->chg_unknown_bat) val->intval = 100; - else if (di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl) { - tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl, - tbl_size = di->bat->bat_type[ - di->bat->batt_id].n_v_cap_tbl_elements; - - for (i = 0; i < tbl_size; ++i) { - if (di->vbat < tbl[i].voltage && - di->vbat > tbl[i+1].voltage) { - di->v_to_cap = tbl[i].capacity; - break; - } - } - val->intval = di->v_to_cap; - } else + else val->intval = di->bat_cap.prev_percent; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: |