OsdSynch.c revision 151948
167760Smsmith/*-
267760Smsmith * Copyright (c) 2000 Michael Smith
367760Smsmith * Copyright (c) 2000 BSDi
467760Smsmith * All rights reserved.
567760Smsmith *
667760Smsmith * Redistribution and use in source and binary forms, with or without
767760Smsmith * modification, are permitted provided that the following conditions
867760Smsmith * are met:
967760Smsmith * 1. Redistributions of source code must retain the above copyright
1067760Smsmith *    notice, this list of conditions and the following disclaimer.
1167760Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1267760Smsmith *    notice, this list of conditions and the following disclaimer in the
1367760Smsmith *    documentation and/or other materials provided with the distribution.
1467760Smsmith *
1567760Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1667760Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1767760Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1867760Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1967760Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2067760Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2167760Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2267760Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2367760Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2467760Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567760Smsmith * SUCH DAMAGE.
2667760Smsmith */
2767760Smsmith
2867760Smsmith/*
2967760Smsmith * 6.1 : Mutual Exclusion and Synchronisation
3067760Smsmith */
3167760Smsmith
32148318Snjl#include <sys/cdefs.h>
33148318Snjl__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 151948 2005-11-01 22:44:08Z jkim $");
34148318Snjl
35150003Sobrien#include <contrib/dev/acpica/acpi.h>
3667760Smsmith
3788420Siwasaki#include "opt_acpi.h"
3867760Smsmith#include <sys/kernel.h>
39105278Sjhb#include <sys/malloc.h>
40105278Sjhb#include <sys/sysctl.h>
4174914Sjhb#include <sys/lock.h>
4267760Smsmith#include <sys/mutex.h>
4367760Smsmith
4477432Smsmith#define _COMPONENT	ACPI_OS_SERVICES
4591128SmsmithACPI_MODULE_NAME("SYNCH")
4671876Smsmith
47128227SnjlMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
4867760Smsmith
49130695Snjl#define AS_LOCK(as)	mtx_lock(&(as)->as_mtx)
50130695Snjl#define AS_UNLOCK(as)	mtx_unlock(&(as)->as_mtx)
51105278Sjhb
5267760Smsmith/*
53128227Snjl * Simple counting semaphore implemented using a mutex.  (Subsequently used
5467760Smsmith * in the OSI code to implement a mutex.  Go figure.)
5567760Smsmith */
5667760Smsmithstruct acpi_semaphore {
5767760Smsmith    struct mtx	as_mtx;
5867760Smsmith    UINT32	as_units;
5967760Smsmith    UINT32	as_maxunits;
6088420Siwasaki    UINT32	as_pendings;
6188420Siwasaki    UINT32	as_resetting;
6288420Siwasaki    UINT32	as_timeouts;
6367760Smsmith};
6467760Smsmith
6588420Siwasaki#ifndef ACPI_NO_SEMAPHORES
6688420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING
6788420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING	4
6888420Siwasaki#endif
6988420Siwasakistatic int	acpi_semaphore_debug = 0;
7088420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
71120494SnjlSYSCTL_DECL(_debug_acpi);
72120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW,
73120494Snjl	   &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages");
74128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
7588420Siwasaki
7667760SmsmithACPI_STATUS
77128227SnjlAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits,
78128227Snjl    ACPI_HANDLE *OutHandle)
7967760Smsmith{
8071876Smsmith#ifndef ACPI_NO_SEMAPHORES
8167760Smsmith    struct acpi_semaphore	*as;
8267760Smsmith
8396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
8471876Smsmith
8567760Smsmith    if (OutHandle == NULL)
86128227Snjl	return_ACPI_STATUS (AE_BAD_PARAMETER);
8767760Smsmith    if (InitialUnits > MaxUnits)
88128227Snjl	return_ACPI_STATUS (AE_BAD_PARAMETER);
8967760Smsmith
90128227Snjl    if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
91128227Snjl	return_ACPI_STATUS (AE_NO_MEMORY);
9267760Smsmith
9393818Sjhb    mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF);
9467760Smsmith    as->as_units = InitialUnits;
9567760Smsmith    as->as_maxunits = MaxUnits;
9688420Siwasaki    as->as_pendings = as->as_resetting = as->as_timeouts = 0;
9767760Smsmith
9888420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
9988420Siwasaki	"created semaphore %p max %d, initial %d\n",
10088420Siwasaki	as, InitialUnits, MaxUnits));
10171876Smsmith
10267760Smsmith    *OutHandle = (ACPI_HANDLE)as;
10371876Smsmith#else
10471876Smsmith    *OutHandle = (ACPI_HANDLE)OutHandle;
105128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
106128227Snjl
107128227Snjl    return_ACPI_STATUS (AE_OK);
10867760Smsmith}
10967760Smsmith
11067760SmsmithACPI_STATUS
111128227SnjlAcpiOsDeleteSemaphore(ACPI_HANDLE Handle)
11267760Smsmith{
11371876Smsmith#ifndef ACPI_NO_SEMAPHORES
11471359Smsmith    struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
11571876Smsmith
11696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
11771876Smsmith
11888420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
11971359Smsmith    mtx_destroy(&as->as_mtx);
12067760Smsmith    free(Handle, M_ACPISEM);
121128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
122128227Snjl
123128227Snjl    return_ACPI_STATUS (AE_OK);
12467760Smsmith}
12567760Smsmith
12667760Smsmith/*
12767760Smsmith * This implementation has a bug, in that it has to stall for the entire
12867760Smsmith * timeout before it will return AE_TIME.  A better implementation would
12967760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up.
13067760Smsmith */
13167760SmsmithACPI_STATUS
132107328SiwasakiAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout)
13367760Smsmith{
13471876Smsmith#ifndef ACPI_NO_SEMAPHORES
135128227Snjl    ACPI_STATUS			result;
13667760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
13771876Smsmith    int				rv, tmo;
13888420Siwasaki    struct timeval		timeouttv, currenttv, timelefttv;
13967760Smsmith
14096926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
14171876Smsmith
14267760Smsmith    if (as == NULL)
143128227Snjl	return_ACPI_STATUS (AE_BAD_PARAMETER);
14467760Smsmith
14588420Siwasaki    if (cold)
146128227Snjl	return_ACPI_STATUS (AE_OK);
14788420Siwasaki
14888420Siwasaki#if 0
14988420Siwasaki    if (as->as_units < Units && as->as_timeouts > 10) {
15088420Siwasaki	printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
151105278Sjhb	AS_LOCK(as);
15288420Siwasaki	as->as_units = as->as_maxunits;
15388420Siwasaki	if (as->as_pendings)
15488420Siwasaki	    as->as_resetting = 1;
15588420Siwasaki	as->as_timeouts = 0;
15688420Siwasaki	wakeup(as);
157105278Sjhb	AS_UNLOCK(as);
158128227Snjl	return_ACPI_STATUS (AE_TIME);
15988420Siwasaki    }
16088420Siwasaki
161128227Snjl    if (as->as_resetting)
162128227Snjl	return_ACPI_STATUS (AE_TIME);
16388420Siwasaki#endif
16488420Siwasaki
165107328Siwasaki    /* a timeout of ACPI_WAIT_FOREVER means "forever" */
166107328Siwasaki    if (Timeout == ACPI_WAIT_FOREVER) {
16771876Smsmith	tmo = 0;
16888420Siwasaki	timeouttv.tv_sec = ((0xffff/1000) + 1);	/* cf. ACPI spec */
16988420Siwasaki	timeouttv.tv_usec = 0;
17071876Smsmith    } else {
17171876Smsmith	/* compute timeout using microseconds per tick */
17277432Smsmith	tmo = (Timeout * 1000) / (1000000 / hz);
17371876Smsmith	if (tmo <= 0)
17471876Smsmith	    tmo = 1;
17588420Siwasaki	timeouttv.tv_sec  = Timeout / 1000;
17688420Siwasaki	timeouttv.tv_usec = (Timeout % 1000) * 1000;
17771876Smsmith    }
17871876Smsmith
17988420Siwasaki    /* calculate timeout value in timeval */
18088420Siwasaki    getmicrotime(&currenttv);
18188420Siwasaki    timevaladd(&timeouttv, &currenttv);
18288420Siwasaki
183105278Sjhb    AS_LOCK(as);
18488420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
18588420Siwasaki	"get %d units from semaphore %p (has %d), timeout %d\n",
18688420Siwasaki	Units, as, as->as_units, Timeout));
18767760Smsmith    for (;;) {
18899492Siwasaki	if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) {
18980071Smsmith	    result = AE_OK;
19080071Smsmith	    break;
19180071Smsmith	}
19267760Smsmith	if (as->as_units >= Units) {
19367760Smsmith	    as->as_units -= Units;
19467760Smsmith	    result = AE_OK;
19567760Smsmith	    break;
19667760Smsmith	}
19788420Siwasaki
19888420Siwasaki	/* limit number of pending treads */
19988420Siwasaki	if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
20067760Smsmith	    result = AE_TIME;
20167760Smsmith	    break;
20267760Smsmith	}
20388420Siwasaki
20488420Siwasaki	/* if timeout values of zero is specified, return immediately */
20588420Siwasaki	if (Timeout == 0) {
20688420Siwasaki	    result = AE_TIME;
20788420Siwasaki	    break;
20888420Siwasaki	}
20988420Siwasaki
21088420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
21188420Siwasaki	    "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
21288420Siwasaki	    as, &as->as_mtx, PCATCH, tmo));
21388420Siwasaki
21488420Siwasaki	as->as_pendings++;
21588420Siwasaki
21688420Siwasaki	if (acpi_semaphore_debug) {
21788420Siwasaki	    printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
21888420Siwasaki		__func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
21988420Siwasaki	}
22088420Siwasaki
22188420Siwasaki	rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
22288420Siwasaki
22388420Siwasaki	as->as_pendings--;
22488420Siwasaki
22588420Siwasaki#if 0
22688420Siwasaki	if (as->as_resetting) {
22788420Siwasaki	    /* semaphore reset, return immediately */
22888420Siwasaki	    if (as->as_pendings == 0) {
22988420Siwasaki		as->as_resetting = 0;
23088420Siwasaki	    }
23188420Siwasaki	    result = AE_TIME;
23288420Siwasaki	    break;
23388420Siwasaki	}
23488420Siwasaki#endif
23588420Siwasaki
23688420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
23771876Smsmith	if (rv == EWOULDBLOCK) {
23867760Smsmith	    result = AE_TIME;
23967760Smsmith	    break;
24067760Smsmith	}
24188420Siwasaki
24288420Siwasaki	/* check if we already awaited enough */
24388420Siwasaki	timelefttv = timeouttv;
24488420Siwasaki	getmicrotime(&currenttv);
24588420Siwasaki	timevalsub(&timelefttv, &currenttv);
24688420Siwasaki	if (timelefttv.tv_sec < 0) {
247128227Snjl	    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n",
248128227Snjl		as));
24988420Siwasaki	    result = AE_TIME;
25088420Siwasaki	    break;
25188420Siwasaki	}
25288420Siwasaki
25388420Siwasaki	/* adjust timeout for the next sleep */
254128227Snjl	tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) /
255128227Snjl	    (1000000 / hz);
25688420Siwasaki	if (tmo <= 0)
25788420Siwasaki	    tmo = 1;
25888420Siwasaki
25988420Siwasaki	if (acpi_semaphore_debug) {
260128227Snjl	    printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, sem %p, thread %d\n",
261128227Snjl		__func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as,
262128227Snjl		AcpiOsGetThreadId());
26388420Siwasaki	}
26467760Smsmith    }
26588420Siwasaki
26688420Siwasaki    if (acpi_semaphore_debug) {
26788420Siwasaki	if (result == AE_TIME && Timeout > 0) {
26888420Siwasaki	    printf("%s: Timeout %d, pending %d, semaphore %p\n",
26988420Siwasaki		__func__, Timeout, as->as_pendings, as);
27088420Siwasaki	}
27188420Siwasaki	if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
272128227Snjl	    printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n",
273128227Snjl		__func__, Units, as->as_units, as->as_pendings, as,
274128227Snjl		AcpiOsGetThreadId());
27588420Siwasaki	}
27688420Siwasaki    }
27788420Siwasaki
278128227Snjl    if (result == AE_TIME)
27988420Siwasaki	as->as_timeouts++;
280128227Snjl    else
28188420Siwasaki	as->as_timeouts = 0;
28288420Siwasaki
283105278Sjhb    AS_UNLOCK(as);
284128227Snjl    return_ACPI_STATUS (result);
28571876Smsmith#else
286128227Snjl    return_ACPI_STATUS (AE_OK);
287128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
28867760Smsmith}
28967760Smsmith
29067760SmsmithACPI_STATUS
29167760SmsmithAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units)
29267760Smsmith{
29371876Smsmith#ifndef ACPI_NO_SEMAPHORES
29467760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
29567760Smsmith
29696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
29771876Smsmith
29867760Smsmith    if (as == NULL)
29971876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
30067760Smsmith
301105278Sjhb    AS_LOCK(as);
30288420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
30388420Siwasaki	"return %d units to semaphore %p (has %d)\n",
30488420Siwasaki	Units, as, as->as_units));
30599492Siwasaki    if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) {
30680071Smsmith	as->as_units += Units;
30780071Smsmith	if (as->as_units > as->as_maxunits)
30880071Smsmith	    as->as_units = as->as_maxunits;
30980071Smsmith    }
31088420Siwasaki
31188420Siwasaki    if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
31288420Siwasaki	printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
31388420Siwasaki	    __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
31488420Siwasaki    }
31588420Siwasaki
31667760Smsmith    wakeup(as);
317105278Sjhb    AS_UNLOCK(as);
318128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
319128227Snjl
320128227Snjl    return_ACPI_STATUS (AE_OK);
32167760Smsmith}
322117530Snjl
323117530SnjlACPI_STATUS
324117530SnjlAcpiOsCreateLock (ACPI_HANDLE *OutHandle)
325117530Snjl{
326117530Snjl    struct mtx *m;
327117530Snjl
328117530Snjl    if (OutHandle == NULL)
329117530Snjl	return (AE_BAD_PARAMETER);
330128227Snjl    m = malloc(sizeof(*m), M_ACPISEM, M_NOWAIT | M_ZERO);
331117530Snjl    if (m == NULL)
332117530Snjl	return (AE_NO_MEMORY);
333117530Snjl
334117530Snjl    mtx_init(m, "acpica subsystem lock", NULL, MTX_DEF);
335117530Snjl    *OutHandle = (ACPI_HANDLE)m;
336117530Snjl    return (AE_OK);
337117530Snjl}
338117530Snjl
339117530Snjlvoid
340117530SnjlAcpiOsDeleteLock (ACPI_HANDLE Handle)
341117530Snjl{
342117530Snjl    struct mtx *m = (struct mtx *)Handle;
343117530Snjl
344117530Snjl    if (Handle == NULL)
345117530Snjl        return;
346117530Snjl    mtx_destroy(m);
347117530Snjl}
348117530Snjl
349117530Snjl/*
350117530Snjl * The Flags parameter seems to state whether or not caller is an ISR
351117530Snjl * (and thus can't block) but since we have ithreads, we don't worry
352117530Snjl * about potentially blocking.
353117530Snjl */
354151948SjkimACPI_NATIVE_UINT
355151948SjkimAcpiOsAcquireLock (ACPI_HANDLE Handle)
356117530Snjl{
357117530Snjl    struct mtx *m = (struct mtx *)Handle;
358117530Snjl
359117530Snjl    if (Handle == NULL)
360151948Sjkim	return (0);
361117530Snjl    mtx_lock(m);
362151948Sjkim    return (0);
363117530Snjl}
364117530Snjl
365117530Snjlvoid
366151948SjkimAcpiOsReleaseLock (ACPI_HANDLE Handle, ACPI_NATIVE_UINT Flags)
367117530Snjl{
368117530Snjl    struct mtx *m = (struct mtx *)Handle;
369117530Snjl
370117530Snjl    if (Handle == NULL)
371128227Snjl	return;
372117530Snjl    mtx_unlock(m);
373117530Snjl}
374128979Snjl
375128979Snjl/* Section 5.2.9.1:  global lock acquire/release functions */
376128979Snjl#define GL_ACQUIRED	(-1)
377128979Snjl#define GL_BUSY		0
378128979Snjl#define GL_BIT_PENDING	0x1
379128979Snjl#define GL_BIT_OWNED	0x2
380128979Snjl#define GL_BIT_MASK	(GL_BIT_PENDING | GL_BIT_OWNED)
381128979Snjl
382128979Snjl/*
383128979Snjl * Acquire the global lock.  If busy, set the pending bit.  The caller
384128979Snjl * will wait for notification from the BIOS that the lock is available
385128979Snjl * and then attempt to acquire it again.
386128979Snjl */
387128979Snjlint
388128979Snjlacpi_acquire_global_lock(uint32_t *lock)
389128979Snjl{
390128979Snjl	uint32_t new, old;
391128979Snjl
392128979Snjl	do {
393128979Snjl		old = *lock;
394128981Snjl		new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) |
395128981Snjl			((old >> 1) & GL_BIT_PENDING);
396128979Snjl	} while (atomic_cmpset_acq_int(lock, old, new) == 0);
397128979Snjl
398128979Snjl	return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY);
399128979Snjl}
400128979Snjl
401128979Snjl/*
402128979Snjl * Release the global lock, returning whether there is a waiter pending.
403128979Snjl * If the BIOS set the pending bit, OSPM must notify the BIOS when it
404128979Snjl * releases the lock.
405128979Snjl */
406128979Snjlint
407128979Snjlacpi_release_global_lock(uint32_t *lock)
408128979Snjl{
409128979Snjl	uint32_t new, old;
410128979Snjl
411128979Snjl	do {
412128979Snjl		old = *lock;
413128979Snjl		new = old & ~GL_BIT_MASK;
414128979Snjl	} while (atomic_cmpset_rel_int(lock, old, new) == 0);
415128979Snjl
416128979Snjl	return (old & GL_BIT_PENDING);
417128979Snjl}
418