Linux DTS (Device Tree Source)设备树源码

49 篇文章 23 订阅
34 篇文章 14 订阅

1. 简介

  • 概念:设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS(Device Tree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如 CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等。如下图所示
    在这里插入图片描述

  • 树的主干就是系统总线,IIC 控制器、GPIO 控制器、SPI 控制器等都是接 到系统主线上的分支。IIC 控制器有分为 IIC1 和 IIC2 两种,其中 IIC1 上接了 FT5206 和 AT24C02 这两个 IIC 设备,IIC2 上只接了 MPU6050 这个设备。DTS 文件的主要功能就是按图所示的结构来描述板子上的设备信息。

  • DTS 是为 Linux 提供一种硬件信息的描述方法,以此代替源码中的 硬件编码 (hard code)。DTS 即 Device Tree Source 设备树源码, Device Tree 是一种描述硬件的数据结构,起源于 OpenFirmware (OF). 在 Linux 2.6 中, ARM 架构的板级硬件细节过多的被硬编码在 arch/arm/plat-xxx 和 arch/arm/mach-xxx (比如板上的 platform 设备,resource, i2c_board_info, spi_board_info 以及各种硬件的 platform_data), 这些板级细节代码对内核来讲只不过是垃圾代码。而采用 Device Tree 后, 许多硬件的细节可以直接透过它传递给 Linux,而不再需要在 kernel 中 进行大量的冗余编码。

  • DTS设备树描述文件中什么代表总线,什么代表设备

    • 设备:一个含有compatible属性的节点就是一个设备
    • 总线:包含一组设备节点的父节点即为总线

1.1 引入DTS的原因

  • 对ARM平台的相关code做出如下相关规范调整,这个也正是引入DTS的原因:
    • ARM的核心代码仍然保存在arch/arm目录下
    • ARM SoC core architecture code保存在arch/arm目录下
    • ARM SOC的周边外设模块的驱动保存在drivers目录下
    • ARM SOC的特定代码在arch/arm/mach-xxx目录下
    • ARM SOC board specific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息。
  • 本质上,Device Tree改变了原来用hardcode方式将HW 配置信息嵌入到内核代码的方法,改用bootloader传递一个DB的形式。
  • 如果我们认为kernel是一个black box,那么其输入参数应该包括:
    • 识别platform的信息
    • runtime的配置参数
    • 设备的拓扑结构以及特性
  • 对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交给内核,此外, 还需要把上述的三个参数信息传递给kernel,以便kernel可以有较大的灵活性。在linux kernel中,Device Tree的设计目标就是如此。

1.2 文件格式

缩写描述
DTS(.dts)设备树源文件(描述板级信息:开发板上有哪些 IIC 设备、SPI 设备等)
DTS(.dtb)设备树编译文件
.dtsi设备树头文件(描述SOC级信息:CPU 架构、主频、外设寄存器地址范围等)
DTC将.dts 编译为.dtb:DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了Device Tree的情况下, 编译内核的时候主机工具dtc会被编译出来,对应scripts/dtc/Makefile中的“hostprogs-y := dtc”这一hostprogs编译target。 在Linux内核的arch/arm/boot/dts/Makefile中,描述了当某种SoC被选中后,哪些.dtb文件会被编译出来
  • 设备树相关文件均在 arch/arm/boot/dts/ 文件夹
  • DTC 工具源码在 Linux 内核的 scripts/dtc 目录下
  • DTC 工具依赖于 dtc.c、flattree.c、fstree.c 等文件,最终编译并链接出 DTC 这个主机文件
  • 在 arch/arm/boot/dts/Makefile 中新增需要编译的DTS文件。
  • 如果要编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命令:
    • make all:编译 Linux 源码中的所有东西,包括 zImage,.ko 驱动模块以及设备 树
    • make dtbs:仅编译设备树

2. DTS语法

  • DTS中常用符号的含义
符号描述
/根节点
@如果设备有地址,则由此符号指定
&引用节点
冒号前的label是为了方便引用给节点起的别名,此label一般使用为&label
属性名称中可以包含逗号。如compatible属性的名字 组成方式为”[manufacturer], [model]”,加入厂商名是为了避免重名。自定义属性名中通常也要有厂商名,并以逗号分隔。
##并不表示注释。如 #address-cells ,#size-cells 用来决定reg属性的格式
空属性并不一定表示没有赋值。如 interrupt-controller 一个空属性用来声明这个node接收中断信号数据类型
””引号中的为字符串,字符串数组:”strint1”,”string2”,”string3”
< >尖括号中的为32位整形数字,整形数组<12 3 4>
[ ]方括号中的为32位十六进制数,十六机制数据[0x11 0x12 0x13] 其中0x可省略

2.1 .dtsi头文件

  • 在 .dts 设备树文件中,可以通过“#include ”来引用 .h 、 .dtsi 和 .dts 文件。
  • .dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART 、 IIC 等等。

2.2 设备节点

  • 设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是 键值对。每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。
  • .dts举例说明
/ { 
    aliases { 
        can0 = &flexcan1; 
    };

    cpus { 
        #address-cells = <1>;
        #size-cells = <0>;

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

    intc: interrupt-controller@00a01000 {
        compatible = "arm,cortex-a7-gic";
        #interrupt-cells = <3>;
        interrupt-controller;
        reg = <0x00a01000 0x1000>,
        <0x00a02000 0x100>;
    };
}
  • “/”是根节点,每个设备树文件只有一个根节点。

2.2.1 设备树中节点命名格式

  • 节点标签:节点名@设备的地址或寄存器首地址
label: node-name@unit-address ​​​
// e.g.:
cpu0:cpu@0 

2.2.2 设备树源码中常用的几种数据类型

1) 字符串

compatible ="arm,cortex-a7";

上述代码设置 compatible 属性的值为字符串“arm,cortex-a7”。

2) 32 位无符号整数

reg =<0>;

上述代码设置 reg 属性的值为 0,reg 的值也可以设置为一组值,比如: reg =<0 0x123456 100>;

3) 字符串列表

属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:

compatible ="fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

上述代码设置属性 compatible 的值为“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。

2.2.3 标准属性

  • 节点是由一堆属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性,Linux 下的很多外设驱动都会使用这些标准属性
属性名描述
compatiblecompatible 属性也叫做“兼容性”属性。 用于将设备和驱动绑定起来
值是一个字符串列表,用于选择设备所要使用的驱动程序
model值是字符串,用于描述设备模块信息,例如名字
status值是字符串,设备的状态信息
regreg意为region,区域。值一般是(address,length)对。
用于描述设备地址空间资源信息,指定某个外设的寄存器地址范围信息
格式为reg = <address1 length1 [address2 length2] [address3 length3]>
#address-cells
#size-cells
值是无符号 32 位整数。可以用在任何拥有子节点的设备中,用于描述子节点的地址信息
#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位 ),即决定了address1/2/3分别包含几个cell
#size-cells 属性值决定了子节点 reg 属性中长度信息所占的 字长 (32 位 ),即决定了length1/2/3分别包含了几个cell
rangesranges 是一个地址映射/ 转换表, ranges 属性每个项目由子地址、父地址和地址空间长度 这三部分组成
如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换
name值是字符串,name 属性用于记录节点名字。 name 属性已经被弃用,不推荐使用 name 属性,一些老的设备树文件可能会使用此属性
device_type值是字符串,IEEE 1275 会用到此属性,用于描述设备的 FCode ,但是设 备树没有 FCode ,所以此属性也被抛弃了。此属性只能用于 cpu 节点或者 memory 节点
interrupt-controller一个空属性用来声明这个node接收中断信号
#interrupt-cells这是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符
interrupt-parent标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的
interrupts一个中断标识符列表,表示每一个中断输出信号

2.3 DTS的加载过程

  • 如果要使用Device Tree,首先用户要了解自己的硬件配置和系统运行参数,并把这些信息组织成Device Tree source file。 通过DTC(Device Tree Compiler),可以将这些适合人类阅读的Device Tree source file变成适合机器处理的 Device Tree binary file(有一个更好听的名字,DTB,device tree blob)。在系统启动的时候,boot program (例如:firmware、bootloader)可以将保存在flash中的DTB copy到内存(当然也可以通过其他方式, 例如可以通过bootloader的交互式命令加载DTB,或者firmware可以探测到device的信息,组织成DTB保存在内存中), 并把DTB的起始地址传递给client program(例如OS kernel,bootloader或者其他特殊功能的程序)。 对于计算机系统(computer system),一般是firmware->bootloader->OS,对于嵌入式系统,一般是bootloader->OS。
    在这里插入图片描述

2.4 DTS的描述信息

  • 设备树的组成:节点+属性

    • Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性, 其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):
    • CPU的数量和类别
    • 内存基地址和大小
    • 总线和桥
    • 外设连接
    • 中断控制器和中断使用情况
    • GPIO控制器和GPIO使用情况
    • Clock控制器和Clock使用情况
  • CPU、总线和设备组成的树

    • 它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树, 并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源, 也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。
  • 只描述无法动态探测到的设备

    • 是否Device Tree要描述系统中的所有硬件信息?答案是否定的。基本上,那些可以动态探测到的设备是不需要描述的, 例如USB device。不过对于SOC上的usb hostcontroller,它是无法动态识别的,需要在device tree中描述。同样的道理, 在computersystem中,PCI device可以被动态探测到,不需要在device tree中描述,但是PCI bridge如果不能被探测,那么就需要描述之。
  • .dtsi包含公共部分

    • .dts文件是一种ASCII 文本格式的Device Tree描述,此文本格式非常人性化,适合人类的阅读习惯。
    • 基本上,在ARM Linux中,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。
    • 由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分, Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi,类似于C语言的头文件。 其他的machine对应的.dts就include这个.dtsi。
    • 例如,对于RK3288而言, rk3288.dtsi就被rk3288-chrome.dts所引用, rk3288-chrome.dts有如下一行:#include“rk3288.dtsi”, 对于rtd1195,在 rtd-119x-nas.dts中就包含了/include/ ”rtd-119x.dtsi” 当然,和C语言的头文件类似,.dtsi也可以include其他的.dtsi,譬如几乎所有的ARM SoC的.dtsi都引用了skeleton.dtsi,即#include”skeleton.dtsi“ 或者 /include/ “skeleton.dtsi”
  • DTS合并

    • 正常情况下所有的dts文件以及dtsi文件都含有一个根节点”/”,这样include之后就会造成有很多个根节点? 按理说 device tree既然是一个树,那么其只能有一个根节点,所有其他的节点都是派生于根节点的child node. 其实Device Tree Compiler会对DTS的node进行合并,最终生成的DTB中只有一个 root node.
  • 节点

    • device tree的基本单元是node。这些node被组织成树状结构,除了root node,每个node都只有一个parent。 一个device tree文件中只能有一个root node。每个node中包含了若干的property/value来描述该node的一些特性。
    • 每个node用节点名字(node name)标识,节点名字的格式是node-name@unit-address。如果该node没有reg属性(后面会描述这个property), 那么该节点名字中必须不能包括@和unit-address。unit-address的具体格式是和设备挂在那个bus上相关。例如对于cpu, 其unit-address就是从0开始编址,以此加一。而具体的设备,例如以太网控制器,其unit-address就是寄存器地址。root node的node name是确定的,必须是“/”。 在一个树状结构的device tree中,如何引用一个node呢?要想唯一指定一个node必须使用full path,例如/node-name-1/node-name-2/node-name-N。

2.3 DTS举例

  • 1个双核ARM Cortex-A9 32位处理器;
    • ARM的local bus:上的内存映射区域分布了2个串口(分别位于0x101F1000 和 0x101F2000)、 GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10115000)、中断控制器(位于0x10140000)和一个external bus桥;
    • External bus:桥上又连接了SMC SMC91111 Ethernet(位于0x10100000)、I2C控制器(位于0x10160000)、64MB NOR Flash(位于0x30000000);
    • External bus桥上连接的I2C控制器所对应的I2C总线上又连接了Maxim DS1338实时钟(I2C地址为0x58)
  • 其对应的.dts文件为:
/ {  
    compatible = "acme,coyotes-revenge";  
    #address-cells = <1>;  
    #size-cells = <1>;  
    interrupt-parent = <&intc>;  
  
    cpus {  
        #address-cells = <1>;  
        #size-cells = <0>;  
        cpu@0 {  
            compatible = "arm,cortex-a9";  
            reg = <0>;  
        };  
        cpu@1 {  
            compatible = "arm,cortex-a9";  
            reg = <1>;  
        };  
    };  
  
    serial@101f1000 {  
        compatible = "arm,pl011";  
        reg = <0x101f1000 0x1000 >;  
        interrupts = < 1 0 >;  
    };  
  
    serial@101f2000 {  
        compatible = "arm,pl011";  
        reg = <0x101f2000 0x1000 >;  
        interrupts = < 2 0 >;  
    };  
   
 
    gpio@101f3000 {  
        compatible = "arm,pl061";  
        reg = <0x101f3000 0x1000  
               0x101f4000 0x0010>;  
        interrupts = < 3 0 >;  
    };  
  
    intc: interrupt-controller@10140000 {  
        compatible = "arm,pl190";  
        reg = <0x10140000 0x1000 >;  
        interrupt-controller;  
        #interrupt-cells = <2>;  
    };  
  
    spi@10115000 {  
        compatible = "arm,pl022";  
        reg = <0x10115000 0x1000 >;  
        interrupts = < 4 0 >;  
    };  
  
 
    external-bus {  
        #address-cells = <2>  
        #size-cells = <1>;  
        // address-cells=<2>  一个是片选序号,另一个是片选序号上的偏移量,表示挂载在外部总线上,
        //                    需要通过片选线工作的一些模块
        // 片选0,偏移0,被映射到CPU地址空间的0x10100000~0x10110000中,地址长度为0x10000(第一行)
        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet  
                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller  
                  2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash  
  
        ethernet@0,0 {  
            compatible = "smc,smc91c111";  
            reg = <0 0 0x1000>;  // 片选、偏移量、地址长度 (前2个cell为地址,后一个cell为长度)
            interrupts = < 5 2 >;  
        };  
  
        i2c@1,0 {  
            compatible = "acme,a1234-i2c-bus";  
            #address-cells = <1>;  
            #size-cells = <0>;  
            reg = <1 0 0x1000>;  // 片选、偏移量、地址长度
            rtc@58 {  
                compatible = "maxim,ds1338";  
                reg = <58>;  
                interrupts = < 7 3 >;  // 第一个值: 该中断位于它的中断控制器的索引
                                       // 第二个值:触发的type
            };  
        };  
  
        flash@2,0 {  
            compatible = "samsung,k8f1315ebm", "cfi-flash";  
            reg = <2 0 0x4000000>;  // 片选、偏移量、地址长度
        };  
    };  
};
  • 说明如下

  • root结点”/”的compatible 属性

    • compatible = “acme,coyotes-revenge”; 定义了系统的名称, 它的组织形式为:, 。Linux内核通过过root结点"/"的compatible 属性即可判断它启动的是什么machine
    • Linux 内核通过根节点 compatible 属性找到对应的设备的函数调用过程:
      在这里插入图片描述
  • 每个设备的compatible属性

    • 在.dts文件的每个设备,都有一个compatible属性,compatible属性用于驱动和设备的绑定
    • compatible 属性是一个字符串的列表, 列表中的第一个字符串表征了结点代表的确切设备,形式为”,",其后的字符串表征可兼容的其他设备。可以说前面的是特指, 后面的则涵盖更广的范围
  • #address-cells和#size-cells

    • 父结点的#address-cells和#size-cells分别决定了子结点的reg属性的address和length字段的长度
    • 在本例中,root结点的#address-cells = <1>;和#size-cells =<1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1
    • cpus 结点的#address-cells= <1>;和#size-cells =<0>;决定了2个cpu子结点的address为1,而length为空, 于是形成了2个cpu的reg =<0>;和reg =<1>;
    • external-bus结点的#address-cells= <2>和#size-cells =<1>; 决定了其下的ethernet、i2c、flash的reg字段形如reg = <0 0 0x1000>;、reg = <1 0 0x1000>;和reg = <2 0 0x4000000>;
  • ranges

    • ranges属性为一个地址转换表。表中的每一行都包含了子地址、父地址、在子地址空间内的区域大小。他们的大小(包含的cell) 分别由子节点的address-cells的值、父节点的address-cells的值和子节点的size-cells来决定。
    • 对于本例而言,子地址空间的#address-cells为2, 父地址空间的#address-cells值为1,因此0 0 0x10100000 0x10000的前2个cell为external-bus上片选0上偏移0, 第3个cell表示external-bus上片选0上偏移0的地址空间被映射到CPU的0x10100000位置,第4个cell表示映射的大小为0x10000。ranges的后面2个项目的含义可以类推。
    • 以第一行为例:
      • 0 0 两个cell,由子节点external-bus的address-cells=<2>决定
      • 0x10100000 一个cell,由父节点的address-cells=<1>决定
      • 0x10000 一个cell,由子节点external-bus的size-cells=<1>决定
    • 即第一行的含义:片选0,偏移量0(选中了网卡),被映射到CPU地址空间的0x10100000~0x10110000中,地址长度为0x10000
#address-cells= <1>;
#size-cells= <1>;
...
external-bus{
    #address-cells = <2>
    #size-cells = <1>;
    ranges = <0 0  0x10100000  0x10000     // Chipselect 1,Ethernet
              1 0  0x10160000  0x10000     // Chipselect 2, i2c controller
              2 0  0x30000000  0x1000000>; // Chipselect 3, NOR Flash
};
  • 节点名

    • node的名字自己可以随便定义,当然最好是见名知意,可以通过驱动程序打印当前使用的设备树节点 printk(“now dts node name is %s\n”,pdev->dev.of_node->name);
  • compatible
    在这里插入图片描述

    • compatible选项是用来和驱动程序中的of_match_table指针所指向的of_device_id结构里的compatible字段匹配的,只有dts里的compatible字段的名字和驱动程序中of_device_id里的compatible字段的名字一样,驱动程序才能进入probe函数

3. 在系统中查看设备树

  • Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的 /proc/device-tree 目录下根据节点名字创建不同文件夹和文件,/proc/device-tree 目录就是设备树在根文件系统中的体现。
cd /proc/device-tree
ls
# 即可查看所有的设备和属性
  • 特殊节点
    • aliases 子节点:主要功能就是定义别名,定义别名的目的就是为了方便访问节点
    • chosen 子节点:主要是为了 uboot 向 Linux 内核传递数据,重点是 bootargs 参数。bootargs 环境变量的值是在uboot 中设置的,而 uboot 中的 fdt_chosen 函数在设备树的 chosen 节点中加入了 bootargs 属性,并且还设置了 bootargs 属性值。

4. Linux内核解析DTB文件流程

  • Linux 内核在启动的时候会解析 DTB 文件,然后在/proc/device-tree 目录下生成相应的设备树节点文件
    在这里插入图片描述

5. 绑定信息文档

  • Linux 内核源码中有详细的 .txt 文档描述了如何添加节点,这些 .txt 文档叫做绑定文档。
  • 路径在Linux 源码目录: /Documentation/devicetree/bindings

6. 设备树常用操作函数

  • Linux 内核给我们提供了一系列函数来获取设备树中的节点或者属性信息,这一系列函数都有统一的前缀“of_ ”,也叫做 OF 函数。OF 函数原型都定义在 include/linux/of.h 文件。

6.1 查找节点的 OF 函数

  • 设备都是以节点的形式“挂”到设备树上的,因此要想获取这个设备的其他属性信息,必须先获取到这个设备的节点。Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件 include/linux/of.h 中,且of函数的实现位于kernel/drivers/of目录。
方法描述
通过节点名字
查找指定的节点
struct device_node * of_find_node_by_name (struct device_node *from, const char *name)
from :开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
name :要查找的节点名字。
返回值 :找到的节点,如果为 NULL 表示查找失败。
通过 device_type 属性
查找指定的节点
struct device_node * of_find_node_by_type (struct device_node *from, const char *type)
from :开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type :要查找的节点对应的 type 字符串,也就是 device_type 属性值。
返回值 :找到的节点,如果为 NULL 表示查找失败。
根据 device_type 和 compatible
两个属性查找指定的节点
struct device_node * of_find_compatible_node (struct device_node *from, const char *type, const char *compatible)
from :开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type :要查找的节点对应的 device_type 属性值,可以为 NULL ,表示忽略 device_type 属性。
compatible : 要查找的节点所对应的 compatible 属性列表。
返回值 :找到的节点,如果为 NULL 表示查找失败
通过 of_device_id 匹配表
查找指定的节点
struct device_node * of_find_matching_node_and_match (struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)
from :开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
matches : of_device_id 匹配表,也就是在此匹配表里面查找节点。
match : 找到的匹配的 of_device_id。
返回值 :找到的节点,如果为 NULL 表示查找失败
通过路径来查找指定的节点inline struct device_node * of_find_node_by_path (const char *path)
path :全路径的节点名,可以使用节点的别名,比如“ /backlight ”就是 backlight 这个节点的全路径。
返回值 :找到的节点,如果为 NULL 表示查找失败

6.2 查找父/子节点的 OF 函数

方法描述
用于获取指定节点的父节点struct device_node * of_get_parent (const struct device_node *node)
node :要查找的父节点的节点。
返回值 :找到的父节点。
用迭代的查找子节点struct device_node * of_get_next_child (const struct device_node *node, struct device_node *prev)
node :父节点。
prev :前一个子节点,从此开始迭代的查找下一个子节点。NULL,表示从第一个子节点开始。
返回值 :找到的下一个子节点

6.3 提取属性值的 OF 函数

方法描述
用于查找指定的属性property * of_find_property (const struct device_node *np, const char *name, int *lenp)
np :设备节点;
name : 属性名字;
lenp :属性值的字节数;
返回值:找到的属性。
用于获取属性中元素的数量
(获取到属性数组的大小)
int of_property_count_elems_of_size (const struct device_node *np, const char *propname,int elem_size)
np :设备节点;
proname: 属性名;
elem_size:元素长度;
返回值 :得到的属性元素数量。
用于从属性中获取指定
标号的 u32 类型数据值
int of_property_read_u32_index (const struct device_node *np, const char *propname, u32 index, u32 *out_value)
np :设备节点。
proname : 要读取的属性名字。
index :要读取的值标号。
out_value :读取到的值
返回值 :0 读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
读取属性中 u8 、 u16 、 u32
和 u64 类型的数组数据
int of_property_read_u8_array (const struct device_node *np, const char *propname, u8 *out_values, size_t sz)
of_property_read_u16_array
of_property_read_u32_array
of_property_read_u64_array
np :设备节点。
proname : 要读取的属性名字。
out_value :读取到的数组值,分别为 u8 、 u16 、 u32 和 u64。
sz :要读取的数组元素数量。
用于读取属性中字符串值int of_property_read_string(struct device_node *np, const char *propname,const char **out_string)
np :设备节点。
proname : 要读取的属性名字。
out_string :读取到的字符串值。
返回值 :0:读取成功,负值:读取失败。
用于获取 #address-cells 属性值int of_n_addr_cells(struct device_node *np)
np :设备节点。
返回值 :获取到的 #address-cells 属性值。
用于获取 #size-cells 属性值int of_n_size_cells (struct device_node *np)
np :设备节点。
返回值 :获取到的 #size-cells 属性值。

6.4 其它常用OF函数

方法描述
用于查看节点的 compatible
属性是否有包含 compat 指定的字符串,
也就是检查设备节点的兼容性
int of_device_is_compatible (const struct device_node *device, const char *compat)
device :设备节点。
compat :要查看的字符串。
返回值 :0 :节点的 compatible 属性中不包含 compat 指定的字符串;
正数:节点的 compatible 属性中包含 compat 指定的字符串。
用于获取地址相关属性,
主要是“ reg ”或者
“assigned-addresses”属性值
const __be32 * of_get_address (struct device_node *dev, int index, u64 *size, unsigned int *flags)
dev :设备节点。
index :要读取的地址标号。
size :地址长度。
flags :参数,比如 IORESOURCE_IO 、 IORESOURCE_MEM 等
返回值 :读取到的地址数据首地址,为 NULL 的话表示读取失败。
将从设备树读取到的
地址转换为物理地址
u64 of_translate_address (struct device_node *dev, const __be32 *in_addr)
dev :设备节点。
in_addr :要转换的地址。
返回值 :得到的物理地址,如果为 OF_BAD_ADDR 的话表示转换失败。
从设备树里面提取资源值,
将 reg 属性值转换为
resource 结构体类型
int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
dev :设备节点。
index :地址资源标号。
r :得到的 resource 类型的资源值。
返回值 :0,成功;负值,失败。
将 reg 属性中地址
信息转换为虚拟地址
void __iomem * of_iomap (struct device_node *np, int index)
np :设备节点。
index : reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为 0。
返回值 :经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。

7. MIPI配置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值