diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-04-18 22:19:21 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-04-18 22:19:21 -0700 |
commit | aadfc6d284e7cbd5af4e38c0ba0a71b71f59a2cd (patch) | |
tree | 48d3131868603ec4c5c54053baceb35898b64d75 | |
parent | 64cf255adfd6283a560f21725d98c0381e9d7085 (diff) | |
parent | 5446858d07bd97cf4f80babc4c86624f63654c87 (diff) |
Merge "power: smb-lib: WA to fix legacy cable detection"
-rw-r--r-- | drivers/power/supply/qcom/qpnp-smb2.c | 18 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb-lib.c | 610 | ||||
-rw-r--r-- | drivers/power/supply/qcom/smb-lib.h | 8 |
3 files changed, 338 insertions, 298 deletions
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index c7d6937bcc49..8855a1c74e0b 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -540,6 +540,12 @@ static int smb2_usb_set_prop(struct power_supply *psy, struct smb_charger *chg = &chip->chg; int rc = 0; + mutex_lock(&chg->lock); + if (!chg->typec_present) { + rc = -EINVAL; + goto unlock; + } + switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_MIN: rc = smblib_set_prop_usb_voltage_min(chg, val); @@ -578,6 +584,8 @@ static int smb2_usb_set_prop(struct power_supply *psy, break; } +unlock: + mutex_unlock(&chg->lock); return rc; } @@ -1350,10 +1358,12 @@ static int smb2_configure_typec(struct smb_charger *chg) return rc; } - /* disable try.SINK mode */ - rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0); + /* disable try.SINK mode and legacy cable IRQs */ + rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT | + TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT | + TYPEC_LEGACY_CABLE_INT_EN_BIT, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc); + dev_err(chg->dev, "Couldn't set Type-C config rc=%d\n", rc); return rc; } @@ -1509,6 +1519,8 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, true, chip->dt.dc_icl_ua); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + true, 0); vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, chip->dt.hvdcp_disable, 0); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index b0c25dda97fe..1a10b458828b 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -543,30 +543,6 @@ static void smblib_rerun_apsd(struct smb_charger *chg) smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc); } -static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg) -{ - const struct apsd_result *apsd_result; - - /* - * PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether - * apsd rerun was tried earlier - */ - if (get_client_vote(chg->hvdcp_disable_votable_indirect, - PD_INACTIVE_VOTER)) { - vote(chg->hvdcp_disable_votable_indirect, - PD_INACTIVE_VOTER, false, 0); - /* ensure hvdcp is enabled */ - if (!get_effective_result( - chg->hvdcp_disable_votable_indirect)) { - apsd_result = smblib_get_apsd_result(chg); - if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) { - smblib_rerun_apsd(chg); - } - } - } - return 0; -} - static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) { const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); @@ -1023,6 +999,7 @@ 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_EN_BIT; + u8 stat; /* vote to enable/disable HW autonomous INOV */ vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0); @@ -1044,6 +1021,16 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, return rc; } + rc = smblib_read(chg, APSD_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc); + return rc; + } + + /* re-run APSD if HVDCP was detected */ + if (stat & QC_CHARGER_BIT) + smblib_rerun_apsd(chg); + return 0; } @@ -1137,6 +1124,22 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable, return 0; } +static int smblib_typec_irq_disable_vote_callback(struct votable *votable, + void *data, int disable, const char *client) +{ + struct smb_charger *chg = data; + + if (!chg->irq_info[TYPE_C_CHANGE_IRQ].irq) + return 0; + + if (disable) + disable_irq_nosync(chg->irq_info[TYPE_C_CHANGE_IRQ].irq); + else + enable_irq(chg->irq_info[TYPE_C_CHANGE_IRQ].irq); + + return 0; +} + /******************* * VCONN REGULATOR * * *****************/ @@ -1145,7 +1148,7 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable, static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); - u8 otg_stat, stat4; + u8 otg_stat, val; int rc = 0, i; if (!chg->external_vconn) { @@ -1176,17 +1179,12 @@ static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev) * VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used * for Vconn, and it should be set with reverse polarity of CC_OUT. */ - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return rc; - } - smblib_dbg(chg, PR_OTG, "enabling VCONN\n"); - stat4 = stat4 & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; + val = chg->typec_status[3] & + CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT, - VCONN_EN_VALUE_BIT | stat4); + VCONN_EN_VALUE_BIT | val); if (rc < 0) { smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc); return rc; @@ -2147,23 +2145,13 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg, int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg, union power_supply_propval *val) { - int rc = 0; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return rc; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", - stat); - - if (stat & CC_ATTACHED_BIT) - val->intval = (bool)(stat & CC_ORIENTATION_BIT) + 1; + if (chg->typec_status[3] & CC_ATTACHED_BIT) + val->intval = + (bool)(chg->typec_status[3] & CC_ORIENTATION_BIT) + 1; else val->intval = 0; - return rc; + return 0; } static const char * const smblib_typec_mode_name[] = { @@ -2181,17 +2169,7 @@ static const char * const smblib_typec_mode_name[] = { static int smblib_get_prop_ufp_mode(struct smb_charger *chg) { - int rc; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_1_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc); - return POWER_SUPPLY_TYPEC_NONE; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat); - - switch (stat) { + switch (chg->typec_status[0]) { case 0: return POWER_SUPPLY_TYPEC_NONE; case UFP_TYPEC_RDSTD_BIT: @@ -2209,17 +2187,7 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) static int smblib_get_prop_dfp_mode(struct smb_charger *chg) { - int rc; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_2_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc); - return POWER_SUPPLY_TYPEC_NONE; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_2 = 0x%02x\n", stat); - - switch (stat & DFP_TYPEC_MASK) { + switch (chg->typec_status[1] & DFP_TYPEC_MASK) { case DFP_RA_RA_BIT: return POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; case DFP_RD_RD_BIT: @@ -2240,28 +2208,17 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg) int smblib_get_prop_typec_mode(struct smb_charger *chg, union power_supply_propval *val) { - int rc; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - val->intval = POWER_SUPPLY_TYPEC_NONE; - return rc; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat); - - if (!(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) { + if (!(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) { val->intval = POWER_SUPPLY_TYPEC_NONE; - return rc; + return 0; } - if (stat & UFP_DFP_MODE_STATUS_BIT) + if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) val->intval = smblib_get_prop_dfp_mode(chg); else val->intval = smblib_get_prop_ufp_mode(chg); - return rc; + return 0; } int smblib_get_prop_typec_power_role(struct smb_charger *chg, @@ -2549,53 +2506,60 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - u8 stat = 0; - bool cc_debounced; - bool orientation; - bool pd_active = val->intval; + bool orientation, cc_debounced, sink_attached, hvdcp; + u8 stat; - if (!get_effective_result(chg->pd_allowed_votable)) { - smblib_err(chg, "PD is not allowed\n"); + if (!get_effective_result(chg->pd_allowed_votable)) return -EINVAL; - } - - vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0); - vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0); - vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0); - /* - * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line - * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set - * or when VCONN_EN_VALUE_BIT is set. - */ - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc); return rc; } - if (pd_active) { - orientation = stat & CC_ORIENTATION_BIT; + cc_debounced = (bool) + (chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT); + sink_attached = (bool) + (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT); + hvdcp = stat & QC_CHARGER_BIT; + + chg->pd_active = val->intval; + if (chg->pd_active) { + vote(chg->apsd_disable_votable, PD_VOTER, true, 0); + vote(chg->pd_allowed_votable, PD_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0); + + /* + * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 + * line when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) + * is set or when VCONN_EN_VALUE_BIT is set. + */ + orientation = chg->typec_status[3] & CC_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, VCONN_EN_ORIENTATION_BIT, orientation ? 0 : VCONN_EN_ORIENTATION_BIT); - if (rc < 0) { + if (rc < 0) smblib_err(chg, "Couldn't enable vconn on CC line rc=%d\n", rc); - return rc; - } + + /* SW controlled CC_OUT */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, TYPEC_SPARE_CFG_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't enable SW cc_out rc=%d\n", + rc); + /* * Enforce 500mA for PD until the real vote comes in later. * It is guaranteed that pd_active is set prior to * pd_current_max */ rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA); - if (rc < 0) { + if (rc < 0) smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n", - rc); - return rc; - } + rc); /* since PD was found the cable must be non-legacy */ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); @@ -2603,36 +2567,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, /* clear USB ICL vote for DCP_VOTER */ rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0); if (rc < 0) - smblib_err(chg, - "Couldn't un-vote DCP from USB ICL rc=%d\n", - rc); + smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n", + rc); /* remove USB_PSY_VOTER */ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); - if (rc < 0) { + if (rc < 0) smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc); - return rc; - } - } + } else { + vote(chg->apsd_disable_votable, PD_VOTER, false, 0); + vote(chg->pd_allowed_votable, PD_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, + false, 0); - /* CC pin selection s/w override in PD session; h/w otherwise. */ - rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, - TYPEC_SPARE_CFG_BIT, - pd_active ? TYPEC_SPARE_CFG_BIT : 0); - if (rc < 0) { - smblib_err(chg, "Couldn't change cc_out ctrl to %s rc=%d\n", - pd_active ? "SW" : "HW", rc); - return rc; - } + /* HW controlled CC_OUT */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", + rc); - cc_debounced = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT); - if (!pd_active && cc_debounced) - try_rerun_apsd_for_hvdcp(chg); + /* + * This WA should only run for HVDCP. Non-legacy SDP/CDP could + * draw more, but this WA will remove Rd causing VBUS to drop, + * and data could be interrupted. Non-legacy DCP could also draw + * more, but it may impact compliance. + */ + if (!chg->typec_legacy_valid && cc_debounced && + !sink_attached && hvdcp) + schedule_work(&chg->legacy_detection_work); + } - chg->pd_active = pd_active; smblib_update_usb_type(chg); power_supply_changed(chg->usb_psy); - return rc; } @@ -3107,7 +3075,11 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) { - if (!vbus_rising) { + if (vbus_rising) { + /* use the typec flag even though its not typec */ + chg->typec_present = 1; + } else { + chg->typec_present = 0; smblib_update_usb_type(chg); extcon_set_cable_state_(chg->extcon, EXTCON_USB, false); smblib_uusb_removal(chg); @@ -3123,18 +3095,16 @@ static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising) } #define PL_DELAY_MS 30000 -irqreturn_t smblib_handle_usb_plugin(int irq, void *data) +void smblib_usb_plugin_locked(struct smb_charger *chg) { - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; int rc; u8 stat; bool vbus_rising; rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); - return IRQ_HANDLED; + smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return; } vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); @@ -3184,8 +3154,18 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) smblib_typec_usb_plugin(chg, vbus_rising); power_supply_changed(chg->usb_psy); - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", - irq_data->name, vbus_rising ? "attached" : "detached"); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", + vbus_rising ? "attached" : "detached"); +} + +irqreturn_t smblib_handle_usb_plugin(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + mutex_lock(&chg->lock); + smblib_usb_plugin_locked(chg); + mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -3354,9 +3334,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, if (rising) { vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); - if (get_effective_result(chg->pd_disallowed_votable_indirect)) - /* could be a legacy cable, try doing hvdcp */ - try_rerun_apsd_for_hvdcp(chg); /* enable HDC and ICL irq for QC2/3 charger */ if (qc_charger) @@ -3391,6 +3368,10 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) { + /* while PD is active it should have complete ICL control */ + if (chg->pd_active) + return; + switch (pst) { case POWER_SUPPLY_TYPE_USB: /* @@ -3430,7 +3411,7 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) apsd_result = smblib_update_usb_type(chg); - if (!chg->pd_active) + if (!chg->typec_legacy_valid) smblib_force_legacy_icl(chg, apsd_result->pst); switch (apsd_result->bit) { @@ -3516,73 +3497,6 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) return IRQ_HANDLED; } -static void typec_source_removal(struct smb_charger *chg) -{ - int rc; - - /* reset legacy unknown vote */ - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); - - /* reset both usbin current and voltage votes */ - vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); - vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); - - cancel_delayed_work_sync(&chg->hvdcp_detect_work); - - if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) { - /* re-enable AUTH_IRQ_EN_CFG_BIT */ - rc = smblib_masked_write(chg, - USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, - AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT); - if (rc < 0) - smblib_err(chg, - "Couldn't enable QC auth setting rc=%d\n", rc); - } - - /* reconfigure allowed voltage for HVDCP */ - rc = smblib_set_adapter_allowance(chg, - USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); - if (rc < 0) - smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", - rc); - - chg->voltage_min_uv = MICRO_5V; - chg->voltage_max_uv = MICRO_5V; - - /* clear USB ICL vote for PD_VOTER */ - rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, "Couldn't un-vote PD from USB ICL rc=%d\n", rc); - - /* clear USB ICL vote for USB_PSY_VOTER */ - rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, - "Couldn't un-vote USB_PSY from USB ICL rc=%d\n", rc); - - /* clear USB ICL vote for DCP_VOTER */ - rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, - "Couldn't un-vote DCP from USB ICL rc=%d\n", rc); - -} - -static void typec_source_insertion(struct smb_charger *chg) -{ - /* - * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for - * ICL, so vote LEGACY_UNKNOWN here if none of the above three have - * casted their votes - */ - if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER) - && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER) - && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER)) - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); - - smblib_set_opt_freq_buck(chg, chg->chg_freq.freq_5V); -} - static void typec_sink_insertion(struct smb_charger *chg) { /* when a sink is inserted we should not wait on hvdcp timeout to @@ -3605,30 +3519,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->cc2_detach_wa_active = false; + /* reset APSD voters */ + vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0); + vote(chg->apsd_disable_votable, PD_VOTER, false, 0); + cancel_delayed_work_sync(&chg->pl_enable_work); - vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); - vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); + cancel_delayed_work_sync(&chg->hvdcp_detect_work); + + /* reset input current limit voters */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + vote(chg->usb_icl_votable, PD_VOTER, false, 0); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + vote(chg->usb_icl_votable, DCP_VOTER, false, 0); + vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); + /* reset hvdcp voters */ + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + + /* reset power delivery voters */ + vote(chg->pd_allowed_votable, PD_VOTER, false, 0); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); + + /* reset usb irq voters */ vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0); - /* reset votes from vbus_cc_short */ - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - true, 0); - vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, - true, 0); - /* - * cable could be removed during hard reset, remove its vote to - * disable apsd - */ - vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0); + /* reset parallel voters */ + vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); + vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); + vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); + vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); chg->vconn_attempts = 0; chg->otg_attempts = 0; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; + chg->voltage_min_uv = MICRO_5V; + chg->voltage_max_uv = MICRO_5V; + chg->pd_active = 0; + chg->pd_hard_reset = 0; + chg->typec_legacy_valid = false; /* enable APSD CC trigger for next insertion */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, @@ -3636,15 +3568,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc); - smblib_update_usb_type(chg); - typec_source_removal(chg); + if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) { + /* re-enable AUTH_IRQ_EN_CFG_BIT */ + rc = smblib_masked_write(chg, + USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, + AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT); + if (rc < 0) + smblib_err(chg, + "Couldn't enable QC auth setting rc=%d\n", rc); + } + + /* reconfigure allowed voltage for HVDCP */ + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + if (rc < 0) + smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", + rc); + + /* enable DRP */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc); + + /* HW controlled CC_OUT */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc); + + /* restore crude sensor */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); + if (rc < 0) + smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc); + typec_sink_removal(chg); + smblib_update_usb_type(chg); } static void smblib_handle_typec_insertion(struct smb_charger *chg, - bool sink_attached, bool legacy_cable) + bool sink_attached) { - int rp, rc; + int rc; vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0); @@ -3654,36 +3619,31 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", rc); - if (sink_attached) { - typec_source_removal(chg); + if (sink_attached) typec_sink_insertion(chg); - } else { + else typec_sink_removal(chg); - typec_source_insertion(chg); - } - - rp = smblib_get_prop_ufp_mode(chg); - if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH - || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { - smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n"); - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - true, 0); - } else { - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - false, 0); - } } static void smblib_handle_typec_debounce_done(struct smb_charger *chg, - bool rising, bool sink_attached, bool legacy_cable) + bool rising, bool sink_attached) { int rc; union power_supply_propval pval = {0, }; - if (rising) - smblib_handle_typec_insertion(chg, sink_attached, legacy_cable); - else - smblib_handle_typec_removal(chg); + if (rising) { + if (!chg->typec_present) { + chg->typec_present = true; + smblib_dbg(chg, PR_MISC, "TypeC insertion\n"); + smblib_handle_typec_insertion(chg, sink_attached); + } + } else { + if (chg->typec_present) { + chg->typec_present = false; + smblib_dbg(chg, PR_MISC, "TypeC removal\n"); + smblib_handle_typec_removal(chg); + } + } rc = smblib_get_prop_typec_mode(chg, &pval); if (rc < 0) @@ -3714,53 +3674,54 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg) return IRQ_HANDLED; } -irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) +static void smblib_usb_typec_change(struct smb_charger *chg) { - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; int rc; - u8 stat4, stat5; - bool debounce_done, sink_attached, legacy_cable; - - if (chg->micro_usb_mode) - return smblib_handle_usb_typec_change_for_uusb(chg); - - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return IRQ_HANDLED; - } + bool debounce_done, sink_attached; - rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); + rc = smblib_multibyte_read(chg, TYPE_C_STATUS_1_REG, + chg->typec_status, 5); if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); - return IRQ_HANDLED; + smblib_err(chg, "Couldn't cache USB Type-C status rc=%d\n", rc); + return; } - debounce_done = (bool)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT); - sink_attached = (bool)(stat4 & UFP_DFP_MODE_STATUS_BIT); - legacy_cable = (bool)(stat5 & TYPEC_LEGACY_CABLE_STATUS_BIT); + debounce_done = + (bool)(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT); + sink_attached = + (bool)(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT); - if (chg->cc2_detach_wa_active) { - smblib_dbg(chg, PR_INTERRUPT, "Ignoring cc2_wrkarnd=%d dd=%d\n", - chg->cc2_detach_wa_active, - debounce_done); - return IRQ_HANDLED; - } + smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached); - smblib_handle_typec_debounce_done(chg, - debounce_done, sink_attached, legacy_cable); + if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT) + smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n"); - if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT) - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n", - irq_data->name); - - if (stat4 & TYPEC_VCONN_OVERCURR_STATUS_BIT) + if (chg->typec_status[3] & TYPEC_VCONN_OVERCURR_STATUS_BIT) schedule_work(&chg->vconn_oc_work); power_supply_changed(chg->usb_psy); - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat4); - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_5 = 0x%02x\n", stat5); +} + +irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + if (chg->micro_usb_mode) { + smblib_handle_usb_typec_change_for_uusb(chg); + return IRQ_HANDLED; + } + + if (chg->cc2_detach_wa_active || chg->typec_en_dis_active) { + smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n", + chg->cc2_detach_wa_active ? + "cc2_detach_wa" : "typec_en_dis"); + return IRQ_HANDLED; + } + + mutex_lock(&chg->lock); + smblib_usb_typec_change(chg); + mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -3788,7 +3749,7 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - int rc; + int rc, usb_icl; u8 stat; if (!(chg->wa_flags & BOOST_BACK_WA)) @@ -3800,8 +3761,9 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) return IRQ_HANDLED; } - if ((stat & USE_USBIN_BIT) && - get_effective_result(chg->usb_icl_votable) < USBIN_25MA) + /* skip suspending input if its already suspended by some other voter */ + usb_icl = get_effective_result(chg->usb_icl_votable); + if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl < USBIN_25MA) return IRQ_HANDLED; if (stat & USE_DCIN_BIT) @@ -3839,12 +3801,7 @@ static void smblib_hvdcp_detect_work(struct work_struct *work) vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); - if (get_effective_result(chg->pd_disallowed_votable_indirect)) - /* pd is still disabled, try hvdcp */ - try_rerun_apsd_for_hvdcp(chg); - else - /* notify pd now that pd is allowed */ - power_supply_changed(chg->usb_psy); + power_supply_changed(chg->usb_psy); } static void bms_update_work(struct work_struct *work) @@ -3886,7 +3843,6 @@ static void rdstd_cc2_detach_work(struct work_struct *work) { int rc; u8 stat4, stat5; - struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"}; struct smb_charger *chg = container_of(work, struct smb_charger, rdstd_cc2_detach_work); @@ -3949,8 +3905,9 @@ static void rdstd_cc2_detach_work(struct work_struct *work) rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, EXIT_SNK_BASED_ON_CC_BIT, 0); smblib_reg_block_restore(chg, cc2_detach_settings); - irq_data.parent_data = chg; - smblib_handle_usb_typec_change(0, &irq_data); + mutex_lock(&chg->lock); + smblib_usb_typec_change(chg); + mutex_unlock(&chg->lock); return; rerun: @@ -4173,6 +4130,56 @@ static void smblib_pl_enable_work(struct work_struct *work) vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); } +static void smblib_legacy_detection_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + legacy_detection_work); + int rc; + u8 stat; + bool legacy, rp_high; + + mutex_lock(&chg->lock); + chg->typec_en_dis_active = 1; + smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n"); + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, + TYPEC_DISABLE_CMD_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc); + + /* wait for the adapter to turn off VBUS */ + msleep(500); + + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc); + + /* wait for type-c detection to complete */ + msleep(100); + + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read typec stat5 rc = %d\n", rc); + goto unlock; + } + + chg->typec_legacy_valid = true; + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT; + rp_high = smblib_get_prop_ufp_mode(chg) == + POWER_SUPPLY_TYPEC_SOURCE_HIGH; + if (!legacy || !rp_high) + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + false, 0); + +unlock: + chg->typec_en_dis_active = 0; + mutex_unlock(&chg->lock); +} + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; @@ -4305,6 +4312,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->typec_irq_disable_votable = create_votable("TYPEC_IRQ_DISABLE", + VOTE_SET_ANY, + smblib_typec_irq_disable_vote_callback, + chg); + if (IS_ERR(chg->typec_irq_disable_votable)) { + rc = PTR_ERR(chg->typec_irq_disable_votable); + return rc; + } + return rc; } @@ -4330,6 +4346,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->apsd_disable_votable); if (chg->hvdcp_hw_inov_dis_votable) destroy_votable(chg->hvdcp_hw_inov_dis_votable); + if (chg->typec_irq_disable_votable) + destroy_votable(chg->typec_irq_disable_votable); } static void smblib_iio_deinit(struct smb_charger *chg) @@ -4350,6 +4368,7 @@ int smblib_init(struct smb_charger *chg) { int rc = 0; + mutex_init(&chg->lock); mutex_init(&chg->write_lock); mutex_init(&chg->otg_oc_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); @@ -4362,6 +4381,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); + INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); chg->fake_capacity = -EINVAL; switch (chg->mode) { diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 70371decacd8..b0d84f014b0d 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -61,6 +61,7 @@ enum print_reason { #define SW_QC3_VOTER "SW_QC3_VOTER" #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" #define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER" +#define CC2_WA_VOTER "CC2_WA_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 @@ -229,6 +230,7 @@ struct smb_charger { int smb_version; /* locks */ + struct mutex lock; struct mutex write_lock; struct mutex ps_change_lock; struct mutex otg_oc_lock; @@ -269,6 +271,7 @@ struct smb_charger { struct votable *apsd_disable_votable; struct votable *hvdcp_hw_inov_dis_votable; struct votable *usb_irq_enable_votable; + struct votable *typec_irq_disable_votable; /* work */ struct work_struct bms_update_work; @@ -282,6 +285,7 @@ struct smb_charger { struct delayed_work otg_ss_done_work; struct delayed_work icl_change_work; struct delayed_work pl_enable_work; + struct work_struct legacy_detection_work; /* cached status */ int voltage_min_uv; @@ -307,10 +311,14 @@ struct smb_charger { int otg_cl_ua; bool uusb_apsd_rerun_done; bool pd_hard_reset; + bool typec_present; + u8 typec_status[5]; + bool typec_legacy_valid; /* workaround flag */ u32 wa_flags; bool cc2_detach_wa_active; + bool typec_en_dis_active; int boost_current_ua; int temp_speed_reading_count; |