1 /*- 2 * Copyright (c) 2001 Michael Smith 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include "opt_acpi.h" /* XXX trim includes */ 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/proc.h> 33 #include <sys/lock.h> 34 #include <sys/malloc.h> 35 #include <sys/mutex.h> 36 #include <sys/bus.h> 37 #include <sys/conf.h> 38 #include <sys/ioccom.h> 39 #include <sys/reboot.h> 40 #include <sys/sysctl.h> 41 #include <sys/systm.h> 42 #include <sys/ctype.h> 43 44 #include <machine/clock.h> 45 46 #include <machine/resource.h> 47 48 #include "acpi.h" 49 50 #include <dev/acpica/acpivar.h> 51 #include <dev/acpica/acpiio.h> 52 53 /* 54 * ACPI power resource management. 55 * 56 * Power resource behaviour is slightly complicated by the fact that 57 * a single power resource may provide power for more than one device. 58 * Thus, we must track the device(s) being powered by a given power 59 * resource, and only deactivate it when there are no powered devices. 60 * 61 * Note that this only manages resources for known devices. There is an 62 * ugly case where we may turn of power to a device which is in use because 63 * we don't know that it depends on a given resource. We should perhaps 64 * try to be smarter about this, but a more complete solution would involve 65 * scanning all of the ACPI namespace to find devices we're not currently 66 * aware of, and this raises questions about whether they should be left 67 * on, turned off, etc. 68 * 69 * XXX locking 70 */ 71 72 MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 73 74 /* 75 * Hooks for the ACPI CA debugging infrastructure 76 */ 77 #define _COMPONENT ACPI_POWER 78 MODULE_NAME("POWERRES") 79 80 /* 81 * A relationship between a power resource and a consumer. 82 */ 83 struct acpi_powerreference { 84 struct acpi_powerconsumer *ar_consumer; 85 struct acpi_powerresource *ar_resource; 86 TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 87 TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 88 }; 89 90 /* 91 * A power-managed device. 92 */ 93 struct acpi_powerconsumer { 94 ACPI_HANDLE ac_consumer; /* device which is powered */ 95 int ac_state; 96 TAILQ_ENTRY(acpi_powerconsumer) ac_link; 97 TAILQ_HEAD(,acpi_powerreference) ac_references; 98 }; 99 100 /* 101 * A power resource. 102 */ 103 struct acpi_powerresource { 104 TAILQ_ENTRY(acpi_powerresource) ap_link; 105 TAILQ_HEAD(,acpi_powerreference) ap_references; 106 ACPI_HANDLE ap_resource; /* the resource's handle */ 107 ACPI_INTEGER ap_systemlevel; 108 ACPI_INTEGER ap_order; 109 int ap_state; 110 #define ACPI_PWR_OFF 0 111 #define ACPI_PWR_ON 1 112 }; 113 114 TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; 115 TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; 116 117 static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 118 static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 119 static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 120 static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 121 static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); 122 static ACPI_STATUS acpi_pwr_switch_power(void); 123 static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); 124 static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 125 126 /* 127 * Initialise our lists. 128 */ 129 static void 130 acpi_pwr_init(void *junk) 131 { 132 TAILQ_INIT(&acpi_powerresources); 133 TAILQ_INIT(&acpi_powerconsumers); 134 } 135 SYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 136 137 /* 138 * Register a power resource. 139 * 140 * It's OK to call this if we already know about the resource. 141 */ 142 static ACPI_STATUS 143 acpi_pwr_register_resource(ACPI_HANDLE res) 144 { 145 ACPI_STATUS status; 146 ACPI_BUFFER buf; 147 ACPI_OPERAND_OBJECT *obj; 148 struct acpi_powerresource *rp, *srp; 149 150 FUNCTION_TRACE(__func__); 151 152 rp = NULL; 153 obj = NULL; 154 155 /* look to see if we know about this resource */ 156 if (acpi_pwr_find_resource(res) != NULL) 157 return_ACPI_STATUS(AE_OK); /* already know about it */ 158 159 /* allocate a new resource */ 160 if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 161 status = AE_NO_MEMORY; 162 goto out; 163 } 164 TAILQ_INIT(&rp->ap_references); 165 rp->ap_resource = res; 166 167 /* get the Power Resource object */ 168 if ((status = acpi_EvaluateIntoBuffer(res, NULL, NULL, &buf)) != AE_OK) { 169 DEBUG_PRINT(TRACE_OBJECTS, ("no power resource object\n")); 170 goto out; 171 } 172 obj = buf.Pointer; 173 if (obj->Common.Type != ACPI_TYPE_POWER) { 174 DEBUG_PRINT(TRACE_OBJECTS, ("bad power resource object\n")); 175 status = AE_TYPE; 176 goto out; 177 } 178 rp->ap_systemlevel = obj->PowerResource.SystemLevel; 179 rp->ap_order = obj->PowerResource.ResourceOrder; 180 181 /* get the current state of the resource */ 182 if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &rp->ap_state)) != AE_OK) { 183 /* XXX is this an error? */ 184 DEBUG_PRINT(TRACE_OBJECTS, ("can't get current power resource state\n")); 185 goto out; 186 } 187 188 /* sort the resource into the list */ 189 status = AE_OK; 190 srp = TAILQ_FIRST(&acpi_powerresources); 191 if ((srp == NULL) || (rp->ap_order < srp->ap_order)) { 192 TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 193 goto out; 194 } 195 TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) 196 if (rp->ap_order < srp->ap_order) { 197 TAILQ_INSERT_BEFORE(srp, rp, ap_link); 198 goto out; 199 } 200 TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 201 DEBUG_PRINT(TRACE_OBJECTS, ("registered power resource %s\n", acpi_name(res))); 202 203 out: 204 if (obj != NULL) 205 AcpiOsFree(obj); 206 if ((status != AE_OK) && (rp != NULL)) 207 free(rp, M_ACPIPWR); 208 return_ACPI_STATUS(status); 209 } 210 211 /* 212 * Deregister a power resource. 213 */ 214 static ACPI_STATUS 215 acpi_pwr_deregister_resource(ACPI_HANDLE res) 216 { 217 struct acpi_powerresource *rp; 218 219 FUNCTION_TRACE(__func__); 220 221 rp = NULL; 222 223 /* find the resource */ 224 if ((rp = acpi_pwr_find_resource(res)) == NULL) 225 return_ACPI_STATUS(AE_BAD_PARAMETER); 226 227 /* check that there are no consumers referencing this resource */ 228 if (TAILQ_FIRST(&rp->ap_references) != NULL) 229 return_ACPI_STATUS(AE_BAD_PARAMETER); 230 231 /* pull it off the list and free it */ 232 TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 233 free(rp, M_ACPIPWR); 234 235 DEBUG_PRINT(TRACE_OBJECTS, ("deregistered power resource %s\n", acpi_name(res))); 236 237 return_ACPI_STATUS(AE_OK); 238 } 239 240 /* 241 * Register a power consumer. 242 * 243 * It's OK to call this if we already know about the consumer. 244 */ 245 static ACPI_STATUS 246 acpi_pwr_register_consumer(ACPI_HANDLE consumer) 247 { 248 struct acpi_powerconsumer *pc; 249 250 FUNCTION_TRACE(__func__); 251 252 /* check to see whether we know about this consumer already */ 253 if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 254 return_ACPI_STATUS(AE_OK); 255 256 /* allocate a new power consumer */ 257 if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 258 return_ACPI_STATUS(AE_NO_MEMORY); 259 TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 260 TAILQ_INIT(&pc->ac_references); 261 pc->ac_consumer = consumer; 262 263 pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX we should try to find its current state */ 264 265 DEBUG_PRINT(TRACE_OBJECTS, ("registered power consumer %s\n", acpi_name(consumer))); 266 267 return_ACPI_STATUS(AE_OK); 268 } 269 270 /* 271 * Deregister a power consumer. 272 * 273 * This should only be done once the consumer has been powered off. 274 * (XXX is this correct? Check once implemented) 275 */ 276 static ACPI_STATUS 277 acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 278 { 279 struct acpi_powerconsumer *pc; 280 281 FUNCTION_TRACE(__func__); 282 283 /* find the consumer */ 284 if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 285 return_ACPI_STATUS(AE_BAD_PARAMETER); 286 287 /* make sure the consumer's not referencing anything right now */ 288 if (TAILQ_FIRST(&pc->ac_references) != NULL) 289 return_ACPI_STATUS(AE_BAD_PARAMETER); 290 291 /* pull the consumer off the list and free it */ 292 TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 293 294 DEBUG_PRINT(TRACE_OBJECTS, ("deregistered power consumer %s\n", acpi_name(consumer))); 295 296 return_ACPI_STATUS(AE_OK); 297 } 298 299 /* 300 * Set a power consumer to a particular power state. 301 */ 302 ACPI_STATUS 303 acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 304 { 305 struct acpi_powerconsumer *pc; 306 struct acpi_powerreference *pr; 307 ACPI_HANDLE method_handle, reslist_handle; 308 ACPI_BUFFER reslist_buffer; 309 ACPI_OBJECT *reslist_object; 310 ACPI_STATUS status; 311 char *method_name, *reslist_name; 312 int res_changed; 313 314 FUNCTION_TRACE(__func__); 315 316 /* find the consumer */ 317 if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 318 if ((status = acpi_pwr_register_consumer(consumer)) != AE_OK) 319 return_ACPI_STATUS(status); 320 if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 321 return_ACPI_STATUS(AE_ERROR); /* something very wrong */ 322 } 323 } 324 325 /* check for valid transitions */ 326 if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0)) 327 return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 328 329 /* find transition mechanism(s) */ 330 switch(state) { 331 case ACPI_STATE_D0: 332 method_name = "_PS0"; 333 reslist_name = "_PR0"; 334 break; 335 case ACPI_STATE_D1: 336 method_name = "_PS1"; 337 reslist_name = "_PR1"; 338 break; 339 case ACPI_STATE_D2: 340 method_name = "_PS2"; 341 reslist_name = "_PR2"; 342 break; 343 case ACPI_STATE_D3: 344 method_name = "_PS3"; 345 reslist_name = "_PR3"; 346 break; 347 default: 348 return_ACPI_STATUS(AE_BAD_PARAMETER); 349 } 350 DEBUG_PRINT(TRACE_OBJECTS, ("setup to switch %s D%d -> D%d\n", 351 acpi_name(consumer), pc->ac_state, state)); 352 353 /* 354 * Verify that this state is supported, ie. one of method or 355 * reslist must be present. We need to do this before we go 356 * dereferencing resources (since we might be trying to go to 357 * a state we don't support). 358 * 359 * Note that if any states are supported, the device has to 360 * support D0 and D3. It's never an error to try to go to 361 * D0. 362 */ 363 if (AcpiGetHandle(consumer, method_name, &method_handle) != AE_OK) 364 method_handle = NULL; 365 if (AcpiGetHandle(consumer, reslist_name, &reslist_handle) != AE_OK) 366 reslist_handle = NULL; 367 if ((reslist_handle == NULL) && (method_handle == NULL)) { 368 if (state == ACPI_STATE_D0) { 369 pc->ac_state = ACPI_STATE_D0; 370 return_ACPI_STATUS(AE_OK); 371 } 372 DEBUG_PRINT(TRACE_OBJECTS, ("attempt to set unsupported state D%d\n", 373 state)); 374 return_ACPI_STATUS(AE_BAD_PARAMETER); 375 } 376 377 /* 378 * Check that we can actually fetch the list of power resources 379 */ 380 if (reslist_handle != NULL) { 381 if ((status = acpi_EvaluateIntoBuffer(reslist_handle, NULL, NULL, &reslist_buffer)) != AE_OK) { 382 DEBUG_PRINT(TRACE_OBJECTS, ("can't evaluate resource list %s\n", 383 acpi_name(reslist_handle))); 384 return_ACPI_STATUS(status); 385 } 386 reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 387 if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 388 DEBUG_PRINT(TRACE_OBJECTS, ("resource list is not ACPI_TYPE_PACKAGE (%d)\n", 389 reslist_object->Type)); 390 return_ACPI_STATUS(AE_TYPE); 391 } 392 } else { 393 reslist_object = NULL; 394 } 395 396 /* 397 * Now we are ready to switch, so kill off any current power resource references. 398 */ 399 res_changed = 0; 400 TAILQ_FOREACH(pr, &pc->ac_references, ar_clink) { 401 TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 402 res_changed = 1; 403 } 404 405 /* 406 * Add new power resource references, if we have any. Traverse the 407 * package that we got from evaluating reslist_handle, and look up each 408 * of the resources that are referenced. 409 */ 410 if (reslist_object != NULL) { 411 DEBUG_PRINT(TRACE_OBJECTS, ("referencing %d new resources\n", 412 reslist_object->Package.Count)); 413 acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); 414 res_changed = 1; 415 } 416 417 /* 418 * If we changed anything in the resource list, we need to run a switch 419 * pass now. 420 */ 421 if ((status = acpi_pwr_switch_power()) != AE_OK) { 422 DEBUG_PRINT(TRACE_OBJECTS, ("failed to correctly switch resources to move %s to D%d\n", 423 acpi_name(consumer), state)); 424 return_ACPI_STATUS(status); /* XXX is this appropriate? Should we return to previous state? */ 425 } 426 427 /* invoke power state switch method (if present) */ 428 if (method_handle != NULL) { 429 DEBUG_PRINT(TRACE_OBJECTS, ("invoking state transition method %s\n", 430 acpi_name(method_handle))); 431 if ((status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL)) != AE_OK) 432 pc->ac_state = ACPI_STATE_UNKNOWN; 433 return_ACPI_STATUS(status); /* XXX is this appropriate? Should we return to previous state? */ 434 } 435 436 /* transition was successful */ 437 pc->ac_state = state; 438 return_ACPI_STATUS(AE_OK); 439 } 440 441 /* 442 * Called to create a reference between a power consumer and a power resource 443 * identified in the object. 444 */ 445 static void 446 acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 447 { 448 struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 449 450 FUNCTION_TRACE(__func__); 451 452 DEBUG_PRINT(TRACE_OBJECTS, ("called to create a reference using object type %d\n", 453 obj->Type)); 454 455 return_VOID; 456 } 457 458 459 /* 460 * Switch power resources to conform to the desired state. 461 * 462 * Consumers may have modified the power resource list in an arbitrary 463 * fashion; we sweep it in sequence order. 464 */ 465 static ACPI_STATUS 466 acpi_pwr_switch_power(void) 467 { 468 struct acpi_powerresource *rp; 469 ACPI_STATUS status; 470 int cur; 471 472 FUNCTION_TRACE(__func__); 473 474 /* 475 * Sweep the list forwards turning things on. 476 */ 477 TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 478 if (rp->ap_state != ACPI_PWR_ON) 479 continue; /* not turning this one on */ 480 481 /* we could cache this if we trusted it not to change under us */ 482 if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) { 483 DEBUG_PRINT(TRACE_OBJECTS, ("can't get status of %s - %d\n", 484 acpi_name(rp->ap_resource), status)); 485 continue; /* XXX is this correct? Always switch if in doubt? */ 486 } 487 488 /* 489 * Switch if required. Note that we ignore the result of the switch 490 * effort; we don't know what to do if it fails, so checking wouldn't 491 * help much. 492 */ 493 if (cur != ACPI_PWR_ON) 494 AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 495 } 496 497 /* 498 * Sweep the list backwards turning things off. 499 */ 500 TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { 501 if (rp->ap_state != ACPI_PWR_OFF) 502 continue; /* not turning this one off */ 503 504 /* we could cache this if we trusted it not to change under us */ 505 if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) { 506 DEBUG_PRINT(TRACE_OBJECTS, ("can't get status of %s - %d\n", 507 acpi_name(rp->ap_resource), status)); 508 continue; /* XXX is this correct? Always switch if in doubt? */ 509 } 510 511 /* 512 * Switch if required. Note that we ignore the result of the switch 513 * effort; we don't know what to do if it fails, so checking wouldn't 514 * help much. 515 */ 516 if (cur != ACPI_PWR_OFF) 517 AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 518 } 519 return_ACPI_STATUS(AE_OK); 520 } 521 522 /* 523 * Find a power resource's control structure. 524 */ 525 static struct acpi_powerresource * 526 acpi_pwr_find_resource(ACPI_HANDLE res) 527 { 528 struct acpi_powerresource *rp; 529 530 FUNCTION_TRACE(__func__); 531 532 TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) 533 if (rp->ap_resource == res) 534 break; 535 return_VALUE(rp); 536 } 537 538 /* 539 * Find a power consumer's control structure. 540 */ 541 static struct acpi_powerconsumer * 542 acpi_pwr_find_consumer(ACPI_HANDLE consumer) 543 { 544 struct acpi_powerconsumer *pc; 545 546 FUNCTION_TRACE(__func__); 547 548 TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) 549 if (pc->ac_consumer == consumer) 550 break; 551 return_VALUE(pc); 552 } 553 554