linux待机是通过上层应用往sysfs节点/sys/power/state中写入mem,
从而执行整个系统的待机流程。
待机过程首先是冻结用户进程,内核线程,workqueue,接着依次
执行各种设备的suspend操作,最后cpu core进入low power模式,
等待外部中断或者事件唤醒整个系统。
恢复的过程跟待机过程刚好相反,首先cpu core收到中断后,唤醒cpu core,
接着执行各种设备的resume操作,最后唤醒用户进程,内核线程,workqueue等,
最后执行调度函数,选取优先级高的进程执行。
msm_cpu_pm_enter_sleep里面执行wfi指令,对于唤醒时间统计可以从这个函数之后进行记时。
从而执行整个系统的待机流程。
待机过程首先是冻结用户进程,内核线程,workqueue,接着依次
执行各种设备的suspend操作,最后cpu core进入low power模式,
等待外部中断或者事件唤醒整个系统。
恢复的过程跟待机过程刚好相反,首先cpu core收到中断后,唤醒cpu core,
接着执行各种设备的resume操作,最后唤醒用户进程,内核线程,workqueue等,
最后执行调度函数,选取优先级高的进程执行。
下面是代码流程的解读。
//user往sys节点写state后,先调用
int pm_suspend(suspend_state_t state)
{
int error;
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
pm_suspend_marker("entry");
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else {
suspend_stats.success++;
}
pm_suspend_marker("exit");
return error;
}
//待机唤醒主体函数,这个函数里面完成了待机跟唤醒的完整过程,
//这里面用到的suspend_ops通过suspend_set_ops完成注册,
//对于高通平台,实际使用的接口在lpm_level.c中定义
static int enter_state(suspend_state_t state)
{
int error;
if (state == PM_SUSPEND_FREEZE) {
#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warning("PM: Unsupported test mode for freeze state,"
"please choose none/freezer/devices/platform.\n");
return -EAGAIN;
}
#endif
} else if (!valid_state(state)) {
return -EINVAL;
}
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
if (state == PM_SUSPEND_FREEZE)
freeze_begin();
//文件系统同步,防止数据损坏
printk(KERN_INFO "PM: Syncing filesystems ... \n");
sys_sync();
printk("done.\n");
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label);
//待机前准备操作
error = suspend_prepare(state);
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM: Entering %s sleep\n", pm_states[state].label);
pm_restrict_gfp_mask();
//这里面进入待机,同时会从待机点依次恢复
error = suspend_devices_and_enter(state);
pm_restore_gfp_mask();
Finish:
pr_debug("PM: Finishing wakeup.\n");
//唤醒整个系统
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
static int suspend_prepare(suspend_state_t state)
{
int error;
if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
return -EPERM;
//将当前console切换到一个虚拟console并重定向内核的kmsg(需要的话)。该功能称作VT switch
pm_prepare_console();
//通知device系统准备进入suspend状态,某些特殊设备
//在设备模型之外通过PM notifier来接受PM消息,执行自身suspend/resume操作
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
//冻结用户进程,内核线程,work_queue,失败的话就唤醒已经被冻结task
error = suspend_freeze_processes();
if (!error)
return 0;
log_suspend_abort_reason("One or more tasks refusing to freeze");
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
bool wakeup = false;
//suspend_ops 是在lpm_probe函数中通过suspend_set_ops进行注册的
if (need_suspend_ops(state) && !suspend_ops)
return -ENOSYS;
trace_machine_suspend(state);
if (need_suspend_ops(state) && suspend_ops->begin) {
error = suspend_ops->begin(state);
if (error)
goto Close;
}
//获取console lock,阻止console被访问
suspend_console();
ftrace_stop();//停止ftrace功能
suspend_test_start();
//dpm-device PM, 设备suspend操作
error = dpm_suspend_start(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend\n");
log_suspend_abort_reason("Some devices failed to suspend");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
do {
error = suspend_enter(state, &wakeup);//待机,唤醒操作
} while (!error && !wakeup && need_suspend_ops(state)
&& suspend_ops->suspend_again && suspend_ops->suspend_again());
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
ftrace_start();
resume_console();
Close:
if (need_suspend_ops(state) && suspend_ops->end)
suspend_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error;
Recover_platform:
if (need_suspend_ops(state) && suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
int error, last_dev;
//未注册prepare函数
if (need_suspend_ops(state) && suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
goto Platform_finish;
}
//执行设备注册的late suspend跟noirq suspend函数,
error = dpm_suspend_end(PMSG_SUSPEND);
if (error) {
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
last_dev %= REC_FAILED_NUM;
printk(KERN_ERR "PM: Some devices failed to power down\n");
log_suspend_abort_reason("%s device failed to power down",
suspend_stats.failed_devs[last_dev]);
goto Platform_finish;
}
//执行lpm_suspend_prepare
if (need_suspend_ops(state) && suspend_ops->prepare_late) {
error = suspend_ops->prepare_late();
if (error)
goto Platform_wake;
}
if (suspend_test(TEST_PLATFORM))
goto Platform_wake;
/*
* PM_SUSPEND_FREEZE equals
* frozen processes + suspended devices + idle processors.
* Thus we should invoke freeze_enter() soon after
* all the devices are suspended.
*/
if (state == PM_SUSPEND_FREEZE) {
freeze_enter();
goto Platform_wake;
}
qiku_regulator_dbg_show(print_gpio_clk_regulate_mask & REG_BIT); //sunshuai
//关闭非启动cpu
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS)) {
log_suspend_abort_reason("Disabling non-boot cpus failed");
goto Enable_cpus;
}
//关中断
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
error = syscore_suspend();
if (!error) {
*wakeup = pm_wakeup_pending();
if (!(suspend_test(TEST_CORE) || *wakeup)) {
//执行lpm_suspend_enter,这里面CPU进入WFI状态,等待外部中断唤醒cpu
error = suspend_ops->enter(state);
events_check_enabled = false;
} else if (*wakeup) {
pm_get_active_wakeup_sources(suspend_abort,
MAX_SUSPEND_ABORT_LEN);
log_suspend_abort_reason(suspend_abort);
error = -EBUSY;
}
syscore_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
//启动nonboot cpu
enable_nonboot_cpus();
Platform_wake:
//wake 为空
if (need_suspend_ops(state) && suspend_ops->wake)
suspend_ops->wake();
//执行设备noirq/early resume
dpm_resume_start(PMSG_RESUME);
Platform_finish:
if (need_suspend_ops(state) && suspend_ops->finish)
suspend_ops->finish();
return error;
}
static void suspend_finish(void)
{
//唤醒task
suspend_thaw_processes();
//特殊设备执行resume操作
pm_notifier_call_chain(PM_POST_SUSPEND);
//切换虚拟终端
pm_restore_console();
}
//跟cpu core相关的操作:
static int lpm_suspend_enter(suspend_state_t state)
{
int cpu = raw_smp_processor_id();
struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
struct lpm_cpu *lpm_cpu = cluster->cpu;
const struct cpumask *cpumask = get_cpu_mask(cpu);
int idx;
for (idx = lpm_cpu->nlevels - 1; idx >= 0; idx--) {
if (lpm_cpu_mode_allow(cpu, idx, false))
break;
}
if (idx < 0) {
pr_err("Failed suspend\n");
return 0;
}
cpu_prepare(cluster, idx, false);
cluster_prepare(cluster, cpumask, idx, false);
if (idx > 0)
update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
0xdeaffeed, false);
/*
* Print the clocks which are enabled during system suspend
* This debug information is useful to know which are the
* clocks that are enabled and preventing the system level
* LPMs(XO and Vmin).
*/
clock_debug_print_enabled();
if (!use_psci)
msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, false);//执行wfi,进入睡眠等待中断唤醒
else
psci_enter_sleep(cluster, idx, true);
if (idx > 0)
update_debug_pc_event(CPU_EXIT, idx, true, 0xdeaffeed,
false);
//cpu从待机状态恢复正常
cluster_unprepare(cluster, cpumask, idx, false);
cpu_unprepare(cluster, idx, false);
return 0;
}
msm_cpu_pm_enter_sleep里面执行wfi指令,对于唤醒时间统计可以从这个函数之后进行记时。