]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
EHCI: workaround for MosChip controller bug
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 12 Oct 2011 14:39:14 +0000 (10:39 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Nov 2011 17:35:17 +0000 (09:35 -0800)
commit 68aa95d5d4de31c9348c1628ffa85c805305ebc5 upstream.

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@rowland.harvard.edu>
Tested-by: Jason N Pitt <jpitt@fhcrc.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci.h

index 40a844c1dbb4945044a9afe2d94d2097bad29af3..3e2ccb0dd255f8d2608accda5263f5dbaa0e1b11 100644 (file)
@@ -808,7 +808,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
        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 c45a116f50fe82532d9b075080e4779ec2f316bc..b27ceab177438b126cd74e6b41660e59a5823d08 100644 (file)
@@ -1188,8 +1188,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 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 1102ce65a3a9eff69fee9c612f65f4a6ead9c3a9..1d1caa6a33fc989c89d42196ca408dd69733192a 100644 (file)
@@ -224,6 +224,11 @@ static int ehci_pci_setup(struct usb_hcd *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 6c9fbe352f7359e7ff6c6c0d8aab1d0bf3c35ccb..063c630d246731383e2c097cb63f2a107f36bd76 100644 (file)
 
 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
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -482,7 +503,7 @@ static int enable_periodic (struct ehci_hcd *ehci)
        ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
 
        /* 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();
@@ -1412,7 +1433,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
@@ -2279,7 +2300,7 @@ scan_periodic (struct ehci_hcd *ehci)
         */
        now_uframe = ehci->next_uframe;
        if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
-               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;
@@ -2458,8 +2479,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;
 
index 989e0a8e01f59cc026af5563d5acc15e144208f4..3ffb27f472c2e49ee619b8e493d0267c8cfb59b5 100644 (file)
@@ -137,6 +137,7 @@ struct ehci_hcd {                   /* one per controller */
        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)
@@ -738,6 +739,22 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
 
 /*-------------------------------------------------------------------------*/
 
+#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 */