慢慢欣赏linux PCI-PCIE初始化学习

powerpc的PCI/PCIE控制器枚举PCI设备实在内核里面实现的,依赖的资源主要是设备树。
设备数提供了PCI/PCIE池的空间基地址/大小,用于分配PCI/PCIE的memory空间;
如果是传统PCI设备,设备树还提供了PCI设备的中断路由表。
例如:
powerpc CPU 的PCIE3对接了一个PCIE->PCI桥片,桥片接了一个PCI设备,其设备树可以如下描述:

pcie@f00008000 {
	......
	ranges = <0x2000000 0x0 0xe8000000 0xc 0xe8000000 0 0x08000000>
	interrupt-parent = <&mpic>
	......
	interrupt-map = <
	/**/
	0x20a00 0x0 0x0 0x3 &mpic 0xb 0x1
	......
	>
}

其中,interrupt-map的含义如下:
根据pci扫描,pci设备的域:总线号:设备号:功能号分别是 0000:02:01.2
根据内核对设备树的解析,0x20a00 >>8bit == 0x20a
0x20a分解: 0010 0000 1010 => 0010 00000 010 == 02:01.2。
总线号是2,因为是PCIE3,设备号1是怎么来的呢?首先,查看硬件原理图,PCI设备的IDSEL是17,
紧接着看一下 http://blog.sina.com.cn/s/blog_6472c4cc0100qqni.html的解答:
PCI总线推荐了一种Device Number字段与AD[31:16]之间的映射关系。其中PCI设备0与Device Number字段的0b00000对应;PCI设备1与Device Number字段的0b00001对应,并以此类推,PCI设备15与Device Number字段的0b01111对应。
在这种映射关系之下,一条PCI总线中,与信号线AD16相连的PCI设备其设备号为0;与信号线AD17相连的PCI设备其设备号为1;以此类推,与信号线AD31相连的PCI设备其设备号为15。在Type 00h配置请求中,设备号并没有像Function Number和Register Number那样以编码的形式出现在AD总线上,而是与AD信号一一对应
在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YkW0WTOy-1658386703981)(https://img-blog.csdn.net/20170530110525752?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hpcGluc2t5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]

根据PCI对中断的定义,功能号0对应INTA,1对应INTB,2对应INTC,3对应INTD,但是桥片在INTB_L错开一位,所以功能2实际对应桥片的INTD_L,而INTD_L透传到了CPU的PCIE3的INTD。

我们用的某PCI USB控制器设备有3个中断,INTA INTB INTC;其中
INTA:is the PCI interrupt signal for OHCI HOST Controller! #1
INTB:is the PCI interrupt signal for OHCI HOST Controller! #2
INTC:is the PCI interrupt signal for EHCI HOST Controller!

我们用的是EHCI模式,所以使用INTC中断。

根据飞思卡尔发布的官方手册,PCIE3的 INTD对应的中断号是11,即0xB。
0x3的含义:根据PCI协议,表示PCI设备用的中断是INTC。1,2,3,4分别表示INTA B C D。
0x1表示低电平触发。

PCI初始化流程:

pci_setup	pcibios_setup //解析bootloader传递下来的参数

define_machine(mpc8572_ds) {
	.name			= "MPC8572 DS",
	.probe			= mpc8572_ds_probe,
	.setup_arch		= mpc85xx_ds_setup_arch,
	.init_IRQ		= mpc85xx_ds_pic_init,
#ifdef CONFIG_PCI
	.pcibios_fixup_bus	= fsl_pcibios_fixup_bus,
#endif
	.get_irq		= mpic_get_irq,
	.restart		= fsl_rstcr_restart,
	.calibrate_decr		= generic_calibrate_decr,
	.progress		= udbg_progress,
};

mpc85xx_ds_setup_arch
	->for_each_node_by_type(np, "pci") {
		if (of_device_is_compatible(np, "fsl,mpc8540-pci") || of_device_is_compatible(np, "fsl,mpc8548-pcie"))
			of_address_to_resource(np, 0, &rsrc);
			fsl_add_bridge(np, 0);//解析设备树,并申请pci_controller并将设备树的信息填入
				->hose = pcibios_alloc_controller(dev);//分配pci_controller并根据设备树填充基本信息,申请pci_controller成功的同时会将其添加到host_list列表里面,host_list列表是PCI控制器的全局列表集合
				->setup_pci_atmu //根据设备树提供的信息,设置outbound和inbound窗口,进行存储器域和PCI域的转换
		
	我们再看一下设备树:以2.6.27原版为例
	pci2: pcie@ffe0a000 {
		cell-index = <2>;
		compatible = "fsl,mpc8548-pcie";
		device_type = "pci";
		#interrupt-cells = <1>;
		#size-cells = <2>;//表示几个cell,表示长度,详见 我眼中的设备树:http://www.linuxidc.com/Linux/2016-01/127337.htm
		#address-cells = <3>;//表示几个cell,表示地址,详见 蜗窝科技 device tree 基本概念 http://www.wowotech.net/device_model/dt_basic_concept.html
		reg = <0xffe0a000 0x1000>;
		bus-range = <0 255>;
		ranges = <0x2000000 0x0 0xc0000000 0xc0000000 0x0 0x20000000
			  0x1000000 0x0 0x0 0xffc20000 0x0 0x10000>;
		clock-frequency = <33333333>;
		interrupt-parent = <&mpic>;
		interrupts = <27 2>;
		interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
		interrupt-map = <
			/* IDSEL 0x0 */
			0000 0x0 0x0 0x1 &mpic 0x0 0x1
			0000 0x0 0x0 0x2 &mpic 0x1 0x1
			0000 0x0 0x0 0x3 &mpic 0x2 0x1
			0000 0x0 0x0 0x4 &mpic 0x3 0x1
			>;
		pcie@0 {
			reg = <0x0 0x0 0x0 0x0 0x0>;
			#size-cells = <2>;
			#address-cells = <3>;
			device_type = "pci";
			ranges = <0x2000000 0x0 0xc0000000
				  0x2000000 0x0 0xc0000000
				  0x0 0x20000000

				  0x1000000 0x0 0x0
				  0x1000000 0x0 0x0
				  0x0 0x100000>;
		};
	};
	上述设备树生成pci总线的platform device设备,fsl,mpc8548-pcie会触发fsl_add_bridge的执行

	of_pci_phb_probe
		->of_address_to_resource(dev, 0, &rsrc)//解析设备树
		->bus_range = of_get_property(dev, "bus-range", &len);//同上
		->hose = pcibios_alloc_controller(dev);

of_address_to_resource() 在设备树中找到第一个"reg",并将解析到的信息填充在"res"结构体里。这个例子里"reg = < 0x50000000 0x1000 >”, 指的是分配一块起始物理地址是0x50000000,长度为0x1000字节的空间。of_address_to_resource()会设置res.start = 0x50000000, res.end = 0x50000fff。

解析完毕设备树,获取了PCI HOST的基本信息。下面开始PCI总线的初始化
original_kernel_thread
	->kernel_init
		->do_one_initcall
			->pcibios_init
				->pcibios_scan_phb //遍历host_list的pci_controller
					->bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, hose);
					->hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); //深度优先遍历PCI桥
						->pci_scan_slot(bus, devfn);//下面开始扫描PCI总线下面的PCI设备,for循环里面执行 for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn))
							->dev = pci_scan_single_device(bus, devfn);
								->dev = pci_scan_device(bus, devfn);
									->pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)//读取PCI_VENDOR_ID,不为全0或者全F就表示存在设备
									->dev = alloc_pci_dev();
									->pci_setup_device(dev)
										->//linux驱动模型,增加Pci设备
										->pci_fixup_device
										->pci_read_irq(dev);//irq保存在dev->irq域成员下
										->pci_read_bases(dev, 6, PCI_ROM_ADDRESS);//区分普通和桥设备,对于普通PCI设备,分配memory空间
											->for (pos = 0; pos < howmany; pos++) //探测6个BASE_ADDRESS寄存器,写全F然后取反+1,获取大小,例如[mem 0x00000000 - 0x00ffffff]
												struct resource *res = &dev->resource[pos];
												reg = PCI_BASE_ADDRESS_0 + (pos << 2);
												pos += __pci_read_base(dev, pci_bar_unknown, res, reg);

								->pci_device_add(dev, bus);
						->pcibios_fixup_bus(bus);//给PCI设备分配中断号
								->pcibios_setup_bus_devices(bus);
									->pci_read_irq_line(dev);/* Read default IRQs and fixup if necessary */
										->of_irq_map_pci(pci_dev, &oirq)
											->of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq);//解析设备树interrupt-map,获取中断号
										->virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
					     oirq.size);
										    ->host = irq_find_host(controller);//找到mpic
										    ->host->ops->xlate(host, controller, intspec, intsize,
				     &hwirq, &type)//实际是mpic_hist_xlate
											     ->mpic_host_xlate//翻译获取设备树的高低电平触发模式
									    ->pci_dev->irq = virq;
				->max = pci_scan_bridge(bus, dev, max, pass);
					->child = pci_find_bus(pci_domain_nr(bus), secondary);
					->if (!child) {
						child = pci_add_new_bus(bus, dev, secondary);
					->cmax = pci_scan_child_bus(child); //被递归调用,深度优先
				->/* Call common code to handle resource allocation */
				pcibios_resource_survey();//在函数pci_read_bases里面获取了mem空间大小,现在需要在PCI BUS域池里面给PCI设备分配PCI地址了
					->pci_assign_unassigned_resources();
						->list_for_each_entry(bus, &pci_root_buses, node)
							pci_bus_assign_resources(bus);
								->__pci_bus_assign_resources(bus, NULL);
									->list_for_each_entry(dev, &bus->devices, bus_list) 
										__pci_bus_assign_resources(b, fail_head);
											->list_for_each_entry(dev, &bus->devices, bus_list) 
												__pci_bus_assign_resources(b, fail_head);//递归调用__pci_bus_assign_resources(b, fail_head);
													->pbus_assign_resources_sorted(bus, fail_head);
														->__assign_resources_sorted(&head, fail_head);
															->pci_assign_resource(list->dev, idx)
																->align = pci_resource_alignment(dev, res);
																->__pci_assign_resource(bus, dev, resno)
																	->pci_bus_alloc_resource//总线池分配资源
																	->pci_update_resource(dev, resno);//写到设备的mem寄存器里面
																	//例如BAR 0:set to [mem 0xf 80000000 - 0xf 80ffffff] PCI addr [0x80000000 - 0x80ffffff]
										

如下几篇文章不错,值得学习参考
PCI学习笔记
http://blog.chinaunix.net/uid-24148050-id-101021.html

PCI驱动初始化流程–基于POWERPC85xx架构的Linux内核PCI初始化
http://www.ithao123.cn/content-1305009.html

kernel hacker修炼之道之PCI subsystem(四)
http://down.51cto.com/data/652228/

Zynq设备树教程(四)
http://xilinx.eetrend.com/blog/7777

2.4 PCI总线的配置
http://blog.sina.com.cn/s/blog_6472c4cc0100qqni.html

PCIe设备发现过程
https://blog.csdn.net/yhb1047818384/article/details/71076371

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值