1 /* $NetBSD: kern_pmf.c,v 1.28 2009/07/08 18:53:36 dyoung Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.28 2009/07/08 18:53:36 dyoung Exp $"); 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <sys/kmem.h> 35 #include <sys/buf.h> 36 #include <sys/callout.h> 37 #include <sys/kernel.h> 38 #include <sys/device.h> 39 #include <sys/pmf.h> 40 #include <sys/queue.h> 41 #include <sys/sched.h> 42 #include <sys/syscallargs.h> /* for sys_sync */ 43 #include <sys/workqueue.h> 44 #include <prop/proplib.h> 45 #include <sys/condvar.h> 46 #include <sys/mutex.h> 47 #include <sys/proc.h> 48 #include <sys/reboot.h> /* for RB_NOSYNC */ 49 #include <sys/sched.h> 50 51 /* XXX ugly special case, but for now the only client */ 52 #include "wsdisplay.h" 53 #if NWSDISPLAY > 0 54 #include <dev/wscons/wsdisplayvar.h> 55 #endif 56 57 #ifdef PMF_DEBUG 58 int pmf_debug_event; 59 int pmf_debug_idle; 60 int pmf_debug_transition; 61 62 #define PMF_EVENT_PRINTF(x) if (pmf_debug_event) printf x 63 #define PMF_IDLE_PRINTF(x) if (pmf_debug_idle) printf x 64 #define PMF_TRANSITION_PRINTF(x) if (pmf_debug_transition) printf x 65 #define PMF_TRANSITION_PRINTF2(y,x) if (pmf_debug_transition>y) printf x 66 #else 67 #define PMF_EVENT_PRINTF(x) do { } while (0) 68 #define PMF_IDLE_PRINTF(x) do { } while (0) 69 #define PMF_TRANSITION_PRINTF(x) do { } while (0) 70 #define PMF_TRANSITION_PRINTF2(y,x) do { } while (0) 71 #endif 72 73 /* #define PMF_DEBUG */ 74 75 MALLOC_DEFINE(M_PMF, "pmf", "device pmf messaging memory"); 76 77 static prop_dictionary_t pmf_platform = NULL; 78 static struct workqueue *pmf_event_workqueue; 79 80 typedef struct pmf_event_handler { 81 TAILQ_ENTRY(pmf_event_handler) pmf_link; 82 pmf_generic_event_t pmf_event; 83 void (*pmf_handler)(device_t); 84 device_t pmf_device; 85 bool pmf_global; 86 } pmf_event_handler_t; 87 88 static TAILQ_HEAD(, pmf_event_handler) pmf_all_events = 89 TAILQ_HEAD_INITIALIZER(pmf_all_events); 90 91 typedef struct pmf_event_workitem { 92 struct work pew_work; 93 pmf_generic_event_t pew_event; 94 device_t pew_device; 95 } pmf_event_workitem_t; 96 97 static pool_cache_t pew_pc; 98 99 static pmf_event_workitem_t *pmf_event_workitem_get(void); 100 static void pmf_event_workitem_put(pmf_event_workitem_t *); 101 102 103 104 static bool pmf_device_resume_locked(device_t PMF_FN_PROTO); 105 static bool pmf_device_suspend_locked(device_t PMF_FN_PROTO); 106 107 static void 108 pmf_event_worker(struct work *wk, void *dummy) 109 { 110 pmf_event_workitem_t *pew; 111 pmf_event_handler_t *event; 112 113 pew = (void *)wk; 114 KASSERT(wk == &pew->pew_work); 115 KASSERT(pew != NULL); 116 117 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 118 if (event->pmf_event != pew->pew_event) 119 continue; 120 if (event->pmf_device == pew->pew_device || event->pmf_global) 121 (*event->pmf_handler)(event->pmf_device); 122 } 123 124 pmf_event_workitem_put(pew); 125 } 126 127 static bool 128 pmf_check_system_drivers(void) 129 { 130 device_t curdev; 131 bool unsupported_devs; 132 deviter_t di; 133 134 unsupported_devs = false; 135 for (curdev = deviter_first(&di, 0); curdev != NULL; 136 curdev = deviter_next(&di)) { 137 if (device_pmf_is_registered(curdev)) 138 continue; 139 if (!unsupported_devs) 140 printf("Devices without power management support:"); 141 printf(" %s", device_xname(curdev)); 142 unsupported_devs = true; 143 } 144 deviter_release(&di); 145 if (unsupported_devs) { 146 printf("\n"); 147 return false; 148 } 149 return true; 150 } 151 152 bool 153 pmf_system_bus_resume(PMF_FN_ARGS1) 154 { 155 bool rv; 156 device_t curdev; 157 deviter_t di; 158 159 aprint_debug("Powering devices:"); 160 /* D0 handlers are run in order */ 161 rv = true; 162 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL; 163 curdev = deviter_next(&di)) { 164 if (!device_pmf_is_registered(curdev)) 165 continue; 166 if (device_is_active(curdev) || 167 !device_is_enabled(curdev)) 168 continue; 169 170 aprint_debug(" %s", device_xname(curdev)); 171 172 if (!device_pmf_bus_resume(curdev PMF_FN_CALL)) { 173 rv = false; 174 aprint_debug("(failed)"); 175 } 176 } 177 deviter_release(&di); 178 aprint_debug("\n"); 179 180 return rv; 181 } 182 183 bool 184 pmf_system_resume(PMF_FN_ARGS1) 185 { 186 bool rv; 187 device_t curdev, parent; 188 deviter_t di; 189 190 if (!pmf_check_system_drivers()) 191 return false; 192 193 aprint_debug("Resuming devices:"); 194 /* D0 handlers are run in order */ 195 rv = true; 196 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL; 197 curdev = deviter_next(&di)) { 198 if (device_is_active(curdev) || 199 !device_is_enabled(curdev)) 200 continue; 201 parent = device_parent(curdev); 202 if (parent != NULL && 203 !device_is_active(parent)) 204 continue; 205 206 aprint_debug(" %s", device_xname(curdev)); 207 208 if (!pmf_device_resume(curdev PMF_FN_CALL)) { 209 rv = false; 210 aprint_debug("(failed)"); 211 } 212 } 213 deviter_release(&di); 214 aprint_debug(".\n"); 215 216 KERNEL_UNLOCK_ONE(0); 217 #if NWSDISPLAY > 0 218 if (rv) 219 wsdisplay_handlex(1); 220 #endif 221 return rv; 222 } 223 224 bool 225 pmf_system_suspend(PMF_FN_ARGS1) 226 { 227 device_t curdev; 228 deviter_t di; 229 230 if (!pmf_check_system_drivers()) 231 return false; 232 #if NWSDISPLAY > 0 233 if (wsdisplay_handlex(0)) 234 return false; 235 #endif 236 KERNEL_LOCK(1, NULL); 237 238 /* 239 * Flush buffers only if the shutdown didn't do so 240 * already and if there was no panic. 241 */ 242 if (doing_shutdown == 0 && panicstr == NULL) { 243 printf("Flushing disk caches: "); 244 sys_sync(NULL, NULL, NULL); 245 if (buf_syncwait() != 0) 246 printf("giving up\n"); 247 else 248 printf("done\n"); 249 } 250 251 aprint_debug("Suspending devices:"); 252 253 for (curdev = deviter_first(&di, DEVITER_F_LEAVES_FIRST); 254 curdev != NULL; 255 curdev = deviter_next(&di)) { 256 if (!device_is_active(curdev)) 257 continue; 258 259 aprint_debug(" %s", device_xname(curdev)); 260 261 /* XXX joerg check return value and abort suspend */ 262 if (!pmf_device_suspend(curdev PMF_FN_CALL)) 263 aprint_debug("(failed)"); 264 } 265 deviter_release(&di); 266 267 aprint_debug(".\n"); 268 269 return true; 270 } 271 272 static bool 273 shutdown_all(int how) 274 { 275 static struct shutdown_state s; 276 device_t curdev; 277 bool progress = false; 278 279 for (curdev = shutdown_first(&s); curdev != NULL; 280 curdev = shutdown_next(&s)) { 281 aprint_debug(" shutting down %s, ", device_xname(curdev)); 282 if (!device_pmf_is_registered(curdev)) 283 aprint_debug("skipped."); 284 #if 0 /* needed? */ 285 else if (!device_pmf_class_shutdown(curdev, how)) 286 aprint_debug("failed."); 287 #endif 288 else if (!device_pmf_driver_shutdown(curdev, how)) 289 aprint_debug("failed."); 290 else if (!device_pmf_bus_shutdown(curdev, how)) 291 aprint_debug("failed."); 292 else { 293 progress = true; 294 aprint_debug("success."); 295 } 296 } 297 return progress; 298 } 299 300 void 301 pmf_system_shutdown(int how) 302 { 303 aprint_debug("Shutting down devices:"); 304 shutdown_all(how); 305 } 306 307 bool 308 pmf_set_platform(const char *key, const char *value) 309 { 310 if (pmf_platform == NULL) 311 pmf_platform = prop_dictionary_create(); 312 if (pmf_platform == NULL) 313 return false; 314 315 return prop_dictionary_set_cstring(pmf_platform, key, value); 316 } 317 318 const char * 319 pmf_get_platform(const char *key) 320 { 321 const char *value; 322 323 if (pmf_platform == NULL) 324 return NULL; 325 326 if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value)) 327 return NULL; 328 329 return value; 330 } 331 332 bool 333 pmf_device_register1(device_t dev, 334 bool (*suspend)(device_t PMF_FN_PROTO), 335 bool (*resume)(device_t PMF_FN_PROTO), 336 bool (*shutdown)(device_t, int)) 337 { 338 if (!device_pmf_driver_register(dev, suspend, resume, shutdown)) 339 return false; 340 341 if (!device_pmf_driver_child_register(dev)) { 342 device_pmf_driver_deregister(dev); 343 return false; 344 } 345 346 return true; 347 } 348 349 void 350 pmf_device_deregister(device_t dev) 351 { 352 device_pmf_class_deregister(dev); 353 device_pmf_bus_deregister(dev); 354 device_pmf_driver_deregister(dev); 355 } 356 357 bool 358 pmf_device_suspend_self(device_t dev) 359 { 360 return pmf_device_suspend(dev, PMF_F_SELF); 361 } 362 363 bool 364 pmf_device_suspend(device_t dev PMF_FN_ARGS) 365 { 366 bool rc; 367 368 PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev))); 369 if (!device_pmf_is_registered(dev)) 370 return false; 371 372 if (!device_pmf_lock(dev PMF_FN_CALL)) 373 return false; 374 375 rc = pmf_device_suspend_locked(dev PMF_FN_CALL); 376 377 device_pmf_unlock(dev PMF_FN_CALL); 378 379 PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev))); 380 return rc; 381 } 382 383 static bool 384 pmf_device_suspend_locked(device_t dev PMF_FN_ARGS) 385 { 386 PMF_TRANSITION_PRINTF2(1, ("%s: self suspend\n", device_xname(dev))); 387 device_pmf_self_suspend(dev, flags); 388 PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev))); 389 if (!device_pmf_class_suspend(dev PMF_FN_CALL)) 390 return false; 391 PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev))); 392 if (!device_pmf_driver_suspend(dev PMF_FN_CALL)) 393 return false; 394 PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev))); 395 if (!device_pmf_bus_suspend(dev PMF_FN_CALL)) 396 return false; 397 398 return true; 399 } 400 401 bool 402 pmf_device_resume_self(device_t dev) 403 { 404 return pmf_device_resume(dev, PMF_F_SELF); 405 } 406 407 bool 408 pmf_device_resume(device_t dev PMF_FN_ARGS) 409 { 410 bool rc; 411 412 PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev))); 413 if (!device_pmf_is_registered(dev)) 414 return false; 415 416 if (!device_pmf_lock(dev PMF_FN_CALL)) 417 return false; 418 419 rc = pmf_device_resume_locked(dev PMF_FN_CALL); 420 421 device_pmf_unlock(dev PMF_FN_CALL); 422 423 PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev))); 424 return rc; 425 } 426 427 static bool 428 pmf_device_resume_locked(device_t dev PMF_FN_ARGS) 429 { 430 PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev))); 431 if (!device_pmf_bus_resume(dev PMF_FN_CALL)) 432 return false; 433 PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev))); 434 if (!device_pmf_driver_resume(dev PMF_FN_CALL)) 435 return false; 436 PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev))); 437 if (!device_pmf_class_resume(dev PMF_FN_CALL)) 438 return false; 439 PMF_TRANSITION_PRINTF2(1, ("%s: self resume\n", device_xname(dev))); 440 device_pmf_self_resume(dev, flags); 441 442 return true; 443 } 444 445 bool 446 pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS) 447 { 448 bool rv = true; 449 device_t curdev; 450 deviter_t di; 451 452 if (!device_is_active(dv)) 453 return true; 454 455 for (curdev = deviter_first(&di, 0); curdev != NULL; 456 curdev = deviter_next(&di)) { 457 if (device_parent(curdev) != dv) 458 continue; 459 if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) { 460 rv = false; 461 break; 462 } 463 } 464 deviter_release(&di); 465 466 return rv && pmf_device_suspend(dv PMF_FN_CALL); 467 } 468 469 bool 470 pmf_device_recursive_resume(device_t dv PMF_FN_ARGS) 471 { 472 device_t parent; 473 474 if (device_is_active(dv)) 475 return true; 476 477 parent = device_parent(dv); 478 if (parent != NULL) { 479 if (!pmf_device_recursive_resume(parent PMF_FN_CALL)) 480 return false; 481 } 482 483 return pmf_device_resume(dv PMF_FN_CALL); 484 } 485 486 bool 487 pmf_device_resume_descendants(device_t dv PMF_FN_ARGS) 488 { 489 bool rv = true; 490 device_t curdev; 491 deviter_t di; 492 493 for (curdev = deviter_first(&di, 0); curdev != NULL; 494 curdev = deviter_next(&di)) { 495 if (device_parent(curdev) != dv) 496 continue; 497 if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) { 498 rv = false; 499 break; 500 } 501 } 502 deviter_release(&di); 503 return rv; 504 } 505 506 bool 507 pmf_device_resume_subtree(device_t dv PMF_FN_ARGS) 508 { 509 if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) 510 return false; 511 512 return pmf_device_resume_descendants(dv PMF_FN_CALL); 513 } 514 515 #include <net/if.h> 516 517 static bool 518 pmf_class_network_suspend(device_t dev PMF_FN_ARGS) 519 { 520 struct ifnet *ifp = device_pmf_class_private(dev); 521 int s; 522 523 s = splnet(); 524 (*ifp->if_stop)(ifp, 0); 525 splx(s); 526 527 return true; 528 } 529 530 static bool 531 pmf_class_network_resume(device_t dev PMF_FN_ARGS) 532 { 533 struct ifnet *ifp = device_pmf_class_private(dev); 534 int s; 535 536 if ((flags & PMF_F_SELF) != 0) 537 return true; 538 539 s = splnet(); 540 if (ifp->if_flags & IFF_UP) { 541 ifp->if_flags &= ~IFF_RUNNING; 542 if ((*ifp->if_init)(ifp) != 0) 543 aprint_normal_ifnet(ifp, "resume failed\n"); 544 (*ifp->if_start)(ifp); 545 } 546 splx(s); 547 548 return true; 549 } 550 551 void 552 pmf_class_network_register(device_t dev, struct ifnet *ifp) 553 { 554 device_pmf_class_register(dev, ifp, pmf_class_network_suspend, 555 pmf_class_network_resume, NULL); 556 } 557 558 bool 559 pmf_event_inject(device_t dv, pmf_generic_event_t ev) 560 { 561 pmf_event_workitem_t *pew; 562 563 pew = pmf_event_workitem_get(); 564 if (pew == NULL) { 565 PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n", 566 dv ? device_xname(dv) : "<anonymous>", ev)); 567 return false; 568 } 569 570 pew->pew_event = ev; 571 pew->pew_device = dv; 572 573 workqueue_enqueue(pmf_event_workqueue, &pew->pew_work, NULL); 574 PMF_EVENT_PRINTF(("%s: PMF event %d injected\n", 575 dv ? device_xname(dv) : "<anonymous>", ev)); 576 577 return true; 578 } 579 580 bool 581 pmf_event_register(device_t dv, pmf_generic_event_t ev, 582 void (*handler)(device_t), bool global) 583 { 584 pmf_event_handler_t *event; 585 586 event = kmem_alloc(sizeof(*event), KM_SLEEP); 587 event->pmf_event = ev; 588 event->pmf_handler = handler; 589 event->pmf_device = dv; 590 event->pmf_global = global; 591 TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link); 592 593 return true; 594 } 595 596 void 597 pmf_event_deregister(device_t dv, pmf_generic_event_t ev, 598 void (*handler)(device_t), bool global) 599 { 600 pmf_event_handler_t *event; 601 602 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 603 if (event->pmf_event != ev) 604 continue; 605 if (event->pmf_device != dv) 606 continue; 607 if (event->pmf_global != global) 608 continue; 609 if (event->pmf_handler != handler) 610 continue; 611 TAILQ_REMOVE(&pmf_all_events, event, pmf_link); 612 kmem_free(event, sizeof(*event)); 613 return; 614 } 615 } 616 617 struct display_class_softc { 618 TAILQ_ENTRY(display_class_softc) dc_link; 619 device_t dc_dev; 620 }; 621 622 static TAILQ_HEAD(, display_class_softc) all_displays; 623 static callout_t global_idle_counter; 624 static int idle_timeout = 30; 625 626 static void 627 input_idle(void *dummy) 628 { 629 PMF_IDLE_PRINTF(("Input idle handler called\n")); 630 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 631 } 632 633 static void 634 input_activity_handler(device_t dv, devactive_t type) 635 { 636 if (!TAILQ_EMPTY(&all_displays)) 637 callout_schedule(&global_idle_counter, idle_timeout * hz); 638 } 639 640 static void 641 pmf_class_input_deregister(device_t dv) 642 { 643 device_active_deregister(dv, input_activity_handler); 644 } 645 646 bool 647 pmf_class_input_register(device_t dv) 648 { 649 if (!device_active_register(dv, input_activity_handler)) 650 return false; 651 652 device_pmf_class_register(dv, NULL, NULL, NULL, 653 pmf_class_input_deregister); 654 655 return true; 656 } 657 658 static void 659 pmf_class_display_deregister(device_t dv) 660 { 661 struct display_class_softc *sc = device_pmf_class_private(dv); 662 int s; 663 664 s = splsoftclock(); 665 TAILQ_REMOVE(&all_displays, sc, dc_link); 666 if (TAILQ_EMPTY(&all_displays)) 667 callout_stop(&global_idle_counter); 668 splx(s); 669 670 kmem_free(sc, sizeof(*sc)); 671 } 672 673 bool 674 pmf_class_display_register(device_t dv) 675 { 676 struct display_class_softc *sc; 677 int s; 678 679 sc = kmem_alloc(sizeof(*sc), KM_SLEEP); 680 681 s = splsoftclock(); 682 if (TAILQ_EMPTY(&all_displays)) 683 callout_schedule(&global_idle_counter, idle_timeout * hz); 684 685 TAILQ_INSERT_HEAD(&all_displays, sc, dc_link); 686 splx(s); 687 688 device_pmf_class_register(dv, sc, NULL, NULL, 689 pmf_class_display_deregister); 690 691 return true; 692 } 693 694 static void 695 pmf_event_workitem_put(pmf_event_workitem_t *pew) 696 { 697 KASSERT(pew != NULL); 698 pool_cache_put(pew_pc, pew); 699 } 700 701 static pmf_event_workitem_t * 702 pmf_event_workitem_get(void) 703 { 704 return pool_cache_get(pew_pc, PR_NOWAIT); 705 } 706 707 static int 708 pew_constructor(void *arg, void *obj, int flags) 709 { 710 memset(obj, 0, sizeof(pmf_event_workitem_t)); 711 return 0; 712 } 713 714 void 715 pmf_init(void) 716 { 717 int err; 718 719 pew_pc = pool_cache_init(sizeof(pmf_event_workitem_t), 0, 0, 0, 720 "pew pool", NULL, IPL_HIGH, pew_constructor, NULL, NULL); 721 pool_cache_setlowat(pew_pc, 16); 722 pool_cache_sethiwat(pew_pc, 256); 723 724 KASSERT(pmf_event_workqueue == NULL); 725 err = workqueue_create(&pmf_event_workqueue, "pmfevent", 726 pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0); 727 if (err) 728 panic("couldn't create pmfevent workqueue"); 729 730 callout_init(&global_idle_counter, 0); 731 callout_setfunc(&global_idle_counter, input_idle, NULL); 732 } 733