aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-06-13 11:20:19 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-22 14:34:15 -0400
commit2c1a56c8c620ff93050ee4ef545cc585d2f93034 (patch)
treebabaa3851adf3a3a6778b3723b192944ade929ff
parent20fc17886631cece5e5a517eca833fdf9d843fb0 (diff)
USB: add NO_D3_DURING_SLEEP flag and revert 151b61284776be2
commit c2fb8a3fa25513de8fedb38509b1f15a5bbee47b upstream. This patch (as1558) fixes a problem affecting several ASUS computers: The machine crashes or corrupts memory when going into suspend if the ehci-hcd driver is bound to any controllers. Users have been forced to unbind or unload ehci-hcd before putting their systems to sleep. After extensive testing, it was determined that the machines don't like going into suspend when any EHCI controllers are in the PCI D3 power state. Presumably this is a firmware bug, but there's nothing we can do about it except to avoid putting the controllers in D3 during system sleep. The patch adds a new flag to indicate whether the problem is present, and avoids changing the controller's power state if the flag is set. Runtime suspend is unaffected; this matters only for system suspend. However as a side effect, the controller will not respond to remote wakeup requests while the system is asleep. Hence USB wakeup is not functional -- but of course, this is already true in the current state of affairs. A similar patch has already been applied as commit 151b61284776be2d6f02d48c23c3625678960b97 (USB: EHCI: fix crash during suspend on ASUS computers). The patch supersedes that one and reverts it. There are two differences: The old patch added the flag at the USB level; this patch adds it at the PCI level. The old patch applied to all chipsets with the same vendor, subsystem vendor, and product IDs; this patch makes an exception for a known-good system (based on DMI information). Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Tested-by: Dâniel Fraga <fragabr@gmail.com> Tested-by: Andrey Rahmatullin <wrar@wrar.name> Tested-by: Steven Rostedt <rostedt@goodmis.org> Reviewed-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/pci/pci.c5
-rw-r--r--drivers/pci/quirks.c26
-rw-r--r--drivers/usb/core/hcd-pci.c9
-rw-r--r--drivers/usb/host/ehci-pci.c8
-rw-r--r--include/linux/pci.h2
-rw-r--r--include/linux/usb/hcd.h2
6 files changed, 33 insertions, 19 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d549bbc93cd..bf401aead87 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1682,6 +1682,11 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
1682 if (target_state == PCI_POWER_ERROR) 1682 if (target_state == PCI_POWER_ERROR)
1683 return -EIO; 1683 return -EIO;
1684 1684
1685 /* Some devices mustn't be in D3 during system sleep */
1686 if (target_state == PCI_D3hot &&
1687 (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP))
1688 return 0;
1689
1685 pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); 1690 pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
1686 1691
1687 error = pci_set_power_state(dev, target_state); 1692 error = pci_set_power_state(dev, target_state);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index a6b07ddad71..975af4353e7 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2856,6 +2856,32 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev)
2856DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); 2856DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq);
2857DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); 2857DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
2858 2858
2859/*
2860 * The Intel 6 Series/C200 Series chipset's EHCI controllers on many
2861 * ASUS motherboards will cause memory corruption or a system crash
2862 * if they are in D3 while the system is put into S3 sleep.
2863 */
2864static void __devinit asus_ehci_no_d3(struct pci_dev *dev)
2865{
2866 const char *sys_info;
2867 static const char good_Asus_board[] = "P8Z68-V";
2868
2869 if (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP)
2870 return;
2871 if (dev->subsystem_vendor != PCI_VENDOR_ID_ASUSTEK)
2872 return;
2873 sys_info = dmi_get_system_info(DMI_BOARD_NAME);
2874 if (sys_info && memcmp(sys_info, good_Asus_board,
2875 sizeof(good_Asus_board) - 1) == 0)
2876 return;
2877
2878 dev_info(&dev->dev, "broken D3 during system sleep on ASUS\n");
2879 dev->dev_flags |= PCI_DEV_FLAGS_NO_D3_DURING_SLEEP;
2880 device_set_wakeup_capable(&dev->dev, false);
2881}
2882DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c26, asus_ehci_no_d3);
2883DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c2d, asus_ehci_no_d3);
2884
2859static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, 2885static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
2860 struct pci_fixup *end) 2886 struct pci_fixup *end)
2861{ 2887{
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index aa7bbbcf9d8..6c1642b382f 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -495,15 +495,6 @@ static int hcd_pci_suspend_noirq(struct device *dev)
495 495
496 pci_save_state(pci_dev); 496 pci_save_state(pci_dev);
497 497
498 /*
499 * Some systems crash if an EHCI controller is in D3 during
500 * a sleep transition. We have to leave such controllers in D0.
501 */
502 if (hcd->broken_pci_sleep) {
503 dev_dbg(dev, "Staying in PCI D0\n");
504 return retval;
505 }
506
507 /* If the root hub is dead rather than suspended, disallow remote 498 /* If the root hub is dead rather than suspended, disallow remote
508 * wakeup. usb_hc_died() should ensure that both hosts are marked as 499 * wakeup. usb_hc_died() should ensure that both hosts are marked as
509 * dying, so we only need to check the primary roothub. 500 * dying, so we only need to check the primary roothub.
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index efb9efcffe4..f76831480c6 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -144,14 +144,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
144 hcd->has_tt = 1; 144 hcd->has_tt = 1;
145 tdi_reset(ehci); 145 tdi_reset(ehci);
146 } 146 }
147 if (pdev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK) {
148 /* EHCI #1 or #2 on 6 Series/C200 Series chipset */
149 if (pdev->device == 0x1c26 || pdev->device == 0x1c2d) {
150 ehci_info(ehci, "broken D3 during system sleep on ASUS\n");
151 hcd->broken_pci_sleep = 1;
152 device_set_wakeup_capable(&pdev->dev, false);
153 }
154 }
155 break; 147 break;
156 case PCI_VENDOR_ID_TDI: 148 case PCI_VENDOR_ID_TDI:
157 if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { 149 if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c446b5ca2d3..ff5970b7a17 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -174,6 +174,8 @@ enum pci_dev_flags {
174 PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1, 174 PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
175 /* Device configuration is irrevocably lost if disabled into D3 */ 175 /* Device configuration is irrevocably lost if disabled into D3 */
176 PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, 176 PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
177 /* Device causes system crash if in D3 during S3 sleep */
178 PCI_DEV_FLAGS_NO_D3_DURING_SLEEP = (__force pci_dev_flags_t) 8,
177}; 179};
178 180
179enum pci_irq_reroute_variant { 181enum pci_irq_reroute_variant {
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 32ba8c55b3a..c0ecc5a2ef9 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -128,8 +128,6 @@ struct usb_hcd {
128 unsigned wireless:1; /* Wireless USB HCD */ 128 unsigned wireless:1; /* Wireless USB HCD */
129 unsigned authorized_default:1; 129 unsigned authorized_default:1;
130 unsigned has_tt:1; /* Integrated TT in root hub */ 130 unsigned has_tt:1; /* Integrated TT in root hub */
131 unsigned broken_pci_sleep:1; /* Don't put the
132 controller in PCI-D3 for system sleep */
133 131
134 int irq; /* irq allocated */ 132 int irq; /* irq allocated */
135 void __iomem *regs; /* device memory/io */ 133 void __iomem *regs; /* device memory/io */