Skip to content

Commit b56a27f

Browse files
tmon-nordicjukkar
authored andcommitted
[nrf fromtree] drivers: udc_dwc2: Workaround endpoint disable race condition
Endpoint disable function is racing against bus traffic. If the bus traffic leads to transfer completion immediately before the endpoint disable is executed, then the transfer complete interrupt will remain set when the endpoint is disabled. For OUT endpoints this leads to "No buffer for ep" errors, while for IN endpoint this can lead to double buffer pull which causes assertion failure. The proper solution would be to change endpoint disable to not actually wait for the individual events (and accept that the endpoint may not need to be disabled because the transfer can just finish). For the time being workaround the issue by clearing XferCompl bit on endpoint disable. Signed-off-by: Tomasz Moń <[email protected]> (cherry picked from commit a26d3c2) (cherry picked from commit 070d76e)
1 parent 5d6f03e commit b56a27f

File tree

1 file changed

+15
-2
lines changed

1 file changed

+15
-2
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,19 @@ static void udc_dwc2_ep_disable(const struct device *dev,
14021402
return;
14031403
}
14041404

1405+
/* FIXME: This function needs to be changed to not synchronously wait
1406+
* for the events to happen because the actions here are racing against
1407+
* the USB host packets. It is possible that the IN token or OUT DATA
1408+
* gets sent shortly before this function disables the endpoint. If this
1409+
* happens, the XferCompl would be set and driver will incorrectly think
1410+
* that either:
1411+
* * never queued transfer finished, OR
1412+
* * transfer queued in incompisoin handler finished (before it really
1413+
* does and then it'll "double"-finish when it actually finishes)
1414+
*
1415+
* For the time being XferCompl is cleared as a workaround.
1416+
*/
1417+
14051418
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
14061419
mem_addr_t dctl_reg, gintsts_reg, doepint_reg;
14071420
uint32_t dctl;
@@ -1440,7 +1453,7 @@ static void udc_dwc2_ep_disable(const struct device *dev,
14401453
}
14411454

14421455
/* Clear Endpoint Disabled interrupt */
1443-
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, doepint_reg);
1456+
sys_write32(USB_DWC2_DOEPINT_EPDISBLD | USB_DWC2_DOEPINT_XFERCOMPL, doepint_reg);
14441457

14451458
dctl |= USB_DWC2_DCTL_CGOUTNAK;
14461459
sys_write32(dctl, dctl_reg);
@@ -1464,7 +1477,7 @@ static void udc_dwc2_ep_disable(const struct device *dev,
14641477
dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_EPDISBLD);
14651478

14661479
/* Clear Endpoint Disabled interrupt */
1467-
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, diepint_reg);
1480+
sys_write32(USB_DWC2_DIEPINT_EPDISBLD | USB_DWC2_DIEPINT_XFERCOMPL, diepint_reg);
14681481

14691482
/* TODO: Read DIEPTSIZn here? Programming Guide suggest it to
14701483
* let application know how many bytes of interrupted transfer

0 commit comments

Comments
 (0)