Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt +49 −0 Original line number Diff line number Diff line Loading @@ -158,6 +158,55 @@ First Level Node - FG Gen4 device loaded earlier by bootloader doesn't match with the profile available in the device tree. - qcom,cl-start-capacity Usage: optional Value type: <u32> Definition: Battery SOC threshold to start the capacity learning. If this is not specified, then the default value used will be 15. Unit is in percentage. - qcom,cl-min-temp Usage: optional Value type: <u32> Definition: Lower limit of battery temperature to start the capacity learning. If this is not specified, then the default value used will be 150 (15 C). Unit is in decidegC. - qcom,cl-max-temp Usage: optional Value type: <u32> Definition: Upper limit of battery temperature to start the capacity learning. If this is not specified, then the default value used will be 500 (50 C). Unit is in decidegC. - qcom,cl-max-increment Usage: optional Value type: <u32> Definition: Maximum capacity increment allowed per capacity learning cycle. If this is not specified, then the default value used will be 5 (0.5%). Unit is in decipercentage. - qcom,cl-max-decrement Usage: optional Value type: <u32> Definition: Maximum capacity decrement allowed per capacity learning cycle. If this is not specified, then the default value used will be 100 (10%). Unit is in decipercentage. - qcom,cl-min-limit Usage: optional Value type: <u32> Definition: Minimum limit that the capacity cannot go below in a capacity learning cycle. If this is not specified, then the default value is 0. Unit is in decipercentage. - qcom,cl-max-limit Usage: optional Value type: <u32> Definition: Maximum limit that the capacity cannot go above in a capacity learning cycle. If this is not specified, then the default value is 0. Unit is in decipercentage. - qcom,hold-soc-while-full Usage: optional Value type: <empty> Loading drivers/power/supply/qcom/Makefile +1 −1 Original line number Diff line number Diff line obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o pmic-voter.o obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o fg-alg.o pmic-voter.o obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o Loading drivers/power/supply/qcom/qpnp-fg-gen4.c +328 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/qpnp/qpnp-revid.h> #include "fg-core.h" #include "fg-reg.h" #include "fg-alg.h" #define FG_GEN4_DEV_NAME "qcom,fg-gen4" Loading Loading @@ -142,9 +143,12 @@ struct fg_dt_props { struct fg_gen4_chip { struct fg_dev fg; struct fg_dt_props dt; struct cycle_counter *counter; struct cap_learning *cl; struct ttf ttf; struct delayed_work ttf_work; char batt_profile[PROFILE_LEN]; char counter_buf[BUCKET_COUNT * 8]; bool ki_coeff_dischg_en; bool slope_limit_en; }; Loading Loading @@ -321,7 +325,7 @@ static int fg_gen4_get_learned_capacity(void *data, int64_t *learned_cap_uah) int rc, act_cap_mah; if (!chip) return -ENODATA; return -ENODEV; fg = &chip->fg; rc = fg_get_sram_prop(fg, FG_SRAM_ACT_BATT_CAP, &act_cap_mah); Loading Loading @@ -542,6 +546,128 @@ static inline void get_esr_meas_current(int curr_ma, u8 *val) *val <<= ESR_PULL_DOWN_IVAL_SHIFT; } /* ALG callback functions below */ static int fg_gen4_store_learned_capacity(void *data, int64_t learned_cap_uah) { struct fg_gen4_chip *chip = data; struct fg_dev *fg; int16_t cc_mah; int rc; if (!chip) return -ENODEV; fg = &chip->fg; if (fg->battery_missing || !learned_cap_uah) return -EPERM; cc_mah = div64_s64(learned_cap_uah, 1000); rc = fg_sram_write(fg, fg->sp[FG_SRAM_ACT_BATT_CAP].addr_word, fg->sp[FG_SRAM_ACT_BATT_CAP].addr_byte, (u8 *)&cc_mah, fg->sp[FG_SRAM_ACT_BATT_CAP].len, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in writing act_batt_cap_bkup, rc=%d\n", rc); return rc; } fg_dbg(fg, FG_CAP_LEARN, "learned capacity %llduah/%dmah stored\n", chip->cl->learned_cap_uah, cc_mah); return 0; } static int fg_gen4_prime_cc_soc_sw(void *data, u32 batt_soc) { struct fg_gen4_chip *chip = data; struct fg_dev *fg; int rc, cc_soc_sw; if (!chip) return -ENODEV; fg = &chip->fg; if (batt_soc == CC_SOC_30BIT) cc_soc_sw = batt_soc; else cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT, BATT_SOC_32BIT); rc = fg_sram_write(fg, fg->sp[FG_SRAM_CC_SOC_SW].addr_word, fg->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, fg->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); else fg_dbg(fg, FG_STATUS, "cc_soc_sw: %x\n", cc_soc_sw); return rc; } static int fg_gen4_get_cc_soc_sw(void *data, int *cc_soc_sw) { struct fg_gen4_chip *chip = data; struct fg_dev *fg; int rc, temp; if (!chip) return -ENODEV; fg = &chip->fg; rc = fg_get_sram_prop(fg, FG_SRAM_CC_SOC_SW, &temp); if (rc < 0) { pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); return rc; } *cc_soc_sw = temp; return rc; } static int fg_gen4_restore_count(void *data, u16 *buf, int length) { struct fg_gen4_chip *chip = data; int id, rc = 0; u8 tmp[2]; if (!chip) return -ENODEV; if (!buf || length > BUCKET_COUNT) return -EINVAL; for (id = 0; id < length; id++) { rc = fg_sram_read(&chip->fg, CYCLE_COUNT_WORD + id, CYCLE_COUNT_OFFSET, (u8 *)tmp, 2, FG_IMA_DEFAULT); if (rc < 0) pr_err("failed to read bucket %d rc=%d\n", id, rc); else *buf++ = tmp[0] | tmp[1] << 8; } return rc; } static int fg_gen4_store_count(void *data, u16 *buf, int id, int length) { struct fg_gen4_chip *chip = data; int rc; if (!chip) return -ENODEV; if (!buf || length > BUCKET_COUNT * 2 || id < 0 || id > BUCKET_COUNT - 1 || ((id * 2) + length) > BUCKET_COUNT * 2) return -EINVAL; rc = fg_sram_write(&chip->fg, CYCLE_COUNT_WORD + id, CYCLE_COUNT_OFFSET, (u8 *)buf, length, FG_IMA_DEFAULT); if (rc < 0) pr_err("failed to write bucket %d rc=%d\n", rc); return rc; } /* All worker and helper functions below */ #define KI_COEFF_MED_DISCHG_DEFAULT 245 Loading Loading @@ -828,6 +954,7 @@ static void profile_load_work(struct work_struct *work) profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int64_t nom_cap_uah; u8 val; int rc; Loading @@ -853,6 +980,8 @@ static void profile_load_work(struct work_struct *work) 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) { Loading Loading @@ -892,6 +1021,14 @@ static void profile_load_work(struct work_struct *work) pr_err("Error in configuring battery profile params, rc:%d\n", rc); rc = fg_gen4_get_nominal_capacity(chip, &nom_cap_uah); if (!rc) { rc = cap_learning_post_profile_init(chip->cl, nom_cap_uah); if (rc < 0) pr_err("Error in cap_learning_post_profile_init rc=%d\n", rc); } batt_psy_initialized(fg); fg_notify_charger(fg); Loading Loading @@ -1233,7 +1370,9 @@ static irqreturn_t fg_delta_bsoc_irq_handler(int irq, void *data) static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) { struct fg_dev *fg = data; struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, batt_soc, batt_temp; bool input_present = is_input_present(fg); fg_dbg(fg, FG_IRQ, "irq %d triggered\n", irq); Loading @@ -1242,10 +1381,17 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc); if (rc < 0) pr_err("Failed to read battery soc rc: %d\n", rc); else cycle_count_update(chip->counter, (u32)batt_soc >> 24, fg->charge_status, fg->charge_done, input_present); rc = fg_gen4_get_battery_temp(fg, &batt_temp); if (rc < 0) pr_err("Failed to read battery temp rc: %d\n", rc); else if (chip->cl->active) cap_learning_update(chip->cl, batt_temp, batt_soc, fg->charge_status, fg->charge_done, input_present, is_qnovo_en(fg)); rc = fg_gen4_charge_full_update(fg); if (rc < 0) Loading Loading @@ -1485,7 +1631,9 @@ static void status_change_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, status_change_work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, batt_soc, batt_temp; bool input_present, qnovo_en; if (!batt_psy_initialized(fg)) { fg_dbg(fg, FG_STATUS, "Charger not available?!\n"); Loading @@ -1511,6 +1659,16 @@ static void status_change_work(struct work_struct *work) goto out; } input_present = is_input_present(fg); qnovo_en = is_qnovo_en(fg); cycle_count_update(chip->counter, (u32)batt_soc >> 24, fg->charge_status, fg->charge_done, input_present); if (fg->charge_status != fg->prev_charge_status) cap_learning_update(chip->cl, batt_temp, batt_soc, fg->charge_status, fg->charge_done, input_present, qnovo_en); rc = fg_gen4_charge_full_update(fg); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); Loading Loading @@ -1879,6 +2037,32 @@ static int fg_get_time_to_empty(struct fg_dev *fg, int *val) return 0; } static const char *fg_gen4_get_cycle_counts(struct fg_gen4_chip *chip) { int i, rc, len = 0; char *buf; buf = chip->counter_buf; for (i = 1; i <= BUCKET_COUNT; i++) { chip->counter->id = i; rc = get_cycle_count(chip->counter); if (rc < 0) { pr_err("Couldn't get cycle count rc=%d\n", rc); return NULL; } if (sizeof(chip->counter_buf) - len < 8) { pr_err("Invalid length %d\n", len); return NULL; } len += snprintf(buf+len, 8, "%d ", rc); } buf[len] = '\0'; return buf; } static void sram_dump_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, Loading Loading @@ -2056,6 +2240,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: rc = fg_gen4_get_charge_counter_shadow(chip, &pval->intval); break; case POWER_SUPPLY_PROP_CYCLE_COUNTS: pval->strval = fg_gen4_get_cycle_counts(chip); break; case POWER_SUPPLY_PROP_SOC_REPORTING_READY: pval->intval = fg->soc_reporting_ready; break; Loading Loading @@ -2087,9 +2274,25 @@ static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) { struct fg_gen4_chip *chip = power_supply_get_drvdata(psy); int rc = 0; switch (psp) { case POWER_SUPPLY_PROP_CHARGE_FULL: if (chip->cl->active) { pr_warn("Capacity learning active!\n"); return 0; } if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) { pr_err("charge_full is out of bounds\n"); return -EINVAL; } mutex_lock(&chip->cl->lock); rc = fg_gen4_store_learned_capacity(chip, pval->intval); if (!rc) chip->cl->learned_cap_uah = pval->intval; mutex_unlock(&chip->cl->lock); break; default: break; } Loading @@ -2101,6 +2304,8 @@ static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_CHARGE_FULL: return 1; default: break; } Loading @@ -2124,6 +2329,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, POWER_SUPPLY_PROP_CYCLE_COUNTS, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_DEBUG_BATTERY, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, Loading Loading @@ -2204,6 +2410,59 @@ static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data, /* All init functions below this */ static int fg_alg_init(struct fg_gen4_chip *chip) { struct fg_dev *fg = &chip->fg; struct cycle_counter *counter; struct cap_learning *cl; int rc; counter = devm_kzalloc(fg->dev, sizeof(*counter), GFP_KERNEL); if (!counter) return -ENOMEM; counter->restore_count = fg_gen4_restore_count; counter->store_count = fg_gen4_store_count; counter->data = chip; rc = cycle_count_init(counter); if (rc < 0) { dev_err(fg->dev, "Error in initializing cycle counter, rc:%d\n", rc); counter->data = NULL; devm_kfree(fg->dev, counter); return rc; } chip->counter = counter; cl = devm_kzalloc(fg->dev, sizeof(*cl), GFP_KERNEL); if (!cl) return -ENOMEM; cl->cc_soc_max = CC_SOC_30BIT; cl->get_cc_soc = fg_gen4_get_cc_soc_sw; cl->prime_cc_soc = fg_gen4_prime_cc_soc_sw; cl->get_learned_capacity = fg_gen4_get_learned_capacity; cl->store_learned_capacity = fg_gen4_store_learned_capacity; cl->data = chip; rc = cap_learning_init(cl); if (rc < 0) { dev_err(fg->dev, "Error in initializing capacity learning, rc:%d\n", rc); counter->data = NULL; cl->data = NULL; devm_kfree(fg->dev, counter); devm_kfree(fg->dev, cl); return rc; } chip->cl = cl; return 0; } #define BATT_TEMP_HYST_MASK GENMASK(3, 0) #define BATT_TEMP_DELTA_MASK GENMASK(7, 4) #define BATT_TEMP_DELTA_SHIFT 4 Loading Loading @@ -2419,6 +2678,12 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip) } } rc = restore_cycle_count(chip->counter); if (rc < 0) { pr_err("Error in restoring cycle_count, rc=%d\n", rc); return rc; } return 0; } Loading Loading @@ -2695,6 +2960,48 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.force_load_profile = of_property_read_bool(node, "qcom,fg-force-load-profile"); rc = of_property_read_u32(node, "qcom,cl-start-capacity", &temp); if (rc < 0) chip->cl->dt.start_soc = DEFAULT_CL_START_SOC; else chip->cl->dt.start_soc = temp; rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp); if (rc < 0) chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC; else chip->cl->dt.min_temp = temp; rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp); if (rc < 0) chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC; else chip->cl->dt.max_temp = temp; rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp); if (rc < 0) chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC; else chip->cl->dt.max_cap_inc = temp; rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp); if (rc < 0) chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC; else chip->cl->dt.max_cap_dec = temp; rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp); if (rc < 0) chip->cl->dt.min_cap_limit = DEFAULT_CL_MIN_LIM_DECIPERC; else chip->cl->dt.min_cap_limit = temp; rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp); if (rc < 0) chip->cl->dt.max_cap_limit = DEFAULT_CL_MAX_LIM_DECIPERC; else chip->cl->dt.max_cap_limit = temp; rc = of_property_read_u32(node, "qcom,fg-batt-temp-hot", &temp); if (rc < 0) chip->dt.batt_temp_hot_thresh = -EINVAL; Loading Loading @@ -2819,6 +3126,13 @@ static int fg_gen4_probe(struct platform_device *pdev) goto exit; } rc = fg_alg_init(chip); if (rc < 0) { dev_err(fg->dev, "Error in alg_init, rc:%d\n", rc); goto exit; } rc = fg_gen4_parse_dt(chip); if (rc < 0) { dev_err(fg->dev, "Error in reading DT parameters, rc:%d\n", Loading Loading @@ -2930,13 +3244,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev) struct fg_dev *fg = &chip->fg; int rc, bsoc; if (fg->charge_full) { rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &bsoc); if (rc < 0) { pr_err("Error in getting BATT_SOC, rc=%d\n", rc); return; } if (fg->charge_full) { /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; Loading @@ -2946,6 +3260,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev) return; } } /* * Charging status doesn't matter when the device shuts down and we * have to treat this as charge done. Hence pass charge_done as true. */ cycle_count_update(chip->counter, (u32)bsoc >> 24, POWER_SUPPLY_STATUS_NOT_CHARGING, true, is_input_present(fg)); } static int fg_gen4_suspend(struct device *dev) Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt +49 −0 Original line number Diff line number Diff line Loading @@ -158,6 +158,55 @@ First Level Node - FG Gen4 device loaded earlier by bootloader doesn't match with the profile available in the device tree. - qcom,cl-start-capacity Usage: optional Value type: <u32> Definition: Battery SOC threshold to start the capacity learning. If this is not specified, then the default value used will be 15. Unit is in percentage. - qcom,cl-min-temp Usage: optional Value type: <u32> Definition: Lower limit of battery temperature to start the capacity learning. If this is not specified, then the default value used will be 150 (15 C). Unit is in decidegC. - qcom,cl-max-temp Usage: optional Value type: <u32> Definition: Upper limit of battery temperature to start the capacity learning. If this is not specified, then the default value used will be 500 (50 C). Unit is in decidegC. - qcom,cl-max-increment Usage: optional Value type: <u32> Definition: Maximum capacity increment allowed per capacity learning cycle. If this is not specified, then the default value used will be 5 (0.5%). Unit is in decipercentage. - qcom,cl-max-decrement Usage: optional Value type: <u32> Definition: Maximum capacity decrement allowed per capacity learning cycle. If this is not specified, then the default value used will be 100 (10%). Unit is in decipercentage. - qcom,cl-min-limit Usage: optional Value type: <u32> Definition: Minimum limit that the capacity cannot go below in a capacity learning cycle. If this is not specified, then the default value is 0. Unit is in decipercentage. - qcom,cl-max-limit Usage: optional Value type: <u32> Definition: Maximum limit that the capacity cannot go above in a capacity learning cycle. If this is not specified, then the default value is 0. Unit is in decipercentage. - qcom,hold-soc-while-full Usage: optional Value type: <empty> Loading
drivers/power/supply/qcom/Makefile +1 −1 Original line number Diff line number Diff line obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o pmic-voter.o obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o fg-alg.o pmic-voter.o obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o Loading
drivers/power/supply/qcom/qpnp-fg-gen4.c +328 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/qpnp/qpnp-revid.h> #include "fg-core.h" #include "fg-reg.h" #include "fg-alg.h" #define FG_GEN4_DEV_NAME "qcom,fg-gen4" Loading Loading @@ -142,9 +143,12 @@ struct fg_dt_props { struct fg_gen4_chip { struct fg_dev fg; struct fg_dt_props dt; struct cycle_counter *counter; struct cap_learning *cl; struct ttf ttf; struct delayed_work ttf_work; char batt_profile[PROFILE_LEN]; char counter_buf[BUCKET_COUNT * 8]; bool ki_coeff_dischg_en; bool slope_limit_en; }; Loading Loading @@ -321,7 +325,7 @@ static int fg_gen4_get_learned_capacity(void *data, int64_t *learned_cap_uah) int rc, act_cap_mah; if (!chip) return -ENODATA; return -ENODEV; fg = &chip->fg; rc = fg_get_sram_prop(fg, FG_SRAM_ACT_BATT_CAP, &act_cap_mah); Loading Loading @@ -542,6 +546,128 @@ static inline void get_esr_meas_current(int curr_ma, u8 *val) *val <<= ESR_PULL_DOWN_IVAL_SHIFT; } /* ALG callback functions below */ static int fg_gen4_store_learned_capacity(void *data, int64_t learned_cap_uah) { struct fg_gen4_chip *chip = data; struct fg_dev *fg; int16_t cc_mah; int rc; if (!chip) return -ENODEV; fg = &chip->fg; if (fg->battery_missing || !learned_cap_uah) return -EPERM; cc_mah = div64_s64(learned_cap_uah, 1000); rc = fg_sram_write(fg, fg->sp[FG_SRAM_ACT_BATT_CAP].addr_word, fg->sp[FG_SRAM_ACT_BATT_CAP].addr_byte, (u8 *)&cc_mah, fg->sp[FG_SRAM_ACT_BATT_CAP].len, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in writing act_batt_cap_bkup, rc=%d\n", rc); return rc; } fg_dbg(fg, FG_CAP_LEARN, "learned capacity %llduah/%dmah stored\n", chip->cl->learned_cap_uah, cc_mah); return 0; } static int fg_gen4_prime_cc_soc_sw(void *data, u32 batt_soc) { struct fg_gen4_chip *chip = data; struct fg_dev *fg; int rc, cc_soc_sw; if (!chip) return -ENODEV; fg = &chip->fg; if (batt_soc == CC_SOC_30BIT) cc_soc_sw = batt_soc; else cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT, BATT_SOC_32BIT); rc = fg_sram_write(fg, fg->sp[FG_SRAM_CC_SOC_SW].addr_word, fg->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, fg->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); else fg_dbg(fg, FG_STATUS, "cc_soc_sw: %x\n", cc_soc_sw); return rc; } static int fg_gen4_get_cc_soc_sw(void *data, int *cc_soc_sw) { struct fg_gen4_chip *chip = data; struct fg_dev *fg; int rc, temp; if (!chip) return -ENODEV; fg = &chip->fg; rc = fg_get_sram_prop(fg, FG_SRAM_CC_SOC_SW, &temp); if (rc < 0) { pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); return rc; } *cc_soc_sw = temp; return rc; } static int fg_gen4_restore_count(void *data, u16 *buf, int length) { struct fg_gen4_chip *chip = data; int id, rc = 0; u8 tmp[2]; if (!chip) return -ENODEV; if (!buf || length > BUCKET_COUNT) return -EINVAL; for (id = 0; id < length; id++) { rc = fg_sram_read(&chip->fg, CYCLE_COUNT_WORD + id, CYCLE_COUNT_OFFSET, (u8 *)tmp, 2, FG_IMA_DEFAULT); if (rc < 0) pr_err("failed to read bucket %d rc=%d\n", id, rc); else *buf++ = tmp[0] | tmp[1] << 8; } return rc; } static int fg_gen4_store_count(void *data, u16 *buf, int id, int length) { struct fg_gen4_chip *chip = data; int rc; if (!chip) return -ENODEV; if (!buf || length > BUCKET_COUNT * 2 || id < 0 || id > BUCKET_COUNT - 1 || ((id * 2) + length) > BUCKET_COUNT * 2) return -EINVAL; rc = fg_sram_write(&chip->fg, CYCLE_COUNT_WORD + id, CYCLE_COUNT_OFFSET, (u8 *)buf, length, FG_IMA_DEFAULT); if (rc < 0) pr_err("failed to write bucket %d rc=%d\n", rc); return rc; } /* All worker and helper functions below */ #define KI_COEFF_MED_DISCHG_DEFAULT 245 Loading Loading @@ -828,6 +954,7 @@ static void profile_load_work(struct work_struct *work) profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int64_t nom_cap_uah; u8 val; int rc; Loading @@ -853,6 +980,8 @@ static void profile_load_work(struct work_struct *work) 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) { Loading Loading @@ -892,6 +1021,14 @@ static void profile_load_work(struct work_struct *work) pr_err("Error in configuring battery profile params, rc:%d\n", rc); rc = fg_gen4_get_nominal_capacity(chip, &nom_cap_uah); if (!rc) { rc = cap_learning_post_profile_init(chip->cl, nom_cap_uah); if (rc < 0) pr_err("Error in cap_learning_post_profile_init rc=%d\n", rc); } batt_psy_initialized(fg); fg_notify_charger(fg); Loading Loading @@ -1233,7 +1370,9 @@ static irqreturn_t fg_delta_bsoc_irq_handler(int irq, void *data) static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) { struct fg_dev *fg = data; struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, batt_soc, batt_temp; bool input_present = is_input_present(fg); fg_dbg(fg, FG_IRQ, "irq %d triggered\n", irq); Loading @@ -1242,10 +1381,17 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc); if (rc < 0) pr_err("Failed to read battery soc rc: %d\n", rc); else cycle_count_update(chip->counter, (u32)batt_soc >> 24, fg->charge_status, fg->charge_done, input_present); rc = fg_gen4_get_battery_temp(fg, &batt_temp); if (rc < 0) pr_err("Failed to read battery temp rc: %d\n", rc); else if (chip->cl->active) cap_learning_update(chip->cl, batt_temp, batt_soc, fg->charge_status, fg->charge_done, input_present, is_qnovo_en(fg)); rc = fg_gen4_charge_full_update(fg); if (rc < 0) Loading Loading @@ -1485,7 +1631,9 @@ static void status_change_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, status_change_work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, batt_soc, batt_temp; bool input_present, qnovo_en; if (!batt_psy_initialized(fg)) { fg_dbg(fg, FG_STATUS, "Charger not available?!\n"); Loading @@ -1511,6 +1659,16 @@ static void status_change_work(struct work_struct *work) goto out; } input_present = is_input_present(fg); qnovo_en = is_qnovo_en(fg); cycle_count_update(chip->counter, (u32)batt_soc >> 24, fg->charge_status, fg->charge_done, input_present); if (fg->charge_status != fg->prev_charge_status) cap_learning_update(chip->cl, batt_temp, batt_soc, fg->charge_status, fg->charge_done, input_present, qnovo_en); rc = fg_gen4_charge_full_update(fg); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); Loading Loading @@ -1879,6 +2037,32 @@ static int fg_get_time_to_empty(struct fg_dev *fg, int *val) return 0; } static const char *fg_gen4_get_cycle_counts(struct fg_gen4_chip *chip) { int i, rc, len = 0; char *buf; buf = chip->counter_buf; for (i = 1; i <= BUCKET_COUNT; i++) { chip->counter->id = i; rc = get_cycle_count(chip->counter); if (rc < 0) { pr_err("Couldn't get cycle count rc=%d\n", rc); return NULL; } if (sizeof(chip->counter_buf) - len < 8) { pr_err("Invalid length %d\n", len); return NULL; } len += snprintf(buf+len, 8, "%d ", rc); } buf[len] = '\0'; return buf; } static void sram_dump_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, Loading Loading @@ -2056,6 +2240,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: rc = fg_gen4_get_charge_counter_shadow(chip, &pval->intval); break; case POWER_SUPPLY_PROP_CYCLE_COUNTS: pval->strval = fg_gen4_get_cycle_counts(chip); break; case POWER_SUPPLY_PROP_SOC_REPORTING_READY: pval->intval = fg->soc_reporting_ready; break; Loading Loading @@ -2087,9 +2274,25 @@ static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) { struct fg_gen4_chip *chip = power_supply_get_drvdata(psy); int rc = 0; switch (psp) { case POWER_SUPPLY_PROP_CHARGE_FULL: if (chip->cl->active) { pr_warn("Capacity learning active!\n"); return 0; } if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) { pr_err("charge_full is out of bounds\n"); return -EINVAL; } mutex_lock(&chip->cl->lock); rc = fg_gen4_store_learned_capacity(chip, pval->intval); if (!rc) chip->cl->learned_cap_uah = pval->intval; mutex_unlock(&chip->cl->lock); break; default: break; } Loading @@ -2101,6 +2304,8 @@ static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_CHARGE_FULL: return 1; default: break; } Loading @@ -2124,6 +2329,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, POWER_SUPPLY_PROP_CYCLE_COUNTS, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_DEBUG_BATTERY, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, Loading Loading @@ -2204,6 +2410,59 @@ static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data, /* All init functions below this */ static int fg_alg_init(struct fg_gen4_chip *chip) { struct fg_dev *fg = &chip->fg; struct cycle_counter *counter; struct cap_learning *cl; int rc; counter = devm_kzalloc(fg->dev, sizeof(*counter), GFP_KERNEL); if (!counter) return -ENOMEM; counter->restore_count = fg_gen4_restore_count; counter->store_count = fg_gen4_store_count; counter->data = chip; rc = cycle_count_init(counter); if (rc < 0) { dev_err(fg->dev, "Error in initializing cycle counter, rc:%d\n", rc); counter->data = NULL; devm_kfree(fg->dev, counter); return rc; } chip->counter = counter; cl = devm_kzalloc(fg->dev, sizeof(*cl), GFP_KERNEL); if (!cl) return -ENOMEM; cl->cc_soc_max = CC_SOC_30BIT; cl->get_cc_soc = fg_gen4_get_cc_soc_sw; cl->prime_cc_soc = fg_gen4_prime_cc_soc_sw; cl->get_learned_capacity = fg_gen4_get_learned_capacity; cl->store_learned_capacity = fg_gen4_store_learned_capacity; cl->data = chip; rc = cap_learning_init(cl); if (rc < 0) { dev_err(fg->dev, "Error in initializing capacity learning, rc:%d\n", rc); counter->data = NULL; cl->data = NULL; devm_kfree(fg->dev, counter); devm_kfree(fg->dev, cl); return rc; } chip->cl = cl; return 0; } #define BATT_TEMP_HYST_MASK GENMASK(3, 0) #define BATT_TEMP_DELTA_MASK GENMASK(7, 4) #define BATT_TEMP_DELTA_SHIFT 4 Loading Loading @@ -2419,6 +2678,12 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip) } } rc = restore_cycle_count(chip->counter); if (rc < 0) { pr_err("Error in restoring cycle_count, rc=%d\n", rc); return rc; } return 0; } Loading Loading @@ -2695,6 +2960,48 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.force_load_profile = of_property_read_bool(node, "qcom,fg-force-load-profile"); rc = of_property_read_u32(node, "qcom,cl-start-capacity", &temp); if (rc < 0) chip->cl->dt.start_soc = DEFAULT_CL_START_SOC; else chip->cl->dt.start_soc = temp; rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp); if (rc < 0) chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC; else chip->cl->dt.min_temp = temp; rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp); if (rc < 0) chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC; else chip->cl->dt.max_temp = temp; rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp); if (rc < 0) chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC; else chip->cl->dt.max_cap_inc = temp; rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp); if (rc < 0) chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC; else chip->cl->dt.max_cap_dec = temp; rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp); if (rc < 0) chip->cl->dt.min_cap_limit = DEFAULT_CL_MIN_LIM_DECIPERC; else chip->cl->dt.min_cap_limit = temp; rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp); if (rc < 0) chip->cl->dt.max_cap_limit = DEFAULT_CL_MAX_LIM_DECIPERC; else chip->cl->dt.max_cap_limit = temp; rc = of_property_read_u32(node, "qcom,fg-batt-temp-hot", &temp); if (rc < 0) chip->dt.batt_temp_hot_thresh = -EINVAL; Loading Loading @@ -2819,6 +3126,13 @@ static int fg_gen4_probe(struct platform_device *pdev) goto exit; } rc = fg_alg_init(chip); if (rc < 0) { dev_err(fg->dev, "Error in alg_init, rc:%d\n", rc); goto exit; } rc = fg_gen4_parse_dt(chip); if (rc < 0) { dev_err(fg->dev, "Error in reading DT parameters, rc:%d\n", Loading Loading @@ -2930,13 +3244,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev) struct fg_dev *fg = &chip->fg; int rc, bsoc; if (fg->charge_full) { rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &bsoc); if (rc < 0) { pr_err("Error in getting BATT_SOC, rc=%d\n", rc); return; } if (fg->charge_full) { /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; Loading @@ -2946,6 +3260,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev) return; } } /* * Charging status doesn't matter when the device shuts down and we * have to treat this as charge done. Hence pass charge_done as true. */ cycle_count_update(chip->counter, (u32)bsoc >> 24, POWER_SUPPLY_STATUS_NOT_CHARGING, true, is_input_present(fg)); } static int fg_gen4_suspend(struct device *dev) Loading