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