休眠过程
系统休眠命令:echo mem > sys/power/state cat此节点获取休眠方式
state节点写入信息流程
state_store(kernel-4.19\kernel\power\main.c)
pm_suspend(kernel-4.19\kernel\power\suspend.c)
enter_state(kernel-4.19\kernel\power\suspend.c)
suspend_prepare(kernel-4.19\kernel\power\suspend.c)
__pm_notifier_call_chain(kernel-4.19\kernel\power\suspend.c) //通知所有关心休眠的驱动
suspend_freeze_processes(kernel-4.19\kernel\power\suspend.c) //冻结所有应用程序
suspend_devices_and_enter(kernel-4.19\kernel\power\suspend.c)//让设备进入休眠状态
platform_suspend_begin
suspend_ops->begin //如果平台相关的代码有相关的begin函数就调用此函数,注1
suspend_set_ops(kernel-4.19\kernel\power\suspend.c)
suspend_console(kernel-4.19\kernel\power\suspend.c)//控制台进行休眠
dpm_suspend_start(kernel-4.19\drivers\base\power\main.c)
dpm_prepare(kernel-4.19\drivers\base\power\main.c) //让各类设备休眠
遍历整个链表dpm_list,调用每个设备的device_prepare函数,执行完后将列表成员放入dpm_prepared_list链表中
对于该设备他会调用dev->pm_domain->ops.prepare或dev->type->pm->prepare或dev->class->pm->prepare或dev->bus->pm->prepare
遍历整个dpm_prepared_list链表,每个设备都调用device_suspend函数,执行完成将链表成员放入dpm_suspended_list链表中
__device_suspend
对于该设备调用dev->pm_domain->ops.suspend或dev->type->pm.suspend或dev->class->pm.suspend或dev->bus->pm.suspend
suspend_enter
platform_suspend_prepare
suspend_ops->prepare()及slp_suspend_ops_prepare函数
dpm_suspend_late(kernel-4.19\drivers\base\power\main.c)
遍历整个链表dpm_suspended_list,调用每个设备device_suspend_late函数
dpm_subsys_suspend_late_cb
对于该设备调用dev->pm_domain->ops.suspend_late或dev->type->pm.suspend_late或dev->class->pm.suspend_late或dev->bus->pm.suspend_late
platform_suspend_prepare_late
s2idle_ops->prepare //根据平台是否注册,注册则进行调用
dpm_suspend_noirq(kernel-4.19\drivers\base\power\main.c) //关闭IRQ
dpm_noirq_begin
cpuidle_pause //多核处理器系统中暂停CPU,进入低功耗状态
device_wakeup_arm_wake_irqs //设备唤醒源(wakeup source)启用唤醒中断,唤醒源方可唤醒cpu
suspend_device_irqs//暂停系统中所有设备的中断请求(IRQ)。
dpm_noirq_suspend_devices
遍历dpm_late_early_list数组,调用每一个设备device_suspend_noirq
__device_suspend_noirq
dpm_subsys_suspend_noirq_cb
对于该设备调用dev->pm_domain->ops.suspend_noirq或dev->type->pm.suspend_noirq或dev->class->pm.suspend_noirq或dev->bus->pm.suspend_noirq
platform_suspend_prepare_noirq //预准备关闭平台IRP
disable_nonboot_cpus //在系统休眠过程中关闭非引导CPU(Non-Boot CPUs),以避免在休眠期间产生不必要的功耗。注2
arch_suspend_disable_irqs //关闭本地中断
syscore_suspend //系统核心休眠
suspend_ops->enter //调用到平台注册的enter函数(slp_suspend_ops_enter),进行真正的休眠(kernel-4.19\drivers\misc\mediatek\base\power\spm_v4\mtk_sleep.c),slp_suspend_ops_enter函数会在休眠前记录唤醒时的返回函数地址
@注1:
suspend_ops函数的赋值
mt_spm_init(kernel-4.19\drivers\misc\mediatek\base\power\spm_v4\mtk_spm_init.c)
slp_module_init(kernel-4.19\drivers\misc\mediatek\base\power\spm_v4\mtk_spm_init.c)
suspend_set_ops(kernel-4.19\kernel\power\suspend.c) //对suspend_ops进行赋值
static const struct platform_suspend_ops slp_suspend_ops = {
.valid = slp_suspend_ops_valid,
.begin = slp_suspend_ops_begin,
.prepare = slp_suspend_ops_prepare,
.enter = slp_suspend_ops_enter,
.finish = slp_suspend_ops_finish,
.end = slp_suspend_ops_end,
};
@注2:
在多核处理器系统中,CPU分为引导CPU(Boot CPU)和非引导CPU(Non-Boot CPU)。引导CPU通常是第一个启动并负责初始化其他CPU的处理器。在系统休眠(suspend)过程中,为了节省能源,需要关闭这些非引导CPU。disable_nonboot_cpus函数通过调用freeze_secondary_cpus实现这一目的。该函数会遍历所有在线的CPU,跳过主CPU,并对每个非引导CPU执行_cpu_down操作,将其状态设置为离线(CPUHP_OFFLINE),从而确保它们在休眠期间不会消耗电力。
唤醒过程
按键导致uboot运行,会继续运行suspend_enter函数(kernel-4.19\kernel\power\suspend.c)中syscore_resume函数,依次调用arch_suspend_enable_irqs,platform_resume_noirq,dpm_resume_noirq,platform_resume_early,dpm_resume_early,platform_resume_finish,唤醒的过程是休眠的反向的。
总结:PM Core会依次调用“prepare—>suspend—>suspend_late—>suspend_noirq-------wakeup--------->resume_noirq—>resume_early—>resume-->complete”。
重要结构体
include/linux/pm.h
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);
};