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