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