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