- The loop in dwc3_gadget_set_link_state was using a udelay(500), which is a long time to delay in interrupt context. Change it to udelay(5) and increase the loop count to match. - dwc3_gadget_wakeup was looping on !time_after(jiffies, timeout)) with interrupts disabled, which is wrong. Fix it. - dwc3_gadget_ep_set_wedge and dwc3_gadget_set_selfpowered were modifying dwc->flags/dwc->is_selfpowered without taking the lock. Since those modifications are non-atomic, that could cause other flags to be corrupted. Fix them both to take the lock. - Transfer resource index is cleared in hardware when XFERCOMPLETE event is generated, so clear the driver's res_trans_idx variable immediately after that event is received. The upcoming hibernation patches also need this change. Signed-off-by: Paul Zimmerman <paulz@xxxxxxxxxxxx> --- drivers/usb/dwc3/core.c | 2 +- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/dwc3-pci.c | 4 +++- drivers/usb/dwc3/ep0.c | 1 + drivers/usb/dwc3/gadget.c | 24 +++++++++++++++++------- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3b1d956..7124ec0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -351,7 +351,7 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) dwc3_cache_hwparams(dwc); reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg &= ~DWC3_GCTL_SCALEDOWN(3); + reg &= ~DWC3_GCTL_SCALEDOWN_MASK; reg &= ~DWC3_GCTL_DISSCRAMBLE; switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 7c4e0b5..6c7945b 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -161,6 +161,7 @@ #define DWC3_GCTL_CORESOFTRESET (1 << 11) #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) #define DWC3_GCTL_DSBLCLKGTNG (1 << 0) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index df69169..1c64d6d 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -79,6 +79,8 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, pci_set_power_state(pci, PCI_D0); pci_set_master(pci); + ret = -ENOMEM; + devid = dwc3_get_device_id(); if (devid < 0) goto err2; @@ -144,9 +146,9 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci) { struct dwc3_pci *glue = pci_get_drvdata(pci); - dwc3_put_device_id(glue->dwc3->id); platform_device_unregister(glue->dwc3); pci_set_drvdata(pci, NULL); + dwc3_put_device_id(glue->dwc3->id); pci_disable_device(pci); kfree(glue); } diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index bf48e2c..25910e2 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -617,6 +617,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; dep->flags &= ~DWC3_EP_BUSY; + dep->res_trans_idx = 0; dwc->setup_packet_pending = false; switch (dwc->ep0state) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 983d2b4..3b0ec64 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -93,11 +93,11 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) * @state: the state to put link into * * Caller should take care of locking. This function will - * return 0 on success or -EINVAL. + * return 0 on success or -ETIMEDOUT. */ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { - int retries = 100; + int retries = 10000; u32 reg; reg = dwc3_readl(dwc->regs, DWC3_DCTL); @@ -111,11 +111,10 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) while (--retries) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); - /* in HS, means ON */ if (DWC3_DSTS_USBLNKST(reg) == state) return 0; - udelay(500); + udelay(5); } dev_vdbg(dwc->dev, "link state change request timed out\n"); @@ -1134,8 +1133,12 @@ out: static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) { struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + spin_lock_irqsave(&dwc->lock, flags); dep->flags |= DWC3_EP_WEDGE; + spin_unlock_irqrestore(&dwc->lock, flags); return dwc3_gadget_ep_set_halt(ep, 1); } @@ -1238,6 +1241,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) /* poll until Link State changes to ON */ timeout = jiffies + msecs_to_jiffies(100); + spin_unlock_irqrestore(&dwc->lock, flags); + while (!time_after(jiffies, timeout)) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -1246,6 +1251,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) break; } + spin_lock_irqsave(&dwc->lock, flags); + if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { dev_err(dwc->dev, "failed to send remote wakeup\n"); ret = -EINVAL; @@ -1261,8 +1268,11 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, int is_selfpowered) { struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + spin_lock_irqsave(&dwc->lock, flags); dwc->is_selfpowered = !!is_selfpowered; + spin_unlock_irqrestore(&dwc->lock, flags); return 0; } @@ -1559,10 +1569,8 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy) { + if (clean_busy) dep->flags &= ~DWC3_EP_BUSY; - dep->res_trans_idx = 0; - } /* * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. @@ -1673,6 +1681,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: + dep->res_trans_idx = 0; + if (usb_endpoint_xfer_isoc(dep->desc)) { dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", dep->name); -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html