SylixOS PCIe设备驱动获取BAR空间信息
通过前面的学习我们知道了如何编写一个简单PCIe设备驱动的基本框架,但是我们还需要知道这个设备的寄存器基址,中断号等才能进一步编写驱动硬件的代码。我们知道PCIe设备寄存器可以在IO或者MEM空间中,那么如何让获得这两个空间的信息呢?
在SylixOS中,使用PCI_RESOURCE_CB 数据结构表示PCIe设备的资源信息,像MEM、IO、中断号等都可以看作是设备的资源信息:
typedef struct { pci_resource_size_t PCIRS_stStart; pci_resource_size_t PCIRS_stEnd; PCHAR PCIRS_pcName; ULONG PCIRS_ulFlags; ULONG PCIRS_ulDesc; } PCI_RESOURCE_CB; typedef PCI_RESOURCE_CB *PCI_RESOURCE_HANDLE;
我们来看看其中几个比较重要的成员:
- PCIRS_stStart:资源的起始值,对于MEM、IO空间而言,这就是其PCIe起始地址,而一般情况下,PCIe地址和CPU物理地址是一一映射的,所以可以认为这个值就是MEM和IO空间寄存器的物理基址。对于中断资源而言,这个值就是设备使用的INTx中断号。
- PCIRS_stEnd:资源的结尾值,对于MEM、IO空间而言,这个值减去上面的起始值就表示了寄存器区域的大小。对于中断而言,这个值和上面的值一样,表示设备INTx中断号。
- PCIRS_ulFlags:资源的属性标志。
在驱动中可以使用API_PciDevResourceGet接口来获得某一种类型资源的信息,我们来看下这个接口原型:
PCI_RESOURCE_HANDLE API_PciDevResourceGet(PCI_DEV_HANDLE hDevHandle, UINT uiType, UINT uiNum);
- hDevHandle:SylixOS下PCIe设备数据结构指针。
- uiType:资源类型,比如PCI_IORESOURCE_MEM、PCI_IORESOURCE_IO、PCI_IORESOURCE_IRQ等。
- uiNum:资源索引,比如有的设备BAR空间有2个BAR都是MEM类型的,那么用uiNum来表示想要获取哪个MEM空间的资源。
- 这个接口如果成功则返回对应的资源信息。
假设我们想获取一个设备第一个MEM空间资源,则方法如下:
PCI_RESOURCE_HANDLE pci_resrc; pci_resrc = API_PciDevResourceGet(pci_dev, PCI_IORESOURCE_MEM, 0);
获取了资源信息后,我们可以使用PCI_RESOURCE_START这个宏来取出资源中的起始值:
phys_addr_t reg_base; reg_base = (phys_addr_t)(PCI_RESOURCE_START(pci_resrc));
对于MEM空间而言,这个值就是寄存器基址。同样的我们可以通过PCI_RESOURCE_SIZE宏来获得这段空间的大小:
size_t size; size = (size_t)(PCI_RESOURCE_SIZE(pci_resrc));
我们现在拿到的只是寄存器区域的物理地址和大小,驱动要想操作必须再映射为虚拟地址进行访问,可以使用API_PciDevIoRemap接口来映射寄存器空间:
void *vaddr; vaddr = API_PciDevIoRemap((void *)reg_base, size);
MEM空间映射完之后,我们就可以使用read/write类接口来读写寄存器了,通过寄存器偏移地址就能访问某个寄存器:
使用readl/writel这类接口来访问设备MEM寄存器 int value = readl(vaddr + MME_REG_OFFSET); writel(value, vaddr + MEM_REG_OFFSET);
驱动卸载时需要释放MEM空间映射的虚拟地址,这是通过API_PciDevIoUnmap 接口来实现的:
API_PciDevIoUnmap(vaddr);
IO空间的使用方式和MEM空间类似,首先获取设备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));
但是访问IO空间并不是使用read/write类接口,而是使用in/out类接口,同样通过IO寄存器偏移地址来访问:
使用in32/out32这类接口来访问设备IO寄存器 int value = in32(reg_base + IO_REG_OFFSET); out32(value, reg_base + IO_REG_OFFSET);
注意:设备驱动中要想通过in/out类接口访问IO寄存器,需要控制器驱动中提前映射IO空间物理地址并设置好访问地址,具体请见控制器驱动开发相关章节。
附源码
pci_driver_demo4.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; static PCI_DEV_ID_CB pci_devices[] = { {PCI_DEVICE(0x1013, 0x00b8)}, {} }; 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); */ 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_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); }
评论