1 /****************************************************************************
3 * Module Name: bmpower.c - Driver for ACPI Power Resource 'devices'
6 ****************************************************************************/
9 * Copyright (C) 2000, 2001 Andrew Grover
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.
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.
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
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.
38 #define _COMPONENT ACPI_POWER_CONTROL
39 MODULE_NAME ("bmpower")
42 /****************************************************************************
44 ****************************************************************************/
48 BM_NOTIFY notify_type
,
49 BM_HANDLE device_handle
,
58 /****************************************************************************
60 ****************************************************************************/
62 /****************************************************************************
64 * FUNCTION: bm_pr_print
72 ****************************************************************************/
76 BM_POWER_RESOURCE
*pr
)
81 return(AE_BAD_PARAMETER
);
85 buffer
.pointer
= acpi_os_callocate(buffer
.length
);
86 if (!buffer
.pointer
) {
90 acpi_get_name(pr
->acpi_handle
, ACPI_FULL_PATHNAME
, &buffer
);
92 acpi_os_printf("Power Resource: found\n");
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"));
100 acpi_os_free(buffer
.pointer
);
106 /****************************************************************************
108 * FUNCTION: bm_pr_get_state
116 ****************************************************************************/
120 BM_POWER_RESOURCE
*pr
)
122 ACPI_STATUS status
= AE_OK
;
123 BM_DEVICE_STATUS device_status
= BM_STATUS_UNKNOWN
;
125 FUNCTION_TRACE("bm_pr_get_state");
128 return_ACPI_STATUS(AE_BAD_PARAMETER
);
131 pr
->state
= ACPI_STATE_UNKNOWN
;
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
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
);
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
);
151 * Mask off all bits but the first as some systems return non-standard
152 * values (e.g. 0x51).
154 switch (device_status
& 0x01) {
156 DEBUG_PRINT(ACPI_INFO
, ("Power resource [0x%02x] is OFF.\n", pr
->device_handle
));
157 pr
->state
= ACPI_STATE_D3
;
160 DEBUG_PRINT(ACPI_INFO
, ("Power resource [0x%02x] is ON.\n", pr
->device_handle
));
161 pr
->state
= ACPI_STATE_D0
;
165 return_ACPI_STATUS(status
);
169 /****************************************************************************
171 * FUNCTION: bm_pr_set_state
179 ****************************************************************************/
183 BM_POWER_RESOURCE
*pr
,
184 BM_POWER_STATE target_state
)
186 ACPI_STATUS status
= AE_OK
;
188 FUNCTION_TRACE("bm_pr_set_state");
191 return_ACPI_STATUS(AE_BAD_PARAMETER
);
194 status
= bm_pr_get_state(pr
);
195 if (ACPI_FAILURE(status
)) {
196 return_ACPI_STATUS(status
);
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
);
204 switch (target_state
) {
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
);
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
);
217 status
= AE_BAD_PARAMETER
;
221 status
= bm_pr_get_state(pr
);
222 if (ACPI_FAILURE(status
)) {
223 return_ACPI_STATUS(status
);
226 return_ACPI_STATUS(status
);
230 /****************************************************************************
232 * FUNCTION: bm_pr_list_get_state
240 ****************************************************************************/
243 bm_pr_list_get_state (
244 BM_HANDLE_LIST
*pr_list
,
245 BM_POWER_STATE
*power_state
)
247 ACPI_STATUS status
= AE_OK
;
248 BM_POWER_RESOURCE
*pr
= NULL
;
251 FUNCTION_TRACE("bm_pr_list_get_state");
253 if (!pr_list
|| !power_state
) {
254 return_ACPI_STATUS(AE_BAD_PARAMETER
);
257 if (pr_list
->count
< 1) {
258 pr
->state
= ACPI_STATE_UNKNOWN
;
259 return_ACPI_STATUS(AE_ERROR
);
262 (*power_state
) = ACPI_STATE_D0
;
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
272 for (i
= 0; i
< pr_list
->count
; i
++) {
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
;
282 status
= bm_pr_get_state(pr
);
283 if (ACPI_FAILURE(status
)) {
284 (*power_state
) = ACPI_STATE_UNKNOWN
;
288 if (pr
->state
!= ACPI_STATE_D0
) {
289 (*power_state
) = pr
->state
;
294 return_ACPI_STATUS(status
);
298 /****************************************************************************
300 * FUNCTION: bm_pr_list_transition
308 ****************************************************************************/
311 bm_pr_list_transition (
312 BM_HANDLE_LIST
*current_list
,
313 BM_HANDLE_LIST
*target_list
)
315 ACPI_STATUS status
= AE_OK
;
316 BM_POWER_RESOURCE
*pr
= NULL
;
319 FUNCTION_TRACE("bm_pr_list_transition");
321 if (!current_list
|| !target_list
) {
322 return_ACPI_STATUS(AE_BAD_PARAMETER
);
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.
332 for (i
= 0; i
< target_list
->count
; i
++) {
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
]));
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
]));
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
358 for (i
= 0; i
< current_list
->count
; i
++) {
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
]));
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
]));
377 return_ACPI_STATUS(status
);
381 /****************************************************************************
383 * FUNCTION: bm_pr_add_device
391 ****************************************************************************/
395 BM_HANDLE device_handle
,
398 ACPI_STATUS status
= AE_OK
;
399 BM_POWER_RESOURCE
*pr
= NULL
;
400 BM_DEVICE
*device
= NULL
;
402 ACPI_OBJECT acpi_object
;
404 FUNCTION_TRACE("bm_pr_add_device");
406 DEBUG_PRINT(ACPI_INFO
, ("Adding power resource [0x%02X].\n", device_handle
));
408 if (!context
|| *context
) {
409 return_ACPI_STATUS(AE_BAD_PARAMETER
);
412 buffer
.length
= sizeof(ACPI_OBJECT
);
413 buffer
.pointer
= &acpi_object
;
416 * Get information on this device.
418 status
= bm_get_device_info(device_handle
, &device
);
419 if (ACPI_FAILURE(status
)) {
420 return_ACPI_STATUS(status
);
424 * Allocate a new BM_POWER_RESOURCE structure.
426 pr
= acpi_os_callocate(sizeof(BM_POWER_RESOURCE
));
428 return_ACPI_STATUS(AE_NO_MEMORY
);
431 pr
->device_handle
= device
->handle
;
432 pr
->acpi_handle
= device
->acpi_handle
;
435 * Get information on this power resource.
437 status
= acpi_evaluate_object(pr
->acpi_handle
, NULL
, NULL
, &buffer
);
438 if (ACPI_FAILURE(status
)) {
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;
448 * Get the power resource's current state (ON|OFF).
450 status
= bm_pr_get_state(pr
);
453 if (ACPI_FAILURE(status
)) {
461 return_ACPI_STATUS(status
);
465 /****************************************************************************
467 * FUNCTION: bm_pr_remove_device
475 ****************************************************************************/
478 bm_pr_remove_device (
481 ACPI_STATUS status
= AE_OK
;
482 BM_POWER_RESOURCE
*pr
= NULL
;
484 FUNCTION_TRACE("bm_pr_remove_device");
486 if (!context
|| !*context
) {
487 return_ACPI_STATUS(AE_BAD_PARAMETER
);
490 pr
= (BM_POWER_RESOURCE
*)*context
;
492 DEBUG_PRINT(ACPI_INFO
, ("Removing power resource [0x%02X].\n", pr
->device_handle
));
496 return_ACPI_STATUS(status
);
500 /****************************************************************************
502 ****************************************************************************/
504 /****************************************************************************
506 * FUNCTION: bm_pr_initialize
514 ****************************************************************************/
517 bm_pr_initialize (void)
519 ACPI_STATUS status
= AE_OK
;
520 BM_DEVICE_ID criteria
;
523 FUNCTION_TRACE("bm_pr_initialize");
525 MEMSET(&criteria
, 0, sizeof(BM_DEVICE_ID
));
526 MEMSET(&driver
, 0, sizeof(BM_DRIVER
));
528 criteria
.type
= BM_TYPE_POWER_RESOURCE
;
530 driver
.notify
= &bm_pr_notify
;
531 driver
.request
= &bm_pr_request
;
533 status
= bm_register_driver(&criteria
, &driver
);
535 return_ACPI_STATUS(status
);
539 /****************************************************************************
541 * FUNCTION: bm_pr_terminate
549 ****************************************************************************/
552 bm_pr_terminate (void)
554 ACPI_STATUS status
= AE_OK
;
555 BM_DEVICE_ID criteria
;
558 FUNCTION_TRACE("bm_pr_terminate");
560 MEMSET(&criteria
, 0, sizeof(BM_DEVICE_ID
));
561 MEMSET(&driver
, 0, sizeof(BM_DRIVER
));
563 criteria
.type
= BM_TYPE_POWER_RESOURCE
;
565 driver
.notify
= &bm_pr_notify
;
566 driver
.request
= &bm_pr_request
;
568 status
= bm_unregister_driver(&criteria
, &driver
);
570 return_ACPI_STATUS(status
);
574 /****************************************************************************
576 * FUNCTION: bm_pr_notify
584 ****************************************************************************/
588 BM_NOTIFY notify_type
,
589 BM_HANDLE device_handle
,
592 ACPI_STATUS status
= AE_OK
;
594 FUNCTION_TRACE("bm_pr_notify");
596 switch (notify_type
) {
598 case BM_NOTIFY_DEVICE_ADDED
:
599 status
= bm_pr_add_device(device_handle
, context
);
602 case BM_NOTIFY_DEVICE_REMOVED
:
603 status
= bm_pr_remove_device(context
);
611 return_ACPI_STATUS(status
);
615 /****************************************************************************
617 * FUNCTION: bm_pr_request
625 ****************************************************************************/
632 ACPI_STATUS status
= AE_OK
;
633 BM_POWER_RESOURCE
*pr
= NULL
;
635 FUNCTION_TRACE("bm_pr_request");
638 * Must have a valid request structure and context.
640 if (!request
|| !context
) {
641 return_ACPI_STATUS(AE_BAD_PARAMETER
);
645 * context contains information specific to this power resource.
647 pr
= (BM_POWER_RESOURCE
*)context
;
653 switch (request
->command
) {
660 request
->status
= status
;
662 return_ACPI_STATUS(status
);