imx6ull-驱动开发篇8——设备树常用 OF 操作函数

目录

前言

[Linux 内核解析 DTB 文件](#Linux 内核解析 DTB 文件)

绑定信息文档

必需属性​

可选属性​

设备树示例​

[标准 I2C 控制器(i.MX51)​](#标准 I2C 控制器(i.MX51))

[高速 I2C(HS-I2C,i.MX51)](#高速 I2C(HS-I2C,i.MX51))

[支持 DMA 的 I2C(Vybrid vf610)​](#支持 DMA 的 I2C(Vybrid vf610))

[设备树常用 OF 操作函数](#设备树常用 OF 操作函数)

查找节点

[of_find_node_by_name 函数](#of_find_node_by_name 函数)

[of_find_node_by_type 函数](#of_find_node_by_type 函数)

[of_find_compatible_node 函数](#of_find_compatible_node 函数)

[of_find_matching_node_and_match 函数](#of_find_matching_node_and_match 函数)

[of_find_node_by_path 函数](#of_find_node_by_path 函数)

查找父/子节点

[of_get_parent 函数](#of_get_parent 函数)

[of_get_next_child 函数](#of_get_next_child 函数)

提取属性值

[of_find_property 函数](#of_find_property 函数)

[of_property_count_elems_of_size 函数](#of_property_count_elems_of_size 函数)

[of_property_read_u32_index 函数](#of_property_read_u32_index 函数)

读取数组数据

读取整形值

[of_property_read_string 函数](#of_property_read_string 函数)

[of_n_addr_cells 函数](#of_n_addr_cells 函数)

[of_n_size_cells 函数](#of_n_size_cells 函数)

[其他常用的 OF 函数](#其他常用的 OF 函数)

[of_device_is_compatible 函数](#of_device_is_compatible 函数)

[of_get_address 函数](#of_get_address 函数)

[of_translate_address 函数](#of_translate_address 函数)

[of_address_to_resource 函数](#of_address_to_resource 函数)

[of_iomap 函数](#of_iomap 函数)


前言

在上一讲内容里,如何编写设备树,我们按照dts语法,创建了小型模板设备树,并且学会怎么查看设备树的根节点和字节点。

本讲实验里,我们大概了解一下设备树节点解析流程、设备树绑定文档、设备树常用 OF 操作函数。

Linux 内核解析 DTB 文件

Linux 内核在启动的时候会解析 DTB 文件,然后在/proc/device-tree 目录下生成相应的设备树节点文件。

Linux 内核解析 DTB 文件的流程如图:

  • 启动入口:start_kernel()→ 内核初始化入口函数,位于 init/main.c
  • 架构初始化:setup_arch()→ 初始化硬件架构相关配置(arch/arm/kernel/setup.c)
  • 设备树展开:unflatten_device_tree()→ 启动设备树解析流程(drivers/of/fdt.c)__unflatten_device_tree()→ 创建内存中的设备树结构(链表+树形结构)
  • 节点解析核心:unflatten_dt_node()→ 递归解析每个设备树节点,包括:
    • 节点属性(compatible, reg等)
    • 父子节点关系
    • 内存资源映射
  • 最终输出生成内核可用的 device_node结构体链表,供驱动和设备管理子系统使用。

绑定信息文档

设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。

在Linux 内核源码中有详细的.txt 文档描述了如何添加节点,这些.txt 文档叫做绑定文档,路径为: Linux 源码目录/Documentation/devicetree/bindings,如图:

比如我们现在要想在 I.MX6ULL 这颗 SOC 的 I2C 下添加一个节点,那么就可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt,

此文档详细的描述了 I.MX 系列的 SOC 如何在设备树中添加 I2C 设备节点,

文档内容如下所示:

cpp 复制代码
* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX
Required properties:
- compatible :
- "fsl,imx1-i2c" for I2C compatible with the one integrated on i.MX1
SoC
- "fsl,imx21-i2c" for I2C compatible with the one integrated on i.MX21
SoC
- "fsl,vf610-i2c" for I2C compatible with the one integrated on Vybrid
vf610 SoC
- reg : Should contain I2C/HS-I2C registers location and length
- interrupts : Should contain I2C/HS-I2C interrupt
- clocks : Should contain the I2C/HS-I2C clock specifier
Optional properties:
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in
Hz.
The absence of the propoerty indicates the default frequency 100 kHz.
- dmas: A list of two dma specifiers, one for each entry in dma-names.
- dma-names: should contain "tx" and "rx".

Examples:
i2c@83fc4000 { /* I2C2 on i.MX51 */
    compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
    reg = <0x83fc4000 0x4000>;
    interrupts = <63>;
};
i2c@70038000 { /* HS-I2C on i.MX51 */
    compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
    reg = <0x70038000 0x4000>;
    interrupts = <64>;
    clock-frequency = <400000>;
};
i2c0: i2c@40066000 { /* i2c0 on vf610 */
    compatible = "fsl,vf610-i2c";
    reg = <0x40066000 0x1000>;
    interrupts =<0 71 0x04>;
    dmas = <&edma0 0 50>,
    <&edma0 0 51>;
    dma-names = "rx","tx";
}

必需属性

​属性​ ​描述​
compatible ​驱动兼容性字符串​ ​,用于匹配内核驱动: • "fsl,imx1-i2c"→ i.MX1 的 I2C 控制器 • "fsl,imx21-i2c"→ i.MX21 及后续 SoC 的 I2C 控制器 • "fsl,vf610-i2c"→ Vybrid vf610 的 I2C 控制器
reg ​寄存器地址和大小​ ​,格式:<基地址 长度> (如 reg = <0x021a0000 0x4000>;表示基地址 0x021a0000,大小 16KB
interrupts ​中断号​ ​,格式:<中断号><GIC_SPI 中断号 触发类型> (如 interrupts = <64>;interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks ​时钟源​ ​,引用时钟控制器节点(如 clocks = <&clks IMX6UL_CLK_I2C1>;

可选属性

​属性​ ​描述​
clock-frequency ​I2C 总线时钟频率(Hz)​ ​,默认 100 kHz(未指定时) (如 clock-frequency = <400000>;表示 400 kHz
dmas ​DMA 通道配置​ ​,格式:<DMA控制器引用 通道号 请求号> (如 dmas = <&edma0 0 50>, <&edma0 0 51>;
dma-names ​DMA 通道名称​ ​,必须与 dmas一一对应: • "tx"→ 发送通道 • "rx"→ 接收通道

设备树示例​

标准 I2C 控制器(i.MX51)​

cpp 复制代码
i2c@83fc4000 { /* I2C2 on i.MX51 */
    compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
    reg = <0x83fc4000 0x4000>;      // 寄存器基地址 0x83fc4000,大小 16KB
    interrupts = <63>;              // 中断号 63
    clocks = <&i2c_clk>;           // 时钟源引用
};

高速 I2C(HS-I2C,i.MX51)

cpp 复制代码
i2c@70038000 { /* HS-I2C on i.MX51 */
    compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
    reg = <0x70038000 0x4000>;      // 寄存器基地址 0x70038000
    interrupts = <64>;              // 中断号 64
    clock-frequency = <400000>;     // 总线频率 400 kHz
};

支持 DMA 的 I2C(Vybrid vf610)​

cpp 复制代码
i2c0: i2c@40066000 { /* i2c0 on vf610 */
    compatible = "fsl,vf610-i2c";
    reg = <0x40066000 0x1000>;      // 寄存器基地址 0x40066000,大小 4KB
    interrupts = <0 71 0x04>;       // GIC 中断号 71,高电平触发
    dmas = <&edma0 0 50>,           // DMA 发送通道
           <&edma0 0 51>;           // DMA 接收通道
    dma-names = "rx", "tx";         // 通道名称
};

设备树常用 OF 操作函数

设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动的时候需要获取到这些信息。

Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀"of_",所以在很多资料里面也被叫做 OF 函数。

这些 OF 函数原型都定义在 include/linux/of.h 文件中。

查找节点

要想获取这个设备的其他属性信息,必须先获取到这个设备的节点。

Linux 内核使用device_node 结构体来描述一个节点,

此结构体定义在文件 include/linux/of.h 中,定义如下:

cpp 复制代码
/**
 * struct device_node - 设备树节点在内核中的表示
 * 
 * 内核将设备树中的每个节点转换为该结构体实例,
 * 用于驱动匹配、资源管理和设备初始化。
 */
struct device_node {
    /* 基础标识信息 */
    const char *name;           // 节点基础名(如 "i2c" 对应节点 i2c@021a0000)
    const char *type;           // 设备类型(历史遗留,现多用 compatible 替代)
    phandle phandle;            // 节点唯一句柄,用于跨节点引用
    const char *full_name;      // 节点全路径名(如 "/soc/i2c@021a0000")
    struct fwnode_handle fwnode;// 统一设备属性接口句柄

    /* 属性管理 */
    struct property *properties; // 有效属性链表头(含 reg/compatible 等)
    struct property *deadprops;  // 已删除属性链表(用于安全卸载)

    /* 节点拓扑关系 */
    struct device_node *parent; // 父节点指针(如 i2c1 的父节点是 soc)
    struct device_node *child;   // 子节点链表头
    struct device_node *sibling; // 兄弟节点指针,形成同级链表

    /* 系统集成 */
    struct kobject kobj;        // sysfs 对象,在 /sys/firmware/devicetree 中可见
    unsigned long _flags;       // 状态标志位(如 OF_POPULATED 表示已初始化)
    void *data;                 // 驱动私有数据指针

#if defined(CONFIG_SPARC)
    /* SPARC 架构专用字段 */
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

与查找节点有关的 OF 函数有 5 个:

of_find_node_by_name 函数

of_find_node_by_name 函数通过节点名字查找指定的节点,函数原型如下:

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

of_find_node_by_type 函数

of_find_node_by_type 函数通过 device_type 属性查找指定的节点,函数原型如下:

cpp 复制代码
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
  • from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
  • type:要查找的节点对应的 type 字符串,也就是 device_type 属性值。
  • 返回值: 找到的节点,如果为 NULL 表示查找失败。

of_find_compatible_node 函数

of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点,函数原型如下:

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

of_find_matching_node_and_match 函数

of_find_matching_node_and_match 函数通过 of_device_id 匹配表来查找指定的节点,函数原型如下:

cpp 复制代码
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 函数

of_find_node_by_path 函数通过路径来查找指定的节点,函数原型如下:

cpp 复制代码
inline struct device_node *of_find_node_by_path(const char *path)
  • path:带有全路径的节点名,可以使用节点的别名,比如"/backlight"就是 backlight 这个节点的全路径。
  • 返回值: 找到的节点,如果为 NULL 表示查找失败。

查找父/子节点

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

of_get_parent 函数

of_get_parent 函数用于获取指定节点的父节点(如果有父节点的话),函数原型如下:

cpp 复制代码
struct device_node *of_get_parent(const struct device_node *node)
  • node:要查找的父节点的节点。
  • 返回值: 找到的父节点。

of_get_next_child 函数

of_get_next_child 函数用迭代的方式查找子节点,函数原型如下:

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

提取属性值

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,

Linux 内核中使用结构体 property 表示属性,property 结构体定义在文件 include/linux/of.h 中,内容如下:

cpp 复制代码
/**
 * struct property - 设备树属性的内核表示
 * 
 * 描述设备树中节点的单个属性(如reg、compatible等),
 * 以链表形式组织在device_node结构中。
 */
struct property {
    char *name;                 /* 属性名称(如 "compatible"、"reg") */
    int length;                 /* 属性值的字节长度(不含结束符) */
    void *value;                /* 属性值指针(类型根据name动态解析):
                                 * - 字符串:"fsl,imx6ul-i2c"
                                 * - 数值数组:<0x021a0000 0x4000>
                                 * - 混合数据:[字节流]
                                 */
    struct property *next;      /* 指向下一个属性的链表指针 */
    unsigned long _flags;       /* 内核内部使用的状态标志位:
                                 * - OF_DYNAMIC: 属性值动态分配
                                 * OF_PROVIDED: 由firmware提供
                                 */
    unsigned int unique_id;     /* 调试用唯一标识符 */
    struct bin_attribute attr;  /* 在sysfs中作为二进制属性暴露时的描述符 */
};

Linux 内核也提供了提取属性值的 OF 函数:

of_find_property 函数

of_find_property 函数用于查找指定的属性,函数原型如下:

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

of_property_count_elems_of_size 函数

of_property_count_elems_of_size 函数用于获取属性中元素的数量,比如 reg 属性值是一个

数组,那么使用此函数可以获取到这个数组的大小,

此函数原型如下:

cpp 复制代码
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 函数

of_property_read_u32_index 函数用于从属性中获取指定标号的 u32 类型数据值(无符号 32位),比如某个属性有多个 u32 类型的值,那么就可以使用此函数来获取指定标号的数据值,

此函数原型如下:

cpp 复制代码
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 个函数分别是读取属性中 u8、 u16、 u32 和 u64 类型的数组数据,比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据。

这四个函数的原型如下:

cpp 复制代码
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:设备节点。
  • proname: 要读取的属性名字。
  • out_value:读取到的数组值,分别为 u8、 u16、 u32 和 u64。
  • sz: 要读取的数组元素数量。

返回值:

0,读取成功,负值,读取失败:

  • -EINVAL 表示属性不存在,
  • -ENODATA 表示没有要读取的数据,
  • -EOVERFLOW 表示属性值列表太小。

读取整形值

有些属性只有一个整形值,这四个函数就是用于读取这种只有一个整形值的属性,分别用于读取 u8、 u16、 u32 和 u64 类型属性值,函数原型如下:

cpp 复制代码
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:设备节点。
  • proname: 要读取的属性名字。
  • out_value:读取到的数组值。
  • 返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。

of_property_read_string 函数

of_property_read_string 函数用于读取属性中字符串值,函数原型如下:

cpp 复制代码
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 函数

of_n_addr_cells 函数用于获取#address-cells 属性值,函数原型如下:

cpp 复制代码
int of_n_addr_cells(struct device_node *np)
  • np:设备节点。
  • 返回值: 获取到的#address-cells 属性值。

of_n_size_cells 函数

of_size_cells 函数用于获取#size-cells 属性值,函数原型如下:

cpp 复制代码
int of_n_size_cells(struct device_node *np)
  • np:设备节点。
  • 返回值: 获取到的#size-cells 属性值.

其他常用的 OF 函数

of_device_is_compatible 函数

of_device_is_compatible 函数用于查看节点的 compatible 属性是否有包含 compat 指定的字

符串,也就是检查设备节点的兼容性,函数原型如下:

cpp 复制代码
int of_device_is_compatible(const struct device_node *device,
                            const char *compat)
  • device:设备节点。
  • compat:要查看的字符串。
  • 返回值: 0,节点的 compatible 属性中不包含 compat 指定的字符串; 正数,节点的 compatible
  • 属性中包含 compat 指定的字符串。

of_get_address 函数

of_get_address 函数用于获取地址相关属性,主要是"reg"或者"assigned-addresses"属性

值,函数原型如下:

cpp 复制代码
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 的话表示读取失败。

of_translate_address 函数

of_translate_address 函数负责将从设备树读取到的地址转换为物理地址,函数原型如下:

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

of_address_to_resource 函数

IIC、 SPI、 GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间, Linux

内核使用 resource 结构体来描述一段内存空间,用 resource结构体描述的都是设备资源信息。

resource 结构体定义在文件 include/linux/ioport.h 中,定义如下:

cpp 复制代码
/**
 * struct resource - 描述硬件资源的内核数据结构
 * 
 * 用于管理内存区域、I/O端口、中断等硬件资源,
 * 形成树形结构以实现资源冲突检测和分配。
 */
struct resource {
    resource_size_t start;  /* 资源起始地址(物理地址/中断号)*/
    resource_size_t end;    /* 资源结束地址(包含在内)*/
    const char *name;       /* 资源名称(如 "uart1-mmio")*/
    unsigned long flags;    /* 资源类型和属性标志:
                            * - IORESOURCE_MEM: 内存映射区域
                            * - IORESOURCE_IO: I/O端口
                            * - IORESOURCE_IRQ: 中断号
                            * - IORESOURCE_BUSY: 资源已占用
                            */
    /* 资源树管理指针 */
    struct resource *parent;  /* 父资源(如总线域)*/
    struct resource *sibling; /* 兄弟节点(同级资源链表)*/
    struct resource *child;   /* 子资源(下级细化资源)*/
};

flags 是资源标志位,一般表示资源类型,可选的资源标志定义在文件 include/linux/ioport.h 中,如下所示:

cpp 复制代码
/**
 * 资源标志位宏定义 - 描述硬件资源的类型和属性
 * 
 * 这些标志位用于struct resource的flags字段,
 * 通过位掩码方式组合使用,分为以下几类:
 */

/* 资源类型标志(占用bit8~bit12)*/
#define IORESOURCE_BITS        0x000000ff  // 资源子类型掩码
#define IORESOURCE_TYPE_BITS   0x00001f00  // 资源类型主类别掩码

/* 基础资源类型(互斥类型)*/
#define IORESOURCE_IO          0x00000100  // I/O端口资源(如x86 in/out指令访问)
#define IORESOURCE_MEM         0x00000200  // 内存映射资源(最常见类型)
#define IORESOURCE_REG         0x00000300  // 寄存器资源(特殊内存区域)
#define IORESOURCE_IRQ         0x00000400  // 中断资源(硬件中断号)
#define IORESOURCE_DMA         0x00000800  // DMA通道资源
#define IORESOURCE_BUS         0x00001000  // 总线编号资源(如PCI总线号)

/* 内存资源扩展属性(可组合使用)*/
#define IORESOURCE_PREFETCH    0x00002000  // 可预取内存(如PCI预取BAR)
#define IORESOURCE_READONLY    0x00004000  // 只读内存区域(如ROM)
#define IORESOURCE_CACHEABLE   0x00008000  // 可缓存内存(默认不可缓存)

/* 特殊功能标志 */
#define IORESOURCE_RANGELENGTH 0x00010000  // 资源长度包含在值中
#define IORESOURCE_SHADOWABLE  0x00020000  // 可影子复制资源(如视频RAM)
#define IORESOURCE_SIZEALIGN   0x00040000  // 需要大小对齐
#define IORESOURCE_STARTALIGN  0x00080000  // 需要起始地址对齐
#define IORESOURCE_MEM_64      0x00100000  // 64位内存地址资源
#define IORESOURCE_WINDOW      0x00200000  // 桥接窗口资源(如PCI桥)
#define IORESOURCE_MUXED       0x00400000  // 多路复用资源(共享物理线路)

/* 状态标志 */
#define IORESOURCE_EXCLUSIVE   0x08000000  // 资源需要独占访问
#define IORESOURCE_DISABLED    0x10000000  // 资源已被禁用
#define IORESOURCE_UNSET       0x20000000  // 资源未初始化
#define IORESOURCE_AUTO        0x40000000  // 自动分配资源
#define IORESOURCE_BUSY        0x80000000  // 资源已被占用(关键标志)

/**
 * 使用示例:
 * 
 * 定义一个内存映射且可缓存的忙状态资源:
 * unsigned long flags = IORESOURCE_MEM | IORESOURCE_CACHEABLE | IORESOURCE_BUSY;
 * 
 * 检查资源类型:
 * if (flags & IORESOURCE_MEM) { ... }
 */

of_address_to_resource 函数,本质上就是将 reg 属性值,然后将其转换为 resource 结构体类型,函数原型如下所示:

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

of_iomap 函数

of_iomap 函数用于直接内存映射,采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟地址。

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

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

通过前面的几篇文章,关于设备树我们重点学习了下面几点内容:

①、 DTS、 DTB 和 DTC 之间的区别,如何将.dts 文件编译为.dtb 文件。

②、设备树语法,这个是重点,因为在实际工作中我们是需要修改设备树的。

③、设备树的几个特殊子节点。

④、关于设备树的 OF 操作函数。

相关推荐
deeper_wind1 小时前
k8s-容器化部署论坛和商城服务(小白的“升级打怪”成长之路)
linux·运维·容器·kubernetes
勇往直前plus1 小时前
VMware centos磁盘容量扩容教程
linux·运维·centos
猫猫的小茶馆3 小时前
【STM32】CubeMX(十二):FreeRTOS消息队列
驱动开发·stm32·单片机·嵌入式硬件·mcu·智能硬件
政安晨3 小时前
Ubuntu 服务器无法 ping 通网站域名的问题解决备忘 ——通常与网络配置有关(DNS解析)
linux·运维·服务器·ubuntu·ping·esp32编译服务器·dns域名解析
路溪非溪4 小时前
嵌入式Linux驱动开发杂项总结
linux·运维·驱动开发
Neolock5 小时前
Linux应急响应一般思路(三)
linux·web安全·应急响应
被遗忘的旋律.6 小时前
Linux驱动开发笔记(七)——并发与竞争(上)——原子操作
linux·驱动开发·笔记
轻松Ai享生活6 小时前
minidump vs core dump
linux
轻松Ai享生活7 小时前
详细的 Linux 常用文件系统介绍
linux
张童瑶7 小时前
Linux 离线安装lrzsz(rz、sz上传下载小插件)
linux·运维·centos