Loading Documentation/devicetree/bindings/batterydata/batterydata.txt +19 −4 Original line number Diff line number Diff line Loading @@ -120,13 +120,28 @@ Profile data node optional properties: The threshold values in range should be in ascending and shouldn't overlap. It support 8 ranges at max. - qcom,jeita-soft-thresholds: A tuple entry to specify ADC code for battery's soft JEITA threshold. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>. threshold. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>. - qcom,jeita-hard-thresholds: A tuple entry to specify ADC code for battery's hard JEITA threshold. <HARD_COLD_ADC_CODE, HARD_HOT_ADC_CODE>. threshold. <HARD_COLD_ADC_CODE, HARD_HOT_ADC_CODE>. - qcom,jeita-soft-hys-thresholds: A tuple entry to specify ADC code for battery's soft JEITA threshold with hysteresis adjustment. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>. These "hysteresis" values should be specified if "qcom,jeita-soft-thresholds" are specified. Without which SW JEITA compensation won't function properly. - qcom,jeita-soft-fcc-ua: A tuple entry to specify the values of Fast charging current (in uA) that needs to be applied during soft JEITA conditions (cool/warm). Element 0 - FCC value for soft cool. Element 1 - FCC value for soft warm. - qcom,jeita-soft-fv-uv: A tuple entry to specify the values of Float voltage (in uV) that needs to be applied during soft JEITA conditions (cool/warm). Element 0 - FV value for soft cool. Element 1 - FV value for soft warm. - qcom,batt-age-level: Battery age level. This is used only when multiple profile loading is supported. Profile data node required subnodes: - qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes temperature to fcc lookup. The units for this lookup Loading drivers/power/supply/qcom/battery.c +61 −3 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__ Loading Loading @@ -40,6 +40,7 @@ #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define ICL_LIMIT_VOTER "ICL_LIMIT_VOTER" #define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER" #define FCC_VOTER "FCC_VOTER" struct pl_data { int pl_mode; Loading @@ -57,6 +58,7 @@ struct pl_data { struct votable *hvdcp_hw_inov_dis_votable; struct votable *usb_icl_votable; struct votable *pl_enable_votable_indirect; struct votable *cp_ilim_votable; struct delayed_work status_change_work; struct work_struct pl_disable_forever_work; struct work_struct pl_taper_work; Loading @@ -68,6 +70,7 @@ struct pl_data { struct power_supply *batt_psy; struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply *cp_master_psy; int charge_type; int total_settled_ua; int pl_settled_ua; Loading @@ -85,6 +88,7 @@ struct pl_data { struct wakeup_source *pl_ws; struct notifier_block nb; bool pl_disable; bool cp_disabled; int taper_entry_fv; int main_fcc_max; /* debugfs directory */ Loading Loading @@ -476,6 +480,46 @@ static void get_fcc_split(struct pl_data *chip, int total_ua, } } static void get_main_fcc_config(struct pl_data *chip, int *total_fcc) { union power_supply_propval pval = {0, }; int rc; if (!chip->cp_master_psy) chip->cp_master_psy = power_supply_get_by_name("charge_pump_master"); if (!chip->cp_master_psy) goto out; rc = power_supply_get_property(chip->cp_master_psy, POWER_SUPPLY_PROP_CP_SWITCHER_EN, &pval); if (rc < 0) { pr_err("Couldn't get switcher enable status, rc=%d\n", rc); goto out; } if (!pval.intval) { /* * To honor main charger upper FCC limit, on CP switcher * disable, skip fcc slewing as it will cause delay in limiting * the charge current flowing through main charger. */ if (!chip->cp_disabled) { chip->fcc_stepper_enable = false; pl_dbg(chip, PR_PARALLEL, "Disabling FCC slewing on CP Switcher disable\n"); } chip->cp_disabled = true; } else { chip->cp_disabled = false; pl_dbg(chip, PR_PARALLEL, "CP Switcher is enabled, don't limit main fcc\n"); return; } out: *total_fcc = min(*total_fcc, chip->main_fcc_max); } static void get_fcc_stepper_params(struct pl_data *chip, int main_fcc_ua, int parallel_fcc_ua) { Loading Loading @@ -618,6 +662,9 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, if (!chip->main_psy) return 0; if (!chip->cp_ilim_votable) chip->cp_ilim_votable = find_votable("CP_ILIM"); if (chip->pl_mode != POWER_SUPPLY_PL_NONE) { get_fcc_split(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua); Loading Loading @@ -814,6 +861,10 @@ static void fcc_stepper_work(struct work_struct *work) chip->main_fcc_ua = main_fcc; chip->slave_fcc_ua = parallel_fcc; if (chip->cp_ilim_votable) vote(chip->cp_ilim_votable, FCC_VOTER, true, chip->main_fcc_ua / 2); if (reschedule_ms) { schedule_delayed_work(&chip->fcc_stepper_work, msecs_to_jiffies(reschedule_ms)); Loading Loading @@ -929,6 +980,9 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, false, 0); if (chip->cp_ilim_votable) vote(chip->cp_ilim_votable, ICL_CHANGE_VOTER, true, icl_ua); return 0; } Loading Loading @@ -1161,8 +1215,7 @@ static int pl_disable_vote_callback(struct votable *votable, (slave_fcc_ua * 100) / total_fcc_ua); } else { if (chip->main_fcc_max) total_fcc_ua = min(total_fcc_ua, chip->main_fcc_max); get_main_fcc_config(chip, &total_fcc_ua); if (!chip->fcc_stepper_enable) { if (IS_USBIN(chip->pl_mode)) Loading @@ -1188,6 +1241,10 @@ static int pl_disable_vote_callback(struct votable *votable, return rc; } if (chip->cp_ilim_votable) vote(chip->cp_ilim_votable, FCC_VOTER, true, total_fcc_ua / 2); /* reset parallel FCC */ chip->slave_fcc_ua = 0; chip->total_settled_ua = 0; Loading Loading @@ -1700,6 +1757,7 @@ int qcom_batt_init(int smb_version) } chip->pl_disable = true; chip->cp_disabled = true; chip->qcom_batt_class.name = "qcom-battery", chip->qcom_batt_class.owner = THIS_MODULE, chip->qcom_batt_class.class_groups = batt_class_groups; Loading drivers/power/supply/qcom/qpnp-smb5.c +9 −20 Original line number Diff line number Diff line Loading @@ -1301,8 +1301,7 @@ static int smb5_batt_get_prop(struct power_supply *psy, QNOVO_VOTER); break; case POWER_SUPPLY_PROP_CURRENT_NOW: rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CURRENT_NOW, val); rc = smblib_get_batt_current_now(chg, val); break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: val->intval = get_client_vote_locked(chg->fcc_votable, Loading Loading @@ -1333,10 +1332,7 @@ static int smb5_batt_get_prop(struct power_supply *psy, val->intval = 0; break; case POWER_SUPPLY_PROP_DIE_HEALTH: if (chg->die_health == -EINVAL) val->intval = smblib_get_prop_die_health(chg); else val->intval = chg->die_health; rc = smblib_get_die_health(chg, val); break; case POWER_SUPPLY_PROP_DP_DM: val->intval = chg->pulse_cnt; Loading Loading @@ -1415,13 +1411,6 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: chg->step_chg_enabled = !!val->intval; break; case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: if (chg->sw_jeita_enabled != (!!val->intval)) { rc = smblib_disable_hw_jeita(chg, !!val->intval); if (rc == 0) chg->sw_jeita_enabled = !!val->intval; } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: chg->batt_profile_fcc_ua = val->intval; vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval); Loading Loading @@ -1472,6 +1461,9 @@ static int smb5_batt_set_prop(struct power_supply *psy, vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER, false, 0); break; case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: chg->fcc_stepper_enable = val->intval; break; default: rc = -EINVAL; } Loading @@ -1492,7 +1484,6 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_RERUN_AICL: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: case POWER_SUPPLY_PROP_DIE_HEALTH: return 1; default: Loading Loading @@ -2152,13 +2143,11 @@ static int smb5_init_hw(struct smb5 *chip) } } if (chg->sw_jeita_enabled) { rc = smblib_disable_hw_jeita(chg, true); if (rc < 0) { dev_err(chg->dev, "Couldn't set hw jeita rc=%d\n", rc); return rc; } } rc = smblib_masked_write(chg, DCDC_ENG_SDCDC_CFG5_REG, ENG_SDCDC_BAT_HPWR_MASK, BOOST_MODE_THRESH_3P6_V); Loading drivers/power/supply/qcom/smb1390-charger-psy.c +79 −20 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ #define CP_VOTER "CP_VOTER" #define USER_VOTER "USER_VOTER" #define ILIM_VOTER "ILIM_VOTER" #define TAPER_END_VOTER "TAPER_END_VOTER" #define FCC_VOTER "FCC_VOTER" #define ICL_VOTER "ICL_VOTER" #define WIRELESS_VOTER "WIRELESS_VOTER" Loading Loading @@ -122,6 +123,7 @@ struct smb1390 { struct votable *disable_votable; struct votable *ilim_votable; struct votable *fcc_votable; struct votable *fv_votable; struct votable *cp_awake_votable; /* power supplies */ Loading @@ -135,6 +137,8 @@ struct smb1390 { bool taper_work_running; struct smb1390_iio iio; int irq_status; int taper_entry_fv; bool switcher_disabled; }; struct smb_irq { Loading Loading @@ -204,6 +208,19 @@ static bool is_psy_voter_available(struct smb1390 *chip) } } if (!chip->fv_votable) { chip->fv_votable = find_votable("FV"); if (!chip->fv_votable) { pr_debug("Couldn't find FV votable\n"); return false; } } if (!chip->disable_votable) { pr_debug("Couldn't find CP DISABLE votable\n"); return false; } return true; } Loading Loading @@ -247,7 +264,8 @@ static int smb1390_get_cp_en_status(struct smb1390 *chip, int id, bool *enable) static irqreturn_t default_irq_handler(int irq, void *data) { struct smb1390 *chip = data; int i; int i, rc; bool enable; for (i = 0; i < NUM_IRQS; ++i) { if (irq == chip->irqs[i]) { Loading @@ -256,8 +274,18 @@ static irqreturn_t default_irq_handler(int irq, void *data) } } rc = smb1390_get_cp_en_status(chip, SWITCHER_EN, &enable); if (!rc) { if (chip->switcher_disabled == enable) { chip->switcher_disabled = !chip->switcher_disabled; if (chip->fcc_votable) rerun_election(chip->fcc_votable); } } if (chip->cp_master_psy) power_supply_changed(chip->cp_master_psy); return IRQ_HANDLED; } Loading Loading @@ -435,18 +463,20 @@ static int smb1390_ilim_vote_cb(struct votable *votable, void *data, return -EINVAL; } rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG, CFG_ILIM_MASK, DIV_ROUND_CLOSEST(max(ilim_uA, 500000) - 500000, 100000)); if (rc < 0) { pr_err("Failed to write ILIM Register, rc=%d\n", rc); return rc; } /* ILIM less than 1A is not accurate; disable charging */ if (ilim_uA < 1000000) { pr_debug("ILIM %duA is too low to allow charging\n", ilim_uA); vote(chip->disable_votable, ILIM_VOTER, true, 0); } else { pr_debug("setting ILIM to %duA\n", ilim_uA); rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG, CFG_ILIM_MASK, DIV_ROUND_CLOSEST(ilim_uA - 500000, 100000)); if (rc < 0) pr_err("Failed to write ILIM Register, rc=%d\n", rc); if (rc >= 0) pr_debug("ILIM set to %duA\n", ilim_uA); vote(chip->disable_votable, ILIM_VOTER, false, 0); } Loading Loading @@ -544,9 +574,13 @@ static void smb1390_status_change_work(struct work_struct *work) pval.intval); } /* input current is always half the charge current */ vote(chip->ilim_votable, FCC_VOTER, true, get_effective_result(chip->fcc_votable) / 2); /* * Remove SMB1390 Taper condition disable vote if float voltage * increased in comparison to voltage at which it entered taper. */ if (chip->taper_entry_fv < get_effective_result(chip->fv_votable)) vote(chip->disable_votable, TAPER_END_VOTER, false, 0); /* * all votes that would result in disabling the charge pump have Loading Loading @@ -574,6 +608,7 @@ static void smb1390_status_change_work(struct work_struct *work) } } else { vote(chip->disable_votable, SRC_VOTER, true, 0); vote(chip->disable_votable, TAPER_END_VOTER, false, 0); vote(chip->fcc_votable, CP_VOTER, false, 0); } Loading @@ -591,11 +626,8 @@ static void smb1390_taper_work(struct work_struct *work) if (!is_psy_voter_available(chip)) goto out; do { fcc_uA = get_effective_result(chip->fcc_votable) - 100000; pr_debug("taper work reducing FCC to %duA\n", fcc_uA); vote(chip->fcc_votable, CP_VOTER, true, fcc_uA); chip->taper_entry_fv = get_effective_result(chip->fv_votable); while (true) { rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); if (rc < 0) { Loading @@ -603,12 +635,30 @@ static void smb1390_taper_work(struct work_struct *work) goto out; } msleep(500); } while (fcc_uA >= 2000000 && pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER); if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) { fcc_uA = get_effective_result(chip->fcc_votable) - 100000; pr_debug("taper work reducing FCC to %duA\n", fcc_uA); vote(chip->fcc_votable, CP_VOTER, true, fcc_uA); if (fcc_uA < 2000000) { vote(chip->disable_votable, TAPER_END_VOTER, true, 0); goto out; } } else if (get_effective_result(chip->fv_votable) > chip->taper_entry_fv) { pr_debug("Float voltage increased. Exiting taper\n"); goto out; } else { pr_debug("In fast charging. Wait for next taper\n"); } msleep(500); } out: pr_debug("taper work exit\n"); vote(chip->fcc_votable, CP_VOTER, false, 0); chip->taper_work_running = false; } Loading Loading @@ -810,6 +860,14 @@ static int smb1390_create_votables(struct smb1390 *chip) */ vote(chip->disable_votable, USER_VOTER, true, 0); /* * In case SMB1390 probe happens after FCC value has been configured, * update ilim vote to reflect FCC / 2 value. */ if (chip->fcc_votable) vote(chip->ilim_votable, FCC_VOTER, true, get_effective_result(chip->fcc_votable) / 2); return 0; } Loading Loading @@ -926,6 +984,7 @@ static int smb1390_probe(struct platform_device *pdev) chip->dev = &pdev->dev; spin_lock_init(&chip->status_change_lock); mutex_init(&chip->die_chan_lock); chip->switcher_disabled = true; chip->regmap = dev_get_regmap(chip->dev->parent, NULL); if (!chip->regmap) { Loading drivers/power/supply/qcom/smb5-lib.c +256 −85 Original line number Diff line number Diff line Loading @@ -908,7 +908,7 @@ static int smblib_notifier_call(struct notifier_block *nb, schedule_work(&chg->bms_update_work); } if (!chg->jeita_configured) if (chg->jeita_configured == JEITA_CFG_NONE) schedule_work(&chg->jeita_update_work); if (chg->sec_pl_present && !chg->pl.psy Loading Loading @@ -1873,6 +1873,19 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, return 0; } int smblib_get_batt_current_now(struct smb_charger *chg, union power_supply_propval *val) { int rc; rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CURRENT_NOW, val); if (!rc) val->intval *= (-1); return rc; } /*********************** * BATTERY PSY SETTERS * ***********************/ Loading Loading @@ -2263,6 +2276,7 @@ int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) rc); return rc; } return 0; } Loading Loading @@ -2343,19 +2357,28 @@ static int smblib_update_thermal_readings(struct smb_charger *chg) } if (chg->sec_chg_selected == POWER_SUPPLY_CHARGER_SEC_CP) { rc = smblib_read_iio_channel(chg, chg->iio.smb_temp_chan, DIV_FACTOR_DECIDEGC, &chg->smb_temp); if (rc < 0) { smblib_err(chg, "Couldn't read SMB TEMP channel, rc=%d\n", if (!chg->cp_psy) chg->cp_psy = power_supply_get_by_name("charge_pump_master"); if (chg->cp_psy) { rc = power_supply_get_property(chg->cp_psy, POWER_SUPPLY_PROP_CP_DIE_TEMP, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get smb1390 charger temp, rc=%d\n", rc); return rc; } chg->smb_temp = pval.intval; } else { smblib_dbg(chg, PR_MISC, "Coudln't find cp_psy\n"); chg->smb_temp = -ENODATA; } } else if (chg->pl.psy && chg->sec_chg_selected == POWER_SUPPLY_CHARGER_SEC_PL) { rc = power_supply_get_property(chg->pl.psy, POWER_SUPPLY_PROP_CHARGER_TEMP, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get smb charger temp, rc=%d\n", smblib_err(chg, "Couldn't get smb1355 charger temp, rc=%d\n", rc); return rc; } Loading Loading @@ -3262,6 +3285,17 @@ int smblib_get_prop_die_health(struct smb_charger *chg) return POWER_SUPPLY_HEALTH_COOL; } int smblib_get_die_health(struct smb_charger *chg, union power_supply_propval *val) { if (chg->die_health == -EINVAL) val->intval = smblib_get_prop_die_health(chg); else val->intval = chg->die_health; return 0; } int smblib_get_prop_connector_health(struct smb_charger *chg) { int rc; Loading Loading @@ -3673,49 +3707,153 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, return rc; } static int smblib_recover_from_soft_jeita(struct smb_charger *chg) #define JEITA_SOFT 0 #define JEITA_HARD 1 static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, int type) { u8 stat1, stat7; int rc; u16 temp, base; rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat1); base = CHGR_JEITA_THRESHOLD_BASE_REG(type); temp = thresholds[1] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); smblib_err(chg, "Couldn't configure Jeita %s hot threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat7); temp = thresholds[0] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", rc); smblib_err(chg, "Couldn't configure Jeita %s cold threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } if ((chg->jeita_status && !(stat7 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK) && ((stat1 & BATTERY_CHARGER_STATUS_MASK) == TERMINATE_CHARGE))) { /* * We are moving from JEITA soft -> Normal and charging * is terminated */ rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable charging rc=%d\n", rc); smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n", (type == JEITA_SOFT) ? "Soft" : "Hard"); return 0; } static int smblib_charge_inhibit_en(struct smb_charger *chg, bool enable) { int rc; rc = smblib_masked_write(chg, CHGR_CFG2_REG, CHARGER_INHIBIT_BIT, enable ? CHARGER_INHIBIT_BIT : 0); return rc; } rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, CHARGING_ENABLE_CMD_BIT); static int smblib_soft_jeita_arb_wa(struct smb_charger *chg) { union power_supply_propval pval; int rc = 0; bool soft_jeita; rc = smblib_get_prop_batt_health(chg, &pval); if (rc < 0) { smblib_err(chg, "Couldn't enable charging rc=%d\n", rc); smblib_err(chg, "Couldn't get battery health rc=%d\n", rc); return rc; } } chg->jeita_status = stat7 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK; /* Do nothing on entering hard JEITA condition */ if (pval.intval == POWER_SUPPLY_HEALTH_COLD || pval.intval == POWER_SUPPLY_HEALTH_HOT) return 0; if (chg->jeita_soft_fcc[0] < 0 || chg->jeita_soft_fcc[1] < 0 || chg->jeita_soft_fv[0] < 0 || chg->jeita_soft_fv[1] < 0) return 0; soft_jeita = (pval.intval == POWER_SUPPLY_HEALTH_COOL) || (pval.intval == POWER_SUPPLY_HEALTH_WARM); /* Do nothing on entering soft JEITA from hard JEITA */ if (chg->jeita_arb_flag && soft_jeita) return 0; /* Do nothing, initial to health condition */ if (!chg->jeita_arb_flag && !soft_jeita) return 0; if (!chg->cp_disable_votable) chg->cp_disable_votable = find_votable("CP_DISABLE"); /* Entering soft JEITA from normal state */ if (!chg->jeita_arb_flag && soft_jeita) { vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0); /* Disable parallel charging */ if (chg->pl_disable_votable) vote(chg->pl_disable_votable, JEITA_ARB_VOTER, true, 0); if (chg->cp_disable_votable) vote(chg->cp_disable_votable, JEITA_ARB_VOTER, true, 0); rc = smblib_charge_inhibit_en(chg, true); if (rc < 0) smblib_err(chg, "Couldn't enable charge inhibit rc=%d\n", rc); rc = smblib_update_jeita(chg, chg->jeita_soft_hys_thlds, JEITA_SOFT); if (rc < 0) smblib_err(chg, "Couldn't configure Jeita soft threshold rc=%d\n", rc); if (pval.intval == POWER_SUPPLY_HEALTH_COOL) { vote(chg->fcc_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fcc[0]); vote(chg->fv_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fv[0]); } else { vote(chg->fcc_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fcc[1]); vote(chg->fv_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fv[1]); } vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0); chg->jeita_arb_flag = true; } else if (chg->jeita_arb_flag && !soft_jeita) { /* Exit to health state from soft JEITA */ vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0); rc = smblib_charge_inhibit_en(chg, false); if (rc < 0) smblib_err(chg, "Couldn't disable charge inhibit rc=%d\n", rc); rc = smblib_update_jeita(chg, chg->jeita_soft_thlds, JEITA_SOFT); if (rc < 0) smblib_err(chg, "Couldn't configure Jeita soft threshold rc=%d\n", rc); vote(chg->fcc_votable, JEITA_ARB_VOTER, false, 0); vote(chg->fv_votable, JEITA_ARB_VOTER, false, 0); if (chg->pl_disable_votable) vote(chg->pl_disable_votable, JEITA_ARB_VOTER, false, 0); if (chg->cp_disable_votable) vote(chg->cp_disable_votable, JEITA_ARB_VOTER, false, 0); vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0); chg->jeita_arb_flag = false; } smblib_dbg(chg, PR_MISC, "JEITA ARB status %d, soft JEITA status %d\n", chg->jeita_arb_flag, soft_jeita); return rc; } /************************ Loading Loading @@ -3873,15 +4011,18 @@ irqreturn_t batt_temp_changed_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc; rc = smblib_recover_from_soft_jeita(chg); smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if (chg->jeita_configured != JEITA_CFG_COMPLETE) return IRQ_HANDLED; rc = smblib_soft_jeita_arb_wa(chg); if (rc < 0) { smblib_err(chg, "Couldn't recover chg from soft jeita rc=%d\n", smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n", rc); return IRQ_HANDLED; } rerun_election(chg->fcc_votable); power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } Loading Loading @@ -4959,7 +5100,7 @@ irqreturn_t wdog_bark_irq_handler(int irq, void *data) if (rc < 0) smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc); if (chg->step_chg_enabled || chg->sw_jeita_enabled) if (chg->step_chg_enabled) power_supply_changed(chg->batt_psy); return IRQ_HANDLED; Loading Loading @@ -5173,42 +5314,6 @@ static void smblib_thermal_regulation_work(struct work_struct *work) rc); } #define JEITA_SOFT 0 #define JEITA_HARD 1 static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, int type) { int rc; u16 temp, base; base = CHGR_JEITA_THRESHOLD_BASE_REG(type); temp = thresholds[1] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't configure Jeita %s hot threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } temp = thresholds[0] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't configure Jeita %s cold threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n", (type == JEITA_SOFT) ? "Soft" : "Hard"); return 0; } static void jeita_update_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, Loading @@ -5216,8 +5321,8 @@ static void jeita_update_work(struct work_struct *work) struct device_node *node = chg->dev->of_node; struct device_node *batt_node, *pnode; union power_supply_propval val; int rc; u32 jeita_thresholds[2]; int rc, tmp[2], max_fcc_ma, max_fv_uv; u32 jeita_hard_thresholds[2]; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { Loading Loading @@ -5250,9 +5355,10 @@ static void jeita_update_work(struct work_struct *work) } rc = of_property_read_u32_array(pnode, "qcom,jeita-hard-thresholds", jeita_thresholds, 2); jeita_hard_thresholds, 2); if (!rc) { rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_HARD); rc = smblib_update_jeita(chg, jeita_hard_thresholds, JEITA_HARD); if (rc < 0) { smblib_err(chg, "Couldn't configure Hard Jeita rc=%d\n", rc); Loading @@ -5261,18 +5367,83 @@ static void jeita_update_work(struct work_struct *work) } rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-thresholds", jeita_thresholds, 2); chg->jeita_soft_thlds, 2); if (!rc) { rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_SOFT); rc = smblib_update_jeita(chg, chg->jeita_soft_thlds, JEITA_SOFT); if (rc < 0) { smblib_err(chg, "Couldn't configure Soft Jeita rc=%d\n", rc); goto out; } rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-hys-thresholds", chg->jeita_soft_hys_thlds, 2); if (rc < 0) { smblib_err(chg, "Couldn't get Soft Jeita hysteresis thresholds rc=%d\n", rc); goto out; } } chg->jeita_soft_fcc[0] = chg->jeita_soft_fcc[1] = -EINVAL; chg->jeita_soft_fv[0] = chg->jeita_soft_fv[1] = -EINVAL; max_fcc_ma = max_fv_uv = -EINVAL; of_property_read_u32(pnode, "qcom,fastchg-current-ma", &max_fcc_ma); of_property_read_u32(pnode, "qcom,max-voltage-uv", &max_fv_uv); if (max_fcc_ma <= 0 || max_fv_uv <= 0) { smblib_err(chg, "Incorrect fastchg-current-ma or max-voltage-uv\n"); goto out; } rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fcc-ua", tmp, 2); if (rc < 0) { smblib_err(chg, "Couldn't get fcc values for soft JEITA rc=%d\n", rc); goto out; } max_fcc_ma *= 1000; if (tmp[0] > max_fcc_ma || tmp[1] > max_fcc_ma) { smblib_err(chg, "Incorrect FCC value [%d %d] max: %d\n", tmp[0], tmp[1], max_fcc_ma); goto out; } chg->jeita_soft_fcc[0] = tmp[0]; chg->jeita_soft_fcc[1] = tmp[1]; rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fv-uv", tmp, 2); if (rc < 0) { smblib_err(chg, "Couldn't get fv values for soft JEITA rc=%d\n", rc); goto out; } if (tmp[0] > max_fv_uv || tmp[1] > max_fv_uv) { smblib_err(chg, "Incorrect FV value [%d %d] max: %d\n", tmp[0], tmp[1], max_fv_uv); goto out; } chg->jeita_soft_fv[0] = tmp[0]; chg->jeita_soft_fv[1] = tmp[1]; rc = smblib_soft_jeita_arb_wa(chg); if (rc < 0) { smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n", rc); goto out; } chg->jeita_configured = JEITA_CFG_COMPLETE; return; out: chg->jeita_configured = true; chg->jeita_configured = JEITA_CFG_FAILURE; } static void smblib_lpd_ra_open_work(struct work_struct *work) Loading Loading @@ -5530,7 +5701,7 @@ int smblib_init(struct smb_charger *chg) } rc = qcom_step_chg_init(chg->dev, chg->step_chg_enabled, chg->sw_jeita_enabled); chg->sw_jeita_enabled, false); if (rc < 0) { smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n", rc); Loading Loading
Documentation/devicetree/bindings/batterydata/batterydata.txt +19 −4 Original line number Diff line number Diff line Loading @@ -120,13 +120,28 @@ Profile data node optional properties: The threshold values in range should be in ascending and shouldn't overlap. It support 8 ranges at max. - qcom,jeita-soft-thresholds: A tuple entry to specify ADC code for battery's soft JEITA threshold. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>. threshold. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>. - qcom,jeita-hard-thresholds: A tuple entry to specify ADC code for battery's hard JEITA threshold. <HARD_COLD_ADC_CODE, HARD_HOT_ADC_CODE>. threshold. <HARD_COLD_ADC_CODE, HARD_HOT_ADC_CODE>. - qcom,jeita-soft-hys-thresholds: A tuple entry to specify ADC code for battery's soft JEITA threshold with hysteresis adjustment. <SOFT_COLD_ADC_CODE, SOFT_HOT_ADC_CODE>. These "hysteresis" values should be specified if "qcom,jeita-soft-thresholds" are specified. Without which SW JEITA compensation won't function properly. - qcom,jeita-soft-fcc-ua: A tuple entry to specify the values of Fast charging current (in uA) that needs to be applied during soft JEITA conditions (cool/warm). Element 0 - FCC value for soft cool. Element 1 - FCC value for soft warm. - qcom,jeita-soft-fv-uv: A tuple entry to specify the values of Float voltage (in uV) that needs to be applied during soft JEITA conditions (cool/warm). Element 0 - FV value for soft cool. Element 1 - FV value for soft warm. - qcom,batt-age-level: Battery age level. This is used only when multiple profile loading is supported. Profile data node required subnodes: - qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes temperature to fcc lookup. The units for this lookup Loading
drivers/power/supply/qcom/battery.c +61 −3 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__ Loading Loading @@ -40,6 +40,7 @@ #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define ICL_LIMIT_VOTER "ICL_LIMIT_VOTER" #define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER" #define FCC_VOTER "FCC_VOTER" struct pl_data { int pl_mode; Loading @@ -57,6 +58,7 @@ struct pl_data { struct votable *hvdcp_hw_inov_dis_votable; struct votable *usb_icl_votable; struct votable *pl_enable_votable_indirect; struct votable *cp_ilim_votable; struct delayed_work status_change_work; struct work_struct pl_disable_forever_work; struct work_struct pl_taper_work; Loading @@ -68,6 +70,7 @@ struct pl_data { struct power_supply *batt_psy; struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply *cp_master_psy; int charge_type; int total_settled_ua; int pl_settled_ua; Loading @@ -85,6 +88,7 @@ struct pl_data { struct wakeup_source *pl_ws; struct notifier_block nb; bool pl_disable; bool cp_disabled; int taper_entry_fv; int main_fcc_max; /* debugfs directory */ Loading Loading @@ -476,6 +480,46 @@ static void get_fcc_split(struct pl_data *chip, int total_ua, } } static void get_main_fcc_config(struct pl_data *chip, int *total_fcc) { union power_supply_propval pval = {0, }; int rc; if (!chip->cp_master_psy) chip->cp_master_psy = power_supply_get_by_name("charge_pump_master"); if (!chip->cp_master_psy) goto out; rc = power_supply_get_property(chip->cp_master_psy, POWER_SUPPLY_PROP_CP_SWITCHER_EN, &pval); if (rc < 0) { pr_err("Couldn't get switcher enable status, rc=%d\n", rc); goto out; } if (!pval.intval) { /* * To honor main charger upper FCC limit, on CP switcher * disable, skip fcc slewing as it will cause delay in limiting * the charge current flowing through main charger. */ if (!chip->cp_disabled) { chip->fcc_stepper_enable = false; pl_dbg(chip, PR_PARALLEL, "Disabling FCC slewing on CP Switcher disable\n"); } chip->cp_disabled = true; } else { chip->cp_disabled = false; pl_dbg(chip, PR_PARALLEL, "CP Switcher is enabled, don't limit main fcc\n"); return; } out: *total_fcc = min(*total_fcc, chip->main_fcc_max); } static void get_fcc_stepper_params(struct pl_data *chip, int main_fcc_ua, int parallel_fcc_ua) { Loading Loading @@ -618,6 +662,9 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, if (!chip->main_psy) return 0; if (!chip->cp_ilim_votable) chip->cp_ilim_votable = find_votable("CP_ILIM"); if (chip->pl_mode != POWER_SUPPLY_PL_NONE) { get_fcc_split(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua); Loading Loading @@ -814,6 +861,10 @@ static void fcc_stepper_work(struct work_struct *work) chip->main_fcc_ua = main_fcc; chip->slave_fcc_ua = parallel_fcc; if (chip->cp_ilim_votable) vote(chip->cp_ilim_votable, FCC_VOTER, true, chip->main_fcc_ua / 2); if (reschedule_ms) { schedule_delayed_work(&chip->fcc_stepper_work, msecs_to_jiffies(reschedule_ms)); Loading Loading @@ -929,6 +980,9 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, false, 0); if (chip->cp_ilim_votable) vote(chip->cp_ilim_votable, ICL_CHANGE_VOTER, true, icl_ua); return 0; } Loading Loading @@ -1161,8 +1215,7 @@ static int pl_disable_vote_callback(struct votable *votable, (slave_fcc_ua * 100) / total_fcc_ua); } else { if (chip->main_fcc_max) total_fcc_ua = min(total_fcc_ua, chip->main_fcc_max); get_main_fcc_config(chip, &total_fcc_ua); if (!chip->fcc_stepper_enable) { if (IS_USBIN(chip->pl_mode)) Loading @@ -1188,6 +1241,10 @@ static int pl_disable_vote_callback(struct votable *votable, return rc; } if (chip->cp_ilim_votable) vote(chip->cp_ilim_votable, FCC_VOTER, true, total_fcc_ua / 2); /* reset parallel FCC */ chip->slave_fcc_ua = 0; chip->total_settled_ua = 0; Loading Loading @@ -1700,6 +1757,7 @@ int qcom_batt_init(int smb_version) } chip->pl_disable = true; chip->cp_disabled = true; chip->qcom_batt_class.name = "qcom-battery", chip->qcom_batt_class.owner = THIS_MODULE, chip->qcom_batt_class.class_groups = batt_class_groups; Loading
drivers/power/supply/qcom/qpnp-smb5.c +9 −20 Original line number Diff line number Diff line Loading @@ -1301,8 +1301,7 @@ static int smb5_batt_get_prop(struct power_supply *psy, QNOVO_VOTER); break; case POWER_SUPPLY_PROP_CURRENT_NOW: rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CURRENT_NOW, val); rc = smblib_get_batt_current_now(chg, val); break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: val->intval = get_client_vote_locked(chg->fcc_votable, Loading Loading @@ -1333,10 +1332,7 @@ static int smb5_batt_get_prop(struct power_supply *psy, val->intval = 0; break; case POWER_SUPPLY_PROP_DIE_HEALTH: if (chg->die_health == -EINVAL) val->intval = smblib_get_prop_die_health(chg); else val->intval = chg->die_health; rc = smblib_get_die_health(chg, val); break; case POWER_SUPPLY_PROP_DP_DM: val->intval = chg->pulse_cnt; Loading Loading @@ -1415,13 +1411,6 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: chg->step_chg_enabled = !!val->intval; break; case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: if (chg->sw_jeita_enabled != (!!val->intval)) { rc = smblib_disable_hw_jeita(chg, !!val->intval); if (rc == 0) chg->sw_jeita_enabled = !!val->intval; } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: chg->batt_profile_fcc_ua = val->intval; vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval); Loading Loading @@ -1472,6 +1461,9 @@ static int smb5_batt_set_prop(struct power_supply *psy, vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER, false, 0); break; case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: chg->fcc_stepper_enable = val->intval; break; default: rc = -EINVAL; } Loading @@ -1492,7 +1484,6 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_RERUN_AICL: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: case POWER_SUPPLY_PROP_DIE_HEALTH: return 1; default: Loading Loading @@ -2152,13 +2143,11 @@ static int smb5_init_hw(struct smb5 *chip) } } if (chg->sw_jeita_enabled) { rc = smblib_disable_hw_jeita(chg, true); if (rc < 0) { dev_err(chg->dev, "Couldn't set hw jeita rc=%d\n", rc); return rc; } } rc = smblib_masked_write(chg, DCDC_ENG_SDCDC_CFG5_REG, ENG_SDCDC_BAT_HPWR_MASK, BOOST_MODE_THRESH_3P6_V); Loading
drivers/power/supply/qcom/smb1390-charger-psy.c +79 −20 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ #define CP_VOTER "CP_VOTER" #define USER_VOTER "USER_VOTER" #define ILIM_VOTER "ILIM_VOTER" #define TAPER_END_VOTER "TAPER_END_VOTER" #define FCC_VOTER "FCC_VOTER" #define ICL_VOTER "ICL_VOTER" #define WIRELESS_VOTER "WIRELESS_VOTER" Loading Loading @@ -122,6 +123,7 @@ struct smb1390 { struct votable *disable_votable; struct votable *ilim_votable; struct votable *fcc_votable; struct votable *fv_votable; struct votable *cp_awake_votable; /* power supplies */ Loading @@ -135,6 +137,8 @@ struct smb1390 { bool taper_work_running; struct smb1390_iio iio; int irq_status; int taper_entry_fv; bool switcher_disabled; }; struct smb_irq { Loading Loading @@ -204,6 +208,19 @@ static bool is_psy_voter_available(struct smb1390 *chip) } } if (!chip->fv_votable) { chip->fv_votable = find_votable("FV"); if (!chip->fv_votable) { pr_debug("Couldn't find FV votable\n"); return false; } } if (!chip->disable_votable) { pr_debug("Couldn't find CP DISABLE votable\n"); return false; } return true; } Loading Loading @@ -247,7 +264,8 @@ static int smb1390_get_cp_en_status(struct smb1390 *chip, int id, bool *enable) static irqreturn_t default_irq_handler(int irq, void *data) { struct smb1390 *chip = data; int i; int i, rc; bool enable; for (i = 0; i < NUM_IRQS; ++i) { if (irq == chip->irqs[i]) { Loading @@ -256,8 +274,18 @@ static irqreturn_t default_irq_handler(int irq, void *data) } } rc = smb1390_get_cp_en_status(chip, SWITCHER_EN, &enable); if (!rc) { if (chip->switcher_disabled == enable) { chip->switcher_disabled = !chip->switcher_disabled; if (chip->fcc_votable) rerun_election(chip->fcc_votable); } } if (chip->cp_master_psy) power_supply_changed(chip->cp_master_psy); return IRQ_HANDLED; } Loading Loading @@ -435,18 +463,20 @@ static int smb1390_ilim_vote_cb(struct votable *votable, void *data, return -EINVAL; } rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG, CFG_ILIM_MASK, DIV_ROUND_CLOSEST(max(ilim_uA, 500000) - 500000, 100000)); if (rc < 0) { pr_err("Failed to write ILIM Register, rc=%d\n", rc); return rc; } /* ILIM less than 1A is not accurate; disable charging */ if (ilim_uA < 1000000) { pr_debug("ILIM %duA is too low to allow charging\n", ilim_uA); vote(chip->disable_votable, ILIM_VOTER, true, 0); } else { pr_debug("setting ILIM to %duA\n", ilim_uA); rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG, CFG_ILIM_MASK, DIV_ROUND_CLOSEST(ilim_uA - 500000, 100000)); if (rc < 0) pr_err("Failed to write ILIM Register, rc=%d\n", rc); if (rc >= 0) pr_debug("ILIM set to %duA\n", ilim_uA); vote(chip->disable_votable, ILIM_VOTER, false, 0); } Loading Loading @@ -544,9 +574,13 @@ static void smb1390_status_change_work(struct work_struct *work) pval.intval); } /* input current is always half the charge current */ vote(chip->ilim_votable, FCC_VOTER, true, get_effective_result(chip->fcc_votable) / 2); /* * Remove SMB1390 Taper condition disable vote if float voltage * increased in comparison to voltage at which it entered taper. */ if (chip->taper_entry_fv < get_effective_result(chip->fv_votable)) vote(chip->disable_votable, TAPER_END_VOTER, false, 0); /* * all votes that would result in disabling the charge pump have Loading Loading @@ -574,6 +608,7 @@ static void smb1390_status_change_work(struct work_struct *work) } } else { vote(chip->disable_votable, SRC_VOTER, true, 0); vote(chip->disable_votable, TAPER_END_VOTER, false, 0); vote(chip->fcc_votable, CP_VOTER, false, 0); } Loading @@ -591,11 +626,8 @@ static void smb1390_taper_work(struct work_struct *work) if (!is_psy_voter_available(chip)) goto out; do { fcc_uA = get_effective_result(chip->fcc_votable) - 100000; pr_debug("taper work reducing FCC to %duA\n", fcc_uA); vote(chip->fcc_votable, CP_VOTER, true, fcc_uA); chip->taper_entry_fv = get_effective_result(chip->fv_votable); while (true) { rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); if (rc < 0) { Loading @@ -603,12 +635,30 @@ static void smb1390_taper_work(struct work_struct *work) goto out; } msleep(500); } while (fcc_uA >= 2000000 && pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER); if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) { fcc_uA = get_effective_result(chip->fcc_votable) - 100000; pr_debug("taper work reducing FCC to %duA\n", fcc_uA); vote(chip->fcc_votable, CP_VOTER, true, fcc_uA); if (fcc_uA < 2000000) { vote(chip->disable_votable, TAPER_END_VOTER, true, 0); goto out; } } else if (get_effective_result(chip->fv_votable) > chip->taper_entry_fv) { pr_debug("Float voltage increased. Exiting taper\n"); goto out; } else { pr_debug("In fast charging. Wait for next taper\n"); } msleep(500); } out: pr_debug("taper work exit\n"); vote(chip->fcc_votable, CP_VOTER, false, 0); chip->taper_work_running = false; } Loading Loading @@ -810,6 +860,14 @@ static int smb1390_create_votables(struct smb1390 *chip) */ vote(chip->disable_votable, USER_VOTER, true, 0); /* * In case SMB1390 probe happens after FCC value has been configured, * update ilim vote to reflect FCC / 2 value. */ if (chip->fcc_votable) vote(chip->ilim_votable, FCC_VOTER, true, get_effective_result(chip->fcc_votable) / 2); return 0; } Loading Loading @@ -926,6 +984,7 @@ static int smb1390_probe(struct platform_device *pdev) chip->dev = &pdev->dev; spin_lock_init(&chip->status_change_lock); mutex_init(&chip->die_chan_lock); chip->switcher_disabled = true; chip->regmap = dev_get_regmap(chip->dev->parent, NULL); if (!chip->regmap) { Loading
drivers/power/supply/qcom/smb5-lib.c +256 −85 Original line number Diff line number Diff line Loading @@ -908,7 +908,7 @@ static int smblib_notifier_call(struct notifier_block *nb, schedule_work(&chg->bms_update_work); } if (!chg->jeita_configured) if (chg->jeita_configured == JEITA_CFG_NONE) schedule_work(&chg->jeita_update_work); if (chg->sec_pl_present && !chg->pl.psy Loading Loading @@ -1873,6 +1873,19 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, return 0; } int smblib_get_batt_current_now(struct smb_charger *chg, union power_supply_propval *val) { int rc; rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CURRENT_NOW, val); if (!rc) val->intval *= (-1); return rc; } /*********************** * BATTERY PSY SETTERS * ***********************/ Loading Loading @@ -2263,6 +2276,7 @@ int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) rc); return rc; } return 0; } Loading Loading @@ -2343,19 +2357,28 @@ static int smblib_update_thermal_readings(struct smb_charger *chg) } if (chg->sec_chg_selected == POWER_SUPPLY_CHARGER_SEC_CP) { rc = smblib_read_iio_channel(chg, chg->iio.smb_temp_chan, DIV_FACTOR_DECIDEGC, &chg->smb_temp); if (rc < 0) { smblib_err(chg, "Couldn't read SMB TEMP channel, rc=%d\n", if (!chg->cp_psy) chg->cp_psy = power_supply_get_by_name("charge_pump_master"); if (chg->cp_psy) { rc = power_supply_get_property(chg->cp_psy, POWER_SUPPLY_PROP_CP_DIE_TEMP, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get smb1390 charger temp, rc=%d\n", rc); return rc; } chg->smb_temp = pval.intval; } else { smblib_dbg(chg, PR_MISC, "Coudln't find cp_psy\n"); chg->smb_temp = -ENODATA; } } else if (chg->pl.psy && chg->sec_chg_selected == POWER_SUPPLY_CHARGER_SEC_PL) { rc = power_supply_get_property(chg->pl.psy, POWER_SUPPLY_PROP_CHARGER_TEMP, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get smb charger temp, rc=%d\n", smblib_err(chg, "Couldn't get smb1355 charger temp, rc=%d\n", rc); return rc; } Loading Loading @@ -3262,6 +3285,17 @@ int smblib_get_prop_die_health(struct smb_charger *chg) return POWER_SUPPLY_HEALTH_COOL; } int smblib_get_die_health(struct smb_charger *chg, union power_supply_propval *val) { if (chg->die_health == -EINVAL) val->intval = smblib_get_prop_die_health(chg); else val->intval = chg->die_health; return 0; } int smblib_get_prop_connector_health(struct smb_charger *chg) { int rc; Loading Loading @@ -3673,49 +3707,153 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, return rc; } static int smblib_recover_from_soft_jeita(struct smb_charger *chg) #define JEITA_SOFT 0 #define JEITA_HARD 1 static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, int type) { u8 stat1, stat7; int rc; u16 temp, base; rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat1); base = CHGR_JEITA_THRESHOLD_BASE_REG(type); temp = thresholds[1] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); smblib_err(chg, "Couldn't configure Jeita %s hot threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat7); temp = thresholds[0] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", rc); smblib_err(chg, "Couldn't configure Jeita %s cold threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } if ((chg->jeita_status && !(stat7 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK) && ((stat1 & BATTERY_CHARGER_STATUS_MASK) == TERMINATE_CHARGE))) { /* * We are moving from JEITA soft -> Normal and charging * is terminated */ rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable charging rc=%d\n", rc); smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n", (type == JEITA_SOFT) ? "Soft" : "Hard"); return 0; } static int smblib_charge_inhibit_en(struct smb_charger *chg, bool enable) { int rc; rc = smblib_masked_write(chg, CHGR_CFG2_REG, CHARGER_INHIBIT_BIT, enable ? CHARGER_INHIBIT_BIT : 0); return rc; } rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, CHARGING_ENABLE_CMD_BIT); static int smblib_soft_jeita_arb_wa(struct smb_charger *chg) { union power_supply_propval pval; int rc = 0; bool soft_jeita; rc = smblib_get_prop_batt_health(chg, &pval); if (rc < 0) { smblib_err(chg, "Couldn't enable charging rc=%d\n", rc); smblib_err(chg, "Couldn't get battery health rc=%d\n", rc); return rc; } } chg->jeita_status = stat7 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK; /* Do nothing on entering hard JEITA condition */ if (pval.intval == POWER_SUPPLY_HEALTH_COLD || pval.intval == POWER_SUPPLY_HEALTH_HOT) return 0; if (chg->jeita_soft_fcc[0] < 0 || chg->jeita_soft_fcc[1] < 0 || chg->jeita_soft_fv[0] < 0 || chg->jeita_soft_fv[1] < 0) return 0; soft_jeita = (pval.intval == POWER_SUPPLY_HEALTH_COOL) || (pval.intval == POWER_SUPPLY_HEALTH_WARM); /* Do nothing on entering soft JEITA from hard JEITA */ if (chg->jeita_arb_flag && soft_jeita) return 0; /* Do nothing, initial to health condition */ if (!chg->jeita_arb_flag && !soft_jeita) return 0; if (!chg->cp_disable_votable) chg->cp_disable_votable = find_votable("CP_DISABLE"); /* Entering soft JEITA from normal state */ if (!chg->jeita_arb_flag && soft_jeita) { vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0); /* Disable parallel charging */ if (chg->pl_disable_votable) vote(chg->pl_disable_votable, JEITA_ARB_VOTER, true, 0); if (chg->cp_disable_votable) vote(chg->cp_disable_votable, JEITA_ARB_VOTER, true, 0); rc = smblib_charge_inhibit_en(chg, true); if (rc < 0) smblib_err(chg, "Couldn't enable charge inhibit rc=%d\n", rc); rc = smblib_update_jeita(chg, chg->jeita_soft_hys_thlds, JEITA_SOFT); if (rc < 0) smblib_err(chg, "Couldn't configure Jeita soft threshold rc=%d\n", rc); if (pval.intval == POWER_SUPPLY_HEALTH_COOL) { vote(chg->fcc_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fcc[0]); vote(chg->fv_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fv[0]); } else { vote(chg->fcc_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fcc[1]); vote(chg->fv_votable, JEITA_ARB_VOTER, true, chg->jeita_soft_fv[1]); } vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0); chg->jeita_arb_flag = true; } else if (chg->jeita_arb_flag && !soft_jeita) { /* Exit to health state from soft JEITA */ vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0); rc = smblib_charge_inhibit_en(chg, false); if (rc < 0) smblib_err(chg, "Couldn't disable charge inhibit rc=%d\n", rc); rc = smblib_update_jeita(chg, chg->jeita_soft_thlds, JEITA_SOFT); if (rc < 0) smblib_err(chg, "Couldn't configure Jeita soft threshold rc=%d\n", rc); vote(chg->fcc_votable, JEITA_ARB_VOTER, false, 0); vote(chg->fv_votable, JEITA_ARB_VOTER, false, 0); if (chg->pl_disable_votable) vote(chg->pl_disable_votable, JEITA_ARB_VOTER, false, 0); if (chg->cp_disable_votable) vote(chg->cp_disable_votable, JEITA_ARB_VOTER, false, 0); vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0); chg->jeita_arb_flag = false; } smblib_dbg(chg, PR_MISC, "JEITA ARB status %d, soft JEITA status %d\n", chg->jeita_arb_flag, soft_jeita); return rc; } /************************ Loading Loading @@ -3873,15 +4011,18 @@ irqreturn_t batt_temp_changed_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc; rc = smblib_recover_from_soft_jeita(chg); smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if (chg->jeita_configured != JEITA_CFG_COMPLETE) return IRQ_HANDLED; rc = smblib_soft_jeita_arb_wa(chg); if (rc < 0) { smblib_err(chg, "Couldn't recover chg from soft jeita rc=%d\n", smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n", rc); return IRQ_HANDLED; } rerun_election(chg->fcc_votable); power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } Loading Loading @@ -4959,7 +5100,7 @@ irqreturn_t wdog_bark_irq_handler(int irq, void *data) if (rc < 0) smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc); if (chg->step_chg_enabled || chg->sw_jeita_enabled) if (chg->step_chg_enabled) power_supply_changed(chg->batt_psy); return IRQ_HANDLED; Loading Loading @@ -5173,42 +5314,6 @@ static void smblib_thermal_regulation_work(struct work_struct *work) rc); } #define JEITA_SOFT 0 #define JEITA_HARD 1 static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, int type) { int rc; u16 temp, base; base = CHGR_JEITA_THRESHOLD_BASE_REG(type); temp = thresholds[1] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't configure Jeita %s hot threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } temp = thresholds[0] & 0xFFFF; temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2); if (rc < 0) { smblib_err(chg, "Couldn't configure Jeita %s cold threshold rc=%d\n", (type == JEITA_SOFT) ? "Soft" : "Hard", rc); return rc; } smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n", (type == JEITA_SOFT) ? "Soft" : "Hard"); return 0; } static void jeita_update_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, Loading @@ -5216,8 +5321,8 @@ static void jeita_update_work(struct work_struct *work) struct device_node *node = chg->dev->of_node; struct device_node *batt_node, *pnode; union power_supply_propval val; int rc; u32 jeita_thresholds[2]; int rc, tmp[2], max_fcc_ma, max_fv_uv; u32 jeita_hard_thresholds[2]; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { Loading Loading @@ -5250,9 +5355,10 @@ static void jeita_update_work(struct work_struct *work) } rc = of_property_read_u32_array(pnode, "qcom,jeita-hard-thresholds", jeita_thresholds, 2); jeita_hard_thresholds, 2); if (!rc) { rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_HARD); rc = smblib_update_jeita(chg, jeita_hard_thresholds, JEITA_HARD); if (rc < 0) { smblib_err(chg, "Couldn't configure Hard Jeita rc=%d\n", rc); Loading @@ -5261,18 +5367,83 @@ static void jeita_update_work(struct work_struct *work) } rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-thresholds", jeita_thresholds, 2); chg->jeita_soft_thlds, 2); if (!rc) { rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_SOFT); rc = smblib_update_jeita(chg, chg->jeita_soft_thlds, JEITA_SOFT); if (rc < 0) { smblib_err(chg, "Couldn't configure Soft Jeita rc=%d\n", rc); goto out; } rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-hys-thresholds", chg->jeita_soft_hys_thlds, 2); if (rc < 0) { smblib_err(chg, "Couldn't get Soft Jeita hysteresis thresholds rc=%d\n", rc); goto out; } } chg->jeita_soft_fcc[0] = chg->jeita_soft_fcc[1] = -EINVAL; chg->jeita_soft_fv[0] = chg->jeita_soft_fv[1] = -EINVAL; max_fcc_ma = max_fv_uv = -EINVAL; of_property_read_u32(pnode, "qcom,fastchg-current-ma", &max_fcc_ma); of_property_read_u32(pnode, "qcom,max-voltage-uv", &max_fv_uv); if (max_fcc_ma <= 0 || max_fv_uv <= 0) { smblib_err(chg, "Incorrect fastchg-current-ma or max-voltage-uv\n"); goto out; } rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fcc-ua", tmp, 2); if (rc < 0) { smblib_err(chg, "Couldn't get fcc values for soft JEITA rc=%d\n", rc); goto out; } max_fcc_ma *= 1000; if (tmp[0] > max_fcc_ma || tmp[1] > max_fcc_ma) { smblib_err(chg, "Incorrect FCC value [%d %d] max: %d\n", tmp[0], tmp[1], max_fcc_ma); goto out; } chg->jeita_soft_fcc[0] = tmp[0]; chg->jeita_soft_fcc[1] = tmp[1]; rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fv-uv", tmp, 2); if (rc < 0) { smblib_err(chg, "Couldn't get fv values for soft JEITA rc=%d\n", rc); goto out; } if (tmp[0] > max_fv_uv || tmp[1] > max_fv_uv) { smblib_err(chg, "Incorrect FV value [%d %d] max: %d\n", tmp[0], tmp[1], max_fv_uv); goto out; } chg->jeita_soft_fv[0] = tmp[0]; chg->jeita_soft_fv[1] = tmp[1]; rc = smblib_soft_jeita_arb_wa(chg); if (rc < 0) { smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n", rc); goto out; } chg->jeita_configured = JEITA_CFG_COMPLETE; return; out: chg->jeita_configured = true; chg->jeita_configured = JEITA_CFG_FAILURE; } static void smblib_lpd_ra_open_work(struct work_struct *work) Loading Loading @@ -5530,7 +5701,7 @@ int smblib_init(struct smb_charger *chg) } rc = qcom_step_chg_init(chg->dev, chg->step_chg_enabled, chg->sw_jeita_enabled); chg->sw_jeita_enabled, false); if (rc < 0) { smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n", rc); Loading