一、设备树编译

1.编译设备树:cd linux-x.xx & make dtbs,生成的dtb在目录linux-x.xx/arch/xxx/boot/dts下

2.反编译dtb,生成dts: linux-x.xx/scripts/dtc/dtc -I dtb -O dts xxxx.dtb -o xxxx.dts

3.将.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文件会被编译出来。

在Linux下,我们可以单独编译Device Tree文件。当我们在Linux内核下运行make dtbs时,若我们之前选择了ARCH_VEXPRESS,上述.dtb都会由对应的.dts编译出来。因为arch/arm/Makefile中含有一个dtbs编译target项目。

4..dtb是.dts被DTC编译后的二进制格式的Device Tree描述,可由Linux内核解析。通常在我们为电路板制作NAND、SD启动image时,会为.dtb文件单独留下一个很小的区域以存放之,之后bootloader在引导kernel的过程中,会先读取该.dtb到内存。

5.对于Device Tree中的结点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt。这些文档位于内核的Documentation/devicetree/bindings目录,其下又分为很多子目录。

6.Uboot从 v1.1.3开始支持Device Tree,其对ARM的支持则是和ARM内核支持Device Tree同期完成。
  为了使能Device Tree,需要编译Uboot的时候在config文件中加入
  #define CONFIG_OF_LIBFDT 
  在Uboot中,可以从NAND、SD或者TFTP等任意介质将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot运行命令fdt addr命令设置.dtb的地址,如:
  U-Boot>  fdt addr 0x71000000
  fdt的其他命令就变地可以使用,如fdt resize、fdt print等。
  对于ARM来讲,可以透过bootz kernel_addr initrd_address dtb_address的命令来启动内核,即dtb_address作为bootz或者bootm的最后一次参数,第一个参数为内核映像的地址,第二个参数为initrd的地址,若不存在initrd,可以用 -代替。

二、设备树的组成格式

“/"代表根节点;

  “model”是板的ID;

  "compatible"是平台兼容,一般格式是"manufacturer,model"。内核或者uboot依靠这个属性找到相对应driver,若"compatible"出现多个属性,按序匹配driver;

  “#address-cells”是address的单位(32bit),可寻址的设备使用它、#size-cells、reg在Device Tree中编码地址信息,reg中的address 和 length 字段是可变长的,父结点的#address-cells和#size-cells分别决定了子结点的reg属性的address和length字段的长度。如果root结点的#address-cells = <1>;和#size-cells = <1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1(reg中描述address和length的字段都只能有1个)。cpus 结点的#address-cells = <1>;和#size-cells = <0>;决定了2个cpu子结点的address为1,而length为空。可以理解为地址需要几个维度来描述,如片选0的偏移0地址处,就需要#address-cells为2.

“#size-cells”是length的单位(32bit);

  "reg"是寄存器,格式是"<address,length>",作为平台内存资源,组织形式为reg = <address1 length1 [address2 length2] [address3 length3] ... >,length则为cell的列表或者为空(若#size-cells = 0)

  "aliase" 是别名,必须节点全称,可以通过地址引用获取;

  ”chosen“是板级启动参数;

  "cpus"是SOC的CPU信息,可以改变运行频率或者开关CPU,命名遵循的组织形式为:<name>[@<unit-address>],多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1,设备的unit-address地址也经常在其对应结点的reg属性中给出。

  "memory"是板级内存的信息。

  "interrupts"是中断控制器,根据SOC自定义格式,这里是<输入类型 中断号 触发方式>,作为平台中断资源;输入类型:0是SPI中断,1是PPI中断;触发类型:0上升沿,2下降沿(对SPI无效),4高电平,8低电平(对SPI无效),见Documentation\devicetree\bindings\interrupt-controller\arm,gic.txt

  “interrupt-controller”指示这个节点是中断控制节点,它的属性为空,中断控制器应该加上此属性表明自己的身份(直接在{}中写上interrupt-controller即可)

  “interrupt-cells”与#address-cells 和 #size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小

  "interrupt-parent"设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent 时,则从父级结点继承。

  "[label:]"如gic: interrupt-controller@1c81000,这个标签可以作为地址赋值到其他节点的属性;

  “device_type":设备类型,寻找节点可以依据这个属性;

  "status"是开关节点设备的状态,取值"okay"或者"ok"表示使能,"disabled"表示失能。

  “ranges” 经过总线桥后的address往往需要经过转换才能对应的CPU的memory映射。external-bus的ranges属性定义了经过external-bus桥后的地址范围如何映射到CPU的memory区域。ranges是地址转换表,其中的每个项目是一个子地址、父地址以及在子地址空间的大小的映射。映射表中的子地址、父地址分别采用子地址空间的#address-cells和父地址空间的#address-cells大小。

例如:ranges = <0 0  0x10100000   0x10000 将子地址空间的片选0的0偏移地址处映射到地址0x10100000处,映射大小为0x10000 字节

1 0  0x10160000   0x10000>; 将子地址空间的片选1的0偏移地址处映射到地址0x10160000处,映射大小为0x10000 字节

 三、系统通过以下步骤来生成树:

  1. CPU 经过初始化后搜索固件。

  2. 主要固件(OpenBoot、基本输入/输出系统 (Basic Input/Output System, BIOS) 或 Bootconf)初始化并创建包含已知或自标识硬件的设备树。

  3. 当主要固件在设备中发现兼容固件时,主要固件将初始化该设备并检索设备属性。

  4. 该固件将查找并引导操作系统。

  5. 内核从树的根节点开始,搜索匹配的设备驱动程序并将该驱动程序绑定到设备。

  6. 如果设备是结点,则内核会查找固件尚未检测到的子设备。内核会将所有子设备都添加到树的子树节点下面。

  7. 内核从步骤 5 开始重复该过程,直到无需再创建设备节点。

主要相关函数在/drivers/of/base.c中,使用上比较打的区别是platform_driver结构被替换为了of_device_id ,而platform_driver不变

四、设备与确定的匹配

  .dts文件中,root结点"/"的compatible 属性定义了系统的名称,它的组织形式为:<manufacturer>,<model>。Linux内核通过它判断启动的是什么machine。
  .dts文件的每个设备,都有一个compatible 属性,compatible属性用户驱动和设备的绑定。compatible 属性是一个字符串的列表(第一个“”内的字符串),列表中的第一个字符串表征了结点代表的确切设备,形式为"<manufacturer>,<model>",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。如compatible = "arm,vexpress-flash", "cfi-flash";  在与驱动进行绑定主要靠mode域,前面的manufacturer不重要。

五、常用的of API

1.判断设备结点的compatible 属性是否包含compat指定的字符串

  int of_device_is_compatible(const struct device_node *device,const char *compat)

2.根据compatible属性,遍历Device Tree中所有的设备结点,获得设备结点

  struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

3. 读取设备结点np的属性名为propname,类型为8、16、32、64位整型数组的属性。对于32位处理器来讲,最常用的是of_property_read_u32_array()

  of_property_read_u32_array() / of_property_read_u64_array()还有u8,u16的

4.前者读取字符串属性,后者读取字符串数组属性中的第index个字符串

  of_property_read_string() / of_property_read_string_index()

5.如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

  of_property_read_bool()

6.通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况,index为0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap。

   void __iomem *of_iomap(struct device_node *np, int index)

7.透过Device Tree或者设备的中断号,实际上是从.dts中的interrupts属性解析出中断号。若设备使用了多个中断,index指定中断的索引号。

  irq_of_parse_and_map

还有一些OF API,这里不一一列举,具体可参考include/linux/of.h头文件。

六、总结

ARM社区一贯充斥的大量垃圾代码导致Linus盛怒,因此社区在2011年到2012年进行了大量的工作。ARM Linux开始围绕Device Tree展开,Device Tree有自己的独立的语法,它的源文件为.dts,编译后得到.dtb,Bootloader在引导Linux内核的时候会将.dtb地址告知内核。之后内核会展开Device Tree并创建和注册相关的设备,因此arch/arm/mach-xxx和arch/arm/plat-xxx中大量的用于注册platform、I2C、SPI板级信息的代码被删除,而驱动也以新的方式和.dts中定义的设备结点进行匹配。

补充:

flash_SY7803:flashlight {
compatible = "qcom,leds-gpio-flash"; //匹配参数
status = "okay";
pinctrl-names = "flash_default";
pinctrl- = <&SY7803_default>;
qcom,flash-en = <&msm_gpio >;
qcom,flash-now = <&msm_gpio >;
qcom,op-seq = "flash_en", "flash_now";
qcom,torch-seq-val = < >;
qcom,flash-seq-val = < >;
linux,name = "flashlight"; //属性 linux,name
linux,default-trigger = "flashlight-trigger";
};

使用: 在代码中获取节点的所有信息

0.先把节点获取到 struct device_node *np = NULL; np = of_find_node_by_path("/test_nod@12345678");

1.of_get_named_gpio(node, "qcom,flash-en", 0);返回31;

2.获取结点中的属性:of_find_property()

3.读取到属性中的整数的数组:uint32_t array_flash_seq[2];  rc = of_property_read_u32_array(node, "qcom,flash-seq-val",array_flash_seq, 2);  ==》array_flash_seq <1 0>

4.读取到属性中的字符串的数组:rc = of_property_read_string_index(node,    "qcom,op-seq", i,     &seq_name);  //"flash_en", "flash_now";

5.获取到中断的号码:irqno = irq_of_parse_and_map(np, 0);

6.可以使用ret = request_irq验证中断号码是否有效

7.获取设备属性名字:of_property_read_string(node, "linux,name", &flash_led->cdev.name)  // flash_led->cdev.name = “flashlight ”;

1. 设备树中 compatible 
    键值对
2.driver中  
 platform_driver 结构体
     probe    
     remove

of_match_table

probe 中
     1.通过of函数获得相关的资源信息,
     2. 申请引脚信息  pinctrl 
     3.注册设备 classdev

参考1:http://www.cnblogs.com/kevinhwang/p/5647021.html

参考2:http://blog.csdn.net/21cnbao/article/details/8457546(好)

补充参考:http://www.cnblogs.com/zzb-Dream-90Time/p/6474526.html

Linux设备树的更多相关文章

  1. Linux设备树语法详解

    概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写.引入了设备树之后,驱动代 ...

  2. Linux设备树语法详解【转】

    转自:http://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备 ...

  3. 宋牧春: Linux设备树文件结构与解析深度分析(2) 【转】

    转自:https://mp.weixin.qq.com/s/WPZSElF3OQPMGqdoldm07A 作者简介 宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot.linux.ucos ...

  4. linux 设备树【转】

    转自:http://blog.csdn.net/chenqianleo/article/details/77779439 [-] linux 设备树 为什么要使用设备树Device Tree 设备树的 ...

  5. linux设备树语法

    设备树语法及绑定 概述 Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF). 就ARM平台来说,设备树文件存放在arch/arm/boot/d ...

  6. 【转载】Linux设备树(Device Tree)机制

    转:Linux设备树(Device Tree)机制   目录 1. 设备树(Device Tree)基本概念及作用2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DT ...

  7. Linux设备树学习

    1.概念 设备树用于实现驱动代码与设备信息相分离.驱动代码只负责处理驱动的逻辑而关于设备的具体信息存放到设备树文件中.(dts文件,编译后为dtb文件).一个dts文件对应一个ARM的machine, ...

  8. linux设备树笔记__dts基本概念及语法【转】

    转自:http://www.360doc.com/content/15/1113/11/15700426_512794532.shtml 设备树手册(Device Tree Usage)原文地址:ht ...

  9. 我眼中的Linux设备树(六 memory&chosen节点)

    六 memory&chosen节点根节点那一节我们说过,最简单的设备树也必须包含cpus节点和memory节点.memory节点用来描述硬件内存布局的.如果有多块内存,既可以通过多个memor ...

  10. 我眼中的Linux设备树(五 根节点)

    五 根节点一个最简单的设备树必须包含根节点,cpus节点,memory节点.根节点的名字及全路径都是"/",至少需要包含model和compatible两个属性.model属性我们 ...

随机推荐

  1. English trip -- VC(情景课)1 B Countries

    Vocabulary focus 核心词汇 Vo ca bu la ry   fo cus [və(ʊ)'kæbjʊlərɪ]      ['fəʊkəs] Listen and repeat  听并 ...

  2. Struts2 简介图

    Struts2官方提供的,strus2的内部工作机制图解.

  3. Leha and another game about graph CodeForces - 840B (dfs)

    链接 大意: 给定无向连通图, 每个点有权值$d_i$($-1\leq d_i \leq 1$), 求选择一个边的集合, 使得删除边集外的所有边后, $d_i$不为-1的点的度数模2等于权值 首先要注 ...

  4. stl算法:next_permutation剖析

    在标准库算法中,next_permutation应用在数列操作上比较广泛.这个函数可以计算一组数据的全排列.但是怎么用,原理如何,我做了简单的剖析. 首先查看stl中相关信息.函数原型: templa ...

  5. Spring boot 嵌入的tomcat不能启动: Unregistering JMX-exposed beans on shutdown

    原因是:没有引入tomcat依赖包 <dependency> <groupId>org.springframework.boot</groupId> <art ...

  6. List原理

    ArrayList: ArrayList是一个可变数组实现,实现了List接口的所有方法,并允许存取null值.ArrayList基本上等同与Vector,但它只对writeObject()和read ...

  7. EL表达式、 jstl标签

    https://www.cnblogs.com/zhaotiancheng/p/6391894.html https://blog.csdn.net/zdwzzu2006/article/detail ...

  8. 根据id来实现小程序tab切换,

    本例根据绑定id来实现tab切换,但本例仍有缺陷,用for循环数据,无法实现切换.如有大神能够有更好方法,欢迎留言更正 WXML: <view class="tab"> ...

  9. PHP:第一章——PHP中常量和预定义常量

    <?php /*****************************************************/ //1.常量的定义 //常量:声明一次后长期不变的量,以字母或下划线开 ...

  10. RTU命令设置笔记

    YN+12VCTL=1 配置+12V输出控制模式:永久输出YN+5VCTL=1 配置+5V输出控制模式:永久输出 YN+GETDATA 读取采样值 YN++LIST 获取设置参数列表 YN+LOAD ...