USB: OHCI: revert the ZF Micro orphan-TD quirk
This patch reverts the important parts of commit 89a0fd18a9
(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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
a40178b2fa
Коммит
95d9a01d72
|
@ -355,8 +355,6 @@ rescan:
|
|||
if (ohci->rh_state != OHCI_RH_RUNNING) {
|
||||
sanitize:
|
||||
ed->state = ED_IDLE;
|
||||
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
|
||||
ohci->eds_scheduled--;
|
||||
finish_unlinks (ohci, 0);
|
||||
}
|
||||
|
||||
|
@ -365,11 +363,6 @@ sanitize:
|
|||
/* major IRQ delivery trouble loses INTR_SF too... */
|
||||
if (limit-- == 0) {
|
||||
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;
|
||||
}
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
|
@ -431,93 +424,6 @@ ohci_shutdown (struct usb_hcd *hcd)
|
|||
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
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
@ -761,15 +667,6 @@ retry:
|
|||
// POTPGT delay is bits 24-31, in 2 ms units.
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
@ -895,31 +792,6 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
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 */
|
||||
|
||||
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
|
||||
|
@ -928,9 +800,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
spin_lock (&ohci->lock);
|
||||
if (ohci->ed_rm_list)
|
||||
finish_unlinks (ohci, ohci_frame_no(ohci));
|
||||
if ((ints & OHCI_INTR_SF) != 0
|
||||
&& !ohci->ed_rm_list
|
||||
&& !ohci->ed_to_check
|
||||
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
|
||||
&& ohci->rh_state == OHCI_RH_RUNNING)
|
||||
ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);
|
||||
spin_unlock (&ohci->lock);
|
||||
|
@ -961,8 +831,6 @@ static void ohci_stop (struct usb_hcd *hcd)
|
|||
free_irq(hcd->irq, hcd);
|
||||
hcd->irq = 0;
|
||||
|
||||
if (quirk_zfmicro(ohci))
|
||||
del_timer(&ohci->unlink_watchdog);
|
||||
if (quirk_amdiso(ohci))
|
||||
usb_amd_dev_put();
|
||||
|
||||
|
|
|
@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
|
|||
ed->ed_prev = NULL;
|
||||
ed->ed_next = NULL;
|
||||
ed->hwNextED = 0;
|
||||
if (quirk_zfmicro(ohci)
|
||||
&& (ed->type == PIPE_INTERRUPT)
|
||||
&& !(ohci->eds_scheduled++))
|
||||
mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
|
||||
wmb ();
|
||||
|
||||
/* we care about rm_list when setting CLE/BLE in case the HC was at
|
||||
|
@ -977,19 +973,13 @@ skip_ed:
|
|||
TD_MASK;
|
||||
|
||||
/* INTR_WDH may need to clean up first */
|
||||
if (td->td_dma != head) {
|
||||
if (ed == ohci->ed_to_check)
|
||||
ohci->ed_to_check = NULL;
|
||||
else
|
||||
goto skip_ed;
|
||||
}
|
||||
if (td->td_dma != head)
|
||||
goto skip_ed;
|
||||
}
|
||||
}
|
||||
|
||||
/* ED's now officially unlinked, hc doesn't see */
|
||||
ed->state = ED_IDLE;
|
||||
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
|
||||
ohci->eds_scheduled--;
|
||||
ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
|
||||
ed->hwNextED = 0;
|
||||
wmb();
|
||||
|
@ -1122,12 +1112,7 @@ rescan_this:
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* Take back a TD from the host controller */
|
||||
static void takeback_td(struct ohci_hcd *ohci, struct td *td)
|
||||
{
|
||||
struct urb *urb = td->urb;
|
||||
|
@ -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
|
||||
* normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
|
||||
* instead of scanning the (re-reversed) donelist as this does. There's
|
||||
* an abnormal path too, handling a quirk in some Compaq silicon: URBs
|
||||
* with TDs that appear to be orphaned are directly reclaimed.
|
||||
* instead of scanning the (re-reversed) donelist as this does.
|
||||
*/
|
||||
static void
|
||||
dl_done_list (struct ohci_hcd *ohci)
|
||||
|
|
|
@ -411,12 +411,6 @@ struct ohci_hcd {
|
|||
|
||||
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_async;
|
||||
struct dentry *debug_periodic;
|
||||
|
|
Загрузка…
Ссылка в новой задаче