设备树引入
一、设备树的基本知识
1、什么是设备树?为什么会有设备树?
2011年,Linux之父Linus Torvalds发现这个问题后,就通过邮件向ARM-Linux开发社区发了一封邮件,不禁的发出了一句“This whole ARM thing is a f*cking pain in the ass”。之后ARM Linux社区引入了设备树。为什么LinusTorvalds会爆粗口呢?
平台总线模型是把驱动分成了俩个部分,一部分是device,一部分是driver,设备信息和驱动分离这个设计非常的好。device部分是描述硬件的。一般device部分的代码会放在内核源码中arch/arm/plat-xxx和arch/arm/mach-xxx下面。但是随着Linux支持的硬件越来越多,在内核源码下关于硬件描述的代码也越来越多。并且每修改一下就要编译一次内核。长此以往Linux内核里面就存在了大量“垃圾代码”,而且非常多,这里说的“垃圾代码”是关于对硬件描述的代码。从长远看,这些代码对Linux内核本身并没有帮助,所以相当于Linux内核是“垃圾代码”。但是并不是说平台总线这种方法不好。
为了解决这个问题,设备树就被引入到了Linux上。使用设备树来剔除相对内核来说的“垃圾代码”,既用设备树来描述硬件信息,用来替代原来的device部分的代码。虽然用设备树替换了原来的device部分,但是平台总线模型的匹配和使用基本不变。并且对硬件修改以后不必重新编译内核。直接需要将设备树文件编译成二进制文件,在通过bootloader传递给内核即可。
所以设备树就是用来描述硬件资源的文件。
2、设备树的基本概念
①为什么叫设备树?
设备树是描述硬件的文本文件,因为语法结构像树一样。所以叫设备树.
如下图:
②基本名词解释
<1>DT:DeviceTree //设备树
<2>FDT:Flattened Device Tree //开放设备树,起源于OpenFirmware(OF)
<3>DTS:DeviceTree source的缩写 //设备树源码
<4>DTSI:DeviceTree sourcein Clude的缩写 //通用的设备树源码
<5>DTB:DeviceTree Blob的缩写 //编译设备树源码得到的文件
<6>DTC:DeviceTree Compiler的缩写 //设备树编译器
DTS、DTSI、DTC、DTB之间的关系:
Linux内核通过读取设备树的描述文件(通常是.dtb或.dts文件),来构建一个内存中的表示,内核和驱动程序则使用这个表示来识别和配置硬件设备。设备树文件在编译时从设备树源文件(.dts)转换成二进制形式(.dtb),然后通常被包含在内核映像或者作为独立文件提供给引导加载程序
补充:
dts
dts文件是一种ASCII文本格式的设备树描述文件,此文件适合人类阅读,主要是给用户看的。 硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份xxxx.dts,一般在Linux源码中存在大量的dts文件,对于 arm 架构可以在arch/arm/boot/dts找到相应的dts。对于rk3399开发板arch/arm/boot/dts/rk3399-nanopi4-common.dtsi中一般会包含一个公共部分的dtsi文件,如下:#include "rk3399-nanopi4-rkisp1.dtsi"
dtsi
对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于 C 语言的方式可以include到dts文件中
二、编译设备树 (DTC编译器的使用)
DTC编译器命令格式:
编译设备树:dtc -I dts -O dtb -o xxx.dtb xxx.dts
反编译设备树:dtc -I dtb -O dts -o xxx.dts xxx.dtb
三、设备树的基本语法
1、设备树根节点
根节点是设备树必须要包含的节点。根节点的名字是/。
/dts-v1/; //第一行表示dts文件的版本
/{ //根节点
};
2、设备树子节点格式
格式:
label: node-name @unit-address{
properties definitions
child nodes};
举例:
nodel{ //子节点,节点名称为nodel
nodel_child{ //子子节点,节点名称为nodel_child}
};
注意:同级节点下节点名称不能相同。不通级节点名称可以相同
3、节点名称
在对节点进行命名的时候,一般要体现设备的类型,比如网口一般命名成ethernet,串口一般命名成uart,对于名称一般要遵循下面的命令格式。
格式:标签:名称@<设备地址>
其中,[标签]和[@<设备地址>]是可选项,<名称>是必选项。另外,这里的设备地址没有实际意义,只是让节点名称更人性化,更方便阅读。
举例:
uart: serial@02288000
其中,uart就是这个节点标签,也叫别名,serial@02288000就是节点名称。
4、reg属性
reg属性可以来描述地址信息。比如寄存器的地址。
reg属性的格式如下:
reg =<address1 length1 address2 length2 address3 length3...>
举例:
reg= <0x02200000 0x4000>;
例
reg= <x022000000x4000
0x02205000 0x4000>;
5、#address-cell和#size-cells属性
#address-cell和#size-cells用来描述子节点中的reg信息中的地址和长度信息
举例:
nodel{
#address-cells = <1>;
#size-cells = <0>;
nodel-child {
reg = <0>;};
};
6、model属性
model属性的值是一个字符串,一般用model描述一些信息。比如设备的名称,名字等。
举例1:
model ="wm8960-audio”;
举例2:
model = "This is Linux board";
7、status属性
status属性是和设备的状态有关系的,status的属性值是字符串。属性值有下面几个状态可选:
8、compatible属性
compatible属性是非常重要的一个属性。compatible是用来和驱动进行匹配的,匹配成功以后
会执行驱动中的probe函数。
举例:
compatible ="xunwei”, "xunwei-board";
在匹配的时候会先使用第一个值xunwei进行匹配,如果没有就会使用第二个值xunwei-board进行匹配。
示例:
9、设备树特殊节点
aliases
特殊节点aliases用来定义别名。定义别名的目的就是为了方便引用节点。当然,除了使用aliases来命名别用,也可以在对节点命名的适合添加标签来命名别名。
举例:
aliases{
mmc0 = &sdmmc0;
mmcl = &sdmmcl;
mmc2 = &sdhci;
serial0 ="/simple@fe000000/serial@llc500”;};
chosen
chosen节点用来uboot给内核传递参数。重点是bootargs参数。chosen节点必须是根节点的子节点。
举例:
chosen{
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200”;}
device_type属性
在某些设备树文件中,可以看到device_type属性,device_type属性的值是字符串,只用于cpu节点或者memory节点进行描述。
举例1:
memory@30000000{
device_type =“memory”;
reg =<0x30000000 0x4000000>;
举例2:
cpu1: cpu@1{
device_type = "cpu”;
compatible = "arm,cortex-a35″, "arm, armv8”;
reg = <0x0 0x1>;};
自定义属性
设备树中规定的属性有时候并不能满足我们的需求,这时候我们可以自定义属性。
举例:
自定义一个管脚标号的属性pinnum。
pinnum = <0 1 2 3 4>;
示例:
四、DTS基本框架
设备树由一系列被命名的节点(Node)和属性(Property)组成,而节点本身可包含子节点。在设备树中,可描述的信息包括:
· CPU的数量和类别。
· 内存基地址和大小。
· 总线和桥。
· 外设连接。
· 中断控制器和中断使用情况。
· GPIO控制器和GPIO使用情况。
· 时钟控制器和时钟使用情况。
基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。
//一级节点:会展开成platfor_device
//内核会把这个节点展开成平台设备,设备名是ff720000.xyd-leds
补充:增加设备树属性信息,可以自动生成设备端代码。
对平台设备驱动模型来说,平台设备代码(platform_device.c)不需要实现,被设备树中设备文件所替代了。
如何添加设备树?
以rk3399作为例子
kernel-rockchip-nanopi4-linux-v4.4.y/arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi
需要添加的硬件资源:
xyd-leds@FF720000{
compatible="xyd-rk3399-leds";
reg=<0x0 0xFF720000 0x0 0x10000>;
reg-names="gpio0-reg-for-leds";
led-total=<1>;
};
编译:在内核源码的顶层目录执行以下命令:
make nanopi4-images -j8
把生成的resource.img 和 kernel.img 烧写到开发板上。
编写平台驱动模型里面的驱动代码,修改设备树,编译内核kernel.img and resource.img文件烧写到板子上.
最终结果: