1. 设备树概念
设备树源码文件(DTS)的作用就是按照图中的结构描述板级设备信息。老版本的linux种没有设备树,是按照arch/arm/mach-xxx和arch/arm/plat-xxx文件夹描述设备信息,结果就是导致内核文件很多。DTS编译之后得到的二进制文件就是DTB文件,编译工具为DTC。
2. DTS语法
2.1 .dtsi头文件及设备节点
dtsi头文件一般用于描述SOC的内部外设信息比如CPU架构,主频等。设备树上的每个设备都是一个设备节点,通过一些属性(Key-Value)描述节点信息。
设备节点命名格式:
①:
node-name@unit-address
——“节点名@设备地址或寄存器首地址,没有就是0”。
②:
label: node-name@unit-address
—–引入label可以通过&label访问节点。
2.2 DTS常用数据形式
①字符串:
比如“ compatible = “arm,cortex-a7” ”设置compatible属性值为”arm,cortex-a7″。
②32位无符号整数:
比如“reg = <0>”设置reg属性的值为0.
③字符串列表:
字符串值之间采用逗号隔开。
2.3 标准属性
①compatible:兼容性
值是字符串列表
,遵循“厂商,模块驱动”的格式,用于将设备和驱动绑定起来。比如:
compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
当WM8960设备寻找驱动时,首先使用第一个兼容值去找,没找到就下一个。在驱动文件种有
OF匹配表
,其中保存了一些兼容值,如果和设备节点的兼容值匹配上就表示设备可以使用该驱动。
<imx-wm8960.c的OF匹配表>
static const struct of_device_id imx_wm8960_dt_ids[] = {
{ .compatible = "fsl,imx-audio-wm8960", },
{ /* sentinel */ }
};
②model:设备信息
值是字符串
,用于描述设备模块信息比如名字之类。
③status:设备状态
值是字符串
,可选状态有:okay(设备可操作),disabled(设备暂时不可操作),fail(设备不可操作),fail-sss(设备不可操作,sss是错误信息)。
④#address-cells 和#size-cells:
值是u32t
,用于描述子节点地址信息。前者决定子节点reg属性中
起始地址
信息占用字长,后者决定子节点reg属性中
地址长度
信息所占字长。
⑤reg:
reg属性一般和地址有关,包括起始地址和地址长度
。值为“address-length”对。
⑥ranges:地址映射表
可以为空,或者是(
子地址,父地址,地址空间长度
)格式的数字矩阵。在I.MX6ULL中子地址空间和父地址空间完全相同,所以经常会有空的ranges属性。
child-bus-address:
<子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长>
parent-bus-address:
<父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长>
length:
<子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长>
2.4 根节点的兼容性属性
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
根节点的兼容性属性一般第一个值是描述设备名,第二个值是描述设备使用的SOC。
Q1:Linux内核使用设备树,如何判断是否支持某一设备:
内核定义了一个DT_MACHINE_START宏,用于定义machine_desc结构体描述设备。这个结构体中有一个.dt_compat成员变量,经过和根节点compatible属性值对比,有就代表兼容。
Q2:machine_desc结构体匹配根节点compatible属性的流程:
start_kernel <---1.启动内核>
setup_arch <---2.匹配machine_desc结构体>
<获取匹配的machine_desc>
setup_machine_fdt: of_flat_dt_match_machine
<获取设备树根节点>
of_get_flat_dt_root
2.5 向节点追加数据
要求:向i2c1节点追加一个fxls8471子节点,不能影响到其它使用I.MX6ULL的板子。
方法:在imx6ull-alientek-emmc.dts中找到&i2c1这个label,通过该label访问.dtsi中对应的节点。
3. 创建小型模板设备树
需要在设备树描述以下内容:
①I.MX6ULL 这个 Cortex-A7 架构的 32 位 CPU。
②I.MX6ULL 内部 ocram,起始地址 0x00900000,大小为 128KB(0x20000)。
③I.MX6ULL 内部 aips1 域下的 ecspi1 外设控制器,寄存器起始地址为 0x02008000,大小为 0x4000。
④I.MX6ULL 内部 aips2 域下的 usbotg1 外设控制器,寄存器起始地址为 0x02184000,大小为 0x4000。
⑤I.MX6ULL 内部 aips3 域下的 rngb 外设控制器,寄存器起始地址为 0x02284000,大小为 0x4000。
步骤:
1)创建myfirst.dts文件,设置根节点compatible属性。
2)添加CPUs节点。
3)添加SOC节点,作为父节点管理众多SOC外设对应的子节点。
4)添加ocram节点。内部RAM,属于SOC的子节点。起始地址0x00900000,大小128KB-0x20000。
5)添加aips123这三个子节点。6ULL的内部分为三个域aips1-3,分管不同的外设控制器。SOC子节点。
6)添加ecspi1,usbotg1,rngb三个外设控制节点。
4. 设备树在系统中的体现及特殊节点
内核启动后,在rootfs的/proc/device-tree目录下为根节点的所有属性和子节点。根节点属性也以文件类型表示,打开后就是属性值。
特殊节点:
1)aliases子节点:
用于定义别名,方便访问节点。
aliases {
can0 = &flexcan1;
can1 = &flexcan2;
...
};
2)chosen子节点:
为了uboot向linux内核传递数据,重点是bootargs参数,该属性不会在代码中出现,但是可以通过ls命令查看到。这个参数是在内核启动时,do_bootm_linux函数指挥是boot通过fdt_chosen这个函数传递进来的。
5. 内核解析DTB文件的流程
当我们需要添加节点时,可以通过内核源码中的/Documentation/devicetree/bindings下的.txt绑定文档查看帮助。
6. 设备树常用OF操作函数
用于获取设备树中的节点或者属性信息的函数,统一前缀“of_”,称为OF函数,定义在include/linux/of.h中。
6.1 查找节点的OF函数
内核使用device_node结构体描述一个节点。相关OF函数如下:
1)of_find_node_by_name:通过节点名查找。
2)of_find_node_by_type:通过device_type属性查找。
3)of_find_compatible_node:通过device_type和compatible属性查找。
4)of_find_matching_node_and_match:通过of_device_id匹配表查找。
5)of_find_node_by_path:通过带节点名的全路径(可以使用别名)查找。
6.2 查找父/子节点的OF函数
1)of_get_parent:获取父节点。
2)of_get_next_child :获取子节点。
6.3 提取属性值的OF函数
内核使用
property结构体
表示属性。
1)of_find_property:查找指定属性。
2)of_property_count_elems_of_size:获取属性中元素的数量。
3)of_property_read_u32_index:从属性中获取指定标号的 u32 类型数据值。
4)of_property_read_u8/16/62/64_array:读取属性中 u8、u16、u32 和 u64 类型的数组数据。
5)of_property_read_u8/16/62/64:读取属性中 u8、u16、u32 和 u64 类型的整形数据。
6)of_property_read_string:读取属性中字符串值。
7)of_n_addr_cells:读取#address-cells值。
8)of_n_size_cells:读取#size-cells值。
6.4 其它常用OF函数
1)of_device_is_compatible:查看节点compatible属性是否包含指定的字符串。用于检查兼容性。
2)of_get_address:获取地址相关属性。
3)of_translate_address:将从设备树独到的地址转化为物理地址。
4)of_address_to_resource:将reg属性值转换为resource结构体类型。该结构体用于描述设备资源信息。
5)of_iomap:直接内存映射,作用等同于ioremap函数。