Linux 设备树

Posted 行稳方能走远

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux 设备树相关的知识,希望对你有一定的参考价值。

前面章节中我们多次提到“设备树”这个概念,因为时机未到,所以当时并没有详细的讲解什么是“设备树”,本章我们就来详细的谈一谈设备树。掌握设备树是Linux 驱动开发人员必备的技能!因为在新版本的Linux 中,ARM 相关的驱动全部采用了设备树(也有支持老式驱动的,比较少),最新出的CPU 其驱动开发也基本都是基于设备树的,比如ST 新出的STM32MP157、
NXP 的I.MX8 系列等。我们所使用的Linux 版本为4.1.15,其支持设备树,所以正点原子I.MX6U-ALPHA 开发板的所有Linux 驱动都是基于设备树的。本章我们就来了解一下设备树的起源、重点学习一下设备树语法。

什么是设备树

设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做DTS(Device Tree Source),这个DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等,如图43.1.1所示:

在图43.1.1 中,树的主干就是系统总线,IIC 控制器、GPIO 控制器、SPI 控制器等都是接到系统主线上的分支。IIC 控制器有分为IIC1 和IIC2 两种,其中IIC1 上接了FT5206 和AT24C02这两个IIC 设备,IIC2 上只接了MPU6050 这个设备。DTS 文件的主要功能就是按照图43.1.1所示的结构来描述板子上的设备信息,DTS 文件描述设备信息是有相应的语法规则要求的,稍
后我们会详细的讲解DTS 语法规则。
在3.x 版本(具体哪个版本笔者也无从考证)以前的Linux 内核中ARM 架构并没有采用设备树。在没有设备树的时候Linux 是如何描述ARM 架构中的板级信息呢?在Linux 内核源码中大量的arch/arm/mach-xxx 和arch/arm/plat-xxx 文件夹,这些文件夹里面的文件就是对应平台下的板级信息。比如在arch/arm/mach-smdk2440.c 中有如下内容(有缩减):

90 static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
91
92 .lcdcon5 = S3C2410_LCDCON5_FRM565 |
93 S3C2410_LCDCON5_INVVLINE |
94 S3C2410_LCDCON5_INVVFRAME |
95 S3C2410_LCDCON5_PWREN |
96 S3C2410_LCDCON5_HWSWP,
......
113 };
114
115 static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
116 .displays = &smdk2440_lcd_cfg,
117 .num_displays = 1,
118 .default_display = 0,
......
133 };
134
135 static struct platform_device *smdk2440_devices[] __initdata = {
136 &s3c_device_ohci,
137 &s3c_device_lcd,
138 &s3c_device_wdt,
139 &s3c_device_i2c0,
140 &s3c_device_iis,
141 };

上述代码中的结构体变量smdk2440_fb_info 就是描述SMDK2440 这个开发板上的LCD 信息的,结构体指针数组smdk2440_devices 描述的SMDK2440 这个开发板上的所有平台相关信息。这个仅仅是使用2440 这个芯片的SMDK2440 开发板下的LCD 信息,SMDK2440 开发板还有很多的其他外设硬件和平台硬件信息。使用2440 这个芯片的板子有很多,每个板子都有描
述相应板级信息的文件,这仅仅只是一个2440。随着智能手机的发展,每年新出的ARM 架构芯片少说都在数十、数百款,Linux 内核下板级信息文件将会成指数级增长!这些板级信息文件都是.c 或.h 文件,都会被硬编码进Linux 内核中,导致Linux 内核“虚胖”。就好比你喜欢吃自助餐,然后花了100 多到一家宣传看着很不错的自助餐厅,结果你想吃的牛排、海鲜、烤肉基本没多少,全都是一些凉菜、炒面、西瓜、饮料等小吃,相信你此时肯定会脱口而出一句“Fk!”、“骗子!”。同样的,当Linux 之父linus 看到ARM 社区向Linux 内核添加了大量“无用”、冗余的板级信息文件,不禁的发出了一句“This whole ARM thing is a fcking pain in the ass”。从此以后ARM 社区就引入了PowerPC 等架构已经采用的设备树(Flattened Device Tree),将这些描述
板级硬件信息的内容都从Linux 内中分离开来,用一个专属的文件格式来描述,这个专属的文件就叫做设备树,文件扩展名为.dts。一个SOC 可以作出很多不同的板子,这些不同的板子肯定是有共同的信息,将这些共同的信息提取出来作为一个通用的文件,其他的.dts 文件直接引用这个通用文件即可,这个通用文件就是.dtsi 文件,类似于C 语言中的头文件。一般.dts 描述
板级信息(也就是开发板上有哪些IIC 设备、SPI 设备等),.dtsi 描述SOC 级信息(也就是SOC 有几个CPU、主频是多少、各个外设控制器信息等)。

这个就是设备树的由来,简而言之就是,Linux 内核中ARM 架构下有太多的冗余的垃圾板级信息文件,导致linus 震怒,然后ARM 社区引入了设备树。

DTS、DTB 和DTC

上一小节说了,设备树源文件扩展名为.dts,但是我们在前面移植Linux 的时候却一直在使用.dtb 文件,那么DTS 和DTB 这两个文件是什么关系呢?DTS 是设备树源码文件,DTB 是将DTS 编译以后得到的二进制文件。将.c 文件编译为.o 需要用到gcc 编译器,那么将.dts 编译为.dtb需要什么工具呢?需要用到DTC 工具!DTC 工具源码在Linux 内核的scripts/dtc 目录下,
scripts/dtc/Makefile 文件内容如下:

1 hostprogs-y := dtc
2 always := $(hostprogs-y)
3
4 dtc-objs:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \\
5 srcpos.o checks.o util.o
6 dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o
......

可以看出,DTC 工具依赖于dtc.c、flattree.c、fstree.c 等文件,最终编译并链接出DTC 这个主机文件。如果要编译DTS 文件的话只需要进入到Linux 源码根目录下,然后执行如下命令:

make all

或者:

make dtbs

“make all”命令是编译Linux 源码中的所有东西,包括zImage,.ko 驱动模块以及设备树,如果只是编译设备树的话建议使用“make dtbs”命令。
基于ARM 架构的SOC 有很多种,一种SOC 又可以制作出很多款板子,每个板子都有一个对应的DTS 文件,那么如何确定编译哪一个DTS 文件呢?我们就以I.MX6ULL 这款芯片对应的板子为例来看一下,打开arch/arm/boot/dts/Makefile,有如下内容:

381 dtb-$(CONFIG_SOC_IMX6UL) += \\
382 imx6ul-14x14-ddr3-arm2.dtb \\
383 imx6ul-14x14-ddr3-arm2-emmc.dtb \\
......
400 dtb-$(CONFIG_SOC_IMX6ULL) += \\
401 imx6ull-14x14-ddr3-arm2.dtb \\
402 imx6ull-14x14-ddr3-arm2-adc.dtb \\
403 imx6ull-14x14-ddr3-arm2-cs42888.dtb \\
404 imx6ull-14x14-ddr3-arm2-ecspi.dtb \\
405 imx6ull-14x14-ddr3-arm2-emmc.dtb \\
406 imx6ull-14x14-ddr3-arm2-epdc.dtb \\
407 imx6ull-14x14-ddr3-arm2-flexcan2.dtb \\
408 imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \\
409 imx6ull-14x14-ddr3-arm2-lcdif.dtb \\
410 imx6ull-14x14-ddr3-arm2-ldo.dtb \\
411 imx6ull-14x14-ddr3-arm2-qspi.dtb \\
412 imx6ull-14x14-ddr3-arm2-qspi-all.dtb \\
413 imx6ull-14x14-ddr3-arm2-tsc.dtb \\
414 imx6ull-14x14-ddr3-arm2-uart2.dtb \\
415 imx6ull-14x14-ddr3-arm2-usb.dtb \\
416 imx6ull-14x14-ddr3-arm2-wm8958.dtb \\
417 imx6ull-14x14-evk.dtb \\
418 imx6ull-14x14-evk-btwifi.dtb \\
419 imx6ull-14x14-evk-emmc.dtb \\
420 imx6ull-14x14-evk-gpmi-weim.dtb \\
421 imx6ull-14x14-evk-usb-certi.dtb \\
422 imx6ull-alientek-emmc.dtb \\
423 imx6ull-alientek-nand.dtb \\
424 imx6ull-9x9-evk.dtb \\
425 imx6ull-9x9-evk-btwifi.dtb \\
426 imx6ull-9x9-evk-ldo.dtb
427 dtb-$(CONFIG_SOC_IMX6SLL) += \\
428 imx6sll-lpddr2-arm2.dtb \\
429 imx6sll-lpddr3-arm2.dtb \\
......

可以看出,当选中I.MX6ULL 这个SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到I.MX6ULL 这个SOC 的板子对应的.dts 文件都会被编译为.dtb。如果我们使用I.MX6ULL 新做了一个板子,只需要新建一个此板子对应的.dts 文件,然后将对应的.dtb 文件名添加到dtb-$(CONFIG_SOC_IMX6ULL)下,这样在编译设备树的时候就会将对应的.dts 编译为二进制的.dtb文件。

示例代码43.2.2 中第422 和423 行就是我们在给正点原子的I.MX6U-ALPHA 开发板移植Linux 系统的时候添加的设备树。关于.dtb 文件怎么使用这里就不多说了,前面讲解Uboot 移植、Linux 内核移植的时候已经无数次的提到如何使用.dtb 文件了(uboot 中使用bootz 或bootm命令向Linux 内核传递二进制设备树文件(.dtb))。

DTS 语法

虽然我们基本上不会从头到尾重写一个.dts 文件,大多时候是直接在SOC 厂商提供的.dts文件上进行修改。但是DTS 文件语法我们还是需要详细的学习一遍,因为我们肯定需要修改.dts文件。大家不要看到要学习新的语法就觉得会很复杂,DTS 语法非常的人性化,是一种ASCII文本文件,不管是阅读还是修改都很方便。
本节我们就以imx6ull-alientek-emmc.dts 这个文件为例来讲解一下DTS 语法。关于设备树详细的语法规则请参考《Devicetree SpecificationV0.2.pdf 》和《Power_ePAPR_APPROVED_v1.12.pdf》这两份文档,此两份文档已经放到了开发板光盘中,路径为:4 、参考资料->Devicetree SpecificationV0.2.pdf 、4 、参考资料-> Power_ePAPR_APPROVED_v1.12.pdf

.dtsi 头文件

和C 语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi。在imx6ull-alientek-emmc.dts 中有如下所示内容:

12 #include <dt-bindings/input/input.h>
13 #include "imx6ull.dtsi"

第12 行,使用“#include”来引用“input.h”这个.h 头文件。
第13 行,使用“#include”来引用“imx6ull.dtsi”这个.dtsi 头文件。

看到这里,大家可能会疑惑,不是说设备树的扩展名是.dtsi 吗?为什么也可以直接引用C语言中的.h 头文件呢?这里并没有错,.dts 文件引用C 语言中的.h 文件,甚至也可以引用.dts 文件,打开imx6ull-14x14-evk-gpmi-weim.dts 这个文件,此文件中有如下内容:

9 #include "imx6ull-14x14-evk.dts"

可以看出,示例代码43.3.1.2 中直接引用了.dts 文件,因此在.dts 设备树文件中,可以通过“#include”来引用.h、.dtsi 和.dts 文件。只是,我们在编写设备树头文件的时候最好选择.dtsi 后缀。

一般.dtsi 文件用于描述SOC 的内部外设信息,比如CPU 架构、主频、外设寄存器地址范围,比如UART、IIC 等等。比如imx6ull.dtsi 就是描述I.MX6ULL 这颗SOC 内部外设情况信息的,内容如下:

10 #include <dt-bindings/clock/imx6ul-clock.h>
11 #include <dt-bindings/gpio/gpio.h>
12 #include <dt-bindings/interrupt-controller/arm-gic.h>
13 #include "imx6ull-pinfunc.h"
14 #include "imx6ull-pinfunc-snvs.h"
15 #include "skeleton.dtsi"
16
17 / {
18 aliases {
19 can0 = &flexcan1;
......
48 };
49
50 cpus {
51 #address-cells = <1>;
52 #size-cells = <0>;
53
54 cpu0: cpu@0 {
55 compatible = "arm,cortex-a7";
56 device_type = "cpu";
......
89 };
90 };
91
92 intc: interrupt-controller@00a01000 {
93 compatible = "arm,cortex-a7-gic";
94 #interrupt-cells = <3>;
95 interrupt-controller;
96 reg = <0x00a01000 0x1000>,
97 <0x00a02000 0x100>;
98 };
99
100 clocks {
101 #address-cells = <1>;
102 #size-cells = <0>;
103
104 ckil: clock@0 {
105 compatible = "fixed-clock";
106 reg = <0>;
107 #clock-cells = <0>;
108 clock-frequency = <32768>;
109 clock-output-names = "ckil";
110 };
......
135 };
136
137 soc {
138 #address-cells = <1>;
139 #size-cells = <1>;
140 compatible = "simple-bus";
141 interrupt-parent = <&gpc>;
142 ranges;
143
144 busfreq {
145 compatible = "fsl,imx_busfreq";
......
162 };
197
198 gpmi: gpmi-nand@01806000{
199 compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
200 #address-cells = <1>;
201 #size-cells = <1>;
202 reg = <0x01806000 0x2000>, <0x01808000 0x4000>;
......
216 };
......
1177 };
1178 };

示例代码43.3.1.3 中第54~89 行就是cpu0 这个设备节点信息,这个节点信息描述了I.MX6ULL 这颗SOC 所使用的CPU 信息,比如架构是cortex-A7,频率支持996MHz、792MHz、528MHz、396MHz 和198MHz 等等。在imx6ull.dtsi 文件中不仅仅描述了cpu0 这一个节点信息,
I.MX6ULL 这颗SOC 所有的外设都描述的清清楚楚,比如ecspi1~ 4、uart1~ 8、usbphy1~ 2、i2c1~4等等,关于这些设备节点信息的具体内容我们稍后在详细的讲解。

设备节点

设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。以下是从imx6ull.dtsi 文件中缩减出来的设备树文件内容:

1 / {
2 aliases {
3 can0 = &flexcan1;
4 };
5
6 cpus {
7 #address-cells = <1>;
8 #size-cells = <0>;
9
10 cpu0: cpu@0 {
11 compatible = "arm,cortex-a7";
12 device_type = "cpu";
13 reg = <0>;
14 };
15 };
16
17 intc: interrupt-controller@00a01000 {
18 compatible = "arm,cortex-a7-gic";
19 #interrupt-cells = <3>;
20 interrupt-controller;
21 reg = <0x00a01000 0x1000>,
22 <0x00a02000 0x100>;
23 };
24 }

第1 行,“/”是根节点,每个设备树文件只有一个根节点。细心的同学应该会发现,imx6ull.dtsi和imx6ull-alientek-emmc.dts 这两个文件都有一个“/”根节点,这样不会出错吗?不会的,因为这两个“/”根节点的内容会合并成一个根节点。
第2、6 和17 行,aliases、cpus 和intc 是三个子节点,在设备树中节点命名格式如下:

node-name@unit-address

其中“node-name”是节点名字,为ASCII 字符串,节点名字应该能够清晰的描述出节点的功能,比如“uart1”就表示这个节点是UART1 外设。“unit-address”一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话“unit-address”可以不要,比如“cpu@0”、“interrupt-controller@00a01000”。
但是我们在示例代码43.3.2.1 中我们看到的节点命名却如下所示:

cpu0:cpu@0

上述命令并不是“node-name@unit-address”这样的格式,而是用“:”隔开成了两部分,“:”前面的是节点标签(label),“:”后面的才是节点名字,格式如下所示:

label: node-name@unit-address

引入label 的目的就是为了方便访问节点,可以直接通过&label 来访问这个节点,比如通过&cpu0 就可以访问“cpu@0”这个节点,而不需要输入完整的节点名字。再比如节点“intc: interrupt-controller@00a01000”,节点label 是intc,而节点名字就很长了,为“interrupt-controller@00a01000”。很明显通过&intc 来访问“interrupt-controller@00a01000”这个节点要方便很多!
第10 行,cpu0 也是一个节点,只是cpu0 是cpus 的子节点。
每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式如下所示:
①、字符串

compatible = "arm,cortex-a7";

上述代码设置compatible 属性的值为字符串“arm,cortex-a7”。
②、32位无符号整数

reg = <0>;

上述代码设置reg 属性的值为0,reg 的值也可以设置为一组值,比如:

reg = <0 0x123456 100>;

③、字符串列表
属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:

compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

上述代码设置属性compatible 的值为“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。

标准属性

节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性,Linux 下的很多外设驱动都会使用
这些标准属性,本节我们就来学习一下几个常用的标准属性。

1、compatible 属性
compatible 属性也叫做“兼容性”属性,这是非常重要的一个属性!compatible 属性的值是一个字符串列表,compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序,compatible 属性的值格式如下所示:

"manufacturer,model"

其中manufacturer 表示厂商,model 一般是模块对应的驱动名字。比如imx6ull-alientek-emmc.dts 中sound 节点是I.MX6U-ALPHA 开发板的音频设备节点,I.MX6U-ALPHA 开发板上的音频芯片采用的欧胜(WOLFSON)出品的WM8960,sound 节点的compatible 属性值如下:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";

属性值有两个,分别为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字。sound这个设备首先使用第一个兼容值在Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。
一般驱动程序文件都会有一个OF 匹配表,此OF 匹配表保存着一些compatible 值,如果设备节点的compatible 属性值和OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。比如在文件imx-wm8960.c 中有如下内容:

632 static const struct of_device_id imx_wm8960_dt_ids[] = {
633 { .compatible = "fsl,imx-audio-wm8960", },
634 { /* sentinel */ }
635 };
636 MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);
637
638 static struct platform_driver imx_wm8960_driver = {
639 .driver = {
640 .name = "imx-wm8960",
641 .pm = &snd_soc_pm_ops,
642 .of_match_table = imx_wm8960_dt_ids,
643 },
644 .probe = imx_wm8960_probe,
645 .remove = imx_wm8960_remove,
646 };

第632~635 行的数组imx_wm8960_dt_ids 就是imx-wm8960.c 这个驱动文件的匹配表,此匹配表只有一个匹配值“fsl,imx-audio-wm8960”。如果在设备树中有哪个节点的compatible 属 性值与此相等,那么这个节点就会使用此驱动文件。

第642 行,wm8960 采用了platform_driver 驱动模式,关于platform_driver 驱动后面会讲解。此行设置.of_match_table 为imx_wm8960_dt_ids,也就是设置这个platform_driver 所使用的OF 匹配表。

2、model 属性
model 属性值也是一个字符串,一般model 属性描述设备模块信息,比如名字什么的,比如:

model = "wm8960-audio";

3、status 属性
status 属性看名字就知道是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息,可选的状态如表43.3.3.1 所示:

描述
“okay”表明设备是可操作的。
“disabled”表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。至于disabled 的具体含义还要看设备的绑定文档。
“fail”表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。
“fail-sss”含义和“fail”相同,后面的sss 部分是检测到的错误内容。

4、#address-cells 和#size-cells 属性
这两个属性的值都是无符号32 位整形,#address-cells 和#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 属性值决定了子节点reg 属性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点reg 属性中长度信息所占的字长(32 位)。#address-cells 和#size-cells 表明了子节点应该如何编写reg 属性值,一般reg 属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:

reg = <address1 length1 address2 length2 address3 length3……>

每个“address length”组合表示一个地址范围,其中address 是起始地址,length 是地址长度,#address-cells 表明address 这个数据所占用的字长,#size-cells 表明length 这个数据所占用的字长,比如:

1 spi4 {
2 compatible = "spi-gpio";
3 #address-cells = <1>;
4 #size-cells = <0>;
5
6 gpio_spi: gpio_spi@0 {
7 compatible = "fairchild,74hc595";
8 reg = <0>;
9 };
10 };
11
12 aips3: aips-bus@02200000 {
13 compatible = "fsl,aips-bus", "simple-bus";
14 #address-cells = <1>;
15 #size-cells = <1>;
16
17 dcp: dcp@02280000 {
18 compatible = "fsl,imx6sl-dcp";
19 reg = <0x02280000 0x4000>;
20 };
21 };

第3,4 行,节点spi4 的#address-cells = <1>,#size-cells = <0>,说明spi4 的子节点reg 属性中起始地址所占用的字长为1,地址长度所占用的字长为0。

第8 行,子节点gpio_spi: gpio_spi@0 的reg 属性值为<0>,因为父节点设置了#address-cells = <1>,#size-cells = <0>,因此addres=0,没有length 的值,相当于设置了起始地址,而没有设置地址长度。

第14,15 行,设置aips3: aips-bus@02200000 节点#address-cells = <1>,#size-cells = <1>,说明aips3: aips-bus@02200000 节点起始地址长度所占用的字长为1,地址长度所占用的字长也为1。

第19 行,子节点dcp: dcp@02280000 的reg 属性值为<0x02280000 0x4000>,因为父节点设置了#address-cells = <1>,#size-cells = <1>,address= 0x02280000,length= 0x4000,相当于设置了起始地址为0x02280000,地址长度为0x40000。

5、reg 属性

reg 属性前面已经提到过了,reg 属性的值一般是(address,length)对。reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息,比如在imx6ull.dtsi 中有如下内容:

323 uart1: serial@02020000 {
324 compatible = "fsl,imx6ul-uart",
325 "fsl,imx6q-uart", "fsl,imx21-uart";
326 reg = <0x02020000 0x4000>;
327 interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
328 clocks = <&clks IMX6UL_CLK_UART1_IPG>,
329 <&clks IMX6UL_CLK_UART1_SERIAL>;
330 clock-names = "ipg", "per";
331 status = "disabled";
332 };

上述代码是节点uart1,uart1 节点描述了I.MX6ULL 的UART1 相关信息,重点是第326 行
的reg 属性。其中uart1 的父节点aips1: aips-bus@02000000 设置了#address-cells = <1>、#size-cells = <1>,因此reg 属性中address=0x02020000,length=0x4000。查阅《I.MX6ULL 参考手册》
可知,I.MX6ULL 的UART1 寄存器首地址为0x02020000,但是UART1 的地址长度(范围)并没
有0x4000 这么多,这里我们重点是获取UART1 寄存器首地址。

6、ranges 属性
ranges 属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵,ranges 是一个地址映射/转换表,ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成:

child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
parent-bus-address:父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长。
length:子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。

如果ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换,对于我们所使用的I.MX6ULL 来说,子地址空间和父地址空间完全相同,

以上是关于Linux 设备树的主要内容,如果未能解决你的问题,请参考以下文章

Linux设备树语法详解

Linux设备树语法详解

Linux设备树语法详解

Linux-设备树设备树

设备树DTS使用

Linux-设备树编译器DTC