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); |
