Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt +8 −0 Original line number Original line Diff line number Diff line Loading @@ -397,6 +397,14 @@ First Level Node - FG Gen4 device five pin battery is used. Based on this, time to full five pin battery is used. Based on this, time to full calculations would use the Rbatt calculated properly. calculations would use the Rbatt calculated properly. - qcom,multi-profile-load Usage: optional Value type: <empty> Definition: A boolean property that when specified indicates that multiple profile loading needs to be enabled. This requires multiple battery profiles to be specified for a battery for proper functionality. ========================================================== ========================================================== Second Level Nodes - Peripherals managed by FG Gen4 driver Second Level Nodes - Peripherals managed by FG Gen4 driver ========================================================== ========================================================== Loading drivers/power/supply/qcom/qpnp-fg-gen4.c +150 −60 Original line number Original line Diff line number Diff line Loading @@ -116,6 +116,8 @@ #define RCONN_OFFSET 0 #define RCONN_OFFSET 0 #define ACT_BATT_CAP_WORD 285 #define ACT_BATT_CAP_WORD 285 #define ACT_BATT_CAP_OFFSET 0 #define ACT_BATT_CAP_OFFSET 0 #define BATT_AGE_LEVEL_WORD 288 #define BATT_AGE_LEVEL_OFFSET 0 #define CYCLE_COUNT_WORD 291 #define CYCLE_COUNT_WORD 291 #define CYCLE_COUNT_OFFSET 0 #define CYCLE_COUNT_OFFSET 0 #define PROFILE_INTEGRITY_WORD 299 #define PROFILE_INTEGRITY_WORD 299 Loading Loading @@ -190,6 +192,7 @@ struct fg_dt_props { bool linearize_soc; bool linearize_soc; bool rapid_soc_dec_en; bool rapid_soc_dec_en; bool five_pin_battery; bool five_pin_battery; bool multi_profile_load; int cutoff_volt_mv; int cutoff_volt_mv; int empty_volt_mv; int empty_volt_mv; int cutoff_curr_ma; int cutoff_curr_ma; Loading Loading @@ -249,6 +252,8 @@ struct fg_gen4_chip { int esr_nominal; int esr_nominal; int soh; int soh; int esr_soh_cycle_count; int esr_soh_cycle_count; int batt_age_level; int last_batt_age_level; bool first_profile_load; bool first_profile_load; bool ki_coeff_dischg_en; bool ki_coeff_dischg_en; bool slope_limit_en; bool slope_limit_en; Loading Loading @@ -1323,7 +1328,7 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) struct device_node *node = fg->dev->of_node; struct device_node *node = fg->dev->of_node; struct device_node *batt_node, *profile_node; struct device_node *batt_node, *profile_node; const char *data; const char *data; int rc, len, i, tuple_len; int rc, len, i, tuple_len, avail_age_level = 0; batt_node = of_find_node_by_name(node, "qcom,battery-data"); batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { if (!batt_node) { Loading @@ -1331,6 +1336,11 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) return -ENXIO; return -ENXIO; } } if (chip->dt.multi_profile_load) profile_node = of_batterydata_get_best_aged_profile(batt_node, fg->batt_id_ohms / 1000, chip->batt_age_level, &avail_age_level); else profile_node = of_batterydata_get_best_profile(batt_node, profile_node = of_batterydata_get_best_profile(batt_node, fg->batt_id_ohms / 1000, NULL); fg->batt_id_ohms / 1000, NULL); if (IS_ERR(profile_node)) if (IS_ERR(profile_node)) Loading @@ -1341,6 +1351,13 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) return -ENODATA; return -ENODATA; } } if (chip->dt.multi_profile_load && chip->batt_age_level != avail_age_level) { fg_dbg(fg, FG_STATUS, "Batt_age_level %d doesn't exist, using %d\n", chip->batt_age_level, avail_age_level); chip->batt_age_level = avail_age_level; } rc = of_property_read_string(profile_node, "qcom,battery-type", rc = of_property_read_string(profile_node, "qcom,battery-type", &fg->bp.batt_type_str); &fg->bp.batt_type_str); if (rc < 0) { if (rc < 0) { Loading Loading @@ -1687,6 +1704,10 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip) FIRST_PROFILE_LOAD_BIT, FIRST_PROFILE_LOAD_BIT, }; }; if (chip->dt.multi_profile_load && (chip->batt_age_level != chip->last_batt_age_level)) return true; rc = fg_sram_read(fg, PROFILE_INTEGRITY_WORD, rc = fg_sram_read(fg, PROFILE_INTEGRITY_WORD, PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { Loading Loading @@ -1756,47 +1777,29 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip) } } #define SOC_READY_WAIT_TIME_MS 1000 #define SOC_READY_WAIT_TIME_MS 1000 static void profile_load_work(struct work_struct *work) static int qpnp_fg_gen4_load_profile(struct fg_gen4_chip *chip) { { struct fg_dev *fg = container_of(work, struct fg_dev *fg = &chip->fg; struct fg_dev, profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int64_t nom_cap_uah; u8 val, mask, buf[2]; u8 val, mask, buf[2]; int rc; int rc; bool normal_profile_load = false; vote(fg->awake_votable, PROFILE_LOAD, true, 0); /* * This is used to determine if the profile is loaded normally i.e. rc = fg_gen4_get_batt_id(chip); * either multi profile loading is disabled OR if it is enabled, then if (rc < 0) { * only loading the profile with battery age level 0 is considered. pr_err("Error in getting battery id, rc:%d\n", rc); */ goto out; normal_profile_load = !chip->dt.multi_profile_load || } (chip->dt.multi_profile_load && chip->batt_age_level == 0); rc = fg_gen4_get_batt_profile(fg); if (normal_profile_load) { if (rc < 0) { rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT, fg->profile_load_status = PROFILE_MISSING; 0); pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", fg->batt_id_ohms / 1000, rc); goto out; } if (!fg->profile_available) goto out; if (!is_profile_load_required(chip)) goto done; clear_cycle_count(chip->counter); fg_dbg(fg, FG_STATUS, "profile loading started\n"); rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT, 0); if (rc < 0) { if (rc < 0) { pr_err("Error in writing to %04x, rc=%d\n", pr_err("Error in writing to %04x, rc=%d\n", BATT_SOC_RESTART(fg), rc); BATT_SOC_RESTART(fg), rc); goto out; return rc; } } } /* load battery profile */ /* load battery profile */ Loading @@ -1804,25 +1807,30 @@ static void profile_load_work(struct work_struct *work) chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); if (rc < 0) { if (rc < 0) { pr_err("Error in writing battery profile, rc:%d\n", rc); pr_err("Error in writing battery profile, rc:%d\n", rc); goto out; return rc; } } if (normal_profile_load) { /* Enable side loading for voltage and current */ /* Enable side loading for voltage and current */ val = mask = BIT(0); val = mask = BIT(0); rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n", rc); pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n", goto out; rc); return rc; } } /* Clear first logged main current ADC values */ /* Clear first logged main current ADC values */ buf[0] = buf[1] = 0; buf[0] = buf[1] = 0; rc = fg_sram_write(fg, FIRST_LOG_CURRENT_v2_WORD, rc = fg_sram_write(fg, FIRST_LOG_CURRENT_v2_WORD, FIRST_LOG_CURRENT_v2_OFFSET, buf, 2, FG_IMA_DEFAULT); FIRST_LOG_CURRENT_v2_OFFSET, buf, 2, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n", rc); pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n", goto out; rc); return rc; } } } /* Set the profile integrity bit */ /* Set the profile integrity bit */ Loading @@ -1831,13 +1839,24 @@ static void profile_load_work(struct work_struct *work) PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("failed to write profile integrity rc=%d\n", rc); pr_err("failed to write profile integrity rc=%d\n", rc); goto out; return rc; } if (chip->dt.multi_profile_load && chip->batt_age_level >= 0) { val = (u8)chip->batt_age_level; rc = fg_sram_write(fg, BATT_AGE_LEVEL_WORD, BATT_AGE_LEVEL_OFFSET, &val, 1, FG_IMA_ATOMIC); if (rc < 0) { pr_err("Error in writing batt_age_level, rc:%d\n", rc); return rc; } } } if (normal_profile_load) { rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS); rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS); if (rc < 0) { if (rc < 0) { pr_err("Error in restarting FG, rc=%d\n", rc); pr_err("Error in restarting FG, rc=%d\n", rc); goto out; return rc; } } /* Clear side loading for voltage and current */ /* Clear side loading for voltage and current */ Loading @@ -1846,10 +1865,57 @@ static void profile_load_work(struct work_struct *work) rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("Error in clearing SYS_CONFIG_WORD[0], rc=%d\n", rc); pr_err("Error in clearing SYS_CONFIG_WORD[0], rc=%d\n", rc); return rc; } } return 0; } static void profile_load_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int64_t nom_cap_uah; u8 val, buf[2]; int rc; vote(fg->awake_votable, PROFILE_LOAD, true, 0); rc = fg_gen4_get_batt_id(chip); if (rc < 0) { pr_err("Error in getting battery id, rc:%d\n", rc); goto out; } rc = fg_gen4_get_batt_profile(fg); if (rc < 0) { fg->profile_load_status = PROFILE_MISSING; pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", fg->batt_id_ohms / 1000, rc); goto out; goto out; } } if (!fg->profile_available) goto out; if (!is_profile_load_required(chip)) goto done; if (!chip->dt.multi_profile_load) clear_cycle_count(chip->counter); fg_dbg(fg, FG_STATUS, "profile loading started\n"); rc = qpnp_fg_gen4_load_profile(chip); if (rc < 0) goto out; fg_dbg(fg, FG_STATUS, "SOC is ready\n"); fg_dbg(fg, FG_STATUS, "SOC is ready\n"); fg->profile_load_status = PROFILE_LOADED; fg->profile_load_status = PROFILE_LOADED; Loading Loading @@ -1900,6 +1966,8 @@ static void profile_load_work(struct work_struct *work) schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000)); schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000)); fg_dbg(fg, FG_STATUS, "profile loaded successfully"); fg_dbg(fg, FG_STATUS, "profile loaded successfully"); out: out: if (chip->dt.multi_profile_load && rc < 0) chip->batt_age_level = chip->last_batt_age_level; fg->soc_reporting_ready = true; fg->soc_reporting_ready = true; vote(fg->awake_votable, ESR_FCC_VOTER, true, 0); vote(fg->awake_votable, ESR_FCC_VOTER, true, 0); schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000)); schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000)); Loading Loading @@ -3511,6 +3579,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CC_STEP_SEL: case POWER_SUPPLY_PROP_CC_STEP_SEL: pval->intval = chip->ttf->cc_step.sel; pval->intval = chip->ttf->cc_step.sel; break; break; case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: pval->intval = chip->batt_age_level; break; default: default: pr_err("unsupported property %d\n", psp); pr_err("unsupported property %d\n", psp); rc = -EINVAL; rc = -EINVAL; Loading Loading @@ -3592,6 +3663,14 @@ static int fg_psy_set_property(struct power_supply *psy, chip->first_profile_load = false; chip->first_profile_load = false; } } break; break; case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: if (!chip->dt.multi_profile_load || pval->intval < 0 || chip->batt_age_level == pval->intval) return -EINVAL; chip->last_batt_age_level = chip->batt_age_level; chip->batt_age_level = pval->intval; schedule_delayed_work(&fg->profile_load_work, 0); break; default: default: break; break; } } Loading @@ -3610,6 +3689,7 @@ static int fg_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_ESR_NOMINAL: case POWER_SUPPLY_PROP_ESR_NOMINAL: case POWER_SUPPLY_PROP_SOH: case POWER_SUPPLY_PROP_SOH: case POWER_SUPPLY_PROP_CLEAR_SOH: case POWER_SUPPLY_PROP_CLEAR_SOH: case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: return 1; return 1; default: default: break; break; Loading Loading @@ -3646,6 +3726,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP_SEL, POWER_SUPPLY_PROP_CC_STEP_SEL, POWER_SUPPLY_PROP_BATT_AGE_LEVEL, }; }; static const struct power_supply_desc fg_psy_desc = { static const struct power_supply_desc fg_psy_desc = { Loading Loading @@ -4220,6 +4301,13 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip) return rc; return rc; } } chip->batt_age_level = chip->last_batt_age_level = -EINVAL; if (chip->dt.multi_profile_load) { rc = fg_sram_read(fg, BATT_AGE_LEVEL_WORD, BATT_AGE_LEVEL_OFFSET, &val, 1, FG_IMA_DEFAULT); if (!rc) chip->batt_age_level = chip->last_batt_age_level = val; } return 0; return 0; } } Loading Loading @@ -4701,6 +4789,8 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.five_pin_battery = of_property_read_bool(node, chip->dt.five_pin_battery = of_property_read_bool(node, "qcom,five-pin-battery"); "qcom,five-pin-battery"); chip->dt.multi_profile_load = of_property_read_bool(node, "qcom,multi-profile-load"); return 0; return 0; } } Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt +8 −0 Original line number Original line Diff line number Diff line Loading @@ -397,6 +397,14 @@ First Level Node - FG Gen4 device five pin battery is used. Based on this, time to full five pin battery is used. Based on this, time to full calculations would use the Rbatt calculated properly. calculations would use the Rbatt calculated properly. - qcom,multi-profile-load Usage: optional Value type: <empty> Definition: A boolean property that when specified indicates that multiple profile loading needs to be enabled. This requires multiple battery profiles to be specified for a battery for proper functionality. ========================================================== ========================================================== Second Level Nodes - Peripherals managed by FG Gen4 driver Second Level Nodes - Peripherals managed by FG Gen4 driver ========================================================== ========================================================== Loading
drivers/power/supply/qcom/qpnp-fg-gen4.c +150 −60 Original line number Original line Diff line number Diff line Loading @@ -116,6 +116,8 @@ #define RCONN_OFFSET 0 #define RCONN_OFFSET 0 #define ACT_BATT_CAP_WORD 285 #define ACT_BATT_CAP_WORD 285 #define ACT_BATT_CAP_OFFSET 0 #define ACT_BATT_CAP_OFFSET 0 #define BATT_AGE_LEVEL_WORD 288 #define BATT_AGE_LEVEL_OFFSET 0 #define CYCLE_COUNT_WORD 291 #define CYCLE_COUNT_WORD 291 #define CYCLE_COUNT_OFFSET 0 #define CYCLE_COUNT_OFFSET 0 #define PROFILE_INTEGRITY_WORD 299 #define PROFILE_INTEGRITY_WORD 299 Loading Loading @@ -190,6 +192,7 @@ struct fg_dt_props { bool linearize_soc; bool linearize_soc; bool rapid_soc_dec_en; bool rapid_soc_dec_en; bool five_pin_battery; bool five_pin_battery; bool multi_profile_load; int cutoff_volt_mv; int cutoff_volt_mv; int empty_volt_mv; int empty_volt_mv; int cutoff_curr_ma; int cutoff_curr_ma; Loading Loading @@ -249,6 +252,8 @@ struct fg_gen4_chip { int esr_nominal; int esr_nominal; int soh; int soh; int esr_soh_cycle_count; int esr_soh_cycle_count; int batt_age_level; int last_batt_age_level; bool first_profile_load; bool first_profile_load; bool ki_coeff_dischg_en; bool ki_coeff_dischg_en; bool slope_limit_en; bool slope_limit_en; Loading Loading @@ -1323,7 +1328,7 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) struct device_node *node = fg->dev->of_node; struct device_node *node = fg->dev->of_node; struct device_node *batt_node, *profile_node; struct device_node *batt_node, *profile_node; const char *data; const char *data; int rc, len, i, tuple_len; int rc, len, i, tuple_len, avail_age_level = 0; batt_node = of_find_node_by_name(node, "qcom,battery-data"); batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { if (!batt_node) { Loading @@ -1331,6 +1336,11 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) return -ENXIO; return -ENXIO; } } if (chip->dt.multi_profile_load) profile_node = of_batterydata_get_best_aged_profile(batt_node, fg->batt_id_ohms / 1000, chip->batt_age_level, &avail_age_level); else profile_node = of_batterydata_get_best_profile(batt_node, profile_node = of_batterydata_get_best_profile(batt_node, fg->batt_id_ohms / 1000, NULL); fg->batt_id_ohms / 1000, NULL); if (IS_ERR(profile_node)) if (IS_ERR(profile_node)) Loading @@ -1341,6 +1351,13 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) return -ENODATA; return -ENODATA; } } if (chip->dt.multi_profile_load && chip->batt_age_level != avail_age_level) { fg_dbg(fg, FG_STATUS, "Batt_age_level %d doesn't exist, using %d\n", chip->batt_age_level, avail_age_level); chip->batt_age_level = avail_age_level; } rc = of_property_read_string(profile_node, "qcom,battery-type", rc = of_property_read_string(profile_node, "qcom,battery-type", &fg->bp.batt_type_str); &fg->bp.batt_type_str); if (rc < 0) { if (rc < 0) { Loading Loading @@ -1687,6 +1704,10 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip) FIRST_PROFILE_LOAD_BIT, FIRST_PROFILE_LOAD_BIT, }; }; if (chip->dt.multi_profile_load && (chip->batt_age_level != chip->last_batt_age_level)) return true; rc = fg_sram_read(fg, PROFILE_INTEGRITY_WORD, rc = fg_sram_read(fg, PROFILE_INTEGRITY_WORD, PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { Loading Loading @@ -1756,47 +1777,29 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip) } } #define SOC_READY_WAIT_TIME_MS 1000 #define SOC_READY_WAIT_TIME_MS 1000 static void profile_load_work(struct work_struct *work) static int qpnp_fg_gen4_load_profile(struct fg_gen4_chip *chip) { { struct fg_dev *fg = container_of(work, struct fg_dev *fg = &chip->fg; struct fg_dev, profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int64_t nom_cap_uah; u8 val, mask, buf[2]; u8 val, mask, buf[2]; int rc; int rc; bool normal_profile_load = false; vote(fg->awake_votable, PROFILE_LOAD, true, 0); /* * This is used to determine if the profile is loaded normally i.e. rc = fg_gen4_get_batt_id(chip); * either multi profile loading is disabled OR if it is enabled, then if (rc < 0) { * only loading the profile with battery age level 0 is considered. pr_err("Error in getting battery id, rc:%d\n", rc); */ goto out; normal_profile_load = !chip->dt.multi_profile_load || } (chip->dt.multi_profile_load && chip->batt_age_level == 0); rc = fg_gen4_get_batt_profile(fg); if (normal_profile_load) { if (rc < 0) { rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT, fg->profile_load_status = PROFILE_MISSING; 0); pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", fg->batt_id_ohms / 1000, rc); goto out; } if (!fg->profile_available) goto out; if (!is_profile_load_required(chip)) goto done; clear_cycle_count(chip->counter); fg_dbg(fg, FG_STATUS, "profile loading started\n"); rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT, 0); if (rc < 0) { if (rc < 0) { pr_err("Error in writing to %04x, rc=%d\n", pr_err("Error in writing to %04x, rc=%d\n", BATT_SOC_RESTART(fg), rc); BATT_SOC_RESTART(fg), rc); goto out; return rc; } } } /* load battery profile */ /* load battery profile */ Loading @@ -1804,25 +1807,30 @@ static void profile_load_work(struct work_struct *work) chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); if (rc < 0) { if (rc < 0) { pr_err("Error in writing battery profile, rc:%d\n", rc); pr_err("Error in writing battery profile, rc:%d\n", rc); goto out; return rc; } } if (normal_profile_load) { /* Enable side loading for voltage and current */ /* Enable side loading for voltage and current */ val = mask = BIT(0); val = mask = BIT(0); rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n", rc); pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n", goto out; rc); return rc; } } /* Clear first logged main current ADC values */ /* Clear first logged main current ADC values */ buf[0] = buf[1] = 0; buf[0] = buf[1] = 0; rc = fg_sram_write(fg, FIRST_LOG_CURRENT_v2_WORD, rc = fg_sram_write(fg, FIRST_LOG_CURRENT_v2_WORD, FIRST_LOG_CURRENT_v2_OFFSET, buf, 2, FG_IMA_DEFAULT); FIRST_LOG_CURRENT_v2_OFFSET, buf, 2, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n", rc); pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n", goto out; rc); return rc; } } } /* Set the profile integrity bit */ /* Set the profile integrity bit */ Loading @@ -1831,13 +1839,24 @@ static void profile_load_work(struct work_struct *work) PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("failed to write profile integrity rc=%d\n", rc); pr_err("failed to write profile integrity rc=%d\n", rc); goto out; return rc; } if (chip->dt.multi_profile_load && chip->batt_age_level >= 0) { val = (u8)chip->batt_age_level; rc = fg_sram_write(fg, BATT_AGE_LEVEL_WORD, BATT_AGE_LEVEL_OFFSET, &val, 1, FG_IMA_ATOMIC); if (rc < 0) { pr_err("Error in writing batt_age_level, rc:%d\n", rc); return rc; } } } if (normal_profile_load) { rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS); rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS); if (rc < 0) { if (rc < 0) { pr_err("Error in restarting FG, rc=%d\n", rc); pr_err("Error in restarting FG, rc=%d\n", rc); goto out; return rc; } } /* Clear side loading for voltage and current */ /* Clear side loading for voltage and current */ Loading @@ -1846,10 +1865,57 @@ static void profile_load_work(struct work_struct *work) rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD, SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT); if (rc < 0) { if (rc < 0) { pr_err("Error in clearing SYS_CONFIG_WORD[0], rc=%d\n", rc); pr_err("Error in clearing SYS_CONFIG_WORD[0], rc=%d\n", rc); return rc; } } return 0; } static void profile_load_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int64_t nom_cap_uah; u8 val, buf[2]; int rc; vote(fg->awake_votable, PROFILE_LOAD, true, 0); rc = fg_gen4_get_batt_id(chip); if (rc < 0) { pr_err("Error in getting battery id, rc:%d\n", rc); goto out; } rc = fg_gen4_get_batt_profile(fg); if (rc < 0) { fg->profile_load_status = PROFILE_MISSING; pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", fg->batt_id_ohms / 1000, rc); goto out; goto out; } } if (!fg->profile_available) goto out; if (!is_profile_load_required(chip)) goto done; if (!chip->dt.multi_profile_load) clear_cycle_count(chip->counter); fg_dbg(fg, FG_STATUS, "profile loading started\n"); rc = qpnp_fg_gen4_load_profile(chip); if (rc < 0) goto out; fg_dbg(fg, FG_STATUS, "SOC is ready\n"); fg_dbg(fg, FG_STATUS, "SOC is ready\n"); fg->profile_load_status = PROFILE_LOADED; fg->profile_load_status = PROFILE_LOADED; Loading Loading @@ -1900,6 +1966,8 @@ static void profile_load_work(struct work_struct *work) schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000)); schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000)); fg_dbg(fg, FG_STATUS, "profile loaded successfully"); fg_dbg(fg, FG_STATUS, "profile loaded successfully"); out: out: if (chip->dt.multi_profile_load && rc < 0) chip->batt_age_level = chip->last_batt_age_level; fg->soc_reporting_ready = true; fg->soc_reporting_ready = true; vote(fg->awake_votable, ESR_FCC_VOTER, true, 0); vote(fg->awake_votable, ESR_FCC_VOTER, true, 0); schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000)); schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000)); Loading Loading @@ -3511,6 +3579,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CC_STEP_SEL: case POWER_SUPPLY_PROP_CC_STEP_SEL: pval->intval = chip->ttf->cc_step.sel; pval->intval = chip->ttf->cc_step.sel; break; break; case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: pval->intval = chip->batt_age_level; break; default: default: pr_err("unsupported property %d\n", psp); pr_err("unsupported property %d\n", psp); rc = -EINVAL; rc = -EINVAL; Loading Loading @@ -3592,6 +3663,14 @@ static int fg_psy_set_property(struct power_supply *psy, chip->first_profile_load = false; chip->first_profile_load = false; } } break; break; case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: if (!chip->dt.multi_profile_load || pval->intval < 0 || chip->batt_age_level == pval->intval) return -EINVAL; chip->last_batt_age_level = chip->batt_age_level; chip->batt_age_level = pval->intval; schedule_delayed_work(&fg->profile_load_work, 0); break; default: default: break; break; } } Loading @@ -3610,6 +3689,7 @@ static int fg_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_ESR_NOMINAL: case POWER_SUPPLY_PROP_ESR_NOMINAL: case POWER_SUPPLY_PROP_SOH: case POWER_SUPPLY_PROP_SOH: case POWER_SUPPLY_PROP_CLEAR_SOH: case POWER_SUPPLY_PROP_CLEAR_SOH: case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: return 1; return 1; default: default: break; break; Loading Loading @@ -3646,6 +3726,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP_SEL, POWER_SUPPLY_PROP_CC_STEP_SEL, POWER_SUPPLY_PROP_BATT_AGE_LEVEL, }; }; static const struct power_supply_desc fg_psy_desc = { static const struct power_supply_desc fg_psy_desc = { Loading Loading @@ -4220,6 +4301,13 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip) return rc; return rc; } } chip->batt_age_level = chip->last_batt_age_level = -EINVAL; if (chip->dt.multi_profile_load) { rc = fg_sram_read(fg, BATT_AGE_LEVEL_WORD, BATT_AGE_LEVEL_OFFSET, &val, 1, FG_IMA_DEFAULT); if (!rc) chip->batt_age_level = chip->last_batt_age_level = val; } return 0; return 0; } } Loading Loading @@ -4701,6 +4789,8 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.five_pin_battery = of_property_read_bool(node, chip->dt.five_pin_battery = of_property_read_bool(node, "qcom,five-pin-battery"); "qcom,five-pin-battery"); chip->dt.multi_profile_load = of_property_read_bool(node, "qcom,multi-profile-load"); return 0; return 0; } } Loading