OsdSynch.c revision 225736
1177633Sdfr/*-
2177633Sdfr * Copyright (c) 2000 Michael Smith
3177633Sdfr * Copyright (c) 2000 BSDi
4177633Sdfr * Copyright (c) 2007-2009 Jung-uk Kim <jkim@FreeBSD.org>
5177633Sdfr * All rights reserved.
6177633Sdfr *
7177633Sdfr * Redistribution and use in source and binary forms, with or without
8177633Sdfr * modification, are permitted provided that the following conditions
9177633Sdfr * are met:
10177633Sdfr * 1. Redistributions of source code must retain the above copyright
11177633Sdfr *    notice, this list of conditions and the following disclaimer.
12177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright
13177633Sdfr *    notice, this list of conditions and the following disclaimer in the
14177633Sdfr *    documentation and/or other materials provided with the distribution.
15177633Sdfr *
16177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17177633Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18177633Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19177633Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20177633Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21177633Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22177633Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23177633Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24177633Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25177633Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26177633Sdfr * SUCH DAMAGE.
27177633Sdfr */
28177633Sdfr
29177633Sdfr/*
30177633Sdfr * 6.1 : Mutual Exclusion and Synchronisation
31177633Sdfr */
32177633Sdfr
33177633Sdfr#include <sys/cdefs.h>
34177633Sdfr__FBSDID("$FreeBSD: stable/9/sys/dev/acpica/Osd/OsdSynch.c 194639 2009-06-22 17:46:55Z jkim $");
35177633Sdfr
36177633Sdfr#include <contrib/dev/acpica/include/acpi.h>
37177633Sdfr#include <contrib/dev/acpica/include/accommon.h>
38177633Sdfr
39177633Sdfr#include <sys/condvar.h>
40177633Sdfr#include <sys/kernel.h>
41177633Sdfr#include <sys/lock.h>
42177633Sdfr#include <sys/malloc.h>
43177633Sdfr#include <sys/mutex.h>
44177633Sdfr
45177633Sdfr#define	_COMPONENT	ACPI_OS_SERVICES
46177633SdfrACPI_MODULE_NAME("SYNCH")
47177633Sdfr
48177633SdfrMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
49177633Sdfr
50177633Sdfr/*
51177633Sdfr * Convert milliseconds to ticks.
52177633Sdfr */
53180025Sdfrstatic int
54180025Sdfrtimeout2hz(UINT16 Timeout)
55177633Sdfr{
56177633Sdfr	struct timeval		tv;
57180064Sdfr
58180743Sdfr	tv.tv_sec = (time_t)(Timeout / 1000);
59180025Sdfr	tv.tv_usec = (suseconds_t)(Timeout % 1000) * 1000;
60177633Sdfr
61177633Sdfr	return (tvtohz(&tv));
62177633Sdfr}
63177633Sdfr
64177633Sdfr/*
65177633Sdfr * ACPI_SEMAPHORE
66177685Sdfr */
67177633Sdfrstruct acpi_sema {
68177633Sdfr	struct mtx	as_lock;
69177633Sdfr	char		as_name[32];
70177633Sdfr	struct cv	as_cv;
71177633Sdfr	UINT32		as_maxunits;
72177633Sdfr	UINT32		as_units;
73177633Sdfr	int		as_waiters;
74177633Sdfr	int		as_reset;
75177633Sdfr};
76177633Sdfr
77177633SdfrACPI_STATUS
78177633SdfrAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits,
79177633Sdfr    ACPI_SEMAPHORE *OutHandle)
80177633Sdfr{
81177633Sdfr	struct acpi_sema	*as;
82177633Sdfr
83177633Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
84177633Sdfr
85177633Sdfr	if (OutHandle == NULL || MaxUnits == 0 || InitialUnits > MaxUnits)
86177633Sdfr		return_ACPI_STATUS (AE_BAD_PARAMETER);
87177633Sdfr
88180025Sdfr	if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
89180025Sdfr		return_ACPI_STATUS (AE_NO_MEMORY);
90180743Sdfr
91180025Sdfr	snprintf(as->as_name, sizeof(as->as_name), "ACPI sema (%p)", as);
92177633Sdfr	mtx_init(&as->as_lock, as->as_name, NULL, MTX_DEF);
93177633Sdfr	cv_init(&as->as_cv, as->as_name);
94177633Sdfr	as->as_maxunits = MaxUnits;
95177633Sdfr	as->as_units = InitialUnits;
96177633Sdfr
97180025Sdfr	*OutHandle = (ACPI_SEMAPHORE)as;
98177633Sdfr
99180025Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s, max %u, initial %u\n",
100177633Sdfr	    as->as_name, MaxUnits, InitialUnits));
101177633Sdfr
102180025Sdfr	return_ACPI_STATUS (AE_OK);
103180025Sdfr}
104180025Sdfr
105180025SdfrACPI_STATUS
106180025SdfrAcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle)
107180025Sdfr{
108180025Sdfr	struct acpi_sema	*as = (struct acpi_sema *)Handle;
109180025Sdfr
110180025Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
111180025Sdfr
112180025Sdfr	if (as == NULL)
113180025Sdfr		return_ACPI_STATUS (AE_BAD_PARAMETER);
114180025Sdfr
115180025Sdfr	mtx_lock(&as->as_lock);
116180025Sdfr
117180025Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", as->as_name));
118180025Sdfr
119180025Sdfr	if (as->as_waiters > 0) {
120180025Sdfr		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
121177633Sdfr		    "reset %s, units %u, waiters %d\n",
122177633Sdfr		    as->as_name, as->as_units, as->as_waiters));
123177633Sdfr		as->as_reset = 1;
124177633Sdfr		cv_broadcast(&as->as_cv);
125177633Sdfr		while (as->as_waiters > 0) {
126177633Sdfr			if (mtx_sleep(&as->as_reset, &as->as_lock,
127177633Sdfr			    PCATCH, "acsrst", hz) == EINTR) {
128180025Sdfr				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
129177633Sdfr				    "failed to reset %s, waiters %d\n",
130177633Sdfr				    as->as_name, as->as_waiters));
131177633Sdfr				mtx_unlock(&as->as_lock);
132177633Sdfr				return_ACPI_STATUS (AE_ERROR);
133180025Sdfr			}
134177633Sdfr			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
135177633Sdfr			    "wait %s, units %u, waiters %d\n",
136177633Sdfr			    as->as_name, as->as_units, as->as_waiters));
137177633Sdfr		}
138180025Sdfr	}
139180025Sdfr
140180025Sdfr	mtx_unlock(&as->as_lock);
141180025Sdfr
142180025Sdfr	mtx_destroy(&as->as_lock);
143180025Sdfr	cv_destroy(&as->as_cv);
144180025Sdfr	free(as, M_ACPISEM);
145180025Sdfr
146180025Sdfr	return_ACPI_STATUS (AE_OK);
147180025Sdfr}
148180025Sdfr
149180025Sdfr#define	ACPISEM_AVAIL(s, u)	((s)->as_units >= (u))
150180025Sdfr
151180025SdfrACPI_STATUS
152177633SdfrAcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout)
153180025Sdfr{
154180025Sdfr	struct acpi_sema	*as = (struct acpi_sema *)Handle;
155180025Sdfr	int			error, prevtick, slptick, tmo;
156180025Sdfr	ACPI_STATUS		status = AE_OK;
157180025Sdfr
158180025Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
159180025Sdfr
160180025Sdfr	if (as == NULL || Units == 0)
161180743Sdfr		return_ACPI_STATUS (AE_BAD_PARAMETER);
162180025Sdfr
163180025Sdfr	mtx_lock(&as->as_lock);
164180025Sdfr
165180025Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
166180025Sdfr	    "get %u unit(s) from %s, units %u, waiters %d, timeout %u\n",
167180025Sdfr	    Units, as->as_name, as->as_units, as->as_waiters, Timeout));
168180025Sdfr
169180025Sdfr	if (as->as_maxunits != ACPI_NO_UNIT_LIMIT && as->as_maxunits < Units) {
170180025Sdfr		mtx_unlock(&as->as_lock);
171180025Sdfr		return_ACPI_STATUS (AE_LIMIT);
172180025Sdfr	}
173180743Sdfr
174180025Sdfr	switch (Timeout) {
175180025Sdfr	case ACPI_DO_NOT_WAIT:
176180025Sdfr		if (!ACPISEM_AVAIL(as, Units))
177180743Sdfr			status = AE_TIME;
178180743Sdfr		break;
179180025Sdfr	case ACPI_WAIT_FOREVER:
180177633Sdfr		while (!ACPISEM_AVAIL(as, Units)) {
181177633Sdfr			as->as_waiters++;
182177633Sdfr			error = cv_wait_sig(&as->as_cv, &as->as_lock);
183177633Sdfr			as->as_waiters--;
184177633Sdfr			if (error == EINTR || as->as_reset) {
185177633Sdfr				status = AE_ERROR;
186177633Sdfr				break;
187177633Sdfr			}
188180743Sdfr		}
189180025Sdfr		break;
190177633Sdfr	default:
191177633Sdfr		tmo = timeout2hz(Timeout);
192180025Sdfr		while (!ACPISEM_AVAIL(as, Units)) {
193177633Sdfr			prevtick = ticks;
194177633Sdfr			as->as_waiters++;
195177633Sdfr			error = cv_timedwait_sig(&as->as_cv, &as->as_lock, tmo);
196177633Sdfr			as->as_waiters--;
197177633Sdfr			if (error == EINTR || as->as_reset) {
198177633Sdfr				status = AE_ERROR;
199177633Sdfr				break;
200177633Sdfr			}
201177633Sdfr			if (ACPISEM_AVAIL(as, Units))
202177633Sdfr				break;
203177633Sdfr			slptick = ticks - prevtick;
204177633Sdfr			if (slptick >= tmo || slptick < 0) {
205177633Sdfr				status = AE_TIME;
206177633Sdfr				break;
207177633Sdfr			}
208177633Sdfr			tmo -= slptick;
209177633Sdfr		}
210177633Sdfr	}
211177633Sdfr	if (status == AE_OK)
212177633Sdfr		as->as_units -= Units;
213177633Sdfr
214180025Sdfr	mtx_unlock(&as->as_lock);
215180743Sdfr
216180743Sdfr	return_ACPI_STATUS (status);
217180743Sdfr}
218180743Sdfr
219180743SdfrACPI_STATUS
220180743SdfrAcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units)
221180743Sdfr{
222180743Sdfr	struct acpi_sema	*as = (struct acpi_sema *)Handle;
223180743Sdfr	UINT32			i;
224180743Sdfr
225180743Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
226177633Sdfr
227180743Sdfr	if (as == NULL || Units == 0)
228180743Sdfr		return_ACPI_STATUS (AE_BAD_PARAMETER);
229180743Sdfr
230180743Sdfr	mtx_lock(&as->as_lock);
231180743Sdfr
232180743Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
233180743Sdfr	    "return %u units to %s, units %u, waiters %d\n",
234180743Sdfr	    Units, as->as_name, as->as_units, as->as_waiters));
235177633Sdfr
236177633Sdfr	if (as->as_maxunits != ACPI_NO_UNIT_LIMIT &&
237177633Sdfr	    (as->as_maxunits < Units ||
238177633Sdfr	    as->as_maxunits - Units < as->as_units)) {
239177633Sdfr		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
240177633Sdfr		    "exceeded max units %u\n", as->as_maxunits));
241177633Sdfr		mtx_unlock(&as->as_lock);
242177633Sdfr		return_ACPI_STATUS (AE_LIMIT);
243177633Sdfr	}
244177633Sdfr
245177633Sdfr	as->as_units += Units;
246177633Sdfr	if (as->as_waiters > 0 && ACPISEM_AVAIL(as, Units))
247177633Sdfr		for (i = 0; i < Units; i++)
248177633Sdfr			cv_signal(&as->as_cv);
249177633Sdfr
250177633Sdfr	mtx_unlock(&as->as_lock);
251177633Sdfr
252177633Sdfr	return_ACPI_STATUS (AE_OK);
253177633Sdfr}
254177633Sdfr
255177633Sdfr#undef ACPISEM_AVAIL
256177633Sdfr
257177633Sdfr/*
258177633Sdfr * ACPI_MUTEX
259177633Sdfr */
260177633Sdfrstruct acpi_mutex {
261177633Sdfr	struct mtx	am_lock;
262177633Sdfr	char		am_name[32];
263177633Sdfr	struct thread	*am_owner;
264177633Sdfr	int		am_nested;
265177633Sdfr	int		am_waiters;
266177633Sdfr	int		am_reset;
267177633Sdfr};
268177633Sdfr
269177633SdfrACPI_STATUS
270177633SdfrAcpiOsCreateMutex(ACPI_MUTEX *OutHandle)
271177633Sdfr{
272177633Sdfr	struct acpi_mutex	*am;
273177633Sdfr
274177633Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
275177633Sdfr
276177633Sdfr	if (OutHandle == NULL)
277177633Sdfr		return_ACPI_STATUS (AE_BAD_PARAMETER);
278177633Sdfr
279177633Sdfr	if ((am = malloc(sizeof(*am), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
280177633Sdfr		return_ACPI_STATUS (AE_NO_MEMORY);
281177633Sdfr
282177633Sdfr	snprintf(am->am_name, sizeof(am->am_name), "ACPI mutex (%p)", am);
283177633Sdfr	mtx_init(&am->am_lock, am->am_name, NULL, MTX_DEF);
284177633Sdfr
285177633Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", am->am_name));
286177633Sdfr
287177633Sdfr	*OutHandle = (ACPI_MUTEX)am;
288177633Sdfr
289177633Sdfr	return_ACPI_STATUS (AE_OK);
290177633Sdfr}
291177633Sdfr
292177633Sdfr#define	ACPIMTX_AVAIL(m)	((m)->am_owner == NULL)
293177633Sdfr#define	ACPIMTX_OWNED(m)	((m)->am_owner == curthread)
294177633Sdfr
295177633Sdfrvoid
296177633SdfrAcpiOsDeleteMutex(ACPI_MUTEX Handle)
297177633Sdfr{
298177633Sdfr	struct acpi_mutex	*am = (struct acpi_mutex *)Handle;
299177633Sdfr
300177633Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
301177633Sdfr
302177633Sdfr	if (am == NULL) {
303177633Sdfr		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "cannot delete null mutex\n"));
304177633Sdfr		return_VOID;
305177633Sdfr	}
306177633Sdfr
307177633Sdfr	mtx_lock(&am->am_lock);
308177633Sdfr
309177633Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", am->am_name));
310177633Sdfr
311177633Sdfr	if (am->am_waiters > 0) {
312177633Sdfr		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
313177633Sdfr		    "reset %s, owner %p\n", am->am_name, am->am_owner));
314177633Sdfr		am->am_reset = 1;
315177633Sdfr		wakeup(am);
316177633Sdfr		while (am->am_waiters > 0) {
317177633Sdfr			if (mtx_sleep(&am->am_reset, &am->am_lock,
318177633Sdfr			    PCATCH, "acmrst", hz) == EINTR) {
319177633Sdfr				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
320177633Sdfr				    "failed to reset %s, waiters %d\n",
321177633Sdfr				    am->am_name, am->am_waiters));
322177633Sdfr				mtx_unlock(&am->am_lock);
323177633Sdfr				return_VOID;
324177633Sdfr			}
325177633Sdfr			if (ACPIMTX_AVAIL(am))
326177633Sdfr				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
327177633Sdfr				    "wait %s, waiters %d\n",
328177633Sdfr				    am->am_name, am->am_waiters));
329177633Sdfr			else
330177633Sdfr				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
331180025Sdfr				    "wait %s, owner %p, waiters %d\n",
332180743Sdfr				    am->am_name, am->am_owner, am->am_waiters));
333180025Sdfr		}
334180025Sdfr	}
335177633Sdfr
336177633Sdfr	mtx_unlock(&am->am_lock);
337177633Sdfr
338177633Sdfr	mtx_destroy(&am->am_lock);
339177633Sdfr	free(am, M_ACPISEM);
340177633Sdfr}
341177633Sdfr
342177633SdfrACPI_STATUS
343177633SdfrAcpiOsAcquireMutex(ACPI_MUTEX Handle, UINT16 Timeout)
344177633Sdfr{
345177633Sdfr	struct acpi_mutex	*am = (struct acpi_mutex *)Handle;
346177633Sdfr	int			error, prevtick, slptick, tmo;
347177633Sdfr	ACPI_STATUS		status = AE_OK;
348177633Sdfr
349177633Sdfr	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
350177633Sdfr
351177633Sdfr	if (am == NULL)
352177633Sdfr		return_ACPI_STATUS (AE_BAD_PARAMETER);
353177633Sdfr
354177633Sdfr	mtx_lock(&am->am_lock);
355177633Sdfr
356177633Sdfr	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", am->am_name));
357177633Sdfr
358177633Sdfr	if (ACPIMTX_OWNED(am)) {
359177633Sdfr		am->am_nested++;
360177633Sdfr		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
361177633Sdfr		    "acquire nested %s, depth %d\n",
362177633Sdfr		    am->am_name, am->am_nested));
363177633Sdfr		mtx_unlock(&am->am_lock);
364177633Sdfr		return_ACPI_STATUS (AE_OK);
365177633Sdfr	}
366177633Sdfr
367177633Sdfr	switch (Timeout) {
368	case ACPI_DO_NOT_WAIT:
369		if (!ACPIMTX_AVAIL(am))
370			status = AE_TIME;
371		break;
372	case ACPI_WAIT_FOREVER:
373		while (!ACPIMTX_AVAIL(am)) {
374			am->am_waiters++;
375			error = mtx_sleep(am, &am->am_lock, PCATCH, "acmtx", 0);
376			am->am_waiters--;
377			if (error == EINTR || am->am_reset) {
378				status = AE_ERROR;
379				break;
380			}
381		}
382		break;
383	default:
384		tmo = timeout2hz(Timeout);
385		while (!ACPIMTX_AVAIL(am)) {
386			prevtick = ticks;
387			am->am_waiters++;
388			error = mtx_sleep(am, &am->am_lock, PCATCH,
389			    "acmtx", tmo);
390			am->am_waiters--;
391			if (error == EINTR || am->am_reset) {
392				status = AE_ERROR;
393				break;
394			}
395			if (ACPIMTX_AVAIL(am))
396				break;
397			slptick = ticks - prevtick;
398			if (slptick >= tmo || slptick < 0) {
399				status = AE_TIME;
400				break;
401			}
402			tmo -= slptick;
403		}
404	}
405	if (status == AE_OK)
406		am->am_owner = curthread;
407
408	mtx_unlock(&am->am_lock);
409
410	return_ACPI_STATUS (status);
411}
412
413void
414AcpiOsReleaseMutex(ACPI_MUTEX Handle)
415{
416	struct acpi_mutex	*am = (struct acpi_mutex *)Handle;
417
418	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
419
420	if (am == NULL) {
421		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
422		    "cannot release null mutex\n"));
423		return_VOID;
424	}
425
426	mtx_lock(&am->am_lock);
427
428	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", am->am_name));
429
430	if (ACPIMTX_OWNED(am)) {
431		if (am->am_nested > 0) {
432			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
433			    "release nested %s, depth %d\n",
434			    am->am_name, am->am_nested));
435			am->am_nested--;
436		} else
437			am->am_owner = NULL;
438	} else {
439		if (ACPIMTX_AVAIL(am))
440			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
441			    "release already available %s\n", am->am_name));
442		else
443			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
444			    "release unowned %s from %p, depth %d\n",
445			    am->am_name, am->am_owner, am->am_nested));
446	}
447	if (am->am_waiters > 0 && ACPIMTX_AVAIL(am))
448		wakeup_one(am);
449
450	mtx_unlock(&am->am_lock);
451}
452
453#undef ACPIMTX_AVAIL
454#undef ACPIMTX_OWNED
455
456/*
457 * ACPI_SPINLOCK
458 */
459struct acpi_spinlock {
460	struct mtx	al_lock;
461	char		al_name[32];
462	int		al_nested;
463};
464
465ACPI_STATUS
466AcpiOsCreateLock(ACPI_SPINLOCK *OutHandle)
467{
468	struct acpi_spinlock	*al;
469
470	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
471
472	if (OutHandle == NULL)
473		return_ACPI_STATUS (AE_BAD_PARAMETER);
474
475	if ((al = malloc(sizeof(*al), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
476		return_ACPI_STATUS (AE_NO_MEMORY);
477
478#ifdef ACPI_DEBUG
479	if (OutHandle == &AcpiGbl_GpeLock)
480		snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (GPE)");
481	else if (OutHandle == &AcpiGbl_HardwareLock)
482		snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (HW)");
483	else
484#endif
485	snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (%p)", al);
486	mtx_init(&al->al_lock, al->al_name, NULL, MTX_SPIN);
487
488	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", al->al_name));
489
490	*OutHandle = (ACPI_SPINLOCK)al;
491
492	return_ACPI_STATUS (AE_OK);
493}
494
495void
496AcpiOsDeleteLock(ACPI_SPINLOCK Handle)
497{
498	struct acpi_spinlock	*al = (struct acpi_spinlock *)Handle;
499
500	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
501
502	if (al == NULL) {
503		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
504		    "cannot delete null spinlock\n"));
505		return_VOID;
506	}
507
508	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", al->al_name));
509
510	mtx_destroy(&al->al_lock);
511	free(al, M_ACPISEM);
512}
513
514ACPI_CPU_FLAGS
515AcpiOsAcquireLock(ACPI_SPINLOCK Handle)
516{
517	struct acpi_spinlock	*al = (struct acpi_spinlock *)Handle;
518
519	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
520
521	if (al == NULL) {
522		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
523		    "cannot acquire null spinlock\n"));
524		return (0);
525	}
526
527	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", al->al_name));
528
529	if (mtx_owned(&al->al_lock)) {
530		al->al_nested++;
531		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
532		    "acquire nested %s, depth %d\n",
533		    al->al_name, al->al_nested));
534	} else
535		mtx_lock_spin(&al->al_lock);
536
537	return (0);
538}
539
540void
541AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
542{
543	struct acpi_spinlock	*al = (struct acpi_spinlock *)Handle;
544
545	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
546
547	if (al == NULL) {
548		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
549		    "cannot release null spinlock\n"));
550		return_VOID;
551	}
552
553	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", al->al_name));
554
555	if (mtx_owned(&al->al_lock)) {
556		if (al->al_nested > 0) {
557			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
558			    "release nested %s, depth %d\n",
559			    al->al_name, al->al_nested));
560			al->al_nested--;
561		} else
562			mtx_unlock_spin(&al->al_lock);
563	} else
564		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
565		    "cannot release unowned %s\n", al->al_name));
566}
567
568/* Section 5.2.10.1: global lock acquire/release functions */
569#define	GL_ACQUIRED	(-1)
570#define	GL_BUSY		0
571#define	GL_BIT_PENDING	0x01
572#define	GL_BIT_OWNED	0x02
573#define	GL_BIT_MASK	(GL_BIT_PENDING | GL_BIT_OWNED)
574
575/*
576 * Acquire the global lock.  If busy, set the pending bit.  The caller
577 * will wait for notification from the BIOS that the lock is available
578 * and then attempt to acquire it again.
579 */
580int
581acpi_acquire_global_lock(uint32_t *lock)
582{
583	uint32_t	new, old;
584
585	do {
586		old = *lock;
587		new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) |
588			((old >> 1) & GL_BIT_PENDING);
589	} while (atomic_cmpset_acq_int(lock, old, new) == 0);
590
591	return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY);
592}
593
594/*
595 * Release the global lock, returning whether there is a waiter pending.
596 * If the BIOS set the pending bit, OSPM must notify the BIOS when it
597 * releases the lock.
598 */
599int
600acpi_release_global_lock(uint32_t *lock)
601{
602	uint32_t	new, old;
603
604	do {
605		old = *lock;
606		new = old & ~GL_BIT_MASK;
607	} while (atomic_cmpset_rel_int(lock, old, new) == 0);
608
609	return (old & GL_BIT_PENDING);
610}
611