summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTaylor Yu <[email protected]>2024-02-25 11:12:24 -0600
committerTaylor Yu <[email protected]>2024-02-26 09:25:51 -0600
commit473d400cfde768e26fe8644bab3d655c81777b64 (patch)
treeb8462e041ff6a7906113a655cf6f7d0815bf3498 /src
parentf21b792712233e3ef32d05628370b8e378254b2e (diff)
work around possible RP2040 erratum
RP2040 device controller does not seem to clear pending transactions configured in EP0 buffer controls when the host aborts a control transfer. This causes assertion failures, including when a buffer AVAILABLE flag set for a previous transfer causes an unexpected transaction completion.
Diffstat (limited to 'src')
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index e8cee73fd..51174f709 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -217,6 +217,18 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void)
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void)
{
+ // Abort any transactions from a prior control transfer, because
+ // receiving SETUP doesn't reset buffer control state. This works around
+ // a possible USB hardware erratum.
+
+ // With this workaround a race window still exists, but smaller.
+ // ABORT flag is unusable prior to hardware B2 (RP2040-E2), so a larger
+ // race window exists for B1 and earlier.
+ if (rp2040_chip_version() >= 2) {
+ usb_hw_set->abort = 0x3;
+ while ((usb_hw->abort_done & 0x3) != 0x3)
+ ;
+ }
// If we have finished this transfer on EP0 set pid back to 1 for next
// setup transfer. Also clear a stall in case
uint8_t addrs[] = {0x0, 0x80};
@@ -224,6 +236,25 @@ TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
ep->next_pid = 1u;
+ // Reset the buffer control now to minimize race conditions
+ _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL);
+ // Explicit delay, because the one in
+ // _hw_endpoint_buffer_control_set_value32 is only to set AVAILABLE
+ __asm volatile (
+ "b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1:\n"
+ : : : "memory");
+ // Make sure local ep state matches peripheral
+ hw_endpoint_reset_transfer(ep);
+ }
+ if (rp2040_chip_version() >= 2) {
+ usb_hw_clear->abort = 0x3;
+ usb_hw_clear->abort_done = 0x3;
}
}