1 /* 2 * acpi_power.c - ACPI Bus Power Management ($Revision: 39 $) 3 * 4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 22 * 23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 */ 25 26 /* 27 * ACPI power-managed devices may be controlled in two ways: 28 * 1. via "Device Specific (D-State) Control" 29 * 2. via "Power Resource Control". 30 * This module is used to manage devices relying on Power Resource Control. 31 * 32 * An ACPI "power resource object" describes a software controllable power 33 * plane, clock plane, or other resource used by a power managed device. 34 * A device may rely on multiple power resources, and a power resource 35 * may be shared by multiple devices. 36 */ 37 38 #include <linux/kernel.h> 39 #include <linux/module.h> 40 #include <linux/init.h> 41 #include <linux/types.h> 42 #include <linux/proc_fs.h> 43 #include <linux/seq_file.h> 44 #include <acpi/acpi_bus.h> 45 #include <acpi/acpi_drivers.h> 46 47 48 #define _COMPONENT ACPI_POWER_COMPONENT 49 ACPI_MODULE_NAME ("acpi_power") 50 51 #define ACPI_POWER_COMPONENT 0x00800000 52 #define ACPI_POWER_CLASS "power_resource" 53 #define ACPI_POWER_DRIVER_NAME "ACPI Power Resource Driver" 54 #define ACPI_POWER_DEVICE_NAME "Power Resource" 55 #define ACPI_POWER_FILE_INFO "info" 56 #define ACPI_POWER_FILE_STATUS "state" 57 #define ACPI_POWER_RESOURCE_STATE_OFF 0x00 58 #define ACPI_POWER_RESOURCE_STATE_ON 0x01 59 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF 60 61 static int acpi_power_add (struct acpi_device *device); 62 static int acpi_power_remove (struct acpi_device *device, int type); 63 static int acpi_power_open_fs(struct inode *inode, struct file *file); 64 65 static struct acpi_driver acpi_power_driver = { 66 .name = ACPI_POWER_DRIVER_NAME, 67 .class = ACPI_POWER_CLASS, 68 .ids = ACPI_POWER_HID, 69 .ops = { 70 .add = acpi_power_add, 71 .remove = acpi_power_remove, 72 }, 73 }; 74 75 struct acpi_power_resource 76 { 77 acpi_handle handle; 78 acpi_bus_id name; 79 u32 system_level; 80 u32 order; 81 int state; 82 int references; 83 }; 84 85 static struct list_head acpi_power_resource_list; 86 87 static struct file_operations acpi_power_fops = { 88 .open = acpi_power_open_fs, 89 .read = seq_read, 90 .llseek = seq_lseek, 91 .release = single_release, 92 }; 93 94 /* -------------------------------------------------------------------------- 95 Power Resource Management 96 -------------------------------------------------------------------------- */ 97 98 static int 99 acpi_power_get_context ( 100 acpi_handle handle, 101 struct acpi_power_resource **resource) 102 { 103 int result = 0; 104 struct acpi_device *device = NULL; 105 106 ACPI_FUNCTION_TRACE("acpi_power_get_context"); 107 108 if (!resource) 109 return_VALUE(-ENODEV); 110 111 result = acpi_bus_get_device(handle, &device); 112 if (result) { 113 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context [%p]\n", 114 handle)); 115 return_VALUE(result); 116 } 117 118 *resource = (struct acpi_power_resource *) acpi_driver_data(device); 119 if (!resource) 120 return_VALUE(-ENODEV); 121 122 return_VALUE(0); 123 } 124 125 126 static int 127 acpi_power_get_state ( 128 struct acpi_power_resource *resource) 129 { 130 acpi_status status = AE_OK; 131 unsigned long sta = 0; 132 133 ACPI_FUNCTION_TRACE("acpi_power_get_state"); 134 135 if (!resource) 136 return_VALUE(-EINVAL); 137 138 status = acpi_evaluate_integer(resource->handle, "_STA", NULL, &sta); 139 if (ACPI_FAILURE(status)) 140 return_VALUE(-ENODEV); 141 142 if (sta & 0x01) 143 resource->state = ACPI_POWER_RESOURCE_STATE_ON; 144 else 145 resource->state = ACPI_POWER_RESOURCE_STATE_OFF; 146 147 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n", 148 resource->name, resource->state?"on":"off")); 149 150 return_VALUE(0); 151 } 152 153 154 static int 155 acpi_power_get_list_state ( 156 struct acpi_handle_list *list, 157 int *state) 158 { 159 int result = 0; 160 struct acpi_power_resource *resource = NULL; 161 u32 i = 0; 162 163 ACPI_FUNCTION_TRACE("acpi_power_get_list_state"); 164 165 if (!list || !state) 166 return_VALUE(-EINVAL); 167 168 /* The state of the list is 'on' IFF all resources are 'on'. */ 169 170 for (i=0; i<list->count; i++) { 171 result = acpi_power_get_context(list->handles[i], &resource); 172 if (result) 173 return_VALUE(result); 174 result = acpi_power_get_state(resource); 175 if (result) 176 return_VALUE(result); 177 178 *state = resource->state; 179 180 if (*state != ACPI_POWER_RESOURCE_STATE_ON) 181 break; 182 } 183 184 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n", 185 *state?"on":"off")); 186 187 return_VALUE(result); 188 } 189 190 191 static int 192 acpi_power_on ( 193 acpi_handle handle) 194 { 195 int result = 0; 196 acpi_status status = AE_OK; 197 struct acpi_device *device = NULL; 198 struct acpi_power_resource *resource = NULL; 199 200 ACPI_FUNCTION_TRACE("acpi_power_on"); 201 202 result = acpi_power_get_context(handle, &resource); 203 if (result) 204 return_VALUE(result); 205 206 resource->references++; 207 208 if ((resource->references > 1) 209 || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) { 210 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", 211 resource->name)); 212 return_VALUE(0); 213 } 214 215 status = acpi_evaluate_object(resource->handle, "_ON", NULL, NULL); 216 if (ACPI_FAILURE(status)) 217 return_VALUE(-ENODEV); 218 219 result = acpi_power_get_state(resource); 220 if (result) 221 return_VALUE(result); 222 if (resource->state != ACPI_POWER_RESOURCE_STATE_ON) 223 return_VALUE(-ENOEXEC); 224 225 /* Update the power resource's _device_ power state */ 226 result = acpi_bus_get_device(resource->handle, &device); 227 if (result) 228 return_VALUE(result); 229 device->power.state = ACPI_STATE_D0; 230 231 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", 232 resource->name)); 233 234 return_VALUE(0); 235 } 236 237 238 static int 239 acpi_power_off_device ( 240 acpi_handle handle) 241 { 242 int result = 0; 243 acpi_status status = AE_OK; 244 struct acpi_device *device = NULL; 245 struct acpi_power_resource *resource = NULL; 246 247 ACPI_FUNCTION_TRACE("acpi_power_off_device"); 248 249 result = acpi_power_get_context(handle, &resource); 250 if (result) 251 return_VALUE(result); 252 253 if (resource->references) 254 resource->references--; 255 256 if (resource->references) { 257 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 258 "Resource [%s] is still in use, dereferencing\n", 259 device->pnp.bus_id)); 260 return_VALUE(0); 261 } 262 263 if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { 264 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", 265 device->pnp.bus_id)); 266 return_VALUE(0); 267 } 268 269 status = acpi_evaluate_object(resource->handle, "_OFF", NULL, NULL); 270 if (ACPI_FAILURE(status)) 271 return_VALUE(-ENODEV); 272 273 result = acpi_power_get_state(resource); 274 if (result) 275 return_VALUE(result); 276 if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF) 277 return_VALUE(-ENOEXEC); 278 279 /* Update the power resource's _device_ power state */ 280 result = acpi_bus_get_device(resource->handle, &device); 281 if (result) 282 return_VALUE(result); 283 device->power.state = ACPI_STATE_D3; 284 285 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", 286 resource->name)); 287 288 return_VALUE(0); 289 } 290 291 /* 292 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): 293 * 1. Power on the power resources required for the wakeup device 294 * 2. Enable _PSW (power state wake) for the device if present 295 */ 296 int acpi_enable_wakeup_device_power (struct acpi_device *dev) 297 { 298 union acpi_object arg = {ACPI_TYPE_INTEGER}; 299 struct acpi_object_list arg_list = {1, &arg}; 300 acpi_status status = AE_OK; 301 int i; 302 int ret = 0; 303 304 ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_power"); 305 if (!dev || !dev->wakeup.flags.valid) 306 return_VALUE(-1); 307 308 arg.integer.value = 1; 309 /* Open power resource */ 310 for (i = 0; i < dev->wakeup.resources.count; i++) { 311 ret = acpi_power_on(dev->wakeup.resources.handles[i]); 312 if (ret) { 313 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 314 "Error transition power state\n")); 315 dev->wakeup.flags.valid = 0; 316 return_VALUE(-1); 317 } 318 } 319 320 /* Execute PSW */ 321 status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); 322 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { 323 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n")); 324 dev->wakeup.flags.valid = 0; 325 ret = -1; 326 } 327 328 return_VALUE(ret); 329 } 330 331 /* 332 * Shutdown a wakeup device, counterpart of above method 333 * 1. Disable _PSW (power state wake) 334 * 2. Shutdown down the power resources 335 */ 336 int acpi_disable_wakeup_device_power (struct acpi_device *dev) 337 { 338 union acpi_object arg = {ACPI_TYPE_INTEGER}; 339 struct acpi_object_list arg_list = {1, &arg}; 340 acpi_status status = AE_OK; 341 int i; 342 int ret = 0; 343 344 ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device_power"); 345 346 if (!dev || !dev->wakeup.flags.valid) 347 return_VALUE(-1); 348 349 arg.integer.value = 0; 350 /* Execute PSW */ 351 status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); 352 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { 353 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n")); 354 dev->wakeup.flags.valid = 0; 355 return_VALUE(-1); 356 } 357 358 /* Close power resource */ 359 for (i = 0; i < dev->wakeup.resources.count; i++) { 360 ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); 361 if (ret) { 362 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 363 "Error transition power state\n")); 364 dev->wakeup.flags.valid = 0; 365 return_VALUE(-1); 366 } 367 } 368 369 return_VALUE(ret); 370 } 371 372 /* -------------------------------------------------------------------------- 373 Device Power Management 374 -------------------------------------------------------------------------- */ 375 376 int 377 acpi_power_get_inferred_state ( 378 struct acpi_device *device) 379 { 380 int result = 0; 381 struct acpi_handle_list *list = NULL; 382 int list_state = 0; 383 int i = 0; 384 385 ACPI_FUNCTION_TRACE("acpi_power_get_inferred_state"); 386 387 if (!device) 388 return_VALUE(-EINVAL); 389 390 device->power.state = ACPI_STATE_UNKNOWN; 391 392 /* 393 * We know a device's inferred power state when all the resources 394 * required for a given D-state are 'on'. 395 */ 396 for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) { 397 list = &device->power.states[i].resources; 398 if (list->count < 1) 399 continue; 400 401 result = acpi_power_get_list_state(list, &list_state); 402 if (result) 403 return_VALUE(result); 404 405 if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { 406 device->power.state = i; 407 return_VALUE(0); 408 } 409 } 410 411 device->power.state = ACPI_STATE_D3; 412 413 return_VALUE(0); 414 } 415 416 417 int 418 acpi_power_transition ( 419 struct acpi_device *device, 420 int state) 421 { 422 int result = 0; 423 struct acpi_handle_list *cl = NULL; /* Current Resources */ 424 struct acpi_handle_list *tl = NULL; /* Target Resources */ 425 int i = 0; 426 427 ACPI_FUNCTION_TRACE("acpi_power_transition"); 428 429 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) 430 return_VALUE(-EINVAL); 431 432 if ((device->power.state < ACPI_STATE_D0) || (device->power.state > ACPI_STATE_D3)) 433 return_VALUE(-ENODEV); 434 435 cl = &device->power.states[device->power.state].resources; 436 tl = &device->power.states[state].resources; 437 438 device->power.state = ACPI_STATE_UNKNOWN; 439 440 if (!cl->count && !tl->count) { 441 result = -ENODEV; 442 goto end; 443 } 444 445 /* TBD: Resources must be ordered. */ 446 447 /* 448 * First we reference all power resources required in the target list 449 * (e.g. so the device doesn't lose power while transitioning). 450 */ 451 for (i=0; i<tl->count; i++) { 452 result = acpi_power_on(tl->handles[i]); 453 if (result) 454 goto end; 455 } 456 457 /* 458 * Then we dereference all power resources used in the current list. 459 */ 460 for (i=0; i<cl->count; i++) { 461 result = acpi_power_off_device(cl->handles[i]); 462 if (result) 463 goto end; 464 } 465 466 /* We shouldn't change the state till all above operations succeed */ 467 device->power.state = state; 468 end: 469 if (result) 470 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 471 "Error transitioning device [%s] to D%d\n", 472 device->pnp.bus_id, state)); 473 474 return_VALUE(result); 475 } 476 477 478 /* -------------------------------------------------------------------------- 479 FS Interface (/proc) 480 -------------------------------------------------------------------------- */ 481 482 static struct proc_dir_entry *acpi_power_dir; 483 484 static int acpi_power_seq_show(struct seq_file *seq, void *offset) 485 { 486 struct acpi_power_resource *resource = NULL; 487 488 ACPI_FUNCTION_TRACE("acpi_power_seq_show"); 489 490 resource = (struct acpi_power_resource *)seq->private; 491 492 if (!resource) 493 goto end; 494 495 seq_puts(seq, "state: "); 496 switch (resource->state) { 497 case ACPI_POWER_RESOURCE_STATE_ON: 498 seq_puts(seq, "on\n"); 499 break; 500 case ACPI_POWER_RESOURCE_STATE_OFF: 501 seq_puts(seq, "off\n"); 502 break; 503 default: 504 seq_puts(seq, "unknown\n"); 505 break; 506 } 507 508 seq_printf(seq, "system level: S%d\n" 509 "order: %d\n" 510 "reference count: %d\n", 511 resource->system_level, 512 resource->order, 513 resource->references); 514 515 end: 516 return_VALUE(0); 517 } 518 519 static int acpi_power_open_fs(struct inode *inode, struct file *file) 520 { 521 return single_open(file, acpi_power_seq_show, PDE(inode)->data); 522 } 523 524 static int 525 acpi_power_add_fs ( 526 struct acpi_device *device) 527 { 528 struct proc_dir_entry *entry = NULL; 529 530 ACPI_FUNCTION_TRACE("acpi_power_add_fs"); 531 532 if (!device) 533 return_VALUE(-EINVAL); 534 535 if (!acpi_device_dir(device)) { 536 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), 537 acpi_power_dir); 538 if (!acpi_device_dir(device)) 539 return_VALUE(-ENODEV); 540 } 541 542 /* 'status' [R] */ 543 entry = create_proc_entry(ACPI_POWER_FILE_STATUS, 544 S_IRUGO, acpi_device_dir(device)); 545 if (!entry) 546 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 547 "Unable to create '%s' fs entry\n", 548 ACPI_POWER_FILE_STATUS)); 549 else { 550 entry->proc_fops = &acpi_power_fops; 551 entry->data = acpi_driver_data(device); 552 } 553 554 return_VALUE(0); 555 } 556 557 558 static int 559 acpi_power_remove_fs ( 560 struct acpi_device *device) 561 { 562 ACPI_FUNCTION_TRACE("acpi_power_remove_fs"); 563 564 if (acpi_device_dir(device)) { 565 remove_proc_entry(ACPI_POWER_FILE_STATUS, 566 acpi_device_dir(device)); 567 remove_proc_entry(acpi_device_bid(device), acpi_power_dir); 568 acpi_device_dir(device) = NULL; 569 } 570 571 return_VALUE(0); 572 } 573 574 575 /* -------------------------------------------------------------------------- 576 Driver Interface 577 -------------------------------------------------------------------------- */ 578 579 static int 580 acpi_power_add ( 581 struct acpi_device *device) 582 { 583 int result = 0; 584 acpi_status status = AE_OK; 585 struct acpi_power_resource *resource = NULL; 586 union acpi_object acpi_object; 587 struct acpi_buffer buffer = {sizeof(acpi_object), &acpi_object}; 588 589 ACPI_FUNCTION_TRACE("acpi_power_add"); 590 591 if (!device) 592 return_VALUE(-EINVAL); 593 594 resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL); 595 if (!resource) 596 return_VALUE(-ENOMEM); 597 memset(resource, 0, sizeof(struct acpi_power_resource)); 598 599 resource->handle = device->handle; 600 strcpy(resource->name, device->pnp.bus_id); 601 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); 602 strcpy(acpi_device_class(device), ACPI_POWER_CLASS); 603 acpi_driver_data(device) = resource; 604 605 /* Evalute the object to get the system level and resource order. */ 606 status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer); 607 if (ACPI_FAILURE(status)) { 608 result = -ENODEV; 609 goto end; 610 } 611 resource->system_level = acpi_object.power_resource.system_level; 612 resource->order = acpi_object.power_resource.resource_order; 613 614 result = acpi_power_get_state(resource); 615 if (result) 616 goto end; 617 618 switch (resource->state) { 619 case ACPI_POWER_RESOURCE_STATE_ON: 620 device->power.state = ACPI_STATE_D0; 621 break; 622 case ACPI_POWER_RESOURCE_STATE_OFF: 623 device->power.state = ACPI_STATE_D3; 624 break; 625 default: 626 device->power.state = ACPI_STATE_UNKNOWN; 627 break; 628 } 629 630 result = acpi_power_add_fs(device); 631 if (result) 632 goto end; 633 634 printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), 635 acpi_device_bid(device), resource->state?"on":"off"); 636 637 end: 638 if (result) 639 kfree(resource); 640 641 return_VALUE(result); 642 } 643 644 645 static int 646 acpi_power_remove ( 647 struct acpi_device *device, 648 int type) 649 { 650 struct acpi_power_resource *resource = NULL; 651 652 ACPI_FUNCTION_TRACE("acpi_power_remove"); 653 654 if (!device || !acpi_driver_data(device)) 655 return_VALUE(-EINVAL); 656 657 resource = (struct acpi_power_resource *) acpi_driver_data(device); 658 659 acpi_power_remove_fs(device); 660 661 kfree(resource); 662 663 return_VALUE(0); 664 } 665 666 667 static int __init acpi_power_init (void) 668 { 669 int result = 0; 670 671 ACPI_FUNCTION_TRACE("acpi_power_init"); 672 673 if (acpi_disabled) 674 return_VALUE(0); 675 676 INIT_LIST_HEAD(&acpi_power_resource_list); 677 678 acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir); 679 if (!acpi_power_dir) 680 return_VALUE(-ENODEV); 681 682 result = acpi_bus_register_driver(&acpi_power_driver); 683 if (result < 0) { 684 remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir); 685 return_VALUE(-ENODEV); 686 } 687 688 return_VALUE(0); 689 } 690 691 subsys_initcall(acpi_power_init); 692 693