目录
- 1、设备树体系
- 1.1 DTS /DTSI / DTC / DTB
- 2、基础语法
- 2.1 节点语法
- 2.1.1 通用名称建议
- 2.2 属性语法
- 2.2.1 属性值
- 2.3 关于label
- 2.4 节点的[@unit-address] 与reg属性
- 2.5 根节点 /
- 2.6 标准属性compatible
- 2.6.1 of_machine_is_compatible函数
- 2.7 地址编码
- 2.7.1 标准属性#address-cells 与 #size-cells
- 2.7.2 标准属性reg
- 2.7.3 标准属性ranges (地址转换)
- 2.7.3.1 转载个实例:
- 2.7.4 dma-ranges
- 2.7.5 2.3.10. dma-coherent
- 2.7.6 name (deprecated)
- 2.7.7 device_type (deprecated)
- 2.8 中断连接
- 2.8.1 属性phandle
- 2.8.2中断产生设备的 属性interrupts
- 2.8.3 中断产生设备的属性interrupt-parent
- 2.8.4 中断产生设备的属性interrupts-extended
- 2.8.5 中断控制器属性#interrupt-cells
- 2.8.6 中断控制器属性 interrupt-controller
- 2.8.7 中断关系属性 interrupt-map
- 2.8.4 中断关系属性 interrupt-map-mask
- 2.8.5 中断关系属性 #interrupt-cells
- 2.8.6 中断映射示例
- 2.9 Nexus节点和说明符映射
- 2.9.1 <specifier>-map
- 2.9.2 <specifier>-map-mask
- 2.9.3 <specifier>-map-pass-thru
- 2.9.4. #<specifier>-cells
- 2.9.4 指定符映射示例
- 2.10 通用属性GPIO
- 2.10.1 GPIO控制器属性
- 2.10.1.1 gpio-controller
- 2.10.1.2 GPIO控制器属性 #gpio-cells
- 2.10.2 GPIO使用者属性
- 2.10.2.1 GPIO使用者节点xxx-gpio
- 2.11 status
- 3、通过一个样机进行实操
- 4、设备特殊数据
- 4.1 特殊节点
- 4.1.1 aliases 节点
- 4.1.2 chosen 节点
- 4.2 Advanced Topics 高级主题
- 4.2.1 Advanced Sample Machine 先进的样品机
- 4.2.2 PCI Host Bridge(PCI主机桥)
- 4.2.2.1 PCI总线编号
- 4.2.2.2 PCI地址转换
- 4.2.2.3 PCI DMA地址转换
- 4.2.3 高级中断映射
本章内容参考:https://elinux.org/Device_Tree_Usage
设备树是一种描述硬件的数据结构。
Bootloader会在运行最后,将这棵传递给内核,然后内核再将树里所刻画的资源绑定给展开的相应的设备。
1、设备树体系
设备树体系包括描述硬件信息的数据文件和对其进行编译的工具。
1.1 DTS /DTSI / DTC / DTB
.dts (device tree source): 是一个ASCII文本格式的设备树描述。在ARM平台对应的dts文件存放在arch/arm/boot/dts目录中。
.dtsi (device tree source include ):对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于C语言的方式可以include到dts文件中,对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置。
.dtb (device tree blob):文件.dtb是.dts被DTC编译后的二进制格式的设备树描述,可由Linux内核解析。
DTC(Devcie Tree Compiler):DTC是将.dts编译为.dtb的工具。DTC源代码位于内核的scripts/dtc目录中,编译内核时一并被编译出来
在Linux下,我们可以单独编译设备树文件。命令是在Linux内核目录内运行make dtbs。
可以在Ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工具
DTC还可以“反汇编”.dtb文件为.dts文件,指令为:
注意:可以到内核 的Documentation/devicetree/bindings目录下,找到对设备树中的节点和属性进行讲解的文本文档,以.txt为后缀名。
用以下的图来说明这些文件之间的关系:
2、基础语法
先展示一个.dts设备树的例子:
设备树.dts文件和.dtsi文件是由一系列被命名的节点(Node) 和 属性(Property) 组成,节点本身还可包含子节点。
而属性,就是成对出现的名称和值。
在设备树中,可描述的信息包括:
- CPU的数量和类别
- 内存基地址和大小
- 总线和桥
- 外设连接
- 中断控制器和中断使用情况
- GPIO控制器和GPIO使用情况
- 时钟控制器和时钟使用情况
因此接下来,就分别说明节点的语法和属性的语法。
2.1 节点语法
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
}
- “[]”表示option(可选项的意思),因此可以定义一个只有node name的空节点,
- label相当于node-name的别名,方便在dts文件中引用
- 每个节点必须有一个“<名称>[@<设备地址>]”形式的名字。如果该节点描述的设备有一个地址的话就还应该加上设备地址(unit-address)。通常,设备地址就是用来访问该设备的主地址,并且该地址也在节点的 reg 属性中列出。
- 多个相同类型设备节点的name可以一样,只要unit-address不同即可。
2.1.1 通用名称建议
节点的名称应该有点通用,反映设备的功能,而不是其精确的编程模型。如果合适,名称应为以下选项之一:
2.2 属性语法
[label:] property-name = value;
[label:] property-name;
- 属性可以无值
- 有值的属性
具体属性可以如下格式:“
补充:
如果是64位的值 0x1122334455667788 则需要拆分成两个32位的值来表示: <0x11223344 0x55667788>
2.2.1 属性值
值 | 说明 |
---|---|
<empty空> | 空值,无值 |
< u32 > | 大端模式的32位整数,例如0x11223344,存储为低位地址存入11,高位地址存入44 |
< u64 > | 以大端序格式表示64位整数。由两个<u32>值组成,其中第一个值包含整数的最高有效位,第二个值包含最低有效位。 <0x11223344 0x55667788>,大端序下,低位地址到高位地址分别存入 11 22 33 44 55 66 77 88 |
< string > | 字符串且以null结尾。示例:字符串“hello”在内存中表示为:从低位地址到高位地址分别存入‘h’ , ‘e’ , ‘l’ , ‘l’ , ‘o’ , ‘\0’ |
< phandle > | 值。phandle值是引用设备树中另一个节点的一种方式。任何可以引用的节点都定义了一个具有唯一<u32>值的phandle属性。 该数字用于具有phandle值类型的属性的值。 |
< stringlist > | 连接在一起的<string>值的列表。示例:字符串列表“hello”、“world”在内存中表示为: 从低位到高位地址分别存有如下字符: ‘h’ , ‘e’ , ‘l’ , ‘l’ , ‘o’ , ‘\0’ , ‘w’ , ‘o’ , ‘r’ , ‘l’ , ‘d’ , ‘\0’ |
2.3 关于label
如上2.1的节点语法,我们可以给一个设备节点添加label,之后就可以通过 &label的形式访问这个label。
如在linux 3.14内核中的/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi中有如下代码:
在同一个目录下的exynos4412-origen.dts有对该label的引用。
2.4 节点的[@unit-address] 与reg属性
1、对于挂 在内存空间的设备而言,@字符后跟的一般就是该设备在内存空间的基地址,如arch/arm/boot/dts/exynos4210.dtsi
在该节点的reg属性的起始地址(例中为0x02020000)与@后面的地址是一样的。
2、对于挂在IIC总线上的外设而言,@后面一般跟的是IIC从设轩的IIC地址,譬如arch/arm/boot/dts/exynos4210-trats.dts中的:
2.5 根节点 /
根节点”/“ 表示整个开发板的信息。所有其它设备节点都是它的后代,即它的子节点。
基下包含了一些直接属性:
属生名称 | 类型 | 说明 |
---|---|---|
#address-cells | u32 | 定义了在在子节点的reg属性中, 使用多少个u32整数来描述地址(address)。 |
#size-cells | u32 | 在子节点的reg属性中, 使用多少个u32整数来描述占用空间的大小(size)。 |
compatible | string list | 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备,即描述其兼容哪些平台。 |
model | string | 指定唯一标识,系统板型号,推荐格式为”制造商,型号“。比如有2款板子配置基本一致, 它们的compatible是一样的,那么就通过model来分辨这2款板子 |
2.6 标准属性compatible
Linux内核通过根节点“/”的兼容属性即可判断它启动的是什么设备。一般包括两个或者两个以上的兼容性字符串,首个兼容性字符串是板子级别的名字,后面一个兼容性是芯片极别的名字。
内核编程时一般使用of_machine_is_compatible()函数来比较某个设备的兼容性。
2.6.1 of_machine_is_compatible函数
声明在:include/linux/of.h
定义在:drivers/of/base.c
int of_machine_is_compatible(const char *compat)
它的工作是:
- 获取根设备节点(根路径为"/")
- 检查根设备节点的兼容性属性与传入的 compat 字符串是否匹配
- 如果匹配则返回 1,否则返回 0
那么它与某个具体的设备树文件是如何关联的呢?这是通过内核的编译和启动过程实现的。
具体来说:
4. 当编译一个内核时,需要指定一个设备树文件(通过DEVICE_TREE Makefile 变量),这个设备树文件会被编译到内核的二进制镜像中。
5. 当内核启动时,它会从二进制镜像中提取此设备树文件,并根据设备树文件初始化内核的设备树数据结构。
6. 此后,每当内核的任何部分需要访问设备树信息时,它都会从这棵已经初始化的内核设备树中获取信息。
7. of_machine_is_compatible() 函数就是通过检查内核已初始化的设备树与传入的兼容字符串是否匹配来实现其功能的。
8. 所以实际上,在内核编译和启动的整个过程中,隐式地将一个具体的设备树文件与内核相关联了。这个设备树文件就是内核要管理和访问的硬件设备信息的来源。
2.7 地址编码
2.7.1 标准属性#address-cells 与 #size-cells
- 父节点的#address-cells和#size-cells分别决定了子节点reg属性的address和length字段的长度,这个长度单位是uint32。
比如:根节点的#address-cells=<1>; #size-cells=<1>,则其下的serial、gpio、spi等节点的address和length字段的长库就分别是1.
如下:
2.7.2 标准属性reg
其中的每一组address length表明了设备使用的一个地址范围。address为1个或多个32位的整型,而length的意义则意味着从address到address+length-1的地址范围都属于该节点。若#size-cells=0 ,则length字段为空。
address和length字段是可变长的,父节点的#address-cells和#size-cells分别决定了子节点reg属性的address和length的长度。
特别的是如果是IIC节点,假设它的address字段为0x58,这是是该设备的I2C地址。
2.7.3 标准属性ranges (地址转换)
https://devicetree-specification.readthedocs.io
ranges的值可以是: <空值> 或者可以是如下的 三元组。
ranges <(child-bus-address parent-bus-address length) >
即:ranges<子总线地址、父总线地址、长度>
说明:
ranges属性提供了一种定义总线的地址空间(子地址空间)和总线节点的父地址空间(父地址空间)之间的映射或转换的方法。
ranges属性的值的格式是任意数量的三元组(子总线地址、父总线地址、长度)
"子总线地址"是子总线地址空间中的一个物理地址。表示地址的cell个数取决于总线,可以根据该节点(出现ranges属性的节点)的#address-cells 来确定。
"父总线地址"是父总线地址空间中的一个物理地址。表示父地址的cell个数取决于总线,可以根据定义父地址空间的节点的#address-cells属性来确定。
“长度”指定子级地址空间中范围的大小。表示大小的cell个数可以根据该节点(出现range属性的节点)的#size-cells来确定。
注意:
- 如果属性是用<空>值定义的,则它指定父地址空间和子地址空间相同,并且不需要地址转换。
- 如果总线节点中不存在该属性,则假定节点的子节点和父地址空间之间不存在映射。
soc节点指定的范围属性为 <0x0 0xe0000000 0x00100000>;
此属性值指定,对于1024 KB范围的地址空间,位于物理0x0的子节点映射到物理0xe000000的父地址。通过这种映射,serial 设备节点可以被加载或存储在位于0xe0004600的地址,偏移量为0x4600(在reg中指定)加上ranges中指定的0xe000000映射。
2.7.3.1 转载个实例:
转载于:https://www.cnblogs.com/pengdonglin137/p/7401049.html
一、设备树
1 / {
2 #address-cells = <1>;
3 #size-cells = <1>;
4
5 demo_level0 {
6 compatible = "simple-bus";
7 ranges = <0x0 0x3000000 0x3000>;
8 #address-cells = <1>;
9 #size-cells = <1>;
10
11 range@0 {
12 compatible = "range";
13 reg = <0x100 0x200>;
14 reg-names = "range0";
15 };
16
17 range@1 {
18 compatible = "range";
19 reg = <0x300 0x200>;
20 reg-names = "range1";
21 };
22
23 range@2 {
24 compatible = "range";
25 reg = <0x600 0x200>;
26 reg-names = "range2";
27 };
28
29 demo_level1 {
30 compatible = "simple-bus";
31 ranges = <0x0 0x1000 0x1000>;
32 #address-cells = <1>;
33 #size-cells = <1>;
34
35 range@3 {
36 compatible = "range";
37 reg = <0x100 0x200>;
38 reg-names = "range3";
39 };
40
41 demo_level1-1 {
42 compatible = "simple-bus";
43 ranges = <0x0 0x300 0x500>;
44 #address-cells = <1>;
45 #size-cells = <1>;
46
47 range@4 {
48 compatible = "range";
49 reg = <0x100 0x200>;
50 reg-names = "range4";
51 };
52
53 range@5 {
54 compatible = "range";
55 reg = <0x300 0x100>;
56 reg-names = "range5";
57 };
58
59 demo_level1-1-1 {
60 compatible = "simple-bus";
61 ranges = <0x0 0x400 0x100>;
62 #address-cells = <1>;
63 #size-cells = <1>;
64
65 range@6 {
66 compatible = "range";
67 reg = <0x50 0x30>;
68 reg-names = "range6";
69 };
70
71 demo_level1-1-1-1 {
72 compatible = "simple-bus";
73 ranges = <0x0 0x20 0x20>;
74 #address-cells = <1>;
75 #size-cells = <1>;
76
77 range@7 {
78 compatible = "range";
79 reg = <0x10 0x10>;
80 reg-names = "range7";
81 };
82
83 range@8 {
84 compatible = "range";
85 reg = <0x0 0x10>;
86 reg-names = "range8";
87 };
88 };
89 };
90 };
91
92 range@9 {
93 compatible = "range";
94 reg = <0x800 0x50>;
95 reg-names = "range9";
96 };
97
98 demo_level1-2 {
99 compatible = "simple-bus";
100 ranges = <0x0 0x900 0x100>;
101 #address-cells = <1>;
102 #size-cells = <1>;
103
104 range@10 {
105 compatible = "range";
106 reg = <0x0 0x50>;
107 reg-names = "range10";
108 };
109
110 demo_level1-2-1 {
111 compatible = "simple-bus";
112 ranges;
113 #address-cells = <1>;
114 #size-cells = <1>;
115
116 range@11 {
117 compatible = "range";
118 reg = <0x50 0x30>;
119 reg-names = "range11";
120 };
121 };
122 };
123 };
124
125 demo_level2 {
126 compatible = "simple-bus";
127 ranges;
128 #address-cells = <1>;
129 #size-cells = <1>;
130
131 range@12 {
132 compatible = "range";
133 reg = <0x2000 0x1000>;
134 reg-names = "range12";
135 };
136 };
137 }
138 };
二、驱动
下面是一个简单的驱动,功能很简单,只是在probe函数中将memory资源的start和(end+1)打印出来.
demo_range.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
static int demo_range_probe(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
printk(KERN_INFO "%s start: 0x%x, end: 0x%x\n",
res->name, res->start, res->end + 1);
return 0;
}
static int demo_range_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id demo_range_of_match[] = {
{ .compatible = "range"},
{},
};
static struct platform_driver demo_range_driver = {
.driver = {
.name = "demo_range",
.owner = THIS_MODULE,
.of_match_table = demo_range_of_match,
},
.probe = demo_range_probe,
.remove = demo_range_remove,
};
module_platform_driver(demo_range_driver);
MODULE_LICENSE("GPL v2");
在驱动中会获得memory资源,然后将start和 (end+1) 打印出来,之所以这里用(end+1),仅仅是为了便于理解下面的kernel log。
三、验证
编译驱动,然后加载,可以看到下面的打印信息:
[root@vexpress mnt]# insmod demo_range.ko
range0 start: 0x3000100, end: 0x3000300
range1 start: 0x3000300, end: 0x3000500
range2 start: 0x3000600, end: 0x3000800
range3 start: 0x3001100, end: 0x3001300
range4 start: 0x3001400, end: 0x3001600
range5 start: 0x3001600, end: 0x3001700
range6 start: 0x3001750, end: 0x3001780
range7 start: 0x3001730, end: 0x3001740
range8 start: 0x3001720, end: 0x3001730
range9 start: 0x3001800, end: 0x3001850
range10 start: 0x3001900, end: 0x3001950
range11 start: 0x3001950, end: 0x3001980
range12 start: 0x3002000, end: 0x3003000
总结:
1、ranges属性值的格式 <local地址, parent地址, size>, 表示将local地址向parent地址的转换。
比如对于#address-cells和#size-cells都为1的话,以<0x0 0x10 0x20>为例,表示将local的从0x0—(0x0 + 0x20)的地址空间映射到parent的0x10—(0x10 + 0x20)
其中,local地址的个数取决于当前含有ranges属性的节点的#address-cells属性的值,size取决于当前含有ranges属性的节点的#size-cells属性的值。
而parent地址的个数取决于当前含有ranges属性的节点的parent节点的#address-cells的值。
2、对于含有ranges属性的节点的子节点来说,其reg都是基于local地址的
3、ranges属性值为空的话,表示1:1映射
4、对于没有ranges属性的节点,代表不是memory map区域
四、示意图
对照上面的log理解下面的框图
2.7.4 dma-ranges
值类型:<empty>或编码为任意数量的<子总线地址、父总线地址、长度>三元组。
说明:
dma-ranges属性用于描述memory-mapped总线的直接内存访问(dma)结构,该总线的设备树父级可以通过源自该总线的dma操作进行访问。它提供了一种定义总线的物理地址空间和总线的父级的物理地址间隔之间的映射或转换的方法。
dma-ranges属性的值的格式是任意数量的三元组(子总线地址、父总线地址、长度)。每个指定的三元组描述一个连续的DMA地址范围。
- 子总线地址是子总线地址空间中的一个物理地址。表示地址的单元数取决于总线,并且可以根据该节点(出现dma-ranges属性的节点)的#address-cells来确定。
- 父总线地址是父总线地址空间中的一个物理地址。表示父地址的单元数取决于总线,可以根据定义父地址空间的节点的#address-cells属性来确定。
- 长度指定子级地址空间中范围的大小。可以根据该节点(出现dma范围属性的节点)的#size-cells来确定表示大小的单元数量。
2.7.5 2.3.10. dma-coherent
值类型:
说明:
对于默认情况下I/O非相干的体系结构,dma-coherent属性用于指示设备能够进行相干dma操作。某些体系结构默认情况下具有一致DMA,并且此属性不适用。
2.7.6 name (deprecated)
(已弃用)
值类型:<string>
说明:
name属性是一个指定节点名称的字符串。此属性已被弃用,不建议使用。但是,它可能用于较旧的不符合DTSpec的设备树。操作系统应根据节点名称的节点名称组件来确定节点名称。
2.7.7 device_type (deprecated)
(已弃用)
值类型:<string>
说明:
device_type属性在IEEE 1275中用于描述设备的FCode编程模型。由于DTSpec没有FCode,因此不赞成新使用该属性,并且为了与IEEE 1275-derived设备树兼容,该属性应仅包含在cpu和内存节点上。
2.8 中断连接
设备树中还可以包含中断连接信息,对于中断控制器而言,它提供如下属性:
interrupt-controller-
#interrupt-cells-
interrupt-parent-
interrupt-
在设备树中,存在一个逻辑中断树,它表示平台硬件中中断的层次结构和路由。
中断源到中断控制器的物理连接在设备树中用中断父属性表示。表示中断生成设备的节点包含一个中断父属性,该属性具有一个phandle值,该值指向设备的中断路由到的设备,通常是一个中断控制器。如果中断生成设备没有中断父属性,则假定其中断父是其设备树父。
每个中断生成设备都包含一个中断属性,该属性的值描述该设备的一个或多个中断源。每个源都用称为中断说明符的信息表示。中断说明符的格式和含义是特定于中断域的,即它取决于中断域根节点上的属性。中断域的根使用#interrupt-cells属性来定义编码中断说明符所需的值的数量。例如,对于Open PIC中断控制器,中断指定器采用两个32位值,并由中断编号和中断的级别/感测信息组成。
中断域是用于解释中断说明符的上下文。域的根是(1)中断控制器或(2)中断节点。
中断控制器是一种物理设备,需要一个驱动程序来处理通过它路由的中断。它也可能级联到另一个中断域。中断控制器由设备树中该节点上是否存在中断控制器属性来指定。
中断关系定义了一个中断域和另一个中断领域之间的转换。转换基于特定于域和特定于总线的信息。域之间的这种转换是使用interrupt-map属性执行的。例如,PCI控制器设备节点可以是中断节点,其定义从PCI中断名称空间(INTA、INTB等)到具有中断请求(IRQ)号的中断控制器的转换。
当中断树的遍历到达中断控制器节点而没有中断属性并且因此没有显式中断父节点时,才确定中断树的根。
2.8.1 属性phandle
值类型:
说明:
phandle属性为设备树中唯一的节点指定一个数字标识符。phandle属性值由需要引用与该属性关联的节点的其他节点使用。
例子:请参阅以下设备树摘录:
定义了一个phandle值1。另一个设备节点可以引用phandle值为1:
注意:
DTS中的大多数设备树将不包含显式phandle属性。当DTS被编译成二进制DTB格式时,DTC工具会自动插入phandle属性。
2.8.2中断产生设备的 属性interrupts
值类型:编码为任意数量的中断说明符
说明:
产生中断的设备节点,需申明该interrupts属性来指定所产生的中断号、触发方法等。而这个属性具体含有多少个cell,则由它依附的中断控制器节点的#interrupt-cells属性决定。而每个cell具体又是什么含义,一般由驱动的实现决定,这些一般会在设备树的绑定文档说明。
譬如,对于ARM GIC中断控制器而言,#interrupt-cells为3,3个cell的具体含义在Documentation/devicetree/bindings/arm/gic.txt中就有如下文字说明:
这样 某个ARM设备树的节点有如下属性:interrupt = <0 156 8>则表示该节点会产生一个SPI号为156的中断,其触发方式为低电平方式。
另:中断被interrupts-extended 属性覆盖,通常只应使用其中一个。
2.8.3 中断产生设备的属性interrupt-parent
值类型:
说明:
由于中断树中节点的层次结构可能与设备树不匹配,因此interrupt-parent属性可用于显式定义中断父属性。该值是中断父级的phandle。如果设备中缺少此属性,则假定其中断父级是其设备树父级。
2.8.4 中断产生设备的属性interrupts-extended
值类型:<phandle><编码数组>
说明:
interrupts-extended 属性列出了设备生成的中断。当一个设备连接到多个中断控制器时,应该使用interrupts-extended而不是interrupts,因为它用每个中断说明符编码一个父phandle。
例子:
此示例显示了具有两个连接到两个独立中断控制器的中断输出的设备如何使用一个interrupts-extended属性来描述连接。pic是一个中断控制器,它的#interrupt-cells说明符值为2,而gic是一个中断控制器,它的#interrupts-cells值为1。
interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;
interrupts 和 interrupts-extended 属性是互斥的。设备节点应该使用其中一个,但不能同时使用这两个。只有在需要与不理解interrupts-extended的软件兼容时,才允许使用两者。如果同时存在interrupts-extended 和 interrupts,则interrupts-extended 会优先。
2.8.5 中断控制器属性#interrupt-cells
值类型:
说明:
The #interrupt-cells property defines the number of cells required to encode an interrupt specifier for an interrupt domain.
#interrupt cells属性定义对中断域的中断说明符进行编码所需的单元数。
2.8.6 中断控制器属性 interrupt-controller
值类型:<空>
说明:
中断控制器属性的存在会将节点定义为中断控制器节点。
2.8.7 中断关系属性 interrupt-map
值类型:编码为任意数量的中断映射条目。
说明:
中断映射是连接节点上的一个属性,它将一个中断域与一组父中断域桥接,并指定子域中的中断说明符如何映射到各自的父域。
中断映射是一个表,其中每一行都是一个映射条目,由五个组件组成:
子单元地址(child unit address)、子中断说明符(child interrupt specifier)、父中断( interrupt-parent)、父单元地址(parent unit address)、父中断说明符(parent interrupt specifier)。
子单元地址(child unit address)
正在映射的子节点的单元地址。指定这一点所需的32位单元数由子节点所在的总线节点的#address-cells属性描述。
子中断说明符((child interrupt specifier))
正在映射的子节点的中断说明符。指定该组件所需的32位单元数由该节点的#interrupt-cells属性描述,该节点是包含中断映射属性的节点。
中断父级(interrupt-parent)
一个<phandle>值,指向子域映射到的中断父级。
父单元地址(parent unit address)
中断父级域中的单元地址。指定此地址所需的32位单元数由interrupt-parent字段指向的节点的#address-cells属性描述。
父中断说明符(parent interrupt specifier)
父域中的中断说明符。指定此组件所需的32位单元数由interrupt-parent字段所指向的节点的#interrupt-cells属性描述。
通过将unit-address/中断说明符对与interrupt-map中的子组件相匹配,在中断映射表上执行查找。由于单元中断说明符中的某些字段可能不相关,因此在查找完成之前会应用掩码。该掩码在interrupt-map-mask属性中定义。
注意
子节点和中断父节点都需要定义#address-cells和#interrupt-cells属性。如果不需要单元地址组件,则#address-cells应明确定义为零。
2.8.4 中断关系属性 interrupt-map-mask
值类型:编码为位掩码
说明:
interrupt-map-mask属性是为中断树中的nexus节点指定的。此属性指定一个掩码,该掩码与在interrupt-map属性中指定的表中查找的传入单元中断说明符进行“与”运算。
2.8.5 中断关系属性 #interrupt-cells
值类型:
说明:
#interrupt-cells属性定义对中断域的中断说明符进行编码所需的单元数。
2.8.6 中断映射示例
以下显示了具有PCI总线控制器的设备树片段的表示,以及用于描述两个PCI插槽(IDSEL 0x11,0x12)的中断路由的示例中断映射。插槽1和2的INTA、INTB、INTC和INTD引脚连接到Open PIC中断控制器。
表示一个Open PIC中断控制器,并将其标识为具有interrupt-controller属性的中断控制器。
interrupt-map表中的每一行都由五个部分组成:子单元地址 和 中断说明符,它被映射到具有指定父单元地址和中断说明符的interrupt-parent节点。
例如,中断映射表的第一行指定插槽1的INTA的映射。该行的组件显示在如下:
子单元地址 child unit address: 0x8800 0 0
子中断说明符 child interrupt specifier: 1
中断父级 interrupt parent: &open-pic
父单元地址 parent unit address: (为空 因为在open-pic节点中 #address-cells = <0> )
父中断说明符parent interrupt specifier: 2 1
子单元地址为<0x8800 0 0>。该值由三个32位单元编码,由PCI控制器的#address-cells属性的值(值为3)确定。这三个单元格代表PCI地址,正如PCI总线的绑定所描述的那样。编码包括总线编号(0x0 << 16)、设备编号(0x11 << 11)和 功能编号(0x0 << 8)。
子中断说明符是<1>,它指定PCI绑定所描述的INTA。这采用PCI控制器的#interrupt-cells属性(值为1)指定的一个32位单元,该属性是子中断域。
中断父级由一个phandle指定,该phandle指向插槽的中断父级,即Open PIC中断控制器。
父级没有单元地址,因为父级中断域(open-pic节点)的#address-cells值<0>。
父中断说明符是<2 1>。表示中断说明符的单元数(两个单元)由中断父节点(open-pic节点)上的#interrupt-cells属性决定。值<2 1>是由Open PIC中断控制器的设备绑定指定的值。值<2>指定INTA连接到的中断控制器上的物理中断源编号。值<1>指定级别/感知编码。
在本例中,interrupt-map-mask属性的值为<0xf800 0 7>。在interrupt-map表中执行查找之前,将此掩码应用于子单元中断说明符。
要查找IDSEL 0x12(插槽2)的INTB的open-pic中断源编号,函数0x3,将执行以下步骤:
子单元地址和中断说明符构成值<0x9300 0 0 2>。
地址的编码包括总线号(0x0<<16)、设备号(0x12<<11)和功能号(0x3<<8)。
中断说明符是2,这是根据PCI绑定对INTB进行的编码。
应用 interrupt-map-mask值<0xf800 0 0 7>,结果为<0x9000 0 0 2>。
该结果在interrupt-map表中查找,该表映射到父中断说明符<4 1>。
2.9 Nexus节点和说明符映射
nexus节点应具有#-cells属性,其中是一些说明符空间,如“gpio”、“clock”、“reset”等。
2.9.1 -map
值类型:编码为任意数量的说明符映射项。
说明:
<说明符>-映射是nexus节点中的一个属性,它将一个说明符域与一组父说明符域桥接,并描述子域中的说明符如何映射到各自的父域。
映射是一个表,其中每一行都是由三个组件组成的映射条目:子说明符(child specifier)、说明符父级(specifier parent) 和 父说明符(parent specifier)。
子说明符 child specifier
The specifier of the child node being mapped. The number of 32-bit cells required to specify this component is described by the #-cells property of this node—the nexus node containing the -map property.
“子说明符”为 要映射的子节点的说明符。指定此组件所需的32位单元格数由该节点的#-cells属性描述,该节点是包含-map属性的nexus节点。
说明符父级specifier parent
“说明符父级”是一个<phandle>值,指向子域映射到的说明符父级。
父说明符parent specifier
父域中的说明符。指定此组件所需的32位单元格数由说明符父节点的#-cells属性描述。
通过将说明符与映射中的子说明符进行匹配,可以对映射表执行查找。因为说明符中的某些字段可能不相关或需要修改,所以在完成查找之前会应用掩码。这个掩码是在<specifier>-map-mask属性中定义的。
类似地,当映射说明符时,单元说明符中的一些字段可能需要保持不变,并从子节点传递到父节点。在这种情况下,可以指定<specifier>-map-pass-thru属性,以将掩码应用于子说明符,并复制与父单元说明符匹配的任何位。
2.9.2 -map-mask
值类型:<prop encoded array>编码为位掩码
说明:
可以为nexus节点指定<specifier>-map-mask属性。此属性指定一个掩码,该掩码与在-map属性中指定的表中查找的子单元说明符进行AND运算。如果未指定此属性,则假定掩码是设置了所有位的掩码。
2.9.3 -map-pass-thru
值类型:<prop encoded array>编码为位掩码
说明:
可以为nexus节点指定<specifier>-map pass-thru属性。此属性指定一个掩码,该掩码应用于在-map属性中指定的表中查找的子单元说明符。子单元说明符中的任何匹配位都会复制到父说明符。如果未指定此属性,则假定掩码为未设置位的掩码。
2.9.4. #-cells
值类型:
说明:
#-cells属性定义对域的说明符进行编码所需的单元格数。
2.9.4 指定符映射示例
以下显示了具有两个GPIO控制器的设备树片段的表示,以及用于描述两个控制器上的几个GPIO通过板上的连接器到设备的GPIO路由的示例说明符映射。扩展设备节点位于连接器节点的一侧,带有两个GPIO控制器的SoC位于连接器的另一侧。
gpio-map映射表中的每一行都由三部分组成:子单元说明符,它被映射到具有父说明符的gpio-controller节点。
例如,specifier-map说明符映射表的第一行指定连接器的GPIO 0的映射。该行的组件显示在此处
子说明符child specifier: 0 0
说明符父级specifier parent: &soc_gpio1
父说明符parent specifier: 1 0
子说明符是<0 0>,它在标志字段为0的连接器中指定GPIO 0。这采用连接器节点的#gpio-cells属性指定的两个32位单元格,连接器节点是子说明符域。
说明符父级由一个phandle指定,该phandle指向连接器的说明符父级,即SoC中的第一个GPIO控制器。
父说明符是<1 0>。表示gpio说明符的单元格数(两个单元格)由说明符父节点soc_gpio1上的#gpio-cells属性决定。
值<1 0>是由GPIO控制器的设备绑定指定的值。值<1>指定GPIO控制器上的GPIO引脚编号,连接器上的GPI0连接到该GPIO控制器。值<0>指定标志(active low, active high等)。
在本例中,gpio-map-mask属性的值为<0xf 0>。在gpio映射表中执行查找之前,将此掩码应用于子单元说明符。类似地,gpio-map-pass-thru属性的值为<0x0 0x1>。将子单元说明符映射到父单元说明符时,会将此掩码应用于子单元说明符。此掩码中设置的任何位都会从父单元说明符中清除,并从子单元说明符复制到父单元说明符。
要从扩展设备的reset-gpios属性中查找GPIO 2的连接器说明符源编号,将执行以下步骤:
子说明符形成值<2 GPIO_ACTIVE_LOW>。
说明符正在为GPIO 2编码,每个GPIO绑定都有活动的低标志。
gpio-map-mask值<0xf 0x0>与子说明符进行“与”运算,得到<0x2 0>的结果。
结果在gpio映射表中查找,该表映射到父说明符<3 0>和&soc_gpio1 phandle。
gpio-map-pass-thru 值<0x0 0x1>被反转,并与gpio映射表中的父说明符进行“与”运算,结果是<3 0>。子说明符与gpio-map-pass-thru 掩码进行“与”运算,形成<0 gpio_ACTIVE_LOW>,然后与清除的父说明符<3 0>进行“或”运算,得到<3 gpio_ACTIVE_LOW>。
说明符<3 GPIO_ACTIVE_LOW>被附加到映射的phandle &soc_gpio1,从而产生<&soc_gpio1 3 GPIO_CTIVE_LOW]。
2.10 通用属性GPIO
在linux内核文档的里有个说明文档/Documentation/devicetree/bindings/gpio/gpio.txt。该文档有对gpio的常用的属性说明有:
2.10.1 GPIO控制器属性
每个GPIO控制器节点都必须有一个空的“GPIO controller”属性,并且具有包含GPIO说明符大小的#gpio-cells。
2.10.1.1 gpio-controller
gpio-controller,无值空属性,用来说明该节点描述的是一个gpio控制器
2.10.1.2 GPIO控制器属性 #gpio-cells
#gpio-cells = < n >,用来表示要用几个cell描述一个 GPIO引脚
举例
以下例子是用于描述用作芯片选择线的gpios引脚;以下相当于填充了芯片选择0、1和3,而芯片选择2为空:
请注意,gpio说明符的长度取决于控制器。在上面的例子中,&gpio1使用2个单元格来指定gpio,而&gpio2只使用一个。
gpio说明符可以按如下编码:bank,pin (bank中的引脚的位置),标志状态(引脚是否为漏极开路(open-drain)以及引脚是否为逻辑反相)
如:
在这一例中,gpio说明符为“18 0”,并编码gpio引脚号和空的gpio标志状态(因为从“qe_pio_e”控制器接受继承)。
特别说明,每个gpio说明符单元格的确切含义是特定于控制器的(即如上面两例中说明符的两种表示方式,不同平台这个说明符的表示方式是不一样的),这需要去设备树绑定(binding)中查看。
2.10.2 GPIO使用者属性
2.10.2.1 GPIO使用者节点xxx-gpio
对于GPIO使用者节点 gpio使用节点的属性
xxx-gpio = <&引用GPIO控制器 pin脚号 bank号(一般为0)>
这上说明符的格式是exynos4412中的格式,具体每个平台不太一样,需要根据各个平台的binding文件查阅
例如:
2.11 status
status =
3、通过一个样机进行实操
为了理解如何使用设备树,我们将从一个简单的机器开始,并建立一个设备树来逐步描述它。
样机的配置情况:
- 一个32bit 的 ARM CPU
- 连接到内存映射串行端口的处理器本地总线、spi总线控制器、i2c控制器、中断控制器和外部总线桥
- 基于0的256MB SDRAM
- 2个串行端口,基于0x101F1000和0x101F2000
- 基于0x101F3000的GPIO控制器
- 基于0x10170000的SPI控制器,带有以下设备
- 带SS引脚的MMC插槽连接到GPIO#1
- 带以下设备的外部总线桥
- SMC SMC91111以太网设备连接到基于0x10100000的外部总线
- 基于0x10160000的i2c控制器,带有以下设备
- Maxim DS1338实时时钟。响应从属地址1101000(0x58)
- 64MB NOR闪存,基于0x3000000
根据以上的设备配置信息,结果形成了以下的设备树:
- 系统中的每个设备都由一个设备树节点表示。在该树中,为系统中的每个设备添加了一个节点,层次结构反映了设备与系统的连接方式。即外部总线上的设备是外部总线节点的子节点,i2c设备是i2c总线控制器节点的子设备。一般来说,层次结构从CPU的角度表示系统的视图。节点名称反映的是设备的类型,而不是特定的型号。应尽可能使用的已定义通用节点名称的列表。
- 每个可寻址设备都会得到一个reg,父节点中的#address-cells和#size-cells属性用于说明每个字段中有多少单元格。
\qquad 在cpus节点中,#address-cells被设置为1,#size-cells被设置成0。这意味着子reg值是一个单独的uint32,表示没有大小字段的地址。在这种情况下,这两个cpu被分配了地址0和1,#cpu节点的大小单元为0,因为每个cpu只分配一个地址。
\qquad 同时reg值与节点名称中的值相匹配。按照惯例,如果节点具有reg属性,则节点名称必须包括单元地址,这是reg属性中的第一个地址值。
\qquad 内存映射设备被分配了一系列地址,它将对这些地址做出响应。#size-cells用于说明每个子reg元组中的长度字段有多大。在以下示例中,每个地址值是1个单元(32位),并且每个长度值也是1个单元,这在32位系统上是典型的。64位机器可以使用#address-cells和#size-cells的值2来获得设备树中的64位寻址。
\qquad 每个设备都被分配了一个基本地址,以及它被分配的区域的大小。该示例中的GPIO设备地址被分配了两个地址范围;0x101f3000…0x101f3ff和0x101f4000…0x101f400f。
\qquad 一些设备在总线上使用不同的寻址方案。例如,设备可以通过分立的芯片选择线连接到外部总线。由于每个父节点都为其子节点定义了寻址域,因此可以选择地址映射来最好地描述系统。下面的代码显示了连接到外部总线的设备的地址分配,其中芯片选择编号编码在地址中。
\qquad 外部总线使用2个单元作为地址值;一个用于芯片选择编号,一个用于从芯片选择的基极偏移。长度字段保持为单个单元格,因为只有地址的偏移部分需要有一个范围。因此,在本例中,每个reg条目包含3个单元格;芯片选择编号、偏移量和长度。
\qquad 由于地址域包含在节点及其子节点中,父节点可以自由定义对总线有意义的任何寻址方案。直接父节点和子节点之外的节点通常不必关心本地寻址域,并且必须映射地址才能从一个域获取到另一个域。
\qquad 其他设备没有在处理器总线上进行内存映射。它们可以有地址范围,但CPU不能直接访问它们。相反,父设备的驱动程序将代表CPU执行间接访问。以i2c设备为例,每个设备都被分配了一个地址,但没有与之相关的长度或范围。这看起来与CPU地址分配非常相似。 - 关于ranges(地址转换):
\qquad 为设备分配的地址,这些地址只是设备节点的本地地址。它还没有描述如何从这些地址映射到CPU可以使用的地址。
\qquad 根节点总是描述CPU对地址空间的视图。根的子节点已经在使用CPU的地址域,因此不需要任何显式映射。例如serial@101f0000设备被直接分配地址0x101f0000。
\qquad 不是根的直接子节点的节点不使用CPU的地址域。为了获得内存映射地址,设备树必须指定如何将地址从一个域转换到另一个域。range属性用于此目的。 - range是一个地址转换列表。范围表中的每个条目都是一个元组,包含子地址、父地址和子地址空间中区域的大小。每个字段的大小是通过取子字段的#address-cells值、父字段的#address-cells值和子字段的#size-cells值来确定的。对于我们示例中的外部总线,子地址是2个单元,父地址是1个单元,大小也是1个单元。正在翻译三个范围:
-
- 芯片选择0的偏移0被映射到地址范围0x10100000…0x1010ffff
-
- 芯片选择1的偏移0映射到地址范围0x10160000…0x1016ffff
-
- 芯片选择2的偏移量0映射到地址范围0x3000000…0x30fffff
\qquad 还应该注意到i2c@1,0节点。原因是与外部总线不同,i2c总线上的设备没有在CPU的地址域上进行内存映射。相反,CPU间接访问rtc@58设备通过i2c@1,0设备。缺少range属性意味着除了其父设备之外,任何设备都无法直接访问设备。
- 中断是如何工作的:
\qquad 与遵循树的自然结构的地址范围转换不同,中断信号可以从机器中的任何设备发出并终止。与在设备树中自然表达的设备寻址不同,中断信号被表达为独立于树的节点之间的链路。四个属性用于描述中断连接: -
- interrupt-controller-一个空属性,声明节点为接收中断信号的设备
-
- #interrupt-cells-这是中断控制器节点的一个属性。它说明此中断控制器的中断说明符中有多少单元(类似于#地址单元和#大小单元)。
-
- interrupt parent(中断父节点)-设备节点的一个属性,包含其所连接的中断控制器的phandle。没有中断父节点属性的节点也可以从其父节点继承该属性。
-
- interrupts(中断)-设备节点的一种属性,包含中断说明符列表,设备上的每个中断输出信号都有一个中断说明符。
\qquad 中断说明符是一个或多个数据单元(由#interrupt-cells指定),用于指定设备连接到哪个中断输入。如下面的示例所示,大多数设备只有一个中断输出,但设备上也可以有多个中断输出。中断说明符的含义完全取决于中断控制器设备的绑定。每个中断控制器可以决定需要多少单元来唯一地定义中断输入。
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};
external-bus {
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
4、设备特殊数据
\qquad
除了公共属性之外,还可以向节点添加任意属性和子节点。只要遵守一些规则,就可以添加操作系统所需的任何数据。
\qquad
首先,新的设备特定属性名称应该使用制造前缀,这样它们就不会与现有的标准属性名称冲突。
\qquad
其次,必须在绑定中记录属性和子节点的含义,以便设备驱动程序作者知道如何解释数据。绑定记录了特定兼容值的含义、它应该具有的属性、它可能具有的子节点以及它代表的设备。每个唯一的兼容值都应该有自己的绑定(或者声明与另一个兼容值的兼容性)。此wiki中记录了新设备的绑定。有关文档格式和审查过程的说明,请参见主页。
\qquad
第三,在devicetree-discuss@lists.ozlabs.org邮件列表。查看新绑定时会发现许多常见错误,这些错误将在将来引发问题。
4.1 特殊节点
4.1.1 aliases 节点
\qquad
特定节点通常由完整路径引用,如外部总线/ethernet@0,0,但当用户真正想知道的是“哪个设备是eth0?”时,这会变得很麻烦。别名节点可以用来为完整的设备路径分配一个短别名。例如:
注意到这里使用了一种新的语法。属性=&label;语法将标签引用的完整节点路径指定为字符串属性。这与phandle=<&label>不同;前面使用的表单,它在单元格中插入一个phandle值。
4.1.2 chosen 节点
所选节点并不代表真正的设备,而是作为固件和操作系统之间传递数据的地方,比如引导参数。所选节点中的数据并不代表硬件。通常,所选节点在.dts源文件中为空,并在启动时填充。
在我们的示例系统中,固件可能会将以下内容添加到所选节点:
4.2 Advanced Topics 高级主题
4.2.1 Advanced Sample Machine 先进的样品机
既然我们已经定义了基本知识,那么让我们在示例机中添加一些硬件来讨论一些更复杂的用例。
高级样本机添加了一个PCI主桥,控制寄存器内存映射到0x10180000,编程为从在地址0x80000000以上开始的BAR。
鉴于我们对设备树的了解,我们可以从添加以下节点开始描述PCI主桥。
4.2.2 PCI Host Bridge(PCI主机桥)
HOST/PCI桥接节点。
4.2.2.1 PCI总线编号
每个PCI总线段都是唯一编号的,并且通过使用bus-range属性在PCI节点中公开总线编号,该属性包含两个单元。第一个单元给出分配给这个节点的总线号,第二个单元给出任何从属该PCI总线的最大总线号。
样本机只有一条pci总线,因此两个单元都为0。
4.2.2.2 PCI地址转换
与前面描述的本地总线类似,PCI地址空间与CPU地址空间完全分离,因此需要从PCI地址到CPU地址的地址转换。与往常一样,这是使用ranges、#address-cells 和 #size-cells 属性来完成的。
如您所见,子地址(PCI地址)使用3个单元,PCI范围编码为2个单元。第一个问题可能是,为什么我们需要三个32位单元来指定PCI地址。这三个单元被标记为phys.hi、phys.mid和phys.low[2]。
PCI地址是64位宽的,编码为phys.mid和phys.low。然而,真正有趣的是phys.high,它是一个位字段:
- n: 可重定位区域标志(在此不起作用)
- p: 可预取(可缓存)区域标志
- t: 别名地址标志(此处不起作用)
- ss:空间码
-
- 00:配置空间
-
- 01: I/O空间
-
- 10: 32位内存空间
-
- 11: 64位内存空间
- bbbbbbbbb:PCI总线编号。PCI可以是分层结构的。因此,我们可能有PCI/PCI桥,它将定义子总线。
- ddddd:设备编号,通常与IDSEL信号连接相关。
- fff:函数编号。用于多功能PCI设备。
- rrrrrrrr:寄存器编号;用于配置周期。
为了进行PCI地址转换,重要的字段是p和ss。phys.hi中的p和ss的值决定访问哪个PCI地址空间。因此,从我们的ranges属性来看,我们有三个区域:
- 从512MByte大小的PCI地址0x80000000开始的32位可预取存储器区域,其将被映射到主机CPU上的地址0x8000000。
- 从256MByte大小的PCI地址0xa0000000开始的32位不可预取存储器区域,其将被映射到主机CPU上的地址0xa000000。
- 从16MB大小的PCI地址0x00000000开始的I/O区域,该I/O区域将被映射到主机CPU上的地址0xb0000000。
要想解决这个问题,phys.hi位字段的存在意味着操作系统需要知道节点代表PCI桥,这样它就可以忽略不相关的字段进行翻译。操作系统将在pci总线节点中查找字符串“pci”,以确定是否需要屏蔽额外的字段。
4.2.2.3 PCI DMA地址转换
上述ranges定义了CPU如何查看PCI内存,并帮助CPU设置正确的内存窗口,并将正确的参数写入各种PCI设备寄存器。这有时被称为出站内存(outbound memory)。
地址转换的一个特殊情况涉及PCI主机硬件如何查看系统的核心内存。当PCI主机控制器将充当主机并独立访问系统的核心存储器时,就会发生这种情况。由于这通常与CPU的视图不同(由于存储器线的布线方式),因此可能需要在初始化时将其编程到PCI主机控制器中。这被视为一种DMA,因为PCI总线独立地执行直接存储器访问,因此映射被命名为DMA范围。这种类型的内存映射有时被称为入站内存(inbound memory),并且不是PCI设备树规范的一部分。
在某些情况下,ROM(BIOS)或类似程序将在引导时设置这些寄存器,但在其他情况下,PCI控制器完全未初始化,需要从设备树中设置这些转换。PCI主机驱动程序通常会解析dma-ranges属性,并相应地在主机控制器中设置一些寄存器。
在以上示例的基础上进行扩展:
这个dma-ranges条目表明,从PCI主机控制器的角度来看,PCI地址0x00000000处的512 MB将出现在主核心存储器地址0x80000000处。正如您所看到的,我们只是将ss地址类型设置为0x02,表示这是一些32位内存。
4.2.3 高级中断映射
现在我们来看最有趣的部分,PCI中断映射。PCI设备可以使用导线#INTA、#INTB、#INTC和#INTD触发中断。中断名称前面的#hash符号意味着它是低电平活动,这是一种常见的约定,PCI中断线总是低电平活动。单个功能设备有义务使用#INTA进行中断。多功能设备如果使用单个中断引脚,则必须使用#INTA;如果使用两个中断引脚,必须使用#INTA和#INTB等。由于这些规则,#INTA通常被比#INTB、#INTC和#INTD更多的函数使用。为了在支持#INTA到#INTD的四条IRQ线路上分配负载,每个PCI插槽或设备通常以旋转方式连接到中断控制器上的不同输入,以避免所有#INTA客户端连接到同一输入中断线路。这个过程被称为冲洗中断( swizzling the interrupts)。因此,设备树需要一种将每个PCI中断信号映射到中断控制器的输入的方法。#interrupt-cells、interrupt-map和interrupt-map-mask属性用于描述中断映射。
实际上,这里描述的中断映射并不局限于PCI总线,任何节点都可以指定复杂的中断映射,但PCI情况是迄今为止最常见的。
首先,你会注意到PCI中断号只使用一个单元,而系统中断控制器使用两个单元;一个用于irq编号,一个用于标志。PCI只需要一个单元用于中断,因为PCI中断被指定为总是低级别敏感的( level-low sensitive.)。
在我们的示例板中,我们有2个PCI插槽,分别带有4条中断线,因此我们必须将8条中断线映射到中断控制器。这是使用interrupt-map属性完成的。中断映射的确切过程在前面进行了描述。
由于中断号(#INTA等)不足以区分单个PCI总线上的几个PCI设备,我们还必须指出哪个PCI设备触发了中断线。幸运的是,每个PCI设备都有一个唯一的设备编号,我们可以使用它。为了区分几个PCI设备的中断,我们需要一个由PCI设备号和PCI中断号组成的元组。更一般地说,我们构造了一个单元中断说明符,它有四个单元:
- 三个#address-cells,包括phys.hi、phys.mid、phys.low和
- 一个#中断单元(#INTA、#INTB、#INTC、#INTD)。
因为我们只需要PCI地址的设备编号部分,所以interrupt-map-mask属性就发挥了作用。interrupt-map-mask也是一个类似于单元中断说明符的4元组。掩码中的1表示应考虑单元中断说明符的哪一部分。在我们的例子中,我们可以看到,只需要phys.hi的设备编号部分,并且我们需要3bit来区分四条中断线(计数PCI中断线从1开始,而不是从0开始!)。
现在我们可以构造interrupt-map属性了。此属性是一个表,该表中的每个条目都由一个子(PCI总线)单元中断说明符、一个父句柄(负责服务中断的中断控制器)和一个父单元中断说明符组成。因此,在第一行中,我们可以看到PCI中断#INTA被映射到IRQ 9上,这是我们中断控制器的低敏感级别( level low sensitive)。
目前唯一缺少的部分是PCI总线单元中断说明符中的奇怪数字。单元中断说明符的重要部分是phys.hi位字段中的设备编号。设备编号是特定于板的,它取决于每个PCI主机控制器如何激活每个设备上的IDSEL引脚。在本例中,PCI插槽1被分配设备id 24(0x18),PCI插槽2被分配设备id 25(0x19)。每个时隙的phys.hi的值是通过将设备号向上移位11位到比特字段的ddddd部分来确定的,如下所示:
- 插槽1的phys.hi为0xC000,并且
- 插槽2的phys.hi为0xC800。
将所有这些放在一起,interrupt-map属性显示:
- #插槽1的INTA为IRQ9,在主中断控制器上为低电平敏感
- #插槽1的INTB为IRQ10,在主中断控制器上为低电平敏感
- #插槽1的INTC为IRQ11,在主中断控制器上为低电平敏感
- #插槽1的INTD为IRQ12,在主中断控制器上为低电平敏感
和 - #插槽2的INTA为IRQ10,在主中断控制器上为低电平敏感
- #插槽2的INTB为IRQ11,在主中断控制器上为低电平敏感
- #插槽2的INTC为IRQ12,在主中断控制器上为低电平敏感
- #插槽2的INTD为IRQ9,在主中断控制器上为低电平敏感
interrupts =<8 0>;属性描述了主机/PCI桥接控制器本身可能触发的中断。不要将这些中断与PCI设备可能触发的中断混淆(使用INTA、INTB…)。
最后要注意的一件事。就像interrupt-parent属性一样,节点上存在interrupt-map属性将更改所有子节点和孙子节点的默认中断控制器。在这个PCI示例中,这意味着PCI主桥成为默认的中断控制器。如果通过PCI总线连接的设备直接连接到另一个中断控制器,那么它还需要指定自己的中断父属性。