设备树深度解析:理论 + 实践全指南(含 of 函数与 i.MX8MP 实例)


📍 本文适用于 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_nodestruct 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 设备):

  1. compatible → 驱动中匹配表 of_device_id
  2. of_match_device() 进行匹配
  3. 匹配成功 → 调用 platform_driver.probe()
  4. 驱动中通过 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_nodeproperty 表示
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 实例)
敬请关注,记得标为原始粉丝。


  1. 设备树结构与 device_node 实例讲解
  2. of_ 函数用法详解(代码逐句分析)
  3. 实例篇:分析 sound-wm8960 节点与驱动流程
  4. 常见调试技巧与设备树疑难问题

欢迎收藏本系列文章📌,关注“嵌入式 Jerry”获取更多 Linux 设备树、驱动开发、Yocto BSP 教程。

👉 优快云 博客:嵌入式 Jerry
🎥 B站频道:嵌入式 Jerry


评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值