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

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2021年3月28日17:08:44 1 1,203

SylixOS申请PCIe设备中断

本来这篇文章是想和读写配置空间放一起讲的,但是梳理了下发现PCIe的中断方面的知识还是比较重要的,因为现在一般的PCIe设备都需要配合中断来进行工作,而PCIe中断又分为INTx中断和MSI中断,如果使用或者设置不当的话,很可能造成设备无法正常使用,所以把中断单独拿出来用一个章节讲解。

1. PCIe中断简单回顾

我们首先来简单回顾下PCIe中断方面的知识,PCIe体系中的中断继承自PCI,分为INTx中断和MSI中断。INTx中断共有四种:INTA、INTB、INTC、INTD,大部分的设备都只使用了INTA作为中断引脚。为了平衡系统中所有设备中断请求负载,会将不同设备的INTx引脚相连到中断控制器的同一个IRQ引脚:

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

INTx中断是低电平有效,PCIe设备还可以使用MSI和MSI-X方式产生中断,MSI中断本质上就是PCIe设备向某个地址提交一个数据写请求,然后就会触发中断控制器处理这个中断并通知CPU来处理。配置空间中有一段寄存器就是给MSI中断使用的,共有4种格式,我们来看最基础的一种:

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

其中Message Control区域中有一个Bit位可以控制PCIe设备是否使能MSI中断机制,当使能MSI中断后,INTx中断机制自动失效。Message Address和Message Data具体设置为何值根据不同的平台而不同,比如对于zynq mpsoc平台,Message Address固定为0xFE440000,Message Data为MSI向量号:

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

2. 申请INTx中断向量号

根据上述信息我们来看看驱动中申请中断的处理:

static int pci_vector_get (int bus, int dev, int func,
                           int msi_en, int line, int pin,
                           void *vector)
{
    return 0;
}

前三个入参很好理解,msi_en表示申请的是INTx还是MSI中断,line只在x86平台上有用,这里不需要关心,pin表示如果申请的是INTx中断向量号,PCIe设备使用的是哪个INT引脚,因为不同设备的INTx引脚可能使用不同的中断号,驱动中需要根据不同平台上的中断路由寻找INTx实际对应的中断向量号:

static int pci_vector_get (int bus, int dev, int func,
                           int msi_en, int line, int pin,
                           void *vector)
{
    if (!msi_en) {
        /*
         *  根据不同平台上的中断路由寻找INTx引脚实际对应的中断向量号
         */
        switch (pin) {
        case 1:
            break;

        case 2:
            break;

        case 3:
            break;

        case 4:
            break;

        default:
            return -1;
        }
    }

    return 0;
}

中断号最终就通过最后出参vector返回给调用者。在一个实际的嵌入式系统中,INTx引脚对应的实际中断号可能只有一个,也就是INTA~INTD公用一个中断号,如mpsoc平台:

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

也可能INTA~INTD各对应一个中断号,比如飞腾2000/4平台:

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

3. 申请MSI中断向量号

当msi_en为true时,表示上层驱动想申请MSI中断向量号,这时最后一个出参就表示PCI_MSI_DESC  类型的数据结构指针,驱动中需要设置这个数据结构来返回正确的信息,我们来看下这个数据结构:

typedef struct {
    UINT32      uiAddressLo;                                            /* low 32 bits of address       */
    UINT32      uiAddressHi;                                            /* high 32 bits of address      */
    UINT32      uiData;                                                 /* 16 bits of msi message data  */
} PCI_MSI_MSG;

typedef struct {
    UINT32          PCIMSI_uiNum;
    ULONG           PCIMSI_ulDevIrqVector;
    PCI_MSI_MSG     PCIMSI_pmmMsg;

    UINT32          PCIMSI_uiMasked;
    UINT32          PCIMSI_uiMaskPos;

    PVOID           PCIMSI_pvPriv;
} PCI_MSI_DESC;
typedef PCI_MSI_DESC       *PCI_MSI_DESC_HANDLE;

这里介绍其中几个比较重要的成员。

  • PCIMSI_uiNum:表示设备驱动想申请多少个向量号,根据标准,MSI支持32个中断向量号,MSI-X支持更多。一般控制器驱动会根据这个值来设置PCIe相关的寄存器来使能MSI向量号对应的中断。
  • PCIMSI_ulDevIrqVector:MSI和MSI-X中的向量号只是从0开始的数值,其需要对应实际平台上中断控制器上的中断号,PCIMSI_ulDevIrqVector就是申请成功时,起始的MSI向量号对应的中断号,这个成员需要控制器来设置。
  • PCIMSI_pmmMsg:根据我们之前回顾的中断知识可知,这是PCIe设备触发MSI中断的方法,根据不同的平台而不同,需要控制器驱动来进行设置。

在mpsoc平台上,MSI向量号支持0~63共64个,对应的实际中断号为两个,0~31MSI向量号的中断号为146,32~63MSI向量号的中断号为147:

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

假设在mpsoc平台上申请MSI向量号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;

        msi_desc->PCIMSI_pmmMsg.uiAddressHi = 0;
        msi_desc->PCIMSI_pmmMsg.uiAddressLo = 0xFE440000;
        msi_desc->PCIMSI_pmmMsg.uiData      = 0 & 0x1f;
        msi_desc->PCIMSI_ulDevIrqVector     = 146;
    } 
    return 0;
}

INTx和MSI中断申请都处理完之后,用pci_vector_get 来初始化pci_driver_funcs 变量即可:

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

 

附源码

pci_host_driver_demo3.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>

static PCI_CTRL_CB pci_host;

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

static int pci_cfg_write (int bus, int dev, int func,
                          int offset, int len, 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)
{
    API_PciCtrlCreate(&pci_host);

    return 0;
}

int module_init (void)
{
    pci_host_probe();

    return 0;
}

void module_exit (void)
{
}
gewenbin
  • 本文由 发表于 2021年3月28日17:08:44
  • 转载请务必保留本文链接:http://www.databusworld.cn/10033.html
匿名

发表评论

匿名网友 填写信息

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

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

      看懂了一点,找个机会对照代码在看一下