1 /* $NetBSD: kern_pmf.c,v 1.21 2009/02/06 01:19:33 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.21 2009/02/06 01:19:33 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 302 for (curdev = shutdown_first(&s); curdev != NULL; 303 curdev = shutdown_next(&s)) { 304 aprint_debug(" attempting %s shutdown", 305 device_xname(curdev)); 306 if (!device_pmf_is_registered(curdev)) 307 aprint_debug("(skipped)"); 308 #if 0 /* needed? */ 309 else if (!device_pmf_class_shutdown(curdev, how)) 310 aprint_debug("(failed)"); 311 #endif 312 else if (!device_pmf_driver_shutdown(curdev, how)) 313 aprint_debug("(failed)"); 314 else if (!device_pmf_bus_shutdown(curdev, how)) 315 aprint_debug("(failed)"); 316 } 317 318 aprint_debug(".\n"); 319 } 320 321 bool 322 pmf_set_platform(const char *key, const char *value) 323 { 324 if (pmf_platform == NULL) 325 pmf_platform = prop_dictionary_create(); 326 if (pmf_platform == NULL) 327 return false; 328 329 return prop_dictionary_set_cstring(pmf_platform, key, value); 330 } 331 332 const char * 333 pmf_get_platform(const char *key) 334 { 335 const char *value; 336 337 if (pmf_platform == NULL) 338 return NULL; 339 340 if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value)) 341 return NULL; 342 343 return value; 344 } 345 346 bool 347 pmf_device_register1(device_t dev, 348 bool (*suspend)(device_t PMF_FN_PROTO), 349 bool (*resume)(device_t PMF_FN_PROTO), 350 bool (*shutdown)(device_t, int)) 351 { 352 if (!device_pmf_driver_register(dev, suspend, resume, shutdown)) 353 return false; 354 355 if (!device_pmf_driver_child_register(dev)) { 356 device_pmf_driver_deregister(dev); 357 return false; 358 } 359 360 return true; 361 } 362 363 void 364 pmf_device_deregister(device_t dev) 365 { 366 device_pmf_class_deregister(dev); 367 device_pmf_bus_deregister(dev); 368 device_pmf_driver_deregister(dev); 369 } 370 371 bool 372 pmf_device_suspend_self(device_t dev) 373 { 374 return pmf_device_suspend(dev, PMF_F_SELF); 375 } 376 377 bool 378 pmf_device_suspend(device_t dev PMF_FN_ARGS) 379 { 380 bool rc; 381 382 PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev))); 383 if (!device_pmf_is_registered(dev)) 384 return false; 385 386 if (!device_pmf_lock(dev PMF_FN_CALL)) 387 return false; 388 389 rc = pmf_device_suspend_locked(dev PMF_FN_CALL); 390 391 device_pmf_unlock(dev PMF_FN_CALL); 392 393 PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev))); 394 return rc; 395 } 396 397 static bool 398 pmf_device_suspend_locked(device_t dev PMF_FN_ARGS) 399 { 400 PMF_TRANSITION_PRINTF2(1, ("%s: self suspend\n", device_xname(dev))); 401 device_pmf_self_suspend(dev, flags); 402 PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev))); 403 if (!device_pmf_class_suspend(dev PMF_FN_CALL)) 404 return false; 405 PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev))); 406 if (!device_pmf_driver_suspend(dev PMF_FN_CALL)) 407 return false; 408 PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev))); 409 if (!device_pmf_bus_suspend(dev PMF_FN_CALL)) 410 return false; 411 412 return true; 413 } 414 415 bool 416 pmf_device_resume_self(device_t dev) 417 { 418 return pmf_device_resume(dev, PMF_F_SELF); 419 } 420 421 bool 422 pmf_device_resume(device_t dev PMF_FN_ARGS) 423 { 424 bool rc; 425 426 PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev))); 427 if (!device_pmf_is_registered(dev)) 428 return false; 429 430 if (!device_pmf_lock(dev PMF_FN_CALL)) 431 return false; 432 433 rc = pmf_device_resume_locked(dev PMF_FN_CALL); 434 435 device_pmf_unlock(dev PMF_FN_CALL); 436 437 PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev))); 438 return rc; 439 } 440 441 static bool 442 pmf_device_resume_locked(device_t dev PMF_FN_ARGS) 443 { 444 PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev))); 445 if (!device_pmf_bus_resume(dev PMF_FN_CALL)) 446 return false; 447 PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev))); 448 if (!device_pmf_driver_resume(dev PMF_FN_CALL)) 449 return false; 450 PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev))); 451 if (!device_pmf_class_resume(dev PMF_FN_CALL)) 452 return false; 453 PMF_TRANSITION_PRINTF2(1, ("%s: self resume\n", device_xname(dev))); 454 device_pmf_self_resume(dev, flags); 455 456 return true; 457 } 458 459 bool 460 pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS) 461 { 462 bool rv = true; 463 device_t curdev; 464 deviter_t di; 465 466 if (!device_is_active(dv)) 467 return true; 468 469 for (curdev = deviter_first(&di, 0); curdev != NULL; 470 curdev = deviter_next(&di)) { 471 if (device_parent(curdev) != dv) 472 continue; 473 if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) { 474 rv = false; 475 break; 476 } 477 } 478 deviter_release(&di); 479 480 return rv && pmf_device_suspend(dv PMF_FN_CALL); 481 } 482 483 bool 484 pmf_device_recursive_resume(device_t dv PMF_FN_ARGS) 485 { 486 device_t parent; 487 488 if (device_is_active(dv)) 489 return true; 490 491 parent = device_parent(dv); 492 if (parent != NULL) { 493 if (!pmf_device_recursive_resume(parent PMF_FN_CALL)) 494 return false; 495 } 496 497 return pmf_device_resume(dv PMF_FN_CALL); 498 } 499 500 bool 501 pmf_device_resume_descendants(device_t dv PMF_FN_ARGS) 502 { 503 bool rv = true; 504 device_t curdev; 505 deviter_t di; 506 507 for (curdev = deviter_first(&di, 0); curdev != NULL; 508 curdev = deviter_next(&di)) { 509 if (device_parent(curdev) != dv) 510 continue; 511 if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) { 512 rv = false; 513 break; 514 } 515 } 516 deviter_release(&di); 517 return rv; 518 } 519 520 bool 521 pmf_device_resume_subtree(device_t dv PMF_FN_ARGS) 522 { 523 if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) 524 return false; 525 526 return pmf_device_resume_descendants(dv PMF_FN_CALL); 527 } 528 529 #include <net/if.h> 530 531 static bool 532 pmf_class_network_suspend(device_t dev PMF_FN_ARGS) 533 { 534 struct ifnet *ifp = device_pmf_class_private(dev); 535 int s; 536 537 s = splnet(); 538 (*ifp->if_stop)(ifp, 0); 539 splx(s); 540 541 return true; 542 } 543 544 static bool 545 pmf_class_network_resume(device_t dev PMF_FN_ARGS) 546 { 547 struct ifnet *ifp = device_pmf_class_private(dev); 548 int s; 549 550 if ((flags & PMF_F_SELF) != 0) 551 return true; 552 553 s = splnet(); 554 if (ifp->if_flags & IFF_UP) { 555 ifp->if_flags &= ~IFF_RUNNING; 556 if ((*ifp->if_init)(ifp) != 0) 557 aprint_normal_ifnet(ifp, "resume failed\n"); 558 (*ifp->if_start)(ifp); 559 } 560 splx(s); 561 562 return true; 563 } 564 565 void 566 pmf_class_network_register(device_t dev, struct ifnet *ifp) 567 { 568 device_pmf_class_register(dev, ifp, pmf_class_network_suspend, 569 pmf_class_network_resume, NULL); 570 } 571 572 bool 573 pmf_event_inject(device_t dv, pmf_generic_event_t ev) 574 { 575 pmf_event_workitem_t *pew; 576 577 pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT); 578 if (pew == NULL) { 579 PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n", 580 dv ? device_xname(dv) : "<anonymous>", ev)); 581 return false; 582 } 583 584 pew->pew_event = ev; 585 pew->pew_device = dv; 586 587 workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL); 588 PMF_EVENT_PRINTF(("%s: PMF event %d injected\n", 589 dv ? device_xname(dv) : "<anonymous>", ev)); 590 591 return true; 592 } 593 594 bool 595 pmf_event_register(device_t dv, pmf_generic_event_t ev, 596 void (*handler)(device_t), bool global) 597 { 598 pmf_event_handler_t *event; 599 600 event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK); 601 event->pmf_event = ev; 602 event->pmf_handler = handler; 603 event->pmf_device = dv; 604 event->pmf_global = global; 605 TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link); 606 607 return true; 608 } 609 610 void 611 pmf_event_deregister(device_t dv, pmf_generic_event_t ev, 612 void (*handler)(device_t), bool global) 613 { 614 pmf_event_handler_t *event; 615 616 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 617 if (event->pmf_event != ev) 618 continue; 619 if (event->pmf_device != dv) 620 continue; 621 if (event->pmf_global != global) 622 continue; 623 if (event->pmf_handler != handler) 624 continue; 625 TAILQ_REMOVE(&pmf_all_events, event, pmf_link); 626 free(event, M_DEVBUF); 627 return; 628 } 629 } 630 631 struct display_class_softc { 632 TAILQ_ENTRY(display_class_softc) dc_link; 633 device_t dc_dev; 634 }; 635 636 static TAILQ_HEAD(, display_class_softc) all_displays; 637 static callout_t global_idle_counter; 638 static int idle_timeout = 30; 639 640 static void 641 input_idle(void *dummy) 642 { 643 PMF_IDLE_PRINTF(("Input idle handler called\n")); 644 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 645 } 646 647 static void 648 input_activity_handler(device_t dv, devactive_t type) 649 { 650 if (!TAILQ_EMPTY(&all_displays)) 651 callout_schedule(&global_idle_counter, idle_timeout * hz); 652 } 653 654 static void 655 pmf_class_input_deregister(device_t dv) 656 { 657 device_active_deregister(dv, input_activity_handler); 658 } 659 660 bool 661 pmf_class_input_register(device_t dv) 662 { 663 if (!device_active_register(dv, input_activity_handler)) 664 return false; 665 666 device_pmf_class_register(dv, NULL, NULL, NULL, 667 pmf_class_input_deregister); 668 669 return true; 670 } 671 672 static void 673 pmf_class_display_deregister(device_t dv) 674 { 675 struct display_class_softc *sc = device_pmf_class_private(dv); 676 int s; 677 678 s = splsoftclock(); 679 TAILQ_REMOVE(&all_displays, sc, dc_link); 680 if (TAILQ_EMPTY(&all_displays)) 681 callout_stop(&global_idle_counter); 682 splx(s); 683 684 free(sc, M_DEVBUF); 685 } 686 687 bool 688 pmf_class_display_register(device_t dv) 689 { 690 struct display_class_softc *sc; 691 int s; 692 693 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK); 694 695 s = splsoftclock(); 696 if (TAILQ_EMPTY(&all_displays)) 697 callout_schedule(&global_idle_counter, idle_timeout * hz); 698 699 TAILQ_INSERT_HEAD(&all_displays, sc, dc_link); 700 splx(s); 701 702 device_pmf_class_register(dv, sc, NULL, NULL, 703 pmf_class_display_deregister); 704 705 return true; 706 } 707 708 void 709 pmf_init(void) 710 { 711 int err; 712 713 KASSERT(pmf_event_workqueue == NULL); 714 err = workqueue_create(&pmf_event_workqueue, "pmfevent", 715 pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0); 716 if (err) 717 panic("couldn't create pmfevent workqueue"); 718 719 callout_init(&global_idle_counter, 0); 720 callout_setfunc(&global_idle_counter, input_idle, NULL); 721 } 722