kern_pmf.c revision 1.22
1/* $NetBSD: kern_pmf.c,v 1.22 2009/04/02 00:09:34 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.22 2009/04/02 00:09:34 dyoung 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 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 97struct shutdown_state { 98 bool initialized; 99 deviter_t di; 100}; 101 102static device_t shutdown_first(struct shutdown_state *); 103static device_t shutdown_next(struct shutdown_state *); 104 105static bool pmf_device_resume_locked(device_t PMF_FN_PROTO); 106static bool pmf_device_suspend_locked(device_t PMF_FN_PROTO); 107 108static void 109pmf_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 128static bool 129pmf_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 153bool 154pmf_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 184bool 185pmf_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 225bool 226pmf_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 273static device_t 274shutdown_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 283static device_t 284shutdown_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 294void 295pmf_system_shutdown(int how) 296{ 297 static struct shutdown_state s; 298 device_t curdev; 299 300 (void)pmf_check_system_drivers(); 301 302 aprint_debug("Shutting down devices:"); 303 suspendsched(); 304 305 for (curdev = shutdown_first(&s); curdev != NULL; 306 curdev = shutdown_next(&s)) { 307 aprint_debug(" attempting %s shutdown", 308 device_xname(curdev)); 309 if ((boothowto & RB_NOSYNC) == 0 && 310 config_detach(curdev, DETACH_SHUTDOWN) == 0) 311 aprint_debug("(detached)"); 312 else if (!device_pmf_is_registered(curdev)) 313 aprint_debug("(skipped)"); 314#if 0 /* needed? */ 315 else if (!device_pmf_class_shutdown(curdev, how)) 316 aprint_debug("(failed)"); 317#endif 318 else if (!device_pmf_driver_shutdown(curdev, how)) 319 aprint_debug("(failed)"); 320 else if (!device_pmf_bus_shutdown(curdev, how)) 321 aprint_debug("(failed)"); 322 } 323 324 aprint_debug(".\n"); 325} 326 327bool 328pmf_set_platform(const char *key, const char *value) 329{ 330 if (pmf_platform == NULL) 331 pmf_platform = prop_dictionary_create(); 332 if (pmf_platform == NULL) 333 return false; 334 335 return prop_dictionary_set_cstring(pmf_platform, key, value); 336} 337 338const char * 339pmf_get_platform(const char *key) 340{ 341 const char *value; 342 343 if (pmf_platform == NULL) 344 return NULL; 345 346 if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value)) 347 return NULL; 348 349 return value; 350} 351 352bool 353pmf_device_register1(device_t dev, 354 bool (*suspend)(device_t PMF_FN_PROTO), 355 bool (*resume)(device_t PMF_FN_PROTO), 356 bool (*shutdown)(device_t, int)) 357{ 358 if (!device_pmf_driver_register(dev, suspend, resume, shutdown)) 359 return false; 360 361 if (!device_pmf_driver_child_register(dev)) { 362 device_pmf_driver_deregister(dev); 363 return false; 364 } 365 366 return true; 367} 368 369void 370pmf_device_deregister(device_t dev) 371{ 372 device_pmf_class_deregister(dev); 373 device_pmf_bus_deregister(dev); 374 device_pmf_driver_deregister(dev); 375} 376 377bool 378pmf_device_suspend_self(device_t dev) 379{ 380 return pmf_device_suspend(dev, PMF_F_SELF); 381} 382 383bool 384pmf_device_suspend(device_t dev PMF_FN_ARGS) 385{ 386 bool rc; 387 388 PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev))); 389 if (!device_pmf_is_registered(dev)) 390 return false; 391 392 if (!device_pmf_lock(dev PMF_FN_CALL)) 393 return false; 394 395 rc = pmf_device_suspend_locked(dev PMF_FN_CALL); 396 397 device_pmf_unlock(dev PMF_FN_CALL); 398 399 PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev))); 400 return rc; 401} 402 403static bool 404pmf_device_suspend_locked(device_t dev PMF_FN_ARGS) 405{ 406 PMF_TRANSITION_PRINTF2(1, ("%s: self suspend\n", device_xname(dev))); 407 device_pmf_self_suspend(dev, flags); 408 PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev))); 409 if (!device_pmf_class_suspend(dev PMF_FN_CALL)) 410 return false; 411 PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev))); 412 if (!device_pmf_driver_suspend(dev PMF_FN_CALL)) 413 return false; 414 PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev))); 415 if (!device_pmf_bus_suspend(dev PMF_FN_CALL)) 416 return false; 417 418 return true; 419} 420 421bool 422pmf_device_resume_self(device_t dev) 423{ 424 return pmf_device_resume(dev, PMF_F_SELF); 425} 426 427bool 428pmf_device_resume(device_t dev PMF_FN_ARGS) 429{ 430 bool rc; 431 432 PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev))); 433 if (!device_pmf_is_registered(dev)) 434 return false; 435 436 if (!device_pmf_lock(dev PMF_FN_CALL)) 437 return false; 438 439 rc = pmf_device_resume_locked(dev PMF_FN_CALL); 440 441 device_pmf_unlock(dev PMF_FN_CALL); 442 443 PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev))); 444 return rc; 445} 446 447static bool 448pmf_device_resume_locked(device_t dev PMF_FN_ARGS) 449{ 450 PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev))); 451 if (!device_pmf_bus_resume(dev PMF_FN_CALL)) 452 return false; 453 PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev))); 454 if (!device_pmf_driver_resume(dev PMF_FN_CALL)) 455 return false; 456 PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev))); 457 if (!device_pmf_class_resume(dev PMF_FN_CALL)) 458 return false; 459 PMF_TRANSITION_PRINTF2(1, ("%s: self resume\n", device_xname(dev))); 460 device_pmf_self_resume(dev, flags); 461 462 return true; 463} 464 465bool 466pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS) 467{ 468 bool rv = true; 469 device_t curdev; 470 deviter_t di; 471 472 if (!device_is_active(dv)) 473 return true; 474 475 for (curdev = deviter_first(&di, 0); curdev != NULL; 476 curdev = deviter_next(&di)) { 477 if (device_parent(curdev) != dv) 478 continue; 479 if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) { 480 rv = false; 481 break; 482 } 483 } 484 deviter_release(&di); 485 486 return rv && pmf_device_suspend(dv PMF_FN_CALL); 487} 488 489bool 490pmf_device_recursive_resume(device_t dv PMF_FN_ARGS) 491{ 492 device_t parent; 493 494 if (device_is_active(dv)) 495 return true; 496 497 parent = device_parent(dv); 498 if (parent != NULL) { 499 if (!pmf_device_recursive_resume(parent PMF_FN_CALL)) 500 return false; 501 } 502 503 return pmf_device_resume(dv PMF_FN_CALL); 504} 505 506bool 507pmf_device_resume_descendants(device_t dv PMF_FN_ARGS) 508{ 509 bool rv = true; 510 device_t curdev; 511 deviter_t di; 512 513 for (curdev = deviter_first(&di, 0); curdev != NULL; 514 curdev = deviter_next(&di)) { 515 if (device_parent(curdev) != dv) 516 continue; 517 if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) { 518 rv = false; 519 break; 520 } 521 } 522 deviter_release(&di); 523 return rv; 524} 525 526bool 527pmf_device_resume_subtree(device_t dv PMF_FN_ARGS) 528{ 529 if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) 530 return false; 531 532 return pmf_device_resume_descendants(dv PMF_FN_CALL); 533} 534 535#include <net/if.h> 536 537static bool 538pmf_class_network_suspend(device_t dev PMF_FN_ARGS) 539{ 540 struct ifnet *ifp = device_pmf_class_private(dev); 541 int s; 542 543 s = splnet(); 544 (*ifp->if_stop)(ifp, 0); 545 splx(s); 546 547 return true; 548} 549 550static bool 551pmf_class_network_resume(device_t dev PMF_FN_ARGS) 552{ 553 struct ifnet *ifp = device_pmf_class_private(dev); 554 int s; 555 556 if ((flags & PMF_F_SELF) != 0) 557 return true; 558 559 s = splnet(); 560 if (ifp->if_flags & IFF_UP) { 561 ifp->if_flags &= ~IFF_RUNNING; 562 if ((*ifp->if_init)(ifp) != 0) 563 aprint_normal_ifnet(ifp, "resume failed\n"); 564 (*ifp->if_start)(ifp); 565 } 566 splx(s); 567 568 return true; 569} 570 571void 572pmf_class_network_register(device_t dev, struct ifnet *ifp) 573{ 574 device_pmf_class_register(dev, ifp, pmf_class_network_suspend, 575 pmf_class_network_resume, NULL); 576} 577 578bool 579pmf_event_inject(device_t dv, pmf_generic_event_t ev) 580{ 581 pmf_event_workitem_t *pew; 582 583 pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT); 584 if (pew == NULL) { 585 PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n", 586 dv ? device_xname(dv) : "<anonymous>", ev)); 587 return false; 588 } 589 590 pew->pew_event = ev; 591 pew->pew_device = dv; 592 593 workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL); 594 PMF_EVENT_PRINTF(("%s: PMF event %d injected\n", 595 dv ? device_xname(dv) : "<anonymous>", ev)); 596 597 return true; 598} 599 600bool 601pmf_event_register(device_t dv, pmf_generic_event_t ev, 602 void (*handler)(device_t), bool global) 603{ 604 pmf_event_handler_t *event; 605 606 event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK); 607 event->pmf_event = ev; 608 event->pmf_handler = handler; 609 event->pmf_device = dv; 610 event->pmf_global = global; 611 TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link); 612 613 return true; 614} 615 616void 617pmf_event_deregister(device_t dv, pmf_generic_event_t ev, 618 void (*handler)(device_t), bool global) 619{ 620 pmf_event_handler_t *event; 621 622 TAILQ_FOREACH(event, &pmf_all_events, pmf_link) { 623 if (event->pmf_event != ev) 624 continue; 625 if (event->pmf_device != dv) 626 continue; 627 if (event->pmf_global != global) 628 continue; 629 if (event->pmf_handler != handler) 630 continue; 631 TAILQ_REMOVE(&pmf_all_events, event, pmf_link); 632 free(event, M_DEVBUF); 633 return; 634 } 635} 636 637struct display_class_softc { 638 TAILQ_ENTRY(display_class_softc) dc_link; 639 device_t dc_dev; 640}; 641 642static TAILQ_HEAD(, display_class_softc) all_displays; 643static callout_t global_idle_counter; 644static int idle_timeout = 30; 645 646static void 647input_idle(void *dummy) 648{ 649 PMF_IDLE_PRINTF(("Input idle handler called\n")); 650 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 651} 652 653static void 654input_activity_handler(device_t dv, devactive_t type) 655{ 656 if (!TAILQ_EMPTY(&all_displays)) 657 callout_schedule(&global_idle_counter, idle_timeout * hz); 658} 659 660static void 661pmf_class_input_deregister(device_t dv) 662{ 663 device_active_deregister(dv, input_activity_handler); 664} 665 666bool 667pmf_class_input_register(device_t dv) 668{ 669 if (!device_active_register(dv, input_activity_handler)) 670 return false; 671 672 device_pmf_class_register(dv, NULL, NULL, NULL, 673 pmf_class_input_deregister); 674 675 return true; 676} 677 678static void 679pmf_class_display_deregister(device_t dv) 680{ 681 struct display_class_softc *sc = device_pmf_class_private(dv); 682 int s; 683 684 s = splsoftclock(); 685 TAILQ_REMOVE(&all_displays, sc, dc_link); 686 if (TAILQ_EMPTY(&all_displays)) 687 callout_stop(&global_idle_counter); 688 splx(s); 689 690 free(sc, M_DEVBUF); 691} 692 693bool 694pmf_class_display_register(device_t dv) 695{ 696 struct display_class_softc *sc; 697 int s; 698 699 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK); 700 701 s = splsoftclock(); 702 if (TAILQ_EMPTY(&all_displays)) 703 callout_schedule(&global_idle_counter, idle_timeout * hz); 704 705 TAILQ_INSERT_HEAD(&all_displays, sc, dc_link); 706 splx(s); 707 708 device_pmf_class_register(dv, sc, NULL, NULL, 709 pmf_class_display_deregister); 710 711 return true; 712} 713 714void 715pmf_init(void) 716{ 717 int err; 718 719 KASSERT(pmf_event_workqueue == NULL); 720 err = workqueue_create(&pmf_event_workqueue, "pmfevent", 721 pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0); 722 if (err) 723 panic("couldn't create pmfevent workqueue"); 724 725 callout_init(&global_idle_counter, 0); 726 callout_setfunc(&global_idle_counter, input_idle, NULL); 727} 728