Loading Documentation/devicetree/bindings/power/qpnp-fg.txt +0 −6 Original line number Diff line number Diff line Loading @@ -109,12 +109,6 @@ Parent node optional properties: present, then the following properties to specify low and high soc thresholds should be defined. - qcom,cycle-counter-low-soc: Low SOC threshold which will be compared against the battery SOC before starting the cycle counter algorithm. - qcom,cycle-counter-high-soc: High SOC threshold which will be compared against the battery SOC before incrementing the cycle counter. - qcom,capacity-learning-on: A boolean property to have the fuel gauge driver attempt to learn the battery capacity when charging. Takes Loading arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +0 −2 Original line number Diff line number Diff line Loading @@ -314,8 +314,6 @@ qcom,fg-iterm-ma = <100>; qcom,fg-chg-iterm-ma = <100>; qcom,cycle-counter-en; qcom,cycle-counter-low-soc = <15>; qcom,cycle-counter-high-soc = <85>; qcom,capacity-learning-on; qcom,fg-cc-cv-threshold-mv = <4340>; qcom,pmic-revid = <&pmi8994_revid>; Loading drivers/power/power_supply_sysfs.c +1 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(dp_dm), POWER_SUPPLY_ATTR(input_current_limited), POWER_SUPPLY_ATTR(rerun_aicl), POWER_SUPPLY_ATTR(cycle_count_id), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ Loading drivers/power/qpnp-fg.c +159 −124 Original line number Diff line number Diff line Loading @@ -70,6 +70,8 @@ #define QPNP_FG_DEV_NAME "qcom,qpnp-fg" #define MEM_IF_TIMEOUT_MS 5000 #define BUCKET_COUNT 8 #define BUCKET_SOC_PCT (256 / BUCKET_COUNT) #define BCL_MA_TO_ADC(_current, _adc_val) { \ _adc_val = (u8)((_current) * 100 / 976); \ Loading Loading @@ -140,6 +142,15 @@ struct fg_rslow_data { struct mutex lock; }; struct fg_cyc_ctr_data { bool en; bool started[BUCKET_COUNT]; u16 count[BUCKET_COUNT]; u8 last_soc[BUCKET_COUNT]; int id; struct mutex lock; }; /* FG_MEMIF setting index */ enum fg_mem_setting_index { FG_MEM_SOFT_COLD = 0, Loading Loading @@ -371,7 +382,6 @@ struct fg_chip { struct completion batt_id_avail; struct power_supply bms_psy; struct mutex rw_lock; struct mutex cyc_ctr_lock; struct mutex sysfs_restart_lock; struct work_struct batt_profile_init; struct work_struct dump_sram; Loading Loading @@ -399,8 +409,6 @@ struct fg_chip { bool power_supply_registered; bool sw_rbias_ctrl; bool use_thermal_coefficients; bool cyc_ctr_en; bool cyc_ctr_started; bool esr_strict_filter; bool soc_empty; bool charge_done; Loading @@ -412,9 +420,6 @@ struct fg_chip { struct delayed_work check_empty_work; char *batt_profile; u8 thermal_coefficients[THERMAL_COEFF_N_BYTES]; u16 cycle_counter; u32 cyc_ctr_low_soc; u32 cyc_ctr_hi_soc; u32 cc_cv_threshold_mv; unsigned int batt_profile_len; unsigned int batt_max_voltage_uv; Loading @@ -422,7 +427,6 @@ struct fg_chip { const char *batt_psy_name; unsigned long last_sram_update_time; unsigned long last_temp_update_time; int cyc_ctr_freq; int64_t ocv_coeffs[12]; int64_t cutoff_voltage; int evaluation_current; Loading @@ -440,6 +444,8 @@ struct fg_chip { struct work_struct fg_cap_learning_work; /* rslow compensation */ struct fg_rslow_data rslow_comp; /* cycle counter */ struct fg_cyc_ctr_data cyc_ctr; /* interleaved memory access */ u16 *offset; bool ima_supported; Loading Loading @@ -2101,107 +2107,145 @@ static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts) return rc; } #define BATT_CYCLE_NUMBER_REG 0x58C #define BATT_CYCLE_OFFSET 3 static int fg_inc_store_cycle_ctr(struct fg_chip *chip) #define BATT_CYCLE_NUMBER_REG 0x5E8 #define BATT_CYCLE_OFFSET 0 static void restore_cycle_counter(struct fg_chip *chip) { int rc = 0; u16 cyc_count; u8 reg[2]; int rc = 0, i, address; u8 data[2]; cyc_count = chip->cycle_counter; cyc_count++; reg[0] = cyc_count & 0xFF; reg[1] = cyc_count >> 8; rc = fg_mem_write(chip, reg, BATT_CYCLE_NUMBER_REG, 2, fg_mem_lock(chip); for (i = 0; i < BUCKET_COUNT; i++) { address = BATT_CYCLE_NUMBER_REG + i * 2; rc = fg_mem_read(chip, (u8 *)&data, address, 2, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n", i, rc); else chip->cyc_ctr.count[i] = data[0] | data[1] << 8; } fg_mem_release(chip); } static void clear_cycle_counter(struct fg_chip *chip) { int rc = 0, len, i; if (!chip->cyc_ctr.en) return; len = sizeof(chip->cyc_ctr.count); memset(chip->cyc_ctr.count, 0, len); for (i = 0; i < BUCKET_COUNT; i++) { chip->cyc_ctr.started[i] = false; chip->cyc_ctr.last_soc[i] = 0; } rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count, BATT_CYCLE_NUMBER_REG, len, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc); } static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) { int rc = 0, address; u16 cyc_count; u8 data[2]; if (bucket < 0 || (bucket > BUCKET_COUNT - 1)) return 0; cyc_count = chip->cyc_ctr.count[bucket]; cyc_count++; data[0] = cyc_count & 0xFF; data[1] = cyc_count >> 8; address = BATT_CYCLE_NUMBER_REG + bucket * 2; rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n", bucket, rc); else chip->cycle_counter = cyc_count; chip->cyc_ctr.count[bucket] = cyc_count; return rc; } #define CYCLE_CTR_UPDATE_FREQUENCY 2 static void update_cycle_count(struct work_struct *work) { int rc = 0; u8 reg[3]; int rc = 0, bucket, i; u8 reg[3], batt_soc; struct fg_chip *chip = container_of(work, struct fg_chip, cycle_count_work); chip->cyc_ctr_freq++; if (chip->cyc_ctr_freq % CYCLE_CTR_UPDATE_FREQUENCY) { if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { mutex_lock(&chip->cyc_ctr.lock); rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3, BATTERY_SOC_OFFSET, 0); if (rc) { pr_err("Failed to read battery soc rc: %d\n", rc); return; pr_err("Failed to read battery soc rc: %d\n", rc); goto out; } batt_soc = reg[2]; /* Check the MSB of battery SOC against the * Low and High SOC thresholds specified for * cycle counter algorithm. Since the low and * high SOC thresholds are already converted * to the scale of 0-255, they can be used * as is against the battery SOC. if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { /* Find out which bucket the SOC falls in */ bucket = batt_soc / BUCKET_SOC_PCT; if (fg_debug_mask & FG_STATUS) pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket); /* * If we've started counting for the previous bucket, * then store the counter for that bucket if the * counter for current bucket is getting started. */ mutex_lock(&chip->cyc_ctr_lock); if ((reg[2] < chip->cyc_ctr_low_soc) && !chip->cyc_ctr_started) { chip->cyc_ctr_started = true; } else if ((reg[2] > chip->cyc_ctr_hi_soc) && chip->cyc_ctr_started) { rc = fg_inc_store_cycle_ctr(chip); if (rc) if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && !chip->cyc_ctr.started[bucket]) { rc = fg_inc_store_cycle_ctr(chip, bucket - 1); if (rc) { pr_err("Error in storing cycle_ctr rc: %d\n", rc); else chip->cyc_ctr_started = false; } mutex_unlock(&chip->cyc_ctr_lock); goto out; } else { /* There is a slim chance where the cycle counter * algorithm might have started and the charger * got disconnected before the delta SOC interrupt * had scheduled this work again. Read the battery * SOC again in such cases to determine whether the * cycle counter needs to be stored. */ if (chip->cyc_ctr_started) { rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3, BATTERY_SOC_OFFSET, 0); if (rc) { pr_err("Failed to read battery soc rc: %d\n", rc); return; chip->cyc_ctr.started[bucket - 1] = false; chip->cyc_ctr.last_soc[bucket - 1] = 0; } mutex_lock(&chip->cyc_ctr_lock); if (reg[2] > chip->cyc_ctr_hi_soc) { rc = fg_inc_store_cycle_ctr(chip); } if (!chip->cyc_ctr.started[bucket]) { chip->cyc_ctr.started[bucket] = true; chip->cyc_ctr.last_soc[bucket] = batt_soc; } } else { for (i = 0; i < BUCKET_COUNT; i++) { if (chip->cyc_ctr.started[i] && batt_soc > chip->cyc_ctr.last_soc[i]) { rc = fg_inc_store_cycle_ctr(chip, i); if (rc) pr_err("Error in storing cycle_ctr rc: %d\n", rc); chip->cyc_ctr.last_soc[i] = 0; } chip->cyc_ctr_started = false; mutex_unlock(&chip->cyc_ctr_lock); } chip->cyc_ctr.started[i] = false; } } else { chip->cyc_ctr_freq = 0; } out: mutex_unlock(&chip->cyc_ctr.lock); } static int fg_get_cycle_count(struct fg_chip *chip) { int cyc_count; mutex_lock(&chip->cyc_ctr_lock); cyc_count = chip->cycle_counter; mutex_unlock(&chip->cyc_ctr_lock); return cyc_count; int count; if (!chip->cyc_ctr.en) return 0; mutex_lock(&chip->cyc_ctr.lock); count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1]; mutex_unlock(&chip->cyc_ctr.lock); return count; } static void half_float_to_buffer(int64_t uval, u8 *buffer) Loading Loading @@ -2440,6 +2484,7 @@ static enum power_supply_property fg_power_props[] = { POWER_SUPPLY_PROP_ESR_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_CYCLE_COUNT_ID, }; static int fg_power_get_property(struct power_supply *psy, Loading Loading @@ -2492,6 +2537,9 @@ static int fg_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CYCLE_COUNT: val->intval = fg_get_cycle_count(chip); break; case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: val->intval = chip->cyc_ctr.id; break; case POWER_SUPPLY_PROP_RESISTANCE_ID: val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID); break; Loading Loading @@ -2990,6 +3038,8 @@ static void status_change_work(struct work_struct *work) schedule_delayed_work(&chip->update_sram_data, msecs_to_jiffies(0)); } if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); } } Loading Loading @@ -3030,6 +3080,9 @@ static int fg_power_set_property(struct power_supply *psy, schedule_work(&chip->set_resume_soc_work); } break; case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: chip->cyc_ctr.id = val->intval; break; default: return -EINVAL; }; Loading @@ -3043,6 +3096,7 @@ static int fg_property_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: case POWER_SUPPLY_PROP_WARM_TEMP: case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: return 1; default: break; Loading Loading @@ -3207,10 +3261,11 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip) chip->battery_missing = true; chip->profile_loaded = false; chip->batt_type = missing_batt_type; mutex_lock(&chip->cyc_ctr_lock); chip->cycle_counter = 0; chip->cyc_ctr_started = false; mutex_unlock(&chip->cyc_ctr_lock); mutex_lock(&chip->cyc_ctr.lock); if (fg_debug_mask & FG_IRQS) pr_info("battery missing, clearing cycle counters\n"); clear_cycle_counter(chip); mutex_unlock(&chip->cyc_ctr.lock); } else { if (!chip->use_otp_profile) { reinit_completion(&chip->batt_id_avail); Loading Loading @@ -3289,7 +3344,7 @@ static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) chip->rslow_comp.chg_rslow_comp_c1 > 0 && chip->rslow_comp.chg_rslow_comp_c2 > 0) schedule_work(&chip->rslow_comp_work); if (chip->cyc_ctr_en) if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); schedule_work(&chip->update_esr_work); return IRQ_HANDLED; Loading Loading @@ -3960,17 +4015,19 @@ wait: < settings[FG_MEM_VBAT_EST_DIFF].value * 1000; profiles_same = memcmp(chip->batt_profile, data, PROFILE_COMPARE_LEN) == 0; if (reg & PROFILE_INTEGRITY_BIT) if (reg & PROFILE_INTEGRITY_BIT) { fg_cap_learning_load_data(chip); if ((reg & PROFILE_INTEGRITY_BIT) && vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) { if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) { if (fg_debug_mask & FG_STATUS) pr_info("Battery profiles same, using default\n"); if (fg_est_dump) schedule_work(&chip->dump_sram); goto done; } } else { pr_info("Battery profile not same, clearing cycle counters\n"); clear_cycle_counter(chip); } if (fg_est_dump) dump_sram(&chip->dump_sram); if ((fg_debug_mask & FG_STATUS) && !vbat_in_range) Loading Loading @@ -4280,26 +4337,10 @@ static int fg_of_init(struct fg_chip *chip) chip->sw_rbias_ctrl = of_property_read_bool(node, "qcom,sw-rbias-control"); chip->cyc_ctr_en = of_property_read_bool(node, chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en"); if (chip->cyc_ctr_en) { rc = of_property_read_u32(node, "qcom,cycle-counter-low-soc", &chip->cyc_ctr_low_soc); rc |= of_property_read_u32(node, "qcom,cycle-counter-high-soc", &chip->cyc_ctr_hi_soc); if (rc) { pr_err("Error in reading cycle-counter-low/high soc rc: %d\n", rc); chip->cyc_ctr_en = false; } else if ((chip->cyc_ctr_hi_soc <= 0) || (chip->cyc_ctr_low_soc <= 0)) { pr_err("Couldn't find valid low/high soc\n"); chip->cyc_ctr_en = false; } chip->cyc_ctr_low_soc = soc_to_setpoint(chip->cyc_ctr_low_soc); chip->cyc_ctr_hi_soc = soc_to_setpoint(chip->cyc_ctr_hi_soc); chip->cycle_counter = 0; } if (chip->cyc_ctr.en) chip->cyc_ctr.id = 1; return rc; } Loading Loading @@ -4501,7 +4542,7 @@ static void fg_cleanup(struct fg_chip *chip) power_supply_unregister(&chip->bms_psy); mutex_destroy(&chip->rslow_comp.lock); mutex_destroy(&chip->rw_lock); mutex_destroy(&chip->cyc_ctr_lock); mutex_destroy(&chip->cyc_ctr.lock); mutex_destroy(&chip->learning_data.learning_lock); mutex_destroy(&chip->sysfs_restart_lock); wakeup_source_trash(&chip->resume_soc_wakeup_source.source); Loading Loading @@ -5103,14 +5144,8 @@ static int fg_8994_hw_init(struct fg_chip *chip) data[1] = KI_COEFF_PRED_FULL_4_0_MSB; fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0); /* Read the cycle counter back from FG SRAM */ if (chip->cyc_ctr_en) { rc = fg_mem_read(chip, data, BATT_CYCLE_NUMBER_REG, 2, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("Failed to read BATT_CYCLE_NUMBER rc: %d\n", rc); else chip->cycle_counter = data[0] | data[1] << 8; } if (chip->cyc_ctr.en) restore_cycle_counter(chip); esr_value = ESR_DEFAULT_VALUE; rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8, Loading Loading @@ -5327,7 +5362,7 @@ static int fg_probe(struct spmi_device *spmi) wakeup_source_init(&chip->resume_soc_wakeup_source.source, "qpnp_fg_set_resume_soc"); mutex_init(&chip->rw_lock); mutex_init(&chip->cyc_ctr_lock); mutex_init(&chip->cyc_ctr.lock); mutex_init(&chip->learning_data.learning_lock); mutex_init(&chip->rslow_comp.lock); mutex_init(&chip->sysfs_restart_lock); Loading Loading @@ -5501,7 +5536,7 @@ cancel_work: of_init_fail: mutex_destroy(&chip->rslow_comp.lock); mutex_destroy(&chip->rw_lock); mutex_destroy(&chip->cyc_ctr_lock); mutex_destroy(&chip->cyc_ctr.lock); mutex_destroy(&chip->learning_data.learning_lock); mutex_destroy(&chip->sysfs_restart_lock); wakeup_source_trash(&chip->resume_soc_wakeup_source.source); Loading include/linux/power_supply.h +1 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_DP_DM, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_CYCLE_COUNT_ID, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ Loading Loading
Documentation/devicetree/bindings/power/qpnp-fg.txt +0 −6 Original line number Diff line number Diff line Loading @@ -109,12 +109,6 @@ Parent node optional properties: present, then the following properties to specify low and high soc thresholds should be defined. - qcom,cycle-counter-low-soc: Low SOC threshold which will be compared against the battery SOC before starting the cycle counter algorithm. - qcom,cycle-counter-high-soc: High SOC threshold which will be compared against the battery SOC before incrementing the cycle counter. - qcom,capacity-learning-on: A boolean property to have the fuel gauge driver attempt to learn the battery capacity when charging. Takes Loading
arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +0 −2 Original line number Diff line number Diff line Loading @@ -314,8 +314,6 @@ qcom,fg-iterm-ma = <100>; qcom,fg-chg-iterm-ma = <100>; qcom,cycle-counter-en; qcom,cycle-counter-low-soc = <15>; qcom,cycle-counter-high-soc = <85>; qcom,capacity-learning-on; qcom,fg-cc-cv-threshold-mv = <4340>; qcom,pmic-revid = <&pmi8994_revid>; Loading
drivers/power/power_supply_sysfs.c +1 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(dp_dm), POWER_SUPPLY_ATTR(input_current_limited), POWER_SUPPLY_ATTR(rerun_aicl), POWER_SUPPLY_ATTR(cycle_count_id), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ Loading
drivers/power/qpnp-fg.c +159 −124 Original line number Diff line number Diff line Loading @@ -70,6 +70,8 @@ #define QPNP_FG_DEV_NAME "qcom,qpnp-fg" #define MEM_IF_TIMEOUT_MS 5000 #define BUCKET_COUNT 8 #define BUCKET_SOC_PCT (256 / BUCKET_COUNT) #define BCL_MA_TO_ADC(_current, _adc_val) { \ _adc_val = (u8)((_current) * 100 / 976); \ Loading Loading @@ -140,6 +142,15 @@ struct fg_rslow_data { struct mutex lock; }; struct fg_cyc_ctr_data { bool en; bool started[BUCKET_COUNT]; u16 count[BUCKET_COUNT]; u8 last_soc[BUCKET_COUNT]; int id; struct mutex lock; }; /* FG_MEMIF setting index */ enum fg_mem_setting_index { FG_MEM_SOFT_COLD = 0, Loading Loading @@ -371,7 +382,6 @@ struct fg_chip { struct completion batt_id_avail; struct power_supply bms_psy; struct mutex rw_lock; struct mutex cyc_ctr_lock; struct mutex sysfs_restart_lock; struct work_struct batt_profile_init; struct work_struct dump_sram; Loading Loading @@ -399,8 +409,6 @@ struct fg_chip { bool power_supply_registered; bool sw_rbias_ctrl; bool use_thermal_coefficients; bool cyc_ctr_en; bool cyc_ctr_started; bool esr_strict_filter; bool soc_empty; bool charge_done; Loading @@ -412,9 +420,6 @@ struct fg_chip { struct delayed_work check_empty_work; char *batt_profile; u8 thermal_coefficients[THERMAL_COEFF_N_BYTES]; u16 cycle_counter; u32 cyc_ctr_low_soc; u32 cyc_ctr_hi_soc; u32 cc_cv_threshold_mv; unsigned int batt_profile_len; unsigned int batt_max_voltage_uv; Loading @@ -422,7 +427,6 @@ struct fg_chip { const char *batt_psy_name; unsigned long last_sram_update_time; unsigned long last_temp_update_time; int cyc_ctr_freq; int64_t ocv_coeffs[12]; int64_t cutoff_voltage; int evaluation_current; Loading @@ -440,6 +444,8 @@ struct fg_chip { struct work_struct fg_cap_learning_work; /* rslow compensation */ struct fg_rslow_data rslow_comp; /* cycle counter */ struct fg_cyc_ctr_data cyc_ctr; /* interleaved memory access */ u16 *offset; bool ima_supported; Loading Loading @@ -2101,107 +2107,145 @@ static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts) return rc; } #define BATT_CYCLE_NUMBER_REG 0x58C #define BATT_CYCLE_OFFSET 3 static int fg_inc_store_cycle_ctr(struct fg_chip *chip) #define BATT_CYCLE_NUMBER_REG 0x5E8 #define BATT_CYCLE_OFFSET 0 static void restore_cycle_counter(struct fg_chip *chip) { int rc = 0; u16 cyc_count; u8 reg[2]; int rc = 0, i, address; u8 data[2]; cyc_count = chip->cycle_counter; cyc_count++; reg[0] = cyc_count & 0xFF; reg[1] = cyc_count >> 8; rc = fg_mem_write(chip, reg, BATT_CYCLE_NUMBER_REG, 2, fg_mem_lock(chip); for (i = 0; i < BUCKET_COUNT; i++) { address = BATT_CYCLE_NUMBER_REG + i * 2; rc = fg_mem_read(chip, (u8 *)&data, address, 2, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n", i, rc); else chip->cyc_ctr.count[i] = data[0] | data[1] << 8; } fg_mem_release(chip); } static void clear_cycle_counter(struct fg_chip *chip) { int rc = 0, len, i; if (!chip->cyc_ctr.en) return; len = sizeof(chip->cyc_ctr.count); memset(chip->cyc_ctr.count, 0, len); for (i = 0; i < BUCKET_COUNT; i++) { chip->cyc_ctr.started[i] = false; chip->cyc_ctr.last_soc[i] = 0; } rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count, BATT_CYCLE_NUMBER_REG, len, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc); } static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) { int rc = 0, address; u16 cyc_count; u8 data[2]; if (bucket < 0 || (bucket > BUCKET_COUNT - 1)) return 0; cyc_count = chip->cyc_ctr.count[bucket]; cyc_count++; data[0] = cyc_count & 0xFF; data[1] = cyc_count >> 8; address = BATT_CYCLE_NUMBER_REG + bucket * 2; rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n", bucket, rc); else chip->cycle_counter = cyc_count; chip->cyc_ctr.count[bucket] = cyc_count; return rc; } #define CYCLE_CTR_UPDATE_FREQUENCY 2 static void update_cycle_count(struct work_struct *work) { int rc = 0; u8 reg[3]; int rc = 0, bucket, i; u8 reg[3], batt_soc; struct fg_chip *chip = container_of(work, struct fg_chip, cycle_count_work); chip->cyc_ctr_freq++; if (chip->cyc_ctr_freq % CYCLE_CTR_UPDATE_FREQUENCY) { if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { mutex_lock(&chip->cyc_ctr.lock); rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3, BATTERY_SOC_OFFSET, 0); if (rc) { pr_err("Failed to read battery soc rc: %d\n", rc); return; pr_err("Failed to read battery soc rc: %d\n", rc); goto out; } batt_soc = reg[2]; /* Check the MSB of battery SOC against the * Low and High SOC thresholds specified for * cycle counter algorithm. Since the low and * high SOC thresholds are already converted * to the scale of 0-255, they can be used * as is against the battery SOC. if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { /* Find out which bucket the SOC falls in */ bucket = batt_soc / BUCKET_SOC_PCT; if (fg_debug_mask & FG_STATUS) pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket); /* * If we've started counting for the previous bucket, * then store the counter for that bucket if the * counter for current bucket is getting started. */ mutex_lock(&chip->cyc_ctr_lock); if ((reg[2] < chip->cyc_ctr_low_soc) && !chip->cyc_ctr_started) { chip->cyc_ctr_started = true; } else if ((reg[2] > chip->cyc_ctr_hi_soc) && chip->cyc_ctr_started) { rc = fg_inc_store_cycle_ctr(chip); if (rc) if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && !chip->cyc_ctr.started[bucket]) { rc = fg_inc_store_cycle_ctr(chip, bucket - 1); if (rc) { pr_err("Error in storing cycle_ctr rc: %d\n", rc); else chip->cyc_ctr_started = false; } mutex_unlock(&chip->cyc_ctr_lock); goto out; } else { /* There is a slim chance where the cycle counter * algorithm might have started and the charger * got disconnected before the delta SOC interrupt * had scheduled this work again. Read the battery * SOC again in such cases to determine whether the * cycle counter needs to be stored. */ if (chip->cyc_ctr_started) { rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3, BATTERY_SOC_OFFSET, 0); if (rc) { pr_err("Failed to read battery soc rc: %d\n", rc); return; chip->cyc_ctr.started[bucket - 1] = false; chip->cyc_ctr.last_soc[bucket - 1] = 0; } mutex_lock(&chip->cyc_ctr_lock); if (reg[2] > chip->cyc_ctr_hi_soc) { rc = fg_inc_store_cycle_ctr(chip); } if (!chip->cyc_ctr.started[bucket]) { chip->cyc_ctr.started[bucket] = true; chip->cyc_ctr.last_soc[bucket] = batt_soc; } } else { for (i = 0; i < BUCKET_COUNT; i++) { if (chip->cyc_ctr.started[i] && batt_soc > chip->cyc_ctr.last_soc[i]) { rc = fg_inc_store_cycle_ctr(chip, i); if (rc) pr_err("Error in storing cycle_ctr rc: %d\n", rc); chip->cyc_ctr.last_soc[i] = 0; } chip->cyc_ctr_started = false; mutex_unlock(&chip->cyc_ctr_lock); } chip->cyc_ctr.started[i] = false; } } else { chip->cyc_ctr_freq = 0; } out: mutex_unlock(&chip->cyc_ctr.lock); } static int fg_get_cycle_count(struct fg_chip *chip) { int cyc_count; mutex_lock(&chip->cyc_ctr_lock); cyc_count = chip->cycle_counter; mutex_unlock(&chip->cyc_ctr_lock); return cyc_count; int count; if (!chip->cyc_ctr.en) return 0; mutex_lock(&chip->cyc_ctr.lock); count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1]; mutex_unlock(&chip->cyc_ctr.lock); return count; } static void half_float_to_buffer(int64_t uval, u8 *buffer) Loading Loading @@ -2440,6 +2484,7 @@ static enum power_supply_property fg_power_props[] = { POWER_SUPPLY_PROP_ESR_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_CYCLE_COUNT_ID, }; static int fg_power_get_property(struct power_supply *psy, Loading Loading @@ -2492,6 +2537,9 @@ static int fg_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CYCLE_COUNT: val->intval = fg_get_cycle_count(chip); break; case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: val->intval = chip->cyc_ctr.id; break; case POWER_SUPPLY_PROP_RESISTANCE_ID: val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID); break; Loading Loading @@ -2990,6 +3038,8 @@ static void status_change_work(struct work_struct *work) schedule_delayed_work(&chip->update_sram_data, msecs_to_jiffies(0)); } if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); } } Loading Loading @@ -3030,6 +3080,9 @@ static int fg_power_set_property(struct power_supply *psy, schedule_work(&chip->set_resume_soc_work); } break; case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: chip->cyc_ctr.id = val->intval; break; default: return -EINVAL; }; Loading @@ -3043,6 +3096,7 @@ static int fg_property_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: case POWER_SUPPLY_PROP_WARM_TEMP: case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: return 1; default: break; Loading Loading @@ -3207,10 +3261,11 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip) chip->battery_missing = true; chip->profile_loaded = false; chip->batt_type = missing_batt_type; mutex_lock(&chip->cyc_ctr_lock); chip->cycle_counter = 0; chip->cyc_ctr_started = false; mutex_unlock(&chip->cyc_ctr_lock); mutex_lock(&chip->cyc_ctr.lock); if (fg_debug_mask & FG_IRQS) pr_info("battery missing, clearing cycle counters\n"); clear_cycle_counter(chip); mutex_unlock(&chip->cyc_ctr.lock); } else { if (!chip->use_otp_profile) { reinit_completion(&chip->batt_id_avail); Loading Loading @@ -3289,7 +3344,7 @@ static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) chip->rslow_comp.chg_rslow_comp_c1 > 0 && chip->rslow_comp.chg_rslow_comp_c2 > 0) schedule_work(&chip->rslow_comp_work); if (chip->cyc_ctr_en) if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); schedule_work(&chip->update_esr_work); return IRQ_HANDLED; Loading Loading @@ -3960,17 +4015,19 @@ wait: < settings[FG_MEM_VBAT_EST_DIFF].value * 1000; profiles_same = memcmp(chip->batt_profile, data, PROFILE_COMPARE_LEN) == 0; if (reg & PROFILE_INTEGRITY_BIT) if (reg & PROFILE_INTEGRITY_BIT) { fg_cap_learning_load_data(chip); if ((reg & PROFILE_INTEGRITY_BIT) && vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) { if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) { if (fg_debug_mask & FG_STATUS) pr_info("Battery profiles same, using default\n"); if (fg_est_dump) schedule_work(&chip->dump_sram); goto done; } } else { pr_info("Battery profile not same, clearing cycle counters\n"); clear_cycle_counter(chip); } if (fg_est_dump) dump_sram(&chip->dump_sram); if ((fg_debug_mask & FG_STATUS) && !vbat_in_range) Loading Loading @@ -4280,26 +4337,10 @@ static int fg_of_init(struct fg_chip *chip) chip->sw_rbias_ctrl = of_property_read_bool(node, "qcom,sw-rbias-control"); chip->cyc_ctr_en = of_property_read_bool(node, chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en"); if (chip->cyc_ctr_en) { rc = of_property_read_u32(node, "qcom,cycle-counter-low-soc", &chip->cyc_ctr_low_soc); rc |= of_property_read_u32(node, "qcom,cycle-counter-high-soc", &chip->cyc_ctr_hi_soc); if (rc) { pr_err("Error in reading cycle-counter-low/high soc rc: %d\n", rc); chip->cyc_ctr_en = false; } else if ((chip->cyc_ctr_hi_soc <= 0) || (chip->cyc_ctr_low_soc <= 0)) { pr_err("Couldn't find valid low/high soc\n"); chip->cyc_ctr_en = false; } chip->cyc_ctr_low_soc = soc_to_setpoint(chip->cyc_ctr_low_soc); chip->cyc_ctr_hi_soc = soc_to_setpoint(chip->cyc_ctr_hi_soc); chip->cycle_counter = 0; } if (chip->cyc_ctr.en) chip->cyc_ctr.id = 1; return rc; } Loading Loading @@ -4501,7 +4542,7 @@ static void fg_cleanup(struct fg_chip *chip) power_supply_unregister(&chip->bms_psy); mutex_destroy(&chip->rslow_comp.lock); mutex_destroy(&chip->rw_lock); mutex_destroy(&chip->cyc_ctr_lock); mutex_destroy(&chip->cyc_ctr.lock); mutex_destroy(&chip->learning_data.learning_lock); mutex_destroy(&chip->sysfs_restart_lock); wakeup_source_trash(&chip->resume_soc_wakeup_source.source); Loading Loading @@ -5103,14 +5144,8 @@ static int fg_8994_hw_init(struct fg_chip *chip) data[1] = KI_COEFF_PRED_FULL_4_0_MSB; fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0); /* Read the cycle counter back from FG SRAM */ if (chip->cyc_ctr_en) { rc = fg_mem_read(chip, data, BATT_CYCLE_NUMBER_REG, 2, BATT_CYCLE_OFFSET, 0); if (rc) pr_err("Failed to read BATT_CYCLE_NUMBER rc: %d\n", rc); else chip->cycle_counter = data[0] | data[1] << 8; } if (chip->cyc_ctr.en) restore_cycle_counter(chip); esr_value = ESR_DEFAULT_VALUE; rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8, Loading Loading @@ -5327,7 +5362,7 @@ static int fg_probe(struct spmi_device *spmi) wakeup_source_init(&chip->resume_soc_wakeup_source.source, "qpnp_fg_set_resume_soc"); mutex_init(&chip->rw_lock); mutex_init(&chip->cyc_ctr_lock); mutex_init(&chip->cyc_ctr.lock); mutex_init(&chip->learning_data.learning_lock); mutex_init(&chip->rslow_comp.lock); mutex_init(&chip->sysfs_restart_lock); Loading Loading @@ -5501,7 +5536,7 @@ cancel_work: of_init_fail: mutex_destroy(&chip->rslow_comp.lock); mutex_destroy(&chip->rw_lock); mutex_destroy(&chip->cyc_ctr_lock); mutex_destroy(&chip->cyc_ctr.lock); mutex_destroy(&chip->learning_data.learning_lock); mutex_destroy(&chip->sysfs_restart_lock); wakeup_source_trash(&chip->resume_soc_wakeup_source.source); Loading
include/linux/power_supply.h +1 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_DP_DM, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_CYCLE_COUNT_ID, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ Loading