[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>
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index cf57d7d..9f79dd6 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -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 @@
 	} 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 @@
 	/* 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);