if (ohci->rh_state != OHCI_RH_RUNNING) {
/* With HC dead, we can clean up right away */
- finish_unlinks(ohci, 0);
+ ohci_work(ohci);
}
}
spin_unlock_irqrestore (&ohci->lock, flags);
if (ohci->rh_state != OHCI_RH_RUNNING) {
sanitize:
ed->state = ED_IDLE;
- finish_unlinks (ohci, 0);
+ ohci_work(ohci);
}
switch (ed->state) {
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
- process_done_list(ohci);
- if (ohci->ed_rm_list)
- finish_unlinks (ohci, ohci_frame_no(ohci));
+ ohci_work(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);
if (!urb->unlinked)
urb->unlinked = -ESHUTDOWN;
}
- finish_unlinks (ohci, 0);
+ ohci_work(ohci);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
static void update_done_list(struct ohci_hcd *);
-static void process_done_list(struct ohci_hcd *);
-static void finish_unlinks (struct ohci_hcd *, u16);
+static void ohci_work(struct ohci_hcd *);
#ifdef CONFIG_PM
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
spin_lock_irq (&ohci->lock);
}
update_done_list(ohci);
- process_done_list(ohci);
- finish_unlinks (ohci, ohci_frame_no(ohci));
+ ohci_work(ohci);
/*
* Some controllers don't handle "global" suspend properly if
/*-------------------------------------------------------------------------*/
/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
-static void
-finish_unlinks (struct ohci_hcd *ohci, u16 tick)
+static void finish_unlinks(struct ohci_hcd *ohci)
{
+ unsigned tick = ohci_frame_no(ohci);
struct ed *ed, **last;
rescan_all:
takeback_td(ohci, td);
}
}
+
+/*
+ * TD takeback and URB giveback must be single-threaded.
+ * This routine takes care of it all.
+ */
+static void ohci_work(struct ohci_hcd *ohci)
+{
+ if (ohci->working) {
+ ohci->restart_work = 1;
+ return;
+ }
+ ohci->working = 1;
+
+ restart:
+ process_done_list(ohci);
+ if (ohci->ed_rm_list)
+ finish_unlinks(ohci);
+
+ if (ohci->restart_work) {
+ ohci->restart_work = 0;
+ goto restart;
+ }
+ ohci->working = 0;
+}
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
unsigned autostop:1; /* rh auto stopping/stopped */
+ unsigned working:1;
+ unsigned restart_work:1;
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */