1. 程式人生 > >[ZZ]PCI-E配置MSI中斷流程解析

[ZZ]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 核心程式碼詳細描述了這一過程:

  1. int pci_enable_msi(struct pci_dev* dev)  
  2. {  
  3.     int status;  
  4.     status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);  
  5.     if (status)  
  6.         return status;  
  7.     WARN_ON(!!dev->msi_enabled);  
  8.     if (dev->msix_enabled) {  
  9.         dev_info(&dev->dev, "can't enable MSI "  
  10.              "(MSI-X already enabled)\n");  
  11.         return -EINVAL;  
  12.     }  
  13.     status = msi_capability_init(dev);//此函式會配置裝置MSI結構並分配替換MSI中斷號  
  14. }  
  15. static int msi_capability_init(struct pci_dev *dev)  
  16. {  
  17.     struct msi_desc *entry;  
  18.     int pos, ret;  
  19.     u16 control;  
  20.     ......  
  21.     msi_set_enable(dev, 0);  
  22.     pci_intx_for_msi(dev, 0);// disable INTx interrupts     
  23.     msi_set_enable(dev, 1);  
  24.     dev->msi_enabled = 1;  
  25.     dev->irq = entry->irq;     
  26.     return 0;  
  27. }