【Linux驱动开发】007 设备树知识总结

欢迎加入QQ技术交流群:100479172

一、为什么需要设备树

直接原因:Linus Torvalds的鼓励,“this whole ARM thing is a fucking pain in the ass”!

主要原因:同一款SOC可以制作很多板子,不同的板子具有不同的硬件配置,然而板子上面的硬件外设正常运行必须有相应的驱动,这些驱动也就需要添加在linux内核中。同一个SOC可以制作很多板子,众多SOC可以制作无数的板子,也就会有无数个板子板子对应的驱动文件,每一个板子的驱动文件都需要添加到内核中,也就导致linux内核具有大量垃圾代码。被Linus Torvalds看到后,就开始鼓励大家了!

为什么要用设备树device tree_设备树的作用是什么_Gaosiy的博客-CSDN博客


二、设备树简介

不同的板子一定具有相同的信息,比如IMX6ULL这个SOC,其不同的板子对应的SOC级信息(几个CPU、主频等)使相同的,如果把这一部分信息分离出来,用.dtsi文件表示,对于具有独特外设的板子,只需要使用.dts文件描述即可。这样,不同的板子(相同SOC)只需要使用同一个.dtsi头文件,针对自己外设的dts文件即可。这样针对一个SOC只需要将你自己板子对应的驱动(dts文件)编译近linux内核即可。

“将描述板级硬件信息的内容从linux中分离出来,用一个专属的文件格式来描述,这个专属的文件格式就叫做设备树,也就是dts后缀的文件。”


三、设备树语法

1、基本概念

.dts:设备树源码文件。一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、SPI 设备等)

.dtb:设备树编译得到的文件。

.dtsi文件:SOC级信息描述文件,或者说是设备树头文件。

将.dts 编译为.dtb需要什么工具呢?需要用到 DTC 工具!

进入到Linux源码目录:

make all (编译所有东西,包括 zImage,.ko 驱动模块以及设备树)

或者

make dtbs (只编译设备树)

 在.dts 设备树文件中,可以通过 “#include” 来引用 .h、.dtsi 和.dts 文件。

2、设备节点

设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是 键—值对。

下面是飞思卡尔官方开发板的一个设备树文件(部分),截图中标出了一个设备树节点的组成,主要包括:

根节点:一个设备树中只允许有一个根节点。

子节点:位于某一个节点内的节点。

节点标签:类似于C++的引用,主要是为了方便访问节点,通过“&标签”即可访问。

节点名:无需解释。

设备地址:如果节点没有地址或寄存器,可以省略。

也就是说,一个节点组成为:label:node-name @ unit-address

3、设备树常见数据形式

字符串:compatible = "arm,cortex-a7"; 

32位无符号整型:reg = <0>; 

字符串列表:compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand"; 

4、标准属性

compatible属性:

        compatible = "manufacturer,model" ;

manufacturer表示厂商,model表示驱动名字。

该属性的主要作用就是选择设备需要使用的驱动程序。(与驱动程序的OF匹配表对应,如果此处的compatible和某个驱动文件的OF匹配表compatible相同,表示该设备可以使用此驱动程序)。

        compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960"; 

如果compatible有两个属性值,那么首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。

        根节点的compatible属性有什么作用呢?如:compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

通过根节点的 compatible 属性可以知道我们所使用的设备,一般第一个值描述了所使用的硬件设备名字,比如这里使用的是 “imx6ull-14x14-evk” 这个设备,第二个值描述了设备所使用的 SOC,比如这里使用的是 “imx6ull” 这颗 SOC。 Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备,如果支持的话设备就会启动 Linux 内核。(见4.2)

model属性:

设备模块信息,如名字。

status属性:

设备状态相关。

“okay”表示设备可以操作,“disabled”表示设备当前不可操作,但可能变为可操作的,“fail”表示设备不操作。

#address-cells和#size-cells属性

无符号32位整型,#address-cells 和#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息,前者描述reg属性(<address length>键值对)地址信息所占用的字长,后者描述reg属性中长度信息所占字长(32bit,1字长)。

https://www.cnblogs.com/youchihwang/p/7050846.html

reg属性:

描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。reg = <address1 length1 address2 length2 ……>,其值为<address,length>键值对 。

ocram: sram@00900000 { // @后面的 0x00900000 就是 ocram 的起始地址。
    compatible = "fsl,lpm-sram"; 
    reg = <0x00900000 0x20000>; //  reg 属性也指明了 ocram 内存的起始地址为 0x00900000,大小为 0x20000。 
}; 

ranges属性:地址映射转换表。不懂,懂得评论区补充。

names属性:记录节点名字,已弃用。


四、设备(板子)解析方法

上面节点的compatible属性是为了匹配对应的驱动,而根节点的属性是为了匹配对应的板子(设备)!如果无法匹配,也就说明linux内核不支持该设备,当然也就无法启动。详见正点原子驱动指南page1091-1094。

1、不使用设备树

在没有使用设备树以前,uboot 会向 Linux 内核传递一个叫做 machine id 的值,machine id 也就是设备 ID,告诉 Linux 内核自己是个什么设备,看看 Linux 内核是否支持。

linux内核支持的设备在内核里面都是一个结构体,uboot传递过来的machine id会与结构体中的id进行匹配,检查linux内核是否支持该设备。

2、使用设备树

对于每一个linux内核支持的设备,也同样会有一个 machine_desc 结构体。根据设备树根节点的compatible属性,对比linux内核中设备结构体中id属性值,检查是否支持该设备。


五、向设备树结点追加数据

假设需要往 i2c1 节点中添加 fxls8471 子节点,i2c1 节点是定义在 imx6ull.dtsi 文件中的,如果直接在 i2c1 节点中添加 fxls8471 相当于在其他的所有板子上都添加了 fxls8471 这个设备,其他所有使用到I.MX6ULL这颗 SOC 的板子都会引用 imx6ull.dtsi 这个文件,但是其他的板子可能并没有这个设备!所以这样操作是不行的。

按照以下方式在.dts文件中添加 fxls8471子节点即可:

1 &i2c1 { 
2   /* 要追加或修改的内容 */ 
3 }; 

通过&label 来访问节点,然后直接在里面编写要追加或者修改的内容,不会对其他使用这颗SOC的板子造成任何影响。


六、设备树在文件系统中的体现

Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的 /proc/device-tree 目录下根据节点名字创建不同文件夹,/proc/device-tree 目录下是根节点“/”的所有属性和子节点, /proc/device-tree 目录就是设备树在根文件系统中的体现,同样是按照树形结构组织的。

注:上方未加粗的是属性,加粗的是子节点。


七、特殊节点

1、aliases 子节点 

aliases 节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。不过我们一般会在节点命名的时候会加上 label,然后通过&label来访问节点,这样也很方便。

18 aliases { 
19    can0 = &flexcan1; 
20    can1 = &flexcan2; 
21    ethernet0 = &fec1; 
22    ethernet1 = &fec2; 
23    gpio0 = &gpio1; 
24    gpio1 = &gpio2; 
...... 
42    spi0 = &ecspi1; 
43    spi1 = &ecspi2; 
44    spi2 = &ecspi3; 
45    spi3 = &ecspi4; 
46    usbphy0 = &usbphy1; 
47    usbphy1 = &usbphy2; 
48 }; 

2、chosen 子节点 

chosen 并不是一个真实的设备,chosen 节点主要是为了 uboot 向 Linux 内核传递数据,重
点是 bootargs 参数。
一般.dts 文件中 chosen 节点通常为空或者内容很少:

// 仅设置了属性“stdout-path”,表示标准输出使用 uart1。
18 chosen { 
19    stdout-path = &uart1; 
20 }; 

进入到 /proc/device-tree/chosen 目录里面,会发现多了 bootargs 这个属性,但是我们并未在设备树中定义 bootargs 变量。其实,如下图所示, uboot 中的 fdt_chosen 函数在设备树的 chosen 节点中加入了 bootargs 属性,并且还设置了 bootargs 属性值。


八、绑定信息文档

那么我们在设备树中添加一个硬件对应的节点的时候从哪里查阅相关的说明呢?在Linux 内核源码中有详细的.txt 文档描述了如何添加节点,这些.txt 文档叫做绑定文档,路径为:Linux 源码目录/Documentation/devicetree/bindings。


以上掺杂个人理解,如有错误,评论区指出。2022/05/11于武汉。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
设备树(device tree)机制是Linux内核从linux-3.x版本开始引进的一种机制,目的是解决内核源码的arch/arm目录下代码混乱的问题:随着ARM生态的快速发展,在内核源码的arch/arm目录下,存放着几十种arm芯片和几百个开发板相关的源文件,很多开发板和处理器的中断、寄存器等相关硬件资源都在这个目录下以.c或.h的文件格式定义。而对于内核来说,与这些硬件耦合,会导致内核代码混乱不堪,每个开发板上运行的内核镜像都必须单独编译配置,无法通用。什么时候Linux内核能像Windows镜像那样,无论你的电脑什么配置,一个Windows安装包,都可以直接下载安装运行呢?设备树机制,实现了Linux内核和硬件平台的解耦:每个硬件平台的硬件资源使用一个设备树文件(xxx.dts)来描述,而不是在arch/arm下以.c 或 .h 文件来定义。Linux内核是一个通用的内核,在启动过程中,在通过解析设备树中的硬件资源来初始化某个具体的平台。 引入设备树后,很多和内核驱动开发的工作也发生了变化:以往驱动工程师关注的头文件宏定义、寄存器定义,现在这些基本上不用关注,关注的重点则转向了如何根据硬件平台去配置和修改设备树文件。很多驱动的编程接口也发生了变化,开始慢慢使用device tree提供的编程接口去开发驱动。本期课程主要面向嵌入式开发人员,分享Linux驱动开发所需要的设备树知识和必备技能

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kashine

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值