2. OHCI 主机控制器寄存器架构:关键寄存器详解

OHCI(Open Host Controller Interface)规范定义了一套标准的内存映射寄存器,用于软件(驱动程序)与硬件(主机控制器)之间的通信。理解这些寄存器的布局、位域含义以及读写规则,是进行中断处理和控制传输调试的基础。本章将深入剖析 HcRevision、HcControl、HcCommandStatus 等核心寄存器。

2.1 寄存器整体布局与访问模型

OHCI 主机控制器的寄存器空间起始于一个基地址(由 PCI 配置空间中的 BAR0 给出),总大小为 256 字节。所有寄存器均为 32 位宽,且必须按 32 位对齐访问。下表列出了驱动开发中最常操作的寄存器及其偏移地址:

偏移地址寄存器名称缩写核心作用
0x00HcRevisionHcRevision版本与结构信息
0x04HcControlHcControl全局控制与操作模式
0x08HcCommandStatusHcCommandStatus命令触发与状态反馈
0x0CHcInterruptStatusHcInterruptStatus中断事件记录(写1清除)
0x10HcInterruptEnableHcInterruptEnable中断使能屏蔽
0x14HcInterruptDisableHcInterruptDisable中断禁能屏蔽
0x18HcHCCAHcHCCA主机控制器通信区基址
0x1CHcPeriodCurrentEDHcPeriodCurrentED当前等时/中断端点描述符指针
0x20HcControlHeadEDHcControlHeadED控制端点列表头指针
0x24HcControlCurrentEDHcControlCurrentED控制列表当前处理位置
0x28HcBulkHeadEDHcBulkHeadED批量端点列表头指针
0x2CHcBulkCurrentEDHcBulkCurrentED批量列表当前处理位置
0x30HcDoneHeadHcDoneHead已完成传输描述符链表头
0x34HcFmIntervalHcFmInterval帧间隔与帧长度控制
0x38HcFmRemainingHcFmRemaining当前帧剩余时间
0x3CHcFmNumberHcFmNumber当前帧号
0x40HcPeriodicStartHcPeriodicStart周期性传输开始时间点
0x44HcLSThresholdHcLSThreshold低速设备传输阈值
0x48HcRhDescriptorAHcRhDescriptorA根集线器特性描述符 A
0x4CHcRhDescriptorBHcRhDescriptorB根集线器特性描述符 B
0x50HcRhStatusHcRhStatus根集线器状态
0x54HcRhPortStatus[1]HcRhPortStatus端口 1 状态(后续端口偏移 +4)
调试提示: 在驱动初始化阶段,建议首先读取 HcRevision 确认控制器版本,然后通过 HcControl 设置操作模式。任何对 HcCommandStatus 的写入操作,都应通过回读来确认命令已被硬件接收。

2.2 HcRevision:版本与结构信息

HcRevision 寄存器(偏移 0x00)是一个只读寄存器,用于标识 OHCI 规范的版本号以及控制器支持的特性。其位域定义如下:

位域宽度名称描述
31:824Reserved保留位,必须返回 0
7:44结构版本指示控制器内部结构版本,通常为 0x0
3:04OHCI 版本OHCI 规范版本号:0x10 表示 1.0,0x11 表示 1.1

在驱动加载时,通过读取此寄存器可以判断控制器是否支持 OHCI 1.1 新增的特性(如增强型帧管理)。如果版本号低于 0x10,驱动应拒绝加载。

代码示例:读取并解析 HcRevision
// 假设 base_addr 为寄存器基地址
uint32_t rev = readl(base_addr + 0x00);
uint8_t ohci_ver = rev & 0x0F;
uint8_t struct_ver = (rev >> 4) & 0x0F;

if (ohci_ver < 0x10) {
    // 错误:不支持的 OHCI 版本
    return -ENODEV;
}
printk("OHCI Revision: 0x%x, Structure: 0x%x\n", ohci_ver, struct_ver);

2.3 HcControl:全局控制与操作模式

HcControl 寄存器(偏移 0x04)是驱动初始化阶段最关键的寄存器之一。它控制主机控制器的运行状态、操作模式以及列表处理策略。其位域结构如下:

位域宽度名称描述
31:1121Reserved保留位,写入必须为 0
101RWE远程唤醒使能
91RWC远程唤醒连接
81IR中断路由:0 = 路由到正常中断线,1 = 路由到 SMI
7:62HCFS主机控制器功能状态:00=复位,01=运行,10=挂起,11=保留
51BLE批量列表使能
41CLE控制列表使能
3:22PLE周期性列表使能(等时 + 中断)
11IE中断使能(全局中断开关)
01R运行/停止:0=停止,1=运行

驱动初始化序列中,HcControl 的配置顺序至关重要:

  1. 首先将 HCFS 设置为 00(复位状态),等待 10ms。
  2. 将 HCFS 设置为 01(运行状态),同时设置 R 位为 1。
  3. 根据传输类型需求,依次使能 CLE、BLE、PLE。
  4. 最后设置 IE 位使能全局中断。
常见错误: 在未设置 HCFS 为运行状态之前,就尝试使能列表或中断,会导致控制器行为不可预测。调试时若发现控制传输无响应,应首先检查 HcControl 的 HCFS 和 R 位是否已正确置位。

2.4 HcCommandStatus:命令触发与状态反馈

HcCommandStatus 寄存器(偏移 0x08)用于软件向主机控制器发送命令,并反映控制器的当前状态。该寄存器具有“写1触发”的特性,即软件通过写入特定位置 1 来发起操作,硬件在处理完成后自动清零。

位域宽度名称描述
31:1814Reserved保留位
171OCR过流状态变化(只读)
161BLF批量列表填充标志(只读)
15:412Reserved保留位
31CLF控制列表填充标志(只读)
21HCR主机控制器复位(写1触发复位)
11LF列表填充标志(写1触发列表处理)
01HCFS_Change功能状态变化(只读)

在控制传输调试中,CLF 和 LF 位是核心关注点:

  • CLF(控制列表填充标志):当硬件检测到控制列表中有新的端点描述符(ED)加入时,会自动置位此标志。驱动也可以通过写入 LF 位来强制触发列表处理。
  • LF(列表填充标志):驱动写入 1 后,主机控制器会立即扫描控制列表和批量列表,处理所有待处理的传输描述符(TD)。此操作在中断处理中常用于“重新触发”列表处理。
调试技巧: 在控制传输完成中断中,如果发现 HcDoneHead 为空但预期应有完成传输,可以尝试读取 HcCommandStatus 的 CLF 位。若 CLF 为 1,说明硬件认为列表仍有待处理项,此时应检查 ED 的 Halted 位或 TD 的 CC 码。

2.5 寄存器协同关系:从初始化到中断

下图展示了 HcControl、HcCommandStatus 和 HcInterruptStatus 在驱动初始化及一次控制传输中的协同工作流程:

OHCI 关键寄存器协同工作流程 OHCI 关键寄存器协同工作流程 驱动初始化 设置 HcControl HCFS=运行, R=1 提交控制 TD 写 HcCommandStatus LF=1 触发列表处理 硬件扫描列表 传输完成 TD 移入 Done 队列 HcInterruptStatus 写中断描述符完成位 驱动中断处理 读取 HcDoneHead 处理完成 TD 写1清除中断状态 关键调试点: 1. 初始化后检查 HcControl 的 HCFS 和 R 位是否置位;2. 提交 TD 后检查 HcCommandStatus 的 CLF 位是否自动置位;3. 中断触发后立即读取 HcDoneHead,避免被后续中断覆盖。

2.6 调试中的寄存器观测要点

在协同调试过程中,建议驱动开发者通过以下步骤验证寄存器状态:

寄存器观测清单(控制传输场景)
// 步骤1:确认控制器已运行
uint32_t ctrl = readl(base + HcControl);
assert((ctrl & (1 << 6)) == (1 << 6)); // HCFS == 运行
assert((ctrl & 0x01) == 0x01);            // R == 1

// 步骤2:确认控制列表已使能
assert((ctrl & (1 << 4)) == (1 << 4)); // CLE == 1

// 步骤3:提交TD后,检查CLF是否置位
uint32_t cmd_sts = readl(base + HcCommandStatus);
if (cmd_sts & (1 << 3)) {
    printk("CLF set: hardware has pending control work\n");
}

// 步骤4:中断处理中,检查中断状态
uint32_t int_sts = readl(base + HcInterruptStatus);
if (int_sts & (1 << 0)) { // WritebackDoneHead 中断
    uint32_t done_head = readl(base + HcDoneHead);
    printk("Done head pointer: 0x%08x\n", done_head);
}

2.7 寄存器读写注意事项

  • 写1清除规则:HcInterruptStatus 和 HcCommandStatus 中的某些位采用“写1清除”机制。驱动在清除中断状态时,必须读取原始值,然后只将需要清除的位写1,不能直接写0。
  • 保留位处理:所有保留位在写入时必须保持为0,读取时应忽略其值。违反此规则可能导致控制器行为异常或后续版本不兼容。
  • 同步访问:在 SMP(对称多处理)系统中,对 HcControl 和 HcCommandStatus 的访问应使用内存屏障(如 wmb())确保写操作顺序。
  • 轮询 vs 中断:在调试初期,建议先使用轮询方式读取 HcInterruptStatus 和 HcDoneHead,确认硬件能正确完成传输后,再切换到中断模式。
实战建议: 在驱动代码中,为每个关键寄存器定义调试打印宏。在控制传输的每个阶段(提交前、提交后、中断处理中、处理完成后)打印相关寄存器值,可以极大缩短定位问题的时间。