changeset 10039:f8ab5da25490

6836590 ehci driver fails to poll for reset completion, causing failures on high speed devices
author pengcheng chen - Sun Microsystems - Beijing China <Pengcheng.Chen@Sun.COM>
date Tue, 07 Jul 2009 03:02:56 +0800
parents aeed618cdc99
children 38b25aeeaf7a
files usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c usr/src/uts/common/sys/usb/hcd/ehci/ehci_hub.h
diffstat 2 files changed, 40 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c	Mon Jul 06 10:15:58 2009 -0700
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c	Tue Jul 07 03:02:56 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -905,6 +905,7 @@
 	uint16_t		port)
 {
 	uint_t			port_status;
+	int			i;
 
 	mutex_enter(&ehcip->ehci_int_mutex);
 
@@ -941,8 +942,22 @@
 
 	mutex_exit(&ehcip->ehci_int_mutex);
 
-	/* Wait 2ms for port to return to high speed mode */
-	delay(drv_usectohz(EHCI_PORT_RESUME_COMP_TIMEWAIT));
+	/*
+	 * Wait for port to return to high speed mode. It's necessary to poll
+	 * for resume completion for some high-speed devices to work correctly.
+	 */
+	for (i = 0; i < EHCI_PORT_RESUME_RETRY_MAX; i++) {
+		delay(drv_usectohz(EHCI_PORT_RESUME_COMP_TIMEWAIT));
+
+		mutex_enter(&ehcip->ehci_int_mutex);
+		port_status = Get_OpReg(ehci_rh_port_status[port]) &
+		    ~EHCI_RH_PORT_CLEAR_MASK;
+		mutex_exit(&ehcip->ehci_int_mutex);
+
+		if (!(port_status & EHCI_RH_PORT_RESUME)) {
+			break;
+		}
+	}
 }
 
 
@@ -958,6 +973,7 @@
 {
 	ehci_root_hub_t		*rh;
 	uint_t			port_status;
+	int			i;
 
 	mutex_enter(&ehcip->ehci_int_mutex);
 
@@ -999,7 +1015,7 @@
 
 		mutex_exit(&ehcip->ehci_int_mutex);
 
-		/* Wait 20ms for reset to complete */
+		/* Wait 50ms for reset to complete */
 		delay(drv_usectohz(EHCI_PORT_RESET_TIMEWAIT));
 
 		mutex_enter(&ehcip->ehci_int_mutex);
@@ -1013,10 +1029,23 @@
 		mutex_exit(&ehcip->ehci_int_mutex);
 
 		/*
-		 * Wait 2ms for hardware to enable this port
-		 * if connected usb device is high speed.
+		 * Wait for hardware to enable this port, if the connected
+		 * usb device is high speed. It's necessary to poll for reset
+		 * completion for some high-speed devices to recognized
+		 * correctly.
 		 */
-		delay(drv_usectohz(EHCI_PORT_RESET_COMP_TIMEWAIT));
+		for (i = 0; i < EHCI_PORT_RESET_RETRY_MAX; i++) {
+			delay(drv_usectohz(EHCI_PORT_RESET_COMP_TIMEWAIT));
+
+			mutex_enter(&ehcip->ehci_int_mutex);
+			port_status = Get_OpReg(ehci_rh_port_status[port]) &
+			    ~EHCI_RH_PORT_CLEAR_MASK;
+			mutex_exit(&ehcip->ehci_int_mutex);
+
+			if (!(port_status & EHCI_RH_PORT_RESET)) {
+				break;
+			}
+		}
 
 		mutex_enter(&ehcip->ehci_int_mutex);
 
--- a/usr/src/uts/common/sys/usb/hcd/ehci/ehci_hub.h	Mon Jul 06 10:15:58 2009 -0700
+++ b/usr/src/uts/common/sys/usb/hcd/ehci/ehci_hub.h	Tue Jul 07 03:02:56 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -100,11 +100,13 @@
  * These timeout values are specified in terms of microseconds.
  */
 #define	EHCI_RH_POLL_TIME		256000	/* RH polling interval */
-#define	EHCI_PORT_RESET_TIMEWAIT	20000	/* RH port reset time */
+#define	EHCI_PORT_RESET_TIMEWAIT	50000	/* RH port reset time */
 #define	EHCI_PORT_RESET_COMP_TIMEWAIT	2000	/* RH port reset complete */
 #define	EHCI_PORT_SUSPEND_TIMEWAIT	10000	/* RH port suspend time */
 #define	EHCI_PORT_RESUME_TIMEWAIT	20000	/* RH port resume time */
 #define	EHCI_PORT_RESUME_COMP_TIMEWAIT	2000	/* RH port resume complete */
+#define	EHCI_PORT_RESET_RETRY_MAX	10	/* RH port reset retry max */
+#define	EHCI_PORT_RESUME_RETRY_MAX	10	/* RH port resume retry max */
 
 #ifdef __cplusplus
 }