From: Michel JAOUEN <michel.jao...@stericsson.com>

Add dedicated test functions for HATS framework. This patch
allows validating fuel gauge HW IP in all possible modes
supported by HW. Services are accessible through DebugFS
interface.

Signed-off-by: Lee Jones <lee.jo...@linaro.org>
Signed-off-by: Loic Pallardy <loic.palla...@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jao...@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.coo...@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.ab...@stericsson.com>
Tested-by: Michel JAOUEN <michel.jao...@stericsson.com>
Tested-by: Jonas ABERG <jonas.ab...@stericsson.com>
---
 drivers/power/Kconfig                |   10 +
 drivers/power/Makefile               |    1 +
 drivers/power/ab8500_fg.c            |  271 +++--------
 drivers/power/ab8500_fg.h            |  242 ++++++++++
 drivers/power/ab8500_fg_deepdebug.c  |  823 ++++++++++++++++++++++++++++++++++
 include/linux/mfd/abx500/ab8500-bm.h |  164 +++++++
 6 files changed, 1297 insertions(+), 214 deletions(-)
 create mode 100644 drivers/power/ab8500_fg.h
 create mode 100644 drivers/power/ab8500_fg_deepdebug.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index b9de00d..16b4869 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -346,6 +346,15 @@ config AB8500_BM
        help
          Say Y to include support for AB8500 battery management.
 
+config AB8500_BM_DEEP_DEBUG
+       bool "AB8500 Battery Management Deep Debug"
+       depends on (AB8500_BM && DEEP_DEBUG)
+       default y
+       help
+         Say Y to include support for Deep Debug interface
+         for battery management.
+         If unsure, say N.
+
 config CHARGER_PM2301
        bool "PM2301 Battery Charger Driver"
        depends on AB8500_BM
@@ -356,6 +365,7 @@ config CHARGER_PM2301
 config PM2XXX_DEEP_DEBUG
        bool "PM2XXX Deep Debug"
        depends on DEEP_DEBUG && CHARGER_PM2301
+       default n
        help
          Deep Debug interface provides an access to all registers.
          It allows to read or write directly a register.
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ef1e79c..476668d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_BATTERY_JZ4740)  += jz4740-battery.o
 obj-$(CONFIG_BATTERY_INTEL_MID)        += intel_mid_battery.o
 obj-$(CONFIG_BATTERY_RX51)     += rx51_battery.o
 obj-$(CONFIG_AB8500_BM)                += ab8500_bmdata.o ab8500_charger.o 
ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
+obj-$(CONFIG_AB8500_BM_DEEP_DEBUG) += ab8500_fg_deepdebug.o
 obj-$(CONFIG_CHARGER_ISP1704)  += isp1704_charger.o
 obj-$(CONFIG_CHARGER_MAX8903)  += max8903_charger.o
 obj-$(CONFIG_CHARGER_TWL4030)  += twl4030_charger.o
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index a0cbbd3..238eeee 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -33,25 +33,12 @@
 #include <linux/mfd/abx500/ab8500-bm.h>
 #include <linux/mfd/abx500/ab8500-gpadc.h>
 #include <linux/kernel.h>
+#include "ab8500_fg.h"
 
-#define MILLI_TO_MICRO                 1000
-#define FG_LSB_IN_MA                   1627
-#define QLSB_NANO_AMP_HOURS_X10                1129
-#define INS_CURR_TIMEOUT               (3 * HZ)
-
-#define SEC_TO_SAMPLE(S)               (S * 4)
-
-#define NBR_AVG_SAMPLES                        20
-
-#define LOW_BAT_CHECK_INTERVAL         (HZ / 16) /* 62.5 ms */
-
-#define VALID_CAPACITY_SEC             (45 * 60) /* 45 minutes */
-#define BATT_OK_MIN                    2360 /* mV */
-#define BATT_OK_INCREMENT              50 /* mV */
-#define BATT_OK_MAX_NR_INCREMENTS      0xE
-
-/* FG constants */
-#define BATT_OVV                       0x01
+char *charge_state[] = {
+       "CHARGE_INIT",
+       "CHARGE_READOUT",
+};
 
 #define interpolate(x, x1, y1, x2, y2) \
        ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
@@ -59,186 +46,6 @@
 #define to_ab8500_fg_device_info(x) container_of((x), \
        struct ab8500_fg, fg_psy);
 
-/**
- * struct ab8500_fg_interrupts - ab8500 fg interupts
- * @name:      name of the interrupt
- * @isr                function pointer to the isr
- */
-struct ab8500_fg_interrupts {
-       char *name;
-       irqreturn_t (*isr)(int irq, void *data);
-};
-
-enum ab8500_fg_discharge_state {
-       AB8500_FG_DISCHARGE_INIT,
-       AB8500_FG_DISCHARGE_INITMEASURING,
-       AB8500_FG_DISCHARGE_INIT_RECOVERY,
-       AB8500_FG_DISCHARGE_RECOVERY,
-       AB8500_FG_DISCHARGE_READOUT_INIT,
-       AB8500_FG_DISCHARGE_READOUT,
-       AB8500_FG_DISCHARGE_WAKEUP,
-};
-
-static char *discharge_state[] = {
-       "DISCHARGE_INIT",
-       "DISCHARGE_INITMEASURING",
-       "DISCHARGE_INIT_RECOVERY",
-       "DISCHARGE_RECOVERY",
-       "DISCHARGE_READOUT_INIT",
-       "DISCHARGE_READOUT",
-       "DISCHARGE_WAKEUP",
-};
-
-enum ab8500_fg_charge_state {
-       AB8500_FG_CHARGE_INIT,
-       AB8500_FG_CHARGE_READOUT,
-};
-
-static char *charge_state[] = {
-       "CHARGE_INIT",
-       "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];
-       __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
-       int pos;
-       int nbr_samples;
-       int sum;
-};
-
-struct ab8500_fg_cap_scaling {
-       bool enable;
-       int cap_to_scale[2];
-       int disable_cap_level;
-       int scaled_cap;
-};
-
-struct ab8500_fg_battery_capacity {
-       int max_mah_design;
-       int max_mah;
-       int mah;
-       int permille;
-       int level;
-       int prev_mah;
-       int prev_percent;
-       int prev_level;
-       int user_mah;
-       struct ab8500_fg_cap_scaling cap_scale;
-};
-
-struct ab8500_fg_flags {
-       bool fg_enabled;
-       bool conv_done;
-       bool charging;
-       bool fully_charged;
-       bool force_full;
-       bool low_bat_delay;
-       bool low_bat;
-       bool bat_ovv;
-       bool batt_unknown;
-       bool calibrate;
-       bool user_cap;
-       bool batt_id_received;
-};
-
-struct inst_curr_result_list {
-       struct list_head list;
-       int *result;
-};
-
-/**
- * struct ab8500_fg - ab8500 FG device information
- * @dev:               Pointer to the structure device
- * @node:              a list of AB8500 FGs, hence prepared for reentrance
- * @irq                        holds the CCEOC interrupt number
- * @vbat:              Battery voltage in mV
- * @vbat_nom:          Nominal battery voltage in mV
- * @inst_curr:         Instantenous battery current in mA
- * @avg_curr:          Average battery current in mA
- * @bat_temp           battery temperature
- * @fg_samples:                Number of samples used in the FG accumulation
- * @accu_charge:       Accumulated charge from the last conversion
- * @recovery_cnt:      Counter for recovery mode
- * @high_curr_cnt:     Counter for high current mode
- * @init_cnt:          Counter for init mode
- * @low_bat_cnt                Counter for number of consecutive low battery 
measures
- * @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
- * @turn_off_fg:       True if fg was off before current measurement
- * @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
- * @avg_cap:           Average capacity filter
- * @parent:            Pointer to the struct ab8500
- * @gpadc:             Pointer to the struct gpadc
- * @bm:                Platform specific battery management information
- * @fg_psy:            Structure that holds the FG specific battery properties
- * @fg_wq:             Work queue for running the FG algorithm
- * @fg_periodic_work:  Work to run the FG algorithm periodically
- * @fg_low_bat_work:   Work to check low bat condition
- * @fg_reinit_work     Work used to reset and reinitialise the FG algorithm
- * @fg_work:           Work to run the FG algorithm instantly
- * @fg_acc_cur_work:   Work to read the FG accumulator
- * @fg_check_hw_failure_work:  Work for checking HW state
- * @cc_lock:           Mutex for locking the CC
- * @fg_kobject:                Structure of type kobject
- */
-struct ab8500_fg {
-       struct device *dev;
-       struct list_head node;
-       int irq;
-       int vbat;
-       int vbat_nom;
-       int inst_curr;
-       int avg_curr;
-       int bat_temp;
-       int fg_samples;
-       int accu_charge;
-       int recovery_cnt;
-       int high_curr_cnt;
-       int init_cnt;
-       int low_bat_cnt;
-       int nbr_cceoc_irq_cnt;
-       bool recovery_needed;
-       bool high_curr_mode;
-       bool init_capacity;
-       bool turn_off_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;
-       struct ab8500_fg_avg_cap avg_cap;
-       struct ab8500 *parent;
-       struct ab8500_gpadc *gpadc;
-       struct abx500_bm_data *bm;
-       struct power_supply fg_psy;
-       struct workqueue_struct *fg_wq;
-       struct delayed_work fg_periodic_work;
-       struct delayed_work fg_low_bat_work;
-       struct delayed_work fg_reinit_work;
-       struct work_struct fg_work;
-       struct work_struct fg_acc_cur_work;
-       struct delayed_work fg_check_hw_failure_work;
-       struct mutex cc_lock;
-       struct kobject fg_kobject;
-};
 static LIST_HEAD(ab8500_fg_list);
 
 /**
@@ -470,7 +277,7 @@ static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, 
int sample)
  * Enable/Disable coulomb counter.
  * On failure returns negative value.
  */
-static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
+int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
 {
        int ret = 0;
        mutex_lock(&di->cc_lock);
@@ -483,11 +290,13 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg 
*di, bool enable)
                        goto cc_err;
 
                /* Program the samples */
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
-                       di->fg_samples);
-               if (ret)
-                       goto cc_err;
+               if (!di->test.enable) {
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+                               di->fg_samples);
+                       if (ret)
+                               goto cc_err;
+               }
 
                /* Start the CC */
                ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
@@ -1403,7 +1212,7 @@ static void ab8500_fg_check_capacity_limits(struct 
ab8500_fg *di, bool init)
        }
 }
 
-static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+void ab8500_fg_charge_state_to(struct ab8500_fg *di,
        enum ab8500_fg_charge_state new_state)
 {
        dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
@@ -1991,12 +1800,17 @@ 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;
-       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);
+
+       if (di->test.enable)
+               complete(&di->test.cceoc_complete);
+       else {
+               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;
 }
@@ -2011,8 +1825,31 @@ static irqreturn_t ab8500_fg_cc_data_end_handler(int 
irq, void *_di)
 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);
+
+       if (di->test.enable) {
+               complete(&di->test.cc_int_calib_complete);
+       } else {
+               di->calib_state = AB8500_FG_CALIB_END;
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cceoc_handler() - end of conversion isr.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+
+static irqreturn_t ab8500_fg_cceoc_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       if (di->test.enable)
+               complete(&di->test.cceoc_complete);
+
        return IRQ_HANDLED;
 }
 
@@ -2027,7 +1864,10 @@ static irqreturn_t ab8500_fg_cc_convend_handler(int irq, 
void *_di)
 {
        struct ab8500_fg *di = _di;
 
-       queue_work(di->fg_wq, &di->fg_acc_cur_work);
+       if (di->test.enable)
+               complete(&di->test.nconv_accu_complete);
+       else
+               queue_work(di->fg_wq, &di->fg_acc_cur_work);
 
        return IRQ_HANDLED;
 }
@@ -2613,6 +2453,7 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
        {"LOW_BAT_F", ab8500_fg_lowbatf_handler},
        {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
        {"CCEOC", ab8500_fg_cc_data_end_handler},
+       {"CCEOC", ab8500_fg_cceoc_handler},
 };
 
 static char *supply_interface[] = {
@@ -2676,6 +2517,8 @@ static int ab8500_fg_probe(struct platform_device *pdev)
        ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
        ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
 
+       ab8500_fg_test_init(di);
+
        /* Create a work queue for running the FG algorithm */
        di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
        if (di->fg_wq == NULL) {
diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
new file mode 100644
index 0000000..946840b
--- /dev/null
+++ b/drivers/power/ab8500_fg.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and 
not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.pals...@stericsson.com>
+ * Author: Karl Komierowski <karl.komierow...@stericsson.com>
+ */
+
+#define MILLI_TO_MICRO                 1000
+#define FG_LSB_IN_MA                   1627
+#define QLSB_NANO_AMP_HOURS_X10                1129
+#define INS_CURR_TIMEOUT               (3 * HZ)
+
+#define SEC_TO_SAMPLE(S)               (S * 4)
+
+#define NBR_AVG_SAMPLES                        20
+
+#define LOW_BAT_CHECK_INTERVAL         (HZ / 16) /* 62.5 ms */
+
+#define VALID_CAPACITY_SEC             (45 * 60) /* 45 minutes */
+#define BATT_OK_MIN                    2360 /* mV */
+#define BATT_OK_INCREMENT              50 /* mV */
+#define BATT_OK_MAX_NR_INCREMENTS      0xE
+
+/* FG constants */
+#define BATT_OVV                       0x01
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+       AB8500_FG_DISCHARGE_INIT,
+       AB8500_FG_DISCHARGE_INITMEASURING,
+       AB8500_FG_DISCHARGE_INIT_RECOVERY,
+       AB8500_FG_DISCHARGE_RECOVERY,
+       AB8500_FG_DISCHARGE_READOUT_INIT,
+       AB8500_FG_DISCHARGE_READOUT,
+       AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+enum ab8500_fg_charge_state {
+       AB8500_FG_CHARGE_INIT,
+       AB8500_FG_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];
+       __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+       int pos;
+       int nbr_samples;
+       int sum;
+};
+
+struct ab8500_fg_cap_scaling {
+       bool enable;
+       int cap_to_scale[2];
+       int disable_cap_level;
+       int scaled_cap;
+};
+
+struct ab8500_fg_battery_capacity {
+       int max_mah_design;
+       int max_mah;
+       int mah;
+       int permille;
+       int level;
+       int prev_mah;
+       int prev_percent;
+       int prev_level;
+       int user_mah;
+       struct ab8500_fg_cap_scaling cap_scale;
+};
+
+struct ab8500_fg_flags {
+       bool fg_enabled;
+       bool conv_done;
+       bool charging;
+       bool fully_charged;
+       bool force_full;
+       bool low_bat_delay;
+       bool low_bat;
+       bool bat_ovv;
+       bool batt_unknown;
+       bool calibrate;
+       bool user_cap;
+       bool batt_id_received;
+};
+
+struct inst_curr_result_list {
+       struct list_head list;
+       int *result;
+};
+
+/**
+ * struct ab8500_fg_test - ab8500 FG device information in test mode
+ * @enable:                    true if fg in test mode else false
+ * @cc_int_offset:             offset for internal calibration
+ * @cc_soft_offset:            offset for software calibration
+ * @cc_sample_conv:            sample read
+ * @cc_sample_conv_calib_uA:   sample converted in uA
+ * @cceoc_complete:            pointer to the struct completion, to indicate
+ *                             the completion of internal calibration and
+ *                             one sample reading
+ * @nconv_accu_complete:       pointer to the struct completion, to indicate
+ *                             the completion of sample to accumulate
+ * @cc_int_calib_complete:     pointer to the struct completion, to indicate
+ *                             the completion of internal calibration
+ * @lock:                      Mutex for locking the CC
+ */
+struct ab8500_fg_test {
+       bool enable;
+       u8 cc_int_offset;
+       u8 cc_soft_offset;
+       u16 cc_sample_conv;
+       int cc_sample_conv_calib_uA;
+       struct completion cceoc_complete;
+       struct completion nconv_accu_complete;
+       struct completion cc_int_calib_complete;
+       struct mutex lock;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev:               Pointer to the structure device
+ * @node:              a list of AB8500 FGs, hence prepared for reentrance
+ * @irq                        holds the CCEOC interrupt number
+ * @vbat:              Battery voltage in mV
+ * @vbat_nom:          Nominal battery voltage in mV
+ * @inst_curr:         Instantenous battery current in mA
+ * @avg_curr:          Average battery current in mA
+ * @bat_temp           battery temperature
+ * @fg_samples:                Number of samples used in the FG accumulation
+ * @accu_charge:       Accumulated charge from the last conversion
+ * @recovery_cnt:      Counter for recovery mode
+ * @high_curr_cnt:     Counter for high current mode
+ * @init_cnt:          Counter for init mode
+ * @low_bat_cnt                Counter for number of consecutive low battery 
measures
+ * @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
+ * @turn_off_fg:       True if fg was off before current measurement
+ * @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
+ * @avg_cap:           Average capacity filter
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @bm:                Platform specific battery management information
+ * @fg_psy:            Structure that holds the FG specific battery properties
+ * @fg_wq:             Work queue for running the FG algorithm
+ * @fg_periodic_work:  Work to run the FG algorithm periodically
+ * @fg_low_bat_work:   Work to check low bat condition
+ * @fg_reinit_work     Work used to reset and reinitialise the FG algorithm
+ * @fg_work:           Work to run the FG algorithm instantly
+ * @fg_acc_cur_work:   Work to read the FG accumulator
+ * @fg_check_hw_failure_work:  Work for checking HW state
+ * @cc_lock:           Mutex for locking the CC
+ * @fg_kobject:                Structure of type kobject
+ */
+struct ab8500_fg {
+       struct device *dev;
+       struct list_head node;
+       int irq;
+       int vbat;
+       int vbat_nom;
+       int inst_curr;
+       int avg_curr;
+       int bat_temp;
+       int fg_samples;
+       int accu_charge;
+       int recovery_cnt;
+       int high_curr_cnt;
+       int init_cnt;
+       int low_bat_cnt;
+       int nbr_cceoc_irq_cnt;
+       bool recovery_needed;
+       bool high_curr_mode;
+       bool init_capacity;
+       bool turn_off_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;
+       struct ab8500_fg_avg_cap avg_cap;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct abx500_bm_data *bm;
+       struct power_supply fg_psy;
+       struct workqueue_struct *fg_wq;
+       struct delayed_work fg_periodic_work;
+       struct delayed_work fg_low_bat_work;
+       struct delayed_work fg_reinit_work;
+       struct work_struct fg_work;
+       struct work_struct fg_acc_cur_work;
+       struct delayed_work fg_check_hw_failure_work;
+       struct mutex cc_lock;
+       struct kobject fg_kobject;
+};
+
+extern char *discharge_state[];
+extern char *charge_state[];
+
+int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
+void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+               enum ab8500_fg_charge_state new_state);
+void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+               enum ab8500_fg_charge_state new_state);
+/* test initialization */
+#ifdef CONFIG_AB8500_BM_DEEP_DEBUG
+void ab8500_fg_test_init(struct ab8500_fg *di);
+#else
+void ab8500_fg_test_init(struct ab8500_fg *di) {return; }
+#endif
diff --git a/drivers/power/ab8500_fg_deepdebug.c 
b/drivers/power/ab8500_fg_deepdebug.c
new file mode 100644
index 0000000..8845de6
--- /dev/null
+++ b/drivers/power/ab8500_fg_deepdebug.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Battery Management Deep debug support
+ *
+ * Note: Deep debug features are needed to perform the
+ * HW validation of the platform
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Cedric Madianga <cedric.madia...@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+#include "ab8500_fg.h"
+
+/* Exposure to the debugfs interface for test purpose only */
+
+/**
+ * ab8500_fg_test_algorithm_en() - enable or disable gas gauge test mode
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable gas gaude test mode
+ *
+ * Return 0 or error code
+ * Only used for test purpose
+ */
+int ab8500_fg_test_algorithm_en(struct ab8500_fg *di, bool enable)
+{
+       int ret = 0;
+
+       if (enable) {
+               /* Set coulomb counter in test mode. */
+               dev_dbg(di->dev, "Try to put gas gauge in test mode\n");
+               cancel_delayed_work_sync(&di->fg_periodic_work);
+               if (di->flags.fg_enabled) {
+                       ret = ab8500_fg_coulomb_counter(di, false);
+                       if (ret)
+                               return ret;
+               }
+               di->test.enable = true;
+               dev_dbg(di->dev, "Gas gauge in test mode\n");
+       } else {
+               /* Set coulomb counter in normal mode. */
+               dev_dbg(di->dev, "Try to put gas gauge in normal mode\n");
+               if (di->flags.fg_enabled) {
+                       ret = ab8500_fg_coulomb_counter(di, false);
+                       if (ret)
+                               return ret;
+               }
+
+               di->init_capacity = true;
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+
+               di->flags.batt_unknown = true;
+               di->flags.batt_id_received = false;
+
+               di->test.enable = false;
+               ab8500_fg_coulomb_counter(di, true);
+
+               di->flags.calibrate = true;
+               di->calib_state = AB8500_FG_CALIB_INIT;
+               dev_dbg(di->dev, "Gas gauge in normal mode\n");
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_is_test_is_algorithm_en() -
+ * Return 1 if fg algorithm is enable 0 else
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Only used for test purpose
+ */
+bool ab8500_fg_test_is_algorithm_en(struct ab8500_fg *di)
+{
+       return di->test.enable;
+}
+
+/**
+ * ab8500_fg_test_en() - enable coulomb counter
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+
+int ab8500_fg_test_en(struct ab8500_fg *di, bool enable)
+{
+       return ab8500_fg_coulomb_counter(di, enable);
+}
+
+/**
+ * ab8500_fg_test_is_en() - Return 1 if fg is enabled 0 else
+ * @di:                pointer to the ab8500_fg structure
+ *
+ *  Only used for test purpose
+ */
+bool ab8500_fg_test_is_en(struct ab8500_fg *di)
+{
+       return di->flags.fg_enabled;
+}
+
+/**
+ * ab8500_fg_test_set_cc_int_n_avg() - set number of conversion to average for
+ * internal calibration
+ * @di:                pointer to the ab8500_fg structure
+ * @val:       number of conversion to average
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_cc_int_n_avg(struct ab8500_fg *di, u8 val)
+{
+       int ret;
+       u8 cc_int_n_avg = 0;
+
+       switch (val) {
+       case 4:
+               cc_int_n_avg = CC_INT_CAL_SAMPLES_4;
+               break;
+       case 8:
+               cc_int_n_avg = CC_INT_CAL_SAMPLES_8;
+               break;
+       case 16:
+               cc_int_n_avg = CC_INT_CAL_SAMPLES_16;
+               break;
+       default:
+               dev_err(di->dev,
+                               "incorrect sample values\n"
+                               "correct sample values should be 4, 8 or 16\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_n_avg);
+       if (ret < 0)
+               dev_err(di->dev,
+                               "set number of conversion to average failed\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_cc_int_n_avg() - get number of conversion to average for
+ * internal calibration
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return number of conversion to average or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_cc_int_n_avg(struct ab8500_fg *di)
+{
+       int ret;
+       u8 val = 0;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_CTRL_REG,  &val);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "get number of conversion to average failed\n");
+               return ret;
+       }
+
+       switch (val & CC_INT_CAL_N_AVG_MASK) {
+       case CC_INT_CAL_SAMPLES_4:
+               ret = 4;
+               break;
+       case CC_INT_CAL_SAMPLES_8:
+               ret = 8;
+               break;
+       case CC_INT_CAL_SAMPLES_16:
+               ret = 16;
+               break;
+       case CC_INT_CAL_N_AVG_MASK:
+               ret = 16;
+               break;
+       default:
+               dev_err(di->dev,
+                       "incorrect val read in AB8500_GASG_CC_CTRL_REG");
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_int_calib() - launch internal calibration
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return result of calibration or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_int_calib(struct ab8500_fg *di)
+{
+       int ret;
+       u8 val;
+
+       mutex_lock(&di->test.lock);
+       dev_dbg(di->dev, "Internal calibration ongoing...\n");
+
+       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) {
+               dev_err(di->dev,
+                       "enabling offset average computation failed\n");
+               goto err;
+       }
+
+       /* wait for completion of calibration */
+       if (!wait_for_completion_timeout(&di->test.cc_int_calib_complete,
+                               5*HZ)) {
+               dev_err(di->dev,
+                       "timeout: didn't receive CCIntCalib interrupt\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_CNTR_AVGOFF_REG,  &val);
+       if (ret < 0)
+               goto err;
+
+       di->test.cc_int_offset = val;
+       dev_dbg(di->dev, "Internal Calibration done...\n");
+       mutex_unlock(&di->test.lock);
+
+       return di->test.cc_int_offset;
+
+err:
+       mutex_unlock(&di->test.lock);
+       dev_err(di->dev, "Internal calibration failure\n");
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_soft_calib() - launch software calibration
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return result of calibration or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_soft_calib(struct ab8500_fg *di)
+{
+       int ret;
+       u8 low_data, high_data;
+
+       mutex_lock(&di->test.lock);
+       dev_dbg(di->dev, "Software calibration ongoing...\n");
+
+       /* Set ADconverter in calibration mode */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+               CC_CALIB, CC_CALIB);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "set ADconverter in calibration mode failed\n");
+               goto err;
+       }
+
+       /* wait for completion of calibration */
+       if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+               dev_err(di->dev,
+                       "timeout: didn't receive CCEOC interrupt\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+               dev_err(di->dev,
+                       "timeout: didn't receive CCEOC interrupt\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* Don't set ADConverter in calibration mode */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+               CC_CALIB, 0x00);
+       if (ret < 0) {
+               dev_err(di->dev, "stopping calibration mode failed\n");
+               goto err;
+       }
+
+       /* Transfer sample and accumulator values */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+               READ_REQ, READ_REQ);
+       if (ret < 0) {
+               dev_err(di->dev, "transfer accumulator data failed\n");
+               goto err;
+       }
+
+       /* Retrieve sample conversion */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVL_REG, &low_data);
+       if (ret < 0) {
+               dev_err(di->dev, "read low byte sample conversion failed\n");
+               goto err;
+       }
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVH_REG, &high_data);
+       if (ret < 0) {
+               dev_err(di->dev, "read high byte sample conversion failed\n");
+               goto err;
+       }
+
+       di->test.cc_soft_offset = (high_data << 8) | low_data;
+       dev_dbg(di->dev, "Software Calibration done...\n");
+       mutex_unlock(&di->test.lock);
+
+       return di->test.cc_soft_offset;
+
+err:
+       mutex_unlock(&di->test.lock);
+       dev_err(di->dev, "Software calibration failure\n");
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_set_cc_soft_offset() - set software offset into register
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    manual offset to be stored
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_cc_soft_offset(struct ab8500_fg *di, u8 val)
+{
+       int ret;
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+                       AB8500_GASG_CC_OFFSET_REG, val);
+       if (ret < 0)
+               dev_err(di->dev,
+                               "set software offset failed\n");
+       else
+               di->test.cc_soft_offset = val;
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_cc_soft_offset() - get software offset into register
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    manual offset to be stored
+ *
+ * Return software offset or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_cc_soft_offset(struct ab8500_fg *di, u8 *val)
+{
+       int ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+                       AB8500_GASG_CC_OFFSET_REG,  val);
+       if (ret < 0)
+               dev_err(di->dev,
+                               "get software offset failed\n");
+       else
+               di->test.cc_soft_offset = *val;
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_set_rst_accu_sample_counter() - set reset accumulator
+ * sample counter bit
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable reset acc
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_rst_accu_sample_counter(struct ab8500_fg *di,
+               bool enable)
+{
+       int ret;
+       u8 val = 0;
+
+       if (enable)
+               val = RESET_ACCU;
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       RESET_ACCU, val);
+       if (ret < 0)
+               dev_err(di->dev,
+                       "set accumulator sample counter reset bit failed\n");
+
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_rst_accu_sample_counter() - get reset accumulator
+ * sample counter bit
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return reset accumulator sample counter bit or error code
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_rst_accu_sample_counter(struct ab8500_fg *di)
+{
+       u8 val = 0;
+       int ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_CTRL_REG,  &val);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "get accumulator sample counter reset bit failed\n");
+               return ret;
+       }
+
+       if (val & RESET_ACCU)
+               ret = 1;
+       else
+               ret = 0;
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_set_cc_mux_offset() - set coumlomb counter offset
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable offset
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_cc_mux_offset(struct ab8500_fg *di, bool enable)
+{
+       int ret;
+       u8 val = 0;
+
+       if (enable)
+               val = CC_MUXOFFSET;
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+               CC_MUXOFFSET, val);
+       if (ret < 0)
+               dev_err(di->dev,
+                       "set mux offset failed\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_cc_mux_offset() - get coulomb counter mux offset
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Get mux offset or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_cc_mux_offset(struct ab8500_fg *di)
+{
+       u8 val = 0;
+       int ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_CTRL_REG,  &val);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "get mux offset failed\n");
+               return ret;
+       }
+
+       if (val & CC_MUXOFFSET)
+               ret = 1;
+       else
+               ret = 0;
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_read_sample() - read one sample
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return sample or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_read_sample(struct ab8500_fg *di)
+{
+       int ret;
+       u8 low_data, high_data;
+
+       mutex_lock(&di->test.lock);
+       dev_dbg(di->dev, "Sample reading ongoing...\n");
+
+       /* wait for completion of calibration */
+       if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+               dev_err(di->dev,
+                       "timeout: didn't receive CCEOC interrupt\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+               dev_err(di->dev,
+                       "timeout: didn't receive CCEOC interrupt\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* Transfer sample and accumulator values */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+               READ_REQ, READ_REQ);
+       if (ret < 0) {
+               dev_err(di->dev, "transfer accumulator data failed\n");
+               goto err;
+       }
+
+       /* Retrieve sample conversion */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVL_REG, &low_data);
+       if (ret < 0) {
+               dev_err(di->dev, "read low byte sample conversion failed\n");
+               goto err;
+       }
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVH_REG, &high_data);
+       if (ret < 0) {
+               dev_err(di->dev, "read high byte sample conversion failed\n");
+               goto err;
+       }
+
+       di->test.cc_sample_conv = (high_data << 8) | low_data;
+
+       dev_dbg(di->dev, "Sample reading done...\n");
+       mutex_unlock(&di->test.lock);
+
+       return di->test.cc_sample_conv;
+
+err:
+       mutex_unlock(&di->test.lock);
+       dev_err(di->dev, "Sample reading failure\n");
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_sample_calibrate() - compute sample calibrated data
+ * @di:                pointer to the ab8500_fg structure
+ * @val:       raw sample
+ *
+ * Return sample calibrated value
+ * Only used for test purpose
+ */
+int ab8500_fg_test_sample_calibrate(struct ab8500_fg *di, int val)
+{
+       int ret;
+
+       ret = ab8500_fg_test_get_cc_mux_offset(di);
+       if (ret < 0)
+               return ret;
+
+       if (ret)
+               return val - di->test.cc_int_offset;
+       else
+               return val - di->test.cc_soft_offset;
+}
+
+/**
+ * ab8500_fg_test_sample_calibrate_to_uA() -  convert sample calibrated data
+ * to nuAH
+ * @di:                pointer to the ab8500_fg structure
+ * @val:       calibrate sample
+ *
+ * Return sample calibrated value
+ * Only used for test purpose
+ */
+int ab8500_fg_test_sample_calibrate_to_uA(struct ab8500_fg *di, int val)
+{
+       di->test.cc_sample_conv_calib_uA = val * QLSB_NANO_AMP_HOURS_X10;
+       return di->test.cc_sample_conv_calib_uA;
+}
+
+/**
+ * ab8500_fg_test_get_nconv_accu() - get number of conversion accumulated
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return umber of conversion accumulated or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_nconv_accu(struct ab8500_fg *di, u8 *val)
+{
+       int ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU, val);
+       if (ret < 0)
+               dev_err(di->dev,
+                       "get nb samples to be accumulated failed\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_nconv_accu_to_uA() - get number of conversion accumulated
+ * in uA
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return umber of conversion accumulated or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_nconv_accu_to_uA(struct ab8500_fg *di, int val)
+{
+       return val * di->test.cc_sample_conv_calib_uA;
+}
+
+/**
+ * ab8500_fg_test_set_rst_nconv_accu() -  allows to reset the 21bits 
accumulator data
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable to reset the 21bits accumulator data
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_rst_nconv_accu(struct ab8500_fg *di,
+               bool enable)
+{
+       int ret;
+       u8 val = 0;
+
+       if (enable)
+               val = RESET_ACCU;
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL,
+               RESET_ACCU, val);
+       if (ret < 0)
+               dev_err(di->dev,
+                       "set accumulator reset bit failed\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_rst_nconv_accu() - get staus of ResetNconvAccu bit
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return accumulator reset bit or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_rst_nconv_accu(struct ab8500_fg *di)
+{
+       u8 val = 0;
+       int ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_CTRL,  &val);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "get accumulator reset bit failedd\n");
+               goto out;
+       }
+
+       if (val & RESET_ACCU)
+               ret = 1;
+       else
+               ret = 0;
+out:
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_set_nconv_accu_nb_sample() - set number of sample conversion
+ * to be accumulated in 21bits accumulator
+ * @di:                pointer to the ab8500_fg structure
+ * @nb_sample: number of samples
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_nconv_accu_nb_sample(struct ab8500_fg *di, u8 val)
+{
+       int ret;
+
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU, val);
+       if (ret < 0)
+               dev_err(di->dev,
+                       "set number of samples to accumulated failed\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_get_nconv_accu_nb_sample() - get number of sample conversion
+ * to be accumulated in 21bits accumulator
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return number of samples to be accumulated or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di, u8 *val)
+{
+       int ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU,  val);
+       if (ret < 0)
+               dev_err(di->dev,
+                       "get number of samples to accumulated failed\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_test_read_nconv_accu_sample() - read of accumulator after N 
samples
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return sample or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di)
+{
+       int ret;
+       int nb_sample;
+       u8 low_data, med_data, high_data;
+
+       /* Get nb sample to average */
+       ret = ab8500_fg_test_get_nconv_accu_nb_sample(di, &nb_sample);
+       if (ret < 0)
+               goto out;
+
+       mutex_lock(&di->test.lock);
+       dev_dbg(di->dev, "N Samples reading ongoing...\n");
+
+       /* Launch measure */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL,
+               RD_NCONV_ACCU_REQ, RD_NCONV_ACCU_REQ);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "launch measure failed\n");
+               goto err;
+       }
+
+       /* wait for completion of measure */
+       if (!wait_for_completion_timeout(&di->test.nconv_accu_complete,
+                               nb_sample*(HZ/4))) {
+               dev_err(di->dev,
+                       "timeout: didn't receive NCONV_ACCU interrupt\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* Retrieve samples */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_LOW,  &low_data);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "read low data failed\n");
+               goto err;
+       }
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_MED,  &med_data);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "read med data failed\n");
+               goto err;
+       }
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_HIGH, &high_data);
+       if (ret < 0) {
+               dev_err(di->dev,
+                       "read high data failed\n");
+               goto err;
+       }
+
+       dev_dbg(di->dev, "N Samples reading done...\n");
+       mutex_unlock(&di->test.lock);
+
+       return (high_data << 16) | (med_data << 8) | low_data;
+
+err:
+       mutex_unlock(&di->test.lock);
+       dev_err(di->dev, "Sample reading failure\n");
+out:
+       return ret;
+
+}
+
+/**
+ * ab8500_fg_test_read_nconv_accu_sample_to_uA - convert accu read in uA
+ * @di:                pointer to the ab8500_fg structure
+ * @val:       accu read
+ *
+ * Return sample or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg *di, int val)
+{
+       return val * QLSB_NANO_AMP_HOURS_X10;
+}
+
+void __devinit ab8500_fg_test_init(struct ab8500_fg *di)
+{
+       /* Initialize objects need for test purpose. */
+       di->test.enable = false;
+       di->test.cc_int_offset = 0;
+       di->test.cc_soft_offset = 0;
+       di->test.cc_sample_conv = 0;
+       di->test.cc_sample_conv_calib_uA = 0;
+       init_completion(&di->test.cceoc_complete);
+       init_completion(&di->test.nconv_accu_complete);
+       init_completion(&di->test.cc_int_calib_complete);
+       mutex_init(&di->test.lock);
+}
+
diff --git a/include/linux/mfd/abx500/ab8500-bm.h 
b/include/linux/mfd/abx500/ab8500-bm.h
index ec796c7..b800332 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -485,4 +485,168 @@ static inline int ab8500_fg_inst_curr_finalize(struct 
ab8500_fg *di, int *res)
 }
 
 #endif
+
+#ifdef CONFIG_AB8500_BM_DEEP_DEBUG
+int ab8500_fg_test_algorithm_en(struct ab8500_fg *di, bool enable);
+bool ab8500_fg_test_is_algorithm_en(struct ab8500_fg *di);
+int ab8500_fg_test_en(struct ab8500_fg *di, bool enable);
+bool ab8500_fg_test_is_en(struct ab8500_fg *di);
+int ab8500_fg_test_set_cc_int_n_avg(struct ab8500_fg *di, u8 val);
+int ab8500_fg_test_get_cc_int_n_avg(struct ab8500_fg *di);
+int ab8500_fg_test_int_calib(struct ab8500_fg *di);
+int ab8500_fg_test_soft_calib(struct ab8500_fg *di);
+int ab8500_fg_test_set_cc_soft_offset(struct ab8500_fg *di, u8 val);
+int ab8500_fg_test_get_cc_soft_offset(struct ab8500_fg *di, u8 *val);
+int ab8500_fg_test_set_rst_accu_sample_counter(struct ab8500_fg *di,
+               bool enable);
+int ab8500_fg_test_get_rst_accu_sample_counter(struct ab8500_fg *di);
+int ab8500_fg_test_set_cc_mux_offset(struct ab8500_fg *di, bool enable);
+int ab8500_fg_test_get_cc_mux_offset(struct ab8500_fg *di);
+int ab8500_fg_test_read_sample(struct ab8500_fg *di);
+int ab8500_fg_test_sample_calibrate(struct ab8500_fg *di, int val);
+int ab8500_fg_test_sample_calibrate_to_uA(struct ab8500_fg *di, int val);
+int ab8500_fg_test_get_nconv_accu(struct ab8500_fg *di, u8 *val);
+int ab8500_fg_test_get_nconv_accu_to_uA(struct ab8500_fg *di, int val);
+int ab8500_fg_test_set_rst_nconv_accu(struct ab8500_fg *di,
+               bool enable);
+int ab8500_fg_test_get_rst_nconv_accu(struct ab8500_fg *di);
+int ab8500_fg_test_set_nconv_accu_nb_sample(struct ab8500_fg *di, u8 val);
+int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di, u8 *val);
+int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di);
+int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg *di, int val);
+#else
+static inline int ab8500_fg_test_algorithm_en(struct ab8500_fg *di, bool 
enable)
+{
+       return -ENODEV;
+}
+
+static inline bool ab8500_fg_test_is_algorithm_en(struct ab8500_fg *di)
+{
+       return false;
+}
+
+static inline int ab8500_fg_test_en(struct ab8500_fg *di, bool enable)
+{
+       return -ENODEV;
+}
+
+static inline bool ab8500_fg_test_is_en(struct ab8500_fg *di)
+{
+       return false;
+}
+
+static inline int ab8500_fg_test_set_cc_int_n_avg(struct ab8500_fg *di, u8 val)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_cc_int_n_avg(struct ab8500_fg *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_int_calib(struct ab8500_fg *di)
+{
+       return -ENODEV;
+}
+
+static inline int ab8500_fg_test_soft_calib(struct ab8500_fg *di)
+{
+       return -ENODEV;
+}
+
+static inline int ab8500_fg_test_set_cc_soft_offset(struct ab8500_fg *di,
+               u8 val)
+{
+       return 0;
+}
+static inline int ab8500_fg_test_get_cc_soft_offset(struct ab8500_fg *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_set_rst_accu_sample_counter(struct ab8500_fg
+               *di, bool enable)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_rst_accu_sample_counter(struct ab8500_fg
+               *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_set_cc_mux_offset(struct ab8500_fg *di,
+               bool enable)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_cc_mux_offset(struct ab8500_fg *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_read_sample(struct ab8500_fg *di)
+{
+       return -ENODEV;
+}
+
+static inline int ab8500_fg_test_sample_calibrate(struct ab8500_fg *di, int 
val)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_sample_calibrate_to_uA(struct ab8500_fg *di,
+               int val)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_nconv_accu(struct ab8500_fg *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_nconv_accu_to_uA(struct ab8500_fg *di,
+               int val)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_set_rst_nconv_accu(struct ab8500_fg *di,
+               bool enable)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_rst_nconv_accu(struct ab8500_fg *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_set_nconv_accu_nb_sample(struct ab8500_fg *di,
+               u8 val)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di)
+{
+       return 0;
+}
+
+static inline int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di)
+{
+       return -ENODEV;
+}
+
+static inline int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg
+               *di, int val)
+{
+       return 0;
+}
+
+#endif
 #endif /* _AB8500_BM_H */
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to