扫盲知识
DTS:设备树源文件
DTSI:设备树头文件(.dts 文件引用 C 语言中的.h 文件,甚至也可以引用.dts 文 件)
DTC:编译设备树的工具
DTB:设备树被编译之后的二进制文件
如果要编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命 令:
make all 或者: make dtbs。
当选中 I.MX6ULL 这个 SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到
I.MX6ULL 这个 SOC 的板子对应的.dts 文件都会被编译为.dtb。
设备树语法
1 / {
2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";//厂商,板子名称(会和内核匹配是否对应得上)
3 }
cpu0: cpu@0 {// @cpu0相当于@cpu,cpu0是标签,cpu是节点名称
10 compatible = "arm,cortex-a7";
11 device_type = "cpu";
12 reg = <0>;
13};
范例:
1 / {
2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
3
4 cpus {
5 #address-cells = <1>;
6 #size-cells = <0>;
7
8 //CPU0 节点
9 cpu0: cpu@0 {
10 compatible = "arm,cortex-a7";
11 device_type = "cpu";
12 reg = <0>;
13 };
14 };
15
16 //soc 节点
17 soc {
18 #address-cells = <1>;
19 #size-cells = <1>;
20 compatible = "simple-bus";
21 ranges;
22
23 //ocram 节点
24 ocram: sram@00900000 {
25 compatible = "fsl,lpm-sram";
26 reg = <0x00900000 0x20000>;//起始地址,大小
27 };
29 //aips1 节点
30 aips1: aips-bus@02000000 {
31 compatible = "fsl,aips-bus", "simple-bus";
32 #address-cells = <1>;
33 #size-cells = <1>;
34 reg = <0x02000000 0x100000>;
35 ranges;
37 //ecspi1 节点
38 ecspi1: ecspi@02008000 {
39 #address-cells = <1>;
40 #size-cells = <0>;
41 compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
42 reg = <0x02008000 0x4000>;
43 status = "disabled";
44 };
36 }
38 //aips2 节点
39 aips2: aips-bus@02100000 {
40 compatible = "fsl,aips-bus", "simple-bus";
41 #address-cells = <1>;
42 #size-cells = <1>;
43 reg = <0x02100000 0x100000>;
44 ranges;
//usbotg1 节点
56 usbotg1: usb@02184000 {
57 compatible = "fsl,imx6ul-usb", "fsl,imx27-usb";
58 reg = <0x02184000 0x4000>;
59 status = "disabled";
};
45 }
46
47 //aips3 节点
48 aips3: aips-bus@02200000 {
49 compatible = "fsl,aips-bus", "simple-bus";
50 #address-cells = <1>;
51 #size-cells = <1>;
52 reg = <0x02200000 0x100000>;
53 ranges;
71 //rngb 节点
72 rngb: rngb@02284000 {
73 compatible = "fsl,imx6sl-rng", "fsl,imx-rng", "imxrng";
74 reg = <0x02284000 0x4000>;
75 };
54 }
55 }
29 }
设备树在系统中的体现
Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的/proc/device
tree 目录下根据节点名字创建不同文件夹

使用cat命令查看model和compatible内容,正是设备树文件根节点的对应属性值。

进一步查看子节点里面的节点

特殊节点
aliases 子节点
18 aliases {
19 can0 = &flexcan1;
20 can1 = &flexcan2;
21 ethernet0 = &fec1;
22 ethernet1 = &fec2;
23 gpio0 = &gpio1;
24 gpio1 = &gpio2;
......
42 spi0 = &ecspi1;
43 spi1 = &ecspi2;
44 spi2 = &ecspi3;
45 spi3 = &ecspi4;
46 usbphy0 = &usbphy1;
47 usbphy1 = &usbphy2;
48 };
aliases 节点的主要功能就是定义别名。
chosen节点
1chosen {
2 stdout-path = &uart1;
3 };
chosen 节点仅仅设置了属性“stdout-path”,表示标准输 出使用 uart1。但是当我们进入到/proc/device-tree/chosen 目录里面,会发现多了 bootargs 这个 属性,如图:

进一步查看bootargs这个属性的值:

这个属性的值和uboot里面设置的bootargs一样。
chosen节点是uboot添加的。

Linux 内核解析 DTB 文件

绑定信息文档
在设备树中添加一个硬件对应的节点的时候从如下路径查找对应稳定说明:
Linux 源码目录/Documentation/devicetree/bindings
设备树常用 OF 操作函数
查找节点的 OF 函数
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
name:要查找的节点名字。
返回值:找到的节点,如果为 NULL 表示查找失败。
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
函数参数和返回值含义如下:
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,也就是 device_type 属性值。
返回值:找到的节点,如果为 NULL 表示查找失败。
struct device_node *of_find_compatible_node(struct device_node *from,
const char *type, const char *compatible)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示
忽略掉 device_type 属性。
compatible:要查找的节点所对应的 compatible 属性列表。
返回值:找到的节点,如果为 NULL 表示查找失败
struct device_node *of_find_matching_node_and_match(struct device_node
*from, const struct of_device_id *matches, const struct of_device_id **match);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
matches:of_device_id 匹配表,也就是在此匹配表里面查找节点。
match:找到的匹配的 of_device_id。
inline struct device_node *of_find_node_by_path(const char *path);
path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个
节点的全路径。
返回值:找到的节点,如果为 NULL 表示查找失败
查找父/子节点的 OF 函数
struct device_node *of_get_parent(const struct device_node *node)
node:要查找的父节点的节点。
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
node:父节点。
prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为
NULL,表示从第一个子节点开始。
返回值:找到的下一个子节点。
提取属性值的 OF 函数
property *of_find_property(const struct device_node *np, const char *name, int *lenp);
np:
设备节点。
name: 属性名字。
lenp:属性值的字节数
返回值:找到的属性。
int of_property_count_elems_of_size(const struct device_node *np,
const char *propname,intelem_size);
np:设备节点。
proname: 需要统计元素数量的属性名字。
elem_size:元素长度。
返回值:得到的属性元素数量。
int of_property_read_u32_index(const struct device_node *np, const char *propname,
u32 index, u32 *out_value);
np:设备节点。
proname: 要读取的属性名字。
index:要读取的值标号。
out_value:读取到的值
返回值:0 读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有
要读取的数据,-EOVERFLOW 表示属性值列表太小。
...