PCI
Peripheral Component Interconnect ( PCI ),好像它的名字暗示的一样,是描述如何通过一个结构化和可控制的方式把系统中的外设组件连接起来的一个标准。标准的 PCI Local Bus 规范描述了系统组件电气连接的方法和它们行为的方法。本章探讨 Linux 核心如何初始化系统的 PCI 总线和设备。
图 6.1 是一个 PCI 基础的系统的逻辑图。 PCI 总线和 PCI-PCI 桥( bridge )是系统组件联系在一起的粘合剂。 CUP 和 video 设备连在主要的 PCI 总线, PCI 总线 0 。一个特殊的 PCI 设备, PCI-PCI 桥把主总线连接到次 PCI 总线, PCI 总线 1 。按照 PCI 规范的术语, PCI 总线 1 描述成为 PCI-PCI 桥的下游而 PCI 总线 0 是桥的上游。连接在次 PCI 总线上的是系统的 SCSI 和以太网设备。物理上桥、次要 PCI 总线和这两种设备可以在同一块 PCI 卡上。系统中的 PCI-ISA 桥支持老的、遗留的 ISA 设备,本图显示了一个超级 I/O 控制芯片,控制键盘、鼠标和软驱。
6.1 PCI Address Space ( PCI 地址空间)
CPU 和 PCI 设备需要访问它们所共享的内存。这些内存让设备驱动程序控制这些 PCI 设备并在它们之间传递信息。一般地共享的内存包括设备的控制和状态寄存器。这些寄存器用于控制设备和读取它的状态。例如: PCI SCSI 设备驱动程序可以读取 SCSI 设备的状态寄存器,判断它是否可以向 SCSI 磁盘写一块信息。或者它可以写入控制寄存器让它关闭的设备开始运行。
CPU 的使用的系统内存可以用作这种共享内存,但是如果这样的话,每一次 PCI 设备访问内存, CPU 都不得不停顿,等待 PCI 设备完成。对于内存的访问通常有限制,同一时间只能有一个系统组件允许访问。这会使得系统速度降低。允许系统的外部设备在一个不受控的方式下访问主内存也不是一个好主意。这会非常危险:一个恶意的设备会让系统非常不稳定。
外部设备由它们自己的内存空间。 CPU 可以访问这些空间,但是设备对于系统内存的访问受到严格的控制,必须通过 DMA ( Direct Memory Access 直接内存存取)通道。 ISA 设备可以访问两种地址空间: ISA I/O (输入 / 输出)和 ISA 内存。 PCI 由三中: PCI I/O 、 PCI 内存和 PCI 配置空间( configuration space )。 CPU 可以访问所有的地址空间其中 PCI I/O 和 PCI 内存地址空间由设备驱动程序使用而 PCI 配置空间由 Linux 和心中的 PCI 初始化代码使用。
Alpha AXP 处理器没有对于除了系统地址空间之外的地址空间的天生的访问模式。它需要使用支持芯片来访问象 PCI 配置空间这样的其他地址空间。它使用了一个地址空间的映射方案,从巨大的虚拟地址空间中偷出一部分映射到 PCI 地址空间。
6.2 PCI Configuration Headers ( PCI 配置头)
系统中的每一个 PCI 设备,包括 PCI-PCI 桥都由一个配置数据结构,位于 PCI 配置地址空间中。 PCI 配置头允许系统识别和控制设备。这个头位于 PCI 配置地址空间的确切位置依赖于设备使用的 PCI 拓扑。例如,插在 PC 主板一个 PCI 槽位的一个 PCI 显示卡配置头会在一个位置,而如果它被插到另一个 PCI 槽位则它的头会出现在 PCI 配置内存中的另一个位置。但是不管这些 PCI 设备和桥在什么位置,系统都可以发现并使用它们配置头中的状态和配置寄存器来配置它们。
通常,系统的设计使得每一个 PCI 槽位的 PCI 配置头都有一个和它在板上的槽位相关的偏移量。所以,举例来说,板上的第一个槽位的 PCI 配置可能位于偏移 0 而第二个槽位的在偏移 256 (所有的头都一样长度, 256 字节),依此类推。定义了系统相关的硬件机制使得 PCI 配置代码可以尝试检查一个给定的 PCI 总线上的所有可能的 PCI 配置头,试图读取头中的一个域(通常是 Vendor Identification 域)得到一些错误,从而知道那些设备存在而那些设备不存在。 PCI Local Bus 规范描述了一种可能的错误信息:试图读取一个空的 PCI 槽位的 Verdor Identification 和 Device Indentification 域时候返回 0xFFFFFFFF 。
图 6.2 显示了 256 字节的 PCI 配置头的布局。它包括以下域:
参见 include/linux/pci.h
Vendor Identification 唯一的数字,描述这个 PCI 设备的发明者。 Digital 的 PCI Vendor Identification 是 0x1011 而 Intel 是 0x8086 。
Device Identification 描述设备自身的唯一数字。例如 Digital 的 21141 快速以太网设备的设备标识符是 0x0009 。
Status 此域给除了设备的状态,它的位的含义由 PCI Local Bus 规范规定。
Command 系统通过写这个域控制这个设备。例如:允许设备访问 PCI I/O 内存。
Class Code 标识了设备的类型。对于每一种设备都有标准分类:显示、 SCSI 等等。对于 SCSI 的类型编码是 0x0100 。
Base Address Registers 这些寄存器用于确定和分配设备可以使用的 PCI I/O 和 PCI 内存的类型、大小和位置。
Interrupt Pin PCI 卡的物理管脚中的 4 个用于向 PCI 总线传递中断。标准中把它们标记为 A 、 B 、 C 和 D 。 Interrupt Pin 域描述了这个 PCI 设备使用那个管脚。通常对于一个设备来说这时硬件决定的。就是说每一次系统启动的时候,这个设备都使用同一个中断管脚。这些信息允许中断处理子系统管理这些设备的中断。
Interrupt Line PCI 配置头中的 Interrupt Line 域用于在 PCI 初始化代码、设备驱动程序和 Linux 的中断处理子系统之间传递中断控制。写在这里的数字对于设备驱动程序来讲是没有意义的,但是它可以让中断处理程序正确地把一个中断从 PCI 设备发送到 Linux 操作系统中正确的设备驱动程序的中断处理代码处。 Linux 如何处理中断参看第 7 章。
6.3 PCI I/O and PCI Memory Address ( PCI I/O 和 PCI 内存地址)
这两种地址空间用于设备和 CPU 上运行的 Linux 核心的它们的设备驱动程序通讯。例如: DECchip 21141 快速以太网设备把它的内部寄存器映射到了 PCI I/O 空间。然后它的 Linux 设备驱动程序通过读写这些寄存器来控制设备。显示驱动程序通常使用大量的 PCI 内存空间来放置显示信息。
直到 PCI 系统建立起来并使用 PCI 配置头中的 Command 域打开了设备对于这些地址空间的访问为止,设备都无法访问这些空间。应该注意的是只有 PCI 配置代码读写 PCI 配置地址, Linux 的设备驱动程序只是读写 PCI I/O 和 PCI 内存地址。
6.4 PCI-ISA Bridges ( PCI-ISA 桥)
这种桥把对于 PCI I/O 和 PCI 内存地址空间的访问转换成为 ISA I/O 和 ISA 内存访问,用来支持 ISA 设备。现在销售的多数系统都包括几个 ISA 总线插槽和几个 PCI 总线插槽。这种向后的兼容的需要会不断减少,将来会有只有 PCI 的系统。在早期的 Intel 8080 基础的 PC 时代,系统中的 ISA 设备的 ISA 地址空间( I/O 和内存)就被固定下来。甚至一个 S5000 Alpha AXP 基础的计算机系统的 ISA 软驱驱动器的 ISA I/O 地址也会和第一台 IBM PC 一样。 PCI 规范保留了 PCI I/O 和 PCI 内存的地址空间中的较低的区域保留给系统中的 ISA 外设并使用一个 PCI-ISA 桥把所有对于这些区域的 PCI 内存访问转换为 ISA 访问。
6.5 PCI-PCI Bridges ( PCI-PCI 桥)
PCI-PCI 桥是特殊的 PCI 设备,把系统中的 PCI 总线粘和在一起。简单系统中只有一个 PCI 总线,当时单个 PCI 总线可以支持的 PCI 设备的数量有电气限制。使用 PCI-PCI 桥增加更多的 PCI 总线允许系统支持更多的 PCI 设备。这对于高性能的服务器尤其重要。当然, Linux 完全支持使用 PCI-PCI 桥的使用。
6.5.1 PCI-PCI Bridges: PCI I/O and PCI Memory Windows
PCI-PCI 桥只向下游传递对于 PCI I/O 和 PCI 内存读和写的一个子集。例如在图 6.1 中,只有读和写的地址属于 SCSI 或者以太网设备的时候 PCI-PCI 桥才会把读写的地址从 PCI 总线 0 传递到总线 1 ,其余的都被忽略。这种过滤阻止了不必要的地址信息遍历系统。为了达到这个目的, PCI-PCI 桥必须编程设置它们必须从主总线向次总线通过的 PCI I/O 和 PCI 内存地址空间访问的基础( base )和限制。一旦系统中的 PCI-PCI 桥设置好,只要 Linux 设备驱动程序只是通过这些窗口存取 PCI I/O 和 PCI 内存空间, PCI-PCI 桥是不可见的。这是个重要的特性,使得 Linux 的 PCI 设备驱动程序的作者的日子好过了。但是它也让 Linux 下的 PCI-PCI 桥在一定程度上需要技巧才能配置,我们不久就会看到。
6.5.2 PCI-PCI Bridges: PCI Configuration Cycles and PCI Bus Numbering ( PCI-PCI 桥: PCI 配置 cycle 和 PCI 总线编号)
既然 CPU 的 PCI 初始化代码可以定位不在主 PCI 总线上的设备,必须有一种机制使得桥可以决定是否把配置 cycle 从它的主接口传递到次接口上。一个 cycle 就是它显示在 PCI 总线上的地址。 PCI 规范定义了两种 PCI 地址配置格式:类型 0 和类型 1 ,分别在图 6.3 和图 6.4 中显示。类型 0 的 PCI 配置 cycle 不包含总线号,被这个 PCI 总线上的所有的 PCI 设备解释用于 PCI 地址配置。配置 cycle 的位 32 : 11 看作是设备选择域。设计系统的一个方法是让每一个位选择一个不同的设备。这种情况下为 11 可能选择槽位 0 的 PCI 设备,位 12 选择槽位 1 的 PCI 设备,依此类推。另一种方法是把设备的槽位号