Linux下的PCI总线驱动

说明转自  http://my.csdn.net/weiqing1981127

一.理论

1.      PCI总线的特点:

1)速度上快,时钟频率提高到33M,而且还为进一步把时钟频率提高到66MHZ、总线带宽提高到64位留下了余地。(2)对于地址的分配和设置,系统软件课自动设置,每块外设通过某种途径告诉系统该外设有几个存储区间和I/O地址区间,每个区间的大小以及本地地址。系统软件知道了总共有多少外设以及各种的存储空间后就会统一为外设分配物理地址。(3)对于总线的竞争,PCI总线上配备了一个仲裁器,遇到冲突,仲裁器会选择其中之一暂时成为当前的主设备,而其他只能等待。同时考虑到这样的效率问题,PCI总线为写提纲了缓冲,故写比读会快。(4)对于总线扩充问题,PCI总线引入HOST-PCI桥(北桥),PCI-PCI,PCI-ISA桥(南桥)。CPU与内存通过系统总线连接,北桥连接内存控制器与主PCI总线,南桥连接主PCI总线和ISA总线,PCI-PCI桥连接主PCI总和次层PCI总线。

2.      PCI设备概述

每个PCI设备有许多地址配置的寄存器,初始化时要通过这些寄存器来配置该设备的总线地址,一旦完成配置以后,CPU就可以访问该设备的各项资源了。PCI标准规定每个设备的配置寄存器组最多可以有256个连续的字节空间,开头64个字节叫头部,分为0型(PCI设备)和1型(PCI桥)头部,头部开头16个字节是设备的类型、型号和厂商等。这些头部寄存器除了地址配置的作用,还能使CPU能够探测到相应设备的存在,这样就不需要用户告诉系统都有哪些设备了,而是改由CPU通过一个号称枚举的过程自动扫描探测所有挂接在PCI总线上的设备。

设备的配置寄存器组采用相同的地址,由所在总线的PCI桥在访问时附加上其他条件区分,对于I386处理器,有两个32位寄存器,0XCF8为地址寄存器,0XCFC为数据寄存器。地址寄存器写入的内容包括总线号,设备号,功能号。逻辑地址(XX:YY.Z),XX表示PCI总线号,最多256个总线。YY表示PCI设备号,最多32个设备。Z表示PCI设备功能号,最多8个功能。这就是通常说的b_d_f,通常用这些来确定设备,但是有的时候一个function会有多个物理上的设备。比如网卡,可以在一个function虚拟出来多个物理网口

3.      查询PCI总线和设备的命令

     查看PCI总线和PCI设备组成的树状图 lspci –t        

     查看配置区的情况 lspci –x,注意PCI寄存器是小端格式

lspci -tnnvv 显示出deviceId和vendor

lspci -vt 一半情况下两个参数混合起来用。

4.      PCI总线架构

所有的根总线都链接在pci_root_buses链表中。Pci_bus ->device链表链接着该总线下的所有设备。而pci_bus->children链表链接着它的下层总线,对于pci_dev来说,pci_dev->bus指向它所属的pci_bus. Pci_dev->bus_list链接在它所属busdevice链表上。此外,所有pci设备都链接在pci_device链表中。

5.      ********

二.PCI驱动

1.      PCI寻找空间

   PCI设备包括三个寻址空间:配置空间,I/O端口空间,内存空间。

1.1 PCI配置空间:

内核为驱动提供的函数:

pci_read_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value)

pci_write_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value)

配置空间的偏移定义在include/linux/pci_regs.h

1.2 PCII/O和内存空间:

从配置区相应寄存器得到I/O区域的基址:

pci_resource_start(struct pci_dev *dev,  int bar)    Bar值的范围为0-5

从配置区相应寄存器得到I/O区域的内存区域长度:

pci_resource_length(struct pci_dev *dev,  int bar)    Bar值的范围为0-5

从配置区相应寄存器得到I/O区域的内存的相关标志:

pci_resource_flags(struct pci_dev *dev,  int bar)    Bar值的范围为0-5

申请I/O端口:

request_mem_region(io_base, length, name)

读写:

inb()  inw()  inl()   outb()     outw()  outl()

2.      PCI总线支持的设备

   PCI驱动程序向PCI子系统注册其支持的厂家ID,设备ID和设备类编码。使用这个数据库,插入的卡通过配置空间被识别后,PCI子系统把插入的卡和对应的驱动程序绑定。

PCI设备列表

   struct pci_device_id {

       __u32 vendor, device;           /* Vendor and device ID or PCI_ANY_ID*/

       __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */

       __u32 class, class_mask;      /* (class,subclass,prog-if) triplet */

       kernel_ulong_t driver_data;    /* Data private to the driver */

};

       注意:如果可以处理任何情况,可将相应的寄存器设置为PCI_ANY_ID

3.      PCI驱动其他API

获取驱动私有数据:pci_get_drvdata();

使能PCI设备:pci_enable_device()

总线主DMA模式设置:pci_set_master()

4.       *******

 

三.PCI驱动模型

一个通过PCI总线与系统连接的设备的驱动主要包括两部分:第一PCI驱动,第二,设备本身的驱动,包括字符设备,网络设备,tty设备,音频设备等。PCI驱动的核心是pci_driver,在探测函数中完成资源的申请,并注册相应的字符设备,网络设备,tty设备,音频设备等。

 

static struct pci_device_id buttons_pci_tbl[] __initdata={

 {PCI_ANY_ID,PCI_ANY_ID,PCI_ANY_ID,PCI_ANY_ID,0,0,0},

{0,}

}; //PCI设备支持项

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

   //中断处理程序

}

static int s3c24xx_buttons_open(struct inode *inode, struct file *file)

{

  

}

static int s3c24xx_buttons_close(struct inode *inode, struct file *file)

{

    

}

static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

   

}

static struct file_operations dev_fops = {

    .owner   =   THIS_MODULE,

    .open    =   s3c24xx_buttons_open,

    .release =   s3c24xx_buttons_close,

    .read    =   s3c24xx_buttons_read,

};

static struct miscdevice misc = {

       .minor = MISC_DYNAMIC_MINOR,

       .name = DEVICE_NAME,

       .fops = &dev_fops,

};

static int pci_key__probe (struct pci_dev *pdev, const struct pci_device_id *ent)

{

       int ret;

       pci_enable_device(pdev);  //使能PCI设备

       pci_set_master(pdev);  

       ret = misc_register(&misc); //注册杂项设备

       printk (DEVICE_NAME"\tinitialized\n");

       return ret;

}

static int pci_key__remove (struct pci_dev *pdev, const struct pci_device_id *ent)

       pci_disable_device(pdev);

       misc_deregister(&misc);

       return 0;

}

static struct pci_driver pci_key_driver = {

       .name         = "pci_key",

       .id_table     =buttons_pci_tbl,

       .probe        =   pci_key__probe,

       .remove       = pci_key__remove,

};

static int __init dev_init(void)

{

              return pci_register_driver(&pci_key_driver);

}

static void __exit dev_exit(void)

{

       pci_unregister_driver(&pci_key_driver);

}

module_init(dev_init);

module_exit(dev_exit);

 

四.PCI设备的枚举过程

基于Mini2440开发板,在此,我只分析linux2.6.32.2内核的PCI设备枚举过程

pci的代码分为两个部份.一个部份是与平台相关的部份.存放在linux2.6.32.2\arch\x86\pci\ ,另一个部份是平台无关的代码,存放在linux2.6.32.2\driver\pci\下面.

我们从与平台相关的部份.存放在linux2.6.32.2\arch\x86\pci\ Makefile看起,

obj-y                            := i386.o init.o

 

obj-$(CONFIG_PCI_BIOS)          += pcbios.o

obj-$(CONFIG_PCI_MMCONFIG)     += mmconfig_$(BITS).o direct.o mmconfig-shared.o

obj-$(CONFIG_PCI_DIRECT)     += direct.o

obj-$(CONFIG_PCI_OLPC)         += olpc.o

 

obj-y                            += fixup.o

obj-$(CONFIG_ACPI)          += acpi.o

obj-y                            += legacy.o irq.o

 

obj-$(CONFIG_X86_VISWS)             += visws.o

 

obj-$(CONFIG_X86_NUMAQ)           += numaq_32.o

 

obj-y                            += common.o early.o

obj-y                            += amd_bus.o

由这个Malefile我们可以知道init.c是一定被编译的,那么我们就看看这个init.c,在这个文件里面只有pci_arch_init函数,那么我们就看看。

static __init int pci_arch_init(void)

{

#ifdef CONFIG_PCI_DIRECT  //直接进行PCI设备的探测和枚举

       int type = 0;

       type = pci_direct_probe();

#endif

       if (!(pci_probe & PCI_PROBE_NOEARLY))

              pci_mmcfg_early_init();

#ifdef CONFIG_PCI_OLPC

       if (!pci_olpc_init())

              return 0;  /* skip additional checks if it's an XO */

#endif

#ifdef CONFIG_PCI_BIOS //通过BIOS进行PCI设备的探测和枚举

       pci_pcbios_init();

#endif

#ifdef CONFIG_PCI_DIRECT  //直接进行PCI设备的探测和枚举

       pci_direct_init(type);

#endif

       if (!raw_pci_ops && !raw_pci_ext_ops)

              printk(KERN_ERR

              "PCI: Fatal: No config space access function found\n");

       dmi_check_pciprobe();

       dmi_check_skip_isa_align();

       return 0;

}

arch_initcall(pci_arch_init);

首先要说明下,这个函数由arch_initcall(pci_arch_init)调用,而arch_initcall引用的函数都会放在init区域,这里面的函数是kernel启动的时候会自己执行的函数。

 

好了,我们现在看看这个pci_arch_init函数吧,咋一眼里面内容好多,CONFIG_PCI_BIOS表示通过BIOS进行PCI设备的探测和枚举; CONFIG_PCI_DIRECT表示直接进行PCI设备的探测和枚举,为了突出重点,我们分析CONFIG_PCI_DIRECT的过程.把其它不相关的代码略掉.剩余的就简单了。

 

首先看pci_direct_probe()函数,在pci规范中定义了两种操作配置空间的方法,即“1型”和“2型”,通常会使用“1型”。因此,在代码中pci_direct_probe()一般会返回1,即使用“1型”。

 

那剩下来只有pci_direct_init函数需要分析的了,继续看

void __init pci_direct_init(int type)

{

       if (type == 0)

              return;

       printk(KERN_INFO "PCI: Using configuration type %d for base access\n",

               type);

       if (type == 1) {

              raw_pci_ops = &pci_direct_conf1;

              if (raw_pci_ext_ops)

                     return;

              if (!(pci_probe & PCI_HAS_IO_ECS))

                     return;

              printk(KERN_INFO "PCI: Using configuration type 1 "

                     "for extended access\n");

              raw_pci_ext_ops = &pci_direct_conf1;

              return;

       }

       raw_pci_ops = &pci_direct_conf2;

}

在这个函数中,我们只关注“1型”情况,我们发现只是把raw_pci_ops = &pci_direct_conf1,那么我们就要看看pci_direct_conf1是什么了,继续跟踪

struct pci_raw_ops pci_direct_conf1 = {

       .read =           pci_conf1_read,

       .write =   pci_conf1_write,

};

这个结构其实就是pci设备配置空间操作的接口。好了,分析到这里,我们知道给raw_pci_ops赋了新值,但仅此而已,这个新值怎么用,我们不知道,路戛然而止。那么我们就继续看这个linux2.6.32.2\arch\x86\pci\ Makefile内容,我们发现除了init.c,还有其他几个文件是默认必须被编译的,包括i386.o init.ofixup.olegacy.o irq.o

我们打开这几个文件发现只有legacy.c里面有个系统调用subsys_initcall(pci_subsys_init),我们知道这个系统调用也会在内核启动时被自动调用,同时因为subsys_initcall比上面我们讲的arch_initcal优先级低,所以,我们应该会在这个文件legacy.c里找到上面我们提出的疑问,即raw_pci_ops怎么用?

好了,我们在legacy.c中搜索raw_pci_ops便发现了pci_legacy_init函数,而这个函数由于被pci_subsys_init调用而会在内核启动时候被调用。那么我们下面的任务就是看看了pci_legacy_init函数了。

static int __init pci_legacy_init(void)

{

       if (!raw_pci_ops) {

              printk("PCI: System does not support PCI\n");

              return 0;

       }

       if (pcibios_scanned++)

              return 0;

       printk("PCI: Probing PCI hardware\n");

       pci_root_bus = pcibios_scan_root(0);

       if (pci_root_bus)

              pci_bus_add_devices(pci_root_bus);

       return 0;

}

很明显根据printk的提示,我们知道pcibios_scan_root(0)是探测PCI设备的,那么0的含义自然就是PCI根总线了。

好,下面我们就好好研究研究这个PCI自动探测过程了。

struct pci_bus * __devinit pcibios_scan_root(int busnum)

{

       struct pci_bus *bus = NULL;

       struct pci_sysdata *sd;

       while ((bus = pci_find_next_bus(bus)) != NULL) {

              if (bus->number == busnum) {

                     /* Already scanned */

                     return bus;

              }

       }

       /* Allocate per-root-bus (not per bus) arch-specific data.

        * TODO: leak; this memory is never freed.

        * It's arguable whether it's worth the trouble to care.

        */

       sd = kzalloc(sizeof(*sd), GFP_KERNEL);

       if (!sd) {

              printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum);

              return NULL;

       }

       sd->node = get_mp_bus_to_node(busnum);

       printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum);

       bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);

       if (!bus)

              kfree(sd);

       return bus;

}

我们可以发现pcibios_scan_root函数刚开始寻找这个总线号是否被遍历过,如果是,直接退出,接下来分配一个sd,然后调用pci_scan_bus_parented完成真正的PCI设备探测,继续看。

struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,

              int bus, struct pci_ops *ops, void *sysdata)

{

       struct pci_bus *b;

       b = pci_create_bus(parent, bus, ops, sysdata);

       if (b)

              b->subordinate = pci_scan_child_bus(b);

       return b;

}

pci_scan_bus_parented里,在这个函数里我们完成两件事,第一件事是根据总线号建立pci_bus结构体,然后将这个结构体加入总线链表pci_root_buses中,这由pci_create_bus完成的。第二件事是遍历这个总线下的所有PCI设备,返回PCI总线的下属PCI总线的总线编号的最大值,这由pci_scan_child_bus完成的。

我们先看第一件事

struct pci_bus * pci_create_bus(struct device *parent,

              int bus, struct pci_ops *ops, void *sysdata)

{

       int error;

       struct pci_bus *b;

       struct device *dev;

       b = pci_alloc_bus();  //分配pci_bus结构体

       if (!b)

              return NULL;

       dev = kzalloc(sizeof(*dev), GFP_KERNEL); 

       if (!dev){

              kfree(b);

              return NULL;

       }

       b->sysdata = sysdata;

       b->ops = ops;

       if (pci_find_bus(pci_domain_nr(b), bus)) {

              /* If we already got to this bus through a different bridge, ignore it */

              pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus);

              goto err_out;

       }

       down_write(&pci_bus_sem);

       list_add_tail(&b->node, &pci_root_buses); //pci_bus加入总线pci_root_buses链表

       up_write(&pci_bus_sem);

       dev->parent = parent;

       dev->release = pci_release_bus_bridge_dev;

       dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus);

       error = device_register(dev);

       if (error)

              goto dev_reg_err;

       b->bridge = get_device(dev);

       if (!parent)

              set_dev_node(b->bridge, pcibus_to_node(b));

       b->dev.class = &pcibus_class;

       b->dev.parent = b->bridge;

       dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);

       error = device_register(&b->dev);

       if (error)

              goto class_dev_reg_err;

       error = device_create_file(&b->dev, &dev_attr_cpuaffinity);

       if (error)

              goto dev_create_file_err;

       /* Create legacy_io and legacy_mem files for this bus */

       pci_create_legacy_files(b);

       b->number = b->secondary = bus;

       b->resource[0] = &ioport_resource;

       b->resource[1] = &iomem_resource;

       return b;

dev_create_file_err:

       device_unregister(&b->dev);

class_dev_reg_err:

       device_unregister(dev);

dev_reg_err:

       down_write(&pci_bus_sem);

       list_del(&b->node);

       up_write(&pci_bus_sem);

err_out:

       kfree(dev);

       kfree(b);

       return NULL;

}

 

剩下来我们把主要精力放在分析第二件事情上,即看看如果遍历总线下面的PCI设备的呢?也就是看看pci_scan_child_bus干了些啥。

unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)

{

       unsigned int devfn, pass, max = bus->secondary;

       struct pci_dev *dev;

       pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);

       /* Go find them, Rover! */

       for (devfn = 0; devfn < 0x100; devfn += 8)

              pci_scan_slot(bus, devfn);

       /* Reserve buses for SR-IOV capability. */

       max += pci_iov_bus_range(bus);

       /*

        * After performing arch-dependent fixup of the bus, look behind

        * all PCI-to-PCI bridges on this bus.

        */

       if (!bus->is_added) {

              pr_debug("PCI: Fixups for bus %04x:%02x\n",

                      pci_domain_nr(bus), bus->number);

              pcibios_fixup_bus(bus);

              if (pci_is_root_bus(bus))

                     bus->is_added = 1;

       }

       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);

              }

       /*

        * We've scanned the bus and so we know all about what's on

        * the other side of any bridges that may be on this bus plus

        * any devices.

        *

        * Return how far we've got finding sub-buses.

        */

       pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",

              pci_domain_nr(bus), bus->number, max);

       return max;

}

我们知道扫描一条PCI总线的直接目的就是逐个发现连接在该总线上的PCI设备

为其建立pci_dev结构体并挂接在相应的队列中,这就是枚举。在pci_scan_child_bus

里,我们通过pci_scan_slot扫描,每次扫描它下面的8个功能号对应的设备,这是每块PCI设备的最大容量。这里的devfn5位设备号和3为功能号结合在一起组成的逻辑设备号。

好了,我们该看看pci_scan_slot的扫描过程了。

int pci_scan_slot(struct pci_bus *bus, int devfn)

{

    int fn, nr = 0;

    struct pci_dev *dev;

    dev = pci_scan_single_device(bus, devfn);

    if (dev && !dev->is_added)  /* new device? */

           nr++;

    if (dev && dev->multifunction) {        //多功能情况

           for (fn = 1; fn < 8; fn++) {

                  dev = pci_scan_single_device(bus, devfn + fn);

                  if (dev) {

                         if (!dev->is_added)

                                nr++;

                         dev->multifunction = 1;

                  }

           }

    }

    /* only one slot has pcie device */

    if (bus->self && nr)

           pcie_aspm_init_link_state(bus->self);

    return nr;

}

设备的功能号最大值是8,但是有的设备是单功能的,有的却是多功能的,这在代码中由dev->multifunction是否为 0判断。如果是单功能设备,则只要判断它的第一个功能号可以了,只会调用一次pci_scan_single_device(),而不需要执行那个for循环。如果是多功能设备,需要每个功能号调用一次pci_scan_single_device()

好了,那我们看看pci_scan_single_device()吧。

struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)

{

    struct pci_dev *dev;

    dev = pci_get_slot(bus, devfn);

    if (dev) {

           pci_dev_put(dev);

           return dev;

    }

    dev = pci_scan_device(bus, devfn);

    if (!dev)

           return NULL;

    pci_device_add(dev, bus);     //pci_dev加至pci_bus->devices

    return dev;

}

pci_scan_single_device函数里,我们发现,其实对每个设备都会调用pci_scan_device()执行扫描的过程,如果该设备存在,就会将该设备加入到所属总线的devices链表上。这是在pci_device_add()函数中完成的。

 

下面我们集中注意力跟踪这个: pci_scan_device()函数

static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)

{

    struct pci_dev *dev;

    u32 l;

    int delay = 1;

//读取编号判断是否有效

    if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))

           return NULL;

    /* some broken boards return 0 or ~0 if a slot is empty: */

    if (l == 0xffffffff || l == 0x00000000 ||

        l == 0x0000ffff || l == 0xffff0000)

           return NULL;  

    /* Configuration request Retry Status */

    while (l == 0xffff0001) {

           msleep(delay);

           delay *= 2;

           if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))

                  return NULL;

           /* Card hasn't responded in 60 seconds?  Must be stuck. */

           if (delay > 60 * 1000) {

                  printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not "

                                "responding\n", pci_domain_nr(bus),

                                bus->number, PCI_SLOT(devfn),

                                PCI_FUNC(devfn));

                  return NULL;

           }

    }

//读取的编号有效则分配pci_dev结构体,并赋值

    dev = alloc_pci_dev();

    if (!dev)

           return NULL;

    dev->bus = bus;

    dev->devfn = devfn;

    dev->vendor = l & 0xffff;

    dev->device = (l >> 16) & 0xffff;

//进一步从PCI口读入有关这个设备的信息

    if (pci_setup_device(dev)) {

           kfree(dev);

           return NULL;

    }

    return dev;

}

pci_scan_device函数中,我们首先读入一个字长度,其低16位为厂商编号,高16位为设备号,只要这些编号不是全1或者全0,就可以认为是有效编号,从而设备存在。然后分配新的pci_dev结构体,并对其赋值,接着调用pci_setup_device进一步从PCI口读入有关这个设备的信息。

好吧,打起精神,继续看pci_setup_device

int pci_setup_device(struct pci_dev *dev)

{

    u32 class;

    u8 hdr_type;

    struct pci_slot *slot;

    if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))

           return -EIO;

    dev->sysdata = dev->bus->sysdata;

    dev->dev.parent = dev->bus->bridge;

    dev->dev.bus = &pci_bus_type;

    dev->hdr_type = hdr_type & 0x7f;

    dev->multifunction = !!(hdr_type & 0x80);

    dev->error_state = pci_channel_io_normal;

    set_pcie_port_type(dev);

    list_for_each_entry(slot, &dev->bus->slots, list)

           if (PCI_SLOT(dev->devfn) == slot->number)

                  dev->slot = slot;

    dev->dma_mask = 0xffffffff;

    dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),

                dev->bus->number, PCI_SLOT(dev->devfn),

                PCI_FUNC(dev->devfn));

    pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);

    dev->revision = class & 0xff;

    class >>= 8;                            /* upper 3 bytes */

    dev->class = class;

    class >>= 8;

    dev_dbg(&dev->dev, "found [%04x:%04x] class %06x header type %02x\n",

            dev->vendor, dev->device, class, dev->hdr_type);

    dev->cfg_size = pci_cfg_space_size(dev);

    dev->current_state = PCI_UNKNOWN;

    pci_fixup_device(pci_fixup_early, dev);

    class = dev->class >> 8;

    switch (dev->hdr_type) {                 /* header type */

    case PCI_HEADER_TYPE_NORMAL:            /* standard header */

           if (class == PCI_CLASS_BRIDGE_PCI)

                  goto bad;

           pci_read_irq(dev);

           pci_read_bases(dev, 6, PCI_ROM_ADDRESS);

           pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);

           pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);

           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);

           set_pcie_hotplug_bridge(dev);

           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);

           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 */

           dev_err(&dev->dev, "unknown header type %02x, "

                  "ignoring device\n", dev->hdr_type);

           return -EIO;

    bad:

           dev_err(&dev->dev, "ignoring class %02x (doesn't match header "

                  "type %02x)\n", class, dev->hdr_type);

           dev->class = PCI_CLASS_NOT_DEFINED;

    }

    /* We found a fine healthy device, go go go... */

    return 0;

}

好吧,感觉有点长,我先说说pci_setup_device这个函数干了些啥,然后再告诉大家为什么吧。嘿嘿,这个pci_setup_device主要是继续初始化pci_dev结构体的版本号,存储空间,中断线等问题。好了,那我们具体看看这个函数吧。

一进入这个函数就先读入具体设备的头部类型,设备类型和版本号字段,然后重点是对头部类型进行判断,PCI_HEADER_TYPE_NORMAL表示一般的PCI设备,PCI_HEADER_TYPE_BRIDGE表示PCI-PCI桥,PCI_HEADER_TYPE_CARDBUS:表示PCI-CardBus桥。

我们先看一般的PCI设备。好了,我们看看这个case里怎么处理的吧。

首先调用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设备通常都是可以发出中断请求的,所以设备配置寄存器组中有两个字节,即PCI_INTERRUPT_PINPCI_INTERRUPT_LINE,在PCI_INTERRUPT_PIN中存放的是将INTA~INTD的哪一个引脚连接到了中断控制器,如果该值为零说明并末将引脚连接至中断控制器,自然也就不能产生中断信号。其实在PCI_INTERRUPT_LINE存放的是该设备的中断线连接在中断控制器的哪一个IRQ线上,也就是对应设备的IRQ。在pci_read_irq中便是获得这两个参数。

接着调用了pci_read_bases函数,继续看

static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)

{

  unsigned int pos, reg;

  for (pos = 0; pos < howmany; pos++) {

         struct resource *res = &dev->resource[pos];

         reg = PCI_BASE_ADDRESS_0 + (pos << 2);

         pos += __pci_read_base(dev, pci_bar_unknown, res, reg);

  }

  if (rom) {

         struct resource *res = &dev->resource[PCI_ROM_RESOURCE];

         dev->rom_base_reg = rom;

         res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |

                       IORESOURCE_READONLY | IORESOURCE_CACHEABLE |

                       IORESOURCE_SIZEALIGN;

         __pci_read_base(dev, pci_bar_mem32, res, rom);

  }

}

 在一般的PCI设备中,会有六个RAM区域,通常的控制或状态寄存器和数据寄存器会以RAM区间的形式出现,这些地址以后都要先映射到系统总线上,然后映射到内核的虚拟空间上。而这个pci_read_bases函数的作用便是把这些区间的大小和当前的地址读进来。

 在设备的配置寄存器组中,用于6个常规地址区间都是连续的,所以可以用一个for循环来读出来,除了常规的6个存储区间外,设备上还可能提供一个扩充ROM区间,由于这个区间和前面的这6个区间不是连在一起的,所以要放在for循环外面单独处理。

 好了,我们看看pci_read_bases中调用的一个重要函数__pci_read_base

 int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,

                struct resource *res, unsigned int pos)

{

  u32 l, sz, mask;

  mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;

  res->name = pci_name(dev);

  pci_read_config_dword(dev, pos, &l);

  pci_write_config_dword(dev, pos, mask);

  pci_read_config_dword(dev, pos, &sz);

  pci_write_config_dword(dev, pos, l);

  /*

   * All bits set in sz means the device isn't working properly.

   * If the BAR isn't implemented, all bits must be 0.  If it's a

   * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit

   * 1 must be clear.

   */

  if (!sz || sz == 0xffffffff)

         goto fail;

  /*

   * I don't know how l can have all bits set.  Copied from old code.

   * Maybe it fixes a bug on some ancient platform.

   */

  if (l == 0xffffffff)

         l = 0;

  if (type == pci_bar_unknown) {

         type = decode_bar(res, l);

         res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;

         if (type == pci_bar_io) {

                l &= PCI_BASE_ADDRESS_IO_MASK;

                mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT;

         } else {

                l &= PCI_BASE_ADDRESS_MEM_MASK;

                mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;

         }

  } else {

         res->flags |= (l & IORESOURCE_ROM_ENABLE);

         l &= PCI_ROM_ADDRESS_MASK;

         mask = (u32)PCI_ROM_ADDRESS_MASK;

  }

  if (type == pci_bar_mem64) {

         u64 l64 = l;

         u64 sz64 = sz;

         u64 mask64 = mask | (u64)~0 << 32;

         pci_read_config_dword(dev, pos + 4, &l);  //读取起始地址

         pci_write_config_dword(dev, pos + 4, ~0); //写入全1

         pci_read_config_dword(dev, pos + 4, &sz); //读取区间大小

         pci_write_config_dword(dev, pos + 4, l);  /写回起始地址/

         l64 |= ((u64)l << 32);

         sz64 |= ((u64)sz << 32);

         sz64 = pci_size(l64, sz64, mask64);  //换算

         if (!sz64)

                goto fail;

         if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {

                dev_err(&dev->dev, "can't handle 64-bit BAR\n");

                goto fail;

         } else if ((sizeof(resource_size_t) < 8) && l) {

                /* Address above 32-bit boundary; disable the BAR */

                pci_write_config_dword(dev, pos, 0);

                pci_write_config_dword(dev, pos + 4, 0);

                res->start = 0;

                res->end = sz64;

         } else {

                res->start = l64;

                res->end = l64 + sz64;

                dev_printk(KERN_DEBUG, &dev->dev,

                       "reg %x %s: %pR\n", pos,

                        (res->flags & IORESOURCE_PREFETCH) ?

                              "64bit mmio pref" : "64bit mmio",

                        res);

         }

         res->flags |= IORESOURCE_MEM_64;

  } else {

         sz = pci_size(l, sz, mask);

         if (!sz)

                goto fail;

         res->start = l;

         res->end = l + sz;

         dev_printk(KERN_DEBUG, &dev->dev, "reg %x %s: %pR\n", pos,

                (res->flags & IORESOURCE_IO) ? "io port" :

                 ((res->flags & IORESOURCE_PREFETCH) ?

                        "32bit mmio pref" : "32bit mmio"),

                res);

  }

 out:

  return (type == pci_bar_mem64) ? 1 : 0;

 fail:

  res->flags = 0;

  goto out;

}

 

从之前的PCI设备配置寄存器中可以看到,有从0x10~0x276base address

存器,里面存放的就是内部存储器的地起地址、长度及其类型。从配置寄存器组的一个长字里首先读到的是区间的起始地址,如果这个起始地址的低3位为0,那么表示这个地址代表的是一个I/O区间,高29位便是这个I/O区间的起始地址。如果这个起始地址的低4位为0,那么表示这个地址代表的是一个存储器区间,高28位便是这个存储器区间的起始地址。

接着再往这个长字中写入全1,然后再从这个长字中读,这时候读到的数值便是区间的大小。这个数值的低4位或低3位为控制信息,这点和起始地址的格式类似,但是在其高28位或29位中只有位置最低的那位1才有效。区间大小必定是2的某次幂,所以其二进制数值应该只有一位是1,其他位均为0。但是读到的数据往往多位为1,而只有最低位的1才有效,所以需要pci_size加以换算。

 

好了,我们走远了,让我们继续回到前面的pci_setup_device吧,在接下的代码中我们看到还把设备的子系统号和子系统厂商号也读进来了。至于后面对PCI-PCI桥的处理,还得进一步扫描次层PCI总线,以后再说了。

至此pci_scan_device的调用已经完成,回到了pci_scan_single_device中,这个也借结束了,继而回到pci_scan_slot,返回一个pci_dev结构体指针,如果该指针为0则表示PCI总线上没有所指定的逻辑设备。

继续往前返回,我们返回到pci_scan_child_bus函数中。

 

本来,从一个逻辑设备读出头部信息并为之建立相应的数据结构后,对这个设备的枚举就完成了,这些信息是固化在芯片中的。但是有些厂家发现固化在芯片内的信息有误,需要软件手段加以修改,为此Linux设计了一种机制来进行这样的修改。这便是pci_scan_child_bus函数中调用pcibios_fixup_bus(bus)所干的事情。

void __devinit pcibios_fixup_bus(struct pci_bus *b)

{

       struct pci_dev *dev;

       if (!b->parent)

              x86_pci_root_bus_res_quirks(b);

       pci_read_bridge_bases(b);

       list_for_each_entry(dev, &b->devices, bus_list)

              pcibios_fixup_device_resources(dev);

}

我们前面说的设备的修正功能主要由pci_read_bridge_bases(b);完成的,具体代码就不分析了,先把理论说一下吧。

对于PCI桥,其本身并不一定有存储器或寄存器空间,但是却有三个用于地址过滤的区间。每一个地址过滤区间决定了一个地址窗口,如果CPU一侧要经过PCI桥访问PCI总线,则它的地址必须要落在这个PCI桥的过滤窗口内才可以通过。另外,PCI桥下游的PCI总线要访问CPU侧。则地址必须要落在过滤窗口外才可以。PCI桥有三个这样的窗口:I/O地址的窗口,存储器地址的窗口,“可预取”存储器地址的窗口。

此外,PCI桥还提供了一个命令寄存器来控制“memory access enable““I/O access enable”两个位来控制两个功能。如果全为0.则两个方向都会关闭。在PCI初始化前,为了防止对CPU侧造成干扰,这两个功能都关闭的。

 

好了,我们继续返回到pci_scan_child_bus函数中吧。

接着一个for循环就是遍历挂在pci_bus->devices上面的设备。如果是pci桥或者是cardbus,就会调用pci_scan_bridge()来遍历桥下面的设备。为什么要分两次扫描呢?因为两次扫描针对的情况不同,第一次扫描主要针对已经由BIOS进行过处理的PCI桥,第二次扫描主要针对未经BIOS处理的PCI桥。这是什么意思呢?我们知道每次枚举总线后,都要为其分配一个总线号,而BIOS处理后的PCI桥的总线号全部都由BIOS分配好了,但是如果要为新的PCI桥分配总线号,就必须要处理完旧的PCI桥才会知道可用的总线号是多少啦。

 

好了,我们看看pci_scan_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;

       int broken = 0;

//读取主总线号

       pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);

       dev_dbg(&dev->dev, "scanning behind bridge, config %06x, pass %d\n",

              buses & 0xffffff, pass);

       if (!pass &&

           ((buses & 0xff) != bus->number || ((buses >> 8) & 0xff) <= bus->number)) {

              dev_dbg(&dev->dev, "bus configuration invalid, reconfiguring\n");

              broken = 1;

       }

       pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);

       pci_write_config_word(dev, PCI_BRIDGE_CONTROL,

                           bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);

       if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus && !broken) {

              unsigned int cmax, busnr;

              if (pass)

                     goto out;

              busnr = (buses >> 8) & 0xFF;

              child = pci_find_bus(pci_domain_nr(bus), busnr);

              if (!child) {

       //构造一个pci_bus.并将其链入到父总线的children链表上

                     child = pci_add_new_bus(bus, dev, busnr);

                     if (!child)

                            goto out;

                     child->primary = buses & 0xFF;

                     child->subordinate = (buses >> 16) & 0xFF;

                     child->bridge_ctl = bctl;

              }

//递归遍历子总线.返回的是下层最大的总线号

              cmax = pci_scan_child_bus(child);

              if (cmax > max)

                     max = cmax;

              if (child->subordinate > max)

                     max = child->subordinate;

       } else {

              if (!pass) {

                     if (pcibios_assign_all_busses() || broken)

                            pci_write_config_dword(dev, PCI_PRIMARY_BUS,

                                                 buses & ~0xffffff);

                     goto out;

              }

              /* Clear errors */

              pci_write_config_word(dev, PCI_STATUS, 0xffff);

              if (pci_find_bus(pci_domain_nr(bus), max+1))

                     goto out;

              child = pci_add_new_bus(bus, dev, ++max);

              buses = (buses & 0xff000000)

                    | ((unsigned int)(child->primary)     <<  0)

                    | ((unsigned int)(child->secondary)   <<  8)

                    | ((unsigned int)(child->subordinate) << 16);

              if (is_cardbus) {

                     buses &= ~0xff000000;

                     buses |= CARDBUS_LATENCY_TIMER << 24;

              }

              pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);

              if (!is_cardbus) {

                     child->bridge_ctl = bctl;

                     pci_fixup_parent_subordinate_busnr(child, max);

                     /* Now we can scan all subordinate buses... */

                     max = pci_scan_child_bus(child);

                     pci_fixup_parent_subordinate_busnr(child, max);

              } else {

                     for (i=0; i<CARDBUS_RESERVE_BUSNR; i++) {

                            struct pci_bus *parent = bus;

                            if (pci_find_bus(pci_domain_nr(bus),

                                                 max+i+1))

                                   break;

                            while (parent->parent) {

                                   if ((!pcibios_assign_all_busses()) &&

                                       (parent->subordinate > max) &&

                                       (parent->subordinate <= max+i)) {

                                          j = 1;

                                   }

                                   parent = parent->parent;

                            }

                            if (j) {

                                   i /= 2;

                                   break;

                            }

                     }

                     max += i;

                     pci_fixup_parent_subordinate_busnr(child, max);

              }

              child->subordinate = max;

              pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);

       }

       sprintf(child->name,

              (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),

              pci_domain_nr(bus), child->number);

//打印出一些调试信息

       while (bus->parent) {

              if ((child->subordinate > bus->subordinate) ||

                  (child->number > bus->subordinate) ||

                  (child->number < bus->number) ||

                  (child->subordinate < bus->number)) {

                     pr_debug("PCI: Bus #%02x (-#%02x) is %s "

                            "hidden behind%s bridge #%02x (-#%02x)\n",

                            child->number, child->subordinate,

                            (bus->number > child->subordinate &&

                             bus->subordinate < child->number) ?

                                   "wholly" : "partially",

                            bus->self->transparent ? " transparent" : "",

                            bus->number, bus->subordinate);

              }

              bus = bus->parent;

       }

out:

       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);

       return max;

}

 

PCI_PRIMARY_BUS寄存器中的值的含义为:从低位到高位分别为:主总线号,次总线号,子层最大线号。各占两位。pcibios_assign_all_busses()是个空操作,被定义为0,。如果从PCI_PRIMARY_BUS取出来的值,次总线号和子层最大线号有意义。说明该PCI桥是被BIOS处理过的。如果该PCI桥是被BIOS处理过的,那调用pci_add_new_bus()来进一步设置新的pci_dev结构体,最后再对这条次层PCI总线递归调用pci_do_sacne_bus进行扫描枚举,可见,这就是系统对PCI总线做的深度优先搜索。

相反,如果该PCI桥没有被BIOS处理过,那就需要我们手动去处理了。这时,为它分配一个可能的总线号。然后将总线号写入PCI_PRIMARY_BUS寄存器。再构造一个pci_bus.递归枚举其下的设备。

 

分析完这个函数,我们继续往前返回,便直接返回到pci_legacy_init函数了

继续看,如果总线遍历成功,就会转入pci_bus_add_devices()

void pci_bus_add_devices(const struct pci_bus *bus)

{

       struct pci_dev *dev;

       struct pci_bus *child;

       int retval;

//

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

              if (dev->is_added)

                     continue;

              retval = pci_bus_add_device(dev);

              if (retval)

                     dev_err(&dev->dev, "Error adding device, continuing\n");

       }

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

              BUG_ON(!dev->is_added);

              child = dev->subordinate;

              if (!child)

                     continue;

              if (list_empty(&child->node)) {

                     down_write(&pci_bus_sem);

                     list_add_tail(&child->node, &dev->bus->children);

                     up_write(&pci_bus_sem);

              }

              pci_bus_add_devices(child);

              if (child->is_added)

                     continue;

              retval = pci_bus_add_child(child);

              if (retval)

                     dev_err(&dev->dev, "Error adding bus, continuing\n");

       }

}

 

从上面的代码可以看出,它先将设备添加并挂载到全局的pci_devices链表上,这样顺着pci_devices就可以找到所有的设备信息了。这样对0PCI总线的扫描枚举本身已经完成了,系统已经基本知道自己的家底了,剩下的就是对PCI设备进行中断和资源的设置了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核的PCI驱动主要负责管理和控制计算机系统中的PCI(Peripheral Component Interconnect)总线PCI总线是一种用于连接计算机系统中各种外设的通信接口标准。 Linux内核的PCI驱动做了以下几方面的工作: 1. 设备的探测和识别:PCI驱动会检测系统中的PCI总线,自动扫描和识别连接在总线上的所有设备。它会读取设备的标识符信息,确定设备的类型和功能。 2. 设备的初始化和配置:PCI驱动负责配置每个设备的寄存器和中断线路,以确保设备能够正确工作。它会根据设备的设备信息,设置设备的资源分配和中断分配等参数。 3. 提供访问接口:PCI驱动为其他内核模块和用户空间程序提供了访问PCI设备的接口。其他模块可以通过调用PCI驱动提供的函数来访问设备的寄存器和内存区域,执行读写操作。 4. 中断处理:当PCI设备产生中断时,PCI驱动会负责处理中断并相应地通知到其他相关的模块和用户空间程序。它会注册中断处理函数,并将中断请求与相应的设备及其处理函数关联起来。 5. 硬件资源的管理:PCI驱动会跟踪管理系统中的PCI设备和它们所占用的硬件资源,包括内存空间、寄存器、DMA通道等。它会协调设备之间的资源冲突,确保每个设备能够按需访问和使用硬件资源。 通过这些工作,Linux内核的PCI驱动实现了对PCI设备的统一管理和控制,提供了稳定可靠的访问接口,使各个设备能够在系统中协同工作,实现各种功能和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值