Commit ffadcc2f authored by Kristen Carlson Accardi's avatar Kristen Carlson Accardi Committed by Greg Kroah-Hartman

[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: default avatarKristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6f0312fd
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <asm/dma.h> /* isa_dma_bridge_buggy */ #include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h" #include "pci.h"
unsigned int pci_pm_d3_delay = 10;
/** /**
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * 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) ...@@ -313,6 +314,14 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
} else if (dev->current_state == state) } else if (dev->current_state == state)
return 0; /* we're already there */ 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 */ /* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM); 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) ...@@ -363,7 +372,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
/* Mandatory power management transition delays */ /* Mandatory power management transition delays */
/* see PCI PM 1.1 5.6.1 table 18 */ /* see PCI PM 1.1 5.6.1 table 18 */
if (state == PCI_D3hot || dev->current_state == PCI_D3hot) 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) else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200); udelay(200);
......
...@@ -47,7 +47,7 @@ extern int pci_msi_quirk; ...@@ -47,7 +47,7 @@ extern int pci_msi_quirk;
#else #else
#define pci_msi_quirk 0 #define pci_msi_quirk 0
#endif #endif
extern unsigned int pci_pm_d3_delay;
#ifdef CONFIG_PCI_MSI #ifdef CONFIG_PCI_MSI
void disable_msi_mode(struct pci_dev *dev, int pos, int type); void disable_msi_mode(struct pci_dev *dev, int pos, int type);
void pci_no_msi(void); void pci_no_msi(void);
...@@ -66,7 +66,15 @@ static inline int pci_save_msix_state(struct pci_dev *dev) { return 0; } ...@@ -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_msi_state(struct pci_dev *dev) {}
static inline void pci_restore_msix_state(struct pci_dev *dev) {} static inline void pci_restore_msix_state(struct pci_dev *dev) {}
#endif #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 int pcie_mch_quirk;
extern struct device_attribute pci_dev_attrs[]; extern struct device_attribute pci_dev_attrs[];
extern struct class_device_attribute class_device_attr_cpuaffinity; 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 ...@@ -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_PXH_1, quirk_pcie_pxh);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, 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 * Fixup the cardbus bridges on the IBM Dock II docking station
......
...@@ -161,6 +161,7 @@ struct pci_dev { ...@@ -161,6 +161,7 @@ struct pci_dev {
unsigned int is_enabled:1; /* pci_enable_device has been called */ unsigned int is_enabled:1; /* pci_enable_device has been called */
unsigned int is_busmaster:1; /* device is busmaster */ unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */ 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 block_ucfg_access:1; /* userspace config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */ unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int msi_enabled:1; unsigned int msi_enabled:1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment