文章基于max96722.c的驱动
休眠唤醒使用原因:摄像头在使用时需要低功耗的模式,不需要使用时需要休眠状态
休眠唤醒的调用在
static struct i2c_driver max96722_i2c_driver = {
.driver = {
.name = MAX96722_NAME,
.pm = &max96722_pm_ops,
.of_match_table = of_match_ptr(max96722_of_match),
},
.probe = &max96722_probe,
.remove = &max96722_remove,
.id_table = max96722_match_id,
};
其中.pm就是休眠调用的位置
往下延伸到
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct attribute_group **dev_groups;
const struct dev_pm_ops *pm;
void (*coredump) (struct device *dev);
struct driver_private *p;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
};
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
ANDROID_KABI_RESERVE(1);
};
可以看到会到这个结构体,调用时会使用到其中的回调函数
作用分别是:
1. int (*prepare)(struct device *dev);
作用简要说明就是在设备进入休眠或挂起之前,去做准备的函数,举一个简要例子
#include <linux/device.h> #include <linux/pm.h> static int my_device_prepare(struct device *dev) { /* 假设这是一个简单的字符设备驱动 */ struct my_device_data *data = dev_get_drvdata(dev); /* 关闭设备的中断 */ if (data->irq) disable_irq(data->irq); /* 停止数据传输或任何正在进行的操作 */ if (data->is_busy) { /* 假设有一个函数可以安全地中止当前操作 */ if (my_device_stop(data)) { pr_err("Failed to stop device before suspending\n"); return -EBUSY; } } /* 其他必要的清理或配置工作 */ return 0; /* 准备成功 */ } static const struct dev_pm_ops my_dev_pm_ops = { .prepare = my_device_prepare, /* 其他电源管理操作,如.suspend、resume等 */ }; static struct device_driver my_driver = { .name = "my_driver", .pm = &my_dev_pm_ops, /* 其他驱动定义 */ };
实际需求是看自己改动,这个prepare就是在为休眠做准备工作的
2. void (*complete)(struct device *dev);作用:设备从低功耗状态(如休眠或挂起)恢复到正常工作状态之后需要执行的回调函数。这个函数主要用于执行设备唤醒后的最终清理工作或恢复设备到完全工作状态所需的操作
例如:
static void my_device_complete(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 重新启用设备中断 */ if (data->irq) enable_irq(data->irq); /* 恢复设备到全功能状态,例如重新配置寄存器 */ my_device_resume_config(data); /* 如果之前保存了状态,现在恢复 */ if (data->saved_state) my_device_restore_state(data); /* 其他必要的恢复操作 */ // ... }
3. int (*suspend)(struct device *dev);
作用 在进入休眠前会做的操作,比如:
•停止数据传输:确保所有数据传输完成或被安全地中止。
•保存状态:如果需要,将设备的当前状态保存到持久存储中,以便恢复时使用。
•关闭电源:关闭设备的电源或调整到低功耗模式。
•释放资源:释放不再需要的系统资源,比如解除映射的内存区域。
static int my_device_suspend(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 确保没有正在进行的传输 */ if (my_device_is_busy(data)) { pr_warn("Device busy, cannot suspend.\n"); return -EBUSY; } /* 保存设备状态 */ if (my_device_save_state(data)) { pr_err("Failed to save device state.\n"); return -EIO; } /* 关闭设备电源或调整到低功耗模式 */ if (my_device_power_down(data)) { pr_err("Failed to power down device.\n"); return -EINVAL; } return 0; /* 操作成功 */ }
4 .int (*resume)(struct device *dev);
作用 在进入唤醒前会做的操作,主要目标是确保设备能够平滑、正确地恢复全部功能
比如:
•恢复电源管理设置:重新开启必要的电源、时钟、中断等。
•恢复硬件状态:根据之前保存的状态信息,恢复设备的硬件配置。
•重新初始化资源:重新初始化在休眠时释放或调整的系统资源。
•继续未完成的任务:如果有在休眠前暂停的任务,检查并决定是否需要继续执行。
static int my_device_resume(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 重新开启设备电源和时钟 */ if (my_device_power_up(data)) { pr_err("Failed to power up device.\n"); return -EINVAL; } /* 恢复中断和DMA设置 */ if (data->irq) enable_irq(data->irq); /* 根据之前保存的状态恢复设备配置 */ if (my_device_restore_state(data)) { pr_err("Failed to restore device state.\n"); return -EIO; } /* 重新初始化或恢复其他必要的资源 */ // ... return 0; /* 恢复成功 */ }
5. int (*freeze)(struct device *dev);
作用:负责设备在系统冻结过程中的操作
•操作内容:
•停止设备活动:停止所有正在进行的I/O操作,确保设备不再改变状态。
•记录必要状态:可能需要记录一些关键的设备状态信息,以便在解冻时恢复到精确的状态点。
•禁止中断:在某些情况下,可能需要临时禁止设备中断,以避免在冻结过程中状态的不一致
static int my_device_freeze(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 停止所有设备活动 */ if (my_device_stop_activities(data)) { pr_err("Failed to stop device activities during freeze.\n"); return -EBUSY; } /* 记录必要的设备状态,如果需要 */ // my_device_record_state_for_freeze(data); /* 禁用中断,视具体设备而定 */ // if (data->irq) // disable_irq_nosync(data->irq); return 0; /* 冻结操作成功 */ }
这里简要分析一下suspend跟freeze的区别(查啦一会,个人理解)
suspend是休眠,一般就是低功耗模式,会把当前状态信息保存到硬盘,cpu一般也会进入特定的低功耗模式,其他硬件下电,等待唤醒信号,
freeze是在特定模式下使用,这个也会保存状态信息到非易失性存储器,类似硬盘,cpu也会进入较低功耗的模式,但是会保证有一定的响应做系统维护
我前面的疑问就是这两个具体区别在哪,我大概理解是在系统升级或者维护的状态下会先做freeze的操作,保证做相关操作时数据不出问题,而休眠就是为啦节能,比较类似的点就是在进入这两个模式时都会对相关信息进行保存
6. int (*thaw)(struct device *dev);作用:解冻,对应冻结恢复到正常模式
功能说明
•恢复设备功能:在设备从冻结状态中解冻时,thaw函数负责恢复设备到其正常工作状态。这包括重新启用之前在freeze阶段被禁用的硬件功能,如中断、定时器、DMA通道等。
•恢复系统活动:如果冻结操作期间暂停了设备的任何后台任务或服务,thaw函数应确保这些任务和服务得以重新启动或继续执行。
•状态恢复:在一些情况下,如果设备在冻结前保存了特定的状态信息(尽管这通常在freeze阶段进行),thaw函数可能还需要负责根据之前保存的状态来恢复设备到一个已知的、连贯的工作状态。
•错误处理:与所有电源管理操作函数一样,thaw应当返回一个整型值表示操作的成功或失败。0表示操作成功,非零值表示有错误发生,这可能需要上层进行错误处理。
static int my_device_thaw(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 重新启用中断 */ if (data->irq) enable_irq_wake(data->irq); /* 恢复设备的正常工作模式 */ if (my_device_resume_activities(data)) { pr_err("Failed to resume device activities after thaw.\n"); return -EBUSY; } /* 重新启动任何在冻结期间暂停的后台任务或服务 */ // my_device_restart_services(data); return 0; /* 解冻操作成功 */ }
7. int (*poweroff)(struct device *dev);
作用:一般用于某一个设备的下电操作
操作内容:关闭设备的电源、释放硬件资源、关闭中断、停止所有活动的进程或任务、保存必要的状态(虽然这通常在suspend或shutdown操作中完成)。
static int my_device_poweroff(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 关闭设备电源 */ if (my_device_disable_power(data)) { pr_err("Failed to disable device power.\n"); return -EIO; } /* 清理和释放资源 */ my_device_cleanup_resources(data); /* 关闭中断 */ if (data->irq) disable_irq_nosync(data->irq); return 0; /* 关机操作成功 */ }
8. int (*restore)(struct device *dev);
作用:用于设备电源管理中的恢复操作。这个函数通常在设备从一个低功耗状态(比如挂起或休眠状态)恢复到完全工作状态时被调用。其主要职责是确保设备的所有功能和状态都能够正确、安全地恢复到休眠前的状态
操作内容:重新初始化硬件、恢复中断和时钟设置、重新配置设备到工作状态、从之前保存的状态中恢复设备的配置和数据。
static int my_device_restore(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 重新初始化硬件 */ if (my_device_init_after_resume(data)) { pr_err("Failed to initialize device after restore.\n"); return -EIO; } /* 恢复中断和时钟设置 */ if (data->irq) enable_irq(data->irq); /* 根据之前保存的状态配置设备 */ if (my_device_restore_state(data)) { pr_err("Failed to restore device state.\n"); return -EINVAL; } return 0; /* 恢复操作成功 */ }
9. int (*suspend_late)(struct device *dev);作用:其主要职责是在大多数设备已经进入低功耗状态之后,执行那些必须在最后阶段进行的、对系统状态敏感的挂起操作。相比于早期挂起(suspend)操作,suspend_late通常涉及那些依赖于系统整体状态已经大部分进入休眠准备的处理
功能说明
•目的:执行那些需要在其他设备挂起之后才能进行的挂起操作。这些操作可能是因为依赖关系、状态同步要求或是对系统整体状态敏感。
•操作内容:可能包括关闭最后一批设备的电源、执行系统状态的最终调整、或者是进行一些必须在绝大多数系统活动停止后才能进行的清理工作。
static int my_device_suspend_late(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 假设此阶段需要关闭与系统状态紧密相关的特定硬件模块 */ if (my_device_late_suspend_actions(data)) { pr_err("Failed to perform late suspend actions.\n"); return -EIO; } /* 执行任何基于系统全局状态的最后调整 */ // ... return 0; /* 操作成功 */ }
注意事项•suspend_late操作应当尽量精简,因为它执行的时机较晚,系统已接近全面休眠状态,过度复杂的操作可能会影响挂起的效率和成功率。•开发者需要仔细考虑哪些操作应该放在suspend与suspend_late之间,确保系统的稳定性和挂起操作的顺利进行。
10 int (*resume_early)(struct device *dev);
作用:这个函数在设备从休眠或挂起状态恢复(resume)的早期阶段被调用,其主要目的是在系统大部分设备还未完全恢复之前,执行那些对系统恢复至关重要的初步恢复操作。这通常涉及那些需要较早初始化以便其他设备或系统组件能够正常恢复的功能
功能说明
•目的:在系统从休眠状态开始恢复的最早阶段,执行设备的初步恢复操作,为后续的恢复流程打下基础。
•操作内容:可能包括初始化必要的硬件模块、设置关键的系统状态、恢复底层驱动或控制器的功能,这些操作对其他设备的恢复流程具有依赖性。
static int my_device_resume_early(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 假设需要在其他大部分设备恢复之前,先初始化核心硬件模块 */ if (my_device_early_resume_setup(data)) { pr_err("Failed to perform early resume setup.\n"); return -EIO; } /* 恢复必要的控制器或总线状态 */ // ... return 0; /* 操作成功 */ }
11. int (*freeze_late)(struct device *dev);
作用:用于执行设备在冻结(freeze)过程中的晚期操作。与suspend_late类似,freeze_late操作发生在设备冻结流程的较晚阶段,当大多数设备状态已经准备就绪,系统即将进入一个稳定、一致的冻结状态之前。
功能说明
•目的:在设备或系统即将完成冻结状态准备的最后阶段,执行那些必须在所有其他冻结操作之后进行的操作。这些操作可能是依赖于系统已经大部分冻结状态的,或者是对系统整体一致性有较高要求的步骤。
•操作内容:可能涉及关闭最后一批设备功能、执行系统状态的最终确认、或者是进行一些需要在系统进入完全冻结状态之前完成的特殊处理。
static int my_device_freeze_late(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 假设此阶段需要执行与系统全局冻结状态紧密相关的最后操作 */ if (my_device_late_freeze_actions(data)) { pr_err("Failed to perform late freeze actions.\n"); return -EIO; } /* 进行任何基于系统整体冻结状态的最终确认 */ // ... return 0; /* 操作成功 */ }
12. int (*thaw_early)(struct device *dev);
作用:用于设备从冻结状态恢复(thaw)的早期阶段。当系统或设备开始从冻结状态恢复,但在大多数设备和服务完全恢复之前,thaw_early函数就被调用,以执行必要的初步恢复操作,为后续的全面恢复奠定基础。
功能说明
•目的:在设备从冻结状态逐渐恢复的最早阶段,执行那些对整个系统或依赖于该设备的其他组件恢复至关重要的初始操作。
•操作内容:可能包括解除设备的冻结状态、初始化关键硬件模块、恢复必要的系统服务或控制器功能,这些操作有助于确保后续恢复流程的顺畅进行
static int my_device_thaw_early(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 假设需要在其他大部分设备或服务恢复之前,先解除设备的冻结状态 */ if (my_device_early_thaw_setup(data)) { pr_err("Failed to perform early thaw setup.\n"); return -EIO; } /* 恢复必要的硬件初始化或控制器状态 */ // ... return 0; /* 操作成功 */ }
12. int (*poweroff_late)(struct device *dev);
作用:用于设备在关机(power off)过程中的晚期操作。与suspend_late类似,poweroff_late函数在设备即将被彻底关闭电源之前被调用,执行那些必须在所有其他设备已经准备就绪关闭或已经关闭之后的操作。
功能说明
•目的:在设备即将永久关闭电源之前,执行最后的清理和关闭步骤。这些操作可能包括处理那些必须在所有其他设备都已关闭后才能进行的、对系统整体状态敏感的步骤。
•操作内容:可能包括释放最后的硬件资源、执行最后的系统状态清理、或者是进行一些在设备完全断电之前必须要做的特殊处理
static int my_device_poweroff_late(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 假设此阶段需要执行设备关闭前的最终清理工作 */ if (my_device_late_poweroff_cleanup(data)) { pr_err("Failed to perform late poweroff cleanup.\n"); return -EIO; } /* 执行任何基于系统即将完全关闭的最后步骤 */ // ... return 0; /* 操作成功 */ }
13 int (*restore_early)(struct device *dev);
作用:专门用于设备从低功耗状态恢复(如从休眠、挂起状态唤醒)的早期阶段。这个函数在设备恢复过程的最初阶段被调用,旨在执行那些需要在大多数设备和服务恢复之前完成的基本初始化和设置操作。
目的:在设备开始从低功耗状态完全恢复的最早阶段,执行那些对系统恢复的基础设施建设至关重要的操作,比如恢复基本的硬件初始化、启动必要的控制器或服务,为后续的恢复流程提供基础支持。
•操作内容:可能包括重置或初始化设备的硬件状态、恢复核心控制器功能、激活必要的时钟和电源管理设置等,这些都是其他设备和服务能够正确恢复的前提。
static int my_device_restore_early(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 假设需要在其他大部分设备和服务恢复之前,先恢复核心硬件和控制器状态 */ if (my_device_early_restore_setup(data)) { pr_err("Failed to perform early restore setup.\n"); return -EIO; } /* 初始化必要的时钟和电源管理配置 */ // ... return 0; /* 操作成功 */ }
14 int (*suspend_noirq)(struct device *dev);作用:专门用于设备挂起(suspend)过程中的无中断操作阶段。这个函数在系统挂起流程的特定点被调用,此时所有的中断处理都已经或即将被禁止,目的是为了执行那些在无中断环境下才能安全进行的挂起操作。
功能说明
•目的:在挂起过程中,当系统准备完全关闭中断处理之前,执行设备的特定挂起步骤。这一步骤对于那些需要在完全无中断环境中操作的硬件尤其重要,以避免数据损坏或状态不一致。
•操作内容:可能包括关闭硬件中断源、执行不希望被打断的硬件配置更改、或进行那些在中断上下文中执行可能会导致问题的操作。
static int my_device_suspend_noirq(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 禁用设备的硬件中断,确保无中断环境下操作的安全 */ if (my_device_disable_irqs(data)) { pr_err("Failed to disable interrupts during noirq suspend.\n"); return -EIO; } /* 执行需要在无中断环境下进行的硬件配置或状态保存 */ // ... return 0; /* 操作成功 */ }
在设备驱动程序中,如果设备需要在无中断环境中进行特定的挂起操作,就需要实现这样一个my_device_suspend_noirq函数,并将其指针赋给struct dev_pm_ops的.suspend_noirq成员。这样,系统挂起流程在中断被正式禁止之前,能够确保设备完成必要的安全挂起步骤。
注意:
•suspend_noirq操作应当快速且安全,因为在无中断模式下,系统的响应性和错误处理机制受到了限制。•此阶段的操作设计需谨慎,确保不会引发需要中断处理的异常情况。•与suspend、suspend_late等操作紧密配合,suspend_noirq帮助构建了设备挂起流程的完整性,特别是在确保数据一致性和硬件状态安全方面。
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);上面差不多
我看到的常用的以下3个:
int (*runtime_suspend)(struct device *dev);作用:顾名思义:系统运行时能进行休眠,用于执行设备的运行时挂起操作。与系统层面的挂起(如整个系统进入休眠状态)不同,运行时挂起是在设备驱动级别进行的,允许单独的设备在系统仍然运行时进入低功耗状态,以节省能源。这种挂起操作可以由系统自动触发(基于设备的空闲状态或其他电源管理策略)或由用户空间程序请求。
•目的:在设备未被频繁使用或系统需要节省能源时,将设备置于低功耗状态,而不影响系统其他部分的正常运行。
•操作内容:关闭设备的非必要功能,比如关闭硬件时钟、中断、DMA通道,以及执行任何必要的状态保存操作,以确保设备可以从这个低功耗状态恢复。
static int my_device_runtime_suspend(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 关闭设备功能和电源 */ if (my_device_power_down(data)) { pr_err("Failed to power down the device for runtime suspend.\n"); return -EIO; } /* 禁用中断,如果适用 */ if (data->irq) disable_irq(data->irq); return 0; /* 操作成功 */ }
int (*runtime_resume)(struct device *dev);作用:系统运行时间能进行唤醒,用于执行设备从运行时挂起状态恢复的操作。当系统检测到设备需要重新激活(例如,有新的输入/输出请求或设备被唤醒事件触发)时,这个函数会被调用,以恢复设备到正常工作状态。
功能说明
•目的:在设备需要从低功耗或挂起状态恢复到完全功能状态时,执行必要的硬件初始化、开启中断和恢复设备上下文的操作。
•操作内容:重新启用设备功能,如开启硬件时钟、中断、DMA通道,恢复之前挂起时保存的任何状态信息,确保设备可以继续接收和处理数据。
static int my_device_runtime_resume(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 重新启用设备功能和电源 */ if (my_device_power_up(data)) { pr_err("Failed to power up the device from runtime suspend.\n"); return -EIO; } /* 重新启用中断,如果之前被禁用 */ if (data->irq) enable_irq(data->irq); /* 恢复任何必要的设备配置或状态 */ // ... return 0; /* 操作成功 */ }
注意事项
•快速响应:运行时恢复操作应当设计得快速有效,以减少对设备响应时间和系统性能的影响。
•错误处理:恢复操作应当包含充分的错误检查和处理逻辑,以应对设备恢复过程中可能出现的各种异常情况。
•与挂起操作的匹配:确保runtime_resume与runtime_suspend操作逻辑相匹配,避免状态不一致或资源泄漏。
•性能考量:考虑到设备频繁进入和退出挂起状态的可能性,优化恢复操作以减少资源消耗和提高系统整体能效
int (*runtime_idle)(struct device *dev);作用:用于指示设备进入空闲状态。这个函数通常在系统检测到设备长时间没有活动,或者根据电源管理策略判断设备可以进一步降低功耗时被调用。它与runtime_suspend类似,但可能执行的是更为轻量级的节能措施,为设备进入更深的低功耗状态做准备或作为初步的节能反应。
功能说明
•目的:在设备进入一个较长时间的不活跃周期时,执行轻度的节能措施,可能是降低设备的运行频率、关闭部分非关键模块或进入更低功耗模式,但不完全等同于完全的挂起操作。
•操作内容:可能包括调整设备的工作模式、降低时钟频率、关闭不必要的功能单元等,以减少能源消耗,同时保持设备在短时间内能够快速响应。
static int my_device_runtime_idle(struct device *dev) { struct my_device_data *data = dev_get_drvdata(dev); /* 降低设备的工作频率 */ if (my_device_reduce_clocks(data)) { pr_warn("Failed to reduce clocks for idle mode.\n"); return -EIO; } /* 关闭非关键模块,如果适用 */ // ... return 0; /* 操作成功 */ }
注意事项
•与runtime_suspend的区分:runtime_idle操作通常比runtime_suspend更为轻量级,它不会导致设备完全脱离工作状态,而是采取初步的节能措施。
•自适应策略:根据设备特性和系统需求,合理设计runtime_idle操作,使之既能有效节能,又能在需要时快速响应。
•兼容性和测试:确保runtime_idle操作不会影响设备的正常功能,且与设备的硬件特性完全兼容。在部署前进行全面的测试,验证节能效果和设备恢复响应性能。
继续跟96722的驱动
static const struct dev_pm_ops max96722_pm_ops = { SET_RUNTIME_PM_OPS(
max96722_runtime_suspend, max96722_runtime_resume, NULL) };
#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
.runtime_suspend = suspend_fn, \
.runtime_resume = resume_fn, \
.runtime_idle = idle_fn,
static int max96722_runtime_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct max96722 *max96722 = v4l2_get_subdevdata(sd);
return __max96722_power_on(max96722);
}
static int max96722_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct max96722 *max96722 = v4l2_get_subdevdata(sd);
__max96722_power_off(max96722);
return 0;
}
可以看到使用的是使用到dev_pm_ops的runtime_suspend和runtime_resume,用于休眠和唤醒的部分,但是从哪开始设置,在哪调用到有点疑惑,继续看
probe中有使用到
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
1. pm_runtime_set_active(dev):•正如此前解释,这个调用会将设备的运行时状态设置为“活跃”(RPM_ACTIVE)。这意味着设备正在使用中或需要准备好立即响应操作。这个操作通常发生在设备即将或正在处理任务时,以确保设备处于能够完全响应的功率状态。
2. pm_runtime_enable(dev):•这个调用启用了设备的运行时电源管理功能。这意味着系统将开始监视该设备的活动状态,并根据设备的使用情况动态调整其电源状态,以节省能源。启用电源管理后,设备可以在没有活动时自动进入低功耗模式,而在需要时快速恢复到活动状态。这个调用通常在设备初始化时执行,以确保电源管理策略能够应用于该设备。
3. pm_runtime_idle(dev):•当调用这个函数时,系统会记录设备当前处于空闲状态。这意味着设备当前没有处理任务,也没有即将到来的任务需求。这个状态变化可能会触发电源管理策略,将设备进一步推向低功耗状态,如进入空闲模式或者更深层次的节能状态,以节省能源。调用时机通常是在设备完成任务且预计一段时间内不会有新任务时。
static inline int pm_runtime_set_active(struct device *dev)
{
return __pm_runtime_set_status(dev, RPM_ACTIVE);
}
int __pm_runtime_set_status(struct device *dev, unsigned int status)
{
struct device *parent = dev->parent;
bool notify_parent = false;
int error = 0;
if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
return -EINVAL;
spin_lock_irq(&dev->power.lock);
/*
* Prevent PM-runtime from being enabled for the device or return an
* error if it is enabled already and working.
*/
if (dev->power.runtime_error || dev->power.disable_depth)
dev->power.disable_depth++;
else
error = -EAGAIN;
spin_unlock_irq(&dev->power.lock);
if (error)
return error;
/*
* If the new status is RPM_ACTIVE, the suppliers can be activated
* upfront regardless of the current status, because next time
* rpm_put_suppliers() runs, the rpm_active refcounts of the links
* involved will be dropped down to one anyway.
*/
if (status == RPM_ACTIVE) {
int idx = device_links_read_lock();
error = rpm_get_suppliers(dev);
if (error)
status = RPM_SUSPENDED;
device_links_read_unlock(idx);
}
spin_lock_irq(&dev->power.lock);
if (dev->power.runtime_status == status || !parent)
goto out_set;
if (status == RPM_SUSPENDED) {
atomic_add_unless(&parent->power.child_count, -1, 0);
notify_parent = !parent->power.ignore_children;
} else {
spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING);
/*
* It is invalid to put an active child under a parent that is
* not active, has runtime PM enabled and the
* 'power.ignore_children' flag unset.
*/
if (!parent->power.disable_depth
&& !parent->power.ignore_children
&& parent->power.runtime_status != RPM_ACTIVE) {
dev_err(dev, "runtime PM trying to activate child device %s but parent (%s) is not active\n",
dev_name(dev),
dev_name(parent));
error = -EBUSY;
} else if (dev->power.runtime_status == RPM_SUSPENDED) {
atomic_inc(&parent->power.child_count);
}
spin_unlock(&parent->power.lock);
if (error) {
status = RPM_SUSPENDED;
goto out;
}
}
out_set:
__update_runtime_status(dev, status);
if (!error)
dev->power.runtime_error = 0;
out:
spin_unlock_irq(&dev->power.lock);
if (notify_parent)
pm_request_idle(parent);
if (status == RPM_SUSPENDED) {
int idx = device_links_read_lock();
rpm_put_suppliers(dev);
device_links_read_unlock(idx);
}
pm_runtime_enable(dev);
return error;
}
void pm_runtime_enable(struct device *dev)
{
unsigned long flags;
spin_lock_irqsave(&dev->power.lock, flags);
if (dev->power.disable_depth > 0) {
dev->power.disable_depth--;
/* About to enable runtime pm, set accounting_timestamp to now */
if (!dev->power.disable_depth)
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();
} else {
dev_warn(dev, "Unbalanced %s!\n", __func__);
}
WARN(!dev->power.disable_depth &&
dev->power.runtime_status == RPM_SUSPENDED &&
!dev->power.ignore_children &&
atomic_read(&dev->power.child_count) > 0,
"Enabling runtime PM for inactive device (%s) with active children\n",
dev_name(dev));
spin_unlock_irqrestore(&dev->power.lock, flags);
}
static inline int pm_runtime_idle(struct device *dev)
{
return __pm_runtime_idle(dev, 0);
}
int __pm_runtime_idle(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count)) {
trace_rpm_usage_rcuidle(dev, rpmflags);
return 0;
}
}
might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
spin_lock_irqsave(&dev->power.lock, flags);
retval = rpm_idle(dev, rpmflags);
spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
}
上面是probe的初始化设置
实际调用的是在摄像头使用时进行唤醒,用到以下函数
static int max96722_s_power(struct v4l2_subdev *sd, int on)
{
struct max96722 *max96722 = v4l2_get_subdevdata(sd);
struct i2c_client *client = max96722->client;
int ret = 0;
mutex_lock(&max96722->mutex);
/* If the power state is not modified - no work to do. */
if (max96722->power_on == !!on)
goto unlock_and_return;
if (on) {
ret = pm_runtime_get_sync(&client->dev);
if (ret < 0) {
pm_runtime_put_noidle(&client->dev);
goto unlock_and_return;
}
max96722->power_on = true;
} else {
pm_runtime_put(&client->dev);
max96722->power_on = false;
}
unlock_and_return:
mutex_unlock(&max96722->mutex);
return ret;
}
其中ret = pm_runtime_get_sync(&client->dev);很重要
static inline int pm_runtime_get_sync(struct device *dev)
{
return __pm_runtime_resume(dev, RPM_GET_PUT);
}
int __pm_runtime_resume(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe &&
dev->power.runtime_status != RPM_ACTIVE);
if (rpmflags & RPM_GET_PUT)
atomic_inc(&dev->power.usage_count);
spin_lock_irqsave(&dev->power.lock, flags);
retval = rpm_resume(dev, rpmflags);
spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
}
再跟进去函数就很大,就不贴上来啦,作用是:_pm_runtime_resume-运行时恢复操作的入口点。就是用来唤醒的作用
另外还有pm_runtime_put_noidle与pm_runtime_put的作用都是要准备不使用设备,但是有一点区别
•pm_runtime_put:告诉系统你完成了对设备的使用,系统在合适的时机(比如设备空闲一段时间)会考虑让设备进入低功耗状态。
•pm_runtime_put_noidle:也是告诉系统你目前不需要设备了,但是特别强调不要因为这次操作就把设备直接推入低功耗状态,即使它看起来空闲了,也要保持在比较容易唤醒的状态。
再贴一下实际去控制设备上下电的函数
static int max96722_runtime_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct max96722 *max96722 = v4l2_get_subdevdata(sd);
return __max96722_power_on(max96722);
}
static int max96722_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct max96722 *max96722 = v4l2_get_subdevdata(sd);
__max96722_power_off(max96722);
return 0;
}
问题还有一个点,就是 pm_runtime_get_sync怎么调到的.pm = &max96722_pm_ops的max96722_runtime_resume
搜索啦一下:
pm_runtime_get_sync函数与.pm = &max96722_pm_ops及其中定义的max96722_runtime_resume函数之间的调用关系,实际上是通过内核电源管理框架自动管理的,具体流程如下:
1. 初始化与注册:在您的设备驱动程序中,通过将.pm = &max96722_pm_ops设置到设备的驱动结构中,您实际上是告诉内核该设备支持运行时电源管理,并指定了用于挂起(max96722_runtime_suspend)和恢复(max96722_runtime_resume)的函数。当驱动程序注册时(比如通过i2c_add_driver(&max96722_i2c_driver);),这些电源管理操作就会与设备关联起来。2. 请求设备活动:当某个进程、中断或系统活动需要访问设备时,可能会调用pm_runtime_get_sync(struct device *dev)。这个函数的主要目的是确保设备处于活动状态,能够接受和处理请求。它通过增加设备的使用计数来表示设备被使用,并且如果设备当前不是活跃的(例如,因为之前被挂起了),pm_runtime_get_sync会阻塞等待,直到设备被成功恢复到活动状态。
3. 触发恢复操作:当pm_runtime_get_sync发现设备需要从低功耗状态恢复时,内核的电源管理子系统会自动调用max96722_runtime_resume函数。这个调用是由内核根据设备的状态和电源管理策略自动触发的,不需要用户代码直接调用max96722_runtime_resume。max96722_runtime_resume负责执行实际的硬件恢复操作,比如重新初始化必要的硬件寄存器、开启中断等,以确保设备能够正常响应接下来的读写操作。简而言之,通过在驱动程序中设置.pm = &max96722_pm_ops,您为设备注册了电源管理操作。当通过pm_runtime_get_sync请求设备时,内核会自动根据需要调用max96722_runtime_resume来恢复设备到活动状态,从而确保设备能够响应操作。这一系列过程是内核电源管理框架的一部分,对开发者来说是透明的,只需关注如何实现具体的挂起和恢复操作即可。
pm_runtime_put跟max96722_runtime_resume同理