usb_hcd_resume_root_hub(hcd);
        }
 
-       if (ints & OHCI_INTR_WDH) {
-               spin_lock (&ohci->lock);
-               dl_done_list (ohci);
-               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->rh_state == OHCI_RH_RUNNING)
                ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);
-       spin_unlock (&ohci->lock);
 
        if (ohci->rh_state == OHCI_RH_RUNNING) {
                ohci_writel (ohci, ints, ®s->intrstatus);
                // flush those writes
                (void) ohci_readl (ohci, &ohci->regs->control);
        }
+       spin_unlock(&ohci->lock);
 
        return IRQ_HANDLED;
 }
 
        }
 }
 
-/* replies to the request have to be on a FIFO basis so
- * we unreverse the hc-reversed done-list
- */
-static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
+/* Add a TD to the done list */
+static void add_to_done_list(struct ohci_hcd *ohci, struct td *td)
+{
+       struct td       *td2, *td_prev;
+       struct ed       *ed;
+
+       if (td->next_dl_td)
+               return;         /* Already on the list */
+
+       /* Add all the TDs going back until we reach one that's on the list */
+       ed = td->ed;
+       td2 = td_prev = td;
+       list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) {
+               if (td2->next_dl_td)
+                       break;
+               td2->next_dl_td = td_prev;
+               td_prev = td2;
+       }
+
+       if (ohci->dl_end)
+               ohci->dl_end->next_dl_td = td_prev;
+       else
+               ohci->dl_start = td_prev;
+
+       /*
+        * Make td->next_dl_td point to td itself, to mark the fact
+        * that td is on the done list.
+        */
+       ohci->dl_end = td->next_dl_td = td;
+}
+
+/* Get the entries on the hardware done queue and put them on our list */
+static void update_done_list(struct ohci_hcd *ohci)
 {
        u32             td_dma;
-       struct td       *td_rev = NULL;
        struct td       *td = NULL;
 
        td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head);
        wmb();
 
        /* get TD from hc's singly linked list, and
-        * prepend to ours.  ed->td_list changes later.
+        * add to ours.  ed->td_list changes later.
         */
        while (td_dma) {
                int             cc;
                                && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H)))
                        ed_halted(ohci, td, cc);
 
-               td->next_dl_td = td_rev;
-               td_rev = td;
                td_dma = hc32_to_cpup (ohci, &td->hwNextTD);
+               add_to_done_list(ohci, td);
        }
-       return td_rev;
 }
 
 /*-------------------------------------------------------------------------*/
                /* only take off EDs that the HC isn't using, accounting for
                 * frame counter wraps and EDs with partially retired TDs
                 */
-               if (likely(ohci->rh_state == OHCI_RH_RUNNING)) {
-                       if (tick_before (tick, ed->tick)) {
+               if (likely(ohci->rh_state == OHCI_RH_RUNNING) &&
+                               tick_before(tick, ed->tick)) {
 skip_ed:
-                               last = &ed->ed_next;
-                               continue;
-                       }
+                       last = &ed->ed_next;
+                       continue;
+               }
+               if (!list_empty(&ed->td_list)) {
+                       struct td       *td;
+                       u32             head;
 
-                       if (!list_empty (&ed->td_list)) {
-                               struct td       *td;
-                               u32             head;
+                       td = list_first_entry(&ed->td_list, struct td, td_list);
 
-                               td = list_entry (ed->td_list.next, struct td,
-                                                       td_list);
-                               head = hc32_to_cpu (ohci, ed->hwHeadP) &
-                                                               TD_MASK;
+                       /* INTR_WDH may need to clean up first */
+                       head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK;
+                       if (td->td_dma != head &&
+                                       ohci->rh_state == OHCI_RH_RUNNING)
+                               goto skip_ed;
 
-                               /* INTR_WDH may need to clean up first */
-                               if (td->td_dma != head)
-                                       goto skip_ed;
-                       }
+                       /* Don't mess up anything already on the done list */
+                       if (td->next_dl_td)
+                               goto skip_ed;
                }
 
                /* ED's now officially unlinked, hc doesn't see */
  * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
  * instead of scanning the (re-reversed) donelist as this does.
  */
-static void
-dl_done_list (struct ohci_hcd *ohci)
+static void process_done_list(struct ohci_hcd *ohci)
 {
-       struct td       *td = dl_reverse_done_list (ohci);
-
-       while (td) {
-               struct td       *td_next = td->next_dl_td;
-               struct ed       *ed = td->ed;
+       struct td       *td;
 
-               /*
-                * Some OHCI controllers (NVIDIA for sure, maybe others)
-                * occasionally forget to add TDs to the done queue.  Since
-                * TDs for a given endpoint are always processed in order,
-                * if we find a TD on the donelist then all of its
-                * predecessors must be finished as well.
-                */
-               for (;;) {
-                       struct td       *td2;
-
-                       td2 = list_first_entry(&ed->td_list, struct td,
-                                       td_list);
-                       if (td2 == td)
-                               break;
-                       takeback_td(ohci, td2);
-               }
+       while (ohci->dl_start) {
+               td = ohci->dl_start;
+               if (td == ohci->dl_end)
+                       ohci->dl_start = ohci->dl_end = NULL;
+               else
+                       ohci->dl_start = td->next_dl_td;
 
                takeback_td(ohci, td);
-               td = td_next;
        }
 }