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);