PCI 结构概述

来自Jack's Lab
跳转到: 导航, 搜索

目录

1 概述

虽 PCI 设计时与体系结构无关,但笔者还是愿意聊聊在非 x86 体系上 PCI 的细节

PCI 全称 Peripheral Component Interconnect,本地话叫外围设备互联,顾名思义,其就是用于连接外设的,比如声卡、网卡等

现代 CPU 的速度都远远快于外围设备,这个速度间的不匹配就需要一个“连接器”(桥,Bridge)来缓冲一下。因此 CPU 都是直接和一个主桥相联,这个主桥下就是 0 号 PCI 总线。(MIPS 和主桥间是通过 64 bit SysAD 总线,在 x86 上是通过前端总线相联)。下图就是早期福珑迷你 PC 的框图:

Fulong.board.arch.png


上图的 North Bridge 就是一个 PCI 主桥,由此引出的总线就是 0 号 PCI 总线。可以看到 PCI Bus 0 上挂了一个 7000M 的显卡,一个 8139 的网卡,一个 NEC USB 2.0 的芯片以及一个 VIA 的南桥。


再看一个 MIPS 公司的 malta 评估板的框图:

Malta.board.arch.jpg


该评估板广泛用于 MIPS 公司自己的 MIPS Core 的评估,4K,5K,24K,34K 系列皆用该板,只是 Core 不一样而已。

Malta 这个主桥也是通过 SysAD 总线和 CPU 相联,不过他还把内存控制器放到了这个主桥内。出来的 PCI Bus 0 也是挂了一个 AMD 的网卡,一个 Crystal 的声卡,还有一个 Intel PIIX4E 的南桥。



2 PCI 设备寻址

PCI 设备由一个 8 bit 的总线号,一个 5 bit 的设备编号以及一个 3 bit 的功能编号来标识

因此一个主桥下最多拥有 256 个总线,这个对大型系统上而言是不够的,为此 Linux 引入 PCI 域的概念,每个 PCI 域可拥有 256 个总线,而每个总线可有 32 个设备,每个设备则可以是多功能板(如音频设备加 CD-ROM 驱动器,最多 8 个功能)。所以每个功能都可以用一个 16 bit 的值来标识,该值用作 PCI 总线内设备的唯一地址

例如在 Malta 板上,lspci 则有如下输出:

0000:00:00.0   Galileo GT64xxx Bridge(0x11ab/0x4620)
0000:00:0a.0   Intel PIIX4 Bridge (0x8086/0x7110)
0000:00:0a.1   Intel PIIX4 IDE (0x8086/0x7111)
0000:00:0a.2   Intel PIIX4 USB (0x8086/0x7112)
0000:00:0a.3   Intel PIIX4 Power (0x8086/0x7113)
0000:00:0b.0   AMD PCNET32 ethernet (0x1022/0x2000)
0000:00:0c.0   Crystal sound card (0x1013/0x6005)

0000 即为 PCI 域

00 则为 PCI Bus 号

00, 0a, 0b, 0c 则为设备号

0, 1, 2, 3 则为功能号,其皆属于 0a 这个设备,则 IDE, USB, Power 皆在同一 PIIX4 桥内



3 调试技巧

3.1 初始化时让 Kernel 忽略某些设备

即让该设备不参与 ioport, iomem 及 irq 资源的分配

常用于资源冲突时,排除该设备的影响

具体的,只要修改 pci_scan_device() 这个函数(位于 drivers/pci/probe.c)的行为,在 pci_setup_device() 之前 return 一个空指针即可,如要忽略所有 Intel 的 PCI 设备,则:

if (0x8086 == (l & 0xffff))
     return NULL;


l & 0xffff 所得即为设备的 Vendor ID,0x8086 为 Intel PCI 设备的 Vendor ID

再如忽略一个 Intel 的、Device ID 为 0x7113 的设备:

if ( (0x8086 == (l & 0xffff)) && (0x7113 == ((l >> 16) & 0xffff)) )
     return NULL;



3.2 初始化时拒绝 Kernel 重新分配的 ioport, iomem, irq 等 resource

即使用 bootloader 已经为该设备分配的 resource

PCI 子系统在读取设备配置空间的 ioports, iomem 等 resource 后,会调用 pci_bus_alloc_resource() 为之从内核的 resource 里分配,尔后通过调用 pci_update_resource() 将新分配的 resource 写入设备的配置空间寄存器

因此只要在 pci_bus_alloc_resource() 之前直接返回即可,这个需要修改 __pci_assign_resource() 这个函数,其位于 drivers/pci/setup-res.c

即,修改后的 __pci_assign_resource() 为:

static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
int resno)
{
     struct resource *res = dev->resource + resno;
     resource_size_t size, min, align;
     int ret;

     if(dev->vendor == 0x1022) 
         return 0;

......
......


对于 irq 资源的分配干预,则将 pcibios_init() 中的 pci_fixup_irqs() 注去即可,pcibios_init() 位于 arch/mips/pci/pci.c












个人工具
名字空间

变换
操作
导航
工具箱