162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: evglock - Global Lock support
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *****************************************************************************/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <acpi/acpi.h>
1162306a36Sopenharmony_ci#include "accommon.h"
1262306a36Sopenharmony_ci#include "acevents.h"
1362306a36Sopenharmony_ci#include "acinterp.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define _COMPONENT          ACPI_EVENTS
1662306a36Sopenharmony_ciACPI_MODULE_NAME("evglock")
1762306a36Sopenharmony_ci#if (!ACPI_REDUCED_HARDWARE)	/* Entire module */
1862306a36Sopenharmony_ci/* Local prototypes */
1962306a36Sopenharmony_cistatic u32 acpi_ev_global_lock_handler(void *context);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*******************************************************************************
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * FUNCTION:    acpi_ev_init_global_lock_handler
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * PARAMETERS:  None
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * RETURN:      Status
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * DESCRIPTION: Install a handler for the global lock release event
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci ******************************************************************************/
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciacpi_status acpi_ev_init_global_lock_handler(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	acpi_status status;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ev_init_global_lock_handler);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	/* If Hardware Reduced flag is set, there is no global lock */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (acpi_gbl_reduced_hardware) {
4262306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Attempt installation of the global lock handler */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL,
4862306a36Sopenharmony_ci						  acpi_ev_global_lock_handler,
4962306a36Sopenharmony_ci						  NULL);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/*
5262306a36Sopenharmony_ci	 * If the global lock does not exist on this platform, the attempt to
5362306a36Sopenharmony_ci	 * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick).
5462306a36Sopenharmony_ci	 * Map to AE_OK, but mark global lock as not present. Any attempt to
5562306a36Sopenharmony_ci	 * actually use the global lock will be flagged with an error.
5662306a36Sopenharmony_ci	 */
5762306a36Sopenharmony_ci	acpi_gbl_global_lock_present = FALSE;
5862306a36Sopenharmony_ci	if (status == AE_NO_HARDWARE_RESPONSE) {
5962306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
6062306a36Sopenharmony_ci			    "No response from Global Lock hardware, disabling lock"));
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock);
6662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
6762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	acpi_gbl_global_lock_pending = FALSE;
7162306a36Sopenharmony_ci	acpi_gbl_global_lock_present = TRUE;
7262306a36Sopenharmony_ci	return_ACPI_STATUS(status);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*******************************************************************************
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * FUNCTION:    acpi_ev_remove_global_lock_handler
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * PARAMETERS:  None
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * RETURN:      Status
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * DESCRIPTION: Remove the handler for the Global Lock
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci ******************************************************************************/
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciacpi_status acpi_ev_remove_global_lock_handler(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	acpi_status status;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	acpi_gbl_global_lock_present = FALSE;
9462306a36Sopenharmony_ci	status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL,
9562306a36Sopenharmony_ci						 acpi_ev_global_lock_handler);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock);
9862306a36Sopenharmony_ci	return_ACPI_STATUS(status);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/*******************************************************************************
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * FUNCTION:    acpi_ev_global_lock_handler
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * PARAMETERS:  context         - From thread interface, not used
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * RETURN:      ACPI_INTERRUPT_HANDLED
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * DESCRIPTION: Invoked directly from the SCI handler when a global lock
11062306a36Sopenharmony_ci *              release interrupt occurs. If there is actually a pending
11162306a36Sopenharmony_ci *              request for the lock, signal the waiting thread.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci ******************************************************************************/
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic u32 acpi_ev_global_lock_handler(void *context)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	acpi_status status;
11862306a36Sopenharmony_ci	acpi_cpu_flags flags;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * If a request for the global lock is not actually pending,
12462306a36Sopenharmony_ci	 * we are done. This handles "spurious" global lock interrupts
12562306a36Sopenharmony_ci	 * which are possible (and have been seen) with bad BIOSs.
12662306a36Sopenharmony_ci	 */
12762306a36Sopenharmony_ci	if (!acpi_gbl_global_lock_pending) {
12862306a36Sopenharmony_ci		goto cleanup_and_exit;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/*
13262306a36Sopenharmony_ci	 * Send a unit to the global lock semaphore. The actual acquisition
13362306a36Sopenharmony_ci	 * of the global lock will be performed by the waiting thread.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1);
13662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
13762306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore"));
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	acpi_gbl_global_lock_pending = FALSE;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cicleanup_and_exit:
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
14562306a36Sopenharmony_ci	return (ACPI_INTERRUPT_HANDLED);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/******************************************************************************
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * FUNCTION:    acpi_ev_acquire_global_lock
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * PARAMETERS:  timeout         - Max time to wait for the lock, in millisec.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * RETURN:      Status
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * DESCRIPTION: Attempt to gain ownership of the Global Lock.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * MUTEX:       Interpreter must be locked
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * Note: The original implementation allowed multiple threads to "acquire" the
16162306a36Sopenharmony_ci * Global Lock, and the OS would hold the lock until the last thread had
16262306a36Sopenharmony_ci * released it. However, this could potentially starve the BIOS out of the
16362306a36Sopenharmony_ci * lock, especially in the case where there is a tight handshake between the
16462306a36Sopenharmony_ci * Embedded Controller driver and the BIOS. Therefore, this implementation
16562306a36Sopenharmony_ci * allows only one thread to acquire the HW Global Lock at a time, and makes
16662306a36Sopenharmony_ci * the global lock appear as a standard mutex on the OS side.
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci *****************************************************************************/
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciacpi_status acpi_ev_acquire_global_lock(u16 timeout)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	acpi_cpu_flags flags;
17362306a36Sopenharmony_ci	acpi_status status;
17462306a36Sopenharmony_ci	u8 acquired = FALSE;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ev_acquire_global_lock);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * Only one thread can acquire the GL at a time, the global_lock_mutex
18062306a36Sopenharmony_ci	 * enforces this. This interface releases the interpreter if we must wait.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	status =
18362306a36Sopenharmony_ci	    acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex.
18462306a36Sopenharmony_ci				      os_mutex, timeout);
18562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
18662306a36Sopenharmony_ci		return_ACPI_STATUS(status);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/*
19062306a36Sopenharmony_ci	 * Update the global lock handle and check for wraparound. The handle is
19162306a36Sopenharmony_ci	 * only used for the external global lock interfaces, but it is updated
19262306a36Sopenharmony_ci	 * here to properly handle the case where a single thread may acquire the
19362306a36Sopenharmony_ci	 * lock via both the AML and the acpi_acquire_global_lock interfaces. The
19462306a36Sopenharmony_ci	 * handle is therefore updated on the first acquire from a given thread
19562306a36Sopenharmony_ci	 * regardless of where the acquisition request originated.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	acpi_gbl_global_lock_handle++;
19862306a36Sopenharmony_ci	if (acpi_gbl_global_lock_handle == 0) {
19962306a36Sopenharmony_ci		acpi_gbl_global_lock_handle = 1;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/*
20362306a36Sopenharmony_ci	 * Make sure that a global lock actually exists. If not, just
20462306a36Sopenharmony_ci	 * treat the lock as a standard mutex.
20562306a36Sopenharmony_ci	 */
20662306a36Sopenharmony_ci	if (!acpi_gbl_global_lock_present) {
20762306a36Sopenharmony_ci		acpi_gbl_global_lock_acquired = TRUE;
20862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	do {
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		/* Attempt to acquire the actual hardware lock */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired);
21862306a36Sopenharmony_ci		if (acquired) {
21962306a36Sopenharmony_ci			acpi_gbl_global_lock_acquired = TRUE;
22062306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
22162306a36Sopenharmony_ci					  "Acquired hardware Global Lock\n"));
22262306a36Sopenharmony_ci			break;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		/*
22662306a36Sopenharmony_ci		 * Did not get the lock. The pending bit was set above, and
22762306a36Sopenharmony_ci		 * we must now wait until we receive the global lock
22862306a36Sopenharmony_ci		 * released interrupt.
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		acpi_gbl_global_lock_pending = TRUE;
23162306a36Sopenharmony_ci		acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
23462306a36Sopenharmony_ci				  "Waiting for hardware Global Lock\n"));
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		/*
23762306a36Sopenharmony_ci		 * Wait for handshake with the global lock interrupt handler.
23862306a36Sopenharmony_ci		 * This interface releases the interpreter if we must wait.
23962306a36Sopenharmony_ci		 */
24062306a36Sopenharmony_ci		status =
24162306a36Sopenharmony_ci		    acpi_ex_system_wait_semaphore
24262306a36Sopenharmony_ci		    (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	} while (ACPI_SUCCESS(status));
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	acpi_gbl_global_lock_pending = FALSE;
24962306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return_ACPI_STATUS(status);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/*******************************************************************************
25562306a36Sopenharmony_ci *
25662306a36Sopenharmony_ci * FUNCTION:    acpi_ev_release_global_lock
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * PARAMETERS:  None
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * RETURN:      Status
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * DESCRIPTION: Releases ownership of the Global Lock.
26362306a36Sopenharmony_ci *
26462306a36Sopenharmony_ci ******************************************************************************/
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ciacpi_status acpi_ev_release_global_lock(void)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	u8 pending = FALSE;
26962306a36Sopenharmony_ci	acpi_status status = AE_OK;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ev_release_global_lock);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Lock must be already acquired */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (!acpi_gbl_global_lock_acquired) {
27662306a36Sopenharmony_ci		ACPI_WARNING((AE_INFO,
27762306a36Sopenharmony_ci			      "Cannot release the ACPI Global Lock, it has not been acquired"));
27862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NOT_ACQUIRED);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (acpi_gbl_global_lock_present) {
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* Allow any thread to release the lock */
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		/*
28862306a36Sopenharmony_ci		 * If the pending bit was set, we must write GBL_RLS to the control
28962306a36Sopenharmony_ci		 * register
29062306a36Sopenharmony_ci		 */
29162306a36Sopenharmony_ci		if (pending) {
29262306a36Sopenharmony_ci			status =
29362306a36Sopenharmony_ci			    acpi_write_bit_register
29462306a36Sopenharmony_ci			    (ACPI_BITREG_GLOBAL_LOCK_RELEASE,
29562306a36Sopenharmony_ci			     ACPI_ENABLE_EVENT);
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
29962306a36Sopenharmony_ci				  "Released hardware Global Lock\n"));
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	acpi_gbl_global_lock_acquired = FALSE;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* Release the local GL mutex */
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
30762306a36Sopenharmony_ci	return_ACPI_STATUS(status);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci#endif				/* !ACPI_REDUCED_HARDWARE */
311