PCIe 学习笔记(三)

pci note 3

-v0.1 2014.11.22 sherlock draft
-v0.2 2015.1.24  sherlock add pci_scan_bridge anlysis

pci_scan_child_bus(b)

这个函数完成pci总线的枚举,完成整个pci树各个总线号的分配。但是并没有分配各个pci桥,

pci device的BAR和mem, I/O, prefetch mem的base/limit寄存器

[cpp]  view plain  copy
  1. unsigned int pci_scan_child_bus(struct pci_bus *bus)  
  2. {  
  3.     unsigned int devfn, pass, max = bus->busn_res.start;  
  4.     struct pci_dev *dev;  
  5.   
  6.     /* 开始pci枚举, 一个pci bus上最多有256个设备,每个设备最多有8个function 
  7.      * 所以这里最多可以扫描32个device。实际上每个function也是有配置空间的 
  8.      * 所以function可以看作是个逻辑的设备 
  9.      */  
  10.     for (devfn = 0; devfn < 0x100; devfn += 8)  
  11.         pci_scan_slot(bus, devfn);  
  12.   
  13.     /* pci虚拟化相关的东西 */  
  14.     max += pci_iov_bus_range(bus);  
  15.   
  16.     ...  
  17.     if (!bus->is_added) {  
  18.         dev_dbg(&bus->dev, "fixups for bus\n");  
  19.         /* 对于arm64来说,是个空函数 */  
  20.         pcibios_fixup_bus(bus);  
  21.         bus->is_added = 1;  
  22.     }  
  23.   
  24.     for (pass = 0; pass < 2; pass++)  
  25.         list_for_each_entry(dev, &bus->devices, bus_list) {  
  26.             if (pci_is_bridge(dev))  
  27.                     /* 开始递归的做pci枚举,在下面的函数中会再次 
  28.                  * 调用pci_scan_child_bus。总线号的确定也在这个 
  29.                  * 函数中 
  30.                  */  
  31.                 max = pci_scan_bridge(bus, dev, max, pass);  
  32.         }  
  33.     ...  
  34.     return max;  
  35. }  
  36.   
  37. pci_scan_slot(struct pci_bus *bus, int devfn)  
  38.     ...  
  39.     --> pci_scan_single_device(bus, devfn)  
  40.         ...  
  41.         /* 扫描devfn设备, 探测设备:读vendor id, 读BAR的大小 。 
  42.          * 软件行为:为device分配struct pci_dev; 根据探测设备得到的数据,填 
  43.          * 写pci_dev里的一些值。若是按照笔记1中的硬件拓扑图现在扫描的device 
  44.          * 应该是pcie root port 
  45.          */  
  46.     --> pci_scan_device(bus, devfn)  
  47.             /* 读devfn的vendor id, 会调用到pci_scan_root_bus的参数ops传入的read操作 */  
  48.         --> pci_bus_read_dev_vendor_id()  
  49.             /* 分配pci_dev结构 */  
  50.         --> pci_alloc_dev(bus)  
  51.         ...  
  52.             /* 处理pci配置头中的class code, Header Type, Interrupt Pin 
  53.          * Interrupt Line等内容,并设定新分配的pci_dev中的对应项。这里 
  54.          * 也会探测device的BAR大小,并设定pci_dev->resource[]。我们关注 
  55.          * 的重点也是pci_bus,pci_dev中相应的元素怎么变化。 
  56.          */  
  57.         --> pci_setup_device(dev)  
  58.             ...  
  59.     --> pci_device_add(dev, bus)   
  60.   
  61. int pci_setup_device(struct pci_dev *dev)  
  62.     ...  
  63.         /* 的到device配置空间中head type的值,可以是普通pci device, pci桥等 
  64.      * 这个值在下面的switch-case中决定程序是走哪个分支。如果当前的设备是 
  65.      * pcie root port,那么走PCI_HEADER_TYPE_BRIDGE分支。设备配置空间 
  66.      * head type的值一般是pci host controller驱动中配置过的, 可以参考 
  67.      * /drivers/pci/host/pcie-designware.c 
  68.      */  
  69.     --> pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type)  
  70.         /* device的parent是上游总线之上的那个pci桥 */  
  71.     --> dev->dev.parent = dev->bus->bridge;  
  72.         /* 读出class revision的值 */  
  73.     --> pci_read_config_dword(dev, PCI_CLASS_REVISION, &class)  
  74.     dev->revision = class & 0xff;  
  75.     dev->class = class >> 8;           /* upper 3 bytes */  
  76.   
  77.     dev->current_state = PCI_UNKNOWN;  
  78.   
  79.     class = dev->class >> 8;  
  80.   
  81.     switch (dev->hdr_type) {         /* header type */  
  82.     case PCI_HEADER_TYPE_NORMAL:            /* standard header */  
  83.         if (class == PCI_CLASS_BRIDGE_PCI)  
  84.             goto bad;  
  85.         pci_read_irq(dev);  
  86.         /* 
  87.          * 得到BAR的大小,并存在pci_dev->resource[]中,主要是对pci 
  88.          * device起作用。当输入参数dev是pci桥时,得到的值似乎对后面影响 
  89.          * 不大,这点需要确认下? 
  90.          */  
  91.         pci_read_bases(dev, 6, PCI_ROM_ADDRESS);  
  92.         pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);  
  93.         pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);  
  94.   
  95.             break;  
  96.   
  97.     case PCI_HEADER_TYPE_BRIDGE:            /* bridge header */  
  98.         if (class != PCI_CLASS_BRIDGE_PCI)  
  99.             goto bad;  
  100.         pci_read_irq(dev);  
  101.         dev->transparent = ((dev->class & 0xff) == 1);  
  102.         pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);  
  103.         set_pcie_hotplug_bridge(dev);  
  104.         pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);  
  105.         if (pos) {  
  106.             pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);  
  107.             pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);  
  108.         }  
  109.         break;  
  110.         ...  
  111.     }  
[cpp]  view plain  copy
  1. 现在回到pci_scan_child_bus中的pci_scan_bridge()函数。  
  2. /* 
  3.  * 如果一个pci_dev是pci桥的话,以它的上游总线bus和这个设备dev本身为参数,扫描这个 
  4.  * 桥。这个函数中实现pci树的递归扫描,在这个函数中为整个pci树上的各个子总线分配 
  5.  * 总线号。这里pci_scan_bridge会调用两次,第一次处理BIOS中已经配置的东西,不清楚 
  6.  * intel在BIOS中做了怎样的处理。 
  7.  */  
  8. pci_scan_bridge(bus, dev, max, pass);  
  9.     ...  
  10.     /* 这里上来就读桥设备的主bus号,是因为有些体系架构下可能在BIOS中已经对这些 
  11.      * 做过配置。在ARM64中暂时没有看到有这样做的情况。 
  12.      */  
  13.     --> pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);  
  14.     ...  
  15.     /* 配置了在kernel中分配bus号,应该不会进入到if中 */  
  16.     --> if ((secondary || subordinate) && !pcibios_assign_all_busses() &&  
  17.         !is_cardbus && !broken) {  
  18.     ...  
  19.     } else {  
  20.             /* 第一次掉用pci_scan_bridge就此返回了 */  
  21.         if (!pass) {  
  22.                 ...  
  23.             goto out;  
  24.         }  
  25.   
  26.         /* Clear errors */  
  27.         pci_write_config_word(dev, PCI_STATUS, 0xffff);  
  28.   
  29.         /* 确定是否已用max+1这个bus号 */  
  30.         child = pci_find_bus(pci_domain_nr(bus), max+1);  
  31.         if (!child) {  
  32.                 /* 分配新的struct pci_bus */  
  33.             child = pci_add_new_bus(bus, dev, max+1);  
  34.             if (!child)  
  35.                 goto out;  
  36.             pci_bus_insert_busn_res(child, max+1, 0xff);  
  37.         }  
  38.         max++;  
  39.         /* 合成新的bus号,准备写入pci桥的配置空间 */  
  40.         buses = (buses & 0xff000000)  
  41.               | ((unsigned int)(child->primary)     <<  0)  
  42.               | ((unsigned int)(child->busn_res.start)   <<  8)  
  43.               | ((unsigned int)(child->busn_res.end) << 16);  
  44.         ...  
  45.         /* 写入该pci桥primary bus, secondary bus, subordinate bus */  
  46.         pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);  
  47.   
  48.         if (!is_cardbus) {  
  49.             child->bridge_ctl = bctl;  
  50.             /* 递归调用pci_scan_child_bus, 扫描这个子总线下的设备*/  
  51.             max = pci_scan_child_bus(child);  
  52.         } else {  
  53.                 /* 不关心cardbus */  
  54.             ...  
  55.         }  
  56.         /* 
  57.          * Set the subordinate bus number to its real value. 
  58.          * 每次递归结束把实际的subordinate bus写入pci桥的配置空间 
  59.          * subordinate bus表示该pci桥下最大的总线号 
  60.          */  
  61.         pci_bus_update_busn_res_end(child, max);  
  62.         pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);  
  63.     }  
  64.     ...  
  65. out:  
  66.     pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);  
  67.   
  68.     return max;  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值