Bring in the FreeBSD-5 ACPICA code as a module. Note: not hooked up yet,
[dragonfly.git] / sys / dev / acpica5 / Osd / OsdSynch.c
1 /*-
2  * Copyright (c) 2000 Michael Smith
3  * Copyright (c) 2000 BSDi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/acpica/Osd/OsdSynch.c,v 1.18 2003/09/26 21:22:10 njl Exp $
28  * $DragonFly: src/sys/dev/acpica5/Osd/OsdSynch.c,v 1.1 2004/02/21 06:48:09 dillon Exp $
29  */
30
31 /*
32  * 6.1 : Mutual Exclusion and Synchronisation
33  */
34
35 #include "acpi.h"
36
37 #include "opt_acpi.h"
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/sysctl.h>
41 #include <sys/thread.h>
42 #include <sys/thread2.h>
43
44 #define _COMPONENT      ACPI_OS_SERVICES
45 ACPI_MODULE_NAME("SYNCH")
46
47 static MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
48
49 #if defined(__DragonFly__)
50 # define AS_LOCK(as)            s = splhigh()
51 # define AS_UNLOCK(as)          splx(s)
52 # define AS_LOCK_DECL           int s
53 # define msleep(a, b, c, d, e)  tsleep(a, c, d, e)
54 #elif __FreeBSD_version < 500000
55 # define AS_LOCK(as)            s = splhigh()
56 # define AS_UNLOCK(as)          splx(s)
57 # define AS_LOCK_DECL           int s
58 # define msleep(a, b, c, d, e)  tsleep(a, c, d, e)
59 #else
60 # define AS_LOCK(as)            mtx_lock(&(as)->as_mtx)
61 # define AS_UNLOCK(as)          mtx_unlock(&(as)->as_mtx)
62 # define AS_LOCK_DECL
63 #endif
64
65 /*
66  * Simple counting semaphore implemented using a mutex. (Subsequently used
67  * in the OSI code to implement a mutex.  Go figure.)
68  */
69 struct acpi_semaphore {
70 #if __FreeBSD_version >= 500000
71     struct mtx  as_mtx;
72 #endif
73     UINT32      as_units;
74     UINT32      as_maxunits;
75     UINT32      as_pendings;
76     UINT32      as_resetting;
77     UINT32      as_timeouts;
78 };
79
80 #ifndef ACPI_NO_SEMAPHORES
81 #ifndef ACPI_SEMAPHORES_MAX_PENDING
82 #define ACPI_SEMAPHORES_MAX_PENDING     4
83 #endif
84 static int      acpi_semaphore_debug = 0;
85 TUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
86 SYSCTL_DECL(_debug_acpi);
87 SYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW,
88            &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages");
89 #endif
90
91 ACPI_STATUS
92 AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle)
93 {
94 #ifndef ACPI_NO_SEMAPHORES
95     struct acpi_semaphore       *as;
96
97     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
98
99     if (OutHandle == NULL)
100         return(AE_BAD_PARAMETER);
101     if (InitialUnits > MaxUnits)
102         return_ACPI_STATUS(AE_BAD_PARAMETER);
103
104     if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL)
105         return_ACPI_STATUS(AE_NO_MEMORY);
106
107     bzero(as, sizeof(*as));
108 #if __FreeBSD_version >= 500000
109     mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF);
110 #endif
111     as->as_units = InitialUnits;
112     as->as_maxunits = MaxUnits;
113     as->as_pendings = as->as_resetting = as->as_timeouts = 0;
114
115     ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
116         "created semaphore %p max %d, initial %d\n", 
117         as, InitialUnits, MaxUnits));
118
119     *OutHandle = (ACPI_HANDLE)as;
120     return_ACPI_STATUS(AE_OK);
121 #else
122     *OutHandle = (ACPI_HANDLE)OutHandle;
123     return(AE_OK);
124 #endif
125 }
126
127 ACPI_STATUS
128 AcpiOsDeleteSemaphore (ACPI_HANDLE Handle)
129 {
130 #ifndef ACPI_NO_SEMAPHORES
131 #if __FreeBSD_version >= 500000
132     struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
133 #endif
134
135     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
136
137     ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
138 #if __FreeBSD_version >= 500000
139     mtx_destroy(&as->as_mtx);
140 #endif
141     free(Handle, M_ACPISEM);
142     return_ACPI_STATUS(AE_OK);
143 #else
144     return(AE_OK);
145 #endif
146 }
147
148 /*
149  * This implementation has a bug, in that it has to stall for the entire
150  * timeout before it will return AE_TIME.  A better implementation would
151  * use getmicrotime() to correctly adjust the timeout after being woken up.
152  */
153 ACPI_STATUS
154 AcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout)
155 {
156 #ifndef ACPI_NO_SEMAPHORES
157     struct acpi_semaphore       *as = (struct acpi_semaphore *)Handle;
158     ACPI_STATUS                 result;
159     int                         rv, tmo;
160     struct timeval              timeouttv, currenttv, timelefttv;
161     AS_LOCK_DECL;
162
163     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
164
165     if (as == NULL)
166         return_ACPI_STATUS(AE_BAD_PARAMETER);
167
168     if (cold)
169         return_ACPI_STATUS(AE_OK);
170
171 #if 0
172     if (as->as_units < Units && as->as_timeouts > 10) {
173         printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
174         AS_LOCK(as);
175         as->as_units = as->as_maxunits;
176         if (as->as_pendings)
177             as->as_resetting = 1;
178         as->as_timeouts = 0;
179         wakeup(as);
180         AS_UNLOCK(as);
181         return_ACPI_STATUS(AE_TIME);
182     }
183
184     if (as->as_resetting) {
185         return_ACPI_STATUS(AE_TIME);
186     }
187 #endif
188
189     /* a timeout of ACPI_WAIT_FOREVER means "forever" */
190     if (Timeout == ACPI_WAIT_FOREVER) {
191         tmo = 0;
192         timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */
193         timeouttv.tv_usec = 0;
194     } else {
195         /* compute timeout using microseconds per tick */
196         tmo = (Timeout * 1000) / (1000000 / hz);
197         if (tmo <= 0)
198             tmo = 1;
199         timeouttv.tv_sec  = Timeout / 1000;
200         timeouttv.tv_usec = (Timeout % 1000) * 1000;
201     }
202
203     /* calculate timeout value in timeval */
204     getmicrotime(&currenttv);
205     timevaladd(&timeouttv, &currenttv);
206
207     AS_LOCK(as);
208     ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
209         "get %d units from semaphore %p (has %d), timeout %d\n",
210         Units, as, as->as_units, Timeout));
211     for (;;) {
212         if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) {
213             result = AE_OK;
214             break;
215         }
216         if (as->as_units >= Units) {
217             as->as_units -= Units;
218             result = AE_OK;
219             break;
220         }
221
222         /* limit number of pending treads */
223         if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
224             result = AE_TIME;
225             break;
226         }
227
228         /* if timeout values of zero is specified, return immediately */
229         if (Timeout == 0) {
230             result = AE_TIME;
231             break;
232         }
233
234 #if __FreeBSD_version >= 500000
235         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
236             "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
237             as, &as->as_mtx, PCATCH, tmo));
238 #endif
239
240         as->as_pendings++;
241
242         if (acpi_semaphore_debug) {
243             printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
244                 __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
245         }
246
247         rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
248
249         as->as_pendings--;
250
251 #if 0
252         if (as->as_resetting) {
253             /* semaphore reset, return immediately */
254             if (as->as_pendings == 0) {
255                 as->as_resetting = 0;
256             }
257             result = AE_TIME;
258             break;
259         }
260 #endif
261
262         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
263         if (rv == EWOULDBLOCK) {
264             result = AE_TIME;
265             break;
266         }
267
268         /* check if we already awaited enough */
269         timelefttv = timeouttv;
270         getmicrotime(&currenttv);
271         timevalsub(&timelefttv, &currenttv);
272         if (timelefttv.tv_sec < 0) {
273             ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as));
274             result = AE_TIME;
275             break;
276         }
277
278         /* adjust timeout for the next sleep */
279         tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz);
280         if (tmo <= 0)
281             tmo = 1;
282
283         if (acpi_semaphore_debug) {
284             printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n",
285                 __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId());
286         }
287     }
288
289     if (acpi_semaphore_debug) {
290         if (result == AE_TIME && Timeout > 0) {
291             printf("%s: Timeout %d, pending %d, semaphore %p\n",
292                 __func__, Timeout, as->as_pendings, as);
293         }
294         if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
295             printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n",
296                 __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
297         }
298     }
299
300     if (result == AE_TIME) {
301         as->as_timeouts++;
302     } else {
303         as->as_timeouts = 0;
304     }
305
306     AS_UNLOCK(as);
307
308     return_ACPI_STATUS(result);
309 #else
310     return(AE_OK);
311 #endif
312 }
313
314 ACPI_STATUS
315 AcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units)
316 {
317 #ifndef ACPI_NO_SEMAPHORES
318     struct acpi_semaphore       *as = (struct acpi_semaphore *)Handle;
319     AS_LOCK_DECL;
320
321     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
322
323     if (as == NULL)
324         return_ACPI_STATUS(AE_BAD_PARAMETER);
325
326     AS_LOCK(as);
327     ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
328         "return %d units to semaphore %p (has %d)\n",
329         Units, as, as->as_units));
330     if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) {
331         as->as_units += Units;
332         if (as->as_units > as->as_maxunits)
333             as->as_units = as->as_maxunits;
334     }
335
336     if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
337         printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
338             __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
339     }
340
341     wakeup(as);
342     AS_UNLOCK(as);
343     return_ACPI_STATUS(AE_OK);
344 #else
345     return(AE_OK);
346 #endif
347 }
348
349 ACPI_STATUS
350 AcpiOsCreateLock (ACPI_HANDLE *OutHandle)
351 {
352     lwkt_token_t tok;
353
354     if (OutHandle == NULL)
355         return (AE_BAD_PARAMETER);
356     MALLOC(tok, lwkt_token_t, sizeof(*tok), M_ACPISEM, M_WAITOK | M_ZERO);
357     if (tok == NULL)
358         return (AE_NO_MEMORY);
359
360     lwkt_inittoken(tok);
361     *OutHandle = (ACPI_HANDLE)tok;
362     return (AE_OK);
363 }
364
365 void
366 AcpiOsDeleteLock (ACPI_HANDLE Handle)
367 {
368 #if 0
369     lwkt_token_t tok = (lwkt_token_t)Handle;
370
371     if (Handle == NULL)
372         return;
373     /*mtx_destroy(m);*/
374 #endif
375 }
376
377 /*
378  * The Flags parameter seems to state whether or not caller is an ISR
379  * (and thus can't block) but since we have ithreads, we don't worry
380  * about potentially blocking.
381  */
382 void
383 AcpiOsAcquireLock (ACPI_HANDLE Handle, UINT32 Flags)
384 {
385     lwkt_token_t tok = (lwkt_token_t)Handle;
386
387     if (Handle == NULL)
388         return;
389     lwkt_gettoken(tok);
390 }
391
392 void
393 AcpiOsReleaseLock (ACPI_HANDLE Handle, UINT32 Flags)
394 {
395     lwkt_token_t tok = (lwkt_token_t)Handle;
396
397     if (Handle == NULL)
398         return;
399     lwkt_reltoken(tok);
400 }