From 11f9c06cc3bf0dcfe65878fb5db7896621f943fc Mon Sep 17 00:00:00 2001 From: William Wu Date: Tue, 19 Oct 2021 16:17:35 +0800 Subject: [PATCH] usb: dwc3: gadget: wait for disconnect done before runtime suspend The dwc3 gadget call pm_runtime_get in the __dwc3_gadget_ep_queue() to increment the device's usage count, and call pm_runtime_put in the dwc3_gadget_giveback() to decrement the device's usage count if the request is completed successfully or disconnection happens. Test on RK3399 IND Type-C interface with HUSB311 (Type-C port controller chip), if plug out the USB cable, the extcon notifier from tcpm framework comes earlier than the dwc3 disconnect event. This cause the dwc3 fails to enter the runtime suspend immediately in the disconnection process of __dwc3_set_mode(). This patch waits for the dwc3 disconnect event done before doing pm_runtime_put_sync_suspend. If it takes 1000ms to wait for the disconnect event timeout, just print warning log and still do the pm_runtime_put_sync_suspend, and then the dwc3 can also enter runtime suspend automatically (DWC3_DEFAULT_AUTOSUSPEND_DELAY is 5000ms) after the disconnect event done. Signed-off-by: William Wu Change-Id: Ie6086d469b5d24f841532992e4e95f0c91c37022 --- drivers/usb/dwc3/core.c | 8 ++++++++ drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/gadget.c | 6 +++++- 3 files changed, 16 insertions(+), 1 deletion(-) 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;