Linux驱动开发设备树
Posted XXX_UUU_XXX
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发设备树相关的知识,希望对你有一定的参考价值。
可参考文件:Power_ePAPR_APPROVED_v1.12和Devicetree SpecificationV0.2
设备树(Device Tree)将板级硬件信息的内容从Linux中分离出来
- .dts文件描述板级信息,包括开发板上有哪些I2C设备、SPI设备等。
- .dtsi文件描述SOC级设备,包括CPU数量、主频、外设控制器信息等。类似C语言中的头文件。
- .dtb文件是将.dts文件编译后得到的二进制文件。
将.dtb文件写入内核中arch/arm/boot/dts/Makefile文件中对应的芯片下
使用DTC工具将.dts文件编译为.dtb文件
- 在内核目录下执行make dtbs命令编译所有的dts文件
- 在内核目录下执行make xxx.dtb命令编译指定的dts文件
设备树支持引用文件的扩展名可以为.dts、.dtsi和.h。支持/* ... */和//注释表示
设备节点
以斜杠 / 作为根节点开始,根节点外的如&cpu0、&i2c1等语句表示追加含义,追加内容会覆盖原来的内容,.dtsi文件和.dts文件的/根节点会合并为一个根节点。
子节点命名(小写字母、数字、逗号、句号、下划线、加号、减号),更多相关命名规则可以查看文件Power_ePAPR_APPROVED_v1.12
- node-name@unit-address
- 节点名@单元地址,单元地址一般为外设寄存器的起始地址和设备的地址,单元地址可以不要,用0表示,如cpu@0.
- label: node-name@unit-address
- 节点标签: 节点名@单元地址,节点标签方便访问节点。
系统启动以后可以在根文件系统里面查看设备树的节点信息,/proc/device-tree路径下为一级节点,节点以文件夹形式表示,具体属性以文件形式表示。
特殊节点
aliases(别名)
定义别名,方便访问节点,例如
- can0 = &flexcan1;
- spi0 = &ecspi1;
chosen(选择)
Linux启动前,在设备树chosen节点下添加子节点bootargs,子节点bootargs存放bootargs环境变量。
属性
compatible
compatible兼容性属性用于将设备和驱动绑定起来,属性值是一个字符串列表。
- compatible = "manufacturer1,model1", "manufacturer2,model2";
- manufacturer:表示厂商(可以不写厂商),model:表示模块对应驱动的名字。设备先使用第一个兼容值在Linux内核中查找与之匹配的驱动文件,如果没有则使用后面第二个兼容值查找。
model
model属性值是一个字符串。描述设备模块信息如设备名字,驱动中很少使用。
- model = "model_name";
status
status属性值是一个字符串。
- status = "okay"; 表示设备是可操作的。
- status = "disabled"; 表示设备当前是不可操作的,未来可以修改为可操作。
- status = "fail"; 表示设备是不可操作的。
- status = "fail-sss"; 表示设备是不可操作的,sss表示检测到的错误内容。
#address-cells 和 #size-cells
#address-cells 和 #size-cells 属性值都是无符号32位整型,可以在任何子节点设备中描述子节点的地址信息。#address-cells 和 #size-cells表明子节点应该如何编写reg属性值。
reg
reg属性用于表示外设寄存器地址范围信息。
- reg = <address1 length1 address2 length2 address3 length3>
- address:表示起始地址,length:表示地址长度。#address-cells表示address起始地址数据所占用的字长,#size-cells表示length地址长度数据所占用的字长。
ranges
range属性值可以为空,为空表示子地址空间和父地址空间完全相同,不需要进行地址转换。
- ranges = <child-bus-address parent-bus-address length>;
- child-bus-address:表示子总线地址空间的物理地址,由父节点的#address-cells确定物理地址所占用的字长,parent-bus-address:表示父总线地址空间的物理地址,由父节点的#address-cells确定物理地址所占用的字长,length:表示子地址空间的长度,由父节点的#size-cells确定地址长度所占用的字长。
name
name属性值是一个字符串,用来记录节点名字,已经被弃用,老版设备树可能会使用。
device_type
device_type属性值是一个字符串,用于cpu和memory节点,IEEE 1275使用device_type属性描述设备的FCode,但是设备树没有FCode,被弃用。
- device_type = "cpu";
绑定信息文档
Linux内核源码.txt文件为绑定文档,仅作为参考,描述如何在设备树中添加节点。路径为:Linux源码目录/Documentation/devicetree/bindings/,如果路径下没有对应文档,查看芯片供应商资料。
OF操作函数
Linux内核提供一系列函数用于获取设备树中节点或属性信息,这些哈数前缀都为“of_”,故称为OF函数。
查找节点的OF函数
of_find_node_by_name
通过节点名字查找指定的节点,速度慢。
- struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
- from:开始查找的节点,为NULL表示从根节点开始查找;name:要查找的借点名字;
- 返回值:查找到的节点,返回NULL表示查找失败
of_find_node_by_type
通过查找device_type属性查找指定的节点,不建议使用。
- struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
- from:开始查找的节点,为NULL表示从根节点开始查找;type:要查找的节点对应的device_type属性值;
- 返回值:查找到的节点,返回NULL表示查找失败
of_find_compatible_node
通过device_type和compatible两个属性查找指定的节点。
- struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible);
- from:开始查找的节点,为NULL表示从根节点开始查找;type:要查找的节点对应的device_type属性值;compatible:要查找的节点对应的compatible属性值;
- 返回值:查找到的节点,返回NULL表示查找失败
of_find_matching_node_and_match
通过 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);
- from:开始查找的节点,为NULL表示从根节点开始查找;matches:of_device_id匹配表里查找节点;match:找到的匹配的of_device_id;
- 返回值:查找到的节点,返回NULL表示查找失败
of_find_node_by_path
通过路径来查找指定的节点,速度快,路径通常已知。常用。
- inline struct device_node *of_find_node_by_path(const char *path)
- path:全路径的节点名,可以使用节点的别名;
- 返回值:查找到的节点,返回NULL表示查找失败
查找父子节点的OF函数
of_get_parent
用于获取指定节点的父节点。
- struct device_node *of_get_parent(const struct device_node *node);
- node:要查找父节点的节点
- 返回值:找到的父节点
of_get_next_child
用迭代的方式查找子节点。
- struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev);
- node:父节点;prev:从哪一个子节点开始迭代的查找下一个子节点。为NULL表示从第一个子节点开始;
- 返回值:找到的下一个子节点
提取属性值的OF函数
of_find_property
用于查找指定的属性。常用。
- property *of_find_property(const struct device_node *np, const char *name, int *lenp);
- np:设备节点;name: 属性名字;lenp:属性值的字节数;
- 返回值:找到的属性
of_property_count_elems_of_size
用于获取属性中元素的数量。获取属性值数组的大小,常用。
- int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size);
- np:设备节点;proname: 需要统计元素数量的属性名字;elem_size:元素长度;
- 返回值:得到的属性元素数量
of_property_read_u32_index
用于从属性中获取指定标号的 u32 类型数据值,属性中有多个u32类型值,获取指定标号的数据。
- int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value);
- np:设备节点;proname: 要读取的属性名字;index:要读取的值标号;out_value:读取到的值;
- 返回值:0 读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小
of_property_read_u8_array
of_property_read_u16_array
of_property_read_u32_array
of_property_read_u64_array
4 个函数分别是读取属性中 u8、u16、u32 和 u64 类型的数组数据,其中常用of_property_read_u32_array
- int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz);
- np:设备节点;proname: 要读取的属性名字;out_value:读取到的数组值;sz:要读取的数组元素数量;
- 返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
of_property_read_u8
of_property_read_u16
of_property_read_u32
of_property_read_u64
4个函数分别用于读取只有一个整形值的属性,其中常用of_property_read_u32
- int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);
- np:设备节点;proname: 要读取的属性名字;out_value:读取到的数组值;
- 返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
of_property_read_string
用于读取属性中字符串值。常用。
- int of_property_read_string(struct device_node *np, const char *propname, const char **out_string);
- np:设备节点;proname: 要读取的属性名字;out_string:读取到的字符串值
- 返回值:0,读取成功,负值,读取失败。
of_n_addr_cells
用于获取#address-cells 属性值。
- int of_n_addr_cells(struct device_node *np);
- np:设备节点;
- 返回值:获取到的#address-cells 属性值
of_size_cells
用于获取#size-cells 属性值。
- int of_n_size_cells(struct device_node *np) ;
- np:设备节点;
- 返回值:获取到的#size-cells 属性值
以上是关于Linux驱动开发设备树的主要内容,如果未能解决你的问题,请参考以下文章
Linux——Linux驱动之设备树基本概述及语法总结(为什么会出现设备树,设备树解决了什么问题?)
Linux——Linux驱动之设备树基本概述及语法总结(为什么会出现设备树,设备树解决了什么问题?)
i.MX6ULL驱动开发 | 04-Linux设备树基本语法与实例解析