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ń <tomasz.mon@nordicsemi.no>
This commit is contained in:
parent
c19d34c5d3
commit
a26d3c2f2e
1 changed files with 15 additions and 2 deletions
|
@ -1402,6 +1402,19 @@ static void udc_dwc2_ep_disable(const struct device *dev,
|
|||
return;
|
||||
}
|
||||
|
||||
/* FIXME: This function needs to be changed to not synchronously wait
|
||||
* for the events to happen because the actions here are racing against
|
||||
* the USB host packets. It is possible that the IN token or OUT DATA
|
||||
* gets sent shortly before this function disables the endpoint. If this
|
||||
* happens, the XferCompl would be set and driver will incorrectly think
|
||||
* that either:
|
||||
* * never queued transfer finished, OR
|
||||
* * transfer queued in incompisoin handler finished (before it really
|
||||
* does and then it'll "double"-finish when it actually finishes)
|
||||
*
|
||||
* For the time being XferCompl is cleared as a workaround.
|
||||
*/
|
||||
|
||||
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
|
||||
mem_addr_t dctl_reg, gintsts_reg, doepint_reg;
|
||||
uint32_t dctl;
|
||||
|
@ -1440,7 +1453,7 @@ static void udc_dwc2_ep_disable(const struct device *dev,
|
|||
}
|
||||
|
||||
/* Clear Endpoint Disabled interrupt */
|
||||
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, doepint_reg);
|
||||
sys_write32(USB_DWC2_DOEPINT_EPDISBLD | USB_DWC2_DOEPINT_XFERCOMPL, doepint_reg);
|
||||
|
||||
dctl |= USB_DWC2_DCTL_CGOUTNAK;
|
||||
sys_write32(dctl, dctl_reg);
|
||||
|
@ -1464,7 +1477,7 @@ static void udc_dwc2_ep_disable(const struct device *dev,
|
|||
dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_EPDISBLD);
|
||||
|
||||
/* Clear Endpoint Disabled interrupt */
|
||||
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, diepint_reg);
|
||||
sys_write32(USB_DWC2_DIEPINT_EPDISBLD | USB_DWC2_DIEPINT_XFERCOMPL, diepint_reg);
|
||||
|
||||
/* TODO: Read DIEPTSIZn here? Programming Guide suggest it to
|
||||
* let application know how many bytes of interrupted transfer
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue