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, ®s->intrdisable);
(void)readl (®s->intrdisable); /* PCI posting flush */
dl_done_list (ohci, dl_reverse_done_list (ohci));
+ wmb();
writel (OHCI_INTR_WDH, ®s->intrenable);
(void)readl (®s->intrdisable); /* PCI posting flush */
}
if (ints & OHCI_INTR_SO) {
dbg("USB Schedule overrun");
+ wmb();
writel (OHCI_INTR_SO, ®s->intrenable);
(void)readl (®s->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 (®s->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, ®s->intrenable);
(void)readl (®s->intrdisable); /* PCI posting flush
*/
}
}
