Device Tree -----设备树

目录

一、设备树的起源

二、设备树的基本概念

三、设备树语法

四、怎么使用设备树


一、设备树的起源

linux的设备树概念是在内核3.x版本之后才加入的,但设备树不是linux原创的,而是在windows的操作系统中,很早就已经有设备树的概念了。在linux 3.x之前的内核版本中,大量的平台代码充斥着内核源文件,导致内核的代码越来越大,代码的灵活性越来越差,正如Linus Torvalds在邮件中所说“this whole ARM thing is a f*cking pain in the ass”,市场上每增加一块硬件板卡,就要往/arch/arm/mach-xxx目录下增加一个文件,在文件中描述硬件板卡的信息。如果你看过linux 3.x之前的mach-xxxx文件,你就会明白为什么Linus Torvalds会发火,因为mach-xxxx文件中大部分都是框架型代码,而每个mach-xxxx都要拷贝一遍这些框架代码,这就好比你明明可以用一个for语句就可以完成数组的初始化,而你却偏偏每个数组元素都赋值一次,可想多么stupid。

终于,在Linus Torvalds怒不可言后,linux的开源组织GNU开始考虑如何优化linux源码结构,这就引入了linux设备树的概念,设备树的引入就是为了优化linux源码中对硬件信息描述的复杂度。其实设备树的作用与之前mach-xxxx中的作用是一样的,但代码结构更为简单高效。

二、设备树的基本概念

涉及到设备树,我们不得不提到有三个概念:dts/dtsi,dtc,dtb

dts:     device tree source                                         设备树源文件

dtsi:    device tree source include                            设备树头文件

dtc:     device tree compiler                                       设备树编译器

dtb:    device tree binary                                            设备树二进制文件

为什么引入个设备树会增加四个东西啊?其实从定义也就能大概知道了。dts文件是一种通俗易懂的编码格式,人可以直接看懂,但uboot和kernel不能直接识别dts文件,他们只能识别二进制文件,所以需要把dts文件编译成dtb文件,因此就引入了dtc,dtc将dts编译成dtb文件。由于针对同一个芯片可能有多个硬件板卡,因此需要将芯片级的硬件信息提取出来,作为局部通用的设备树文件,把这部分通用的设备树文件命名为dtsi。   

                    

三、设备树语法

DTS的基本语法范例,如图所示。

/{                                  //根节点
    node1{                          //node1是节点名,是/的子节点
        key=value;                  //node1的属性
        ...
        node3{                      //node3是node1的子节点
            key=value;              //node3的属性
            ...
        }
    }                               //node1的描述到此为止
    node2{
        key=value;
        ...
    }
}

它包括一系列节点,以及描述节点的属性。

“/”为root节点。在一个.dts文件中,有且仅有一个root节点;在root节点下有“node1”,“node2”子节点,称root为“node1”和“node2”的parent节点,除了root节点外,每个节点有且仅有一个parent;其中子节点node1下还存在子节点“node3”。

注:如果看过内核/arch/arm/boot/dts目录的读者看到这可能有一个疑问。在每个.dsti和.dts中都会存在一个“/”根节点,那么如果在一个设备树文件中include一个.dtsi文件,那么岂不是存在多个“/”根节点了么。其实不然,编译器dtc在对.dts进行编译生成dtb时,会对node进行合并操作,最终生成的dtb只有一个root node。dtc会进行合并操作这一点从属性上也可以得到验证。这个稍后做讲解。

在/arch/arm/boot/dts/目录中有一个文件skeleton.dtsi,该文件为各ARM vendor共用的一些硬件定义信息。以下为skeleton.dtsi的全部内容。

/ {

#address-cells = <1>;

#size-cells = <1>;

chosen { };

aliases { };

memory { device_type = "memory"; reg = <0 0>; };

};

属性# address-cells的值为1,它代表以“/”根节点为parent的子节点中,reg属性中存在一个address值;

#size-cells的值为1,它代表以“\” 根节点为parent的子节点中,reg属性中存在一个size值。即父节点的# address-cells和#size-cells决定了子节点的address和size的长度;Reg的组织形式为reg = 

下面列举例子,对一些典型节点进行具体描述。

  • chosen node

	chosen {
		bootargs = "console=ttyPS0,115200 root=/dev/ram rw earlyprintk";
		linux,stdout-path = "/amba/serial@e0001000";
	};

chosen node 主要用来描述由系统指定的runtime parameter,它并没有描述任何硬件设备节点信息。原先通过tag list传递的一些linux kernel运行的参数,可以通过chosen节点来传递。如command line可以通过bootargs这个property来传递。如果存在chosen node,它的parent节点必须为“/”根节点。

  • aliases node

	aliases {
		ethernet0 = &gem0;
		serial0 = &uart1;
		serial1 = &uart0;
		spi0 = &qspi;
	};

aliases node用来定义别名,类似C++中引用。上面是一个在.dtsi中的典型应用,当使用ethernet0时,也即使用gem0,使得引用节点变得简单方便。例:当.dts  包含该.dtsi时,将ethernet0的status属性赋值为okay,则表明该主板上的gem0处于enable状态;反之,status赋值为disabled,则表明该主板上的gem0处于disenable状态。如下是引用的具体例子:

&gem0 {
    compatible = "cdns,gem";
    reg = <0xe000b000 0x4000>;
    status = "okay";
    interrupts = <0 22 4>;
    interrupt-parent = <&intc>;
    clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;
    clock-names = "pclk", "hclk", "tx_clk";
    phy-mode = "rgmii-id";
    phy-handle = <&phy0>;
    phy0: phy@0 {
        compatible = "atheros,ar8035";
        device_type = "ethernet-phy";
        reg = <0>;
    };
};
  • memory node

	memory {
		device_type = "memory";
		reg = <0x0 0x40000000>;
	};

对于memory node,device_type必须为memory,由之前的描述可以知道该memory node是以0x00000000为起始地址,以0x30000000为结束地址的1GB的空间。

  • 其他节点

由于其他设备节点依据属性进行描述,具有类似的形式。接下来的部分主要分析各种属性的含义及作用,并结合相关的例子进行阐述。

1. Reg属性

在device node 中,reg是描述memory-mapped IO register的offset和length。子节点的reg属性address和length长度取决于父节点对应的#address-cells和#size-cells的值。例:

 

在上述的smcc节点中,存在子节点nand0。nand0中的中reg为reg = <0xe1000000 0x1000000>,其0xe1000000为base address,0x1000000为size。

设备节点的名称格式node-name@unit-address,节点名称用node-name唯一标识,为一个ASCII字符串。其中@unit-address为可选项,可以不作描述。unit-address的具体格式和设备挂载在哪个bus上相关。如:cpu的unit-address从0开始编址,以此加1;本例中,smcc为0xe000e000 。

2. compatible属性

compatible属性为string list,用来将设备匹配对应的driver驱动,优先级为从左向右。本例中smcc的驱动会寻找“arm,pl353-smc-r2p1”驱动。即compatible实现了原先内核版本3.x之前,platform_device中.name的功能,.

注:对于“/”root节点,它也存在compatible属性,用来匹配machine type。

3. interrupts属性

 

设备节点通过interrupt-parent来指定它所依附的中断控制器,当节点没有指定interrupt-parent时,则从parent节点中继承。上面例子中,root节点的interrupt-parent = <&intc>。这里使用了引用,即intc引用了interrupt-controller@f8f01000。

在interrupts属性后面带3个参数是什么意思呢?这三个参数其实是跟硬件紧密相关的,我们以zynq的中断控制器来作为例子进行分析:

  • 一般第一个参数是指终端控制类型的,中断控制类型分为:

IPI:inter-processer interrupt    中断号0~15

PPI: per-processer interrupt      中断号16~31

SPI: shared processer interrupt  中断号32~32+224

在arm-gic.h文件中定义只有SPI和PPI

#define GIC_SPI 0
#define GIC_PPI 1

因此,对于我们的例子,第一个参数为0表示用的是SPI。

  • 第二个参数是中断号,但这里的中断号不是实际的物理中断号,而是将物理中断号减去32而得到的。例子中第二个参数是18,那这个18是怎么来的呢?

从图中我们可以看到物理中断号为50,减去32,正好是18,这样就对应起来了。

  • 第三个参数是选择哪种中断触发方式

1表示low-to-high edge triggered

2表示high-to-low edge triggered      (invalid for SPI)

3表示active high level-sensitive

4表示active low level-sensitive         (invalid for SPI)

四、怎么使用设备树

首先,设备树是在linux3.X以后才引入的,因此内核版本必须保证是3.x以后的版本,否则内核根本无法解析设备树;

其次,需要修改u-boot中的配置,配置成支持设备树;

再之,根据硬件板卡信息,修改设备树节点属性;

最后,利用dtc将新编辑的设备树编译成dtb文件。

 

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值