7. 设备树和设备树插件

本章节内容作为后面章节外设修改的基础知识,所涉及内容并不深奥,仅作为概括。

7.1. 设备树和设备树插件源码位置

根据驱动开发实战指南篇的 驱动章节实验环境搭建 内容将内核源码克隆下来。

设备树文件在内核源码 /arch/arm/boot/dts/ 目录中, 其中imx6ull-mmc-npi.dts和imx6ull-nand-npi.dts分别为野火imx6ull的emmc和nand核心板使用的主设备树文件。

设备树插件在内核源码 /arch/arm/boot/dts/overlays/ 目录中, 包含了开发板核心板各脚做不同外设功能的参考设备树插件。

7.2. 设备树介绍

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

设备树

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

其部分源码如下所示

imx6ull-mmc-npi.dts / imx6ull-nand-npi.dts 部分内容
 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
 #include <dt-bindings/input/input.h>
 #include "imx6ull.dtsi"

 / {
     model = "Seeed i.MX6 ULL NPi Board";
     compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

     aliases {
             pwm0 = &pwm1;
             pwm1 = &pwm2;
             pwm2 = &pwm3;
             pwm3 = &pwm4;
     };
     chosen {
             stdout-path = &uart1;
     };

     memory {
             reg = <0x80000000 0x20000000>;
     };

     /*-------------以下内容省略-------------*/
 };

 &cpu0 {
     dc-supply = <&reg_gpio_dvfs>;
     clock-frequency = <800000000>;
 };

 &clks {
     assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
     assigned-clock-rates = <786432000>;
 };

 /*-------------以下内容省略--------------*/
  1. imx6ull.dtsi 文件由NXP官方提供,这是一个imx6ull平台“共用”的设备树文件(用于外设基地址定义之类的内容)。 可直接使用include包含。

  2. /{……}”为设备树的根节点(可想象为树干),每个设备树只有一个根节点。

  3. aliases {…}、chosen {…}、memory {…}” 为根节点的子节点(树枝); 除此之外,在子节点下还能包含各种子节点(“设备树”正在茁壮成长)。

  4. &cpu0{……}”表示直接引用cpu0节点,向并向其中添加/修改新的属性信息,这些存在的节点 可能定义在在“ imx6ull-mmc-npi.dtb / imx6ull-nand-npi.dtb ”文件, 也可能定义在“imx6ull.dtsi”文件所包含的设备树文件里

7.2.1. 节点里的内容

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

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

7.2.1.1. 节点名

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

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

7.2.1.2. 节点属性

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

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

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

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

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

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

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

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

7.3. 设备树插件介绍

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__ {…};内。

7.4. pinctrl子系统

Linux在引入pinctrl子系统之后,我们便能够使用pinctrl子系统将GPIO引脚设置为各种功能。 也就是说pinctrl子系统帮助我们管理芯片引脚并自动完成引脚的初始化。

在 imx6ull-mmc-npi.dts / imx6ull-nand-npi.dts 文件追加了对于iomuxc节点的信息,如下所示

imx6ull-mmc-npi.dts / imx6ull-nand-npi.dts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
&iomuxc {
    pinctrl-names = "default","init","sleep";
    pinctrl-0 = <&pinctrl_uart1>;
    //表示省略......
    pinctrl_uart1: uart1grp {
        fsl,pins = <
            MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
            MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
        >;
    };
    //表示省略......
}
  • pinctrl-names: 定义引脚状态。

  • pinctrl-0: 定义第0种状态需要使用到的引脚,可引用其他节点标识

上面代码的重点内容在于 pinctrl_uart1: uart1grp{……}, 其中节点信息 fsl,pins:用该属性来标识引脚的配置信息,结合imx6ull的pinctrl子系统驱动使用, 不同厂商的pinctrl子系统的节点属性并不相同,因此此处的设置格式并不适用于其他厂商芯片。

MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 所表示的信息是将UART1_TX_DATA引脚(数据查询手册可知为GPIO1_IO16) 设置为UART1_DCE_TX功能。 宏定义在内核 /arch/arm/boot/dts/imx6ul-pinfunc.h 中,在imx6ul-pinfunc.h包含了各种引脚的复用关系, 0x1b0b1 为GPIO的其他电气属性,配置较为复杂,在配置时参考其他类似引脚功能配置即可, 也可参考 Documentation/devicetree/bindings/pinctrl/fsl,imx6ul-pinctrl.txt 文档中的配置信息。

7.5. 编译与替换加载

7.5.1. 编译主设备树和设备树插件(野火imx6ull)

进入内核源码中最外层目录

1
2
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

以上操作将在内核源码 /arch/arm/boot/dts/ 目录下所有设备树编译生成 .dtb 在同目录, /arch/arm/boot/dts/overlays/ 目录下的所有设备树插件编译生成 .dtbo 在同目录。

可以将自己编写的设备树或者设备树插件分别添加到以上目录一同编译。

7.5.2. 替换设备树

设备树.dtb文件可以直接替换到板子的 /usr/lib/linux-image-4.19.35-imx6/ 目录下后重启。

7.5.3. 添加或者替换设备插件

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

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

    dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/xxx.dtbo

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