传统的LINUX内核中,ARM架构的板级硬件细节过多地被硬件编码在arch/arm/plat-xxx和arch/arm-xxx,比如platform设备,resource,
i2c_board,spi_aboard_info以及各种硬件的platform_data,这些板级细节代码对内核来讲不过是垃圾代码。
而采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码,
导致ARM的merge工作量较大。
之后经过讨论。对ARM平台的相关代码做出如下相关的规范和调整,这个也是引入DTS的原因。
1.ARM的核心代码仍然保存在arch/arm目录下
2.ARM SoC core architecture code保存在arch/arm目录下
3.ARM SoC的周边外设模块的驱动保存在drivers目录下
4.ARM SoC的特定代码在arch/arm/mach-xxx目录下
5.ARM SoC board specific的代码被移除,由Device Tree机制来负责硬件拓扑和硬件资源信息
本质上,Device Tree改变了原来用code的方式将硬件信息嵌入到内核代码的方法,改用bootloader传递一个DB的形式。对于嵌入式系统,
在系统启动阶段,bootloader会加载内核并将控制权限交给呢耦合,此外,还需要把上述的三个参数信息传递给kernel,以便有较大的灵活性,
在linux kernel中,Device Tree的设计目标就是如此。
在device tree中,可描述的信息包括:
1.CPU的数量和类别
2.内存基地址和大小
3.总线和桥
4.外设连接
5.中断控制和中断的使用情况。
6.GPIO控制器和clock使用情况
7.clock控制器和clock使用情况
它基本就是一棵电路板上的CPU,总线,设备组成的树,Bootloader会将这棵树传递给内核,然后内核来识别这棵树,
这根据它展开出Linux内核中的 platform_device,i2c_client,spi_device等设备,而这些设备用到的内存,IRQ等资源,也被传递给内核,
内核会将这些资源绑定给展开的相应设备。
Linux内核从3.x开始引入设备树的概念,用于实现驱动代码和设备信息相分离。在设备树出现以前,所有关于设备的具体信息都要写在驱动里,
一旦外围设备变化,驱动代码就要重写,引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,
这样,如果硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改驱动代码,
比如ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的“arch/arm/booot/dts”目录内,
比如stm1a-dk1参考板的板级设备文件就是“arch/arm/boot/dts/”目录内,
比如stm1a-dk1参考板的板级设备树文件就是“arch/arm/boot/stm32mp157a-dk1.dts”。
这个文件可以通过make ditbs命令编译成二进制的.dtb文件供内核驱动使用。
基于同样的的软件分层设计的思想,由于一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,
那么势必相当一些.dts文件有重复的部分,为了解决这个问题,
Linux设备树目录把SoC公用的部分或者多个machine共同部分提炼为了相应的.disi文件。这样每个.dts就只有自己差异的部分,
公有的部分只需要“include”相应的.dtsi文件。这样整个设备树的管理更加有序.
dts
硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份例如stm32mp157a-dk1.dts,一般在Linux源码中存在大量的dts文件,对于arm架构可以在arch/arm/boot/dts,找到相应的dts,一个dts文件对应一个ARM的machine
dtsi
对于一些相同的dts配置可以抽象到dtsi文件中,然后类似C语言的方式可以include到dts文件中,对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置
dtc
dtc是编译dts的工具,可以在Ubuntu系统上通过指令apt-get insatll device-tree-complier安装dtc工具,不过在内核源码scripts/dtc路径下包含了dtc工具;
dtb
dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序加载到内核。所以Bootloader需要支持设备树才行;
Kernel也需要加入设备树的支持;
/dts-v1/;
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01 23 34 56];
child-node1 {
9 first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 {
};
};
};
device tree的基本单元是一个node。这些node被组织成树状结构,除了root node,每个node都只有一个parent。一个device tree文件中只能有一个root node。每个node中包含了若干的property/value来描述node的一些特性。每个node用节点名字(node name)标识,节点名字的格式node-name@unit-address。如果该node没有reg属性(后面会描述这个property),
bus上相关。例如对cpu,其unit-address就是寄存器地址。root node的node name是确定的,必须是"/."。
从上面的设备树可以看到
1)一个根节点“/”;
2)根节点下面有可以有多个子节点
3)子节点可以包含自己的子节点如“child-node1”,“child-node2”
4)各个几点都有一系列的属性 (property)
(1)这些属性可能为空,如an-empty-property
(2) 可能为字符 a-string-proerty
(3) 可能为字符串树组 a-string-list-property
(4) 可能为cells(由u32)整型组成,如second-child-property
基本数据类型:
1)text string(以null结束),以双引号括起来,如:string-property=“a string”;
2)cells是无符号32位无符号整型数,以括号括起来,如cell-proerty=<0xbeef 123 0xabcd1234>;
3)binary data 以方括号括起来,如:binary-property = [0x01 0x23 0x45 0x67];
4)不同类型数据可以在同一个属性中存在,以逗号分格,如:mixed-property = “a string”, [0x01 0x23 0x45 0x67],<0x12345678>;
5)多个字符串组成的列表也使用逗号分格,如:string-list = “red fish”,”blue fish”;
/ {
model = "st FS-MP1A Discovery Board&