4. xHCI 操作寄存器详解:USBSTS、USBINTR、CRCR、DCBAAP、CONFIG 寄存器的作用与编程方法
xHCI 主机控制器通过一组 MMIO(Memory-Mapped I/O)寄存器对外暴露其状态与控制接口。在 Linux xHCI 驱动中,正确理解并操作这些寄存器是实现设备枚举、传输调度、中断处理与电源管理的基础。本章聚焦于五个核心寄存器:USBSTS、USBINTR、CRCR、DCBAAP 和 CONFIG,逐一解析其位定义、作用域及编程示例。
ioremap 映射后的虚拟地址访问,驱动使用 readl() / writel() 系列函数进行 32 位读写。操作前必须确保 PCIe 配置空间中的 BAR0 已正确启用并分配了内存地址。
4.1 USBSTS(USB Status Register)—— 主机控制器状态寄存器
偏移地址:0x04(CAPLENGTH 之后)
复位值:0x0000 0000(HCHalted 位除外,复位后为 1)
USBSTS 反映 xHCI 控制器的实时运行状态,驱动通过轮询或中断方式读取该寄存器以判断事件是否发生。关键位域定义如下:
| 位 | 名称 | 描述 | 驱动关注点 |
|---|---|---|---|
| 0 | HCHalted | 主机控制器是否处于停止状态(1=停止,0=运行) | 启动/停止流程中必须轮询此位确认状态切换 |
| 1 | HSE | 主机系统错误(严重不可恢复错误) | 触发后驱动应执行复位恢复流程 |
| 2 | EINT | 事件中断挂起(Event Interrupt Pending) | 中断处理程序入口判断依据 |
| 3 | PCD | 端口连接状态变化(Port Change Detect) | 触发端口枚举或断开处理 |
| 4 | SSS | 保存状态已设置(Save State Status) | 电源管理上下文保存完成标志 |
| 5 | RSS | 恢复状态已设置(Restore State Status) | 电源管理上下文恢复完成标志 |
| 6 | SRE | 停止请求错误(Stop Request Error) | 停止命令执行异常时置位 |
| 7 | CNR | 控制器未就绪(Controller Not Ready) | 复位或初始化期间必须等待此位清零 |
| 8 | HCE | 主机控制器错误(Host Controller Error) | 严重错误,需复位控制器 |
writel(USBSTS_EINT | USBSTS_PCD, xhci->op_regs->status);
// Linux 内核 xhci-hcd 驱动中读取 USBSTS 的典型代码片段
u32 status = readl(&xhci->op_regs->status);
if (status & STS_EINT) {
// 处理事件环中断
xhci_handle_event(xhci);
// 清除事件中断挂起位
writel(STS_EINT, &xhci->op_regs->status);
}
if (status & STS_PCD) {
// 处理端口连接状态变化
xhci_port_change_work(xhci);
writel(STS_PCD, &xhci->op_regs->status);
}
if (status & STS_HSE) {
dev_err(xhci->dev, "Host System Error\n");
xhci_halt(xhci);
xhci_reset(xhci);
}
4.2 USBINTR(USB Interrupt Enable Register)—— 中断使能寄存器
偏移地址:0x08
复位值:0x0000 0000(所有中断默认屏蔽)
USBINTR 与 USBSTS 一一对应,用于使能或屏蔽特定中断源。驱动在初始化阶段根据需求设置该寄存器,只有被使能的中断源才能触发 IRQ。
| 位 | 名称 | 对应 USBSTS 位 | 说明 |
|---|---|---|---|
| 0 | HCE | HCHalted | 主机控制器停止中断使能 |
| 1 | HSE | HSE | 主机系统错误中断使能 |
| 2 | EINT | EINT | 事件中断使能(必须使能才能接收事件环通知) |
| 3 | PCD | PCD | 端口连接变化中断使能 |
| 4 | SSS | SSS | 保存状态完成中断使能 |
| 5 | RSS | RSS | 恢复状态完成中断使能 |
// Linux xHCI 驱动初始化时设置中断使能
u32 irq_enable = USBINTR_EINT | USBINTR_PCD | USBINTR_HSE;
writel(irq_enable, &xhci->op_regs->irq_pending);
// 注意:HCE 位通常不使能,驱动通过轮询 HCHalted 位来确认停止状态
4.3 CRCR(Command Ring Control Register)—— 命令环控制寄存器
偏移地址:0x18
大小:64 位(占用两个 32 位寄存器:CRCR_LO 和 CRCR_HI)
CRCR 是 xHCI 驱动向控制器下发命令的通道。驱动通过该寄存器指定命令环的基地址、环大小以及控制标志。命令环是一个循环缓冲区,驱动将命令 TRB(Transfer Request Block)写入环中,然后通过 CRCR 的“环运行”位通知控制器开始处理。
| 位域 | 名称 | 描述 |
|---|---|---|
| 63:4 | RCS | 命令环基地址(64 字节对齐,低 4 位为 0) |
| 3 | CS | 环循环状态(Cycle State),与 TRB 中的 Cycle 位配合 |
| 2 | CA | 命令中止(Command Abort),置 1 中止当前命令 |
| 1 | CRR | 命令环运行(Command Ring Running),只读,指示控制器是否正在处理命令 |
| 0 | RCS | 环使能/停止(Ring Cycle Select),写 1 启动环,写 0 停止 |
// 设置命令环基地址并启动命令环
dma_addr_t cmd_ring_dma = xhci->cmd_ring->dma;
u64 crcr_val = cmd_ring_dma & ~0x1F; // 64 字节对齐
crcr_val |= CRCR_RCS; // 设置 Cycle State
crcr_val |= CRCR_CA; // 先清除可能遗留的中止标志
// 写入 64 位寄存器(分两次 32 位写入)
writel((u32)crcr_val, &xhci->op_regs->cmd_ring_control);
writel((u32)(crcr_val >> 32), &xhci->op_regs->cmd_ring_control + 4);
// 等待 CRR 位变为 1,确认命令环已启动
while (!(readl(&xhci->op_regs->cmd_ring_control) & CRCR_CRR))
cpu_relax();
4.4 DCBAAP(Device Context Base Address Array Pointer)—— 设备上下文基地址数组指针
偏移地址:0x20(64 位寄存器)
作用:指向一个由主机内存分配的数组,数组每个元素是一个 64 位指针,指向对应设备槽位的设备上下文(Device Context)。
DCBAAP 是 xHCI 驱动管理多设备的核心数据结构。每个 USB 设备被分配一个槽位 ID(Slot ID),驱动通过 DCBAAP 数组找到该设备的上下文,进而操作端点、传输数据。
| 槽位 ID | DCBAAP 数组元素 | 指向内容 |
|---|---|---|
| 0 | DCBAAP[0] | 输入设备上下文(Input Device Context),用于地址分配 |
| 1 | DCBAAP[1] | 设备 1 的输出设备上下文(Output Device Context) |
| 2 | DCBAAP[2] | 设备 2 的输出设备上下文 |
| ... | ... | ... |
| N | DCBAAP[N] | 设备 N 的输出设备上下文 |
// 分配 DCBAAP 数组(最大支持 255 个设备)
size_t dcbaap_size = sizeof(dma_addr_t) * (xhci->max_slots + 1);
xhci->dcbaa = dma_alloc_coherent(dev, dcbaap_size, &xhci->dcbaa_dma, GFP_KERNEL);
// 将 DCBAAP 数组的 DMA 地址写入寄存器
writel(lower_32_bits(xhci->dcbaa_dma), &xhci->op_regs->dcbaap);
writel(upper_32_bits(xhci->dcbaa_dma), &xhci->op_regs->dcbaap + 4);
// 当新设备分配槽位时,更新 DCBAAP 数组元素
xhci->dcbaa[slot_id] = output_ctx_dma;
4.5 CONFIG(Configure Register)—— 配置寄存器
偏移地址:0x38
复位值:0x0000 0000
CONFIG 寄存器用于设置 xHCI 控制器启用的设备槽位数,并控制某些全局配置。驱动在初始化最后阶段写入该寄存器,正式激活控制器。
| 位 | 名称 | 描述 |
|---|---|---|
| 7:0 | MaxSlotsEn | 启用的最大设备槽位数(1~255),必须 ≤ HCSPARAMS1.MaxSlots |
| 8 | U3E | USB3 端口使能(Enable USB3 Ports) |
| 9 | U2E | USB2 端口使能(Enable USB2 Ports) |
| 31:10 | Reserved | 保留,必须写 0 |
// 读取 HCSPARAMS1 获取硬件支持的最大槽位数
u32 hcsparams1 = readl(&xhci->cap_regs->hcsparams1);
u32 max_slots = HCS_MAX_SLOTS(hcsparams1);
// 设置 CONFIG 寄存器:启用所有可用槽位,同时使能 USB2/USB3 端口
u32 config_val = (max_slots & 0xFF) | CONFIG_U3E | CONFIG_U2E;
writel(config_val, &xhci->op_regs->config);
// 写入后,控制器开始监听端口事件,驱动可以开始枚举设备
xhci_run() 函数中严格遵循此顺序。
4.6 寄存器初始化顺序总结
五个寄存器的初始化顺序直接影响 xHCI 控制器能否正常工作。以下表格总结了推荐的编程顺序及每个步骤的依赖关系:
| 步骤 | 寄存器 | 操作 | 前置条件 |
|---|---|---|---|
| 1 | USBSTS | 读取确认 HCHalted=1,CNR=0 | 控制器复位完成 |
| 2 | DCBAAP | 分配数组并写入 DMA 地址 | 内存分配成功 |
| 3 | CRCR | 设置命令环基地址并启动 | 命令环 TRB 池已分配 |
| 4 | CONFIG | 写入 MaxSlotsEn 和端口使能 | DCBAAP 和 CRCR 已就绪 |
| 5 | USBINTR | 使能所需中断源 | 事件环已配置(ERST 已写入) |