一、设备树为什么会诞生?

大势所趋,世界各地的开发者向社区提交了关于大量板级信息代码,板级信息都写到.c里面,导致linux内核臃肿。

嘿嘿,社区之父狂怒了,“This whole ARM thing is a f"cking pain in the ass”。大佬们想看到是算法优化啥的,这些东西对他们来说是小儿科吧。

因此ARM社区引入PowerPC等架构采用的设备树。

二、什么是设备树?

设备和树( Flattened Device Tree)

将板子信息做成独立的格式,文件扩展名为.dts。一个平台或者机器对应一个.dts。

一个SOC可以作出很多不同的板子,这些不同的板子肯定是有共同的信息,将这些共同的信息提取出来作为一个通用的文件,其他的dts文件直接引用这个通用文件即可,这个通用文件就是dtsi文件,类似于C语言中的头文件。一般dts描述板级信息(也就是开发板上有哪些IC设备、SPI设备等),dtsi描述SOC级信息(也就是SOC有几个CPU、主频是多少、各个外设控制器信息等)。

tmp2D77.png

三、DTS、DTB和DTC的关系

.dts相当于.c,就是DTS源码文件。

DTC工具相当于gcc编译器,将.dts编译成.dtb。

.dtb相当于bin文件,或可执行文件。

dtc工具源码路径

script/dtc

四、DTS基本语法

设备树也有头文件,扩展名为.dtsi。可以将一款SOC他的其他所有设备/平台的共有的信息提出来,作为一个通用的.dtsi文件。

tmp3253.png

第1行,“/"”是根节点,每个设备树文件只有一个根节点。
第2、6和17行, aliases、cpus和intc是三个子节点,在设备树中节点命名格式如下:
node-name@unit-address
“node-name”是节点名字,为ASCI字符串,节点名字应该能够清晰的描述出节点的功能
“uart”就表示这个节点是UART1外设。
“unit- address”一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话“ unit-address”可以不要
但是我们在示例代码43.3.2.1中我们看到的节点命名却如下所示
cpu0: cpu@0
上述命令并不是“node- name@unit-address”这样的格式,而是用“:”隔开成了两部分,
前面的是节点标签( label),“:”后面的オ是节点名字,格式如下所示:

label: node-name@ unit-address

引入 label的目的就是为了方便访问节点,可以直接通过& label来访问这个节点,比如通过
&cpu0就可以访问“cpu@0”这个节点,而不需要输入完整的节点名字。
第10行,cpu0也是一个节点,只是cpu0是cpus的子节点。
每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任
意的字节流。设备树源码中常用的几种数据形式如下所示:
①、字符串
compatible ="arm, cortex-a7";
上述代码设置 compatible属性的值为字符串“arm, cortex-a7”。
②、32位无符号整数
reg=<0>
上述代码设置reg属性的值为0,reg的值也可以设置为一组值,比如:
reg=<00x123456 100>
③、字符串列表
属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:
compatible="fsl, imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand"
上述代码设置属性 compatible I的值为“ fsl.imx6ul-gpmi-nand”和“fsl,imx6ul-gpmi-nand”。

DTS基本语法小结

  • 1、DTS也是’/’开始
  • 2,从/根节点开始描述设备信息
  • 3,在/根节点外有一些&cpu0这样的语句是“追加“。
  • 4,节点名字,完整的要求
    node-name@unit-address

unit-address一般都是外设寄存器的起始地址,有时候是I2C的设备地址,或者其他含义,具体节点具体分析。设备树里面常常遇到如下所示节点名字:
intc: interrupt-controller@00a01000
:前面是标签label,后面才是名字。intc,完整的名字是interrupt-controller@00a01000。
&intc

五、标准属性

1、compatible属性

  • compatible属性也叫做“兼容性”属性,这是非常重要的一个属性! compatible属性的值是个字符串列表, compatible属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序。
  • compatible属性的值格式如下所示:
    "manufacturer, model"

其中 manufacturer表示厂商, model一般是模块对应的驱动名字。
比如sound节点的 compatible属性值如下:
compatible ="sl, imx6ul-evk-wm8960", sl, imx-audio-wm8960"
属性值有两个,分别为“fl,imx6ul-evk-wm8960”和“fsl,imx- audio-wm8960”,其中“fsl”表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx- audio-wm8960”表示驱动模块名字。

  • 这个设备首先使用第一个兼容值在 Linux内核里面査找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。
  • 一般驱动程序文件都会有一个OF匹配表,此OF匹配表保存着一些 compatible值,如果设备节点的 compatible属性值和OF匹配表中的任何一个值相等,那么就表示设备可以使用这个

tmp3E47.png

第632~635行的数组 imx_wm8960_dt_ids就是imx-wm8960.c这个驱动文件的匹配表,此匹配表只有一个匹配值“ fs.imx- audio-wm8960”。如果在设备树中有哪个节点的 compatible属性值与此相等,那么这个节点就会使用此驱动文件。
第642行,wm8960采用了 platform driver驱动模式,of_ match table为 Imx wm8960_dt_ids,也就是设置这个 platform driver所使用的OF匹配表。

2、 model属性

model属性值也是一个字符串,一般 model属性描述设备模块信息,比如名字什么的,比
model ="wm8960-audio“

3、 status属性

status属性看名字就知道是和设备状态有关的, status属性值也是字符串,字符串是设备的状态信息

描述
OK表明设备是可操作的。
disable表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。至于 disabled的具体含义还要看设备的绑定文档。
fail表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作
fail-sss含义和“fail”相同,后面的ss部分是检测到的错误内容

4、# address-cells和#size-cells属性

这两个属性的值都是无符号32位整形

# address-cells #size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。

# address-cells属性值决定了子节点reg属性中地址信息所占用的字长(32位)

#size-cells属性值决定了子节点reg属性中长度信息,所占的字长(32位)。

#address-cells和#size-cells表明了子节点应该如何编写reg属性值

一般reg属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg属性的格式一为:
reg=<address lengthl address2 length2 address3 length3..

六、特殊节点

1,aliases 别名,方便访问

2,chosen节点,主要目的就是将uboot里面bootargs环境变量值,传递给Linux内核作为命令行参数 cmdline。

uboot是如何向kernel传递bootargs?

cat /proc/device-tree/chosen/bootargs
经过查看发现chosen节点中包含bootargs属性,属性值和uboot的bootargs一致。

uboot接触过dtb,最终通过bootz 80800000 – 83000000 来启动内核。经过分析判断uboot拥有bootargs环境变量和dtb,因此最有可能“作案”。

最终发现在uboot的fdt_chosen函数中会查找chosen节点,并且在里面添加bootargs属性,属性值为bootargs变量值。

fdt_chosen函数位置 uboot/common/fdt_support.c

七、特殊的属性

1、使用设备树之前设备匹配方法

在没有使用设备树以前, uboot会向 Linux内核传递一个叫做 machine id的值, machine id也就是设备ID,告诉 Linux内核自己是个什么设备,看看 Linux内核是否支持。 Linux内核是支持很多设备的,针对每一个设备(板子, Linux内核都用 MACHINE START和 MACHINE_END来定义一个 machine desc结构体来描述这个设备,比如在文件arch/ arm/mach-imx/machmx353d.c中有如下定义:

tmpDAB7.png

上述代码就是定义了“ Freescale MX35PDK”这个设备,其中 MACHINE START和MACHINE END定义在文件arch/ arm/include/asm/mach/arch.h中,内容如下

tmpC075.png

这里定义了一个 machine desc类型的结构体变量。变量存储在“.arch.info.init”段中。
MACH_TYPE_MX35_3DS定义在文件 include/ generated/mach- -types. h中,此文件定义了大量的内容如下所

tmpAEA5.png

2、使用设备树以后的设备匹配方法

当 Linux内核引入设备树以后就不再使用 MACHINE START了,而是换为了DT_MACHINE_START.定义在文件arch/ arm/include/asm/ mach/arch.h

tmpF2.png

可以看出, DT_MACHINE_START和 MACHINE_START基本相同,只是.nr的设置不同,在 DT_MACHINE_START里面直接将.nr设置为~0。说明引入设备树以后不会再根据 machineid来检査 Linux内核是否支持某个设备了。

tmp1050.png

machine_desc结构体中有个.dt compat成员变量,此成员变量保存着本设备兼容属性,只要某个设备(板子)根节点“/”的 compatible属性值与imx6ul_ dt_compat表中的任何一个值相等,那么就表示 Linux内核支持此设备。

八、设备树在系统中的体现

系统启动以后可以在根文件系统里面看到设备树的节点信息。在/proc/device-tree/目录下存放着设备树信息。

内核启动的时候会解析设备树,然后在/proc/device-tree/目录下呈现出来。

最后修改:2021 年 12 月 16 日
如果觉得我的文章对你有用,请随意赞赏