As discussed here:

http://sourceforge.net/mailarchive/forum.php?thread_id=6433255&forum_id=5395

A short summary is that memory barriers are needed in several places for correct operation on my MIPS based platform.

I have attached a patch against usb-ohci.c from the 2.4.29 sources I obtained from linux-mips.org. These should be the same as the "master" 2.4.29 sources WRT USB.

It is possible that some of the wmb() that I added are not necessary, but which ones they are is unclear to me.

Signed-off-by: David Daney <[EMAIL PROTECTED]>


David Daney.
*** usb-ohci.c.orig     2005-01-28 10:26:05.229595962 -0800
--- usb-ohci.c  2005-01-28 10:26:45.651510861 -0800
*************** static int ep_link (ohci_t * ohci, ed_t 
*** 1075,1084 ****
--- 1075,1085 ----
                }
                ed->ed_prev = ohci->ed_controltail;
                if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
                        !ohci->ed_rm_list[1] && !ohci->sleeping) {
                        ohci->hc_control |= OHCI_CTRL_CLE;
+                       wmb();
                        writel (ohci->hc_control, &ohci->regs->control);
                }
                ohci->ed_controltail = edi;       
                break;
                
*************** static int ep_link (ohci_t * ohci, ed_t 
*** 1091,1100 ****
--- 1092,1102 ----
                }
                ed->ed_prev = ohci->ed_bulktail;
                if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
                        !ohci->ed_rm_list[1] && !ohci->sleeping) {
                        ohci->hc_control |= OHCI_CTRL_BLE;
+                       wmb();
                        writel (ohci->hc_control, &ohci->regs->control);
                }
                ohci->ed_bulktail = edi;          
                break;
                
*************** static int ep_unlink (ohci_t * ohci, ed_
*** 1183,1192 ****
--- 1185,1195 ----
        switch (ed->type) {
        case PIPE_CONTROL:
                if (ed->ed_prev == NULL) {
                        if (!ed->hwNextED) {
                                ohci->hc_control &= ~OHCI_CTRL_CLE;
+                               wmb();
                                writel (ohci->hc_control, &ohci->regs->control);
                        }
                        writel (le32_to_cpup (&ed->hwNextED), 
&ohci->regs->ed_controlhead);
                } else {
                        ed->ed_prev->hwNextED = ed->hwNextED;
*************** static int ep_unlink (ohci_t * ohci, ed_
*** 1200,1209 ****
--- 1203,1213 ----
        
        case PIPE_BULK:
                if (ed->ed_prev == NULL) {
                        if (!ed->hwNextED) {
                                ohci->hc_control &= ~OHCI_CTRL_BLE;
+                               wmb();
                                writel (ohci->hc_control, &ohci->regs->control);
                        }
                        writel (le32_to_cpup (&ed->hwNextED), 
&ohci->regs->ed_bulkhead);
                } else {
                        ed->ed_prev->hwNextED = ed->hwNextED;
*************** static void ep_rm_ed (struct usb_device 
*** 1327,1340 ****
--- 1331,1346 ----
  
        if (!ohci->disabled) {
                switch (ed->type) {
                        case PIPE_CONTROL: /* stop control list */
                                ohci->hc_control &= ~OHCI_CTRL_CLE;
+                               wmb();
                                writel (ohci->hc_control, 
&ohci->regs->control); 
                                break;
                        case PIPE_BULK: /* stop bulk list */
                                ohci->hc_control &= ~OHCI_CTRL_BLE;
+                               wmb();
                                writel (ohci->hc_control, 
&ohci->regs->control); 
                                break;
                }
        }
  
*************** td_fill (ohci_t * ohci, unsigned int inf
*** 1398,1409 ****
                td->hwBE = 0;
        td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
        td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
  
        /* append to queue */
-       wmb();
        td->ed->hwTailP = td->hwNextTD;
  }
  
  /*-------------------------------------------------------------------------*/
   
  /* prepare all TDs of a transfer */
--- 1404,1415 ----
                td->hwBE = 0;
        td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
        td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
  
        /* append to queue */
        td->ed->hwTailP = td->hwNextTD;
+       wmb();
  }
  
  /*-------------------------------------------------------------------------*/
   
  /* prepare all TDs of a transfer */
*************** static void dl_del_list (ohci_t  * ohci,
*** 1701,1710 ****
--- 1707,1717 ----
                if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) {
                        if (ohci->ed_controltail)
                                ohci->hc_control |= OHCI_CTRL_CLE;
                        if (ohci->ed_bulktail)
                                ohci->hc_control |= OHCI_CTRL_BLE;
+                       wmb();
                        writel (ohci->hc_control, &ohci->regs->control);   
                }
        }
  
        ohci->ed_rm_list[frame] = NULL;
*************** static int hc_reset (ohci_t * ohci)
*** 2195,2204 ****
--- 2202,2212 ----
        int smm_timeout = 50; /* 0,5 sec */
                
  #ifndef __hppa__
        /* PA-RISC doesn't have SMM, but PDC might leave IR set */
        if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+               wmb();
                writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership 
*/
                dbg("USB HC TakeOver from SMM");
                while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
                        wait_ms (10);
                        if (--smm_timeout == 0) {
*************** static int hc_start (ohci_t * ohci)
*** 2265,2274 ****
--- 2273,2283 ----
        writel (0x628, &ohci->regs->lsthresh);
  
        /* start controller operations */
        ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
        ohci->disabled = 0;
+       wmb();
        writel (ohci->hc_control, &ohci->regs->control);
   
        /* Choose the interrupts we care about now, others later on demand */
        mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
        writel (mask, &ohci->regs->intrenable);
*************** static void hc_interrupt (int irq, void 
*** 2358,2367 ****
--- 2367,2377 ----
        int ints; 
  
        spin_lock (&ohci->ohci_lock);
  
        /* avoid (slow) readl if only WDH happened */
+       wmb();
        if ((ohci->hcca->done_head != 0)
                        && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
                ints =  OHCI_INTR_WDH;
  
        /* cardbus/... hardware gone before remove() */
*************** static void hc_interrupt (int irq, void 
*** 2398,2413 ****
--- 2408,2425 ----
    
        if (ints & OHCI_INTR_WDH) {
                writel (OHCI_INTR_WDH, &regs->intrdisable);     
                (void)readl (&regs->intrdisable); /* PCI posting flush */
                dl_done_list (ohci, dl_reverse_done_list (ohci));
+               wmb();
                writel (OHCI_INTR_WDH, &regs->intrenable); 
                (void)readl (&regs->intrdisable); /* PCI posting flush */
        }
    
        if (ints & OHCI_INTR_SO) {
                dbg("USB Schedule overrun");
+               wmb();
                writel (OHCI_INTR_SO, &regs->intrenable);        
                (void)readl (&regs->intrdisable); /* PCI posting flush */
        }
  
        // FIXME:  this assumes SOF (1/ms) interrupts don't get lost...
*************** static void hc_interrupt (int irq, void 
*** 2417,2426 ****
--- 2429,2439 ----
                (void)readl (&regs->intrdisable); /* PCI posting flush */
                if (ohci->ed_rm_list[!frame] != NULL) {
                        dl_del_list (ohci, !frame);
                }
                if (ohci->ed_rm_list[frame] != NULL) {
+                       wmb();
                        writel (OHCI_INTR_SF, &regs->intrenable);       
                        (void)readl (&regs->intrdisable); /* PCI posting flush 
*/
                }
        }
  

Reply via email to