Linux platform驱动开发与设备树资源适配 - 第2节

2.1 Platform 总线的作用与优势

在 Linux 内核中,Platform 总线(也称“虚拟平台总线”)是一种用于连接那些不依赖硬件发现机制(如 PCI、USB 的枚举)的设备的软件抽象层。它主要服务于 SoC 内部集成的外设、内存映射设备以及传统总线(如 I2C、SPI)上的控制器。

2.1.1 为什么需要 Platform 总线?

传统总线(如 PCI)具备硬件枚举能力:设备插入后自动分配资源并触发驱动匹配。然而,嵌入式 SoC 中的许多设备(如 GPIO 控制器、DMA 引擎、时钟控制器)不具备这种能力。Platform 总线提供了一种统一的机制,让驱动开发者能够以标准方式注册设备、匹配驱动,并传递硬件资源(如中断号、内存地址)。

核心作用:将“不可枚举”的设备纳入内核设备模型,实现驱动与设备的自动匹配与解耦。

2.1.2 Platform 总线的优势

优势 说明
统一设备模型 所有设备都挂载在同一个设备树上,便于电源管理、sysfs 导出和热插拔模拟。
资源传递标准化 通过 struct resource 传递内存、中断、DMA 通道,无需驱动自行解析硬件寄存器。
驱动与设备解耦 驱动只需关注匹配表和 probe 逻辑,设备信息可从代码、设备树或 ACPI 中获取。
跨平台可移植 同一驱动可运行于 x86、ARM、RISC-V 等架构,只需更换设备描述文件(如 DTS)。
热插拔模拟 支持通过 sysfs 手动绑定/解绑驱动,便于调试和运行时重配置。

2.2 Platform_device 与 Platform_driver 结构体

Platform 总线的核心数据结构是 platform_device 和 platform_driver。它们分别代表“设备”和“驱动”,通过总线上的匹配机制建立连接。

2.2.1 platform_device 结构体

该结构体描述一个平台设备的硬件属性,包括名称、资源列表、设备私有数据等。通常在板级文件或设备树中定义,内核启动时动态创建。

struct platform_device {
    const char  *name;          // 设备名称,用于驱动匹配
    int         id;             // 设备实例 ID,-1 表示单实例
    struct device   dev;        // 内嵌通用设备结构体
    u32         num_resources;  // 资源数量
    struct resource *resource;  // 指向资源数组的指针
    const struct platform_device_id *id_entry; // 匹配到的 ID 表项
    /* ... 其他字段 */
};

2.2.2 platform_driver 结构体

驱动开发者主要填充该结构体,实现 probe(探测)、remove(移除)、电源管理回调等操作。

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;    // 内嵌通用驱动结构体
    const struct platform_device_id *id_table; // 匹配 ID 表
};
关键匹配字段:驱动通过 driver.name 或 id_table 与设备的 name 字段进行字符串匹配。匹配成功后,内核调用驱动的 probe 函数。

2.2.3 匹配流程与生命周期

下图展示了 Platform 总线如何将设备与驱动连接起来:

Platform 总线设备-驱动匹配流程 Platform 总线匹配流程 platform_device_register() 设备注册到总线 platform_driver_register() 驱动注册到总线 platform_match() 名称 / ID 表 / 设备树匹配 probe() 调用 驱动初始化设备 匹配失败时,设备保持“孤儿”状态,驱动不会收到 probe 调用

2.3 Platform 总线与设备树的关联

设备树(Device Tree)是描述硬件拓扑的扁平数据结构,它替代了传统的板级文件(board-*.c),成为 ARM、RISC-V 等架构的主流硬件描述方式。Platform 总线与设备树的结合,实现了“硬件描述”与“驱动代码”的彻底分离。

2.3.1 设备树节点到 platform_device 的转换

内核在启动时解析设备树(.dtb 文件),为每个 compatible 属性匹配的节点创建一个 platform_device。转换过程如下:

  1. 解析 DTB:内核调用 unflatten_device_tree() 将二进制 DTB 展开为 device_node 树。
  2. 创建 platform_device:遍历 device_node,对每个非简单总线(如 I2C、SPI)的节点,调用 of_platform_device_create() 生成 platform_device。
  3. 资源提取:从节点 reg、interrupts、dmas 等属性中提取 struct resource,填充到 platform_device 中。
  4. 注册到总线:将生成的 platform_device 添加到 Platform 总线,触发匹配流程。

2.3.2 设备树中的典型节点示例

以下是一个 UART 控制器的设备树节点,它将被转换为一个 platform_device:

/ {
    soc {
        uart0: serial@10000000 {
            compatible = "vendor,uart-v1";
            reg = <0x10000000 0x1000>;
            interrupts = <33>;
            clocks = <&clk_uart>;
            status = "okay";
        };
    };
};
设备树属性 转换后的 platform_device 字段 说明
compatible = "vendor,uart-v1" name 或 id_table 匹配依据 驱动通过 of_match_table 匹配该字符串
reg = <0x10000000 0x1000> resource[0] (IORESOURCE_MEM) 指定寄存器基地址和范围
interrupts = <33> resource[1] (IORESOURCE_IRQ) 指定中断号
clocks 通过 clk API 获取,不直接映射为 resource 驱动在 probe 中调用 clk_get() 获取

2.3.3 驱动中的设备树匹配

驱动开发者使用 of_match_table 来声明支持的设备树兼容性字符串:

static const struct of_device_id uart_of_match[] = {
    { .compatible = "vendor,uart-v1", },
    { .compatible = "vendor,uart-v2", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uart_of_match);

static struct platform_driver uart_driver = {
    .probe  = uart_probe,
    .remove = uart_remove,
    .driver = {
        .name = "vendor-uart",
        .of_match_table = uart_of_match,
    },
};
module_platform_driver(uart_driver);
常见陷阱:如果驱动只填写了 driver.name 而未提供 of_match_table,则设备树节点必须包含 device_type = "vendor-uart" 才能匹配。现代内核推荐始终使用 of_match_table 进行设备树匹配。

2.3.4 设备树与 Platform 总线的协作关系

下图展示了从设备树源文件到驱动 probe 的完整数据流:

设备树到 Platform 驱动的数据流 设备树 → Platform 设备 → 驱动 Probe 数据流 DTS 源文件 *.dts / *.dtsi DTC 编译 *.dtb 二进制 unflatten device_node 树 of_platform platform_device platform_match() compatible 属性匹配 probe() 执行 驱动获取资源并初始化

2.4 本章小结

  • Platform 总线是 Linux 内核为“不可枚举”设备提供的虚拟总线,实现了设备与驱动的标准化匹配。
  • platform_device 描述硬件资源(内存、中断),platform_driver 实现驱动逻辑,两者通过名称或 ID 表匹配。
  • 设备树是硬件描述的标准语言,内核将其节点自动转换为 platform_device,驱动通过 of_match_table 匹配兼容性字符串。
  • 三者结合实现了“一处驱动,多平台复用”的嵌入式开发最佳实践。