內容詳見《【正點原子】I.MX6U嵌入式Linux驅動開發指南》四十三章
開發板:imx6ull mini
虛擬機:VMware17
ubuntu:ubuntu20.04
一、什么是設備樹
視頻:第6.1講 Linux設備樹詳解-什么是設備樹?_嗶哩嗶哩_bilibili
對應《指南》43.1部分
????????uboot啟動時需要內核zImage 和 .dtb文件,其中dtb便是由設備樹文件dts(Device Tree Source)轉換而來。dts文件用樹形結構來描述板級信息,即開發板上的設備信息,如CPU數量、 內存基地址、IIC接了哪些設備等等。
? ? ? ? bootcmd命令中,80800000就是zImage在RAM中的存放起始地址,83000000就是設備樹在RAM中的存儲地址。
tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000
????????沒有設備樹以前,設備的板級信息等都在.c文件中進行配置,最后會被編碼到內核里面,又臭又長。因此引入了設備樹,用一個專用的文件格式.dts來描述板級信息,將這些玩意與linux分離。
????????如果不同的板子有相同的信息,就可以將這部分提取出來作為一個通用文件.dtsi,其他的.dts文件直接引用就可以。
? ? ? ? 一般.dts描述板級信息,.dtsi描述SOC級信息。
二、DTS文件
視頻:第6.2講 Linux設備樹詳解-DTS文件以及組織形式_嗶哩嗶哩_bilibili
對應《指南》43.2~3部分
2.1 編譯為dtb
命令:
# 進入到Linux源碼根目錄下
make all # 全部編譯,包括zImage、.ko文件等等
make dtbs # 將當前內核里所有.dts文件編譯為.dtb
make XXX.dtb # 將指定的dts文件編譯為dtb
示例:?
cd …………/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/
make dtbs
cd arch/arm/boot/dts/
ls *.dtb # 此時應當可以看到一大堆的dts文件
?2.2 dts語法
????????打開兩個文件:linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/boot/dts下的imx6ull-alientek-emmc.dts和imx6ull.dtsi。
? ? ? ? 打開兩個文檔:(4、參考資料)中的Devicetree SpecificationV0.2.pdf和Power_ePAPR_APPROVED_v1.12.pdf
/dts-v1/;
首先在imx6ull-alientek-emmc.dts開頭可以看到:
/dts-v1/;
(解釋在power_ePAPR_APPROVED_v1.12.pdf的96頁:)
????????The /dts-v1/; shall be present to identify the file as a version 1 DTS (dts files?without this tag will be treated by dtc as being in the obsolete version 0, which?uses a different format for integers in addition to other small but incompatible?changes).
????????/dts-v1/; 必須寫上,將文件標識為版本1 DTS(沒有此標簽的DTS文件將被dtc視為過時的版本0,而舊格式在整數的表示方式等方面與版本1不兼容)
節點命名規則
(Power_ePAPR_APPROVED_v1.12.pdf 15頁)
《指南》43.3.2部分
????????節點名完整寫法為:node-name@unit-address。其中node-name為設備名,unit-address一般是外設寄存器的起始地址,但也不一定,比如下面的例子。需要具體分析。
// imx6ull.dtsi 1096行
i2c4: i2c@021f8000 { // i2c為name,021f8000為i2c4寄存器起始絕對地址…………
};// imx6ull-alientek-emmc.dts 245行
&i2c1 {mag3110@0e { // 這里mag3110并不是IMX6ULL的外設,而是一個IIC外設,// 因此0e也不是外設寄存器起始地址,而是一個IIC地址…………};
};// imx6ull.dtsi 94行
intc: interrupt-controller@00a01000 { //↑這里有個冒號,冒號前面是一個標簽label,后面才是節點名// 這樣可以通過 &label 來訪問該節點,如&intc就可以訪問interrupt-controller@00a01000節點// 比如上面的&i2c1,就可以在imx6ull.dtsi 939行找到“i2c1: i2c@021a0000”// &定義的節點內容如果沒有則追加,如果已存在則替換,比如&i2c1里的內容就會替換原本i2c1: i2c@021a0000的內容…………
};
層級結構
Power_ePAPR_APPROVED_v1.12.pdf 14頁
?????????“/”表示設備樹文件的根節點,每個設備樹文件只有一個根節點。
????????imx6ull.dtsi和imx6ull-alientek-emmc.dts這兩個文件都有一個“/”根節點,這兩個“/”根節點的內容會合并成一個根節點。
2.3 設備樹在系統中的體現
第6.4講 Linux設備樹詳解-設備樹在根文件系統中的體現以及添加自定義節點_嗶哩嗶哩_bilibili
? ? ? ? 系統啟動后可以在根文件系統里看到設備樹的節點信息。
? ? ? ? 內核啟動時會解析設備樹文件,并在/proc/device-tree目錄下生成相應的設備樹節點文件,存放設備樹信息。cd到該路徑下使用ls命令,可以看到很到諸如chosen、memory、reserved-memory、backlight等文件。
????????這與imx6ull-alientek-emmc.dts中的結構一致:
? ? ? ? cat查看model的內容:
? ? ? ? 這與imx6ull-alientek-emmc.dts中model的屬性一致:
? ? ? ? cd到soc/aips-bus@02100000/i2c@021a0000下,可以看到這些文件:
? ? ? ? 但是imx6ull.dtsi的939行下只能看到:
? ? ? ? clock-frequency、fxls8471@1e、mag3110@0e、name、printrl-0、printrl-names是從哪來的?用&追加的。在imx6ull-alientek-emmc.dts的245行可以找到:
? ? ? ? 其中還修改了status,使用cat status可以看到status被覆蓋為了okay。
2.4 其他特殊節點
第6.5講 Linux設備樹詳解-設備樹特殊節點_嗶哩嗶哩_bilibili
《指南》43.6部分
2.4.1?aliases
在imx6ull.dtsi的開頭可以看到:
/ {aliases {can0 = &flexcan1;can1 = &flexcan2;…………};
};
同時,/soc/下定義了flexcan1:
/ {soc {flexcan1: can@02090000{……}……};……
};?
? ? ? ? 那么,can0 = &flexcan1; 等價于?can0 = &{/soc/can@02090000}; 或?can0 = "/soc/can@02090000";
????????aliases節點的主要功能就是定義別名,以方便訪問節點。不過一般會在節點命名的時候會加上label,然后通過&label來訪問節點。
2.4.2?chosen
? ? ? ? 主要目的是將uboot里的bootargs環境變量的值傳給linux內核作為命令行參數cmd line
? ? ? ? 在串口cd /proc/device-tree/chosen,可以看到bootargs、name、stdout-path三個。
但是imx6ull-alientek-emmc.dts中的chosen節點只定義了stdout-path一個:
chosen {stdout-path = &uart1;};
????????所以bootargs是從來的?(name先不管)當執行bootcmd中的bootz 80800000 - 83000000這句時,bootz層層調用,最終fdt_chosen函數讀取bootargs的值并傳給/chosen節點,供內核使用:
在alientek_uboot/common/fdt_common下可以找到fdt_chosen:
int fdt_chosen(void *fdt){int nodeoffset;int err;char *str; /* used to set string properties */err = fdt_check_header(fdt);if (err < 0) {printf("fdt_chosen: %s\n", fdt_strerror(err));return err;}/* find or create "/chosen" node. 如果存在子節點/chosen,則返回其偏移nodeoffset,否則創建并返回nodeoffset*/nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); // fdt為指向設備樹文件的指針,0表示根節點, chosen為要查找的子節點名稱if (nodeoffset < 0) // 失敗return nodeoffset;str = getenv("bootargs"); // 從uboot的環境變量中讀取bootargs的值if (str) { // 如果bootargs不為空err = fdt_setprop(fdt, nodeoffset, "bootargs", str, // 通過fdt_setprop向/chosen節點添加或修改bootargs屬性strlen(str) + 1);if (err < 0) { // 失敗printf("WARNING: could not set bootargs %s.\n",fdt_strerror(err));return err;}}return fdt_fixup_stdout(fdt, nodeoffset);
}
2.4 修改節點
2.4.1添加自定義節點
????????為方便查看,直接在imx6ull-alientek-emmc.dts的根節點下添加自定義節點:
// 自定義節點。添加到imx6ull-alientek-emmc.dts的148行mytestnode: mytest@0101 {};
# VSCODE終端
cd .../linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek # 到內核根目錄下
make dtbs # 編譯
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb .../tftpboot/ -f # 復制到自己存放zImage和dtb文件的目錄下,如果忘了哪個路徑就用vi /etc/default/tftpd-hpa命令看看# 串口
reboot # 重啟開發板ls /proc/device-tree/ # 此時應當能看到mytest@0101
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(打錯字了↓)?
2.4.2 使用&進行追加
????????在imx6ull-alientek-emmc.dts文件最末尾添加:
&intc{mytestnode{};
};
// 追加給imx6ull.dtsi 94行的intc
# VSCODE終端
cd .../linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek # 到內核根目錄下
make dtbs # 編譯
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb .../tftpboot/ -f # 復制到自己存放zImage和dtb文件的目錄下,如果忘了哪個路徑就用vi /etc/default/tftpd-hpa命令看看# 串口
reboot # 重啟開發板cd /proc/device-tree/interrupt-controller@00a01000/
ls # 此時能看到mytestnode