]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/host/ohci-hcd.c
USB: OHCI: redesign the TD done list
[karo-tx-linux.git] / drivers / usb / host / ohci-hcd.c
index f98d03f3144c3de66ef831d0068b069ede71b42d..3112799bba7ffeacb79db6e7534c95983e5d9c45 100644 (file)
@@ -76,8 +76,8 @@ static const char     hcd_name [] = "ohci_hcd";
 #include "ohci.h"
 #include "pci-quirks.h"
 
-static void ohci_dump (struct ohci_hcd *ohci, int verbose);
-static void ohci_stop (struct usb_hcd *hcd);
+static void ohci_dump(struct ohci_hcd *ohci);
+static void ohci_stop(struct usb_hcd *hcd);
 
 #include "ohci-hub.c"
 #include "ohci-dbg.c"
@@ -109,6 +109,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
 
 /*-------------------------------------------------------------------------*/
 
+static int number_of_tds(struct urb *urb)
+{
+       int                     len, i, num, this_sg_len;
+       struct scatterlist      *sg;
+
+       len = urb->transfer_buffer_length;
+       i = urb->num_mapped_sgs;
+
+       if (len > 0 && i > 0) {         /* Scatter-gather transfer */
+               num = 0;
+               sg = urb->sg;
+               for (;;) {
+                       this_sg_len = min_t(int, sg_dma_len(sg), len);
+                       num += DIV_ROUND_UP(this_sg_len, 4096);
+                       len -= this_sg_len;
+                       if (--i <= 0 || len <= 0)
+                               break;
+                       sg = sg_next(sg);
+               }
+
+       } else {                        /* Non-SG transfer */
+               /* one TD for every 4096 Bytes (could be up to 8K) */
+               num = DIV_ROUND_UP(len, 4096);
+       }
+       return num;
+}
+
 /*
  * queue up an urb for anything except the root hub
  */
@@ -142,12 +169,8 @@ static int ohci_urb_enqueue (
                // case PIPE_INTERRUPT:
                // case PIPE_BULK:
                default:
-                       /* one TD for every 4096 Bytes (can be up to 8K) */
-                       size += urb->transfer_buffer_length / 4096;
-                       /* ... and for any remaining bytes ... */
-                       if ((urb->transfer_buffer_length % 4096) != 0)
-                               size++;
-                       /* ... and maybe a zero length packet to wrap it up */
+                       size += number_of_tds(urb);
+                       /* maybe a zero-length packet to wrap it up */
                        if (size == 0)
                                size++;
                        else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
@@ -277,30 +300,24 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
        unsigned long           flags;
        int                     rc;
+       urb_priv_t              *urb_priv;
 
        spin_lock_irqsave (&ohci->lock, flags);
        rc = usb_hcd_check_unlink_urb(hcd, urb, status);
-       if (rc) {
-               ;       /* Do nothing */
-       } else if (ohci->rh_state == OHCI_RH_RUNNING) {
-               urb_priv_t  *urb_priv;
+       if (rc == 0) {
 
                /* Unless an IRQ completed the unlink while it was being
                 * handed to us, flag it for unlink and giveback, and force
                 * some upcoming INTR_SF to call finish_unlinks()
                 */
                urb_priv = urb->hcpriv;
-               if (urb_priv) {
-                       if (urb_priv->ed->state == ED_OPER)
-                               start_ed_unlink (ohci, urb_priv->ed);
+               if (urb_priv->ed->state == ED_OPER)
+                       start_ed_unlink(ohci, urb_priv->ed);
+
+               if (ohci->rh_state != OHCI_RH_RUNNING) {
+                       /* With HC dead, we can clean up right away */
+                       finish_unlinks(ohci, 0);
                }
-       } else {
-               /*
-                * with HC dead, we won't respect hc queue pointers
-                * any more ... just clean up every urb's memory.
-                */
-               if (urb->hcpriv)
-                       finish_urb(ohci, urb, status);
        }
        spin_unlock_irqrestore (&ohci->lock, flags);
        return rc;
@@ -332,8 +349,6 @@ rescan:
        if (ohci->rh_state != OHCI_RH_RUNNING) {
 sanitize:
                ed->state = ED_IDLE;
-               if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
-                       ohci->eds_scheduled--;
                finish_unlinks (ohci, 0);
        }
 
@@ -342,11 +357,6 @@ sanitize:
                /* major IRQ delivery trouble loses INTR_SF too... */
                if (limit-- == 0) {
                        ohci_warn(ohci, "ED unlink timeout\n");
-                       if (quirk_zfmicro(ohci)) {
-                               ohci_warn(ohci, "Attempting ZF TD recovery\n");
-                               ohci->ed_to_check = ed;
-                               ohci->zf_delay = 2;
-                       }
                        goto sanitize;
                }
                spin_unlock_irqrestore (&ohci->lock, flags);
@@ -408,93 +418,6 @@ ohci_shutdown (struct usb_hcd *hcd)
        ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval);
 }
 
-static int check_ed(struct ohci_hcd *ohci, struct ed *ed)
-{
-       return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0
-               && (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK)
-                       == (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK)
-               && !list_empty(&ed->td_list);
-}
-
-/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes
- * an interrupt TD but neglects to add it to the donelist.  On systems with
- * this chipset, we need to periodically check the state of the queues to look
- * for such "lost" TDs.
- */
-static void unlink_watchdog_func(unsigned long _ohci)
-{
-       unsigned long   flags;
-       unsigned        max;
-       unsigned        seen_count = 0;
-       unsigned        i;
-       struct ed       **seen = NULL;
-       struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
-
-       spin_lock_irqsave(&ohci->lock, flags);
-       max = ohci->eds_scheduled;
-       if (!max)
-               goto done;
-
-       if (ohci->ed_to_check)
-               goto out;
-
-       seen = kcalloc(max, sizeof *seen, GFP_ATOMIC);
-       if (!seen)
-               goto out;
-
-       for (i = 0; i < NUM_INTS; i++) {
-               struct ed       *ed = ohci->periodic[i];
-
-               while (ed) {
-                       unsigned        temp;
-
-                       /* scan this branch of the periodic schedule tree */
-                       for (temp = 0; temp < seen_count; temp++) {
-                               if (seen[temp] == ed) {
-                                       /* we've checked it and what's after */
-                                       ed = NULL;
-                                       break;
-                               }
-                       }
-                       if (!ed)
-                               break;
-                       seen[seen_count++] = ed;
-                       if (!check_ed(ohci, ed)) {
-                               ed = ed->ed_next;
-                               continue;
-                       }
-
-                       /* HC's TD list is empty, but HCD sees at least one
-                        * TD that's not been sent through the donelist.
-                        */
-                       ohci->ed_to_check = ed;
-                       ohci->zf_delay = 2;
-
-                       /* The HC may wait until the next frame to report the
-                        * TD as done through the donelist and INTR_WDH.  (We
-                        * just *assume* it's not a multi-TD interrupt URB;
-                        * those could defer the IRQ more than one frame, using
-                        * DI...)  Check again after the next INTR_SF.
-                        */
-                       ohci_writel(ohci, OHCI_INTR_SF,
-                                       &ohci->regs->intrstatus);
-                       ohci_writel(ohci, OHCI_INTR_SF,
-                                       &ohci->regs->intrenable);
-
-                       /* flush those writes */
-                       (void) ohci_readl(ohci, &ohci->regs->control);
-
-                       goto out;
-               }
-       }
-out:
-       kfree(seen);
-       if (ohci->eds_scheduled)
-               mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
-done:
-       spin_unlock_irqrestore(&ohci->lock, flags);
-}
-
 /*-------------------------------------------------------------------------*
  * HC functions
  *-------------------------------------------------------------------------*/
@@ -506,6 +429,9 @@ static int ohci_init (struct ohci_hcd *ohci)
        int ret;
        struct usb_hcd *hcd = ohci_to_hcd(ohci);
 
+       /* Accept arbitrarily long scatter-gather lists */
+       hcd->self.sg_tablesize = ~0;
+
        if (distrust_firmware)
                ohci->flags |= OHCI_QUIRK_HUB_POWER;
 
@@ -559,7 +485,7 @@ static int ohci_init (struct ohci_hcd *ohci)
                return 0;
 
        ohci->hcca = dma_alloc_coherent (hcd->self.controller,
-                       sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+                       sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
        if (!ohci->hcca)
                return -ENOMEM;
 
@@ -735,16 +661,7 @@ retry:
        // POTPGT delay is bits 24-31, in 2 ms units.
        mdelay ((val >> 23) & 0x1fe);
 
-       if (quirk_zfmicro(ohci)) {
-               /* Create timer to watch for bad queue state on ZF Micro */
-               setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func,
-                               (unsigned long) ohci);
-
-               ohci->eds_scheduled = 0;
-               ohci->ed_to_check = NULL;
-       }
-
-       ohci_dump (ohci, 1);
+       ohci_dump(ohci);
 
        return 0;
 }
@@ -825,7 +742,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
                        usb_hc_died(hcd);
                }
 
-               ohci_dump (ohci, 1);
+               ohci_dump(ohci);
                ohci_usb_reset (ohci);
        }
 
@@ -863,51 +780,21 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
                        usb_hcd_resume_root_hub(hcd);
        }
 
-       if (ints & OHCI_INTR_WDH) {
-               spin_lock (&ohci->lock);
-               dl_done_list (ohci);
-               spin_unlock (&ohci->lock);
-       }
-
-       if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
-               spin_lock(&ohci->lock);
-               if (ohci->ed_to_check) {
-                       struct ed *ed = ohci->ed_to_check;
-
-                       if (check_ed(ohci, ed)) {
-                               /* HC thinks the TD list is empty; HCD knows
-                                * at least one TD is outstanding
-                                */
-                               if (--ohci->zf_delay == 0) {
-                                       struct td *td = list_entry(
-                                               ed->td_list.next,
-                                               struct td, td_list);
-                                       ohci_warn(ohci,
-                                                 "Reclaiming orphan TD %p\n",
-                                                 td);
-                                       takeback_td(ohci, td);
-                                       ohci->ed_to_check = NULL;
-                               }
-                       } else
-                               ohci->ed_to_check = NULL;
-               }
-               spin_unlock(&ohci->lock);
-       }
+       spin_lock(&ohci->lock);
+       if (ints & OHCI_INTR_WDH)
+               update_done_list(ohci);
 
        /* could track INTR_SO to reduce available PCI/... bandwidth */
 
        /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
         * when there's still unlinking to be done (next frame).
         */
-       spin_lock (&ohci->lock);
+       process_done_list(ohci);
        if (ohci->ed_rm_list)
                finish_unlinks (ohci, ohci_frame_no(ohci));
-       if ((ints & OHCI_INTR_SF) != 0
-                       && !ohci->ed_rm_list
-                       && !ohci->ed_to_check
+       if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
                        && ohci->rh_state == OHCI_RH_RUNNING)
                ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
-       spin_unlock (&ohci->lock);
 
        if (ohci->rh_state == OHCI_RH_RUNNING) {
                ohci_writel (ohci, ints, &regs->intrstatus);
@@ -915,6 +802,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
                // flush those writes
                (void) ohci_readl (ohci, &ohci->regs->control);
        }
+       spin_unlock(&ohci->lock);
 
        return IRQ_HANDLED;
 }
@@ -925,7 +813,7 @@ static void ohci_stop (struct usb_hcd *hcd)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
-       ohci_dump (ohci, 1);
+       ohci_dump(ohci);
 
        if (quirk_nec(ohci))
                flush_work(&ohci->nec_work);
@@ -935,8 +823,6 @@ static void ohci_stop (struct usb_hcd *hcd)
        free_irq(hcd->irq, hcd);
        hcd->irq = 0;
 
-       if (quirk_zfmicro(ohci))
-               del_timer(&ohci->unlink_watchdog);
        if (quirk_amdiso(ohci))
                usb_amd_dev_put();