diff options
-rw-r--r-- | drivers/pci/pci.c | 69 | ||||
-rw-r--r-- | include/linux/pci.h | 1 |
2 files changed, 52 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 | */ | ||
547 | static 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 | */ | ||
569 | static 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 | */ | ||
582 | int __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 | } | ||
587 | EXPORT_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 | } |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 7bd624bfdcfd..df3644132617 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -689,6 +689,7 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size); | |||
689 | /* Power management related routines */ | 689 | /* Power management related routines */ |
690 | int pci_save_state(struct pci_dev *dev); | 690 | int pci_save_state(struct pci_dev *dev); |
691 | int pci_restore_state(struct pci_dev *dev); | 691 | int pci_restore_state(struct pci_dev *dev); |
692 | int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state); | ||
692 | int pci_set_power_state(struct pci_dev *dev, pci_power_t state); | 693 | int pci_set_power_state(struct pci_dev *dev, pci_power_t state); |
693 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); | 694 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); |
694 | bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); | 695 | bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); |