中斷一般包括中斷產生設備和中斷處理設備。中斷控制器負責處理中斷,每一個中斷都有對應的中斷號及觸發條件。中斷產生設備可能有多個中斷源,有時多個中斷源對應中斷控制器中的一個中斷,這種情況中斷產生設備的中斷源稱之為中斷控制器中對應中斷的子中斷。一般情況中斷產生設備數量要多于中斷控制器,多個中斷產生設備的中斷都由一個中斷控制器處理,這種多對一的關系也很像一個樹形結構,所以在設備樹中,中斷也被描述成樹,叫中斷樹。以下表述的時候為了明確是在說中斷樹,在父節點和子節點前邊我們都加上“中斷”二字,是為了防止和設備樹的父節點、子節點混淆(雖然大部分情況設備樹的父子關系就是中斷樹的父子關系,但是因為存在特例,所以我們還是強調是中斷父子關系)。
中斷產生設備用interrupts屬性描述中斷源(interrupt specifier),因為不同的硬件描述中斷源需要的數據量不同,所以interrupts屬性的類型也是。為了明確表示一個中斷由幾個u32表示,又引入了#interrupt-cells屬性,#interrupt-cells屬性的類型是u32,假如一個中斷源需要2個u32表示(一個表示中斷號,另一個表示中斷類型),那么#interrupt-cells就設置成2。有些情況下,設備樹的父節點不是中斷的父節點(主要是中斷控制器一般不是父節點),為此引入了interrupt-parent屬性,該屬性的類型是,用來引用中斷父節點(我們前邊說過,一般用父節點的標簽,這個地方說中斷父節點而不是中斷控制器是有原因的)。如果設備樹的父節點就是中斷父節點,那么可以不用設置interrupt-parent屬性。interrupts屬性和interrupt-parent屬性都是中斷產生設備節點的屬性,但是#interrupt-cells屬性不是,#interrupt-cells屬性是中斷控制器節點以及interrupt nexus節點的屬性,這兩類節點都可能是中斷父節點。
中斷控制器節點用interrupt-controller屬性表示自己是中斷控制器,這個屬性的類型是空,不用設置值,只要存在這個節點就表示該節點是中斷控制器。除了這個屬性外,中斷控制器節點還有#interrupt-cells屬性,用來表示該中斷控制器直接管理下的interrupt domain(后邊我們會講中斷控制器的中斷子節點interrupt nexus節點有單獨的interrupt domain)用幾個u32表示一個中斷源(interrupt specifier)。中斷控制器節點就包括interrupt-controller和#interrupt-cells兩個關于中斷的屬性。中斷控制器的#address-cells屬性和中斷映射有關系,但是該屬性不是為中斷設計的,中斷映射只是用到了這個屬性而已。
前邊說中斷控制器中的一個中斷可能對應中斷產生設備中的多個中斷源,那這種對應關系用什么描述呢?我們還說過#interrupt-celll屬性不僅是中斷控制器節點的屬性,還是interrupt nexus節點的屬性,這個interrupt nexus節點就是描述中斷映射關系的,該節點通過interrupt-map,interrupt-map-mask屬性描述中斷映射關系。interrupt-map屬性是類型的,每個元素表示一個中斷映射關系(注意是一個"中斷映射關系",不是"一個中斷"映射關系),從前向后包括:中斷子設備地址,中斷子設備中斷源(interrupt specifier),中斷父設備,中斷父設備地址,中斷父設備中斷源(interrupt specifier)五部分。中斷子設備地址具體由幾個u32組成是由中斷子設備所在總線(不是中斷父設備)的#address-cells屬性決定的,這個地方為什么用中斷設備地址而不用中斷設備的phandle,是有原因的,因為中斷設備會用interrupt-parrent屬性指向中斷父節點,所以中斷子設備是可以確定的,不需要說明。還因為中斷子設備地址可以做與運算,通過interrupt-map-mask屬性就可以實現多對一的映射。中斷子設備中斷源(interrupt specifier)由幾個u32組成是由該interrupt nexus節點下的#interrupt-cell決定的。中斷父設備是一個指向中斷父設備的屬性,一般情況下是中斷控制器,但是按照中斷樹的邏輯,也可能是更高一級的interrupt nexus節點。中斷父設備地址具體由幾個u32組成是由中斷父設備節點下的#address-cells屬性決定的(注意,不是中斷父設備所在總線的#address-cells屬性)。中斷父設備中斷源(interrupt specifier)由幾個u32組成是由中斷父設備的#interrupt-cells屬性決定的。
還記得前邊說過中斷設備的中斷源和中斷控制器的中斷源可能是多對一的關系,如果每個子中斷都用interrupt-map中的一行表示,那么interrupt-map屬性將非常大。為了讓多個子中斷共享映射關系,引入了interrupt-map-mask屬性,該屬性的類型也是,包含中斷子設備地址和中斷子設備中斷源的bit mask,給定一個子中斷源,那么首先和interrupt-map-mask做與運算,運算結果再通過interrupt-map屬性查找對應的中斷父設備中斷源。這就是我們前邊為什么說interrupt-map屬性的一行是一個“中斷映射關系”,而不是“一個中斷”映射關系的原因。
我們再來復習一下,整個中斷樹的最底層是中斷產生設備(也可能是從interrupt nexus節點),中斷產生設備用interrupts屬性描述他能產生的中斷。因為他的中斷父設備可能和設備樹的父設備不同,那么用interrupt-parent屬性指向他的中斷父設備。他的中斷父設備可能是中斷控制器(如果中斷產生設備的中斷和中斷控制器的中斷是一一對應的,或者最底層是interrupt nexus節點),也可能是interrupt nexus節點(如果最底層是中斷產生設備,且需要映射)。interrupt nexus節點及他的所有直接子節點構成了一個interrupt domain,在該interrupt domain下中斷源怎樣表示由#interrupt-cells屬性決定,如何由中斷子設備中斷源找到中斷父設備中斷源由interrupt-map和interrupt-map-mask屬性決定。interrupt nexus的父節點可能還是一個interrupt nexus父節點,也可能是一個中斷控制器,當向上找到最后一個中斷控制器,并且該中斷控制器再也沒有中斷父設備時,整個中斷樹就遍歷完成了。中斷控制器用interrupt-controller屬性表示自己是中斷控制器,并且用#interrupt-cells屬性表示他所直接管理的interrupt domain用幾個u32表示一個中斷源。根據中斷樹的特性,一個設備樹中是有可能有多個中斷樹的。
以上是中斷在設備樹中如何描述的規則,聽起來是挺復雜的,但只要理解了就很簡單,為了幫助理解我們舉一個實際的例子。為了突出中斷部分,我們做了簡化。
/ {
?? ?model = "Marvell Armada 375 family SoC";
?? ?compatible = "marvell,armada375";
?? ?soc {
?? ??? ?#address-cells = <2>;
?? ??? ?#size-cells = <1>;
?? ??? ?interrupt-parent = <&gic>;
?? ??? ?internal-regs {
?? ??? ??? ?compatible = "simple-bus";
?? ??? ??? ?#address-cells = <1>;
?? ??? ??? ?#size-cells = <1>;
?? ??? ??? ?timer@c600 {
?? ??? ??? ??? ?compatible = "arm,cortex-a9-twd-timer";
?? ??? ??? ??? ?reg = <0xc600 0x20>;
?? ??? ??? ??? ?interrupts = ;
?? ??? ??? ??? ?clocks = <&coreclk 2>;
?? ??? ??? ?};
?? ??? ??? ?gic: interrupt-controller@d000 {
?? ??? ??? ??? ?compatible = "arm,cortex-a9-gic";
?? ??? ??? ??? ?#interrupt-cells = <3>;
?? ??? ??? ??? ?#address-cells = <0>;
?? ??? ??? ??? ?interrupt-controller;
?? ??? ??? ??? ?reg = <0xd000 0x1000>,
?? ??? ??? ??? ?????? <0xc100 0x100>;
?? ??? ??? ?};
?? ??? ?}
?? ??? ?pcie-controller {
?? ??? ??? ?compatible = "marvell,armada-370-pcie";
?? ??? ??? ?#address-cells = <3>;
?? ??? ??? ?#size-cells = <2>;
?? ??? ??? ?pcie@1,0 {
?? ??? ??? ??? ?#address-cells = <3>;
?? ??? ??? ??? ?#size-cells = <2>;
?? ??? ??? ??? ?#interrupt-cells = <1>;
?? ??? ??? ??? ?interrupt-map-mask = <0 0 0 0>;
?? ??? ??? ??? ?interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
?? ??? ??? ?};
?? ??? ?};
}
首先我們看到timer@c600這個設備節點下定義了interrupts屬性,這說明該設備可以產生中斷,但是這個屬性下描述了幾個中斷我們是看不出來的(如果有經驗了,我們能猜出只是一個中斷,現在我們按照規則確認)。因為該節點沒有interrupt-parent屬性,那么認為設備樹的父節點internal-regs就是中斷父節點,在internal-regs父節點下還是沒有interrupt-parent屬性,那么還是繼續找設備樹父節點,找到了soc,在該節點下邊有interrupt-parent屬性。該屬性引用的標簽為gic,搜索整個設備樹,interrupt-controller@d000的標簽為gic。gic節點下有interrupt-controller屬性,說明他是一個中斷控制器。gic節點還有屬性#interrupt-cells = <3>,說明在該控制器的interrupt domain下,中斷源(interrupt specifier)用3個u32表示,我們再看timer@c600下的interrupts屬性也確實由3個u32組成(可以參考GIC的規范,第一個u32表示中斷類型,第二個是中斷號,第三個是中斷觸發條件)。這個例子說明如果中斷產生設備的中斷源和中斷控制器的中斷源是一一對應的,那么可以不需要interrupt nexus節點及相關的屬性來表示中斷映射。
再看pcie@1,0這個節點,有#interrupt-cells屬性,但是沒有interrupt-controller屬性,這說明他是一個interrupt nexus節點。該節點的#interrupt-cells屬性為1,說明該interrupt nexus節點管轄下的中斷源用1個u32表示就可以了。在pcie@1,0節點下邊沒有子節點,且也沒有節點的interrupt-parent屬性指向pcie@1,0節點,所以從設備樹上看不到該interrupt domain下的中斷產生設備,可能的原因是這些中斷產生設備軟件可以動態識別所以不需要設備樹描述。因為interrupt-map-mask屬性是由中斷產生設備的地址和中斷源(interrupt specifier)組成,且中斷源用1個u32表示,那么可以推測中斷產生設備地址由3個u32組成。這里需要注意的是pcie@1,0節點的#address-cells屬性為3,是說該總線下邊的設備地址用3個u32表示,但并不代表中斷產生設備的設備地址也一定3個u32表示,此處不能說是巧合,但是我們要清楚中斷產生設備的地址由幾個u32組成是由該設備所在總線決定的,對于pcie總線也確實是3,但是其他總線可能存在其他種的情況。現在我們來分析interrupt-map屬性,前三個數字是中斷設備地址,第四個數字是中斷設備的中斷源。因為interrupt-map-mask是全0,這樣不管與什么數字做與運算結果都是0,interrupt-map屬性的前4個數字也都是0,這說明在pcie@1,0下邊所有的中斷映射到中斷父節點的中斷都是一個中斷。接著是指向gic的,因為gic節點下#address-cells屬性為0,所以后邊不需要描述中斷父設備的地址了,后邊3個數字都是表示中斷父設備中斷源的。一句話描述就是pcie@1,0下的所有中斷都映射到gic,GIC_SPI類型的第29號中斷,觸發類型為高電平觸發。這個例子說明在中斷樹的最下邊可以是interrupt nexus節點。
以上例子中斷樹的根是gic,gic下邊有兩個孩子,一個是中斷設備timer@c600,一個是interrupt nexus節點pcie@1,0。gic直接管轄的interrupt domain用3個u32表示中斷源,timer@c600在這個interrupt domain下。pcie@1,0下定義了一個新的interrupt domain,在該interrupt domain下,中斷源用1個u32表示,pcie@1,0用interrupt-map和interrupt-map-mask屬性將下邊所有設備的中斷映射到一個gic下邊的中斷上。
?
評論
查看更多