本文档是使用 PCI 端点框架(PCI Endpoint Framework)创建端点控制器驱动、端点功能驱动以及使用 configfs 接口将功能驱动绑定到控制器驱动的指南。
9.1. 简介¶
Linux 拥有一个全面的 PCI 子系统,以支持在 Root Complex 模式下运行的 PCI 控制器。该子系统能够扫描 PCI 总线、分配内存资源和 IRQ 资源、加载 PCI 驱动(基于供应商 ID、设备 ID)、支持热插拔、电源管理、高级错误报告和虚拟通道等其他服务。
然而,某些 SoC 中集成的 PCI 控制器 IP 能够以 Root Complex 模式或 Endpoint 模式运行。PCI 端点框架将在 Linux 中添加端点模式支持。这将有助于在 EP 系统中运行 Linux,该系统可用于从测试或验证到协处理器加速等各种用例。
9.2. PCI 端点核心¶
PCI 端点核心层包含 3 个组件:端点控制器库、端点功能库以及用于将端点功能与端点控制器绑定的 configfs 层。
9.2.1. PCI 端点控制器 (EPC) 库¶
EPC 库提供了供可在端点模式下运行的控制器使用的 API。它还提供了供功能驱动/库使用的 API,以实现特定的端点功能。
9.2.1.1. PCI 控制器驱动的 API¶
本节列出了 PCI 端点核心提供给 PCI 控制器驱动使用的 API。
devm_pci_epc_create()/pci_epc_create()
PCI 控制器驱动应实现以下操作:
write_header: 填充配置空间头部的操作
set_bar: 配置 BAR 的操作
clear_bar: 重置 BAR 的操作
alloc_addr_space: 在 PCI 控制器地址空间中分配的操作
free_addr_space: 释放已分配地址空间的操作
raise_irq: 触发传统、MSI 或 MSI-X 中断的操作
start: 启动 PCI 链接的操作
stop: 停止 PCI 链接的操作
PCI 控制器驱动可以通过调用 devm_pci_epc_create()/pci_epc_create() 创建新的 EPC 设备。
pci_epc_destroy()
PCI 控制器驱动可以使用 pci_epc_destroy() 销毁由 pci_epc_create() 创建的 EPC 设备。
pci_epc_linkup()
为了通知所有功能设备,它们所链接的 EPC 设备已与主机建立连接,PCI 控制器驱动应调用 pci_epc_linkup()。
pci_epc_mem_init()
初始化用于分配 EPC 地址空间的 pci_epc_mem 结构。
pci_epc_mem_exit()
清除在 pci_epc_mem_init() 期间分配的 pci_epc_mem 结构。
9.2.1.2. PCI 端点功能驱动的 EPC API¶
本节列出了 PCI 端点核心提供给 PCI 端点功能驱动使用的 API。
pci_epc_write_header()
PCI 端点功能驱动应使用 pci_epc_write_header() 将标准配置头部写入端点控制器。
pci_epc_set_bar()
PCI 端点功能驱动应使用 pci_epc_set_bar() 配置基地址寄存器 (Base Address Register),以便主机分配 PCI 地址空间。功能驱动的寄存器空间通常使用此 API 进行配置。
pci_epc_clear_bar()
PCI 端点功能驱动应使用 pci_epc_clear_bar() 重置 BAR。
pci_epc_raise_irq()
PCI 端点功能驱动应使用 pci_epc_raise_irq() 触发传统中断、MSI 或 MSI-X 中断。
pci_epc_mem_alloc_addr()
PCI 端点功能驱动应使用 pci_epc_mem_alloc_addr(),从 EPC 地址空间分配访问 RC 缓冲区所需的内存地址。
pci_epc_mem_free_addr()
PCI 端点功能驱动应使用 pci_epc_mem_free_addr() 释放使用 pci_epc_mem_alloc_addr() 分配的内存空间。
pci_epc_map_addr()
PCI 端点功能驱动应使用 pci_epc_map_addr() 将通过 pci_epc_mem_alloc_addr() 获取的本地内存的 CPU 地址映射到 RC PCI 地址。
pci_epc_unmap_addr()
PCI 端点功能驱动应使用 pci_epc_unmap_addr() 解除通过 pci_epc_map_addr() 映射到 RC 地址的本地内存的 CPU 地址。
pci_epc_mem_map()
PCI 端点控制器可能会对可映射的 RC PCI 地址施加限制。pci_epc_mem_map() 函数允许端点功能驱动在处理此类限制的同时分配和映射控制器内存。此函数将确定必须使用 pci_epc_mem_alloc_addr() 分配的内存大小,以成功映射 RC PCI 地址范围。此函数还将指示实际映射的 PCI 地址范围大小(可能小于请求的大小),以及在分配内存中用于访问映射的 RC PCI 地址范围的偏移量。
pci_epc_mem_unmap()
PCI 端点功能驱动可以使用 pci_epc_mem_unmap() 解除映射并释放使用 pci_epc_mem_map() 分配和映射的控制器内存。
9.2.1.3. 其他 EPC API¶
EPC 库还提供了其他 API。这些 API 用于将 EPF 设备与 EPC 设备绑定。pci-ep-cfs.c 可用作使用这些 API 的参考。
pci_epc_get()
根据控制器的设备名称获取 PCI 端点控制器的引用。
pci_epc_put()
释放使用 pci_epc_get() 获取的 PCI 端点控制器的引用
pci_epc_add_epf()
将 PCI 端点功能添加到 PCI 端点控制器。根据规范,一个 PCIe 设备最多可以有 8 个功能。
pci_epc_remove_epf()
从 PCI 端点控制器中移除 PCI 端点功能。
pci_epc_start()
PCI 端点功能驱动在配置完端点功能并希望启动 PCI 链接后,应调用 pci_epc_start()。
pci_epc_stop()
PCI 端点功能驱动应调用 pci_epc_stop() 来停止 PCI 链接。
9.2.2. PCI 端点功能 (EPF) 库¶
EPF 库提供了供功能驱动和 EPC 库用于提供端点模式功能的 API。
9.2.2.1. PCI 端点功能驱动的 EPF API¶
本节列出了 PCI 端点核心提供给 PCI 端点功能驱动使用的 API。
pci_epf_register_driver()
- PCI 端点功能驱动应实现以下操作:
bind: 当 EPC 设备绑定到 EPF 设备时执行的操作
unbind: 当 EPC 设备和 EPF 设备之间的绑定丢失时执行的操作
add_cfs: 可选操作,用于创建功能特定的 configfs 属性
PCI 功能驱动可以使用 pci_epf_register_driver() 注册 PCI EPF 驱动。
pci_epf_unregister_driver()
PCI 功能驱动可以使用 pci_epf_unregister_driver() 注销 PCI EPF 驱动。
pci_epf_alloc_space()
PCI 功能驱动可以使用 pci_epf_alloc_space() 为特定 BAR 分配空间。
pci_epf_free_space()
PCI 功能驱动可以通过调用 pci_epf_free_space() 释放已分配的空间(使用 pci_epf_alloc_space 分配)。
9.2.2.2. PCI 端点控制器库的 API¶
本节列出了 PCI 端点核心提供给 PCI 端点控制器库使用的 API。
pci_epf_linkup()
当 EPC 设备与主机建立连接时,PCI 端点控制器库会调用 pci_epf_linkup()。
9.2.2.3. 其他 EPF API¶
EPF 库还提供了其他 API。这些 API 用于在 EPF 设备绑定到 EPC 设备时通知功能驱动。pci-ep-cfs.c 可用作使用这些 API 的参考。
pci_epf_create()
通过传递 PCI EPF 设备的名称来创建新的 PCI EPF 设备。此名称将用于将 EPF 设备绑定到 EPF 驱动。
pci_epf_destroy()
销毁已创建的 PCI EPF 设备。
pci_epf_bind()
当 EPF 设备绑定到 EPC 设备时,应调用 pci_epf_bind()。
pci_epf_unbind()
当 EPC 设备和 EPF 设备之间的绑定丢失时,应调用 pci_epf_unbind()。