1. How To Write Linux PCI Drivers — The Linux Kernel documentation
PCI LIB相关API
PCI Support Library — The Linux Kernel documentation
任何一个PCIe EP的功能都分成两部分,其中一部分是PCIe功能,另外一部门是自己的域内的功能(比如网卡、FC、SAS、NVMe、GPU),其中PCIe域内的功能的初始化都是套路。主要涉及下面几个方面:
1、准备一个数据static struct pci_driver xxx_driver,该数据结构主要包括:
struct pci_driver定义见
pci.h - include/linux/pci.h - Linux source code (v5.15.1) - Bootlin
(1)const struct pci_device_id(存放EP的device和vendorid)
(2)int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); EP的probe函数
(3)void (*remove)(struct pci_dev *dev); EP的remove函数
(4)int (*suspend)(struct pci_dev *dev, pm_message_t state); EP进入低功耗的suspend函数
(5)int (*resume)(struct pci_dev *dev); EP从低功耗唤醒的resume函数
(6)void (*shutdown)(struct pci_dev *dev); reboot时调用的EP的shutdown函数
(7)const struct pci_error_handlers *err_handler 错误处理相关函数
(8)struct device_driver driver.pm power management相关的OPS
其中(1)、(2)、(3)是必须的。
struct pci_driver {
struct list_head node;
const char *name;
const struct pci_device_id *id_table; /* Must be non-NULL for probe to be called */
int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend)(struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*resume)(struct pci_dev *dev); /* Device woken up */
void (*shutdown)(struct pci_dev *dev);
int (*sriov_configure)(struct pci_dev *dev, int num_vfs); /* On PF */
int (*sriov_set_msix_vec_count)(struct pci_dev *vf, int msix_vec_count); /* On PF */
u32 (*sriov_get_vf_total_msix)(struct pci_dev *pf);
const struct pci_error_handlers *err_handler;
const struct attribute_group **groups;
const struct attribute_group **dev_groups;
struct device_driver driver;
struct pci_dynids dynids;
};
2、init函数调用pci_regiser_driver把static struct pci_driver xxx_driver注册给PCIe EP driver框架
3、exit函数调用pci_unregiser_driver把static struct pci_driver xxx_driver从PCIe EP driver框架卸载
4、在probe函数调用下面函数,使能PCIe相关功能
(1)pci_set_drvdata:设置私有数据
(2)pci_enable_device:写command reg使能memory和io方式访问,wake up设备。
(3)pci_request_regions:使用owner name来mark IO和memory资源。
(4)pci_set_master:写command reg使能master功能,使得EP可以发起memory方式请求。
(5)dma_set_mask_and_coherent:设置DMA mask和coherent mask
(6)pci_iomap:BAR地址ioremp成虚拟地址
(7)pci_alloc_irq_vector:申请 multiple IRQs,并使能对应的中断
->pci_enable_msix_range:使能MSI-X功能(如果支持MSI-X中断)
- >pci_enable_msi_range:使能MSI功能(如果支持MSI中断)
(8)request_irq申请中断
(9)pci_enable_pcie_error_reporting:写device ctrl reg使能PCIe错误上报
linux kernel 6.6.0之后把pci_disable_pcie_error_reporting删除,并且不在export pci_enable_pcie_error_reporting,这将导致EP drv无法直接调用pci_enable_pcie_error_reporting来enable错误上报功能,需要在EP drv里面实现对应功能
[PATCH 0/2] PCI/AER: Remove/unexport error reporting enable/disable
(10)pci_save_state保存配置空间
注意:上面的set和enable在失败时都有对应的unset和disable函数,需要调用。
5、在remove函数按照相反顺序把PCIe相关功能disbale。