作者:付汉杰 hankf@amd.com
AMD Xilinx的Versal器件中的PCIe IP,也可以作为PCIe Host。 AR76647 提供了相关驱动。 Xilinx Linux PL PCIe Root Port 提供了配置和测试过程。
最近研究了Linux下,AMD Xilinx PCIe Host 配置空间访问流程。
pci_read_config_xxx 和 pci_write_config_xxx 函数定义
首先,Linux通用的PCI代码,需要PCI配置空间的访问函数 pci_read_config_xxx 和 pci_write_config_xxx。 文件include\linux\Pci.h中对它们进行了声明。
int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val); int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val); int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val); int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val); int pci_write_config_word(const struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val);
文件drivers\pci\Access.c中,通过调用pci_bus_read_config_xxx 和 pci_bus_write_config_xxx, 定义了上述函数。
int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val) { if (pci_dev_is_disconnected(dev)) { *val = ~0; return PCIBIOS_DEVICE_NOT_FOUND; } return pci_bus_read_config_byte(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_read_config_byte); int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val) { if (pci_dev_is_disconnected(dev)) { *val = ~0; return PCIBIOS_DEVICE_NOT_FOUND; } return pci_bus_read_config_word(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_read_config_word); int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val) { if (pci_dev_is_disconnected(dev)) { *val = ~0; return PCIBIOS_DEVICE_NOT_FOUND; } return pci_bus_read_config_dword(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_read_config_dword); int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val) { if (pci_dev_is_disconnected(dev)) return PCIBIOS_DEVICE_NOT_FOUND; return pci_bus_write_config_byte(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_write_config_byte); int pci_write_config_word(const struct pci_dev *dev, int where, u16 val) { if (pci_dev_is_disconnected(dev)) return PCIBIOS_DEVICE_NOT_FOUND; return pci_bus_write_config_word(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_write_config_word); int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val) { if (pci_dev_is_disconnected(dev)) return PCIBIOS_DEVICE_NOT_FOUND; return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_write_config_dword);
pci_bus_read_config_xxx 和 pci_bus_write_config_xxx 函数定义
pci_bus_read_config_xxx 和 pci_bus_write_config_xxx是更底层的函数。它们也在文件include\linux\Pci.h中被声明。
int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 *val); int pci_bus_read_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 *val); int pci_bus_read_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 *val); int pci_bus_write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 val); int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 val); int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val);
pci_bus_read_config_xxx 和 pci_bus_write_config_xxx的代码通过宏“PCI_OP_READ”和宏“PCI_OP_WRITE”实现。
#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”和宏“PCI_OP_WRITE”,定义了pci_bus_read_config_xxx 和 pci_bus_write_config_xxx。
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);
通过宏“PCI_OP_READ”和宏“PCI_OP_WRITE”,也可以看出,实现pci_bus_read_config_xxx 和 pci_bus_write_config_xxx,需要数据结构“struct pci_bus”中嵌套的数据结构“struct pci_ops”中的函数read和write函数。
/* Low-level architecture-dependent routines */ struct pci_ops { int (*add_bus)(struct pci_bus *bus); void (*remove_bus)(struct pci_bus *bus); void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where); int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); };
pcie-xdma-pl.c 中的pci_ops
pcie-xdma-pl.c定义了pci_ops。
/* PCIe operations */ static struct pci_ops xilinx_pcie_ops = { .map_bus = xilinx_pcie_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, };
pci_generic_config_read和pci_generic_config_write在drivers\pci\access.c中定义。
int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { void __iomem *addr; addr = bus->ops->map_bus(bus, devfn, where); if (!addr) { *val = ~0; return PCIBIOS_DEVICE_NOT_FOUND; } if (size == 1) *val = readb(addr); else if (size == 2) *val = readw(addr); else *val = readl(addr); return PCIBIOS_SUCCESSFUL; } EXPORT_SYMBOL_GPL(pci_generic_config_read); int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { void __iomem *addr; addr = bus->ops->map_bus(bus, devfn, where); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; if (size == 1) writeb(val, addr); else if (size == 2) writew(val, addr); else writel(val, addr); return PCIBIOS_SUCCESSFUL; } EXPORT_SYMBOL_GPL(pci_generic_config_write);
可以看到,它们调用了“struct pci_bus *bus”中的map_bus()函数。
pcie-xdma-pl.c 也实现了 map_bus()函数。它根据总线号、设备号,得到一个寄存器地址。通过这个寄存器地址操作,就能对设备的发起配置寄存器的读写操作。
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; }
这样,PCIe Host 配置空间访问流程中,与设备相关的代码就齐全了。
总结
从上到下,PCIe Host 配置空间访问过程中,相关的函数如下:
pci_read_config_xxx 和 pci_write_config_xxx 函数
pci_bus_read_config_xxx 和 pci_bus_write_config_xxx 函数
static struct pci_ops xilinx_pcie_ops
xilinx_pcie_map_bus
文章来源:博客园