From 9b1513098b1ea710c5eb0ea9635b35580a9a243f Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Wed, 22 Mar 2017 14:44:49 -0700 Subject: power: smb-lib: cleanup everything upon USB removal Currently it is expected upon USB removal that consumers will receive a notification that USB has been removed and will cleanup after themselves. Unfortunately this makes it very difficult to keep track of all of the necessary cleanup steps upon removal. Also, in preparation of not allowing consumers to set properties after a USB removal has happened it is now the responsibility of the charger driver to cleanup on their behalf. Moreover, since we don't have a separate removal/insertion interrupt, the removal/insertion code may run even if the typeC cable is not physically inserted/removed. Fix it by tracking a typec_present flag. CRs-Fixed: 2020132 Change-Id: Ia514abaa4e12f72daec17fd1e95f3c51cc38a15f Signed-off-by: Nicholas Troast Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/supply/qcom/smb-lib.c | 235 ++++++++++++++++++------------------ drivers/power/supply/qcom/smb-lib.h | 1 + 2 files changed, 120 insertions(+), 116 deletions(-) diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index b0c25dda97fe..f17ccc91e38c 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -2550,52 +2550,53 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, { int rc; u8 stat = 0; - bool cc_debounced; bool orientation; - bool pd_active = val->intval; - 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); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return rc; } - if (pd_active) { + 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 = stat & 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 +2604,32 @@ 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); - /* 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); + if ((stat & (TYPEC_DEBOUNCE_DONE_STATUS_BIT | UFP_DFP_MODE_BIT)) + == TYPEC_DEBOUNCE_DONE_STATUS_BIT) + try_rerun_apsd_for_hvdcp(chg); + } - chg->pd_active = pd_active; smblib_update_usb_type(chg); power_supply_changed(chg->usb_psy); - return rc; } @@ -3516,58 +3513,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) { /* @@ -3605,30 +3550,47 @@ 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; /* enable APSD CC trigger for next insertion */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, @@ -3636,9 +3598,42 @@ 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, @@ -3655,7 +3650,6 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, rc); if (sink_attached) { - typec_source_removal(chg); typec_sink_insertion(chg); } else { typec_sink_removal(chg); @@ -3663,8 +3657,7 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, } rp = smblib_get_prop_ufp_mode(chg); - if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH - || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { + if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH) { 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); @@ -3680,10 +3673,20 @@ static void smblib_handle_typec_debounce_done(struct smb_charger *chg, 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, + legacy_cable); + } + } 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) diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 70371decacd8..002d58b8c33c 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -307,6 +307,7 @@ struct smb_charger { int otg_cl_ua; bool uusb_apsd_rerun_done; bool pd_hard_reset; + bool typec_present; /* workaround flag */ u32 wa_flags; -- cgit v1.2.3 From fd0747e34a4fddf2ed14ddc1d9b83d637924504f Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Mon, 17 Apr 2017 12:15:19 -0700 Subject: power: qcom: lock USB removal During USB removal a lot of cleanup happens; votables are reset, flags are cleared, etc. After the cleanup is finished there is a chance that USB power supply consumers may set properties before getting the USB removal notification. This can lead to many problems where ICL limits are set based on the previous insertion, or APSD is disabled due to a late setting of PD_ACTIVE. Introduce a lock which prevents USB power supply consumers from setting properties when USB has been removed. This lock will ensure that the next insertion starts with a clean slate. CRs-Fixed: 2020132 Change-Id: I05a4145289b6097e41afc30aa09782722fa03fb6 Signed-off-by: Nicholas Troast Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/supply/qcom/qpnp-smb2.c | 8 ++++ drivers/power/supply/qcom/smb-lib.c | 76 ++++++++++++++++++++++------------- drivers/power/supply/qcom/smb-lib.h | 1 + 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index c7d6937bcc49..aa51b67acf72 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; } diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index f17ccc91e38c..c9fcb2be8569 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -3104,7 +3104,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); @@ -3120,18 +3124,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); @@ -3181,8 +3183,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; } @@ -3717,46 +3729,33 @@ 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; + return; } rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); - return IRQ_HANDLED; + 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); - 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, legacy_cable); if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT) - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n", - irq_data->name); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n"); if (stat4 & TYPEC_VCONN_OVERCURR_STATUS_BIT) schedule_work(&chg->vconn_oc_work); @@ -3764,6 +3763,26 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) 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) { + smblib_dbg(chg, PR_INTERRUPT, "Ignoring cc2_wa_active\n"); + return IRQ_HANDLED; + } + + mutex_lock(&chg->lock); + smblib_usb_typec_change(chg); + mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -3889,7 +3908,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); @@ -3952,8 +3970,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: @@ -4353,6 +4372,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); diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 002d58b8c33c..877f62a77d1a 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -229,6 +229,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; -- cgit v1.2.3 From fcc7e888ff106d5ac7b3e11dc96af9425cc36e6d Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Mon, 17 Apr 2017 12:26:26 -0700 Subject: power: smb-lib: cache USB Type-C status Currently the USB Type-C status is retrieved from the hardware in real time. The Type-C change IRQ should trigger on every change of the Type-C status, therefore it is not necessary to read the status registers multiple times if a Type-C change IRQ has not triggered. Furthermore, workarounds which force UFP/DFP mode, or disable Type-C altogether could mislead the software into thinking a removal has happened. Cache all of the USB Type-C status registers upon receiving a USB Type-C change IRQ, and use the cached status where appropriate. CRs-Fixed: 2020132 Change-Id: I99f2ff29633207898ae803672162db0c3cec80dc Signed-off-by: Nicholas Troast Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/supply/qcom/smb-lib.c | 115 +++++++++--------------------------- drivers/power/supply/qcom/smb-lib.h | 1 + 2 files changed, 30 insertions(+), 86 deletions(-) diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index c9fcb2be8569..f2fb6154e9e2 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -1145,7 +1145,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 +1176,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 +2142,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 +2166,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 +2184,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 +2205,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); + if (!(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) { 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)) { - 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,18 +2503,11 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - u8 stat = 0; bool orientation; if (!get_effective_result(chg->pd_allowed_votable)) return -EINVAL; - 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; - } - chg->pd_active = val->intval; if (chg->pd_active) { vote(chg->apsd_disable_votable, PD_VOTER, true, 0); @@ -2572,7 +2519,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, * line when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) * is set or when VCONN_EN_VALUE_BIT is set. */ - orientation = stat & CC_ORIENTATION_BIT; + orientation = chg->typec_status[3] & CC_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, VCONN_EN_ORIENTATION_BIT, @@ -2623,8 +2570,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc); - if ((stat & (TYPEC_DEBOUNCE_DONE_STATUS_BIT | UFP_DFP_MODE_BIT)) - == TYPEC_DEBOUNCE_DONE_STATUS_BIT) + if ((chg->typec_status[3] & + (TYPEC_DEBOUNCE_DONE_STATUS_BIT | UFP_DFP_MODE_BIT)) + == TYPEC_DEBOUNCE_DONE_STATUS_BIT) try_rerun_apsd_for_hvdcp(chg); } @@ -3732,37 +3680,32 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg) static void smblib_usb_typec_change(struct smb_charger *chg) { int rc; - u8 stat4, stat5; bool debounce_done, sink_attached, legacy_cable; - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); + 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_4 rc=%d\n", rc); - return; - } - - rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); + 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); + legacy_cable = + (bool)(chg->typec_status[4] & TYPEC_LEGACY_CABLE_STATUS_BIT); smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached, legacy_cable); - if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT) + if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT) smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n"); - 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) diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 877f62a77d1a..603628655567 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -309,6 +309,7 @@ struct smb_charger { bool uusb_apsd_rerun_done; bool pd_hard_reset; bool typec_present; + u8 typec_status[5]; /* workaround flag */ u32 wa_flags; -- cgit v1.2.3 From 5446858d07bd97cf4f80babc4c86624f63654c87 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Mon, 17 Apr 2017 17:36:14 -0700 Subject: power: smb-lib: WA to fix legacy cable detection Currently a legacy cable is always assumed which causes decreased performance from non-legacy HVDCP adapters with 10k ohm Rp. Fix this by disabling and re-enabling Type-C to rerun the legacy cable detection. Moreover, the legacy cable IRQs are not used and cause unnecessary type-c-change IRQs to fire. Disable them. CRs-Fixed: 2020132 Change-Id: I57fc3762251ead028298f01b06d66f52fd119c6b Signed-off-by: Nicholas Troast Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/supply/qcom/qpnp-smb2.c | 10 +- drivers/power/supply/qcom/smb-lib.c | 220 +++++++++++++++++++++------------- drivers/power/supply/qcom/smb-lib.h | 5 + 3 files changed, 149 insertions(+), 86 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index aa51b67acf72..8855a1c74e0b 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -1358,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; } @@ -1517,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 f2fb6154e9e2..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 * * *****************/ @@ -2503,11 +2506,24 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - bool orientation; + bool orientation, cc_debounced, sink_attached, hvdcp; + u8 stat; if (!get_effective_result(chg->pd_allowed_votable)) return -EINVAL; + 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; + } + + 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); @@ -2562,6 +2578,8 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, 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); /* HW controlled CC_OUT */ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, @@ -2570,10 +2588,15 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc); - if ((chg->typec_status[3] & - (TYPEC_DEBOUNCE_DONE_STATUS_BIT | UFP_DFP_MODE_BIT)) - == TYPEC_DEBOUNCE_DONE_STATUS_BIT) - 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); } smblib_update_usb_type(chg); @@ -3311,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) @@ -3348,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: /* @@ -3387,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) { @@ -3473,21 +3497,6 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) return IRQ_HANDLED; } -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 @@ -3551,6 +3560,7 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) 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, @@ -3597,9 +3607,9 @@ static void smblib_handle_typec_removal(struct smb_charger *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); @@ -3609,26 +3619,14 @@ 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) { + 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) { - 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, }; @@ -3637,8 +3635,7 @@ static void smblib_handle_typec_debounce_done(struct smb_charger *chg, if (!chg->typec_present) { chg->typec_present = true; smblib_dbg(chg, PR_MISC, "TypeC insertion\n"); - smblib_handle_typec_insertion(chg, sink_attached, - legacy_cable); + smblib_handle_typec_insertion(chg, sink_attached); } } else { if (chg->typec_present) { @@ -3680,7 +3677,7 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg) static void smblib_usb_typec_change(struct smb_charger *chg) { int rc; - bool debounce_done, sink_attached, legacy_cable; + bool debounce_done, sink_attached; rc = smblib_multibyte_read(chg, TYPE_C_STATUS_1_REG, chg->typec_status, 5); @@ -3693,11 +3690,8 @@ static void smblib_usb_typec_change(struct smb_charger *chg) (bool)(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT); sink_attached = (bool)(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT); - legacy_cable = - (bool)(chg->typec_status[4] & TYPEC_LEGACY_CABLE_STATUS_BIT); - smblib_handle_typec_debounce_done(chg, - debounce_done, sink_attached, legacy_cable); + smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached); if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT) smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n"); @@ -3718,8 +3712,10 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) return IRQ_HANDLED; } - if (chg->cc2_detach_wa_active) { - smblib_dbg(chg, PR_INTERRUPT, "Ignoring cc2_wa_active\n"); + 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; } @@ -3753,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)) @@ -3765,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) @@ -3804,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) @@ -4138,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; @@ -4270,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; } @@ -4295,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) @@ -4328,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 603628655567..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 @@ -270,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; @@ -283,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; @@ -310,10 +313,12 @@ struct smb_charger { 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; -- cgit v1.2.3