Linux 学习笔记: 设备树—常用OF操作函数

一、概述

Linux内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀 ”of“ , 所以在很多资料里面也被叫做OF函数。这些OF 函数原型定义在include/linux/of.h 文件中。

device_node

Linux 内核使用device_node 结构体来描述一个节点,此结构体定义在文件include/linux/of.h 中,定义如下:

struct device_node {
	const char *name;
	const char *type;
	phandle phandle;
	const char *full_name;
	struct fwnode_handle fwnode;

	struct	property *properties;
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;
	struct	device_node *child;
	struct	device_node *sibling;
	struct	kobject kobj;
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	const char *path_component_name;
	unsigned int unique_id;
	struct of_irq_controller *irq_trans;
#endif
};

二、查找节点的OF函数

1. of_find_node_by_name

该函数通过节点名字查找指定的节点

struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
  • from : 开始查找的节点,如果为NULL 表示从根节点开始查找整个设备树。
  • **name :**要查找的节点名字。
  • **返回值:**找到的节点,如果为NULL 表示查找失败。

2. 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 :**要查找的节点对应的type 字符串,也就是device_type 属性值。
  • **返回值:**找到的节点,如果为NULL 表示查找失败。

3. of_find_compatible_node

该函数根据device_type 和compatible 这两个属性查找指定的节点

struct device_node *of_find_compatible_node(struct device_node *from,
const char *type, const char *compat);
  • from : 开始查找的节点,如果为NULL 表示从根节点开始查找整个设备树。
  • **type :**要查找的节点对应的type 字符串,也就是device_type 属性值。可以为NULL,表示忽略掉device_type 属性。
  • compatible : 要查找的节点所对应的compatible 属性列表。
  • **返回值:**找到的节点,如果为NULL 表示查找失败。

4. 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 表示查找失败。

4. of_find_node_by_path

该函数通过路径来查找指定的节点

struct device_node *of_find_node_opts_by_path(const char *path,
	const char **opts);
  • path : 带有全路径的节点名,可以使用节点的别名,比如:“/backlight” 就是backlight 这个节点的
  • **返回值:**找到的节点,如果为NULL 表示查找失败。

三、查找父/子节点的OF函数

Linux 内核提供了几个查找节点对应的父节点或子节点的OF函数。

1. of_get_parent

该函数用于获取指定节点的父节点(如果有父节点的话)。

struct device_node *of_get_parent(const struct device_node *node);
  • node : 要查找父节点的节点。
  • 返回值: 找到的父节点。

2. of_get_next_child

该函数用迭代的方式查找子节点

struct device_node *of_get_next_child(const struct device_node *node,
					     struct device_node *prev);
  • node : 父节点。
  • **prev:**前一个子节点,也就是从哪个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。
  • **返回值:**找到的下一个子节点

四、提取属性值的OF函数

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内核中使用结构体property 表示属性,此结构体同样定义在文件include/linux/of.h 中,内容如下:

struct property {
	char	*name;
	int	length;
	void	*value;
	struct property *next;
	unsigned long _flags;
	unsigned int unique_id;
	struct bin_attribute attr;
};

1. of_find_property

该函数用于查找指定的属性。

struct property *of_find_property(const struct device_node *np,
					 const char *name,
					 int *lenp);
  • np : 设备节点。
  • name: 属性名字。
  • lenp: 属性值的字节数。
  • **返回值:**找到的属性值。

2. of_property_count_elems_of_size

该函数用于获取属性中元素的数量,比如reg 属性值是一个数组,那么使用此函数可以获取到这个数组的大小。

int of_property_count_elems_of_size(const struct device_node *np,
				const char *propname, int elem_size);
  • np : 设备节点。
  • proname : 需要统计元素数量的属性名字。
  • elem_size : 元素长度。
  • 返回值: 得到的属性元素数量。

3. of_property_read_u32_index

该函数用于从属性中获取指定标号的u32 类型的数据值(无符号32位),比如某个属性有多个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 表示属性值列表太小

4. of_property_read_u8_array , of_property_read_u16_array,of_property_read_u32_array,of_property_read_64_array

这个4个函数分别是读取属性中u8,u16,u32,u64 类型的数组数据,比如大多数的reg 属性都是数组数据,可以使用这4个函数一次读取出reg 属性中的所有数据。

of_property_read_u32_array(const struct device_node *np,
				      const char *propname,
				      u32 *out_values,
				      size_t sz);
  • np : 设备节点
  • proname : 要读取的属性名字
  • out_value : 读取到的数组值,分别是u8,u16,u32,u64
  • sz : 要读取的数组元素数量。
  • 返回值:
    • 0,读取成功
    • 负值,读取失败
    • -EINVAL 表示属性不存在
    • -ENODATA 表示没有要读取的数据
    • -EOVERFLOW 表示属性值列表太小

5. of_property_read_u8, of_property_read_u16,of_property_read_u32,of_property_read_64

有些属性只有一个整形值,这四个函数就是用于读取这种只有一个整形值的属性。

static inline int of_property_read_u8(const struct device_node *np,
				       const char *propname,
				       u8 *out_value)
  • np : 设备节点
  • proname : 要读取的属性名字
  • out_value : 读取到的数值,分别是u8,u16,u32,u64
  • 返回值:
    • 0,读取成功
    • 负值,读取失败
    • -EINVAL 表示属性不存在
    • -ENODATA 表示没有要读取的数据
    • -EOVERFLOW 表示属性值列表太小

6. 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,读取成功;负值,读取失败。

7. of_n_addr_cells

该函数用于获取#address-cell 属性值

int of_n_addr_cells(struct device_node *np);
  • np : 设备节点
  • 返回值: 获取到的#address-cells 属性值

8. of_n_size_cells

该函数用于获取#size-cells 属性值

int of_n_size_cells(struct device_node *np);
  • np : 设备节点
  • 返回值: 获取到的#size-cells 属性值

五、其他常用的OF函数

1. of_device_is_compatible

该函数用于查看节点的compatible 属性是否包含compat 指定的字符串,也就是检查设备节点的兼容性。

int of_device_is_compatible(const struct device_node *device,
				   const char * compat);
  • device : 设备节点
  • compat : 要查看的字符串
  • 返回值: 0 ,不包含;正数,包含。

2. of_get_address

该函数用于获取地址相关属性,主要是 “reg” 或者 “assigned-address” 属性值。

const __be32 *of_get_address(struct device_node *dev, int index,
			   u64 *size, unsigned int *flags);
  • dev : 设备节点
  • index : 要读取的地址标号
  • size : 地址长度
  • flags : 参数,比如IORESOURCE_IO , IORESOURCE_MEM 等
  • **返回值:**读取到的地址数据首地址,为NULL 的话表示读取失败

3. of_translate_address

该函数负责从设备树读取到的地址转换为物理地址

u64 of_translate_address(struct device_node *np, const __be32 *in_addr);
  • dev : 设备节点
  • in_addr : 要转换的地址。
  • **返回值:**得到的物理地址,如果为OF_BAD_ADDR 的话表示转换失败。

4. of_address_to_resource

IIC,SPI , GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间,Linux 内核使用resource 结构体来描述一段内存空间,“resource” 翻译出来就是“资源”,因此 用resource 结构体描述的都是设备资源信息,resource 结构体定义在文件 include/linux/ioport.h 中,定义如下:

struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};
  • 对于32位的SOC 来说,resource_size_t 就是u32 类型的。
  • 其中start 表示开始地址,end 表示 结束地址。
  • name 就是这个资源的名字。
  • flags 是资源标志位,一般表示资源类型

可选的资源标志定义在文件 include/linux/ioport.h 中

#define IORESOURCE_BITS		0x000000ff	/* Bus-specific bits */

#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100	/* PCI/ISA I/O ports */
#define IORESOURCE_MEM		0x00000200
#define IORESOURCE_REG		0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		0x00000400
#define IORESOURCE_DMA		0x00000800
#define IORESOURCE_BUS		0x00001000

#define IORESOURCE_PREFETCH	0x00002000	/* No side effects */
#define IORESOURCE_READONLY	0x00004000
#define IORESOURCE_CACHEABLE	0x00008000
#define IORESOURCE_RANGELENGTH	0x00010000
#define IORESOURCE_SHADOWABLE	0x00020000
......

一般最常见的资源标志就是:

  • IORESOURCE_MEM
  • IORESOURCE_REG
  • IORESOURCE_IRQ

of_address_to_resource 函数看名字像是从设备树里面提取资源值,但是本质上是将reg 属性值转换为resource 结构体类型。函数原型如下:

int of_address_to_resource(struct device_node *dev, int index,
				  struct resource *r);
  • dev : 设备节点
  • index : 地址资源标号
  • r : 得到的resource 类型的资源值
  • 返回值: 0,成功;负值,失败。

5. of_iomap

of_iomap 函数用于直接内存映射,以前我们会通过ioremap 函数来完成物理地址到虚拟地址的映射,采用设备树以后就可以直接通过of_iomap 函数来获取内存地址所对应的虚拟地址。

of_iomap 函数本质上也是将reg 属性中地址信息转换为虚拟地址,reg 属性有多段的话,可以通过index 参数指定要完成内存映射的是哪一段。

void __iomem *of_iomap(struct device_node *node, int index);
  • node : 设备节点
  • index : reg 属性中要完成内存映射的段,如果reg 属性只有一段的话 index 就设置为0 。
  • 返回值 :经过内存映射后的虚拟内存首地址,如果为NULL 表示内存映射失败。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gdut_llkkyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值