Sync with trunk head (part 1 of x)
[reactos.git] / drivers / bus / acpi / events / evregion.c
1 /******************************************************************************
2 *
3 * Module Name: evregion - ACPI Address_space (Op_region) handler dispatch
4 * $Revision: 1.1 $
5 *
6 *****************************************************************************/
7
8 /*
9 * Copyright (C) 2000, 2001 R. Byron Moore
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26
27 #include <acpi.h>
28
29 #define _COMPONENT ACPI_EVENTS
30 MODULE_NAME ("evregion")
31
32
33 /**************************************************************************
34 *
35 * FUNCTION: Acpi_ev_install_default_address_space_handlers
36 *
37 * PARAMETERS:
38 *
39 * RETURN: Status
40 *
41 * DESCRIPTION: Installs the core subsystem address space handlers.
42 *
43 *************************************************************************/
44
45 ACPI_STATUS
46 acpi_ev_install_default_address_space_handlers (
47 void)
48 {
49 ACPI_STATUS status;
50
51
52 /*
53 * All address spaces (PCI Config, EC, SMBus) are scope dependent
54 * and registration must occur for a specific device. In the case
55 * system memory and IO address spaces there is currently no device
56 * associated with the address space. For these we use the root.
57 * We install the default PCI config space handler at the root so
58 * that this space is immediately available even though the we have
59 * not enumerated all the PCI Root Buses yet. This is to conform
60 * to the ACPI specification which states that the PCI config
61 * space must be always available -- even though we are nowhere
62 * near ready to find the PCI root buses at this point.
63 *
64 * NOTE: We ignore AE_EXIST because this means that a handler has
65 * already been installed (via Acpi_install_address_space_handler)
66 */
67
68 status = acpi_install_address_space_handler (acpi_gbl_root_node,
69 ADDRESS_SPACE_SYSTEM_MEMORY,
70 ACPI_DEFAULT_HANDLER, NULL, NULL);
71 if ((ACPI_FAILURE (status)) &&
72 (status != AE_EXIST)) {
73 return (status);
74 }
75
76 status = acpi_install_address_space_handler (acpi_gbl_root_node,
77 ADDRESS_SPACE_SYSTEM_IO,
78 ACPI_DEFAULT_HANDLER, NULL, NULL);
79 if ((ACPI_FAILURE (status)) &&
80 (status != AE_EXIST)) {
81 return (status);
82 }
83
84 status = acpi_install_address_space_handler (acpi_gbl_root_node,
85 ADDRESS_SPACE_PCI_CONFIG,
86 ACPI_DEFAULT_HANDLER, NULL, NULL);
87 if ((ACPI_FAILURE (status)) &&
88 (status != AE_EXIST)) {
89 return (status);
90 }
91
92
93 return (AE_OK);
94 }
95
96
97 /* TBD: [Restructure] Move elsewhere */
98
99 /**************************************************************************
100 *
101 * FUNCTION: Acpi_ev_execute_reg_method
102 *
103 * PARAMETERS: Region_obj - Object structure
104 * Function - On (1) or Off (0)
105 *
106 * RETURN: Status
107 *
108 * DESCRIPTION: Execute _REG method for a region
109 *
110 *************************************************************************/
111
112 static ACPI_STATUS
113 acpi_ev_execute_reg_method (
114 ACPI_OPERAND_OBJECT *region_obj,
115 u32 function)
116 {
117 ACPI_OPERAND_OBJECT *params[3];
118 ACPI_OPERAND_OBJECT space_id_desc;
119 ACPI_OPERAND_OBJECT function_desc;
120 ACPI_STATUS status;
121
122
123 if (region_obj->region.extra->extra.method_REG == NULL) {
124 return (AE_OK);
125 }
126
127 /*
128 * _REG method has two arguments
129 * Arg0: Integer: Operation region space ID
130 * Same value as Region_obj->Region.Space_id
131 * Arg1: Integer: connection status
132 * 1 for connecting the handler,
133 * 0 for disconnecting the handler
134 * Passed as a parameter
135 */
136
137 acpi_cm_init_static_object (&space_id_desc);
138 acpi_cm_init_static_object (&function_desc);
139
140 /*
141 * Method requires two parameters.
142 */
143 params [0] = &space_id_desc;
144 params [1] = &function_desc;
145 params [2] = NULL;
146
147 /*
148 * Set up the parameter objects
149 */
150 space_id_desc.common.type = ACPI_TYPE_INTEGER;
151 space_id_desc.integer.value = region_obj->region.space_id;
152
153 function_desc.common.type = ACPI_TYPE_INTEGER;
154 function_desc.integer.value = function;
155
156 /*
157 * Execute the method, no return value
158 */
159 status = acpi_ns_evaluate_by_handle (region_obj->region.extra->extra.method_REG, params, NULL);
160 return (status);
161 }
162
163
164 /**************************************************************************
165 *
166 * FUNCTION: Acpi_ev_address_space_dispatch
167 *
168 * PARAMETERS: Region_obj - internal region object
169 * Space_id - ID of the address space (0-255)
170 * Function - Read or Write operation
171 * Address - Where in the space to read or write
172 * Bit_width - Field width in bits (8, 16, or 32)
173 * Value - Pointer to in or out value
174 *
175 * RETURN: Status
176 *
177 * DESCRIPTION: Dispatch an address space or operation region access to
178 * a previously installed handler.
179 *
180 *************************************************************************/
181
182 ACPI_STATUS
183 acpi_ev_address_space_dispatch (
184 ACPI_OPERAND_OBJECT *region_obj,
185 u32 function,
186 ACPI_PHYSICAL_ADDRESS address,
187 u32 bit_width,
188 u32 *value)
189 {
190 ACPI_STATUS status;
191 ADDRESS_SPACE_HANDLER handler;
192 ADDRESS_SPACE_SETUP region_setup;
193 ACPI_OPERAND_OBJECT *handler_desc;
194 void *region_context = NULL;
195
196
197 /*
198 * Ensure that there is a handler associated with this region
199 */
200 handler_desc = region_obj->region.addr_handler;
201 if (!handler_desc) {
202 return(AE_NOT_EXIST);
203 }
204
205 /*
206 * It may be the case that the region has never been initialized
207 * Some types of regions require special init code
208 */
209 if (!(region_obj->region.flags & AOPOBJ_INITIALIZED)) {
210 /*
211 * This region has not been initialized yet, do it
212 */
213 region_setup = handler_desc->addr_handler.setup;
214 if (!region_setup) {
215 /*
216 * Bad news, no init routine and not init'd
217 */
218 return (AE_UNKNOWN_STATUS);
219 }
220
221 /*
222 * We must exit the interpreter because the region setup will potentially
223 * execute control methods
224 */
225 acpi_aml_exit_interpreter ();
226
227 status = region_setup (region_obj, ACPI_REGION_ACTIVATE,
228 handler_desc->addr_handler.context,
229 &region_context);
230
231 /* Re-enter the interpreter */
232
233 acpi_aml_enter_interpreter ();
234
235 /*
236 * Init routine may fail
237 */
238 if (ACPI_FAILURE (status)) {
239 return(status);
240 }
241
242 region_obj->region.flags |= AOPOBJ_INITIALIZED;
243
244 /*
245 * Save the returned context for use in all accesses to
246 * this particular region.
247 */
248 region_obj->region.extra->extra.region_context = region_context;
249 }
250
251 /*
252 * We have everything we need, begin the process
253 */
254 handler = handler_desc->addr_handler.handler;
255
256 if (!(handler_desc->addr_handler.flags & ADDR_HANDLER_DEFAULT_INSTALLED)) {
257 /*
258 * For handlers other than the default (supplied) handlers, we must
259 * exit the interpreter because the handler *might* block -- we don't
260 * know what it will do, so we can't hold the lock on the intepreter.
261 */
262 acpi_aml_exit_interpreter();
263 }
264
265 /*
266 * Invoke the handler.
267 */
268 status = handler (function, address, bit_width, value,
269 handler_desc->addr_handler.context,
270 region_obj->region.extra->extra.region_context);
271
272
273 if (!(handler_desc->addr_handler.flags & ADDR_HANDLER_DEFAULT_INSTALLED)) {
274 /* We just returned from a non-default handler, we must re-enter the
275 interpreter */
276
277 acpi_aml_enter_interpreter ();
278 }
279
280 return (status);
281 }
282
283 /******************************************************************************
284 *
285 * FUNCTION: Acpi_ev_disassociate_region_from_handler
286 *
287 * PARAMETERS: Region_obj - Region Object
288 * Acpi_ns_is_locked - Namespace Region Already Locked?
289 *
290 * RETURN: None
291 *
292 * DESCRIPTION: Break the association between the handler and the region
293 * this is a two way association.
294 *
295 ******************************************************************************/
296
297 void
298 acpi_ev_disassociate_region_from_handler(
299 ACPI_OPERAND_OBJECT *region_obj,
300 u8 acpi_ns_is_locked)
301 {
302 ACPI_OPERAND_OBJECT *handler_obj;
303 ACPI_OPERAND_OBJECT *obj_desc;
304 ACPI_OPERAND_OBJECT **last_obj_ptr;
305 ADDRESS_SPACE_SETUP region_setup;
306 void *region_context;
307 ACPI_STATUS status;
308
309
310 region_context = region_obj->region.extra->extra.region_context;
311
312 /*
313 * Get the address handler from the region object
314 */
315
316 handler_obj = region_obj->region.addr_handler;
317 if (!handler_obj) {
318 /*
319 * This region has no handler, all done
320 */
321 return;
322 }
323
324
325 /*
326 * Find this region in the handler's list
327 */
328
329 obj_desc = handler_obj->addr_handler.region_list;
330 last_obj_ptr = &handler_obj->addr_handler.region_list;
331
332 while (obj_desc) {
333 /*
334 * See if this is the one
335 */
336 if (obj_desc == region_obj) {
337 /*
338 * This is it, remove it from the handler's list
339 */
340 *last_obj_ptr = obj_desc->region.next;
341 obj_desc->region.next = NULL; /* Must clear field */
342
343 if (acpi_ns_is_locked) {
344 acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
345 }
346
347 /*
348 * Now stop region accesses by executing the _REG method
349 */
350 acpi_ev_execute_reg_method (region_obj, 0);
351
352 if (acpi_ns_is_locked) {
353 acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
354 }
355
356 /*
357 * Call the setup handler with the deactivate notification
358 */
359 region_setup = handler_obj->addr_handler.setup;
360 status = region_setup (region_obj, ACPI_REGION_DEACTIVATE,
361 handler_obj->addr_handler.context,
362 &region_context);
363
364 /*
365 * Init routine may fail, Just ignore errors
366 */
367
368 region_obj->region.flags &= ~(AOPOBJ_INITIALIZED);
369
370 /*
371 * Remove handler reference in the region
372 *
373 * NOTE: this doesn't mean that the region goes away
374 * The region is just inaccessible as indicated to
375 * the _REG method
376 *
377 * If the region is on the handler's list
378 * this better be the region's handler
379 */
380 ACPI_ASSERT (region_obj->region.addr_handler == handler_obj);
381
382 region_obj->region.addr_handler = NULL;
383
384 return;
385
386 } /* found the right handler */
387
388 /*
389 * Move through the linked list of handlers
390 */
391 last_obj_ptr = &obj_desc->region.next;
392 obj_desc = obj_desc->region.next;
393 }
394
395 /*
396 * If we get here, the region was not in the handler's region list
397 */
398 return;
399 }
400
401
402 /******************************************************************************
403 *
404 * FUNCTION: Acpi_ev_associate_region_and_handler
405 *
406 * PARAMETERS: Handler_obj - Handler Object
407 * Region_obj - Region Object
408 * Acpi_ns_is_locked - Namespace Region Already Locked?
409 *
410 * RETURN: None
411 *
412 * DESCRIPTION: Create the association between the handler and the region
413 * this is a two way association.
414 *
415 ******************************************************************************/
416
417 ACPI_STATUS
418 acpi_ev_associate_region_and_handler (
419 ACPI_OPERAND_OBJECT *handler_obj,
420 ACPI_OPERAND_OBJECT *region_obj,
421 u8 acpi_ns_is_locked)
422 {
423 ACPI_STATUS status;
424
425
426 ACPI_ASSERT (region_obj->region.space_id == handler_obj->addr_handler.space_id);
427 ACPI_ASSERT (region_obj->region.addr_handler == 0);
428
429 /*
430 * Link this region to the front of the handler's list
431 */
432
433 region_obj->region.next = handler_obj->addr_handler.region_list;
434 handler_obj->addr_handler.region_list = region_obj;
435
436 /*
437 * set the region's handler
438 */
439
440 /*
441 Handler_obj->Common.Reference_count =
442 (u16) (Handler_obj->Common.Reference_count +
443 Region_obj->Common.Reference_count - 1);
444 */
445 region_obj->region.addr_handler = handler_obj;
446
447 /*
448 * Last thing, tell all users that this region is usable
449 */
450 if (acpi_ns_is_locked) {
451 acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
452 }
453
454 status = acpi_ev_execute_reg_method (region_obj, 1);
455
456 if (acpi_ns_is_locked) {
457 acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
458 }
459
460 return (status);
461 }
462
463
464 /****************************************************************************
465 *
466 * FUNCTION: Acpi_ev_addr_handler_helper
467 *
468 * PARAMETERS: Handle - Node to be dumped
469 * Level - Nesting level of the handle
470 * Context - Passed into Acpi_ns_walk_namespace
471 *
472 * DESCRIPTION: This routine checks to see if the object is a Region if it
473 * is then the address handler is installed in it.
474 *
475 * If the Object is a Device, and the device has a handler of
476 * the same type then the search is terminated in that branch.
477 *
478 * This is because the existing handler is closer in proximity
479 * to any more regions than the one we are trying to install.
480 *
481 ***************************************************************************/
482
483 ACPI_STATUS
484 acpi_ev_addr_handler_helper (
485 ACPI_HANDLE obj_handle,
486 u32 level,
487 void *context,
488 void **return_value)
489 {
490 ACPI_OPERAND_OBJECT *handler_obj;
491 ACPI_OPERAND_OBJECT *tmp_obj;
492 ACPI_OPERAND_OBJECT *obj_desc;
493 ACPI_NAMESPACE_NODE *node;
494 ACPI_STATUS status;
495
496
497 handler_obj = (ACPI_OPERAND_OBJECT *) context;
498
499 /* Parameter validation */
500
501 if (!handler_obj) {
502 return (AE_OK);
503 }
504
505 /* Convert and validate the device handle */
506
507 node = acpi_ns_convert_handle_to_entry (obj_handle);
508 if (!node) {
509 return (AE_BAD_PARAMETER);
510 }
511
512 /*
513 * We only care about regions.and objects
514 * that can have address handlers
515 */
516
517 if ((node->type != ACPI_TYPE_DEVICE) &&
518 (node->type != ACPI_TYPE_REGION) &&
519 (node != acpi_gbl_root_node)) {
520 return (AE_OK);
521 }
522
523 /* Check for an existing internal object */
524
525 obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
526 if (!obj_desc) {
527 /*
528 * The object DNE, we don't care about it
529 */
530 return (AE_OK);
531 }
532
533 /*
534 * Devices are handled different than regions
535 */
536 if (IS_THIS_OBJECT_TYPE (obj_desc, ACPI_TYPE_DEVICE)) {
537 /*
538 * See if this guy has any handlers
539 */
540 tmp_obj = obj_desc->device.addr_handler;
541 while (tmp_obj) {
542 /*
543 * Now let's see if it's for the same address space.
544 */
545 if (tmp_obj->addr_handler.space_id == handler_obj->addr_handler.space_id) {
546 /*
547 * It's for the same address space
548 */
549 /*
550 * Since the object we found it on was a device, then it
551 * means that someone has already installed a handler for
552 * the branch of the namespace from this device on. Just
553 * bail out telling the walk routine to not traverse this
554 * branch. This preserves the scoping rule for handlers.
555 */
556 return (AE_CTRL_DEPTH);
557 }
558
559 /*
560 * Move through the linked list of handlers
561 */
562 tmp_obj = tmp_obj->addr_handler.next;
563 }
564
565 /*
566 * As long as the device didn't have a handler for this
567 * space we don't care about it. We just ignore it and
568 * proceed.
569 */
570 return (AE_OK);
571 }
572
573 /*
574 * Only here if it was a region
575 */
576 ACPI_ASSERT (obj_desc->common.type == ACPI_TYPE_REGION);
577
578 if (obj_desc->region.space_id != handler_obj->addr_handler.space_id) {
579 /*
580 * This region is for a different address space
581 * ignore it
582 */
583 return (AE_OK);
584 }
585
586 /*
587 * Now we have a region and it is for the handler's address
588 * space type.
589 *
590 * First disconnect region for any previous handler (if any)
591 */
592 acpi_ev_disassociate_region_from_handler (obj_desc, FALSE);
593
594 /*
595 * Then connect the region to the new handler
596 */
597 status = acpi_ev_associate_region_and_handler (handler_obj, obj_desc, FALSE);
598
599 return (status);
600 }
601
602