PCIe 学习笔记(四)

pci note 4
==========

-v0.1 2015.6.20 Sherlock draft: about pci resource assignment, just analysis
                                ARM specific PCIe
-v0.2 2015.6.22 Sherlock add introduction, some details analysis

有些机器没有中文输入法,就成了现在中英杂揉了 :(
linux kernel中pci分配资源的代码比较复杂,下面先总体介绍大体的流程,有了一个整体
概念后再去理解细节就比较容易一些了。

整个资源分配的过程就是从系统的总资源里给每个pci设备的bar分配资源,给每个pci桥
的base, limit的寄存器分配资源。借用这个系列笔记一中的图,资源分配所要做的就是填写
pci ep设备和pci桥中配置空间的寄存器,从总的资源(这里假设soc系统给这个pci host
bridge分配的资源是 0xb200_0000 ~ 0xb400_0000)给pci桥上的base/limit寄存器分资源,
给pci ep设备上的Bar分资源。

现在已知的是:
1. resource: 0xb200_0000 ~ 0xb400_0000. 这个是系统早就分好的。pcie dts节点中的range项是其的一个子集。

2. pci ep设备有多少个bar和各个bar的信息, 这些信息已经存在了对应的pci_dev->resource[]中了。

[cpp]  view plain  copy
  1.   +----------------+ ----> PCIe host bridge  
  2.   | pcie root port |  
  3.   +----------------+ ----> in Soc  
  4.    |           (resource: 0xb200_0000 ~ 0xb400_0000)  
  5. +-------------------------------------------------+ ----> switch  
  6. |           +----------------+                |  
  7. |           |   pci bridge   |                |  
  8. |           +----------------+                |  
  9. |               |                         |  
  10. |         -------------------------------         |  
  11. |         |                             |         |  
  12. | +----------------+       +----------------+ |  io base/limit  
  13. | |   pci bridge   |       |   pci bridge   | |  32bits mem base/limit  
  14. | +----------------+       +----------------+ |  64bits mem base/limit  
  15. +-------------------------------------------------+  pref 64bits mem base/limit  
  16.           |                             |  
  17.   +----------------+           +----------------+  
  18.   |  PCIe net card |           | PCIe net card  |  
  19.   +----------------+           +----------------+  
  20.   |     BAR 0      |           |     BAR 0      |  
  21.   +----------------+           +----------------+  
  22.   |     ...        |           |     ...        |  
  23.   +----------------+           +----------------+  
  24.   |     BAR 5      |           |     BAR 5      |  
  25.   +----------------+           +----------------+  
大体流程是:
所有分配可以用pci_assign_unassigned_bus_resources完成。
__pci_bus_size_bridges用深度优先递归确定各级pci桥上base/limit的大小。会记录在
pci_dev->resource[PCI_BRIDGE_RESOURCES] ...中,这时并没有在寄存器中写入数值。
__pci_bus_assign_resources()首先对当前总线下的设备请求的资源排序,这个资源中包括
总线下的设备上的bar; 总线下游请求的资源,即base/limit下的资源。对于该总线下设备
上的bar资源,在下面__assign_resources_sorted的调用链中立即分配, pci桥上的base/limit
则先不分配。__pci_bus_assign_resources()中再次用深度优先递归的办法,依次分配各个
pci ep设备上的bar资源,在每个递归向上返回的过程中调用__pci_bus_size_bridges()
设置pci桥上的base/limit寄存器。

下面用具体例子再详细解释一下分配过程, 是用的例子是直接在pci host bridge上插
一个pcie ep设备:

[cpp]  view plain  copy
  1.     |   root bus: 0   ---->  struct pci_bus  
  2.     |  
  3. +----------------+        ---->  struct pci_host_bridge  
  4. | pcie root port |        ---->  struct pci_dev  
  5. +----------------+          
  6.     |   bus: 1        ---->  struct pci_bus  
  7.     |  
  8. +----------------+  
  9. | pcie net cards |        ---->  struct pci_dev  
  10. +----------------+  
[cpp]  view plain  copy
  1. void pci_assign_unassigned_bus_resources(struct pci_bus *bus)  
  2. {  
  3.     struct pci_dev *dev;  
  4.     LIST_HEAD(add_list); /* list of resources that 
  5.                     want additional resources */  
  6.   
  7.     down_read(&pci_bus_sem);  
  8.     /* bus is root pci bus, so bus->devices is pci host bridge's pci_dev */  
  9.     list_for_each_entry(dev, &bus->devices, bus_list)  
  10.         if (pci_is_bridge(dev) && pci_has_subordinate(dev))  
  11.                 /* dev->subordinate is pci_bus: 1, pcie device 
  12.                  * is connected to pci bus 1(assuming root bus 
  13.                  * is 0) 
  14.                  */  
  15.                 __pci_bus_size_bridges(dev->subordinate,  
  16.                              &add_list);  
  17.     up_read(&pci_bus_sem);  
  18.     /* bus 0 */  
  19.     __pci_bus_assign_resources(bus, &add_list, NULL);  
  20.         --> pbus_assign_resources_sorted(bus, realloc_head, fail_head);  
  21.         /* will parse the size of resource(each bar), and add 
  22.          * them to list head using struct pci_dev_resource. 
  23.          *  
  24.          * bus is root bus, so dev is pci host bridge, 
  25.          * but comments in __dev_sort_resources said that 
  26.          * we do not parse the resources in host bridge 
  27.          *  
  28.          * in pcie-designware, it does not use PCI_CLASS_BRIDGE_HOST 
  29.          * to indicate PCIe host bridge, but use PCI_CLASS_BRIDGE_PCI. 
  30.          * so here will parse "Bar" resources in host bridge, and 
  31.          * resaults will be stored in pci_dev->resource[PCI_BRIDGE_RESOURCES]... 
  32.          * so pci_assign_resource will assign Bar 7 and Bar 8 according 
  33.          * to pci_dev->resource[PCI_BRIDGE_RESOURCES]..., in fact, this 
  34.          * info should be a base/limit info. And in pci_assign_resource 
  35.          * it will not allow above base/limit data to be wrotten to bar 
  36.          *  
  37.          * So we need to find a way to fix above problem 
  38.          */  
  39.         --> list_for_each_entry(dev, &bus->devices, bus_list)  
  40.             __dev_sort_resources(dev, &head);  
  41.   
  42.         --> __assign_resources_sorted(&head, realloc_head, fail_head);  
  43.             --> ...  
  44.             --> assign_requested_resources_sorted(head, fail_head);  
  45.                 --> list_for_each_entry(dev_res, head, list)  
  46.                     --> pci_assign_resource(dev_res->dev, idx)  
  47.     --> list_for_each_entry(dev, &bus->devices, bus_list)  
  48.         /* 这里会递归进入下一级总线,对已有的bar资源排序,然后分配。 
  49.          * pci_assign_resource会对bar写入相应的资源及地址 
  50.          */  
  51.         --> __pci_bus_assign_resources(b, realloc_head, fail_head);  
  52.         ...  
  53.         /* 从__pci_bus_assign_resources返回进入pci_setup_bridge分配pci 
  54.          * bridge的base/limit资源 
  55.          */  
  56.         --> case PCI_CLASS_BRIDGE_PCI:  
  57.         --> pci_setup_bridge(b);  
  58.   
  59.     BUG_ON(!list_empty(&add_list));  
  60. }  
  61.   
  62. /* I am afraid that below function added pci_dev->resource[PCI_BRIDGE_RESOURCES] 
  63.  * 
  64.  * dev->subordinate: pci_bus 1 
  65.  */  
  66. --> __pci_bus_size_bridges(dev->subordinate, &add_list);  
  67.     /* bus: pci_bus 1, here we find pci_bus 1 and go out this list_for_each_entry */  
  68.     --> list_for_each_entry(dev, &bus->devices, bus_list)  
  69.         --> struct pci_bus *b = dev->subordinate;  
  70.         if (!b)  
  71.             continue;  
  72.         ...  
  73.         --> case PCI_CLASS_BRIDGE_PCI:  
  74.             default:  
  75.                 __pci_bus_size_bridges(b, realloc_head);  
  76.   
  77.     --> switch (bus->self->class >> 8)  
  78.         ...  
  79.         /* check if this bridge support io and prefetch mem ranges */  
  80.         /* 
  81.          *  log if an intel 82575 plugged in above pci host bridge: 
  82.          * 
  83.          *  begin: pci_bridge_check_ranges 
  84.          *  in pci_bridge_check_ranges: mem size: 1 
  85.          *  in pci_bridge_check_ranges: first read io: 0 
  86.          *  in pci_bridge_check_ranges: second read io: e0f0 
  87.          *  in pci_bridge_check_ranges: io size: 1 
  88.          *  in pci_bridge_check_ranges: first read pref mem: 10001 
  89.          *  in pci_bridge_check_ranges: first read 64 pref mem: 0 
  90.          *  in pci_bridge_check_ranges: second read 64 pref mem: ffffffff 
  91.          */  
  92.         --> case PCI_CLASS_BRIDGE_PCI  
  93.             pci_bridge_check_ranges(bus);  
  94.                 /* why writing 0xe0f0, so here just write a 
  95.                  * random number to test if this register can 
  96.                  * be wrotten something? 
  97.                  */  
  98.                 --> pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);  
  99.         --> default:  
  100.         /* to caculate size of io and mem ranges of this pci bridge */  
  101.         --> pbus_size_io()  
  102.   
  103.         /* pci_dev->resource[0] is 32 mem ? */  
  104.         --> b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES];  
  105.             mask = IORESOURCE_MEM;  
  106.             prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;  
  107.   
  108.         /* pci_dev->resource[2] is 64 mem ? */  
  109.         --> if (b_res[2].flags & IORESOURCE_MEM_64)  
  110.         ...  
  111.   
  112.         pbus_size_mem()  
  113.   
  114. --> pbus_size_io(struct pci_bus *bus, resource_size_t min_size,  
  115.     resource_size_t add_size, struct list_head *realloc_head)  
  116.     /* below function will return pci_bus->resource[], here will be io 
  117.      * resource item 
  118.      */  
  119.     --> find_free_bus_resource()  
  120.     /* from comments in kernel code: I/O windows are 4K-aligned, but some 
  121.      * bridges have an extension to support 1K alignment.  
  122.      * 
  123.      * min_align is 4k here. 
  124.      */  
  125.     --> min_align = window_alignment(bus, IORESOURCE_IO);  
  126.     --> list_for_each_entry(dev, &bus->devices, bus_list)  
  127.     /* find required io resource in pci_dev->resource */  
  128.     --> for (i = 0; i < PCI_NUM_RESOURCES; i++)  
  129.   
  130.     ...  
  131.   
  132.     /* add io resource to pci_bus->resource */  
  133.     --> b_res->start = min_align;  
  134.         b_res->end = b_res->start + size0 - 1;  
  135.     b_res->flags |= IORESOURCE_STARTALIGN;  
  136.   
  137. --> assign_requested_resources_sorted(head, fail_head);  
  138.     /* For resources in host bridge, head here has bar0, bar7 and bar8. 
  139.      * I do not know when bar7 and bar8 resources had been added to head 
  140.      * 
  141.      * I added some prints, it seems that before resource assignment 
  142.      * (__pci_bus_assign_resources) bar7 and bar8 already added to 
  143.      * pci_dev->resource[PCI_BRIDGE_RESOURCES] and [PCI_BRIDGE_RESOURCES + 1] 
  144.      */  
  145.     --> list_for_each_entry(dev_res, head, list)  
  146.         --> pci_assign_resource(dev_res->dev, idx)  


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值