4. MTK Sensor Driver 框架:Linux 内核驱动模型、MTK 平台 Sensor 驱动注册流程、platform_driver 与 i2c_driver 的选择

4.1 Linux 内核驱动模型基础

MTK 平台的 Sensor 驱动完全遵循 Linux 内核的 设备驱动模型(Device Driver Model)。该模型的核心思想是将 设备(Device)、驱动(Driver) 和 总线(Bus) 三者解耦,通过内核的 driver core 自动完成匹配与绑定。

核心三要素:
  • bus_type:定义设备与驱动的匹配规则(如 I2C 总线、SPI 总线、Platform 虚拟总线)。
  • device_driver:描述驱动能力,包含 probe()、remove() 等回调。
  • device:描述硬件实例,包含资源信息(如中断号、寄存器地址、GPIO)。

当驱动注册时,内核遍历挂载在对应总线上的设备列表,调用总线的 match() 函数进行匹配。匹配成功后,内核调用驱动的 probe() 函数,完成硬件初始化、注册输入子系统或 IIO 框架等操作。

Linux 内核驱动模型核心架构 Linux 内核驱动模型核心架构 bus_type (I2C / Platform) device (Sensor 实例) device_driver (probe/remove) match() probe() 成功 → 驱动绑定设备

4.2 MTK 平台 Sensor 驱动注册流程

在 MTK 平台上,Sensor 驱动的注册流程遵循一套标准化的步骤,从设备树解析到最终设备注册,环环相扣。以下是一个典型的 I2C 类型 Sensor 驱动的注册流程:

  1. 设备树(DTS)配置:在 DTS 文件中定义 Sensor 节点,指定 compatible 字符串、I2C 地址、中断引脚、电源域等资源。
  2. 驱动入口函数:调用 module_i2c_driver() 或手动注册 i2c_add_driver(),将驱动注册到 I2C 总线。
  3. 内核匹配:I2C 总线根据 DTS 中的 compatible 属性与驱动的 id_table 进行匹配。
  4. probe() 调用:匹配成功后,内核调用驱动的 probe() 函数,传入 i2c_client 结构体指针。
  5. 资源获取与初始化:在 probe() 中,通过 device_property_read_* 解析 DTS 属性,获取 GPIO、中断号、电源控制等信息。
  6. 注册 IIO 或 Input 设备:根据 Sensor 类型,调用 devm_iio_device_register() 或 input_register_device() 向内核注册。
  7. 使能中断与数据上报:注册中断处理函数,启动硬件数据采集,通过 IIO 或 Input 子系统上报数据。
MTK 平台特有步骤: 在 probe() 中通常需要调用 mtk_scp_sensor_register() 或 mtk_sspm_register() 将 Sensor 注册到 MTK 的 Sensor Hub(SCP/SSPM)管理框架中,以实现低功耗硬件轮询。
MTK Sensor 驱动注册流程 MTK Sensor 驱动注册流程 ① DTS 配置 Sensor 节点 ② module_i2c_driver() 注册驱动 ③ 内核匹配 compatible 字符串 ④ probe() 被调用 ⑤ 解析 DTS 资源 (GPIO/IRQ/电源) ⑥ 注册 IIO / Input / MTK Sensor Hub ⑦ 使能中断 → 数据上报

4.3 platform_driver 与 i2c_driver 的选择

在 MTK Sensor 驱动开发中,选择 platform_driver 还是 i2c_driver 取决于 Sensor 的物理连接方式以及是否涉及虚拟设备。下表列出了两者的核心差异与适用场景:

对比维度 i2c_driver platform_driver
总线类型 I2C 物理总线 Platform 虚拟总线
设备来源 DTS 中挂载在 I2C 控制器下的节点 DTS 中根节点或特定 compatible 节点
匹配方式 of_match_table + id_table of_match_table
典型场景 物理 I2C Sensor(如加速度计、陀螺仪) 虚拟 Sensor、MISC 设备、电源管理 IC
probe 参数 struct i2c_client *client struct platform_device *pdev
资源获取 i2c_client->irq、i2c_read/write platform_get_resource()、devm_*
MTK 特殊用法 直接与 Sensor 芯片通信 用于注册 Sensor Hub 管理驱动或虚拟 Sensor

4.3.1 如何选择?

  • 物理 I2C Sensor:必须使用 i2c_driver。因为 Sensor 芯片物理上连接在 I2C 总线上,需要通过 I2C 协议读写寄存器。
  • 虚拟 Sensor 或中间层驱动:使用 platform_driver。例如,MTK 的 Sensor Hub 管理驱动、数据融合算法驱动等,它们不直接操作硬件 I2C,而是通过内存或 IPC 与底层通信。
  • 混合场景:某些 MTK 平台会使用 platform_driver 封装一个 Sensor 控制器,内部再通过 i2c_client 操作具体 Sensor。这种设计常见于多合一 Sensor 模块。
常见错误: 在 DTS 中将 I2C Sensor 节点错误地放在根节点下,导致内核无法通过 I2C 总线匹配驱动。务必确认 Sensor 节点是 I2C 控制器子节点,且 compatible 字符串与驱动 id_table 完全一致。

4.3.2 代码结构对比

以下分别展示 i2c_driver 和 platform_driver 的典型注册代码片段:

// i2c_driver 示例
static const struct i2c_device_id sensor_i2c_id[] = {
    { "bmi160", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, sensor_i2c_id);

static const struct of_device_id sensor_of_match[] = {
    { .compatible = "bosch,bmi160" },
    { }
};
MODULE_DEVICE_TABLE(of, sensor_of_match);

static struct i2c_driver sensor_i2c_driver = {
    .probe    = sensor_probe,
    .remove   = sensor_remove,
    .id_table = sensor_i2c_id,
    .driver   = {
        .name = "bmi160",
        .of_match_table = sensor_of_match,
    },
};
module_i2c_driver(sensor_i2c_driver);
// platform_driver 示例
static const struct of_device_id sensor_plat_of_match[] = {
    { .compatible = "mediatek,sensor_hub" },
    { }
};
MODULE_DEVICE_TABLE(of, sensor_plat_of_match);

static struct platform_driver sensor_plat_driver = {
    .probe  = sensor_hub_probe,
    .remove = sensor_hub_remove,
    .driver = {
        .name = "sensor_hub",
        .of_match_table = sensor_plat_of_match,
    },
};
module_platform_driver(sensor_plat_driver);
关键区别:
  • i2c_driver 需要定义 id_table,用于非 DTS 环境下的传统匹配。
  • platform_driver 不需要 id_table,完全依赖 of_match_table。
  • 注册宏不同:module_i2c_driver() vs module_platform_driver()。

4.4 本章小结

本章深入剖析了 MTK Sensor 驱动所依赖的 Linux 内核驱动模型,详细描述了从 DTS 配置到 probe 完成的完整注册流程,并系统对比了 platform_driver 与 i2c_driver 的选择依据。掌握这些基础框架知识,是后续进行 Sensor 驱动移植与 Buffer 管理适配的基石。