From 77632b0961584031f4bf0586a88e64e01c5385b4 Mon Sep 17 00:00:00 2001 From: William Wu Date: Tue, 2 Jun 2020 15:59:32 +0800 Subject: [PATCH] usb: xhci: support warm port reset quirk The RK3399 Type-C PHY requires that it must hold the PIPE power state in P2 before initializing the PHY. The current code reset the whole USB3.0 OTG controller in the DWC3 PM suspend/resume to set the PIPE in P2 if the link_state is RxDetect, and then power on/off the Type-C PHY safely during PM suspend/resume. In this case, we assumed that the RxDetect state meant no USB device connected. But actually, if an USB 1.0/1.1/2.0 device connected, the link is in the RxDetect State too. So reset the USB controller will cause the USB 1.0/1.1/2.0 device to be reenumerated upon PM resume. This patch uses xHC warm port reset instead of controller reset to set the PIPE in P2. It can avoid reseting the USB 1.0/1.1/2.0 device upon PM resume. Fixes: c9b15ba1eb12 ("usb: dwc3: core: prevent re-enumeration in host mode") Change-Id: I4f02f429019771699993358059731f949140eb08 Signed-off-by: William Wu --- drivers/usb/dwc3/core.c | 21 +++++---------------- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/host.c | 5 ++++- drivers/usb/host/xhci-plat.c | 4 ++++ drivers/usb/host/xhci.c | 27 +++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 1 + 6 files changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a51717be794a..535fb9a9b9bb 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -787,11 +787,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) phy_power_off(dwc->usb3_generic_phy); clk_bulk_disable(dwc->num_clks, dwc->clks); clk_bulk_unprepare(dwc->num_clks, dwc->clks); - /* - * We're resetting only the device side, it can avoid to reset the DWC3 - * controller when resume from PM suspend which may cause the usb - * device to be reenumerated. - */ + if (!dwc->drd_connected && dwc->dr_mode == USB_DR_MODE_OTG) reset_control_assert(dwc->reset); } @@ -1437,6 +1433,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,dis_metastability_quirk"); dwc->needs_fifo_resize = device_property_read_bool(dev, "snps,tx-fifo-resize"); + dwc->xhci_warm_reset_on_suspend_quirk = device_property_read_bool(dev, + "snps,xhci-warm-reset-on-suspend-quirk"); dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; @@ -1706,11 +1704,6 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) { int ret; - /* - * We're resetting only the device side, it can avoid to reset the DWC3 - * controller when resume from PM suspend which may cause the usb - * device to be reenumerated. - */ if (!dwc->drd_connected && dwc->dr_mode == USB_DR_MODE_OTG) { ret = reset_control_deassert(dwc->reset); if (ret) @@ -1979,10 +1972,8 @@ static int dwc3_suspend(struct device *dev) */ dwc->link_state = dwc3_gadget_get_link_state(dwc); if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && - dwc->link_state == DWC3_LINK_STATE_RX_DET) { + dwc->link_state == DWC3_LINK_STATE_RX_DET) phy_power_off(dwc->usb3_generic_phy); - reset_control_assert(dwc->reset); - } pinctrl_pm_select_sleep_state(dev); @@ -2000,10 +1991,8 @@ static int dwc3_resume(struct device *dev) pinctrl_pm_select_default_state(dev); if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && - dwc->link_state == DWC3_LINK_STATE_RX_DET) { - reset_control_deassert(dwc->reset); + dwc->link_state == DWC3_LINK_STATE_RX_DET) phy_power_on(dwc->usb3_generic_phy); - } ret = dwc3_resume_common(dwc, PMSG_RESUME); if (ret) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 8686cab787e1..2f4d79d53a33 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1031,6 +1031,8 @@ struct dwc3_scratchpad_array { * @xhci_trb_ent_quirk: set if need to enable the Evaluate Next TRB(ENT) flag in the TRB data structure to force xHC to pre-fetch the next TRB of a TD. + * @xhci_warm_reset_on_suspend_quirk: set if need to do a warm port reset + * for xHC USB3 port upon suspend. * @dis_u3_autosuspend_quirk: set if the we want to disable usb3 autosuspend * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value @@ -1227,6 +1229,7 @@ struct dwc3 { unsigned dis_tx_ipgap_linecheck_quirk:1; unsigned xhci_slow_suspend_quirk:1; unsigned xhci_trb_ent_quirk:1; + unsigned xhci_warm_reset_on_suspend_quirk:1; unsigned dis_u3_autosuspend_quirk:1; unsigned tx_de_emphasis_quirk:1; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 4f00cc7d7b1a..d745e39e18da 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -46,7 +46,7 @@ out: int dwc3_host_init(struct dwc3 *dwc) { - struct property_entry props[6]; + struct property_entry props[7]; struct platform_device *xhci; int ret, irq; struct resource *res; @@ -116,6 +116,9 @@ int dwc3_host_init(struct dwc3 *dwc) if (dwc->revision <= DWC3_REVISION_300A) props[prop_idx++].name = "quirk-broken-port-ped"; + if (dwc->xhci_warm_reset_on_suspend_quirk) + props[prop_idx++].name = "xhci-warm-reset-on-suspend"; + if (prop_idx) { ret = platform_device_add_properties(xhci, props); if (ret) { diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index c113757465ee..ed992dfa8a03 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -297,6 +297,10 @@ static int xhci_plat_probe(struct platform_device *pdev) if (device_property_read_bool(tmpdev, "usb3-dis-autosuspend")) xhci->quirks |= XHCI_DIS_AUTOSUSPEND; + if (device_property_read_bool(tmpdev, + "xhci-warm-reset-on-suspend")) + xhci->quirks |= XHCI_WARM_RESET_ON_SUSPEND; + device_property_read_u32(tmpdev, "imod-interval-ns", &xhci->imod_interval); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 52b8338fdbae..3ad2947bfe41 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -961,6 +961,26 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) return false; } +static void xhci_warm_port_reset_quirk(struct xhci_hcd *xhci) +{ + struct xhci_port **ports; + int port_index; + u32 portsc; + + port_index = xhci->usb3_rhub.num_ports; + ports = xhci->usb3_rhub.ports; + while (port_index--) { + portsc = readl(ports[port_index]->addr); + /* Do warm port reset if no USB3 device connected */ + if (!(portsc & PORT_CONNECT)) { + portsc |= PORT_WR; + writel(portsc, ports[port_index]->addr); + /* flush write */ + readl(ports[port_index]->addr); + } + } +} + /* * Stop HC (not bus-specific) * @@ -988,6 +1008,13 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) if (!do_wakeup) xhci_disable_port_wake_on_bits(xhci); + /* + * Do a warm reset for USB3 port to resets the USB3 link, + * forcing the link to enter the Rx.Detect state. + */ + if (xhci->quirks & XHCI_WARM_RESET_ON_SUSPEND) + xhci_warm_port_reset_quirk(xhci); + /* Don't poll the roothubs on bus suspend. */ xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ad35f109bded..f5c4c5c379e1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1878,6 +1878,7 @@ struct xhci_hcd { #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) #define XHCI_TRB_ENT_QUIRK BIT_ULL(36) #define XHCI_DIS_AUTOSUSPEND BIT_ULL(37) +#define XHCI_WARM_RESET_ON_SUSPEND BIT_ULL(38) unsigned int num_active_eps; unsigned int limit_active_eps;