evmisc.c revision 217365
1/****************************************************************************** 2 * 3 * Module Name: evmisc - Miscellaneous event manager support functions 4 * 5 *****************************************************************************/ 6 7/* 8 * Copyright (C) 2000 - 2011, 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 <contrib/dev/acpica/include/acpi.h> 45#include <contrib/dev/acpica/include/accommon.h> 46#include <contrib/dev/acpica/include/acevents.h> 47#include <contrib/dev/acpica/include/acnamesp.h> 48#include <contrib/dev/acpica/include/acinterp.h> 49 50#define _COMPONENT ACPI_EVENTS 51 ACPI_MODULE_NAME ("evmisc") 52 53 54/* Local prototypes */ 55 56static void ACPI_SYSTEM_XFACE 57AcpiEvNotifyDispatch ( 58 void *Context); 59 60static UINT32 61AcpiEvGlobalLockHandler ( 62 void *Context); 63 64static ACPI_STATUS 65AcpiEvRemoveGlobalLockHandler ( 66 void); 67 68 69/******************************************************************************* 70 * 71 * FUNCTION: AcpiEvIsNotifyObject 72 * 73 * PARAMETERS: Node - Node to check 74 * 75 * RETURN: TRUE if notifies allowed on this object 76 * 77 * DESCRIPTION: Check type of node for a object that supports notifies. 78 * 79 * TBD: This could be replaced by a flag bit in the node. 80 * 81 ******************************************************************************/ 82 83BOOLEAN 84AcpiEvIsNotifyObject ( 85 ACPI_NAMESPACE_NODE *Node) 86{ 87 switch (Node->Type) 88 { 89 case ACPI_TYPE_DEVICE: 90 case ACPI_TYPE_PROCESSOR: 91 case ACPI_TYPE_THERMAL: 92 /* 93 * These are the ONLY objects that can receive ACPI notifications 94 */ 95 return (TRUE); 96 97 default: 98 return (FALSE); 99 } 100} 101 102 103/******************************************************************************* 104 * 105 * FUNCTION: AcpiEvQueueNotifyRequest 106 * 107 * PARAMETERS: Node - NS node for the notified object 108 * NotifyValue - Value from the Notify() request 109 * 110 * RETURN: Status 111 * 112 * DESCRIPTION: Dispatch a device notification event to a previously 113 * installed handler. 114 * 115 ******************************************************************************/ 116 117ACPI_STATUS 118AcpiEvQueueNotifyRequest ( 119 ACPI_NAMESPACE_NODE *Node, 120 UINT32 NotifyValue) 121{ 122 ACPI_OPERAND_OBJECT *ObjDesc; 123 ACPI_OPERAND_OBJECT *HandlerObj = NULL; 124 ACPI_GENERIC_STATE *NotifyInfo; 125 ACPI_STATUS Status = AE_OK; 126 127 128 ACPI_FUNCTION_NAME (EvQueueNotifyRequest); 129 130 131 /* 132 * For value 3 (Ejection Request), some device method may need to be run. 133 * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need 134 * to be run. 135 * For value 0x80 (Status Change) on the power button or sleep button, 136 * initiate soft-off or sleep operation? 137 */ 138 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, 139 "Dispatching Notify on [%4.4s] Node %p Value 0x%2.2X (%s)\n", 140 AcpiUtGetNodeName (Node), Node, NotifyValue, 141 AcpiUtGetNotifyName (NotifyValue))); 142 143 /* Get the notify object attached to the NS Node */ 144 145 ObjDesc = AcpiNsGetAttachedObject (Node); 146 if (ObjDesc) 147 { 148 /* We have the notify object, Get the right handler */ 149 150 switch (Node->Type) 151 { 152 /* Notify allowed only on these types */ 153 154 case ACPI_TYPE_DEVICE: 155 case ACPI_TYPE_THERMAL: 156 case ACPI_TYPE_PROCESSOR: 157 158 if (NotifyValue <= ACPI_MAX_SYS_NOTIFY) 159 { 160 HandlerObj = ObjDesc->CommonNotify.SystemNotify; 161 } 162 else 163 { 164 HandlerObj = ObjDesc->CommonNotify.DeviceNotify; 165 } 166 break; 167 168 default: 169 170 /* All other types are not supported */ 171 172 return (AE_TYPE); 173 } 174 } 175 176 /* 177 * If there is any handler to run, schedule the dispatcher. 178 * Check for: 179 * 1) Global system notify handler 180 * 2) Global device notify handler 181 * 3) Per-device notify handler 182 */ 183 if ((AcpiGbl_SystemNotify.Handler && 184 (NotifyValue <= ACPI_MAX_SYS_NOTIFY)) || 185 (AcpiGbl_DeviceNotify.Handler && 186 (NotifyValue > ACPI_MAX_SYS_NOTIFY)) || 187 HandlerObj) 188 { 189 NotifyInfo = AcpiUtCreateGenericState (); 190 if (!NotifyInfo) 191 { 192 return (AE_NO_MEMORY); 193 } 194 195 if (!HandlerObj) 196 { 197 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, 198 "Executing system notify handler for Notify (%4.4s, %X) " 199 "node %p\n", 200 AcpiUtGetNodeName (Node), NotifyValue, Node)); 201 } 202 203 NotifyInfo->Common.DescriptorType = ACPI_DESC_TYPE_STATE_NOTIFY; 204 NotifyInfo->Notify.Node = Node; 205 NotifyInfo->Notify.Value = (UINT16) NotifyValue; 206 NotifyInfo->Notify.HandlerObj = HandlerObj; 207 208 Status = AcpiOsExecute ( 209 OSL_NOTIFY_HANDLER, AcpiEvNotifyDispatch, NotifyInfo); 210 if (ACPI_FAILURE (Status)) 211 { 212 AcpiUtDeleteGenericState (NotifyInfo); 213 } 214 } 215 else 216 { 217 /* There is no notify handler (per-device or system) for this device */ 218 219 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, 220 "No notify handler for Notify (%4.4s, %X) node %p\n", 221 AcpiUtGetNodeName (Node), NotifyValue, Node)); 222 } 223 224 return (Status); 225} 226 227 228/******************************************************************************* 229 * 230 * FUNCTION: AcpiEvNotifyDispatch 231 * 232 * PARAMETERS: Context - To be passed to the notify handler 233 * 234 * RETURN: None. 235 * 236 * DESCRIPTION: Dispatch a device notification event to a previously 237 * installed handler. 238 * 239 ******************************************************************************/ 240 241static void ACPI_SYSTEM_XFACE 242AcpiEvNotifyDispatch ( 243 void *Context) 244{ 245 ACPI_GENERIC_STATE *NotifyInfo = (ACPI_GENERIC_STATE *) Context; 246 ACPI_NOTIFY_HANDLER GlobalHandler = NULL; 247 void *GlobalContext = NULL; 248 ACPI_OPERAND_OBJECT *HandlerObj; 249 250 251 ACPI_FUNCTION_ENTRY (); 252 253 254 /* 255 * We will invoke a global notify handler if installed. This is done 256 * _before_ we invoke the per-device handler attached to the device. 257 */ 258 if (NotifyInfo->Notify.Value <= ACPI_MAX_SYS_NOTIFY) 259 { 260 /* Global system notification handler */ 261 262 if (AcpiGbl_SystemNotify.Handler) 263 { 264 GlobalHandler = AcpiGbl_SystemNotify.Handler; 265 GlobalContext = AcpiGbl_SystemNotify.Context; 266 } 267 } 268 else 269 { 270 /* Global driver notification handler */ 271 272 if (AcpiGbl_DeviceNotify.Handler) 273 { 274 GlobalHandler = AcpiGbl_DeviceNotify.Handler; 275 GlobalContext = AcpiGbl_DeviceNotify.Context; 276 } 277 } 278 279 /* Invoke the system handler first, if present */ 280 281 if (GlobalHandler) 282 { 283 GlobalHandler (NotifyInfo->Notify.Node, NotifyInfo->Notify.Value, 284 GlobalContext); 285 } 286 287 /* Now invoke the per-device handler, if present */ 288 289 HandlerObj = NotifyInfo->Notify.HandlerObj; 290 if (HandlerObj) 291 { 292 HandlerObj->Notify.Handler (NotifyInfo->Notify.Node, 293 NotifyInfo->Notify.Value, 294 HandlerObj->Notify.Context); 295 } 296 297 /* All done with the info object */ 298 299 AcpiUtDeleteGenericState (NotifyInfo); 300} 301 302 303/******************************************************************************* 304 * 305 * FUNCTION: AcpiEvGlobalLockHandler 306 * 307 * PARAMETERS: Context - From thread interface, not used 308 * 309 * RETURN: ACPI_INTERRUPT_HANDLED 310 * 311 * DESCRIPTION: Invoked directly from the SCI handler when a global lock 312 * release interrupt occurs. Attempt to acquire the global lock, 313 * if successful, signal the thread waiting for the lock. 314 * 315 * NOTE: Assumes that the semaphore can be signaled from interrupt level. If 316 * this is not possible for some reason, a separate thread will have to be 317 * scheduled to do this. 318 * 319 ******************************************************************************/ 320 321static UINT32 322AcpiEvGlobalLockHandler ( 323 void *Context) 324{ 325 BOOLEAN Acquired = FALSE; 326 ACPI_STATUS Status; 327 328 329 /* 330 * Attempt to get the lock. 331 * 332 * If we don't get it now, it will be marked pending and we will 333 * take another interrupt when it becomes free. 334 */ 335 ACPI_ACQUIRE_GLOBAL_LOCK (AcpiGbl_FACS, Acquired); 336 if (Acquired) 337 { 338 /* Got the lock, now wake the thread waiting for it */ 339 340 AcpiGbl_GlobalLockAcquired = TRUE; 341 342 /* Send a unit to the semaphore */ 343 344 Status = AcpiOsSignalSemaphore (AcpiGbl_GlobalLockSemaphore, 1); 345 if (ACPI_FAILURE (Status)) 346 { 347 ACPI_ERROR ((AE_INFO, "Could not signal Global Lock semaphore")); 348 } 349 } 350 351 return (ACPI_INTERRUPT_HANDLED); 352} 353 354 355/******************************************************************************* 356 * 357 * FUNCTION: AcpiEvInitGlobalLockHandler 358 * 359 * PARAMETERS: None 360 * 361 * RETURN: Status 362 * 363 * DESCRIPTION: Install a handler for the global lock release event 364 * 365 ******************************************************************************/ 366 367ACPI_STATUS 368AcpiEvInitGlobalLockHandler ( 369 void) 370{ 371 ACPI_STATUS Status; 372 373 374 ACPI_FUNCTION_TRACE (EvInitGlobalLockHandler); 375 376 377 /* Attempt installation of the global lock handler */ 378 379 Status = AcpiInstallFixedEventHandler (ACPI_EVENT_GLOBAL, 380 AcpiEvGlobalLockHandler, NULL); 381 382 /* 383 * If the global lock does not exist on this platform, the attempt to 384 * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick). 385 * Map to AE_OK, but mark global lock as not present. Any attempt to 386 * actually use the global lock will be flagged with an error. 387 */ 388 if (Status == AE_NO_HARDWARE_RESPONSE) 389 { 390 ACPI_ERROR ((AE_INFO, 391 "No response from Global Lock hardware, disabling lock")); 392 393 AcpiGbl_GlobalLockPresent = FALSE; 394 return_ACPI_STATUS (AE_OK); 395 } 396 397 AcpiGbl_GlobalLockPresent = TRUE; 398 return_ACPI_STATUS (Status); 399} 400 401 402/******************************************************************************* 403 * 404 * FUNCTION: AcpiEvRemoveGlobalLockHandler 405 * 406 * PARAMETERS: None 407 * 408 * RETURN: Status 409 * 410 * DESCRIPTION: Remove the handler for the Global Lock 411 * 412 ******************************************************************************/ 413 414static ACPI_STATUS 415AcpiEvRemoveGlobalLockHandler ( 416 void) 417{ 418 ACPI_STATUS Status; 419 420 421 ACPI_FUNCTION_TRACE (EvRemoveGlobalLockHandler); 422 423 AcpiGbl_GlobalLockPresent = FALSE; 424 Status = AcpiRemoveFixedEventHandler (ACPI_EVENT_GLOBAL, 425 AcpiEvGlobalLockHandler); 426 427 return_ACPI_STATUS (Status); 428} 429 430 431/****************************************************************************** 432 * 433 * FUNCTION: AcpiEvAcquireGlobalLock 434 * 435 * PARAMETERS: Timeout - Max time to wait for the lock, in millisec. 436 * 437 * RETURN: Status 438 * 439 * DESCRIPTION: Attempt to gain ownership of the Global Lock. 440 * 441 * MUTEX: Interpreter must be locked 442 * 443 * Note: The original implementation allowed multiple threads to "acquire" the 444 * Global Lock, and the OS would hold the lock until the last thread had 445 * released it. However, this could potentially starve the BIOS out of the 446 * lock, especially in the case where there is a tight handshake between the 447 * Embedded Controller driver and the BIOS. Therefore, this implementation 448 * allows only one thread to acquire the HW Global Lock at a time, and makes 449 * the global lock appear as a standard mutex on the OS side. 450 * 451 *****************************************************************************/ 452 453ACPI_STATUS 454AcpiEvAcquireGlobalLock ( 455 UINT16 Timeout) 456{ 457 ACPI_STATUS Status = AE_OK; 458 BOOLEAN Acquired = FALSE; 459 460 461 ACPI_FUNCTION_TRACE (EvAcquireGlobalLock); 462 463 464 /* 465 * Only one thread can acquire the GL at a time, the GlobalLockMutex 466 * enforces this. This interface releases the interpreter if we must wait. 467 */ 468 Status = AcpiExSystemWaitMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex, 469 Timeout); 470 if (ACPI_FAILURE (Status)) 471 { 472 return_ACPI_STATUS (Status); 473 } 474 475 /* 476 * Update the global lock handle and check for wraparound. The handle is 477 * only used for the external global lock interfaces, but it is updated 478 * here to properly handle the case where a single thread may acquire the 479 * lock via both the AML and the AcpiAcquireGlobalLock interfaces. The 480 * handle is therefore updated on the first acquire from a given thread 481 * regardless of where the acquisition request originated. 482 */ 483 AcpiGbl_GlobalLockHandle++; 484 if (AcpiGbl_GlobalLockHandle == 0) 485 { 486 AcpiGbl_GlobalLockHandle = 1; 487 } 488 489 /* 490 * Make sure that a global lock actually exists. If not, just treat the 491 * lock as a standard mutex. 492 */ 493 if (!AcpiGbl_GlobalLockPresent) 494 { 495 AcpiGbl_GlobalLockAcquired = TRUE; 496 return_ACPI_STATUS (AE_OK); 497 } 498 499 /* Attempt to acquire the actual hardware lock */ 500 501 ACPI_ACQUIRE_GLOBAL_LOCK (AcpiGbl_FACS, Acquired); 502 if (Acquired) 503 { 504 /* We got the lock */ 505 506 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Acquired hardware Global Lock\n")); 507 508 AcpiGbl_GlobalLockAcquired = TRUE; 509 return_ACPI_STATUS (AE_OK); 510 } 511 512 /* 513 * Did not get the lock. The pending bit was set above, and we must now 514 * wait until we get the global lock released interrupt. 515 */ 516 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Waiting for hardware Global Lock\n")); 517 518 /* 519 * Wait for handshake with the global lock interrupt handler. 520 * This interface releases the interpreter if we must wait. 521 */ 522 Status = AcpiExSystemWaitSemaphore (AcpiGbl_GlobalLockSemaphore, 523 ACPI_WAIT_FOREVER); 524 525 return_ACPI_STATUS (Status); 526} 527 528 529/******************************************************************************* 530 * 531 * FUNCTION: AcpiEvReleaseGlobalLock 532 * 533 * PARAMETERS: None 534 * 535 * RETURN: Status 536 * 537 * DESCRIPTION: Releases ownership of the Global Lock. 538 * 539 ******************************************************************************/ 540 541ACPI_STATUS 542AcpiEvReleaseGlobalLock ( 543 void) 544{ 545 BOOLEAN Pending = FALSE; 546 ACPI_STATUS Status = AE_OK; 547 548 549 ACPI_FUNCTION_TRACE (EvReleaseGlobalLock); 550 551 552 /* Lock must be already acquired */ 553 554 if (!AcpiGbl_GlobalLockAcquired) 555 { 556 ACPI_WARNING ((AE_INFO, 557 "Cannot release the ACPI Global Lock, it has not been acquired")); 558 return_ACPI_STATUS (AE_NOT_ACQUIRED); 559 } 560 561 if (AcpiGbl_GlobalLockPresent) 562 { 563 /* Allow any thread to release the lock */ 564 565 ACPI_RELEASE_GLOBAL_LOCK (AcpiGbl_FACS, Pending); 566 567 /* 568 * If the pending bit was set, we must write GBL_RLS to the control 569 * register 570 */ 571 if (Pending) 572 { 573 Status = AcpiWriteBitRegister ( 574 ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT); 575 } 576 577 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Released hardware Global Lock\n")); 578 } 579 580 AcpiGbl_GlobalLockAcquired = FALSE; 581 582 /* Release the local GL mutex */ 583 584 AcpiOsReleaseMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex); 585 return_ACPI_STATUS (Status); 586} 587 588 589/****************************************************************************** 590 * 591 * FUNCTION: AcpiEvTerminate 592 * 593 * PARAMETERS: none 594 * 595 * RETURN: none 596 * 597 * DESCRIPTION: Disable events and free memory allocated for table storage. 598 * 599 ******************************************************************************/ 600 601void 602AcpiEvTerminate ( 603 void) 604{ 605 UINT32 i; 606 ACPI_STATUS Status; 607 608 609 ACPI_FUNCTION_TRACE (EvTerminate); 610 611 612 if (AcpiGbl_EventsInitialized) 613 { 614 /* 615 * Disable all event-related functionality. In all cases, on error, 616 * print a message but obviously we don't abort. 617 */ 618 619 /* Disable all fixed events */ 620 621 for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) 622 { 623 Status = AcpiDisableEvent (i, 0); 624 if (ACPI_FAILURE (Status)) 625 { 626 ACPI_ERROR ((AE_INFO, 627 "Could not disable fixed event %u", (UINT32) i)); 628 } 629 } 630 631 /* Disable all GPEs in all GPE blocks */ 632 633 Status = AcpiEvWalkGpeList (AcpiHwDisableGpeBlock, NULL); 634 635 /* Remove SCI handler */ 636 637 Status = AcpiEvRemoveSciHandler (); 638 if (ACPI_FAILURE(Status)) 639 { 640 ACPI_ERROR ((AE_INFO, 641 "Could not remove SCI handler")); 642 } 643 644 Status = AcpiEvRemoveGlobalLockHandler (); 645 if (ACPI_FAILURE(Status)) 646 { 647 ACPI_ERROR ((AE_INFO, 648 "Could not remove Global Lock handler")); 649 } 650 } 651 652 /* Deallocate all handler objects installed within GPE info structs */ 653 654 Status = AcpiEvWalkGpeList (AcpiEvDeleteGpeHandlers, NULL); 655 656 /* Return to original mode if necessary */ 657 658 if (AcpiGbl_OriginalMode == ACPI_SYS_MODE_LEGACY) 659 { 660 Status = AcpiDisable (); 661 if (ACPI_FAILURE (Status)) 662 { 663 ACPI_WARNING ((AE_INFO, "AcpiDisable failed")); 664 } 665 } 666 return_VOID; 667} 668 669