SylixOS PCIe控制器驱动开发(九)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2021年3月28日21:37:28 1 1,285

SylixOS PCIe控制器驱动使用自动配置功能

1. 地址空间简单回顾

自动配置主要就是配置各个设备BAR空间的PCIe地址,所以我们简单回顾下PCIe地址和CPU物理地址的概念。

PCIe控制器和PCIe设备之间通信都是使用的PCIe地址,PCIe地址分为32位和64位,32位只能使用4GB地址空间,64位可以使用超过4GB地址空间。PCIe设备配置空间中BAR寄存器中记录的就是PCIe空间地址,这个地址是Bootloader或者操作系统在启动的过程中分配的。

BAR寄存器中记录的地址其实就是PCIe设备寄存器的基地址,设置好这个基地址后,CPU这时还不能直接去访问,因为CPU访问所使用的是物理地址,而PCIe体系中都是通过PCIe地址来访问的,所以还需要设置PCIe控制器中的地址翻译单元(ATU),将CPU的物理地址和PCIe设备使用的PCIe地址建立映射。大部分情况下,Bootloader或者操作系统都会为这两种地址建立一一映射的关系,所以从数值上来看,这两种地址的值是一样的。在不同的平台中,在CPU的物理地址域中专门有一段空间是留给PCIe控制器建立映射使用的,比如飞腾2000/4平台:

SylixOS PCIe控制器驱动开发(九)

PCIe体系中,地址空间分为三种:配置空间用于访问设备的配置空间寄存器;IO和MEM空间用于访问设备的寄存器。其中IO空间只是为了兼容以前老的PCI设备而保留的,现在大部分PCIe设备都通过MEM方式访问寄存器。

2. Uboot下查看映射关系

一般情况下,PCIe控制器的电源、时钟、地址映射关系等初始化Bootloader都会做好,x86下是BIOS来做的,龙芯下是PMON来做的,飞腾下是Uboot下来做的,设备的枚举和BAR地址分配Bootloader也会做好。

大部分情况下,SylixOS为了简化控制器驱动工作是不再做上述这些工作的,但是在某些情况下可能需要SylixOS重新分配设备的BAR地址,这时就需要使能控制器驱动中的自动配置功能,使用自动配置之前,我们需要知道Bootloader对CPU地址和PCIe地址的映射关系,这个一般只能通过阅读Bootloader源码来了解,但是如果系统使用了Uboot来引导,并且使能了Uboot中PCI相关命令后,可以通过pci region 这个命令来查看UBoot中建立的映射关系:

SylixOS PCIe控制器驱动开发(九)

其中MEM空间根据PCIe标准可以分为支持预取和不支持预取两种,图中的Bus就是指PCIe地址,Phys就是指CPU物理地址。我们可以看出mem和mem prefetch两段空间是一一映射的,IO空间的PCIe地址是从0开始的,其对应的CPU物理地址是从0x50000000开始的,总共15MB。

3. SylixOS使用自动配置

SylixOS下通过PCI_AUTO_CB 这个数据结构来记录自动配置的信息,通过初始化这个成员我们就可以使能自动配置功能:

PCI_AUTO_HANDLE pci_auto_cfg;
pci_auto_cfg = &pci_host.PCI_tAutoConfig;
pci_auto_cfg->PCIAUTO_iConfigEn        = LW_TRUE;
pci_auto_cfg->PCIAUTO_iHostBridegCfgEn = LW_TRUE;
pci_auto_cfg->PCIAUTO_uiFirstBusNo     = 0;
pci_auto_cfg->PCIAUTO_uiLastBusNo      = PCI_MAX_BUS - 1;
pci_auto_cfg->PCIAUTO_uiCurrentBusNo   = 0;
pci_auto_cfg->PCIAUTO_ucCacheLineSize  = PCI_AUTO_CACHE_LINE_SIZE;
pci_auto_cfg->PCIAUTO_ucLatencyTimer   = PCI_AUTO_LATENCY_TIMER;
pci_auto_cfg->PCIAUTO_pfuncDevFixup    = LW_NULL;
pci_auto_cfg->PCIAUTO_pfuncDevIrqFixup = LW_NULL;
pci_auto_cfg->PCIAUTO_pvPriv           = LW_NULL;
  • PCIAUTO_iConfigEn:是否使能自动配置功能。
  • PCIAUTO_iHostBridegCfgEn:是否使能主桥自动配置功能。
  • PCIAUTO_uiFirstBusNo:使用自动配置的起始总线号,一般为0。
  • PCIAUTO_uiLastBusNo:使用自动配置的最后一个总线号,一般为255。
  • PCIAUTO_uiCurrentBusNo:当前总线号,一般为0。
  • PCIAUTO_ucCacheLineSize:PCIe高速缓冲大小,一般设置为PCI_AUTO_CACHE_LINE_SIZE。
  • PCIAUTO_ucLatencyTimer:时间相关参数,一般设置为PCI_AUTO_LATENCY_TIMER。
  • PCIAUTO_pfuncDevFixup:修正回调函数,一般为NULL。
  • PCIAUTO_pfuncDevIrqFixup:中断修正回调函数,一般为NULL。
  • PCIAUTO_pvPriv:私有数据,根据实际需要设置。

通过上面的初始化我们使能了自动配置的功能,除此之外,我们还需要告诉系统各个PCIe空间起始地址和大小,以让系统自动配置时为各个设备的BAR空间分配地址。在SylixOS下,这是通过API_PciAutoCtrlRegionSet 接口来实现的,这个接口的函数原型如下:

LW_API INT API_PciAutoCtrlRegionSet(PCI_CTRL_HANDLE hCtrl,
                                    UINT            uiIndex,
                                    pci_bus_addr_t  addrBusStart,
                                    pci_addr_t      addrPhyStart,
                                    pci_size_t      stSize,
                                    ULONG           ulFlags);
  • hCtrl:PCIe控制器数据结构。
  • uiIndex:各段PCIe地址空间的索引号。
  • addrBusStart:PCIe地址起始值。
  • addrPhyStart:PCIe地址对应的CPU物理地址值,这个成员目前在SylixOS下没有使用。
  • stSize:PCIe空间大小。
  • ulFlags:PCIe空间属性,比如时IO还是MEM空间,支持不支持预取等。

注意:目前SylxiOS不支持处理MEM空间PCIe地址和CPU物理地址不一一映射的情况,PCIe设备驱动通过系统接口只能拿到BAR空间的PCIe地址,如果不是一一映射,访问时还需要手动将PCIe地址转换成物理地址以进行访问。

以飞腾2000/4平台来说,通过Uboot下查看映射关系,我们就可以在SylixOS下如下设置IO空间,PCI_AUTO_REGION_IO 宏标明设置的是IO空间:

#define PCI_IO_REGION_BASE      (0x0)
#define PCI_IO_SIZE             (15 * LW_CFG_MB_SIZE)


API_PciAutoCtrlRegionSet(&pci_host, PCI_AUTO_REGION_INDEX_0,
                         PCI_IO_REGION_BASE, PCI_IO_REGION_BASE, PCI_IO_SIZE,
                         PCI_AUTO_REGION_IO);

同样,不支持预取功能的MEM空间设置如下,PCI_AUTO_REGION_MEM宏标明设置的是非预取MEM空间:

#define PCI_MEM_NONPRE_BASE     (0x0000000058000000)
#define PCI_NONPRE_SIZE         (640 * LW_CFG_MB_SIZE)

API_PciAutoCtrlRegionSet(&pci_host, PCI_AUTO_REGION_INDEX_1,
                         PCI_MEM_NONPRE_BASE, PCI_MEM_NONPRE_BASE, PCI_NONPRE_SIZE,
                         PCI_AUTO_REGION_MEM);

如果需要设置预取MEM空间,则标志上还需加上PCI_AUTO_REGION_PREFETCH这个宏,如下所示:

#define PCI_MEM_PRE_BASE        (0x0000000100000000)
#define PCI_PRE_SIZE            (64 * LW_CFG_GB_SIZE)

API_PciAutoCtrlRegionSet(&pci_host, PCI_AUTO_REGION_INDEX_2,
                         PCI_MEM_PRE_BASE, PCI_MEM_PRE_BASE, PCI_PRE_SIZE,
                         PCI_AUTO_REGION_MEM | PCI_AUTO_REGION_PREFETCH);

最后将要自动配置的空间个数赋值给PCIAUTO_uiRegionCount这个成员:

pci_auto_cfg->PCIAUTO_uiRegionCount = 3;

另外,针对IO空间我们还需要额外做一些设置,因为我们知道IO空间现在是非一一映射的,我们需要在驱动中定义个全局变量__IO_BASE__,并将IO空间的起始物理地址经过MMU映射后的虚拟地址赋值给这个变量:

ioaddr_t __IO_BASE__ = (ioaddr_t)0x50000000ul;

一般在SylixOS启动过程中可以将IO空间的物理地址和虚拟地址一一映射,这样就可以直接将物理地址值赋值给__IO_BASE__变量了

如果PCIe设备使用IO空间对寄存器访问,那么在设备驱动中,我们就必须使用in8、out8这类接口来访问IO空间:

#define in8(a)              read8(__IO_BASE__ + a)
#define in16(a)             read16(__IO_BASE__ + a)
#define in32(a)             read32(__IO_BASE__ + a)
#define in64(a)             read64(__IO_BASE__ + a)

#define out8(d, a)          write8(d, __IO_BASE__ + a)
#define out16(d, a)         write16(d, __IO_BASE__ + a)
#define out32(d, a)         write32(d, __IO_BASE__ + a)
#define out64(d, a)         write64(d, __IO_BASE__ + a)

至此,我们就完成了自动配置的相关设置。

附源码

pci_host_driver_demo5.c源码:

#define __SYLIXOS_KERNEL
#define __SYLIXOS_PCI_DRV
#include <SylixOS.h>
#include <module.h>
#include <system/device/pci/pciBus.h>
#include <system/device/pci/pciMsi.h>
#include <system/device/pci/pciLib.h>

#define PCI_CFG_BASE            (0x0000000040000000)
#define PCI_IO_REGION_BASE      (0x0)
#define PCI_MEM_NONPRE_BASE     (0x0000000058000000)
#define PCI_MEM_PRE_BASE        (0x0000000100000000)

#define PCI_CFG_SIZE            (256 * LW_CFG_MB_SIZE)
#define PCI_IO_SIZE             (15 * LW_CFG_MB_SIZE)
#define PCI_NONPRE_SIZE         (640 * LW_CFG_MB_SIZE)
#define PCI_PRE_SIZE            (64 * LW_CFG_GB_SIZE)

ioaddr_t __IO_BASE__ = (ioaddr_t)0x50000000ul;

static PCI_CTRL_CB pci_host;

static int pci_cfg_read (int bus, int dev, int func,
                         int offset, int line, void *data)
{
    return 0;
}

static int pci_cfg_write (int bus, int dev, int func,
                          int offset, int line, unsigned int data)
{
    return 0;
}

static int pci_vector_get (int bus, int dev, int func,
                           int msi_en, int line, int pin,
                           void *vector)
{
    PCI_MSI_DESC *msi_desc;

    if (msi_en) {
        msi_desc = (PCI_MSI_DESC *)vector;

       /*
        *  根据不同平台进行设置,这里以mpsoc平台为例
        *  msi_desc->PCIMSI_pmmMsg.uiAddressHi = 0;
        *  msi_desc->PCIMSI_pmmMsg.uiAddressLo = 0xFE440000;
        *  msi_desc->PCIMSI_pmmMsg.uiData      = 0 & 0x1f;
        *  msi_desc->PCIMSI_ulDevIrqVector     = 146;
        */
    } else {
        /*
         *  根据不同平台上的中断路由寻找INTx实际对应的中断向量号
         */
        switch (pin) {
        case 1:
            break;

        case 2:
            break;

        case 3:
            break;

        case 4:
            break;

        default:
            return -1;
        }
    }

    return 0;
}

static PCI_DRV_FUNCS0  pci_driver_funcs = {
    .cfgRead  = pci_cfg_read,
    .cfgWrite = pci_cfg_write,
    .irqGet   = pci_vector_get,
};

int pci_host_probe (void)
{
    pci_host.PCI_iIndex       = 0;
    pci_host.PCI_iBusMax      = PCI_MAX_BUS;
    pci_host.PCI_pDrvFuncs0   = &pci_driver_funcs;
    pci_host.PCI_ucMechanism  = PCI_MECHANISM_0;
    pci_host.PCI_pvPriv       = LW_NULL;

    PCI_AUTO_HANDLE pci_auto_cfg;
    pci_auto_cfg = &pci_host.PCI_tAutoConfig;
    pci_auto_cfg->PCIAUTO_iConfigEn        = LW_TRUE;
    pci_auto_cfg->PCIAUTO_iHostBridegCfgEn = LW_TRUE;
    pci_auto_cfg->PCIAUTO_uiFirstBusNo     = 0;
    pci_auto_cfg->PCIAUTO_uiLastBusNo      = PCI_MAX_BUS - 1;
    pci_auto_cfg->PCIAUTO_uiCurrentBusNo   = 0;
    pci_auto_cfg->PCIAUTO_ucCacheLineSize  = PCI_AUTO_CACHE_LINE_SIZE;
    pci_auto_cfg->PCIAUTO_ucLatencyTimer   = PCI_AUTO_LATENCY_TIMER;
    pci_auto_cfg->PCIAUTO_pfuncDevFixup    = LW_NULL;
    pci_auto_cfg->PCIAUTO_pfuncDevIrqFixup = LW_NULL;
    pci_auto_cfg->PCIAUTO_pvPriv           = LW_NULL;

    API_PciAutoCtrlRegionSet(&pci_host, PCI_AUTO_REGION_INDEX_0,
                             PCI_IO_REGION_BASE, PCI_IO_REGION_BASE, PCI_IO_SIZE,
                             PCI_AUTO_REGION_IO);
    API_PciAutoCtrlRegionSet(&pci_host, PCI_AUTO_REGION_INDEX_1,
                             PCI_MEM_NONPRE_BASE, PCI_MEM_NONPRE_BASE, PCI_NONPRE_SIZE,
                             PCI_AUTO_REGION_MEM);
    API_PciAutoCtrlRegionSet(&pci_host, PCI_AUTO_REGION_INDEX_2,
                             PCI_MEM_PRE_BASE, PCI_MEM_PRE_BASE, PCI_PRE_SIZE,
                             PCI_AUTO_REGION_MEM | PCI_AUTO_REGION_PREFETCH);
    pci_auto_cfg->PCIAUTO_uiRegionCount = 3;

    API_PciCtrlCreate(&pci_host);

    return 0;
}

int module_init (void)
{
    pci_host_probe();

    return 0;
}

void module_exit (void)
{
}

 

 

 

 

gewenbin
  • 本文由 发表于 2021年3月28日21:37:28
  • 转载请务必保留本文链接:http://www.databusworld.cn/10050.html
匿名

发表评论

匿名网友 填写信息

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

评论:1   其中:访客  1   博主  0
    • lijiajie lijiajie 1

      你明天能不能调休?