1 /* $NetBSD: kern_pmf.c,v 1.24 2009/04/07 18:16:28 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.24 2009/04/07 18:16:28 dyoung Exp $"); 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <sys/malloc.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 struct shutdown_state { 98 bool initialized; 99 deviter_t di; 100 }; 101 102 static device_t shutdown_first(struct shutdown_state *); 103 static device_t shutdown_next(struct shutdown_state *); 104 105 static bool pmf_device_resume_locked(device_t PMF_FN_PROTO); 106 static bool pmf_device_suspend_locked(device_t PMF_FN_PROTO); 107 108 static void 109 pmf_event_worker(struct work *wk, void *dummy) 110 { 111 pmf_event_workitem_t *pew; 112 pmf_event_handler_t *event; 113 114 pew = (void *)wk; 115 KASSERT(wk == &pew->pew_work); 116 KASSERT(pew != NULL); 117 118 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 119 if (event->pmf_event != pew->pew_event) 120 continue; 121 if (event->pmf_device == pew->pew_device || event->pmf_global) 122 (*event->pmf_handler)(event->pmf_device); 123 } 124 125 free(pew, M_TEMP); 126 } 127 128 static bool 129 pmf_check_system_drivers(void) 130 { 131 device_t curdev; 132 bool unsupported_devs; 133 deviter_t di; 134 135 unsupported_devs = false; 136 for (curdev = deviter_first(&di, 0); curdev != NULL; 137 curdev = deviter_next(&di)) { 138 if (device_pmf_is_registered(curdev)) 139 continue; 140 if (!unsupported_devs) 141 printf("Devices without power management support:"); 142 printf(" %s", device_xname(curdev)); 143 unsupported_devs = true; 144 } 145 deviter_release(&di); 146 if (unsupported_devs) { 147 printf("\n"); 148 return false; 149 } 150 return true; 151 } 152 153 bool 154 pmf_system_bus_resume(PMF_FN_ARGS1) 155 { 156 bool rv; 157 device_t curdev; 158 deviter_t di; 159 160 aprint_debug("Powering devices:"); 161 /* D0 handlers are run in order */ 162 rv = true; 163 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL; 164 curdev = deviter_next(&di)) { 165 if (!device_pmf_is_registered(curdev)) 166 continue; 167 if (device_is_active(curdev) || 168 !device_is_enabled(curdev)) 169 continue; 170 171 aprint_debug(" %s", device_xname(curdev)); 172 173 if (!device_pmf_bus_resume(curdev PMF_FN_CALL)) { 174 rv = false; 175 aprint_debug("(failed)"); 176 } 177 } 178 deviter_release(&di); 179 aprint_debug("\n"); 180 181 return rv; 182 } 183 184 bool 185 pmf_system_resume(PMF_FN_ARGS1) 186 { 187 bool rv; 188 device_t curdev, parent; 189 deviter_t di; 190 191 if (!pmf_check_system_drivers()) 192 return false; 193 194 aprint_debug("Resuming devices:"); 195 /* D0 handlers are run in order */ 196 rv = true; 197 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL; 198 curdev = deviter_next(&di)) { 199 if (device_is_active(curdev) || 200 !device_is_enabled(curdev)) 201 continue; 202 parent = device_parent(curdev); 203 if (parent != NULL && 204 !device_is_active(parent)) 205 continue; 206 207 aprint_debug(" %s", device_xname(curdev)); 208 209 if (!pmf_device_resume(curdev PMF_FN_CALL)) { 210 rv = false; 211 aprint_debug("(failed)"); 212 } 213 } 214 deviter_release(&di); 215 aprint_debug(".\n"); 216 217 KERNEL_UNLOCK_ONE(0); 218 #if NWSDISPLAY > 0 219 if (rv) 220 wsdisplay_handlex(1); 221 #endif 222 return rv; 223 } 224 225 bool 226 pmf_system_suspend(PMF_FN_ARGS1) 227 { 228 device_t curdev; 229 deviter_t di; 230 231 if (!pmf_check_system_drivers()) 232 return false; 233 #if NWSDISPLAY > 0 234 if (wsdisplay_handlex(0)) 235 return false; 236 #endif 237 KERNEL_LOCK(1, 0); 238 239 /* 240 * Flush buffers only if the shutdown didn't do so 241 * already and if there was no panic. 242 */ 243 if (doing_shutdown == 0 && panicstr == NULL) { 244 printf("Flushing disk caches: "); 245 sys_sync(NULL, NULL, NULL); 246 if (buf_syncwait() != 0) 247 printf("giving up\n"); 248 else 249 printf("done\n"); 250 } 251 252 aprint_debug("Suspending devices:"); 253 254 for (curdev = deviter_first(&di, DEVITER_F_LEAVES_FIRST); 255 curdev != NULL; 256 curdev = deviter_next(&di)) { 257 if (!device_is_active(curdev)) 258 continue; 259 260 aprint_debug(" %s", device_xname(curdev)); 261 262 /* XXX joerg check return value and abort suspend */ 263 if (!pmf_device_suspend(curdev PMF_FN_CALL)) 264 aprint_debug("(failed)"); 265 } 266 deviter_release(&di); 267 268 aprint_debug(".\n"); 269 270 return true; 271 } 272 273 static device_t 274 shutdown_first(struct shutdown_state *s) 275 { 276 if (!s->initialized) { 277 deviter_init(&s->di, DEVITER_F_SHUTDOWN|DEVITER_F_LEAVES_FIRST); 278 s->initialized = true; 279 } 280 return shutdown_next(s); 281 } 282 283 static device_t 284 shutdown_next(struct shutdown_state *s) 285 { 286 device_t dv; 287 288 while ((dv = deviter_next(&s->di)) != NULL && !device_is_active(dv)) 289 ; 290 291 return dv; 292 } 293 294 void 295 pmf_system_shutdown(int how) 296 { 297 static struct shutdown_state s; 298 device_t curdev; 299 300 aprint_debug("Shutting down devices:"); 301 suspendsched(); 302 303 for (curdev = shutdown_first(&s); curdev != NULL; 304 curdev = shutdown_next(&s)) { 305 aprint_debug(" attempting %s shutdown", device_xname(curdev)); 306 if ((boothowto & RB_NOSYNC) == 0 && 307 config_detach(curdev, DETACH_SHUTDOWN) == 0) 308 aprint_debug("(detached)"); 309 else if (!device_pmf_is_registered(curdev)) 310 aprint_debug("(skipped)"); 311 #if 0 /* needed? */ 312 else if (!device_pmf_class_shutdown(curdev, how)) 313 aprint_debug("(failed)"); 314 #endif 315 else if (!device_pmf_driver_shutdown(curdev, how)) 316 aprint_debug("(failed)"); 317 else if (!device_pmf_bus_shutdown(curdev, how)) 318 aprint_debug("(failed)"); 319 } 320 321 aprint_debug(".\n"); 322 } 323 324 bool 325 pmf_set_platform(const char *key, const char *value) 326 { 327 if (pmf_platform == NULL) 328 pmf_platform = prop_dictionary_create(); 329 if (pmf_platform == NULL) 330 return false; 331 332 return prop_dictionary_set_cstring(pmf_platform, key, value); 333 } 334 335 const char * 336 pmf_get_platform(const char *key) 337 { 338 const char *value; 339 340 if (pmf_platform == NULL) 341 return NULL; 342 343 if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value)) 344 return NULL; 345 346 return value; 347 } 348 349 bool 350 pmf_device_register1(device_t dev, 351 bool (*suspend)(device_t PMF_FN_PROTO), 352 bool (*resume)(device_t PMF_FN_PROTO), 353 bool (*shutdown)(device_t, int)) 354 { 355 if (!device_pmf_driver_register(dev, suspend, resume, shutdown)) 356 return false; 357 358 if (!device_pmf_driver_child_register(dev)) { 359 device_pmf_driver_deregister(dev); 360 return false; 361 } 362 363 return true; 364 } 365 366 void 367 pmf_device_deregister(device_t dev) 368 { 369 device_pmf_class_deregister(dev); 370 device_pmf_bus_deregister(dev); 371 device_pmf_driver_deregister(dev); 372 } 373 374 bool 375 pmf_device_suspend_self(device_t dev) 376 { 377 return pmf_device_suspend(dev, PMF_F_SELF); 378 } 379 380 bool 381 pmf_device_suspend(device_t dev PMF_FN_ARGS) 382 { 383 bool rc; 384 385 PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev))); 386 if (!device_pmf_is_registered(dev)) 387 return false; 388 389 if (!device_pmf_lock(dev PMF_FN_CALL)) 390 return false; 391 392 rc = pmf_device_suspend_locked(dev PMF_FN_CALL); 393 394 device_pmf_unlock(dev PMF_FN_CALL); 395 396 PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev))); 397 return rc; 398 } 399 400 static bool 401 pmf_device_suspend_locked(device_t dev PMF_FN_ARGS) 402 { 403 PMF_TRANSITION_PRINTF2(1, ("%s: self suspend\n", device_xname(dev))); 404 device_pmf_self_suspend(dev, flags); 405 PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev))); 406 if (!device_pmf_class_suspend(dev PMF_FN_CALL)) 407 return false; 408 PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev))); 409 if (!device_pmf_driver_suspend(dev PMF_FN_CALL)) 410 return false; 411 PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev))); 412 if (!device_pmf_bus_suspend(dev PMF_FN_CALL)) 413 return false; 414 415 return true; 416 } 417 418 bool 419 pmf_device_resume_self(device_t dev) 420 { 421 return pmf_device_resume(dev, PMF_F_SELF); 422 } 423 424 bool 425 pmf_device_resume(device_t dev PMF_FN_ARGS) 426 { 427 bool rc; 428 429 PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev))); 430 if (!device_pmf_is_registered(dev)) 431 return false; 432 433 if (!device_pmf_lock(dev PMF_FN_CALL)) 434 return false; 435 436 rc = pmf_device_resume_locked(dev PMF_FN_CALL); 437 438 device_pmf_unlock(dev PMF_FN_CALL); 439 440 PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev))); 441 return rc; 442 } 443 444 static bool 445 pmf_device_resume_locked(device_t dev PMF_FN_ARGS) 446 { 447 PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev))); 448 if (!device_pmf_bus_resume(dev PMF_FN_CALL)) 449 return false; 450 PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev))); 451 if (!device_pmf_driver_resume(dev PMF_FN_CALL)) 452 return false; 453 PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev))); 454 if (!device_pmf_class_resume(dev PMF_FN_CALL)) 455 return false; 456 PMF_TRANSITION_PRINTF2(1, ("%s: self resume\n", device_xname(dev))); 457 device_pmf_self_resume(dev, flags); 458 459 return true; 460 } 461 462 bool 463 pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS) 464 { 465 bool rv = true; 466 device_t curdev; 467 deviter_t di; 468 469 if (!device_is_active(dv)) 470 return true; 471 472 for (curdev = deviter_first(&di, 0); curdev != NULL; 473 curdev = deviter_next(&di)) { 474 if (device_parent(curdev) != dv) 475 continue; 476 if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) { 477 rv = false; 478 break; 479 } 480 } 481 deviter_release(&di); 482 483 return rv && pmf_device_suspend(dv PMF_FN_CALL); 484 } 485 486 bool 487 pmf_device_recursive_resume(device_t dv PMF_FN_ARGS) 488 { 489 device_t parent; 490 491 if (device_is_active(dv)) 492 return true; 493 494 parent = device_parent(dv); 495 if (parent != NULL) { 496 if (!pmf_device_recursive_resume(parent PMF_FN_CALL)) 497 return false; 498 } 499 500 return pmf_device_resume(dv PMF_FN_CALL); 501 } 502 503 bool 504 pmf_device_resume_descendants(device_t dv PMF_FN_ARGS) 505 { 506 bool rv = true; 507 device_t curdev; 508 deviter_t di; 509 510 for (curdev = deviter_first(&di, 0); curdev != NULL; 511 curdev = deviter_next(&di)) { 512 if (device_parent(curdev) != dv) 513 continue; 514 if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) { 515 rv = false; 516 break; 517 } 518 } 519 deviter_release(&di); 520 return rv; 521 } 522 523 bool 524 pmf_device_resume_subtree(device_t dv PMF_FN_ARGS) 525 { 526 if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) 527 return false; 528 529 return pmf_device_resume_descendants(dv PMF_FN_CALL); 530 } 531 532 #include <net/if.h> 533 534 static bool 535 pmf_class_network_suspend(device_t dev PMF_FN_ARGS) 536 { 537 struct ifnet *ifp = device_pmf_class_private(dev); 538 int s; 539 540 s = splnet(); 541 (*ifp->if_stop)(ifp, 0); 542 splx(s); 543 544 return true; 545 } 546 547 static bool 548 pmf_class_network_resume(device_t dev PMF_FN_ARGS) 549 { 550 struct ifnet *ifp = device_pmf_class_private(dev); 551 int s; 552 553 if ((flags & PMF_F_SELF) != 0) 554 return true; 555 556 s = splnet(); 557 if (ifp->if_flags & IFF_UP) { 558 ifp->if_flags &= ~IFF_RUNNING; 559 if ((*ifp->if_init)(ifp) != 0) 560 aprint_normal_ifnet(ifp, "resume failed\n"); 561 (*ifp->if_start)(ifp); 562 } 563 splx(s); 564 565 return true; 566 } 567 568 void 569 pmf_class_network_register(device_t dev, struct ifnet *ifp) 570 { 571 device_pmf_class_register(dev, ifp, pmf_class_network_suspend, 572 pmf_class_network_resume, NULL); 573 } 574 575 bool 576 pmf_event_inject(device_t dv, pmf_generic_event_t ev) 577 { 578 pmf_event_workitem_t *pew; 579 580 pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT); 581 if (pew == NULL) { 582 PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n", 583 dv ? device_xname(dv) : "<anonymous>", ev)); 584 return false; 585 } 586 587 pew->pew_event = ev; 588 pew->pew_device = dv; 589 590 workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL); 591 PMF_EVENT_PRINTF(("%s: PMF event %d injected\n", 592 dv ? device_xname(dv) : "<anonymous>", ev)); 593 594 return true; 595 } 596 597 bool 598 pmf_event_register(device_t dv, pmf_generic_event_t ev, 599 void (*handler)(device_t), bool global) 600 { 601 pmf_event_handler_t *event; 602 603 event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK); 604 event->pmf_event = ev; 605 event->pmf_handler = handler; 606 event->pmf_device = dv; 607 event->pmf_global = global; 608 TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link); 609 610 return true; 611 } 612 613 void 614 pmf_event_deregister(device_t dv, pmf_generic_event_t ev, 615 void (*handler)(device_t), bool global) 616 { 617 pmf_event_handler_t *event; 618 619 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 620 if (event->pmf_event != ev) 621 continue; 622 if (event->pmf_device != dv) 623 continue; 624 if (event->pmf_global != global) 625 continue; 626 if (event->pmf_handler != handler) 627 continue; 628 TAILQ_REMOVE(&pmf_all_events, event, pmf_link); 629 free(event, M_DEVBUF); 630 return; 631 } 632 } 633 634 struct display_class_softc { 635 TAILQ_ENTRY(display_class_softc) dc_link; 636 device_t dc_dev; 637 }; 638 639 static TAILQ_HEAD(, display_class_softc) all_displays; 640 static callout_t global_idle_counter; 641 static int idle_timeout = 30; 642 643 static void 644 input_idle(void *dummy) 645 { 646 PMF_IDLE_PRINTF(("Input idle handler called\n")); 647 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 648 } 649 650 static void 651 input_activity_handler(device_t dv, devactive_t type) 652 { 653 if (!TAILQ_EMPTY(&all_displays)) 654 callout_schedule(&global_idle_counter, idle_timeout * hz); 655 } 656 657 static void 658 pmf_class_input_deregister(device_t dv) 659 { 660 device_active_deregister(dv, input_activity_handler); 661 } 662 663 bool 664 pmf_class_input_register(device_t dv) 665 { 666 if (!device_active_register(dv, input_activity_handler)) 667 return false; 668 669 device_pmf_class_register(dv, NULL, NULL, NULL, 670 pmf_class_input_deregister); 671 672 return true; 673 } 674 675 static void 676 pmf_class_display_deregister(device_t dv) 677 { 678 struct display_class_softc *sc = device_pmf_class_private(dv); 679 int s; 680 681 s = splsoftclock(); 682 TAILQ_REMOVE(&all_displays, sc, dc_link); 683 if (TAILQ_EMPTY(&all_displays)) 684 callout_stop(&global_idle_counter); 685 splx(s); 686 687 free(sc, M_DEVBUF); 688 } 689 690 bool 691 pmf_class_display_register(device_t dv) 692 { 693 struct display_class_softc *sc; 694 int s; 695 696 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK); 697 698 s = splsoftclock(); 699 if (TAILQ_EMPTY(&all_displays)) 700 callout_schedule(&global_idle_counter, idle_timeout * hz); 701 702 TAILQ_INSERT_HEAD(&all_displays, sc, dc_link); 703 splx(s); 704 705 device_pmf_class_register(dv, sc, NULL, NULL, 706 pmf_class_display_deregister); 707 708 return true; 709 } 710 711 void 712 pmf_init(void) 713 { 714 int err; 715 716 KASSERT(pmf_event_workqueue == NULL); 717 err = workqueue_create(&pmf_event_workqueue, "pmfevent", 718 pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0); 719 if (err) 720 panic("couldn't create pmfevent workqueue"); 721 722 callout_init(&global_idle_counter, 0); 723 callout_setfunc(&global_idle_counter, input_idle, NULL); 724 } 725