1220604Sjkim/******************************************************************************
2220604Sjkim *
3220604Sjkim * Module Name: evglock - Global Lock support
4220604Sjkim *
5220604Sjkim *****************************************************************************/
6220604Sjkim
7220604Sjkim/*
8281075Sdim * Copyright (C) 2000 - 2015, Intel Corp.
9220604Sjkim * All rights reserved.
10220604Sjkim *
11220604Sjkim * Redistribution and use in source and binary forms, with or without
12220604Sjkim * modification, are permitted provided that the following conditions
13220604Sjkim * are met:
14220604Sjkim * 1. Redistributions of source code must retain the above copyright
15220604Sjkim *    notice, this list of conditions, and the following disclaimer,
16220604Sjkim *    without modification.
17220604Sjkim * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18220604Sjkim *    substantially similar to the "NO WARRANTY" disclaimer below
19220604Sjkim *    ("Disclaimer") and any redistribution must be conditioned upon
20220604Sjkim *    including a substantially similar Disclaimer requirement for further
21220604Sjkim *    binary redistribution.
22220604Sjkim * 3. Neither the names of the above-listed copyright holders nor the names
23220604Sjkim *    of any contributors may be used to endorse or promote products derived
24220604Sjkim *    from this software without specific prior written permission.
25220604Sjkim *
26220604Sjkim * Alternatively, this software may be distributed under the terms of the
27220604Sjkim * GNU General Public License ("GPL") version 2 as published by the Free
28220604Sjkim * Software Foundation.
29220604Sjkim *
30220604Sjkim * NO WARRANTY
31220604Sjkim * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32220604Sjkim * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33220604Sjkim * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34220604Sjkim * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35220604Sjkim * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36220604Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37220604Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38220604Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39220604Sjkim * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40220604Sjkim * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41220604Sjkim * POSSIBILITY OF SUCH DAMAGES.
42220604Sjkim */
43220604Sjkim
44220663Sjkim#include <contrib/dev/acpica/include/acpi.h>
45220663Sjkim#include <contrib/dev/acpica/include/accommon.h>
46220663Sjkim#include <contrib/dev/acpica/include/acevents.h>
47220663Sjkim#include <contrib/dev/acpica/include/acinterp.h>
48220604Sjkim
49220604Sjkim#define _COMPONENT          ACPI_EVENTS
50220604Sjkim        ACPI_MODULE_NAME    ("evglock")
51220604Sjkim
52231844Sjkim#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
53220604Sjkim
54220604Sjkim/* Local prototypes */
55220604Sjkim
56220604Sjkimstatic UINT32
57220604SjkimAcpiEvGlobalLockHandler (
58220604Sjkim    void                    *Context);
59220604Sjkim
60220604Sjkim
61220604Sjkim/*******************************************************************************
62220604Sjkim *
63220604Sjkim * FUNCTION:    AcpiEvInitGlobalLockHandler
64220604Sjkim *
65220604Sjkim * PARAMETERS:  None
66220604Sjkim *
67220604Sjkim * RETURN:      Status
68220604Sjkim *
69220604Sjkim * DESCRIPTION: Install a handler for the global lock release event
70220604Sjkim *
71220604Sjkim ******************************************************************************/
72220604Sjkim
73220604SjkimACPI_STATUS
74220604SjkimAcpiEvInitGlobalLockHandler (
75220604Sjkim    void)
76220604Sjkim{
77220604Sjkim    ACPI_STATUS             Status;
78220604Sjkim
79220604Sjkim
80220604Sjkim    ACPI_FUNCTION_TRACE (EvInitGlobalLockHandler);
81220604Sjkim
82220604Sjkim
83228110Sjkim    /* If Hardware Reduced flag is set, there is no global lock */
84228110Sjkim
85228110Sjkim    if (AcpiGbl_ReducedHardware)
86228110Sjkim    {
87228110Sjkim        return_ACPI_STATUS (AE_OK);
88228110Sjkim    }
89228110Sjkim
90220604Sjkim    /* Attempt installation of the global lock handler */
91220604Sjkim
92220604Sjkim    Status = AcpiInstallFixedEventHandler (ACPI_EVENT_GLOBAL,
93220604Sjkim                AcpiEvGlobalLockHandler, NULL);
94220604Sjkim
95220604Sjkim    /*
96220604Sjkim     * If the global lock does not exist on this platform, the attempt to
97220604Sjkim     * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick).
98220604Sjkim     * Map to AE_OK, but mark global lock as not present. Any attempt to
99220604Sjkim     * actually use the global lock will be flagged with an error.
100220604Sjkim     */
101220604Sjkim    AcpiGbl_GlobalLockPresent = FALSE;
102220604Sjkim    if (Status == AE_NO_HARDWARE_RESPONSE)
103220604Sjkim    {
104220604Sjkim        ACPI_ERROR ((AE_INFO,
105220604Sjkim            "No response from Global Lock hardware, disabling lock"));
106220604Sjkim
107220604Sjkim        return_ACPI_STATUS (AE_OK);
108220604Sjkim    }
109220604Sjkim
110220604Sjkim    Status = AcpiOsCreateLock (&AcpiGbl_GlobalLockPendingLock);
111220604Sjkim    if (ACPI_FAILURE (Status))
112220604Sjkim    {
113220604Sjkim        return_ACPI_STATUS (Status);
114220604Sjkim    }
115220604Sjkim
116220604Sjkim    AcpiGbl_GlobalLockPending = FALSE;
117220604Sjkim    AcpiGbl_GlobalLockPresent = TRUE;
118220604Sjkim    return_ACPI_STATUS (Status);
119220604Sjkim}
120220604Sjkim
121220604Sjkim
122220604Sjkim/*******************************************************************************
123220604Sjkim *
124220604Sjkim * FUNCTION:    AcpiEvRemoveGlobalLockHandler
125220604Sjkim *
126220604Sjkim * PARAMETERS:  None
127220604Sjkim *
128220604Sjkim * RETURN:      Status
129220604Sjkim *
130220604Sjkim * DESCRIPTION: Remove the handler for the Global Lock
131220604Sjkim *
132220604Sjkim ******************************************************************************/
133220604Sjkim
134220604SjkimACPI_STATUS
135220604SjkimAcpiEvRemoveGlobalLockHandler (
136220604Sjkim    void)
137220604Sjkim{
138220604Sjkim    ACPI_STATUS             Status;
139220604Sjkim
140220604Sjkim
141220604Sjkim    ACPI_FUNCTION_TRACE (EvRemoveGlobalLockHandler);
142220604Sjkim
143220604Sjkim    AcpiGbl_GlobalLockPresent = FALSE;
144220604Sjkim    Status = AcpiRemoveFixedEventHandler (ACPI_EVENT_GLOBAL,
145220604Sjkim                AcpiEvGlobalLockHandler);
146220604Sjkim
147250838Sjkim    AcpiOsDeleteLock (AcpiGbl_GlobalLockPendingLock);
148220604Sjkim    return_ACPI_STATUS (Status);
149220604Sjkim}
150220604Sjkim
151220604Sjkim
152220604Sjkim/*******************************************************************************
153220604Sjkim *
154220604Sjkim * FUNCTION:    AcpiEvGlobalLockHandler
155220604Sjkim *
156220604Sjkim * PARAMETERS:  Context         - From thread interface, not used
157220604Sjkim *
158220604Sjkim * RETURN:      ACPI_INTERRUPT_HANDLED
159220604Sjkim *
160220604Sjkim * DESCRIPTION: Invoked directly from the SCI handler when a global lock
161220604Sjkim *              release interrupt occurs. If there is actually a pending
162220604Sjkim *              request for the lock, signal the waiting thread.
163220604Sjkim *
164220604Sjkim ******************************************************************************/
165220604Sjkim
166220604Sjkimstatic UINT32
167220604SjkimAcpiEvGlobalLockHandler (
168220604Sjkim    void                    *Context)
169220604Sjkim{
170220604Sjkim    ACPI_STATUS             Status;
171220604Sjkim    ACPI_CPU_FLAGS          Flags;
172220604Sjkim
173220604Sjkim
174220604Sjkim    Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
175220604Sjkim
176220604Sjkim    /*
177220604Sjkim     * If a request for the global lock is not actually pending,
178220604Sjkim     * we are done. This handles "spurious" global lock interrupts
179220604Sjkim     * which are possible (and have been seen) with bad BIOSs.
180220604Sjkim     */
181220604Sjkim    if (!AcpiGbl_GlobalLockPending)
182220604Sjkim    {
183220604Sjkim        goto CleanupAndExit;
184220604Sjkim    }
185220604Sjkim
186220604Sjkim    /*
187220604Sjkim     * Send a unit to the global lock semaphore. The actual acquisition
188220604Sjkim     * of the global lock will be performed by the waiting thread.
189220604Sjkim     */
190220604Sjkim    Status = AcpiOsSignalSemaphore (AcpiGbl_GlobalLockSemaphore, 1);
191220604Sjkim    if (ACPI_FAILURE (Status))
192220604Sjkim    {
193220604Sjkim        ACPI_ERROR ((AE_INFO, "Could not signal Global Lock semaphore"));
194220604Sjkim    }
195220604Sjkim
196220604Sjkim    AcpiGbl_GlobalLockPending = FALSE;
197220604Sjkim
198220604Sjkim
199220604SjkimCleanupAndExit:
200220604Sjkim
201220604Sjkim    AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags);
202220604Sjkim    return (ACPI_INTERRUPT_HANDLED);
203220604Sjkim}
204220604Sjkim
205220604Sjkim
206220604Sjkim/******************************************************************************
207220604Sjkim *
208220604Sjkim * FUNCTION:    AcpiEvAcquireGlobalLock
209220604Sjkim *
210220604Sjkim * PARAMETERS:  Timeout         - Max time to wait for the lock, in millisec.
211220604Sjkim *
212220604Sjkim * RETURN:      Status
213220604Sjkim *
214220604Sjkim * DESCRIPTION: Attempt to gain ownership of the Global Lock.
215220604Sjkim *
216220604Sjkim * MUTEX:       Interpreter must be locked
217220604Sjkim *
218220604Sjkim * Note: The original implementation allowed multiple threads to "acquire" the
219220604Sjkim * Global Lock, and the OS would hold the lock until the last thread had
220220604Sjkim * released it. However, this could potentially starve the BIOS out of the
221220604Sjkim * lock, especially in the case where there is a tight handshake between the
222220604Sjkim * Embedded Controller driver and the BIOS. Therefore, this implementation
223220604Sjkim * allows only one thread to acquire the HW Global Lock at a time, and makes
224220604Sjkim * the global lock appear as a standard mutex on the OS side.
225220604Sjkim *
226220604Sjkim *****************************************************************************/
227220604Sjkim
228220604SjkimACPI_STATUS
229220604SjkimAcpiEvAcquireGlobalLock (
230220604Sjkim    UINT16                  Timeout)
231220604Sjkim{
232220604Sjkim    ACPI_CPU_FLAGS          Flags;
233220604Sjkim    ACPI_STATUS             Status;
234220604Sjkim    BOOLEAN                 Acquired = FALSE;
235220604Sjkim
236220604Sjkim
237220604Sjkim    ACPI_FUNCTION_TRACE (EvAcquireGlobalLock);
238220604Sjkim
239220604Sjkim
240220604Sjkim    /*
241220604Sjkim     * Only one thread can acquire the GL at a time, the GlobalLockMutex
242220604Sjkim     * enforces this. This interface releases the interpreter if we must wait.
243220604Sjkim     */
244220604Sjkim    Status = AcpiExSystemWaitMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex,
245220604Sjkim                Timeout);
246220604Sjkim    if (ACPI_FAILURE (Status))
247220604Sjkim    {
248220604Sjkim        return_ACPI_STATUS (Status);
249220604Sjkim    }
250220604Sjkim
251220604Sjkim    /*
252220604Sjkim     * Update the global lock handle and check for wraparound. The handle is
253220604Sjkim     * only used for the external global lock interfaces, but it is updated
254220604Sjkim     * here to properly handle the case where a single thread may acquire the
255220604Sjkim     * lock via both the AML and the AcpiAcquireGlobalLock interfaces. The
256220604Sjkim     * handle is therefore updated on the first acquire from a given thread
257220604Sjkim     * regardless of where the acquisition request originated.
258220604Sjkim     */
259220604Sjkim    AcpiGbl_GlobalLockHandle++;
260220604Sjkim    if (AcpiGbl_GlobalLockHandle == 0)
261220604Sjkim    {
262220604Sjkim        AcpiGbl_GlobalLockHandle = 1;
263220604Sjkim    }
264220604Sjkim
265220604Sjkim    /*
266220604Sjkim     * Make sure that a global lock actually exists. If not, just
267220604Sjkim     * treat the lock as a standard mutex.
268220604Sjkim     */
269220604Sjkim    if (!AcpiGbl_GlobalLockPresent)
270220604Sjkim    {
271220604Sjkim        AcpiGbl_GlobalLockAcquired = TRUE;
272220604Sjkim        return_ACPI_STATUS (AE_OK);
273220604Sjkim    }
274220604Sjkim
275220604Sjkim    Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
276220604Sjkim
277220604Sjkim    do
278220604Sjkim    {
279220604Sjkim        /* Attempt to acquire the actual hardware lock */
280220604Sjkim
281220604Sjkim        ACPI_ACQUIRE_GLOBAL_LOCK (AcpiGbl_FACS, Acquired);
282220604Sjkim        if (Acquired)
283220604Sjkim        {
284220604Sjkim            AcpiGbl_GlobalLockAcquired = TRUE;
285220604Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
286220604Sjkim                "Acquired hardware Global Lock\n"));
287220604Sjkim            break;
288220604Sjkim        }
289220604Sjkim
290220604Sjkim        /*
291220604Sjkim         * Did not get the lock. The pending bit was set above, and
292220604Sjkim         * we must now wait until we receive the global lock
293220604Sjkim         * released interrupt.
294220604Sjkim         */
295220604Sjkim        AcpiGbl_GlobalLockPending = TRUE;
296220604Sjkim        AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags);
297220604Sjkim
298220604Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
299220604Sjkim            "Waiting for hardware Global Lock\n"));
300220604Sjkim
301220604Sjkim        /*
302220604Sjkim         * Wait for handshake with the global lock interrupt handler.
303220604Sjkim         * This interface releases the interpreter if we must wait.
304220604Sjkim         */
305220604Sjkim        Status = AcpiExSystemWaitSemaphore (AcpiGbl_GlobalLockSemaphore,
306220604Sjkim                    ACPI_WAIT_FOREVER);
307220604Sjkim
308220604Sjkim        Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
309220604Sjkim
310220604Sjkim    } while (ACPI_SUCCESS (Status));
311220604Sjkim
312220604Sjkim    AcpiGbl_GlobalLockPending = FALSE;
313220604Sjkim    AcpiOsReleaseLock (AcpiGbl_GlobalLockPendingLock, Flags);
314220604Sjkim
315220604Sjkim    return_ACPI_STATUS (Status);
316220604Sjkim}
317220604Sjkim
318220604Sjkim
319220604Sjkim/*******************************************************************************
320220604Sjkim *
321220604Sjkim * FUNCTION:    AcpiEvReleaseGlobalLock
322220604Sjkim *
323220604Sjkim * PARAMETERS:  None
324220604Sjkim *
325220604Sjkim * RETURN:      Status
326220604Sjkim *
327220604Sjkim * DESCRIPTION: Releases ownership of the Global Lock.
328220604Sjkim *
329220604Sjkim ******************************************************************************/
330220604Sjkim
331220604SjkimACPI_STATUS
332220604SjkimAcpiEvReleaseGlobalLock (
333220604Sjkim    void)
334220604Sjkim{
335220604Sjkim    BOOLEAN                 Pending = FALSE;
336220604Sjkim    ACPI_STATUS             Status = AE_OK;
337220604Sjkim
338220604Sjkim
339220604Sjkim    ACPI_FUNCTION_TRACE (EvReleaseGlobalLock);
340220604Sjkim
341220604Sjkim
342220604Sjkim    /* Lock must be already acquired */
343220604Sjkim
344220604Sjkim    if (!AcpiGbl_GlobalLockAcquired)
345220604Sjkim    {
346220604Sjkim        ACPI_WARNING ((AE_INFO,
347220604Sjkim            "Cannot release the ACPI Global Lock, it has not been acquired"));
348220604Sjkim        return_ACPI_STATUS (AE_NOT_ACQUIRED);
349220604Sjkim    }
350220604Sjkim
351220604Sjkim    if (AcpiGbl_GlobalLockPresent)
352220604Sjkim    {
353220604Sjkim        /* Allow any thread to release the lock */
354220604Sjkim
355220604Sjkim        ACPI_RELEASE_GLOBAL_LOCK (AcpiGbl_FACS, Pending);
356220604Sjkim
357220604Sjkim        /*
358220604Sjkim         * If the pending bit was set, we must write GBL_RLS to the control
359220604Sjkim         * register
360220604Sjkim         */
361220604Sjkim        if (Pending)
362220604Sjkim        {
363220604Sjkim            Status = AcpiWriteBitRegister (
364220604Sjkim                        ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT);
365220604Sjkim        }
366220604Sjkim
367220604Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Released hardware Global Lock\n"));
368220604Sjkim    }
369220604Sjkim
370220604Sjkim    AcpiGbl_GlobalLockAcquired = FALSE;
371220604Sjkim
372220604Sjkim    /* Release the local GL mutex */
373220604Sjkim
374220604Sjkim    AcpiOsReleaseMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex);
375220604Sjkim    return_ACPI_STATUS (Status);
376220604Sjkim}
377231844Sjkim
378231844Sjkim#endif /* !ACPI_REDUCED_HARDWARE */
379