summaryrefslogtreecommitdiff
path: root/drivers/power/sprd_2713_fgu.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/power/sprd_2713_fgu.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/power/sprd_2713_fgu.c')
-rw-r--r--drivers/power/sprd_2713_fgu.c1193
1 files changed, 1193 insertions, 0 deletions
diff --git a/drivers/power/sprd_2713_fgu.c b/drivers/power/sprd_2713_fgu.c
new file mode 100644
index 00000000..036612f6
--- /dev/null
+++ b/drivers/power/sprd_2713_fgu.c
@@ -0,0 +1,1193 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/wakelock.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/adc.h>
+
+#include "sprd_2713_fgu.h"
+#include "sprd_battery.h"
+
+#define REGS_FGU_BASE ANA_FPU_INT_BASE
+
+/*
+ */
+/* registers definitions for controller REGS_FGU */
+#define REG_FGU_START SCI_ADDR(REGS_FGU_BASE, 0x0000)
+#define REG_FGU_CONFIG SCI_ADDR(REGS_FGU_BASE, 0x0004)
+#define REG_FGU_ADC_CONFIG SCI_ADDR(REGS_FGU_BASE, 0x0008)
+#define REG_FGU_STATUS SCI_ADDR(REGS_FGU_BASE, 0x000c)
+#define REG_FGU_INT_EN SCI_ADDR(REGS_FGU_BASE, 0x0010)
+#define REG_FGU_INT_CLR SCI_ADDR(REGS_FGU_BASE, 0x0014)
+#define REG_FGU_INT_RAW SCI_ADDR(REGS_FGU_BASE, 0x0018)
+#define REG_FGU_INT_STS SCI_ADDR(REGS_FGU_BASE, 0x001c)
+#define REG_FGU_VOLT_VAL SCI_ADDR(REGS_FGU_BASE, 0x0020)
+#define REG_FGU_OCV_VAL SCI_ADDR(REGS_FGU_BASE, 0x0024)
+#define REG_FGU_POCV_VAL SCI_ADDR(REGS_FGU_BASE, 0x0028)
+#define REG_FGU_CURT_VAL SCI_ADDR(REGS_FGU_BASE, 0x002c)
+#define REG_FGU_HIGH_OVER SCI_ADDR(REGS_FGU_BASE, 0x0030)
+#define REG_FGU_LOW_OVER SCI_ADDR(REGS_FGU_BASE, 0x0034)
+#define REG_FGU_VTHRE_HH SCI_ADDR(REGS_FGU_BASE, 0x0038)
+#define REG_FGU_VTHRE_HL SCI_ADDR(REGS_FGU_BASE, 0x003c)
+#define REG_FGU_VTHRE_LH SCI_ADDR(REGS_FGU_BASE, 0x0040)
+#define REG_FGU_VTHRE_LL SCI_ADDR(REGS_FGU_BASE, 0x0044)
+#define REG_FGU_OCV_LOCKLO SCI_ADDR(REGS_FGU_BASE, 0x0048)
+#define REG_FGU_OCV_LOCKHI SCI_ADDR(REGS_FGU_BASE, 0x004c)
+#define REG_FGU_CLBCNT_SETH SCI_ADDR(REGS_FGU_BASE, 0x0050)
+#define REG_FGU_CLBCNT_SETL SCI_ADDR(REGS_FGU_BASE, 0x0054)
+#define REG_FGU_CLBCNT_DELTH SCI_ADDR(REGS_FGU_BASE, 0x0058)
+#define REG_FGU_CLBCNT_DELTL SCI_ADDR(REGS_FGU_BASE, 0x005c)
+#define REG_FGU_CLBCNT_LASTOCVH SCI_ADDR(REGS_FGU_BASE, 0x0060)
+#define REG_FGU_CLBCNT_LASTOCVL SCI_ADDR(REGS_FGU_BASE, 0x0064)
+#define REG_FGU_CLBCNT_VALH SCI_ADDR(REGS_FGU_BASE, 0x0068)
+#define REG_FGU_CLBCNT_VALL SCI_ADDR(REGS_FGU_BASE, 0x006c)
+#define REG_FGU_CLBCNT_QMAXH SCI_ADDR(REGS_FGU_BASE, 0x0070)
+#define REG_FGU_CLBCNT_QMAXL SCI_ADDR(REGS_FGU_BASE, 0x0074)
+#define REG_FGU_QMAX_TOSET SCI_ADDR(REGS_FGU_BASE, 0x0078)
+#define REG_FGU_QMAX_TIMER SCI_ADDR(REGS_FGU_BASE, 0x007c)
+#define REG_FGU_RELAX_CURT_THRE SCI_ADDR(REGS_FGU_BASE, 0x0080)
+#define REG_FGU_RELAX_CNT_THRE SCI_ADDR(REGS_FGU_BASE, 0x0084)
+#define REG_FGU_RELAX_CNT SCI_ADDR(REGS_FGU_BASE, 0x0088)
+#define REG_FGU_OCV_LAST_CNT SCI_ADDR(REGS_FGU_BASE, 0x008c)
+#define REG_FGU_CURT_OFFSET SCI_ADDR(REGS_FGU_BASE, 0x0090)
+
+#define REG_FGU_USER_AREA_SET SCI_ADDR(REGS_FGU_BASE, 0x00A0)
+#define REG_FGU_USER_AREA_CLEAR SCI_ADDR(REGS_FGU_BASE, 0x00A4)
+#define REG_FGU_USER_AREA_STATUS SCI_ADDR(REGS_FGU_BASE, 0x00A8)
+
+#define BITS_POWERON_TYPE(_x_) ( (_x_) << 12 & (0xF000))
+#define BITS_RTC_AREA(_x_) ( (_x_) << 0 & (0xFFF) )
+
+/* bits definitions for register REG_FGU_START */
+#define BIT_QMAX_UPDATE_EN ( BIT(2) )
+#define BIT_FGU_RESET ( BIT(1) )
+#define BIT_WRITE_SELCLB_EN ( BIT(0) )
+
+/* bits definitions for register REG_FGU_CONFIG */
+#define BIT_VOLT_H_VALID ( BIT(12) )
+#define BIT_FGU_DISABLE_EN ( BIT(11) )
+#define BIT_CLBCNT_DELTA_MODE ( BIT(10) )
+#define BITS_ONEADC_DUTY(_x_) ( (_x_) << 8 & (BIT(8)|BIT(9)) )
+#define BIT_CURT_DUTY ( BIT(7) )
+#define BITS_VOLT_DUTY(_x_) ( (_x_) << 5 & (BIT(5)|BIT(6)) )
+#define BIT_AD1_ENABLE ( BIT(4) )
+#define BIT_SW_DIS_CURT ( BIT(3) )
+#define BIT_FORCE_LOCK_EN ( BIT(2) )
+#define BIT_LOW_POWER_MODE ( BIT(1) )
+#define BIT_AUTO_LOW_POWER ( BIT(0) )
+
+/* bits definitions for register REG_FGU_ADC_CONFIG */
+#define BIT_FORCE_AD1_VIN_EN ( BIT(7) )
+#define BIT_FORCE_AD0_VIN_EN ( BIT(6) )
+#define BIT_FORCE_AD0_IIN_EN ( BIT(5) )
+#define BIT_FORCE_AD_EN ( BIT(4) )
+#define BIT_AD1_VOLT_REF ( BIT(3) )
+#define BIT_AD0_VOLT_REF ( BIT(2) )
+#define BIT_AD01_RESET ( BIT(1) )
+#define BIT_AD01_PD ( BIT(0) )
+
+/* bits definitions for register REG_FGU_STATUS */
+#define BIT_POWER_LOW ( BIT(5) )
+#define BIT_CURT_LOW ( BIT(4) )
+#define BITS_OCV_LOCK_STS(_x_) ( (_x_) << 2 & (BIT(2)|BIT(3)) )
+#define BIT_QMAX_UPDATE_STS ( BIT(1) )
+#define BIT_WRITE_ACTIVE_STS ( BIT(0) )
+
+/* bits definitions for register REG_FGU_INT_EN */
+#define BIT_CURT_RDEN_INT ( BIT(7) )
+#define BIT_VOLT_RDEN_INT ( BIT(6) )
+#define BIT_QMAX_UPD_TOUT ( BIT(5) )
+#define BIT_QMAX_UPD_DONE ( BIT(4) )
+#define BIT_RELX_CNT_INT ( BIT(3) )
+#define BIT_CLBCNT_DELTA_INT ( BIT(2) )
+#define BIT_VOLT_HIGH_INT ( BIT(1) )
+#define BIT_VOLT_LOW_INT ( BIT(0) )
+
+#define BITS_VOLT_VALUE(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) )
+
+/* bits definitions for register REG_FGU_CURT_VAL */
+#define BITS_CURT_VALUE(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)) )
+
+/* bits definitions for register REG_FGU_CLBCNT_SETH */
+#define BITS_CLBCNT_SETH(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)) )
+
+/* bits definitions for register REG_FGU_CLBCNT_SETL */
+#define BITS_CLBCNT_SETL(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) )
+
+/* bits definitions for register REG_FGU_CLBCNT_DELTHAH */
+#define BITS_CLBCNT_DELTHH(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)) )
+
+/* bits definitions for register REG_FGU_CLBCNT_DELTAL */
+#define BITS_CLBCNT_DELTHL(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) )
+
+/* bits definitions for register REG_FGU_RELAX_CURT_THRE */
+#define BITS_RELAX_CUR_THRE(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)) )
+
+/* bits definitions for register REG_FGU_RELAX_CNT_THRE */
+#define BITS_RELAX_CNT_THRE(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) )
+
+/* bits definitions for register REG_FGU_RELAX_CNT */
+#define BITS_RELAX_CNT_VAL(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) )
+
+/* bits definitions for register REG_FGU_OCV_LAST_CNT */
+#define BITS_OCV_LAST_CNT(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) )
+
+/* bits definitions for register REG_FGU_CURT_OFFSET */
+#define BITS_CURT_OFFSET_VAL(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)) )
+
+#define SPRDFGU__DEBUG
+#ifdef SPRDFGU__DEBUG
+#define FGU_DEBUG(format, arg...) do{\
+ pr_info("sprdfgu: " format, ## arg);\
+ }while(0)
+#else
+#define FGU_DEBUG(format, arg...)
+#endif
+
+#define SPRDFGU_OCV_VALID_TIME 20
+
+#define CUR_0ma_IDEA_ADC 8192
+
+#define FGU_CUR_SAMPLE_HZ 2
+
+#define FIRST_POWERTON 0xF
+#define NORMAIL_POWERTON 0x5
+#define WDG_POWERTON 0xA
+struct sprdfgu_drivier_data {
+ struct sprd_battery_platform_data *pdata;
+ int adp_status;
+ int warning_cap;
+ int shutdown_vol;
+ int bat_full_vol;
+ int cur_rint;
+ int poweron_rint;
+ int init_cap;
+ int init_clbcnt;
+ unsigned int int_status;
+ struct delayed_work fgu_irq_work;
+ struct power_supply sprdfgu;
+ struct mutex lock;
+ struct wake_lock low_power_lock;
+};
+
+struct sprdfgu_drivier_data sprdfgu_data;
+struct sprdfgu_cal {
+ int cur_1000ma_adc;
+ int vol_1000mv_adc;
+ int cur_offset;
+ int vol_offset;
+ int cal_type;
+};
+static struct sprdfgu_cal fgu_cal = { 2872, 678, 0, 0, SPRDBAT_FGUADC_CAL_NO };
+
+/*for debug*/
+struct delayed_work sprdfgu_debug_work;
+uint32_t sprdfgu_debug_log_time = 120;
+static int poweron_clbcnt;
+static u32 start_time;
+/*for debug end*/
+
+static int fgu_nv_4200mv = 2752;
+static int fgu_nv_3600mv = 2374;
+static int fgu_0_cur_adc = 8338;
+
+static int cmd_vol_raw, cmd_cur_raw;
+
+static BLOCKING_NOTIFIER_HEAD(fgu_chain_head);
+extern int in_calibration(void);
+
+#define REG_SYST_VALUE ((void __iomem *)(SPRD_SYSCNT_BASE + 0x0004))
+static u32 sci_syst_read(void)
+{
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8)) //mingwei TODO
+ u32 t = __raw_readl(REG_SYST_VALUE);
+ while (t != __raw_readl(REG_SYST_VALUE))
+ t = __raw_readl(REG_SYST_VALUE);
+ return t;
+#else
+ return 0;
+#endif
+}
+
+static int sprdfgu_vol2capacity(uint32_t voltage)
+{
+ int percentum =
+ sprdbat_interpolate(voltage, sprdfgu_data.pdata->ocv_tab_size,
+ sprdfgu_data.pdata->ocv_tab);
+
+ return percentum;
+}
+
+static int __init fgu_cal_start(char *str)
+{
+ unsigned int fgu_data[3] = { 0 };
+ char *cali_data = &str[1];
+ if (str) {
+ printk("sprdfgu fgu_cal%s!\n", str);
+ sscanf(cali_data, "%d,%d,%d", &fgu_data[0], &fgu_data[1],
+ &fgu_data[2]);
+ printk("sprdfgu fgu_data: 0x%x 0x%x,0x%x!\n", fgu_data[0],
+ fgu_data[1], fgu_data[2]);
+ fgu_nv_4200mv = (fgu_data[0] >> 16) & 0xffff;
+ fgu_nv_3600mv = (fgu_data[1] >> 16) & 0xffff;
+ fgu_0_cur_adc = fgu_data[2];
+ fgu_cal.cal_type = SPRDBAT_FGUADC_CAL_NV;
+ }
+ return 1;
+}
+
+__setup("fgu_cal", fgu_cal_start);
+static int __init fgu_cmd(char *str)
+{
+ int fgu_data[2] = { 0 };
+ char *cali_data = &str[1];
+ if (str) {
+ pr_info("fgu cmd%s!\n", str);
+ sscanf(cali_data, "%d,%d", &fgu_data[0], &fgu_data[1]);
+ pr_info("fgu_cmd adc_data: 0x%x 0x%x!\n", fgu_data[0],
+ fgu_data[1]);
+ cmd_vol_raw = fgu_data[0];
+ cmd_cur_raw = fgu_data[1];
+ }
+ return 1;
+}
+
+__setup("fgu_init", fgu_cmd);
+
+static int sprdfgu_cal_init(void)
+{
+ BUG_ON(fgu_nv_4200mv <= fgu_nv_3600mv);
+ if (0 == fgu_nv_3600mv) {
+ fgu_cal.vol_1000mv_adc =
+ DIV_ROUND_CLOSEST((fgu_nv_4200mv) * 10, 42);
+ fgu_cal.vol_offset = 0;
+ fgu_cal.cur_offset = 0;
+ fgu_cal.cur_1000ma_adc =
+ DIV_ROUND_CLOSEST(fgu_cal.vol_1000mv_adc * 4 *
+ sprdfgu_data.pdata->rsense_real,
+ sprdfgu_data.pdata->rsense_spec);
+ } else {
+ fgu_cal.vol_1000mv_adc =
+ DIV_ROUND_CLOSEST((fgu_nv_4200mv - fgu_nv_3600mv) * 10, 6);
+ fgu_cal.vol_offset =
+ 0 - (fgu_nv_4200mv * 10 - fgu_cal.vol_1000mv_adc * 42) / 10;
+ fgu_cal.cur_offset = CUR_0ma_IDEA_ADC - fgu_0_cur_adc;
+ fgu_cal.cur_1000ma_adc =
+ DIV_ROUND_CLOSEST(fgu_cal.vol_1000mv_adc * 4 *
+ sprdfgu_data.pdata->rsense_real,
+ sprdfgu_data.pdata->rsense_spec);
+ }
+ if (SPRDBAT_FGUADC_CAL_CHIP == fgu_cal.cal_type) {
+ fgu_cal.vol_offset += sprdfgu_data.pdata->fgu_cal_ajust;
+ printk("sprdfgu: sprdfgu_data.pdata->fgu_cal_ajust = %d\n",
+ sprdfgu_data.pdata->fgu_cal_ajust);
+ }
+
+ printk
+ ("sprdfgu: sprdfgu_cal_init fgu_nv_4200mv = %d,fgu_nv_3600mv = %d,fgu_0_cur_adc = %d\n",
+ fgu_nv_4200mv, fgu_nv_3600mv, fgu_0_cur_adc);
+ printk
+ ("sprdfgu: sprdfgu_cal_init fgu_cal.cur_1000ma_adc = %d,fgu_cal.vol_1000mv_adc = %d,fgu_cal.vol_offset = %d,fgu_cal.cur_offset = %d\n",
+ fgu_cal.cur_1000ma_adc, fgu_cal.vol_1000mv_adc, fgu_cal.vol_offset,
+ fgu_cal.cur_offset);
+ return 0;
+}
+
+
+#ifdef CONFIG_OTP_SPRD
+int sci_efuse_fgu_cal_get(unsigned int *p_cal_data);
+#endif
+static int sprdfgu_cal_from_chip(void)
+{
+ unsigned int fgu_data[4] = { 0 };
+
+#ifdef CONFIG_OTP_SPRD
+ if (!sci_efuse_fgu_cal_get(fgu_data)) {
+ printk("sprdfgu: sprdfgu_cal_from_chip efuse no cal data\n");
+ return 1;
+ }
+#endif
+ printk("sprdfgu fgu_data: 0x%x 0x%x,0x%x,0x%x!\n", fgu_data[0],
+ fgu_data[1], fgu_data[2], fgu_data[3]);
+ printk("sprdfgu: sprdfgu_cal_from_chip\n");
+
+ fgu_nv_4200mv = fgu_data[0];
+#if defined(CONFIG_ADIE_SC2723) //2723 use one point to cal fgu adc
+ fgu_nv_3600mv = 0;
+ fgu_0_cur_adc = 0;
+#else
+ fgu_nv_3600mv = fgu_data[1];
+ fgu_0_cur_adc = fgu_data[2];
+#endif
+ fgu_cal.cal_type = SPRDBAT_FGUADC_CAL_CHIP;
+
+ return 0;
+}
+
+static u32 sprdfgu_adc2vol_mv(u32 adc)
+{
+ return ((adc + fgu_cal.vol_offset) * 1000) / fgu_cal.vol_1000mv_adc;
+}
+
+static u32 sprdfgu_vol2adc_mv(u32 vol)
+{
+ return (vol * fgu_cal.vol_1000mv_adc) / 1000 - fgu_cal.vol_offset;
+}
+
+static int sprdfgu_adc2cur_ma(int adc)
+{
+ return (adc * 1000) / fgu_cal.cur_1000ma_adc;
+}
+
+static u32 sprdfgu_cur2adc_ma(u32 cur)
+{
+ return (cur * fgu_cal.cur_1000ma_adc) / 1000;
+}
+
+#if defined(CONFIG_ADIE_SC2723S) || defined(CONFIG_ADIE_SC2723)
+static void sprdfgu_rtc_reg_write(uint32_t val)
+{
+ sci_adi_write(REG_FGU_USER_AREA_CLEAR, BITS_RTC_AREA(~val),
+ BITS_RTC_AREA(~0));
+ sci_adi_write(REG_FGU_USER_AREA_SET, BITS_RTC_AREA(val),
+ BITS_RTC_AREA(~0));
+}
+
+static uint32_t sprdfgu_rtc_reg_read(void)
+{
+ int shft = __ffs(BITS_RTC_AREA(~0));
+ return (sci_adi_read(REG_FGU_USER_AREA_STATUS) & BITS_RTC_AREA(~0)) >>
+ shft;
+}
+
+static void sprdfgu_poweron_type_write(uint32_t val)
+{
+ sci_adi_write(REG_FGU_USER_AREA_CLEAR, BITS_POWERON_TYPE(~val),
+ BITS_POWERON_TYPE(~0));
+ sci_adi_write(REG_FGU_USER_AREA_SET, BITS_POWERON_TYPE(val),
+ BITS_POWERON_TYPE(~0));
+}
+
+static uint32_t sprdfgu_poweron_type_read(void)
+{
+ int shft = __ffs(BITS_POWERON_TYPE(~0));
+ return (sci_adi_read(REG_FGU_USER_AREA_STATUS) & BITS_POWERON_TYPE(~0))
+ >> shft;
+}
+#endif
+static inline int sprdfgu_clbcnt_get(void)
+{
+ volatile int cc1 = 0, cc2 = 1;
+
+ do {
+ cc1 = (sci_adi_read(REG_FGU_CLBCNT_VALL)) & 0xFFFF;
+ cc1 |= (((sci_adi_read(REG_FGU_CLBCNT_VALH)) & 0xFFFF) << 16);
+
+ cc2 = (sci_adi_read(REG_FGU_CLBCNT_VALL)) & 0xFFFF;
+ cc2 |= (((sci_adi_read(REG_FGU_CLBCNT_VALH)) & 0xFFFF) << 16);
+ } while (cc1 != cc2);
+
+ //FGU_DEBUG("sprdfgu_clbcnt_get cc: %d\n", cc1);
+ return cc1;
+}
+
+static inline int sprdfgu_clbcnt_set(int clbcc)
+{
+ sci_adi_write(REG_FGU_CLBCNT_SETL, clbcc & 0xFFFF, ~0);
+ sci_adi_write(REG_FGU_CLBCNT_SETH, (clbcc >> 16) & 0xFFFF, ~0);
+ sci_adi_set(REG_FGU_START, BIT_WRITE_SELCLB_EN);
+ udelay(130);
+ return 0;
+}
+
+static inline int sprdfgu_reg_get(unsigned long reg)
+{
+ volatile int vaule;
+
+ do {
+ vaule = sci_adi_read(reg);
+ } while (vaule != sci_adi_read(reg));
+
+ return vaule;
+}
+
+static inline int sprdfgu_clbcnt_init(u32 capacity)
+{
+ int init_cap =
+ DIV_ROUND_CLOSEST(sprdfgu_data.pdata->cnom * capacity, 100);
+ int clbcnt =
+ DIV_ROUND_CLOSEST(init_cap * fgu_cal.cur_1000ma_adc * 36 *
+ FGU_CUR_SAMPLE_HZ, 10);
+ return clbcnt;
+}
+
+static inline void sprdfgu_soc_adjust(int capacity)
+{
+ sprdfgu_data.init_cap = capacity;
+ sprdfgu_data.init_clbcnt = sprdfgu_clbcnt_get();
+ FGU_DEBUG("sprdfgu_soc_adjust sprdfgu_data.init_cap= %d,%d\n",
+ sprdfgu_data.init_cap, sprdfgu_data.init_clbcnt);
+}
+
+uint32_t sprdfgu_read_vbat_vol(void)
+{
+ u32 cur_vol_raw;
+ uint32_t temp;
+ cur_vol_raw = sprdfgu_reg_get(REG_FGU_VOLT_VAL);
+ //FGU_DEBUG("cur_vol_raw = %d\n", cur_vol_raw);
+ temp = sprdfgu_adc2vol_mv(cur_vol_raw);
+ //FGU_DEBUG("sprdfgu_read_vbat_vol : %d\n", temp);
+ return temp;
+}
+
+static inline u32 sprdfgu_ocv_vol_get(void)
+{
+ u32 ocv_vol_raw;
+ ocv_vol_raw = sprdfgu_reg_get(REG_FGU_OCV_VAL);
+ //FGU_DEBUG("ocv_vol_raw = %x\n", ocv_vol_raw);
+ return sprdfgu_adc2vol_mv(ocv_vol_raw);
+}
+
+static inline int sprdfgu_cur_current_get(void)
+{
+ int current_raw;
+
+ current_raw = sprdfgu_reg_get(REG_FGU_CURT_VAL);
+ //FGU_DEBUG("current_raw: %d\n", current_raw);
+
+ return sprdfgu_adc2cur_ma(current_raw - CUR_0ma_IDEA_ADC);
+}
+
+int sprdfgu_read_batcurrent(void)
+{
+#ifndef CONFIG_SPRD_NOFGUCURRENT_CHG
+ int temp = sprdfgu_cur_current_get();
+ //FGU_DEBUG("sprdfgu_read_batcurrent : %d\n", temp);
+ return temp;
+#else
+ return 0;
+#endif
+}
+
+static int sprdfgu_read_vbat_ocv_pure(uint32_t * vol)
+{
+ if (sprdfgu_reg_get(REG_FGU_OCV_LAST_CNT) > SPRDFGU_OCV_VALID_TIME
+ || sprdfgu_reg_get(REG_FGU_OCV_VAL) == 0) {
+ *vol = 0;
+ return 0;
+ }
+ *vol = sprdfgu_ocv_vol_get();
+ return 1;
+}
+
+uint32_t sprdfgu_read_vbat_ocv(void)
+{
+#ifndef CONFIG_SPRD_NOFGUCURRENT_CHG
+ uint32_t vol;
+ int rint = sprdfgu_data.cur_rint;
+
+#ifdef SPRDFGU_TEMP_COMP_SOC
+ {
+ int temp;
+
+ temp = sprdbat_read_temp();
+ rint = sprdbat_interpolate(temp/10, sprdfgu_data.pdata->rint_temp_tab_size,
+ sprdfgu_data.pdata->rint_temp_tab);
+ FGU_DEBUG("rint:%d,temp:%d\n", rint, temp);
+ }
+#endif
+
+// if (sprdfgu_read_vbat_ocv_pure(&vol)) {
+// FGU_DEBUG("hwocv...\n");
+// return vol;
+// } else {
+ FGU_DEBUG("%s(): ocv = Vbat - Vrint", __func__);
+ return sprdfgu_read_vbat_vol() -
+ (sprdfgu_read_batcurrent() * rint) / 1000;
+// }
+#else
+ return sprdfgu_read_vbat_vol();
+#endif
+}
+
+static int sprdfgu_temp_comp_soc(int soc, int temp)
+{
+ int cnom_temp;
+ int comp_soc, delta_soc;
+
+ cnom_temp = 0;
+
+ cnom_temp = sprdbat_interpolate(temp, sprdfgu_data.pdata->cnom_temp_tab_size,
+ sprdfgu_data.pdata->cnom_temp_tab);
+ delta_soc =
+ (sprdfgu_data.pdata->cnom - cnom_temp) * 100 / sprdfgu_data.pdata->cnom;
+
+ comp_soc = (long)(soc - delta_soc) * 100 / (100 - delta_soc);
+
+ if (comp_soc < 0)
+ comp_soc = 0;
+
+ if (comp_soc > 100)
+ comp_soc = 100;
+
+ FGU_DEBUG("cnom_temp %d, delta_soc %d,comp_soc%d,soc %d,temp %d\n",
+ cnom_temp, delta_soc, comp_soc, soc, temp);
+
+ return (comp_soc);
+}
+
+int sprdfgu_read_soc(void)
+{
+ int cur_cc, cc_delta, capcity_delta, temp;
+ uint32_t cur_ocv;
+
+ mutex_lock(&sprdfgu_data.lock);
+
+ cur_cc = sprdfgu_clbcnt_get();
+ cc_delta = cur_cc - sprdfgu_data.init_clbcnt;
+ temp = DIV_ROUND_CLOSEST(cc_delta, (3600 * FGU_CUR_SAMPLE_HZ));
+ temp = sprdfgu_adc2cur_ma(temp);
+ FGU_DEBUG("sprdfgu_read_soc delta %dmAh,sprdfgu_data.init_clbcnt:%d\n",
+ temp, sprdfgu_data.init_clbcnt);
+ capcity_delta = DIV_ROUND_CLOSEST(temp * 100, sprdfgu_data.pdata->cnom);
+ FGU_DEBUG("sprdfgu_read_soc delta capacity %d,full capacity %d\n",
+ capcity_delta, sprdfgu_data.pdata->cnom);
+
+ capcity_delta += sprdfgu_data.init_cap;
+
+ FGU_DEBUG("sprdfgu_read_soc soc %d,sprdfgu_data.init_cap %d\n",
+ capcity_delta, sprdfgu_data.init_cap);
+
+ cur_ocv = sprdfgu_read_vbat_ocv();
+ if (cur_ocv >= sprdfgu_data.bat_full_vol) {
+ FGU_DEBUG("sprdfgu_read_soc cur_ocv %d\n", cur_ocv);
+ if (capcity_delta < 100 || capcity_delta > 102) {
+ capcity_delta = 100;
+ sprdfgu_soc_adjust(100);
+ }
+ }
+
+ if (capcity_delta > 100) {
+ capcity_delta = 100;
+ sprdfgu_soc_adjust(100);
+ }
+ if (capcity_delta <= sprdfgu_data.warning_cap
+ && cur_ocv > sprdfgu_data.pdata->alm_vol) {
+ FGU_DEBUG("sprdfgu_read_soc soc low...\n");
+ capcity_delta = sprdfgu_data.warning_cap + 1;
+ sprdfgu_soc_adjust(capcity_delta);
+ } else if (capcity_delta <= 0 && cur_ocv > sprdfgu_data.shutdown_vol) {
+ FGU_DEBUG("sprdfgu_read_soc soc 0...\n");
+ capcity_delta = 1;
+ sprdfgu_soc_adjust(capcity_delta);
+ } else if (cur_ocv < sprdfgu_data.shutdown_vol) {
+ FGU_DEBUG("sprdfgu_read_soc vol 0...\n");
+ capcity_delta = 0;
+ sprdfgu_soc_adjust(capcity_delta);
+ } else if (capcity_delta > sprdfgu_data.warning_cap
+ && cur_ocv < sprdfgu_data.pdata->alm_vol) {
+ FGU_DEBUG("sprdfgu_read_soc high...\n");
+ sprdfgu_soc_adjust(sprdfgu_vol2capacity(cur_ocv));
+ capcity_delta = sprdfgu_vol2capacity(cur_ocv);
+ }
+
+#ifdef SPRDFGU_TEMP_COMP_SOC
+ {
+ int temp;
+
+ temp = sprdbat_read_temp();
+ capcity_delta =
+ sprdfgu_temp_comp_soc(capcity_delta, temp / 10);
+ }
+#endif
+
+#ifdef SPRDFGU_TEMP_COMP_SOC
+ if (sprdfgu_read_vbat_vol() < sprdfgu_data.pdata->soft_vbat_uvlo) {
+ FGU_DEBUG("TEMP_COMP_SOC vol 0...\n");
+ capcity_delta = 0;
+ sprdfgu_soc_adjust(capcity_delta);
+ }
+#endif
+
+ mutex_unlock(&sprdfgu_data.lock);
+ return capcity_delta;
+}
+
+//for debug only
+int sprdfgu_avg_current_query(void)
+{
+ int cur_cc, cc_delta, raw_avg, temp, curr_avg, capcity;
+ u32 cur_time = sci_syst_read();
+ u32 time_delta;
+
+ cur_cc = sprdfgu_clbcnt_get();
+ time_delta = cur_time - start_time;
+ cc_delta = cur_cc - poweron_clbcnt;
+ temp = time_delta / 500;
+ raw_avg = cc_delta / temp;
+
+ //FGU_DEBUG("start_time:%d,cur_time : %d,poweron_clbcnt: 0x%x,cur_cc:0x%x\n",
+ // start_time, cur_time, poweron_clbcnt, cur_cc);
+ FGU_DEBUG("time_delta : %d,cc_delta: %d,raw_avg = %d\n", time_delta,
+ cc_delta, raw_avg);
+ curr_avg = sprdfgu_adc2cur_ma(raw_avg);
+
+ temp = time_delta / 3600;
+ capcity = temp * curr_avg;
+ FGU_DEBUG("capcity/1000 capcity = : %dmah\n", capcity);
+ return curr_avg;
+}
+
+static void sprdfgu_debug_works(struct work_struct *work)
+{
+ FGU_DEBUG("dump fgu msg s\n");
+ if (!sprdfgu_data.pdata->fgu_mode) {
+ FGU_DEBUG("avg current = %d\n", sprdfgu_avg_current_query());
+ }
+ FGU_DEBUG("vol:%d,softocv:%d,hardocv:%d,current%d,cal_type:%d\n",
+ sprdfgu_read_vbat_vol(), sprdfgu_read_vbat_ocv(),
+ sprdfgu_ocv_vol_get(), sprdfgu_cur_current_get(),
+ fgu_cal.cal_type);
+ //FGU_DEBUG("pocv_raw = 0x%x,pocv_voltage = %d\n",
+ // sci_adi_read(REG_FGU_POCV_VAL),
+ // sprdfgu_adc2vol_mv(sci_adi_read(REG_FGU_POCV_VAL)));
+ //FGU_DEBUG("REG_FGU_CURT_OFFSET--- = %d\n",
+ //sci_adi_read(REG_FGU_CURT_OFFSET));
+ //FGU_DEBUG("REG_FGU_RELAX_CURT_THRE--- = %d\n",
+ // sci_adi_read(REG_FGU_RELAX_CURT_THRE));
+ //FGU_DEBUG("REG_FGU_RELAX_CNT--- = %d\n",
+ // sci_adi_read(REG_FGU_RELAX_CNT));
+ //FGU_DEBUG("REG_FGU_OCV_LAST_CNT--- = %d\n",
+ // sci_adi_read(REG_FGU_OCV_LAST_CNT));
+ //FGU_DEBUG("REG_FGU_LOW_OVER--- = %d\n", sci_adi_read(REG_FGU_LOW_OVER));
+ //FGU_DEBUG("REG_FGU_CONFIG--- = 0x%x\n", sci_adi_read(REG_FGU_CONFIG));
+ //printk("ANA_REG_GLB_MP_MISC_CTRL 0x%x ,0x%x \n", sci_adi_read(ANA_REG_GLB_MP_MISC_CTRL), sci_adi_read(ANA_REG_GLB_DCDC_CTRL2));
+ if (!sprdfgu_data.pdata->fgu_mode) {
+ FGU_DEBUG("soc():%d\n", sprdfgu_read_soc());
+ }
+ FGU_DEBUG("dump fgu msg e\n");
+ schedule_delayed_work(&sprdfgu_debug_work, sprdfgu_debug_log_time * HZ);
+}
+
+static void sprdfgu_cal_battery_impedance(void)
+{
+ int delta_vol_raw;
+ int delta_current_raw;
+ int temp;
+ int impedance;
+
+ delta_vol_raw = sprdfgu_reg_get(REG_FGU_VOLT_VAL);
+ delta_current_raw = sprdfgu_reg_get(REG_FGU_CURT_VAL);
+#if 0 //use pocv and poci to caculate impedance
+ cmd_vol_raw = sprdfgu_reg_get(REG_FGU_POCV_VAL);
+ cmd_cur_raw = sprdfgu_reg_get(REG_FGU_CLBCNT_QMAXL) << 1;
+#endif
+ printk("sprdfgu: fgu delta_vol_raw: 0x%x delta_current_raw 0x%x!\n",
+ delta_vol_raw, delta_current_raw);
+ printk("sprdfgu: fgu cmd_vol_raw: 0x%x cmd_cur_raw 0x%x!\n",
+ cmd_vol_raw, cmd_cur_raw);
+ if (0 == cmd_vol_raw || 0 == cmd_cur_raw) {
+ printk(KERN_ERR
+ "sprdfgu: sprdfgu_cal_battery_impedance warning.....!\n");
+ return;
+ }
+ delta_vol_raw -= cmd_vol_raw;
+ delta_current_raw -= cmd_cur_raw;
+ delta_vol_raw = ((delta_vol_raw) * 1000) / fgu_cal.vol_1000mv_adc;
+ delta_current_raw = sprdfgu_adc2cur_ma(delta_current_raw);
+ printk("sprdfgu: delta vol delta_vol_raw: %d delta_current_raw %d!\n",
+ (delta_vol_raw), (delta_current_raw));
+ temp = (delta_vol_raw * 1000) / delta_current_raw;
+
+ impedance = abs(temp);
+ if (impedance > 100) {
+ sprdfgu_data.poweron_rint = impedance;
+ } else {
+ printk("sprdfgu: impedance warning: %d!\n", impedance);
+ }
+ printk("sprdfgu: fgu sprdfgu_data.poweron_rint: %d!\n",
+ sprdfgu_data.poweron_rint);
+}
+
+uint32_t sprdfgu_read_capacity(void)
+{
+ int32_t voltage;
+ int cap;
+
+ if (sprdfgu_data.pdata->fgu_mode) {
+ voltage = sprdfgu_read_vbat_ocv();
+ cap = sprdfgu_vol2capacity(voltage);
+ } else {
+ cap = sprdfgu_read_soc();
+ }
+ if (cap > 100)
+ cap = 100;
+ else if (cap < 0)
+ cap = 0;
+ return cap;
+}
+
+uint32_t sprdfgu_poweron_capacity(void)
+{
+ return sprdfgu_data.init_cap;
+}
+
+void sprdfgu_adp_status_set(int plugin)
+{
+ sprdfgu_data.adp_status = plugin;
+ if (plugin) {
+ uint32_t adc;
+ adc = sprdfgu_vol2adc_mv(sprdfgu_data.pdata->alm_vol);
+ sci_adi_set(REG_FGU_INT_CLR, BIT_VOLT_LOW_INT);
+ sci_adi_write(REG_FGU_LOW_OVER, adc & 0xFFFF, ~0);
+ sci_adi_clr(REG_FGU_INT_EN, BIT_CLBCNT_DELTA_INT);
+ }
+}
+
+static void sprdfgu_irq_works(struct work_struct *work)
+{
+ uint32_t cur_ocv;
+ uint32_t adc;
+ int cur_soc;
+
+ wake_lock_timeout(&(sprdfgu_data.low_power_lock), 2 * HZ);
+
+ printk("sprdfgu: sprdfgu_irq_works......0x%x.cur vol = %d\n",
+ sprdfgu_data.int_status, sprdfgu_read_vbat_vol());
+ if (sprdfgu_data.int_status & BIT_VOLT_HIGH_INT) {
+ blocking_notifier_call_chain(&fgu_chain_head, 1, 0);
+ }
+
+ if (sprdfgu_data.int_status & BIT_VOLT_LOW_INT) {
+ cur_soc = sprdfgu_read_soc(); //it must be at here
+ mutex_lock(&sprdfgu_data.lock);
+
+ cur_ocv = sprdfgu_read_vbat_ocv();
+ if (cur_ocv <= sprdfgu_data.shutdown_vol) {
+ printk
+ ("sprdfgu: sprdfgu_irq_works...sprdfgu_data.shutdown_vol .\n");
+ sprdfgu_soc_adjust(0);
+ } else if (cur_ocv <= sprdfgu_data.pdata->alm_vol) {
+ printk
+ ("sprdfgu: sprdfgu_irq_works...sprdfgu_data.pdata->alm_vol %d.\n",
+ cur_soc);
+ if (cur_soc > sprdfgu_data.warning_cap) {
+ sprdfgu_soc_adjust(sprdfgu_data.warning_cap);
+ } else if (cur_soc <= 0) {
+ sprdfgu_soc_adjust(sprdfgu_vol2capacity
+ (cur_ocv));
+ }
+ if (!sprdfgu_data.adp_status) {
+ adc =
+ sprdfgu_vol2adc_mv
+ (sprdfgu_data.shutdown_vol);
+ sci_adi_write(REG_FGU_LOW_OVER, adc & 0xFFFF,
+ ~0);
+ }
+ } else {
+ //todo?
+ }
+ mutex_unlock(&sprdfgu_data.lock);
+ }
+}
+
+static irqreturn_t _sprdfgu_interrupt(int irq, void *dev_id)
+{
+ sprdfgu_data.int_status = sci_adi_read(REG_FGU_INT_STS);
+ sci_adi_set(REG_FGU_INT_CLR, sprdfgu_data.int_status);
+ printk
+ ("sprdfgu: _sprdfgu_interrupt.....raw..0x%x,sprdfgu_data.int_status0x%x\n",
+ sci_adi_read(REG_FGU_INT_RAW), sprdfgu_data.int_status);
+ schedule_delayed_work(&sprdfgu_data.fgu_irq_work, 0);
+ udelay(60); //fix int bug
+ return IRQ_HANDLED;
+}
+
+static int sprdfgu_int_init(void)
+{
+ uint32_t adc;
+ int delta_cc = sprdfgu_clbcnt_init(1);
+ int ret = -ENODEV;
+
+ INIT_DELAYED_WORK(&sprdfgu_data.fgu_irq_work, sprdfgu_irq_works);
+
+ sci_adi_set(REG_FGU_INT_CLR, 0xFFFF);
+
+ adc = sprdfgu_vol2adc_mv(sprdfgu_data.pdata->chg_bat_safety_vol);
+ sci_adi_write(REG_FGU_HIGH_OVER, adc & 0xFFFF, ~0);
+ adc = sprdfgu_vol2adc_mv(sprdfgu_data.pdata->alm_vol);
+ sci_adi_write(REG_FGU_LOW_OVER, adc & 0xFFFF, ~0);
+
+ sci_adi_write(REG_FGU_CLBCNT_DELTL, delta_cc & 0xFFFF, ~0);
+ sci_adi_write(REG_FGU_CLBCNT_DELTH, (delta_cc >> 16) & 0xFFFF, ~0);
+
+ ret = request_irq(sprdfgu_data.pdata->irq_fgu, _sprdfgu_interrupt,
+ IRQF_NO_SUSPEND, "sprdfgu", NULL);
+
+ if (ret) {
+ printk(KERN_ERR "sprdfgu: request sprdfgu irq %d failed\n",
+ sprdfgu_data.pdata->irq_fgu);
+ }
+
+ sci_adi_set(REG_FGU_INT_EN, BIT_VOLT_HIGH_INT);
+ return 0;
+}
+
+void sprdfgu_pm_op(int is_suspend)
+{
+ if (is_suspend) {
+ if (!sprdfgu_data.adp_status) {
+ sci_adi_set(REG_FGU_INT_EN, BIT_VOLT_LOW_INT);
+ if (sprdfgu_read_vbat_ocv() <
+ sprdfgu_data.pdata->alm_vol) {
+ sci_adi_set(REG_FGU_INT_CLR,
+ BIT_CLBCNT_DELTA_INT);
+ sci_adi_set(REG_FGU_INT_EN,
+ BIT_CLBCNT_DELTA_INT);
+ }
+ }
+ } else {
+ sci_adi_clr(REG_FGU_INT_EN,
+ BIT_VOLT_LOW_INT | BIT_CLBCNT_DELTA_INT);
+ }
+}
+int __weak in_calibration(void){return 0;}
+static void sprdfgu_hw_init(void)
+{
+ u32 cur_vol_raw, ocv_raw;
+ int current_raw;
+ u32 pocv_raw;
+ FGU_DEBUG("FGU_Init\n");
+
+#if !defined(CONFIG_ARCH_SCX15) && !defined(CONFIG_ADIE_SC2723S) && !defined(CONFIG_ADIE_SC2723)
+ //sci_adi_set(ANA_REG_GLB_MP_MISC_CTRL, (BIT(1)));
+ //sci_adi_write(ANA_REG_GLB_DCDC_CTRL2, (4 << 8), (7 << 8));
+#endif
+
+ sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_FGU_EN);
+ sci_adi_set(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_FGU_EN | BIT_RTC_FGUA_EN);
+
+#if !defined(CONFIG_ARCH_SCX15) && !defined(CONFIG_ADIE_SC2723S) && !defined(CONFIG_ADIE_SC2723)
+ sci_adi_write(REG_FGU_CONFIG, BITS_VOLT_DUTY(3), BITS_VOLT_DUTY(3) | BIT_VOLT_H_VALID); //mingwei
+#endif
+ sci_adi_write(REG_FGU_RELAX_CURT_THRE, BITS_RELAX_CUR_THRE(sprdfgu_cur2adc_ma(sprdfgu_data.pdata->relax_current)), BITS_RELAX_CUR_THRE(~0)); //mingwei
+ udelay(130);
+ sprdfgu_cal_battery_impedance();
+ pocv_raw = sci_adi_read(REG_FGU_POCV_VAL);
+ cur_vol_raw = sprdfgu_reg_get(REG_FGU_VOLT_VAL);
+ ocv_raw = sprdfgu_reg_get(REG_FGU_OCV_VAL);
+ current_raw = sprdfgu_reg_get(REG_FGU_CURT_VAL);
+ start_time = sci_syst_read();
+
+#if defined(CONFIG_ADIE_SC2723S) ||defined(CONFIG_ADIE_SC2723)
+ FGU_DEBUG("REG_FGU_USER_AREA_STATUS- = 0x%x\n",
+ sci_adi_read(REG_FGU_USER_AREA_STATUS));
+ if ((FIRST_POWERTON == sprdfgu_poweron_type_read())
+ || (sprdfgu_rtc_reg_read() == 0xFFF) || (sprdfgu_rtc_reg_read() == 0)) {
+ FGU_DEBUG("FIRST_POWERTON- = 0x%x\n",
+ sprdfgu_poweron_type_read());
+#ifdef CONFIG_SPRD_EXT_IC_POWER
+ if(gpio_get_value(sprdfgu_data.pdata->gpio_vchg_detect)){
+ uint32_t soft_ocv = sprdfgu_read_vbat_vol() -
+ (sprdfgu_adc2cur_ma
+ (current_raw - CUR_0ma_IDEA_ADC +
+ fgu_cal.cur_offset) * sprdfgu_data.poweron_rint) / 1000;
+
+ sprdfgu_data.init_cap = sprdfgu_vol2capacity(soft_ocv);
+ FGU_DEBUG
+ ("Charger poweron soft_ocv:%d,sprdfgu_data.init_cap:%d\n",
+ soft_ocv, sprdfgu_data.init_cap);
+ } else
+#endif
+ {
+ int poci_raw =
+ sprdfgu_reg_get(REG_FGU_CLBCNT_QMAXL) << 1;
+ int poci_curr =
+ sprdfgu_adc2cur_ma(poci_raw - CUR_0ma_IDEA_ADC +
+ fgu_cal.cur_offset);
+ uint32_t p_ocv =
+ sprdfgu_adc2vol_mv(pocv_raw) -
+ (poci_curr * sprdfgu_data.poweron_rint) / 1000;
+ sprdfgu_data.init_cap = sprdfgu_vol2capacity(p_ocv);
+ FGU_DEBUG
+ ("poci_raw:0x%x,poci_current:%d,p_softocv:%d,sprdfgu_data.init_cap:%d\n",
+ poci_raw, poci_curr, p_ocv, sprdfgu_data.init_cap);
+ }
+ sprdfgu_rtc_reg_write(sprdfgu_data.init_cap);
+ } else {
+ sprdfgu_data.init_cap = sprdfgu_rtc_reg_read();
+ FGU_DEBUG("NORMAIL_POWERTON-- sprdfgu_data.init_cap= %d\n",
+ sprdfgu_data.init_cap);
+ }
+ sprdfgu_poweron_type_write(NORMAIL_POWERTON);
+
+#else
+ {
+ int cnt = 50000;
+ while((sprdfgu_read_vbat_vol() < 3000) && --cnt){
+ FGU_DEBUG("voltage not ready = %d\n", sprdfgu_read_vbat_vol());
+ udelay(100);
+ }
+ if(cnt <= 0) {
+ printk(KERN_EMERG "sprdfgu: voltage error!!!!");
+ BUG_ON(1);
+ }
+ }
+ {
+ uint32_t soft_ocv = sprdfgu_read_vbat_vol() -
+ (sprdfgu_adc2cur_ma
+ (current_raw - CUR_0ma_IDEA_ADC +
+ fgu_cal.cur_offset) * sprdfgu_data.cur_rint) / 1000;
+ sprdfgu_data.init_cap = sprdfgu_vol2capacity(soft_ocv);
+ }
+#endif
+
+ sprdfgu_data.init_clbcnt = poweron_clbcnt =
+ sprdfgu_clbcnt_init(sprdfgu_data.init_cap);
+ sprdfgu_clbcnt_set(poweron_clbcnt);
+#if !defined(CONFIG_ADIE_SC2723S) && !defined(CONFIG_ADIE_SC2723)
+ if (!in_calibration()) {
+ sci_adi_write(REG_FGU_CURT_OFFSET, fgu_cal.cur_offset, ~0);
+ }
+#endif
+
+ FGU_DEBUG("pocv_raw = 0x%x,pocv_voltage = %d\n", pocv_raw,
+ sprdfgu_adc2vol_mv(pocv_raw));
+ FGU_DEBUG("current voltage raw_data = 0x%x,cur voltage = %d\n",
+ cur_vol_raw, sprdfgu_adc2vol_mv(cur_vol_raw));
+ FGU_DEBUG("ocv_raw: 0x%x,ocv voltage = %d\n", ocv_raw,
+ sprdfgu_adc2vol_mv(ocv_raw));
+ FGU_DEBUG("current_raw: 0x%x,current = %d\n", current_raw,
+ sprdfgu_adc2cur_ma(current_raw - CUR_0ma_IDEA_ADC));
+ FGU_DEBUG("poweron_clbcnt: 0x%x,cur_cc0x%x\n", poweron_clbcnt,
+ sprdfgu_clbcnt_get());
+ FGU_DEBUG("sprdfgu_data.poweron_rint = %d\n", sprdfgu_data.poweron_rint);
+
+}
+
+int sprdfgu_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&fgu_chain_head, nb);
+}
+
+int sprdfgu_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&fgu_chain_head, nb);
+}
+
+static ssize_t sprdfgu_store_attribute(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t sprdfgu_show_attribute(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+#define SPRDFGU_ATTR(_name) \
+{ \
+ .attr = { .name = #_name, .mode = S_IRUGO | S_IWUSR | S_IWGRP, }, \
+ .show = sprdfgu_show_attribute, \
+ .store = sprdfgu_store_attribute, \
+}
+#define SPRDFGU_ATTR_RO(_name) \
+{ \
+ .attr = { .name = #_name, .mode = S_IRUGO, }, \
+ .show = sprdfgu_show_attribute, \
+}
+#define SPRDFGU_ATTR_WO(_name) \
+{ \
+ .attr = { .name = #_name, .mode = S_IWUSR | S_IWGRP, }, \
+ .store = sprdfgu_store_attribute, \
+}
+
+static struct device_attribute sprdfgu_attribute[] = {
+ SPRDFGU_ATTR_RO(fgu_vol_adc),
+ SPRDFGU_ATTR_RO(fgu_current_adc),
+ SPRDFGU_ATTR_RO(fgu_vol),
+ SPRDFGU_ATTR_RO(fgu_current),
+ SPRDFGU_ATTR_WO(fgu_log_time),
+ SPRDFGU_ATTR_RO(fgu_cal_from_type),
+};
+
+enum SPRDFGU_ATTRIBUTE {
+ FGU_VOL_ADC = 0,
+ FGU_CURRENT_ADC,
+ FGU_VOL,
+ FGU_CURRENT,
+ FGU_LOG_TIME,
+ FGU_CAL_FROM_TYPE,
+};
+
+static ssize_t sprdfgu_store_attribute(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long set_value;
+ const ptrdiff_t off = attr - sprdfgu_attribute;
+
+ set_value = simple_strtoul(buf, NULL, 10);
+ pr_info("sprdfgu_store_attribute %lu %lu\n", off, set_value);
+
+ switch (off) {
+ case FGU_LOG_TIME:
+ sprdfgu_debug_log_time = set_value;
+ break;
+ default:
+ count = -EINVAL;
+ break;
+ }
+ return count;
+}
+
+static ssize_t sprdfgu_show_attribute(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i = 0;
+ const ptrdiff_t off = attr - sprdfgu_attribute;
+
+ switch (off) {
+ case FGU_VOL_ADC:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ sci_adi_read(REG_FGU_VOLT_VAL));
+ break;
+ case FGU_CURRENT_ADC:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ sci_adi_read(REG_FGU_CURT_VAL));
+ break;
+ case FGU_VOL:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ sprdfgu_read_vbat_vol());
+ break;
+ case FGU_CURRENT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ sprdfgu_read_batcurrent());
+ break;
+ case FGU_CAL_FROM_TYPE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ fgu_cal.cal_type);
+ break;
+
+ default:
+ i = -EINVAL;
+ break;
+ }
+
+ return i;
+}
+
+static int sprdfgu_creat_attr(struct device *dev)
+{
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(sprdfgu_attribute); i++) {
+ rc = device_create_file(dev, &sprdfgu_attribute[i]);
+ if (rc)
+ goto sprd_attrs_failed;
+ }
+ goto sprd_attrs_succeed;
+
+sprd_attrs_failed:
+ while (i--)
+ device_remove_file(dev, &sprdfgu_attribute[i]);
+
+sprd_attrs_succeed:
+ return rc;
+}
+
+static int sprdfgu_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ return -EINVAL;
+}
+
+int sprdfgu_init(struct sprd_battery_platform_data *pdata)
+{
+ int ret = 0;
+ int temp;
+ //struct sprdbat_drivier_data *data = platform_get_drvdata(pdev);
+
+ sprdfgu_data.pdata = pdata;
+ sprdfgu_data.warning_cap =
+ sprdfgu_vol2capacity(sprdfgu_data.pdata->alm_vol);
+ temp = sprdfgu_data.pdata->ocv_tab_size;
+ sprdfgu_data.shutdown_vol = sprdfgu_data.pdata->ocv_tab[temp - 1].x;
+
+ sprdfgu_data.bat_full_vol = sprdfgu_data.pdata->ocv_tab[0].x;
+ sprdfgu_data.cur_rint = sprdfgu_data.pdata->rint;
+ sprdfgu_data.poweron_rint = sprdfgu_data.pdata->rint;
+
+#ifdef CONFIG_SPRD_EXT_IC_POWER
+ ret = gpio_request(sprdfgu_data.pdata->gpio_vchg_detect, "chg_vchg_detect");
+ if (ret) {
+ FGU_DEBUG("Already request vchg gpio:%d\n",sprdfgu_data.pdata->gpio_vchg_detect);
+ }
+ gpio_direction_input(sprdfgu_data.pdata->gpio_vchg_detect);
+#endif
+
+ if (fgu_cal.cal_type == SPRDBAT_FGUADC_CAL_NO) {
+ sprdfgu_cal_from_chip(); //try to find cal data from efuse
+ }
+ sprdfgu_cal_init();
+ sprdfgu_hw_init();
+
+ sprdfgu_data.sprdfgu.name = "sprdfgu";
+ sprdfgu_data.sprdfgu.get_property = sprdfgu_power_get_property;
+ ret = power_supply_register(NULL, &sprdfgu_data.sprdfgu);
+ if (ret) {
+ pr_err("register power supply error!\n");
+ return -EFAULT;
+ }
+ sprdfgu_creat_attr(sprdfgu_data.sprdfgu.dev);
+
+ INIT_DELAYED_WORK(&sprdfgu_debug_work, sprdfgu_debug_works);
+ mutex_init(&sprdfgu_data.lock);
+ wake_lock_init(&(sprdfgu_data.low_power_lock), WAKE_LOCK_SUSPEND,
+ "sprdfgu_powerlow_lock");
+ sprdfgu_int_init();
+ schedule_delayed_work(&sprdfgu_debug_work, sprdfgu_debug_log_time * HZ);
+ FGU_DEBUG("sprdfgu_init end\n");
+ return ret;
+}
+
+int sprdfgu_reset(void)
+{
+ start_time = sci_syst_read();
+ sprdfgu_data.init_cap = sprdfgu_vol2capacity(sprdfgu_read_vbat_ocv());
+#if defined(CONFIG_ADIE_SC2723S) || defined(CONFIG_ADIE_SC2723)
+ sprdfgu_rtc_reg_write(sprdfgu_data.init_cap);
+#endif
+ sprdfgu_data.init_clbcnt = poweron_clbcnt =
+ sprdfgu_clbcnt_init(sprdfgu_data.init_cap);
+ sprdfgu_clbcnt_set(poweron_clbcnt);
+
+ return 0;
+}
+
+void sprdfgu_record_cap(u32 cap)
+{
+#if defined(CONFIG_ADIE_SC2723S) || defined(CONFIG_ADIE_SC2723)
+ sprdfgu_rtc_reg_write(cap);
+#endif
+}