| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2019 MediaTek Inc. |
| * Author Wy Chuang<wy.chuang@mediatek.com> |
| */ |
| |
| #include "mtk_battery.h" |
| |
| #define CAR_MIN_GAP 15 |
| |
| int set_kernel_soc(struct mtk_battery *gm, int _soc) |
| { |
| gm->soc = (_soc + 50) / 100; |
| return 0; |
| } |
| |
| void set_fg_bat_tmp_c_gap(int tmp) |
| { |
| battery_set_property(BAT_PROP_UISOC, tmp); |
| } |
| |
| void set_fg_time(struct mtk_battery *gm, int _time) |
| { |
| struct timespec64 tmp_time_now, end_time; |
| ktime_t ktime, time_now; |
| |
| time_now = ktime_get_boottime(); |
| tmp_time_now = ktime_to_timespec64(time_now); |
| end_time.tv_sec = tmp_time_now.tv_sec + _time; |
| |
| ktime = ktime_set(end_time.tv_sec, end_time.tv_nsec); |
| alarm_start(&gm->tracking_timer, ktime); |
| } |
| |
| int get_d0_c_soc_cust(struct mtk_battery *gm, int value) |
| { |
| //implemented by the customer |
| return value; |
| } |
| |
| int get_uisoc_cust(struct mtk_battery *gm, int value) |
| { |
| //implemented by the customer |
| return value; |
| } |
| |
| int get_ptimrac(void) |
| { |
| return gauge_get_int_property( |
| GAUGE_PROP_PTIM_RESIST); |
| } |
| |
| int get_ptim_vbat(void) |
| { |
| return gauge_get_int_property(GAUGE_PROP_PTIM_BATTERY_VOLTAGE) * 10; |
| } |
| |
| int get_ptim_i(struct mtk_battery *gm) |
| { |
| struct power_supply *psy; |
| union power_supply_propval val; |
| |
| psy = gm->gauge->psy; |
| |
| if (psy != NULL) |
| power_supply_get_property(psy, |
| POWER_SUPPLY_PROP_CURRENT_NOW, &val); |
| |
| return val.intval; |
| } |
| |
| void get_hw_info(void) |
| { |
| gauge_set_property(GAUGE_PROP_HW_INFO, 0); |
| } |
| |
| int get_charger_exist(void) |
| { |
| struct power_supply *psy; |
| union power_supply_propval val; |
| int ret; |
| |
| psy = power_supply_get_by_name("ac"); |
| if (psy != NULL) { |
| ret = power_supply_get_property(psy, |
| POWER_SUPPLY_PROP_ONLINE, &val); |
| if (val.intval == true) |
| return true; |
| } |
| |
| psy = power_supply_get_by_name("usb"); |
| if (psy != NULL) { |
| ret = power_supply_get_property(psy, |
| POWER_SUPPLY_PROP_ONLINE, &val); |
| if (val.intval == true) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int get_charger_status(struct mtk_battery *gm) |
| { |
| int charger_status = 0; |
| |
| if (gm->bs_data.bat_status == |
| POWER_SUPPLY_STATUS_NOT_CHARGING) |
| charger_status = -1; |
| else |
| charger_status = 0; |
| |
| return charger_status; |
| } |
| |
| int get_imix_r(void) |
| { |
| /*todo in alps*/ |
| return 0; |
| } |
| |
| int fg_adc_reset(struct mtk_battery *gm) |
| { |
| battery_set_property(BAT_PROP_FG_RESET, 0); |
| return 0; |
| } |
| |
| static int interpolation(int i1, int b1, int i2, int b2, int i) |
| { |
| int ret; |
| |
| ret = (b2 - b1) * (i - i1) / (i2 - i1) + b1; |
| return ret; |
| } |
| |
| int fg_get_saddles(struct mtk_battery *gm) |
| { |
| return gm->fg_table_cust_data.fg_profile[0].size; |
| } |
| |
| struct fuelgauge_profile_struct *fg_get_profile( |
| struct mtk_battery *gm, int temperature) |
| { |
| int i; |
| struct fuel_gauge_table_custom_data *ptable; |
| |
| ptable = &gm->fg_table_cust_data; |
| for (i = 0; i < ptable->active_table_number; i++) |
| if (ptable->fg_profile[i].temperature == temperature) |
| return &ptable->fg_profile[i].fg_profile[0]; |
| |
| if (ptable->temperature_tb0 == temperature) |
| return &ptable->fg_profile_temperature_0[0]; |
| |
| if (ptable->temperature_tb1 == temperature) |
| return &ptable->fg_profile_temperature_1[0]; |
| |
| bm_debug("[%s]: no table for %d\n", |
| __func__, |
| temperature); |
| |
| return NULL; |
| } |
| |
| int fg_check_temperature_order(struct mtk_battery *gm, |
| int *is_ascending, int *is_descending) |
| { |
| int i; |
| struct fuel_gauge_table_custom_data *ptable; |
| |
| ptable = &gm->fg_table_cust_data; |
| *is_ascending = 0; |
| *is_descending = 0; |
| /* is ascending*/ |
| |
| bm_debug("act:%d table: %d %d %d %d %d %d %d %d %d %d\n", |
| ptable->active_table_number, |
| ptable->fg_profile[0].temperature, |
| ptable->fg_profile[1].temperature, |
| ptable->fg_profile[2].temperature, |
| ptable->fg_profile[3].temperature, |
| ptable->fg_profile[4].temperature, |
| ptable->fg_profile[5].temperature, |
| ptable->fg_profile[6].temperature, |
| ptable->fg_profile[7].temperature, |
| ptable->fg_profile[8].temperature, |
| ptable->fg_profile[9].temperature); |
| |
| for (i = 0; i < ptable->active_table_number - 1; i++) { |
| if (ptable->fg_profile[i].temperature > |
| ptable->fg_profile[i + 1].temperature) |
| break; |
| *is_ascending = 1; |
| *is_descending = 0; |
| } |
| |
| /* is descending*/ |
| for (i = 0; i < ptable->active_table_number - 1; i++) { |
| if (ptable->fg_profile[i].temperature < |
| ptable->fg_profile[i + 1].temperature) |
| break; |
| *is_ascending = 0; |
| *is_descending = 1; |
| } |
| |
| bm_debug("active_table_no is %d, %d %d\n", |
| ptable->active_table_number, |
| *is_ascending, |
| *is_descending); |
| for (i = 0; i < ptable->active_table_number; i++) { |
| bm_debug("table[%d]:%d\n", |
| i, |
| ptable->fg_profile[i].temperature); |
| } |
| |
| if (*is_ascending == 0 && *is_descending == 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| void fgr_construct_battery_profile(struct mtk_battery *gm, int table_idx) |
| { |
| struct fuelgauge_profile_struct *low_profile_p = NULL; |
| struct fuelgauge_profile_struct *high_profile_p = NULL; |
| struct fuelgauge_profile_struct *temp_profile_p = NULL; |
| int low_temp = 0, high_temp = 0, temperature = 0; |
| int i, saddles; |
| int low_pseudo1 = 0, high_pseudo1 = 0; |
| int low_pseudo100 = 0, high_pseudo100 = 0; |
| int low_qmax = 0, high_qmax = 0, low_qmax_h = 0, high_qmax_h = 0; |
| int low_shutdown_zcv = 0, high_shutdown_zcv = 0; |
| int is_ascending, is_descending; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| temperature = algo->last_temp; |
| temp_profile_p = fg_get_profile(gm, table_idx); |
| |
| if (temp_profile_p == NULL) { |
| bm_debug("[FGADC] fg_get_profile : create table fail !\n"); |
| return; |
| } |
| |
| if (fg_check_temperature_order(gm, &is_ascending, &is_descending)) { |
| bm_err("[FGADC] fg_check_temperature_order : t0~t3 setting error !\n"); |
| return; |
| } |
| |
| for (i = 1; i < ptable->active_table_number; i++) { |
| if (is_ascending) { |
| if (temperature <= ptable->fg_profile[i].temperature) |
| break; |
| } else { |
| if (temperature >= ptable->fg_profile[i].temperature) |
| break; |
| } |
| } |
| |
| if (i > (ptable->active_table_number - 1)) |
| i = ptable->active_table_number - 1; |
| |
| if (is_ascending) { |
| low_profile_p = |
| fg_get_profile(gm, |
| ptable->fg_profile[i - 1].temperature); |
| high_profile_p = |
| fg_get_profile(gm, |
| ptable->fg_profile[i].temperature); |
| low_temp = |
| ptable->fg_profile[i - 1].temperature; |
| high_temp = |
| ptable->fg_profile[i].temperature; |
| low_pseudo1 = |
| ptable->fg_profile[i - 1].pseudo1; |
| high_pseudo1 = |
| ptable->fg_profile[i].pseudo1; |
| low_pseudo100 = |
| ptable->fg_profile[i - 1].pseudo100; |
| high_pseudo100 = |
| ptable->fg_profile[i].pseudo100; |
| low_qmax = ptable->fg_profile[i - 1].q_max; |
| high_qmax = ptable->fg_profile[i].q_max; |
| low_qmax_h = |
| ptable->fg_profile[i - 1].q_max_h_current; |
| high_qmax_h = |
| ptable->fg_profile[i].q_max_h_current; |
| low_shutdown_zcv = |
| ptable->fg_profile[i - 1].shutdown_hl_zcv; |
| high_shutdown_zcv = |
| ptable->fg_profile[i].shutdown_hl_zcv; |
| } else { |
| low_profile_p = |
| fg_get_profile(gm, ptable->fg_profile[i].temperature); |
| high_profile_p = |
| fg_get_profile(gm, |
| ptable->fg_profile[i - 1].temperature); |
| low_temp = ptable->fg_profile[i].temperature; |
| high_temp = ptable->fg_profile[i - 1].temperature; |
| low_pseudo1 = ptable->fg_profile[i].pseudo1; |
| high_pseudo1 = ptable->fg_profile[i - 1].pseudo1; |
| low_pseudo100 = ptable->fg_profile[i].pseudo100; |
| high_pseudo100 = ptable->fg_profile[i - 1].pseudo100; |
| low_qmax = ptable->fg_profile[i].q_max; |
| high_qmax = ptable->fg_profile[i - 1].q_max; |
| low_qmax_h = ptable->fg_profile[i].q_max_h_current; |
| high_qmax_h = ptable->fg_profile[i - 1].q_max_h_current; |
| low_shutdown_zcv = ptable->fg_profile[i].shutdown_hl_zcv; |
| high_shutdown_zcv = ptable->fg_profile[i - 1].shutdown_hl_zcv; |
| } |
| if (temperature < low_temp) |
| temperature = low_temp; |
| else if (temperature > high_temp) |
| temperature = high_temp; |
| |
| if (table_idx == 255) |
| algo->T_table = temperature; |
| if (table_idx == 254) |
| algo->T_table_c = temperature; |
| |
| saddles = fg_get_saddles(gm); |
| for (i = 0; i < saddles; i++) { |
| temp_profile_p[i].mah = |
| interpolation(low_temp, low_profile_p[i].mah, |
| high_temp, high_profile_p[i].mah, temperature); |
| temp_profile_p[i].voltage = |
| interpolation(low_temp, low_profile_p[i].voltage, |
| high_temp, high_profile_p[i].voltage, temperature); |
| temp_profile_p[i].resistance = |
| interpolation(low_temp, low_profile_p[i].resistance, |
| high_temp, high_profile_p[i].resistance, temperature); |
| temp_profile_p[i].charge_r.rdc[0] = |
| interpolation(low_temp, low_profile_p[i].charge_r.rdc[0], |
| high_temp, high_profile_p[i].charge_r.rdc[0], temperature); |
| |
| } |
| |
| if (table_idx == ptable->temperature_tb0) { |
| if (pdata->pseudo1_en == true) |
| algo->batterypseudo1_h = interpolation( |
| low_temp, |
| low_pseudo1, |
| high_temp, |
| high_pseudo1, |
| temperature); |
| |
| if (pdata->pseudo100_en == true) |
| algo->batterypseudo100 = interpolation( |
| low_temp, |
| low_pseudo100, |
| high_temp, |
| high_pseudo100, |
| temperature); |
| |
| bm_debug("[Profile_Table]pseudo1_en:[%d] lowT %d %d %d lowPs1 %d highPs1 %d batterypseudo1_h [%d]\n", |
| pdata->pseudo1_en, low_temp, |
| high_temp, temperature, |
| low_pseudo1, high_pseudo1, |
| algo->batterypseudo1_h); |
| bm_debug("[Profile_Table]pseudo100_en:[%d] %d lowT %d %d %d low100 %d %d [%d]\n", |
| pdata->pseudo100_en, pdata->pseudo100_en_dis, |
| low_temp, high_temp, temperature, |
| low_pseudo100, high_pseudo100, |
| algo->batterypseudo100); |
| |
| /* |
| * low_qmax and High_qmax need to do |
| * UNIT_TRANS_10 from "1 mAHR" to "0.1 mAHR" |
| */ |
| algo->qmax_t_0ma_h = interpolation( |
| low_temp, UNIT_TRANS_10 * low_qmax, |
| high_temp, UNIT_TRANS_10 * high_qmax, |
| temperature); |
| algo->qmax_t_Nma_h = interpolation( |
| low_temp, UNIT_TRANS_10 * low_qmax_h, |
| high_temp, UNIT_TRANS_10 * high_qmax_h, |
| temperature); |
| |
| bm_debug("[Profile_Table]lowT %d %d %d lowQ %d %d qmax_t_0ma_h [%d]\n", |
| low_temp, high_temp, temperature, |
| UNIT_TRANS_10 * low_qmax, |
| UNIT_TRANS_10 * high_qmax, |
| algo->qmax_t_0ma_h); |
| bm_debug("[Profile_Table]lowT %d %d %d lowQh %d %d qmax_t_Nma_h [%d]\n", |
| low_temp, high_temp, temperature, |
| UNIT_TRANS_10 * low_qmax_h, |
| UNIT_TRANS_10 * high_qmax_h, |
| algo->qmax_t_Nma_h); |
| |
| algo->shutdown_hl_zcv = interpolation( |
| low_temp, UNIT_TRANS_10 * low_shutdown_zcv, |
| high_temp, UNIT_TRANS_10 * high_shutdown_zcv, |
| temperature); |
| |
| bm_debug("[Profile_Table]lowT %d %d %d LowShutZCV %d HighShutZCV %d shutdown_hl_zcv [%d]\n", |
| low_temp, high_temp, temperature, |
| UNIT_TRANS_10 * low_shutdown_zcv, |
| UNIT_TRANS_10 * high_shutdown_zcv, |
| algo->shutdown_hl_zcv); |
| |
| } else if (table_idx == ptable->temperature_tb1) { |
| /* |
| * low_qmax and High_qmax need to do |
| * UNIT_TRANS_10 from "1 mAHR" to "0.1 mAHR" |
| */ |
| algo->qmax_t_0ma_h_tb1 = interpolation( |
| low_temp, UNIT_TRANS_10 * low_qmax, |
| high_temp, UNIT_TRANS_10 * high_qmax, |
| temperature); |
| algo->qmax_t_Nma_h_tb1 = interpolation( |
| low_temp, UNIT_TRANS_10 * low_qmax_h, |
| high_temp, UNIT_TRANS_10 * high_qmax_h, |
| temperature); |
| |
| bm_debug("[Profile_Table]lowT %d %d %d lowQ %d %d qmax_t_0ma_h [%d]\n", |
| low_temp, high_temp, temperature, |
| UNIT_TRANS_10 * low_qmax, |
| UNIT_TRANS_10 * high_qmax, |
| algo->qmax_t_0ma_h_tb1); |
| bm_debug("[Profile_Table]lowT %d %d %d lowQh %d %d qmax_t_Nma_h [%d]\n", |
| low_temp, high_temp, temperature, |
| UNIT_TRANS_10 * low_qmax_h, |
| UNIT_TRANS_10 * high_qmax_h, |
| algo->qmax_t_Nma_h_tb1); |
| } |
| |
| bm_debug("[Profile_Table]T_table %d T_table_c %d %d %d is_ascend %d %d\n", |
| algo->T_table, algo->T_table_c, pdata->pseudo1_en, |
| pdata->pseudo100_en, is_ascending, is_descending); |
| bm_debug("[Profile_Table]Pseudo1_h %d %d, Qmax_T_0mA_H %d,%d qmax_t_0ma_h_tb1 %d %d\n", |
| algo->batterypseudo1_h, |
| algo->batterypseudo100, |
| algo->qmax_t_0ma_h, |
| algo->qmax_t_Nma_h, |
| algo->qmax_t_0ma_h_tb1, |
| algo->qmax_t_Nma_h_tb1); |
| } |
| |
| void fgr_construct_table_by_temp( |
| struct mtk_battery *gm, bool update, int table_idx) |
| { |
| int fg_temp; |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| fg_temp = force_get_tbat(gm, true); |
| if (fg_temp != algo->last_temp || update == true) { |
| bm_err("[%s] tempture from(%d)to(%d) Tb:%d", |
| __func__, |
| algo->last_temp, fg_temp, table_idx); |
| algo->last_temp = fg_temp; |
| fgr_construct_battery_profile(gm, table_idx); |
| } |
| } |
| |
| void fg_construct_battery_profile_by_qmax(struct mtk_battery *gm, |
| int qmax, int table_index) |
| { |
| int i; |
| struct fuelgauge_profile_struct *profile_p; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| profile_p = fg_get_profile(gm, table_index); |
| |
| if (table_index == ptable->temperature_tb0) { |
| algo->qmax_t_0ma = qmax; |
| |
| for (i = 0; i < 100; i++) |
| profile_p[i].percentage = |
| profile_p[i].mah * 10000 / algo->qmax_t_0ma; |
| } else if (table_index == ptable->temperature_tb1) { |
| algo->qmax_t_0ma_tb1 = qmax; |
| for (i = 0; i < 100; i++) |
| profile_p[i].percentage = |
| profile_p[i].mah * 10000 / algo->qmax_t_0ma_tb1; |
| } |
| |
| bm_debug("[%s] qmax:%d qmax_t_0ma:%d\n", |
| __func__, |
| qmax, algo->qmax_t_0ma); |
| } |
| |
| void fg_construct_battery_profile_by_vboot(struct mtk_battery *gm, |
| int _vboot, int table_index) |
| { |
| int i, j; |
| struct fuelgauge_profile_struct *profile_p; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| profile_p = fg_get_profile(gm, table_index); |
| |
| for (j = 0; j < 100; j++) |
| if (profile_p[j].voltage < _vboot) |
| break; |
| |
| if (table_index == ptable->temperature_tb0) { |
| if (j == 0) { |
| algo->qmax_t_0ma = profile_p[0].mah; |
| } else if (j >= 100) { |
| algo->qmax_t_0ma = profile_p[99].mah; |
| } else { |
| /*qmax_t_0ma = profile_p[j].mah;*/ |
| algo->qmax_t_0ma = interpolation( |
| profile_p[j].voltage, |
| profile_p[j].mah, |
| profile_p[j-1].voltage, |
| profile_p[j-1].mah, |
| _vboot); |
| } |
| |
| if (algo->qmax_t_0ma < 3000) { |
| bm_err("[ERR][%s]index %d idx:%d _vboot:%d %d qmax_t_0ma:[%d => 3000]\n", |
| __func__, |
| table_index, j, |
| _vboot, profile_p[j].voltage, algo->qmax_t_0ma); |
| } |
| |
| if (algo->qmax_t_0ma > 50000) { |
| bm_err("[ERR][%s]index %d idx:%d _vboot:%d %d qmax_t_0ma:[%d => 50000]\n", |
| __func__, |
| table_index, j, |
| _vboot, profile_p[j].voltage, algo->qmax_t_0ma); |
| } |
| |
| for (i = 0; i < 100; i++) |
| profile_p[i].percentage = |
| profile_p[i].mah * 10000 / algo->qmax_t_0ma; |
| |
| } else if (table_index == ptable->temperature_tb1) { |
| if (j == 0) { |
| algo->qmax_t_0ma_tb1 = profile_p[0].mah; |
| } else if (j >= 100) { |
| algo->qmax_t_0ma_tb1 = profile_p[99].mah; |
| } else { |
| /*qmax_t_0ma = profile_p[j].mah;*/ |
| algo->qmax_t_0ma_tb1 = |
| interpolation( |
| profile_p[j].voltage, |
| profile_p[j].mah, |
| profile_p[j-1].voltage, |
| profile_p[j-1].mah, _vboot); |
| } |
| |
| if (algo->qmax_t_0ma_tb1 < 3000) { |
| bm_err("[ERR][%s]index %d idx:%d _vboot:%d %d qmax_t_0ma_tb1:[%d => 3000]\n", |
| __func__, |
| table_index, j, |
| _vboot, profile_p[j].voltage, |
| algo->qmax_t_0ma_tb1); |
| } |
| |
| if (algo->qmax_t_0ma_tb1 > 50000) { |
| bm_err("[ERR][%s]index %d idx:%d _vboot:%d %d qmax_t_0ma_tb1:[%d => 50000]\n", |
| __func__, |
| table_index, j, |
| _vboot, profile_p[j].voltage, |
| algo->qmax_t_0ma_tb1); |
| } |
| |
| for (i = 0; i < 100; i++) |
| profile_p[i].percentage = |
| profile_p[i].mah * 10000 / algo->qmax_t_0ma_tb1; |
| } |
| |
| if (table_index == ptable->temperature_tb1) { |
| bm_debug("[%s]index %d idx:%d _vboot:%d %d qmax_t_0ma_tb1:%d\n", |
| __func__, |
| table_index, j, _vboot, |
| profile_p[j].voltage, algo->qmax_t_0ma_tb1); |
| } else { |
| bm_debug("[%s]index %d idx:%d _vboot:%d %d qmax_t_0ma:%d\n", |
| __func__, |
| table_index, j, _vboot, |
| profile_p[j].voltage, algo->qmax_t_0ma); |
| } |
| } |
| |
| static int fg_compensate_battery_voltage_from_low( |
| struct mtk_battery *gm, |
| int oriv, int curr, int tablei) |
| { |
| int fg_volt, fg_volt_withIR, ret_compensate_value = 0; |
| int hit_h_percent = 0, hit_l_percent = 0; |
| struct fuelgauge_profile_struct *profile_p; |
| int i = 0, size, high = 0; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| profile_p = fg_get_profile(gm, tablei); |
| if (profile_p == NULL) { |
| bm_err("[ERR][%s] fail ,profile_p=null!\n", |
| __func__); |
| return 0; |
| } |
| size = fg_get_saddles(gm); |
| |
| bm_debug("[%s]size:%d oriv=%d I:%d\n", |
| __func__, |
| size, oriv, curr); |
| |
| for (; size > 0; size--) { |
| high = size-1; |
| if (high >= 1) { |
| if (profile_p[high-1].percentage < 10000) { |
| bm_debug("[%s]find high=%d,[%d][%d]\n", |
| __func__, |
| high, profile_p[high].percentage, |
| profile_p[high-1].percentage); |
| break; |
| } |
| } |
| } |
| |
| for (; high > 0; high--) { |
| if (high >= 1) { |
| fg_volt = profile_p[high-1].voltage; |
| algo->fg_resistance_bat = profile_p[high-1].resistance; |
| ret_compensate_value = |
| (curr * (algo->fg_resistance_bat * |
| algo->DC_ratio / 100 + pdata->r_fg_value + |
| pdata->fg_meter_resistance)) / 1000; |
| ret_compensate_value = (ret_compensate_value + 5) / 10; |
| fg_volt_withIR = fg_volt + ret_compensate_value; |
| if (fg_volt_withIR > oriv) { |
| hit_h_percent = profile_p[high].percentage; |
| hit_l_percent = profile_p[high-1].percentage; |
| bm_err("[%s]h_percent=[%d,%d],high=%d,fg_volt_withIR=%d > oriv=%d\n", |
| __func__, |
| hit_h_percent, hit_l_percent, |
| high, fg_volt_withIR, oriv); |
| break; |
| } |
| } else { |
| bm_err("[ERR][%s] can't find available voltage!!!\n", |
| __func__); |
| fg_volt = profile_p[0].voltage; |
| } |
| } |
| |
| /* check V+IR > orig_v every 0.1% */ |
| for (i = hit_h_percent; i >= hit_l_percent; i = i-10) { |
| fg_volt = interpolation( |
| profile_p[high-1].percentage, |
| profile_p[high-1].voltage, |
| profile_p[high].percentage, |
| profile_p[high].voltage, i); |
| |
| algo->fg_resistance_bat = interpolation( |
| profile_p[high-1].percentage, |
| profile_p[high-1].resistance, |
| profile_p[high].percentage, |
| profile_p[high].resistance, i); |
| |
| ret_compensate_value = |
| (curr * (algo->fg_resistance_bat * algo->DC_ratio |
| / 100 + pdata->r_fg_value + |
| pdata->fg_meter_resistance)) / 1000; |
| ret_compensate_value = |
| (ret_compensate_value + 5) / 10; |
| fg_volt_withIR = fg_volt + ret_compensate_value; |
| |
| if (fg_volt_withIR > oriv) { |
| bm_err("[%s]fg_volt=%d,%d,IR=%d,orig_v:%d,+IR=%d,percent=%d,\n", |
| __func__, |
| fg_volt, high, |
| ret_compensate_value, oriv, |
| fg_volt_withIR, i); |
| return fg_volt; |
| } |
| } |
| |
| bm_err("[ERR][%s] should not reach here!!!!!!\n", |
| __func__); |
| return fg_volt; |
| } |
| |
| void fgr_construct_vboot(struct mtk_battery *gm, int table_idx) |
| { |
| int iboot = 0; |
| int rac = get_ptimrac(); |
| int ptim_vbat; |
| int ptim_i; |
| int vboot_t = 0; |
| int curr_temp = force_get_tbat(gm, true); |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| ptim_i = get_ptim_i(gm); |
| ptim_vbat = gauge_get_int_property(GAUGE_PROP_PTIM_BATTERY_VOLTAGE) |
| * 10; |
| |
| bm_debug("[%s] idx %d T_NEW %d T_table %d T_table_c %d qmax_sel %d\n", |
| __func__, |
| table_idx, curr_temp, |
| algo->T_table, algo->T_table_c, |
| pdata->qmax_sel); |
| |
| if (pdata->iboot_sel == 0) |
| iboot = ptable->fg_profile[0].pon_iboot; |
| else |
| iboot = pdata->shutdown_system_iboot; |
| |
| if (pdata->qmax_sel == 0) { |
| algo->vboot = |
| ptable->fg_profile[0].pmic_min_vol |
| + iboot * rac / 10000; |
| if (table_idx == ptable->temperature_tb0) |
| fg_construct_battery_profile_by_qmax(gm, |
| algo->qmax_t_0ma_h, table_idx); |
| if (table_idx == ptable->temperature_tb1) |
| fg_construct_battery_profile_by_qmax(gm, |
| algo->qmax_t_0ma_h_tb1, table_idx); |
| } else if (pdata->qmax_sel == 1) { |
| vboot_t = |
| ptable->fg_profile[0].pmic_min_vol |
| + iboot * rac / 10000; |
| |
| fg_construct_battery_profile_by_vboot(gm, vboot_t, table_idx); |
| if (table_idx == 255) { |
| algo->vboot = |
| fg_compensate_battery_voltage_from_low(gm, |
| ptable->fg_profile[0].pmic_min_vol, |
| (0 - iboot), table_idx); |
| fg_construct_battery_profile_by_vboot(gm, |
| algo->vboot, table_idx); |
| } else if (table_idx == 254) { |
| algo->vboot_c = |
| fg_compensate_battery_voltage_from_low(gm, |
| ptable->fg_profile[0].pmic_min_vol, |
| (0 - iboot), table_idx); |
| fg_construct_battery_profile_by_vboot(gm, |
| algo->vboot_c, table_idx); |
| } |
| bm_debug("[%s]idx %d T_NEW %d T_table %d T_table_c %d qmax_sel %d vboot_t=[%d:%d:%d] %d %d rac %d\n", |
| __func__, |
| table_idx, curr_temp, |
| algo->T_table, algo->T_table_c, |
| pdata->qmax_sel, vboot_t, |
| algo->vboot, algo->vboot_c, |
| ptable->fg_profile[0].pmic_min_vol, |
| iboot, rac); |
| } |
| |
| /* batterypseudo1_auto = get_batterypseudo1_auto(vboot, shutdown_hl_zcv); */ |
| |
| if (algo->qmax_t_aging == 9999999 || algo->aging_factor > 10000) |
| algo->aging_factor = 10000; |
| |
| bm_debug("[%s] qmax_sel=%d iboot_sel=%d iboot:%d vbat:%d i:%d vboot:%d %d %d\n", |
| __func__, |
| pdata->qmax_sel, pdata->iboot_sel, iboot, |
| ptim_vbat, ptim_i, algo->vboot, algo->vboot_c, vboot_t); |
| |
| if (pdata->qmax_sel == 0) { |
| bm_debug("[%s][by_qmax]qmax_sel %d qmax %d vboot %d %d pmic_min_vol %d iboot %d r %d\n", |
| __func__, |
| pdata->qmax_sel, algo->qmax_t_0ma_h, |
| algo->vboot, algo->vboot_c, |
| ptable->fg_profile[0].pmic_min_vol, |
| iboot, rac); |
| } |
| if (pdata->qmax_sel == 1) { |
| bm_debug("[%s][by_vboot]qmax_sel %d vboot_t %d vboot %d %d pmic_min_vol %d iboot %d rac %d\n", |
| __func__, |
| pdata->qmax_sel, vboot_t, algo->vboot, |
| algo->vboot_c, |
| ptable->fg_profile[0].pmic_min_vol, |
| iboot, rac); |
| } |
| } |
| |
| void fgr_dump_table(struct mtk_battery *gm, int idx) |
| { |
| struct fuelgauge_profile_struct *profile_p; |
| int i; |
| |
| profile_p = fg_get_profile(gm, idx); |
| |
| bm_err("[%s]table idx:%d (i,mah,voltage,resistance,percentage)\n", |
| __func__, |
| idx); |
| for (i = 0; i < fg_get_saddles(gm); i = i + 5) { |
| bm_err("(%2d,%5d,%5d,%5d,%3d)(%2d,%5d,%5d,%5d,%3d)(%2d,%5d,%5d,%5d,%3d)(%2d,%5d,%5d,%5d,%3d)(%2d,%5d,%5d,%5d,%3d)\n", |
| i, profile_p[i].mah, profile_p[i].voltage, |
| profile_p[i].resistance, profile_p[i].percentage, |
| i+1, profile_p[i+1].mah, profile_p[i+1].voltage, |
| profile_p[i+1].resistance, profile_p[i+1].percentage, |
| i+2, profile_p[i+2].mah, profile_p[i+2].voltage, |
| profile_p[i+2].resistance, profile_p[i+2].percentage, |
| i+3, profile_p[i+3].mah, profile_p[i+3].voltage, |
| profile_p[i+3].resistance, profile_p[i+3].percentage, |
| i+4, profile_p[i+4].mah, profile_p[i+4].voltage, |
| profile_p[i+4].resistance, profile_p[i+4].percentage |
| ); |
| } |
| } |
| |
| void fgr_update_quse(struct mtk_battery *gm, int caller) |
| { |
| int aging_factor_cust = 0; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| /* caller = 1 means update c table */ |
| /* caller = 2 means update v table */ |
| |
| if (caller == 1) { |
| if (pdata->aging_sel == 1) |
| algo->quse_tb1 = |
| algo->qmax_t_0ma_tb1 * aging_factor_cust / 10000; |
| else |
| algo->quse_tb1 = |
| algo->qmax_t_0ma_tb1 * algo->aging_factor / 10000; |
| } else { |
| if (pdata->aging_sel == 1) |
| algo->quse_tb0 = |
| algo->qmax_t_0ma * aging_factor_cust / 10000; |
| else |
| algo->quse_tb0 = |
| algo->qmax_t_0ma * algo->aging_factor / 10000; |
| } |
| |
| if (caller == 1) { |
| bm_debug("[%s]aging_sel %d qmax_t_0ma_tb1 %d quse_tb1 [%d] aging[%d]\n", |
| __func__, |
| pdata->aging_sel, algo->qmax_t_0ma_tb1, |
| algo->quse_tb1, algo->aging_factor); |
| } |
| } |
| |
| /* update uisoc ht/lt gap */ |
| void fgr_update_uisoc_threshold(struct mtk_battery *gm) |
| { |
| int D_Remain = 0; |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| |
| algo->car = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| fgr_update_quse(gm, 1); |
| |
| /* calculate ui ht gap */ |
| algo->ht_gap = algo->quse_tb1 / 100; |
| |
| if (algo->ht_gap < (algo->quse_tb1 / 1000)) |
| algo->ht_gap = algo->quse_tb1 / 1000; |
| |
| if (algo->ui_soc <= 100) |
| algo->ht_gap = algo->quse_tb1 / 200; |
| |
| if (algo->ht_gap < CAR_MIN_GAP) |
| algo->ht_gap = CAR_MIN_GAP; |
| |
| /* calculate ui lt_gap */ |
| D_Remain = algo->soc * algo->quse_tb1 / 10000; |
| algo->lt_gap = |
| D_Remain * gm->fg_cust_data.diff_soc_setting |
| / algo->ui_soc; |
| |
| if (algo->lt_gap < (algo->quse_tb1 / 1000)) |
| algo->lt_gap = algo->quse_tb1 / 1000; |
| |
| if (algo->ui_soc <= 100) |
| algo->lt_gap = algo->quse_tb1 / 200; |
| |
| if (algo->lt_gap < CAR_MIN_GAP) |
| algo->lt_gap = CAR_MIN_GAP; |
| |
| bm_debug("[%s]car:%d quse_tb1[%d %d] gap[%d %d][%d]\n", |
| __func__, |
| algo->car, algo->quse_tb1, |
| algo->soc, algo->ht_gap, |
| algo->lt_gap, D_Remain); |
| } |
| |
| void fgr_update_uisoc_ht(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| fgr_update_uisoc_threshold(gm); |
| battery_set_property(BAT_PROP_UISOC_HT_INT_GAP, algo->ht_gap); |
| bm_debug("[%s] update ht_en:%d ht_gap:%d\n", |
| __func__, |
| algo->uisoc_ht_en, algo->ht_gap); |
| |
| } |
| |
| void fgr_update_uisoc_lt(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| fgr_update_uisoc_threshold(gm); |
| battery_set_property(BAT_PROP_UISOC_LT_INT_GAP, algo->lt_gap); |
| bm_debug("[%s] update lt_en:%d lt_gap:%d\n", |
| __func__, |
| algo->uisoc_lt_en, algo->lt_gap); |
| } |
| |
| void fg_enable_uisoc_ht(struct mtk_battery *gm, int en) |
| { |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| algo->uisoc_ht_en = en; |
| battery_set_property(BAT_PROP_ENABLE_UISOC_HT_INT, en); |
| bm_debug("[%s] ht_en:%d ht_gap:%d\n", |
| __func__, |
| algo->uisoc_ht_en, algo->ht_gap); |
| } |
| |
| void fg_enable_uisoc_lt(struct mtk_battery *gm, int en) |
| { |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| algo->uisoc_lt_en = en; |
| battery_set_property(BAT_PROP_ENABLE_UISOC_LT_INT, en); |
| bm_debug("[%s] lt_en:%d lt_gap:%d\n", |
| __func__, algo->uisoc_lt_en, algo->lt_gap); |
| } |
| |
| int SOC_to_OCV_c(struct mtk_battery *gm, int _soc) |
| { |
| struct fuelgauge_profile_struct *profile_p; |
| int ret_vol = 0; |
| int i = 0, size, high; |
| int _dod = 10000 - _soc; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| profile_p = fg_get_profile(gm, ptable->temperature_tb1); |
| if (profile_p == NULL) { |
| bm_err("[%s] fgauge get c table: fail !\n", |
| __func__); |
| return 0; |
| } |
| |
| size = fg_get_saddles(gm); |
| |
| for (i = 0; i < size; i++) { |
| if (profile_p[i].percentage >= _dod) |
| break; |
| } |
| |
| if (i == 0) { |
| high = 1; |
| ret_vol = profile_p[0].voltage; |
| } else if (i >= size) { |
| high = size-1; |
| ret_vol = profile_p[high].voltage; |
| } else { |
| high = i; |
| |
| ret_vol = interpolation( |
| profile_p[high-1].percentage, |
| profile_p[high-1].voltage, |
| profile_p[high].percentage, |
| profile_p[high].voltage, |
| _dod); |
| } |
| bm_debug("[%s]soc:%d dod:%d! voltage:%d highidx:%d\n", |
| __func__, |
| _soc, _dod, ret_vol, high); |
| |
| return ret_vol; |
| } |
| |
| int DOD_to_OCV_c(struct mtk_battery *gm, int _dod) |
| { |
| struct fuelgauge_profile_struct *profile_p; |
| int ret_vol = 0; |
| int i = 0, size, high; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| profile_p = fg_get_profile(gm, ptable->temperature_tb1); |
| if (profile_p == NULL) { |
| bm_err("[%s] fgauge get c table fail !\n", |
| __func__); |
| return 0; |
| } |
| |
| size = fg_get_saddles(gm); |
| |
| for (i = 0; i < size; i++) { |
| if (profile_p[i].percentage >= _dod) |
| break; |
| } |
| |
| if (i == 0) { |
| high = 1; |
| ret_vol = profile_p[0].voltage; |
| } else if (i >= size) { |
| high = size-1; |
| ret_vol = profile_p[high].voltage; |
| } else { |
| high = i; |
| |
| ret_vol = interpolation( |
| profile_p[high-1].percentage, |
| profile_p[high-1].voltage, |
| profile_p[high].percentage, |
| profile_p[high].voltage, |
| _dod); |
| } |
| bm_debug("[%s]DOD_to_OCV: dod:%d vol:%d highidx:%d\n", |
| __func__, |
| _dod, ret_vol, high); |
| |
| return ret_vol; |
| } |
| |
| int OCV_to_SOC_c(struct mtk_battery *gm, int _ocv) |
| { |
| struct fuelgauge_profile_struct *profile_p; |
| int ret_vol = 0; |
| int i = 0, size, high; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| |
| profile_p = fg_get_profile(gm, ptable->temperature_tb1); |
| if (profile_p == NULL) { |
| bm_err("[%s]can't get c table: fail\n", |
| __func__); |
| return 0; |
| } |
| |
| size = fg_get_saddles(gm); |
| |
| for (i = 0; i < size; i++) { |
| if (profile_p[i].voltage <= _ocv) |
| break; |
| } |
| |
| if (i == 0) { |
| high = 1; |
| ret_vol = profile_p[0].percentage; |
| ret_vol = 10000 - ret_vol; |
| } else if (i >= size) { |
| high = size-1; |
| ret_vol = profile_p[high].percentage; |
| ret_vol = 10000 - ret_vol; |
| } else { |
| high = i; |
| |
| ret_vol = interpolation( |
| profile_p[high-1].voltage, |
| profile_p[high-1].percentage, |
| profile_p[high].voltage, |
| profile_p[high].percentage, |
| _ocv); |
| |
| ret_vol = 10000 - ret_vol; |
| } |
| bm_debug("[%s]voltage:%d dod:%d highidx:%d\n", |
| __func__, |
| _ocv, ret_vol, high); |
| |
| return ret_vol; |
| } |
| |
| int OCV_to_DOD_c(struct mtk_battery *gm, int _ocv) |
| { |
| struct fuelgauge_profile_struct *profile_p; |
| int ret_vol = 0; |
| int i = 0, size, high; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| profile_p = fg_get_profile(gm, ptable->temperature_tb1); |
| if (profile_p == NULL) { |
| bm_err("[%s] fgauge can't get c table: fail\n", |
| __func__); |
| return 0; |
| } |
| |
| size = fg_get_saddles(gm); |
| |
| for (i = 0; i < size; i++) { |
| if (profile_p[i].voltage <= _ocv) |
| break; |
| } |
| |
| if (i == 0) { |
| high = 1; |
| ret_vol = profile_p[0].percentage; |
| } else if (i >= size) { |
| high = size-1; |
| ret_vol = profile_p[high].percentage; |
| } else { |
| high = i; |
| |
| ret_vol = interpolation( |
| profile_p[high-1].voltage, |
| profile_p[high-1].percentage, |
| profile_p[high].voltage, |
| profile_p[high].percentage, |
| _ocv); |
| } |
| |
| bm_debug("[%s]voltage:%d dod:%d highidx:%d\n", |
| __func__, |
| _ocv, ret_vol, high); |
| |
| return ret_vol; |
| } |
| |
| void Set_fg_c_d0_by_ocv(struct mtk_battery *gm, int _ocv) |
| { |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| algo->fg_c_d0_ocv = _ocv; |
| algo->fg_c_d0_dod = OCV_to_DOD_c(gm, _ocv); |
| algo->fg_c_d0_soc = 10000 - algo->fg_c_d0_dod; |
| } |
| |
| void fgr_set_soc_by_vc_mode(struct mtk_battery *gm) |
| { |
| gm->algo.soc = gm->algo.fg_c_soc; |
| } |
| |
| void fgr_update_fg_bat_tmp_threshold_c(struct mtk_battery *gm) |
| { |
| gm->algo.fg_bat_tmp_c_gap = 1; |
| } |
| |
| void fgr_update_c_dod(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| algo->car = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| fgr_update_quse(gm, 1); |
| Set_fg_c_d0_by_ocv(gm, algo->fg_c_d0_ocv); |
| algo->fg_c_dod = algo->fg_c_d0_dod - algo->car * 10000 / algo->quse_tb1; |
| algo->fg_c_soc = 10000 - algo->fg_c_dod; |
| |
| bm_debug("[%s] fg_c_dod %d fg_c_d0_dod %d car %d quse_tb1 %d fg_c_soc %d\n", |
| __func__, |
| algo->fg_c_dod, algo->fg_c_d0_dod, |
| algo->car, algo->quse_tb1, |
| algo->fg_c_soc); |
| } |
| |
| void fgr_dod_init(struct mtk_battery *gm) |
| { |
| int init_swocv = get_ptim_vbat(); |
| int con0_soc = gauge_get_int_property(GAUGE_PROP_CON0_SOC); |
| int con0_uisoc = gauge_get_int_property(GAUGE_PROP_RTC_UI_SOC); |
| |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| algo->rtc_ui_soc = UNIT_TRANS_100 * con0_uisoc; |
| init_swocv = gauge_get_int_property(GAUGE_PROP_PTIM_BATTERY_VOLTAGE) |
| * 10; |
| |
| if (algo->rtc_ui_soc == 0 || con0_soc == 0) { |
| algo->rtc_ui_soc = OCV_to_SOC_c(gm, init_swocv); |
| algo->fg_c_d0_soc = algo->rtc_ui_soc; |
| |
| if (algo->rtc_ui_soc < 0) { |
| bm_err("[%s]rtcui<0,set to 0,rtc_ui_soc:%d fg_c_d0_soc:%d\n", |
| __func__, |
| algo->rtc_ui_soc, algo->fg_c_d0_soc); |
| algo->rtc_ui_soc = 0; |
| } |
| |
| algo->ui_d0_soc = algo->rtc_ui_soc; |
| bm_err("[%s]rtcui=0 case,init_swocv=%d,OCV_to_SOC_c=%d ui:[%d %d] con0_soc=[%d %d]\n", |
| __func__, |
| init_swocv, algo->fg_c_d0_soc, |
| algo->ui_d0_soc, algo->rtc_ui_soc, |
| con0_soc, con0_uisoc); |
| } else { |
| algo->ui_d0_soc = algo->rtc_ui_soc; |
| algo->fg_c_d0_soc = UNIT_TRANS_100 * con0_soc; |
| } |
| |
| algo->fg_c_d0_ocv = SOC_to_OCV_c(gm, algo->fg_c_d0_soc); |
| Set_fg_c_d0_by_ocv(gm, algo->fg_c_d0_ocv); |
| fg_adc_reset(gm); |
| |
| if (pdata->d0_sel == 1) { |
| /* reserve for custom c_d0 / custom ui_soc */ |
| algo->fg_c_d0_soc = get_d0_c_soc_cust(gm, algo->fg_c_d0_soc); |
| algo->ui_d0_soc = get_uisoc_cust(gm, algo->ui_d0_soc); |
| |
| algo->fg_c_d0_ocv = SOC_to_OCV_c(gm, algo->fg_c_d0_soc); |
| Set_fg_c_d0_by_ocv(gm, algo->fg_c_d0_ocv); |
| } |
| fgr_update_c_dod(gm); |
| algo->ui_soc = algo->ui_d0_soc; |
| fgr_set_soc_by_vc_mode(gm); |
| |
| bm_err("[%s]fg_c_d0[%d %d %d] d0_sel[%d] c_soc[%d %d] ui[%d %d] soc[%d] con0[ui %d %d]\n", |
| __func__, |
| algo->fg_c_d0_soc, algo->fg_c_d0_ocv, |
| algo->fg_c_d0_dod, pdata->d0_sel, |
| algo->fg_c_dod, algo->fg_c_soc, |
| algo->rtc_ui_soc, algo->ui_d0_soc, |
| algo->soc, con0_uisoc, con0_soc); |
| } |
| |
| void fgr_imix_error_calibration(struct mtk_battery *gm) |
| { |
| int imix = 0; |
| int iboot = 0; |
| |
| imix = get_imix_r(); |
| iboot = gm->fg_cust_data.shutdown_system_iboot; |
| |
| if ((imix < iboot) && (imix > 0)) |
| fg_adc_reset(gm); |
| } |
| |
| void fgr_dlpt_sd_handler(struct mtk_battery *gm) |
| { |
| gm->ui_soc = 0; |
| gm->algo.low_tracking_enable = 0; |
| set_fg_time(gm, 0); |
| battery_set_property(BAT_PROP_ENABLE_UISOC_HT_INT, 0); |
| battery_set_property(BAT_PROP_ENABLE_UISOC_LT_INT, 0); |
| battery_set_property(BAT_PROP_UISOC, gm->algo.ui_soc); |
| fgr_imix_error_calibration(gm); |
| } |
| |
| void fgr_shutdown_int_handler(struct mtk_battery *gm) |
| { |
| gm->algo.low_tracking_enable = 1; |
| set_fg_time(gm, gm->fg_cust_data.discharge_tracking_time); |
| fgr_imix_error_calibration(gm); |
| } |
| |
| void fgr_error_calibration2(struct mtk_battery *gm, int intr_no) |
| { |
| int shutdown_cond = get_shutdown_cond(gm); |
| |
| if (shutdown_cond != 1) |
| gm->algo.low_tracking_enable = false; |
| } |
| |
| void fgr_int_end_flow(struct mtk_battery *gm, unsigned int intr_no) |
| { |
| int curr_temp, vbat; |
| char intr_name[32]; |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| switch (intr_no) { |
| case FG_INTR_0: |
| sprintf(intr_name, "FG_INTR_INIT"); |
| break; |
| case FG_INTR_TIMER_UPDATE: |
| sprintf(intr_name, "FG_INTR_TIMER_UPDATE"); |
| break; |
| case FG_INTR_BAT_CYCLE: |
| sprintf(intr_name, "FG_INTR_BAT_CYCLE"); |
| break; |
| case FG_INTR_CHARGER_OUT: |
| sprintf(intr_name, "FG_INTR_CHARGER_OUT"); |
| break; |
| case FG_INTR_CHARGER_IN: |
| sprintf(intr_name, "FG_INTR_CHARGER_IN"); |
| break; |
| case FG_INTR_FG_TIME: |
| sprintf(intr_name, "FG_INTR_FG_TIME"); |
| break; |
| case FG_INTR_BAT_INT1_HT: |
| sprintf(intr_name, "FG_INTR_COULOMB_HT"); |
| break; |
| case FG_INTR_BAT_INT1_LT: |
| sprintf(intr_name, "FG_INTR_COULOMB_LT"); |
| break; |
| case FG_INTR_BAT_INT2_HT: |
| sprintf(intr_name, "FG_INTR_UISOC_HT"); |
| break; |
| case FG_INTR_BAT_INT2_LT: |
| sprintf(intr_name, "FG_INTR_UISOC_LT"); |
| break; |
| case FG_INTR_BAT_TMP_HT: |
| sprintf(intr_name, "FG_INTR_BAT_TEMP_HT"); |
| break; |
| case FG_INTR_BAT_TMP_LT: |
| sprintf(intr_name, "FG_INTR_BAT_TEMP_LT"); |
| break; |
| case FG_INTR_BAT_TIME_INT: |
| sprintf(intr_name, "FG_INTR_BAT_TIME_INT"); |
| break; |
| case FG_INTR_NAG_C_DLTV: |
| sprintf(intr_name, "FG_INTR_NAFG_VOLTAGE"); |
| break; |
| case FG_INTR_FG_ZCV: |
| sprintf(intr_name, "FG_INTR_FG_ZCV"); |
| break; |
| case FG_INTR_SHUTDOWN: |
| sprintf(intr_name, "FG_INTR_SHUTDOWN"); |
| break; |
| case FG_INTR_RESET_NVRAM: |
| sprintf(intr_name, "FG_INTR_RESET_NVRAM"); |
| break; |
| case FG_INTR_BAT_PLUGOUT: |
| sprintf(intr_name, "FG_INTR_BAT_PLUGOUT"); |
| break; |
| case FG_INTR_IAVG: |
| sprintf(intr_name, "FG_INTR_IAVG"); |
| break; |
| case FG_INTR_VBAT2_L: |
| sprintf(intr_name, "FG_INTR_VBAT2_L"); |
| break; |
| case FG_INTR_VBAT2_H: |
| sprintf(intr_name, "FG_INTR_VBAT2_H"); |
| break; |
| case FG_INTR_CHR_FULL: |
| sprintf(intr_name, "FG_INTR_CHR_FULL"); |
| break; |
| case FG_INTR_DLPT_SD: |
| sprintf(intr_name, "FG_INTR_DLPT_SD"); |
| break; |
| case FG_INTR_BAT_TMP_C_HT: |
| sprintf(intr_name, "FG_INTR_BAT_TMP_C_HT"); |
| break; |
| case FG_INTR_BAT_TMP_C_LT: |
| sprintf(intr_name, "FG_INTR_BAT_TMP_C_LT"); |
| break; |
| case FG_INTR_BAT_INT1_CHECK: |
| sprintf(intr_name, "FG_INTR_COULOMB_C"); |
| break; |
| default: |
| sprintf(intr_name, "FG_INTR_UNKNOWN"); |
| bm_err("[Intr_Number_to_Name] unknown intr %d\n", |
| intr_no); |
| break; |
| } |
| |
| algo->car = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| get_hw_info(); |
| |
| if (gm->disableGM30) |
| vbat = 4000; |
| else |
| vbat = gauge_get_int_property(GAUGE_PROP_BATTERY_VOLTAGE); |
| |
| curr_temp = force_get_tbat(gm, true); |
| |
| set_kernel_soc(gm, algo->soc); |
| battery_set_property(BAT_PROP_UISOC, algo->ui_soc); |
| gauge_set_property(GAUGE_PROP_RTC_UI_SOC, |
| (algo->ui_soc + 50) / 100); |
| |
| if (algo->soc <= 100) |
| gauge_set_property(GAUGE_PROP_CON0_SOC, 100); |
| else if (algo->soc >= 10000) |
| gauge_set_property(GAUGE_PROP_CON0_SOC, 10000); |
| else |
| gauge_set_property(GAUGE_PROP_CON0_SOC, algo->soc); |
| |
| fgr_error_calibration2(gm, intr_no); |
| bm_debug("[%s][%s]soc:%d, c_soc:%d ui_soc:%d VBAT %d T:[%d C:%d] car:%d\n", |
| __func__, |
| intr_name, algo->soc, algo->fg_c_soc, |
| algo->ui_soc, vbat, curr_temp, |
| algo->T_table_c, algo->car); |
| } |
| |
| void fgr_temp_c_int_handler(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| |
| /* int curr_temp; */ |
| fgr_construct_table_by_temp(gm, true, ptable->temperature_tb1); |
| fgr_construct_vboot(gm, ptable->temperature_tb1); |
| /* fg_debug_dump(ptable->temperature_tb1);*/ |
| fgr_update_c_dod(gm); |
| |
| fgr_set_soc_by_vc_mode(gm); |
| algo->fg_bat_tmp_c_gap = 1; |
| set_fg_bat_tmp_c_gap(algo->fg_bat_tmp_c_gap); |
| } |
| |
| void fgr_update_fg_bat_int1_threshold(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| pdata = &gm->fg_cust_data; |
| |
| algo->fg_bat_int1_gap = algo->quse_tb1 |
| * pdata->diff_soc_setting / 10000; |
| |
| if (algo->fg_bat_int1_gap < CAR_MIN_GAP) |
| algo->fg_bat_int1_gap = CAR_MIN_GAP; |
| |
| bm_debug("[%s] quse_tb1:%d gap:%d diff_soc_setting:%d MIN:%d\n", |
| __func__, |
| algo->quse_tb1, algo->fg_bat_int1_gap, |
| pdata->diff_soc_setting, CAR_MIN_GAP); |
| } |
| |
| void fgr_bat_int1_handler(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| |
| algo = &gm->algo; |
| fgr_update_c_dod(gm); |
| fgr_update_fg_bat_int1_threshold(gm); |
| battery_set_property(BAT_PROP_COULOMB_INT_GAP, |
| algo->fg_bat_int1_gap); |
| fgr_set_soc_by_vc_mode(gm); |
| |
| bm_debug("[%s]soc %d\n", |
| __func__, algo->soc); |
| } |
| |
| void fgr_bat_int2_h_handler(struct mtk_battery *gm) |
| { |
| int ui_gap_ht = 0; |
| /* int is_charger_exist = get_charger_exist(); */ |
| int _car; |
| int delta_car_bat0; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| _car = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| delta_car_bat0 = abs(algo->prev_car_bat0 - _car); |
| |
| fgr_update_uisoc_threshold(gm); |
| ui_gap_ht = delta_car_bat0 * UNIT_TRANS_100 / algo->ht_gap; |
| |
| bm_debug("[%s][IN]ui_soc %d, ht_gap:[%d %d], _car[%d %d %d]\n", |
| __func__, |
| algo->ui_soc, algo->ht_gap, ui_gap_ht, |
| _car, algo->prev_car_bat0, delta_car_bat0); |
| |
| if (ui_gap_ht > 100) |
| ui_gap_ht = 100; |
| |
| if (ui_gap_ht > 0) |
| algo->prev_car_bat0 = _car; |
| |
| if (algo->ui_soc >= 10000) |
| algo->ui_soc = 10000; |
| else { |
| if ((algo->ui_soc + ui_gap_ht) >= 10000) |
| algo->ui_soc = 10000; |
| else |
| algo->ui_soc = algo->ui_soc + ui_gap_ht; |
| } |
| |
| if (algo->ui_soc >= 10000) |
| algo->ui_soc = 10000; |
| |
| fgr_update_uisoc_ht(gm); |
| fgr_update_uisoc_lt(gm); |
| |
| bm_debug("[%s][OUT]ui_soc %d, ui_gap_ht:%d, _car[%d %d %d]\n", |
| __func__, algo->ui_soc, ui_gap_ht, |
| _car, algo->prev_car_bat0, delta_car_bat0); |
| } |
| |
| void fgr_bat_int2_l_handler(struct mtk_battery *gm) |
| { |
| int ui_gap_lt = 0; |
| int is_charger_exist = get_charger_exist(); |
| int _car; |
| int delta_car_bat0; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| _car = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| delta_car_bat0 = abs(algo->prev_car_bat0 - _car); |
| fgr_update_uisoc_threshold(gm); |
| |
| if (algo->ui_soc > algo->soc && algo->soc >= 100) { |
| ui_gap_lt = delta_car_bat0 * UNIT_TRANS_100 / algo->lt_gap; |
| ui_gap_lt = ui_gap_lt * algo->ui_soc / algo->soc; |
| } else |
| ui_gap_lt = delta_car_bat0 * UNIT_TRANS_100 / algo->lt_gap; |
| |
| bm_debug("[%s][IN]ui_soc %d, lt_gap[%d %d] _car[%d %d %d]\n", |
| __func__, |
| algo->ui_soc, algo->lt_gap, |
| ui_gap_lt, _car, |
| algo->prev_car_bat0, delta_car_bat0); |
| |
| if (ui_gap_lt > 100) |
| ui_gap_lt = 100; |
| |
| if (ui_gap_lt < 0) { |
| bm_debug("[FG_ERR][%s] ui_gap_lt %d should not less than 0\n", |
| __func__, |
| ui_gap_lt); |
| ui_gap_lt = 0; |
| } |
| |
| if (ui_gap_lt > 0) |
| algo->prev_car_bat0 = _car; |
| |
| if (is_charger_exist == true) { |
| algo->ui_soc = algo->ui_soc - ui_gap_lt; |
| } else { |
| if (algo->ui_soc <= 100) { |
| if (algo->ui_soc == 0) |
| algo->ui_soc = 0; |
| else |
| algo->ui_soc = 100; |
| } else { |
| if ((algo->ui_soc - ui_gap_lt) < 100) |
| algo->ui_soc = 100; |
| else |
| algo->ui_soc = algo->ui_soc - ui_gap_lt; |
| |
| if (algo->ui_soc < 100) |
| algo->ui_soc = 100; |
| } |
| } |
| fgr_update_uisoc_ht(gm); |
| fgr_update_uisoc_lt(gm); |
| |
| bm_debug("[%s][OUT]ui_soc %d, ui_gap_lt:%d, _car[%d %d %d]\n", |
| __func__, algo->ui_soc, ui_gap_lt, |
| _car, algo->prev_car_bat0, delta_car_bat0); |
| } |
| |
| void fgr_bat_int2_handler(struct mtk_battery *gm, int source) |
| { |
| int _car = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| bm_debug("[%s]car:%d pre_car:%d ht:%d lt:%d u_type:%d source:%d\n", |
| __func__, _car, algo->prev_car_bat0, |
| algo->ht_gap, algo->lt_gap, pdata->uisoc_update_type, source); |
| if (_car > algo->prev_car_bat0) |
| fgr_bat_int2_h_handler(gm); |
| else if (_car < algo->prev_car_bat0) |
| fgr_bat_int2_l_handler(gm); |
| |
| } |
| |
| void fgr_time_handler(struct mtk_battery *gm) |
| { |
| int is_charger_exist = get_charger_exist(); |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| bm_debug("[%s][IN] chr:%d, low_tracking:%d ui_soc:%d\n", |
| __func__, is_charger_exist, |
| algo->low_tracking_enable, algo->ui_soc); |
| |
| if (algo->low_tracking_enable) { |
| if (is_charger_exist) |
| return; |
| |
| if (is_charger_exist == false) { |
| algo->ui_soc = algo->ui_soc - 100; |
| if (algo->ui_soc <= 0) { |
| algo->ui_soc = 0; |
| algo->low_tracking_enable = 0; |
| } |
| } |
| } else |
| set_fg_time(gm, 0); |
| } |
| |
| void fgr_vbat2_h_int_handler(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| pdata = &gm->fg_cust_data; |
| gauge_set_property(GAUGE_PROP_EN_HIGH_VBAT_INTERRUPT, false); |
| algo->fg_vbat2_lt = pdata->vbat2_det_voltage1; |
| gauge_set_property(GAUGE_PROP_VBAT_LT_INTR_THRESHOLD, |
| algo->fg_vbat2_lt); |
| gauge_set_property(GAUGE_PROP_EN_LOW_VBAT_INTERRUPT, true); |
| bm_debug("[%s]fg_vbat2_lt=%d %d\n", |
| __func__, |
| algo->fg_vbat2_lt, algo->fg_vbat2_ht); |
| } |
| |
| void fgr_vbat2_l_int_handler(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| |
| if (algo->fg_vbat2_lt == pdata->vbat2_det_voltage1) { |
| set_shutdown_cond(gm, LOW_BAT_VOLT); |
| algo->fg_vbat2_lt = pdata->vbat2_det_voltage2; |
| algo->fg_vbat2_ht = pdata->vbat2_det_voltage3; |
| gauge_set_property(GAUGE_PROP_VBAT_LT_INTR_THRESHOLD, |
| algo->fg_vbat2_lt); |
| gauge_set_property(GAUGE_PROP_VBAT_HT_INTR_THRESHOLD, |
| algo->fg_vbat2_ht); |
| gauge_set_property(GAUGE_PROP_EN_LOW_VBAT_INTERRUPT, true); |
| gauge_set_property(GAUGE_PROP_EN_HIGH_VBAT_INTERRUPT, true); |
| } |
| bm_debug("[%s]fg_vbat2_lt=%d %d,[%d %d %d]\n", |
| __func__, |
| algo->fg_vbat2_lt, algo->fg_vbat2_ht, |
| pdata->vbat2_det_voltage1, |
| pdata->vbat2_det_voltage2, |
| pdata->vbat2_det_voltage3); |
| } |
| |
| void do_fg_algo(struct mtk_battery *gm, unsigned int intr_num) |
| { |
| switch (intr_num) { |
| case FG_INTR_BAT_TMP_C_HT: |
| fgr_temp_c_int_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_BAT_TMP_HT); |
| break; |
| case FG_INTR_BAT_TMP_C_LT: |
| fgr_temp_c_int_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_BAT_TMP_LT); |
| break; |
| case FG_INTR_BAT_INT1_HT: |
| fgr_bat_int1_handler(gm); |
| fgr_bat_int2_handler(gm, 0); |
| fgr_int_end_flow(gm, FG_INTR_BAT_INT1_HT); |
| break; |
| case FG_INTR_BAT_INT1_LT: |
| fgr_bat_int1_handler(gm); |
| fgr_bat_int2_handler(gm, 0); |
| fgr_int_end_flow(gm, FG_INTR_BAT_INT1_LT); |
| break; |
| case FG_INTR_BAT_INT2_HT: |
| fgr_bat_int2_h_handler(gm); |
| fgr_bat_int1_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_BAT_INT2_HT); |
| break; |
| case FG_INTR_BAT_INT2_LT: |
| fgr_bat_int2_l_handler(gm); |
| fgr_bat_int1_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_BAT_INT2_LT); |
| break; |
| case FG_INTR_FG_TIME: |
| fgr_time_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_FG_TIME); |
| break; |
| case FG_INTR_SHUTDOWN: |
| fgr_shutdown_int_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_SHUTDOWN); |
| break; |
| case FG_INTR_DLPT_SD: |
| fgr_dlpt_sd_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_DLPT_SD); |
| break; |
| case FG_INTR_VBAT2_H: |
| fgr_vbat2_h_int_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_VBAT2_H); |
| break; |
| case FG_INTR_VBAT2_L: |
| fgr_vbat2_l_int_handler(gm); |
| fgr_int_end_flow(gm, FG_INTR_VBAT2_L); |
| break; |
| } |
| bm_debug("[%s] intr_num=0x%x\n", __func__, intr_num); |
| } |
| |
| void fgr_set_int1(struct mtk_battery *gm) |
| { |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| struct fuel_gauge_custom_data *pdata; |
| int car_now = gauge_get_int_property(GAUGE_PROP_COULOMB); |
| |
| algo = &gm->algo; |
| ptable = &gm->fg_table_cust_data; |
| pdata = &gm->fg_cust_data; |
| fgr_update_quse(gm, 1); |
| /* set c gap */ |
| fgr_update_fg_bat_int1_threshold(gm); |
| battery_set_property(BAT_PROP_COULOMB_INT_GAP, |
| algo->fg_bat_int1_gap); |
| bm_debug("[%s]set cgap :fg_bat_int1_gap %d to kernel done\n", |
| __func__, |
| algo->fg_bat_int1_gap); |
| |
| /* set ui_soc gap*/ |
| algo->prev_car_bat0 = car_now; |
| fgr_update_uisoc_ht(gm); |
| fgr_update_uisoc_lt(gm); |
| |
| battery_set_property(BAT_PROP_ENABLE_UISOC_HT_INT, 1); |
| battery_set_property(BAT_PROP_ENABLE_UISOC_LT_INT, 1); |
| |
| /*set bat tempture */ |
| algo->fg_bat_tmp_c_gap = 1; |
| set_fg_bat_tmp_c_gap(algo->fg_bat_tmp_c_gap); |
| bm_debug("[%s]fg_bat_tmp_c_gap %d\n", |
| __func__, algo->fg_bat_tmp_c_gap); |
| |
| algo->fg_vbat2_lt = pdata->vbat2_det_voltage1; |
| gauge_set_property(GAUGE_PROP_VBAT_LT_INTR_THRESHOLD, |
| algo->fg_vbat2_lt); |
| gauge_set_property(GAUGE_PROP_EN_LOW_VBAT_INTERRUPT, true); |
| |
| set_kernel_soc(gm, algo->soc); |
| battery_set_property(BAT_PROP_UISOC, algo->ui_soc); |
| gauge_set_property(GAUGE_PROP_RTC_UI_SOC, |
| (algo->ui_soc + 50) / 100); |
| |
| if (algo->soc <= 100) |
| gauge_set_property(GAUGE_PROP_CON0_SOC, 100); |
| else if (algo->soc >= 12000) |
| gauge_set_property(GAUGE_PROP_CON0_SOC, 10000); |
| else |
| gauge_set_property(GAUGE_PROP_CON0_SOC, algo->soc); |
| |
| bm_debug("[%s] done\n", __func__); |
| } |
| |
| void battery_algo_init(struct mtk_battery *gm) |
| { |
| int is_bat_exist = 1; |
| struct mtk_battery_algo *algo; |
| struct fuel_gauge_table_custom_data *ptable; |
| |
| ptable = &gm->fg_table_cust_data; |
| algo = &gm->algo; |
| algo->fg_bat_tmp_c_gap = 1; |
| algo->aging_factor = 10000; |
| algo->DC_ratio = 100; |
| gauge_get_property(GAUGE_PROP_BATTERY_EXIST, &is_bat_exist); |
| bm_err("MTK Battery algo init bat_exist:%d\n", |
| is_bat_exist); |
| |
| if (is_bat_exist) { |
| fgr_construct_table_by_temp(gm, true, |
| ptable->temperature_tb1); |
| fgr_construct_vboot(gm, |
| ptable->temperature_tb1); |
| fgr_dump_table(gm, ptable->temperature_tb1); |
| fgr_dod_init(gm); |
| fgr_set_int1(gm); |
| battery_set_property(BAT_PROP_INIT_DONE, 1); |
| gauge_set_property(GAUGE_PROP_IS_NVRAM_FAIL_MODE, 1); |
| } |
| bm_err("[battery_recovery] is_evb:%d is_bat_exist %d\n", |
| gm->disableGM30, is_bat_exist); |
| } |
| |