3. Capability Registers 详解:HCCPARAMS、HCCSPARAMS、HCSPARAMS、DCIVERSION 的结构与位域含义

EHCI 主机控制器的寄存器空间分为两部分:Capability Registers(能力寄存器)和Operational Registers(操作寄存器)。Capability Registers 位于基地址偏移 0x00 处,用于描述控制器的硬件能力、版本信息、端口数量等静态属性。驱动在初始化阶段必须首先读取这些寄存器,以确定后续操作寄存器的基地址和可用资源。

本章聚焦于四个核心能力寄存器:HCCPARAMS、HCCSPARAMS、HCSPARAMS 和 DCIVERSION。理解它们的位域布局是编写正确 EHCI 驱动的第一步。

关键概念: Capability Registers 是只读的,其内容在硬件复位后即固定。驱动不应尝试写入这些寄存器。

3.1 能力寄存器整体布局

EHCI 规范定义的能力寄存器从基地址偏移 0x00 开始,其布局如下表所示:

偏移地址 寄存器名称 大小 描述
0x00 CAPLENGTH 8 位 操作寄存器基地址相对于能力寄存器基地址的偏移量
0x01 — 8 位 保留
0x02 HCIVERSION 16 位 主机控制器接口版本号(BCD 编码)
0x04 HCSPARAMS 32 位 结构参数寄存器
0x08 HCCPARAMS 32 位 能力参数寄存器
0x0C DCIVERSION 16 位 调试端口接口版本号(可选)
0x0E — 16 位 保留

驱动通过读取 CAPLENGTH 寄存器(偏移 0x00)获取操作寄存器的起始偏移量,然后才能访问 USBCMD、USBSTS 等操作寄存器。

EHCI 能力寄存器内存布局 CAPLENGTH (8b) Reserved (8b) HCIVERSION (16b) HCSPARAMS (32b) HCCPARAMS (32b) DCIVERSION (16b) Reserved (16b) 0x00 0x04 0x08 0x0C 寄存器访问说明 • 所有寄存器均为只读 • 驱动必须读取 CAPLENGTH 以计算操作寄存器基地址 • DCIVERSION 为可选寄存器 仅当 HCCPARAMS[17]=1 时有效

3.2 HCSPARAMS — 结构参数寄存器

HCSPARAMS(Host Controller Structural Parameters)位于偏移 0x04,32 位宽度。它描述了控制器的物理结构特性,包括端口数量、端口路由逻辑等。

位域 位范围 名称 描述
N_PORTS [3:0] 端口数量 主机控制器支持的 USB 端口数量。有效值 1–15,0 表示保留。
P_INDICATOR [4] 端口指示灯 1 = 支持端口指示灯控制;0 = 不支持。
N_CC [7:5] companion 控制器数量 与 EHCI 配合使用的 UHCI/OHCI 控制器数量。
N_PCC [11:8] 每 companion 的端口数 每个 companion 控制器管理的端口数量。
PORTROUTE [12] 端口路由 1 = 端口路由逻辑实现方式为可配置;0 = 固定路由。
— [31:13] 保留 驱动必须忽略读取值,写入时保持原值。
驱动开发提示: N_PORTS 字段决定了驱动需要初始化和监控的端口数量。在遍历端口时,循环条件应为 port < (hcs_params & 0xF),而非固定值。

端口路由机制(PORTROUTE 位)对于支持 USB 1.1/2.0 分频的控制器至关重要。当 EHCI 控制器将端口所有权交给 companion 控制器时,路由逻辑决定哪个 companion 接管该端口。

HCSPARAMS 位域结构 N_PORTS PI N_CC N_PCC PR Reserved [3:0] [4] [7:5] [11:8] [12] [31:13] HCSPARAMS 关键字段用途 • N_PORTS:驱动初始化端口状态机时,需为每个端口分配 PortSC 寄存器基地址 • PORTROUTE:若为 1,驱动需读取 PORTROUTE 寄存器以确定端口与 companion 的映射关系

3.3 HCCPARAMS — 能力参数寄存器

HCCPARAMS(Host Controller Capability Parameters)位于偏移 0x08,32 位宽度。它描述了控制器的高级能力特性,如 64 位寻址支持、异步/周期性调度能力、调试端口等。

位域 位范围 名称 描述
ADC [0] 64 位寻址能力 1 = 支持 64 位数据结构寻址(使用 CTRLDSSEGMENT 寄存器);0 = 仅支持 32 位。
PFLF [1] 帧列表可编程 1 = 支持通过 FLSIZE 字段调整帧列表大小;0 = 固定 1024 元素。
ASPC [2] 异步调度能力 1 = 支持异步调度(默认开启);0 = 不支持(罕见)。
IST [4:3] 中断阈值 编码中断调度阈值:00=1 微帧,01=2 微帧,10=4 微帧,11=8 微帧。
EECP [15:8] 扩展能力指针 指向 EHCI 扩展能力寄存器链的偏移量(相对于能力寄存器基地址)。0 表示无扩展能力。
DCP [17] 调试端口能力 1 = 支持调试端口(DCIVERSION 寄存器有效);0 = 不支持。
— [31:18] 保留 驱动必须忽略。
常见陷阱: 许多驱动在初始化时忽略 ADC 位,直接假设 32 位寻址。如果硬件支持 64 位寻址而驱动未设置 CTRLDSSEGMENT 寄存器,当 DMA 地址高于 4GB 时会导致数据传输错误。

EECP 字段是 EHCI 扩展能力链的入口点。常见的扩展能力包括:

  • USB Legacy Support(能力 ID = 0x01):用于 BIOS 与操作系统之间的 EHCI 所有权切换。
  • Port Indicator(能力 ID = 0x02):提供端口指示灯控制。
  • Debug Port(能力 ID = 0x0A):提供调试端口功能。
HCCPARAMS 位域与扩展能力链 ADC PFLF ASPC IST RSVD EECP DCP Reserved 扩展能力链(EECP 指向) Legacy Support Port Indicator Debug Port 扩展能力遍历方法 while (eecp != 0) { 读取能力 ID 和下一个指针; 处理能力; eecp = 下一个指针; }

3.4 DCIVERSION — 调试端口接口版本寄存器

DCIVERSION(Debug Control Interface Version)位于偏移 0x0C,16 位宽度。该寄存器为可选寄存器,仅当 HCCPARAMS[17](DCP 位)为 1 时才有效。

位域 位范围 名称 描述
DCI_VERSION [15:0] 调试接口版本 BCD 编码的调试端口接口版本号。例如 0x0100 表示版本 1.00。

调试端口允许通过 USB 进行内核调试。驱动在检测到 DCP=1 后,应读取 DCIVERSION 以确定调试接口的协议版本,然后通过扩展能力链中的 Debug Port 能力寄存器配置调试功能。

工程实践: 在 Linux 内核中,ehci-hcd 驱动通过 ehci_readl 宏读取 HCCPARAMS,然后使用 HCC_EXT_CAPS 宏提取 EECP 字段。调试端口检测在 ehci_pci_setup 函数中完成。

3.5 驱动初始化中的寄存器读取顺序

在 EHCI 驱动初始化阶段,读取 Capability Registers 的典型顺序如下:

  1. 读取 CAPLENGTH(偏移 0x00):获取操作寄存器基地址偏移量。
  2. 读取 HCIVERSION(偏移 0x02):验证接口版本是否为 1.0(0x0100)。
  3. 读取 HCSPARAMS(偏移 0x04):获取端口数量 N_PORTS 和 companion 控制器信息。
  4. 读取 HCCPARAMS(偏移 0x08):获取 64 位寻址能力、帧列表大小、中断阈值和扩展能力指针。
  5. 如果 HCCPARAMS[17]=1,读取 DCIVERSION(偏移 0x0C)以获取调试端口版本。
  6. 遍历扩展能力链:从 EECP 字段开始,处理 Legacy Support、Debug Port 等能力。

以下是一个简化的 C 代码示例,展示如何读取这些寄存器:

/* 假设 base 为能力寄存器基地址(MMIO 映射后的虚拟地址) */
u8 cap_length = ehci_readb(base, 0x00);
u16 hci_version = ehci_readw(base, 0x02);
u32 hcs_params = ehci_readl(base, 0x04);
u32 hcc_params = ehci_readl(base, 0x08);

/* 提取关键字段 */
int n_ports = hcs_params & 0xF;
int adc64 = (hcc_params >> 0) & 0x1;
int ist = (hcc_params >> 3) & 0x3;
int eecp = (hcc_params >> 8) & 0xFF;
int dcp = (hcc_params >> 17) & 0x1;

/* 操作寄存器基地址 */
void __iomem *op_regs = base + cap_length;

/* 如果支持调试端口,读取 DCIVERSION */
if (dcp) {
    u16 dci_version = ehci_readw(base, 0x0C);
    pr_info("EHCI Debug Port version: %x.%x\n",
            (dci_version >> 8) & 0xF, dci_version & 0xF);
}

/* 遍历扩展能力链 */
while (eecp != 0) {
    u32 ext_cap = ehci_readl(base, eecp);
    u8 cap_id = ext_cap & 0xFF;
    u8 next = (ext_cap >> 8) & 0xFF;
    /* 根据 cap_id 处理对应能力 */
    eecp = next;
}
驱动初始化 Capability Registers 读取流程 读取 CAPLENGTH 读取 HCIVERSION 读取 HCSPARAMS 读取 HCCPARAMS DCP? 读取 DCIVERSION

3.6 常见问题与调试技巧

问题 可能原因 排查方法
读取 HCSPARAMS 返回 0 MMIO 基地址错误或 BAR 未正确映射 检查 PCI 配置空间中的 BAR0 值,确认 ioremap 成功
端口数量为 0 N_PORTS 字段被错误掩码 确认使用 hcs_params & 0xF 而非 & 0xFF
64 位寻址未生效 驱动未读取 ADC 位或未设置 CTRLDSSEGMENT 在初始化时检查 HCCPARAMS[0],若为 1 则写入高 32 位地址
扩展能力链遍历死循环 下一个指针指向自身或无效地址 限制遍历深度(如最多 10 次),检查 EECP 是否在有效范围内
调试建议: 在驱动初始化早期,通过 printk 或 dev_dbg 打印所有 Capability Registers 的原始值和解析后的字段,有助于快速定位硬件配置问题。