How Addressing Works
可寻址的设备使用以下属性将地址信息编码进设备树:
- reg
- #address-cells
- #size-cells
每个可寻址的设备都有一个 reg,它是一个元组列表,形式上 reg = <address1 length1 [address2 length2] [address3 length3] ...>。每个元组表示设备使用的地址范围。每个地址值是一个或多个名为 cells的32位整数的列表。类似的,长度值既可以是cells 列表,也可以为空。
由于地址和长度字段都是可变大小的变量,所以父节点中的 #address-cells 和 #size-cells 属性被用来表示每个字段有多少个cells。或者换句话说,正确的解释一个 reg 属性需要父节点的 #address-cells 和 #size-cells 值。为了了解这一切如何工作,我们可以将寻址属性添加到样例设备树中,从CPUs开始。
CPU addressing
当讨论寻址时,CPU节点代表了最简单的情况。每个CPU被分配一个唯一的ID,并且没有与CPU ids相关联的大小。
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
在cpus节点中, #address-cells 设置成1,#size-cells 设置成0。这意味着子节点中 reg 值是一个单独的uint32,表示没有大小字段的地址。这种情况下,两个cpus被分配为地址0 和 1。对于cpu 节点来说,#size-cells为0是因为每个cpu只会被分配一个地址。
你也将注意到 reg 值与节点名中的值相匹配。按照惯例,假如节点有一个reg属性,那么节点名必须包含 unit-address,这是reg属性中的第一个地址值。
Memory Mapped Devices
与在cpu节点中找到的单个地址值不同,内存映射设备被分配一段地址范围,并将响应这些地址。#size-cells 用于说明每个子reg元组中长度字段的大小。在下面的例子中,每个地址值是 1 cell (32 bits),每个长度值也是1 cell,这在32位系统上是典型的。64位机器可以使用2的值来处理 #address-cells 和 #size-cells,以便在设备树中获得64位寻址。
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
...
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};
interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
};
...
};
每个设备都被分配一个基地址,以及它所分配的区域的大小。本例中的GPIO设备地址分配了两个地址范围;0x101f3000...0x101f3fff 和 0x101f4000...0x101f400f。
一些设备存在在不同寻址方案的总线上。例如,一个设备可以附加到一个带有离散芯片选择线的外部总线上。由于每个父节点为其子节点定义寻址域,所以可以选择地址映射来最好的描述系统。下面的代码显示了连接到外部总线的设备的地址分配,并将芯片选择号编码进地址。
external-bus {
#address-cells = <2>;
#size-cells = <1>;
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
external-bus 使用2个cells来处理地址值;一个用于芯片选择号,另一个用于芯片选择基地址的偏移。长度字段仍然是一个单独的cell,因为只有地址的偏移部分需要有一个范围。因此,在本例中,每个reg条目包含3个cells;芯片选择号,偏移量,和长度。
由于地址域被包含在一个节点及其子节点上,因此父节点可以自由定义任何对总线有意的寻址方案。父节点和子节点之外的节点通常不需要关心本地寻址域,并且地址必须从一个域映射到另一个域。
Non Memory Mapped Devices
其他设备不在处理器总线上映射的内存。它们可以有地址范围,但它们不能被CPU直接访问。相反,父设备的驱动程序将代表CPU执行间接访问。
以i2c设备为例,每个设备分配一个地址,但没有与之相关联的长度和范围。这看起来和CPU地址分配差不多。
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};
Ranges (Address Translation)
我们已经讨论如何分配地址给设备,但是在这一点上,这些地址仅仅是设备节点的本地地址。它还没有描述如何将这些地址映射到CPU可以使用的地址。
根节点总是描述CPU对地址空间的视图。根节点的子节点已经在使用CPU的地址域,因此不需要任何显式的映射。例如,serial@101f0000 设备直接分配地址0x101f0000。
不是root直接子节点的节点不使用CPU的地址域。为了得到内存映射地址,设备树必须指定如何将地址从一个域转换到另一个域。 ranges 属性用于此目的。
这是一个带有ranges属性添加的样本设备树。
/dts-v1/;
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
...
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>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
ranges 是一个地址转换列表。在ranges 表中每个条目都是一个包含子地址,父地址和子地址空间中区域大小的元组。每个字段大小取决于子节点的#address-cells值,父节点的#address-cells值,和子节点的#size-cells值。在我们的例子中,对外部总线来说,子地址是2 cells,父地址是1 cell,并且大小也是1 cell。三个范围被转换:
- 芯片选择0偏移量0被映射的地址范围是 0x10100000...0x1010FFFF
- 芯片选择1偏移量0被映射的地址范围是 0x10160000...0x1016FFFF
- 芯片选择2偏移量0被映射的地址范围是 0x30000000...0x30FFFFFF
另外,假如父地址空间和子地址空间是相同的,那么节点可以增加一个空的ranges属性代替。一个空的ranges属性存在意味着在子地址空间的地址被1:1映射到父地址空间。
你可能会问为什么要用1:1映射来编写地址转换。一些总线(如PCI)具有完全不同的地址空间,其细节需要暴露在操作系统中。其他的有DMA引擎需要总线上的真实地址。有时设备需要被组合在一起,因为它们都共享相同的软件可编程物理地址映射。是否应该使用1:1映射很大程度依赖于操作系统所需要的信息,已经硬件设计。
你也应该注意到在i2c@1,0 节点中没有ranges 属性。这样做的原因是,跟外部总线不同,i2c总线上的设备不是在CPU地址域上映射的内存。相反,CPU是通过i2c@1,0 设备间接访问rtc@58设备。缺少一个ranges属性意味着一个设备不能被任何不是它父节点设备的设备直接访问。
未完待续。。。。