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;