全部博文(2005)
分类: 嵌入式
2009-09-03 21:08:07
Peripheral Component Interconnect (PCI),
as its name implies is a standard that describes how to connect
the peripheral components of a system together in a structured
and controlled way.
The standard describes
the way that the system components are electrically
connected and the way that they should behave.
This chapter looks at how the Linux kernel initializes the system's PCI
buses and devices.
Figure is a logical diagram of an example PCI based system. The PCI buses and PCI-PCI bridges are the glue connecting the system components together; the CPU is connected to PCI bus 0, the primary PCI bus as is the video device. A special PCI device, a PCI-PCI bridge connects the primary bus to the secondary PCI bus, PCI bus 1. In the jargon of the PCI specification, PCI bus 1 is described as being downstream of the PCI-PCI bridge and PCI bus 0 is up-stream of the bridge. Connected to the secondary PCI bus are the SCSI and ethernet devices for the system. Physically the bridge, secondary PCI bus and two devices would all be contained on the same combination PCI card. The PCI-ISA bridge in the system supports older, legacy ISA devices and the diagram shows a super I/O controller chip, which controls the keyboard, mouse and floppy.
The CPU and the PCI devices need to access memory that is shared between them. This memory is used by device drivers to control the PCI devices and to pass information between them. Typically the shared memory contains control and status registers for the device. These registers are used to control the device and to read its status. For example, the PCI SCSI device driver would read its status register to find out if the SCSI device was ready to write a block of information to the SCSI disk. Or it might write to the control register to start the device running after it has been turned on.
The CPU's system memory could be used for this shared memory but if it were, then every time a PCI device accessed memory, the CPU would have to stall, waiting for the PCI device to finish. Access to memory is generally limited to one system component at a time. This would slow the system down. It is also not a good idea to allow the system's peripheral devices to access main memory in an uncontrolled way. This would be very dangerous; a rogue device could make the system very unstable.
Peripheral devices have their own memory spaces. The CPU can access these spaces but access by the devices into the system's memory is very strictly controlled using DMA (Direct Memory Access) channels. ISA devices have access to two address spaces, ISA I/O (Input/Output) and ISA memory. PCI has three; PCI I/O, PCI Memory and PCI Configuration space. All of these address spaces are also accessible by the CPU with the the PCI I/O and PCI Memory address spaces being used by the device drivers and the PCI Configuration space being used by the PCI initialization code within the Linux kernel.
The Alpha AXP processor does not have natural access to addresses spaces other than the system address space. It uses support chipsets to access other address spaces such as PCI Configuration space. It uses a sparse address mapping scheme which steals part of the large virtual address space and maps it to the PCI address spaces.
Every PCI device in the system, including the PCI-PCI bridges has a configuration data structure that is somewhere in the PCI configuration address space. The PCI Configuration header allows the system to identify and control the device. Exactly where the header is in the PCI Configuration address space depends on where in the PCI topology that device is. For example, a PCI video card plugged into one PCI slot on the PC motherboard will have its configuration header at one location and if it is plugged into another PCI slot then its header will appear in another location in PCI Configuration memory. This does not matter, for wherever the PCI devices and bridges are the system will find and configure them using the status and configuration registers in their configuration headers.
Typically, systems are designed so that every PCI slot has it's PCI Configuration Header in an offset that is related to its slot on the board. So, for example, the first slot on the board might have its PCI Configuration at offset 0 and the second slot at offset 256 (all headers are the same length, 256 bytes) and so on. A system specific hardware mechanism is defined so that the PCI configuration code can attempt to examine all possible PCI Configuration Headers for a given PCI bus and know which devices are present and which devices are absent simply by trying to read one of the fields in the header (usually the Vendor Identification field) and getting some sort of error. The describes one possible error message as returning 0xFFFFFFFF when attempting to read the Vendor Identification and Device Identification fields for an empty PCI slot.
Figure shows the layout of the 256 byte PCI configuration header. It contains the following fields:
=================================
PCI based systems are much more dynamic than ISA based systems. The interrupt pin that an ISA device uses is often set using jumpers on the hardware device and fixed in the device driver. On the other hand, PCI devices have their interrupts allocated by the PCI BIOS or the PCI subsystem as PCI is initialized when the system boots. Each PCI device may use one of four interrupt pins, A, B, C or D. This was fixed when the device was built and most devices default to interrupt on pin A. The PCI interrupt lines A, B, C and D for each PCI slot are routed to the interrupt controller. So, Pin A from PCI slot 4 might be routed to pin 6 of the interrupt controller, pin B of PCI slot 4 to pin 7 of the interrupt controller and so on.
How the PCI interrupts are routed is entirely system specific and there must be some set up code which understands this PCI interrupt routing topology. On Intel based PCs this is the system BIOS code that runs at boot time but for system's without BIOS (for example Alpha AXP based systems) the Linux kernel does this setup.
The PCI set up code writes the pin number of the interrupt controller into the PCI configuration header for each device. It determines the interrupt pin (or IRQ) number using its knowledge of the PCI interrupt routing topology together with the devices PCI slot number and which PCI interrupt pin that it is using. The interrupt pin that a device uses is fixed and is kept in a field in the PCI configuration header for this device. It writes this information into the interrupt line field that is reserved for this purpose. When the device driver runs, it reads this information and uses it to request control of the interrupt from the Linux kernel.
There may be many PCI interrupt sources in the system, for example when PCI-PCI bridges are used. The number of interrupt sources may exceed the number of pins on the system's programmable interrupt controllers. In this case, PCI devices may share interrupts, one pin on the interrupt controller taking interrupts from more than one PCI device. Linux supports this by allowing the first requestor of an interrupt source declare whether it may be shared. Sharing interrupts results in several irqaction data structures being pointed at by one entry in the irq_action vector vector. When a shared interrupt happens, Linux will call all of the interrupt handlers for that source. Any device driver that can share interrupts (which should be all PCI device drivers) must be prepared to have its interrupt handler called when there is no interrupt to be serviced.
These two address spaces are used by the devices to communicate with their device drivers running in the Linux kernel on the CPU. For example, the DECchip 21141 fast ethernet device maps its internal registers into PCI I/O space. Its Linux device driver then reads and writes those registers to control the device. Video drivers typically use large amounts of PCI memory space to contain video information.
Until the PCI system has been set up and the device's access to these address spaces has been turned on using the Command field in the PCI Configuration header, nothing can access them. It should be noted that only the PCI configuration code reads and writes PCI configuration addresses; the Linux device drivers only read and write PCI I/O and PCI memory addresses.
PCI-PCI bridges are special PCI devices that glue the PCI buses of the system together. Simple systems have a single PCI bus but there is an electrical limit on the number of PCI devices that a single PCI bus can support. Using PCI-PCI bridges to add more PCI buses allows the system to support many more PCI devices. This is particularly important for a high performance server. Of course, Linux fully supports the use of PCI-PCI bridges.
So that the CPU's PCI initialization code can address devices that are not on the main PCI bus, there has to be a mechanism that allows bridges to decide whether or not to pass Configuration cycles from their primary interface to their secondary interface. A cycle is just an address as it appears on the PCI bus. The PCI specification defines two formats for the PCI Configuration addresses; Type 0 and Type 1; these are shown in Figure and Figure respectively. Type 0 PCI Configuration cycles do not contain a bus number and these are interpretted by all devices as being for PCI configuration addresses on this PCI bus. Bits 31:11 of the Type 0 configuraration cycles are treated as the device select field. One way to design a system is to have each bit select a different device. In this case bit 11 would select the PCI device in slot 0, bit 12 would select the PCI device in slot 1 and so on. Another way is to write the device's slot number directly into bits 31:11. Which mechanism is used in a system depends on the system's PCI memory controller.
Type 1 PCI Configuration cycles contain a PCI bus number and this type of configuration cycle is ignored by all PCI devices except the PCI-PCI bridges. All of the PCI-PCI Bridges seeing Type 1 configuration cycles may choose to pass them to the PCI buses downstream of themselves. Whether the PCI-PCI Bridge ignores the Type 1 configuration cycle or passes it onto the downstream PCI bus depends on how the PCI-PCI Bridge has been configured. Every PCI-PCI bridge has a primary bus interface number and a secondary bus interface number. The primary bus interface being the one nearest the CPU and the secondary bus interface being the one furthest away. Each PCI-PCI Bridge also has a subordinate bus number and this is the maximum bus number of all the PCI buses that are bridged beyond the secondary bus interface. Or to put it another way, the subordinate bus number is the highest numbered PCI bus downstream of the PCI-PCI bridge. When the PCI-PCI bridge sees a Type 1 PCI configuration cycle it does one of the following things:
So, if we want to address Device 1 on bus 3 of the topology Figure on page we must generate a Type 1 Configuration command from the CPU. Bridge1 passes this unchanged onto Bus 1. Bridge2 ignores it but Bridge3 converts it into a Type 0 Configuration command and sends it out on Bus 3 where Device 1 responds to it.
It is up to each individual operating system to allocate bus numbers during PCI configuration but whatever the numbering scheme used the following statement must be true for all of the PCI-PCI bridges in the system:
``All PCI buses located behind a PCI-PCI bridge must reside between the seondary bus number and the subordinate bus number (inclusive).''
If this rule is broken then the PCI-PCI Bridges will not pass and translate Type 1 PCI configuration cycles correctly and the system will fail to find and initialise the PCI devices in the system. To achieve this numbering scheme, Linux configures these special devices in a particular order. Section on page describes Linux's PCI bridge and bus numbering scheme in detail together with a worked example.
The PCI initialisation code in Linux is broken into three logical parts:
As the Linux kernel initialises the PCI system it builds data structures mirroring the real PCI topology of the system. Figure shows the relationships of the data structures that it would build for the example PCI system in Figure on page .
Each PCI device (including the PCI-PCI Bridges) is described by a pci_dev data structure. Each PCI bus is described by a pci_bus data structure. The result is a tree structure of PCI buses each of which has a number of child PCI devices attached to it. As a PCI bus can only be reached using a PCI-PCI Bridge (except the primary PCI bus, bus 0), each pci_bus contains a pointer to the PCI device (the PCI-PCI Bridge) that it is accessed through. That PCI device is a child of the the PCI Bus's parent PCI bus.
Not shown in the Figure is a pointer to all of the PCI devices in the system, pci_devices. All of the PCI devices in the system have their pci_dev data structures queued onto this queue.. This queue is used by the Linux kernel to quickly find all of the PCI devices in the system.
The PCI device driver is not really a device driver at all but a function of the operating system called at system initialisation time. The PCI initialisation code must scan all of the PCI buses in the system looking for all PCI devices in the system (including PCI-PCI bridge devices).
It uses the PCI BIOS code to find out if every possible slot in the current PCI bus that it is scanning is occupied. If the PCI slot is occupied, it builds a pci_dev data structure describing the device and links into the list of known PCI devices (pointed at by pci_devices).
The PCI initialisation code starts by scanning PCI Bus 0. It tries to read the Vendor Identification and Device Identification fields for every possible PCI device in every possible PCI slot. When it finds an occupied slot it builds a pci_dev data structure describing the device. All of the pci_dev data structures built by the PCI initialisation code (including all of the PCI-PCI Bridges) are linked into a singly linked list; pci_devices.
If the PCI device that was found was a PCI-PCI bridge then a pci_bus data structure is built and linked into the tree of pci_bus and pci_dev data structures pointed at by pci_root. The PCI initialisation code can tell if the PCI device is a PCI-PCI Bridge because it has a class code of 0x060400. The Linux kernel then configures the PCI bus on the other (downstream) side of the PCI-PCI Bridge that it has just found. If more PCI-PCI Bridges are found then these are also configured. This process is known as a depthwise algorithm; the system's PCI topology is fully mapped depthwise before searching breadthwise. Looking at Figure on page , Linux would configure PCI Bus 1 with its Ethernet and SCSI device before it configured the video device on PCI Bus 0.
As Linux searches for downstream PCI buses it must also configure the intervening PCI-PCI bridges' secondary and subordinate bus numbers. This is described in detail in Section below.
For PCI-PCI bridges to pass PCI I/O, PCI Memory or PCI Configuration address space reads and writes across them, they need to know the following:
The problem is that at the time when you wish to configure any given PCI-PCI bridge you do not know the subordinate bus number for that bridge. You do not know if there are further PCI-PCI bridges downstream and if you did, you do not know what numbers will be assigned to them. The answer is to use a depthwise recursive algorithm and scan each bus for any PCI-PCI bridges assigning them numbers as they are found. As each PCI-PCI bridge is found and its secondary bus numbered, assign it a temporary subordinate number of 0xFF and scan and assign numbers to all PCI-PCI bridges downstream of it. This all seems complicated but the worked example below makes this process clearer.
The PCI BIOS functions are a series of standard routines which are common across all platforms. For example, they are the same for both Intel and Alpha AXP based systems. They allow the CPU controlled access to all of the PCI address spaces.
Only Linux kernel code and device drivers may use them.
The PCI fixup code for Alpha AXP does rather more than that for Intel (which basically does nothing).
For Intel based systems the system BIOS, which ran at boot time, has already fully configured the PCI system. This leaves Linux with little to do other than map that configuration. For non-Intel based systems further configuration needs to happen to:
The next subsections describe how that code works.
There are two basic types of Base Address Register, the first indicates within which address space the devices registers must reside; either PCI I/O or PCI Memory space. This is indicated by Bit 0 of the register. Figure shows the two forms of the Base Address Register for PCI Memory and for PCI I/O.
To find out just how much of each address space a given Base Address Register is requesting, you write all 1s (向该寄存器写全1到,如果是32位系统就是0xffffffff) into the register and then read it back. The device will specify zeros in the don't care address bits, effectively specifying the address space required. This design implies that all address spaces used are a power of two and are naturally aligned.[地址值为2^的幂,同时地址都已经按2^的幂实现对齐.luther.gliethttp]如下一篇文章包含了对地址使用上的简短叙述包含一些细致的PCI配置加载流程--PCI卡的WDM驱动开发笔记
For example when you initialize the DECChip 21142 PCI Fast Ethernet device, it tells you that it needs 0x100 bytes of space of either PCI I/O or PCI Memory. The initialization code allocates it space. The moment that it allocates space, the 21142's control and status registers can be seen at those addresses.
The algorithm that Linux uses relies on each device described by the bus/device tree built by the PCI Device Driver being allocated address space in ascending PCI I/O memory order. Again a recursive algorithm is used to walk the pci_bus and pci_dev data structures built by the PCI initialisation code. Starting at the root PCI bus (pointed at by pci_root) the BIOS fixup code:
Taking the PCI system in Figure on page as our example the PCI Fixup code would set up the system in the following way:
For example?
外围设备互连(PCI)是一种将系统中外部设备以结构化与可控制方式连接到起来的总线标准,包括系统部件连接的电气特性及行为。本章将详细讨论Linux核心对系统中的PCI总线与设备的初始化过程。
图6.1是一个基于PCI的系统示意图。PCI总线和PCI-PCI桥接器在连接系统中设备到上起关键作用,在这个系统中CPU和视频设备 被连到PCI bus 0上,它是系统中的主干PCI总线。而PCI-PCI桥接器这个特殊PCI设备将主干总线PCI bus 0与下级总线PCI bus 1连接到一起。PCI标准术语中,PCI bus 1是PCI-PCI桥接器的downstream而PCI bus 0是此桥接器的up-stream。SCSI和以太网设备通过二级PCI总线连接到这个系统中。而在物理实现上,桥接器和二级PCI总线被集成到一块 PCI卡上。而PCI-ISA桥接器用来支持古老的ISA设备,图中有一个高级I/O控制芯片来控制键盘、鼠标及软盘设备。
CPU和PCI设备需要存取在它们之间共享的内存空间。这块内存区域被设备驱动用来控制PCI设备并在CPU与PCI设备之间传递信息。最典型的共享内存包括设备的控制与状态寄存器。这些寄存器用来控制设备并读取其 信息。例如PCI SCSI设备驱动可以通过读取其状态寄存器,找出已准备好将一块数据写入SCSI磁盘的SCSI设备。同时还可以在设备加电后,通过对控制寄存器写入信息来启动设备。
CPU的系统内存可以被用作这种共享内存,但是如果采用这种方式,则每次PCI设备访问此内存块时,CPU将被迫停止工作以等待PCI设备完成此操作。这 种方式将共享内存限制成每次只允许一个系统设备访问。该策略会大大降低系统性能。但如果允许系统外设不受限制地访问主存也不是好办法。它的危险之处在于一 个有恶意行为的设备将使整个系统置于不稳定状态。
外设有其自身的内存空间。CPU可以自由存取此空间,但设备对系统主存的访问将处于DMA(直接内存访问)通道的严格控制下。ISA设备需要存取两个地址空间:ISA I/O(输入输出)和ISA内存。而PCI设备需要访问三种地址空间:PCI I/O、PCI内存和PCI配置空间。CPU则可以访问所有这些地址空间。PCI I/O和 PCI内存由设备驱动程序使用而PCI配置空间被Linux 核心中的PCI初始化代码使用。
Alpha AXP处理器并不能象访问系统地址空间那样随意访问这些地址空间,它只能通过辅助芯片组来存取这些 地址空间,如PCI配置空间。Alpha AXP处理器使用稀疏地址映射策略来从系统巨大的虚拟内存中"窃取"一部分并将其映射到PCI地址空间。
系统中每个PCI设备,包括PCI-PCI桥接器在内,都有一个配置数据结构,它通常位于PCI配置地址空间中。PCI配置头允许系统来标 识与控制设备。配置头在PCI配置空间的位置取决于系统中PCI设备的拓扑结构。例如将一个PCI视频卡插入不同的PCI槽,其配置头位置会变化。但对系 统没什么影响,系统将找到每个PCI设备与桥接器并使用它们配置头中的信息来配置其寄存器。
典型的办法是用PCI槽相对主板的位置来决定其PCI配置头在配置空间中的偏移。比如主板中的第一个PCI槽的PCI配置头位于配置空间偏移0处,而第二 个则位于偏移256处(所有PCI配置头长度都相等,为256字节),其它槽可以由此类推。系统还将提供一种硬件相关机制以便PCI设置代码能正确的辨认 出对应PCI总线上所有存在的设备的PCI配置头。通过PCI配置头中的某些域来判断哪些设备存在及哪些设备不存在(这个域叫厂商标志域: Vendor Identification field)。对空PCI槽中这个域的读操作将得到一个值为0xFFFFFFFF的错误信息。
图6.2给出了256字节PCI配置头的结构,它包含以下域:
这两个地址空间用来实现PCI设备和Linux核心中设备驱动程序之间的通讯。例如DEC21141快速以太网设备的内部寄存器被映射到PIC I/O空间上时,其对应的Linux设备驱动可以通过对这些寄存器的读写来控制此设备。PCI视频卡通常使用大量的PCI内存空间来存储视频信息。
在PCI系统建立并通过用PCI配置头中的命令域来打开这些地址空间前,系统决不允许对它们进行存取。值得注意的是只有PCI配置代码读取和写入PCI配置空间,Linux设备驱动只读写PCI I/O和PCI内存地址。
PCI-PCI桥接器是一种将系统中所有PCI总线连接起来的特殊PCI设备。在简单系统中只存在一条PCI总线,由于受电气特性的限制,它所连接的 PCI设备个数有限。引入PCI-PCI桥接器后系统可以使用更多的PCI设备。对于高性能服务器这是非常重要的。Linux提供了对PCI-PCI桥接 器的全面支持。
为了让CPU上运行的PCI初始化代码能访问位于分支PCI总线上的设备,必须为桥接器提供某种机制以便它可以决定是否将配置循环从主干接 口传递到其二级接口。循环是出现在PCI总线上的一个地址。PCI 标准定义了两种PCI配置寻址格式;类型0和类型1;它们分别如图6.3及6.4所示。类型0 PCI配置循环不包含总线序号,同时在此PCI总线上对应于这个PCI配置地址的所有PCI设备都会来对它们进行解释。类型0 配置循环的11 位到31位用来进行PCI设备选择。有种设计方式是让每位代表系统中一个不同的设备。这时11位对应PCI槽0中的PCI设备而12位标识槽1中的设备等 等,如此类推。另外一种方式是直接将设备的槽号写入到位31到11中。系统使用哪种机制依赖于系统PCI内存控制器。
类型1 PCI配置循环包含一个PCI总线序号,同时这种配置循环将被除桥接器外的所有PCI设备所忽略。所有发现类型1 配置循环的PCI-PCI桥接器把它们看到的地址传递到各自的下级PCI总线。至于PCI-PCI桥接器是否忽略类型1 配置循环或将其传递到PCI总线则依赖于PCI-PCI桥接器的配置方式。每个PCI-PCI桥接器都拥有一个主干总线接口序号以及一个二级总线接口序 号。主干总线是那个离CPU最近的PCI总线而二级总线是离它稍远的PCI总线。任何PCI-PCI桥接器还包含一个从属总线序号,这是所有二级总线接口 所桥接的PCI总线中序号最大的那个。或者说这个从属总线序号是PCI-PCI桥接器向下连接中PCI总线的最大序号。当PCI-PCI桥接器看到类型1 PCI配置循环时它将进行如下操作:
所以如果想寻址PCI-PCI配置例4中总线3上的设备1,我们继续从CPU中产生一个类型1 配置命令。桥接器1将其传递给总线1。桥接器2虽然忽略它但会将其转换成一个类型0 配置命令并送到总线3上,在那里设备1将作出相应反应。
PCI配置中总线序号由操作系统来分配。但是序号分配策略必须遵循对系统中所有PCI-PCI桥接器都正确的描叙:
“位于PCI-PCI桥接器后所有的PCI总线必须位于二级总线序号和从属总线序号之间”。
如果这个规则被打破,则PCI-PCI桥接器将不能正确的传递与转换类型1 PCI配置循环,同时系统将找不到或者不能正确地初始化系统中的PCI设备。为了满足这个序号分配策略,Linux以特殊的顺序配置这些特殊的设备。一节详细描叙了Linux的PCI桥接器与总线序号分配策略。
Linux中的PCI初始化代码逻辑上可分成三个部分:
Linux核心初始化PCI系统时同时也建立了反应系统中真实PCI拓扑的数据结构。 图6.5显示了图6.1所标识的PCI示例系统中数据结构间关系。每个PCI设备(包括PCI-PCI桥接器)用一个pci_dev数据结构来描叙。每个 PCI总线用一个pci_bus数据结构来描叙。这样系统中形成了一个PCI总线树,每棵树上由一些子PCI设备组成。由于PCI总线仅能通过PCI- PCI桥接器(除了主干PCI总线0)存取,所以pci_bus结构中包含一个指向PCI-PCI桥接器的指针。这个PCI设备是PCI总线的父PCI总 线的子设备。
在图6.5中没有显示出来的是一个指向系统中所有PCI设备的指针,pci_devices。系统中所有的PCI设备将其各自的pci_dev数据结构加入此队列中。这个队列被Linux核心用来迅速查找系统中所有的PCI设备。
PCI设备驱动根本不是真正的设备驱动,它仅是在系统初始化时由操作系统调用的一些函数。PCI初始化代码将扫描系统中所有的PCI总线以找到系统中所有的PCI设备(包括PCI-PCI桥接器)。
它通过PCI BIOS代码来检查当前PCI总线的每个插槽是否已被占用。如果被占用则它建立一个pci_dev数据结构来描叙此设备并将其连接到已知PCI设备链表中(由pci_devices指向)。
首先PCI初始化代码扫描PCI总线0。它将试图读取对每个PCI槽中可能的PCI设备厂商标志与设备标志域。当发现槽被占用后将建立一个pci_dev 结构来描叙此设备。所有这些PCI初始化代码建立的pci_dev结构(包括PCI-PCI桥接器)将被连接到一个单向链表pci_devices中。
如果这个PCI设备是一个PCI-PCI桥接器则建立一个pci_bus结构并将其连接到由pci_root指向的pci_dev结构和pci_bus树 中。PCI初始化代码通过类别代码0x060400来判断此PCI设备是否是一个PCI-PCI桥接器。然后Linux 核心代码将配置此PCI-PCI桥接器下方的PCI设备。如果有更多的桥接器被找到则进行同样的配置。显然这个过程使用了深度优先搜索算法;系统中PCI 拓扑将在进行广度映射前先进行深度优先映射。图6.1中Linux将在配置PCI总线0上的视频设备前先配置PCI设备1上的以太与SCSI设备。
由于Linux优先搜索从属的PCI总线,它必须处理PCI-PCI桥接器二级总线与从属总线序号。在下面的pci-pci总线序号分配中将进行详细讨论。
为了让PCI-PCI桥接器可以传递PCI I/O、PCI内存或PCI配置地址空间,它们需要如下内容:
配置任一PCI-PCI桥接器时我们对此桥接器的从属总线序号一无所知。不知道是否还有下一级桥接器存在,同时也不知道指派给它们的序号是什么。但可以使 用深度优先遍历算法来对扫描出指定PCI-PCI桥接器连接的每条总线,同时将它们编号。当找到一个PCI-PCI桥接器时,其二级总线被编号并且将临时 从属序号0xff指派给它以便对其所有下属PCI-PCI桥接器进行扫描与指定序号。以上过程看起来十分复杂,下面将提供一个实例以帮助理解。
PCI BIOS函数是一组适用于所有平台的标准过程。在Intel和Alpha AXP系统上没有区别。虽然在CPU控制下可以用它们对所有PCI地址空间进行访问。但只有Linux核心代码和设备驱动才能使用它们。
在Alpha AXP平台上的PCI补丁代码所作工作量要大于Intel平台。
基于Intel的系统在系统启动时就已经由系统BIOS完成了PCI系统的配置。Linux只需要完成简单的映射配置. 非Intel系统将需要更多的配置:
下一节将描叙这些代码的工作过程。
有两类基本的基地址寄存器,一类标识设备寄存器必须驻留的地址空间;另一类是PCI I/O或PCI内存空间。此寄存器的0位来进行类型的区分。图6.10给出了对应于PCI内存和PCI I/O两种不同类型的基地址寄存器。
确定某个基地址寄存器所需地址空间大小时,先向此寄存器写入全1再读取此寄存器,设备将在某些位填上0来形成一个二进制数表示所需有效地址空间。
以初始化DEC 21142 PCI快速以太设备为例,它将告诉系统需要0x100字节的PCI I/O空间或者PCI内存空间。于是初始化代码为其分配空间。空间分配完毕后,就可以在那些地址上看到21142的控制与状态寄存器。
Linux使用的算法依赖于由PCI设备驱动程序建立的描叙PCI设备的总线/设备树,每个设备的地址空间按照PCI I/O内存顺序的升序来分配。同时再次使用遍历算法来遍历由PCI初始化代码建立的pci_bus和 pci_dev结构。从根PCI总线开始(由pci_boot指向)PCI补丁代码将完成下列工作:
以图6.1中的PCI系统为例,PCI补丁代码将以如下方式设置系统: