Device Tree Usage(二)

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属性意味着一个设备不能被任何不是它父节点设备的设备直接访问。

 

 

未完待续。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值