pcie的pci_scan_device

在pci_scan_device 中通过pci_bus_read_dev_vendor_id 找到pci 设备后,就调用pci_setup_device 来初始化设备
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;
    u32 l;

    if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
        return NULL;

    dev = pci_alloc_dev(bus);
    if (!dev)
        return NULL;

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

    pci_set_of_node(dev);

    if (pci_setup_device(dev)) {
        pci_bus_put(dev->bus);
        kfree(dev);
        return NULL;
    }

    return dev;
}
从pci_scan_device 中可以看到kernel中使用pci_dev 来代表一个pci device,首先通过pci_alloc_dev 申请一个pci_dev 结构体。
struct pci_dev *pci_alloc_dev(struct pci_bus *bus)
{
    struct pci_dev *dev;

    dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
    if (!dev)
        return NULL;

    INIT_LIST_HEAD(&dev->bus_list);
    dev->dev.type = &pci_dev_type;
    dev->bus = pci_bus_get(bus);

    return dev;
}
在pci_alloc_dev 中通过kzalloc 空间后,将dev的type设置为pci_dev_type,并给dev->bus 赋值.
回到pci_scan_device 中,
    dev->devfn = devfn;
    dev->vendor = l & 0xffff;
    dev->device = (l >> 16) & 0xffff;

可见第一次读取的32 bit中的低16 bit代表vendor,高16 bit代表device id.
pci_set_of_node 这个函数是针对dtb的,在acpi mode下等同于空函数.
void pci_set_of_node(struct pci_dev *dev)
{
    if (!dev->bus->dev.of_node)
        return;
    dev->dev.of_node = of_pci_find_child_device(dev->bus->dev.of_node,
                            dev->devfn);
}
在pci_scan_device 中得到vendor/device后,最后调用pci_setup_device 来添加device
{
    u32 class;
    u16 cmd;
    u8 hdr_type;
    int pos = 0;
    struct pci_bus_region region;
    struct resource *res;

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

    pci_dev_assign_slot(dev);
    /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
       set this higher, assuming the system even supports it.  */
    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;
    dev->class = class >> 8;            /* upper 3 bytes */

    dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %02x class %#08x\n",
           dev->vendor, dev->device, dev->hdr_type, dev->class);

    /* need to have dev->class ready */
    dev->cfg_size = pci_cfg_space_size(dev);

    /* "Unknown power state" */
    dev->current_state = PCI_UNKNOWN;

    /* Early fixups, before probing the BARs */
    pci_fixup_device(pci_fixup_early, dev);
    /* device class may be changed after fixup */
    class = dev->class >> 8;

    if (dev->non_compliant_bars) {
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
            dev_info(&dev->dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
            cmd &= ~PCI_COMMAND_IO;
            cmd &= ~PCI_COMMAND_MEMORY;
            pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
    }

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

        /*
         * 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) {
                xxx
        }
        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);
        pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
        if (pos) {
            pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
            pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
        }
        break;

    case PCI_HEADER_TYPE_CARDBUS:            /* CardBus bridge header */
        return 0;
}
在pci_setup_device 中首先读取PCI_HEADER_TYPE,并赋值给dev
    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;
注意PCI_HEADER_TYPE 是一个8bit的,最高为代表是否支持multifunction。
然后调用set_pcie_port_type 来判断是否是pcie设备还是pci设备
    pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
    if (!pos)
        return;
    pdev->pcie_cap = pos;
这个主要是通过读取PCI_CAP_ID_EXP,来决定的。
后面就通过
static inline int pci_pcie_cap(struct pci_dev *dev)
{
    return dev->pcie_cap;
}
来判断是pci还是pcie设备
在pci_setup_device 中继续调用pci_setup_device 来给dev->slot 赋值
void pci_dev_assign_slot(struct pci_dev *dev)
{
    struct pci_slot *slot;

    mutex_lock(&pci_slot_mutex);
    list_for_each_entry(slot, &dev->bus->slots, list)
        if (PCI_SLOT(dev->devfn) == slot->number)
            dev->slot = slot;
    mutex_unlock(&pci_slot_mutex);
}
最后到用switch 语句来分配irq和bar/rom
从code看pci设备其实分为三类,PCI_HEADER_TYPE_NORMAL 表示普通的pcie endpoint,PCI_HEADER_TYPE_BRIDGE 表示pci bridge,PCI_HEADER_TYPE_CARDBUS 表示pci cardbus.
通过pci_read_irq 来设置pin和irq,pin表示哪跟pin连到gic上,irq 表示中断号。这些都是按照pcie spec来读就可以了.

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_read_bases 来读取bar和rom的内容。到这里pci_setup_device 就返回了,然后pci_scan_device 也就返回了。
最后返回到pci_scan_single_device 中通过pci_device_add 将找到的pcie设备添加到bus中,这里的bus就是指pcie bus
最后就返回到
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
    unsigned fn, nr = 0;
    struct pci_dev *dev;

    if (only_one_child(bus) && (devfn > 0))
        return 0; /* Already scanned the entire slot */

    dev = pci_scan_single_device(bus, devfn);
    if (!dev)
        return 0;
    if (!dev->is_added)
        nr++;

    for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
        dev = pci_scan_single_device(bus, devfn + fn);
        if (dev) {
            if (!dev->is_added)
                nr++;
            dev->multifunction = 1;
        }
    }
}
由于fn0 是肯定存在的,因此当fn0 结束后,在通过for循环遍历身下的7个fn.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值