基于MIPS的Linux内核PCI子系统分析——PCI总线枚举

A lane is composed of two differential signaling pairs: one pair for receiving data, the other for transmitting. Thus, each lane is composed of four wires or signal traces. Conceptually, each lane is used as a full-duplex byte stream, transporting data packets in eight-bit 'byte' format, between endpoints of a link, in both directions simultaneously.[7] Physical PCI Express slots may contain from one to thirty-two lanes, more precisely 1, 2, 4, 8, 12, 16 or 32 lanes.[6][5] Lane counts are written with an × prefix (for example, ×16 represents a sixteen-lane card or slot), with ×16 being the largest size in common use.

 

与PCI所有设备共享同一条总线资源不同,PCI Express总线采用点对点的串行连接技术 ,能够为每一块设备分配独享通道带宽,不需要在设备之间共享资源,这样充分保障了各设备的带宽资源,提高数据传输速率。

  • 双通道,类似于全双工模式;
  • 灵活扩展行。与PCI不同,PCI Express总线能够延伸到系统之外,采用专用线缆可将各种外设直接与系统内的PCI Express总线连接在一起。
  • 低电源消耗,并具有电源管理功能;
  • 支持设备热插拔和热交换;PCI Express总线接口插槽中含有“热插拔检测信号”
  • 支持同步数据传输;PCI Express总线设备可以通过主机桥接芯片进行基于主机的传输,也可以通过交换器进行点对点传输。
  • 具有数据包和层协议架构。采用分层模式,各层使用专用的协议架构。
  • PCI Express缺乏高速缓存一致性协议;

PCI Express的基本结构包括根组件(Root Complex)、交换器(Switch)和各种终端设备(Endpoint)。根组件可以集成在北桥芯片中,用于处理器和内存子系统与I/O设备之间的链接;交换器用来为I/O总线提供输出端。

PCIe VF 配置空间_ci

 

Four layers:

PCIe VF 配置空间_寄存器_02

 

PCI 总线标准中定义了一套配置空间寄存器用于读取或着设置PCI设备的信息。每个PCI设备/桥都有自己的配置空间寄存器。配置空间共有256字节,设备类型不同,其配置空间的布局也不尽相同。设备类型的区分可以通过配置空间内的Header Type寄存器(0X0E)进行,该寄存器值为0X00表示当前设备为一个PCI设备,0X01表示当前设备是一个PCI桥。配置空间的前64字节是配置空间起始段,它对于每种类型的设备都是相同的。

下图显示了PCI设备的配置空间起始段。

PCIe VF 配置空间_PCIe VF 配置空间_03

下图显示了PCI桥的配置空间起始段。

PCIe VF 配置空间_PCIe VF 配置空间_04

配置空间寄存器有些是只读的,有些是可写的。

Device ID 和 Vendor ID 寄存器

  这两个寄存器分别存放了设备信息和厂商信息(值在0X0000和0XFFFF之间,但不能取0XFFFF),因此软件开发者可以通过读取这两个寄存器的值,并与0XFFFF比较,从而判断当前设备是否有效。

Command和Status寄存器

  Command寄存器存放了设备的配置信息,比如是否允许 Memory/IO方式的总线操作、是否为主设备等。Status寄存器存放了设备的状态信息,比如中断状态、错误状态等。

Header Type寄存器

  这个寄存器定义了设备的类型。

Base Address寄存器

  PCI设备最多有6个Base Address寄存器,而PCI桥最多有2个Base Address寄存器。

  这个寄存器有三个作用:

  • 该寄存器存放了 Memory/IO 访问空间的起始地址;
  • 该寄存器存放了 Memory/IO访问空间的大小,该值可以通过执行下3个步骤获取:

          1. 向寄存器里写0XFFFFFFFF;

    2. 读取寄存器的值,并取反;

          3. 将上一步的值加上1后就是该空间的大小。

  • 该寄存器定义了这段地址空间的访问类型(Memory方式还是IO方式)。

Subordinate Bus Number,Secondary Bus Number和Primary Bus Number寄存器

  这三个寄存器只在PCI桥配置空间中存在,因为PCI桥会连接两条PCI总线,上行的总线称为Primary Bus,下行的总线被称为Secondary Bus, Primary Bus Number和 Secondary Bus Number寄存器分别存储了上行和下行总线的编号,而Subordinate Bus Number寄存器则存储了当前桥所能直接或间接访问到的总线的最大编号。

 

 

Linux PCI子系统扫描系统中的所有PCI总线(深度优先算法),寻找系统中的PCI设备/桥。每条PCI总线都有个编号number,根PCI总线的编号是0。

1)系统当前存在的所有根总线都通过其pci_bus结构体中的node成员链接成一个全局的根总线链表,其表头由struct list_head类型的全局变量pci_root_buses来描述。根总线的下级总线通过其pci_bus.node链接到父总线的children链表中。

2)每个PCI设备由pci_dev表示,pci_dev.bus_list挂入其所在总线的pci_dev结构队列pci_bus.devices,并且使指针pci_dev.bus指向代表着其所在总线的pci_bus结构。如果具体的设备是bridge,则还要使其指针subordinate指向代表着另一条PCI总线的pci_bus结构。

PCI总线扫描从总线0扫描到总线255,对于每条总线,系统都会扫描所有(总线号,设备号,功能号)。PCI自动扫描主要完成以下工作:

1)扫描PCI总线,识别PCI总线上的所有设备。

2)对于连接在PCI总线上的所有PCI桥进行总线编号。

3)对于连接在PCI总线上的所有PCI设备和PCI桥进行Memory/IO 访问访问空间的配置。

关于配置访问空间:

  当系统需要访问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/IO空间,在给PCI设备/桥进行访问空间配置时,就是取事先约定的地址空间中的某段地址进行配置,所有设备/桥的访问地址不能冲突。

 

 

=========需要仔细阅读======PCI & code ==========

X86代码(……)

代码入口:vim arch/x86/pci/legacy.c +/pci_subsys_init  ====>  subsys_initcall(pci_subsys_init);

注:参考的资料(依据2.6)提到 pci_controller,但是(依据3.11)x86没有使用;pci_root_buses已经可以串起所有的PCI根总线……其所述的MEM/IO资源注册,对于x86而言:pcibios_allocate_dev_resources & ioapic_insert_resources

关于注册设备:查询:pci_root_buses