跳到主要内容

RK3588 DWC3 DMA 寻址能力分析

问题现象

在 RK3588 设备上测试 CrabUSB DWC3 驱动时发现:

  • 32 位地址作为 DMA buffer 时,工作正常 ✅
  • 36 位地址(超过 4GB)时,读取数据全为 0 ❌

根本原因

xHCI 控制器寻址能力

xHCI 规范定义了控制器的寻址能力通过 HCCPARAMS1 寄存器的 Bit[0] (AC64) 来标识:

AC64 位寻址能力说明
164-bit addressing控制器支持完整的 64 位物理地址
032-bit addressing控制器只支持 32 位物理地址(< 4GB)

Linux 内核的处理逻辑

Linux xHCI 驱动(drivers/usb/host/xhci.c)的标准流程:

// 读取 HCCPARAMS1 寄存器
if (HCC_64BIT_ADDR(xhci->hcc_params) &&
!dma_set_mask(dev, DMA_BIT_MASK(64))) {
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
} else {
retval = dma_set_mask(dev, DMA_BIT_MASK(32));
xhci_dbg(xhci, "Enabling 32-bit DMA addresses.\n");
}

RK3588 DWC3 控制器的实际情况

关键发现:RK3588 的 DWC3 xHCI 控制器 AC64=0,只支持 32 位地址寻址!

这解释了为什么:

  • ✅ 32 位地址(< 4GB)正常工作
  • ❌ 36 位地址(≥ 4GB)全零读取

DWC3 控制器地址范围

  • 物理地址:0x0000_0000 - 0xFFFF_FFFF(32 位)
  • 如果物理内存超过 4GB,高地址部分无法直接用于 DMA

解决方案

方案 1:自动检测 AC64 位(推荐)✅

CrabUSB 现已实现自动检测:

// usb-host/src/backend/xhci/host.rs:89-107
let hccparams1 = reg.capability.hccparams1.read_volatile();
let ac64 = hccparams1.addressing_capability();

let effective_dma_mask = if ac64 {
dma_mask // 控制器支持 64 位
} else {
u32::MAX as usize // 强制限制在 32 位
};

优点

  • ✅ 自动适配所有 xHCI 控制器
  • ✅ 避免硬编码错误配置
  • ✅ 符合 xHCI 规范标准流程

方案 2:测试代码中固定 32 位 mask

修改文件usb-host/tests/test_dwc.rs:518

// 原代码:
dma_mask: u32::MAX as usize,

// 这已经是正确的!保持不变即可

说明

  • RK3588 DWC3 只支持 32 位地址
  • 使用 u32::MAX 作为 dma_mask 是正确的
  • 自动检测会进一步确保即使传入更大的 mask 也会被限制到 32 位

RK3588 内存布局

从启动日志可以看到:

ram: 0xffff900000200000 -> 0x200000 (低地址,可 DMA)
ram: 0xffff900100000000 -> 0x100000000
ram: 0xffff9003fc500000 -> 0x3fc500000 (高地址,超出 32 位)
ram: 0xffff9004f0000000 -> 0x4f0000000

地址映射

  • 虚拟地址0xffff9000_0000_0000 + 物理偏移
  • 物理地址0x0000_0000 - 0x4f00_0000(38 位地址空间)
  • DMA 可用范围0x0000_0000 - 0xffff_ffff(只使用低 32 位)

DMA 分配策略

  • Sparreal OS 需要从 ZONE_DMA32(< 4GB)分配 DMA 缓冲区
  • 避免分配到高地址内存(> 4GB)

其他 SoC 的寻址能力对比

SoCAC64实际支持DMA mask备注
RK3588032-bitDMA_BIT_MASK(32)✅ CrabUSB 已适配
R-Car1 (假)32-bitDMA_BIT_MASK(32)需要 XHCI_NO_64BIT_SUPPORT quirk
Tegra140-bitDMA_BIT_MASK(40)特殊限制
Histb?32-bitDMA_BIT_MASK(32)强制 32 位
通用平台164-bitDMA_BIT_MASK(64)标准 xHCI 控制器

测试验证

验证步骤

  1. 检查 HCCPARAMS1 寄存器

    cargo test -p crab-usb --test test --target aarch64-unknown-none-softfloat -- --show-output uboot
  2. 确认日志输出

    xHCI: HCCPARAMS1 = 0x????????
    xHCI: Addressing Capability (AC64) = false (32-bit addressing)
    xHCI: Using DMA mask = 0xffffffff
  3. 验证数据传输

    • ✅ 设备描述符应该正常读取
    • ✅ 不应该出现全零数据

相关规范

  • xHCI Specification:Section 5.3.2 - hccparams1 (Offset 0x10)

    • Bit[0]: AC64 - 64-bit Addressing Capability
    • 1b = The xHC supports 64-bit addressing
    • 0b = The xHC does not support 64-bit addressing
  • DWC3 datasheet

    • RK3588 DWC3 控制器限制在 32 位地址空间
    • 不支持高地址 DMA 传输

总结

问题根源:RK3588 DWC3 xHCI 控制器硬件限制在 32 位地址寻址(AC64=0)

解决方案

  1. ✅ CrabUSB 已实现自动检测 AC64 位
  2. ✅ 自动调整 DMA mask 到控制器支持的范围
  3. ✅ 测试代码保持 dma_mask: u32::MAX 即可

Linux 兼容性

  • Linux xHCI 驱动同样通过 AC64 位检测
  • RK3588 平台使用 DMA_BIT_MASK(32)
  • CrabUSB 的实现与 Linux 完全一致

后续优化

  • Sparreal OS 应该实现 ZONE_DMA32 内存池
  • 优先从低 4GB 地址空间分配 DMA 缓冲区
  • 避免高地址内存分配失败