1d08ca2caSLen Brown /* 2d08ca2caSLen Brown * sleep.c - ACPI sleep support. 3d08ca2caSLen Brown * 4d08ca2caSLen Brown * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> 5d08ca2caSLen Brown * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com> 6d08ca2caSLen Brown * Copyright (c) 2000-2003 Patrick Mochel 7d08ca2caSLen Brown * Copyright (c) 2003 Open Source Development Lab 8d08ca2caSLen Brown * 9d08ca2caSLen Brown * This file is released under the GPLv2. 10d08ca2caSLen Brown * 11d08ca2caSLen Brown */ 12d08ca2caSLen Brown 13d08ca2caSLen Brown #include <linux/delay.h> 14d08ca2caSLen Brown #include <linux/irq.h> 15d08ca2caSLen Brown #include <linux/dmi.h> 16d08ca2caSLen Brown #include <linux/device.h> 17d08ca2caSLen Brown #include <linux/suspend.h> 18d08ca2caSLen Brown #include <linux/reboot.h> 19d1ee4335SH. Peter Anvin #include <linux/acpi.h> 20a2ef5c4fSLin Ming #include <linux/module.h> 21d08ca2caSLen Brown #include <asm/io.h> 22d08ca2caSLen Brown 23e60cc7a6SBjorn Helgaas #include "internal.h" 24d08ca2caSLen Brown #include "sleep.h" 25d08ca2caSLen Brown 2601eac60bSStephen Hemminger static u8 sleep_states[ACPI_S_STATE_COUNT]; 27d08ca2caSLen Brown 28d08ca2caSLen Brown static void acpi_sleep_tts_switch(u32 acpi_state) 29d08ca2caSLen Brown { 300db98202SJiang Liu acpi_status status; 31d08ca2caSLen Brown 320db98202SJiang Liu status = acpi_execute_simple_method(NULL, "\\_TTS", acpi_state); 33d08ca2caSLen Brown if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { 34d08ca2caSLen Brown /* 35d08ca2caSLen Brown * OS can't evaluate the _TTS object correctly. Some warning 36d08ca2caSLen Brown * message will be printed. But it won't break anything. 37d08ca2caSLen Brown */ 38d08ca2caSLen Brown printk(KERN_NOTICE "Failure in evaluating _TTS object\n"); 39d08ca2caSLen Brown } 40d08ca2caSLen Brown } 41d08ca2caSLen Brown 42d08ca2caSLen Brown static int tts_notify_reboot(struct notifier_block *this, 43d08ca2caSLen Brown unsigned long code, void *x) 44d08ca2caSLen Brown { 45d08ca2caSLen Brown acpi_sleep_tts_switch(ACPI_STATE_S5); 46d08ca2caSLen Brown return NOTIFY_DONE; 47d08ca2caSLen Brown } 48d08ca2caSLen Brown 49d08ca2caSLen Brown static struct notifier_block tts_notifier = { 50d08ca2caSLen Brown .notifier_call = tts_notify_reboot, 51d08ca2caSLen Brown .next = NULL, 52d08ca2caSLen Brown .priority = 0, 53d08ca2caSLen Brown }; 54d08ca2caSLen Brown 55d08ca2caSLen Brown static int acpi_sleep_prepare(u32 acpi_state) 56d08ca2caSLen Brown { 57d08ca2caSLen Brown #ifdef CONFIG_ACPI_SLEEP 58d08ca2caSLen Brown /* do we have a wakeup address for S2 and S3? */ 59d08ca2caSLen Brown if (acpi_state == ACPI_STATE_S3) { 60319b6ffcSH. Peter Anvin if (!acpi_wakeup_address) 61d08ca2caSLen Brown return -EFAULT; 62319b6ffcSH. Peter Anvin acpi_set_firmware_waking_vector(acpi_wakeup_address); 63d08ca2caSLen Brown 64d08ca2caSLen Brown } 65d08ca2caSLen Brown ACPI_FLUSH_CPU_CACHE(); 66d08ca2caSLen Brown #endif 67d08ca2caSLen Brown printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n", 68d08ca2caSLen Brown acpi_state); 6978f5f023SRafael J. Wysocki acpi_enable_wakeup_devices(acpi_state); 70d08ca2caSLen Brown acpi_enter_sleep_state_prep(acpi_state); 71d08ca2caSLen Brown return 0; 72d08ca2caSLen Brown } 73d08ca2caSLen Brown 74a4e90bedSRafael J. Wysocki static bool acpi_sleep_state_supported(u8 sleep_state) 75a4e90bedSRafael J. Wysocki { 76a4e90bedSRafael J. Wysocki acpi_status status; 77a4e90bedSRafael J. Wysocki u8 type_a, type_b; 78a4e90bedSRafael J. Wysocki 79a4e90bedSRafael J. Wysocki status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b); 80a4e90bedSRafael J. Wysocki return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware 81a4e90bedSRafael J. Wysocki || (acpi_gbl_FADT.sleep_control.address 82a4e90bedSRafael J. Wysocki && acpi_gbl_FADT.sleep_status.address)); 83a4e90bedSRafael J. Wysocki } 84a4e90bedSRafael J. Wysocki 85d08ca2caSLen Brown #ifdef CONFIG_ACPI_SLEEP 86091aad6aSJan Beulich static u32 acpi_target_sleep_state = ACPI_STATE_S0; 87a6ae7594SRafael J. Wysocki 88a6ae7594SRafael J. Wysocki u32 acpi_target_system_state(void) 89a6ae7594SRafael J. Wysocki { 90a6ae7594SRafael J. Wysocki return acpi_target_sleep_state; 91a6ae7594SRafael J. Wysocki } 92*fad16dd9SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_target_system_state); 93a6ae7594SRafael J. Wysocki 94a5ca7345SRafael J. Wysocki static bool pwr_btn_event_pending; 95091aad6aSJan Beulich 96d7f0eea9SZhang Rui /* 9772ad5d77SRafael J. Wysocki * The ACPI specification wants us to save NVS memory regions during hibernation 9872ad5d77SRafael J. Wysocki * and to restore them during the subsequent resume. Windows does that also for 9972ad5d77SRafael J. Wysocki * suspend to RAM. However, it is known that this mechanism does not work on 10072ad5d77SRafael J. Wysocki * all machines, so we allow the user to disable it with the help of the 10172ad5d77SRafael J. Wysocki * 'acpi_sleep=nonvs' kernel command line option. 10272ad5d77SRafael J. Wysocki */ 10372ad5d77SRafael J. Wysocki static bool nvs_nosave; 10472ad5d77SRafael J. Wysocki 10572ad5d77SRafael J. Wysocki void __init acpi_nvs_nosave(void) 10672ad5d77SRafael J. Wysocki { 10772ad5d77SRafael J. Wysocki nvs_nosave = true; 10872ad5d77SRafael J. Wysocki } 10972ad5d77SRafael J. Wysocki 11072ad5d77SRafael J. Wysocki /* 1111bad2f19SKristen Carlson Accardi * The ACPI specification wants us to save NVS memory regions during hibernation 1121bad2f19SKristen Carlson Accardi * but says nothing about saving NVS during S3. Not all versions of Windows 1131bad2f19SKristen Carlson Accardi * save NVS on S3 suspend either, and it is clear that not all systems need 1141bad2f19SKristen Carlson Accardi * NVS to be saved at S3 time. To improve suspend/resume time, allow the 1151bad2f19SKristen Carlson Accardi * user to disable saving NVS on S3 if their system does not require it, but 1161bad2f19SKristen Carlson Accardi * continue to save/restore NVS for S4 as specified. 1171bad2f19SKristen Carlson Accardi */ 1181bad2f19SKristen Carlson Accardi static bool nvs_nosave_s3; 1191bad2f19SKristen Carlson Accardi 1201bad2f19SKristen Carlson Accardi void __init acpi_nvs_nosave_s3(void) 1211bad2f19SKristen Carlson Accardi { 1221bad2f19SKristen Carlson Accardi nvs_nosave_s3 = true; 1231bad2f19SKristen Carlson Accardi } 1241bad2f19SKristen Carlson Accardi 1251bad2f19SKristen Carlson Accardi /* 126d08ca2caSLen Brown * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the 127d08ca2caSLen Brown * user to request that behavior by using the 'acpi_old_suspend_ordering' 128d08ca2caSLen Brown * kernel command line option that causes the following variable to be set. 129d08ca2caSLen Brown */ 130d08ca2caSLen Brown static bool old_suspend_ordering; 131d08ca2caSLen Brown 132d08ca2caSLen Brown void __init acpi_old_suspend_ordering(void) 133d08ca2caSLen Brown { 134d08ca2caSLen Brown old_suspend_ordering = true; 135d08ca2caSLen Brown } 136d08ca2caSLen Brown 137d08ca2caSLen Brown static int __init init_old_suspend_ordering(const struct dmi_system_id *d) 138d08ca2caSLen Brown { 1390ac1b1d7SZhang Rui acpi_old_suspend_ordering(); 140d08ca2caSLen Brown return 0; 141d08ca2caSLen Brown } 142d08ca2caSLen Brown 14353998648SRafael J. Wysocki static int __init init_nvs_nosave(const struct dmi_system_id *d) 14453998648SRafael J. Wysocki { 14553998648SRafael J. Wysocki acpi_nvs_nosave(); 14653998648SRafael J. Wysocki return 0; 14753998648SRafael J. Wysocki } 14853998648SRafael J. Wysocki 1493e4376ffSSachin Kamat static struct dmi_system_id acpisleep_dmi_table[] __initdata = { 150d08ca2caSLen Brown { 151d08ca2caSLen Brown .callback = init_old_suspend_ordering, 152d08ca2caSLen Brown .ident = "Abit KN9 (nForce4 variant)", 153d08ca2caSLen Brown .matches = { 154d08ca2caSLen Brown DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"), 155d08ca2caSLen Brown DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"), 156d08ca2caSLen Brown }, 157d08ca2caSLen Brown }, 158d08ca2caSLen Brown { 159d08ca2caSLen Brown .callback = init_old_suspend_ordering, 160d08ca2caSLen Brown .ident = "HP xw4600 Workstation", 161d08ca2caSLen Brown .matches = { 162d08ca2caSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 163d08ca2caSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"), 164d08ca2caSLen Brown }, 165d08ca2caSLen Brown }, 166d08ca2caSLen Brown { 167a1404495SAndy Whitcroft .callback = init_old_suspend_ordering, 168a1404495SAndy Whitcroft .ident = "Asus Pundit P1-AH2 (M2N8L motherboard)", 169a1404495SAndy Whitcroft .matches = { 170a1404495SAndy Whitcroft DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek Computer INC."), 171a1404495SAndy Whitcroft DMI_MATCH(DMI_BOARD_NAME, "M2N8L"), 172a1404495SAndy Whitcroft }, 173a1404495SAndy Whitcroft }, 17445e77988SZhang Rui { 1752a9ef8e1SZhao Yakui .callback = init_old_suspend_ordering, 1762a9ef8e1SZhao Yakui .ident = "Panasonic CF51-2L", 1772a9ef8e1SZhao Yakui .matches = { 1782a9ef8e1SZhao Yakui DMI_MATCH(DMI_BOARD_VENDOR, 1792a9ef8e1SZhao Yakui "Matsushita Electric Industrial Co.,Ltd."), 1802a9ef8e1SZhao Yakui DMI_MATCH(DMI_BOARD_NAME, "CF51-2L"), 1812a9ef8e1SZhao Yakui }, 1822a9ef8e1SZhao Yakui }, 18353998648SRafael J. Wysocki { 18453998648SRafael J. Wysocki .callback = init_nvs_nosave, 18566f2fda9SJoseph Salisbury .ident = "Sony Vaio VGN-FW41E_H", 18666f2fda9SJoseph Salisbury .matches = { 18766f2fda9SJoseph Salisbury DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 18866f2fda9SJoseph Salisbury DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW41E_H"), 18966f2fda9SJoseph Salisbury }, 19066f2fda9SJoseph Salisbury }, 19166f2fda9SJoseph Salisbury { 19266f2fda9SJoseph Salisbury .callback = init_nvs_nosave, 193d11c78e9SDave Jones .ident = "Sony Vaio VGN-FW21E", 194d11c78e9SDave Jones .matches = { 195d11c78e9SDave Jones DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 196d11c78e9SDave Jones DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"), 197d11c78e9SDave Jones }, 198d11c78e9SDave Jones }, 199d11c78e9SDave Jones { 200d11c78e9SDave Jones .callback = init_nvs_nosave, 201469dd1c4SFabio Valentini .ident = "Sony Vaio VGN-FW21M", 202469dd1c4SFabio Valentini .matches = { 203469dd1c4SFabio Valentini DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 204469dd1c4SFabio Valentini DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21M"), 205469dd1c4SFabio Valentini }, 206469dd1c4SFabio Valentini }, 207469dd1c4SFabio Valentini { 208469dd1c4SFabio Valentini .callback = init_nvs_nosave, 209ddf6ce45SDave Jones .ident = "Sony Vaio VPCEB17FX", 210ddf6ce45SDave Jones .matches = { 211ddf6ce45SDave Jones DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 212ddf6ce45SDave Jones DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"), 213ddf6ce45SDave Jones }, 214ddf6ce45SDave Jones }, 215ddf6ce45SDave Jones { 216ddf6ce45SDave Jones .callback = init_nvs_nosave, 21753998648SRafael J. Wysocki .ident = "Sony Vaio VGN-SR11M", 21853998648SRafael J. Wysocki .matches = { 21953998648SRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 22053998648SRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR11M"), 22153998648SRafael J. Wysocki }, 22253998648SRafael J. Wysocki }, 22353998648SRafael J. Wysocki { 22453998648SRafael J. Wysocki .callback = init_nvs_nosave, 22553998648SRafael J. Wysocki .ident = "Everex StepNote Series", 22653998648SRafael J. Wysocki .matches = { 22753998648SRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "Everex Systems, Inc."), 22853998648SRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"), 22953998648SRafael J. Wysocki }, 23053998648SRafael J. Wysocki }, 231af48931cSRafael J. Wysocki { 232af48931cSRafael J. Wysocki .callback = init_nvs_nosave, 233af48931cSRafael J. Wysocki .ident = "Sony Vaio VPCEB1Z1E", 234af48931cSRafael J. Wysocki .matches = { 235af48931cSRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 236af48931cSRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"), 237af48931cSRafael J. Wysocki }, 238af48931cSRafael J. Wysocki }, 239291a73c9SRafael J. Wysocki { 240291a73c9SRafael J. Wysocki .callback = init_nvs_nosave, 241291a73c9SRafael J. Wysocki .ident = "Sony Vaio VGN-NW130D", 242291a73c9SRafael J. Wysocki .matches = { 243291a73c9SRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 244291a73c9SRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NW130D"), 245291a73c9SRafael J. Wysocki }, 246291a73c9SRafael J. Wysocki }, 2477b330707SRafael J. Wysocki { 2487b330707SRafael J. Wysocki .callback = init_nvs_nosave, 24993f77084SLan Tianyu .ident = "Sony Vaio VPCCW29FX", 25093f77084SLan Tianyu .matches = { 25193f77084SLan Tianyu DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 25293f77084SLan Tianyu DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"), 25393f77084SLan Tianyu }, 25493f77084SLan Tianyu }, 25593f77084SLan Tianyu { 25693f77084SLan Tianyu .callback = init_nvs_nosave, 2577b330707SRafael J. Wysocki .ident = "Averatec AV1020-ED2", 2587b330707SRafael J. Wysocki .matches = { 2597b330707SRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"), 2607b330707SRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"), 2617b330707SRafael J. Wysocki }, 2627b330707SRafael J. Wysocki }, 263bb0c5ed6SZhang Rui { 264bb0c5ed6SZhang Rui .callback = init_old_suspend_ordering, 265bb0c5ed6SZhang Rui .ident = "Asus A8N-SLI DELUXE", 266bb0c5ed6SZhang Rui .matches = { 267bb0c5ed6SZhang Rui DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), 268bb0c5ed6SZhang Rui DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"), 269bb0c5ed6SZhang Rui }, 270bb0c5ed6SZhang Rui }, 271bb0c5ed6SZhang Rui { 272bb0c5ed6SZhang Rui .callback = init_old_suspend_ordering, 273bb0c5ed6SZhang Rui .ident = "Asus A8N-SLI Premium", 274bb0c5ed6SZhang Rui .matches = { 275bb0c5ed6SZhang Rui DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), 276bb0c5ed6SZhang Rui DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"), 277bb0c5ed6SZhang Rui }, 278bb0c5ed6SZhang Rui }, 27989e8ea12SRafael J. Wysocki { 28089e8ea12SRafael J. Wysocki .callback = init_nvs_nosave, 28189e8ea12SRafael J. Wysocki .ident = "Sony Vaio VGN-SR26GN_P", 28289e8ea12SRafael J. Wysocki .matches = { 28389e8ea12SRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 28489e8ea12SRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR26GN_P"), 28589e8ea12SRafael J. Wysocki }, 28689e8ea12SRafael J. Wysocki }, 287731b25a4SBogdan Radulescu { 288731b25a4SBogdan Radulescu .callback = init_nvs_nosave, 289876ab790SLan Tianyu .ident = "Sony Vaio VPCEB1S1E", 290876ab790SLan Tianyu .matches = { 291876ab790SLan Tianyu DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 292876ab790SLan Tianyu DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1S1E"), 293876ab790SLan Tianyu }, 294876ab790SLan Tianyu }, 295876ab790SLan Tianyu { 296876ab790SLan Tianyu .callback = init_nvs_nosave, 297731b25a4SBogdan Radulescu .ident = "Sony Vaio VGN-FW520F", 298731b25a4SBogdan Radulescu .matches = { 299731b25a4SBogdan Radulescu DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 300731b25a4SBogdan Radulescu DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW520F"), 301731b25a4SBogdan Radulescu }, 302731b25a4SBogdan Radulescu }, 3035a50a7c3SKeng-Yu Lin { 3045a50a7c3SKeng-Yu Lin .callback = init_nvs_nosave, 3055a50a7c3SKeng-Yu Lin .ident = "Asus K54C", 3065a50a7c3SKeng-Yu Lin .matches = { 3075a50a7c3SKeng-Yu Lin DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 3085a50a7c3SKeng-Yu Lin DMI_MATCH(DMI_PRODUCT_NAME, "K54C"), 3095a50a7c3SKeng-Yu Lin }, 3105a50a7c3SKeng-Yu Lin }, 3115a50a7c3SKeng-Yu Lin { 3125a50a7c3SKeng-Yu Lin .callback = init_nvs_nosave, 3135a50a7c3SKeng-Yu Lin .ident = "Asus K54HR", 3145a50a7c3SKeng-Yu Lin .matches = { 3155a50a7c3SKeng-Yu Lin DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 3165a50a7c3SKeng-Yu Lin DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"), 3175a50a7c3SKeng-Yu Lin }, 3185a50a7c3SKeng-Yu Lin }, 319d08ca2caSLen Brown {}, 320d08ca2caSLen Brown }; 3210ac1b1d7SZhang Rui 3220ac1b1d7SZhang Rui static void acpi_sleep_dmi_check(void) 3230ac1b1d7SZhang Rui { 3240ac1b1d7SZhang Rui dmi_check_system(acpisleep_dmi_table); 3250ac1b1d7SZhang Rui } 3260ac1b1d7SZhang Rui 3270ac1b1d7SZhang Rui /** 3280ac1b1d7SZhang Rui * acpi_pm_freeze - Disable the GPEs and suspend EC transactions. 3290ac1b1d7SZhang Rui */ 3300ac1b1d7SZhang Rui static int acpi_pm_freeze(void) 3310ac1b1d7SZhang Rui { 3320ac1b1d7SZhang Rui acpi_disable_all_gpes(); 3330ac1b1d7SZhang Rui acpi_os_wait_events_complete(); 3340ac1b1d7SZhang Rui acpi_ec_block_transactions(); 3350ac1b1d7SZhang Rui return 0; 3360ac1b1d7SZhang Rui } 3370ac1b1d7SZhang Rui 3380ac1b1d7SZhang Rui /** 3390ac1b1d7SZhang Rui * acpi_pre_suspend - Enable wakeup devices, "freeze" EC and save NVS. 3400ac1b1d7SZhang Rui */ 3410ac1b1d7SZhang Rui static int acpi_pm_pre_suspend(void) 3420ac1b1d7SZhang Rui { 3430ac1b1d7SZhang Rui acpi_pm_freeze(); 3440ac1b1d7SZhang Rui return suspend_nvs_save(); 3450ac1b1d7SZhang Rui } 3460ac1b1d7SZhang Rui 3470ac1b1d7SZhang Rui /** 3480ac1b1d7SZhang Rui * __acpi_pm_prepare - Prepare the platform to enter the target state. 3490ac1b1d7SZhang Rui * 3500ac1b1d7SZhang Rui * If necessary, set the firmware waking vector and do arch-specific 3510ac1b1d7SZhang Rui * nastiness to get the wakeup code to the waking vector. 3520ac1b1d7SZhang Rui */ 3530ac1b1d7SZhang Rui static int __acpi_pm_prepare(void) 3540ac1b1d7SZhang Rui { 3550ac1b1d7SZhang Rui int error = acpi_sleep_prepare(acpi_target_sleep_state); 3560ac1b1d7SZhang Rui if (error) 3570ac1b1d7SZhang Rui acpi_target_sleep_state = ACPI_STATE_S0; 3580ac1b1d7SZhang Rui 3590ac1b1d7SZhang Rui return error; 3600ac1b1d7SZhang Rui } 3610ac1b1d7SZhang Rui 3620ac1b1d7SZhang Rui /** 3630ac1b1d7SZhang Rui * acpi_pm_prepare - Prepare the platform to enter the target sleep 3640ac1b1d7SZhang Rui * state and disable the GPEs. 3650ac1b1d7SZhang Rui */ 3660ac1b1d7SZhang Rui static int acpi_pm_prepare(void) 3670ac1b1d7SZhang Rui { 3680ac1b1d7SZhang Rui int error = __acpi_pm_prepare(); 3690ac1b1d7SZhang Rui if (!error) 3700ac1b1d7SZhang Rui error = acpi_pm_pre_suspend(); 3710ac1b1d7SZhang Rui 3720ac1b1d7SZhang Rui return error; 3730ac1b1d7SZhang Rui } 3740ac1b1d7SZhang Rui 3750ac1b1d7SZhang Rui static int find_powerf_dev(struct device *dev, void *data) 3760ac1b1d7SZhang Rui { 3770ac1b1d7SZhang Rui struct acpi_device *device = to_acpi_device(dev); 3780ac1b1d7SZhang Rui const char *hid = acpi_device_hid(device); 3790ac1b1d7SZhang Rui 3800ac1b1d7SZhang Rui return !strcmp(hid, ACPI_BUTTON_HID_POWERF); 3810ac1b1d7SZhang Rui } 3820ac1b1d7SZhang Rui 3830ac1b1d7SZhang Rui /** 3840ac1b1d7SZhang Rui * acpi_pm_finish - Instruct the platform to leave a sleep state. 3850ac1b1d7SZhang Rui * 3860ac1b1d7SZhang Rui * This is called after we wake back up (or if entering the sleep state 3870ac1b1d7SZhang Rui * failed). 3880ac1b1d7SZhang Rui */ 3890ac1b1d7SZhang Rui static void acpi_pm_finish(void) 3900ac1b1d7SZhang Rui { 3910ac1b1d7SZhang Rui struct device *pwr_btn_dev; 3920ac1b1d7SZhang Rui u32 acpi_state = acpi_target_sleep_state; 3930ac1b1d7SZhang Rui 3940ac1b1d7SZhang Rui acpi_ec_unblock_transactions(); 3950ac1b1d7SZhang Rui suspend_nvs_free(); 3960ac1b1d7SZhang Rui 3970ac1b1d7SZhang Rui if (acpi_state == ACPI_STATE_S0) 3980ac1b1d7SZhang Rui return; 3990ac1b1d7SZhang Rui 4000ac1b1d7SZhang Rui printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n", 4010ac1b1d7SZhang Rui acpi_state); 4020ac1b1d7SZhang Rui acpi_disable_wakeup_devices(acpi_state); 4030ac1b1d7SZhang Rui acpi_leave_sleep_state(acpi_state); 4040ac1b1d7SZhang Rui 4050ac1b1d7SZhang Rui /* reset firmware waking vector */ 4060ac1b1d7SZhang Rui acpi_set_firmware_waking_vector((acpi_physical_address) 0); 4070ac1b1d7SZhang Rui 4080ac1b1d7SZhang Rui acpi_target_sleep_state = ACPI_STATE_S0; 4090ac1b1d7SZhang Rui 410781d737cSRafael J. Wysocki acpi_resume_power_resources(); 411781d737cSRafael J. Wysocki 4120ac1b1d7SZhang Rui /* If we were woken with the fixed power button, provide a small 4130ac1b1d7SZhang Rui * hint to userspace in the form of a wakeup event on the fixed power 4140ac1b1d7SZhang Rui * button device (if it can be found). 4150ac1b1d7SZhang Rui * 4160ac1b1d7SZhang Rui * We delay the event generation til now, as the PM layer requires 4170ac1b1d7SZhang Rui * timekeeping to be running before we generate events. */ 4180ac1b1d7SZhang Rui if (!pwr_btn_event_pending) 4190ac1b1d7SZhang Rui return; 4200ac1b1d7SZhang Rui 4210ac1b1d7SZhang Rui pwr_btn_event_pending = false; 4220ac1b1d7SZhang Rui pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL, 4230ac1b1d7SZhang Rui find_powerf_dev); 4240ac1b1d7SZhang Rui if (pwr_btn_dev) { 4250ac1b1d7SZhang Rui pm_wakeup_event(pwr_btn_dev, 0); 4260ac1b1d7SZhang Rui put_device(pwr_btn_dev); 4270ac1b1d7SZhang Rui } 4280ac1b1d7SZhang Rui } 4290ac1b1d7SZhang Rui 4300ac1b1d7SZhang Rui /** 431ad07277eSRafael J. Wysocki * acpi_pm_start - Start system PM transition. 432ad07277eSRafael J. Wysocki */ 433ad07277eSRafael J. Wysocki static void acpi_pm_start(u32 acpi_state) 434ad07277eSRafael J. Wysocki { 435ad07277eSRafael J. Wysocki acpi_target_sleep_state = acpi_state; 436ad07277eSRafael J. Wysocki acpi_sleep_tts_switch(acpi_target_sleep_state); 437ad07277eSRafael J. Wysocki acpi_scan_lock_acquire(); 438ad07277eSRafael J. Wysocki } 439ad07277eSRafael J. Wysocki 440ad07277eSRafael J. Wysocki /** 441ad07277eSRafael J. Wysocki * acpi_pm_end - Finish up system PM transition. 4420ac1b1d7SZhang Rui */ 4430ac1b1d7SZhang Rui static void acpi_pm_end(void) 4440ac1b1d7SZhang Rui { 445ad07277eSRafael J. Wysocki acpi_scan_lock_release(); 4460ac1b1d7SZhang Rui /* 4470ac1b1d7SZhang Rui * This is necessary in case acpi_pm_finish() is not called during a 4480ac1b1d7SZhang Rui * failing transition to a sleep state. 4490ac1b1d7SZhang Rui */ 4500ac1b1d7SZhang Rui acpi_target_sleep_state = ACPI_STATE_S0; 4510ac1b1d7SZhang Rui acpi_sleep_tts_switch(acpi_target_sleep_state); 4520ac1b1d7SZhang Rui } 4530ac1b1d7SZhang Rui #else /* !CONFIG_ACPI_SLEEP */ 4540ac1b1d7SZhang Rui #define acpi_target_sleep_state ACPI_STATE_S0 4550ac1b1d7SZhang Rui static inline void acpi_sleep_dmi_check(void) {} 4560ac1b1d7SZhang Rui #endif /* CONFIG_ACPI_SLEEP */ 4570ac1b1d7SZhang Rui 4580ac1b1d7SZhang Rui #ifdef CONFIG_SUSPEND 4590ac1b1d7SZhang Rui static u32 acpi_suspend_states[] = { 4600ac1b1d7SZhang Rui [PM_SUSPEND_ON] = ACPI_STATE_S0, 4610ac1b1d7SZhang Rui [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, 4620ac1b1d7SZhang Rui [PM_SUSPEND_MEM] = ACPI_STATE_S3, 4630ac1b1d7SZhang Rui [PM_SUSPEND_MAX] = ACPI_STATE_S5 4640ac1b1d7SZhang Rui }; 4650ac1b1d7SZhang Rui 4660ac1b1d7SZhang Rui /** 4670ac1b1d7SZhang Rui * acpi_suspend_begin - Set the target system sleep state to the state 4680ac1b1d7SZhang Rui * associated with given @pm_state, if supported. 4690ac1b1d7SZhang Rui */ 4700ac1b1d7SZhang Rui static int acpi_suspend_begin(suspend_state_t pm_state) 4710ac1b1d7SZhang Rui { 4720ac1b1d7SZhang Rui u32 acpi_state = acpi_suspend_states[pm_state]; 473ad07277eSRafael J. Wysocki int error; 4740ac1b1d7SZhang Rui 4750ac1b1d7SZhang Rui error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc(); 4760ac1b1d7SZhang Rui if (error) 4770ac1b1d7SZhang Rui return error; 4780ac1b1d7SZhang Rui 479ad07277eSRafael J. Wysocki if (!sleep_states[acpi_state]) { 480ad07277eSRafael J. Wysocki pr_err("ACPI does not support sleep state S%u\n", acpi_state); 481ad07277eSRafael J. Wysocki return -ENOSYS; 4820ac1b1d7SZhang Rui } 483ad07277eSRafael J. Wysocki 484ad07277eSRafael J. Wysocki acpi_pm_start(acpi_state); 485ad07277eSRafael J. Wysocki return 0; 4860ac1b1d7SZhang Rui } 4870ac1b1d7SZhang Rui 4880ac1b1d7SZhang Rui /** 4890ac1b1d7SZhang Rui * acpi_suspend_enter - Actually enter a sleep state. 4900ac1b1d7SZhang Rui * @pm_state: ignored 4910ac1b1d7SZhang Rui * 4920ac1b1d7SZhang Rui * Flush caches and go to sleep. For STR we have to call arch-specific 4930ac1b1d7SZhang Rui * assembly, which in turn call acpi_enter_sleep_state(). 4940ac1b1d7SZhang Rui * It's unfortunate, but it works. Please fix if you're feeling frisky. 4950ac1b1d7SZhang Rui */ 4960ac1b1d7SZhang Rui static int acpi_suspend_enter(suspend_state_t pm_state) 4970ac1b1d7SZhang Rui { 4980ac1b1d7SZhang Rui acpi_status status = AE_OK; 4990ac1b1d7SZhang Rui u32 acpi_state = acpi_target_sleep_state; 5000ac1b1d7SZhang Rui int error; 5010ac1b1d7SZhang Rui 5020ac1b1d7SZhang Rui ACPI_FLUSH_CPU_CACHE(); 5030ac1b1d7SZhang Rui 5040ac1b1d7SZhang Rui switch (acpi_state) { 5050ac1b1d7SZhang Rui case ACPI_STATE_S1: 5060ac1b1d7SZhang Rui barrier(); 5070ac1b1d7SZhang Rui status = acpi_enter_sleep_state(acpi_state); 5080ac1b1d7SZhang Rui break; 5090ac1b1d7SZhang Rui 5100ac1b1d7SZhang Rui case ACPI_STATE_S3: 511d6a77eadSKonrad Rzeszutek Wilk if (!acpi_suspend_lowlevel) 512d6a77eadSKonrad Rzeszutek Wilk return -ENOSYS; 5130ac1b1d7SZhang Rui error = acpi_suspend_lowlevel(); 5140ac1b1d7SZhang Rui if (error) 5150ac1b1d7SZhang Rui return error; 5160ac1b1d7SZhang Rui pr_info(PREFIX "Low-level resume complete\n"); 5170ac1b1d7SZhang Rui break; 5180ac1b1d7SZhang Rui } 5190ac1b1d7SZhang Rui 5200ac1b1d7SZhang Rui /* This violates the spec but is required for bug compatibility. */ 5210ac1b1d7SZhang Rui acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); 5220ac1b1d7SZhang Rui 5230ac1b1d7SZhang Rui /* Reprogram control registers */ 5240ac1b1d7SZhang Rui acpi_leave_sleep_state_prep(acpi_state); 5250ac1b1d7SZhang Rui 5260ac1b1d7SZhang Rui /* ACPI 3.0 specs (P62) says that it's the responsibility 5270ac1b1d7SZhang Rui * of the OSPM to clear the status bit [ implying that the 5280ac1b1d7SZhang Rui * POWER_BUTTON event should not reach userspace ] 5290ac1b1d7SZhang Rui * 5300ac1b1d7SZhang Rui * However, we do generate a small hint for userspace in the form of 5310ac1b1d7SZhang Rui * a wakeup event. We flag this condition for now and generate the 5320ac1b1d7SZhang Rui * event later, as we're currently too early in resume to be able to 5330ac1b1d7SZhang Rui * generate wakeup events. 5340ac1b1d7SZhang Rui */ 5350ac1b1d7SZhang Rui if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) { 53651468a9dSAl Stone acpi_event_status pwr_btn_status = ACPI_EVENT_FLAG_DISABLED; 5370ac1b1d7SZhang Rui 5380ac1b1d7SZhang Rui acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status); 5390ac1b1d7SZhang Rui 5400ac1b1d7SZhang Rui if (pwr_btn_status & ACPI_EVENT_FLAG_SET) { 5410ac1b1d7SZhang Rui acpi_clear_event(ACPI_EVENT_POWER_BUTTON); 5420ac1b1d7SZhang Rui /* Flag for later */ 5430ac1b1d7SZhang Rui pwr_btn_event_pending = true; 5440ac1b1d7SZhang Rui } 5450ac1b1d7SZhang Rui } 5460ac1b1d7SZhang Rui 5470ac1b1d7SZhang Rui /* 5480ac1b1d7SZhang Rui * Disable and clear GPE status before interrupt is enabled. Some GPEs 5490ac1b1d7SZhang Rui * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. 5500ac1b1d7SZhang Rui * acpi_leave_sleep_state will reenable specific GPEs later 5510ac1b1d7SZhang Rui */ 5520ac1b1d7SZhang Rui acpi_disable_all_gpes(); 5530ac1b1d7SZhang Rui /* Allow EC transactions to happen. */ 5540ac1b1d7SZhang Rui acpi_ec_unblock_transactions_early(); 5550ac1b1d7SZhang Rui 5560ac1b1d7SZhang Rui suspend_nvs_restore(); 5570ac1b1d7SZhang Rui 5580ac1b1d7SZhang Rui return ACPI_SUCCESS(status) ? 0 : -EFAULT; 5590ac1b1d7SZhang Rui } 5600ac1b1d7SZhang Rui 5610ac1b1d7SZhang Rui static int acpi_suspend_state_valid(suspend_state_t pm_state) 5620ac1b1d7SZhang Rui { 5630ac1b1d7SZhang Rui u32 acpi_state; 5640ac1b1d7SZhang Rui 5650ac1b1d7SZhang Rui switch (pm_state) { 5660ac1b1d7SZhang Rui case PM_SUSPEND_ON: 5670ac1b1d7SZhang Rui case PM_SUSPEND_STANDBY: 5680ac1b1d7SZhang Rui case PM_SUSPEND_MEM: 5690ac1b1d7SZhang Rui acpi_state = acpi_suspend_states[pm_state]; 5700ac1b1d7SZhang Rui 5710ac1b1d7SZhang Rui return sleep_states[acpi_state]; 5720ac1b1d7SZhang Rui default: 5730ac1b1d7SZhang Rui return 0; 5740ac1b1d7SZhang Rui } 5750ac1b1d7SZhang Rui } 5760ac1b1d7SZhang Rui 5770ac1b1d7SZhang Rui static const struct platform_suspend_ops acpi_suspend_ops = { 5780ac1b1d7SZhang Rui .valid = acpi_suspend_state_valid, 5790ac1b1d7SZhang Rui .begin = acpi_suspend_begin, 5800ac1b1d7SZhang Rui .prepare_late = acpi_pm_prepare, 5810ac1b1d7SZhang Rui .enter = acpi_suspend_enter, 5820ac1b1d7SZhang Rui .wake = acpi_pm_finish, 5830ac1b1d7SZhang Rui .end = acpi_pm_end, 5840ac1b1d7SZhang Rui }; 5850ac1b1d7SZhang Rui 5860ac1b1d7SZhang Rui /** 5870ac1b1d7SZhang Rui * acpi_suspend_begin_old - Set the target system sleep state to the 5880ac1b1d7SZhang Rui * state associated with given @pm_state, if supported, and 5890ac1b1d7SZhang Rui * execute the _PTS control method. This function is used if the 5900ac1b1d7SZhang Rui * pre-ACPI 2.0 suspend ordering has been requested. 5910ac1b1d7SZhang Rui */ 5920ac1b1d7SZhang Rui static int acpi_suspend_begin_old(suspend_state_t pm_state) 5930ac1b1d7SZhang Rui { 5940ac1b1d7SZhang Rui int error = acpi_suspend_begin(pm_state); 5950ac1b1d7SZhang Rui if (!error) 5960ac1b1d7SZhang Rui error = __acpi_pm_prepare(); 5970ac1b1d7SZhang Rui 5980ac1b1d7SZhang Rui return error; 5990ac1b1d7SZhang Rui } 6000ac1b1d7SZhang Rui 6010ac1b1d7SZhang Rui /* 6020ac1b1d7SZhang Rui * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has 6030ac1b1d7SZhang Rui * been requested. 6040ac1b1d7SZhang Rui */ 6050ac1b1d7SZhang Rui static const struct platform_suspend_ops acpi_suspend_ops_old = { 6060ac1b1d7SZhang Rui .valid = acpi_suspend_state_valid, 6070ac1b1d7SZhang Rui .begin = acpi_suspend_begin_old, 6080ac1b1d7SZhang Rui .prepare_late = acpi_pm_pre_suspend, 6090ac1b1d7SZhang Rui .enter = acpi_suspend_enter, 6100ac1b1d7SZhang Rui .wake = acpi_pm_finish, 6110ac1b1d7SZhang Rui .end = acpi_pm_end, 6120ac1b1d7SZhang Rui .recover = acpi_pm_finish, 6130ac1b1d7SZhang Rui }; 61402040f0bSRafael J. Wysocki 61502040f0bSRafael J. Wysocki static void acpi_sleep_suspend_setup(void) 61602040f0bSRafael J. Wysocki { 61702040f0bSRafael J. Wysocki int i; 61802040f0bSRafael J. Wysocki 619a4e90bedSRafael J. Wysocki for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) 620a4e90bedSRafael J. Wysocki if (acpi_sleep_state_supported(i)) 62102040f0bSRafael J. Wysocki sleep_states[i] = 1; 62202040f0bSRafael J. Wysocki 62302040f0bSRafael J. Wysocki suspend_set_ops(old_suspend_ordering ? 62402040f0bSRafael J. Wysocki &acpi_suspend_ops_old : &acpi_suspend_ops); 62502040f0bSRafael J. Wysocki } 62602040f0bSRafael J. Wysocki #else /* !CONFIG_SUSPEND */ 62702040f0bSRafael J. Wysocki static inline void acpi_sleep_suspend_setup(void) {} 62802040f0bSRafael J. Wysocki #endif /* !CONFIG_SUSPEND */ 629d08ca2caSLen Brown 630d08ca2caSLen Brown #ifdef CONFIG_HIBERNATION 631d08ca2caSLen Brown static unsigned long s4_hardware_signature; 632d08ca2caSLen Brown static struct acpi_table_facs *facs; 633d08ca2caSLen Brown static bool nosigcheck; 634d08ca2caSLen Brown 635d08ca2caSLen Brown void __init acpi_no_s4_hw_signature(void) 636d08ca2caSLen Brown { 637d08ca2caSLen Brown nosigcheck = true; 638d08ca2caSLen Brown } 639d08ca2caSLen Brown 640d08ca2caSLen Brown static int acpi_hibernation_begin(void) 641d08ca2caSLen Brown { 642d08ca2caSLen Brown int error; 643d08ca2caSLen Brown 64472ad5d77SRafael J. Wysocki error = nvs_nosave ? 0 : suspend_nvs_alloc(); 645ad07277eSRafael J. Wysocki if (!error) 646ad07277eSRafael J. Wysocki acpi_pm_start(ACPI_STATE_S4); 647d08ca2caSLen Brown 648d08ca2caSLen Brown return error; 649d08ca2caSLen Brown } 650d08ca2caSLen Brown 651d08ca2caSLen Brown static int acpi_hibernation_enter(void) 652d08ca2caSLen Brown { 653d08ca2caSLen Brown acpi_status status = AE_OK; 654d08ca2caSLen Brown 655d08ca2caSLen Brown ACPI_FLUSH_CPU_CACHE(); 656d08ca2caSLen Brown 657d08ca2caSLen Brown /* This shouldn't return. If it returns, we have a problem */ 6583f6f49c7SLen Brown status = acpi_enter_sleep_state(ACPI_STATE_S4); 6593f6f49c7SLen Brown /* Reprogram control registers */ 6603f6f49c7SLen Brown acpi_leave_sleep_state_prep(ACPI_STATE_S4); 661d08ca2caSLen Brown 662d08ca2caSLen Brown return ACPI_SUCCESS(status) ? 0 : -EFAULT; 663d08ca2caSLen Brown } 664d08ca2caSLen Brown 665d08ca2caSLen Brown static void acpi_hibernation_leave(void) 666d08ca2caSLen Brown { 667d08ca2caSLen Brown /* 668d08ca2caSLen Brown * If ACPI is not enabled by the BIOS and the boot kernel, we need to 669d08ca2caSLen Brown * enable it here. 670d08ca2caSLen Brown */ 671d08ca2caSLen Brown acpi_enable(); 6723f6f49c7SLen Brown /* Reprogram control registers */ 6733f6f49c7SLen Brown acpi_leave_sleep_state_prep(ACPI_STATE_S4); 674d08ca2caSLen Brown /* Check the hardware signature */ 6755c551e62SOliver Neukum if (facs && s4_hardware_signature != facs->hardware_signature) 6765c551e62SOliver Neukum pr_crit("ACPI: Hardware changed while hibernated, success doubtful!\n"); 677d08ca2caSLen Brown /* Restore the NVS memory area */ 678dd4c4f17SMatthew Garrett suspend_nvs_restore(); 679d5a64513SRafael J. Wysocki /* Allow EC transactions to happen. */ 680fe955682SRafael J. Wysocki acpi_ec_unblock_transactions_early(); 681d08ca2caSLen Brown } 682d08ca2caSLen Brown 683d5a64513SRafael J. Wysocki static void acpi_pm_thaw(void) 684f6bb13aaSRafael J. Wysocki { 685fe955682SRafael J. Wysocki acpi_ec_unblock_transactions(); 686d08ca2caSLen Brown acpi_enable_all_runtime_gpes(); 687d08ca2caSLen Brown } 688d08ca2caSLen Brown 689073ef1f6SLionel Debroux static const struct platform_hibernation_ops acpi_hibernation_ops = { 690d08ca2caSLen Brown .begin = acpi_hibernation_begin, 691d08ca2caSLen Brown .end = acpi_pm_end, 692c5f7a1bbSRafael J. Wysocki .pre_snapshot = acpi_pm_prepare, 6932a6b6976SMatthew Garrett .finish = acpi_pm_finish, 694d08ca2caSLen Brown .prepare = acpi_pm_prepare, 695d08ca2caSLen Brown .enter = acpi_hibernation_enter, 696d08ca2caSLen Brown .leave = acpi_hibernation_leave, 697d5a64513SRafael J. Wysocki .pre_restore = acpi_pm_freeze, 698d5a64513SRafael J. Wysocki .restore_cleanup = acpi_pm_thaw, 699d08ca2caSLen Brown }; 700d08ca2caSLen Brown 701d08ca2caSLen Brown /** 702d08ca2caSLen Brown * acpi_hibernation_begin_old - Set the target system sleep state to 703d08ca2caSLen Brown * ACPI_STATE_S4 and execute the _PTS control method. This 704d08ca2caSLen Brown * function is used if the pre-ACPI 2.0 suspend ordering has been 705d08ca2caSLen Brown * requested. 706d08ca2caSLen Brown */ 707d08ca2caSLen Brown static int acpi_hibernation_begin_old(void) 708d08ca2caSLen Brown { 709d08ca2caSLen Brown int error; 710d08ca2caSLen Brown /* 711d08ca2caSLen Brown * The _TTS object should always be evaluated before the _PTS object. 712d08ca2caSLen Brown * When the old_suspended_ordering is true, the _PTS object is 713d08ca2caSLen Brown * evaluated in the acpi_sleep_prepare. 714d08ca2caSLen Brown */ 715d08ca2caSLen Brown acpi_sleep_tts_switch(ACPI_STATE_S4); 716d08ca2caSLen Brown 717d08ca2caSLen Brown error = acpi_sleep_prepare(ACPI_STATE_S4); 718d08ca2caSLen Brown 719d08ca2caSLen Brown if (!error) { 72072ad5d77SRafael J. Wysocki if (!nvs_nosave) 721dd4c4f17SMatthew Garrett error = suspend_nvs_alloc(); 722ad07277eSRafael J. Wysocki if (!error) { 723d08ca2caSLen Brown acpi_target_sleep_state = ACPI_STATE_S4; 724ad07277eSRafael J. Wysocki acpi_scan_lock_acquire(); 725ad07277eSRafael J. Wysocki } 726d08ca2caSLen Brown } 727d08ca2caSLen Brown return error; 728d08ca2caSLen Brown } 729d08ca2caSLen Brown 730d08ca2caSLen Brown /* 731d08ca2caSLen Brown * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has 732d08ca2caSLen Brown * been requested. 733d08ca2caSLen Brown */ 734073ef1f6SLionel Debroux static const struct platform_hibernation_ops acpi_hibernation_ops_old = { 735d08ca2caSLen Brown .begin = acpi_hibernation_begin_old, 736d08ca2caSLen Brown .end = acpi_pm_end, 737c5f7a1bbSRafael J. Wysocki .pre_snapshot = acpi_pm_pre_suspend, 738d5a64513SRafael J. Wysocki .prepare = acpi_pm_freeze, 7392a6b6976SMatthew Garrett .finish = acpi_pm_finish, 740d08ca2caSLen Brown .enter = acpi_hibernation_enter, 741d08ca2caSLen Brown .leave = acpi_hibernation_leave, 742d5a64513SRafael J. Wysocki .pre_restore = acpi_pm_freeze, 743d5a64513SRafael J. Wysocki .restore_cleanup = acpi_pm_thaw, 744d08ca2caSLen Brown .recover = acpi_pm_finish, 745d08ca2caSLen Brown }; 74602040f0bSRafael J. Wysocki 74702040f0bSRafael J. Wysocki static void acpi_sleep_hibernate_setup(void) 74802040f0bSRafael J. Wysocki { 749a4e90bedSRafael J. Wysocki if (!acpi_sleep_state_supported(ACPI_STATE_S4)) 75002040f0bSRafael J. Wysocki return; 75102040f0bSRafael J. Wysocki 75202040f0bSRafael J. Wysocki hibernation_set_ops(old_suspend_ordering ? 75302040f0bSRafael J. Wysocki &acpi_hibernation_ops_old : &acpi_hibernation_ops); 75402040f0bSRafael J. Wysocki sleep_states[ACPI_STATE_S4] = 1; 75502040f0bSRafael J. Wysocki if (nosigcheck) 75602040f0bSRafael J. Wysocki return; 75702040f0bSRafael J. Wysocki 75802040f0bSRafael J. Wysocki acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs); 75902040f0bSRafael J. Wysocki if (facs) 76002040f0bSRafael J. Wysocki s4_hardware_signature = facs->hardware_signature; 76102040f0bSRafael J. Wysocki } 76202040f0bSRafael J. Wysocki #else /* !CONFIG_HIBERNATION */ 76302040f0bSRafael J. Wysocki static inline void acpi_sleep_hibernate_setup(void) {} 76402040f0bSRafael J. Wysocki #endif /* !CONFIG_HIBERNATION */ 765d08ca2caSLen Brown 766d08ca2caSLen Brown int acpi_suspend(u32 acpi_state) 767d08ca2caSLen Brown { 768d08ca2caSLen Brown suspend_state_t states[] = { 769d08ca2caSLen Brown [1] = PM_SUSPEND_STANDBY, 770d08ca2caSLen Brown [3] = PM_SUSPEND_MEM, 771d08ca2caSLen Brown [5] = PM_SUSPEND_MAX 772d08ca2caSLen Brown }; 773d08ca2caSLen Brown 774d08ca2caSLen Brown if (acpi_state < 6 && states[acpi_state]) 775d08ca2caSLen Brown return pm_suspend(states[acpi_state]); 776d08ca2caSLen Brown if (acpi_state == 4) 777d08ca2caSLen Brown return hibernate(); 778d08ca2caSLen Brown return -EINVAL; 779d08ca2caSLen Brown } 780d08ca2caSLen Brown 781d08ca2caSLen Brown static void acpi_power_off_prepare(void) 782d08ca2caSLen Brown { 783d08ca2caSLen Brown /* Prepare to power off the system */ 784d08ca2caSLen Brown acpi_sleep_prepare(ACPI_STATE_S5); 785d08ca2caSLen Brown acpi_disable_all_gpes(); 786d08ca2caSLen Brown } 787d08ca2caSLen Brown 788d08ca2caSLen Brown static void acpi_power_off(void) 789d08ca2caSLen Brown { 790d08ca2caSLen Brown /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ 7914d939155SFrank Seidel printk(KERN_DEBUG "%s called\n", __func__); 792d08ca2caSLen Brown local_irq_disable(); 7933f6f49c7SLen Brown acpi_enter_sleep_state(ACPI_STATE_S5); 79496f15efcSLen Brown } 79596f15efcSLen Brown 796d08ca2caSLen Brown int __init acpi_sleep_init(void) 797d08ca2caSLen Brown { 798ed4cf5b2SJoe Perches char supported[ACPI_S_STATE_COUNT * 3 + 1]; 799ed4cf5b2SJoe Perches char *pos = supported; 800ed4cf5b2SJoe Perches int i; 801d08ca2caSLen Brown 8020ac1b1d7SZhang Rui acpi_sleep_dmi_check(); 8030ac1b1d7SZhang Rui 804d08ca2caSLen Brown sleep_states[ACPI_STATE_S0] = 1; 805d08ca2caSLen Brown 80602040f0bSRafael J. Wysocki acpi_sleep_suspend_setup(); 80702040f0bSRafael J. Wysocki acpi_sleep_hibernate_setup(); 808d08ca2caSLen Brown 809a4e90bedSRafael J. Wysocki if (acpi_sleep_state_supported(ACPI_STATE_S5)) { 810d08ca2caSLen Brown sleep_states[ACPI_STATE_S5] = 1; 811d08ca2caSLen Brown pm_power_off_prepare = acpi_power_off_prepare; 812d08ca2caSLen Brown pm_power_off = acpi_power_off; 813d08ca2caSLen Brown } 814ed4cf5b2SJoe Perches 815ed4cf5b2SJoe Perches supported[0] = 0; 816ed4cf5b2SJoe Perches for (i = 0; i < ACPI_S_STATE_COUNT; i++) { 817ed4cf5b2SJoe Perches if (sleep_states[i]) 818ed4cf5b2SJoe Perches pos += sprintf(pos, " S%d", i); 819ed4cf5b2SJoe Perches } 820ed4cf5b2SJoe Perches pr_info(PREFIX "(supports%s)\n", supported); 821ed4cf5b2SJoe Perches 822d08ca2caSLen Brown /* 823d08ca2caSLen Brown * Register the tts_notifier to reboot notifier list so that the _TTS 824d08ca2caSLen Brown * object can also be evaluated when the system enters S5. 825d08ca2caSLen Brown */ 826d08ca2caSLen Brown register_reboot_notifier(&tts_notifier); 827d08ca2caSLen Brown return 0; 828d08ca2caSLen Brown } 829