Linux下设备树内容(详细)总结及示例解析

转自:https://blog.csdn.net/Luckiers/article/details/124772722

一、简介
设备树是在PowerPC平台最先使用,后来2011年3月份Linux创始人Linus Torvalds在邮件建议ARM社区也使用设备树的方式去描述板级结构。所以设备树其实就是描述开发板上的硬件信息,由于其结构就像现实世界的大树一样,所以就将这种结构叫设备树,如下图所示。
在这里插入图片描述
二、设备树基础内容
2.1 设备树文件存放路径
dts源码都在对应的架构目录下,如arm平台arch/arm/boot/dts,对应的文件主要有1个dts文件+n个dtsi文件,它们编译而成的dtb文件就是真正的设备树。
soc厂商会把soc公共的特性和多块开发板公用的特性提炼为dtsi,而dts则负责描述某个具体的产品(开发板)的特性。dts直接或间接的包含多个dtsi(类似于c语言的头文件),就体现了一个完整的产品(开发板)所有的特性。以solidrun公司的hummingboard为例,其组成为

imx6dl-hummingboard.dts
        |_imx6dl.dtsi
        |   |_imx6qdl.dtsi
        |_imx6qdl-microsom.dtsi
        |_imx6qdl-microsom-ar8035.dtsi 

2.2 DTS、DTB和DTC关系
DTS(devcie tree source):设备树源码文件
DTB(device tree binary):将 .dts 编译后得到二进制文件,下载到 DDR 中的是 .dtb 文件
DTC(device tree compiler):将 .dts 编译为 .dtb 的编译工具,它有个文件夹,经过编译后得到 DTC

2.3 传统驱动代码和使用设备树的对比
传统方式:
(1)代码冗余
在没有使用设备树的时候,有关板级的硬件信息都被硬编码在内核中,这就导致了内核中描述板级硬件的代码过于庞大,不利于阅读。以arm架构为例,通常这些硬件信息会放在/arch/arm/mach-xxx和/arch/arm/plat-xxx里面。

(2)修改麻烦
在内核中有一种叫总线的模型,这种模型的作用是将设备信息与驱动进行分离,在没有使用设备树的情况下,这里的设备信息就被硬编码到/arch/arm/mach-xxx和/arch/arm/plat-xxx中。这就会导致一种不好的结果,当你修改了某个设备的信息时,你就需要重新编译内核并把内核烧到系统中。

设备树的优点:
在使用设备树后,内核中就不再需要那些硬件信息了,描述硬件的信息都会被统一放到/arch/arm/boot/dts的设备树文件中,这样就可以减少代码的冗余。
当你需要修改设备的硬件信息时,只需要在修改设备树之后,对设备树进行编译并放到系统中,uboot就会将设备树的地址告诉Linux内核,让Linux内核到相应的内存地址去解析设备树信息,不再需要重新编译内核。

三、设备树内容属性介绍
3.1 节点名称

node-name@unit-address  //node-name:节点名字 unit-address:表示寄存器基地址或设备地址,如下serial@101f0000
label:node-name@unit-address        // 引入label目的就是为了方便便访问节点,可以直接通过&label来访问这个
serial@101f0000 {  
	compatible = "arm,pl011";  
	reg = <0x101f0000 0x1000 >;  
	interrupts = < 1 0 >;  

3.2 compatible
compatible 属性值为字符串列表,⽤于将设备和驱动绑定起来,字符串列表⽤于选择设备所要使用的驱动程序。

"manufacturer,model"     //manufacturer :厂商  model:模块对应的驱动名

一般驱动程序文件中都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。

3.3 model 属性
model 属性:描述设备模块信息,比如名字什么的,如:model = “wm8960-audio”。

3.4 status 属性
status 属性:描述设备状态,如:okay - 设备可操作,disabled - 设备不可操作

3.5 #address-cells 和 #size-cells 属性
#address-cells 和 #size-cells 描述⼦节点应如何编写 reg 属性值,一般 reg 属性是某个外设的寄存器地址范围信息。

3.6 ranges 属性
ranges它是一个地址映射/转换表,如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换。

3.7 aliases 节点
用 aliases 节点给多个同类型的控制器分配唯一编号,便于Linux内核区分。在Linux启动时会解析aliases节点。

3.8 chosen 节点
chosen 并不是一个真实的设备,主要用于将 uboot 中的 bootargs 环境变量值传递给 Linux 内核作为命令行参数。

四、设备树文件内容示例解析

    / {  
        compatible = "acme,coyotes-revenge";  
        #address-cells = <1>;  
        #size-cells = <1>;  
        interrupt-parent = <&intc>;  
      
        cpus {  
            #address-cells = <1>;  
            #size-cells = <0>;  
            cpu@0 {  
                compatible = "arm,cortex-a9";  
                reg = <0>;  
            };  
            cpu@1 {  
                compatible = "arm,cortex-a9";  
                reg = <1>;  
            };  
        };  
      
        serial@101f0000 {  
            compatible = "arm,pl011";  
            reg = <0x101f0000 0x1000 >;  
            interrupts = < 1 0 >;  
        };  
      
        serial@101f2000 {  
            compatible = "arm,pl011";  
            reg = <0x101f2000 0x1000 >;  
            interrupts = < 2 0 >;  
        };  
      
        gpio@101f3000 {  
            compatible = "arm,pl061";  
            reg = <0x101f3000 0x1000  
                   0x101f4000 0x0010>;  
            interrupts = < 3 0 >;  
        };  
      
        intc: interrupt-controller@10140000 {  
            compatible = "arm,pl190";  
            reg = <0x10140000 0x1000 >;  
            interrupt-controller;  
            #interrupt-cells = <2>;  
        };  
      
        spi@10115000 {  
            compatible = "arm,pl022";  
            reg = <0x10115000 0x1000 >;  
            interrupts = < 4 0 >;  
        };  
      
        external-bus {  
            #address-cells = <2>  
            #size-cells = <1>;  
            ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet  
                      1 0  0x10160000   0x10000     // Chipselect 2, i2c controller  
                      2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash  
      
            ethernet@0,0 {  
                compatible = "smc,smc91c111";  
                reg = <0 0 0x1000>;  
                interrupts = < 5 2 >;  
            };  
      
            i2c@1,0 {  
                compatible = "acme,a1234-i2c-bus";  
                #address-cells = <1>;  
                #size-cells = <0>;  
                reg = <1 0 0x1000>;  
                interrupts = < 6 2 >;  
                rtc@58 {  
                    compatible = "maxim,ds1338";  
                    reg = <58>;  
                    interrupts = < 7 3 >;  
                };  
            };  
      
            flash@2,0 {  
                compatible = "samsung,k8f1315ebm", "cfi-flash";  
                reg = <2 0 0x4000000>;  
            };  
        };  
    };  

4.1 设备树关键内容解析
(1)address、length和#address-cells 、#size-cells的关系
reg的组织形式如reg=<地址1 长度1 [地址2 长度2] [地址3 长度3]…>,每个元组都表示该设备使用的一个地址范围。每个地址值是一个或者多个32整数列表,成为一个cell。由于地址和长度字段都是可变大小的变量,那么父节点的#adderss-cells和#size-cells属性就用来声明各个字段的cell数量。换句话说,正确完整的解释reg属性需要用到父节点的#address-cells和#size-cells的值。
在本例中,root结点的#address-cells = <1>;和#size-cells = <1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1,如: reg = <0x10115000 0x1000 >;
cpus 结点的#address-cells = <1>;和#size-cells = <0>;决定了2个cpu子结点的address为1,而length为空,于是形成了2个cpu的reg = <0>;和reg = <1>;

external-bus结点的#address-cells = <2>和#size-cells = <1>;决定了其下的ethernet、i2c、flash的reg字段形如:
reg = <0 0 0x1000>;
reg = <1 0 0x1000>;
reg = <2 0 0x4000000>;
其中,address字段长度为2,开始的第一列cell(0、1、2)是对应的片选,第2列cell(0,0,0)是相对该片选的基地址,第3列cell(0x1000、0x1000、0x4000000)为length。
特别要留意的是i2c结点中定义的 #address-cells = <1>;和#size-cells = <0>;又作用到了I2C总线上连接的RTC,它的address字段为0x58,是设备的I2C地址。

(2)中断号和电平触发方式

interrupts = < 6 2 >;   //中断号为6,下降沿触发
#电平触发方式定义
1 = low-to-high edge triggered 
2 = high-to-low edge triggered 
4 = active high level-sensitive 
8 = active low level-sensitive 

(3)驱动获取设备树内容的常用函数
设备树描述了设备的详细信息,我们在编写驱动时需要获取到这些信息,Linux内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,内核启动时会解析.dtb文件,从而获取设备树中各个节点的信息,并且在根文件系统的/proc/device-tree目录下根据节点名字创建不同文件夹。

查找节点:of_find_node_by_path 函数,通过指定全路径来查找指定节点。
提取属性值:of_find_property 函数 ,获取到的值保存到了 property 结构体中。
读取属性中字符串值:of_property_read_string 函数。
读取属性中数组数据:of_property_read_u32_array 函数,常用于一次读取出 reg 属性中的所有数据。
直接内存映射:of_iomap 函数,获取内存地址所对应的虚拟地址

(4)绑定信息文档查询
我们往往不知道如何在设备树中添加一个硬件对应的节点,此时我们可以参考Linux内核源码中有详细的.txt文档描述了如何添加节点,这些.txt文档叫做绑定文档。
路径:/Documentation/devicetree/bindings
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LINUX设备驱动第三版_ 前言 第一章 设备驱动程序简介 设备驱动程序的作用 内核功能划分 设备和模块的分类 安全问题 版本编号 许可证条款 加入内核开发社团 本书概要 第二章 构造和运行模块 设置测试系统 Hello World模块 核心模块与应用程序的对比 编译和装载 内核符号表 预备知识 初始化和关闭 模块参数 在用户空间编写驱动程序 快速参考 第三章 字符设备驱动程序 scull的设计 主设备号和次设备号 一些重要的数据结构 字符设备的注册 open和release scull的内存使用 read和write 试试新设备 快速参考 第四章 调试技术 内核中的调试支持 通过打印调试 通过查询调试 通过监视调试 调试系统故障 调试器和相关工具 第五章 并发和竞态 scull的缺陷 并发及其管理 信号量和互斥体 completion 自旋锁 锁陷阱 除了锁之外的办法 快速参考 第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 第七章 时间、延迟及延缓操作 度量时间差 获取当前时间 延迟执行 内核定时器 tasklet 工作队列 快速参考 第八章 分配内存 kmalloc函数的内幕 后备高速缓存 get_free_page和相关函数 vmalloc及其辅助函数 per-CPU变量 获取大的缓冲区 快速参考 第九章 与硬件通信 I/O端口和I/O内存 使用I/O端口 I/O端口示例 使用I/O内存 快速参考 第十章 中断处理 准备并口 安装中断处理例程 实现中断处理例程 顶半部和底半部 中断共享 中断驱动的I/O 快速参考 第十一章 内核的数据类型 使用标准C语言类型 为数据项分配确定的空间大小 接口特定的类型 其他有关移植性的问题 链表 快速参考 第十二章 PCI驱动程序 PCI接口 ISA回顾 PC/104和PC/104+ 其他的PC总线 SBus NuBus 外部总线 快速参考 第十三章 USB驱动程序 USB设备基础 USB和Sysfs USB urb 编写USB驱动程序 不使用urb的USB传输 快速参考 第十四章 Linux设备模型 kobject、kset和子系统 低层sysfs操作 热插拔事件的产生 总线、设备和驱动程序 类 各环节的整合 热插拔 处理固件 快速索引 第十五章 内存映射和DMA Linux的内存管理 mmap设备操作 执行直接I/O访问 直接内存访问 快速参考 第十六章 块设备驱动程序 注册 块设备操作 请求处理 其他一些细节 快速参考 第十七章 网络驱动程序 snull设计 连接到内核 net_device结构细节 打开和关闭 数据包传输 数据包的接收 中断处理例程 不使用接收中断 链路状态的改变 套接字缓冲区 MAC 地址解析 定制 ioctl 命令 统计信息 组播 其他知识点详解 快速参考 第十八章 TTY驱动程序 小型TTY驱动程序 tty_driver函数指针 TTY线路设置 ioctls proc和sysfs对TTY设备的处理 tty_driver结构详解 tty_operations结构详解 tty_struct结构详解 快速参考 参考书目 9112405-1_o.jpg (85.53 KB, 下载次数: 50)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值