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