linux 设备树of函数学习笔记

上一篇对dts设备树有了初步的了解,完全手写设备树目前还打不到这样的实力,还好的就是技术的开放性,很多设备都有dts模板,可以更改适配。​

活动地址:CSDN21天学习挑战赛

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:
想系统/深入学习某技术知识点…
一个人摸索学习很难坚持,想组团高效学习…
想写博客但无从下手,急需写作干货注入能量…
热爱写作,愿意让自己成为更好的人…

​本篇文章讲述的是设备树的解析函数,就是of系列函数,参考网址:

DeviceTree Kernel API — The Linux Kernel documentation

Linux device tree API (programs.wiki)

在学习这部分知识的时候,发现很多人都写好了,资料也特别多,想看源码的话,参考下面的链接:包含了各个版本的linux内核代码,每一个函数参考起来会跳来跳去,充分体现了数据结构组织的好,代码写得少的哲学关系。

of.h - include/linux/of.h - Linux source code (v2.6.39.4) - Bootlin

根据上课所用的教学PPT,大约有这么多函数。

 以下内容为翻译的wiki,可以直接看原文,只是为了学习和打卡,望读者见谅。

Linux内核采用设备树后,驱动需要获取设备树的属性。 Linux内核提供了一系列API函数供驱动获取设备树的属性。 Linux 内核中的“Of_” 从设备树 API 开始。

首先看一下device_node结构体定义:

struct device_node {
	const char *name;
	const char *type;
	phandle phandle;        // 这是一个u32的数据
	char	*full_name;

	struct	property *properties;
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;
	struct	device_node *child;
	struct	device_node *sibling;
	struct	device_node *next;	/* next device of same type */
	struct	device_node *allnext;	/* next in list of all nodes */
	struct	proc_dir_entry *pde;	/* this node's proc directory */
	struct	kref kref;
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	char	*path_component_name;
	unsigned int unique_id;
	struct of_irq_controller *irq_trans;
#endif
};

1.获取设备节点API
在内核中,设备是以节点的形式附加到设备树上的,所以要获取设备信息,首先要获取设备节点。

1.1、of_find_node_by_name函数
of_find_node_by_name函数通过设备节点的名字获取设备节点

struct device_node *of_find_node_by_name(struct device_node *from,const char *name)

from:指定从哪里开始搜索设备节点。 如果为 NULL,则从根节点开始搜索。
name:要查找的设备节点的名称。
成功返回设备节点结构,失败返回NULL。

1.2、of_find_node_by_type函数
of_find_node_by_type函数通过节点类型获取设备节点

struct device_node *of_find_node_by_type(struct device_node *from,const char *type)

From:指定从哪里开始搜索设备节点。 如果为 NULL,则从根节点开始搜索。
type:要查找的设备节点的类型,device_type属性值。
成功返回设备节点结构,失败返回NULL。

1.3、of_find_compatible_node函数
of_find_compatible_node函数通过节点的compatible属性和类型获取设备节点

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

From:指定从哪里开始搜索设备节点。 如果为 NULL,则从根节点开始搜索。
type:要查找的设备节点的类型。 如果为 NULL,则类型将被忽略。
Compatible:要查找的设备节点的兼容属性名称。
成功返回设备节点结构,失败返回NULL。

1.4、of_find_node_by_path函数
of_find_node_by_path函数通过路径名获取设备节点

struct device_node *of_find_node_by_path(const char *path)

Path: path name
The device node structure is returned successfully, and NULL is returned for failure.

2.获取父子节点API

2.1,of_get_parent function

struct device_node *of_get_parent(const struct device_node *node)

Node:要查找的父节点的节点
该节点的父节点返回成功,失败则返回NULL

2.2,of_get_next_child function
of_get_next_child函数可以循环遍历子节点

struct device_node *of_get_next_child(const struct device_node *node,
	struct device_node *prev)

node:父节点
prev:上一个子节点,即从哪个子节点开始搜索。 NULL 表示从第一个开始搜索。
返回值是找到的下一个子节点.

3.提取设备树属性API
设备树中的属性在内核中以结构的形式表示。 下面是属性结构.

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

3.1 of_find_property function

of_find_property function能够获得指定的属性

struct property *of_find_property(const struct device_node *np,
				  const char *name,
				  int *lenp)

np: device node
Name: attribute name
lenp: attribute length
example:

of_find_property(client->dev.of_node, "linux,gpio-keymap",&proplen)

上述代码段通过of_find_属性函数获取设备中属性“Linux, GPIO keymap”的值。

3.2,of_property_read_u32_index function

of_property_read_u32_index 读取设备树中属性为 32 位无符号的值。 您可以指定要读取的标签。

int of_property_read_u32_index(const struct device_node *np,
				       const char *propname,
				       u32 index, u32 *out_value)

np:设备节点
propname:属性名称
index:标签,读取数字
out_value; 读出值
返回值:0 读取成功,-EINVAL 表示属性不存在,-ENODATA 表示没有数据可读取,-EOVERFLOW 表示属性值列表太小。

3.3,of_property_read_u8_array of_property_read_u16_array of_property_read_u32_array of_property_read_u64_array

of_property_read_u8_array
of_property_read_u16_array
of_property_read_u32_array
of_property_read_u64_array
These four functions can read unsigned data at one time, such as address data in reg attribute.

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)

np:设备节点
propname:属性名称
out_values:读出值
sz:读取多少数据
返回值:0,读取成功,-EINVAL表示该属性不存在,-ENODATA表示不存在
有数据要读取,- EOVERFLOW 表示属性值列表太小。

3.4,of_property_read_u8 of_property_read_u16 of_property_read_u32 of_property_read_u64

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)

这些函数用于读取属性值
np:设备节点
propname:属性名称
out_values:读出值
返回值:0,读取成功,-EINVAL表示该属性不存在,-ENODATA表示不存在
有数据要读取,- EOVERFLOW 表示属性值列表太小。

3.5,of_property_read_string

of_property_read_string is used to get the string

int of_property_read_string(struct device_node *np, const char *propname,
				const char **out_string)

np:设备节点
propname:属性名称
out_string:读取字符串
返回值:0,读取成功,负值,读取失败。

3.6,of_n_addr_cells
of_n_addr_cells is used to get the address_cells attribute value

int of_n_addr_cells(struct device_node *np)

np:设备节点
返回值:#address 单元格属性的获取值。

3.7,of_n_size_cells

of_n_size_cells is used to get the size cells attribute value

int of_n_size_cells(struct device_node *np)

np:设备节点
返回值:#size 个单元格属性的获取值。

4.其他常用的设备树API函数
IIC、SPI、GPIO等外设都有自己的寄存器地址,而这些寄存器地址实际上是一组内存空间。 Linux内核提供了设备树的一些API函数来获取这些寄存器地址。

4.1 获取设备资源
Linux内核将寄存器、中断等信息描述为一组内存资源,用一个结构体表示

struct resource {
	resource_size_t start; // Starting address
	resource_size_t end;   // End address
	const char *name;      // name
	unsigned long flags;   // attribute
	struct resource *parent, *sibling, *child;
};

flags 用于表示资源类型,可以包括以下资源类型

#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

#define IORESOURCE_SIZEALIGN	0x00040000	/* size indicates alignment */
#define IORESOURCE_STARTALIGN	0x00080000	/* start field is alignment */

#define IORESOURCE_MEM_64	0x00100000
#define IORESOURCE_WINDOW	0x00200000	/* forwarded by bridge */
#define IORESOURCE_MUXED	0x00400000	/* Resource is software muxed */

#define IORESOURCE_EXCLUSIVE	0x08000000	/* Userland may not map this resource */
#define IORESOURCE_DISABLED	0x10000000
#define IORESOURCE_UNSET	0x20000000	/* No address assigned yet */
#define IORESOURCE_AUTO		0x40000000
#define IORESOURCE_BUSY		0x80000000	/* Driver has marked this resource busy */

of_ address_ to_ The resource function is used to obtain resources

int of_address_to_resource(struct device_node *node, int index,
			   struct resource *r)

节点:设备节点
index:获取的资源数量
r:获取资源结构
返回值:0,成功; 负值,失败。

4.1、of_iomap
of_iomap函数用于映射内存地址,将物理地址映射到虚拟地址。 过去没有设备树的时候,内存地址映射通常需要两步。 首先platform_get_resource获取资源,然后ioremap进行地址映射。 现在有了设备树,可以直接使用of_iomap同时进行资源获取和地址映射。

void __iomem *of_iomap(struct device_node *np, int index)
{
	struct resource res;

	if (of_address_to_resource(np, index, &res))
		return NULL;

	return ioremap(res.start, resource_size(&res));
}

np:设备节点
index:你想获取哪个资源
返回值:内存映射后的虚拟内存首地址。 如果为NULL,则表示内存映射失败。

========================================================================

实际上内核写好了这些函数,所以我们学习的时候,可以不求甚解,会用就行。

猜你喜欢

转载自blog.csdn.net/weixin_41579872/article/details/126138811