在linux中,dts是設備樹源文件,用來描述設備資訊的;設備樹技術將設備的硬體資源資訊就寫在dts文件中。設備樹源檔案dts被編譯成dtb二進制,在bootloader運行時傳遞給作業系統,作業系統對其進行解析展開,從而產生一個硬體設備的拓撲圖,有了這個拓撲圖,在編成過程可以直接通過系統提供的介面取得到設備樹的節點和屬性資訊。
本教學操作環境:linux7.3系統、Dell G3電腦。
裝置樹(dt:device tree)是linux核心採用的參數表示和傳遞技術,在系統開機啟動階段進行裝置初始化的時候,將裝置樹中所描述的硬體資訊傳遞給操作系統;
dts(device tree source):設備樹源文件,描述設備資訊的;
設備樹源文件dts被編譯成dtb二進制,在bootloader運行時傳遞給作業系統,作業系統對其進行解析展開,從而產生一個硬體設備的拓撲圖,有了這個拓撲圖,在編成過程可以直接透過系統提供的介面取得到設備樹的節點和屬性資訊
dtc(device tree compiler):裝置樹編譯/反編譯/偵錯工具;
dtb(device tree binary) :二進位設備樹鏡像;
dtsi(device tree source include):功能類似設備樹檔的頭文件,可以被dts檔透過include引用,dtsi檔一般是描述共通點;
在裝置驅動程式原始碼中,分為驅動程式碼和裝置程式碼,驅動程式碼是操作硬體的方法,裝置程式碼是硬體資源、數據,當驅動程式碼和裝置程式碼匹配時就會呼叫驅動的probe函數,probe函數會利用裝置程式碼的資源去初始化設備;
裝置樹之前,裝置程式碼都是直接寫在核心原始碼中的,以platform_device結構體的形式存在,驅動程式碼和裝置程式碼也是在platform總線上匹配,當需要修改設備資源時,就需要修改核心源碼;
設備樹技術將設備的硬體資源資訊就寫在dts文件中,需要修改就修改dts文件,不必在修改內核原始碼;
不採用設備樹技術:內核原始碼中會充斥大量設備硬體描述訊息,導致內核原始碼不停增多,但是增多的硬體描述訊息碼和核心功能不相關;
採用設備樹技術之後:設備的硬體描述資訊都在dts檔案中,修改方便,但是核心要增加解析dts檔案格式的程式碼;
驅動開發者根據硬體編寫/修改dts文件,使得將來驅動程式碼能匹配到適當的設備硬體資訊;
編譯核心時,kernel會先編譯出dtc,然後再用dtc將dts檔案編譯成dtb;
uboot啟動kernel時,將核心鏡像和dtb都重定位到內存,並告訴核心dtb的所在記憶體位址;
#核心啟動初期呼叫內部函數解析dtb,得到硬體資訊後再組裝成硬體函數,最後去和驅動程式碼進行比對;
#arm架構:arch/arm/boot/dts目錄中
註解用/* */,注意#開頭的不是註解
分號是段落區塊之間的分隔符,{}和[]和是段落區塊的封裝符號,和C語言語言類別
/dts-v1/節點,表示dts的版本號,目前都是v1
/{}是根節點root node,理論上只應該有一個根節點,有說法dtc會合併所有root node為同一個
dts是樹狀的多節點組織,基本單元是node,除root外其他node都有parent,還可以有child
[label:] <node-name> [@<unit-address>]{ [property] [child nodes] [child nodes] ...... };</unit-address></node-name>
[]:表示該項目可以省略,:表示不可省略;
[label:]:label是標籤名,為了方便存取節點,後面可以直接透過&label來存取該節點。
node-name:节点名称。根节点的名称必须是/
[@unit-address]:unit-address是设备地址,如cpu node就是0、1这种,reg node就是0x12010000这种;
cpus { /* 下面三项是cpus节点的属性 */ #address-cells = <1>; #size-cells = <0>; enable-method = "hisilicon,hi3516dv300"; /* 下面是子节点 */ cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a7"; clock-frequency = <HI3516DV300_FIXED_1000M>; reg = <0>; }; };
cpus是cpu的父节点,从形式来能直观的看出来,cpu节点是被cpus节点的大括号括起来的;
cpus节点省略了标签名和设备地址,只有节点名称;
/{ gpx1:gpx1{ controller; #gpio-cells=<2>; }; key@11400c24{ compatible="fs4412,key"; reg=<0x11400c24 0x4>; intn-key=<&gpx1 2 2>; } }
gpio-controller:说明该节点描述的是一个gpio控制器;
#gpio-cells:描述gpio使用节点的属性一个cell的内容;
uart0: uart@120a0000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x120a0000 0x1000>; interrupts = <0 6 4>; clocks = <&clock HI3516DV300_UART0_CLK>; clock-names = "apb_pclk"; status = "disabled"; }; /* 在驱动中对应的结构体*/ //struct device_driver->of_match_table->compatible struct of_device_id { char name[32]; char type[32]; char compatible[128]; const void *data; };
(1)compatible属性是用于设备节点和设备驱动匹配用的,在内核描述驱动的structdevice_driver结构体中,compatible变量中就会保存用于匹配的字符串,当设备节点和驱动的
compatible相同时就匹配成功;
(2)compatible后面可以有多个字符串,优先匹配靠前的字符串,靠前的字符串匹配不上才会匹配后面的字符串;
/ { model = "Tyr DEMO Board"; compatible = "hisilicon,hi3516dv300"; memory { device_type = "memory"; reg = <0x82000000 0x20000000>; };};
(1)model是描述模块信息的,一般只有根节点才有,标明设备树文件对应的开发板的名称;
(2)在内核的启动打印中可以看到model的值:“OF: fdt:Machine model: Tyr DEMO Board”;
&uart0 { status = "okay"; };
状态值 | 含义 |
---|---|
okey | 表示设备是可操作的 |
disabled | 表示当前不可操作,但是后续是可以更改为可操作性的 |
fail、failed | 表示有严重错误,几乎不可能再可操作了 |
(1)status描述设备信息状态,在设备树文件中可以根据需求设置模块的状态,功能就是开启/关闭某个模块;
(2)在dtsi文件中,默认都是关闭模块的,在开发板对应的dts文件中自己去打开需要的模块;
clock: clock@12010000 { compatible = "hisilicon,hi3516dv300-clock"; #address-cells = <1>; /* 表示reg里面的数据address占用一个字长*/ #size-cells = <1>; /* 表示reg里面的数据size占用一个字长,注意字长不是字节*/ #clock-cells = <1>; #reset-cells = <2>; reg = <0x12010000 0x1000>; /*起始地址是0x12010000,长度是0x1000*/ };
reg属性:配置某个硬件模块对应的地址范围信息;
#address-cells属性:表示reg里面的数据address占用的字长,注意字长不是字节;
#size-cells:表示reg里面的数据size占用的字长,注意字长不是字节;
reg =
gic: interrupt-controller@10300000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; /*表示interrupts用三个cell来描述中断*/ #address-cells = <0>; interrupt-controller; /*标明gic节点是中断控制器*/ /* gic dist base, gic cpu base , no virtual support */ reg = <0x10301000 0x1000>, <0x10302000 0x100>; }; ipcm: ipcm@045E0000 { compatible = "hisilicon,ipcm-interrupt"; interrupt-parent = <&gic>; /*父节点是gic节点*/ interrupts = <0 10 4>; /*<中断域 中断 触发方式>*/ reg = <0x10300000 0x4000>; status = "okay"; };
(1)interrupt-controller:无值属性,表示这是个中断控制器node
(2)#interrupt-cells:这是中断控制器节点的属性,用来标识这个控制器需要几个cell做中断描述符
(3)interrupt-parent:标识此设备节点属于哪一个中断控制器,如果没有这个属性,会自动依附父节点
(4)interrupts :一个中断标识符列表,表示每一个中断输出信号
chosen { stdout-path = "serial0:115200n8"; };
(1)chosen子节点不对应真实的设备,是用来描述内核启动参数的,对应于uboot启动内核时传递的bootargs参数;
(2)上面是摘抄的内核dts文件中的chosen子节点,里面只设置了stdout-path属性,也就是把输出设置成串口0,波特率是115200;
(3)dts文件中设置的属性会被覆盖点,具体就是uboot在启动内核时,会将bootargs启动参数转换成chosen子节点的属性,替换掉dts文件中设置的属性;
~ # ls /proc/device-tree/chosen/ bootargs name ~ # ~ # cat /proc/device-tree/chosen/bootargs mem=1408M console=ttyS0,115200 root=/dev/mmcblk0p7 rootfstype=squashfs rootwait ~ # ~ # cat /proc/device-tree/chosen/name chosen ~ #
aliases { serial0 = &uart0; gpio0 = &gpio_chip0; gpio1 = &gpio_chip1; gpio2 = &gpio_chip2; ······ };
aliases就是别名的意思,aliases节点主要功能就是给节点定义别名,为了方便访问节点。不过我们在节点命名的时候可以加上label标签,直接通过&label引用标签来访问也很方便,aliases节点内部其实也是通过引用标签名来定义别名;
gpio_chip1: gpio_chip@120d1000 { compatible = "arm,pl061", "arm,primecell"; reg = <0x120d1000 0x1000>; interrupts = <0 17 4>; clocks = <&clock HI3516DV300_SYSAPB_CLK>; clock-names = "apb_pclk"; #gpio-cells = <2>; status = "disabled"; }; /*引用gpio_chip1节点*/ &gpio_chip1 { status = "okay"; /*替换status属性内容*/ };
对于已经定义好的节点,我们通过引用节点的方式,重新定义某些属性,效果上看就是替换掉某些属性的值;
/{ node{ key1=value1; } } /{ node{ key2=value2; } } //合并的结果 /{ node{ key1=value1; key2=value2; } }
有时候我们需要增加硬件描述的信息,这时候就可以在后面创新定义该节点,最后解析的时候会把同名节点不同的部分进行合并;
相关推荐:《Linux视频教程》
以上是linux dts有什麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!