From e86c77ed32163e2ca73b367b36e4d6472d021cf9 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 2 Mar 2020 14:50:19 -0800 Subject: [PATCH] dwc3: Handle USB spoof disconnect when EUD is enabled Due to dpdm regulator is not being used on newer platform as ADSP firmware takes care of charger detection, there is no way to detect USB cable disconnect vs USB spoof disconnect when EUD is enabled. This change identifies USB spoof disconnect and provides spoof disconnect related information to USB PHY driver to avoid turning off PHY clocks and regulators. Change-Id: I8f6b953e7196378b9786fe268a08cc1a0e2620d3 Signed-off-by: Mayank Rana --- drivers/usb/dwc3/dwc3-msm.c | 58 +++++++++++++++++++++++++++---- drivers/usb/phy/phy-msm-snps-hs.c | 17 ++++++--- include/linux/usb/dwc3-msm.h | 3 ++ 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 48249d1f0c0d..5fade9acc038 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -444,7 +444,9 @@ struct dwc3_msm { bool in_device_mode; enum usb_device_speed max_rh_port_speed; unsigned int tx_fifo_size; + bool check_eud_state; bool vbus_active; + bool eud_active; bool suspend; bool use_pdc_interrupts; enum dwc3_id_state id_state; @@ -3149,6 +3151,39 @@ static void dwc3_ext_event_notify(struct dwc3_msm *mdwc) clear_bit(B_SUSPEND, &mdwc->inputs); } + if (mdwc->check_eud_state) { + mdwc->hs_phy->flags &= + ~(EUD_SPOOF_CONNECT | EUD_SPOOF_DISCONNECT); + dev_dbg(mdwc->dev, "eud: state:%d active:%d hs_phy_flags:0x%x\n", + mdwc->check_eud_state, mdwc->eud_active, + mdwc->hs_phy->flags); + if (mdwc->eud_active) { + mdwc->hs_phy->flags |= EUD_SPOOF_CONNECT; + dev_dbg(mdwc->dev, "EUD: XCVR: BSV set\n"); + set_bit(B_SESS_VLD, &mdwc->inputs); + } else { + mdwc->hs_phy->flags |= EUD_SPOOF_DISCONNECT; + dev_dbg(mdwc->dev, "EUD: XCVR: BSV clear\n"); + clear_bit(B_SESS_VLD, &mdwc->inputs); + } + + mdwc->check_eud_state = false; + } + + + dev_dbg(mdwc->dev, "eud: state:%d active:%d hs_phy_flags:0x%x\n", + mdwc->check_eud_state, mdwc->eud_active, mdwc->hs_phy->flags); + + /* handle case of USB cable disconnect after USB spoof disconnect */ + if (!mdwc->vbus_active && + (mdwc->hs_phy->flags & EUD_SPOOF_DISCONNECT)) { + mdwc->hs_phy->flags &= ~EUD_SPOOF_DISCONNECT; + mdwc->hs_phy->flags |= PHY_SUS_OVERRIDE; + usb_phy_set_suspend(mdwc->hs_phy, 1); + mdwc->hs_phy->flags &= ~PHY_SUS_OVERRIDE; + return; + } + queue_delayed_work(mdwc->sm_usb_wq, &mdwc->sm_work, 0); } @@ -3475,6 +3510,8 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb, struct extcon_dev *edev = ptr; struct extcon_nb *enb = container_of(nb, struct extcon_nb, vbus_nb); struct dwc3_msm *mdwc = enb->mdwc; + char *eud_str; + const char *edev_name; if (!edev || !mdwc) return NOTIFY_DONE; @@ -3482,15 +3519,24 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb, dwc = platform_get_drvdata(mdwc->dwc3); dbg_event(0xFF, "extcon idx", enb->idx); + dev_dbg(mdwc->dev, "vbus:%ld event received\n", event); + edev_name = extcon_get_edev_name(edev); + dbg_log_string("edev:%s\n", edev_name); - if (mdwc->vbus_active == event) - return NOTIFY_DONE; + /* detect USB spoof disconnect/connect notification with EUD device */ + eud_str = strnstr(edev_name, "eud", strlen(edev_name)); + if (eud_str) { + if (mdwc->eud_active == event) + return NOTIFY_DONE; + mdwc->eud_active = event; + mdwc->check_eud_state = true; + } else { + if (mdwc->vbus_active == event) + return NOTIFY_DONE; + mdwc->vbus_active = event; + } mdwc->ext_idx = enb->idx; - - dev_dbg(mdwc->dev, "vbus:%ld event received\n", event); - - mdwc->vbus_active = event; if (dwc->dr_mode == USB_DR_MODE_OTG && !mdwc->in_restart) queue_work(mdwc->dwc3_wq, &mdwc->resume_work); diff --git a/drivers/usb/phy/phy-msm-snps-hs.c b/drivers/usb/phy/phy-msm-snps-hs.c index 121669dfe89b..ff349d65be58 100644 --- a/drivers/usb/phy/phy-msm-snps-hs.c +++ b/drivers/usb/phy/phy-msm-snps-hs.c @@ -341,7 +341,8 @@ static int msm_hsphy_init(struct usb_phy *uphy) if (phy->eud_enable_reg && readl_relaxed(phy->eud_enable_reg)) { dev_err(phy->phy.dev, "eud is enabled\n"); - return 0; + ret = msm_hsphy_enable_power(phy, true); + return ret; } ret = msm_hsphy_enable_power(phy, true); @@ -460,11 +461,15 @@ static int msm_hsphy_set_suspend(struct usb_phy *uphy, int suspend) struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy); if (phy->suspended && suspend) { + if (phy->phy.flags & PHY_SUS_OVERRIDE) + goto suspend; + dev_dbg(uphy->dev, "%s: USB PHY is already suspended\n", - __func__); + __func__); return 0; } +suspend: if (suspend) { /* Bus suspend */ if (phy->cable_connected || (phy->phy.flags & PHY_HOST_MODE)) { @@ -480,9 +485,13 @@ static int msm_hsphy_set_suspend(struct usb_phy *uphy, int suspend) msm_hsphy_enable_clocks(phy, false); } else {/* Cable disconnect */ mutex_lock(&phy->phy_lock); + dev_dbg(uphy->dev, "phy->flags:0x%x\n", phy->phy.flags); if (!phy->dpdm_enable) { - msm_hsphy_enable_clocks(phy, false); - msm_hsphy_enable_power(phy, false); + if (!(phy->phy.flags & EUD_SPOOF_DISCONNECT)) { + dev_dbg(uphy->dev, "turning off clocks/ldo\n"); + msm_hsphy_enable_clocks(phy, false); + msm_hsphy_enable_power(phy, false); + } } else { dev_dbg(uphy->dev, "dpdm reg still active. Keep clocks/ldo ON\n"); } diff --git a/include/linux/usb/dwc3-msm.h b/include/linux/usb/dwc3-msm.h index 9880477d70b1..37726701b1b0 100644 --- a/include/linux/usb/dwc3-msm.h +++ b/include/linux/usb/dwc3-msm.h @@ -16,6 +16,9 @@ #define PHY_LANE_B BIT(3) #define PHY_HSFS_MODE BIT(4) #define PHY_LS_MODE BIT(5) +#define EUD_SPOOF_DISCONNECT BIT(6) +#define EUD_SPOOF_CONNECT BIT(7) +#define PHY_SUS_OVERRIDE BIT(8) /* * The following are bit fields describing the USB BAM options.