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