UPSTREAM: 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 <quic_wcheng@quicinc.com>
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
Link: https://lore.kernel.org/r/20220309205402.4467-1-quic_wcheng@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Bug: 230843866
(cherry picked from commit e4cf6580ac740f766dae26203bd6311d353dcd42)

Change-Id: I7f643ae340a20f12d5f86965674aa58df1be3979
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
This commit is contained in:
Thinh Nguyen
2022-03-09 12:54:02 -08:00
committed by Todd Kjos
parent 75059d208e
commit fe513e1c26
4 changed files with 30 additions and 1 deletions

View File

@@ -729,6 +729,7 @@ struct dwc3_ep {
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) #define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11) #define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12) #define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13)
/* This last one is specific to EP0 */ /* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31) #define DWC3_EP0_DIR_IN BIT(31)

View File

@@ -271,6 +271,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{ {
struct dwc3_ep *dep; struct dwc3_ep *dep;
int ret; int ret;
int i;
complete(&dwc->ep0_in_setup); complete(&dwc->ep0_in_setup);
@@ -279,6 +280,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
DWC3_TRBCTL_CONTROL_SETUP, false); DWC3_TRBCTL_CONTROL_SETUP, false);
ret = dwc3_ep0_start_trans(dep); ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0); WARN_ON(ret < 0);
for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
struct dwc3_ep *dwc3_ep;
dwc3_ep = dwc->eps[i];
if (!dwc3_ep)
continue;
if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
continue;
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
dwc3_stop_active_transfer(dwc3_ep, true, true);
}
} }
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)

View File

@@ -1874,6 +1874,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
*/ */
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) || if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
(dep->flags & DWC3_EP_WEDGE) || (dep->flags & DWC3_EP_WEDGE) ||
(dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_STALL)) { (dep->flags & DWC3_EP_STALL)) {
dep->flags |= DWC3_EP_DELAY_START; dep->flags |= DWC3_EP_DELAY_START;
return 0; return 0;
@@ -2008,6 +2009,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
if (r == req) { if (r == req) {
struct dwc3_request *t; struct dwc3_request *t;
/*
* 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
* timeout. Delay issuing the End Transfer command until the Setup TRB is
* prepared.
*/
if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
dep->flags |= DWC3_EP_DELAY_STOP;
/* wait until it is processed */ /* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true); dwc3_stop_active_transfer(dep, true, true);
@@ -2089,7 +2100,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (!list_empty(&dep->started_list)) if (!list_empty(&dep->started_list))
dep->flags |= DWC3_EP_DELAY_START; dep->flags |= DWC3_EP_DELAY_START;
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
(dep->flags & DWC3_EP_DELAY_STOP)) {
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
return 0; return 0;
} }
@@ -3585,6 +3597,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
int ret; int ret;
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
(dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return; return;

View File

@@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags); gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc); void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
/** /**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW