图解Kernel Device Tree(设备树)的使用
Posted 内核笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解Kernel Device Tree(设备树)的使用相关的知识,希望对你有一定的参考价值。
活动地址:CSDN21天学习挑战赛
文章目录
平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3399 | Linux4.4 | android7.1 |
本质上,Device Tree
改变了原来用hardcode
方式将HW
配置信息嵌入到内核代码的方法,改用bootloader
传递一个DB
的形式。对于基于ARM CPU
的嵌入式系统,我们习惯于针对每一个platform
进行内核的编译。但是随着ARM
在消费类电子上的广泛应用(甚至桌面系统、服务器系统),我们期望ARM
能够象X86
那样用一个kernel image
来支持多个platform
。在这种情况下,如果我们认为kernel
是一个black box
,那么其输入参数应该包括:
- 识别
platform
的信息 runtime
的配置参数- 设备的拓扑结构以及特性
对于嵌入式系统,在系统启动阶段,bootloader
会加载内核并将控制权转交给内核,此外,还需要把上述的三个参数信息传递给kernel
如上图,以便kernel
可以有较大的灵活性。在linux kernel
中,Device Tree
的设计目标就是如此。
一、device Tree包含的硬件信息有哪些?(海纳百川?)
Device Tree
是否可以描述所有的硬件信息?
答案是不行的,因为基本上,那些可以动态探测到的设备是不需要描述的,例如USB device
。不过对于SOC
上的usb host controller
,它是无法动态识别的,需要在device tree
中描述。同样的道理,在computer system
中,PCI device
可以被动态探测到,不需要在device tree
中描述,但是PCI bridge
如果不能被探测,那么就需要描述之。 需要描述的内容一般包括:
CPUs
Memory
Buses
Peripheral connections
Interrupt Controllers
GPIO controllers
Clock controllers
二、Device Tree示例 (线头)
为了了解Device Tree
的结构,我们首先给出一个Device Tree
的示例:
最重要的属性 compatible
, reg
, clocks
, interrupts
, and status
。
下面我们一起对其解析:
三、Device Tree语法解析(药引子)
其实DeviceTree
的结构非常简单,由两种元素组成:Node
(节点)、Property
(属性)。
从上图中可以看出,device tree
的基本单元是node
。系统中的每个设备用一个node
来描述,这些node
被组织成树状结构,除了root node
,每个node
都只有一个parent
。一个device tree
文件中只能有一个root node
。每个node
中包含了若干的property/value
来描述该node
的一些特性。每个node
用节点名字(node name
)标识,节点名字的格式是:
node-name
说明了何种设备,必须使用字符开头unit-address
访问此设备的主地址,必须唯一,必须和此节点的reg
属性的开始地址一致
[label:] node-name[@unit-address]
[properties definitions]
[child nodes]
“[]”
表示option
,因此可以定义一个只有node name
的空节点。labe
l方便在dts
文件中引用。child node
的格式和node
是完全一样的,因此,一个dts
文件中就是若干嵌套组成的node
,property
以及child note
、child note property
描述。
注意:
如果该node
没有reg
属性,那么该节点名字中必须不能包括@
和unit-address
,例如上图spi0
总线下的device
child node
。unit-address
的具体格式是和设备挂在那个bus
上相关。例如对于cpu
,其unit-address
就是从0
开始编址,以此加一。而具体的设备,例如以太网控制器,其unit-address
就是寄存器地址。root node
的node name
是确定的,必须是“/”
。
四、特殊节点的介绍
4.1、根节点root node
只能有一个root node
,它用来描述从CPU
端看到的地址空间,至少需要用cpu
和memory
节点组成,如下:
/
compatible = "rockchip,rk3399";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
cpus
......
4.2、别名节点
aliases
节点定义了一些别名。为何要定义这个node
呢?因为Device tree
是树状结构,当要引用一个node
的时候要指明相对于root node
的full path
,例如/node1/child-node1
。如果多次引用,每次都要写这么复杂的字符串多少是有些麻烦,因此可以在aliases
节点定义一些设备节点full path
的缩写。
4.3、CPU节点
对于根节点,必须有一个cpus
的child node
来描述系统中的CPU
信息。
cpus
#address-cells = <2>;
#size-cells = <0>;
cpu_l0: cpu@0
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0x0 0x0>;
enable-method = "psci";
#cooling-cells = <2>; /* min followed by max */
clocks = <&cru ARMCLKL>;
dynamic-power-coefficient = <100>;
;
4.4、Memory节点
memory device node
是所有设备树文件的必备节点,它定义了系统物理内存的layout
。device_type
属性定义了该node
的设备类型,例如cpu
、serial
等。对于memory node
,其device_type
必须等于memory
。reg
属性定义了访问该device node
的地址信息,该属性的值被解析成任意长度的(address
,size
)数组,具体用多长的数据来表示address
和size
是在其parent node
中定义(#address-cells
和#size-cells
)。对于device node
,reg
描述了memory-mapped IO register
的offset
和length
。对于memory node
,定义了该memory
的起始地址和长度。
4.5、可选节点
chosen node
主要用来描述由系统firmware
指定的runtime parameter
。如果存在chosen
这个node
,其parent node
必须是名字是“/”
的根节点。原来通过tag list
传递的一些linux kernel
的运行时参数可以通过Device Tree
传递。例如command line
可以通过bootargs
这个property
这个属性传递;initrd
的开始地址也可以通过linux,initrd-start
这个property
这个属性传递。在实际中,建议增加一个bootargs
的属性,例如:
chosen bootargs = "console=ttymxc0,115200"; ;
我们知道,device tree
用于HW platform
识别,runtime parameter
传递以及硬件设备描述。chosen
节点并没有描述任何硬件设备节点的信息,它只是传递了runtime parameter
。
五、属性
5.1、Compatible属性和model属性
“compatible”
属性通常用来device
和driver
的适配,推荐的格式为”manufacturer,model”
demo:
compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
fsl表示厂商,“imx6ul-evk-wm8960
” 和 “imx-audio-wm8960
“表示驱动模块名字。 该设备在linux
内核先查找第一个兼容值"imx6ul-evk-wm8960
”,找不到再找第二兼容值"imx-audio-wm8960
”
一般驱动程序文件.c
都会有一个 OF
匹配表,此 OF
匹配表保存着一些 compatible
值,如果设备节点的 compatible
属性值和 OF
匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动,
demo
static const struct of_device_id imx_wm8960_dt_ids[] =
.compatible = "fsl,imx-audio-wm8960", ,
.compatible = "fsl,imx6ul-evk-wm8960", ,
;
如果在设备树中有哪个节点的 compatible 属性值与此相等,那么这个节点就会使用此驱动.c文件
“model”
属性只是简单的表示型号,root
节点用其来传递值给machine_desc_str
属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的。
model = "wm8960-audio";
5.2、属性标签
5.3、寻址属性
如果一个device node
中包含了有寻址需求(要定义reg property
)的sub node
(后文也许会用child node
,和sub node
是一样的意思),那么就必须要定义这两个属性。
#address-cells
defines the number of cells used to encode the address field in a child node’s reg property#size-cells
property defines the number of cells used to encode the size field in a child node’s reg property.
其中“#”
是number
的意思,#address-cells
这个属性是用来描述sub node
中的reg
属性的地址域特性的,也就是说需要用多少个u32
的cell
来描述该地址域。
reg = <address1 length1 [address2 length2] [address3 length3] ... >
在 spi
节点中,#address-cells
设置为 1
,#size-cells
设置为 0
。
5.4、中断
属性 | 含义 |
---|---|
interrupt-controller | a property of the interrupt controller node. It states how many cells are in an interrupt specifier for this interrupt controller (Similar to #address-cells and #size-cells). |
#interrupt-cells | a property of the interrupt controller node. It states how many cells are in an interrupt specifier for this interrupt controller (Similar to #address-cells and #size-cells). |
interrupt-parent | A property of a device node containing a list of interrupt specifiers, one for each interrupt output signal on the device. |
interrupts | A property of a device node containing a list of interrupt specifiers, one for each interrupt output signal on the device. |
具体各个HW block 的interrupt source 是如何物理的连接到interruptcontroller 的呢?在dts 文件中是用interrupt-parent 这个属性来标识的。且慢,这里定义interrupt-parent 属性的是root node ,难道root node 会产生中断到interrupt controller 吗?当然不会,只不过如果一个能够产生中断的device node 没有定义interrupt-parent 的话,其interrupt-parent 属性就是跟随parent node 。因此,与其在所有的下游设备中定义interrupt-parent ,不如统一在root node 中定义了。 | |
intc 是一个lable ,标识了一个device node (在本例中是标识了intc : interrupt-controller@00a01000 这个device node )。实际上,interrupt-parent 属性值应该是是一个u32 的整数值(这个整数值在Device Tree 的范围内唯一识别了一个device node ,也就是phandle ),不过,在dts 文件中中,可以使用类似c 语言的Labels and References 机制。定义一个lable ,唯一标识一个node 或者propert y,后续可以使用& 来引用这个lable 。DTC 会将lable 转换成u32 的整数值放入到DTB 中,用户层面就不再关心具体转换的整数值了。 | |
关于interrupt ,我们值得进一步描述。在Device Tree 中,有一个概念叫做interrupt tree ,也就是说interrupt 也是一个树状结构。我们以下图为例(该图来自Power_ePAPR_APPROVED_v1.1 ): | |
5.5、节点状态
可以使用okay
和disabled
来打开和注掉某一个驱动的设备注册。
设备状态有关的, status 属性值也是 字符串 ,字符串是设备的状态信息
值 | 描述 |
---|---|
“okay” | 设备是可操作 |
“disable” | 设备当前是不可操作 |
“fail” | 备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作 |
“fail-sss” | 含义和“fail”相同 |
以上是关于图解Kernel Device Tree(设备树)的使用的主要内容,如果未能解决你的问题,请参考以下文章
Linux DTS (Device Tree Source)设备树源码