1 Linux 设备树概述
1.1 Linux 设备树概述
Linux 设备树是一个包含节点和属性的简单树状结构。属性是基于 key-value 对的,
而节点可以同时包含属性和子节点。下面这个树就是一个典型结构:
/ {
node1 {
a-string-property = “A string”;
a-string-list-property = “first string”, “second string”;
a-byte-data-property = [0x01 0x23 0x34 0x56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = “Hello, world”;
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 >; /* each number (cell) is a uint32 */
child-node1 {
};
};
};
这棵树没有描述任何东西,但是它体现了节点的一些属性:
一个单独的根节点:“/”
两个子节点:“node1”和“node2”
两个 node1 的子节点:“child-node1”和“child-node2”
一堆分散在树里的属性
可以这么简单理解:节点就是树枝,属性就是树叶;树枝上可以有再长树枝也可以长
树叶,而树叶上则不会再长树枝。
属性是基于 key-value 结构的,value 可以为空或者特定格式的字符串内容。由于数据
类型并不被编码到最终的数据结构中,设备树源代码中仅能支持有限的几种基本数据类
型,如下:
文本字符串(无结束符)可以用双引号表示:
string-property = “a string”
'Cells’是 32 位无符号整数,用尖括号表示:
cell-property = <0xbeef xabcd>
二进制数据用方括号表示:
binary-property = [0x01 0x23 0x45 0x67];
不同表示形式的数据可以使用逗号连在一起:
mixed-property = “a string”, [0x01 0x23 0x45 0x67], <0x12345678>;
逗号也可用于创建字符串列表:
string-list = “red fish”, “blue fish”;
1.2 基本概念
1.2.1 初始结构
第一步就是构建一个基本结构,这是一个设备树最基本的结构。在这个阶段,需要一
个唯一的标识该机器。
/ {
compatible = “gpio-leds”;
};
compatible 指定了系统的名称。它包含了一个“<>,<>”形式的字符串。重要的是要指定
一个确切的设备,并且包括制造商的名字,以避免命名空间冲突。由于操作系统会使用
compatible 的值来决定如何在机器上运行,所以正确的设置这个属性十分重要。
1.2.2 中央处理器
第二步就是描述 CPU。先添加一个名为“cpus”的容器节点,然后为每个 CPU 分别添
加子节点,以 TI AM437x 平台为例:
/ {
compatible = “ti,am437x-gp-evm”;
cpus {
cpu@0 {
compatible = “ti,am4372”;
};
cpu@1 {
compatible = “ti,am4372”;
};
};
};
每个 cpu 节点 compatible 属性是一个“<>,<>”形式的字符串,并指定了确切的 cpu,就
像顶层的 compatible 属性一样。
1.2.3 节点名称
每个节点必须有一个“<>[@<>]”形式的名字。
<< span>名称>就是一个不超过 31 位的简单 ascii 字符串。通常,节点的命名应该根据
它所体现的是什么样的设备。比如一个 3com 以太网适配器的节点就应该命名为
ethernet,而不应该是 3com509。 如果该节点描述的设备有一个地址的话,还应该加上设备地址(unit-address)。通常,
设备地址就是用来访问该设备的主地址,并且该地址也在节点的 reg 属性中列出。 同级节点命名必须是唯一的,但只要地址不同,多个节点也可以使用一样的通用名,
例如 serial@101f1000 和 serial@101f2000。
1.2.4 设备
系统中每个设备都表示为一个设备树节点。所以接下来就应该为这个设备树填充设备
节点。
/ {
compatible = “ti,am437x-gp-evm”;
cpus {
cpu@0 {
compatible = “ti,am4372”;
};
cpu@1{
compatible = “ti,am4372”;
};
};
serial@101F0000 {
compatible = “ti,omap2-uart”;
};
serial@101F2000 {
compatible = “ti,am4372-uart”;
};
gpio@101F3000 {
compatible = “ti,pl061”;
};
spi@10115000 {
compatible = “ti,ads7846”;
};
external-bus {
ethernet@0,0 {
compatible = “ti,smc91c111”;
};
i2c@1,0 {
compatible = “ti,tps65218”;
rtc@58 {
compatible = “ti,ds1338”;
};
};
flash@2,0 {
compatible = “ti,omap2-nand”, “omap3-nand”;
}
在此树中,已经为系统中的每个设备添加了节点,而且这个层次结构也反映了设备与
系统的连接方式。例如,外部总线上的设备就是外部总线节点的子节点,i2c 设备就是 i2c
总线节点的子节点。通常,这个层次结构表现的是 CPU 视角的系统视图。
在这颗树中,应该注意这些事情:
每个设备节点都拥有一个 compatible 属性。 flash 闪存节点的 compatible 属性由两个字符串构成。 正如前面所述,节点的命名应当反映设备的类型而不是特定的型号。
1.2.5 理解 compatible 属性
设备树中每个节点都需要一个 compatible 属性。compatible 属性是操作系统用来决定
使用哪个设备驱动来绑定到一个设备上的关键因素。
compatible 是一个字符串列表,其中第一个字符串指定了这个节点所表示的确切的设
备,该字符串的格式为:"<>,<>"。剩下的字符串的则表示其它与之相兼容的设备。
1.3 如何编址
可编址设备使用以下属性将地址信息编码进设备树:
reg
#address-cells
#size-cells
每个可编址设备都有一个元组列表的 reg,元组的形式为:reg = <>。每个元组都表
示一个该设备使用的地址范围。每个地址值是一个或多个 32 位整型数列表,称为 cell。
同样,长度值也可以是一个 cell 列表或者为空。
由于地址和长度字段都是可变大小的变量,那么父节点的#address-cells 和#size-cells
属性就用来声明各个字段的 cell 的数量。换句话说,正确解释一个 reg 属性需要用到父节
点的#address-cells 和#size-cells 的值。
1.3.1 CPU 编址
CPU 节点是一个关于编址的最简单的例子。每个 CPU 都分配了一个唯一的 ID,并且
没有 CPU id 相关的大小信息。
- cpus {
- #address-cells = <1>;
- #size-cells = <0>;
- cpu@0 {
- compatible = “ti,am4372”;
- reg = <0>;
- };
- cpu@1 {
- compatible = “ti,am4372”;
- reg = <1>;
- };
- };
在 cpu 节点中,#address-cells 设置为 1,#size-cells 设置为 0。这意味着子节点的 reg
值是一个单一的 uint32,这是一个不包含大小字段的地址,为这两个 cpu 分配的地址是 0 和 1。cpu 节点的#size-cells 为 0 是因为只为每个 cpu 分配一个单独的地址。
注意:reg 的值和节点名字是相同的。按照惯例,如果一个节点有 reg 属性,那么该节点
的名字就必须包含设备地址,这个设备地址就是 reg 属性里第一个地址值。
1.3.2 内存映射设备
与 cpu 节点里单一地址值不同,应该分配给内存映射设备一个地址范围。#size-cells
声明每个子节点的 reg 元组中长度字段的大小。在接下来的例子中,每个地址值是 1 个
cell(32 位),每个长度值也是 1 个 cell,这是典型的 32 位系统。64 位的机器则可以使
用值为 2 的#address-cells 和#size-cells 来获得在设备树中的 64 位编址。
/ {
#address-cells = <1>;
#size-cells = <1>;
…
serial@101f0000 {compatible = “ti,omap2-uart”;
reg = <0x101f0000 x >;
};
serial@101f2000 {
compatible = “ti,am4372-uart”;
reg = <0x101f2000 x >;
};
gpio@101f3000 {
compatible = “ti,pl061”;
reg = <0x101f3000 xspan>
0x101f4000 0x0010>;
};
spi@10115000 {
compatible = “ti,ads7846”;
reg = <0x10115000 x >;
};
…
};
每个设备都被分配了一个基址以及该区域的大小。这个例子中为 GPIO 分配了两个地
址范围:0x101f3000…0x101f3fff 和 0x101f4000…0x101f400f。
一些挂在总线上的设备有不同的编址方案。例如一个带独立片选线的设备也可以连接
至外部总线。由于父节点会为其子节点定义地址域,所以可以选择不同的地址映射来最
恰当的描述该系统。下面的代码展示了设备连接至外部总线并将其片选号编码进地址的
地址分配。
external-bus {
#address-cells = <2>
#size-cells = <1>;
ethernet@0,0 {
compatible = “ti,smc91c111”;
reg = <0 x>;
};
i2c@1,0 {
compatible = “ti,a1234-i2c-bus”;
reg = <1 x>;
rtc@58 {
compatible = “ti,ds1338”;
};
};
flash@2,0 {
compatible = “ti,omap2-nand”, “omap3-nand”;
reg = <2 x>;
};
};
外部总线的地址值使用了两个 cell,一个用于片选号,另一个则用于片选基址的偏移
量。而长度字段则还是单个 cell,这是因为只有地址的偏移部分才需要一个范围量。所以,
在这个例子中,每个 reg 项都有三个 cell:片选号、偏移量和长度。
由于地址域是包含于一个节点及其子节点的,所以父节点可以自由的定义任何对于
该总线来说有意义的编址方案。那些在直接父节点和子节点以外的节点通常不关心本地
地址域,而地址应该从一个域映射到另一个域。
详细更多在官网
销售邮箱:sales@tronlong.com
技术邮箱:support@tronlong.com
创龙总机:020-8998-6280
技术热线:020-3893-9734
创龙官网:www.tronlong.com
技术论坛:www.51ele.net
线上商城:https://tronlong.taobao.com