天天看點

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

   在傳統的pci中斷體系中,每一個pci總線上的裝置被配置設定一個特定的中斷号,然後當裝置需要中斷cpu時,裝置直接發出int信号,然後在cpu的inta引腳拉低的時候将自己的中斷号放在資料總線上,一切都要裝置自己負責,這一切的緣由一部分就是因為pci的并行性,實作事務很複雜,而pcie是串行的,很容易定義協定包,是以很容易就實作了由root complex代理中斷的功能,是以裝置也就可以動态的配置設定獨占的中斷号了,因為中斷号的配置設定完全是軟體解決的,而不再像傳統pci那樣是硬體解決的了,軟體的最大特點就是其靈活性,是以pcie更适合大量裝置的環境,中斷處理程式再也不需要大量周遊共享中斷号的裝置來确定中斷源了。

在調試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中斷節點,其位置由裝置自定義)

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

主要制器

1> 主要制器的工作是掃描到該裝置後順藤摸瓜,沿着capabilities list找到msi中斷節點.

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

2> 主要制器給裝置上的address register和data register倆寄存器指派(以mpc8548e為例,該值是中斷控制器的msi中斷寄存器定義決定);

裝置

    msi中斷, 本質上是一個記憶體寫事務,該事務的payload部分都由msi capabilities 寄存器的值組成。

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

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-E配置MSI中斷流程解析

    和傳統中斷在系統初始化掃描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;  

}  

繼續閱讀