一、什么是设备树
- uboot启动内核用到zImage,imx6ull-alientek-emmc.dtb。bootz 80800000 – 83000000.
- 设备树:设备和树。Device Tree
- 在单片机驱动里面比如W25QXX,SPI,速度都是在.c文件里面写死。板级信息都写到.c里面,导致linux内核臃肿。因此 将板子信息做成独立的格式,文件扩展名为.dts。一个平台或者机器对应一个.dts。
使用设备树的特点在于,在设备树dts文件指定硬件资源,dts被编译为dtb文件, 在启动单板时,U-boot会将dtb文件传给内核,使得驱动程序与硬件分离,我们只需要修改dts文件,便能实现需求。这就是设备树易于扩展的特点. 硬件有变动时不需要重新编译内核或驱动程序,只需要提供不一样的dtb文件
二、DTS、DTB和DTC的关系
DTS 就是Device Tree Source,是一个文本形式的文件,用于描述硬件信息,文件后缀名 .dst
,一般在如下路径
/arch/arm/boot/dts
.dts
相当于.c,,就是DTS源码文件。
DTC工具相当于gcc编译器,将.dts
编译成.dtb
。
.dtb
相当于bin文件,或可执行文件。
通过make dtbs编译所有的dts文件。如果要编译指定的dtbs
make imx6ull-alientek-emmc.dtb
三、DTS基本语法
-
设备树也有头文件,扩展名为.dtsi。可以将一款SOC他的其他所有设备/平台的共有的信息提出来,作为一个通用的.dtsi文件。
-
DTS也是
/
开始,根节点 -
从/根节点开始描述设备信息
-
在/根节点外有一些
&cpu0
这样的语句是追加
。 -
节点名字,完整的要求,参见
Power_ePAPR_APPROVED_v1.12.pdf
2.2.1小节
node-name@unit-address
unit-address
一般都是外设寄存器的起始地址,有时候是I2C的设备地址,或者其他含义,具体节点具体分析。设备树里面常常遇到如下所示节点名字:
intc: interrupt-controller@00a01000
:
前面是标签label,后面才是名字。intc,完整的名字是interrupt-controller@00a01000。&intc
lable
的目的是方便访问节点,感觉就是名字的简化。
四、创建小型的设备树模板
设备树例子:
/dts-v1/;
#include .h
#include .dtsi
/{
/*不同的/根节点内容会合并更新*/
/*skeletondtsi文件*/
#address-cells = <1>;
#size-cells = <1>;
chosen { stdout-path = &uart1;};
aliases {
can0 = &flexcan1;
。。。
};
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
/*imx6ull-aliente-emmc.dts中文件*/
model = "Freescale i.MX6 ULL 14x14 EVK Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
chosen {
};
memory {
};
reserved-memory {
};
...
};
&cpu0{
}
五、设备树在系统中的体现
系统启动以后可以在根文件系统里面看到设备树的节点信息。在/proc/device-tree/目录下存放着设备树信息。
内核启动的时候会解析设备树,然后在/proc/device-tree/目录下呈现出来。
六、特殊节点
- aliases (翻译:别名)
主要功能就是定义别名,定义别名的目的就是为了方便访问节点,不过一般用label访问 - chosen节点,主要目的就是将uboot里面bootargs环境变量值,传递给Linux内核作为命令行参数。cmdline;uboot里面bootargs值为:
bootargs=console=ttymxc0,115200 rw root=/dev/nfs nfsroot=192.168.1.249:/home/zzk/linux/nfs/rootfs ip=192.168.1.50:192.168.1.249:192.168.1.1:255.255.255.0::eth0:off
linux内核cmdline值为:
Kernel command line: console=ttymxc0,115200 rw root=/dev/nfs nfsroot=192.168.1.249:/home/zzk/linux/nfs/rootfs ip=192.168.1.50:192.168.1.249:192.168.1.1:255.255.255.0::eth0:off
uboot是如何向kernel传递bootargs?
经过查看发现chosen节点中包含bootargs属性,属性值和uboot的bootargs一致。
uboot接触过dtb,最终通过bootz 80800000 – 83000000 来启动内核。经过分析判断uboot拥有bootargs环境变量和dtb,因此最有可能“作案”。
最终发现在uboot的fdt_chosen函数中会查找chosen节点,并且在里面添加bootargs属性,属性值为bootargs变量值。
七、特殊的属性
compatible
属性,值是字符串。很重要
设备节点下的compatible用来匹配Linux内核中的驱动程序
根节点/下面的compatible,内核启动的时候会检查是否支持此平台。
不使用设备树的时候通过machine id来判断内核是否支持此机器。
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
MACHINE_START(MX35_3DS, "Freescale MX35PDK")
/* Maintainer: Freescale Semiconductor, Inc */
.atag_offset = 0x100,
.map_io = mx35_map_io,
.init_early = imx35_init_early,
.init_irq = mx35_init_irq,
.init_time = mx35pdk_timer_init,
.init_machine = mx35_3ds_init,
.reserve = mx35_3ds_reserve,
.restart = mxc_restart,
MACHINE_END
展开以后:
static const struct machine_desc __mach_desc_MX35_3DS __used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_MX35_3DS, //机器ID
.name = "Freescale MX35PDK",
.atag_offset = 0x100,
.map_io = mx35_map_io,
.init_early = imx35_init_early,
.init_irq = mx35_init_irq,
.init_time = mx35pdk_timer_init,
.init_machine = mx35_3ds_init,
.reserve = mx35_3ds_reserve,
.restart = mxc_restart,
};
使用设备树的时候不使用机器ID,而是使用根节点/下的compatible。
#define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = ~0, \
.name = _namestr,
DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
.map_io = imx6ul_map_io,
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.init_late = imx6ul_init_late,
.dt_compat = imx6ul_dt_compat,
MACHINE_END
展开以后就是:
static const struct machine_desc __mach_desc_IMX6UL __used
__attribute__((__section__(".arch.info.init"))) = {
.nr = ~0,
.name = "Freescale i.MX6 Ultralite (Device Tree)",
.map_io = imx6ul_map_io,
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.init_late = imx6ul_init_late,
.dt_compat = imx6ul_dt_compat,
};
- model 描述设备模块信息
- status 设备属性,表示设备状态
- #address-cells和#size-cells属性
#address-cells 属性值决定了子节点 reg 属
性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)
八、Linux内核的OF操作函数
- 驱动如何获取到设备树中节点信息。在驱动中使用OF函数获取设备树属性内容。
- 驱动要想获取到设备树节点内容,首先要找到节点。