本文档是关于如何使用 PCI 端点框架来创建端点控制器驱动程序、端点功能驱动程序以及使用 configfs 接口将功能驱动程序绑定到控制器驱动程序的指南。
9.1. 简介¶
Linux 具有全面的 PCI 子系统,以支持在根复合体模式下运行的 PCI 控制器。该子系统具有扫描 PCI 总线、分配内存资源和 IRQ 资源、加载 PCI 驱动程序(基于供应商 ID、设备 ID)、支持热插拔、电源管理、高级错误报告和虚拟通道等其他服务的能力。
然而,某些 SoC 中集成的 PCI 控制器 IP 能够以根复合体模式或端点模式运行。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 设备。
devm_pci_epc_destroy()/pci_epc_destroy()
PCI 控制器驱动程序可以使用 devm_pci_epc_destroy() 或 pci_epc_destroy() 销毁由 devm_pci_epc_create() 或 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() 来配置基址寄存器,以便主机分配 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。这些用于将 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。这些用于在 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()。