Platform Camera驱动框架下的电源管理设计工程 - 第3节
3. Platform Camera驱动模型
在 Platform Camera 驱动框架中,platform_driver 是驱动与内核平台子系统之间的核心绑定接口。该结构体定义于 <linux/platform_device.h>,驱动开发者通过填充其回调函数来实现设备初始化、电源管理、热插拔等操作。
| 成员 | 类型 | 说明 |
|---|---|---|
| probe | int (*probe)(struct platform_device *) |
设备匹配成功后调用,完成硬件初始化、资源申请、电源域注册 |
| remove | int (*remove)(struct platform_device *) |
设备移除或驱动卸载时调用,执行逆序清理与电源关闭 |
| shutdown | void (*shutdown)(struct platform_device *) |
系统关机或重启时调用,确保 Camera 进入安全断电状态 |
| driver | struct device_driver |
内嵌通用驱动结构,包含 pm 指针用于挂载电源管理操作集 |
| id_table | const struct platform_device_id * |
传统设备 ID 匹配表,用于非 Device Tree 平台 |
| driver.of_match_table | const struct of_device_id * |
Device Tree 兼容性匹配表,现代 Camera 驱动首选方式 |
设计要点: Camera 驱动通常将
pm 指针指向 SET_SYSTEM_SLEEP_PM_OPS 或 SET_RUNTIME_PM_OPS 宏展开的 dev_pm_ops 结构体,实现系统挂起/恢复与运行时电源管理。
3.1.1 典型 platform_driver 定义示例
static const struct dev_pm_ops camera_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(camera_suspend, camera_resume)
SET_RUNTIME_PM_OPS(camera_runtime_suspend, camera_runtime_resume, NULL)
};
static const struct of_device_id camera_of_match[] = {
{ .compatible = "vendor,platform-camera" },
{ /* sentinel */ }
};
static struct platform_driver camera_platform_driver = {
.probe = camera_probe,
.remove = camera_remove,
.shutdown = camera_shutdown,
.driver = {
.name = "platform_camera",
.of_match_table = camera_of_match,
.pm = &camera_pm_ops,
},
};
module_platform_driver(camera_platform_driver);
3.2 probe / remove 流程与电源管理集成
probe() 是 Camera 驱动的“生命起点”,remove() 是“安全终点”。电源管理接口必须在这两个函数中完成电源域、时钟、稳压器的注册与注销,否则系统挂起/恢复时会出现设备状态不一致。
3.2.1 probe 流程中的电源管理步骤
- 解析设备树:获取
regulator、clocks、power-domains属性。 - 获取电源资源:调用
devm_regulator_get()、devm_clk_get()获取稳压器和时钟句柄。 - 使能电源域:通过
pm_runtime_enable()启用运行时 PM,并调用pm_runtime_get_sync()确保设备上电。 - 硬件初始化:配置 MIPI CSI 接口、I2C 寄存器、复位序列。
- 注册子设备:调用
v4l2_device_register()、media_device_register()等。 - 注册中断:
devm_request_threaded_irq()绑定 Camera 帧同步中断。
最佳实践: 使用
devm_ 系列 API(Managed Device Resources)可以自动处理资源释放,避免 remove() 中遗漏电源关闭操作。
3.2.2 remove 流程中的电源管理步骤
- 停止数据流:关闭视频流、停止 DMA 传输。
- 注销子设备:
v4l2_device_unregister()、media_device_unregister()。 - 释放中断:
devm_free_irq()(通常由 devm 自动处理)。 - 关闭电源:
pm_runtime_put_sync()减少引用计数,触发运行时挂起。 - 禁用电源域:
pm_runtime_disable()关闭运行时 PM。 - 释放稳压器/时钟:
regulator_disable()、clk_disable_unprepare()。
3.3 电源管理接口设计:dev_pm_ops 与运行时 PM
Platform Camera 驱动的电源管理接口通过 dev_pm_ops 结构体挂载到 platform_driver.driver.pm 指针。该结构体包含系统级休眠和运行时动态电源管理两套回调。
3.3.1 dev_pm_ops 核心回调
| 回调函数 | 触发场景 | Camera 驱动典型操作 |
|---|---|---|
| .suspend | 系统进入 Suspend-to-RAM/Standby | 停止 MIPI 时钟、保存寄存器上下文、关闭 ISP 电源 |
| .resume | 系统从挂起状态恢复 | 恢复寄存器上下文、重新初始化 MIPI PHY、启动时钟 |
| .runtime_suspend | 设备引用计数降为 0(无用户打开) | 关闭 Sensor 供电、关闭 CSI 时钟、进入低功耗模式 |
| .runtime_resume | 设备引用计数从 0 变为 1(用户打开) | 重新上电、初始化寄存器、恢复 MIPI 链路 |
| .freeze | 休眠镜像创建(hibernation) | 同 suspend,但不需要恢复硬件状态 |
| .thaw | 休眠镜像恢复后 | 同 resume,重新初始化硬件 |
3.3.2 运行时 PM 接口设计模式
static int camera_runtime_suspend(struct device *dev)
{
struct camera_device *cam = dev_get_drvdata(dev);
// 1. 关闭 MCLK 主时钟
clk_disable_unprepare(cam->mclk);
// 2. 关闭 Sensor 稳压器
regulator_disable(cam->sensor_vdd);
// 3. 关闭 CSI 接口电源
regulator_disable(cam->csi_io_vdd);
dev_dbg(dev, "Camera runtime suspended\n");
return 0;
}
static int camera_runtime_resume(struct device *dev)
{
struct camera_device *cam = dev_get_drvdata(dev);
// 1. 使能 CSI 接口电源
regulator_enable(cam->csi_io_vdd);
// 2. 使能 Sensor 稳压器
regulator_enable(cam->sensor_vdd);
// 3. 使能 MCLK 主时钟
clk_prepare_enable(cam->mclk);
// 4. 重新初始化 Sensor 寄存器
camera_sensor_init(cam);
dev_dbg(dev, "Camera runtime resumed\n");
return 0;
}
static const struct dev_pm_ops camera_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(camera_suspend, camera_resume)
SET_RUNTIME_PM_OPS(camera_runtime_suspend, camera_runtime_resume, NULL)
};
注意: 运行时 PM 回调中不能调用
msleep() 或任何可能引起调度的函数,因为运行时 PM 可能在原子上下文中执行。如果硬件需要等待稳定时间,应使用 usleep_range() 或 mdelay()。
3.3.3 电源管理接口与 probe/remove 的协作
电源管理接口并非独立存在,它必须与 probe() 和 remove() 中的资源管理紧密配合:
- probe() 中调用
pm_runtime_enable():启用运行时 PM 框架,允许后续自动挂起/恢复。 - probe() 中调用
pm_runtime_get_sync():确保设备在初始化期间处于上电状态,防止硬件访问失败。 - remove() 中调用
pm_runtime_put_sync():释放 probe 时增加的引用计数,触发运行时挂起。 - remove() 中调用
pm_runtime_disable():禁用运行时 PM,防止在驱动卸载后仍有电源操作。 - 系统挂起时:内核会先调用
runtime_suspend(如果设备未挂起),再调用suspend回调。 - 系统恢复时:内核先调用
resume回调,再调用runtime_resume(如果设备之前处于运行时挂起状态)。
3.4 电源管理接口设计中的关键避坑
常见错误 1: probe() 中调用
pm_runtime_get_sync() 后忘记在 remove() 中调用 pm_runtime_put_sync(),导致设备引用计数泄漏,系统永远无法进入运行时挂起状态。
常见错误 2: 在
runtime_suspend 回调中调用 msleep() 或 mutex_lock(),导致内核调度器警告或死锁。应使用 usleep_range() 和 spin_lock()。
调试技巧: 通过
/sys/kernel/debug/pm_runtime/ 下的文件可以查看每个设备的运行时 PM 状态、引用计数和挂起/恢复次数,是排查电源管理问题的第一入口。
3.4.1 电源管理接口设计检查清单
| 检查项 | 要求 | 验证方法 |
|---|---|---|
| pm_runtime_enable 调用位置 | probe() 中,硬件资源获取之后 | 代码审查 |
| pm_runtime_disable 调用位置 | remove() 中,所有资源释放之前 | 代码审查 |
| 引用计数对称性 | get/put 成对出现 | 调试文件 /sys/kernel/debug/pm_runtime/ 检查 usage_count |
| 原子上下文合规 | runtime_suspend/resume 中无阻塞调用 | 动态检测 + lockdep 验证 |
| 系统休眠与运行时 PM 互斥 | suspend/resume 回调中处理设备状态保存/恢复 | 循环挂起/恢复 1000 次测试 |
| 电源域依赖顺序 | 先关 Sensor 电源,再关 CSI 接口电源 | 示波器测量电源轨时序 |