=============================================
問題一:以前的Linux Kernel如何描述硬體,現在又如何描述呢?
在以前的核心版本中:
1)核心包含了對硬體的全部描述;
2)bootloader會加載一個二進制的核心鏡像,并執行它,比如uImage或者zImage;
3)bootloader會提供一些額外的資訊,成為ATAGS,它的位址會通過r2寄存器傳給核心;
ATAGS包含了記憶體大小和位址,kernel command line等等;
4)bootloader會告訴核心加載哪一款board,通過r1寄存器存放的machine type integer;
5)U-Boot的核心啟動指令:bootm
6)Barebox變量:bootm.image (?)
現今的核心版本使用了Device Tree:
1)核心不再包含對硬體的描述,它以二進制的形式單獨存儲在另外的位置:the device tree blob
2)bootloader需要加載兩個二進制檔案:核心鏡像和DTB
核心鏡像仍然是uImage或者zImage;
DTB檔案在arch/arm/boot/dts中,每一個board對應一個dts檔案;
3)bootloader通過r2寄存器來傳遞DTB位址,通過修改DTB可以修改記憶體資訊,kernel command line,以及潛在的其它資訊;
4)不再有machine type;
5)U-Boot的核心啟動指令:bootm -
6)Barebox變量:bootm.image,bootm.oftree
有些bootloader不支援Device Tree,或者有些專門給特定裝置寫的版本太老了,也不包含。為了解決這個問題,CONFIG_ARM_APPENDED_DTB被引進。
它告訴核心,在緊跟着核心的位址裡查找DTB檔案;
由于沒有built-in Makefile rule來産生這樣的核心,是以需要手動操作:
cat arch/arm/boot/zImage arch/arm/boot/dts/myboard.dtb > my-zImage
mkimage ... -d my-zImage my-uImage
(cat這個指令,還能夠直接合并兩個mp3檔案哦!so easy!)
另外,CONFIG_ARM_ATAG_DTB_COMPAT選項告訴核心去bootloader裡面讀取ATAGS,并使用它們更新DT。
=============================================
問題二:現在Linux Kernel使用的Device Tree到底是個什麼東東?
引用the Power.org Standard for Embedded Power Architecture Platform Requirements (ePAPR)的定義:
1)ePAPR使用device tree的概念描述硬體。boot程式會加載device tree到client program's memory中,并将device tree的指針傳遞給client;
2)device tree是一個樹形資料結構with nodes,用來描述系統的physical devices;
3)一個ePAPR-complient device tree描述的裝置資訊不能被client program讀取;
From Source to binary
1)在ARM系統中,所有的DTS檔案放置在arch/arm/boot/dts中:
.dts檔案為闆級定義
.dtsi檔案為SoC級定義
2)Device Tree Compiler工具,将源代碼編譯成二進制形式;
它的源代碼放置在scripts/dtc中
3)編譯器會産生DTB檔案,bootloader會加載這個DTB檔案,核心在boot時去解析它;
4)arch/arm/boot/dts/Makefile會指定産生哪個DTB檔案;
上圖是pdf裡面自帶的例子,我再從arch/arm/boot/dts/am33xxx.dtsi中摘錄了兩個:
uart0: [email protected] {
compatible = "ti,omap3-uart";
ti,hwmods = "uart1";
clock-frequency = <48000000>;
reg = <0x44e09000 0x2000>;
interrupts = <72>;
status = "disabled";
};
uart1: [email protected] {
compatible = "ti,omap3-uart";
ti,hwmods = "uart2";
clock-frequency = <48000000>;
reg = <0x48022000 0x2000>;
interrupts = <73>;
status = "disabled";
};
對比圖檔中的注釋,就能夠知道對于uart0這個外設:
Node name: serial
Unit Address: 0x44e09000
compatible: 定義了裝置的programming model,允許作業系統識别對應的程式驅動;
clock-frequency: 48000000,晶振頻率為24MHz,這應該是PLL倍頻後的輸出(?);
reg: 寄存器的位址和寄存器長度,uart0的位址起始為0x44e09000,長度為0x2000;
interrupts: 中斷号;
status: 狀态值,初始的時候為disabled,即禁用它;
=============================================
問題三:Device Tree的編寫規則是怎樣的?
1)Device Tree inclusion不一定要做成monolithic,它們可以分散在不同的檔案中,互相包含;
2).dtsi檔案是被包含的,.dts檔案才是最終的Device Trees;
3).dts檔案包含了闆級資訊;
4)including工作主要是将including file覆寫到included file上面;
5)inclusion使用DT操作符/include/,或者在某些少量的核心釋出版中,由于DTS是使用了C preprocessor,是以推薦#include。
由這張圖可見,如果included file中的某項,被including file檔案定義了,則會使用後者的定義,也就是使用更上層更新的定義;如果沒有被定義,則添加進入。
1)bindings是device tree裡面可已包含的specific types and classes of devices。
2)compatible特征描述了節點編譯的specific binding;
3)當為一個裝置建立新的device tree時,應該建立a binding來描述裝置的全部細節。
=============================================
問題四:在哪裡可以找到Device Tree的文檔呢?
1)所有可被核心識别的Device Tree bindings在文檔Documentation/devicetree/bindings裡面;
2)每個binding文檔描述了哪些properties可以被接受,可以使用哪些值,哪些特征是必須的,哪些是可選的;
3)所有新的Device Tree bindings必須讓代碼維護者稽核,送出到[email protected]上。這用來保證它們的正确性和一緻性。
=============================================
問題五:Device Tree中的常見關鍵字含義是什麼?
Device Tree organization: top-level nodes
在裝置的最頂層節點上,一般可以發現如下這些:
cpus:描述了系統的CPU
memory:定義了RAM的位址和大小
chosen:定義了boot時被系統固件選擇或定義的參數;可用來傳遞kernel command line;
aliases:定義了certain nodes的shotcuts;
一個或多個總線定義;
一個或多個闆上裝置定義;
下面是am33xx.dtsi中的定義:
/ {
compatible = "ti,am33xx";
interrupt-parent = <&intc>;
aliases {
i2c0 = &i2c0;
i2c1 = &i2c1;
i2c2 = &i2c2;
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
serial3 = &uart3;
serial4 = &uart4;
serial5 = &uart5;
d_can0 = &dcan0;
d_can1 = &dcan1;
usb0 = &usb0;
usb1 = &usb1;
phy0 = &usb0_phy;
phy1 = &usb1_phy;
ethernet0 = &cpsw_emac0;
ethernet1 = &cpsw_emac1;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
[email protected] {
compatible = "arm,cortex-a8";
device_type = "cpu";
reg = <0>;
operating-points = <
720000 1285000
600000 1225000
500000 1125000
275000 1125000
>;
voltage-tolerance = <2>;
clocks = <&dpll_mpu_ck>;
clock-names = "cpu";
clock-latency = <300000>;
};
};
pmu {
compatible = "arm,cortex-a8-pmu";
interrupts = <3>;
};
soc {
compatible = "ti,omap-infra";
mpu {
compatible = "ti,omap3-mpu";
ti,hwmods = "mpu";
};
};
};
從上面的代碼裡面可以找出四個compatible,分别是:
top: compatible = "ti,am33xx";
cpu0: compatible = "arm,cortex-a8";
pmu: compatible = "arm,cortex-a8-pmu";
soc: compatible = "ti,omap-infra";
怎樣使用compatible呢?
方法一是用來比對DT_MACHINE結構體中的dt_compat域,方法二是使用of_machine_is_compatible函數。
在總線中,一般要定義compatile、#address-cells、#size-cells、ranges,比如:
i2c0: [email protected] {
compatible = "ti,omap4-i2c";
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c1";
reg = <0x44e0b000 0x1000>;
interrupts = <70>;
status = "disabled";
};
=============================================
問題六:有關DT的政策,你應該了解的~
DT是一種硬體描述,而不是一種配置。
它應該描述硬體的構成,和硬體工作的方式;
它不應該描述那種硬體配置你更加喜歡;
例如:
你可以在DT中描述是否硬體配置支援DMA;
但是你不要在DT中描述你是否想要DMA。
1)DT獨立于OS,它也需要非常穩定;
2)最初的設想是,DTBs由生産廠家燒寫進晶片中,使用者直接安裝系統就好了;
3)當Device Tree binding被定義,并且在DTBs使用之後,它就不能再改變,但可以擴充;
4)這意味着Device Tree binding是核心的二進制程式接口(ABI),它需要same care;
5)但是核心開發者意識到了這個很難達到,并且會減慢驅動程式的內建:
ARM Kernel Mini-submit discussions放松了這些限制;
未來在Kernel Summit時會有additional discussion;
Basic guidelines for binding design:
1) A precise compatible string is better than a vague one.
參考文獻:http://events.linuxfoundation.org/sites/events/files/slides/petazzoni-device-tree-dummies.pdf