2022-02-03 08:26:50

by Wesley Cheng

[permalink] [raw]
Subject: [RFC PATCH 0/3] Fix enumeration issues during composition switching

This patch series addresses a few enumeration issues being seen during fast
composition switching test cases:
- Missing DWC3 core soft reset before run/stop enable. Recommended by the
Synopsis databook to do this during the pullup enable case.
- Endxfer command timeouts leading to controller halt failures in the pullup
disable path.
- Endxfer command timeouts leading to EP dequeue to never return a completion
to the function drivers, which can cause to unbind issues depending on
function driver implementation.

With regards to the endxfer timeout, it was communicated to us that if there is
a pending setup/control transfer in progress, the DWC3 controller is unable to
service the endxfer command for other endpoints. USB bus sniffer and USB ftrace
logs confirmed that when the endxfer timeouts occurred, there was a SETUP token
being sent by the host, while the pullup routine was in progress, as during the
pullup disable, we should not see any ftrace logs since IRQs are disabled.

It was recommended by Synopsis to ensure that packets on EP0 are always handled,
and the only way to drain the dedicated control transfer internal memory is to
issue a startxfer command. With this in mind, the pullup disable case is able
to discard any cached setup packets (as disconnect would pursue), so the
approach to issue a startxfer after an unsuccessful endxfer is possible.
Test logs show the subsequent endxfer is successful:

[004] d..1 15631.849982: dwc3_gadget_ep_cmd: ep1out: cmd 'End Transfer' [20c08] params 00000000 00000000 00000000 --> status: Timed Out
[004] d..1 15631.850008: dwc3_prepare_trb: ep0out: trb ffffffc019dad000 (E0:D0) buf 00000000efffa000 size 8 ctrl 00000c23 (HLcs:SC:setup)
[004] d..1 15631.850024: dwc3_gadget_ep_cmd: ep0out: cmd 'Start Transfer' [406] params 00000000 efffa000 00000000 --> status: Successful
[004] d..1 15631.857380: dwc3_gadget_ep_cmd: ep1out: cmd 'End Transfer' [20c08] params 00000000 00000000 00000000 --> status: Successful
[004] d..1 15631.857409: dwc3_gadget_giveback: ep1out: req ffffff8789212300 length 0/16384 zsI ==> -108

Attempts to ensure EP0 transfers don't happen during pullup disable were added
as well.

For the dequeue path, since usb_ep_dequeue() can be called from the function
driver at any time, the same approach can not be used. In this case, if there
is an endxfer failure observed when dequeuing a request, mark the endpoint w/ a
flag, which will be later checked when the control transfer is complete. From
there it will traverse through all EPs to service the ones that need to
re-issue the endxfer command. This logic will only trigger if there is at least
one EP that needs servicing.

Wesley Cheng (3):
usb: dwc3: Flush pending SETUP data during stop active xfers
usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue
usb: dwc3: Issue core soft reset before enabling run/stop

drivers/usb/dwc3/core.c | 4 +-
drivers/usb/dwc3/core.h | 14 ++++++
drivers/usb/dwc3/ep0.c | 23 ++++++---
drivers/usb/dwc3/gadget.c | 100 +++++++++++++++++++++++++++++++++-----
4 files changed, 118 insertions(+), 23 deletions(-)


2022-02-03 09:22:33

by Wesley Cheng

[permalink] [raw]
Subject: [RFC PATCH 3/3] usb: dwc3: Issue core soft reset before enabling run/stop

It is recommended by the Synopsis databook to issue a DCTL.CSftReset
when reconnecting from a device-initiated disconnect routine. This
resolves issues with enumeration during fast composition switching
cases, which result in an unknown device on the host.

Signed-off-by: Wesley Cheng <[email protected]>
---
drivers/usb/dwc3/core.c | 4 +---
drivers/usb/dwc3/core.h | 2 ++
drivers/usb/dwc3/gadget.c | 11 +++++++++++
3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f4c09951b517..d128c7f22b01 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -115,8 +115,6 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
dwc->current_dr_role = mode;
}

-static int dwc3_core_soft_reset(struct dwc3 *dwc);
-
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
@@ -261,7 +259,7 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
-static int dwc3_core_soft_reset(struct dwc3 *dwc)
+int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
int retries = 1000;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index d418ed55a566..e15b83a6f094 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1527,6 +1527,8 @@ bool dwc3_has_imod(struct dwc3 *dwc);
int dwc3_event_buffers_setup(struct dwc3 *dwc);
void dwc3_event_buffers_cleanup(struct dwc3 *dwc);

+int dwc3_core_soft_reset(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1efa30907d42..94cfbe32da8d 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2572,6 +2572,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
dwc->ev_buf->length;
}
} else {
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 1.90a
+ * Section 4.1.9, it specifies that for a reconnect after a
+ * device-initiated disconnect requires a core soft reset
+ * (DCTL.CSftRst) before enabling the run/stop bit.
+ */
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_core_soft_reset(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc);
}

2022-02-04 01:32:01

by Wesley Cheng

[permalink] [raw]
Subject: [RFC PATCH 1/3] usb: dwc3: Flush pending SETUP data during stop active xfers

While running the pullup disable sequence, if there are pending SETUP
transfers stored in the controller, then the endxfer commands will
fail w/ ETIMEDOUT. As a suggestion from SNPS, in order to drain the
SETUP packets, SW needs to issue a STARTXFER command. After issuing
the STARTXFER, and retrying the ENDXFER, the command should succeed.
Else, if the endpoints are not properly stopped, the controller halt
sequence will fail as well.

One limitation is that the current logic will drop the SETUP data
being received (ie dropping the SETUP packet), however, it should be
acceptable in the pullup disable case, as the device is eventually
going to disconnect from the host.

Signed-off-by: Wesley Cheng <[email protected]>
---
drivers/usb/dwc3/core.h | 7 +++++++
drivers/usb/dwc3/ep0.c | 21 ++++++++++++--------
drivers/usb/dwc3/gadget.c | 42 ++++++++++++++++++++++++++++++++++-----
3 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e1cc3f7398fb..a124694c0038 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1546,6 +1546,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
u32 param);
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1567,6 +1569,11 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
{ }
+static inline void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+{ }
+static inline void dwc3_ep0_end_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep)
+{ }
#endif

#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 658739410992..eb677b888610 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret;

spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc || !dwc->pullups_connected) {
+ if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
ret = -ESHUTDOWN;
@@ -218,19 +218,21 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
return ret;
}

-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
struct dwc3_ep *dep;

/* reinitialize physical ep1 */
dep = dwc->eps[1];
dep->flags = DWC3_EP_ENABLED;
+ dep->trb_enqueue = 0;

/* stall is always issued on EP0 */
dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1, false);
dep->flags = DWC3_EP_ENABLED;
dwc->delayed_status = false;
+ dep->trb_enqueue = 0;

if (!list_empty(&dep->pending_list)) {
struct dwc3_request *req;
@@ -240,7 +242,9 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
}

dwc->ep0state = EP0_SETUP_PHASE;
- dwc3_ep0_out_start(dwc);
+ complete(&dwc->ep0_in_setup);
+ if (dwc->softconnect)
+ dwc3_ep0_out_start(dwc);
}

int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
@@ -272,8 +276,6 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
struct dwc3_ep *dep;
int ret;

- complete(&dwc->ep0_in_setup);
-
dep = dwc->eps[0];
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
@@ -922,7 +924,9 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
dwc->setup_packet_pending = true;

dwc->ep0state = EP0_SETUP_PHASE;
- dwc3_ep0_out_start(dwc);
+ complete(&dwc->ep0_in_setup);
+ if (dwc->softconnect)
+ dwc3_ep0_out_start(dwc);
}

static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
@@ -1073,7 +1077,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
}

-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
@@ -1083,7 +1087,8 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
return;

cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= dwc->connected ? 0 : DWC3_DEPCMD_HIPRI_FORCERM;
+ cmd |= dwc->connected ? DWC3_DEPCMD_CMDIOC : 0;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 520031ba38aa..19b8d837e9d0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -885,12 +885,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);

- if (usb_endpoint_xfer_control(desc))
- goto out;
-
/* Initialize the TRB ring */
dep->trb_dequeue = 0;
dep->trb_enqueue = 0;
+
+ if (usb_endpoint_xfer_control(desc))
+ goto out;
+
memset(dep->trb_pool, 0,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM);

@@ -2463,7 +2464,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
* Per databook, when we want to stop the gadget, if a control transfer
* is still in process, complete it and get the core into setup phase.
*/
- if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+ if ((!is_on && (dwc->ep0state != EP0_SETUP_PHASE ||
+ dwc->ep0_next_event != DWC3_EP0_COMPLETE))) {
reinit_completion(&dwc->ep0_in_setup);

ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
@@ -2506,6 +2508,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
u32 count;

dwc->connected = false;
+
+ /*
+ * Ensure no pending data/setup stages, and disable ep0 to
+ * block further EP0 transactions before stopping pending
+ * transfers.
+ */
+ dwc3_ep0_end_control_data(dwc, dwc->eps[1]);
+ dwc3_ep0_stall_and_restart(dwc);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+
/*
* In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
* Section 4.1.8 Table 4-7, it states that for a device-initiated
@@ -3587,8 +3600,10 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc = dep->dwc;
u32 cmd;
int ret;
+ int retries = 1;

if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
@@ -3620,7 +3635,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
*
* This mode is NOT available on the DWC_usb31 IP.
*/
-
+retry:
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
@@ -3628,6 +3643,23 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
WARN_ON_ONCE(ret);
+ if (ret == -ETIMEDOUT) {
+ if (!dwc->connected) {
+ /*
+ * While the controller is in an active setup/control
+ * transfer, the HW is unable to service other eps
+ * potentially leading to an endxfer command timeout.
+ * It was recommended to ensure that there are no
+ * pending/cached setup packets stored in internal
+ * memory. Only way to achieve this is to issue another
+ * start transfer, and retry.
+ */
+ if (retries--) {
+ dwc3_ep0_out_start(dwc);
+ goto retry;
+ }
+ }
+ }
dep->resource_index = 0;

if (!interrupt)

2022-02-04 12:32:24

by Wesley Cheng

[permalink] [raw]
Subject: [RFC PATCH 2/3] usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue

If the request being dequeued is currently active, then the current
logic is to issue a stop transfer command, and allow the command
completion to cleanup the cancelled list. The DWC3 controller will
run into endxfer command timeouts if there is an ongoing EP0
transaction. If this is the case, wait for the EP0 completion event
before proceeding to retry the endxfer command again.

Signed-off-by: Wesley Cheng <[email protected]>
---
drivers/usb/dwc3/core.h | 5 +++++
drivers/usb/dwc3/ep0.c | 2 ++
drivers/usb/dwc3/gadget.c | 47 +++++++++++++++++++++++++++++++++------
3 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index a124694c0038..d418ed55a566 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -733,6 +733,7 @@ struct dwc3_ep {
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
+#define DWC3_EP_PENDING_DEQUEUE BIT(13)

/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -1267,6 +1268,7 @@ struct dwc3 {
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
+ unsigned ep_dequeue_pending:1;
unsigned has_hibernation:1;
unsigned sysdev_is_parent:1;
unsigned has_lpm_erratum:1;
@@ -1548,6 +1550,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
+int dwc3_gadget_check_ep_dequeue(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1574,6 +1577,8 @@ static inline void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
static inline void dwc3_ep0_end_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep)
{ }
+static inline int dwc3_gadget_check_ep_dequeue(struct dwc3 *dwc)
+{ return 0; }
#endif

#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index eb677b888610..cc3339e4308f 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -243,6 +243,7 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)

dwc->ep0state = EP0_SETUP_PHASE;
complete(&dwc->ep0_in_setup);
+ dwc3_gadget_check_ep_dequeue(dwc);
if (dwc->softconnect)
dwc3_ep0_out_start(dwc);
}
@@ -925,6 +926,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,

dwc->ep0state = EP0_SETUP_PHASE;
complete(&dwc->ep0_in_setup);
+ dwc3_gadget_check_ep_dequeue(dwc);
if (dwc->softconnect)
dwc3_ep0_out_start(dwc);
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 19b8d837e9d0..1efa30907d42 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -654,7 +654,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}

-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+static int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);

/**
@@ -1081,6 +1081,31 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return ret;
}

+int dwc3_gadget_check_ep_dequeue(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret = 0;
+ int i;
+
+ if (!dwc->ep_dequeue_pending)
+ return 0;
+
+ for (i = 0; i < dwc->num_eps; i++) {
+ dep = dwc->eps[i];
+ if (dep->flags & DWC3_EP_PENDING_DEQUEUE) {
+ ret = dwc3_stop_active_transfer(dep, false, true);
+ if (ret)
+ goto exit;
+
+ dep->flags &= ~DWC3_EP_PENDING_DEQUEUE;
+ }
+ }
+
+ dwc->ep_dequeue_pending = 0;
+exit:
+ return ret;
+}
+
static int dwc3_gadget_ep_disable(struct usb_ep *ep)
{
struct dwc3_ep *dep;
@@ -2020,10 +2045,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
list_for_each_entry(r, &dep->started_list, list) {
if (r == req) {
struct dwc3_request *t;
-
- /* wait until it is processed */
- dwc3_stop_active_transfer(dep, true, true);
-
/*
* Remove any started request if the transfer is
* cancelled.
@@ -2032,6 +2053,12 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
dwc3_gadget_move_cancelled_request(r,
DWC3_REQUEST_STATUS_DEQUEUED);

+ ret = dwc3_stop_active_transfer(dep, false, true);
+ if (ret == -ETIMEDOUT) {
+ dep->flags |= DWC3_EP_PENDING_DEQUEUE;
+ dwc->ep_dequeue_pending = 1;
+ }
+
dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;

goto out;
@@ -2306,6 +2333,7 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
continue;

dwc3_remove_requests(dwc, dep);
+ dep->flags &= ~DWC3_EP_PENDING_DEQUEUE;
}
}

@@ -2702,6 +2730,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc->ep0state = EP0_SETUP_PHASE;
dwc->link_state = DWC3_LINK_STATE_SS_DIS;
dwc->delayed_status = false;
+ dwc->ep_dequeue_pending = 0;
dwc3_ep0_out_start(dwc);

dwc3_gadget_enable_irq(dwc);
@@ -3420,6 +3449,7 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
if (dep->stream_capable)
dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;

+ dep->flags &= ~DWC3_EP_PENDING_DEQUEUE;
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
@@ -3596,7 +3626,7 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}

-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+static int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
struct dwc3_gadget_ep_cmd_params params;
@@ -3607,7 +3637,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,

if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
- return;
+ return 0;

/*
* NOTICE: We are violating what the Databook says about the
@@ -3658,6 +3688,8 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
dwc3_ep0_out_start(dwc);
goto retry;
}
+ } else {
+ return ret;
}
}
dep->resource_index = 0;
@@ -3666,6 +3698,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ return ret;
}

static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)

2022-02-09 05:32:44

by Jack Pham

[permalink] [raw]
Subject: Re: [RFC PATCH 1/3] usb: dwc3: Flush pending SETUP data during stop active xfers

Hi Wesley,

On Thu, Feb 03, 2022 at 12:00:15AM -0800, Wesley Cheng wrote:
> While running the pullup disable sequence, if there are pending SETUP
> transfers stored in the controller, then the endxfer commands will

Should mention specifically that the endxfer commands *on non-EP0*
endpoints will time out.


Also let's use the same terminology as defined by the macros, i.e.

endxfer/ENDXFER -> ENDTRANSFER
STARTXFER -> STARTTRANSFER

> fail w/ ETIMEDOUT. As a suggestion from SNPS, in order to drain the
> SETUP packets, SW needs to issue a STARTXFER command. After issuing
^^^^ on EP0.

> the STARTXFER, and retrying the ENDXFER, the command should succeed.
> Else, if the endpoints are not properly stopped, the controller halt
> sequence will fail as well.
>
> One limitation is that the current logic will drop the SETUP data
> being received (ie dropping the SETUP packet), however, it should be
> acceptable in the pullup disable case, as the device is eventually
> going to disconnect from the host.
>
> Signed-off-by: Wesley Cheng <[email protected]>
> ---
> drivers/usb/dwc3/core.h | 7 +++++++
> drivers/usb/dwc3/ep0.c | 21 ++++++++++++--------
> drivers/usb/dwc3/gadget.c | 42 ++++++++++++++++++++++++++++++++++-----
> 3 files changed, 57 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index e1cc3f7398fb..a124694c0038 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -1546,6 +1546,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
> int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
> u32 param);
> void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
> +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
> +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
> #else
> static inline int dwc3_gadget_init(struct dwc3 *dwc)
> { return 0; }
> @@ -1567,6 +1569,11 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
> { return 0; }
> static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
> { }
> +static inline void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
> +{ }
> +static inline void dwc3_ep0_end_control_data(struct dwc3 *dwc,
> + struct dwc3_ep *dep)
> +{ }
> #endif
>
> #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
> index 658739410992..eb677b888610 100644
> --- a/drivers/usb/dwc3/ep0.c
> +++ b/drivers/usb/dwc3/ep0.c
> @@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
> int ret;
>
> spin_lock_irqsave(&dwc->lock, flags);
> - if (!dep->endpoint.desc || !dwc->pullups_connected) {
> + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
> dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
> dep->name);
> ret = -ESHUTDOWN;
> @@ -218,19 +218,21 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
> return ret;
> }
>
> -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
> +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
> {
> struct dwc3_ep *dep;
>
> /* reinitialize physical ep1 */
> dep = dwc->eps[1];
> dep->flags = DWC3_EP_ENABLED;
> + dep->trb_enqueue = 0;
>
> /* stall is always issued on EP0 */
> dep = dwc->eps[0];
> __dwc3_gadget_ep_set_halt(dep, 1, false);
> dep->flags = DWC3_EP_ENABLED;
> dwc->delayed_status = false;
> + dep->trb_enqueue = 0;
>
> if (!list_empty(&dep->pending_list)) {
> struct dwc3_request *req;
> @@ -240,7 +242,9 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
> }
>
> dwc->ep0state = EP0_SETUP_PHASE;
> - dwc3_ep0_out_start(dwc);
> + complete(&dwc->ep0_in_setup);
> + if (dwc->softconnect)
> + dwc3_ep0_out_start(dwc);
> }
>
> int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
> @@ -272,8 +276,6 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
> struct dwc3_ep *dep;
> int ret;
>
> - complete(&dwc->ep0_in_setup);
> -
> dep = dwc->eps[0];
> dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
> DWC3_TRBCTL_CONTROL_SETUP, false);
> @@ -922,7 +924,9 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
> dwc->setup_packet_pending = true;
>
> dwc->ep0state = EP0_SETUP_PHASE;
> - dwc3_ep0_out_start(dwc);
> + complete(&dwc->ep0_in_setup);
> + if (dwc->softconnect)
> + dwc3_ep0_out_start(dwc);
> }
>
> static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
> @@ -1073,7 +1077,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
> __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
> }
>
> -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
> +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
> {
> struct dwc3_gadget_ep_cmd_params params;
> u32 cmd;
> @@ -1083,7 +1087,8 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
> return;
>
> cmd = DWC3_DEPCMD_ENDTRANSFER;
> - cmd |= DWC3_DEPCMD_CMDIOC;
> + cmd |= dwc->connected ? 0 : DWC3_DEPCMD_HIPRI_FORCERM;
> + cmd |= dwc->connected ? DWC3_DEPCMD_CMDIOC : 0;

Can this be combined?

cmd |= dwc->connected ? DWC3_DEPCMD_CMDIOC : DWC3_DEPCMD_HIPRI_FORCERM;

> cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
> memset(&params, 0, sizeof(params));
> ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index 520031ba38aa..19b8d837e9d0 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -885,12 +885,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
> reg |= DWC3_DALEPENA_EP(dep->number);
> dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
>
> - if (usb_endpoint_xfer_control(desc))
> - goto out;
> -
> /* Initialize the TRB ring */
> dep->trb_dequeue = 0;
> dep->trb_enqueue = 0;
> +
> + if (usb_endpoint_xfer_control(desc))
> + goto out;
> +
> memset(dep->trb_pool, 0,
> sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
>
> @@ -2463,7 +2464,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
> * Per databook, when we want to stop the gadget, if a control transfer
> * is still in process, complete it and get the core into setup phase.
> */
> - if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
> + if ((!is_on && (dwc->ep0state != EP0_SETUP_PHASE ||
> + dwc->ep0_next_event != DWC3_EP0_COMPLETE))) {
> reinit_completion(&dwc->ep0_in_setup);
>
> ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
> @@ -2506,6 +2508,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
> u32 count;
>
> dwc->connected = false;
> +
> + /*
> + * Ensure no pending data/setup stages, and disable ep0 to
> + * block further EP0 transactions before stopping pending
> + * transfers.
> + */
> + dwc3_ep0_end_control_data(dwc, dwc->eps[1]);
> + dwc3_ep0_stall_and_restart(dwc);
> + __dwc3_gadget_ep_disable(dwc->eps[0]);
> + __dwc3_gadget_ep_disable(dwc->eps[1]);
> +
> /*
> * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
> * Section 4.1.8 Table 4-7, it states that for a device-initiated
> @@ -3587,8 +3600,10 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
> bool interrupt)
> {
> struct dwc3_gadget_ep_cmd_params params;
> + struct dwc3 *dwc = dep->dwc;
> u32 cmd;
> int ret;
> + int retries = 1;
>
> if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
> (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
> @@ -3620,7 +3635,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
> *
> * This mode is NOT available on the DWC_usb31 IP.
> */
> -
> +retry:
> cmd = DWC3_DEPCMD_ENDTRANSFER;
> cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
> cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
> @@ -3628,6 +3643,23 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
> memset(&params, 0, sizeof(params));
> ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
> WARN_ON_ONCE(ret);
> + if (ret == -ETIMEDOUT) {
> + if (!dwc->connected) {
> + /*
> + * While the controller is in an active setup/control
> + * transfer, the HW is unable to service other eps
> + * potentially leading to an endxfer command timeout.
> + * It was recommended to ensure that there are no
> + * pending/cached setup packets stored in internal
> + * memory. Only way to achieve this is to issue another
> + * start transfer, and retry.
> + */
> + if (retries--) {
> + dwc3_ep0_out_start(dwc);
> + goto retry;
> + }
> + }
> + }
> dep->resource_index = 0;
>
> if (!interrupt)

2022-02-12 15:52:08

by Wesley Cheng

[permalink] [raw]
Subject: Re: [RFC PATCH 1/3] usb: dwc3: Flush pending SETUP data during stop active xfers

Hi Jack,

On 2/8/2022 9:33 AM, Jack Pham wrote:
> Hi Wesley,
>
> On Thu, Feb 03, 2022 at 12:00:15AM -0800, Wesley Cheng wrote:
>> While running the pullup disable sequence, if there are pending SETUP
>> transfers stored in the controller, then the endxfer commands will
>
> Should mention specifically that the endxfer commands *on non-EP0*
> endpoints will time out.
>
Thanks for the feedback and initial review. Done.
>
> Also let's use the same terminology as defined by the macros, i.e.
>
> endxfer/ENDXFER -> ENDTRANSFER
> STARTXFER -> STARTTRANSFER
>
>> fail w/ ETIMEDOUT. As a suggestion from SNPS, in order to drain the
>> SETUP packets, SW needs to issue a STARTXFER command. After issuing
> ^^^^ on EP0.
>
Sure will add explicit EP0 statement.

>> the STARTXFER, and retrying the ENDXFER, the command should succeed.
>> Else, if the endpoints are not properly stopped, the controller halt
>> sequence will fail as well.
>>
>> One limitation is that the current logic will drop the SETUP data
>> being received (ie dropping the SETUP packet), however, it should be
>> acceptable in the pullup disable case, as the device is eventually
>> going to disconnect from the host.
>>
>> Signed-off-by: Wesley Cheng <[email protected]>
>> ---
>> drivers/usb/dwc3/core.h | 7 +++++++
>> drivers/usb/dwc3/ep0.c | 21 ++++++++++++--------
>> drivers/usb/dwc3/gadget.c | 42 ++++++++++++++++++++++++++++++++++-----
>> 3 files changed, 57 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
>> index e1cc3f7398fb..a124694c0038 100644
>> --- a/drivers/usb/dwc3/core.h
>> +++ b/drivers/usb/dwc3/core.h
>> @@ -1546,6 +1546,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
>> int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
>> u32 param);
>> void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
>> +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
>> +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
>> #else
>> static inline int dwc3_gadget_init(struct dwc3 *dwc)
>> { return 0; }
>> @@ -1567,6 +1569,11 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
>> { return 0; }
>> static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
>> { }
>> +static inline void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
>> +{ }
>> +static inline void dwc3_ep0_end_control_data(struct dwc3 *dwc,
>> + struct dwc3_ep *dep)
>> +{ }
>> #endif
>>
>> #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
>> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
>> index 658739410992..eb677b888610 100644
>> --- a/drivers/usb/dwc3/ep0.c
>> +++ b/drivers/usb/dwc3/ep0.c
>> @@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
>> int ret;
>>
>> spin_lock_irqsave(&dwc->lock, flags);
>> - if (!dep->endpoint.desc || !dwc->pullups_connected) {
>> + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
>> dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
>> dep->name);
>> ret = -ESHUTDOWN;
>> @@ -218,19 +218,21 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
>> return ret;
>> }
>>
>> -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
>> +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
>> {
>> struct dwc3_ep *dep;
>>
>> /* reinitialize physical ep1 */
>> dep = dwc->eps[1];
>> dep->flags = DWC3_EP_ENABLED;
>> + dep->trb_enqueue = 0;
>>
>> /* stall is always issued on EP0 */
>> dep = dwc->eps[0];
>> __dwc3_gadget_ep_set_halt(dep, 1, false);
>> dep->flags = DWC3_EP_ENABLED;
>> dwc->delayed_status = false;
>> + dep->trb_enqueue = 0;
>>
>> if (!list_empty(&dep->pending_list)) {
>> struct dwc3_request *req;
>> @@ -240,7 +242,9 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
>> }
>>
>> dwc->ep0state = EP0_SETUP_PHASE;
>> - dwc3_ep0_out_start(dwc);
>> + complete(&dwc->ep0_in_setup);
>> + if (dwc->softconnect)
>> + dwc3_ep0_out_start(dwc);
>> }
>>
>> int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
>> @@ -272,8 +276,6 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
>> struct dwc3_ep *dep;
>> int ret;
>>
>> - complete(&dwc->ep0_in_setup);
>> -
>> dep = dwc->eps[0];
>> dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
>> DWC3_TRBCTL_CONTROL_SETUP, false);
>> @@ -922,7 +924,9 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
>> dwc->setup_packet_pending = true;
>>
>> dwc->ep0state = EP0_SETUP_PHASE;
>> - dwc3_ep0_out_start(dwc);
>> + complete(&dwc->ep0_in_setup);
>> + if (dwc->softconnect)
>> + dwc3_ep0_out_start(dwc);
>> }
>>
>> static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
>> @@ -1073,7 +1077,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
>> __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
>> }
>>
>> -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
>> +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
>> {
>> struct dwc3_gadget_ep_cmd_params params;
>> u32 cmd;
>> @@ -1083,7 +1087,8 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
>> return;
>>
>> cmd = DWC3_DEPCMD_ENDTRANSFER;
>> - cmd |= DWC3_DEPCMD_CMDIOC;
>> + cmd |= dwc->connected ? 0 : DWC3_DEPCMD_HIPRI_FORCERM;
>> + cmd |= dwc->connected ? DWC3_DEPCMD_CMDIOC : 0;
>
> Can this be combined?
>
Agreed. Will combine them.

Thanks
Wesley Cheng

> cmd |= dwc->connected ? DWC3_DEPCMD_CMDIOC : DWC3_DEPCMD_HIPRI_FORCERM;
>
>> cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
>> memset(&params, 0, sizeof(params));
>> ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>> index 520031ba38aa..19b8d837e9d0 100644
>> --- a/drivers/usb/dwc3/gadget.c
>> +++ b/drivers/usb/dwc3/gadget.c
>> @@ -885,12 +885,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
>> reg |= DWC3_DALEPENA_EP(dep->number);
>> dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
>>
>> - if (usb_endpoint_xfer_control(desc))
>> - goto out;
>> -
>> /* Initialize the TRB ring */
>> dep->trb_dequeue = 0;
>> dep->trb_enqueue = 0;
>> +
>> + if (usb_endpoint_xfer_control(desc))
>> + goto out;
>> +
>> memset(dep->trb_pool, 0,
>> sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
>>
>> @@ -2463,7 +2464,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
>> * Per databook, when we want to stop the gadget, if a control transfer
>> * is still in process, complete it and get the core into setup phase.
>> */
>> - if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
>> + if ((!is_on && (dwc->ep0state != EP0_SETUP_PHASE ||
>> + dwc->ep0_next_event != DWC3_EP0_COMPLETE))) {
>> reinit_completion(&dwc->ep0_in_setup);
>>
>> ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
>> @@ -2506,6 +2508,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
>> u32 count;
>>
>> dwc->connected = false;
>> +
>> + /*
>> + * Ensure no pending data/setup stages, and disable ep0 to
>> + * block further EP0 transactions before stopping pending
>> + * transfers.
>> + */
>> + dwc3_ep0_end_control_data(dwc, dwc->eps[1]);
>> + dwc3_ep0_stall_and_restart(dwc);
>> + __dwc3_gadget_ep_disable(dwc->eps[0]);
>> + __dwc3_gadget_ep_disable(dwc->eps[1]);
>> +
>> /*
>> * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
>> * Section 4.1.8 Table 4-7, it states that for a device-initiated
>> @@ -3587,8 +3600,10 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
>> bool interrupt)
>> {
>> struct dwc3_gadget_ep_cmd_params params;
>> + struct dwc3 *dwc = dep->dwc;
>> u32 cmd;
>> int ret;
>> + int retries = 1;
>>
>> if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
>> (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
>> @@ -3620,7 +3635,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
>> *
>> * This mode is NOT available on the DWC_usb31 IP.
>> */
>> -
>> +retry:
>> cmd = DWC3_DEPCMD_ENDTRANSFER;
>> cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
>> cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
>> @@ -3628,6 +3643,23 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
>> memset(&params, 0, sizeof(params));
>> ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
>> WARN_ON_ONCE(ret);
>> + if (ret == -ETIMEDOUT) {
>> + if (!dwc->connected) {
>> + /*
>> + * While the controller is in an active setup/control
>> + * transfer, the HW is unable to service other eps
>> + * potentially leading to an endxfer command timeout.
>> + * It was recommended to ensure that there are no
>> + * pending/cached setup packets stored in internal
>> + * memory. Only way to achieve this is to issue another
>> + * start transfer, and retry.
>> + */
>> + if (retries--) {
>> + dwc3_ep0_out_start(dwc);
>> + goto retry;
>> + }
>> + }
>> + }
>> dep->resource_index = 0;
>>
>> if (!interrupt)