diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-03-26 17:51:40 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2009-03-30 15:46:56 -0400 |
commit | 0e5dd46b761195356065a30611f265adec286d0d (patch) | |
tree | 4ca10dac14ac44789a51048c4ceb3989be175f63 | |
parent | 931ff68a5a53fa84bcdf9b1b179a80e54e034bd0 (diff) |
PCI PM: Introduce __pci_[start|complete]_power_transition() (rev. 2)
The radeonfb driver needs to program the device's PMCSR directly due
to some quirky hardware it has to handle (see
http://bugzilla.kernel.org/show_bug.cgi?id=12846 for details) and
after doing that it needs to call the platform (usually ACPI) to
finish the power transition of the device. Currently it uses
pci_set_power_state() for this purpose, however making a specific
assumption about the internal behavior of this function, which has
changed recently so that this assumption is no longer satisfied.
For this reason, introduce __pci_complete_power_transition() that may
be called by the radeonfb driver to complete the power transition of
the device. For symmetry, introduce __pci_start_power_transition().
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-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); |