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) ...@@ -330,6 +330,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp; u32 temp;
unsigned count = 256/4;
spin_lock_init (&ehci->lock); spin_lock_init (&ehci->lock);
...@@ -345,16 +346,21 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -345,16 +346,21 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
else else
temp = 0; temp = 0;
while (temp) { while (temp && count--) {
u32 cap; 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); ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
switch (cap & 0xff) { switch (cap & 0xff) {
case 1: /* BIOS/SMM/... handoff */ case 1: /* BIOS/SMM/... handoff */
if (bios_handoff (ehci, temp, cap) != 0) if (bios_handoff (ehci, temp, cap) != 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
break; 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 */ case 0: /* illegal reserved capability */
ehci_warn (ehci, "illegal capability!\n"); ehci_warn (ehci, "illegal capability!\n");
cap = 0; cap = 0;
...@@ -364,6 +370,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -364,6 +370,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
} }
temp = (cap >> 8) & 0xff; temp = (cap >> 8) & 0xff;
} }
if (!count) {
ehci_err (ehci, "bogus capabilities ... PCI problems!\n");
return -EIO;
}
#endif #endif
/* cache this readonly data; minimize PCI reads */ /* cache this readonly data; minimize PCI reads */
...@@ -577,7 +587,8 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -577,7 +587,8 @@ static void ehci_stop (struct usb_hcd *hcd)
/* root hub is shut down separately (first, when possible) */ /* root hub is shut down separately (first, when possible) */
spin_lock_irq (&ehci->lock); spin_lock_irq (&ehci->lock);
ehci_work (ehci, NULL); if (ehci->async)
ehci_work (ehci, NULL);
spin_unlock_irq (&ehci->lock); spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
......
...@@ -252,14 +252,18 @@ static int ehci_hub_control ( ...@@ -252,14 +252,18 @@ static int ehci_hub_control (
/* force reset to complete */ /* force reset to complete */
writel (temp & ~PORT_RESET, writel (temp & ~PORT_RESET,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
do { retval = handshake (
temp = readl ( &ehci->regs->port_status [wIndex],
&ehci->regs->port_status [wIndex]); PORT_RESET, 0, 500);
udelay (10); if (retval != 0) {
} while (temp & PORT_RESET); ehci_err (ehci, "port %d reset error %d\n",
wIndex + 1, retval);
goto error;
}
/* see what we found out */ /* 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 // 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