Home 设备树基本用法
Post
Cancel

设备树基本用法

设备树用法

基本数据格式

树状结构,包含大量节点和属性,属性是key-value键值对,节点中可以包含各种属性和子节点。源文件以 .dts为后缀名。example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/dts-v1/;

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        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 {
        };
    };
};

上例中仅是用法示例,并没有和硬件有关联,主要展示了节点结构和属性。包括以下:

  • 一个根节点: “/”
  • 一系列子节点(根节点下): “node1” and “node2”
  • 子节点的子节点(node1节点下): “child-node1” and “child-node2”
  • 各个节点下的大量属性描述

属性是简单的 key-value 键值对形式,其中value值可以是空的,或是包含任意的字节流。虽然数据类型没有编码到数据结构中, 但是有一些基本的数据表示可以在设备树源文件中表示。

文本字符串
文本字符串(以null结尾)使用双引号表示,

1
2
3
4
5
string-property = "a string";

model = "Xunlong Orange Pi PC";

clock-names = "apb_pclk";

cells
cells,cells是32位的无符号整数(unsigned int)的特定集合,使用尖括号包围,如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cell-property = <0xbeef 123 0xabcd1234>;

interrupt-parent = <&gic>;

#address-cells = <1>;
#size-cells = <1>;
reg = <0x60000000 0x40000000>;

max-memory-bandwidth = <95000000>;

interrupt-map = <0 0 &gic 0 36 4>,
                <0 1 &gic 0 37 4>,
                <0 2 &gic 0 38 4>,
                <0 3 &gic 0 39 4>;

有各种cells,这里尖括号< >包围的是值,值是有一个个32位无符号数组成,而cell属性的名称是可以自定义的, 不过有一些是特定的特殊的,如reg。另外在cells的值中,可以混用十进制和十六进制。

对于这个cells的理解,应该是 一对 < > 就是一个cell,即一组特定的数据,而 < > 里面的具体有几个值是其他地方决定的, 不同的场景需要的值数量不同,对于cell而言,不用太关心数量,而是将< >里面的值视为一个整体。

二进制数据
纯二进制数据用方括号分隔:

1
2
3
4
5
6
Binary-property = [0x01 0x23 0x45 0x67];

invensense,key = [4e cc 7e eb f6 1e 35 22 00 34 0d 65 32 e9 94 89];

local-mac-address = [00 0a 35 15 01 50];

不同表示形式的数据可以用逗号连接在一起

1
mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

逗号还可以用来创建字符串列表

1
2
3
string-list = "red fish", "blue fish";

compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";

节点追加或修改

可以通过标签对已有的节点进行内容的追加或修改。这个是设备树中比较常见。如有网卡节点

1
2
3
4
5
6
7
8
9
10
11
12
        gem0: ethernet@ff0c0000 {
                compatible = "cdns,versal-gem";
                status = "disabled";
                reg = <0 0xff0c0000 0 0x1000>;
                interrupts = <0 56 4>, <0 56 4>;
                clock-names = "pclk", "hclk", "tx_clk", "rx_clk", "tsu_clk";
                #stream-id-cells = <1>;
                /* iommus = <&smmu 0x234>; */
                /* dma-coherent; */
                #address-cells = <1>;
                #size-cells = <0>;
        };

则可以通过如下形式,在后文或者其他包含它的文件中对节点追加,可以多次追加,顺序类似于C的预处理机制。

1
2
3
4
5
6
7
8
9
10
11
// A文件追加内容
&gem0 {
        phy-mode = "rgmii-id";
        status = "okay";
};

// B文件追加内容
&gem0 {
        local-mac-address = [00 0a 35 15 01 50];
};

基本概念

基本结构

第一步是确定机器的平台,这是有效设备树所需的最小结构。在这个阶段,需要唯一地标识机器。

1
2
3
4
5
6
7
8
9
10
/dts-v1/;

/ {
    compatible = "acme,coyotes-revenge";
    //compatible = "acme,ariettag25", "atmel,at91sam9x5", "atmel,at91sam9";
    //
    //compatible = "xunlong,orangepi-pc", "allwinner,sun8i-h3";
    //compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";
    //compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
};

Compatible指定系统的名称。它包含一个<manufacturer>,<model>形式的字符串。重要的是要指定确切的设备,并包括制造商名称,以避免名称空间冲突。 由于操作系统将使用compatible值来决定如何在机器上运行,因此将正确的数据放入该属性非常重要。

CPUs

接下来要对CPU进行描述,有一个特殊的cpus容器节点来负责描述它,可以在该容器节点中为每个CPU添加一个子节点。如以下示例中是一个ARM的双核Cortex A9系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/dts-v1/;

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

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

每个cpu节点中的compatible属性是一个字符串,它以<manufacturer>、<model>的形式指定确切的cpu型号,就像顶层的compatible属性一样。 更多cpus节点的属性参考:devicetree-specification-v0.4-rc1.pdf

节点名称

设备树里有大量的nodes,它们的命名也是有形式的。按照规范,每个节点必须有一个形式为<name>[@<unit-address>]的名称。

<name>是一个简单的ASCII字符串,长度不超过31个字符一般来说,节点是根据它所代表的设备类型来命名的,即通用名称。如假设有一个网卡 3Com公司的3C90x,那么在设备树中应当使用 ethernet 而不是3Com90x来命名。所以,可以看到一些容易理解的node名称,如mmc,emac,uart, usb等等

[@<unit-address>]是可选的,这个叫单元地址。如果一个节点是描述某个地址上的设备的,那么可以带上,一般用来描述该设备的访问主地址, 而该设备的所有地址列表具体写在reg属性中,这里名称中的单元地址就是放主地址,一般就是起始地址。

如果有兄弟节点,兄弟节点必须各自使用唯一的命名,因为有相同设备的情况也比较多,所以需要使用单元地址区分,比如有两个串口,那么就可以这样描述 serial@101f1000 和 serial@101f2000,多个节点使用相同的通用名称是很常见的。 兄弟节点必须唯一命名,但只要地址不同(例如serial@101f1000和serial@101f2000),多个节点使用相同的通用名称是正常的。

关于通用名称,也比较多,具体可以参考 devicetree-specification-v0.4-rc1.pdf 的章节 Generic Names Recommendation,如 adc,bus,can,clock,cpus,gpio,i2c,.......,使用通用名称,主要是为了规范,也不是必须的。

Devices

设备树的主要内容就是大量的设备树节点,是这些节点组成了树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/dts-v1/;

/ {
    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属性。
  • 如前所述,节点名称反映设备的类型,而不是特定的型号。

compatible属性理解

设备树中表示设备的节点都必须具有compatible属性compatible是操作系统用来决定将哪个设备驱动程序绑定到设备的关键。

compatible的值是一个字符串列表。列表中的第一个字符串以<manufacturer>,<model>的形式指定节点所表示的确切设备。后面的字符串表示该设备兼容的其他设备。 如compatible = "m25p80","jedec,spi-nor";

1
2
3
compatible = "xlnx,versal-8.9a","arasan,sdhci-8.9a";

compatible = "xlnx,versal-ospi-1.0","cadence,qspi","cdns,qspi-nor";

有时有无厂商字符串的,一般是历史原因。所有新的兼容值都应该使用制造商前缀。

这种做法允许将现有设备驱动程序绑定到新设备,同时仍然唯一地标识确切的硬件。

另外,不要使用模糊的compatible值,如“fsl,mpc83xx-uart”或类似的值,应该具体写明。如果有兼容的,作为兼容值写在后面列表上。

寻址表达

可寻址的设备使用以下属性将地址信息编码到设备树中:这三个属性也是特殊的

  • reg
  • #address-cells
  • #size-cells

每个可寻址设备有一个reg属性,这是一个元组列表,形式为 reg = < address1 length1 [address2 length2] [address3 length3] ... >, 这里的一个 address length就是一个元组,每个元组表示设备使用的地址范围。每个地址值是一个或多个32位整数的列表,称为cells,长度值也是。 由于地址和长度字段都是可变大小的变量,因此使用父节点中的#address-cells#size-cells属性来声明每个字段中有多少个单元格。或者换句话说, 正确解释reg属性需要父节点的#address-cells#size-cells值。

CPU寻址

CPU是最简单的情况,每个CPU被分配一个唯一的ID,并且没有size指定大小。

1
2
3
4
5
6
7
8
9
10
11
12
    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        cpu@0 {
            compatible = "arm,cortex-a9";
            reg = <0>;
        };
        cpu@1 {
            compatible = "arm,cortex-a9";
            reg = <1>;
        };
    };

在cpu节点中,#address-cells设置为1,#size-cells设置为0。这意味着子reg值是一个单独的uint32,表示没有大小字段的地址。 在本例中,两个cpu分别分配了地址0和1。对于CPU节点,#size-cells为0,因为每个CPU只分配一个地址。 reg值与节点名称中的值相匹配。按照惯例,如果节点具有reg属性,则节点名称必须包含单元地址,一般就是reg属性中的第一个地址值。

Memory Mapped Devices

内存映射设备被分配了一个地址范围。#size-cells用于声明每个子reg元组中的长度字段有多大。在下面的示例中,每个地址值是1个单元(32位), 每个长度值也是1个单元,这在32位系统中是典型的。64位机器可以使用#address-cells和#size-cells的值2来在设备树中获得64位寻址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/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 >;
    };

    ...

};

每个设备被分配一个基址,以及它被分配的区域的大小。

有些存在于总线上的设备使用不同的寻址方案。例如,一个设备可以通过片选线连接到一个外部总线。 由于每个父节点为其子节点定义寻址域,因此可以选择地址映射来最好地描述系统。 下例显示了连接到外部总线的设备的地址分配,CS片选号被编码到地址中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    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>;
        };
    };

上例中外部总线使用2个cell作为地址长度:第一个值用于芯片CS片选号,第二个用于相对片选号的偏移量。长度字段保持为单个cell。 因此,在本例中,每个reg条目包含3个cell;CS片选号,偏移量和长度。

由于地址域包含在节点及其子节点中,因此父节点可以自由地定义对总线有意义的任何寻址方案。

Non Memory Mapped Devices

有的设备在处理器总线上没有内存映射,它们就不能被CPU直接访问,但是它们一样也可以有自己的地址范围,并由父设备的驱动程序来 代表CPU进行间接访问。典型的,比如I2C设备,每个设备都分配了一个地址,但没有与之关联的长度或范围。这看起来与CPU地址分配非常相似。

1
2
3
4
5
6
7
8
9
10
      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>;
          };
      };

上例中的rtc@58就是,定义了地址,它内部是有地址范围的,但没有映射到CPU总线上,通过父设备I2C控制来间接访问。

Ranges (Address Translation)

上文记录了如何给设备分配地址,但是到目前为止,那些地址仅仅属于是设备节点的本地地址。如何从父设备节点下的本地地址map到CPU可用的地址? 根节点(root node)总是描述CPU的地址空间视图。根节点的子节点已经在使用CPU的地址域,因此不需要任何显式映射。 例如,前面示例中的serial@101f0000设备分配的地址就是CPU可用地址0x101f0000

然而,那些不是根节点的直接子节点的的设备节点并不使用CPU的地址域。如果要获得CPU视角的内存映射地址,设备树必须指定如何将地址从一个域转换到另一个域。 ranges属性用于此目的。添加ranges属性后的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/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属性由地址转换列表组成。每个入口都是一个元组,包含子节点地址,父节点地址,子节点地址映射大小。 子节点地址和父节点地址的cell大小参考各自父节点指定的#address-cells大小,而子节点地址映射大小则是根据子节点的#size-cells。 大致就是 from [child addr] to [parent(cpu) addr] by [child size]。 对于上例的external bus,先是子节点地址,地址cells大小是2,定义在external-bus下,父节点,也就是映射到CPU视角的地址范围,地址cells大小是1, 最后长度cells,也是1。所以最后的映射效果是:

Offset 0 from chip select 0 is mapped to address range 0x10100000..0x1010ffff
Offset 0 from chip select 1 is mapped to address range 0x10160000..0x1016ffff
Offset 0 from chip select 2 is mapped to address range 0x30000000..0x30ffffff

此外,如果父节点和子节点的地址空间是相同的,即一一对应映射,那么ranges可以省略,省略就表示子节点的地址空间一一映射到父节点地址空间。

既然可以使用1:1映射编写地址转换,为什么还要使用地址转换?有些总线(如PCI)具有完全不同的地址空间,其详细信息需要向操作系统公开。 其他的DMA引擎需要知道总线上的真实地址。有时设备需要分组在一起,因为它们都共享相同的软件可编程物理地址映射。 是否应该使用1:1映射在很大程度上取决于操作系统所需的信息和硬件设计。

另外,注意,i2c@1,0节点中没有ranges属性。这样做的原因是,与外部总线不同,i2c总线上的设备没有内存映射到CPU的地址域。 相反,CPU通过i2c@1,0设备间接访问rtc@58设备。缺少ranges属性意味着一个设备不能被它的父设备以外的任何设备直接访问。

中断表达

中断信号比较特殊,它可以起源和终止于机器中的任何设备,和设备树中自然表达的设备寻址不同,中断信号表达为节点之间的连接,独立于树。 主要有四个属性用于表达中断连接:

  • interrupt-controller - 关键字,没有value,声明该节点是接收中断信号的设备
  • interrupt-parent - 一个设备节点属性,它包含一个phandle,指向它连接的中断控制器。没有interrupt-parent属性的节点也可以从它们的父节点继承该属性。(所以常见在根节点看到这个属性)
  • #interrupt-cells - 这是中断控制器节点的属性, 声明这个中断控制器的中断表示符需要多少个cells
  • interrupts - 一个设备节点属性,它就是设备的中断表示符列表。一个用于设备上的每个中断输出信号的表示。

中断指示符是一个或多个数据单元(由#interrupt-cells指定),它指定设备关联到哪一个中断。大多数设备只有一个中断输出,多个的较少。 中断指示符的含义完全由绑定的中断控制器设备解释。每个中断控制器可以决定需要多少个单元来唯一地确定一个中断输入。 在一个系统中,可以有多个中断控制器设备,形成级联的效果。

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/dts-v1/;

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

在上例中,可以看到:

  • 该机器只有一个中断控制器设备,在 10140000 地址
  • 标签’intc’被添加到中断控制器节点,该标签主要用于给根节点中的interrupt-parent分配phandle。并且,该interrupt-parent的值在系统中默认就是这个intc,除非节点中另外显示指定。
  • 每个设备使用一个interrupt属性来表示一个中断线。
  • #interrupt-cells是2,所以每个中断指示符interrupt有2个cell,这里的用法是常见的用法,第一个cell表示中断号,第二个cell表示中断标志,如高/低有效,电平/边沿触发。 实际使用中,需要参考给定的中断控制器的对应的文档来看是如何解释编码的。

Device Specific Data

除了上面的通用属性之外,还可以向节点添加任意属性和子节点。只要符合规则,操作系统所需的任何数据都可以添加。 一般适用于用户自定义的设备。主要规则:

  • 新的特定设备的属性名应该使用制造商名作为前缀,这样它们就不会与现有的标准属性名冲突。属性名里是可以使用逗号的, 如fsl,channel-fifo-len,ibm,ppc-interrupt-server#s,属性名哪些字符可用也可以参考设备树手册
  • 关于用户自己添加的属性和其子节点,其解释和用法应当写明在文档中,以便于驱动程序作者知道如何解释这个属性。对于每个新的compatbile值都应该要有它的对应文档。 文档一般在内核源码目录的 Documentation/devicetree/bindings/, 设备树源码中包含的头文件的 一般位于 include/dt-bindings/,或是用户自己额外添加的地方。
  • 通用的属性也是在不断更新的,应当时常检查,避免导致问题。

Special Nodes

一些特殊节点。

aliases 节点

特定节点通常由完整路径引用,如/external-bus/ethernet@0,0,但是当用户真正想知道的是“哪个设备是eth0?”时,这会变得很麻烦。 aliases节点可用于为完整设备路径分配短别名。例如:一个vexpress的设备树的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    ......

    v2m_serial0: uart@9000 {
            compatible = "arm,pl011", "arm,primecell";
            reg = <0x09000 0x1000>;
            interrupts = <5>;
            clocks = <&v2m_oscclk2>, <&smbclk>;
            clock-names = "uartclk", "apb_pclk";
    };

    ......

    aliases {
            serial0 = &v2m_serial0;
            serial1 = &v2m_serial1;
            serial2 = &v2m_serial2;
            serial3 = &v2m_serial3;
            i2c0 = &v2m_i2c_dvi;
            i2c1 = &v2m_i2c_pcie;
    };

如在系统中查看,就可以方便看到完整路径

1
2
# cat /proc/device-tree/aliases/serial0 
/smb@4000000/motherboard/iofpga@7,00000000/uart@9000

注意,这里的 v2m_serial0是一个标签。在操作系统中标识一个设备时,使用一个alias更方便查找,这样就很容易知道串口0使用的到底是哪一个串口了。

另外,在这里使用了一种新的语法,property = &label;这相当于把label实际对应的字符串(完整路局)赋值给了property。 与上文的 interrupt-parent = <&intc>;不同,property = < &label >; ,这里是有尖括号的,里面其实是传递了一个cell值,即u32的数。

chosen 节点

该节点也不代表真正的设备,而是作为在固件和操作系统之间传递数据的地方,比如内核引导参数bootargs。example:

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

参考

Open Firmware and Devicetree
DeviceTree Kernel API
Device Tree Usage
Devicetree Specification

This post is licensed under CC BY 4.0 by the author.