本节笔记主要学习了Linux设备树相关知识点,由于内容较多,打算分两天进行总结。今天着重学习Linux设备树,主要包括前三节内容,分别是概念、格式和语法。
本节思维导图内容如下:
一、什么是设备树
设备树可以用一个图来进行表示,如下:
树的主干就是系统总线, IIC 控制器、 GPIO 控制器、 SPI 控制器等都是接
到系统主线上的分支。IIC 控制器有分为 IIC1 和 IIC2 两种,其中 IIC1 上接了 FT5206 和 AT24C02
这两个 IIC 设备, IIC2 上只接了 MPU6050 这个设备。 DTS 文件的主要功能就是按照图 43.1.1
所示的结构来描述板子上的设备信息。
将这些描述板级硬件信息的内容都从 Linux 内中分离开来,用一个专属的文件格式来描述,这个专属的文
件就叫做设备树,文件扩展名为.dts。
这个通用文件就是.dtsi 文件,类似于 C 语言中的头文件。
一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、 SPI 设备等), .dtsi 描述 SOC 级信息(也就是 SOC 有
几个 CPU、主频是多少、各个外设控制器信息等)。
二、DTS、DTB和DTC
DTS 是设备树源码文件, DTB 是将DTS 编译以后得到的二进制文件。
将.c 文件编译为.o 需要用到 gcc 编译器;将.dts 编译为.dtb需要用到 DTC工具。
如果要编译DTS文件,只需要在Linux源码根目录下,输入指令make all或 make dtbs.
当选中 I.MX6ULL 这个 SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到I.MX6ULL 这个 SOC 的板子对应的.dts 文件都会被编译为.dtb。
三、DTS语法
1.dtsi头文件
设备树的头文件扩展名为.dtsi。在 imx6ull-alientekemmc.dts 中有如下所示内容:
12 #include <dt-bindings/input/input.h>
13 #include "imx6ull.dtsi"
在.dts 设备树文件中,可以通过“ #include”来引用.h、 .dtsi 和.dts 文件。我们在编写设备树头文件的时候最好选择.dtsi 后
缀。
一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、 IIC 等等。比如 imx6ull.dtsi 就是描述 I.MX6ULL 这颗 SOC 内部外设情况信息的,内容如下:
2.设备节点
设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。以下是从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 }
在设备树中节点命名格式如下:
node-name@unit-address
“ node-name”是节点名字。
“ unit-address”一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话“ unit-address”可以不要,
另外一种格式如下:
label: node-name@unit-address
可以直接通过&label 来访问这个节点。
每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式有字符串、32位无符号数、字符串列表【字符串和字符串之间采用“ ,”隔开】。
3.标准属性
1)、compatible属性
compatible 属性也叫做“兼容性”属性,字符串列表用于选择设备所要使用的驱动程序, compatible 属性的值格式如下所示:
"manufacturer,model"
其中,manufacturer是厂家,model是模块对应的驱动名字。
设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。
一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。
2)、model属性
model 属性描述设备模块信息。
3)、status属性
status 属性看名字就知道是和设备状态有关的,如下:
值 | 描述 |
“ okay” | 表明设备是可操作的。 |
“disabled” | 表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。至于 disabled 的具 体含义还要看设备的绑定文档。 |
“ fail” | 表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。 |
“ fail-sss” | 含义和“ fail”相同,后面的 sss 部分是检测到的错误内容。 |
4)、#address_cells和#size_cells属性
用于描述子节点的地址信息。一般 reg 属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:
reg = <address1 length1 address2 length2 address3 length3……>
每个“ address length”组合表示一个地址范围,其中 address 是起始地址, length 是地址长度, #address-cells 表明 address 这个数据所占用的字长, #size-cells 表明 length 这个数据所占用的字长。
5)、reg属性
reg 属性的值一般是(address, length)对。 reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。
6)、range属性
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 来说,子地址空间和父地址空间完全相同,因此会在 imx6ull.dtsi中找到大量的值为空的 ranges 属性,如下所示:
137 soc {
138 #address-cells = <1>;
139 #size-cells = <1>;
140 compatible = "simple-bus";
141 interrupt-parent = <&gpc>;
142 ranges;
......
1177 }
不为空的实例代码为:
1 soc {
2 compatible = "simple-bus";
3 #address-cells = <1>;
4 #size-cells = <1>;
5 ranges = <0x0 0xe0000000 0x00100000>;
6
7 serial {
8 device_type = "serial";
9 compatible = "ns16550";
10 reg = <0x4600 0x100>;
11 clock-frequency = <0>;
12 interrupts = <0xA 0x8>;
13 interrupt-parent = <&ipic>;
14 };
15 };
第 5 行,节点 soc 定义的 ranges 属性,值为<0x0 0xe0000000 0x00100000>,此属性值指定了一个 1024KB(0x00100000)的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物
理起始地址为 0xe0000000。
第 10 行, serial 是串口设备节点, reg 属性定义了 serial 设备寄存器的起始地址为0x4600,寄存器长度为 0x100。经过地址转换, serial 设备可以从 0xe0004600 开始进行读写操作,0xe0004600=0x4600+0xe0000000。
7)、name属性
已经弃用。
8)、device_type属性
用于描述设备的 FCode,也已经弃用。
4.根节点compatible属性
imx6ull-alientek-emmc.dts 文件中根节点的 compatible 属性内容如下所示:
14 / {
15 model = "Freescale i.MX6 ULL 14x14 EVK Board";
16 compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
......
148 }
通过根节点的 compatible 属性可以知道我们所使用的设备,一般第一个值描述了所使用的硬件设备名字,比如这里使用的是“ imx6ull-14x14-evk”这个设备,第二个值描述了设备所使用的SOC,比如这里使用的是“ imx6ull”这颗 SOC。 Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备,如果支持的话设备就会启动 Linux 内核。
1)、使用设备树之前设备匹配方法
machine id进行比对匹配。
2)、使用设备树之后设备匹配方法
用下图可以清晰的标识Linux内核通过根节点compatible属性找到对应设备的函数调用过程。
5.向节点追加或修改内容
开发板使用的设备树文件为 imx6ull-alientek-emmc.dts,因此我们需要在imx6ull-alientek-emmc.dts 文件中完成数据追加的内容,方式如下:
1 &i2c1 {
2 /* 要追加或修改的内容 */
3 };
打开 imx6ull-alientek-emmc.dts,找到如下所示内容:
224 &i2c1 {
225 clock-frequency = <100000>;
226 pinctrl-names = "default";
227 pinctrl-0 = <&pinctrl_i2c1>;
228 status = "okay";
229
230 mag3110@0e {
231 compatible = "fsl,mag3110";
232 reg = <0x0e>;
233 position = <2>;
234 };
235
236 fxls8471@1e {
237 compatible = "fsl,fxls8471";
238 reg = <0x1e>;
239 position = <0>;
240 interrupt-parent = <&gpio5>;
241 interrupts = <0 8>;
242 };
向节点追加或修改内容,重点就是通过&label 来访问节点,然后直接在里面编写要追加或者修改的内容。
四、总结
本笔记主要学习了Linux设备树最基本的概念,主要学习了设备树的概念,DTS、DTB和DTC的区别以及设备树相关的语法知识。
Linux版本号4.1.15 芯片I.MX6ULL
本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。