acpi_powerres.c revision 150003
1214503Srpaulo/*-
2214503Srpaulo * Copyright (c) 2001 Michael Smith
3214503Srpaulo * All rights reserved.
4214503Srpaulo *
5214503Srpaulo * Redistribution and use in source and binary forms, with or without
6214503Srpaulo * modification, are permitted provided that the following conditions
7214503Srpaulo * are met:
8214503Srpaulo * 1. Redistributions of source code must retain the above copyright
9214503Srpaulo *    notice, this list of conditions and the following disclaimer.
10214503Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11214503Srpaulo *    notice, this list of conditions and the following disclaimer in the
12214503Srpaulo *    documentation and/or other materials provided with the distribution.
13214503Srpaulo *
14214503Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15214503Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16214503Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17214503Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18214503Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19214503Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20214503Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21214503Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22214503Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23214503Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24214503Srpaulo * SUCH DAMAGE.
25214503Srpaulo */
26214503Srpaulo
27214503Srpaulo#include <sys/cdefs.h>
28214503Srpaulo__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_powerres.c 150003 2005-09-11 18:39:03Z obrien $");
29214503Srpaulo
30214503Srpaulo#include "opt_acpi.h"
31214503Srpaulo#include <sys/param.h>
32214503Srpaulo#include <sys/kernel.h>
33214503Srpaulo#include <sys/malloc.h>
34214503Srpaulo#include <sys/bus.h>
35214503Srpaulo
36214503Srpaulo#include <contrib/dev/acpica/acpi.h>
37214503Srpaulo#include <dev/acpica/acpivar.h>
38214503Srpaulo
39214503Srpaulo/*
40214503Srpaulo * ACPI power resource management.
41214503Srpaulo *
42214503Srpaulo * Power resource behaviour is slightly complicated by the fact that
43214503Srpaulo * a single power resource may provide power for more than one device.
44214503Srpaulo * Thus, we must track the device(s) being powered by a given power
45214503Srpaulo * resource, and only deactivate it when there are no powered devices.
46214503Srpaulo *
47214503Srpaulo * Note that this only manages resources for known devices.  There is an
48214503Srpaulo * ugly case where we may turn of power to a device which is in use because
49214503Srpaulo * we don't know that it depends on a given resource.  We should perhaps
50214503Srpaulo * try to be smarter about this, but a more complete solution would involve
51214503Srpaulo * scanning all of the ACPI namespace to find devices we're not currently
52214503Srpaulo * aware of, and this raises questions about whether they should be left
53214503Srpaulo * on, turned off, etc.
54214503Srpaulo */
55214503Srpaulo
56214503SrpauloMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources");
57214503Srpaulo
58214503Srpaulo/* Hooks for the ACPI CA debugging infrastructure */
59214503Srpaulo#define _COMPONENT	ACPI_POWERRES
60214503SrpauloACPI_MODULE_NAME("POWERRES")
61214503Srpaulo
62214503Srpaulo/* Return values from _STA on a power resource */
63214503Srpaulo#define ACPI_PWR_OFF	0
64214503Srpaulo#define ACPI_PWR_ON	1
65214503Srpaulo#define ACPI_PWR_UNK	(-1)
66214503Srpaulo
67214503Srpaulo/* A relationship between a power resource and a consumer. */
68214503Srpaulostruct acpi_powerreference {
69214503Srpaulo    struct acpi_powerconsumer		*ar_consumer;
70214503Srpaulo    struct acpi_powerresource		*ar_resource;
71214503Srpaulo    TAILQ_ENTRY(acpi_powerreference)	ar_rlink; /* link on resource list */
72214503Srpaulo    TAILQ_ENTRY(acpi_powerreference)	ar_clink; /* link on consumer */
73214503Srpaulo};
74214503Srpaulo
75214503Srpaulo/* A power-managed device. */
76214503Srpaulostruct acpi_powerconsumer {
77214503Srpaulo    /* Device which is powered */
78214503Srpaulo    ACPI_HANDLE				ac_consumer;
79214503Srpaulo    int					ac_state;
80214503Srpaulo    TAILQ_ENTRY(acpi_powerconsumer)	ac_link;
81214503Srpaulo    TAILQ_HEAD(,acpi_powerreference)	ac_references;
82214503Srpaulo};
83214503Srpaulo
84214503Srpaulo/* A power resource. */
85214503Srpaulostruct acpi_powerresource {
86214503Srpaulo    TAILQ_ENTRY(acpi_powerresource)	ap_link;
87252726Srpaulo    TAILQ_HEAD(,acpi_powerreference)	ap_references;
88252726Srpaulo    ACPI_HANDLE				ap_resource;
89252726Srpaulo    ACPI_INTEGER			ap_systemlevel;
90252726Srpaulo    ACPI_INTEGER			ap_order;
91252726Srpaulo    int					ap_state;
92214503Srpaulo};
93252726Srpaulo
94252726Srpaulostatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource)
95252726Srpaulo	acpi_powerresources;
96214503Srpaulostatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer)
97214503Srpaulo	acpi_powerconsumers;
98214503SrpauloACPI_SERIAL_DECL(powerres, "ACPI power resources");
99214503Srpaulo
100214503Srpaulostatic ACPI_STATUS	acpi_pwr_register_consumer(ACPI_HANDLE consumer);
101214503Srpaulo#ifdef notyet
102214503Srpaulostatic ACPI_STATUS	acpi_pwr_deregister_consumer(ACPI_HANDLE consumer);
103214503Srpaulo#endif /* notyet */
104214503Srpaulostatic ACPI_STATUS	acpi_pwr_register_resource(ACPI_HANDLE res);
105214503Srpaulo#ifdef notyet
106214503Srpaulostatic ACPI_STATUS	acpi_pwr_deregister_resource(ACPI_HANDLE res);
107214503Srpaulo#endif /* notyet */
108214503Srpaulostatic void		acpi_pwr_reference_resource(ACPI_OBJECT *obj,
109252726Srpaulo						    void *arg);
110252726Srpaulostatic int		acpi_pwr_dereference_resource(struct acpi_powerconsumer
111214503Srpaulo			    *pc);
112252726Srpaulostatic ACPI_STATUS	acpi_pwr_switch_power(void);
113214503Srpaulostatic struct acpi_powerresource
114214503Srpaulo			*acpi_pwr_find_resource(ACPI_HANDLE res);
115214503Srpaulostatic struct acpi_powerconsumer
116252726Srpaulo			*acpi_pwr_find_consumer(ACPI_HANDLE consumer);
117252726Srpaulo
118252726Srpaulo/* Initialise our lists. */
119214503Srpaulostatic void
120214503Srpauloacpi_pwr_init(void *junk)
121214503Srpaulo{
122214503Srpaulo    TAILQ_INIT(&acpi_powerresources);
123252726Srpaulo    TAILQ_INIT(&acpi_powerconsumers);
124214503Srpaulo}
125214503SrpauloSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL);
126214503Srpaulo
127214503Srpaulo/*
128214503Srpaulo * Register a power resource.
129214503Srpaulo *
130214503Srpaulo * It's OK to call this if we already know about the resource.
131214503Srpaulo */
132214503Srpaulostatic ACPI_STATUS
133214503Srpauloacpi_pwr_register_resource(ACPI_HANDLE res)
134214503Srpaulo{
135214503Srpaulo    ACPI_STATUS			status;
136214503Srpaulo    ACPI_BUFFER			buf;
137214503Srpaulo    ACPI_OBJECT			*obj;
138214503Srpaulo    struct acpi_powerresource	*rp, *srp;
139214503Srpaulo
140214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
141214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
142214503Srpaulo
143214503Srpaulo    rp = NULL;
144214503Srpaulo    buf.Pointer = NULL;
145214503Srpaulo
146214503Srpaulo    /* Look to see if we know about this resource */
147214503Srpaulo    if (acpi_pwr_find_resource(res) != NULL)
148214503Srpaulo	return_ACPI_STATUS (AE_OK);		/* already know about it */
149214503Srpaulo
150214503Srpaulo    /* Allocate a new resource */
151214503Srpaulo    if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
152214503Srpaulo	status = AE_NO_MEMORY;
153214503Srpaulo	goto out;
154214503Srpaulo    }
155214503Srpaulo    TAILQ_INIT(&rp->ap_references);
156214503Srpaulo    rp->ap_resource = res;
157214503Srpaulo
158214503Srpaulo    /* Get the Power Resource object */
159214503Srpaulo    buf.Length = ACPI_ALLOCATE_BUFFER;
160214503Srpaulo    if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) {
161214503Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n"));
162214503Srpaulo	goto out;
163214503Srpaulo    }
164214503Srpaulo    obj = buf.Pointer;
165214503Srpaulo    if (obj->Type != ACPI_TYPE_POWER) {
166214503Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
167214503Srpaulo			 "questionable power resource object %s\n",
168214503Srpaulo			 acpi_name(res)));
169214503Srpaulo	status = AE_TYPE;
170214503Srpaulo	goto out;
171214503Srpaulo    }
172214503Srpaulo    rp->ap_systemlevel = obj->PowerResource.SystemLevel;
173214503Srpaulo    rp->ap_order = obj->PowerResource.ResourceOrder;
174214503Srpaulo    rp->ap_state = ACPI_PWR_UNK;
175214503Srpaulo
176214503Srpaulo    /* Sort the resource into the list */
177214503Srpaulo    status = AE_OK;
178214503Srpaulo    srp = TAILQ_FIRST(&acpi_powerresources);
179214503Srpaulo    if (srp == NULL || rp->ap_order < srp->ap_order) {
180214503Srpaulo	TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link);
181214503Srpaulo	goto done;
182214503Srpaulo    }
183214503Srpaulo    TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) {
184214503Srpaulo	if (rp->ap_order < srp->ap_order) {
185214503Srpaulo	    TAILQ_INSERT_BEFORE(srp, rp, ap_link);
186214503Srpaulo	    goto done;
187214503Srpaulo	}
188214503Srpaulo    }
189214503Srpaulo    TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link);
190214503Srpaulo
191214503Srpaulo done:
192214503Srpaulo    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
193214503Srpaulo		     "registered power resource %s\n", acpi_name(res)));
194214503Srpaulo
195214503Srpaulo out:
196214503Srpaulo    if (buf.Pointer != NULL)
197214503Srpaulo	AcpiOsFree(buf.Pointer);
198214503Srpaulo    if (ACPI_FAILURE(status) && rp != NULL)
199214503Srpaulo	free(rp, M_ACPIPWR);
200214503Srpaulo    return_ACPI_STATUS (status);
201214503Srpaulo}
202214503Srpaulo
203214503Srpaulo#ifdef notyet
204214503Srpaulo/*
205214503Srpaulo * Deregister a power resource.
206214503Srpaulo */
207214503Srpaulostatic ACPI_STATUS
208214503Srpauloacpi_pwr_deregister_resource(ACPI_HANDLE res)
209252726Srpaulo{
210252726Srpaulo    struct acpi_powerresource	*rp;
211252726Srpaulo
212252726Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
213252726Srpaulo    ACPI_SERIAL_ASSERT(powerres);
214252726Srpaulo
215252726Srpaulo    rp = NULL;
216214503Srpaulo
217214503Srpaulo    /* Find the resource */
218214503Srpaulo    if ((rp = acpi_pwr_find_resource(res)) == NULL)
219214503Srpaulo	return_ACPI_STATUS (AE_BAD_PARAMETER);
220214503Srpaulo
221214503Srpaulo    /* Check that there are no consumers referencing this resource */
222214503Srpaulo    if (TAILQ_FIRST(&rp->ap_references) != NULL)
223214503Srpaulo	return_ACPI_STATUS (AE_BAD_PARAMETER);
224214503Srpaulo
225214503Srpaulo    /* Pull it off the list and free it */
226214503Srpaulo    TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
227214503Srpaulo    free(rp, M_ACPIPWR);
228214503Srpaulo
229214503Srpaulo    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n",
230214503Srpaulo		     acpi_name(res)));
231214503Srpaulo
232214503Srpaulo    return_ACPI_STATUS (AE_OK);
233214503Srpaulo}
234214503Srpaulo#endif /* notyet */
235214503Srpaulo
236214503Srpaulo/*
237214503Srpaulo * Register a power consumer.
238214503Srpaulo *
239214503Srpaulo * It's OK to call this if we already know about the consumer.
240214503Srpaulo */
241214503Srpaulostatic ACPI_STATUS
242214503Srpauloacpi_pwr_register_consumer(ACPI_HANDLE consumer)
243214503Srpaulo{
244214503Srpaulo    struct acpi_powerconsumer	*pc;
245214503Srpaulo
246214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
247214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
248214503Srpaulo
249214503Srpaulo    /* Check to see whether we know about this consumer already */
250214503Srpaulo    if ((pc = acpi_pwr_find_consumer(consumer)) != NULL)
251214503Srpaulo	return_ACPI_STATUS (AE_OK);
252214503Srpaulo
253214503Srpaulo    /* Allocate a new power consumer */
254214503Srpaulo    if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL)
255214503Srpaulo	return_ACPI_STATUS (AE_NO_MEMORY);
256214503Srpaulo    TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
257214503Srpaulo    TAILQ_INIT(&pc->ac_references);
258214503Srpaulo    pc->ac_consumer = consumer;
259214503Srpaulo
260214503Srpaulo    /* XXX we should try to find its current state */
261214503Srpaulo    pc->ac_state = ACPI_STATE_UNKNOWN;
262214503Srpaulo
263214503Srpaulo    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n",
264214503Srpaulo		     acpi_name(consumer)));
265214503Srpaulo
266214503Srpaulo    return_ACPI_STATUS (AE_OK);
267214503Srpaulo}
268214503Srpaulo
269214503Srpaulo#ifdef notyet
270214503Srpaulo/*
271214503Srpaulo * Deregister a power consumer.
272214503Srpaulo *
273214503Srpaulo * This should only be done once the consumer has been powered off.
274214503Srpaulo * (XXX is this correct?  Check once implemented)
275214503Srpaulo */
276214503Srpaulostatic ACPI_STATUS
277214503Srpauloacpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
278214503Srpaulo{
279214503Srpaulo    struct acpi_powerconsumer	*pc;
280214503Srpaulo
281214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
282214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
283214503Srpaulo
284214503Srpaulo    /* Find the consumer */
285214503Srpaulo    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
286214503Srpaulo	return_ACPI_STATUS (AE_BAD_PARAMETER);
287214503Srpaulo
288214503Srpaulo    /* Make sure the consumer's not referencing anything right now */
289214503Srpaulo    if (TAILQ_FIRST(&pc->ac_references) != NULL)
290214503Srpaulo	return_ACPI_STATUS (AE_BAD_PARAMETER);
291214503Srpaulo
292214503Srpaulo    /* Pull the consumer off the list and free it */
293214503Srpaulo    TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
294214503Srpaulo    free(pc, M_ACPIPWR);
295214503Srpaulo
296214503Srpaulo    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n",
297214503Srpaulo		     acpi_name(consumer)));
298214503Srpaulo
299214503Srpaulo    return_ACPI_STATUS (AE_OK);
300214503Srpaulo}
301214503Srpaulo#endif /* notyet */
302214503Srpaulo
303214503Srpaulo/*
304214503Srpaulo * Set a power consumer to a particular power state.
305214503Srpaulo */
306214503SrpauloACPI_STATUS
307214503Srpauloacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
308214503Srpaulo{
309214503Srpaulo    struct acpi_powerconsumer	*pc;
310214503Srpaulo    ACPI_HANDLE			method_handle, reslist_handle, pr0_handle;
311214503Srpaulo    ACPI_BUFFER			reslist_buffer;
312214503Srpaulo    ACPI_OBJECT			*reslist_object;
313214503Srpaulo    ACPI_STATUS			status;
314214503Srpaulo    char			*method_name, *reslist_name;
315214503Srpaulo    int				res_changed;
316214503Srpaulo
317214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
318214503Srpaulo
319214503Srpaulo    /* It's never ok to switch a non-existent consumer. */
320214503Srpaulo    if (consumer == NULL)
321214503Srpaulo	return_ACPI_STATUS (AE_NOT_FOUND);
322214503Srpaulo    reslist_buffer.Pointer = NULL;
323214503Srpaulo    reslist_object = NULL;
324214503Srpaulo    ACPI_SERIAL_BEGIN(powerres);
325214503Srpaulo
326214503Srpaulo    /* Find the consumer */
327214503Srpaulo    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
328214503Srpaulo	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
329214503Srpaulo	    goto out;
330214503Srpaulo	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
331214503Srpaulo	    panic("acpi added power consumer but can't find it");
332214503Srpaulo    }
333214503Srpaulo
334214503Srpaulo    /* Check for valid transitions.  We can only go to D0 from D3. */
335214503Srpaulo    status = AE_BAD_PARAMETER;
336214503Srpaulo    if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0)
337214503Srpaulo	goto out;
338214503Srpaulo
339214503Srpaulo    /* Find transition mechanism(s) */
340214503Srpaulo    switch (state) {
341214503Srpaulo    case ACPI_STATE_D0:
342214503Srpaulo	method_name = "_PS0";
343214503Srpaulo	reslist_name = "_PR0";
344214503Srpaulo	break;
345214503Srpaulo    case ACPI_STATE_D1:
346214503Srpaulo	method_name = "_PS1";
347214503Srpaulo	reslist_name = "_PR1";
348214503Srpaulo	break;
349214503Srpaulo    case ACPI_STATE_D2:
350214503Srpaulo	method_name = "_PS2";
351214503Srpaulo	reslist_name = "_PR2";
352214503Srpaulo	break;
353214503Srpaulo    case ACPI_STATE_D3:
354214503Srpaulo	method_name = "_PS3";
355214503Srpaulo	reslist_name = "_PR3";
356214503Srpaulo	break;
357214503Srpaulo    default:
358214503Srpaulo	goto out;
359252726Srpaulo    }
360252726Srpaulo    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n",
361252726Srpaulo		     acpi_name(consumer), pc->ac_state, state));
362252726Srpaulo
363252726Srpaulo    /*
364252726Srpaulo     * Verify that this state is supported, ie. one of method or
365214503Srpaulo     * reslist must be present.  We need to do this before we go
366252726Srpaulo     * dereferencing resources (since we might be trying to go to
367252726Srpaulo     * a state we don't support).
368252726Srpaulo     *
369252726Srpaulo     * Note that if any states are supported, the device has to
370252726Srpaulo     * support D0 and D3.  It's never an error to try to go to
371214503Srpaulo     * D0.
372214503Srpaulo     */
373214503Srpaulo    if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
374214503Srpaulo	method_handle = NULL;
375214503Srpaulo    if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
376214503Srpaulo	reslist_handle = NULL;
377214503Srpaulo    if (reslist_handle == NULL && method_handle == NULL) {
378214503Srpaulo	if (state == ACPI_STATE_D0) {
379214503Srpaulo	    pc->ac_state = ACPI_STATE_D0;
380214503Srpaulo	    status = AE_OK;
381252726Srpaulo	    goto out;
382252726Srpaulo	}
383252726Srpaulo	if (state != ACPI_STATE_D3) {
384252726Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
385252726Srpaulo		"attempt to set unsupported state D%d\n", state));
386252726Srpaulo	    goto out;
387252726Srpaulo	}
388252726Srpaulo
389252726Srpaulo	/*
390214503Srpaulo	 * Turn off the resources listed in _PR0 to go to D3.  If there is
391214503Srpaulo	 * no _PR0 method, this object doesn't support ACPI power states.
392214503Srpaulo	 */
393214503Srpaulo	if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
394214503Srpaulo	    status = AE_NOT_FOUND;
395214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
396214503Srpaulo		"device missing _PR0 (desired state was D%d)\n", state));
397214503Srpaulo	    goto out;
398214503Srpaulo	}
399214503Srpaulo	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
400214503Srpaulo	status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer);
401214503Srpaulo	if (ACPI_FAILURE(status)) {
402214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
403214503Srpaulo		"can't evaluate _PR0 for device %s, state D%d\n",
404214503Srpaulo		acpi_name(consumer), state));
405214503Srpaulo	    goto out;
406214503Srpaulo	}
407214503Srpaulo	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
408214503Srpaulo	if (!ACPI_PKG_VALID(reslist_object, 1)) {
409214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
410214503Srpaulo		"invalid package object for state D%d\n", state));
411214503Srpaulo	    status = AE_TYPE;
412214503Srpaulo	    goto out;
413214503Srpaulo	}
414214503Srpaulo	AcpiOsFree(reslist_buffer.Pointer);
415214503Srpaulo	reslist_buffer.Pointer = NULL;
416214503Srpaulo	reslist_object = NULL;
417214503Srpaulo    }
418214503Srpaulo
419214503Srpaulo    /*
420214503Srpaulo     * Check that we can actually fetch the list of power resources
421214503Srpaulo     */
422214503Srpaulo    if (reslist_handle != NULL) {
423214503Srpaulo	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
424214503Srpaulo	status = AcpiEvaluateObject(reslist_handle, NULL, NULL,
425214503Srpaulo				    &reslist_buffer);
426214503Srpaulo	if (ACPI_FAILURE(status)) {
427214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
428214503Srpaulo			     "can't evaluate resource list %s\n",
429214503Srpaulo			     acpi_name(reslist_handle)));
430214503Srpaulo	    goto out;
431214503Srpaulo	}
432214503Srpaulo	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
433252726Srpaulo	if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
434252726Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
435252726Srpaulo			     "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
436252726Srpaulo			     reslist_object->Type));
437252726Srpaulo	    status = AE_TYPE;
438252726Srpaulo	    goto out;
439252726Srpaulo	}
440252726Srpaulo    }
441252726Srpaulo
442252726Srpaulo    /*
443252726Srpaulo     * Now we are ready to switch, so kill off any current power
444252726Srpaulo     * resource references.
445252726Srpaulo     */
446252726Srpaulo    res_changed = acpi_pwr_dereference_resource(pc);
447252726Srpaulo
448252726Srpaulo    /*
449252726Srpaulo     * Add new power resource references, if we have any.  Traverse the
450252726Srpaulo     * package that we got from evaluating reslist_handle, and look up each
451252726Srpaulo     * of the resources that are referenced.
452252726Srpaulo     */
453252726Srpaulo    if (reslist_object != NULL) {
454252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
455252726Srpaulo			  reslist_object->Package.Count));
456252726Srpaulo	acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource,
457252726Srpaulo				  pc);
458252726Srpaulo	res_changed = 1;
459252726Srpaulo    }
460252726Srpaulo
461252726Srpaulo    /*
462252726Srpaulo     * If we changed anything in the resource list, we need to run a switch
463252726Srpaulo     * pass now.
464252726Srpaulo     */
465252726Srpaulo    if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
466252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
467252726Srpaulo			 "failed to switch resources from %s to D%d\n",
468252726Srpaulo			  acpi_name(consumer), state));
469252726Srpaulo
470252726Srpaulo	/* XXX is this appropriate?  Should we return to previous state? */
471252726Srpaulo	goto out;
472252726Srpaulo    }
473252726Srpaulo
474252726Srpaulo    /* Invoke power state switch method (if present) */
475252726Srpaulo    if (method_handle != NULL) {
476252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
477252726Srpaulo			 "invoking state transition method %s\n",
478252726Srpaulo			 acpi_name(method_handle)));
479252726Srpaulo	status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL);
480252726Srpaulo	if (ACPI_FAILURE(status)) {
481252726Srpaulo		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
482252726Srpaulo				 AcpiFormatException(status)));
483252726Srpaulo		pc->ac_state = ACPI_STATE_UNKNOWN;
484252726Srpaulo
485252726Srpaulo		/* XXX Should we return to previous state? */
486252726Srpaulo		goto out;
487252726Srpaulo	}
488252726Srpaulo    }
489252726Srpaulo
490252726Srpaulo    /* Transition was successful */
491252726Srpaulo    pc->ac_state = state;
492252726Srpaulo    status = AE_OK;
493252726Srpaulo
494252726Srpauloout:
495252726Srpaulo    ACPI_SERIAL_END(powerres);
496252726Srpaulo    if (reslist_buffer.Pointer != NULL)
497252726Srpaulo	AcpiOsFree(reslist_buffer.Pointer);
498252726Srpaulo    return_ACPI_STATUS (status);
499252726Srpaulo}
500252726Srpaulo
501252726Srpaulo/* Enable or disable a power resource for wake */
502252726SrpauloACPI_STATUS
503252726Srpauloacpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable)
504252726Srpaulo{
505252726Srpaulo    ACPI_STATUS status;
506252726Srpaulo    struct acpi_powerconsumer *pc;
507252726Srpaulo    struct acpi_prw_data prw;
508252726Srpaulo    int i;
509252726Srpaulo
510252726Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
511252726Srpaulo
512252726Srpaulo    if (consumer == NULL)
513252726Srpaulo	return (AE_BAD_PARAMETER);
514252726Srpaulo
515252726Srpaulo    ACPI_SERIAL_BEGIN(powerres);
516252726Srpaulo    if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
517252726Srpaulo	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
518252726Srpaulo	    goto out;
519252726Srpaulo	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
520252726Srpaulo	    panic("acpi wake added power consumer but can't find it");
521252726Srpaulo    }
522252726Srpaulo
523252726Srpaulo    status = AE_OK;
524252726Srpaulo    if (acpi_parse_prw(consumer, &prw) != 0)
525252726Srpaulo	goto out;
526252726Srpaulo    for (i = 0; i < prw.power_res_count; i++)
527252726Srpaulo	if (enable)
528252726Srpaulo	    acpi_pwr_reference_resource(&prw.power_res[i], pc);
529252726Srpaulo	else
530252726Srpaulo	    acpi_pwr_dereference_resource(pc);
531252726Srpaulo
532252726Srpaulo    if (prw.power_res_count > 0)
533252726Srpaulo	acpi_pwr_switch_power();
534252726Srpaulo
535252726Srpauloout:
536252726Srpaulo    ACPI_SERIAL_END(powerres);
537252726Srpaulo    return (status);
538252726Srpaulo}
539252726Srpaulo
540252726Srpaulo/*
541252726Srpaulo * Called to create a reference between a power consumer and a power resource
542252726Srpaulo * identified in the object.
543252726Srpaulo */
544252726Srpaulostatic void
545252726Srpauloacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
546252726Srpaulo{
547252726Srpaulo    struct acpi_powerconsumer	*pc = (struct acpi_powerconsumer *)arg;
548252726Srpaulo    struct acpi_powerreference	*pr;
549252726Srpaulo    struct acpi_powerresource	*rp;
550252726Srpaulo    ACPI_HANDLE			res;
551252726Srpaulo    ACPI_STATUS			status;
552252726Srpaulo
553252726Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
554252726Srpaulo    ACPI_SERIAL_ASSERT(powerres);
555252726Srpaulo
556252726Srpaulo    res = acpi_GetReference(NULL, obj);
557252726Srpaulo    if (res == NULL) {
558252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
559252726Srpaulo			 "can't create a power reference for object type %d\n",
560252726Srpaulo			 obj->Type));
561252726Srpaulo	return_VOID;
562252726Srpaulo    }
563252726Srpaulo
564252726Srpaulo    /* Create/look up the resource */
565252726Srpaulo    if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
566252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
567252726Srpaulo			 "couldn't register power resource %s - %s\n",
568252726Srpaulo			 obj->String.Pointer, AcpiFormatException(status)));
569252726Srpaulo	return_VOID;
570252726Srpaulo    }
571252726Srpaulo    if ((rp = acpi_pwr_find_resource(res)) == NULL) {
572252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
573252726Srpaulo	return_VOID;
574252726Srpaulo    }
575252726Srpaulo    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n",
576252726Srpaulo		     acpi_name(rp->ap_resource)));
577252726Srpaulo
578252726Srpaulo    /* Create a reference between the consumer and resource */
579252726Srpaulo    if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
580252726Srpaulo	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
581252726Srpaulo			 "allocation failed for a power consumer reference\n"));
582252726Srpaulo	return_VOID;
583252726Srpaulo    }
584252726Srpaulo    pr->ar_consumer = pc;
585252726Srpaulo    pr->ar_resource = rp;
586252726Srpaulo    TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
587214503Srpaulo    TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
588214503Srpaulo
589214503Srpaulo    return_VOID;
590214503Srpaulo}
591214503Srpaulo
592214503Srpaulostatic int
593214503Srpauloacpi_pwr_dereference_resource(struct acpi_powerconsumer *pc)
594214503Srpaulo{
595214503Srpaulo    struct acpi_powerreference *pr;
596214503Srpaulo    int changed;
597214503Srpaulo
598214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
599214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
600214503Srpaulo
601214503Srpaulo    changed = 0;
602214503Srpaulo    while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
603214503Srpaulo        ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n",
604214503Srpaulo                         acpi_name(pr->ar_resource->ap_resource)));
605214503Srpaulo        TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
606214503Srpaulo        TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
607214503Srpaulo        free(pr, M_ACPIPWR);
608214503Srpaulo        changed = 1;
609214503Srpaulo    }
610214503Srpaulo
611214503Srpaulo    return (changed);
612214503Srpaulo}
613214503Srpaulo
614214503Srpaulo/*
615214503Srpaulo * Switch power resources to conform to the desired state.
616214503Srpaulo *
617214503Srpaulo * Consumers may have modified the power resource list in an arbitrary
618214503Srpaulo * fashion; we sweep it in sequence order.
619214503Srpaulo */
620214503Srpaulostatic ACPI_STATUS
621214503Srpauloacpi_pwr_switch_power(void)
622214503Srpaulo{
623214503Srpaulo    struct acpi_powerresource	*rp;
624214503Srpaulo    ACPI_STATUS			status;
625214503Srpaulo    int				cur;
626214503Srpaulo
627214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
628214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
629214503Srpaulo
630214503Srpaulo    /*
631214503Srpaulo     * Sweep the list forwards turning things on.
632214503Srpaulo     */
633214503Srpaulo    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
634214503Srpaulo	if (TAILQ_FIRST(&rp->ap_references) == NULL) {
635214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
636214503Srpaulo			     "%s has no references, not turning on\n",
637214503Srpaulo			     acpi_name(rp->ap_resource)));
638214503Srpaulo	    continue;
639214503Srpaulo	}
640214503Srpaulo
641214503Srpaulo	/* We could cache this if we trusted it not to change under us */
642214503Srpaulo	status = acpi_GetInteger(rp->ap_resource, "_STA", &cur);
643252726Srpaulo	if (ACPI_FAILURE(status)) {
644252726Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
645214503Srpaulo			      acpi_name(rp->ap_resource), status));
646214503Srpaulo	    /* XXX is this correct?  Always switch if in doubt? */
647214503Srpaulo	    continue;
648214503Srpaulo	} else if (rp->ap_state == ACPI_PWR_UNK)
649214503Srpaulo	    rp->ap_state = cur;
650214503Srpaulo
651214503Srpaulo	/*
652214503Srpaulo	 * Switch if required.  Note that we ignore the result of the switch
653214503Srpaulo	 * effort; we don't know what to do if it fails, so checking wouldn't
654214503Srpaulo	 * help much.
655214503Srpaulo	 */
656214503Srpaulo	if (rp->ap_state != ACPI_PWR_ON) {
657214503Srpaulo	    status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL);
658214503Srpaulo	    if (ACPI_FAILURE(status)) {
659214503Srpaulo		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
660214503Srpaulo				 "failed to switch %s on - %s\n",
661214503Srpaulo				 acpi_name(rp->ap_resource),
662214503Srpaulo				 AcpiFormatException(status)));
663214503Srpaulo	    } else {
664214503Srpaulo		rp->ap_state = ACPI_PWR_ON;
665214503Srpaulo		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n",
666214503Srpaulo				 acpi_name(rp->ap_resource)));
667214503Srpaulo	    }
668214503Srpaulo	} else {
669214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n",
670214503Srpaulo			     acpi_name(rp->ap_resource)));
671214503Srpaulo	}
672214503Srpaulo    }
673214503Srpaulo
674214503Srpaulo    /* Sweep the list backwards turning things off. */
675214503Srpaulo    TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list,
676214503Srpaulo	ap_link) {
677214503Srpaulo
678214503Srpaulo	if (TAILQ_FIRST(&rp->ap_references) != NULL) {
679214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
680214503Srpaulo			     "%s has references, not turning off\n",
681214503Srpaulo			     acpi_name(rp->ap_resource)));
682214503Srpaulo	    continue;
683214503Srpaulo	}
684214503Srpaulo
685214503Srpaulo	/* We could cache this if we trusted it not to change under us */
686252726Srpaulo	status = acpi_GetInteger(rp->ap_resource, "_STA", &cur);
687252726Srpaulo	if (ACPI_FAILURE(status)) {
688252726Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
689252726Srpaulo			      acpi_name(rp->ap_resource), status));
690252726Srpaulo	    /* XXX is this correct?  Always switch if in doubt? */
691252726Srpaulo	    continue;
692252726Srpaulo	} else if (rp->ap_state == ACPI_PWR_UNK)
693214503Srpaulo	    rp->ap_state = cur;
694214503Srpaulo
695214503Srpaulo	/*
696214503Srpaulo	 * Switch if required.  Note that we ignore the result of the switch
697252726Srpaulo	 * effort; we don't know what to do if it fails, so checking wouldn't
698252726Srpaulo	 * help much.
699214503Srpaulo	 */
700252726Srpaulo	if (rp->ap_state != ACPI_PWR_OFF) {
701214503Srpaulo	    status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL);
702214503Srpaulo	    if (ACPI_FAILURE(status)) {
703214503Srpaulo		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
704214503Srpaulo				 "failed to switch %s off - %s\n",
705214503Srpaulo				 acpi_name(rp->ap_resource),
706214503Srpaulo				 AcpiFormatException(status)));
707214503Srpaulo	    } else {
708214503Srpaulo		rp->ap_state = ACPI_PWR_OFF;
709214503Srpaulo		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n",
710214503Srpaulo				 acpi_name(rp->ap_resource)));
711214503Srpaulo	    }
712214503Srpaulo	} else {
713214503Srpaulo	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n",
714214503Srpaulo			     acpi_name(rp->ap_resource)));
715214503Srpaulo	}
716214503Srpaulo    }
717214503Srpaulo
718214503Srpaulo    return_ACPI_STATUS (AE_OK);
719214503Srpaulo}
720214503Srpaulo
721214503Srpaulo/*
722214503Srpaulo * Find a power resource's control structure.
723214503Srpaulo */
724214503Srpaulostatic struct acpi_powerresource *
725214503Srpauloacpi_pwr_find_resource(ACPI_HANDLE res)
726214503Srpaulo{
727214503Srpaulo    struct acpi_powerresource	*rp;
728214503Srpaulo
729214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
730214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
731214503Srpaulo
732214503Srpaulo    TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
733214503Srpaulo	if (rp->ap_resource == res)
734214503Srpaulo	    break;
735214503Srpaulo    }
736214503Srpaulo
737214503Srpaulo    return_PTR (rp);
738214503Srpaulo}
739214503Srpaulo
740214503Srpaulo/*
741214503Srpaulo * Find a power consumer's control structure.
742214503Srpaulo */
743214503Srpaulostatic struct acpi_powerconsumer *
744214503Srpauloacpi_pwr_find_consumer(ACPI_HANDLE consumer)
745214503Srpaulo{
746214503Srpaulo    struct acpi_powerconsumer	*pc;
747214503Srpaulo
748214503Srpaulo    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
749214503Srpaulo    ACPI_SERIAL_ASSERT(powerres);
750214503Srpaulo
751214503Srpaulo    TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) {
752214503Srpaulo	if (pc->ac_consumer == consumer)
753214503Srpaulo	    break;
754214503Srpaulo    }
755214503Srpaulo
756214503Srpaulo    return_PTR (pc);
757214503Srpaulo}
758214503Srpaulo