设备树是Linux驱动开发的必备技能。
1.什么是设备树
设备树(Device Tree),描述设备树的文件叫做DTS(Device Tree Source),这个DTS文件采用树形结构描述板级信息如下图:
设备树源文件DTS,将.dts文件编译后会得到二进制文件DTB,需要用到DTC工具。进入源码里,使用
make all
会编译Linux中所有的东西,包括zImage、.ko、.dts等。也可以只使用
make dtbs
只编译dts文件,会更快些。
2.设备树语法属性
下面以一个小型模板设备树为例子,介绍其语法和各个属性。
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0{
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
};
};
soc {
compatible = "simple-bus";
#addre-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0xe0000000 0x00100000>;
};
aips1:aips-bus@02000000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x2000000 0x100000>;
ranges;
ecspil:ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-ecspi","fsl,imx51-ecspi";
reg = <0x02008000 0x4000>;
status = "disabled";
};
};
}
- /{ } 表示跟节点,在花括号里面添加子节点。
- 子节点:label:node-name@unit-address,引入label为了方便访问节点,可以通过&label来访问,而不用输入整个名字。可以在子节点里面再添加子节点,格式一样。如aips1节点里面的ecspil节点。
- compatible属性:兼容性属性,是一个字符串列表。格式如“manufacturer,model”。上面fsl表示飞思卡尔厂商。
- model属性:也是字符串,描述设备模块信息,如modul = "aops-bus"。
- status属性:和设备状态有关。有四种状态:"okey"表示设备是可操作的;"disabled"设备不可操作,未来可操作;"fail"设备不可操作,未来也不可能操作;"fail-sss"和fail相同,sss表示检测到的错误信息。
- #address-cells和#size-cells属性:都是无符号32位整型,前面决定节点的地址,后面表示长度。这两个表面子节点如何编写reg属性。两个都是<1>,表示后面的reg属性中地址占用1个字节,长度占用1个字节。如果长度<0>表示没有长度这个属性。
- reg属性:一般为(address, length),用来描述设备地址空间资源信息。比如aips1中起始地址为0x2000000,地址长度为0x100000;
- ranges属性:一般为空。或者按照(child-bus-address, parent-address, length)格式编写,每个项目由子地址、父地址、长度三部分组成。
其他属性在后续学习中再添加。
3.常用OF设备树操作函数
3.1查找节点
//通过节点名字查找指定节点
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
//通过device_type属性查找指定节点
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
//根据device_type和compatible两个属性查找
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible);
//通过of_device_id匹配表查找
struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match);
//通过路径查找节点
inline struct device_node *of_find_node_by_path(const char *path);
各个参数说明:
- from:开始查找的节点,为空则从根节点开始查找整个设备树
- name:要查找节点的名字
- type:要查找节点对应的type字符串,也就是device_type属性值
- compatible:所对应的compatible属性列表
- matches:of_device_id匹配表,也就是在匹配吧里查找节点
- match:找到匹配的of_device_id
- path:带有全路径的节点名
- return:找到的节点
3.2查找父/子节点
//获取指定节点的父节点
struct device_node *of_get_parent(const struct device_node *node);
//用迭代的方式查找子节点
struct device_node *of_get_next_child (const struct device_node *node, struct device_node *prev);
- node:要查找的父/子节点的节点
- prev:前一个子节点
- return:找到的父/子节点
3.3提取属性值函数
//查找指定属性
of_find_propetry(const struct device_node *np, const char *name, int *lenp)
- np:设备jied
- name:属性名字
- lenp:属性值的字节数
- return:找到的属性
//获取属性中元素的数量
int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size);
- np:设备节点
- proname:需要统计元素数量的属性名字
- elem_size:元素长度
- return:属性元素数量
//从属性中获取指定标号的u32类型数据值
int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)
- index:要读取的值标号
- out_value:读取的值
- return:0读取成功;负值读取失败;-EINVAL 表示属性不存在; -ENODATA 表示没有
要读取的数据; -EOVERFLOW 表示属性值列表太小。
//读取属性中u8、u16、u32、u64类型的数组数据
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)
int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)
- out_value:读取到的数组值,分别为u8、u16、u32、u64.
- sz:要读取元素的数量
- return:0读取成功;负值读取失败;-EINVAL 表示属性不存在; -ENODATA 表示没有
要读取的数据; -EOVERFLOW 表示属性值列表太小。
//读取只有一个整型值的属性
int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value)
int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value)
int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value)
int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value)
参数和上一个一样
//读取属性中字符串值
int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)
- out_string:读取到的字符串
- return:0读取成功;负值读取失败
//获取#address_cells
int of_n_addr_cells(struct device_node *np)
//获取#size_cells
int of_n_size_cells(struct device_node *np)
- np:设备节点
- return:获取的属性值
3.4其他常用函数
//获取地址相关属性
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)
- dev:设备节点
- index:要读取的地址标号
- size:地址长度
- flags:参数比如 IORESOURCE_IO、 IORESOURCE_MEM 等
- return:读取到的首地址,NULL表示失败
//将设备树读取到的地址转换为物理dihzi
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
- in_addr:要转换的地址
- return:得到的物理地址
//将reg属性值转换为resource结构体类型
int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
- dev:设备节点
- index:地址资源标号
- r:得到的resource类型的资源值
- return:0成功;负值失败
//直接内存映射
void __iomem *of_iomap(struct device_node *np, int index);
- np:设备节点
- index:reg属性中要完成内存映射的段,如果reg属性只有一段则index是0
- return:经过映射的虚拟地址首地址,为NULL则失败