OsdSynch.c revision 193530
18705Sjkh/*-
28705Sjkh * Copyright (c) 2000 Michael Smith
38705Sjkh * Copyright (c) 2000 BSDi
48705Sjkh * All rights reserved.
58705Sjkh *
68705Sjkh * Redistribution and use in source and binary forms, with or without
712661Speter * modification, are permitted provided that the following conditions
88705Sjkh * are met:
98705Sjkh * 1. Redistributions of source code must retain the above copyright
108705Sjkh *    notice, this list of conditions and the following disclaimer.
118705Sjkh * 2. Redistributions in binary form must reproduce the above copyright
128705Sjkh *    notice, this list of conditions and the following disclaimer in the
138705Sjkh *    documentation and/or other materials provided with the distribution.
148705Sjkh *
158705Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
168881Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178881Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
188705Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
198705Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
208705Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
218705Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
228705Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
238705Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248705Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258705Sjkh * SUCH DAMAGE.
268705Sjkh */
278705Sjkh
288705Sjkh/*
298705Sjkh * 6.1 : Mutual Exclusion and Synchronisation
308705Sjkh */
318705Sjkh
328705Sjkh#include <sys/cdefs.h>
338705Sjkh__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 193530 2009-06-05 18:44:36Z jkim $");
348705Sjkh
358705Sjkh#include <contrib/dev/acpica/include/acpi.h>
368705Sjkh#include <contrib/dev/acpica/include/accommon.h>
378705Sjkh
388705Sjkh#include "opt_acpi.h"
398705Sjkh#include <sys/kernel.h>
408705Sjkh#include <sys/malloc.h>
418705Sjkh#include <sys/sysctl.h>
428705Sjkh#include <sys/lock.h>
438705Sjkh#include <sys/mutex.h>
448705Sjkh
458705Sjkh#define _COMPONENT	ACPI_OS_SERVICES
468768SjkhACPI_MODULE_NAME("SYNCH")
478705Sjkh
488705SjkhMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
498705Sjkh
508705Sjkh#define AS_LOCK(as)	mtx_lock(&(as)->as_mtx)
518705Sjkh#define AS_UNLOCK(as)	mtx_unlock(&(as)->as_mtx)
528705Sjkh
5312661Speter/*
548705Sjkh * Simple counting semaphore implemented using a mutex.  (Subsequently used
5512661Speter * in the OSI code to implement a mutex.  Go figure.)
568705Sjkh */
5712661Speterstruct acpi_semaphore {
5812661Speter    struct mtx	as_mtx;
5912661Speter    UINT32	as_units;
6012661Speter    UINT32	as_maxunits;
6112661Speter    UINT32	as_pendings;
6212661Speter    UINT32	as_resetting;
638705Sjkh    UINT32	as_timeouts;
6412661Speter};
6512661Speter
668705Sjkh/* Default number of maximum pending threads. */
678705Sjkh#ifndef ACPI_NO_SEMAPHORES
6812661Speter#ifndef ACPI_SEMAPHORES_MAX_PENDING
698705Sjkh#define ACPI_SEMAPHORES_MAX_PENDING	4
708705Sjkh#endif
7112661Speter
7212661Speterstatic int	acpi_semaphore_debug = 0;
7312661SpeterTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
7412661SpeterSYSCTL_DECL(_debug_acpi);
7512661SpeterSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW,
7612661Speter	   &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages");
7712661Speter#endif /* !ACPI_NO_SEMAPHORES */
7812661Speter
7912661SpeterACPI_STATUS
8012661SpeterAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits,
8112661Speter    ACPI_SEMAPHORE *OutHandle)
8212661Speter{
8312661Speter#ifndef ACPI_NO_SEMAPHORES
8412661Speter    struct acpi_semaphore	*as;
8512661Speter
8612661Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
8712661Speter
888705Sjkh    if (OutHandle == NULL)
8912661Speter	return_ACPI_STATUS (AE_BAD_PARAMETER);
908751Sjkh    if (InitialUnits > MaxUnits)
9112661Speter	return_ACPI_STATUS (AE_BAD_PARAMETER);
928751Sjkh
938751Sjkh    if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
948751Sjkh	return_ACPI_STATUS (AE_NO_MEMORY);
9512661Speter
9612661Speter    mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF);
978751Sjkh    as->as_units = InitialUnits;
988751Sjkh    as->as_maxunits = MaxUnits;
9912661Speter    as->as_pendings = as->as_resetting = as->as_timeouts = 0;
1008751Sjkh
1018751Sjkh    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
1028751Sjkh	"created semaphore %p max %d, initial %d\n",
1038705Sjkh	as, InitialUnits, MaxUnits));
1048705Sjkh
1059202Srgrimes    *OutHandle = (ACPI_HANDLE)as;
1069202Srgrimes#else
1079202Srgrimes    *OutHandle = (ACPI_HANDLE)OutHandle;
1088705Sjkh#endif /* !ACPI_NO_SEMAPHORES */
1098705Sjkh
1108705Sjkh    return_ACPI_STATUS (AE_OK);
1118705Sjkh}
1128705Sjkh
1138705SjkhACPI_STATUS
1148705SjkhAcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle)
1158756Sjkh{
1168705Sjkh#ifndef ACPI_NO_SEMAPHORES
1178705Sjkh    struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
1188705Sjkh
1198705Sjkh    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1208705Sjkh
1218705Sjkh    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
1228705Sjkh    mtx_destroy(&as->as_mtx);
1239202Srgrimes    free(Handle, M_ACPISEM);
1248705Sjkh#endif /* !ACPI_NO_SEMAPHORES */
1258705Sjkh
1268705Sjkh    return_ACPI_STATUS (AE_OK);
1278705Sjkh}
1288705Sjkh
1298705Sjkh/*
1308705Sjkh * This implementation has a bug, in that it has to stall for the entire
1318705Sjkh * timeout before it will return AE_TIME.  A better implementation would
1328705Sjkh * use getmicrotime() to correctly adjust the timeout after being woken up.
1338705Sjkh */
1348705SjkhACPI_STATUS
1358820SjkhAcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout)
13610882Speter{
1378705Sjkh#ifndef ACPI_NO_SEMAPHORES
1388705Sjkh    ACPI_STATUS			result;
1398705Sjkh    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
1408705Sjkh    int				rv, tmo;
1418705Sjkh    struct timeval		timeouttv, currenttv, timelefttv;
1428705Sjkh
1439202Srgrimes    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1449202Srgrimes
1459202Srgrimes    if (as == NULL)
1468705Sjkh	return_ACPI_STATUS (AE_BAD_PARAMETER);
1478705Sjkh
14812661Speter    if (cold)
1498722Sjkh	return_ACPI_STATUS (AE_OK);
1508705Sjkh
1518705Sjkh#if 0
1528705Sjkh    if (as->as_units < Units && as->as_timeouts > 10) {
1538705Sjkh	printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
1548705Sjkh	AS_LOCK(as);
1558705Sjkh	as->as_units = as->as_maxunits;
1568705Sjkh	if (as->as_pendings)
1579202Srgrimes	    as->as_resetting = 1;
1589202Srgrimes	as->as_timeouts = 0;
15912661Speter	wakeup(as);
16012661Speter	AS_UNLOCK(as);
16112661Speter	return_ACPI_STATUS (AE_TIME);
16212661Speter    }
16312661Speter
16412661Speter    if (as->as_resetting)
16512661Speter	return_ACPI_STATUS (AE_TIME);
1669202Srgrimes#endif
16712661Speter
1688705Sjkh    /* a timeout of ACPI_WAIT_FOREVER means "forever" */
1698705Sjkh    if (Timeout == ACPI_WAIT_FOREVER) {
17012661Speter	tmo = 0;
1718705Sjkh	timeouttv.tv_sec = ((0xffff/1000) + 1);	/* cf. ACPI spec */
17212661Speter	timeouttv.tv_usec = 0;
1738705Sjkh    } else {
1748705Sjkh	/* compute timeout using microseconds per tick */
1758705Sjkh	tmo = (Timeout * 1000) / (1000000 / hz);
1769202Srgrimes	if (tmo <= 0)
1778705Sjkh	    tmo = 1;
1788705Sjkh	timeouttv.tv_sec  = Timeout / 1000;
1798705Sjkh	timeouttv.tv_usec = (Timeout % 1000) * 1000;
1808705Sjkh    }
1818705Sjkh
1828705Sjkh    /* calculate timeout value in timeval */
1838751Sjkh    getmicrotime(&currenttv);
1848751Sjkh    timevaladd(&timeouttv, &currenttv);
1858751Sjkh
1869202Srgrimes    AS_LOCK(as);
1878705Sjkh    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
1888751Sjkh	"get %d units from semaphore %p (has %d), timeout %d\n",
1898751Sjkh	Units, as, as->as_units, Timeout));
1909202Srgrimes    for (;;) {
1918751Sjkh	if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) {
1928751Sjkh	    result = AE_OK;
1938705Sjkh	    break;
19412661Speter	}
19512661Speter	if (as->as_units >= Units) {
1968705Sjkh	    as->as_units -= Units;
1978705Sjkh	    result = AE_OK;
1988705Sjkh	    break;
19912661Speter	}
20012661Speter
20112661Speter	/* limit number of pending threads */
20212661Speter	if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
2038705Sjkh	    result = AE_TIME;
2048705Sjkh	    break;
2058705Sjkh	}
2068837Sjkh
2079202Srgrimes	/* if timeout values of zero is specified, return immediately */
20812661Speter	if (Timeout == 0) {
2099202Srgrimes	    result = AE_TIME;
2108705Sjkh	    break;
2119202Srgrimes	}
2128705Sjkh
2138705Sjkh	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
2148705Sjkh	    "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
2158705Sjkh	    as, &as->as_mtx, PCATCH, tmo));
2168705Sjkh
2178705Sjkh	as->as_pendings++;
2188705Sjkh
21912661Speter	if (acpi_semaphore_debug) {
22012661Speter	    printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
22112661Speter		__func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
22212661Speter	}
22312661Speter
22412661Speter	rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
2258705Sjkh
2268705Sjkh	as->as_pendings--;
2278705Sjkh
2288705Sjkh#if 0
2298705Sjkh	if (as->as_resetting) {
2308705Sjkh	    /* semaphore reset, return immediately */
2318705Sjkh	    if (as->as_pendings == 0) {
23212661Speter		as->as_resetting = 0;
23312661Speter	    }
23412661Speter	    result = AE_TIME;
23512661Speter	    break;
23612661Speter	}
23712661Speter#endif
2388705Sjkh
2398705Sjkh	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
2409202Srgrimes	if (rv == EWOULDBLOCK) {
2419202Srgrimes	    result = AE_TIME;
24212661Speter	    break;
2438705Sjkh	}
2448705Sjkh
2458722Sjkh	/* check if we already awaited enough */
2468722Sjkh	timelefttv = timeouttv;
2478722Sjkh	getmicrotime(&currenttv);
2488722Sjkh	timevalsub(&timelefttv, &currenttv);
24912661Speter	if (timelefttv.tv_sec < 0) {
25012661Speter	    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n",
2518705Sjkh		as));
2528722Sjkh	    result = AE_TIME;
2538705Sjkh	    break;
2548722Sjkh	}
25512661Speter
2568722Sjkh	/* adjust timeout for the next sleep */
2578722Sjkh	tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) /
25812661Speter	    (1000000 / hz);
2598722Sjkh	if (tmo <= 0)
2608722Sjkh	    tmo = 1;
2618722Sjkh
26212661Speter	if (acpi_semaphore_debug) {
26312661Speter	    printf("%s: Wakeup timeleft(%jd, %lu), tmo %u, sem %p, thread %d\n",
26412661Speter		__func__, (intmax_t)timelefttv.tv_sec, timelefttv.tv_usec, tmo, as,
2658722Sjkh		AcpiOsGetThreadId());
2668722Sjkh	}
26712661Speter    }
26812661Speter
26912661Speter    if (acpi_semaphore_debug) {
27012661Speter	if (result == AE_TIME && Timeout > 0) {
27112661Speter	    printf("%s: Timeout %d, pending %d, semaphore %p\n",
2728756Sjkh		__func__, Timeout, as->as_pendings, as);
2738722Sjkh	}
2748722Sjkh	if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
2758722Sjkh	    printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n",
27612661Speter		__func__, Units, as->as_units, as->as_pendings, as,
27712661Speter		AcpiOsGetThreadId());
2788722Sjkh	}
2798722Sjkh    }
28012661Speter
2818756Sjkh    if (result == AE_TIME)
28212661Speter	as->as_timeouts++;
28312661Speter    else
2848756Sjkh	as->as_timeouts = 0;
28512661Speter
28612661Speter    AS_UNLOCK(as);
2878756Sjkh    return_ACPI_STATUS (result);
2888756Sjkh#else
2898756Sjkh    return_ACPI_STATUS (AE_OK);
29012661Speter#endif /* !ACPI_NO_SEMAPHORES */
2918756Sjkh}
29212661Speter
2938773SjkhACPI_STATUS
29412661SpeterAcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units)
2958756Sjkh{
2968722Sjkh#ifndef ACPI_NO_SEMAPHORES
2978722Sjkh    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
29812661Speter
29912661Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
30012661Speter
3018756Sjkh    if (as == NULL)
3028768Sjkh	return_ACPI_STATUS(AE_BAD_PARAMETER);
30312661Speter
30412661Speter    AS_LOCK(as);
3058756Sjkh    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
3068768Sjkh	"return %d units to semaphore %p (has %d)\n",
30712661Speter	Units, as, as->as_units));
3088768Sjkh    if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) {
3098768Sjkh	as->as_units += Units;
3108768Sjkh	if (as->as_units > as->as_maxunits)
3118768Sjkh	    as->as_units = as->as_maxunits;
3128768Sjkh    }
3138768Sjkh
31412661Speter    if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
3158768Sjkh	printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
31612661Speter	    __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
31712661Speter    }
31812661Speter
3198768Sjkh    wakeup(as);
3208768Sjkh    AS_UNLOCK(as);
32112661Speter#endif /* !ACPI_NO_SEMAPHORES */
3228768Sjkh
32312661Speter    return_ACPI_STATUS (AE_OK);
3248768Sjkh}
3258756Sjkh
3268705Sjkh/* Combined mutex + mutex name storage since the latter must persist. */
3278705Sjkhstruct acpi_spinlock {
3288722Sjkh    struct mtx	lock;
3298722Sjkh    char	name[32];
3308722Sjkh};
33112661Speter
33212661SpeterACPI_STATUS
3338722SjkhAcpiOsCreateLock (ACPI_SPINLOCK *OutHandle)
3348722Sjkh{
3359202Srgrimes    struct acpi_spinlock *h;
3369202Srgrimes
3379202Srgrimes    if (OutHandle == NULL)
33812661Speter	return (AE_BAD_PARAMETER);
3399202Srgrimes    h = malloc(sizeof(*h), M_ACPISEM, M_NOWAIT | M_ZERO);
3409202Srgrimes    if (h == NULL)
3418705Sjkh	return (AE_NO_MEMORY);
3428722Sjkh
3438705Sjkh    /* Build a unique name based on the address of the handle. */
3448709Sjkh    if (OutHandle == &AcpiGbl_GpeLock)
34512661Speter	snprintf(h->name, sizeof(h->name), "acpi subsystem GPE lock");
3468709Sjkh    else if (OutHandle == &AcpiGbl_HardwareLock)
3479202Srgrimes	snprintf(h->name, sizeof(h->name), "acpi subsystem HW lock");
3488709Sjkh    else
3499202Srgrimes	snprintf(h->name, sizeof(h->name), "acpi subsys %p", OutHandle);
35012661Speter    mtx_init(&h->lock, h->name, NULL, MTX_DEF|MTX_RECURSE);
35112661Speter    *OutHandle = (ACPI_SPINLOCK)h;
35212661Speter    return (AE_OK);
35312661Speter}
35412661Speter
35512661Spetervoid
3569202SrgrimesAcpiOsDeleteLock (ACPI_SPINLOCK Handle)
3578709Sjkh{
35812661Speter    struct acpi_spinlock *h = (struct acpi_spinlock *)Handle;
35912661Speter
36012661Speter    if (Handle == NULL)
36112661Speter        return;
36212661Speter    mtx_destroy(&h->lock);
36312661Speter    free(h, M_ACPISEM);
3648709Sjkh}
3658709Sjkh
36612661Speter/*
3678709Sjkh * The Flags parameter seems to state whether or not caller is an ISR
3688709Sjkh * (and thus can't block) but since we have ithreads, we don't worry
3698709Sjkh * about potentially blocking.
37012661Speter */
37112661SpeterACPI_CPU_FLAGS
37212661SpeterAcpiOsAcquireLock (ACPI_SPINLOCK Handle)
3738709Sjkh{
3749202Srgrimes    struct acpi_spinlock *h = (struct acpi_spinlock *)Handle;
3759202Srgrimes
3769202Srgrimes    if (Handle == NULL)
3779202Srgrimes	return (0);
37812661Speter    mtx_lock(&h->lock);
37912661Speter    return (0);
38012661Speter}
38112661Speter
38212661Spetervoid
38312661SpeterAcpiOsReleaseLock (ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
38412661Speter{
38512661Speter    struct acpi_spinlock *h = (struct acpi_spinlock *)Handle;
38612661Speter
38712661Speter    if (Handle == NULL)
38812661Speter	return;
38912661Speter    mtx_unlock(&h->lock);
39012661Speter}
39112661Speter
39212661Speter/* Section 5.2.9.1:  global lock acquire/release functions */
39312661Speter#define GL_ACQUIRED	(-1)
3949202Srgrimes#define GL_BUSY		0
3959202Srgrimes#define GL_BIT_PENDING	0x1
39612661Speter#define GL_BIT_OWNED	0x2
3979202Srgrimes#define GL_BIT_MASK	(GL_BIT_PENDING | GL_BIT_OWNED)
3988705Sjkh
3998715Sjkh/*
4008715Sjkh * Acquire the global lock.  If busy, set the pending bit.  The caller
4019202Srgrimes * will wait for notification from the BIOS that the lock is available
4029202Srgrimes * and then attempt to acquire it again.
40312661Speter */
40412661Speterint
40512661Speteracpi_acquire_global_lock(uint32_t *lock)
4069202Srgrimes{
4079202Srgrimes	uint32_t new, old;
4089202Srgrimes
4098722Sjkh	do {
4108715Sjkh		old = *lock;
41112661Speter		new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) |
41212661Speter			((old >> 1) & GL_BIT_PENDING);
4138768Sjkh	} while (atomic_cmpset_acq_int(lock, old, new) == 0);
41412661Speter
41512661Speter	return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY);
41612661Speter}
41712661Speter
41812661Speter/*
41912661Speter * Release the global lock, returning whether there is a waiter pending.
42012661Speter * If the BIOS set the pending bit, OSPM must notify the BIOS when it
42112661Speter * releases the lock.
42212661Speter */
42312661Speterint
42412661Speteracpi_release_global_lock(uint32_t *lock)
42512661Speter{
42612661Speter	uint32_t new, old;
42712661Speter
42812661Speter	do {
42912661Speter		old = *lock;
43012661Speter		new = old & ~GL_BIT_MASK;
43112661Speter	} while (atomic_cmpset_rel_int(lock, old, new) == 0);
43212661Speter
43312661Speter	return (old & GL_BIT_PENDING);
43412661Speter}
43512661Speter