summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Aberg <eabejon@steludxu2826.(none)>2011-11-03 09:32:54 +0100
committerPhilippe Langlais <philippe.langlais@linaro.org>2012-03-19 08:51:04 +0100
commit2de3347c47b751033e2431441ff307b4039443c0 (patch)
treeaa7955fd781f1705776cd01ded48358db548cd8e
parent417d493df12e0a9062b7db79316bdd342a783cca (diff)
drivers:power:ab8500: Simplify current measurement
Previous complex implementation of blocking and none-blocking current measurement includes a race. Simplify the blocking and none-blocking measurements after the needs instead in order to get rid of the race. ST-Ericsson ID: 370038 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Iaf17fa5d0dbf0880d37fbfdc3b7d1ca56aa818ff Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36182 Reviewed-by: QABUILD Reviewed-by: Johan BJORNSTEDT <johan.bjornstedt@stericsson.com> Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com> Tested-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
-rw-r--r--drivers/power/ab8500_btemp.c38
-rw-r--r--drivers/power/ab8500_fg.c257
-rw-r--r--include/linux/mfd/ab8500/bm.h18
3 files changed, 117 insertions, 196 deletions
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index cab732a6681..c08f0334ca9 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -349,7 +349,7 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
int batctrl = 0;
int res;
int inst_curr;
- int i = 0;
+ int i;
/*
* BATCTRL current sources are included on AB8500 cut2.0
@@ -363,15 +363,37 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
if (!di->fg)
di->fg = ab8500_fg_get();
- if (!di->fg || ab8500_fg_inst_curr_nonblocking(di->fg, &inst_curr))
- inst_curr = 0;
- do {
+ if (!di->fg) {
+ dev_err(di->dev, "No fg found\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_fg_inst_curr_start(di->fg);
+
+ if (ret) {
+ dev_err(di->dev, "Failed to start current measurement\n");
+ return ret;
+ }
+
+ /*
+ * Since there is no interrupt when current measurement id one,
+ * loop for over 250ms, since 250ms is one sample conversion time
+ * with 32.768 Khz RTC clock.
+ */
+
+ for (i = 0; i < 11; i++) {
batctrl += ab8500_btemp_read_batctrl_voltage(di);
- i++;
- msleep(1);
- barrier();
- } while (inst_curr == INVALID_CURRENT);
+ msleep(25);
+ }
+
batctrl /= i;
+
+ ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
+ if (ret) {
+ dev_err(di->dev, "Failed to finalize current measurement\n");
+ return ret;
+ }
+
res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
ret = ab8500_btemp_curr_source_enable(di, false);
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 46291b2a229..bf9f9cafffd 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -143,23 +143,7 @@ struct inst_curr_result_list {
* @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
- * @inst_curr_mip: Indicate if 'instant' current measurement is in progress
- * AB8500 does not support real instant current
- * readings. The best we can do is sample over
- * 250ms.
- * @inst_curr_lock: Control access to inst curr wait queues and result lists
- * @inst_curr_wq: Work queue for running 'instant' current measurements
- * @fg_inst_curr_work: Work to measure 'instant' current
- * @result_wq: Wait queue for blocking 'instant' current clients
- * @cpw_a_wq: Physical wait queue A for concurrent inst curr clients
- * @cpw_b_wq: Physical wait queue B for concurrent inst curr clients
- * @cpw_next_wq: Logical next wait queue for concurrent inst curr clients
- * @cpw_this_wq: Logical this wait queue for concurrent inst curr clients
- * @inst_curr_result: Result register for blocking instant current clients
- * @result_a_list: Physical result list A for concurrent inst curr clients
- * @result_b_list: Physical result list B for concurrent inst curr clients
- * @next_result_list: Logical next results for concurrent inst curr clients
- * @this_result_list: Logical this results for concurrent inst curr clients
+ * @fg_off: True if fg was off before current measurement
* @calib_state State during offset calibration
* @discharge_state: Current discharge state
* @charge_state: Current charge state
@@ -195,20 +179,7 @@ struct ab8500_fg {
bool recovery_needed;
bool high_curr_mode;
bool init_capacity;
- bool inst_curr_mip;
- spinlock_t inst_curr_lock;
- struct workqueue_struct *inst_curr_wq;
- struct delayed_work fg_inst_curr_work;
- wait_queue_head_t result_wq;
- wait_queue_head_t cpw_a_wq;
- wait_queue_head_t cpw_b_wq;
- wait_queue_head_t *cpw_next_wq;
- wait_queue_head_t *cpw_this_wq;
- int inst_curr_result;
- struct list_head result_a_list;
- struct list_head result_b_list;
- struct list_head *next_result_list;
- struct list_head *this_result_list;
+ bool fg_off;
enum ab8500_fg_calibration_state calib_state;
enum ab8500_fg_discharge_state discharge_state;
enum ab8500_fg_charge_state charge_state;
@@ -521,145 +492,82 @@ cc_err:
}
/**
- * ab8500_fg_inst_curr_nonblocking() - battery instantaneous current
- * @di: pointer to the ab8500_fg structure
- * @local_result: pointer to result location, updated after measurement is
- * completed ~250ms after this function returns
- *
- * Returns error code
- */
-int ab8500_fg_inst_curr_nonblocking(struct ab8500_fg *di, int *local_result)
-{
- DEFINE_WAIT(wait);
-
- /*
- * caller needs to do some other work at
- * the same time as current measurement
- */
- wait_queue_head_t *cpw_my_wq;
- struct inst_curr_result_list *new =
- kmalloc(sizeof(struct inst_curr_result_list), GFP_KERNEL);
- if (!new)
- return -ENOMEM;
- new->result = local_result;
- *local_result = INVALID_CURRENT;
- INIT_LIST_HEAD(&new->list);
- spin_lock(&di->inst_curr_lock);
- list_add(&new->list, di->next_result_list);
- cpw_my_wq = di->cpw_next_wq;
- add_wait_queue(cpw_my_wq, &wait);
- /* queue_work may schedule */
- queue_delayed_work(di->inst_curr_wq, &di->fg_inst_curr_work, 1);
- set_current_state(TASK_UNINTERRUPTIBLE);
- spin_unlock(&di->inst_curr_lock);
- schedule();
- finish_wait(cpw_my_wq, &wait);
- return 0;
-}
-
-/**
- * ab8500_fg_inst_curr_blocking() - battery instantaneous current
+ * ab8500_fg_inst_curr_start() - start battery instantaneous current
* @di: pointer to the ab8500_fg structure
*
- * Returns battery instantenous current(on success) else error code
- */
-int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
-{
- DEFINE_WAIT(wait);
-
- /* caller will wait for the next available result */
- spin_lock(&di->inst_curr_lock);
- add_wait_queue(&di->result_wq, &wait);
- if (!di->inst_curr_mip)
- /* queue_work may schedule */
- queue_delayed_work(di->inst_curr_wq, &di->fg_inst_curr_work, 1);
- set_current_state(TASK_UNINTERRUPTIBLE);
- spin_unlock(&di->inst_curr_lock);
- schedule();
- finish_wait(&di->result_wq, &wait);
- return di->inst_curr_result;
-}
-
-/**
- * ab8500_fg_inst_curr_work() - take an 'instant' battery current reading
- * @work: pointer to the work_struct structure
- *
- * AB8500 does not provide instant current readings, the best we can do is
- * average over 250ms.
+ * Returns 0 or error code
+ * Note: This is part "one" and has to be called before
+ * ab8500_fg_inst_curr_finalize()
*/
-static void ab8500_fg_inst_curr_work(struct work_struct *work)
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
{
- struct list_head *temp_result_list;
- wait_queue_head_t *cpw_temp_wq;
- u8 low, high, reg_val;
- static int val;
- int ret = 0;
- bool fg_off = false;
-
- struct ab8500_fg *di = container_of(work,
- struct ab8500_fg, fg_inst_curr_work.work);
-
- spin_lock(&di->inst_curr_lock);
- temp_result_list = di->next_result_list;
- cpw_temp_wq = di->cpw_next_wq;
- di->next_result_list = di->this_result_list;
- di->cpw_next_wq = di->cpw_this_wq;
- di->this_result_list = temp_result_list;
- di->cpw_this_wq = cpw_temp_wq;
-
- di->inst_curr_mip = true;
- spin_unlock(&di->inst_curr_lock);
+ u8 reg_val;
+ int ret;
mutex_lock(&di->cc_lock);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
AB8500_RTC_CC_CONF_REG, &reg_val);
if (ret < 0)
- goto inst_curr_err1;
+ goto fail;
if (!(reg_val & CC_PWR_UP_ENA)) {
dev_dbg(di->dev, "%s Enable FG\n", __func__);
- fg_off = true;
+ di->fg_off = true;
/* Program the samples */
ret = abx500_set_register_interruptible(di->dev,
AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
SEC_TO_SAMPLE(10));
if (ret)
- goto inst_curr_err1;
+ goto fail;
/* Start the CC */
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8500_RTC_CC_CONF_REG,
(CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
if (ret)
- goto inst_curr_err1;
+ goto fail;
}
/* Reset counter and Read request */
ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
AB8500_GASG_CC_CTRL_REG, (RESET_ACCU | READ_REQ));
if (ret)
- goto inst_curr_err1;
+ goto fail;
- wake_up(di->cpw_this_wq);
+ /* Note: cc_lock is still locked */
+ return 0;
+fail:
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
- /*
- * Since there is no interrupt for this, just wait for 250ms
- * 250ms is one sample conversion time with 32.768 Khz RTC clock
- */
- msleep(250);
+/**
+ * ab8500_fg_inst_curr_finalize() - battery instantaneous current
+ * @di: pointer to the ab8500_fg structure
+ * @res: battery instantenous current(on success)
+ *
+ * Returns 0 or an error code
+ * Note: This is part "two" and has to be called at earliest 250 ms
+ * after ab8500_fg_inst_curr_start()
+ */
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
+{
+ u8 low, high;
+ int val;
+ int ret;
/* Read CC Sample conversion value Low and high */
ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
AB8500_GASG_CC_SMPL_CNVL_REG, &low);
if (ret < 0)
- goto inst_curr_err2;
+ goto fail;
ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
AB8500_GASG_CC_SMPL_CNVH_REG, &high);
if (ret < 0)
- goto inst_curr_err2;
+ goto fail;
/*
* negative value for Discharging
@@ -678,49 +586,63 @@ static void ab8500_fg_inst_curr_work(struct work_struct *work)
*/
val = ((val * 66660) / (4096 * di->bat->fg_res));
- if (fg_off) {
+ if (di->fg_off) {
dev_dbg(di->dev, "%s Disable FG\n", __func__);
/* Clear any pending read requests */
ret = abx500_set_register_interruptible(di->dev,
AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
if (ret)
- goto inst_curr_err3;
+ goto fail;
/* Stop the CC */
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8500_RTC_CC_CONF_REG, 0);
if (ret)
- goto inst_curr_err3;
+ goto fail;
}
+ mutex_unlock(&di->cc_lock);
+ (*res) = val;
-finished:
+ return 0;
+fail:
mutex_unlock(&di->cc_lock);
+ return ret;
+}
- spin_lock(&di->inst_curr_lock);
- di->inst_curr_result = val;
-
- while (!list_empty(di->this_result_list)) {
- struct inst_curr_result_list *this = list_first_entry(
- di->this_result_list,
- struct inst_curr_result_list,
- list);
- *(this->result) = val;
- list_del(&this->list);
- kfree(this);
+/**
+ * ab8500_fg_inst_curr_blocking() - battery instantaneous current
+ * @di: pointer to the ab8500_fg structure
+ * @res: battery instantenous current(on success)
+ *
+ * Returns 0 else error code
+ */
+int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
+{
+ int ret;
+ int res = 0;
+
+ ret = ab8500_fg_inst_curr_start(di);
+ if (ret) {
+ dev_err(di->dev, "Failed to initialize fg_inst\n");
+ return 0;
+ }
+
+ /*
+ * Since there is no interrupt for this wait for 253ms to be
+ * on the safe side.
+ *
+ * one sample conversion takes 250 ms at 32.768 Khz RTC clock
+ */
+ msleep(253);
+
+ ret = ab8500_fg_inst_curr_finalize(di, &res);
+ if (ret) {
+ dev_err(di->dev, "Failed to finalize fg_inst\n");
+ return 0;
}
- di->inst_curr_mip = false;
- wake_up(&di->result_wq);
- spin_unlock(&di->inst_curr_lock);
- return;
-inst_curr_err1:
- wake_up(di->cpw_this_wq);
-inst_curr_err2:
- val = 0;
-inst_curr_err3:
- dev_err(di->dev, "%s Get instanst current failed\n", __func__);
- goto finished;
+ return res;
}
/**
@@ -2092,8 +2014,6 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev)
if (ret)
dev_err(di->dev, "failed to disable coulomb counter\n");
- flush_workqueue(di->inst_curr_wq);
- destroy_workqueue(di->inst_curr_wq);
destroy_workqueue(di->fg_wq);
ab8500_fg_sysfs_exit(di);
@@ -2166,19 +2086,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
di->init_capacity = true;
- di->inst_curr_mip = false;
- spin_lock_init(&di->inst_curr_lock);
- init_waitqueue_head(&di->result_wq);
- init_waitqueue_head(&di->cpw_a_wq);
- init_waitqueue_head(&di->cpw_b_wq);
- di->cpw_next_wq = &di->cpw_a_wq;
- di->cpw_this_wq = &di->cpw_b_wq;
- di->inst_curr_result = 0;
- INIT_LIST_HEAD(&di->result_a_list);
- INIT_LIST_HEAD(&di->result_b_list);
- di->next_result_list = &di->result_a_list;
- di->this_result_list = &di->result_b_list;
-
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
@@ -2189,16 +2096,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
goto free_device_info;
}
- di->inst_curr_wq = create_singlethread_workqueue("ab8500_inst_curr_wq");
- if (di->inst_curr_wq == NULL) {
- dev_err(di->dev, "failed to create work queue\n");
- goto free_fg_wq;
- }
-
- /* Init work for running the instant current measurment */
- INIT_DELAYED_WORK_DEFERRABLE(&di->fg_inst_curr_work,
- ab8500_fg_inst_curr_work);
-
/* Init work for running the fg algorithm instantly */
INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
@@ -2281,8 +2178,6 @@ free_irq:
free_irq(irq, di);
}
free_inst_curr_wq:
- destroy_workqueue(di->inst_curr_wq);
-free_fg_wq:
destroy_workqueue(di->fg_wq);
free_device_info:
kfree(di);
diff --git a/include/linux/mfd/ab8500/bm.h b/include/linux/mfd/ab8500/bm.h
index 1fb67d25deb..9f2341d20a7 100644
--- a/include/linux/mfd/ab8500/bm.h
+++ b/include/linux/mfd/ab8500/bm.h
@@ -228,9 +228,6 @@
/* Battery type */
#define BATTERY_UNKNOWN 00
-/* Concurrent instant current i/f */
-#define INVALID_CURRENT INT_MAX
-
/*
* ADC for the battery thermistor.
* When using the ADC_THERM_BATCTRL the battery ID resistor is combined with
@@ -480,7 +477,9 @@ struct ab8500_btemp *ab8500_btemp_get(void);
int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp);
struct ab8500_fg *ab8500_fg_get(void);
int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
-int ab8500_fg_inst_curr_nonblocking(struct ab8500_fg *dev, int *local_result);
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
+
#else
static void ab8500_fg_reinit(void)
{
@@ -504,11 +503,16 @@ static int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev)
{
return -ENODEV;
}
-static int ab8500_fg_inst_curr_nonblocking(
- struct ab8500_fg *dev,
- int *local_result)
+
+static inline int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+{
+ return -ENODEV;
+}
+
+static inline int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
{
return -ENODEV;
}
+
#endif
#endif /* _AB8500_BM_H */