Linux上设备树使用

1.简介

  DTS, Device Tree Source设备源码树,是一种描述硬件的数据结构体,dts的加入,先将
dts解析成树型结构,然后放在内存中,等待内核后面注册device 和 driver的时候再来铺开调用。
  DTS由一系列被命名为节点node和属性property组成,而节点本身可以包含子节点,所谓属性
就是成对出现的name和value。

2.DTS使用

  DTS在内核中,dts使用各种device node形式存在,而这些device node对于大部分的内核驱动\ 模型platform driver来说,最终需要由对应的platform device来匹配,才可以完成一次device 和 driver\ 的probe过程。
  device node转为platform device,这个过程是交给of_platform_populate来
完成的,(dts相关的device node tree是在start_kernel的setup_arch->unflatten_device_tree
来加载dtb并解析)。
  of_platform_populate的入口一般是处于init_machine中,对于arm架构而言位于board.c中的
DT_MACHINE_START()的init_machine中定义,而init_machine的调用是以一个module的形式存在的,
该module被加载后的目的,就是做device的create,在以前旧的board中,会将board.c中定义的
device_info转换为对应的device,以便后面的driver加载时可以match到相应的device。
需要说明的是dts中定义的各种device node,往往只是用来辅助核心的device node而存在的,
也就是说这些node存在不需要加载为platform device,那么哪些device node是不会在
of_platform_populate中被解析为device的呢?

static int of_platform_bus_of_platform_populatereate(struce device_node *bus,
   const struct of_device_id *matches,
   const struct of_dev_auxdata *lookup,
   struct device *parent,bool strict)

  源码省略,of_get_property("compatible"),如果这个节点的root device属性不存在,则表明其
不需要生成为platform device,随后,root device node由函数of_platform_device_create_pdate
创建为platform device后,需要检查当前节点的compatible是否和match table中定义的table list
相互匹配。对于dts中定义的device node,只有其所属的parent node所属的compatible属性和调用
of_platform_populate时传入的of_device_id相互匹配,则说明如果当前的device node只要包含有
compatible属性就会被创建为platform device。

2.1 节点语法

  节点构成:node_name@unit_address,node_name:由数字,大小字符等组成。unit_address:表示
是节点所表示的硬件配置的起始地址即reg属性的起始值,若节点没有reg属性则不需要设置。
  属性property,属性有两种,一种是没有值的空属性,一种是有值的属性。 例如:

compatible="gpio-keys";

上面是常见的有值的属性,属性值有三种 a).用引号括起来,字符串,如上述实例 b).使用尖括号, 每个成员都是32bit数据,称为arrays of cells; c).使用中括号[xx cc xx ]括起来的字节序列(byte string),例如一些初始化序列等参数。   dts文件中有一些默认的属性名字,比如model,compatible,#address-cells,#size-cells,
这些是根节点必须有的属性,它们代表的含义都是默认的.   参考一个瑞芯微上驱动一个mipi屏幕的驱动代码:

&dsi {
    status = "okay";
    rockchip,lane-rate = <650>; //680

    panel: panel@0 {
        compatible = "simple-panel-dsi";
        reg = <0>;
        backlight = <&backlight>;
        reset-gpios = <&gpio2 RK_PB3 GPIO_ACTIVE_HIGH>;
        enable-gpios = <&gpio2 RK_PC7 GPIO_ACTIVE_HIGH>;
        power-supply = <&vcc33_lcd>;
......
        prepare-delay-ms = <20>; //enable gpio delay
        reset-delay-ms = <20>; //reset gpio delay
        init-delay-ms = <20>; //after gpio befor send cmds delay
        enable-delay-ms = <20>; //enable backlight
        status = "okay";

        panel-init-sequence = [
            29 00 02 B0 00 
            05 78 01 29
            05 46 01 11
        ];

        panel-init-sequence2 = [
            29 14 16 CB 00 18 00 80 01 00 00  83 03 E8 00 00 
....
            29 14 1B D3 1B 33 BB 77 77 77 33 33 33  12 8A 07 3D BC
        ];
        panel-exit-sequence = [
            05 64 01 28
            05 96 01 10
        ];

        disp_timings: display-timings {
            native-mode = <&timing0>;

            timing0: timing0 {
                clock-frequency = <27000000>; //29700000
                hactive = <640>;
                hback-porch = <49>;
                hfront-porch = <67>;
                hsync-len = <8>;
....
                de-active = <0>;
                pixelclk-active = <0>;
            };
        };

    };
};

3.代码测试

  实际测试使用全志linux开发板测试。
设备树DTS:

//假设使用如下dts文件
/{
    //在根节点下面添加按键信息
    gpio-keys{
        compatible="gpio-keys";
        power{
            label = "Power Button";
            gpios = <&gpio3 291 1>;
            linux,code = <116>;//key power
        };
    };
};

上述设备树中compatible属性

//开始解析该dts文件
#include <linux/module.h>
#include <linux/init>
#include <linux/fs.h>
#include <linux/gpio_keys.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>


static int __init device_tree_ce(void)
{
    struct device_node *node,*pp;
    int nbuttons;
    int code;
    const char* desc;
    int gpio;
    /*
    根据下面函数解析内容看:只有compatible属性值有作用感,
    第一节点什么用也没有发现。
    */
    node = of_find_compatible_node(NULL,NULLL,"gpio-keys");
    //上面函数找到DTS中compatible为“gpio-keys”的节点
    printk(KERN_EMERG"%d",of_device_id_compatible(node,"gpio-leys"));
    //判断节点的compatible属性是否包含compat指定的字符串,当一个驱动支持2个
    //或者多个设备时,这些不同的dts文件中设备的compatible属性都会进入驱动
    //OF匹配表,因此驱动可以透过bootloader传递给内核的Device Tree中真正
    //节点的compatible属性以确定究竟是哪一种设备,从而根据不同设备类型
    //进行不同的处理。
    nbuttons = of_get_child_count(node);//算出节点下面共有多少个节点
    //printk;
    for_each_child_of_node(node,pp)//遍历子节点
    {
        enum of_gpio_flags flags;
        if(!of_find_property(pp,"gpios",NULL))//子节点里面是否包含“gpio”属性
        {
            printk(KERN_EMERG"Found button without gpios\n");
            continue;
        }
        gpio=of_get_gpio_flags(pp,0,&flags);
        of_property_read_u32(pp,"linux,code",&code);//读取属性名为“linux,code”属性的节点整型值。
        desc = of_get_property(pp,"label",NULL);//读取属性名为“label"
    }
}