Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基說明:node
先回顧一下PCIe的架構圖:數據結構
Root Complex
部分,至關於PCI的Host Bridge
部分;nwl-pcie
來進行分析;match
函數),當發現驅動與設備能進行匹配時,就會執行probe函數的操做;《Linux PCI驅動框架分析(二)》
中提到過PCI設備、PCI總線和PCI驅動的建立,PCI設備和PCI驅動掛接在PCI總線上,這個理解很直觀。針對PCIe的控制器來講,一樣遵循設備、總線、驅動的匹配模型,不過這裏的總線是由虛擬總線platform
總線來替代,相應的設備和驅動分別爲platform_device
和platform_driver
;那麼問題來了,platform_device
是在何時建立的呢?那就不得不提到Device Tree
設備樹了。架構
device_node
描述的Device Tree
;device_node
節點,建立platform_device
結構,並最終註冊進系統,這個也就是PCIe Host設備的建立過程;咱們看看PCIe Host的設備樹內容:app
pcie: pcie@fd0e0000 { compatible = "xlnx,nwl-pcie-2.11"; status = "disabled"; #address-cells = <3>; #size-cells = <2>; #interrupt-cells = <1>; msi-controller; device_type = "pci"; interrupt-parent = <&gic>; interrupts = <0 118 4>, <0 117 4>, <0 116 4>, <0 115 4>, /* MSI_1 [63...32] */ <0 114 4>; /* MSI_0 [31...0] */ interrupt-names = "misc", "dummy", "intx", "msi1", "msi0"; msi-parent = <&pcie>; reg = <0x0 0xfd0e0000 0x0 0x1000>, <0x0 0xfd480000 0x0 0x1000>, <0x80 0x00000000 0x0 0x1000000>; reg-names = "breg", "pcireg", "cfg"; ranges = <0x02000000 0x00000000 0xe0000000 0x00000000 0xe0000000 0x00000000 0x10000000 /* non-prefetchable memory */ 0x43000000 0x00000006 0x00000000 0x00000006 0x00000000 0x00000002 0x00000000>;/* prefetchable memory */ bus-range = <0x00 0xff>; interrupt-map-mask = <0x0 0x0 0x0 0x7>; interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc 0x1>, <0x0 0x0 0x0 0x2 &pcie_intc 0x2>, <0x0 0x0 0x0 0x3 &pcie_intc 0x3>, <0x0 0x0 0x0 0x4 &pcie_intc 0x4>; pcie_intc: legacy-interrupt-controller { interrupt-controller; #address-cells = <0>; #interrupt-cells = <1>; }; };
關鍵字段描述以下:框架
compatible
:用於匹配PCIe Host驅動;msi-controller
:表示是一個MSI(Message Signaled Interrupt
)控制器節點,這裏須要注意的是,有的SoC中斷控制器使用的是GICv2版本,而GICv2並不支持MSI,因此會致使該功能的缺失;device-type
:必須是"pci"
;interrupts
:包含NWL PCIe控制器的中斷號;interrupts-name
:msi1, msi0
用於MSI中斷,intx
用於舊式中斷,與interrupts
中的中斷號對應;reg
:包含用於訪問PCIe控制器操做的寄存器物理地址和大小;reg-name
:分別表示Bridge registers
,PCIe Controller registers
, Configuration space region
,與reg
中的值對應;ranges
:PCIe地址空間轉換到CPU的地址空間中的範圍;bus-range
:PCIe總線的起始範圍;interrupt-map-mask
和interrupt-map
:標準PCI屬性,用於定義PCI接口到中斷號的映射;legacy-interrupt-controller
:舊式的中斷控制器;compatible
字段匹配上後,會調用probe函數,也就是nwl_pcie_probe
;看一下nwl_pcie_probe
函數:dom
pci_host_bridge
結構,最終經過這個bridge
去枚舉PCI總線上的全部設備;devm_pci_alloc_host_bridge
:分配並初始化一個基礎的pci_hsot_bridge
結構;nwl_pcie_parse_dt
:獲取DTS中的寄存器信息及中斷信息,並經過irq_set_chained_handler_and_data
設置intx
中斷號對應的中斷處理函數,該處理函數用於中斷的級聯;nwl_pcie_bridge_init
:硬件的Controller一堆設置,這部分須要去查閱Spec,瞭解硬件工做的細節。此外,經過devm_request_irq
註冊misc
中斷號對應的中斷處理函數,該處理函數用於控制器自身狀態的處理;pci_parse_request_of_pci_ranges
:用於解析PCI總線的總線範圍和總線上的地址範圍,也就是CPU能看到的地址區域;nwl_pcie_init_irq_domain
和mwl_pcie_enable_msi
與中斷級聯相關,下個小節介紹;pci_scan_root_bus_bridge
:對總線上的設備進行掃描枚舉,這個流程在Linux PCI驅動框架分析(二)
中分析過。brdige
結構體中的pci_ops
字段,用於指向PCI的讀寫操做函數集,當具體掃描到設備要讀寫配置空間時,調用的就是這個函數,由具體的Controller驅動實現;PCIe控制器,經過PCIe總線鏈接各類設備,所以它自己充當一箇中斷控制器,級聯到上一層的中斷控制器(好比GIC),以下圖:函數
INTA#, INTB#, INTC#, INTD#
四根中斷信號,PCI設備藉助這四根信號使用電平觸發方式提交中斷請求;Message Signaled Interrupt
) Interrupt:基於消息機制的中斷,也就是往一個指定地址寫入特定消息,從而觸發一箇中斷;針對兩種處理方式,NWL PCIe
驅動中,實現了兩個irq_chip
,也就是兩種方式的中斷控制器:工具
irq_domain
對應一箇中斷控制器(irq_chip
),irq_domain
負責將硬件中斷號映射到虛擬中斷號上;再來看一下nwl_pcie_enable_msi
函數:fetch
因此,稍微彙總一下,做爲兩種不一樣的中斷處理方式,套路都是同樣的,都是建立irq_chip
中斷控制器,爲該中斷控制器添加irq_domain
,具體設備的中斷響應流程以下:spa
nwl_pcie_leg_handler
,nwl_pcie_msi_handler_high
,和nwl_pcie_leg_handler_low
;chained_irq_enter
進入中斷級聯處理;irq_find_mapping
找到具體的PCIe設備的中斷號;generic_handle_irq
觸發具體的PCIe設備的中斷處理函數執行;chained_irq_exit
退出中斷級聯的處理;file_operation
操做函數集;下篇開始,繼續迴歸到虛擬化,期待一下吧。
Documentation/devicetree/bindings/pci/xlinx-nwl-pcie.txt
歡迎關注我的公衆號,不按期分享技術文章: