From 53c27e155bb1dc5a7593e10667de62922751be46 Mon Sep 17 00:00:00 2001 From: Ricard Rosson Date: Wed, 15 Jul 2026 21:00:42 +0100 Subject: [PATCH] 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 Signed-off-by: Ricard Rosson --- arch/arm/src/rp2040/rp2040_usbdev.c | 13 +++++++++++-- arch/arm/src/rp23xx/rp23xx_usbdev.c | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/arch/arm/src/rp2040/rp2040_usbdev.c b/arch/arm/src/rp2040/rp2040_usbdev.c index ceb7a9e965c..470f32567ed 100644 --- a/arch/arm/src/rp2040/rp2040_usbdev.c +++ b/arch/arm/src/rp2040/rp2040_usbdev.c @@ -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, diff --git a/arch/arm/src/rp23xx/rp23xx_usbdev.c b/arch/arm/src/rp23xx/rp23xx_usbdev.c index cfece8936bf..357b96eb8d8 100644 --- a/arch/arm/src/rp23xx/rp23xx_usbdev.c +++ b/arch/arm/src/rp23xx/rp23xx_usbdev.c @@ -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,