File Index
Files and directories
Sign in
FILE

evglock.c

File preview with syntax highlighting.

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