1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /****************************************************************************** 3 * 4 * Module Name: evglock - Global Lock support 5 * 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_EVENTS 16 ACPI_MODULE_NAME("evglock") 17 #if (!ACPI_REDUCED_HARDWARE) /* Entire module */ 18 /* Local prototypes */ 19 static u32 acpi_ev_global_lock_handler(void *context); 20 21 /******************************************************************************* 22 * 23 * FUNCTION: acpi_ev_init_global_lock_handler 24 * 25 * PARAMETERS: None 26 * 27 * RETURN: Status 28 * 29 * DESCRIPTION: Install a handler for the global lock release event 30 * 31 ******************************************************************************/ 32 33 acpi_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 to 53 * 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 to 55 * 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_handler 78 * 79 * PARAMETERS: None 80 * 81 * RETURN: Status 82 * 83 * DESCRIPTION: Remove the handler for the Global Lock 84 * 85 ******************************************************************************/ 86 87 acpi_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_handler 104 * 105 * PARAMETERS: context - From thread interface, not used 106 * 107 * RETURN: ACPI_INTERRUPT_HANDLED 108 * 109 * DESCRIPTION: Invoked directly from the SCI handler when a global lock 110 * release interrupt occurs. If there is actually a pending 111 * request for the lock, signal the waiting thread. 112 * 113 ******************************************************************************/ 114 115 static 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 interrupts 125 * 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 acquisition 133 * 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 142 cleanup_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_lock 151 * 152 * PARAMETERS: timeout - Max time to wait for the lock, in millisec. 153 * 154 * RETURN: Status 155 * 156 * DESCRIPTION: Attempt to gain ownership of the Global Lock. 157 * 158 * MUTEX: Interpreter must be locked 159 * 160 * Note: The original implementation allowed multiple threads to "acquire" the 161 * Global Lock, and the OS would hold the lock until the last thread had 162 * released it. However, this could potentially starve the BIOS out of the 163 * lock, especially in the case where there is a tight handshake between the 164 * Embedded Controller driver and the BIOS. Therefore, this implementation 165 * allows only one thread to acquire the HW Global Lock at a time, and makes 166 * the global lock appear as a standard mutex on the OS side. 167 * 168 *****************************************************************************/ 169 170 acpi_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_mutex 180 * 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 is 191 * only used for the external global lock interfaces, but it is updated 192 * here to properly handle the case where a single thread may acquire the 193 * lock via both the AML and the acpi_acquire_global_lock interfaces. The 194 * handle is therefore updated on the first acquire from a given thread 195 * 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, just 204 * 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, and 227 * we must now wait until we receive the global lock 228 * 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_semaphore 242 (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_lock 257 * 258 * PARAMETERS: None 259 * 260 * RETURN: Status 261 * 262 * DESCRIPTION: Releases ownership of the Global Lock. 263 * 264 ******************************************************************************/ 265 266 acpi_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 control 289 * register 290 */ 291 if (pending) { 292 status = 293 acpi_write_bit_register 294 (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