第八章 DTS语法 (Part II)
8.1 DTS语法
DTS 版本
在DTS根文件里, 第一行需要描述版本。 如下
/dts-v1/;
设备树源文件(DTS)的语法版本目前只有一个,即版本1,用/dts-v1/;表示。
注释
1). 单行注释
使用双斜杠 // 来添加单行注释。注释内容从 // 开始,直到行尾结束。 比如:
// this is a single line comment.
cpus {
cpu@0 {
reg = <0x0 0x0>;
};
};
2). 多行注释
使用 /* 和 */
来添加多行注释。注释内容在 /* 和 */ 之间。比如:
/*
* This is multiple line comments.
* define a memory node.
*/
memory@0 {
device_type = "memory";
reg = <0x0 0x80000000>; // memory start address and the size.
};
这两种注释方式可以帮助你在DTS文件中添加说明和注释,以便更好地理解和维护代码。
#include语法
可以使用#include指令来包含其他DTS文件。这类似于C语言中的#include指令,用于将外部文件的内容插入到当前文件中。
语法
#include "filename.dtsi"
比如, 假设你有一个名为common.dtsi的文件,其中包含一些通用的设备树节点定义。你可以在你的DTS文件中包含这个文件,如下所示:
/dts-v1/;
#include "common.dtsi"
/ {
/**
* other DTS nodes...
*/
};
使用#include指令可以帮助你组织和管理设备树文件,特别是当你有多个设备树文件需要共享通用配置时。
.dtsi 文件
• 全称:Device Tree Source Include
• 用途:作为包含文件,描述多个平台或板子共享的硬件配置。
• 内容:包含通用的设备树节点定义,通常用于定义SoC(系统级芯片)或其他共享硬件的配置。
• 示例:一个SoC的设备树文件,包含该SoC上所有硬件的通用信息,可以被多个板子的.dts文件包含。
比如, soc.dtsi:
/dts-v1/;
/ {
cpus {
cpu@0 {
reg = <0x0 0x0>;
};
};
memory@0 {
device_type = "memory";
reg = <0x0 0x80000000>;
};
};
这样在不同的开发板就可以include此soc.dtsi文件, board.dts:
/dts-v1/;
#include "soc.dtsi"
DTS 根节点
根节点通常包含一些全局属性,这些属性适用于整个设备树。根节点的定义通常如下所示:
/dts-v1/;
/** below is DTS root node */
/ {
/**
* here, add some global variables...
*/
}; // end root node.
根节点(root node)是整个设备树的起点,表示系统的最高层次。根节点没有父节点,但可以有多个子节点。
DTS 全局属性
在根节点里, 通常包含一些全局属性, 比如:
/dts-v1/;
/ {
#address-cells = <2>;
#size-cells = <1>;
compatible = "neardi,lkd3588-linux-f0", "rockchip,rk3588";
model = "Rockchip RK3588 Neardi LKD3588 Board";
cpus {
#address-cells = <2>;
#size-cells = <1>;
cpu@0 {
reg = <0x0 0x0 0x0>;
};
};
memory@0 {
device_type = "memory";
reg = <0x0 0x80000000 0x20000000>;
};
serial@80000000{
compatible = "neardi,demo_serial";
reg = <0x0 0x80000000 0x20>;
};
};
这里的常见属性是:
1).#address-cells:定义地址单元的数量,通常为2,表示每个地址由两个32位单元组成。
2). #size-cells:定义大小单元的数量,通常为1,表示每个大小由一个32位单元组成。
3). compatible:描述设备或系统的兼容性信息,通常用于匹配驱动程序。
4). model:描述系统的模型或名称。
DTS 节点(node)语法
节点的语法格式如下:
[label:] <node-name>[@<unit-addr>] {
[<property-name> = <property-value>;]
[<child-node>]
};
• 标签(label):可选,用于标识节点或属性,以便在设备树的其他部分引用。
• 节点名称(node-name):必需,表示节点的名称。
• 单元地址(unit-addr):可选,表示节点的地址,具体取决于节点所在的总线类型。
• 属性(property-name = property-value):可选,用于描述设备的特性。
• 子节点(child-node):可选,表示当前节点的子节点。
比如:
cpus {
#address-cells = <2>;
#size-cells = <1>;
cpu0: cpu@0 {
reg = <0x0 0x0>;
device_type = "cpu";
compatible = "arm,cortex-a9";
status = "okay";
};
};
• 标签(label):cpu0和uart0是标签,用于标识cpu@0。
• 节点名称(node-name):cpus, cpu是子节点。
• 单元地址(unit-addr):@0。
• 属性(property-name = property-value):reg、device_type、compatible、status等。
• 子节点(child-node):cpu@0。
通过这种语法格式,你可以清晰地定义和组织设备树文件,使操作系统能够正确解析和配置硬件。
DTS 节点属性
1). compatible
描述设备的兼容性信息,用于匹配驱动程序, 与驱动代码里的compatible相配。
格式常常是: "manufacture,model", 比如:
compatible = "neardi,demo_uio";
2). reg
定义设备的寄存器地址和大小, 其中addr和size的位宽是有 #address-cells和#size-cells定义。比如:
my_reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
demo_memory: buffer@50000000 {
compatible = "shared-dma-pool";
reg = <0x0 0x50000000 0x0 0x01000000>;
no-map;
};
};
这里addr和size都是2个32位数, 因此reg的格式为:
reg = <high_addr low_addr high_size low_size>;
3). status
指示设备的状态,可以是"okay"、"disabled"等。 比如:
demo_uio: uio@0 {
compatible = "neardi,demo-uio";
reg = <0x0 (512 * 0x100000) 0x0 (16 * 0x100000)>; // 16M size
interrupt-parent = <&gic>;
interrupts = <0 192 4>;
memory-region = <&demo_memory>;
status = "okay";
};
4). gpios
定义设备使用的GPIO引脚, 比如:
leds: leds {
compatible = "gpio-leds";
status="okay";
work_led1: work1 {
gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>; //15
linux,default-trigger = "heartbeat";
};
};
5). interrupts
定义设备使用的中断号, 比如:
hym8563: hym8563@51 {
compatible = "haoyu,hym8563";
reg = <0x51>;
#clock-cells = <0>;
clock-frequency = <32768>;
clock-output-names = "hym8563";
pinctrl-names = "default";
pinctrl-0 = <&hym8563_int>;
interrupt-parent = <&gpio0>;
interrupts = <RK_PB0 IRQ_TYPE_LEVEL_LOW>;
};
• interrupt-parent:指定中断控制器的父节点。在这个例子中,&gpio0表示这个设备的中断是由gpio0中断控制器处理的。
• interrupts:定义中断号和触发类型。
• RK_PB0:这是中断号,表示具体的GPIO引脚(在这个例子中是RK_PB0引脚)。
• IRQ_TYPE_LEVEL_LOW:这是中断触发类型,表示低电平触发。
此处中断表示该设备使用中断控制器gpio0的第RK_PB0号中断,并且该中断是低电平触发的。
pinctrl
pinctrl(Pin Control)子系统用于管理和配置引脚的多路复用、配置参数(如上拉/下拉电阻、驱动能力等)以及引脚组。
通常包括以下属性:
• pinctrl-names:定义设备的不同状态,如default、sleep等。
• pinctrl-0、pinctrl-1等:引用具体的引脚配置节点。比如:
leds: leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&work1_led_pin>;
status="okay";
work_led1: work1 {
gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
};
};
&pinctrl {
leds {
work1_led_pin: work1_led_pin {
rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
DTS 节点引用及属性替换
可以通过多种方式替换节点属性, 比如直接更改节点属性。 这里我们讨论Overlay模式, 也即覆盖。
节点引用格式:
&node-name {
};
比如把i2c1给禁用, 则:
&i2c1 {
status = "disabled";
};
这样就把之前定义的i2c1节点里的status属性设置为"disabled"。
DTS 特殊节点
1). chosen
chosen节点主要用于传递一些启动参数给操作系统内核,特别是从U-Boot传递到Linux内核的参数。这个节点并不是一个实际的硬件设备,而是一个特殊的节点,用于存储启动相关的信息。
chosen节点里的bootargs,用于传递内核启动参数。也可以在设备运行时查看此参数, 比如:
neardi@LPA3588:~$ cat /proc/device-tree/chosen/bootargs
storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTUUID=614e0000-0000
在U-Boot中,启动参数通常通过环境变量bootargs来设置。在启动Linux内核之前,U-Boot会将这些参数写入设备树的chosen节点中。
2). aliases子节点
aliases节点通过为设备节点分配别名,使得访问这些节点更加简便。
例如,可以为接在I2C控制器上的传感器分配别名,如speed、ir_distance等。
aliases {
speed= &i2c0;
ir_distance= &i2c1;
};
这样会在/dev/speed替换/dev/i2c-0。通过这些别名,可以更方便地在驱动程序和其他代码中引用这些设备。