/*
* sleep.c - ACPI sleep support.
*
* Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
* Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com>
* Copyright (c) 2000-2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Lab
*
* This file is released under the GPLv2.
*
*/
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/dmi.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/reboot.h>
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/syscore_ops.h>
#include <asm/io.h>
#include <trace/events/power.h>
#include "internal.h"
#include "sleep.h"
/*
* Some HW-full platforms do not have _S5, so they may need
* to leverage efi power off for a shutdown.
*/
bool acpi_no_s5;
static u8 sleep_states[ACPI_S_STATE_COUNT];
static void acpi_sleep_tts_switch(u32 acpi_state)
{
acpi_status status;
status = acpi_execute_simple_method(NULL, "\\_TTS", acpi_state);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
/*
* OS can't evaluate the _TTS object correctly. Some warning
* message will be printed. But it won't break anything.
*/
printk(KERN_NOTICE "Failure in evaluating _TTS object\n");
}
}
static int tts_notify_reboot(struct notifier_block *this,
unsigned long code, void *x)
{
acpi_sleep_tts_switch(ACPI_STATE_S5);
return NOTIFY_DONE;
}
static struct notifier_block tts_notifier = {
.notifier_call = tts_notify_reboot,
.next = NULL,
.priority = 0,
};
static int acpi_sleep_prepare(u32 acpi_state)
{
#ifdef CONFIG_ACPI_SLEEP
/* do we have a wakeup address for S2 and S3? */
if (acpi_state == ACPI_STATE_S3) {
if (!acpi_wakeup_address)
return -EFAULT;
acpi_set_waking_vector(acpi_wakeup_address);
}
ACPI_FLUSH_CPU_CACHE();
#endif
printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n",
acpi_state);
acpi_enable_wakeup_devices(acpi_state);
acpi_enter_sleep_state_prep(acpi_state);
return 0;
}
static bool acpi_sleep_state_supported(u8 sleep_state)
{
acpi_status status;
u8 type_a, type_b;
status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b);
return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware
|| (acpi_gbl_FADT.sleep_control.address
&& acpi_gbl_FADT.sleep_status.address));
}
#ifdef CONFIG_ACPI_SLEEP
static u32 acpi_target_sleep_state = ACPI_STATE_S0;
u32 acpi_target_system_state(void)
{
return acpi_target_sleep_state;
}
EXPORT_SYMBOL_GPL(acpi_target_system_state);
static bool pwr_btn_event_pending;
/*
* The ACPI specification wants us to save NVS memory regions during hibernation
* and to restore them during the subsequent resume. Windows does that also for
* suspend to RAM. However, it is known that this mechanism does not work on
* all machines, so we allow the user to disable it with the help of the
* 'acpi_sleep=nonvs' kernel command line option.
*/
static bool nvs_nosave;
void __init acpi_nvs_nosave(void)
{
nvs_nosave = true;
}
/*
* The ACPI specification wants us to save NVS memory regions during hibernation
* but says nothing about saving NVS during S3. Not all versions of Windows
* save NVS on S3 suspend either, and it is clear that not all systems need
* NVS to be saved at S3 time. To improve suspend/resume time, allow the
* user to disable saving NVS on S3 if their system does not require it, but
* continue to save/restore NVS for S4 as specified.
*/
static bool nvs_nosave_s3;
void __init acpi_nvs_nosave_s3(void)
{
nvs_nosave_s3 = true;
}
static int __init init_nvs_save_s3(const struct dmi_system_id *d)
{
nvs_nosave_s3 = false;
return 0;
}
/*
* ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
* user to request that behavior by using the 'acpi_old_suspend_ordering'
* kernel command line option that causes the following variable to be set.
*/
static bool old_suspend_ordering;
void __init acpi_old_suspend_ordering(void)
{
old_suspend_ordering = true;
}
static int __init init_old_suspend_ordering(const struct dmi_system_id *d)
{
acpi_old_suspend_ordering();
return 0;
}
static