diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2016-10-03 10:43:21 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2016-10-03 10:43:21 -0400 |
commit | 3f4f35678fb83da2f9ef17ddaa7507a45c2f7049 (patch) | |
tree | bf73ab51e0e7a9847e94fbcae1323c83aa81ed7c /drivers/pci/pci.c | |
parent | 6c6cba49495c3a254c1bc922afeb69e1431d2c23 (diff) | |
parent | a0d2a959d3da343554523d26902de1d40a9e5c28 (diff) |
Merge branch 'pci/pm' into next
* pci/pm:
PCI: Avoid unnecessary resume after direct-complete
PCI: Recognize D3cold in pci_update_current_state()
PCI: Query platform firmware for device power state
PCI: Afford direct-complete to devices with non-standard PM
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 52 |
1 files changed, 35 insertions, 17 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index aab9d5115a5f..b2be8957a290 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -552,8 +552,9 @@ static const struct pci_platform_pm_ops *pci_platform_pm; | |||
552 | 552 | ||
553 | int pci_set_platform_pm(const struct pci_platform_pm_ops *ops) | 553 | int pci_set_platform_pm(const struct pci_platform_pm_ops *ops) |
554 | { | 554 | { |
555 | if (!ops->is_manageable || !ops->set_state || !ops->choose_state || | 555 | if (!ops->is_manageable || !ops->set_state || !ops->get_state || |
556 | !ops->sleep_wake || !ops->run_wake || !ops->need_resume) | 556 | !ops->choose_state || !ops->sleep_wake || !ops->run_wake || |
557 | !ops->need_resume) | ||
557 | return -EINVAL; | 558 | return -EINVAL; |
558 | pci_platform_pm = ops; | 559 | pci_platform_pm = ops; |
559 | return 0; | 560 | return 0; |
@@ -570,6 +571,11 @@ static inline int platform_pci_set_power_state(struct pci_dev *dev, | |||
570 | return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS; | 571 | return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS; |
571 | } | 572 | } |
572 | 573 | ||
574 | static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev) | ||
575 | { | ||
576 | return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN; | ||
577 | } | ||
578 | |||
573 | static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) | 579 | static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) |
574 | { | 580 | { |
575 | return pci_platform_pm ? | 581 | return pci_platform_pm ? |
@@ -701,26 +707,25 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) | |||
701 | } | 707 | } |
702 | 708 | ||
703 | /** | 709 | /** |
704 | * pci_update_current_state - Read PCI power state of given device from its | 710 | * pci_update_current_state - Read power state of given device and cache it |
705 | * PCI PM registers and cache it | ||
706 | * @dev: PCI device to handle. | 711 | * @dev: PCI device to handle. |
707 | * @state: State to cache in case the device doesn't have the PM capability | 712 | * @state: State to cache in case the device doesn't have the PM capability |
713 | * | ||
714 | * The power state is read from the PMCSR register, which however is | ||
715 | * inaccessible in D3cold. The platform firmware is therefore queried first | ||
716 | * to detect accessibility of the register. In case the platform firmware | ||
717 | * reports an incorrect state or the device isn't power manageable by the | ||
718 | * platform at all, we try to detect D3cold by testing accessibility of the | ||
719 | * vendor ID in config space. | ||
708 | */ | 720 | */ |
709 | void pci_update_current_state(struct pci_dev *dev, pci_power_t state) | 721 | void pci_update_current_state(struct pci_dev *dev, pci_power_t state) |
710 | { | 722 | { |
711 | if (dev->pm_cap) { | 723 | if (platform_pci_get_power_state(dev) == PCI_D3cold || |
724 | !pci_device_is_present(dev)) { | ||
725 | dev->current_state = PCI_D3cold; | ||
726 | } else if (dev->pm_cap) { | ||
712 | u16 pmcsr; | 727 | u16 pmcsr; |
713 | 728 | ||
714 | /* | ||
715 | * Configuration space is not accessible for device in | ||
716 | * D3cold, so just keep or set D3cold for safety | ||
717 | */ | ||
718 | if (dev->current_state == PCI_D3cold) | ||
719 | return; | ||
720 | if (state == PCI_D3cold) { | ||
721 | dev->current_state = PCI_D3cold; | ||
722 | return; | ||
723 | } | ||
724 | pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); | 729 | pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); |
725 | dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); | 730 | dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); |
726 | } else { | 731 | } else { |
@@ -1959,9 +1964,22 @@ static pci_power_t pci_target_state(struct pci_dev *dev) | |||
1959 | default: | 1964 | default: |
1960 | target_state = state; | 1965 | target_state = state; |
1961 | } | 1966 | } |
1962 | } else if (!dev->pm_cap) { | 1967 | |
1968 | return target_state; | ||
1969 | } | ||
1970 | |||
1971 | if (!dev->pm_cap) | ||
1963 | target_state = PCI_D0; | 1972 | target_state = PCI_D0; |
1964 | } else if (device_may_wakeup(&dev->dev)) { | 1973 | |
1974 | /* | ||
1975 | * If the device is in D3cold even though it's not power-manageable by | ||
1976 | * the platform, it may have been powered down by non-standard means. | ||
1977 | * Best to let it slumber. | ||
1978 | */ | ||
1979 | if (dev->current_state == PCI_D3cold) | ||
1980 | target_state = PCI_D3cold; | ||
1981 | |||
1982 | if (device_may_wakeup(&dev->dev)) { | ||
1965 | /* | 1983 | /* |
1966 | * Find the deepest state from which the device can generate | 1984 | * Find the deepest state from which the device can generate |
1967 | * wake-up events, make it the target state and enable device | 1985 | * wake-up events, make it the target state and enable device |