Merge from vendor branch GCC:
[dragonfly.git] / sys / dev / acpica / acpi_powerres.c
1 /*-
2  * Copyright (c) 2001 Michael Smith
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *      $FreeBSD: src/sys/dev/acpica/acpi_powerres.c,v 1.14.6.1 2003/08/22 20:49:20 jhb Exp $
27  *      $DragonFly: src/sys/dev/acpica/Attic/acpi_powerres.c,v 1.3 2004/06/03 13:12:24 joerg Exp $ 
28  */
29
30 #include "opt_acpi.h"           /* XXX trim includes */
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/proc.h>
34 #include <sys/malloc.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/ioccom.h>
38 #include <sys/reboot.h>
39 #include <sys/sysctl.h>
40 #include <sys/systm.h>
41 #include <sys/ctype.h>
42
43 #include <machine/clock.h>
44
45 #include <machine/resource.h>
46
47 #include "acpi.h"
48
49 #include <dev/acpica/acpivar.h>
50 #include <dev/acpica/acpiio.h>
51
52 /*
53  * ACPI power resource management.
54  *
55  * Power resource behaviour is slightly complicated by the fact that
56  * a single power resource may provide power for more than one device.
57  * Thus, we must track the device(s) being powered by a given power
58  * resource, and only deactivate it when there are no powered devices.
59  *
60  * Note that this only manages resources for known devices.  There is an
61  * ugly case where we may turn of power to a device which is in use because
62  * we don't know that it depends on a given resource.  We should perhaps
63  * try to be smarter about this, but a more complete solution would involve
64  * scanning all of the ACPI namespace to find devices we're not currently
65  * aware of, and this raises questions about whether they should be left 
66  * on, turned off, etc.
67  *
68  * XXX locking
69  */
70
71 MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources");
72
73 /*
74  * Hooks for the ACPI CA debugging infrastructure
75  */
76 #define _COMPONENT      ACPI_POWER
77 ACPI_MODULE_NAME("POWERRES")
78
79 /* return values from _STA on a power resource */
80 #define ACPI_PWR_OFF    0
81 #define ACPI_PWR_ON     1
82
83 /*
84  * A relationship between a power resource and a consumer.
85  */
86 struct acpi_powerreference {
87     struct acpi_powerconsumer   *ar_consumer;
88     struct acpi_powerresource   *ar_resource;
89     TAILQ_ENTRY(acpi_powerreference) ar_rlink;  /* link on resource list */
90     TAILQ_ENTRY(acpi_powerreference) ar_clink;  /* link on consumer */
91 };
92     
93 /*
94  * A power-managed device.
95  */
96 struct acpi_powerconsumer {
97     ACPI_HANDLE         ac_consumer;            /* device which is powered */
98     int                 ac_state;
99     TAILQ_ENTRY(acpi_powerconsumer) ac_link;
100     TAILQ_HEAD(,acpi_powerreference) ac_references;
101 };
102
103 /*
104  * A power resource.
105  */
106 struct acpi_powerresource {
107     TAILQ_ENTRY(acpi_powerresource) ap_link;
108     TAILQ_HEAD(,acpi_powerreference) ap_references;
109     ACPI_HANDLE         ap_resource;            /* the resource's handle */
110     ACPI_INTEGER        ap_systemlevel;
111     ACPI_INTEGER        ap_order;
112 };
113
114 static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource)  acpi_powerresources;
115 static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer)  acpi_powerconsumers;
116
117 static ACPI_STATUS              acpi_pwr_register_consumer(ACPI_HANDLE consumer);
118 #ifdef unused
119 static ACPI_STATUS              acpi_pwr_deregister_consumer(ACPI_HANDLE consumer);
120 #endif
121 static ACPI_STATUS              acpi_pwr_register_resource(ACPI_HANDLE res);
122 #if unused
123 static ACPI_STATUS              acpi_pwr_deregister_resource(ACPI_HANDLE res);
124 #endif
125 static void                     acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg);
126 static ACPI_STATUS              acpi_pwr_switch_power(void);
127 static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res);
128 static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer);
129
130 /*
131  * Initialise our lists.
132  */    
133 static void
134 acpi_pwr_init(void *junk)
135 {
136     TAILQ_INIT(&acpi_powerresources);
137     TAILQ_INIT(&acpi_powerconsumers);
138 }
139 SYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL);
140
141 /*
142  * Register a power resource.
143  *
144  * It's OK to call this if we already know about the resource.
145  */
146 static ACPI_STATUS
147 acpi_pwr_register_resource(ACPI_HANDLE res)
148 {
149     ACPI_STATUS                 status;
150     ACPI_BUFFER                 buf;
151     ACPI_OBJECT                 *obj;
152     struct acpi_powerresource   *rp, *srp;
153
154     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
155
156     rp = NULL;
157     buf.Pointer = NULL;
158     
159     /* look to see if we know about this resource */
160     if (acpi_pwr_find_resource(res) != NULL)
161         return_ACPI_STATUS(AE_OK);              /* already know about it */
162
163     /* allocate a new resource */
164     rp = malloc(sizeof(*rp), M_ACPIPWR, M_INTWAIT | M_ZERO);
165     TAILQ_INIT(&rp->ap_references);
166     rp->ap_resource = res;
167
168     /* get the Power Resource object */
169     buf.Length = ACPI_ALLOCATE_BUFFER;
170     if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) {
171         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n"));
172         goto out;
173     }
174     obj = buf.Pointer;
175     if (obj->Type != ACPI_TYPE_POWER) {
176         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res)));
177         status = AE_TYPE;
178         goto out;
179     }
180     rp->ap_systemlevel = obj->PowerResource.SystemLevel;
181     rp->ap_order = obj->PowerResource.ResourceOrder;
182     
183     /* sort the resource into the list */
184     status = AE_OK;
185     srp = TAILQ_FIRST(&acpi_powerresources);
186     if ((srp == NULL) || (rp->ap_order < srp->ap_order)) {
187         TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link);
188         goto done;
189     }
190     TAILQ_FOREACH(srp, &acpi_powerresources, ap_link)
191         if (rp->ap_order < srp->ap_order) {
192             TAILQ_INSERT_BEFORE(srp, rp, ap_link);
193             goto done;
194         }
195     TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link);
196
197  done:
198     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res)));
199  out:
200     if (buf.Pointer != NULL)
201         AcpiOsFree(buf.Pointer);
202     if (ACPI_FAILURE(status) && (rp != NULL))
203         free(rp, M_ACPIPWR);
204     return_ACPI_STATUS(status);
205 }
206
207 #ifdef unused
208 /*
209  * Deregister a power resource.
210  */
211 static ACPI_STATUS
212 acpi_pwr_deregister_resource(ACPI_HANDLE res)
213 {
214     struct acpi_powerresource   *rp;
215
216     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
217
218     rp = NULL;
219     
220     /* find the resource */
221     if ((rp = acpi_pwr_find_resource(res)) == NULL)
222         return_ACPI_STATUS(AE_BAD_PARAMETER);
223
224     /* check that there are no consumers referencing this resource */
225     if (TAILQ_FIRST(&rp->ap_references) != NULL)
226         return_ACPI_STATUS(AE_BAD_PARAMETER);
227
228     /* pull it off the list and free it */
229     TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
230     free(rp, M_ACPIPWR);
231
232     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res)));
233
234     return_ACPI_STATUS(AE_OK);
235 }
236 #endif /* unused */
237
238 /*
239  * Register a power consumer.  
240  *
241  * It's OK to call this if we already know about the consumer.
242  */
243 static ACPI_STATUS
244 acpi_pwr_register_consumer(ACPI_HANDLE consumer)
245 {
246     struct acpi_powerconsumer   *pc;
247     
248     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
249
250     /* check to see whether we know about this consumer already */
251     if ((pc = acpi_pwr_find_consumer(consumer)) != NULL)
252         return_ACPI_STATUS(AE_OK);
253     
254     /* allocate a new power consumer */
255     pc = malloc(sizeof(*pc), M_ACPIPWR, M_INTWAIT);
256     TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
257     TAILQ_INIT(&pc->ac_references);
258     pc->ac_consumer = consumer;
259
260     pc->ac_state = ACPI_STATE_UNKNOWN;  /* XXX we should try to find its current state */
261
262     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer)));
263     
264     return_ACPI_STATUS(AE_OK);
265 }
266
267 #ifdef unused
268 /*
269  * Deregister a power consumer.
270  *
271  * This should only be done once the consumer has been powered off.
272  * (XXX is this correct?  Check once implemented)
273  */
274 static ACPI_STATUS
275 acpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
276 {
277     struct acpi_powerconsumer   *pc;
278     
279     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
280
281     /* find the consumer */
282     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
283         return_ACPI_STATUS(AE_BAD_PARAMETER);
284     
285     /* make sure the consumer's not referencing anything right now */
286     if (TAILQ_FIRST(&pc->ac_references) != NULL)
287         return_ACPI_STATUS(AE_BAD_PARAMETER);
288
289     /* pull the consumer off the list and free it */
290     TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
291
292     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer)));
293
294     return_ACPI_STATUS(AE_OK);
295 }
296 #endif /* unused */
297
298 /*
299  * Set a power consumer to a particular power state.
300  */
301 ACPI_STATUS
302 acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
303 {
304     struct acpi_powerconsumer   *pc;
305     struct acpi_powerreference  *pr;
306     ACPI_HANDLE                 method_handle, reslist_handle, pr0_handle;
307     ACPI_BUFFER                 reslist_buffer;
308     ACPI_OBJECT                 *reslist_object;
309     ACPI_STATUS                 status;
310     char                        *method_name, *reslist_name;
311     int                         res_changed;
312
313     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
314
315     /* find the consumer */
316     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
317         if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
318             return_ACPI_STATUS(status);
319         if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
320             return_ACPI_STATUS(AE_ERROR);       /* something very wrong */
321         }
322     }
323
324     /* check for valid transitions */
325     if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0))
326         return_ACPI_STATUS(AE_BAD_PARAMETER);   /* can only go to D0 from D3 */
327
328     /* find transition mechanism(s) */
329     switch(state) {
330     case ACPI_STATE_D0:
331         method_name = "_PS0";
332         reslist_name = "_PR0";
333         break;
334     case ACPI_STATE_D1:
335         method_name = "_PS1";
336         reslist_name = "_PR1";
337         break;
338     case ACPI_STATE_D2:
339         method_name = "_PS2";
340         reslist_name = "_PR2";
341         break;
342     case ACPI_STATE_D3:
343         method_name = "_PS3";
344         reslist_name = "_PR3";
345         break;
346     default:
347         return_ACPI_STATUS(AE_BAD_PARAMETER);
348     }
349     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n",
350                       acpi_name(consumer), pc->ac_state, state));
351
352     /*
353      * Verify that this state is supported, ie. one of method or
354      * reslist must be present.  We need to do this before we go 
355      * dereferencing resources (since we might be trying to go to
356      * a state we don't support).
357      *
358      * Note that if any states are supported, the device has to
359      * support D0 and D3.  It's never an error to try to go to
360      * D0.
361      */
362     reslist_buffer.Pointer = NULL;
363     reslist_object = NULL;
364     if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
365         method_handle = NULL;
366     if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
367         reslist_handle = NULL;
368     if ((reslist_handle == NULL) && (method_handle == NULL)) {
369         if (state == ACPI_STATE_D0) {
370             pc->ac_state = ACPI_STATE_D0;
371             return_ACPI_STATUS(AE_OK);
372         }
373         if (state != ACPI_STATE_D3) {
374             goto bad;
375         }
376
377         /* turn off the resources listed in _PR0 to go to D3. */
378         if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
379             goto bad;
380         }
381         reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
382         if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) {
383             goto bad;
384         }
385         reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
386         if ((reslist_object->Type != ACPI_TYPE_PACKAGE) ||
387             (reslist_object->Package.Count == 0)) {
388             goto bad;
389         }
390         AcpiOsFree(reslist_buffer.Pointer);
391         reslist_buffer.Pointer = NULL;
392         reslist_object = NULL;
393     }
394
395     /*
396      * Check that we can actually fetch the list of power resources
397      */
398     if (reslist_handle != NULL) {
399         reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
400         if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) {
401             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n",
402                               acpi_name(reslist_handle)));
403             goto out;
404         }
405         reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
406         if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
407             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
408                               reslist_object->Type));
409             status = AE_TYPE;
410             goto out;
411         }
412     }
413
414     /*
415      * Now we are ready to switch, so  kill off any current power resource references.
416      */
417     res_changed = 0;
418     while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
419         res_changed = 1;
420         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource)));
421         TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
422         TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
423         free(pr, M_ACPIPWR);
424     }
425
426     /*
427      * Add new power resource references, if we have any.  Traverse the
428      * package that we got from evaluating reslist_handle, and look up each
429      * of the resources that are referenced.
430      */
431     if (reslist_object != NULL) {
432         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 
433                           reslist_object->Package.Count));
434         acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc);
435         res_changed = 1;
436     }
437
438     /*
439      * If we changed anything in the resource list, we need to run a switch
440      * pass now.
441      */
442     if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
443         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n",
444                           acpi_name(consumer), state));
445         goto out;               /* XXX is this appropriate?  Should we return to previous state? */
446     }
447
448     /* invoke power state switch method (if present) */
449     if (method_handle != NULL) {
450         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n",
451                           acpi_name(method_handle)));
452         if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) {
453                 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
454                                   AcpiFormatException(status)));
455                 pc->ac_state = ACPI_STATE_UNKNOWN;
456                 goto out;       /* XXX Should we return to previous state? */
457         }
458     }
459         
460     /* transition was successful */
461     pc->ac_state = state;
462     return_ACPI_STATUS(AE_OK);
463
464  bad:
465     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", 
466                       state));
467     status = AE_BAD_PARAMETER;
468
469  out:
470     if (reslist_buffer.Pointer != NULL)
471         AcpiOsFree(reslist_buffer.Pointer);
472     return_ACPI_STATUS(status);
473 }
474
475 /*
476  * Called to create a reference between a power consumer and a power resource
477  * identified in the object.
478  */
479 static void
480 acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
481 {
482     struct acpi_powerconsumer   *pc = (struct acpi_powerconsumer *)arg;
483     struct acpi_powerreference  *pr;
484     struct acpi_powerresource   *rp;
485     ACPI_HANDLE                 res;
486     ACPI_STATUS                 status;
487
488     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
489
490     /* check the object type */
491     switch (obj->Type) {
492     case ACPI_TYPE_ANY:
493         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
494                           acpi_name(pc->ac_consumer), acpi_name(obj->Reference.Handle)));
495
496         res = obj->Reference.Handle;
497         break;
498
499     case ACPI_TYPE_STRING:
500         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n",
501                           acpi_name(pc->ac_consumer), obj->String.Pointer));
502
503         /* get the handle of the resource */
504         if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) {
505             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n", 
506                               obj->String.Pointer));
507             return_VOID;
508         }
509         break;
510
511     default:
512         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n", 
513                           obj->Type));
514         return_VOID;
515     }
516
517     /* create/look up the resource */
518     if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
519         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n",
520                           obj->String.Pointer, AcpiFormatException(status)));
521         return_VOID;
522     }
523     if ((rp = acpi_pwr_find_resource(res)) == NULL) {
524         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
525         return_VOID;
526     }
527     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource)));
528
529     /* create a reference between the consumer and resource */
530     pr = malloc(sizeof(*pr), M_ACPIPWR, M_INTWAIT | M_ZERO);
531     pr->ar_consumer = pc;
532     pr->ar_resource = rp;
533     TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
534     TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
535     
536     return_VOID;
537 }
538
539
540 /*
541  * Switch power resources to conform to the desired state.
542  *
543  * Consumers may have modified the power resource list in an arbitrary
544  * fashion; we sweep it in sequence order.
545  */
546 static ACPI_STATUS
547 acpi_pwr_switch_power(void)
548 {
549     struct acpi_powerresource   *rp;
550     ACPI_STATUS                 status;
551     int                         cur;
552
553     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
554
555     /*
556      * Sweep the list forwards turning things on.
557      */
558     TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
559         if (TAILQ_FIRST(&rp->ap_references) == NULL) {
560             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n",
561                               acpi_name(rp->ap_resource)));
562             continue;
563         }
564
565         /* we could cache this if we trusted it not to change under us */
566         if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
567             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
568                               acpi_name(rp->ap_resource), status));
569             continue;   /* XXX is this correct?  Always switch if in doubt? */
570         }
571
572         /*
573          * Switch if required.  Note that we ignore the result of the switch
574          * effort; we don't know what to do if it fails, so checking wouldn't
575          * help much.
576          */
577         if (cur != ACPI_PWR_ON) {
578             if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) {
579                 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", 
580                                   acpi_name(rp->ap_resource), AcpiFormatException(status)));
581             } else {
582                 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource)));
583             }
584         } else {
585             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource)));
586         }
587     }
588     
589     /*
590      * Sweep the list backwards turning things off.
591      */
592     TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) {
593         if (TAILQ_FIRST(&rp->ap_references) != NULL) {
594             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n",
595                               acpi_name(rp->ap_resource)));
596             continue;
597         }
598
599         /* we could cache this if we trusted it not to change under us */
600         if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) {
601             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
602                               acpi_name(rp->ap_resource), status));
603             continue;   /* XXX is this correct?  Always switch if in doubt? */
604         }
605
606         /*
607          * Switch if required.  Note that we ignore the result of the switch
608          * effort; we don't know what to do if it fails, so checking wouldn't
609          * help much.
610          */
611         if (cur != ACPI_PWR_OFF) {
612             if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) {
613                 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", 
614                                   acpi_name(rp->ap_resource), AcpiFormatException(status)));
615             } else {
616                 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource)));
617             }
618         } else {
619             ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource)));
620         }
621     }
622     return_ACPI_STATUS(AE_OK);
623 }
624
625 /*
626  * Find a power resource's control structure.
627  */
628 static struct acpi_powerresource *
629 acpi_pwr_find_resource(ACPI_HANDLE res)
630 {
631     struct acpi_powerresource   *rp;
632     
633     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
634
635     TAILQ_FOREACH(rp, &acpi_powerresources, ap_link)
636         if (rp->ap_resource == res)
637             break;
638     return_PTR(rp);
639 }
640
641 /*
642  * Find a power consumer's control structure.
643  */
644 static struct acpi_powerconsumer *
645 acpi_pwr_find_consumer(ACPI_HANDLE consumer)
646 {
647     struct acpi_powerconsumer   *pc;
648     
649     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
650
651     TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link)
652         if (pc->ac_consumer == consumer)
653             break;
654     return_PTR(pc);
655 }
656