diff options
author | Jung-uk Kim <jkim@FreeBSD.org> | 2011-04-13 18:18:52 +0000 |
---|---|---|
committer | Jung-uk Kim <jkim@FreeBSD.org> | 2011-04-13 18:18:52 +0000 |
commit | 997de4e17cf02a81027df8d01a4fcefe25da3796 (patch) | |
tree | f5abd67cc9e3ada1ae289fe7cf2e23d9abf07fcc /events/evglock.c | |
parent | 4d8fe534b7309d798d941e14e51985eed6b511bc (diff) | |
download | src-997de4e17cf02a81027df8d01a4fcefe25da3796.tar.gz src-997de4e17cf02a81027df8d01a4fcefe25da3796.zip |
Import ACPICA 20110413.vendor/acpica/20110413
Notes
Notes:
svn path=/vendor-sys/acpica/dist/; revision=220604
svn path=/vendor-sys/acpica/20110413/; revision=220605; tag=vendor/acpica/20110413
Diffstat (limited to 'events/evglock.c')
-rw-r--r-- | events/evglock.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/events/evglock.c b/events/evglock.c new file mode 100644 index 000000000000..cab444d06d67 --- /dev/null +++ b/events/evglock.c @@ -0,0 +1,367 @@ +/****************************************************************************** + * + * Module Name: evglock - Global Lock support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2011, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include "acpi.h" +#include "accommon.h" +#include "acevents.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evglock") + + +/* Local prototypes */ + +static UINT32 +AcpiEvGlobalLockHandler ( + void *Context); + + +/******************************************************************************* + * + * FUNCTION: AcpiEvInitGlobalLockHandler + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for the global lock release event + * + ******************************************************************************/ + +ACPI_STATUS +AcpiEvInitGlobalLockHandler ( + void) +{ + ACPI_STATUS Status; + + + ACPI_FUNCTION_TRACE (EvInitGlobalLockHandler); + + + /* Attempt installation of the global lock handler */ + + Status = AcpiInstallFixedEventHandler (ACPI_EVENT_GLOBAL, + AcpiEvGlobalLockHandler, NULL); + + /* + * If the global lock does not exist on this platform, the attempt to + * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick). + * Map to AE_OK, but mark global lock as not present. Any attempt to + * actually use the global lock will be flagged with an error. + */ + AcpiGbl_GlobalLockPresent = FALSE; + if (Status == AE_NO_HARDWARE_RESPONSE) + { + ACPI_ERROR ((AE_INFO, + "No response from Global Lock hardware, disabling lock")); + + return_ACPI_STATUS (AE_OK); + } + + Status = AcpiOsCreateLock (&AcpiGbl_GlobalLockPendingLock); + if (ACPI_FAILURE (Status)) + { + return_ACPI_STATUS (Status); + } + + AcpiGbl_GlobalLockPending = FALSE; + AcpiGbl_GlobalLockPresent = TRUE; + return_ACPI_STATUS (Status); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiEvRemoveGlobalLockHandler + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Remove the handler for the Global Lock + * + ******************************************************************************/ + +ACPI_STATUS +AcpiEvRemoveGlobalLockHandler ( + void) +{ + ACPI_STATUS Status; + + + ACPI_FUNCTION_TRACE (EvRemoveGlobalLockHandler); + + AcpiGbl_GlobalLockPresent = FALSE; + Status = AcpiRemoveFixedEventHandler (ACPI_EVENT_GLOBAL, + AcpiEvGlobalLockHandler); + + return_ACPI_STATUS (Status); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiEvGlobalLockHandler + * + * PARAMETERS: Context - From thread interface, not used + * + * RETURN: ACPI_INTERRUPT_HANDLED + * + * DESCRIPTION: Invoked directly from the SCI handler when a global lock + * release interrupt occurs. If there is actually a pending + * request for the lock, signal the waiting thread. + * + ******************************************************************************/ + +static UINT32 +AcpiEvGlobalLockHandler ( + void *Context) +{ + ACPI_STATUS Status; + ACPI_CPU_FLAGS Flags; + + + Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock); + + /* + * If a request for the global lock is not actually pending, + * we are done. This handles "spurious" global lock interrupts + * which are possible (and have been seen) with bad BIOSs. + */ + if (!AcpiGbl_GlobalLockPending) + { + goto CleanupAndExit; + } + + /* + * Send a unit to the global lock semaphore. The actual acquisition + * of the global lock will be performed by the waiting thread. + */ + Status = AcpiOsSignalSemaphore (AcpiGbl_GlobalLockSemaphore, 1); + if (ACPI_FAILURE (Status)) + { + ACPI_ERROR ((AE_INFO, "Could not signal Global Lock semaphore")); + } + + AcpiGbl_GlobalLockPending = FALSE; + + +CleanupAndExit: + + AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags); + return (ACPI_INTERRUPT_HANDLED); +} + + +/****************************************************************************** + * + * FUNCTION: AcpiEvAcquireGlobalLock + * + * PARAMETERS: Timeout - Max time to wait for the lock, in millisec. + * + * RETURN: Status + * + * DESCRIPTION: Attempt to gain ownership of the Global Lock. + * + * MUTEX: Interpreter must be locked + * + * Note: The original implementation allowed multiple threads to "acquire" the + * Global Lock, and the OS would hold the lock until the last thread had + * released it. However, this could potentially starve the BIOS out of the + * lock, especially in the case where there is a tight handshake between the + * Embedded Controller driver and the BIOS. Therefore, this implementation + * allows only one thread to acquire the HW Global Lock at a time, and makes + * the global lock appear as a standard mutex on the OS side. + * + *****************************************************************************/ + +ACPI_STATUS +AcpiEvAcquireGlobalLock ( + UINT16 Timeout) +{ + ACPI_CPU_FLAGS Flags; + ACPI_STATUS Status; + BOOLEAN Acquired = FALSE; + + + ACPI_FUNCTION_TRACE (EvAcquireGlobalLock); + + + /* + * Only one thread can acquire the GL at a time, the GlobalLockMutex + * enforces this. This interface releases the interpreter if we must wait. + */ + Status = AcpiExSystemWaitMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex, + Timeout); + if (ACPI_FAILURE (Status)) + { + return_ACPI_STATUS (Status); + } + + /* + * Update the global lock handle and check for wraparound. The handle is + * only used for the external global lock interfaces, but it is updated + * here to properly handle the case where a single thread may acquire the + * lock via both the AML and the AcpiAcquireGlobalLock interfaces. The + * handle is therefore updated on the first acquire from a given thread + * regardless of where the acquisition request originated. + */ + AcpiGbl_GlobalLockHandle++; + if (AcpiGbl_GlobalLockHandle == 0) + { + AcpiGbl_GlobalLockHandle = 1; + } + + /* + * Make sure that a global lock actually exists. If not, just + * treat the lock as a standard mutex. + */ + if (!AcpiGbl_GlobalLockPresent) + { + AcpiGbl_GlobalLockAcquired = TRUE; + return_ACPI_STATUS (AE_OK); + } + + Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock); + + do + { + /* Attempt to acquire the actual hardware lock */ + + ACPI_ACQUIRE_GLOBAL_LOCK (AcpiGbl_FACS, Acquired); + if (Acquired) + { + AcpiGbl_GlobalLockAcquired = TRUE; + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Acquired hardware Global Lock\n")); + break; + } + + /* + * Did not get the lock. The pending bit was set above, and + * we must now wait until we receive the global lock + * released interrupt. + */ + AcpiGbl_GlobalLockPending = TRUE; + AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Waiting for hardware Global Lock\n")); + + /* + * Wait for handshake with the global lock interrupt handler. + * This interface releases the interpreter if we must wait. + */ + Status = AcpiExSystemWaitSemaphore (AcpiGbl_GlobalLockSemaphore, + ACPI_WAIT_FOREVER); + + Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock); + + } while (ACPI_SUCCESS (Status)); + + AcpiGbl_GlobalLockPending = FALSE; + AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags); + + return_ACPI_STATUS (Status); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiEvReleaseGlobalLock + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Releases ownership of the Global Lock. + * + ******************************************************************************/ + +ACPI_STATUS +AcpiEvReleaseGlobalLock ( + void) +{ + BOOLEAN Pending = FALSE; + ACPI_STATUS Status = AE_OK; + + + ACPI_FUNCTION_TRACE (EvReleaseGlobalLock); + + + /* Lock must be already acquired */ + + if (!AcpiGbl_GlobalLockAcquired) + { + ACPI_WARNING ((AE_INFO, + "Cannot release the ACPI Global Lock, it has not been acquired")); + return_ACPI_STATUS (AE_NOT_ACQUIRED); + } + + if (AcpiGbl_GlobalLockPresent) + { + /* Allow any thread to release the lock */ + + ACPI_RELEASE_GLOBAL_LOCK (AcpiGbl_FACS, Pending); + + /* + * If the pending bit was set, we must write GBL_RLS to the control + * register + */ + if (Pending) + { + Status = AcpiWriteBitRegister ( + ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Released hardware Global Lock\n")); + } + + AcpiGbl_GlobalLockAcquired = FALSE; + + /* Release the local GL mutex */ + + AcpiOsReleaseMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex); + return_ACPI_STATUS (Status); +} |