diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
| -rw-r--r-- | drivers/pci/pci-driver.c | 177 |
1 files changed, 121 insertions, 56 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 9de07b75b99..93eac142358 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
| @@ -355,6 +355,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) | |||
| 355 | int i = 0; | 355 | int i = 0; |
| 356 | 356 | ||
| 357 | if (drv && drv->suspend) { | 357 | if (drv && drv->suspend) { |
| 358 | pci_power_t prev = pci_dev->current_state; | ||
| 359 | |||
| 358 | pci_dev->state_saved = false; | 360 | pci_dev->state_saved = false; |
| 359 | 361 | ||
| 360 | i = drv->suspend(pci_dev, state); | 362 | i = drv->suspend(pci_dev, state); |
| @@ -365,8 +367,13 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) | |||
| 365 | if (pci_dev->state_saved) | 367 | if (pci_dev->state_saved) |
| 366 | goto Fixup; | 368 | goto Fixup; |
| 367 | 369 | ||
| 368 | if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0)) | 370 | if (pci_dev->current_state != PCI_D0 |
| 371 | && pci_dev->current_state != PCI_UNKNOWN) { | ||
| 372 | WARN_ONCE(pci_dev->current_state != prev, | ||
| 373 | "PCI PM: Device state not saved by %pF\n", | ||
| 374 | drv->suspend); | ||
| 369 | goto Fixup; | 375 | goto Fixup; |
| 376 | } | ||
| 370 | } | 377 | } |
| 371 | 378 | ||
| 372 | pci_save_state(pci_dev); | 379 | pci_save_state(pci_dev); |
| @@ -419,38 +426,24 @@ static int pci_legacy_resume(struct device *dev) | |||
| 419 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) | 426 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) |
| 420 | { | 427 | { |
| 421 | pci_restore_standard_config(pci_dev); | 428 | pci_restore_standard_config(pci_dev); |
| 429 | pci_dev->state_saved = false; | ||
| 422 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | 430 | pci_fixup_device(pci_fixup_resume_early, pci_dev); |
| 423 | } | 431 | } |
| 424 | 432 | ||
| 425 | static int pci_pm_default_resume(struct pci_dev *pci_dev) | 433 | static void pci_pm_default_resume(struct pci_dev *pci_dev) |
| 426 | { | 434 | { |
| 427 | pci_fixup_device(pci_fixup_resume, pci_dev); | 435 | pci_fixup_device(pci_fixup_resume, pci_dev); |
| 428 | 436 | ||
| 429 | if (!pci_is_bridge(pci_dev)) | 437 | if (!pci_is_bridge(pci_dev)) |
| 430 | pci_enable_wake(pci_dev, PCI_D0, false); | 438 | pci_enable_wake(pci_dev, PCI_D0, false); |
| 431 | |||
| 432 | return pci_pm_reenable_device(pci_dev); | ||
| 433 | } | ||
| 434 | |||
| 435 | static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) | ||
| 436 | { | ||
| 437 | /* If device is enabled at this point, disable it */ | ||
| 438 | pci_disable_enabled_device(pci_dev); | ||
| 439 | /* | ||
| 440 | * Save state with interrupts enabled, because in principle the bus the | ||
| 441 | * device is on may be put into a low power state after this code runs. | ||
| 442 | */ | ||
| 443 | pci_save_state(pci_dev); | ||
| 444 | } | 439 | } |
| 445 | 440 | ||
| 446 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) | 441 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) |
| 447 | { | 442 | { |
| 448 | pci_pm_default_suspend_generic(pci_dev); | 443 | /* Disable non-bridge devices without PM support */ |
| 449 | |||
| 450 | if (!pci_is_bridge(pci_dev)) | 444 | if (!pci_is_bridge(pci_dev)) |
| 451 | pci_prepare_to_sleep(pci_dev); | 445 | pci_disable_enabled_device(pci_dev); |
| 452 | 446 | pci_save_state(pci_dev); | |
| 453 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
| 454 | } | 447 | } |
| 455 | 448 | ||
| 456 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) | 449 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) |
| @@ -495,21 +488,49 @@ static void pci_pm_complete(struct device *dev) | |||
| 495 | static int pci_pm_suspend(struct device *dev) | 488 | static int pci_pm_suspend(struct device *dev) |
| 496 | { | 489 | { |
| 497 | struct pci_dev *pci_dev = to_pci_dev(dev); | 490 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 498 | struct device_driver *drv = dev->driver; | 491 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| 499 | int error = 0; | ||
| 500 | 492 | ||
| 501 | if (pci_has_legacy_pm_support(pci_dev)) | 493 | if (pci_has_legacy_pm_support(pci_dev)) |
| 502 | return pci_legacy_suspend(dev, PMSG_SUSPEND); | 494 | return pci_legacy_suspend(dev, PMSG_SUSPEND); |
| 503 | 495 | ||
| 504 | if (drv && drv->pm && drv->pm->suspend) { | 496 | if (!pm) { |
| 505 | error = drv->pm->suspend(dev); | 497 | pci_pm_default_suspend(pci_dev); |
| 506 | suspend_report_result(drv->pm->suspend, error); | 498 | goto Fixup; |
| 507 | } | 499 | } |
| 508 | 500 | ||
| 509 | if (!error) | 501 | pci_dev->state_saved = false; |
| 510 | pci_pm_default_suspend(pci_dev); | ||
| 511 | 502 | ||
| 512 | return error; | 503 | if (pm->suspend) { |
| 504 | pci_power_t prev = pci_dev->current_state; | ||
| 505 | int error; | ||
| 506 | |||
| 507 | error = pm->suspend(dev); | ||
| 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 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | if (!pci_dev->state_saved) { | ||
| 525 | pci_save_state(pci_dev); | ||
| 526 | if (!pci_is_bridge(pci_dev)) | ||
| 527 | pci_prepare_to_sleep(pci_dev); | ||
| 528 | } | ||
| 529 | |||
| 530 | Fixup: | ||
| 531 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
| 532 | |||
| 533 | return 0; | ||
| 513 | } | 534 | } |
| 514 | 535 | ||
| 515 | static int pci_pm_suspend_noirq(struct device *dev) | 536 | static int pci_pm_suspend_noirq(struct device *dev) |
| @@ -552,18 +573,29 @@ static int pci_pm_resume_noirq(struct device *dev) | |||
| 552 | static int pci_pm_resume(struct device *dev) | 573 | static int pci_pm_resume(struct device *dev) |
| 553 | { | 574 | { |
| 554 | struct pci_dev *pci_dev = to_pci_dev(dev); | 575 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 555 | struct device_driver *drv = dev->driver; | 576 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| 556 | int error = 0; | 577 | int error = 0; |
| 557 | 578 | ||
| 579 | /* | ||
| 580 | * This is necessary for the suspend error path in which resume is | ||
| 581 | * called without restoring the standard config registers of the device. | ||
| 582 | */ | ||
| 583 | if (pci_dev->state_saved) | ||
| 584 | pci_restore_standard_config(pci_dev); | ||
| 585 | |||
| 558 | if (pci_has_legacy_pm_support(pci_dev)) | 586 | if (pci_has_legacy_pm_support(pci_dev)) |
| 559 | return pci_legacy_resume(dev); | 587 | return pci_legacy_resume(dev); |
| 560 | 588 | ||
| 561 | error = pci_pm_default_resume(pci_dev); | 589 | pci_pm_default_resume(pci_dev); |
| 562 | 590 | ||
| 563 | if (!error && drv && drv->pm && drv->pm->resume) | 591 | if (pm) { |
| 564 | error = drv->pm->resume(dev); | 592 | if (pm->resume) |
| 593 | error = pm->resume(dev); | ||
| 594 | } else { | ||
| 595 | pci_pm_reenable_device(pci_dev); | ||
| 596 | } | ||
| 565 | 597 | ||
| 566 | return error; | 598 | return 0; |
| 567 | } | 599 | } |
| 568 | 600 | ||
| 569 | #else /* !CONFIG_SUSPEND */ | 601 | #else /* !CONFIG_SUSPEND */ |
| @@ -580,21 +612,31 @@ static int pci_pm_resume(struct device *dev) | |||
| 580 | static int pci_pm_freeze(struct device *dev) | 612 | static int pci_pm_freeze(struct device *dev) |
| 581 | { | 613 | { |
| 582 | struct pci_dev *pci_dev = to_pci_dev(dev); | 614 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 583 | struct device_driver *drv = dev->driver; | 615 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| 584 | int error = 0; | ||
| 585 | 616 | ||
| 586 | if (pci_has_legacy_pm_support(pci_dev)) | 617 | if (pci_has_legacy_pm_support(pci_dev)) |
| 587 | return pci_legacy_suspend(dev, PMSG_FREEZE); | 618 | return pci_legacy_suspend(dev, PMSG_FREEZE); |
| 588 | 619 | ||
| 589 | if (drv && drv->pm && drv->pm->freeze) { | 620 | if (!pm) { |
| 590 | error = drv->pm->freeze(dev); | 621 | pci_pm_default_suspend(pci_dev); |
| 591 | suspend_report_result(drv->pm->freeze, error); | 622 | return 0; |
| 623 | } | ||
| 624 | |||
| 625 | pci_dev->state_saved = false; | ||
| 626 | |||
| 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; | ||
| 592 | } | 634 | } |
| 593 | 635 | ||
| 594 | if (!error) | 636 | if (!pci_dev->state_saved) |
| 595 | pci_pm_default_suspend_generic(pci_dev); | 637 | pci_save_state(pci_dev); |
| 596 | 638 | ||
| 597 | return error; | 639 | return 0; |
| 598 | } | 640 | } |
| 599 | 641 | ||
| 600 | static int pci_pm_freeze_noirq(struct device *dev) | 642 | static int pci_pm_freeze_noirq(struct device *dev) |
| @@ -637,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev) | |||
| 637 | static int pci_pm_thaw(struct device *dev) | 679 | static int pci_pm_thaw(struct device *dev) |
| 638 | { | 680 | { |
| 639 | struct pci_dev *pci_dev = to_pci_dev(dev); | 681 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 640 | struct device_driver *drv = dev->driver; | 682 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| 641 | int error = 0; | 683 | int error = 0; |
| 642 | 684 | ||
| 643 | if (pci_has_legacy_pm_support(pci_dev)) | 685 | if (pci_has_legacy_pm_support(pci_dev)) |
| 644 | return pci_legacy_resume(dev); | 686 | return pci_legacy_resume(dev); |
| 645 | 687 | ||
| 646 | pci_pm_reenable_device(pci_dev); | 688 | if (pm) { |
| 647 | 689 | if (pm->thaw) | |
| 648 | if (drv && drv->pm && drv->pm->thaw) | 690 | error = pm->thaw(dev); |
| 649 | error = drv->pm->thaw(dev); | 691 | } else { |
| 692 | pci_pm_reenable_device(pci_dev); | ||
| 693 | } | ||
| 650 | 694 | ||
| 651 | return error; | 695 | return error; |
| 652 | } | 696 | } |
| @@ -654,19 +698,29 @@ static int pci_pm_thaw(struct device *dev) | |||
| 654 | static int pci_pm_poweroff(struct device *dev) | 698 | static int pci_pm_poweroff(struct device *dev) |
| 655 | { | 699 | { |
| 656 | struct pci_dev *pci_dev = to_pci_dev(dev); | 700 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 657 | struct device_driver *drv = dev->driver; | 701 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| 658 | int error = 0; | 702 | int error = 0; |
| 659 | 703 | ||
| 660 | if (pci_has_legacy_pm_support(pci_dev)) | 704 | if (pci_has_legacy_pm_support(pci_dev)) |
| 661 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); | 705 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); |
| 662 | 706 | ||
| 663 | if (drv && drv->pm && drv->pm->poweroff) { | 707 | if (!pm) { |
| 664 | error = drv->pm->poweroff(dev); | 708 | pci_pm_default_suspend(pci_dev); |
| 665 | suspend_report_result(drv->pm->poweroff, error); | 709 | goto Fixup; |
| 666 | } | 710 | } |
| 667 | 711 | ||
| 668 | if (!error) | 712 | pci_dev->state_saved = false; |
| 669 | pci_pm_default_suspend(pci_dev); | 713 | |
| 714 | if (pm->poweroff) { | ||
| 715 | error = pm->poweroff(dev); | ||
| 716 | suspend_report_result(pm->poweroff, error); | ||
| 717 | } | ||
| 718 | |||
| 719 | if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) | ||
| 720 | pci_prepare_to_sleep(pci_dev); | ||
| 721 | |||
| 722 | Fixup: | ||
| 723 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
| 670 | 724 | ||
| 671 | return error; | 725 | return error; |
| 672 | } | 726 | } |
| @@ -707,16 +761,27 @@ static int pci_pm_restore_noirq(struct device *dev) | |||
| 707 | static int pci_pm_restore(struct device *dev) | 761 | static int pci_pm_restore(struct device *dev) |
| 708 | { | 762 | { |
| 709 | struct pci_dev *pci_dev = to_pci_dev(dev); | 763 | struct pci_dev *pci_dev = to_pci_dev(dev); |
| 710 | struct device_driver *drv = dev->driver; | 764 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| 711 | int error = 0; | 765 | int error = 0; |
| 712 | 766 | ||
| 767 | /* | ||
| 768 | * This is necessary for the hibernation error path in which restore is | ||
| 769 | * called without restoring the standard config registers of the device. | ||
| 770 | */ | ||
| 771 | if (pci_dev->state_saved) | ||
| 772 | pci_restore_standard_config(pci_dev); | ||
| 773 | |||
| 713 | if (pci_has_legacy_pm_support(pci_dev)) | 774 | if (pci_has_legacy_pm_support(pci_dev)) |
| 714 | return pci_legacy_resume(dev); | 775 | return pci_legacy_resume(dev); |
| 715 | 776 | ||
| 716 | error = pci_pm_default_resume(pci_dev); | 777 | pci_pm_default_resume(pci_dev); |
| 717 | 778 | ||
| 718 | if (!error && drv && drv->pm && drv->pm->restore) | 779 | if (pm) { |
| 719 | error = drv->pm->restore(dev); | 780 | if (pm->restore) |
| 781 | error = pm->restore(dev); | ||
| 782 | } else { | ||
| 783 | pci_pm_reenable_device(pci_dev); | ||
| 784 | } | ||
| 720 | 785 | ||
| 721 | return error; | 786 | return error; |
| 722 | } | 787 | } |
