前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AMD Xilinx PCIe Host 配置空间访问流程

AMD Xilinx PCIe Host 配置空间访问流程

作者头像
hankfu
发布2024-08-20 09:08:51
860
发布2024-08-20 09:08:51
举报
文章被收录于专栏:hank

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中对它们进行了声明。

代码语言: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);

文件drivers\pci\Access.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是更底层的函数。它们也在文件include\linux\Pci.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在drivers\pci\access.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 配置空间访问过程中,相关的函数如下:

  1. pci_read_config_xxx 和 pci_write_config_xxx 函数
  2. pci_bus_read_config_xxx 和 pci_bus_write_config_xxx 函数
  3. static struct pci_ops xilinx_pcie_ops
  4. xilinx_pcie_map_bus
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • pci_read_config_xxx 和 pci_write_config_xxx 函数定义
  • pci_bus_read_config_xxx 和 pci_bus_write_config_xxx 函数定义
  • pcie-xdma-pl.c 中的pci_ops
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档