162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
562306a36Sopenharmony_ci *                   original/legacy sleep/PM registers.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *****************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <acpi/acpi.h>
1262306a36Sopenharmony_ci#include "accommon.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define _COMPONENT          ACPI_HARDWARE
1562306a36Sopenharmony_ciACPI_MODULE_NAME("hwsleep")
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#if (!ACPI_REDUCED_HARDWARE)	/* Entire module */
1862306a36Sopenharmony_ci/*******************************************************************************
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * FUNCTION:    acpi_hw_legacy_sleep
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * PARAMETERS:  sleep_state         - Which sleep state to enter
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * RETURN:      Status
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers
2762306a36Sopenharmony_ci *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci ******************************************************************************/
3062306a36Sopenharmony_ciacpi_status acpi_hw_legacy_sleep(u8 sleep_state)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct acpi_bit_register_info *sleep_type_reg_info;
3362306a36Sopenharmony_ci	struct acpi_bit_register_info *sleep_enable_reg_info;
3462306a36Sopenharmony_ci	u32 pm1a_control;
3562306a36Sopenharmony_ci	u32 pm1b_control;
3662306a36Sopenharmony_ci	u32 in_value;
3762306a36Sopenharmony_ci	acpi_status status;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_legacy_sleep);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	sleep_type_reg_info =
4262306a36Sopenharmony_ci	    acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE);
4362306a36Sopenharmony_ci	sleep_enable_reg_info =
4462306a36Sopenharmony_ci	    acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* Clear wake status */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	status = acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS,
4962306a36Sopenharmony_ci					 ACPI_CLEAR_STATUS);
5062306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
5162306a36Sopenharmony_ci		return_ACPI_STATUS(status);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Disable all GPEs */
5562306a36Sopenharmony_ci	status = acpi_hw_disable_all_gpes();
5662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
5762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	status = acpi_hw_clear_acpi_status();
6062306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
6162306a36Sopenharmony_ci		return_ACPI_STATUS(status);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	acpi_gbl_system_awake_and_running = FALSE;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	 /* Enable all wakeup GPEs */
6662306a36Sopenharmony_ci	status = acpi_hw_enable_all_wakeup_gpes();
6762306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
6862306a36Sopenharmony_ci		return_ACPI_STATUS(status);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* Get current value of PM1A control */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL,
7462306a36Sopenharmony_ci				       &pm1a_control);
7562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
7662306a36Sopenharmony_ci		return_ACPI_STATUS(status);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_INIT,
7962306a36Sopenharmony_ci			  "Entering sleep state [S%u]\n", sleep_state));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* Clear the SLP_EN and SLP_TYP fields */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	pm1a_control &= ~(sleep_type_reg_info->access_bit_mask |
8462306a36Sopenharmony_ci			  sleep_enable_reg_info->access_bit_mask);
8562306a36Sopenharmony_ci	pm1b_control = pm1a_control;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Insert the SLP_TYP bits */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	pm1a_control |=
9062306a36Sopenharmony_ci	    (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position);
9162306a36Sopenharmony_ci	pm1b_control |=
9262306a36Sopenharmony_ci	    (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/*
9562306a36Sopenharmony_ci	 * We split the writes of SLP_TYP and SLP_EN to workaround
9662306a36Sopenharmony_ci	 * poorly implemented hardware.
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Write #1: write the SLP_TYP data to the PM1 Control registers */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
10262306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
10362306a36Sopenharmony_ci		return_ACPI_STATUS(status);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Insert the sleep enable (SLP_EN) bit */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	pm1a_control |= sleep_enable_reg_info->access_bit_mask;
10962306a36Sopenharmony_ci	pm1b_control |= sleep_enable_reg_info->access_bit_mask;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Flush caches, as per ACPI specification */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (sleep_state < ACPI_STATE_S4) {
11462306a36Sopenharmony_ci		ACPI_FLUSH_CPU_CACHE();
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	status = acpi_os_enter_sleep(sleep_state, pm1a_control, pm1b_control);
11862306a36Sopenharmony_ci	if (status == AE_CTRL_TERMINATE) {
11962306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
12262306a36Sopenharmony_ci		return_ACPI_STATUS(status);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Write #2: Write both SLP_TYP + SLP_EN */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
12862306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
12962306a36Sopenharmony_ci		return_ACPI_STATUS(status);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (sleep_state > ACPI_STATE_S3) {
13362306a36Sopenharmony_ci		/*
13462306a36Sopenharmony_ci		 * We wanted to sleep > S3, but it didn't happen (by virtue of the
13562306a36Sopenharmony_ci		 * fact that we are still executing!)
13662306a36Sopenharmony_ci		 *
13762306a36Sopenharmony_ci		 * Wait ten seconds, then try again. This is to get S4/S5 to work on
13862306a36Sopenharmony_ci		 * all machines.
13962306a36Sopenharmony_ci		 *
14062306a36Sopenharmony_ci		 * We wait so long to allow chipsets that poll this reg very slowly
14162306a36Sopenharmony_ci		 * to still read the right value. Ideally, this block would go
14262306a36Sopenharmony_ci		 * away entirely.
14362306a36Sopenharmony_ci		 */
14462306a36Sopenharmony_ci		acpi_os_stall(10 * ACPI_USEC_PER_SEC);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL,
14762306a36Sopenharmony_ci						sleep_enable_reg_info->
14862306a36Sopenharmony_ci						access_bit_mask);
14962306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
15062306a36Sopenharmony_ci			return_ACPI_STATUS(status);
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Wait for transition back to Working State */
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	do {
15762306a36Sopenharmony_ci		status =
15862306a36Sopenharmony_ci		    acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
15962306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
16062306a36Sopenharmony_ci			return_ACPI_STATUS(status);
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	} while (!in_value);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*******************************************************************************
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * FUNCTION:    acpi_hw_legacy_wake_prep
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci * PARAMETERS:  sleep_state         - Which sleep state we just exited
17362306a36Sopenharmony_ci *
17462306a36Sopenharmony_ci * RETURN:      Status
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
17762306a36Sopenharmony_ci *              sleep.
17862306a36Sopenharmony_ci *              Called with interrupts ENABLED.
17962306a36Sopenharmony_ci *
18062306a36Sopenharmony_ci ******************************************************************************/
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciacpi_status acpi_hw_legacy_wake_prep(u8 sleep_state)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	acpi_status status = AE_OK;
18562306a36Sopenharmony_ci	struct acpi_bit_register_info *sleep_type_reg_info;
18662306a36Sopenharmony_ci	struct acpi_bit_register_info *sleep_enable_reg_info;
18762306a36Sopenharmony_ci	u32 pm1a_control;
18862306a36Sopenharmony_ci	u32 pm1b_control;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_legacy_wake_prep);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * Set SLP_TYPE and SLP_EN to state S0.
19462306a36Sopenharmony_ci	 * This is unclear from the ACPI Spec, but it is required
19562306a36Sopenharmony_ci	 * by some machines.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	if (acpi_gbl_sleep_type_a_s0 != ACPI_SLEEP_TYPE_INVALID) {
19862306a36Sopenharmony_ci		sleep_type_reg_info =
19962306a36Sopenharmony_ci		    acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE);
20062306a36Sopenharmony_ci		sleep_enable_reg_info =
20162306a36Sopenharmony_ci		    acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		/* Get current value of PM1A control */
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL,
20662306a36Sopenharmony_ci					       &pm1a_control);
20762306a36Sopenharmony_ci		if (ACPI_SUCCESS(status)) {
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci			/* Clear the SLP_EN and SLP_TYP fields */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci			pm1a_control &= ~(sleep_type_reg_info->access_bit_mask |
21262306a36Sopenharmony_ci					  sleep_enable_reg_info->
21362306a36Sopenharmony_ci					  access_bit_mask);
21462306a36Sopenharmony_ci			pm1b_control = pm1a_control;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci			/* Insert the SLP_TYP bits */
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci			pm1a_control |= (acpi_gbl_sleep_type_a_s0 <<
21962306a36Sopenharmony_ci					 sleep_type_reg_info->bit_position);
22062306a36Sopenharmony_ci			pm1b_control |= (acpi_gbl_sleep_type_b_s0 <<
22162306a36Sopenharmony_ci					 sleep_type_reg_info->bit_position);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci			/* Write the control registers and ignore any errors */
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci			(void)acpi_hw_write_pm1_control(pm1a_control,
22662306a36Sopenharmony_ci							pm1b_control);
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return_ACPI_STATUS(status);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*******************************************************************************
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci * FUNCTION:    acpi_hw_legacy_wake
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * PARAMETERS:  sleep_state         - Which sleep state we just exited
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * RETURN:      Status
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
24262306a36Sopenharmony_ci *              Called with interrupts ENABLED.
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci ******************************************************************************/
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciacpi_status acpi_hw_legacy_wake(u8 sleep_state)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	acpi_status status;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_legacy_wake);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
25562306a36Sopenharmony_ci	acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * GPEs must be enabled before _WAK is called as GPEs
25962306a36Sopenharmony_ci	 * might get fired there
26062306a36Sopenharmony_ci	 *
26162306a36Sopenharmony_ci	 * Restore the GPEs:
26262306a36Sopenharmony_ci	 * 1) Disable all GPEs
26362306a36Sopenharmony_ci	 * 2) Enable all runtime GPEs
26462306a36Sopenharmony_ci	 */
26562306a36Sopenharmony_ci	status = acpi_hw_disable_all_gpes();
26662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
26762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	status = acpi_hw_enable_all_runtime_gpes();
27162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
27262306a36Sopenharmony_ci		return_ACPI_STATUS(status);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * Now we can execute _WAK, etc. Some machines require that the GPEs
27762306a36Sopenharmony_ci	 * are enabled before the wake methods are executed.
27862306a36Sopenharmony_ci	 */
27962306a36Sopenharmony_ci	acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/*
28262306a36Sopenharmony_ci	 * Some BIOS code assumes that WAK_STS will be cleared on resume
28362306a36Sopenharmony_ci	 * and use it to determine whether the system is rebooting or
28462306a36Sopenharmony_ci	 * resuming. Clear WAK_STS for compatibility.
28562306a36Sopenharmony_ci	 */
28662306a36Sopenharmony_ci	(void)acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS,
28762306a36Sopenharmony_ci				      ACPI_CLEAR_STATUS);
28862306a36Sopenharmony_ci	acpi_gbl_system_awake_and_running = TRUE;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Enable power button */
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	(void)
29362306a36Sopenharmony_ci	    acpi_write_bit_register(acpi_gbl_fixed_event_info
29462306a36Sopenharmony_ci				    [ACPI_EVENT_POWER_BUTTON].
29562306a36Sopenharmony_ci				    enable_register_id, ACPI_ENABLE_EVENT);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	(void)
29862306a36Sopenharmony_ci	    acpi_write_bit_register(acpi_gbl_fixed_event_info
29962306a36Sopenharmony_ci				    [ACPI_EVENT_POWER_BUTTON].
30062306a36Sopenharmony_ci				    status_register_id, ACPI_CLEAR_STATUS);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Enable sleep button */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	(void)
30562306a36Sopenharmony_ci	    acpi_write_bit_register(acpi_gbl_fixed_event_info
30662306a36Sopenharmony_ci				    [ACPI_EVENT_SLEEP_BUTTON].
30762306a36Sopenharmony_ci				    enable_register_id, ACPI_ENABLE_EVENT);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	(void)
31062306a36Sopenharmony_ci	    acpi_write_bit_register(acpi_gbl_fixed_event_info
31162306a36Sopenharmony_ci				    [ACPI_EVENT_SLEEP_BUTTON].
31262306a36Sopenharmony_ci				    status_register_id, ACPI_CLEAR_STATUS);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
31562306a36Sopenharmony_ci	return_ACPI_STATUS(status);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci#endif				/* !ACPI_REDUCED_HARDWARE */
319