Commit 95d9a01d authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: OHCI: revert the ZF Micro orphan-TD quirk

This patch reverts the important parts of commit 89a0fd18 (USB:
OHCI handles more ZFMicro quirks), namely, the parts related to
handling orphan TDs for interrupt endpoints.  A later patch in this
series will introduce a more general mechanism that applies to all
endpoint types and all controllers.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a40178b2
...@@ -355,8 +355,6 @@ ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) ...@@ -355,8 +355,6 @@ ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
if (ohci->rh_state != OHCI_RH_RUNNING) { if (ohci->rh_state != OHCI_RH_RUNNING) {
sanitize: sanitize:
ed->state = ED_IDLE; ed->state = ED_IDLE;
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
ohci->eds_scheduled--;
finish_unlinks (ohci, 0); finish_unlinks (ohci, 0);
} }
...@@ -365,11 +363,6 @@ ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) ...@@ -365,11 +363,6 @@ ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
/* major IRQ delivery trouble loses INTR_SF too... */ /* major IRQ delivery trouble loses INTR_SF too... */
if (limit-- == 0) { if (limit-- == 0) {
ohci_warn(ohci, "ED unlink timeout\n"); ohci_warn(ohci, "ED unlink timeout\n");
if (quirk_zfmicro(ohci)) {
ohci_warn(ohci, "Attempting ZF TD recovery\n");
ohci->ed_to_check = ed;
ohci->zf_delay = 2;
}
goto sanitize; goto sanitize;
} }
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
...@@ -431,93 +424,6 @@ ohci_shutdown (struct usb_hcd *hcd) ...@@ -431,93 +424,6 @@ ohci_shutdown (struct usb_hcd *hcd)
ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval);
} }
static int check_ed(struct ohci_hcd *ohci, struct ed *ed)
{
return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0
&& (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK)
== (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK)
&& !list_empty(&ed->td_list);
}
/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes
* an interrupt TD but neglects to add it to the donelist. On systems with
* this chipset, we need to periodically check the state of the queues to look
* for such "lost" TDs.
*/
static void unlink_watchdog_func(unsigned long _ohci)
{
unsigned long flags;
unsigned max;
unsigned seen_count = 0;
unsigned i;
struct ed **seen = NULL;
struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
spin_lock_irqsave(&ohci->lock, flags);
max = ohci->eds_scheduled;
if (!max)
goto done;
if (ohci->ed_to_check)
goto out;
seen = kcalloc(max, sizeof *seen, GFP_ATOMIC);
if (!seen)
goto out;
for (i = 0; i < NUM_INTS; i++) {
struct ed *ed = ohci->periodic[i];
while (ed) {
unsigned temp;
/* scan this branch of the periodic schedule tree */
for (temp = 0; temp < seen_count; temp++) {
if (seen[temp] == ed) {
/* we've checked it and what's after */
ed = NULL;
break;
}
}
if (!ed)
break;
seen[seen_count++] = ed;
if (!check_ed(ohci, ed)) {
ed = ed->ed_next;
continue;
}
/* HC's TD list is empty, but HCD sees at least one
* TD that's not been sent through the donelist.
*/
ohci->ed_to_check = ed;
ohci->zf_delay = 2;
/* The HC may wait until the next frame to report the
* TD as done through the donelist and INTR_WDH. (We
* just *assume* it's not a multi-TD interrupt URB;
* those could defer the IRQ more than one frame, using
* DI...) Check again after the next INTR_SF.
*/
ohci_writel(ohci, OHCI_INTR_SF,
&ohci->regs->intrstatus);
ohci_writel(ohci, OHCI_INTR_SF,
&ohci->regs->intrenable);
/* flush those writes */
(void) ohci_readl(ohci, &ohci->regs->control);
goto out;
}
}
out:
kfree(seen);
if (ohci->eds_scheduled)
mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
done:
spin_unlock_irqrestore(&ohci->lock, flags);
}
/*-------------------------------------------------------------------------* /*-------------------------------------------------------------------------*
* HC functions * HC functions
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
...@@ -761,15 +667,6 @@ static int ohci_run (struct ohci_hcd *ohci) ...@@ -761,15 +667,6 @@ static int ohci_run (struct ohci_hcd *ohci)
// POTPGT delay is bits 24-31, in 2 ms units. // POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((val >> 23) & 0x1fe); mdelay ((val >> 23) & 0x1fe);
if (quirk_zfmicro(ohci)) {
/* Create timer to watch for bad queue state on ZF Micro */
setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func,
(unsigned long) ohci);
ohci->eds_scheduled = 0;
ohci->ed_to_check = NULL;
}
ohci_dump(ohci); ohci_dump(ohci);
return 0; return 0;
...@@ -895,31 +792,6 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ...@@ -895,31 +792,6 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
spin_unlock (&ohci->lock); spin_unlock (&ohci->lock);
} }
if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
spin_lock(&ohci->lock);
if (ohci->ed_to_check) {
struct ed *ed = ohci->ed_to_check;
if (check_ed(ohci, ed)) {
/* HC thinks the TD list is empty; HCD knows
* at least one TD is outstanding
*/
if (--ohci->zf_delay == 0) {
struct td *td = list_entry(
ed->td_list.next,
struct td, td_list);
ohci_warn(ohci,
"Reclaiming orphan TD %p\n",
td);
takeback_td(ohci, td);
ohci->ed_to_check = NULL;
}
} else
ohci->ed_to_check = NULL;
}
spin_unlock(&ohci->lock);
}
/* could track INTR_SO to reduce available PCI/... bandwidth */ /* could track INTR_SO to reduce available PCI/... bandwidth */
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
...@@ -928,9 +800,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ...@@ -928,9 +800,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
spin_lock (&ohci->lock); spin_lock (&ohci->lock);
if (ohci->ed_rm_list) if (ohci->ed_rm_list)
finish_unlinks (ohci, ohci_frame_no(ohci)); finish_unlinks (ohci, ohci_frame_no(ohci));
if ((ints & OHCI_INTR_SF) != 0 if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& !ohci->ed_rm_list
&& !ohci->ed_to_check
&& ohci->rh_state == OHCI_RH_RUNNING) && ohci->rh_state == OHCI_RH_RUNNING)
ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable); ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
spin_unlock (&ohci->lock); spin_unlock (&ohci->lock);
...@@ -961,8 +831,6 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -961,8 +831,6 @@ static void ohci_stop (struct usb_hcd *hcd)
free_irq(hcd->irq, hcd); free_irq(hcd->irq, hcd);
hcd->irq = 0; hcd->irq = 0;
if (quirk_zfmicro(ohci))
del_timer(&ohci->unlink_watchdog);
if (quirk_amdiso(ohci)) if (quirk_amdiso(ohci))
usb_amd_dev_put(); usb_amd_dev_put();
......
...@@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) ...@@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
ed->ed_prev = NULL; ed->ed_prev = NULL;
ed->ed_next = NULL; ed->ed_next = NULL;
ed->hwNextED = 0; ed->hwNextED = 0;
if (quirk_zfmicro(ohci)
&& (ed->type == PIPE_INTERRUPT)
&& !(ohci->eds_scheduled++))
mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
wmb (); wmb ();
/* we care about rm_list when setting CLE/BLE in case the HC was at /* we care about rm_list when setting CLE/BLE in case the HC was at
...@@ -977,19 +973,13 @@ finish_unlinks (struct ohci_hcd *ohci, u16 tick) ...@@ -977,19 +973,13 @@ finish_unlinks (struct ohci_hcd *ohci, u16 tick)
TD_MASK; TD_MASK;
/* INTR_WDH may need to clean up first */ /* INTR_WDH may need to clean up first */
if (td->td_dma != head) { if (td->td_dma != head)
if (ed == ohci->ed_to_check) goto skip_ed;
ohci->ed_to_check = NULL;
else
goto skip_ed;
}
} }
} }
/* ED's now officially unlinked, hc doesn't see */ /* ED's now officially unlinked, hc doesn't see */
ed->state = ED_IDLE; ed->state = ED_IDLE;
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
ohci->eds_scheduled--;
ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
ed->hwNextED = 0; ed->hwNextED = 0;
wmb(); wmb();
...@@ -1122,12 +1112,7 @@ finish_unlinks (struct ohci_hcd *ohci, u16 tick) ...@@ -1122,12 +1112,7 @@ finish_unlinks (struct ohci_hcd *ohci, u16 tick)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /* Take back a TD from the host controller */
* Used to take back a TD from the host controller. This would normally be
* called from within dl_done_list, however it may be called directly if the
* HC no longer sees the TD and it has not appeared on the donelist (after
* two frames). This bug has been observed on ZF Micro systems.
*/
static void takeback_td(struct ohci_hcd *ohci, struct td *td) static void takeback_td(struct ohci_hcd *ohci, struct td *td)
{ {
struct urb *urb = td->urb; struct urb *urb = td->urb;
...@@ -1174,9 +1159,7 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) ...@@ -1174,9 +1159,7 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td)
* *
* This is the main path for handing urbs back to drivers. The only other * This is the main path for handing urbs back to drivers. The only other
* normal path is finish_unlinks(), which unlinks URBs using ed_rm_list, * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
* instead of scanning the (re-reversed) donelist as this does. There's * instead of scanning the (re-reversed) donelist as this does.
* an abnormal path too, handling a quirk in some Compaq silicon: URBs
* with TDs that appear to be orphaned are directly reclaimed.
*/ */
static void static void
dl_done_list (struct ohci_hcd *ohci) dl_done_list (struct ohci_hcd *ohci)
......
...@@ -411,12 +411,6 @@ struct ohci_hcd { ...@@ -411,12 +411,6 @@ struct ohci_hcd {
struct work_struct nec_work; /* Worker for NEC quirk */ struct work_struct nec_work; /* Worker for NEC quirk */
/* Needed for ZF Micro quirk */
struct timer_list unlink_watchdog;
unsigned eds_scheduled;
struct ed *ed_to_check;
unsigned zf_delay;
struct dentry *debug_dir; struct dentry *debug_dir;
struct dentry *debug_async; struct dentry *debug_async;
struct dentry *debug_periodic; struct dentry *debug_periodic;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment