Linux设备驱动之pci设备的枚举(3)

2019-04-14 11:59

/*

* Do the ugly legacy mode stuff here rather than broken chip * quirk code. Legacy mode ATA controllers have fixed * addresses. These are not always echoed in BAR0-3, and * BAR0-3 in a few cases contain junk! */

if (class == PCI_CLASS_STORAGE_IDE) { u8 progif;

pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if ((progif & 1) == 0) {

dev->resource[0].start = 0x1F0; dev->resource[0].end = 0x1F7;

dev->resource[0].flags = LEGACY_IO_RESOURCE; dev->resource[1].start = 0x3F6; dev->resource[1].end = 0x3F6;

dev->resource[1].flags = LEGACY_IO_RESOURCE; }

if ((progif & 4) == 0) {

dev->resource[2].start = 0x170; dev->resource[2].end = 0x177;

dev->resource[2].flags = LEGACY_IO_RESOURCE; dev->resource[3].start = 0x376; dev->resource[3].end = 0x376;

dev->resource[3].flags = LEGACY_IO_RESOURCE; } } break;

case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ if (class != PCI_CLASS_BRIDGE_PCI) goto bad;

/* The PCI-to-PCI bridge spec requires that subtractive decoding (i.e. transparent) bridge must have programming interface code of 0x01. */ pci_read_irq(dev);

dev->transparent = ((dev->class & 0xff) == 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); break;

case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ if (class != PCI_CLASS_BRIDGE_CARDBUS) goto bad; pci_read_irq(dev); pci_read_bases(dev, 1, 0);

11

pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device); break;

default: /* unknown header */

printk(KERN_ERR \ pci_name(dev), dev->hdr_type); return -1;

bad:

printk(KERN_ERR \ pci_name(dev), class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; }

/* We found a fine healthy device, go go go... */ return 0; }

总共有三种类型的设备,分别为常规设备(PCI_HEADER_TYPE_NORMAL) ,pci-pci桥设备(PCI_HEADER_TYPE_BRIDGE),笔记本电脑上使用的cardbus(PCI_HEADER_TYPE_CARDBUS).这里的操作不外乎是IRQ的确定,设备存储区间映射等.先将这几个操作分析如下: 1: IRQ号的确定

该操作接口为pci_read_irq():

static void pci_read_irq(struct pci_dev *dev) {

unsigned char irq;

pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); dev->pin = irq; if (irq)

pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); dev->irq = irq; }

在PCI_INTERRUPT_PIN中存放的是将INTA~INTD的哪一个引脚连接到了中断控制器,如果该值为零.说明并末将引脚连接至中断控制器.自然也就不能产生中断信号.

其实,在PCI_INTERRUPT_LINE存放的是该设备的中断线连接在中断控制器的哪一个IRQ线上.也就是对应设备的IRQ.

注意这里的寄存器只读有意义,并不是更改寄存器的值就更改该设备的IRQ

2:内部存储区间的确定

从之前的pci设备配置寄存器图中可以看到.有从0x10~0x27的6个base address寄存器.里面存放的就是内部存储器的地起地址和长度,及其类型.

首先将对应寄存器的值取出.如果最低位为1.则说明该区域是I/O端口,高29位是端口地址的高29位,低3位为零.否则是存储映射区间.前28位是存储区的高28位,低四位为零.

12

然后,将该寄存器全部置1.再读,取得的是长度信息. 如果是I/O端口,屏弊其低三位,如果是存储区间,屏弊其低四位.最后取第1个位为1对应的大小,即为相应区间的长度. 例如,取出来的值是0xC107.假设是I/O端口

屏蔽掉低三位,为0xC100.第一个为1的值对应的值为0x0100.即0x100 另上,ROM的操作也跟此类似.

在上面的代码中,内部存储区间的确定是由pci_read_bases()完成的.这个函数代码比较长.涉及到的东西又不多,因此不做详细分析.结构上面的分析,应该很容易看懂代码了.

从上面的代码可以看出,对于常规设备,有6个存储区间和一个ROM。Pci briage只有2个存储区间和一个ROM。Cardbus只有一个存储区间没有ROM。

好了,再这里,每一类设备的信息都已经完全读取出来了,并存放在pci_dev的相关字段。此后在驱动中就可以直接找到pci_dev.取得相应的信息,而不需要再次去枚举了.

再这里,万里长征只是迈出了一小步。我们知道,pci总线可以通过pci bridge再连一层pci总线。这个问题显然是一个递归过程。我们接下来看pci桥的处理。

返回到pci_scan_child_bus()中。我们将下面要分析的代码列出来: unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) {

…… ……

for (devfn = 0; devfn < 0x100; devfn += 8) pci_scan_slot(bus, devfn); pcibios_fixup_bus(bus); for (pass=0; pass < 2; pass++)

list_for_each_entry(dev, &bus->devices, bus_list) {

if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) max = pci_scan_bridge(bus, dev, max, pass); } …… }

Pcibios_fixup_bus()这个函数看名字是用来修正总线的。芯片厂商在发布产品后,又检测到上次发布的产品有问题。回厂升级是不可能的了。只能提供软件修改的手段,发布一些修正包。Linux将很多厂商的修改正集合在一起。这也就是pcibios_fixup_bus()要进行的操作。具体设备的修正功能,我们就不再研究了。这个函数里还有一个重要的操作。列出代码如下:

void __devinit pcibios_fixup_bus(struct pci_bus *b) {

struct pci_dev *dev;

pcibios_fixup_ghosts(b); pci_read_bridge_bases(b);

list_for_each_entry(dev, &b->devices, bus_list) pcibios_fixup_device_resources(dev); }

13

我们所在讨论的重要的操作就是在pci_read_bridge_bases()中完成的。除了之上分析的配置字段外,其实pci桥还有一个很重要的配置项。即:过滤窗口。

过滤窗口决定了访问的方向。例如:如果cpu一侧要经过pci bridge访问pci总线,则它的地址必须要落在这个pci桥的过滤窗口内才可以通过。另外,pci bridge下游的pci bus要访问cpu侧。则地址必须要落在过滤窗口外才可以。

此外,pci bridge还提供了一个命令寄存器来控制“memory access enable“和“I/O access enable”两个位来控制两个功能。如果全为0.则两个方向都会关闭。在pci初始化前,为了防止对cpu侧造成干扰, 这两个功能都关闭的, Pci bridge有三个这样的窗口,分别如下:

1:起始地址在PCI_IO_BASE中,长度在PCI_IO_LIMIT中。如果是32位,还要通过PCI_IO_BASE_UPPER16和PCI_IO_LIMIT_UPPER16提供高16位。

2:起始地址在PCI_MEMORY_BASE,长度在PCI_MEMORY_LIMIT中。这个是一个16位的窗口。

3:起始地址在PCI_PREF_MEMORY_BASE,长度在PCI_PREF_MEMORY_LIMIT.默认是32位。如果是64,则需要PCI_PREF_BASE_UPPER32和PCI_PREF_LIMIT_UPPER32提供高32位. 存储区间在这里看起来有点繁杂。以图的形式总结如下:

14

结合上面说的,理解pci_read_bridge_bases()的代码就不难了。这里不再做详细分析。

现在终于把应该读的配置读完了,可以进行下层pci总线的遍历了。 列出这段代码:

unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) { …… ……

for (pass=0; pass < 2; pass++)

list_for_each_entry(dev, &bus->devices, bus_list) {

if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) max = pci_scan_bridge(bus, dev, max, pass); } …… …… }

上面的操作基本上就是遍历挂在pci_bus->devices上面的设备(是否还记得上面在分析的时候,每枚举到一个设备都会加入到pci_bus->device呢*^_^*)。如果是pci桥或者是cardbus。就会调用pci_scan_bridge()来遍历桥下面的设备.

这里让人疑惑的是,为什么要遍历二次呢?

这是因为,在x86上,系统启动的时候,bios会枚举一次pci设备。所以有些pci bridge是经过bios处理过的。而有些是bios可能没有枚举到的。这就需要分两次处理。一次来处理那里已经由bios处理过的pci bridge.一次是处理全新的pci bridge.这样做,这样做是因为,每次枚举总线后,要为其分配一个总线号,而bios处理后的pci bridge的总线号全部都由bios分配好了,要为新的pci bridge分配总线号。而必须要处理完旧的pci bridge才会知道可用的总线号是多少。

跟进pci_sacn_bridge()的代码,这段代码较长,分段分析如下:

int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) {

struct pci_bus *child;

int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); u32 buses, i, j = 0; u16 bctl;

pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);

pr_debug(\ pci_name(dev), buses & 0xffffff, pass);

/* Disable MasterAbortMode during probing to avoid reporting of bus errors (in some architectures) */

pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl); pci_write_config_word(dev, PCI_BRIDGE_CONTROL,

15


Linux设备驱动之pci设备的枚举(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:完整的工程结算单书(市政)

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: