Linux DTS 裝置樹
裝置樹就是有一些屬性和節點組成的一種資料結構,屬性一般會賦予一些屬性值,而節點則可能是是由屬性跟其下的子節點構成。下面是一個簡單的例子:
/dts-v1/;
/ {
node1 {
a-string-property = “A string”;
a-string-list-property = “first string”, “secondstring”;
a-byte-data-property = [0x01 0x23 0x34 0x56];
child-node1{
first-child-property;
second-child-property = <1>;
a-string-property = “Hello, world”;
};
child-node2{
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1{
};
};
};
上面描述的樹顯然沒有任何使用價值,沒有描述任何板級系統,這裡只是描述各個節點的屬性結構,下面對上面結構做出解釋:
Ø 一個單一的root節點”/”
Ø 一對子節點”node1” 和”node2”
Ø 子節點node1還有一對子節點中的子節點:“child-node1”以及“child-node2”
Ø 很多散佈在整個樹結構的屬性值
對於遍佈於整個樹結構的屬性,屬性可以為空,也可以跟賦予任何位元組的資料資訊,對於屬性的賦值,主要有如下的資料結構:
Ø 被雙引號包括的文字字串(空字元結尾):
string-property= “a string”;
Ø “cells”代表的是32位無符號整形資料,賦值的時候用“< >”包括:
cell-property =<0xbeef 123 0xabcd1234>;
Ø 被“[ ]”包括的二進位制資料:
binary-property= [0x01 0x23 0x45 0x67];
Ø 混合各種資料型別,不同資料型別用逗號隔開:
mixed-property = “astring”, [0x01 0x23 0x45 0x67], <0x12345678>;
Ø 逗號也可以當做多個字串的分隔符:
下面給出一個例子,從0開始描述如何寫一個完整的裝置樹。現在有個arm平臺的板子,假設製造商為“acme”我們給他命名為“Coyote’s Revenge”首先,假設我們有如下的硬體平臺:
· 一顆 32bit ARM CPU
· 處理器本地總線上映射了串列埠SPI控制器、I2C控制器、中斷控制器以及外部匯流排橋。
· 基地址為0,大小為256MB的SDRAM
· 基地址分別為0x101F1000 和0x101F2000的2個串列埠
· 基地址為0x101F3000 GPIO控制器
· 基地址為0x10170000的SPI 控制器上有如下的裝置:
· MMC 卡槽的SS引腳連線到了GPIO #1
· 外部匯流排橋上有如下的裝置:
· SMC公司生產的SMC91111 Ethernet,其基地址為0x10100000。
· 在基地址為0x10160000的 i2c 控制器有如下裝置:
· 美信公司的DS1338 實時時鐘晶片. 從站相應地址為0x58
· 大小為64MB 的 NOR flash 其基地址為0x30000000
下面將按照上面的描述資訊,寫一個DTS檔案。
初始化結構體
首先先給整個裝置樹寫一個框架如下所示:
/dts-v1/;
/ {
compatible =”acme,coyotes-revenge”;
};
上面首先在根節點下寫了一個屬性compatible,該屬性是系統用來識別不同板級裝置的重要依據,其一般由廠商名和樣板名兩部分組成,比如在上面我們的製造商為acme板子的名字叫做coyotes-revenge。
加入CPU
假如我們的CPU是一顆雙核A9的CPU,我們新增一個叫”cpus”的子節點,具體如下:
/dts-v1/;
/ {
compatible =”acme,coyotes-revenge”;
cpus {
compatible = “arm,cortex-a9”;
};
compatible = “arm,cortex-a9”;
};
};
};
cpu的compatible設定與頂層的類似,有兩部分組成。Arm描述了製造商,cortex-a9指明瞭型號。
實際上為了完整的描述一個cpu需要新增更多的資訊,但是現在我們先了解下其他的基礎概念
節點命名:
到此為止,我們有必要先了解下節點的命名方式,一般節點有如下的格式:
<name>[@<unit-address>
]
<name>
是不超過
31
個字元的字串,通常節點名是以裝置的型別來命名的而不是具體的裝置型號,比如有個網路裝置
,那麼我們命令的時候節點是“ethernet”而不是具體的型號比如“3com509
.”
unit-address描述一個裝置是否需要設定訪問地址,通常unit-address描述的訪問該裝置的基地址,同時該地址也會在reg屬性列出,稍後我們會講解reg屬性的設定。
同一層次下的節點命名必須是唯一的,但是隻要基地址不同,同一層次也可以存在相同的命名節點,比如:[email protected] & [email protected]
系統中每個裝置都是依靠裝置樹節點來描述的,接下來就要新增一些裝置節點來描述每一個裝置:
/dts-v1/;
/ {
compatible = “acme,coyotes-revenge”;
cpus {
compatible = “arm,cortex-a9”;
};
compatible = “arm,cortex-a9”;
};
};
[email protected]F0000{
compatible = “arm,pl011”;
};
compatible = “arm,pl011”;
};
compatible = “arm,pl061”;
};
compatible = “arm,pl190”;
};
compatible = “arm,pl022”;
};
external-bus {
compatible = “smc,smc91c111”;
};
compatible = “acme,a1234-i2c-bus”;
compatible =”maxim,ds1338”;
};
};
compatible = “samsung,k8f1315ebm”,”cfi-flash”;
};
};
};
通過上面的列表可以看出來,通過dts的層次關係,可以看到具體的板級的裝置連線關係,比如外部總線上有ethercat、i2c和flash三個裝置。
但是上面描述的這課樹還不是一個完整有效的裝置樹,因此他缺少必要的連線方式、地址資訊等。稍後會加上這些資訊。
不過有如下幾點需要注意:
· 每一個裝置節點都有一個compatible屬性.
· Flash節點的 compatible 屬性賦值了兩個字串,具體原因下一節將會解釋。
理解
Compatible
每個裝置節點都會有
compatible
屬性,
裝置與驅動之間的結合就依賴這個屬性的匹配。
Compatible屬性是由字串列表組成,就像上面列出的flash節點,compatible有兩個屬性值,以一個字串確切的表示該裝置的資訊,第二個字串描述的與該節點描述符相相容的裝置。
裝置如何定址
裝置定址地址在裝置樹中通過如下的屬性資訊來表示:
reg
#address-cells
#size-cells
每一個可定址設都會有一個reg屬性,該屬性有一個或多個元素組成,其基本格式為:
reg =<address1 length1 [address2 length2] [address3 length3] … >
上面的每一個元素都代表裝置的定址地址及其定址大小,每一個元素中的address值可以是一個或者多個無符號32位整形資料型別cell來表示,元素中的length可以為空也可以使一個或者多個無符號32位整形資料型別cell。
由於每個可定址裝置都會有reg屬性可設定,而且reg屬性元素也是靈活可選擇的,那麼誰來制定reg屬性元素中每個元素也就是address和length的個數呢?
在這裡,要關注到期父節點的兩個屬性,其中#address-cells表示reg中address元素的個數,#size-cells用來表示length元素的個數。
為了展示剛剛接手實行的作用,那麼現在做一個演示,首先從cpu節點開始演示:
CPU 定址地址
對於定址地址的編寫,cpu節點是最簡單的一個例子,之前介紹,而每個cpu節點都包含一個標記ID,但是沒有其他的描述資訊,這裡填充一些其他的屬性:
cpus {
#address-cells= <1>;
#size-cells = <0>;
compatible = “arm,cortex-a9”;
reg= <0>;
};
compatible = “arm,cortex-a9”;
reg= <1>;
};
};
在cpus節點#address-cells
賦予 1,
#size-cells
賦予 0.。這意味著在其子節點的reg只有一個地址元素值,沒有長度元素值。在這個案例當中,兩顆cpu核的地址分別唄分配成0和1。因為每個cpu只分配了地址,所以節點#size-cells
元素被設定為
0。
記憶體對映裝置
需要記憶體對映的裝置不同於上面的cpu節點,這類的裝置需要一段記憶體而不是單一的記憶體地址,因此不近需要包含記憶體的基地址還而且還需要對映地址的長度,因此需要使用 #size-cells屬性來表示reg屬性元素中表示地址長度元素的個數。在下面的例子中,每一個節點的address值有一個32位無符號整形資料而且length值也是用一個32位無符號整形資料來表示。因此在32的系統中#address-cells 和#size-cells都要設定為1,但是在64位系統中#address-cells就要設定成2了。具體設定如下:
/dts-v1/;
/ {
#address-cells= <1>;
#size-cells = <1>;
…
compatible= “arm,pl011”;
reg =<0x101f0000 0x1000 >;
};
compatible= “arm,pl011”;
reg =<0x101f2000 0x1000 >;
};
compatible= “arm,pl061”;
reg =<0x101f3000 0x1000
0x101f4000 0x0010>;
};
compatible= “arm,pl190”;
reg =<0x10140000 0x1000 >;
};
compatible= “arm,pl022”;
reg =<0x10115000 0x1000 >;
};
…
};
上面的例子中reg屬性都有address元素和length屬性,值的注意的是,例子中的GPIO被分配了兩個地址範圍,分別是0x101f3000…0x101f3fff 以及0x101f4000..0x101f400f。
有一些裝置可能有不同的定址方案,比如一個裝置掛載到總線上連線一個片選訊號線,可以通過片選訊號選擇不同的裝置。由於父節點可以定義了其子節點的地址對映域,所以可以選擇最適合的一項來描述硬體裝置。下面的程式碼就是把片選號碼編入地址碼掛載到外部總線上的一個裝置。
external-bus {
#address-cells= <2>
#size-cells = <1>;
compatible = “smc,smc91c111”;
reg= <0 0 0x1000>;
};
compatible = “acme,a1234-i2c-bus”;
reg = <1 0 0x1000>;
compatible = “maxim,ds1338”;
};
};
compatible= “samsung,k8f1315ebm”, “cfi-flash”;
compatible= <2 0 0x4000000>;
};
};
上面的程式碼中, #address-cells 屬性為2,則表示reg屬性的address有兩個地址域,其中一個表示片選號,另一個表示裝置到片選基地址的偏移量,#size-cells為1,其地址範圍量的個數還是一個32位的無符號整數。所以最後reg有三個屬性值,分別表示片選號、偏移量、地址範圍。
無記憶體對映裝置
其他的一些裝置,他們在處理器匯流排瓶沒有記憶體對映。他們擁有地址範圍但是他們不被cpu直接的訪問,而是被父裝置驅動替代cpu進行訪問。
舉個例子,對於i2c裝置,每個裝置都會有一個指定的訪問地址,但是這些裝置不會有相關聯的範圍或者地址長度,這有點類似cpu節點地址分配。如下是具體程式碼的例子:
compatible = “acme,a1234-i2c-bus”;
#address-cells= <1>;
#size-cells = <0>;
reg =<1 0 0x1000>;
compatible = “maxim,ds1338”;
reg= <58>;
};
};
Ranges (地址轉換)
之前已經介紹過如何給裝置分配地址了,但是這個地址都是裝置的本地地址並不一定是cpu可以訪問該裝置的地址。
根節點是從cpu的角度來描述地址空間的,根節點的子節點總是依賴於cpu的地址域,所以不需要顯示地地址對映。比如,[email protected] 裝置描述的地址就是0x101f0000.
但是有些節點並不是根節點的直接子節點,因此其不使用cpu地址域。為了可以得到裝置的對映地址,裝置樹必須說明一個域到另一個作用域的記憶體地址的轉換關係,range屬性就此誕生。
下面是一個簡單的例子:
/dts-v1/;
/ {
compatible =”acme,coyotes-revenge”;
#address-cells= <1>;
#size-cells =<1>;
…
external-bus {
#address-cells = <2>
#size-cells= <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2ccontroller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
compatible = “smc,smc91c111”;
reg =<0 0 0x1000>;
};
compatible = “acme,a1234-i2c-bus”;
#address-cells = <1>;
#size-cells = <0>;
reg =<1 0 0x1000>;
compatible = “maxim,ds1338”;
reg= <58>;
};
};
compatible = “samsung,k8f1315ebm”, “cfi-flash”;
reg =<2 0 0x4000000>;
};
};
};
ranges屬性是說明地址轉換的一個列表,列表的每一個條目分別表示子節點的地址,父節點的地址,子節點地址空間的長度(記憶體空間大小),每個條目的個數分別是用子節點的#address-cells
值,父節點#address-cells
值,以及子節點#size-cells
的值來表示。
· 比如,就上面出顯得裝置樹程式碼來講,子節點的#address-cells
為
2
,父節點
#address-cells
為
1
,子節點
#size-cells
為1,則上面程式碼中程式先的ranges屬性的含義就是:
· 片選號為0,偏移量為0的裝置對映的地址為0x10100000..0x1010ffff
· 片選號為1,偏移量為0的裝置對映的地址為0x10160000..0x1016ffff
· 片選號為2,偏移量為0的裝置對映的地址為0x30000000..0x30ffffff
此外,假如父節點的地址空間與子節點的地址空間是相同的,可以新增一個空的ranges屬性,當你看到一個空的ranges的時候,這意味著子節點的地址與父節點的地址空間是1:1對映的。
或許,你會感到困惑,既然對映比例是1:1那麼為什麼還要地址轉換。一些匯流排(比如PCI匯流排)用於完全不同的地址空間,然而這種匯流排需要把具體的地對映範圍反應給作業系統。一些可DMA訪問的匯流排裝置,作業系統必須知道其真實的地址。有些時候需要把一些共享相同軟體可程式設計物理對映地址的裝置分成一個組。是否1:1對映記憶體地址取決於作業系統需要獲取的資訊以及對硬體本身的描述。
或許細心的你已經注意到,在外部匯流排裡面的[email protected],0的節點中,並沒有新增ranges屬性資訊。不同於外部匯流排,i2c中的裝置不需要在cpu地址域中進行地址對映,cpu通過訪問[email protected],0來間接的訪問[email protected]裝置。一個節點缺少ranges屬性意味著該裝置不需要直接被除了父節點意外的裝置訪問。
中斷如何工作?
與依賴裝置樹結構進行地址轉換不同,中斷訊號可以產生與或者中斷與板子上的任何裝置。對於一般的裝置,其地址資訊在裝置樹中自然的被表示出來,中斷訊號被表現在獨立在樹的節點之間的連線。下面的四個屬性用來描述中斷連線。
· interrupt-controller – 一個空的屬性表示該節點描述的裝置接受中斷訊號
· #interrupt-cells – 這是一箇中斷控制器節點屬性,該屬性類似於#address-cells和#size-cells表示中斷控制器包含多箇中斷描述符。
· interrupt-parent -該屬性表示該裝置節點所一來的中斷控制器的控制代碼,有些節點沒有interrupt-parent屬性,則表示繼承其父節點的interrupt-parent屬性。
· interrupts – 該屬性描述了裝置節點包含的一系列的中斷描述符,對應於該裝置的中斷輸出訊號。
一箇中斷描述符就是一個或者多個無符號32位整形資料的個數(具體的個數在#interrupt-cells中定義),重點描述符表示該裝置接受哪些中斷輸入訊號。就像下面裝置樹程式碼中的例子,大多數的裝置僅僅有一個輸出中斷,但是有時候一個裝置也有可能含有多個輸出中斷。這意味著一箇中斷描述符完全依賴繫結在裝置上的中斷控制器。每個中斷控制器可以決定需要多少個cells來描述一個獨一無二的書屋中斷。
下面是一個簡單的例子:
/dts-v1/;
/ {
compatible =”acme,coyotes-revenge”;
#address-cells= <1>;
#size-cells =<1>;
interrupt-parent= <&intc>;
cpus {
#address-cells = <1>;
#size-cells= <0>;
compatible = “arm,cortex-a9”;
reg =<0>;
};
compatible = “arm,cortex-a9”;
reg =<1>;
};
};
compatible= “arm,pl011”;
reg =<0x101f0000 0x1000 >;
interrupts= < 1 0 >;
};
compatible= “arm,pl011”;
reg =<0x101f2000 0x1000 >;
interrupts= < 2 0 >;
};
compatible= “arm,pl061”;
reg =<0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts= < 3 0 >;
};
intc:[email protected] {
compatible= “arm,pl190”;
reg =<0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
compatible= “arm,pl022”;
reg =<0x10115000 0x1000 >;
interrupts= < 4 0 >;
};
external-bus {
#address-cells = <2>
#size-cells= <1>;
ranges =<0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
10 0x10160000 0x10000 // Chipselect 2, i2c controller
20 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
compatible = “smc,smc91c111”;
reg =<0 0 0x1000>;
interrupts= < 5 2 >;
};
compatible = “acme,a1234-i2c-bus”;
#address-cells = <1>;
#size-cells = <0>;
reg =<1 0 0x1000>;
interrupts= < 6 2 >;
compatible = “maxim,ds1338”;
reg= <58>;
interrupts= < 7 3 >;
};
};
compatible = “samsung,k8f1315ebm”, “cfi-flash”;
reg =<2 0 0x4000000>;
};
};
};
下面幾點需要格外的注意:
· 該評估板僅僅包含一箇中斷控制器,[email protected]
· 標號為“intc:”新增到中斷控制器節點中,這個標號主要是被用於根節點的interrupt-parent屬性的賦值控制代碼,除非在根節點的子節點中明確的定義了interrupt-parent屬性,否則所有根節點的子節點將繼承根節點的interrupt-parent屬性。
· 每個裝置都會使用interrupt屬性來描述一個獨一無二的中斷輸入線.
· #interrupt-cells屬性設定為2表示每個中斷描述符包含2個cells,一般第一個cells表示表示中斷線號,第二個cells表示一個標記號,比如表示該中斷是高電平觸發、是低電平觸發還是電平觸發等等。對於所有給定的中斷控制器,請參考控制器繫結文件以便獲取物件中斷編碼。
裝置特定資料
除了上面出現幾個通用含義的屬性,在子節點中可以新增任意屬性資訊。只要屬性遵循下面的規矩,新增的任何屬性都可被作業系統識別。
首選,新新增的自定義的屬性名字需要加上製造商的名字作為字首以免與標準通用的屬性名發生衝突。
其次,每當定義了一個自定義屬性都應該在相應的核心文件中加以說明,以便核心開發者可以明天該屬性的具體含義。在相應的文件中需要有對該屬性的說明,該屬性值的含義,該屬性可以賦予何值,以及該值的具體含義。
特殊節點
別名節點
引用一個特定的節點通常是通過全路徑的方式,比如:
/external-bus/[email protected],0,但是這樣有缺陷,使用者根本不關係真實路徑資訊,他們關心的僅僅是哪個是eth0,aliase節點就是將一個全部路徑心機簡化為一個別名資訊,比如:
aliases {
ethernet0 =ð0;
serial0 =&serial0;
};
這裡你會發現一個新的語法property= &label;用字串屬性引用一個標號來替代一個完整的路徑資訊。但是這不同於上面介紹的phandle=< &label>,因為phandle= < &label>是把一個phandle= < &label>,因值插入到一個cell.
chosen節點
chosen並不是代表一個真實的裝置,僅僅是作為作業系統與韌體之間傳遞資料的一個地方。相當於啟動引數、在chosen節點的資料並不代表韌體。通常,在。Dts檔案中chosen節點為空,在啟動的是將會被填充。
在假定的評估板中,韌體可填充如下的資訊。
chosen {
bootargs =”root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200”;
};
知識進階
到目前為止,我們自定義的評估板上裝置都有所介紹,為了提高普適性,我們新加入其他的一些稍微複雜難度的裝置。
這裡,在板子上新增一個PCI主橋,其記憶體對映地址為0x10180000,且BARS以0x8000000為起始地址。
我們先從如下的裝置樹節點來描述一個PCI主橋:
compatible = “arm,versatile-pci-hostbridge”, “pci”;
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
};
PCI匯流排編號
每個PCI匯流排都會被分配獨立的編號,這些編號在bus-ranges屬性中被定義,該屬性包含兩個cells,第一個表示當前節點PCI匯流排的標號,第二個表示子PCI匯流排的最大編號。
下面給出只含有一個PCI匯流排的板級說明,因此bus-ranges兩個屬性值應該全是0:
compatible= “arm,versatile-pci-hostbridge”, “pci”;
reg =<0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges= <0 0>;
};
PCI地址轉換
跟之前介紹的情況雷同,PCI地址空間與CPU地址空間是完全獨立的,因此需要將PCI的地址對映到CPU的地址域。與之前一樣,這裡仍然使用range
, #address-cells
,
以及#size-cells
這些屬性。
compatible = “arm,versatile-pci-hostbridge”, “pci”;
reg =<0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;
#address-cells= <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
};
正如你所看到的,子地址(PCI地址)使用了3個cells,PCI地址範圍使用了2個cells。或許你會有個疑問,為什麼表示一個PCI地址需要3個cells,下面就這個問題我們做一個解釋,首先地址有3個cells,我們把他分別定義為:phys.hi, phys.midand phys.low ,每一個cells為一個無符號32位整數,我們可以吧每一位用如下格式表示出來:
· phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr
· phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
· phys.low cell: llllllll llllllll llllllll llllllll
上面再表示出每一位的時候,用不同的字母做以替代,每個字母都代表不同的含義。由於PCI地址是一個64位資料,所以分別用phys.hi及其phys.Low來表示高低32位,對於phys.hi,有如下的位域。具體含義如下:
· n: 重定義標記 (在這裡不起作用)
· p: 可預區(快取) 區域標記位
· t: 地址別名標記位(在這裡不起作用)
· ss: 空間編碼,具體含義如下
· 00: 配置空間
· 01: I/O 空間
· 10: 32 位地址空間
· 11: 64 為地址空間
· bbbbbbbb: PCI匯流排編號,PCI匯流排可以分層結構,所以我們可能會在子匯流排定義一些PCI/PCI橋
· ddddd: 裝置編號,通常與初始化裝置選擇訊號(IDSEL)有關係。
· fff: 功能選擇號,用於多功能PCI裝置。
· rrrrrrrr: 再配置週期使用的註冊號。
為了達到地址轉換的目的,最重要的域是p跟ss,這兩個域直接決定訪問哪個PCI空間。因此通過檢視ranges屬性,我們可以得到三個區域:
· PCI地址為0x80000000 大小為512M預存地址區,對映到主CPU記憶體地址為0x80000000 處。
· PCI地址為0xa0000000大小為512M無預存地址區,對映到主CPU記憶體地址為0xa0000000處。
· PCI地址為0x00000000大小為16M IO區,對映到主CPU記憶體地址為0xb0000000處。
為防止這些工作,phys.hi 位域的存在就意味著作業系統必須知道該節點代表了一個 PCI 橋,這樣作業系統才能為了地址轉換而忽略那些不相關的欄位。為了判斷應該忽略哪些額外的欄位,作業系統需要在 PCI 匯流排節點中尋找“pci”字串。
高階中斷對映
下面我們開始介紹最有意思的部分—PCI中斷對映,一個PCI裝置可以被#INTA, #INTB, #INTC
以及 #INTD來觸發,。假如我們沒有多功能PCI裝置,這個裝置則使用#INTA。但是,一般情況下每個PCI槽或者PCI裝置會連線到不同中斷控制器的輸入端。因此需要在裝置樹定義一種機制可以表示出PCI發出的訊號與中斷控制器之間的連線關係。#interrupt-cells
, interrupt-map
以及interrupt-map-mask
就是用來描述這種對映關係。
事實上,這種對映關係不僅僅存在於
PCI
匯流排航,在其他的節點上也可以用此種方法來描述複雜的中斷對映關係,只是這種關係在
PCI
裝置上應用比較廣泛。
compatible = “arm,versatile-pci-hostbridge”, “pci”;
reg =<0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;
#address-cells = <3>
#size-cells = <2>;
ranges= <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
#interrupt-cells= <1>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0xc000 0 0 1 &intc 9 3 // 1st slot
0xc000 0 0 2 &intc 10 3
0xc000 0 0 3 &intc 11 3
0xc000 0 0 4 &intc 12 3
0xc800 0 0 1 &intc 10 3 // 2nd slot
0xc800 0 0 2 &intc 11 3
0xc800 0 0 3 &intc 12 3
0xc800 0 0 4 &intc 9 3>;
};
首先可以注意到,PCI中斷不同於系統的中斷,系統中斷號用2個cells來表示,一個代表IRQ中斷號,另一個是標記號碼,PCI中斷只有一個cells,因為PCI中斷總是低電平觸發。
在我們的實驗板上,我們有兩個PCI卡槽連線4箇中斷線,現在需要在中斷控制器上對映8箇中斷線,此時就需要使用interrupt-map屬性。
僅僅通過中斷號(#INTA等)是無法區分出單個PCI總線上的PCI裝置的,但是我們還必須指出是哪個PCI裝置觸發了中斷線。幸好每個裝置都有一個獨一無二的裝置號,我們可通過該裝置號識別不同的裝置。為了辨別出到底是哪一個裝置觸發的中斷線,我們需要一個元素,該元素需要包含PCI裝置號以及PCI中斷號,通俗的來說,我們需要構造一箇中斷說明單元,該單元包含如下四個元素:
· 3個#address-cells包含 phys.hi, phys.mid,phys.low,
· 一個 #interrupt-cell (#INTA, #INTB, #INTC,#INTD).
很多時候,我們僅僅需要PCI地址中的裝置號部分,因此interrupt-map-mask 應運而生,interrupt-map-mask也是擁有四個元素,第一個元素表示中斷說明單元中哪一個部分應該被考慮。在我們的例子中,我們僅僅需要phys.hi中裝置號部分且我們需要3個bit位來指明四個中斷線(在計數的時候是從1開始的不是0)。
現在我們可以構造interrupt-map 屬性,這個屬性是一個表格,其中表格中的每一個條目包含一個子(PCI匯流排)中斷說明單元、一個父控制代碼(複製中斷服務的中斷控制器)以及一個父中斷說明單元。所以我們在上面的程式碼的第一行就可以得出PCI中斷#INTA被對映到IRQ9,低電平觸發
現在,還有一點需要補充介紹一下PCI匯流排中斷說明單元中的連線號,PCI匯流排中斷說明單元重要組成部分就是pyhs.hi位域中的裝置號部分,裝置號是板級特定的,裝置號具體的跟每個PCI主控制器如何啟用各個裝置的 IDSEL 管腳有關。就上面的例子來講PCI卡槽1被分配的裝置ID是24(0x18),PCI卡槽2被分配的裝置ID是25(0x19)。對於每一個PCI卡槽的phys.hi中的值等與裝置號左移11位到dddd位域得到的:
phys.hi 插到PCI卡槽1的phys.hi 的值為 0xC000(0x18左移11位)
· phys.hi 插到PCI卡槽2的phys.hi 的值為 0Xc800(0x19左移11位)
把所有的元素放到一起,則 interrupt-map屬性為:
· 卡槽1#INTA對應主中斷控制器的中斷號為IRQ9, 低電平觸發
· 卡槽1#INTB對應主中斷控制器的中斷號為IRQ10, 低電平觸發
· 卡槽1#INTC對應主中斷控制器的中斷號為IRQ11, 低電平觸發
· 卡槽1#INTD對應主中斷控制器的中斷號為IRQ12, 低電平觸發
· 卡槽2#INTA對應主中斷控制器的中斷號為IRQ10, 低電平觸發
· 卡槽2#INTB對應主中斷控制器的中斷號為IRQ11, 低電平觸發
· 卡槽2#INTC對應主中斷控制器的中斷號為IRQ12, 低電平觸發
· 卡槽2#INTD對應主中斷控制器的中斷號為IRQ9, 低電平觸發
interrupts = <8 0>屬性表示中斷控制器PCI橋控制器本身可以觸發中斷,不要跟PCI裝置使用INTA、INTB觸發中斷混淆。
最後需要注意的事。就像interrupt-parent 屬性一樣,節點中 interrupt-map 屬性的存在將改變子節點和孫節點的預設中斷控制器。在這個 PCI 示例中,這意味著 PCI 主橋變成了預設中斷控制器。如果一個通過 PCI 匯流排連線的裝置同時還直接連線至另一箇中斷控制器,這