pci设备的初始化

来源:互联网 发布:对戒 知乎 编辑:程序博客网 时间:2024/05/16 05:42

pci设备的初始化

这里讨论系统上电时的情况, 热插拔的情况应该差不多.
首先是从根总线开始, 然后就是扫描这个根总线上的每一条子BUS, 如下:
unsigned int pci_scan_child_bus(struct pci_bus *bus)
{
    unsigned intdevfn, pass, max = bus->secondary;
    structpci_dev *dev;
   pr_debug("PCI: Scanning bus x:x\n", pci_domain_nr(bus),bus->number);
   
   这里循环256(0x100)/8 = 32次的意思是, 每个总线可能有32个设备,
   而每个设备可能是多功能的, 且最多有8个功能.
    for (devfn =0; devfn < 0x100; devfn += 8)
       pci_scan_slot(bus, devfn);
   //下面在递归扫描PCI桥,不是我们要关心的.
   
   pr_debug("PCI: Fixups for bus x:x\n", pci_domain_nr(bus),bus->number);
   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);
       }
   
   pr_debug("PCI: Bus scan for x:x returning with max=x\n",
       pci_domain_nr(bus), bus->number, max);
    returnmax;
}

int pci_scan_slot(struct pci_bus *bus, int devfn)
{
    int func, nr= 0;
    intscan_all_fns;
    //空函数
    scan_all_fns= pcibios_scan_all_fns(bus, devfn);
   //每个设备可能有8个功能
    for (func =0; func < 8; func++, devfn++) {
       struct pci_dev *dev;
       //分配并初始化找到的设备
       dev = pci_scan_single_device(bus, devfn);
       if (dev) {
           nr++;
           
           if (!dev->multifunction) {
               if (func > 0) {
                   dev->multifunction = 1;
               } else {
                    break;
               }
           }
       } else {
           if (func == 0 && !scan_all_fns)
               break;
       }
    }
    returnnr;
}
struct pci_dev *pci_scan_single_device(struct pci_bus *bus, intdevfn)
{
    structpci_dev *dev;
    //扫描设备,如果存在, 分配一个结构,
   //并读取这个设备的寄存器信息将其初始化
    dev =pci_scan_device(bus, devfn);
    if(!dev)
       return NULL;
   //将设备加入的bus总线
   pci_device_add(dev, bus);
    returndev;
}

这个函数的功能上面的注释已经说明白了
static struct pci_dev * __devinit
pci_scan_device(struct pci_bus *bus, int devfn)
{
    structpci_dev *dev;
    u32 l;
    u8hdr_type;
    int delay =1;
    //两个检查
    if(pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID,&l))
       return NULL;
   
    if (l ==0xffffffff || l == 0x00000000 ||
       l == 0x0000ffff || l == 0xffff0000)
       return NULL;
   
    while (l ==0xffff0001) {
       msleep(delay);
       delay *= 2;
       if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID,&l))
           return NULL;
       
       if (delay > 60 * 1000) {
           printk(KERN_WARNING "Device x:x:x.%d not "
                   "responding\n", pci_domain_nr(bus),
                   bus->number, PCI_SLOT(devfn),
                   PCI_FUNC(devfn));
           return NULL;
       }
    }
    //读PCI类型信息,PCI_HEADER_TYPE是在这个寄存器中的偏移值
   //可能的类型信息如下:
#definePCI_HEADER_TYPE       0x0e   
#definePCI_HEADER_TYPE_NORMAL       0
#definePCI_HEADER_TYPE_BRIDGE       1
#definePCI_HEADER_TYPE_CARDBUS   2
    if(pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE,&hdr_type))
       return NULL;
    //如果上面的成功了,则为设备分配一个pci_dev结构,
   //我们在写驱动的时候看到, 在调用probe函数是, 参数传入的就是这里得到的结构.
   //这个结构在这个函数里面已经初始化好了.
    dev =alloc_pci_dev();
    if(!dev)
       return NULL;
   //初步初始化一些域
    dev->bus= bus;
   dev->sysdata = bus->sysdata;
   dev->dev.parent = bus->bridge;
   dev->dev.bus = &pci_bus_type;
   dev->devfn = devfn;
   dev->hdr_type = hdr_type & 0x7f;
   dev->multifunction = !!(hdr_type & 0x80);
   dev->vendor = l & 0xffff;
   dev->device = (l >> 16) & 0xffff;
   dev->cfg_size = pci_cfg_space_size(dev); // 用于PCI-X
   dev->error_state = pci_channel_io_normal;
   
   dev->dma_mask = 0xffffffff; //32为DMA
    //进一步初始化,数据来自于寄存器
    if(pci_setup_device(dev) < 0) {
       kfree(dev);
       return NULL;
    }
    returndev;
}
//分配一个结构并初始化两个队列
struct pci_dev *alloc_pci_dev(void)
{
    structpci_dev *dev;
    dev =kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
    if(!dev)
       return NULL;
   INIT_LIST_HEAD(&dev->global_list);
   INIT_LIST_HEAD(&dev->bus_list);
   pci_msi_init_pci_dev(dev);
    returndev;
}
struct bus_type pci_bus_type = {
   .name       = "pci",
   .match       = pci_bus_match,
   .uevent       = pci_uevent,
   .probe       = pci_device_probe,
   .remove       = pci_device_remove,
   .suspend    =pci_device_suspend,
   .suspend_late   = pci_device_suspend_late,
   .resume_early   = pci_device_resume_early,
   .resume       = pci_device_resume,
   .shutdown    =pci_device_shutdown,
   .dev_attrs    =pci_dev_attrs,
};

这个函数比较简单, 可以看注释,就不说了
static int pci_setup_device(struct pci_dev * dev)
{
    u32class;
   sprintf(pci_name(dev), "x:x:x.%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);
    class>>=8;                   
   dev->class = class;
    class>>= 8;
   pr_debug("PCI: Found %s [x/x] x x\n", pci_name(dev),
       dev->vendor, dev->device, class, dev->hdr_type);
   
   dev->current_state = PCI_UNKNOWN;
   
   pci_fixup_device(pci_fixup_early, dev);
    class =dev->class >> 8;
    switch(dev->hdr_type){           
    casePCI_HEADER_TYPE_NORMAL:           
       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;
    casePCI_HEADER_TYPE_BRIDGE:           
       if (class != PCI_CLASS_BRIDGE_PCI)
           goto bad;
       
       pci_read_irq(dev);
       dev->transparent = ((dev->class & 0xff) == 1);
       pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
       break;
    casePCI_HEADER_TYPE_CARDBUS:           
       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:                   
       printk(KERN_ERR "PCI: device %s has unknown header type x,ignoring.\n",
           pci_name(dev), dev->hdr_type);
       return -1;
    bad:
       printk(KERN_ERR "PCI: %s: class %x doesn't match header type x.Ignoring
class.\n",
              pci_name(dev), class, dev->hdr_type);
       dev->class = PCI_CLASS_NOT_DEFINED;
    }
   
    return0;
}
设备初始化完成后, 我们就回到了pci_scan_single_device()这个函数,
下一步就是将这个分配好的设备加入的bus队列中去了.
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
   device_initialize(&dev->dev);
   dev->dev.release = pci_release_dev;
   pci_dev_get(dev);
   set_dev_node(&dev->dev, pcibus_to_node(bus));
   dev->dev.dma_mask = &dev->dma_mask;
   dev->dev.coherent_dma_mask = 0xffffffffull;
   
   pci_fixup_device(pci_fixup_header, dev);
   
   INIT_LIST_HEAD(&dev->global_list);
   down_write(&pci_bus_sem);
   list_add_tail(&dev->bus_list, &bus->devices);//加入到bus的device队列
   up_write(&pci_bus_sem);
}

void device_initialize(struct device *dev)
{
   //属于devices_subsys子系统
   kobj_set_kset_s(dev, devices_subsys);
   //以下是初始化一下队列, 如等待队列
   kobject_init(&dev->kobj);
   klist_init(&dev->klist_children, klist_children_get,
          klist_children_put);
   INIT_LIST_HEAD(&dev->dma_pools);
   INIT_LIST_HEAD(&dev->node);
   init_MUTEX(&dev->sem);
   spin_lock_init(&dev->devres_lock);
   INIT_LIST_HEAD(&dev->devres_head);
   device_init_wakeup(dev, 0);
   set_dev_node(dev, -1);
}

0 0
原创粉丝点击