1 /* $NetBSD: kern_pmf.c,v 1.20 2008/06/17 16:17:21 tsutsui 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.20 2008/06/17 16:17:21 tsutsui 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_subtree(device_t dv PMF_FN_ARGS) 502 { 503 bool rv = true; 504 device_t curdev; 505 deviter_t di; 506 507 if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) 508 return false; 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 #include <net/if.h> 524 525 static bool 526 pmf_class_network_suspend(device_t dev PMF_FN_ARGS) 527 { 528 struct ifnet *ifp = device_pmf_class_private(dev); 529 int s; 530 531 s = splnet(); 532 (*ifp->if_stop)(ifp, 0); 533 splx(s); 534 535 return true; 536 } 537 538 static bool 539 pmf_class_network_resume(device_t dev PMF_FN_ARGS) 540 { 541 struct ifnet *ifp = device_pmf_class_private(dev); 542 int s; 543 544 if ((flags & PMF_F_SELF) != 0) 545 return true; 546 547 s = splnet(); 548 if (ifp->if_flags & IFF_UP) { 549 ifp->if_flags &= ~IFF_RUNNING; 550 if ((*ifp->if_init)(ifp) != 0) 551 aprint_normal_ifnet(ifp, "resume failed\n"); 552 (*ifp->if_start)(ifp); 553 } 554 splx(s); 555 556 return true; 557 } 558 559 void 560 pmf_class_network_register(device_t dev, struct ifnet *ifp) 561 { 562 device_pmf_class_register(dev, ifp, pmf_class_network_suspend, 563 pmf_class_network_resume, NULL); 564 } 565 566 bool 567 pmf_event_inject(device_t dv, pmf_generic_event_t ev) 568 { 569 pmf_event_workitem_t *pew; 570 571 pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT); 572 if (pew == NULL) { 573 PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n", 574 dv ? device_xname(dv) : "<anonymous>", ev)); 575 return false; 576 } 577 578 pew->pew_event = ev; 579 pew->pew_device = dv; 580 581 workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL); 582 PMF_EVENT_PRINTF(("%s: PMF event %d injected\n", 583 dv ? device_xname(dv) : "<anonymous>", ev)); 584 585 return true; 586 } 587 588 bool 589 pmf_event_register(device_t dv, pmf_generic_event_t ev, 590 void (*handler)(device_t), bool global) 591 { 592 pmf_event_handler_t *event; 593 594 event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK); 595 event->pmf_event = ev; 596 event->pmf_handler = handler; 597 event->pmf_device = dv; 598 event->pmf_global = global; 599 TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link); 600 601 return true; 602 } 603 604 void 605 pmf_event_deregister(device_t dv, pmf_generic_event_t ev, 606 void (*handler)(device_t), bool global) 607 { 608 pmf_event_handler_t *event; 609 610 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 611 if (event->pmf_event != ev) 612 continue; 613 if (event->pmf_device != dv) 614 continue; 615 if (event->pmf_global != global) 616 continue; 617 if (event->pmf_handler != handler) 618 continue; 619 TAILQ_REMOVE(&pmf_all_events, event, pmf_link); 620 free(event, M_DEVBUF); 621 return; 622 } 623 } 624 625 struct display_class_softc { 626 TAILQ_ENTRY(display_class_softc) dc_link; 627 device_t dc_dev; 628 }; 629 630 static TAILQ_HEAD(, display_class_softc) all_displays; 631 static callout_t global_idle_counter; 632 static int idle_timeout = 30; 633 634 static void 635 input_idle(void *dummy) 636 { 637 PMF_IDLE_PRINTF(("Input idle handler called\n")); 638 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 639 } 640 641 static void 642 input_activity_handler(device_t dv, devactive_t type) 643 { 644 if (!TAILQ_EMPTY(&all_displays)) 645 callout_schedule(&global_idle_counter, idle_timeout * hz); 646 } 647 648 static void 649 pmf_class_input_deregister(device_t dv) 650 { 651 device_active_deregister(dv, input_activity_handler); 652 } 653 654 bool 655 pmf_class_input_register(device_t dv) 656 { 657 if (!device_active_register(dv, input_activity_handler)) 658 return false; 659 660 device_pmf_class_register(dv, NULL, NULL, NULL, 661 pmf_class_input_deregister); 662 663 return true; 664 } 665 666 static void 667 pmf_class_display_deregister(device_t dv) 668 { 669 struct display_class_softc *sc = device_pmf_class_private(dv); 670 int s; 671 672 s = splsoftclock(); 673 TAILQ_REMOVE(&all_displays, sc, dc_link); 674 if (TAILQ_EMPTY(&all_displays)) 675 callout_stop(&global_idle_counter); 676 splx(s); 677 678 free(sc, M_DEVBUF); 679 } 680 681 bool 682 pmf_class_display_register(device_t dv) 683 { 684 struct display_class_softc *sc; 685 int s; 686 687 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK); 688 689 s = splsoftclock(); 690 if (TAILQ_EMPTY(&all_displays)) 691 callout_schedule(&global_idle_counter, idle_timeout * hz); 692 693 TAILQ_INSERT_HEAD(&all_displays, sc, dc_link); 694 splx(s); 695 696 device_pmf_class_register(dv, sc, NULL, NULL, 697 pmf_class_display_deregister); 698 699 return true; 700 } 701 702 void 703 pmf_init(void) 704 { 705 int err; 706 707 KASSERT(pmf_event_workqueue == NULL); 708 err = workqueue_create(&pmf_event_workqueue, "pmfevent", 709 pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0); 710 if (err) 711 panic("couldn't create pmfevent workqueue"); 712 713 callout_init(&global_idle_counter, 0); 714 callout_setfunc(&global_idle_counter, input_idle, NULL); 715 } 716