sync with trunk r46493
[reactos.git] / drivers / bus / acpi / events / evmisc.c
1 /******************************************************************************
2 *
3 * Module Name: evmisc - ACPI device notification handler dispatch
4 * and ACPI Global Lock support
5 * $Revision: 1.1 $
6 *
7 *****************************************************************************/
8
9 /*
10 * Copyright (C) 2000, 2001 R. Byron Moore
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include <acpi.h>
28
29 #define _COMPONENT ACPI_EVENTS
30 MODULE_NAME ("evmisc")
31
32
33 /**************************************************************************
34 *
35 * FUNCTION: Acpi_ev_queue_notify_request
36 *
37 * PARAMETERS:
38 *
39 * RETURN: None.
40 *
41 * DESCRIPTION: Dispatch a device notification event to a previously
42 * installed handler.
43 *
44 *************************************************************************/
45
46 ACPI_STATUS
47 acpi_ev_queue_notify_request (
48 ACPI_NAMESPACE_NODE *node,
49 u32 notify_value)
50 {
51 ACPI_OPERAND_OBJECT *obj_desc;
52 ACPI_OPERAND_OBJECT *handler_obj = NULL;
53 ACPI_GENERIC_STATE *notify_info;
54 ACPI_STATUS status = AE_OK;
55
56
57 /*
58 * For value 1 (Ejection Request), some device method may need to be run.
59 * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need to be run.
60 * For value 0x80 (Status Change) on the power button or sleep button,
61 * initiate soft-off or sleep operation?
62 */
63
64 switch (notify_value) {
65 case 0:
66 break;
67
68 case 1:
69 break;
70
71 case 2:
72 break;
73
74 case 0x80:
75 break;
76
77 default:
78 break;
79 }
80
81
82 /*
83 * Get the notify object attached to the device Node
84 */
85
86 obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
87 if (obj_desc) {
88
89 /* We have the notify object, Get the right handler */
90
91 switch (node->type) {
92 case ACPI_TYPE_DEVICE:
93 if (notify_value <= MAX_SYS_NOTIFY) {
94 handler_obj = obj_desc->device.sys_handler;
95 }
96 else {
97 handler_obj = obj_desc->device.drv_handler;
98 }
99 break;
100
101 case ACPI_TYPE_THERMAL:
102 if (notify_value <= MAX_SYS_NOTIFY) {
103 handler_obj = obj_desc->thermal_zone.sys_handler;
104 }
105 else {
106 handler_obj = obj_desc->thermal_zone.drv_handler;
107 }
108 break;
109 }
110 }
111
112
113 /* If there is any handler to run, schedule the dispatcher */
114
115 if ((acpi_gbl_sys_notify.handler && (notify_value <= MAX_SYS_NOTIFY)) ||
116 (acpi_gbl_drv_notify.handler && (notify_value > MAX_SYS_NOTIFY)) ||
117 handler_obj) {
118
119 notify_info = acpi_cm_create_generic_state ();
120 if (!notify_info) {
121 return (AE_NO_MEMORY);
122 }
123
124 notify_info->notify.node = node;
125 notify_info->notify.value = (u16) notify_value;
126 notify_info->notify.handler_obj = handler_obj;
127
128 status = acpi_os_queue_for_execution (OSD_PRIORITY_HIGH,
129 acpi_ev_notify_dispatch, notify_info);
130 if (ACPI_FAILURE (status)) {
131 acpi_cm_delete_generic_state (notify_info);
132 }
133 }
134
135 if (!handler_obj) {
136 /* There is no per-device notify handler for this device */
137
138 }
139
140
141 return (status);
142 }
143
144
145 /**************************************************************************
146 *
147 * FUNCTION: Acpi_ev_notify_dispatch
148 *
149 * PARAMETERS:
150 *
151 * RETURN: None.
152 *
153 * DESCRIPTION: Dispatch a device notification event to a previously
154 * installed handler.
155 *
156 *************************************************************************/
157
158 void
159 acpi_ev_notify_dispatch (
160 void *context)
161 {
162 ACPI_GENERIC_STATE *notify_info = (ACPI_GENERIC_STATE *) context;
163 NOTIFY_HANDLER global_handler = NULL;
164 void *global_context = NULL;
165 ACPI_OPERAND_OBJECT *handler_obj;
166
167
168 /*
169 * We will invoke a global notify handler if installed.
170 * This is done _before_ we invoke the per-device handler attached to the device.
171 */
172
173 if (notify_info->notify.value <= MAX_SYS_NOTIFY) {
174 /* Global system notification handler */
175
176 if (acpi_gbl_sys_notify.handler) {
177 global_handler = acpi_gbl_sys_notify.handler;
178 global_context = acpi_gbl_sys_notify.context;
179 }
180 }
181
182 else {
183 /* Global driver notification handler */
184
185 if (acpi_gbl_drv_notify.handler) {
186 global_handler = acpi_gbl_drv_notify.handler;
187 global_context = acpi_gbl_drv_notify.context;
188 }
189 }
190
191
192 /* Invoke the system handler first, if present */
193
194 if (global_handler) {
195 global_handler (notify_info->notify.node, notify_info->notify.value, global_context);
196 }
197
198 /* Now invoke the per-device handler, if present */
199
200 handler_obj = notify_info->notify.handler_obj;
201 if (handler_obj) {
202 handler_obj->notify_handler.handler (notify_info->notify.node, notify_info->notify.value,
203 handler_obj->notify_handler.context);
204 }
205
206
207 /* All done with the info object */
208
209 acpi_cm_delete_generic_state (notify_info);
210 }
211
212
213 /***************************************************************************
214 *
215 * FUNCTION: Acpi_ev_global_lock_thread
216 *
217 * RETURN: None
218 *
219 * DESCRIPTION: Invoked by SCI interrupt handler upon acquisition of the
220 * Global Lock. Simply signal all threads that are waiting
221 * for the lock.
222 *
223 **************************************************************************/
224
225 static void
226 acpi_ev_global_lock_thread (
227 void *context)
228 {
229
230 /* Signal threads that are waiting for the lock */
231
232 if (acpi_gbl_global_lock_thread_count) {
233 /* Send sufficient units to the semaphore */
234
235 acpi_os_signal_semaphore (acpi_gbl_global_lock_semaphore,
236 acpi_gbl_global_lock_thread_count);
237 }
238 }
239
240
241 /***************************************************************************
242 *
243 * FUNCTION: Acpi_ev_global_lock_handler
244 *
245 * RETURN: Status
246 *
247 * DESCRIPTION: Invoked directly from the SCI handler when a global lock
248 * release interrupt occurs. Grab the global lock and queue
249 * the global lock thread for execution
250 *
251 **************************************************************************/
252
253 static u32
254 acpi_ev_global_lock_handler (
255 void *context)
256 {
257 u8 acquired = FALSE;
258 void *global_lock;
259
260
261 /*
262 * Attempt to get the lock
263 * If we don't get it now, it will be marked pending and we will
264 * take another interrupt when it becomes free.
265 */
266
267 global_lock = acpi_gbl_FACS->global_lock;
268 ACPI_ACQUIRE_GLOBAL_LOCK (global_lock, acquired);
269 if (acquired) {
270 /* Got the lock, now wake all threads waiting for it */
271
272 acpi_gbl_global_lock_acquired = TRUE;
273
274 /* Run the Global Lock thread which will signal all waiting threads */
275
276 acpi_os_queue_for_execution (OSD_PRIORITY_HIGH, acpi_ev_global_lock_thread,
277 context);
278 }
279
280 return (INTERRUPT_HANDLED);
281 }
282
283
284 /***************************************************************************
285 *
286 * FUNCTION: Acpi_ev_init_global_lock_handler
287 *
288 * RETURN: Status
289 *
290 * DESCRIPTION: Install a handler for the global lock release event
291 *
292 **************************************************************************/
293
294 ACPI_STATUS
295 acpi_ev_init_global_lock_handler (void)
296 {
297 ACPI_STATUS status;
298
299
300 acpi_gbl_global_lock_present = TRUE;
301 status = acpi_install_fixed_event_handler (ACPI_EVENT_GLOBAL,
302 acpi_ev_global_lock_handler, NULL);
303
304 /*
305 * If the global lock does not exist on this platform, the attempt
306 * to enable GBL_STS will fail (the GBL_EN bit will not stick)
307 * Map to AE_OK, but mark global lock as not present.
308 * Any attempt to actually use the global lock will be flagged
309 * with an error.
310 */
311 if (status == AE_NO_HARDWARE_RESPONSE) {
312 acpi_gbl_global_lock_present = FALSE;
313 status = AE_OK;
314 }
315
316 return (status);
317 }
318
319
320 /***************************************************************************
321 *
322 * FUNCTION: Acpi_ev_acquire_global_lock
323 *
324 * RETURN: Status
325 *
326 * DESCRIPTION: Attempt to gain ownership of the Global Lock.
327 *
328 **************************************************************************/
329
330 ACPI_STATUS
331 acpi_ev_acquire_global_lock(void)
332 {
333 ACPI_STATUS status = AE_OK;
334 u8 acquired = FALSE;
335 void *global_lock;
336
337
338 /* Make sure that we actually have a global lock */
339
340 if (!acpi_gbl_global_lock_present) {
341 return (AE_NO_GLOBAL_LOCK);
342 }
343
344 /* One more thread wants the global lock */
345
346 acpi_gbl_global_lock_thread_count++;
347
348
349 /* If we (OS side) have the hardware lock already, we are done */
350
351 if (acpi_gbl_global_lock_acquired) {
352 return (AE_OK);
353 }
354
355 /* Only if the FACS is valid */
356
357 if (!acpi_gbl_FACS) {
358 return (AE_OK);
359 }
360
361
362 /* We must acquire the actual hardware lock */
363
364 global_lock = acpi_gbl_FACS->global_lock;
365 ACPI_ACQUIRE_GLOBAL_LOCK (global_lock, acquired);
366 if (acquired) {
367 /* We got the lock */
368
369 acpi_gbl_global_lock_acquired = TRUE;
370
371 return (AE_OK);
372 }
373
374
375 /*
376 * Did not get the lock. The pending bit was set above, and we must now
377 * wait until we get the global lock released interrupt.
378 */
379
380 /*
381 * Acquire the global lock semaphore first.
382 * Since this wait will block, we must release the interpreter
383 */
384
385 status = acpi_aml_system_wait_semaphore (acpi_gbl_global_lock_semaphore,
386 ACPI_UINT32_MAX);
387
388 return (status);
389 }
390
391
392 /***************************************************************************
393 *
394 * FUNCTION: Acpi_ev_release_global_lock
395 *
396 * DESCRIPTION: Releases ownership of the Global Lock.
397 *
398 **************************************************************************/
399
400 void
401 acpi_ev_release_global_lock (void)
402 {
403 u8 pending = FALSE;
404 void *global_lock;
405
406
407 if (!acpi_gbl_global_lock_thread_count) {
408 REPORT_WARNING(("Global Lock has not be acquired, cannot release\n"));
409 return;
410 }
411
412 /* One fewer thread has the global lock */
413
414 acpi_gbl_global_lock_thread_count--;
415
416 /* Have all threads released the lock? */
417
418 if (!acpi_gbl_global_lock_thread_count) {
419 /*
420 * No more threads holding lock, we can do the actual hardware
421 * release
422 */
423
424 global_lock = acpi_gbl_FACS->global_lock;
425 ACPI_RELEASE_GLOBAL_LOCK (global_lock, pending);
426 acpi_gbl_global_lock_acquired = FALSE;
427
428 /*
429 * If the pending bit was set, we must write GBL_RLS to the control
430 * register
431 */
432 if (pending) {
433 acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK,
434 GBL_RLS, 1);
435 }
436 }
437
438 return;
439 }