翻译来自https://www.rocketboards.org/foswiki/Documentation/EmbeddedLinuxBeginnerSGuide
设备树是一种定义系统内部硬件(包括soc级硬件和板级硬件)的数据结构。设备树是独立于U-Boot和内核之外的一个单独的文件,可单独修改。这意味着,只要编译了这两个软件让其支持你要添加的那些设备了,那您就可以在设备树里做相应的添加或修改来说明您拥有该硬件,接下来内核将处理该过程的其余部分(加载和运行对应的驱动程序)。
在设备树出现之前,每个SoC和主板制造商都需要在内核中编写初始化代码,以告诉内核他们特定的平台支持什么。这样,内核将只加载系统中特定硬件的驱动程序。这些SoC或主板有一个“机器ID”跟它们绑定,U-Boot会将机器ID传递给正在启动的机器的内核。这让内核知道要运行哪些初始化例程。
随着基于arm的SoC设备的激增,这成为Linux内核维护者的维护噩梦。许多SoC设备具有非常相似的初始化代码或外设,但每个芯片都有自己的内存映射和中断向量。随着SoC设备的增加,在设备之间共享代码变得越来越困难,因为每个制造商必须将地址、寄存器和其他特定于设备的信息硬编码到内核代码中。最终,很多内核开发人员厌倦了处理所有这些问题,决定采用开放固件项目使用的扁平设备树(FDT)接口来描述系统中的硬件。
现在,可以在运行时动态确定外设配置细节,而不需要再为功能相同的硬件(也许只碰巧具有不同的内存映射)编写额外的代码。设备树不仅存储内存映射,它还包含有关所使用的中断(如果有的话)、时钟以及驱动程序可能需要了解如何使用设备的任何其他信息的详细信息。有了设备树,单个Linux映像就可以支持多种硬件变体(当您的硬件是可编程的时,这一点显然很重要)。
设备树以人类可读(这是有争议的)文本格式(称为设备树源(dts))编写,并编译成设备树二进制文件(dtb)。这个设备树二进制文件通过U-Boot从SD卡加载并放入RAM中。然后bootz命令将加载设备树二进制文件的地址传递给内核。当内核启动时,它加载设备树,解析它,并确定加载哪些驱动程序以适应您的平台硬件。
Linux内核使用设备树数据结构有三个主要目的:
- 平台识别(它在哪个SoC/板上运行)
- 运行时配置(传递引导参数的另一种方法)
- 设备填充(为SoC/板上的任何外设或设备加载驱动程序)
了解设备树源
正如其名所示,设备树具有层次结构。有一个由多个子节点组成的根节点。每个子节点可以有其他子节点,每个节点可以有许多不同的属性(一些是标准的,一些是特定于驱动程序的)。设备树源通常包含以下信息:
- cpu数量
- 各种ram的大小和位置
- 总线和桥
- 外围设备连接
- 中断控制器和IRQ线路连接
- 具体的设备驱动配置,例如:
-
- 以太网MAC地址
- 外设输入时钟
下面是来自Altera SoC研讨会系列的图片,它分解了单个节点的内容:
这只是一种特定类型的节点(这里举例的是Synopsis I2C 控制器)。不同的设备需要不同的属性设定或绑定。当从设备树中读取信息时,每个驱动程序通常都有自己特定的属性集。Linux内核中所有驱动程序的“绑定”都位于内核源代码中的“Documentation/devicetree/bindings”中。在创建您的设备树源时,请确保仔细阅读您的设备将使用的驱动程序的确切绑定。
但是内核如何知道如何将设备树中的设备与驱动程序相匹配呢?其实是通过每个设备树节点中的“compatible”字符串。该字符串显示在设备树中的大多数节点中,以及支持设备树设备的任何驱动程序中。每个驱动程序都在一个“兼容性字符串”列表中进行硬编码,这些字符串定义了它们支持哪些设备。当内核在加载设备树时,它读取每个节点的兼容性字符串,并尝试查找具有相同兼容性字符串的驱动程序。如果它找到一个匹配的,它就加载那个驱动程序,并调用驱动程序的“probe”函数来通知它添加了一个新设备(这将在本指南的驱动程序部分进行更详细的解释)。
停!在进一步讨论之前,我强烈建议阅读Free electronics发布的“Device Tree for Dummies”指南。如果你想看演示,这里还有一个视频video。
如果您仍然对设备树的功能和工作原理感到困惑,请跳到本页底部的参考资料部分去查阅更多资料。参考资料还包含一些设备树语法解释的链接。
设备树生成器
随着可重新编程SoC的发明,只需为SoC编写一次设备树就在不需要修改的场景变少了。由于Altera SoC中的硬件可以更改,因此每次硬件更改时都需要重新创建设备树(这确保内核加载正确的驱动程序和初始化例程)。幸运的是,Altera提供了一个工具,使这个过程相对简单。
设备树生成器工具接受Qsys输出的.sopcinfo文件以及一个或多个描述板上设备的板XML文件(不在芯片/Qsys系统内的任何东西)。然后,它将神奇地自动为您生成设备树源。之后,您所要做的就是将设备树源代码编译成二进制文件,然后就可以开始使用了。虽然您可以让设备树为您生成二进制文件(如下图所示),但我建议您只生成源代码,然后再编译。这样,您就有机会检查生成的源代码,并验证它是否做得很好。
这些XML文件描述了SoC连接到的PCB上的设备和外设。因为这些外部设备不会出现在Qsys中,所以如果你不明确说明它们是什么,生成器就不可能知道它们并正确地将它们包含在设计中。这些文件采用altera创建的语法,将新属性和新节点附加到源中已经存在的节点(也就是从.sopcinfo文件生成的节点)。
生成设备树
sopc2dts --input soc_system.sopcinfo\ --output soc_system.dts\ --type dts\ --board soc_system_board_info.xml\ --board hps_common_board_info.xml\ --bridge-removal all\ --clocks
dtc -I dts -O dtb -o soc_system.dtb soc_system.dts
为自定义IP添加设备树节点信息
当我们为这个模块创建自定义驱动程序时,将使用上述兼容性字符串。我们的模块将在内核中注册,它可以支持任何具有“dev,custom-leds”兼容性字符串的设备。一旦内核知道了这一点,它就会告诉我们的驱动程序,它在读取设备树时找到了这样一个设备。 在那之后,我们的驱动程序将可以访问任何放置在设备树中的信息(在本例中,是寄存器映射和时钟),用于“Custom LEDs”模块的特定实例化。如果那个自定义IP有更多的实例化,那么驱动程序将为每个实例化调用一次。
参考资源
- Device Tree for Dummies slides A great explanation of what device trees are and how they work.
- The video for the above slides
- The Linux kernel’s own Device Tree usage documentation If the previous slides didn’t clear up any confusion, this document goes into great detail about what the device tree is and how it fits into the Linux driver model.
- A Device Tree Usage walkthrough Explains the device tree source syntax in quite a bit of detail.
- The official Device Tree specification (it’s a part of the ePAPR spec) It’s about as exciting and easy to read as you’d imagine. Needless to say, it makes for some great bathroom reading.
- Part 1 and Part 2 of a series Linux Weekly News did on the Device Tree
- Rocketboards documentation for the Device Tree Generator Anything you should ever need to know about the device tree generator all in one place.
- How to add Device Tree Generation support to a custom IP Block If you have custom IP you’re integrating into a Qsys system, then this guide will tell you how to make your IP play nice with the Device Tree Generator.