311 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/******************************************************************************3 *4 * Module Name: evglock - Global Lock support5 *6 * Copyright (C) 2000 - 2023, Intel Corp.7 *8 *****************************************************************************/9 10#include <acpi/acpi.h>11#include "accommon.h"12#include "acevents.h"13#include "acinterp.h"14 15#define _COMPONENT ACPI_EVENTS16ACPI_MODULE_NAME("evglock")17#if (!ACPI_REDUCED_HARDWARE) /* Entire module */18/* Local prototypes */19static u32 acpi_ev_global_lock_handler(void *context);20 21/*******************************************************************************22 *23 * FUNCTION: acpi_ev_init_global_lock_handler24 *25 * PARAMETERS: None26 *27 * RETURN: Status28 *29 * DESCRIPTION: Install a handler for the global lock release event30 *31 ******************************************************************************/32 33acpi_status acpi_ev_init_global_lock_handler(void)34{35 acpi_status status;36 37 ACPI_FUNCTION_TRACE(ev_init_global_lock_handler);38 39 /* If Hardware Reduced flag is set, there is no global lock */40 41 if (acpi_gbl_reduced_hardware) {42 return_ACPI_STATUS(AE_OK);43 }44 45 /* Attempt installation of the global lock handler */46 47 status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL,48 acpi_ev_global_lock_handler,49 NULL);50 51 /*52 * If the global lock does not exist on this platform, the attempt to53 * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick).54 * Map to AE_OK, but mark global lock as not present. Any attempt to55 * actually use the global lock will be flagged with an error.56 */57 acpi_gbl_global_lock_present = FALSE;58 if (status == AE_NO_HARDWARE_RESPONSE) {59 ACPI_ERROR((AE_INFO,60 "No response from Global Lock hardware, disabling lock"));61 62 return_ACPI_STATUS(AE_OK);63 }64 65 status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock);66 if (ACPI_FAILURE(status)) {67 return_ACPI_STATUS(status);68 }69 70 acpi_gbl_global_lock_pending = FALSE;71 acpi_gbl_global_lock_present = TRUE;72 return_ACPI_STATUS(status);73}74 75/*******************************************************************************76 *77 * FUNCTION: acpi_ev_remove_global_lock_handler78 *79 * PARAMETERS: None80 *81 * RETURN: Status82 *83 * DESCRIPTION: Remove the handler for the Global Lock84 *85 ******************************************************************************/86 87acpi_status acpi_ev_remove_global_lock_handler(void)88{89 acpi_status status;90 91 ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler);92 93 acpi_gbl_global_lock_present = FALSE;94 status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL,95 acpi_ev_global_lock_handler);96 97 acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock);98 return_ACPI_STATUS(status);99}100 101/*******************************************************************************102 *103 * FUNCTION: acpi_ev_global_lock_handler104 *105 * PARAMETERS: context - From thread interface, not used106 *107 * RETURN: ACPI_INTERRUPT_HANDLED108 *109 * DESCRIPTION: Invoked directly from the SCI handler when a global lock110 * release interrupt occurs. If there is actually a pending111 * request for the lock, signal the waiting thread.112 *113 ******************************************************************************/114 115static u32 acpi_ev_global_lock_handler(void *context)116{117 acpi_status status;118 acpi_cpu_flags flags;119 120 flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);121 122 /*123 * If a request for the global lock is not actually pending,124 * we are done. This handles "spurious" global lock interrupts125 * which are possible (and have been seen) with bad BIOSs.126 */127 if (!acpi_gbl_global_lock_pending) {128 goto cleanup_and_exit;129 }130 131 /*132 * Send a unit to the global lock semaphore. The actual acquisition133 * of the global lock will be performed by the waiting thread.134 */135 status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1);136 if (ACPI_FAILURE(status)) {137 ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore"));138 }139 140 acpi_gbl_global_lock_pending = FALSE;141 142cleanup_and_exit:143 144 acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);145 return (ACPI_INTERRUPT_HANDLED);146}147 148/******************************************************************************149 *150 * FUNCTION: acpi_ev_acquire_global_lock151 *152 * PARAMETERS: timeout - Max time to wait for the lock, in millisec.153 *154 * RETURN: Status155 *156 * DESCRIPTION: Attempt to gain ownership of the Global Lock.157 *158 * MUTEX: Interpreter must be locked159 *160 * Note: The original implementation allowed multiple threads to "acquire" the161 * Global Lock, and the OS would hold the lock until the last thread had162 * released it. However, this could potentially starve the BIOS out of the163 * lock, especially in the case where there is a tight handshake between the164 * Embedded Controller driver and the BIOS. Therefore, this implementation165 * allows only one thread to acquire the HW Global Lock at a time, and makes166 * the global lock appear as a standard mutex on the OS side.167 *168 *****************************************************************************/169 170acpi_status acpi_ev_acquire_global_lock(u16 timeout)171{172 acpi_cpu_flags flags;173 acpi_status status;174 u8 acquired = FALSE;175 176 ACPI_FUNCTION_TRACE(ev_acquire_global_lock);177 178 /*179 * Only one thread can acquire the GL at a time, the global_lock_mutex180 * enforces this. This interface releases the interpreter if we must wait.181 */182 status =183 acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex.184 os_mutex, timeout);185 if (ACPI_FAILURE(status)) {186 return_ACPI_STATUS(status);187 }188 189 /*190 * Update the global lock handle and check for wraparound. The handle is191 * only used for the external global lock interfaces, but it is updated192 * here to properly handle the case where a single thread may acquire the193 * lock via both the AML and the acpi_acquire_global_lock interfaces. The194 * handle is therefore updated on the first acquire from a given thread195 * regardless of where the acquisition request originated.196 */197 acpi_gbl_global_lock_handle++;198 if (acpi_gbl_global_lock_handle == 0) {199 acpi_gbl_global_lock_handle = 1;200 }201 202 /*203 * Make sure that a global lock actually exists. If not, just204 * treat the lock as a standard mutex.205 */206 if (!acpi_gbl_global_lock_present) {207 acpi_gbl_global_lock_acquired = TRUE;208 return_ACPI_STATUS(AE_OK);209 }210 211 flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);212 213 do {214 215 /* Attempt to acquire the actual hardware lock */216 217 ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired);218 if (acquired) {219 acpi_gbl_global_lock_acquired = TRUE;220 ACPI_DEBUG_PRINT((ACPI_DB_EXEC,221 "Acquired hardware Global Lock\n"));222 break;223 }224 225 /*226 * Did not get the lock. The pending bit was set above, and227 * we must now wait until we receive the global lock228 * released interrupt.229 */230 acpi_gbl_global_lock_pending = TRUE;231 acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);232 233 ACPI_DEBUG_PRINT((ACPI_DB_EXEC,234 "Waiting for hardware Global Lock\n"));235 236 /*237 * Wait for handshake with the global lock interrupt handler.238 * This interface releases the interpreter if we must wait.239 */240 status =241 acpi_ex_system_wait_semaphore242 (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER);243 244 flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);245 246 } while (ACPI_SUCCESS(status));247 248 acpi_gbl_global_lock_pending = FALSE;249 acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);250 251 return_ACPI_STATUS(status);252}253 254/*******************************************************************************255 *256 * FUNCTION: acpi_ev_release_global_lock257 *258 * PARAMETERS: None259 *260 * RETURN: Status261 *262 * DESCRIPTION: Releases ownership of the Global Lock.263 *264 ******************************************************************************/265 266acpi_status acpi_ev_release_global_lock(void)267{268 u8 pending = FALSE;269 acpi_status status = AE_OK;270 271 ACPI_FUNCTION_TRACE(ev_release_global_lock);272 273 /* Lock must be already acquired */274 275 if (!acpi_gbl_global_lock_acquired) {276 ACPI_WARNING((AE_INFO,277 "Cannot release the ACPI Global Lock, it has not been acquired"));278 return_ACPI_STATUS(AE_NOT_ACQUIRED);279 }280 281 if (acpi_gbl_global_lock_present) {282 283 /* Allow any thread to release the lock */284 285 ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending);286 287 /*288 * If the pending bit was set, we must write GBL_RLS to the control289 * register290 */291 if (pending) {292 status =293 acpi_write_bit_register294 (ACPI_BITREG_GLOBAL_LOCK_RELEASE,295 ACPI_ENABLE_EVENT);296 }297 298 ACPI_DEBUG_PRINT((ACPI_DB_EXEC,299 "Released hardware Global Lock\n"));300 }301 302 acpi_gbl_global_lock_acquired = FALSE;303 304 /* Release the local GL mutex */305 306 acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);307 return_ACPI_STATUS(status);308}309 310#endif /* !ACPI_REDUCED_HARDWARE */311