ChangeSet 1.1931, 2004/04/22 13:43:11-07:00, david-b@pacbell.net [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. drivers/usb/host/ehci-hcd.c | 17 ++++++++++++++--- drivers/usb/host/ehci-hub.c | 16 ++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Thu Apr 22 14:41:25 2004 +++ b/drivers/usb/host/ehci-hcd.c Thu Apr 22 14:41:25 2004 @@ -330,6 +330,7 @@ { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; + unsigned count = 256/4; spin_lock_init (&ehci->lock); @@ -345,16 +346,21 @@ 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 @@ } 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 @@ /* 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); diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c --- a/drivers/usb/host/ehci-hub.c Thu Apr 22 14:41:25 2004 +++ b/drivers/usb/host/ehci-hub.c Thu Apr 22 14:41:25 2004 @@ -252,14 +252,18 @@ /* 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