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