xref: /openbmc/linux/drivers/acpi/acpica/hwxfsleep.c (revision f220d3eb)
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /******************************************************************************
3  *
4  * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
5  *
6  * Copyright (C) 2000 - 2018, Intel Corp.
7  *
8  *****************************************************************************/
9 
10 #define EXPORT_ACPI_INTERFACES
11 
12 #include <acpi/acpi.h>
13 #include "accommon.h"
14 
15 #define _COMPONENT          ACPI_HARDWARE
16 ACPI_MODULE_NAME("hwxfsleep")
17 
18 /* Local prototypes */
19 #if (!ACPI_REDUCED_HARDWARE)
20 static acpi_status
21 acpi_hw_set_firmware_waking_vector(struct acpi_table_facs *facs,
22 				   acpi_physical_address physical_address,
23 				   acpi_physical_address physical_address64);
24 #endif
25 
26 static acpi_status acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id);
27 
28 /*
29  * Dispatch table used to efficiently branch to the various sleep
30  * functions.
31  */
32 #define ACPI_SLEEP_FUNCTION_ID         0
33 #define ACPI_WAKE_PREP_FUNCTION_ID     1
34 #define ACPI_WAKE_FUNCTION_ID          2
35 
36 /* Legacy functions are optional, based upon ACPI_REDUCED_HARDWARE */
37 
38 static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
39 	{ACPI_STRUCT_INIT(legacy_function,
40 			  ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_sleep)),
41 	 ACPI_STRUCT_INIT(extended_function,
42 			  acpi_hw_extended_sleep)},
43 	{ACPI_STRUCT_INIT(legacy_function,
44 			  ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake_prep)),
45 	 ACPI_STRUCT_INIT(extended_function,
46 			  acpi_hw_extended_wake_prep)},
47 	{ACPI_STRUCT_INIT(legacy_function,
48 			  ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake)),
49 	 ACPI_STRUCT_INIT(extended_function,
50 			  acpi_hw_extended_wake)}
51 };
52 
53 /*
54  * These functions are removed for the ACPI_REDUCED_HARDWARE case:
55  *      acpi_set_firmware_waking_vector
56  *      acpi_enter_sleep_state_s4bios
57  */
58 
59 #if (!ACPI_REDUCED_HARDWARE)
60 /*******************************************************************************
61  *
62  * FUNCTION:    acpi_hw_set_firmware_waking_vector
63  *
64  * PARAMETERS:  facs                - Pointer to FACS table
65  *              physical_address    - 32-bit physical address of ACPI real mode
66  *                                    entry point
67  *              physical_address64  - 64-bit physical address of ACPI protected
68  *                                    mode entry point
69  *
70  * RETURN:      Status
71  *
72  * DESCRIPTION: Sets the firmware_waking_vector fields of the FACS
73  *
74  ******************************************************************************/
75 
76 static acpi_status
77 acpi_hw_set_firmware_waking_vector(struct acpi_table_facs *facs,
78 				   acpi_physical_address physical_address,
79 				   acpi_physical_address physical_address64)
80 {
81 	ACPI_FUNCTION_TRACE(acpi_hw_set_firmware_waking_vector);
82 
83 
84 	/*
85 	 * According to the ACPI specification 2.0c and later, the 64-bit
86 	 * waking vector should be cleared and the 32-bit waking vector should
87 	 * be used, unless we want the wake-up code to be called by the BIOS in
88 	 * Protected Mode.  Some systems (for example HP dv5-1004nr) are known
89 	 * to fail to resume if the 64-bit vector is used.
90 	 */
91 
92 	/* Set the 32-bit vector */
93 
94 	facs->firmware_waking_vector = (u32)physical_address;
95 
96 	if (facs->length > 32) {
97 		if (facs->version >= 1) {
98 
99 			/* Set the 64-bit vector */
100 
101 			facs->xfirmware_waking_vector = physical_address64;
102 		} else {
103 			/* Clear the 64-bit vector if it exists */
104 
105 			facs->xfirmware_waking_vector = 0;
106 		}
107 	}
108 
109 	return_ACPI_STATUS(AE_OK);
110 }
111 
112 /*******************************************************************************
113  *
114  * FUNCTION:    acpi_set_firmware_waking_vector
115  *
116  * PARAMETERS:  physical_address    - 32-bit physical address of ACPI real mode
117  *                                    entry point
118  *              physical_address64  - 64-bit physical address of ACPI protected
119  *                                    mode entry point
120  *
121  * RETURN:      Status
122  *
123  * DESCRIPTION: Sets the firmware_waking_vector fields of the FACS
124  *
125  ******************************************************************************/
126 
127 acpi_status
128 acpi_set_firmware_waking_vector(acpi_physical_address physical_address,
129 				acpi_physical_address physical_address64)
130 {
131 
132 	ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
133 
134 	if (acpi_gbl_FACS) {
135 		(void)acpi_hw_set_firmware_waking_vector(acpi_gbl_FACS,
136 							 physical_address,
137 							 physical_address64);
138 	}
139 
140 	return_ACPI_STATUS(AE_OK);
141 }
142 
143 ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
144 
145 /*******************************************************************************
146  *
147  * FUNCTION:    acpi_enter_sleep_state_s4bios
148  *
149  * PARAMETERS:  None
150  *
151  * RETURN:      Status
152  *
153  * DESCRIPTION: Perform a S4 bios request.
154  *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
155  *
156  ******************************************************************************/
157 acpi_status acpi_enter_sleep_state_s4bios(void)
158 {
159 	u32 in_value;
160 	acpi_status status;
161 
162 	ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios);
163 
164 	/* Clear the wake status bit (PM1) */
165 
166 	status =
167 	    acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
168 	if (ACPI_FAILURE(status)) {
169 		return_ACPI_STATUS(status);
170 	}
171 
172 	status = acpi_hw_clear_acpi_status();
173 	if (ACPI_FAILURE(status)) {
174 		return_ACPI_STATUS(status);
175 	}
176 
177 	/*
178 	 * 1) Disable all GPEs
179 	 * 2) Enable all wakeup GPEs
180 	 */
181 	status = acpi_hw_disable_all_gpes();
182 	if (ACPI_FAILURE(status)) {
183 		return_ACPI_STATUS(status);
184 	}
185 	acpi_gbl_system_awake_and_running = FALSE;
186 
187 	status = acpi_hw_enable_all_wakeup_gpes();
188 	if (ACPI_FAILURE(status)) {
189 		return_ACPI_STATUS(status);
190 	}
191 
192 	ACPI_FLUSH_CPU_CACHE();
193 
194 	status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
195 				    (u32)acpi_gbl_FADT.s4_bios_request, 8);
196 
197 	do {
198 		acpi_os_stall(ACPI_USEC_PER_MSEC);
199 		status =
200 		    acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
201 		if (ACPI_FAILURE(status)) {
202 			return_ACPI_STATUS(status);
203 		}
204 
205 	} while (!in_value);
206 
207 	return_ACPI_STATUS(AE_OK);
208 }
209 
210 ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
211 #endif				/* !ACPI_REDUCED_HARDWARE */
212 /*******************************************************************************
213  *
214  * FUNCTION:    acpi_hw_sleep_dispatch
215  *
216  * PARAMETERS:  sleep_state         - Which sleep state to enter/exit
217  *              function_id         - Sleep, wake_prep, or Wake
218  *
219  * RETURN:      Status from the invoked sleep handling function.
220  *
221  * DESCRIPTION: Dispatch a sleep/wake request to the appropriate handling
222  *              function.
223  *
224  ******************************************************************************/
225 static acpi_status acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id)
226 {
227 	acpi_status status;
228 	struct acpi_sleep_functions *sleep_functions =
229 	    &acpi_sleep_dispatch[function_id];
230 
231 #if (!ACPI_REDUCED_HARDWARE)
232 	/*
233 	 * If the Hardware Reduced flag is set (from the FADT), we must
234 	 * use the extended sleep registers (FADT). Note: As per the ACPI
235 	 * specification, these extended registers are to be used for HW-reduced
236 	 * platforms only. They are not general-purpose replacements for the
237 	 * legacy PM register sleep support.
238 	 */
239 	if (acpi_gbl_reduced_hardware) {
240 		status = sleep_functions->extended_function(sleep_state);
241 	} else {
242 		/* Legacy sleep */
243 
244 		status = sleep_functions->legacy_function(sleep_state);
245 	}
246 
247 	return (status);
248 
249 #else
250 	/*
251 	 * For the case where reduced-hardware-only code is being generated,
252 	 * we know that only the extended sleep registers are available
253 	 */
254 	status = sleep_functions->extended_function(sleep_state);
255 	return (status);
256 
257 #endif				/* !ACPI_REDUCED_HARDWARE */
258 }
259 
260 /*******************************************************************************
261  *
262  * FUNCTION:    acpi_enter_sleep_state_prep
263  *
264  * PARAMETERS:  sleep_state         - Which sleep state to enter
265  *
266  * RETURN:      Status
267  *
268  * DESCRIPTION: Prepare to enter a system sleep state.
269  *              This function must execute with interrupts enabled.
270  *              We break sleeping into 2 stages so that OSPM can handle
271  *              various OS-specific tasks between the two steps.
272  *
273  ******************************************************************************/
274 
275 acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
276 {
277 	acpi_status status;
278 	struct acpi_object_list arg_list;
279 	union acpi_object arg;
280 	u32 sst_value;
281 
282 	ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep);
283 
284 	status = acpi_get_sleep_type_data(sleep_state,
285 					  &acpi_gbl_sleep_type_a,
286 					  &acpi_gbl_sleep_type_b);
287 	if (ACPI_FAILURE(status)) {
288 		return_ACPI_STATUS(status);
289 	}
290 
291 	/* Execute the _PTS method (Prepare To Sleep) */
292 
293 	arg_list.count = 1;
294 	arg_list.pointer = &arg;
295 	arg.type = ACPI_TYPE_INTEGER;
296 	arg.integer.value = sleep_state;
297 
298 	status =
299 	    acpi_evaluate_object(NULL, METHOD_PATHNAME__PTS, &arg_list, NULL);
300 	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
301 		return_ACPI_STATUS(status);
302 	}
303 
304 	/* Setup the argument to the _SST method (System STatus) */
305 
306 	switch (sleep_state) {
307 	case ACPI_STATE_S0:
308 
309 		sst_value = ACPI_SST_WORKING;
310 		break;
311 
312 	case ACPI_STATE_S1:
313 	case ACPI_STATE_S2:
314 	case ACPI_STATE_S3:
315 
316 		sst_value = ACPI_SST_SLEEPING;
317 		break;
318 
319 	case ACPI_STATE_S4:
320 
321 		sst_value = ACPI_SST_SLEEP_CONTEXT;
322 		break;
323 
324 	default:
325 
326 		sst_value = ACPI_SST_INDICATOR_OFF;	/* Default is off */
327 		break;
328 	}
329 
330 	/*
331 	 * Set the system indicators to show the desired sleep state.
332 	 * _SST is an optional method (return no error if not found)
333 	 */
334 	acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, sst_value);
335 	return_ACPI_STATUS(AE_OK);
336 }
337 
338 ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
339 
340 /*******************************************************************************
341  *
342  * FUNCTION:    acpi_enter_sleep_state
343  *
344  * PARAMETERS:  sleep_state         - Which sleep state to enter
345  *
346  * RETURN:      Status
347  *
348  * DESCRIPTION: Enter a system sleep state
349  *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
350  *
351  ******************************************************************************/
352 acpi_status acpi_enter_sleep_state(u8 sleep_state)
353 {
354 	acpi_status status;
355 
356 	ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
357 
358 	if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
359 	    (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
360 		ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
361 			    acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
362 		return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
363 	}
364 
365 	status = acpi_hw_sleep_dispatch(sleep_state, ACPI_SLEEP_FUNCTION_ID);
366 	return_ACPI_STATUS(status);
367 }
368 
369 ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
370 
371 /*******************************************************************************
372  *
373  * FUNCTION:    acpi_leave_sleep_state_prep
374  *
375  * PARAMETERS:  sleep_state         - Which sleep state we are exiting
376  *
377  * RETURN:      Status
378  *
379  * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
380  *              sleep. Called with interrupts DISABLED.
381  *              We break wake/resume into 2 stages so that OSPM can handle
382  *              various OS-specific tasks between the two steps.
383  *
384  ******************************************************************************/
385 acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
386 {
387 	acpi_status status;
388 
389 	ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
390 
391 	status =
392 	    acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_PREP_FUNCTION_ID);
393 	return_ACPI_STATUS(status);
394 }
395 
396 ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state_prep)
397 
398 /*******************************************************************************
399  *
400  * FUNCTION:    acpi_leave_sleep_state
401  *
402  * PARAMETERS:  sleep_state         - Which sleep state we are exiting
403  *
404  * RETURN:      Status
405  *
406  * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
407  *              Called with interrupts ENABLED.
408  *
409  ******************************************************************************/
410 acpi_status acpi_leave_sleep_state(u8 sleep_state)
411 {
412 	acpi_status status;
413 
414 	ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
415 
416 	status = acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_FUNCTION_ID);
417 	return_ACPI_STATUS(status);
418 }
419 
420 ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state)
421