700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Device Tree -----设备树

Device Tree -----设备树

时间:2022-11-29 05:51:48

相关推荐

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只有一个rootnode。dtc会进行合并操作这一点从属性上也可以得到验证。这个稍后做讲解。

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

/{#address-cells=<1>;#size-cells=<1>;chosen{};aliases{};memory{device_type="memory";reg=<00>;};};

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

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

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

chosennode

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

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

aliasesnode

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

aliasesnode用来定义别名,类似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>;};};

memorynode

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

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

其他节点

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

1. Reg属性

在devicenode中,reg是描述memory-mappedIOregister的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属性为stringlist,用来将设备匹配对应的driver驱动,优先级为从左向右。本例中smcc的驱动会寻找“arm,pl353-smc-r2p1”驱动。即compatible实现了原先内核版本3.x之前,platform_device中.name的功能,.

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

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文件。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。