linux设备树基础分析

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

一、什么是设备树  device tree source

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、编译设备树 device tree source 

设备树文件的格式为dts,包含的头文件格式为dtsi (共用部分DTS),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文件了。 DTC device tree compiler
 

å¾1 dtsådtbæ件转æ¢

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

二、设备树dts格式

1、dts格式

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

 
  1. /dts-v1/; //表示dts文件的版本号

  2. [memory reservations] //表示内存保留,即CPU是否需要在flash上保留一段内存用作他用,可以有也可以没有

  3. / { //表示根节点

  4. [property definitions] //就是属性定义,对当前节点描述,将硬件信息提供给内核处理

  5. [child nodes] //子节点 

  6. };

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

 
  1. /dts-v1/; 

  2. #include <dt-bindings/gpio/gpio.h>

  3. #include <dt-bindings/interrupt-controller/irq.h>

  4. #include "s3c6410.dtsi"

  5. / {

  6.     model = "FriendlyARM Mini6410 board based on S3C6410";

  7.     compatible = "friendlyarm,mini6410", "samsung,s3c6410";

  8.  
  9.     memory@50000000 {

  10.         device_type = "memory";

  11.         reg = <0x50000000 0x10000000>;

  12.     };

  13. }

[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方式引用

 
  1. pic@10000000 {

  2. phandle = <1>;

  3. interrupt-controller;

  4. };

  5. another-device-node {

  6. interrupt-parent = <1>; // 使用phandle值为1来引用上述节点

  7. };

2)label 方式引用

 
  1. PIC: pic@10000000 {

  2. interrupt-controller;

  3. };

  4. another-device-node {

  5. interrupt-parent = <&PIC>; // 使用label来引用上述节点,

  6. // 使用lable时实际上也是使用phandle来引用,

  7. // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性

  8. };

3)直接引用

 
  1. vic0: interrupt-controller@71200000 {

  2. compatible = "arm,pl192-vic";

  3. interrupt-controller;

  4. reg = <0x71200000 0x1000>;

  5. #interrupt-cells = <1>;

  6. };

  7. &vic0 {//直接引用vic0 节点,添加属性

  8. valid-mask = <0xffffff7f>;

  9. valid-wakeup-mask = <0x00200004>;

  10. };

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文件主要分为四块:

1.dtb头部信息块、

2.内存保留信息块、

3.dts文件属性的值信息块、

4.dts文件属性名信息块

 

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

 
  1. struct fdt_header {

  2. uint32_t magic; //0xd00dfeed

  3. uint32_t totalsize; //dtb文件的大小

  4. uint32_t off_dt_struct; //dts文件属性的值信息块的偏移地址

  5. uint32_t off_dt_strings; //dts文件属性名信息块的偏移地址

  6. uint32_t off_mem_rsvmap; //dts文件内存保留信息块的偏移地址

  7. uint32_t version;

  8. uint32_t last_comp_version;

  9. uint32_t boot_cpuid_phys;

  10. uint32_t size_dt_strings;

  11. uint32_t size_dt_struct;

  12. };

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

 
  1. struct fdt_reserve_entry {

  2. uint64_t address; //保留内存的起始地址

  3. uint64_t size; //保留地址的大小

  4. };

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

 
  1. struct {

  2. uint32_t len; //属性值得长度

  3. uint32_t nameoff; //属性名字在dtb文件中的偏移地址:off_dt_struct+nameoff 即为属性名字的地址

  4. }

节点的一个属性描述以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文件

 
  1. // SPDX-License-Identifier: GPL-2.0

  2. /*

  3. * SAMSUNG SMDK2440 board device tree source

  4. *

  5. * Copyright (c) 2018 weidongshan@qq.com

  6. * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb

  7. */

  8.  
  9. #define S3C2410_GPA(_nr) ((0<<16) + (_nr))

  10. #define S3C2410_GPB(_nr) ((1<<16) + (_nr))

  11. #define S3C2410_GPC(_nr) ((2<<16) + (_nr))

  12. #define S3C2410_GPD(_nr) ((3<<16) + (_nr))

  13. #define S3C2410_GPE(_nr) ((4<<16) + (_nr))

  14. #define S3C2410_GPF(_nr) ((5<<16) + (_nr))

  15. #define S3C2410_GPG(_nr) ((6<<16) + (_nr))

  16. #define S3C2410_GPH(_nr) ((7<<16) + (_nr))

  17. #define S3C2410_GPJ(_nr) ((8<<16) + (_nr))

  18. #define S3C2410_GPK(_nr) ((9<<16) + (_nr))

  19. #define S3C2410_GPL(_nr) ((10<<16) + (_nr))

  20. #define S3C2410_GPM(_nr) ((11<<16) + (_nr))

  21.  
  22. /dts-v1/;

  23.  
  24. /memreserve/ 0x33f00000 0x100000;

  25.  
  26. / {

  27. model = "SMDK24440";

  28. compatible = "samsung,smdk2440";

  29. #address-cells = <1>;

  30. #size-cells = <1>;

  31.  
  32. memory {

  33. device_type = "memory";

  34. reg = <0x30000000 0x4000000 0 4096>;

  35. };

  36. chosen {

  37. bootargs = "noinitrd root=/dev/mtdblock4 \

  38. rw init=/linuxrc console=ttySAC0,115200";

  39. };

  40. led {

  41. compatible = "jz2440_led";

  42. pin = <S3C2410_GPF(5)>;

  43. };

  44. };

 
  1. 00000000 d0 0d fe ed 00 00 01 d1 00 00 00 48 00 00 01 88 |...........H....|

  2. 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............|

  3. 00000020 00 00 00 49 00 00 01 40 00 00 00 00 33 f0 00 00 |...I...@....3...|

  4. 00000030 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 |................|

  5. 00000040 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|

  6. 00000050 00 00 00 03 00 00 00 0a 00 00 00 00 53 4d 44 4b |............SMDK|

  7. 00000060 32 34 34 34 30 00 00 00 00 00 00 03 00 00 00 11 |24440...........|

  8. 00000070 00 00 00 06 73 61 6d 73 75 6e 67 2c 73 6d 64 6b |....samsung,smdk|

  9. 00000080 32 34 34 30 00 00 00 00 00 00 00 03 00 00 00 04 |2440............|

  10. 00000090 00 00 00 11 00 00 00 01 00 00 00 03 00 00 00 04 |................|

  11. 000000a0 00 00 00 20 00 00 00 01 00 00 00 01 6d 65 6d 6f |... ........memo|

  12. 000000b0 72 79 00 00 00 00 00 03 00 00 00 07 00 00 00 2c |ry.............,|

  13. 000000c0 6d 65 6d 6f 72 79 00 00 00 00 00 03 00 00 00 10 |memory..........|

  14. 000000d0 00 00 00 38 30 00 00 00 04 00 00 00 00 00 00 00 |...80...........|

  15. 000000e0 00 00 10 00 00 00 00 02 00 00 00 01 63 68 6f 73 |............chos|

  16. 000000f0 65 6e 00 00 00 00 00 03 00 00 00 47 00 00 00 3c |en.........G...<|

  17. 00000100 6e 6f 69 6e 69 74 72 64 20 72 6f 6f 74 3d 2f 64 |noinitrd root=/d|

  18. 00000110 65 76 2f 6d 74 64 62 6c 6f 63 6b 34 20 09 09 72 |ev/mtdblock4 ..r|

  19. 00000120 77 20 69 6e 69 74 3d 2f 6c 69 6e 75 78 72 63 20 |w init=/linuxrc |

  20. 00000130 63 6f 6e 73 6f 6c 65 3d 74 74 79 53 41 43 30 2c |console=ttySAC0,|

  21. 00000140 31 31 35 32 30 30 00 00 00 00 00 02 00 00 00 01 |115200..........|

  22. 00000150 6c 65 64 00 00 00 00 03 00 00 00 0b 00 00 00 06 |led.............|

  23. 00000160 6a 7a 32 34 34 30 5f 6c 65 64 00 00 00 00 00 03 |jz2440_led......|

  24. 00000170 00 00 00 04 00 00 00 45 00 05 00 05 00 00 00 02 |.......E........|

  25. 00000180 00 00 00 02 00 00 00 09 6d 6f 64 65 6c 00 63 6f |........model.co|

  26. 00000190 6d 70 61 74 69 62 6c 65 00 23 61 64 64 72 65 73 |mpatible.#addres|

  27. 000001a0 73 2d 63 65 6c 6c 73 00 23 73 69 7a 65 2d 63 65 |s-cells.#size-ce|

  28. 000001b0 6c 6c 73 00 64 65 76 69 63 65 5f 74 79 70 65 00 |lls.device_type.|

  29. 000001c0 72 65 67 00 62 6f 6f 74 61 72 67 73 00 70 69 6e |reg.bootargs.pin|

  30. 000001d0 00 |.|

  31. 000001d1

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值