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 | |
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
-rw-r--r-- | drivers/pci/pci-acpi.c | 22 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 15 | ||||
-rw-r--r-- | drivers/pci/pci.c | 52 | ||||
-rw-r--r-- | drivers/pci/pci.h | 3 |
4 files changed, 73 insertions, 19 deletions
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 9a033e8ee9a4..d966d47c9e80 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c | |||
@@ -452,6 +452,27 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) | |||
452 | return error; | 452 | return error; |
453 | } | 453 | } |
454 | 454 | ||
455 | static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) | ||
456 | { | ||
457 | struct acpi_device *adev = ACPI_COMPANION(&dev->dev); | ||
458 | static const pci_power_t state_conv[] = { | ||
459 | [ACPI_STATE_D0] = PCI_D0, | ||
460 | [ACPI_STATE_D1] = PCI_D1, | ||
461 | [ACPI_STATE_D2] = PCI_D2, | ||
462 | [ACPI_STATE_D3_HOT] = PCI_D3hot, | ||
463 | [ACPI_STATE_D3_COLD] = PCI_D3cold, | ||
464 | }; | ||
465 | int state; | ||
466 | |||
467 | if (!adev || !acpi_device_power_manageable(adev)) | ||
468 | return PCI_UNKNOWN; | ||
469 | |||
470 | if (acpi_device_get_power(adev, &state) || state == ACPI_STATE_UNKNOWN) | ||
471 | return PCI_UNKNOWN; | ||
472 | |||
473 | return state_conv[state]; | ||
474 | } | ||
475 | |||
455 | static bool acpi_pci_can_wakeup(struct pci_dev *dev) | 476 | static bool acpi_pci_can_wakeup(struct pci_dev *dev) |
456 | { | 477 | { |
457 | struct acpi_device *adev = ACPI_COMPANION(&dev->dev); | 478 | struct acpi_device *adev = ACPI_COMPANION(&dev->dev); |
@@ -534,6 +555,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) | |||
534 | static const struct pci_platform_pm_ops acpi_pci_platform_pm = { | 555 | static const struct pci_platform_pm_ops acpi_pci_platform_pm = { |
535 | .is_manageable = acpi_pci_power_manageable, | 556 | .is_manageable = acpi_pci_power_manageable, |
536 | .set_state = acpi_pci_set_power_state, | 557 | .set_state = acpi_pci_set_power_state, |
558 | .get_state = acpi_pci_get_power_state, | ||
537 | .choose_state = acpi_pci_choose_state, | 559 | .choose_state = acpi_pci_choose_state, |
538 | .sleep_wake = acpi_pci_sleep_wake, | 560 | .sleep_wake = acpi_pci_sleep_wake, |
539 | .run_wake = acpi_pci_run_wake, | 561 | .run_wake = acpi_pci_run_wake, |
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index edd78e04b693..1ccce1cd6aca 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -682,8 +682,19 @@ static int pci_pm_prepare(struct device *dev) | |||
682 | 682 | ||
683 | static void pci_pm_complete(struct device *dev) | 683 | static void pci_pm_complete(struct device *dev) |
684 | { | 684 | { |
685 | pci_dev_complete_resume(to_pci_dev(dev)); | 685 | struct pci_dev *pci_dev = to_pci_dev(dev); |
686 | pm_complete_with_resume_check(dev); | 686 | |
687 | pci_dev_complete_resume(pci_dev); | ||
688 | pm_generic_complete(dev); | ||
689 | |||
690 | /* Resume device if platform firmware has put it in reset-power-on */ | ||
691 | if (dev->power.direct_complete && pm_resume_via_firmware()) { | ||
692 | pci_power_t pre_sleep_state = pci_dev->current_state; | ||
693 | |||
694 | pci_update_current_state(pci_dev, pci_dev->current_state); | ||
695 | if (pci_dev->current_state < pre_sleep_state) | ||
696 | pm_request_resume(dev); | ||
697 | } | ||
687 | } | 698 | } |
688 | 699 | ||
689 | #else /* !CONFIG_PM_SLEEP */ | 700 | #else /* !CONFIG_PM_SLEEP */ |
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 |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 194521bfb1a3..451856210e18 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -42,6 +42,8 @@ int pci_probe_reset_function(struct pci_dev *dev); | |||
42 | * | 42 | * |
43 | * @set_state: invokes the platform firmware to set the device's power state | 43 | * @set_state: invokes the platform firmware to set the device's power state |
44 | * | 44 | * |
45 | * @get_state: queries the platform firmware for a device's current power state | ||
46 | * | ||
45 | * @choose_state: returns PCI power state of given device preferred by the | 47 | * @choose_state: returns PCI power state of given device preferred by the |
46 | * platform; to be used during system-wide transitions from a | 48 | * platform; to be used during system-wide transitions from a |
47 | * sleeping state to the working state and vice versa | 49 | * sleeping state to the working state and vice versa |
@@ -62,6 +64,7 @@ int pci_probe_reset_function(struct pci_dev *dev); | |||
62 | struct pci_platform_pm_ops { | 64 | struct pci_platform_pm_ops { |
63 | bool (*is_manageable)(struct pci_dev *dev); | 65 | bool (*is_manageable)(struct pci_dev *dev); |
64 | int (*set_state)(struct pci_dev *dev, pci_power_t state); | 66 | int (*set_state)(struct pci_dev *dev, pci_power_t state); |
67 | pci_power_t (*get_state)(struct pci_dev *dev); | ||
65 | pci_power_t (*choose_state)(struct pci_dev *dev); | 68 | pci_power_t (*choose_state)(struct pci_dev *dev); |
66 | int (*sleep_wake)(struct pci_dev *dev, bool enable); | 69 | int (*sleep_wake)(struct pci_dev *dev, bool enable); |
67 | int (*run_wake)(struct pci_dev *dev, bool enable); | 70 | int (*run_wake)(struct pci_dev *dev, bool enable); |