1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /******************************************************************************
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Module Name: evglock - Global Lock support
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2000 - 2020, Intel Corp.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun *****************************************************************************/
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <acpi/acpi.h>
11*4882a593Smuzhiyun #include "accommon.h"
12*4882a593Smuzhiyun #include "acevents.h"
13*4882a593Smuzhiyun #include "acinterp.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #define _COMPONENT ACPI_EVENTS
16*4882a593Smuzhiyun ACPI_MODULE_NAME("evglock")
17*4882a593Smuzhiyun #if (!ACPI_REDUCED_HARDWARE) /* Entire module */
18*4882a593Smuzhiyun /* Local prototypes */
19*4882a593Smuzhiyun static u32 acpi_ev_global_lock_handler(void *context);
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /*******************************************************************************
22*4882a593Smuzhiyun *
23*4882a593Smuzhiyun * FUNCTION: acpi_ev_init_global_lock_handler
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * PARAMETERS: None
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * RETURN: Status
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * DESCRIPTION: Install a handler for the global lock release event
30*4882a593Smuzhiyun *
31*4882a593Smuzhiyun ******************************************************************************/
32*4882a593Smuzhiyun
acpi_ev_init_global_lock_handler(void)33*4882a593Smuzhiyun acpi_status acpi_ev_init_global_lock_handler(void)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun acpi_status status;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun ACPI_FUNCTION_TRACE(ev_init_global_lock_handler);
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* If Hardware Reduced flag is set, there is no global lock */
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun if (acpi_gbl_reduced_hardware) {
42*4882a593Smuzhiyun return_ACPI_STATUS(AE_OK);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* Attempt installation of the global lock handler */
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL,
48*4882a593Smuzhiyun acpi_ev_global_lock_handler,
49*4882a593Smuzhiyun NULL);
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /*
52*4882a593Smuzhiyun * If the global lock does not exist on this platform, the attempt to
53*4882a593Smuzhiyun * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick).
54*4882a593Smuzhiyun * Map to AE_OK, but mark global lock as not present. Any attempt to
55*4882a593Smuzhiyun * actually use the global lock will be flagged with an error.
56*4882a593Smuzhiyun */
57*4882a593Smuzhiyun acpi_gbl_global_lock_present = FALSE;
58*4882a593Smuzhiyun if (status == AE_NO_HARDWARE_RESPONSE) {
59*4882a593Smuzhiyun ACPI_ERROR((AE_INFO,
60*4882a593Smuzhiyun "No response from Global Lock hardware, disabling lock"));
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun return_ACPI_STATUS(AE_OK);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock);
66*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
67*4882a593Smuzhiyun return_ACPI_STATUS(status);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun acpi_gbl_global_lock_pending = FALSE;
71*4882a593Smuzhiyun acpi_gbl_global_lock_present = TRUE;
72*4882a593Smuzhiyun return_ACPI_STATUS(status);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /*******************************************************************************
76*4882a593Smuzhiyun *
77*4882a593Smuzhiyun * FUNCTION: acpi_ev_remove_global_lock_handler
78*4882a593Smuzhiyun *
79*4882a593Smuzhiyun * PARAMETERS: None
80*4882a593Smuzhiyun *
81*4882a593Smuzhiyun * RETURN: Status
82*4882a593Smuzhiyun *
83*4882a593Smuzhiyun * DESCRIPTION: Remove the handler for the Global Lock
84*4882a593Smuzhiyun *
85*4882a593Smuzhiyun ******************************************************************************/
86*4882a593Smuzhiyun
acpi_ev_remove_global_lock_handler(void)87*4882a593Smuzhiyun acpi_status acpi_ev_remove_global_lock_handler(void)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun acpi_status status;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler);
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun acpi_gbl_global_lock_present = FALSE;
94*4882a593Smuzhiyun status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL,
95*4882a593Smuzhiyun acpi_ev_global_lock_handler);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock);
98*4882a593Smuzhiyun return_ACPI_STATUS(status);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /*******************************************************************************
102*4882a593Smuzhiyun *
103*4882a593Smuzhiyun * FUNCTION: acpi_ev_global_lock_handler
104*4882a593Smuzhiyun *
105*4882a593Smuzhiyun * PARAMETERS: context - From thread interface, not used
106*4882a593Smuzhiyun *
107*4882a593Smuzhiyun * RETURN: ACPI_INTERRUPT_HANDLED
108*4882a593Smuzhiyun *
109*4882a593Smuzhiyun * DESCRIPTION: Invoked directly from the SCI handler when a global lock
110*4882a593Smuzhiyun * release interrupt occurs. If there is actually a pending
111*4882a593Smuzhiyun * request for the lock, signal the waiting thread.
112*4882a593Smuzhiyun *
113*4882a593Smuzhiyun ******************************************************************************/
114*4882a593Smuzhiyun
acpi_ev_global_lock_handler(void * context)115*4882a593Smuzhiyun static u32 acpi_ev_global_lock_handler(void *context)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun acpi_status status;
118*4882a593Smuzhiyun acpi_cpu_flags flags;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /*
123*4882a593Smuzhiyun * If a request for the global lock is not actually pending,
124*4882a593Smuzhiyun * we are done. This handles "spurious" global lock interrupts
125*4882a593Smuzhiyun * which are possible (and have been seen) with bad BIOSs.
126*4882a593Smuzhiyun */
127*4882a593Smuzhiyun if (!acpi_gbl_global_lock_pending) {
128*4882a593Smuzhiyun goto cleanup_and_exit;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /*
132*4882a593Smuzhiyun * Send a unit to the global lock semaphore. The actual acquisition
133*4882a593Smuzhiyun * of the global lock will be performed by the waiting thread.
134*4882a593Smuzhiyun */
135*4882a593Smuzhiyun status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1);
136*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
137*4882a593Smuzhiyun ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore"));
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun acpi_gbl_global_lock_pending = FALSE;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun cleanup_and_exit:
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
145*4882a593Smuzhiyun return (ACPI_INTERRUPT_HANDLED);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun /******************************************************************************
149*4882a593Smuzhiyun *
150*4882a593Smuzhiyun * FUNCTION: acpi_ev_acquire_global_lock
151*4882a593Smuzhiyun *
152*4882a593Smuzhiyun * PARAMETERS: timeout - Max time to wait for the lock, in millisec.
153*4882a593Smuzhiyun *
154*4882a593Smuzhiyun * RETURN: Status
155*4882a593Smuzhiyun *
156*4882a593Smuzhiyun * DESCRIPTION: Attempt to gain ownership of the Global Lock.
157*4882a593Smuzhiyun *
158*4882a593Smuzhiyun * MUTEX: Interpreter must be locked
159*4882a593Smuzhiyun *
160*4882a593Smuzhiyun * Note: The original implementation allowed multiple threads to "acquire" the
161*4882a593Smuzhiyun * Global Lock, and the OS would hold the lock until the last thread had
162*4882a593Smuzhiyun * released it. However, this could potentially starve the BIOS out of the
163*4882a593Smuzhiyun * lock, especially in the case where there is a tight handshake between the
164*4882a593Smuzhiyun * Embedded Controller driver and the BIOS. Therefore, this implementation
165*4882a593Smuzhiyun * allows only one thread to acquire the HW Global Lock at a time, and makes
166*4882a593Smuzhiyun * the global lock appear as a standard mutex on the OS side.
167*4882a593Smuzhiyun *
168*4882a593Smuzhiyun *****************************************************************************/
169*4882a593Smuzhiyun
acpi_ev_acquire_global_lock(u16 timeout)170*4882a593Smuzhiyun acpi_status acpi_ev_acquire_global_lock(u16 timeout)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun acpi_cpu_flags flags;
173*4882a593Smuzhiyun acpi_status status;
174*4882a593Smuzhiyun u8 acquired = FALSE;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun ACPI_FUNCTION_TRACE(ev_acquire_global_lock);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /*
179*4882a593Smuzhiyun * Only one thread can acquire the GL at a time, the global_lock_mutex
180*4882a593Smuzhiyun * enforces this. This interface releases the interpreter if we must wait.
181*4882a593Smuzhiyun */
182*4882a593Smuzhiyun status =
183*4882a593Smuzhiyun acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex.
184*4882a593Smuzhiyun os_mutex, timeout);
185*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
186*4882a593Smuzhiyun return_ACPI_STATUS(status);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /*
190*4882a593Smuzhiyun * Update the global lock handle and check for wraparound. The handle is
191*4882a593Smuzhiyun * only used for the external global lock interfaces, but it is updated
192*4882a593Smuzhiyun * here to properly handle the case where a single thread may acquire the
193*4882a593Smuzhiyun * lock via both the AML and the acpi_acquire_global_lock interfaces. The
194*4882a593Smuzhiyun * handle is therefore updated on the first acquire from a given thread
195*4882a593Smuzhiyun * regardless of where the acquisition request originated.
196*4882a593Smuzhiyun */
197*4882a593Smuzhiyun acpi_gbl_global_lock_handle++;
198*4882a593Smuzhiyun if (acpi_gbl_global_lock_handle == 0) {
199*4882a593Smuzhiyun acpi_gbl_global_lock_handle = 1;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun /*
203*4882a593Smuzhiyun * Make sure that a global lock actually exists. If not, just
204*4882a593Smuzhiyun * treat the lock as a standard mutex.
205*4882a593Smuzhiyun */
206*4882a593Smuzhiyun if (!acpi_gbl_global_lock_present) {
207*4882a593Smuzhiyun acpi_gbl_global_lock_acquired = TRUE;
208*4882a593Smuzhiyun return_ACPI_STATUS(AE_OK);
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun do {
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* Attempt to acquire the actual hardware lock */
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired);
218*4882a593Smuzhiyun if (acquired) {
219*4882a593Smuzhiyun acpi_gbl_global_lock_acquired = TRUE;
220*4882a593Smuzhiyun ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
221*4882a593Smuzhiyun "Acquired hardware Global Lock\n"));
222*4882a593Smuzhiyun break;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /*
226*4882a593Smuzhiyun * Did not get the lock. The pending bit was set above, and
227*4882a593Smuzhiyun * we must now wait until we receive the global lock
228*4882a593Smuzhiyun * released interrupt.
229*4882a593Smuzhiyun */
230*4882a593Smuzhiyun acpi_gbl_global_lock_pending = TRUE;
231*4882a593Smuzhiyun acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
234*4882a593Smuzhiyun "Waiting for hardware Global Lock\n"));
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun /*
237*4882a593Smuzhiyun * Wait for handshake with the global lock interrupt handler.
238*4882a593Smuzhiyun * This interface releases the interpreter if we must wait.
239*4882a593Smuzhiyun */
240*4882a593Smuzhiyun status =
241*4882a593Smuzhiyun acpi_ex_system_wait_semaphore
242*4882a593Smuzhiyun (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun } while (ACPI_SUCCESS(status));
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun acpi_gbl_global_lock_pending = FALSE;
249*4882a593Smuzhiyun acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun return_ACPI_STATUS(status);
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /*******************************************************************************
255*4882a593Smuzhiyun *
256*4882a593Smuzhiyun * FUNCTION: acpi_ev_release_global_lock
257*4882a593Smuzhiyun *
258*4882a593Smuzhiyun * PARAMETERS: None
259*4882a593Smuzhiyun *
260*4882a593Smuzhiyun * RETURN: Status
261*4882a593Smuzhiyun *
262*4882a593Smuzhiyun * DESCRIPTION: Releases ownership of the Global Lock.
263*4882a593Smuzhiyun *
264*4882a593Smuzhiyun ******************************************************************************/
265*4882a593Smuzhiyun
acpi_ev_release_global_lock(void)266*4882a593Smuzhiyun acpi_status acpi_ev_release_global_lock(void)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun u8 pending = FALSE;
269*4882a593Smuzhiyun acpi_status status = AE_OK;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun ACPI_FUNCTION_TRACE(ev_release_global_lock);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /* Lock must be already acquired */
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun if (!acpi_gbl_global_lock_acquired) {
276*4882a593Smuzhiyun ACPI_WARNING((AE_INFO,
277*4882a593Smuzhiyun "Cannot release the ACPI Global Lock, it has not been acquired"));
278*4882a593Smuzhiyun return_ACPI_STATUS(AE_NOT_ACQUIRED);
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun if (acpi_gbl_global_lock_present) {
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun /* Allow any thread to release the lock */
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending);
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun /*
288*4882a593Smuzhiyun * If the pending bit was set, we must write GBL_RLS to the control
289*4882a593Smuzhiyun * register
290*4882a593Smuzhiyun */
291*4882a593Smuzhiyun if (pending) {
292*4882a593Smuzhiyun status =
293*4882a593Smuzhiyun acpi_write_bit_register
294*4882a593Smuzhiyun (ACPI_BITREG_GLOBAL_LOCK_RELEASE,
295*4882a593Smuzhiyun ACPI_ENABLE_EVENT);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
299*4882a593Smuzhiyun "Released hardware Global Lock\n"));
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun acpi_gbl_global_lock_acquired = FALSE;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun /* Release the local GL mutex */
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
307*4882a593Smuzhiyun return_ACPI_STATUS(status);
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun #endif /* !ACPI_REDUCED_HARDWARE */
311