设备树dts详解

原文网址:设备树dts详解_IT利刃出鞘的博客-CSDN博客

DTS文件结构

1.dts文件布局

设备树布局


上图中的内容依次为:
dts版本
保留的内存区域。例:板子有64M内存,其中的2M想自己使用,不给内核。可以不写
根节点
[label:] 可以写也可以不写,它可以用于引用。

2 DTS文件组成及格式

2.1 DTS文件的组成

DTS文件主要由:  root­node、child­node、property、include组成。
    root­node:         由'/'表示,DT的Entry Point,所有设备均以子节点的形式处于根节点下
    child­node:        格式:node­name@address {};{}中是该node的内容,根节点下一般是platform设备和总线
    property:       属性,以key­ = value的形式表示,位于节点中
    include file:   包含其他源文件到dts中,dtsi一般是多个Machine公用的文件(i代表include)
                                例如:#include "skeleton.dtsi"   也可用/include/ "skeleton.dtsi"
                             include/dt-bindings路径是设备树可包含的头文件,常用的有:
                               interrupt-controller/irq.h   中断触发方式等   例:#define  IRQ_TYPE_EDGE_RISING  1
                               gpio/gpio.h                        GPIO配置            例:#define GPIO_ACTIVE_HIGH 0
                               dma/s3c2440_dma_ch.h    DMA的channel

2.2 设备树节点的格式

label:name@unit_address{
    ... ;
};
    label是标签,可不写;name是设备名;unit_address是设备地址,@address可不写,写它一般是为了区分多个设备名字相同。
    子节点可以和父节点同名,例如:memory下可以有memory节点,但很少有这样奇怪用法。
    每个大括号、属性后边都有分号。

3.特殊节点

node不一定是实际的设备,它还可以用来表示Runtime Configuration,例如bootargs,boot address。

3.1 root node

    根节点必须有四个属性:#address_cells, #size_cells, model, compatible

3.2 aliases

本节点不是必须的。
作用
1. 在引用节点时不需要写&了
2. 可以通过of_alias_get_id获得名字最后的数字。
    例:aliases{ i2c0 = &i2c1};  //aliases的属性还有另一种写法:i2c0  = "/i2c@address";
           id = of_alias_get_id(pdev->dev.of_node, "i2c")
           若匹配到的设备是i2c0,则id等于0
注意:aliases节点的命名:a-z, 0-9, - 只有这三种可以作为命名字符。

3.3 memory

本节点是必须的。
    内存节点必须有2个属性:device_type, reg。其中device_type的value必须为"memory"
    可选属性:initial-mapped-area

3.4chosen

本节点不是必须的,但一般都会有bootargs。
    有三个可选属性:bootargs, stdout-path, stdin-path。
chosen节点是个特殊节点,它不代表真实的设备,它是启动参数,例:
    chosen {
        bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
    };

3.5 cpus

本节点是必须的。它不是真实设备,它扮演子cpu节点的容器的角色。
    有两个必须的属性:#address-cells, #size-cells。

3.6 cpus/cpu*

    必须的属性(实际也不是必须,韦东山只用了一个compatible):
        device_type (值必须为"cpu"),reg, 

4. 示例

/{                                     //根节点
    node1{                        //node1是节点名,是/的子节点
        key=value;              //node1的属性
        ...
        node2{                    //node2是node1的子节点
            key=value;          //node2的属性
            ...
        }
    }                                 //node1的描述到此为止
    node3{
        key=value;
        ...
    }
}

DTS语法

Property,Label and Reference

1.Property

    属性(Property)是DT中描述设备的原语,其形式为:key = value。key可以分为:dt保留的key和厂商(soc厂商或者外设厂商)自定义的key。这些key的意义以及写法介绍,在Documents/devicetree/bindings/目录下。dt保留的key有compatible, #address­-cells, reg,厂商自定义的key举例:goodix,irqgpio。
    key种类是内核约定俗成的,并非IEEE或者Linux的。key的命名约定俗成的做法:使用 vendor,prop形式。属性Compatible也应遵守"vendor,model"的形式。例如qcom,qcom,clk­rates;goodix,irq­gpio  

2. key的分类

a. arrays of cells。
    1个或者多个32位数据。cell:一个32位的数据。
    格式:尖括号,用空格隔开。
    例:my_value = <1 0x2 0x12345678>
          64位的数据:使用两个cell来表示:
          my_value = <0x11111111 0x22222222>

b. string
    格式:双引号
    例:my_str = “my str”

c. bytestring
    格式:中括号,以字节为单位的两个16进制数,必须是两个,不能简写。可以用空格隔开,空格也可以省略。
    例1:my_byte1 = [aa bb]
             my_byte2 = [ccddeeff]

d. boolprop
     布尔类型只有key没有value。定义了bool­prop这个key则代表这个属性为true,若没定义则代表该属性为false。

    可以将abc三种组合起来,中间用逗号隔开。例:my_mix = "str",<0x12345678>, [ff dd]
    一般会将同种类型的放一起,例: my_str2 = “hi”,”str”
    key也可以进行引用:   refprop = <&othernode>

3. dt保留的key及其意义

以下:string简写为str,stringlist简写为strs,unsigned int简写为u32,reference简写为ref

本处由于尖括号会隐藏,全部添加“\”

key

类型

释义

compatible

 strs

用于device与driver匹配。除了chosen节点,一般都有此key。例:compatible = “sumsung,i2c”

model

 str

厂商设备型号。若compatible一样,用此项分辨。推荐格式:"manufacturer,model"  韦东山示例: "JZ2440"

reg

u32

设备或者cpu的地址空间。格式:reg = \<address0 length0 address1 length1>;
                                        这样也可以:reg = \<address0 length0\>,\<address1 length1\>;

cpu节点只需要地址,不需要写长度,而其他设备需要写长度。

#address­-cells

u32

子节点reg属性的地址数据的个数(即:几个u32)。例:#address-cells = \<1\>

#size­-cells

u32

子节点reg属性的地址长度的个数(即:几个u32)。例:#size-cells = \<1\>

interrupt-controller

一个空的属性,表示是中断控制器。

例:gic: interrupt-controller@36000000 {

interrupt-controller;

#interrupt-cells

u32

子节点interrupts属性用多少个u32来描述中断。

例:gic: interrupt-controller@36000000 {

#interrupt-cells = \<3\>;

则其他要使用gic的控制器的interrupts都要是3个u32描述,例:

uart0: serial@10100000 {

        interrupts = \<GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH\>;

interrupt-­parent

ref

该设备的中断连接到的interrupt controller。例:interrupt-parent=\<&gpf\>

无此属性的节点继承其父节点的interrupt-parent属性。

interrupts

 u32

一个数:中断号。

两个数:第1个:中断号;第2个:中断触发方式。

三个数:第1个:共享中断(GIC_SPI)还是独立中断(SPI_PPI);第2个:中断号;第3个:中断触发方式。

示例:interrupts = \<GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH\>;

备注:

ARM SMP cores are often associated with a GIC, providing per processor interrupts (PPI), shared processor interrupts (SPI) and software generated interrupts (SGI).

第1个:

取值见:include\dt-bindings\interrupt-controller\arm-gic.h

#define GIC_SPI 0

#define GIC_PPI 1

第2个(中断号):

如果是GIC_SPI,本处的值是实际中断号减32。例:uart0中断号是124,则本处要填92.

如果是GIC_PPI,本处的值是实际中断号减16.

第3个(触发方式):

取值:/include/dt-bindings/interrupt-controller/irq.h

#define IRQ_TYPE_NONE 0

#define IRQ_TYPE_EDGE_RISING 1

#define IRQ_TYPE_EDGE_FALLING 2

#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)

#define IRQ_TYPE_LEVEL_HIGH 4

#define IRQ_TYPE_LEVEL_LOW 8

interrupts-extended

u32

类似interrupts。节点有多个父节点(中断控制器)时,必须用此项。

例:interrupts-extended = \<&intc, 0, 0, 0, 3\>, \<&gpg, 11, 3\>;

&intc:要发给哪个中断控制器,后边:哪个中断,其父节点指定的

 #interrupt-cells是除了第一个引用(父中断控制器)之外的个数。

描述的详细含义见:Documentation\devicetree\bindings\ interrupt-controller\samsung,s3c24xx-irq.txt

phandle

 u32

节点的编号,每个节点都不一样。用来让别的节点对此节点引用

status

 str

设备状态,有"okey","disabled","failed","fail-sss"。有的"ok" 等于 "okey"

virtual-reg

u32

reg属性的物理首地址的有效映射地址,一般用不到。

ranges

u32

父子节点的映射关系。见:从零开始写设备树DTS

dma-ranges

u32

父子节点的映射关系。与DMA有关,类似ranges

name

str

节点的名字。 即将弃用

device-type

str

节点类型。即将弃用

b. reg详解:
       其他reg节点的用法见:从零开始写设备数DTS    在网页内搜索“内存映射设备”

4. 引用其他节点或属性

法一:phandle

 节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)
例:
global_intc : global_intc@10000000 {
    interrupt-controller;
    phandle = <1>;
};

gpf : gpf {
    gpio-controller;
    #gpio-cells = <0x2>;
    interrupt-controller;
    interrupt-parent = <1>    //表示其连接到的控制器是global_intc
                                             //本处也可以用interrupt-parent = <&global_intc>,这种方法见法二 
    #interrupt-cells = <0x2>;
    phandle = <0x6>;
};

ethernet@20000000 {
    compatible = "davicom,dm9000";
    reg = <0x20000000 0x2 0x20000004 0x2>;
    interrupt-parent = <6>;    //表示其连接到的控制器是gpf
                                              //本处也可以用interrupt-parent = <&gpf>,这种方法见法二
    interrupts = <7 IRQ_TYPE_EDGE_RISING>;
    local-mac-address = [00 00 de ad be ef];
    davicom,no-eeprom;
};

法二:labels(标签) 

        标签可以用于节点和属性。标签用于节点时可在任意地方引用该标签而不用关注该标签的全路径;
当标签应用于属性时,可在其他属性中引用该标签,避免重复的工作。例如Label一个字符串,避免在每个需要的地方重复写同一个字符串。(全路径是节点在树形结构中的位置,即从根节点索引到该节点所经过的所有父节点组合)

例如:
带标签的例子:
        带标签的节点定义
         /{
              i2c0: i2c@ff121288 {
                  compatible = "arm,s3c2440_i2c";
                  reg = <0x10100000 0x1000>;
              };
              msmgpio: gpio@fd510000 {
                  msmgpiocomp: compatible = "msmgpio";
              };
          }
            // 以上i2c0,msmgpio,msmgpiocomp这三个都是标签
         
        对带标签的节点的引用(此时,就向i2c0节点添加了属性)
            &i2c0 {
               status = "ok";
            }

不带标签的例子:
        不带标签的节点定义
         /{
              i2c@ff121288 {
                  compatible = "arm,s3c2440_i2c";
                  reg = <0x10100000 0x1000>;
              };
              gpio@fd510000 {
                  compatible = "msmgpio";
              };
          }
         
        对不带标签的节点的引用(此时,就向i2c0节点添加了属性)
            / {
                i2c@ff121288 {
                    goodixts@5d {
                        goodix,rstgpio = <&msmgpio 16 0x00>;
                    };
                };
            };
                
        另外,也可以使用alias为标签定义别名,如: alias { i2c_0 = &i2c0}; 这样在引用的地方就不用
    再写&号了。    

5.引用、合并、替换

节点引用、合并节点内容、替换节点内容。
/* 参考板已经写好的node节点 */
/{
  node:node@80000000{
    item1 = value1;
  };
};

引用
/*移植者的*/
/{
  demo{
    item2 = <&node>
  };
};

合并

/*移植者的*/
/{
  &node{
    item2 = value2;
  };
};

结果:
/{
  node{
    item1 = value1;
    item2 = value2;
  };
};

修改

/*移植者的*/
/{
  &node{
    item1 = value2;
  };
};

其他语法

1. 可以用像C语言一样使用#define宏定义,可以使用//注释单行,用注释多行

2. 属性有多个值时会一一对应

设备树示例:
/ {
    platform_device_test,test{
        compatible = "dts_test";
        #address-cells = <1>;
        #size-cells = <1>;

        reg = <0x500 0x32>,<0x200 0x32>;
        reg-names = "test mem1","resource2 MEM";

        interrupt-parent = <&vic0>;
        interrupts = <0x11 2>,<0x12 2>;
        interrupt-names = "test irq1","resource4 IRQ";
    };
};    
上边例子中,
<0x500 0x32>对应"test mem1",<0x200 0x32>对应"resource2 MEM"
<0x11 2>对应"test irq1",<0x12 2>对应"resource4 IRQ"
设备树或者平台驱动的一些函数有与这个顺序有关的参数,例如:
of_get_named_gpio(pdev->dev.of_node, “eint-pins”, i);
platform_get_resource(pdev, IORESOURCE_MEM, i)
上边两个函数的的参数i,就是这个顺序,从0开始。

常用函数

        读取属性的值(见《设备树笔记》=>第04节=>设备树分布图)。
        device_node.properties->value  此指针指向dtb文件的structure block的相应位置(该节点属性的起始地址),大端存储,以字节为单位存储,可以将其作为整型数据读出,也可以作为字符串读出。

dtb -> device_node -> platform_device
include/linux/目录下有很多of开头的头文件

a. 处理DTB

of_fdt.h           // dtb文件的相关操作函数, 我们一般用不到, 因为dtb文件在内核中已经被转换为device_node树(它更易于使用)

b. 处理device_node

of.h               // 提供设备树的一般处理函数           

函数名

作用

示例

of_property_read_u32

读取某个属性的u32值

unsigned int reg_val = 0;

ret = of_property_read_u32(pdev->dev.of_node, “reg”, ®_val);

if(ret < 0){

    printk(“read error\n”);

}

of_alias_get_id

获得alias下的节点的编号。

int id = 0;

id = of_alias_get_id(pdev->dev.of_node, “i2c”);

对于以下设备树,返回的id为0.

aliases {

i2c0    = &i2c_bus0;

};

of_get_child_count

获取某个device_node的子节点数

of_get_named_gpio

通过名字获得GPIO的数值

unsigned int pin, i;

pin = of_get_named_gpio(pdev->dev.of_node, “eint-pins”, i)

 of_address.h       // 地址相关的函数,

函数名

作用

示例

of_get_address

获得reg属性中的addr, size值

of_dma.h           // 设备树中DMA相关属性的函数
of_gpio.h           // GPIO相关的函数
of_graph.h         // GPU相关驱动中用到的函数, 从设备树中获得GPU信息
of_iommu.h         // 很少用到
of_irq.h               // 中断相关的函数
of_mdio.h          // MDIO (Ethernet PHY) API
of_net.h            // OF helpers for network devices. 
of_pci.h            // PCI相关函数
of_pdt.h           // 很少用到
of_reserved_mem.h  // reserved_mem的相关函数

c. 处理 platform_device

of_platform.h   // 把device_node转换为platform_device时用到的函数

函数名

作用

示例

of_device_alloc

根据device_node分配设置platform_device

of_find_device_by_node

根据device_node查找到platform_device

of_platform_bus_probe

处理device_node及它的子节点

of_device.h        // 设备相关的函数

函数名

作用

示例

of_match_device

从matches数组中取出与当前设备最匹配的一项

d. 其他函数

函数名

作用

示例

platform_get_resource

通过序号获得 “reg”属性对应的resource。

设备树:

reg = <0x500 0x32>,<0x600 0x31>

reg-names = "test mem1","resource2 MEM";

程序:

struct resource *res;

struct platform_device *pdev;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0)

i2c->base = devm_ioremap_resource(&pdev->dev, res); 

//i2c->base类型为void __iomem *

platform_get_resource_byname

通过名字获得 “reg”属性对应的resource

设备树:

reg = <0x500 0x32>,<0x600 0x31>

reg-names = "test mem1","resource2 MEM";

//获得名字为“test mem1”对应 “reg” 的resource

res = platform_get_resource(pdev, IORESOURCE_MEM, “test mem1”);

platform_get_irq

通过序号获得中断的resource

设备树:

interrupts = <23>,<24>;

interrupt-names = "test irq1","resource4 IRQ";

程序:

u32 irq;

//获得第0个。

irq = platform_get_irq(pdev, IORESOURCE_IRQ, 0);

ret = devm_request_irq( , irq, ...);

platform_get_irq_byname

获得名字为“test irq1”对应的resource

//获得名字为“test irq1”对应的resource

platform_get_irq_byname(pdev, IORESOURCE_IRQ, “test irq1”);

其他网址

设备树用法(官方英文)                 前文部分翻译

设备树总体详细机制

of_alias_get_id 函数与设备树中aliases节点的关系【转】

其他文档

内核源码相关文档

Documentation\devicetree\bindings\interrupt-controller\interrupts.txt
Documentation\devicetree\bindings\外设子系统路径
节点,属性,串口和网络设备的详细介绍见《devicetree-specification-v0.2.pdf》

头文件路径

arch\arm\boot\dts\include\dt-bindings\外设子系统路径    
include\dt-bindings\外设子系统路径       

其他文档

1.韦东山设备树视频附带文件jz2440.dts
2.《Linux设备驱动开发详解》第18章   

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT利刃出鞘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值