static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
#include "ohci.h"
#include "pci-quirks.h"
static void ohci_dump(struct ohci_hcd *ohci);
static void ohci_stop(struct usb_hcd *hcd);
+static void io_watchdog_func(unsigned long _ohci);
#include "ohci-hub.c"
#include "ohci-dbg.c"
usb_hcd_unlink_urb_from_ep(hcd, urb);
goto fail;
}
+
+ /* Start up the I/O watchdog timer, if it's not running */
+ if (!timer_pending(&ohci->io_watchdog) &&
+ list_empty(&ohci->eds_in_use))
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ list_add(&ed->in_use_list, &ohci->eds_in_use);
+
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = ohci_frame_no(ohci);
udelay(10);
ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval);
+ ohci->rh_state = OHCI_RH_HALTED;
}
/*-------------------------------------------------------------------------*
if (ohci->hcca)
return 0;
+ setup_timer(&ohci->io_watchdog, io_watchdog_func,
+ (unsigned long) ohci);
+ set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20));
+
ohci->hcca = dma_alloc_coherent (hcd->self.controller,
sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
if (!ohci->hcca)
/*-------------------------------------------------------------------------*/
+/*
+ * Some OHCI controllers are known to lose track of completed TDs. They
+ * don't add the TDs to the hardware done queue, which means we never see
+ * them as being completed.
+ *
+ * This watchdog routine checks for such problems. Without some way to
+ * tell when those TDs have completed, we would never take their EDs off
+ * the unlink list. As a result, URBs could never be dequeued and
+ * endpoints could never be released.
+ */
+static void io_watchdog_func(unsigned long _ohci)
+{
+ struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
+ bool takeback_all_pending = false;
+ u32 status;
+ u32 head;
+ struct ed *ed;
+ struct td *td, *td_start, *td_next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ /*
+ * One way to lose track of completed TDs is if the controller
+ * never writes back the done queue head. If it hasn't been
+ * written back since the last time this function ran and if it
+ * was non-empty at that time, something is badly wrong with the
+ * hardware.
+ */
+ status = ohci_readl(ohci, &ohci->regs->intrstatus);
+ if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) {
+ if (ohci->prev_donehead) {
+ ohci_err(ohci, "HcDoneHead not written back; disabled\n");
+ usb_hc_died(ohci_to_hcd(ohci));
+ ohci_dump(ohci);
+ ohci_shutdown(ohci_to_hcd(ohci));
+ goto done;
+ } else {
+ /* No write back because the done queue was empty */
+ takeback_all_pending = true;
+ }
+ }
+
+ /* Check every ED which might have pending TDs */
+ list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) {
+ if (ed->pending_td) {
+ if (takeback_all_pending ||
+ OKAY_TO_TAKEBACK(ohci, ed)) {
+ unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO);
+
+ ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n",
+ 0x007f & tmp,
+ (0x000f & (tmp >> 7)) +
+ ((tmp & ED_IN) >> 5));
+ add_to_done_list(ohci, ed->pending_td);
+ }
+ }
+
+ /* Starting from the latest pending TD, */
+ td = ed->pending_td;
+
+ /* or the last TD on the done list, */
+ if (!td) {
+ list_for_each_entry(td_next, &ed->td_list, td_list) {
+ if (!td_next->next_dl_td)
+ break;
+ td = td_next;
+ }
+ }
+
+ /* find the last TD processed by the controller. */
+ head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK;
+ td_start = td;
+ td_next = list_prepare_entry(td, &ed->td_list, td_list);
+ list_for_each_entry_continue(td_next, &ed->td_list, td_list) {
+ if (head == (u32) td_next->td_dma)
+ break;
+ td = td_next; /* head pointer has passed this TD */
+ }
+ if (td != td_start) {
+ /*
+ * In case a WDH cycle is in progress, we will wait
+ * for the next two cycles to complete before assuming
+ * this TD will never get on the done queue.
+ */
+ ed->takeback_wdh_cnt = ohci->wdh_cnt + 2;
+ ed->pending_td = td;
+ }
+ }
+
+ ohci_work(ohci);
+
+ if (ohci->rh_state == OHCI_RH_RUNNING) {
+ if (!list_empty(&ohci->eds_in_use)) {
+ ohci->prev_wdh_cnt = ohci->wdh_cnt;
+ ohci->prev_donehead = ohci_readl(ohci,
+ &ohci->regs->donehead);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ }
+
+ done:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+}
+
/* an interrupt happens */
static irqreturn_t ohci_irq (struct usb_hcd *hcd)
if (ohci->rh_state == OHCI_RH_RUNNING) {
ohci_writel (ohci, ints, ®s->intrstatus);
+ if (ints & OHCI_INTR_WDH)
+ ++ohci->wdh_cnt;
+
ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
+ del_timer_sync(&ohci->io_watchdog);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset(ohci);
struct ed *ed_next; /* on schedule or rm_list */
struct ed *ed_prev; /* for non-interrupt EDs */
struct list_head td_list; /* "shadow list" of our TDs */
+ struct list_head in_use_list;
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
+
+ /* Detect TDs not added to the done queue */
+ unsigned takeback_wdh_cnt;
+ struct td *pending_td;
+#define OKAY_TO_TAKEBACK(ohci, ed) \
+ ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0)
+
} __attribute__ ((aligned(16)));
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
struct td *td_hash [TD_HASH_SIZE];
struct td *dl_start, *dl_end; /* the done list */
struct list_head pending;
+ struct list_head eds_in_use; /* all EDs with at least 1 TD */
/*
* driver state
// there are also chip quirks/bugs in init logic
+ unsigned wdh_cnt, prev_wdh_cnt;
+ u32 prev_donehead;
+ struct timer_list io_watchdog;
+
struct work_struct nec_work; /* Worker for NEC quirk */
struct dentry *debug_dir;