浅谈 Linux 内核开发之 PCI 设备驱动(2)

来源:developerWorks 中国 作者:赵 昊翔
  

  1. 扫描 PCI 总线,识别 PCI 总线上的所有设备。
  2. 对于连接在 PCI 总线上的所有 PCI 桥进行总线编号。
  3. 对于连接在 PCI 总线上的所有 PCI 设备和 PCI 桥进行 Memory/IO 访问空间的配置。

这部分代码在 pciauto_bus_scan() 函数中(位于 arch/ppc/syslib/pci_auto.c 中)。

识别 PCI 总线上的设备

PCI 总线扫描的原理是从总线 0 扫描到总线 255,对于每条总线,系统都会扫描所有(总线号,设备号,功能号),通过 Configuration 方式读出每个设备的 Device ID 和 Vendor ID 寄存器,如果这两个寄存器的值是个有效值(非 0xFFFF),则说明当前设备是个有效的 PCI 设备/桥。进而再读取该设备的 Header Type 寄存器,如果该寄存器为 1,则表示当前设备是 PCI 桥,否则是 PCI 设备。

对所有 PCI 总线进行编号

PCI 桥如何知道它所连接的 PCI 总线情况呢?这就需要对 PCI 桥进行总线编号。前面介绍过 PCI 桥提供了 Primary Bus Number、Secondary Bus Number 和 Subordinate Bus Number 三个寄存器用于标志该桥所连接的 PCI 总线,下面通过一个示例来说明内核对于 PCI 总线是如何进行编号的。


图 6. 示例

  1. 系统运行初始,Bus A 为 0,通过上面的 PCI 总线扫描得到连接在 Bus A 上的 PCI 桥(即图中的 Bridge 1)。
  2. 下面开始设置 Bridge 1 的 Bus 寄存器。将 Primary Bus Number 寄存器设置成 Bus A 的编号,即 0。将 Secondary Bus Number 寄存器设置成 Bus B 的编号,它的值等于(Bus A + 1),也就是 1。由于暂时无法知道该桥所能访问的所有下行总线数目,Subordinate Bus Number 寄存器暂时设置成 0xFF。
  3. 当扫描完所有 Bus A 上所有(设备号,功能号)后,开始扫描 Bus B,Bus B 的编号在扫描完 Bus A 后已经得到,为 1。Bus B 的扫描方法同步骤(1),先扫描出 Bus B 上的 PCI 桥(即图中的 Bridge 2),然后配置 Primary Bus Number 寄存器为 1,Secondary Bus Number 寄存器为 2,而 Subordinate Bus Number 寄存器依然为 0xFF。
  4. Bus B 扫描完后得到 Bus C 的编号,为2。下面开始扫描 Bus C,因为 Bus C 上没有 PCI 桥,于是在扫描完其它(设备号,功能号)后,Bus C 的扫描结束。
  5. 由于 Bridge 2 所能访问到的最大 Bus 编号是 2,因此重新设置 Bridge 2 的 Subordinate Bus Number 寄存器为 2。
  6. 由于 Bridge 1 所能访问到的最大 Bus 编号也是 2,因此重新设置 Bridge 1 的 Subordinate Bus Number 寄存器为 2。
  7. 总线编号结束。

配置访问空间

当系统需要访问 PCI 设备时,它需要产生 Configuration、Memory 或者 IO 的读写操作,对于 Memory/IO 的访问方式来说,它们需要定义一个地址范围,落在这个地址范围的操作会被认为是相应的 Memory/IO 的读写操作。

通常 PCI 设备提供了最多6组 Base Address 寄存器,在 PCI 总线扫描时,每当扫描出一个可用的 PCI 设备后,会对该设备的 Base Address 寄存器进行 Memory/IO 访问空间的配置。

而对于 PCI 桥来说,它只提供了2组 Base Address 寄存器,当 PCI 总线扫描出一个 PCI 桥后,也会对该桥的 Base Address 寄存器进行 Memory/IO 访问空间的配置。

需要注意的是,在构建系统之初,需要明确当前系统的地址范围,划分出特定的物理地址作为 PCI Memory 或者 PCI IO 空间,在给 PCI 设备/桥进行访问空间配置时,就是取事先约定的地址空间中的某段地址进行配置,所有设备/桥的访问地址不能冲突。定义系统的 Memory/IO 访问空间是在 mpc85xx_setup_hose() 函数中提供的(位于 arch/ppc/syslib/ppc85xx_setup.c 中)。

PCI 设备和总线初始化

这一操作在 pcibios_init() 函数中进行(位于 arch/ppc/kernel/pci.c 中)。它会在前面操作结束后,对 PCI 总线和 PCI 设备分别分配 pci_bus 和 pci_dev 类型的节点,并建立如所示的组织结构关系。

Linux 的 PCI 设备初始化

Linux 会将 PCI 的相关信息保存在一个文件中,从而方便用户的查阅。这一文件的创建就在 pci_proc_init() 函数中进行(位于 drivers/pci/proc.c 中)。首先它在 /proc/bus 目录下建立起一个名为 pci 的目录,然后在该目录中建立一个名为 devices 的文件,该文件中存放了当前内核所配置的所有 PCI 设备的信息。

我们通常会使用 lspci 命令来查看系统中的 PCI 设备,这条命令就是从 devices 文件中解析相应的字段来显示的。





总结

PCI 标准更新很快,现在最新的标准被称为 PCI Express,从硬件角度来看与以前的 PCI 有了很大的区别。但是由于 PCI 标准的兼容性,其设备配置空间的布局仍然相同,从而也保证了软件的兼容性,对于 PCI Express 的内核配置与 PCI 基本相同。良好的兼容性——这大概也是 PCI 的一个魅力所在吧!(责任编辑:A6)


时间:2009-06-15 08:53 来源:developerWorks 中国 作者:赵 昊翔 原文链接

好文,顶一下
(2)
100%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量