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: c9b15ba1eb ("usb: dwc3: core: prevent re-enumeration in host mode")
Change-Id: I4f02f429019771699993358059731f949140eb08
Signed-off-by: William Wu <william.wu@rock-chips.com>
This commit is contained in:
William Wu
2020-06-02 15:59:32 +08:00
committed by Tao Huang
parent 89d01c470d
commit 77632b0961
6 changed files with 44 additions and 17 deletions

View File

@@ -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)

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;