I'm forwarding this to the list, it's already merged into
Linus' tree since it seems to fix his printer problem ... :)

This could well resolve some of the other problems folk
have reported recently with the OHCI driver, though some
of the "bad entry" problems might really be slab related.
(Ripping out some kmalloc calls made them vanish for me,
but they were never that reproducible for me anyway.)

- Force out the PCI write disabling control/bulk queues.
  This "shouldn't" matter, they're empty.

- The rule is that if an ED is IDLE, its OK to just
  schedule it and start appending TDs.  Hard to do that
  when the typical error path left them still halted!

- Sometimes ed->hwTailP needs updating when TDs are
  removed from the queue, not just ed->hwHeadP. Oops.

- Oh, and it's not the high bits we want to save when
  we unlink ... it's the low bits (actually just toggle).

It should be available in the 2.5.47bk2 snapshot.

- Dave
--- ./drivers/usb-dist/host/ohci-q.c    Mon Nov 11 06:37:47 2002
+++ ./drivers/usb/host/ohci-q.c Tue Nov 12 20:17:36 2002
@@ -265,6 +269,9 @@
                        if (!ed->hwNextED) {
                                ohci->hc_control &= ~OHCI_CTRL_CLE;
                                writel (ohci->hc_control, &ohci->regs->control);
+                               writel (0, &ohci->regs->ed_controlcurrent);
+                               // post those pci writes
+                               (void) readl (&ohci->regs->control);
                        }
                        writel (le32_to_cpup (&ed->hwNextED),
                                &ohci->regs->ed_controlhead);
@@ -286,6 +293,9 @@
                        if (!ed->hwNextED) {
                                ohci->hc_control &= ~OHCI_CTRL_BLE;
                                writel (ohci->hc_control, &ohci->regs->control);
+                               writel (0, &ohci->regs->ed_bulkcurrent);
+                               // post those pci writes
+                               (void) readl (&ohci->regs->control);
                        }
                        writel (le32_to_cpup (&ed->hwNextED),
                                &ohci->regs->ed_bulkhead);
@@ -315,8 +325,12 @@
         * involves not marking it ED_IDLE till INTR_SF; we always do that
         * if td_list isn't empty.  Otherwise the race is small; but ...
         */
-       if (ed->state == ED_OPER)
+       if (ed->state == ED_OPER) {
                ed->state = ED_IDLE;
+               ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
+               ed->hwHeadP &= ~ED_H;
+               wmb ();
+       }
 }
 
 
@@ -767,6 +782,8 @@
                next->next_dl_td = rev; 
                rev = next;
 
+               if (ed->hwTailP == cpu_to_le32 (next->td_dma))
+                       ed->hwTailP = next->hwNextTD;
                ed->hwHeadP = next->hwNextTD | toggle;
        }
 
@@ -881,8 +897,13 @@
                                continue;
                        }
 
-                       /* patch pointer hc uses */
-                       savebits = *prev & cpu_to_le32 (TD_MASK);
+                       /* patch pointers hc uses ... tail, if we're removing
+                        * an otherwise active td, and whatever td pointer
+                        * points to this td
+                        */
+                       if (ed->hwTailP == cpu_to_le32 (td->td_dma))
+                               ed->hwTailP = td->hwNextTD;
+                       savebits = *prev & ~cpu_to_le32 (TD_MASK);
                        *prev = td->hwNextTD | savebits;
 
                        /* HC may have partly processed this TD */

Reply via email to