1/*-
2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2000 BSDi
4 * Copyright (c) 2007-2009 Jung-uk Kim <jkim@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29/*
30 * 6.1 : Mutual Exclusion and Synchronisation
31 */
32
33#include <sys/cdefs.h>
34#include <contrib/dev/acpica/include/acpi.h>
35#include <contrib/dev/acpica/include/accommon.h>
36
37#include <sys/condvar.h>
38#include <sys/kernel.h>
39#include <sys/lock.h>
40#include <sys/malloc.h>
41#include <sys/mutex.h>
42
43#define	_COMPONENT	ACPI_OS_SERVICES
44ACPI_MODULE_NAME("SYNCH")
45
46static MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
47
48/*
49 * Convert milliseconds to ticks.
50 */
51static int
52timeout2hz(UINT16 Timeout)
53{
54	struct timeval		tv;
55
56	tv.tv_sec = (time_t)(Timeout / 1000);
57	tv.tv_usec = (suseconds_t)(Timeout % 1000) * 1000;
58
59	return (tvtohz(&tv));
60}
61
62/*
63 * ACPI_SEMAPHORE
64 */
65struct acpi_sema {
66	struct mtx	as_lock;
67	char		as_name[32];
68	struct cv	as_cv;
69	UINT32		as_maxunits;
70	UINT32		as_units;
71	int		as_waiters;
72	int		as_reset;
73};
74
75ACPI_STATUS
76AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits,
77    ACPI_SEMAPHORE *OutHandle)
78{
79	struct acpi_sema	*as;
80
81	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
82
83	if (OutHandle == NULL || MaxUnits == 0 || InitialUnits > MaxUnits)
84		return_ACPI_STATUS (AE_BAD_PARAMETER);
85
86	if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
87		return_ACPI_STATUS (AE_NO_MEMORY);
88
89	snprintf(as->as_name, sizeof(as->as_name), "ACPI sema (%p)", as);
90	mtx_init(&as->as_lock, as->as_name, NULL, MTX_DEF);
91	cv_init(&as->as_cv, as->as_name);
92	as->as_maxunits = MaxUnits;
93	as->as_units = InitialUnits;
94
95	*OutHandle = (ACPI_SEMAPHORE)as;
96
97	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s, max %u, initial %u\n",
98	    as->as_name, MaxUnits, InitialUnits));
99
100	return_ACPI_STATUS (AE_OK);
101}
102
103ACPI_STATUS
104AcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle)
105{
106	struct acpi_sema	*as = (struct acpi_sema *)Handle;
107
108	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
109
110	if (as == NULL)
111		return_ACPI_STATUS (AE_BAD_PARAMETER);
112
113	mtx_lock(&as->as_lock);
114
115	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", as->as_name));
116
117	if (as->as_waiters > 0) {
118		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
119		    "reset %s, units %u, waiters %d\n",
120		    as->as_name, as->as_units, as->as_waiters));
121		as->as_reset = 1;
122		cv_broadcast(&as->as_cv);
123		while (as->as_waiters > 0) {
124			if (mtx_sleep(&as->as_reset, &as->as_lock,
125			    PCATCH, "acsrst", hz) == EINTR) {
126				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
127				    "failed to reset %s, waiters %d\n",
128				    as->as_name, as->as_waiters));
129				mtx_unlock(&as->as_lock);
130				return_ACPI_STATUS (AE_ERROR);
131			}
132			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
133			    "wait %s, units %u, waiters %d\n",
134			    as->as_name, as->as_units, as->as_waiters));
135		}
136	}
137
138	mtx_unlock(&as->as_lock);
139
140	mtx_destroy(&as->as_lock);
141	cv_destroy(&as->as_cv);
142	free(as, M_ACPISEM);
143
144	return_ACPI_STATUS (AE_OK);
145}
146
147#define	ACPISEM_AVAIL(s, u)	((s)->as_units >= (u))
148
149ACPI_STATUS
150AcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout)
151{
152	struct acpi_sema	*as = (struct acpi_sema *)Handle;
153	int			error, prevtick, slptick, tmo;
154	ACPI_STATUS		status = AE_OK;
155
156	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
157
158	if (as == NULL || Units == 0)
159		return_ACPI_STATUS (AE_BAD_PARAMETER);
160
161	mtx_lock(&as->as_lock);
162
163	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
164	    "get %u unit(s) from %s, units %u, waiters %d, timeout %u\n",
165	    Units, as->as_name, as->as_units, as->as_waiters, Timeout));
166
167	if (as->as_maxunits != ACPI_NO_UNIT_LIMIT && as->as_maxunits < Units) {
168		mtx_unlock(&as->as_lock);
169		return_ACPI_STATUS (AE_LIMIT);
170	}
171
172	switch (Timeout) {
173	case ACPI_DO_NOT_WAIT:
174		if (!ACPISEM_AVAIL(as, Units))
175			status = AE_TIME;
176		break;
177	case ACPI_WAIT_FOREVER:
178		while (!ACPISEM_AVAIL(as, Units)) {
179			as->as_waiters++;
180			error = cv_wait_sig(&as->as_cv, &as->as_lock);
181			as->as_waiters--;
182			if (error == EINTR || as->as_reset) {
183				status = AE_ERROR;
184				break;
185			}
186		}
187		break;
188	default:
189		if (cold) {
190			/*
191			 * Just spin polling the semaphore once a
192			 * millisecond.
193			 */
194			while (!ACPISEM_AVAIL(as, Units)) {
195				if (Timeout == 0) {
196					status = AE_TIME;
197					break;
198				}
199				Timeout--;
200				mtx_unlock(&as->as_lock);
201				DELAY(1000);
202				mtx_lock(&as->as_lock);
203			}
204			break;
205		}
206		tmo = timeout2hz(Timeout);
207		while (!ACPISEM_AVAIL(as, Units)) {
208			prevtick = ticks;
209			as->as_waiters++;
210			error = cv_timedwait_sig(&as->as_cv, &as->as_lock, tmo);
211			as->as_waiters--;
212			if (error == EINTR || as->as_reset) {
213				status = AE_ERROR;
214				break;
215			}
216			if (ACPISEM_AVAIL(as, Units))
217				break;
218			slptick = ticks - prevtick;
219			if (slptick >= tmo || slptick < 0) {
220				status = AE_TIME;
221				break;
222			}
223			tmo -= slptick;
224		}
225	}
226	if (ACPI_SUCCESS(status))
227		as->as_units -= Units;
228
229	mtx_unlock(&as->as_lock);
230
231	return_ACPI_STATUS (status);
232}
233
234ACPI_STATUS
235AcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units)
236{
237	struct acpi_sema	*as = (struct acpi_sema *)Handle;
238	UINT32			i;
239
240	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
241
242	if (as == NULL || Units == 0)
243		return_ACPI_STATUS (AE_BAD_PARAMETER);
244
245	mtx_lock(&as->as_lock);
246
247	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
248	    "return %u units to %s, units %u, waiters %d\n",
249	    Units, as->as_name, as->as_units, as->as_waiters));
250
251	if (as->as_maxunits != ACPI_NO_UNIT_LIMIT &&
252	    (as->as_maxunits < Units ||
253	    as->as_maxunits - Units < as->as_units)) {
254		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
255		    "exceeded max units %u\n", as->as_maxunits));
256		mtx_unlock(&as->as_lock);
257		return_ACPI_STATUS (AE_LIMIT);
258	}
259
260	as->as_units += Units;
261	if (as->as_waiters > 0 && ACPISEM_AVAIL(as, Units))
262		for (i = 0; i < Units; i++)
263			cv_signal(&as->as_cv);
264
265	mtx_unlock(&as->as_lock);
266
267	return_ACPI_STATUS (AE_OK);
268}
269
270#undef ACPISEM_AVAIL
271
272/*
273 * ACPI_MUTEX
274 */
275struct acpi_mutex {
276	struct mtx	am_lock;
277	char		am_name[32];
278	struct thread	*am_owner;
279	int		am_nested;
280	int		am_waiters;
281	int		am_reset;
282};
283
284ACPI_STATUS
285AcpiOsCreateMutex(ACPI_MUTEX *OutHandle)
286{
287	struct acpi_mutex	*am;
288
289	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
290
291	if (OutHandle == NULL)
292		return_ACPI_STATUS (AE_BAD_PARAMETER);
293
294	if ((am = malloc(sizeof(*am), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
295		return_ACPI_STATUS (AE_NO_MEMORY);
296
297	snprintf(am->am_name, sizeof(am->am_name), "ACPI mutex (%p)", am);
298	mtx_init(&am->am_lock, am->am_name, NULL, MTX_DEF);
299
300	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", am->am_name));
301
302	*OutHandle = (ACPI_MUTEX)am;
303
304	return_ACPI_STATUS (AE_OK);
305}
306
307#define	ACPIMTX_AVAIL(m)	((m)->am_owner == NULL)
308#define	ACPIMTX_OWNED(m)	((m)->am_owner == curthread)
309
310void
311AcpiOsDeleteMutex(ACPI_MUTEX Handle)
312{
313	struct acpi_mutex	*am = (struct acpi_mutex *)Handle;
314
315	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
316
317	if (am == NULL) {
318		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "cannot delete null mutex\n"));
319		return_VOID;
320	}
321
322	mtx_lock(&am->am_lock);
323
324	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", am->am_name));
325
326	if (am->am_waiters > 0) {
327		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
328		    "reset %s, owner %p\n", am->am_name, am->am_owner));
329		am->am_reset = 1;
330		wakeup(am);
331		while (am->am_waiters > 0) {
332			if (mtx_sleep(&am->am_reset, &am->am_lock,
333			    PCATCH, "acmrst", hz) == EINTR) {
334				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
335				    "failed to reset %s, waiters %d\n",
336				    am->am_name, am->am_waiters));
337				mtx_unlock(&am->am_lock);
338				return_VOID;
339			}
340			if (ACPIMTX_AVAIL(am))
341				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
342				    "wait %s, waiters %d\n",
343				    am->am_name, am->am_waiters));
344			else
345				ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
346				    "wait %s, owner %p, waiters %d\n",
347				    am->am_name, am->am_owner, am->am_waiters));
348		}
349	}
350
351	mtx_unlock(&am->am_lock);
352
353	mtx_destroy(&am->am_lock);
354	free(am, M_ACPISEM);
355}
356
357ACPI_STATUS
358AcpiOsAcquireMutex(ACPI_MUTEX Handle, UINT16 Timeout)
359{
360	struct acpi_mutex	*am = (struct acpi_mutex *)Handle;
361	int			error, prevtick, slptick, tmo;
362	ACPI_STATUS		status = AE_OK;
363
364	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
365
366	if (am == NULL)
367		return_ACPI_STATUS (AE_BAD_PARAMETER);
368
369	mtx_lock(&am->am_lock);
370
371	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", am->am_name));
372
373	if (ACPIMTX_OWNED(am)) {
374		am->am_nested++;
375		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
376		    "acquire nested %s, depth %d\n",
377		    am->am_name, am->am_nested));
378		mtx_unlock(&am->am_lock);
379		return_ACPI_STATUS (AE_OK);
380	}
381
382	switch (Timeout) {
383	case ACPI_DO_NOT_WAIT:
384		if (!ACPIMTX_AVAIL(am))
385			status = AE_TIME;
386		break;
387	case ACPI_WAIT_FOREVER:
388		while (!ACPIMTX_AVAIL(am)) {
389			am->am_waiters++;
390			error = mtx_sleep(am, &am->am_lock, PCATCH, "acmtx", 0);
391			am->am_waiters--;
392			if (error == EINTR || am->am_reset) {
393				status = AE_ERROR;
394				break;
395			}
396		}
397		break;
398	default:
399		if (cold) {
400			/*
401			 * Just spin polling the mutex once a
402			 * millisecond.
403			 */
404			while (!ACPIMTX_AVAIL(am)) {
405				if (Timeout == 0) {
406					status = AE_TIME;
407					break;
408				}
409				Timeout--;
410				mtx_unlock(&am->am_lock);
411				DELAY(1000);
412				mtx_lock(&am->am_lock);
413			}
414			break;
415		}
416		tmo = timeout2hz(Timeout);
417		while (!ACPIMTX_AVAIL(am)) {
418			prevtick = ticks;
419			am->am_waiters++;
420			error = mtx_sleep(am, &am->am_lock, PCATCH,
421			    "acmtx", tmo);
422			am->am_waiters--;
423			if (error == EINTR || am->am_reset) {
424				status = AE_ERROR;
425				break;
426			}
427			if (ACPIMTX_AVAIL(am))
428				break;
429			slptick = ticks - prevtick;
430			if (slptick >= tmo || slptick < 0) {
431				status = AE_TIME;
432				break;
433			}
434			tmo -= slptick;
435		}
436	}
437	if (ACPI_SUCCESS(status))
438		am->am_owner = curthread;
439
440	mtx_unlock(&am->am_lock);
441
442	return_ACPI_STATUS (status);
443}
444
445void
446AcpiOsReleaseMutex(ACPI_MUTEX Handle)
447{
448	struct acpi_mutex	*am = (struct acpi_mutex *)Handle;
449
450	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
451
452	if (am == NULL) {
453		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
454		    "cannot release null mutex\n"));
455		return_VOID;
456	}
457
458	mtx_lock(&am->am_lock);
459
460	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", am->am_name));
461
462	if (ACPIMTX_OWNED(am)) {
463		if (am->am_nested > 0) {
464			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
465			    "release nested %s, depth %d\n",
466			    am->am_name, am->am_nested));
467			am->am_nested--;
468		} else
469			am->am_owner = NULL;
470	} else {
471		if (ACPIMTX_AVAIL(am))
472			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
473			    "release already available %s\n", am->am_name));
474		else
475			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
476			    "release unowned %s from %p, depth %d\n",
477			    am->am_name, am->am_owner, am->am_nested));
478	}
479	if (am->am_waiters > 0 && ACPIMTX_AVAIL(am))
480		wakeup_one(am);
481
482	mtx_unlock(&am->am_lock);
483}
484
485#undef ACPIMTX_AVAIL
486#undef ACPIMTX_OWNED
487
488/*
489 * ACPI_SPINLOCK
490 */
491struct acpi_spinlock {
492	struct mtx	al_lock;
493	char		al_name[32];
494	int		al_nested;
495};
496
497ACPI_STATUS
498AcpiOsCreateLock(ACPI_SPINLOCK *OutHandle)
499{
500	struct acpi_spinlock	*al;
501
502	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
503
504	if (OutHandle == NULL)
505		return_ACPI_STATUS (AE_BAD_PARAMETER);
506
507	if ((al = malloc(sizeof(*al), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
508		return_ACPI_STATUS (AE_NO_MEMORY);
509
510#ifdef ACPI_DEBUG
511	if (OutHandle == &AcpiGbl_GpeLock)
512		snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (GPE)");
513	else if (OutHandle == &AcpiGbl_HardwareLock)
514		snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (HW)");
515	else
516#endif
517	snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (%p)", al);
518	mtx_init(&al->al_lock, al->al_name, NULL, MTX_SPIN);
519
520	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", al->al_name));
521
522	*OutHandle = (ACPI_SPINLOCK)al;
523
524	return_ACPI_STATUS (AE_OK);
525}
526
527void
528AcpiOsDeleteLock(ACPI_SPINLOCK Handle)
529{
530	struct acpi_spinlock	*al = (struct acpi_spinlock *)Handle;
531
532	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
533
534	if (al == NULL) {
535		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
536		    "cannot delete null spinlock\n"));
537		return_VOID;
538	}
539
540	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", al->al_name));
541
542	mtx_destroy(&al->al_lock);
543	free(al, M_ACPISEM);
544}
545
546ACPI_CPU_FLAGS
547AcpiOsAcquireLock(ACPI_SPINLOCK Handle)
548{
549	struct acpi_spinlock	*al = (struct acpi_spinlock *)Handle;
550
551	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
552
553	if (al == NULL) {
554		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
555		    "cannot acquire null spinlock\n"));
556		return (0);
557	}
558
559	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", al->al_name));
560
561	if (mtx_owned(&al->al_lock)) {
562		al->al_nested++;
563		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
564		    "acquire nested %s, depth %d\n",
565		    al->al_name, al->al_nested));
566	} else
567		mtx_lock_spin(&al->al_lock);
568
569	return (0);
570}
571
572void
573AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
574{
575	struct acpi_spinlock	*al = (struct acpi_spinlock *)Handle;
576
577	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
578
579	if (al == NULL) {
580		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
581		    "cannot release null spinlock\n"));
582		return_VOID;
583	}
584
585	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", al->al_name));
586
587	if (mtx_owned(&al->al_lock)) {
588		if (al->al_nested > 0) {
589			ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
590			    "release nested %s, depth %d\n",
591			    al->al_name, al->al_nested));
592			al->al_nested--;
593		} else
594			mtx_unlock_spin(&al->al_lock);
595	} else
596		ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
597		    "cannot release unowned %s\n", al->al_name));
598}
599
600/* Section 5.2.10.1: global lock acquire/release functions */
601
602/*
603 * Acquire the global lock.  If busy, set the pending bit.  The caller
604 * will wait for notification from the BIOS that the lock is available
605 * and then attempt to acquire it again.
606 */
607int
608acpi_acquire_global_lock(volatile uint32_t *lock)
609{
610	uint32_t	new, old;
611
612	do {
613		old = *lock;
614		new = (old & ~ACPI_GLOCK_PENDING) | ACPI_GLOCK_OWNED;
615		if ((old & ACPI_GLOCK_OWNED) != 0)
616			new |= ACPI_GLOCK_PENDING;
617	} while (atomic_cmpset_32(lock, old, new) == 0);
618
619	return ((new & ACPI_GLOCK_PENDING) == 0);
620}
621
622/*
623 * Release the global lock, returning whether there is a waiter pending.
624 * If the BIOS set the pending bit, OSPM must notify the BIOS when it
625 * releases the lock.
626 */
627int
628acpi_release_global_lock(volatile uint32_t *lock)
629{
630	uint32_t	new, old;
631
632	do {
633		old = *lock;
634		new = old & ~(ACPI_GLOCK_PENDING | ACPI_GLOCK_OWNED);
635	} while (atomic_cmpset_32(lock, old, new) == 0);
636
637	return ((old & ACPI_GLOCK_PENDING) != 0);
638}
639