Skip to content

Add USB Host (XHCI) driver and MSC support for Tegra X1 - #2

Open
nafields wants to merge 2 commits into
masterfrom
claude/usb-host-emummc-plan-6M6mB
Open

Add USB Host (XHCI) driver and MSC support for Tegra X1#2
nafields wants to merge 2 commits into
masterfrom
claude/usb-host-emummc-plan-6M6mB

Conversation

@nafields

@nafields nafields commented Jun 9, 2026

Copy link
Copy Markdown
Owner

This PR adds a complete USB Host (XHCI) driver implementation for Tegra X1, enabling the bootloader to read from USB Mass Storage Class (MSC) devices connected via the dock's USB port.

Summary

Implements a minimal but functional XHCI host controller driver that supports device enumeration, control transfers, and bulk I/O operations. Includes a USB MSC (Mass Storage Class) driver using the BOT (Bulk-Only Transport) protocol and a hub class driver stub for future multi-device support. Integrates USB storage as an alternative emuMMC backend alongside existing SD card and file-based options.

Key Changes

Core XHCI Host Driver (bdk/usb/usbh.c)

  • Full XHCI host controller initialization including PHY setup, clock configuration, and ring buffer management
  • Command ring operations: ENABLE_SLOT, ADDRESS_DEVICE, CONFIGURE_EP
  • Control transfer (EP0) implementation with setup, data, and status stages
  • Bulk transfer support (IN/OUT) for endpoints 1
  • Port detection, reset, and device speed negotiation
  • Device enumeration with descriptor reading and configuration
  • Hub detection with stub for future multi-device support

USB MSC Driver (bdk/usb/usbh_msc.c)

  • BOT (Bulk-Only Transport) protocol implementation
  • SCSI command support: TEST_UNIT_READY, INQUIRY, READ_CAPACITY(10), READ(10)
  • Chunked read support for large transfers (up to 1MB per chunk)
  • Device readiness polling and validation

Hub Class Driver (bdk/usb/usbh_hub.c)

  • Hub port enumeration and power management
  • Port status monitoring and reset handling
  • Stub implementation for future multi-slot device support

Block Device Abstraction (bootloader/storage/usb_blkdev.c/h)

  • Thin wrapper presenting USB MSC as a block device interface
  • Compatible with existing emuMMC storage abstraction

Integration

  • Updated emummc.c to support USB as an emuMMC backend with configurable LBA offset
  • Updated main.c to initialize USB host on boot when configured
  • Added USB host buffer definitions to memory_map.h
  • Extended usb_t210.h with XUSB host register definitions
  • Updated emummc.h to include USB configuration fields

Implementation Details

  • Ring buffers (command, event, transfer) are placed in a dedicated 1KB block at XUSB_RING_ADDR with proper 64-byte alignment per XHCI spec
  • Cycle State (PCS/CCS) tracking for ring wrap-around handling
  • Timeout-based polling for controller operations (500ms default)
  • Support for both full-speed (8-byte) and high-speed (64-byte) EP0 max packet sizes
  • Bulk endpoint max packet size detection (64 bytes FS, 512 bytes HS)
  • Mutual exclusion between USB device and host modes via clock/reset control
  • VBUS assertion for downstream device power delivery

https://claude.ai/code/session_01BAVie7bqN6o7NEcR5GSrr6

claude added 2 commits May 31, 2026 16:25
Implements USB SSD as a third emuMMC storage backend alongside the
existing SD-partition and file-based paths.  When usb_enabled=1 and
usb_sector=<lba> are set in the emummc INI, Atmosphere boots from
the USB drive rather than an SD card partition.

New files:
  bdk/usb/usbh.c        — XHCI host controller driver (pure MMIO,
                          no firmware blob); host clocks, PHY init
                          (mirrored from xusbd.c), port reset, ENABLE_SLOT,
                          ADDRESS_DEVICE, CONFIGURE_EP, control/bulk xfers.
                          Ring wrap follows XHCI §4.9.3: link TRB written
                          with current PCS before PCS is toggled.
  bdk/usb/usbh.h        — XHCI TRB constants, ring/context structs, public API.
  bdk/usb/usbh_msc.c    — BOT host driver: CBW→data→CSW, SCSI subset
                          (TEST_UNIT_READY, INQUIRY, READ_CAPACITY(10),
                          READ(10)), sector-chunked reads.
  bdk/usb/usbh_msc.h    — usbh_msc_t struct and declarations.
  bdk/usb/usbh_hub.c    — Hub class driver (powers ports, resets downstream
                          port); full multi-slot re-enumeration is TODO —
                          returns USB_ERROR_INIT with fallback message.
  bootloader/storage/usb_blkdev.c — thin block-device wrapper over usbh_msc.
  bootloader/storage/usb_blkdev.h — usb_blkdev_t and declarations.

Modified files:
  bdk/memory_map.h          — USBH_BULK_IN/OUT_BUF_ADDR + USBH_BULK_BUF_SZ.
  bdk/usb/usb_t210.h        — XUSB_HOST_CONFIGURATION / HOST_CONFIGURATION_EN_FPCI.
  bootloader/storage/emummc.h — usb_enabled + usb_sector fields in emummc_cfg_t.
  bootloader/storage/emummc.c — INI parsing + routing branches for USB path.
  bootloader/main.c         — early emummc_load_cfg() + conditional usbh_init().

https://claude.ai/code/session_01BAVie7bqN6o7NEcR5GSrr6
Verification pass against the XHCI spec, T210 TRM and Linux xhci-tegra
found one missing hard requirement and several fatal bugs:

Missing requirement:
- The XUSB host complex is driven by an integrated Falcon microcontroller;
  the xHCI interface does not function until its firmware is loaded and
  booted (unlike the XUSB device block, which is pure hardware).  Add the
  CSB/L2IMEM firmware loader (mirrors Linux xhci-tegra.c), reading the
  standard tegra21x XUSB firmware from sd:/bootloader/sys/xusbfw.bin into
  a resident DRAM buffer.  Also program the XUSB_FALCON clock (204MHz).

Fatal fixes:
- CLK_SOURCE_XUSB_CORE_HOST is 0x600, not 0x614 (TRM CAR layout around
  the existing 0x608/0x60C/0x610 defines).
- Runtime base had interrupter 0 offset applied twice (rt_base += 0x20 on
  top of 0x20-based register macros), so ERSTBA/ERDP landed on
  interrupter 1 and the event ring was never armed.
- Input context EP entries were indexed by DCI instead of DCI-1, so
  ADDRESS_DEVICE read a zeroed EP0 context.
- VBUS_ID write OR-ed in ID_OVR GND (value 0) without clearing the OVR
  field, leaving ID floating and the controller out of host role.
- IOC was set on Setup/Data/Status TRBs but only 1-2 events were consumed,
  desynchronizing the event ring after the first control transfer.  IOC is
  now only on the final TRB (one event per transfer).
- Ring wrap mid-control-transfer overwrote the Link TRB (no per-TRB wrap
  check); replaced with a _trb_alloc helper that handles wrap per enqueue.
- Unsolicited Port Status Change Events were consumed as command/transfer
  completions; they are now filtered out.
- 1MB bulk chunks overflowed the 17-bit TRB transfer length field; chunk
  at 64KB.
- emummc_storage_read() USB branch returned 0-on-success into a
  1-on-success convention (every successful read reported failure).
- emummc_storage_write() had no USB branch and could fall through to the
  SD-offset path; added a write path (WRITE10 through usbh_msc_write).

Robustness/compat:
- Parse the configuration descriptor for bConfigurationValue and the real
  bulk endpoint addresses/max-packet (devices are not all EP1/EP1); use
  dynamic DCIs for doorbells and endpoint contexts.
- Allocate XHCI scratchpad buffers per HCSPARAMS2 (firmware requires them).
- Wait for USBSTS.CNR before and after HC reset.
- Preserve-mask PED and W1C bits on PORTSC writes (writing PED=1 disables
  the port); acknowledge CSC/PRC after port reset and check PED.
- Source 5V onto VBUS via regulator_5v_usb_src_enable() for the
  handheld/OTG path (Icosa); dock supplies downstream power itself.
- EP0 max packet 64 for FS devices (8 only for LS).
- usbh_msc.c was missing the usbd.h include for USB_RES_*/USB_ERROR_*.

https://claude.ai/code/session_01BAVie7bqN6o7NEcR5GSrr6
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