From d2dc4fb8c3eb071fcfaf21accf67bb3102c37b99 Mon Sep 17 00:00:00 2001 From: chenjh Date: Tue, 12 May 2015 17:22:33 +0800 Subject: [PATCH] power: rk81x-battery: update verison to 4.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit this verison can support both ARM and SOFIA platform. Signed-off-by: chenjh Signed-off-by: 许盛飞 --- drivers/power/rk818_battery.c | 4483 +++++++++-------- .../linux => drivers}/power/rk818_battery.h | 44 +- include/linux/mfd/rk818.h | 2 +- 3 files changed, 2446 insertions(+), 2083 deletions(-) rename {include/linux => drivers}/power/rk818_battery.h (96%) diff --git a/drivers/power/rk818_battery.c b/drivers/power/rk818_battery.c index c544efd7bda0..433077c370e4 100755 --- a/drivers/power/rk818_battery.c +++ b/drivers/power/rk818_battery.c @@ -1,11 +1,31 @@ /* - * rk818 battery driver + * rk818/rk819 battery driver * - * 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. - * */ - + * Copyright (C) 2014 Rockchip Electronics Co., Ltd + * Author: zhangqing + * chenjh + * Andy Yan + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * Author: Texas Instruments, Inc. + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * Author: Texas Instruments, Inc. + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Author: zhangqing + * Copyright (C) 2014-2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include #include #include #include @@ -20,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -29,13 +48,22 @@ #include #include #include +#include +#include + +#if defined(CONFIG_X86_INTEL_SOFIA) +#include +#endif +#include "rk818_battery.h" /* if you want to disable, don't set it as 0, - just be: "static int dbg_enable;" is ok*/ +just be: "static int dbg_enable;" is ok*/ + static int dbg_enable; #define RK818_SYS_DBG 1 module_param_named(dbg_level, dbg_enable, int, 0644); + #define DBG(args...) \ do { \ if (dbg_enable) { \ @@ -43,32 +71,31 @@ module_param_named(dbg_level, dbg_enable, int, 0644); } \ } while (0) - #define DEFAULT_BAT_RES 135 -#define DEFAULT_VLMT 4200 -#define DEFAULT_ILMT 2000 -#define DEFAULT_ICUR 1600 - -#define DEF_TEST_ILMT_MA 2000 -#define DEF_TEST_CURRENT_MA 1800 +#define DEFAULT_CHRG_VOL 4200 +#define DEFAULT_CHRG_CUR 1000 +#define DEFAULT_INPUT_CUR 1400 +#define DEFAULT_SLP_ENTER_CUR 600 +#define DEFAULT_SLP_EXIT_CUR 600 +#define DSOC_DISCHRG_EMU_CURR 1200 #define DSOC_DISCHRG_FAST_DEC_SEC 120 /*seconds*/ -#define DSOC_DISCHRG_FAST_EER_RANGE 25 +#define DSOC_DISCHRG_FAST_EER_RANGE 10 #define DSOC_CHRG_FAST_CALIB_CURR_MAX 400 /*mA*/ #define DSOC_CHRG_FAST_INC_SEC 120 /*seconds*/ -#define DSOC_CHRG_FAST_EER_RANGE 15 +#define DSOC_CHRG_FAST_EER_RANGE 10 #define DSOC_CHRG_EMU_CURR 1200 -#define DSOC_CHG_TERM_CURR 600 -#define DSOC_CHG_TERM_VOL 4100 -#define CHG_FINISH_VOL 4100 +#define DSOC_CHRG_TERM_CURR 600 +#define DSOC_CHRG_TERM_VOL 4100 +#define CHRG_FINISH_VOL 4100 /*realtime RSOC calib param*/ -#define RSOC_DISCHG_ERR_LOWER 40 -#define RSOC_DISCHG_ERR_UPPER 50 +#define RSOC_DISCHRG_ERR_LOWER 40 +#define RSOC_DISCHRG_ERR_UPPER 50 #define RSOC_ERR_CHCK_CNT 15 #define RSOC_COMPS 20 /*compensation*/ #define RSOC_CALIB_CURR_MAX 900 /*mA*/ -#define RSOC_CALIB_DISCHGR_TIME 3 /*min*/ +#define RSOC_CALIB_DISCHRGR_TIME 3 /*min*/ #define RSOC_RESUME_ERR 10 #define REBOOT_INTER_MIN 1 @@ -77,9 +104,9 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define MAX_INT 0x7FFF #define TIME_10MIN_SEC 600 -#define CHG_VOL_SHIFT 4 -#define CHG_ILIM_SHIFT 0 -#define CHG_ICUR_SHIFT 0 +#define CHRG_VOL_SHIFT 4 +#define CHRG_ILIM_SHIFT 0 +#define CHRG_ICUR_SHIFT 0 #define DEF_CHRG_VOL CHRG_VOL4200 #define DEF_CHRG_CURR_SEL CHRG_CUR1400mA #define DEF_CHRG_CURR_LMT ILIM_2000MA @@ -93,69 +120,95 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define TEST_AC_ONLINE 1 #define TEST_USB_ONLINE 0 +#define ZERO_ALGOR_THRESD 3800 +#define DISCHRG_ZERO_MODE 1 +#define DISCHRG_NORMAL_MODE 0 +#define DEF_LAST_ZERO_MODE_SOC -1 + +#define DISCHRG_MODE 0 +#define CHRG_MODE 1 + +#define TREND_STAT_FLAT 0 +#define TREND_STAT_DOWN -1 +#define TREND_STAT_UP 1 +#define TREND_CAP_DIFF 5 + +#define MINUTE 60 + +#define SLP_CURR_MAX 40 +#define SLP_CURR_MIN 6 +#define WAKEUP_SEC_THRESD 40 +#define CHRG_TIME_STEP (60) +#define DISCHRG_TIME_STEP_0 (30 * 60) +#define DISCHRG_TIME_STEP_1 (60 * 60) + +#define DEF_PCB_OFFSET 42 +#define DEF_CAL_OFFSET 0x832 +#define DEF_PWRPATH_RES 50 +#define SEC_TO_EMPTY 300 +#define DSOC_CHRG_FINISH_CURR 1100 +#define SLP_CHRG_CURR 1000 +#define SLP_DSOC_VOL_THRESD 3600 +#define PWR_OFF_THRESD 3400 +#define MIN_ZERO_ACCURACY 10 /*0.01%*/ + +#define MAX_FCC 10000 +#define MIN_FCC 500 /* * the following table value depends on datasheet */ -int CHG_V_LMT[] = {4050, 4100, 4150, 4200, 4300, 4350}; +int CHRG_V_LMT[] = {4050, 4100, 4150, 4200, 4300, 4350}; -int CHG_I_CUR[] = {1000, 1200, 1400, 1600, 1800, 2000, +int CHRG_I_CUR[] = {1000, 1200, 1400, 1600, 1800, 2000, 2250, 2400, 2600, 2800, 3000}; -int CHG_I_LMT[] = {450, 800, 850, 1000, 1250, 1500, 1750, +int CHRG_I_LMT[] = {450, 800, 850, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000}; -u8 CHG_CVCC_HOUR[] = {4, 5, 6, 8, 10, 12, 14, 16}; +u8 CHRG_CVCC_HOUR[] = {4, 5, 6, 8, 10, 12, 14, 16}; #define RK818_DC_IN 0 #define RK818_DC_OUT 1 -#define SEC_TO_MIN(x) ((x)/60) -#define BASE_TO_MIN(x) ((get_seconds()-(x))/60) -#define BASE_TO_SEC(x) (get_seconds()-(x)) #define OCV_VALID_SHIFT (0) #define OCV_CALIB_SHIFT (1) #define FIRST_PWRON_SHIFT (2) +#define LOADER_CHRG_SHIFT (3) +#define SEC_TO_MIN(x) ((x) / 60) -struct battery_info { +struct rk81x_battery { 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 rk818 *rk818; - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; - struct battery_platform_data *platform_data; + struct battery_platform_data *pdata; int dc_det_pin; int dc_det_level; - int dc_det_pullup_inside; - int work_on; + int dc_det_irq; int irq; int ac_online; int usb_online; - int dc_online; - int status; + int psy_status; int current_avg; int current_offset; uint16_t voltage; uint16_t voltage_ocv; uint16_t relax_voltage; - u8 charge_status; + u8 chrg_status; + u8 slp_chrg_status; + u8 otg_status; int pcb_ioffset; bool pcb_ioffset_updated; - unsigned long queue_work_cnt; - u32 term_chg_cnt; - u32 emu_chg_cnt; - - uint16_t warnning_voltage; int design_capacity; int fcc; @@ -163,10 +216,10 @@ struct battery_info { int remain_capacity; int nac; int temp_nac; - int real_soc; + int dsoc; int display_soc; - int odd_capacity; - int temp_soc; + int rsoc; + int trend_start_cap; int est_ocv_vol; int est_ocv_soc; @@ -196,86 +249,139 @@ struct battery_info { int voltage_k;/* VCALIB0 VCALIB1 */ int voltage_b; + bool enter_finish; + int zero_timeout_cnt; + int zero_old_remain_cap; - int zero_updated; - int old_display_soc; - int zero_cycle; - - - int update_k; int line_k; - int voltage_old; - - int q_dead; - int q_err; - int q_shtd; - u8 check_count; - /* u32 status; */ - struct timeval soc_timer; - struct timeval change_timer; - int vol_smooth_time; int charge_smooth_time; int sum_suspend_cap; int suspend_cap; - int resume_capacity; - struct timespec suspend_time; - struct timespec resume_time; - unsigned long suspend_time_start; - unsigned long count_sleep_time; + + unsigned long suspend_time_sum; int suspend_rsoc; - int sleep_status; + int slp_psy_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; + bool discharge_smooth_status; - unsigned long charging_time; - unsigned long discharging_time; - unsigned long finish_time; + u32 plug_in_min; + u32 plug_out_min; + u32 finish_sig_min; - u32 charge_min; - u32 discharge_min; - u32 finish_min; struct notifier_block battery_nb; + struct usb_phy *usb_phy; + struct notifier_block usb_nb; + struct notifier_block fb_nb; + int fb_blank; + int early_resume; + int s2r; /*suspend to resume*/ struct workqueue_struct *wq; struct delayed_work battery_monitor_work; struct delayed_work charge_check_work; + struct delayed_work usb_phy_delay_work; + struct delayed_work chrg_term_mode_switch_work; int charge_otg; + int ma; struct wake_lock resume_wake_lock; - unsigned long sys_on_base; - unsigned long chrg_time_base; - int chrg_time2_full; - int chrg_cap2_full; + unsigned long plug_in_base; + unsigned long plug_out_base; + unsigned long finish_sig_base; + unsigned long power_on_base; - bool is_first_poweron; - int first_on_cap; + int chrg_time2full; + int chrg_cap2full; + bool is_first_poweron; int fg_drv_mode; - int test_chrg_current; - int test_chrg_ilmt; int debug_finish_real_soc; int debug_finish_temp_soc; int chrg_min[10]; - int chg_v_lmt; - int chg_i_lmt; - int chg_i_cur; - + int chrg_v_lmt; + int chrg_i_lmt; + int chrg_i_cur; + uint16_t pwroff_min; + unsigned long wakeup_sec; + u32 delta_vol_smooth; + unsigned long dischrg_normal_base; + unsigned long dischrg_emu_base; + unsigned long chrg_normal_base; + unsigned long chrg_term_base; + unsigned long chrg_emu_base; + unsigned long chrg_finish_base; + unsigned long fcc_update_sec; + int loader_charged; + u8 dischrg_algorithm_mode; + int last_zero_mode_dsoc; + u8 current_mode; + unsigned long dischrg_save_sec; + unsigned long chrg_save_sec; + struct timeval suspend_rtc_base; }; -struct battery_info *g_battery; -u32 support_uboot_chrg, support_usb_adp, support_dc_adp; -static void rk81x_update_battery_info(struct battery_info *di); +u32 support_usb_adp, support_dc_adp; + +#define to_device_info(x) container_of((x), \ + struct rk81x_battery, bat) + +#define to_ac_device_info(x) container_of((x), \ + struct rk81x_battery, ac) + +#define to_usb_device_info(x) container_of((x), \ + struct rk81x_battery, usb) + +static int loader_charged; + +static int __init rk81x_bat_loader_charged(char *__unused) +{ + loader_charged = 1; + + pr_info("battery charged in loader\n"); + + return 0; +} +__setup("loader_charged", rk81x_bat_loader_charged); + +static u64 g_base_sec; +static u64 get_runtime_sec(void) +{ + u64 ts_ns = local_clock(); + + do_div(ts_ns, 1000000000); + + return ts_ns + g_base_sec; +} + +static u64 is_local_clock_reset(void) +{ + u64 ts_ns = local_clock(); + + do_div(ts_ns, 1000000000); + + return !ts_ns; +} + +static inline unsigned long BASE_TO_SEC(unsigned long x) +{ + if (x) + return get_runtime_sec() - x; + else + return 0; +} -static bool rk81x_support_adp_type(enum hw_support_adp_t type) +static inline unsigned long BASE_TO_MIN(unsigned long x) +{ + return BASE_TO_SEC(x) / 60; +} + +static bool rk81x_bat_support_adp_type(enum hw_support_adp type) { bool bl = false; @@ -299,6 +405,11 @@ static bool rk81x_support_adp_type(enum hw_support_adp_t type) return bl; } +static bool rk81x_chrg_online(struct rk81x_battery *di) +{ + return di->usb_online || di->ac_online; +} + static u32 interpolate(int value, u32 *table, int size) { uint8_t i; @@ -310,11 +421,11 @@ static u32 interpolate(int value, u32 *table, int size) } if ((i > 0) && (i < size)) { - d = (value - table[i-1]) * (INTERPOLATE_MAX/(size-1)); + d = (value - table[i-1]) * (INTERPOLATE_MAX / (size - 1)); d /= table[i] - table[i-1]; - d = d + (i-1) * (INTERPOLATE_MAX/(size-1)); + d = d + (i-1) * (INTERPOLATE_MAX / (size - 1)); } else { - d = i * ((INTERPOLATE_MAX+size/2)/size); + d = i * ((INTERPOLATE_MAX + size / 2) / size); } if (d > 1000) @@ -322,6 +433,7 @@ static u32 interpolate(int value, u32 *table, int size) return d; } + /* Returns (a * b) / c */ static int32_t ab_div_c(u32 a, u32 b, u32 c) { @@ -335,7 +447,7 @@ static int32_t ab_div_c(u32 a, u32 b, u32 c) if (sign) c = -c; - tmp = ((int32_t) a*b + (c>>1)) / c; + tmp = (a * b + (c >> 1)) / c; if (tmp < MAX_INT) ans = tmp; @@ -347,99 +459,160 @@ static int32_t ab_div_c(u32 a, u32 b, u32 c) return ans; } -static int32_t abs_int(int32_t x) +static int div(int val) { - return (x > 0) ? x : -x; + return (val == 0) ? 1 : val; } -static int abs32_int(int x) +static int rk81x_bat_read(struct rk81x_battery *di, u8 reg, + u8 buf[], unsigned len) { - return (x > 0) ? x : -x; -} + int ret = -1; + int i; -static int div(int val) -{ - return (val == 0) ? 1 : val; + for (i = 0; ret < 0 && i < 3; i++) { + ret = rk818_i2c_read(di->rk818, reg, len, buf); + if (ret < 0) + dev_err(di->dev, "read reg:0x%02x failed\n", reg); + } + + return (ret < 0) ? ret : 0; } -static int battery_read(struct rk818 *rk818, u8 reg, - u8 buf[], unsigned len) +static int rk81x_bat_write(struct rk81x_battery *di, u8 reg, + u8 const buf[], unsigned len) { - int ret; + int ret = -1; + int i; - ret = rk818_i2c_read(rk818, reg, len, buf); - return ret; + for (i = 0; ret < 0 && i < 3; i++) { + ret = rk818_i2c_write(di->rk818, reg, (int)len, *buf); + if (ret < 0) + dev_err(di->dev, "write reg:0x%02x failed\n", reg); + } + + return (ret < 0) ? ret : 0; } -static int battery_write(struct rk818 *rk818, u8 reg, - u8 const buf[], unsigned len) +static int rk81x_bat_set_bit(struct rk81x_battery *di, u8 reg, u8 shift) { - int ret; + int ret = -1; + int i; + + for (i = 0; ret < 0 && i < 3; i++) { + ret = rk818_set_bits(di->rk818, reg, 1 << shift, 1 << shift); + if (ret < 0) + dev_err(di->dev, "set reg:0x%02x failed\n", reg); + } - ret = rk818_i2c_write(rk818, reg, (int)len, *buf); return ret; } -static void rk81x_set_bit(struct battery_info *di, u8 reg, u8 shift) +static int rk81x_bat_clr_bit(struct rk81x_battery *di, u8 reg, u8 shift) { - rk818_set_bits(di->rk818, reg, 1 << shift, 1 << shift); -} + int ret = -1; + int i; -static void rk81x_clr_bit(struct battery_info *di, u8 reg, u8 shift) -{ - rk818_set_bits(di->rk818, reg, 1 << shift, 0 << shift); + for (i = 0; ret < 0 && i < 3; i++) { + ret = rk818_set_bits(di->rk818, reg, 1 << shift, 0 << shift); + if (ret < 0) + dev_err(di->dev, "set reg:0x%02x failed\n", reg); + } + + return ret; } -static u8 rk81x_read_bit(struct battery_info *di, u8 reg, u8 shift) +static u8 rk81x_bat_read_bit(struct rk81x_battery *di, u8 reg, u8 shift) { u8 buf; u8 val; - battery_read(di->rk818, reg, &buf, 1); + rk81x_bat_read(di, reg, &buf, 1); val = (buf & BIT(shift)) >> shift; return val; } -static void dump_gauge_register(struct battery_info *di) +static void rk81x_dbg_dmp_gauge_regs(struct rk81x_battery *di) { int i = 0; - char buf; + u8 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); + for (i = 0xAC; i < 0xEE; i++) { + rk81x_bat_read(di, i, &buf, 1); + DBG("0x%02x : 0x%02x\n", i, buf); } DBG("demp end!\n"); } -static void dump_charger_register(struct battery_info *di) +static void rk81x_dbg_dmp_charger_regs(struct rk81x_battery *di) { 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); + rk81x_bat_read(di, i, &buf, 1); DBG(" the register is 0x%02x, the value is 0x%02x\n", i, buf); } DBG("demp end!\n"); } -#if RK818_SYS_DBG +static void rk81x_bat_reset_zero_var(struct rk81x_battery *di) +{ + di->dischrg_algorithm_mode = DISCHRG_NORMAL_MODE; + di->last_zero_mode_dsoc = DEF_LAST_ZERO_MODE_SOC; +} + +static void rk81x_bat_capacity_init_post(struct rk81x_battery *di) +{ + rk81x_bat_reset_zero_var(di); + di->trend_start_cap = di->remain_capacity; +} -static void _capacity_init(struct battery_info *di, u32 capacity); +static void rk81x_bat_capacity_init(struct rk81x_battery *di, u32 capacity) +{ + u8 buf; + u32 capacity_ma; + int delta_cap; + + delta_cap = capacity - di->remain_capacity; + if (!delta_cap) + return; + + di->adjust_cap += delta_cap; + + capacity_ma = capacity * 2390;/* 2134;//36*14/900*4096/521*500; */ + do { + buf = (capacity_ma >> 24) & 0xff; + rk81x_bat_write(di, GASCNT_CAL_REG3, &buf, 1); + buf = (capacity_ma >> 16) & 0xff; + rk81x_bat_write(di, GASCNT_CAL_REG2, &buf, 1); + buf = (capacity_ma >> 8) & 0xff; + rk81x_bat_write(di, GASCNT_CAL_REG1, &buf, 1); + buf = (capacity_ma & 0xff) | 0x01; + rk81x_bat_write(di, GASCNT_CAL_REG0, &buf, 1); + rk81x_bat_read(di, GASCNT_CAL_REG0, &buf, 1); + + } while (buf == 0); + + dev_info(di->dev, "update capacity :%d--remain_cap:%d\n", + capacity, di->remain_capacity); +} +#if RK818_SYS_DBG /* - * interface for debug: do rsoc_first_poweron_init() without unloading battery + * interface for debug: do rk81x_bat_first_pwron() without unloading battery */ static ssize_t bat_calib_read(struct device *dev, struct device_attribute *attr, char *buf) { - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); int val; - val = rk81x_read_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + val = rk81x_bat_read_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + return sprintf(buf, "%d\n", val); } @@ -449,16 +622,17 @@ static ssize_t bat_calib_write(struct device *dev, { u8 val; int ret; - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); ret = kstrtou8(buf, 0, &val); if (ret < 0) return ret; if (val) - rk81x_set_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + rk81x_bat_set_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); else - rk81x_clr_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + rk81x_bat_clr_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); return count; } @@ -468,7 +642,8 @@ static ssize_t bat_calib_write(struct device *dev, static ssize_t bat_test_power_read(struct device *dev, struct device_attribute *attr, char *buf) { - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); return sprintf(buf, "%d\n", di->fg_drv_mode); } @@ -479,7 +654,8 @@ static ssize_t bat_test_power_write(struct device *dev, { u8 val; int ret; - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); ret = kstrtou8(buf, 0, &val); if (ret < 0) @@ -493,22 +669,13 @@ static ssize_t bat_test_power_write(struct device *dev, return count; } - -static ssize_t bat_state_read(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct battery_info *di = g_battery; - - return sprintf(buf, "dsoc = %d, rsoc = %d\n", - di->real_soc, di->temp_soc); -} - static ssize_t bat_fcc_read(struct device *dev, struct device_attribute *attr, char *buf) { - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); - return sprintf(buf, "%d", di->fcc); + return sprintf(buf, "%d\n", di->fcc); } static ssize_t bat_fcc_write(struct device *dev, @@ -517,7 +684,8 @@ static ssize_t bat_fcc_write(struct device *dev, { u16 val; int ret; - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); ret = kstrtou16(buf, 0, &val); if (ret < 0) @@ -528,316 +696,198 @@ static ssize_t bat_fcc_write(struct device *dev, return count; } - -static ssize_t bat_soc_read(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t bat_dsoc_read(struct device *dev, + struct device_attribute *attr, char *buf) { - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); - return sprintf(buf, "%d", di->real_soc); + return sprintf(buf, "%d\n", di->dsoc); } -static ssize_t bat_soc_write(struct device *dev, - struct device_attribute *attr, +static ssize_t bat_dsoc_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { u8 val; int ret; - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); ret = kstrtou8(buf, 0, &val); if (ret < 0) return ret; - di->real_soc = val; + di->dsoc = val; return count; } -static ssize_t bat_temp_soc_read(struct device *dev, - struct device_attribute *attr, char *buf) + +static ssize_t bat_rsoc_read(struct device *dev, + struct device_attribute *attr, char *buf) { - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); - return sprintf(buf, "%d", di->temp_soc); + return sprintf(buf, "%d\n", di->rsoc); } -static ssize_t bat_temp_soc_write(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t bat_rsoc_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { u8 val; int ret; u32 capacity; - struct battery_info *di = g_battery; + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); ret = kstrtou8(buf, 0, &val); if (ret < 0) return ret; - capacity = di->fcc*val/100; - _capacity_init(di, capacity); + capacity = di->fcc * val / 100; + rk81x_bat_capacity_init(di, capacity); + rk81x_bat_capacity_init_post(di); return count; } -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); -} - -static ssize_t bat_avr_current_read(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct battery_info *di = g_battery; - - return sprintf(buf, "%d", di->current_avg); -} - static ssize_t bat_remain_cap_read(struct device *dev, struct device_attribute *attr, char *buf) { - struct battery_info *di = g_battery; - - return sprintf(buf, "%d", di->remain_capacity); -} - -/* - * interface for debug: debug info switch - */ -static ssize_t bat_debug_write(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - u8 val; - int ret; - - ret = kstrtou8(buf, 0, &val); - if (ret < 0) - return ret; - - dbg_enable = val; - - return count; -} - -static ssize_t bat_regs_read(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u32 i; - u32 start_offset = 0x0; - u32 end_offset = 0xf2; - struct battery_info *di = g_battery; - u8 val; - char *str = buf; - - str += sprintf(str, "start from add=0x%x, offset=0x%x\n", - start_offset, end_offset); + struct power_supply *psy_bat = dev_get_drvdata(dev); + struct rk81x_battery *di = to_device_info(psy_bat); - for (i = start_offset; i <= end_offset; ) { - battery_read(di->rk818, i, &val, 1); - str += sprintf(str, "0x%x=0x%x", i, val); - - if (i % 4 == 0) { - str += sprintf(str, "\n"); - } else { - if (i != end_offset) - str += sprintf(str, " "); - else - str += sprintf(str, "\n"); - } - i++; - } - return (str - buf); + return sprintf(buf, "%d\n", di->remain_capacity); } - static struct device_attribute rk818_bat_attr[] = { __ATTR(fcc, 0664, bat_fcc_read, bat_fcc_write), - __ATTR(soc, 0664, bat_soc_read, bat_soc_write), - __ATTR(temp_soc, 0664, bat_temp_soc_read, bat_temp_soc_write), - __ATTR(voltage, 0664, bat_voltage_read, NULL), - __ATTR(avr_current, 0664, bat_avr_current_read, NULL), + __ATTR(dsoc, 0664, bat_dsoc_read, bat_dsoc_write), + __ATTR(rsoc, 0664, bat_rsoc_read, bat_rsoc_write), __ATTR(remain_capacity, 0664, bat_remain_cap_read, NULL), - __ATTR(debug, 0664, NULL, bat_debug_write), - __ATTR(regs, 0664, bat_regs_read, NULL), - __ATTR(state, 0664, bat_state_read, NULL), __ATTR(test_power, 0664, bat_test_power_read, bat_test_power_write), __ATTR(calib, 0664, bat_calib_read, bat_calib_write), }; - #endif -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) -{ - struct battery_info *data = g_battery; - - 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); -} - -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 create_sysfs_interfaces(struct device *dev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(rkbatt_attrs); i++) { - if (device_create_file(dev, rkbatt_attrs + i)) - goto error; - } - - return 0; - -error: - for (; i >= 0; i--) - device_remove_file(dev, rkbatt_attrs + i); - - dev_err(dev, "%s:Unable to create sysfs interface\n", __func__); - return -1; -} - -static int _gauge_enable(struct battery_info *di) +static int rk81x_bat_gauge_enable(struct rk81x_battery *di) { int ret; u8 buf; - - ret = battery_read(di->rk818, TS_CTRL_REG, &buf, 1); + ret = rk81x_bat_read(di, TS_CTRL_REG, &buf, 1); if (ret < 0) { dev_err(di->dev, "error reading TS_CTRL_REG"); return ret; } - 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); - return 0; - } - DBG("%s, %d\n", __func__, buf); + buf |= GG_EN; + rk81x_bat_write(di, TS_CTRL_REG, &buf, 1); + return 0; } -static void save_level(struct battery_info *di, u8 save_soc) +static void rk81x_bat_save_level(struct rk81x_battery *di, u8 save_soc) { - u8 soc; - - soc = save_soc; - battery_write(di->rk818, UPDAT_LEVE_REG, &soc, 1); + rk81x_bat_write(di, UPDAT_LEVE_REG, &save_soc, 1); } -static u8 get_level(struct battery_info *di) + +static u8 rk81x_bat_get_level(struct rk81x_battery *di) { u8 soc; - battery_read(di->rk818, UPDAT_LEVE_REG, &soc, 1); + rk81x_bat_read(di, UPDAT_LEVE_REG, &soc, 1); + return soc; } -static int _get_vcalib0(struct battery_info *di) +static int rk81x_bat_get_vcalib0(struct rk81x_battery *di) { int ret; int temp = 0; u8 buf; - ret = battery_read(di->rk818, VCALIB0_REGL, &buf, 1); + ret = rk81x_bat_read(di, VCALIB0_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818, VCALIB0_REGH, &buf, 1); - temp |= buf<<8; + ret = rk81x_bat_read(di, VCALIB0_REGH, &buf, 1); + temp |= buf << 8; DBG("%s voltage0 offset vale is %d\n", __func__, temp); return temp; } -static int _get_vcalib1(struct battery_info *di) +static int rk81x_bat_get_vcalib1(struct rk81x_battery *di) { int ret; int temp = 0; u8 buf; - ret = battery_read(di->rk818, VCALIB1_REGL, &buf, 1); + ret = rk81x_bat_read(di, VCALIB1_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818, VCALIB1_REGH, &buf, 1); - temp |= buf<<8; + ret = rk81x_bat_read(di, VCALIB1_REGH, &buf, 1); + temp |= buf << 8; DBG("%s voltage1 offset vale is %d\n", __func__, temp); return temp; } -static int _get_ioffset(struct battery_info *di) +static int rk81x_bat_get_ioffset(struct rk81x_battery *di) { int ret; int temp = 0; u8 buf; - ret = battery_read(di->rk818, IOFFSET_REGL, &buf, 1); + ret = rk81x_bat_read(di, IOFFSET_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818, IOFFSET_REGH, &buf, 1); - temp |= buf<<8; + ret = rk81x_bat_read(di, IOFFSET_REGH, &buf, 1); + temp |= buf << 8; return temp; } -static uint16_t _get_cal_offset(struct battery_info *di) +static uint16_t rk81x_bat_get_cal_offset(struct rk81x_battery *di) { int ret; uint16_t temp = 0; u8 buf; - ret = battery_read(di->rk818, CAL_OFFSET_REGL, &buf, 1); + ret = rk81x_bat_read(di, CAL_OFFSET_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818, CAL_OFFSET_REGH, &buf, 1); - temp |= buf<<8; + ret = rk81x_bat_read(di, CAL_OFFSET_REGH, &buf, 1); + temp |= buf << 8; return temp; } -static int _set_cal_offset(struct battery_info *di, u32 value) + +static int rk81x_bat_set_cal_offset(struct rk81x_battery *di, u32 value) { int ret; u8 buf; - 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); + buf = value & 0xff; + ret = rk81x_bat_write(di, CAL_OFFSET_REGL, &buf, 1); + buf = (value >> 8) & 0xff; + ret = rk81x_bat_write(di, CAL_OFFSET_REGH, &buf, 1); return 0; } -static void _get_voltage_offset_value(struct battery_info *di) + +static void rk81x_bat_get_vol_offset(struct rk81x_battery *di) { int vcalib0, vcalib1; - vcalib0 = _get_vcalib0(di); - vcalib1 = _get_vcalib1(di); + vcalib0 = rk81x_bat_get_vcalib0(di); + vcalib1 = rk81x_bat_get_vcalib1(di); - di->voltage_k = (4200 - 3000)*1000/div((vcalib1 - vcalib0)); - di->voltage_b = 4200 - (di->voltage_k*vcalib1)/1000; + di->voltage_k = (4200 - 3000) * 1000 / div((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); } -static uint16_t _get_OCV_voltage(struct battery_info *di) + +static uint16_t rk81x_bat_get_ocv_vol(struct rk81x_battery *di) { int ret; u8 buf; @@ -847,10 +897,10 @@ static uint16_t _get_OCV_voltage(struct battery_info *di) int val[3]; for (i = 0; i < 3; i++) { - ret = battery_read(di->rk818, BAT_OCV_REGL, &buf, 1); + ret = rk81x_bat_read(di, BAT_OCV_REGL, &buf, 1); val[i] = buf; - ret = battery_read(di->rk818, BAT_OCV_REGH, &buf, 1); - val[i] |= buf<<8; + ret = rk81x_bat_read(di, BAT_OCV_REGH, &buf, 1); + val[i] |= buf << 8; if (ret < 0) { dev_err(di->dev, "error read BAT_OCV_REGH"); @@ -863,12 +913,12 @@ static uint16_t _get_OCV_voltage(struct battery_info *di) else temp = val[2]; - voltage_now = di->voltage_k*temp/1000 + di->voltage_b; + voltage_now = di->voltage_k * temp / 1000 + di->voltage_b; return voltage_now; } -static int _get_battery_voltage(struct battery_info *di) +static int rk81x_bat_get_vol(struct rk81x_battery *di) { int ret; int voltage_now = 0; @@ -878,10 +928,10 @@ static int _get_battery_voltage(struct battery_info *di) int i; for (i = 0; i < 3; i++) { - ret = battery_read(di->rk818, BAT_VOL_REGL, &buf, 1); + ret = rk81x_bat_read(di, BAT_VOL_REGL, &buf, 1); val[i] = buf; - ret = battery_read(di->rk818, BAT_VOL_REGH, &buf, 1); - val[i] |= buf<<8; + ret = rk81x_bat_read(di, BAT_VOL_REGH, &buf, 1); + val[i] |= buf << 8; if (ret < 0) { dev_err(di->dev, "error read BAT_VOL_REGH"); @@ -894,259 +944,248 @@ static int _get_battery_voltage(struct battery_info *di) else temp = val[2]; - voltage_now = di->voltage_k*temp/1000 + di->voltage_b; + voltage_now = di->voltage_k * temp / 1000 + di->voltage_b; return voltage_now; } -/* 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 bool is_rk81x_bat_relax_mode(struct rk81x_battery *di) { - u32 *ocv_table; - int ocv_size; - u32 tmp; - int ocv_soc; + int ret; + u8 status; - 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); - ocv_soc = ab_div_c(tmp, MAX_PERCENTAGE, INTERPOLATE_MAX); - di->temp_nac = ab_div_c(tmp, di->fcc, INTERPOLATE_MAX); + ret = rk81x_bat_read(di, GGSTS, &status, 1); - return ocv_soc; + if ((!(status & RELAX_VOL1_UPD)) || (!(status & RELAX_VOL2_UPD))) + return false; + else + return true; } -static uint16_t _get_relax_vol1(struct battery_info *di) +static uint16_t rk81x_bat_get_relax_vol1(struct rk81x_battery *di) { int ret; u8 buf; uint16_t temp = 0, voltage_now; - ret = battery_read(di->rk818, RELAX_VOL1_REGL, &buf, 1); + ret = rk81x_bat_read(di, RELAX_VOL1_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818, RELAX_VOL1_REGH, &buf, 1); - temp |= (buf<<8); + ret = rk81x_bat_read(di, RELAX_VOL1_REGH, &buf, 1); + temp |= (buf << 8); - voltage_now = di->voltage_k*temp/1000 + di->voltage_b; + voltage_now = di->voltage_k * temp / 1000 + di->voltage_b; return voltage_now; } -static uint16_t _get_relax_vol2(struct battery_info *di) +static uint16_t rk81x_bat_get_relax_vol2(struct rk81x_battery *di) { int ret; - uint16_t temp = 0, voltage_now; u8 buf; + uint16_t temp = 0, voltage_now; - ret = battery_read(di->rk818, RELAX_VOL2_REGL, &buf, 1); + ret = rk81x_bat_read(di, RELAX_VOL2_REGL, &buf, 1); temp = buf; - ret = battery_read(di->rk818, RELAX_VOL2_REGH, &buf, 1); - temp |= (buf<<8); + ret = rk81x_bat_read(di, RELAX_VOL2_REGH, &buf, 1); + temp |= (buf << 8); - voltage_now = di->voltage_k*temp/1000 + di->voltage_b; + voltage_now = di->voltage_k * temp / 1000 + di->voltage_b; return voltage_now; } -static int _get_raw_adc_current(struct battery_info *di) +static uint16_t rk81x_bat_get_relax_vol(struct rk81x_battery *di) +{ + int ret; + u8 status; + uint16_t relax_vol1, relax_vol2; + u8 ggcon; + + ret = rk81x_bat_read(di, GGSTS, &status, 1); + ret = rk81x_bat_read(di, GGCON, &ggcon, 1); + + relax_vol1 = rk81x_bat_get_relax_vol1(di); + relax_vol2 = rk81x_bat_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_rk81x_bat_relax_mode(di)) + return relax_vol1 > relax_vol2 ? relax_vol1 : relax_vol2; + else + return 0; +} + +/* OCV Lookup table + * Open Circuit Voltage (OCV) correction routine. This function estimates SOC, + * based on the voltage. + */ +static int rk81x_bat_vol_to_capacity(struct rk81x_battery *di, int voltage) +{ + u32 *ocv_table; + int ocv_size; + u32 tmp; + int ocv_soc; + + ocv_table = di->pdata->battery_ocv; + ocv_size = di->pdata->ocv_size; + tmp = interpolate(voltage, ocv_table, ocv_size); + ocv_soc = ab_div_c(tmp, MAX_PERCENTAGE, INTERPOLATE_MAX); + di->temp_nac = ab_div_c(tmp, di->fcc, INTERPOLATE_MAX); + + return ocv_soc; +} + +static int rk81x_bat_get_raw_adc_current(struct rk81x_battery *di) { u8 buf; int ret; - int current_now; + int val; - ret = battery_read(di->rk818, BAT_CUR_AVG_REGL, &buf, 1); + ret = rk81x_bat_read(di, 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); + val = buf; + ret = rk81x_bat_read(di, 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); + val |= (buf << 8); if (ret < 0) { dev_err(di->dev, "error reading BAT_CUR_AVG_REGH"); return ret; } - return current_now; + if (val > 2047) + val -= 4096; + + return val; } -static void reset_zero_var(struct battery_info *di) -{ - di->update_k = 0; - di->q_err = 0; - di->voltage_old = 0; - di->display_soc = 0; -} - -static void ioffset_sample_time(struct battery_info *di, int time) +static void rk81x_bat_ioffset_sample_set(struct rk81x_battery *di, int time) { u8 ggcon; - battery_read(di->rk818, GGCON, &ggcon, 1); + rk81x_bat_read(di, GGCON, &ggcon, 1); ggcon &= ~(0x30); /*clear <5:4>*/ ggcon |= time; - battery_write(di->rk818, GGCON, &ggcon, 1); -} - -static void update_cal_offset(struct battery_info *di) -{ - int mod = di->queue_work_cnt % TIME_10MIN_SEC; - u8 pcb_offset; - - battery_read(di->rk818, PCB_IOFFSET_REG, &pcb_offset, 1); - 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, _get_ioffset(di)+pcb_offset); - DBG("<%s>. 10min update cal_offset = %d", - __func__, di->pcb_ioffset+_get_ioffset(di)); - } + rk81x_bat_write(di, GGCON, &ggcon, 1); } /* * when charger finish signal comes, we need calibrate the current, make it * close to 0. */ -static void zero_current_calib(struct battery_info *di) +static bool rk81x_bat_zero_current_calib(struct rk81x_battery *di) { int adc_value; uint16_t C0; uint16_t C1; int ioffset; - u8 pcb_offset; + u8 pcb_offset = 0; u8 retry = 0; + bool ret; - if ((di->charge_status == CHARGE_FINISH) && - (abs32_int(di->current_avg) > 4)) { + if ((di->chrg_status == CHARGE_FINISH) && + (BASE_TO_MIN(di->power_on_base) >= 3) && + (abs(di->current_avg) > 4)) { for (retry = 0; retry < 5; retry++) { - adc_value = _get_raw_adc_current(di); - if (adc_value > 2047) - adc_value -= 4096; + adc_value = rk81x_bat_get_raw_adc_current(di); + if (!rk81x_chrg_online(di) || abs(adc_value) > 30) { + dev_dbg(di->dev, "charger plugout\n"); + ret = true; + break; + } DBG("<%s>. adc_value = %d\n", __func__, adc_value); - C0 = _get_cal_offset(di); + C0 = rk81x_bat_get_cal_offset(di); C1 = adc_value + C0; DBG("<%s>. C0(cal_offset) = %d, C1 = %d\n", __func__, C0, C1); - _set_cal_offset(di, C1); + rk81x_bat_set_cal_offset(di, C1); DBG("<%s>. new cal_offset = %d\n", - __func__, _get_cal_offset(di)); - msleep(2000); - - adc_value = _get_raw_adc_current(di); + __func__, rk81x_bat_get_cal_offset(di)); + msleep(3000); + adc_value = rk81x_bat_get_raw_adc_current(di); DBG("<%s>. adc_value = %d\n", __func__, adc_value); - if (adc_value < 4) { - if (_get_cal_offset(di) < 0x7ff) - _set_cal_offset(di, di->current_offset+ - 42); - else { - ioffset = _get_ioffset(di); + if (abs(adc_value) < 4) { + if (rk81x_bat_get_cal_offset(di) < 0x7ff) { + ioffset = rk81x_bat_get_ioffset(di); + rk81x_bat_set_cal_offset(di, + ioffset + 42); + } else { + ioffset = rk81x_bat_get_ioffset(di); pcb_offset = C1 - ioffset; di->pcb_ioffset = pcb_offset; di->pcb_ioffset_updated = true; - battery_write(di->rk818, - PCB_IOFFSET_REG, - &pcb_offset, 1); + rk81x_bat_write(di, + PCB_IOFFSET_REG, + &pcb_offset, 1); } DBG("<%s>. update the cal_offset, C1 = %d\n" "i_offset = %d, pcb_offset = %d\n", __func__, C1, ioffset, pcb_offset); + ret = false; break; } else { - di->pcb_ioffset_updated = false; + dev_warn(di->dev, "ioffset cal failed\n"); + rk81x_bat_set_cal_offset(di, C0); } + + 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; + return ret; } -static void _set_relax_thres(struct battery_info *di) +static void rk81x_bat_set_relax_thres(struct rk81x_battery *di) { u8 buf; 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; + enter_thres = (cell->config->ocv->sleep_enter_current) * 1000 / 1506; + exit_thres = (cell->config->ocv->sleep_exit_current) * 1000 / 1506; DBG("<%s>. sleep_enter_current = %d, sleep_exit_current = %d\n", __func__, cell->config->ocv->sleep_enter_current, cell->config->ocv->sleep_exit_current); - 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); + buf = enter_thres & 0xff; + rk81x_bat_write(di, RELAX_ENTRY_THRES_REGL, &buf, 1); + buf = (enter_thres >> 8) & 0xff; + rk81x_bat_write(di, RELAX_ENTRY_THRES_REGH, &buf, 1); - 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); + buf = exit_thres & 0xff; + rk81x_bat_write(di, RELAX_EXIT_THRES_REGL, &buf, 1); + buf = (exit_thres >> 8) & 0xff; + rk81x_bat_write(di, RELAX_EXIT_THRES_REGH, &buf, 1); /* set sample time */ - battery_read(di->rk818, GGCON, &buf, 1); - buf &= ~(3<<2);/*8min*/ + rk81x_bat_read(di, GGCON, &buf, 1); + buf &= ~(3 << 2);/*8min*/ buf &= ~0x01; /* clear bat_res calc*/ - battery_write(di->rk818, GGCON, &buf, 1); + rk81x_bat_write(di, GGCON, &buf, 1); } -static void restart_relax(struct battery_info *di) +static void rk81x_bat_restart_relax(struct rk81x_battery *di) { - u8 ggcon;/* chrg_ctrl_reg2;*/ + u8 ggcon; u8 ggsts; - battery_read(di->rk818, GGCON, &ggcon, 1); + rk81x_bat_read(di, GGCON, &ggcon, 1); ggcon &= ~0x0c; - battery_write(di->rk818, GGCON, &ggcon, 1); + rk81x_bat_write(di, GGCON, &ggcon, 1); - battery_read(di->rk818, GGSTS, &ggsts, 1); + rk81x_bat_read(di, GGSTS, &ggsts, 1); ggsts &= ~0x0c; - battery_write(di->rk818, GGSTS, &ggsts, 1); + rk81x_bat_write(di, GGSTS, &ggsts, 1); } -static int _get_average_current(struct battery_info *di) +static int rk81x_bat_get_avg_current(struct rk81x_battery *di) { u8 buf; int ret; @@ -1156,14 +1195,14 @@ static int _get_average_current(struct battery_info *di) int i; for (i = 0; i < 3; i++) { - ret = battery_read(di->rk818, BAT_CUR_AVG_REGL, &buf, 1); + ret = rk81x_bat_read(di, BAT_CUR_AVG_REGL, &buf, 1); if (ret < 0) { dev_err(di->dev, "error read BAT_CUR_AVG_REGL"); return ret; } val[i] = buf; - ret = battery_read(di->rk818, BAT_CUR_AVG_REGH, &buf, 1); + ret = rk81x_bat_read(di, BAT_CUR_AVG_REGH, &buf, 1); if (ret < 0) { dev_err(di->dev, "error read BAT_CUR_AVG_REGH"); return ret; @@ -1179,47 +1218,99 @@ static int _get_average_current(struct battery_info *di) if (current_now & 0x800) current_now -= 4096; - temp = current_now*1506/1000;/*1000*90/14/4096*500/521;*/ + temp = current_now * 1506 / 1000;/*1000*90/14/4096*500/521;*/ return temp; } -static int is_rk81x_bat_exist(struct battery_info *di) +static void rk81x_bat_set_power_supply_state(struct rk81x_battery *di, + enum charger_type charger_type) +{ + di->usb_online = OFFLINE; + di->ac_online = OFFLINE; + + switch (charger_type) { + case NO_CHARGER: + di->psy_status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case USB_CHARGER: + di->usb_online = ONLINE; + di->psy_status = POWER_SUPPLY_STATUS_CHARGING; + break; + case DC_CHARGER:/*treat dc as ac*/ + case AC_CHARGER: + di->ac_online = ONLINE; + di->psy_status = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + di->psy_status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + if (di->wq) + queue_delayed_work(di->wq, &di->chrg_term_mode_switch_work, + msecs_to_jiffies(1000)); +} + +/* high load: current < 0 with charger in. + * System will not shutdown while dsoc=0% with charging state(ac_online), + * which will cause over discharge, so oppose status before report states. + */ +static void rk81x_bat_lowpwr_check(struct rk81x_battery *di) +{ + static u64 time; + int pwr_off_thresd = di->pdata->power_off_thresd - 50; + + if (di->current_avg < 0 && di->voltage < pwr_off_thresd) { + if (!time) + time = get_runtime_sec(); + + if (BASE_TO_SEC(time) > (MINUTE)) { + rk81x_bat_set_power_supply_state(di, NO_CHARGER); + dev_info(di->dev, "low power....\n"); + } + } else { + time = 0; + } +} + +static int is_rk81x_bat_exist(struct rk81x_battery *di) { u8 buf; - battery_read(di->rk818, SUP_STS_REG, &buf, 1); + rk81x_bat_read(di, SUP_STS_REG, &buf, 1); + return (buf & 0x80) ? 1 : 0; } -static bool _is_first_poweron(struct battery_info *di) +static bool is_rk81x_bat_first_poweron(struct rk81x_battery *di) { u8 buf; u8 temp; - battery_read(di->rk818, GGSTS, &buf, 1); + rk81x_bat_read(di, 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); + rk81x_bat_write(di, GGSTS, &buf, 1); + rk81x_bat_read(di, GGSTS, &temp, 1); + } while (temp & BAT_CON); return true; } return false; } -static void flatzone_voltage_init(struct battery_info *di) + +static void rk81x_bat_flatzone_vol_init(struct rk81x_battery *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; + ocv_table = di->pdata->battery_ocv; + ocv_size = di->pdata->ocv_size; for (j = 0; j < 21; j++) temp_table[j] = 0; @@ -1230,15 +1321,14 @@ static void flatzone_voltage_init(struct battery_info *di) temp_table[j++] = i; } - temp_table[j] = temp_table[j-1]+1; + 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; + j = i + 1; } i = temp_table[j]; @@ -1248,26 +1338,25 @@ static void flatzone_voltage_init(struct battery_info *di) di->enter_flatzone, di->exit_flatzone); } -static void power_on_save(struct battery_info *di, int ocv_voltage) +static void rk81x_bat_power_on_save(struct rk81x_battery *di, int ocv_voltage) { u8 ocv_valid, first_pwron; - u8 save_soc; + u8 soc_level; u8 ocv_soc; /*buf==1: OCV_VOL is valid*/ - ocv_valid = rk81x_read_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); - first_pwron = rk81x_read_bit(di, MISC_MARK_REG, FIRST_PWRON_SHIFT); + ocv_valid = rk81x_bat_read_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); + first_pwron = rk81x_bat_read_bit(di, MISC_MARK_REG, FIRST_PWRON_SHIFT); DBG("readbit: ocv_valid=%d, first_pwron=%d\n", ocv_valid, first_pwron); if (first_pwron == 1 || ocv_valid == 1) { DBG("<%s> enter.\n", __func__); - ocv_soc = _voltage_to_capacity(di, ocv_voltage); - if (ocv_soc < 20) { + ocv_soc = rk81x_bat_vol_to_capacity(di, ocv_voltage); + if ((ocv_soc < 20) && (ocv_voltage > 2750)) { di->dod0_voltage = ocv_voltage; - di->dod0_capacity = di->nac; - di->dod0_status = 1; + di->dod0_capacity = di->temp_nac; + di->adjust_cap = 0; di->dod0 = ocv_soc; - di->dod0_level = 80; if (ocv_soc <= 0) di->dod0_level = 100; @@ -1275,22 +1364,35 @@ static void power_on_save(struct battery_info *di, int ocv_voltage) di->dod0_level = 95; else if (ocv_soc < 10) di->dod0_level = 90; + else + di->dod0_level = 80; /* 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>: dod0_vol:%d, dod0_cap:%d, dod0:%d, level:%d", - __func__, di->dod0_voltage, di->dod0_capacity, - ocv_soc, save_soc); + soc_level = rk81x_bat_get_level(di); + if (soc_level > di->dod0_level) { + di->dod0_status = 0; + soc_level -= 5; + if (soc_level <= 80) + soc_level = 80; + rk81x_bat_save_level(di, soc_level); + } else { + di->dod0_status = 1; + /*time start*/ + di->fcc_update_sec = get_runtime_sec(); + } + + dev_info(di->dev, "dod0_vol:%d, dod0_cap:%d\n" + "dod0:%d, soc_level:%d: dod0_status:%d\n" + "dod0_level:%d", + di->dod0_voltage, di->dod0_capacity, + ocv_soc, soc_level, di->dod0_status, + di->dod0_level); } } } - -static int _get_soc(struct battery_info *di) +static int rk81x_bat_get_rsoc(struct rk81x_battery *di) { - return di->remain_capacity * 100 / div(di->fcc); + return (di->remain_capacity + di->fcc / 200) * 100 / div(di->fcc); } static enum power_supply_property rk_battery_props[] = { @@ -1302,55 +1404,46 @@ static enum power_supply_property rk_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY, }; -#define to_device_info(x) container_of((x), \ - struct battery_info, bat) - static int rk81x_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); + struct rk81x_battery *di = to_device_info(psy); switch (psp) { case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = di->current_avg*1000;/*uA*/ + val->intval = di->current_avg * 1000;/*uA*/ if (di->fg_drv_mode == TEST_POWER_MODE) - val->intval = TEST_CURRENT*1000; + val->intval = TEST_CURRENT * 1000; break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = di->voltage*1000;/*uV*/ + val->intval = di->voltage * 1000;/*uV*/ if (di->fg_drv_mode == TEST_POWER_MODE) - val->intval = TEST_VOLTAGE*1000; + val->intval = TEST_VOLTAGE * 1000; break; - case POWER_SUPPLY_PROP_PRESENT: val->intval = is_rk81x_bat_exist(di); if (di->fg_drv_mode == TEST_POWER_MODE) val->intval = TEST_PRESET; break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = di->real_soc; + val->intval = di->dsoc; if (di->fg_drv_mode == TEST_POWER_MODE) val->intval = TEST_SOC; DBG("<%s>, report dsoc: %d\n", __func__, val->intval); break; - case POWER_SUPPLY_PROP_HEALTH: val->intval = POWER_SUPPLY_HEALTH_GOOD; break; - case POWER_SUPPLY_PROP_STATUS: - val->intval = di->status; + val->intval = di->psy_status; if (di->fg_drv_mode == TEST_POWER_MODE) val->intval = TEST_STATUS; break; - default: return -EINVAL; } @@ -1358,33 +1451,30 @@ static int rk81x_battery_get_property(struct power_supply *psy, 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 rk81x_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); + struct rk81x_battery *di = to_ac_device_info(psy); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: + if (rk81x_chrg_online(di)) + rk81x_bat_lowpwr_check(di); val->intval = di->ac_online; /*discharging*/ if (di->fg_drv_mode == TEST_POWER_MODE) val->intval = TEST_AC_ONLINE; break; - default: ret = -EINVAL; break; @@ -1392,28 +1482,21 @@ static int rk81x_battery_ac_get_property(struct power_supply *psy, return ret; } -#define to_usb_device_info(x) container_of((x), \ - struct battery_info, usb) - static int rk81x_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); + struct rk81x_battery *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; - + if (rk81x_chrg_online(di)) + rk81x_bat_lowpwr_check(di); + val->intval = di->usb_online; if (di->fg_drv_mode == TEST_POWER_MODE) val->intval = TEST_USB_ONLINE; break; - default: ret = -EINVAL; break; @@ -1422,9 +1505,10 @@ static int rk81x_battery_usb_get_property(struct power_supply *psy, return ret; } - -static void battery_power_supply_init(struct battery_info *di) +static int rk81x_bat_power_supply_init(struct rk81x_battery *di) { + int ret; + di->bat.name = "BATTERY"; di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.properties = rk_battery_props; @@ -1442,26 +1526,20 @@ static void battery_power_supply_init(struct battery_info *di) di->usb.properties = rk_battery_usb_props; di->usb.num_properties = ARRAY_SIZE(rk_battery_usb_props); di->usb.get_property = rk81x_battery_usb_get_property; -} - -static int battery_power_supply_register(struct battery_info *di) -{ - int ret; - struct device *dev = di->dev; - ret = power_supply_register(dev, &di->bat); + ret = power_supply_register(di->dev, &di->bat); if (ret) { - dev_err(dev, "failed to register main battery\n"); + dev_err(di->dev, "failed to register main battery\n"); goto batt_failed; } - ret = power_supply_register(dev, &di->usb); + ret = power_supply_register(di->dev, &di->usb); if (ret) { - dev_err(dev, "failed to register usb power supply\n"); + dev_err(di->dev, "failed to register usb power supply\n"); goto usb_failed; } - ret = power_supply_register(dev, &di->ac); + ret = power_supply_register(di->dev, &di->ac); if (ret) { - dev_err(dev, "failed to register ac power supply\n"); + dev_err(di->dev, "failed to register ac power supply\n"); goto ac_failed; } @@ -1477,37 +1555,11 @@ batt_failed: return ret; } -static void _capacity_init(struct battery_info *di, u32 capacity) -{ - u8 buf; - u32 capacity_ma; - int delta_cap; - - delta_cap = capacity - di->remain_capacity; - di->adjust_cap += delta_cap; - - reset_zero_var(di); - - 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 void _save_remain_capacity(struct battery_info *di, u32 capacity) +static void rk81x_bat_save_remain_capacity(struct rk81x_battery *di, + int capacity) { u8 buf; - u32 capacity_ma; + static u32 capacity_ma; if (capacity >= di->qmax) capacity = di->qmax; @@ -1515,19 +1567,22 @@ static void _save_remain_capacity(struct battery_info *di, u32 capacity) if (capacity <= 0) capacity = 0; + if (capacity_ma == capacity) + return; + 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); + buf = (capacity_ma >> 24) & 0xff; + rk81x_bat_write(di, REMAIN_CAP_REG3, &buf, 1); + buf = (capacity_ma >> 16) & 0xff; + rk81x_bat_write(di, REMAIN_CAP_REG2, &buf, 1); + buf = (capacity_ma >> 8) & 0xff; + rk81x_bat_write(di, REMAIN_CAP_REG1, &buf, 1); + buf = (capacity_ma & 0xff) | 0x01; + rk81x_bat_write(di, REMAIN_CAP_REG0, &buf, 1); } -static int _get_remain_capacity(struct battery_info *di) +static int rk81x_bat_get_remain_capacity(struct rk81x_battery *di) { int ret; u8 buf; @@ -1536,13 +1591,13 @@ static int _get_remain_capacity(struct battery_info *di) int val[3]; for (i = 0; i < 3; i++) { - ret = battery_read(di->rk818, REMAIN_CAP_REG3, &buf, 1); + ret = rk81x_bat_read(di, REMAIN_CAP_REG3, &buf, 1); val[i] = buf << 24; - ret = battery_read(di->rk818, REMAIN_CAP_REG2, &buf, 1); + ret = rk81x_bat_read(di, REMAIN_CAP_REG2, &buf, 1); val[i] |= buf << 16; - ret = battery_read(di->rk818, REMAIN_CAP_REG1, &buf, 1); + ret = rk81x_bat_read(di, REMAIN_CAP_REG1, &buf, 1); val[i] |= buf << 8; - ret = battery_read(di->rk818, REMAIN_CAP_REG0, &buf, 1); + ret = rk81x_bat_read(di, REMAIN_CAP_REG0, &buf, 1); val[i] |= buf; } @@ -1554,49 +1609,53 @@ static int _get_remain_capacity(struct battery_info *di) return capacity; } - -static void _save_FCC_capacity(struct battery_info *di, u32 capacity) +static void rk81x_bat_save_fcc(struct rk81x_battery *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); + buf = (capacity_ma >> 24) & 0xff; + rk81x_bat_write(di, NEW_FCC_REG3, &buf, 1); + buf = (capacity_ma >> 16) & 0xff; + rk81x_bat_write(di, NEW_FCC_REG2, &buf, 1); + buf = (capacity_ma >> 8) & 0xff; + rk81x_bat_write(di, NEW_FCC_REG1, &buf, 1); + buf = (capacity_ma & 0xff) | 0x01; + rk81x_bat_write(di, NEW_FCC_REG0, &buf, 1); + + dev_info(di->dev, "update fcc : %d\n", capacity); } -static int _get_FCC_capacity(struct battery_info *di) +static int rk81x_bat_get_fcc(struct rk81x_battery *di) { - int ret; - int temp = 0; u8 buf; u32 capacity; - 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); + rk81x_bat_read(di, NEW_FCC_REG3, &buf, 1); + capacity = buf << 24; + rk81x_bat_read(di, NEW_FCC_REG2, &buf, 1); + capacity |= buf << 16; + rk81x_bat_read(di, NEW_FCC_REG1, &buf, 1); + capacity |= buf << 8; + rk81x_bat_read(di, NEW_FCC_REG0, &buf, 1); + capacity |= buf; + + if (capacity < MIN_FCC) { + dev_warn(di->dev, "invalid fcc(0x%x), use design capacity", + capacity); + capacity = di->design_capacity; + rk81x_bat_save_fcc(di, capacity); + } else if (capacity > di->qmax) { + dev_warn(di->dev, "invalid fcc(0x%x), use qmax", capacity); + capacity = di->qmax; + rk81x_bat_save_fcc(di, capacity); + } return capacity; } -static int _get_realtime_capacity(struct battery_info *di) +static int rk81x_bat_get_realtime_capacity(struct rk81x_battery *di) { int ret; int temp = 0; @@ -1606,13 +1665,13 @@ static int _get_realtime_capacity(struct battery_info *di) int val[3]; for (i = 0; i < 3; i++) { - ret = battery_read(di->rk818, GASCNT3, &buf, 1); + ret = rk81x_bat_read(di, GASCNT3, &buf, 1); val[i] = buf << 24; - ret = battery_read(di->rk818, GASCNT2, &buf, 1); + ret = rk81x_bat_read(di, GASCNT2, &buf, 1); val[i] |= buf << 16; - ret = battery_read(di->rk818, GASCNT1, &buf, 1); + ret = rk81x_bat_read(di, GASCNT1, &buf, 1); val[i] |= buf << 8; - ret = battery_read(di->rk818, GASCNT0, &buf, 1); + ret = rk81x_bat_read(di, GASCNT0, &buf, 1); val[i] |= buf; } if (val[0] == val[1]) @@ -1620,35 +1679,69 @@ static int _get_realtime_capacity(struct battery_info *di) else temp = val[2]; - capacity = temp/2390;/* 4096*900/14/36*500/521; */ + capacity = temp / 2390;/* 4096*900/14/36*500/521; */ return capacity; } -static int _copy_soc(struct battery_info *di, u8 save_soc) +static int rk81x_bat_save_dsoc(struct rk81x_battery *di, u8 save_soc) { - u8 soc; + static u8 last_soc; + + if (last_soc != save_soc) { + rk81x_bat_write(di, SOC_REG, &save_soc, 1); + last_soc = save_soc; + } - soc = save_soc; - battery_write(di->rk818, SOC_REG, &soc, 1); return 0; } -static int copy_reboot_cnt(struct battery_info *di, u8 save_cnt) +static int rk81x_bat_save_reboot_cnt(struct rk81x_battery *di, u8 save_cnt) { u8 cnt; cnt = save_cnt; - battery_write(di->rk818, REBOOT_CNT_REG, &cnt, 1); + rk81x_bat_write(di, REBOOT_CNT_REG, &cnt, 1); return 0; } -static bool support_uboot_charge(void) +static u8 rk81x_bat_support_loader_chrg(struct rk81x_battery *di) +{ + u8 ret; + + ret = rk81x_bat_read_bit(di, MISC_MARK_REG, LOADER_CHRG_SHIFT); + rk81x_bat_clr_bit(di, MISC_MARK_REG, LOADER_CHRG_SHIFT); + return ret; +} + +static void rk81x_bat_set_current(struct rk81x_battery *di, int charge_current) { - return support_uboot_chrg ? true : false; + u8 usb_ctrl_reg; + + rk81x_bat_read(di, 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); + rk81x_bat_write(di, USB_CTRL_REG, &usb_ctrl_reg, 1); } +static void rk81x_bat_set_chrg_current(struct rk81x_battery *di, + enum charger_type charger_type) +{ + switch (charger_type) { + case NO_CHARGER: + case USB_CHARGER: + rk81x_bat_set_current(di, ILIM_450MA); + break; + case AC_CHARGER: + case DC_CHARGER: + rk81x_bat_set_current(di, di->chrg_i_lmt); + break; + default: + rk81x_bat_set_current(di, ILIM_450MA); + } +} +#if defined(CONFIG_ARCH_ROCKCHIP) /* * There are three ways to detect dc_adp: * 1. hardware only support dc_adp: by reg VB_MOD_REG of rk818, @@ -1657,16 +1750,16 @@ static bool support_uboot_charge(void) * 3. support usb_adp and dc_adp: by VB_MOD_REG and usb interface. * case that: gpio invalid or not define. */ -static enum charger_type_t rk81x_get_dc_state(struct battery_info *di) +static enum charger_type rk81x_bat_get_dc_state(struct rk81x_battery *di) { - enum charger_type_t charger_type; + enum charger_type charger_type; u8 buf; int ret; - battery_read(di->rk818, VB_MOD_REG, &buf, 1); + rk81x_bat_read(di, VB_MOD_REG, &buf, 1); /*only HW_ADP_TYPE_DC: det by rk818 is easily and will be successful*/ - if (!rk81x_support_adp_type(HW_ADP_TYPE_USB)) { + if (!rk81x_bat_support_adp_type(HW_ADP_TYPE_USB)) { if ((buf & PLUG_IN_STS) != 0) charger_type = DC_CHARGER; else @@ -1675,7 +1768,6 @@ static enum charger_type_t rk81x_get_dc_state(struct battery_info *di) return charger_type; } -#if 1 /*det by gpio level*/ if (gpio_is_valid(di->dc_det_pin)) { ret = gpio_request(di->dc_det_pin, "rk818_dc_det"); @@ -1696,10 +1788,9 @@ static enum charger_type_t rk81x_get_dc_state(struct battery_info *di) DBG("**********rk818 dc_det_pin=%d\n", ret); return charger_type; - } -#endif + /*HW_ADP_TYPE_DUAL: det by rk818 and usb*/ - else if (rk81x_support_adp_type(HW_ADP_TYPE_DUAL)) { + } else if (rk81x_bat_support_adp_type(HW_ADP_TYPE_DUAL)) { if ((buf & PLUG_IN_STS) != 0) { charger_type = dwc_otg_check_dpdm(); if (charger_type == 0) @@ -1712,9 +1803,9 @@ static enum charger_type_t rk81x_get_dc_state(struct battery_info *di) return charger_type; } -static enum charger_type_t rk81x_get_usbac_state(struct battery_info *di) +static enum charger_type rk81x_bat_get_usbac_state(struct rk81x_battery *di) { - enum charger_type_t charger_type; + enum charger_type charger_type; int usb_id, gadget_flag; usb_id = dwc_otg_check_dpdm(); @@ -1749,6 +1840,7 @@ static enum charger_type_t rk81x_get_usbac_state(struct battery_info *di) } } else { charger_type = USB_CHARGER; + di->check_count = 0; } } else { di->check_count = 0; @@ -1758,199 +1850,357 @@ static enum charger_type_t rk81x_get_usbac_state(struct battery_info *di) } /* - * it is first time for battery to be weld, init by ocv table + * when support HW_ADP_TYPE_DUAL, and at the moment that usb_adp + * and dc_adp are plugined in together, the dc_apt has high priority. + * so we check dc_apt first and return rigth away if it's found. */ -static void rsoc_first_poweron_init(struct battery_info *di) +static enum charger_type rk81x_bat_get_adp_type(struct rk81x_battery *di) { - _save_FCC_capacity(di, di->design_capacity); - di->fcc = _get_FCC_capacity(di); + u8 buf; + enum charger_type charger_type = NO_CHARGER; - di->temp_soc = _voltage_to_capacity(di, di->voltage_ocv); - di->real_soc = di->temp_soc; - di->nac = di->temp_nac; - di->first_on_cap = di->nac; + /*check by ic hardware: this check make check work safer*/ + rk81x_bat_read(di, VB_MOD_REG, &buf, 1); + if ((buf & PLUG_IN_STS) == 0) + return NO_CHARGER; - rk81x_set_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); - rk81x_set_bit(di, MISC_MARK_REG, FIRST_PWRON_SHIFT);/*save*/ - DBG("<%s>.this is first poweron: OCV-SOC:%d, OCV-CAP:%d, FCC:%d\n", - __func__, di->real_soc, di->nac, di->fcc); -} + /*check DC first*/ + if (rk81x_bat_support_adp_type(HW_ADP_TYPE_DC)) { + charger_type = rk81x_bat_get_dc_state(di); + if (charger_type == DC_CHARGER) + return charger_type; + } -/* - * it is not first time for battery to be weld, init by last record info - */ -static void rsoc_not_first_poweron_init(struct battery_info *di) -{ - u8 pwron_soc; - u8 init_soc; - u8 last_shtd_time; - u8 curr_shtd_time; - int remain_capacity; - int ocv_soc; - enum charger_type_t charger_type; + /*HW_ADP_TYPE_USB*/ + charger_type = rk81x_bat_get_usbac_state(di); - rk81x_clr_bit(di, MISC_MARK_REG, FIRST_PWRON_SHIFT); - battery_read(di->rk818, SOC_REG, &pwron_soc, 1); - init_soc = pwron_soc; - DBG("<%s> Not first pwron, SOC_REG = %d\n", __func__, pwron_soc); + return charger_type; +} - if (rk81x_support_adp_type(HW_ADP_TYPE_USB)) { - charger_type = rk81x_get_usbac_state(di); - if ((pwron_soc == 0) && (charger_type == USB_CHARGER)) { - init_soc = 1; - battery_write(di->rk818, SOC_REG, &init_soc, 1); - } - } +static void rk81x_bat_status_check(struct rk81x_battery *di) +{ + static enum charger_type old_charger_type = DUAL_CHARGER; + enum charger_type charger_type; - remain_capacity = _get_remain_capacity(di); - /* check if support uboot charge, - * if support, uboot charge driver should have done init work, - * so here we should skip init work - */ - if (support_uboot_charge()) - goto out; + charger_type = rk81x_bat_get_adp_type(di); + if (charger_type == old_charger_type) + return; + rk81x_bat_set_chrg_current(di, charger_type); + rk81x_bat_set_power_supply_state(di, charger_type); + old_charger_type = charger_type; +} +#endif - 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, charger_type); +#if defined(CONFIG_X86_INTEL_SOFIA) +static int rk81x_get_chrg_type_by_usb_phy(struct rk81x_battery *di, int ma) +{ + enum charger_type charger_type; - ocv_soc = _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); + if (ma > 500) + charger_type = AC_CHARGER; + else if (ma >= 100) + charger_type = USB_CHARGER; + else + charger_type = NO_CHARGER; - /* if plugin, make sure current shtd_time diff from last_shtd_time.*/ - if (last_shtd_time != curr_shtd_time) { - if (curr_shtd_time > 30) { - rk81x_set_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); + di->ma = ma; - remain_capacity = di->temp_nac; - di->first_on_cap = remain_capacity; - DBG("<%s>pwroff > 30 minute, remain_cap = %d\n", - __func__, remain_capacity); + dev_info(di->dev, "limit current:%d\n", ma); - } else if ((curr_shtd_time > 5) && - (abs32_int(ocv_soc - init_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; - DBG("<%s> pwroff > 3 minute, remain_cap = %d\n", - __func__, remain_capacity); - } - } else { - rk81x_clr_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); - } -out: - 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 charger_type; } -static u8 get_sys_pwroff_min(struct battery_info *di) +static void rk81x_battery_usb_notifier_delayed_work(struct work_struct *work) { - u8 curr_shtd_time, last_shtd_time; + struct rk81x_battery *di; + enum charger_type type; - 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); + di = container_of(work, struct rk81x_battery, usb_phy_delay_work.work); + type = rk81x_get_chrg_type_by_usb_phy(di, di->ma); - return (curr_shtd_time != last_shtd_time) ? curr_shtd_time : 0; + rk81x_bat_set_chrg_current(di, type); + power_supply_changed(&di->usb); } -static int _rsoc_init(struct battery_info *di) +static int rk81x_battery_usb_notifier(struct notifier_block *nb, + unsigned long event, void *data) { - u8 pwroff_min; - u8 calib_en;/*debug*/ + struct rk81x_battery *di; + struct power_supply_cable_props *cable_props; + enum charger_type type; - di->voltage = _get_battery_voltage(di); - di->voltage_ocv = _get_OCV_voltage(di); - pwroff_min = get_sys_pwroff_min(di); + di = container_of(nb, struct rk81x_battery, usb_nb); - DBG("OCV voltage=%d, voltage=%d, pwroff_min=%d\n", - di->voltage_ocv, di->voltage, pwroff_min); + if (!data) + return NOTIFY_BAD; - calib_en = rk81x_read_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); - DBG("readbit: calib_en=%d\n", calib_en); - if (_is_first_poweron(di) || - ((pwroff_min >= 30) && (calib_en == 1))) { - rsoc_first_poweron_init(di); - rk81x_clr_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + switch (event) { + case USB_EVENT_CHARGER: + cable_props = (struct power_supply_cable_props *)data; + type = rk81x_get_chrg_type_by_usb_phy(di, cable_props->ma); + rk81x_bat_set_power_supply_state(di, type); + queue_delayed_work(di->wq, &di->usb_phy_delay_work, + msecs_to_jiffies(50)); + break; - } else { - rsoc_not_first_poweron_init(di); + default: + break; } - return 0; + return NOTIFY_OK; } +#endif - -static u8 rk81x_get_charge_status(struct battery_info *di) +static int rk81x_battery_fb_notifier(struct notifier_block *nb, + unsigned long event, void *data) { - u8 status; - u8 ret = 0; + struct rk81x_battery *di; + struct fb_event *evdata = data; + int blank; - battery_read(di->rk818, SUP_STS_REG, &status, 1); - status &= (0x70); - switch (status) { - case CHARGE_OFF: - ret = CHARGE_OFF; - DBG(" CHARGE-OFF ...\n"); - break; + di = container_of(nb, struct rk81x_battery, fb_nb); - case DEAD_CHARGE: - ret = DEAD_CHARGE; - DBG(" DEAD CHARGE ...\n"); - break; + if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK) + return 0; - case TRICKLE_CHARGE: - ret = DEAD_CHARGE; - DBG(" TRICKLE CHARGE ...\n "); - break; + blank = *(int *)evdata->data; - case CC_OR_CV: - ret = CC_OR_CV; - DBG(" CC or CV ...\n"); - break; + if (di->fb_blank != blank) + di->fb_blank = blank; + else + return 0; - case CHARGE_FINISH: - ret = CHARGE_FINISH; - DBG(" CHARGE FINISH ...\n"); - break; + if (blank == FB_BLANK_UNBLANK) + di->early_resume = 1; + + return 0; +} + +static int rk81x_battery_register_fb_notify(struct rk81x_battery *di) +{ + memset(&di->fb_nb, 0, sizeof(di->fb_nb)); + di->fb_nb.notifier_call = rk81x_battery_fb_notifier; + + return fb_register_client(&di->fb_nb); +} + +/* + * it is first time for battery to be weld, init by ocv table + */ +static void rk81x_bat_first_pwron(struct rk81x_battery *di) +{ + rk81x_bat_save_fcc(di, di->design_capacity); + di->fcc = rk81x_bat_get_fcc(di); + + di->rsoc = rk81x_bat_vol_to_capacity(di, di->voltage_ocv); + di->dsoc = di->rsoc; + di->nac = di->temp_nac; + + rk81x_bat_set_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); + rk81x_bat_set_bit(di, MISC_MARK_REG, FIRST_PWRON_SHIFT);/*save*/ + DBG("<%s>.this is first poweron: OCV-SOC:%d, OCV-CAP:%d, FCC:%d\n", + __func__, di->dsoc, di->nac, di->fcc); +} + +static int rk81x_bat_get_calib_vol(struct rk81x_battery *di) +{ + int calib_vol; + int init_cur, diff; + int est_vol; + int relax_vol = di->relax_voltage; + int ocv_vol = di->voltage_ocv; + + init_cur = rk81x_bat_get_avg_current(di); + diff = (di->bat_res + di->pdata->chrg_diff_vol) * init_cur; + diff /= 1000; + est_vol = di->voltage - diff; + + if (di->loader_charged) { + calib_vol = est_vol; + return calib_vol; + } + + if (di->pwroff_min > 8) { + if (abs(relax_vol - ocv_vol) < 100) { + calib_vol = ocv_vol; + } else { + if (abs(relax_vol - est_vol) > abs(ocv_vol - est_vol)) + calib_vol = ocv_vol; + else + calib_vol = relax_vol; + } + } else if (di->pwroff_min > 2) { + calib_vol = ocv_vol; + } else { + calib_vol = -1; + } + + dev_info(di->dev, "c=%d, v=%d, relax:%d, ocv:=%d, est=%d, calib=%d\n", + init_cur, di->voltage, relax_vol, ocv_vol, est_vol, calib_vol); + + return calib_vol; +} + +/* + * it is not first time for battery to be weld, init by last record info + */ +static void rk81x_bat_not_first_pwron(struct rk81x_battery *di) +{ + u8 pwron_soc; + u8 init_soc; + int remain_capacity; + int ocv_soc; + int calib_vol, calib_soc, calib_capacity; + + rk81x_bat_clr_bit(di, MISC_MARK_REG, FIRST_PWRON_SHIFT); + rk81x_bat_read(di, SOC_REG, &pwron_soc, 1); + init_soc = pwron_soc; + remain_capacity = rk81x_bat_get_remain_capacity(di); + + /* check if support uboot charge, + * if support, uboot charge driver should have done init work, + * so here we should skip init work + */ + if (rk81x_bat_support_loader_chrg(di)) + goto out; + + calib_vol = rk81x_bat_get_calib_vol(di); + if (calib_vol > 0) { + calib_soc = rk81x_bat_vol_to_capacity(di, calib_vol); + calib_capacity = di->temp_nac; + + if (abs(calib_soc - init_soc) >= 70 || di->loader_charged) { + init_soc = calib_soc; + remain_capacity = calib_capacity; + } + dev_info(di->dev, "calib_vol %d, init soc %d, remain_cap %d\n", + calib_vol, init_soc, remain_capacity); + } + + ocv_soc = rk81x_bat_vol_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); + + if (di->pwroff_min > 0) { + if (di->pwroff_min > 30) { + rk81x_bat_set_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); + + remain_capacity = di->temp_nac; + DBG("<%s>pwroff > 30 minute, remain_cap = %d\n", + __func__, remain_capacity); + + } else if ((di->pwroff_min > 5) && + (abs(ocv_soc - init_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; + DBG("<%s> pwroff > 5 minute, remain_cap = %d\n", + __func__, remain_capacity); + } + } else { + rk81x_bat_clr_bit(di, MISC_MARK_REG, OCV_VALID_SHIFT); + } +out: + di->dsoc = init_soc; + di->nac = remain_capacity; + if (di->nac <= 0) + di->nac = 0; + DBG("<%s> init_soc = %d, init_capacity=%d\n", + __func__, di->dsoc, di->nac); +} + +static u8 rk81x_bat_get_pwroff_min(struct rk81x_battery *di) +{ + u8 curr_pwroff_min, last_pwroff_min; + + rk81x_bat_read(di, NON_ACT_TIMER_CNT_REG, + &curr_pwroff_min, 1); + rk81x_bat_read(di, NON_ACT_TIMER_CNT_REG_SAVE, + &last_pwroff_min, 1); + + rk81x_bat_write(di, NON_ACT_TIMER_CNT_REG_SAVE, + &curr_pwroff_min, 1); + + return (curr_pwroff_min != last_pwroff_min) ? curr_pwroff_min : 0; +} + +static int rk81x_bat_rsoc_init(struct rk81x_battery *di) +{ + u8 calib_en;/*debug*/ + + di->voltage = rk81x_bat_get_vol(di); + di->voltage_ocv = rk81x_bat_get_ocv_vol(di); + di->pwroff_min = rk81x_bat_get_pwroff_min(di); + di->relax_voltage = rk81x_bat_get_relax_vol(di); + di->current_avg = rk81x_bat_get_avg_current(di); + + dev_info(di->dev, "v=%d, ov=%d, rv=%d, c=%d, pwroff_min=%d\n", + di->voltage, di->voltage_ocv, di->relax_voltage, + di->current_avg, di->pwroff_min); + + calib_en = rk81x_bat_read_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + DBG("readbit: calib_en=%d\n", calib_en); + if (is_rk81x_bat_first_poweron(di) || + ((di->pwroff_min >= 30) && (calib_en == 1))) { + rk81x_bat_first_pwron(di); + rk81x_bat_clr_bit(di, MISC_MARK_REG, OCV_CALIB_SHIFT); + + } else { + rk81x_bat_not_first_pwron(di); + } + + return 0; +} + +static u8 rk81x_bat_get_chrg_status(struct rk81x_battery *di) +{ + u8 status; + u8 ret = 0; + rk81x_bat_read(di, SUP_STS_REG, &status, 1); + status &= (0x70); + switch (status) { + case CHARGE_OFF: + ret = CHARGE_OFF; + DBG(" CHARGE-OFF ...\n"); + break; + case DEAD_CHARGE: + ret = DEAD_CHARGE; + DBG(" DEAD CHARGE ...\n"); + break; + case TRICKLE_CHARGE: + ret = DEAD_CHARGE; + DBG(" TRICKLE CHARGE ...\n "); + break; + case CC_OR_CV: + ret = CC_OR_CV; + DBG(" CC or CV ...\n"); + break; + case CHARGE_FINISH: + ret = CHARGE_FINISH; + DBG(" CHARGE FINISH ...\n"); + break; case USB_OVER_VOL: ret = USB_OVER_VOL; DBG(" USB OVER VOL ...\n"); break; - case BAT_TMP_ERR: ret = BAT_TMP_ERR; DBG(" BAT TMP ERROR ...\n"); break; - case TIMER_ERR: ret = TIMER_ERR; DBG(" TIMER ERROR ...\n"); break; - case USB_EXIST: ret = USB_EXIST; DBG(" USB EXIST ...\n"); break; - case USB_EFF: ret = USB_EFF; DBG(" USB EFF...\n"); break; - default: return -EINVAL; } @@ -1958,108 +2208,114 @@ static u8 rk81x_get_charge_status(struct battery_info *di) 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 void rk81x_fg_match_param(struct battery_info *di, int chg_vol, - int chg_ilim, int chg_cur) +static void rk81x_bat_match_param(struct rk81x_battery *di, int chrg_vol, + int chrg_ilim, int chrg_cur) { int i; - di->chg_v_lmt = DEF_CHRG_VOL; - di->chg_i_lmt = DEF_CHRG_CURR_LMT; - di->chg_i_cur = DEF_CHRG_CURR_SEL; + di->chrg_v_lmt = DEF_CHRG_VOL; + di->chrg_i_lmt = DEF_CHRG_CURR_LMT; + di->chrg_i_cur = DEF_CHRG_CURR_SEL; - for (i = 0; i < ARRAY_SIZE(CHG_V_LMT); i++) { - if (chg_vol < CHG_V_LMT[i]) + for (i = 0; i < ARRAY_SIZE(CHRG_V_LMT); i++) { + if (chrg_vol < CHRG_V_LMT[i]) break; - else - di->chg_v_lmt = (i << CHG_VOL_SHIFT); + + di->chrg_v_lmt = (i << CHRG_VOL_SHIFT); } - for (i = 0; i < ARRAY_SIZE(CHG_I_LMT); i++) { - if (chg_ilim < CHG_I_LMT[i]) + for (i = 0; i < ARRAY_SIZE(CHRG_I_LMT); i++) { + if (chrg_ilim < CHRG_I_LMT[i]) break; - else - di->chg_i_lmt = (i << CHG_ILIM_SHIFT); + + di->chrg_i_lmt = (i << CHRG_ILIM_SHIFT); } - for (i = 0; i < ARRAY_SIZE(CHG_I_CUR); i++) { - if (chg_cur < CHG_I_CUR[i]) + for (i = 0; i < ARRAY_SIZE(CHRG_I_CUR); i++) { + if (chrg_cur < CHRG_I_CUR[i]) break; - else - di->chg_i_cur = (i << CHG_ICUR_SHIFT); + + di->chrg_i_cur = (i << CHRG_ICUR_SHIFT); } DBG("<%s>. vol = 0x%x, i_lim = 0x%x, cur=0x%x\n", - __func__, di->chg_v_lmt, di->chg_i_lmt, di->chg_i_cur); + __func__, di->chrg_v_lmt, di->chrg_i_lmt, di->chrg_i_cur); } -static u8 rk81x_chose_finish_ma(int fcc) +static u8 rk81x_bat_select_finish_ma(int fcc) { u8 ma = FINISH_150MA; - if (fcc < 3000) - ma = FINISH_100MA; - - else if (fcc >= 3000 && fcc <= 4000) - ma = FINISH_150MA; + if (fcc > 5000) + ma = FINISH_250MA; - else if (fcc > 4000 && fcc <= 5000) + else if (fcc >= 4000) ma = FINISH_200MA; - else/*fcc > 5000*/ - ma = FINISH_250MA; + else if (fcc >= 3000) + ma = FINISH_150MA; + + else + ma = FINISH_100MA; return ma; } -static void rk81x_battery_charger_init(struct battery_info *di) +/* + * there is a timer inside rk81x to calc how long the battery is in charging + * state. rk81x will close PowerPath inside IC when timer reach, which will + * stop the charging work. we have to reset the corresponding bits to restart + * the timer to avoid that case. + */ +static void rk81x_bat_init_chrg_timer(struct rk81x_battery *di) +{ + u8 buf; + + rk81x_bat_read(di, CHRG_CTRL_REG3, &buf, 1); + buf &= ~CHRG_TIMER_CCCV_EN; + rk81x_bat_write(di, CHRG_CTRL_REG3, &buf, 1); + udelay(40); + rk81x_bat_read(di, CHRG_CTRL_REG3, &buf, 1); + buf |= CHRG_TIMER_CCCV_EN; + rk81x_bat_write(di, CHRG_CTRL_REG3, &buf, 1); + dev_info(di->dev, "reset cccv charge timer\n"); +} + +static void rk81x_bat_charger_init(struct rk81x_battery *di) { u8 chrg_ctrl_reg1, usb_ctrl_reg, chrg_ctrl_reg2, chrg_ctrl_reg3; - u8 sup_sts_reg, thremal_reg; - int chg_vol, chg_cur, chg_ilim; + u8 sup_sts_reg, thremal_reg, ggcon; + int chrg_vol, chrg_cur, chrg_ilim; u8 finish_ma; - chg_vol = di->rk818->battery_data->max_charger_voltagemV; + chrg_vol = di->pdata->max_charger_voltagemV; + chrg_cur = di->pdata->max_charger_currentmA; + chrg_ilim = di->pdata->max_charger_ilimitmA; - if (di->fg_drv_mode == TEST_POWER_MODE) { - chg_cur = di->test_chrg_current; - chg_ilim = di->test_chrg_ilmt; - } else { - chg_cur = di->rk818->battery_data->max_charger_currentmA; - chg_ilim = di->rk818->battery_data->max_charger_ilimitmA; - } - - rk81x_fg_match_param(di, chg_vol, chg_ilim, chg_cur); - finish_ma = rk81x_chose_finish_ma(di->fcc); + rk81x_bat_match_param(di, chrg_vol, chrg_ilim, chrg_cur); + finish_ma = rk81x_bat_select_finish_ma(di->fcc); - battery_read(di->rk818, THERMAL_REG, &thremal_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, SUP_STS_REG, &sup_sts_reg, 1); - battery_read(di->rk818, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + rk81x_bat_init_chrg_timer(di); + rk81x_bat_read(di, THERMAL_REG, &thremal_reg, 1); + rk81x_bat_read(di, USB_CTRL_REG, &usb_ctrl_reg, 1); + rk81x_bat_read(di, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + rk81x_bat_read(di, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + rk81x_bat_read(di, SUP_STS_REG, &sup_sts_reg, 1); + rk81x_bat_read(di, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + rk81x_bat_read(di, GGCON, &ggcon, 1); usb_ctrl_reg &= (~0x0f); - if (rk81x_support_adp_type(HW_ADP_TYPE_USB)) + if (rk81x_bat_support_adp_type(HW_ADP_TYPE_USB)) usb_ctrl_reg |= (CHRG_CT_EN | ILIM_450MA);/*en temp feed back*/ else - usb_ctrl_reg |= (CHRG_CT_EN | di->chg_i_lmt); + usb_ctrl_reg |= (CHRG_CT_EN | di->chrg_i_lmt); - thremal_reg &= (~0x0c); - thremal_reg |= TEMP_105C;/*temp feed back: 105c*/ + if (di->fg_drv_mode == TEST_POWER_MODE) + usb_ctrl_reg |= (CHRG_CT_EN | di->chrg_i_lmt); chrg_ctrl_reg1 &= (0x00); - chrg_ctrl_reg1 |= (CHRG_EN) | (di->chg_v_lmt | di->chg_i_cur); + chrg_ctrl_reg1 |= (CHRG_EN) | (di->chrg_v_lmt | di->chrg_i_cur); chrg_ctrl_reg3 |= CHRG_TERM_DIG_SIGNAL;/* digital finish mode*/ chrg_ctrl_reg2 &= ~(0xc7); @@ -2070,85 +2326,80 @@ static void rk81x_battery_charger_init(struct battery_info *di) thremal_reg &= (~0x0c); thremal_reg |= TEMP_105C;/*temp feed back: 105c*/ + ggcon |= ADC_CURRENT_MODE; - battery_write(di->rk818, THERMAL_REG, &thremal_reg, 1); - 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); + rk81x_bat_write(di, THERMAL_REG, &thremal_reg, 1); + rk81x_bat_write(di, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + /*don't touch charge setting when boot int loader charge mode*/ + if (!di->loader_charged) + rk81x_bat_write(di, USB_CTRL_REG, &usb_ctrl_reg, 1); + rk81x_bat_write(di, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + rk81x_bat_write(di, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + rk81x_bat_write(di, SUP_STS_REG, &sup_sts_reg, 1); + rk81x_bat_write(di, GGCON, &ggcon, 1); } -void charge_disable_open_otg(int value) +void rk81x_charge_disable_open_otg(struct rk81x_battery *di) { - struct battery_info *di = g_battery; + int value = di->charge_otg; - if (value == 1) { + if (value) { 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); - } - if (value == 0) { + } else { DBG("charge enable, disable OTG.\n"); rk818_set_bits(di->rk818, 0x23, 1 << 7, 0 << 7); rk818_set_bits(di->rk818, CHRG_CTRL_REG1, 1 << 7, 1 << 7); } } -static void rk81x_low_waring_init(struct battery_info *di) -{ - u8 vb_mon_reg; - u8 vb_mon_reg_init; - - battery_read(di->rk818, VB_MOD_REG, &vb_mon_reg, 1); - - /* 3.4v: interrupt*/ - vb_mon_reg_init = (((vb_mon_reg | (1 << 4)) & (~0x07)) | 0x06); - battery_write(di->rk818, VB_MOD_REG, &vb_mon_reg_init, 1); -} - -static void rk81x_fg_init(struct battery_info *di) +static void rk81x_bat_fg_init(struct rk81x_battery *di) { - u8 adc_ctrl_val; - u8 buf = 0; u8 pcb_offset; int cal_offset; + u8 val; - adc_ctrl_val = 0x30; - battery_write(di->rk818, ADC_CTRL_REG, &adc_ctrl_val, 1); + val = 0x30; + rk81x_bat_write(di, ADC_CTRL_REG, &val, 1); + rk81x_bat_read(di, RK818_VB_MON_REG, &val, 1); + if (val & PLUG_IN_STS) + rk81x_bat_set_power_supply_state(di, USB_CHARGER); - _gauge_enable(di); + rk81x_bat_gauge_enable(di); /* get the volatege offset */ - _get_voltage_offset_value(di); - rk81x_battery_charger_init(di); - _set_relax_thres(di); + rk81x_bat_get_vol_offset(di); + rk81x_bat_charger_init(di); + rk81x_bat_set_relax_thres(di); /* get the current offset , the value write to the CAL_OFFSET */ - di->current_offset = _get_ioffset(di); - battery_read(di->rk818, PCB_IOFFSET_REG, &pcb_offset, 1); - DBG("<%s>. pcb_offset = 0x%x\n", __func__, pcb_offset); - DBG("<%s>. io_offset = 0x%x\n", __func__, di->current_offset); - - _set_cal_offset(di, di->current_offset+pcb_offset); - cal_offset = _get_cal_offset(di); - if ((cal_offset < 0x7ff) || (pcb_offset == 0)) - _set_cal_offset(di, di->current_offset+42); + di->current_offset = rk81x_bat_get_ioffset(di); + rk81x_bat_read(di, PCB_IOFFSET_REG, &pcb_offset, 1); + DBG("<%s>. pcb_offset = 0x%x, io_offset = 0x%x\n", + __func__, pcb_offset, di->current_offset); + if (!pcb_offset) + pcb_offset = DEF_PCB_OFFSET; + cal_offset = pcb_offset + di->current_offset; + if (cal_offset < 0x7ff || cal_offset > 0x8ff) + cal_offset = DEF_CAL_OFFSET; + rk81x_bat_set_cal_offset(di, cal_offset); + /* set sample time for cal_offset interval*/ + rk81x_bat_ioffset_sample_set(di, SAMP_TIME_8MIN); - _rsoc_init(di); - _capacity_init(di, di->nac); + rk81x_bat_rsoc_init(di); + rk81x_bat_capacity_init(di, di->nac); + rk81x_bat_capacity_init_post(di); - di->remain_capacity = _get_realtime_capacity(di); - di->current_avg = _get_average_current(di); + di->remain_capacity = rk81x_bat_get_realtime_capacity(di); + di->current_avg = rk81x_bat_get_avg_current(di); - rk81x_low_waring_init(di); - restart_relax(di); - power_on_save(di, di->voltage_ocv); - battery_write(di->rk818, OCV_VOL_VALID_REG, &buf, 1); + rk81x_bat_restart_relax(di); + rk81x_bat_power_on_save(di, di->voltage_ocv); + val = 0; + rk81x_bat_write(di, OCV_VOL_VALID_REG, &val, 1); - /* set sample time for cal_offset interval*/ - ioffset_sample_time(di, SAMP_TIME_8MIN); - dump_gauge_register(di); - dump_charger_register(di); + rk81x_dbg_dmp_gauge_regs(di); + rk81x_dbg_dmp_charger_regs(di); DBG("<%s> :\n" "nac = %d , remain_capacity = %d\n" @@ -2158,196 +2409,116 @@ static void rk81x_fg_init(struct battery_info *di) __func__, di->nac, di->remain_capacity, di->voltage_ocv, di->voltage, - di->real_soc, di->fcc, di->current_avg, + di->dsoc, di->fcc, di->current_avg, cal_offset); } -/* - * this is a very important algorithm to avoid over discharge. - */ -/* int R_soc, D_soc, r_soc, zq, k, Q_err, Q_ocv; */ -static void zero_get_soc(struct battery_info *di) + +static void rk81x_bat_zero_calc_linek(struct rk81x_battery *di) { int dead_voltage, ocv_voltage; - int temp_soc = -1, real_soc; - int currentold, currentnow, voltage; - int i; - int voltage_k; - int count_num = 0; - int q_ocv; - int ocv_soc; + int voltage, voltage_old, voltage_now; + int i, rsoc; + int q_ocv, q_dead; + int count_num, currentnow; + int ocv_soc, dead_soc; + int power_off_thresd = di->pdata->power_off_thresd; - DBG("\n\n+++++++zero mode++++++display soc+++++++++++\n"); do { - currentold = _get_average_current(di); - _get_cal_offset(di); - _get_ioffset(di); + voltage_old = rk81x_bat_get_vol(di); msleep(100); - currentnow = _get_average_current(di); + voltage_now = rk81x_bat_get_vol(di); count_num++; - } while ((currentold == currentnow) && (count_num < 11)); + } while ((voltage_old == voltage_now) && (count_num < 11)); + DBG("<%s>. current calc count=%d\n", __func__, count_num); - voltage = 0; - for (i = 0; i < 10 ; i++) - voltage += _get_battery_voltage(di); + voltage = 0; + for (i = 0; i < 10; i++) { + voltage += rk81x_bat_get_vol(di); + msleep(100); + } 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; - currentnow = _get_average_current(di); - - dead_voltage = 3400 + abs32_int(currentnow)*(di->bat_res+65)/1000; - /* 65 mo power-path mos */ - ocv_voltage = voltage + abs32_int(currentnow)*di->bat_res/1000; - DBG("ZERO: dead_voltage(shtd) = %d, ocv_voltage(now) = %d\n", + currentnow = rk81x_bat_get_avg_current(di); + + /* 50 mo power-path mos */ + dead_voltage = power_off_thresd - currentnow * + (di->bat_res + DEF_PWRPATH_RES) / 1000; + + ocv_voltage = voltage - (currentnow * di->bat_res) / 1000; + DBG("ZERO0: dead_voltage(shtd) = %d, ocv_voltage(now) = %d\n", dead_voltage, ocv_voltage); - ocv_soc = _voltage_to_capacity(di, dead_voltage); - di->q_dead = di->temp_nac; - DBG("ZERO: dead_voltage_soc = %d, q_dead = %d\n", - ocv_soc, di->q_dead); + dead_soc = rk81x_bat_vol_to_capacity(di, dead_voltage); + q_dead = di->temp_nac; + DBG("ZERO0: dead_voltage_soc = %d, q_dead = %d\n", + dead_soc, q_dead); - ocv_soc = _voltage_to_capacity(di, ocv_voltage); + ocv_soc = rk81x_bat_vol_to_capacity(di, ocv_voltage); q_ocv = di->temp_nac; - DBG("ZERO: ocv_voltage_soc = %d, q_ocv = %d\n", + DBG("ZERO0: ocv_voltage_soc = %d, q_ocv = %d\n", ocv_soc, q_ocv); - /*[Q_err]: Qerr, [temp_nac]:check_voltage_nac*/ - di->q_err = di->remain_capacity - q_ocv; - DBG("q_err=%d, [remain_capacity]%d - [q_ocv]%d", - di->q_err, di->remain_capacity, q_ocv); - - if (di->display_soc == 0) - di->display_soc = di->real_soc*1000; - real_soc = di->display_soc; - - DBG("remain_capacity = %d, q_dead = %d, q_err = %d\n", - di->remain_capacity, di->q_dead, di->q_err); - /*[temp_nac]:dead_voltage*/ - if (q_ocv > di->q_dead) { - DBG("first: q_ocv > di->q_dead\n"); - - /*initical K0*/ - if ((di->update_k == 0) || (di->zero_cycle >= 500)) { - DBG("[K == 0]\n"); - di->zero_cycle = 0; - di->update_k = 1; - /* ZQ = Q_ded + Qerr */ - /*[temp_nac]:dead_voltage*/ - di->q_shtd = di->q_dead + di->q_err; - temp_soc = (di->remain_capacity - di->q_shtd)* - 1000/div(di->fcc); - if (temp_soc == 0) - di->update_k = 0; - else - di->line_k = (real_soc + temp_soc/2) - /div(temp_soc); - /* recalc K0*/ - } else if (di->zero_updated && di->update_k >= 10) { - DBG("[K >= 10].\n"); - di->update_k = 1; - _voltage_to_capacity(di, dead_voltage); - di->q_dead = di->temp_nac; - di->q_shtd = di->q_dead + di->q_err; - temp_soc = ((di->remain_capacity - di->q_shtd)* - 1000 + di->fcc/2)/div(di->fcc); /* z1 */ - if (temp_soc == 0) - di->update_k = 0; - else - di->line_k = (real_soc + temp_soc/2) - /div(temp_soc); - - DBG("[K >= 10]. new:line_k = %d\n", di->line_k); - DBG("[K >= 10]. new:Y0(dis_soc)=%d\n", di->display_soc); - DBG("[K >= 10]. new:X0(temp) = %d\n", temp_soc); - - } else { /*update_k[1~9]*/ - DBG("[K1~9]\n"); - di->zero_cycle++; - di->update_k++; - DBG("[K1~9]. (old)Y0=%d, Y0=%d\n", - di->old_display_soc, di->display_soc); - if (di->update_k == 2) - di->old_display_soc = di->display_soc; - - temp_soc = ((di->remain_capacity - di->q_shtd)* - 1000 + di->fcc/2)/div(di->fcc); - real_soc = di->line_k*temp_soc; - di->display_soc = real_soc; - - /* make sure display_soc change at least once*/ - if (di->display_soc >= di->old_display_soc) - di->zero_updated = false; - else - di->zero_updated = true; - - DBG("[K1~9]. (temp_soc)X0 = %d\n", temp_soc); - DBG("[K1~9]. line_k = %d\n", di->line_k); - DBG("[K1~9]. (dis-soc)Y0=%d,real-soc=%d\n", - di->display_soc, di->real_soc); - - if ((di->display_soc+500)/1000 < di->real_soc) { - /*special for 0%*/ - if ((di->real_soc == 1) && - (di->display_soc < 100)) - di->real_soc--; - else - di->real_soc--; - /*di->odd_capacity = 0;*/ - } - } + rsoc = ocv_soc - dead_soc; + if ((di->dsoc == 1) && (rsoc > 0)) {/*discharge*/ + di->line_k = 1000; + } else if (rsoc > 1) { + di->line_k = (di->display_soc + rsoc / 2) / div(rsoc); } else { - DBG("second: q_ocv < di->q_dead\n"); - di->update_k++; - - if (di->voltage < 3400) { - DBG("second: voltage < 3400\n"); - di->real_soc--; - } else { - if (di->update_k > 10) { - di->update_k = 0; - di->real_soc--; - di->odd_capacity = 0; - } - } - } - - if (di->line_k <= 0) { - reset_zero_var(di); - DBG("ZERO: line_k <= 0, Update line_k!\n"); + di->dsoc--; + di->display_soc = di->dsoc * 1000; } - DBG("ZERO: update_k=%d, odd_cap=%d\n", di->update_k, di->odd_capacity); - DBG("ZERO: q_ocv - q_dead=%d\n", (q_ocv-di->q_dead)); - DBG("ZERO: remain_cap - q_shtd=%d\n", - (di->remain_capacity - di->q_shtd)); - DBG("ZERO: (line_k)K0 = %d,(disp-soc)Y0 = %d, (temp_soc)X0 = %d\n", - di->line_k, di->display_soc, temp_soc); - DBG("ZERO: zero_cycle=%d,(old)Y0=%d, zero_updated=%d, update_k=%d\n", - di->zero_cycle, di->old_display_soc, - di->zero_updated, di->update_k); + di->zero_old_remain_cap = di->remain_capacity; - DBG("ZERO: remain_capacity=%d, q_shtd(nac)=%d, q_err(Q_rm-q_ocv)=%d\n", - di->remain_capacity, di->q_shtd, di->q_err); - DBG("ZERO: Warn_voltage=%d,temp_soc=%d,real_soc=%d\n\n", - di->warnning_voltage, _get_soc(di), di->real_soc); + DBG("ZERO-new: new-line_k=%d, dsoc=%d, X0soc=%d\n" + "ZERO-new: di->display_soc=%d, old_remain_cap=%d\n\n", + di->line_k, di->dsoc, rsoc, + di->display_soc, di->zero_old_remain_cap); } +static void rk81x_bat_zero_algorithm(struct rk81x_battery *di) +{ + int delta_cap, delta_soc; + int tmp_dsoc; + + di->zero_timeout_cnt++; + delta_cap = di->zero_old_remain_cap - di->remain_capacity; + delta_soc = di->line_k * (delta_cap * 100) / div(di->fcc); + + DBG("ZERO1: line_k=%d, display_soc(Y0)=%d, dsoc=%d, rsoc=%d\n" + "ZERO1: delta_soc(X0)=%d, delta_cap=%d, old_remain_cap = %d\n" + "ZERO1: timeout_cnt=%d\n\n", + di->line_k, di->display_soc, di->dsoc, di->rsoc, + delta_soc, delta_cap, di->zero_old_remain_cap, + di->zero_timeout_cnt); + + if ((abs(delta_soc) > MIN_ZERO_ACCURACY) || + (di->zero_timeout_cnt > 500)) { + DBG("ZERO1:--------- enter calc -----------\n"); + di->zero_timeout_cnt = 0; + di->display_soc -= delta_soc; + tmp_dsoc = (di->display_soc + 500) / 1000; + di->dsoc = tmp_dsoc; + + DBG("ZERO1: display_soc(Y0)=%d, dsoc=%d, rsoc=%d, tmp_soc=%d", + di->display_soc, di->dsoc, di->rsoc, tmp_dsoc); -static int estimate_bat_ocv_vol(struct battery_info *di) + rk81x_bat_zero_calc_linek(di); + } +} + +static int rk81x_bat_est_ocv_vol(struct rk81x_battery *di) { return (di->voltage - (di->bat_res * di->current_avg) / 1000); } -static int estimate_bat_ocv_soc(struct battery_info *di) +static int rk81x_bat_est_ocv_soc(struct rk81x_battery *di) { int ocv_soc, ocv_voltage; - ocv_voltage = estimate_bat_ocv_vol(di); - ocv_soc = _voltage_to_capacity(di, ocv_voltage); + ocv_voltage = rk81x_bat_est_ocv_vol(di); + ocv_soc = rk81x_bat_vol_to_capacity(di, ocv_voltage); return ocv_soc; } @@ -2356,39 +2527,39 @@ static int estimate_bat_ocv_soc(struct battery_info *di) * if there is a big offset between ocv_soc and rsoc, * we will decide whether we should reinit capacity or not */ -static void rsoc_dischrg_calib(struct battery_info *di) +static void rk81x_bat_rsoc_dischrg_check(struct rk81x_battery *di) { int ocv_soc = di->est_ocv_soc; int ocv_volt = di->est_ocv_vol; - int temp_soc = _get_soc(di); - int max_volt = di->rk818->battery_data->max_charger_voltagemV; + int rsoc = rk81x_bat_get_rsoc(di); + int max_volt = di->pdata->max_charger_voltagemV; if (ocv_volt > max_volt) goto out; - if (di->discharge_min >= RSOC_CALIB_DISCHGR_TIME) { - if ((ocv_soc-temp_soc >= RSOC_DISCHG_ERR_LOWER) || - (di->temp_soc == 0) || - (temp_soc-ocv_soc >= RSOC_DISCHG_ERR_UPPER)) { + if (di->plug_out_min >= RSOC_CALIB_DISCHRGR_TIME) { + if ((ocv_soc-rsoc >= RSOC_DISCHRG_ERR_LOWER) || + (di->rsoc == 0) || + (rsoc-ocv_soc >= RSOC_DISCHRG_ERR_UPPER)) { di->err_chck_cnt++; di->err_soc_sum += ocv_soc; } else { goto out; } - DBG("<%s>. rsoc err_chck_cnt = %d\n", - __func__, di->err_chck_cnt); - DBG("<%s>. rsoc err_soc_sum = %d\n", - __func__, di->err_soc_sum); + DBG("<%s>. rsoc err_chck_cnt = %d, err_soc_sum = %d\n", + __func__, di->err_chck_cnt, di->err_soc_sum); if (di->err_chck_cnt >= RSOC_ERR_CHCK_CNT) { ocv_soc = di->err_soc_sum / RSOC_ERR_CHCK_CNT; - if (temp_soc-ocv_soc >= RSOC_DISCHG_ERR_UPPER) + if (rsoc-ocv_soc >= RSOC_DISCHRG_ERR_UPPER) ocv_soc += RSOC_COMPS; di->temp_nac = ocv_soc * di->fcc / 100; - _capacity_init(di, di->temp_nac); - di->temp_soc = _get_soc(di); - di->remain_capacity = _get_realtime_capacity(di); + rk81x_bat_capacity_init(di, di->temp_nac); + rk81x_bat_capacity_init_post(di); + di->rsoc = rk81x_bat_get_rsoc(di); + di->remain_capacity = + rk81x_bat_get_realtime_capacity(di); di->err_soc_sum = 0; di->err_chck_cnt = 0; DBG("<%s>. update: rsoc = %d\n", __func__, ocv_soc); @@ -2400,206 +2571,277 @@ out: } } -static void rsoc_realtime_calib(struct battery_info *di) +static void rk81x_bat_rsoc_check(struct rk81x_battery *di) { - u8 status = di->status; + u8 status = di->psy_status; if ((status == POWER_SUPPLY_STATUS_CHARGING) || (status == POWER_SUPPLY_STATUS_FULL)) { - if ((di->current_avg < -10) && - (di->charge_status != CHARGE_FINISH)) - rsoc_dischrg_calib(di); + if ((di->current_avg < 0) && + (di->chrg_status != CHARGE_FINISH)) + rk81x_bat_rsoc_dischrg_check(di); /* else rsoc_chrg_calib(di); */ } else if (status == POWER_SUPPLY_STATUS_DISCHARGING) { - rsoc_dischrg_calib(di); + rk81x_bat_rsoc_dischrg_check(di); + } +} + +static void rk81x_bat_emulator_dischrg(struct rk81x_battery *di) +{ + u32 temp, soc_time; + unsigned long sec_unit; + + if (!di->dischrg_emu_base) + di->dischrg_emu_base = get_runtime_sec(); + + sec_unit = BASE_TO_SEC(di->dischrg_emu_base) + di->dischrg_save_sec; + + temp = di->fcc * 3600 / 100; + + if (abs(di->current_avg) < DSOC_DISCHRG_EMU_CURR) + soc_time = temp / div(abs(DSOC_DISCHRG_EMU_CURR)); + else + soc_time = temp / div(abs(di->current_avg)); + + if (sec_unit > soc_time) { + di->dsoc--; + di->dischrg_emu_base = get_runtime_sec(); + di->dischrg_save_sec = 0; } + + DBG("<%s> soc_time=%d, sec_unit=%lu\n", + __func__, soc_time, sec_unit); } /* * when there is a big offset between dsoc and rsoc, dsoc needs to * speed up to keep pace witch rsoc. */ -static bool do_ac_charger_emulator(struct battery_info *di) +static void rk81x_bat_emulator_chrg(struct rk81x_battery *di) { - int delta_soc = di->temp_soc - di->real_soc; - u32 soc_time; + u32 soc_time, temp; + int plus_soc; + unsigned long chrg_emu_sec; + + if (!di->chrg_emu_base) + di->chrg_emu_base = get_runtime_sec(); + + chrg_emu_sec = BASE_TO_SEC(di->chrg_emu_base) + di->chrg_save_sec; + temp = di->fcc * 3600 / 100; - if ((di->charge_status != CHARGE_FINISH) && - (di->ac_online == ONLINE) && - (delta_soc >= DSOC_CHRG_FAST_EER_RANGE)) { + if (di->ac_online) { if (di->current_avg < DSOC_CHRG_EMU_CURR) - soc_time = di->fcc*3600/100/ - (abs_int(DSOC_CHRG_EMU_CURR)); + soc_time = temp / abs(DSOC_CHRG_EMU_CURR); else - soc_time = di->fcc*3600/100/ - div(abs_int(di->current_avg)); - di->emu_chg_cnt++; - if (di->emu_chg_cnt > soc_time) { - di->real_soc++; - di->emu_chg_cnt = 0; - } - DBG("<%s>. soc_time=%d, emu_cnt=%d\n", - __func__, soc_time, di->emu_chg_cnt); + soc_time = temp / div(abs(di->current_avg)); + } else { + soc_time = temp / 450; + } - return true; + plus_soc = chrg_emu_sec / soc_time; + if (chrg_emu_sec > soc_time) { + di->dsoc += plus_soc; + di->chrg_emu_base = get_runtime_sec(); + di->chrg_save_sec = 0; } - return false; + DBG("<%s>. soc_time=%d, chrg_emu_sec=%lu, plus_soc=%d\n", + __func__, soc_time, chrg_emu_sec, plus_soc); } /* check voltage and current when dsoc is close to full. * we will do a fake charge to adjust charing speed which * aims to make battery full charged and match finish signal. */ -static bool do_term_chrg_calib(struct battery_info *di) +static void rk81x_bat_terminal_chrg(struct rk81x_battery *di) { u32 soc_time; - u32 *ocv_table = di->platform_data->battery_ocv; + int plus_soc; + unsigned long chrg_term_sec; + + if (!di->chrg_term_base) + di->chrg_term_base = get_runtime_sec(); + chrg_term_sec = BASE_TO_SEC(di->chrg_term_base) + di->chrg_save_sec; /*check current and voltage*/ - if ((di->ac_online == ONLINE && di->real_soc >= 90) && - ((di->current_avg > DSOC_CHG_TERM_CURR) || - (di->voltage < ocv_table[18]+20))) { - soc_time = di->fcc*3600/100/(abs32_int(DSOC_CHG_TERM_CURR)); - di->term_chg_cnt++; - if (di->term_chg_cnt > soc_time) { - di->real_soc++; - di->term_chg_cnt = 0; - } - DBG("<%s>. soc_time=%d, term_cnt=%d\n", - __func__, soc_time, di->term_chg_cnt); - return true; - } + soc_time = di->fcc * 3600 / 100 / (abs(DSOC_CHRG_TERM_CURR)); - return false; + plus_soc = chrg_term_sec / soc_time; + if (chrg_term_sec > soc_time) { + di->dsoc += plus_soc; + di->chrg_term_base = get_runtime_sec(); + di->chrg_save_sec = 0; + } + DBG("<%s>. soc_time=%d, chrg_term_sec=%lu, plus_soc=%d\n", + __func__, soc_time, chrg_term_sec, plus_soc); } -static void normal_discharge(struct battery_info *di) +static void rk81x_bat_normal_dischrg(struct rk81x_battery *di) { int soc_time = 0; int now_current = di->current_avg; - int delta_soc = di->real_soc - di->temp_soc; - - if (delta_soc > DSOC_DISCHRG_FAST_EER_RANGE) { - soc_time = DSOC_DISCHRG_FAST_DEC_SEC; - DBG("<%s>. dsoc decrease fast! delta_soc = %d\n", - __func__, delta_soc); - } else { - soc_time = di->fcc*3600/100/div(abs_int(now_current)); - } - - if (di->temp_soc == di->real_soc) { - DBG("<%s>. temp_soc == real_soc\n", __func__); - - } else if (di->temp_soc > di->real_soc) { - DBG("<%s>. temp_soc > real_soc\n", __func__); - di->vol_smooth_time++; - if (di->vol_smooth_time > soc_time*3/2) { - di->real_soc--; - di->vol_smooth_time = 0; + unsigned long dischrg_normal_sec; + + if (!di->dischrg_normal_base) + di->dischrg_normal_base = get_runtime_sec(); + + dischrg_normal_sec = BASE_TO_SEC(di->dischrg_normal_base) + + di->dischrg_save_sec; + + soc_time = di->fcc * 3600 / 100 / div(abs(now_current)); + DBG("<%s>. rsoc=%d, dsoc=%d, dischrg_st=%d\n", + __func__, di->rsoc, di->dsoc, di->discharge_smooth_status); + + if (di->rsoc == di->dsoc) { + DBG("<%s>. rsoc == dsoc\n", __func__); + di->dsoc = di->rsoc; + di->dischrg_normal_base = get_runtime_sec(); + di->dischrg_save_sec = 0; + /*di->discharge_smooth_status = false;*/ + } else if (di->rsoc > di->dsoc - 1) { + DBG("<%s>. rsoc > dsoc - 1\n", __func__); + if (dischrg_normal_sec > soc_time * 3 / 2) { + di->dsoc--; + di->dischrg_normal_base = get_runtime_sec(); + di->dischrg_save_sec = 0; } - - } else { - DBG("<%s>. temp_soc < 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/4) { - di->real_soc--; - di->vol_smooth_time = 0; + di->discharge_smooth_status = true; + + } else if (di->rsoc < di->dsoc - 1) { + DBG("<%s>. rsoc < dsoc - 1\n", __func__); + if (dischrg_normal_sec > soc_time * 3 / 4) { + di->dsoc--; + di->dischrg_normal_base = get_runtime_sec(); + di->dischrg_save_sec = 0; + } + di->discharge_smooth_status = true; + + } else if (di->rsoc == di->dsoc - 1) { + DBG("<%s>. rsoc == dsoc - 1\n", __func__); + if (di->discharge_smooth_status) { + if (dischrg_normal_sec > soc_time * 3 / 4) { + di->dsoc--; + di->dischrg_normal_base = get_runtime_sec(); + di->dischrg_save_sec = 0; + di->discharge_smooth_status = false; } + } else { + di->dsoc--; + di->dischrg_normal_base = get_runtime_sec(); + di->dischrg_save_sec = 0; + di->discharge_smooth_status = false; } } - reset_zero_var(di); - DBG("<%s>, temp_soc = %d, real_soc = %d\n", - __func__, di->temp_soc, di->real_soc); - DBG("<%s>, vol_smooth_time = %d, soc_time = %d\n", - __func__, di->vol_smooth_time, soc_time); + + DBG("<%s>, rsoc = %d, dsoc = %d, discharge_smooth_status = %d\n" + "dischrg_normal_sec = %lu, soc_time = %d, delta_vol=%d\n", + __func__, di->rsoc, di->dsoc, di->discharge_smooth_status, + dischrg_normal_sec, soc_time, di->delta_vol_smooth); } -static void rk81x_battery_discharge_smooth(struct battery_info *di) +static void rk81x_bat_dischrg_smooth(struct rk81x_battery *di) { - int ocv_soc; + int delta_soc; - ocv_soc = _voltage_to_capacity(di, 3800); - di->temp_soc = _get_soc(di); + /* first resume from suspend: we don't run this, + * the sleep_dischrg will handle dsoc, and what + * ever this is fake wakeup or not, we should clean + * zero algorithm mode, or it will handle the dsoc. + */ + if (di->s2r) { + rk81x_bat_reset_zero_var(di); + return; + } - DBG("<%s>. temp_soc = %d, real_soc = %d\n", - __func__, di->temp_soc, di->real_soc); + di->rsoc = rk81x_bat_get_rsoc(di); - if (di->voltage < 3800) + DBG("<%s>. rsoc = %d, dsoc = %d, dischrg_algorithm_mode=%d\n", + __func__, di->rsoc, di->dsoc, di->dischrg_algorithm_mode); - zero_get_soc(di); - else - normal_discharge(di); -} + if (di->dischrg_algorithm_mode == DISCHRG_NORMAL_MODE) { + delta_soc = di->dsoc - di->rsoc; -static int get_charging_time(struct battery_info *di) -{ - return (di->charging_time/60); -} + if (delta_soc > DSOC_DISCHRG_FAST_EER_RANGE) { + di->dischrg_normal_base = 0; + rk81x_bat_emulator_dischrg(di); + } else { + di->chrg_emu_base = 0; + rk81x_bat_normal_dischrg(di); + } -static int get_discharging_time(struct battery_info *di) -{ - return (di->discharging_time/60); -} + if (di->voltage < ZERO_ALGOR_THRESD) { + di->dischrg_normal_base = 0; + di->chrg_emu_base = 0; + di->dischrg_algorithm_mode = DISCHRG_ZERO_MODE; + di->zero_timeout_cnt = 0; + + DBG("<%s>. dsoc=%d, last_zero_mode_dsoc=%d\n", + __func__, di->dsoc, di->last_zero_mode_dsoc); + if (di->dsoc != di->last_zero_mode_dsoc) { + di->display_soc = di->dsoc * 1000; + di->last_zero_mode_dsoc = di->dsoc; + rk81x_bat_zero_calc_linek(di); + DBG("<%s>. first calc, init linek\n", __func__); + } + } + } else { + rk81x_bat_zero_algorithm(di); -static int get_finish_time(struct battery_info *di) -{ - return (di->finish_time/60); + if (di->voltage > ZERO_ALGOR_THRESD + 50) { + di->dischrg_algorithm_mode = DISCHRG_NORMAL_MODE; + di->zero_timeout_cnt = 0; + DBG("<%s>. exit zero_algorithm\n", __func__); + } + } } -static void upd_time_table(struct battery_info *di); -static void collect_debug_info(struct battery_info *di) +static void rk81x_bat_dbg_time_table(struct rk81x_battery *di) { - if ((di->ac_online == ONLINE) || (di->usb_online == ONLINE)) { - di->charging_time++; - di->discharging_time = 0; - } else { - di->charging_time = 0; - if (di->voltage < 3800) - di->discharging_time += 2; - else - di->discharging_time++; - } - if (di->charge_status == CHARGE_FINISH) - di->finish_time++; + u8 i; + static int old_index; + static int old_min; + u32 time; + int mod = di->dsoc % 10; + int index = di->dsoc / 10; + + if (rk81x_chrg_online(di)) + time = di->plug_in_min; else - di->finish_time = 0; + time = di->plug_out_min; - di->charge_min = get_charging_time(di); - di->discharge_min = get_discharging_time(di); - di->finish_min = get_finish_time(di); + if ((mod == 0) && (index > 0) && (old_index != index)) { + di->chrg_min[index-1] = time - old_min; + old_min = time; + old_index = index; + } - upd_time_table(di); + for (i = 1; i < 11; i++) + DBG("Time[%d]=%d, ", (i * 10), di->chrg_min[i-1]); + DBG("\n"); } -static void dump_debug_info(struct battery_info *di) +static void rk81x_bat_dbg_dmp_info(struct rk81x_battery *di) { u8 sup_tst_reg, ggcon_reg, ggsts_reg, vb_mod_reg; u8 usb_ctrl_reg, chrg_ctrl_reg1, thremal_reg; u8 chrg_ctrl_reg2, chrg_ctrl_reg3, rtc_val, misc_reg; - collect_debug_info(di); - - battery_read(di->rk818, MISC_MARK_REG, &misc_reg, 1); - 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); - battery_read(di->rk818, THERMAL_REG, &thremal_reg, 1); + rk81x_bat_read(di, MISC_MARK_REG, &misc_reg, 1); + rk81x_bat_read(di, GGCON, &ggcon_reg, 1); + rk81x_bat_read(di, GGSTS, &ggsts_reg, 1); + rk81x_bat_read(di, SUP_STS_REG, &sup_tst_reg, 1); + rk81x_bat_read(di, VB_MOD_REG, &vb_mod_reg, 1); + rk81x_bat_read(di, USB_CTRL_REG, &usb_ctrl_reg, 1); + rk81x_bat_read(di, CHRG_CTRL_REG1, &chrg_ctrl_reg1, 1); + rk81x_bat_read(di, CHRG_CTRL_REG2, &chrg_ctrl_reg2, 1); + rk81x_bat_read(di, CHRG_CTRL_REG3, &chrg_ctrl_reg3, 1); + rk81x_bat_read(di, 0x00, &rtc_val, 1); + rk81x_bat_read(di, THERMAL_REG, &thremal_reg, 1); DBG("\n------------- dump_debug_regs -----------------\n" "GGCON = 0x%2x, GGSTS = 0x%2x, RTC = 0x%2x\n" @@ -2614,495 +2856,548 @@ static void dump_debug_info(struct battery_info *di) chrg_ctrl_reg2, chrg_ctrl_reg3 ); - DBG( - "########################## [read] 3.0############################\n" - "--------------------------------------------------------------\n" - "realx-voltage = %d, voltage = %d, current-avg = %d\n" + DBG("#######################################################\n" + "voltage = %d, current-avg = %d\n" "fcc = %d, remain_capacity = %d, ocv_volt = %d\n" "check_ocv = %d, check_soc = %d, bat_res = %d\n" - "diplay_soc = %d, cpapacity_soc = %d, test_mode = %d\n" + "display_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" "i_offset=0x%x, cal_offset=0x%x, adjust_cap=%d\n" - "chrg_time = %d, dischrg_time = %d, finish_time = %d\n", - get_relax_voltage(di), + "plug_in = %d, plug_out = %d, finish_sig = %d\n" + "sec: chrg=%lu, dischrg=%lu, term_chrg=%lu, emu_chrg=%lu\n" + "emu_dischrg = %lu, power_on_sec = %lu, g_base_sec=%lld\n" + "mode:%d, save_chrg_sec = %lu, save_dischrg_sec = %lu\n" + "#########################################################\n", di->voltage, di->current_avg, - di->fcc, di->remain_capacity, _get_OCV_voltage(di), + di->fcc, di->remain_capacity, di->voltage_ocv, di->est_ocv_vol, di->est_ocv_soc, di->bat_res, - di->real_soc, _get_soc(di), di->fg_drv_mode, - di->ac_online, di->usb_online, di->status, + di->dsoc, di->rsoc, + di->ac_online, di->usb_online, di->psy_status, di->debug_finish_real_soc, di->debug_finish_temp_soc, - _get_ioffset(di), _get_cal_offset(di), di->adjust_cap, - get_charging_time(di), get_discharging_time(di), get_finish_time(di) + rk81x_bat_get_ioffset(di), rk81x_bat_get_cal_offset(di), + di->adjust_cap, di->plug_in_min, di->plug_out_min, + di->finish_sig_min, + BASE_TO_SEC(di->chrg_normal_base), + BASE_TO_SEC(di->dischrg_normal_base), + BASE_TO_SEC(di->chrg_term_base), + BASE_TO_SEC(di->chrg_emu_base), + BASE_TO_SEC(di->dischrg_emu_base), + BASE_TO_SEC(di->power_on_base), g_base_sec, + di->current_mode, di->chrg_save_sec, di->dischrg_save_sec ); - rk81x_get_charge_status(di); - DBG("###########################################################\n"); + DBG(); } -static void update_fcc_capacity(struct battery_info *di) +static void rk81x_bat_update_fcc(struct rk81x_battery *di) { int fcc0; int remain_cap; + int dod0_to_soc100_min; + + remain_cap = di->remain_capacity - di->dod0_capacity - di->adjust_cap; + dod0_to_soc100_min = BASE_TO_MIN(di->fcc_update_sec); - remain_cap = di->remain_capacity + di->adjust_cap - di->first_on_cap; - DBG("%s: remain_cap:%d, ajust_cap:%d, first_on_cap=%d\n", - __func__, remain_cap, di->adjust_cap, di->first_on_cap); + DBG("%s: remain_cap:%d, ajust_cap:%d, dod0_status=%d\n" + "dod0_capacity:%d, dod0_to_soc100_min:%d\n", + __func__, remain_cap, di->adjust_cap, di->dod0_status, + di->dod0_capacity, dod0_to_soc100_min); - if ((di->charge_status == CHARGE_FINISH) && (di->dod0_status == 1)) { + if ((di->chrg_status == CHARGE_FINISH) && (di->dod0_status == 1) && + (dod0_to_soc100_min < 1200)) { DBG("%s: dod0:%d, dod0_cap:%d, dod0_level:%d\n", __func__, di->dod0, di->dod0_capacity, di->dod0_level); - if (get_level(di) >= di->dod0_level) { - fcc0 = (remain_cap - di->dod0_capacity)*100 - /(100-di->dod0); - if (fcc0 > di->qmax) - fcc0 = di->qmax; - - DBG("%s: fcc0:%d, fcc:%d\n", __func__, fcc0, di->fcc); - if ((fcc0 < di->fcc) && (fcc0 > 1000)) { - di->fcc = fcc0; - _capacity_init(di, di->fcc); - _save_FCC_capacity(di, di->fcc); - DBG("%s: new fcc0:%d\n", __func__, di->fcc); - } + fcc0 = remain_cap * 100 / div(100 - di->dod0); + + dev_info(di->dev, "%s: fcc0:%d, fcc:%d\n", + __func__, fcc0, di->fcc); + + if ((fcc0 < di->qmax) && (fcc0 > 1000)) { + di->dod0_status = 0; + di->fcc = fcc0; + rk81x_bat_capacity_init(di, di->fcc); + rk81x_bat_capacity_init_post(di); + rk81x_bat_save_fcc(di, di->fcc); + rk81x_bat_save_level(di, di->dod0_level); + DBG("%s: new fcc0:%d\n", __func__, di->fcc); } + di->dod0_status = 0; } } -static void debug_get_finish_soc(struct battery_info *di) +static void rk81x_bat_dbg_get_finish_soc(struct rk81x_battery *di) { - if (di->charge_status == CHARGE_FINISH) { - di->debug_finish_real_soc = di->real_soc; - di->debug_finish_temp_soc = di->temp_soc; + if (di->chrg_status == CHARGE_FINISH) { + di->debug_finish_real_soc = di->dsoc; + di->debug_finish_temp_soc = di->rsoc; } } -static void wait_charge_finish_signal(struct battery_info *di) +static void rk81x_bat_wait_finish_sig(struct rk81x_battery *di) { - if ((di->charge_status == CHARGE_FINISH) && - (di->voltage > CHG_FINISH_VOL)) - update_fcc_capacity(di);/* save new fcc*/ + int chrg_finish_vol = di->pdata->max_charger_voltagemV; + bool ret; - /* debug msg*/ - debug_get_finish_soc(di); + if ((di->chrg_status == CHARGE_FINISH) && + (di->voltage > chrg_finish_vol - 150) && di->enter_finish) { + rk81x_bat_update_fcc(di);/* save new fcc*/ + ret = rk81x_bat_zero_current_calib(di); + if (ret) + di->enter_finish = false; + /* debug msg*/ + rk81x_bat_dbg_get_finish_soc(di); + } } -static void charge_finish_routine(struct battery_info *di) +static void rk81x_bat_finish_chrg(struct rk81x_battery *di) { - if ((di->charge_status == CHARGE_FINISH) && - (di->voltage > CHG_FINISH_VOL)) { - _capacity_init(di, di->fcc); - zero_current_calib(di); - - if (di->real_soc < 100) { - DBG("<%s>,CHARGE_FINISH:real_soc<100,real_soc=%d\n", - __func__, di->real_soc); + unsigned long sec_finish; + int soc_time, plus_soc; + int temp; - if ((di->soc_counter < 80)) { - di->soc_counter++; - } else { - di->soc_counter = 0; - di->real_soc++; - } + if (di->dsoc < 100) { + if (!di->chrg_finish_base) + di->chrg_finish_base = get_runtime_sec(); + + sec_finish = BASE_TO_SEC(di->chrg_finish_base) + + di->chrg_save_sec; + temp = di->fcc * 3600 / 100; + if (di->ac_online) + soc_time = temp / DSOC_CHRG_FINISH_CURR; + else if (di->usb_online) + soc_time = temp / 450; + + plus_soc = sec_finish / soc_time; + if (sec_finish > soc_time) { + di->dsoc += plus_soc; + di->chrg_finish_base = get_runtime_sec(); + di->chrg_save_sec = 0; } + DBG("<%s>,CHARGE_FINISH:dsoc<100,dsoc=%d\n" + "soc_time=%d, sec_finish=%lu, plus_soc=%d\n", + __func__, di->dsoc, soc_time, sec_finish, plus_soc); } } -static void normal_charge(struct battery_info *di) +static void rk81x_bat_normal_chrg(struct rk81x_battery *di) { - int now_current, soc_time; + int now_current; + u32 soc_time, unit_sec; + int plus_soc = 0; + unsigned long chrg_normal_sec; - now_current = _get_average_current(di); - soc_time = di->fcc*3600/100/div(abs_int(now_current)); /* 1% time; */ - di->temp_soc = _get_soc(di); + now_current = rk81x_bat_get_avg_current(di); + soc_time = di->fcc * 3600 / 100 / div(abs(now_current)); /*1% time*/ - DBG("<%s>. temp_soc = %d, real_soc = %d\n", - __func__, di->temp_soc, di->real_soc); + if (!di->chrg_normal_base) + di->chrg_normal_base = get_runtime_sec(); - if (di->real_soc == di->temp_soc) { - DBG("<%s>. temp_soc == real_soc\n", __func__); - di->temp_soc = _get_soc(di); - } - if ((di->temp_soc != di->real_soc) && (now_current != 0)) { - if (di->temp_soc < di->real_soc + 1) { - DBG("<%s>. temp_soc < real_soc\n", __func__); - di->charge_smooth_time++; - if (di->charge_smooth_time > soc_time*3/2) { - di->real_soc++; - di->charge_smooth_time = 0; - } - di->charge_smooth_status = true; - } + chrg_normal_sec = BASE_TO_SEC(di->chrg_normal_base) + di->chrg_save_sec; + di->rsoc = rk81x_bat_get_rsoc(di); - else if (di->temp_soc > di->real_soc + 1) { - DBG("<%s>. temp_soc > real_soc\n", __func__); - di->charge_smooth_time++; - if (di->charge_smooth_time > soc_time*3/4) { - di->real_soc++; - di->charge_smooth_time = 0; - } - di->charge_smooth_status = true; - - } else if (di->temp_soc == di->real_soc + 1) { - DBG("<%s>. temp_soc == real_soc + 1\n", __func__); - if (di->charge_smooth_status) { - di->charge_smooth_time++; - if (di->charge_smooth_time > soc_time*3/4) { - di->real_soc = di->temp_soc; - di->charge_smooth_time = 0; - di->charge_smooth_status = false; - } + DBG("<%s>. rsoc=%d, dsoc=%d, chrg_st=%d\n", + __func__, di->rsoc, di->dsoc, di->charge_smooth_status); - } else { - di->real_soc = di->temp_soc; + if (di->dsoc == di->rsoc) { + DBG("<%s>. rsoc == dsoc + 1\n", __func__); + di->rsoc = rk81x_bat_get_rsoc(di); + di->chrg_normal_base = get_runtime_sec(); + di->chrg_save_sec = 0; + /*di->charge_smooth_status = false;*/ + } else if (di->rsoc < di->dsoc + 1) { + DBG("<%s>. rsoc < dsoc + 1\n", __func__); + unit_sec = soc_time * 3 / 2; + plus_soc = chrg_normal_sec / unit_sec; + if (chrg_normal_sec > unit_sec) { + di->dsoc += plus_soc; + di->chrg_normal_base = get_runtime_sec(); + di->chrg_save_sec = 0; + } + di->charge_smooth_status = true; + } else if (di->rsoc > di->dsoc + 1) { + DBG("<%s>. rsoc > dsoc + 1\n", __func__); + unit_sec = soc_time * 3 / 4; + plus_soc = chrg_normal_sec / unit_sec; + if (chrg_normal_sec > unit_sec) { + di->dsoc += plus_soc; + di->chrg_normal_base = get_runtime_sec(); + di->chrg_save_sec = 0; + } + di->charge_smooth_status = true; + } else if (di->rsoc == di->dsoc + 1) { + DBG("<%s>. rsoc == dsoc + 1\n", __func__); + if (di->charge_smooth_status) { + unit_sec = soc_time * 3 / 4; + if (chrg_normal_sec > unit_sec) { + di->dsoc = di->rsoc; + di->chrg_normal_base = get_runtime_sec(); di->charge_smooth_status = false; + di->chrg_save_sec = 0; } + } else { + di->dsoc = di->rsoc; + di->chrg_normal_base = get_runtime_sec(); + di->charge_smooth_status = false; + di->chrg_save_sec = 0; } } - DBG("<%s>, temp_soc = %d, real_soc = %d\n", - __func__, di->temp_soc, di->real_soc); - DBG("<%s>, vol_smooth_time = %d, soc_time = %d\n", - __func__, di->charge_smooth_time, soc_time); + DBG("<%s>, rsoc = %d, dsoc = %d, charge_smooth_status = %d\n" + "chrg_normal_sec = %lu, soc_time = %d, plus_soc=%d\n", + __func__, di->rsoc, di->dsoc, di->charge_smooth_status, + chrg_normal_sec, soc_time, plus_soc); } - - -static void rk81x_battery_charge_smooth(struct battery_info *di) +static void rk81x_bat_update_time(struct rk81x_battery *di) { - reset_zero_var(di); - /*calibrate: aim to match finish signal*/ - if (do_term_chrg_calib(di)) - return; + u64 runtime_sec; - /*calibrate: aim to calib error*/ - di->term_chg_cnt = 0; - if (do_ac_charger_emulator(di)) - return; + runtime_sec = get_runtime_sec(); - normal_charge(di); -} - -static void rk81x_battery_display_smooth(struct battery_info *di) -{ - 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)) - rk81x_battery_discharge_smooth(di); - else - rk81x_battery_charge_smooth(di); + /*update by charger type*/ + if (rk81x_chrg_online(di)) + di->plug_out_base = runtime_sec; + else + di->plug_in_base = runtime_sec; - } else if (status == POWER_SUPPLY_STATUS_DISCHARGING) { - rk81x_battery_discharge_smooth(di); - if (di->real_soc == 1) { - di->time2empty++; - if (di->time2empty >= 300) - di->real_soc = 0; - } else { - di->time2empty = 0; - } + /*update by current*/ + if (di->chrg_status != CHARGE_FINISH) { + di->finish_sig_base = runtime_sec; + di->chrg_finish_base = runtime_sec; } -} -/* - * update rsoc by relax voltage - */ -static void resume_relax_calib(struct battery_info *di) -{ - int relax_vol = di->relax_voltage; - int ocv_soc, capacity; + di->plug_in_min = BASE_TO_MIN(di->plug_in_base); + di->plug_out_min = BASE_TO_MIN(di->plug_out_base); + di->finish_sig_min = BASE_TO_MIN(di->finish_sig_base); - ocv_soc = _voltage_to_capacity(di, relax_vol); - capacity = (ocv_soc * di->fcc / 100); - _capacity_init(di, capacity); - di->remain_capacity = _get_realtime_capacity(di); - di->temp_soc = _get_soc(di); - DBG("%s, RSOC=%d, CAP=%d\n", __func__, ocv_soc, capacity); + rk81x_bat_dbg_time_table(di); } -/* condition: - * 1: must do it - * 0: when neccessary - */ -static void resume_vol_calib(struct battery_info *di, int condition) +static int rk81x_bat_get_rsoc_trend(struct rk81x_battery *di) { - int ocv_vol = di->est_ocv_vol; - int ocv_soc = 0, capacity = 0; + int trend_start_cap = di->trend_start_cap; + int remain_cap = di->remain_capacity; + int diff_cap; + int state; - ocv_soc = _voltage_to_capacity(di, ocv_vol); - capacity = (ocv_soc * di->fcc / 100); - if (condition || (abs(ocv_soc-di->temp_soc) >= RSOC_RESUME_ERR)) { - _capacity_init(di, capacity); - di->remain_capacity = _get_realtime_capacity(di); - di->temp_soc = _get_soc(di); - DBG("<%s>, rsoc updated!\n", __func__); + DBG("<%s>. trend_start_cap = %d, diff_cap = %d\n", + __func__, trend_start_cap, diff_cap); + diff_cap = remain_cap - trend_start_cap; + if (abs(diff_cap) >= TREND_CAP_DIFF) { + di->trend_start_cap = di->remain_capacity; + state = (diff_cap > 0) ? TREND_STAT_UP : TREND_STAT_DOWN; + DBG("<%s>. new trend_start_cap=%d", __func__, trend_start_cap); + } else { + state = TREND_STAT_FLAT; } - DBG("<%s>, OCV_VOL=%d,OCV_SOC=%d, CAP=%d\n", - __func__, ocv_vol, ocv_soc, capacity); + + if (di->s2r) + di->trend_start_cap = di->remain_capacity; + + return state; } -/* - * when support HW_ADP_TYPE_DUAL, and at the moment that usb_adp - * and dc_adp are plugined in together, the dc_apt has high priority. - * so we check dc_apt first and return rigth away if it's found. - */ -static enum charger_type_t rk81x_get_adp_type(struct battery_info *di) +static void rk81x_bat_arbitrate_rsoc_trend(struct rk81x_battery *di) { - u8 buf; - enum charger_type_t charger_type = NO_CHARGER; + int state, soc_time; + static int trend_down_cnt, trend_up_cnt; + int trend_cnt_thresd; + int now_current = di->current_avg; - /*check by ic hardware: this check make check work safer*/ - battery_read(di->rk818, VB_MOD_REG, &buf, 1); - if ((buf & PLUG_IN_STS) == 0) - return NO_CHARGER; + trend_cnt_thresd = di->fcc / 100 / TREND_CAP_DIFF; + state = rk81x_bat_get_rsoc_trend(di); + DBG("<%s>. TREND_STAT = %d, trend_down_cnt = %d\n", + __func__, state, trend_down_cnt); + if (di->chrg_status == CHARGE_FINISH) + return; - /*check DC first*/ - if (rk81x_support_adp_type(HW_ADP_TYPE_DC)) { - charger_type = rk81x_get_dc_state(di); - if (charger_type == DC_CHARGER) - return charger_type; + if (state == TREND_STAT_UP) { + rk81x_bat_reset_zero_var(di); + trend_down_cnt = 0; + trend_up_cnt++; + if (trend_up_cnt >= trend_cnt_thresd) { + trend_up_cnt = 0; + di->dischrg_save_sec = 0; + } + } else if (state == TREND_STAT_DOWN) { + trend_up_cnt = 0; + trend_down_cnt++; + if (trend_down_cnt >= trend_cnt_thresd) { + trend_down_cnt = 0; + di->chrg_save_sec = 0; + } + } else {/*state == TREND_STAT_FLAT*/ + soc_time = di->fcc * 3600 / 100 / div(abs(now_current)); + if ((di->chrg_save_sec + 20 > soc_time) && + (trend_up_cnt <= trend_cnt_thresd / 2) && + (now_current >= 0)) + di->chrg_save_sec = 0; + + else if ((di->dischrg_save_sec + 20 > soc_time) && + (trend_down_cnt <= trend_cnt_thresd / 2) && + (now_current < 0)) + di->dischrg_save_sec = 0; } - /*HW_ADP_TYPE_USB*/ - charger_type = rk81x_get_usbac_state(di); - - return charger_type; + DBG("<%s>. state=%d, cnt_thresd=%d, soc_time=%d\n" + "up_cnt=%d, down_cnt=%d\n", + __func__, state, trend_cnt_thresd, soc_time, + trend_up_cnt, trend_down_cnt); +} + +static void rk81x_bat_chrg_smooth(struct rk81x_battery *di) +{ + u32 *ocv_table = di->pdata->battery_ocv; + int delta_soc = di->rsoc - di->dsoc; + int chrg_finish_vol = di->pdata->max_charger_voltagemV; + + if ((di->chrg_status == CHARGE_FINISH || + di->slp_chrg_status == CHARGE_FINISH) && + (di->voltage > chrg_finish_vol - 150)) { + /*clear sleep charge status*/ + di->slp_chrg_status = rk81x_bat_get_chrg_status(di); + di->chrg_emu_base = 0; + di->chrg_normal_base = 0; + di->chrg_term_base = 0; + rk81x_bat_finish_chrg(di); + rk81x_bat_capacity_init(di, di->fcc); + rk81x_bat_capacity_init_post(di); + } else if ((di->ac_online == ONLINE && di->dsoc >= 90) && + ((di->current_avg > DSOC_CHRG_TERM_CURR) || + (di->voltage < ocv_table[18] + 20))) { + di->chrg_emu_base = 0; + di->chrg_normal_base = 0; + di->chrg_finish_base = 0; + rk81x_bat_terminal_chrg(di); + } else if (di->chrg_status != CHARGE_FINISH && + delta_soc >= DSOC_CHRG_FAST_EER_RANGE) { + di->chrg_term_base = 0; + di->chrg_normal_base = 0; + di->chrg_finish_base = 0; + rk81x_bat_emulator_chrg(di); + } else { + di->chrg_emu_base = 0; + di->chrg_term_base = 0; + di->chrg_finish_base = 0; + rk81x_bat_normal_chrg(di); + } } -static void rk81x_sleep_discharge(struct battery_info *di) +static unsigned long rk81x_bat_save_dischrg_sec(struct rk81x_battery *di) { - int delta_cap; - int delta_soc; - int sleep_min; - unsigned long sleep_sec; - int enter_rsoc; - - enter_rsoc = di->real_soc; - sleep_sec = BASE_TO_SEC(di->suspend_time_start); - sleep_min = BASE_TO_MIN(di->suspend_time_start); - delta_cap = di->suspend_cap - di->remain_capacity; - delta_soc = di->suspend_rsoc - _get_soc(di); - di->sum_suspend_cap += delta_cap; - - DBG("<%s>, slp_sec(s)=%lu, slp_min=%d\n" - "delta_cap(s)=%d, delta_soc=%d, sum_cap=%d\n" - "remain_cap=%d, rsoc=%d, dsoc=%d\n" - "relax_vol=%d, vol=%d, curr=%d\n", - __func__, sleep_sec, sleep_min, - delta_cap, delta_soc, di->sum_suspend_cap, - di->remain_capacity, _get_soc(di), di->real_soc, - di->relax_voltage, di->voltage, _get_average_current(di)); - - /*handle rsoc*/ - if ((sleep_min >= 30) && - (di->relax_voltage >= di->voltage)) { - resume_relax_calib(di); - restart_relax(di); + unsigned long dischrg_normal_sec = BASE_TO_SEC(di->dischrg_normal_base); + unsigned long dischrg_emu_sec = BASE_TO_SEC(di->dischrg_emu_base); - /* current_avg < 0: make sure the system is not - * wakeup by charger plugin. - */ + DBG("dischrg_normal_sec=%lu, dischrg_emu_sec=%lu\n", + dischrg_normal_sec, dischrg_emu_sec); - /* even if relax voltage is not caught rightly, realtime voltage - * is quite close to relax voltage, we should not do nothing after - * sleep 30min - */ - } else if ((sleep_min >= 30) && (di->current_avg < 0)) { - resume_vol_calib(di, 1); - } else if ((sleep_min >= 3) && (di->current_avg < 0)) { - resume_vol_calib(di, 0); - } + return (dischrg_normal_sec > dischrg_emu_sec) ? + dischrg_normal_sec : dischrg_emu_sec; +} - /*handle dsoc*/ - delta_soc = di->sum_suspend_cap/(di->fcc/100); - - DBG("<%s>. sum_cap ==> delta_soc = %d\n", __func__, delta_soc); - if (delta_soc > 0) { - if (di->real_soc-(delta_soc*1/3) <= di->temp_soc) - di->real_soc -= (delta_soc*1/3); - else if (di->real_soc-(delta_soc*1/2) < di->temp_soc) - di->real_soc -= (delta_soc*1/2); - else - di->real_soc -= delta_soc; +static unsigned long rk81x_bat_save_chrg_sec(struct rk81x_battery *di) +{ + unsigned long sec1, sec2; + unsigned long chrg_normal_sec = BASE_TO_SEC(di->chrg_normal_base); + unsigned long chrg_term_sec = BASE_TO_SEC(di->chrg_term_base); + unsigned long chrg_emu_sec = BASE_TO_SEC(di->chrg_emu_base); + unsigned long chrg_finish_sec = BASE_TO_SEC(di->chrg_finish_base); - /*di->sum_suspend_cap %= (di->fcc/100);*/ - if (di->real_soc != enter_rsoc) - di->sum_suspend_cap = 0; + sec1 = (chrg_normal_sec > chrg_term_sec) ? + chrg_normal_sec : chrg_term_sec; - } else if (delta_soc < 0) { - di->real_soc--; - } - DBG("<%s>, out: dsoc=%d, rsoc=%d, sum_cap=%d\n", - __func__, di->real_soc, di->temp_soc, di->sum_suspend_cap); + sec2 = (chrg_emu_sec > chrg_finish_sec) ? + chrg_emu_sec : chrg_finish_sec; + DBG("chrg_normal_sec=%lu, chrg_term_sec=%lu\n" + "chrg_emu_sec=%lu, chrg_finish_sec=%lu\n", + chrg_normal_sec, chrg_term_sec, + chrg_emu_sec, chrg_finish_sec); + + return (sec1 > sec2) ? sec1 : sec2; } -static void rk81x_sleep_charge(struct battery_info *di) +static void rk81x_bat_display_smooth(struct rk81x_battery *di) { - unsigned long sleep_soc; - unsigned long sleep_sec; - int delta_cap; - int delta_soc; - int sleep_min; - u8 charge_status = di->charge_status; - - if ((di->suspend_charge_current >= 0) || - (rk81x_get_charge_status(di) == CHARGE_FINISH)) { - sleep_sec = BASE_TO_SEC(di->suspend_time_start); - sleep_min = BASE_TO_MIN(di->suspend_time_start); - delta_cap = di->suspend_cap - di->remain_capacity; - delta_soc = di->suspend_rsoc - _get_soc(di); - - DBG("<%s>, ac=%d, usb=%d, slp_curr=%d\n", - __func__, di->ac_online, di->usb_online, - di->suspend_charge_current); - if (((di->suspend_charge_current < 800) && - (di->ac_online == ONLINE)) || - (charge_status == CHARGE_FINISH)) { - DBG("<%s>,sleep: ac online current < 800\n", __func__); - if (sleep_sec > 0) { - /*default charge current: 1000mA*/ - di->count_sleep_time += sleep_sec; - sleep_soc = 1000*di->count_sleep_time*100 - /3600/div(di->fcc); - DBG("<%s> 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 { - DBG("<%s>, usb charge\n", __func__); - if ((di->temp_soc - di->suspend_rsoc) > 0) - di->real_soc += - (di->temp_soc - di->suspend_rsoc); + if ((di->current_avg >= 0) || (di->chrg_status == CHARGE_FINISH)) { + if (di->current_mode == DISCHRG_MODE) { + di->current_mode = CHRG_MODE; + di->dischrg_save_sec += rk81x_bat_save_dischrg_sec(di); + di->dischrg_normal_base = 0; + di->dischrg_emu_base = 0; + if (di->chrg_status == CHARGE_FINISH) + di->dischrg_save_sec = 0; + if ((di->chrg_status == CHARGE_FINISH) && + (di->dsoc >= 100)) + di->chrg_save_sec = 0; + + DBG("<%s>---dischrg_save_sec = %lu\n", + __func__, di->dischrg_save_sec); } - DBG("<%s>, out: dsoc=%d, rsoc=%d\n", - __func__, di->real_soc, di->temp_soc); + if (!rk81x_chrg_online(di)) { + dev_err(di->dev, "discharge, current error:%d\n", + di->current_avg); + } else { + rk81x_bat_chrg_smooth(di); + di->discharge_smooth_status = true; + } + } else { + if (di->current_mode == CHRG_MODE) { + di->current_mode = DISCHRG_MODE; + di->chrg_save_sec += rk81x_bat_save_chrg_sec(di); + di->chrg_normal_base = 0; + di->chrg_emu_base = 0; + di->chrg_term_base = 0; + di->chrg_finish_base = 0; + DBG("<%s>---chrg_save_sec = %lu\n", + __func__, di->chrg_save_sec); + } + rk81x_bat_dischrg_smooth(di); + di->charge_smooth_status = true; } } /* - * we need flag "sys_wakeup" to make sure that the system is reall power up. - * because there is fake system power up which causes suspend param be cleaned. + * update rsoc by relax voltage */ -static void update_resume_state(struct battery_info *di) +static void rk81x_bat_relax_vol_calib(struct rk81x_battery *di) { - if (di->resume) { - di->resume = false; - di->sys_wakeup = true; - /*update the info first*/ - rk81x_update_battery_info(di); - reset_zero_var(di); - - if (di->sleep_status == POWER_SUPPLY_STATUS_DISCHARGING) - rk81x_sleep_discharge(di); + int relax_vol = di->relax_voltage; + int ocv_soc, capacity; - else if (di->sleep_status == POWER_SUPPLY_STATUS_CHARGING) - rk81x_sleep_charge(di); - } + ocv_soc = rk81x_bat_vol_to_capacity(di, relax_vol); + capacity = (ocv_soc * di->fcc / 100); + rk81x_bat_capacity_init(di, capacity); + di->remain_capacity = rk81x_bat_get_realtime_capacity(di); + di->rsoc = rk81x_bat_get_rsoc(di); + rk81x_bat_capacity_init_post(di); + DBG("%s, RSOC=%d, CAP=%d\n", __func__, ocv_soc, capacity); } -static void rk81x_set_charger_current(struct battery_info *di, - enum charger_type_t charger_type) +/* condition: + * 1: must do it, 0: when necessary + */ +static void rk81x_bat_vol_calib(struct rk81x_battery *di, int condition) { - switch (charger_type) { - case NO_CHARGER: - case USB_CHARGER: - set_charge_current(di, ILIM_450MA); - break; + int ocv_vol = di->est_ocv_vol; + int ocv_soc = 0, capacity = 0; - case AC_CHARGER: - case DC_CHARGER: - set_charge_current(di, di->chg_i_lmt); - break; - default: - set_charge_current(di, ILIM_450MA); + ocv_soc = rk81x_bat_vol_to_capacity(di, ocv_vol); + capacity = (ocv_soc * di->fcc / 100); + if (condition || (abs(ocv_soc-di->rsoc) >= RSOC_RESUME_ERR)) { + rk81x_bat_capacity_init(di, capacity); + di->remain_capacity = rk81x_bat_get_realtime_capacity(di); + di->rsoc = rk81x_bat_get_rsoc(di); + rk81x_bat_capacity_init_post(di); + DBG("<%s>, rsoc updated!\n", __func__); } + DBG("<%s>, OCV_VOL=%d,OCV_SOC=%d, CAP=%d\n", + __func__, ocv_vol, ocv_soc, capacity); } - -static void rk81x_set_power_supply_state(struct battery_info *di, - enum charger_type_t charger_type) +static int rk81x_bat_sleep_dischrg(struct rk81x_battery *di) { - di->usb_online = OFFLINE; - di->ac_online = OFFLINE; - di->dc_online = OFFLINE; + int delta_soc, temp_dsoc; + unsigned long sleep_sec = di->suspend_time_sum; + int power_off_thresd = di->pdata->power_off_thresd; - switch (charger_type) { - case NO_CHARGER: - di->status = POWER_SUPPLY_STATUS_DISCHARGING; - break; + DBG("<%s>, enter: dsoc=%d, rsoc=%d\n" + "relax_vol=%d, vol=%d, sleep_min=%lu\n", + __func__, di->dsoc, di->rsoc, + di->relax_voltage, di->voltage, sleep_sec / 60); - case USB_CHARGER: - di->usb_online = ONLINE; - di->status = POWER_SUPPLY_STATUS_CHARGING; - break; + if (di->relax_voltage >= di->voltage) { + rk81x_bat_relax_vol_calib(di); + rk81x_bat_restart_relax(di); - case DC_CHARGER:/*treat dc as ac*/ - di->dc_online = ONLINE; - case AC_CHARGER: - di->ac_online = ONLINE; - di->status = POWER_SUPPLY_STATUS_CHARGING; - break; - default: - di->status = POWER_SUPPLY_STATUS_DISCHARGING; + /* current_avg < 0: make sure the system is not + * wakeup by charger plugin. + */ + /* even if relax voltage is not caught rightly, realtime voltage + * is quite close to relax voltage, we should not do nothing after + * sleep 30min + */ + } else { + rk81x_bat_vol_calib(di, 1); } - if (di->real_soc >= 100) - di->status = POWER_SUPPLY_STATUS_FULL; -} + /*handle dsoc*/ + if (di->dsoc <= di->rsoc) { + di->sum_suspend_cap = (SLP_CURR_MIN * sleep_sec / 3600); + delta_soc = di->sum_suspend_cap * 100 / di->fcc; + temp_dsoc = di->dsoc - delta_soc; + + if (delta_soc > 0) { + if ((temp_dsoc < di->dsoc) && (di->dsoc < 5)) + di->dsoc--; + else if ((temp_dsoc < 5) && (di->dsoc >= 5)) + di->dsoc = 5; + else if (temp_dsoc > 5) + di->dsoc = temp_dsoc; + } -static void rk81x_check_battery_status(struct battery_info *di) -{ - enum charger_type_t charger_type; + DBG("%s: dsoc<=rsoc, sum_cap=%d==>delta_soc=%d,temp_dsoc=%d\n", + __func__, di->sum_suspend_cap, delta_soc, temp_dsoc); + } else { + /*di->dsoc > di->rsoc*/ + di->sum_suspend_cap = (SLP_CURR_MAX * sleep_sec / 3600); + delta_soc = di->sum_suspend_cap / (di->fcc / 100); + temp_dsoc = di->dsoc - di->rsoc; + if ((di->est_ocv_vol > SLP_DSOC_VOL_THRESD) && + (temp_dsoc > delta_soc)) + di->dsoc -= delta_soc; + else + di->dsoc = di->rsoc; - charger_type = rk81x_get_adp_type(di); - rk81x_set_charger_current(di, charger_type); - rk81x_set_power_supply_state(di, charger_type); -} + DBG("%s: dsoc > rsoc, sum_cap=%d==>delta_soc=%d,temp_dsoc=%d\n", + __func__, di->sum_suspend_cap, delta_soc, temp_dsoc); + } + if (!di->relax_voltage && di->voltage <= power_off_thresd) + di->dsoc = 0; -/* high load: current < 0 with charger in. - * System will not shutdown while dsoc=0% with charging state(ac_online), - * which will cause over discharge, so oppose status before report states. - */ -static void last_check_report(struct battery_info *di) -{ - static u32 time; + if (di->dsoc <= 0) + di->dsoc = 0; + + DBG("<%s>, out: dsoc=%d, rsoc=%d, sum_cap=%d\n", + __func__, di->dsoc, di->rsoc, di->sum_suspend_cap); - if ((di->real_soc == 0) && - (di->status == POWER_SUPPLY_STATUS_CHARGING) && - di->current_avg < 0) { - if (BASE_TO_SEC(time) > 60) - rk81x_set_power_supply_state(di, NO_CHARGER); + return delta_soc; +} - DBG("dsoc=0, time=%ld\n", get_seconds() - time); - DBG("status=%d, ac_online=%d, usb_online=%d\n", - di->status, di->ac_online, di->usb_online); +static int rk81x_bat_sleep_chrg(struct rk81x_battery *di) +{ + int sleep_soc; + unsigned long sleep_sec; + sleep_sec = di->suspend_time_sum; + if (((di->suspend_charge_current < 800) && + (di->ac_online == ONLINE)) || + (di->chrg_status == CHARGE_FINISH)) { + DBG("<%s>,sleep: ac online current < 800\n", __func__); + if (sleep_sec > 0) { + /*default charge current: 1000mA*/ + sleep_soc = SLP_CHRG_CURR * sleep_sec * 100 + / 3600 / div(di->fcc); + } } else { - time = get_seconds(); + DBG("<%s>, usb charge\n", __func__); } + + return sleep_soc; } + /* * only do report when there is a change. * - * if ((di->real_soc == 0) && (di->fg_drv_mode == FG_NORMAL_MODE)): - * when real_soc == 0, we must do report. But it will generate too much android + * if ((di->dsoc == 0) && (di->fg_drv_mode == FG_NORMAL_MODE)): + * when dsoc == 0, we must do report. But it will generate too much android * info when we enter test_power mode without battery, so we add a fg_drv_mode * ajudgement. */ -static void report_power_supply_changed(struct battery_info *di) +static void rk81x_bat_power_supply_changed(struct rk81x_battery *di) { static u32 old_soc; static u32 old_ac_status; @@ -3111,110 +3406,75 @@ static void report_power_supply_changed(struct battery_info *di) bool state_changed; state_changed = false; - if ((di->real_soc == 0) && (di->fg_drv_mode == FG_NORMAL_MODE)) + if ((di->dsoc == 0) && (di->fg_drv_mode == FG_NORMAL_MODE)) state_changed = true; - else if (di->real_soc != old_soc) + else if (di->dsoc != 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) + else if (old_charge_status != di->psy_status) state_changed = true; + if (di->dsoc >= 100 && rk81x_chrg_online(di)) + di->psy_status = POWER_SUPPLY_STATUS_FULL; + if (state_changed) { power_supply_changed(&di->bat); power_supply_changed(&di->usb); power_supply_changed(&di->ac); - old_soc = di->real_soc; + old_soc = di->dsoc; old_ac_status = di->ac_online; old_usb_status = di->usb_online; - old_charge_status = di->status; + old_charge_status = di->psy_status; DBG("<%s>. report: dsoc=%d, rsoc=%d\n", - __func__, di->real_soc, di->temp_soc); - } -} - -static void upd_time_table(struct battery_info *di) -{ - u8 i; - static int old_index; - static int old_min; - u32 time; - int mod = di->real_soc % 10; - int index = di->real_soc / 10; - - if (di->ac_online == ONLINE || di->usb_online == ONLINE) - time = di->charge_min; - else - time = di->discharge_min; - - if ((mod == 0) && (index > 0) && (old_index != index)) { - di->chrg_min[index-1] = time - old_min; - old_min = time; - old_index = index; + __func__, di->dsoc, di->rsoc); } - - for (i = 1; i < 11; i++) - DBG("Time[%d]=%d, ", (i*10), di->chrg_min[i-1]); - DBG("\n"); -} - -/* - * there is a timer inside rk81x to calc how long the battery is in charging - * state. rk81x will close PowerPath inside IC when timer reach, which will - * stop the charging work. we have to reset the corresponding bits to restart - * the timer to avoid that case. - */ -static void rk81x_init_chrg_timer(struct battery_info *di) -{ - u8 buf; - - battery_read(di->rk818, CHRG_CTRL_REG3, &buf, 1); - buf &= ~(0x4); - battery_write(di->rk818, CHRG_CTRL_REG3, &buf, 1); - battery_read(di->rk818, CHRG_CTRL_REG3, &buf, 1); - DBG("%s: clr: CHRG_CTRL_REG3<2> = 0x%x", __func__, buf); - buf |= 0x04; - battery_write(di->rk818, CHRG_CTRL_REG3, &buf, 1); } -static u8 get_cvcc_charge_hour(struct battery_info *di) +static u8 rk81x_bat_get_cvcc_chrg_hour(struct rk81x_battery *di) { u8 hour, buf; - battery_read(di->rk818, CHRG_CTRL_REG2, &buf, 1); + rk81x_bat_read(di, CHRG_CTRL_REG2, &buf, 1); hour = buf & 0x07; - return CHG_CVCC_HOUR[hour]; + return CHRG_CVCC_HOUR[hour]; } /* we have to estimate the charging finish time from now, to decide * whether we should reset the timer or not. */ -static void rk81x_check_chrg_over_time(struct battery_info *di) +static void rk81x_bat_chrg_over_time_check(struct rk81x_battery *di) { u8 cvcc_hour; + int remain_capacity; + + cvcc_hour = rk81x_bat_get_cvcc_chrg_hour(di); + if (di->dsoc < di->rsoc) + remain_capacity = di->dsoc * di->fcc / 100; + else + remain_capacity = di->remain_capacity; - cvcc_hour = get_cvcc_charge_hour(di); - DBG("CHG_TIME(min): %ld, cvcc hour: %d", - BASE_TO_MIN(di->chrg_time_base), cvcc_hour); + DBG("CHRG_TIME(min): %ld, cvcc hour: %d", + BASE_TO_MIN(di->plug_in_base), cvcc_hour); - if (BASE_TO_MIN(di->chrg_time_base) >= (cvcc_hour-2)*60) { - di->chrg_cap2_full = di->fcc - di->remain_capacity; + if (BASE_TO_MIN(di->plug_in_base) >= (cvcc_hour - 2) * 60) { + di->chrg_cap2full = di->fcc - remain_capacity; if (di->current_avg <= 0) di->current_avg = 1; - di->chrg_time2_full = di->chrg_cap2_full*3600/ - div(abs_int(di->current_avg)); + di->chrg_time2full = di->chrg_cap2full * 3600 / + div(abs(di->current_avg)); - DBG("CHG_TIME2FULL(min):%d, chrg_cap2_full=%d, current=%d\n", - SEC_TO_MIN(di->chrg_time2_full), di->chrg_cap2_full, + DBG("CHRG_TIME2FULL(min):%d, chrg_cap2full=%d, current=%d\n", + SEC_TO_MIN(di->chrg_time2full), di->chrg_cap2full, di->current_avg); - if (SEC_TO_MIN(di->chrg_time2_full) > 60) { - rk81x_init_chrg_timer(di); - di->chrg_time_base = get_seconds(); + if (SEC_TO_MIN(di->chrg_time2full) > 60) { + rk81x_bat_init_chrg_timer(di); + di->plug_in_base = get_runtime_sec(); DBG("%s: reset charge timer\n", __func__); } } @@ -3224,126 +3484,242 @@ static void rk81x_check_chrg_over_time(struct battery_info *di) * in case that we will do reboot stress test, we need a special way * to ajust the dsoc. */ -static void rk81x_check_reboot(struct battery_info *di) +static void rk81x_bat_check_reboot(struct rk81x_battery *di) { - u8 rsoc = di->temp_soc; - u8 dsoc = di->real_soc; - u8 status = di->status; + u8 rsoc = di->rsoc; + u8 dsoc = di->dsoc; + u8 status = di->psy_status; u8 cnt; int unit_time; int smooth_time; - battery_read(di->rk818, REBOOT_CNT_REG, &cnt, 1); + rk81x_bat_read(di, REBOOT_CNT_REG, &cnt, 1); cnt++; - unit_time = di->fcc*3600/100/1200;/*1200mA default*/ - smooth_time = cnt*BASE_TO_SEC(di->sys_on_base); + unit_time = di->fcc * 3600 / 100 / 1200;/*1200mA default*/ + smooth_time = cnt * BASE_TO_SEC(di->power_on_base); DBG("%s: cnt:%d, unit:%d, sm:%d, sec:%lu, dsoc:%d, rsoc:%d\n", __func__, cnt, unit_time, smooth_time, - BASE_TO_SEC(di->sys_on_base), dsoc, rsoc); + BASE_TO_SEC(di->power_on_base), dsoc, rsoc); if (((status == POWER_SUPPLY_STATUS_CHARGING) || - (status == POWER_SUPPLY_STATUS_FULL)) && (di->current_avg > 0)) { - DBG("chrg, sm:%d, aim:%d\n", smooth_time, unit_time*3/5); - if ((dsoc < rsoc-1) && (smooth_time > unit_time*3/5)) { + (status == POWER_SUPPLY_STATUS_FULL)) && + (abs(di->current_avg) < 5)) { + DBG("chrg, sm:%d, aim:%d\n", smooth_time, unit_time * 3 / 5); + if ((dsoc < rsoc - 1) && (smooth_time > unit_time * 3 / 5)) { cnt = 0; dsoc++; if (dsoc >= 100) dsoc = 100; - _copy_soc(di, dsoc); + rk81x_bat_save_dsoc(di, dsoc); } } else {/*status == POWER_SUPPLY_STATUS_DISCHARGING*/ - DBG("dischrg, sm:%d, aim:%d\n", smooth_time, unit_time*3/5); - if ((dsoc > rsoc) && (smooth_time > unit_time*3/5)) { + DBG("dischrg, sm:%d, aim:%d\n", smooth_time, unit_time * 3/5); + if ((dsoc > rsoc) && (smooth_time > unit_time * 3/5)) { cnt = 0; dsoc--; if (dsoc <= 0) dsoc = 0; - _copy_soc(di, dsoc); + rk81x_bat_save_dsoc(di, dsoc); } } - copy_reboot_cnt(di, cnt); + rk81x_bat_save_reboot_cnt(di, cnt); +} + +static void rk81x_bat_update_calib_param(struct rk81x_battery *di) +{ + static u32 old_min; + u32 min; + int current_offset; + uint16_t cal_offset; + u8 pcb_offset = DEF_PCB_OFFSET; + + min = BASE_TO_MIN(di->power_on_base); + if ((min % 8) && (old_min != min)) { + old_min = min; + rk81x_bat_get_vol_offset(di); + if (di->pcb_ioffset_updated) + rk81x_bat_read(di, PCB_IOFFSET_REG, &pcb_offset, 1); + + current_offset = rk81x_bat_get_ioffset(di); + rk81x_bat_set_cal_offset(di, current_offset + pcb_offset); + cal_offset = rk81x_bat_get_cal_offset(di); + if (cal_offset < 0x7ff) + rk81x_bat_set_cal_offset(di, di->current_offset + + DEF_PCB_OFFSET); + DBG("<%s>. k=%d, b=%d, cal_offset=%d, i_offset=%d\n", + __func__, di->voltage_k, di->voltage_b, cal_offset, + rk81x_bat_get_ioffset(di)); + } } - -static void rk81x_update_battery_info(struct battery_info *di) +static void rk81x_bat_update_info(struct rk81x_battery *di) { - int round_off_dsoc; + if (di->dsoc > 100) + di->dsoc = 100; + else if (di->dsoc < 0) + di->dsoc = 0; + + /* + * we need update fcc in continuous charging state, if discharge state + * keep at least 2 hour, we decide not to update fcc, so clear the + * fcc update flag: dod0_status. + */ + if (BASE_TO_MIN(di->plug_out_base) > 120) + di->dod0_status = 0; + + di->voltage = rk81x_bat_get_vol(di); + di->current_avg = rk81x_bat_get_avg_current(di); + di->chrg_status = rk81x_bat_get_chrg_status(di); + di->relax_voltage = rk81x_bat_get_relax_vol(di); + di->est_ocv_vol = rk81x_bat_est_ocv_vol(di); + di->est_ocv_soc = rk81x_bat_est_ocv_soc(di); + rk81x_bat_chrg_over_time_check(di); + rk81x_bat_update_calib_param(di); + if (di->chrg_status == CC_OR_CV) + di->enter_finish = true; +#if defined(CONFIG_ARCH_ROCKCHIP) + rk81x_bat_status_check(di);/* ac_online, usb_online, status*/ +#endif + + if (!rk81x_chrg_online(di) && di->s2r) + return; - di->remain_capacity = _get_realtime_capacity(di); - if (di->remain_capacity > di->fcc) - _capacity_init(di, di->fcc); + di->remain_capacity = rk81x_bat_get_realtime_capacity(di); + if (di->remain_capacity > di->fcc) { + rk81x_bat_capacity_init(di, di->fcc); + rk81x_bat_capacity_init_post(di); + di->remain_capacity = di->fcc; + } + + di->rsoc = rk81x_bat_get_rsoc(di); +} + +static int rk81x_bat_update_resume_state(struct rk81x_battery *di) +{ + if (di->slp_psy_status) + return rk81x_bat_sleep_chrg(di); + else + return rk81x_bat_sleep_dischrg(di); +} - if (di->real_soc > 100) - di->real_soc = 100; - else if (di->real_soc < 0) - di->real_soc = 0; +static void rk81x_bat_fcc_flag_check(struct rk81x_battery *di) +{ + u8 ocv_soc, soc_level; + int relax_vol = di->relax_voltage; - if (di->chrg_time_base == 0) - di->chrg_time_base = get_seconds(); + if (relax_vol <= 0) + return; - if (di->sys_on_base == 0) - di->sys_on_base = get_seconds(); + ocv_soc = rk81x_bat_vol_to_capacity(di, relax_vol); + DBG("<%s>. ocv_soc=%d, min=%lu, vol=%d\n", __func__, + ocv_soc, SEC_TO_MIN(di->suspend_time_sum), relax_vol); + + if ((SEC_TO_MIN(di->suspend_time_sum) > 30) && + (di->dod0_status == 0) && + (ocv_soc <= 10)) { + di->dod0_voltage = relax_vol; + di->dod0_capacity = di->temp_nac; + di->adjust_cap = 0; + di->dod0 = ocv_soc; + + if (ocv_soc <= 1) + di->dod0_level = 100; + else if (ocv_soc < 5) + di->dod0_level = 90; + else + di->dod0_level = 80; - if (di->status == POWER_SUPPLY_STATUS_DISCHARGING) { - di->chrg_time_base = get_seconds(); + /* save_soc = di->dod0_level; */ + soc_level = rk81x_bat_get_level(di); + if (soc_level > di->dod0_level) { + di->dod0_status = 0; + } else { + di->dod0_status = 1; + /*time start*/ + di->fcc_update_sec = get_runtime_sec(); + } - /*round off dsoc = 100*/ - round_off_dsoc = (di->remain_capacity+di->fcc/100/2)* - 100/div(di->fcc); - if (round_off_dsoc >= 100 && di->real_soc >= 99) - di->real_soc = 100; - DBG("<%s>. round_off_dsoc = %d", __func__, round_off_dsoc); + dev_info(di->dev, "resume: relax_vol:%d, dod0_cap:%d\n" + "dod0:%d, soc_level:%d: dod0_status:%d\n" + "dod0_level:%d", + di->dod0_voltage, di->dod0_capacity, + ocv_soc, soc_level, di->dod0_status, + di->dod0_level); } +} - di->work_on = 1; - di->voltage = _get_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 = rk81x_get_charge_status(di); - di->relax_voltage = get_relax_voltage(di); - di->temp_soc = _get_soc(di); - di->est_ocv_vol = estimate_bat_ocv_vol(di); - di->est_ocv_soc = estimate_bat_ocv_soc(di); - rk81x_check_battery_status(di);/* ac_online, usb_online, status*/ - rk81x_check_chrg_over_time(di); - update_cal_offset(di); +static void rk81x_chrg_term_mode_set(struct rk81x_battery *di, int mode) +{ + u8 buf; + u8 mask = 0x20; + + rk81x_bat_read(di, CHRG_CTRL_REG3, &buf, 1); + buf &= ~mask; + buf |= mode; + rk81x_bat_write(di, CHRG_CTRL_REG3, &buf, 1); + + dev_info(di->dev, "set charge to %s termination mode\n", + mode ? "digital" : "analog"); +} + +static void rk81x_chrg_term_mode_switch_work(struct work_struct *work) +{ + struct rk81x_battery *di; + + di = container_of(work, struct rk81x_battery, + chrg_term_mode_switch_work.work); + + if (rk81x_chrg_online(di)) + rk81x_chrg_term_mode_set(di, CHRG_TERM_DIG_SIGNAL); + else + rk81x_chrg_term_mode_set(di, CHRG_TERM_ANA_SIGNAL); } static void rk81x_battery_work(struct work_struct *work) { - struct battery_info *di = container_of(work, - struct battery_info, battery_monitor_work.work); + struct rk81x_battery *di; + int ms = TIMER_MS_COUNTS; - update_resume_state(di); - wait_charge_finish_signal(di); - charge_finish_routine(di); + di = container_of(work, struct rk81x_battery, + battery_monitor_work.work); + if (rk81x_chrg_online(di)) { + rk81x_bat_wait_finish_sig(di); + /*rk81x_bat_chrg_finish_routine(di);*/ + } + rk81x_bat_fcc_flag_check(di); + rk81x_bat_arbitrate_rsoc_trend(di); + rk81x_bat_display_smooth(di); + rk81x_bat_update_time(di); + rk81x_bat_update_info(di); + rk81x_bat_rsoc_check(di); + rk81x_bat_power_supply_changed(di); + rk81x_bat_save_dsoc(di, di->dsoc); + rk81x_bat_save_remain_capacity(di, di->remain_capacity); + + rk81x_bat_dbg_dmp_info(di); + + if (!di->early_resume && di->s2r && !di->slp_psy_status) + ms = 30 * TIMER_MS_COUNTS; + else + di->early_resume = 0; - rk81x_battery_display_smooth(di); - rk81x_update_battery_info(di); - rsoc_realtime_calib(di); - last_check_report(di); - report_power_supply_changed(di); - _copy_soc(di, di->real_soc); - _save_remain_capacity(di, di->remain_capacity); + di->s2r = 0; - dump_debug_info(di); - di->queue_work_cnt++; queue_delayed_work(di->wq, &di->battery_monitor_work, - msecs_to_jiffies(TIMER_MS_COUNTS)); + msecs_to_jiffies(ms)); } static void rk81x_battery_charge_check_work(struct work_struct *work) { - struct battery_info *di = container_of(work, - struct battery_info, charge_check_work.work); + struct rk81x_battery *di = container_of(work, + struct rk81x_battery, charge_check_work.work); DBG("rk81x_battery_charge_check_work\n"); - charge_disable_open_otg(di->charge_otg); + rk81x_charge_disable_open_otg(di); } static BLOCKING_NOTIFIER_HEAD(battery_chain_head); @@ -3367,294 +3743,230 @@ int battery_notifier_call_chain(unsigned long val) } EXPORT_SYMBOL_GPL(battery_notifier_call_chain); -static void poweron_lowerpoer_handle(struct battery_info *di) +static void poweron_lowerpoer_handle(struct rk81x_battery *di) { #ifdef CONFIG_LOGO_LOWERPOWER_WARNING - if ((di->real_soc <= 2) && - (di->status == POWER_SUPPLY_STATUS_DISCHARGING)) { + if ((di->dsoc <= 2) && + (di->psy_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) +static int rk81x_bat_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) { - struct battery_info *di = - container_of(nb, struct battery_info, battery_nb); + struct rk81x_battery *di = + container_of(nb, struct rk81x_battery, battery_nb); switch (event) { case 0: DBG(" CHARGE enable\n"); di->charge_otg = 0; + rk81x_bat_clr_bit(di, NT_STS_MSK_REG2, PLUG_IN_INT); + rk81x_bat_clr_bit(di, NT_STS_MSK_REG2, PLUG_OUT_INT); queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(50)); break; - case 1: di->charge_otg = 1; + rk81x_bat_set_bit(di, NT_STS_MSK_REG2, PLUG_IN_INT); + rk81x_bat_set_bit(di, NT_STS_MSK_REG2, PLUG_OUT_INT); queue_delayed_work(di->wq, &di->charge_check_work, msecs_to_jiffies(50)); DBG("charge disable OTG enable\n"); break; - 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) +static irqreturn_t rk81x_vbat_lo_irq(int irq, void *bat) { - pr_info("<%s>lower power warning!\n", __func__); + pr_info("\n------- %s:lower power warning!\n", __func__); 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) +static irqreturn_t rk81x_vbat_plug_in(int irq, void *bat) { - /* 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)); + pr_info("\n------- %s:irq = %d\n", __func__, irq); + rk_send_wakeup_key(); + return IRQ_HANDLED; } -static irqreturn_t rk818_vbat_plug_in(int irq, void *di) +static irqreturn_t rk81x_vbat_plug_out(int irq, void *bat) { - pr_info("\n------- %s:irq = %d\n", __func__, irq); - g_battery->chrg_time_base = get_seconds(); + 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) + +static irqreturn_t rk81x_vbat_charge_ok(int irq, void *bat) { - pr_info("\n-------- %s:irq = %d\n", __func__, irq); - charge_disable_open_otg(0); + struct rk81x_battery *di = (struct rk81x_battery *)bat; + + pr_info("\n---------- %s:irq = %d\n", __func__, irq); + di->finish_sig_base = get_runtime_sec(); rk_send_wakeup_key(); return IRQ_HANDLED; } -static irqreturn_t rk818_vbat_charge_ok(int irq, void *di) +static irqreturn_t rk81x_vbat_dc_det(int irq, void *bat) { - pr_info("---------- %s:irq = %d\n", __func__, irq); rk_send_wakeup_key(); + return IRQ_HANDLED; } -static int rk81x_battery_sysfs_init(struct battery_info *di, struct device *dev) +static int rk81x_bat_sysfs_init(struct rk81x_battery *di) { int ret; int i; - struct kobject *rk818_fg_kobj; - - 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; - } + ret = sysfs_create_file(&di->bat.dev->kobj, + &rk818_bat_attr[i].attr); + if (ret != 0) + dev_err(di->dev, "create battery node(%s) error\n", + rk818_bat_attr[i].attr.name); } - return ret; - -err_sysfs: - power_supply_unregister(&di->ac); - power_supply_unregister(&di->usb); - power_supply_unregister(&di->bat); - return ret; } -static void rk81x_battery_irq_init(struct battery_info *di) +static void rk81x_bat_irq_init(struct rk81x_battery *di) { - int plug_in_irq, plug_out_irq, chg_ok_irq, vb_lo_irq; + int plug_in_irq, plug_out_irq, chrg_ok_irq, vb_lo_irq; int ret; struct rk818 *chip = di->rk818; +#if defined(CONFIG_X86_INTEL_SOFIA) + vb_lo_irq = chip->irq_base + RK818_IRQ_VB_LO; + chrg_ok_irq = chip->irq_base + RK818_IRQ_CHG_OK; + plug_in_irq = chip->irq_base + RK818_IRQ_PLUG_IN; + plug_out_irq = chip->irq_base + RK818_IRQ_PLUG_OUT; +#else 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); + chrg_ok_irq = irq_create_mapping(chip->irq_domain, RK818_IRQ_CHG_OK); +#endif - ret = request_threaded_irq(vb_lo_irq, NULL, rk818_vbat_lo_irq, - IRQF_TRIGGER_HIGH, "rk818_vbatlow", chip); + ret = request_threaded_irq(vb_lo_irq, NULL, rk81x_vbat_lo_irq, + IRQF_TRIGGER_HIGH, "rk818_vbatlow", di); 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); + ret = request_threaded_irq(plug_in_irq, NULL, rk81x_vbat_plug_in, + IRQF_TRIGGER_RISING, "rk81x_vbat_plug_in", + di); 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); + ret = request_threaded_irq(plug_out_irq, NULL, rk81x_vbat_plug_out, + IRQF_TRIGGER_FALLING, "rk81x_vbat_plug_out", + di); 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); + ret = request_threaded_irq(chrg_ok_irq, NULL, rk81x_vbat_charge_ok, + IRQF_TRIGGER_RISING, "rk81x_vbat_charge_ok", + di); if (ret != 0) - dev_err(chip->dev, "chg_ok_irq request failed!\n"); + dev_err(chip->dev, "chrg_ok_irq request failed!\n"); } - -static void rk81x_battery_info_init(struct battery_info *di, struct rk818 *chip) +static void rk81x_bat_info_init(struct rk81x_battery *di, + struct rk818 *chip) { - int fcc_capacity; - u8 i; + unsigned long time_base = get_runtime_sec(); - 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->voltage_old = 0; - di->display_soc = 0; - di->bat_res = 0; - di->resume = false; - di->sys_wakeup = true; - di->status = POWER_SUPPLY_STATUS_DISCHARGING; - di->finish_min = 0; - di->charge_min = 0; - di->discharge_min = 0; - di->charging_time = 0; - di->discharging_time = 0; - di->finish_time = 0; - di->q_dead = 0; - di->q_err = 0; - di->q_shtd = 0; - di->odd_capacity = 0; - di->bat_res = di->rk818->battery_data->sense_resistor_mohm; - di->term_chg_cnt = 0; - di->emu_chg_cnt = 0; - di->zero_cycle = 0; - di->chrg_time_base = 0; - di->sys_on_base = 0; - di->sum_suspend_cap = 0; - di->adjust_cap = 0; - di->first_on_cap = 0; + di->cell.config = di->pdata->cell_cfg; + di->design_capacity = di->pdata->cell_cfg->design_capacity; + di->qmax = di->pdata->cell_cfg->design_qmax; + di->early_resume = 1; + di->psy_status = POWER_SUPPLY_STATUS_DISCHARGING; + di->bat_res = di->pdata->sense_resistor_mohm; di->fg_drv_mode = FG_NORMAL_MODE; - - for (i = 0; i < 10; i++) - di->chrg_min[i] = 0; - - 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; + di->dischrg_algorithm_mode = DISCHRG_NORMAL_MODE; + di->last_zero_mode_dsoc = DEF_LAST_ZERO_MODE_SOC; + di->slp_chrg_status = rk81x_bat_get_chrg_status(di); + di->loader_charged = loader_charged; + di->chrg_finish_base = time_base; + di->power_on_base = time_base; + di->plug_in_base = time_base; + di->plug_out_base = time_base; + di->finish_sig_base = time_base; + di->fcc = rk81x_bat_get_fcc(di); } -/* -static struct of_device_id rk818_battery_of_match[] = { -{ .compatible = "rk818_battery" }, -{ } -}; - -MODULE_DEVICE_TABLE(of, rk818_battery_of_match); -*/ - -/* - * dc_det_pullup_inside: - * - * 0: thers is resistance in the pcb to pull the pin up; - * 1: there is no resistance in the pcb to pull the pin up. - * we have to use inside pullup resistance function, - * so we have to define pinctrl info in DTS and analyze it - */ -static void rk81x_dc_det_init(struct battery_info *di, - struct device_node *np) +static void rk81x_bat_dc_det_init(struct rk81x_battery *di, + struct device_node *np) { struct device *dev = di->dev; - struct rk818 *rk818 = di->rk818; enum of_gpio_flags flags; int ret; - /*thers is resistance in the pcb to pull the pin up*/ - if (!di->dc_det_pullup_inside) - goto out; + di->dc_det_pin = of_get_named_gpio_flags(np, "dc_det_gpio", 0, &flags); + if (di->dc_det_pin == -EPROBE_DEFER) + dev_err(dev, "dc_det_gpio error\n"); + if (gpio_is_valid(di->dc_det_pin)) + di->dc_det_level = (flags & OF_GPIO_ACTIVE_LOW) ? + RK818_DC_IN : RK818_DC_OUT; - /*there is no resistance in the pcb to pull the pin up.*/ - di->pinctrl = devm_pinctrl_get(rk818->dev); - if (IS_ERR(di->pinctrl)) { - dev_err(dev, "No pinctrl used!\n"); - return; - } + di->dc_det_irq = gpio_to_irq(di->dc_det_pin); + ret = request_irq(di->dc_det_irq, rk81x_vbat_dc_det, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rk81x_dc_det", NULL); - /* lookup default state */ - di->pins_default = pinctrl_lookup_state(di->pinctrl, "default"); - if (IS_ERR(di->pins_default)) { - dev_err(dev, "No default pinctrl found!\n"); - } else { - ret = pinctrl_select_state(di->pinctrl, di->pins_default); - if (ret < 0) { - dev_err(dev, "Default pinctrl setting failed!\n"); - } else { -out: - di->dc_det_pin = of_get_named_gpio_flags(np, - "dc_det_gpio", 0, &flags); - if (di->dc_det_pin == -EPROBE_DEFER) - dev_err(dev, "dc_det_gpio error\n"); - if (gpio_is_valid(di->dc_det_pin)) - di->dc_det_level = - (flags & OF_GPIO_ACTIVE_LOW) ? - RK818_DC_IN : RK818_DC_OUT; - } + if (ret != 0) + dev_err(di->dev, "rk818_dc_det_irq request failed!\n"); + enable_irq_wake(di->dc_det_irq); +} + +static int rk81x_bat_get_suspend_sec(struct rk81x_battery *di) +{ + int err; + int delta_sec; + struct rtc_time tm; + struct timespec tv = { + .tv_nsec = NSEC_PER_SEC >> 1, + }; + struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + + err = rtc_read_time(rtc, &tm); + if (err) { + dev_err(rtc->dev.parent, + "hctosys: unable to read the hardware clock\n"); } + err = rtc_valid_tm(&tm); + if (err) { + dev_err(rtc->dev.parent, + "hctosys: invalid date/time\n"); + } + + rtc_tm_to_time(&tm, &tv.tv_sec); + delta_sec = tv.tv_sec - di->suspend_rtc_base.tv_sec; + + return delta_sec; } #ifdef CONFIG_OF -static int rk81x_battery_parse_dt(struct battery_info *di) +static int rk81x_bat_parse_dt(struct rk81x_battery *di) { - struct device_node *regs, *rk818_pmic_np, *test_np; - struct battery_platform_data *data; + struct device_node *np; + struct battery_platform_data *pdata; struct cell_config *cell_cfg; struct ocv_config *ocv_cfg; struct property *prop; @@ -3662,147 +3974,117 @@ static int rk81x_battery_parse_dt(struct battery_info *di) struct device *dev = di->dev; u32 out_value; int length, ret; + size_t size; - rk818_pmic_np = of_node_get(rk818->dev->of_node); - if (!rk818_pmic_np) { - dev_err(dev, "could not find pmic sub-node\n"); - return -EINVAL; - } - - regs = of_find_node_by_name(rk818_pmic_np, "battery"); - if (!regs) { + np = of_find_node_by_name(rk818->dev->of_node, "battery"); + if (!np) { 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"); + pdata = devm_kzalloc(rk818->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) return -ENOMEM; - } cell_cfg = devm_kzalloc(rk818->dev, sizeof(*cell_cfg), GFP_KERNEL); - if (!cell_cfg) { - dev_err(dev, "kzalloc for cell_config failed!\n"); + if (!cell_cfg) 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"); + if (!ocv_cfg) return -ENOMEM; - } - prop = of_find_property(regs, "ocv_table", &length); + prop = of_find_property(np, "ocv_table", &length); if (!prop) { dev_err(dev, "ocv_table not found!\n"); return -EINVAL; } - data->ocv_size = length / sizeof(u32); + pdata->ocv_size = length / sizeof(u32); + if (pdata->ocv_size <= 0) { + dev_err(dev, "invalid ocv table\n"); + return -EINVAL; + } - if (data->ocv_size > 0) { - size_t size = sizeof(*data->battery_ocv) * data->ocv_size; + size = sizeof(*pdata->battery_ocv) * pdata->ocv_size; - 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); - if (ret < 0) - return ret; - } + pdata->battery_ocv = devm_kzalloc(rk818->dev, size, GFP_KERNEL); + if (!pdata->battery_ocv) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "ocv_table", pdata->battery_ocv, + pdata->ocv_size); + if (ret < 0) + return ret; /******************** charger param ****************************/ - ret = of_property_read_u32(regs, "max_charge_currentmA", &out_value); + ret = of_property_read_u32(np, "max_chrg_currentmA", &out_value); if (ret < 0) { - dev_err(dev, "max_charge_currentmA not found!\n"); - out_value = DEFAULT_ICUR; + dev_err(dev, "max_chrg_currentmA not found!\n"); + out_value = DEFAULT_CHRG_CUR; } - data->max_charger_currentmA = out_value; + pdata->max_charger_currentmA = out_value; - ret = of_property_read_u32(regs, "max_charge_ilimitmA", &out_value); + ret = of_property_read_u32(np, "max_input_currentmA", &out_value); if (ret < 0) { dev_err(dev, "max_charger_ilimitmA not found!\n"); - out_value = DEFAULT_ILMT; + out_value = DEFAULT_INPUT_CUR; } - data->max_charger_ilimitmA = out_value; + pdata->max_charger_ilimitmA = out_value; - ret = of_property_read_u32(regs, "bat_res", &out_value); + ret = of_property_read_u32(np, "bat_res", &out_value); if (ret < 0) { dev_err(dev, "bat_res not found!\n"); out_value = DEFAULT_BAT_RES; } - data->sense_resistor_mohm = out_value; + pdata->sense_resistor_mohm = out_value; - ret = of_property_read_u32(regs, "max_charge_voltagemV", &out_value); + ret = of_property_read_u32(np, "max_charge_voltagemV", &out_value); if (ret < 0) { dev_err(dev, "max_charge_voltagemV not found!\n"); - out_value = DEFAULT_VLMT; + out_value = DEFAULT_CHRG_VOL; } - data->max_charger_voltagemV = out_value; + pdata->max_charger_voltagemV = out_value; - ret = of_property_read_u32(regs, "design_capacity", &out_value); + ret = of_property_read_u32(np, "design_capacity", &out_value); 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); + ret = of_property_read_u32(np, "design_qmax", &out_value); 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); + ret = of_property_read_u32(np, "sleep_enter_current", &out_value); if (ret < 0) { dev_err(dev, "sleep_enter_current not found!\n"); - return ret; + out_value = DEFAULT_SLP_ENTER_CUR; } ocv_cfg->sleep_enter_current = out_value; - ret = of_property_read_u32(regs, "sleep_exit_current", &out_value); + ret = of_property_read_u32(np, "sleep_exit_current", &out_value); if (ret < 0) { dev_err(dev, "sleep_exit_current not found!\n"); - return ret; + out_value = DEFAULT_SLP_EXIT_CUR; } ocv_cfg->sleep_exit_current = out_value; - /******************** test power param ****************************/ - test_np = of_find_node_by_name(regs, "test_power"); - if (!regs) { - dev_err(dev, "test-power node not found!\n"); - di->test_chrg_current = DEF_TEST_CURRENT_MA; - di->test_chrg_ilmt = DEF_TEST_ILMT_MA; - } else { - ret = of_property_read_u32(test_np, "test_charge_currentmA", - &out_value); - if (ret < 0) { - dev_err(dev, "test_charge_currentmA not found!\n"); - out_value = DEF_TEST_CURRENT_MA; - } - di->test_chrg_current = out_value; - - ret = of_property_read_u32(test_np, "test_charge_ilimitmA", - &out_value); - if (ret < 0) { - dev_err(dev, "test_charge_ilimitmA not found!\n"); - out_value = DEF_TEST_ILMT_MA; - } - di->test_chrg_ilmt = out_value; + ret = of_property_read_u32(np, "power_off_thresd", &out_value); + if (ret < 0) { + dev_warn(dev, "power_off_thresd not found!\n"); + out_value = PWR_OFF_THRESD; } + pdata->power_off_thresd = out_value; + + of_property_read_u32(np, "chrg_diff_voltagemV", &pdata->chrg_diff_vol); /************* charger support adp types **********************/ - ret = of_property_read_u32(regs, "support_uboot_chrg", &support_uboot_chrg); - ret = of_property_read_u32(regs, "support_usb_adp", &support_usb_adp); - ret = of_property_read_u32(regs, "support_dc_adp", &support_dc_adp); - ret = of_property_read_u32(regs, "dc_det_pullup_inside", &out_value); - if (ret < 0) - out_value = 0; - di->dc_det_pullup_inside = out_value; + ret = of_property_read_u32(np, "support_usb_adp", &support_usb_adp); + ret = of_property_read_u32(np, "support_dc_adp", &support_dc_adp); if (!support_usb_adp && !support_dc_adp) { dev_err(dev, "miss both: usb_adp and dc_adp,default:usb_adp!\n"); @@ -3810,163 +4092,220 @@ static int rk81x_battery_parse_dt(struct battery_info *di) } if (support_dc_adp) - rk81x_dc_det_init(di, regs); + rk81x_bat_dc_det_init(di, np); cell_cfg->ocv = ocv_cfg; - data->cell_cfg = cell_cfg; - rk818->battery_data = data; - - DBG("\n--------- the battery OCV TABLE dump:\n"); - DBG("bat_res :%d\n", data->sense_resistor_mohm); - DBG("max_charge_ilimitmA :%d\n", data->max_charger_ilimitmA); - 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("support_uboot_chrg = %d\n", support_uboot_chrg); - DBG("support_usb_adp = %d\n", support_usb_adp); - DBG("support_dc_adp= %d\n", support_dc_adp); - DBG("test_charge_currentmA = %d\n", di->test_chrg_current); - DBG("test_charge_ilimitmA = %d\n", di->test_chrg_ilmt); - DBG("dc_det_pullup_inside = %d\n", di->dc_det_pullup_inside); - DBG("--------- rk818_battery dt_parse ok.\n"); + pdata->cell_cfg = cell_cfg; + di->pdata = pdata; + + DBG("\nthe battery dts info dump:\n" + "bat_res:%d\n" + "max_input_currentmA:%d\n" + "max_chrg_currentmA:%d\n" + "max_charge_voltagemV:%d\n" + "design_capacity:%d\n" + "design_qmax :%d\n" + "sleep_enter_current:%d\n" + "sleep_exit_current:%d\n" + "support_usb_adp:%d\n" + "support_dc_adp:%d\n" + "power_off_thresd:%d\n", + pdata->sense_resistor_mohm, pdata->max_charger_ilimitmA, + pdata->max_charger_currentmA, pdata->max_charger_voltagemV, + cell_cfg->design_capacity, cell_cfg->design_qmax, + cell_cfg->ocv->sleep_enter_current, + cell_cfg->ocv->sleep_exit_current, + support_usb_adp, support_dc_adp, pdata->power_off_thresd); + return 0; } #else -static int rk81x_battery_parse_dt(struct battery_info *di) +static int rk81x_bat_parse_dt(struct rk81x_battery *di) { return -ENODEV; } #endif - static int rk81x_battery_probe(struct platform_device *pdev) { struct rk818 *chip = dev_get_drvdata(pdev->dev.parent); - struct battery_info *di; + struct rk81x_battery *di; int ret; - DBG("battery driver version %s\n", DRIVER_VERSION); di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); - if (!di) { - dev_err(&pdev->dev, "kzalloc di failed!\n"); + if (!di) return -ENOMEM; - } di->rk818 = chip; di->dev = &pdev->dev; platform_set_drvdata(pdev, di); - ret = rk81x_battery_parse_dt(di); + ret = rk81x_bat_parse_dt(di); if (ret < 0) { dev_err(&pdev->dev, "rk81x battery parse dt failed!\n"); return ret; } - rk81x_battery_info_init(di, chip); + + rk81x_bat_info_init(di, chip); if (!is_rk81x_bat_exist(di)) { - pr_info("not find Li-ion battery, test power mode\n"); - rk81x_battery_charger_init(di); + dev_info(di->dev, "not battery, enter test power mode\n"); di->fg_drv_mode = TEST_POWER_MODE; } - battery_power_supply_init(di); - ret = battery_power_supply_register(di); + ret = rk81x_bat_power_supply_init(di); if (ret) { dev_err(&pdev->dev, "rk81x power supply register failed!\n"); return ret; } - rk81x_battery_irq_init(di); - rk81x_battery_sysfs_init(di, &pdev->dev); + rk81x_bat_irq_init(di); + rk81x_bat_sysfs_init(di); - rk81x_fg_init(di); + rk81x_bat_fg_init(di); wake_lock_init(&di->resume_wake_lock, WAKE_LOCK_SUSPEND, "resume_charging"); - flatzone_voltage_init(di); - rk81x_check_battery_status(di); + rk81x_bat_flatzone_vol_init(di); + +#if defined(CONFIG_X86_INTEL_SOFIA) + di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(di->usb_phy)) { + dev_err(di->dev, "get usb phy failed\n"); + return PTR_ERR(di->usb_phy); + } + di->usb_nb.notifier_call = rk81x_battery_usb_notifier; + ret = usb_register_notifier(di->usb_phy, &di->usb_nb); + if (ret) + dev_err(di->dev, "registr usb phy notification failed\n"); + INIT_DELAYED_WORK(&di->usb_phy_delay_work, + rk81x_battery_usb_notifier_delayed_work); +#endif - di->wq = create_singlethread_workqueue("rk81x-battery-work"); + rk81x_battery_register_fb_notify(di); + di->wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM | WQ_FREEZABLE, + "rk81x-battery-work"); INIT_DELAYED_WORK(&di->battery_monitor_work, rk81x_battery_work); + INIT_DELAYED_WORK(&di->chrg_term_mode_switch_work, + rk81x_chrg_term_mode_switch_work); + queue_delayed_work(di->wq, &di->battery_monitor_work, - msecs_to_jiffies(TIMER_MS_COUNTS*5)); + msecs_to_jiffies(TIMER_MS_COUNTS * 5)); + INIT_DELAYED_WORK(&di->charge_check_work, rk81x_battery_charge_check_work); - di->battery_nb.notifier_call = battery_notifier_call; + di->battery_nb.notifier_call = rk81x_bat_notifier_call; register_battery_notifier(&di->battery_nb); - DBG("rk81x battery probe ok!\n"); + dev_info(di->dev, "battery driver version %s\n", DRIVER_VERSION); return ret; } - -#ifdef CONFIG_PM - static int rk81x_battery_suspend(struct platform_device *dev, pm_message_t state) { - struct battery_info *di = platform_get_drvdata(dev); + struct rk81x_battery *di = platform_get_drvdata(dev); - enable_vbat_low_irq(di); - di->sleep_status = di->status; + di->slp_psy_status = rk81x_chrg_online(di); - /* avoid abrupt wakeup which will clean the variable*/ - if (di->sys_wakeup) { - di->suspend_cap = di->remain_capacity; - di->suspend_rsoc = _get_soc(di); - di->suspend_time_start = get_seconds(); - di->sys_wakeup = false; + di->chrg_status = rk81x_bat_get_chrg_status(di); + di->slp_chrg_status = rk81x_bat_get_chrg_status(di); + di->suspend_charge_current = rk81x_bat_get_avg_current(di); + di->dischrg_save_sec += rk81x_bat_save_dischrg_sec(di); + di->dischrg_normal_base = 0; + di->dischrg_emu_base = 0; + do_gettimeofday(&di->suspend_rtc_base); + /* + * do not modify the g_base_sec + */ + g_base_sec = BASE_TO_SEC(di->power_on_base)+di->power_on_base; + + if (!rk81x_chrg_online(di)) { + di->chrg_save_sec += rk81x_bat_save_chrg_sec(di); + di->chrg_normal_base = 0; + di->chrg_emu_base = 0; + di->chrg_term_base = 0; + di->chrg_finish_base = 0; } - pr_info("rk81x-battery suspend: v=%d ld=%d lr=%d c=%d chg=%d\n", - _get_battery_voltage(di), di->real_soc, _get_soc(di), - _get_average_current(di), di->status); - - cancel_delayed_work(&di->battery_monitor_work); + di->s2r = 0; return 0; } static int rk81x_battery_resume(struct platform_device *dev) { - struct battery_info *di = platform_get_drvdata(dev); + struct rk81x_battery *di = platform_get_drvdata(dev); + int pwroff_thresd = di->pdata->power_off_thresd; + int delta_time; + int time_step; + int delta_soc; - di->resume = true; - disable_vbat_low_irq(di); - queue_delayed_work(di->wq, &di->battery_monitor_work, - msecs_to_jiffies(TIMER_MS_COUNTS/2)); + di->discharge_smooth_status = true; + di->charge_smooth_status = true; + di->s2r = 1; + di->voltage = rk81x_bat_get_vol(di); + di->current_avg = rk81x_bat_get_avg_current(di); + di->relax_voltage = rk81x_bat_get_relax_vol(di); + di->est_ocv_vol = rk81x_bat_est_ocv_vol(di); + di->est_ocv_soc = rk81x_bat_est_ocv_soc(di); + delta_time = rk81x_bat_get_suspend_sec(di); + di->suspend_time_sum += delta_time; + + /* + * do not modify the g_base_sec + */ + if (is_local_clock_reset()) + g_base_sec += delta_time; + else + g_base_sec = 0; + + if (di->slp_psy_status) { + time_step = CHRG_TIME_STEP; + } else { + if (di->voltage <= pwroff_thresd + 50) + time_step = DISCHRG_TIME_STEP_0; + else + time_step = DISCHRG_TIME_STEP_1; + } + + if (di->suspend_time_sum > time_step) { + delta_soc = rk81x_bat_update_resume_state(di); + if (delta_soc) + di->suspend_time_sum = 0; + } - if (di->sleep_status == POWER_SUPPLY_STATUS_CHARGING || - di->real_soc <= 5) - wake_lock_timeout(&di->resume_wake_lock, 5*HZ); + if ((!rk81x_chrg_online(di) && di->voltage <= pwroff_thresd) || + rk81x_chrg_online(di)) + wake_lock_timeout(&di->resume_wake_lock, 5 * HZ); + + DBG("<%s>. current:%d, est_ocv_vol:%d, delta_time:%d, vol:%d\n" + "relax-vol:%d, suspend_time:%ld, online:%d, resume_sec:%lld\n", + __func__, di->current_avg, di->est_ocv_vol, delta_time, + di->voltage, di->relax_voltage, di->suspend_time_sum, + di->slp_psy_status, get_runtime_sec()); - pr_info("rk81x-battery resume: v=%d rv=%d ld=%d lr=%d c=%d chg=%d\n", - _get_battery_voltage(di), get_relax_voltage(di), - di->real_soc, _get_soc(di), _get_average_current(di), - di->status); return 0; } + static int rk81x_battery_remove(struct platform_device *dev) { - struct battery_info *di = platform_get_drvdata(dev); + struct rk81x_battery *di = platform_get_drvdata(dev); cancel_delayed_work_sync(&di->battery_monitor_work); return 0; } + static void rk81x_battery_shutdown(struct platform_device *dev) { - struct battery_info *di = platform_get_drvdata(dev); + struct rk81x_battery *di = platform_get_drvdata(dev); cancel_delayed_work_sync(&di->battery_monitor_work); - if (BASE_TO_MIN(di->sys_on_base) <= REBOOT_INTER_MIN) - rk81x_check_reboot(di); + if (BASE_TO_MIN(di->power_on_base) <= REBOOT_INTER_MIN) + rk81x_bat_check_reboot(di); else - copy_reboot_cnt(di, 0); - DBG("rk818 shutdown!"); + rk81x_bat_save_reboot_cnt(di, 0); + rk81x_chrg_term_mode_set(di, CHRG_TERM_ANA_SIGNAL); } -#endif static struct platform_driver rk81x_battery_driver = { .driver = { diff --git a/include/linux/power/rk818_battery.h b/drivers/power/rk818_battery.h similarity index 96% rename from include/linux/power/rk818_battery.h rename to drivers/power/rk818_battery.h index f9bcc72b0b44..7e358d6c1679 100644 --- a/include/linux/power/rk818_battery.h +++ b/drivers/power/rk818_battery.h @@ -4,10 +4,10 @@ */ #ifndef RK818_BATTERY #define RK818_BATTERY -#include #define VB_MOD_REG 0x21 #define THERMAL_REG 0x22 +#define NT_STS_MSK_REG2 0x4f #define DCDC_ILMAX_REG 0x90 #define CHRG_COMP_REG1 0x99 #define CHRG_COMP_REG2 0x9A @@ -106,6 +106,8 @@ #define PCB_IOFFSET_REG 0xED #define MISC_MARK_REG 0xEE +#define PLUG_IN_INT (0) +#define PLUG_OUT_INT (1) /* gasgauge module enable bit 0: disable 1:enabsle TS_CTRL_REG 0xAC*/ @@ -253,8 +255,9 @@ bit 0: disable 1: enable /* CHRG_CTRL_REG3*/ -#define CHRG_TERM_ANA_SIGNAL (0 << 5) -#define CHRG_TERM_DIG_SIGNAL (1 << 5) +#define CHRG_TERM_ANA_SIGNAL (0 << 5) +#define CHRG_TERM_DIG_SIGNAL (1 << 5) +#define CHRG_TIMER_CCCV_EN (1 << 2) /*CHRG_CTRL_REG2*/ #define CHG_CCCV_4HOUR (0x00) @@ -272,7 +275,10 @@ bit 0: disable 1: enable #define SAMP_TIME_32MIN (0X02<<4) #define SAMP_TIME_48MIN (0X03<<4) -#define DRIVER_VERSION "3.0.0" +#define ADC_CURRENT_MODE (1 << 1) +#define ADC_VOLTAGE_MODE (0 << 1) + +#define DRIVER_VERSION "4.0.0" #define ROLEX_SPEED (100 * 1000) #define CHARGING 0x01 @@ -611,7 +617,8 @@ struct battery_platform_data { unsigned int max_bat_voltagemV; unsigned int low_bat_voltagemV; - + unsigned int chrg_diff_vol; + unsigned int power_off_thresd; unsigned int sense_resistor_mohm; /* twl6032 */ @@ -621,12 +628,12 @@ struct battery_platform_data { struct cell_config *cell_cfg; }; -enum fg_mode_t { +enum fg_mode { FG_NORMAL_MODE = 0,/*work normally*/ TEST_POWER_MODE, /*work without battery*/ }; -enum hw_support_adp_t { +enum hw_support_adp { HW_ADP_TYPE_USB = 0,/*'HW' means:hardware*/ HW_ADP_TYPE_DC, HW_ADP_TYPE_DUAL @@ -636,7 +643,7 @@ enum hw_support_adp_t { /* don't change the following ID, they depend on usb check * interface: dwc_otg_check_dpdm() */ -enum charger_type_t { +enum charger_type { NO_CHARGER = 0, USB_CHARGER, AC_CHARGER, @@ -644,15 +651,32 @@ enum charger_type_t { DUAL_CHARGER }; -enum charger_state_t { +enum charger_state { OFFLINE = 0, ONLINE }; +void kernel_power_off(void); +#if defined(CONFIG_ARCH_ROCKCHIP) int dwc_vbus_status(void); int get_gadget_connect_flag(void); int dwc_otg_check_dpdm(void); -void kernel_power_off(void); void rk_send_wakeup_key(void); +#else + +static inline int get_gadget_connect_flag(void) +{ + return 0; +} + +static inline int dwc_otg_check_dpdm(void) +{ + return 0; +} + +static inline void rk_send_wakeup_key(void) +{ +} +#endif #endif diff --git a/include/linux/mfd/rk818.h b/include/linux/mfd/rk818.h index 02aedd151d08..bdcc3c6d32e4 100755 --- a/include/linux/mfd/rk818.h +++ b/include/linux/mfd/rk818.h @@ -16,7 +16,7 @@ #include #include #include -#include +//#include //#define RK818_START 30 -- 2.34.1