diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index eb244046b4ac..b06b0d083a6e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -284,6 +284,14 @@ disconnect: } break; case DWC3_GCTL_PRTCAP_DEVICE: + if (dwc->connected) { + ret = wait_for_completion_timeout(&dwc->discon_done, + msecs_to_jiffies(DWC3_DISCON_TIMEOUT)); + if (!ret) + dev_warn(dwc->dev, + "timed out waiting for disconnect\n"); + } + break; case DWC3_GCTL_PRTCAP_OTG: break; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c98fbfe14478..4f23717d2299 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -32,6 +32,7 @@ #define DWC3_MSG_MAX 500 /* Global constants */ +#define DWC3_DISCON_TIMEOUT 1000 /* ms */ #define DWC3_PULL_UP_TIMEOUT 500 /* ms */ #define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */ #define DWC3_EP0_SETUP_SIZE 512 @@ -929,6 +930,7 @@ struct dwc3_scratchpad_array { * @ep0_usb_req: dummy req used while handling STD USB requests * @scratch_addr: dma address of scratchbuf * @ep0_in_setup: one control transfer is completed and enter setup phase + * @discon_done: disconnect event is completed * @lock: for synchronizing * @dev: pointer to our struct device * @sysdev: pointer to the DMA-capable device @@ -1071,6 +1073,7 @@ struct dwc3 { dma_addr_t scratch_addr; struct dwc3_request ep0_usb_req; struct completion ep0_in_setup; + struct completion discon_done; /* device lock */ spinlock_t lock; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1bac00c1512b..1c3d3c00c990 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3021,8 +3021,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (epnum == 0 || epnum == 1) { if (!dwc->connected && - event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE) + event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE) { + reinit_completion(&dwc->discon_done); dwc->connected = true; + } dwc3_ep0_interrupt(dwc, event); return; } @@ -3187,6 +3189,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); dwc->connected = false; + complete(&dwc->discon_done); } static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) @@ -3786,6 +3789,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) } init_completion(&dwc->ep0_in_setup); + init_completion(&dwc->discon_done); dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.speed = USB_SPEED_UNKNOWN;