linux驱动子系统--PCI

PCI子系统的关键概念包括:PCI设备、pci插槽、PCI域、PCI总线、PCI桥、热插拔控制器、


PCI设备探测流程

linux启动过程中会调用PCI核心的探测函数进行PCI设备的探测,并建立起相关的拓扑关系以及记录每个探测到的设备的信息;探测采用递归的方式从根控制器开始逐级往下探测,遇到桥控制器则继续探测隐藏在它下面的设备,依次类推。

PCI探测的接口函数包括:pci_scan_root_bus、pci_scan_bus

在追踪PCI设备探测的流程之前先看一下pci_sys_data这个数据结构,这个数据结构记录了PCI探测过程中需要的各种信息,没有这个数据结构,PCI探测将无法实现。另外对于每个PCI控制器都有这样一个实例(显而易见,不同的控制器相关信息也不同)。

struct pci_sys_data {
#ifdef CONFIG_PCI_DOMAINS
	int		domain;   //对于服务器来说由于有相当多的PCI设备,所以需要通过分域来区别不同的设备
#endif
	struct list_head node;
	int		busnr;		/* primary bus number			*/
	u64		mem_offset;	/* bus->cpu memory mapping offset	*/
	unsigned long	io_offset;	/* bus->cpu IO mapping offset	*/
	struct pci_bus	*bus;		/* PCI bus				*/
	struct list_head resources;	/* root bus resources (apertures)       */
					/* Bridge swizzling			*/
	u8		(*swizzle)(struct pci_dev *, u8 *);
					/* IRQ mapping				*/
	int		(*map_irq)(const struct pci_dev *, u8, u8);
	struct hw_pci	*hw;
	void		*private_data;	/* platform controller private data	*/
}

而pci_sys_data中的信息又通过启动时初始化由hw_pci来获取,这个地方不知道为什么要转换一次?先不管这么多,直接看hw_pci这个结构体,到底都有哪些成员。

struct hw_pci {
#ifdef CONFIG_PCI_DOMAINS
	int		domain;
#endif
	struct list_head buses;
	int		nr_controllers;
	int		(*setup)(int nr, struct pci_sys_data *);
	struct pci_bus *(*scan)(int nr, struct pci_sys_data *);
	void		(*preinit)(void);
	void		(*postinit)(void);
	u8		(*swizzle)(struct pci_dev *dev, u8 *pin);
	int		(*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
}

看起来和pci_sys_data很类似,又仔细看了下代码才发现hw_pci和pci_sys_data的区别。hw_pci记录的是系统中总的和PCI相关的信息,而pci_sys_data是针对每个控制器都有一个,记录的是单个控制器的信息。

看下hw_pci的成员吧,知道了它就知道了pci_sys_data。

domain:因为PCI规范允许单个系统拥有高达256个总线,所以总线编号是8位。但对于大型系统而言,这是不够的,所以,引入了域的概念,每个PCI域可以拥有最多256个总线,每个总线上可支持32个设备,而每个设备上最多可有8种功能。

buses:所以探测的总线都链接到这个双向链表中

nr_controllers:系统已知的控制器数量

setup:主要是获取以及申请PCI总线的地址空间

scan:扫描总线以探测设备和获取设备信息

preinit:初始化PCI控制器的硬件,使软件可以在后面顺利的通过控制器探测设备

postinit:大多数情况下都不需要它,既然设备什么的都探测到了,也就不需要再去初始化了吧

swizzle和map_irq:这里涉及到PCI中断机制,PCI规范在硬件上规定了4根INT管脚,PCI设备的每种功能都可以任意连接到4根INT管脚中的一根,当有多个功能连接到一根INT管脚上时采用线与的方式。同时设备的每种功能还有中断线,用于在控制器那边区别到底是哪种功能设备产生了中断,该值需要在设备探测过程中进行初始化。swizzle将PCI设备的中断管脚一级一级路由到根控制器的中断管脚上,对于大多数PCI桥来说中断管脚路由遵循(((pin - 1) + slot) % 4) + 1的规律。调用swizzle之后得到了远端的PCI设备映射到根控制器的中断管脚,可以根据这个信息给PCI设备分配主机可以识别的虚拟中断线,然后把分配的中断线写入PCI设备的配置空间。

瞧,中断线在这里配置:

中断在桥设备上的路由一般遵循以下规律:

 

了解了hw_pci这个东东,我们再来看驱动核心具体是如何初始化PCI设备列表的。我们的解读以ARM平台作为分析的目标,在ARM平台上调用驱动核心进行设备探测的API是pci_common_init这个函数。

void __init pci_common_init(struct hw_pci *hw)
{
	struct pci_sys_data *sys;

	INIT_LIST_HEAD(&hw->buses);

	pci_add_flags(PCI_REASSIGN_ALL_RSRC);
        //初始化PCI控制器硬件,后续的探测才能正常开展
	if (hw->preinit)
		hw->preinit();
        //设备探测的任务都在这个不起眼的函数里面,所谓小身材大作用是也
	pcibios_init_hw(hw);
	if (hw->postinit)
		hw->postinit();

        //这里开始配置总线上的每个设备的中断线了
	pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);

	list_for_each_entry(sys, &hw->buses, node) {
		struct pci_bus *bus = sys->bus;

		if (!pci_has_flag(PCI_PROBE_ONLY)) {
			/*
			 * Size the bridge windows.
			 */
			pci_bus_size_bridges(bus);

			/*
			 * Assign resources.
			 */
			pci_bus_assign_resources(bus);

			/*
			 * Enable bridges
			 */
			pci_enable_bridges(bus);
		}

		/*
		 * Tell drivers about devices found.
		 */
		pci_bus_add_devices(bus);
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值