]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
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>
Wed, 9 Sep 2009 03:34:01 +0000 (20:34 -0700)
commit 914b701280a76f96890ad63eb0fa99bf204b961c upstream.

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>
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 bf69f473910785465c8c20ef4be46ea7acab2946..5c25b1a1d2c503fc5587968f7be1adaecbd7747e 100644 (file)
@@ -112,6 +112,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 01c3da34f678e4a52107f64bca1674bb7e64c807..7fb1ef07856b61b860e0e6a0bba66329e81c616f 100644 (file)
@@ -324,6 +324,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 c637207a1c80b63eab4bfa5587d4250bfe0d3c86..abeea7bddb16003323ab62652d5ee77de77fa01a 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 9c32063a0c2f6611fc938760ea84906d1f3f9666..8573b0331b03c0ca65b72d6b7baf3a138ed98104 100644 (file)
@@ -60,6 +60,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 9d487908012e56fb515d82576ccdbd5db71fa480..64ab30a02a85d3187409d7a62e8c7da0f42eef0c 100644 (file)
@@ -164,6 +164,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 5aa8bce90e1f3856d7195db5614394a6c709ba9f..a26b7f7db727bff5dda795b8989b6f8af11b3585 100644 (file)
@@ -408,6 +408,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 ef732b704f53d996aed213bc1563c10063e89978..8b6556ef7687bace4e4a01f3fd06305c2169ca85 100644 (file)
@@ -78,6 +78,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 1ba9f9a8c3088bd607fb95b5599e0982c6c8c9d2..efefc913aed5f89778d9d09525c3a6fdf9d463d3 100644 (file)
@@ -74,6 +74,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 ps3_ehci_probe(struct ps3_system_bus_device *dev)
index 68bf81e982d239fec2f2a38dc6b20d6129edde16..e3d2b627bfb33cc41021c6872807816b91d29f82 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 6cff195e1a365e46d739bc42263b57381365bb3b..ec5af223200f46ea1e74023dc01b8e1b8ad966a2 100644 (file)
@@ -353,7 +353,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)));
 
 /*-------------------------------------------------------------------------*/