设备树:是一种描述硬件的数据结构,Linux3.x以后的版本才引入了设备树,不是将设备的每个细节都硬编码到操作系统中,而是可以在引导时传递给操作系统的数据结构中描述硬件的许多方面。设备树由OpenFirmware、OpenPOWER抽象层(OPAL)、电源架构平台需求(PAPR)和独立的扁平设备树(FDT)形式使用。
在早些的linux内核,这些“硬件平台的板级细节”保存在linux内核目录“/arch”,
以ARM平台为例“硬件平台的板级细节”保存在“/arch/arm/plat-xxx”和“/arch/arm/mach-xxx”目录下。
设备树是描述一个硬件平台的硬件资源。这个“设备树”可以被bootloader(uboot)传递到内核, 内核可以从设备树中获取硬件信息。在操作系统(OS)引导阶段进行设备初始化(DTB文件在linux内核启动的时候内核解析),解析之后设备树就被放到内存在上(逻辑结构:树状结构)。如果某个驱动需要使用设备信息,直接从设备树上获取对应的设备信息即可。
1、树的主干就是系统的总线,在设备树里称为“根节点”。比如I2C控制器,SPI控制器,CAN控制器等都是接到系统主线上的分支,在设备树上称为“根节点的子节点”
2、设备树可以像头文件(.h)那样,一个设备文件引用另一个设备文件,实现“代码”的重用(复用),例如GEC6818是一款基于ARM公版开发板的产品,它采用了S5P6818芯片作为处理器,如果多个硬件平台都是用S5P6818芯片作为主控芯片,那么我们可以把S5P6818芯片的硬件资源写到一个单独的设备树文件里面一般使用“.dtsi”后缀,其他设备树文件直接使用“#includexxxx”引用即可。
DTS、DTC和DTB它们是文档中常见的几个缩写:
查看设备树的框架和语法
*设备树根节点*/
/ {
model = "imx6ull 正点原子"; /*model属性,用于指定设备的制造商和型号*/
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull"; /*compatible属性,系统用来决定绑定到设备驱动的关键,用来查找节点的方法之一*/
/*根节点的子节点*/
chosen {
stdout-path = &uart1;
};
/*根节点的子节点*/
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
/*根节点的子节点*/
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0xa000000>;
linux,cma-default;
};
};
/*根节点的子节点*/
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_leds>;
status = "okay";
sysled {
lable = "sysled";
gpios = <&gpio4 16 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
};
/*-------------以下内容省略-------------*/
};
/*设备树节点追加内容*/
/*+--------------+
| Misc Modules |
+--------------+*/
/*而是向原有节点追加内容*/
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&pwm1 { /* backlight */
#pwm-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm1>;
status = "okay";
};
设备树里面的每一个“{}”都是一个节点,设备树的源码分为三个部分:
我们查看imx6ull.dtsi头文件,在内核源码/arch/arm/boot/dts/imx6ull.dtsi
pwm1: pwm@2080000 { /*节点标签:节点名称@单元地址*/
compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm"; /*model属性用于指定设备的制造商和型号*/
reg = <0x02080000 0x4000>; /*reg属性描述设备资源在其父总线定义的地址空间内的地址*/
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; /*描述中断相关的信息*/
clocks = <&clks IMX6UL_CLK_PWM1>, /*初始化GPIO外设时钟信息*/
<&clks IMX6UL_CLK_PWM1>;
clock-names = "ipg", "per";
#pwm-cells = <3>; /*表示有多少个cells来描述pwm引脚*/
status = "disabled"; /*状态属性用于指示设备的“操作状态”*/
};
我们通过文件查看了解到,设备树是由一个根节点和很多个子节点组成的,子节点也可以包含其他节点。
设备树下的节点都有规定的命名格式:
node-name@unit-address{
属性1 = xx
属性2 = xx
属性3 = xx
子节点xx
}
1.compatible属性
compatible属性值由一个或多个字符串组成,有多个字符串时使用“,”分隔开。
设备树中的每一个设备的节点都要有一个compatible属性。系统通过compatible属性决定绑定哪一个设备的设备驱动,是用来查找节点的方法之一,也可以通过节点名或节点路径查找指定节点。
例如系统初始化时会初始化platform总线上的设备时,根据设备节点”compatible”属性和驱动中of_match_table对应的值加载对应的驱动。
mqs: mqs {
compatible = "fsl,imx6sx-mqs";//设置compatible属性值,与mqs平台驱动做匹配
pinctrl-names = "default";//定义引脚状态
pinctrl-0 = <&pinctrl_mqs>;//指定mqs引脚的pinctrl信息
clocks = <&clks IMX6UL_CLK_SAI1>;//设置时钟信号
clock-names = "mclk";//设置时钟名
gpr = <&gpr>;//设置寄存器
status = "okay";//设置mqs操作状态
};
2.model属性
model属性用于指定设备的制造商和型号。
*设备树根节点*/
/ {
model = "imx6ull 正点原子"; /*model属性,用于指定设备的制造商和型号*/
---省略---
}
3.status属性
状态属性用于指示设备的“操作状态”,通过status可以去禁止设备或者启用设备,默认情况下不设置status属性设备是使能的。
mqs:mqs{
status = "okay";//设置mqs操作状态
};
4.#address-cells 和 #size-cells
#size-cells和#address-cells决定了子节点的reg属性中哪些数据是“地址”,哪些数据是“长度”信息。
#address-cells,用于指定子节点reg属性“地址字段”所占的字长(单元格cells的个数)。
#size-cells,用于指定子节点reg属性“大小字段”所占的字长(单元格cells的个数)。
例如#address-cells=2,#address-cells=1,每个cells是一个32位宽的数字。
//每个“address length”组合表示一个地址范围,
//其中 address 是起始地址, length 是地址长度,
//#address-cells 表明 address 这个数据所占用的字长,
// #size-cells 表明 length 这个数据所占用的字长.
reg =
5.reg属性
ret属性的书写格式为reg = < cells cells cells cells cells cells…>
reg属性描述设备资源在其父总线定义的地址空间内的地址。通常情况下用于表示一块寄存器的起始地址(偏移地址)和长度, 在特定情况下也有不同的含义。reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息.
例如#address-cells = <1>,#address-cells = <1>,reg = <0x9000000 x4000>, 其中0x9000000表示的是地址,0x4000表示的是地址长度,这里的reg属性指定了起始地址为0x9000000,长度为0x4000的一块地址空间。
6.ranges
该属性提供了子节点地址空间和父地址空间的映射(转换)方法,常见格式是 <子地址、父地址、地址长度>。如果父地址空间和子地址空间相同则无需转换。
ranges 是一个地址映射/转换表, ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成。
如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换。
child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长
parent-bus-address: 父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长
length: 子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长
比如对于#address-cells和#size-cells都为1的话,以ranges=<0x0 0x10 0x20>为例,表示将子地址的从0x0~(0x0 + 0x20)的地址空间映射到父地址的0x10~(0x10 + 0x20)。
7.name和device_type
这两个属性很少用(已经被废弃),不推荐使用。name用于指定节点名,在旧的设备树中它用于确定节点名, 现在我们使用的设备树已经弃用。device_type属性也是一个很少用的属性,只用在CPU和内存的节点上。 如上例中所示,device_type用在了CPU节点。
我们在设备树中添加了一个“led”节点, 正常情况下我们可以从这个节点获取编写led驱动所用到的所有信息,例如led相关控制寄存器地址、 led时钟控制寄存器地址等等。
内核提供了一组函数用于从设备节点获取资源(设备节点中定义的属性)的函数,这些函数以of_开头,称为OF操作函数。