上两篇博客描述了一些设备树的基础知识和一些简单常见的设备树撰写然而撰写设备树的很大一部分目的是用于代替平台总线Platform的 device 部分。所以这篇文章我们讲述这些设备树节点最终是怎么进入 Linux 驱动模型的设备树是怎样代替 device.c 的。1.从设备树源码的Platform的总体流程整体流程可以描述为如下内容.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文件如dtc -I dts -O dtb -o xxx.dtb xxx.dts然后通过bootloader 把 DTB 传给内核.dtb生成之后接下来并不是驱动立刻去解析它而是由 bootloader例如 U-Boot在启动内核时把这份 DTB 一起传进去。Linux 官方文档把这个过程称为 Linux 的 device tree usage modelbootloader 负责提供硬件描述内核在启动初期接收并展开它。1.2 第二阶段内核收到 DTB 后首先做的不是马上创建设备而是先把二进制设备树展开成一棵内核内部可访问的树也就是 device_node 树。这棵树里的每个节点都会对应一个 struct device_node。也就是说内核眼里的设备树不再是文本而是一组互相连接的device_node节点对象。所以你后面看到的很多 of_* 函数本质上都是在对这棵 device_node 树做操作比如找节点读属性解析地址解析中断找父子关系这也是为什么设备树相关接口都叫 of_*——它们是 OFOpen Firmware / Device Tree框架下的一套 API。1.3 第三阶段platform_device的诞生我们刚刚讲过设备树的很大一部分目的是替换平台总线中的device所以设备树节点device_node是要转换成platform_device的。而 platform 设备的创建关键入口之一就是of_platform_populate()函数的作用可以理解为把设备树中的合适节点转换成 platform_device并挂到 platform 总线。注意然而内核并不会把设备树里的所有节点都转换成 platform_deviceof_platform_populate有着严格的准入规则根节点下的子节点只要含有 compatible 属性通常会被转换为 platform_device。特定的“总线”子节点带有 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例如compatible myvendor,myled;2. 驱动里的 of_match_table例如static const struct of_device_id myled_of_match[] { { .compatible myvendor,myled }, { } }; MODULE_DEVICE_TABLE(of, myled_of_match);然后平台驱动会这样注册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/gpiofe740000。of_find_compatible_node(struct device_node *from, const char *type, const char *compatible):通过 compatible 属性全局查找目标节点。of_get_parent(const struct device_node *node):获取当前节点的父节点。