xref: /freebsd/sys/dev/acpica/acpi_powerres.c (revision 02a8fadd2c4dc4b78d6d93d9d8b70e9348a6de6d)
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 static ACPI_STATUS	acpi_pwr_infer_state(struct acpi_powerconsumer *pc);
121 static ACPI_STATUS	acpi_pwr_get_state_locked(ACPI_HANDLE consumer, int *state);
122 
123 /*
124  * Register a power resource.
125  *
126  * It's OK to call this if we already know about the resource.
127  */
128 static ACPI_STATUS
acpi_pwr_register_resource(ACPI_HANDLE res)129 acpi_pwr_register_resource(ACPI_HANDLE res)
130 {
131     ACPI_STATUS			status;
132     ACPI_BUFFER			buf;
133     ACPI_OBJECT			*obj;
134     struct acpi_powerresource	*rp, *srp;
135 
136     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
137     ACPI_SERIAL_ASSERT(powerres);
138 
139     rp = NULL;
140     buf.Pointer = NULL;
141 
142     /* Look to see if we know about this resource */
143     if (acpi_pwr_find_resource(res) != NULL)
144 	return_ACPI_STATUS (AE_OK);		/* already know about it */
145 
146     /* Allocate a new resource */
147     if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
148 	status = AE_NO_MEMORY;
149 	goto out;
150     }
151     TAILQ_INIT(&rp->ap_references);
152     rp->ap_resource = res;
153 
154     /* Get the Power Resource object */
155     buf.Length = ACPI_ALLOCATE_BUFFER;
156     if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) {
157 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n"));
158 	goto out;
159     }
160     obj = buf.Pointer;
161     if (obj->Type != ACPI_TYPE_POWER) {
162 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
163 			 "questionable power resource object %s\n",
164 			 acpi_name(res)));
165 	status = AE_TYPE;
166 	goto out;
167     }
168     rp->ap_systemlevel = obj->PowerResource.SystemLevel;
169     rp->ap_order = obj->PowerResource.ResourceOrder;
170 
171     /* Sort the resource into the list */
172     status = AE_OK;
173     srp = TAILQ_FIRST(&acpi_powerresources);
174     if (srp == NULL || rp->ap_order < srp->ap_order) {
175 	TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link);
176 	goto done;
177     }
178     TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) {
179 	if (rp->ap_order < srp->ap_order) {
180 	    TAILQ_INSERT_BEFORE(srp, rp, ap_link);
181 	    goto done;
182 	}
183     }
184     TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link);
185 
186  done:
187     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
188 		     "registered power resource %s\n", acpi_name(res)));
189 
190  out:
191     if (buf.Pointer != NULL)
192 	AcpiOsFree(buf.Pointer);
193     if (ACPI_FAILURE(status) && rp != NULL)
194 	free(rp, M_ACPIPWR);
195     return_ACPI_STATUS (status);
196 }
197 
198 #ifdef notyet
199 /*
200  * Deregister a power resource.
201  */
202 static ACPI_STATUS
acpi_pwr_deregister_resource(ACPI_HANDLE res)203 acpi_pwr_deregister_resource(ACPI_HANDLE res)
204 {
205     struct acpi_powerresource	*rp;
206 
207     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
208     ACPI_SERIAL_ASSERT(powerres);
209 
210     rp = NULL;
211 
212     /* Find the resource */
213     if ((rp = acpi_pwr_find_resource(res)) == NULL)
214 	return_ACPI_STATUS (AE_BAD_PARAMETER);
215 
216     /* Check that there are no consumers referencing this resource */
217     if (TAILQ_FIRST(&rp->ap_references) != NULL)
218 	return_ACPI_STATUS (AE_BAD_PARAMETER);
219 
220     /* Pull it off the list and free it */
221     TAILQ_REMOVE(&acpi_powerresources, rp, ap_link);
222     free(rp, M_ACPIPWR);
223 
224     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n",
225 		     acpi_name(res)));
226 
227     return_ACPI_STATUS (AE_OK);
228 }
229 #endif /* notyet */
230 
231 /*
232  * Evaluate the _PRx (power resources each D-state depends on).  This also
233  * populates the acpi_powerresources queue with the power resources discovered
234  * during this step.
235  *
236  * ACPI 7.3.8 - 7.3.11 guarantee that _PRx will return the same data each
237  * time they are evaluated.
238  *
239  * If this function fails, acpi_pwr_deregister_consumer() must be called on the
240  * power consumer to free already allocated memory.
241  */
242 static ACPI_STATUS
acpi_pwr_get_power_resources(ACPI_HANDLE consumer,struct acpi_powerconsumer * pc)243 acpi_pwr_get_power_resources(ACPI_HANDLE consumer, struct acpi_powerconsumer *pc)
244 {
245     ACPI_INTEGER	status;
246     ACPI_STRING		reslist_name;
247     ACPI_HANDLE		reslist_handle;
248     ACPI_STRING		reslist_names[] = {"_PR0", "_PR1", "_PR2", "_PR3"};
249     ACPI_BUFFER		reslist;
250     ACPI_OBJECT		*reslist_object;
251     ACPI_OBJECT		*dep;
252     ACPI_HANDLE		*res;
253 
254     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
255     ACPI_SERIAL_ASSERT(powerres);
256 
257     MPASS(consumer != NULL);
258 
259     for (int state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) {
260 	pc->ac_prx[state].prx_has = false;
261 	pc->ac_prx[state].prx_count = 0;
262 	pc->ac_prx[state].prx_deps = NULL;
263 
264 	reslist_name = reslist_names[state - ACPI_STATE_D0];
265 	if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
266 	    continue;
267 
268 	reslist.Pointer = NULL;
269 	reslist.Length = ACPI_ALLOCATE_BUFFER;
270 	status = AcpiEvaluateObjectTyped(reslist_handle, NULL, NULL, &reslist,
271 					 ACPI_TYPE_PACKAGE);
272 	if (ACPI_FAILURE(status) || reslist.Pointer == NULL)
273 	    /*
274 	     * ACPI_ALLOCATE_BUFFER entails everything will be freed on error
275 	     * by AcpiEvaluateObjectTyped.
276 	     */
277 	    continue;
278 
279 	reslist_object = (ACPI_OBJECT *)reslist.Pointer;
280 	pc->ac_prx[state].prx_has = true;
281 	pc->ac_prx[state].prx_count = reslist_object->Package.Count;
282 
283 	if (reslist_object->Package.Count == 0) {
284 	    AcpiOsFree(reslist_object);
285 	    continue;
286 	}
287 
288 	pc->ac_prx[state].prx_deps = mallocarray(pc->ac_prx[state].prx_count,
289 	    sizeof(*pc->ac_prx[state].prx_deps), M_ACPIPWR, M_NOWAIT);
290 	if (pc->ac_prx[state].prx_deps == NULL) {
291 	    AcpiOsFree(reslist_object);
292 	    return_ACPI_STATUS (AE_NO_MEMORY);
293 	}
294 
295 	for (size_t i = 0; i < reslist_object->Package.Count; i++) {
296 	    dep = &reslist_object->Package.Elements[i];
297 	    res = dep->Reference.Handle;
298 	    pc->ac_prx[state].prx_deps[i] = res;
299 
300 	    /* It's fine to attempt to register the same resource twice. */
301 	    acpi_pwr_register_resource(res);
302 	}
303 	AcpiOsFree(reslist_object);
304     }
305 
306     return_ACPI_STATUS (AE_OK);
307 }
308 
309 /*
310  * Register a power consumer.
311  *
312  * It's OK to call this if we already know about the consumer.
313  */
314 static ACPI_STATUS
acpi_pwr_register_consumer(ACPI_HANDLE consumer)315 acpi_pwr_register_consumer(ACPI_HANDLE consumer)
316 {
317     ACPI_INTEGER		status;
318     struct acpi_powerconsumer	*pc;
319 
320     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
321     ACPI_SERIAL_ASSERT(powerres);
322 
323     /* Check to see whether we know about this consumer already */
324     if (acpi_pwr_find_consumer(consumer) != NULL)
325 	return_ACPI_STATUS (AE_OK);
326 
327     /* Allocate a new power consumer */
328     if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL)
329 	return_ACPI_STATUS (AE_NO_MEMORY);
330     TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link);
331     TAILQ_INIT(&pc->ac_references);
332     pc->ac_consumer = consumer;
333 
334     /*
335      * Get all its power resource dependencies, if it has _PRx.  We do this now
336      * as an opportunity to populate the acpi_powerresources queue.
337      *
338      * If this fails, immediately deregister it.
339      */
340     status = acpi_pwr_get_power_resources(consumer, pc);
341     if (ACPI_FAILURE(status)) {
342 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
343 			 "failed to get power resources for %s\n",
344 			 acpi_name(consumer)));
345 	acpi_pwr_deregister_consumer(consumer);
346 	return_ACPI_STATUS (status);
347     }
348 
349     /* Find its initial state. */
350     if (ACPI_FAILURE(acpi_pwr_get_state_locked(consumer, &pc->ac_state)))
351 	pc->ac_state = ACPI_STATE_UNKNOWN;
352 
353     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n",
354 		     acpi_name(consumer)));
355 
356     return_ACPI_STATUS (AE_OK);
357 }
358 
359 /*
360  * Deregister a power consumer.
361  *
362  * This should only be done once the consumer has been powered off.
363  * (XXX is this correct?  Check once implemented)
364  */
365 static ACPI_STATUS
acpi_pwr_deregister_consumer(ACPI_HANDLE consumer)366 acpi_pwr_deregister_consumer(ACPI_HANDLE consumer)
367 {
368     struct acpi_powerconsumer	*pc;
369 
370     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
371     ACPI_SERIAL_ASSERT(powerres);
372 
373     /* Find the consumer */
374     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
375 	return_ACPI_STATUS (AE_BAD_PARAMETER);
376 
377     /* Make sure the consumer's not referencing anything right now */
378     if (TAILQ_FIRST(&pc->ac_references) != NULL)
379 	return_ACPI_STATUS (AE_BAD_PARAMETER);
380 
381     /* Pull the consumer off the list and free it */
382     TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link);
383     for (size_t i = 0; i < sizeof(pc->ac_prx) / sizeof(*pc->ac_prx); i++)
384 	if (pc->ac_prx[i].prx_deps != NULL)
385 	    free(pc->ac_prx[i].prx_deps, M_ACPIPWR);
386     free(pc, M_ACPIPWR);
387 
388     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n",
389 		     acpi_name(consumer)));
390 
391     return_ACPI_STATUS (AE_OK);
392 }
393 
394 /*
395  * The _PSC control method isn't required if it's possible to infer the D-state
396  * from the _PRx control methods.  (See 7.3.6.)
397  * We can infer that a given D-state has been achieved when all the dependencies
398  * are in the ON state.
399  */
400 static ACPI_STATUS
acpi_pwr_infer_state(struct acpi_powerconsumer * pc)401 acpi_pwr_infer_state(struct acpi_powerconsumer *pc)
402 {
403     ACPI_HANDLE		*res;
404     uint32_t		on;
405     bool		all_on = false;
406 
407     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
408     ACPI_SERIAL_ASSERT(powerres);
409 
410     /* It is important we go from the hottest to the coldest state. */
411     for (
412 	pc->ac_state = ACPI_STATE_D0;
413 	pc->ac_state <= ACPI_STATE_D3_HOT && !all_on;
414 	pc->ac_state++
415     ) {
416 	MPASS(pc->ac_state <= sizeof(pc->ac_prx) / sizeof(*pc->ac_prx));
417 
418 	if (!pc->ac_prx[pc->ac_state].prx_has)
419 	    continue;
420 
421 	all_on = true;
422 
423 	for (size_t i = 0; i < pc->ac_prx[pc->ac_state].prx_count; i++) {
424 	    res = pc->ac_prx[pc->ac_state].prx_deps[i];
425 	    /* If failure, better to assume D-state is hotter than colder. */
426 	    if (ACPI_FAILURE(acpi_GetInteger(res, "_STA", &on)))
427 		continue;
428 	    if (on == 0) {
429 		all_on = false;
430 		break;
431 	    }
432 	}
433     }
434 
435     MPASS(pc->ac_state != ACPI_STATE_D0);
436 
437     /*
438      * If none of the power resources required for the shallower D-states are
439      * on, then we can assume it is unpowered (i.e. D3cold).  A device is not
440      * required to support D3cold however; in that case, _PR3 is not explicitly
441      * provided.  Those devices should default to D3hot instead.
442      *
443      * See comments of first row of table 7.1 in ACPI spec.
444      */
445     if (!all_on)
446 	pc->ac_state = pc->ac_prx[ACPI_STATE_D3_HOT].prx_has ?
447 	    ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT;
448     else
449 	pc->ac_state--;
450 
451     return_ACPI_STATUS (AE_OK);
452 }
453 
454 static ACPI_STATUS
acpi_pwr_get_state_locked(ACPI_HANDLE consumer,int * state)455 acpi_pwr_get_state_locked(ACPI_HANDLE consumer, int *state)
456 {
457     struct acpi_powerconsumer	*pc;
458     ACPI_HANDLE			method_handle;
459     ACPI_STATUS			status;
460     ACPI_BUFFER			result;
461     ACPI_OBJECT			*object = NULL;
462 
463     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
464     ACPI_SERIAL_ASSERT(powerres);
465 
466     if (consumer == NULL)
467 	return_ACPI_STATUS (AE_NOT_FOUND);
468 
469     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
470 	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
471 	    goto out;
472 	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
473 	    panic("acpi added power consumer but can't find it");
474     }
475 
476     status = AcpiGetHandle(consumer, "_PSC", &method_handle);
477     if (ACPI_FAILURE(status)) {
478 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no _PSC object - %s\n",
479 			 AcpiFormatException(status)));
480 	status = acpi_pwr_infer_state(pc);
481 	if (ACPI_FAILURE(status)) {
482 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't infer D-state - %s\n",
483 				 AcpiFormatException(status)));
484 		pc->ac_state = ACPI_STATE_UNKNOWN;
485 	}
486 	goto out;
487     }
488 
489     result.Pointer = NULL;
490     result.Length = ACPI_ALLOCATE_BUFFER;
491     status = AcpiEvaluateObjectTyped(method_handle, NULL, NULL, &result, ACPI_TYPE_INTEGER);
492     if (ACPI_FAILURE(status) || result.Pointer == NULL) {
493 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to get state with _PSC - %s\n",
494 			 AcpiFormatException(status)));
495 	pc->ac_state = ACPI_STATE_UNKNOWN;
496 	goto out;
497     }
498 
499     object = (ACPI_OBJECT *)result.Pointer;
500     pc->ac_state = ACPI_STATE_D0 + object->Integer.Value;
501     status = AE_OK;
502 
503 out:
504     if (object != NULL)
505 	AcpiOsFree(object);
506     *state = pc->ac_state;
507     return_ACPI_STATUS (status);
508 }
509 
510 /*
511  * Get a power consumer's D-state.
512  */
513 ACPI_STATUS
acpi_pwr_get_state(ACPI_HANDLE consumer,int * state)514 acpi_pwr_get_state(ACPI_HANDLE consumer, int *state)
515 {
516 	ACPI_STATUS	res;
517 
518 	ACPI_SERIAL_BEGIN(powerres);
519 	res = acpi_pwr_get_state_locked(consumer, state);
520 	ACPI_SERIAL_END(powerres);
521 	return (res);
522 }
523 
524 /*
525  * Set a power consumer to a particular D-state.
526  */
527 ACPI_STATUS
acpi_pwr_switch_consumer(ACPI_HANDLE consumer,int state)528 acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
529 {
530     struct acpi_powerconsumer	*pc;
531     ACPI_HANDLE			method_handle, reslist_handle, pr0_handle;
532     ACPI_BUFFER			reslist_buffer;
533     ACPI_OBJECT			*reslist_object;
534     ACPI_STATUS			status;
535     char			*method_name, *reslist_name = NULL;
536     int				new_state;
537 
538     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
539 
540     /* It's never ok to switch a non-existent consumer. */
541     if (consumer == NULL)
542 	return_ACPI_STATUS (AE_NOT_FOUND);
543     reslist_buffer.Pointer = NULL;
544     reslist_object = NULL;
545     ACPI_SERIAL_BEGIN(powerres);
546 
547     /* Find the consumer */
548     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
549 	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
550 	    goto out;
551 	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
552 	    panic("acpi added power consumer but can't find it");
553     }
554 
555     /* Stop here if we're already at the target D-state. */
556     if (pc->ac_state == state) {
557 	status = AE_OK;
558 	goto out;
559     }
560 
561     /*
562      * Check for valid transitions.  From D3hot or D3cold, we can only go to D0.
563      * The exception to this is going from D3hot to D3cold or the other way
564      * around.  This is because they both use _PS3, so the only difference when
565      * doing these transitions is whether or not the power resources for _PR3
566      * are on for devices which support D3cold, and turning these power
567      * resources on/off is always perfectly fine (ACPI 7.3.11).
568      */
569     status = AE_BAD_PARAMETER;
570     if (pc->ac_state == ACPI_STATE_D3_HOT && state != ACPI_STATE_D0 &&
571 	state != ACPI_STATE_D3_COLD)
572 	goto out;
573     if (pc->ac_state == ACPI_STATE_D3_COLD && state != ACPI_STATE_D0 &&
574 	state != ACPI_STATE_D3_HOT)
575 	goto out;
576 
577     /* Find transition mechanism(s) */
578     switch (state) {
579     case ACPI_STATE_D0:
580 	method_name = "_PS0";
581 	reslist_name = "_PR0";
582 	break;
583     case ACPI_STATE_D1:
584 	method_name = "_PS1";
585 	reslist_name = "_PR1";
586 	break;
587     case ACPI_STATE_D2:
588 	method_name = "_PS2";
589 	reslist_name = "_PR2";
590 	break;
591     case ACPI_STATE_D3_HOT:
592 	method_name = "_PS3";
593 	reslist_name = "_PR3";
594 	break;
595     case ACPI_STATE_D3_COLD:
596 	method_name = "_PS3";
597 	reslist_name = NULL;
598 	break;
599     default:
600 	goto out;
601     }
602     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s %s -> %s\n",
603 		     acpi_name(consumer), acpi_d_state_to_str(pc->ac_state),
604 		     acpi_d_state_to_str(state)));
605 
606     /*
607      * Verify that this state is supported, ie. one of method or
608      * reslist must be present.  We need to do this before we go
609      * dereferencing resources (since we might be trying to go to
610      * a state we don't support).
611      *
612      * Note that if any states are supported, the device has to
613      * support D0 and D3.  It's never an error to try to go to
614      * D0.
615      */
616     if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
617 	method_handle = NULL;
618     if (reslist_name == NULL ||
619 	ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
620 	reslist_handle = NULL;
621     if (reslist_handle == NULL && method_handle == NULL) {
622 	if (state == ACPI_STATE_D0) {
623 	    pc->ac_state = ACPI_STATE_D0;
624 	    status = AE_OK;
625 	    goto out;
626 	}
627 	if (state == ACPI_STATE_D3_COLD)
628 	    state = ACPI_STATE_D3_HOT;
629 	if (state != ACPI_STATE_D3_HOT) {
630 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
631 		"attempt to set unsupported state %s\n",
632 		acpi_d_state_to_str(state)));
633 	    goto out;
634 	}
635 
636 	/*
637 	 * Turn off the resources listed in _PR0 to go to D3.  If there is
638 	 * no _PR0 method, this object doesn't support ACPI power states.
639 	 */
640 	if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
641 	    status = AE_NOT_FOUND;
642 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
643 		"device missing _PR0 (desired state was %s)\n",
644 		acpi_d_state_to_str(state)));
645 	    goto out;
646 	}
647 	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
648 	status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer);
649 	if (ACPI_FAILURE(status)) {
650 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
651 		"can't evaluate _PR0 for device %s, state %s\n",
652 		acpi_name(consumer), acpi_d_state_to_str(state)));
653 	    goto out;
654 	}
655 	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
656 	if (!ACPI_PKG_VALID(reslist_object, 1)) {
657 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
658 		"invalid package object for state %s\n",
659 		acpi_d_state_to_str(state)));
660 	    status = AE_TYPE;
661 	    goto out;
662 	}
663 	AcpiOsFree(reslist_buffer.Pointer);
664 	reslist_buffer.Pointer = NULL;
665 	reslist_object = NULL;
666     }
667 
668     /*
669      * Check that we can actually fetch the list of power resources
670      */
671     if (reslist_handle != NULL) {
672 	reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
673 	status = AcpiEvaluateObject(reslist_handle, NULL, NULL,
674 				    &reslist_buffer);
675 	if (ACPI_FAILURE(status)) {
676 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
677 			     "can't evaluate resource list %s\n",
678 			     acpi_name(reslist_handle)));
679 	    goto out;
680 	}
681 	reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
682 	if (reslist_object->Type != ACPI_TYPE_PACKAGE) {
683 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
684 			     "resource list is not ACPI_TYPE_PACKAGE (%d)\n",
685 			     reslist_object->Type));
686 	    status = AE_TYPE;
687 	    goto out;
688 	}
689     }
690 
691     /*
692      * Now we are ready to switch, so kill off any current power
693      * resource references.
694      */
695     acpi_pwr_dereference_resource(pc);
696 
697     /*
698      * Add new power resource references, if we have any.  Traverse the
699      * package that we got from evaluating reslist_handle, and look up each
700      * of the resources that are referenced.
701      */
702     if (reslist_object != NULL) {
703 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n",
704 			  reslist_object->Package.Count));
705 	acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource,
706 				  pc);
707     }
708 
709     /*
710      * If we changed anything in the resource list, we need to run a switch
711      * pass now.
712      */
713     if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
714 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
715 			 "failed to switch resources from %s to %s\n",
716 			  acpi_name(consumer), acpi_d_state_to_str(state)));
717 
718 	/* XXX is this appropriate?  Should we return to previous state? */
719 	goto out;
720     }
721 
722     /* Invoke power state switch method (if present) */
723     if (method_handle != NULL) {
724 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
725 			 "invoking state transition method %s\n",
726 			 acpi_name(method_handle)));
727 	status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL);
728 	if (ACPI_FAILURE(status)) {
729 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n",
730 				 AcpiFormatException(status)));
731 		pc->ac_state = ACPI_STATE_UNKNOWN;
732 
733 		/* XXX Should we return to previous state? */
734 		goto out;
735 	}
736     }
737 
738     /*
739      * Make sure the transition succeeded.  If getting new state failed,
740      * just assume the new state is what we wanted.  This was the behaviour
741      * before we were checking D-states.
742      */
743     if (ACPI_FAILURE(acpi_pwr_get_state_locked(consumer, &new_state))) {
744 	printf("%s: failed to get new D-state\n", __func__);
745 	pc->ac_state = state;
746     } else {
747 	if (new_state != state)
748 	    printf("%s: new power state %s is not the one requested %s\n",
749 		   __func__, acpi_d_state_to_str(new_state),
750 		   acpi_d_state_to_str(state));
751 	pc->ac_state = new_state;
752     }
753 
754     /*
755      * We consider the transition successful even if the state we got doesn't
756      * reflect what we set it to.  This is because we weren't previously
757      * checking the new state at all, so there might exist buggy platforms on
758      * which suspend would otherwise succeed if we failed here.
759      */
760     status = AE_OK;
761 
762 out:
763     ACPI_SERIAL_END(powerres);
764     if (reslist_buffer.Pointer != NULL)
765 	AcpiOsFree(reslist_buffer.Pointer);
766     return_ACPI_STATUS (status);
767 }
768 
769 /* Enable or disable a power resource for wake */
770 ACPI_STATUS
acpi_pwr_wake_enable(ACPI_HANDLE consumer,int enable)771 acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable)
772 {
773     ACPI_STATUS status;
774     struct acpi_powerconsumer *pc;
775     struct acpi_prw_data prw;
776     int i;
777 
778     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
779 
780     if (consumer == NULL)
781 	return (AE_BAD_PARAMETER);
782 
783     ACPI_SERIAL_BEGIN(powerres);
784     if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) {
785 	if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer)))
786 	    goto out;
787 	if ((pc = acpi_pwr_find_consumer(consumer)) == NULL)
788 	    panic("acpi wake added power consumer but can't find it");
789     }
790 
791     status = AE_OK;
792     if (acpi_parse_prw(consumer, &prw) != 0)
793 	goto out;
794     for (i = 0; i < prw.power_res_count; i++)
795 	if (enable)
796 	    acpi_pwr_reference_resource(&prw.power_res[i], pc);
797 	else
798 	    acpi_pwr_dereference_resource(pc);
799 
800     if (prw.power_res_count > 0)
801 	acpi_pwr_switch_power();
802 
803 out:
804     ACPI_SERIAL_END(powerres);
805     return (status);
806 }
807 
808 /*
809  * Called to create a reference between a power consumer and a power resource
810  * identified in the object.
811  */
812 static void
acpi_pwr_reference_resource(ACPI_OBJECT * obj,void * arg)813 acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg)
814 {
815     struct acpi_powerconsumer	*pc = (struct acpi_powerconsumer *)arg;
816     struct acpi_powerreference	*pr;
817     struct acpi_powerresource	*rp;
818     ACPI_HANDLE			res;
819     ACPI_STATUS			status;
820 
821     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
822     ACPI_SERIAL_ASSERT(powerres);
823 
824     res = acpi_GetReference(NULL, obj);
825     if (res == NULL) {
826 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
827 			 "can't create a power reference for object type %d\n",
828 			 obj->Type));
829 	return_VOID;
830     }
831 
832     /* Create/look up the resource */
833     if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) {
834 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
835 			 "couldn't register power resource %s - %s\n",
836 			 obj->String.Pointer, AcpiFormatException(status)));
837 	return_VOID;
838     }
839     if ((rp = acpi_pwr_find_resource(res)) == NULL) {
840 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n"));
841 	return_VOID;
842     }
843     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n",
844 		     acpi_name(rp->ap_resource)));
845 
846     /* Create a reference between the consumer and resource */
847     if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) {
848 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
849 			 "allocation failed for a power consumer reference\n"));
850 	return_VOID;
851     }
852     pr->ar_consumer = pc;
853     pr->ar_resource = rp;
854     TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink);
855     TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink);
856 
857     return_VOID;
858 }
859 
860 static int
acpi_pwr_dereference_resource(struct acpi_powerconsumer * pc)861 acpi_pwr_dereference_resource(struct acpi_powerconsumer *pc)
862 {
863     struct acpi_powerreference *pr;
864     int changed;
865 
866     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
867     ACPI_SERIAL_ASSERT(powerres);
868 
869     changed = 0;
870     while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) {
871         ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n",
872                          acpi_name(pr->ar_resource->ap_resource)));
873         TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink);
874         TAILQ_REMOVE(&pc->ac_references, pr, ar_clink);
875         free(pr, M_ACPIPWR);
876         changed = 1;
877     }
878 
879     return (changed);
880 }
881 
882 /*
883  * Switch power resources to conform to the desired state.
884  *
885  * Consumers may have modified the power resource list in an arbitrary
886  * fashion; we sweep it in sequence order.
887  */
888 static ACPI_STATUS
acpi_pwr_switch_power(void)889 acpi_pwr_switch_power(void)
890 {
891     struct acpi_powerresource	*rp;
892     ACPI_STATUS			status;
893     int				cur;
894 
895     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
896     ACPI_SERIAL_ASSERT(powerres);
897 
898     /*
899      * Sweep the list forwards turning things on.
900      */
901     TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
902 	if (TAILQ_FIRST(&rp->ap_references) == NULL) {
903 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
904 			     "%s has no references, not turning on\n",
905 			     acpi_name(rp->ap_resource)));
906 	    continue;
907 	}
908 
909 	status = acpi_GetInteger(rp->ap_resource, "_STA", &cur);
910 	if (ACPI_FAILURE(status)) {
911 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
912 			      acpi_name(rp->ap_resource), status));
913 	    /* XXX is this correct?  Always switch if in doubt? */
914 	    continue;
915 	}
916 
917 	/*
918 	 * Switch if required.  Note that we ignore the result of the switch
919 	 * effort; we don't know what to do if it fails, so checking wouldn't
920 	 * help much.
921 	 */
922 	if (cur != ACPI_PWR_ON) {
923 	    status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL);
924 	    if (ACPI_FAILURE(status)) {
925 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
926 				 "failed to switch %s on - %s\n",
927 				 acpi_name(rp->ap_resource),
928 				 AcpiFormatException(status)));
929 	    } else {
930 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n",
931 				 acpi_name(rp->ap_resource)));
932 	    }
933 	} else {
934 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n",
935 			     acpi_name(rp->ap_resource)));
936 	}
937     }
938 
939     /* Sweep the list backwards turning things off. */
940     TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list,
941 	ap_link) {
942 	if (TAILQ_FIRST(&rp->ap_references) != NULL) {
943 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
944 			     "%s has references, not turning off\n",
945 			     acpi_name(rp->ap_resource)));
946 	    continue;
947 	}
948 
949 	status = acpi_GetInteger(rp->ap_resource, "_STA", &cur);
950 	if (ACPI_FAILURE(status)) {
951 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n",
952 			      acpi_name(rp->ap_resource), status));
953 	    /* XXX is this correct?  Always switch if in doubt? */
954 	    continue;
955 	}
956 
957 	/*
958 	 * Switch if required.  Note that we ignore the result of the switch
959 	 * effort; we don't know what to do if it fails, so checking wouldn't
960 	 * help much.
961 	 */
962 	if (cur != ACPI_PWR_OFF) {
963 	    status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL);
964 	    if (ACPI_FAILURE(status)) {
965 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
966 				 "failed to switch %s off - %s\n",
967 				 acpi_name(rp->ap_resource),
968 				 AcpiFormatException(status)));
969 	    } else {
970 		ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n",
971 				 acpi_name(rp->ap_resource)));
972 	    }
973 	} else {
974 	    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n",
975 			     acpi_name(rp->ap_resource)));
976 	}
977     }
978 
979     return_ACPI_STATUS (AE_OK);
980 }
981 
982 /*
983  * Find a power resource's control structure.
984  */
985 static struct acpi_powerresource *
acpi_pwr_find_resource(ACPI_HANDLE res)986 acpi_pwr_find_resource(ACPI_HANDLE res)
987 {
988     struct acpi_powerresource	*rp;
989 
990     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
991     ACPI_SERIAL_ASSERT(powerres);
992 
993     TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) {
994 	if (rp->ap_resource == res)
995 	    break;
996     }
997 
998     return_PTR (rp);
999 }
1000 
1001 /*
1002  * Find a power consumer's control structure.
1003  */
1004 static struct acpi_powerconsumer *
acpi_pwr_find_consumer(ACPI_HANDLE consumer)1005 acpi_pwr_find_consumer(ACPI_HANDLE consumer)
1006 {
1007     struct acpi_powerconsumer	*pc;
1008 
1009     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1010     ACPI_SERIAL_ASSERT(powerres);
1011 
1012     TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) {
1013 	if (pc->ac_consumer == consumer)
1014 	    break;
1015     }
1016 
1017     return_PTR (pc);
1018 }
1019