DTS基本知識
dts
硬件的相應信息都會寫在.dts為后綴的文件中,每一款硬件可以單獨寫一份xxxx.dts,一般在Linux源碼中存在大量的dts文件,對于arm架構可以在arch/arm/boot/dts找到相應的dts,一個dts文件對應一個ARM的machie。
dtsi
值得一提的是,對于一些相同的dts配置可以抽象到dtsi文件中,然后類似于C語言的方式可以include到dts文件中,對于同一個節點的設置情況,dts中的配置會覆蓋dtsi中的配置。
dtc
dtc是編譯dts的工具,可以在Ubuntu系統上通過指令apt-get install device-tree-compiler安裝dtc工具,不過在內核源碼scripts/dtc路徑下已經包含了dtc工具;
scripts/dtc/Makefile 文件內容如下:
示例代碼 43.2.1 scripts/dtc/Makefile 文件代碼段 1 hostprogs-y := dtc 2 always := $(hostprogs-y) 3 4 dtc-objs:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \ 5 srcpos.o checks.o util.o 6 dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o ......
可以看出,DTC 工具依賴于 dtc.c、flattree.c、fstree.c 等文件,最終編譯并鏈接出 DTC 這個主機文件。如果要編譯 DTS 文件的話只需要進入到 Linux 源碼根目錄下,然后執行如下命令:
make all
或者:
make dtbs
“make all”命令是編譯 Linux 源碼中的所有東西,包括 zImage,.ko 驅動模塊以及設備樹,如果只是編譯設備樹的話建議使用“make dtbs”命令。
dtb
dtb(Device Tree Blob),dts經過dtc編譯之后會得到dtb文件,dtb通過Bootloader引導程序加載到內核。所以Bootloader需要支持設備樹才行;Kernel也需要加入設備樹的支持;
基于 ARM 架構的 SOC 有很多種,一種 SOC 又可以制作出很多款板子,每個板子都有一個對應的 DTS 文件,那么如何確定編譯哪一個 DTS 文件呢?我們就以 I.MX6ULL 這款芯片對應的板子為例來看一下,打開 arch/arm/boot/dts/Makefile,有如下內容:
示例代碼 43.2.2 arch/arm/boot/dts/Makefile 文件代碼段 381 dtb-$(CONFIG_SOC_IMX6UL) += \ 382 imx6ul-14x14-ddr3-arm2.dtb \ 383 imx6ul-14x14-ddr3-arm2-emmc.dtb \ ...... 400 dtb-$(CONFIG_SOC_IMX6ULL) += \ 401 imx6ull-14x14-ddr3-arm2.dtb \ 402 imx6ull-14x14-ddr3-arm2-adc.dtb \ 403 imx6ull-14x14-ddr3-arm2-cs42888.dtb \ 404 imx6ull-14x14-ddr3-arm2-ecspi.dtb \ 405 imx6ull-14x14-ddr3-arm2-emmc.dtb \ 406 imx6ull-14x14-ddr3-arm2-epdc.dtb \ 407 imx6ull-14x14-ddr3-arm2-flexcan2.dtb \ 408 imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \ 409 imx6ull-14x14-ddr3-arm2-lcdif.dtb \ 410 imx6ull-14x14-ddr3-arm2-ldo.dtb \ 411 imx6ull-14x14-ddr3-arm2-qspi.dtb \ 412 imx6ull-14x14-ddr3-arm2-qspi-all.dtb \ 413 imx6ull-14x14-ddr3-arm2-tsc.dtb \ 414 imx6ull-14x14-ddr3-arm2-uart2.dtb \ 415 imx6ull-14x14-ddr3-arm2-usb.dtb \ 416 imx6ull-14x14-ddr3-arm2-wm8958.dtb \ 417 imx6ull-14x14-evk.dtb \ 418 imx6ull-14x14-evk-btwifi.dtb \ 419 imx6ull-14x14-evk-emmc.dtb \ 420 imx6ull-14x14-evk-gpmi-weim.dtb \ 421 imx6ull-14x14-evk-usb-certi.dtb \ 422 imx6ull-alientek-emmc.dtb \ 423 imx6ull-alientek-nand.dtb \ 424 imx6ull-9x9-evk.dtb \ 425 imx6ull-9x9-evk-btwifi.dtb \ 426 imx6ull-9x9-evk-ldo.dtb 427 dtb-$(CONFIG_SOC_IMX6SLL) += \ 428 imx6sll-lpddr2-arm2.dtb \ 429 imx6sll-lpddr3-arm2.dtb \ ......
可以看出,當選中 I.MX6ULL 這個 SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到I.MX6ULL 這個 SOC 的板子對應的.dts 文件都會被編譯為.dtb。如果我們使用 I.MX6ULL 新做了一個板子,只需要新建一個此板子對應的.dts 文件,然后將對應的.dtb 文件名添加到 dtb-$(CONFIG_SOC_IMX6ULL)下,這樣在編譯設備樹的時候就會將對應的.dts 編譯為二進制的.dtb文件。示例代碼 43.2.2 中第 422 和 423 行就是我們在給正點原子的 I.MX6U-ALPHA 開發板移植Linux 系統的時候添加的設備樹。
DTS 語法?
雖然我們基本上不會從頭到尾重寫一個.dts 文件,大多時候是直接在 SOC 廠商提供的.dts文件上進行修改。但是 DTS 文件語法我們還是需要詳細的學習一遍,因為我們肯定需要修改.dts文件。大家不要看到要學習新的語法就覺得會很復雜,DTS 語法非常的人性化,是一種 ASCII文本文件,不管是閱讀還是修改都很方便。本節我們就以 imx6ull-alientek-emmc.dts 這個文件為例來講解一下 DTS 語法。
dtb文件是由booloader解析還是kernel解析?
DTB文件主要是由內核解析的,但Bootloader也在一定程度上參與處理DTB文件。以下是對這一過程的詳細解釋:
Bootloader對DTB文件的處理
加載DTB到內存:Bootloader的主要職責之一是在系統啟動初期將操作系統內核和相關資源加載到內存中。在這個過程中,如果存在DTB文件(通常是由設備樹源文件編譯而來的二進制文件),Bootloader會將其加載到內存中的特定位置。例如,在U-Boot這樣的Bootloader中,會將DTB文件存儲在內存的某個預定義區域,以便后續內核能夠找到并使用它。
告知內核DTB位置:Bootloader在將控制權交給內核之前,需要將DTB文件在內存中的地址或其他相關信息傳遞給內核。這樣,內核在啟動時就能夠知道從哪里找到DTB文件,并開始對其進行解析。
內核對DTB文件的解析
驗證DTB文件格式:內核首先會對加載的DTB文件進行格式驗證,檢查其頭部信息是否正確,包括魔數、版本號等字段,以確保這是一個有效的設備樹描述文件。
解析節點和屬性:如果DTB文件格式正確,內核會進一步解析其中的節點和屬性信息。節點代表了硬件設備或組件,而屬性則描述了這些節點的各種特性和參數,如設備的型號、寄存器地址、中斷號等。內核會將這些信息組織成內部的數據結構,以便后續的設備驅動程序能夠方便地訪問和使用。
創建設備模型:基于解析得到的設備樹信息,內核會在內存中構建出整個系統的設備模型。這個設備模型反映了系統中所有硬件設備的層次結構和連接關系,為內核的設備管理和驅動程序的加載提供了基礎。
綜上所述,雖然Bootloader參與了DTB文件的加載和傳遞工作,但真正對DTB文件進行深入解析和利用的是Linux內核。通過解析DTB文件,內核能夠獲取硬件設備的信息,從而正確地初始化和管理這些設備,確保系統能夠正常運行。
設備樹dtb被解析后是存在內存里還是文件系統里?
設備樹DTB文件在解析后主要存在于內存中,但也可能與文件系統存在一定關聯。以下是對這一問題的詳細解釋:
內存中的存在形式
作為內核數據結構:設備樹DTB文件在被內核解析后,其內容會被轉化為一系列的內核數據結構,如
device_node
和property
等。這些數據結構存儲在內存中,用于描述系統中的設備及其屬性和配置信息。內存中的保留區域:在某些情況下,設備樹DTB文件可能會被加載到內存中的特定區域,以便內核能夠直接訪問和解析。例如,在ARM架構的系統中,設備樹DTB文件通常會被加載到內存的起始地址處。
文件系統中的存在形式
/sys文件系統:內核會在
/sys
文件系統的/sys/firmware/fdt/
目錄下創建設備樹的相關信息,包括設備樹的字符串表(strings)和結構體(structure)部分。這些信息以文件的形式存在,用戶可以通過讀取這些文件來查看設備樹的內容。/proc文件系統:雖然/proc文件系統主要用于提供內核運行時的信息,但在某些情況下,它也可以用來查看與設備樹相關的信息。例如,通過訪問
/proc/device-tree
目錄,可以獲取到設備樹的原始內容或經過處理后的信息。綜上所述,設備樹DTB文件在解析后主要存在于內存中,但也可能與文件系統(如/sys和/proc)存在一定的關聯,以便用戶和開發者能夠查看和管理設備樹的內容。這種設計既保證了設備樹信息的高效使用,又提供了方便的接口供外部訪問和調試。
.dtsi 頭文件
和 C 語言一樣,設備樹也支持頭文件,設備樹的頭文件擴展名為.dtsi。在 imx6ull-alientek
emmc.dts 中有如下所示內容:
示例代碼 43.3.1.1 imx6ull-alientek-emmc.dts 文件代碼段 12 #include <dt-bindings/input/input.h> 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 這個文件,此文件中有如下內容:
示例代碼 43.3.1.2 imx6ull-14x14-evk-gpmi-weim.dts 文件代碼段#include "imx6ull-14x14-evk.dts"
可以看出,示例代碼 43.3.1.2 中直接引用了.dts 文件,因此在.dts 設備樹文件中,可以通過“#include”來引用.h、.dtsi 和.dts 文件。只是,我們在編寫設備樹頭文件的時候最好選擇.dtsi 后綴。
一般.dtsi 文件用于描述 SOC 的內部外設信息,比如 CPU 架構、主頻、外設寄存器地址范圍,比如 UART、IIC 等等。比如 imx6ull.dtsi 就是描述 I.MX6ULL 這顆 SOC 內部外設情況信息的,內容如下:
示例代碼 43.3.1.3 中第 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 行,“/”是根節點,每個設備樹文件只有一個根節點。細心的同學應該會發現,imx6ull.dtsi和 imx6ull-alientek-emmc.dts 這兩個文件都有一個“/”根節點,這樣不會出錯嗎?不會的,因為這兩個“/”根節點的內容會合并成一個根節點。
dtsi中的根結點會和包含它的dts文件中的根結點沖突嗎?
在Linux內核中,設備樹(Device Tree)是一種用于描述硬件拓撲結構的數據結構。設備樹通常由多個文件組成,包括.dts(Device Tree Source)文件和.dtsi(Device Tree Source Include)文件。這些文件最終會被編譯成一個設備樹二進制文件(Device Tree Blob,簡稱DTB),供內核在啟動時使用。
關于DTSI中的根結點是否會和包含它的DTS文件中的根結點沖突,實際上并不會沖突。原因如下:
編譯時的節點合并:當包含DTSI文件的DTS文件被編譯成DTB時,編譯器會將DTSI中的節點合并到DTS中的相應位置。這意味著,盡管在源代碼層面看起來DTSI和DTS各自有一個根結點,但在最終編譯生成的設備樹中,這些節點會被合并成一個統一的樹狀結構。
單一的根節點:設備樹本質上是一棵樹,因此它只能有一個根節點。在編譯過程中,所有的節點都會被整合到這個單一的根節點下,形成一個完整的設備樹結構。
綜上所述,DTSI中的根結點不會與包含它的DTS文件中的根結點沖突。在設備樹的編譯和鏈接過程中,這些節點會被適當地合并和管理,以確保最終生成的設備樹具有一致且正確的結構。
dts中的根結點會和包含它的dts文件中的根結點沖突嗎?
在Linux中,DTS中的根結點和包含它的DTS文件中的根結點實際上不會沖突。
在一個DTS文件中,使用
#include
語句可以引用其他文件(通常是.dtsi文件),這些被引用的文件可以看作是對當前DTS文件的擴展或補充。當編譯器處理這種包含關系時,它會將被包含的內容合并到主DTS文件中,從而形成一個完整的設備樹結構。在這個過程中,并不會因為包含操作而產生多個根結點,最終生成的設備樹仍然只有一個根結點。其實,只要最終能保證層級關系不亂就行了,這還是很容易做到的。
第 2、6 和 17 行,aliases、cpus 和 intc 是三個子節點,在設備樹中節點命名格式如下:
node-name@unit-address
其中“node-name”是節點名字,為 ASCII 字符串,節點名字應該能夠清晰的描述出節點的功能,比如“uart1”就表示這個節點是 UART1 外設。“unit-address”一般表示設備的地址或寄存器首地址,如果某個節點沒有地址或者寄存器的話“unit-address”可以不要,比如“cpu@0”、“interrupt-controller@00a01000”。
但是我們在示例代碼 43.3.2.1 中我們看到的節點命名卻如下所示:
cpu0:cpu@0
上述命令并不是“node-name@unit-address”這樣的格式,而是用“:”隔開成了兩部分,“:”前面的是節點標簽(label),“:”后面的才是節點名字,格式如下所示:
label: node-name@unit-address
引入 label 的目的就是為了方便訪問節點,可以直接通過&label 來訪問這個節點,比如通過&cpu0 就可以訪問“cpu@0”這個節點,而不需要輸入完整的節點名字。再比如節點 “intc:interrupt-controller@00a01000”,節點 label 是 intc,而節點名字就很長了,為“interrupt-controller@00a01000”。很明顯通過&intc 來訪問“interrupt-controller@00a01000”這個節點要方便很多!
第 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 下的很多外設驅動都會使用這些標準屬性,本節我們就來學習一下幾個常用的標準屬性。
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 內核里面查找,看看能不能找到與之匹配的驅動文件,如果沒有找到的話就使用第二個兼容值查。
一般驅動程序文件都會有一個 OF 匹配表,此 OF 匹配表保存著一些 compatible 值,如果設備節點的compatible 屬性值和 OF 匹配表中的任何一個值相等,那么就表示設備可以使用這個驅動。比如在文件 imx-wm8960.c 中有如下內容:
示例代碼 43.3.3.1 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); 637 638 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 匹配表。
model 屬性
model 屬性值也是一個字符串,一般 model 屬性描述設備模塊信息,比如名字什么的,比如:
model = "wm8960-audio";
model屬性
設備樹中的model屬性是一個字符串類型的屬性,用于描述設備的型號或者模塊信息。以下是關于model屬性的詳細解釋:
屬性概述:
model 屬性在設備樹中扮演著重要的角色,它提供了設備的型號或模塊名稱等信息,幫助內核或其他軟件組件識別和適配不同的硬件平臺。
具體作用:
設備匹配:與compatible屬性一起,model屬性用于將設備與驅動程序進行匹配。內核在啟動時會檢查設備樹中的model屬性值,以確定加載哪個驅動程序來控制該設備。
硬件識別:通過指定設備的型號,操作系統能夠更好地了解所連接的硬件,并為其分配適當的資源和配置參數。
應用場景:
在嵌入式系統中,如使用ARM架構的開發板,model屬性通常用于指定開發板的型號,以便系統能夠正確初始化并運行相應的軟件。
在PCIe設備枚舉過程中,主機可以使用設備樹中的model屬性來確定設備的兼容性和驅動需求。
綜上所述,model屬性是設備樹中不可或缺的一部分,對于確保硬件的正確識別、配置以及與軟件的兼容至關重要。
status 屬性
status 屬性看名字就知道是和設備狀態有關的,status 屬性值也是字符串,字符串是設備的狀態信息,可選的狀態如表 43.3.3.1 所示:
#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 = <address1 length1 address2 length2 address3 length3……>
每個“address length”組合表示一個地址范圍,其中 address 是起始地址,length 是地址長度,#address-cells 表明 address 這個數據所占用的字長,#size-cells 表明 length 這個數據所占用的字長,比如:
第 3,4 行,節點 spi4 的#address-cells = <1>,#size-cells = <0>,說明 spi4 的子節點 reg 屬性中起始地址所占用的字長為 1,地址長度所占用的字長為 0。
第 8 行,子節點 gpio_spi: gpio_spi@0 的 reg 屬性值為 <0>,因為父節點設置了#address cells = <1>,#size-cells = <0>,因此 addres=0,沒有 length 的值,相當于設置了起始地址,而沒有設置地址長度。
第 14,15 行,設置 aips3: aips-bus@02200000 節點#address-cells = <1>,#size-cells = <1>,說明 aips3: aips-bus@02200000 節點起始地址長度所占用的字長為 1,地址長度所占用的字長也為 1。
第 19 行,子節點 dcp: dcp@02280000 的 reg 屬性值為<0x02280000 0x4000>,因為父節點設置了#address-cells = <1>,#size-cells = <1>,address= 0x02280000,length= 0x4000,相當于設置了起始地址為 0x02280000,地址長度為 0x40000。
不太明白,地址和長度不都是1個字長嗎?難道還有超過1個字長的地址或者長度?
參考:終于搞懂Linux 設備樹中的#address-cells,#size-cells 和reg 屬性-CSDN博客
再看個例子:
alphaled {#address-cells = <1>;#size-cells = <1>;compatible = "atkalpha-led";status = "okay";reg = < 0X020C406C 0X04 /* CCM_CCGR1_BAE */0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */0X0209C000 0X04 /* GPIO1_DR_BASE */0X0209C004 0X04>; /* GPIO1_GDIR_BASE */};
這里
address-cells = 1
,size-cells = 1
?, 表示?reg
屬性中,地址信息的長度是1
個字長,地址長度信息也是1
個字長。可見,大部分情況下,都是1個字長。
有沒有不是1個字長的?
比如:
pci@1,0 {#address-cells = <3>;#size-cells = <2>;compatible = "intel,ce4100-pci", "pci";device_type = "pci";bus-range = <1 1>;reg = <0x0800 0x0 0x0 0x0 0x0>;... }
這里
address-cells = 3
,size-cells = 2
?, 表示?reg
屬性中,地址信息的長度是3
個字長,地址長度信息是2
個字長,即是:address = 0x0800 0x0 0x0, length = 0x00 0x00,具體含義具體對待。此時,地址和長度均無法通過1個字長來描述。
在設備樹(Device Tree)中,
#size-cells
前面的 符號具有特定的含義和作用。以下是對其的解釋:屬性標識作用
?區分普通屬性名與特殊屬性:在設備樹的語法規則中,以
#
開頭的屬性通常具有特殊的意義或用途,與常規的屬性名有所區別。這種標識方式能夠讓設備樹的解析器快速識別和正確處理這些特殊屬性,避免與普通屬性產生混淆。例如,#size-cells
表示該屬性是用于描述節點地址空間中尺寸信息的特殊屬性,而普通的屬性如compatible
、model
等則用于描述設備的兼容性、型號等信息。明確屬性語義類別:
#
符號的使用有助于將與地址、大小相關的屬性歸類在一起,方便管理和理解。像#address-cells
和#size-cells
都屬于這類描述節點地址信息的屬性,通過#
符號的統一標識,可以清晰地表明它們在語義上的關聯性和特殊性。
reg 屬性
reg 屬性前面已經提到過了,reg 屬性的值一般是(address,length)對。reg 屬性一般用于描述設備地址空間資源信息,一般都是某個外設的寄存器地址范圍信息,比如在 imx6ull.dtsi 中有如下內容:
?
上述代碼是節點 uart1,uart1 節點描述了 I.MX6ULL 的 UART1 相關信息,重點是第 326 行的 reg 屬性。其中 uart1 的父節點 aips1: aips-bus@02000000 設置了#address-cells = <1>、#sizecells = <1>,因此 reg 屬性中 address=0x02020000,length=0x4000。查閱《I.MX6ULL 參考手冊》可知,I.MX6ULL 的 UART1 寄存器首地址為 0x02020000,但是 UART1 的地址長度(范圍)并沒有 0x4000 這么多,這里我們重點是獲取 UART1 寄存器首地址。
進一步思考。
假設reg里的數據是這樣的
reg = <0x08000000 0x4000 0x08006000 0x5000 0x0800a000 0x4000>
你知道這里面哪些是地址哪些是長度嗎?只知道用空格隔開了一些數據。而且,就算我們能從中看出來哪個是長度哪個是地址,但計算機怎判斷呢?這就要我們告訴計算機,地址和長度所占的數據個數是怎么樣的。
假設
#address-cells = <1>;
#size-cells = <1>;
那么上述6個數的順序就是:<地址 長度 地址 長度 地址 長度>
假設
#address-cells = <1>;
#size-cells = <0>;
那么上述6個數的順序就是:<地址 地址 地址 地址 地址 地址>,即沒有哪個數據表示長度了
假設
#address-cells = <3>;
#size-cells = <3>;
那么上述6個數的順序就是:<地址 長度>,前面三個數都表示地址,后面三個數都表示長度。
只是,一般都是
#address-cells = <1>;
#size-cells = <0>;
或者
#address-cells = <1>;
#size-cells = <1>;
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 屬性,如下所示:
?
第 142 行定義了 ranges 屬性,但是 ranges 屬性值為空。
ranges 屬性不為空的示例代碼如下所示:
?
第 5 行,節點 soc 定義的 ranges 屬性,值為<0x0 0xe0000000 0x00100000>,此屬性值指定了一個 1024KB(0x00100000)的地址范圍,子地址空間的物理起始地址為 0x0,父地址空間的物理起始地址為 0xe0000000。
第 10 行,serial 是串口設備節點,reg 屬性定義了 serial 設備寄存器的起始地址為 0x4600,
寄存器長度為 0x100。經過地址轉換,serial 設備可以從 0xe0004600 開始進行讀寫操作,
0xe0004600=0x4600+0xe0000000。
name 屬性
name 屬性值為字符串,name 屬性用于記錄節點名字,name 屬性已經被棄用,不推薦使用name 屬性,一些老的設備樹文件可能會使用此屬性。
device_type 屬性
device_type 屬性值為字符串,IEEE 1275 會用到此屬性,用于描述設備的 FCode,但是設備樹沒有 FCode,所以此屬性也被拋棄了。此屬性只能用于 cpu 節點或者 memory 節點。imx6ull.dtsi 的 cpu0 節點用到了此屬性,內容如下所示:
?
在Linux的設備樹(Device Tree)中,
device_type
屬性是一個用于標識設備節點類型的字符串類型屬性。以下是關于device_type
屬性的詳細解釋:
基本概念
device_type
屬性用于描述設備樹中各個設備節點的具體類型,它為內核或用戶空間提供了一種識別和區分不同設備的機制。通過這個屬性,系統可以知道某個節點所代表的是CPU、內存、I2C設備、SPI設備還是其他類型的設備。常見取值
CPU相關:對于CPU設備節點,
device_type
通常被設置為“cpu”,以明確表示該節點是一個CPU設備。內存相關:內存設備節點的
device_type
一般被設置為“memory”,用于標識內存設備。存儲設備:如MMC(MultiMediaCard)設備節點,其
device_type
可能被設置為“mmc”,以表明這是一個MMC存儲設備。通信接口設備:例如I2C設備節點的
device_type
可以是“i2c”,SPI設備節點的device_type
可以是“spi”,USB設備節點的device_type
可以是“usb”等,這些設置有助于驅動程序正確地識別和處理相應的通信接口設備。網絡設備:以太網設備節點的
device_type
可能被設置為“ethernet”,從而讓系統能夠識別以太網相關的設備。其他外設設備:像GPIO設備節點的
device_type
可以被設置為“gpio”,PWM設備節點的device_type
可以是“pwm”等,以便系統對各種外設進行準確的識別和管理。作用與意義
設備識別與驅動綁定:內核在啟動或運行時會讀取設備樹中的信息,根據
device_type
屬性來確定每個設備節點的類型,并將相應的設備與對應的驅動程序進行綁定。這樣,當系統需要與某個設備進行交互時,就能夠找到正確的驅動程序來操作該設備。硬件資源管理:通過明確設備的類型,系統可以更好地管理和分配硬件資源。例如,對于不同類型的設備,系統可以根據其特性和需求分配適當的內存、中斷等資源,以確保設備的正常運行。
兼容性與擴展性:使用
device_type
屬性可以提高系統的兼容性和擴展性。在添加新的設備類型時,只需要在設備樹中為新設備節點設置正確的device_type
屬性,并編寫相應的驅動程序即可,無需對系統的核心代碼進行大規模的修改。綜上所述,device_type屬性在Linux的設備樹中扮演著至關重要的角色,它不僅幫助內核和系統準確地識別和區分各種設備節點,還促進了設備與驅動程序的正確綁定以及硬件資源的有效管理。
注意,名稱并不是強制的,是自定義的,只不過一般很多名稱都是約定俗成的而已。
關于標準屬性就講解這么多,其他的比如中斷、IIC、SPI 等使用的標準屬性等到具體的例程再講解。
特殊節點
/aliases 子節點
aliases 節點的主要功能就是定義別名,定義別名的目的就是為了方便訪問節點。例如:定義 flexcan1 和 flexcan2 的別名是 can0 和 can1。
aliases {can0 = &flexcan1;can1 = &flexcan2; };
單詞 aliases 的意思是“別名”,因此 aliases 節點的主要功能就是定義別名,定義別名的目的就是為了方便訪問節點。不過我們一般會在節點命名的時候會加上 label,然后通過&label來訪問節點,這樣也很方便,而且設備樹里面大量的使用&label 的形式來訪問節點。
/memory 子節點
所有設備樹都需要一個memory設備節點,它描述了系統的物理內存布局。如果系統有多個內存塊,可以創建多個memory節點,或者可以在單個memory節點的reg屬性中指定這些地址范圍和內存空間大小。
例如:一個64位的系統有兩塊內存空間:RAM1: 起始地址是0x0,地址空間是 0x80000000;RAM2: 起始地址是0x10000000,地址空間也是0x80000000;同時根節點下的 #address-cells = <2>和#size-cells = <2>,這個memory節點描述為:
memory@0 {device_type = "memory";reg = <0x00000000 0x00000000 0x00000000 0x80000000 0x00000000 0x10000000 0x00000000 0x80000000>; };
或者:
memory@0 {device_type = "memory";reg = <0x00000000 0x00000000 0x00000000 0x80000000>; };memory@10000000 {device_type = "memory";reg = <0x00000000 0x10000000 0x00000000 0x80000000>; };
?更多待補充。
/chosen 子節點
chosen 并不是一個真實的設備, chosen 節點主要是為了 uboot 向 Linux 內核傳遞數據,重點是 bootargs 參數。例如:
chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; };
更多待補充。
在Linux設備樹中,可以自定義屬性。以下是關于自定義屬性的定義和引用方法的詳細解釋:
自定義屬性的定義
自定義屬性允許用戶根據特定硬件設備的特性和需求來定義屬性名和屬性值。這些屬性在Linux內核和驅動程序中并不具有特殊的含義,而是由用戶在設備樹文件中定義,并在驅動程序中通過特定的API接口進行讀取和處理。
自定義屬性的引用
在設備樹文件中定義自定義屬性:
自定義屬性以鍵值對的形式存在,屬性名由用戶自定義,屬性值可以是整數、字符串、布爾值等各種類型。例如:
&i2c1 {my-device@48 {compatible = "my-company,my-device";reg = <0x48>;example-config = <1 9600>; /* 工作模式為1,波特率為9600 */}; };
在這個例子中,
example-config
就是一個自定義屬性,用于描述設備的特定配置選項。在驅動程序中讀取自定義屬性:
在Linux驅動程序中,可以使用如
of_property_read_u32
、of_property_read_string
等函數來讀取設備樹中的自定義屬性。例如:#include <linux> static int my_driver_probe(struct platform_device *pdev) {struct device_node *np = pdev->dev.of_node;u32 example_config;int ret;/* 讀取自定義屬性example-config */ret = of_property_read_u32(np, "example-config", &example_config);if (ret) {dev_err(&pdev->dev, "Failed to read 'example-config' property");return ret;}/* 使用example_config進行設備配置 */// ...return 0; }
在這個示例中,驅動程序在
probe
函數中通過of_property_read_u32
函數讀取了設備樹中的自定義屬性example-config
,并根據該屬性的值進行設備配置。綜上所述,Linux設備樹允許用戶自定義屬性,并通過特定的API接口在驅動程序中讀取和處理這些屬性。這為用戶提供了靈活的方式來描述硬件設備的特定特性和配置選項。
如何在設備樹中表示和引用設備信息
先分析一個完整的設備樹,是怎么表達各種外設信息的。
以imux6ull開發板為例進行說明。
這個文件里就一個設備信息
?
才這么點內容,是不是出問題了?當然不是,我們知道dts文件是可包含的,所以,最終形成的一個完整文件就是把各文件給整合起來,以根結點為起始的一個文件。 根據前面了解到的知識,這里應該是對已有外設的信息的追加,不過我們暫時不管這里,而是根據包含關系找到起始的地方,然后順著往下看。
梳理后,有如下包含關系:
imx6ull-14x14-evk-emmc.dts包含了imx6ull-14x14-evk.dts;
imx6ull-14x14-evk.dts又包含了imx6ull.dtsi;
imx6ull.dtsi又包含了skeleton.dtsi;
skeleton:骨骼,骨架;框架;梗概,綱要
再往上就沒有了,所以,先看看skeleton.dtsi文件
?
skeleton.dtsi 文件是用于設備樹的通用頭文件,通常被包含在其他設備樹源文件(.dts 或 .dtsi)中。它定義了一些基本節點和屬性,以滿足設備樹的基本要求。以下是對 skeleton.dtsi 文件內容的詳細解釋:
根節點
/ {:這是設備樹的根節點,每個設備樹文件有且僅有一個根節點。在包含 skeleton.dtsi 的文件中,這個根節點會與其他文件的根節點合并,最終形成一個統一的設備樹結構。
地址單元和大小單元
#address-cells = <1>;:指定了該設備樹中地址單元的數量。對于大多數簡單的設備樹節點,地址單元數量為 1 即可滿足需求。這意味著在描述設備的地址信息時,使用一個單元(通常是 32 位或 64 位的值)來表示地址。例如,對于內存設備的基地址,就可以用一個這樣的地址單元來指定。
#size-cells = <1>;:定義了大小單元的數量。同樣,對于很多設備節點,大小單元數量設為 1 就足夠了。它用于描述設備所占用的內存或資源的大小,比如內存區域的大小、寄存器塊的大小等,以一個單元的值來表示具體的字節數。
chosen 節點
chosen { };:這是一個特殊的節點,通常用于在設備樹中標記一些被選擇或推薦的設置。在內核引導過程中,引導加載程序可能會根據這個節點中的信息來進行一些特定的配置或初始化操作。不過在 skeleton.dtsi 中,這個節點暫時沒有包含任何具體的子節點或屬性。
aliases 節點
aliases { };:用于創建設備樹中的別名。通過別名,可以給設備樹中的節點起一個更易讀、更有意義的名字,方便在其他部分的設備樹文件中引用。這有助于提高設備樹的可讀性和可維護性。但在 skeleton.dtsi 中,目前也沒有具體的別名定義。
memory 節點
memory { device_type = "memory"; reg = <0 0>; };:定義了一個內存設備節點。device_type = "memory" 明確指出這是一個內存類型的設備。reg = <0 0> 表示該內存設備的起始地址為 0x00000000,大小為 0x00000000(這里的大小為 0 可能是一個占位符,實際的內存大小會在具體的設備樹文件中根據硬件情況進行設置)。這個內存節點是設備樹中描述系統內存的基礎模板,其他具體的內存設備節點可以在繼承這個節點的基礎上進行修改和擴展。
總的來說,skeleton.dtsi 文件提供了設備樹的基本框架和一些常用的節點定義,使得其他的設備樹文件可以在此基礎上進行擴展和定制,從而減少代碼的重復編寫,提高設備樹的可維護性和可擴展性。 可見,該文件只是一個最基礎的框架性模版,沒有具體的設備信息。
接下來看imx6ull.dtsi這個文件,這個文件的內容比較多。
imx6ull.dtsi 文件是一個設備樹包含文件(Device Tree Include File),用于描述 I.MX6ULL SoC(System on Chip)的內部外設信息。可見,是一個描述芯片信息的設備樹包含文件。因此,建議不要在該文件中包含片外的外設。 不過雖然看著很長,其實總體上就那么幾類信息,大部分的長度都在soc片上外設或者控制器等上面。 imx6ull.dtsi 文件是一個設備樹包含文件(Device Tree Include File),用于描述 I.MX6ULL SoC(System on Chip)的內部外設信息。以下是對該文件內容的詳細解釋:
頭文件引用
時鐘、GPIO、中斷控制器相關頭文件:
#include <dt-bindings>:包含了與 I.MX6ULL 時鐘配置相關的綁定信息,這些信息定義了時鐘的各種屬性、頻率范圍等,以便在設備樹中對時鐘進行準確的描述和配置。
#include <dt-bindings>:提供了 GPIO(通用輸入輸出端口)的相關信息,如 GPIO 的編號、功能、狀態等定義,使得在設備樹中可以正確地描述和使用 GPIO 引腳。
#include <dt-bindings>:包含了 ARM 通用中斷控制器(GIC)的綁定信息,這對于處理 I.MX6ULL 的中斷機制非常重要,通過該頭文件可以在設備樹中準確地設置中斷控制器的屬性和行為。
針腳功能相關頭文件:
#include "imx6ull-pinfunc.h" 和 #include "imx6ull-pinfunc-snvs.h":這兩個頭文件定義了 I.MX6ULL 的針腳功能和特殊非易失性存儲(SNVS)相關的針腳功能。它們指定了各個針腳的用途、電氣特性等,對于連接外部設備以及正確配置芯片的引腳功能至關重要。
基礎框架頭文件:
#include "skeleton.dtsi":這是一個基礎的設備樹框架頭文件,通常包含了一些通用的設備樹節點定義和基本的結構信息,為后續具體的設備樹描述提供了一個基礎模板。
根節點及子節點
根節點:
/ {:這是設備樹的根節點,所有的其他節點都是直接或間接地掛在這個根節點下面。在設備樹的語法中,根節點使用反斜杠“/”表示,它是整個設備樹的起點,代表了整個系統的硬件資源。
別名節點(aliases):
aliases { can0 = &flexcan1; };:別名節點用于為設備樹中的節點創建易于理解和使用的別名。在這里,將 &flexcan1 這個節點命名為 can0,這樣在其他地方引用 can0 時,實際上就是指代 flexcan1 節點。這種別名機制方便了設備樹的編寫和閱讀,尤其是對于復雜的硬件系統,可以通過有意義的別名來快速定位和引用特定的硬件設備。
CPU 節點(cpus):
cpus { #address-cells = <1>; #size-cells = <0>; cpu0: cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0>; }; };:這個節點描述了 I.MX6ULL SoC 上的 CPU 信息。其中,#address-cells = <1> 和 #size-cells = <0> 定義了地址單元和大小單元的數量,對于 CPU 節點來說,這里不需要大小單元。cpu0 是第一個 CPU 節點,其屬性包括 compatible 字段,表示該 CPU 與 “arm,cortex-a7” 兼容,device_type 字段表明這是一個 CPU 類型的設備,reg 字段給出了 CPU 的寄存器地址范圍,這里是 <0>,表示基地址為 0。
中斷控制器節點(intc):
intc: interrupt-controller@00a01000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; interrupt-controller; reg = <0x00a01000 0x1000>, <0x00a02000 0x100>; };:該節點定義了 I.MX6ULL 的中斷控制器。compatible 字段指出該中斷控制器與 “arm,cortex-a7-gic” 兼容,#interrupt-cells = <3> 表示中斷控制器需要 3 個單元來描述中斷信息。interrupt-controller; 是一個特殊的屬性,用于標識這是一個中斷控制器節點。reg 字段定義了中斷控制器的寄存器地址范圍,這里分別是 0x00a01000 - 0x00a02000。
時鐘節點(clocks):
clocks { #address-cells = <1>; #size-cells = <0>; ckil: clock@0 { compatible = "fixed-clock"; reg = <0>; #clock-cells = <0>; clock-frequency = <32768>; clock-output-names = "ckil"; }; };:此節點描述了 I.MX6ULL 的一個固定時鐘。ckil 是時鐘的名稱,compatible 字段表示這是一個固定時鐘類型,reg 字段為空,表示該時鐘沒有特定的寄存器地址(因為它是固定頻率的時鐘,不需要配置寄存器)。#clock-cells = <0> 說明不需要額外的單元來描述時鐘屬性,clock-frequency = <32768> 設定了時鐘的頻率為 32768Hz,clock-output-names = "ckil" 給出了時鐘輸出的名稱。
系統節點(soc):
soc { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; interrupt-parent = <&gpc>; ranges; busfreq { compatible = "fsl,imx_busfreq"; }; };:該節點代表了一個簡化的總線結構,用于描述 SoC 中的總線相關信息。#address-cells = <1> 和 #size-cells = <1> 定義了地址和大小的單元數量。compatible = "simple-bus" 表示這是一個簡單的總線類型。interrupt-parent = <&gpc>; 指定了中斷父節點是全局平臺控制器(GPC),這意味著該總線的中斷信號將連接到 GPC。ranges; 表示總線的范圍信息(具體內容未詳細展開)。busfreq 子節點進一步描述了總線頻率相關的信息,這里使用了 "fsl,imx_busfreq" 作為兼容屬性,但具體的頻率值等信息未在這段代碼中給出。
總結來說,imx6ull.dtsi 文件通過一系列的節點和屬性定義,詳細描述了 I.MX6ULL SoC 的內部外設信息,包括 CPU、中斷控制器、時鐘、總線以及特定的外圍設備(如 GPMI NAND)等。這些信息對于構建基于 I.MX6ULL 的嵌入式系統的設備樹至關重要,因為它們告訴操作系統如何正確地識別、配置和訪問這些硬件組件。 這里先了解設備樹整體的框架,后續再展開一些重點的地方來進一步說明。
再來看看imx6ull-14x14-evk.dts文件,這個文件的內容也比較長,其實就是描述特定開發板的設備信息的,也有對芯片信息的進一步補充。 imx6ull-14x14-evk.dts 文件是用于描述 i.MX6ULL 14x14 EVK(Evaluation Kit,評估套件)硬件的 Device Tree Source(設備樹源)文件。以下是對該文件內容的詳細解釋:
頭文件引用
#include <dt-bindings>:引入與輸入設備相關的綁定定義,為后續描述輸入設備節點提供基礎信息和屬性綁定。
#include "imx6ull.dtsi":包含 i.MX6ULL 芯片通用的設備樹包含文件,其中定義了 i.MX6ULL 芯片相關的外設描述、時鐘、中斷控制器等基本信息,可在當前文件中直接使用這些預定義的節點和屬性。
根節點及基本屬性
/ {:根節點,代表整個設備樹的根,所有其他的節點都是直接或間接掛在這個根節點下面。
model = "Freescale i.MX6 ULL 14x14 EVK Board";:定義了該設備樹所對應的開發板型號,即 Freescale(現 NXP)的 i.MX6ULL 14x14 EVK 評估板。
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";:指定了設備的兼容屬性,表明該設備與 “fsl,imx6ull-14x14-evk” 和 “fsl,imx6ull” 兼容,這樣內核在加載設備樹時可以根據這些兼容字符串來匹配相應的驅動程序。
chosen { stdout-path = &uart1; };:選擇了默認的控制臺輸出設備,這里將 uart1 設置為系統啟動時的默認控制臺輸出端口,以便在系統啟動過程中輸出日志信息。
內存節點
memory { reg = <0x80000000 0x20000000>; };:描述了內存區域的起始地址和大小,0x80000000 是內存的起始物理地址,0x20000000 是內存的大小,表示該評估板上有 512MB 的內存可供使用。
保留內存節點
reserved-memory {:定義了一些需要保留的內存區域,這些區域通常用于特定的硬件功能或未來的擴展,不允許常規的內存訪問。
#address-cells = <1>; #size-cells = <1>; ranges;:指定了地址單元和大小單元的數量,以及使用范圍來描述保留內存的區域。
linux,cma { compatible = "shared-dma-pool"; reusable; size = <0x14000000>; linux,cma-default; };:保留了一塊用于 Contiguous Memory Allocator(CMA)的內存區域,大小為 0x14000000 字節(約 36MB),compatible 屬性為 "shared-dma-pool",表示這是一個共享的 DMA 池,reusable 表示該內存區域可重復使用,linux,cma-default 表示這是默認的 CMA 區域。
背光節點
backlight { compatible = "pwm-backlight"; pwms = <&pwm1 0 5000000>; brightness-levels = <0 4 8 16 32 64 128 255>; default-brightness-level = <6>; status = "okay"; };:描述了背光設備的相關信息。compatible 屬性為 "pwm-backlight",表示該背光設備使用 PWM(脈寬調制)控制。pwms 屬性指定了用于控制背光的 PWM 控制器及其參數,這里使用了 pwm1 通道 0,頻率為 5MHz。brightness-levels 定義了背光的亮度級別,從 0 到 255 共 256 級亮度可調。default-brightness-level 設置了默認的亮度級別為 6。status = "okay" 表示該背光設備工作正常。
攝像頭節點
pxp_v4l2 { compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2"; status = "okay"; };:定義了一個 V4L2(Video for Linux Two)接口的攝像頭設備節點。compatible 屬性列出了該攝像頭設備所兼容的驅動類型,包括 "fsl,imx6ul-pxp-v4l2"、"fsl,imx6sx-pxp-v4l2" 和 "fsl,imx6sl-pxp-v4l2",表示該攝像頭可以在使用這些 SoC(System on Chip)的開發板上通過相應的驅動程序進行視頻采集等工作。status = "okay" 表示該攝像頭設備工作正常。
電源管理節點
regulators { compatible = "simple-bus"; //省略... };:描述了電源管理相關的信息。compatible 屬性為 "simple-bus",表示這是一個簡單的電源總線。此處省略了具體的電壓調節器等電源組件的定義,實際的文件中可能會詳細列出各個電源域、電壓調節器的類型、輸出電壓等信息,用于內核對電源的管理。
CPU 供電相關節點
&cpu0 { arm-supply = <?_arm>; soc-supply = <?_soc>; dc-supply = <?_gpio_dvfs>; };:針對 CPU0 的供電配置節點。arm-supply、soc-supply 和 dc-supply 分別指定了 CPU 的不同供電域所使用的電源控制器或電源軌,如 ?_arm、?_soc 和 ?_gpio_dvfs,這些是在其他部分定義好的電源相關的節點,用于為 CPU 提供穩定的電源供應。
時鐘節點
&clks { assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; assigned-clock-rates = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; };:描述了時鐘相關的配置信息。assigned-clocks 和 assigned-clock-rates 屬性分別指定了分配給設備的時鐘源和時鐘頻率,這里使用了 &clks IMX6UL_CLK_PLL4_AUDIO_DIV,表示使用的是 i.MX6ULL 的 PLL4 音頻時鐘分頻后的時鐘源作為設備的時鐘輸入,具體的時鐘頻率值也在其他地方定義。
其他節點
總的來說,imx6ull-14x14-evk.dts 文件通過上述各種節點和屬性的定義,詳細地描述了 i.MX6ULL 14x14 EVK 開發板的硬件資源和配置信息,包括內存、背光、攝像頭、電源管理、CPU 供電、時鐘等方面,使得操作系統內核能夠正確地識別和使用這些硬件資源,從而實現對開發板的支持和管理。在實際的嵌入式系統開發中,根據具體的硬件設計和需求,還可能需要對該文件進行進一步的修改和擴展。
總體框架暫時就了解這么多,接下來,看一些重點細節。
重點其實是在imx6ull.dtsi和imx6ull-14x14-evk.dts這兩個文件上,因此我們也著眼于這兩個文件。
?
其中,板級相關的在這個imx6ull-14x14-evk.dts文件里,芯片級相關的在這個imx6ull.dtsi文件里,我們以板級imx6ull-14x14-evk.dts作為進一步的著手點來分析。
model、compatible、chosen、memory等屬性如果作為一級屬性,通常是放在板級的dts文件中,而不是放在dtsi文件中,因為這些信息基本都是用來匹配開發板的。
?
設備樹中的reserved-memory節點是用于預留一段內存區域的特殊節點。以下是關于該節點的詳細解釋:
?
定義與用途
在設備樹中,reserved-memory節點用于指定需要預留的內存區域。這些區域通常不會被操作系統或驅動程序用于常規的內存分配,而是保留給特定的硬件設備、驅動程序或系統功能使用。
典型應用場景
DMA和其他硬件加速功能:許多硬件設備(如網絡接口卡、音頻設備等)可能需要直接訪問內存而不經過CPU,這種技術稱為DMA(Direct Memory Access)。為了確保這些設備能夠安全地訪問內存,通常會在設備樹中使用reserved-memory節點來預留所需的內存區域。
避免內存沖突:通過預留特定的內存區域,可以防止操作系統或其他驅動程序錯誤地將這些內存分配給其他用途,從而避免潛在的內存沖突和系統不穩定。
節點屬性
reg:表示要預留的物理內存區域的起始地址和大小,格式為<address>, <size>。
compatible:可選屬性,用于指定預留內存區域的兼容類型,如"shared-dma-pool"表示該內存區域可用作一組設備的DMA緩沖區共享池。
no-map:可選屬性,如果存在,表示操作系統不得創建該區域的虛擬映射,也不能將其作為系統內存標準映射的一部分。
reusable:可選屬性,如果存在,表示操作系統可以使用該區域中的內存,但前提是擁有該區域的設備驅動程序能夠回收它。
等等。 看下這個
?
這里除了對pwm1的引用,后面還加了些其他數據,不要覺得這樣不行,無非就是后面按照這種規則去解析罷了,只要能解析到數據,就沒問題。 在設備樹(Device Tree)中,pwms = <&pwm1 0 5000000> 是一種描述 PWM(脈寬調制)控制器和其通道配置的語法。具體來說,這行代碼的含義如下:
pwms: 這是設備樹中的一個屬性,表示該設備支持 PWM 功能。
<&pwm1 0 5000000>: 這是屬性的值,描述了具體的 PWM 控制器及其配置。
詳細解釋如下:
&pwm1: 這是一個引用,指向一個名為 pwm1 的 PWM 控制器節點。& 符號表示這是一個對其他節點的引用。
0: 這是 PWM 通道號。在這個例子中,它表示使用 pwm1 控制器的第 0 個通道。
5000000: 這是 PWM 的頻率,單位是納秒(ns)。在這個例子中,PWM 的頻率為 5,000,000 納秒,即 5 MHz。
綜合起來,這行代碼的意思是:
該設備使用 pwm1 控制器的第 0 個通道。
該通道的 PWM 頻率設置為 5 MHz。
這種配置通常用于需要精確控制信號頻率的設備,如電機控制、LED 調光等應用。 這個被引用的pwm1,是在imx6ull.dtsi文件中定義的
?
并且在imx6ull-14x14-evk.dts的后面進行了追加
?
接著看電源穩壓器、聲卡、spi、iic等外設信息
?
?
…… 更多自行查看。 看下iomuxc節點
?
這里是個追加,其定義是在imx6ull.dtsi文件里,屬于片上soc的外設子節點
?
可以對子節點直接追加,可見,如果名稱是唯一的,那么是可以直接追加的。 iomuxc其實就是引腳復用控制器,這個節點里面是對引腳基本功能比如復用、驅動能力、上下拉等的描述,按照外設所用到的引腳集合在一起,比如i2c1用到的引腳
?
比如pwm1用到的所有引腳
?
等等。
pinctrl-names 屬性
pinctrl-names 屬性是一個字符串列表,它為引腳的不同狀態或功能模式提供了名稱標識。這些名稱可以代表引腳的不同復用功能、電氣特性配置或其他特定的工作狀態等,方便在設備樹中對引腳的配置進行描述和管理。通過為不同的引腳配置或功能模式指定獨特的名稱,如 “default”、“sleep”、“init” (自定義)等,使得設備樹中的其他節點可以引用這些名稱來快速定位和使用相應的引腳配置。
pinctrl-0通常用于指定設備或引腳組在默認狀態下的引腳配置。這種狀態通常是設備上電后或復位后的初始狀態。它引用了一個在設備樹的其他節點中定義的引腳組,這個引腳組包含了一組引腳及其相關的配置信息,如引腳復用功能、上下拉電阻、驅動能力等。 這兩個屬性貌似經常一起使用,是不是可以理解成,默認狀態下所使用的引腳?
當 pinctrl-names = "default" 和 pinctrl-0 = <&pinctrl_uart1> 一起出現在設備樹的一個節點中時,意味著當前節點所代表的設備或引腳組在默認狀態下,將采用 pinctrl_uart1 節點所定義的引腳配置。 考慮某種應用場景,在驅動里獲取pinctrl-names和pinctrl-0的屬性值,然后判斷pinctrl-names是否處于“default”狀態,如果是的話,那就使用pinctrl-0提供的引腳信息來進行初始化。具體的行為還得看驅動是怎么去使用這些信息的。
為了進一步理解這個問題,看下這個節點,里面pinctrl-names有三個值,然后下面就分別有pinctrl-0和pinctrl-1和pinctrl-2,和pinctrl-names的三個名稱是一一對應的,在驅動程序里肯定是根據不同的狀態判斷,去使用不同的引腳組。
iomuxc中pinctrl-names和pinctrl-0是什么屬性 在設備樹中,iomuxc 節點用于描述 IOMUXC(Input/Output Multiplexer Complex)的配置信息。該節點通常包含以下屬性: pinctrl-names 含義:定義引腳狀態,一種狀態對應一個 pinctrl-x。它為引腳的不同功能或模式提供了一種命名方式,方便在設備樹中對引腳配置進行分類和管理。例如,可以定義 “default”、”init”、”sleep” 等狀態,來分別表示引腳在默認情況下、初始化過程中以及低功耗狀態下的不同配置。 作用:通過指定不同的引腳狀態,可以在不同的場景下靈活地配置引腳的功能和屬性,滿足系統在不同運行階段的需求。比如在設備啟動時,將引腳配置為初始化狀態;在設備進入低功耗模式時,將引腳配置為睡眠狀態,以降低功耗。 pinctrl-0 含義:定義第 0 種狀態需要使用到的引腳組,可引用其他節點標識。它是一個引用屬性,通過引用其他定義了具體引腳配置的節點,來確定在該狀態下所使用的引腳設置。 作用:將引腳的實際配置信息與具體的引腳狀態相關聯。當設備的引腳狀態為 “default” 時,pinctrl-0 所引用的節點中的引腳配置信息就會被應用到相應的引腳上,從而實現對該狀態下引腳功能的設置。這樣可以方便地對不同狀態下的引腳配置進行集中管理和修改,提高代碼的可維護性和可讀性。 總的來說,pinctrl-names 和 pinctrl-0 屬性在設備樹的 iomuxc 節點中扮演著關鍵角色,它們共同協作以實現對引腳配置的靈活管理與精確控制。
iomuxc的最后一級子節點里面只有一個fls,pins屬性,這個名字也是廠家自定義的,只要能跟驅動里引用節點時的名稱對應起來即可。 fsl,pins 屬性是一個特定于 Freescale(現為 NXP)SoC 的屬性,用于描述引腳的配置信息。它通常包含一個或多個引腳配置項,每個配置項由引腳功能標識符和配置值組成。通過設置 fsl,pins 屬性,可以指定引腳的復用功能以及相關的電氣特性,如上下拉電阻、驅動能力等。這對于正確配置 SoC 的引腳以適應不同的外設功能至關重要。
繼續
?
iomuxc_snvs是設備樹中的一個屬性,主要用于配置與安全非易失性存儲(SNVS)子系統相關的引腳復用功能。以下是對該屬性的詳細解釋:
基本概念
iomuxc_snvs屬性通常出現在設備樹節點中,用于描述某個或某些引腳與SNVS子系統之間的關聯和配置信息。這些引腳可能具有特定的復用功能,通過該屬性進行設置和說明。
作用范圍
主要針對與SNVS子系統相關的引腳,這些引腳在系統中通常用于電源管理、安全存儲或其他與安全相關的功能。
典型用途
電源管理配置:可以指定某些引腳作為電源管理引腳,如開關機控制引腳等。通過iomuxc_snvs屬性的配置,可以確定這些引腳在系統中的具體功能和行為。
安全功能設置:對于涉及安全功能的引腳,如加密算法的硬件加速引腳、安全認證引腳等,使用iomuxc_snvs屬性進行配置,以確保系統的安全運行。
特點
引腳復用選項有限:與一般的IOMUXC寄存器相比,iomuxc_snvs屬性所涉及的引腳復用選項相對較少,通常只與特定的電源管理和安全功能相關。
安全性要求高:由于涉及到安全相關的功能,對iomuxc_snvs屬性的配置和使用需要特別小心,以確保系統的安全性和穩定性。
綜上所述,iomuxc_snvs屬性在設備樹中扮演著重要的角色,它用于配置與SNVS子系統相關的引腳復用功能,確保這些引腳能夠正確地實現電源管理和安全存儲等功能。
注意,設備樹中的節點名稱都是可以自己定義的,只要在同一層級下保持唯一即可。
設備樹中寫在前面的節點可以引用后面的節點嗎?
在設備樹中,前面的節點可以引用后面的節點。 設備樹中的節點引用通常使用phandle(物理句柄)和label(標簽)兩種方式來表示。phandle是一個32位的整數值,用于唯一標識一個節點;label是一個字符串,用于標識一個節點。通過這些方式,一個節點可以引用另一個節點,無論它們在設備樹中的位置如何。例如,可以在前面的節點中使用“&”符號和后面節點的phandle值或label來引用該節點。 設備樹中引用節點是引用節點的某個屬性還是引用整個節點,如何理解? 在設備樹中,引用節點既可以是引用節點的某個具體屬性,也可以理解為引用整個節點及其所有屬性。以下是對這兩種情況的具體解釋:
引用節點的某個屬性:
在某些情況下,一個節點可能會引用另一個節點的特定屬性值。例如,一個設備節點可能需要配置其電源管理參數,而它本身沒有直接定義這些參數,而是引用了另一個專門用于電源管理的節點中的相關屬性。
這種引用方式使得設備樹能夠更加靈活地組織和管理硬件信息,避免重復定義相同的屬性,同時也方便了設備的初始化和配置過程。
引用整個節點及其所有屬性:
更常見的情況是,一個節點引用另一個節點時,實際上是將整個被引用節點的所有屬性都納入考慮范圍。
例如,在一個復雜的嵌入式系統中,可能存在多個不同功能模塊的節點,其中一個模塊節點需要使用另一個模塊節點的資源或配置信息。通過引用整個節點,可以方便地獲取和使用該節點的所有屬性,而不僅僅是其中的某個特定屬性。
綜上所述,設備樹中的節點引用既可以指代對單個屬性的引用,也可以指代對整個節點及其所有屬性的引用。這種靈活性使得設備樹成為一種強大的工具,能夠有效地描述和管理復雜的硬件系統結構。 設備樹中如果某個屬性的屬性值是一個節點,那么如果進一步獲取屬性節點的屬性值? 在設備樹中,如果某個屬性的屬性值是一個節點,要進一步獲取該屬性節點的屬性值,可以通過以下步驟實現:
一、理解設備樹結構
節點與屬性:設備樹由節點(Node)和屬性(Property)組成。節點代表硬件設備或組件,屬性用于描述節點的特性或配置參數。
屬性值類型:屬性的值可以是多種類型,包括整數、字符串、數組、甚至是另一個節點(這種情況較少見但確實存在)。
二、定位屬性節點
查找節點:首先,需要在設備樹中找到包含目標屬性的節點。這通常通過遍歷設備樹或使用特定的查找函數(如of_find_node_by_path或of_find_node_by_name)來實現。
讀取屬性:一旦找到目標節點,就可以讀取其屬性值。如果屬性值是一個節點,那么這個屬性值實際上就是另一個節點的引用(可能是phandle、label或其他形式的引用)。
三、解析屬性節點
獲取節點引用:如果屬性值是一個節點,需要解析這個引用以獲取實際的節點對象。這通常涉及到將引用轉換為實際的節點標識符(如phandle轉換為節點指針)。
讀取節點屬性:一旦獲得了實際的節點對象,就可以像訪問普通節點一樣讀取其屬性值。這通常通過設備樹庫提供的接口函數(如of_get_property)來實現。
其實就很像json數據的鍵的值又是一個json數據。
設備樹中節點屬性的值可以是另一個節點。
以下是對這一行為的詳細解釋:
一、設備樹中的屬性值類型
屬性值的多樣性:在設備樹中,節點的屬性值可以是多種數據類型,包括整數、字符串、數組(包括字節數組和字符串數組)、以及布爾值等。
節點作為屬性值:除了上述基本數據類型外,設備樹還支持一種特殊類型的屬性值,即節點本身。這意味著一個節點的屬性值可以引用或指向另一個節點。
二、實現方式
使用phandle:在設備樹中,當一個節點的屬性值是另一個節點時,通常使用物理句柄(phandle)來表示這種引用關系。phandle是一個32位的整數值,用于唯一標識一個節點。通過指定phandle,設備樹能夠清晰地表達節點之間的層次和依賴關系。
語法規則:為了在設備樹中表示phandle,需要使用特定的語法。例如,在設備樹源碼中,可以使用“&”符號后跟節點標簽名或phandle來引用其他節點。同時,為了將phandle與屬性相關聯,可能需要使用“<”和“>”符號來包含phandle值。
三、應用場景
硬件復用:在嵌入式系統中,多個設備或組件可能會共享相同的硬件資源(如中斷控制器、時鐘源等)。通過在設備樹中使用節點作為屬性值,可以方便地實現硬件資源的共享和復用。
模塊化設計:設備樹中的節點引用也有助于實現模塊化設計。通過將相關的節點組織在一起,并使用屬性值進行引用,可以構建出更加清晰、易于維護的設備樹結構。
四、注意事項
循環引用:雖然設備樹允許節點屬性的值是另一個節點,但需要注意避免循環引用的情況。循環引用可能導致設備樹解析器進入無限循環,從而無法正確解析設備樹。
語義清晰性:當使用節點作為屬性值時,應確保這種引用關系具有明確的語義意義。這有助于提高設備樹的可讀性和可維護性。
綜上所述,設備樹中節點屬性的值確實可以是另一個節點,并且這種特性在嵌入式系統的硬件描述和配置中發揮著重要作用。通過合理使用節點引用和phandle機制,可以構建出更加靈活、可擴展和可維護的設備樹結構。
更多補充
中斷結點
?
?#interrupt-cells
是一個整數類型的屬性,用于指定在設備樹的中斷描述中使用多少個單元來表示一個中斷。例如,如果#interrupt-cells
的值為2,那么在描述每個中斷時,需要使用2個單元的數據來表示相關信息。注意,不是對應的reg,具體對應著什么,暫時我也不知道,后面再看。在設備樹中,
interrupt-controller
屬性通常是一個空屬性,其作用是聲明當前節點是一個中斷控制器。通過設置interrupt-controller
屬性(即使為空),可以讓設備樹和系統清晰地知道該節點代表的是一個中斷控制器設備。這有助于在復雜的硬件架構中快速識別和定位負責中斷處理的設備,方便系統對其進行正確的配置和管理。在設備樹中,存在各種不同類型的設備節點,如CPU、內存、I/O設備等。interrupt-controller
屬性為空可以作為一種簡單的標識方式,將中斷控制器與其他普通設備區分開來,避免混淆,確保系統能夠準確地識別和處理中斷相關的設備。?上面的#interrupt-cells應該對應的是子結點的interrupts屬性的表示個數。
?interrupts
屬性是一個整數數組,用于指定與該設備相關聯的中斷請求信號的信息。每個元素通常包含兩個主要部分信息,一是中斷控制器的引用(通過 phandle 或設備樹節點路徑表示),二是中斷編號或中斷名稱(具體取決于設備和平臺的表示方式)。?例如,
interrupts = <&intc 23 0x04>;
這個屬性值中,&intc
是中斷控制器的設備樹節點標簽,23
是中斷編號,0x04
表示中斷觸發類型(如上升沿觸發等)。?作用與意義
硬件中斷配置:它告訴操作系統或硬件抽象層(HAL)當設備需要產生中斷時,應該向哪個中斷控制器發送信號,以及使用哪個中斷號。這樣,當設備發生特定事件(如數據接收、錯誤發生等)需要通知處理器時,處理器能夠正確地響應并執行相應的中斷服務程序。
系統資源分配和管理:系統可以根據
interrupts
屬性的信息,為設備分配合適的中斷處理資源,確保不同設備的中斷請求能夠得到及時、準確的處理,避免中斷沖突和混亂。驅動程序開發:對于設備驅動開發者來說,
interrupts
屬性提供了關鍵的信息,以便編寫正確的中斷處理代碼。驅動程序需要根據這個屬性的值來注冊相應的中斷處理函數,使設備能夠正常工作。常見屬性值的含義
中斷編號:如果
interrupts
屬性值中只包含一個數字,那么這個數字通常代表中斷編號。例如,在一些簡單的系統中,interrupts = <5>;
表示設備使用中斷編號為 5 的中斷線。中斷名稱:在某些情況下,可能會使用中斷名稱來代替中斷編號。例如,
interrupts = <"irq_name">;
這里的"irq_name"
是在設備樹中定義的一個中斷名稱標識符。這種方式可以使設備樹更加易讀和可維護,尤其是當中斷編號較多且不易理解時。復合值:有些復雜的設備可能會有更復雜的
interrupts
屬性值,包含多個單元的復合值。這些復合值可能包括中斷域、觸發方式、CPU 接口信息等。例如,interrupts = <gic_ppi>;
這個屬性值中,GIC_PPI
表示這是一個與通用中斷控制器(GIC)相關的私有外設中斷(PPI),37
是中斷號,1
表示中斷觸發類型等信息。總之,設備樹中的
interrupts
屬性至關重要,它不僅明確了設備的中斷請求信號細節,還為系統資源分配、驅動程序開發提供了關鍵依據,確保了硬件中斷的有效管理和設備的正常運行。?具體還要看程序里是怎么獲取的。
#clock-cells
?
通過指定
#clock-cells
的值,可以明確子節點的時鐘信息在設備樹中應該如何被表示和解析。例如,如果#clock-cells = <2>
,則表示該節點下的子節點時鐘信息將使用 2 個單元的數據來描述。這有助于系統準確地理解和處理不同節點的時鐘配置信息,確保硬件設備的時鐘設置能夠被正確地識別和應用。
芯片上的外設通常都作為soc結點的子結點。
?設備樹的語法規定了屬性值可以是整數、字符串、數組等多種形式。當屬性值為數組時,尖括號用于標識數組的開始和結束。分開寫尖括號并不影響設備樹編譯器對屬性值的解析,因為編譯器會按照設備樹的語法規則正確處理這種寫法。例如,對于一個包含多個數值的數組屬性
reg = <0x1000 0x2000 0x3000>;
,無論是連續寫還是分開寫尖括號,如reg = <0x1000>,<0x2000>,<0x3000>;
,中間以逗號或者空格隔開應該都可以,設備樹編譯器都能識別并將其解析為一個包含三個元素的數組。也可以分行寫reg = <0x1000>,
??
<0x2000>,
??
<0x3000>;
??比如:
?
gpio-controller
?
在設備樹(Device Tree)中,
gpio-controller
屬性通常用于標識某個節點代表一個 GPIO(通用輸入輸出)控制器。這個屬性告訴系統該節點下的硬件資源是與 GPIO 控制相關的,使得操作系統能夠正確地識別和使用這些 GPIO 引腳。
使用pinctrl子系統時,我們通常都會將所有的pin引腳信息都放在一起;然后具體被哪個外設使用時,需要再在對應外設節點中引用pin信息。
目前還是沒太搞明白,pin,gpio和具體外設之間的設備樹的關系,所有的引腳都是pin,需要先設置好pin的復用功能、驅動能力以及上下拉等基本屬性;如果是復用為gpio,那么就作為gpio這種外設功能;但是,gpio也需要依附其他外設來使用的。這里要搞清楚,gpio是一種片上外設,一般都定義在soc節點中吧?然后片外的外設,就不放在soc里面?這塊還得再研究研究,捋一捋。