devicetree的结构
节点名字 node-name
node-name@unit-address
node-name应该能够描述设备的一般类别
unit-address是特定于节点所在的总线类型,unit-address必须与reg指定的地址保持一致,如果node没有reg属性,则在同一级中node的名字必须唯一,且unit-address需要省略。
属性 properties
属性(properties)描述节点的特点,是一个键值对。
名字(name)是自己取的,非标准的属性名需要一个独一无二的字符串前缀
值的类型
type | 描述 |
---|---|
<empty> | 用来传输true-false信息 |
<u32> | 大端保存的32位整型 |
<u64> | 大端保存的64位整型 |
<string> | 以’\0’结尾 |
<prop-encoded-array> | |
<phandle> | 本质上是u32类型,在当前节点添加该属性并设定一个独一无二的值,可以供其他节点进行引用 |
<stringlist> | string类型的集合 |
标准属性
- compatible
属性名:compatible
值的类型:<stringlist>
描述:包含一个或者多个指明device编程模型的字符串。
推荐的格式如下:
"manufacture,model"
例子:
compatible = "fsl,mpc8641", "nsl6550";
对于包含该属性的设备,操作系统会首先定位到支持fsl,mpc8641的驱动程序,如果没有会定位到支持nsl6550的驱动程序。
- model
属性名:model
值的类型:<string>
描述:有点像compatible中的单个元素
例子:
model = "fsl, MPC8349EMITX";
- phandle
属性名:phandle
值的类型:<u32>
描述:phandle属性为节点指定了一个在设备树中独一无二的数字标识符
例子:
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node {
interrupt-parent = <1>;
};
使用phandle属性值定义设备pic@10000000,这样在其他设备中就可以直接使用<1>直接引用设备pic@10000000
- status
属性名: status
值类型: <string>
描述: 以下是可选的值
- “okay” : 指明该设备是可操作的
- “disable” : 指明该设备当前是不可操作的,但是之后可以被操作
- “reserved” : 指明该设备是可操作的,但是不应该被使用。通常用在被其他驱动软件控制的设备
- “fail” : 指明该设备是不可操作的,设备被检测到有严重问题,不修复的话没法使用
- “fail-sss” : 与fail属性类似,'sss’指明了错误的情况
#address-cells
和#size-cell
属性名:#address-cells 和 #size-cell
值类型:<u32>
描述: 这两个属性可以用在device tree层次结构中具有子级的任何设备节点中,用来描述应如何寻址子级设备节点。
#address-cells
属性用来在子节点的reg属性中编码地址区域。
#size-cells
属性用来在子节点的reg属性中编码大小区域。
这两个属性并不会从父级设备中继承,每个设备都需要指定该值。
如果没有指定的话,#address-cells
的默认值是2,#size-cells
的默认值是1.
例子:
soc {
#address-cells = <1>;
#size-cells = <1>;
serial@4600 {
compatible = "ns16550";
reg = <0x4600 0x100>
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&ipic>;
};
};
在这个例子中,#address-cells
和#size-cells
属性都设为1,在reg属性中,设备的地址值和该节点的大小都只占一个单元。
- reg
属性名:reg
属性类型:<prop-encoded-array>
编码方式是任意数量的地址、长度对
描述:该属性描述了被父级总线定义的设备资源的地址,通常来该属性表示了设备资源在总线上的偏移量和大小,但是在一些特定的总线类型上可能有所不同。
例子:
假设一个soc上的设备具有两块寄存器组,一个在soc地址偏移0x3000的32字节的块和一个偏移0xFE00的256字节的块。reg的编码如下:
(前提是#address-cells
和#size-cells
都为1)
reg = <0x3000 0x20 0xFE00 0x100>
- virtual-reg
属性名:virtual-reg
值的类型:<u32>
描述:virtual-reg属性指定一个有效地址,该地址映射到设备节点的reg属性中指定的第一个物理地址,该属性使能启动程序在初始化的过程中为用户程序提供虚拟地址到物理地址的映射
- ranges
属性名:ranges
值的类型:empty或者是prop-encoded-array
描述:该属性是提供了一种。ranges值的格式是任意数量的三元组(child-bus-address,parent-bus-address,length)。
child-bus-address是子总线地址空间的物理地址(真实的映射需要加上子设备相对于父设备的地址偏移)
parent-bus-address是父总线地址空间的物理地址
length是指定了range中子地址空间的大小
地址转换的例子:
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0xe0000000 0x00100000>;
serial@4600 {
device_type = "serial";
compatible = "ns16550";
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&pic>;
};
};
ranges属性指定了1024KB大小的地址空间,把子总线soc的物理地址0x0处的内存映射到父总线的物理地址0xe0000000的内存。在这种映射关系中,serial设备的起始地址实际上被映射到总线物理地址为0xe0004600的地址空间
- dma-ranges
属性名:dma-ranges
值的类型:empty或者是prop-encoded-array
描述:该属性的使用与ranges属性类似,该属性是用来描述DMA结构的内存映射关系
Interrupts and Interrupt Mapping
中断树模型:在设备树中,存在逻辑中断树,该逻辑中断树表示平台硬件中的中断的层次结构和路由。虽然通常称为中断树,其实它的结构是有向无环图。
在设备树中,使用interrupt-parent属性表示中断源与中断控制器的物理连线。代表中断产生设备的节点都包含interrupt-parent属性,该值用于接收中断的设备。
中断域的概念:在中断初始化过程中,Linux内核会从特定整数区间中选取一个整数作为一个中断源「interrupt source」的编号,并且使每个中断源都有一个独一无二且不重复的编号。如果系统仅存有一个中断控制器,那么能简单地给每个引脚对应的中断源分配一个编号。不过,如前所述,在有些情况下系统可能配置了多个中断控制器,所以中断源的编号方式将会有点复杂。因此内核需要一种机制去管理硬件中断编号「hwirq」与Linux中断编号「irq」之间的映射,以便使多个中断控制器之间不会出现重复相同的irq。正因如此,内核抽象出了中断域概念,由它实现处理中断号的分配和管理。
例子:
如图所示,定义了一个soc的设备树文件
根据interrupt-parent属性可以整理出该设备树中的中断树,如下图所示
从这个例子中大致可以了解interrupt-parent属性的作用,就是明确的指出设备树中跟中断相关的各个设备的关系。
中断产生设备的属性
- interrupts
属性名:interrupts
值的类型:prop-encoded-array
描述:设备的interrupts属性定义了设备产生的中断
例子:在开放的PIC兼容中断域中,中断说明符的常见定义由两个单元组成。第一个单元定义了中断数和中断等级。下面定义的中断说明符,中断数是0xA,中断等级为8
interrupts = <0xA 8>;
- interrupt-parent
属性名:interrupt-parent
值的类型:phandle
描述:由于节点在中断树中的层级和在设备树中的层级可能不匹配。所以设定了这个属性用来显式的指定中断源。
- interrupts-extended
属性名:interrupts-extended
值的类型:phandle prop-encodeed-array
描述:该属性描述了该设备能够产生的中断。当存在多个中断控制器时,使用interrupts-extended代替interrupts描述中断。
例子:下面的代码,描述了两个中断输入分别接在两个不同的中断控制器上,pic是一个#interrupt-cells属性为2的中断控制器,gic是一个#interrupt-cells属性为1的中断控制器。
interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;
中断控制器的属性
#interrupt-cells
属性名:#interrupt-cells
值的类型: u32
描述:该属性定义了一个中断说明符(中断域)所需要的单元数。
- interrupt-controller
属性名:interrupt-controller
值的类型: <empty>
描述:该属性将包含该属性的节点定义为中断控制器
中断链接属性
每个中断链接节点都要拥有#interrupt-cells属性
- interrupt-map
属性名:interrupt-map
值的类型:prop-encode-array
描述:该属性将一个中断域与一组父中断域桥接,并指定子域中的中断说明符如何映射到其各自的父域。
格式如下:
每一行有5项:
- child unit address: 子节点的单元地址
- child interrupt specifier: 子节点的中断说明符
- interrupt-parent: 该值指向子节点将被映射到的中断父域
- parent unit address: 中断父域的地址
- parent interrupt specifier: 父域中的中断描述符
- interrupt-map-mask
属性名:interrupt-map-mask
值的类型:prop-encoded-array
描述:该属性指定了设备树中的链接节点
#interrupt-cells
属性名:#interrupt-cells
值的类型: u32
描述:该属性定义了一个中断说明符(中断域)所需要的单元数。
中断映射的例子
下面的例子展示了一个设备树的片段,包含了一个PCI总线控制器和一个描述了两个PCI插槽的中断映射的样例
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
open-pic {
clock-frequency = <0>;
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <2>;
};
pci {
#interrupt-cells = <1>;
#size-cells = <2>;
#address-cells = <3>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <
/* IDSEL 0x11 - PCI slot 1 */
0x8800 0 0 1 &open-pic 2 1 /* INTA */
0x8800 0 0 2 &open-pic 3 1 /* INTB */
0x8800 0 0 3 &open-pic 4 1 /* INTC */
0x8800 0 0 4 &open-pic 1 1 /* INTD */
/* IDSEL 0x12 - PCI slot 2 */
0x9000 0 0 1 &open-pic 3 1 /* INTA */
0x9000 0 0 2 &open-pic 4 1 /* INTB */
0x9000 0 0 3 &open-pic 1 1 /* INTC */
0x9000 0 0 4 &open-pic 2 1 /* INTD */
>;
};
};
中断映射表的每一行都包含5个部分,第一行的组成如下:
child unit address: 0x8800 0 0 (pci的#address-cells属性是3)
child interrupt specifier: 1 (pci的#interrupt-cells属性是1)
interrupt parent: &open-pic
parent unit address:是空的(open-pic的address-cells为0)
parent interrupt specifier: 2 1(open-pic的#interrupt-cells属性是2)
在这个例子中,interrupt-map-mask属性的值为<0xf800 0 0 7>
上面的设备树文件主要描述的就是一个具有两个pci插槽的soc,两个pci直接连在soc上,总线号为0x0,pci的设备号,一个是0x11,另一个是0x12
要从open-pic找到function 0x3,IDSEL 0x12(slot2)的父级中断域的说明符(子中断域说明符为2),需要执行以下步骤:
- 先得到设备A的子单元地址和中断说明符,步骤是这样的,总线号左移16位
(0x0<<16)
,设备号左移11位(0x12<<11)
,函数号左移8位(0x3<<8)
,得到的结果<0x9300 0 0 2>
- 把步骤1中得到的结果与interrupt-map-mask进行与操作,得到结果
<0x9000 0 0 2>
- 根据中断映射表得到父级中断说明符为
<4 1>
链接节点和说明符映射
链接节点的属性
一个链接节点必须拥有#<specifier>-cells
属性,<specifier>
是指像’gpio’,‘clock’,'reset’等等的说明符空间。
- (手动转义)
<specifier>-map
属性:<specifier>-map
值的类型是:<prop-encoded-array>
描述:该属性将一个说明符域和一系列的父域桥接起来,并且描述了子域如何映射到它们各自的父域。
该映射表的每一行都由三个部分组成,
child specifier: 被映射的子节点的说明符
specifier parent: 是一个<phandle>
类型的值,用于指定子域被映射到的父域。
parent specifier: 父域
说明符映射的例子:
以下设备树文件说明了
soc {
soc_gpio1: gpio-controller1 {
#gpio-cells = <2>;
};
soc_gpio2: gpio-controller2 {
#gpio-cells = <2>;
};
};
connector: connector {
#gpio-cells = <2>;
gpio-map = <0 0 &soc_gpio1 1 0>,
<1 0 &soc_gpio2 4 0>,
<2 0 &soc_gpio1 3 0>,
<3 0 &soc_gpio2 2 0>;
gpio-map-mask = <0xf 0x0>;
gpio-map-pass-thru = <0x0 0x1>;
};
expansion_device {
reset-gpios = <&connector 2 GPIO_ACTIVE_LOW>;
};
对应具体的设备树,可以从linux的源码中查看相应的设备树的信息
linux/Documentation/devicetree/