ab8500_bm: Skip first CCEOC irq for instant current
authorJohan Bjornstedt <johan.bjornstedt@stericsson.com>
Fri, 11 Jan 2013 13:12:50 +0000 (13:12 +0000)
committerAnton Vorontsov <anton@enomsg.org>
Wed, 16 Jan 2013 01:10:17 +0000 (17:10 -0800)
When enabling the CCEOC irq we might get false interrupt
from ab8500-driver due to the latched value will be saved
and interpreted as an IRQ when enabled

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
Signed-off-by: Henrik Solver <henrik.solver@stericsson.com>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
Signed-off-by: Anton Vorontsov <anton@enomsg.org>
drivers/power/ab8500_btemp.c
drivers/power/ab8500_fg.c
include/linux/mfd/abx500/ab8500-bm.h

index e1d28039ce7b0fdd4a636dfd0e6b5441bd9f41d4..4a570b6c9e47cb542260e4203258f93567641e7e 100644 (file)
@@ -374,13 +374,10 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
                return ret;
        }
 
-       /*
-        * Since there is no interrupt when current measurement is done,
-        * loop for over 250ms (250ms is one sample conversion time
-        * with 32.768 Khz RTC clock). Note that a stop time must be set
-        * since the ab8500_btemp_read_batctrl_voltage call can block and
-        * take an unknown amount of time to complete.
-        */
+       do {
+               msleep(20);
+       } while (!ab8500_fg_inst_curr_started(di->fg));
+
        i = 0;
 
        do {
index 3d05c73813c8a594fcbc2ec220c063653c3d953b..0f40c968286c241e1d544dea4f48ce2aa3006212 100644 (file)
@@ -160,6 +160,7 @@ struct inst_curr_result_list {
  * @recovery_cnt:      Counter for recovery mode
  * @high_curr_cnt:     Counter for high current mode
  * @init_cnt:          Counter for init mode
+ * @nbr_cceoc_irq_cnt  Counter for number of CCEOC irqs received since enabled
  * @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
@@ -167,6 +168,7 @@ struct inst_curr_result_list {
  * @calib_state                State during offset calibration
  * @discharge_state:   Current discharge state
  * @charge_state:      Current charge state
+ * @ab8500_fg_started  Completion struct used for the instant current start
  * @ab8500_fg_complete Completion struct used for the instant current reading
  * @flags:             Structure for information about events triggered
  * @bat_cap:           Structure for battery capacity specific parameters
@@ -199,6 +201,7 @@ struct ab8500_fg {
        int recovery_cnt;
        int high_curr_cnt;
        int init_cnt;
+       int nbr_cceoc_irq_cnt;
        bool recovery_needed;
        bool high_curr_mode;
        bool init_capacity;
@@ -206,6 +209,7 @@ struct ab8500_fg {
        enum ab8500_fg_calibration_state calib_state;
        enum ab8500_fg_discharge_state discharge_state;
        enum ab8500_fg_charge_state charge_state;
+       struct completion ab8500_fg_started;
        struct completion ab8500_fg_complete;
        struct ab8500_fg_flags flags;
        struct ab8500_fg_battery_capacity bat_cap;
@@ -524,13 +528,14 @@ cc_err:
  * Note: This is part "one" and has to be called before
  * ab8500_fg_inst_curr_finalize()
  */
- int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
 {
        u8 reg_val;
        int ret;
 
        mutex_lock(&di->cc_lock);
 
+       di->nbr_cceoc_irq_cnt = 0;
        ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
                AB8500_RTC_CC_CONF_REG, &reg_val);
        if (ret < 0)
@@ -558,6 +563,7 @@ cc_err:
        }
 
        /* Return and WFI */
+       INIT_COMPLETION(di->ab8500_fg_started);
        INIT_COMPLETION(di->ab8500_fg_complete);
        enable_irq(di->irq);
 
@@ -568,6 +574,17 @@ fail:
        return ret;
 }
 
+/**
+ * ab8500_fg_inst_curr_started() - check if fg conversion has started
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 1 if conversion started, 0 if still waiting
+ */
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
+{
+       return completion_done(&di->ab8500_fg_started);
+}
+
 /**
  * ab8500_fg_inst_curr_done() - check if fg conversion is done
  * @di:         pointer to the ab8500_fg structure
@@ -596,13 +613,15 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
        int timeout;
 
        if (!completion_done(&di->ab8500_fg_complete)) {
-               timeout = wait_for_completion_timeout(&di->ab8500_fg_complete,
+               timeout = wait_for_completion_timeout(
+                       &di->ab8500_fg_complete,
                        INS_CURR_TIMEOUT);
                dev_dbg(di->dev, "Finalize time: %d ms\n",
                        ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
                if (!timeout) {
                        ret = -ETIME;
                        disable_irq(di->irq);
+                       di->nbr_cceoc_irq_cnt = 0;
                        dev_err(di->dev, "completion timed out [%d]\n",
                                __LINE__);
                        goto fail;
@@ -610,6 +629,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
        }
 
        disable_irq(di->irq);
+       di->nbr_cceoc_irq_cnt = 0;
 
        ret = abx500_mask_and_set_register_interruptible(di->dev,
                        AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
@@ -684,6 +704,7 @@ fail:
 int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
 {
        int ret;
+       int timeout;
        int res = 0;
 
        ret = ab8500_fg_inst_curr_start(di);
@@ -692,13 +713,32 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
                return 0;
        }
 
+       /* Wait for CC to actually start */
+       if (!completion_done(&di->ab8500_fg_started)) {
+               timeout = wait_for_completion_timeout(
+                       &di->ab8500_fg_started,
+                       INS_CURR_TIMEOUT);
+               dev_dbg(di->dev, "Start time: %d ms\n",
+                       ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
+               if (!timeout) {
+                       ret = -ETIME;
+                       dev_err(di->dev, "completion timed out [%d]\n",
+                               __LINE__);
+                       goto fail;
+               }
+       }
+
        ret = ab8500_fg_inst_curr_finalize(di, &res);
        if (ret) {
                dev_err(di->dev, "Failed to finalize fg_inst\n");
                return 0;
        }
 
+       dev_dbg(di->dev, "%s instant current: %d", __func__, res);
        return res;
+fail:
+       mutex_unlock(&di->cc_lock);
+       return ret;
 }
 
 /**
@@ -1523,8 +1563,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
 
        case AB8500_FG_DISCHARGE_WAKEUP:
                ab8500_fg_coulomb_counter(di, true);
-               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
-
                ab8500_fg_calc_cap_discharge_voltage(di, true);
 
                di->fg_samples = SEC_TO_SAMPLE(
@@ -1641,8 +1679,6 @@ static void ab8500_fg_periodic_work(struct work_struct *work)
                fg_periodic_work.work);
 
        if (di->init_capacity) {
-               /* A dummy read that will return 0 */
-               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
                /* Get an initial capacity calculation */
                ab8500_fg_calc_cap_discharge_voltage(di, true);
                ab8500_fg_check_capacity_limits(di, true);
@@ -1828,7 +1864,13 @@ static void ab8500_fg_instant_work(struct work_struct *work)
 static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
 {
        struct ab8500_fg *di = _di;
-       complete(&di->ab8500_fg_complete);
+       if (!di->nbr_cceoc_irq_cnt) {
+               di->nbr_cceoc_irq_cnt++;
+               complete(&di->ab8500_fg_started);
+       } else {
+               di->nbr_cceoc_irq_cnt = 0;
+               complete(&di->ab8500_fg_complete);
+       }
        return IRQ_HANDLED;
 }
 
@@ -2551,7 +2593,11 @@ static int ab8500_fg_probe(struct platform_device *pdev)
        di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
        ab8500_fg_coulomb_counter(di, true);
 
-       /* Initialize completion used to notify completion of inst current */
+       /*
+        * Initialize completion used to notify completion and start
+        * of inst current
+        */
+       init_completion(&di->ab8500_fg_started);
        init_completion(&di->ab8500_fg_complete);
 
        /* Register interrupts */
@@ -2571,6 +2617,7 @@ static int ab8500_fg_probe(struct platform_device *pdev)
        }
        di->irq = platform_get_irq_byname(pdev, "CCEOC");
        disable_irq(di->irq);
+       di->nbr_cceoc_irq_cnt = 0;
 
        platform_set_drvdata(pdev, di);
 
index 44310c98ee6eb59f38ca3024622966e6b528bf1d..6c6a02e53cd9eb3599eada136dcca5de96358641 100644 (file)
@@ -431,11 +431,18 @@ struct ab8500_fg *ab8500_fg_get(void);
 int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
 int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
 int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
 int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
 
 #else
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
+{
+       return 0;
+}
+
 int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
 {
+       return 0;
 }
 static void ab8500_fg_reinit(void)
 {