Linux DeviceTree学习(一)
1 ARM Device Tree起源
在 Linux 2.6 中,ARM 架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx 和 arch/arm/mach-xxx,采用 Device Tree 后,许多硬件的细节可以直接透过它传递给 Linux,而不再需要在 kernel 中进行大量的冗余编码。
Device Tree 由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的 name 和 value。在 Device Tree 中,可描述的信息包括(原先这些信息大多被 hard code 到 kernel 中):
- CPU 的数量和类别
- 内存基地址和大小
- 总线和桥
- 外设连接
- 中断控制器和中断使用情况
- GPIO 控制器和 GPIO 使用情况
- Clock 控制器和 Clock 使用情况
它基本上就是画一棵电路板上 CPU、总线、设备组成的树,Bootloader 会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出 Linux 内核中的 platform_device、i2c_client、spi_device 等设备,而这些设备用到的内存、IRQ 等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。
2 Device Tree组成和结构
整个Device Tree 牵涉面比较广,即增加了新的用于描述设备硬件信息的文本格式,又增加了编译这一文本的工具,同时BootLoader也需要支持将编译后的Device Tree传递给Linux内核。
2.1 DTS(device tree source)
.dts文件是一种ASCII文本格式的Device Tree描述,在ARM Linux中,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts目录,另外一个SoC可能对应多个machine(即一个SoC可对应多个产品和电路板),而这些.dts问价需包含许多共同的部分,则Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分提炼为.dtsi。.dtsi与C语言的头文件类似。
2.2 DTC(device tree compiler)
-
将.dts编译为.dtb的工具,DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能Device Tree的情况下,编译内核的时候主机工具dtc会被编译出来,对应scripts/dtc/Makefile中的“hostprogs-y:=dtc”这一hostPro公司编译target。
在 Linux 内核的 arch/arm/boot/dts/Makefile 中,描述了当某种 SoC 被选中后,哪些.dtb文件会被编译出来,如与 VEXPRESS 对应的.dtb 包括:dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \ vexpress-v2p-ca9.dtb \ vexpress-v2p-ca15-tc1.dtb \ vexpress-v2p-ca15_a7.dtb \ xenvm-4.2.dtb
-
在 Linux 下,我们可以单独编译 Device Tree 文件。当我们在 Linux 内核下运行 make dtbs 时,若我们之前选择了 ARCH_VEXPRESS,上述.dtb 都会由对应的.dts 编译出来。因为 arch/arm/Makefile 中含有一个 dtbs 编译 target 项目。
Device Tree Blob(.dtb) -
.dtb是.dts被DTC编译后的二进制格式的Device Tree描述,可由Linux内核解析。通常在我们为电路板制作NAND、SD启动image时,会为.dtb文件单独留下一个很小的区域以存放之,之后的BootLoader在引导kernel的启动过程中,会先读取该.dtb到内存。
2.3 Binding
对于Device Tree中的节点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt,这些文档位于内核的Documentation/devicetree/bindings目录,其下又分为很多子目录。
2.4 BootLoader
Uboot 从v1.1.3开始支持Device Tree,其对ARM的支持则是和ARM内核支持Device Tree同期完成。
为了使能Device Tree,需要编译Uboot的时候在.config文件中加入
#define CONFIG_OF_LIBFDT
2.5 Device Tree编译方法
Dtb文件单独编译的方法如下:
dtc [-I input-format] [-O output-format][-o output-filename] [-V output_version] input_filename
参数说明:
-I:输入的文件格式,如:dts或dtb;
-O:输出的文件格式,如:dtb或dts;
-o:输出的文件路径;
-
1)dts编译成dtb
./dtc -I dts -O dtb -o B_dtb.dtb A_dts.dts
-
2)dtb编译成dts
./dtc -I dtb -O dts -o A_dts.dts A_dtb.dtb
2.6 Device Tree头信息
在Linux内核中使用struct fdt_header结构体描述。struct fdt_header结构体定义在scripts\dtc\libfdt\fdt.h文件中,具体如下:
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC--0xd00dfeed */
fdt32_t totalsize; /* total size of DT block */
fdt32_t off_dt_struct; /* offset to structure */
fdt32_t off_dt_strings; /* offset to strings */
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
fdt32_t version; /* format version */
fdt32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */
/* version 3 fields below */
fdt32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
fdt32_t size_dt_struct; /* size of the structure block */
};
2.7 Device Tree文件结构
Device Tree文件结构如上图所示,dtb的头部首先存放fdt_header的结构体信息,接着是填充区域,填充大小为off_dt_struct-sizeof(struct fd_header),填充的值为0。接着就是struct fdt_property结构体的相关信息,最后就是de_string。
节点(node)信息使用struct fdt_node_header结构体描述,属性信息使用struct fdt_property结构体描述。相关信息如下:
struct fdt_node_header {
fdt32_t tag; //node起始结束等信息标志位
char name[0]; //执行node名称的首地址
};
Tag的取值如下:
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: name off, size, content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
struct fdt_property {
fdt32_t tag; //标志为属性,取值为FDT_PROP
fdt32_t len; //属性值长度
fdt32_t nameoff; //nameoff为属性名称存储位置相对于off_dt_strings的偏移地址
char data[0];
};