• 【Linux驱动开发】设备树详解(二)设备树语法详解


    活动地址:CSDN21天学习挑战赛

    【Linux驱动开发】设备树详解(一)设备树基础介绍
    【Linux驱动开发】设备树详解(二)设备树语法详解
    【Linux驱动开发】设备树详解(三)设备树Kernel解析
     

    img
    个人主页:董哥聊技术
    我是董哥,嵌入式领域新星创作者
    创作理念:专注分享高质量嵌入式文章,让大家读有所得!
    img

    4、设备树语法

    dts文件是一种ASCII文本格式的设备树描述,它有以下几种特性:

    • 每个设备树文件都有一个根节点,每个设备都是一个节点。

    • 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。

    • 每个设备的属性都用一组key-value对(键值对)来描述。

    • 每个属性的描述用;结束

    记住上面的几个核心特性,往下看!

    4.1 数据格式

    /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 {
            };
        };
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • /:表示根节点
    • node1node2:表示根节点下的两个子节点
    • child-node1child-node2:表示子节点node1下的两个子节点
    • a-string-property = "A string";:字符串属性,用双引号表示
    • cell-property = <0xbeef 123 0xabcd1234>;:32bit的无符号整数,用尖括号表示
    • binary-property = [0x01 0x23 0x45 0x67];:二进制数据用方括号表示
    • a-string-list-property = "first string", "second string";:用逗号表示字符串列表

     

    4.2 数据结构

    image-20220805084615794

    DeviceTree的结构非常简单,由两种元素组成:Node(节点)和Property(属性)。

    [label:] node-name[@unit-address] {
        [properties definitions]
        [child nodes]
    }
    
    • 1
    • 2
    • 3
    • 4

    想象一下,一棵大树,每一个树干都认为是一个节点,每一片树叶,想作一个属性!

    • label:节点的一个标签,可以作为别名
    • node-name:节点的名称
    • unit-address:单元地址,也就是控制器的地址
    • properties:属性名称
    • definitions:属性的值

     

    4.3 属性介绍

    /dts-v1/;
    
    / {
        compatible = "acme,coyotes-revenge";
        #address-cells = <1>;
        #size-cells = <0>;
        cpus {
            cpu@0 {
                compatible = "arm,cortex-a9";
                reg = <0>;
            };
            cpu@1 {
                compatible = "arm,cortex-a9";
                reg = <1>;
            };
        };
    
        serial@101f0000 {
        	#address-cells = <1>;
        	#size-cells = <1>;
            compatible = "arm,pl011";
            reg = <0x101f0000 0x1000 >;
        };
    
    };
    
    
    • 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

     

    4.3.1 基本属性之compatible、name、unit-address

    下面几个属性是基本属性

    • /dts-v1/;:表示一个dts设备树文件
    • /:表示根节点
    • compatible = "acme,coyotes-revenge";
      • compatible: “兼容性” 属性,这是非常重要的一个属性兼容属性,由该属性值来匹配对应的驱动代码。
      • "acme,coyotes-revenge":该值遵循"manufacturer,model"格式manufacturer表示芯片厂商,model表示驱动名称

    compatible是一个字符串列表。列表中的第一个字符串指定节点在表单中表示的确切设备","

    例如,飞思卡尔 MPC8349 片上系统 (SoC) 有一个串行设备,可实现 National Semiconductor ns16550 寄存器接口。因此,MPC8349 串行设备的 compatible 属性应为:compatible = "fsl,mpc8349-uart", "ns16550". 在这种情况下,fsl,mpc8349-uart指定确切的设备,并ns16550声明它与 National Semiconductor 16550 UART 的寄存器级兼容。

    • cpus:表示一个子节点,该子节点下又有两个子节点,分别为cpu0cpu1
    • cpu@0:遵循[@]格式
      • :ascii字符串,表示节点名称
      • :单元地址,设备的私有地址,在节点reg属性中描述。
         

    4.3.2 寻址属性之address-cells、size-cells、reg、range

    下面几个属性与寻址相关的

    • #address-cells :表示reg属性中表示地址字段的单元个数,每个单元32bit,即用多少个32bit单元表示地址信息。

    • #size-cells:表示reg属性中表示长度字段的单元个数,每个单元32bit,即用多少个32bit单元表示长度信息。

    • reg:该属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。其式为reg = 。每个地址值都是一个或多个 32 位整数的列表,称为单元格。同样,长度值可以是单元格列表,也可以是空的。

     

    cpu节点为例

        cpu@0 {
            compatible = "arm,cortex-a9";
            reg = <0>;
        };
    
    • 1
    • 2
    • 3
    • 4

    #address-cells=1表示reg属性中描述地址字段,所需32bit的单元个数为1,#size-cells=0表示reg属性中没有表示长度的单元,即reg=<0>

     

    再以serial节点为例

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

    该设备都被分配一个基址,以及被分配区域的大小

    #address-cells=1表示reg属性中描述地址字段需要1个32bit单元,#size-cells=1表示reg属性中描述长度字段需要2个单元,即reg=<0x101f0000 0x1000>

    • 0x101f0000:表示serial的控制器起始地址
    • 0x1000:表示serial控制器所占用的大小

     

    地址映射部分还要了解一个属性,为什么要引入这个属性呢

    根节点与根节点的直接子节点,都使用了CPU的地址分配空间,但是根节点的非直接子节点,并不会自动实用CPU的地址空间,因此需要手动用属性分配。

    如上述的serial节点,属于根节点下的直接子节点,无需手动再次分配地址空间,而下面所述的 external-bus节点,其内部的子节点就需要再次分配!

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

    该总线使用了不同的寻址方式,分析一下external-bus节点

    • #address-cells = <2>:用两个单元表示地址
    • #size-cells = <1>:用一个单元表示长度
    • reg = <0 0 0x1000>:第一个0表示片选号,第二个0表示基于片选的偏移,第三个表示偏移的大小

     

    这种抽象的表示,如何映射到CPU地址区域呢?```属性来帮助!

       ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                 1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                 2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash
    
    • 1
    • 2
    • 3

    range:表示了不同设备的地址空间范围,表中的每一项都是一个元组,包含子地址、父地址以及子地址空间中区域的大小,这三个字段。

    • 子地址字段:由子节点的#address-cells决定,如前面的0 00 1
    • 父地址字段:由父节点的#address-cells决定,如0x101000000x10160000
    • 子地址空间字段:描述子节点的空间大小,由父节点的#size-cells决定,如0x100000x10000

    经过映射后,总线的地址映射如下:

    • 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

     

    4.3.3 中断属性之interrupt-controller、interrupt-cells、interrupt-parent、interrupts

    /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 >;
        };
    
        intc: interrupt-controller@10140000 {
            compatible = "arm,pl190";
            reg = <0x10140000 0x1000 >;
            interrupt-controller;
            #interrupt-cells = <2>;
        };
    };
    
    • 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

    如上

    • interrupt-controller:声明一个节点是接收中断信号的设备,也就是中断控制器
    • #interrupt-cellsinterrupt-controller节点下的一个属性,表明中断标识符用多少个单元表示
    • interrupt-parent:设备节点中的一个属性,选择哪个中断控制器
    • interrupts:设备节点的一个属性,中断标识符列表,其单元个数取决于#interrupt-cells

    根据设备树,我们了解到:

    • 该机器有一个中断控制器interrupt-controller@10140000
    • intc标签,为中断控制器的别名,方便引用
    • #interrupt-cells = <2>;:中断标识符用两个单元格表示
    • interrupt-parent = <&intc>;:选择中断控制器
    • interrupts = < 1 0 >;:表示一个中断,第一个值用于表明中断线编号,第二个值表明中断类型,如高电平,低电平,跳变沿等

     

    4.3.4 其他属性之aliases、chosen

        aliases {
            ethernet0 = ð0;
            serial0 = &serial0;
        };
    
    • 1
    • 2
    • 3
    • 4

    aliases:正如其名,别名属性,使用方式:property = &label;

     

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

    chosen:该属性并不表示一个真实的设备,但是提供一个空间,用于传输固件和Linux之间的数据,像启动参数,

     

    img

  • 相关阅读:
    Java高级:网络编程
    odata expand
    Python函数式编程
    单片机——硬件系统
    MySQL 如何避免克隆失败后再次初始化
    AAAAAAAAA
    操作系统-浅谈CPU与内存
    并发容器线程安全应对之道-ConcurrentHashMap
    C/C++语言100题练习计划 87——火柴棒等式(枚举实现)
    gerrit系统如何配置访问控制
  • 原文地址:https://blog.csdn.net/dong__ge/article/details/126214404