ILD

device tree usage
作者:Herbert Yuan 邮箱:yuanjp@hust.edu.cn
发布时间:2022-1-26 站点:Inside Linux Development

Basic Data Format

设备树是节点和属性的简单树型结构。属性是键值对,节点可以包含属性和子节点。如下是一个简单的.dts格式的树:

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"和"node2"

子节点还有子节点,以及一些属性。

属性是简单的键值对,值可以为空,也可以包含任意字节流。数据类型没有编码进树结构。在设备树源文件中有一些基本的数据表示:

字符串用双引号表示 string-property = " a string"

32位无符号整数用尖括号分开 cell-property = <0xbeef 123 0xabcd1234>

二进制数据用方括号 binary-property = [0x01 0x23 0x45 0x67]

不同类型的数据可以结合在一起,用逗号分开 mixed-property = "a string, [0x1 0x23], <0x12345678>"

逗号也可以创建字符串列表 string-list = "red fish", "blue fish"


Basic Concepts

Linux 设备树的基本约定。


Node Names

每个节点应该是<name>[@<unit-address>]的格式。

<name>最多31个字符,表示设备的类型。unit-address是设备的地址。


compatible Property

表示设备的每一个节点,要求有一个compatible属性。

<manufacturer>,<model>

接下来的字符串表示兼容的设备。如

compatible = "fsl,mpc8349-uart", "ns16550"

表示兼容"ns16550"


Addressing

下列3个属性用来编码地址信息:

reg

#address-cells

#size-cells


reg = <addr1 len1 addr2 len2 ... >

addr1有#address-cells个,同理addr2也有同样多个。

len1有#size-cells个,同理len2也有同样多个。#size-cells可以为0.


例子1:

1
2
3
4
5
6
7
8
9
10
/ {    
    #address-cells = <1>;
    #size-cells = <1>;
 
    ...
 
    serial@101f0000 {
        compatible = "arm,pl011";        
        reg = <0x101f0000 0x1000 >;
    };


例子2:

1
2
3
4
5
6
7
8
  external-bus {        
        #address-cells = <2>;
        #size-cells = <1>;
 
        ethernet@0,0 {
            compatible = "smc,smc91c111";            
            reg = <0 0 0x1000>;
        };

有些总线有不同的地址框架,因此灵活使用地址模型。注意地址并不仅仅是地址,也可能是id或者其它标识设备的信息。

 

如果设备节点有reg属性,则设备名中的unit-address必须为reg的第一个地址域。

如上述:serial@101f0000 ethernet@0,0


Ranges (Address Translation)

如果设备直接使用CPU地址,则不需要ranges,如果设备没有使用CPU的地址域,为了获得地址映射关系,使用ranges属性表述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/ {
    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>;
        };

如上,0 0是子设备的地址,0x1010000是CPU的起始地址,0x10000是地址的长度。


Interrupts

有4个属性用来描述中断连接:

interrupt-controller

节点中的一个空的该属性,表明这个节点的设备是一个中断控制器。


#interrupt-cells

表示interrupts属性中标识中断id的个数。


interrupt-parent

这个属性标识设备的终端控制器是哪一个。子节点没有这个属性,可继承。


interrupts

设备节点的该属性,标识该设备产生的中断号。


需要一个例子:

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

这个machine有一个中断控制器。

标签intc,用来表示设备用的哪个中断控制器,在interrupt-parent属性中。

每个设备有一个interrupt属性,表示不同的中断输入线。

#interrupt-cells是2,第一个表示中断id,第二个表示flags,如active high等。


Special nodes

aliases node

property = &label,例子:

1
2
3
4
  aliases {
        ethernet0 = &eth0;
        serial0 = &serial0;
    };


chosen node

chosen node不是真的设备,用来在固件和操作系统中传递参数,例如boot arguments。通常chosen node在dts文件中为空,在启动时增加:

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


2022/126 补充


node可以有label。而且可以有一个或多个label,格式如下:

label : node-name {

}


使用 @lablel的方式,引用节点。可以作为phandle引用,也可以直接作为节点名:


节点名称是全局的,不能重复。可以在根节点之外,写节点属性,device tree compiler会合并成一个。

在编译出的dtb中,不存在label。

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
/ {
    gsw: gsw@0 {
                compatible = "mediatek,mt753x";
                mediatek,ethsys = <&ethsys>;
                #address-cells = <1>;
                #size-cells = <0>;
    };
}
 
@gsw {
        mediatek,mdio = <&mdio>;
        mediatek,portmap = "llllw";
        mediatek,mdio_master_pinmux = <0>;
        reset-gpios = <&pio 39 0>;
        interrupt-parent = <&pio>;
        interrupts = <38 IRQ_TYPE_LEVEL_HIGH>;
        status = "okay";
 
        port5: port@5 {
                compatible = "mediatek,mt753x-port";
                reg = <5>;
                phy-mode = "sgmii";
                fixed-link {
                        speed = <2500>;
                        full-duplex;
                };
        };
 
        port6: port@6 {
                compatible = "mediatek,mt753x-port";
                mediatek,ssc-on;
                reg = <6>;
                phy-mode = "sgmii";
                fixed-link {
                        speed = <2500>;
                        full-duplex;
                };
        };
};
 
&hnat {
        mtketh-wan = "eth1";
        mtketh-lan = "eth0";
        mtketh-max-gmac = <2>;
        status = "okay";
};


如上,gsw@0有label gsw。且分成了两个地方。编译后,合并成一个。

mediatek,mdio = <&mdio>;

mdio是一个label。


dts文件中可以包含头文件,然后使用宏。例如:

1
2
#include <dt-bindings/gpio/gpio.h>
interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>,

IRQ_TYPE_LEVEL_HIGH就是一个宏。



参考

https://elinux.org/Device_Tree_Usage

https://elinux.org/Device_Tree_Reference

https://www.nxp.com/docs/en/application-note/AN5125.pdf



Copyright © insidelinuxdev.net 2017-2021. Some Rights Reserved.