设备树(一)—— 设备树基础分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ggxyx123/article/details/85273896

参考:https://www.devicetree.org/specifications/

一、什么是设备树

1、设备树的引入

Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a fucking pain in the ass”,ARM Linux社区对此作出了回应,引入设备树。

这是因为linux内核随着不断的发展维护,充斥着大量的板级文件细节,各种芯片厂商的arch/arm/plat-xxx和arch/arm/mach-xxx使得内核越来越臃肿庞大。这种以树状节点的方式描述一个设备的各种硬件信息:CPU、GPIO、时钟、中断、内存等,形成类似文本文件,很好的解决了这些问题。

2、设备树特点

a、对于传统字符驱动的编写有两种方式:

一是在驱动程序中,直接写死硬件资源,如:GPIO、寄存器地址、中断号等,使得硬件改动时,必须修改驱动程序。

二是采用总线驱动platform模型,将硬件资源与驱动软件分离,在platform_device中描述硬件资源,arch/arm/mach-xxx对应的文件,便是以platform_device描述各自CPU对应的硬件资源;在platform_driver中分配/设置/注册 file_operations结构体, 并从platform_device获得硬件资源。这种编写方式使得驱动易于扩展,硬件改动时只需修改platform_device或者platform_driver,这就导致linux内核产生大量的冗余代码。

b、 使用设备树的特点在于,在设备树dts文件指定硬件资源,dts被编译为dtb文件, 在启动单板时,U-boot会将dtb文件传给内核,使得驱动程序与硬件分离,我们只需要修改dts文件,便能实现需求。这就是设备树易于扩展,硬件有变动时不需要重新编译内核或驱动程序,只需要提供不一样的dtb文件。

3、编译设备树

设备树文件的格式为dts,包含的头文件格式为dtsi,dts文件是一种人可以看懂的编码格式。但是uboot和linux不能直接识别,他们只能识别二进制文件,所以需要把dts文件编译成dtb文件。dtb文件是一种可以被kernel和uboot识别的二进制文件。把dts编译成dtb文件的工具是dtc。Linux源码目录下scripts/dtc目录包含dtc工具的源码。在Linux的scripts/dtc目录下除了提供dtc工具外,也可以自己安装dtc工具,linux下执行:sudo apt-get install device-tree-compiler安装dtc工具。其中还提供了一个fdtdump的工具,可以反编译dtb文件。dts和dtb文件的转换如图所示。 
dtc工具的使用方法是:dtc –I dts –O dtb –o xxx.dtb xxx.dts,即可生成dts文件对应的dtb文件了。 
 

å¾1 dtsådtbæ件转æ¢

在编译linux内核时。也可以直接make dtbs生成dtb文件。

二、设备树dts格式

1、dts格式

a、dts文件的基本组成单元为:

/dts-v1/;                    //表示dts文件的版本号
 [memory reservations]       //表示内存保留,即CPU是否需要在flash上保留一段内存用作他用,可以有也可以没有
 / {                         //表示根节点
    [property definitions]   //就是属性定义,对当前节点描述,将硬件信息提供给内核处理
    [child nodes]            //子节点 
};

b、从s3c6410-mini6410.dts截取一段dts文件加以分析,dts文件也像C一样,可以包含.dtsi文件和.h文件,引用变量,引用节点

/dts-v1/; 
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include "s3c6410.dtsi"
/ {
    model = "FriendlyARM Mini6410 board based on S3C6410";
    compatible = "friendlyarm,mini6410", "samsung,s3c6410";

    memory@50000000 {
        device_type = "memory";
        reg = <0x50000000 0x10000000>;
    };
}

[property definitions]:
model = "FriendlyARM Mini6410 board based on S3C6410";  //对设备制造商的描述
compatible = "friendlyarm,mini6410", "samsung,s3c6410";   //对单板的描述,即整个dts文件支持描述的两个开发板
这两个属性的定义便是对根节点的描述,也就是对s3c6410-mini6410.dts文件的描述。

[child nodes]:
memory@50000000 {
        device_type = "memory";
        reg = <0x50000000 0x10000000>;
};
memory@50000000是根节点 / 的子节点,同样通过[property definitions]即device_type 属性和reg属性描述节点。

 c、在dts文件中,对于properties,有一些常用的、默认的、特殊的属性,定义如下:

model
        设备制造商的描述,如果有2款板子配置基本一致, 它们的compatible是一样的那么就通过model来分辨这2款板子
compatible
        定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备,即这个板子兼容哪些平台 
reg             
        描述设备资源在其父总线定义的地址空间中的地址。通常这意味着内存映射IO寄存器块的偏移量和长度,但在某些总线类型上可能有不同的含义。根节点定义的地址空间中的地址是CPU实际地址。
#address-cells
       在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells     
      在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
phandle
      节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样),使用phandle值来引用节点
bootargs
      内核command line参数, 跟u-boot中设置的bootargs作用一样 

 d、在dts文件中,引用其他节点:

1)phandle方式引用

pic@10000000 {
    phandle = <1>;
    interrupt-controller;
};
another-device-node {
    interrupt-parent = <1>;   // 使用phandle值为1来引用上述节点
};

2)label 方式引用

PIC: pic@10000000 {
    interrupt-controller;
};
another-device-node {
    interrupt-parent = <&PIC>;   // 使用label来引用上述节点, 
                                 // 使用lable时实际上也是使用phandle来引用, 
                                 // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};

3)直接引用

vic0: interrupt-controller@71200000 {
	compatible = "arm,pl192-vic";
	interrupt-controller;
	reg = <0x71200000 0x1000>;
	#interrupt-cells = <1>;
};
&vic0 {//直接引用vic0 节点,添加属性
	valid-mask = <0xffffff7f>;
	valid-wakeup-mask = <0x00200004>;
};

e、对于节点的属性描述,具有几种不同类型,如图所示:

举例说明:

1). Arrays of cells : cell就是一个32位的数据
interrupts = <17 0xc>;

2). 64bit数据使用2个cell来表示:
clock-frequency = <0x00000001 0x00000000>;

3). A null-terminated string (有结束符的字符串):
compatible = "simple-bus";

4). A bytestring(字节序列) :
local-mac-address = [00 00 12 34 56 78];  // 每个byte使用2个16进制数来表示
local-mac-address = [000012345678];       // 每个byte使用2个16进制数来表示

5). 可以是各种值的组合, 用逗号隔开:
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";
davicom,no-eeprom;

三、设备树dtb格式

dtb文件是dts编译后的供内核使用的文件,分析dtb文件,有助于后面深入理解设备树文件在内核中是如何被解析的,如何在设备驱动中使用。

1、dtb文件结构

从结构图中可以看出,dtb文件主要分为四块:dtb头部信息块、内存保留信息块、dts文件属性的值信息块、dts文件属性名信息块。

a、dtb头部信息块在dtb文件中的描述,完整的描述了dtb文件信息,内核通过这些头部信息即可解析设备树文件

struct fdt_header {
    uint32_t magic;            //0xd00dfeed
    uint32_t totalsize;        //dtb文件的大小
    uint32_t off_dt_struct;    //dts文件属性的值信息块的偏移地址
    uint32_t off_dt_strings;   //dts文件属性名信息块的偏移地址
    uint32_t off_mem_rsvmap;   //dts文件内存保留信息块的偏移地址
    uint32_t version;
    uint32_t last_comp_version;
    uint32_t boot_cpuid_phys;
    uint32_t size_dt_strings;
    uint32_t size_dt_struct;
};

b、内存保留信息块在dtb文件中的描述

struct fdt_reserve_entry {
    uint64_t address;    //保留内存的起始地址
    uint64_t size;       //保留地址的大小
};

c、dts文件属性的值信息块在dtb文件中的描述

struct {
    uint32_t len;        //属性值得长度
    uint32_t nameoff;    //属性名字在dtb文件中的偏移地址:off_dt_struct+nameoff 即为属性名字的地址
}

节点的一个属性描述以0x00000003开始,32byte表示属性值得长度,32byte表示属性名字在dtb文件中的的偏移地址,紧随其后填充属性值value。

d、dts文件属性名信息块在dtb文件中的描述

dts文件在编译时将dts文件中的属性名从off_dt_strings依次写入dtb文件,内核解析时通过off_dt_strings和dts文件属性的值信息块即可准确解析属性名。

2、dtb文件对dts文件中节点的描述

如图所示,通过dtb文件属性的值信息块和dts文件属性名信息块,在dtb文件中根节点以0x00000001开始,以0x00000009结束,子节点以0x00000001开始,以0x00000002结束,每个属性描述以0x00000003开始,描述节点信息。

3、举例说明

随便打开一个编译好的dtb文件,分析如图所示

附上分析的dts和dtb文件

// SPDX-License-Identifier: GPL-2.0
/*
 * SAMSUNG SMDK2440 board device tree source
 *
 * Copyright (c) 2018 [email protected]
 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
 */
 
#define S3C2410_GPA(_nr)	((0<<16) + (_nr))
#define S3C2410_GPB(_nr)	((1<<16) + (_nr))
#define S3C2410_GPC(_nr)	((2<<16) + (_nr))
#define S3C2410_GPD(_nr)	((3<<16) + (_nr))
#define S3C2410_GPE(_nr)	((4<<16) + (_nr))
#define S3C2410_GPF(_nr)	((5<<16) + (_nr))
#define S3C2410_GPG(_nr)	((6<<16) + (_nr))
#define S3C2410_GPH(_nr)	((7<<16) + (_nr))
#define S3C2410_GPJ(_nr)	((8<<16) + (_nr))
#define S3C2410_GPK(_nr)	((9<<16) + (_nr))
#define S3C2410_GPL(_nr)	((10<<16) + (_nr))
#define S3C2410_GPM(_nr)	((11<<16) + (_nr))

/dts-v1/;

/memreserve/ 0x33f00000 0x100000;

/ {
	model 				= "SMDK24440";
	compatible 			= "samsung,smdk2440";
	#address-cells      = <1>;
	#size-cells         = <1>;
		
	memory {
			device_type	= "memory";
			reg    =  <0x30000000 0x4000000 0 4096>;		
	};
	chosen {
			bootargs 	= "noinitrd root=/dev/mtdblock4 \
			rw init=/linuxrc console=ttySAC0,115200";
	};
	led {
			compatible 	= "jz2440_led";
			pin 		= <S3C2410_GPF(5)>;
	};
};
00000000  d0 0d fe ed 00 00 01 d1  00 00 00 48 00 00 01 88  |...........H....|
00000010  00 00 00 28 00 00 00 11  00 00 00 10 00 00 00 00  |...(............|
00000020  00 00 00 49 00 00 01 40  00 00 00 00 33 f0 00 00  |[email protected]...|
00000030  00 00 00 00 00 10 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000050  00 00 00 03 00 00 00 0a  00 00 00 00 53 4d 44 4b  |............SMDK|
00000060  32 34 34 34 30 00 00 00  00 00 00 03 00 00 00 11  |24440...........|
00000070  00 00 00 06 73 61 6d 73  75 6e 67 2c 73 6d 64 6b  |....samsung,smdk|
00000080  32 34 34 30 00 00 00 00  00 00 00 03 00 00 00 04  |2440............|
00000090  00 00 00 11 00 00 00 01  00 00 00 03 00 00 00 04  |................|
000000a0  00 00 00 20 00 00 00 01  00 00 00 01 6d 65 6d 6f  |... ........memo|
000000b0  72 79 00 00 00 00 00 03  00 00 00 07 00 00 00 2c  |ry.............,|
000000c0  6d 65 6d 6f 72 79 00 00  00 00 00 03 00 00 00 10  |memory..........|
000000d0  00 00 00 38 30 00 00 00  04 00 00 00 00 00 00 00  |...80...........|
000000e0  00 00 10 00 00 00 00 02  00 00 00 01 63 68 6f 73  |............chos|
000000f0  65 6e 00 00 00 00 00 03  00 00 00 47 00 00 00 3c  |en.........G...<|
00000100  6e 6f 69 6e 69 74 72 64  20 72 6f 6f 74 3d 2f 64  |noinitrd root=/d|
00000110  65 76 2f 6d 74 64 62 6c  6f 63 6b 34 20 09 09 72  |ev/mtdblock4 ..r|
00000120  77 20 69 6e 69 74 3d 2f  6c 69 6e 75 78 72 63 20  |w init=/linuxrc |
00000130  63 6f 6e 73 6f 6c 65 3d  74 74 79 53 41 43 30 2c  |console=ttySAC0,|
00000140  31 31 35 32 30 30 00 00  00 00 00 02 00 00 00 01  |115200..........|
00000150  6c 65 64 00 00 00 00 03  00 00 00 0b 00 00 00 06  |led.............|
00000160  6a 7a 32 34 34 30 5f 6c  65 64 00 00 00 00 00 03  |jz2440_led......|
00000170  00 00 00 04 00 00 00 45  00 05 00 05 00 00 00 02  |.......E........|
00000180  00 00 00 02 00 00 00 09  6d 6f 64 65 6c 00 63 6f  |........model.co|
00000190  6d 70 61 74 69 62 6c 65  00 23 61 64 64 72 65 73  |mpatible.#addres|
000001a0  73 2d 63 65 6c 6c 73 00  23 73 69 7a 65 2d 63 65  |s-cells.#size-ce|
000001b0  6c 6c 73 00 64 65 76 69  63 65 5f 74 79 70 65 00  |lls.device_type.|
000001c0  72 65 67 00 62 6f 6f 74  61 72 67 73 00 70 69 6e  |reg.bootargs.pin|
000001d0  00                                                |.|
000001d1

猜你喜欢

转载自blog.csdn.net/ggxyx123/article/details/85273896