Loading drivers/power/supply/qcom/qg-battery-profile.c +10 −8 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "QG-K: %s: " fmt, __func__ Loading Loading @@ -100,7 +100,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, rc = -EINVAL; } else { /* OCV is passed as deci-uV - 10^-4 V */ soc = interpolate_soc(&battery->profile[bp.table_index], soc = qg_interpolate_soc( &battery->profile[bp.table_index], bp.batt_temp, UV_TO_DECIUV(bp.ocv_uv)); soc = CAP(QG_MIN_SOC, QG_MAX_SOC, soc); rc = put_user(soc, &bp_user->soc); Loading @@ -120,7 +121,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { ocv_uv = interpolate_var( ocv_uv = qg_interpolate_var( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); ocv_uv = DECIUV_TO_UV(ocv_uv); Loading @@ -142,7 +143,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { fcc_mah = interpolate_single_row_lut( fcc_mah = qg_interpolate_single_row_lut( &battery->profile[bp.table_index], bp.batt_temp, DEGC_SCALE); fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah); Loading @@ -162,7 +163,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { var = interpolate_var(&battery->profile[bp.table_index], var = qg_interpolate_var( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); var = CAP(QG_MIN_VAR, QG_MAX_VAR, var); rc = put_user(var, &bp_user->var); Loading @@ -182,7 +184,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { slope = interpolate_slope( slope = qg_interpolate_slope( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); slope = CAP(QG_MIN_SLOPE, QG_MAX_SLOPE, slope); Loading Loading @@ -394,7 +396,7 @@ int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging) if (!the_battery || !the_battery->profile_node) return -ENODEV; *soc = interpolate_soc(&the_battery->profile[table_index], *soc = qg_interpolate_soc(&the_battery->profile[table_index], batt_temp, UV_TO_DECIUV(ocv_uv)); *soc = CAP(0, 100, DIV_ROUND_CLOSEST(*soc, 100)); Loading @@ -410,7 +412,7 @@ int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging) if (!the_battery || !the_battery->profile_node) return -ENODEV; fcc_mah = interpolate_single_row_lut( fcc_mah = qg_interpolate_single_row_lut( &the_battery->profile[table_index], batt_temp, DEGC_SCALE); fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah); Loading drivers/power/supply/qcom/qg-core.h +7 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ struct qg_dt { int shutdown_soc_threshold; int min_sleep_time_secs; int sys_min_volt_mv; int fvss_vbat_mv; bool hold_soc_while_full; bool linearize_soc; bool cl_disable; Loading @@ -67,6 +68,7 @@ struct qg_dt { bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; bool fvss_enable; }; struct qg_esr_data { Loading Loading @@ -129,6 +131,7 @@ struct qpnp_qg { bool dc_present; bool charge_full; bool force_soc; bool fvss_active; int charge_status; int charge_type; int chg_iterm_ma; Loading @@ -137,6 +140,8 @@ struct qpnp_qg { int esr_nominal; int soh; int soc_reporting_ready; int last_fifo_v_uv; int last_fifo_i_ua; u32 fifo_done_count; u32 wa_flags; u32 seq_no; Loading @@ -145,6 +150,8 @@ struct qpnp_qg { u32 esr_last; u32 s2_state; u32 s2_state_mask; u32 soc_fvss_entry; u32 vbat_fvss_entry; ktime_t last_user_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; Loading drivers/power/supply/qcom/qg-profile-lib.c +17 −17 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ #include <linux/module.h> Loading @@ -9,7 +9,7 @@ #include "qg-profile-lib.h" #include "qg-defs.h" static int linear_interpolate(int y0, int x0, int y1, int x1, int x) int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x) { if (y0 == y1 || x == x0) return y0; Loading @@ -19,7 +19,7 @@ static int linear_interpolate(int y0, int x0, int y1, int x1, int x) return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); } int interpolate_single_row_lut(struct profile_table_data *lut, int qg_interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale) { int i, result; Loading @@ -45,7 +45,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut, if (x == lut->col_entries[i] * scale) { result = lut->data[0][i]; } else { result = linear_interpolate( result = qg_linear_interpolate( lut->data[0][i-1], lut->col_entries[i-1] * scale, lut->data[0][i], Loading @@ -56,7 +56,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut, return result; } int interpolate_soc(struct profile_table_data *lut, int qg_interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv) { int i, j, soc_high, soc_low, soc; Loading Loading @@ -87,7 +87,7 @@ int interpolate_soc(struct profile_table_data *lut, if (ocv >= lut->data[i][j]) { if (ocv == lut->data[i][j]) return lut->row_entries[i]; soc = linear_interpolate( soc = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i - 1], Loading @@ -108,7 +108,7 @@ int interpolate_soc(struct profile_table_data *lut, for (i = 0; i < rows-1; i++) { if (soc_high == 0 && is_between(lut->data[i][j], lut->data[i+1][j], ocv)) { soc_high = linear_interpolate( soc_high = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i + 1], Loading @@ -118,7 +118,7 @@ int interpolate_soc(struct profile_table_data *lut, if (soc_low == 0 && is_between(lut->data[i][j-1], lut->data[i+1][j-1], ocv)) { soc_low = linear_interpolate( soc_low = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j-1], lut->row_entries[i + 1], Loading @@ -127,7 +127,7 @@ int interpolate_soc(struct profile_table_data *lut, } if (soc_high && soc_low) { soc = linear_interpolate( soc = qg_linear_interpolate( soc_low, lut->col_entries[j-1] * DEGC_SCALE, soc_high, Loading @@ -148,7 +148,7 @@ int interpolate_soc(struct profile_table_data *lut, return 10000; } int interpolate_var(struct profile_table_data *lut, int qg_interpolate_var(struct profile_table_data *lut, int batt_temp, int soc) { int i, var1, var2, var, rows, cols; Loading Loading @@ -192,7 +192,7 @@ int interpolate_var(struct profile_table_data *lut, break; if (batt_temp == lut->col_entries[i] * DEGC_SCALE) { var = linear_interpolate( var = qg_linear_interpolate( lut->data[row1][i], lut->row_entries[row1], lut->data[row2][i], Loading @@ -201,21 +201,21 @@ int interpolate_var(struct profile_table_data *lut, return var; } var1 = linear_interpolate( var1 = qg_linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); var2 = linear_interpolate( var2 = qg_linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); var = linear_interpolate( var = qg_linear_interpolate( var1, lut->row_entries[row1], var2, Loading @@ -225,7 +225,7 @@ int interpolate_var(struct profile_table_data *lut, return var; } int interpolate_slope(struct profile_table_data *lut, int qg_interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc) { int i, ocvrow1, ocvrow2, rows, cols; Loading Loading @@ -277,14 +277,14 @@ int interpolate_slope(struct profile_table_data *lut, lut->row_entries[row2]); return slope; } ocvrow1 = linear_interpolate( ocvrow1 = qg_linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); ocvrow2 = linear_interpolate( ocvrow2 = qg_linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], Loading drivers/power/supply/qcom/qg-profile-lib.h +6 −5 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ #ifndef __QG_PROFILE_LIB_H__ Loading @@ -15,13 +15,14 @@ struct profile_table_data { int **data; }; int interpolate_single_row_lut(struct profile_table_data *lut, int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x); int qg_interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale); int interpolate_soc(struct profile_table_data *lut, int qg_interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv); int interpolate_var(struct profile_table_data *lut, int qg_interpolate_var(struct profile_table_data *lut, int batt_temp, int soc); int interpolate_slope(struct profile_table_data *lut, int qg_interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc); #endif /*__QG_PROFILE_LIB_H__ */ drivers/power/supply/qcom/qg-soc.c +91 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "qg-reg.h" #include "qg-util.h" #include "qg-defs.h" #include "qg-profile-lib.h" #include "qg-soc.h" #define DEFAULT_UPDATE_TIME_MS 64000 Loading Loading @@ -45,6 +46,11 @@ static ssize_t soc_interval_ms_store(struct device *dev, } DEVICE_ATTR_RW(soc_interval_ms); static int qg_fvss_delta_soc_interval_ms = 10000; module_param_named( fvss_soc_interval_ms, qg_fvss_delta_soc_interval_ms, int, 0600 ); static int qg_delta_soc_cold_interval_ms = 4000; static ssize_t soc_cold_interval_ms_show(struct device *dev, struct device_attribute *attr, char *buf) Loading Loading @@ -87,6 +93,84 @@ static ssize_t maint_soc_update_ms_store(struct device *dev, } DEVICE_ATTR_RW(maint_soc_update_ms); /* FVSS scaling only based on VBAT */ static int qg_fvss_vbat_scaling = 1; module_param_named( fvss_vbat_scaling, qg_fvss_vbat_scaling, int, 0600 ); static int qg_process_fvss_soc(struct qpnp_qg *chip, int sys_soc) { int rc, vbat_uv = 0, vbat_cutoff_uv = chip->dt.vbatt_cutoff_mv * 1000; int soc_vbat = 0, wt_vbat = 0, wt_sys = 0, soc_fvss = 0; if (!chip->dt.fvss_enable) return 0; if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) goto exit_soc_scale; rc = qg_get_battery_voltage(chip, &vbat_uv); if (rc < 0) goto exit_soc_scale; if (!chip->last_fifo_v_uv) chip->last_fifo_v_uv = vbat_uv; if (chip->last_fifo_v_uv > (chip->dt.fvss_vbat_mv * 1000)) { qg_dbg(chip, QG_DEBUG_SOC, "FVSS: last_fifo_v=%d fvss_entry_uv=%d - exit\n", chip->last_fifo_v_uv, chip->dt.fvss_vbat_mv * 1000); goto exit_soc_scale; } /* Enter FVSS */ if (!chip->fvss_active) { chip->vbat_fvss_entry = CAP(vbat_cutoff_uv, chip->dt.fvss_vbat_mv * 1000, chip->last_fifo_v_uv); chip->soc_fvss_entry = sys_soc; chip->fvss_active = true; } else if (chip->last_fifo_v_uv > chip->vbat_fvss_entry) { /* VBAT has gone beyond the entry voltage */ chip->vbat_fvss_entry = chip->last_fifo_v_uv; chip->soc_fvss_entry = sys_soc; } soc_vbat = qg_linear_interpolate(chip->soc_fvss_entry, chip->vbat_fvss_entry, 0, vbat_cutoff_uv, chip->last_fifo_v_uv); soc_vbat = CAP(0, 100, soc_vbat); if (qg_fvss_vbat_scaling) { wt_vbat = 100; wt_sys = 0; } else { wt_sys = qg_linear_interpolate(100, chip->soc_fvss_entry, 0, 0, sys_soc); wt_sys = CAP(0, 100, wt_sys); wt_vbat = 100 - wt_sys; } soc_fvss = ((soc_vbat * wt_vbat) + (sys_soc * wt_sys)) / 100; soc_fvss = CAP(0, 100, soc_fvss); qg_dbg(chip, QG_DEBUG_SOC, "FVSS: vbat_fvss_entry=%d soc_fvss_entry=%d cutoff_uv=%d vbat_uv=%d fifo_avg_v=%d soc_vbat=%d sys_soc=%d wt_vbat=%d wt_sys=%d soc_fvss=%d\n", chip->vbat_fvss_entry, chip->soc_fvss_entry, vbat_cutoff_uv, vbat_uv, chip->last_fifo_v_uv, soc_vbat, sys_soc, wt_vbat, wt_sys, soc_fvss); return soc_fvss; exit_soc_scale: chip->fvss_active = false; return sys_soc; } int qg_adjust_sys_soc(struct qpnp_qg *chip) { int soc, vbat_uv, rc; Loading @@ -113,8 +197,11 @@ int qg_adjust_sys_soc(struct qpnp_qg *chip) soc = DIV_ROUND_CLOSEST(chip->sys_soc, 100); } qg_dbg(chip, QG_DEBUG_SOC, "last_adj_sys_soc=%d adj_sys_soc=%d\n", chip->last_adj_ssoc, soc); qg_dbg(chip, QG_DEBUG_SOC, "sys_soc=%d adjusted sys_soc=%d\n", chip->sys_soc, soc); soc = qg_process_fvss_soc(chip, soc); chip->last_adj_ssoc = soc; return soc; Loading Loading @@ -144,6 +231,8 @@ static void get_next_update_time(struct qpnp_qg *chip) else if (chip->maint_soc > 0 && chip->maint_soc >= chip->recharge_soc) /* if in maintenance mode scale slower */ min_delta_soc_interval_ms = qg_maint_soc_update_ms; else if (chip->fvss_active) min_delta_soc_interval_ms = qg_fvss_delta_soc_interval_ms; if (!min_delta_soc_interval_ms) min_delta_soc_interval_ms = 1000; /* 1 second */ Loading Loading
drivers/power/supply/qcom/qg-battery-profile.c +10 −8 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "QG-K: %s: " fmt, __func__ Loading Loading @@ -100,7 +100,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, rc = -EINVAL; } else { /* OCV is passed as deci-uV - 10^-4 V */ soc = interpolate_soc(&battery->profile[bp.table_index], soc = qg_interpolate_soc( &battery->profile[bp.table_index], bp.batt_temp, UV_TO_DECIUV(bp.ocv_uv)); soc = CAP(QG_MIN_SOC, QG_MAX_SOC, soc); rc = put_user(soc, &bp_user->soc); Loading @@ -120,7 +121,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { ocv_uv = interpolate_var( ocv_uv = qg_interpolate_var( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); ocv_uv = DECIUV_TO_UV(ocv_uv); Loading @@ -142,7 +143,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { fcc_mah = interpolate_single_row_lut( fcc_mah = qg_interpolate_single_row_lut( &battery->profile[bp.table_index], bp.batt_temp, DEGC_SCALE); fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah); Loading @@ -162,7 +163,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { var = interpolate_var(&battery->profile[bp.table_index], var = qg_interpolate_var( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); var = CAP(QG_MIN_VAR, QG_MAX_VAR, var); rc = put_user(var, &bp_user->var); Loading @@ -182,7 +184,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { slope = interpolate_slope( slope = qg_interpolate_slope( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); slope = CAP(QG_MIN_SLOPE, QG_MAX_SLOPE, slope); Loading Loading @@ -394,7 +396,7 @@ int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging) if (!the_battery || !the_battery->profile_node) return -ENODEV; *soc = interpolate_soc(&the_battery->profile[table_index], *soc = qg_interpolate_soc(&the_battery->profile[table_index], batt_temp, UV_TO_DECIUV(ocv_uv)); *soc = CAP(0, 100, DIV_ROUND_CLOSEST(*soc, 100)); Loading @@ -410,7 +412,7 @@ int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging) if (!the_battery || !the_battery->profile_node) return -ENODEV; fcc_mah = interpolate_single_row_lut( fcc_mah = qg_interpolate_single_row_lut( &the_battery->profile[table_index], batt_temp, DEGC_SCALE); fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah); Loading
drivers/power/supply/qcom/qg-core.h +7 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ struct qg_dt { int shutdown_soc_threshold; int min_sleep_time_secs; int sys_min_volt_mv; int fvss_vbat_mv; bool hold_soc_while_full; bool linearize_soc; bool cl_disable; Loading @@ -67,6 +68,7 @@ struct qg_dt { bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; bool fvss_enable; }; struct qg_esr_data { Loading Loading @@ -129,6 +131,7 @@ struct qpnp_qg { bool dc_present; bool charge_full; bool force_soc; bool fvss_active; int charge_status; int charge_type; int chg_iterm_ma; Loading @@ -137,6 +140,8 @@ struct qpnp_qg { int esr_nominal; int soh; int soc_reporting_ready; int last_fifo_v_uv; int last_fifo_i_ua; u32 fifo_done_count; u32 wa_flags; u32 seq_no; Loading @@ -145,6 +150,8 @@ struct qpnp_qg { u32 esr_last; u32 s2_state; u32 s2_state_mask; u32 soc_fvss_entry; u32 vbat_fvss_entry; ktime_t last_user_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; Loading
drivers/power/supply/qcom/qg-profile-lib.c +17 −17 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ #include <linux/module.h> Loading @@ -9,7 +9,7 @@ #include "qg-profile-lib.h" #include "qg-defs.h" static int linear_interpolate(int y0, int x0, int y1, int x1, int x) int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x) { if (y0 == y1 || x == x0) return y0; Loading @@ -19,7 +19,7 @@ static int linear_interpolate(int y0, int x0, int y1, int x1, int x) return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); } int interpolate_single_row_lut(struct profile_table_data *lut, int qg_interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale) { int i, result; Loading @@ -45,7 +45,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut, if (x == lut->col_entries[i] * scale) { result = lut->data[0][i]; } else { result = linear_interpolate( result = qg_linear_interpolate( lut->data[0][i-1], lut->col_entries[i-1] * scale, lut->data[0][i], Loading @@ -56,7 +56,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut, return result; } int interpolate_soc(struct profile_table_data *lut, int qg_interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv) { int i, j, soc_high, soc_low, soc; Loading Loading @@ -87,7 +87,7 @@ int interpolate_soc(struct profile_table_data *lut, if (ocv >= lut->data[i][j]) { if (ocv == lut->data[i][j]) return lut->row_entries[i]; soc = linear_interpolate( soc = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i - 1], Loading @@ -108,7 +108,7 @@ int interpolate_soc(struct profile_table_data *lut, for (i = 0; i < rows-1; i++) { if (soc_high == 0 && is_between(lut->data[i][j], lut->data[i+1][j], ocv)) { soc_high = linear_interpolate( soc_high = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i + 1], Loading @@ -118,7 +118,7 @@ int interpolate_soc(struct profile_table_data *lut, if (soc_low == 0 && is_between(lut->data[i][j-1], lut->data[i+1][j-1], ocv)) { soc_low = linear_interpolate( soc_low = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j-1], lut->row_entries[i + 1], Loading @@ -127,7 +127,7 @@ int interpolate_soc(struct profile_table_data *lut, } if (soc_high && soc_low) { soc = linear_interpolate( soc = qg_linear_interpolate( soc_low, lut->col_entries[j-1] * DEGC_SCALE, soc_high, Loading @@ -148,7 +148,7 @@ int interpolate_soc(struct profile_table_data *lut, return 10000; } int interpolate_var(struct profile_table_data *lut, int qg_interpolate_var(struct profile_table_data *lut, int batt_temp, int soc) { int i, var1, var2, var, rows, cols; Loading Loading @@ -192,7 +192,7 @@ int interpolate_var(struct profile_table_data *lut, break; if (batt_temp == lut->col_entries[i] * DEGC_SCALE) { var = linear_interpolate( var = qg_linear_interpolate( lut->data[row1][i], lut->row_entries[row1], lut->data[row2][i], Loading @@ -201,21 +201,21 @@ int interpolate_var(struct profile_table_data *lut, return var; } var1 = linear_interpolate( var1 = qg_linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); var2 = linear_interpolate( var2 = qg_linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); var = linear_interpolate( var = qg_linear_interpolate( var1, lut->row_entries[row1], var2, Loading @@ -225,7 +225,7 @@ int interpolate_var(struct profile_table_data *lut, return var; } int interpolate_slope(struct profile_table_data *lut, int qg_interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc) { int i, ocvrow1, ocvrow2, rows, cols; Loading Loading @@ -277,14 +277,14 @@ int interpolate_slope(struct profile_table_data *lut, lut->row_entries[row2]); return slope; } ocvrow1 = linear_interpolate( ocvrow1 = qg_linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); ocvrow2 = linear_interpolate( ocvrow2 = qg_linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], Loading
drivers/power/supply/qcom/qg-profile-lib.h +6 −5 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ #ifndef __QG_PROFILE_LIB_H__ Loading @@ -15,13 +15,14 @@ struct profile_table_data { int **data; }; int interpolate_single_row_lut(struct profile_table_data *lut, int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x); int qg_interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale); int interpolate_soc(struct profile_table_data *lut, int qg_interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv); int interpolate_var(struct profile_table_data *lut, int qg_interpolate_var(struct profile_table_data *lut, int batt_temp, int soc); int interpolate_slope(struct profile_table_data *lut, int qg_interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc); #endif /*__QG_PROFILE_LIB_H__ */
drivers/power/supply/qcom/qg-soc.c +91 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "qg-reg.h" #include "qg-util.h" #include "qg-defs.h" #include "qg-profile-lib.h" #include "qg-soc.h" #define DEFAULT_UPDATE_TIME_MS 64000 Loading Loading @@ -45,6 +46,11 @@ static ssize_t soc_interval_ms_store(struct device *dev, } DEVICE_ATTR_RW(soc_interval_ms); static int qg_fvss_delta_soc_interval_ms = 10000; module_param_named( fvss_soc_interval_ms, qg_fvss_delta_soc_interval_ms, int, 0600 ); static int qg_delta_soc_cold_interval_ms = 4000; static ssize_t soc_cold_interval_ms_show(struct device *dev, struct device_attribute *attr, char *buf) Loading Loading @@ -87,6 +93,84 @@ static ssize_t maint_soc_update_ms_store(struct device *dev, } DEVICE_ATTR_RW(maint_soc_update_ms); /* FVSS scaling only based on VBAT */ static int qg_fvss_vbat_scaling = 1; module_param_named( fvss_vbat_scaling, qg_fvss_vbat_scaling, int, 0600 ); static int qg_process_fvss_soc(struct qpnp_qg *chip, int sys_soc) { int rc, vbat_uv = 0, vbat_cutoff_uv = chip->dt.vbatt_cutoff_mv * 1000; int soc_vbat = 0, wt_vbat = 0, wt_sys = 0, soc_fvss = 0; if (!chip->dt.fvss_enable) return 0; if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) goto exit_soc_scale; rc = qg_get_battery_voltage(chip, &vbat_uv); if (rc < 0) goto exit_soc_scale; if (!chip->last_fifo_v_uv) chip->last_fifo_v_uv = vbat_uv; if (chip->last_fifo_v_uv > (chip->dt.fvss_vbat_mv * 1000)) { qg_dbg(chip, QG_DEBUG_SOC, "FVSS: last_fifo_v=%d fvss_entry_uv=%d - exit\n", chip->last_fifo_v_uv, chip->dt.fvss_vbat_mv * 1000); goto exit_soc_scale; } /* Enter FVSS */ if (!chip->fvss_active) { chip->vbat_fvss_entry = CAP(vbat_cutoff_uv, chip->dt.fvss_vbat_mv * 1000, chip->last_fifo_v_uv); chip->soc_fvss_entry = sys_soc; chip->fvss_active = true; } else if (chip->last_fifo_v_uv > chip->vbat_fvss_entry) { /* VBAT has gone beyond the entry voltage */ chip->vbat_fvss_entry = chip->last_fifo_v_uv; chip->soc_fvss_entry = sys_soc; } soc_vbat = qg_linear_interpolate(chip->soc_fvss_entry, chip->vbat_fvss_entry, 0, vbat_cutoff_uv, chip->last_fifo_v_uv); soc_vbat = CAP(0, 100, soc_vbat); if (qg_fvss_vbat_scaling) { wt_vbat = 100; wt_sys = 0; } else { wt_sys = qg_linear_interpolate(100, chip->soc_fvss_entry, 0, 0, sys_soc); wt_sys = CAP(0, 100, wt_sys); wt_vbat = 100 - wt_sys; } soc_fvss = ((soc_vbat * wt_vbat) + (sys_soc * wt_sys)) / 100; soc_fvss = CAP(0, 100, soc_fvss); qg_dbg(chip, QG_DEBUG_SOC, "FVSS: vbat_fvss_entry=%d soc_fvss_entry=%d cutoff_uv=%d vbat_uv=%d fifo_avg_v=%d soc_vbat=%d sys_soc=%d wt_vbat=%d wt_sys=%d soc_fvss=%d\n", chip->vbat_fvss_entry, chip->soc_fvss_entry, vbat_cutoff_uv, vbat_uv, chip->last_fifo_v_uv, soc_vbat, sys_soc, wt_vbat, wt_sys, soc_fvss); return soc_fvss; exit_soc_scale: chip->fvss_active = false; return sys_soc; } int qg_adjust_sys_soc(struct qpnp_qg *chip) { int soc, vbat_uv, rc; Loading @@ -113,8 +197,11 @@ int qg_adjust_sys_soc(struct qpnp_qg *chip) soc = DIV_ROUND_CLOSEST(chip->sys_soc, 100); } qg_dbg(chip, QG_DEBUG_SOC, "last_adj_sys_soc=%d adj_sys_soc=%d\n", chip->last_adj_ssoc, soc); qg_dbg(chip, QG_DEBUG_SOC, "sys_soc=%d adjusted sys_soc=%d\n", chip->sys_soc, soc); soc = qg_process_fvss_soc(chip, soc); chip->last_adj_ssoc = soc; return soc; Loading Loading @@ -144,6 +231,8 @@ static void get_next_update_time(struct qpnp_qg *chip) else if (chip->maint_soc > 0 && chip->maint_soc >= chip->recharge_soc) /* if in maintenance mode scale slower */ min_delta_soc_interval_ms = qg_maint_soc_update_ms; else if (chip->fvss_active) min_delta_soc_interval_ms = qg_fvss_delta_soc_interval_ms; if (!min_delta_soc_interval_ms) min_delta_soc_interval_ms = 1000; /* 1 second */ Loading