xref: /dragonfly/sys/dev/acpica/acpi_powerres.c (revision 1bf4b486)
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 
657