轉載自 http://huaqianlee.me/2015/08/19/Android/高通平臺Android源碼分析之Linux內核設備樹-DT-Device-Tree-dts文件/
剛開始接觸Android源碼的時候,發現在kernel裏面多了一種dts文件,因爲當初自學Linux時和在第一家公司做物聯網模型時都是用的比較老的內核,內核代碼還比較混亂,沒有采用dts這種方便簡潔的格式。後面才知道這是因爲Linus的一句”this whole arm thing is a fucking pain in ass“促進改革的,記得Linux早期代碼裏面板級細節都是在C文件中描述的,代碼就顯得十分臃腫和混亂。如此優化之後就顯得簡潔多了,並且也更易於學習、移植。
今天準備專門來分析一下內核設備樹,主要按照如下三個方向來分析:
Device Tree組成及用法
Device Tree由一系列node(節點)和property(屬性)組成,節點本身可包含更多的子節點。屬性是成對出現的name-value鍵值對。在device tree中主要描述如下信息:
- CPU的數量及類別
- 內存基地址和size
- 總線和橋
- 外設連接
- 中斷
- GPIO
- CLOCK
Device Tree在內核的作用有點類似於描述出PCB上的CPU、內存、總線、設備及IRQ GPIO等組成的tree結構。然後經由bootloader傳遞給內核,內核再根據此設備樹解析出需要的i2c、spi等設備,然後將內存、IRQ、GPIO等資源綁定到相應的設備。
lk中通過tag傳遞到kernel,文件路徑:bootable/bootloader/lk/app/aboot/aboot.c,由DEVICE_TREE宏開關控制
DTS(device tree source)
dts文件是一種ASCII文本格式的device tree描述文件,其結構明瞭,第一次看到都能大概猜出其描述意圖。在內核中arm部分,基本上一個.dts文件對應一個arm的machine,一般位於kernel/arch/arm/boot/dts。由於一個soc可能對應多個machine,
所以一般講多個machine通用的部分提煉爲一個.dsti文件,有點類似於頭文件的作用,引用方式也類似:#include “xxx.dtsi”,dtsi文件也可以相互引用。
dts中的基本元素
dts中的基本元素爲節點和屬性,節點可以包含屬性和子節點,屬性爲name-value鍵值對,如下:
1 |
/ { node1 { a-string-property = "A string"; // 值爲字符串 a-string-list-property = "first string", "second string";// 值爲字符數組 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; // 值爲kog /* each number (cell) is a uint32 */ a-cell-property = <1 2 3 4>; // cells(由uint32組成) child-node1 { }; }; }; |
上述dt並沒有什麼真實用途,沒有描述任何東西。不過展示了dt的結構:
- 一個根節點:”/“;
- 一對子節點:”node1”和”node2”;
- 子節點的子節點:”child-node”;
- 屬性定義: 屬性值可以爲空、字符串、cells(整數組成)、數組及二進制等任意字節流;
屬性中常用的字節流如下:
1 |
# 字符串,用雙引號引用: |
Sample Machine
理解設備樹怎麼被用的最好辦法,就是做一遍,接下來就通過一步一步構建描述一個簡單machine的device tree來理解設備樹。假設machine的硬件配置如下:
- 一個32bit的ARM CPU
- 處理器的local bus的內存映射分佈了串口、spi總線控制器、i2c控制器、中斷控制器和外部總線橋
- 256MB的SDRAM,基地址爲0
- 2個串口基地址爲:0x101f1000 和 0x101f2000
- GPIO控制器,基地址爲0x101f3000
- spi控制器,基地址爲0x10170000,從屬設備:
- MMC slot with ss pin attached to GPIO #1 (不能很好理解其意思,所以就不胡亂翻譯了)
- External bus橋,從屬設備:
- smc smc91111 Ethernet設備,基地址爲0x10100000
- i2c控制器,基地址爲0x10160000,從屬設備:
- Maxim DS1338時鐘芯片,從設備I2C地址 1101000(0x58)
- 64MB Nor flash,基地址爲0x30000000
初始化結構
首先,爲machine創建一個框架結構,一個有效設備樹的最簡單的結構,如下:
1 |
/ { compatible = "acme,coyotes-revenge"; }; |
compatible指定系統的名字,格式: compatible = “< manufacturer>,< model>”(製造商,型號)。它非常重要,用來精確指定設備,並通過包含manufacurer(製造商)名字來避免衝突。因爲操作系統通過compatible的值來決定machine怎麼運行,所以使用正確的值是非常重要的。
理論上來說,compatible是操作系統所有數據標示machine的唯一標示符,os將通過頂層compatible尋找相應的值。
CPUs
第二步,描述CPU的”cpus”節點,其包含每一個CPU描述信息的子節點,在這個例子中,CPU爲一個雙核的arm cortex A9處理器,所以其描述如下:
1 |
/ { compatible = "acme,coyotes-revenge"; cpus { cpu@0 { compatible = "arm,cortex-a9"; // 格式同頂層節點,<manufacturer>,<model> }; cpu@1 { compatible = "arm,cortex-a9"; }; }; }; |
節點名
每一個節點必須有一個節點名,格式: < name>[@< unit-address>]。
- < name>:爲最長31個字符的ascii字符串,一般用其代表的設備類型命名,ie. 一個3com Ethernet adapter的節點名:ethernet,不用3com509。
- unit-address: 描述設備的地址,一般情況下,其提供訪問設備的基地址,節點的reg property也用此參數,見下文。
同層次兄弟節點的節點名必須是獨一無二的,不過多個節點可以使用一樣的通用name,只要地址不同就可以了。ie. serial@101f1000 & serial@101f2000
Devices
每一個device在系統中由一個設備樹節點描述,所以接下來,第三步是爲設備填充樹的節點。不過,現在我爲新節點創建一個空節點,直到我們知道地址範圍和如何處理irqs請求之後再填寫相應內容。如下:
1 |
/ { compatible = "acme,coyotes-revenge"; cpus { cpu@0 { compatible = "arm,cortex-a9"; }; cpu@1 { compatible = "arm,cortex-a9"; }; }; serial@101F0000 { compatible = "arm,pl011"; }; serial@101F2000 { compatible = "arm,pl011"; }; gpio@101F3000 { compatible = "arm,pl061"; }; interrupt-controller@10140000 { compatible = "arm,pl190"; }; spi@10115000 { compatible = "arm,pl022"; }; external-bus { ethernet@0,0 { compatible = "smc,smc91c111"; }; i2c@1,0 { compatible = "acme,a1234-i2c-bus"; rtc@58 { compatible = "maxim,ds1338"; }; }; flash@2,0 { compatible = "samsung,k8f1315ebm", "cfi-flash"; }; }; }; |
在此tree中,在系統中爲每一個device增加了節點,其層次結構反應了系統中的連接情況。ie. extern bus上的的設備憋創建爲external bus節點的子節點,i2c設備被創建爲i2c總線控制器的子節點。簡單來說,tree中的層次結構代表了系統中的CPU視圖。
目前,這個tree是無效的,因爲它沒有設備之間的連接信息,接下來再添加這些信息。
在這個tree中有幾點需要注意:
- 每個設備節點都有一個compatible屬性
- flash節點的compatible屬性有兩個字符串值。
- 如前所述,節點名反映設備類型,而非詳細型號。
compatible詳解
設備樹中每個節點都需要有compatible屬性,compatible屬性決定每一個設備驅動綁定哪一個設備。如上所介紹,compatible是一個字符串序列,第一個字符串指定精確設備,第二字符串指定兼容設備。
例如:Freescale MPC8349片上有一個根據國家半導體ns16550接口實現的串行設備,定義爲:compatible = “fsl,mpc8349-uart”, “ns16550”. 第一個字符串指定精確設備,第二個指定國家半導體16550 uart兼容設備。
ns16550沒有製造商前綴(manufacturer)純屬歷史原因,所有的compatible值應該帶有製造商前綴。
這種做法允許將存在的設備驅動綁定到一類更新的設備,並且仍然能識別到精確的設備。
警告:不要使用通配符賦值,如:”fsl,mpc83xx-uart”等。爲了兼容後續設備,一般會選擇一個特定實現,如上的:”ns16550”。
設備尋址
關於設備尋址,設備樹中通過如下屬性encode地址信息:
1 |
reg :每個可尋址的設備有一個reg cells. |
CPU尋址
CPU節點尋址是尋址裏面最簡單的,每個CPU被一個獨一無二的ID標記,沒有size與CPU ids關聯。如下:
1 |
cpus { #address-cells = <1>; #size-cells = <0>; // 此兩個屬性表明子節點reg 值爲一個沒有size的uint32地址 cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; // 值與節點名的unit-address相同 }; cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; }; }; |
如果一個節點有reg屬性,則節點名必須包含unit-address,並且取reg屬性的第一個address值。
有內存映像地址的設備
與cpu中只有address值不同,有內存映像地址的設備還需分配地址範圍值,每個子節點reg元素定義地址長度值的數量由父節點的#size-cells指定。如下:
1 |
/ { #address-cells = <1>; // 值爲 1 cell(32bits) #size-cells = <1>; // 每個長度值爲 1 cell // 如果是64 bit machines, 則以上兩值爲2 ... serial@101f0000 { compatible = "arm,pl011"; reg = <0x101f0000 0x1000 >; // 第一個參數爲基地址,第二個參數爲地址長度,此處表示serial的內存地址範圍:0x101f0000~0x101f0fff }; serial@101f2000 { compatible = "arm,pl011"; reg = <0x101f2000 0x1000 >; }; gpio@101f3000 { compatible = "arm,pl061"; reg = <0x101f3000 0x1000 0x101f4000 0x0010>; // GPIO設備被分配到兩個地址範圍 }; interrupt-controller@10140000 { compatible = "arm,pl190"; reg = <0x10140000 0x1000 >; }; spi@10115000 { compatible = "arm,pl022"; reg = <0x10115000 0x1000 >; }; ... }; |
當然,並不是所有設備都直接和cpu相連,也有一些設備通過掛載到一條總線上和cpu相連。對於掛接到總線的設備,每個父節點爲子節點定義地址域,如下:
1 |
external-bus { //父節點 #address-cells = <2> // 子節點有2 cells基地址值,一個用於指定chip number,一個用於指定選中芯片基地址的偏移量 #size-cells = <1>; // 子節點有1 cell 地址長度 ethernet@0,0 { compatible = "smc,smc91c111"; reg = <0 0 0x1000>; }; i2c@1,0 { compatible = "acme,a1234-i2c-bus"; reg = <1 0 0x1000>; rtc@58 { compatible = "maxim,ds1338"; }; }; flash@2,0 { compatible = "samsung,k8f1315ebm", "cfi-flash"; reg = <2 0 0x4000000>; }; }; |
由於地址域被節點和其子節點一起定義,所以父節點可以爲總線定義任何尋址方式。除了直接父親以外的所有節點和子節點都不用關心本地的尋址域,不用關心地址從哪映射到哪。
如不明白,請繼續往下看,相信接下來的部分會幫你解惑
無內存映像的設備
無內存映像的設備沒有直接訪問cpu的權限,父設備的驅動將間接訪問cpu,其cpu一樣reg屬性會有一個地址值,但沒有地址長度或範圍,如下:
1 |
i2c@1,0 { compatible = "acme,a1234-i2c-bus"; #address-cells = <1>; #size-cells = <0>; reg = <1 0 0x1000>; rtc@58 { compatible = "maxim,ds1338"; reg = <58>; }; }; |
地址轉換
前面講了怎麼給設備分配本地地址,但沒有說明怎麼映射到cpu能直接訪問的地址。接下來就詳細分析一下這一部分:
根節點描述cpu地址空間視圖,根節點的子節點不需要做任何顯性的映射直接使用cpu的地址域。比如:serial@101f0000直接分配到地址0x101f0000.
而不是根節點的直接孩子的節點不使用cpu的地址域,爲了能將其映射到cpu的內存地址,設備樹就得對其地址進行轉換,ranges屬性就是用來實現這個目的的,加入ranges屬性後如下:
1 |
/ { compatible = "acme,coyotes-revenge"; #address-cells = <1>; #size-cells = <1>; ... external-bus { #address-cells = <2> #size-cells = <1>; ranges = <0 0 0x10100000 0x10000 // Chipselect 0, Ethernet 1 0 0x10160000 0x10000 // Chipselect 1, i2c controller 2 0 0x30000000 0x10000000>; // Chipselect 2, NOR Flash,此處參考文章地址空間大小少一個0,但我覺得不對,所以自己做了修改,下同,就不再說明 // 相信大家直接通過這個列表就能知道地址怎麼轉換的了,如下: 1. 偏移量爲0的Chipselect0映射到0x10100000~0x1010ffff 2. 偏移量爲0的Chipselect1映射到0x10160000~0x1016ffff 3. 偏移量爲0的Chipselect2映射到0x30000000~0x3fffffff (此處參考文章寫的0x10000000,但我覺得應該是0x3fffffff,原地址見博文最後引用) ethernet@0,0 { compatible = "smc,smc91c111"; reg = <0 0 0x1000>; }; // i2c總線節點沒有ranges參數,因爲i2c總線上的設備不需映射到cpu地址域,cpu直接通過i2c就能訪問i2c設備 i2c@1,0 { compatible = "acme,a1234-i2c-bus"; #address-cells = <1>; #size-cells = <0>; reg = <1 0 0x1000>; rtc@58 { compatible = "maxim,ds1338"; reg = <58>; }; }; flash@2,0 { compatible = "samsung,k8f1315ebm", "cfi-flash"; reg = <2 0 0x10000000>; // 此處參考文章寫的0x4000000, 但我覺得爲0x10000000 - 256MB }; }; }; |
ranges參數的值是一個地址轉換列表,每一個條目由如下幾部分組成:
- 子節點地址:由子節點的#address-cells值決定
- 父節點地址:由父節點的#address-cells值決定
- 子節點地址空間的大小 :由子節點的#size-cells值決定
如果ranges參數爲空,則表示子節點地址和父節點地址1:1映射。你可能會有疑問,既然1:1映射,爲什麼還要通過地址轉換來獲得地址空間。一些總線(比如PCI)有完全不同的地址空間細節需要暴露給操作系統。其他帶DMA的設備需要知道設備在總線上的真實地址。有時設備需要組合在一起去共享相同的可編程物理地址映射。是否需要通過1:1映射依賴於操作系統和硬件設計的很多信息。
缺乏ranges參數意味着,一個設備只能被其父節點訪問而不能被cpu直接訪問。
中斷
中斷信號可以來自machine的任何設備,中斷信號在設備樹中被描述爲節點之間的links。主要有如下4中屬性:
- interrupt-controller:一個空屬性,定義節點爲中斷控制器;
- #interrupt-cells:表明連接此中斷控制器的interrupts屬性cell大小(類似於#address-cells和#size-cells);
- interrupt-parent:指定節點設備所依附的中斷控制器的phandle,若沒有此參數,則從父節點繼承;
- interrupts:中斷說明符列表,節點通過此方法指定中斷號、觸發方式等;
一箇中斷說明符描述指定中斷輸入設備的相關信息,由#interrupt-cells指定中斷說明符cell數量。設備可能一個或多箇中斷源。一箇中斷設備的說明符完全取決於綁定的中斷控制器設備。 定義一箇中斷源需要多少cells由中斷控制器決定。加入中斷相關屬性後如下:
1 |
/ { compatible = "acme,coyotes-revenge"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&intc>; // intc->interrupt-controller,作爲系統默認的interrupt-parent屬性,子節點重寫則覆蓋 cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; }; cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; }; }; serial@101f0000 { compatible = "arm,pl011"; reg = <0x101f0000 0x1000 >; interrupts = < 1 0 >; // 指定中斷源 }; serial@101f2000 { compatible = "arm,pl011"; reg = <0x101f2000 0x1000 >; interrupts = < 2 0 >; }; gpio@101f3000 { compatible = "arm,pl061"; reg = <0x101f3000 0x1000 0x101f4000 0x0010>; interrupts = < 3 0 >; }; intc: interrupt-controller@10140000 { // 中斷控制器 compatible = "arm,pl190"; reg = <0x10140000 0x1000 >; interrupt-controller; #interrupt-cells = <2>; // 中斷說明符有2 cells,此例中cell 1表示中斷號,cell 2 表示觸發方式 }; spi@10115000 { compatible = "arm,pl022"; reg = <0x10115000 0x1000 >; interrupts = < 4 0 >; // 注:設備還可以使用多箇中斷號,假如此spi使用兩個,則:interrupts = <0 4 0>, <1 5 0>; }; external-bus { #address-cells = <2> #size-cells = <1>; ranges = <0 0 0x10100000 0x10000 // Chipselect 0, Ethernet 1 0 0x10160000 0x10000 // Chipselect 1, i2c controller 2 0 0x30000000 0x10000000>; // Chipselect 2, NOR Flash ethernet@0,0 { compatible = "smc,smc91c111"; reg = <0 0 0x1000>; interrupts = < 5 2 >; }; i2c@1,0 { compatible = "acme,a1234-i2c-bus"; #address-cells = <1>; #size-cells = <0>; reg = <1 0 0x1000>; interrupts = < 6 2 >; rtc@58 { compatible = "maxim,ds1338"; reg = <58>; interrupts = < 7 3 >; }; }; flash@2,0 { compatible = "samsung,k8f1315ebm", "cfi-flash"; reg = <2 0 0x10000000>; }; }; }; |
另, 關於cell含義在內核中的相關文檔有詳細描述,比如arm gic 中斷:
1 |
# Documentation/devicetree/bindings/arm/gic.txt The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI interrupts. The 2nd cell contains the interrupt number for the interrupt type. SPI interrupts are in the range [0-987]. PPI interrupts are in the range [0-15]. The 3rd cell is the flags, encoded as follows: bits[3:0] trigger type and level flags. 1 = low-to-high edge triggered 2 = high-to-low edge triggered 4 = active high level-sensitive 8 = active low level-sensitive bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of the 8 possible cpus attached to the GIC. A bit set to '1' indicated the interrupt is wired to that CPU. Only valid for PPI interrupts. |
設備特有數據
除了上面講的常用屬性,任意需要的屬性和子節點都可以被加入到設備樹,不過新device-specific屬性應將製造商名作爲前綴命名,以避免與標準的屬性衝突;
其實還有一些要求,不過主要針對內核開發者的,而我還沒有那個水平,就沒詳細看了
特殊節點
aliases節點
一個specific節點通常以完全路徑的形式引用,如:/external-bus/ethernet@0,0 , 但是這樣太複雜了,不利於閱讀。所以通常會用以一個短的別名命名的aliases節點去指定設備的完全路徑,如下:
1 |
aliases { ethernet0 = ð0; serial0 = &serial0; }; |
注:property = &Label 不同於如上中斷phandle引用的phandle = <&Lable>
chosen節點
chosen節點不指明真實的設備,其爲硬件和操作系統數據傳輸服務,如:啓動參數。通常chosen節點在dts源文件中寫爲空,在啓動時再填充,在例中增加如下:
1 |
chosen { bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; }; |
DTC (device tree compiler)
DTC將.dts編譯爲.dtb的工具。DTC的源代碼位於內核的scripts/dtc目錄,在Linux內核使能了Device Tree的情況下,編譯內核的時候主機工具dtc會被編譯出來,對應scripts/dtc/Makefile中的“hostprogs-y := dtc”。
在Linux內核的arch/arm/boot/dts/Makefile中,描述了當某種SoC被選中後,哪些.dtb文件會被編譯出來,如與VEXPRESS對應的.dtb包括:
1 |
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \ |
在Linux下,我們可以通過make dtbs命令單獨編譯Device Tree文件。因爲arch/arm/Makefile中含有一個dtbs編譯target,如下:
1 |
# Build the DT binary blobs if we have OF configured |
Device Tree Blob (dtb)
dtb是dts被DTC編譯後生成的二進制格式Device Tree描述,可由Linux內核解析。系統設計時通常會單獨留下一個很小的flash空間存放.dtb文件,bootloader在引導kernel的過程中,會先讀取該.dtb到內存。
Binding
對於Device Tree中的結點和屬性具體是如何來描述設備的硬件細節的,內核裏有相應的文檔,位於:Documentation/devicetree/bindings目錄,其下又分爲很多子目錄。
dts解析API
注:此部分基本完全摘自參考文檔
在Linux的BSP和驅動代碼中,解析dts的API通常被以“of_”作爲前綴,它們的實現代碼位於內核的drivers/of目錄。接下來就介紹一下常用的API。
int of_device_is_compatible(const struct device_node device,const char compat);
判斷設備結點的compatible 屬性是否包含compat指定的字符串。當一個驅動支持2個或多個設備的時候,這些不同.dts文件中設備的compatible 屬性都會進入驅動 OF匹配表。因此驅動可以透過Bootloader傳遞給內核的Device Tree中的真正結點的compatible 屬性以確定究竟是哪一種設備,從而根據不同的設備類型進行不同的處理。如drivers/pinctrl/pinctrl-sirf.c即兼容於”sirf,prima2-pinctrl”,又兼容於”sirf,prima2-pinctrl”,在驅動中就有相應分支處理:
1 |
if (of_device_is_compatible(np, "sirf,marco-pinctrl")) |
根據compatible屬性,獲得設備結點。遍歷Device Tree中所有的設備結點,看看哪個結點的類型、compatible屬性與本函數的輸入參數匹配,大多數情況下,from、type爲NULL。
int of_property_read_u8_array(const struct device_node np, const char propname, u8 out_values, size_t sz);
int of_property_read_u16_array(const struct device_node np, const char propname, u16 out_values, size_t sz);
int of_property_read_u32_array(const struct device_node np, const char propname, u32 out_values, size_t sz);
int of_property_read_u64(const struct device_node np, const char propname, u64 out_value);
讀取設備結點np的屬性名爲propname,類型爲8、16、32、64位整型數組的屬性。對於32位處理器來講,最常用的是of_property_read_u32_array()。如在arch/arm/mm/cache-l2x0.c中,透過如下語句讀取L2 cache的”arm,data-latency”屬性:
1 |
of_property_read_u32_array(np, "arm,data-latency", |
在arch/arm/boot/dts/vexpress-v2p-ca9.dts中,含有”arm,data-latency”屬性的L2 cache結點如下:
1 |
L2: cache-controller@1e00a000 { |
有些情況下,整形屬性的長度可能爲1,於是內核爲了方便調用者,又在上述API的基礎上封裝出了更加簡單的讀單一整形屬性的API,它們爲int of_property_read_u8()、of_property_read_u16()等,實現於include/linux/of.h:
1 |
static inline int of_property_read_u8(const struct device_node *np, |
int of_property_read_string(struct device_node np, const char propname, const char out_string);
int of_property_read_string_index(struct device_node np, const char propname, int index, const char output);
前者讀取字符串屬性,後者讀取字符串數組屬性中的第index個字符串。如drivers/clk/clk.c中的of_clk_get_parent_name()透過of_property_read_string_index()遍歷clkspec結點的所有”clock-output-names”字符串數組屬性。
1 |
const char *of_clk_get_parent_name(struct device_node *np, int index) |
static inline bool of_property_read_bool(const struct device_node np, const char propname);
如果設備結點np含有propname屬性,則返回true,否則返回false。一般用於檢查空屬性是否存在。
void __iomem of_iomap(struct device_node node, int index);
通過設備結點直接進行設備內存區間的 ioremap(),index是內存段的索引。若設備結點的reg屬性有多段,可通過index標示要ioremap的是哪一段,只有1段的情況,index爲0。採用Device Tree後,大量的設備驅動通過of_iomap()進行映射,而不再通過傳統的ioremap。
unsigned int irq_of_parse_and_map(struct device_node *dev, int index);
透過Device Tree或者設備的中斷號,實際上是從.dts中的interrupts屬性解析出中斷號。若設備使用了多箇中斷,index指定中斷的索引號。
還有一些OF API,這裏不一一列舉,具體可參考include/linux/of.h頭文件。
高通Android源碼中dts文件
AndroidBoard.mk
Android編譯過程(如想了解更多可參考:Android編譯過程詳解)中會解析到device\qcom\msm8916_32\AndroidBoard.mk,此文件中選擇了kernel的默認配置文件,如下:
1 |
# device\qcom\msm8916_32\AndroidBoard.mk |
msm8916_defconfig
此文件中主要是一些編譯開關,包括dts文件的編譯開關,如下:
1 |
# kernel\arch\arm\configs\msm8916_defconfig |
Makefile
dts文件目錄的mk文件決定需要加載哪些dts文件,這些文件最終打包到dt.img,再經由mkbootimg工具和其他鏡像一起打包到boot.img。關鍵源碼如下:
1 |
# kernel\arch\arm\boot\dts\qcom\Makefile |
dts中的platform info
msm8916-cdp.dts文件中定義平臺信息,如下:
1 |
# kernel\arch\arm\boot\dts\qcom\msm8916-cdp.dts #include "msm8916-cdp.dtsi" #include "msm8916-memory.dtsi" / { model = "Qualcomm Technologies, Inc. MSM 8916 CDP"; compatible = "qcom,msm8916-cdp", "qcom,msm8916", "qcom,cdp"; qcom,board-id = <1 0>; }; ... |
不過我們在每個項目的dts文件中重新定義了平臺信息,如下:
1 |
# kernel\arch\arm\boot\dts\qcom\msm8916-qrd-skuh-$(OEM_PROJECT_NAME).dts #include "msm8916-qrd-skuh.dtsi" #include "msm8916-memory.dtsi" / { model = "Qualcomm Technologies, Inc. MSM 8916 QRD SKUH changcheng l783"; compatible = "qcom,msm8916-qrd-skuh", "qcom,msm8916-qrd", "qcom,msm8916", "qcom,qrd"; qcom,board-id = <0x1000b 0> , <0x1000b 4>; }; ... |
Reference
我的這篇博文只是寫了一些基本的東西,主要參考下面這些文檔,並且很多內容直接翻譯自下面的文檔,如果想了解更多請查閱如下引用文檔:
kernel\Documentation\devicetree:源碼中的文檔,很有參考價值,其實需要的基本能在裏面找到,我已上傳至百度雲,可以click下載查看
http://devicetree.org/Device_Tree_Usage :很多內容譯自此處
Power_ePAPR_APPROVED_v1.0.pdf:進階文檔,因爲官網總是不能成功訪問,所以在我百度網盤存了一份,分享給大家
http://blog.csdn.net/21cnbao/article/details/8457546