evmisc.c revision 219707
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. If there is actually a pending 313 * request for the lock, signal the waiting thread. 314 * 315 ******************************************************************************/ 316 317static UINT32 318AcpiEvGlobalLockHandler ( 319 void *Context) 320{ 321 ACPI_STATUS Status; 322 ACPI_CPU_FLAGS Flags; 323 324 325 Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock); 326 327 /* 328 * If a request for the global lock is not actually pending, 329 * we are done. This handles "spurious" global lock interrupts 330 * which are possible (and have been seen) with bad BIOSs. 331 */ 332 if (!AcpiGbl_GlobalLockPending) 333 { 334 goto CleanupAndExit; 335 } 336 337 /* 338 * Send a unit to the global lock semaphore. The actual acquisition 339 * of the global lock will be performed by the waiting thread. 340 */ 341 Status = AcpiOsSignalSemaphore (AcpiGbl_GlobalLockSemaphore, 1); 342 if (ACPI_FAILURE (Status)) 343 { 344 ACPI_ERROR ((AE_INFO, "Could not signal Global Lock semaphore")); 345 } 346 347 AcpiGbl_GlobalLockPending = FALSE; 348 349 350CleanupAndExit: 351 352 AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags); 353 return (ACPI_INTERRUPT_HANDLED); 354} 355 356 357/******************************************************************************* 358 * 359 * FUNCTION: AcpiEvInitGlobalLockHandler 360 * 361 * PARAMETERS: None 362 * 363 * RETURN: Status 364 * 365 * DESCRIPTION: Install a handler for the global lock release event 366 * 367 ******************************************************************************/ 368 369ACPI_STATUS 370AcpiEvInitGlobalLockHandler ( 371 void) 372{ 373 ACPI_STATUS Status; 374 375 376 ACPI_FUNCTION_TRACE (EvInitGlobalLockHandler); 377 378 379 /* Attempt installation of the global lock handler */ 380 381 Status = AcpiInstallFixedEventHandler (ACPI_EVENT_GLOBAL, 382 AcpiEvGlobalLockHandler, NULL); 383 384 /* 385 * If the global lock does not exist on this platform, the attempt to 386 * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick). 387 * Map to AE_OK, but mark global lock as not present. Any attempt to 388 * actually use the global lock will be flagged with an error. 389 */ 390 AcpiGbl_GlobalLockPresent = FALSE; 391 if (Status == AE_NO_HARDWARE_RESPONSE) 392 { 393 ACPI_ERROR ((AE_INFO, 394 "No response from Global Lock hardware, disabling lock")); 395 396 return_ACPI_STATUS (AE_OK); 397 } 398 399 Status = AcpiOsCreateLock (&AcpiGbl_GlobalLockPendingLock); 400 if (ACPI_FAILURE (Status)) 401 { 402 return_ACPI_STATUS (Status); 403 } 404 405 AcpiGbl_GlobalLockPending = FALSE; 406 AcpiGbl_GlobalLockPresent = TRUE; 407 return_ACPI_STATUS (Status); 408} 409 410 411/******************************************************************************* 412 * 413 * FUNCTION: AcpiEvRemoveGlobalLockHandler 414 * 415 * PARAMETERS: None 416 * 417 * RETURN: Status 418 * 419 * DESCRIPTION: Remove the handler for the Global Lock 420 * 421 ******************************************************************************/ 422 423static ACPI_STATUS 424AcpiEvRemoveGlobalLockHandler ( 425 void) 426{ 427 ACPI_STATUS Status; 428 429 430 ACPI_FUNCTION_TRACE (EvRemoveGlobalLockHandler); 431 432 AcpiGbl_GlobalLockPresent = FALSE; 433 Status = AcpiRemoveFixedEventHandler (ACPI_EVENT_GLOBAL, 434 AcpiEvGlobalLockHandler); 435 436 return_ACPI_STATUS (Status); 437} 438 439 440/****************************************************************************** 441 * 442 * FUNCTION: AcpiEvAcquireGlobalLock 443 * 444 * PARAMETERS: Timeout - Max time to wait for the lock, in millisec. 445 * 446 * RETURN: Status 447 * 448 * DESCRIPTION: Attempt to gain ownership of the Global Lock. 449 * 450 * MUTEX: Interpreter must be locked 451 * 452 * Note: The original implementation allowed multiple threads to "acquire" the 453 * Global Lock, and the OS would hold the lock until the last thread had 454 * released it. However, this could potentially starve the BIOS out of the 455 * lock, especially in the case where there is a tight handshake between the 456 * Embedded Controller driver and the BIOS. Therefore, this implementation 457 * allows only one thread to acquire the HW Global Lock at a time, and makes 458 * the global lock appear as a standard mutex on the OS side. 459 * 460 *****************************************************************************/ 461 462ACPI_STATUS 463AcpiEvAcquireGlobalLock ( 464 UINT16 Timeout) 465{ 466 ACPI_CPU_FLAGS Flags; 467 ACPI_STATUS Status; 468 BOOLEAN Acquired = FALSE; 469 470 471 ACPI_FUNCTION_TRACE (EvAcquireGlobalLock); 472 473 474 /* 475 * Only one thread can acquire the GL at a time, the GlobalLockMutex 476 * enforces this. This interface releases the interpreter if we must wait. 477 */ 478 Status = AcpiExSystemWaitMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex, 479 Timeout); 480 if (ACPI_FAILURE (Status)) 481 { 482 return_ACPI_STATUS (Status); 483 } 484 485 /* 486 * Update the global lock handle and check for wraparound. The handle is 487 * only used for the external global lock interfaces, but it is updated 488 * here to properly handle the case where a single thread may acquire the 489 * lock via both the AML and the AcpiAcquireGlobalLock interfaces. The 490 * handle is therefore updated on the first acquire from a given thread 491 * regardless of where the acquisition request originated. 492 */ 493 AcpiGbl_GlobalLockHandle++; 494 if (AcpiGbl_GlobalLockHandle == 0) 495 { 496 AcpiGbl_GlobalLockHandle = 1; 497 } 498 499 /* 500 * Make sure that a global lock actually exists. If not, just 501 * treat the lock as a standard mutex. 502 */ 503 if (!AcpiGbl_GlobalLockPresent) 504 { 505 AcpiGbl_GlobalLockAcquired = TRUE; 506 return_ACPI_STATUS (AE_OK); 507 } 508 509 Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock); 510 511 do 512 { 513 /* Attempt to acquire the actual hardware lock */ 514 515 ACPI_ACQUIRE_GLOBAL_LOCK (AcpiGbl_FACS, Acquired); 516 if (Acquired) 517 { 518 AcpiGbl_GlobalLockAcquired = TRUE; 519 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, 520 "Acquired hardware Global Lock\n")); 521 break; 522 } 523 524 /* 525 * Did not get the lock. The pending bit was set above, and 526 * we must now wait until we receive the global lock 527 * released interrupt. 528 */ 529 AcpiGbl_GlobalLockPending = TRUE; 530 AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags); 531 532 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, 533 "Waiting for hardware Global Lock\n")); 534 535 /* 536 * Wait for handshake with the global lock interrupt handler. 537 * This interface releases the interpreter if we must wait. 538 */ 539 Status = AcpiExSystemWaitSemaphore (AcpiGbl_GlobalLockSemaphore, 540 ACPI_WAIT_FOREVER); 541 542 Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock); 543 544 } while (ACPI_SUCCESS (Status)); 545 546 AcpiGbl_GlobalLockPending = FALSE; 547 AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags); 548 549 return_ACPI_STATUS (Status); 550} 551 552 553/******************************************************************************* 554 * 555 * FUNCTION: AcpiEvReleaseGlobalLock 556 * 557 * PARAMETERS: None 558 * 559 * RETURN: Status 560 * 561 * DESCRIPTION: Releases ownership of the Global Lock. 562 * 563 ******************************************************************************/ 564 565ACPI_STATUS 566AcpiEvReleaseGlobalLock ( 567 void) 568{ 569 BOOLEAN Pending = FALSE; 570 ACPI_STATUS Status = AE_OK; 571 572 573 ACPI_FUNCTION_TRACE (EvReleaseGlobalLock); 574 575 576 /* Lock must be already acquired */ 577 578 if (!AcpiGbl_GlobalLockAcquired) 579 { 580 ACPI_WARNING ((AE_INFO, 581 "Cannot release the ACPI Global Lock, it has not been acquired")); 582 return_ACPI_STATUS (AE_NOT_ACQUIRED); 583 } 584 585 if (AcpiGbl_GlobalLockPresent) 586 { 587 /* Allow any thread to release the lock */ 588 589 ACPI_RELEASE_GLOBAL_LOCK (AcpiGbl_FACS, Pending); 590 591 /* 592 * If the pending bit was set, we must write GBL_RLS to the control 593 * register 594 */ 595 if (Pending) 596 { 597 Status = AcpiWriteBitRegister ( 598 ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT); 599 } 600 601 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Released hardware Global Lock\n")); 602 } 603 604 AcpiGbl_GlobalLockAcquired = FALSE; 605 606 /* Release the local GL mutex */ 607 608 AcpiOsReleaseMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex); 609 return_ACPI_STATUS (Status); 610} 611 612 613/****************************************************************************** 614 * 615 * FUNCTION: AcpiEvTerminate 616 * 617 * PARAMETERS: none 618 * 619 * RETURN: none 620 * 621 * DESCRIPTION: Disable events and free memory allocated for table storage. 622 * 623 ******************************************************************************/ 624 625void 626AcpiEvTerminate ( 627 void) 628{ 629 UINT32 i; 630 ACPI_STATUS Status; 631 632 633 ACPI_FUNCTION_TRACE (EvTerminate); 634 635 636 if (AcpiGbl_EventsInitialized) 637 { 638 /* 639 * Disable all event-related functionality. In all cases, on error, 640 * print a message but obviously we don't abort. 641 */ 642 643 /* Disable all fixed events */ 644 645 for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) 646 { 647 Status = AcpiDisableEvent (i, 0); 648 if (ACPI_FAILURE (Status)) 649 { 650 ACPI_ERROR ((AE_INFO, 651 "Could not disable fixed event %u", (UINT32) i)); 652 } 653 } 654 655 /* Disable all GPEs in all GPE blocks */ 656 657 Status = AcpiEvWalkGpeList (AcpiHwDisableGpeBlock, NULL); 658 659 /* Remove SCI handler */ 660 661 Status = AcpiEvRemoveSciHandler (); 662 if (ACPI_FAILURE(Status)) 663 { 664 ACPI_ERROR ((AE_INFO, 665 "Could not remove SCI handler")); 666 } 667 668 Status = AcpiEvRemoveGlobalLockHandler (); 669 if (ACPI_FAILURE(Status)) 670 { 671 ACPI_ERROR ((AE_INFO, 672 "Could not remove Global Lock handler")); 673 } 674 } 675 676 /* Deallocate all handler objects installed within GPE info structs */ 677 678 Status = AcpiEvWalkGpeList (AcpiEvDeleteGpeHandlers, NULL); 679 680 /* Return to original mode if necessary */ 681 682 if (AcpiGbl_OriginalMode == ACPI_SYS_MODE_LEGACY) 683 { 684 Status = AcpiDisable (); 685 if (ACPI_FAILURE (Status)) 686 { 687 ACPI_WARNING ((AE_INFO, "AcpiDisable failed")); 688 } 689 } 690 return_VOID; 691} 692 693