设备树学习报告

1. 设备树的基本概念

1.1 什么是设备树

在linux3.x版本之前的内核源码,存在大量对板级细节信息的描述造成内核源码的冗余。为了解决这个问题引入了设备树

官方对设备树的描述是: 一个描述硬件的数据结构 设备树通过bootloader将硬件资源传给内核。使内核和硬件资源描述相对独立

1.2 设备树的组成

设备树的主要由三部分组成 DTC (device tree compiler) DTS (device tree source) 和 DTB (device tree blob)

DTS:dts 文件时对device Tree 的描述,放置在内核的/arch/arm/boot/dts目录中。一个***.dtb 文件对应一个ARM的machine。dts文件描述了一个板子的硬件资源

DTC:DTC为编译工具,它可以将.dts文件编译成.dtb文件

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

设备树中还有一个文件: dtsi文件 由于一个SOC可能有多个不同的电路板。而每个电路板都拥有一个.dts。 这些dts会存在很多共同的部分,为了提高代码的复用率。设备树使用dist文件保存了这些共同部分的代码。这样就可以提供给不同的dts使用。

所以1个dts文件+n个dtsi文件,它们编译而成的dtb文件就是真正的设备树,此外,dts/dtsi兼容c语言的一些语法,能使用宏定义,也能包含.h文件

2. 设备树的基本结构

下面是六轴主控制器的设备树文件的一部分 下面以它为例对设备树的基本结构进行分析

/include/ "am572x_clock.dtsi"

/   {
    model = "TI_AM572X_IDK - Cortex-A15 (ARMV7A)";
    compatible = "ti,sitara-ctxa15";
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;
    
    ...

     i2c1@48070000
            {
            compatible  = "ti,am38xx-i2c";
            interrupts  = <88 0 4>;
            reg         = <0x48070000 0xd6>;
            clocks      = <&l4per_i2c1_mod>;
            clock-names = "l4per_i2c1_mod";
            clock-frequency = <400000>;
            #address-cells = <1>;
            #size-cells = <0>;
            pinmux-0    = <&i2c1_pads>;
            
            pcf8564@0x51
                {
                compatible  = "nxp,pcf8564";
                reg         = <0x51>;
                data-scl-frequency = <400000>;
                };
            
            tps659037@0x58
                {
                compatible  = "ti,tps659037-compatible";
                reg         = <0x58>;
                data-scl-frequency = <400000>;
                };

            pinmux-0    = <&i2c1_pads>; 标号引用&i2c1_pads
    ...

    }

2.1 设备树的基本构造

{}包围起来的结构称之为设备树节点,dts中最开头的/ {},称为根节点。节点的标准结构是xxx@yyy{…},xxx是节点的名字,yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址).比如i2c1: i2c@48070000中的48070000就是一个i2c控制器的寄存器基地址,pcf8564@0x51中的0x51就是这个rtc设备的i2c地址

1.属性:地址

有关节点的地址,比如i2c1@48070000,虽然它在名字后面跟了地址。但正式的设置是在reg属性中设置的比如 reg = <0x48070000 0xd6>; reg的格式通常为<address(地址) lenth(长度)>;0x48070000是寄存器基地址,0xd6是长度。address和length的个数是可变的,由父节点的属性#address-cells 和#size-cells 决定,比如节点i2c1@48070000的父节点 #address-cells 和#size-cells均为1,所以下面的i2c节点的reg属性就有address 和length,而i2c节点本身#address-cells 和#size-cells 分别为1和0,所以其下的rtc: pcf8564@0x51 的reg属性就只有一个0x51(i2c地址)了

2.属性:兼容性

如果一个节点是设备节点,这个设备节点就会有一个compatible(属性),这是设备用于和驱动匹配的依据,compatible(兼容性)的值可以有不止一个字符串以满足不同的需求。而根节点的compatible也是非常的重要,也是就是"ti,sitara-ctxa15"这个字符串,因为内核系统启动后,将根据根节点的compatible来判断cpu信息,并由此进行初始化。

3.属性设置的技巧

属性设置主要有二种技巧

(1).可以参考类似的dts

(2).查询内核中的文档

2.2 节点之间的联系

节点和节点之间的关联,由“标号引用” 和“包含”来实现

标号引用就是在节点名称前加上标号,这样设备树的其他位置就能够通过&符号来调用/访问该节点,比如上面代码所示i2c节点中的 pinmux-0属性,就引用了i2c1_pads标号处的节点

pinmux-0    = <&i2c1_pads>; 标号引用&i2c1_pads
i2c1_pads: i2c1pads
                    {
                    pin-set = <
                               0x1800 0x00060000   /* BALL:C21 CTRL_CORE_PAD_I2C1_SDA = PIN_INPUT_PULLUP | MUX_MODE0 */
                               0x1804 0x00060000   /* BALL:C20 CTRL_CORE_PAD_I2C1_SCL = PIN_INPUT_PULLUP | MUX_MODE0 */
                               >;
                    };

包含是最基本的方式,比如如上面的代码我们要在在i2c1接口添加一个i2c外设,那么就必须要在i2c1下面添加一个节点,比如上面代码中的pcf8564@0x51{}

2.3内核与节点匹配

内核需要知道dtb文件的地址,这样内核就可以通过一些API任意获取设备树的内部信息

驱动程序直接通过设备节点中的compatible(兼容性)来与设备节点直接进行配对的,

这里以GPMC驱动为例 只要驱动中的compatible值与设备节点中的compatible相匹配,

LOCAL const VXB_FDT_DEV_MATCH_ENTRY vxbGpmc_match[] =  //match table 
    {
        {
        "zjc,gpmc", /* compatible */
        (void *)NULL
        },
        {}
    };

Probe 函数匹配如下图所示 vxbFdtDevMatch函数进行兼容性匹配 如果匹配成功就进入attach函数进行挂载设备

STATUS vxbGpmcProbe
    (
    struct vxbDev * pDev
    )
    {
    VXB_FDT_DEV_MATCH_ENTRY   * match;
    STATUS                      ret;
    
    /* Check if the pDev pointer is valid */
    VXB_ASSERT (pDev != NULL, ERROR);
         
    ret = vxbFdtDevMatch (pDev, vxbGpmc_match, &match);
    if (ret == ERROR)
        return ERROR;
   
    printf(" GPMC Probe success \n");
         
    return OK;
    }

2.4 自定义属性的设置与获取

所谓的自定义属性,有点类似于老内核中的platform_data,我们在设备节点中可以随意添加自定义属性,比如下面GPMC节点里面的nand-addr属性

 gpmc:gpmc@0x50000000
           {
           compatible = "zjc,gpmc";
           reg = <0x50000000 0x37c>,
                 <0x08000000 0x01000000>;
           nand-addr = <0x08000000>;
           pinmux-0 = <&gpmc_pads>;
           };

使用以下vxFdtPropGet函数进行获取nand-addr属性的

const void * vxFdtPropGet
    (
    int    offset,      /* node offset */
    char * pPropName,   /* contains property name */
    int *  pLen         /* on return, contains length, in bytes, */
                        /* of property value */
    )       
vxFdtPropGet( pFdtDev->offset, "nand-addr", &len)

针对32位整形的属性可以利用下面这个API来获取属性值,第一个参数是节点,第二个参数是属性名字,第三个参数是返回属性值内容的字节大小

vxFdtPropGet( pFdtDev->offset, "nand-addr", &len)

针对32位整形的属性可以利用下面这个API来获取属性值,第一个参数是节点,第二个参数是属性名字,第三个参数是返回属性值内容的字节大小

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设备(device tree)机制是Linux内核从linux-3.x版本开始引进的一种机制,目的是解决内核源码的arch/arm目录下代码混乱的问题:随着ARM生态的快速发展,在内核源码的arch/arm目录下,存放着几十种arm芯片和几百个开发板相关的源文件,很多开发板和处理器的中断、寄存器等相关硬件资源都在这个目录下以.c或.h的文件格式定义。而对于内核来说,与这些硬件耦合,会导致内核代码混乱不堪,每个开发板上运行的内核镜像都必须单独编译配置,无法通用。什么时候Linux内核能像Windows镜像那样,无论你的电脑什么配置,一个Windows安装包,都可以直接下载安装运行呢?设备机制,实现了Linux内核和硬件平台的解耦:每个硬件平台的硬件资源使用一个设备文件(xxx.dts)来描述,而不是在arch/arm下以.c 或 .h 文件来定义。Linux内核是一个通用的内核,在启动过程中,在通过解析设备中的硬件资源来初始化某个具体的平台。 引入设备后,很多和内核驱动开发的工作也发生了变化:以往驱动工程师关注的头文件宏定义、寄存器定义,现在这些基本上不用关注,关注的重点则转向了如何根据硬件平台去配置和修改设备文件。很多驱动的编程接口也发生了变化,开始慢慢使用device tree提供的编程接口去开发驱动。本期课程主要面向嵌入式开发人员,分享Linux下驱动开发所需要的设备知识和必备技能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值