Linux DeviceTree学习(一)

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];
};
发布了47 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u013836909/article/details/95668279