1、框架
2、休眠流程
應用層通過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_DEBUG
?? ??? ?if (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_suspend
?? ?if (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;
}
3、兩種阻止進入休眠
最終都是通過__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_awake
? ? ? ? pm_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;
?}
修改后系統可被正常喚醒
4、echo mem > /sys/power/state
做如上操作后,整個函數調用流程如下: