USB: EHCI: reorganize isochronous scheduler routine
[linux-2.6-microblaze.git] / drivers / usb / host / ehci-sched.c
index 805ec63..3381319 100644 (file)
@@ -880,8 +880,7 @@ static int intr_submit (
 
        spin_lock_irqsave (&ehci->lock, flags);
 
-       if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
-                       &ehci_to_hcd(ehci)->flags))) {
+       if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
                status = -ESHUTDOWN;
                goto done_not_linked;
        }
@@ -1396,30 +1395,25 @@ iso_stream_schedule (
        struct ehci_iso_stream  *stream
 )
 {
-       u32                     now, next, start, period;
+       u32                     now, next, start, period, span;
        int                     status;
        unsigned                mod = ehci->periodic_size << 3;
        struct ehci_iso_sched   *sched = urb->hcpriv;
-       struct pci_dev          *pdev;
 
-       if (sched->span > (mod - SCHEDULE_SLOP)) {
-               ehci_dbg (ehci, "iso request %p too long\n", urb);
-               status = -EFBIG;
-               goto fail;
+       period = urb->interval;
+       span = sched->span;
+       if (!stream->highspeed) {
+               period <<= 3;
+               span <<= 3;
        }
 
-       if ((stream->depth + sched->span) > mod) {
-               ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n",
-                       urb, stream->depth, sched->span, mod);
+       if (span > mod - SCHEDULE_SLOP) {
+               ehci_dbg (ehci, "iso request %p too long\n", urb);
                status = -EFBIG;
                goto fail;
        }
 
-       period = urb->interval;
-       if (!stream->highspeed)
-               period <<= 3;
-
-       now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
+       now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
 
        /* Typical case: reuse current schedule, stream is still active.
         * Hopefully there are no gaps from the host falling behind
@@ -1427,34 +1421,35 @@ iso_stream_schedule (
         * slot in the schedule, implicitly assuming URB_ISO_ASAP.
         */
        if (likely (!list_empty (&stream->td_list))) {
-               pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
-               start = stream->next_uframe;
+               u32     excess;
 
                /* For high speed devices, allow scheduling within the
-                * isochronous scheduling threshold.  For full speed devices,
-                * don't. (Work around for Intel ICH9 bug.)
+                * isochronous scheduling threshold.  For full speed devices
+                * and Intel PCI-based controllers, don't (work around for
+                * Intel ICH9 bug).
                 */
-               if (!stream->highspeed &&
-                               pdev->vendor == PCI_VENDOR_ID_INTEL)
+               if (!stream->highspeed && ehci->fs_i_thresh)
                        next = now + ehci->i_thresh;
                else
                        next = now;
 
-               /* Fell behind (by up to twice the slop amount)? */
-               if (((start - next) & (mod - 1)) >=
-                               mod - 2 * SCHEDULE_SLOP)
-                       start += period * DIV_ROUND_UP(
-                                       (next - start) & (mod - 1),
-                                       period);
-
-               /* Tried to schedule too far into the future? */
-               if (unlikely(((start - now) & (mod - 1)) + sched->span
-                                       >= mod - 2 * SCHEDULE_SLOP)) {
+               /* Fell behind (by up to twice the slop amount)?
+                * We decide based on the time of the last currently-scheduled
+                * slot, not the time of the next available slot.
+                */
+               excess = (stream->next_uframe - period - next) & (mod - 1);
+               if (excess >= mod - 2 * SCHEDULE_SLOP)
+                       start = next + excess - mod + period *
+                                       DIV_ROUND_UP(mod - excess, period);
+               else
+                       start = next + excess + period;
+               if (start - now >= mod) {
+                       ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
+                                       urb, start - now - period, period,
+                                       mod);
                        status = -EFBIG;
                        goto fail;
                }
-               stream->next_uframe = start;
-               goto ready;
        }
 
        /* need to schedule; when's the next (u)frame we could start?
@@ -1463,51 +1458,60 @@ iso_stream_schedule (
         * can also help high bandwidth if the dma and irq loads don't
         * jump until after the queue is primed.
         */
-       start = SCHEDULE_SLOP + (now & ~0x07);
-       start %= mod;
-       stream->next_uframe = start;
-
-       /* NOTE:  assumes URB_ISO_ASAP, to limit complexity/bugs */
-
-       /* find a uframe slot with enough bandwidth */
-       for (; start < (stream->next_uframe + period); start++) {
-               int             enough_space;
-
-               /* check schedule: enough space? */
-               if (stream->highspeed)
-                       enough_space = itd_slot_ok (ehci, mod, start,
-                                       stream->usecs, period);
-               else {
-                       if ((start % 8) >= 6)
-                               continue;
-                       enough_space = sitd_slot_ok (ehci, mod, stream,
-                                       start, sched, period);
+       else {
+               start = SCHEDULE_SLOP + (now & ~0x07);
+
+               /* NOTE:  assumes URB_ISO_ASAP, to limit complexity/bugs */
+
+               /* find a uframe slot with enough bandwidth */
+               next = start + period;
+               for (; start < next; start++) {
+
+                       /* check schedule: enough space? */
+                       if (stream->highspeed) {
+                               if (itd_slot_ok(ehci, mod, start,
+                                               stream->usecs, period))
+                                       break;
+                       } else {
+                               if ((start % 8) >= 6)
+                                       continue;
+                               if (sitd_slot_ok(ehci, mod, stream,
+                                               start, sched, period))
+                                       break;
+                       }
                }
 
-               /* schedule it here if there's enough bandwidth */
-               if (enough_space) {
-                       stream->next_uframe = start % mod;
-                       goto ready;
+               /* no room in the schedule */
+               if (start == next) {
+                       ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n",
+                               urb, now, now + mod);
+                       status = -ENOSPC;
+                       goto fail;
                }
        }
 
-       /* no room in the schedule */
-       ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
-               list_empty (&stream->td_list) ? "" : "re",
-               urb, now, now + mod);
-       status = -ENOSPC;
+       /* Tried to schedule too far into the future? */
+       if (unlikely(start - now + span - period
+                               >= mod - 2 * SCHEDULE_SLOP)) {
+               ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
+                               urb, start - now, span - period,
+                               mod - 2 * SCHEDULE_SLOP);
+               status = -EFBIG;
+               goto fail;
+       }
 
-fail:
-       iso_sched_free (stream, sched);
-       urb->hcpriv = NULL;
-       return status;
+       stream->next_uframe = start & (mod - 1);
 
-ready:
        /* report high speed start in uframes; full speed, in frames */
        urb->start_frame = stream->next_uframe;
        if (!stream->highspeed)
                urb->start_frame >>= 3;
        return 0;
+
+ fail:
+       iso_sched_free(stream, sched);
+       urb->hcpriv = NULL;
+       return status;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1602,7 +1606,7 @@ itd_link_urb (
        struct ehci_iso_sched   *iso_sched = urb->hcpriv;
        struct ehci_itd         *itd;
 
-       next_uframe = stream->next_uframe % mod;
+       next_uframe = stream->next_uframe & (mod - 1);
 
        if (unlikely (list_empty(&stream->td_list))) {
                ehci_to_hcd(ehci)->self.bandwidth_allocated
@@ -1640,13 +1644,13 @@ itd_link_urb (
 
                next_uframe += stream->interval;
                stream->depth += stream->interval;
-               next_uframe %= mod;
+               next_uframe &= mod - 1;
                packet++;
 
                /* link completed itds into the schedule */
                if (((next_uframe >> 3) != frame)
                                || packet == urb->number_of_packets) {
-                       itd_link (ehci, frame % ehci->periodic_size, itd);
+                       itd_link(ehci, frame & (ehci->periodic_size - 1), itd);
                        itd = NULL;
                }
        }
@@ -1815,8 +1819,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
 
        /* schedule ... need to lock */
        spin_lock_irqsave (&ehci->lock, flags);
-       if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
-                              &ehci_to_hcd(ehci)->flags))) {
+       if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
                status = -ESHUTDOWN;
                goto done_not_linked;
        }
@@ -2024,7 +2027,7 @@ sitd_link_urb (
                        "sched devp %s ep%d%s-iso [%d] %dms/%04x\n",
                        urb->dev->devpath, stream->bEndpointAddress & 0x0f,
                        (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
-                       (next_uframe >> 3) % ehci->periodic_size,
+                       (next_uframe >> 3) & (ehci->periodic_size - 1),
                        stream->interval, hc32_to_cpu(ehci, stream->splits));
                stream->start = jiffies;
        }
@@ -2047,13 +2050,13 @@ sitd_link_urb (
                sitd->urb = urb;
 
                sitd_patch(ehci, stream, sitd, sched, packet);
-               sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
+               sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
                                sitd);
 
                next_uframe += stream->interval << 3;
                stream->depth += stream->interval << 3;
        }
-       stream->next_uframe = next_uframe % mod;
+       stream->next_uframe = next_uframe & (mod - 1);
 
        /* don't need that schedule data any more */
        iso_sched_free (stream, sched);
@@ -2201,8 +2204,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
 
        /* schedule ... need to lock */
        spin_lock_irqsave (&ehci->lock, flags);
-       if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
-                              &ehci_to_hcd(ehci)->flags))) {
+       if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
                status = -ESHUTDOWN;
                goto done_not_linked;
        }
@@ -2263,7 +2265,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_frame = (clock >> 3) % ehci->periodic_size;
+               clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
        } else  {
                clock = now_uframe + mod - 1;
                clock_frame = -1;
@@ -2272,7 +2274,7 @@ scan_periodic (struct ehci_hcd *ehci)
                free_cached_lists(ehci);
                ehci->clock_frame = clock_frame;
        }
-       clock %= mod;
+       clock &= mod - 1;
        clock_frame = clock >> 3;
 
        for (;;) {
@@ -2361,7 +2363,7 @@ restart:
                                 * frame is current.
                                 */
                                if (((frame == clock_frame) ||
-                                    (((frame + 1) % ehci->periodic_size)
+                                    (((frame + 1) & (ehci->periodic_size - 1))
                                      == clock_frame))
                                    && live
                                    && (q.sitd->hw_results &
@@ -2428,7 +2430,8 @@ restart:
                                        || ehci->periodic_sched == 0)
                                break;
                        ehci->next_uframe = now_uframe;
-                       now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
+                       now = ehci_readl(ehci, &ehci->regs->frame_index) &
+                                       (mod - 1);
                        if (now_uframe == now)
                                break;
 
@@ -2441,7 +2444,7 @@ restart:
                        }
                } else {
                        now_uframe++;
-                       now_uframe %= mod;
+                       now_uframe &= mod - 1;
                }
        }
 }