Skip to content

raspberrypi: wake core 0 from idle when core 1 posts USB host events#11130

Merged
dhalbert merged 1 commit into
adafruit:mainfrom
mikeysklar:rp2-usb-host-core1-wake
Jul 19, 2026
Merged

raspberrypi: wake core 0 from idle when core 1 posts USB host events#11130
dhalbert merged 1 commit into
adafruit:mainfrom
mikeysklar:rp2-usb-host-core1-wake

Conversation

@mikeysklar

@mikeysklar mikeysklar commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

On rp2, the TinyUSB host task only runs when core 0 drains the background callback queue. The PIO USB host runs on core 1 and posts its events there, but port_wake_main_task() was a no-op, so core 1 could not wake core 0 out of the WFI in port_idle_until_interrupt(). While a program is in time.sleep(), core 0 stays parked in that WFI for the whole sleep, so host events never get serviced. A device plugged in while a program sleeps can fail to enumerate at all.

Implement port_wake_main_task() as SEV and idle with WFE instead of WFI, so core 1 can wake core 0. SEVONPEND keeps the old wake on pending interrupt behavior. It lives in shared port.c, so it covers both rp2040 and rp2350. 12 lines of code.

Start this with nothing in the host port, then hot-plug a USB device while it runs:

import time
import usb.core

print("plug a device into the host port now")
while True:
    devices = list(usb.core.find(find_all=True))
    if devices:
        print("enumerated:", devices[0].product)
        break
    time.sleep(0.5)

On the current main it never prints enumerated, the device never shows up while the program sleeps (over 50 seconds):

With this PR it enumerates within one loop iteration:

plug a device into the host port now
enumerated: Macropad RP2040

Confirmed on a Fruit Jam (RP2350) and a Feather RP2040 USB Host (RP2040).

Why: on rp2 the TinyUSB host task only runs when core 0 drains the background
callback queue. The PIO USB host runs on core 1 and posts its events there,
but port_wake_main_task() was a no-op, so core 1 could not wake core 0 out of
the WFI in port_idle_until_interrupt(). While a program is in time.sleep(),
core 0 stays parked in that WFI for the whole sleep, so host events never get
serviced. A device plugged in while a program sleeps can fail to enumerate.

What: implement port_wake_main_task() as SEV and idle with WFE instead of WFI,
so core 1 can wake core 0. SEVONPEND keeps the old wake on pending interrupt
behavior. Shared port.c, so it covers both rp2040 and rp2350.

Testing: a program looping "find(); time.sleep(0.5)" with no other USB polling.
Plug a device into the host port while it runs. On main it never enumerates
(over 50 seconds). With this change it enumerates within one loop iteration.
Confirmed on a Fruit Jam (RP2350) and a Feather RP2040 USB Host (RP2040).
@mikeysklar
mikeysklar marked this pull request as ready for review July 18, 2026 02:31
@dhalbert

Copy link
Copy Markdown
Collaborator

Did you try running something like Fruit Jam OS to smoke test general operation? Thanks if you can.

@mikeysklar

mikeysklar commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Dan -

Loaded up Fruit Jam OS (v1.092) + patched with this PR commit. No issues with using the keyboard from boot to navigating the menus and opening apps. I was able to pull the keyboard and re-insert it multiple times. The keyboard continued to work.

>>> import os
>>> os.uname().version
'10.3.0-alpha.3-36-g691004d7b1-dirty on 2026-07-18'
>>> os.uname().machine
'Adafruit Fruit Jam with rp2350b'
>>>
IMG_0642

@dhalbert dhalbert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for general testing. This looks OK. I don't know how I might have come up with the this fix, though. Hurrah for LLMs.

@dhalbert
dhalbert merged commit 74671c5 into adafruit:main Jul 19, 2026
172 checks passed
@mikeysklar

mikeysklar commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

The LLM added a lot of instrumentation and at one point looked at how espressif was doing port_wake_main_task() and noticed that only RP2 didn't have a mechanism for core 1 to wake core 0. Anyway...Thanks Claude (Fable 5). That goes away tomorrow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants