一、设备树基础概念1、基本数据格式device tree是一个简单的节点和属性树,属性是键值对,节点可以包含属性和子节点。下面是一个.dts格式的简单设备树。/ {node1 {a-string-property = 'A string';a-string-list-property = 'first string', 'second string';a-byte-data-property = [0x01 0x23 0x34 0x56];child-node1 {first-child-property;second-child-property = <1>;a-string-property = 'Hello, world';};child-node2 {};};node2 {an-empty-property;a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */child-node1 {};};};该树并未描述任何东西,也不具备任何实际意义,但它却揭示了节点和属性的结构。即:a -- 一个的根节点:'/',两个子节点:node1和node2;node1的子节点:child-node1和child-node2,一些属性分散在树之间。b -- 属性是一些简单的键值对(key-value pairs):value可以为空也可以包含任意的字节流。而数据类型并没有编码成数据结构,有一些基本数据表示可以在device tree源文件中表示。c -- 文本字符串(null 终止)用双引号来表示:string-property = 'a string'd -- “Cells”是由尖括号分隔的32位无符号整数:cell-property = <0xbeef 123 0xabcd1234>e -- 二进制数据是用方括号分隔:binary-property = [0x01 0x23 0x45 0x67];f -- 不同格式的数据可以用逗号连接在一起:mixed-property = 'a string', [0x01 0x23 0x45 0x67], <0x12345678>;g -- 逗号也可以用来创建字符串列表:string-list = 'red fish', 'blue fish';
二、设备在device tree 中的描述系统中的每个设备由device tree的一个节点来表示;1、节点命名花些时间谈谈命名习惯是值得的。每个节点都必须有一个[@]格式的名称。是一个简单的ascii字符串,最长为31个字符,总的来说,节点命名是根据它代表什么设备。比如说,一个代表3com以太网适配器的节点应该命名为ethernet,而不是3com509。如果节点描述的设备有地址的话,就应该加上unit-address,unit-address通常是用来访问设备的主地址,并在节点的reg属性中被列出。后面我们将谈到reg属性。2、设备接下来将为设备树添加设备节点:/ {compatible = 'acme,coyotes-revenge';cpus {cpu@0 {compatible = 'arm,cortex-a9';};cpu@1 {compatible = 'arm,cortex-a9';};};serial@101F0000 {compatible = 'arm,pl011';};serial@101F2000 {compatible = 'arm,pl011';};gpio@101F3000 {compatible = 'arm,pl061';};interrupt-controller@10140000 {compatible = 'arm,pl190';};spi@10115000 {compatible = 'arm,pl022';};external-bus {ethernet@0,0 {compatible = 'smc,smc91c111';};i2c@1,0 {compatible = 'acme,a1234-i2c-bus';rtc@58 {compatible = 'maxim,ds1338';};};flash@2,0 {compatible = 'samsung,k8f1315ebm', 'cfi-flash';};};};在上面的设备树中,系统中的设备节点已经添加进来,树的层次结构反映了设备如何连到系统中。外部总线上的设备就是外部总线节点的子节点,i2c设备是i2c总线控制节点的子节点。总的来说,层次结构表现的是从CPU视角来看的系统视图。在这里这棵树是依然是无效的。它缺少关于设备之间的连接信息。稍后将添加这些数据。设备树中应当注意:每个设备节点有一个compatible属性。flash节点的compatible属性有两个字符串。请阅读下一节以了解更多内容。 之前提到的,节点命名应当反映设备的类型,而不是特定型号。请参考ePAPR规范2.2.2节的通用节点命名,应优先使用这些命名。3、compatible 属性树中的每一个代表了一个设备的节点都要有一个compatible属性。compatible是OS用来决定绑定到设备的设备驱动的关键。compatible是字符串的列表。列表中的第一个字符串指定了','格式的节点代表的确切设备,第二个字符串代表了与该设备兼容的其他设备。例如,Freescale MPC8349 SoC有一个串口设备实现了National Semiconductor ns16550寄存器接口。因此MPC8349串口设备的compatible属性为:compatible= 'fsl,mpc8349-uart', 'ns16550'。在这里,fsl,mpc8349-uart指定了确切的设备,ns16550表明它与NationalSemiconductor 16550 UART是寄存器级兼容的。注:由于历史原因,ns16550没有制造商前缀,所有新的compatible值都应使用制造商的前缀。这种做法使得现有的设备驱动程序可以绑定到一个新设备上,同时仍能唯一准确的识别硬件。4、编址可编址的设备使用下列属性来将地址信息编码进设备树:reg#address-cells#size-cells每个可寻址的设备有一个reg属性,即以下面形式表示的元组列表:reg =每个元组,。每个地址值由一个或多个32位整数列表组成,被称做cells。同样地,长度值可以是cells列表,也可以为空。既然address和length字段是大小可变的变量,父节点的#address-cells和#size-cells属性用来说明各个子节点有多少个cells。换句话说,正确解释一个子节点的reg属性需要父节点的#address-cells和#size-cells值。5、内存映射设备与CPU节点中的单一地址值不同,内存映射设备会被分配一个它能响应的地址范围。#size-cells用来说明每个子节点种reg元组的长度大小。在下面的示例中,每个地址值是1 cell (32位) ,并且每个的长度值也为1 cell,这在32位系统中是非常典型的。64位计算机可以在设备树中使用2作为#address-cells和#size-cells的值来实现64位寻址。serial@101f2000 {compatible = 'arm,pl011';reg = <0x101f2000 0x1000 >;};gpio@101f3000 {compatible = 'arm,pl061';reg = <0x101f3000 0x10000x101f4000 0x0010>;};interrupt-controller@10140000 {compatible = 'arm,pl190';reg = <0x10140000 0x1000 >;};每个设备都被分配了一个基地址及该区域大小。本例中的GPIO设备地址被分成两个地址范围:0x101f3000~0x101f3fff和0x101f4000~0x101f400f。
三、设备树在platform设备驱动开发中的使用解析我们仍以Linux 设备驱动开发 —— platform设备驱动应用实例解析文中的例子来解析设备树在platform设备驱动中如何使用;1、设备树对platform中platform_device的替换其实我们可以看到,Device Tree 是用来描述设备信息的,每一个设备在设备树中是以节点的形式表现出来;而在上面的 platform 设备中,我们利用platform_device 来描述一个设备,我们可以看一下二者的对比fs4412-beep{compatible = 'fs4412,beep';reg = <0x114000a0 0x40x139D0000 0x14>;};a -- fs4412-beep 为节点名,符合咱们前面提到的节点命名规范;我们通过名字可以知道,该节点描述的设备是beep, 设备名是fs4412-beep;b -- compatible = 'fs4412,beep';compatible 属性, 即一个字符串;前面提到,所有新的compatible值都应使用制造商的前缀,这里是fs4412;c --reg = <0x114000a0 0x40x139D00000x14>;reg属性来将地址信息编码进设备树,表示该设备的地址范围;这里是我们用到的寄存器及偏移量;staticstructresourcebeep_resource[]={[0]={.start=0x114000a0,.end=0x114000a0+0x4,.flags=IORESOURCE_MEM,},[1]={.start=0x139D0000,.end=0x139D0000+0x14,.flags=IORESOURCE_MEM,},};staticstructplatform_devicehello_device={.name='bigbang',//没用了.id=-1,.dev.release=hello_release,.num_resources=ARRAY_SIZE(beep_resource),.resource=beep_resource,};可以看到设备树中的设备节点完全可以替代掉platform_device。2、有了设备树,如何实现device 与 driver 的匹配?我们在上一篇还有 platform_device 中,是利用 .name 来实现device与driver的匹配的,但现在设备树替换掉了device,那我们将如何实现二者的匹配呢?有了设备树后,platform比较的名字存在哪?我们先看一下原来是如何匹配的 ,platform_bus_type 下有个match成员,platform_match 利用pdev->name,drv->name 来进行匹配 。原来在platform_driver 中有如下定义:static struct platform_driver beep_driver={.driver.name = 'bigbang',.probe = beep_probe,.remove = beep_remove,};可以看到原来是利用platform_driver 下的 struct driver 结构体中的 name 成员来匹配的,看一下 struct driver 结构体的定义:struct device_driver {const char *name;struct bus_type *bus;struct module *owner;const char *mod_name; /* used for built-in modules */bool suppress_bind_attrs; /* disables bind/unbind via sysfs */const struct of_device_id *of_match_table;const struct acpi_device_id *acpi_match_table;int (*probe) (struct device *dev);int (*remove) (struct device *dev);void (*shutdown) (struct device *dev);int (*suspend) (struct device *dev, pm_message_t state);int (*resume) (struct device *dev);const struct attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;} 成员中有const struct of_device_id*of_match_table; 是struct of_device_id 类型,定义如下:/** Struct used for matching a device*/struct of_device_id{char name[32];char type[32];char compatible[128];const void *data;}; 可以看到其作用就是为了匹配一个设备。我们所要做的就是对 charcompatible[128] 的填充;设备树加载之后,内核会自动把设备树节点转换成 platform_device这种格式,同时把名字放到of_node这个地方。3、基于设备树的driver的结构体的填充匹配的方式发生了改变,那我们的platform_driver 也要修改了基于设备树的driver的结构体的填充:staticstructof_device_idbeep_table[]={{.compatible='fs4412,beep'},};staticstructplatform_driver beep_driver={.probe= beep_probe,.remove= beep_remove,.driver={.name='bigbang',.of_match_table=beep_table,},};原来的driver是这样的,可以对比一下static struct platform_driver beep_driver={.driver.name = 'bigbang',.probe = beep_probe,.remove = beep_remove,};4、设备树编译我们在arch/arm/boot/dts/exynos4412-fs4412.dts 中添加fs4412-beep{compatible = 'fs4412,beep';reg = <0x114000a0 0x40x139D0000 0x14>;};就可以编译设备树了makedtbs 在内核根目录vimarch/arm/boot/dts/exynos4412-fs4412.dtssudocparch/arm/boot/dts/exynos4412-fs4412.dtb/tftpboot/