Commit 836115bf authored by David Brownell's avatar David Brownell Committed by Linus Torvalds

[PATCH] USB: ehci handles pci misbehavior better

Cope better when PCI misbehaves badly and registers misbehave:

    - terminate some loops before they get to infinity
       * capability scan
       * port reset
    - after init failure, memory may already be cleaned up

Some systems have been reporting such problems after ACPI resume.
parent 1246206f
......@@ -330,6 +330,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
unsigned count = 256/4;
spin_lock_init (&ehci->lock);
......@@ -345,16 +346,21 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
else
temp = 0;
while (temp) {
while (temp && count--) {
u32 cap;
pci_read_config_dword (to_pci_dev(ehci->hcd.self.controller), temp, &cap);
pci_read_config_dword (to_pci_dev(ehci->hcd.self.controller),
temp, &cap);
ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
switch (cap & 0xff) {
case 1: /* BIOS/SMM/... handoff */
if (bios_handoff (ehci, temp, cap) != 0)
return -EOPNOTSUPP;
break;
case 0x0a: /* appendix C */
ehci_dbg (ehci, "debug registers, BAR %d offset %d\n",
(cap >> 29) & 0x07, (cap >> 16) & 0x0fff);
break;
case 0: /* illegal reserved capability */
ehci_warn (ehci, "illegal capability!\n");
cap = 0;
......@@ -364,6 +370,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
}
temp = (cap >> 8) & 0xff;
}
if (!count) {
ehci_err (ehci, "bogus capabilities ... PCI problems!\n");
return -EIO;
}
#endif
/* cache this readonly data; minimize PCI reads */
......@@ -577,7 +587,8 @@ static void ehci_stop (struct usb_hcd *hcd)
/* root hub is shut down separately (first, when possible) */
spin_lock_irq (&ehci->lock);
ehci_work (ehci, NULL);
if (ehci->async)
ehci_work (ehci, NULL);
spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci);
......
......@@ -252,14 +252,18 @@ static int ehci_hub_control (
/* force reset to complete */
writel (temp & ~PORT_RESET,
&ehci->regs->port_status [wIndex]);
do {
temp = readl (
&ehci->regs->port_status [wIndex]);
udelay (10);
} while (temp & PORT_RESET);
retval = handshake (
&ehci->regs->port_status [wIndex],
PORT_RESET, 0, 500);
if (retval != 0) {
ehci_err (ehci, "port %d reset error %d\n",
wIndex + 1, retval);
goto error;
}
/* see what we found out */
temp = check_reset_complete (ehci, wIndex, temp);
temp = check_reset_complete (ehci, wIndex,
readl (&ehci->regs->port_status [wIndex]));
}
// don't show wPortStatus if it's owned by a companion hc
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment