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 框架等操作。
4.2 MTK 平台 Sensor 驱动注册流程
在 MTK 平台上,Sensor 驱动的注册流程遵循一套标准化的步骤,从设备树解析到最终设备注册,环环相扣。以下是一个典型的 I2C 类型 Sensor 驱动的注册流程:
- 设备树(DTS)配置:在 DTS 文件中定义 Sensor 节点,指定 compatible 字符串、I2C 地址、中断引脚、电源域等资源。
- 驱动入口函数:调用
module_i2c_driver()或手动注册i2c_add_driver(),将驱动注册到 I2C 总线。 - 内核匹配:I2C 总线根据 DTS 中的
compatible属性与驱动的id_table进行匹配。 - probe() 调用:匹配成功后,内核调用驱动的
probe()函数,传入i2c_client结构体指针。 - 资源获取与初始化:在
probe()中,通过device_property_read_*解析 DTS 属性,获取 GPIO、中断号、电源控制等信息。 - 注册 IIO 或 Input 设备:根据 Sensor 类型,调用
devm_iio_device_register()或input_register_device()向内核注册。 - 使能中断与数据上报:注册中断处理函数,启动硬件数据采集,通过 IIO 或 Input 子系统上报数据。
MTK 平台特有步骤: 在 probe() 中通常需要调用
mtk_scp_sensor_register() 或 mtk_sspm_register() 将 Sensor 注册到 MTK 的 Sensor Hub(SCP/SSPM)管理框架中,以实现低功耗硬件轮询。
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()vsmodule_platform_driver()。
4.4 本章小结
本章深入剖析了 MTK Sensor 驱动所依赖的 Linux 内核驱动模型,详细描述了从 DTS 配置到 probe 完成的完整注册流程,并系统对比了 platform_driver 与 i2c_driver 的选择依据。掌握这些基础框架知识,是后续进行 Sensor 驱动移植与 Buffer 管理适配的基石。