From 709cf5ea7a8bea1b956d361ee7cef1945423200c Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 30 Jun 2006 02:31:25 -0700 Subject: [PATCH] PCI: Clear abnormal poweroff flag on VIA southbridges, fix resume Some VIA southbridges contain a flag in the ACPI register space that indicates whether an abnormal poweroff has occured, presumably with the intention that it can be cleared on clean shutdown. Some BIOSes check this flag at resume time, and will re-POST the system rather than jump back to the OS if it's set. Clearing it at boot time appears to be sufficient. I'm not sure if drivers/pci/quirks.c is the right place to do it, but I'm not sure where would be cleaner. [akpm@osdl.org: cleanups, build fix] Signed-off-by: Matthew Garrett Cc: Greg KH Cc: "Brown, Len" Cc: "Yu, Luming" Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index de3bbc88fb26..53a887e1014c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -683,6 +683,33 @@ static void __devinit quirk_vt82c598_id(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt82c598_id ); +#ifdef CONFIG_ACPI_SLEEP + +/* + * Some VIA systems boot with the abnormal status flag set. This can cause + * the BIOS to re-POST the system on resume rather than passing control + * back to the OS. Clear the flag on boot + */ +static void __devinit quirk_via_abnormal_poweroff(struct pci_dev *dev) +{ + u32 reg; + + acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_STATUS, + ®); + + if (reg & 0x800) { + printk("Clearing abnormal poweroff flag\n"); + acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1_STATUS, + (u16)0x800); + } +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_via_abnormal_poweroff); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_abnormal_poweroff); + +#endif + /* * CardBus controllers have a legacy base address that enables them * to respond as i82365 pcmcia controllers. We don't want them to -- cgit v1.2.3 From ffadcc2ff42ecedf71ea67d9051ff028927aed08 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Wed, 12 Jul 2006 08:59:00 -0700 Subject: [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 Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 11 ++++++++++- drivers/pci/pci.h | 10 +++++++++- drivers/pci/quirks.c | 31 +++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 4 files changed, 51 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cf57d7de3765..9f79dd6d51ab 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -19,6 +19,7 @@ #include /* 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); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9cc842b666eb..08d58fc78ee1 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -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; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 53a887e1014c..e3c78c39b7e4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -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 diff --git a/include/linux/pci.h b/include/linux/pci.h index 983fca251b25..8565b81d7fbc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -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; -- cgit v1.2.3