DTS设备树学习——语法

上一节大致介绍了一下DTS的概念,了解了他的起源,加载过程以及编译方式。

这一节就介绍一下DTS的语法结构。

文件结构

上一节了解过DTS的文件结构,有.dts.dtsi,其中.dtsi是提炼出来的soc共用的部分,减少冗余。

以高通QSDK的kernel部分的dts为例

ipq5332.dtsi    //soc公用部分
ipq5332-mi04.1.dts //单板的特殊内容
ipq5332-mi04.3.dts 
ipq5332-memory.dtsi //公用内存配置
......

查看具体文件内容,打开ipq5332-mi04.1.dts,可以在开头看见引用,与C类似,类似于头文件引用。

// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
 * IPQ5332 AP-MI04.1 board device tree source
 *
 * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
 */

/dts-v1/;

#include "ipq5332.dtsi"  #这里引用了公用SOC部分
#include "ipq5332-memory.dtsi" #这里引用了公用内存部分

DTS的基础框架

打开.dts可以看到类似如下的基本结构。类似xml或者json的格式。

/dts-v1/;
/ {                                 //根节点
    node1{                          //node1是节点名,是/的子节点
        key=value;                  //node1的属性
        ...
        node2{                      //node2是node1的子节点
            key=value;              //node2的属性
            ...
        }
    }                               //node1的描述到此为止
    node3{
        key=value;
        ...
    }
}

从上面的基础结构可以看到 根节点描述为 /{}

并且还有一个声明/dts-v1/;,声明内容为设备树的版本,如果没有这个声明,那么编译dtb时,会报出syntax error的错误,排查起来很麻烦。

设备树使用树状结构描述设备信息,它有以下几种特性:

  1. 每个设备树文件都有一个根节点,每个设备都是一个节点。
  2. 节点由 节点名 + 属性 组成。
  3. 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
  4. 每个设备的属性都用一组key-value对(键值对)来描述。
  5. 每个属性的描述用;结束

节点Node

{}包围起来的结构称之为节点,dts中最开头的/ {},称为根节点。

在节点中,以 key = value 代表节点属性。

/dts-v1/;
/ {
	clocks {
		sleep_clk: sleep-clk {
			compatible = "fixed-clock";
			clock-frequency = <32000>;
			#clock-cells = <0>;
		};
	}
	blsp1_uart1: serial@78b0000 {
			compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
			reg = <0x078b0000 0x200>;
		};
}

节点Name

/dts-v1/;
/ {
	blsp1_uart1: serial@78b0000 {
			compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
			reg = <0x078b0000 0x200>;
		};
}

节点名称:每个节点名格式为:<name>[@<unit_address>]

<name>:设备名,就是一个不超过31位的简单 ascii 字符串,节点的命名应该根据它所体现的是什么样的设备。

<unit_address>:设备地址,用来唯一标识一共节点。没有指定<unit_address>时,同级节点命名必须是唯一的;但只要<unit_address>不同,多个节点也可以使用一样的通用名称。

别名(引用)

以上面代码内容为例blsp1_uart1: serial@78b0000

<name>:设备名 就是 serial 表示串行通信设备

<unit_address>:设备地址 78b0000 设备的唯一地址

那么前面的blsp1_uart1是什么? 这就是别名,或者叫引用。

当我们找一个节点的时候,我们必须书写完整的节点路径,这样当一个节点嵌套比较深的时候就不是很方便。所以,设备树允许我们用下面的形式为节点标注引用(起别名),借以省去冗长的路径。标号引用常常还作为节点的重写方式,用于修改节点属性。

例如:/soc@0/apb-bridge@10000000/apb-bus@0/blsp1@10000000/blsp1_uart1@78b0000

这样一个长度的路径明显会为dts的书写与阅读造成困难,所以有了别名。

  • 格式:
    • 声明别名: 别名 : 节点名
    • 访问 : &别名

节点属性Property

属性一般由 key = value; 键值对构成。

Linux设备树语法中定义了一些具有规范意义的属性,包括:compatible, address, interrupt等,这些信息能够在内核初始化找到节点的时候,自动解析生成相应的设备信息。

此外,还有一些Linux内核定义好的,一类设备通用的有默认意义的属性,这些属性一般不能被内核自动解析生成相应的设备信息,但是内核已经编写的相应的解析提取函数,常见的有 "mac_addr"、"gpio"、"clock"、"power"、"regulator" 等等。

属性一般有以下几种数据类型:

  • 字符串 device_type = "network";
  • 整数型 reg = <0x39C00000 0x10000>;
  • 二进制数据(方括号) local-mac-address = [000000000000];
  • 列表类型(用逗号组合) pins = "gpio10", "gpio11", "gpio12", "gpio13";
  • 混合类型(用逗号组合) mixed-property = "a string", [01 23 45 67], <0x12345678>;

compatible 兼容性

如果一个节点是设备节点,那么它一定要有compatible(兼容性),因为这将作为驱动和设备(设备节点)的匹配依据,compatible(兼容性)的值可以有不止一个字符串以满足不同的需求。

compatible属性是用来查找节点的方法之一,另外还可以通过节点名或节点路径查找指定节点。

根节点的compatible也是非常重要的,一般在系统启动以后,用于识别对应系统一些东西,并由此进行对应的初始化。

wifi1: wifi1@c000000 {
			compatible = "qcom,cnss-qcn6122", "qcom,qcn6122-wifi";
			qcom,rproc = <&qcom_q6v5_wcss>;
			status = "disabled";
		};

如上方节点为例,compatible = "qcom,cnss-qcn6122", "qcom,qcn6122-wifi";

compatible属性格式为:compatible = "<manufacturer>,<model>" [, "<manufacturer>,<model>"]

manufacturer表示厂家,这个节点为qcom,表示为高通。

model表示设备型号,这里有两个分别是cnss-qcn6122qcn6122-wifi,表示了射频模块qcn6122及其wifi配置。

compatible 属性是除了节点绝对路径之外的又一索引方式,可以通过该属性索引相关节点,以及查找解析Dts源码里的结构体

address 地址属性

在DTS中一般以reg属性表示地址,reg是最常见的用于描述硬件设备寄存器基址的属性。

这个属性对于处理器与外设通信至关重要,因为它的存在是为了告诉内核这些设备的控制和状态寄存器在哪里,从而允许驱动程序正确地读写这些寄存器。

除了reg用来描述地址信息,还有#address-cells#size-cells这两个属性用来约束子节点reg属性的内容格式。

aips-bus@02000000 { /* AIPS1 */
    compatible = "fsl,aips-bus", "simple-bus";
    #address-cells = <1>;
    #size-cells = <1>;
    reg = <0x02000000 0x100000>;

    i2c1: i2c@021a0000 {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
        reg = <0x021a0000 0x4000>;

	rtc: rtc@68 {
	    compatible = "stm,mt41t62";
	    reg = <0x68>;
	};
    };
};

从上面的代码片段可以分析,aips-bus@02000000 i2c@021a0000 的父节点;i2c@021a0000rtc@68 的父节点。

首先来看父节点aips-bus@02000000,包含属性
#address-cells = <1>;
#size-cells = <1>;
所以其子节点i2c@021a0000 的reg属性被约束为
reg = <0x021a0000 0x4000>;
reg = <address size>;
i2c@021a0000 ,包含属性
#address-cells = <1>;
#size-cells = <0>;

所以其子节点rtc: rtc@68的reg属性被约束为
reg = <0x68>;
reg = <size>;

总而言之,若父节点#address-cells为n,#size-cells为m
则子节点reg属性约束为reg = <address*n size*m>
可能有人会有疑问,问什么节点名称中已经包含了地址,还要再节点属性添加reg来重复一遍?
节点名称中的地址如aips-bus@02000000,这里的@02000000是一种命名约定,用于直观地表明节点所代表的设备或总线的基地址。
而reg中的地址则是正式通知驱动,他要操作的设备的寄存器地址。

interrupts 中断属性

中断产生设备用interrupts属性描述中断源(interrupt specifier),因为不同的硬件描述中断源需要的数据量不同,所以interrupts属性的类型也是。

该属性引入了以下属性

  • interrupt-controller  一个空属性用来声明这个node接收中断信号,即这个node是一个中断控制器。
  • #interrupt-cells 中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符
  • interrupt-parent 标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的
  • interrupts 一个中断标识符列表,表示每一个中断输出信号。
/ {
    compatible = "acme,coyotes-revenge";
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;//指定依附的中断控制器是intc

    serial@101f0000 {   //子节点:串口设备
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >;
        interrupts = < 1 0 >;
    };

    intc: interrupt-controller@10140000 { //intc中断控制器
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
        interrupt-controller;//定义为中断控制器设备
        #interrupt-cells = <2>;
    };
}

#interrupt-cells:为了明确表示一个中断由几个u32表示,引入了#interrupt-cells属性,#interrupt-cells属性的类型是整数型

在ARM GIC(Generic Interrupt Controller)中,interrupts属性一般有如下表示:

当#interrupt-cells为3时:

  • interrupts = <中断类型 中断触发信号 中断触发标志>
    • 中断类型: 0 表示SPI中断,1 表示PPI中断
    • 中断触发信号: PPI中断【0-15】、SPI中断类型【0-987】
    • 中断触发标志:
      • bits [ 3 :0 ]
        • 1 = 低- 至- 高边沿触发
        • 2 = 高- 到- 低边沿触发
        • 4 = 活跃的高水平 - 敏感
        • 8 = 低电平有效 - 敏感
      • bits [ 15 :8 ] 
        • PPI中断cpu掩码。每个位对应于每个位附加到GIC的8个可能的cpu。

当#interrupt-cells为2时:

  • interrupts = <中断触发信号 中断触发标志>
    • 中断触发信号:SGI中断即软件触发中断 【0-15】、PPI中断即私有外设中断【16-31】、SPI中断即公用外设中断【32-1019】
    • 中断触发标志:
      • bits [ 3 :0 ]
        • 1 = 低- 至- 高边沿触发
        • 2 = 高- 到- 低边沿触发
        • 4 = 活跃的高水平 - 敏感
        • 8 = 低电平有效 - 敏感
      • bits [ 15 :8 ] 
        • PPI中断cpu掩码。每个位对应于每个位附加到GIC的8个可能的cpu。
当然interrupts 属性如果直接使用整数表示,阅读起来会有些麻烦,所以有些dts会使用宏的形式书写
interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;

    GPIO属性

    GPIO即通用输入输出口。常见与定义CPU的GPIO接口。

    该属性一般有以下几种:

    • gpio-controller 空字段,用来表示这个节点是GPIO控制器
    • #gpio-cells 用来描述GPIO使用节点的属性
    tlmm: pinctrl@1000000 {
    			compatible = "qcom,ipq5018-pinctrl";
    			reg = <0x1000000 0x300000>;
    			interrupts = <0x0 0xd0 0x0>;
    			gpio-controller;
    			#gpio-cells = <0x2>; #表示GPIO引脚需要2个单元格的数据
    			interrupt-controller;
    			#interrupt-cells = <0x2>;
    		};
    
    &soc {
    	gpio_keys {
    		compatible = "gpio-keys";
    		/* pinctrl-0 = <&button_pins>;
    		pinctrl-names = "default"; */
    
    		button@1 {
    			/* label = "wps";
    			linux,code = <KEY_WPS_BUTTON>; */
    			gpios = <&tlmm 34 GPIO_ACTIVE_LOW>; #使用控制器tlmm GPIO34 低电平触发
    			linux,input-type = <1>;
    			debounce-interval = <60>;
    		};
    	};
    };

    上面的案例可以看到GPIO控制器tlmm的约束值#gpio-cells为2,表示需要两个单元格的数据。后面在button节点也可以看到,当引用控制器后,后面跟着两个值分别为GPIO序列、工作模式。

    结语

    这一篇文章主要内容就是介绍下,dts的文件结构了解其如何组成dtb,以及具体的编写方式,从根节点出发,到子节点,以及一些特殊节点和其相关的特殊属性的了解。其实这里只列出了一些较通用的节点和属性。实际上在真正的复杂板级结构上制作dts,有时候需要自己编写额外的dts解析源码,这样会引入一些自己定义的key。相对私有化,故不在本篇介绍了。

    其实看完第二节基础框架后,去看真实的dts,哪怕有些地方看不懂,猜也能猜个大概了。

    暂无评论

    发送评论 编辑评论

    
    				
    |´・ω・)ノ
    ヾ(≧∇≦*)ゝ
    (☆ω☆)
    (╯‵□′)╯︵┴─┴
     ̄﹃ ̄
    (/ω\)
    ∠( ᐛ 」∠)_
    (๑•̀ㅁ•́ฅ)
    →_→
    ୧(๑•̀⌄•́๑)૭
    ٩(ˊᗜˋ*)و
    (ノ°ο°)ノ
    (´இ皿இ`)
    ⌇●﹏●⌇
    (ฅ´ω`ฅ)
    (╯°A°)╯︵○○○
    φ( ̄∇ ̄o)
    ヾ(´・ ・`。)ノ"
    ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
    (ó﹏ò。)
    Σ(っ °Д °;)っ
    ( ,,´・ω・)ノ"(´っω・`。)
    ╮(╯▽╰)╭
    o(*////▽////*)q
    >﹏<
    ( ๑´•ω•) "(ㆆᴗㆆ)
    😂
    😀
    😅
    😊
    🙂
    🙃
    😌
    😍
    😘
    😜
    😝
    😏
    😒
    🙄
    😳
    😡
    😔
    😫
    😱
    😭
    💩
    👻
    🙌
    🖕
    👍
    👫
    👬
    👭
    🌚
    🌝
    🙈
    💊
    😶
    🙏
    🍦
    🍉
    😣
    Source: github.com/k4yt3x/flowerhd
    颜文字
    Emoji
    小恐龙
    花!
    上一篇
    下一篇