zoukankan      html  css  js  c++  java
  • Linux 设备树说明【转】

    一、什么是设备树?

      在Linux3.x之前的内核源码中,存在大量对板级细节信息描述的代码。这些代码充斥在/arch/arm/plat-xxx/arch/arm/mach-xxx目录中。每年新出的 ARM 架构芯片少说都有数十、数百款,每一款芯片又会有很多款基于该芯片的板子,这些板子上的设备又不相同,每一块板子都要有自己的板级信息文件,所以Linux 内核下板级信息文件将会成指数级增长!关键是这些板级信息文件还都是.c 或.h 文件,它们都会被编译进 Linux 内核中,导致 Linux 内核“虚胖”。因此为了解决这个问题,引入设备树。

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

      

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

    所以总结来说就是:设备树是一种描述硬件资源的数据结构。它通过bootloader将硬件资源传给内核,使得内核和硬件资源描述相对独立,也即设备树文件不需要编译进内核。

      设备树的主要优势:在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写。而引入设备树之后,对于同一SOC的不同主板,只需更换设备树文件.dtb即可实现不同主板的无差异支持,而无需更换内核文件。

    二、dts、dtb、dtc、dtsui 的介绍和他们之间的关系

      dts文件是对Device Tree的描述,放置在内核的/arch/arm/boot/dts目录。一个*.dts文件对应一个ARMmachine。dts文件描述了一个板子的硬件资源。以前写在mach-xxx文件中的内容被转成了dts文件。

      dtb 是编译*.dts生成的二进制文件(.dtb)bootloader在引导内核时,会预先读取.dtb到内存,进而由内核解析。

      dtc为编译工具,它可以将.dts文件编译成.dtb文件。DTC 工具源码在 Linux 内核的 scripts/dtc 目录下。如果要编译 .dts文件的话只需要进入到 Linux 源码根目录下,然后执行如下命令:make all或者make dtbs。make all命令是编译 Linux 源码中的所有东西,包括 zImage,.ko 驱动模块以及设备树,如果只是编译设备树的话建议使用make dtbs命令。还可以直接指定要编译的文件,如xxx.dtb,意思是将xxx.dts编译成dtb。注意我们编译的时候只会编译修改的文件,没有修改的dts文件是不会被再次编译的。

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

    三、设备树框架

    设备树用树状结构描述设备信息,它有以下几种特性

    • 每个设备树文件都有一个根节点,每个设备都是一个节点。
    • 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
    • 每个设备的属性都用一组key-value对(键值对)来描述。
    • 每个属性的描述用;结束
    /*
    所以,一个设备树的基本框架可以写成下面这个样子,一般来说,/表示板子,
    它的子节点node1表示SoC上的某个控制器,控制器中的子节点node2表示
    挂接在这个控制器上的设备(们)。
    */
    
    /{                                  //根节点
        node1{                          //node1是节点名,是/的子节点
            key=value;                  //node1的属性
            ...
            node2{                      //node2是node1的子节点
                key=value;              //node2的属性
                ...
            }
        }                               //node1的描述到此为止
        node3{
            key=value;
            ...
        }
    }

      下面通过一个具体的dtsi文件来看一下设备树文件的框架是怎样的。注意,dtsdtsi虽然是不同的文件,但是他们只是描述的信息不同,结构都是一样的。

    10 #include <dt-bindings/clock/imx6ul-clock.h>
    11 #include <dt-bindings/gpio/gpio.h>
    12 #include <dt-bindings/interrupt-controller/arm-gic.h>
    13 #include "imx6ull-pinfunc.h"
    14 #include "imx6ull-pinfunc-snvs.h"
    15 #include "skeleton.dtsi"
    16 
    17 / {
    18    aliases {
    19       can0 = &flexcan1;
    ......
    48    };
    49 
    50    cpus {
    51        #address-cells = <1>;
    52        #size-cells = <0>;
    53 
    54        cpu0: cpu@0 {
    55             compatible = "arm,cortex-a7";
    56             device_type = "cpu";
    ......
    89         };
    90      };
    91 
    92    intc: interrupt-controller@00a01000 {
    93        compatible = "arm,cortex-a7-gic";
    94        #interrupt-cells = <3>;
    95        interrupt-controller;
    96        reg = <0x00a01000 0x1000>,
    97              <0x00a02000 0x100>;
    98     };
    99 
    100   clocks {
    101       #address-cells = <1>;
    102       #size-cells = <0>;
    103 
    104       ckil: clock@0 {
    105            compatible = "fixed-clock";
    106            reg = <0>;
    107            #clock-cells = <0>;
    108            clock-frequency = <32768>;
    109            clock-output-names = "ckil";
    110         };
    ......
    135      };
    136 
    137   soc {
    138       #address-cells = <1>;
    139       #size-cells = <1>;
    140       compatible = "simple-bus";
    141       interrupt-parent = <&gpc>;
    142       ranges;
    143 
    144       busfreq {
    145          compatible = "fsl,imx_busfreq";
    ......
    162       };
    197 
    198       gpmi: gpmi-nand@01806000{
    199          compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
    200          #address-cells = <1>;
    201          #size-cells = <1>;
    202          reg = <0x01806000 0x2000>, <0x01808000 0x4000>;
    ......
    216       };
    ......
    1177   };
    1178 };

      示例代码中第 54~89行就是 cpu0这个设备节点信息,这个节点信息描述了SOC所使用的 CPU信息,比如架构是 cortex-A7,频率支持 996MHz、792MHz、 528MHz、396MHz 和 198MHz 等等。在文件中不仅仅描述了 cpu0这一个节点信息,这颗 SOC 所有的外设都描述的清清楚楚,比如 ecspi1-4、uart1-8、usbphy1-2、i2c1-4等等。

    四、设备树在linux系统中的体现

      Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的/proc/device-tree 目录下根据节点名字创建不同文件夹,如下图所示:

      

      首先需要注意的是,既然你的板子可以跑这个系统,那么这个系统肯定是已经对你的板子做了适配,因此当你到此文件夹下的时候,里面的各个属性和子节点肯定是针对的运行该系统的这个板子,也即对应的是你这个板子的dts文件。当然这是在已经完全适配的情况下,在此之前我们可以自己往里面添加属性和子节点。

      我们要知道的是,在目录下,文件对应属性,文件夹对应子节点,每个子节点又可以有属性和子节点,就这样一级一级循环下去。因为属性有值,而且对应的是文件,我们就可以通过cat命令来查看文件内容,也就是属性的值。

    五、设备树的绑定信息文档

      设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。那么我们在设备树中添加一个硬件对应的节点的时候从哪里查阅相关的说明呢?在Linux 内核源码中有详细的.txt文档描述了如何添加节点,这些.txt文档叫做绑定文档,路径为:Linux 源码目录 /Documentation/devicetree/bindings

      

      比如我们现在要想在I.MX6ULL这颗SOC 的 I2C下添加一个节点,那么就可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt,此文档详细的描述了I.MX 系列的SOC如何在设备树中添加 I2C设备节点,文档内容如下所示:

    * Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX
    
    Required properties:
    - compatible :
      - "fsl,imx1-i2c" for I2C compatible with the one integrated on i.MX1 SoC
      - "fsl,imx21-i2c" for I2C compatible with the one integrated on i.MX21 SoC
      - "fsl,vf610-i2c" for I2C compatible with the one integrated on Vybrid vf610 SoC
    - reg : Should contain I2C/HS-I2C registers location and length
    - interrupts : Should contain I2C/HS-I2C interrupt
    - clocks : Should contain the I2C/HS-I2C clock specifier
    
    Optional properties:
    - clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
      The absence of the propoerty indicates the default frequency 100 kHz.
    - dmas: A list of two dma specifiers, one for each entry in dma-names.
    - dma-names: should contain "tx" and "rx".
    
    Examples:
    
    i2c@83fc4000 { /* I2C2 on i.MX51 */
        compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
        reg = <0x83fc4000 0x4000>;
        interrupts = <63>;
    };
    
    i2c@70038000 { /* HS-I2C on i.MX51 */
        compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
        reg = <0x70038000 0x4000>;
        interrupts = <64>;
        clock-frequency = <400000>;
    };
    
    i2c0: i2c@40066000 { /* i2c0 on vf610 */
        compatible = "fsl,vf610-i2c";
        reg = <0x40066000 0x1000>;
        interrupts =<0 71 0x04>;
        dmas = <&edma0 0 50>,
            <&edma0 0 51>;
        dma-names = "rx","tx";
    };

    注:本文为转发,只做记录学习用,原文参考链接:https://blog.csdn.net/qq_39507748/article/details/105874830

     

  • 相关阅读:
    【移动安全基础篇】——30、class.dex文件格式讲解
    【移动安全基础篇】——29、Android源码定制添加反反调试机制
    【移动安全基础篇】——28、Apk加固
    【移动安全基础篇】——27、常用调试检测方法与过检测方法
    【移动安全基础篇】——26、两个简单app破解
    粒子特效优化
    android studio生成aar包
    AndroidManifest
    声音
    unity webview
  • 原文地址:https://www.cnblogs.com/xingboy/p/14898899.html
Copyright © 2011-2022 走看看