设备树介绍

一、背景介绍

二、工作流程

通常由.dts文件以文本方式对系统设备树进行描述,经过Device Tree Compiler(dtc)dts文件转换成二进制文件binary device tree blob(dtb).dtb文件可由Linux内核解析,有了device tree就可以在不改动Linux内核的情况下,对不同的平台实现无差异的支持,只需更换相应的dts文件,即可满足。
 

在系统启动的时候,bootloader将dtb传递给内核,内核解析改文件得到一个个的device_node,linux driver初始化的时候回去获取device_node定义好的资源。

三、基本语法

1、设备节点信息

设备树是一个由节点和属性组成的简单树状结构。属性是键值对,节点可包含属性和子节点。如下是一个简单的设备树的例子:

/dts-v1/;

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        child-node1 {
        };
    };
};

很明显,上面的设备树并没有实际意义,因为它没有描述任何设备,但是它展示了节点和属性的结构,如下:

  • 一个简单的根节点:”/”
  • 一对子节点:”node1” and “node2”
  • “node1”的一对子节点:”child-node1” and “child-node2”
  • 一堆分散在树的属性

属性是简单的键值对,它可以为空,也可以是任意的字节流。

基本类型
字符串 文本字符串(以空字符结尾)用双引号表示:
string-property = “a string”;
字符串列表 逗号分隔
string-list = “red fish”, “blue fish”;
cell

尖括号括起来 ,例如<1 0x3 0x123>,每个成员都是32bit无符号整形数据,每个成员都是一个cell,称为arrays of cells;

二进制数据

二进制数据用中括号‘[]’限定
binary-property = [0x01 0x23 0x45 0x67];

用中括号括起来的字节序列(byte string)[xx xx xx ...],使用16进制表示1个/多个byte,需要注意的是,byte string中,一个byte必须用2位16进制数来表示,例如[00 11 22],其中00不能简写为0,但是byte之间的空格可以省略,也就是[00 11 22]和[001122]是一样的;

自由组合 多种不同类型的数据之间用逗号分隔
mixed-property = “a string”, [0x01 0x23 0x45 0x67], <0x12345678>;

2、设备节点及 label 的命名

下面我们看一个节点命令的示例:cpus和intc是节点,cpu0是cpu的一个子节点。
设备树介绍_第1张图片

 

我们注意到节点和子节点之间的命名有所不同,它们都遵循了下面的命名格式:

node-name@unit-address

node-name 是必选项,name 是一个 ASCII 字符串,用于描述节点对应 的设备类型,如:“uart1”,可以清晰的描述出设备的功能。

unit-address 则是可选项如果一个节点描述的设备有地址,则应该给出@unit-address

这个地址并不是驱动中操作的某一个寄存器地址,而是作为区分用的。多个相同类型设备节点的 name 可以一样,只要 unit-address 不同即可,如 cpu@0cpu@1 以及 serial@101f0000 serial@101f2000 这样的同名节点。

当我们找一个节点的时候,必须书写完整的节点路径,如果节点的路径很长,在引用的时候就非常不方便,所以,设备数允许给设备取一个别名(lable)帮助我们更加方便地索引节点。

label: node-name@unit-address

如:“cpu0:cpu@0”,和上面的命名格式差不多,前面多了一个 label,称作节点标签。label 的作用就是为了方便访问节点,可以直接通过&label 来访问这个节点,比如通过“&cpu0”就可以访问“cpu@0”这 个节点,而不用输入完整的节点名字。在例如节点“intc: interrupt-controller@00a01000”,通过&intc 来访 问比节点名字方便很多。

四、常用属性

设备书规范预定义了一些标准的属性

1、compatible

每个节点都要有一个compatible属性。

compatible 属性的值是一个字符串列表,compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序,compatible 属性的值格式如下所示:

compatible = "manufacturer,model"

manufacturer 表示厂商,model 一般是模块对应的驱动名字。

例如:

   sound {undefined
          compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
           . . . . . .

         }

上述实例中compatible属性有两个值,分别是“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl” 表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字,这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。

那么怎么找?去哪里找?

一般驱动程序文件都会有一个OF匹配表,此OF匹配表保存着一些compatible值,如果设备节点的compatible属性值和OF匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。

比如在文件imx-wm8960.c中有如下内容

static const struct of_device_id imx_wm8960_dt_ids[] = {
    { .compatible = "fsl,imx-audio-wm8960", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);
 
static struct platform_driver imx_wm8960_driver = {
    .driver = {
        .name = "imx-wm8960",
        .pm = &snd_soc_pm_ops,
        .of_match_table = imx_wm8960_dt_ids,
    },
    .probe = imx_wm8960_probe,
    .remove = imx_wm8960_remove,
};


imx_wm8960_dt_ids 就是 imx-wm8960.c 这个驱动文件的匹配表,此匹配表只有一个匹配值“fsl,imx-audio-wm8960”。如果在设备树中有哪个节点的 compatible 属性值与此相等,那么这个节点就会使用此驱动文件
 

status 属性

status 属性是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息,默认情况下不设置该属性是使能状态(okay)

dtsi 文件中定义了很多设备,但是在你的板子上某些设备是没有的。这时你可以给这个设备节点添加一个 status 属性,设置为“disabled”.

Value Description
"okay" 设备正常运行
"disabled" 设备不可操作,但是后面可能恢复工作
"fail" 发生了严重错误,需修复
"fail-sss" 发生了严重错误,需要修复;sss表示错误信息

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

可寻址的设备可以使用两种方式来声明设备地址,第一种是结点名@地址,第二种是在结点内部使用reg属性指定。

每一个可寻址的设备都有一个”reg”属性,其是以reg = 格式组成的一系列元组,每一个元组代表了设备使用的地址空间,包含起始地址和大小。还有另外一个问题,地址和大小用几个u32表示呢?这个就由父节点的"#address-cells","#size-cells"属性确定。

    cpus {
        #address-cells = <1>;   //地址个数为1
        #size-cells = <0>;      //length个数为0,即只有地址,没有长度

        cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a8";
            reg = <0>;
        };
    };

        amba {
            #address-cells = <1>;  // 地址个数为1
            #size-cells = <1>;     // 长度个数为1
            compatible = "arm,amba-bus";
            ranges;

            pdma0: dma@e0900000 {
                compatible = "arm,pl330", "arm,primecell";
                reg = <0xe0900000 0x1000>;//起始地址0xe0900000,长度0x1000
                interrupt-parent = <&vic0>;
                interrupts = <19>;
                clocks = <&clocks CLK_PDMA0>;
                clock-names = "apb_pclk";
                #dma-cells = <1>;
                #dma-channels = <8>;
                #dma-requests = <32>;
            };

        };

ranges和dma-ranges属性

我们之前讨论了如何给设备分配地址,但是这个地址仅仅是相对于当前的设备节点而言的。我们还没有介绍如何将这个地址转换成CPU能够使用的地址。

根节点描述了从CPU侧看的地址分配情况,根节点的子节点已经使用了CPU的地址域,因此无需做任何显性的映射。例如我们为串口设备”serial@101f0000”直接分配CPU地址0x101f0000。

注意那些不是根节点的子节点没有使用CPU的地址域。为了得到映射地址,设备树必须指明如何实现一个地址域到另一个地址域的转换。ranges属性就是为这个目的设置的。

总结:

1、ranges属性值的格式 <local地址, parent地址, size>, 表示将local地址向parent地址的转换。

比如对于#address-cells和#size-cells都为1的话,以<0x0  0x10 0x20>为例,表示将local的从0x0~(0x0 + 0x20)的地址空间映射到parent的0x10~(0x10 + 0x20)

其中,local地址的个数取决于当前含有ranges属性的节点的#address-cells属性的值,size取决于当前含有ranges属性的节点的#size-cells属性的值。

parent地址的个数取决于当前含有ranges属性的节点的parent节点的#address-cells的值。

2、对于含有ranges属性的节点的子节点来说,其reg都是基于local地址

3、ranges属性值为空的话,表示1:1映射,说明子地址空间和父地址空间完全相同,不需要进行地址转换。

4、对于没有ranges属性的节点,代表不是memory map区域


'dma-ranges'属性的结构和定义与'ranges'属性完全相同,唯一不同的是地址是dma使用的地址,'ranges'中的地址是cpu使用的地址。

五、设备树调试

当烧录到开发板后,可以通过

系统启动后进入到/sys/firmware/devicetree/base目录可以看到当前已注册设备的设备树信息,通过相关命令可以查看当前设备的结点信息、状态等。

也可以通过/proc/device-tree/目录查看已经注册的设备树信息,这两个目录的内容是一样的

ref:

Device Tree(一):背景介绍

Device Tree(二):基本概念

设备树中ranges属性分析(1) - 摩斯电码 - 博客园

5. 设备树的规范 - DTS格式 - 简书

linux设备树笔记--dts基本概念及语法

一文带你搞懂设备树 - 云+社区 - 腾讯云

Linux device tree(设备树)_往事随风的博客-CSDN博客

2.1设备树的规范(dts和dtb)——DTS格式_一个嵌入式软件工程师的博客-CSDN博客_设备树规范

【linux】设备树dts文件中节点的属性compatible #address-cells 和#size-cells reg属性_那可真是太开心了的博客-CSDN博客_dts reg属性

你可能感兴趣的:(嵌入式linux驱动,linux,运维,服务器)