1 /****************************************************************************** 2 * 3 * Module Name: evglock - Global Lock support 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2013, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include <acpi/acpi.h> 45 #include "accommon.h" 46 #include "acevents.h" 47 #include "acinterp.h" 48 49 #define _COMPONENT ACPI_EVENTS 50 ACPI_MODULE_NAME("evglock") 51 #if (!ACPI_REDUCED_HARDWARE) /* Entire module */ 52 /* Local prototypes */ 53 static u32 acpi_ev_global_lock_handler(void *context); 54 55 /******************************************************************************* 56 * 57 * FUNCTION: acpi_ev_init_global_lock_handler 58 * 59 * PARAMETERS: None 60 * 61 * RETURN: Status 62 * 63 * DESCRIPTION: Install a handler for the global lock release event 64 * 65 ******************************************************************************/ 66 67 acpi_status acpi_ev_init_global_lock_handler(void) 68 { 69 acpi_status status; 70 71 ACPI_FUNCTION_TRACE(ev_init_global_lock_handler); 72 73 /* If Hardware Reduced flag is set, there is no global lock */ 74 75 if (acpi_gbl_reduced_hardware) { 76 return_ACPI_STATUS(AE_OK); 77 } 78 79 /* Attempt installation of the global lock handler */ 80 81 status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL, 82 acpi_ev_global_lock_handler, 83 NULL); 84 85 /* 86 * If the global lock does not exist on this platform, the attempt to 87 * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick). 88 * Map to AE_OK, but mark global lock as not present. Any attempt to 89 * actually use the global lock will be flagged with an error. 90 */ 91 acpi_gbl_global_lock_present = FALSE; 92 if (status == AE_NO_HARDWARE_RESPONSE) { 93 ACPI_ERROR((AE_INFO, 94 "No response from Global Lock hardware, disabling lock")); 95 96 return_ACPI_STATUS(AE_OK); 97 } 98 99 status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock); 100 if (ACPI_FAILURE(status)) { 101 return_ACPI_STATUS(status); 102 } 103 104 acpi_gbl_global_lock_pending = FALSE; 105 acpi_gbl_global_lock_present = TRUE; 106 return_ACPI_STATUS(status); 107 } 108 109 /******************************************************************************* 110 * 111 * FUNCTION: acpi_ev_remove_global_lock_handler 112 * 113 * PARAMETERS: None 114 * 115 * RETURN: Status 116 * 117 * DESCRIPTION: Remove the handler for the Global Lock 118 * 119 ******************************************************************************/ 120 121 acpi_status acpi_ev_remove_global_lock_handler(void) 122 { 123 acpi_status status; 124 125 ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler); 126 127 acpi_gbl_global_lock_present = FALSE; 128 status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL, 129 acpi_ev_global_lock_handler); 130 131 return_ACPI_STATUS(status); 132 } 133 134 /******************************************************************************* 135 * 136 * FUNCTION: acpi_ev_global_lock_handler 137 * 138 * PARAMETERS: context - From thread interface, not used 139 * 140 * RETURN: ACPI_INTERRUPT_HANDLED 141 * 142 * DESCRIPTION: Invoked directly from the SCI handler when a global lock 143 * release interrupt occurs. If there is actually a pending 144 * request for the lock, signal the waiting thread. 145 * 146 ******************************************************************************/ 147 148 static u32 acpi_ev_global_lock_handler(void *context) 149 { 150 acpi_status status; 151 acpi_cpu_flags flags; 152 153 flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); 154 155 /* 156 * If a request for the global lock is not actually pending, 157 * we are done. This handles "spurious" global lock interrupts 158 * which are possible (and have been seen) with bad BIOSs. 159 */ 160 if (!acpi_gbl_global_lock_pending) { 161 goto cleanup_and_exit; 162 } 163 164 /* 165 * Send a unit to the global lock semaphore. The actual acquisition 166 * of the global lock will be performed by the waiting thread. 167 */ 168 status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1); 169 if (ACPI_FAILURE(status)) { 170 ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore")); 171 } 172 173 acpi_gbl_global_lock_pending = FALSE; 174 175 cleanup_and_exit: 176 177 acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); 178 return (ACPI_INTERRUPT_HANDLED); 179 } 180 181 /****************************************************************************** 182 * 183 * FUNCTION: acpi_ev_acquire_global_lock 184 * 185 * PARAMETERS: timeout - Max time to wait for the lock, in millisec. 186 * 187 * RETURN: Status 188 * 189 * DESCRIPTION: Attempt to gain ownership of the Global Lock. 190 * 191 * MUTEX: Interpreter must be locked 192 * 193 * Note: The original implementation allowed multiple threads to "acquire" the 194 * Global Lock, and the OS would hold the lock until the last thread had 195 * released it. However, this could potentially starve the BIOS out of the 196 * lock, especially in the case where there is a tight handshake between the 197 * Embedded Controller driver and the BIOS. Therefore, this implementation 198 * allows only one thread to acquire the HW Global Lock at a time, and makes 199 * the global lock appear as a standard mutex on the OS side. 200 * 201 *****************************************************************************/ 202 203 acpi_status acpi_ev_acquire_global_lock(u16 timeout) 204 { 205 acpi_cpu_flags flags; 206 acpi_status status; 207 u8 acquired = FALSE; 208 209 ACPI_FUNCTION_TRACE(ev_acquire_global_lock); 210 211 /* 212 * Only one thread can acquire the GL at a time, the global_lock_mutex 213 * enforces this. This interface releases the interpreter if we must wait. 214 */ 215 status = 216 acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex. 217 os_mutex, timeout); 218 if (ACPI_FAILURE(status)) { 219 return_ACPI_STATUS(status); 220 } 221 222 /* 223 * Update the global lock handle and check for wraparound. The handle is 224 * only used for the external global lock interfaces, but it is updated 225 * here to properly handle the case where a single thread may acquire the 226 * lock via both the AML and the acpi_acquire_global_lock interfaces. The 227 * handle is therefore updated on the first acquire from a given thread 228 * regardless of where the acquisition request originated. 229 */ 230 acpi_gbl_global_lock_handle++; 231 if (acpi_gbl_global_lock_handle == 0) { 232 acpi_gbl_global_lock_handle = 1; 233 } 234 235 /* 236 * Make sure that a global lock actually exists. If not, just 237 * treat the lock as a standard mutex. 238 */ 239 if (!acpi_gbl_global_lock_present) { 240 acpi_gbl_global_lock_acquired = TRUE; 241 return_ACPI_STATUS(AE_OK); 242 } 243 244 flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); 245 246 do { 247 248 /* Attempt to acquire the actual hardware lock */ 249 250 ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); 251 if (acquired) { 252 acpi_gbl_global_lock_acquired = TRUE; 253 ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 254 "Acquired hardware Global Lock\n")); 255 break; 256 } 257 258 /* 259 * Did not get the lock. The pending bit was set above, and 260 * we must now wait until we receive the global lock 261 * released interrupt. 262 */ 263 acpi_gbl_global_lock_pending = TRUE; 264 acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); 265 266 ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 267 "Waiting for hardware Global Lock\n")); 268 269 /* 270 * Wait for handshake with the global lock interrupt handler. 271 * This interface releases the interpreter if we must wait. 272 */ 273 status = 274 acpi_ex_system_wait_semaphore 275 (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER); 276 277 flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); 278 279 } while (ACPI_SUCCESS(status)); 280 281 acpi_gbl_global_lock_pending = FALSE; 282 acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); 283 284 return_ACPI_STATUS(status); 285 } 286 287 /******************************************************************************* 288 * 289 * FUNCTION: acpi_ev_release_global_lock 290 * 291 * PARAMETERS: None 292 * 293 * RETURN: Status 294 * 295 * DESCRIPTION: Releases ownership of the Global Lock. 296 * 297 ******************************************************************************/ 298 299 acpi_status acpi_ev_release_global_lock(void) 300 { 301 u8 pending = FALSE; 302 acpi_status status = AE_OK; 303 304 ACPI_FUNCTION_TRACE(ev_release_global_lock); 305 306 /* Lock must be already acquired */ 307 308 if (!acpi_gbl_global_lock_acquired) { 309 ACPI_WARNING((AE_INFO, 310 "Cannot release the ACPI Global Lock, it has not been acquired")); 311 return_ACPI_STATUS(AE_NOT_ACQUIRED); 312 } 313 314 if (acpi_gbl_global_lock_present) { 315 316 /* Allow any thread to release the lock */ 317 318 ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending); 319 320 /* 321 * If the pending bit was set, we must write GBL_RLS to the control 322 * register 323 */ 324 if (pending) { 325 status = 326 acpi_write_bit_register 327 (ACPI_BITREG_GLOBAL_LOCK_RELEASE, 328 ACPI_ENABLE_EVENT); 329 } 330 331 ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 332 "Released hardware Global Lock\n")); 333 } 334 335 acpi_gbl_global_lock_acquired = FALSE; 336 337 /* Release the local GL mutex */ 338 339 acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex); 340 return_ACPI_STATUS(status); 341 } 342 343 #endif /* !ACPI_REDUCED_HARDWARE */ 344