diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-11-24 17:59:46 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-11-30 00:39:23 -0500 |
commit | 8de98402652c01839ae321be6cb3054cf5735d83 (patch) | |
tree | 6d4856ddb60be0dcd361441f0794596709553dd1 /drivers/usb/host/ehci-pci.c | |
parent | d3420ba4930d61f4ec4abc046765de274182b4ed (diff) |
[PATCH] USB: Fix USB suspend/resume crasher (#2)
This patch closes the IRQ race and makes various other OHCI & EHCI code
path safer vs. suspend/resume.
I've been able to (finally !) successfully suspend and resume various
Mac models, with or without USB mouse plugged, or plugging while asleep,
or unplugging while asleep etc... all without a crash.
Alan, please verify the UHCI bit I did, I only verified that it builds.
It's very simple so I wouldn't expect any issue there. If you aren't
confident, then just drop the hunks that change uhci-hcd.c
I also made the patch a little bit more "safer" by making sure the store
to the interrupt register that disables interrupts is not posted before
I set the flag and drop the spinlock.
Without this patch, you cannot reliably sleep/wakeup any recent Mac, and
I suspect PCs have some more sneaky issues too (they don't frankly crash
with machine checks because x86 tend to silently swallow PCI errors but
that won't last afaik, at least PCI Express will blow up in those
situations, but the USB code may still misbehave).
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-pci.c')
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 441c26064b44..14fff5714c1e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -228,14 +228,36 @@ static int ehci_pci_reset(struct usb_hcd *hcd) | |||
228 | static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message) | 228 | static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message) |
229 | { | 229 | { |
230 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 230 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
231 | unsigned long flags; | ||
232 | int rc = 0; | ||
231 | 233 | ||
232 | if (time_before(jiffies, ehci->next_statechange)) | 234 | if (time_before(jiffies, ehci->next_statechange)) |
233 | msleep(10); | 235 | msleep(10); |
234 | 236 | ||
237 | /* Root hub was already suspended. Disable irq emission and | ||
238 | * mark HW unaccessible, bail out if RH has been resumed. Use | ||
239 | * the spinlock to properly synchronize with possible pending | ||
240 | * RH suspend or resume activity. | ||
241 | * | ||
242 | * This is still racy as hcd->state is manipulated outside of | ||
243 | * any locks =P But that will be a different fix. | ||
244 | */ | ||
245 | spin_lock_irqsave (&ehci->lock, flags); | ||
246 | if (hcd->state != HC_STATE_SUSPENDED) { | ||
247 | rc = -EINVAL; | ||
248 | goto bail; | ||
249 | } | ||
250 | writel (0, &ehci->regs->intr_enable); | ||
251 | (void)readl(&ehci->regs->intr_enable); | ||
252 | |||
253 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
254 | bail: | ||
255 | spin_unlock_irqrestore (&ehci->lock, flags); | ||
256 | |||
235 | // could save FLADJ in case of Vaux power loss | 257 | // could save FLADJ in case of Vaux power loss |
236 | // ... we'd only use it to handle clock skew | 258 | // ... we'd only use it to handle clock skew |
237 | 259 | ||
238 | return 0; | 260 | return rc; |
239 | } | 261 | } |
240 | 262 | ||
241 | static int ehci_pci_resume(struct usb_hcd *hcd) | 263 | static int ehci_pci_resume(struct usb_hcd *hcd) |
@@ -251,6 +273,9 @@ static int ehci_pci_resume(struct usb_hcd *hcd) | |||
251 | if (time_before(jiffies, ehci->next_statechange)) | 273 | if (time_before(jiffies, ehci->next_statechange)) |
252 | msleep(100); | 274 | msleep(100); |
253 | 275 | ||
276 | /* Mark hardware accessible again as we are out of D3 state by now */ | ||
277 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
278 | |||
254 | /* If CF is clear, we lost PCI Vaux power and need to restart. */ | 279 | /* If CF is clear, we lost PCI Vaux power and need to restart. */ |
255 | if (readl(&ehci->regs->configured_flag) != FLAG_CF) | 280 | if (readl(&ehci->regs->configured_flag) != FLAG_CF) |
256 | goto restart; | 281 | goto restart; |