Linux驱动开发:设备树dts详解_dts设备树

/ {
aliases {
can0 = &flexcan1;

};
cpus {
#address-cells = <1>;
#size-cells = <0>;

	cpu0: cpu@0 {
		compatible = "arm,cortex-a7";
		device_type = "cpu";
		reg = <0>;
		clock-latency = <61036>; /* two CLK32 periods */
		operating-points = <
			/* kHz	uV */
			996000	1275000
			792000	1225000
			528000	1175000
			396000	1025000
			198000	950000
		>;
						/* kHz	uV */
			996000	1275000
			792000	1225000
			528000	1175000
			396000	1025000
		...
};

intc: interrupt-controller@00a01000 {
	compatible = "arm,cortex-a7-gic";
	#interrupt-cells = <3>;
	interrupt-controller;
	reg = <0x00a01000 0x1000>,
	<0x00a02000 0x100>;
};
	...

文件描述了 **CPU arm**,**cortex-a7** ,支持 **996MHz**、 **792MHz**等频率, 时钟一些信息。


* “/”是根节点,每个设备树文件只有一个根节点
* “/”是根节点,每个设备树文件只有一个根节点



node-name@unit-address


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



label: node-name@unit-address


引入 **label** 的目的就是为了方便访问节点,可以直接通过 **&label** 来访问这个节点,比如通过**&cpu0** 就可以访问 **“cpu@0”** 这个节点。很明显通过 **&intc** 来访问 **“interrupt-controller@00a01000”** 这个节点要方便很多!


每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式如下所示:


**1、字符串**



compatible = “fairchild,74hc595”;


**2、32 位无符号整数**



reg = <0 0x123456 100>;


**3、字符串列表**



compatible = “fsl,imx6ul-pxp-v4l2”, “fsl,imx6sx-pxp-v4l2”, “fsl,imx6sl-pxp-v4l2”;


## 二、设备树详解


### 2.1 标准属性


**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”**和 **“imx-audio-wm8960”** 表示驱动模块名字。设备首先使用第一个兼容值在 Linux 内核里面查找,如果没有找到的话就使用第二个兼容值查。


**2、 model 属性**


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



> 
> model = "wm8960-audio";
> 
> 
> 


**3、status 属性**


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


![](https://img-blog.csdnimg.cn/c4bdba60a991476783f6c5a566cbb4fc.png)


**4、★#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** 这个数据所占用的字长 



aips3: aips-bus@02200000 {
compatible = “fsl,aips-bus”, “simple-bus”;
#address-cells = <1>;
#size-cells = <1>;

dcp: dcp@02280000 {
	compatible = "fsl,imx6sl-dcp";
	reg = <0x02280000 0x4000>;
};

};


说明 aips3: aips-bus@02200000 节点起始地址长度所占用的字长为 1,地址长度所占用的字长也为 1  
 子节点 dcp: dcp@02280000 的 reg 属性值为<0x02280000 0x4000>相当于设置了起始地址为 0x02280000,地址长度为 0x40000,但是 dcp的地址长度(范围)并没有 0x4000 这么多


**5、 ★reg 属性**


reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息,一般是(address, length)组成,详情如上所述!


**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 属性值为空值**,说明子地址空间和父地址空间完全相同,不需要进行地址转换,例程如下:



soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = “simple-bus”;
interrupt-parent = <&gpc>;
ranges;//为空


**ranges** 属性不为空的示例代码如下所示:



soc {
compatible = “simple-bus”;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0xe0000000 0x00100000>;

serial {
	device_type = "serial";
	compatible = "ns16550";
	reg = <0x4600 0x100>;
	clock-frequency = <0>;
	interrupts = <0xA 0x8>;
	interrupt-parent = <&ipic>;
};

};


节点 soc 定义的 ranges 属性,值为 **<0x0 0xe0000000 0x00100000>**,此属性值指定了一个 1024KB(0x00100000)的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000。


**serial** 是串口设备节点,**reg** 属性定义了 serial 设备寄存器的起始地址为 **0x4600**,寄存器长度为 **0x100**。经过地址转换,**serial** 设备可以从 **0xe0004600** 开始进行读写操作,**0xe0004600****=0x4600+0xe0000000**。


### 2.2 向节点追加或修改内容


**imx6ull.dtsi** 有以下内容,表示 **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”;
};


现在要在 **i2c1** 节点下创建一个子节点,这个子节点就是 **fxls8471**,最简单的方法就是在 i2c1 下直接添加一个名为 **fxls8471** 的子节点,如下所示: 



&i2c1 {
clock-frequency = <100000>;
pinctrl-names = “default”;
pinctrl-0 = <&pinctrl_i2c1>;
status = “okay”;

fxls8471@1e {
	compatible = "fsl,fxls8471";
	reg = <0x1e>;
	position = <0>;
	interrupt-parent = <&gpio5>;
	interrupts = <0 8>;

};


子节点可以修改增加一些属性;  
 比如子节点中 **clock-frequency** 新增加的属性。  
**status** 状态由disabled变成 **okay**


### 2.3 设备树在目录中的体现


运行 **cd /proc/device-tree** 后,**ls -a** 查询当前目录下的文本情况


![](https://img-blog.csdnimg.cn/1ce9fe5e7b414c7cbf3abd07d44649dc.png)


**1、在当前目录下执行 cat model**



> 
> **model** 的内容是 **“Freescale i.MX6 ULL 14x14 EVK Board”**
> 
> 
> **compatible** 的内容为 **“fsl,imx6ull-14x14-evkfsl,imx6ull”**
> 
> 
> 


![](https://img-blog.csdnimg.cn/8d3b5603da274ca7947a312c87fece40.png)


打开文件 **imx6ull-alientek-emmc.dts** 查看一下,这正是根节点 **“/”** 的 **model** 和 **compatible** 属性值


**2、soc子节点**


![](https://img-blog.csdnimg.cn/27302362af68416eb5155f2086d203aa.png)


**3、aliases 子节点**


![](https://img-blog.csdnimg.cn/9072dc94a12844fdb77a8519c73ffab1.png)


 与imx6ull.dtsi中的 aliases一致


**4、chosen 子节点**


**chosen** 并不是一个真实的设备, **chosen** 节点主要是为了 **uboot 向 Linux 内核传递数据**,重点是 bootargs 参数,一般.dts 文件中 chosen 节点通常为空或者内容很少, **imx6ull-alientekemmc.dts** 中 chosen 节点内容如下所示:



chosen {
stdout-path = &uart1;
}


**chosen** 节点仅仅设置了属性 **“stdout-path”**,表示标准输出使用 **uart1**。



root@ATK-IMX6U:/proc/device-tree/chosen# ls
bootargs name stdout-path


我们可以发现 **chosen** 内存在 **boot** 的启动参数 **bootargs**!


![](https://img-blog.csdnimg.cn/ae690851600441448168396532ee1b56.png)



> 
> **cat 查看确实是启动信息**  
>  1,我们并没有在设备树中设置 chosen 节点的 bootargs 属性,那么 bootargs这个属性是怎么产生的如何关联起来的呢?  
>  2,为什么和 uboot 中的参数不一致?
> 
> 
> 


**chosen** 节点的 bootargs 属性不是我们在设备树里面设置的,那么只有一种可能,那就是 uboot 自己在 chosen 节点里面添加了 bootargs 属性,并且设置 bootargs 属性的值为 bootargs环境变量的值。


uboot 源码中搜索 “chosen”,在文件 **common/fdt\_support.c**中



int fdt_chosen(void *fdt)
{
//寻找chosen节点
nodeoffset = fdt_find_or_add_subnode(fdt, 0, “chosen”);

if (nodeoffset < 0)
	return nodeoffset;
//读取bootargs环境
str = getenv("bootargs");

}


### 2.4 Linux 内核解析 DTB 文件


**启动内核流程函数**


![](https://img-blog.csdnimg.cn/b7126f9952fe4dd580780412e8307927.png)


**start\_kernel** 函数中最终调用了函数为 **unflatten\_dt\_node**(很多初始化操作都在**start\_kernel** )!


### **2.5 帮助文档信息**


设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。那么我们在设备树中添加一个硬件对应的节点的时候从哪里查阅相关的说明呢?路径为:Linux 源码目录 **/Documentation/devicetree/bindings**


比如我们现在要想在 I.MX6ULL 这颗 SOC 的 I2C 下添加一个节点,那么就可以查看**Documentation/devicetree/bindings/i2c/i2c-imx.txt**,此文档详细的描述了 I.MX 系列的 SOC 如何在设备树中添加 I2C 设备节点,文档内容如下所示:



root@ubuntu:/home/wy/imx6ul/kernel/Documentation/devicetree/bindings/i2c# cat i2c-imx.txt

  • Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX

Required properties:

  • compatible :
    • “fsl,imx1-i2c” for I2C compatible with the one integrated on i.MX1 SoC
    • “fsl,imx21-i2c” for I2C compatible with the one integrated on i.MX21 SoC
    • “fsl,vf610-i2c” for I2C compatible with the one integrated on Vybrid vf610 SoC
  • reg : Should contain I2C/HS-I2C registers location and length
  • interrupts : Should contain I2C/HS-I2C interrupt
  • clocks : Should contain the I2C/HS-I2C clock specifier

Optional properties:

  • clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
    The absence of the propoerty indicates the default frequency 100 kHz.
  • dmas: A list of two dma specifiers, one for each entry in dma-names.
  • dma-names: should contain “tx” and “rx”.

Examples:

i2c@83fc4000 { /* I2C2 on i.MX51 */
compatible = “fsl,imx51-i2c”, “fsl,imx21-i2c”;
reg = <0x83fc4000 0x4000>;
interrupts = <63>;
};

i2c@70038000 { /* HS-I2C on i.MX51 */
compatible = “fsl,imx51-i2c”, “fsl,imx21-i2c”;
reg = <0x70038000 0x4000>;
interrupts = <64>;
clock-frequency = <400000>;
};

i2c0: i2c@40066000 { /* i2c0 on vf610 */
compatible = “fsl,vf610-i2c”;
reg = <0x40066000 0x1000>;
interrupts =<0 71 0x04>;
dmas = <&edma0 0 50>,
<&edma0 0 51>;
dma-names = “rx”,“tx”;
};


## 三、设备树节点的操作函数



> 
> Linux 驱动程序往往需要去读取到 Linux 内核中附带的 dts 文件,并操作设备树 DTS 的相关节点!接下来我们来学习一下,如何进行设备树节点操作! 
> 
> 
> 


### 1、查找节点的 of 函数


Linux 内核使用 device\_node 结构体来描述一个节点


1、 of\_find\_node\_by\_name 函数



//通过节点名字查找指定的节点
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
//from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
//name:要查找的节点名字


2、of\_find\_node\_by\_type 函数
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/c157a25bb660004c396367281a5ef027.png)

![img](https://img-blog.csdnimg.cn/img_convert/9cdc1998a700b55fb55299262834a3ec.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/9792cd4ad0ac008359829c1e8f04b9e4.png)

 **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

![img](https://img-blog.csdnimg.cn/img_convert/7ef4d9769ebd9580382ed1286ae5923e.png)

![img](https://img-blog.csdnimg.cn/img_convert/2887969b8ef9f29466fedf4a0a838956.png)

 

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)**

<img src="https://img-community.csdnimg.cn/images/73bb5de17851459088c6af944156ee24.jpg" alt="img" style="zoom: 67%;" />



# 最后

**资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

**你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!**

1712297512591)]

 **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[外链图片转存中...(img-IRd5SQ1L-1712297512592)]

[外链图片转存中...(img-WXmKgdlg-1712297512593)]

 

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)**

<img src="https://img-community.csdnimg.cn/images/73bb5de17851459088c6af944156ee24.jpg" alt="img" style="zoom: 67%;" />



# 最后

**资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

**你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!**

**[更多资料点击此处获qu!!](https://bbs.csdn.net/topics/618376385)**
  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值