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
