休眠流程
应用层通过echo mem > /sys/power/state写入休眠状态,给一张大概流程图
这个操作对应在kernel/power/main.c的state这个attr的store操作
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n)
{suspend_state_t state;int error;error = pm_autosleep_lock();if (error)return error;if (pm_autosleep_state() > PM_SUSPEND_ON) {error = -EBUSY;goto out;}state = decode_state(buf, n);if (state < PM_SUSPEND_MAX) {if (state == PM_SUSPEND_MEM)state = mem_sleep_current;error = pm_suspend(state);} else if (state == PM_SUSPEND_MAX) {error = hibernate();} else {error = -EINVAL;}out:pm_autosleep_unlock();return error ? error : n;
}
应用层通过/sys/power/state写入休眠状态;或者使能autosleep都会调用这个
int pm_suspend(suspend_state_t state)
{int error;if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)return -EINVAL;pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);error = enter_state(state);if (error) {suspend_stats.fail++;dpm_save_failed_errno(error);} else {suspend_stats.success++;}pr_info("suspend exit\n");return error;
}
不同state,进入不同休眠状态
static int enter_state(suspend_state_t state)
{int error;trace_suspend_resume(TPS("suspend_enter"), state, true);if (state == PM_SUSPEND_TO_IDLE) {
#ifdef CONFIG_PM_DEBUGif (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");return -EAGAIN;}
#endif} else if (!valid_state(state)) {return -EINVAL;}if (!mutex_trylock(&system_transition_mutex))return -EBUSY;if (state == PM_SUSPEND_TO_IDLE)s2idle_begin();if (sync_on_suspend_enabled) {trace_suspend_resume(TPS("sync_filesystems"), 0, true);ksys_sync_helper();trace_suspend_resume(TPS("sync_filesystems"), 0, false);}pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);pm_suspend_clear_flags();error = suspend_prepare(state);if (error)goto Unlock;if (suspend_test(TEST_FREEZER))goto Finish;trace_suspend_resume(TPS("suspend_enter"), state, false);pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);pm_restrict_gfp_mask();error = suspend_devices_and_enter(state);pm_restore_gfp_mask();Finish:events_check_enabled = false;pm_pr_dbg("Finishing wakeup.\n");suspend_finish();Unlock:mutex_unlock(&system_transition_mutex);return error;
}
设备进入休眠,被唤醒或者休眠失败,就会走对应的唤醒流程;
挂起console,比如串口,终端等;
挂起devfreq,cpufreq;执行device_suspend
int suspend_devices_and_enter(suspend_state_t state)
{int error;bool wakeup = false;if (!sleep_state_supported(state))return -ENOSYS;pm_suspend_target_state = state;if (state == PM_SUSPEND_TO_IDLE)pm_set_suspend_no_platform();error = platform_suspend_begin(state);if (error)goto Close;suspend_console();//挂起console,比如串口,终端等suspend_test_start();error = dpm_suspend_start(PMSG_SUSPEND);//挂起devfreq,cpufreq;执行device_suspendif (error) {pr_err("Some devices failed to suspend, or early wake event detected\n");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 && platform_suspend_again(state));Resume_devices:suspend_test_start();dpm_resume_end(PMSG_RESUME);suspend_test_finish("resume devices");trace_suspend_resume(TPS("resume_console"), state, true);resume_console();trace_suspend_resume(TPS("resume_console"), state, false);Close:platform_resume_end(state);pm_suspend_target_state = PM_SUSPEND_ON;return error;Recover_platform:platform_recover(state);goto Resume_devices;
}
平台进入休眠;被唤醒或者休眠失败,就会走对应的唤醒流程;
检查pendind标记,检查休眠锁标记,来进入平台实现的enter函数;
平台休眠的最后,会开启中断,用与响应外部中断,来唤醒系统并继续执行接下来的代码唤醒系统
static int suspend_enter(suspend_state_t state, bool *wakeup)
{int error;error = platform_suspend_prepare(state);if (error)goto Platform_finish;error = dpm_suspend_late(PMSG_SUSPEND);if (error) {pr_err("late suspend of devices failed\n");goto Platform_finish;}error = platform_suspend_prepare_late(state);if (error)goto Devices_early_resume;error = dpm_suspend_noirq(PMSG_SUSPEND);if (error) {pr_err("noirq suspend of devices failed\n");goto Platform_early_resume;}error = platform_suspend_prepare_noirq(state);if (error)goto Platform_wake;if (suspend_test(TEST_PLATFORM))goto Platform_wake;if (state == PM_SUSPEND_TO_IDLE) {s2idle_loop();goto Platform_wake;}error = pm_sleep_disable_secondary_cpus();if (error || suspend_test(TEST_CPUS))goto Enable_cpus;arch_suspend_disable_irqs();BUG_ON(!irqs_disabled());system_state = SYSTEM_SUSPEND;error = syscore_suspend();if (!error) {*wakeup = pm_wakeup_pending();//检查能否进休眠if (!(suspend_test(TEST_CORE) || *wakeup)) {trace_suspend_resume(TPS("machine_suspend"),state, true);error = suspend_ops->enter(state);trace_suspend_resume(TPS("machine_suspend"),state, false);} else if (*wakeup) {error = -EBUSY;}syscore_resume();}system_state = SYSTEM_RUNNING;arch_suspend_enable_irqs();//平台休眠,但是开启中断,用与响应中断,唤醒系统并继续执行接下来的代码唤醒系统BUG_ON(irqs_disabled());Enable_cpus:pm_sleep_enable_secondary_cpus();Platform_wake:platform_resume_noirq(state);dpm_resume_noirq(PMSG_RESUME);Platform_early_resume:platform_resume_early(state);Devices_early_resume:dpm_resume_early(PMSG_RESUME);Platform_finish:platform_resume_finish(state);return error;
}
两种阻止进入休眠
最终都是通过__pm_stay_awake
应用层
echo abc > /sys/power/wake_lock 来申请一个休眠锁;
使用cat /sys/kernel/debug/wakeup_sources看什么在持有休眠锁;
echo abc > /sys/power/wake_unlock来接触休眠锁
内核层
应用请求休眠,系统进入休眠流程,此时如果设备触发了中断,中断处理程序中首先关闭中断,然后调度内核线程去处理work,但假如这个时候此work还未被调度到,系统就进入休眠了,那么这个设备就被永久关闭中断了,再也不能唤醒系统。pm_stay_awake()和pm_relax()的设计就是用来解决这个问题。
pm_stay_awakepm_wake_lock__pm_stay_awake
恢复pm_wake_unlock__pm_relax
休眠检查
pm_wakeup_pending
示例:休眠后,无法唤醒?
开启打印信息
休眠后系统卡住,组织串口来休眠,并开启相关打印;在Linux内核睡眠过程中,会先调用suspend_console()函数使串口进入睡眠状态,这样会导致后续设备驱动的睡眠过程不可见。可以在boot启动参数中增加no_console_suspend参数,显示设备驱动睡眠日志
remove_cmdline_param(cmdline, "no_console_suspend");
sprintf(cmdline + strlen(cmdline), " no_console_suspend=%d", 1);
修改串口日志打印等级,显示更多调试信息
echo 8 > /proc/sys/kernel/printk
设置pm_print_times参数,可以显示设备驱动睡眠唤醒时间,方便调试时查看哪个函数处理占用时间过长
echo 1 > /sys/power/pm_print_times
设置pm_debug_messages,打印来自系统的调试消息的暂停/休眠内核日志的基础结构
echo 1 > /sys/power/pm_debug_messages
打印信息
PM: pm_system_irq_wakeup: 20 triggered PMIC
pxa2xx-i2c pxa2xx-i2c.2: calling i2c_pxa_suspend_noirq+0x1/0x24 @ 6223, parent: d4000000.apb
i2c: <pxa_i2c-i2c> ICR is modified!
pxa2xx-i2c pxa2xx-i2c.2: i2c_pxa_suspend_noirq+0x1/0x24 returned 0 after 0 usecs
i2c: reset controller!
Workqueue: events chargeic_update_state_work_func
pcie-falcon d4220000.pcie: calling pcie_resume_noirq+0x1/0x1c @ 6223, parent: d4200000.axi
PCIe Host: No link negotiated
pcie-falcon d4220000.pcie: pcie_resume_noirq+0x1/0x1c returned 0 after 114202 usecs
pci 0001:00:00.0: calling pci_pm_resume_noirq+0x1/0xd4 @ 6165, parent: pci0001:00
pxa2xx-i2c pxa2xx-i2c.0: calling i2c_pxa_resume_noirq+0x1/0x38 @ 6223, parent: d4000000.apb
解决办法
i2c还在工作----而且打印了正在工作的函数;确认是充电ic休眠函数没去暂停工作队列;实现PM函数即可修复
#ifdef CONFIG_PM
static int charger_suspend(struct device *dev)
{cancel_delayed_work_sync(g_info->chg_state_update_work);return 0;
}static int charger_resume(struct device *dev)
{mod_delayed_work(system_wq, g_info->chg_state_update_work,msecs_to_jiffies(1000));return 0;
}static const struct dev_pm_ops pm_ops = {.suspend = charger_suspend,.resume = charger_resume,
};#endif
找不到pcie设备----确认供电;如无异常,确认cpu和ddr频率是否恢复;如无异常,确认现象是否跟复位脚异常有关
--- a/drivers/pci/controller/pcie-host.c
+++ b/drivers/pci/controller/pcie-host.c
@@ -646,6 +646,7 @@ static int __maybe_unused pcie_suspend_noirq(struct device *dev)phy_exit(port->phy);pm_qos_update_request(&pcie->qos_idle,PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+ gpio_set_value(port->gpio_reset,0);return 0;}@@ -653,10 +654,10 @@ static int __maybe_unused pcie_resume_noirq(struct device *dev){struct pcie *pcie = dev_get_drvdata(dev);struct pcie_port *port = pcie->port;
-
+ gpio_set_value(port->gpio_reset,1);
+ mdelay(200);pm_qos_update_request(&pcie->qos_idle, port->lpm_qos);pcie_enable_port(port);return 0;}
修改后系统可被正常唤醒