参考理解文档:https://wenku.baidu.com/view/23335dd349649b6648d747bf.html
1.PCI设备编号
一条PCI总线支持256个PFA,即支持256个PCI device
每个PCI芯片都有自己的device number(取决于IDSEL管脚),每个PCI芯片占用8个PFA。
每个PCI芯片的第一个PCI device的PFA必为8的倍数。
若PCI device的配置空间中PCI_HEADER_TYPE寄存器的最高bit为1,说明此芯片还有其他PFA,即还有其他device,即当前芯片是
multi-function device. Each function on a multi-function device has its own configuration space
在系统中,每个PCI芯片上的所独有的信号线是:INTA、INTB、INTC、INTD、IDSEL
每个芯片上的IDSEL需要连到PCI总线中AD[31:11]中的一根,这对应于PCI device PFA的device number
IDSEL is equivalent to chip select on the CPU side during address phase of CFG RD\WR command.
DEVSEL is used in every transaction when the target claims the cycle address to it. It is like saying this cycle in mine, and nobody touches it!
2.PCI配置空间
PCI配置空间中的BAR(Base Address Register)用来映射PCI设备的寄存器,里面的值是bus地址首地址,至于空间的大小,先向bar中写0xFFFFFFFFF,然后读取,选最低的一位非0的,比如为0x1000,那个空间的大小就为0x1000。
3.PCI地址转换
3.PCI拓扑结构
3.PCI枚举过程
通过PCI枚举,CPU知道当前系统上有多少PCI设备,多少根PCI总线,PCI配置空间初始化
PCI 总线扫描的原理是从总线 0 扫描到总线 255,对于每条总线,系统都会扫描所有(总线号,设备号,功能号),读出每个设备配置空间的Device ID和Vendor ID寄存器,如果这两个寄存器的值是个无效值(0xFFFF),则说明当前位置上没有设备,接着扫描下一个位置
如果是有效值(非0xFFFF),当前位置是个有效的 PCI 设备/桥。进而再读取该设备的 Header Type 寄存器,如果该寄存器为 1,则表示当前设备是 PCI 桥,否则是 PCI 设备
3.Linux内核PCI数据结构.
PCI 总线 用 pci_bus 结构来描述,它有以下几个主要的属性:
parent:可通过该属性索引到上层 PCI 总线。
self:该属性标志了连接的上行 PCI 桥(对应的数据结构是 pci_dev)。
children:该属性标志了总线连接的所有 PCI 子总线链表。
devices:该属性标志了总线连接的所有 PCI 设备链表。
ops:该属性标志了总线上所有 PCI 设备的配制空间读写操作函数。
number:该属性标志了当前 PCI 总线的编号。
primary:该属性标志了 PCI 上行总线编号。
secondary:该属性标志了 PCI 下行总线编号。
subordinate:该属性标志了能够访问到的最大总线编号。
resource:该属性标志了 Memory/IO 地址空间
PCI 设备 用 pci_dev 结构来描述,它有以下几个主要的属性:
global_list:Linux 定义了一个全局列表来索引所有PCI 设备,该属性标志了这个全局列表的首指针。
bus:该属性标志了当前设备所在的 PCI 总线(对应的数据结构是 pci_bus)。
devfn:该属性标志了设备编号和功能编号。
vendor:该属性标志了供应商编号。
device:该属性标志了设备编号。
driver:该属性标志了设备对应的驱动代码(对应的数据结构是 pci_driver)。
irq:该属性标志了中断号。
resource:该属性标志了 Memory/IO 地址区间
当 Linux 内核在做 PCI 初始化工作时,它会根据建立一个由 pci_controller、pci_bus 和 pci_dev 三者组成的一个组织结构图。根据这个结构,软件开发者可以很方便的通过 PCI 控制器索引到每个 PCI 设备或者 PCI 总线
4.Linux的PCI子系统初始化流程
4.1 初始化PCI控制器
4.2 PCI枚举过程
内核分配mem资源时是从高地址开始分配的。
mpc85xx_setup_hose() -->pciauto_bus_scan() for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
//遍历0号总线上所有PCI设备 if (读当前设备配置空间PCI_HEADER_TYPE失败) continue; //当前设备不存在,扫描下一个 读当前设备配置空间PCI_CLASS_REVISION If(当前设备是PCI桥){
pciauto_setup_bars() //分配pci_controller的资源的子集 pciauto_prescan_setup_bridge() //写PCI桥配置空间 PCI_PRIMARY_BUS //PCI_SECONDARY_BUS //PCI_SUBORDINATE_BUS pciauto_bus_scan() //递归扫描下一条pci bus pciauto_postscan_setup_bridge() //写PCI桥配置空间PCI_SUBORDINATE_BUS } 。。。。。略去 else {
//当前设备是PCI设备 pciauto_setup_bars() } }
4.3 创建PCI树
上面说了,PCI设备配置空间都初始化差不多了,但是PCI设备还没有通过数据结构组织起来。
上面说了,PCI设备配置空间都初始化差不多了,但是PCI设备还没有通过数据结构组织起来。 do_initcalls() -->pcibios_init() //所在文件 arch/ppc/kernel/pci.c pcibios_init() (1):为PCI设备构造数据结构,组织成PCI树 -->pci_scan_bus(hose->first_busno, hose->ops, hose) //hose就是上面的pci_controller结构 -->pci_scan_bus_parented -->pci_create_bus // 建立 PCI bus 0 对应的数据结构,这个bus的资源尚未初始化 -->pci_scan_child_bus // 从PCI bus 0 开始扫描生成PCI树,使用了递归 -->pci_scan_slot -->pci_scan_single_dev -->pci_scan_device() //创建 pci_dev结构 -->pci_setup_device() //区分桥与设备,分别进行初始化 -->pci_read_bases(); //在这才初始化了pci_dev->resource[]
pci_dev->resouce[]中保存的才是cpu internal address(EA),可以对这些地址进行用ioremap,在驱动程序对bar所指位置读写的时候一定要用这个
(2)给PCI设备分配IRQ号
-->pci_fixup_irqs() //遍历所有pci设备,调用pdev_fixup_irq() -->pdev_fixup_irq -->ppc_md.pci_swizzle() //实际调用common_swizzle(),获得pci所在slot编号 读PCI设备配置空间PCI_INTERRUPT_PIN,获得中断pin编号[1到4] 之所以是1到4,因为PCI规范里设备最多4个管脚(除第一个外,其他3个仅用于多功能设备) -->ppc_md.pci_map_irq() //实际调用mpc85xx_map_irq() //根据slot,pin 和 pci_irq_table[][4] //来计算出irq号 -->pcibios_update_irq(pci_dev,irq) //将irq号写入pci设备的配置空间PCI_INTERRUPT_LINE //注意,这里寄存器只是用来保存结果,例如把其值8改为9,并不能改变中断号
(3)PCI结构树有了,现在构建PCI的资源树,有冲突就修改
-->pcibios_allocate_bus_resource() //只考虑pci_bus,形成bus级资源树(并同时check,资源冲突了就修改) -->pcibios_allocate_resources() //把pci_dev也考虑进去,完成资源树
4.3 加载PCI设备驱动
static struct pci_driver e1000_driver = {
.name = e1000_driver_name, .id_table = e1000_pci_tbl, .probe = e1000_probe, .remove = __devexit_p(e1000_remove), #ifdef CONFIG_PM /* Power Managment Hooks */ .suspend = e1000_suspend, .resume = e1000_resume, #endif .shutdown = e1000_shutdown, .err_handler = &e1000_err_handler }; module_init(e1000_init_module);
do_initcalls() -->e1000_init_module pci_register_driver(&e1000_driver) // e1000_driver.driver.bus = &pci_bus_type; -->driver_register(e1000_driver.driver); //pci_bus_type.probe() 非空,即调用pci_device_probe() pci_device_probe(*device) -->__pci_device_probe(*pci_driver,*pci_dev) -->pci_match_device(*pci_driver,*pci_dev) -->pci_call_probe(*pci_driver,*pci_dev,pci_device_id) -->drv->probe(*pci_device,*pci_device_id) 即执行 e1000_probe(*pci_device,*pci_device_id) 接下来就是e1000 PCI 网卡驱动的具体代码。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205890.html原文链接:https://javaforall.net
