aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-03-26 17:51:40 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2009-03-30 15:46:56 -0400
commit0e5dd46b761195356065a30611f265adec286d0d (patch)
tree4ca10dac14ac44789a51048c4ceb3989be175f63
parent931ff68a5a53fa84bcdf9b1b179a80e54e034bd0 (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.c69
-rw-r--r--include/linux/pci.h1
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 */
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}
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 */
690int pci_save_state(struct pci_dev *dev); 690int pci_save_state(struct pci_dev *dev);
691int pci_restore_state(struct pci_dev *dev); 691int pci_restore_state(struct pci_dev *dev);
692int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state);
692int pci_set_power_state(struct pci_dev *dev, pci_power_t state); 693int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
693pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); 694pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
694bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); 695bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);