From 7956759a6acd530c7c3b6bfeeb8aeddbe39f6735 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Mon, 16 Mar 2015 09:20:41 +0800 Subject: [PATCH] rk818-battery: add calibration of charge and discharge MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: 许盛飞 --- drivers/power/rk818_battery.c | 250 ++++++++++++++++++++++++++++++---- 1 file changed, 226 insertions(+), 24 deletions(-) diff --git a/drivers/power/rk818_battery.c b/drivers/power/rk818_battery.c index 3cf0627291c2..5d9174d8e33a 100644 --- a/drivers/power/rk818_battery.c +++ b/drivers/power/rk818_battery.c @@ -45,6 +45,22 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define DEFAULT_ILMT 2000 #define DEFAULT_ICUR 1600 +#define DSOC_DISCHRG_FAST_DEC_SEC 120 /*seconds*/ +#define DSOC_DISCHRG_FAST_EER_RANGE 25 +#define DSOC_CHRG_FAST_CALIB_CURR_MAX 400 /*mA*/ +#define DSOC_CHRG_FAST_INC_SEC 120 /*seconds*/ +#define DSOC_CHRG_FAST_EER_RANGE 25 +#define DSOC_CHRG_EMU_CURR 1000 +#define DSOC_CHG_TERM_CURR 500 + +/*realtime RSOC calib param*/ +#define RSOC_DISCHG_ERR_LOWER 40 +#define RSOC_DISCHG_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 INTERPOLATE_MAX 1000 #define MAX_INT 0x7FFF #define TIME_10MIN_SEC 600 @@ -84,6 +100,9 @@ struct battery_info { 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; @@ -100,6 +119,8 @@ struct battery_info { int est_ocv_vol; int est_ocv_soc; + u8 err_chck_cnt; + int err_soc_sum; int bat_res_update_cnt; int soc_counter; @@ -306,7 +327,6 @@ static int _get_realtime_capacity(struct battery_info *di); static void power_on_save(struct battery_info *di, int voltage); static void _capacity_init(struct battery_info *di, u32 capacity); static void battery_poweron_status_init(struct battery_info *di); -static void power_on_save(struct battery_info *di, int voltage); static void flatzone_voltage_init(struct battery_info *di); static int _get_FCC_capacity(struct battery_info *di); static void _save_FCC_capacity(struct battery_info *di, u32 capacity); @@ -392,6 +412,19 @@ static ssize_t bat_soc_read(struct device *dev, struct device_attribute *attr, c return sprintf(buf, "%d", di->real_soc); } +static ssize_t bat_soc_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + int ret; + struct battery_info *di = g_battery; + + ret = sscanf(buf, "%d", &val); + di->real_soc = val; + + return count; +} static ssize_t bat_temp_soc_read(struct device *dev, struct device_attribute *attr, char *buf) { struct battery_info *di = g_battery; @@ -399,6 +432,24 @@ static ssize_t bat_temp_soc_read(struct device *dev, struct device_attribute *at return sprintf(buf, "%d", di->temp_soc); } +static ssize_t bat_temp_soc_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + int ret; + u32 capacity; + struct battery_info *di = g_battery; + + ret = sscanf(buf, "%d", &val); + capacity = di->fcc*val/100; + _capacity_init(di, capacity); + di->temp_soc = _get_soc(di); + di->remain_capacity = _get_realtime_capacity(di); + + return count; +} + static ssize_t bat_voltage_read(struct device *dev, struct device_attribute *attr, char *buf) { struct battery_info *di = g_battery; @@ -424,8 +475,8 @@ static struct device_attribute rk818_bat_attr[] = { __ATTR(state, 0664, bat_state_read, NULL), __ATTR(regs, 0664, bat_reg_read, NULL), __ATTR(fcc, 0664, bat_fcc_read, NULL), - __ATTR(soc, 0664, bat_soc_read, NULL), - __ATTR(temp_soc, 0664, bat_temp_soc_read, NULL), + __ATTR(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(remain_capacity, 0664, bat_remain_capacity_read, NULL), @@ -1436,7 +1487,7 @@ static int _rsoc_init(struct battery_info *di) DBG("<%s>Not first pwron, real_remain_cap = %d, ocv-remain_cp=%d\n", __func__, remain_capacity, di->temp_nac); /* if plugin, make sure current shtd_time different from last_shtd_time.*/ - if (((otg_status != 0) && (curr_shtd_time > 0) && (last_shtd_time != curr_shtd_time)) || ((curr_shtd_time > 0) && (otg_status == 0))) { + if (last_shtd_time != curr_shtd_time) { if (curr_shtd_time > 30) { remain_capacity = di->temp_nac; @@ -1676,11 +1727,11 @@ static void fg_init(struct battery_info *di) DBG("<%s> :\n" "nac = %d , remain_capacity = %d\n" "OCV_voltage = %d, voltage = %d\n" - "SOC = %d, fcc = %d\n", + "SOC = %d, fcc = %d\n, current=%d", __func__, di->nac, di->remain_capacity, di->voltage_ocv, di->voltage, - di->real_soc, di->fcc); + di->real_soc, di->fcc, di->current_avg); } @@ -1898,17 +1949,139 @@ static int estimate_bat_ocv_soc(struct battery_info *di) return ocv_soc; } + +static void rsoc_dischrg_calib(struct battery_info *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; + + 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)) { + + 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); + + 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) + 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); + di->err_soc_sum = 0; + di->err_chck_cnt = 0; + DBG("<%s>. update: rsoc = %d\n", __func__, ocv_soc); + } + } else { +out: + di->err_chck_cnt = 0; + di->err_soc_sum = 0; + } + +} + +static void rsoc_realtime_calib(struct battery_info *di) +{ + u8 status = di->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); + /* + else + rsoc_chrg_calib(di); + */ + + } else if (status == POWER_SUPPLY_STATUS_DISCHARGING) { + rsoc_dischrg_calib(di); + } +} + +static bool do_ac_charger_emulator(struct battery_info *di) +{ + int delta_soc = di->temp_soc - di->real_soc; + u32 soc_time; + + if ((di->charge_status != CHARGE_FINISH) + && (di->ac_online) + && (delta_soc >= DSOC_CHRG_FAST_EER_RANGE)){ + + soc_time = di->fcc*3600/100/(abs_int(DSOC_CHRG_EMU_CURR)); + 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); + + return true; + } + + return false; +} + +static bool do_term_chrg_cali(struct battery_info *di) +{ + u32 soc_time; + + if (di->ac_online && + (di->real_soc >= 90)&& + (di->current_avg > 600)){ + + 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; + } + + return false; +} + static void voltage_to_soc_discharge_smooth(struct battery_info *di) { int voltage; int now_current, soc_time = -1; int volt_to_soc; + int delta_soc = di->real_soc - di->temp_soc; voltage = di->voltage; now_current = di->current_avg; if (now_current == 0) now_current = 1; - soc_time = di->fcc*3600/100/(abs_int(now_current)); + + 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/(abs_int(now_current)); _voltage_to_capacity(di, 3800); volt_to_soc = di->temp_soc; di->temp_soc = _get_soc(di); @@ -1924,7 +2097,7 @@ static void voltage_to_soc_discharge_smooth(struct battery_info *di) } else if (di->temp_soc > di->real_soc) { DBG("<%s>. di->temp_soc > di->real_soc\n", __func__); di->vol_smooth_time++; - if (di->vol_smooth_time > soc_time*3) { + if (di->vol_smooth_time > soc_time*3/2) { di->real_soc--; di->vol_smooth_time = 0; } @@ -1936,7 +2109,7 @@ static void voltage_to_soc_discharge_smooth(struct battery_info *di) di->real_soc = di->temp_soc; } else { di->vol_smooth_time++; - if (di->vol_smooth_time > soc_time/3) { + if (di->vol_smooth_time > soc_time*3/4) { di->real_soc--; di->vol_smooth_time = 0; } @@ -2046,7 +2219,8 @@ static void wait_charge_finish_signal(struct battery_info *di) static void charge_finish_routine(struct battery_info *di) { - if (di->charge_status == CHARGE_FINISH) { + if ((di->charge_status == CHARGE_FINISH)&& + (di->finish_min >= 1)) { _capacity_init(di, di->fcc); zero_current_calibration(di); @@ -2067,23 +2241,24 @@ static void voltage_to_soc_charge_smooth(struct battery_info *di) int now_current, soc_time; reset_zero_var(di); + /*calibrate: aim to match finish signal*/ + if (do_term_chrg_cali(di)) + return; + + /*calibrate: aim to calib error*/ + di->term_chg_cnt = 0; + if (do_ac_charger_emulator(di)) + return; + + di->emu_chg_cnt = 0; now_current = _get_average_current(di); if (now_current == 0) now_current = 1; + soc_time = di->fcc*3600/100/(abs_int(now_current)); /* 1% time; */ di->temp_soc = _get_soc(di); DBG("<%s>. di->temp_soc = %d, di->real_soc = %d\n", __func__, di->temp_soc, di->real_soc); - /* - if ((di->temp_soc >= 85)&&(di->real_soc >= 85)){ - di->charge_smooth_time++; - - if (di->charge_smooth_time > soc_time/3) { - di->real_soc++; - di->charge_smooth_time = 0; - } - di->charge_smooth_status = true; - }*/ if (di->real_soc == di->temp_soc) { DBG("<%s>. di->temp_soc == di->real_soc\n", __func__); @@ -2094,7 +2269,7 @@ static void voltage_to_soc_charge_smooth(struct battery_info *di) if (di->temp_soc < di->real_soc + 1) { DBG("<%s>. di->temp_soc < di->real_soc\n", __func__); di->charge_smooth_time++; - if (di->charge_smooth_time > soc_time*2) { + if (di->charge_smooth_time > soc_time*3/2) { di->real_soc++; di->charge_smooth_time = 0; } @@ -2104,7 +2279,7 @@ static void voltage_to_soc_charge_smooth(struct battery_info *di) else if (di->temp_soc > di->real_soc + 1) { DBG("<%s>. di->temp_soc > di->real_soc\n", __func__); di->charge_smooth_time++; - if (di->charge_smooth_time > soc_time/3) { + if (di->charge_smooth_time > soc_time*3/4) { di->real_soc++; di->charge_smooth_time = 0; } @@ -2114,7 +2289,7 @@ static void voltage_to_soc_charge_smooth(struct battery_info *di) DBG("<%s>. di->temp_soc == di->real_soc + 1\n", __func__); if (di->charge_smooth_status) { di->charge_smooth_time++; - if (di->charge_smooth_time > soc_time/3) { + 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; @@ -2553,6 +2728,29 @@ static void check_battery_status(struct battery_info *di) #endif } +static void last_check_report(struct battery_info *di) +{ +/* high load: current < 0 with charger in. + * System will not shutdown when dsoc=0% with charging state(ac_online), + * which will cause over discharge, so oppose status. + */ + static u32 time; + + if ((di->real_soc == 0) && (di->status == POWER_SUPPLY_STATUS_CHARGING) + && di->current_avg < 0){ + if (get_seconds() - time > 60){ + di->status = POWER_SUPPLY_STATUS_DISCHARGING; + di->ac_online = 0; + di->usb_online = 0; + } + 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); + + } else + time = get_seconds(); +} + static void report_power_supply_changed(struct battery_info *di) { static u32 old_soc; @@ -2669,7 +2867,8 @@ static void rk_battery_work(struct work_struct *work) rk_battery_display_smooth(di); 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); @@ -2908,6 +3107,9 @@ static void battery_info_init(struct battery_info *di, struct rk818 *chip) 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; + for (i=0; i<10; i++) di->chrg_min[i] = -1; -- 2.34.1