Hi Felipe, I hope you are doing well. My team and I are frequently experiencing an issue with the dwc3 in our CherryTrail SoC where we encounter an LFPS Polling timeout while our device is being enumerated. We configured the dwc3 as an ethernet gadget using configfs and the ecm and RNDIS functions. The dwc3 transitions to U3 after configuration as expected. Only once we connect our device to a USB host do we see the link state transition to Polling. We are assuming LFPS Polling times out because the link_state file in debugfs shows the link has transitioned to compliance mode only after entering LFPS.Polling, and we recently learned that compliance mode is triggered by a timeout during LFPS.Polling. This issue is not 100% reproducible, but is occuring rather frequently at the moment. We're unsure of the root cause of the issue as well. One culprit might be the USB SuperSpeed Redriver we use in our design. We would like to disable compliance mode in the meantime to allow other team members to continue developing and testing USB device mode features while we dig into the root cause of the issue. Is there a proper way to disable compliance mode entirely? If not, is there some mechanism we could implement to reset the dwc3 when we enter compliance mode? I attempted some sort of mechanism to reset the link state, but it does not seem to help the issue. I've attached my patch and the trace events for my attempted workaround to this email. My initial approach was to transition the link from Compliance -> SS.Disabled -> Rx.Detect when we detect we've entered compliance mode. The traces show that the dwc3 just enters LFPS.Polling and subsequently enters compliance mode, despite the link being reset. Do you have any ideas on how we might work around compliance mode in the meantime? Thanks in advance for your input! Cheers, Rob Weber
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 53b26e9..07d8412 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -48,6 +48,91 @@ #include "debug.h" #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ +#define COMP_MODE_RECOVERY_MSECS 2000 + +static void compliance_mode_recovery(unsigned long arg) +{ + struct dwc3 *dwc3; + int state; + unsigned long flags; + + dwc3 = (struct dwc3 *) arg; + + dwc3_trace(trace_dwc3_core, "Tick! Checking for compliance mode\n"); + + // Read link state + + spin_lock_irqsave(&dwc3->lock, flags); + state = dwc3_gadget_get_link_state(dwc3); + + // if in compliance mode or loopback mode, we need to revert the link to normal operation + // Couple options for resetting link state + // * setting link state to RESET - NOT WORKING + // * setting linsk state to SS disabled or RX detect + // * try a hard reset (we're afraid we will have to reconfigure the gadget with this method) + if (state == DWC3_LINK_STATE_CMPLY || state == DWC3_LINK_STATE_LPBK) { + dwc3_trace(trace_dwc3_core, "Compliance Mode detected. Attempting recovery routine\n"); + printk("dwc3 compliance mode detected. Attempting recovery\n"); + + state = dwc3_gadget_set_link_state(dwc3, DWC3_LINK_STATE_SS_DIS); + if (state < 0) { + dwc3_trace(trace_dwc3_core, "Compliance -> SS.Disabled transition failed: %d (Timed out?)\n", state); + printk("Compliance -> SS.Disabled transition failed: %d (Timed out?)\n", state); + + } + + udelay(1000); + state = dwc3_gadget_set_link_state(dwc3, DWC3_LINK_STATE_RX_DET); + if (state < 0) { + dwc3_trace(trace_dwc3_core, "SS.Disabled -> Rx.Detect transition failed: %d (Timed out?)\n", state); + printk("SS.Disabled -> Rx.Detect transition failed: %d (Timed out?)\n", state); + } + } + + spin_unlock_irqrestore(&dwc3->lock, flags); +/* + struct *xhci; + struct usb_hcd *hcd; + u32 temp; + int i; + + xhci = (struct xhci_hcd *)arg; + + for (i = 0; i < xhci->num_usb3_ports; i++) { + temp = xhci_readl(xhci, xhci->usb3_ports[i]); + if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) { + xhci_dbg(xhci, "Compliance Mode Detected->Port %d!\n", + i + 1); + xhci_dbg(xhci, "Attempting Recovery routine!\n"); + hcd = xhci->shared_hcd; + + if (hcd->state == HC_STATE_SUSPENDED) + usb_hcd_resume_root_hub(hcd); + + usb_hcd_poll_rh_status(hcd); + } + } + + if (xhci->port_status_u0 != ((1 << xhci->num_usb3_ports)-1)) + mod_timer(&xhci->comp_mode_recovery_timer, + jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS)); +*/ + /* TODO: figure out when we don't need to keep re-enabling this timer anymore */ + mod_timer(&dwc3->comp_mode_recovery_timer, + jiffies + msecs_to_jiffies(COMP_MODE_RECOVERY_MSECS)); +} + +static void compliance_mode_recovery_timer_init(struct dwc3 *dwc3) +{ + setup_timer(&dwc3->comp_mode_recovery_timer, + compliance_mode_recovery, (unsigned long)dwc3); + + dwc3->comp_mode_recovery_timer.expires = jiffies + + msecs_to_jiffies(COMP_MODE_RECOVERY_MSECS); + + add_timer(&dwc3->comp_mode_recovery_timer); + dwc3_trace(trace_dwc3_core, "Compliance Mode Recovery Timer Initialized.\n"); +} /** * dwc3_get_dr_mode - Validates and sets dr_mode @@ -612,6 +697,8 @@ static void dwc3_core_exit(struct dwc3 *dwc) usb_phy_set_suspend(dwc->usb3_phy, 1); phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb3_generic_phy); + + del_timer_sync(&dwc->comp_mode_recovery_timer); } /** @@ -786,6 +873,9 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); } + /* Enable timer to check for and recover from entering compliance mode */ + compliance_mode_recovery_timer_init(dwc); + return 0; err4: diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index affb3d7..c2c9077 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -979,6 +979,8 @@ struct dwc3 { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + + struct timer_list comp_mode_recovery_timer; }; /* -------------------------------------------------------------------------- */
# tracer: nop # # entries-in-buffer/entries-written: 220/220 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | <idle>-0 [003] ..s1 178.444339: dwc3_core: Tick! Checking for compliance mode <idle>-0 [003] d.s2 178.444352: dwc3_core: Compliance Mode detected. Attempting recovery routine irq/23-dwc3-1115 [002] d..1 178.445380: dwc3_event: event (00140301): Link Change [SS.Disabled] irq/23-dwc3-1115 [002] d..1 178.445826: dwc3_event: event (00150301): Link Change [RX.Detect] irq/23-dwc3-1115 [002] d..1 178.445832: dwc3_event: event (00170301): Link Change [Polling] <idle>-0 [003] ..s1 180.492293: dwc3_core: Tick! Checking for compliance mode <idle>-0 [003] d.s2 180.492306: dwc3_core: Compliance Mode detected. Attempting recovery routine irq/23-dwc3-1115 [002] d..1 180.493333: dwc3_event: event (00140301): Link Change [SS.Disabled] irq/23-dwc3-1115 [002] d..1 180.493636: dwc3_event: event (00150301): Link Change [RX.Detect] irq/23-dwc3-1115 [002] d..1 180.493641: dwc3_event: event (00170301): Link Change [Polling] <idle>-0 [003] ..s1 182.540350: dwc3_core: Tick! Checking for compliance mode <idle>-0 [003] d.s2 182.540362: dwc3_core: Compliance Mode detected. Attempting recovery routine irq/23-dwc3-1115 [002] d..1 182.541392: dwc3_event: event (00140301): Link Change [SS.Disabled] irq/23-dwc3-1115 [002] d..1 182.541976: dwc3_event: event (00150301): Link Change [RX.Detect] irq/23-dwc3-1115 [002] d..1 182.541982: dwc3_event: event (00170301): Link Change [Polling] <idle>-0 [003] .Ns1 184.588211: dwc3_core: Tick! Checking for compliance mode <idle>-0 [003] dNs2 184.588232: dwc3_core: Compliance Mode detected. Attempting recovery routine