summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorKalle Komierowski <kalle.komierowski@gmail.com>2011-03-08 09:42:20 +0100
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:14:56 +0200
commit6e3a27e26e07f1f6fc8cea37f6054c75798331e6 (patch)
tree113b661d07c476314755d9304a7e9145fdc31d04 /drivers/power
parentbf7498b0bc89cf33e67a7273d05642966d4e8dae (diff)
power: ab8500_fg: Adding internal fuel gauge offset calibration
This calibration is to compensate for the offset in the fuel gauge ST-Ericsson ID: ER327880 Change-Id: Ibf7e37ea65c4e8da334dcd554da71d442f3cdcb7 Signed-off-by: Kalle Komierowski <kalle.komierowski@gmail.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17815 Reviewed-by: Johan PALSSON <johan.palsson@stericsson.com> Tested-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/ab8500_fg.c98
1 files changed, 89 insertions, 9 deletions
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index d141f44d2cd..a42ce95d48f 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -84,6 +84,12 @@ static char *charge_state[] = {
"CHARGE_READOUT",
};
+enum ab8500_fg_calibration_state {
+ AB8500_FG_CALIB_INIT,
+ AB8500_FG_CALIB_WAIT,
+ AB8500_FG_CALIB_END,
+};
+
struct ab8500_fg_avg_cap {
int avg;
int samples[NBR_AVG_SAMPLES];
@@ -113,6 +119,7 @@ struct ab8500_fg_flags {
bool low_bat;
bool bat_ovv;
bool batt_unknown;
+ bool calibrate;
};
/**
@@ -130,6 +137,7 @@ struct ab8500_fg_flags {
* @recovery_needed: Indicate if recovery is needed
* @high_curr_mode: Indicate if we're in high current mode
* @init_capacity: Indicate if initial capacity measuring should be done
+ * @calib_state State during offset calibration
* @discharge_state: Current discharge state
* @charge_state: Current charge state
* @flags: Structure for information about events triggered
@@ -161,6 +169,7 @@ struct ab8500_fg {
bool recovery_needed;
bool high_curr_mode;
bool init_capacity;
+ enum ab8500_fg_calibration_state calib_state;
enum ab8500_fg_discharge_state discharge_state;
enum ab8500_fg_charge_state charge_state;
struct ab8500_fg_flags flags;
@@ -1073,14 +1082,12 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
case AB8500_FG_DISCHARGE_INIT:
/* We use the FG IRQ to work on */
di->init_cnt = 0;
- di->fg_samples = SEC_TO_SAMPLE(
- di->bat->fg_params->init_timer);
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_INITMEASURING);
/* Intentional fallthrough */
-
case AB8500_FG_DISCHARGE_INITMEASURING:
/*
* Discard a number of samples during startup.
@@ -1232,6 +1239,56 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
}
/**
+ * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration
+ * @di: pointer to the ab8500_fg structure
+ *
+ */
+static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
+{
+ int ret;
+
+ switch (di->calib_state) {
+ case AB8500_FG_CALIB_INIT:
+ dev_dbg(di->dev, "Calibration ongoing...\n");
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8);
+ if (ret < 0)
+ goto err;
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
+ if (ret < 0)
+ goto err;
+ di->calib_state = AB8500_FG_CALIB_WAIT;
+ break;
+ case AB8500_FG_CALIB_END:
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_MUXOFFSET, CC_MUXOFFSET);
+ if (ret < 0)
+ goto err;
+ di->flags.calibrate = false;
+ dev_dbg(di->dev, "Calibration done...\n");
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ break;
+ case AB8500_FG_CALIB_WAIT:
+ dev_dbg(di->dev, "Calibration WFI\n");
+ default:
+ break;
+ }
+ return;
+err:
+ /* Something went wrong, don't calibrate then */
+ dev_err(di->dev, "failed to calibrate the CC\n");
+ di->flags.calibrate = false;
+ di->calib_state = AB8500_FG_CALIB_INIT;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+}
+
+/**
* ab8500_fg_algorithm() - Entry point for the FG algorithm
* @di: pointer to the ab8500_fg structure
*
@@ -1239,10 +1296,14 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
*/
static void ab8500_fg_algorithm(struct ab8500_fg *di)
{
- if (di->flags.charging)
- ab8500_fg_algorithm_charging(di);
- else
- ab8500_fg_algorithm_discharging(di);
+ if (di->flags.calibrate)
+ ab8500_fg_algorithm_calibrate(di);
+ else {
+ if (di->flags.charging)
+ ab8500_fg_algorithm_charging(di);
+ else
+ ab8500_fg_algorithm_discharging(di);
+ }
dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d\n",
@@ -1282,9 +1343,9 @@ static void ab8500_fg_periodic_work(struct work_struct *work)
ab8500_fg_calc_cap_discharge_voltage(di, true);
ab8500_fg_check_capacity_limits(di, true);
di->init_capacity = false;
- } else {
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ } else
ab8500_fg_algorithm(di);
- }
}
/**
@@ -1345,6 +1406,21 @@ static void ab8500_fg_instant_work(struct work_struct *work)
*
* Returns IRQ status(IRQ_HANDLED)
*/
+static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+ di->calib_state = AB8500_FG_CALIB_END;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
{
struct ab8500_fg *di = _di;
@@ -1690,6 +1766,7 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
{"NCONV_ACCU", ab8500_fg_cc_convend_handler},
{"BATT_OVV", ab8500_fg_batt_ovv_handler},
{"LOW_BAT_F", ab8500_fg_lowbatf_handler},
+ {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
};
static int __devinit ab8500_fg_probe(struct platform_device *pdev)
@@ -1808,6 +1885,9 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, di);
+ /* Calibrate the fg first time */
+ di->flags.calibrate = true;
+ di->calib_state = AB8500_FG_CALIB_INIT;
/* Run the FG algorithm */
queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);