[PATCH] EHCI: workaround for MosChip controller bug

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch (as1489) works around a hardware bug in MosChip EHCI
controllers.  Evidently when one of these controllers increments the
frame-index register, it changes the three low-order bits (the
microframe counter) before changing the higher order bits (the frame
counter).  If the register is read at just the wrong time, the value
obtained is too low by 8.

When the appropriate quirk flag is set, we work around this problem by
reading the frame-index register a second time if the first value's
three low-order bits are all 0.  This gives the hardware a chance to
finish updating the register, yielding the correct value.

Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
Tested-by: Jason N Pitt <jpitt@xxxxxxxxx>
CC: <stable@xxxxxxxxxx>

---

 drivers/usb/host/ehci-dbg.c   |    2 +-
 drivers/usb/host/ehci-hcd.c   |    3 +--
 drivers/usb/host/ehci-pci.c   |    5 +++++
 drivers/usb/host/ehci-sched.c |   30 +++++++++++++++++++++++++-----
 drivers/usb/host/ehci.h       |   17 +++++++++++++++++
 5 files changed, 49 insertions(+), 8 deletions(-)

Index: usb-3.1/drivers/usb/host/ehci.h
===================================================================
--- usb-3.1.orig/drivers/usb/host/ehci.h
+++ usb-3.1/drivers/usb/host/ehci.h
@@ -146,6 +146,7 @@ struct ehci_hcd {			/* one per controlle
 	unsigned		fs_i_thresh:1;	/* Intel iso scheduling */
 	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
 	unsigned		has_synopsys_hc_bug:1; /* Synopsys HC */
+	unsigned		frame_index_bug:1; /* MosChip (AKA NetMos) */
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
@@ -747,6 +748,22 @@ static inline u32 hc32_to_cpup (const st
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_PCI
+
+/* For working around the MosChip frame-index-register bug */
+static unsigned ehci_read_frame_index(struct ehci_hcd *ehci);
+
+#else
+
+static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci)
+{
+	return ehci_readl(ehci, &ehci->regs->frame_index);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 #ifndef DEBUG
 #define STUB_DEBUG_FILES
 #endif	/* DEBUG */
Index: usb-3.1/drivers/usb/host/ehci-hcd.c
===================================================================
--- usb-3.1.orig/drivers/usb/host/ehci-hcd.c
+++ usb-3.1/drivers/usb/host/ehci-hcd.c
@@ -1166,8 +1166,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd,
 static int ehci_get_frame (struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	return (ehci_readl(ehci, &ehci->regs->frame_index) >> 3) %
-		ehci->periodic_size;
+	return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size;
 }
 
 /*-------------------------------------------------------------------------*/
Index: usb-3.1/drivers/usb/host/ehci-dbg.c
===================================================================
--- usb-3.1.orig/drivers/usb/host/ehci-dbg.c
+++ usb-3.1/drivers/usb/host/ehci-dbg.c
@@ -821,7 +821,7 @@ static ssize_t fill_registers_buffer(str
 	next += temp;
 
 	temp = scnprintf (next, size, "uframe %04x\n",
-			ehci_readl(ehci, &ehci->regs->frame_index));
+			ehci_read_frame_index(ehci));
 	size -= temp;
 	next += temp;
 
Index: usb-3.1/drivers/usb/host/ehci-pci.c
===================================================================
--- usb-3.1.orig/drivers/usb/host/ehci-pci.c
+++ usb-3.1/drivers/usb/host/ehci-pci.c
@@ -224,6 +224,11 @@ static int ehci_pci_setup(struct usb_hcd
 			pci_dev_put(p_smbus);
 		}
 		break;
+	case PCI_VENDOR_ID_NETMOS:
+		/* MosChip frame-index-register bug */
+		ehci_info(ehci, "applying MosChip frame-index workaround\n");
+		ehci->frame_index_bug = 1;
+		break;
 	}
 
 	/* optional debug port, normally in the first BAR */
Index: usb-3.1/drivers/usb/host/ehci-sched.c
===================================================================
--- usb-3.1.orig/drivers/usb/host/ehci-sched.c
+++ usb-3.1/drivers/usb/host/ehci-sched.c
@@ -36,6 +36,27 @@
 
 static int ehci_get_frame (struct usb_hcd *hcd);
 
+#ifdef CONFIG_PCI
+
+static unsigned ehci_read_frame_index(struct ehci_hcd *ehci)
+{
+	unsigned uf;
+
+	/*
+	 * The MosChip MCS9990 controller updates its microframe counter
+	 * a little before the frame counter, and occasionally we will read
+	 * the invalid intermediate value.  Avoid problems by checking the
+	 * microframe number (the low-order 3 bits); if they are 0 then
+	 * re-read the register to get the correct value.
+	 */
+	uf = ehci_readl(ehci, &ehci->regs->frame_index);
+	if (unlikely(ehci->frame_index_bug && ((uf & 7) == 0)))
+		uf = ehci_readl(ehci, &ehci->regs->frame_index);
+	return uf;
+}
+
+#endif
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -481,7 +502,7 @@ static int enable_periodic (struct ehci_
 	/* posted write ... PSS happens later */
 
 	/* make sure ehci_work scans these */
-	ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index)
+	ehci->next_uframe = ehci_read_frame_index(ehci)
 		% (ehci->periodic_size << 3);
 	if (unlikely(ehci->broken_periodic))
 		ehci->last_periodic_enable = ktime_get_real();
@@ -1408,7 +1429,7 @@ iso_stream_schedule (
 		goto fail;
 	}
 
-	now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
+	now = ehci_read_frame_index(ehci) & (mod - 1);
 
 	/* Typical case: reuse current schedule, stream is still active.
 	 * Hopefully there are no gaps from the host falling behind
@@ -2275,7 +2296,7 @@ scan_periodic (struct ehci_hcd *ehci)
 	 */
 	now_uframe = ehci->next_uframe;
 	if (ehci->rh_state == EHCI_RH_RUNNING) {
-		clock = ehci_readl(ehci, &ehci->regs->frame_index);
+		clock = ehci_read_frame_index(ehci);
 		clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
 	} else  {
 		clock = now_uframe + mod - 1;
@@ -2454,8 +2475,7 @@ restart:
 					|| ehci->periodic_sched == 0)
 				break;
 			ehci->next_uframe = now_uframe;
-			now = ehci_readl(ehci, &ehci->regs->frame_index) &
-					(mod - 1);
+			now = ehci_read_frame_index(ehci) & (mod - 1);
 			if (now_uframe == now)
 				break;
 

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux