diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
| -rw-r--r-- | drivers/pci/pci-driver.c | 160 |
1 files changed, 133 insertions, 27 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e5d47be3c6d7..f9a0aec3abcf 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
| 18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
| 19 | #include <linux/cpu.h> | 19 | #include <linux/cpu.h> |
| 20 | #include <linux/pm_runtime.h> | ||
| 20 | #include "pci.h" | 21 | #include "pci.h" |
| 21 | 22 | ||
| 22 | struct pci_dynid { | 23 | struct pci_dynid { |
| @@ -404,6 +405,35 @@ static void pci_device_shutdown(struct device *dev) | |||
| 404 | pci_msix_shutdown(pci_dev); | 405 | pci_msix_shutdown(pci_dev); |
| 405 | } | 406 | } |
| 406 | 407 | ||
| 408 | #ifdef CONFIG_PM_OPS | ||
| 409 | |||
| 410 | /* Auxiliary functions used for system resume and run-time resume. */ | ||
| 411 | |||
| 412 | /** | ||
| 413 | * pci_restore_standard_config - restore standard config registers of PCI device | ||
| 414 | * @pci_dev: PCI device to handle | ||
| 415 | */ | ||
| 416 | static int pci_restore_standard_config(struct pci_dev *pci_dev) | ||
| 417 | { | ||
| 418 | pci_update_current_state(pci_dev, PCI_UNKNOWN); | ||
| 419 | |||
| 420 | if (pci_dev->current_state != PCI_D0) { | ||
| 421 | int error = pci_set_power_state(pci_dev, PCI_D0); | ||
| 422 | if (error) | ||
| 423 | return error; | ||
| 424 | } | ||
| 425 | |||
| 426 | return pci_restore_state(pci_dev); | ||
| 427 | } | ||
| 428 | |||
| 429 | static void pci_pm_default_resume_early(struct pci_dev *pci_dev) | ||
| 430 | { | ||
| 431 | pci_restore_standard_config(pci_dev); | ||
| 432 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
| 433 | } | ||
| 434 | |||
| 435 | #endif | ||
| 436 | |||
| 407 | #ifdef CONFIG_PM_SLEEP | 437 | #ifdef CONFIG_PM_SLEEP |
| 408 | 438 | ||
| 409 | /* | 439 | /* |
| @@ -520,29 +550,6 @@ static int pci_legacy_resume(struct device *dev) | |||
| 520 | 550 | ||
| 521 | /* Auxiliary functions used by the new power management framework */ | 551 | /* Auxiliary functions used by the new power management framework */ |
| 522 | 552 | ||
| 523 | /** | ||
| 524 | * pci_restore_standard_config - restore standard config registers of PCI device | ||
| 525 | * @pci_dev: PCI device to handle | ||
| 526 | */ | ||
| 527 | static int pci_restore_standard_config(struct pci_dev *pci_dev) | ||
| 528 | { | ||
| 529 | pci_update_current_state(pci_dev, PCI_UNKNOWN); | ||
| 530 | |||
| 531 | if (pci_dev->current_state != PCI_D0) { | ||
| 532 | int error = pci_set_power_state(pci_dev, PCI_D0); | ||
| 533 | if (error) | ||
| 534 | return error; | ||
| 535 | } | ||
| 536 | |||
| 537 | return pci_restore_state(pci_dev); | ||
| 538 | } | ||
| 539 | |||
| 540 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) | ||
| 541 | { | ||
| 542 | pci_restore_standard_config(pci_dev); | ||
| 543 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
| 544 | } | ||
| 545 | |||
| 546 | static void pci_pm_default_resume(struct pci_dev *pci_dev) | 553 | static void pci_pm_default_resume(struct pci_dev *pci_dev) |
| 547 | { | 554 | { |
| 548 | pci_fixup_device(pci_fixup_resume, pci_dev); | 555 | pci_fixup_device(pci_fixup_resume, pci_dev); |
| @@ -581,6 +588,17 @@ static int pci_pm_prepare(struct device *dev) | |||
| 581 | struct device_driver *drv = dev->driver; | 588 | struct device_driver *drv = dev->driver; |
| 582 | int error = 0; | 589 | int error = 0; |
| 583 | 590 | ||
| 591 | /* | ||
| 592 | * PCI devices suspended at run time need to be resumed at this | ||
| 593 | * point, because in general it is necessary to reconfigure them for | ||
| 594 | * system suspend. Namely, if the device is supposed to wake up the | ||
| 595 | * system from the sleep state, we may need to reconfigure it for this | ||
| 596 | * purpose. In turn, if the device is not supposed to wake up the | ||
| 597 | * system from the sleep state, we'll have to prevent it from signaling | ||
| 598 | * wake-up. | ||
| 599 | */ | ||
| 600 | pm_runtime_resume(dev); | ||
| 601 | |||
| 584 | if (drv && drv->pm && drv->pm->prepare) | 602 | if (drv && drv->pm && drv->pm->prepare) |
| 585 | error = drv->pm->prepare(dev); | 603 | error = drv->pm->prepare(dev); |
| 586 | 604 | ||
| @@ -595,6 +613,13 @@ static void pci_pm_complete(struct device *dev) | |||
| 595 | drv->pm->complete(dev); | 613 | drv->pm->complete(dev); |
| 596 | } | 614 | } |
| 597 | 615 | ||
| 616 | #else /* !CONFIG_PM_SLEEP */ | ||
| 617 | |||
| 618 | #define pci_pm_prepare NULL | ||
| 619 | #define pci_pm_complete NULL | ||
| 620 | |||
| 621 | #endif /* !CONFIG_PM_SLEEP */ | ||
| 622 | |||
| 598 | #ifdef CONFIG_SUSPEND | 623 | #ifdef CONFIG_SUSPEND |
| 599 | 624 | ||
| 600 | static int pci_pm_suspend(struct device *dev) | 625 | static int pci_pm_suspend(struct device *dev) |
| @@ -681,7 +706,7 @@ static int pci_pm_resume_noirq(struct device *dev) | |||
| 681 | struct device_driver *drv = dev->driver; | 706 | struct device_driver *drv = dev->driver; |
| 682 | int error = 0; | 707 | int error = 0; |
| 683 | 708 | ||
| 684 | pci_pm_default_resume_noirq(pci_dev); | 709 | pci_pm_default_resume_early(pci_dev); |
| 685 | 710 | ||
| 686 | if (pci_has_legacy_pm_support(pci_dev)) | 711 | if (pci_has_legacy_pm_support(pci_dev)) |
| 687 | return pci_legacy_resume_early(dev); | 712 | return pci_legacy_resume_early(dev); |
| @@ -879,7 +904,7 @@ static int pci_pm_restore_noirq(struct device *dev) | |||
| 879 | struct device_driver *drv = dev->driver; | 904 | struct device_driver *drv = dev->driver; |
| 880 | int error = 0; | 905 | int error = 0; |
| 881 | 906 | ||
| 882 | pci_pm_default_resume_noirq(pci_dev); | 907 | pci_pm_default_resume_early(pci_dev); |
| 883 | 908 | ||
| 884 | if (pci_has_legacy_pm_support(pci_dev)) | 909 | if (pci_has_legacy_pm_support(pci_dev)) |
| 885 | return pci_legacy_resume_early(dev); | 910 | return pci_legacy_resume_early(dev); |
| @@ -931,6 +956,84 @@ static int pci_pm_restore(struct device *dev) | |||
| 931 | 956 | ||
| 932 | #endif /* !CONFIG_HIBERNATION */ | 957 | #endif /* !CONFIG_HIBERNATION */ |
| 933 | 958 | ||
| 959 | #ifdef CONFIG_PM_RUNTIME | ||
| 960 | |||
| 961 | static int pci_pm_runtime_suspend(struct device *dev) | ||
| 962 | { | ||
| 963 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
| 964 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | ||
| 965 | pci_power_t prev = pci_dev->current_state; | ||
| 966 | int error; | ||
| 967 | |||
| 968 | if (!pm || !pm->runtime_suspend) | ||
| 969 | return -ENOSYS; | ||
| 970 | |||
| 971 | error = pm->runtime_suspend(dev); | ||
| 972 | suspend_report_result(pm->runtime_suspend, error); | ||
| 973 | if (error) | ||
| 974 | return error; | ||
| 975 | |||
| 976 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
| 977 | |||
| 978 | if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 | ||
| 979 | && pci_dev->current_state != PCI_UNKNOWN) { | ||
| 980 | WARN_ONCE(pci_dev->current_state != prev, | ||
| 981 | "PCI PM: State of device not saved by %pF\n", | ||
| 982 | pm->runtime_suspend); | ||
| 983 | return 0; | ||
| 984 | } | ||
| 985 | |||
| 986 | if (!pci_dev->state_saved) | ||
| 987 | pci_save_state(pci_dev); | ||
| 988 | |||
| 989 | pci_finish_runtime_suspend(pci_dev); | ||
| 990 | |||
| 991 | return 0; | ||
| 992 | } | ||
| 993 | |||
| 994 | static int pci_pm_runtime_resume(struct device *dev) | ||
| 995 | { | ||
| 996 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
| 997 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | ||
| 998 | |||
| 999 | if (!pm || !pm->runtime_resume) | ||
| 1000 | return -ENOSYS; | ||
| 1001 | |||
| 1002 | pci_pm_default_resume_early(pci_dev); | ||
| 1003 | __pci_enable_wake(pci_dev, PCI_D0, true, false); | ||
| 1004 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
| 1005 | |||
| 1006 | return pm->runtime_resume(dev); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | static int pci_pm_runtime_idle(struct device *dev) | ||
| 1010 | { | ||
| 1011 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | ||
| 1012 | |||
| 1013 | if (!pm) | ||
| 1014 | return -ENOSYS; | ||
| 1015 | |||
| 1016 | if (pm->runtime_idle) { | ||
| 1017 | int ret = pm->runtime_idle(dev); | ||
| 1018 | if (ret) | ||
| 1019 | return ret; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | pm_runtime_suspend(dev); | ||
| 1023 | |||
| 1024 | return 0; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | #else /* !CONFIG_PM_RUNTIME */ | ||
| 1028 | |||
| 1029 | #define pci_pm_runtime_suspend NULL | ||
| 1030 | #define pci_pm_runtime_resume NULL | ||
| 1031 | #define pci_pm_runtime_idle NULL | ||
| 1032 | |||
| 1033 | #endif /* !CONFIG_PM_RUNTIME */ | ||
| 1034 | |||
| 1035 | #ifdef CONFIG_PM_OPS | ||
| 1036 | |||
| 934 | const struct dev_pm_ops pci_dev_pm_ops = { | 1037 | const struct dev_pm_ops pci_dev_pm_ops = { |
| 935 | .prepare = pci_pm_prepare, | 1038 | .prepare = pci_pm_prepare, |
| 936 | .complete = pci_pm_complete, | 1039 | .complete = pci_pm_complete, |
| @@ -946,15 +1049,18 @@ const struct dev_pm_ops pci_dev_pm_ops = { | |||
| 946 | .thaw_noirq = pci_pm_thaw_noirq, | 1049 | .thaw_noirq = pci_pm_thaw_noirq, |
| 947 | .poweroff_noirq = pci_pm_poweroff_noirq, | 1050 | .poweroff_noirq = pci_pm_poweroff_noirq, |
| 948 | .restore_noirq = pci_pm_restore_noirq, | 1051 | .restore_noirq = pci_pm_restore_noirq, |
| 1052 | .runtime_suspend = pci_pm_runtime_suspend, | ||
| 1053 | .runtime_resume = pci_pm_runtime_resume, | ||
| 1054 | .runtime_idle = pci_pm_runtime_idle, | ||
| 949 | }; | 1055 | }; |
| 950 | 1056 | ||
| 951 | #define PCI_PM_OPS_PTR (&pci_dev_pm_ops) | 1057 | #define PCI_PM_OPS_PTR (&pci_dev_pm_ops) |
| 952 | 1058 | ||
| 953 | #else /* !CONFIG_PM_SLEEP */ | 1059 | #else /* !COMFIG_PM_OPS */ |
| 954 | 1060 | ||
| 955 | #define PCI_PM_OPS_PTR NULL | 1061 | #define PCI_PM_OPS_PTR NULL |
| 956 | 1062 | ||
| 957 | #endif /* !CONFIG_PM_SLEEP */ | 1063 | #endif /* !COMFIG_PM_OPS */ |
| 958 | 1064 | ||
| 959 | /** | 1065 | /** |
| 960 | * __pci_register_driver - register a new pci driver | 1066 | * __pci_register_driver - register a new pci driver |
