PCIe 访问 EP 配置空间,空间映射详解,BDF 计算偏移

访问 EP 的配置空间方法

  1. 内存映射
  2. IO 访问

内存访问配置空间

  1. 前置知识
    PCIe 设备的寻址是按照 BDF 即 Bus-Device-Function 来组织的。访问某个设备则需要根据BDF计算偏移地址。

  2. 两种不同的内存访问配置空间方法

    • 类 xilinx,基地址 + 偏移地址访问
// linux-5.10\drivers\pci\controller\pcie-xilinx.c
/* ECAM definitions */
#define ECAM_BUS_NUM_SHIFT		20
#define ECAM_DEV_NUM_SHIFT		12
/**
 * xilinx_pcie_map_bus - Get configuration base
 * @bus: PCI Bus structure
 * @devfn: Device/function
 * @where: Offset from base
 *
 * Return: Base address of the configuration space needed to be
 *	   accessed.
 */
static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus,
					 unsigned int devfn, int where)
{
	struct xilinx_pcie_port *port = bus->sysdata;
	int relbus;

	if (!xilinx_pcie_valid_device(bus, devfn))
		return NULL;

	relbus = (bus->number << ECAM_BUS_NUM_SHIFT) |
		 (devfn << ECAM_DEV_NUM_SHIFT);

	return port->reg_base + relbus + where;
}

这个函数是转换配置空间的地址,port->reg_base = devm_pci_remap_cfg_resource(dev, &regs); 从设备树获取的基地址,然后加上 bus->number 与 devfn 计算得到的偏移地址。这个地址可以直接通过 readl 与 writel 直接访问。

  • 类 designware,变基地址 + 偏移地址访问
static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
						unsigned int devfn, int where)
{
	int type;
	u32 busdev;
	struct pcie_port *pp = bus->sysdata;
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

	/*
	 * Checking whether the link is up here is a last line of defense
	 * against platforms that forward errors on the system bus as
	 * SError upon PCI configuration transactions issued when the link
	 * is down. This check is racy by definition and does not stop
	 * the system from triggering an SError if the link goes down
	 * after this check is performed.
	 */
	if (!dw_pcie_link_up(pci))
		return NULL;

	busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
		 PCIE_ATU_FUNC(PCI_FUNC(devfn));

	if (pci_is_root_bus(bus->parent))
		type = PCIE_ATU_TYPE_CFG0;
	else
		type = PCIE_ATU_TYPE_CFG1;


	dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
				  type, pp->cfg0_base,
				  busdev, pp->cfg0_size);

	return pp->va_cfg0_base + where;
}

static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn,
				 int where, int size, u32 *val)
{
	int ret;
	struct pcie_port *pp = bus->sysdata;
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

	ret = pci_generic_config_read(bus, devfn, where, size, val);

	if (!ret && pci->num_viewport <= 2)
		dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
					  PCIE_ATU_TYPE_IO, pp->io_base,
					  pp->io_bus_addr, pp->io_size);

	return ret;
}

static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn,
				 int where, int size, u32 val)
{
	int ret;
	struct pcie_port *pp = bus->sysdata;
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

	ret = pci_generic_config_write(bus, devfn, where, size, val);

	if (!ret && pci->num_viewport <= 2)
		dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
					  PCIE_ATU_TYPE_IO, pp->io_base,
					  pp->io_bus_addr, pp->io_size);

	return ret;
}

static struct pci_ops dw_child_pcie_ops = {
	.map_bus = dw_pcie_other_conf_map_bus,
	.read = dw_pcie_rd_other_conf,
	.write = dw_pcie_wr_other_conf,
};

分析 dw_pcie_other_conf_map_bus() 函数,这个是用来计算访问 EP 配置空间地址。同样也是通过 BDF 计算出 busdev 的偏移,但是它调用了 dw_pcie_prog_outbound_atu(busdev) 做了地址转换,所以它每次返回的转换后的地址都是 pp->va_cfg0_base + where,而 pp->va_cfg0_base 是固定的值,它是由 pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, pp->cfg0_base, pp->cfg0_size); 从设备树里读出来的固定地址空间。

总结

两种 MEM 访问 EP 配置空间的方式都是通过基地址 + 偏移地址访问的。类 xilinx 的方式是全映射,需要 256M 的空间,将所有的 PCIe 设备的配置空间都映射到 CPU 的地址空间来。类 designware 则取巧,使用少量的 CPU 地址空间,但是每次访问之前需要重新映射 EP 的地址,使得相同的 CPU 地址空间可以放到的不同的 EP 设备的配置空间。
两种访问区别

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值