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