From 73a51c4f0aa722e027148d57f2d7b154696063fe Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Mon, 13 Jul 2026 15:14:01 -0700 Subject: [PATCH] raspberrypi: make tusb_time_millis_api RAM-resident; core1 calls it Since the TinyUSB update in #11093, tuh_task_event_ready() calls tusb_time_millis_api() (added upstream in hathach/tinyusb@3a262cb6e to report a due deferred call_after callback). On RP2 boards, usb_host polls tuh_task_event_ready() from core1's PIO-USB frame loop (common-hal/usb_host/Port.c). tuh_task_event_ready() is deliberately placed in RAM by link-rp2*.ld, and everything it calls must be RAM-resident: on RP2350 core1 sets an MPU region that makes flash execute-never, and on RP2040 flash may be unavailable while core0 writes to it. The shared tusb_time_millis_api() implementation (supervisor_ticks_ms32) lives in flash, so core1 died at the first device attach (the first time call_after was pending) and the PIO-USB bus went silent: no USB host device was ever detected. Fixes the regression reported in #10243. Make the shared implementation MP_WEAK and override it in the raspberrypi port with a PLACE_IN_ITCM function that reads the timer directly. time_us_32() >> 10 yields ~1.024 ms units rather than exact milliseconds, which is fine because TinyUSB only uses this API for relative delay arithmetic and every use goes through the same function. Tested on Feather RP2040 USB Host and Fruit Jam with a USB flash drive: both fail to enumerate on current main and enumerate immediately with this change, with the TinyUSB submodule pristine at 5453ed09f. Co-Authored-By: Claude Fable 5 --- ports/raspberrypi/supervisor/usb.c | 16 ++++++++++++++++ supervisor/shared/usb/usb.c | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ports/raspberrypi/supervisor/usb.c b/ports/raspberrypi/supervisor/usb.c index 398f3f448a1..244c5bbce9b 100644 --- a/ports/raspberrypi/supervisor/usb.c +++ b/ports/raspberrypi/supervisor/usb.c @@ -6,14 +6,30 @@ #include "lib/tinyusb/src/device/usbd.h" #include "supervisor/background_callback.h" +#include "supervisor/linker.h" #include "supervisor/usb.h" #include "hardware/irq.h" +#include "hardware/timer.h" #include "pico/platform.h" #include "hardware/regs/intctrl.h" void init_usb_hardware(void) { } +// Override the shared implementation with one that is safe to call from core1. +// TinyUSB's tuh_task_event_ready() calls this, and usb_host polls +// tuh_task_event_ready() from core1's PIO-USB frame loop (see +// common-hal/usb_host/Port.c). tuh_task_event_ready() is deliberately placed in +// RAM by link-rp2*.ld, and everything it calls must be RAM-resident too: on +// RP2350, core1 sets an MPU region that makes flash execute-never, and on +// RP2040 flash may be unavailable while core0 writes to it. time_us_32() is a +// static-inline register read. >>10 yields ~1.024 ms units rather than exact +// milliseconds, which is fine because TinyUSB only uses this API for relative +// delay arithmetic and every use goes through this same function. +uint32_t PLACE_IN_ITCM(tusb_time_millis_api)(void) { + return time_us_32() >> 10; +} + static void _usb_irq_wrapper(void) { usb_irq_handler(0); } diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index e8a4422bb36..51222be0e59 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -184,7 +184,10 @@ void usb_background(void) { } } -uint32_t tusb_time_millis_api(void) { +// Ports may override this with a RAM-resident version when TinyUSB code that +// calls it must not execute from flash (e.g. raspberrypi polls +// tuh_task_event_ready(), which calls this, from core1). +MP_WEAK uint32_t tusb_time_millis_api(void) { return supervisor_ticks_ms32(); }