0%

Linux设备树语法

本文主要描述设备树的基本语法。设备树顾名思义就是一些node和property组成的树形结构,设备树包含了板级的硬件资源,目的就是解耦Linux内核里board和设备驱动支持的硬件配置,而不是把board specific的硬件资源硬编码进内核里。

一.设备树基础语法

  1. 设备树根节点是”/

  2. 设备树里的property是key-value pairs,其值可以是空也可以是任意的字节流,比如字符串、二进制数据、整型值

    1
    2
    3
    string-property="this is a string"
    binary-property=[0x01 0x02 0x03 0x04]
    unitcell-property=<GPIO_GROUP GPIO_NUM GPIO_TRIGGER>
  3. 设备树的compatible

    每个node都要用compatible属性用来标识一个特定的设备,其值由一个或多个字符串组成,组织形式是:”,“比如imx6ul的dts里compatible = “fsl,imx6ul-14x14-evk”, “fsl,imx6ul”;第一个字符串”fsl,imx6ul-14x14-evk”代表了确切的设备,第二个字符串就代表了设备兼容的其它设备

  4. node names

    每个node都必须有名字,格式为:`[@]。unit-address是用于访问name设备的地址,可以在该节点的reg属性里找到。需要注意的是多个node的名字可以是相同的,比如存在多个serial设备,这个时候只要确保unit-address不同就可以了

  5. 寻址

    可寻址设备可以使用如下三个属性来向设备树传达其地址编码信息

    • reg

    • #address-cells

    • #size-cells

      reg属性常见组织形式是:reg = <address1 length1 [address2 length2] [address3 length3] … >,意思就是reg是一个元组(tuple),每一个元素称之为cell,每一个cell代表了设备的可寻址范围。当#address-cells = <1>就意味着用1个unit32的数表示地址,#size-cells = <1>代表了可寻址的范围(也用一个unit32数表示)

  6. reg属性

    reg 属性描述了设备资源在其父总线定义的地址空间内的地址,其组织形式是:,其中prop-encoded-array每一个cell都是address-length pairs。如果一个node有reg属性,那么该node的名字必须包含unit-address,unit-address来源于reg属性的第一个地址值。例子如下,可以看出intc是一个node,代表了中断控制器,其reg的第一个地址值是0x00a01000,那么对应的该节点的unit-address也必须包含0x00a01000,并且也可以看出intc有两个可寻址空间,分别是0x0x00a01000—0x00a01fff和0x00a02000—0x00a020ff

    1
    2
    3
    4
    5
    6
    7
    85 intc: interrupt-controller@00a01000 {
    86 compatible = "arm,cortex-a7-gic";
    87 #interrupt-cells = <3>;
    88 interrupt-controller;
    89 reg = <0x00a01000 0x1000>,
    90 <0x00a02000 0x100>;
    91 };
  7. ranges属性

    ranges可以理解为地址翻译表,其目的主要是显式把设备地址映射为CPU可以使用的地址。rangs组织方式为: or ,其中prop-encoded-array每一个元素都由三部分组成:child-bus-address, parent-bus-address, length

    • child-bus-address:子总线地址空间里的物理地址

    • parent-bus-address:父总线地址空间的物理地址

    • length:子地址空间的size

      如果rangs为空,就表示子地址空间和父地址空间是一一对应的,不再需要地址翻译

      注意:如果某个node缺少ranges,除了其父节点,其他任何节点都不能直接访问

  8. 中断

    中断一般包括中断产生设备和中断处理设备。中断控制器负责处理中断,每一个中断都有对应的中断号及触发条件。中断产生设备可能有多个中断源,有时多个中断源对应中断控制器中的一个中断,这种情况中断产生设备的中断源称之为中断控制器中对应中断的子中断。一般情况中断产生设备数量要多于中断控制器,多个中断产生设备的中断都由一个中断控制器处理,这种多对一的关系也很像一个树形结构,所以在设备树中,中断也被描述成树,叫中断树。

    使用 如下四部分来描述中断:

    • interrupt-controller:中断控制器,其值为空,用来接收中断信号。中断控制器节点用interrupt-controller属性表示自己是中断控制器

    • #interrupt-cells:跟#address-cells和#size-cells很相似,用来说明有多少个中断说明符(interrupt specifier )

    • interrupt-parent:指向interrupt-controller的phandle,如果某个节点没有该属性,默认继承其父节点的中断属性

    • interrupts:包含了许多中断描述符(interrupt specifier,也就是中断源),比如中断类型(PPI,SPI……)、中断号、中断触发类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
       intc: interrupt-controller@00a01000 {
      86 compatible = "arm,cortex-a7-gic";
      87 #interrupt-cells = <3>;
      88 interrupt-controller;
      89 reg = <0x00a01000 0x1000>, <0x00a02000 0x100>;
      };
      ......

      gpio1: gpio@0209c000 { compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
      509 reg = <0x0209c000 0x4000>;
      510 interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
      511 <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
      512 gpio-controller;
      513 #gpio-cells = <2>;
      514 interrupt-controller;
      515 #interrupt-cells = <2>;
      516 };

      上面代码里interrupts的cell数量由intc也就是中断控制器决定,在该控制器的interrupt domain下,中断源(interrupt specifier)用3个u32表示,intc是一个GIC中断控制器,由GIC规范可知第一个u32表示中断类型,第二个是中断号,第三个是中断触发条件

  9. alias

    alias是用来引用一个节点的,一般情况下引用一个节点需要写出器相对根节点的完整路径,当需要新增许多节点时,还需要考虑节点具体的位置,所以这个时候在需要引用的节点前设置一个label,引用的时候只需要&label即可.相当于为node起一个别名,然后其它的node就可以引用这个别名。它的好处是:有些node在不同的版型里面,真名可能有变化,例如gpio,有些版型可能叫做gpio0、gpio1、…,而有些可能叫做gpioa、gpiob、…,我们node起名的依据应该尽量贴近硬件。而其它driver(例如使用gpio的driver)引用gpio时,又不想随着gpio的真名的改变,统统改变一下,所以它们就可以引用gpio的别名。别名尽量固定,更改真名的情况下,只需要更改alias处即可,不需要更改每一个用到的driver,和typedef定义类型有点类似。

  10. chose

    chose主要作用就是传递数据,一般用作传递启动参数,比如:

    1
    2
    3
    chosen {
    bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
    };
  11. 许多板子都有公共的硬件资源,这些硬件资源又可以封装一下放到.dtsi文件里,就和C语言里访问头文件一样,include相应dtsi就可以获取相应资源定义