设备树的概念(一):设备树机制

设备树(DT)是一个易于阅读的硬件描述文件,具有类似json的格式化风格,这是一个简单的树结构,其中设备由节点及其属性表示。属性可以为空(仅有key,用于描述布尔值),也可以为key-value对,其中value可以包含任意字节流。本章是对DT的简单介绍。每个内核子系统或框架都有自己的DT绑定。我们将在讨论相关主题时讨论这些特定的绑定。dt起源于OF, OF是计算机公司认可的标准,其主要目的是为计算机固件系统定义接口。也就是说,您可以在http://www.devicetree.org/上找到更多关于DT规范的信息。本文将介绍DTs的基础知识,例如:

  • 命名约定,以及别名和标签
  • 描述数据类型及其APIs
  • 地址方案管理和设备资源访问
  • 实现OF匹配样式并提供特定于应用程序的数据

设备树机制

通过将CONFIG_OF选项设置为y,可以在内核中启用DT。为了从驱动程序中使用DT API,必须添加以下头文件:

#include <linux/of.h>
#include <linux/of_device.h>

DT支持几种数据类型。下面用一个示例节点描述来看看它们:

/* This is a comment */
// This is another comment
node_label: nodename@reg{
  string-property = "a string";
  string-list = "red fish", "blue fish";
  one-int-property = <197>; /* One cell in this property */
  int-list-property = <0xbeef 123 0xabcd4>; /*each number(cell)is a 32 bit integer(uint32). There are 3 cells in this property*/
  mixed-list-property = "a string", <0xadbcd45>, <35>, [0x01 0x23 0x45]
  byte-array-property = [0x01 0x23 0x45 0x67];
  boolean-property;
};

以下是DTs中使用的数据类型的一些定义:

  •  文本字符串用双引号表示。可以使用逗号创建字符串列表。
  • cell是由尖括号分隔的32位无符号整数。
  • 布尔数据只是一个空属性。值的真或假取决于属性是否存在。

命名约定

每个节点必须有一个<name>[@<address>]形式的名称,其中<name>是一个最多31个字符的字符串,[@<address>]是可选的,取决于节点是否表示可寻址设备。<address>应是用于访问设备的主地址。设备命名举例如下:

expander@20 {
    compatible = "microchip,mcp23017";
    reg = <20>;
    [...]
};

下面是另一个例子:

i2c@021a0000 {
    compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
    reg = <0x021a0000 0x4000>;
    [...]
};

另一方面,标签(label)是可选的。只有当一个节点的属性打算被另一个节点引用时,标记该节点才有用。您可以将标签视为指向节点的指针,这将在下文中解释。

Aliases, labels 和 phandle

理解这三个元素是如何工作的是非常重要的。它们经常用于DTs。下面就让DT君来解释一下它们的工作原理:

aliases {
    ethernet0 = &fec;
    gpio0 = &gpio1;
    gpio1 = &gpio2;
    mmc0 = &usdhc1;
    [...]
};

gpio1: gpio@0209c000 {
    compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
    [...]
};

node_label: nodename@reg {
    [...];
    gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
};

标签只是标记节点的一种方法,让节点由唯一的名称标识。在现实世界中,DT编译器将该名称转换为唯一的32位值。在上面的例子中,gpio1和node_label都是标签。然后可以使用标签来引用节点,因为标签对于节点是唯一的。指针句柄(phandle)是一个与节点关联的32位值,用于唯一标识该节点,以便可以从另一个节点中的属性引用该节点。标签用于有一个指向节点的指针。通过使用<&mylabel>,可以指向其标签为mylabel的节点。

&的使用就像在C编程语言中一样,获取元素的地址。

在前面的示例中,&gpio1被转换为一个phandle,它指向gpio1节点。下面的例子也是一样:

thename@address {
    property = <&mylabel>;
};

mylabel: thename@adresss {
    [...]
}

为了不遍历整个树来查找节点,引入了别名的概念。在DTs中,可以将别名节点视为快速查找表,即另一个节点的索引。可以使用find_node_by_alias()函数查找给定别名的节点。在DT源代码中并没有直接使用别名,而是由Linux内核加以引用。

DT编译器

DT有两种形式:文本形式(表示源代码,也称为DTS)和二进制blob形式(表示编译后的DT,也称为DTB)。源文件扩展名为.dts。实际上,还有.dtsi文本文件,表示SoC级别的定义,而.dts文件表示板卡级别的定义。您可以看到.dtsi作为头文件应该包含在.dts文件中,有点像在源文件(.c)中包含头文件(.h)。另一方面,二进制文件使用.dtb扩展名。

实际上还有第三种形式,即DT的运行时表示形式/proc/device-tree。

顾名思义,用于编译设备树的工具称为设备树编译器(dtc)。从根内核源代码可以编译一个独立的特定DT,也可以编译特定体系结构的所有DT。

让我们编译ARM soc的所有DT (.dts)文件:

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make dtbs

这是针对独立DT的:

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make imx6dl-sabrelite.dtb

在此例中,源文件名为“imx6dl-sabrelite.dts”。

给定一个已编译的设备树(.dtb)文件,您可以执行相反的操作并提取源文件(.dts):

dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/imx6dl-sabrelite.dtb 

出于调试的目的,向用户空间公开DT可能很有用。CONFIG_PROC_DEVICETREE配置变量将为您完成这一工作。然后,您可以在/proc/device-tree中探索和遍历DT。

posted @ 2023-03-12 15:43  闹闹爸爸  阅读(169)  评论(0编辑  收藏  举报