Branch data Line data Source code
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 - 2020, 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 : 3 : acpi_status acpi_ev_init_global_lock_handler(void) 34 : : { 35 : 3 : acpi_status status; 36 : : 37 : 3 : ACPI_FUNCTION_TRACE(ev_init_global_lock_handler); 38 : : 39 : : /* If Hardware Reduced flag is set, there is no global lock */ 40 : : 41 [ + - ]: 3 : if (acpi_gbl_reduced_hardware) { 42 : : return_ACPI_STATUS(AE_OK); 43 : : } 44 : : 45 : : /* Attempt installation of the global lock handler */ 46 : : 47 : 3 : 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 : 3 : acpi_gbl_global_lock_present = FALSE; 58 [ - + ]: 3 : if (status == AE_NO_HARDWARE_RESPONSE) { 59 : 0 : ACPI_ERROR((AE_INFO, 60 : : "No response from Global Lock hardware, disabling lock")); 61 : : 62 : 0 : return_ACPI_STATUS(AE_OK); 63 : : } 64 : : 65 [ + - + - ]: 3 : status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock); 66 : 3 : if (ACPI_FAILURE(status)) { 67 : : return_ACPI_STATUS(status); 68 : : } 69 : : 70 : 3 : acpi_gbl_global_lock_pending = FALSE; 71 : 3 : acpi_gbl_global_lock_present = TRUE; 72 : 3 : 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 : 0 : acpi_status acpi_ev_remove_global_lock_handler(void) 88 : : { 89 : 0 : acpi_status status; 90 : : 91 : 0 : ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler); 92 : : 93 : 0 : acpi_gbl_global_lock_present = FALSE; 94 : 0 : status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL, 95 : : acpi_ev_global_lock_handler); 96 : : 97 : 0 : acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock); 98 : 0 : 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 : 0 : static u32 acpi_ev_global_lock_handler(void *context) 116 : : { 117 : 0 : acpi_status status; 118 : 0 : acpi_cpu_flags flags; 119 : : 120 : 0 : 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 [ # # ]: 0 : if (!acpi_gbl_global_lock_pending) { 128 : 0 : 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 : 0 : status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1); 136 [ # # ]: 0 : if (ACPI_FAILURE(status)) { 137 : 0 : ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore")); 138 : : } 139 : : 140 : 0 : acpi_gbl_global_lock_pending = FALSE; 141 : : 142 : 0 : cleanup_and_exit: 143 : : 144 : 0 : acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); 145 : 0 : 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 : 24 : acpi_status acpi_ev_acquire_global_lock(u16 timeout) 171 : : { 172 : 24 : acpi_cpu_flags flags; 173 : 24 : acpi_status status; 174 : 24 : u8 acquired = FALSE; 175 : : 176 : 24 : 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 : 24 : status = 183 : 24 : acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex. 184 : : os_mutex, timeout); 185 [ + - ]: 24 : 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 : 24 : acpi_gbl_global_lock_handle++; 198 [ - + ]: 24 : if (acpi_gbl_global_lock_handle == 0) { 199 : 0 : 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 [ - + ]: 24 : if (!acpi_gbl_global_lock_present) { 207 : 0 : acpi_gbl_global_lock_acquired = TRUE; 208 : 0 : return_ACPI_STATUS(AE_OK); 209 : : } 210 : : 211 : 24 : flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); 212 : : 213 : 24 : do { 214 : : 215 : : /* Attempt to acquire the actual hardware lock */ 216 : : 217 : 24 : ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); 218 [ + - ]: 24 : if (acquired) { 219 : 24 : acpi_gbl_global_lock_acquired = TRUE; 220 : : ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 221 : 24 : "Acquired hardware Global Lock\n")); 222 : 24 : 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 : 0 : acpi_gbl_global_lock_pending = TRUE; 231 : 0 : acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); 232 : : 233 : : ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 234 : 0 : "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 : 0 : status = 241 : 0 : acpi_ex_system_wait_semaphore 242 : : (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER); 243 : : 244 : 0 : flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); 245 : : 246 [ # # ]: 0 : } while (ACPI_SUCCESS(status)); 247 : : 248 : 24 : acpi_gbl_global_lock_pending = FALSE; 249 : 24 : acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); 250 : : 251 : 24 : 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 : 24 : acpi_status acpi_ev_release_global_lock(void) 267 : : { 268 : 24 : u8 pending = FALSE; 269 : 24 : acpi_status status = AE_OK; 270 : : 271 : 24 : ACPI_FUNCTION_TRACE(ev_release_global_lock); 272 : : 273 : : /* Lock must be already acquired */ 274 : : 275 [ - + ]: 24 : if (!acpi_gbl_global_lock_acquired) { 276 : 0 : ACPI_WARNING((AE_INFO, 277 : : "Cannot release the ACPI Global Lock, it has not been acquired")); 278 : 0 : return_ACPI_STATUS(AE_NOT_ACQUIRED); 279 : : } 280 : : 281 [ + - ]: 24 : if (acpi_gbl_global_lock_present) { 282 : : 283 : : /* Allow any thread to release the lock */ 284 : : 285 : 24 : 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 [ - + ]: 24 : if (pending) { 292 : 0 : status = 293 : 0 : acpi_write_bit_register 294 : : (ACPI_BITREG_GLOBAL_LOCK_RELEASE, 295 : : ACPI_ENABLE_EVENT); 296 : : } 297 : : 298 : : ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 299 : 24 : "Released hardware Global Lock\n")); 300 : : } 301 : : 302 : 24 : acpi_gbl_global_lock_acquired = FALSE; 303 : : 304 : : /* Release the local GL mutex */ 305 : : 306 : 24 : acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex); 307 : 24 : return_ACPI_STATUS(status); 308 : : } 309 : : 310 : : #endif /* !ACPI_REDUCED_HARDWARE */