1、设备树引入
1.1、诞生的缘由
常人做法:
我们想一想,我们要平时要描述一个外设资源会怎样去做?
在c代码中指定,在代码中描述出来,再好一点就将驱动和设备分离出来。
问题出现:
使用同一个芯片的板子,它们所用的外设资源不一样,比如A板的LED用GPIO A,B板的LED用GPIO B。那么就需要不一样的代码来描述。
随着ARM芯片的流行,内核中针对这些ARM板保存有大量的、没有技术含量的文件。
解决方法:
Linus大发雷霆:“this whole ARM thing is a f*cking pain in the ass”。
Linus发火之后,内核开始全面使用设备树来改造,神人就神人。
牛人发火就是不一样!!!
1.2、为什么叫设备“树”?
设备树(Device Tree),将这个词分开就是“设备”和“树”。“树”这个描述非常形象,因为它确实像树,如下图。
1.3、设备树简介
- 本质:
是采用树状结构来描述板子上的设备信息的文件
- 作用:
用来给内核里的驱动程序,指定硬件的信息
- 特点:
设备树都有一个根节点,其下是由大括号括住的设备节点
2、linux设备树中的DTS、DTB和DTC
2.1、DTS(DeviceTree Source)
-介绍:
.dts文件是设备树的源文件
- 主要功能:
根据开发板的结构,描述开发板的设备信息
2.2、DTB(DeviceTree Blob)
- 介绍:
.dtb文件是.dts被DTC编译后的二进制格式的设备树文件
- 作用:
可以被Linux内核解析
2.3、DTC(DeviceTree Compiler)
- 介绍:
DTC是将.dts编译为.dtb的工具,相当于gcc
2.4、三者关系图
3、设备树语法
3.1、DTS文件布局
/dts-v1/; // 表示版本
[memory reservations] // 格式为: /memreserve/ <address> <length>;
/ { // '/'表示根节点
[property definitions]
[child nodes]
};
3.2、.dtsi 头文件
3.2.1、介绍
由于一个SoC可能对应多个设备,这些.dst文件可能包含很多共同的部分,共同的部分一般被提炼为一个.dtsi 文件,这个文件相当于C语言的头文件
3.2.2、作用
一般.dtsi文件用于描述SOC的内部外设信息,比如CPU架构、主频、外设寄存器地址范围,比如 UART、IIC 等等
3.2.3、举例
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
注意:.dts 文件引用 C 语言中的.h 文件,甚至也可以引用.dts 文件
不过,在编写设备树头文件的时候最好选择.dtsi 后缀
3.3、设备节点
3.3.1、作用
描述设备的信息,设备树中的基本单元
3.3.2、语法
- 格式
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
};
- label
节点标签,为了方便访问节点
可以直接通过&label 来访问这个节点
- node-name
节点名字,为 ASCII 字符串
节点名字应该要能够清晰的描述出节点的功能
- unit-address
一般表示设备的地址或寄存器首地址
如果某个节点没有地址或者寄存器的话“unit-address”可以不要
3.4、常用的属性
节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。
3.4.1、compatible 属性
“compatible”表示“兼容”,对于某个LED,内核中可能有A、B、C三个驱动都支持它,那可以这样写:
led {
compatible = “A”, “B”, “C”;
};
内核启动时,就会为这个LED按这样的优先顺序为它找到驱动程序:A、B、C
- 根节点下也有compatible属性,用来选择哪一个“machine desc”:一个内核可以支持machine A,也支持machine B,内核启动后会根据根节点的compatible属性找到对应的machine desc结构体,执行其中的初始化函数。
- compatible的值,建议取这样的形式:“manufacturer,model”,即“厂家名,模块名”。
3.4.2、 #address-cells、#size-cells属性
这两个属性的值都是无符号 32 位整形。
- address-cells:描述子节点reg属性值的地址表中首地址cell数量,表明子节点reg中 address 这个数据所占用的字长。
- size-cells:描述子节点reg属性值的地址表中地址长度cell数量,表明子节点reg中 length 这个数据所占用的字长。
比如一段内存,怎么描述它的起始地址和大小?
/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
address-cells为1,所以reg中用1个32位数来表示地址,即用0x80000000来表示地址
size-cells为1,所以reg中用1个32位数来表示大小,即用0x20000000表示大小
3.4.3、model 属性
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比
如:
model = "wm8960-audio";
3.4.4、status 属性
status 属性是和设备状态有关的, status 属性值也是字符串,字符串是设备的状态信息,可选的状态如下表所示:
值 | 描述 |
---|---|
“okay” | 设备正常运行 |
“disabled” | 设备不可操作,但是未来可以变为可操作性,如热插拔设备 |
“fail” | 发生严重错误,需要修复 |
“fail-sss” | 含义和“fail”相同,后面的 sss 部分是检测到的错误内容 |
3.4.5、reg 属性
reg的本意是register,用来表示寄存器地址。
但是在设备树里,它可以用来描述一段空间。对于ARM系统,寄存器和内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。
reg属性的值,是一系列的“address size”,用多少个32位的数来表示address和size,由其父节点的#address-cells、#size-cells决定。
示例:
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
3.5、根节点
每个设备树文件只有一个根节点“/”,其他所有的设备节点都是它的子节点。
每个节点都有 compatible 属性,根节点“/”也不例外。
- 根节点 compatible 属性:
作用:①通过根节点的 compatible 属性可以知道我们所使用的设备;
②Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备。
组成:①第一个值描述了所使用的硬件设备名字
②第二个值描述了设备所使用的 SOC
3.6、向节点追加或修改内容
3.6.1、直接修改
我们设想一下,当我们想要添加一个I2C设备,会怎么做,可能很多人想直接在I2C节点下添加信息:
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled";
// 我们添加的I2C子节点
i2c_device@xx {
/* 子节点信息 */
};
};
虽然说这样写没有错,但是有个大问题!i2c1节点是定义在imx6ull.dtsi 文件中的,而前面讲得.dtsi是设备树的头文件,这样就会导致:每一个引用imx6ull.dtsi 文件的设备,都添加了i2c_device这个设备,这并不是我们想要的。
3.6.2、通过标签访问修改
一般情况下,都是在对应的.dts文件下通过引用标签,对相应节点进行修改或添加的。
示例:
&i2c1 {
clock-frequency = <100000>; // 向i2c1节点添加clock-frequency属性
status = "okay"; // 将i2c1节点的status属性修改为okay
i2c_device@xx { // 在i2c1节点下添加子节点
/* 子节点信息 */
};
};
4、编译设备树
要编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命令:
make all
编译 Linux 源码中的所有东西,包括 zImage,.ko 驱动模块以及设备树
或者
make dtbs
只编译设备树
5、内核对设备树的处理
- dts文件在PC机上被编译为dtb文件
- u-boot把dtb文件传给内核
- 内核解析dtb文件,把每一个节点都转换为device_node结构体
- 对于某些device_node结构体,会被转换为platform_device结构体