diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d080b5dc3358..33db59228a56 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -796,8 +796,7 @@ out: static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; - - dwc3_stop_active_transfer(dep, true, false); + int ret = -EINVAL; if (dep->number == 0 && dwc->ep0state != EP0_SETUP_PHASE) { unsigned int dir; @@ -813,6 +812,13 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) dwc->eps[1]->trb_enqueue = 0; } + ret = dwc3_stop_active_transfer(dep, true, false); + if (ret < 0) { + dbg_log_string("transfer not stopped for %s(%d), status:%d", + dep->name, dep->number, ret); + return; + } + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) udelay(2000); @@ -2185,7 +2191,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) dwc->softconnect = is_on; if (((dwc->dr_mode > USB_DR_MODE_HOST) && !dwc->vbus_active) - || !dwc->gadget_driver) { + || !dwc->gadget_driver || dwc->err_evt_seen) { /* * Need to wait for vbus_session(on) from otg driver or to * the udc_start. @@ -2249,8 +2255,15 @@ 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__); - ret = dwc3_device_core_soft_reset(dwc); + /* + * If we fail to stop the controller then mark it as an error + * event since it can lead the controller to go into an unknown + * state. + */ + dbg_log_string("%s: error event seen\n", __func__); + dwc->err_evt_seen = true; + dwc3_notify_event(dwc, DWC3_CONTROLLER_ERROR_EVENT, 0); + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0); } enable_irq(dwc->irq); @@ -2258,7 +2271,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) pm_runtime_put_autosuspend(dwc->dev); dbg_event(0xFF, "Pullup put", atomic_read(&dwc->dev->power.usage_count)); - return ret; + return 0; } static void dwc3_gadget_enable_irq(struct dwc3 *dwc) @@ -3150,7 +3163,7 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) } } -void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) +int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { struct dwc3 *dwc = dep->dwc; struct dwc3_gadget_ep_cmd_params params; @@ -3159,7 +3172,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) - return; + return 0; dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER, dep->number); @@ -3213,6 +3226,8 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) udelay(100); + + return ret; } EXPORT_SYMBOL(dwc3_stop_active_transfer); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 828e0f71a5be..e150fdb52a01 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -116,7 +116,7 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); -void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); +int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); static inline dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, struct dwc3_trb *trb)