aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.c69
1 files changed, 51 insertions, 18 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 979ceb1d37e8..de54fd643baf 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -540,6 +540,53 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
540} 540}
541 541
542/** 542/**
543 * pci_platform_power_transition - Use platform to change device power state
544 * @dev: PCI device to handle.
545 * @state: State to put the device into.
546 */
547static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state)
548{
549 int error;
550
551 if (platform_pci_power_manageable(dev)) {
552 error = platform_pci_set_power_state(dev, state);
553 if (!error)
554 pci_update_current_state(dev, state);
555 } else {
556 error = -ENODEV;
557 /* Fall back to PCI_D0 if native PM is not supported */
558 pci_update_current_state(dev, PCI_D0);
559 }
560
561 return error;
562}
563
564/**
565 * __pci_start_power_transition - Start power transition of a PCI device
566 * @dev: PCI device to handle.
567 * @state: State to put the device into.
568 */
569static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
570{
571 if (state == PCI_D0)
572 pci_platform_power_transition(dev, PCI_D0);
573}
574
575/**
576 * __pci_complete_power_transition - Complete power transition of a PCI device
577 * @dev: PCI device to handle.
578 * @state: State to put the device into.
579 *
580 * This function should not be called directly by device drivers.
581 */
582int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state)
583{
584 return state > PCI_D0 ?
585 pci_platform_power_transition(dev, state) : -EINVAL;
586}
587EXPORT_SYMBOL_GPL(__pci_complete_power_transition);
588
589/**
543 * pci_set_power_state - Set the power state of a PCI device 590 * pci_set_power_state - Set the power state of a PCI device
544 * @dev: PCI device to handle. 591 * @dev: PCI device to handle.
545 * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. 592 * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
@@ -575,16 +622,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
575 if (dev->current_state == state) 622 if (dev->current_state == state)
576 return 0; 623 return 0;
577 624
578 if (state == PCI_D0) { 625 __pci_start_power_transition(dev, state);
579 /* 626
580 * Allow the platform to change the state, for example via ACPI
581 * _PR0, _PS0 and some such, but do not trust it.
582 */
583 int ret = platform_pci_power_manageable(dev) ?
584 platform_pci_set_power_state(dev, PCI_D0) : 0;
585 if (!ret)
586 pci_update_current_state(dev, PCI_D0);
587 }
588 /* This device is quirked not to be put into D3, so 627 /* This device is quirked not to be put into D3, so
589 don't put it in D3 */ 628 don't put it in D3 */
590 if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) 629 if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
@@ -592,14 +631,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
592 631
593 error = pci_raw_set_power_state(dev, state); 632 error = pci_raw_set_power_state(dev, state);
594 633
595 if (state > PCI_D0 && platform_pci_power_manageable(dev)) { 634 if (!__pci_complete_power_transition(dev, state))
596 /* Allow the platform to finalize the transition */ 635 error = 0;
597 int ret = platform_pci_set_power_state(dev, state);
598 if (!ret) {
599 pci_update_current_state(dev, state);
600 error = 0;
601 }
602 }
603 636
604 return error; 637 return error;
605} 638}