SylixOS PCIe设备驱动中断处理
现在的PCIe设备要想正常工作基本都会用到中断功能,在SylixOS下要想使用设备中断功能,必须注册设备的中断处理函数并使能中断。而PCIe的中断又分为INTx、MSI和MSI-X三种,本章节我们主要讲解如何在驱动中使用INTx和MSI中断,MSI-X中断和MSI类似,具体使用方法请参考内核的PCIe设备驱动程序。
我们先来看看INTx中断如何使用,首先需要获得设备的INTx中断号,跟上一章节获取MEM、IO空间资源一样,INTx中断也是一种资源信息,同样可以通过API_PciDevResourceGet这个接口获取:
unsigned long vector; /* * 设备使用INTx中断 */ pci_resrc = API_PciDevResourceGet(pci_dev, PCI_IORESOURCE_IRQ, 0); vector = (unsigned long)(PCI_RESOURCE_START(pci_resrc));
拿到设备的INTx中断号后,我们就可以使用API_PciDevInterConnect接口来注册中断处理函数了:
API_PciDevInterConnect(pci_dev, vector, pci_driver_isr, LW_NULL, "pci_demo_isr");
- pci_dev为SylixOS下PCIe设备的数据结构指针。
- vector:中断号。
- pci_driver_isr:中断处理函数。
- LW_NULL:中断处理函数入参,如果没有填NULL。
- "pci_demo_isr":用于标识这个中断的名字,可以使用ints 命令查看系统中所有的中断信息。
注册好中断处理函数后,就可以使能中断了,这是通过API_PciDevInterEnable 接口来实现的:
API_PciDevInterEnable(pci_dev, vector, pci_driver_isr, LW_NULL);
由于不同的PCIe设备可能共享同一个中断号,所以在设备的中断处理函数中必须先读取设备中断状态来判断是不是当前设备产生了中断,如果不是需要直接返回以让内核继续调用真正产生中断设备的中断处理程序:
static irqreturn_t pci_driver_isr (void *arg, unsigned long vector) { int int_status = 0; /* * 检查是否是本设备产生了中断 */ if (!int_status) { return (LW_IRQ_NONE); } return (LW_IRQ_HANDLED); }
中断处理中不是当前设备中断需要返回LW_IRQ_NONE,如果中断正常处理完毕则返回LW_IRQ_HANDLED。
在PCIe标准中规定设备必须支持MSI或者MSI-X中断,INTx中断可以支持也可以不支持,所以如果一个PCIe设备可能只支持MSI中断而不支持其他中断,这时中断号的获取流程和INTx是不一样的。
使用MSI中断之前首先需要使能设备的MSI中断,这是通过API_PciDevMsiEnableSet 接口来实现:
API_PciDevMsiEnableSet(pci_dev, LW_TRUE);
LW_TRUE表示使能,LW_FALSE表示禁止。MSI中断支持申请多个向量号,这是通过API_PciDevMsiRangeEnable 接口来实现的:
API_PciDevMsiRangeEnable(pci_dev, 1, 1);
第一个1表示起始向量号,第二个1表示要申请的个数。申请成功后,则通过如下方法获取到MSI起始向量号对应的中断:
vector = pci_dev->PCIDEV_ulDevIrqVector;
PCIDEV_ulDevIrqVector这个成员其实就表示设备的INTx或者MSI中断号,当没使能MSI之前,这个成员就表示INTx中断号,当使能MSI中断并成功申请MSI向量之后,这个成员会被更新为MSI中断号,接下来注册和使能中断号对应的中断就和INTx下处理一样了,不再赘述。
当驱动卸载时,通过以下接口关闭中断并卸载中断处理函数:
API_PciDevInterDisableEx(pci_dev, vector, pci_driver_isr, LW_NULL, 1); API_PciDevInterDisconnect(pci_dev, vector, pci_driver_isr, LW_NULL);
附源码
pci_driver_demo5.c源码:
#define __SYLIXOS_KERNEL #define __SYLIXOS_PCI_DRV #include <SylixOS.h> #include <module.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <linux/compat.h> #include <system/device/pci/pciBus.h> #include <system/device/pci/pciMsi.h> #include <system/device/pci/pciLib.h> #include <system/device/pci/pciIds.h> #include <system/device/pci/pciDev.h> #define PCI_DRV_NAME "pci_demo_drv" void *vaddr; unsigned long vector; static PCI_DEV_ID_CB pci_devices[] = { {PCI_DEVICE(0x1013, 0x00b8)}, {} }; static irqreturn_t pci_driver_isr (void *arg, unsigned long vector) { int int_status = 0; /* * 检查是否是本设备产生了中断 */ if (!int_status) { return (LW_IRQ_NONE); } return (LW_IRQ_HANDLED); } static int pci_driver_probe (PCI_DEV_HANDLE pci_dev, const PCI_DEV_ID_HANDLE id) { PCI_RESOURCE_HANDLE pci_resrc; phys_addr_t reg_base; size_t size; printk("probe: bus %d, dev %d, func %d.\r\n", pci_dev->PCIDEV_iDevBus, pci_dev->PCIDEV_iDevDevice, pci_dev->PCIDEV_iDevFunction); /* * 获取MEM空间信息 */ pci_resrc = API_PciDevResourceGet(pci_dev, PCI_IORESOURCE_MEM, 0); size = (size_t)(PCI_RESOURCE_SIZE(pci_resrc)); reg_base = (phys_addr_t)(PCI_RESOURCE_START(pci_resrc)); vaddr = API_PciDevIoRemap((void *)reg_base, size); /* * 使用readl/writel这类接口来访问设备MEM寄存器 * int value = readl(vaddr + MME_REG_OFFSET); * writel(value, vaddr + MEM_REG_OFFSET); */ /* * 获取IO空间信息 */ pci_resrc = API_PciDevResourceGet(pci_dev, PCI_IORESOURCE_IO, 0); size = (size_t)(PCI_RESOURCE_SIZE(pci_resrc)); reg_base = (phys_addr_t)(PCI_RESOURCE_START(pci_resrc)); /* * 使用in32/out32这类接口来访问设备IO寄存器 * int value = in32(reg_base + IO_REG_OFFSET); * out32(value, reg_base + IO_REG_OFFSET); */ /* * 设备使用INTx中断 */ pci_resrc = API_PciDevResourceGet(pci_dev, PCI_IORESOURCE_IRQ, 0); vector = (unsigned long)(PCI_RESOURCE_START(pci_resrc)); /* * 设备使用MSI中断 */ API_PciDevMsiEnableSet(pci_dev, LW_TRUE); API_PciDevMsiRangeEnable(pci_dev, 1, 1); vector = pci_dev->PCIDEV_ulDevIrqVector; /* * 注册中断处理函数并使能 */ API_PciDevInterConnect(pci_dev, vector, pci_driver_isr, LW_NULL, "pci_demo_isr"); API_PciDevInterEnable(pci_dev, vector, pci_driver_isr, LW_NULL); return 0; } static void pci_driver_remove (PCI_DEV_HANDLE pci_dev) { printk("remove: bus %d, dev %d, func %d.\r\n", pci_dev->PCIDEV_iDevBus, pci_dev->PCIDEV_iDevDevice, pci_dev->PCIDEV_iDevFunction); API_PciDevInterDisableEx(pci_dev, vector, pci_driver_isr, LW_NULL, 1); API_PciDevInterDisconnect(pci_dev, vector, pci_driver_isr, LW_NULL); API_PciDevIoUnmap(vaddr); } static PCI_DRV_CB pci_driver = { .PCIDRV_hDrvIdTable = (PCI_DEV_ID_HANDLE)pci_devices, .PCIDRV_uiDrvIdTableSize = sizeof(pci_devices) / sizeof(PCI_DEV_ID_CB), .PCIDRV_pfuncDevProbe = pci_driver_probe, .PCIDRV_pfuncDevRemove = pci_driver_remove, }; int module_init (void) { strlcpy(pci_driver.PCIDRV_cDrvName, PCI_DRV_NAME, PCI_DRV_NAME_MAX); API_PciDrvRegister(&pci_driver); return 0; } void module_exit (void) { PCI_DRV_HANDLE pci_driver; pci_driver = API_PciDrvHandleGet(PCI_DRV_NAME); API_PciDrvUnregister(pci_driver); }
评论