📍 本文适用于 Linux 内核开发者、嵌入式工程师、Yocto 项目实践者,深入讲解设备树(Device Tree)的结构、属性、驱动匹配机制与常用
of_函数,结合 NXP i.MX 8M Plus EVK 示例展开分析。
🔍
B站相应的视屏教程:
📌 内核:博文+视频 - 备树深度解析:理论 + 实践全指南(含 of 函数与 i.MX8MP 实例)
敬请关注,记得标为原始粉丝。
🔧
一、设备树基础回顾
设备树(Device Tree)是一种结构化的数据格式,用于描述嵌入式系统中 SoC 的硬件资源,让 Linux 内核能够在运行时自动识别与配置设备。

为什么需要设备树?
- 内核解耦:避免将硬件信息写死在内核中,提升可移植性
- 硬件抽象:用统一的语法格式描述 CPU、总线、中断、GPIO、I2C 等外设结构
- 启动过程简化:U-Boot 加载 DTB 文件传递给内核,内核依赖设备树完成 early init
示例结构
uart2: serial@30890000 {
compatible = "fsl,imx8mp-uart", "fsl,imx6q-uart"; // 用于匹配驱动的字符串
reg = <0x30890000 0x10000>; // 寄存器地址范围
status = "okay"; // 启用状态
};
二、核心结构:device_node 与 property
在内核中,设备树通过 struct device_node 和 struct property 两个核心结构体在内存中表示:
2.1 device_node 结构体详解
struct device_node {
const char *name; // 节点名称(如 serial@30890000)
phandle phandle; // 节点唯一标识(句柄 ID)
const char *full_name; // 完整路径名称(如 /soc/serial@30890000)
struct property *properties;// 该节点的属性链表
struct device_node *parent; // 父节点
struct device_node *child; // 第一个子节点
struct device_node *sibling;// 下一个兄弟节点
void *data; // 绑定的私有数据
};
- 每个设备节点都构成一棵设备树结构体的内存树。
- 每个节点都包含其父子兄弟节点指针,构成树形结构。
data字段常用于内核内部传递与该节点关联的资源。
2.2 property 结构体详解
struct property {
char *name; // 属性名称,例如 "compatible"
int length; // 属性值长度(单位:字节)
void *value; // 属性值,可能是字符串、整数数组或结构体等
struct property *next;// 下一个属性指针(链表结构)
};
- 所有属性构成该节点下的属性链表。
- 属性值(
value)内容不定,由具体解析函数(如of_property_read_u32)转换。 - 内核遍历这些属性用于驱动匹配、资源获取。
三、常用 of_ 函数解析与实践
Linux 内核提供了一系列以 of_ 开头的函数用于访问和遍历设备树信息,以下列举最常用的函数及其用法:
3.1 查找节点
of_find_node_by_name(NULL, "serial");
of_find_compatible_node(NULL, NULL, "fsl,imx8mp-uart");
of_find_node_by_path("/soc/serial@30890000");
3.2 获取属性
of_get_property(np, "reg", NULL);
of_property_read_u32(np, "clock-frequency", &val);
of_property_read_string(np, "model", &str);
3.3 遍历节点与属性
for_each_child_of_node(parent, child) {
pr_info("Child: %s\n", child->full_name);
}
for_each_property_of_node(np, prop) {
pr_info("Property: %s\n", prop->name);
}
3.4 解析 phandle 引用(深入讲解)
buck2: regulator@2 {
compatible = "regulator-fixed";
regulator-name = "buck2";
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <900000>;
};
cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x0>;
cpu-supply = <&buck2>; // 通过 phandle 引用 buck2 节点
};
struct device_node *supply;
supply = of_parse_phandle(cpu_np, "cpu-supply", 0);
📌 什么是 phandle?
phandle是设备树中引用其他节点的一种机制,本质上是“指针”或“句柄”。- 使用
<&label>(如<&buck2>)来引用其他节点。 - 被引用的节点必须具备
phandle(自动分配或显示声明)
📌 cpu-supply 与 phandle 的关系?
cpu-supply是一个属性,值为一个 phandle,引用另一个节点。- 该机制用于连接设备之间的关系(如 CPU 电源由哪个 regulator 提供)
- 内核驱动通过
of_parse_phandle()等函数解析这些引用,实现设备之间的依赖关系绑定。
四、i.MX8MP EVK 实例分析:sound-wm8960 节点
我们选取 sound-wm8960 节点作为典型实例,结合驱动解析:
4.1 设备树定义
sound-wm8960 {
compatible = "fsl,imx-audio-wm8960"; // 用于驱动匹配
model = "wm8960-audio"; // 声卡名称
audio-cpu = <&sai3>; // 所使用的 SAI 接口节点
audio-codec = <&codec>; // 音频编解码器(如 wm8960)
audio-asrc = <&easrc>; // 可选的异步采样率转换器
hp-det-gpio = <&gpio4 28 0>; // 耳机插入检测 GPIO
audio-routing =
"Headphone Jack", "HP_L",
"Headphone Jack", "HP_R",
"Ext Spk", "SPK_LP",
"Ext Spk", "SPK_LN";
};
4.2 驱动匹配逻辑
static const struct of_device_id imx_audio_ids[] = {
{ .compatible = "fsl,imx-audio-wm8960" },
{ }
};
MODULE_DEVICE_TABLE(of, imx_audio_ids);
- 驱动使用
of_match_device()匹配compatible字段。 - 匹配成功后调用
probe()函数,完成设备初始化。
4.3 驱动中解析属性
np = pdev->dev.of_node;
ret = of_property_read_u32(np, "hp-det-gpio", &gpio);
dai_node = of_parse_phandle(np, "audio-cpu", 0);
pdev->dev.of_node指向与该驱动关联的设备树节点。- 使用
of_property_read_u32()读取整数类属性值。 - 使用
of_parse_phandle()查找引用的 SAI 接口节点。
五、设备树与驱动模型关联
每个设备树节点最终会注册为一个 struct device(如 platform 设备):
compatible→ 驱动中匹配表of_device_idof_match_device()进行匹配- 匹配成功 → 调用
platform_driver.probe() - 驱动中通过
pdev->dev.of_node获取节点结构 → 使用of_系列函数解析属性
这是一套标准的设备绑定流程,Yocto 项目和 NXP BSP 均遵循这一机制。
六、调试与验证技巧
6.1 查看设备树内容
root@imx8mp-lpddr4-evk:~# cat /proc/device-tree/sound-wm8960/compatible
fsl,imx-audio-wm8960r
root@imx8mp-lpddr4-evk:~# hexdump /proc/device-tree/sound-wm8960/audio-routing
0000000 6548 6461 6870 6e6f 2065 614a 6b63 4800
0000010 5f50 004c 6548 6461 6870 6e6f 2065 614a
0000020 6b63 4800 5f50 0052 7845 2074 7053 006b
0000030 5053 5f4b 504c 4500 7478 5320 6b70 5300
0000040 4b50 4c5f 004e 7845 2074 7053 006b 5053
0000050 5f4b 5052 4500 7478 5320 6b70 5300 4b50
0000060 525f 004e 494c 504e 5455 0031 694d 2063
0000070 614a 6b63 4c00 4e49 5550 3354 4d00 6369
0000080 4a20 6361 006b 694d 2063 614a 6b63 4d00
0000090 43
✅ compatible 和 audio-routing 都是节点属性,其中 audio-routing 为字符串对数组,hexdump 可显示其在 DTB 中的编码形式。
6.2 验证设备是否加载成功
dmesg | grep wm8960
root@imx8mp-lpddr4-evk:~# ls /sys/firmware/devicetree/base/sound-wm8960
audio-asrc audio-codec audio-cpu audio-routing compatible hp-det-gpio model name
✅ 表示 DTB 中的节点已经被成功加载进内核,设备树内容已映射到 /sys/firmware/devicetree/base/ 中。
6.3 验证驱动是否绑定
root@imx8mp-lpddr4-evk:~# ls -l /sys/bus/platform/devices/sound-wm8960/driver
lrwxrwxrwx 1 root root 0 Feb 28 02:06 /sys/bus/platform/devices/sound-wm8960/driver -> ../../../bus/platform/drivers/fsl-asoc-card
✅ 表示设备 sound-wm8960 已经成功绑定到了驱动 fsl-asoc-card,这是 NXP BSP 中用于处理 fsl,imx-audio-wm8960 类型声卡的标准 ASoC 驱动。
七、总结
| 内容 | 说明 |
|---|---|
| 设备树节点结构 | device_node 与 property 表示 |
of_ 函数 | 遍历、查询、读取属性的关键 API |
phandle 机制 | 节点间引用连接方式 |
| 实例分析 | sound-wm8960 展示典型节点结构与驱动匹配 |
| 驱动接口 | dev->of_node 是设备树与驱动交互核心 |
附:常用 of_ 函数表
| 函数名 | 功能 |
|---|---|
of_find_node_by_path | 按路径查找节点 |
of_property_read_u32 | 读取 32 位整数属性 |
of_parse_phandle | 解析 phandle 引用 |
of_match_node | 驱动匹配设备节点 |
of_get_property | 直接读取属性原始值 |
for_each_child_of_node | 遍历子节点 |
📺 视频讲解
B站相应的视屏教程:
📌 内核:博文+视频 - 备树深度解析:理论 + 实践全指南(含 of 函数与 i.MX8MP 实例)
敬请关注,记得标为原始粉丝。
- 设备树结构与
device_node实例讲解 of_函数用法详解(代码逐句分析)- 实例篇:分析
sound-wm8960节点与驱动流程 - 常见调试技巧与设备树疑难问题
欢迎收藏本系列文章📌,关注“嵌入式 Jerry”获取更多 Linux 设备树、驱动开发、Yocto BSP 教程。
👉 优快云 博客:嵌入式 Jerry
🎥 B站频道:嵌入式 Jerry
1995





