一、概述
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 表示内存映射失败。