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