Loading Documentation/devicetree/bindings/power/supply/qcom/smb1351-charger.txt +2 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,8 @@ Optional Properties: via pin in a parallel-charger configuration. 0 - Active low and 1 - Active high. If not specified the default value is active-low. - qcom,parallel-external-current-sense If present specifies external rsense is used for charge current sensing. Example for standalone charger: Loading drivers/power/supply/qcom/battery.c +40 −8 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ struct pl_data { struct votable *fv_votable; struct votable *pl_disable_votable; struct votable *pl_awake_votable; struct votable *hvdcp_hw_inov_dis_votable; struct work_struct status_change_work; struct work_struct pl_disable_forever_work; struct delayed_work pl_taper_work; Loading Loading @@ -96,7 +97,8 @@ static void split_settled(struct pl_data *chip) * for desired split */ if (chip->pl_mode != POWER_SUPPLY_PARALLEL_USBIN_USBIN) if ((chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN) && (chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN_EXT)) return; if (!chip->main_psy) Loading Loading @@ -262,7 +264,7 @@ static void split_fcc(struct pl_data *chip, int total_ua, hw_cc_delta_ua = pval.intval; bcl_ua = INT_MAX; if (chip->pl_mode == POWER_SUPPLY_PARALLEL_MID_MID) { if (chip->pl_mode == POWER_SUPPLY_PL_USBMID_USBMID) { rc = power_supply_get_property(chip->main_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval); if (rc < 0) { Loading @@ -287,6 +289,14 @@ static void split_fcc(struct pl_data *chip, int total_ua, slave_limited_ua = min(effective_total_ua, bcl_ua); *slave_ua = (slave_limited_ua * chip->slave_pct) / 100; *slave_ua = (*slave_ua * chip->taper_pct) / 100; /* * In USBIN_USBIN configuration with internal rsense parallel * charger's current goes through main charger's BATFET, keep * the main charger's FCC to the votable result. */ if (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) *master_ua = max(0, total_ua); else *master_ua = max(0, total_ua - *slave_ua); } Loading Loading @@ -316,7 +326,7 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, total_fcc_ua = pval.intval; } if (chip->pl_mode == POWER_SUPPLY_PARALLEL_NONE if (chip->pl_mode == POWER_SUPPLY_PL_NONE || get_effective_result_locked(chip->pl_disable_votable)) { pval.intval = total_fcc_ua; rc = power_supply_set_property(chip->main_psy, Loading Loading @@ -391,7 +401,7 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, return rc; } if (chip->pl_mode != POWER_SUPPLY_PARALLEL_NONE) { if (chip->pl_mode != POWER_SUPPLY_PL_NONE) { pval.intval += PARALLEL_FLOAT_VOLTAGE_DELTA_UV; rc = power_supply_set_property(chip->pl_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); Loading @@ -411,6 +421,10 @@ static void pl_disable_forever_work(struct work_struct *work) /* Disable Parallel charger forever */ vote(chip->pl_disable_votable, PL_HW_ABSENT_VOTER, true, 0); /* Re-enable autonomous mode */ if (chip->hvdcp_hw_inov_dis_votable) vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, false, 0); } static int pl_disable_vote_callback(struct votable *votable, Loading Loading @@ -451,7 +465,8 @@ static int pl_disable_vote_callback(struct votable *votable, pr_err("Couldn't change slave suspend state rc=%d\n", rc); if (chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) split_settled(chip); /* * we could have been enabled while in taper mode, Loading @@ -469,7 +484,8 @@ static int pl_disable_vote_callback(struct votable *votable, } } } else { if (chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) split_settled(chip); /* pl_psy may be NULL while in the disable branch */ Loading Loading @@ -552,6 +568,21 @@ static bool is_parallel_available(struct pl_data *chip) * pl_psy is present and valid. */ chip->pl_mode = pval.intval; /* Disable autonomous votage increments for USBIN-USBIN */ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { if (!chip->hvdcp_hw_inov_dis_votable) chip->hvdcp_hw_inov_dis_votable = find_votable("HVDCP_HW_INOV_DIS"); if (chip->hvdcp_hw_inov_dis_votable) /* Read current pulse count */ vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, true, 0); else return false; } vote(chip->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0); return true; Loading Loading @@ -610,7 +641,8 @@ static void handle_settled_aicl_split(struct pl_data *chip) int rc; if (!get_effective_result(chip->pl_disable_votable) && chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) { && (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN || chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { /* * call aicl split only when USBIN_USBIN and enabled * and if aicl changed Loading drivers/power/supply/qcom/qpnp-smb2.c +16 −0 Original line number Diff line number Diff line Loading @@ -848,6 +848,8 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_PARALLEL_DISABLE, POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_DP_DM, }; static int smb2_batt_get_prop(struct power_supply *psy, Loading Loading @@ -933,6 +935,12 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_DIE_HEALTH: rc = smblib_get_prop_die_health(chg, val); break; case POWER_SUPPLY_PROP_DP_DM: val->intval = chg->pulse_cnt; break; case POWER_SUPPLY_PROP_RERUN_AICL: val->intval = 0; break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; Loading Loading @@ -986,6 +994,12 @@ static int smb2_batt_set_prop(struct power_supply *psy, break; rc = smblib_set_prop_ship_mode(chg, val); break; case POWER_SUPPLY_PROP_RERUN_AICL: rc = smblib_rerun_aicl(chg); break; case POWER_SUPPLY_PROP_DP_DM: rc = smblib_dp_dm(chg, val->intval); break; default: rc = -EINVAL; } Loading @@ -1001,6 +1015,8 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_PARALLEL_DISABLE: case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: return 1; default: break; Loading drivers/power/supply/qcom/smb-lib.c +227 −17 Original line number Diff line number Diff line Loading @@ -58,6 +58,12 @@ int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) return rc; } int smblib_multibyte_read(struct smb_charger *chg, u16 addr, u8 *val, int count) { return regmap_bulk_read(chg->regmap, addr, val, count); } int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val) { int rc = 0; Loading Loading @@ -652,6 +658,8 @@ static void smblib_uusb_removal(struct smb_charger *chg) chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; chg->usb_icl_delta_ua = 0; chg->pulse_cnt = 0; /* clear USB ICL vote for USB_PSY_VOTER */ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); Loading Loading @@ -741,6 +749,40 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) return 0; } static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) { int rc; u8 val[2]; switch (chg->smb_version) { case PMI8998_SUBTYPE: rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, val); if (rc) { pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n", rc); return rc; } *count = val[0] & QC_PULSE_COUNT_MASK; break; case PM660_SUBTYPE: rc = smblib_multibyte_read(chg, QC_PULSE_COUNT_STATUS_1_REG, val, 2); if (rc) { pr_err("failed to read QC_PULSE_COUNT_STATUS_1_REG rc=%d\n", rc); return rc; } *count = (val[1] << 8) | val[0]; break; default: smblib_dbg(chg, PR_PARALLEL, "unknown SMB chip %d\n", chg->smb_version); return -EINVAL; } return 0; } /********************* * VOTABLE CALLBACKS * *********************/ Loading Loading @@ -975,8 +1017,10 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, { struct smb_charger *chg = data; int rc; u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_EN_BIT; u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT; /* vote to enable/disable HW autonomous INOV */ vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0); /* * Disable the autonomous bit and auth bit for disabling hvdcp. Loading @@ -987,9 +1031,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, val = HVDCP_EN_BIT; rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, HVDCP_EN_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_AUTH_ALG_EN_CFG_BIT, HVDCP_EN_BIT | HVDCP_AUTH_ALG_EN_CFG_BIT, val); if (rc < 0) { smblib_err(chg, "Couldn't %s hvdcp rc=%d\n", Loading Loading @@ -1058,6 +1100,37 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable, return 0; } static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable, void *data, int disable, const char *client) { struct smb_charger *chg = data; int rc; if (disable) { /* * the pulse count register get zeroed when autonomous mode is * disabled. Track that in variables before disabling */ rc = smblib_get_pulse_cnt(chg, &chg->pulse_cnt); if (rc < 0) { pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n", rc); return rc; } } rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT, disable ? 0 : HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT); if (rc < 0) { smblib_err(chg, "Couldn't %s hvdcp rc=%d\n", disable ? "disable" : "enable", rc); return rc; } return rc; } /******************* * VCONN REGULATOR * * *****************/ Loading Loading @@ -1645,6 +1718,98 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, return 0; } int smblib_rerun_aicl(struct smb_charger *chg) { int rc = 0; u8 val; /* * Use restart_AICL instead of trigger_AICL as it runs the * complete AICL instead of starting from the last settled value. * * 8998 only supports trigger_AICL return error for 8998 */ switch (chg->smb_version) { case PMI8998_SUBTYPE: smblib_dbg(chg, PR_PARALLEL, "AICL rerun not supported\n"); return -EINVAL; case PM660_SUBTYPE: val = RESTART_AICL_BIT; break; default: smblib_dbg(chg, PR_PARALLEL, "unknown SMB chip %d\n", chg->smb_version); return -EINVAL; } rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val); if (rc < 0) smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", rc); return rc; } static int smblib_dp_pulse(struct smb_charger *chg) { int rc; /* QC 3.0 increment */ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_INCREMENT_BIT, SINGLE_INCREMENT_BIT); if (rc < 0) smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", rc); return rc; } static int smblib_dm_pulse(struct smb_charger *chg) { int rc; /* QC 3.0 decrement */ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_DECREMENT_BIT, SINGLE_DECREMENT_BIT); if (rc < 0) smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", rc); return rc; } int smblib_dp_dm(struct smb_charger *chg, int val) { int target_icl_ua, rc = 0; switch (val) { case POWER_SUPPLY_DP_DM_DP_PULSE: rc = smblib_dp_pulse(chg); if (!rc) chg->pulse_cnt++; smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); break; case POWER_SUPPLY_DP_DM_DM_PULSE: rc = smblib_dm_pulse(chg); if (!rc && chg->pulse_cnt) chg->pulse_cnt--; smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); break; case POWER_SUPPLY_DP_DM_ICL_DOWN: chg->usb_icl_delta_ua -= 100000; target_icl_ua = get_effective_result(chg->usb_icl_votable); vote(chg->usb_icl_votable, SW_QC3_VOTER, true, target_icl_ua + chg->usb_icl_delta_ua); break; case POWER_SUPPLY_DP_DM_ICL_UP: default: break; } return rc; } /******************* * DC PSY GETTERS * *******************/ Loading Loading @@ -2915,26 +3080,38 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } #define USB_WEAK_INPUT_UA 1400000 #define ICL_CHANGE_DELAY_MS 1000 irqreturn_t smblib_handle_icl_change(int irq, void *data) { u8 stat; int rc, settled_ua, delay = ICL_CHANGE_DELAY_MS; struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; int rc, settled_ua; rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua); if (chg->mode == PARALLEL_MASTER) { rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc); return IRQ_HANDLED; } rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua); if (rc < 0) { smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc); return IRQ_HANDLED; } if (chg->mode == PARALLEL_MASTER) { power_supply_changed(chg->usb_main_psy); vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, settled_ua >= USB_WEAK_INPUT_UA, 0); /* If AICL settled then schedule work now */ if ((settled_ua == get_effective_result(chg->usb_icl_votable)) || (stat & AICL_DONE_BIT)) delay = 0; schedule_delayed_work(&chg->icl_change_work, msecs_to_jiffies(delay)); } smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s icl_settled=%d\n", irq_data->name, settled_ua); return IRQ_HANDLED; } Loading Loading @@ -3279,6 +3456,8 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->vconn_attempts = 0; chg->otg_attempts = 0; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; chg->usb_ever_removed = true; Loading Loading @@ -3801,6 +3980,25 @@ static void smblib_otg_ss_done_work(struct work_struct *work) mutex_unlock(&chg->otg_oc_lock); } static void smblib_icl_change_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, icl_change_work.work); int rc, settled_ua; rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua); if (rc < 0) { smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc); return; } power_supply_changed(chg->usb_main_psy); vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, settled_ua >= USB_WEAK_INPUT_UA, 0); smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua); } static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; Loading Loading @@ -3917,6 +4115,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } chg->hvdcp_hw_inov_dis_votable = create_votable("HVDCP_HW_INOV_DIS", VOTE_SET_ANY, smblib_hvdcp_hw_inov_dis_vote_callback, chg); if (IS_ERR(chg->hvdcp_hw_inov_dis_votable)) { rc = PTR_ERR(chg->hvdcp_hw_inov_dis_votable); return rc; } return rc; } Loading @@ -3940,6 +4147,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->pl_enable_votable_indirect); if (chg->apsd_disable_votable) destroy_votable(chg->apsd_disable_votable); if (chg->hvdcp_hw_inov_dis_votable) destroy_votable(chg->hvdcp_hw_inov_dis_votable); } static void smblib_iio_deinit(struct smb_charger *chg) Loading Loading @@ -3970,6 +4179,7 @@ int smblib_init(struct smb_charger *chg) INIT_WORK(&chg->otg_oc_work, smblib_otg_oc_work); INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work); INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); chg->fake_capacity = -EINVAL; switch (chg->mode) { Loading drivers/power/supply/qcom/smb-lib.h +7 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ enum print_reason { #define PD_SUSPEND_SUPPORTED_VOTER "PD_SUSPEND_SUPPORTED_VOTER" #define PL_DELAY_HVDCP_VOTER "PL_DELAY_HVDCP_VOTER" #define CTM_VOTER "CTM_VOTER" #define SW_QC3_VOTER "SW_QC3_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 Loading Loading @@ -267,6 +268,7 @@ struct smb_charger { struct votable *hvdcp_disable_votable_indirect; struct votable *hvdcp_enable_votable; struct votable *apsd_disable_votable; struct votable *hvdcp_hw_inov_dis_votable; /* work */ struct work_struct bms_update_work; Loading @@ -278,6 +280,7 @@ struct smb_charger { struct work_struct otg_oc_work; struct work_struct vconn_oc_work; struct delayed_work otg_ss_done_work; struct delayed_work icl_change_work; /* cached status */ int voltage_min_uv; Loading Loading @@ -315,6 +318,8 @@ struct smb_charger { /* qnovo */ int qnovo_fcc_ua; int qnovo_fv_uv; int usb_icl_delta_ua; int pulse_cnt; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); Loading Loading @@ -472,6 +477,8 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg, union power_supply_propval *val); int smblib_icl_override(struct smb_charger *chg, bool override); int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua); int smblib_dp_dm(struct smb_charger *chg, int val); int smblib_rerun_aicl(struct smb_charger *chg); int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/smb1351-charger.txt +2 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,8 @@ Optional Properties: via pin in a parallel-charger configuration. 0 - Active low and 1 - Active high. If not specified the default value is active-low. - qcom,parallel-external-current-sense If present specifies external rsense is used for charge current sensing. Example for standalone charger: Loading
drivers/power/supply/qcom/battery.c +40 −8 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ struct pl_data { struct votable *fv_votable; struct votable *pl_disable_votable; struct votable *pl_awake_votable; struct votable *hvdcp_hw_inov_dis_votable; struct work_struct status_change_work; struct work_struct pl_disable_forever_work; struct delayed_work pl_taper_work; Loading Loading @@ -96,7 +97,8 @@ static void split_settled(struct pl_data *chip) * for desired split */ if (chip->pl_mode != POWER_SUPPLY_PARALLEL_USBIN_USBIN) if ((chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN) && (chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN_EXT)) return; if (!chip->main_psy) Loading Loading @@ -262,7 +264,7 @@ static void split_fcc(struct pl_data *chip, int total_ua, hw_cc_delta_ua = pval.intval; bcl_ua = INT_MAX; if (chip->pl_mode == POWER_SUPPLY_PARALLEL_MID_MID) { if (chip->pl_mode == POWER_SUPPLY_PL_USBMID_USBMID) { rc = power_supply_get_property(chip->main_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval); if (rc < 0) { Loading @@ -287,6 +289,14 @@ static void split_fcc(struct pl_data *chip, int total_ua, slave_limited_ua = min(effective_total_ua, bcl_ua); *slave_ua = (slave_limited_ua * chip->slave_pct) / 100; *slave_ua = (*slave_ua * chip->taper_pct) / 100; /* * In USBIN_USBIN configuration with internal rsense parallel * charger's current goes through main charger's BATFET, keep * the main charger's FCC to the votable result. */ if (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) *master_ua = max(0, total_ua); else *master_ua = max(0, total_ua - *slave_ua); } Loading Loading @@ -316,7 +326,7 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, total_fcc_ua = pval.intval; } if (chip->pl_mode == POWER_SUPPLY_PARALLEL_NONE if (chip->pl_mode == POWER_SUPPLY_PL_NONE || get_effective_result_locked(chip->pl_disable_votable)) { pval.intval = total_fcc_ua; rc = power_supply_set_property(chip->main_psy, Loading Loading @@ -391,7 +401,7 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, return rc; } if (chip->pl_mode != POWER_SUPPLY_PARALLEL_NONE) { if (chip->pl_mode != POWER_SUPPLY_PL_NONE) { pval.intval += PARALLEL_FLOAT_VOLTAGE_DELTA_UV; rc = power_supply_set_property(chip->pl_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); Loading @@ -411,6 +421,10 @@ static void pl_disable_forever_work(struct work_struct *work) /* Disable Parallel charger forever */ vote(chip->pl_disable_votable, PL_HW_ABSENT_VOTER, true, 0); /* Re-enable autonomous mode */ if (chip->hvdcp_hw_inov_dis_votable) vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, false, 0); } static int pl_disable_vote_callback(struct votable *votable, Loading Loading @@ -451,7 +465,8 @@ static int pl_disable_vote_callback(struct votable *votable, pr_err("Couldn't change slave suspend state rc=%d\n", rc); if (chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) split_settled(chip); /* * we could have been enabled while in taper mode, Loading @@ -469,7 +484,8 @@ static int pl_disable_vote_callback(struct votable *votable, } } } else { if (chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) split_settled(chip); /* pl_psy may be NULL while in the disable branch */ Loading Loading @@ -552,6 +568,21 @@ static bool is_parallel_available(struct pl_data *chip) * pl_psy is present and valid. */ chip->pl_mode = pval.intval; /* Disable autonomous votage increments for USBIN-USBIN */ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { if (!chip->hvdcp_hw_inov_dis_votable) chip->hvdcp_hw_inov_dis_votable = find_votable("HVDCP_HW_INOV_DIS"); if (chip->hvdcp_hw_inov_dis_votable) /* Read current pulse count */ vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, true, 0); else return false; } vote(chip->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0); return true; Loading Loading @@ -610,7 +641,8 @@ static void handle_settled_aicl_split(struct pl_data *chip) int rc; if (!get_effective_result(chip->pl_disable_votable) && chip->pl_mode == POWER_SUPPLY_PARALLEL_USBIN_USBIN) { && (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN || chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { /* * call aicl split only when USBIN_USBIN and enabled * and if aicl changed Loading
drivers/power/supply/qcom/qpnp-smb2.c +16 −0 Original line number Diff line number Diff line Loading @@ -848,6 +848,8 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_PARALLEL_DISABLE, POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_DP_DM, }; static int smb2_batt_get_prop(struct power_supply *psy, Loading Loading @@ -933,6 +935,12 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_DIE_HEALTH: rc = smblib_get_prop_die_health(chg, val); break; case POWER_SUPPLY_PROP_DP_DM: val->intval = chg->pulse_cnt; break; case POWER_SUPPLY_PROP_RERUN_AICL: val->intval = 0; break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; Loading Loading @@ -986,6 +994,12 @@ static int smb2_batt_set_prop(struct power_supply *psy, break; rc = smblib_set_prop_ship_mode(chg, val); break; case POWER_SUPPLY_PROP_RERUN_AICL: rc = smblib_rerun_aicl(chg); break; case POWER_SUPPLY_PROP_DP_DM: rc = smblib_dp_dm(chg, val->intval); break; default: rc = -EINVAL; } Loading @@ -1001,6 +1015,8 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_PARALLEL_DISABLE: case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: return 1; default: break; Loading
drivers/power/supply/qcom/smb-lib.c +227 −17 Original line number Diff line number Diff line Loading @@ -58,6 +58,12 @@ int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) return rc; } int smblib_multibyte_read(struct smb_charger *chg, u16 addr, u8 *val, int count) { return regmap_bulk_read(chg->regmap, addr, val, count); } int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val) { int rc = 0; Loading Loading @@ -652,6 +658,8 @@ static void smblib_uusb_removal(struct smb_charger *chg) chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; chg->usb_icl_delta_ua = 0; chg->pulse_cnt = 0; /* clear USB ICL vote for USB_PSY_VOTER */ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); Loading Loading @@ -741,6 +749,40 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) return 0; } static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) { int rc; u8 val[2]; switch (chg->smb_version) { case PMI8998_SUBTYPE: rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, val); if (rc) { pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n", rc); return rc; } *count = val[0] & QC_PULSE_COUNT_MASK; break; case PM660_SUBTYPE: rc = smblib_multibyte_read(chg, QC_PULSE_COUNT_STATUS_1_REG, val, 2); if (rc) { pr_err("failed to read QC_PULSE_COUNT_STATUS_1_REG rc=%d\n", rc); return rc; } *count = (val[1] << 8) | val[0]; break; default: smblib_dbg(chg, PR_PARALLEL, "unknown SMB chip %d\n", chg->smb_version); return -EINVAL; } return 0; } /********************* * VOTABLE CALLBACKS * *********************/ Loading Loading @@ -975,8 +1017,10 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, { struct smb_charger *chg = data; int rc; u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_EN_BIT; u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT; /* vote to enable/disable HW autonomous INOV */ vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0); /* * Disable the autonomous bit and auth bit for disabling hvdcp. Loading @@ -987,9 +1031,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, val = HVDCP_EN_BIT; rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, HVDCP_EN_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_AUTH_ALG_EN_CFG_BIT, HVDCP_EN_BIT | HVDCP_AUTH_ALG_EN_CFG_BIT, val); if (rc < 0) { smblib_err(chg, "Couldn't %s hvdcp rc=%d\n", Loading Loading @@ -1058,6 +1100,37 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable, return 0; } static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable, void *data, int disable, const char *client) { struct smb_charger *chg = data; int rc; if (disable) { /* * the pulse count register get zeroed when autonomous mode is * disabled. Track that in variables before disabling */ rc = smblib_get_pulse_cnt(chg, &chg->pulse_cnt); if (rc < 0) { pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n", rc); return rc; } } rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT, disable ? 0 : HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT); if (rc < 0) { smblib_err(chg, "Couldn't %s hvdcp rc=%d\n", disable ? "disable" : "enable", rc); return rc; } return rc; } /******************* * VCONN REGULATOR * * *****************/ Loading Loading @@ -1645,6 +1718,98 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, return 0; } int smblib_rerun_aicl(struct smb_charger *chg) { int rc = 0; u8 val; /* * Use restart_AICL instead of trigger_AICL as it runs the * complete AICL instead of starting from the last settled value. * * 8998 only supports trigger_AICL return error for 8998 */ switch (chg->smb_version) { case PMI8998_SUBTYPE: smblib_dbg(chg, PR_PARALLEL, "AICL rerun not supported\n"); return -EINVAL; case PM660_SUBTYPE: val = RESTART_AICL_BIT; break; default: smblib_dbg(chg, PR_PARALLEL, "unknown SMB chip %d\n", chg->smb_version); return -EINVAL; } rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val); if (rc < 0) smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", rc); return rc; } static int smblib_dp_pulse(struct smb_charger *chg) { int rc; /* QC 3.0 increment */ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_INCREMENT_BIT, SINGLE_INCREMENT_BIT); if (rc < 0) smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", rc); return rc; } static int smblib_dm_pulse(struct smb_charger *chg) { int rc; /* QC 3.0 decrement */ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_DECREMENT_BIT, SINGLE_DECREMENT_BIT); if (rc < 0) smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", rc); return rc; } int smblib_dp_dm(struct smb_charger *chg, int val) { int target_icl_ua, rc = 0; switch (val) { case POWER_SUPPLY_DP_DM_DP_PULSE: rc = smblib_dp_pulse(chg); if (!rc) chg->pulse_cnt++; smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); break; case POWER_SUPPLY_DP_DM_DM_PULSE: rc = smblib_dm_pulse(chg); if (!rc && chg->pulse_cnt) chg->pulse_cnt--; smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); break; case POWER_SUPPLY_DP_DM_ICL_DOWN: chg->usb_icl_delta_ua -= 100000; target_icl_ua = get_effective_result(chg->usb_icl_votable); vote(chg->usb_icl_votable, SW_QC3_VOTER, true, target_icl_ua + chg->usb_icl_delta_ua); break; case POWER_SUPPLY_DP_DM_ICL_UP: default: break; } return rc; } /******************* * DC PSY GETTERS * *******************/ Loading Loading @@ -2915,26 +3080,38 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } #define USB_WEAK_INPUT_UA 1400000 #define ICL_CHANGE_DELAY_MS 1000 irqreturn_t smblib_handle_icl_change(int irq, void *data) { u8 stat; int rc, settled_ua, delay = ICL_CHANGE_DELAY_MS; struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; int rc, settled_ua; rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua); if (chg->mode == PARALLEL_MASTER) { rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc); return IRQ_HANDLED; } rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua); if (rc < 0) { smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc); return IRQ_HANDLED; } if (chg->mode == PARALLEL_MASTER) { power_supply_changed(chg->usb_main_psy); vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, settled_ua >= USB_WEAK_INPUT_UA, 0); /* If AICL settled then schedule work now */ if ((settled_ua == get_effective_result(chg->usb_icl_votable)) || (stat & AICL_DONE_BIT)) delay = 0; schedule_delayed_work(&chg->icl_change_work, msecs_to_jiffies(delay)); } smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s icl_settled=%d\n", irq_data->name, settled_ua); return IRQ_HANDLED; } Loading Loading @@ -3279,6 +3456,8 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->vconn_attempts = 0; chg->otg_attempts = 0; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; chg->usb_ever_removed = true; Loading Loading @@ -3801,6 +3980,25 @@ static void smblib_otg_ss_done_work(struct work_struct *work) mutex_unlock(&chg->otg_oc_lock); } static void smblib_icl_change_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, icl_change_work.work); int rc, settled_ua; rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua); if (rc < 0) { smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc); return; } power_supply_changed(chg->usb_main_psy); vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, settled_ua >= USB_WEAK_INPUT_UA, 0); smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua); } static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; Loading Loading @@ -3917,6 +4115,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } chg->hvdcp_hw_inov_dis_votable = create_votable("HVDCP_HW_INOV_DIS", VOTE_SET_ANY, smblib_hvdcp_hw_inov_dis_vote_callback, chg); if (IS_ERR(chg->hvdcp_hw_inov_dis_votable)) { rc = PTR_ERR(chg->hvdcp_hw_inov_dis_votable); return rc; } return rc; } Loading @@ -3940,6 +4147,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->pl_enable_votable_indirect); if (chg->apsd_disable_votable) destroy_votable(chg->apsd_disable_votable); if (chg->hvdcp_hw_inov_dis_votable) destroy_votable(chg->hvdcp_hw_inov_dis_votable); } static void smblib_iio_deinit(struct smb_charger *chg) Loading Loading @@ -3970,6 +4179,7 @@ int smblib_init(struct smb_charger *chg) INIT_WORK(&chg->otg_oc_work, smblib_otg_oc_work); INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work); INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); chg->fake_capacity = -EINVAL; switch (chg->mode) { Loading
drivers/power/supply/qcom/smb-lib.h +7 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ enum print_reason { #define PD_SUSPEND_SUPPORTED_VOTER "PD_SUSPEND_SUPPORTED_VOTER" #define PL_DELAY_HVDCP_VOTER "PL_DELAY_HVDCP_VOTER" #define CTM_VOTER "CTM_VOTER" #define SW_QC3_VOTER "SW_QC3_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 Loading Loading @@ -267,6 +268,7 @@ struct smb_charger { struct votable *hvdcp_disable_votable_indirect; struct votable *hvdcp_enable_votable; struct votable *apsd_disable_votable; struct votable *hvdcp_hw_inov_dis_votable; /* work */ struct work_struct bms_update_work; Loading @@ -278,6 +280,7 @@ struct smb_charger { struct work_struct otg_oc_work; struct work_struct vconn_oc_work; struct delayed_work otg_ss_done_work; struct delayed_work icl_change_work; /* cached status */ int voltage_min_uv; Loading Loading @@ -315,6 +318,8 @@ struct smb_charger { /* qnovo */ int qnovo_fcc_ua; int qnovo_fv_uv; int usb_icl_delta_ua; int pulse_cnt; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); Loading Loading @@ -472,6 +477,8 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg, union power_supply_propval *val); int smblib_icl_override(struct smb_charger *chg, bool override); int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua); int smblib_dp_dm(struct smb_charger *chg, int val); int smblib_rerun_aicl(struct smb_charger *chg); int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); Loading