USB: EHCI: use the new clear_tt_buffer interface
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 29 Jun 2009 14:47:30 +0000 (10:47 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Sun, 12 Jul 2009 22:16:39 +0000 (15:16 -0700)
This patch (as1256) changes ehci-hcd and all the other drivers in the
EHCI family to make use of the new clear_tt_buffer callbacks.  When a
Clear-TT-Buffer request is in progress for a QH, the QH is not allowed
to be linked into the async schedule until the request is finished.
At that time, if there are any URBs queued for the QH, it is linked
into the async schedule.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-ixp4xx.c
drivers/usb/host/ehci-orion.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci.h

index c3a778b..59d208d 100644 (file)
@@ -113,6 +113,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
        .bus_resume             = ehci_bus_resume,
        .relinquish_port        = ehci_relinquish_port,
        .port_handed_over       = ehci_port_handed_over,
+
+       .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 };
 
 static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
index bf86809..9911749 100644 (file)
@@ -325,6 +325,8 @@ static const struct hc_driver ehci_fsl_hc_driver = {
        .bus_resume = ehci_bus_resume,
        .relinquish_port = ehci_relinquish_port,
        .port_handed_over = ehci_port_handed_over,
+
+       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
 };
 
 static int ehci_fsl_drv_probe(struct platform_device *pdev)
index 99c7560..7d03549 100644 (file)
@@ -1003,6 +1003,8 @@ idle_timeout:
                schedule_timeout_uninterruptible(1);
                goto rescan;
        case QH_STATE_IDLE:             /* fully unlinked */
+               if (qh->clearing_tt)
+                       goto idle_timeout;
                if (list_empty (&qh->qtd_list)) {
                        qh_put (qh);
                        break;
index a44bb4a..89b7c70 100644 (file)
@@ -61,6 +61,8 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
 #endif
        .relinquish_port        = ehci_relinquish_port,
        .port_handed_over       = ehci_port_handed_over,
+
+       .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 };
 
 static int ixp4xx_ehci_probe(struct platform_device *pdev)
index 770dd9a..dc2ac61 100644 (file)
@@ -165,6 +165,8 @@ static const struct hc_driver ehci_orion_hc_driver = {
        .bus_resume = ehci_bus_resume,
        .relinquish_port = ehci_relinquish_port,
        .port_handed_over = ehci_port_handed_over,
+
+       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
 };
 
 static void __init
index f3683e1..c2f1b7d 100644 (file)
@@ -404,6 +404,8 @@ static const struct hc_driver ehci_pci_hc_driver = {
        .bus_resume =           ehci_bus_resume,
        .relinquish_port =      ehci_relinquish_port,
        .port_handed_over =     ehci_port_handed_over,
+
+       .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 };
 
 /*-------------------------------------------------------------------------*/
index fbd2722..36f96da 100644 (file)
@@ -79,6 +79,8 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
 #endif
        .relinquish_port        = ehci_relinquish_port,
        .port_handed_over       = ehci_port_handed_over,
+
+       .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 };
 
 
index 93f7035..1dee33b 100644 (file)
@@ -75,6 +75,8 @@ static const struct hc_driver ps3_ehci_hc_driver = {
 #endif
        .relinquish_port        = ehci_relinquish_port,
        .port_handed_over       = ehci_port_handed_over,
+
+       .clear_tt_buffer_complete       = ehci_clear_tt_buffer_complete,
 };
 
 static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev)
index 68bf81e..e3d2b62 100644 (file)
@@ -139,6 +139,55 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 /*-------------------------------------------------------------------------*/
 
+static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd,
+               struct usb_host_endpoint *ep)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+       struct ehci_qh          *qh = ep->hcpriv;
+       unsigned long           flags;
+
+       spin_lock_irqsave(&ehci->lock, flags);
+       qh->clearing_tt = 0;
+       if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
+                       && HC_IS_RUNNING(hcd->state))
+               qh_link_async(ehci, qh);
+       spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh,
+               struct urb *urb, u32 token)
+{
+
+       /* If an async split transaction gets an error or is unlinked,
+        * the TT buffer may be left in an indeterminate state.  We
+        * have to clear the TT buffer.
+        *
+        * Note: this routine is never called for Isochronous transfers.
+        */
+       if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
+#ifdef DEBUG
+               struct usb_device *tt = urb->dev->tt->hub;
+               dev_dbg(&tt->dev,
+                       "clear tt buffer port %d, a%d ep%d t%08x\n",
+                       urb->dev->ttport, urb->dev->devnum,
+                       usb_pipeendpoint(urb->pipe), token);
+#endif /* DEBUG */
+               if (!ehci_is_TDI(ehci)
+                               || urb->dev->tt->hub !=
+                                  ehci_to_hcd(ehci)->self.root_hub) {
+                       if (usb_hub_clear_tt_buffer(urb) == 0)
+                               qh->clearing_tt = 1;
+               } else {
+
+                       /* REVISIT ARC-derived cores don't clear the root
+                        * hub TT buffer in this way...
+                        */
+               }
+       }
+}
+
 static int qtd_copy_status (
        struct ehci_hcd *ehci,
        struct urb *urb,
@@ -195,28 +244,6 @@ static int qtd_copy_status (
                        usb_pipeendpoint (urb->pipe),
                        usb_pipein (urb->pipe) ? "in" : "out",
                        token, status);
-
-               /* if async CSPLIT failed, try cleaning out the TT buffer */
-               if (status != -EPIPE
-                               && urb->dev->tt
-                               && !usb_pipeint(urb->pipe)
-                               && ((token & QTD_STS_MMF) != 0
-                                       || QTD_CERR(token) == 0)
-                               && (!ehci_is_TDI(ehci)
-                                       || urb->dev->tt->hub !=
-                                          ehci_to_hcd(ehci)->self.root_hub)) {
-#ifdef DEBUG
-                       struct usb_device *tt = urb->dev->tt->hub;
-                       dev_dbg (&tt->dev,
-                               "clear tt buffer port %d, a%d ep%d t%08x\n",
-                               urb->dev->ttport, urb->dev->devnum,
-                               usb_pipeendpoint (urb->pipe), token);
-#endif /* DEBUG */
-                       /* REVISIT ARC-derived cores don't clear the root
-                        * hub TT buffer in this way...
-                        */
-                       usb_hub_clear_tt_buffer(urb);
-               }
        }
 
        return status;
@@ -407,9 +434,16 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
                        /* qh unlinked; token in overlay may be most current */
                        if (state == QH_STATE_IDLE
                                        && cpu_to_hc32(ehci, qtd->qtd_dma)
-                                               == qh->hw_current)
+                                               == qh->hw_current) {
                                token = hc32_to_cpu(ehci, qh->hw_token);
 
+                               /* An unlink may leave an incomplete
+                                * async transaction in the TT buffer.
+                                * We have to clear it.
+                                */
+                               ehci_clear_tt_buffer(ehci, qh, urb, token);
+                       }
+
                        /* force halt for unlinked or blocked qh, so we'll
                         * patch the qh later and so that completions can't
                         * activate it while we "know" it's stopped.
@@ -435,6 +469,13 @@ halt:
                                        && (qtd->hw_alt_next
                                                & EHCI_LIST_END(ehci)))
                                last_status = -EINPROGRESS;
+
+                       /* As part of low/full-speed endpoint-halt processing
+                        * we must clear the TT buffer (11.17.5).
+                        */
+                       if (unlikely(last_status != -EINPROGRESS &&
+                                       last_status != -EREMOTEIO))
+                               ehci_clear_tt_buffer(ehci, qh, urb, token);
                }
 
                /* if we're removing something not at the queue head,
@@ -864,6 +905,10 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
        __hc32          dma = QH_NEXT(ehci, qh->qh_dma);
        struct ehci_qh  *head;
 
+       /* Don't link a QH if there's a Clear-TT-Buffer pending */
+       if (unlikely(qh->clearing_tt))
+               return;
+
        /* (re)start the async schedule? */
        head = ehci->async;
        timer_action_done (ehci, TIMER_ASYNC_OFF);
index 90ad339..2bfff30 100644 (file)
@@ -354,7 +354,9 @@ struct ehci_qh {
        unsigned short          period;         /* polling interval */
        unsigned short          start;          /* where polling starts */
 #define NO_FRAME ((unsigned short)~0)                  /* pick new start */
+
        struct usb_device       *dev;           /* access to TT */
+       unsigned                clearing_tt:1;  /* Clear-TT-Buf in progress */
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/