arch/rp2040: set buffer AVAILABLE bit after the rest of buffer control

Per the RP2040 datasheet section 4.1.2.5.1, when handing a buffer to the
USB controller the AVAILABLE bit must be written after the rest of the
buffer-control register (length and data PID) and after a short delay,
because buffer control crosses from the system clock domain into the USB
clock domain.  Writing everything in a single store risks the controller
acting on a stale length or PID.  rp2040_update_buffer_control() wrote
the whole word, AVAILABLE included, in one access.

Follow the sequence the datasheet (and the Pico SDK) use: write the
control word with AVAILABLE cleared, wait ~12 CPU cycles, then set
AVAILABLE.  The delay covers system clocks up to 12x the 48 MHz USB
clock.

Validated on raspberrypi-pico (RP2040) as part of bringing up cdcncm;
no regression on cdcacm/usbmsc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AHJRvWeBMTHwzpwjaUg4HW
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
This commit is contained in:
Ricard Rosson 2026-07-08 22:05:03 +01:00 committed by Xiang Xiao
parent 53b5048b1f
commit 7ff0b8e876

View file

@ -473,6 +473,7 @@ static void rp2040_update_buffer_control(struct rp2040_ep_s *privep,
uint32_t or_mask)
{
uint32_t value = 0;
int i;
if (and_mask)
{
@ -482,6 +483,24 @@ static void rp2040_update_buffer_control(struct rp2040_ep_s *privep,
if (or_mask)
{
value |= or_mask;
if (or_mask & RP2040_USBCTRL_DPSRAM_EP_BUFF_CTRL_AVAIL)
{
/* RP2040 datasheet 4.1.2.5.1: the AVAILABLE bit must be set
* after the rest of the buffer control register has been
* written and had time to settle across the clock domain
* crossing, or the controller may act on a stale length/PID.
* 12 CPU cycles covers system clocks up to 12x clk_usb.
*/
putreg32(value & ~RP2040_USBCTRL_DPSRAM_EP_BUFF_CTRL_AVAIL,
privep->buf_ctrl);
for (i = 0; i < 12; i++)
{
__asm__ volatile("nop");
}
}
}
putreg32(value, privep->buf_ctrl);