xref: /linux/drivers/acpi/sleep.c (revision fad16dd9c962229c5965ec6f5cd5f48180f94fd4)
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