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。 文件includelinuxPci.h中对它们进行了声明。
代码语言:javascript复制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);
文件driverspciAccess.c中,通过调用pci_bus_read_config_xxx 和 pci_bus_write_config_xxx, 定义了上述函数。
代码语言:javascript复制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是更底层的函数。它们也在文件includelinuxPci.h中被声明。
代码语言:javascript复制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”实现。
代码语言:javascript复制#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。
代码语言:javascript复制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函数。
代码语言:javascript复制/* 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。
代码语言:javascript复制/* 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在driverspciaccess.c中定义。
代码语言:javascript复制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()函数。它根据总线号、设备号,得到一个寄存器地址。通过这个寄存器地址操作,就能对设备的发起配置寄存器的读写操作。
代码语言:javascript复制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