2. OHCI 主机控制器寄存器架构:关键寄存器详解
OHCI(Open Host Controller Interface)规范定义了一套标准的内存映射寄存器,用于软件(驱动程序)与硬件(主机控制器)之间的通信。理解这些寄存器的布局、位域含义以及读写规则,是进行中断处理和控制传输调试的基础。本章将深入剖析 HcRevision、HcControl、HcCommandStatus 等核心寄存器。
2.1 寄存器整体布局与访问模型
OHCI 主机控制器的寄存器空间起始于一个基地址(由 PCI 配置空间中的 BAR0 给出),总大小为 256 字节。所有寄存器均为 32 位宽,且必须按 32 位对齐访问。下表列出了驱动开发中最常操作的寄存器及其偏移地址:
| 偏移地址 | 寄存器名称 | 缩写 | 核心作用 |
|---|---|---|---|
| 0x00 | HcRevision | HcRevision | 版本与结构信息 |
| 0x04 | HcControl | HcControl | 全局控制与操作模式 |
| 0x08 | HcCommandStatus | HcCommandStatus | 命令触发与状态反馈 |
| 0x0C | HcInterruptStatus | HcInterruptStatus | 中断事件记录(写1清除) |
| 0x10 | HcInterruptEnable | HcInterruptEnable | 中断使能屏蔽 |
| 0x14 | HcInterruptDisable | HcInterruptDisable | 中断禁能屏蔽 |
| 0x18 | HcHCCA | HcHCCA | 主机控制器通信区基址 |
| 0x1C | HcPeriodCurrentED | HcPeriodCurrentED | 当前等时/中断端点描述符指针 |
| 0x20 | HcControlHeadED | HcControlHeadED | 控制端点列表头指针 |
| 0x24 | HcControlCurrentED | HcControlCurrentED | 控制列表当前处理位置 |
| 0x28 | HcBulkHeadED | HcBulkHeadED | 批量端点列表头指针 |
| 0x2C | HcBulkCurrentED | HcBulkCurrentED | 批量列表当前处理位置 |
| 0x30 | HcDoneHead | HcDoneHead | 已完成传输描述符链表头 |
| 0x34 | HcFmInterval | HcFmInterval | 帧间隔与帧长度控制 |
| 0x38 | HcFmRemaining | HcFmRemaining | 当前帧剩余时间 |
| 0x3C | HcFmNumber | HcFmNumber | 当前帧号 |
| 0x40 | HcPeriodicStart | HcPeriodicStart | 周期性传输开始时间点 |
| 0x44 | HcLSThreshold | HcLSThreshold | 低速设备传输阈值 |
| 0x48 | HcRhDescriptorA | HcRhDescriptorA | 根集线器特性描述符 A |
| 0x4C | HcRhDescriptorB | HcRhDescriptorB | 根集线器特性描述符 B |
| 0x50 | HcRhStatus | HcRhStatus | 根集线器状态 |
| 0x54 | HcRhPortStatus[1] | HcRhPortStatus | 端口 1 状态(后续端口偏移 +4) |
HcRevision 确认控制器版本,然后通过 HcControl 设置操作模式。任何对 HcCommandStatus 的写入操作,都应通过回读来确认命令已被硬件接收。
2.2 HcRevision:版本与结构信息
HcRevision 寄存器(偏移 0x00)是一个只读寄存器,用于标识 OHCI 规范的版本号以及控制器支持的特性。其位域定义如下:
| 位域 | 宽度 | 名称 | 描述 |
|---|---|---|---|
| 31:8 | 24 | Reserved | 保留位,必须返回 0 |
| 7:4 | 4 | 结构版本 | 指示控制器内部结构版本,通常为 0x0 |
| 3:0 | 4 | OHCI 版本 | OHCI 规范版本号:0x10 表示 1.0,0x11 表示 1.1 |
在驱动加载时,通过读取此寄存器可以判断控制器是否支持 OHCI 1.1 新增的特性(如增强型帧管理)。如果版本号低于 0x10,驱动应拒绝加载。
// 假设 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:11 | 21 | Reserved | 保留位,写入必须为 0 |
| 10 | 1 | RWE | 远程唤醒使能 |
| 9 | 1 | RWC | 远程唤醒连接 |
| 8 | 1 | IR | 中断路由:0 = 路由到正常中断线,1 = 路由到 SMI |
| 7:6 | 2 | HCFS | 主机控制器功能状态:00=复位,01=运行,10=挂起,11=保留 |
| 5 | 1 | BLE | 批量列表使能 |
| 4 | 1 | CLE | 控制列表使能 |
| 3:2 | 2 | PLE | 周期性列表使能(等时 + 中断) |
| 1 | 1 | IE | 中断使能(全局中断开关) |
| 0 | 1 | R | 运行/停止:0=停止,1=运行 |
驱动初始化序列中,HcControl 的配置顺序至关重要:
- 首先将
HCFS设置为 00(复位状态),等待 10ms。 - 将
HCFS设置为 01(运行状态),同时设置R位为 1。 - 根据传输类型需求,依次使能
CLE、BLE、PLE。 - 最后设置
IE位使能全局中断。
HCFS 为运行状态之前,就尝试使能列表或中断,会导致控制器行为不可预测。调试时若发现控制传输无响应,应首先检查 HcControl 的 HCFS 和 R 位是否已正确置位。
2.4 HcCommandStatus:命令触发与状态反馈
HcCommandStatus 寄存器(偏移 0x08)用于软件向主机控制器发送命令,并反映控制器的当前状态。该寄存器具有“写1触发”的特性,即软件通过写入特定位置 1 来发起操作,硬件在处理完成后自动清零。
| 位域 | 宽度 | 名称 | 描述 |
|---|---|---|---|
| 31:18 | 14 | Reserved | 保留位 |
| 17 | 1 | OCR | 过流状态变化(只读) |
| 16 | 1 | BLF | 批量列表填充标志(只读) |
| 15:4 | 12 | Reserved | 保留位 |
| 3 | 1 | CLF | 控制列表填充标志(只读) |
| 2 | 1 | HCR | 主机控制器复位(写1触发复位) |
| 1 | 1 | LF | 列表填充标志(写1触发列表处理) |
| 0 | 1 | HCFS_Change | 功能状态变化(只读) |
在控制传输调试中,CLF 和 LF 位是核心关注点:
- CLF(控制列表填充标志):当硬件检测到控制列表中有新的端点描述符(ED)加入时,会自动置位此标志。驱动也可以通过写入
LF位来强制触发列表处理。 - LF(列表填充标志):驱动写入 1 后,主机控制器会立即扫描控制列表和批量列表,处理所有待处理的传输描述符(TD)。此操作在中断处理中常用于“重新触发”列表处理。
HcDoneHead 为空但预期应有完成传输,可以尝试读取 HcCommandStatus 的 CLF 位。若 CLF 为 1,说明硬件认为列表仍有待处理项,此时应检查 ED 的 Halted 位或 TD 的 CC 码。
2.5 寄存器协同关系:从初始化到中断
下图展示了 HcControl、HcCommandStatus 和 HcInterruptStatus 在驱动初始化及一次控制传输中的协同工作流程:
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,确认硬件能正确完成传输后,再切换到中断模式。