xref: /freebsd/sys/dev/acpica/acpi_powerres.c (revision 24e4dcf4ba5e9dedcf89efd358ea3e1fe5867020)
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 
27 #include <sys/cdefs.h>
28 #include "opt_acpi.h"
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
32 #include <sys/bus.h>
33 
34 #include <contrib/dev/acpica/include/acpi.h>
35 #include <contrib/dev/acpica/include/accommon.h>
36 
37 #include <dev/acpica/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 off 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 
56 static MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources");
57 
58 /* Hooks for the ACPI CA debugging infrastructure */
59 #define _COMPONENT	ACPI_POWERRES
60 ACPI_MODULE_NAME("POWERRES")
61 
62 /* Return values from _STA on a power resource */
63 #define ACPI_PWR_OFF	0
64 #define ACPI_PWR_ON	1
65 
66 /* A relationship between a power resource and a consumer. */
67 struct acpi_powerreference {
68     struct acpi_powerconsumer		*ar_consumer;
69     struct acpi_powerresource		*ar_resource;
70     TAILQ_ENTRY(acpi_powerreference)	ar_rlink; /* link on resource list */
71     TAILQ_ENTRY(acpi_powerreference)	ar_clink; /* link on consumer */
72 };
73 
74 /* A power-managed device. */
75 struct acpi_powerconsumer {
76     /* Device which is powered */
77     ACPI_HANDLE				ac_consumer;
78     int					ac_state;
79 
80     struct {
81 	bool				prx_has;
82 	size_t				prx_count;
83 	ACPI_HANDLE			*prx_deps;
84     } ac_prx[ACPI_D_STATE_COUNT];
85 
86     TAILQ_ENTRY(acpi_powerconsumer)	ac_link;
87     TAILQ_HEAD(,acpi_powerreference)	ac_references;
88 };
89 
90 /* A power resource. */
91 struct acpi_powerresource {
92     TAILQ_ENTRY(acpi_powerresource)	ap_link;
93     TAILQ_HEAD(,acpi_powerreference)	ap_references;
94     ACPI_HANDLE				ap_resource;
95     UINT64				ap_systemlevel;
96     UINT64				ap_order;
97 };
98 
99 static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource)
100     acpi_powerresources = TAILQ_HEAD_INITIALIZER(acpi_powerresources);
101 static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer)
102     acpi_powerconsumers = TAILQ_HEAD_INITIALIZER(acpi_powerconsumers);
103 ACPI_SERIAL_DECL(powerres, "ACPI power resources");
104 
105 static ACPI_STATUS	acpi_pwr_register_consumer(ACPI_HANDLE consumer);
106 static ACPI_STATUS	acpi_pwr_deregister_consumer(ACPI_HANDLE consumer);
107 static ACPI_STATUS	acpi_pwr_register_resource(ACPI_HANDLE res);
108 #ifdef notyet
109 static ACPI_STATUS	acpi_pwr_deregister_resource(ACPI_HANDLE res);
110 #endif /* notyet */
111 static void		acpi_pwr_reference_resource(ACPI_OBJECT *obj,
112 						    void *arg);
113 static int		acpi_pwr_dereference_resource(struct acpi_powerconsumer
114 			    *pc);
115 static ACPI_STATUS	acpi_pwr_switch_power(void);
116 static struct acpi_powerresource
117 			*acpi_pwr_find_resource(ACPI_HANDLE res);
118 static struct acpi_powerconsumer
119 			*acpi_pwr_find_consumer(ACPI_HANDLE consumer);
120 
121 /*
122  * Register a power resource.
123  *
124  * It's OK to call this if we already know about the resource.
125  */
126 static ACPI_STATUS
127 acpi_pwr_register_resource(ACPI_HANDLE res)
128 {
129     ACPI_STATUS			status;
130     ACPI_BUFFER			buf;
131     ACPI_OBJECT			*obj;
132     struct acpi_powerresource	*rp, *srp;
133 
134     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
135     ACPI_SERIAL_ASSERT(powerres);
136 
137     rp = NULL;
138     buf.Pointer = NULL;
139 
140     /* Look to see if we know about this resource */
141     if (acpi_pwr_find_resource(res) != NULL)
142 	return_ACPI_STATUS (AE_OK);		/* already know about it */
143 
144     /* Allocate a new resource */
145     if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
146 	status = AE_NO_MEMORY;
147 	goto out;
148     }
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 	free(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     ACPI_SERIAL_ASSERT(powerres);
207 
208     rp = NULL;
209 
210     /* Find the resource */
211     if ((rp = acpi_pwr_find_resource(res)) == NULL)
212 	return_ACPI_STATUS (AE_BAD_PARAMETER);
213 
214     /* Check that there are no consumers referencing this resource */
215     if (TAILQ_FIRST(&rp->ap_references) != NULL)
216 	return_ACPI_STATUS (AE_BAD_PARAMETER);
217 
218     /* Pull it off the list and free it */
219     TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
220     free(rp, M_ACPIPWR);
221 
222     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n",
223 		     acpi_name(res)));
224 
225     return_ACPI_STATUS (AE_OK);
226 }
227 #endif /* notyet */
228 
229 /*
230  * Evaluate the _PRx (power resources each D-state depends on).  This also
231  * populates the acpi_powerresources queue with the power resources discovered
232  * during this step.
233  *
234  * ACPI 7.3.8 - 7.3.11 guarantee that _PRx will return the same data each
235  * time they are evaluated.
236  *
237  * If this function fails, acpi_pwr_deregister_consumer() must be called on the
238  * power consumer to free already allocated memory.
239  */
240 static ACPI_STATUS
241 acpi_pwr_get_power_resources(ACPI_HANDLE consumer, struct acpi_powerconsumer *pc)
242 {
243     ACPI_INTEGER	status;
244     ACPI_STRING		reslist_name;
245     ACPI_HANDLE		reslist_handle;
246     ACPI_STRING		reslist_names[] = {"_PR0", "_PR1", "_PR2", "_PR3"};
247     ACPI_BUFFER		reslist;
248     ACPI_OBJECT		*reslist_object;
249     ACPI_OBJECT		*dep;
250     ACPI_HANDLE		*res;
251 
252     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
253     ACPI_SERIAL_ASSERT(powerres);
254 
255     MPASS(consumer != NULL);
256 
257     for (int state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) {
258 	pc->ac_prx[state].prx_has = false;
259 	pc->ac_prx[state].prx_count = 0;
260 	pc->ac_prx[state].prx_deps = NULL;
261 
262 	reslist_name = reslist_names[state - ACPI_STATE_D0];
263 	if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
264 	    continue;
265 
266 	reslist.Pointer = NULL;
267 	reslist.Length = ACPI_ALLOCATE_BUFFER;
268 	status = AcpiEvaluateObjectTyped(reslist_handle, NULL, NULL, &reslist,
269 					 ACPI_TYPE_PACKAGE);
270 	if (ACPI_FAILURE(status) || reslist.Pointer == NULL)
271 	    /*
272 	     * ACPI_ALLOCATE_BUFFER entails everything will be freed on error
273 	     * by AcpiEvaluateObjectTyped.
274 	     */
275 	    continue;
276 
277 	reslist_object = (ACPI_OBJECT *)reslist.Pointer;
278 	pc->ac_prx[state].prx_has = true;
279 	pc->ac_prx[state].prx_count = reslist_object->Package.Count;
280 
281 	if (reslist_object->Package.Count == 0) {
282 	    AcpiOsFree(reslist_object);
283 	    continue;
284 	}
285 
286 	pc->ac_prx[state].prx_deps = mallocarray(pc->ac_prx[state].prx_count,
287 	    sizeof(*pc->ac_prx[state].prx_deps), M_ACPIPWR, M_NOWAIT);
288 	if (pc->ac_prx[state].prx_deps == NULL) {
289 	    AcpiOsFree(reslist_object);
290 	    return_ACPI_STATUS (AE_NO_MEMORY);
291 	}
292 
293 	for (size_t i = 0; i < reslist_object->Package.Count; i++) {
294 	    dep = &reslist_object->Package.Elements[i];
295 	    res = dep->Reference.Handle;
296 	    pc->ac_prx[state].prx_deps[i] = res;
297 
298 	    /* It's fine to attempt to register the same resource twice. */
299 	    acpi_pwr_register_resource(res);
300 	}
301 	AcpiOsFree(reslist_object);
302     }
303 
304     return_ACPI_STATUS (AE_OK);
305 }
306 
307 /*
308  * Register a power consumer.
309  *
310  * It's OK to call this if we already know about the consumer.
311  */
312 static ACPI_STATUS
313 acpi_pwr_register_consumer(ACPI_HANDLE consumer)
314 {
315     ACPI_INTEGER		status;
316     struct acpi_powerconsumer	*pc;
317 
318     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
319     ACPI_SERIAL_ASSERT(powerres);
320 
321     /* Check to see whether we know about this consumer already */
322     if (acpi_pwr_find_consumer(consumer) != NULL)
323 	return_ACPI_STATUS (AE_OK);
324 
325     /* Allocate a new power consumer */
326     if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL)
327 	return_ACPI_STATUS (AE_NO_MEMORY);
328     TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
329     TAILQ_INIT(&pc->ac_references);
330     pc->ac_consumer = consumer;
331 
332     /*
333      * Get all its power resource dependencies, if it has _PRx.  We do this now
334      * as an opportunity to populate the acpi_powerresources queue.
335      *
336      * If this fails, immediately deregister it.
337      */
338     status = acpi_pwr_get_power_resources(consumer, pc);
339     if (ACPI_FAILURE(status)) {
340 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
341 			 "failed to get power resources for %s\n",
342 			 acpi_name(consumer)));
343 	acpi_pwr_deregister_consumer(consumer);
344 	return_ACPI_STATUS (status);
345     }
346 
347     /* XXX we should try to find its current state */
348     pc->ac_state = ACPI_STATE_UNKNOWN;
349 
350     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n",
351 		     acpi_name(consumer)));
352 
353     return_ACPI_STATUS (AE_OK);
354 }
355 
356 /*
357  * Deregister a power consumer.
358  *
359  * This should only be done once the consumer has been powered off.
360  * (XXX is this correct?  Check once implemented)
361  */
362 static ACPI_STATUS
363 acpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
364 {
365     struct acpi_powerconsumer	*pc;
366 
367     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
368     ACPI_SERIAL_ASSERT(powerres);
369 
370     /* Find the consumer */
371     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
372 	return_ACPI_STATUS (AE_BAD_PARAMETER);
373 
374     /* Make sure the consumer's not referencing anything right now */
375     if (TAILQ_FIRST(&pc->ac_references) != NULL)
376 	return_ACPI_STATUS (AE_BAD_PARAMETER);
377 
378     /* Pull the consumer off the list and free it */
379     TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
380     for (size_t i = 0; i < sizeof(pc->ac_prx) / sizeof(*pc->ac_prx); i++)
381 	if (pc->ac_prx[i].prx_deps != NULL)
382 	    free(pc->ac_prx[i].prx_deps, M_ACPIPWR);
383     free(pc, M_ACPIPWR);
384 
385     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n",
386 		     acpi_name(consumer)));
387 
388     return_ACPI_STATUS (AE_OK);
389 }
390 
391 /*
392  * Set a power consumer to a particular power state.
393  */
394 ACPI_STATUS
395 acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
396 {
397     struct acpi_powerconsumer	*pc;
398     ACPI_HANDLE			method_handle, reslist_handle, pr0_handle;
399     ACPI_BUFFER			reslist_buffer;
400     ACPI_OBJECT			*reslist_object;
401     ACPI_STATUS			status;
402     char			*method_name, *reslist_name = NULL;
403 
404     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
405 
406     /* It's never ok to switch a non-existent consumer. */
407     if (consumer == NULL)
408 	return_ACPI_STATUS (AE_NOT_FOUND);
409     reslist_buffer.Pointer = NULL;
410     reslist_object = NULL;
411     ACPI_SERIAL_BEGIN(powerres);
412 
413     /* Find the consumer */
414     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
415 	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
416 	    goto out;
417 	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
418 	    panic("acpi added power consumer but can't find it");
419     }
420 
421     /* Stop here if we're already at the target D-state. */
422     if (pc->ac_state == state) {
423 	status = AE_OK;
424 	goto out;
425     }
426 
427     /*
428      * Check for valid transitions.  From D3hot or D3cold, we can only go to D0.
429      * The exception to this is going from D3hot to D3cold or the other way
430      * around.  This is because they both use _PS3, so the only difference when
431      * doing these transitions is whether or not the power resources for _PR3
432      * are on for devices which support D3cold, and turning these power
433      * resources on/off is always perfectly fine (ACPI 7.3.11).
434      */
435     status = AE_BAD_PARAMETER;
436     if (pc->ac_state == ACPI_STATE_D3_HOT && state != ACPI_STATE_D0 &&
437 	state != ACPI_STATE_D3_COLD)
438 	goto out;
439     if (pc->ac_state == ACPI_STATE_D3_COLD && state != ACPI_STATE_D0 &&
440 	state != ACPI_STATE_D3_HOT)
441 	goto out;
442 
443     /* Find transition mechanism(s) */
444     switch (state) {
445     case ACPI_STATE_D0:
446 	method_name = "_PS0";
447 	reslist_name = "_PR0";
448 	break;
449     case ACPI_STATE_D1:
450 	method_name = "_PS1";
451 	reslist_name = "_PR1";
452 	break;
453     case ACPI_STATE_D2:
454 	method_name = "_PS2";
455 	reslist_name = "_PR2";
456 	break;
457     case ACPI_STATE_D3_HOT:
458 	method_name = "_PS3";
459 	reslist_name = "_PR3";
460 	break;
461     case ACPI_STATE_D3_COLD:
462 	method_name = "_PS3";
463 	reslist_name = NULL;
464 	break;
465     default:
466 	goto out;
467     }
468     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s %s -> %s\n",
469 		     acpi_name(consumer), acpi_d_state_to_str(pc->ac_state),
470 		     acpi_d_state_to_str(state)));
471 
472     /*
473      * Verify that this state is supported, ie. one of method or
474      * reslist must be present.  We need to do this before we go
475      * dereferencing resources (since we might be trying to go to
476      * a state we don't support).
477      *
478      * Note that if any states are supported, the device has to
479      * support D0 and D3.  It's never an error to try to go to
480      * D0.
481      */
482     if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
483 	method_handle = NULL;
484     if (reslist_name == NULL ||
485 	ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
486 	reslist_handle = NULL;
487     if (reslist_handle == NULL && method_handle == NULL) {
488 	if (state == ACPI_STATE_D0) {
489 	    pc->ac_state = ACPI_STATE_D0;
490 	    status = AE_OK;
491 	    goto out;
492 	}
493 	if (state == ACPI_STATE_D3_COLD)
494 	    state = ACPI_STATE_D3_HOT;
495 	if (state != ACPI_STATE_D3_HOT) {
496 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
497 		"attempt to set unsupported state %s\n",
498 		acpi_d_state_to_str(state)));
499 	    goto out;
500 	}
501 
502 	/*
503 	 * Turn off the resources listed in _PR0 to go to D3.  If there is
504 	 * no _PR0 method, this object doesn't support ACPI power states.
505 	 */
506 	if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
507 	    status = AE_NOT_FOUND;
508 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
509 		"device missing _PR0 (desired state was %s)\n",
510 		acpi_d_state_to_str(state)));
511 	    goto out;
512 	}
513 	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
514 	status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer);
515 	if (ACPI_FAILURE(status)) {
516 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
517 		"can't evaluate _PR0 for device %s, state %s\n",
518 		acpi_name(consumer), acpi_d_state_to_str(state)));
519 	    goto out;
520 	}
521 	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
522 	if (!ACPI_PKG_VALID(reslist_object, 1)) {
523 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
524 		"invalid package object for state %s\n",
525 		acpi_d_state_to_str(state)));
526 	    status = AE_TYPE;
527 	    goto out;
528 	}
529 	AcpiOsFree(reslist_buffer.Pointer);
530 	reslist_buffer.Pointer = NULL;
531 	reslist_object = NULL;
532     }
533 
534     /*
535      * Check that we can actually fetch the list of power resources
536      */
537     if (reslist_handle != NULL) {
538 	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
539 	status = AcpiEvaluateObject(reslist_handle, NULL, NULL,
540 				    &reslist_buffer);
541 	if (ACPI_FAILURE(status)) {
542 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
543 			     "can't evaluate resource list %s\n",
544 			     acpi_name(reslist_handle)));
545 	    goto out;
546 	}
547 	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
548 	if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
549 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
550 			     "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
551 			     reslist_object->Type));
552 	    status = AE_TYPE;
553 	    goto out;
554 	}
555     }
556 
557     /*
558      * Now we are ready to switch, so kill off any current power
559      * resource references.
560      */
561     acpi_pwr_dereference_resource(pc);
562 
563     /*
564      * Add new power resource references, if we have any.  Traverse the
565      * package that we got from evaluating reslist_handle, and look up each
566      * of the resources that are referenced.
567      */
568     if (reslist_object != NULL) {
569 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
570 			  reslist_object->Package.Count));
571 	acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource,
572 				  pc);
573     }
574 
575     /*
576      * If we changed anything in the resource list, we need to run a switch
577      * pass now.
578      */
579     if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
580 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
581 			 "failed to switch resources from %s to %s\n",
582 			  acpi_name(consumer), acpi_d_state_to_str(state)));
583 
584 	/* XXX is this appropriate?  Should we return to previous state? */
585 	goto out;
586     }
587 
588     /* Invoke power state switch method (if present) */
589     if (method_handle != NULL) {
590 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
591 			 "invoking state transition method %s\n",
592 			 acpi_name(method_handle)));
593 	status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL);
594 	if (ACPI_FAILURE(status)) {
595 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
596 				 AcpiFormatException(status)));
597 		pc->ac_state = ACPI_STATE_UNKNOWN;
598 
599 		/* XXX Should we return to previous state? */
600 		goto out;
601 	}
602     }
603 
604     /* Transition was successful */
605     pc->ac_state = state;
606     status = AE_OK;
607 
608 out:
609     ACPI_SERIAL_END(powerres);
610     if (reslist_buffer.Pointer != NULL)
611 	AcpiOsFree(reslist_buffer.Pointer);
612     return_ACPI_STATUS (status);
613 }
614 
615 /* Enable or disable a power resource for wake */
616 ACPI_STATUS
617 acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable)
618 {
619     ACPI_STATUS status;
620     struct acpi_powerconsumer *pc;
621     struct acpi_prw_data prw;
622     int i;
623 
624     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
625 
626     if (consumer == NULL)
627 	return (AE_BAD_PARAMETER);
628 
629     ACPI_SERIAL_BEGIN(powerres);
630     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
631 	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
632 	    goto out;
633 	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
634 	    panic("acpi wake added power consumer but can't find it");
635     }
636 
637     status = AE_OK;
638     if (acpi_parse_prw(consumer, &prw) != 0)
639 	goto out;
640     for (i = 0; i < prw.power_res_count; i++)
641 	if (enable)
642 	    acpi_pwr_reference_resource(&prw.power_res[i], pc);
643 	else
644 	    acpi_pwr_dereference_resource(pc);
645 
646     if (prw.power_res_count > 0)
647 	acpi_pwr_switch_power();
648 
649 out:
650     ACPI_SERIAL_END(powerres);
651     return (status);
652 }
653 
654 /*
655  * Called to create a reference between a power consumer and a power resource
656  * identified in the object.
657  */
658 static void
659 acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
660 {
661     struct acpi_powerconsumer	*pc = (struct acpi_powerconsumer *)arg;
662     struct acpi_powerreference	*pr;
663     struct acpi_powerresource	*rp;
664     ACPI_HANDLE			res;
665     ACPI_STATUS			status;
666 
667     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
668     ACPI_SERIAL_ASSERT(powerres);
669 
670     res = acpi_GetReference(NULL, obj);
671     if (res == NULL) {
672 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
673 			 "can't create a power reference for object type %d\n",
674 			 obj->Type));
675 	return_VOID;
676     }
677 
678     /* Create/look up the resource */
679     if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
680 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
681 			 "couldn't register power resource %s - %s\n",
682 			 obj->String.Pointer, AcpiFormatException(status)));
683 	return_VOID;
684     }
685     if ((rp = acpi_pwr_find_resource(res)) == NULL) {
686 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
687 	return_VOID;
688     }
689     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n",
690 		     acpi_name(rp->ap_resource)));
691 
692     /* Create a reference between the consumer and resource */
693     if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
694 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
695 			 "allocation failed for a power consumer reference\n"));
696 	return_VOID;
697     }
698     pr->ar_consumer = pc;
699     pr->ar_resource = rp;
700     TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
701     TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
702 
703     return_VOID;
704 }
705 
706 static int
707 acpi_pwr_dereference_resource(struct acpi_powerconsumer *pc)
708 {
709     struct acpi_powerreference *pr;
710     int changed;
711 
712     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
713     ACPI_SERIAL_ASSERT(powerres);
714 
715     changed = 0;
716     while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
717         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n",
718                          acpi_name(pr->ar_resource->ap_resource)));
719         TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
720         TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
721         free(pr, M_ACPIPWR);
722         changed = 1;
723     }
724 
725     return (changed);
726 }
727 
728 /*
729  * Switch power resources to conform to the desired state.
730  *
731  * Consumers may have modified the power resource list in an arbitrary
732  * fashion; we sweep it in sequence order.
733  */
734 static ACPI_STATUS
735 acpi_pwr_switch_power(void)
736 {
737     struct acpi_powerresource	*rp;
738     ACPI_STATUS			status;
739     int				cur;
740 
741     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
742     ACPI_SERIAL_ASSERT(powerres);
743 
744     /*
745      * Sweep the list forwards turning things on.
746      */
747     TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
748 	if (TAILQ_FIRST(&rp->ap_references) == NULL) {
749 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
750 			     "%s has no references, not turning on\n",
751 			     acpi_name(rp->ap_resource)));
752 	    continue;
753 	}
754 
755 	status = acpi_GetInteger(rp->ap_resource, "_STA", &cur);
756 	if (ACPI_FAILURE(status)) {
757 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
758 			      acpi_name(rp->ap_resource), status));
759 	    /* XXX is this correct?  Always switch if in doubt? */
760 	    continue;
761 	}
762 
763 	/*
764 	 * Switch if required.  Note that we ignore the result of the switch
765 	 * effort; we don't know what to do if it fails, so checking wouldn't
766 	 * help much.
767 	 */
768 	if (cur != ACPI_PWR_ON) {
769 	    status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL);
770 	    if (ACPI_FAILURE(status)) {
771 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
772 				 "failed to switch %s on - %s\n",
773 				 acpi_name(rp->ap_resource),
774 				 AcpiFormatException(status)));
775 	    } else {
776 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n",
777 				 acpi_name(rp->ap_resource)));
778 	    }
779 	} else {
780 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n",
781 			     acpi_name(rp->ap_resource)));
782 	}
783     }
784 
785     /* Sweep the list backwards turning things off. */
786     TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list,
787 	ap_link) {
788 	if (TAILQ_FIRST(&rp->ap_references) != NULL) {
789 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
790 			     "%s has references, not turning off\n",
791 			     acpi_name(rp->ap_resource)));
792 	    continue;
793 	}
794 
795 	status = acpi_GetInteger(rp->ap_resource, "_STA", &cur);
796 	if (ACPI_FAILURE(status)) {
797 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
798 			      acpi_name(rp->ap_resource), status));
799 	    /* XXX is this correct?  Always switch if in doubt? */
800 	    continue;
801 	}
802 
803 	/*
804 	 * Switch if required.  Note that we ignore the result of the switch
805 	 * effort; we don't know what to do if it fails, so checking wouldn't
806 	 * help much.
807 	 */
808 	if (cur != ACPI_PWR_OFF) {
809 	    status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL);
810 	    if (ACPI_FAILURE(status)) {
811 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
812 				 "failed to switch %s off - %s\n",
813 				 acpi_name(rp->ap_resource),
814 				 AcpiFormatException(status)));
815 	    } else {
816 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n",
817 				 acpi_name(rp->ap_resource)));
818 	    }
819 	} else {
820 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n",
821 			     acpi_name(rp->ap_resource)));
822 	}
823     }
824 
825     return_ACPI_STATUS (AE_OK);
826 }
827 
828 /*
829  * Find a power resource's control structure.
830  */
831 static struct acpi_powerresource *
832 acpi_pwr_find_resource(ACPI_HANDLE res)
833 {
834     struct acpi_powerresource	*rp;
835 
836     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
837     ACPI_SERIAL_ASSERT(powerres);
838 
839     TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
840 	if (rp->ap_resource == res)
841 	    break;
842     }
843 
844     return_PTR (rp);
845 }
846 
847 /*
848  * Find a power consumer's control structure.
849  */
850 static struct acpi_powerconsumer *
851 acpi_pwr_find_consumer(ACPI_HANDLE consumer)
852 {
853     struct acpi_powerconsumer	*pc;
854 
855     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
856     ACPI_SERIAL_ASSERT(powerres);
857 
858     TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) {
859 	if (pc->ac_consumer == consumer)
860 	    break;
861     }
862 
863     return_PTR (pc);
864 }
865