Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+10 −1
Original line number Original line Diff line number Diff line
@@ -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)
	} 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)
	/* 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);


+9 −1
Original line number Original line Diff line number Diff line
@@ -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; }
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;
+31 −0
Original line number Original line Diff line number Diff line
@@ -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
+1 −0
Original line number Original line Diff line number Diff line
@@ -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;