4. xHCI 操作寄存器详解:USBSTS、USBINTR、CRCR、DCBAAP、CONFIG 寄存器的作用与编程方法

xHCI 主机控制器通过一组 MMIO(Memory-Mapped I/O)寄存器对外暴露其状态与控制接口。在 Linux xHCI 驱动中,正确理解并操作这些寄存器是实现设备枚举、传输调度、中断处理与电源管理的基础。本章聚焦于五个核心寄存器:USBSTS、USBINTR、CRCR、DCBAAP 和 CONFIG,逐一解析其位定义、作用域及编程示例。

寄存器访问前提: 所有 xHCI 寄存器均通过 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) 严重错误,需复位控制器
编程要点: USBSTS 是“写 1 清零”寄存器。驱动在中断处理函数中读取 USBSTS 后,必须将已处理的中断源对应位写 1 清除,否则会持续触发中断。例如: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);
}
USBSTS 寄存器位与驱动处理流程 USBSTS 寄存器位 → 驱动处理流程 HCHalted (位0) HSE (位1) EINT (位2) PCD (位3) CNR (位7) 轮询等待 HCHalted=0 复位控制器 xhci_reset() 事件环处理 xhci_handle_event() 端口变化处理 xhci_port_change() 等待就绪 CNR=0 轮询 写 1 清零:writel(bitmask, &xhci->op_regs->status)

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 位来确认停止状态
常见错误: 驱动在初始化事件环之前使能了 EINT 中断,但事件环指针尚未写入 ERST(Event Ring Segment Table),此时控制器可能因无效的事件环地址产生不可恢复错误。正确的顺序是:先配置 ERST 和事件环基址,再使能 USBINTR.EINT。

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();
命令环与 CRCR 寄存器交互 命令环与 CRCR 寄存器交互 命令环 (Command Ring) TRB 0 (Cycle=1) TRB 1 (Cycle=1) TRB 2 (Cycle=0) ... (Cycle=0) CRCR 寄存器 (64位) 位 63:4 = 命令环基地址 (64B 对齐) 位 3 = CS (Cycle State) 位 0 = RCS (Ring Enable/Stop) writel() 写入基地址

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;
关键约束: DCBAAP 数组必须分配在 64 字节对齐的连续内存中。数组大小至少为 (MaxSlots + 1) 个元素,其中 MaxSlots 从 HCSPARAMS1 寄存器读取。槽位 0 保留给输入设备上下文,驱动在地址分配阶段使用。

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);

// 写入后,控制器开始监听端口事件,驱动可以开始枚举设备
顺序依赖: CONFIG 寄存器必须在 DCBAAP 和命令环(CRCR)配置完成之后写入。如果先写 CONFIG 再配置 DCBAAP,控制器可能因访问无效的设备上下文基地址而产生错误。Linux xHCI 驱动在 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 已写入)
xHCI 核心寄存器初始化顺序 步骤 1 USBSTS 检查 步骤 2 DCBAAP 配置 步骤 3 CRCR 启动 步骤 4 CONFIG 写入 步骤 5 USBINTR 顺序不可颠倒:DCBAAP → CRCR → CONFIG → USBINTR USBSTS 检查贯穿整个过程,用于确认控制器状态 ← 上一章 📖 返回目录 下一章 →