设备树

一、设备树的由来

Linux是世界上最大的开源项目之一,由全世界的内核开发者来维护,所有Linux爱好者都可以提交自己的代码进行审核。

Linux内核中有这么一部分是描述一个设备的文件,在早期linux内核下对各个平台/机器的硬件设备描述是使用.c文件(结构体)描述,他们充斥在arch/arm/plat-xxx和arch/arm/mach-xxx目录下。
但是由于每个平台上的硬件设备各有不同,这样对每个平台都需要一个.c文件来描述,随着arm平台越来越多,这样描述板级设备的文件就越多。
然而这对linux内核来说就是一些垃圾代码,(都是一些冗余的硬件描述代码),提交代码的目的是为了更新、优化内核,这些板级描述信息就显得没有意义。
于是为了方便管理将各个平台的硬件描述信息都放到/arch/arm/boot/dts/ 目录下用.dts的文件来描述硬件信息,这个文件采用树形结构来描述。那么 .dts 文件就是设备树。

二、dis、dtc和dtb的关系

dts是设备源码树(device tree sourse)是人能看懂的文件(字符文本文件),内核无法识别,所以使用dtc(Device Tree Compiler)这个编译工具来编译,得到内核可以识别的dtb(Device Tree binary )文件,这是一个二进制文件。

dts文件和dtb文件都在 /arch/arm/boot/dts/ 目录下。

dtc反汇编:
dtc -I dtb -O dts -o xxxx.dts xxxx.dtb

三、dts语法

dts是用于描述硬件平台信息,让内核知道一个设备上有那些外设,它们的地址、使用到哪些IO等。
设备树的结构就像树一样,它由若干个节点组成。

最上层的是根节点“/”,节点包括属性(属性就像变量,保存一些信息)和子节点。就这样若干个节点。
在这里插入图片描述

/dts-v1/; 文件开头的这个是设备树的版本。

注释: dts文件下注释方法和c一样,“/**/” 或 // 。

支持头文件:
支持.dtsi头文件和.h的头文件
值得注意的是dts文件也支持头文件,扩展名为.dtsi,用于描述一个soc的通用信息(比如cpu、soc、中断控制等等),当要用的时候直接引用这个.dtsi文件。这个头文件由soc厂商为我们提供。

比如我们想要使用正点原子阿尔法的开发板下的I2C外设与其它模块通信,那么我们的驱动就需要获取到该I2C外设的一些信息(时钟频率、器件地址、IO控制 等等),设备树就是用来描述这些信息。

我们知道阿尔法开发板使用的是I.MX6ULL这样一款soc,在I.MX6ULL的参考手册下可以找到I2C1是属于soc/AIPS-2/I2C1(这些是芯片厂商自己规定的),那么在对应I.MX6ULL就会有一个.dtsi的头文件来描述这样的信息,至于正点原子用它来做ap3216c这样一个实例则只要引用该设备树的头文件,采用追加的方式添加具体的属性就好了。引用头文件,追加具体的属性用‘&’符号。

前面SOC的外设控制器信息没必要重复描述,放头文件,所有使用该soc的板子都引用这个头文件,具体信息的独立描述,这也就是一个板子一个dts文件。
节点中主要对一些属性赋值,对于头文件中已经赋值过的节点,在dts文件中可以重新赋值。


在这里插入图片描述

设备树的节点

设备树由多个节点构成。
节点用于描述设备的硬件信息(用属性值来描述)。
一个设备树只有一个根节点,当引用头文件时,头文件中的根节点与dts文件中根节点是同一个节点(节点中的内容(子节点或属性)则是两个文件之和(比如有两个memory(内存)节点)。在头文件和dts文件中有重复赋值的属性,以最后的赋值为准。)dts文件中可以对头文件中的节点追加内容。

节点命名

节点名格式:node-name@unit-address;没有reg属性的后面没有单元地址。
比如I.MX6ULL的I2C4节点就是:I2C4@021F8000。I2C4是节点名021F8000是单元地址 ,就是I2C4 寄存器的首地址 ,可以在I.MX6ULL参考手册中找到。
单元地址——一般就是外设寄存器的起始地址(不绝对),根据具体的节点而言。

标签
节点名前面有时候会有标签,使用‘:’号分隔,标签的意义是为了更好的找到节点,追加内容的时候通过‘&’符可以找到标签所在的节点(比如在dts文件在使用"&I2C4",这样追加,那么在头文件中就会有个节点前面有“I2C4:”标签修饰)。标签不属于节点名。

设备树在系统中的体现

bootloader在启动的时候会将设备树在内存中的地址作为参数传递给内核,所以内核能找到这些信息。内核会解析设备树(dtb文件),在/proc/device-tree目录下呈现。

根节点

系统启动后可以在根文件系统里面看到设备树的节点信息。在 /proc/device-tree目录下存放着根节点的属性和子节点 (节点属性以文件的形式存在/proc/device-tree目录下,子节点以文件夹的形式存在)。

在这里插入图片描述

soc节点

soc子节点下一般是一些外设控制的子节点,多与寄存器相关。
这个不是I.MX6ULL,这个平台的I2C节点直接放在soc下了

I2C控制器节点

如图I2C节点下是对I2C外设控制器的描述,如:#address-cells ,status,clock等等属性描述,还有在他下面的子节点
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";
			};

具体实例节点,如rtc

下图是对I2C外设的追加,status = “okay”,修改该status属性,使能I2C。rtc@32节点是对I2C外设使用的实例,用于rtc(实时时钟,因为RTC要与cpu通信)。rtc@32,这个32是rtc的器件地址。

注意:追加的是写在根节点外面的,通过“&标签名”追加。
在dts文件里追加

特殊节点

aliases(别名)节点 和chosen 节点。

aliases节点

aliases一般是给节点定义别名用的(为了更好的找到节点),但是我们一般不用别名来访问,一般用标签的形式。内核会使用。
在这里插入图片描述

chosen节点

chosen 节点:主要目的是将uboot里面bootargs环境变量值,传递给内核作为命令行参数,cmd line。

在uboot中可以查看环境变量bootarges的值,同时在Linux启动时,会打印一行cmdline它的值和bootarges是一样的。

uboot是如何向内核传递bootarges的呢?
经过查看发现节点chosen中包含bootarges属性(我们并没有去写这个属性),它的值和uboot的bootarges是一致的。
那么chosen节点中bootarges是谁添加的呢?最有可能是uboot添加(拥有属性值(bootarges环境变量),而且对dtb操作过)。

uboot中有一个fdt_chosen函数会对chosen节点做一些修改,其中就有给bootarges属性赋值。

特殊属性

属性是节点中用来描述硬件信息,具体的属性由各个节点来决定(因为每个外设,硬件什么的特性都不一样)。
然而也会有一些约定熟成的属性:compatible(兼容的)属性、

compatible(兼容的)属性

设备节点下的compatible属性用于查找可用的驱动程序,根节点下的compatible属性用于匹配某个版本内核是否支持一个平台/机器。

设备节点下的compatible属性

compatible(兼容的)属性也叫 兼容性属性,值是字符串。它是用来描述兼容性的,在驱动程序中也会维护一个兼容性列表(数组结构体),在这个表里会列出它所支持的设备。通过设备树的compatible属性值,与驱动程序里的列表相匹配(字符串是不是一样),可以确定是否兼容。

compatible 属性值的格式:
自己随意赋值,只要是字符串,它的值可以有多个,也就是可以兼容多种驱动

根节点下的compatible属性

用于查看某个版本linux内核是否支持一个平台。
在没有设备树之前uboot会向内核传递一个machline_id值,内核查看是否支持此机器。

在使用设备树的时候,不使用机器ID而是使用根节点下的compatible属性。内核会维护一段空间来保存compatible属性(所有支持的机器)的值。

根节点下compatible值的格式:
“厂商,设备名” 值可以有多个(兼容多个平台的时候)

modle

它的值也是一个字符串,用来描述设备模块的信息,比如名字什么的,应该是平台名字。

modle = “wm8960-audio”;

status属性(相当于使能)

disabled表示不使用,okay表示可用。

#address-cells属性和#size-cells属性

它们的值是32位无符号整型数,可以用到任何一个子节点中,用于描述地址相关的信息。

#address-cells属性: 用于表示子节点reg属性中地址信息的单元数。
#size-cells属性: 用于表示子节点reg属性中长度(地址长度)信息的单元数。
地址信息和地址长度就表示了一段内存。

reg = <寄存器首地址 地址长度>
一个节点的reg属性里的数据会由它的父节点里的#address-cells属性和#size-cells属性决定。

#address-cells = <1>;		//表示地址信息占一个单元
#size-cells = <1>;				//地址长度也占一个单元 

reg属性

前面提到了reg属性,**在大部分情况下reg属性是用来描述一段内存的。(I2C设备节点中reg是表示对方的器件地址,不是I2C控制器)**例如:
** reg = <寄存器首地址 地址长度> ** ,其中它里面的单元个数是由它的父节点的#address-cells属性和#size-cells属性决定的。

比如:

father{
	#address-cells = <1>;		//表示地址信息占一个单元
	#size-cells = <1>;				//地址长度也占一个单元 

……
	son@起始地址{
		……
		reg = <起始地址1 长度1>
	};
};

father{
	#address-cells = <2>;		//表示地址信息占两个单元
	#size-cells = <1>;				//地址长度也占一个单元 
	
	……
	
	son@起始地址{
		reg = <起始地址1 起始地址2 长度1>
};
	
father{
	#address-cells = <2>;		//表示地址信息占两个单元
	#size-cells = <2>;				//地址长度也占两个单元 
	
……

	son@起始地址{
		reg = <起始地址1 起始地址2 长度1 长度2>		//这样来表示
	};
};

之前说在大部分情况下用于描述一段内存,I2C最后与器件的描述就例外,它的reg用于描述与之通信的器件地址。
在这里插入图片描述

ranges属性

用于地址映射相关的,arm很少用到,在I.MX6ULL 中虽然有这个属性,但是值都是空的,表示不存在地址映射。

name属性和device_type属性

name属性:它的值是一个字符串,用来表示节点的名字。已经被淘汰,在老的平台上可能会看到。

device_type属性:也被淘汰,一般会用到cpu的属性和memory属性描述中。

绑定信息文档(相当于是设备树一些常见设备描述的参考文档)

描述一个设备的信息是用属性的,属性可以自定义,但是一些设备的属性大多类似(磁力计、陀螺仪传感器这一类属性大多通用),所以对于描述这样的设备都会遵循一个规则,这个规则在内核设备树的文档里面有写,叫绑定信息文档。

在 Documentation/devicetree/bindings/ 目录下有许多设备的绑定信息文档。
在这里插入图片描述进入I2C外设这个文件夹,可以看到这些不同命名的.txt的文件,这其实是不同厂家芯片对与I2C这个外设的描述。
在这里插入图片描述
打开i2c-imx.txt 就可以看到恩智浦官方对I2C外设的描述:

* 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 property 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".
- scl-gpios: specify the gpio related to SCL pin				
- sda-gpios: specify the gpio related to SDA pin
- pinctrl: add extra pinctrl to configure i2c pins to gpio function for i2c
  bus recovery, call it "gpio" state

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";
        pinctrl-names = "default", "gpio";
        pinctrl-0 = <&pinctrl_i2c1>;
        pinctrl-1 = <&pinctrl_i2c1_gpio>;
        scl-gpios = <&gpio5 26 GPIO_ACTIVE_HIGH>;
        sda-gpios = <&gpio5 27 GPIO_ACTIVE_HIGH>;
};
~

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值