[PATCH] PCI: PCIE power management quirk
When changing power states from D0->DX and then from DX->D0, some Intel PCIE chipsets will cause a device reset to occur. This will cause problems for any D State other than D3, since any state information that the driver will expect to be present coming from a D1 or D2 state will have been cleared. This patch addes a flag to the pci_dev structure to indicate that devices should not use states D1 or D2, and will set that flag for the affected chipsets. This patch also modifies pci_set_power_state() so that when a device driver tries to set the power state on a device that is downstream from an affected chipset, or on one of the affected devices it only allows state changes to or from D0 & D3. In addition, this patch allows the delay time between D3->D0 to be changed via a quirk. These chipsets also need additional time to change states beyond the normal 10ms. Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
6f0312fd7e
commit
ffadcc2ff4
4 changed files with 51 additions and 2 deletions
|
@ -19,6 +19,7 @@
|
|||
#include <asm/dma.h> /* isa_dma_bridge_buggy */
|
||||
#include "pci.h"
|
||||
|
||||
unsigned int pci_pm_d3_delay = 10;
|
||||
|
||||
/**
|
||||
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
|
||||
|
@ -313,6 +314,14 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|||
} else if (dev->current_state == state)
|
||||
return 0; /* we're already there */
|
||||
|
||||
/*
|
||||
* If the device or the parent bridge can't support PCI PM, ignore
|
||||
* the request if we're doing anything besides putting it into D0
|
||||
* (which would only happen on boot).
|
||||
*/
|
||||
if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
|
||||
return 0;
|
||||
|
||||
/* find PCI PM capability in list */
|
||||
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
|
||||
|
@ -363,7 +372,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|||
/* Mandatory power management transition delays */
|
||||
/* see PCI PM 1.1 5.6.1 table 18 */
|
||||
if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
|
||||
msleep(10);
|
||||
msleep(pci_pm_d3_delay);
|
||||
else if (state == PCI_D2 || dev->current_state == PCI_D2)
|
||||
udelay(200);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ extern int pci_msi_quirk;
|
|||
#else
|
||||
#define pci_msi_quirk 0
|
||||
#endif
|
||||
|
||||
extern unsigned int pci_pm_d3_delay;
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
void disable_msi_mode(struct pci_dev *dev, int pos, int type);
|
||||
void pci_no_msi(void);
|
||||
|
@ -66,7 +66,15 @@ static inline int pci_save_msix_state(struct pci_dev *dev) { return 0; }
|
|||
static inline void pci_restore_msi_state(struct pci_dev *dev) {}
|
||||
static inline void pci_restore_msix_state(struct pci_dev *dev) {}
|
||||
#endif
|
||||
static inline int pci_no_d1d2(struct pci_dev *dev)
|
||||
{
|
||||
unsigned int parent_dstates = 0;
|
||||
|
||||
if (dev->bus->self)
|
||||
parent_dstates = dev->bus->self->no_d1d2;
|
||||
return (dev->no_d1d2 || parent_dstates);
|
||||
|
||||
}
|
||||
extern int pcie_mch_quirk;
|
||||
extern struct device_attribute pci_dev_attrs[];
|
||||
extern struct class_device_attribute class_device_attr_cpuaffinity;
|
||||
|
|
|
@ -1418,6 +1418,37 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_pc
|
|||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_pcie_pxh);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_pcie_pxh);
|
||||
|
||||
/*
|
||||
* Some Intel PCI Express chipsets have trouble with downstream
|
||||
* device power management.
|
||||
*/
|
||||
static void quirk_intel_pcie_pm(struct pci_dev * dev)
|
||||
{
|
||||
pci_pm_d3_delay = 120;
|
||||
dev->no_d1d2 = 1;
|
||||
}
|
||||
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25e2, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25e3, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25e4, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25e5, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25e6, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25e7, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25f7, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25f8, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25f9, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x25fa, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2601, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2602, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2603, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2604, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2605, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2606, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2607, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2608, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm);
|
||||
|
||||
/*
|
||||
* Fixup the cardbus bridges on the IBM Dock II docking station
|
||||
|
|
|
@ -161,6 +161,7 @@ struct pci_dev {
|
|||
unsigned int is_enabled:1; /* pci_enable_device has been called */
|
||||
unsigned int is_busmaster:1; /* device is busmaster */
|
||||
unsigned int no_msi:1; /* device may not use msi */
|
||||
unsigned int no_d1d2:1; /* only allow d0 or d3 */
|
||||
unsigned int block_ucfg_access:1; /* userspace config space access is blocked */
|
||||
unsigned int broken_parity_status:1; /* Device generates false positive parity */
|
||||
unsigned int msi_enabled:1;
|
||||
|
|
Loading…
Reference in a new issue