Skip to content

Commit e4cf658

Browse files
Thinh Nguyengregkh
authored andcommitted
usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue
If a Setup packet is received but yet to DMA out, the controller will not process the End Transfer command of any endpoint. Polling of its DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a command timeout. This may occur if the driver doesn’t service the completion interrupt of the control status stage yet due to system latency, then it won’t prepare TRB and start the transfer for the next Setup Stage. To the host side, the control transfer had completed, and the host can send a new Setup packet at this point. In the meanwhile, if the driver receives an async call to dequeue a request (triggering End Transfer) to any endpoint, then the driver will service that End transfer first, blocking the control status stage completion handler. Since no TRB is available for the Setup stage, the Setup packet can’t be DMA’ed out and the End Transfer gets hung. The driver must not block setting up of the Setup stage. So track and only issue the End Transfer command only when there’s Setup TRB prepared so that the controller can DMA out the Setup packet. Delay the End transfer command if there's no Setup TRB available. This is applicable to all DWC_usb3x IPs. Co-developed-by: Wesley Cheng <[email protected]> Signed-off-by: Thinh Nguyen <[email protected]> Signed-off-by: Wesley Cheng <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent e192cc7 commit e4cf658

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed

drivers/usb/dwc3/core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ struct dwc3_ep {
737737
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
738738
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
739739
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
740+
#define DWC3_EP_DELAY_STOP BIT(13)
740741

741742
/* This last one is specific to EP0 */
742743
#define DWC3_EP0_DIR_IN BIT(31)

drivers/usb/dwc3/ep0.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
271271
{
272272
struct dwc3_ep *dep;
273273
int ret;
274+
int i;
274275

275276
complete(&dwc->ep0_in_setup);
276277

@@ -279,6 +280,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
279280
DWC3_TRBCTL_CONTROL_SETUP, false);
280281
ret = dwc3_ep0_start_trans(dep);
281282
WARN_ON(ret < 0);
283+
for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
284+
struct dwc3_ep *dwc3_ep;
285+
286+
dwc3_ep = dwc->eps[i];
287+
if (!dwc3_ep)
288+
continue;
289+
290+
if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
291+
continue;
292+
293+
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
294+
dwc3_stop_active_transfer(dwc3_ep, true, true);
295+
}
282296
}
283297

284298
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)

drivers/usb/dwc3/gadget.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -654,9 +654,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
654654
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
655655
}
656656

657-
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
658-
bool interrupt);
659-
660657
/**
661658
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
662659
* @dwc: pointer to the DWC3 context
@@ -1926,6 +1923,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
19261923
*/
19271924
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
19281925
(dep->flags & DWC3_EP_WEDGE) ||
1926+
(dep->flags & DWC3_EP_DELAY_STOP) ||
19291927
(dep->flags & DWC3_EP_STALL)) {
19301928
dep->flags |= DWC3_EP_DELAY_START;
19311929
return 0;
@@ -2058,6 +2056,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
20582056
if (r == req) {
20592057
struct dwc3_request *t;
20602058

2059+
/*
2060+
* If a Setup packet is received but yet to DMA out, the controller will
2061+
* not process the End Transfer command of any endpoint. Polling of its
2062+
* DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
2063+
* timeout. Delay issuing the End Transfer command until the Setup TRB is
2064+
* prepared.
2065+
*/
2066+
if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
2067+
dep->flags |= DWC3_EP_DELAY_STOP;
2068+
20612069
/* wait until it is processed */
20622070
dwc3_stop_active_transfer(dep, true, true);
20632071

@@ -2141,7 +2149,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
21412149
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
21422150
dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
21432151

2144-
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
2152+
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
2153+
(dep->flags & DWC3_EP_DELAY_STOP)) {
21452154
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
21462155
return 0;
21472156
}
@@ -3621,10 +3630,11 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
36213630
}
36223631
}
36233632

3624-
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
3633+
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
36253634
bool interrupt)
36263635
{
36273636
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
3637+
(dep->flags & DWC3_EP_DELAY_STOP) ||
36283638
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
36293639
return;
36303640

drivers/usb/dwc3/gadget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
116116
gfp_t gfp_flags);
117117
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
118118
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
119+
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
119120

120121
/**
121122
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW

0 commit comments

Comments
 (0)