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