1 /* $NetBSD: kern_pmf.c,v 1.19 2008/05/05 00:15:57 jmcneill 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.19 2008/05/05 00:15:57 jmcneill 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/syscallargs.h> /* for sys_sync */ 42 #include <sys/workqueue.h> 43 #include <prop/proplib.h> 44 #include <sys/condvar.h> 45 #include <sys/mutex.h> 46 #include <sys/proc.h> 47 #include <sys/reboot.h> /* for RB_NOSYNC */ 48 #include <sys/sched.h> 49 50 /* XXX ugly special case, but for now the only client */ 51 #include "wsdisplay.h" 52 #if NWSDISPLAY > 0 53 #include <dev/wscons/wsdisplayvar.h> 54 #endif 55 56 #ifdef PMF_DEBUG 57 int pmf_debug_event; 58 int pmf_debug_idle; 59 int pmf_debug_transition; 60 61 #define PMF_EVENT_PRINTF(x) if (pmf_debug_event) printf x 62 #define PMF_IDLE_PRINTF(x) if (pmf_debug_idle) printf x 63 #define PMF_TRANSITION_PRINTF(x) if (pmf_debug_transition) printf x 64 #define PMF_TRANSITION_PRINTF2(y,x) if (pmf_debug_transition>y) printf x 65 #else 66 #define PMF_EVENT_PRINTF(x) do { } while (0) 67 #define PMF_IDLE_PRINTF(x) do { } while (0) 68 #define PMF_TRANSITION_PRINTF(x) do { } while (0) 69 #define PMF_TRANSITION_PRINTF2(y,x) do { } while (0) 70 #endif 71 72 /* #define PMF_DEBUG */ 73 74 MALLOC_DEFINE(M_PMF, "pmf", "device pmf messaging memory"); 75 76 static prop_dictionary_t pmf_platform = NULL; 77 static struct workqueue *pmf_event_workqueue; 78 79 typedef struct pmf_event_handler { 80 TAILQ_ENTRY(pmf_event_handler) pmf_link; 81 pmf_generic_event_t pmf_event; 82 void (*pmf_handler)(device_t); 83 device_t pmf_device; 84 bool pmf_global; 85 } pmf_event_handler_t; 86 87 static TAILQ_HEAD(, pmf_event_handler) pmf_all_events = 88 TAILQ_HEAD_INITIALIZER(pmf_all_events); 89 90 typedef struct pmf_event_workitem { 91 struct work pew_work; 92 pmf_generic_event_t pew_event; 93 device_t pew_device; 94 } pmf_event_workitem_t; 95 96 struct shutdown_state { 97 bool initialized; 98 deviter_t di; 99 }; 100 101 static device_t shutdown_first(struct shutdown_state *); 102 static device_t shutdown_next(struct shutdown_state *); 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 free(pew, M_TEMP); 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, 0); 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 device_t 273 shutdown_first(struct shutdown_state *s) 274 { 275 if (!s->initialized) { 276 deviter_init(&s->di, DEVITER_F_SHUTDOWN|DEVITER_F_LEAVES_FIRST); 277 s->initialized = true; 278 } 279 return shutdown_next(s); 280 } 281 282 static device_t 283 shutdown_next(struct shutdown_state *s) 284 { 285 device_t dv; 286 287 while ((dv = deviter_next(&s->di)) != NULL && !device_is_active(dv)) 288 ; 289 290 return dv; 291 } 292 293 void 294 pmf_system_shutdown(int how) 295 { 296 static struct shutdown_state s; 297 device_t curdev; 298 299 aprint_debug("Shutting down devices:"); 300 301 for (curdev = shutdown_first(&s); curdev != NULL; 302 curdev = shutdown_next(&s)) { 303 aprint_debug(" attempting %s shutdown", 304 device_xname(curdev)); 305 if (!device_pmf_is_registered(curdev)) 306 aprint_debug("(skipped)"); 307 #if 0 /* needed? */ 308 else if (!device_pmf_class_shutdown(curdev, how)) 309 aprint_debug("(failed)"); 310 #endif 311 else if (!device_pmf_driver_shutdown(curdev, how)) 312 aprint_debug("(failed)"); 313 else if (!device_pmf_bus_shutdown(curdev, how)) 314 aprint_debug("(failed)"); 315 } 316 317 aprint_debug(".\n"); 318 } 319 320 bool 321 pmf_set_platform(const char *key, const char *value) 322 { 323 if (pmf_platform == NULL) 324 pmf_platform = prop_dictionary_create(); 325 if (pmf_platform == NULL) 326 return false; 327 328 return prop_dictionary_set_cstring(pmf_platform, key, value); 329 } 330 331 const char * 332 pmf_get_platform(const char *key) 333 { 334 const char *value; 335 336 if (pmf_platform == NULL) 337 return NULL; 338 339 if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value)) 340 return NULL; 341 342 return value; 343 } 344 345 bool 346 pmf_device_register1(device_t dev, 347 bool (*suspend)(device_t PMF_FN_PROTO), 348 bool (*resume)(device_t PMF_FN_PROTO), 349 bool (*shutdown)(device_t, int)) 350 { 351 if (!device_pmf_driver_register(dev, suspend, resume, shutdown)) 352 return false; 353 354 if (!device_pmf_driver_child_register(dev)) { 355 device_pmf_driver_deregister(dev); 356 return false; 357 } 358 359 return true; 360 } 361 362 void 363 pmf_device_deregister(device_t dev) 364 { 365 device_pmf_class_deregister(dev); 366 device_pmf_bus_deregister(dev); 367 device_pmf_driver_deregister(dev); 368 } 369 370 bool 371 pmf_device_suspend_self(device_t dev) 372 { 373 return pmf_device_suspend(dev, PMF_F_SELF); 374 } 375 376 bool 377 pmf_device_suspend(device_t dev PMF_FN_ARGS) 378 { 379 bool rc; 380 381 PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev))); 382 if (!device_pmf_is_registered(dev)) 383 return false; 384 385 if (!device_pmf_lock(dev PMF_FN_CALL)) 386 return false; 387 388 rc = pmf_device_suspend_locked(dev PMF_FN_CALL); 389 390 device_pmf_unlock(dev PMF_FN_CALL); 391 392 PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev))); 393 return rc; 394 } 395 396 static bool 397 pmf_device_suspend_locked(device_t dev PMF_FN_ARGS) 398 { 399 PMF_TRANSITION_PRINTF2(1, ("%s: self suspend\n", device_xname(dev))); 400 device_pmf_self_suspend(dev, flags); 401 PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev))); 402 if (!device_pmf_class_suspend(dev PMF_FN_CALL)) 403 return false; 404 PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev))); 405 if (!device_pmf_driver_suspend(dev PMF_FN_CALL)) 406 return false; 407 PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev))); 408 if (!device_pmf_bus_suspend(dev PMF_FN_CALL)) 409 return false; 410 411 return true; 412 } 413 414 bool 415 pmf_device_resume_self(device_t dev) 416 { 417 return pmf_device_resume(dev, PMF_F_SELF); 418 } 419 420 bool 421 pmf_device_resume(device_t dev PMF_FN_ARGS) 422 { 423 bool rc; 424 425 PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev))); 426 if (!device_pmf_is_registered(dev)) 427 return false; 428 429 if (!device_pmf_lock(dev PMF_FN_CALL)) 430 return false; 431 432 rc = pmf_device_resume_locked(dev PMF_FN_CALL); 433 434 device_pmf_unlock(dev PMF_FN_CALL); 435 436 PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev))); 437 return rc; 438 } 439 440 static bool 441 pmf_device_resume_locked(device_t dev PMF_FN_ARGS) 442 { 443 PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev))); 444 if (!device_pmf_bus_resume(dev PMF_FN_CALL)) 445 return false; 446 PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev))); 447 if (!device_pmf_driver_resume(dev PMF_FN_CALL)) 448 return false; 449 PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev))); 450 if (!device_pmf_class_resume(dev PMF_FN_CALL)) 451 return false; 452 PMF_TRANSITION_PRINTF2(1, ("%s: self resume\n", device_xname(dev))); 453 device_pmf_self_resume(dev, flags); 454 455 return true; 456 } 457 458 bool 459 pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS) 460 { 461 bool rv = true; 462 device_t curdev; 463 deviter_t di; 464 465 if (!device_is_active(dv)) 466 return true; 467 468 for (curdev = deviter_first(&di, 0); curdev != NULL; 469 curdev = deviter_next(&di)) { 470 if (device_parent(curdev) != dv) 471 continue; 472 if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) { 473 rv = false; 474 break; 475 } 476 } 477 deviter_release(&di); 478 479 return rv && pmf_device_suspend(dv PMF_FN_CALL); 480 } 481 482 bool 483 pmf_device_recursive_resume(device_t dv PMF_FN_ARGS) 484 { 485 device_t parent; 486 487 if (device_is_active(dv)) 488 return true; 489 490 parent = device_parent(dv); 491 if (parent != NULL) { 492 if (!pmf_device_recursive_resume(parent PMF_FN_CALL)) 493 return false; 494 } 495 496 return pmf_device_resume(dv PMF_FN_CALL); 497 } 498 499 bool 500 pmf_device_resume_subtree(device_t dv PMF_FN_ARGS) 501 { 502 bool rv = true; 503 device_t curdev; 504 deviter_t di; 505 506 if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) 507 return false; 508 509 for (curdev = deviter_first(&di, 0); curdev != NULL; 510 curdev = deviter_next(&di)) { 511 if (device_parent(curdev) != dv) 512 continue; 513 if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) { 514 rv = false; 515 break; 516 } 517 } 518 deviter_release(&di); 519 return rv; 520 } 521 522 #include <net/if.h> 523 524 static bool 525 pmf_class_network_suspend(device_t dev PMF_FN_ARGS) 526 { 527 struct ifnet *ifp = device_pmf_class_private(dev); 528 int s; 529 530 s = splnet(); 531 (*ifp->if_stop)(ifp, 0); 532 splx(s); 533 534 return true; 535 } 536 537 static bool 538 pmf_class_network_resume(device_t dev PMF_FN_ARGS) 539 { 540 struct ifnet *ifp = device_pmf_class_private(dev); 541 int s; 542 543 if ((flags & PMF_F_SELF) != 0) 544 return true; 545 546 s = splnet(); 547 if (ifp->if_flags & IFF_UP) { 548 ifp->if_flags &= ~IFF_RUNNING; 549 if ((*ifp->if_init)(ifp) != 0) 550 aprint_normal_ifnet(ifp, "resume failed\n"); 551 (*ifp->if_start)(ifp); 552 } 553 splx(s); 554 555 return true; 556 } 557 558 void 559 pmf_class_network_register(device_t dev, struct ifnet *ifp) 560 { 561 device_pmf_class_register(dev, ifp, pmf_class_network_suspend, 562 pmf_class_network_resume, NULL); 563 } 564 565 bool 566 pmf_event_inject(device_t dv, pmf_generic_event_t ev) 567 { 568 pmf_event_workitem_t *pew; 569 570 pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT); 571 if (pew == NULL) { 572 PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n", 573 dv ? device_xname(dv) : "<anonymous>", ev)); 574 return false; 575 } 576 577 pew->pew_event = ev; 578 pew->pew_device = dv; 579 580 workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL); 581 PMF_EVENT_PRINTF(("%s: PMF event %d injected\n", 582 dv ? device_xname(dv) : "<anonymous>", ev)); 583 584 return true; 585 } 586 587 bool 588 pmf_event_register(device_t dv, pmf_generic_event_t ev, 589 void (*handler)(device_t), bool global) 590 { 591 pmf_event_handler_t *event; 592 593 event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK); 594 event->pmf_event = ev; 595 event->pmf_handler = handler; 596 event->pmf_device = dv; 597 event->pmf_global = global; 598 TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link); 599 600 return true; 601 } 602 603 void 604 pmf_event_deregister(device_t dv, pmf_generic_event_t ev, 605 void (*handler)(device_t), bool global) 606 { 607 pmf_event_handler_t *event; 608 609 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 610 if (event->pmf_event != ev) 611 continue; 612 if (event->pmf_device != dv) 613 continue; 614 if (event->pmf_global != global) 615 continue; 616 if (event->pmf_handler != handler) 617 continue; 618 TAILQ_REMOVE(&pmf_all_events, event, pmf_link); 619 free(event, M_DEVBUF); 620 return; 621 } 622 } 623 624 struct display_class_softc { 625 TAILQ_ENTRY(display_class_softc) dc_link; 626 device_t dc_dev; 627 }; 628 629 static TAILQ_HEAD(, display_class_softc) all_displays; 630 static callout_t global_idle_counter; 631 static int idle_timeout = 30; 632 633 static void 634 input_idle(void *dummy) 635 { 636 PMF_IDLE_PRINTF(("Input idle handler called\n")); 637 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 638 } 639 640 static void 641 input_activity_handler(device_t dv, devactive_t type) 642 { 643 if (!TAILQ_EMPTY(&all_displays)) 644 callout_schedule(&global_idle_counter, idle_timeout * hz); 645 } 646 647 static void 648 pmf_class_input_deregister(device_t dv) 649 { 650 device_active_deregister(dv, input_activity_handler); 651 } 652 653 bool 654 pmf_class_input_register(device_t dv) 655 { 656 if (!device_active_register(dv, input_activity_handler)) 657 return false; 658 659 device_pmf_class_register(dv, NULL, NULL, NULL, 660 pmf_class_input_deregister); 661 662 return true; 663 } 664 665 static void 666 pmf_class_display_deregister(device_t dv) 667 { 668 struct display_class_softc *sc = device_pmf_class_private(dv); 669 int s; 670 671 s = splsoftclock(); 672 TAILQ_REMOVE(&all_displays, sc, dc_link); 673 if (TAILQ_EMPTY(&all_displays)) 674 callout_stop(&global_idle_counter); 675 splx(s); 676 677 free(sc, M_DEVBUF); 678 } 679 680 bool 681 pmf_class_display_register(device_t dv) 682 { 683 struct display_class_softc *sc; 684 int s; 685 686 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK); 687 688 s = splsoftclock(); 689 if (TAILQ_EMPTY(&all_displays)) 690 callout_schedule(&global_idle_counter, idle_timeout * hz); 691 692 TAILQ_INSERT_HEAD(&all_displays, sc, dc_link); 693 splx(s); 694 695 device_pmf_class_register(dv, sc, NULL, NULL, 696 pmf_class_display_deregister); 697 698 return true; 699 } 700 701 void 702 pmf_init(void) 703 { 704 int err; 705 706 KASSERT(pmf_event_workqueue == NULL); 707 err = workqueue_create(&pmf_event_workqueue, "pmfevent", 708 pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0); 709 if (err) 710 panic("couldn't create pmfevent workqueue"); 711 712 callout_init(&global_idle_counter, 0); 713 callout_setfunc(&global_idle_counter, input_idle, NULL); 714 } 715