diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 401b9271a455..1bedfb92fd0d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -811,25 +811,6 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) dwc3_stop_active_transfer(dep, true, false); - /* - giveback all requests to gadget driver */ - while (!list_empty(&dep->started_list)) { - req = next_request(&dep->started_list); - - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); - } - - while (!list_empty(&dep->pending_list)) { - req = next_request(&dep->pending_list); - - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); - } - - while (!list_empty(&dep->cancelled_list)) { - req = next_request(&dep->cancelled_list); - - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); - } - if (dep->number == 1 && dwc->ep0state != EP0_SETUP_PHASE) { unsigned int dir; @@ -844,6 +825,25 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) dwc->eps[1]->trb_enqueue = 0; } + /* - giveback all requests to gadget driver */ + while (!list_empty(&dep->started_list)) { + req = next_request(&dep->started_list); + if (req) + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + + while (!list_empty(&dep->pending_list)) { + req = next_request(&dep->pending_list); + if (req) + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); + if (req) + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + dbg_log_string("DONE for %s(%d)", dep->name, dep->number); } @@ -1980,6 +1980,38 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc) } } +/** + * dwc3_device_core_soft_reset - Issues device core soft reset + * @dwc: pointer to our context structure + */ +static int dwc3_device_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + int retries = 10; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= DWC3_DCTL_CSFTRST; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + goto done; + + usleep_range(1000, 1100); + } while (--retries); + + dev_err(dwc->dev, "%s timedout\n", __func__); + + return -ETIMEDOUT; + +done: + /* phy sync delay as per data book */ + msleep(50); + + return 0; +} + static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg, reg1; @@ -2142,6 +2174,10 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); + if (!is_on && ret == -ETIMEDOUT) { + dev_err(dwc->dev, "%s: Core soft reset...\n", __func__); + dwc3_device_core_soft_reset(dwc); + } enable_irq(dwc->irq); pm_runtime_mark_last_busy(dwc->dev); @@ -2226,6 +2262,7 @@ static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active) { struct dwc3 *dwc = gadget_to_dwc(_gadget); unsigned long flags; + int ret = 0; if (dwc->dr_mode != USB_DR_MODE_OTG) return -EPERM; @@ -2233,6 +2270,11 @@ static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active) is_active = !!is_active; dbg_event(0xFF, "VbusSess", is_active); + + disable_irq(dwc->irq); + + flush_work(&dwc->bh_work); + spin_lock_irqsave(&dwc->lock, flags); /* Mark that the vbus was powered */ @@ -2248,9 +2290,9 @@ static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active) * Both vbus was activated by otg and pullup was * signaled by the gadget driver. */ - dwc3_gadget_run_stop(dwc, 1, false); + ret = dwc3_gadget_run_stop(dwc, 1, false); } else { - dwc3_gadget_run_stop(dwc, 0, false); + ret = dwc3_gadget_run_stop(dwc, 0, false); } } @@ -2264,6 +2306,13 @@ static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active) } spin_unlock_irqrestore(&dwc->lock, flags); + if (!is_active && ret == -ETIMEDOUT) { + dev_err(dwc->dev, "%s: Core soft reset...\n", __func__); + dwc3_device_core_soft_reset(dwc); + } + + enable_irq(dwc->irq); + return 0; }