Linux驱动开发设备树

Posted XXX_UUU_XXX

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发设备树相关的知识,希望对你有一定的参考价值。

可参考文件:Power_ePAPR_APPROVED_v1.12Devicetree 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属性值是一个字符串,用于cpumemory节点,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——Linux驱动之设备树基本概述及语法总结(为什么会出现设备树,设备树解决了什么问题?)

i.MX6ULL驱动开发 | 04-Linux设备树基本语法与实例解析

Linux——Linux驱动之设备树编译环境搭建实践总结(设备树uboot内核编译及镜像烧写)

Linux——Linux驱动之设备树编译环境搭建实践总结(设备树uboot内核编译及镜像烧写)