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 <william.wu@rock-chips.com>
Change-Id: Ie6086d469b5d24f841532992e4e95f0c91c37022
This commit is contained in:
William Wu
2021-10-19 16:17:35 +08:00
committed by Tao Huang
parent 02456ae203
commit 11f9c06cc3
3 changed files with 16 additions and 1 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;