rk818-battery: add calibration of charge and discharge
author许盛飞 <xsf@rock-chips.com>
Mon, 16 Mar 2015 01:20:41 +0000 (09:20 +0800)
committer许盛飞 <xsf@rock-chips.com>
Mon, 16 Mar 2015 01:20:41 +0000 (09:20 +0800)
Signed-off-by: 许盛飞 <xsf@rock-chips.com>
drivers/power/rk818_battery.c

index 3cf0627291c256c7315d5ad7d89e11d998e250e4..5d9174d8e33a87a6e68a1ed4faf7611ff2be763f 100644 (file)
@@ -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;