- Make ACPI use PCH. Cuts down compile time to 9 seconds on gcc.
[reactos.git] / reactos / drivers / bus / acpi / ospm / busmgr / bmpower.c
1 /****************************************************************************
2 *
3 * Module Name: bmpower.c - Driver for ACPI Power Resource 'devices'
4 * $Revision: 1.1 $
5 *
6 ****************************************************************************/
7
8 /*
9 * Copyright (C) 2000, 2001 Andrew Grover
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 * TODO:
28 * -----
29 * 1. Sequencing of power resource list transitions.
30 * 2. Global serialization of power resource transtions (see ACPI
31 * spec section 7.1.2/7.1.3).
32 * 3. Better error handling.
33 */
34
35
36 #include <acpi.h>
37
38 #define _COMPONENT ACPI_POWER_CONTROL
39 MODULE_NAME ("bmpower")
40
41
42 /****************************************************************************
43 * Function Prototypes
44 ****************************************************************************/
45
46 ACPI_STATUS
47 bm_pr_notify (
48 BM_NOTIFY notify_type,
49 BM_HANDLE device_handle,
50 void **context);
51
52 ACPI_STATUS
53 bm_pr_request (
54 BM_REQUEST *request,
55 void *context);
56
57
58 /****************************************************************************
59 * Internal Functions
60 ****************************************************************************/
61
62 /****************************************************************************
63 *
64 * FUNCTION: bm_pr_print
65 *
66 * PARAMETERS: <TBD>
67 *
68 * RETURN: <TBD>
69 *
70 * DESCRIPTION: <TBD>
71 *
72 ****************************************************************************/
73
74 ACPI_STATUS
75 bm_pr_print (
76 BM_POWER_RESOURCE *pr)
77 {
78 ACPI_BUFFER buffer;
79
80 if (!pr) {
81 return(AE_BAD_PARAMETER);
82 }
83
84 buffer.length = 256;
85 buffer.pointer = acpi_os_callocate(buffer.length);
86 if (!buffer.pointer) {
87 return(AE_NO_MEMORY);
88 }
89
90 acpi_get_name(pr->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
91
92 acpi_os_printf("Power Resource: found\n");
93
94 DEBUG_PRINT(ACPI_INFO, ("+------------------------------------------------------------\n"));
95 DEBUG_PRINT(ACPI_INFO, ("PowerResource[0x%02X]|[0x%08X] %s\n", pr->device_handle, pr->acpi_handle, buffer.pointer));
96 DEBUG_PRINT(ACPI_INFO, (" system_level[S%d] resource_order[%d]\n", pr->system_level, pr->resource_order));
97 DEBUG_PRINT(ACPI_INFO, (" state[D%d] reference_count[%d]\n", pr->state, pr->reference_count));
98 DEBUG_PRINT(ACPI_INFO, ("+------------------------------------------------------------\n"));
99
100 acpi_os_free(buffer.pointer);
101
102 return(AE_OK);
103 }
104
105
106 /****************************************************************************
107 *
108 * FUNCTION: bm_pr_get_state
109 *
110 * PARAMETERS: <TBD>
111 *
112 * RETURN: <TBD>
113 *
114 * DESCRIPTION: <TBD>
115 *
116 ****************************************************************************/
117
118 ACPI_STATUS
119 bm_pr_get_state (
120 BM_POWER_RESOURCE *pr)
121 {
122 ACPI_STATUS status = AE_OK;
123 BM_DEVICE_STATUS device_status = BM_STATUS_UNKNOWN;
124
125 FUNCTION_TRACE("bm_pr_get_state");
126
127 if (!pr) {
128 return_ACPI_STATUS(AE_BAD_PARAMETER);
129 }
130
131 pr->state = ACPI_STATE_UNKNOWN;
132
133 /*
134 * Evaluate _STA:
135 * --------------
136 * Evalute _STA to determine whether the power resource is ON or OFF.
137 * Note that if the power resource isn't present we'll get AE_OK but
138 * an unknown status.
139 */
140 status = bm_get_device_status(pr->device_handle, &device_status);
141 if (ACPI_FAILURE(status)) {
142 DEBUG_PRINT(ACPI_ERROR, ("Error reading status for power resource [0x%02x].\n", pr->device_handle));
143 return_ACPI_STATUS(status);
144 }
145 if (device_status == BM_STATUS_UNKNOWN) {
146 DEBUG_PRINT(ACPI_ERROR, ("Error reading status for power resource [0x%02x].\n", pr->device_handle));
147 return_ACPI_STATUS(AE_NOT_EXIST);
148 }
149
150 /*
151 * Mask off all bits but the first as some systems return non-standard
152 * values (e.g. 0x51).
153 */
154 switch (device_status & 0x01) {
155 case 0:
156 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02x] is OFF.\n", pr->device_handle));
157 pr->state = ACPI_STATE_D3;
158 break;
159 case 1:
160 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02x] is ON.\n", pr->device_handle));
161 pr->state = ACPI_STATE_D0;
162 break;
163 }
164
165 return_ACPI_STATUS(status);
166 }
167
168
169 /****************************************************************************
170 *
171 * FUNCTION: bm_pr_set_state
172 *
173 * PARAMETERS: <TBD>
174 *
175 * RETURN: <TBD>
176 *
177 * DESCRIPTION: <TBD>
178 *
179 ****************************************************************************/
180
181 ACPI_STATUS
182 bm_pr_set_state (
183 BM_POWER_RESOURCE *pr,
184 BM_POWER_STATE target_state)
185 {
186 ACPI_STATUS status = AE_OK;
187
188 FUNCTION_TRACE("bm_pr_set_state");
189
190 if (!pr) {
191 return_ACPI_STATUS(AE_BAD_PARAMETER);
192 }
193
194 status = bm_pr_get_state(pr);
195 if (ACPI_FAILURE(status)) {
196 return_ACPI_STATUS(status);
197 }
198
199 if (target_state == pr->state) {
200 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02X] already at target power state [D%d].\n", pr->device_handle, pr->state));
201 return_ACPI_STATUS(AE_OK);
202 }
203
204 switch (target_state) {
205
206 case ACPI_STATE_D0:
207 DEBUG_PRINT(ACPI_INFO, ("Turning power resource [0x%02X] ON.\n", pr->device_handle));
208 status = bm_evaluate_object(pr->acpi_handle, "_ON", NULL, NULL);
209 break;
210
211 case ACPI_STATE_D3:
212 DEBUG_PRINT(ACPI_INFO, ("Turning power resource [0x%02X] OFF.\n", pr->device_handle));
213 status = bm_evaluate_object(pr->acpi_handle, "_OFF", NULL, NULL);
214 break;
215
216 default:
217 status = AE_BAD_PARAMETER;
218 break;
219 }
220
221 status = bm_pr_get_state(pr);
222 if (ACPI_FAILURE(status)) {
223 return_ACPI_STATUS(status);
224 }
225
226 return_ACPI_STATUS(status);
227 }
228
229
230 /****************************************************************************
231 *
232 * FUNCTION: bm_pr_list_get_state
233 *
234 * PARAMETERS: <TBD>
235 *
236 * RETURN: <TBD>
237 *
238 * DESCRIPTION: <TBD>
239 *
240 ****************************************************************************/
241
242 ACPI_STATUS
243 bm_pr_list_get_state (
244 BM_HANDLE_LIST *pr_list,
245 BM_POWER_STATE *power_state)
246 {
247 ACPI_STATUS status = AE_OK;
248 BM_POWER_RESOURCE *pr = NULL;
249 u32 i = 0;
250
251 FUNCTION_TRACE("bm_pr_list_get_state");
252
253 if (!pr_list || !power_state) {
254 return_ACPI_STATUS(AE_BAD_PARAMETER);
255 }
256
257 if (pr_list->count < 1) {
258 pr->state = ACPI_STATE_UNKNOWN;
259 return_ACPI_STATUS(AE_ERROR);
260 }
261
262 (*power_state) = ACPI_STATE_D0;
263
264 /*
265 * Calculate Current power_state:
266 * -----------------------------
267 * The current state of a list of power resources is ON if all
268 * power resources are currently in the ON state. In other words,
269 * if any power resource in the list is OFF then the collection
270 * isn't fully ON.
271 */
272 for (i = 0; i < pr_list->count; i++) {
273
274 status = bm_get_device_context(pr_list->handles[i],
275 (BM_DRIVER_CONTEXT*)(&pr));
276 if (ACPI_FAILURE(status)) {
277 DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", pr_list->handles[i]));
278 (*power_state) = ACPI_STATE_UNKNOWN;
279 break;
280 }
281
282 status = bm_pr_get_state(pr);
283 if (ACPI_FAILURE(status)) {
284 (*power_state) = ACPI_STATE_UNKNOWN;
285 break;
286 }
287
288 if (pr->state != ACPI_STATE_D0) {
289 (*power_state) = pr->state;
290 break;
291 }
292 }
293
294 return_ACPI_STATUS(status);
295 }
296
297
298 /****************************************************************************
299 *
300 * FUNCTION: bm_pr_list_transition
301 *
302 * PARAMETERS: <TBD>
303 *
304 * RETURN: <TBD>
305 *
306 * DESCRIPTION: <TBD>
307 *
308 ****************************************************************************/
309
310 ACPI_STATUS
311 bm_pr_list_transition (
312 BM_HANDLE_LIST *current_list,
313 BM_HANDLE_LIST *target_list)
314 {
315 ACPI_STATUS status = AE_OK;
316 BM_POWER_RESOURCE *pr = NULL;
317 u32 i = 0;
318
319 FUNCTION_TRACE("bm_pr_list_transition");
320
321 if (!current_list || !target_list) {
322 return_ACPI_STATUS(AE_BAD_PARAMETER);
323 }
324
325 /*
326 * Reference Target:
327 * -----------------
328 * Reference all resources for the target power state first (so
329 * the device doesn't get turned off while transitioning). Power
330 * resources that aren't on (new reference count of 1) are turned on.
331 */
332 for (i = 0; i < target_list->count; i++) {
333
334 status = bm_get_device_context(target_list->handles[i],
335 (BM_DRIVER_CONTEXT*)(&pr));
336 if (ACPI_FAILURE(status)) {
337 DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", target_list->handles[i]));
338 continue;
339 }
340
341 if (++pr->reference_count == 1) {
342 /* TODO: Need ordering based upon resource_order */
343 status = bm_pr_set_state(pr, ACPI_STATE_D0);
344 if (ACPI_FAILURE(status)) {
345 /* TODO: How do we handle this? */
346 DEBUG_PRINT(ACPI_WARN, ("Unable to change power state for power resource [0x%02X].\n", target_list->handles[i]));
347 }
348 }
349 }
350
351 /*
352 * Dereference Current:
353 * --------------------
354 * Dereference all resources for the current power state. Power
355 * resources no longer referenced (new reference count of 0) are
356 * turned off.
357 */
358 for (i = 0; i < current_list->count; i++) {
359
360 status = bm_get_device_context(current_list->handles[i],
361 (BM_DRIVER_CONTEXT*)(&pr));
362 if (ACPI_FAILURE(status)) {
363 DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", target_list->handles[i]));
364 continue;
365 }
366
367 if (--pr->reference_count == 0) {
368 /* TODO: Need ordering based upon resource_order */
369 status = bm_pr_set_state(pr, ACPI_STATE_D3);
370 if (ACPI_FAILURE(status)) {
371 /* TODO: How do we handle this? */
372 DEBUG_PRINT(ACPI_ERROR, ("Unable to change power state for power resource [0x%02X].\n", current_list->handles[i]));
373 }
374 }
375 }
376
377 return_ACPI_STATUS(status);
378 }
379
380
381 /****************************************************************************
382 *
383 * FUNCTION: bm_pr_add_device
384 *
385 * PARAMETERS: <TBD>
386 *
387 * RETURN: <TBD>
388 *
389 * DESCRIPTION: <TBD>
390 *
391 ****************************************************************************/
392
393 ACPI_STATUS
394 bm_pr_add_device (
395 BM_HANDLE device_handle,
396 void **context)
397 {
398 ACPI_STATUS status = AE_OK;
399 BM_POWER_RESOURCE *pr = NULL;
400 BM_DEVICE *device = NULL;
401 ACPI_BUFFER buffer;
402 ACPI_OBJECT acpi_object;
403
404 FUNCTION_TRACE("bm_pr_add_device");
405
406 DEBUG_PRINT(ACPI_INFO, ("Adding power resource [0x%02X].\n", device_handle));
407
408 if (!context || *context) {
409 return_ACPI_STATUS(AE_BAD_PARAMETER);
410 }
411
412 buffer.length = sizeof(ACPI_OBJECT);
413 buffer.pointer = &acpi_object;
414
415 /*
416 * Get information on this device.
417 */
418 status = bm_get_device_info(device_handle, &device);
419 if (ACPI_FAILURE(status)) {
420 return_ACPI_STATUS(status);
421 }
422
423 /*
424 * Allocate a new BM_POWER_RESOURCE structure.
425 */
426 pr = acpi_os_callocate(sizeof(BM_POWER_RESOURCE));
427 if (!pr) {
428 return_ACPI_STATUS(AE_NO_MEMORY);
429 }
430
431 pr->device_handle = device->handle;
432 pr->acpi_handle = device->acpi_handle;
433
434 /*
435 * Get information on this power resource.
436 */
437 status = acpi_evaluate_object(pr->acpi_handle, NULL, NULL, &buffer);
438 if (ACPI_FAILURE(status)) {
439 goto end;
440 }
441
442 pr->system_level = acpi_object.power_resource.system_level;
443 pr->resource_order = acpi_object.power_resource.resource_order;
444 pr->state = ACPI_STATE_UNKNOWN;
445 pr->reference_count = 0;
446
447 /*
448 * Get the power resource's current state (ON|OFF).
449 */
450 status = bm_pr_get_state(pr);
451
452 end:
453 if (ACPI_FAILURE(status)) {
454 acpi_os_free(pr);
455 }
456 else {
457 *context = pr;
458 bm_pr_print(pr);
459 }
460
461 return_ACPI_STATUS(status);
462 }
463
464
465 /****************************************************************************
466 *
467 * FUNCTION: bm_pr_remove_device
468 *
469 * PARAMETERS: <TBD>
470 *
471 * RETURN: <TBD>
472 *
473 * DESCRIPTION: <TBD>
474 *
475 ****************************************************************************/
476
477 ACPI_STATUS
478 bm_pr_remove_device (
479 void **context)
480 {
481 ACPI_STATUS status = AE_OK;
482 BM_POWER_RESOURCE *pr = NULL;
483
484 FUNCTION_TRACE("bm_pr_remove_device");
485
486 if (!context || !*context) {
487 return_ACPI_STATUS(AE_BAD_PARAMETER);
488 }
489
490 pr = (BM_POWER_RESOURCE*)*context;
491
492 DEBUG_PRINT(ACPI_INFO, ("Removing power resource [0x%02X].\n", pr->device_handle));
493
494 acpi_os_free(pr);
495
496 return_ACPI_STATUS(status);
497 }
498
499
500 /****************************************************************************
501 * External Functions
502 ****************************************************************************/
503
504 /****************************************************************************
505 *
506 * FUNCTION: bm_pr_initialize
507 *
508 * PARAMETERS: <none>
509 *
510 * RETURN:
511 *
512 * DESCRIPTION: <TBD>
513 *
514 ****************************************************************************/
515
516 ACPI_STATUS
517 bm_pr_initialize (void)
518 {
519 ACPI_STATUS status = AE_OK;
520 BM_DEVICE_ID criteria;
521 BM_DRIVER driver;
522
523 FUNCTION_TRACE("bm_pr_initialize");
524
525 MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
526 MEMSET(&driver, 0, sizeof(BM_DRIVER));
527
528 criteria.type = BM_TYPE_POWER_RESOURCE;
529
530 driver.notify = &bm_pr_notify;
531 driver.request = &bm_pr_request;
532
533 status = bm_register_driver(&criteria, &driver);
534
535 return_ACPI_STATUS(status);
536 }
537
538
539 /****************************************************************************
540 *
541 * FUNCTION: bm_pr_terminate
542 *
543 * PARAMETERS: <TBD>
544 *
545 * RETURN: <TBD>
546 *
547 * DESCRIPTION: <TBD>
548 *
549 ****************************************************************************/
550
551 ACPI_STATUS
552 bm_pr_terminate (void)
553 {
554 ACPI_STATUS status = AE_OK;
555 BM_DEVICE_ID criteria;
556 BM_DRIVER driver;
557
558 FUNCTION_TRACE("bm_pr_terminate");
559
560 MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
561 MEMSET(&driver, 0, sizeof(BM_DRIVER));
562
563 criteria.type = BM_TYPE_POWER_RESOURCE;
564
565 driver.notify = &bm_pr_notify;
566 driver.request = &bm_pr_request;
567
568 status = bm_unregister_driver(&criteria, &driver);
569
570 return_ACPI_STATUS(status);
571 }
572
573
574 /****************************************************************************
575 *
576 * FUNCTION: bm_pr_notify
577 *
578 * PARAMETERS: <TBD>
579 *
580 * RETURN: <TBD>
581 *
582 * DESCRIPTION: <TBD>
583 *
584 ****************************************************************************/
585
586 ACPI_STATUS
587 bm_pr_notify (
588 BM_NOTIFY notify_type,
589 BM_HANDLE device_handle,
590 void **context)
591 {
592 ACPI_STATUS status = AE_OK;
593
594 FUNCTION_TRACE("bm_pr_notify");
595
596 switch (notify_type) {
597
598 case BM_NOTIFY_DEVICE_ADDED:
599 status = bm_pr_add_device(device_handle, context);
600 break;
601
602 case BM_NOTIFY_DEVICE_REMOVED:
603 status = bm_pr_remove_device(context);
604 break;
605
606 default:
607 status = AE_SUPPORT;
608 break;
609 }
610
611 return_ACPI_STATUS(status);
612 }
613
614
615 /****************************************************************************
616 *
617 * FUNCTION: bm_pr_request
618 *
619 * PARAMETERS: <TBD>
620 *
621 * RETURN: <TBD>
622 *
623 * DESCRIPTION: <TBD>
624 *
625 ****************************************************************************/
626
627 ACPI_STATUS
628 bm_pr_request (
629 BM_REQUEST *request,
630 void *context)
631 {
632 ACPI_STATUS status = AE_OK;
633 BM_POWER_RESOURCE *pr = NULL;
634
635 FUNCTION_TRACE("bm_pr_request");
636
637 /*
638 * Must have a valid request structure and context.
639 */
640 if (!request || !context) {
641 return_ACPI_STATUS(AE_BAD_PARAMETER);
642 }
643
644 /*
645 * context contains information specific to this power resource.
646 */
647 pr = (BM_POWER_RESOURCE*)context;
648
649 /*
650 * Handle request:
651 * ---------------
652 */
653 switch (request->command) {
654
655 default:
656 status = AE_SUPPORT;
657 break;
658 }
659
660 request->status = status;
661
662 return_ACPI_STATUS(status);
663 }
664
665
666