From: 许盛飞 Date: Mon, 5 Jan 2015 07:07:36 +0000 (+0800) Subject: battery: update rk818-battery driver X-Git-Tag: firefly_0821_release~4345 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=f5458974e1b514b385087c0d96dde2e40b8c3d68;p=firefly-linux-kernel-4.4.55.git battery: update rk818-battery driver Signed-off-by: 许盛飞 --- diff --git a/arch/arm/boot/dts/rk312x-sdk.dtsi b/arch/arm/boot/dts/rk312x-sdk.dtsi index d99fbab9a5e2..57a7a497aac8 100755 --- a/arch/arm/boot/dts/rk312x-sdk.dtsi +++ b/arch/arm/boot/dts/rk312x-sdk.dtsi @@ -611,6 +611,9 @@ max_charge_currentmA = <1500>; max_charge_voltagemV = <4260>; max_bat_voltagemV = <4200>; + sleep_enter_current = <100>; + sleep_exit_current = <130>; + support_uboot_chrg = <0>; }; }; diff --git a/arch/arm/configs/rockchip_defconfig b/arch/arm/configs/rockchip_defconfig index 6fb723c95b79..7d88cf9d7bc7 100644 --- a/arch/arm/configs/rockchip_defconfig +++ b/arch/arm/configs/rockchip_defconfig @@ -357,6 +357,7 @@ CONFIG_BATTERY_BQ24296=y CONFIG_BATTERY_BQ27320=y CONFIG_BATTERY_RK30_ADC_FAC=y CONFIG_CW2015_BATTERY=y +CONFIG_BATTERY_RK818=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO=y CONFIG_SENSORS_ROCKCHIP_TSADC=y diff --git a/drivers/power/rk818_battery.c b/drivers/power/rk818_battery.c index 0d362e5fa0ee..b72e2344e92c 100644 --- a/drivers/power/rk818_battery.c +++ b/drivers/power/rk818_battery.c @@ -4,8 +4,8 @@ * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - */ + * */ + #include #include #include @@ -17,287 +17,67 @@ #include #include #include +#include #include #include +#include #include -//#include #include -#include -#include +#include +#include +#include + +/* if you want to disable, don't set it as 0, just be: "static int dbg_enable;" is ok*/ +static int dbg_enable; +#define RK818_SYS_DBG 1 -static int dbg_enable =0; module_param_named(dbg_level, dbg_enable, int, 0644); -#define DBG( args...) \ +#define DBG(args...) \ do { \ if (dbg_enable) { \ pr_info(args); \ } \ } while (0) -#define VB_MOD_REG 0x21 -#define CHRG_COMP_REG1 0x99 -#define CHRG_COMP_REG2 0x9A -#define SUP_STS_REG 0xA0 -#define USB_CTRL_REG 0xA1 -#define CHRG_CTRL_REG1 0xA3 -#define CHRG_CTRL_REG2 0xA4 -#define CHRG_CTRL_REG3 0xA5 -#define BAT_CTRL_REG 0xA6 -#define BAT_HTS_TS1_REG 0xA8 -#define BAT_LTS_TS1_REG 0xA9 -#define BAT_HTS_TS2_REG 0xAA -#define BAT_LTS_TS2_REG 0xAB - - -#define TS_CTRL_REG 0xAC -#define ADC_CTRL_REG 0xAD - -#define ON_SOURCE 0xAE -#define OFF_SOURCE 0xAF - -#define GGCON 0xB0 -#define GGSTS 0xB1 -#define FRAME_SMP_INTERV_REG 0xB2 -#define AUTO_SLP_CUR_THR_REG 0xB3 - -#define GASCNT_CAL_REG3 0xB4 -#define GASCNT_CAL_REG2 0xB5 -#define GASCNT_CAL_REG1 0xB6 -#define GASCNT_CAL_REG0 0xB7 -#define GASCNT3 0xB8 -#define GASCNT2 0xB9 -#define GASCNT1 0xBA -#define GASCNT0 0xBB - -#define BAT_CUR_AVG_REGH 0xBC -#define BAT_CUR_AVG_REGL 0xBD - - -#define TS1_ADC_REGH 0xBE -#define TS1_ADC_REGL 0xBF -#define TS2_ADC_REGH 0xC0 -#define TS2_ADC_REGL 0xC1 - -#define BAT_OCV_REGH 0xC2 -#define BAT_OCV_REGL 0xC3 -#define BAT_VOL_REGH 0xC4 -#define BAT_VOL_REGL 0xC5 - -#define RELAX_ENTRY_THRES_REGH 0xC6 -#define RELAX_ENTRY_THRES_REGL 0xC7 -#define RELAX_EXIT_THRES_REGH 0xC8 -#define RELAX_EXIT_THRES_REGL 0xC9 - -#define RELAX_VOL1_REGH 0xCA -#define RELAX_VOL1_REGL 0xCB -#define RELAX_VOL2_REGH 0xCC -#define RELAX_VOL2_REGL 0xCD - -#define BAT_CUR_R_CALC_REGH 0xCE -#define BAT_CUR_R_CALC_REGL 0xCF -#define BAT_VOL_R_CALC_REGH 0xD0 -#define BAT_VOL_R_CALC_REGL 0xD1 - -#define CAL_OFFSET_REGH 0xD2 -#define CAL_OFFSET_REGL 0xD3 - -#define NON_ACT_TIMER_CNT_REGL 0xD4 - -#define VCALIB0_REGH 0xD5 -#define VCALIB0_REGL 0xD6 -#define VCALIB1_REGH 0xD7 -#define VCALIB1_REGL 0xD8 - -#define IOFFSET_REGH 0xDD -#define IOFFSET_REGL 0xDE - - -/*0xE0 ~0xF2 data register,*/ -#define SOC_REG 0xE0 - -#define REMAIN_CAP_REG3 0xE1 -#define REMAIN_CAP_REG2 0xE2 -#define REMAIN_CAP_REG1 0xE3 -#define REMAIN_CAP_REG0 0xE4 - - - -#define FCC_REGL 0xE1 -#define FCC_REGH 0xE2 - -#define GG_EN 1<<7 // gasgauge module enable bit 0: disable 1:enabsle TS_CTRL_REG 0xAC -//ADC_CTRL_REG -#define ADC_VOL_EN 1<<7 //if GG_EN = 0 , then the ADC of BAT voltage controlled by the bit 0:diabsle 1:enable -#define ADC_CUR_EN 1<<6 //if GG_EN = 0, then the ADC of BAT current controlled by the bit 0: disable 1: enable -#define ADC_TS1_EN 1<<5 //the ADC of TS1 controlled by the bit 0:disabsle 1:enable -#define ADC_TS2_EN 1<<4 //the ADC of TS2 controlled by the bit 0:disabsle 1:enable -#define ADC_PHASE 1<<3 //ADC colock phase 0:normal 1:inverted -#define ADC_CLK_SEL 7 -/******************************************************************* -#define ADC_CLK_SEL_2M 0x000 -#define ADC_CLK_SEL_1M 0x001 -#define ADC_CLK_SEL_500K 0x002 -#define ADC_CLK_SEL_250K 0x003 -#define ADC_CLK_SEL_125K 0x004 -**********************************************************************/ -//GGCON -#define CUR_SAMPL_CON_TIMES 3<<6 // ADC bat current continue sample times 00:8 01:16 10:32 11:64 -#define ADC_OFF_CAL_INTERV 3<<4 //ADC offset calibreation interval time 00:8min 01:16min 10:32min 11:48min -#define OCV_SAMPL_INTERV 3<<2 //OCV sampling interval time 00:8min 01:16min 10:32min :11:48min - -//???????? -#define ADC_CUR_VOL_MODE 1<<1 //ADC working in current voltage collection mode -#define ADC_RES_MODE 1 //ADC working in resistor calculation mode 0:disable 1:enable - -//GGSTS -#define RES_CUR_AVG_SEL 3<<5 //average current filter times 00:1/2 01:1/4 10:1/8 11:1/16 -#define BAT_CON 1<<4 //battery first connection,edge trigger 0:NOT 1:YES -#define RELAX_VOL1_UPD 1<<3 //battery voltage1 update in relax status 0: NOT 1:YE -#define RELAX_VOL2_UPD 1<<2 //battery voltage2 update in relax status 0: NOT 1:YE -#define RELAX_STS 1<<1 //battery coming into relax status 0: NOT 1:YE -#define IV_AVG_UPD_STS 1<<0 //battery average voltage and current updated status 0: NOT 1:YES - -//FRAME_SMP_INTERV_REG -#define AUTO_SLP_EN 1<<5 // auto sleep mode 0:disable 1:enable -#define FRAME_SMP_INTERV_TIME 0x1F // - -#define PLUG_IN_STS 1<<6 - -//SUP_STS_REG -#define BAT_EXS (1<<7) -#define CHARGE_OFF (0x00<<4) -#define DEAD_CHARGE (0x01<<4) -#define TRICKLE_CHARGE (0x02<<4) -#define CC_OR_CV (0x03<<4) -#define CHARGE_FINISH (0x04<<4) -#define USB_OVER_VOL (0x05<<4) -#define BAT_TMP_ERR (0x06<<4) -#define TIMER_ERR (0x07<<4) -#define USB_EXIST (1<<1)// usb is exists -#define USB_EFF (1<<0)// usb is effective - -//USB_CTRL_REG -#define CHRG_CT_EN (1<<7) -// USB_VLIM_SEL -#define VLIM_4000MV (0x00<<4) -#define VLIM_4100MV (0x01<<4) -#define VLIM_4200MV (0x02<<4) -#define VLIM_4300MV (0x03<<4) -#define VLIM_4400MV (0x04<<4) -#define VLIM_4500MV (0x05<<4) -#define VLIM_4600MV (0x06<<4) -#define VLIM_4700MV (0x07<<4) -//USB_ILIM_SEL -#define ILIM_45MA (0x00) -#define ILIM_300MA (0x01) -#define ILIM_80MA (0x02) -#define ILIM_820MA (0x03) -#define ILIM_1000MA (0x04) -#define ILIM_1200MA (0x05) -#define ILIM_1400MA (0x06) -#define ILIM_1600MA (0x07) -#define ILIM_1800MA (0x08) -#define ILIM_2000MA (0x09) -#define ILIM_2200MA (0x0A) -#define ILIM_2400MA (0x0B) -#define ILIM_2600MA (0x0C) -#define ILIM_2800MA (0x0D) -#define ILIM_3000MA (0x0E) - -//CHRG_CTRL_REG -#define CHRG_EN (0x01<<7) -// CHRG_VOL_SEL - -#define CHRG_VOL4050 (0x00<<4) -#define CHRG_VOL4100 (0x01<<4) -#define CHRG_VOL4150 (0x02<<4) -#define CHRG_VOL4200 (0x03<<4) -#define CHRG_VOL4300 (0x04<<4) -#define CHRG_VOL4350 (0x05<<4) - -//CHRG_CUR_SEL -#define CHRG_CUR1000mA (0x00) -#define CHRG_CUR1200mA (0x01) -#define CHRG_CUR1400mA (0x02) -#define CHRG_CUR1600mA (0x03) -#define CHRG_CUR1800mA (0x04) -#define CHRG_CUR2000mA (0x05) -#define CHRG_CUR2200mA (0x06) -#define CHRG_CUR2400mA (0x07) -#define CHRG_CUR2600mA (0x08) -#define CHRG_CUR2800mA (0x09) -#define CHRG_CUR3000mA (0x0A) - - -#define DRIVER_VERSION "1.0.0" -#define ROLEX_SPEED 100 * 1000 - -#define CHARGING 0x01 -#define DISCHARGING 0x00 - -#define TIMER_MS_COUNTS 1000 -#define MAX_CHAR 0x7F -#define MAX_UNSIGNED_CHAR 0xFF -#define MAX_INT 0x7FFFFFFF -#define MAX_UNSIGNED_INT 0xFFFF -#define MAX_INT8 0x7F -#define MAX_UINT8 0xFF - -/* Voltage and Current buffers */ -#define AV_SIZE 5 - -static int16_t av_v[AV_SIZE]; -static int16_t av_c[AV_SIZE]; - -static uint16_t av_v_index; -static uint16_t av_c_index; - -#define INTERPOLATE_MAX 1000 -//#define OCV_TABLE_SIZE - -struct battery_info{ +#define INTERPOLATE_MAX 1000 +#define MAX_INT 0x7FFF +#define TIME_10MIN_SEC 600 + +struct battery_info { struct device *dev; struct cell_state cell; struct power_supply bat; struct power_supply ac; struct power_supply usb; struct delayed_work work; -// struct i2c_client *client; + /* struct i2c_client *client; */ + struct rk818 *rk818; - struct rk818 *rk818; struct battery_platform_data *platform_data; - struct notifier_block battery_nb; - struct workqueue_struct *wq; - struct delayed_work battery_monitor_work; - struct delayed_work charge_check_work; + int work_on; + int irq; int ac_online; int usb_online; - int health; - int tempreture; - int present; int status; - - int bat_current; int current_avg; int current_offset; - int voltage; - int voltage_avg; - int voltage_offset; - int voltage_ocv; - - int poweroff_voltage; - int warnning_voltage; - int poweron_voltage; + uint16_t voltage; + uint16_t voltage_ocv; + uint16_t relax_voltage; + u8 charge_status; + u8 otg_status; + int pcb_ioffset; + bool pcb_ioffset_updated; + unsigned long queue_work_cnt; + uint16_t warnning_voltage; int design_capacity; int fcc; - int new_fcc; - u32 qmax; + int qmax; int remain_capacity; - int warnning_capacity; int nac; int temp_nac; @@ -305,61 +85,109 @@ struct battery_info{ int display_soc; int temp_soc; + int bat_res_update_cnt; int soc_counter; int dod0; + int dod0_status; + int dod0_voltage; int dod0_capacity; - int dod1; - int dod1_capacity; - - int temperature; + unsigned long dod0_time; + u8 dod0_level; + int enter_flatzone; + int exit_flatzone; int time2empty; int time2full; int *ocv_table; - int ocv_size; int *res_table; - int current_k;//(ICALIB0,ICALIB1) + int current_k;/* (ICALIB0, ICALIB1) */ int current_b; - int voltage_k;//VCALIB0 VCALIB1 + int voltage_k;/* VCALIB0 VCALIB1 */ int voltage_b; - - int relax_entry_thres; - int relax_exit_thres; - - int relax_vol1; - int relax_vol2; - - u8 sleep_cur; - u8 sleep_smp_time; + + int update_k; + int line_k; + int line_q; + int update_q; + int voltage_old; + u8 check_count; -// u32 status; - struct timeval soc_timer; - struct timeval change_timer; + /* u32 status; */ + struct timeval soc_timer; + struct timeval change_timer; + + int vol_smooth_time; + int charge_smooth_time; + + int suspend_capacity; + int resume_capacity; + struct timespec suspend_time; + struct timespec resume_time; + unsigned long suspend_time_start; + unsigned long count_sleep_time; + + unsigned long dischrg_sum_sleep_sec; + unsigned long dischrg_sum_sleep_capacity; + int suspend_temp_soc; + int sleep_status; + int suspend_charge_current; + int resume_soc; + int bat_res; + bool bat_res_updated; + bool charge_smooth_status; + bool resume; + unsigned long last_plugin_time; + bool sys_wakeup; + + unsigned long charging_time; + unsigned long discharging_time; - bool resume; - int charge_otg; + struct notifier_block battery_nb; + struct workqueue_struct *wq; + struct delayed_work battery_monitor_work; + struct delayed_work charge_check_work; + int charge_otg; + struct wake_lock resume_wake_lock; + + int debug_finish_real_soc; + int debug_finish_temp_soc; }; - struct battery_info *data; + +struct battery_info *data; +struct battery_info *g_battery; +u32 support_uboot_chrg; + +extern int dwc_vbus_status(void); +extern int get_gadget_connect_flag(void); +extern int dwc_otg_check_dpdm(void); +extern void kernel_power_off(void); +extern int rk818_set_bits(struct rk818 *rk818, u8 reg, u8 mask, u8 val); +extern unsigned int irq_create_mapping(struct irq_domain *domain, + irq_hw_number_t hwirq); + +static void update_battery_info(struct battery_info *di); + +#define SUPPORT_USB_CHARGE -u32 interpolate(int value, u32 *table, int size) +static u32 interpolate(int value, u32 *table, int size) { uint8_t i; uint16_t d; - for (i = 0; i < size; i++){ + for (i = 0; i < size; i++) { if (value < table[i]) break; } - if ((i > 0) && (i < size)) { + if ((i > 0) && (i < size)) { d = (value - table[i-1]) * (INTERPOLATE_MAX/(size-1)); - d /= table[i] - table[i-1]; + d /= table[i] - table[i-1]; d = d + (i-1) * (INTERPOLATE_MAX/(size-1)); } else { d = i * ((INTERPOLATE_MAX+size/2)/size); @@ -371,7 +199,7 @@ u32 interpolate(int value, u32 *table, int size) return d; } /* Returns (a * b) / c */ -int32_t ab_div_c(u32 a, u32 b, u32 c) +static int32_t ab_div_c(u32 a, u32 b, u32 c) { bool sign; u32 ans = MAX_INT; @@ -400,1307 +228,2309 @@ static int32_t abs_int(int32_t x) return (x > 0) ? x : -x; } -/* Returns diviation between 'size' array members */ -uint16_t diff_array(int16_t *arr, uint8_t size) +static int abs32_int(int x) { - uint8_t i; - uint32_t diff = 0; - - for (i = 0; i < size-1; i++) - diff += abs_int(arr[i] - arr[i+1]); - - if (diff > MAX_UNSIGNED_INT) - diff = MAX_UNSIGNED_INT; - - return (uint16_t) diff; + return (x > 0) ? x : -x; } -static enum power_supply_property rk818_battery_props[] = { - - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_CAPACITY, -#if 0 - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_HEALTH, - //POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - //POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, - //POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, -#endif - -}; - -static enum power_supply_property rk818_battery_ac_props[] = { - POWER_SUPPLY_PROP_ONLINE, -}; -static enum power_supply_property rk818_battery_usb_props[] = { - POWER_SUPPLY_PROP_ONLINE, -}; - - static int battery_read(struct rk818 *rk818, u8 reg, u8 buf[], unsigned len) { int ret; - ret = rk818_i2c_read(rk818, reg, len,buf); - return ret; + + ret = rk818_i2c_read(rk818, reg, len, buf); + return ret; } static int battery_write(struct rk818 *rk818, u8 reg, u8 const buf[], unsigned len) { - int ret; - ret = rk818_i2c_write(rk818, reg,(int)len, *buf); + int ret; + ret = rk818_i2c_write(rk818, reg, (int)len, *buf); return ret; } static void dump_gauge_register(struct battery_info *di) { - int i = 0; - char buf; - DBG("%s dump charger register start: \n",__FUNCTION__); - for(i = 0xAC;i < 0xDE; i ++){ - battery_read(di ->rk818, i, &buf,1); - DBG(" the register is 0x%02x, the value is 0x%02x\n ", i, buf); - } - DBG("demp end!\n"); - + int i = 0; + char buf; + DBG("%s dump charger register start: \n", __func__); + for (i = 0xAC; i < 0xDF; i++) { + battery_read(di->rk818, i, &buf, 1); + DBG(" the register is 0x%02x, the value is 0x%02x\n ", i, buf); + } + DBG("demp end!\n"); } static void dump_charger_register(struct battery_info *di) { - int i = 0; - char buf; - DBG("%s dump the register start: \n",__FUNCTION__); - for(i = 0x99;i < 0xAB; i ++){ - battery_read(di ->rk818, i, &buf,1); - DBG(" the register is 0x%02x, the value is 0x%02x\n ", i, buf); - } - DBG("demp end!\n"); + int i = 0; + char buf; + DBG("%s dump the register start: \n", __func__); + for (i = 0x99; i < 0xAB; i++) { + battery_read(di->rk818, i, &buf, 1); + DBG(" the register is 0x%02x, the value is 0x%02x\n ", i, buf); + } + DBG("demp end!\n"); } -#if 0 -//POWER_SUPPLY_PROP_STATUS -static int rk818_battery_status(struct battery_info *di) + +#if RK818_SYS_DBG + +static uint16_t _get_OCV_voltage(struct battery_info *di); +static int _voltage_to_capacity(struct battery_info *di, int voltage); +static int _get_realtime_capacity(struct battery_info *di); +static void power_on_save(struct battery_info *di, int voltage); +static void _capacity_init(struct battery_info *di, u32 capacity); +static void battery_poweron_status_init(struct battery_info *di); +static void power_on_save(struct battery_info *di, int voltage); +static void flatzone_voltage_init(struct battery_info *di); +static int _get_FCC_capacity(struct battery_info *di); +static void _save_FCC_capacity(struct battery_info *di, u32 capacity); +static int _get_soc(struct battery_info *di); +static int _get_average_current(struct battery_info *di); +static int rk_battery_voltage(struct battery_info *di); +static uint16_t _get_relax_vol1(struct battery_info *di); +static uint16_t _get_relax_vol2(struct battery_info *di); +static void update_battery_info(struct battery_info *di); + +static ssize_t bat_state_read(struct device *dev, struct device_attribute *attr, char *buf) { - return di->status; + struct battery_info *di = g_battery; + u8 status; + u8 rtc_val; + u8 soc_reg; + u8 shtd_time; + + battery_read(di->rk818, SUP_STS_REG, &status, 1); + battery_read(di->rk818, SOC_REG, &soc_reg, 1); + battery_read(di->rk818, 0x00, &rtc_val, 1); + di->voltage_ocv = _get_OCV_voltage(di); + _voltage_to_capacity(di, di->voltage_ocv); + battery_read(di->rk818, NON_ACT_TIMER_CNT_REG, &shtd_time, 1); + + return sprintf(buf, "-----------------------------------------------------------------------------\n" + "volt = %d, ocv_volt = %d, avg_current = %d, remain_cap = %d, ocv_cap = %d\n" + "real_soc = %d, temp_soc = %d\n" + "fcc = %d, FCC_REG = %d, shutdown_time = %d\n" + "usb_online = %d, ac_online = %d\n" + "SUP_STS_REG(0xc7) = 0x%02x, RTC_REG = 0x%02x\n" + "voltage_k = %d, voltage_b = %d, SOC_REG = 0x%02x\n" + "relax_volt1 = %d, relax_volt2 = %d\n" + "---------------------------------------------------------------------------\n", + rk_battery_voltage(di), di->voltage_ocv, _get_average_current(di), _get_realtime_capacity(di), di->temp_nac, + di->real_soc, _get_soc(di), + di->fcc, _get_FCC_capacity(di), shtd_time, + di->usb_online, di->ac_online, + status, rtc_val, + di->voltage_k, di->voltage_b, soc_reg, + _get_relax_vol1(di), _get_relax_vol2(di)); } -//POWER_SUPPLY_PROP_PRESENT, -static int rk818_battery_present(struct rk818_battery_info *di) + +static ssize_t bat_reg_read(struct device *dev, struct device_attribute *attr, char *buf) { - return 1; + struct battery_info *di = g_battery; + u8 sup_tst_reg, ggcon_reg, ggsts_reg, vb_mod_reg; + u8 usb_ctrl_reg, chrg_ctrl_reg1; + u8 chrg_ctrl_reg2, chrg_ctrl_reg3, rtc_val; + + battery_read(di->rk818, GGCON, &ggcon_reg, 1); + battery_read(di->rk818, GGSTS, &ggsts_reg, 1); + battery_read(di->rk818, SUP_STS_REG, &sup_tst_reg, 1); + battery_read(di->rk818, VB_MOD_REG, &vb_mod_reg, 1); + battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + battery_read(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + battery_read(di->rk818, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + battery_read(di->rk818, 0x00, &rtc_val, 1); + + return sprintf(buf, "\n------------- dump_debug_regs -----------------\n" + "GGCON = 0x%2x, GGSTS = 0x%2x, RTC = 0x%2x\n" + "SUP_STS_REG = 0x%2x, VB_MOD_REG = 0x%2x\n" + "USB_CTRL_REG = 0x%2x, CHRG_CTRL_REG1 = 0x%2x\n" + "CHRG_CTRL_REG2 = 0x%2x, CHRG_CTRL_REG3 = 0x%2x\n" + "---------------------------------------------------------------------------\n", + ggcon_reg, ggsts_reg, rtc_val, + sup_tst_reg, vb_mod_reg, + usb_ctrl_reg, chrg_ctrl_reg1, + chrg_ctrl_reg2, chrg_ctrl_reg3 + ); } -#endif -/* OCV Lookup table - * Open Circuit Voltage (OCV) correction routine. This function estimates SOC, - * based on the voltage. - */ -static int _voltage_to_capacity(struct battery_info * di, int voltage) +static ssize_t bat_fcc_read(struct device *dev, struct device_attribute *attr, char *buf) { - u32 *ocv_table; - int ocv_size; - u32 tmp; - - ocv_table = di->platform_data->battery_ocv; - ocv_size = di->platform_data->ocv_size; - // ocv_table = di->ocv_table; - // ocv_size = di->ocv_size; - tmp = interpolate(voltage, ocv_table, ocv_size); - di->temp_soc = ab_div_c(tmp, MAX_PERCENTAGE, INTERPOLATE_MAX); - di->temp_nac= ab_div_c(tmp, di->fcc, INTERPOLATE_MAX); - DBG("temp = %d real-soc =%d nac= %d, fcc = %d\n", tmp, di->temp_soc, di->temp_nac,di->fcc); - return 0; + struct battery_info *di = g_battery; + + return sprintf(buf, "%d", di->fcc); } -//POWER_SUPPLY_PROP_CURRENT_NOW, -static int _get_average_current(struct battery_info *di) +static ssize_t bat_soc_read(struct device *dev, struct device_attribute *attr, char *buf) { - u8 buf;//[2]; - int ret; - int current_now; - int temp; - - ret = battery_read(di->rk818,BAT_CUR_AVG_REGL, &buf, 1); - if(ret < 0){ - dev_err(di->dev, "error reading BAT_CUR_AVG_REGL"); - return ret; - } - current_now = buf; - ret = battery_read(di->rk818,BAT_CUR_AVG_REGH, &buf, 1); - if(ret < 0){ - dev_err(di->dev, "error reading BAT_CUR_AVG_REGH"); - return ret; - } - current_now |= (buf<<8); + struct battery_info *di = g_battery; - if(current_now &0x800) - current_now -= 4096; - -// temp = current_now*1000*90/14/4096*500/521; - temp = current_now*1506/1000;//1000*90/14/4096*500/521; + return sprintf(buf, "%d", di->real_soc); +} - if(ret < 0){ - dev_err(di->dev, "error reading BAT_CUR_AVG_REGH"); - return ret; - } +static ssize_t bat_temp_soc_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct battery_info *di = g_battery; - DBG("%s, average current current_now = %d current = %d\n",__FUNCTION__, current_now, temp); - return temp; + return sprintf(buf, "%d", di->temp_soc); +} +static ssize_t bat_voltage_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct battery_info *di = g_battery; + + return sprintf(buf, "%d", di->voltage); } -#define to_device_info(x) container_of((x), \ - struct battery_info, bat); +static ssize_t bat_avr_current_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct battery_info *di = g_battery; -static int rk818_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) + return sprintf(buf, "%d", di->current_avg); +} + +static ssize_t bat_remain_capacity_read(struct device *dev, struct device_attribute *attr, char *buf) { - int ret = 0; - struct battery_info *di = to_device_info(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = di->current_avg; - break; - - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - case POWER_SUPPLY_PROP_PRESENT: - val->intval = di->voltage;// rk818_battery_voltage(di); - if (psp == POWER_SUPPLY_PROP_PRESENT) - val->intval = val->intval <= 0 ? 0 : 1; - break; + struct battery_info *di = g_battery; - case POWER_SUPPLY_PROP_CAPACITY: - if(di->real_soc < 0) - di->real_soc = 0; - if(di->real_soc > 100) - di->real_soc = 100; - val->intval =di->real_soc; - //DBG("POWER_SUPPLY_PROP_CAPACITY = %d, val->intval =%d\n", di->real_soc, val->intval); - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = POWER_SUPPLY_HEALTH_GOOD;//rk818_battery_health(di); - break; + return sprintf(buf, "%d", di->remain_capacity); +} - case POWER_SUPPLY_PROP_STATUS: - val->intval = di->status; - //DBG("gBatStatus=%d\n",val->intval); - break; +static struct device_attribute rk818_bat_attr[] = { + __ATTR(state, 0664, bat_state_read, NULL), + __ATTR(regs, 0664, bat_reg_read, NULL), + __ATTR(fcc, 0664, bat_fcc_read, NULL), + __ATTR(soc, 0664, bat_soc_read, NULL), + __ATTR(temp_soc, 0664, bat_temp_soc_read, NULL), + __ATTR(voltage, 0664, bat_voltage_read, NULL), + __ATTR(avr_current, 0664, bat_avr_current_read, NULL), + __ATTR(remain_capacity, 0664, bat_remain_capacity_read, NULL), +}; - default: - return -EINVAL; - } +#endif - return ret; +#define BATT_NUM 11 + +static int batt_table[22]; + +static ssize_t bat_param_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + int i; + + for (i = 0; i < BATT_NUM; i++) + printk(KERN_INFO"i = %d batt_table = %d\n", i, batt_table[i]); + + for (i = 0; i < BATT_NUM; i++) + printk(KERN_INFO"i = %d batt_table = %d\n", i + BATT_NUM , batt_table[i+BATT_NUM]); + return 0; } -#define to_ac_device_info(x) container_of((x), \ - struct battery_info, ac); +static ssize_t bat_param_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + return size; +} -static int rk818_battery_ac_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) + +DEVICE_ATTR(rk818batparam, 0664, bat_param_read, bat_param_write); +static uint16_t get_relax_voltage(struct battery_info *di); + +static ssize_t show_state_attrs(struct device *dev, + struct device_attribute *attr, char *buf) { - //DBG("%s:%d psp = %d\n",__FUNCTION__,__LINE__,psp); - int ret = 0; - struct battery_info *di = to_ac_device_info(psy); + printk(KERN_INFO"get_relax_voltage relax voltage = %d\n", get_relax_voltage(data)); - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = di->ac_online; /*discharging*/ - //DBG("%s:%d val->intval = %d di->status = %d\n",__FUNCTION__,__LINE__,val->intval, di->status); - break; - - default: - ret = -EINVAL; - break; - } - return ret; + if (0 == get_relax_voltage(data)) { + return sprintf(buf, + "voltage = %d, remain_capacity = %d, status = %d\n", + data->voltage, data->remain_capacity, + data->status); + + } else + return sprintf(buf, + "voltage = %d, remain_capacity = %d, status = %d\n", + get_relax_voltage(data), data->remain_capacity, + data->status); } -#define to_usb_device_info(x) container_of((x), \ - struct battery_info, usb); +static ssize_t restore_state_attrs(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + return size; +} +static struct device_attribute rkbatt_attrs[] = { + __ATTR(state, 0664, show_state_attrs, restore_state_attrs), +}; -static int rk818_battery_usb_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static int create_sysfs_interfaces(struct device *dev) { - int ret = 0; - struct battery_info *di = to_usb_device_info(psy); + int liTmep; - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = di->usb_online; /*discharging*/ - //DBG("%s:%d val->intval = %d\n",__FUNCTION__,__LINE__,val->intval); - break; - - default: - ret = -EINVAL; - break; + for (liTmep = 0; liTmep < ARRAY_SIZE(rkbatt_attrs); liTmep++) { + if (device_create_file(dev, rkbatt_attrs + liTmep)) + goto error; } - return ret; -} + return 0; + +error: + for (; liTmep >= 0; liTmep--) + device_remove_file(dev, rkbatt_attrs + liTmep); + + dev_err(dev, "%s:Unable to create sysfs interface\n", __func__); + return -1; +} -static void battery_powersupply_init(struct battery_info *di) +static int debug_reg(struct battery_info *di, u8 reg, char *reg_name) { - di->bat.name = "BATTERY"; - di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->bat.properties = rk818_battery_props; - di->bat.num_properties = ARRAY_SIZE(rk818_battery_props); - di->bat.get_property = rk818_battery_get_property; - - di->ac.name = "AC"; - di->ac.type = POWER_SUPPLY_TYPE_MAINS; - di->ac.properties = rk818_battery_ac_props; - di->ac.num_properties = ARRAY_SIZE(rk818_battery_ac_props); - di->ac.get_property = rk818_battery_ac_get_property; + u8 val; - di->usb.name = "USB"; - di->usb.type = POWER_SUPPLY_TYPE_USB; - di->usb.properties = rk818_battery_usb_props; - di->usb.num_properties = ARRAY_SIZE(rk818_battery_usb_props); - di->usb.get_property = rk818_battery_usb_get_property; + battery_read(di->rk818, reg, &val, 1); + DBG("<%s>: %s = 0x%2x\n", __func__, reg_name, val); + return val; } -//enabsle GG_EN + static int _gauge_enable(struct battery_info *di) { int ret; u8 buf; - DBG("%s start \n", __FUNCTION__); - ret = battery_read(di->rk818,TS_CTRL_REG, &buf, 1); - DBG("_gauge_enable read-%d\n", buf); - - if(ret < 0){ + + ret = battery_read(di->rk818, TS_CTRL_REG, &buf, 1); + if (ret < 0) { dev_err(di->dev, "error reading TS_CTRL_REG"); return ret; } - if(!(buf & GG_EN)){ + if (!(buf & GG_EN)) { buf |= GG_EN; - ret = battery_write(di->rk818, TS_CTRL_REG, &buf, 1); //enable - ret = battery_read(di->rk818,TS_CTRL_REG, &buf, 1); + ret = battery_write(di->rk818, TS_CTRL_REG, &buf, 1); /* enable */ + ret = battery_read(di->rk818, TS_CTRL_REG, &buf, 1); return 0; } - DBG("%s,%d\n",__FUNCTION__, buf); + DBG("%s, %d\n", __func__, buf); return 0; - + } +static void save_level(struct battery_info *di, u8 save_soc) +{ + u8 soc; -#if 0 + soc = save_soc; + battery_write(di->rk818, UPDAT_LEVE_REG, &soc, 1); +} +static u8 get_level(struct battery_info *di) +{ + u8 soc; + + battery_read(di->rk818, UPDAT_LEVE_REG, &soc, 1); + return soc; +} -static int _gauge_disable(struct battery_info *di) +static int _get_vcalib0(struct battery_info *di) { int ret; + int temp = 0; u8 buf; - ret = battery_read(di->rk818,TS_CTRL_REG, &buf, 1); - if(ret < 0){ - dev_err(di->dev, "error reading TS_CTRL_REG"); - return ret; - } - if((buf & GG_EN)){ - buf &= (~0x80);//GG_EN - ret = battery_write(di->rk818, TS_CTRL_REG, &buf, 1); //enable - return 0; - } - return 0; + ret = battery_read(di->rk818, VCALIB0_REGL, &buf, 1); + temp = buf; + ret = battery_read(di->rk818, VCALIB0_REGH, &buf, 1); + temp |= buf<<8; + + DBG("%s voltage0 offset vale is %d\n", __func__, temp); + return temp; } -static int _set_auto_sleep_cur(struct battery_info *di, u8 value) +static int _get_vcalib1(struct battery_info *di) { int ret; + int temp = 0; u8 buf; - buf = value; - ret = battery_write(di->rk818, AUTO_SLP_CUR_THR_REG, &buf, 1); //enable - return 0; + + ret = battery_read(di->rk818, VCALIB1_REGL, &buf, 1); + temp = buf; + ret = battery_read(di->rk818, VCALIB1_REGH, &buf, 1); + temp |= buf<<8; + + DBG("%s voltage1 offset vale is %d\n", __func__, temp); + return temp; } -static int _set_sleep_smp_time(struct battery_info *di, u8 value) + +static int _get_ioffset(struct battery_info *di) { int ret; - u8 temp; + int temp = 0; u8 buf; - ret = battery_read(di->rk818,FRAME_SMP_INTERV_REG, &buf, 1); - if(ret < 0){ - dev_err(di->dev, "error reading FRAME_SMP_INTERV_REG"); - return ret; - } - - temp = (buf&(AUTO_SLP_EN))|value; - ret = battery_write(di->rk818, FRAME_SMP_INTERV_REG, &temp, 1); //enable + ret = battery_read(di->rk818, IOFFSET_REGL, &buf, 1); + temp = buf; + ret = battery_read(di->rk818, IOFFSET_REGH, &buf, 1); + temp |= buf<<8; - return 0; + return temp; } -static int _autosleep_enable(struct battery_info *di) +static uint16_t _get_cal_offset(struct battery_info *di) { int ret; + uint16_t temp = 0; u8 buf; - ret = battery_read(di->rk818,FRAME_SMP_INTERV_REG, &buf, 1); - if(ret < 0){ - dev_err(di->dev, "error reading FRAME_SMP_INTERV_REG"); - return ret; - } - if(!(buf & AUTO_SLP_EN)){ - buf |= AUTO_SLP_EN; - ret = battery_write(di->rk818, FRAME_SMP_INTERV_REG, &buf, 1); //enable - return 0; - } - - _set_auto_sleep_cur(di, di->sleep_cur); // sleep_cur , into sleep-mode - _set_sleep_smp_time(di, di->sleep_smp_time); // time of adc work , sleep-mode - - - return 0; - + ret = battery_read(di->rk818, CAL_OFFSET_REGL, &buf, 1); + temp = buf; + ret = battery_read(di->rk818, CAL_OFFSET_REGH, &buf, 1); + temp |= buf<<8; + return temp; } - -static int _autosleep_disable(struct battery_info *di) +static int _set_cal_offset(struct battery_info *di, u32 value) { int ret; u8 buf; - ret = battery_read(di->rk818,FRAME_SMP_INTERV_REG, &buf, 1); - if(ret < 0){ - dev_err(di->dev, "error reading FRAME_SMP_INTERV_REG"); - return ret; - } - if((buf & AUTO_SLP_EN)){ - buf &= (~AUTO_SLP_EN); - ret = battery_write(di->rk818, FRAME_SMP_INTERV_REG, &buf, 1); //enable - return 0; - } + buf = value&0xff; + ret = battery_write(di->rk818, CAL_OFFSET_REGL, &buf, 1); + buf = (value >> 8)&0xff; + ret = battery_write(di->rk818, CAL_OFFSET_REGH, &buf, 1); + return 0; +} +static void _get_voltage_offset_value(struct battery_info *di) +{ + int vcalib0, vcalib1; + vcalib0 = _get_vcalib0(di); + vcalib1 = _get_vcalib1(di); + di->voltage_k = (4200 - 3000)*1000/(vcalib1 - vcalib0); + di->voltage_b = 4200 - (di->voltage_k*vcalib1)/1000; + DBG("voltage_k = %d(x1000) voltage_b = %d\n", di->voltage_k, di->voltage_b); } - -#endif -static int rk818_battery_voltage(struct battery_info *di) +static uint16_t _get_OCV_voltage(struct battery_info *di) { int ret; - int voltage_now = 0; u8 buf; - int temp; -#if 1 - ret = battery_read(di->rk818,BAT_VOL_REGL, &buf, 1); + uint16_t temp; + uint16_t voltage_now = 0; + + ret = battery_read(di->rk818, BAT_OCV_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818,BAT_VOL_REGH,&buf, 1); + ret = battery_read(di->rk818, BAT_OCV_REGH, &buf, 1); temp |= buf<<8; -#endif - //ret = battery_read(di->rk818,BAT_VOL_REGH, buf, 2); - if(ret < 0){ - dev_err(di->dev, "error reading BAT_VOL_REGH"); + if (ret < 0) { + dev_err(di->dev, "error read BAT_OCV_REGH"); return ret; } - //voltage_now = temp;//(buf[0]<<8)|buf[1]; - voltage_now = di ->voltage_k*temp + di->voltage_b; + voltage_now = di->voltage_k*temp/1000 + di->voltage_b; - DBG("the rea-time voltage is %d\n",voltage_now); return voltage_now; } -static int _get_OCV_voltage(struct battery_info *di) +static int rk_battery_voltage(struct battery_info *di) { int ret; int voltage_now = 0; u8 buf; int temp; -#if 1 - ret = battery_read(di->rk818,BAT_OCV_REGL, &buf, 1); + + ret = battery_read(di->rk818, BAT_VOL_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818,BAT_OCV_REGH, &buf, 1); + ret = battery_read(di->rk818, BAT_VOL_REGH, &buf, 1); temp |= buf<<8; -#endif - //ret = battery_read(di->rk818,BAT_OCV_REGH, &buf, 2); - if(ret < 0){ - dev_err(di->dev, "error reading BAT_OCV_REGH"); + if (ret < 0) { + dev_err(di->dev, "error read BAT_VOL_REGH"); return ret; } - //voltage_now = temp;//(buf[0]<<8)|buf[1]; - voltage_now = di ->voltage_k*temp + di->voltage_b; - DBG("the OCV voltage is %d\n", voltage_now); + voltage_now = di->voltage_k*temp/1000 + di->voltage_b; return voltage_now; } -#if 0 -static int _get_ts1_adc(struct battery_info *di) -{ - int ret; - int temp = 0; + +/* OCV Lookup table + * Open Circuit Voltage (OCV) correction routine. This function estimates SOC, + * based on the voltage. + */ +static int _voltage_to_capacity(struct battery_info *di, int voltage) +{ + u32 *ocv_table; + int ocv_size; + u32 tmp; + + ocv_table = di->platform_data->battery_ocv; + ocv_size = di->platform_data->ocv_size; + di->warnning_voltage = ocv_table[3]; + tmp = interpolate(voltage, ocv_table, ocv_size); + di->temp_soc = ab_div_c(tmp, MAX_PERCENTAGE, INTERPOLATE_MAX); + di->temp_nac = ab_div_c(tmp, di->fcc, INTERPOLATE_MAX); + + return 0; +} + +static uint16_t _get_relax_vol1(struct battery_info *di) +{ + int ret; u8 buf; + uint16_t temp = 0, voltage_now; - ret = battery_read(di->rk818,TS1_ADC_REGL, &buf, 1); + ret = battery_read(di->rk818, RELAX_VOL1_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818,TS1_ADC_REGH, &buf, 1); - temp = (buf<<8); + ret = battery_read(di->rk818, RELAX_VOL1_REGH, &buf, 1); + temp |= (buf<<8); - return temp; + voltage_now = di->voltage_k*temp/1000 + di->voltage_b; + + return voltage_now; } -static int _get_ts2_adc(struct battery_info *di) +static uint16_t _get_relax_vol2(struct battery_info *di) { int ret; - int temp = 0; + uint16_t temp = 0, voltage_now; u8 buf; -#if 1 - ret = battery_read(di->rk818,TS2_ADC_REGL, &buf, 1); + + ret = battery_read(di->rk818, RELAX_VOL2_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818,TS2_ADC_REGH, &buf, 1); - temp |= buf<<8; -#endif + ret = battery_read(di->rk818, RELAX_VOL2_REGH, &buf, 1); + temp |= (buf<<8); - return temp; + voltage_now = di->voltage_k*temp/1000 + di->voltage_b; + + return voltage_now; } -#endif -static void _capacity_init(struct battery_info *di, u32 capacity) + +static int _get_raw_adc_current(struct battery_info *di) +{ + u8 buf; + int ret; + int current_now; + + ret = battery_read(di->rk818, BAT_CUR_AVG_REGL, &buf, 1); + if (ret < 0) { + dev_err(di->dev, "error reading BAT_CUR_AVG_REGL"); + return ret; + } + current_now = buf; + ret = battery_read(di->rk818, BAT_CUR_AVG_REGH, &buf, 1); + if (ret < 0) { + dev_err(di->dev, "error reading BAT_CUR_AVG_REGH"); + return ret; + } + current_now |= (buf<<8); + + if (ret < 0) { + dev_err(di->dev, "error reading BAT_CUR_AVG_REGH"); + return ret; + } + + return current_now; + +} + + +static void ioffset_sample_time(struct battery_info *di, int time) +{ + u8 ggcon; + + battery_read(di->rk818, GGCON, &ggcon, 1); + ggcon &= ~(0x30); /*clear <5:4>*/ + ggcon |= time; + battery_write(di->rk818, GGCON, &ggcon, 1); + debug_reg(di, GGCON, "GGCON"); +} + +static void update_cal_offset(struct battery_info *di) +{ + int mod = di->queue_work_cnt % TIME_10MIN_SEC; + + DBG("<%s>, queue_work_cnt = %lu, mod = %d\n", __func__, di->queue_work_cnt, mod); + if ((!mod) && (di->pcb_ioffset_updated)) { + _set_cal_offset(di, di->pcb_ioffset+_get_ioffset(di)); + DBG("<%s>. 10min update cal_offset = %d", __func__, di->pcb_ioffset+_get_ioffset(di)); + } +} + + +static void zero_current_calibration(struct battery_info *di) +{ + int adc_value; + uint16_t C0; + uint16_t C1; + int ioffset; + int pcb_offset; + u8 retry = 0; + + if ((di->charge_status == CHARGE_FINISH) && (abs32_int(di->current_avg) > 4)) { + + for (retry = 0; retry < 5; retry++) { + adc_value = _get_raw_adc_current(di); + DBG("<%s>. adc_value = %d\n", __func__, adc_value); + + C0 = _get_cal_offset(di); + C1 = adc_value + C0; + _set_cal_offset(di, C1); + DBG("<%s>. C1 = %d\n", __func__, C1); + msleep(2000); + + adc_value = _get_raw_adc_current(di); + DBG("<%s>. adc_value = %d\n", __func__, adc_value); + if (adc_value < 4) { + + ioffset = _get_ioffset(di); + pcb_offset = C1 - ioffset; + di->pcb_ioffset = pcb_offset; + di->pcb_ioffset_updated = true; + DBG("<%s>. update the cal_offset, pcb_offset = %d\n", __func__, pcb_offset); + break; + } else + di->pcb_ioffset_updated = false; + } + } +} + + +static bool _is_relax_mode(struct battery_info *di) { + int ret; + u8 status; + + ret = battery_read(di->rk818, GGSTS, &status, 1); + + if ((!(status&RELAX_VOL1_UPD)) || (!(status&RELAX_VOL2_UPD))) + return false; + else + return true; +} +static uint16_t get_relax_voltage(struct battery_info *di) +{ + int ret; + u8 status; + uint16_t relax_vol1, relax_vol2; + u8 ggcon; + + ret = battery_read(di->rk818, GGSTS, &status, 1); + ret = battery_read(di->rk818, GGCON, &ggcon, 1); + + relax_vol1 = _get_relax_vol1(di); + relax_vol2 = _get_relax_vol2(di); + DBG("<%s>. GGSTS = 0x%x, GGCON = 0x%x, relax_vol1 = %d, relax_vol2 = %d\n", __func__, status, ggcon, relax_vol1, relax_vol2); + if (_is_relax_mode(di)) + return relax_vol1 > relax_vol2?relax_vol1:relax_vol2; + else + return 0; +} + +static void _set_relax_thres(struct battery_info *di) +{ u8 buf; - u32 capacity_ma; + int enter_thres, exit_thres; + struct cell_state *cell = &di->cell; + + enter_thres = (cell->config->ocv->sleep_enter_current)*1000/1506; + exit_thres = (cell->config->ocv->sleep_exit_current)*1000/1506; - capacity_ma = capacity*2201;//36*14/900*4096/521*500; - DBG("%s WRITE GANCNT_CAL_REG %d\n", __FUNCTION__, capacity_ma); - do{ - buf = (capacity_ma>>24)&0xff; - battery_write(di->rk818, GASCNT_CAL_REG3, &buf,1); - buf = (capacity_ma>>16)&0xff; - battery_write(di->rk818, GASCNT_CAL_REG2, &buf,1); - buf = (capacity_ma>>8)&0xff; - battery_write(di->rk818, GASCNT_CAL_REG1, &buf,1); - buf = (capacity_ma&0xff)|0x01; - battery_write(di->rk818, GASCNT_CAL_REG0, &buf,1); - battery_read(di->rk818,GASCNT_CAL_REG0, &buf, 1); + buf = enter_thres&0xff; + battery_write(di->rk818, RELAX_ENTRY_THRES_REGL, &buf, 1); + buf = (enter_thres>>8)&0xff; + battery_write(di->rk818, RELAX_ENTRY_THRES_REGH, &buf, 1); - }while(buf == 0); - return; + buf = exit_thres&0xff; + battery_write(di->rk818, RELAX_EXIT_THRES_REGL, &buf, 1); + buf = (exit_thres>>8)&0xff; + battery_write(di->rk818, RELAX_EXIT_THRES_REGH, &buf, 1); + /* set sample time */ + battery_read(di->rk818, GGCON, &buf, 1); + buf &= ~(3<<2);/*8min*/ + buf &= ~0x01; /* clear bat_res calc*/ + battery_write(di->rk818, GGCON, &buf, 1); } -static void _save_remain_capacity(struct battery_info *di, u32 capacity) +static void restart_relax(struct battery_info *di) +{ + u8 ggcon;/* chrg_ctrl_reg2;*/ + u8 ggsts; + + battery_read(di->rk818, GGCON, &ggcon, 1); + ggcon &= ~0x0c; + battery_write(di->rk818, GGCON, &ggcon, 1); + + battery_read(di->rk818, GGSTS, &ggsts, 1); + ggsts &= ~0x0c; + battery_write(di->rk818, GGSTS, &ggsts, 1); +} + +static int _get_average_current(struct battery_info *di) { + u8 buf; + int ret; + int current_now; + int temp; + ret = battery_read(di->rk818, BAT_CUR_AVG_REGL, &buf, 1); + if (ret < 0) { + dev_err(di->dev, "error read BAT_CUR_AVG_REGL"); + return ret; + } + current_now = buf; + ret = battery_read(di->rk818, BAT_CUR_AVG_REGH, &buf, 1); + if (ret < 0) { + dev_err(di->dev, "error read BAT_CUR_AVG_REGH"); + return ret; + } + current_now |= (buf<<8); + + if (current_now & 0x800) + current_now -= 4096; + + temp = current_now*1506/1000;/*1000*90/14/4096*500/521;*/ + + return temp; + +} + +static bool _is_first_poweron(struct battery_info *di) +{ u8 buf; - u32 capacity_ma; + u8 temp; - if(capacity >= di ->qmax){ - capacity = di ->qmax; + battery_read(di->rk818, GGSTS, &buf, 1); + DBG("%s GGSTS value is 0x%2x \n", __func__, buf); + /*di->pwron_bat_con = buf;*/ + if (buf&BAT_CON) { + buf &= ~(BAT_CON); + do { + battery_write(di->rk818, GGSTS, &buf, 1); + battery_read(di->rk818, GGSTS, &temp, 1); + } while (temp&BAT_CON); + return true; } - capacity_ma = capacity; -// DBG("%s WRITE GANCNT_CAL_REG %d\n", __FUNCTION__, capacity_ma); - buf = (capacity_ma>>24)&0xff; - battery_write(di->rk818, REMAIN_CAP_REG3, &buf,1); - buf = (capacity_ma>>16)&0xff; - battery_write(di->rk818, REMAIN_CAP_REG2, &buf,1); - buf = (capacity_ma>>8)&0xff; - battery_write(di->rk818, REMAIN_CAP_REG1, &buf,1); - buf = (capacity_ma&0xff)|0x01; - battery_write(di->rk818, REMAIN_CAP_REG0, &buf,1); - - return; + return false; +} +static void flatzone_voltage_init(struct battery_info *di) +{ + u32 *ocv_table; + int ocv_size; + int temp_table[21]; + int i, j; + + ocv_table = di->platform_data->battery_ocv; + ocv_size = di->platform_data->ocv_size; + + for (j = 0; j < 21; j++) + temp_table[j] = 0; + + j = 0; + for (i = 1; i < ocv_size-1; i++) { + if (ocv_table[i+1] < ocv_table[i] + 20) + temp_table[j++] = i; + } + + temp_table[j] = temp_table[j-1]+1; + i = temp_table[0]; + di->enter_flatzone = ocv_table[i]; + j = 0; + + + for (i = 0; i <= 20; i++) { + if (temp_table[i] < temp_table[i+1]) + j = i+1; + } + + i = temp_table[j]; + di->exit_flatzone = ocv_table[i]; + + DBG("enter_flatzone = %d exit_flatzone = %d\n", di->enter_flatzone, di->exit_flatzone); } -static int _get_remain_capacity(struct battery_info *di) +#if 0 +static int is_not_flatzone(struct battery_info *di, int voltage) +{ + if ((voltage >= di->enter_flatzone) && (voltage <= di->exit_flatzone)) { + DBG("<%s>. is in flat zone\n", __func__); + return 0; + } else { + DBG("<%s>. is not in flat zone\n", __func__); + return 1; + } +} +#endif +static void power_on_save(struct battery_info *di, int voltage) { - int ret; - int temp = 0; u8 buf; - u32 capacity; + u8 save_soc; + + battery_read(di->rk818, NON_ACT_TIMER_CNT_REG, &buf, 1); + + if (_is_first_poweron(di) || buf > 30) { /* first power-on or power off time > 30min */ + _voltage_to_capacity(di, voltage); + if (di->temp_soc < 20) { + di->dod0_voltage = voltage; + di->dod0_capacity = di->nac; + di->dod0_status = 1; + di->dod0 = di->temp_soc;/* _voltage_to_capacity(di, voltage); */ + di->dod0_level = 80; + + if (di->temp_soc <= 0) + di->dod0_level = 100; + else if (di->temp_soc < 5) + di->dod0_level = 95; + else if (di->temp_soc < 10) + di->dod0_level = 90; + /* save_soc = di->dod0_level; */ + save_soc = get_level(di); + if (save_soc < di->dod0_level) + save_soc = di->dod0_level; + save_level(di, save_soc); + DBG("<%s>UPDATE-FCC POWER ON : dod0_voltage = %d, dod0_capacity = %d ", __func__, di->dod0_voltage, di->dod0_capacity); + } + } - ret = battery_read(di->rk818,REMAIN_CAP_REG3, &buf, 1); - temp = buf<<24; - ret = battery_read(di->rk818,REMAIN_CAP_REG2, &buf, 1); - temp |= buf<<16; - ret = battery_read(di->rk818,REMAIN_CAP_REG1, &buf, 1); - temp |= buf<<8; - ret = battery_read(di->rk818,REMAIN_CAP_REG0, &buf, 1); - temp |= buf; +} - capacity = temp;///4096*900/14/36*500/521; - DBG("%s GASCNT_CAL_REG %d capacity =%d \n",__FUNCTION__, temp, capacity); - return capacity; +static int _get_soc(struct battery_info *di) +{ + return di->remain_capacity * 100 / di->fcc; } +static enum power_supply_property rk_battery_props[] = { -static int _get_capacity(struct battery_info *di) + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CAPACITY, +}; + +#define to_device_info(x) container_of((x), \ + struct battery_info, bat) + +static int rk_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct battery_info *di = to_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = di->current_avg*1000;/*uA*/ + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = di->voltage*1000;/*uV*/ + break; + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = val->intval <= 0 ? 0 : 1; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = di->real_soc; + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = di->status; + break; + + default: + return -EINVAL; + } + + return 0; +} + + +static enum power_supply_property rk_battery_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; +static enum power_supply_property rk_battery_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + + +#define to_ac_device_info(x) container_of((x), \ + struct battery_info, ac) + +static int rk_battery_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct battery_info *di = to_ac_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = di->ac_online; /*discharging*/ + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +#define to_usb_device_info(x) container_of((x), \ + struct battery_info, usb) + +static int rk_battery_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct battery_info *di = to_usb_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if ((strstr(saved_command_line, "charger") == NULL) && (di->real_soc == 0) && (di->work_on == 1)) + val->intval = 0; + else + val->intval = di->usb_online; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + + +static void battery_power_supply_init(struct battery_info *di) +{ + di->bat.name = "BATTERY"; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = rk_battery_props; + di->bat.num_properties = ARRAY_SIZE(rk_battery_props); + di->bat.get_property = rk_battery_get_property; + + di->ac.name = "AC"; + di->ac.type = POWER_SUPPLY_TYPE_MAINS; + di->ac.properties = rk_battery_ac_props; + di->ac.num_properties = ARRAY_SIZE(rk_battery_ac_props); + di->ac.get_property = rk_battery_ac_get_property; + + di->usb.name = "USB"; + di->usb.type = POWER_SUPPLY_TYPE_USB; + di->usb.properties = rk_battery_usb_props; + di->usb.num_properties = ARRAY_SIZE(rk_battery_usb_props); + di->usb.get_property = rk_battery_usb_get_property; +} + +static int battery_power_supply_register(struct battery_info *di, struct device *dev) { int ret; - int temp = 0; + + ret = power_supply_register(dev, &di->bat); + if (ret) { + dev_err(dev, "failed to register main battery\n"); + goto batt_failed; + } + ret = power_supply_register(dev, &di->usb); + if (ret) { + dev_err(dev, "failed to register usb power supply\n"); + goto usb_failed; + } + ret = power_supply_register(dev, &di->ac); + if (ret) { + dev_err(dev, "failed to register ac power supply\n"); + goto ac_failed; + } + + return 0; + +ac_failed: + power_supply_unregister(&di->ac); +usb_failed: + power_supply_unregister(&di->usb); +batt_failed: + power_supply_unregister(&di->bat); + + return ret; +} + +#if 1 +/*calc battery resister*/ +static void res_mode_init(struct battery_info *di) +{ + u8 ggcon;/* chrg_ctrl_reg2; */ + u8 ggsts; + + battery_read(di->rk818, GGCON, &ggcon, 1); + battery_read(di->rk818, GGSTS, &ggsts, 1); + + ggcon |= 0x01; + ggsts &= ~0x01; + ggsts |= 0x60; + battery_write(di->rk818, GGCON, &ggcon, 1); + battery_write(di->rk818, GGSTS, &ggsts, 1); +} +#endif + +static void _capacity_init(struct battery_info *di, u32 capacity) +{ u8 buf; - u32 capacity; + u32 capacity_ma; - ret = battery_read(di->rk818,GASCNT_CAL_REG3, &buf, 1); - temp = buf<<24; - ret = battery_read(di->rk818,GASCNT_CAL_REG2, &buf, 1); - temp |= buf<<16; - ret = battery_read(di->rk818,GASCNT_CAL_REG1, &buf, 1); - temp |= buf<<8; - ret = battery_read(di->rk818,GASCNT_CAL_REG0, &buf, 1); - temp |= buf; + di->update_k = 0; + di->update_q = 0; + di->voltage_old = 0; + di->display_soc = 0; - capacity = temp/2201;///4096*900/14/36*500/521; - //DBG("%s GASCNT_CAL_REG %d capacity =%d \n",__FUNCTION__, temp, capacity); - return capacity; + capacity_ma = capacity*2390;/* 2134;//36*14/900*4096/521*500; */ + do { + buf = (capacity_ma>>24)&0xff; + battery_write(di->rk818, GASCNT_CAL_REG3, &buf, 1); + buf = (capacity_ma>>16)&0xff; + battery_write(di->rk818, GASCNT_CAL_REG2, &buf, 1); + buf = (capacity_ma>>8)&0xff; + battery_write(di->rk818, GASCNT_CAL_REG1, &buf, 1); + buf = (capacity_ma&0xff) | 0x01; + battery_write(di->rk818, GASCNT_CAL_REG0, &buf, 1); + battery_read(di->rk818, GASCNT_CAL_REG0, &buf, 1); + } while (buf == 0); } -static int _get_realtime_capacity(struct battery_info *di) + +static void _save_remain_capacity(struct battery_info *di, u32 capacity) { + u8 buf; + u32 capacity_ma; + if (capacity >= di->qmax) + capacity = di->qmax; + + capacity_ma = capacity; + + buf = (capacity_ma>>24)&0xff; + battery_write(di->rk818, REMAIN_CAP_REG3, &buf, 1); + buf = (capacity_ma>>16)&0xff; + battery_write(di->rk818, REMAIN_CAP_REG2, &buf, 1); + buf = (capacity_ma>>8)&0xff; + battery_write(di->rk818, REMAIN_CAP_REG1, &buf, 1); + buf = (capacity_ma&0xff) | 0x01; + battery_write(di->rk818, REMAIN_CAP_REG0, &buf, 1); +} + +static int _get_remain_capacity(struct battery_info *di) +{ int ret; int temp = 0; u8 buf; u32 capacity; - ret = battery_read(di->rk818,GASCNT3, &buf, 1); - temp = buf<<24; - ret = battery_read(di->rk818,GASCNT2, &buf, 1); - temp |= buf<<16; - ret = battery_read(di->rk818,GASCNT1, &buf, 1); - temp |= buf<<8; - ret = battery_read(di->rk818,GASCNT0, &buf, 1); + ret = battery_read(di->rk818, REMAIN_CAP_REG3, &buf, 1); + temp = buf << 24; + ret = battery_read(di->rk818, REMAIN_CAP_REG2, &buf, 1); + temp |= buf << 16; + ret = battery_read(di->rk818, REMAIN_CAP_REG1, &buf, 1); + temp |= buf << 8; + ret = battery_read(di->rk818, REMAIN_CAP_REG0, &buf, 1); temp |= buf; -// ret = battery_read(di->rk818,GASCNT_CAL_REG3, &buf, 4); -// temp = buf[0] << 24 | buf[1] << 24 | buf[2] << 24 |buf[3] ; - capacity = temp/2201;///4096*900/14/36*500/521; - //DBG("%s GASCNT = 0x%4x capacity =%d \n",__FUNCTION__, temp,capacity); - return capacity; - + capacity = temp;/* /4096*900/14/36*500/521; */ + + return capacity; } -static int _get_vcalib0(struct battery_info *di) + +static void _save_FCC_capacity(struct battery_info *di, u32 capacity) { + u8 buf; + u32 capacity_ma; + capacity_ma = capacity; + buf = (capacity_ma>>24)&0xff; + battery_write(di->rk818, NEW_FCC_REG3, &buf, 1); + buf = (capacity_ma>>16)&0xff; + battery_write(di->rk818, NEW_FCC_REG2, &buf, 1); + buf = (capacity_ma>>8)&0xff; + battery_write(di->rk818, NEW_FCC_REG1, &buf, 1); + buf = (capacity_ma&0xff) | 0x01; + battery_write(di->rk818, NEW_FCC_REG0, &buf, 1); +} + +static int _get_FCC_capacity(struct battery_info *di) +{ int ret; int temp = 0; u8 buf; -#if 1 - ret = battery_read(di->rk818,VCALIB0_REGL, &buf, 1); - temp = buf; - ret = battery_read(di->rk818,VCALIB0_REGH, &buf, 1); - temp |= buf<<8; -#endif - //ret = battery_read(di->rk818,VCALIB0_REGH, &buf,2); - //temp = (buf[0]<<8)|buf[1]; + u32 capacity; - DBG("%s voltage0 offset vale is %d\n",__FUNCTION__, temp); - return temp; + ret = battery_read(di->rk818, NEW_FCC_REG3, &buf, 1); + temp = buf << 24; + ret = battery_read(di->rk818, NEW_FCC_REG2, &buf, 1); + temp |= buf << 16; + ret = battery_read(di->rk818, NEW_FCC_REG1, &buf, 1); + temp |= buf << 8; + ret = battery_read(di->rk818, NEW_FCC_REG0, &buf, 1); + temp |= buf; + + if (temp > 1) + capacity = temp-1;/* 4096*900/14/36*500/521 */ + else + capacity = temp; + DBG("%s NEW_FCC_REG %d capacity = %d\n", __func__, temp, capacity); + + return capacity; } -static int _get_vcalib1(struct battery_info *di) +static int _get_realtime_capacity(struct battery_info *di) { - int ret; int temp = 0; u8 buf; - #if 1 - ret = battery_read(di->rk818,VCALIB1_REGL, &buf, 1); - temp = buf; - ret = battery_read(di->rk818,VCALIB1_REGH, &buf, 1); - temp |= buf<<8; - #endif - //ret = battery_read(di->rk818,VCALIB1_REGH, &buf, 2); - //temp = (buf[0]<<8)|buf[1]; - DBG("%s voltage1 offset vale is %d\n",__FUNCTION__, temp); - return temp; -} + u32 capacity; -static void _get_voltage_offset_value(struct battery_info *di) -{ - int vcalib0,vcalib1; + ret = battery_read(di->rk818, GASCNT3, &buf, 1); + temp = buf << 24; + ret = battery_read(di->rk818, GASCNT2, &buf, 1); + temp |= buf << 16; + ret = battery_read(di->rk818, GASCNT1, &buf, 1); + temp |= buf << 8; + ret = battery_read(di->rk818, GASCNT0, &buf, 1); + temp |= buf; - vcalib0 = _get_vcalib0(di); - vcalib1 = _get_vcalib1(di); + capacity = temp/2390;/* 4096*900/14/36*500/521; */ - di->voltage_k = (4200 - 3000)/(vcalib1 - vcalib0); - di->voltage_b = 4200 - di->voltage_k*vcalib1; - - return; + return capacity; } -static int _get_ioffset(struct battery_info *di) +static void relax_volt_update_remain_capacity(struct battery_info *di, uint16_t relax_voltage, int sleep_min) { + int remain_capacity; + int relax_capacity; + int now_temp_soc; + int relax_soc; + int abs_soc; + int min, soc_time; + int now_current; - int ret; - int temp = 0; - u8 buf; + now_temp_soc = _get_soc(di); + _voltage_to_capacity(di, relax_voltage); + relax_soc = di->temp_soc; + relax_capacity = di->temp_nac; + abs_soc = abs32_int(relax_soc - now_temp_soc); - ret = battery_read(di->rk818,IOFFSET_REGL, &buf, 1); - temp = buf; - ret = battery_read(di->rk818,IOFFSET_REGH, &buf, 1); - temp |= buf<<8; + DBG("<%s>. suspend_temp_soc=%d, temp_soc=%d, ,real_soc = %d\n", __func__, di->suspend_temp_soc, now_temp_soc, di->real_soc); + DBG("<%s>. relax_soc = %d, abs_soc = %d\n", __func__, relax_soc, abs_soc); - //ret = battery_read(di->rk818,IOFFSET_REGH, &buf, 2); - //temp = (buf[0]<<8)|buf[1]; + /*handle temp_soc*/ + if (abs32_int(di->real_soc - relax_soc) <= 5) { + remain_capacity = relax_capacity; + DBG("<%s>. real-soc is close to relax-soc, set: temp_soc = relax_soc\n", __func__); + } else { + if (abs_soc == 0) + remain_capacity = _get_realtime_capacity(di); + else if (abs_soc <= 10) + remain_capacity = relax_capacity; + else if (abs_soc <= 20) + remain_capacity = relax_capacity*70/100+di->remain_capacity*30/100; + else + remain_capacity = relax_capacity*50/100+di->remain_capacity*50/100; + } + _capacity_init(di, remain_capacity); + di->temp_soc = _get_soc(di); + di->remain_capacity = _get_realtime_capacity(di); + + /*handle real_soc*/ + DBG("<%s>. real_soc = %d, adjust delta = %d\n", __func__, di->real_soc, di->suspend_temp_soc - relax_soc); + if (relax_soc < now_temp_soc) { + if (di->suspend_temp_soc - relax_soc <= 5) + di->real_soc = di->real_soc - (di->suspend_temp_soc - relax_soc); + else if (di->suspend_temp_soc - relax_soc <= 10) + di->real_soc = di->real_soc - 5; + else + di->real_soc = di->real_soc - (di->suspend_temp_soc - relax_soc)/2; + } else { + now_current = _get_average_current(di); + soc_time = di->fcc*3600/100/(abs_int(now_current));/*1% time cost*/ + min = soc_time / 60; + if (sleep_min > min) + di->real_soc--; + } - DBG("%s IOFFSET value is %d\n", __FUNCTION__, temp); - return temp; + DBG("<%s>. new_temp_soc=%d, new_real_soc=%d, new_remain_cap=%d\n", __func__, _get_soc(di), di->real_soc, di->remain_capacity); } -static int _set_cal_offset(struct battery_info *di, u32 value) + +static int _copy_soc(struct battery_info *di, u8 save_soc) { - int ret; - int temp = 0; - u8 buf; - DBG("%s\n",__FUNCTION__); - buf = value&0xff; - ret = battery_write(di->rk818, CAL_OFFSET_REGL, &buf, 1); //enable - buf = (value >> 8)&0xff; - ret = battery_write(di->rk818, CAL_OFFSET_REGH, &buf, 1); //enable - DBG("%s set CAL_OFFSET_REG %d\n",__FUNCTION__, temp); + u8 soc; + soc = save_soc; + battery_write(di->rk818, SOC_REG, &soc, 1); return 0; } -static bool _is_first_poweron(struct battery_info * di) +static bool support_uboot_charge(void) { - u8 buf; - u8 temp; - u8 ret; - - ret = battery_read(di->rk818,GGSTS, &buf, 1); - DBG("%s GGSTS value is %2x \n", __FUNCTION__, buf ); - if( buf&BAT_CON){ - buf &=~(BAT_CON); - do{ - battery_write(di->rk818,GGSTS, &buf, 1); - battery_read(di->rk818,GGSTS, &temp, 1); - }while(temp&BAT_CON); - return true; - } - return false; + return support_uboot_chrg?true:false; } - -#if 0 -static bool fg_check_relaxed(struct battery_info * di)//(struct cell_state *cell) +static int _rsoc_init(struct battery_info *di) { - struct cell_state *cell = &di->cell; + u8 pwron_soc; + u8 init_soc; + u32 remain_capacity; + u8 last_shtd_time; + u8 curr_shtd_time; +#ifdef SUPPORT_USB_CHARGE + int otg_status; +#else + u8 buf; +#endif + di->voltage = rk_battery_voltage(di); + di->voltage_ocv = _get_OCV_voltage(di); + DBG("OCV voltage = %d\n" , di->voltage_ocv); - struct timeval now; - - if (!cell->sleep) { - if (abs_int(di->current_avg) <= - cell->config->ocv->sleep_enter_current) { - if (cell->sleep_samples < MAX_UINT8) - cell->sleep_samples++; - - if (cell->sleep_samples >= - cell->config->ocv->sleep_enter_samples) { - /* Entering sleep mode */ - do_gettimeofday(&cell->sleep_timer); - do_gettimeofday(&cell->el_sleep_timer); - cell->sleep = true; - cell->calibrate = true; - } - } else { - cell->sleep_samples = 0; - } - } else { - /* The battery cell is Sleeping, checking if need to exit - sleep mode count number of seconds that cell spent in - sleep */ - do_gettimeofday(&now); - cell->cumulative_sleep += - now.tv_sec + cell->el_sleep_timer.tv_sec; - do_gettimeofday(&cell->el_sleep_timer); + if (_is_first_poweron(di)) { + _save_FCC_capacity(di, di->design_capacity); + di->fcc = _get_FCC_capacity(di); - /* Check if we need to reset Sleep */ - if (abs_int(di->current_avg) > - cell->config->ocv->sleep_exit_current) { + _voltage_to_capacity(di, di->voltage_ocv); + di->real_soc = di->temp_soc; + di->nac = di->temp_nac; + DBG("<%s>.this is first poweron: OCV-SOC = %d, OCV-CAPACITY = %d, FCC = %d\n", __func__, di->real_soc, di->nac, di->fcc); - if (abs_int(di->current_avg) > - cell->config->ocv->sleep_exit_current) { + } else { + battery_read(di->rk818, SOC_REG, &pwron_soc, 1); + init_soc = pwron_soc; + DBG("<%s>this is NOT first poweron.SOC_REG = %d\n", __func__, pwron_soc); + +#ifdef SUPPORT_USB_CHARGE + otg_status = dwc_otg_check_dpdm(); + if ((pwron_soc == 0) && (otg_status == 1)) { /*usb charging*/ + init_soc = 1; + battery_write(di->rk818, SOC_REG, &init_soc, 1); + } +#else + battery_read(di->rk818, VB_MOD_REG, &buf, 1); + if ((pwron_soc == 0) && ((buf&PLUG_IN_STS) != 0)) { + init_soc = 1; + battery_write(di->rk818, SOC_REG, &init_soc, 1); + } +#endif + remain_capacity = _get_remain_capacity(di); - if (cell->sleep_samples < MAX_UINT8) - cell->sleep_samples++; + battery_read(di->rk818, NON_ACT_TIMER_CNT_REG, &curr_shtd_time, 1); + battery_read(di->rk818, NON_ACT_TIMER_CNT_REG_SAVE, &last_shtd_time, 1); + battery_write(di->rk818, NON_ACT_TIMER_CNT_REG_SAVE, &curr_shtd_time, 1); + DBG("<%s>, now_shtd_time = %d, last_shtd_time = %d, otg_status = %d\n", __func__, curr_shtd_time, last_shtd_time, otg_status); - } else { - cell->sleep_samples = 0; - } + if (!support_uboot_charge()) { + _voltage_to_capacity(di, di->voltage_ocv); + DBG("<%s>Not first pwron, real_remain_cap = %d, ocv-remain_cp=%d\n", __func__, remain_capacity, di->temp_nac); - /* Check if we need to reset a Sleep timer */ - if (cell->sleep_samples > - cell->config->ocv->sleep_exit_samples) { - /* Exit sleep mode */ + /* if plugin, make sure current shtd_time different from last_shtd_time.*/ + if (((otg_status != 0) && (curr_shtd_time > 0) && (last_shtd_time != curr_shtd_time)) || ((curr_shtd_time > 0) && (otg_status == 0))) { - cell->sleep_timer.tv_sec = 0; - cell->sleep = false; - cell->relax = false; - } - } else { - cell->sleep_samples = 0; + if (curr_shtd_time > 30) { + remain_capacity = di->temp_nac; + DBG("<%s>shutdown_time > 30 minute, remain_cap = %d\n", __func__, remain_capacity); - if (!cell->relax) { + } else if ((curr_shtd_time > 5) && (abs32_int(di->temp_soc - di->real_soc) >= 10)) { + if (remain_capacity >= di->temp_nac*120/100) + remain_capacity = di->temp_nac*110/100; + else if (remain_capacity < di->temp_nac*8/10) + remain_capacity = di->temp_nac*9/10; - if (now.tv_sec-cell->sleep_timer.tv_sec > - cell->config->ocv->relax_period) { - cell->relax = true; - cell->calibrate = true; + DBG("<%s> shutdown_time > 3 minute, remain_cap = %d\n", __func__, remain_capacity); } } } + + di->real_soc = init_soc; + di->nac = remain_capacity; + if (di->nac <= 0) + di->nac = 0; + DBG("<%s> init_soc = %d, init_capacity=%d\n", __func__, di->real_soc, di->nac); } - - return cell->relax; + return 0; } -/* Checks for right conditions for OCV correction */ -static bool fg_can_ocv(struct battery_info * di)//(struct cell_state *cell) + +static u8 get_charge_status(struct battery_info *di) { - struct cell_state *cell = &di->cell; -#if 1 - /* Voltage should be stable */ - if (cell->config->ocv->voltage_diff <= diff_array(av_v, AV_SIZE)) - return false; + u8 status; + u8 ret = 0; + + battery_read(di->rk818, SUP_STS_REG, &status, 1); + status &= (0x70); + switch (status) { + case CHARGE_OFF: + ret = CHARGE_OFF; + DBG(" CHARGE-OFF ...\n"); + break; - /* Current should be stable */ - if (cell->config->ocv->current_diff <= diff_array(av_c, AV_SIZE)) - return false; -#endif - /* SOC should be out of Flat Zone */ - if ((di->real_soc>= cell->config->ocv->flat_zone_low) - && (di->real_soc <= cell->config->ocv->flat_zone_high)) - return false; + case DEAD_CHARGE: + ret = DEAD_CHARGE; + DBG(" DEAD CHARGE ...\n"); + break; - /* Current should be less then SleepEnterCurrent */ - if (abs_int(di->current_avg) >= cell->config->ocv->sleep_enter_current) - return false; + case TRICKLE_CHARGE: /* (0x02 << 4) */ + ret = DEAD_CHARGE; + DBG(" TRICKLE CHARGE ...\n "); + break; - /* Don't allow OCV below EDV1, unless OCVbelowEDV1 is set */ - //if (cell->edv1 && !cell->config->ocv_below_edv1) - // return false; + case CC_OR_CV: /* (0x03 << 4) */ + ret = CC_OR_CV; + DBG(" CC or CV ...\n"); + break; - return true; -} + case CHARGE_FINISH: /* (0x04 << 4) */ + ret = CHARGE_FINISH; + DBG(" CHARGE FINISH ...\n"); + break; -#endif + case USB_OVER_VOL: /* (0x05 << 4) */ + ret = USB_OVER_VOL; + DBG(" USB OVER VOL ...\n"); + break; -/* Sets the battery Voltage, and recalculates the average voltage */ -void fg_set_voltage(int16_t voltage) -{ - int16_t i; - int32_t tmp = 0; + case BAT_TMP_ERR: /* (0x06 << 4) */ + ret = BAT_TMP_ERR; + DBG(" BAT TMP ERROR ...\n"); + break; - /* put voltage reading int the buffer and update average */ - av_v_index++; - av_v_index %= AV_SIZE; - av_v[av_v_index] = voltage; - for (i = 0; i < AV_SIZE; i++) - tmp += av_v[i]; -} + case TIMER_ERR: /* (0x07 << 4) */ + ret = TIMER_ERR; + DBG(" TIMER ERROR ...\n"); + break; + case USB_EXIST: /* (1 << 1)// usb is exists */ + ret = USB_EXIST; + DBG(" USB EXIST ...\n"); + break; -/* Sets the battery Current, and recalculates the average current */ -void fg_set_current( int16_t cur) -{ - int16_t i; - int32_t tmp = 0; + case USB_EFF: /* (1 << 0)// usb is effective */ + ret = USB_EFF; + DBG(" USB EFF...\n"); + break; - /* put current reading int the buffer and update average */ - av_c_index++; - av_c_index %= AV_SIZE; - av_c[av_c_index] = cur; - for (i = 0; i < AV_SIZE; i++) - tmp += av_c[i]; + default: + return -EINVAL; + } + + return ret; + +} +static void set_charge_current(struct battery_info *di, int charge_current) +{ + u8 usb_ctrl_reg; + battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + usb_ctrl_reg &= (~0x0f);/* (VLIM_4400MV | ILIM_1200MA) |(0x01 << 7); */ + usb_ctrl_reg |= (charge_current | CHRG_CT_EN); + battery_write(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); } -static int _copy_soc(struct battery_info * di, u8 save_soc) +static void rk_battery_charger_init(struct battery_info *di) { - u8 soc; + u8 chrg_ctrl_reg1, usb_ctrl_reg, chrg_ctrl_reg2, chrg_ctrl_reg3; + u8 sup_sts_reg; - soc = save_soc; - //soc = 85; - battery_write(di->rk818, SOC_REG, &soc, 1); - battery_read(di->rk818, SOC_REG, &soc, 1); - DBG(" the save soc-reg = %d \n", soc); - - return 0; + DBG("%s start\n", __func__); + battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + battery_read(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + battery_read(di->rk818, SUP_STS_REG, &sup_sts_reg, 1); + battery_read(di->rk818, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + + DBG("old usb_ctrl_reg = 0x%2x, CHRG_CTRL_REG1 = 0x%2x\n ", usb_ctrl_reg, chrg_ctrl_reg1); + usb_ctrl_reg &= (~0x0f); +#ifdef SUPPORT_USB_CHARGE + usb_ctrl_reg |= (VLIM_4400MV | ILIM_45MA | CHRG_CT_EN); +#else + usb_ctrl_reg |= (VLIM_4400MV | ILIM_3000MA) | CHRG_CT_EN); +#endif + chrg_ctrl_reg1 &= (0x00); + chrg_ctrl_reg1 |= (CHRG_EN) | (CHRG_VOL4200 | CHRG_CUR1400mA); + + chrg_ctrl_reg3 |= CHRG_TERM_DIG_SIGNAL;/* digital finish mode*/ + chrg_ctrl_reg2 &= ~(0xc0); + chrg_ctrl_reg2 |= FINISH_100MA; + + sup_sts_reg &= ~(0x01 << 3); + sup_sts_reg |= (0x01 << 2); + + battery_write(di->rk818, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + battery_write(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + battery_write(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + battery_write(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + battery_write(di->rk818, SUP_STS_REG, &sup_sts_reg, 1); + + debug_reg(di, CHRG_CTRL_REG1, "CHRG_CTRL_REG1"); + debug_reg(di, SUP_STS_REG, "SUP_STS_REG"); + debug_reg(di, USB_CTRL_REG, "USB_CTRL_REG"); + debug_reg(di, CHRG_CTRL_REG1, "CHRG_CTRL_REG1"); + + DBG("%s end\n", __func__); } -static int _rsoc_init(struct battery_info * di) +void charge_disable_open_otg(int value) { - int vol; - u8 temp; - u32 remain_capacity; - - vol = di->voltage_ocv; //_get_OCV_voltage(di); - DBG("OCV voltage = %d\n" , di->voltage_ocv); - if(_is_first_poweron(di)){ + struct battery_info *di = g_battery; - DBG(" %s this is first poweron\n", __FUNCTION__); - _voltage_to_capacity(di, di->voltage_ocv); - di->real_soc = di->temp_soc; - di->nac = di->temp_nac; - }else{ - DBG(" %s this is not not not first poweron\n", __FUNCTION__); - battery_read(di->rk818,SOC_REG, &temp, 1); - remain_capacity = _get_remain_capacity(di); - if(remain_capacity >= di->qmax) - remain_capacity = di->qmax; - DBG("saved SOC_REG = 0x%8x\n", temp); - DBG("saved remain_capacity = %d\n", remain_capacity); - - - di->real_soc = temp; - //di->nac = di->fcc*temp/100; - di->nac = remain_capacity; + if (value == 1) { + DBG("charge disable, enable OTG.\n"); + rk818_set_bits(di->rk818, CHRG_CTRL_REG1, 1 << 7, 0 << 7); + rk818_set_bits(di->rk818, 0x23, 1 << 7, 1 << 7); /* enable OTG */ + } + if (value == 0) { + DBG("charge enable, disable OTG.\n"); + rk818_set_bits(di->rk818, 0x23, 1 << 7, 0 << 7); /* disable OTG */ + rk818_set_bits(di->rk818, CHRG_CTRL_REG1, 1 << 7, 1 << 7); } - return 0; } -static int _get_soc(struct battery_info *di) +static void low_waring_init(struct battery_info *di) { + u8 vb_mon_reg; + u8 vb_mon_reg_init; - return di->remain_capacity * 100 / di->fcc; + battery_read(di->rk818, VB_MOD_REG, &vb_mon_reg, 1); + + /* 2.8v~3.5v, interrupt */ + vb_mon_reg_init = (((vb_mon_reg | (1 << 4)) & (~0x07)) | 0x06); /* 3400mV*/ + battery_write(di->rk818, VB_MOD_REG, &vb_mon_reg_init, 1); } -static u8 get_charge_status(struct battery_info * di) +static void fg_init(struct battery_info *di) { - u8 status; - u8 ret =0; - - battery_read(di->rk818, SUP_STS_REG, &status, 1); - DBG("%s ----- SUP_STS_REG(0xA0) = 0x%02x\n", __FUNCTION__, status); - status &= ~(0x07<<4); - switch(status){ - case CHARGE_OFF: - ret = CHARGE_OFF; - break; - case DEAD_CHARGE: - ret = DEAD_CHARGE; - break; - case TRICKLE_CHARGE:// (0x02<<4) - ret = DEAD_CHARGE; - break; - case CC_OR_CV: // (0x03<<4) - ret = CC_OR_CV; - break; - case CHARGE_FINISH:// (0x04<<4) - ret = CHARGE_FINISH; - break; + u8 adc_ctrl_val; - case USB_OVER_VOL:// (0x05<<4) - ret = USB_OVER_VOL; - break; + adc_ctrl_val = 0x30; + battery_write(di->rk818, ADC_CTRL_REG, &adc_ctrl_val, 1); - case BAT_TMP_ERR:// (0x06<<4) - ret = BAT_TMP_ERR; - break; + _gauge_enable(di); + /* get the volatege offset */ + _get_voltage_offset_value(di); + rk_battery_charger_init(di); + _set_relax_thres(di); + /* get the current offset , the value write to the CAL_OFFSET */ + di->current_offset = _get_ioffset(di); + _set_cal_offset(di, di->current_offset+42); + _rsoc_init(di); + _capacity_init(di, di->nac); + res_mode_init(di); + di->remain_capacity = _get_realtime_capacity(di); + di->current_avg = _get_average_current(di); + + low_waring_init(di); + restart_relax(di); + power_on_save(di, di->voltage_ocv); + /* set sample time for cal_offset interval*/ + ioffset_sample_time(di, SAMP_TIME_8MIN); + dump_gauge_register(di); + dump_charger_register(di); - case TIMER_ERR:// (0x07<<4) - ret = TIMER_ERR; - break; + DBG("<%s> :\n" + "nac = %d , remain_capacity = %d\n" + "OCV_voltage = %d, voltage = %d\n" + "SOC = %d, fcc = %d\n", + __func__, + di->nac, di->remain_capacity, + di->voltage_ocv, di->voltage, + di->real_soc, di->fcc); +} + + +/* int R_soc, D_soc, r_soc, zq, k, Q_err, Q_ocv; */ +static void zero_get_soc(struct battery_info *di) +{ + int ocv_voltage, check_voltage; + int temp_soc = -1, real_soc; + int currentold, currentnow, voltage; + int i; + int voltage_k; + int count_num = 0; + + DBG("\n\n+++++++zero mode++++++display soc+++++++++++\n"); + /* if (di->voltage < 3600)//di->warnning_voltage) */ + { + /* DBG("+++++++zero mode++++++++displaysoc+++++++++\n"); */ + do { + currentold = _get_average_current(di); + _get_cal_offset(di); + _get_ioffset(di); + msleep(100); + currentnow = _get_average_current(di); + count_num++; + } while ((currentold == currentnow) && (count_num < 11)); + + voltage = 0; + for (i = 0; i < 10 ; i++) + voltage += rk_battery_voltage(di); + voltage /= 10; + + if (di->voltage_old == 0) + di->voltage_old = voltage; + voltage_k = voltage; + voltage = (di->voltage_old*2 + 8*voltage)/10; + di->voltage_old = voltage; + /* DBG("Zero: voltage = %d\n", voltage); */ + + currentnow = _get_average_current(di); + /* DBG(" zero: current = %d, voltage = %d\n", currentnow, voltage); */ + + ocv_voltage = 3400 + abs32_int(currentnow)*200/1000; + check_voltage = voltage + abs32_int(currentnow)*(200 - 65)/1000; /* 65 mo power-path mos */ + _voltage_to_capacity(di, check_voltage); + /* if ((di->remain_capacity > di->nac) && (update_q == 0)) */ + /* DBG(" xxx Zerro: tui suan OCV cap :%d\n", di->temp_nac); */ + di->update_q = di->remain_capacity - di->temp_nac; + /* update_q = di->temp_nac; */ + + /* DBG("Zero: update_q = %d , remain_capacity = %d, temp_nac = %d\n ", di->update_q, di->remain_capacity, di->temp_nac); */ + /* relax_volt_update_remain_capacity(di, 3600 + abs32_int(di->current_avg)*200/1000); */ + + _voltage_to_capacity(di, ocv_voltage); + /*di->temp_nac; + temp_soc = _get_soc(di); */ + if (di->display_soc == 0) + di->display_soc = di->real_soc*1000; + + real_soc = di->display_soc; + /* DBG(" Zerro: Q (err) cap :%d\n", di->temp_nac); + DBG(" ZERO : real-soc = %d\n ", di->real_soc); */ + DBG("ZERO : ocv_voltage = %d, check_voltage = %d\n ", ocv_voltage, check_voltage); + if (di->remain_capacity > di->temp_nac + di->update_q) { + + if (di->update_k == 0 || di->update_k >= 10) { + /* DBG("one..\n"); */ + if (di->update_k == 0) { + di->line_q = di->temp_nac + di->update_q; /* ZQ = Q_ded + Qerr */ + /* line_q = update_q - di->temp_nac; */ + temp_soc = (di->remain_capacity - di->line_q)*1000/di->fcc;/* (RM - ZQ) / FCC = r0 = R0 ; */ + /* temp_soc = (line_q)*1000/di->fcc;//(RM - ZQ) / FCC = r0 = R0 ;* + /di->line_k = (real_soc*1000 + temp_soc/2)/temp_soc;//k0 = y0/x0 */ + di->line_k = (real_soc + temp_soc/2)/temp_soc;/* k0 = y0/x0 */ + /* DBG("Zero: one link = %d realsoc = %d , temp_soc = %d\n", di->line_k, di->real_soc, temp_soc); */ + + + } else { + /* + if (line_q == 0) + line_q = di->temp_nac + update_q; + */ + /* DBG("two...\n"); */ + temp_soc = ((di->remain_capacity - di->line_q)*1000 + di->fcc/2)/di->fcc; /* x1 10 */ + /* + temp_soc = (line_q)*1000/di->fcc;// x1 + real_soc = (di->line_k*temp_soc+500)/1000; //y1 = k0*x1 + */ + real_soc = (di->line_k*temp_soc); /* y1 = k0*x1 */ + /* DBG("Zero: two link = %d realsoc = %d , temp_soc = %d\n", di->line_k, real_soc, temp_soc); */ + di->display_soc = real_soc; + /* if (real_soc != di->real_soc) */ + if ((real_soc+500)/1000 < di->real_soc) + di->real_soc--; + /* + DBG("Zero two di->real_soc = %d\n", di->real_soc); + DBG("Zero : temp_soc : %d\n", real_soc); + */ + _voltage_to_capacity(di, ocv_voltage); + di->line_q = di->temp_nac + di->update_q; /* Q1 */ + /* line_q = update_q - di->temp_nac; */ + temp_soc = ((di->remain_capacity - di->line_q)*1000 + di->fcc/2)/di->fcc; /* z1 */ + /* + temp_soc = (line_q)*1000/di->fcc; + di->line_k = (di->real_soc*1000 + temp_soc/2)/temp_soc; //k1 = y1/z1 + */ + di->line_k = (di->display_soc + temp_soc/2)/temp_soc; /* k1 = y1/z1 */ + /* DBG("Zero: two link = %d display_soc = %d , temp_soc = %d\n", di->line_k, di->display_soc, temp_soc); */ + /* line_q = di->temp_nac + update_q;// Q1 */ - case USB_EXIST:// (1<<1)// usb is exists - ret = USB_EXIST; - break; - case USB_EFF:// (1<<0)// usb is effective - ret = USB_EFF; - break; - default: - return -EINVAL; - } + } + di->update_k = 0; - return ret; + } + + /* DBG("di->remain_capacity = %d, line_q = %d\n ", di->remain_capacity, di->line_q); */ + + di->update_k++; + if (di->update_k == 1 || di->update_k != 10) { + temp_soc = ((di->remain_capacity - di->line_q)*1000 + di->fcc/2)/di->fcc;/* x */ + di->display_soc = di->line_k*temp_soc; + /* if (((di->line_k*temp_soc+500)/1000) != di->real_soc), */ + DBG("ZERO : display-soc = %d, real-soc = %d\n", di->display_soc, di->real_soc); + if ((di->display_soc+500)/1000 < di->real_soc) + di->real_soc--; + /* di->real_soc = (line_k*temp_soc+500)/1000 ;//y = k0*x */ + } + } else { + /* DBG("three..\n"); */ + di->update_k++; + if (di->update_k > 10) { + di->update_k = 0; + di->real_soc--; + } + } + DBG("ZERO : update_k = %d\n", di->update_k); + DBG("ZERO : remain_capacity = %d , nac = %d, update_q = %d\n", di->remain_capacity, di->line_q, di->update_q); + DBG("ZERO : Warnning_voltage = %d, line_k = %d, temp_soc = %d real_soc = %d\n\n", di->warnning_voltage, di->line_k, temp_soc, di->real_soc); + } } -static void rk818_battery_charger_init(struct battery_info *di) + +static void voltage_to_soc_discharge_smooth(struct battery_info *di) { - u8 chrg_ctrl_reg1,usb_ctrl_reg;// chrg_ctrl_reg2; - u8 sup_sts_reg; - + int voltage; + int now_current, soc_time = -1; + int volt_to_soc; - DBG("%s start\n",__FUNCTION__); + voltage = di->voltage; + now_current = di->current_avg; + soc_time = di->fcc*3600/100/(abs_int(now_current)); + _voltage_to_capacity(di, 3800); + volt_to_soc = di->temp_soc; + di->temp_soc = _get_soc(di); - battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); - battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); -// battery_read(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); - battery_read(di->rk818, SUP_STS_REG, &sup_sts_reg, 1); + DBG("<%s>. 3.8v ocv_to_soc = %d\n", __func__, volt_to_soc); + DBG("<%s>. di->temp_soc = %d, di->real_soc = %d\n", __func__, di->temp_soc, di->real_soc); + if ((di->voltage < 3800) || (di->voltage > 3800 && di->real_soc < volt_to_soc)) { /* di->warnning_voltage) */ + zero_get_soc(di); + return; - DBG("old usb_ctrl_reg =0x%2x,CHRG_CTRL_REG1=0x%2x\n ",usb_ctrl_reg, chrg_ctrl_reg1); - //usb_ctrl_reg &= (0x01<<7); - usb_ctrl_reg |= (VLIM_4400MV | ILIM_1200MA)|(0x01<<7); - - chrg_ctrl_reg1 &= (0x00); - chrg_ctrl_reg1 |=(0x01<<7)| (CHRG_VOL4200| CHRG_CUR1400mA); - - sup_sts_reg &= ~(0x01<<3); - sup_sts_reg |= (0x01<<2); - - battery_write(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); - - battery_write(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); - //battery_write(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); - battery_write(di->rk818, SUP_STS_REG, &sup_sts_reg, 1); + } else if (di->temp_soc == di->real_soc) { + DBG("<%s>. di->temp_soc == di->real_soc\n", __func__); + } else if (di->temp_soc > di->real_soc) { + DBG("<%s>. di->temp_soc > di->real_soc\n", __func__); + di->vol_smooth_time++; + if (di->vol_smooth_time > soc_time*3) { + di->real_soc--; + di->vol_smooth_time = 0; + } - - battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); -// battery_read(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); - battery_read(di->rk818, SUP_STS_REG, &sup_sts_reg, 1); - battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); - DBG(" new usb_ctrl_reg =0x%2x,CHRG_CTRL_REG1=0x%2x, SUP_STS_REG=0x%2x\n ", - usb_ctrl_reg, chrg_ctrl_reg1,sup_sts_reg); + } else { + DBG("<%s>. di->temp_soc < di->real_soc\n", __func__); + if (di->real_soc == (di->temp_soc + 1)) { + di->change_timer = di->soc_timer; + di->real_soc = di->temp_soc; + } else { + di->vol_smooth_time++; + if (di->vol_smooth_time > soc_time/3) { + di->real_soc--; + di->vol_smooth_time = 0; + } + } + } + + DBG("<%s>, di->temp_soc = %d, di->real_soc = %d\n", __func__, di->temp_soc, di->real_soc); + DBG("<%s>, di->vol_smooth_time = %d, soc_time = %d\n", __func__, di->vol_smooth_time, soc_time); +} + +static int get_charging_time(struct battery_info *di) +{ + return (di->charging_time/60); +} + +static int get_discharging_time(struct battery_info *di) +{ + return (di->discharging_time/60); +} +static void dump_debug_info(struct battery_info *di) +{ + u8 sup_tst_reg, ggcon_reg, ggsts_reg, vb_mod_reg; + u8 usb_ctrl_reg, chrg_ctrl_reg1; + u8 chrg_ctrl_reg2, chrg_ctrl_reg3, rtc_val; + + battery_read(di->rk818, GGCON, &ggcon_reg, 1); + battery_read(di->rk818, GGSTS, &ggsts_reg, 1); + battery_read(di->rk818, SUP_STS_REG, &sup_tst_reg, 1); + battery_read(di->rk818, VB_MOD_REG, &vb_mod_reg, 1); + battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + battery_read(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + battery_read(di->rk818, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + battery_read(di->rk818, 0x00, &rtc_val, 1); + + DBG("\n------------- dump_debug_regs -----------------\n" + "GGCON = 0x%2x, GGSTS = 0x%2x, RTC = 0x%2x\n" + "SUP_STS_REG = 0x%2x, VB_MOD_REG = 0x%2x\n" + "USB_CTRL_REG = 0x%2x, CHRG_CTRL_REG1 = 0x%2x\n" + "CHRG_CTRL_REG2 = 0x%2x, CHRG_CTRL_REG3 = 0x%2x\n\n", + ggcon_reg, ggsts_reg, rtc_val, + sup_tst_reg, vb_mod_reg, + usb_ctrl_reg, chrg_ctrl_reg1, + chrg_ctrl_reg2, chrg_ctrl_reg3 + ); + + DBG( + "########################## [read] ################################\n" + "info: 3.4v low warning, digital 100mA finish, 4.2v, 1.6A\n" + "-----------------------------------------------------------------\n" + "realx-voltage = %d, voltage = %d, current-avg = %d\n" + "fcc = %d, remain_capacity = %d, ocv_volt = %d\n" + "diplay_soc = %d, cpapacity_soc = %d\n" + "AC-ONLINE = %d, USB-ONLINE = %d, charging_status = %d\n" + "finish_real_soc = %d, finish_temp_soc = %d\n" + "chrg_time = %d, dischrg_time = %d\n", + get_relax_voltage(di), + di->voltage, di->current_avg, + di->fcc, di->remain_capacity, _get_OCV_voltage(di), + di->real_soc, _get_soc(di), + di->ac_online, di->usb_online, di->status, + di->debug_finish_real_soc, di->debug_finish_temp_soc, + get_charging_time(di), get_discharging_time(di) + ); + get_charge_status(di); + DBG("################################################################\n"); + +} + +static void update_fcc_capacity(struct battery_info *di) +{ + if ((di->charge_status == CHARGE_FINISH) && (di->dod0_status == 1)) { + if (get_level(di) >= di->dod0_level) { + di->fcc = (di->remain_capacity - di->dod0_capacity)*100/(100-di->dod0); + if (di->fcc > di->qmax) + di->fcc = di->qmax; + + _capacity_init(di, di->fcc); + _save_FCC_capacity(di, di->fcc); + } + di->dod0_status = 0; + } +} + +static void debug_get_finish_soc(struct battery_info *di) +{ + if (di->charge_status == CHARGE_FINISH) { + di->debug_finish_real_soc = di->real_soc; + di->debug_finish_temp_soc = di->temp_soc; + } +} - DBG("%s end\n",__FUNCTION__); +static void wait_charge_finish_signal(struct battery_info *di) +{ + if (di->charge_status == CHARGE_FINISH) + update_fcc_capacity(di);/* save new fcc*/ + /* debug msg*/ + debug_get_finish_soc(di); } -extern int rk818_set_bits(struct rk818 *rk818, u8 reg, u8 mask, u8 val); +static void charge_finish_routine(struct battery_info *di) +{ + if (di->charge_status == CHARGE_FINISH) { + _capacity_init(di, di->fcc); + zero_current_calibration(di); + + if (di->real_soc < 100) { + DBG("<%s>,CHARGE_FINISH di->real_soc < 100, real_soc=%d\n", __func__, di->real_soc); + if ((di->soc_counter < 80)) { + di->soc_counter++; + } else { + di->soc_counter = 0; + di->real_soc++; + } + } + } +} -void charge_disable_open_otg(struct battery_info *di, int value ) +static void voltage_to_soc_charge_smooth(struct battery_info *di) { -// u8 chrg_ctrl_reg1,dcdc_en_reg; - if(value == 1){ - DBG("1 ---- charge disable \n"); - rk818_set_bits(di->rk818, CHRG_CTRL_REG1, 1 << 7, 0<< 7); //ldo9 - rk818_set_bits(di->rk818, 0x23, 1 << 7, 1 << 7); //ldo9 + int now_current, soc_time; + + now_current = _get_average_current(di); + soc_time = di->fcc*3600/100/(abs_int(now_current)); /* 1% time; */ + di->temp_soc = _get_soc(di); + + DBG("<%s>. di->temp_soc = %d, di->real_soc = %d\n", __func__, di->temp_soc, di->real_soc); + /* + if ((di->temp_soc >= 85)&&(di->real_soc >= 85)){ + di->charge_smooth_time++; + + if (di->charge_smooth_time > soc_time/3) { + di->real_soc++; + di->charge_smooth_time = 0; + } + di->charge_smooth_status = true; + }*/ + + if (di->real_soc == di->temp_soc) { + DBG("<%s>. di->temp_soc == di->real_soc\n", __func__); + di->temp_soc = _get_soc(di); } - if(value == 0){ - DBG("1 ---- charge disable \n"); - rk818_set_bits(di->rk818, 0x23, 1 << 7, 0 << 7); //ldo9 - rk818_set_bits(di->rk818, CHRG_CTRL_REG1, 1 << 7, 1 << 7); //ldo9 + if ((di->temp_soc != di->real_soc) && (now_current != 0)) { + + if (di->temp_soc < di->real_soc + 1) { + DBG("<%s>. di->temp_soc < di->real_soc\n", __func__); + di->charge_smooth_time++; + if (di->charge_smooth_time > soc_time*2) { + di->real_soc++; + di->charge_smooth_time = 0; + } + di->charge_smooth_status = true; + } + + else if (di->temp_soc > di->real_soc + 1) { + DBG("<%s>. di->temp_soc > di->real_soc\n", __func__); + di->charge_smooth_time++; + if (di->charge_smooth_time > soc_time/3) { + di->real_soc++; + di->charge_smooth_time = 0; + } + di->charge_smooth_status = true; + + } else if (di->temp_soc == di->real_soc + 1) { + DBG("<%s>. di->temp_soc == di->real_soc + 1\n", __func__); + if (di->charge_smooth_status) { + di->charge_smooth_time++; + if (di->charge_smooth_time > soc_time/3) { + di->real_soc = di->temp_soc; + di->charge_smooth_time = 0; + di->charge_smooth_status = false; + } + + } else { + di->real_soc = di->temp_soc; + di->charge_smooth_status = false; + + } + } } + DBG("<%s>, di->temp_soc = %d, di->real_soc = %d\n", __func__, di->temp_soc, di->real_soc); + DBG("<%s>, di->vol_smooth_time = %d, soc_time = %d\n", __func__, di->charge_smooth_time, soc_time); } -static void fg_init(struct battery_info *di) +static void rk_battery_display_smooth(struct battery_info *di) { - DBG("%s start\n",__FUNCTION__); - _gauge_enable(di); - _get_voltage_offset_value(di); //get the volatege offset -// _autosleep_enable(di); - rk818_battery_charger_init(di); -// _set_relax_thres(di); - di->current_offset = _get_ioffset(di); //get the current offset , the value write to the CAL_OFFSET - _set_cal_offset(di,di->current_offset+42); - - di->voltage = rk818_battery_voltage(di); - di->voltage_ocv = _get_OCV_voltage(di); - _rsoc_init( di); - _capacity_init(di, di->nac); -// _get_realtime_capacity( di); - di->remain_capacity = _get_capacity(di); - // _get_realtime_capacity( di); - do_gettimeofday(&di->soc_timer); - di->change_timer = di->soc_timer; + int status; + u8 charge_status; + + status = di->status; + charge_status = di->charge_status; + if ((status == POWER_SUPPLY_STATUS_CHARGING) || (status == POWER_SUPPLY_STATUS_FULL)) { + + if ((di->current_avg < -10) && (charge_status != CHARGE_FINISH)) + voltage_to_soc_discharge_smooth(di); + else + voltage_to_soc_charge_smooth(di); + + } else if (status == POWER_SUPPLY_STATUS_DISCHARGING) { + voltage_to_soc_discharge_smooth(di); + if (di->real_soc == 1) { + di->time2empty++; + if (di->time2empty >= 300) + di->real_soc = 0; + } else { + di->time2empty = 0; + } + } + +} + #if 0 - for (i = 0; i < AV_SIZE; i++) { - av_v[i] = di->voltage; - av_c[i] = 0; +static void software_recharge(struct battery_info *di, int max_cnt) +{ + static int recharge_cnt; + u8 chrg_ctrl_reg1; + + if ((CHARGE_FINISH == get_charge_status(di)) && (rk_battery_voltage(di) < 4100) && (recharge_cnt < max_cnt)) { + battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + chrg_ctrl_reg1 &= ~(1 << 7); + battery_write(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + DBG("recharge, clear bit7, CHRG_CTRL_REG1 = 0x%x\n", chrg_ctrl_reg1); + msleep(400); + chrg_ctrl_reg1 |= (1 << 7); + battery_write(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + battery_read(di->rk818, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + DBG("recharge, set bit7, CHRG_CTRL_REG1 = 0x%x\n", chrg_ctrl_reg1); + + recharge_cnt++; } - av_v_index = 0; - av_c_index = 0; +} #endif - dump_gauge_register(di); - dump_charger_register(di); - DBG("nac =%d , remain_capacity = %d \n" - " OCV_voltage =%d, voltage =%d \n", - di->nac, di->remain_capacity, - di->voltage_ocv, di->voltage); + +#if 0 +static int estimate_battery_resister(struct battery_info *di) +{ + int i; + int avr_voltage1 = 0, avr_current1; + int avr_voltage2 = 0, avr_current2; + u8 usb_ctrl_reg; + int bat_res, ocv_votage; + static unsigned long last_time; + unsigned long delta_time; + int charge_ocv_voltage1, charge_ocv_voltage2; + int charge_ocv_soc1, charge_ocv_soc2; + + delta_time = get_seconds() - last_time; + DBG("<%s>--- delta_time = %lu\n", __func__, delta_time); + if (delta_time >= 20) {/*20s*/ + + /*first sample*/ + set_charge_current(di, ILIM_45MA);/*450mA*/ + msleep(1000); + for (i = 0; i < 10 ; i++) { + msleep(100); + avr_voltage1 += rk_battery_voltage(di); + } + avr_voltage1 /= 10; + avr_current1 = _get_average_current(di); + battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + DBG("------------------------------------------------------------------------------------------\n"); + DBG("avr_voltage1 = %d, avr_current1 = %d, USB_CTRL_REG = 0x%x\n", avr_voltage1, avr_current1, usb_ctrl_reg); + + /*second sample*/ + set_charge_current(di, ILIM_3000MA); + msleep(1000); + for (i = 0; i < 10 ; i++) { + msleep(100); + avr_voltage2 += rk_battery_voltage(di); + } + avr_voltage2 /= 10; + avr_current2 = _get_average_current(di); + battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); + DBG("avr_voltage2 = %d, avr_current2 = %d, USB_CTRL_REG = 0x%x\n", avr_voltage2, avr_current2, usb_ctrl_reg); + + /*calc resister and ocv_votage ocv*/ + bat_res = (avr_voltage1 - avr_voltage2)*1000/(avr_current1 - avr_current2); + ocv_votage = avr_voltage1 - (bat_res * avr_current1) / 1000; + DBG("bat_res = %d, OCV = %d\n", bat_res, ocv_votage); + + /*calc sample voltage ocv*/ + charge_ocv_voltage1 = avr_voltage1 - avr_current1*200/1000; + charge_ocv_voltage2 = avr_voltage2 - avr_current2*200/1000; + _voltage_to_capacity(di, charge_ocv_voltage1); + charge_ocv_soc1 = di->temp_soc; + _voltage_to_capacity(di, charge_ocv_voltage2); + charge_ocv_soc2 = di->temp_soc; + + DBG("charge_ocv_voltage1 = %d, charge_ocv_soc1 = %d\n", charge_ocv_voltage1, charge_ocv_soc1); + DBG("charge_ocv_voltage2 = %d, charge_ocv_soc2 = %d\n", charge_ocv_voltage2, charge_ocv_soc2); + DBG("------------------------------------------------------------------------------------------\n"); + last_time = get_seconds(); + + return bat_res; + } + + return 0; } +#endif #if 0 -static int capacity_changed(struct battery_info *di) +static int update_battery_resister(struct battery_info *di) { - s32 acc_value, samples = 0; - int ret; - int acc_q; - -// fg_set_voltage(&di->cell, di->voltage_mV); - //fg_set_current(&di->cell, (int16_t)(di->current_uA/1000)); + int tmp_res; + if ((get_charging_time(di) > 5) && (!di->bat_res_updated)) {/*charge at least 8min*/ - return 0; + if ((di->temp_soc >= 80) && (di->bat_res_update_cnt < 10)) { + tmp_res = estimate_battery_resister(di); + if (tmp_res != 0) + di->bat_res_update_cnt++; + di->bat_res += tmp_res; + DBG("<%s>. tmp_bat_res = %d, bat_res_update_cnt = %d\n", __func__, tmp_res, di->bat_res_update_cnt); + if (di->bat_res_update_cnt == 10) { + di->bat_res_updated = true; + di->bat_res /= 10; + } + DBG("<%s>. bat_res = %d, bat_res_update_cnt = %d\n", __func__, di->bat_res, di->bat_res_update_cnt); + } + } + + return tmp_res; } +#endif + +#if 0 +static void charge_soc_check_routine(struct battery_info *di) +{ + int min; + int ocv_voltage; + int old_temp_soc; + int ocv_temp_soc; + int remain_capcity; + + if (di->status == POWER_SUPPLY_STATUS_CHARGING) { + min = get_charging_time(di); + update_battery_resister(di); + if (0) + if ((min >= 30) && (di->bat_res_updated)) { + + old_temp_soc = di->temp_soc; + ocv_voltage = di->voltage + di->bat_res*abs(di->current_avg); + _voltage_to_capacity(di, ocv_voltage); + ocv_temp_soc = di->temp_soc; + + DBG("<%s>. charge_soc_updated_point0 = %d, charge_soc_updated_point1 = %d\n", __func__, di->charge_soc_updated_point0, di->charge_soc_updated_point1); + DBG("<%s>. ocv_voltage = %d, ocv_soc = %d\n", __func__, ocv_voltage, ocv_temp_soc); + DBG("<%s>. voltage = %d, temp_soc = %d\n", __func__, di->voltage, old_temp_soc); + + if (abs32_int(ocv_temp_soc - old_temp_soc) > 10) + di->temp_soc = ocv_temp_soc; + else + di->temp_soc = old_temp_soc*50/100 + ocv_temp_soc*50/100; + + remain_capcity = di->temp_soc * di->fcc / 100; + _capacity_init(di, remain_capcity); + di->remain_capacity = _get_realtime_capacity(di); + DBG("<%s>. old_temp_soc = %d, updated_temp_soc = %d\n", __func__, old_temp_soc, di->temp_soc); + } + } -static void rk818_battery_info(struct battery_info *di) -{ - //di->status = rk818_battery_status(di); - //di->voltage = rk818_battery_voltage(di); - di->present = rk818_battery_present(di); - di->bat_current = _get_average_current(di); - di->temp_soc= rk818_battery_soc(di); - di->tempreture =rk818_battery_temperature(di); - di->health = rk818_battery_health(di); } #endif -static void rk818_battery_display_smooth(struct battery_info *di) +#if 1 +static void update_resume_status_relax_voltage(struct battery_info *di) { - int status; + unsigned long sleep_soc; + unsigned long sum_sleep_soc; + unsigned long sleep_sec; + int relax_voltage; u8 charge_status; -// int relaxmode_soc; -// int coulomp_soc, soc; - - status = di->status; - if(status == POWER_SUPPLY_STATUS_CHARGING){ - //DBG("charging smooth ... \n"); - if(1){ - //DBG(" BATTERY NOT RELAX MODE \n"); - DBG("di->remain_capacity =%d, di->fcc = %d\n", di->remain_capacity,di->fcc); - di->temp_soc = _get_soc(di); - charge_status = get_charge_status( di); - if(di->temp_soc >= 100){ - di->temp_soc = 100; - //di->status = POWER_SUPPLY_STATUS_FULL; - } - - do_gettimeofday(&di->soc_timer); - - if(di->temp_soc!= di->real_soc){ - di->change_timer = di->soc_timer; - if(di->real_soc < di->temp_soc) - di->real_soc++; - else - di->real_soc =di->temp_soc; - } - - // DBG("charge_status =0x%x\n", charge_status); - if((charge_status ==CHARGE_FINISH) && (di->real_soc < 100)){ - DBG("CHARGE_FINISH di->real_soc < 100 \n "); - if((di->soc_counter < 10)){ - di->soc_counter ++; - }else{ - di->soc_counter = 0; - if(di->real_soc < 100){ - di->real_soc ++; - // _save_rsoc_nac( di); - } + int delta_capacity; + int delta_soc; + int sum_sleep_avr_current; + int sleep_min; + + if (di->resume) { + update_battery_info(di); + di->resume = false; + di->sys_wakeup = true; + + DBG("<%s>, resume----------checkstart\n", __func__); + sleep_sec = get_seconds() - di->suspend_time_start; + sleep_min = sleep_sec / 60; + + DBG("<%s>, resume, sleep_sec(s) = %lu, sleep_min = %d\n", + __func__, sleep_sec, sleep_min); + + if (di->sleep_status == POWER_SUPPLY_STATUS_DISCHARGING) { + DBG("<%s>, resume, POWER_SUPPLY_STATUS_DISCHARGING\n", __func__); + + delta_capacity = di->suspend_capacity - di->remain_capacity; + delta_soc = di->suspend_temp_soc - _get_soc(di); + di->dischrg_sum_sleep_capacity += delta_capacity; + di->dischrg_sum_sleep_sec += sleep_sec; + + sum_sleep_soc = di->dischrg_sum_sleep_capacity * 100 / di->fcc; + sum_sleep_avr_current = di->dischrg_sum_sleep_capacity * 3600 / di->dischrg_sum_sleep_sec; + + DBG("<%s>, resume, suspend_capacity=%d, resume_capacity=%d, real_soc = %d\n", + __func__, di->suspend_capacity, di->remain_capacity, di->real_soc); + DBG("<%s>, resume, delta_soc=%d, delta_capacity=%d, sum_sleep_avr_current=%d mA\n", + __func__, delta_soc, delta_capacity, sum_sleep_avr_current); + DBG("<%s>, resume, sum_sleep_soc=%lu, dischrg_sum_sleep_capacity=%lu, dischrg_sum_sleep_sec=%lu\n", + __func__, sum_sleep_soc, di->dischrg_sum_sleep_capacity, di->dischrg_sum_sleep_sec); + DBG("<%s>, relax_voltage=%d, voltage = %d\n", __func__, di->relax_voltage, di->voltage); + + /*large suspend current*/ + if (sum_sleep_avr_current > 20) { + sum_sleep_soc = di->dischrg_sum_sleep_capacity * 100 / di->fcc; + di->real_soc -= sum_sleep_soc; + DBG("<%s>. resume, sleep_avr_current is Over 20mA, sleep_soc = %lu, updated real_soc = %d\n", + __func__, sum_sleep_soc, di->real_soc); + + /* small suspend current*/ + } else if ((sum_sleep_avr_current >= 0) && (sum_sleep_avr_current <= 20)) { + + relax_voltage = get_relax_voltage(di); + di->voltage = rk_battery_voltage(di); + + if ((sleep_min >= 30) && (relax_voltage > di->voltage)) { /* sleep_min >= 30, update by relax voltage*/ + DBG("<%s>, resume, sleep_min > 30 min\n", __func__); + relax_volt_update_remain_capacity(di, relax_voltage, sleep_sec); + + } else { + DBG("<%s>, resume, sleep_min < 30 min\n", __func__); + if (sum_sleep_soc > 0) + di->real_soc -= sum_sleep_soc; } } - } - if(di->real_soc <= 0) - di->real_soc = 0; - if(di->real_soc >= 100){ - di->real_soc = 100; - di->status = POWER_SUPPLY_STATUS_FULL; - } - - } - if(status == POWER_SUPPLY_STATUS_DISCHARGING){ - //DBG("discharging smooth ... \n"); - di->temp_soc = _get_soc(di); - do_gettimeofday(&di->soc_timer); - if(di->temp_soc!= di->real_soc){ - di->change_timer = di->soc_timer; - di->real_soc = di->temp_soc; - // _save_rsoc_nac( di); - } - if(di->real_soc <= 0) - di->real_soc = 0; - if(di->real_soc >= 100){ - di->real_soc = 100; - } -#if 0 - if(!_is_relax_mode( di)){ - DBG(" BATTERY NOT RELAX MODE \n"); - di->temp_soc = _get_soc(di); - do_gettimeofday(&di->soc_timer); - if(di->temp_soc!= di->real_soc){ - di->change_timer = di->soc_timer; - di->real_soc = di->temp_soc; - _save_rsoc_nac( di); + if ((sum_sleep_soc > 0) || (sleep_min >= 30)) { /*Íê³ÉÁËÒ»´ÎrelaxУ׼*/ + di->dischrg_sum_sleep_capacity = 0; + di->dischrg_sum_sleep_sec = 0; } + DBG("<%s>--------- resume DISCHARGE end\n", __func__); + DBG("<%s>. dischrg_sum_sleep_capacity = %lu, dischrg_sum_sleep_sec = %lu\n", __func__, di->dischrg_sum_sleep_capacity, di->dischrg_sum_sleep_sec); + } - }else{ - DBG("BATTERY RELAX MODE\n "); - //relaxmode_soc = relax_soc(di); - coulomp_soc = _get_soc(di); - soc =coulomp_soc;// (coulomp_soc*20 + relaxmode_soc*80)/100; - - if((soc > di->real_soc)&&(di->soc_counter < 10)){ - di->soc_counter ++; + else if (di->sleep_status == POWER_SUPPLY_STATUS_CHARGING) { + DBG("<%s>, resume, POWER_SUPPLY_STATUS_CHARGING\n", __func__); + if ((di->suspend_charge_current >= 0) || (get_charge_status(di) == CHARGE_FINISH)) { + di->temp_soc = _get_soc(di); + charge_status = get_charge_status(di); + + DBG("<%s>, resume, ac-online = %d, usb-online = %d, sleep_current=%d\n", __func__, di->ac_online, di->usb_online, di->suspend_charge_current); + if (((di->suspend_charge_current < 800) && (di->ac_online == 1)) || (charge_status == CHARGE_FINISH)) { + DBG("resume, sleep : ac online charge current < 1000\n"); + if (sleep_sec > 0) { + di->count_sleep_time += sleep_sec; + sleep_soc = 1000*di->count_sleep_time*100/3600/di->fcc; + DBG("<%s>, resume, sleep_soc=%lu, real_soc=%d\n", __func__, sleep_soc, di->real_soc); + if (sleep_soc > 0) + di->count_sleep_time = 0; + di->real_soc += sleep_soc; + if (di->real_soc > 100) + di->real_soc = 100; + } + } else { - }else{ - di->soc_counter = 0; - if(di->real_soc < 100){ - di->real_soc --; - _save_rsoc_nac( di); + DBG("<%s>, usb charging\n", __func__); + if (di->suspend_temp_soc + 15 < di->temp_soc) + di->real_soc += (di->temp_soc - di->suspend_temp_soc)*3/2; + else + di->real_soc += (di->temp_soc - di->suspend_temp_soc); } + + DBG("POWER_SUPPLY_STATUS_CHARGING: di->temp_soc = %d, di->real_soc = %d, sleep_time = %ld\n ", di->temp_soc , di->real_soc, sleep_sec); } - DBG(" remaxmode_soc = %d , coulomp-soc =%d soc = %d\n",relaxmode_soc, coulomp_soc, soc); } -#endif - } - //DBG("%s exit \n", __FUNCTION__); } +#endif -static void rk818_battery_update_status(struct battery_info *di) +#ifdef SUPPORT_USB_CHARGE +static int get_charging_status_type(struct battery_info *di) { + int otg_status = dwc_otg_check_dpdm(); + + if (0 == otg_status) { + di->usb_online = 0; + di->ac_online = 0; + di->check_count = 0; + + } else if (1 == otg_status) { + if (0 == get_gadget_connect_flag()) { + if (++di->check_count >= 5) { + di->ac_online = 1; + di->usb_online = 0; + } else { + di->ac_online = 0; + di->usb_online = 1; + } + } else { + di->ac_online = 0; + di->usb_online = 1; + } - di->voltage = rk818_battery_voltage( di); - di->current_avg = _get_average_current(di); - di->remain_capacity = _get_realtime_capacity( di); - _get_capacity(di); + } else if (2 == otg_status) { + di->ac_online = 1; + di->usb_online = 0; + di->check_count = 0; + } - rk818_battery_display_smooth(di); - - DBG("%s\n" - "voltage = %d, current-avg = %d\n" - "fcc = %d ,remain_capacity =%d\n" - "real_soc = %d\n", - __FUNCTION__, - di->voltage, di->current_avg, - di->fcc, di->remain_capacity, - di->real_soc - ); + if (di->ac_online == 1) + set_charge_current(di, ILIM_3000MA); + else + set_charge_current(di, ILIM_45MA); + return otg_status; } -extern int dwc_vbus_status(void); -extern int get_gadget_connect_flag(void); - //state of charge ----running -static int get_charging_status(struct battery_info *di) +#endif + +static void battery_poweron_status_init(struct battery_info *di) { + int otg_status; -//////////////////////////////////////////// -#if 0 - u8 usb_ctrl_reg;// chrg_ctrl_reg2; - +#ifndef SUPPORT_USB_CHARGE + u8 buf; +#endif + +#ifdef SUPPORT_USB_CHARGE + otg_status = dwc_otg_check_dpdm(); + if (otg_status == 1) { + di->usb_online = 1; + di->ac_online = 0; + set_charge_current(di, ILIM_45MA); + di->status = POWER_SUPPLY_STATUS_CHARGING; + DBG("++++++++ILIM_45MA++++++\n"); + + } else if (otg_status == 2) { + di->usb_online = 0; + di->ac_online = 1; + di->status = POWER_SUPPLY_STATUS_CHARGING; + set_charge_current(di, ILIM_3000MA); + DBG("++++++++ILIM_1000MA++++++\n"); + } + DBG(" CHARGE: SUPPORT_USB_CHARGE. charge_status = %d\n", otg_status); - battery_read(di->rk818, USB_CTRL_REG, &usb_ctrl_reg, 1); -// battery_read(di->rk818, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); +#else - DBG("old usb_ctrl_reg =0x%2x,CHRG_CTRL_REG1=0x%2x\n ",usb_ctrl_reg, chrg_ctrl_reg1); - usb_ctrl_reg &= (0x01<<7); - usb_ctrl_reg |= ( ILIM_300MA); + battery_read(di->rk818, VB_MOD_REG, &buf, 1); + if (buf&PLUG_IN_STS) { + di->ac_online = 1; + di->usb_online = 0; + di->status = POWER_SUPPLY_STATUS_CHARGING; + if (di->real_soc == 100) + di->status = POWER_SUPPLY_STATUS_FULL; + } else { + di->status = POWER_SUPPLY_STATUS_DISCHARGING; + di->ac_online = 0; + di->usb_online = 0; + } + DBG(" CHARGE: NOT SUPPORT_USB_CHARGE\n"); #endif -///////////////////////////////////////// +} -// struct rk30_adc_battery_platform_data *pdata = bat->pdata; - int usb_status = 0; // 0--dischage ,1 ---usb charge, 2 ---ac charge - int vbus_status = dwc_vbus_status(); - if (1 == vbus_status) { - if (0 == get_gadget_connect_flag()){ - if (++di->check_count >= 5){ - di->ac_online = 1; - di->usb_online = 0; - }else{ - di->ac_online =0; - di->usb_online = 1; +static void check_battery_status(struct battery_info *di) +{ + u8 buf; + int ret; - } - }else{ + ret = battery_read(di->rk818, VB_MOD_REG, &buf, 1); +#ifdef SUPPORT_USB_CHARGE - di->ac_online =0; - di->usb_online = 1; + if (strstr(saved_command_line, "charger")) { + if ((buf&PLUG_IN_STS) == 0) { + di->status = POWER_SUPPLY_STATUS_DISCHARGING; + di->ac_online = 0; + di->usb_online = 0; } - - }else{ - if (2 == vbus_status) { - di->ac_online = 1; - di->usb_online = 0; - }else{ + } else { + if (buf&PLUG_IN_STS) { + get_charging_status_type(di); - di->ac_online = 0; - di->usb_online = 0; + di->status = POWER_SUPPLY_STATUS_CHARGING; + if (di->real_soc == 100) + di->status = POWER_SUPPLY_STATUS_FULL; + } else { + di->status = POWER_SUPPLY_STATUS_DISCHARGING; + di->ac_online = 0; + di->usb_online = 0; } - di->check_count=0; + } +#else + if (buf & PLUG_IN_STS) { + di->ac_online = 1; + di->usb_online = 0; + di->status = POWER_SUPPLY_STATUS_CHARGING; + if (di->real_soc == 100) + di->status = POWER_SUPPLY_STATUS_FULL; + } else { + di->status = POWER_SUPPLY_STATUS_DISCHARGING; + di->ac_online = 0; + di->usb_online = 0; } - return usb_status; +#endif +} +static void report_power_supply_changed(struct battery_info *di) +{ + static u32 old_soc; + static u32 old_ac_status; + static u32 old_usb_status; + static u32 old_charge_status; + bool state_changed; + + state_changed = false; + if (di->real_soc == 0) + state_changed = true; + else if (di->real_soc == 100) + state_changed = true; + else if (di->real_soc != old_soc) + state_changed = true; + else if (di->ac_online != old_ac_status) + state_changed = true; + else if (di->usb_online != old_usb_status) + state_changed = true; + else if (old_charge_status != di->status) + state_changed = true; + + if (state_changed) { + power_supply_changed(&di->bat); + power_supply_changed(&di->usb); + power_supply_changed(&di->ac); + old_soc = di->real_soc; + old_ac_status = di->ac_online; + old_usb_status = di->usb_online; + old_charge_status = di->status; + } } -static void get_battery_status(struct battery_info *di) +static void update_battery_info(struct battery_info *di) { + di->remain_capacity = _get_realtime_capacity(di); + if (di->remain_capacity > di->fcc) + _capacity_init(di, di->fcc); + else if (di->remain_capacity < 0) + _capacity_init(di, 0); - u8 buf; - int ret; - ret = battery_read(di->rk818,VB_MOD_REG, &buf, 1); - //int vbus_status = dwc_vbus_status(); + if (di->real_soc > 100) + di->real_soc = 100; + else if (di->real_soc < 0) + di->real_soc = 0; - if(buf&PLUG_IN_STS){ - //if(vbus_status != 0){ - get_charging_status(di); - di->status = POWER_SUPPLY_STATUS_CHARGING; - // di->ac_online = 1; - if(di->real_soc == 100) - di->status = POWER_SUPPLY_STATUS_FULL; + if ((di->ac_online) || (di->usb_online)) {/*charging*/ + di->charging_time++; + di->discharging_time = 0; + } else { + di->discharging_time++; + di->charging_time = 0; } - else{ - di->status = POWER_SUPPLY_STATUS_DISCHARGING; - di->ac_online =0; - di->usb_online =0; - } - //DBG("%s ,di->status = %d\n",__FUNCTION__, di->status); + di->work_on = 1; + di->voltage = rk_battery_voltage(di); + di->current_avg = _get_average_current(di); + di->remain_capacity = _get_realtime_capacity(di); + di->voltage_ocv = _get_OCV_voltage(di); + di->charge_status = get_charge_status(di); + di->otg_status = dwc_otg_check_dpdm(); + di->relax_voltage = get_relax_voltage(di); + di->temp_soc = _get_soc(di); + di->remain_capacity = _get_realtime_capacity(di); + check_battery_status(di);/* ac_online, usb_online, status*/ + update_cal_offset(di); } -static void rk818_battery_work(struct work_struct *work) +static void rk_battery_work(struct work_struct *work) { - u8 buf; struct battery_info *di = container_of(work, - struct battery_info, battery_monitor_work.work); - int vbus_status ; - get_battery_status(di); - battery_read(di->rk818,0x00, &buf, 1); - DBG("RTC =0x%2x\n ", buf); - battery_read(di->rk818,VB_MOD_REG, &buf, 1); - //DBG("VB_MOD_REG =%2x, the value is %2x\n ", VB_MOD_REG,buf); - battery_read(di->rk818,SUP_STS_REG, &buf, 1); -// DBG("SUP_STS_REG =%2x, the value is %2x\n ", SUP_STS_REG,buf); - vbus_status = dwc_vbus_status(); -// DBG("vbus_status =%2x\n ", vbus_status); - - rk818_battery_update_status(di); - - if(di ->resume){ - di ->resume = false; - di->real_soc = _get_soc(di); - if(di->real_soc <= 0) - di->real_soc = 0; - if(di->real_soc >= 100) - di->real_soc = 100; - } - if ((di->ac_online == 0 )&&( di->usb_online ==0)&&(di->remain_capacity > di->qmax +10)){ - _capacity_init(di, di->qmax); - di->remain_capacity = _get_realtime_capacity( di); - } - - //DBG("soc = %d", di->real_soc); - _copy_soc(di, di->real_soc); + struct battery_info, battery_monitor_work.work); + + update_resume_status_relax_voltage(di); + wait_charge_finish_signal(di); + charge_finish_routine(di); + + rk_battery_display_smooth(di); + update_battery_info(di); + + report_power_supply_changed(di); + _copy_soc(di, di->real_soc); _save_remain_capacity(di, di->remain_capacity); - power_supply_changed(&di->bat); -// power_supply_changed(&di->usb); - power_supply_changed(&di->ac); - queue_delayed_work(di->wq, &di->battery_monitor_work, msecs_to_jiffies(TIMER_MS_COUNTS*5)); + dump_debug_info(di); + di->queue_work_cnt++; + queue_delayed_work(di->wq, &di->battery_monitor_work, msecs_to_jiffies(TIMER_MS_COUNTS)); } -static void rk818_battery_charge_check_work(struct work_struct *work) +static void rk_battery_charge_check_work(struct work_struct *work) { struct battery_info *di = container_of(work, - struct battery_info, charge_check_work.work); - charge_disable_open_otg(di,di->charge_otg); + struct battery_info, charge_check_work.work); + + DBG("rk_battery_charge_check_work\n"); + charge_disable_open_otg(di->charge_otg); } static BLOCKING_NOTIFIER_HEAD(battery_chain_head); @@ -1720,259 +2550,475 @@ EXPORT_SYMBOL_GPL(unregister_battery_notifier); int battery_notifier_call_chain(unsigned long val) { return (blocking_notifier_call_chain(&battery_chain_head, val, NULL) - == NOTIFY_BAD) ? -EINVAL : 0; + == NOTIFY_BAD) ? -EINVAL : 0; } EXPORT_SYMBOL_GPL(battery_notifier_call_chain); + +static void poweron_lowerpoer_handle(struct battery_info *di) +{ +#ifdef CONFIG_LOGO_LOWERPOWER_WARNING + if ((di->real_soc <= 2) && (di->status == POWER_SUPPLY_STATUS_DISCHARGING)) { + mdelay(1500); + /* kernel_power_off(); */ + } +#endif +} + static int battery_notifier_call(struct notifier_block *nb, - unsigned long event, void *data) + unsigned long event, void *data) { - struct battery_info *di= - container_of(nb, struct battery_info, battery_nb); + struct battery_info *di = + container_of(nb, struct battery_info, battery_nb); switch (event) { - case 0: - DBG(" CHARGE enable \n"); - di ->charge_otg = 0; - queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(50)); - break; + case 0: + DBG(" CHARGE enable\n"); + di->charge_otg = 0; + queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(50)); + break; - case 1: - di ->charge_otg = 1; - queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(50)); + case 1: + di->charge_otg = 1; + queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(50)); + DBG("charge disable OTG enable\n"); + break; - DBG("charge disable OTG enable \n"); - break; - default: - return NOTIFY_OK; - } + case 2: + poweron_lowerpoer_handle(di); + break; + + default: + return NOTIFY_OK; + } return NOTIFY_OK; } + +static irqreturn_t rk818_vbat_lo_irq(int irq, void *di) +{ + pr_info("<%s>lower power warning!\n", __func__); + + _copy_soc(g_battery, 0); + _capacity_init(g_battery, 0); + rk_send_wakeup_key(); + kernel_power_off(); + return IRQ_HANDLED; +} + +static void disable_vbat_low_irq(struct battery_info *di) +{ + /* mask vbat low */ + rk818_set_bits(di->rk818, 0x4d, (0x1 << 1), (0x1 << 1)); + /*clr vbat low interrupt */ + /* rk818_set_bits(di->rk818, 0x4c, (0x1 << 1), (0x1 << 1));*/ +} +static void enable_vbat_low_irq(struct battery_info *di) +{ + /* clr vbat low interrupt */ + rk818_set_bits(di->rk818, 0x4c, (0x1 << 1), (0x1 << 1)); + /* mask vbat low */ + rk818_set_bits(di->rk818, 0x4d, (0x1 << 1), (0x0 << 1)); +} + +static irqreturn_t rk818_vbat_plug_in(int irq, void *di) +{ + pr_info("\n------- %s:irq = %d\n", __func__, irq); + rk_send_wakeup_key(); + return IRQ_HANDLED; +} +static irqreturn_t rk818_vbat_plug_out(int irq, void *di) +{ + pr_info("\n-------- %s:irq = %d\n", __func__, irq); + charge_disable_open_otg(0); + rk_send_wakeup_key(); + return IRQ_HANDLED; +} + +static irqreturn_t rk818_vbat_charge_ok(int irq, void *di) +{ + pr_info("---------- %s:irq = %d\n", __func__, irq); + rk_send_wakeup_key(); + return IRQ_HANDLED; +} + + + +static int rk818_battery_sysfs_init(struct battery_info *di, struct device *dev) +{ + int ret; + int i; + struct kobject *rk818_fg_kobj; + + ret = device_create_file(dev, &dev_attr_rk818batparam); + if (ret) { + ret = -EINVAL; + dev_err(dev, "failed to create bat param file\n"); + goto err_sysfs; + } + + ret = create_sysfs_interfaces(dev); + if (ret < 0) { + ret = -EINVAL; + dev_err(dev, "device RK818 battery sysfs register failed\n"); + goto err_sysfs; + } + + rk818_fg_kobj = kobject_create_and_add("rk818_battery", NULL); + if (!rk818_fg_kobj) + return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(rk818_bat_attr); i++) { + ret = sysfs_create_file(rk818_fg_kobj, &rk818_bat_attr[i].attr); + if (ret != 0) { + dev_err(dev, "create rk818_battery node error\n"); + goto err_sysfs; + } + } + + return ret; + +err_sysfs: + power_supply_unregister(&di->ac); + power_supply_unregister(&di->usb); + power_supply_unregister(&di->bat); + + return ret; +} + +static void rk818_battery_irq_init(struct battery_info *di) +{ + int plug_in_irq, plug_out_irq, chg_ok_irq, vb_lo_irq; + int ret; + struct rk818 *chip = di->rk818; + + vb_lo_irq = irq_create_mapping(chip->irq_domain, RK818_IRQ_VB_LO); + plug_in_irq = irq_create_mapping(chip->irq_domain, RK818_IRQ_PLUG_IN); + plug_out_irq = irq_create_mapping(chip->irq_domain, RK818_IRQ_PLUG_OUT); + chg_ok_irq = irq_create_mapping(chip->irq_domain, RK818_IRQ_CHG_OK); + + ret = request_threaded_irq(vb_lo_irq, NULL, rk818_vbat_lo_irq, + IRQF_TRIGGER_HIGH, "rk818_vbatlow", chip); + if (ret != 0) + dev_err(chip->dev, "vb_lo_irq request failed!\n"); + + di->irq = vb_lo_irq; + enable_irq_wake(di->irq); + disable_vbat_low_irq(di); + + ret = request_threaded_irq(plug_in_irq, NULL, rk818_vbat_plug_in, + IRQF_TRIGGER_RISING, "rk818_vbat_plug_in", chip); + if (ret != 0) + dev_err(chip->dev, "plug_in_irq request failed!\n"); + + + ret = request_threaded_irq(plug_out_irq, NULL, rk818_vbat_plug_out, + IRQF_TRIGGER_FALLING, "rk818_vbat_plug_out", chip); + if (ret != 0) + dev_err(chip->dev, "plug_out_irq request failed!\n"); + + + ret = request_threaded_irq(chg_ok_irq, NULL, rk818_vbat_charge_ok, + IRQF_TRIGGER_RISING, "rk818_vbat_charge_ok", chip); + if (ret != 0) + dev_err(chip->dev, "chg_ok_irq request failed!\n"); +} + +static void battery_info_init(struct battery_info *di, struct rk818 *chip) +{ + u32 fcc_capacity; + + di->rk818 = chip; + g_battery = di; + di->platform_data = chip->battery_data; + di->cell.config = di->platform_data->cell_cfg; + di->design_capacity = di->platform_data->cell_cfg->design_capacity; + di->qmax = di->platform_data->cell_cfg->design_qmax; + di->fcc = di->design_capacity; + di->vol_smooth_time = 0; + di->charge_smooth_time = 0; + di->charge_smooth_status = false; + di->sleep_status = 0; + di->work_on = 0; + di->sys_wakeup = true; + di->pcb_ioffset = 0; + di->pcb_ioffset_updated = false; + di->queue_work_cnt = 0; + di->update_k = 0; + di->update_q = 0; + di->voltage_old = 0; + di->display_soc = 0; + di->bat_res = 0; + di->bat_res_updated = false; + di->resume = false; + di->sys_wakeup = true; + di->status = POWER_SUPPLY_STATUS_DISCHARGING; + + di->debug_finish_real_soc = 0; + di->debug_finish_temp_soc = 0; + + fcc_capacity = _get_FCC_capacity(di); + if (fcc_capacity > 1000) + di->fcc = fcc_capacity; + else + di->fcc = di->design_capacity; +} +/* +static struct of_device_id rk818_battery_of_match[] = { +{ .compatible = "rk818_battery" }, +{ } +}; + +MODULE_DEVICE_TABLE(of, rk818_battery_of_match); +*/ #ifdef CONFIG_OF -static int rk818_battery_parse_dt(struct rk818 *rk818) +static int rk_battery_parse_dt(struct rk818 *rk818, struct device *dev) { - struct device_node *regs,*rk818_pmic_np; - struct battery_platform_data *data; + struct device_node *regs, *rk818_pmic_np; + struct battery_platform_data *data; struct cell_config *cell_cfg; + struct ocv_config *ocv_cfg; struct property *prop; u32 out_value; - int i, length, ret; + int length, ret; rk818_pmic_np = of_node_get(rk818->dev->of_node); if (!rk818_pmic_np) { - printk("could not find pmic sub-node\n"); + dev_err(dev, "could not find pmic sub-node\n"); return -EINVAL; } regs = of_find_node_by_name(rk818_pmic_np, "battery"); - if (!regs){ - printk("could not find battery sub-node\n"); + if (!regs) { + dev_err(dev, "battery node not found!\n"); return -EINVAL; } data = devm_kzalloc(rk818->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(dev, "kzalloc for battery_platform_data failed!\n"); + return -ENOMEM; + } memset(data, 0, sizeof(*data)); cell_cfg = devm_kzalloc(rk818->dev, sizeof(*cell_cfg), GFP_KERNEL); - /* determine the number of brightness levels */ + if (!cell_cfg) { + dev_err(dev, "kzalloc for cell_config failed!\n"); + return -ENOMEM; + } + ocv_cfg = devm_kzalloc(rk818->dev, sizeof(*ocv_cfg), GFP_KERNEL); + if (!ocv_cfg) { + dev_err(dev, "kzalloc for ocv_config failed!\n"); + return -ENOMEM; + } + prop = of_find_property(regs, "ocv_table", &length); - if (!prop) + if (!prop) { + dev_err(dev, "ocv_table not found!\n"); return -EINVAL; - data->ocv_size= length / sizeof(u32); - /* read brightness levels from DT property */ + } + data->ocv_size = length / sizeof(u32); + if (data->ocv_size > 0) { size_t size = sizeof(*data->battery_ocv) * data->ocv_size; - data->battery_ocv= devm_kzalloc(rk818->dev, size, GFP_KERNEL); - if (!data->battery_ocv) + + data->battery_ocv = devm_kzalloc(rk818->dev, size, GFP_KERNEL); + if (!data->battery_ocv) { + dev_err(dev, "kzalloc for ocv_table failed!\n"); return -ENOMEM; + } ret = of_property_read_u32_array(regs, "ocv_table", data->battery_ocv, data->ocv_size); - DBG("the battery OCV TABLE : "); - for(i =0; i< data->ocv_size; i++ ) - DBG("%d ", data->battery_ocv[i]); - DBG("\n"); if (ret < 0) return ret; } + ret = of_property_read_u32(regs, "max_charge_currentmA", &out_value); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "max_charge_currentmA not found!\n"); return ret; - data->max_charger_currentmA= out_value; + } + data->max_charger_currentmA = out_value; + ret = of_property_read_u32(regs, "max_charge_voltagemV", &out_value); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "max_charge_voltagemV not found!\n"); return ret; - data->max_charger_voltagemV= out_value; + } + data->max_charger_voltagemV = out_value; + ret = of_property_read_u32(regs, "design_capacity", &out_value); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "design_capacity not found!\n"); return ret; + } cell_cfg->design_capacity = out_value; + ret = of_property_read_u32(regs, "design_qmax", &out_value); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "design_qmax not found!\n"); + return ret; + } + cell_cfg->design_qmax = out_value; + + ret = of_property_read_u32(regs, "sleep_enter_current", &out_value); + if (ret < 0) { + dev_err(dev, "sleep_enter_current not found!\n"); + return ret; + } + ocv_cfg->sleep_enter_current = out_value; + + ret = of_property_read_u32(regs, "sleep_exit_current", &out_value); + if (ret < 0) { + dev_err(dev, "sleep_exit_current not found!\n"); return ret; - cell_cfg->design_qmax =out_value; - data->cell_cfg =cell_cfg; + } + ocv_cfg->sleep_exit_current = out_value; + + ret = of_property_read_u32(regs, "support_uboot_chrg", &support_uboot_chrg); + + cell_cfg->ocv = ocv_cfg; + data->cell_cfg = cell_cfg; rk818->battery_data = data; + + DBG("\n--------- the battery OCV TABLE dump:\n"); DBG("max_charge_currentmA :%d\n", data->max_charger_currentmA); DBG("max_charge_voltagemV :%d\n", data->max_charger_voltagemV); DBG("design_capacity :%d\n", cell_cfg->design_capacity); DBG("design_qmax :%d\n", cell_cfg->design_qmax); - + DBG("sleep_enter_current :%d\n", cell_cfg->ocv->sleep_enter_current); + DBG("sleep_exit_current :%d\n", cell_cfg->ocv->sleep_exit_current); + DBG("uboot chrg = %d\n", support_uboot_chrg); + DBG("\n--------- rk818_battery dt_parse ok.\n"); return 0; } -static struct of_device_id rk818_battery_of_match[] = { - { .compatible = "rk818_battery" }, - { } -}; -MODULE_DEVICE_TABLE(of, rk818_battery_of_match); #else -static int rk818_battery_parse_dt(struct device *dev) +static int rk_battery_parse_dt(struct rk818 *rk818, struct device *dev) { return -ENODEV; } #endif -static int battery_probe(struct platform_device *pdev) + +static int battery_probe(struct platform_device *pdev) { struct rk818 *chip = dev_get_drvdata(pdev->dev.parent); -// struct battery_platform_data *pdata ;//= rk818_platform_data->battery_data; -// struct battery_platform_data defdata ;//= rk818_platform_data->battery_data; struct battery_info *di; - struct ocv_config *ocv; - struct edv_config *edv; int ret; - - DBG("%s is the battery driver version %s\n",__FUNCTION__,DRIVER_VERSION); - rk818_battery_parse_dt(chip); - di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + DBG("battery driver version %s\n", DRIVER_VERSION); + di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) { - dev_err(&pdev->dev, "no memory for state\n"); - ret = -ENOMEM; - return ret; - } - ocv = devm_kzalloc(&pdev->dev, sizeof(*ocv), GFP_KERNEL); - if (!ocv) { - dev_err(&pdev->dev, "ocv no memory for state\n"); - ret = -ENOMEM; - return ret; + dev_err(&pdev->dev, "kzalloc battery_info memory failed!\n"); + return -ENOMEM; } - edv = devm_kzalloc(&pdev->dev, sizeof(*edv), GFP_KERNEL); - if (!edv) { - dev_err(&pdev->dev, "edv no memory for state\n"); - ret = -ENOMEM; - return ret; + ret = rk_battery_parse_dt(chip, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "rk_battery_parse_dt failed!\n"); + return -EINVAL; } - di->rk818 = chip; -#if 0 - di->platform_data = kmemdup(pdata, sizeof(*pdata), GFP_KERNEL); - if (!di->platform_data) { - kfree(di); - return -ENOMEM; - } -#endif -// data = di; platform_set_drvdata(pdev, di); - /*apply battery cell configuration*/ - //di->cell.config = di->platform_data->cell_cfg; - di->platform_data = chip->battery_data; - di->platform_data->cell_cfg = chip->battery_data->cell_cfg; - di->platform_data->cell_cfg->ocv = ocv; - di->platform_data->cell_cfg->edv = edv; - di->design_capacity = chip->battery_data->cell_cfg->design_capacity; - di->qmax = chip->battery_data->cell_cfg->design_qmax; - di->fcc = di->design_capacity; - di->status = POWER_SUPPLY_STATUS_DISCHARGING; - - battery_powersupply_init(di); + battery_info_init(di, chip); fg_init(di); - ret = power_supply_register(&pdev->dev, &di->bat); - if (ret) { - dev_dbg(&pdev->dev, "failed to register main battery\n"); - goto batt_failed; - } - ret = power_supply_register(&pdev->dev, &di->usb); - if (ret) { - dev_dbg(&pdev->dev, "failed to register usb power supply\n"); - goto usb_failed; - } - ret = power_supply_register(&pdev->dev, &di->ac); + + wake_lock_init(&di->resume_wake_lock, WAKE_LOCK_SUSPEND, "resume_charging"); + + flatzone_voltage_init(di); + battery_poweron_status_init(di); + battery_power_supply_init(di); + ret = battery_power_supply_register(di, &pdev->dev); if (ret) { - dev_dbg(&pdev->dev, "failed to register ac power supply\n"); - goto ac_failed; + dev_err(&pdev->dev, "rk power supply register failed!\n"); + return ret; } - di->wq = create_singlethread_workqueue("battery-work"); - INIT_DELAYED_WORK(&di->battery_monitor_work,rk818_battery_work); + INIT_DELAYED_WORK(&di->battery_monitor_work, rk_battery_work); queue_delayed_work(di->wq, &di->battery_monitor_work, msecs_to_jiffies(TIMER_MS_COUNTS*5)); - //queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(TIMER_MS_COUNTS*5)); - INIT_DELAYED_WORK(&di->charge_check_work,rk818_battery_charge_check_work); - + INIT_DELAYED_WORK(&di->charge_check_work, rk_battery_charge_check_work); + di->battery_nb.notifier_call = battery_notifier_call; register_battery_notifier(&di->battery_nb); - printk("battery probe ok... \n"); - return ret; - -ac_failed: - power_supply_unregister(&di->ac); -usb_failed: - power_supply_unregister(&di->usb); -batt_failed: - power_supply_unregister(&di->bat); + + rk818_battery_irq_init(di); + rk818_battery_sysfs_init(di, &pdev->dev); + DBG("------ RK81x battery_probe ok!-------\n"); return ret; } -static int battery_remove(struct platform_device *dev) -{ - return 0; -} -#if 1 -static int battery_suspend(struct platform_device *dev,pm_message_t state) + +#ifdef CONFIG_PM + +static int battery_suspend(struct platform_device *dev, pm_message_t state) { struct battery_info *di = platform_get_drvdata(dev); - DBG("%s--------------------\n",__FUNCTION__); - if(di == NULL) - printk("battery NULL di\n"); - cancel_delayed_work(&di ->battery_monitor_work); - DBG("%s---------end--------\n",__FUNCTION__); + + enable_vbat_low_irq(di); + di->sleep_status = di->status; + di->suspend_charge_current = _get_average_current(di); + + /* avoid abrupt wakeup which will clean the variable*/ + if (di->sys_wakeup) { + di->suspend_capacity = di->remain_capacity; + di->suspend_temp_soc = _get_soc(di); + di->suspend_time_start = get_seconds(); + di->sys_wakeup = false; + } + + cancel_delayed_work(&di->battery_monitor_work); + DBG("<%s>. suspend_temp_soc,=%d, suspend_charge_current=%d, suspend_cap=%d, sleep_status=%d\n", + __func__, di->suspend_temp_soc, di->suspend_charge_current, + di->suspend_capacity, di->sleep_status); return 0; } static int battery_resume(struct platform_device *dev) { - - u8 buf; - int ret; struct battery_info *di = platform_get_drvdata(dev); - ret = battery_read(di->rk818,VB_MOD_REG, &buf, 1); + di->resume = true; + DBG("<%s>\n", __func__); + disable_vbat_low_irq(di); + queue_delayed_work(di->wq, &di->battery_monitor_work, + msecs_to_jiffies(TIMER_MS_COUNTS/2)); -// struct battery_info *di = platform_get_drvdata(dev); - DBG("%s--------------------\n",__FUNCTION__); - queue_delayed_work(di->wq, &di->battery_monitor_work, msecs_to_jiffies(TIMER_MS_COUNTS)); - di ->resume = true; - DBG("charge--status 0x%02x--------------------buf = 0x%02x\n", get_charge_status( di),buf); + if (di->sleep_status == POWER_SUPPLY_STATUS_CHARGING || di->real_soc <= 5) + wake_lock_timeout(&di->resume_wake_lock, 5*HZ); + + return 0; +} +static int battery_remove(struct platform_device *dev) +{ + struct battery_info *di = platform_get_drvdata(dev); + cancel_delayed_work_sync(&di->battery_monitor_work); return 0; } +static void battery_shutdown(struct platform_device *dev) +{ + struct battery_info *di = platform_get_drvdata(dev); + + cancel_delayed_work_sync(&di->battery_monitor_work); + DBG("rk818 shutdown!"); +} #endif + static struct platform_driver battery_driver = { - .probe = battery_probe, - .remove = battery_remove, - .suspend = battery_suspend, - .resume = battery_resume, - - .driver = { - .name = "rk818-battery", - //.pm = &pm_ops, - .of_match_table = of_match_ptr(rk818_battery_of_match), + .driver = { + .name = "rk818-battery", + .owner = THIS_MODULE, }, + + .probe = battery_probe, + .remove = battery_remove, + .suspend = battery_suspend, + .resume = battery_resume, + .shutdown = battery_shutdown, }; static int __init battery_init(void) { return platform_driver_register(&battery_driver); } + fs_initcall_sync(battery_init); static void __exit battery_exit(void) { @@ -1984,10 +3030,3 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:rk818-battery"); MODULE_AUTHOR("ROCKCHIP"); - - - - - - - diff --git a/include/linux/power/rk818_battery.h b/include/linux/power/rk818_battery.h index f9b00e32e352..5ac820b304ca 100644 --- a/include/linux/power/rk818_battery.h +++ b/include/linux/power/rk818_battery.h @@ -1,275 +1,607 @@ -/* - *rk818-battery.h - Battery fuel gauge driver structures - * - */ - -#include -/* Gas Gauge Constatnts */ -#define TEMP_0C 2732 -#define MAX_CAPACITY 0x7fff -#define MAX_SOC 100 -#define MAX_PERCENTAGE 100 - -/* Num, cycles with no Learning, after this many cycles, the gauge - start adjusting FCC, based on Estimated Cell Degradation */ -#define NO_LEARNING_CYCLES 25 -/* Size of the OCV Lookup table */ -#define OCV_TABLE_SIZE 21 -/* - * OCV Config - */ -struct ocv_config { -/*voltage_diff, current_diff: Maximal allowed deviation of the voltage and the current from -one reading to the next that allows the fuel gauge to apply an OCV correction. The main purpose - of these thresholds is to filter current and voltage spikes. Recommended value: these value are highly - depend on the load nature. if the load creates a lot of current spikes .the value may need to be increase*/ - uint8_t voltage_diff; - uint8_t current_diff; -/* sleep_enter_current: if the current remains under this threshold for [sleep_enter_samples] - consecutive samples. the gauge enters the SLEEP MODE*/ - uint8_t sleep_enter_current; -/*sleep_enter_samples: the number of samples that satis fy asleep enter or exit condition in order -to actually enter of exit SLEEP mode*/ - uint8_t sleep_enter_samples; -/*sleep_exit_samples: to exit SLEEP mode , average current should pass this threshold first. then -current should remain above this threshold for [sleep_exit_samples] consecutive samples*/ - uint8_t sleep_exit_current; -/*sleep_exit_samples: to exit SLEEP mode, average current should pass this threshold first, then current -should remain above this threshold for [sleep_exit_samples] consecutive samples.*/ - uint8_t sleep_exit_samples; -/*relax_period: defines the number of seconds the fuel gauge should spend in the SLEEP mode -before entering the OCV mode, this setting makes the gauge wait for a cell voltage recovery after -a charge or discharge operation*/ - uint16_t relax_period; -/* flat_zone_low : flat_zone_high :if soc falls into the flat zone low% - flat zone high %.the fuel gauge -wait for a cell voltage recovery after a charge or discharge operation.*/ - uint8_t flat_zone_low; - uint8_t flat_zone_high; -/*FCC leaning is disqualified if the discharge capacity in the OCV mode is greater than this threshold*/ - uint16_t max_ocv_discharge; -/*the 21-point OCV table*/ - uint16_t table[OCV_TABLE_SIZE]; - //uint16_t *table; -}; - -/* EDV Point */ -struct edv_point { - int16_t voltage; - uint8_t percent; -}; - -/* EDV Point tracking data */ -struct edv_state { - int16_t voltage; - uint8_t percent; - int16_t min_capacity; - uint8_t edv_cmp; -}; - -/* EDV Configuration */ -struct edv_config { -/*avieraging: True = evokes averaging on voltage reading to detect an EDV condition. - False = no averaging of voltage readings to detect an EDV conditation.*/ - bool averaging; -/*sequential_edv: the sequential_edv setting defines how many times in a row the battery should -pass the EDV threshold to detect an EDV condition. this setting is intended to fiter short voltage spikes -cause by current spikes*/ - uint8_t sequential_edv; -/*filter_light: difine the calculated EDV voltage recovery IIR filter strength - light-lsetting : for light load (below Qmax/5) - heavy setting : for ligh load (above Qmax/5) - the filter is applied only if the load is greater than Qmax/3 - if average = True. then the Qmax/5 threshold is compared to averge current. - otherwise it is compared to current. - Recommended value: 15-255. 255---disabsle the filter - */ - uint8_t filter_light; - uint8_t filter_heavy; -/*overload_current: the current level above which an EDV condition will not be detected and -capacity not reconciled*/ - int16_t overload_current; - - struct edv_point edv[3]; //xsf -/*edv: the end-of-discharge voltage-to-capactiy correlation points.*/ - //struct edv_point *edv; -}; - -/* General Battery Cell Gauging Configuration */ -struct cell_config { - bool cc_polarity; //´ý¶¨To Be Determined - bool cc_out; - /*ocv_below_edv1: if set (True), OCV correction allowed bellow EDV1 point*/ - bool ocv_below_edv1; - /*cc_voltage: the charge complete voltage threshold(e.g. 4.2v) of the battery. - charge cannot be considered complete if the battery voltage is below this threshold*/ - int16_t cc_voltage; - /*cc_current:the charge complete current threshold(e.g. c/20). charge cannot be considered complete - when charge current and average current are greater than this threshold*/ - int16_t cc_current; - /*design_capacity: design capacity of the battery. the battery datasheet should provide this value*/ - uint16_t design_capacity; - /*design_qmax: the calculated discharge capacity of the OCV discharge curve*/ - int16_t design_qmax; - /*r_sense: the resistance of the current sence element. the sense resistor needs to be slelected to - ensure accurate current measuremen and integration at currents >OFF consumption*/ - uint8_t r_sense; - /*qmax_adjust: the value decremented from QMAX every cycle for aging compensation.*/ - uint8_t qmax_adjust; - /*fcc_adjust: the value decremented from the FCC when no learning happen for 25 cycles in a row*/ - uint8_t fcc_adjust; - /*max_overcharge: the fuel gauge tracks the capacity that goes into the battery after a termination - condition is detected. this improve gauging accuracy if the charger's charge termination condition does't - match to the fuel gauge charge termination condition.*/ - uint16_t max_overcharge; - /*electronics_load: the current that the system consumes int the OFF mode(MPU low power, screen OFF)*/ - uint16_t electronics_load; - /*max_increment: the maximum increment of FCC if the learned capacity is much greater than the exiting - FCC. recommentded value 150mAh*/ - int16_t max_increment; - /*max_decrement: the maximum increment of FCC if the learned capacity is much lower - than the exiting FCC*/ - int16_t max_decrement; - /*low_temp: the correlation between voltage and remaining capacity is considered inaccurate below - this temperature. any leaning will be disqualified, if the battery temperature is below this threshold*/ - uint8_t low_temp; - /*deep_dsg_voltage:in order to qualify capacity learning on the discharge, the battery voltage should - be within EDV-deep-dsg_voltage and EDV.*/ - uint16_t deep_dsg_voltage; - /*max_dsg_voltage:limits the amount of the estimated discharge when learning is in progress.\ - if the amount of the capacity estimation get greater than this threshold ,the learning gets disqualified*/ - uint16_t max_dsg_estimate; - /*light_load: FCC learning on discharge disqualifies if the load is below this threshold when the - when EDV2 is reached.*/ - uint8_t light_load; - /*near_full: this defines a capacity zone from FCC to FCC - near_full. A discharge cycles start - from this capacity zone qualifies for FCC larning.*/ - uint16_t near_full; - /*cycle_threshold: the amount of capacity that should be dicharged from the battery to increment - the cycle count by 1.cycle counting happens on the discharge only.*/ - uint16_t cycle_threshold; - /*recharge: the voltage of recharge.*/ - uint16_t recharge; - /*mode_swtich_capacity: this defines how much capacity should pass through the coulomb counter - to cause a cycle count start condition (either charge or discharge). the gauge support 2 cycle typeds. - charge and discharge. a cycle starts when mode_switch_capacity passes through the coulomb counter - the cycle get canceled and switches to the opposite direciton if mode_switch_capacity passes though - the coulomb counter in oppositer direciton.*/ - uint8_t mode_switch_capacity; - /*call_period: approximate time between fuel gauge calls.*/ - uint8_t call_period; - - struct ocv_config *ocv; - struct edv_config *edv; - //struct ocv_config ocv; - //struct edv_config edv; - -}; - -/* Cell State */ -/* -light-load: ( < C/40) - -*/ -struct cell_state { -/*SOC : state-of-charge of the battery in %,it represents the % full of the battery from the - system empty voltage. - SOC = NAC/FCC, SOC = 1 -DOD -*/ - int16_t soc; -/* nac :nominal avaiable charge of the battery in mAh. it represents the present -remain capacity of the battery to the system empty voltage under nominal conditions*/ - int16_t nac; -/*fcc: full battery capacity .this represents the discharge capacity of the battery from -the defined full condition to the system empty voltage(EDV0) under nominal conditions. - the value is learned by the algorithm on qualified charge and discharge cycleds*/ - int16_t fcc; -/* qmax: the battery capacity(mAh) at the OCV curve discharge rate*/ - int16_t qmax; - - int16_t voltage; - int16_t av_voltage; - int16_t cur; - int16_t av_current; - - int16_t temperature; -/*cycle_count: it represents how many charge or discharge cycles a battery has experience. -this is used to estimate the change of impedance of the battery due to "aging"*/ - int16_t cycle_count; -/*sleep : in this mode ,the battery fuel gauge is counting discharge with the coulomb -counter and checking for the battery relaxed condition, if a relaxed battery is -destected the fuel gauge enters OCV mode*/ - bool sleep; - bool relax; - - bool chg; - bool dsg; - - bool edv0; - bool edv1; - bool edv2; - bool ocv; - bool cc; - bool full; - - bool eocl; - bool vcq; - bool vdq; - bool init; - - struct timeval sleep_timer; - struct timeval el_sleep_timer; - uint16_t cumulative_sleep; - - int16_t prev_soc; - int16_t learn_q; - uint16_t dod_eoc; - int16_t learn_offset; - uint16_t learned_cycle; - int16_t new_fcc; - int16_t ocv_total_q; - int16_t ocv_enter_q; - int16_t negative_q; - int16_t overcharge_q; - int16_t charge_cycle_q; - int16_t discharge_cycle_q; - int16_t cycle_q; - uint8_t sequential_cc; - uint8_t sleep_samples; - uint8_t sequential_edvs; - - uint16_t electronics_load; - uint16_t cycle_dsg_estimate; - - struct edv_state edv; - - bool updated; - bool calibrate; - - struct cell_config *config; -}; - -struct battery_platform_data { - int *battery_tmp_tbl; - unsigned int tblsize; - u32 * battery_ocv ; - unsigned int ocv_size; - - unsigned int monitoring_interval; - - unsigned int max_charger_currentmA; - unsigned int max_charger_voltagemV; - unsigned int termination_currentmA; - - unsigned int max_bat_voltagemV; - unsigned int low_bat_voltagemV; - - unsigned int sense_resistor_mohm; - - /* twl6032 */ - unsigned long features; - unsigned long errata; - - struct cell_config *cell_cfg; -}; - - +/* +*rk818-battery.h - Battery fuel gauge driver structures + * + */ +#ifndef RK818_BATTERY +#define RK818_BATTERY +#include + +#define VB_MOD_REG 0x21 + +#define CHRG_COMP_REG1 0x99 +#define CHRG_COMP_REG2 0x9A +#define SUP_STS_REG 0xA0 +#define USB_CTRL_REG 0xA1 +#define CHRG_CTRL_REG1 0xA3 +#define CHRG_CTRL_REG2 0xA4 +#define CHRG_CTRL_REG3 0xA5 +#define BAT_CTRL_REG 0xA6 +#define BAT_HTS_TS1_REG 0xA8 +#define BAT_LTS_TS1_REG 0xA9 +#define BAT_HTS_TS2_REG 0xAA +#define BAT_LTS_TS2_REG 0xAB + + +#define TS_CTRL_REG 0xAC +#define ADC_CTRL_REG 0xAD + +#define ON_SOURCE 0xAE +#define OFF_SOURCE 0xAF + +#define GGCON 0xB0 +#define GGSTS 0xB1 +#define FRAME_SMP_INTERV_REG 0xB2 +#define AUTO_SLP_CUR_THR_REG 0xB3 + +#define GASCNT_CAL_REG3 0xB4 +#define GASCNT_CAL_REG2 0xB5 +#define GASCNT_CAL_REG1 0xB6 +#define GASCNT_CAL_REG0 0xB7 +#define GASCNT3 0xB8 +#define GASCNT2 0xB9 +#define GASCNT1 0xBA +#define GASCNT0 0xBB + +#define BAT_CUR_AVG_REGH 0xBC +#define BAT_CUR_AVG_REGL 0xBD + +#define TS1_ADC_REGH 0xBE +#define TS1_ADC_REGL 0xBF +#define TS2_ADC_REGH 0xC0 +#define TS2_ADC_REGL 0xC1 + +#define BAT_OCV_REGH 0xC2 +#define BAT_OCV_REGL 0xC3 +#define BAT_VOL_REGH 0xC4 +#define BAT_VOL_REGL 0xC5 + +#define RELAX_ENTRY_THRES_REGH 0xC6 +#define RELAX_ENTRY_THRES_REGL 0xC7 +#define RELAX_EXIT_THRES_REGH 0xC8 +#define RELAX_EXIT_THRES_REGL 0xC9 + +#define RELAX_VOL1_REGH 0xCA +#define RELAX_VOL1_REGL 0xCB +#define RELAX_VOL2_REGH 0xCC +#define RELAX_VOL2_REGL 0xCD + +#define BAT_CUR_R_CALC_REGH 0xCE +#define BAT_CUR_R_CALC_REGL 0xCF +#define BAT_VOL_R_CALC_REGH 0xD0 +#define BAT_VOL_R_CALC_REGL 0xD1 + +#define CAL_OFFSET_REGH 0xD2 +#define CAL_OFFSET_REGL 0xD3 + +#define NON_ACT_TIMER_CNT_REG 0xD4 + +#define VCALIB0_REGH 0xD5 +#define VCALIB0_REGL 0xD6 +#define VCALIB1_REGH 0xD7 +#define VCALIB1_REGL 0xD8 + +#define IOFFSET_REGH 0xDD +#define IOFFSET_REGL 0xDE + + +/*0xE0 ~0xF2 data register,*/ +#define SOC_REG 0xE0 + +#define REMAIN_CAP_REG3 0xE1 +#define REMAIN_CAP_REG2 0xE2 +#define REMAIN_CAP_REG1 0xE3 +#define REMAIN_CAP_REG0 0xE4 + +#define UPDAT_LEVE_REG 0xE5 + +#define NEW_FCC_REG3 0xE6 +#define NEW_FCC_REG2 0xE7 +#define NEW_FCC_REG1 0xE8 +#define NEW_FCC_REG0 0xE9 + +#define NON_ACT_TIMER_CNT_REG_SAVE 0xEA +#define TEMP_SOC_REG 0xEB + +#define UBT_INIT_SOC_REG 0xEC +#define UBT_INIT_TEMP_SOC_REG 0xED +#define UBT_INIT_BRANCH 0xEE +#define UBT_PWRON_SOC_REG 0xEF + +/* gasgauge module enable bit 0: disable 1:enabsle +TS_CTRL_REG 0xAC*/ +#define GG_EN (1<<7) + +/*ADC_CTRL_REG*/ +/* +if GG_EN = 0 , then the ADC of BAT voltage controlled by the +bit 0:diabsle 1:enable +*/ +#define ADC_VOL_EN (1<<7) +/* +if GG_EN = 0, then the ADC of BAT current controlled by the +bit 0: disable 1: enable +*/ +#define ADC_CUR_EN (1<<6) +/*the ADC of TS1 controlled by the bit 0:disabsle 1:enable */ +#define ADC_TS1_EN (1<<5) +/*the ADC of TS2 controlled by the bit 0:disabsle 1:enable */ +#define ADC_TS2_EN (1<<4) +/*ADC colock phase 0:normal 1:inverted*/ +#define ADC_PHASE (1<<3) +#define ADC_CLK_SEL 7 +/***************************************************** +#define ADC_CLK_SEL_2M 0x000 +#define ADC_CLK_SEL_1M 0x001 +#define ADC_CLK_SEL_500K 0x002 +#define ADC_CLK_SEL_250K 0x003 +#define ADC_CLK_SEL_125K 0x004 +******************************************************/ +/*GGCON*/ +/* ADC bat current continue sample times 00:8 01:16 10:32 11:64*/ +#define CUR_SAMPL_CON_TIMES (3<<6) +/*ADC offset calibreation interval time 00:8min 01:16min 10:32min 11:48min*/ +#define ADC_OFF_CAL_INTERV (3<<4) +/*OCV sampling interval time 00:8min 01:16min 10:32min :11:48min*/ +#define OCV_SAMPL_INTERV (3<<2) + +/*ADC working in current voltage collection mode*/ +#define ADC_CUR_VOL_MODE (1<<1) +/*ADC working in resistor calculation mode 0:disable 1:enable*/ +#define ADC_RES_MODE 1 + +/*GGSTS*/ +/*average current filter times 00:1/2 01:1/4 10:1/8 11:1/16*/ +#define RES_CUR_AVG_SEL (3<<5) +/*battery first connection,edge trigger 0:NOT 1:YES*/ +#define BAT_CON (1<<4) +/*battery voltage1 update in relax status 0: NOT 1:YE*/ +#define RELAX_VOL1_UPD (1<<3) +/*battery voltage2 update in relax status 0: NOT 1:YE*/ +#define RELAX_VOL2_UPD (1<<2) +/*battery coming into relax status 0: NOT 1:YE*/ +#define RELAX_STS (1<<1) +/*battery average voltage and current updated status 0: NOT 1:YES*/ +#define IV_AVG_UPD_STS (1<<0) + +/*FRAME_SMP_INTERV_REG*/ +#define AUTO_SLP_EN (1<<5) +/* auto sleep mode 0:disable 1:enable*/ +#define FRAME_SMP_INTERV_TIME 0x1F + +#define PLUG_IN_STS (1<<6) + +/*SUP_STS_REG*/ +#define BAT_EXS (1<<7) +#define CHARGE_OFF (0x00<<4) +#define DEAD_CHARGE (0x01<<4) +#define TRICKLE_CHARGE (0x02<<4) +#define CC_OR_CV (0x03<<4) +#define CHARGE_FINISH (0x04<<4) +#define USB_OVER_VOL (0x05<<4) +#define BAT_TMP_ERR (0x06<<4) +#define TIMER_ERR (0x07<<4) +/* usb is exists*/ +#define USB_EXIST (1<<1) +/* usb is effective*/ +#define USB_EFF (1<<0) + +/*USB_CTRL_REG*/ +#define CHRG_CT_EN (1<<7) +/* USB_VLIM_SEL*/ +#define VLIM_4000MV (0x00<<4) +#define VLIM_4100MV (0x01<<4) +#define VLIM_4200MV (0x02<<4) +#define VLIM_4300MV (0x03<<4) +#define VLIM_4400MV (0x04<<4) +#define VLIM_4500MV (0x05<<4) +#define VLIM_4600MV (0x06<<4) +#define VLIM_4700MV (0x07<<4) +/*USB_ILIM_SEL*/ +#define ILIM_45MA (0x00) +#define ILIM_300MA (0x01) +#define ILIM_80MA (0x02) +#define ILIM_820MA (0x03) +#define ILIM_1000MA (0x04) +#define ILIM_1200MA (0x05) +#define ILIM_1400MA (0x06) +#define ILIM_1600MA (0x07) +#define ILIM_1800MA (0x08) +#define ILIM_2000MA (0x09) +#define ILIM_2200MA (0x0A) +#define ILIM_2400MA (0x0B) +#define ILIM_2600MA (0x0C) +#define ILIM_2800MA (0x0D) +#define ILIM_3000MA (0x0E) + +/*CHRG_CTRL_REG*/ +#define CHRG_EN (0x01<<7) + +/*CHRG_VOL_SEL*/ +#define CHRG_VOL4050 (0x00<<4) +#define CHRG_VOL4100 (0x01<<4) +#define CHRG_VOL4150 (0x02<<4) +#define CHRG_VOL4200 (0x03<<4) +#define CHRG_VOL4300 (0x04<<4) +#define CHRG_VOL4350 (0x05<<4) + +/*CHRG_CUR_SEL*/ +#define CHRG_CUR1000mA (0x00) +#define CHRG_CUR1200mA (0x01) +#define CHRG_CUR1400mA (0x02) +#define CHRG_CUR1600mA (0x03) +#define CHRG_CUR1800mA (0x04) +#define CHRG_CUR2000mA (0x05) +#define CHRG_CUR2200mA (0x06) +#define CHRG_CUR2400mA (0x07) +#define CHRG_CUR2600mA (0x08) +#define CHRG_CUR2800mA (0x09) +#define CHRG_CUR3000mA (0x0A) + +/*CHRG_CTRL_REG2*/ +#define FINISH_100MA (0X00 << 6) +#define FINISH_150MA (0X01 << 6) +#define FINISH_200MA (0X10 << 6) +#define FINISH_250MA (0X11 << 6) + +/* CHRG_CTRL_REG2*/ +#define CHRG_TERM_ANA_SIGNAL (0 << 5) +#define CHRG_TERM_DIG_SIGNAL (1 << 5) + +/*GGCON*/ +#define SAMP_TIME_8MIN (0X00<<4) +#define SAMP_TIME_16MIN (0X01<<4) +#define SAMP_TIME_32MIN (0X02<<4) +#define SAMP_TIME_48MIN (0X03<<4) + +#define DRIVER_VERSION "2.0.0" +#define ROLEX_SPEED (100 * 1000) + +#define CHARGING 0x01 +#define DISCHARGING 0x00 + +#define TIMER_MS_COUNTS 1000 +#define MAX_CHAR 0x7F +#define MAX_UNSIGNED_CHAR 0xFF +#define MAX_INT 0x7FFF +#define MAX_UNSIGNED_INT 0xFFFF +#define MAX_INT8 0x7F +#define MAX_UINT8 0xFF + +/* Gas Gauge Constatnts */ +#define TEMP_0C 2732 +#define MAX_CAPACITY 0x7fff +#define MAX_SOC 100 +#define MAX_PERCENTAGE 100 + +/* Num, cycles with no Learning, after this many cycles, the gauge + start adjusting FCC, based on Estimated Cell Degradation */ +#define NO_LEARNING_CYCLES 25 +/* Size of the OCV Lookup table */ +#define OCV_TABLE_SIZE 21 +/* + * OCV Config + */ +struct ocv_config { + /*voltage_diff, current_diff: Maximal allowed deviation + of the voltage and the current from one reading to the + next that allows the fuel gauge to apply an OCV correction. + The main purpose of these thresholds is to filter current + and voltage spikes. Recommended value: these value are + highly depend on the load nature. if the load creates a lot + of current spikes .the value may need to be increase*/ + uint8_t voltage_diff; + uint8_t current_diff; + /* sleep_enter_current: if the current remains under + this threshold for [sleep_enter_samples] + consecutive samples. the gauge enters the SLEEP MODE*/ + uint8_t sleep_enter_current; + /*sleep_enter_samples: the number of samples that + satis fy asleep enter or exit condition in order + to actually enter of exit SLEEP mode*/ + uint8_t sleep_enter_samples; + /*sleep_exit_samples: to exit SLEEP mode , average + current should pass this threshold first. then + current should remain above this threshold for + [sleep_exit_samples] consecutive samples*/ + uint8_t sleep_exit_current; + /*sleep_exit_samples: to exit SLEEP mode, average + current should pass this threshold first, then current + should remain above this threshold for [sleep_exit_samples] + consecutive samples.*/ + uint8_t sleep_exit_samples; + /*relax_period: defines the number of seconds the + fuel gauge should spend in the SLEEP mode + before entering the OCV mode, this setting makes + the gauge wait for a cell voltage recovery after + a charge or discharge operation*/ + uint16_t relax_period; + /* flat_zone_low : flat_zone_high :if soc falls into + the flat zone low% - flat zone high %.the fuel gauge + wait for a cell voltage recovery after a charge or + discharge operation.*/ + uint8_t flat_zone_low; + uint8_t flat_zone_high; + /*FCC leaning is disqualified if the discharge capacity + in the OCV mode is greater than this threshold*/ + uint16_t max_ocv_discharge; + /*the 21-point OCV table*/ + uint16_t table[OCV_TABLE_SIZE]; + /*uint16_t *table;*/ +}; + +/* EDV Point */ +struct edv_point { + int16_t voltage; + uint8_t percent; +}; + +/* EDV Point tracking data */ +struct edv_state { + int16_t voltage; + uint8_t percent; + int16_t min_capacity; + uint8_t edv_cmp; +}; + +/* EDV Configuration */ +struct edv_config { + /*avieraging: True = evokes averaging on voltage + reading to detect an EDV condition. + False = no averaging of voltage readings to detect an + EDV conditation.*/ + bool averaging; + /*sequential_edv: the sequential_edv setting defines + how many times in a row the battery should + pass the EDV threshold to detect an EDV condition. + this setting is intended to fiter short voltage spikes + cause by current spikes*/ + uint8_t sequential_edv; + /*filter_light: difine the calculated EDV voltage + recovery IIR filter strength + light-lsetting : for light load (below Qmax/5) + heavy setting : for ligh load (above Qmax/5) + the filter is applied only if the load is greater than + Qmax/3. if average = True. then the Qmax/5 threshold + is compared to averge current.otherwise it is compared + to current. + Recommended value: 15-255. 255---disabsle the filter + */ + uint8_t filter_light; + uint8_t filter_heavy; + /*overload_current: the current level above which an + EDV condition will not be detected and + capacity not reconciled*/ + int16_t overload_current; + + struct edv_point edv[3]; + /*edv: the end-of-discharge voltage-to-capactiy + correlation points.*/ + /*struct edv_point *edv;*/ +}; + +/* General Battery Cell Gauging Configuration */ +struct cell_config { + bool cc_polarity; /*To Be Determined*/ + bool cc_out; + /*ocv_below_edv1: if set (True), OCV correction allowed + bellow EDV1 point*/ + bool ocv_below_edv1; + /*cc_voltage: the charge complete voltage threshold(e.g. 4.2v) + of the battery. charge cannot be considered complete if the + battery voltage is below this threshold*/ + int16_t cc_voltage; + /*cc_current:the charge complete current threshold(e.g. c/20). + charge cannot be considered complete when charge + current and average current are greater than this threshold*/ + int16_t cc_current; + /*design_capacity: design capacity of the battery. + the battery datasheet should provide this value*/ + uint16_t design_capacity; + /*design_qmax: the calculated discharge capacity of + the OCV discharge curve*/ + int16_t design_qmax; + /*r_sense: the resistance of the current sence element. + the sense resistor needs to be slelected to + ensure accurate current measuremen and integration + at currents >OFF consumption*/ + uint8_t r_sense; + /*qmax_adjust: the value decremented from QMAX + every cycle for aging compensation.*/ + uint8_t qmax_adjust; + /*fcc_adjust: the value decremented from the FCC + when no learning happen for 25 cycles in a row*/ + uint8_t fcc_adjust; + /*max_overcharge: the fuel gauge tracks the capacity + that goes into the battery after a termination + condition is detected. this improve gauging accuracy + if the charger's charge termination condition does't + match to the fuel gauge charge termination condition.*/ + uint16_t max_overcharge; + /*electronics_load: the current that the system consumes + int the OFF mode(MPU low power, screen OFF)*/ + uint16_t electronics_load; + /*max_increment: the maximum increment of FCC if the + learned capacity is much greater than the exiting + FCC. recommentded value 150mAh*/ + int16_t max_increment; + /*max_decrement: the maximum increment of FCC if the + learned capacity is much lower than the exiting FCC*/ + int16_t max_decrement; + /*low_temp: the correlation between voltage and remaining + capacity is considered inaccurate below this temperature. + any leaning will be disqualified, if the battery temperature + is below this threshold + */ + uint8_t low_temp; + /*deep_dsg_voltage:in order to qualify capacity learning on + the discharge, the battery voltage should + be within EDV-deep-dsg_voltage and EDV.*/ + uint16_t deep_dsg_voltage; + /* + max_dsg_voltage:limits the amount of the estimated + discharge when learning is in progress. if the amount of + the capacity estimation get greater than this threshold, + the learning gets disqualified + */ + uint16_t max_dsg_estimate; + /* + light_load: FCC learning on discharge disqualifies if + the load is below this threshold when the + when EDV2 is reached. + */ + uint8_t light_load; + /* + near_full: this defines a capacity zone from FCC + to FCC - near_full. A discharge cycles start + from this capacity zone qualifies for FCC larning. + */ + uint16_t near_full; + /* + cycle_threshold: the amount of capacity that should + be dicharged from the battery to increment the cycle + count by 1.cycle counting happens on the discharge only. + */ + uint16_t cycle_threshold; + /*recharge: the voltage of recharge.*/ + uint16_t recharge; + /* + mode_swtich_capacity: this defines how much capacity + should pass through the coulomb counter to cause a cycle + count start condition (either charge or discharge). the gauge + support 2 cycle typeds.charge and discharge. a cycle starts + when mode_switch_capacity passes through the coulomb counter + the cycle get canceled and switches to the opposite direciton + if mode_switch_capacity passes though + the coulomb counter in oppositer direciton. + */ + uint8_t mode_switch_capacity; + /*call_period: approximate time between fuel gauge calls.*/ + uint8_t call_period; + + struct ocv_config *ocv; + struct edv_config *edv; +}; + +/* Cell State */ +/* +light-load: ( < C/40) + +*/ +struct cell_state { + /* + SOC : state-of-charge of the battery in %,it represents + the % full of the battery from the system empty voltage. + SOC = NAC/FCC, SOC = 1 -DOD + */ + int16_t soc; + /* + nac :nominal avaiable charge of the battery in mAh. + it represents the present remain capacity of the battery + to the system empty voltage under nominal conditions + */ + int16_t nac; + /* + fcc: full battery capacity .this represents the discharge capacity + of the battery from the defined full condition to the system empty + voltage(EDV0) under nominal conditions.the value is learned by + the algorithm on qualified charge and discharge cycleds + */ + int16_t fcc; + /* qmax: the battery capacity(mAh) at the OCV curve discharge rate*/ + int16_t qmax; + + int16_t voltage; + int16_t av_voltage; + int16_t cur; + int16_t av_current; + + int16_t temperature; + /* + cycle_count: it represents how many charge or discharge + cycles a battery has experience. this is used to estimate the + change of impedance of the battery due to "aging" + */ + int16_t cycle_count; + /* + sleep : in this mode ,the battery fuel gauge is counting + discharge with the coulomb counter and checking for the + battery relaxed condition, if a relaxed battery is destected + the fuel gauge enters OCV mode + */ + bool sleep; + bool relax; + + bool chg; + bool dsg; + + bool edv0; + bool edv1; + bool edv2; + bool ocv; + bool cc; + bool full; + + bool eocl; + bool vcq; + bool vdq; + bool init; + + struct timeval sleep_timer; + struct timeval el_sleep_timer; + uint16_t cumulative_sleep; + + int16_t prev_soc; + int16_t learn_q; + uint16_t dod_eoc; + int16_t learn_offset; + uint16_t learned_cycle; + int16_t new_fcc; + int16_t ocv_total_q; + int16_t ocv_enter_q; + int16_t negative_q; + int16_t overcharge_q; + int16_t charge_cycle_q; + int16_t discharge_cycle_q; + int16_t cycle_q; + uint8_t sequential_cc; + uint8_t sleep_samples; + uint8_t sequential_edvs; + + uint16_t electronics_load; + uint16_t cycle_dsg_estimate; + + struct edv_state edv; + + bool updated; + bool calibrate; + + struct cell_config *config; +}; + +struct battery_platform_data { + int *battery_tmp_tbl; + unsigned int tblsize; + u32 *battery_ocv; + unsigned int ocv_size; + + unsigned int monitoring_interval; + + unsigned int max_charger_currentmA; + unsigned int max_charger_voltagemV; + unsigned int termination_currentmA; + + unsigned int max_bat_voltagemV; + unsigned int low_bat_voltagemV; + + unsigned int sense_resistor_mohm; + + /* twl6032 */ + unsigned long features; + unsigned long errata; + + struct cell_config *cell_cfg; +}; + +extern void rk_send_wakeup_key(void); + +#endif