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; }; /* -------------------------------------------------------------------------- */