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        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
 148static 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
 203acpi_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
 299acpi_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