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.
41 #define _COMPONENT ACPI_POWER_CONTROL
42 MODULE_NAME ("bmpower")
45 /****************************************************************************
47 ****************************************************************************/
51 BM_NOTIFY notify_type
,
52 BM_HANDLE device_handle
,
61 /****************************************************************************
63 ****************************************************************************/
65 /****************************************************************************
67 * FUNCTION: bm_pr_print
75 ****************************************************************************/
79 BM_POWER_RESOURCE
*pr
)
84 return(AE_BAD_PARAMETER
);
88 buffer
.pointer
= acpi_os_callocate(buffer
.length
);
89 if (!buffer
.pointer
) {
93 acpi_get_name(pr
->acpi_handle
, ACPI_FULL_PATHNAME
, &buffer
);
95 acpi_os_printf("Power Resource: found\n");
97 DEBUG_PRINT(ACPI_INFO
, ("+------------------------------------------------------------\n"));
98 DEBUG_PRINT(ACPI_INFO
, ("PowerResource[0x%02X]|[0x%08X] %s\n", pr
->device_handle
, pr
->acpi_handle
, buffer
.pointer
));
99 DEBUG_PRINT(ACPI_INFO
, (" system_level[S%d] resource_order[%d]\n", pr
->system_level
, pr
->resource_order
));
100 DEBUG_PRINT(ACPI_INFO
, (" state[D%d] reference_count[%d]\n", pr
->state
, pr
->reference_count
));
101 DEBUG_PRINT(ACPI_INFO
, ("+------------------------------------------------------------\n"));
103 acpi_os_free(buffer
.pointer
);
109 /****************************************************************************
111 * FUNCTION: bm_pr_get_state
119 ****************************************************************************/
123 BM_POWER_RESOURCE
*pr
)
125 ACPI_STATUS status
= AE_OK
;
126 BM_DEVICE_STATUS device_status
= BM_STATUS_UNKNOWN
;
128 FUNCTION_TRACE("bm_pr_get_state");
131 return_ACPI_STATUS(AE_BAD_PARAMETER
);
134 pr
->state
= ACPI_STATE_UNKNOWN
;
139 * Evalute _STA to determine whether the power resource is ON or OFF.
140 * Note that if the power resource isn't present we'll get AE_OK but
143 status
= bm_get_device_status(pr
->device_handle
, &device_status
);
144 if (ACPI_FAILURE(status
)) {
145 DEBUG_PRINT(ACPI_ERROR
, ("Error reading status for power resource [0x%02x].\n", pr
->device_handle
));
146 return_ACPI_STATUS(status
);
148 if (device_status
== BM_STATUS_UNKNOWN
) {
149 DEBUG_PRINT(ACPI_ERROR
, ("Error reading status for power resource [0x%02x].\n", pr
->device_handle
));
150 return_ACPI_STATUS(AE_NOT_EXIST
);
154 * Mask off all bits but the first as some systems return non-standard
155 * values (e.g. 0x51).
157 switch (device_status
& 0x01) {
159 DEBUG_PRINT(ACPI_INFO
, ("Power resource [0x%02x] is OFF.\n", pr
->device_handle
));
160 pr
->state
= ACPI_STATE_D3
;
163 DEBUG_PRINT(ACPI_INFO
, ("Power resource [0x%02x] is ON.\n", pr
->device_handle
));
164 pr
->state
= ACPI_STATE_D0
;
168 return_ACPI_STATUS(status
);
172 /****************************************************************************
174 * FUNCTION: bm_pr_set_state
182 ****************************************************************************/
186 BM_POWER_RESOURCE
*pr
,
187 BM_POWER_STATE target_state
)
189 ACPI_STATUS status
= AE_OK
;
191 FUNCTION_TRACE("bm_pr_set_state");
194 return_ACPI_STATUS(AE_BAD_PARAMETER
);
197 status
= bm_pr_get_state(pr
);
198 if (ACPI_FAILURE(status
)) {
199 return_ACPI_STATUS(status
);
202 if (target_state
== pr
->state
) {
203 DEBUG_PRINT(ACPI_INFO
, ("Power resource [0x%02X] already at target power state [D%d].\n", pr
->device_handle
, pr
->state
));
204 return_ACPI_STATUS(AE_OK
);
207 switch (target_state
) {
210 DEBUG_PRINT(ACPI_INFO
, ("Turning power resource [0x%02X] ON.\n", pr
->device_handle
));
211 status
= bm_evaluate_object(pr
->acpi_handle
, "_ON", NULL
, NULL
);
215 DEBUG_PRINT(ACPI_INFO
, ("Turning power resource [0x%02X] OFF.\n", pr
->device_handle
));
216 status
= bm_evaluate_object(pr
->acpi_handle
, "_OFF", NULL
, NULL
);
220 status
= AE_BAD_PARAMETER
;
224 status
= bm_pr_get_state(pr
);
225 if (ACPI_FAILURE(status
)) {
226 return_ACPI_STATUS(status
);
229 return_ACPI_STATUS(status
);
233 /****************************************************************************
235 * FUNCTION: bm_pr_list_get_state
243 ****************************************************************************/
246 bm_pr_list_get_state (
247 BM_HANDLE_LIST
*pr_list
,
248 BM_POWER_STATE
*power_state
)
250 ACPI_STATUS status
= AE_OK
;
251 BM_POWER_RESOURCE
*pr
= NULL
;
254 FUNCTION_TRACE("bm_pr_list_get_state");
256 if (!pr_list
|| !power_state
) {
257 return_ACPI_STATUS(AE_BAD_PARAMETER
);
260 if (pr_list
->count
< 1) {
261 pr
->state
= ACPI_STATE_UNKNOWN
;
262 return_ACPI_STATUS(AE_ERROR
);
265 (*power_state
) = ACPI_STATE_D0
;
268 * Calculate Current power_state:
269 * -----------------------------
270 * The current state of a list of power resources is ON if all
271 * power resources are currently in the ON state. In other words,
272 * if any power resource in the list is OFF then the collection
275 for (i
= 0; i
< pr_list
->count
; i
++) {
277 status
= bm_get_device_context(pr_list
->handles
[i
],
278 (BM_DRIVER_CONTEXT
*)(&pr
));
279 if (ACPI_FAILURE(status
)) {
280 DEBUG_PRINT(ACPI_WARN
, ("Invalid reference to power resource [0x%02X].\n", pr_list
->handles
[i
]));
281 (*power_state
) = ACPI_STATE_UNKNOWN
;
285 status
= bm_pr_get_state(pr
);
286 if (ACPI_FAILURE(status
)) {
287 (*power_state
) = ACPI_STATE_UNKNOWN
;
291 if (pr
->state
!= ACPI_STATE_D0
) {
292 (*power_state
) = pr
->state
;
297 return_ACPI_STATUS(status
);
301 /****************************************************************************
303 * FUNCTION: bm_pr_list_transition
311 ****************************************************************************/
314 bm_pr_list_transition (
315 BM_HANDLE_LIST
*current_list
,
316 BM_HANDLE_LIST
*target_list
)
318 ACPI_STATUS status
= AE_OK
;
319 BM_POWER_RESOURCE
*pr
= NULL
;
322 FUNCTION_TRACE("bm_pr_list_transition");
324 if (!current_list
|| !target_list
) {
325 return_ACPI_STATUS(AE_BAD_PARAMETER
);
331 * Reference all resources for the target power state first (so
332 * the device doesn't get turned off while transitioning). Power
333 * resources that aren't on (new reference count of 1) are turned on.
335 for (i
= 0; i
< target_list
->count
; i
++) {
337 status
= bm_get_device_context(target_list
->handles
[i
],
338 (BM_DRIVER_CONTEXT
*)(&pr
));
339 if (ACPI_FAILURE(status
)) {
340 DEBUG_PRINT(ACPI_WARN
, ("Invalid reference to power resource [0x%02X].\n", target_list
->handles
[i
]));
344 if (++pr
->reference_count
== 1) {
345 /* TODO: Need ordering based upon resource_order */
346 status
= bm_pr_set_state(pr
, ACPI_STATE_D0
);
347 if (ACPI_FAILURE(status
)) {
348 /* TODO: How do we handle this? */
349 DEBUG_PRINT(ACPI_WARN
, ("Unable to change power state for power resource [0x%02X].\n", target_list
->handles
[i
]));
355 * Dereference Current:
356 * --------------------
357 * Dereference all resources for the current power state. Power
358 * resources no longer referenced (new reference count of 0) are
361 for (i
= 0; i
< current_list
->count
; i
++) {
363 status
= bm_get_device_context(current_list
->handles
[i
],
364 (BM_DRIVER_CONTEXT
*)(&pr
));
365 if (ACPI_FAILURE(status
)) {
366 DEBUG_PRINT(ACPI_WARN
, ("Invalid reference to power resource [0x%02X].\n", target_list
->handles
[i
]));
370 if (--pr
->reference_count
== 0) {
371 /* TODO: Need ordering based upon resource_order */
372 status
= bm_pr_set_state(pr
, ACPI_STATE_D3
);
373 if (ACPI_FAILURE(status
)) {
374 /* TODO: How do we handle this? */
375 DEBUG_PRINT(ACPI_ERROR
, ("Unable to change power state for power resource [0x%02X].\n", current_list
->handles
[i
]));
380 return_ACPI_STATUS(status
);
384 /****************************************************************************
386 * FUNCTION: bm_pr_add_device
394 ****************************************************************************/
398 BM_HANDLE device_handle
,
401 ACPI_STATUS status
= AE_OK
;
402 BM_POWER_RESOURCE
*pr
= NULL
;
403 BM_DEVICE
*device
= NULL
;
405 ACPI_OBJECT acpi_object
;
407 FUNCTION_TRACE("bm_pr_add_device");
409 DEBUG_PRINT(ACPI_INFO
, ("Adding power resource [0x%02X].\n", device_handle
));
411 if (!context
|| *context
) {
412 return_ACPI_STATUS(AE_BAD_PARAMETER
);
415 buffer
.length
= sizeof(ACPI_OBJECT
);
416 buffer
.pointer
= &acpi_object
;
419 * Get information on this device.
421 status
= bm_get_device_info(device_handle
, &device
);
422 if (ACPI_FAILURE(status
)) {
423 return_ACPI_STATUS(status
);
427 * Allocate a new BM_POWER_RESOURCE structure.
429 pr
= acpi_os_callocate(sizeof(BM_POWER_RESOURCE
));
431 return_ACPI_STATUS(AE_NO_MEMORY
);
434 pr
->device_handle
= device
->handle
;
435 pr
->acpi_handle
= device
->acpi_handle
;
438 * Get information on this power resource.
440 status
= acpi_evaluate_object(pr
->acpi_handle
, NULL
, NULL
, &buffer
);
441 if (ACPI_FAILURE(status
)) {
445 pr
->system_level
= acpi_object
.power_resource
.system_level
;
446 pr
->resource_order
= acpi_object
.power_resource
.resource_order
;
447 pr
->state
= ACPI_STATE_UNKNOWN
;
448 pr
->reference_count
= 0;
451 * Get the power resource's current state (ON|OFF).
453 status
= bm_pr_get_state(pr
);
456 if (ACPI_FAILURE(status
)) {
464 return_ACPI_STATUS(status
);
468 /****************************************************************************
470 * FUNCTION: bm_pr_remove_device
478 ****************************************************************************/
481 bm_pr_remove_device (
484 ACPI_STATUS status
= AE_OK
;
485 BM_POWER_RESOURCE
*pr
= NULL
;
487 FUNCTION_TRACE("bm_pr_remove_device");
489 if (!context
|| !*context
) {
490 return_ACPI_STATUS(AE_BAD_PARAMETER
);
493 pr
= (BM_POWER_RESOURCE
*)*context
;
495 DEBUG_PRINT(ACPI_INFO
, ("Removing power resource [0x%02X].\n", pr
->device_handle
));
499 return_ACPI_STATUS(status
);
503 /****************************************************************************
505 ****************************************************************************/
507 /****************************************************************************
509 * FUNCTION: bm_pr_initialize
517 ****************************************************************************/
520 bm_pr_initialize (void)
522 ACPI_STATUS status
= AE_OK
;
523 BM_DEVICE_ID criteria
;
526 FUNCTION_TRACE("bm_pr_initialize");
528 MEMSET(&criteria
, 0, sizeof(BM_DEVICE_ID
));
529 MEMSET(&driver
, 0, sizeof(BM_DRIVER
));
531 criteria
.type
= BM_TYPE_POWER_RESOURCE
;
533 driver
.notify
= &bm_pr_notify
;
534 driver
.request
= &bm_pr_request
;
536 status
= bm_register_driver(&criteria
, &driver
);
538 return_ACPI_STATUS(status
);
542 /****************************************************************************
544 * FUNCTION: bm_pr_terminate
552 ****************************************************************************/
555 bm_pr_terminate (void)
557 ACPI_STATUS status
= AE_OK
;
558 BM_DEVICE_ID criteria
;
561 FUNCTION_TRACE("bm_pr_terminate");
563 MEMSET(&criteria
, 0, sizeof(BM_DEVICE_ID
));
564 MEMSET(&driver
, 0, sizeof(BM_DRIVER
));
566 criteria
.type
= BM_TYPE_POWER_RESOURCE
;
568 driver
.notify
= &bm_pr_notify
;
569 driver
.request
= &bm_pr_request
;
571 status
= bm_unregister_driver(&criteria
, &driver
);
573 return_ACPI_STATUS(status
);
577 /****************************************************************************
579 * FUNCTION: bm_pr_notify
587 ****************************************************************************/
591 BM_NOTIFY notify_type
,
592 BM_HANDLE device_handle
,
595 ACPI_STATUS status
= AE_OK
;
597 FUNCTION_TRACE("bm_pr_notify");
599 switch (notify_type
) {
601 case BM_NOTIFY_DEVICE_ADDED
:
602 status
= bm_pr_add_device(device_handle
, context
);
605 case BM_NOTIFY_DEVICE_REMOVED
:
606 status
= bm_pr_remove_device(context
);
614 return_ACPI_STATUS(status
);
618 /****************************************************************************
620 * FUNCTION: bm_pr_request
628 ****************************************************************************/
635 ACPI_STATUS status
= AE_OK
;
636 BM_POWER_RESOURCE
*pr
= NULL
;
638 FUNCTION_TRACE("bm_pr_request");
641 * Must have a valid request structure and context.
643 if (!request
|| !context
) {
644 return_ACPI_STATUS(AE_BAD_PARAMETER
);
648 * context contains information specific to this power resource.
650 pr
= (BM_POWER_RESOURCE
*)context
;
656 switch (request
->command
) {
663 request
->status
= status
;
665 return_ACPI_STATUS(status
);