linux/drivers/acpi/acpica/evglock.c
<<
>>
Prefs
   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
  50ACPI_MODULE_NAME("evglock")
  51#if (!ACPI_REDUCED_HARDWARE)    /* Entire module */
  52/* Local prototypes */
  53static 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
  67acpi_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
 121acpi_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        acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock);
 132        return_ACPI_STATUS(status);
 133}
 134
 135/*******************************************************************************
 136 *
 137 * FUNCTION:    acpi_ev_global_lock_handler
 138 *
 139 * PARAMETERS:  context         - From thread interface, not used
 140 *
 141 * RETURN:      ACPI_INTERRUPT_HANDLED
 142 *
 143 * DESCRIPTION: Invoked directly from the SCI handler when a global lock
 144 *              release interrupt occurs. If there is actually a pending
 145 *              request for the lock, signal the waiting thread.
 146 *
 147 ******************************************************************************/
 148
 149static u32 acpi_ev_global_lock_handler(void *context)
 150{
 151        acpi_status status;
 152        acpi_cpu_flags flags;
 153
 154        flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
 155
 156        /*
 157         * If a request for the global lock is not actually pending,
 158         * we are done. This handles "spurious" global lock interrupts
 159         * which are possible (and have been seen) with bad BIOSs.
 160         */
 161        if (!acpi_gbl_global_lock_pending) {
 162                goto cleanup_and_exit;
 163        }
 164
 165        /*
 166         * Send a unit to the global lock semaphore. The actual acquisition
 167         * of the global lock will be performed by the waiting thread.
 168         */
 169        status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1);
 170        if (ACPI_FAILURE(status)) {
 171                ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore"));
 172        }
 173
 174        acpi_gbl_global_lock_pending = FALSE;
 175
 176      cleanup_and_exit:
 177
 178        acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
 179        return (ACPI_INTERRUPT_HANDLED);
 180}
 181
 182/******************************************************************************
 183 *
 184 * FUNCTION:    acpi_ev_acquire_global_lock
 185 *
 186 * PARAMETERS:  timeout         - Max time to wait for the lock, in millisec.
 187 *
 188 * RETURN:      Status
 189 *
 190 * DESCRIPTION: Attempt to gain ownership of the Global Lock.
 191 *
 192 * MUTEX:       Interpreter must be locked
 193 *
 194 * Note: The original implementation allowed multiple threads to "acquire" the
 195 * Global Lock, and the OS would hold the lock until the last thread had
 196 * released it. However, this could potentially starve the BIOS out of the
 197 * lock, especially in the case where there is a tight handshake between the
 198 * Embedded Controller driver and the BIOS. Therefore, this implementation
 199 * allows only one thread to acquire the HW Global Lock at a time, and makes
 200 * the global lock appear as a standard mutex on the OS side.
 201 *
 202 *****************************************************************************/
 203
 204acpi_status acpi_ev_acquire_global_lock(u16 timeout)
 205{
 206        acpi_cpu_flags flags;
 207        acpi_status status;
 208        u8 acquired = FALSE;
 209
 210        ACPI_FUNCTION_TRACE(ev_acquire_global_lock);
 211
 212        /*
 213         * Only one thread can acquire the GL at a time, the global_lock_mutex
 214         * enforces this. This interface releases the interpreter if we must wait.
 215         */
 216        status =
 217            acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex.
 218                                      os_mutex, timeout);
 219        if (ACPI_FAILURE(status)) {
 220                return_ACPI_STATUS(status);
 221        }
 222
 223        /*
 224         * Update the global lock handle and check for wraparound. The handle is
 225         * only used for the external global lock interfaces, but it is updated
 226         * here to properly handle the case where a single thread may acquire the
 227         * lock via both the AML and the acpi_acquire_global_lock interfaces. The
 228         * handle is therefore updated on the first acquire from a given thread
 229         * regardless of where the acquisition request originated.
 230         */
 231        acpi_gbl_global_lock_handle++;
 232        if (acpi_gbl_global_lock_handle == 0) {
 233                acpi_gbl_global_lock_handle = 1;
 234        }
 235
 236        /*
 237         * Make sure that a global lock actually exists. If not, just
 238         * treat the lock as a standard mutex.
 239         */
 240        if (!acpi_gbl_global_lock_present) {
 241                acpi_gbl_global_lock_acquired = TRUE;
 242                return_ACPI_STATUS(AE_OK);
 243        }
 244
 245        flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
 246
 247        do {
 248
 249                /* Attempt to acquire the actual hardware lock */
 250
 251                ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired);
 252                if (acquired) {
 253                        acpi_gbl_global_lock_acquired = TRUE;
 254                        ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
 255                                          "Acquired hardware Global Lock\n"));
 256                        break;
 257                }
 258
 259                /*
 260                 * Did not get the lock. The pending bit was set above, and
 261                 * we must now wait until we receive the global lock
 262                 * released interrupt.
 263                 */
 264                acpi_gbl_global_lock_pending = TRUE;
 265                acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
 266
 267                ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
 268                                  "Waiting for hardware Global Lock\n"));
 269
 270                /*
 271                 * Wait for handshake with the global lock interrupt handler.
 272                 * This interface releases the interpreter if we must wait.
 273                 */
 274                status =
 275                    acpi_ex_system_wait_semaphore
 276                    (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER);
 277
 278                flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
 279
 280        } while (ACPI_SUCCESS(status));
 281
 282        acpi_gbl_global_lock_pending = FALSE;
 283        acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
 284
 285        return_ACPI_STATUS(status);
 286}
 287
 288/*******************************************************************************
 289 *
 290 * FUNCTION:    acpi_ev_release_global_lock
 291 *
 292 * PARAMETERS:  None
 293 *
 294 * RETURN:      Status
 295 *
 296 * DESCRIPTION: Releases ownership of the Global Lock.
 297 *
 298 ******************************************************************************/
 299
 300acpi_status acpi_ev_release_global_lock(void)
 301{
 302        u8 pending = FALSE;
 303        acpi_status status = AE_OK;
 304
 305        ACPI_FUNCTION_TRACE(ev_release_global_lock);
 306
 307        /* Lock must be already acquired */
 308
 309        if (!acpi_gbl_global_lock_acquired) {
 310                ACPI_WARNING((AE_INFO,
 311                              "Cannot release the ACPI Global Lock, it has not been acquired"));
 312                return_ACPI_STATUS(AE_NOT_ACQUIRED);
 313        }
 314
 315        if (acpi_gbl_global_lock_present) {
 316
 317                /* Allow any thread to release the lock */
 318
 319                ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending);
 320
 321                /*
 322                 * If the pending bit was set, we must write GBL_RLS to the control
 323                 * register
 324                 */
 325                if (pending) {
 326                        status =
 327                            acpi_write_bit_register
 328                            (ACPI_BITREG_GLOBAL_LOCK_RELEASE,
 329                             ACPI_ENABLE_EVENT);
 330                }
 331
 332                ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
 333                                  "Released hardware Global Lock\n"));
 334        }
 335
 336        acpi_gbl_global_lock_acquired = FALSE;
 337
 338        /* Release the local GL mutex */
 339
 340        acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
 341        return_ACPI_STATUS(status);
 342}
 343
 344#endif                          /* !ACPI_REDUCED_HARDWARE */
 345