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