设备树的引入与作用
以LED驱动为例,如果要更换LED所用的GPIO引脚,需要修改驱动程序源码,重新编译驱动,重新加载驱动。
在内核中,使用同一个芯片的板子,它们所用的外设资源也不一样,比如A板使用GPIO A,B板使用GPIO B。
而GPIO的驱动程序既支持GPIO A,也支持GPIO B,需要指定使用哪一个引脚,怎么指定?在C代码中指定。
随着ARM芯片流行,内核中针对这些ARM板保存有大量的、没有技术含量的文件。
设备树是用来给内核里的驱动程序指定硬件的信息。
比如LED驱动,在内核的驱动程序里去操作寄存器,但是操作哪一个引脚?由设备树指定。
/sys/firmware/devicetree目录下是以目录结构呈献的dtb文件,根节点对应base目录,每一个节点对应一个目录,每一个属性对应一个文件。
这些属性的值如果是字符串,可以使用cat命令把它打印出来;对于数值,可以用hexdump把它打印出来。
一个单板启动时,u-boot先运行,它的作用是启动内核。U-boot会把内核和设备树文件都读入内存,然后启动内核。
在启动内核时会把设备树在内存中的地址告诉内核。
设备树的语法
我们需要编写设备树文件(dts:device tree source),它需要编译为dtb(device tree blob)文件,内核使用的是dtb文件。
dts文件是根本,它的语法很简单。
/dts-v1/; //表示版本
/{
};
设备树中的基本单元,被称为node
[label:]node-name[@unit-address]{
[properties definitions]
[child nodes]
};
label是标号,可以省略。label的作用是为了方便引用node。
/dts-v1/;
/{
uart0:uart@fe001000{
compatible = "ns16550";
reg=<0x80908090 0x100>;
};
};
可以使用2种方法修改uart@fe001000这个node。
//在根节点之外使用label引用node
&uart0{
status = "disabled";
};
//或在根节点之外使用全路径
&{/uart@fe001000}{
status = "disabled";
};
properties的格式
简单地说,properties就是"name=value",value有很多种取值方式。
dts文件包含dtsi文件
设备树文件不需要我们从零写出来,内核支持某款芯片比如imx6ull,在内核的arch/arm/boot/dts目录下就有了能用的设备树模板,一般命名为xxx.dtsi,i表示include,被别的文件引用的。
我们使用某款芯片制作出了自己的单板,所用资源跟xxxx.dtsi是大部分相同,小部分不同,所以需要引用xxxx.dtsi并修改。
dtsi文件跟dts文件的语法是完全一样的。
dts中可以包含.h头文件,也可以包含dtsi头文件,在.h头文件中可以定义一些宏。
/dts-v1/;
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
/{
};
常用的属性
#address-cells、#size-cells
- cell指一个32位的数值
- address-cells:address要用多少个32位数来表示
- size-cells:size要用多少个32位数来表示。
/{
#address-cells = <1>;
#size-cells = <1>;
memory{
reg = <0x80000000 0x20000000>;
};
}
compatible表示兼容,对于某个LED,内核中可能有A、B、C三个驱动都支持它,那可以这样写:
led{
compatible = "A","B","C";
};
内核启动时,就会为这个LED按这样的优先顺序找到驱动程序。
根节点下也有compatible属性,用来选择哪一个“machine desc”:一个内核可以支持machine A,也支持machine B,内核启动后会根据根节点的compatible属性找到对应的machine desc结构体,执行其中的初始化函数。
compatible的值,建议取这样的形式:“manufacture,model”,即“厂家名,模块名”。
model
compatible属性是一个字符串列表,表示可以兼容A、B、C等驱动。
model准确地定义这个硬件是什么。
根节点中可以这样写:
{
compatible = "samsung,smdk2440", "samsung,mini2440";
model = "jz2440_v3";
};
它表示这个单板,可以兼容内核中的“smdk2440”,也兼容“mini2440”。
status
dtsi文件中定义了很多设备,但是在你的板子上某些设备是没有的。这时可以给这个设备节点添加一个status属性,设置为“disabled”。
&usart1{
status = "disabled";
};
reg
reg的本意是register,表示寄存器地址。
但是在设备树里,它可以用来描述一段空间。
反正对于ARM系统,寄存器和内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。
reg属性的值,是一系列的“address size”,用多少个32位的数来表示address和size,由其父节点的#address-cells、#size-cells决定。
name
name表示节点的名字,在跟platform_driver匹配时,优先级最低。
compatible属性在匹配过程中,优先级最高。device_type优先级为中。
常用的节点(node)——根节点
/dts-v1/;
/{
model = "SMDK2440";
compatible = "samsung,smdk2440";
#address-cells = <1>;
#size-cells = <1>;
};
chosen节点
可以通过设备树文件给内核传入一些参数,这要在chosen节点中设置bootargs属性。
chosen {
bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};
在内核中直接make
设置ARCH、CROSS_COMPILE、PATH这三个环境变量后,进入ubuntu上板子内核源码的目录,执行如下命令即可编译dtb文件
make dtbs V=1