rp2040/rp23xx: preserve SIE_CTRL.PULLUP_EN in usbdev_register

usbdev_register() calls CLASS_BIND, which ends with DEV_CONNECT
(composite_bind/cdcacm_bind) and sets SIE_CTRL.PULLUP_EN, and then
performs a wholesale putreg32 of SIE_CTRL to set EP0_INT_1BUF --
clobbering the pull-up microseconds after it was asserted.

Enumeration only ever succeeded because the host happened to latch the
microsecond pull-up blip and issued a bus reset, whose handler
(CLASS_DISCONNECT -> DEV_CONNECT) re-arms the pull-up.  A warm host
port catches the blip; a cold-plugged port is still in attach debounce,
misses it, and never resets -- PULLUP_EN stays 0 forever and the device
is totally silent on the bus while NuttX runs normally underneath.
This presented as an intermittent, image-dependent "cold boot brick"
(boot timing shifts the blip in or out of the host's blind window).

Fix: set EP0_INT_1BUF with setbits_reg32 so PULLUP_EN survives.

Validated on RP2350 silicon (Raspberry Pi Pico 2 W): an image that
failed 0/10 cold plugs enumerated 10/10 with the fix; a second board
that had never enumerated at all was recovered by it.  The rp2040
driver has the identical code and receives the identical fix
(build-tested; the RP2350 validation exercised the shared logic).

Fixes apache/nuttx#19434

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
This commit is contained in:
Ricard Rosson 2026-07-15 21:00:42 +01:00 committed by Xiang Xiao
parent ce645060fa
commit 53c27e155b
2 changed files with 22 additions and 4 deletions

View file

@ -2184,8 +2184,17 @@ int usbdev_register(struct usbdevclass_driver_s *driver)
/* Enable interrupt */
putreg32(RP2040_USBCTRL_REGS_SIE_CTRL_EP0_INT_1BUF,
RP2040_USBCTRL_REGS_SIE_CTRL);
/* Use setbits, NOT putreg32: CLASS_BIND above ends with DEV_CONNECT
* (composite_bind/cdcacm_bind), which sets SIE_CTRL.PULLUP_EN. A
* wholesale write here clobbers that pull-up microseconds after it was
* asserted; enumeration then only succeeds if the host happened to
* latch the short pull-up blip and issues a bus reset (whose handler
* re-arms the pull-up). A cold-plugged host port misses the blip and
* the device stays disconnected forever.
*/
setbits_reg32(RP2040_USBCTRL_REGS_SIE_CTRL_EP0_INT_1BUF,
RP2040_USBCTRL_REGS_SIE_CTRL);
putreg32(RP2040_USBCTRL_REGS_INTR_BUFF_STATUS |
RP2040_USBCTRL_REGS_INTR_BUS_RESET |
RP2040_USBCTRL_REGS_INTR_SETUP_REQ,

View file

@ -2176,8 +2176,17 @@ int usbdev_register(struct usbdevclass_driver_s *driver)
/* Enable interrupt */
putreg32(RP23XX_USBCTRL_REGS_SIE_CTRL_EP0_INT_1BUF,
RP23XX_USBCTRL_REGS_SIE_CTRL);
/* Use setbits, NOT putreg32: CLASS_BIND above ends with DEV_CONNECT
* (composite_bind/cdcacm_bind), which sets SIE_CTRL.PULLUP_EN. A
* wholesale write here clobbers that pull-up microseconds after it was
* asserted; enumeration then only succeeds if the host happened to
* latch the short pull-up blip and issues a bus reset (whose handler
* re-arms the pull-up). A cold-plugged host port misses the blip and
* the device stays disconnected forever.
*/
setbits_reg32(RP23XX_USBCTRL_REGS_SIE_CTRL_EP0_INT_1BUF,
RP23XX_USBCTRL_REGS_SIE_CTRL);
putreg32(RP23XX_USBCTRL_REGS_INTR_BUFF_STATUS |
RP23XX_USBCTRL_REGS_INTR_BUS_RESET |
RP23XX_USBCTRL_REGS_INTR_SETUP_REQ,