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

来源:互联网 发布:淘宝清仓冬装 编辑:程序博客网 时间:2024/05/21 17:06

    在调试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, andthe 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;}
原创粉丝点击