2. PCIe 基础回顾:总线拓扑、配置空间与中断机制
在深入 xHCI 驱动与 PCIe 接口的对接之前,必须牢固掌握 PCIe 子系统的核心基础。本章将系统回顾 PCIe 总线拓扑结构、配置空间(Type0/Type1)、BAR 空间、MSI/MSI-X 中断机制以及 PCIe Capability 结构。这些知识是理解 xHCI 控制器如何被主机枚举、资源分配以及中断处理的基石。
2.1 PCIe 总线拓扑结构
PCIe 采用树形拓扑结构,由根复合体(Root Complex)、交换器(Switch)和端点(Endpoint)组成。与传统的 PCI 并行总线不同,PCIe 使用点对点串行链路,每个设备通过独立的通道(Lane)与上游设备通信。
2.2 配置空间:Type0 与 Type1
每个 PCIe 设备(包括桥设备)都必须实现配置空间。配置空间是 256 字节或 4096 字节(扩展配置空间)的寄存器集合,用于设备识别、资源请求和状态控制。根据设备类型,配置空间分为 Type0(端点设备)和 Type1(桥设备,如 Switch 的上行/下行端口)。
| 特性 | Type0 配置空间 | Type1 配置空间 |
|---|---|---|
| 适用设备 | 端点(EP)、xHCI 控制器 | PCIe 桥、Switch 端口 |
| BAR 寄存器数量 | 6 个(BAR0 ~ BAR5) | 2 个(BAR0 ~ BAR1) |
| 桥接功能 | 无 | 包含主总线号、次总线号、从总线号 |
| 配置空间大小 | 256 字节(标准)+ 扩展 | 256 字节(标准)+ 扩展 |
| 关键寄存器 | Vendor ID、Device ID、Class Code、BAR | Vendor ID、Device ID、Bus Numbers、Memory/IO Base/Limit |
2.3 BAR 空间:基地址寄存器
BAR(Base Address Register)是配置空间中最重要的资源请求机制。设备通过 BAR 向系统声明需要的 MMIO(Memory-Mapped I/O)或 I/O 空间大小。BIOS/UEFI 或操作系统在枚举时分配物理地址,并将基地址写入 BAR 寄存器。
- MMIO BAR: 最常用类型,xHCI 控制器使用 MMIO BAR 映射寄存器空间。
- I/O BAR: 传统 PCI 遗留机制,PCIe 设备通常不使用。
- 64 位 BAR: 占用连续两个 BAR 寄存器(如 BAR0 + BAR1),支持 64 位地址空间。
- 可预取(Prefetchable): 允许 CPU 预取数据,适用于帧缓冲等场景。
pci_resource_start() 和 pci_resource_len() 用于获取 BAR 的物理地址和长度。xHCI 驱动通过 ioremap() 将 MMIO 区域映射到内核虚拟地址空间。
// 示例:xHCI 驱动获取 BAR0 并映射
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long mmio_start = pci_resource_start(pdev, 0);
unsigned long mmio_len = pci_resource_len(pdev, 0);
// 映射到内核虚拟地址
struct xhci_hcd *xhci;
xhci->cap_regs = ioremap(mmio_start, mmio_len);
if (!xhci->cap_regs) {
dev_err(&pdev->dev, "ioremap failed for BAR0\n");
return -ENOMEM;
}
dev_info(&pdev->dev, "MMIO mapped at 0x%p (phys 0x%lx)\n",
xhci->cap_regs, mmio_start);
2.4 MSI/MSI-X 中断机制
PCIe 设备支持三种中断机制:传统 INTx、MSI(Message Signaled Interrupt)和 MSI-X。xHCI 控制器强烈推荐使用 MSI-X,因为它支持大量中断向量(最多 2048 个),且每个向量可以独立配置。
| 特性 | INTx | MSI | MSI-X |
|---|---|---|---|
| 中断方式 | 边带信号(Sideband) | 内存写事务(In-band) | 内存写事务(In-band) |
| 最大向量数 | 4(共享) | 32(1, 2, 4, 8, 16, 32) | 2048 |
| 向量独立配置 | 不支持 | 不支持 | 支持(每个向量独立地址和数据) |
| xHCI 推荐度 | 不推荐 | 可选 | 强烈推荐 |
| 中断亲和性 | 不支持 | 有限支持 | 完全支持(每个 CPU 核绑定) |
pci_enable_msi() 的返回值。MSI 向量数必须为 2 的幂,且 xHCI 需要至少 8 个中断向量(事件环、命令环、各端口状态变更)。如果 MSI 向量不足,驱动可能无法正常工作。
2.5 PCIe Capability 结构
PCIe 使用 Capability 链表机制扩展配置空间功能。每个 Capability 结构以 8 位 Capability ID 开头,后跟 8 位 Next Capability Pointer,形成单向链表。xHCI 驱动需要解析多个 Capability 结构来获取设备特性。
- PCI Power Management Capability (Cap ID 0x01): 控制设备电源状态 D0-D3hot。
- MSI Capability (Cap ID 0x05): 配置 MSI 中断向量数和地址。
- MSI-X Capability (Cap ID 0x11): 配置 MSI-X 中断,包含 Table 和 PBA 的 BAR 索引和偏移。
- PCI Express Capability (Cap ID 0x10): 核心 PCIe 能力,包含设备类型、链路状态、错误报告等。
- Device Serial Number Capability (Cap ID 0x03): 64 位唯一设备序列号。
pci_find_capability() 和 pci_find_ext_capability() 遍历 Capability 链表。xHCI 驱动通过 pci_find_capability(pdev, PCI_CAP_ID_MSIX) 检查 MSI-X 支持。
// 示例:遍历 PCIe Capability 链表
u8 cap_ptr = pdev->pcie_cap; // PCIe Capability 偏移
if (cap_ptr) {
u16 pcie_cap_reg;
pci_read_config_word(pdev, cap_ptr + PCI_EXP_DEVCTL, &pcie_cap_reg);
dev_info(&pdev->dev, "PCIe Capability at 0x%x, Device Control: 0x%04x\n",
cap_ptr, pcie_cap_reg);
}
// 检查 MSI-X Capability
int msix_cap = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
if (msix_cap) {
dev_info(&pdev->dev, "MSI-X Capability found at offset 0x%x\n", msix_cap);
}