- Linux设备树
Linux3.x以后的版本才引入了设备树,设备树用于描述一个硬件平台的板级细节。 在早些的linux内核,这些“硬件平台的板级细节”保存在linux内核目录“/arch”, 以ARM平台为例“硬件平台的板级细节”保存在“/arch/arm/plat-xxx”和“/arch/arm/mach-xxx”目录下。 随着处理器数量的增多用于描述“硬件平台板级细节”的文件越来越多导致Linux内核非常臃肿, Linux之父发现这个问题之后决定使用设备树解决这个问题。设备树简单、易用、可重用性强, linux3.x之后大多采用设备树编写驱动。
关于设备树的详细请参考:https://www.devicetree.org/
8.1. 设备树简介
设备树的作用就是描述一个硬件平台的硬件资源。这个“设备树”可以被bootloader(uboot)传递到内核, 内核可以从设备树中获取硬件信息。
设备树
设备树描述硬件资源时有两个特点。
第一,以“树状”结构描述硬件资源。例如本地总线为树的“主干”在设备树里面称为“根节点”, 挂载到本地总线的IIC总线、SPI总线、UART总线为树的“枝干”在设备树里称为“根节点的子节点”, IIC 总线下的IIC设备不止一个,这些“枝干”又可以再分。
第二,设备树可以像头文件(.h文件)那样,一个设备树文件引用另外一个设备树文件, 这样可以实现“代码”的重用。例如多个硬件平台都使用i.MX6ULL作为主控芯片, 那么我们可以将i.MX6ULL芯片的硬件资源写到一个单独的设备树文件里面一般使用“.dtsi”后缀, 其他设备树文件直接使用“# includexxx”引用即可。
DTS、DTC和DTB它们是文档中常见的几个缩写。
DTS 是指.dts格式的文件,是一种ASII 文本格式的设备树描述,也是我们要编写的设备树源码,一般一个.dts文件对应一个硬件平台,位于Linux源码的“/arch/arm/boot/dts”目录下。
DTC 是指编译设备树源码的工具,一般情况下我们需要手动安装这个编译工具。
DTB 是设备树源码编译生成的文件,类似于我们C语言中“.C”文件编译生成“.bin”文件。
8.2. 设备树框架
上一小节简单了解了设备树的作用,到现在为止我们还不知道“设备树”是究竟是个什么样子。 不妨打开本章节的配套代码~linux_driver/device_tree/imx6ull-mmc-npi.dts 或者 内核源码/arch/arm/boot/dts/imx6ull-mmc-npi.dts 先睹为快。
下面的内容将围绕着设备树源码,来讲解设备树框架和基本语法。
设备树 (内核源码/arch/arm/boot/dts/imx6ull-mmc-npi.dts)
#include <dt-bindings/input/input.h>
#include “imx6ull.dtsi”
/ {
model = “Embedfire i.MX6ULL Board”;
compatible = “fsl,imx6ull-14x14-evk”, “fsl,imx6ull”;
aliases {
pwm0 = &pwm1;
pwm1 = &pwm2;
pwm2 = &pwm3;
pwm3 = &pwm4;
};
chosen {
stdout-path = &uart1;
};
memory {
reg = <0x80000000 0x20000000>;
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x14000000>;
linux,cma-default;
};
};
/*-------------以下内容省略-------------*/
};
&cpu0 {
/dc-supply = <®_gpio_dvfs>;/
clock-frequency = <800000000>;
};
&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <786432000>;
};
&fec1 {
pinctrl-names = “default”;
pinctrl-0 = <&pinctrl_enet1>;
phy-mode = “rmii”;
phy-handle = <ðphy0>;
status = “okay”;
};
/-------------以下内容省略--------------/
imx6ull.dtsi头文件 (内核源码/arch/arm/boot/dts/imx6ull.dtsi)
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
/*-------------以下内容省略--------------*/
};
};
设备树源码分为三部分,介绍如下:
第1-2行: 头文件。设备树是可以像C语言那样使用“#include”引用“.h”后缀的头文件,也可以引用设备树“.dtsi”后缀的头文件。 imx6ull.dtsi由NXP官方提供,是一个imx6ull平台“共用”的设备树文件。
第4-35行: 设备树节点。设备树给我们最直观的感受是它由一些嵌套的大括号“{}”组成, 每一个“{}”都是一个“节点”。“/ {…};”表示“根节点”,每一个设备树只有一个根节点。 如果打开“imx6ull.dtsi”文件可以发现它也有一个根节点,虽然“imx6ull-mmc-npi.dts”引用了“imx6ull.dtsi”文件, 但这并不代表“imx6ull-mmc-npi.dts”设备树有两个根节点,因为不同文件的根节点最终会合并为一个。 在根节点内部的“aliases {…}”、“chosen {…}”、“memory {…}”等字符,都是根节点的子节点。
第37-53行: 设备树节点追加内容。第三部分的子节点比根节点下的子节点多了一个“&”, 这表示该节点在向已经存在的子节点追加数据。这些“已经存在的节点”可能定义在“imx6ull-mmc-npi.dts”文件, 也可能定义在“imx6ull-mmc-npi.dts”文件所包含的设备树文件里。 本代码中的“&cpu0 {…}”、“&clks {…}”、“&fec1 {…}”等等追加的目标节点,就是定义在“imx6ull.dtsi”中。
到目前为止我们知道设备树由一个根节点和众多子节点组成,子节点也可以继续包含其他节点,也就是子节点的子节点。 设备树的组成很简单,下面我们一起来看看节点的基本格式和节点属性。
8.2.1. 节点基本格式
设备树中的每个节点都按照以下约定命名:
节点基本格式
node-name@unit-address{
属性1 = …
属性2 = …
属性3 = …
子节点…
}
node-name 节点名称
节点格式中的 node-name 用于指定节点的名称。 它的长度为1至31个字符,只能由如下字符组成
表 节点名称
字符
描述
0-9
数字
a-z
小写字母
A-Z
大写字母
,
英文逗号
.
英文句号
_
下划线
加号
减号
另外,节点名应当使用大写或小写字母开头,并且能够描述设备类别。
注意,根节点没有节点名,它直接使用“/”指代这是一个根节点。
@unit-address
@unit-address ,其中的符号“@”可以理解为是一个分割符,“unit-address”用于指定“单元地址”, 它的值要和节点“reg”属性的第一个地址一致。如果节点没有“reg”属性值,可以直接省略“@unit-address”, 不过要注意这时要求同级别的设备树下(相同级别的子节点)节点名唯一,从这个侧面也可以了解到, 同级别的子节点的节点名可以相同,但是要求“单元地址”不同,node-name@unit-address 的整体要求同级唯一。
8.2.2. 节点标签
在imx6ull.dtsi头文件中,节点名“cpu”前面多了个“cpu0”,这个“cpu0”就是我们所说的节点标签。 通常节点标签是节点名的简写,所以它的作用是当其它位置需要引用时可以使用节点标签来向该节点中追加内容。
8.2.3. 节点路径
通过指定从根节点到所需节点的完整路径,可以唯一地标识设备树中的节点,不同层次的设备树节点名字可以相同,同层次的设备树节点要唯一。 这有点类似于我们Windows上的文件,一个路径唯一标识一个文件或文件夹,不同目录下的文件文件名可以相同。
8.2.4. 节点属性
在节点的“{}”中包含的内容是节点属性,通常情况下一个节点包含多个属性信息, 这些属性信息就是要传递到内核的“板级硬件描述信息”,驱动中会通过一些API函数获取这些信息。
例如根节点“/”就有属性compatible = “fsl,imx6ull-14x14-evk”, “fsl,imx6ull”。 我们可以通过该属性了解到硬件设备相关的名字叫“imx6ull-14x14-evk”,设备所使用的的是“imx6ull”这颗 SOC。
我们编写设备树最主要的内容是编写节点的节点属性,通常情况下一个节点代表一个设备, 设备有哪些属性、怎么编写这些属性、在驱动中怎么引用这些属性是我们后面讲解的重点, 这一小节只讲解设备节点有哪些可设置属性。有一些节点属性是所有节点共有的,一些作用于特定的节点, 我们这里介绍那些共有的节点属性,其他节点属性使用到时再详细介绍。
节点属性分为标准属性和自定