5. 设备树修改基础知识

5.1. 设备树知识

Linux内核从3.x开始引入设备树的概念,实现驱动代码与设备信息相分离。 设备树用于描述一个硬件平台的硬件资源。这个“设备树”可以被bootloader(uboot)传递到内核, 内核可以从设备树中获取硬件信息。

设备树

设备树以“树状”结构描述硬件资源。例如本地总线为树的“主干”在设备树里面称为“根节点”, 挂载到本地总线的IIC总线为树的“枝干”在设备树里称为“根节点的子节点”, IIC 总线下的IIC设备不止一个,这些“枝干”又可以再分。

在内核中设备树文件在 /arch/arm/boot/dts/ 目录中, 其中 stm32mp157a-basic.dtb 为野火STM32MP157开发板使用的设备树文件。 其部分源码如下所示

stm32mp157a-basic.dtb部分内容
 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
 #include "stm32mp157c.dtsi"
 #include "stm32mp157cac-pinctrl.dtsi"
 #include "stm32mp157c-m4-srm.dtsi"
 #include <dt-bindings/input/input.h>
 #include <dt-bindings/mfd/st,stpmic1.h>

 /{
     model = "Embedfire STM32MP157 Star LubanCat Robot S1 Board";
     compatible = "st,stm32mp157a-dk1", "st,stm32mp157";

     aliases {
         ethernet0 = &ethernet0;
         serial0 = &uart4;
         serial1 = &usart1;
         serial2 = &usart2;
         serial3 = &usart3;
     };

     chosen {
         stdout-path = "serial0:115200n8";
     };

     memory@c0000000 {
         reg = <0xc0000000 0x40000000>;
     };
     /*-------------以下内容省略--------------*/
 };


 &cpu0{
     //cpu-supply = <&vddcore>;
     clock-frequency = <650000000>;
 };

 &cpu1{
     //cpu-supply = <&vddcore>;
     clock-frequency = <650000000>;
 };


 /*-------------以下内容省略--------------*/
  1. #include 用于包含其他设备树文件和一些宏定义, 主要是平台“共用”的设备树文件(用于外设基地址定义之类的内容)。

  2. /{……}”为设备树的根节点,每个设备树只有一个根节点。

  3. aliases {…}、chosen {…}、memory@c0000000 {…}” 为根节点的子节点; 除此之外,在子节点下还能包含各种子节点。

  4. &cpu0{……}”表示直接引用cpu0节点,向并向其中添加/修改新的属性信息,这些存在的节点 可能定义在“stm32mp157a-basic.dtb”文件, 也可能定义在使用include引用的设备树文件里。

5.1.1. 节点里的内容

设备树中的每个节点都按照以下约定命名:

节点基本格式
1
2
3
4
5
6
node-name@unit-address{
    节点属性1 = …
    节点属性2 = …
    节点属性3 = …
    子节点{}…
}

5.1.1.1. 节点名

其中节点格式中的 node-name 用于指定节点的名称,最长可以为31个字符, unit-address一般为设备地址,用来唯一标识一个节点。必须保证 node-name@unit-address 的唯一性。 其中根节点没有节点名,它直接使用“/”指代这是一个根节点。

其节点路径是 /node-name@unit-address,节点路径是唯一的。 不同层次的设备树节点名字可以相同,但同层次的设备树节点要唯一

5.1.1.2. 节点标签

在stm32mp157c.dtsi头文件中,节点名“cpu”前面多了个“cpu0”如下所示

节点基本格式
1
2
3
cpu0: cpu@0 {
    //省略节点内中内容...
}

“cpu0”为节点标签,通常节点标签是节点名的简写, 它的作用是当其它位置需要引用时可以使用节点标签来向该节点中追加内容,如下所示

节点基本格式
1
2
3
4
&cpu1{
    //cpu-supply = <&vddcore>;
    clock-frequency = <650000000>;
};

5.1.1.3. 节点属性

通常情况下一个节点包含多个属性信息,这些属性信息就是要传递到内核的“板级硬件描述信息”, 驱动中会通过一些API函数获取这些信息。

编写设备树最主要的内容是编写节点的节点属性,通常情况下一个节点代表一个设备, 不同的设备具有不同的节点属性。 内核中为我们提供了很多关于设备树节点属性的资料,其路径为 Documentation/devicetree/ , 当我们需要编写某个设备的设备树节点时查找该目录即可。

常用的通用节点属性介绍如下:

  • compatible属性:用于设备树节点和驱动的匹配。

  • status属性:状态属性用于指示设备的“操作状态”,通过status可以去禁止设备或者启用设备

  • reg属性:地址、长度数据对

  • address属性

    #address-cells属性:用来描述子节点”reg”属性的地址表中用来描述首地址的cell的数量。

    #size-cells属性:用来描述子节点”reg”属性的地址表中用来描述地址长度的cell的数量。

具体使用方式可参照设备的节点属性配置文档 Documentation/devicetree/

5.2. 设备树插件

Linux4.4以后引入了动态设备树(Dynamic DeviceTree)。 设备树插件可以理解为主设备树的“补丁”它动态的加载到系统中,并被内核识别。 例如我们要在系统中增加RGB驱动,那么我们可以针对RGB这个硬件设备写一个设备树插件, 然后编译、加载到系统即可,无需从新编译整个设备树。

设备树插件拥有相对固定的格式,甚至可以认为它只是把设备节点加了一个“壳”编译后内核能够动态加载它。 我们只需要根据格式编写即可,格式如下:

设备树插件语法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/dts-v1/;
/plugin/;

/{
        fragment@0 {
            target-path = "/";
            __overlay__ {
                /*在此添加要插入的节点*/
            };
        };
};
  • 第1行:用于指定dts的版本。

  • 第2行:表示允许使用未定义的引用并记录它们,设备树插件中可以引用主设备树中的节点, 而这些“引用的节点”对于设备树插件来说就是未定义的,所以设备树插件应该加上“/plugin/”。

  • 第6行:指定设备树插件的加载位置,默认我们加载到根节点下,既“target-path =“/”。

  • 第7-8行:我们要插入的设备及节点或者要引用(追加)的设备树节点放在__overlay__ {…};内。

5.3. 编译方式

主设备树源码位于 内核源码/arch/arm/boot/dts 目录下, 设备树插件源码位于 内核源码/arch/arm/boot/dts/overlays 目录下, 编译得到后的设备树文件也位于这两个目录中。

5.3.1. 方式一

1
2
     make ARCH=arm stm32mp157_ebf_defconfig
     make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

编译完成后替换到板子的 /boot/dtbs/4.19.94-stm-r1 目录下的设备树文件

5.3.2. 方式二

  1. 安装device-tree-compiler

    sudo apt install device-tree-compiler

  2. 编译动态设备树

    dtc -I dts -O dtb -o xxx.dtbo xxx.dts

此方式不支持某些设备树语法

5.3.3. 通用加载方式

  1. 在/sys/kernel/config/device-tree/overlays/下创建一个新目录

    mkdir /sys/kernel/config/device-tree/overlays/xxx

  2. 将dtbo固件echo到path属性文件中

    echo xxx.dtbo >/sys/kernel/config/device-tree/overlays/xxx/path

  3. 或者将dtbo的内容cat到dtbo属性文件

    cat xxx.dtbo >/sys/kernel/config/device-tree/overlays/xxx/dtbo

  4. 节点将被创建,查看内核设备树

    ls /proc/device-tree

  5. 删除动态设备树

    rmdir /sys/kernel/config/device-tree/overlays/xxx

5.3.4. 野火linux开发板加载设备树插件

  1. 将xxx.dtbo设备树插件文件放在 /usr/lib/linux-image-4.19.94-stm-r1/overlays 目录下。

  2. /boot/uEnv.txt 中添加以下语句,开机便会自动加载设备树插件

    dtoverlay=/usr/lib/linux-image-4.19.94-stm-r1/overlaysxxx.dtbo

关于uEnv.txt文件之前已有介绍: uEnv.txt文件