changeset 12898:2e278bf762e0

6968282 panic with assertion failed: status & EHCI_QH_STS_HALTED
author Raymond Chen <Raymond.Chen@Sun.COM>
date Thu, 22 Jul 2010 10:15:06 +0800
parents dc96b005136b
children 5abbd90da85a
files usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c usr/src/uts/common/io/usb/hcd/ehci/ehci_xfer.c usr/src/uts/common/sys/usb/hcd/ehci/ehci_util.h
diffstat 3 files changed, 104 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c	Wed Jul 21 16:49:10 2010 -0700
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c	Thu Jul 22 10:15:06 2010 +0800
@@ -4001,6 +4001,104 @@
 	return (USB_SUCCESS);
 }
 
+/*
+ * Toggle the async/periodic schedule based on opened pipe count.
+ * During pipe cleanup(in pipe reset case), the pipe's QH is temporarily
+ * disabled. But the TW on the pipe is not freed. In this case, we need
+ * to disable async/periodic schedule for some non-compatible hardware.
+ * Otherwise, the hardware will overwrite software's configuration of
+ * the QH.
+ */
+void
+ehci_toggle_scheduler_on_pipe(ehci_state_t *ehcip)
+{
+	uint_t  temp_reg, cmd_reg;
+
+	cmd_reg = Get_OpReg(ehci_command);
+	temp_reg = cmd_reg;
+
+	/*
+	 * Enable/Disable asynchronous scheduler, and
+	 * turn on/off async list door bell
+	 */
+	if (ehcip->ehci_open_async_count) {
+		if ((ehcip->ehci_async_req_count > 0) &&
+		    ((cmd_reg & EHCI_CMD_ASYNC_SCHED_ENABLE) == 0)) {
+			/*
+			 * For some reason this address might get nulled out by
+			 * the ehci chip. Set it here just in case it is null.
+			 */
+			Set_OpReg(ehci_async_list_addr,
+			    ehci_qh_cpu_to_iommu(ehcip,
+			    ehcip->ehci_head_of_async_sched_list));
+
+			/*
+			 * For some reason this register might get nulled out by
+			 * the Uli M1575 Southbridge. To workaround the HW
+			 * problem, check the value after write and retry if the
+			 * last write fails.
+			 *
+			 * If the ASYNCLISTADDR remains "stuck" after
+			 * EHCI_MAX_RETRY retries, then the M1575 is broken
+			 * and is stuck in an inconsistent state and is about
+			 * to crash the machine with a trn_oor panic when it
+			 * does a DMA read from 0x0.  It is better to panic
+			 * now rather than wait for the trn_oor crash; this
+			 * way Customer Service will have a clean signature
+			 * that indicts the M1575 chip rather than a
+			 * mysterious and hard-to-diagnose trn_oor panic.
+			 */
+			if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
+			    (ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
+			    (ehci_qh_cpu_to_iommu(ehcip,
+			    ehcip->ehci_head_of_async_sched_list) !=
+			    Get_OpReg(ehci_async_list_addr))) {
+				int retry = 0;
+
+				Set_OpRegRetry(ehci_async_list_addr,
+				    ehci_qh_cpu_to_iommu(ehcip,
+				    ehcip->ehci_head_of_async_sched_list),
+				    retry);
+				if (retry >= EHCI_MAX_RETRY)
+					cmn_err(CE_PANIC,
+					    "ehci_toggle_scheduler_on_pipe: "
+					    "ASYNCLISTADDR write failed.");
+
+				USB_DPRINTF_L2(PRINT_MASK_ATTA,
+				    ehcip->ehci_log_hdl,
+				    "ehci_toggle_scheduler_on_pipe:"
+				    " ASYNCLISTADDR write failed, retry=%d",
+				    retry);
+			}
+
+			cmd_reg |= EHCI_CMD_ASYNC_SCHED_ENABLE;
+		}
+	} else {
+		cmd_reg &= ~EHCI_CMD_ASYNC_SCHED_ENABLE;
+	}
+
+	if (ehcip->ehci_open_periodic_count) {
+		if ((ehcip->ehci_periodic_req_count > 0) &&
+		    ((cmd_reg & EHCI_CMD_PERIODIC_SCHED_ENABLE) == 0)) {
+			/*
+			 * For some reason this address get's nulled out by
+			 * the ehci chip. Set it here just in case it is null.
+			 */
+			Set_OpReg(ehci_periodic_list_base,
+			    (uint32_t)(ehcip->ehci_pflt_cookie.dmac_address &
+			    0xFFFFF000));
+			cmd_reg |= EHCI_CMD_PERIODIC_SCHED_ENABLE;
+		}
+	} else {
+		cmd_reg &= ~EHCI_CMD_PERIODIC_SCHED_ENABLE;
+	}
+
+	/* Just an optimization */
+	if (temp_reg != cmd_reg) {
+		Set_OpReg(ehci_command, cmd_reg);
+	}
+}
+
 
 /*
  * ehci_toggle_scheduler:
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_xfer.c	Wed Jul 21 16:49:10 2010 -0700
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_xfer.c	Thu Jul 22 10:15:06 2010 +0800
@@ -854,6 +854,7 @@
 
 	/* Remove this qh from the HCD's view, but do not reclaim it */
 	ehci_remove_qh(ehcip, pp, B_FALSE);
+	ehci_toggle_scheduler_on_pipe(ehcip);
 
 	/*
 	 * Wait for atleast one SOF, just in case the HCD is in the
@@ -871,6 +872,7 @@
 
 	/* Insert this QH back into the HCD's view */
 	ehci_insert_qh(ehcip, ph);
+	ehci_toggle_scheduler_on_pipe(ehcip);
 }
 
 
@@ -894,6 +896,7 @@
 
 	/* Remove this qh from the HCD's view, but do not reclaim it */
 	ehci_remove_qh(ehcip, pp, B_FALSE);
+	ehci_toggle_scheduler_on_pipe(ehcip);
 
 	/*
 	 * Wait for atleast one SOF, just in case the HCD is in the
@@ -921,6 +924,7 @@
 
 	/* Insert this QH back into the HCD's view */
 	ehci_insert_qh(ehcip, ph);
+	ehci_toggle_scheduler_on_pipe(ehcip);
 }
 
 
--- a/usr/src/uts/common/sys/usb/hcd/ehci/ehci_util.h	Wed Jul 21 16:49:10 2010 -0700
+++ b/usr/src/uts/common/sys/usb/hcd/ehci/ehci_util.h	Thu Jul 22 10:15:06 2010 +0800
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _SYS_USB_EHCI_UTIL_H
@@ -102,6 +101,7 @@
 				ehci_state_t		*ehcip);
 extern void	ehci_toggle_scheduler(
 				ehci_state_t		*ehcip);
+extern void	ehci_toggle_scheduler_on_pipe(ehci_state_t *ehcip);
 
 extern void	ehci_print_caps(ehci_state_t 		*ehcip);
 extern void	ehci_print_regs(ehci_state_t 		*ehcip);