PCI-E配置MSI中断流程解析

    在调试PCI-E的MSI中断前,需要先保证将传统中断调通,然后再调试这个。MSI中断究其本质,就是一个存储器读写事件。将MSI Address设置为内存中的某个地址(可以为64位),产生MSI中断时,中断源会在MSI Address所在的地址写入MSI Data。也就是说,如果有四条MSI中断线,就会依次写入Data、Data+1、Data+2、Data+3在内存中,依次来区分中断源设备。

设备端的定义
    设备在自己的配置空间定义了自己的Capabilities list. 如果该设备支持MSI中断,在此capabilities list其中必定有一个节点的Capabilities ID=0x5D(0x5D 表明是MSI中断节点,其位置由设备自定义)

主控制器
1> 主控制器的工作是扫描到该设备后顺藤摸瓜,沿着Capabilities List找到MSI中断节点.

2> 主控制器给设备上的Address Register和data register俩寄存器赋值(以MPC8548E为例,该值是中断控制器的MSI中断寄存器定义决定);
设备
    MSI中断, 本质上是一个内存写事务,该事务的payload部分都由MSI Capabilities 寄存器的值组成。

The key points here are:
1> Device prepare the capabilities list and the MSI node
2> Controller assign a value to the address register, which is inside the MSI capability node, and the value assigned is the kernel virtual address of the MSI interrupt description register inside the interrupt controller.
3> As well, the value assigned to the data register is defined by the MSI registers inside the interrupt controller.

    Capabilites list 指针位于config space的 0x34 偏移量处,它是所有capabilities 节点的根节点。

    和传统中断在系统初始化扫描PCI bus tree时就已自动为设备分配好中断号不同,MSI中断是在设备驱动程序初始化时调用pci_enable_msi() kernel API 时才分配中断号的。所以如果使用传统中断,在设备驱动程序中直接调用request_irq(pDev->irq, handler,...) 注册设备中断处理函数即可。而使用MSI中断的话,需先调用pci_enable_msi() 初始化设备MSI 结构,分配MSI中断号,并替换INTx中断号,再调用request_irq(pDev->irq, handler,...) 注册设备中断处理函数。除了卸载中断处理函数需要相应地调用pci_diable_msi()外,其他的处理完全相同。下面的Linux 内核代码详细描述了这一过程:

int pci_enable_msi(struct pci_dev* dev)
{
    int status;

    status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
    if (status)
        return status;

    WARN_ON(!!dev->msi_enabled);

   
    if (dev->msix_enabled) {
        dev_info(&dev->dev, "can't enable MSI "
             "(MSI-X already enabled)\n");
        return -EINVAL;
    }
    status = msi_capability_init(dev);//此函数会配置设备MSI结构并分配替换MSI中断号
}

static int msi_capability_init(struct pci_dev *dev)
{
    struct msi_desc *entry;
    int pos, ret;
    u16 control;
    ......
    msi_set_enable(dev, 0);
   
    pci_intx_for_msi(dev, 0);// disable INTx interrupts   
    msi_set_enable(dev, 1);
    dev->msi_enabled = 1;

    dev->irq = entry->irq;   
    return 0;
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭