84. linux--设备树(上)

我们在编译的时候,会发现要编译一下设备树文件,那么为什么要多此一举加这个文件?这个文件有什么作用呢??

一.设备树的由来(驱动框架的演进)

 

 

总体来讲,产品的开发越来越简单,架构越来越复杂,开发工作量减少。

原始架构依然很重要,里面的知识并没有被淘汰,只是被封装和继承了。

Linux 2.6版本,封装了原始架构,更加抽象,引入了设备驱动模型(sysfs)。使做产品更省事,实现了应用程序与驱动程序的分离

其实从驱动程序的演进过程我们就会发现,设备树的引入是一种必然。

为什么要使用设备树??

在Linux 2.6中,(为了适应各种平台)arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的 垃圾代码,对内核而言这些platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data绝大多数纯属垃圾冗余代码。 这个时候大神linus肯定是不允许这种事情发生的,于是引入了设备树。ARM Device Tree起源于OpenFirmware (OF)。

 

设备树特点:

1.设备树的主要优势:对于同一SOC的不同主板,只需更换设备树文件.dtb即可实现不同主板的无差异支持,而无需更换内核文件。

2.设备树信息被保存在一个ASCII 文本文件中,适合人类的阅读习惯,类似于xml文件, 在ARM Linux中,一个.dts文件对应一个ARM的machine放置在内核的

arch/arm/boot/dts/目录

3,设备树是一种数据结构,用于描述设备信息的语言,具体而言,是用于操作系统 中描述硬件,使得不需要对设备的信息进行硬编码(hard code)

什么是硬编码??

比较形象的举例,我们之前点灯,直接设置gpio3,现在将gpio3(写死的)替换为LED_GPIO(可变的),这个定义通过配置文件进行读入,现在你的管脚改变了,只需要改下配置文件的指定,就可以了。

4, Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身 可包含子结点。所谓属性,其实就是成对出现的name和value。

 

二. 设备树基本概念

DTS即Device Tree Source,是一个文本形式的文件,用于描述硬件信息,包括CPU的数量和类别、内存基地址和大小、中断控制器、总线和桥、外设、时钟和GPIO控制器等。

DTB即Device Tree Blob,是一个二进制形式的文件,由linux内核识别,为其中的设备匹配合适的驱动程序。被内核解析,用一个树形结构的结构体保存(不用说这个结构体一定是在内存中的)
                                         源码路径 include/linux/of.h  struct device_node {...}

DTC即Device Tree Compiler,将适合人类阅读和编辑的DTS文件编译成适合机器处理的DTB文件。DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了Device Tree的情况下,编译 内核的时候主机工具dtc会被编译出来

DTSI : device tree source include (类似于.h头文件),不同板子中有相同的节点信息提取到 DTSI中,使用 #include "xxx.dtsi" 的方式引入

dts也就是我们要编辑的文件,通过DTC工具生成二进制文件DTB,供内容识别。

bootloader 读取dtb文件并放入RAM中,并将存放地址告诉linux内核(bootm 0x41000000 - 0x42000000),内核启动以后从该地址读取相应的设备信息,匹配平台和设备驱动,分析设备树节点,并创建树形结构体 struct device_node {...},展开树。

DTS中实现了类似于这样的一个树状结构。

 

三.如何快速编译设备树文件

 

1.参考板origen的设备数文件为参考(找到厂家提供的类似的dtb) 

cp arch/arm/boot/dts/exynos4412-origen.dts   arch/arm/boot/dts/exynos4412-fs4412.dts

2.添加新文件需修改Makefile才能编译

vim arch/arm/boot/dts/Makefile, 在exynos4412-origen.dtb  下添加如下内容

exynos4412-fs4412.dtb 

3.编译设备树文件

make dtbs

()

4.拷贝内核和设备树文件到/tftpboot目录下

cp arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/

5.设置启动参数

set bootcmd tftp 0x41000000 uImage \; tftp 0x42000000 exynos4412-fs4412.dtb \; bootm 0x41000000 - 0x42000000

 

 

四.设备树编写规则

我们知道了怎么编译DTS文件,但是如果我们修改了外设,或者更换了平台,我们也要跟着修改设备树文件,那么我们如何修改设备树呢?

我们就需要了解设备树的结构及调用原理,理解设备树的编写规则。我们才能更好的将设备树应用到我们的项目中去。

设备树的语法框架:

/ {																				//根结点
		node1 {																	//子节点1
				a-string-property = "A string";									//键值对表示方式,name,value
				a-string-list-property = "first string", "second string";
				a-byte-data-property = [0x01 0x23 0x34 0x56];
				child-node1 {													//子子节点1
								first-child-property;
								second-child-property = <1>;
								a-string-property = "Hello, world";
							};
							
				child-node2 {													//子子节点2
							};
		};
		node2 {																	//子节点2															
				an-empty-property;
				a-cell-property = <1 2 3 4>; 
				child-node1 {													//子子节点
							};
		};
  };

这是一个简单的设备树,包括根结点,子结点,结点中又包含了对设备的一些描述(属性)。重点理解设备的描述的规则,我们就可以自由的添加自己的外设了。

下面是我们之前项目中的用到的dm9000的网卡驱动用到的设备树文件。

srom-cs1@5000000 {                            //结点名称
 
       compatible = "simple-bus";
 
        #address-cells = <1>;
 
        #size-cells = <1>;
 
        reg = <0x5000000 0x1000000>;  // 对应芯片手册3Memory Map 的0x0500_0000和16 MB
 
        ranges;
 
        ethernet@5000000 {
 
            compatible = "davicom,dm9000";          //内核通过该名字来匹配驱动
            reg = <0x5000000 0x2 0x5000004 0x2>;   //寄存器地址和数据宽度
            interrupt-parent = <&gpx0>;            //继承于 中断控制器gpx0
            interrupts = <6 4>; //6对应中断源 DM9000_IRQ -> XEINT6 。4对应 active high             
            level-sensitive         
            davicom,no-eeprom;
            mac-address = [00 0a 2d a6 55 a2];
 
          };
 
}

1.结点名称

节点名称:每个节点必须有一个"<名称>[@<设备地址>"形式的名字

<名称>就是一个不超过31位的简单ascii字符串,节点的命名应该根据它所体现的是什么样的设备。比如一个3com以太网适配器的节点就应该命名为ethernet,而不应该是3com509.

<设备地址>用来访问该设备的主地址,并且该地址也在节点的reg属性中列出,同级节点命名必须是唯一的,但只要地址不同,多个节点也可以使用一样的通用名称当然设备地址也是可选的,可以有也可以没有.

 ethernet@5000000     //设备地址不是必须,可省略

 

2.compatible属性

指定了系统的名称,是一个字符串的列表,实际在代码中可以用于进行匹配。它包含了一个"<制造商>,<型号>"形式的字符串。

树中每个表示一个设备的节点都需要一个compatible属性

 compatible = "davicom,dm9000";          //内核通过该名字来匹配驱动  ,命名要确切,避免命名冲突。不要使用带通配符的compatible值

 

3.#address-cells,#size-cells

#address-cells = <1>  表示address字段的长度的为1

#size-cells = <1>;表示length字段的长度为1

        #address-cells = <1>;  //表示address字段的长度的为1
 
        #size-cells = <1>;       //表示length字段的长度为1
 
        reg = <0x5000000 0x1000000>;  // //地址站1个cells ,长度站1个cells (reg = <0 0 0x1000>; //地址站两个cells ,长度站1个cells)

4.reg属性

reg的组织形式为req = <address1 length1 [address2 length2] [address3 length3] ..>

其中的每一组address length表明了设备使用的一个地址范围

 reg = <0x5000000 0x2 0x5000004 0x2>;   //寄存器地址和数据宽度

 

5.中断 属性

描述中断连接需要四个属性:

interrupt-controller-一个空的属性定义该节点作为一个接收中断信号的设备 //可省略

#interrupt-cells-这是一个中断控制器节点的属性。它声明了该中断控制器的中断指示符中cell的个数(类似于#address-cells和#size-cells ) //可省略

interrupt-parent-这是一个设备节点的属性,包含一个指向该设备连接的中断控制器的phandle(指向或者可以引用&)那些没有interrupt-parent的节点则从它们的父节点中继承该属性。

interrupts-一个设备节点属性,包含一个中断指示符的列表,对应于该设备上的每个中断输出信号

 interrupt-parent = <&gpx0>;            //继承于 中断控制器gpx0
 interrupts = <6 4>;                          //6对应中断源 DM9000_IRQ -> XEINT6 。4对应 active high  level-sensitive         

 

6.property 属性

简单的键-值对,它的值可以为空或者包含一个任意字节流。虽然数据类型并没有编码进数据结构,但在设备树源文件中任有几个基本的数据表示形式

  • 文本字符串(无结束符)可以用双引号表示:

string-property = "a string“

  • Cells是32位无符号整数,用尖括号限定:

cell-property = <Oxbeef 123 0xabcd1234>;

  • 二进制数据用方括号限定

binary-property = [01 23 45 67];

  • 不同表示形式的数据可以使用逗号连在一起:

mixed-property = "a string", [01 23 45 67], <0x12345678>;

  • 逗号也可用于创建字符串列表:

string-list = "red fish", "blue fish";

 

五.常用的OF API

OF提供的函数主要集中在drivers/of/目录下,有address.c,base.c,device.c,fdt.c,irq. ,platform.c等等

1.根据deice_node结构的fullname参数,在全局链表 of-allnodes 中,查找合适的 device node

struct device_node *of find_node_by_path(const char *path)

2.根据property结构的name参数,在指定的 device node 中查找合适的 property 

struct property *of_find_property(const struct device_node *np, const char*name,int *lenp)

3.根据compat参数与 device node的 compatible匹配,返回匹配度

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

4.获得父节点的device node

struct device_node *of get-parent(const struct device_node *node)

5.根据属性名 propname  ,读出该属性的数组中sz个属性值给 out-values

int of_property_read_u32_array(const struct device_node *np,const char *propname, u8 *out _values, size t sz)

6.读取该设备的第 index 个 irq号

unsigned int irg of parse and map(struct device node *dev, int index)

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值