x86 pci bus 初始化流程

本文深入探讨了X86架构下PCIe的初始化枚举流程,重点分析了kernel5.2.9版本中PCIe初始化的两大部分:与CPU框架紧密相关的初始化,以及PCI子系统的初始化。文章详细解释了初始化函数pci_arch_init和pci_subsys_init的工作原理,以及PCI设备的发现和配置过程。

X86上pcie的初始化枚举流程,基于kernel 5.2.9分析

                  `只是一个看代码笔记,仅供参考`

pcie的代码在kernel里面大致分两部分初始化的:
一部分和cpu的框架密切相关,使用arch_initcall初始化,x86的初始化代码位于init.c arch\x86\pci,arch_initcall(pci_arch_init); 主要是检测pci type, 设置全局的config 空间read/write函数。
另一部分是pcie subsystem的初始化,使用subsys_initcall 初始化,x86的代码位于legacy.c arch\x86\pci里面,前面适合x86相关的,最终会调用pci_scan_root_bus来扫描初始化总线下所有的bridge和ep设备。

A. arch_initcall(pci_arch_init)的初始化:

探测bus总线,设置read,write函数

函数pci_arch_init:
/* arch_initcall has too random ordering, so call the initializers
   in the right sequence from here. */
static __init int pci_arch_init(void)
{
   
   
#ifdef CONFIG_PCI_DIRECT
	int type = 0;

	type = pci_direct_probe();
#endif

	if (!(pci_probe & PCI_PROBE_NOEARLY))
		pci_mmcfg_early_init();

	if (x86_init.pci.arch_init && !x86_init.pci.arch_init())
		return 0;

#ifdef CONFIG_PCI_BIOS
	pci_pcbios_init();
#endif
	/*
	 * don't check for raw_pci_ops here because we want pcbios as last
	 * fallback, yet it's needed to run first to set pcibios_last_bus
	 * in case legacy PCI probing is used. otherwise detecting peer busses
	 * fails.
	 */
#ifdef CONFIG_PCI_DIRECT
	pci_direct_init(type); /  下面有具体的函数  /
#endif
	if (!raw_pci_ops && !raw_pci_ext_ops)
		printk(KERN_ERR
		"PCI: Fatal: No config space access function found\n");

	dmi_check_pciprobe();

	dmi_check_skip_isa_align();

	return 0;
}
arch_initcall(pci_arch_init);
函数pci_direct_init:
void __init pci_direct_init(int type)
{
   
   
	if (type == 0)
		return;
	printk(KERN_INFO "PCI: Using configuration type %d for base access\n",
		 type);
	if (type == 1) {
   
   
		raw_pci_ops = &pci_direct_conf1;
		if (raw_pci_ext_ops)
			return;
		if (!(pci_probe & PCI_HAS_IO_ECS))
			return;
		printk(KERN_INFO "PCI: Using configuration type 1 "
		       "for extended access\n");
		raw_pci_ext_ops = &pci_direct_conf1;/ 结构体的函数实现在下面 /
		return;
	}
	raw_pci_ops = &pci_direct_conf2;
}
pci_direct_conf1 结构体定义及回调函数
const struct pci_raw_ops pci_direct_conf1 = {
   
   
	.read =		pci_conf1_read,
	.write =	pci_conf1_write,
};
static int pci_conf1_read(unsigned int seg, unsigned int bus,
			  unsigned int devfn, int reg, int len, u32 *value)
{
   
   
	unsigned long flags;

	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095)) {
   
   
		*value = -1;
		return -EINVAL;
	}
	raw_spin_lock_irqsave(&pci_config_lock, flags);
/*
 *	#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
 *	(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
 *	| (devfn << 8) | (reg & 0xFC))
 */
	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

	switch (len) {
   
   
	case 1:
		*value = inb(0xCFC + (reg & 3));
		break;
	case 2:
		*value = inw(0xCFC + (reg & 2));
		break;
	case 4:
		*value = inl(0xCFC);
		break;
	}

	raw_spin_unlock_irqrestore(&pci_config_lock, flags);

	return 0;
}

static int pci_conf1_write(unsigned int seg, unsigned int bus,
			   unsigned int devfn, int reg, int len, u32 value)
{
   
   
	unsigned long flags;

	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095))
		return -EINVAL;

	raw_spin_lock_irqsave(&pci_config_lock, flags);

	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

	switch (len) {
   
   
	case 1:
		outb((u8)value, 0xCFC + (reg & 3));
		break;
	case 2:
		outw((u16)value, 0xCFC + (reg & 2));
		break;
	case 4:
		outl((u32)value, 0xCFC);
		break;
	}

	raw_spin_unlock_irqrestore(&pci_config_lock, flags);

	return 0;
}

B. pcie read/write config 函数的注册流程:

pci_bus_read_config_xx and pci_bus_write_config_xx:
#define PCI_OP_READ(size, type, len) \
int noinline pci_bus_read_config_##size \
	(struct pci_bus *bus, unsigned int devfn, int pos, type *value)	\
{
   
   									\
	int res;							\
	unsigned long flags;						\
	u32 data = 0;							\
	if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;	\
	pci_lock_config(flags);						\
	res = bus->ops->read(bus, devfn, pos, len, &data);		\
	*value = (type)data;						\
	pci_unlock_config(flags);					\
	return res;							\
}

#define PCI_OP_WRITE(size, type, len) \
int noinline pci_bus_write_config_##size \
	(struct pci_bus *bus, unsigned int devfn, int pos, type value)	\
{
   
   									\
	int res;							\
	unsigned long flags;						\
	if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;	\
	pci_lock_config(flags);						\
	res = bus->ops->write(bus, devfn, pos, len, value);		\
	pci_unlock_config(flags);					\
	return res;							\
}
PCI_OP_READ(byte, u8, 1)
PCI_OP_READ(word, u16, 2)
PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(byte, u8, 1)
PCI_OP_WRITE(word, u16, 2)
PCI_OP_WRITE(dword, u32, 4)

EXPORT_SYMBOL(pci_bus_read_config_byte);
EXPORT_SYMBOL(pci_bus_read_config_word);
EXPORT_SYMBOL(pci_bus_read_config_dword);
EXPORT_SYMBOL(pci_bus_write_config_byte);
EXPORT_SYMBOL(pci_bus_write_config_word);
EXPORT_SYMBOL(pci_bus_write_config_dword);

C. pcie subsystem的初始化流程

1.下面是pcie scan and add device的大概函数调用流程

	subsys_initcall(pci_subsys_init);
		pci_subsys_init()
			pci_legacy_init()
				pcibios_scan_root(0);
					pci_scan_root_bus();
						
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值