Linux设备树

1、设备树引入

1.1、诞生的缘由

常人做法:

  我们想一想,我们要平时要描述一个外设资源会怎样去做?
  在c代码中指定,在代码中描述出来,再好一点就将驱动和设备分离出来。

问题出现:

  使用同一个芯片的板子,它们所用的外设资源不一样,比如A板的LED用GPIO A,B板的LED用GPIO B。那么就需要不一样的代码来描述。
  随着ARM芯片的流行,内核中针对这些ARM板保存有大量的、没有技术含量的文件。

解决方法:

  Linus大发雷霆:“this whole ARM thing is a f*cking pain in the ass”。

  Linus发火之后,内核开始全面使用设备树来改造,神人就神人。

牛人发火就是不一样!!!

1.2、为什么叫设备“树”?

  设备树(Device Tree),将这个词分开就是“设备”和“树”。“树”这个描述非常形象,因为它确实像树,如下图。
设备树

1.3、设备树简介

- 本质:

  是采用树状结构来描述板子上的设备信息的文件

- 作用:

  用来给内核里的驱动程序,指定硬件的信息

- 特点:

  设备树都有一个根节点,其下是由大括号括住的设备节点

2、linux设备树中的DTS、DTB和DTC

2.1、DTS(DeviceTree Source)

-介绍:

  .dts文件是设备树的源文件

- 主要功能:

  根据开发板的结构,描述开发板的设备信息

2.2、DTB(DeviceTree Blob)

- 介绍:

  .dtb文件是.dts被DTC编译后的二进制格式的设备树文件

- 作用:

  可以被Linux内核解析

2.3、DTC(DeviceTree Compiler)

- 介绍:

  DTC是将.dts编译为.dtb的工具,相当于gcc

2.4、三者关系图

dts源文件
dtc编译器
dtb二进制文件

3、设备树语法

3.1、DTS文件布局

/dts-v1/;                		// 表示版本
[memory reservations]    		// 格式为: /memreserve/ <address> <length>;
/ {								// '/'表示根节点
    [property definitions]
    [child nodes]
};

3.2、.dtsi 头文件

3.2.1、介绍

  由于一个SoC可能对应多个设备,这些.dst文件可能包含很多共同的部分,共同的部分一般被提炼为一个.dtsi 文件,这个文件相当于C语言的头文件

3.2.2、作用

  一般.dtsi文件用于描述SOC的内部外设信息,比如CPU架构、主频、外设寄存器地址范围,比如 UART、IIC 等等

3.2.3、举例

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

注意:.dts 文件引用 C 语言中的.h 文件,甚至也可以引用.dts 文件
     不过,在编写设备树头文件的时候最好选择.dtsi 后缀

3.3、设备节点

3.3.1、作用

  描述设备的信息,设备树中的基本单元

3.3.2、语法

- 格式

[label:] node-name[@unit-address] {
  	[properties definitions]
 	[child nodes]
};

- label
  节点标签,为了方便访问节点
  可以直接通过&label 来访问这个节点

- node-name
  节点名字,为 ASCII 字符串
  节点名字应该要能够清晰的描述出节点的功能

- unit-address
  一般表示设备的地址或寄存器首地址
  如果某个节点没有地址或者寄存器的话“unit-address”可以不要

3.4、常用的属性

  节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。

3.4.1、compatible 属性

  “compatible”表示“兼容”,对于某个LED,内核中可能有A、B、C三个驱动都支持它,那可以这样写:

led {
	compatible =A,B,C;
};

内核启动时,就会为这个LED按这样的优先顺序为它找到驱动程序:ABC
  • 根节点下也有compatible属性,用来选择哪一个“machine desc”:一个内核可以支持machine A,也支持machine B,内核启动后会根据根节点的compatible属性找到对应的machine desc结构体,执行其中的初始化函数。
  • compatible的值,建议取这样的形式:“manufacturer,model”,即“厂家名,模块名”。

3.4.2、 #address-cells、#size-cells属性

这两个属性的值都是无符号 32 位整形。

  • address-cells:描述子节点reg属性值的地址表中首地址cell数量,表明子节点reg中 address 这个数据所占用的字长。
  • size-cells:描述子节点reg属性值的地址表中地址长度cell数量,表明子节点reg中 length 这个数据所占用的字长。

比如一段内存,怎么描述它的起始地址和大小?

/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
	reg = <0x80000000 0x20000000>;
	};
};

address-cells为1,所以reg中用132位数来表示地址,即用0x80000000来表示地址
size-cells为1,所以reg中用132位数来表示大小,即用0x20000000表示大小

3.4.3、model 属性

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

model = "wm8960-audio";

3.4.4、status 属性

  status 属性是和设备状态有关的, status 属性值也是字符串,字符串是设备的状态信息,可选的状态如下表所示:

描述
“okay”设备正常运行
“disabled”设备不可操作,但是未来可以变为可操作性,如热插拔设备
“fail”发生严重错误,需要修复
“fail-sss”含义和“fail”相同,后面的 sss 部分是检测到的错误内容

3.4.5、reg 属性

  reg的本意是register,用来表示寄存器地址。
  但是在设备树里,它可以用来描述一段空间。对于ARM系统,寄存器和内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。
  reg属性的值,是一系列的“address size”,用多少个32位的数来表示address和size,由其父节点的#address-cells、#size-cells决定。

示例:

/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>; 
memory {
	reg = <0x80000000 0x20000000>;
};
};

3.5、根节点

  每个设备树文件只有一个根节点“/”,其他所有的设备节点都是它的子节点
  每个节点都有 compatible 属性,根节点“/”也不例外。

- 根节点 compatible 属性:
作用:①通过根节点的 compatible 属性可以知道我们所使用的设备;
   ②Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备。
组成:①第一个值描述了所使用的硬件设备名字
   ②第二个值描述了设备所使用的 SOC

3.6、向节点追加或修改内容

3.6.1、直接修改

  我们设想一下,当我们想要添加一个I2C设备,会怎么做,可能很多人想直接在I2C节点下添加信息:

i2c1: i2c@021a0000 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
	reg = <0x021a0000 0x4000>;
	interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_I2C1>;
	status = "disabled";

	// 我们添加的I2C子节点
	i2c_device@xx {
		/* 子节点信息 */
	};
};

  虽然说这样写没有错,但是有个大问题!i2c1节点是定义在imx6ull.dtsi 文件中的,而前面讲得.dtsi是设备树的头文件,这样就会导致:每一个引用imx6ull.dtsi 文件的设备,都添加了i2c_device这个设备,这并不是我们想要的。

3.6.2、通过标签访问修改

  一般情况下,都是在对应的.dts文件下通过引用标签,对相应节点进行修改或添加的。

示例:

&i2c1 {
	clock-frequency = <100000>;		// 向i2c1节点添加clock-frequency属性
	status = "okay";				// 将i2c1节点的status属性修改为okay

	i2c_device@xx {					// 在i2c1节点下添加子节点
		/* 子节点信息 */
	};
};

4、编译设备树

要编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命令:

make all

编译 Linux 源码中的所有东西,包括 zImage,.ko 驱动模块以及设备树

或者

make dtbs

只编译设备树

5、内核对设备树的处理

PC
内核
内核
DTS
DTB
device_node
platform_device
  1. dts文件在PC机上被编译为dtb文件
  2. u-boot把dtb文件传给内核
  3. 内核解析dtb文件,把每一个节点都转换为device_node结构体
  4. 对于某些device_node结构体,会被转换为platform_device结构体
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vis-Lin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值