aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-02-03 20:09:07 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-02-04 20:22:35 -0500
commit5294e256717923f4a3297bb8b802f5e0625763f3 (patch)
tree897947ba605ab479413f7b382f3382a2a9a50a7f /drivers/pci
parent49c968111aee4a463d3247937b63efa63a65f378 (diff)
PCI PM: make the PM core more careful with drivers using the new PM framework
Currently, the PM core always attempts to manage devices with drivers that use the new PM framework. In particular, it attempts to disable the devices (which is unnecessary), to save their state (which may be undesirable if the driver has done that already) and to put them into low power states (again, this may be undesirable if the driver has already put the device into a low power state). That need not be the right thing to do, so make the core be more careful in this respect. Generally, there are the following categories of devices to consider: * bridge devices without drivers * non-bridge devices without drivers * bridge devices with drivers * non-bridge devices with drivers and each of them should be handled differently. For bridge devices without drivers the PCI PM core will save their state on suspend and restore it (early) during resume, after putting them into D0 if necessary. It will not attempt to do anything else to these devices. For non-bridge devices without drivers the PCI PM core will disable them and save their state on suspend. During resume, it will put them into D0, if necessary, restore their state (early) and reenable them. For bridge devices with drivers the PCI PM core will only save their state on suspend if the driver hasn't done that already. Still, the core will restore their state (early) during resume, after putting them into D0, if necessary. For non-bridge devices with drivers the PCI PM core will only save their state on suspend if the driver hasn't done that already. Also, if the state of the device hasn't been saved by the driver, the core will attempt to put the device into a low power state. During resume the core will restore the state of the device (early), after putting it into D0, if necessary. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci-driver.c145
1 files changed, 93 insertions, 52 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index ac6c9e493f4c..93eac1423585 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -430,39 +430,22 @@ static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
430 pci_fixup_device(pci_fixup_resume_early, pci_dev); 430 pci_fixup_device(pci_fixup_resume_early, pci_dev);
431} 431}
432 432
433static int pci_pm_default_resume(struct pci_dev *pci_dev) 433static void pci_pm_default_resume(struct pci_dev *pci_dev)
434{ 434{
435 pci_fixup_device(pci_fixup_resume, pci_dev); 435 pci_fixup_device(pci_fixup_resume, pci_dev);
436 436
437 if (pci_is_bridge(pci_dev)) 437 if (!pci_is_bridge(pci_dev))
438 return 0; 438 pci_enable_wake(pci_dev, PCI_D0, false);
439
440 pci_enable_wake(pci_dev, PCI_D0, false);
441 return pci_pm_reenable_device(pci_dev);
442} 439}
443 440
444static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) 441static void pci_pm_default_suspend(struct pci_dev *pci_dev)
445{ 442{
446 /* If a non-bridge device is enabled at this point, disable it */ 443 /* Disable non-bridge devices without PM support */
447 if (!pci_is_bridge(pci_dev)) 444 if (!pci_is_bridge(pci_dev))
448 pci_disable_enabled_device(pci_dev); 445 pci_disable_enabled_device(pci_dev);
449 /*
450 * Save state with interrupts enabled, because in principle the bus the
451 * device is on may be put into a low power state after this code runs.
452 */
453 pci_save_state(pci_dev); 446 pci_save_state(pci_dev);
454} 447}
455 448
456static void pci_pm_default_suspend(struct pci_dev *pci_dev, bool prepare)
457{
458 pci_pm_default_suspend_generic(pci_dev);
459
460 if (prepare && !pci_is_bridge(pci_dev))
461 pci_prepare_to_sleep(pci_dev);
462
463 pci_fixup_device(pci_fixup_suspend, pci_dev);
464}
465
466static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) 449static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
467{ 450{
468 struct pci_driver *drv = pci_dev->driver; 451 struct pci_driver *drv = pci_dev->driver;
@@ -506,20 +489,48 @@ static int pci_pm_suspend(struct device *dev)
506{ 489{
507 struct pci_dev *pci_dev = to_pci_dev(dev); 490 struct pci_dev *pci_dev = to_pci_dev(dev);
508 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 491 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
509 int error = 0;
510 492
511 if (pci_has_legacy_pm_support(pci_dev)) 493 if (pci_has_legacy_pm_support(pci_dev))
512 return pci_legacy_suspend(dev, PMSG_SUSPEND); 494 return pci_legacy_suspend(dev, PMSG_SUSPEND);
513 495
514 if (pm && pm->suspend) { 496 if (!pm) {
497 pci_pm_default_suspend(pci_dev);
498 goto Fixup;
499 }
500
501 pci_dev->state_saved = false;
502
503 if (pm->suspend) {
504 pci_power_t prev = pci_dev->current_state;
505 int error;
506
515 error = pm->suspend(dev); 507 error = pm->suspend(dev);
516 suspend_report_result(pm->suspend, error); 508 suspend_report_result(pm->suspend, error);
509 if (error)
510 return error;
511
512 if (pci_dev->state_saved)
513 goto Fixup;
514
515 if (pci_dev->current_state != PCI_D0
516 && pci_dev->current_state != PCI_UNKNOWN) {
517 WARN_ONCE(pci_dev->current_state != prev,
518 "PCI PM: State of device not saved by %pF\n",
519 pm->suspend);
520 goto Fixup;
521 }
517 } 522 }
518 523
519 if (!error) 524 if (!pci_dev->state_saved) {
520 pci_pm_default_suspend(pci_dev, !!pm); 525 pci_save_state(pci_dev);
526 if (!pci_is_bridge(pci_dev))
527 pci_prepare_to_sleep(pci_dev);
528 }
521 529
522 return error; 530 Fixup:
531 pci_fixup_device(pci_fixup_suspend, pci_dev);
532
533 return 0;
523} 534}
524 535
525static int pci_pm_suspend_noirq(struct device *dev) 536static int pci_pm_suspend_noirq(struct device *dev)
@@ -562,7 +573,7 @@ static int pci_pm_resume_noirq(struct device *dev)
562static int pci_pm_resume(struct device *dev) 573static int pci_pm_resume(struct device *dev)
563{ 574{
564 struct pci_dev *pci_dev = to_pci_dev(dev); 575 struct pci_dev *pci_dev = to_pci_dev(dev);
565 struct device_driver *drv = dev->driver; 576 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
566 int error = 0; 577 int error = 0;
567 578
568 /* 579 /*
@@ -575,12 +586,16 @@ static int pci_pm_resume(struct device *dev)
575 if (pci_has_legacy_pm_support(pci_dev)) 586 if (pci_has_legacy_pm_support(pci_dev))
576 return pci_legacy_resume(dev); 587 return pci_legacy_resume(dev);
577 588
578 error = pci_pm_default_resume(pci_dev); 589 pci_pm_default_resume(pci_dev);
579 590
580 if (!error && drv && drv->pm && drv->pm->resume) 591 if (pm) {
581 error = drv->pm->resume(dev); 592 if (pm->resume)
593 error = pm->resume(dev);
594 } else {
595 pci_pm_reenable_device(pci_dev);
596 }
582 597
583 return error; 598 return 0;
584} 599}
585 600
586#else /* !CONFIG_SUSPEND */ 601#else /* !CONFIG_SUSPEND */
@@ -597,21 +612,31 @@ static int pci_pm_resume(struct device *dev)
597static int pci_pm_freeze(struct device *dev) 612static int pci_pm_freeze(struct device *dev)
598{ 613{
599 struct pci_dev *pci_dev = to_pci_dev(dev); 614 struct pci_dev *pci_dev = to_pci_dev(dev);
600 struct device_driver *drv = dev->driver; 615 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
601 int error = 0;
602 616
603 if (pci_has_legacy_pm_support(pci_dev)) 617 if (pci_has_legacy_pm_support(pci_dev))
604 return pci_legacy_suspend(dev, PMSG_FREEZE); 618 return pci_legacy_suspend(dev, PMSG_FREEZE);
605 619
606 if (drv && drv->pm && drv->pm->freeze) { 620 if (!pm) {
607 error = drv->pm->freeze(dev); 621 pci_pm_default_suspend(pci_dev);
608 suspend_report_result(drv->pm->freeze, error); 622 return 0;
609 } 623 }
610 624
611 if (!error) 625 pci_dev->state_saved = false;
612 pci_pm_default_suspend_generic(pci_dev);
613 626
614 return error; 627 if (pm->freeze) {
628 int error;
629
630 error = pm->freeze(dev);
631 suspend_report_result(pm->freeze, error);
632 if (error)
633 return error;
634 }
635
636 if (!pci_dev->state_saved)
637 pci_save_state(pci_dev);
638
639 return 0;
615} 640}
616 641
617static int pci_pm_freeze_noirq(struct device *dev) 642static int pci_pm_freeze_noirq(struct device *dev)
@@ -654,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev)
654static int pci_pm_thaw(struct device *dev) 679static int pci_pm_thaw(struct device *dev)
655{ 680{
656 struct pci_dev *pci_dev = to_pci_dev(dev); 681 struct pci_dev *pci_dev = to_pci_dev(dev);
657 struct device_driver *drv = dev->driver; 682 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
658 int error = 0; 683 int error = 0;
659 684
660 if (pci_has_legacy_pm_support(pci_dev)) 685 if (pci_has_legacy_pm_support(pci_dev))
661 return pci_legacy_resume(dev); 686 return pci_legacy_resume(dev);
662 687
663 pci_pm_reenable_device(pci_dev); 688 if (pm) {
664 689 if (pm->thaw)
665 if (drv && drv->pm && drv->pm->thaw) 690 error = pm->thaw(dev);
666 error = drv->pm->thaw(dev); 691 } else {
692 pci_pm_reenable_device(pci_dev);
693 }
667 694
668 return error; 695 return error;
669} 696}
@@ -677,13 +704,23 @@ static int pci_pm_poweroff(struct device *dev)
677 if (pci_has_legacy_pm_support(pci_dev)) 704 if (pci_has_legacy_pm_support(pci_dev))
678 return pci_legacy_suspend(dev, PMSG_HIBERNATE); 705 return pci_legacy_suspend(dev, PMSG_HIBERNATE);
679 706
680 if (pm && pm->poweroff) { 707 if (!pm) {
708 pci_pm_default_suspend(pci_dev);
709 goto Fixup;
710 }
711
712 pci_dev->state_saved = false;
713
714 if (pm->poweroff) {
681 error = pm->poweroff(dev); 715 error = pm->poweroff(dev);
682 suspend_report_result(pm->poweroff, error); 716 suspend_report_result(pm->poweroff, error);
683 } 717 }
684 718
685 if (!error) 719 if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
686 pci_pm_default_suspend(pci_dev, !!pm); 720 pci_prepare_to_sleep(pci_dev);
721
722 Fixup:
723 pci_fixup_device(pci_fixup_suspend, pci_dev);
687 724
688 return error; 725 return error;
689} 726}
@@ -724,7 +761,7 @@ static int pci_pm_restore_noirq(struct device *dev)
724static int pci_pm_restore(struct device *dev) 761static int pci_pm_restore(struct device *dev)
725{ 762{
726 struct pci_dev *pci_dev = to_pci_dev(dev); 763 struct pci_dev *pci_dev = to_pci_dev(dev);
727 struct device_driver *drv = dev->driver; 764 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
728 int error = 0; 765 int error = 0;
729 766
730 /* 767 /*
@@ -737,10 +774,14 @@ static int pci_pm_restore(struct device *dev)
737 if (pci_has_legacy_pm_support(pci_dev)) 774 if (pci_has_legacy_pm_support(pci_dev))
738 return pci_legacy_resume(dev); 775 return pci_legacy_resume(dev);
739 776
740 error = pci_pm_default_resume(pci_dev); 777 pci_pm_default_resume(pci_dev);
741 778
742 if (!error && drv && drv->pm && drv->pm->restore) 779 if (pm) {
743 error = drv->pm->restore(dev); 780 if (pm->restore)
781 error = pm->restore(dev);
782 } else {
783 pci_pm_reenable_device(pci_dev);
784 }
744 785
745 return error; 786 return error;
746} 787}