diff options
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 10 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ohci-q.c | 28 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 2 |
4 files changed, 34 insertions, 12 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 3112799bba7f..ad588538e2e7 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -316,7 +316,7 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 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); @@ -349,7 +349,7 @@ rescan: if (ohci->rh_state != OHCI_RH_RUNNING) { sanitize: ed->state = ED_IDLE; - finish_unlinks (ohci, 0); + ohci_work(ohci); } switch (ed->state) { @@ -789,9 +789,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) /* 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); @@ -879,7 +877,7 @@ int ohci_restart(struct ohci_hcd *ohci) 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: */ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index dccb90edd66e..8991692bcfb8 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -40,8 +40,7 @@ (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) @@ -89,8 +88,7 @@ __acquires(ohci->lock) 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 diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index f36b2fa0ee2f..1974ddc68e45 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -964,9 +964,9 @@ static void update_done_list(struct ohci_hcd *ohci) /*-------------------------------------------------------------------------*/ /* 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: @@ -1202,3 +1202,27 @@ static void process_done_list(struct ohci_hcd *ohci) 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; +} diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index a8259bc6fd8b..ef348c2e1e4b 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -393,6 +393,8 @@ struct ohci_hcd { 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 */ |