From patchwork Mon Jan 21 12:03:48 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lee Jones X-Patchwork-Id: 2011941 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 670643FD1A for ; Mon, 21 Jan 2013 12:17:00 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TxGFk-00033u-Tw; Mon, 21 Jan 2013 12:13:28 +0000 Received: from mail-wi0-f182.google.com ([209.85.212.182]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TxG8X-0006ZN-7n for linux-arm-kernel@lists.infradead.org; Mon, 21 Jan 2013 12:06:38 +0000 Received: by mail-wi0-f182.google.com with SMTP id hn14so4854592wib.3 for ; Mon, 21 Jan 2013 04:04:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=lF1A5TcAAtCQSOGsw+QJxiUAWjZxh4YZG7nk7j+YU+0=; b=MnZrsqVjuC9ikLdTAD4dsj+akWCF9glua2AS5YJ3L0RTFnVPm5oTion61vGbdkWYvY CnUbnbD1pQ8rZHsbzYC8uuWOsR4LQsYRn+R/D+A9Kp5Q0SMM0Q8DpgVtshoEl3avpYdC Sm8lqZ2EUcDxD1ggSBDU26n8K71pgZRnaUhYX3LNaKGoEO73SA2T34M427Wl6BzhS9Ll fm3GO/4GIpbuyn6V7A/y4xXG2Pxv7gxFTLN97TeW+rIjcmbpoBtzlK6ucxSig9pHYKNU 3zbjFmgh3bgn17t84wSvLSUqekPRWe/FHjxMwzukgY2nsK3k0Xo8rRIq0Gkb6w57mL3F CDjw== X-Received: by 10.180.82.170 with SMTP id j10mr15009329wiy.2.1358769880429; Mon, 21 Jan 2013 04:04:40 -0800 (PST) Received: from localhost.localdomain (cpc34-aztw25-2-0-cust250.18-1.cable.virginmedia.com. [86.16.136.251]) by mx.google.com with ESMTPS id i2sm16575305wiw.3.2013.01.21.04.04.38 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 21 Jan 2013 04:04:39 -0800 (PST) From: Lee Jones To: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 12/24] ab8500-fg: Add test interface for u9540 Date: Mon, 21 Jan 2013 12:03:48 +0000 Message-Id: <1358769840-4763-13-git-send-email-lee.jones@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1358769840-4763-1-git-send-email-lee.jones@linaro.org> References: <1358769840-4763-1-git-send-email-lee.jones@linaro.org> X-Gm-Message-State: ALoCoQmM5vtI4JuDN3nu8Qa+tdCY1NFOa88hv3M8fqG4deCINEFTy6tQvShDuzry20IKokqTediy X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130121_070601_860767_97BD2248 X-CRM114-Status: GOOD ( 26.57 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.182 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: linus.walleij@stericsson.com, arnd@arndb.de, cbou@mail.ru, Loic Pallardy , Michel JAOUEN , anton.vorontsov@linaro.org, Lee Jones X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Michel JAOUEN 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 Signed-off-by: Loic Pallardy Reviewed-by: Michel JAOUEN Reviewed-by: Marcus COOPER Reviewed-by: Jonas ABERG Tested-by: Michel JAOUEN Tested-by: Jonas ABERG --- 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 #include #include +#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 + * Author: Karl Komierowski + */ + +#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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */