diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-05-19 18:49:04 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-06-10 13:59:51 -0400 |
commit | bbb44d9f23d868a2837c6b22b8dfb123d8e7800c (patch) | |
tree | 15573ad50a41601b0fda2f7d8568e6c94fee307b /drivers | |
parent | 1eede070a59e1cc73da51e1aaa00d9ab86572cfc (diff) |
PCI: implement new suspend/resume callbacks
Implement new suspend and hibernation callbacks for the PCI bus type.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pci-driver.c | 392 |
1 files changed, 346 insertions, 46 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 677fd9d6db12..8eb8a3091dc7 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -274,7 +274,57 @@ static int pci_device_remove(struct device * dev) | |||
274 | return 0; | 274 | return 0; |
275 | } | 275 | } |
276 | 276 | ||
277 | static int pci_device_suspend(struct device * dev, pm_message_t state) | 277 | static void pci_device_shutdown(struct device *dev) |
278 | { | ||
279 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
280 | struct pci_driver *drv = pci_dev->driver; | ||
281 | |||
282 | if (drv && drv->shutdown) | ||
283 | drv->shutdown(pci_dev); | ||
284 | pci_msi_shutdown(pci_dev); | ||
285 | pci_msix_shutdown(pci_dev); | ||
286 | } | ||
287 | |||
288 | #ifdef CONFIG_PM_SLEEP | ||
289 | |||
290 | /* | ||
291 | * Default "suspend" method for devices that have no driver provided suspend, | ||
292 | * or not even a driver at all. | ||
293 | */ | ||
294 | static void pci_default_pm_suspend(struct pci_dev *pci_dev) | ||
295 | { | ||
296 | pci_save_state(pci_dev); | ||
297 | /* | ||
298 | * mark its power state as "unknown", since we don't know if | ||
299 | * e.g. the BIOS will change its device state when we suspend. | ||
300 | */ | ||
301 | if (pci_dev->current_state == PCI_D0) | ||
302 | pci_dev->current_state = PCI_UNKNOWN; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * Default "resume" method for devices that have no driver provided resume, | ||
307 | * or not even a driver at all. | ||
308 | */ | ||
309 | static int pci_default_pm_resume(struct pci_dev *pci_dev) | ||
310 | { | ||
311 | int retval = 0; | ||
312 | |||
313 | /* restore the PCI config space */ | ||
314 | pci_restore_state(pci_dev); | ||
315 | /* if the device was enabled before suspend, reenable */ | ||
316 | retval = pci_reenable_device(pci_dev); | ||
317 | /* | ||
318 | * if the device was busmaster before the suspend, make it busmaster | ||
319 | * again | ||
320 | */ | ||
321 | if (pci_dev->is_busmaster) | ||
322 | pci_set_master(pci_dev); | ||
323 | |||
324 | return retval; | ||
325 | } | ||
326 | |||
327 | static int pci_legacy_suspend(struct device *dev, pm_message_t state) | ||
278 | { | 328 | { |
279 | struct pci_dev * pci_dev = to_pci_dev(dev); | 329 | struct pci_dev * pci_dev = to_pci_dev(dev); |
280 | struct pci_driver * drv = pci_dev->driver; | 330 | struct pci_driver * drv = pci_dev->driver; |
@@ -284,21 +334,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) | |||
284 | i = drv->suspend(pci_dev, state); | 334 | i = drv->suspend(pci_dev, state); |
285 | suspend_report_result(drv->suspend, i); | 335 | suspend_report_result(drv->suspend, i); |
286 | } else { | 336 | } else { |
287 | pci_save_state(pci_dev); | 337 | pci_default_pm_suspend(pci_dev); |
288 | /* | ||
289 | * mark its power state as "unknown", since we don't know if | ||
290 | * e.g. the BIOS will change its device state when we suspend. | ||
291 | */ | ||
292 | if (pci_dev->current_state == PCI_D0) | ||
293 | pci_dev->current_state = PCI_UNKNOWN; | ||
294 | } | 338 | } |
295 | |||
296 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
297 | |||
298 | return i; | 339 | return i; |
299 | } | 340 | } |
300 | 341 | ||
301 | static int pci_device_suspend_late(struct device * dev, pm_message_t state) | 342 | static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) |
302 | { | 343 | { |
303 | struct pci_dev * pci_dev = to_pci_dev(dev); | 344 | struct pci_dev * pci_dev = to_pci_dev(dev); |
304 | struct pci_driver * drv = pci_dev->driver; | 345 | struct pci_driver * drv = pci_dev->driver; |
@@ -311,26 +352,7 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state) | |||
311 | return i; | 352 | return i; |
312 | } | 353 | } |
313 | 354 | ||
314 | /* | 355 | static int pci_legacy_resume(struct device *dev) |
315 | * Default resume method for devices that have no driver provided resume, | ||
316 | * or not even a driver at all. | ||
317 | */ | ||
318 | static int pci_default_resume(struct pci_dev *pci_dev) | ||
319 | { | ||
320 | int retval = 0; | ||
321 | |||
322 | /* restore the PCI config space */ | ||
323 | pci_restore_state(pci_dev); | ||
324 | /* if the device was enabled before suspend, reenable */ | ||
325 | retval = pci_reenable_device(pci_dev); | ||
326 | /* if the device was busmaster before the suspend, make it busmaster again */ | ||
327 | if (pci_dev->is_busmaster) | ||
328 | pci_set_master(pci_dev); | ||
329 | |||
330 | return retval; | ||
331 | } | ||
332 | |||
333 | static int pci_device_resume(struct device * dev) | ||
334 | { | 356 | { |
335 | int error; | 357 | int error; |
336 | struct pci_dev * pci_dev = to_pci_dev(dev); | 358 | struct pci_dev * pci_dev = to_pci_dev(dev); |
@@ -339,35 +361,313 @@ static int pci_device_resume(struct device * dev) | |||
339 | if (drv && drv->resume) | 361 | if (drv && drv->resume) |
340 | error = drv->resume(pci_dev); | 362 | error = drv->resume(pci_dev); |
341 | else | 363 | else |
342 | error = pci_default_resume(pci_dev); | 364 | error = pci_default_pm_resume(pci_dev); |
343 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
344 | return error; | 365 | return error; |
345 | } | 366 | } |
346 | 367 | ||
347 | static int pci_device_resume_early(struct device * dev) | 368 | static int pci_legacy_resume_early(struct device *dev) |
348 | { | 369 | { |
349 | int error = 0; | 370 | int error = 0; |
350 | struct pci_dev * pci_dev = to_pci_dev(dev); | 371 | struct pci_dev * pci_dev = to_pci_dev(dev); |
351 | struct pci_driver * drv = pci_dev->driver; | 372 | struct pci_driver * drv = pci_dev->driver; |
352 | 373 | ||
353 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
354 | |||
355 | if (drv && drv->resume_early) | 374 | if (drv && drv->resume_early) |
356 | error = drv->resume_early(pci_dev); | 375 | error = drv->resume_early(pci_dev); |
357 | return error; | 376 | return error; |
358 | } | 377 | } |
359 | 378 | ||
360 | static void pci_device_shutdown(struct device *dev) | 379 | static int pci_pm_prepare(struct device *dev) |
380 | { | ||
381 | struct device_driver *drv = dev->driver; | ||
382 | int error = 0; | ||
383 | |||
384 | if (drv && drv->pm && drv->pm->prepare) | ||
385 | error = drv->pm->prepare(dev); | ||
386 | |||
387 | return error; | ||
388 | } | ||
389 | |||
390 | static void pci_pm_complete(struct device *dev) | ||
391 | { | ||
392 | struct device_driver *drv = dev->driver; | ||
393 | |||
394 | if (drv && drv->pm && drv->pm->complete) | ||
395 | drv->pm->complete(dev); | ||
396 | } | ||
397 | |||
398 | #ifdef CONFIG_SUSPEND | ||
399 | |||
400 | static int pci_pm_suspend(struct device *dev) | ||
401 | { | ||
402 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
403 | struct device_driver *drv = dev->driver; | ||
404 | int error = 0; | ||
405 | |||
406 | if (drv && drv->pm) { | ||
407 | if (drv->pm->suspend) { | ||
408 | error = drv->pm->suspend(dev); | ||
409 | suspend_report_result(drv->pm->suspend, error); | ||
410 | } else { | ||
411 | pci_default_pm_suspend(pci_dev); | ||
412 | } | ||
413 | } else { | ||
414 | error = pci_legacy_suspend(dev, PMSG_SUSPEND); | ||
415 | } | ||
416 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
417 | |||
418 | return error; | ||
419 | } | ||
420 | |||
421 | static int pci_pm_suspend_noirq(struct device *dev) | ||
361 | { | 422 | { |
362 | struct pci_dev *pci_dev = to_pci_dev(dev); | 423 | struct pci_dev *pci_dev = to_pci_dev(dev); |
363 | struct pci_driver *drv = pci_dev->driver; | 424 | struct pci_driver *drv = pci_dev->driver; |
425 | int error = 0; | ||
364 | 426 | ||
365 | if (drv && drv->shutdown) | 427 | if (drv && drv->pm) { |
366 | drv->shutdown(pci_dev); | 428 | if (drv->pm->suspend_noirq) { |
367 | pci_msi_shutdown(pci_dev); | 429 | error = drv->pm->suspend_noirq(dev); |
368 | pci_msix_shutdown(pci_dev); | 430 | suspend_report_result(drv->pm->suspend_noirq, error); |
431 | } | ||
432 | } else { | ||
433 | error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); | ||
434 | } | ||
435 | |||
436 | return error; | ||
437 | } | ||
438 | |||
439 | static int pci_pm_resume(struct device *dev) | ||
440 | { | ||
441 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
442 | struct device_driver *drv = dev->driver; | ||
443 | int error; | ||
444 | |||
445 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
446 | |||
447 | if (drv && drv->pm) { | ||
448 | error = drv->pm->resume ? drv->pm->resume(dev) : | ||
449 | pci_default_pm_resume(pci_dev); | ||
450 | } else { | ||
451 | error = pci_legacy_resume(dev); | ||
452 | } | ||
453 | |||
454 | return error; | ||
455 | } | ||
456 | |||
457 | static int pci_pm_resume_noirq(struct device *dev) | ||
458 | { | ||
459 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
460 | struct pci_driver *drv = pci_dev->driver; | ||
461 | int error = 0; | ||
462 | |||
463 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
464 | |||
465 | if (drv && drv->pm) { | ||
466 | if (drv->pm->resume_noirq) | ||
467 | error = drv->pm->resume_noirq(dev); | ||
468 | } else { | ||
469 | error = pci_legacy_resume_early(dev); | ||
470 | } | ||
471 | |||
472 | return error; | ||
473 | } | ||
474 | |||
475 | #else /* !CONFIG_SUSPEND */ | ||
476 | |||
477 | #define pci_pm_suspend NULL | ||
478 | #define pci_pm_suspend_noirq NULL | ||
479 | #define pci_pm_resume NULL | ||
480 | #define pci_pm_resume_noirq NULL | ||
481 | |||
482 | #endif /* !CONFIG_SUSPEND */ | ||
483 | |||
484 | #ifdef CONFIG_HIBERNATION | ||
485 | |||
486 | static int pci_pm_freeze(struct device *dev) | ||
487 | { | ||
488 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
489 | struct device_driver *drv = dev->driver; | ||
490 | int error = 0; | ||
491 | |||
492 | if (drv && drv->pm) { | ||
493 | if (drv->pm->freeze) { | ||
494 | error = drv->pm->freeze(dev); | ||
495 | suspend_report_result(drv->pm->freeze, error); | ||
496 | } else { | ||
497 | pci_default_pm_suspend(pci_dev); | ||
498 | } | ||
499 | } else { | ||
500 | error = pci_legacy_suspend(dev, PMSG_FREEZE); | ||
501 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
502 | } | ||
503 | |||
504 | return error; | ||
505 | } | ||
506 | |||
507 | static int pci_pm_freeze_noirq(struct device *dev) | ||
508 | { | ||
509 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
510 | struct pci_driver *drv = pci_dev->driver; | ||
511 | int error = 0; | ||
512 | |||
513 | if (drv && drv->pm) { | ||
514 | if (drv->pm->freeze_noirq) { | ||
515 | error = drv->pm->freeze_noirq(dev); | ||
516 | suspend_report_result(drv->pm->freeze_noirq, error); | ||
517 | } | ||
518 | } else { | ||
519 | error = pci_legacy_suspend_late(dev, PMSG_FREEZE); | ||
520 | } | ||
521 | |||
522 | return error; | ||
523 | } | ||
524 | |||
525 | static int pci_pm_thaw(struct device *dev) | ||
526 | { | ||
527 | struct device_driver *drv = dev->driver; | ||
528 | int error = 0; | ||
529 | |||
530 | if (drv && drv->pm) { | ||
531 | if (drv->pm->thaw) | ||
532 | error = drv->pm->thaw(dev); | ||
533 | } else { | ||
534 | pci_fixup_device(pci_fixup_resume, to_pci_dev(dev)); | ||
535 | error = pci_legacy_resume(dev); | ||
536 | } | ||
537 | |||
538 | return error; | ||
539 | } | ||
540 | |||
541 | static int pci_pm_thaw_noirq(struct device *dev) | ||
542 | { | ||
543 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
544 | struct pci_driver *drv = pci_dev->driver; | ||
545 | int error = 0; | ||
546 | |||
547 | if (drv && drv->pm) { | ||
548 | if (drv->pm->thaw_noirq) | ||
549 | error = drv->pm->thaw_noirq(dev); | ||
550 | } else { | ||
551 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
552 | error = pci_legacy_resume_early(dev); | ||
553 | } | ||
554 | |||
555 | return error; | ||
556 | } | ||
557 | |||
558 | static int pci_pm_poweroff(struct device *dev) | ||
559 | { | ||
560 | struct device_driver *drv = dev->driver; | ||
561 | int error = 0; | ||
562 | |||
563 | pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); | ||
564 | |||
565 | if (drv && drv->pm) { | ||
566 | if (drv->pm->poweroff) { | ||
567 | error = drv->pm->poweroff(dev); | ||
568 | suspend_report_result(drv->pm->poweroff, error); | ||
569 | } | ||
570 | } else { | ||
571 | error = pci_legacy_suspend(dev, PMSG_HIBERNATE); | ||
572 | } | ||
573 | |||
574 | return error; | ||
575 | } | ||
576 | |||
577 | static int pci_pm_poweroff_noirq(struct device *dev) | ||
578 | { | ||
579 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
580 | struct pci_driver *drv = pci_dev->driver; | ||
581 | int error = 0; | ||
582 | |||
583 | if (drv && drv->pm) { | ||
584 | if (drv->pm->poweroff_noirq) { | ||
585 | error = drv->pm->poweroff_noirq(dev); | ||
586 | suspend_report_result(drv->pm->poweroff_noirq, error); | ||
587 | } | ||
588 | } else { | ||
589 | error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE); | ||
590 | } | ||
591 | |||
592 | return error; | ||
593 | } | ||
594 | |||
595 | static int pci_pm_restore(struct device *dev) | ||
596 | { | ||
597 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
598 | struct device_driver *drv = dev->driver; | ||
599 | int error; | ||
600 | |||
601 | if (drv && drv->pm) { | ||
602 | error = drv->pm->restore ? drv->pm->restore(dev) : | ||
603 | pci_default_pm_resume(pci_dev); | ||
604 | } else { | ||
605 | error = pci_legacy_resume(dev); | ||
606 | } | ||
607 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
608 | |||
609 | return error; | ||
610 | } | ||
611 | |||
612 | static int pci_pm_restore_noirq(struct device *dev) | ||
613 | { | ||
614 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
615 | struct pci_driver *drv = pci_dev->driver; | ||
616 | int error = 0; | ||
617 | |||
618 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
619 | |||
620 | if (drv && drv->pm) { | ||
621 | if (drv->pm->restore_noirq) | ||
622 | error = drv->pm->restore_noirq(dev); | ||
623 | } else { | ||
624 | error = pci_legacy_resume_early(dev); | ||
625 | } | ||
626 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
627 | |||
628 | return error; | ||
369 | } | 629 | } |
370 | 630 | ||
631 | #else /* !CONFIG_HIBERNATION */ | ||
632 | |||
633 | #define pci_pm_freeze NULL | ||
634 | #define pci_pm_freeze_noirq NULL | ||
635 | #define pci_pm_thaw NULL | ||
636 | #define pci_pm_thaw_noirq NULL | ||
637 | #define pci_pm_poweroff NULL | ||
638 | #define pci_pm_poweroff_noirq NULL | ||
639 | #define pci_pm_restore NULL | ||
640 | #define pci_pm_restore_noirq NULL | ||
641 | |||
642 | #endif /* !CONFIG_HIBERNATION */ | ||
643 | |||
644 | struct pm_ext_ops pci_pm_ops = { | ||
645 | .base = { | ||
646 | .prepare = pci_pm_prepare, | ||
647 | .complete = pci_pm_complete, | ||
648 | .suspend = pci_pm_suspend, | ||
649 | .resume = pci_pm_resume, | ||
650 | .freeze = pci_pm_freeze, | ||
651 | .thaw = pci_pm_thaw, | ||
652 | .poweroff = pci_pm_poweroff, | ||
653 | .restore = pci_pm_restore, | ||
654 | }, | ||
655 | .suspend_noirq = pci_pm_suspend_noirq, | ||
656 | .resume_noirq = pci_pm_resume_noirq, | ||
657 | .freeze_noirq = pci_pm_freeze_noirq, | ||
658 | .thaw_noirq = pci_pm_thaw_noirq, | ||
659 | .poweroff_noirq = pci_pm_poweroff_noirq, | ||
660 | .restore_noirq = pci_pm_restore_noirq, | ||
661 | }; | ||
662 | |||
663 | #define PCI_PM_OPS_PTR &pci_pm_ops | ||
664 | |||
665 | #else /* !CONFIG_PM_SLEEP */ | ||
666 | |||
667 | #define PCI_PM_OPS_PTR NULL | ||
668 | |||
669 | #endif /* !CONFIG_PM_SLEEP */ | ||
670 | |||
371 | /** | 671 | /** |
372 | * __pci_register_driver - register a new pci driver | 672 | * __pci_register_driver - register a new pci driver |
373 | * @drv: the driver structure to register | 673 | * @drv: the driver structure to register |
@@ -390,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, | |||
390 | drv->driver.owner = owner; | 690 | drv->driver.owner = owner; |
391 | drv->driver.mod_name = mod_name; | 691 | drv->driver.mod_name = mod_name; |
392 | 692 | ||
693 | if (drv->pm) | ||
694 | drv->driver.pm = &drv->pm->base; | ||
695 | |||
393 | spin_lock_init(&drv->dynids.lock); | 696 | spin_lock_init(&drv->dynids.lock); |
394 | INIT_LIST_HEAD(&drv->dynids.list); | 697 | INIT_LIST_HEAD(&drv->dynids.list); |
395 | 698 | ||
@@ -515,12 +818,9 @@ struct bus_type pci_bus_type = { | |||
515 | .uevent = pci_uevent, | 818 | .uevent = pci_uevent, |
516 | .probe = pci_device_probe, | 819 | .probe = pci_device_probe, |
517 | .remove = pci_device_remove, | 820 | .remove = pci_device_remove, |
518 | .suspend = pci_device_suspend, | ||
519 | .suspend_late = pci_device_suspend_late, | ||
520 | .resume_early = pci_device_resume_early, | ||
521 | .resume = pci_device_resume, | ||
522 | .shutdown = pci_device_shutdown, | 821 | .shutdown = pci_device_shutdown, |
523 | .dev_attrs = pci_dev_attrs, | 822 | .dev_attrs = pci_dev_attrs, |
823 | .pm = PCI_PM_OPS_PTR, | ||
524 | }; | 824 | }; |
525 | 825 | ||
526 | static int __init pci_driver_init(void) | 826 | static int __init pci_driver_init(void) |