From 12658022a95908017ac9d54c452a50d21ed8f3a8 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Tue, 19 Jan 2021 14:31:46 +0530 Subject: [PATCH] usb: dwc3: gadget: Bail out for short packets/ZLPs only if IOC is not set Currently the driver explicitly clears the HWO bit of the TRB prepared for short packet or ZLP for EPs in both the directions, and returns with non-zero code which breaks cleanup of completed requests. The driver does this because it is written with the understanding that the HW does not clear HWO for such packets. But the databook section 4.2.3.2 says that the HW does not clear HWO for such packets on the OUT EP only. Now consider a device-to-host(USB IN) usecase like SW NCM, where the u_ether driver looks for interrupt on every 5th request(say). In between these 5 requests, if there is any packet of multiple of MPS, then ZLP TRB will also be prepared. When the event is received for the 5th request, we will only handle upto the ZLP TRB because of the faulty logic described above. And only on the event for the 10th request, we handle the 5th request (because of IOC set on the 5th request). So, clearly the TRB cleanup logic is lagging behind the HW actually completing the requests. When this mismatch happens multiple times, there eventually comes a time when the SW does not have any free requests anymore and hence stall will be seen. Fix this by returning with non-zero code only if the IOC bit is set for the short packet or ZLP TRB, so that the cleanup routine continues upto the TRB for which the event was raised. Change-Id: I984e6de383993fc3c2da6b74147d6f50e081de34 Signed-off-by: Ajay Agarwal --- drivers/usb/dwc3/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f1b67f1c7c60..b02dadcbab1f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3064,7 +3064,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - return 1; + return (((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) ? 1 : 0); } count = trb->size & DWC3_TRB_SIZE_MASK;