Loading drivers/power/supply/qcom/fg-alg.c +95 −2 Original line number Diff line number Diff line Loading @@ -798,12 +798,16 @@ static int ttf_lerp(const struct ttf_pt *pts, size_t tablesize, static int get_time_to_full_locked(struct ttf *ttf, int *val) { struct step_chg_data *step_chg_data = ttf->step_chg_data; struct range_data *step_chg_cfg = ttf->step_chg_cfg; int rc, ibatt_avg, vbatt_avg, rbatt = 0, msoc = 0, act_cap_mah = 0, i_cc2cv = 0, soc_cc2cv, tau, divisor, iterm = 0, ttf_mode = 0, i, soc_per_step, msoc_this_step, msoc_next_step, ibatt_this_step, t_predicted_this_step, ttf_slope, t_predicted_cv, t_predicted = 0, charge_type = 0, t_predicted_cv, t_predicted = 0, charge_type = 0, i_step, float_volt_uv = 0; int vbatt_now, multiplier, curr_window = 0, pbatt_avg; bool power_approx = false; s64 delta_ms; rc = ttf->get_ttf_param(ttf->data, TTF_MSOC, &msoc); Loading Loading @@ -887,9 +891,13 @@ static int get_time_to_full_locked(struct ttf *ttf, int *val) pr_err("failed to get charge_type rc=%d\n", rc); return rc; } pr_debug("TTF: mode: %d\n", ttf->mode); /* estimated battery current at the CC to CV transition */ switch (ttf->mode) { case TTF_MODE_NORMAL: case TTF_MODE_V_STEP_CHG: i_cc2cv = ibatt_avg * vbatt_avg / max(MILLI_UNIT, float_volt_uv / MILLI_UNIT); break; Loading Loading @@ -945,6 +953,91 @@ static int get_time_to_full_locked(struct ttf *ttf, int *val) msoc_this_step, msoc_next_step, ibatt_this_step, t_predicted_this_step); } break; case TTF_MODE_V_STEP_CHG: if (!step_chg_data || !step_chg_cfg) break; pbatt_avg = vbatt_avg * ibatt_avg; rc = ttf->get_ttf_param(ttf->data, TTF_VBAT, &vbatt_now); if (rc < 0) { pr_err("failed to get battery voltage, rc=%d\n", rc); return rc; } curr_window = ttf->step_chg_num_params - 1; for (i = 0; i < ttf->step_chg_num_params; i++) { if (is_between(step_chg_cfg[i].low_threshold, step_chg_cfg[i].high_threshold, vbatt_now)) curr_window = i; } pr_debug("TTF: curr_window: %d pbatt_avg: %d\n", curr_window, pbatt_avg); t_predicted_this_step = 0; for (i = 0; i < ttf->step_chg_num_params; i++) { /* * If Ibatt_avg differs by step charging threshold by * more than 100 mA, then use power approximation to * get charging current step. */ if (step_chg_cfg[i].value - ibatt_avg > 100) power_approx = true; /* Calculate OCV for each window */ if (power_approx) { i_step = pbatt_avg / max((u32)MILLI_UNIT, (step_chg_cfg[i].high_threshold / MILLI_UNIT)); } else { if (i == curr_window) i_step = ((step_chg_cfg[i].value / MILLI_UNIT) + ibatt_avg) / 2; else i_step = (step_chg_cfg[i].value / MILLI_UNIT); } step_chg_data[i].ocv = step_chg_cfg[i].high_threshold - (rbatt * i_step); /* Calculate SOC for each window */ step_chg_data[i].soc = (float_volt_uv - step_chg_data[i].ocv) / OCV_SLOPE_UV; step_chg_data[i].soc = 100 - step_chg_data[i].soc; /* Calculate CC time for each window */ multiplier = act_cap_mah * HOURS_TO_SECONDS; if (curr_window > 0 && i < curr_window) t_predicted_this_step = 0; else if (i == curr_window) t_predicted_this_step = div_s64((s64)multiplier * (step_chg_data[i].soc - msoc), i_step); else if (i > 0) t_predicted_this_step = div_s64((s64)multiplier * (step_chg_data[i].soc - step_chg_data[i - 1].soc), i_step); if (t_predicted_this_step < 0) t_predicted_this_step = 0; t_predicted_this_step = DIV_ROUND_CLOSEST(t_predicted_this_step, 100); pr_debug("TTF: step: %d i_step: %d OCV: %d SOC: %d t_pred: %d\n", i, i_step, step_chg_data[i].ocv, step_chg_data[i].soc, t_predicted_this_step); t_predicted += t_predicted_this_step; } break; default: pr_err("TTF mode %d is not supported\n", ttf->mode); Loading drivers/power/supply/qcom/fg-alg.h +17 −0 Original line number Diff line number Diff line Loading @@ -13,11 +13,18 @@ #ifndef __FG_ALG_H__ #define __FG_ALG_H__ #include "step-chg-jeita.h" #define BUCKET_COUNT 8 #define BUCKET_SOC_PCT (256 / BUCKET_COUNT) #define MAX_CC_STEPS 20 #define MAX_TTF_SAMPLES 10 #define is_between(left, right, value) \ (((left) >= (right) && (left) >= (value) \ && (value) >= (right)) \ || ((left) <= (right) && (left) <= (value) \ && (value) <= (right))) struct cycle_counter { void *data; char str_buf[BUCKET_COUNT * 8]; Loading Loading @@ -63,6 +70,7 @@ struct cap_learning { enum ttf_mode { TTF_MODE_NORMAL = 0, TTF_MODE_QNOVO, TTF_MODE_V_STEP_CHG, }; enum ttf_param { Loading Loading @@ -94,12 +102,21 @@ struct ttf_pt { s32 y; }; struct step_chg_data { int ocv; int soc; }; struct ttf { void *data; struct ttf_circ_buf ibatt; struct ttf_circ_buf vbatt; struct ttf_cc_step_data cc_step; struct mutex lock; struct step_chg_data *step_chg_data; struct range_data *step_chg_cfg; bool step_chg_cfg_valid; int step_chg_num_params; int mode; int last_ttf; int input_present; Loading drivers/power/supply/qcom/qpnp-fg-gen4.c +71 −1 Original line number Diff line number Diff line Loading @@ -629,6 +629,8 @@ static int fg_gen4_get_ttf_param(void *data, enum ttf_param param, int *val) case TTF_MODE: if (is_qnovo_en(fg)) *val = TTF_MODE_QNOVO; else if (chip->ttf->step_chg_cfg_valid) *val = TTF_MODE_V_STEP_CHG; else *val = TTF_MODE_NORMAL; break; Loading Loading @@ -924,7 +926,7 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) struct device_node *node = fg->dev->of_node; struct device_node *batt_node, *profile_node; const char *data; int rc, len; int rc, len, i, tuple_len; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { Loading Loading @@ -999,6 +1001,64 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) } } /* * Currently step charging thresholds should be read only for Vbatt * based and not for SOC based. */ if (!of_property_read_bool(profile_node, "qcom,soc-based-step-chg") && of_find_property(profile_node, "qcom,step-chg-ranges", &len) && fg->bp.float_volt_uv > 0 && fg->bp.fastchg_curr_ma > 0) { len /= sizeof(u32); tuple_len = len / (sizeof(struct range_data) / sizeof(u32)); if (tuple_len <= 0 || tuple_len > MAX_STEP_CHG_ENTRIES) return -EINVAL; mutex_lock(&chip->ttf->lock); chip->ttf->step_chg_cfg = kcalloc(len, sizeof(*chip->ttf->step_chg_cfg), GFP_KERNEL); if (!chip->ttf->step_chg_cfg) { mutex_unlock(&chip->ttf->lock); return -ENOMEM; } chip->ttf->step_chg_data = kcalloc(tuple_len, sizeof(*chip->ttf->step_chg_data), GFP_KERNEL); if (!chip->ttf->step_chg_data) { kfree(chip->ttf->step_chg_cfg); mutex_unlock(&chip->ttf->lock); return -ENOMEM; } rc = read_range_data_from_node(profile_node, "qcom,step-chg-ranges", chip->ttf->step_chg_cfg, fg->bp.float_volt_uv, fg->bp.fastchg_curr_ma * 1000); if (rc < 0) { pr_err("Error in reading qcom,step-chg-ranges from battery profile, rc=%d\n", rc); kfree(chip->ttf->step_chg_data); kfree(chip->ttf->step_chg_cfg); chip->ttf->step_chg_cfg = NULL; mutex_unlock(&chip->ttf->lock); return rc; } chip->ttf->step_chg_num_params = tuple_len; chip->ttf->step_chg_cfg_valid = true; mutex_unlock(&chip->ttf->lock); if (chip->ttf->step_chg_cfg_valid) { for (i = 0; i < tuple_len; i++) pr_debug("Vbatt_low: %d Vbatt_high: %d FCC: %d\n", chip->ttf->step_chg_cfg[i].low_threshold, chip->ttf->step_chg_cfg[i].high_threshold, chip->ttf->step_chg_cfg[i].value); } } if (of_find_property(profile_node, "qcom,therm-pull-up", NULL)) { rc = of_property_read_u32(profile_node, "qcom,therm-pull-up", &fg->bp.therm_pull_up_kohms); Loading Loading @@ -1765,6 +1825,7 @@ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) { struct fg_dev *fg = data; struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); u8 status; int rc; Loading @@ -1783,6 +1844,15 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) fg->profile_load_status = PROFILE_NOT_LOADED; fg->soc_reporting_ready = false; fg->batt_id_ohms = -EINVAL; mutex_lock(&chip->ttf->lock); chip->ttf->step_chg_cfg_valid = false; chip->ttf->step_chg_num_params = 0; kfree(chip->ttf->step_chg_cfg); chip->ttf->step_chg_cfg = NULL; kfree(chip->ttf->step_chg_data); chip->ttf->step_chg_data = NULL; mutex_unlock(&chip->ttf->lock); return IRQ_HANDLED; } Loading Loading
drivers/power/supply/qcom/fg-alg.c +95 −2 Original line number Diff line number Diff line Loading @@ -798,12 +798,16 @@ static int ttf_lerp(const struct ttf_pt *pts, size_t tablesize, static int get_time_to_full_locked(struct ttf *ttf, int *val) { struct step_chg_data *step_chg_data = ttf->step_chg_data; struct range_data *step_chg_cfg = ttf->step_chg_cfg; int rc, ibatt_avg, vbatt_avg, rbatt = 0, msoc = 0, act_cap_mah = 0, i_cc2cv = 0, soc_cc2cv, tau, divisor, iterm = 0, ttf_mode = 0, i, soc_per_step, msoc_this_step, msoc_next_step, ibatt_this_step, t_predicted_this_step, ttf_slope, t_predicted_cv, t_predicted = 0, charge_type = 0, t_predicted_cv, t_predicted = 0, charge_type = 0, i_step, float_volt_uv = 0; int vbatt_now, multiplier, curr_window = 0, pbatt_avg; bool power_approx = false; s64 delta_ms; rc = ttf->get_ttf_param(ttf->data, TTF_MSOC, &msoc); Loading Loading @@ -887,9 +891,13 @@ static int get_time_to_full_locked(struct ttf *ttf, int *val) pr_err("failed to get charge_type rc=%d\n", rc); return rc; } pr_debug("TTF: mode: %d\n", ttf->mode); /* estimated battery current at the CC to CV transition */ switch (ttf->mode) { case TTF_MODE_NORMAL: case TTF_MODE_V_STEP_CHG: i_cc2cv = ibatt_avg * vbatt_avg / max(MILLI_UNIT, float_volt_uv / MILLI_UNIT); break; Loading Loading @@ -945,6 +953,91 @@ static int get_time_to_full_locked(struct ttf *ttf, int *val) msoc_this_step, msoc_next_step, ibatt_this_step, t_predicted_this_step); } break; case TTF_MODE_V_STEP_CHG: if (!step_chg_data || !step_chg_cfg) break; pbatt_avg = vbatt_avg * ibatt_avg; rc = ttf->get_ttf_param(ttf->data, TTF_VBAT, &vbatt_now); if (rc < 0) { pr_err("failed to get battery voltage, rc=%d\n", rc); return rc; } curr_window = ttf->step_chg_num_params - 1; for (i = 0; i < ttf->step_chg_num_params; i++) { if (is_between(step_chg_cfg[i].low_threshold, step_chg_cfg[i].high_threshold, vbatt_now)) curr_window = i; } pr_debug("TTF: curr_window: %d pbatt_avg: %d\n", curr_window, pbatt_avg); t_predicted_this_step = 0; for (i = 0; i < ttf->step_chg_num_params; i++) { /* * If Ibatt_avg differs by step charging threshold by * more than 100 mA, then use power approximation to * get charging current step. */ if (step_chg_cfg[i].value - ibatt_avg > 100) power_approx = true; /* Calculate OCV for each window */ if (power_approx) { i_step = pbatt_avg / max((u32)MILLI_UNIT, (step_chg_cfg[i].high_threshold / MILLI_UNIT)); } else { if (i == curr_window) i_step = ((step_chg_cfg[i].value / MILLI_UNIT) + ibatt_avg) / 2; else i_step = (step_chg_cfg[i].value / MILLI_UNIT); } step_chg_data[i].ocv = step_chg_cfg[i].high_threshold - (rbatt * i_step); /* Calculate SOC for each window */ step_chg_data[i].soc = (float_volt_uv - step_chg_data[i].ocv) / OCV_SLOPE_UV; step_chg_data[i].soc = 100 - step_chg_data[i].soc; /* Calculate CC time for each window */ multiplier = act_cap_mah * HOURS_TO_SECONDS; if (curr_window > 0 && i < curr_window) t_predicted_this_step = 0; else if (i == curr_window) t_predicted_this_step = div_s64((s64)multiplier * (step_chg_data[i].soc - msoc), i_step); else if (i > 0) t_predicted_this_step = div_s64((s64)multiplier * (step_chg_data[i].soc - step_chg_data[i - 1].soc), i_step); if (t_predicted_this_step < 0) t_predicted_this_step = 0; t_predicted_this_step = DIV_ROUND_CLOSEST(t_predicted_this_step, 100); pr_debug("TTF: step: %d i_step: %d OCV: %d SOC: %d t_pred: %d\n", i, i_step, step_chg_data[i].ocv, step_chg_data[i].soc, t_predicted_this_step); t_predicted += t_predicted_this_step; } break; default: pr_err("TTF mode %d is not supported\n", ttf->mode); Loading
drivers/power/supply/qcom/fg-alg.h +17 −0 Original line number Diff line number Diff line Loading @@ -13,11 +13,18 @@ #ifndef __FG_ALG_H__ #define __FG_ALG_H__ #include "step-chg-jeita.h" #define BUCKET_COUNT 8 #define BUCKET_SOC_PCT (256 / BUCKET_COUNT) #define MAX_CC_STEPS 20 #define MAX_TTF_SAMPLES 10 #define is_between(left, right, value) \ (((left) >= (right) && (left) >= (value) \ && (value) >= (right)) \ || ((left) <= (right) && (left) <= (value) \ && (value) <= (right))) struct cycle_counter { void *data; char str_buf[BUCKET_COUNT * 8]; Loading Loading @@ -63,6 +70,7 @@ struct cap_learning { enum ttf_mode { TTF_MODE_NORMAL = 0, TTF_MODE_QNOVO, TTF_MODE_V_STEP_CHG, }; enum ttf_param { Loading Loading @@ -94,12 +102,21 @@ struct ttf_pt { s32 y; }; struct step_chg_data { int ocv; int soc; }; struct ttf { void *data; struct ttf_circ_buf ibatt; struct ttf_circ_buf vbatt; struct ttf_cc_step_data cc_step; struct mutex lock; struct step_chg_data *step_chg_data; struct range_data *step_chg_cfg; bool step_chg_cfg_valid; int step_chg_num_params; int mode; int last_ttf; int input_present; Loading
drivers/power/supply/qcom/qpnp-fg-gen4.c +71 −1 Original line number Diff line number Diff line Loading @@ -629,6 +629,8 @@ static int fg_gen4_get_ttf_param(void *data, enum ttf_param param, int *val) case TTF_MODE: if (is_qnovo_en(fg)) *val = TTF_MODE_QNOVO; else if (chip->ttf->step_chg_cfg_valid) *val = TTF_MODE_V_STEP_CHG; else *val = TTF_MODE_NORMAL; break; Loading Loading @@ -924,7 +926,7 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) struct device_node *node = fg->dev->of_node; struct device_node *batt_node, *profile_node; const char *data; int rc, len; int rc, len, i, tuple_len; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { Loading Loading @@ -999,6 +1001,64 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) } } /* * Currently step charging thresholds should be read only for Vbatt * based and not for SOC based. */ if (!of_property_read_bool(profile_node, "qcom,soc-based-step-chg") && of_find_property(profile_node, "qcom,step-chg-ranges", &len) && fg->bp.float_volt_uv > 0 && fg->bp.fastchg_curr_ma > 0) { len /= sizeof(u32); tuple_len = len / (sizeof(struct range_data) / sizeof(u32)); if (tuple_len <= 0 || tuple_len > MAX_STEP_CHG_ENTRIES) return -EINVAL; mutex_lock(&chip->ttf->lock); chip->ttf->step_chg_cfg = kcalloc(len, sizeof(*chip->ttf->step_chg_cfg), GFP_KERNEL); if (!chip->ttf->step_chg_cfg) { mutex_unlock(&chip->ttf->lock); return -ENOMEM; } chip->ttf->step_chg_data = kcalloc(tuple_len, sizeof(*chip->ttf->step_chg_data), GFP_KERNEL); if (!chip->ttf->step_chg_data) { kfree(chip->ttf->step_chg_cfg); mutex_unlock(&chip->ttf->lock); return -ENOMEM; } rc = read_range_data_from_node(profile_node, "qcom,step-chg-ranges", chip->ttf->step_chg_cfg, fg->bp.float_volt_uv, fg->bp.fastchg_curr_ma * 1000); if (rc < 0) { pr_err("Error in reading qcom,step-chg-ranges from battery profile, rc=%d\n", rc); kfree(chip->ttf->step_chg_data); kfree(chip->ttf->step_chg_cfg); chip->ttf->step_chg_cfg = NULL; mutex_unlock(&chip->ttf->lock); return rc; } chip->ttf->step_chg_num_params = tuple_len; chip->ttf->step_chg_cfg_valid = true; mutex_unlock(&chip->ttf->lock); if (chip->ttf->step_chg_cfg_valid) { for (i = 0; i < tuple_len; i++) pr_debug("Vbatt_low: %d Vbatt_high: %d FCC: %d\n", chip->ttf->step_chg_cfg[i].low_threshold, chip->ttf->step_chg_cfg[i].high_threshold, chip->ttf->step_chg_cfg[i].value); } } if (of_find_property(profile_node, "qcom,therm-pull-up", NULL)) { rc = of_property_read_u32(profile_node, "qcom,therm-pull-up", &fg->bp.therm_pull_up_kohms); Loading Loading @@ -1765,6 +1825,7 @@ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) { struct fg_dev *fg = data; struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); u8 status; int rc; Loading @@ -1783,6 +1844,15 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) fg->profile_load_status = PROFILE_NOT_LOADED; fg->soc_reporting_ready = false; fg->batt_id_ohms = -EINVAL; mutex_lock(&chip->ttf->lock); chip->ttf->step_chg_cfg_valid = false; chip->ttf->step_chg_num_params = 0; kfree(chip->ttf->step_chg_cfg); chip->ttf->step_chg_cfg = NULL; kfree(chip->ttf->step_chg_data); chip->ttf->step_chg_data = NULL; mutex_unlock(&chip->ttf->lock); return IRQ_HANDLED; } Loading