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> 19d08ca2caSLen Brown 20d08ca2caSLen Brown #include <asm/io.h> 21d08ca2caSLen Brown 22d08ca2caSLen Brown #include <acpi/acpi_bus.h> 23d08ca2caSLen Brown #include <acpi/acpi_drivers.h> 24d08ca2caSLen Brown #include "sleep.h" 25d08ca2caSLen Brown 26d08ca2caSLen Brown u8 sleep_states[ACPI_S_STATE_COUNT]; 27d08ca2caSLen Brown 28d08ca2caSLen Brown static void acpi_sleep_tts_switch(u32 acpi_state) 29d08ca2caSLen Brown { 30d08ca2caSLen Brown union acpi_object in_arg = { ACPI_TYPE_INTEGER }; 31d08ca2caSLen Brown struct acpi_object_list arg_list = { 1, &in_arg }; 32d08ca2caSLen Brown acpi_status status = AE_OK; 33d08ca2caSLen Brown 34d08ca2caSLen Brown in_arg.integer.value = acpi_state; 35d08ca2caSLen Brown status = acpi_evaluate_object(NULL, "\\_TTS", &arg_list, NULL); 36d08ca2caSLen Brown if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { 37d08ca2caSLen Brown /* 38d08ca2caSLen Brown * OS can't evaluate the _TTS object correctly. Some warning 39d08ca2caSLen Brown * message will be printed. But it won't break anything. 40d08ca2caSLen Brown */ 41d08ca2caSLen Brown printk(KERN_NOTICE "Failure in evaluating _TTS object\n"); 42d08ca2caSLen Brown } 43d08ca2caSLen Brown } 44d08ca2caSLen Brown 45d08ca2caSLen Brown static int tts_notify_reboot(struct notifier_block *this, 46d08ca2caSLen Brown unsigned long code, void *x) 47d08ca2caSLen Brown { 48d08ca2caSLen Brown acpi_sleep_tts_switch(ACPI_STATE_S5); 49d08ca2caSLen Brown return NOTIFY_DONE; 50d08ca2caSLen Brown } 51d08ca2caSLen Brown 52d08ca2caSLen Brown static struct notifier_block tts_notifier = { 53d08ca2caSLen Brown .notifier_call = tts_notify_reboot, 54d08ca2caSLen Brown .next = NULL, 55d08ca2caSLen Brown .priority = 0, 56d08ca2caSLen Brown }; 57d08ca2caSLen Brown 58d08ca2caSLen Brown static int acpi_sleep_prepare(u32 acpi_state) 59d08ca2caSLen Brown { 60d08ca2caSLen Brown #ifdef CONFIG_ACPI_SLEEP 61d08ca2caSLen Brown /* do we have a wakeup address for S2 and S3? */ 62d08ca2caSLen Brown if (acpi_state == ACPI_STATE_S3) { 63d08ca2caSLen Brown if (!acpi_wakeup_address) { 64d08ca2caSLen Brown return -EFAULT; 65d08ca2caSLen Brown } 66d08ca2caSLen Brown acpi_set_firmware_waking_vector( 67d08ca2caSLen Brown (acpi_physical_address)acpi_wakeup_address); 68d08ca2caSLen Brown 69d08ca2caSLen Brown } 70d08ca2caSLen Brown ACPI_FLUSH_CPU_CACHE(); 71d08ca2caSLen Brown acpi_enable_wakeup_device_prep(acpi_state); 72d08ca2caSLen Brown #endif 73d08ca2caSLen Brown printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n", 74d08ca2caSLen Brown acpi_state); 75d08ca2caSLen Brown acpi_enter_sleep_state_prep(acpi_state); 76d08ca2caSLen Brown return 0; 77d08ca2caSLen Brown } 78d08ca2caSLen Brown 79d08ca2caSLen Brown #ifdef CONFIG_ACPI_SLEEP 80d08ca2caSLen Brown static u32 acpi_target_sleep_state = ACPI_STATE_S0; 81d08ca2caSLen Brown /* 82d08ca2caSLen Brown * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the 83d08ca2caSLen Brown * user to request that behavior by using the 'acpi_old_suspend_ordering' 84d08ca2caSLen Brown * kernel command line option that causes the following variable to be set. 85d08ca2caSLen Brown */ 86d08ca2caSLen Brown static bool old_suspend_ordering; 87d08ca2caSLen Brown 88d08ca2caSLen Brown void __init acpi_old_suspend_ordering(void) 89d08ca2caSLen Brown { 90d08ca2caSLen Brown old_suspend_ordering = true; 91d08ca2caSLen Brown } 92d08ca2caSLen Brown 93d08ca2caSLen Brown /* 94d08ca2caSLen Brown * According to the ACPI specification the BIOS should make sure that ACPI is 95d08ca2caSLen Brown * enabled and SCI_EN bit is set on wake-up from S1 - S3 sleep states. Still, 96d08ca2caSLen Brown * some BIOSes don't do that and therefore we use acpi_enable() to enable ACPI 97d08ca2caSLen Brown * on such systems during resume. Unfortunately that doesn't help in 98d08ca2caSLen Brown * particularly pathological cases in which SCI_EN has to be set directly on 99d08ca2caSLen Brown * resume, although the specification states very clearly that this flag is 100d08ca2caSLen Brown * owned by the hardware. The set_sci_en_on_resume variable will be set in such 101d08ca2caSLen Brown * cases. 102d08ca2caSLen Brown */ 103d08ca2caSLen Brown static bool set_sci_en_on_resume; 104d08ca2caSLen Brown /* 105d08ca2caSLen Brown * The ACPI specification wants us to save NVS memory regions during hibernation 106d08ca2caSLen Brown * and to restore them during the subsequent resume. However, it is not certain 107d08ca2caSLen Brown * if this mechanism is going to work on all machines, so we allow the user to 108d08ca2caSLen Brown * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line 109d08ca2caSLen Brown * option. 110d08ca2caSLen Brown */ 111d08ca2caSLen Brown static bool s4_no_nvs; 112d08ca2caSLen Brown 113d08ca2caSLen Brown void __init acpi_s4_no_nvs(void) 114d08ca2caSLen Brown { 115d08ca2caSLen Brown s4_no_nvs = true; 116d08ca2caSLen Brown } 117d08ca2caSLen Brown 118d08ca2caSLen Brown /** 119d08ca2caSLen Brown * acpi_pm_disable_gpes - Disable the GPEs. 120d08ca2caSLen Brown */ 121d08ca2caSLen Brown static int acpi_pm_disable_gpes(void) 122d08ca2caSLen Brown { 123d08ca2caSLen Brown acpi_disable_all_gpes(); 124d08ca2caSLen Brown return 0; 125d08ca2caSLen Brown } 126d08ca2caSLen Brown 127d08ca2caSLen Brown /** 128d08ca2caSLen Brown * __acpi_pm_prepare - Prepare the platform to enter the target state. 129d08ca2caSLen Brown * 130d08ca2caSLen Brown * If necessary, set the firmware waking vector and do arch-specific 131d08ca2caSLen Brown * nastiness to get the wakeup code to the waking vector. 132d08ca2caSLen Brown */ 133d08ca2caSLen Brown static int __acpi_pm_prepare(void) 134d08ca2caSLen Brown { 135d08ca2caSLen Brown int error = acpi_sleep_prepare(acpi_target_sleep_state); 136d08ca2caSLen Brown 137d08ca2caSLen Brown if (error) 138d08ca2caSLen Brown acpi_target_sleep_state = ACPI_STATE_S0; 139d08ca2caSLen Brown return error; 140d08ca2caSLen Brown } 141d08ca2caSLen Brown 142d08ca2caSLen Brown /** 143d08ca2caSLen Brown * acpi_pm_prepare - Prepare the platform to enter the target sleep 144d08ca2caSLen Brown * state and disable the GPEs. 145d08ca2caSLen Brown */ 146d08ca2caSLen Brown static int acpi_pm_prepare(void) 147d08ca2caSLen Brown { 148d08ca2caSLen Brown int error = __acpi_pm_prepare(); 149d08ca2caSLen Brown 150d08ca2caSLen Brown if (!error) 151d08ca2caSLen Brown acpi_disable_all_gpes(); 152d08ca2caSLen Brown return error; 153d08ca2caSLen Brown } 154d08ca2caSLen Brown 155d08ca2caSLen Brown /** 156d08ca2caSLen Brown * acpi_pm_finish - Instruct the platform to leave a sleep state. 157d08ca2caSLen Brown * 158d08ca2caSLen Brown * This is called after we wake back up (or if entering the sleep state 159d08ca2caSLen Brown * failed). 160d08ca2caSLen Brown */ 161d08ca2caSLen Brown static void acpi_pm_finish(void) 162d08ca2caSLen Brown { 163d08ca2caSLen Brown u32 acpi_state = acpi_target_sleep_state; 164d08ca2caSLen Brown 165d08ca2caSLen Brown if (acpi_state == ACPI_STATE_S0) 166d08ca2caSLen Brown return; 167d08ca2caSLen Brown 168d08ca2caSLen Brown printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n", 169d08ca2caSLen Brown acpi_state); 170d08ca2caSLen Brown acpi_disable_wakeup_device(acpi_state); 171d08ca2caSLen Brown acpi_leave_sleep_state(acpi_state); 172d08ca2caSLen Brown 173d08ca2caSLen Brown /* reset firmware waking vector */ 174d08ca2caSLen Brown acpi_set_firmware_waking_vector((acpi_physical_address) 0); 175d08ca2caSLen Brown 176d08ca2caSLen Brown acpi_target_sleep_state = ACPI_STATE_S0; 177d08ca2caSLen Brown } 178d08ca2caSLen Brown 179d08ca2caSLen Brown /** 180d08ca2caSLen Brown * acpi_pm_end - Finish up suspend sequence. 181d08ca2caSLen Brown */ 182d08ca2caSLen Brown static void acpi_pm_end(void) 183d08ca2caSLen Brown { 184d08ca2caSLen Brown /* 185d08ca2caSLen Brown * This is necessary in case acpi_pm_finish() is not called during a 186d08ca2caSLen Brown * failing transition to a sleep state. 187d08ca2caSLen Brown */ 188d08ca2caSLen Brown acpi_target_sleep_state = ACPI_STATE_S0; 189d08ca2caSLen Brown acpi_sleep_tts_switch(acpi_target_sleep_state); 190d08ca2caSLen Brown } 191d08ca2caSLen Brown #else /* !CONFIG_ACPI_SLEEP */ 192d08ca2caSLen Brown #define acpi_target_sleep_state ACPI_STATE_S0 193d08ca2caSLen Brown #endif /* CONFIG_ACPI_SLEEP */ 194d08ca2caSLen Brown 195d08ca2caSLen Brown #ifdef CONFIG_SUSPEND 196d08ca2caSLen Brown extern void do_suspend_lowlevel(void); 197d08ca2caSLen Brown 198d08ca2caSLen Brown static u32 acpi_suspend_states[] = { 199d08ca2caSLen Brown [PM_SUSPEND_ON] = ACPI_STATE_S0, 200d08ca2caSLen Brown [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, 201d08ca2caSLen Brown [PM_SUSPEND_MEM] = ACPI_STATE_S3, 202d08ca2caSLen Brown [PM_SUSPEND_MAX] = ACPI_STATE_S5 203d08ca2caSLen Brown }; 204d08ca2caSLen Brown 205d08ca2caSLen Brown /** 206d08ca2caSLen Brown * acpi_suspend_begin - Set the target system sleep state to the state 207d08ca2caSLen Brown * associated with given @pm_state, if supported. 208d08ca2caSLen Brown */ 209d08ca2caSLen Brown static int acpi_suspend_begin(suspend_state_t pm_state) 210d08ca2caSLen Brown { 211d08ca2caSLen Brown u32 acpi_state = acpi_suspend_states[pm_state]; 212d08ca2caSLen Brown int error = 0; 213d08ca2caSLen Brown 214d08ca2caSLen Brown if (sleep_states[acpi_state]) { 215d08ca2caSLen Brown acpi_target_sleep_state = acpi_state; 216d08ca2caSLen Brown acpi_sleep_tts_switch(acpi_target_sleep_state); 217d08ca2caSLen Brown } else { 218d08ca2caSLen Brown printk(KERN_ERR "ACPI does not support this state: %d\n", 219d08ca2caSLen Brown pm_state); 220d08ca2caSLen Brown error = -ENOSYS; 221d08ca2caSLen Brown } 222d08ca2caSLen Brown return error; 223d08ca2caSLen Brown } 224d08ca2caSLen Brown 225d08ca2caSLen Brown /** 226d08ca2caSLen Brown * acpi_suspend_enter - Actually enter a sleep state. 227d08ca2caSLen Brown * @pm_state: ignored 228d08ca2caSLen Brown * 229d08ca2caSLen Brown * Flush caches and go to sleep. For STR we have to call arch-specific 230d08ca2caSLen Brown * assembly, which in turn call acpi_enter_sleep_state(). 231d08ca2caSLen Brown * It's unfortunate, but it works. Please fix if you're feeling frisky. 232d08ca2caSLen Brown */ 233d08ca2caSLen Brown static int acpi_suspend_enter(suspend_state_t pm_state) 234d08ca2caSLen Brown { 235d08ca2caSLen Brown acpi_status status = AE_OK; 236d08ca2caSLen Brown unsigned long flags = 0; 237d08ca2caSLen Brown u32 acpi_state = acpi_target_sleep_state; 238d08ca2caSLen Brown 239d08ca2caSLen Brown ACPI_FLUSH_CPU_CACHE(); 240d08ca2caSLen Brown 241d08ca2caSLen Brown /* Do arch specific saving of state. */ 242d08ca2caSLen Brown if (acpi_state == ACPI_STATE_S3) { 243d08ca2caSLen Brown int error = acpi_save_state_mem(); 244d08ca2caSLen Brown 245d08ca2caSLen Brown if (error) 246d08ca2caSLen Brown return error; 247d08ca2caSLen Brown } 248d08ca2caSLen Brown 249d08ca2caSLen Brown local_irq_save(flags); 250d08ca2caSLen Brown acpi_enable_wakeup_device(acpi_state); 251d08ca2caSLen Brown switch (acpi_state) { 252d08ca2caSLen Brown case ACPI_STATE_S1: 253d08ca2caSLen Brown barrier(); 254d08ca2caSLen Brown status = acpi_enter_sleep_state(acpi_state); 255d08ca2caSLen Brown break; 256d08ca2caSLen Brown 257d08ca2caSLen Brown case ACPI_STATE_S3: 258d08ca2caSLen Brown do_suspend_lowlevel(); 259d08ca2caSLen Brown break; 260d08ca2caSLen Brown } 261d08ca2caSLen Brown 262d08ca2caSLen Brown /* If ACPI is not enabled by the BIOS, we need to enable it here. */ 263d08ca2caSLen Brown if (set_sci_en_on_resume) 264d08ca2caSLen Brown acpi_set_register(ACPI_BITREG_SCI_ENABLE, 1); 265d08ca2caSLen Brown else 266d08ca2caSLen Brown acpi_enable(); 267d08ca2caSLen Brown 268d08ca2caSLen Brown /* Reprogram control registers and execute _BFS */ 269d08ca2caSLen Brown acpi_leave_sleep_state_prep(acpi_state); 270d08ca2caSLen Brown 271d08ca2caSLen Brown /* ACPI 3.0 specs (P62) says that it's the responsibility 272d08ca2caSLen Brown * of the OSPM to clear the status bit [ implying that the 273d08ca2caSLen Brown * POWER_BUTTON event should not reach userspace ] 274d08ca2caSLen Brown */ 275d08ca2caSLen Brown if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) 276d08ca2caSLen Brown acpi_clear_event(ACPI_EVENT_POWER_BUTTON); 277d08ca2caSLen Brown 278d08ca2caSLen Brown /* 279d08ca2caSLen Brown * Disable and clear GPE status before interrupt is enabled. Some GPEs 280d08ca2caSLen Brown * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. 281d08ca2caSLen Brown * acpi_leave_sleep_state will reenable specific GPEs later 282d08ca2caSLen Brown */ 283d08ca2caSLen Brown acpi_disable_all_gpes(); 284d08ca2caSLen Brown 285d08ca2caSLen Brown local_irq_restore(flags); 286d08ca2caSLen Brown printk(KERN_DEBUG "Back to C!\n"); 287d08ca2caSLen Brown 288d08ca2caSLen Brown /* restore processor state */ 289d08ca2caSLen Brown if (acpi_state == ACPI_STATE_S3) 290d08ca2caSLen Brown acpi_restore_state_mem(); 291d08ca2caSLen Brown 292d08ca2caSLen Brown return ACPI_SUCCESS(status) ? 0 : -EFAULT; 293d08ca2caSLen Brown } 294d08ca2caSLen Brown 295d08ca2caSLen Brown static int acpi_suspend_state_valid(suspend_state_t pm_state) 296d08ca2caSLen Brown { 297d08ca2caSLen Brown u32 acpi_state; 298d08ca2caSLen Brown 299d08ca2caSLen Brown switch (pm_state) { 300d08ca2caSLen Brown case PM_SUSPEND_ON: 301d08ca2caSLen Brown case PM_SUSPEND_STANDBY: 302d08ca2caSLen Brown case PM_SUSPEND_MEM: 303d08ca2caSLen Brown acpi_state = acpi_suspend_states[pm_state]; 304d08ca2caSLen Brown 305d08ca2caSLen Brown return sleep_states[acpi_state]; 306d08ca2caSLen Brown default: 307d08ca2caSLen Brown return 0; 308d08ca2caSLen Brown } 309d08ca2caSLen Brown } 310d08ca2caSLen Brown 311d08ca2caSLen Brown static struct platform_suspend_ops acpi_suspend_ops = { 312d08ca2caSLen Brown .valid = acpi_suspend_state_valid, 313d08ca2caSLen Brown .begin = acpi_suspend_begin, 314d08ca2caSLen Brown .prepare = acpi_pm_prepare, 315d08ca2caSLen Brown .enter = acpi_suspend_enter, 316d08ca2caSLen Brown .finish = acpi_pm_finish, 317d08ca2caSLen Brown .end = acpi_pm_end, 318d08ca2caSLen Brown }; 319d08ca2caSLen Brown 320d08ca2caSLen Brown /** 321d08ca2caSLen Brown * acpi_suspend_begin_old - Set the target system sleep state to the 322d08ca2caSLen Brown * state associated with given @pm_state, if supported, and 323d08ca2caSLen Brown * execute the _PTS control method. This function is used if the 324d08ca2caSLen Brown * pre-ACPI 2.0 suspend ordering has been requested. 325d08ca2caSLen Brown */ 326d08ca2caSLen Brown static int acpi_suspend_begin_old(suspend_state_t pm_state) 327d08ca2caSLen Brown { 328d08ca2caSLen Brown int error = acpi_suspend_begin(pm_state); 329d08ca2caSLen Brown 330d08ca2caSLen Brown if (!error) 331d08ca2caSLen Brown error = __acpi_pm_prepare(); 332d08ca2caSLen Brown return error; 333d08ca2caSLen Brown } 334d08ca2caSLen Brown 335d08ca2caSLen Brown /* 336d08ca2caSLen Brown * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has 337d08ca2caSLen Brown * been requested. 338d08ca2caSLen Brown */ 339d08ca2caSLen Brown static struct platform_suspend_ops acpi_suspend_ops_old = { 340d08ca2caSLen Brown .valid = acpi_suspend_state_valid, 341d08ca2caSLen Brown .begin = acpi_suspend_begin_old, 342d08ca2caSLen Brown .prepare = acpi_pm_disable_gpes, 343d08ca2caSLen Brown .enter = acpi_suspend_enter, 344d08ca2caSLen Brown .finish = acpi_pm_finish, 345d08ca2caSLen Brown .end = acpi_pm_end, 346d08ca2caSLen Brown .recover = acpi_pm_finish, 347d08ca2caSLen Brown }; 348d08ca2caSLen Brown 349d08ca2caSLen Brown static int __init init_old_suspend_ordering(const struct dmi_system_id *d) 350d08ca2caSLen Brown { 351d08ca2caSLen Brown old_suspend_ordering = true; 352d08ca2caSLen Brown return 0; 353d08ca2caSLen Brown } 354d08ca2caSLen Brown 355d08ca2caSLen Brown static int __init init_set_sci_en_on_resume(const struct dmi_system_id *d) 356d08ca2caSLen Brown { 357d08ca2caSLen Brown set_sci_en_on_resume = true; 358d08ca2caSLen Brown return 0; 359d08ca2caSLen Brown } 360d08ca2caSLen Brown 361d08ca2caSLen Brown static struct dmi_system_id __initdata acpisleep_dmi_table[] = { 362d08ca2caSLen Brown { 363d08ca2caSLen Brown .callback = init_old_suspend_ordering, 364d08ca2caSLen Brown .ident = "Abit KN9 (nForce4 variant)", 365d08ca2caSLen Brown .matches = { 366d08ca2caSLen Brown DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"), 367d08ca2caSLen Brown DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"), 368d08ca2caSLen Brown }, 369d08ca2caSLen Brown }, 370d08ca2caSLen Brown { 371d08ca2caSLen Brown .callback = init_old_suspend_ordering, 372d08ca2caSLen Brown .ident = "HP xw4600 Workstation", 373d08ca2caSLen Brown .matches = { 374d08ca2caSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 375d08ca2caSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"), 376d08ca2caSLen Brown }, 377d08ca2caSLen Brown }, 378d08ca2caSLen Brown { 379d08ca2caSLen Brown .callback = init_set_sci_en_on_resume, 380d08ca2caSLen Brown .ident = "Apple MacBook 1,1", 381d08ca2caSLen Brown .matches = { 382d08ca2caSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), 383d08ca2caSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"), 384d08ca2caSLen Brown }, 385d08ca2caSLen Brown }, 386d08ca2caSLen Brown { 387d08ca2caSLen Brown .callback = init_set_sci_en_on_resume, 388d08ca2caSLen Brown .ident = "Apple MacMini 1,1", 389d08ca2caSLen Brown .matches = { 390d08ca2caSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), 391d08ca2caSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"), 392d08ca2caSLen Brown }, 393d08ca2caSLen Brown }, 394d08ca2caSLen Brown {}, 395d08ca2caSLen Brown }; 396d08ca2caSLen Brown #endif /* CONFIG_SUSPEND */ 397d08ca2caSLen Brown 398d08ca2caSLen Brown #ifdef CONFIG_HIBERNATION 399d08ca2caSLen Brown static unsigned long s4_hardware_signature; 400d08ca2caSLen Brown static struct acpi_table_facs *facs; 401d08ca2caSLen Brown static bool nosigcheck; 402d08ca2caSLen Brown 403d08ca2caSLen Brown void __init acpi_no_s4_hw_signature(void) 404d08ca2caSLen Brown { 405d08ca2caSLen Brown nosigcheck = true; 406d08ca2caSLen Brown } 407d08ca2caSLen Brown 408d08ca2caSLen Brown static int acpi_hibernation_begin(void) 409d08ca2caSLen Brown { 410d08ca2caSLen Brown int error; 411d08ca2caSLen Brown 412d08ca2caSLen Brown error = s4_no_nvs ? 0 : hibernate_nvs_alloc(); 413d08ca2caSLen Brown if (!error) { 414d08ca2caSLen Brown acpi_target_sleep_state = ACPI_STATE_S4; 415d08ca2caSLen Brown acpi_sleep_tts_switch(acpi_target_sleep_state); 416d08ca2caSLen Brown } 417d08ca2caSLen Brown 418d08ca2caSLen Brown return error; 419d08ca2caSLen Brown } 420d08ca2caSLen Brown 421d08ca2caSLen Brown static int acpi_hibernation_pre_snapshot(void) 422d08ca2caSLen Brown { 423d08ca2caSLen Brown int error = acpi_pm_prepare(); 424d08ca2caSLen Brown 425d08ca2caSLen Brown if (!error) 426d08ca2caSLen Brown hibernate_nvs_save(); 427d08ca2caSLen Brown 428d08ca2caSLen Brown return error; 429d08ca2caSLen Brown } 430d08ca2caSLen Brown 431d08ca2caSLen Brown static int acpi_hibernation_enter(void) 432d08ca2caSLen Brown { 433d08ca2caSLen Brown acpi_status status = AE_OK; 434d08ca2caSLen Brown unsigned long flags = 0; 435d08ca2caSLen Brown 436d08ca2caSLen Brown ACPI_FLUSH_CPU_CACHE(); 437d08ca2caSLen Brown 438d08ca2caSLen Brown local_irq_save(flags); 439d08ca2caSLen Brown acpi_enable_wakeup_device(ACPI_STATE_S4); 440d08ca2caSLen Brown /* This shouldn't return. If it returns, we have a problem */ 441d08ca2caSLen Brown status = acpi_enter_sleep_state(ACPI_STATE_S4); 442d08ca2caSLen Brown /* Reprogram control registers and execute _BFS */ 443d08ca2caSLen Brown acpi_leave_sleep_state_prep(ACPI_STATE_S4); 444d08ca2caSLen Brown local_irq_restore(flags); 445d08ca2caSLen Brown 446d08ca2caSLen Brown return ACPI_SUCCESS(status) ? 0 : -EFAULT; 447d08ca2caSLen Brown } 448d08ca2caSLen Brown 449d08ca2caSLen Brown static void acpi_hibernation_finish(void) 450d08ca2caSLen Brown { 451d08ca2caSLen Brown hibernate_nvs_free(); 452d08ca2caSLen Brown acpi_pm_finish(); 453d08ca2caSLen Brown } 454d08ca2caSLen Brown 455d08ca2caSLen Brown static void acpi_hibernation_leave(void) 456d08ca2caSLen Brown { 457d08ca2caSLen Brown /* 458d08ca2caSLen Brown * If ACPI is not enabled by the BIOS and the boot kernel, we need to 459d08ca2caSLen Brown * enable it here. 460d08ca2caSLen Brown */ 461d08ca2caSLen Brown acpi_enable(); 462d08ca2caSLen Brown /* Reprogram control registers and execute _BFS */ 463d08ca2caSLen Brown acpi_leave_sleep_state_prep(ACPI_STATE_S4); 464d08ca2caSLen Brown /* Check the hardware signature */ 465d08ca2caSLen Brown if (facs && s4_hardware_signature != facs->hardware_signature) { 466d08ca2caSLen Brown printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " 467d08ca2caSLen Brown "cannot resume!\n"); 468d08ca2caSLen Brown panic("ACPI S4 hardware signature mismatch"); 469d08ca2caSLen Brown } 470d08ca2caSLen Brown /* Restore the NVS memory area */ 471d08ca2caSLen Brown hibernate_nvs_restore(); 472d08ca2caSLen Brown } 473d08ca2caSLen Brown 474d08ca2caSLen Brown static void acpi_pm_enable_gpes(void) 475d08ca2caSLen Brown { 476d08ca2caSLen Brown acpi_enable_all_runtime_gpes(); 477d08ca2caSLen Brown } 478d08ca2caSLen Brown 479d08ca2caSLen Brown static struct platform_hibernation_ops acpi_hibernation_ops = { 480d08ca2caSLen Brown .begin = acpi_hibernation_begin, 481d08ca2caSLen Brown .end = acpi_pm_end, 482d08ca2caSLen Brown .pre_snapshot = acpi_hibernation_pre_snapshot, 483d08ca2caSLen Brown .finish = acpi_hibernation_finish, 484d08ca2caSLen Brown .prepare = acpi_pm_prepare, 485d08ca2caSLen Brown .enter = acpi_hibernation_enter, 486d08ca2caSLen Brown .leave = acpi_hibernation_leave, 487d08ca2caSLen Brown .pre_restore = acpi_pm_disable_gpes, 488d08ca2caSLen Brown .restore_cleanup = acpi_pm_enable_gpes, 489d08ca2caSLen Brown }; 490d08ca2caSLen Brown 491d08ca2caSLen Brown /** 492d08ca2caSLen Brown * acpi_hibernation_begin_old - Set the target system sleep state to 493d08ca2caSLen Brown * ACPI_STATE_S4 and execute the _PTS control method. This 494d08ca2caSLen Brown * function is used if the pre-ACPI 2.0 suspend ordering has been 495d08ca2caSLen Brown * requested. 496d08ca2caSLen Brown */ 497d08ca2caSLen Brown static int acpi_hibernation_begin_old(void) 498d08ca2caSLen Brown { 499d08ca2caSLen Brown int error; 500d08ca2caSLen Brown /* 501d08ca2caSLen Brown * The _TTS object should always be evaluated before the _PTS object. 502d08ca2caSLen Brown * When the old_suspended_ordering is true, the _PTS object is 503d08ca2caSLen Brown * evaluated in the acpi_sleep_prepare. 504d08ca2caSLen Brown */ 505d08ca2caSLen Brown acpi_sleep_tts_switch(ACPI_STATE_S4); 506d08ca2caSLen Brown 507d08ca2caSLen Brown error = acpi_sleep_prepare(ACPI_STATE_S4); 508d08ca2caSLen Brown 509d08ca2caSLen Brown if (!error) { 510d08ca2caSLen Brown if (!s4_no_nvs) 511d08ca2caSLen Brown error = hibernate_nvs_alloc(); 512d08ca2caSLen Brown if (!error) 513d08ca2caSLen Brown acpi_target_sleep_state = ACPI_STATE_S4; 514d08ca2caSLen Brown } 515d08ca2caSLen Brown return error; 516d08ca2caSLen Brown } 517d08ca2caSLen Brown 518d08ca2caSLen Brown static int acpi_hibernation_pre_snapshot_old(void) 519d08ca2caSLen Brown { 520d08ca2caSLen Brown int error = acpi_pm_disable_gpes(); 521d08ca2caSLen Brown 522d08ca2caSLen Brown if (!error) 523d08ca2caSLen Brown hibernate_nvs_save(); 524d08ca2caSLen Brown 525d08ca2caSLen Brown return error; 526d08ca2caSLen Brown } 527d08ca2caSLen Brown 528d08ca2caSLen Brown /* 529d08ca2caSLen Brown * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has 530d08ca2caSLen Brown * been requested. 531d08ca2caSLen Brown */ 532d08ca2caSLen Brown static struct platform_hibernation_ops acpi_hibernation_ops_old = { 533d08ca2caSLen Brown .begin = acpi_hibernation_begin_old, 534d08ca2caSLen Brown .end = acpi_pm_end, 535d08ca2caSLen Brown .pre_snapshot = acpi_hibernation_pre_snapshot_old, 536d08ca2caSLen Brown .finish = acpi_hibernation_finish, 537d08ca2caSLen Brown .prepare = acpi_pm_disable_gpes, 538d08ca2caSLen Brown .enter = acpi_hibernation_enter, 539d08ca2caSLen Brown .leave = acpi_hibernation_leave, 540d08ca2caSLen Brown .pre_restore = acpi_pm_disable_gpes, 541d08ca2caSLen Brown .restore_cleanup = acpi_pm_enable_gpes, 542d08ca2caSLen Brown .recover = acpi_pm_finish, 543d08ca2caSLen Brown }; 544d08ca2caSLen Brown #endif /* CONFIG_HIBERNATION */ 545d08ca2caSLen Brown 546d08ca2caSLen Brown int acpi_suspend(u32 acpi_state) 547d08ca2caSLen Brown { 548d08ca2caSLen Brown suspend_state_t states[] = { 549d08ca2caSLen Brown [1] = PM_SUSPEND_STANDBY, 550d08ca2caSLen Brown [3] = PM_SUSPEND_MEM, 551d08ca2caSLen Brown [5] = PM_SUSPEND_MAX 552d08ca2caSLen Brown }; 553d08ca2caSLen Brown 554d08ca2caSLen Brown if (acpi_state < 6 && states[acpi_state]) 555d08ca2caSLen Brown return pm_suspend(states[acpi_state]); 556d08ca2caSLen Brown if (acpi_state == 4) 557d08ca2caSLen Brown return hibernate(); 558d08ca2caSLen Brown return -EINVAL; 559d08ca2caSLen Brown } 560d08ca2caSLen Brown 561d08ca2caSLen Brown #ifdef CONFIG_PM_SLEEP 562d08ca2caSLen Brown /** 563d08ca2caSLen Brown * acpi_pm_device_sleep_state - return preferred power state of ACPI device 564d08ca2caSLen Brown * in the system sleep state given by %acpi_target_sleep_state 565d08ca2caSLen Brown * @dev: device to examine; its driver model wakeup flags control 566d08ca2caSLen Brown * whether it should be able to wake up the system 567d08ca2caSLen Brown * @d_min_p: used to store the upper limit of allowed states range 568d08ca2caSLen Brown * Return value: preferred power state of the device on success, -ENODEV on 569d08ca2caSLen Brown * failure (ie. if there's no 'struct acpi_device' for @dev) 570d08ca2caSLen Brown * 571d08ca2caSLen Brown * Find the lowest power (highest number) ACPI device power state that 572d08ca2caSLen Brown * device @dev can be in while the system is in the sleep state represented 573d08ca2caSLen Brown * by %acpi_target_sleep_state. If @wake is nonzero, the device should be 574d08ca2caSLen Brown * able to wake up the system from this sleep state. If @d_min_p is set, 575d08ca2caSLen Brown * the highest power (lowest number) device power state of @dev allowed 576d08ca2caSLen Brown * in this system sleep state is stored at the location pointed to by it. 577d08ca2caSLen Brown * 578d08ca2caSLen Brown * The caller must ensure that @dev is valid before using this function. 579d08ca2caSLen Brown * The caller is also responsible for figuring out if the device is 580d08ca2caSLen Brown * supposed to be able to wake up the system and passing this information 581d08ca2caSLen Brown * via @wake. 582d08ca2caSLen Brown */ 583d08ca2caSLen Brown 584d08ca2caSLen Brown int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) 585d08ca2caSLen Brown { 586d08ca2caSLen Brown acpi_handle handle = DEVICE_ACPI_HANDLE(dev); 587d08ca2caSLen Brown struct acpi_device *adev; 588d08ca2caSLen Brown char acpi_method[] = "_SxD"; 589d08ca2caSLen Brown unsigned long long d_min, d_max; 590d08ca2caSLen Brown 591d08ca2caSLen Brown if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { 592d08ca2caSLen Brown printk(KERN_DEBUG "ACPI handle has no context!\n"); 593d08ca2caSLen Brown return -ENODEV; 594d08ca2caSLen Brown } 595d08ca2caSLen Brown 596d08ca2caSLen Brown acpi_method[2] = '0' + acpi_target_sleep_state; 597d08ca2caSLen Brown /* 598d08ca2caSLen Brown * If the sleep state is S0, we will return D3, but if the device has 599d08ca2caSLen Brown * _S0W, we will use the value from _S0W 600d08ca2caSLen Brown */ 601d08ca2caSLen Brown d_min = ACPI_STATE_D0; 602d08ca2caSLen Brown d_max = ACPI_STATE_D3; 603d08ca2caSLen Brown 604d08ca2caSLen Brown /* 605d08ca2caSLen Brown * If present, _SxD methods return the minimum D-state (highest power 606d08ca2caSLen Brown * state) we can use for the corresponding S-states. Otherwise, the 607d08ca2caSLen Brown * minimum D-state is D0 (ACPI 3.x). 608d08ca2caSLen Brown * 609d08ca2caSLen Brown * NOTE: We rely on acpi_evaluate_integer() not clobbering the integer 610d08ca2caSLen Brown * provided -- that's our fault recovery, we ignore retval. 611d08ca2caSLen Brown */ 612d08ca2caSLen Brown if (acpi_target_sleep_state > ACPI_STATE_S0) 613d08ca2caSLen Brown acpi_evaluate_integer(handle, acpi_method, NULL, &d_min); 614d08ca2caSLen Brown 615d08ca2caSLen Brown /* 616d08ca2caSLen Brown * If _PRW says we can wake up the system from the target sleep state, 617d08ca2caSLen Brown * the D-state returned by _SxD is sufficient for that (we assume a 618d08ca2caSLen Brown * wakeup-aware driver if wake is set). Still, if _SxW exists 619d08ca2caSLen Brown * (ACPI 3.x), it should return the maximum (lowest power) D-state that 620d08ca2caSLen Brown * can wake the system. _S0W may be valid, too. 621d08ca2caSLen Brown */ 622d08ca2caSLen Brown if (acpi_target_sleep_state == ACPI_STATE_S0 || 623d08ca2caSLen Brown (device_may_wakeup(dev) && adev->wakeup.state.enabled && 624d08ca2caSLen Brown adev->wakeup.sleep_state <= acpi_target_sleep_state)) { 625d08ca2caSLen Brown acpi_status status; 626d08ca2caSLen Brown 627d08ca2caSLen Brown acpi_method[3] = 'W'; 628d08ca2caSLen Brown status = acpi_evaluate_integer(handle, acpi_method, NULL, 629d08ca2caSLen Brown &d_max); 630d08ca2caSLen Brown if (ACPI_FAILURE(status)) { 631d08ca2caSLen Brown d_max = d_min; 632d08ca2caSLen Brown } else if (d_max < d_min) { 633d08ca2caSLen Brown /* Warn the user of the broken DSDT */ 634d08ca2caSLen Brown printk(KERN_WARNING "ACPI: Wrong value from %s\n", 635d08ca2caSLen Brown acpi_method); 636d08ca2caSLen Brown /* Sanitize it */ 637d08ca2caSLen Brown d_min = d_max; 638d08ca2caSLen Brown } 639d08ca2caSLen Brown } 640d08ca2caSLen Brown 641d08ca2caSLen Brown if (d_min_p) 642d08ca2caSLen Brown *d_min_p = d_min; 643d08ca2caSLen Brown return d_max; 644d08ca2caSLen Brown } 645d08ca2caSLen Brown 646d08ca2caSLen Brown /** 647d08ca2caSLen Brown * acpi_pm_device_sleep_wake - enable or disable the system wake-up 648d08ca2caSLen Brown * capability of given device 649d08ca2caSLen Brown * @dev: device to handle 650d08ca2caSLen Brown * @enable: 'true' - enable, 'false' - disable the wake-up capability 651d08ca2caSLen Brown */ 652d08ca2caSLen Brown int acpi_pm_device_sleep_wake(struct device *dev, bool enable) 653d08ca2caSLen Brown { 654d08ca2caSLen Brown acpi_handle handle; 655d08ca2caSLen Brown struct acpi_device *adev; 656d08ca2caSLen Brown 657d08ca2caSLen Brown if (!device_may_wakeup(dev)) 658d08ca2caSLen Brown return -EINVAL; 659d08ca2caSLen Brown 660d08ca2caSLen Brown handle = DEVICE_ACPI_HANDLE(dev); 661d08ca2caSLen Brown if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { 662d08ca2caSLen Brown printk(KERN_DEBUG "ACPI handle has no context!\n"); 663d08ca2caSLen Brown return -ENODEV; 664d08ca2caSLen Brown } 665d08ca2caSLen Brown 666d08ca2caSLen Brown return enable ? 667d08ca2caSLen Brown acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) : 668d08ca2caSLen Brown acpi_disable_wakeup_device_power(adev); 669d08ca2caSLen Brown } 670d08ca2caSLen Brown #endif 671d08ca2caSLen Brown 672d08ca2caSLen Brown static void acpi_power_off_prepare(void) 673d08ca2caSLen Brown { 674d08ca2caSLen Brown /* Prepare to power off the system */ 675d08ca2caSLen Brown acpi_sleep_prepare(ACPI_STATE_S5); 676d08ca2caSLen Brown acpi_disable_all_gpes(); 677d08ca2caSLen Brown } 678d08ca2caSLen Brown 679d08ca2caSLen Brown static void acpi_power_off(void) 680d08ca2caSLen Brown { 681d08ca2caSLen Brown /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ 682*4d939155SFrank Seidel printk(KERN_DEBUG "%s called\n", __func__); 683d08ca2caSLen Brown local_irq_disable(); 684d08ca2caSLen Brown acpi_enable_wakeup_device(ACPI_STATE_S5); 685d08ca2caSLen Brown acpi_enter_sleep_state(ACPI_STATE_S5); 686d08ca2caSLen Brown } 687d08ca2caSLen Brown 688d08ca2caSLen Brown int __init acpi_sleep_init(void) 689d08ca2caSLen Brown { 690d08ca2caSLen Brown acpi_status status; 691d08ca2caSLen Brown u8 type_a, type_b; 692d08ca2caSLen Brown #ifdef CONFIG_SUSPEND 693d08ca2caSLen Brown int i = 0; 694d08ca2caSLen Brown 695d08ca2caSLen Brown dmi_check_system(acpisleep_dmi_table); 696d08ca2caSLen Brown #endif 697d08ca2caSLen Brown 698d08ca2caSLen Brown if (acpi_disabled) 699d08ca2caSLen Brown return 0; 700d08ca2caSLen Brown 701d08ca2caSLen Brown sleep_states[ACPI_STATE_S0] = 1; 702d08ca2caSLen Brown printk(KERN_INFO PREFIX "(supports S0"); 703d08ca2caSLen Brown 704d08ca2caSLen Brown #ifdef CONFIG_SUSPEND 705d08ca2caSLen Brown for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) { 706d08ca2caSLen Brown status = acpi_get_sleep_type_data(i, &type_a, &type_b); 707d08ca2caSLen Brown if (ACPI_SUCCESS(status)) { 708d08ca2caSLen Brown sleep_states[i] = 1; 709d08ca2caSLen Brown printk(" S%d", i); 710d08ca2caSLen Brown } 711d08ca2caSLen Brown } 712d08ca2caSLen Brown 713d08ca2caSLen Brown suspend_set_ops(old_suspend_ordering ? 714d08ca2caSLen Brown &acpi_suspend_ops_old : &acpi_suspend_ops); 715d08ca2caSLen Brown #endif 716d08ca2caSLen Brown 717d08ca2caSLen Brown #ifdef CONFIG_HIBERNATION 718d08ca2caSLen Brown status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); 719d08ca2caSLen Brown if (ACPI_SUCCESS(status)) { 720d08ca2caSLen Brown hibernation_set_ops(old_suspend_ordering ? 721d08ca2caSLen Brown &acpi_hibernation_ops_old : &acpi_hibernation_ops); 722d08ca2caSLen Brown sleep_states[ACPI_STATE_S4] = 1; 723d08ca2caSLen Brown printk(" S4"); 724d08ca2caSLen Brown if (!nosigcheck) { 725d08ca2caSLen Brown acpi_get_table(ACPI_SIG_FACS, 1, 726d08ca2caSLen Brown (struct acpi_table_header **)&facs); 727d08ca2caSLen Brown if (facs) 728d08ca2caSLen Brown s4_hardware_signature = 729d08ca2caSLen Brown facs->hardware_signature; 730d08ca2caSLen Brown } 731d08ca2caSLen Brown } 732d08ca2caSLen Brown #endif 733d08ca2caSLen Brown status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); 734d08ca2caSLen Brown if (ACPI_SUCCESS(status)) { 735d08ca2caSLen Brown sleep_states[ACPI_STATE_S5] = 1; 736d08ca2caSLen Brown printk(" S5"); 737d08ca2caSLen Brown pm_power_off_prepare = acpi_power_off_prepare; 738d08ca2caSLen Brown pm_power_off = acpi_power_off; 739d08ca2caSLen Brown } 740d08ca2caSLen Brown printk(")\n"); 741d08ca2caSLen Brown /* 742d08ca2caSLen Brown * Register the tts_notifier to reboot notifier list so that the _TTS 743d08ca2caSLen Brown * object can also be evaluated when the system enters S5. 744d08ca2caSLen Brown */ 745d08ca2caSLen Brown register_reboot_notifier(&tts_notifier); 746d08ca2caSLen Brown return 0; 747d08ca2caSLen Brown } 748