设备树基础分析

落日映苍穹つ 2023-02-08 15:54 106阅读 0赞

参考: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、时钟、中断、内存等,形成类似文本文件,很好的解决了这些问题。

format_png

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文件的基本组成单元为:

  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. memory@50000000 {
  9. device_type = "memory";
  10. reg = <0x50000000 0x10000000>;
  11. };
  12. }

[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、对于节点的属性描述,具有几种不同类型,如图所示:

format_png 1

举例说明:

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文件结构

format_png 2

从结构图中可以看出,dtb文件主要分为四块:dtb头部信息块、内存保留信息块、dts文件属性的值信息块、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文件中节点的描述

format_png 3

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

3、举例说明

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

format_png 4

附上分析的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. #define S3C2410_GPA(_nr) ((0<<16) + (_nr))
  9. #define S3C2410_GPB(_nr) ((1<<16) + (_nr))
  10. #define S3C2410_GPC(_nr) ((2<<16) + (_nr))
  11. #define S3C2410_GPD(_nr) ((3<<16) + (_nr))
  12. #define S3C2410_GPE(_nr) ((4<<16) + (_nr))
  13. #define S3C2410_GPF(_nr) ((5<<16) + (_nr))
  14. #define S3C2410_GPG(_nr) ((6<<16) + (_nr))
  15. #define S3C2410_GPH(_nr) ((7<<16) + (_nr))
  16. #define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
  17. #define S3C2410_GPK(_nr) ((9<<16) + (_nr))
  18. #define S3C2410_GPL(_nr) ((10<<16) + (_nr))
  19. #define S3C2410_GPM(_nr) ((11<<16) + (_nr))
  20. /dts-v1/;
  21. /memreserve/ 0x33f00000 0x100000;
  22. / {
  23. model = "SMDK24440";
  24. compatible = "samsung,smdk2440";
  25. #address-cells = <1>;
  26. #size-cells = <1>;
  27. memory {
  28. device_type = "memory";
  29. reg = <0x30000000 0x4000000 0 4096>;
  30. };
  31. chosen {
  32. bootargs = "noinitrd root=/dev/mtdblock4 \
  33. rw init=/linuxrc console=ttySAC0,115200";
  34. };
  35. led {
  36. compatible = "jz2440_led";
  37. pin = <S3C2410_GPF(5)>;
  38. };
  39. };
  40. 00000000 d0 0d fe ed 00 00 01 d1 00 00 00 48 00 00 01 88 |...........H....|
  41. 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............|
  42. 00000020 00 00 00 49 00 00 01 40 00 00 00 00 33 f0 00 00 |...I...@....3...|
  43. 00000030 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 |................|
  44. 00000040 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|
  45. 00000050 00 00 00 03 00 00 00 0a 00 00 00 00 53 4d 44 4b |............SMDK|
  46. 00000060 32 34 34 34 30 00 00 00 00 00 00 03 00 00 00 11 |24440...........|
  47. 00000070 00 00 00 06 73 61 6d 73 75 6e 67 2c 73 6d 64 6b |....samsung,smdk|
  48. 00000080 32 34 34 30 00 00 00 00 00 00 00 03 00 00 00 04 |2440............|
  49. 00000090 00 00 00 11 00 00 00 01 00 00 00 03 00 00 00 04 |................|
  50. 000000a0 00 00 00 20 00 00 00 01 00 00 00 01 6d 65 6d 6f |... ........memo|
  51. 000000b0 72 79 00 00 00 00 00 03 00 00 00 07 00 00 00 2c |ry.............,|
  52. 000000c0 6d 65 6d 6f 72 79 00 00 00 00 00 03 00 00 00 10 |memory..........|
  53. 000000d0 00 00 00 38 30 00 00 00 04 00 00 00 00 00 00 00 |...80...........|
  54. 000000e0 00 00 10 00 00 00 00 02 00 00 00 01 63 68 6f 73 |............chos|
  55. 000000f0 65 6e 00 00 00 00 00 03 00 00 00 47 00 00 00 3c |en.........G...<|
  56. 00000100 6e 6f 69 6e 69 74 72 64 20 72 6f 6f 74 3d 2f 64 |noinitrd root=/d|
  57. 00000110 65 76 2f 6d 74 64 62 6c 6f 63 6b 34 20 09 09 72 |ev/mtdblock4 ..r|
  58. 00000120 77 20 69 6e 69 74 3d 2f 6c 69 6e 75 78 72 63 20 |w init=/linuxrc |
  59. 00000130 63 6f 6e 73 6f 6c 65 3d 74 74 79 53 41 43 30 2c |console=ttySAC0,|
  60. 00000140 31 31 35 32 30 30 00 00 00 00 00 02 00 00 00 01 |115200..........|
  61. 00000150 6c 65 64 00 00 00 00 03 00 00 00 0b 00 00 00 06 |led.............|
  62. 00000160 6a 7a 32 34 34 30 5f 6c 65 64 00 00 00 00 00 03 |jz2440_led......|
  63. 00000170 00 00 00 04 00 00 00 45 00 05 00 05 00 00 00 02 |.......E........|
  64. 00000180 00 00 00 02 00 00 00 09 6d 6f 64 65 6c 00 63 6f |........model.co|
  65. 00000190 6d 70 61 74 69 62 6c 65 00 23 61 64 64 72 65 73 |mpatible.#addres|
  66. 000001a0 73 2d 63 65 6c 6c 73 00 23 73 69 7a 65 2d 63 65 |s-cells.#size-ce|
  67. 000001b0 6c 6c 73 00 64 65 76 69 63 65 5f 74 79 70 65 00 |lls.device_type.|
  68. 000001c0 72 65 67 00 62 6f 6f 74 61 72 67 73 00 70 69 6e |reg.bootargs.pin|
  69. 000001d0 00 |.|
  70. 000001d1

发表评论

表情:
评论列表 (有 0 条评论,106人围观)

还没有评论,来说两句吧...

相关阅读

    相关 设备dts详解

    原文网址:[设备树dts详解\_IT利刃出鞘的博客-CSDN博客][dts_IT_-CSDN] DTS文件结构 1.dts文件布局 ![设备树布局][watermar

    相关 三、设备

    一、设备树。 一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、 SPI 设备等), .dtsi 描述 SOC 级信息(也就是 SOC 有几个 CPU、主频是

    相关 设备

    本文将介绍如何为一个新机器编写设备树。我们准备提供一个有关设备树概念的概述和如何使用这些设备树来描述一个机器。完整的设备树数据格式的技术说明书请参考ePAPR规范。ePA

    相关 Linux设备详解

    ARM Linux社区为什么要引入设备树 Linux之父Linus Torvalds闲来无事,在翻看ARM Linux代码的时候,有一天终于忍不住了。他在2011年3月17日

    相关 Linux 设备介绍

    > 设备树的历史 > > 1、kernel最早加入设备树的历史得追溯到v2.6.23,从这个版本开始,在driver目录下多了一个of目录。当然,此时只是引入一些新想法而已。