Skip to content

Commit

Permalink
USB: add NO_D3_DURING_SLEEP flag and revert 151b612
Browse files Browse the repository at this point in the history
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
151b612 (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 <[email protected]>
Tested-by: Dâniel Fraga <[email protected]>
Tested-by: Andrey Rahmatullin <[email protected]>
Tested-by: Steven Rostedt <[email protected]>
Cc: stable <[email protected]>
Reviewed-by: Rafael J. Wysocki <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
AlanStern authored and gregkh committed Jun 13, 2012
1 parent 0ef0be1 commit c2fb8a3
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 19 deletions.
5 changes: 5 additions & 0 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,11 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
if (target_state == PCI_POWER_ERROR)
return -EIO;

/* Some devices mustn't be in D3 during system sleep */
if (target_state == PCI_D3hot &&
(dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP))
return 0;

pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));

error = pci_set_power_state(dev, target_state);
Expand Down
26 changes: 26 additions & 0 deletions drivers/pci/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -2929,6 +2929,32 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);

/*
* The Intel 6 Series/C200 Series chipset's EHCI controllers on many
* ASUS motherboards will cause memory corruption or a system crash
* if they are in D3 while the system is put into S3 sleep.
*/
static void __devinit asus_ehci_no_d3(struct pci_dev *dev)
{
const char *sys_info;
static const char good_Asus_board[] = "P8Z68-V";

if (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP)
return;
if (dev->subsystem_vendor != PCI_VENDOR_ID_ASUSTEK)
return;
sys_info = dmi_get_system_info(DMI_BOARD_NAME);
if (sys_info && memcmp(sys_info, good_Asus_board,
sizeof(good_Asus_board) - 1) == 0)
return;

dev_info(&dev->dev, "broken D3 during system sleep on ASUS\n");
dev->dev_flags |= PCI_DEV_FLAGS_NO_D3_DURING_SLEEP;
device_set_wakeup_capable(&dev->dev, false);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c26, asus_ehci_no_d3);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c2d, asus_ehci_no_d3);

static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
struct pci_fixup *end)
{
Expand Down
9 changes: 0 additions & 9 deletions drivers/usb/core/hcd-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,15 +493,6 @@ static int hcd_pci_suspend_noirq(struct device *dev)

pci_save_state(pci_dev);

/*
* Some systems crash if an EHCI controller is in D3 during
* a sleep transition. We have to leave such controllers in D0.
*/
if (hcd->broken_pci_sleep) {
dev_dbg(dev, "Staying in PCI D0\n");
return retval;
}

/* If the root hub is dead rather than suspended, disallow remote
* wakeup. usb_hc_died() should ensure that both hosts are marked as
* dying, so we only need to check the primary roothub.
Expand Down
8 changes: 0 additions & 8 deletions drivers/usb/host/ehci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
hcd->has_tt = 1;
tdi_reset(ehci);
}
if (pdev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK) {
/* EHCI #1 or #2 on 6 Series/C200 Series chipset */
if (pdev->device == 0x1c26 || pdev->device == 0x1c2d) {
ehci_info(ehci, "broken D3 during system sleep on ASUS\n");
hcd->broken_pci_sleep = 1;
device_set_wakeup_capable(&pdev->dev, false);
}
}
break;
case PCI_VENDOR_ID_TDI:
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
Expand Down
2 changes: 2 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
/* Provide indication device is assigned by a Virtual Machine Manager */
PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4,
/* Device causes system crash if in D3 during S3 sleep */
PCI_DEV_FLAGS_NO_D3_DURING_SLEEP = (__force pci_dev_flags_t) 8,
};

enum pci_irq_reroute_variant {
Expand Down
2 changes: 0 additions & 2 deletions include/linux/usb/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,6 @@ struct usb_hcd {
unsigned wireless:1; /* Wireless USB HCD */
unsigned authorized_default:1;
unsigned has_tt:1; /* Integrated TT in root hub */
unsigned broken_pci_sleep:1; /* Don't put the
controller in PCI-D3 for system sleep */

unsigned int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
Expand Down

0 comments on commit c2fb8a3

Please sign in to comment.