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);
platform_driver 结构体与电源管理接口关系 platform_driver .probe() .remove() .shutdown() .driver.pm → dev_pm_ops 设备匹配与资源申请 电源域注册 & 时钟使能 逆序清理 & 电源关闭 系统挂起/恢复回调 of_match_table id_table driver.name

3.2 probe / remove 流程与电源管理集成

probe() 是 Camera 驱动的“生命起点”,remove() 是“安全终点”。电源管理接口必须在这两个函数中完成电源域、时钟、稳压器的注册与注销,否则系统挂起/恢复时会出现设备状态不一致。

3.2.1 probe 流程中的电源管理步骤

  1. 解析设备树:获取 regulator、clocks、power-domains 属性。
  2. 获取电源资源:调用 devm_regulator_get()、devm_clk_get() 获取稳压器和时钟句柄。
  3. 使能电源域:通过 pm_runtime_enable() 启用运行时 PM,并调用 pm_runtime_get_sync() 确保设备上电。
  4. 硬件初始化:配置 MIPI CSI 接口、I2C 寄存器、复位序列。
  5. 注册子设备:调用 v4l2_device_register()、media_device_register() 等。
  6. 注册中断:devm_request_threaded_irq() 绑定 Camera 帧同步中断。
最佳实践: 使用 devm_ 系列 API(Managed Device Resources)可以自动处理资源释放,避免 remove() 中遗漏电源关闭操作。

3.2.2 remove 流程中的电源管理步骤

  1. 停止数据流:关闭视频流、停止 DMA 传输。
  2. 注销子设备:v4l2_device_unregister()、media_device_unregister()。
  3. 释放中断:devm_free_irq()(通常由 devm 自动处理)。
  4. 关闭电源:pm_runtime_put_sync() 减少引用计数,触发运行时挂起。
  5. 禁用电源域:pm_runtime_disable() 关闭运行时 PM。
  6. 释放稳压器/时钟:regulator_disable()、clk_disable_unprepare()。
probe / remove 流程中的电源管理操作 probe() 入口 解析设备树电源属性 获取 regulator / clock pm_runtime_enable() pm_runtime_get_sync() 硬件初始化 & 注册 V4L2 probe() 成功返回 0 remove() 入口 停止视频流 & DMA 注销 V4L2 / Media 设备 pm_runtime_put_sync() pm_runtime_disable() 关闭 regulator / clock remove() 返回 0

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(如果设备之前处于运行时挂起状态)。
电源管理接口与 probe/remove 协作时序 probe() pm_runtime_enable pm_runtime_get_sync 硬件初始化 运行时 PM 生命周期 引用计数 = 1 引用计数 = 0 → runtime_suspend 引用计数 = 1 → runtime_resume remove() 停止数据流 pm_runtime_put_sync pm_runtime_disable

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 接口电源 示波器测量电源轨时序