diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-05-17 14:17:06 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-05-19 18:26:07 -0400 |
commit | b3bad72e494fb2ff0c81be4ca2ddb94adf6a47c2 (patch) | |
tree | 522ad3082eb938a99b85ed922d1eb1cdc5a925f1 /drivers | |
parent | 279e677faa775ad16e75c32e1bf4a37f8158bc61 (diff) |
PCI PM: Fix initialization and kexec breakage for some devices
Recent PCI PM changes introduced a bug that causes some devices to be
mishandled after kexec and during early initialization. The failure
scenario in the kexec case is the following:
* Assume a PCI device is not power-manageable by the platform and has
PCI_PM_CTRL_NO_SOFT_RESET set in PMCSR.
* The device is put into D3 before kexec (using the native PCI PM).
* After kexec, pci_setup_device() sets the device's power state to
PCI_UNKNOWN.
* pci_set_power_state(dev, PCI_D0) is called by the device's driver.
* __pci_start_power_transition(dev, PCI_D0) is called and since the
device is not power-manageable by the platform, it causes
pci_update_current_state(dev, PCI_D0) to be called. As a result
the device's current_state field is updated to PCI_D3, in
accordance with the contents of its PCI PM registers.
* pci_raw_set_power_state() is called and it changes the device power
state to D0. *However*, it should also call pci_restore_bars() to
reinitialize the device, but it doesn't, because the device's
current_state field has been modified earlier.
To prevent this from happening, modify pci_platform_power_transition()
so that it doesn't use pci_update_current_state() to update the
current_state field for devices that aren't power-manageable by the
platform. Instead, this field should be updated directly for devices
that don't support the native PCI PM.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pci.c | 3 |
1 files changed, 2 insertions, 1 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 34bf0fdf5047..1a91bf9687af 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -557,7 +557,8 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) | |||
557 | } else { | 557 | } else { |
558 | error = -ENODEV; | 558 | error = -ENODEV; |
559 | /* Fall back to PCI_D0 if native PM is not supported */ | 559 | /* Fall back to PCI_D0 if native PM is not supported */ |
560 | pci_update_current_state(dev, PCI_D0); | 560 | if (!dev->pm_cap) |
561 | dev->current_state = PCI_D0; | ||
561 | } | 562 | } |
562 | 563 | ||
563 | return error; | 564 | return error; |