1. 程式人生 > 其它 >linux uart 二進位制_Linux裝置樹專有名詞及語法規則詳解(上)

linux uart 二進位制_Linux裝置樹專有名詞及語法規則詳解(上)

技術標籤:linux uart 二進位制

裝置樹原始檔副檔名為.dts,但是我們在移植 Linux 的時候卻一直在使用.dtb 檔案,那麼 DTS 和DTB 這兩個檔案是什麼關係呢?DTS 是裝置樹原始碼檔案,DTB 是將DTS 編譯以後得到的二進位制檔案,Linux 核心和 uboot 只能 DTB 檔案。將.c 檔案編譯為.o 需要用到 gcc 編譯器,那麼將.dts 編譯為.dtb 需要什麼工具呢?需要用到DTC 工具。

雖然我們基本上不會從頭到尾重寫一個.dts 檔案,大多時候是直接在 SOC 廠商提供的.dts檔案上進行修改。但是 DTS 檔案語法我們還是需要詳細的學習一遍,因為我們肯定需要修改.dts檔案。大家不要看到要學習新的語法就覺得會很複雜,DTS 語法非常的人性化,是一種 ASCII文字檔案,不管是閱讀還是修改都很方便。

和 C 語言一樣,裝置樹也支援標頭檔案,裝置樹的標頭檔案副檔名為.dtsi。在 imx6ull-alientek-emmc.dts 中有如下所示內容:

12 #include 13 #include "imx6ull.dtsi"

第 12 行,使用“#include”來引用“input.h”這個.h 標頭檔案。

第 13 行,使用“#include”來引用“imx6ull.dtsi”這個.dtsi 標頭檔案。

看到這裡,大家可能會疑惑,不是說裝置樹的副檔名是.dtsi 嗎?為什麼也可以直接引用 C語言中的.h 標頭檔案呢?這裡並沒有錯,.dts 檔案引用C 語言中的.h 檔案,甚至也可以引用.dts 檔案,開啟 imx6ull-14x14-evk-gpmi-weim.dts 這個檔案,此檔案中有如下內容:

9 #include "imx6ull-14x14-evk.dts"

可以看出,示例程式碼中直接引用了.dts 檔案,因此在.dts 裝置樹檔案中,可以通過 “#include”來.h、.dtsi 和.dts 檔案。只是,我們在編寫裝置樹標頭檔案的時候最好選擇.dtsi 字尾。一般.dtsi 檔案用於描述 SOC 的內部外設資訊,比如 CPU 架構、主頻、外設暫存器地址範圍,比如 UART、IIC 等等。比如 imx6ull.dtsi 就是描述 I.MX6ULL 這顆 SOC 內部外設情況資訊的,內容如下:

10 #include 11 #include 12 #include 13 #include "imx6ull-pinfunc.h"14 #include "imx6ull-pinfunc-snvs.h"15 #include "skeleton.dtsi" 1617 / {18 aliases {19 can0 = &flexcan1;......48 };4950 cpus {51 #address-cells = <1>;52 #size-cells = <0>;5354 cpu0: 
[email protected]
{55 compatible = "arm,cortex-a7";56 device_type = "cpu";......89 };90 };9192 intc: [email protected] {93 compatible = "arm,cortex-a7-gic";94 #interrupt-cells = <3>;95 interrupt-controller;96 reg = <0x00a01000 0x1000>,97 <0x00a02000 0x100>;98 };99100 clocks {101 #address-cells = <1>;102 #size-cells = <0>;103104 ckil: [email protected] {105 compatible = "fixed-clock";106 reg = <0>;107 #clock-cells = <0>;108 clock-frequency = <32768>;109 clock-output-names = "ckil"; 110 };......135 };136137 soc {138 #address-cells = <1>;139 #size-cells = <1>;140 compatible = "simple-bus";141 interrupt-parent = ;142 ranges; 143144 busfreq {145 compatible = "fsl,imx_busfreq";......162 };197198 gpmi: [email protected]{199 compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi- nand";200 #address-cells = <1>;201 #size-cells = <1>;202 reg = <0x01806000 0x2000>, <0x01808000 0x4000>;......216 };......1177 };1178 };

示例程式碼中第 54~89 行就是 cpu0 這個裝置節點資訊,這個節點資訊描述了I.MX6ULL 這顆SOC 所使用的CPU 資訊,比如架構是 cortex-A7,頻率支援 996MHz、792MHz、528MHz、396MHz 和 198MHz 等等。在 imx6ull.dtsi 檔案中不僅僅描述了 cpu0 這一個節點資訊,I.MX6ULL 這顆SOC 所有的外設都描述的清清楚楚,比如 ecspi1~4、uart1~8、usbphy1~2、i2c1~4等等,關於這些裝置節點資訊的具體內容我們稍後在詳細的講解。

裝置樹是採用樹形結構來描述板子上的裝置資訊的檔案,每個裝置都是一個節點,叫做裝置節點,每個節點都通過一些屬性資訊來描述節點資訊,屬性就是鍵—值對。一下是從imx6ull.dtsi 檔案中縮減出來的裝置樹檔案內容:

1 / {2 aliases {3 can0 = &flexcan1;4 };56 cpus {7 #address-cells = <1>;8 #size-cells = <0>;910 cpu0: [email protected] {11 compatible = "arm,cortex-a7";12 device_type = "cpu";13 reg = <0>;14 };15 };1617 intc: [email protected] {18 compatible = "arm,cortex-a7-gic";19 #interrupt-cells = <3>;20 interrupt-controller;21 reg = <0x00a01000 0x1000>,22 <0x00a02000 0x100>;23 };24 }

第 1 行,“/”是根節點,每個裝置樹檔案只有一個根節點。細心的同學應該會發現,imx6ull.dtsi和 imx6ull-alientek-emmc.dts 這兩個檔案都有一個“/”根節點,這樣不會出錯嗎?不會的,因為這兩個“/”根節點的內容會合併成一個根節點。

第 2、6 和 17 行,aliases、cpus 和 intc 是三個子節點,在裝置樹中節點命名格式如下:

[email protected]

其中“node-name”是節點名字,為 ASCII 字串,節點名字應該能夠清晰的描述出節點的功能,比如“uart1”就表示這個節點是UART1 外設。“unit-address”一般表示裝置的地址或暫存器首地址,如果某個節點沒有地址或者暫存器的話“unit-address”可以不要,比如“[email protected]”、 “[email protected]”。

但是我們在示例程式碼中我們看到的節點命名卻如下所示:

cpu0:[email protected]

上述命令並不是“[email protected]”這樣的格式,而是用“:”隔開成了兩部分,“:”前面的是節點標籤(label),“:”後面的才是節點名字,格式如下所示:

label: [email protected]

引入 label 的目的就是為了方便訪問節點,可以直接通過&label 來訪問這個節點,比如通過&cpu0 就可以訪問“[email protected]”這個節點,而不需要輸入完整的節點名字。再比如節點 “intc: [email protected] ”, 節點 label 是 intc, 而節點名字就很長了,為“ [email protected]”。很明顯通過&intc 來訪問“[email protected]”這個節點要方便很多!

第 10 行,cpu0 也是一個節點,只是 cpu0 是 cpus 的子節點。

每個節點都有不同屬性,不同的屬性又有不同的內容,屬性都是鍵值對,值可以為空或任意的位元組流。裝置樹原始碼中常用的幾種資料形式如下所示:

①、字串

compatible = "arm,cortex-a7";

上述程式碼設定 compatible 屬性的值為字串“arm,cortex-a7”。

②、32 位無符號整數

reg = <0>;

上述程式碼設定 reg 屬性的值為 0,reg 的值也可以設定為一組值,比如:

reg = <0 0x123456 100>;

③、字串列表

屬性值也可以為字串列表,字串和字串之間採用“,”隔開,如下所示:

compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

上述程式碼設定屬性 compatible 的值為“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。

節點是由一堆的屬性組成,節點都是具體的裝置,不同的裝置需要的屬性不同,使用者可以自定義屬性。除了使用者自定義屬性,有很多屬性是標準屬性,Linux 下的很多外設驅動都會使用這些標準屬性,我們就來學習一下幾個常用的標準屬性。

1、compatible 屬性

compatible 屬性也叫做“相容性”屬性,這是非常重要的一個屬性!compatible 屬性的值是一個字串列表,compatible 屬性用於將裝置和驅動繫結起來。字串列表用於選擇裝置所要使用的驅動程式,compatible 屬性的值格式如下所示:

"manufacturer,model"

其中 manufacturer 表示廠商,model 一般是模組對應的驅動名字。比如 imx6ull-alientek-emmc.dts 中 sound 節點是 I.MX6U-ALPHA 開發板的音訊裝置節點,I.MX6U-ALPHA 開發板上的音訊晶片採用的歐勝(WOLFSON)出品的 WM8960,sound 節點的 compatible 屬性值如下:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";

屬性值有兩個,分別為“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示廠商是飛思卡爾,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驅動模組名字。sound這個裝置首先使用第一個相容值在Linux 核心裡面查詢,看看能不能找到與之匹配的驅動檔案,如果沒有找到的話就使用第二個相容值查詢,直到找到或者查詢完整個 Linux 核心也沒有找到對應的驅動。

一般驅動程式檔案都會有一個OF 匹配表,此 OF 匹配表儲存著一些 compatible 值,如果裝置節點的 compatible 屬性值和 OF 匹配表中的任何一個值相等,那麼就表示裝置可以使用這個驅動。比如在檔案 imx-wm8960.c 中有如下內容:

632 static const struct of_device_id imx_wm8960_dt_ids[] = { 633 { .compatible = "fsl,imx-audio-wm8960", },634 { /* sentinel */ } 635 };636 MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);637638 static struct platform_driver imx_wm8960_driver = {639 .driver = {640 .name = "imx-wm8960",641 .pm = &snd_soc_pm_ops,642 .of_match_table = imx_wm8960_dt_ids,643 },644 .probe = imx_wm8960_probe, 645 .remove = imx_wm8960_remove, 646 };

第 632~635 行的陣列 imx_wm8960_dt_ids 就是 imx-wm8960.c 這個驅動檔案的匹配表,此匹配表只有一個匹配值“fsl,imx-audio-wm8960”。如果在裝置樹中有哪個節點的 compatible 屬性值與此相等,那麼這個節點就會使用此驅動檔案。

第 642 行,wm8960 採用了 platform_driver 驅動模式,關於 platform_driver 驅動後面會講解。此行設定.of_match_table 為 imx_wm8960_dt_ids,也就是設定這個 platform_driver 所使用的OF 匹配表。

2、model 屬性

model 屬性值也是一個字串,一般 model 屬性描述裝置模組資訊,比如名字什麼的,比如:model = "wm8960-audio";

3、status 屬性

status 屬性看名字就知道是和裝置狀態有關的,status 屬性值也是字串,字串是裝置的狀態資訊,可選的狀態如表所示:

6b6bb5f0861820df97e1b29622148a2f.png

status 屬性值表

4、#address-cells 和#size-cells 屬性

這兩個屬性的值都是無符號 32 位整形,#address-cells 和#size-cells 這兩個屬性可以用在任何擁有子節點的裝置中,用於描述子節點的地址資訊。#address-cells 屬性值決定了子節點 reg 屬性中地址資訊所佔用的字長(32 位),#size-cells 屬性值決定了子節點 reg 屬性中長度資訊所佔的字長(32 位)。#address-cells 和#size-cells 表明了子節點應該如何編寫 reg 屬性值,一般 reg 屬性都是和地址有關的內容,和地址相關的資訊有兩種:起始地址和地址長度,reg 屬性的格式一為:

reg =

每個“address length”組合表示一個地址範圍,其中 address 是起始地址,length 是地址長度,#address-cells 表明 address 這個資料所佔用的字長,#size-cells 表明 length 這個資料所佔用的字長,比如:

1 spi4 { #address-cells 和#size-cells 屬性2 compatible = "spi-gpio";3 #address-cells = <1>;4 #size-cells = <0>;56 gpio_spi: [email protected] {7 compatible = "fairchild,74hc595";8 reg = <0>;9 };10 };1112 aips3: [email protected] {13 compatible = "fsl,aips-bus", "simple-bus";14 #address-cells = <1>;15 #size-cells = <1>;1617 dcp: [email protected] {18 compatible = "fsl,imx6sl-dcp";19 reg = <0x02280000 0x4000>;20 };21 };

第 2,3 行,節點 spi4 的#address-cells = <1>,#size-cells = <0>,說明 spi4 的子節點 reg 屬性中起始地址所佔用的字長為 1,地址長度所佔用的字長為 0。

第 8 行,子節點 gpio_spi: [email protected] 的 reg 屬性值為 <0>,因為父節點設定了#address- cells = <1>,#size-cells = <0>,因此 addres=0,沒有 length 的值,相當於設定了起始地址,而沒有設定地址長度。

第 14,15 行,設定 aips3: [email protected] 節點#address-cells = <1>,#size-cells = <1>,說明 aips3:

[email protected] 節點起始地址長度所佔用的字長為 1,地址長度所佔用的字長也為 1。

第 19 行,子節點 dcp: [email protected] 的 reg 屬性值為<0x02280000 0x4000>,因為父節點設定了#address-cells = <1>,#size-cells = <1>,address= 0x02280000,length= 0x4000,相當於設定了起始地址為 0x02280000,地址長度為 0x40000。

5、reg 屬性

reg 屬性前面已經提到過了,reg 屬性的值一般是(address,length)對。reg 屬性一般用於描述裝置地址空間資源資訊,一般都是某個外設的暫存器地址範圍資訊,比如在 imx6ull.dtsi 中有如下內容:

323 uart1: [email protected] {324 compatible = "fsl,imx6ul-uart",325 "fsl,imx6q-uart", "fsl,imx21-uart"; 326 reg = <0x02020000 0x4000>;327 interrupts = ;328 clocks = ,329 ;330 clock-names = "ipg", "per";331 status = "disabled"; 332 };

上述程式碼是節點 uart1,uart1 節點描述了 I.MX6ULL 的 UART1 相關資訊,重點是第 326 行的 reg 屬性。其中 uart1 的父節點 aips1: [email protected] 設定了#address-cells = <1>、#size- cells = <1>,因此 reg 屬性中 address=0x02020000,length=0x4000。查閱《I.MX6ULL 參考手冊》可知,I.MX6ULL 的 UART1 暫存器首地址為 0x02020000,但是UART1 的地址長度(範圍)並沒有 0x4000 這麼多,這裡我們重點是獲取UART1 暫存器首地址。

6、ranges 屬性

ranges 屬性值可以為空或者按照(child-bus-address,parent-bus-address,length)格式編寫的數字矩陣,ranges 是一個地址對映/轉換表,ranges 屬性每個專案由子地址、父地址和地址空間長度這三部分組成:

child-bus-address:子匯流排地址空間的實體地址,由父節點的#address-cells 確定此實體地址所佔用的字長。

parent-bus-address:父匯流排地址空間的實體地址,同樣由父節點的#address-cells 確定此實體地址所佔用的字長。

length:子地址空間的長度,由父節點的#size-cells 確定此地址長度所佔用的字長。

如果ranges 屬性值為空值,說明子地址空間和父地址空間完全相同,不需要進行地址轉換,對於我們所使用的I.MX6ULL 來說,子地址空間和父地址空間完全相同,因此會在 imx6ull.dtsi中找到大量的值為空的 ranges 屬性,如下所示:

137 soc {138 #address-cells = <1>;139 #size-cells = <1>;140 compatible = "simple-bus";141 interrupt-parent = ;142 ranges;......1177 }

第 142 行定義了 ranges 屬性,但是 ranges 屬性值為空。

ranges 屬性不為空的示例程式碼如下所示:

1 soc {2 compatible = "simple-bus";3 #address-cells = <1>;4 #size-cells = <1>;5 ranges = <0x0 0xe0000000 0x00100000>;67 serial {8 device_type = "serial";9 compatible = "ns16550";10 reg = <0x4600 0x100>;11 clock-frequency = <0>;12 interrupts = <0xA 0x8>;13 interrupt-parent = ;14 };15 };

第 5 行,節點 soc 定義的 ranges 屬性,值為<0x0 0xe0000000 0x00100000>,此屬性值指定了一個 1024KB(0x00100000)的地址範圍,子地址空間的物理起始地址為 0x0,父地址空間的物理起始地址為 0xe0000000。

第 6 行,serial 是串列埠裝置節點,reg 屬性定義了 serial 裝置暫存器的起始地址為 0x4600,暫存器長度為 0x100。經過地址轉換,serial 裝置可以從 0xe0004600 開始進行讀寫操作,0xe0004600=0x4600+0xe0000000。

7、name 屬性

name 屬性值為字串,name 屬性用於記錄節點名字,name 屬性已經被棄用,不推薦使用name 屬性,一些老的裝置樹檔案可能會使用此屬性。

8、device_type 屬性

device_type 屬性值為字串,IEEE 1275 會用到此屬性,用於描述裝置的 FCode,但是裝置樹沒有 FCode,所以此屬性也被拋棄了。此屬性只能用於 cpu 節點或者 memory 節點。

imx6ull.dtsi 的 cpu0 節點用到了此屬性,內容如下所示:

54 cpu0: [email protected] {55 compatible = "arm,cortex-a7";56 device_type = "cpu"; 57 reg = <0>;......89 };

關於標準屬性就講解這麼多,其他的比如中斷、IIC、SPI 等使用的標準屬性等後面文章再講解。