一: SOC(荷电状态)计算方法
公式:
SOC = RUC / (FCC-UUC)
名词:
术语 |
全称 |
注释 |
FCC |
Full-Charge Capacity |
满电荷电量 |
UC |
Remaining Capacity |
RC 剩余电量 |
CC |
Coulumb Counter |
电量计 |
UUC |
Unusable Capacity |
不可用电量 |
RUC |
Remaining Usable Capacity |
RUC=RC-CC-UUC,剩余可用电量 |
OCV |
Open Circuit Voltage |
开路电压,电池在开路状态下的端电压称为开路电压 |
SoC |
State of Charge |
电量百分比 |
PC |
Percentage Charge |
剩余电荷占FCC百分比 |
二:各参数计算方法:
1.FCC:根据电池温度temp通过查找表(lut)来计算
static int calculate_fcc(struct qpnp_bms_chip *chip, int batt_temp) { int fcc_uah; if (chip->adjusted_fcc_temp_lut == NULL) { // log显示,没有对温度lut进行修正; /* interpolate_fcc returns a mv value. */ fcc_uah = interpolate_fcc(chip->fcc_temp_lut, batt_temp) * 1000; // 不修正直接返回对应温度的FCC值 printk("fcc = %d uAh\n", fcc_uah); return fcc_uah; } else { return 1000 * interpolate_fcc(chip->adjusted_fcc_temp_lut, batt_temp); } }
lut:fcc_temp,x轴温度,y轴电量:
static struct single_row_lut fcc_temp = { .x = {-20, 0, 25, 40, 65}, .y = {4526, 4594, 4691, 4687, 4664}, .cols = 5 };
2. RC: 依赖ocv,通过ocv计算;单位uAh
/* calculate remaining charge at the time of ocv */ static int calculate_ocv_charge(struct qpnp_bms_chip *chip, struct raw_soc_params *raw, int fcc_uah) { int ocv_uv, pc; // RAW - 存在pm?PM里读出来的未经修正的原始数据? // read_soc_params_raw -> qpnp_read_wrapper(chip, (u8 *)&raw->last_good_ocv_raw, // chip->base + BMS1_OCV_FOR_SOC_DATA0, 2); ocv_uv = raw->last_good_ocv_uv; // 1. 上次关机时存储的ocv;2. 电池新插入时会更新该值;3. 模拟电池新插入,手动更新:chip->insertion_ocv_uv;最终会更新raw->last_good_ocv_uv; pc = calculate_pc(chip, ocv_uv, chip->last_ocv_temp); // 上次ocv占FCC的百分比,受温度影响 printk("ocv_uv = %d pc = %d\n", ocv_uv, pc); return (fcc_uah * pc) / 100; } static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp) { int pc; pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp / 10, ocv_uv / 1000); // 根据lut查找百分比 printk("pc = %u %% for ocv = %d uv batt_temp = %d\n", pc, ocv_uv, batt_temp); /* Multiply the initial FCC value by the scale factor. */ return pc; }
有时开机时的ocv和关机时的ocv(raw->last_good_ocv_raw?)差距太大就明显影响百分比计算了;
所以就需要做修正了;
pc-lut:
static struct pc_temp_ocv_lut pc_temp_ocv = { .rows = 29, .cols = 5, .temp = {-20, 0, 25, 40, 65}, .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, .ocv = { {4240, 4223, 4237, 4234, 4229}, {4169, 4172, 4185, 4183, 4178}, {4115, 4125, 4138, 4136, 4131}, {4068, 4080, 4091, 4089, 4085}, {3995, 4036, 4053, 4048, 4042}, {3941, 3981, 3989, 3999, 4000}, {3905, 3942, 3950, 3958, 3959}, {3874, 3907, 3913, 3915, 3916}, {3848, 3877, 3880, 3881, 3881}, {3825, 3852, 3854, 3855, 3854}, {3806, 3829, 3831, 3832, 3832}, {3788, 3810, 3811, 3812, 3811}, {3771, 3792, 3793, 3795, 3794}, {3755, 3774, 3776, 3777, 3774}, {3739, 3758, 3759, 3757, 3748}, {3722, 3742, 3744, 3738, 3725}, {3703, 3726, 3728, 3721, 3707}, {3677, 3707, 3707, 3700, 3686}, {3641, 3689, 3691, 3684, 3670}, {3631, 3687, 3688, 3681, 3667}, {3621, 3685, 3686, 3679, 3665}, {3610, 3683, 3684, 3677, 3663}, {3600, 3680, 3681, 3674, 3660}, {3590, 3672, 3674, 3668, 3655}, {3580, 3657, 3659, 3655, 3642}, {3565, 3630, 3633, 3629, 3618}, {3547, 3595, 3597, 3594, 3585}, {3526, 3553, 3554, 3552, 3547}, {3500, 3500, 3500, 3500, 3500} } };
3、CC:将bms电量计读值转换成uAh,coulomb counter有两种, 没有手册不知道shadow型具体含义,不知是否和80x86里的影子寄存器是否为相同设计?:
/* Coulomb counter data */ #define BMS1_CC_DATA0 0x8A /* Shadow Coulomb counter data */ #define BMS1_SW_CC_DATA0 0xA8 /** * calculate_cc() - converts a hardware coulomb counter reading into uah * @chip: the bms chip pointer * @cc: the cc reading from bms h/w * @cc_type: calcualte cc from regular or shadow coulomb counter * @clear_cc: whether this function should clear the hardware counter * after reading * * Converts the 64 bit hardware coulomb counter into microamp-hour by taking * into account hardware resolution and adc errors. * * Return: the coulomb counter based charge in uAh (micro-amp hour) */ static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc, int cc_type, int clear_cc) { struct qpnp_iadc_calib calibration; struct qpnp_vadc_result result; int64_t cc_voltage_uv, cc_pvh, cc_uah, *software_counter; int rc; // chip->software_shdw_cc_uah == 0; chip->software_cc_uah == 0; software_counter = cc_type == SHDW_CC ? &chip->software_shdw_cc_uah : &chip->software_cc_uah; rc = qpnp_vadc_read(chip->vadc_dev, DIE_TEMP, &result); if (rc) { pr_err("could not read pmic die temperature: %d\n", rc); return *software_counter; } qpnp_iadc_get_gain_and_offset(chip->iadc_dev, &calibration); // 获得平台相关的增益修正参数,msm8610,msm8912; printk("%scc = %lld, die_temp = %lld\n", cc_type == SHDW_CC ? "shdw_" : "", cc, result.physical); cc_voltage_uv = cc_reading_to_uv(cc); cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv, calibration.gain_raw - calibration.offset_raw); cc_pvh = cc_uv_to_pvh(cc_voltage_uv); // 为减小cc的精度损失; cc_uah = div_s64(cc_pvh, chip->r_sense_uohm); // i = u / r;算出cc电流 rc = qpnp_iadc_comp_result(chip->iadc_dev, &cc_uah); // 根据QPNP不同版本ID,比如PM8110:QPNP_IADC_REV_ID_8110_1_0,做增益补偿 if (rc) printk("error compensation failed: %d\n", rc); if (clear_cc == RESET) { //1. calculate_state_of_charge -> calculate_soc_params中有RESET调用; // 2. load_shutdown_data(probe获取上次关机时的一些参数,比如ocv) -> recalculate_raw_soc -> recalculate_soc ->calculate_soc_params有调用(先cc、后shadow_cc); // 3. calculate_soc_work(calculate_soc_work会延时调用自身) -> recalculate_soc -> calculate_state_of_charge -> calculate_soc_params; // 4. recalculate_work(电池充电高压、检查电池状态、电池插入检查、ocv阈值中断、sw_cc中断、高温死机中断都会重新sched_work) -> recalculate_soc; printk("software_%scc = %lld, added cc_uah = %lld\n", cc_type == SHDW_CC ? "sw_" : "", *software_counter, cc_uah); *software_counter += cc_uah; reset_cc(chip, cc_type == SHDW_CC ? CLEAR_SHDW_CC : CLEAR_CC); return (int)*software_counter; } else { printk("software_%scc = %lld, cc_uah = %lld, total = %lld\n", cc_type == SHDW_CC ? "shdw_" : "", *software_counter, cc_uah, *software_counter + cc_uah); return *software_counter + cc_uah; } }
对于影子寄存器(Shadow Register):
"It depends on the context. Even my friend named something as shadow
register in his security
architecture. Usually shadow somthing means a backup. Whenever a misprediction or a wrong path
execution is detected, the shadows one can be used to restore the correct status in architecture."
“即对用户不可见的,有时候叫投影寄存器,比如linux中,ES,CS,SS等段寄存器存放的是段选择子,每个都有对应的影子寄存器用来存放段描述符,cpu会根据段寄存器内容自动装载对应的影子寄存器,所以访问同一个段的时候不用总是访问内存来读段描述符来确定段基址。”
4.UUC:不可使用电量,受温度影响;
static int calculate_termination_uuc(struct qpnp_bms_chip *chip, struct soc_params *params, int batt_temp, int uuc_iavg_ma, int *ret_pc_unusable) { int unusable_uv, pc_unusable, uuc_uah; int i = 0; int ocv_mv; int batt_temp_degc = batt_temp / 10; int rbatt_mohm; int delta_uv; int prev_delta_uv = 0; int prev_rbatt_mohm = 0; int uuc_rbatt_mohm; for (i = 0; i <= 100; i++) { ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, // 根据temp通过lut获得ocv, batt_temp_degc, i); rbatt_mohm = get_rbatt(chip, i, batt_temp); // batt_temp用于获得对应温度下的比例因子来得到对应温度下的rbatt; unusable_uv = (rbatt_mohm * uuc_iavg_ma) + (chip->v_cutoff_uv); // UUC = cutoff_uv + rbatt * iavg; delta_uv = ocv_mv * 1000 - unusable_uv; if (delta_uv > 0) break; prev_delta_uv = delta_uv; prev_rbatt_mohm = rbatt_mohm; } uuc_rbatt_mohm = linear_interpolate(rbatt_mohm, delta_uv, // 用插值法修正uuc_rbatt_mohm prev_rbatt_mohm, prev_delta_uv, 0); unusable_uv = (uuc_rbatt_mohm * uuc_iavg_ma) + (chip->v_cutoff_uv); // 更新uuv pc_unusable = calculate_pc(chip, unusable_uv, batt_temp); // 利用uuv和temp获得不可使用的电量占FCC的百分比; uuc_uah = (params->fcc_uah * pc_unusable) / 100; // 获得UUC printk("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d rbatt_pc = %d uuc = %d\n", uuc_iavg_ma, uuc_rbatt_mohm, unusable_uv, pc_unusable, i, uuc_uah); *ret_pc_unusable = pc_unusable; return uuc_uah; }
以上只是获得bms对应参数的函数调用,高通还添加了针对soc和ocv的校正方法;
三、调用流程
recalculate_soc -> qpnp_vadc_read -> read_soc_params_raw -> calculate_state_of_charge(adjust_soc对soc进行修正):
1. rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &result);
通过热敏电阻获得电池的温度batt_temp = (int)result.physical;后面计算FCC时需要;
2. read_soc_params_raw(chip, &raw, batt_temp);
“ Add functions necessary for driver initialization and
exported CHARGE_FULL_DESIGN and CURRENT_NOW properties in the
bms power supply.“
“power:
qpnp-bms: remember the temperature when ocv was taken
The code looks up the percent charge based on ocv at the
current temperature. The ocv could have been recorded earlier
under different temperature conditions. Looking it up at the
current temperature is wrong.
Hence remember the temperature when the ocv was first seen. And
use that to lookup the ocv values.“
“power: qpnp-bms: estimate OCV when a new battery is inserted
When a new battery is inserted, the BMS will continue to use its old
OCV value until a new OCV is taken. This is clearly wrong, as the new
battery can have a completely different state of charge than the old
one.
Fix this by making BMS driver estimate a new OCV based on the close
circuit battery voltage and IR drop across the battery when a battery
insertion is detected.“
“ power: qpnp-bms: detect warm resets
During warm PMIC resets, the BMS will not take a new OCV. This may
cause the SOC upon reboot to be completely wrong if no recent OCVs
have been taken.
Fix this by checking for invalid OCVs and warm resets. If either
occur, estimate a new OCV based on vbat and use that instead.“
“power: qpnp-bms: fake a high OCV when charging completes
When charging is finished and the battery is considered full, the BMS
does not necessarily report 100%. Fix this by faking a high OCV when
the charger notifies BMS of EOC.“
“power: qpnp-bms: export shadow coulomb counter value
Export the shadow coulomb counter register value to userspace in
order to assist in recording power consumption."
3.充电检测:
static void power_supply_changed_work(struct work_struct *work)// 该工作会定期检查电源状态:插入DC充电,并遍历已注册supply的pst->external_power_changed,如果有就调用;更新系统LED;最后发送kobject_uevent;
external_power_changed:
会检查1、是否有电池插入;2、电源路径是否发生变化(BATFET);3、检查电池状态:未充电、充电、充电结束,调度recalc_work重新计算SoC;
4. 关机保存SOC和iavg:
report_state_of_charge -> report_cc_based_soc -> backup_soc_and_iavg
-> qpnp_masked_write_base(chip, chip->soc_storage_addr,
SOC_STORAGE_MASK, (soc + 1) << 1);
开始充电、充电结束、计算soc(calculate_state_of_charge)时都会调用report_state_of_charge;
也就是每次新的SOC都会存到bms中;
5.开机时probe会加载上次关机时保存的soc:
load_shutdown_data -> read_shutdown_soc -> rc = qpnp_read_wrapper(chip,
&stored_soc, chip->soc_storage_addr, 1);
6. 当dts中有如下配置会忽略关机时的SoC:
/* Invalidate the shutdown SoC if any of these conditions hold true */ if (chip->ignore_shutdown_soc <span style="white-space:pre"> </span>|| invalid_stored_soc <span style="white-space:pre"> </span>|| offmode_battery_replaced <span style="white-space:pre"> </span>|| shutdown_soc_out_of_limit) {
这意味着开机时不会用上次关机SOC来修正开机时读到的OCV(PON_OCV_UV)
四、SOC校准
上面参数或多或少都和ocv有关,ocv的变化有:
1. 开机时和上次关机存储的ocv有变化;
2. 长时间suspend后的resume会更新ocv;
3. 手动更新ocv;
4. 低电会进入adjust_soc()更新ocv;
“在高通8064平台由于电量计对大电流计算不准确,一直亮屏的情况(没有经历睡眠唤醒的ocv更新与CC RST)会导致关机电压到达3.74V。要想解决这个问题必须使得校准SOC可以正常工作。但是当满电时开机就会记录ocv的值偏高,导致快要低电时不能很好的校准soc。所以有必要在马上进入低电(15%)时做一次模拟开机一次(电量计RERST
CC=0从soc找出ocv )使得last_ocv_uv降下来,才可以完美发挥adjust_soc的作用,使得关机电压能一直能到3.4V左右。”
当bms计算的SoC在98以上、等于soc_est或者soc_est在adjust-soc-low-threshold以上时不做修正直接返回;
static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params, int soc, int batt_temp) { int ibat_ua = 0, vbat_uv = 0; int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0; int delta_ocv_uv = 0; int n = 0; int rc_new_uah = 0; int pc_new = 0; int soc_new = 0; int slope = 0; int rc = 0; int delta_ocv_uv_limit = 0; int correction_limit_uv = 0; rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv); if (rc < 0) { pr_err("simultaneous vbat ibat failed err = %d\n", rc); goto out; } very_low_voltage_check(chip, vbat_uv); cv_voltage_check(chip, vbat_uv); delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000); ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt_mohm)/1000; pc_est = calculate_pc(chip, ocv_est_uv, batt_temp); soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100, (s64)params->fcc_uah - params->uuc_uah); soc_est = bound_soc(soc_est); /* never adjust during bms reset mode */ if (bms_reset) { printk("bms reset mode, SOC adjustment skipped\n"); goto out; } if (is_battery_charging(chip)) { soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua, batt_temp); /* Skip adjustments if we are in CV or ibat is negative */ if (chip->soc_at_cv != -EINVAL || ibat_ua < 0) goto out; } /* * do not adjust * if soc_est is same as what bms calculated * OR if soc_est > adjust_soc_low_threshold * OR if soc is above 90 * because we might pull it low * and cause a bad user experience */ if (!wake_lock_active(&chip->low_voltage_wake_lock) && (soc_est == soc || soc_est > chip->adjust_soc_low_threshold || soc >= NO_ADJUST_HIGH_SOC_THRESHOLD)) goto out; if (chip->last_soc_est == -EINVAL) chip->last_soc_est = soc; n = min(200, max(1 , soc + soc_est + chip->last_soc_est)); chip->last_soc_est = soc_est; pc = calculate_pc(chip, chip->last_ocv_uv, chip->last_ocv_temp); if (pc > 0) { pc_new = calculate_pc(chip, chip->last_ocv_uv - (++slope * 1000), chip->last_ocv_temp); while (pc_new == pc) { /* start taking 10mV steps */ slope = slope + 10; pc_new = calculate_pc(chip, chip->last_ocv_uv - (slope * 1000), chip->last_ocv_temp); } } else { /* * pc is already at the lowest point, * assume 1 millivolt translates to 1% pc */ pc = 1; pc_new = 0; slope = 1; } delta_ocv_uv = div_s64((soc - soc_est) * (s64)slope * 1000, n * (pc - pc_new)); if (abs(delta_ocv_uv) > delta_ocv_uv_limit) { printk("limiting delta ocv %d limit = %d\n", delta_ocv_uv, delta_ocv_uv_limit); if (delta_ocv_uv > 0) delta_ocv_uv = delta_ocv_uv_limit; else delta_ocv_uv = -1 * delta_ocv_uv_limit; printk("new delta ocv = %d\n", delta_ocv_uv); } if (wake_lock_active(&chip->low_voltage_wake_lock)) { /* when in the cutoff region, do not correct upwards */ delta_ocv_uv = max(0, delta_ocv_uv); goto skip_limits; } if (chip->last_ocv_uv > chip->flat_ocv_threshold_uv) correction_limit_uv = chip->high_ocv_correction_limit_uv;// [email protected] else correction_limit_uv = chip->low_ocv_correction_limit_uv; // [email protected] if (abs(delta_ocv_uv) > correction_limit_uv) { printk("limiting delta ocv %d limit = %d\n", delta_ocv_uv, correction_limit_uv); if (delta_ocv_uv > 0) delta_ocv_uv = correction_limit_uv; else delta_ocv_uv = -correction_limit_uv; printk("new delta ocv = %d\n", delta_ocv_uv); } skip_limits: chip->last_ocv_uv -= delta_ocv_uv; if (chip->last_ocv_uv >= chip->max_voltage_uv) chip->last_ocv_uv = chip->max_voltage_uv; /* calculate the soc based on this new ocv */ pc_new = calculate_pc(chip, chip->last_ocv_uv, chip->last_ocv_temp); rc_new_uah = (params->fcc_uah * pc_new) / 100; soc_new = (rc_new_uah - params->cc_uah - params->uuc_uah)*100 / (params->fcc_uah - params->uuc_uah); soc_new = bound_soc(soc_new); /* * if soc_new is ZERO force it higher so that phone doesnt report soc=0 * soc = 0 should happen only when soc_est is above a set value */ if (soc_new == 0 && soc_est >= chip->hold_soc_est) soc_new = 1; soc = soc_new; out: printk("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, pc_new = %d, soc_new = %d, rbatt = %d, slope = %d\n", ibat_ua, vbat_uv, ocv_est_uv, pc_est, soc_est, n, delta_ocv_uv, chip->last_ocv_uv, pc_new, soc_new, params->rbatt_mohm, slope); return soc; }
参考:
http://blog.csdn.net/linux_devices_driver/article/details/20762839
附件:运行中log
调用栈:
<6>[
406.051003] enabled source qpnp_soc_wake
<6>[ 406.075253] batt_temp phy = 301 meas = 0xc090d488c1a153e4 // 计算CC <6>[ 406.075351] last_good_ocv_raw= 0x922a, last_good_ocv_uv= 3738332uV <6>[ 406.075363] cc_raw= 0xffffffffffd631b1 <6>[ 406.075410] tm_sec = 2425, delta_s = 20 <6>[ 406.075423] fcc = 4690000 uAh <6>[ 406.075434] FCC = 4690000uAh batt_temp = 301 <6>[ 406.075451] pc = 24 % for ocv = 3738332 uv batt_temp = 298 <6>[ 406.075464] ocv_uv = 3738332 pc = 24 <6>[ 406.075474] ocv_charge_uah = 1125600uAh <6>[ 406.084235] cc = -2739791, die_temp = 37432 <6>[ 406.084249] adjusting_uv = -14864325 <6>[ 406.084262] adjusting by factor: 3291/3061 = 107% <6>[ 406.084273] result_uv = -15981213 <6>[ 406.084286] software_cc = -16133, added cc_uah = -797 <6>[ 406.084298] resetting cc manually with flags 128 <6>[ 406.093446] shdw_cc = -2738411, die_temp = 37384 <6>[ 406.093459] adjusting_uv = -14856838 <6>[ 406.093471] adjusting by factor: 3291/3061 = 107% <6>[ 406.093482] result_uv = -15973163 <6>[ 406.093494] software_sw_cc = -16131, added cc_uah = -797 <6>[ 406.093506] resetting cc manually with flags 64 <6>[ 406.093658] cc_uah = -16930uAh raw->cc = ffffffffffd631b1, shdw_cc_uah = -16928uAh raw->shdw_cc = ffffffffffd63715 <6>[ 406.093677] rbatt_mohm = 113 <6>[ 406.093689] delta_cc = -797 iavg_ua = -143460 // 计算UUC:calculate_unusable_charge_uah -> calculate_termination_uuc <6>[ 406.093701] iavg_samples_ma[0] = 250 <6>[ 406.093711] iavg_samples_ma[1] = 250 <6>[ 406.093721] iavg_samples_ma[2] = 250 <6>[ 406.093731] iavg_samples_ma[3] = 250 <6>[ 406.093741] iavg_samples_ma[4] = 250 <6>[ 406.093751] iavg_samples_ma[5] = 250 <6>[ 406.093761] iavg_samples_ma[6] = 250 <6>[ 406.093771] iavg_samples_ma[7] = 250 <6>[ 406.093781] iavg_samples_ma[8] = 250 <6>[ 406.093791] iavg_samples_ma[9] = 250 <6>[ 406.093801] iavg_samples_ma[10] = 250 <6>[ 406.093811] iavg_samples_ma[11] = 250 <6>[ 406.093821] iavg_samples_ma[12] = 250 <6>[ 406.093831] iavg_samples_ma[13] = 250 <6>[ 406.093841] iavg_samples_ma[14] = 250 <6>[ 406.093851] iavg_samples_ma[15] = 250 <6>[ 406.093869] pc = 0 % for ocv = 3400000 uv batt_temp = 301 <6>[ 406.093886] For uuc_iavg_ma = 250, unusable_rbatt = 0 unusable_uv = 3400000 unusable_pc = 0 rbatt_pc = 0 uuc = 0 <6>[ 406.093901] uuc_iavg_ma = 250 uuc with iavg = 0 <6>[ 406.093913] UUC = 0uAh <6>[ 406.093935] RUC = 1142530uAh <6>[ 406.093947] SOC before adjustment = 24 <6>[ 406.098457] pc = 18 % for ocv = 3717671 uv batt_temp = 301 <6>[ 406.098499] CC CHG SOC 24 <6>[ 406.098519] ibat_ua = -169206, vbat_uv = 3736791, ocv_est_uv = 3717671, pc_est = 18, soc_est = 18, n = 0, delta_ocv_uv = 0, last_ocv_uv = 3738332, pc_new = 0, soc_new = 0, rbatt = 113, slope = 0 <6>[ 406.108713] mvolts phy = 3737379 meas = 0x390723 <6>[ 406.108730] not clamping, using soc = 24, vbat = 3737379 and cutoff = 3400000 // 3737379 <6>[ 406.108777] CC based calculated SOC = 24 <6>[ 406.113307] batt_temp phy = 302 meas = 0x12d00000012 <6>[ 406.113391] last_soc = 24, calculated_soc = 24, soc = 24, time since last change = 402 <6>[ 406.113433] Reported SOC = 24 <6>[ 406.113471] disabled source qpnp_soc_wake