SylixOS freescale powerpc p4080 pci msi 中断驱动

飞思卡尔 powerpc  pci msi 中断实现和x86有点不相同,在SylixOS 实现msi 中断驱动参考了《PCI+EXPRESS体系结构导读》

和一个 博客链接,不知道这个博主叫啥名字,但是多谢他指导我分开调试msi功能。

 不同体系结构在pci msi实现在会在msi 配置空间address 和data两个地方不同。x86的address是内容如下图:

 

 

 

在飞思卡尔  powerpc 上address 是指向MSIIR寄存器,在p4080 上有三个MSIIR寄存器,分别是MSIIRA,MSIIRB ,MSIIRC。

在SylixOS 上已经存在MSI的框架,通过针对不同体系结构实现不同address 和 data 的填写。

1.MSI使能

在SylixOS中使用MSI功能首先要使用API_PciDevMsiEnableSet函数使能MSI功能。

INT  API_PciDevMsiEnableSet (PCI_DEV_HANDLE  hHandle, INT  iEnable)
{
    INT         iRet        = PX_ERROR;
    UINT32      uiMsiCapOft = 0;

    if (hHandle == LW_NULL) {
        return  (PX_ERROR);
    }

    if (!iEnable) {
        iRet = API_PciIntxEnableSet(hHandle->PCIDEV_iDevBus,
                                    hHandle->PCIDEV_iDevDevice,
                                    hHandle->PCIDEV_iDevFunction, 1);
        if (iRet != ERROR_NONE) {
            return  (PX_ERROR);
        }
    } else {
        iRet = API_PciIntxEnableSet(hHandle->PCIDEV_iDevBus,
                                    hHandle->PCIDEV_iDevDevice,
                                    hHandle->PCIDEV_iDevFunction, 0);
        if (iRet != ERROR_NONE) {
            return  (PX_ERROR);
        }
    }

    iRet = API_PciCapFind(hHandle->PCIDEV_iDevBus,
                          hHandle->PCIDEV_iDevDevice,
                          hHandle->PCIDEV_iDevFunction,
                          PCI_CAP_ID_MSI,
                          &uiMsiCapOft);
    if (iRet != ERROR_NONE) {
        return  (PX_ERROR);
    }

    iRet = API_PciMsiEnableSet(hHandle->PCIDEV_iDevBus,
                               hHandle->PCIDEV_iDevDevice,
                               hHandle->PCIDEV_iDevFunction,
                               uiMsiCapOft,
                               iEnable);
    if (iRet != ERROR_NONE) {
        return  (PX_ERROR);
    }
    
    hHandle->PCIDEV_iDevIrqMsiEn = iEnable;

    return  (ERROR_NONE);
}

 API_PciDevMsiEnableSet首先会判断是否MSI使用,如果MSI使用能关闭INTX中断使用,也就是在INTX和MSI中断只能选择一个使用,不能同时使用。在使用和关闭INTX中断功能时会使用总线号,设备号,功能号访问对应设备的配置空间。在x86和powerpc上都有两个专门的寄存器来存放要访问的地址和读取的数据。以下是p4080手册中的pci设备访问地址寄存器

可以看到对设备的配置空间访问也是通过总线号,设备号,功能号实现。

 

 在API_PciIntxEnableSet 函数中首先读取了pcie配置空间的commond 寄存器的值,从下图一看看出第10位是INTX使能位,函数中对该位进行设置。

在关闭INTX中断后,需要探测是被是否支持MSI中断,设备如果支持MSI中断会再扩展空间中存在ID为05。

在pcie中有一个地址是0x34 ,这个位置是pcie的扩展空间。

 

扩展空间结构如图,在查找到ID为0x05时,表明设备支持MSI能力。此时设备也会找到当前MSI在配置空间的偏移值。判断支持MSI后系统会查找MSI control。在MSI扩展空间如下图:

Message Control  包含了一些重要信息。

  

截图来自 《PCI+EXPRESS体系结构导读》的第10章,书写的非常好。SylixOS系统首先会对第0位使能。启用设备的MSI功能。

使能工作到这里就结束了。

 2.MSI中断数量

API_PciDevMsiRangeEnable函数是为设备申请中断数量,在Message control 中第0-3位是设备申请使用的中断数量,系统会读出当前硬件申请的中断数量。第4-6位是软件分配完成分配中断后回写值。这个值可能比硬件申请的中断数量少,这个是系统根据中断使用情况分配中断的个数。

NT  API_PciDevMsiRangeEnable (PCI_DEV_HANDLE  hHandle, UINT  uiVecMin, UINT  uiVecMax)
{
    INT                     i, j, iRet  = PX_ERROR;
    UINT8                   ucMsiEn     = 0;
    UINT32                  uiMsiCapOft = 0;
    UINT32                  uiVecNum    = 0;
    PCI_MSI_DESC_HANDLE     hMsgHandle  = LW_NULL;

    if (hHandle == LW_NULL) {
        return  (PX_ERROR);
    }
    
    if ((uiVecMin > 32) || (uiVecMax > 32) ||
        (uiVecMax < uiVecMin) || (uiVecMin < 1)) {
        return  (PX_ERROR);
    }

    iRet = API_PciCapFind(hHandle->PCIDEV_iDevBus,
                          hHandle->PCIDEV_iDevDevice,
                          hHandle->PCIDEV_iDevFunction,
                          PCI_CAP_ID_MSI,
                          &uiMsiCapOft);
    if ((iRet != ERROR_NONE) ||
        (!PCI_DEV_MSI_IS_EN(hHandle))) {
        return  (PX_ERROR);
    }
    
    for (i = 0; i < 6; i++) {
        j = 1 << i;
        if (j >= uiVecMin) {
            break;
        }
    }
    uiVecMin = j;
    
    for (i = 0; i < 6; i++) {
        j = 1 << i;
        if (j >= uiVecMax) {
            break;
        }
    }
    uiVecMax = j;

    iRet = API_PciMsiVecCountGet(hHandle->PCIDEV_iDevBus,
                                 hHandle->PCIDEV_iDevDevice,
                                 hHandle->PCIDEV_iDevFunction,
                                 uiMsiCapOft, &uiVecNum);
    if (iRet != ERROR_NONE) {
        return  (PX_ERROR);
    }

    if (uiVecNum < uiVecMin) {
        return  (PX_ERROR);
    
    } else if (uiVecNum > uiVecMax) {
        uiVecNum = uiVecMax;
    }

    hMsgHandle = &hHandle->PCIDEV_pmdDevIrqMsiDesc;
    hMsgHandle->PCIMSI_uiNum = uiVecNum;

__reget:
    iRet = API_PciDevInterMsiGet(hHandle, hMsgHandle);
    if (iRet != ERROR_NONE) {
        hMsgHandle->PCIMSI_uiNum >>= 1;
        if (hMsgHandle->PCIMSI_uiNum < uiVecMin) {
            return  (PX_ERROR);
        }
        goto    __reget;
    }

    hHandle->PCIDEV_uiDevIrqMsiNum = hMsgHandle->PCIMSI_uiNum;
    hHandle->PCIDEV_ulDevIrqVector = hMsgHandle->PCIMSI_ulDevIrqVector;

    /*
     *  MSI can support only 1, 2, 4, 8, 16, 32 number of vectors
     */
    switch (hHandle->PCIDEV_uiDevIrqMsiNum) {
    
    case 1:
        ucMsiEn = 0;
        break;

    case 2:
        ucMsiEn = 1;
        break;

    case 4:
        ucMsiEn = 2;
        break;

    case 8:
        ucMsiEn = 3;
        break;

    case 16:
        ucMsiEn = 4;
        break;

    case 32:
        ucMsiEn = 5;
        break;

    default:
        return  (PX_ERROR);
    }

    iRet = API_PciMsiMsgWrite(hHandle->PCIDEV_iDevBus,
                              hHandle->PCIDEV_iDevDevice,
                              hHandle->PCIDEV_iDevFunction,
                              uiMsiCapOft,
                              ucMsiEn,
                              &hHandle->PCIDEV_pmdDevIrqMsiDesc.PCIMSI_pmmMsg);
    if (iRet != ERROR_NONE) {
        return  (PX_ERROR);
    }

    return  (ERROR_NONE);
}

上面代码中获取硬件申请的中断个数后,会传递给API_PciDevInterMsiGet 函数,此函数会调用驱动层提供好的中断获取函数。在SylixOS pci 框架里针对不同平台访问配置空间和获取中断以及MSI的address和data不同,将读写函数和中断获取函数抽象为驱动层。

MSI中断支持将放在_pciIrqGet函数中。

3.freescale powerpc msi 寄存器

以下介绍参考了《PCI+EXPRESS体系结构导读》 和p4080 硬件手册。

在freescale powerpc中MSI 结构的address 填写的是MSIIR寄存器的地址。MSIIR寄存器偏移地址在P4080手册中有。MSIIR的基地址是CCSRBAR寄存器的值,这个值也是p4080寄存器配置的基地址,这个值并不是固定值,在p4080中默认是0xfe000000。 系统不同,可能对这个值进行修改。

但是在freescale powerpc 中pci域无法直接访问存储空间地址,需要进行inbound 和outbound设置才可以,在p4080中uboot已经分配了pcie设备配置空间的基地址和inbound,oubound。 所以系统中没有在进行单独设置。在p4080中往MSIIR写入时提供了一个PEXCSRBAR寄存器。也就是把bar0换了个名字。

msi address  实际上是PEXCSRBAR + MSIIR寄存器的偏移值。 这个PEXCSRBAR我之前一直以为是当前设备上的PEXCSRBAR。我的pci网卡设备这个位置是0x1000,这个地方卡主了我两天,数据移植无法写入到MSIIR寄存器。后来在网上看到别人这个PEXCSRBAR 寄存器的值是0xff000000 。我发现p4080 bus0,dev 0,function 0 设备的PEXCSRBAR 是0xff00000 ,把pci网卡 msi address地址改为 us0,dev 0,function 0 PEXCSRBAR 的值加上MSIIR寄存器偏移值能够写入到MSIIR寄存器中。

在p4080中把数据写入到MSIIIR寄存器中并没有结束。

数据写入后会触发MSIRn寄存器对应的位 ,系统想知道那个MSIR寄存器被触发了,需要读取MSIIR寄存器来获取,有一个需要注意的是在pcie配置空间里数据都是小端存放的。但是p4080是大端的。计算起来比较费脑。 MSI data 发现p4080好像只取了8位数据写到MSIIIR寄存器,这个地方比较疑惑。比如data值是0x20 其实触发的是MSIR1寄存器。

有个MSIVPR寄存器,这个寄存器存放的是当前MSIRn寄存器对用的中断号,中断优先级,是否使能。此寄存器uboot会根据设备树设置的MSI中断进行设置。每个MSIRn对应的是MSIVPRn寄存器中中断号,所以系统要从MSIVPR在中读取相应的中断号,好触发中断函数。有个需要注意的是MSIR寄存器必须读一次才能清零,否则一直触发中断。《PCI+EXPRESS体系结构导读》对这些寄存器介绍更为详细。

4.SylixOS p4080 实现

 首先从uboot传进ftd树中读取msi中断相应的中断号,保存到数组中。

 

在中断获取函数中为MSI设备申请中断

 

通过data数据,反查MSIVPR寄存器中的中断号,然后映射为系统中断,填充到address 和data, API_PciDevMsiRangeEnable会判断msi配置空间是否是64位然后将address和data填写到msi配置空间中。MSIVPR寄存器是可读可写的,理论上说直接写入中断号应该也能触发,但是我自己写入不触发,也不知道为啥。用uboot配置好的MSIVPR的值是没问题的。

  调试心得: 在调试freescale powerpc msi功能时应该先手动往MSIIR寄存器写入数据,然后打印MSIR,MSISR寄存器的值,如果写入MSIIR寄存器值正确,会触发MSIR和MSISR寄存器相应的位变化。每次只能读取一次,读取会清零。还要注意powerpc p4080是大端的,写入时大小端要处理好,要不可能和想象的不符合。在往MSIIR写入数据触发中断没问题后,在调试在pci设备中填些msi 的address和data。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值