底层软件 | 十分详细,为了学习设备树,我写了5w字笔记!

  • 0、设备树是什么?
  • 1、DTS
    • 1.1 dts简介
    • 1.2 dts例子
  • 2、DTC(Device Tree Compiler)
  • 3、DTB(Device Tree Blob)
  • 4、绑定(Binding)
  • 5、Bootloader
    1. compatible属性
  • 7、 #address-cells和#size-cells属性
  • 8、reg属性
  • 9、根节点兼容性
  • 10、地址编码
  • 11、ranges
  • 12、中断连接
  • 13、GPIO
  • 14、时钟
  • 15、pinmux
  • 16、设备点属性回顾
  • 17、设备添加label
  • 1.寻找节点
  • 2.读取属性
  • 3.内存映射
  • 4.解析中断
  • 5.获取与节点对应的platform_device
    1. 打个总结
    • 1、注册platform_device,绑定resource,即内存、IRQ等板级信息
    • 2、注册i2c_board_info,指定IRQ等板级信息
    • 3、注册spi_board_info,指定IRQ等板级信息
    • 4、多个针对不同电路板的设备,以及相关的回调函数
    • 5、设备与驱动的匹配方式
    • 6、设备的平台数据属性化
  • 18、站在Android看设备树
  • 1、概览
  • 2、加载设备树
  • 3、术语
  • 4、分割 DT
  • 5、构建主 DT 和叠加 DT
  • 6、对 DT 进行分区
  • 7、在引导加载程序中运行
  • 8、保持兼容性
  • 9、确保安全
  • 10、小结-白话一刻

看完这个文章,搞清楚下面三点就OK:

  • 1、设备树是什么?
  • 2、设备树有啥用?
  • 3、设备树怎么用?

至于那些来历啊、传闻啊,确实很有意思,但是有很多的博客和书有写,我就不重复那个部分,来强调一下知识。

先整个图片:

0、设备树是什么?

我的理解,就是以前对于很多的冗余重复的东西进行来组件化。

以前每个硬件都有自己的代码进行细节描述,但是将这些代码组件化后,你每个板子需要用什么,给你个dts树架子,你需要啥,就选啥,少啥就加啥。

就别重复整了。

同时讲内核和dts组合,那些细节代码哪儿凉快哪里待着,因为我内核是需要接触硬件的一些东西(应用包含了驱动),所以需要板子的细节,但是现在有了dts,那么那些细节代码byebye。

没dts之前–>把板子的信息放在内核的
有dts之后–>dts将硬件细节传输给内核,内核干净了

在设备树中,可描述的信息包括(原先这些信息大多被硬编码在内核中):

·CPU的数量和类别。

·内存基地址和大小。

·总线和桥。

·外设连接。

·中断控制器和中断使用情况。

·GPIO控制器和GPIO使用情况。

·时钟控制器和时钟使用情况。

这些信息都是一些硬件的信息,我最近要解析的就是内存地址,可惜我现在还无从下手。

它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

这里啰嗦几点:

  • 1、dts是描述设备信息(硬件板子)的文本
  • 2、dts不是直接用的,而是编译后给内核用的
  • 3、dts不是直接和内核联系,而是通过bootloader将编译后的文件传输给kernel

下面来看看什么是DTS?

1、DTS

文件.dts是一种ASCII文本格式的设备树描述,此文本格式非常人性化,适合人类的阅读习惯。但是内核肯定和人不一样,所以就的转换。

基本上,在ARM Linux中,一个.dts文件对应一个ARM的设备,一般放置在内核的arch/arm/boot/dts/目录中。值得注意的是,在arch/powerpc/boot/dts、arch/powerpc/boot/dts、arch/c6x/boot/dts、arch/openrisc/boot/dts等目录中,也存在大量的.dts文件,这证明DTS绝对不是ARM的专利。

1.1 dts简介

由于一个SoC可能对应多个设备(一个SoC可以对应多个产品和电路板),这些.dts文件势必须包含许多共同的部分,Linux内核为了简化,**把SoC公用的部分或者多个设备共同的部分一般提炼为.dtsi,**类似于C语言的头文件。(我要解析的就是.dtsi文件)

其他的设备对应的.dts就包括这个.dtsi。譬如,对于VEXPRESS而言,vexpress-v2m.dtsi就被vexpress-v2p-ca9.dts所引用,vexpress-v2p-ca9.dts有如下一行代码:

/include/ "vexpress-v2m.dtsi"

这个和我写bp那里有点相似的思维,果然include包含世界。

当然,和C语言的头文件类似,.dtsi也可以包括其他的.dtsi,譬如几乎所有的ARM SoC的.dtsi都引用了skeleton.dtsi。

文件.dts(或者其包括的.dtsi)的基本元素即为前文所述的节点和属性,代码清单18.1给出了一个设备树结构的模版。

 1 / {
 2      node1 {
 3          a-string-property = "A string";
 4          a-string-list-property = "first string", "second string";
 5          a-byte-data-property = [0x01 0x23 0x34 0x56];
 6          child-node1 {
 7              first-child-property;
 8              second-child-property = <1>;
 9              a-string-property = "Hello, world";
10          };
11          child-node2 {
12          };
13      };
14      node2 {
15          an-empty-property;
16          a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
17          child-node1 {
18          };
19      };
20 };

上述.dts文件并没有什么真实的用途,但它基本表征了一个设备树源文件的结构:

1个root节点"/";

root节点下面含一系列子节点,本例中为node1和node2;

节点node1下又含有一系列子节点,本例中为child-node1和child-node2;

各节点都有一系列属性。

这些属性可能为空,如an-empty-property;

可能为字符串,如a-string-property;

可能为字符串数组,如a-string-list-property;

可能为Cells(由u32整数组成),如second-child-property;

可能为二进制数,如a-byte-data-property。

1.2 dts例子

上面知道了基础的知识,下来整个栗瞅瞅

在这里插入图片描述
先来文字描述:

1个双核ARM Cortex-A932位处理器;

ARM本地总线上的内存映射区域分布有两个串口(分别位于0x101F1000和0x101F2000)、GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10170000)、中断控制器(位于0x10140000)和一个外部总线桥;

外部总线桥上又连接了SMC SMC91111以太网(位于0x10100000)、I2C控制器(位于0x10160000)、64MB NOR Flash(位于0x30000000);

外部总线桥上连接的I2C控制器所对应的I2C总线上又连接了Maxim DS1338实时钟(I2C地址为0x58)。

转换成dts文件

 1 / {
 2      compatible = "acme,coyotes-revenge";
 3      #address-cells = <1>;
 4      #size-cells = <1>;
 5      interrupt-parent = <&intc>;
 6
 7      cpus {
 8          #address-cells = <1>;
 9          #size-cells = <0>;
10          cpu@0 {
11              compatible = "arm,cortex-a9";
12              reg = <0>;
13          };
14          cpu@1 {
15              compatible = "arm,cortex-a9";
16              reg = <1>;
17          };
18      };
19
20      serial@101f0000 {
21          compatible = "arm,pl011";
22          reg = <0x101f0000 0x1000 >;
23          interrupts = < 1 0 >;
24      };
25
26      serial@101f2000 {
27          compatible = "arm,pl011";
28          reg = <0x101f2000 0x1000 >;
29          interrupts = < 2 0 >;
30      };
31
32      gpio@101f3000 {
33          compatible = "arm,pl061";
34          reg = <0x101f3000 0x1000
35                 0x101f4000 0x0010>;
36          interrupts = < 3 0 >;
37      };
38
39      intc: interrupt-controller@10140000 {
40          compatible = "arm,pl190";
41          reg = <0x10140000 0x1000 >;
42          interrupt-controller;
43          #interrupt-cells = <2>;
44      };
45
46      spi@10115000 {
47          compatible = "arm,pl022";
48          reg = <0x10115000 0x1000 >;
49          interrupts = < 4 0 >;
50      };
51
52      external-bus {
53          #address-cells = <2>
54          #size-cells = <1>;
55          ranges = <0 0  0x10100000  0x10000     // Chipselect 1, Ethernet
56                    1 0  0x10160000  0x10000        // Chipselect 2, i2c controller
57                    2 0  0x30000000  0x1000000>; // Chipselect 3, NOR Flash
58
59          ethernet@0,0 {
60              compatible = "smc,smc91c111";
61              reg = <0 0 0x1000>;
62              interrupts = < 5 2 >;
63          };
64
65          i2c@1,0 {
66              compatible = "acme,a1234-i2c-bus";
67              #address-cells = <1>;
68              #size-cells = <0>;
69              reg = <1 0 0x1000>;
70              interrupts = < 6 2 >;
71              rtc@58 {
72                  compatible = "maxim,ds1338";
73                  reg = <58>;
74                  interrupts = < 7 3 >;
75              };
76          };
77
78          flash@2,0 {
79              compatible = "samsung,k8f1315ebm", "cfi-flash";
80              reg = <2 0 0x4000000>;
81          };
82      };
83 };

在上述.dts文件中,可以看出external-bus是根节点的子节点,而I2C又是external-bus的子节点,RTC又进一步是I2C的子节点。每一级节点都有一些属性信息。

下面再来看看什么是DTC

2、DTC(Device Tree Compiler)

DTC是将.dts编译为.dtb的工具。

DTC的源代码位于内核的scripts/dtc目录中,在Linux内核使能了设备树的情况下,编译内核的时候主机工具DTC会被编译出来,对应于scripts/dtc/Makefile中“hostprogs-y:=dtc”这一hostprogs的编译目标。

当然,DTC也可以在Ubuntu中单独安装,命令如下:

sudo apt-get install device-tree-compiler

在Linux内核的arch/arm/boot/dts/Makefile中,描述了当某种SoC被选中后,哪些.dtb文件会被编译出来,如与VEXPRESS对应的.dtb包括:(哪儿都跑不掉的makefile)

dtb-$(CONfiG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
vexpress-v2p-ca15_a7.dtb \
xenvm-4.2.dtb

在Linux下,我们可以单独编译设备树文件。当我们在Linux内核下运行make dtbs时,若我们之前选择了ARCH_VEXPRESS,上述.dtb都会由对应的.dts编译出来,因为arch/arm/Makefile中含有一个.dtbs编译目标项目。

DTC除了可以编译.dts文件以外,其实也可以“反汇编”.dtb文件为.dts文件,其指令格式为:

./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb

3、DTB(Device Tree Blob)

文件.dtb是.dts被DTC编译后的二进制格式的设备树描述,可由Linux内核解析,当然U-Boot这样的bootloader也是可以识别.dtb的。(至于谁用,得看启动流程,或者都用)

通常在我们为电路板制作NAND、SD启动映像时,会为.dtb文件单独留下一个很小的区域以存放之,之后bootloader在引导内核的过程中,会先读取该.dtb到内存。

Linux内核也支持一种变通的模式,可以不把.dtb文件单独存放,而是直接和zImage绑定在一起做成一个映像文件,类似cat zImage xxx.dtb>zImage_with_dtb的效果。当然内核编译时候要使能CONFIG_ARM_APPENDED_DTB这个选项,以支持“Use appended device tree blob to zImage”(见Linux内核中的菜单)。(单独整个镜像)

4、绑定(Binding)

对于设备树中的节点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt。

在这个.txt文件中,需要描述对应节点的兼容性必需的属性可选的属性

这些文档位于内核的Documentation/devicetree/bindings目录下,其下又分为很多子目录。譬如,Documentation/devicetree/bindings/i2c/i2c-xiic.txt描述了Xilinx的I2C控制器,其内容如下:

Xilinx IIC controller:
Required properties:
- compatible : Must be "xlnx,xps-iic-2.00.a"
- reg : IIC register location and length
- interrupts : IIC controller unterrupt
- #address-cells = <1>
- #size-cells = <0>
Optional properties:
- Child nodes conforming to i2c bus binding
Example:
        axi_iic_0: i2c@40800000 {
compatible = "xlnx,xps-iic-2.00.a";
interrupts = < 1 2 >;
reg = < 0x40800000 0x10000 >;
                #size-cells = <0>;
                #address-cells = <1>;
        };

(我想解析,第一步这里知道了寄存器的含义,下一步找到我要解析的寄存器,学习怎么去解析读取就好诶)

基本可以看出,设备树绑定文档的主要内容包括:

  • ·关于该模块最基本的描述。
  • ·必需属性(Required Properties)的描述。
  • ·可选属性(Optional Properties)的描述。
  • ·一个实例。

Linux内核下的scripts/checkpatch.pl会运行一个检查,如果有人在设备树中新添加了compatible字符串,而没有添加相应的文档进行解释,checkpatch程序会报出警告:UNDOCUMENTED_DT_STRINGDT compatible string xxx appears un-documented,因此程序员要养成及时写DT Binding文档的习惯。

5、Bootloader

Uboot设备从v1.1.3开始支持设备树,其对ARM的支持则是和ARM内核支持设备树同期完成。为了使能设备树,需要在编译Uboot的时候在config文件中加入:

#define CONfiG_OF_LIBFDT

在Uboot中,可以从NAND、SD或者TFTP等任意介质中将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot中运行fdt addr命令设置.dtb的地址,如:

UBoot> fdt addr 0x71000000

fdt的其他命令就变得可以使用,如fdt resize、fdt print等。

对于ARM来讲,可以通过bootz kernel_addr initrd_address dtb_address的命令来启动内核,即dtb_address作为bootz或者bootm的最后一次参数,第一个参数为内核映像的地址,第二个参数为initrd的地址,若不存在initrd,可以用“-”符号代替。

(Linux初始RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个 initrd RAM磁盘卸载,并释放内存。在很多嵌入式Linux系统中,initrd 就是最终的根文件系统。)

(可惜关于bootloader这里没有整个具体例子讲讲,保持这种感觉欠那么一点的感觉继续学习。)

前面整了很多虚头巴老的东西,这个部分来看看实例,一个dts到底有什么东西。

这里先讲最近我遇到的三个属性,内容是来自某位前辈的blog,但是我搞忘链接了,抱歉。

设备树是由节点组成的,节点是由一堆属性组成的,节点都是具体的设备, 不同的设备需要的属性不同,用户可以自定义属性。

除了用户自定义属性,有很多属性都是标准属性。

6. compatible属性

ksms驱动中of_find_compatible_node,就是查找节点的compatible属性。

compatible属性也叫做“兼容性”属性,是一个字符串列表,compatible属性用于将设备和驱动绑定起来。

字符串列表用于选择设备所要使用的驱动程序,格式如下所示

“manufacturer,model”

其中manufacturrer表示厂商,model一般是模块对应的驱动名字。比如像下面这个sound节点

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

. . . . . .

}

属性分别是“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl” 表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字,这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。

设备树可描述的信息

·CPU的数量和类别。

·内存基地址和大小。

·总线和桥。

·外设连接。

·中断控制器和中断使用情况。

·GPIO控制器和GPIO使用情况。

·时钟控制器和时钟使用情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TrustZone_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值