linux 设备树【转】

转自:http://blog.csdn.net/chenqianleo/article/details/77779439

[-]

  1. linux 设备树
    1. 为什么要使用设备树Device Tree
    2. 设备树的的组成和结构
      1. 1设备树的组成
        1. 11 DTS和DTSI
        2. 12 DTC
        3. 13 DTB
        4. 14 绑定bingding
        5. 15 Bootloader 使用dtb
      2. 2设备树框架
    3. 设备树语法
      1. 下面这个是rk3399-fpgadts
      2. 1根节点兼容性
      3. 2节点名
      4. 3引用
      5. KEY
        1. 1compatible
        2. 2address
        3. 3interrupts
        4. 4gpio
      6. DTB的加载过程
      7. API调用

linux 设备树



参考地址
http://blog.csdn.net/green1900/article/details/45646095
http://www.cnblogs.com/xiaojiang1025/p/6131381.html
http://blog.csdn.net/21cnbao/article/details/8457546


1.为什么要使用设备树(Device Tree)?

在以前的内核源码中,存在大量对板级细节信息描述的代码,这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录,对内核而言这些platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data绝大多数纯属垃圾冗余代码。为了解决这一问题,ARM内核版本3.x之后引入了原先在Power
PC等其他体系架构已经使用的Flattened Device Tree。DTS不是arm的专利

在使用了设备树后,对于同一SOC的不同主板,只需更换设备树文件.dtb即可实现不同主板的无差异支持,而无需更换内核文件。


2.设备树的的组成和结构

设备树可以描述的信息包括了
1. CPU的数量和类别、
2. 内存基地址和大小、
3. 总线和桥、
4. 外设连接、
5. 中断控制器和中断使用情况、
6. GPIO控制器和GPIO使用情况、
7. Clock控制器和Clock使用情况。
需要注意的是,设备树对于可热插拔的热备不进行具体描述,它只描述用于控制该热插拔设备的控制器

2.1设备树的组成

设备树包含了DTC(device tree compiler) , DTS(device tree resource) 和 DTB(device tree blob),简单来说,dts是源码,dtc是编译器,dtb是生成的可执行文件

2.1.1 DTS和DTSI

.dts和.dtsi是一种ASCII文本的设备树描述,此文本格式非常适合人们阅读,基本上,一个.dts对应一种ARM设备,放在arch/arm/boot/dts目录,由于一个soc对应好多个不同的开发板,每个开发板有一个.dts,所以这些dts势必有共同部分,为了减少代码的屯余,设备树将这些共同部分提炼保存在dtsi中,供不同的dts使用,dtsi文件类似于c语言的头文件

2.1.2 DTC

DTC为编译工具,它可以将.dts文件编译成.dtb文件。DTC的源码位于内核的scripts/dtc目录,内核选中CONFIG_OF,编译内核的时候,主机可执行程序DTC就会被编译出来

2.1.3 DTB

DTB设备由DTC编译后的二进制格式的设备树描述,可以由linux内核解析,uboot这样的bootloader也可以识别.dtb,有两种使用方式,一种是bootloader启动内核过程中会先读取dtb到文件中;第二种是把dtb和zImage打包在一起做成一个印象文件,firefly-3399就是采用这种方式,打包生成了boot.img

2.1.4 绑定(bingding)

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

2.1.5 Bootloader 使用dtb

在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,可以用
-代替,第三个就是dtb地址

2.2设备树框架

设备树用树状结构描述设备信息,它有以下几种特性
1. 每个设备树文件都有一个根节点,每个设备都是一个节点。
2. 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
3. 每个设备的属性都用一组key-value对(键值对)来描述。
4. 每个属性的描述用;结束


3. 设备树语法

设备树是一颗树,书上的每个节点由节点和属性组成,属性是键值对

下面这个是rk3399-fpga.dts

#include "rk3399.dtsi"  //包含了公共部分
/ {
        model = "Rockchip RK3399 FPGA Board";
        compatible = "rockchip,fpga", "rockchip,rk3399"; //根节点兼容性分析,下面具体分析
        chosen {
                bootargs = "init=/init console=uart,mmio32,0xff1a0000";
        };
        memory@00000000 { //子节点  memory@00000000节点名
                device_type = "memory";
                reg = <0x0 0x00000000 0x0 0x20000000>;
        };
};
&uart2 { //使用了引用
        status = "okay";
        clocks = <&xin24m>, <&xin24m>;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.1根节点兼容性

compatible = "rockchip,fpga", "rockchip,rk3399";
  • 1
上面是根节点的兼容属性,定义了整个系统(设备级别)的名称,通过这个属性就可以判断出它启动的是什么设备。它的组织形式是&lt;manufacture&gt;&lt;model&gt;,在实际中一般包括两个或两个以上的兼容字符串,上面第一个是"rockchip,fpga",第二个是"rockchip,rk3399",我们来看第二个,manufacture是板子级别的名字,“rockchip”代表的是瑞芯微公司,model是芯片级别的,“rk3399”是瑞芯微公司一个soc的名称

我们从源码中找出rk3399的两个dts,可以看出第一个兼容字符串的model不同,第二个完全相同

rk3399-firefly-linux.dts
compatible = "rockchip,rk3399-firefly-linux", "rockchip,rk3399";
rk3399-fpga.dts
compatible = "rockchip,fpga", "rockchip,rk3399";
  • 1
  • 2
  • 3
  • 4

3.2节点名

理论个节点名只要是长度不超过31个字符的ASCII字符串即可,Linux内核还约定设备名应写成形如[@]的形式,其中name就是设备名,最长可以是31个字符长度。unit_address一般是设备地址,用来唯一标识一个节点
Linux中的设备树还包括几个特殊的节点,比如chosen,chosen节点不描述一个真实设备,而是用于firmware传递一些数据给OS,比如bootloader传递内核启动参数给内核

chosen{
    bootargs = "console=ttySAC2,115200";
    stdout-path=&serial_2;
};
  • 1
  • 2
  • 3
  • 4

3.3引用

当我们找一个节点的时候,我们必须书写完整的节点路径,这样当一个节点嵌套比较深的时候就不是很方便,所以,设备树允许我们用下面的形式为节点标注引用(起别名),借以省去冗长的路径。这样就可以实现类似函数调用的效果

3.KEY

在设备树中,键值对是描述属性的方式,比如,Linux驱动中可以通过设备节点中的”compatible”这个属性查找设备节点
inux设备树语法中定义了一些具有规范意义的属性,包括:compatible, address,
interrupt等,这些信息能够在内核初始化找到节点的时候,自动解析生成相应的设备信息。此外,还有一些Linux内核定义好的,一类设备通用的有默认意义的属性,这些属性一般不能被内核自动解析生成相应的设备信息,但是内核已经编写的相应的解析提取函数,常见的有
“mac_addr”,”gpio”,”clock”,”power”。”regulator” 等等。

3.1.compatible

设备节点中对应的节点信息已经被内核构造成struct platform_device。驱动可以通过相应的函数从中提取信息。主要有三种方法提取信息

    1、compatible属性是用来查找节点
    2、通过节点名查找指定节点
    3、节点路径查找指定节点
  • 1
  • 2
  • 3

看一个使用compatible提取属性的例子

#dts
    gpio_demo: gpio_demo {
        status = "okay";
        compatible = "firefly,rk3399-gpio";
    };
#驱动代码
static struct of_device_id firefly_match_table[] = {
    { .compatible = "firefly,rk3399-gpio",}, //完全相同
    {}, //最后一个成员一定是空,因为相关的操作API会读取这个数组直到遇到一个空。
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.2address

  • #address-cells,用来描述子节点”reg”属性的地址表中用来描述首地址的cell的数量
  • #size-cells,用来描述子节点”reg”属性的地址表中用来描述地址长度的cell的数量。
        pinctrl: pinctrl {
                compatible = "rockchip,rk3399-pinctrl";
                #address-cells = <0x2>;
                #size-cells = <0x2>;
                gpio0: gpio0@ff720000 {
                        compatible = "rockchip,gpio-bank";
                        reg = <0x0 0xff720000 0x0 0x100>;
                        //前两个数字表示一个地址0x0 0xff720000
                        //后两个数字表示一个地址跨度 0x100
                };
            ...
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.3interrupts

一个计算机系统中大量设备都是通过中断请求CPU服务的,所以设备节点中就需要在指定中断

  • interrupt-controller 一个空属性用来声明这个node接收中断信号,即这个node是一个中断控制器
  • #interrupt-cells,是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符,用来描述子节点中”interrupts”属性使用了父节点中的interrupts属性的具体的哪个值。一般,如果父节点的该属性的值是3,则子节点的interrupts一个cell的三个32bits整数值分别为:<中断域 中断 触发方式>,如果父节点的该属性是2,则是<中断 触发方式>
  • interrupt-parent,标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的
  • interrupts,一个中断标识符列表,表示每一个中断输出信号

3.4gpio

  • gpio-controller,用来说明该节点描述的是一个gpio控制器
  • #gpio-cells,用来描述gpio使用节点的属性一个cell的内容,即 `属性 = <&引用GPIO节点别名 GPIO标号 工作模式>
firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;          /* GPIO0_B4 */
firefly-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO4_D5 */  
  • 1
  • 2

4.DTB的加载过程

参考地址
http://blog.csdn.net/green1900/article/details/45646095

http://blog.csdn.net/lichengtongxiazai/article/details/38941913

总的归纳为:

① kernel入口处获取到uboot传过来的.dtb镜像的基地址

② 通过early_init_dt_scan()函数来获取kernel初始化时需要的bootargs和cmd_line等系统引导参数。

③ 调用unflatten_device_tree函数来解析dtb文件,构建一个由device_node结构连接而成的单向链表,并使用全局变量of_allnodes保存这个链表的头指针。

④ 内核调用OF的API接口,获取of_allnodes链表信息来初始化内核其他子系统、设备等。


5.API调用

#来查找在dtb中的根节点
unsigned long __init of_get_flat_dt_root(void)

# 根据deice_node结构的full_name参数,在全局链表of_allnodes中,查找合适的device_node
struct device_node *of_find_node_by_path(const char *path)

#若from=NULL,则在全局链表of_allnodes中根据name查找合适的device_node
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)

#根据设备类型查找相应的device_node
struct device_node *of_find_node_by_type(struct device_node *from,const char *type)

# 根据compatible字符串查找device_node
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

#根据节点属性的name查找device_node
struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name)

#根据compat参数与device node的compatible匹配,返回匹配度
int of_device_is_compatible(const struct device_node *device,const char *compat)

#获得父节点的device node
struct device_node *of_get_parent(const struct device_node *node)

#读取该设备的第index个irq号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

#读取该设备的第index个irq号,并填充一个irq资源结构体
int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)

#获取该设备的irq个数
int of_irq_count(struct device_node *dev)
时间: 2024-09-20 21:31:50

linux 设备树【转】的相关文章

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

转自:http://www.360doc.com/content/15/1113/11/15700426_512794532.shtml   设备树手册(Device Tree Usage)原文地址:http://www.devicetree.org/Device_Tree_Usage 有关device tree数据格式的更完整技术说明,读者可以参考ePAPR规范(http://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.0

Linux 获取设备树源文件(DTS)里描述的资源【转】

转自:http://www.linuxidc.com/Linux/2013-07/86839.htm 转自:http://blog.sina.com.cn/s/blog_636a55070101mced.html 在linux使用platform_driver_register() 注册  platform_driver 时, 需要在 platform_driver 的probe() 里面知道设备的中断号, 内存地址等资源. 这些资源的描述信息存放在 resource 数据结构中, 相同的资源存

《Linux设备驱动开发详解 A》一一3.4 Linux内核的编译及加载

3.4 Linux内核的编译及加载 3.4.1 Linux内核的编译 Linux驱动开发者需要牢固地掌握Linux内核的编译方法以为嵌入式系统构建可运行的Linux操作系统映像.在编译内核时,需要配置内核,可以使用下面命令中的一个: make conf?ig(基于文本的最为传统的配置界面,不推荐使用) make menuconf?ig(基于文本菜单的配置界面) make xconf?ig(要求QT被安装) make gconf?ig(要求GTK+被安装) 在配置Linux内核所使用的make c

imx6设备树pinctrl解析【转】

转自:http://blog.csdn.net/michaelcao1980/article/details/50730421 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在移植linux,用到kernel版本为3.14.28,在高版本的内核源码中用到了设备树(device-tree),设备树中用到pinctrl的配置,记录一下. 1.普通设置 在配置串口时,pinctrl的配置信息如下所示:     &uart2 {       pinctrl-names = ;   ;   /

Linux设备模型(热插拔、mdev 与 firmware)【转】

转自:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077469.html 转自:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&cuid=460882 热插拔有 2 个不同角度来看待热插拔:   从内核角度看,热插拔是在硬件.内核和内核驱动之间的交互.   从用户角度看,热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug.udev 和 mdev)的交

linux目录树简介

linux目录树如下: 详解: /bin: 系统有很多放置执行档的目录,但/bin比较特殊.因为/bin放置的是在单人维护模式下还能够被操作的指令.在/bin底下的指令可以被root与一般帐号所使用,主要有:cat, chmod, chown, date, mv, mkdir, cp, bash等等常用的指令. /boot: 这个目录主要在放置开机会使用到的档案,包括Linux核心档案以及开机选单与开机所需设定档等等. Linux kernel常用的档名为:vmlinuz,如果使用的是grub这

微软发布2万行Linux设备驱动程序代码

[新浪科技讯]7月21日凌晨消息,微软公司宣布首次直接面向Linux社区发布2万行Linux设备驱动程序代码,以增加其在虚拟化市场的竞争力. 首次发布2万行Linux驱动代码 北京时间7月21日凌晨,微软公司对外宣布,面向Linux内核社区发布2万行的设备驱动程序代码.这是微软首次直接面向Linux社区发布Linux设备驱动程序代码.这些代码的许可证类型是GPLV2(GNU通用公共授权第二版),这是目前Linux社区最受欢迎的许可方式. 微软发布的2万行设备驱动程序代码可供Linux社区和客户使

为系统处理器编写Linux设备驱动程序

引 言 编写 Linux 设备驱动程序无疑是一项复杂的工作.本文将集中介绍非标准硬件的设备驱动程序编写,探讨硬件应用编程接口,并借用 Cirrus Logic EP9312 片上系统嵌入式平台添加设备驱动程序这一案例来进行分析. 如果有些编程内容未能在本文中涉及,那么读者亦可以查阅相似的设备驱动程序编码,以做参考.还有一种方法,就是检索历史档案或者向 Linux 内核问讯中心去函问讯. Linux 概述 Linux 是 UNIX 操作系统的翻版,1991 年由 Linus Torvalds 最先

《Linux 设备驱动开发详解(第2版)》——1.6 设备驱动Hello World:LED驱动

1.6 设备驱动Hello World:LED驱动 Linux 设备驱动开发详解(第2版)1.6.1 无操作系统时的LED驱动 在嵌入式系统的设计中,LED一般直接由CPU的GPIO(通用可编程I/O口)控制.GPIO一般由两组寄存器控制,即一组控制寄存器和一组数据寄存器.控制寄存器可设置GPIO口的工作方式为输入或是输出.当引脚被设置为输出时,向数据寄存器的对应位写入1和0会分别在引脚上产生高电平和低电平:当引脚设置为输入时,读取数据寄存器的对应位可获得引脚上的电平为高或低. 在本例子中,我们