Linux内核与驱动:13.从设备树到Platform平台总线

上两篇博客描述了一些设备树的基础知识和一些简单常见的设备树撰写,然而撰写设备树的很大一部分目的是用于代替平台总线Platform的 device 部分。

所以,这篇文章我们讲述这些设备树节点最终是怎么进入 Linux 驱动模型的,设备树是怎样代替 device.c 的。

1.从设备树源码的Platform的总体流程

整体流程可以描述为如下内容:

bash 复制代码
.dts / .dtsi
   ↓ 通过 dtc 编译
.dtb
   ↓ 由 bootloader 传给内核
内核早期解析 dtb
   ↓
展开成 device_node 树
   ↓
of_platform_populate()
   ↓
生成 platform_device
   ↓
挂到 platform bus
   ↓
platform_driver 根据 of_match_table 匹配 compatible
   ↓
调用 probe()
   ↓
驱动在 probe 中读取 reg / irq / gpio / clock 等资源

1.1 第一阶段

首先,我们通过DTC编译把 dts 设备树源文件编译为 dtb文件,如:

bash 复制代码
dtc -I dts -O dtb -o xxx.dtb xxx.dts

然后通过bootloader 把 DTB 传给内核:

.dtb 生成之后,接下来并不是驱动立刻去解析它,而是由 bootloader(例如 U-Boot)在启动内核时把这份 DTB 一起传进去。Linux 官方文档把这个过程称为 Linux 的 device tree usage model:bootloader 负责提供硬件描述,内核在启动初期接收并展开它。

1.2 第二阶段

内核收到 DTB 后,首先做的不是马上创建设备,而是先把二进制设备树展开成一棵内核内部可访问的树,也就是 device_node 树。

这棵树里的每个节点,都会对应一个 struct device_node。

也就是说,内核眼里的设备树,不再是文本,而是:一组互相连接的 device_node 节点对象。

所以你后面看到的很多 of_* 函数,本质上都是在对这棵 device_node 树做操作,比如:

  • 找节点
  • 读属性
  • 解析地址
  • 解析中断
  • 找父子关系

这也是为什么设备树相关接口都叫 of_*------它们是 OF(Open Firmware / Device Tree)框架下的一套 API。

1.3 第三阶段:platform_device的诞生

我们刚刚讲过设备树的很大一部分目的是替换平台总线中的device,所以设备树节点device_node是要转换成platform_device的。

而 platform 设备的创建,关键入口之一就是:

cpp 复制代码
of_platform_populate()

函数的作用可以理解为:

把设备树中的合适节点,转换成 platform_device,并挂到 platform 总线。

**注意:**然而内核并不会把设备树里的所有节点都转换成 platform_device!

of_platform_populate有着严格的准入规则:

  1. **根节点下的子节点:**只要含有 compatible 属性,通常会被转换为 platform_device。
  2. **特定的"总线"子节点:**带有 simple-bus、simple-mfd、isa、arm,amba-bus 等属性的节点,其子节点也会被递归地转化为 platform_device。(例如 RK3588 的 DTS 中,大量外设节点都在一个 compatible = "simple-bus" 的根节点之下)。

注意: 挂载在 I2C、SPI 等具体物理总线控制器下的子节点(如摄像头 Sensor),在这里不会被转换为 platform_device。它们会在 I2C/SPI 主机控制器驱动初始化时,由总线特定的代码(如 of_i2c_register_devices)解析,并转化为 i2c_client 或 spi_device。

1.4 第四阶段

当 platform_device 被注册到 platform bus 后,Linux 驱动模型就开始进行设备-驱动匹配。

在设备树场景下,匹配最关键的是:

1. 设备树节点里的 compatible

例如:

cpp 复制代码
compatible = "myvendor,myled";

2. 驱动里的 of_match_table

例如:

cpp 复制代码
static const struct of_device_id myled_of_match[] = {
    { .compatible = "myvendor,myled" },
    { }
};
MODULE_DEVICE_TABLE(of, myled_of_match);

然后平台驱动会这样注册:

cpp 复制代码
static struct platform_driver myled_driver = {
    .probe = myled_probe,
    .driver = {
        .name = "myled",
        .of_match_table = myled_of_match,
    },
};

这时 platform bus 会拿:

  • 节点的 compatible
  • 驱动的 of_match_table

进行匹配。匹配成功后,就会进入驱动的 probe()。

2.核心 of_ API 解析

当流程进入你编写的 probe 函数时,你需要从硬件设备中提取配置。此时,你需要大量使用 of_ 开头的 API(定义在 include/linux/of.h 等头文件中)。

所有获取属性的操作,都围绕着 platform_device->dev.of_node 展开。

1. 节点查找函数 (通常用于非标准获取,如查找引用节点)

  • of_find_node_by_path(const char *path): 通过绝对路径寻找节点(如 "/soc/gpio@fe740000")。
  • of_find_compatible_node(struct device_node *from, const char *type, const char *compatible): 通过 compatible 属性全局查找目标节点。
  • of_get_parent(const struct device_node *node): 获取当前节点的父节点。
相关推荐
振南的单片机世界2 小时前
电源、复位、时钟:单片机的“生存三要素”
单片机·嵌入式硬件
qeen872 小时前
【算法笔记】模拟与高精度加减乘除
c++·笔记·算法·高精度·模拟
纯氧゜2 小时前
文件名长度真相:别再被8.3规则误导了
linux·ai写作
txinyu的博客2 小时前
高并发内存池 - 简化版 tcmalloc
c++
Agent产品评测局2 小时前
企业流程异常处理自动化落地,预警处置全流程实现方案:2026企业“数字免疫系统”构建指南
运维·人工智能·ai·chatgpt·自动化
xlq223222 小时前
43.线程同步
大数据·linux
charlie1145141912 小时前
嵌入式Linux驱动开发指南02——内核空间基础与硬件访问
linux·运维·c语言·驱动开发·嵌入式硬件
少司府2 小时前
C++基础入门:内存管理
c语言·开发语言·c++·内存管理·delete·new·malloc
踏着七彩祥云的小丑2 小时前
嵌入式——小白入门
嵌入式硬件