xref: /titanic_44/usr/src/uts/intel/io/acpica/events/evglock.c (revision 5fd03bc0f2e00e7ba02316c2e08f45d52aab15db)
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 }
368