Device Tree的用法

Device Tree 的用法

基本数据格式
Device tree是一种简单的节点和属性的树形结构。属性是键值对,而节点可能包括属性和子节点。例如,下面是.dts格式的树形结构:
/ {
    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 2 3 4>; /* each number (cell) is a uint32 */
        child-node1 {
        };
    };
};


此树形结构很明显的是毫无用处,因为它没描述任何东西,但它确实展示了节点属性的结构。主要有:
1   一个单独的root node:“/

2   一对子节点:“node1”和“node2

3   节点1的一对子节点:“child-node1”和”child-node2

4   分散于树形结构当中的一些属性
属性是简单的键值对,此处的值可以为空,也可以包括任意的字节流。当数据类型没有被编进数据结构时,会有一些基础数据表示法能够在device tree源文件中进行表达。

5  文本串可以用双引号表示
a  string-property = "a string"

6   单元格是由尖括号分隔的32 bit无符号整数
a  cell-property = <0xbeef 123 0xabcd1234>

7   二进制数据使用的是方
a  binary-property = [0x01 0x23 0x45 0x67];

8   不同示意的数据可以用逗号串联在一起
a mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

9   逗号也用来创建字符串列表
a  string-list = "red fish", "blue fish";


基本概念
要了解如何使用device tree,我们先从样机和创建一个device tree开始。

样机
以下为虚拟机,由”Acme”制造,名为“Coyote’s Revenge”。
1   一个32bit ARM CPU

2   附属于内存映射串行端口的处理器本地总线,spi总线控制器,i2c控制器,中断控制器和外总线桥

3   256MBSDRAM

4   基于0X101F10000X101F20002串行端口

5   基于0x101F3000GPIO控制器

6   基于0X1017000的拥有以下设备的SPI控制器
a  附属于GPIO #1的有SS pinMMC slot

7   拥有以下设备的外部总线桥
a  附属于基于0x101100000外总线的SMC91111以太网设备
b  拥有以下设备的基于0x10160000i2c控制器
c  Maxim DS1338 real time clock。响应slave address 1101000(0x58)

8  基于0x3000000064MBNOR flash


初始结构
第一步是为设备创建一个骨架结构。这是一具有效的device tree所需的最基本的结构。现在你想唯一的标识此设备。
/ {     compatible = "acme,coyotes-revenge"; };
C
ompatible指定了系统的名称。它包括字符串<manufacturer>,<model>。指定确切的设备是很重要的一点,并且包含制造商以避免命名空间冲突。操作系统将使用compatible值来决定如何在设备上运行,那么将正确数据加入属性中就显得非常重要。
理论上,compatibleOS所需的唯一指定设备的所有数据。如果所有设备资料都是硬编码,那么OS可以在高级compatible属性查找“acme,coyotes-revenge”。
高级中断映射

现在我们开始最有趣的部分,PCI中断映射。一个PCI设备能够使用#NTA,#NTB,#NTC和#NTD来触发中断。如果我们没有多功能PCI设备,就会有某个设备负责使用#NTA来进行中断。然而每个PCI插口或设备都会被连线到中断控制器的不同输入。所以device tree需要一种方式将每个PCI中断信号映射到中断控制器的输入。#interrupt-cells,interrupt-map和interrupt-map-mask属性用来描述中断映射。

事实上,这里描述的中断映射并不局限于PCI总线,任何节点都可以指定复杂的中断映射,但PCI是最常见的情况。

pci@0x10180000 {
            compatible = "arm,versatile-pci-hostbridge", "pci";
            reg = <0x10180000 0x1000>;
            interrupts = <8 0>;
            bus-ranges = <0 0>;

            #address-cells = <3>
            #size-cells = <2>;
            ranges = <0x42000000 0 0x80000000  0x80000000  0 0x20000000
                      0x02000000 0 0xa0000000  0xa0000000  0 0x10000000
                      0x01000000 0 0x00000000  0xb0000000  0 0x01000000>;

            #interrupt-cells = <1>;
            interrupt-map-mask = <0xf800 0 0 7>;
            interrupt-map = <0xc000 0 0 1 &intc  9 3 // 1st slot
                             0xc000 0 0 2 &intc 10 3
                             0xc000 0 0 3 &intc 11 3
                             0xc000 0 0 4 &intc 12 3

                             0xc800 0 0 1 &intc 10 3 // 2nd slot
                             0xc800 0 0 2 &intc 11 3
                             0xc800 0 0 3 &intc 12 3
                             0xc800 0 0 4 &intc  9 3>;
        };

首先你将注意到PCI中断编号只使用一个cell,与使用两个cell的系统中断控制器不同,一个是irq编号,另一个是为了标记。

在示例中,我们有两个4中断线的PCI插槽,所以我们需要将8个中断线映射到中断控制器。这是通过使用interrupt-map属性实现的。

因为中断编号不足以区分位于单独PCI总线的一些PCI设备,我们也需要指出是哪个PCI设备触发中断线。幸运的是我们可以使用每个PCI设备都有一个唯一设备编号。想要区分一些PCI设备的中断,我们需要一个元组,包括PCI设备编号和PCI中断编号。简单来了说,我们要构建一个拥有4 cell的单元中断说明符:
        三个#address-cells,包括phys.hi,phys.mid,phys.low,和
        一个#interrupt-cell(#INTA,#INTB,#INTC,#INTD)

因为我们只需要PCI地址的设备编号部分,此时可以使用interrupt-map-mask属性。Interrupt-map-mask也是4-元组,像单元中断说明符一样。在示例中可以看到我们只需要phys.hi设备编号部分,并且我们需要3 bit来区分四个中断线。

现在我们可以创建interrupt-map属性。此属性是一个表格,每个在此表格的输入包括子单元中断说明符,一个母handle和一个母单元中断说明符。所以在第一个可以读出PCI中断#INTA被映射到IRQ9。

单元中断说明符最重要的部分是源于phys.hi bit字段的设备编码。设备编码取决于每个PCI主机控制器是如何启用每个设备上的IDSEL pin的。在此示例中,PCI插槽被分配了设备id 24 (0x18),并且PCI插槽2被分配到了设备id 25 (0x19)。每个插槽的phys.hi值是由设备编码移位来了决定的,如下:
        为插槽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,主中断控制器的低敏感

最后需要注意的是,与interrupt-parent属性相似,一个节点的interrupt-map属性会为所有子节点改变默认中断控制器。在此PCI示例中,这意味着PCI主机桥会成为默认中断控制器。如果通过PCI总线附加上一个设备,而此设备与另外一个中断控制器直接相连的话,它仍然需要指定自己的interrupt-parent属性。
CPU
下一步是描述CPU。一个名为“cpus”的container node为每个CPU添加了一个子节点。在此情况下此系统为源于ARM的双核Cortex A9系统。

/ {
    compatible = "acme,coyotes-revenge";

    cpus {
        cpu@0 {
            compatible = "arm,cortex-a9";
        };
        cpu@1 {
            compatible = "arm,cortex-a9";
        };
    };
};

在每个cpu节点的兼容属性是字符串<manufacturer>,<model>,用以指定确切的cpu,就像是高级兼容属性一样。
此后会向cpu节点添加更多的属性,但我们首先要谈的是一些基本概念。

节点名称
我们值得花一些时间来谈论命名规范。每个节点都必须有一个名称,形式是<name>[@<unit-address>]。
<name>是一个简单的ascii字符串,长度可以达到31个字符。通常节点名称是依据它所代表的设备。例如一个3com以太网适配器应该使用的名称为ethernet,而不是3com509。
如果节点是用一个adress来描述设备的,那么就应该包括Unit-address。通常unit address是用来接入设备的原地址,并列入到节点的reg属性。我们将在下文提及reg属性。
同级节点必须有唯一名称,但是只要地址不同,不只一个节点会使用相同的属名(例如,serial@101f1000 & serial@101f2000)。

设备

系统中的每个设备都由一个device tree节点所代表。下一步是要为每个设备用节点填充树形结构。现在新节点将会为空,直到我们了解如何处理地址范围和终端请求。

/ {
    compatible = "acme,coyotes-revenge";

    cpus {
        cpu@0 {
            compatible = "arm,cortex-a9";
        };
        cpu@1 {
            compatible = "arm,cortex-a9";
        };
    };

    serial@101F0000 {
        compatible = "arm,pl011";
    };

    serial@101F2000 {
        compatible = "arm,pl011";
    };

    gpio@101F3000 {
        compatible = "arm,pl061";
    };

    interrupt-controller@10140000 {
        compatible = "arm,pl190";
    };

    spi@10115000 {
        compatible = "arm,pl022";
    };

    external-bus {
        ethernet@0,0 {
            compatible = "smc,smc91c111";
        };

        i2c@1,0 {
            compatible = "acme,a1234-i2c-bus";
            rtc@58 {
                compatible = "maxim,ds1338";
            };
        };

        flash@2,0 {
            compatible = "samsung,k8f1315ebm", "cfi-flash";
        };
    };
};

此树形结构中,在系统里为每个设备添加了一个节点,并且层级结构也反映出设备是如何与系统相关联的。例如外总线的设备是外总线节点的子节点,i2C设备是i2c总线控制器节点的子节点。通常系统的层级结构代表了CPU的视图。
此时这个树形结构是无效的。它缺失设备间相关联的信息。过后会将这些数据添加上去。需要注意的是:
        每个设备节点都有一个compatible属性
        Flash节点在兼容属性中有两个字符串。
        如前文所提,节点名称代表了设备类型,并不是详细型号。

了解compatible属性
树形结构中代表一个设备的每个节点都要求拥有compatible属性。Compatible是操作系统决定使用哪个设备驱动程序的关键。
Compatible是字符串列表。列表中第一个字符串“<manufacturer>,<model>”指定了节点代表的确切的设备。以下字符串代表了其它设备。
例如,位于芯片上的Freescale MPC8349系统拥有一个串行设备,此设备是National Semiconductor ns16550注册界面的工具。MPC8349串口设备的compatible属性应该为:compatible = "fsl,mpc8349-uart", "ns16550"。此时fsl,mpc8349-uart指定了确切的设备,ns16550表示与National Semiconductor 16550 UART兼容。
注:ns16550由于历史原因没有制造商前缀。所有新的compatible值都应该使用制造商前缀。
此练习允许已存在设备驱动程序绑定到设备上,但仍然是唯一指定确切的硬件。

注:不要使用wildcard compatible值,如:fsl,mpc83xx-uart或类似的。如果它没有及时进行变更,Silicon供应商的变更会打破你的wildcard假设。你可以选择一个特定的silicon执行程序并将所有后期的silicon与之相兼容。
寻址的运行


可寻址的设备是使用以下属性将地址信息编入device tree的:
n   Reg

n   #address-cells

n   #size-cells



每个可寻址设备都会得到一个reg,它是一个元组列表:reg = <address1 length1 [address2 length2] [address3 length3] ... >。每个元组代表设备的地址范围。每个address值是一个或多个被叫做cells的32bit整数的列表。类似的,长度值可以为cells列表或为空。


由于地址或长度字段都是可变的,母节点的#address-cells和#size-cells属性就表示在每个字段中有多少个cells。也就是说想要准确的解释一个reg属性则需要有母节点的#address-cells和#size-cells值。想要了解这些是如何运行的,让我们将寻址属性添加到device tree样本,从CPUs开始。


CPU寻址


当谈论到寻址是,CPU节点是最简单的例子。每个CPU都有一个单独且唯一的ID,并且没有size与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,它用无大小字段表示地址。在此情况下,这两个cpu分配到的地址为0和1。Cpu节点的#size-cells是0因为每个cpu只分配到了一个单独的地址。


你仍然需要注意reg值班需要与节点名的值相匹配。按照惯例,如果一个节点有一个reg属性,那么这个节点名称必须包括unit-address,这是reg属性的第一个address值。


内存映射设备


与在cpu节点中单独的address值不同,内存映射设备被分配了一系列将要响应的地址。#size-cells用来表示在每个子reg元组中长度字段的大小。在以下示例中,每个address值为1 cell(32 bits),每个长度值也是1 cell,这在32 bit系统是比较典型的。64 bit设备也许会为#address-cells和#size-cells使用数值2,在device tree中获取64 bit addressing。


/ {

    #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 >;

    };



    ...



};





非内存映射设备


处理器总线的其它设备为非内存映射设备。他们有地址范围,但不能被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>;

            };

        };
范围(地址转换)

我们已经讨论过如何向设备分配地址,但此时这些地址只是本地设备节点,还没有说明如何从那些地址里映射到cpu可以使用的地址。

根节点经常描述地址空间的CPU视图。根节点的子节点已经使用了CPU的address domain,所以不需要任何明确的映射。例如,serial@101f0000设备被直接分配了地址0x101f0000。

根节点的非直接子节点是无法使用CPU的address domain的。为了在deivce tree获取内存映射地址必须指定如何从一个域名将地址转换到另一个。Ranges属性就用于此目的。

以下是添加了ranges属性的device tree示例。

/ {
    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-cell值和子地址的#size-cells值而定。以外部总线为例,子地址是2 cells,母地址是1 cell,大小也为1 cell。转换三个ranges:

        Offset 0 from chip select 0 被映射到address range 0x10100000..0x1010ffff 
        Offset 0 from chip select 1被映射到address range 0x10160000..0x1016ffff 
        Offset 0 from chip select 2被映射到 address range 0x30000000..0x10000000

轮流的,如果母地址或子地址空间是唯一的,那么一个节点可以添加一个空ranges属性。一个空ranges属性的出现表示位于子地址空间的地址被1:1的映射到母地址空间。
你也许要问为什么当地址可以被1:1映射的时候还要使用地址转换。一些总线(如PCI)拥有完全不同的地址空间,而这些地址空间细节需要出现在操作系统。其它则拥有DMA驱动程序,这些程序需要在总线了解真正的地址。有时设备需要被集合,因为他们都分享相同的软件可编程物理地址映射。
你需要注意的是在i2c@1,0节点中没有ranges属性。原因是与外部总线不同,i2c总线的设备在cpu地址域名没有内存映射。而是cpu通过i2c@1,0设备间接访问rtc@58设备。缺少ranges属性意味着一个人设备除了线设备之外,不允许任何设备进行直接访问。

如何中断运行

与遵循自然树结构的地址范围转换不同,中断信号可以源自于或终止于一个机器的任何设备。与树结构中自然传递的设备地址不同,中断信号只在树结构中的独立节点间传递。四个属笥用于描述中断联接:
        Interrupt-controller-一个空属性表明一个节点作为一个接收中断信号的设备
        #interrupt-cell-这是中断控制器节点的一个属性。它代表此中断控制器的interrupt specifier有多少cells。
        Interrupts-包括interrupt specifier列表设备的一个属性,设备上每个中断输出信号都有一个。

一个interrupt specifier数据的是一个或多个cell,此数据表示此设备附属于哪个中断输入。大多数设备只有一个单独的中断输出,如下面示例,但设备上也可能有多个中断输出。一个中断说明符表达的意思完全取决于与中断控制器设备的捆绑。每个中断控制器都可以决定唯一识别中断输入的所需的cell量。

以下代码向我们Coyote’s Revenge样机添加了中断连接:

/ {
    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>;
        };
    };
};

注意事项:
        此样机拥有一个单独的中断控制器,interrupt-controller@10140000。
        “intc:”标签被添加到了中断控制器节点,并且用于向根节点的interrupt-parent属性分配一个phandle。此interrupt-parent值成了系统的默认值。
        每个设备都使用一个中断属性来指定一个不同的输入线
        #interrupt-cell是2,所以每个中断说明符拥有2个cell。
设备具体数据

除了通用的属性以外,任意属性和子节点都能添加到节点。任何操作系统需要的数据只要有一些规则就可以进行添加。
首先,新device-specific属性名称应该使用一个制造商前缀,这样它们才不会与已存在标准属性名称相冲突。
其次,属性和子节点代表的意义必须有绑定的说明,这样设备驱动程序开发者才能了解如何中断数据。一个绑定的文档能常包括特殊兼容值含义,应该有哪些属性,可能存在的子节点和代表的是什么设备。每个唯一的compatible值都应该有它自己的绑定文档。新设备的绑定文档应该在wiki。
第三,将新的绑定文档放在devicetree-discuss@lists.ozlabs.org邮件列表中。查看新的绑定文档来获取将来可能会发生的常见问题。


特殊节点

aliases节点
一个特定的节点通常由全路径引用,如/external-bus/ethernet@0,0,但当用户想知道具体内容的时候显得太累赘,“哪个设备是eth0?”。Aliases邛咪可以用来为设备全路径分本一个短的ALIAS。如:

aliases {
        ethernet0 = &eth0;
        serial0 = &serial0;
    };

当为设备分配一个标识符的时候,操作系统更倾向于使用aliases。
你会注意到这里使用的新句法。Property = &label;句法分配了由用作字符串属性的label引用的完整节点路径。这与phandle = < &label >不同;

chosen节点
chosen节点不代表一个真正的设备,但功能与在固件和操作系统间传递数据的地点一样,如根参数。位于chosen节点的数据不代表硬件。具有代表性的是在in.dts源文件中值为空。

在我们的示例系统中,固件也许添加了以下的chosen节点:

chosen {
        bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
    };

高级主题

高级样机

现在我们了解了基本定义,那么我们就向样机添加一些固件来讨论一些更加复杂的情况。
高级样机使用控制寄存器内存映射0X101180000来添加PCI主机桥,并执行BARs来启动以上的地址0x80000000。
给出我们已经了解的关于device tree的内容。我们就可以从以下附加的节点来描述PCI主机桥了。

pci@10180000 {
            compatible = "arm,versatile-pci-hostbridge", "pci";
            reg = <0x10180000 0x1000>;
            interrupts = <8 0>;
        };

PCI主机桥

此节描述了Host/PCI桥节点。
注意的是,一些关于PCI的基本知识。这不是关于PCI的指南,如果你需要一些更深入的信息,请读[1]。

PCI总线编号
每个PCI总线段都有唯一编号,并且总线编号是通过使用bus-ranges属性显示出来的,此属性包括2个cell。第一个cell会将分配到此节点的总线编号,第二个cell会给出任何一个PCI总线下级当中最高线线编号。
此样机拥有一个单独的pci总线,所以两个cell都为0。

pci@0x10180000 {
            compatible = "arm,versatile-pci-hostbridge", "pci";
            reg = <0x10180000 0x1000>;
            interrupts = <8 0>;
            bus-ranges = <0 0>;
        };

PCI地址转换

与之前讨论过的本地总线相似,PCI地址空间从CPU地址空间中完全分离出去了,所以需要从PCI址址到CPU地址进行地址转换。通常情况会使用rang,#address-cells和size-cells属性来完成。

pci@0x10180000 {
            compatible = "arm,versatile-pci-hostbridge", "pci";
            reg = <0x10180000 0x1000>;
            interrupts = <8 0>;
            bus-ranges = <0 0>;

            #address-cells = <3>
            #size-cells = <2>;
            ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
                      0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
                      0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
        };

正如你所看到的,子地址(PCI地址)使用了3个cell,PCI range被编入了2个cell。首先的问题可能是为什么我们需要3个32 bit cell来指定PCI地址。这三个cell被标记成phys.hi.mid和phys.low。
        phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr 
        phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh 
        phys.low cell: llllllll llllllll llllllll llllllll

PCI地址宽度是64 bi,并且都编入到phys.mid和phys.low。然而真正有趣的事是在1 bit字段的phys.high:
        n: 可再定址的地区标记(在这里无效)
        p: 可预取地址标记
        t: 别名地址标记(此处无效)
        ss: 空间代码
        00: 配置空间
        01: I/O空间
        10:32 bit 内存空间
        11:64 bit 内存空间
        Bbbbbbbb: PCI总线编号。PCI也许是分等级的结构。所以我们也许需要有PCI/PCI桥来定义子总线。
        Ddddd: 设备编号,通常与IDSEL信号连接相匹配。
        Fff: 功能编号。用于多功能PCI设备。
        Rrrrrrr: 寄存器编号,用于设置周期。

为了进行PCI地址转换,最重要的字段是p和ss。位于phys.hi的P和ss的值确定了访问哪个PCI地址空间。所以查看我们的ranges属性,我们有3个区域:
        一个32 bit可预取内存区域,它位于512MByte size的PCI地址0x80000000,并且将映射到主机CPU的地址0x80000000。
        一个32 bit非可预取内存区域,它位于256MByte size的PCI地址0xa0000000,并且将映射到主机CPU的地址0xa0000000。
        一个I/O区域,它位于16MByte size的PCI地址0x0000000,并且将会映射到主机CPU地址0x0000000。

此phys.hi位字段的存在表示一个操作系统需要知道节点代表了一个PCI桥,这样它才能忽略不相干的字段实现转换。一个OS将在PCI总线节点查询字符串“pci”来决定它是否需要掩饰额外的字段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DeviceTree 是一种描述硬件设备的树形数据结构,用于在嵌入式系统中进行硬件设备的配置和管理。它的设计目的是解决硬件平台不同、设备管理复杂的问题。在32位系统中,我们可以通过CSDN这个开发者社区来学习和探索 DeviceTree 的应用。 首先,CSDN(中国软件开发者社区)是一个专注于软件开发和技术分享的网站,其中包含丰富的开发教程和文章。在CSDN上,我们可以找到许多有关DeviceTree的教程和指南,这些教程可以帮助开发者了解如何使用DeviceTree来描述硬件设备。这些教程通常会介绍DeviceTree的基本语法和使用方法,并提供示例代码和明确的步骤,以便开发者能够快速开始使用DeviceTree。 在32位系统上,我们可以使用CSDN上的DeviceTree相关教程来学习如何编写和配置DeviceTree文件。需要注意的是,硬件平台可能会有不同的DeviceTree文件,因此我们需要根据实际硬件平台进行相应的配置。CSDN上的教程通常会提供如何查找和修改设备节点的相关信息,以及如何将DeviceTree文件与操作系统内核进行关联和编译。 另外,CSDN上还可以找到一些适用于32位系统的DeviceTree实际应用案例,例如如何描述和管理设备驱动、如何配置设备中断等。这些案例可以帮助开发者更好地理解如何应用DeviceTree来管理和配置硬件设备。 总之,通过在CSDN上搜索相关的DeviceTree教程和案例,我们可以在32位系统中学习和应用DeviceTree。这将帮助开发者更好地配置和管理硬件设备,提高系统的稳定性和可维护性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值