Linux device tree of function study notes

The previous article gave me a preliminary understanding of dts device trees. A completely handwritten device tree is currently not capable of achieving such capabilities. Fortunately, the technology is open. Many devices have dts templates that can be changed and adapted.

Event address: CSDN 21-day learning challenge

The biggest reason for learning is to get rid of mediocrity. One day early will bring more excitement to life; one day late will bring more troubles of mediocrity. Dear friends, if you:
want to systematically/in-depth study a certain technical knowledge point...
It is difficult to persist in learning alone, and you want to form a group to learn efficiently... You
want to write a blog but have no idea how to start, and you urgently need some writing to inject energy...
Love writing, and are willing to let yourself become Better people

This article talks about the parsing function of the device tree, which is the of series of functions. Reference website:

DeviceTree Kernel API — The Linux Kernel documentation

Linux device tree API (programs.wiki)

When learning this part of knowledge, I found that many people have written it, and there is a lot of information. If you want to see the source code, refer to the link below: It contains various versions of the Linux kernel code, and each function will jump around when you refer to it. , fully embodies the philosophical relationship of good data structure organization and less code writing.

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

According to the teaching PPT used in class, there are approximately so many functions.

 The following content is a translated wiki. You can read the original text directly. It is just for learning and checking in. I hope readers will forgive me.

After the Linux kernel adopts the device tree, the driver needs to obtain the attributes of the device tree. The Linux kernel provides a series of API functions for drivers to obtain the attributes of the device tree. "Of_" in the Linux kernel starts with the device tree API.

First, take a look at the device_node structure definition:

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. Obtain device node API
In the kernel, devices are attached to the device tree in the form of nodes, so to obtain device information, you must first obtain the device node.

1.1. of_find_node_by_name function
of_find_node_by_name function obtains the device node through the name of the device node

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

from: Specify where to start searching for device nodes. If NULL, the search starts from the root node.
name: The name of the device node to be found.
The device node structure is returned successfully, and NULL is returned on failure.

1.2. The of_find_node_by_type function
of_find_node_by_type function obtains the device node through the node type.

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

From: Specify where to start searching for device nodes. If NULL, the search starts from the root node.
type: The type of device node to be found, device_type attribute value.
The device node structure is returned successfully, and NULL is returned on failure.

1.3. The of_find_compatible_node function
of_find_compatible_node function obtains the device node through the node's compatible attribute and type.

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

From: Specify where to start searching for device nodes. If NULL, the search starts from the root node.
type: The type of device node to be found. If NULL, the type will be ignored.
Compatible: The compatible attribute name of the device node to be found.
The device node structure is returned successfully, and NULL is returned on failure.

1.4. The of_find_node_by_path function
of_find_node_by_path function obtains the device node through the path name.

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. Get the parent-child node API

2.1,of_get_parent function

struct device_node *of_get_parent(const struct device_node *node)

Node: The node of the parent node to be found.
The parent node of this node returns success, otherwise NULL is returned.

2.2, of_get_next_child function
of_get_next_child function can loop through child nodes

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

node: parent node
prev: previous child node, that is, which child node to start searching from. NULL means to start searching from the first one.
The return value is the next child node found.

3. Extract device tree attributes.
The attributes in the API device tree are represented in the form of structures in the kernel. Below is the attribute structure.

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 can obtain the specified property

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)

The above code snippet obtains the value of the attribute "Linux, GPIO keymap" in the device through the of_find_attribute function.

3.2,of_property_read_u32_index function

of_property_read_u32_index reads the 32-bit unsigned value of the property in the device tree. You can specify which tags to read.

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

np: device node
propname: attribute name
index: label, read number
out_value; read value return
value: 0 read successfully, -EINVAL indicates that the attribute does not exist, -ENODATA indicates that there is no data to read, -EOVERFLOW indicates the attribute value list too small.

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: device node
propname: attribute name
out_values: read value
sz: how much data to read
Return value: 0, read successfully, -EINVAL means the attribute does not exist, -ENODATA means it does not exist and
there is data to read, - EOVERFLOW means Property value list is too small.

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)

These functions are used to read attribute values
​​np: device node
propname: attribute name
out_values: read value return
value: 0, read successfully, -EINVAL means that the attribute does not exist, -ENODATA means that there is no
data to read, - EOVERFLOW indicates that the attribute value list is too small.

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: device node
propname: attribute name
out_string: read string
return value: 0, read successfully, negative value, read failed.

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: device node
Return value: #address Get value of cell attribute.

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: Device node
Return value: The obtained value of #size cell attributes.

4. Other commonly used device tree API functions such as
IIC, SPI, GPIO and other peripherals have their own register addresses, and these register addresses are actually a set of memory spaces. The Linux kernel provides some API functions of the device tree to obtain these register addresses.

4.1 Obtaining device resources
The Linux kernel describes registers, interrupts and other information as a set of memory resources, represented by a structure

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 are used to indicate resource types, which can include the following resource types

#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)

Node: device node
index: number of resources obtained
r: resource structure obtained
Return value: 0, success; negative value, failure.

4.1. of_iomap
The of_iomap function is used to map memory addresses and map physical addresses to virtual addresses. In the past, when there was no device tree, memory address mapping usually required two steps. First platform_get_resource obtains the resource, and then ioremap performs address mapping. Now that there is a device tree, you can directly use of_iomap to obtain resources and address mapping at the same time.

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: device node
index: which resource you want to obtain
. Return value: the first address of the virtual memory after memory mapping. If NULL, memory mapping failed.

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

In fact, the kernel has written these functions, so when we learn, we don’t need to ask for a detailed explanation, just know how to use them.

Guess you like

Origin blog.csdn.net/weixin_41579872/article/details/126138811