访问 EP 的配置空间方法
- 内存映射
- IO 访问
内存访问配置空间
-
前置知识
PCIe 设备的寻址是按照 BDF 即 Bus-Device-Function 来组织的。访问某个设备则需要根据BDF计算偏移地址。 -
两种不同的内存访问配置空间方法
- 类 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, ®s);
从设备树获取的基地址,然后加上 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;elsetype = 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 设备的配置空间。