1 /******************************************************************************
   2  *
   3  * Module Name: evglock - Global Lock support
   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 "acpi.h"
  45 #include "accommon.h"
  46 #include "acevents.h"
  47 #include "acinterp.h"
  48 
  49 #define _COMPONENT          ACPI_EVENTS
  50         ACPI_MODULE_NAME    ("evglock")
  51 
  52 
  53 /* Local prototypes */
  54 
  55 static UINT32
  56 AcpiEvGlobalLockHandler (
  57     void                    *Context);
  58 
  59 
  60 /*******************************************************************************
  61  *
  62  * FUNCTION:    AcpiEvInitGlobalLockHandler
  63  *
  64  * PARAMETERS:  None
  65  *
  66  * RETURN:      Status
  67  *
  68  * DESCRIPTION: Install a handler for the global lock release event
  69  *
  70  ******************************************************************************/
  71 
  72 ACPI_STATUS
  73 AcpiEvInitGlobalLockHandler (
  74     void)
  75 {
  76     ACPI_STATUS             Status;
  77 
  78 
  79     ACPI_FUNCTION_TRACE (EvInitGlobalLockHandler);
  80 
  81 
  82     /* Attempt installation of the global lock handler */
  83 
  84     Status = AcpiInstallFixedEventHandler (ACPI_EVENT_GLOBAL,
  85                 AcpiEvGlobalLockHandler, NULL);
  86 
  87     /*
  88      * If the global lock does not exist on this platform, the attempt to
  89      * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick).
  90      * Map to AE_OK, but mark global lock as not present. Any attempt to
  91      * actually use the global lock will be flagged with an error.
  92      */
  93     AcpiGbl_GlobalLockPresent = FALSE;
  94     if (Status == AE_NO_HARDWARE_RESPONSE)
  95     {
  96         ACPI_ERROR ((AE_INFO,
  97             "No response from Global Lock hardware, disabling lock"));
  98 
  99         return_ACPI_STATUS (AE_OK);
 100     }
 101 
 102     Status = AcpiOsCreateLock (&AcpiGbl_GlobalLockPendingLock);
 103     if (ACPI_FAILURE (Status))
 104     {
 105         return_ACPI_STATUS (Status);
 106     }
 107 
 108     AcpiGbl_GlobalLockPending = FALSE;
 109     AcpiGbl_GlobalLockPresent = TRUE;
 110     return_ACPI_STATUS (Status);
 111 }
 112 
 113 
 114 /*******************************************************************************
 115  *
 116  * FUNCTION:    AcpiEvRemoveGlobalLockHandler
 117  *
 118  * PARAMETERS:  None
 119  *
 120  * RETURN:      Status
 121  *
 122  * DESCRIPTION: Remove the handler for the Global Lock
 123  *
 124  ******************************************************************************/
 125 
 126 ACPI_STATUS
 127 AcpiEvRemoveGlobalLockHandler (
 128     void)
 129 {
 130     ACPI_STATUS             Status;
 131 
 132 
 133     ACPI_FUNCTION_TRACE (EvRemoveGlobalLockHandler);
 134 
 135     AcpiGbl_GlobalLockPresent = FALSE;
 136     Status = AcpiRemoveFixedEventHandler (ACPI_EVENT_GLOBAL,
 137                 AcpiEvGlobalLockHandler);
 138 
 139     return_ACPI_STATUS (Status);
 140 }
 141 
 142 
 143 /*******************************************************************************
 144  *
 145  * FUNCTION:    AcpiEvGlobalLockHandler
 146  *
 147  * PARAMETERS:  Context         - From thread interface, not used
 148  *
 149  * RETURN:      ACPI_INTERRUPT_HANDLED
 150  *
 151  * DESCRIPTION: Invoked directly from the SCI handler when a global lock
 152  *              release interrupt occurs. If there is actually a pending
 153  *              request for the lock, signal the waiting thread.
 154  *
 155  ******************************************************************************/
 156 
 157 static UINT32
 158 AcpiEvGlobalLockHandler (
 159     void                    *Context)
 160 {
 161     ACPI_STATUS             Status;
 162     ACPI_CPU_FLAGS          Flags;
 163 
 164 
 165     Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
 166 
 167     /*
 168      * If a request for the global lock is not actually pending,
 169      * we are done. This handles "spurious" global lock interrupts
 170      * which are possible (and have been seen) with bad BIOSs.
 171      */
 172     if (!AcpiGbl_GlobalLockPending)
 173     {
 174         goto CleanupAndExit;
 175     }
 176 
 177     /*
 178      * Send a unit to the global lock semaphore. The actual acquisition
 179      * of the global lock will be performed by the waiting thread.
 180      */
 181     Status = AcpiOsSignalSemaphore (AcpiGbl_GlobalLockSemaphore, 1);
 182     if (ACPI_FAILURE (Status))
 183     {
 184         ACPI_ERROR ((AE_INFO, "Could not signal Global Lock semaphore"));
 185     }
 186 
 187     AcpiGbl_GlobalLockPending = FALSE;
 188 
 189 
 190 CleanupAndExit:
 191 
 192     AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags);
 193     return (ACPI_INTERRUPT_HANDLED);
 194 }
 195 
 196 
 197 /******************************************************************************
 198  *
 199  * FUNCTION:    AcpiEvAcquireGlobalLock
 200  *
 201  * PARAMETERS:  Timeout         - Max time to wait for the lock, in millisec.
 202  *
 203  * RETURN:      Status
 204  *
 205  * DESCRIPTION: Attempt to gain ownership of the Global Lock.
 206  *
 207  * MUTEX:       Interpreter must be locked
 208  *
 209  * Note: The original implementation allowed multiple threads to "acquire" the
 210  * Global Lock, and the OS would hold the lock until the last thread had
 211  * released it. However, this could potentially starve the BIOS out of the
 212  * lock, especially in the case where there is a tight handshake between the
 213  * Embedded Controller driver and the BIOS. Therefore, this implementation
 214  * allows only one thread to acquire the HW Global Lock at a time, and makes
 215  * the global lock appear as a standard mutex on the OS side.
 216  *
 217  *****************************************************************************/
 218 
 219 ACPI_STATUS
 220 AcpiEvAcquireGlobalLock (
 221     UINT16                  Timeout)
 222 {
 223     ACPI_CPU_FLAGS          Flags;
 224     ACPI_STATUS             Status;
 225     BOOLEAN                 Acquired = FALSE;
 226 
 227 
 228     ACPI_FUNCTION_TRACE (EvAcquireGlobalLock);
 229 
 230 
 231     /*
 232      * Only one thread can acquire the GL at a time, the GlobalLockMutex
 233      * enforces this. This interface releases the interpreter if we must wait.
 234      */
 235     Status = AcpiExSystemWaitMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex,
 236                 Timeout);
 237     if (ACPI_FAILURE (Status))
 238     {
 239         return_ACPI_STATUS (Status);
 240     }
 241 
 242     /*
 243      * Update the global lock handle and check for wraparound. The handle is
 244      * only used for the external global lock interfaces, but it is updated
 245      * here to properly handle the case where a single thread may acquire the
 246      * lock via both the AML and the AcpiAcquireGlobalLock interfaces. The
 247      * handle is therefore updated on the first acquire from a given thread
 248      * regardless of where the acquisition request originated.
 249      */
 250     AcpiGbl_GlobalLockHandle++;
 251     if (AcpiGbl_GlobalLockHandle == 0)
 252     {
 253         AcpiGbl_GlobalLockHandle = 1;
 254     }
 255 
 256     /*
 257      * Make sure that a global lock actually exists. If not, just
 258      * treat the lock as a standard mutex.
 259      */
 260     if (!AcpiGbl_GlobalLockPresent)
 261     {
 262         AcpiGbl_GlobalLockAcquired = TRUE;
 263         return_ACPI_STATUS (AE_OK);
 264     }
 265 
 266     Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
 267 
 268     do
 269     {
 270         /* Attempt to acquire the actual hardware lock */
 271 
 272         ACPI_ACQUIRE_GLOBAL_LOCK (AcpiGbl_FACS, Acquired);
 273         if (Acquired)
 274         {
 275             AcpiGbl_GlobalLockAcquired = TRUE;
 276             ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
 277                 "Acquired hardware Global Lock\n"));
 278             break;
 279         }
 280 
 281         /*
 282          * Did not get the lock. The pending bit was set above, and
 283          * we must now wait until we receive the global lock
 284          * released interrupt.
 285          */
 286         AcpiGbl_GlobalLockPending = TRUE;
 287         AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags);
 288 
 289         ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
 290             "Waiting for hardware Global Lock\n"));
 291 
 292         /*
 293          * Wait for handshake with the global lock interrupt handler.
 294          * This interface releases the interpreter if we must wait.
 295          */
 296         Status = AcpiExSystemWaitSemaphore (AcpiGbl_GlobalLockSemaphore,
 297                     ACPI_WAIT_FOREVER);
 298 
 299         Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
 300 
 301     } while (ACPI_SUCCESS (Status));
 302 
 303     AcpiGbl_GlobalLockPending = FALSE;
 304     AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags);
 305 
 306     return_ACPI_STATUS (Status);
 307 }
 308 
 309 
 310 /*******************************************************************************
 311  *
 312  * FUNCTION:    AcpiEvReleaseGlobalLock
 313  *
 314  * PARAMETERS:  None
 315  *
 316  * RETURN:      Status
 317  *
 318  * DESCRIPTION: Releases ownership of the Global Lock.
 319  *
 320  ******************************************************************************/
 321 
 322 ACPI_STATUS
 323 AcpiEvReleaseGlobalLock (
 324     void)
 325 {
 326     BOOLEAN                 Pending = FALSE;
 327     ACPI_STATUS             Status = AE_OK;
 328 
 329 
 330     ACPI_FUNCTION_TRACE (EvReleaseGlobalLock);
 331 
 332 
 333     /* Lock must be already acquired */
 334 
 335     if (!AcpiGbl_GlobalLockAcquired)
 336     {
 337         ACPI_WARNING ((AE_INFO,
 338             "Cannot release the ACPI Global Lock, it has not been acquired"));
 339         return_ACPI_STATUS (AE_NOT_ACQUIRED);
 340     }
 341 
 342     if (AcpiGbl_GlobalLockPresent)
 343     {
 344         /* Allow any thread to release the lock */
 345 
 346         ACPI_RELEASE_GLOBAL_LOCK (AcpiGbl_FACS, Pending);
 347 
 348         /*
 349          * If the pending bit was set, we must write GBL_RLS to the control
 350          * register
 351          */
 352         if (Pending)
 353         {
 354             Status = AcpiWriteBitRegister (
 355                         ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT);
 356         }
 357 
 358         ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Released hardware Global Lock\n"));
 359     }
 360 
 361     AcpiGbl_GlobalLockAcquired = FALSE;
 362 
 363     /* Release the local GL mutex */
 364 
 365     AcpiOsReleaseMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex);
 366     return_ACPI_STATUS (Status);
 367 }