一、概念
设备树是用来描述设备硬件上信息,设备树文件dts被编译成dtb文件,dtb文件再传给内核,之后内核解析dtb,获得其中的设备信息,构造出一系列device_node结构体,这些结构体最后会转换为platform_device结构体被程序调用。也因为dts存在的缘故,所以我们不必要在c文件中特别定义设备信息,避免了一个单板程序就要定义一个c文件描述的问题,对于程序只需要调用platform_device结构体,在编译时候链接具体的设备dts文件,达到了程序和硬件解耦的目的。
二、dts文件格式
/ {
node1 {
a-string-property = "A string"; // 值为字符串
a-string-list-property = "first string", "second string";// 值为字符数组
a-byte-data-property = [0x01 0x23 0x34 0x56]; // 值为二进制
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property; // 值为kog
/* each number (cell) is a uint32 */
a-cell-property = <1 2 3 4>; // cells(由uint32组成)
child-node1 {
};
};
};
上述只是格式展示,释义如下:
一个根节点:”/“;
一对子节点:”node1”和”node2”;
子节点的子节点:”child-node”;
属性定义: 属性值可以为空、字符串、cells(整数组成)、数组及二进制等任意字节流;
属性中常用的字节流如下:
# 字符串,用双引号引用:
string-property = "A string";
#cells(32 bits),用尖括号引用分隔开的32bit无符号整数:
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";
三、常见的属性
# lable节点,使用如下:
PIC: pic@10000000 {
interrupt-controller;
};
another-device-node {
interrupt-parent = <&PIC>; // 使用label来引用上述节点,
// 使用lable时实际上也是使用phandle来引用,
// 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};
# reg属性
reg 属性的值一般是 (address, length) 对,reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。
例如:一个设备有两个寄存器块,一个的地址是0x3000,占据32字节;另一个的地址是0xFE00,占据256字节,表示如下:
reg = <0x3000 0x20 0xFE00 0x100>;
注:上述对应#address-cells = <1>; #size-cells = <1>;。
# model属性
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,例如:
model = "Ruijie SoC";
# address-cells 和 size-cells
address-cells 和 size-cells的值都是无符号 32 位整型,可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位), #size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。#address-cells 和 #size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性都是和 地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:
reg = <address1 length1 address2 length2 address3 length3……>
例如一个64位的处理器:
soc {
#address-cells = <2>;
#size-cells = <1>;
serial {
compatible = "xxx";
reg = <0x4600 0x5000 0x100>; /*地址信息是:0x00004600 00005000,长度信息是:0x100*/
};
};
# range属性
ranges属性值可以为空或者按照 (child-bus-address,parent-bus-address,length) 格式编写的数字矩阵, ranges 是一个地址映射/转换表, ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成:
child-bus-address:子总线地址空间的物理地址,由父节点的 #address-cells 确定此物理地址所占用的字长。
parent-bus-address:父总线地址空间的物理地址,同样由父节点的 #address-cells 确定此物理地址所占用的字长。
length:子地址空间的长度,由父节点的 #size-cells 确定此地址长度所占用的字长。
# aliases 节点
aliases 节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。
例如:定义 flexcan1 和 flexcan2 的别名是 can0 和 can1。
aliases {
can0 = &flexcan1;
can1 = &flexcan2;
};
# chosen 节点
chosen 并不是一个真实的设备, chosen 节点主要是为了 uboot 向 Linux 内核传递数据,重点是 bootargs 参数。例如:
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};
# phandle属性
节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样),唯一标识一个节点,其他节点可以应用这个属性来引用对应节点
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node {
interrupt-parent = <1>; // 使用phandle值为1来引用上述节点
};