SylixOS PCIe设备驱动开发(十四)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2021年3月30日16:03:46 评论 819

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);
}

 

gewenbin
  • 本文由 发表于 2021年3月30日16:03:46
  • 转载请务必保留本文链接:http://www.databusworld.cn/10084.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: