小白帶你探索Linux設備樹1_框架篇V1

0. 說在前面的話

學習過程中的總結記錄,感悟比較淺顯,經不起推敲,歡迎批評指正!!!

文章主體是以imx8qm-mek評估板爲實例進行介紹設備樹。

1. 什麼是設備樹

1.1 設備樹的定義

官方定義:

The primary purpose of Device Tree in Linux is to provide a way to
describe non-discoverable hardware. This information was previously
hard coded in source code.

ARM設備樹出現之前的電路板硬件的細節被硬編碼到內核中了,導致內核代碼臃腫難以維護,因此將內核與那些臃腫的硬件代碼解耦,方便維護。

1.2 設備樹的使用

首先看下設備樹如何被被加載到內核當中的過程
在這裏插入圖片描述
首先將上圖涉及到的dts、dtb、dtc簡單說明下。

  • dts:device tree source 設備樹源文件,也就是我們進行編輯的文件,用於增刪相關的硬件細節,可以看做C之類的代碼,並且支持部分C語法,後面會詳談;
  • dtb:device tree blob 可以視爲二進制版本的dts,內核可以解析;
  • dtc:device tree compiler 設備樹編譯器,dtb文件就是通過dtc將dts文件編譯而成的。個人感覺可以類比C語言的編譯。

上圖可以總結爲:利用dtc將dts文件編譯爲可被內核解析的dtb文件,通過bootloader的引導,kernel會將dtb文件加載到內存,並進行解析。
在沒引進設備樹之前,當每次更改設備,都需要重新編譯內核,而現在僅僅需要編譯設備樹即可,省時省力。

2. ARM Linux設備樹的起源

2.1 忍無可忍

設備樹出現的原因,還得從2011年3月17日Linus Torvalds的那封信說起,下面是信件的截圖:
Linus 那封對於ARM垃圾代碼忍無可忍的信件
因ARM帶來大量的板級細節代碼,導致內核中充斥着大量的垃圾代碼,最後Linus忍無可忍了,於是乎這就成了ARM Linux設備樹誕生的契機。

2.2 事件背景

每次正式的linux kernel release之後都會有兩週的merge
window,在這個窗口期間,kernel各個部分的維護者都會提交各自的patch,將自己測試穩定的代碼請求併入kernel main
line。每到這個時候,Linus就會比較繁忙,他需要從各個內核維護者的分支上取得最新代碼並merge到自己的kernel source
tree中。Tony Lindgren,內核OMAP development
tree的維護者,發送了一個郵件給Linus,請求提交OMAP平臺代碼修改,並給出了一些細節描述:

  • 1、簡單介紹本次改動
  • 2、關於如何解決merge conficts。有些git mergetool就可以處理,不能處理的,給出了詳細介紹和解決方案

一切都很平常,也給出了足夠的信息,然而,正是這個pull request引發了一場針對ARM
linux的內核代碼的爭論。我相信Linus一定是對ARM相關的代碼早就不爽了,ARM的merge工作量較大倒在其次,主要是他認爲ARM很多的代碼都是垃圾,代碼裏面有若干愚蠢的table,而多個人在維護這個table,從而導致了衝突。因此,在處理完OMAP的pull
request之後(Linus並非針對OMAP平臺,只是Tony Lindgren撞在槍口上了),他發出了怒吼:

對事件背景感興趣的詳見wowo寫的Device Tree(一):背景介紹,我就不在這裏班門弄斧了。

3. DT的文件組織結構

3.1 dts目錄簡介

dts文件一般位於./arch/xxx/boot/dts目錄中,首先映入眼簾的是各大SOC廠商的目錄
在這裏插入圖片描述
我們這裏選擇freescale也就是現在的NXP作爲分析對象
在這裏插入圖片描述

3.2 dts分析文件下載

如果大家沒有內核源代碼的話,可以使用如下命令將我們的作案工具單獨下載下來,不用clone整個內核源代碼,耗時又費力!!!

git clone https://github.com/sazczmh/imx8qm_linux_dts.git

如果clone不了的話,也可以從我的百度雲盤下載
鏈接: https://pan.baidu.com/s/15lg0u54RMNjaMkge5e3E4g 密碼: nsnv

clone後的代碼如下
在這裏插入圖片描述

3.3 完整dts文件的組成

從上圖可以看到給大家的倉庫中,不僅有.dts後綴的文件還有.dtsi後綴的文件,還記得我們之前提到過的dts支持部分C語法嗎?這裏的.dtsi類似C語言的頭文件。

爲了模塊化dts文件,驅動工程師將電路板公用的部分提煉爲各個.dtsi文件,減少代碼的重複,並且易於移植。下圖就是頭文件包含示例:
在這裏插入圖片描述
從圖中也可以明顯的注意到,該文件還包含了.h文件,是不是有點C的趕腳。

下面我們簡單分析下倉庫裏那麼多dts、dtsi的組織,在分析之前需要明白以下幾個點:

  1. 倉庫裏設備樹文件不是一塊評估板的設備樹,是同一芯片imx8qm的多個評估板的設備樹;
  2. 同一塊評估板因爲用途不同,有多種設備樹進行個性化配置;
  3. dtsi文件是提煉出來的公用的模塊文件,爲的是減少代碼重複,易於移植。

明白了以上幾個點之後,讓我們着手分析吧

首先拋棄dtsi文件僅僅分析dts文件
在這裏插入圖片描述
從圖中可以明顯的看到imx8qm的評估板種類有三種(我猜的…,大概是的),咱們的重點放在mek-Multisensory Enablement Kit系列,關於這幾個文件的解釋可以查看官網的release note,相關截圖如下。
在這裏插入圖片描述
從上圖可以明顯的看出來,mek那麼多設備樹分別有不同的特性。下面我們就僅用fsl-imx8qm-mek.dts來進行示例分析。

如下是fsl-imx8qm-mek.dts的層次結構
在這裏插入圖片描述
從上圖也可以看出一個完整的設備樹文件是由1個dts文件+n個dtsi文件組成的。

4. DT的語法組織框架

4.1 DT的基本語法

/dts-v1/;

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        child-node1 {
        };
    };
};

在這裏插入圖片描述

該抽象框架引自https://elinux.org/Device_Tree_Usage

說實話,小白我第一次遇到這個語法圖的時候,真的是一臉懵逼,這個語法圖太抽象了,和真實的設備樹文件匹配不上,真實的設備樹文件如下:
在這裏插入圖片描述
如果這時候大家還沒有厭煩我的嘮叨的話,就且聽我慢慢道來dts語法組織框架吧。

  • 唯一的根節點: “/” ,多個根節點會合並;
  • 根節點會包含一系列子節點,抽象框架裏面有兩個子節點,它們分別是"node1" 和 “node2”;
  • 子節點"node1“又包含一系列子節點,這個抽象框架裏有兩個子節點,它們分別是: “child-node1” and “child-node2”;
  • 各個節點又包含一系列屬性。

屬性是簡單的關鍵詞和其對應的值所組成,與關鍵詞所對應的值可以爲空或者任意的數據屬性,下面簡單說說設備樹源文件幾個常見的屬性例子:

  • 文本字符串數據屬性以雙引號的形式展示出來:
    • a-string-property = "A string";
  • ‘Cells’ 屬性是32 bit 無符號整數,並且由尖括號來定位:
    • cell-property = <0xbeef 123 0xabcd1234>;
  • 二進制數據屬性由方括號來定位:
    • binary-property = [0x01 0x23 0x45 0x67];
  • 可以使用逗號將不同表示形式的數據連接在一起:
    • mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
  • 逗號還可用於創建字符串列表:
    • string-list = "red fish", "blue fish";

說過基本的語法,接下來讓我們開始實戰。

4.2 DT的簡化框架

當了解基本的DT語法之後,首先展示一副簡化的DT框架,讓大家對DT有個簡單的認識
在這裏插入圖片描述

https://www.nxp.com/docs/en/application-note/AN5125.pdf

4.3 根節點的model與compatible

在這裏插入圖片描述

  • model屬性指明瞭該評估板的設備生產商(感覺主要指的芯片生產商)、該評估板(也可指芯片系列)的名字,一般而言,該屬性的數值爲“manufacturer,model”,本示例當中

    model = "Freescale i.MX8QM MEK";
    指明瞭該評估板的生產商爲Freescale,評估板的名字爲i.MX8QM MEK。

  • compatible屬性是上文所提到的字符串列表屬性,這些屬性用於匹配machine type的,可以據此判斷啓動的設備。
    string-list = "red fish", "blue fish";
    根節點的compatible屬性一般包括兩個以上的兼容性字符串,本例中爲
    compatible = "fsl,imx8qm-mek", "fsl,imx8qm";

    • fsl,imx8qm-mek爲板子級別的名字,代表imx8qm的MEK評估板;
    • fsl,imx8qm爲芯片級別的名字,代表imx8qm這款SOC。

這裏面要提一點根節點僅有一個,但是觀察dtsi文件發現,存在多個根節點,dtc會將多個根節點進行合併,類似下圖。
在這裏插入圖片描述

https://bootlin.com/pub/conferences/2014/elc/petazzoni-device-tree-dummies/

4.4 節點名字

下面集中列舉幾個節點名字,先有個感性的認識。

  • sata: sata@5f020000
  • memory@80000000
  • reserved-memory

節點名字具有如下格式

[label: ]<name>[@<unit-address>]
  • name是由簡單的ascii字符串,最長31字符。通常情況下name的命名是說明這個設備節點的種類,例如sata、memory。
  • unit-address指的是該設備節點的地址,會在reg屬性中具體指定,節點名字中可以不包含,但通常情況下都加上個unit-address。
  • label: 相當於該設備節點的指針,方便其他設備節點引用,或者形成層次性的結構。
    • 方便其他設備引用
      對於gic: interrupt-controller@51a00000來說, 設備節點通過&gic來引用該設備節點
      在這裏插入圖片描述
    • 形成層次性的結構
      對於sata: sata@5f020000,該設備節點在fsl-imx8qm-device.dtsi中進行定義了
      在這裏插入圖片描述
      然後又在fsl-imx8qm-mek.dtsi中進行進一步配置
      在這裏插入圖片描述

4.5 reg屬性

先來看個reg屬性實例
在這裏插入圖片描述
首先reg的第一個地址,對應着unit-address,reg格式的定義由父節點的如下屬性所控制。

  • #address-cells
  • #size-cells

reg屬性的語法如下

  • reg = <address1 length1 [address2 length2] [address3 length3] ... >

sata父節點的reg屬性的限制如下
在這裏插入圖片描述
另外再提一下,reg是根據該soc的RM中描述,下面舉個例子
在這裏插入圖片描述
最後爲了驅動中方便查詢,通常給利用reg-name屬性給reg起個別名,sata的reg-name如下

  • reg-names = "ctl", "phy";

4.6 interrupts屬性

sata的中斷屬性如下

  • interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;

該屬性由interrupt-parent屬性所限定,如果該節點沒有指定interrupt-parent,那麼將有父節點的interrupt-parent所限定,sata的父節點的相應屬性爲。

  • interrupt-parent = <&gic>;

gic節點的定義爲

gic: interrupt-controller@51a00000 {
		compatible = "arm,gic-v3";
		reg = <0x0 0x51a00000 0 0x10000>, /* GIC Dist */
		      <0x0 0x51b00000 0 0xC0000>, /* GICR */
		      <0x0 0x52000000 0 0x2000>,  /* GICC */
		      <0x0 0x52010000 0 0x1000>,  /* GICH */
		      <0x0 0x52020000 0 0x20000>; /* GICV */
		#interrupt-cells = <3>;
		interrupt-controller;
		interrupts = <GIC_PPI 9
			(GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_HIGH)>;
		interrupt-parent = <&gic>;
	};

注意如下幾個宏定義均在相應.h文件中進行聲明。

  • GIC_SPI
  • IRQ_TYPE_LEVEL_HIGH

4.7 其他屬性

clocks、power-domain、status…等屬性都是如法炮製,我就不在這裏多解釋了。

4.8 特殊節點

  • aliases節點:某些節點的快捷訪問方式?官方是這樣解釋的,但我是沒看出來…。
aliases {
		csi0 = &mipi_csi_0;
		csi1 = &mipi_csi_1;
		dpu0 = &dpu1;
		dpu1 = &dpu2;
		ethernet0 = &fec1;
		ethernet1 = &fec2;
		dsi_phy0 = &mipi_dsi_phy1;
		dsi_phy1 = &mipi_dsi_phy2;
		mipi_dsi0 = &mipi_dsi1;
		mipi_dsi1 = &mipi_dsi2;
		ldb0 = &ldb1;
		ldb1 = &ldb2;
		isi0 = &isi_0;
		isi1 = &isi_1;
		isi2 = &isi_2;
		isi3 = &isi_3;
		isi4 = &isi_4;
		isi5 = &isi_5;
		isi6 = &isi_6;
		isi7 = &isi_7;
		serial0 = &lpuart0;
		serial1 = &lpuart1;
		serial2 = &lpuart2;
		serial3 = &lpuart3;
		serial4 = &lpuart4;
		mmc0 = &usdhc1;
		mmc1 = &usdhc2;
		mmc2 = &usdhc3;
		usbphy0 = &usbphy1;
		can0 = &flexcan1;
		can1 = &flexcan2;
		can2 = &flexcan3;
		i2c0 = &i2c_rpbus_0;
		i2c1 = &i2c_rpbus_1;
	};
  • chosen 節點:該節點不代表一個真實設備,通常用於固件和操作系統傳遞參數的橋樑,就像boot參數。而且其parent node必須是名字是“/”的根節點。
chosen {
		bootargs = "console=ttyLP0,115200 earlycon=lpuart32,0x5a060000,115200";
		stdout-path = &lpuart0;
	};

5. 總結

本文以imx8qm-mek評估板爲實例進行介紹DT,讓大家對DT的定義、ARM Linux DT的起源、具體評估板DT的文件組織結構、DT的語法組織框架有個簡單的認識。本篇博客並沒有事無鉅細的介紹DT,僅僅是點到爲止,而且小編也是剛剛接觸設備樹,沒有太多的經驗,如發現錯誤,還望指出來,大家一起交流共同進步。

過段時間會推出本篇博客的續作小白帶你探索Linux設備樹2_API篇V1,進行總結一個driver如何使用設備樹。

參考文獻

主要是國外官方的權威資料

  1. https://elinux.org/Device_Tree_Reference
  2. https://elinux.org/Device_Tree_Usage
  3. https://bootlin.com/pub/conferences/2014/elc/petazzoni-device-tree-dummies/
  4. https://www.nxp.com/docs/en/application-note/AN5125.pdf
  5. http://www.wowotech.net/device_model/dt_basic_concept.html


原創不易,切勿剽竊!

在這裏插入圖片描述

歡迎大家關注我創建的微信公衆號——小白倉庫
原創經驗資料分享:包含但不僅限於FPGA、ARM、RISC-V、Linux、LabVIEW等軟硬件開發,另外分享生活中的趣事以及感悟。目的是建立一個平臺記錄學習過的知識,並分享出來自認爲有用的與感興趣的道友相互交流進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章