* [ (C) Copyright 1999 Gregory P. Smith]
*
*
+ * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
+ * interfaces (though some non-x86 Intel chips use it). It supports
+ * smarter hardware than UHCI. A download link for the spec available
+ * through the http://www.usb.org website.
+ *
* History:
*
+ * 2002/07/19 fixes to management of ED and schedule state.
+ * 2002/06/09 SA-1111 support (Christopher Hoover)
* 2002/06/01 remember frame when HC won't see EDs any more; use that info
* to fix urb unlink races caused by interrupt latency assumptions;
* minor ED field and function naming updates
/*
* TO DO:
*
- * - "disabled" should be the hcd state
+ * - "disabled" and "sleeping" should be in hcd->state
* - bandwidth alloc to generic code
* - lots more testing!!
*/
-#define DRIVER_VERSION "2002-Jun-15"
+#define DRIVER_VERSION "2002-Jul-19"
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
int i, size = 0;
unsigned long flags;
int bustime = 0;
+ int retval = 0;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "SUB", usb_pipein (pipe));
return -ENOMEM;
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* don't submit to a dead HC */
+ if (ohci->disabled || ohci->sleeping) {
+ retval = -ENODEV;
+ goto fail;
+ }
+
/* fill the private part of the URB */
urb_priv->length = size;
urb_priv->ed = ed;
/* allocate the TDs (updating hash chains) */
- spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < size; i++) {
urb_priv->td [i] = td_alloc (ohci, SLAB_ATOMIC);
if (!urb_priv->td [i]) {
urb_priv->length = i;
- urb_free_priv (ohci, urb_priv);
- spin_unlock_irqrestore (&ohci->lock, flags);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto fail;
}
}
switch (usb_pipetype (pipe)) {
case PIPE_ISOCHRONOUS:
if (urb->transfer_flags & USB_ISO_ASAP) {
- urb->start_frame = ( (ed->state == ED_OPER)
+ urb->start_frame = ((ed->state != ED_IDLE)
? (ed->intriso.last_iso + 1)
: (le16_to_cpu (ohci->hcca->frame_no)
+ 10)) & 0xffff;
- }
+ }
/* FALLTHROUGH */
case PIPE_INTERRUPT:
if (urb->bandwidth == 0) {
urb->hcpriv = urb_priv;
- /* link the ed into a chain if is not already */
- if (ed->state != ED_OPER)
- ep_link (ohci, ed);
+ /* schedule the ed if needed */
+ if (ed->state == ED_IDLE)
+ ed_schedule (ohci, ed);
/* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed
*/
td_submit_urb (urb);
+fail:
+ if (retval)
+ urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&ohci->lock, flags);
-
- return 0;
+ return retval;
}
/*
if (!ohci->disabled) {
urb_priv_t *urb_priv;
- /* flag the urb's data for deletion in some upcoming
- * SF interrupt's delete list processing
+ /* Unless an IRQ completed the unlink while it was being
+ * handed to us, flag it for unlink and giveback, and force
+ * some upcoming INTR_SF to call finish_unlinks()
*/
spin_lock_irqsave (&ohci->lock, flags);
urb_priv = urb->hcpriv;
-
- if (!urb_priv || (urb_priv->state == URB_DEL)) {
- spin_unlock_irqrestore (&ohci->lock, flags);
- return 0;
+ if (urb_priv) {
+ urb_priv->state = URB_DEL;
+ if (urb_priv->ed->state == ED_OPER)
+ start_urb_unlink (ohci, urb_priv->ed);
}
-
- urb_priv->state = URB_DEL;
- start_urb_unlink (ohci, urb_priv->ed);
spin_unlock_irqrestore (&ohci->lock, flags);
} else {
/*
* any more ... just clean up every urb's memory.
*/
finish_urb (ohci, urb);
- }
+ }
return 0;
}
/*-------------------------------------------------------------------------*/
+/* frees config/altsetting state for endpoints,
+ * including ED memory, dummy TD, and bulk/intr data toggle
+ */
+
static void
ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
{
struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;
int i;
unsigned long flags;
+#ifdef DEBUG
+ int rescans = 0;
+#endif
+rescan:
/* free any eds, and dummy tds, still hanging around */
spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < 32; i++) {
if (!ed)
continue;
- ed->state &= ~ED_URB_DEL;
- if (ohci->disabled && ed->state == ED_OPER)
- ed->state = ED_UNLINK;
+ if (ohci->disabled && ed->state != ED_IDLE)
+ ed->state = ED_IDLE;
switch (ed->state) {
- case ED_NEW:
- break;
- case ED_UNLINK:
+ case ED_UNLINK: /* wait a frame? */
+ goto do_rescan;
+ case ED_IDLE: /* fully unlinked */
td_free (ohci, ed->dummy);
break;
-
- case ED_OPER:
default:
+#ifdef DEBUG
err ("illegal ED %d state in free_config, %d",
i, ed->state);
-#ifdef DEBUG
- BUG ();
#endif
+ /* ED_OPER: some driver disconnect() is broken,
+ * it didn't even start its unlinks much less wait
+ * for their completions.
+ * OTHERWISE: hcd bug, ed is garbage
+ */
+ BUG ();
}
ed_free (ohci, ed);
}
spin_unlock_irqrestore (&ohci->lock, flags);
+ return;
+
+do_rescan:
+#ifdef DEBUG
+ /* a driver->disconnect() returned before its unlinks completed? */
+ if (in_interrupt ()) {
+ dbg ("WARNING: spin in interrupt; driver->disconnect() bug");
+ dbg ("dev usb-%s-%s ep 0x%x",
+ ohci->hcd.self.bus_name, udev->devpath, i);
+ }
+ BUG_ON (!(readl (&ohci->regs->intrenable) & OHCI_INTR_SF));
+ BUG_ON (rescans >= 2); /* HWBUG */
+ rescans++;
+#endif
+
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ wait_ms (1);
+ goto rescan;
}
static int ohci_get_frame (struct usb_hcd *hcd)
/* link an ed into one of the HC chains */
-static int ep_link (struct ohci_hcd *ohci, struct ed *edi)
+static void ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
{
int int_branch, i;
int inter, interval, load;
__u32 *ed_p;
- volatile struct ed *ed = edi;
ed->state = ED_OPER;
+ ed->hwNextED = 0;
+ wmb ();
+
+ /* we care about rm_list when setting CLE/BLE in case the HC was at
+ * work on some TD when CLE/BLE was turned off, and isn't quiesced
+ * yet. finish_unlinks() restarts as needed, some upcoming INTR_SF.
+ */
switch (ed->type) {
case PIPE_CONTROL:
- ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
writel (ed->dma, &ohci->regs->ed_controlhead);
} else {
ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
}
ed->ed_prev = ohci->ed_controltail;
- if (!ohci->ed_controltail
- && !ohci->ed_rm_list
- && !ohci->sleeping
- ) {
+ if (!ohci->ed_controltail && !ohci->ed_rm_list) {
ohci->hc_control |= OHCI_CTRL_CLE;
+ writel (0, &ohci->regs->ed_controlcurrent);
writel (ohci->hc_control, &ohci->regs->control);
}
- ohci->ed_controltail = edi;
+ ohci->ed_controltail = ed;
break;
case PIPE_BULK:
- ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
writel (ed->dma, &ohci->regs->ed_bulkhead);
} else {
ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
}
ed->ed_prev = ohci->ed_bulktail;
- if (!ohci->ed_bulktail
- && !ohci->ed_rm_list
- && !ohci->sleeping
- ) {
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list) {
ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (0, &ohci->regs->ed_bulkcurrent);
writel (ohci->hc_control, &ohci->regs->control);
}
- ohci->ed_bulktail = edi;
+ ohci->ed_bulktail = ed;
break;
case PIPE_INTERRUPT:
ed->hwNextED = *ed_p;
*ed_p = cpu_to_le32 (ed->dma);
}
+ wmb ();
#ifdef OHCI_VERBOSE_DEBUG
ohci_dump_periodic (ohci, "LINK_INT");
#endif
break;
case PIPE_ISOCHRONOUS:
- ed->hwNextED = 0;
- ed->interval = 1;
+ ed->ed_prev = ohci->ed_isotail;
if (ohci->ed_isotail != NULL) {
ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
- ed->ed_prev = ohci->ed_isotail;
} else {
for ( i = 0; i < NUM_INTS; i += inter) {
inter = 1;
inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->interval);
*ed_p = cpu_to_le32 (ed->dma);
}
- ed->ed_prev = NULL;
}
- ohci->ed_isotail = edi;
+ wmb ();
+ ohci->ed_isotail = ed;
#ifdef OHCI_VERBOSE_DEBUG
ohci_dump_periodic (ohci, "LINK_ISO");
#endif
break;
}
- return 0;
+
+ /* the HC may not see the schedule updates yet, but if it does
+ * then they'll be properly ordered.
+ */
}
/*-------------------------------------------------------------------------*/
* just the link to the ed is unlinked.
* the link from the ed still points to another operational ed or 0
* so the HC can eventually finish the processing of the unlinked ed
- * caller guarantees the ED has no active TDs.
*/
-static int start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
+static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed)
{
int i;
break;
}
- /* FIXME ED's "unlink" state is indeterminate;
- * the HC might still be caching it (till SOF).
- * - use ed_rm_list and finish_unlinks(), adding some state that
- * prevents clobbering hw linkage before the appropriate SOF
- * - a speedup: when only one urb is queued on the ed, save 1msec
- * by making start_urb_unlink() use this routine to deschedule.
+ /* FIXME Except for a couple of exceptionally clean unlink cases
+ * (like unlinking the only c/b ED, with no TDs) HCs may still be
+ * caching this (till SOF).
+ *
+ * To avoid racing with the hardware, this needs to use ED_UNLINK
+ * and delay til next INTR_SF. Merge with start_urb_unlink().
*/
- ed->state = ED_UNLINK;
- return 0;
+ ed->state = ED_IDLE;
}
spin_lock_irqsave (&ohci->lock, flags);
if (!(ed = dev->ep [ep])) {
+ struct td *td;
+
ed = ed_alloc (ohci, SLAB_ATOMIC);
if (!ed) {
/* out of memory */
goto done;
}
dev->ep [ep] = ed;
- }
-
- if (ed->state & ED_URB_DEL) {
- /* pending unlink request */
- ed = 0;
- goto done;
- }
-
- if (ed->state == ED_NEW) {
- struct td *td;
- ed->hwINFO = ED_SKIP;
/* dummy td; end of td list for ed */
td = td_alloc (ohci, SLAB_ATOMIC);
if (!td) {
/* out of memory */
+ ed_free (ohci, ed);
ed = 0;
goto done;
}
ed->dummy = td;
ed->hwTailP = cpu_to_le32 (td->td_dma);
ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
- ed->state = ED_UNLINK;
+ ed->state = ED_IDLE;
ed->type = type;
}
* state/mode info. Currently the upper layers don't support such
* guarantees; we're lucky changing config/altsetting is rare.
*/
- if (ed->state == ED_UNLINK) {
+ if (ed->state == ED_IDLE) {
u32 info;
info = usb_pipedevice (pipe);
/*-------------------------------------------------------------------------*/
/* request unlinking of an endpoint from an operational HC.
- * put the ep on the rm_list and stop the bulk or ctrl list
+ * put the ep on the rm_list
* real work is done at the next start frame (SF) hardware interrupt
*/
static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
{
- /* already pending? */
- if (ed->state & ED_URB_DEL)
- return;
- ed->state |= ED_URB_DEL;
-
- ed->hwINFO |= ED_SKIP;
-
- switch (ed->type) {
- case PIPE_CONTROL: /* stop control list */
- ohci->hc_control &= ~OHCI_CTRL_CLE;
- writel (ohci->hc_control,
- &ohci->regs->control);
- break;
- case PIPE_BULK: /* stop bulk list */
- ohci->hc_control &= ~OHCI_CTRL_BLE;
- writel (ohci->hc_control,
- &ohci->regs->control);
- break;
- }
+ ed_deschedule (ohci, ed);
+ ed->state = ED_UNLINK;
/* SF interrupt might get delayed; record the frame counter value that
* indicates when the HC isn't looking at it, so concurrent unlinks
*/
ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
- ed->ed_rm_list = ohci->ed_rm_list;
+ ed->ed_next = ohci->ed_rm_list;
ohci->ed_rm_list = ed;
/* enable SOF interrupt */
u32 tdINFO = le32_to_cpup (&td->hwINFO);
int cc = 0;
-
/* ISO ... drivers see per-TD length/status */
if (tdINFO & TD_ISO) {
u16 tdPSW = le16_to_cpu (td->hwPSW [0]);
int dlen = 0;
cc = (tdPSW >> 12) & 0xF;
+ if (cc >= 0x0E) /* hc didn't touch? */
+ return;
+
if (usb_pipeout (urb->pipe))
dlen = urb->iso_frame_desc [td->index].length;
else
urb->iso_frame_desc [td->index].actual_length = dlen;
urb->iso_frame_desc [td->index].status = cc_to_error [cc];
- if (cc != 0)
+#ifdef VERBOSE_DEBUG
+ if (cc != TD_CC_NOERROR)
dbg (" urb %p iso TD %p (%d) len %d CC %d",
urb, td, 1 + td->index, dlen, cc);
+#endif
/* BULK, INT, CONTROL ... drivers see aggregate length/status,
* except that "setup" bytes aren't counted and "short" transfers
if (cc == TD_DATAUNDERRUN
&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
cc = TD_CC_NOERROR;
- if (cc != TD_CC_NOERROR) {
+ if (cc != TD_CC_NOERROR && cc < 0x0E) {
spin_lock (&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = cc_to_error [cc];
}
#ifdef VERBOSE_DEBUG
- if (cc != 0)
+ if (cc != TD_CC_NOERROR && cc < 0x0E)
dbg (" urb %p TD %p (%d) CC %d, len=%d/%d",
urb, td, 1 + td->index, cc,
urb->actual_length,
static void finish_unlinks (struct ohci_hcd *ohci, u16 tick)
{
struct ed *ed, **last;
- int ctrl = 0, bulk = 0;
+rescan_all:
for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
struct td *td, *td_next, *tdHeadP, *tdTailP;
u32 *td_p;
- int unlinked;
+ int completed, modified;
/* only take off EDs that the HC isn't using, accounting for
- * frame counter wraps. completion callbacks might prepend
- * EDs to the list, they'll be checked next irq.
+ * frame counter wraps.
*/
- if (tick_before (tick, ed->tick)) {
- last = &ed->ed_rm_list;
+ if (tick_before (tick, ed->tick) && !ohci->disabled) {
+ last = &ed->ed_next;
continue;
}
- *last = ed->ed_rm_list;
- ed->ed_rm_list = 0;
- unlinked = 0;
- /* unlink urbs from first one requested to queue end;
- * leave earlier urbs alone
+ /* reentrancy: if we drop the schedule lock, someone might
+ * have modified this list. normally it's just prepending
+ * entries (which we'd ignore), but paranoia won't hurt.
*/
+ *last = ed->ed_next;
+ ed->ed_next = 0;
+ modified = 0;
+
+ /* unlink urbs as requested, but rescan the list after
+ * we call a completion since it might have unlinked
+ * another (earlier) urb
+ *
+ * FIXME use td_list to scan, not ed hashtables.
+ * completely abolish ed hashtables!
+ */
+rescan_this:
+ completed = 0;
+
tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP));
tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
td_p = &ed->hwHeadP;
td_next = dma_to_td (ohci,
le32_to_cpup (&td->hwNextTD));
- if (unlinked || (urb_priv->state == URB_DEL)) {
- u32 tdINFO = le32_to_cpup (&td->hwINFO);
-
- unlinked = 1;
+ if (urb_priv->state == URB_DEL) {
/* HC may have partly processed this TD */
- if (TD_CC_GET (tdINFO) < 0xE)
- td_done (urb, td);
+ td_done (urb, td);
+ urb_priv->td_cnt++;
+
*td_p = td->hwNextTD | (*td_p
& __constant_cpu_to_le32 (0x3));
/* URB is done; clean up */
- if (++ (urb_priv->td_cnt) == urb_priv->length) {
- if (urb->status == -EINPROGRESS)
- urb->status = -ECONNRESET;
+ if (urb_priv->td_cnt == urb_priv->length) {
+ modified = completed = 1;
spin_unlock (&ohci->lock);
finish_urb (ohci, urb);
spin_lock (&ohci->lock);
}
}
- /* FIXME actually want four cases here:
- * (a) finishing URB unlink
- * [a1] no URBs queued, so start ED unlink
- * [a2] some (earlier) URBs still linked, re-enable
- * (b) finishing ED unlink
- * [b1] no URBs queued, ED is truly idle now
- * ... we could set state ED_NEW and free dummy
- * [b2] URBs now queued, link ED back into schedule
- * right now we only have (a)
- */
- ed->state &= ~ED_URB_DEL;
- tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
-
- if (tdHeadP == tdTailP) {
- if (ed->state == ED_OPER)
- start_ed_unlink (ohci, ed);
- } else
- ed->hwINFO &= ~ED_SKIP;
+ /* ED's now officially unlinked, hc doesn't see */
+ ed->state = ED_IDLE;
+ ed->hwINFO &= ~ED_SKIP;
+ ed->hwHeadP &= ~cpu_to_le32 (ED_H);
+ ed->hwNextED = 0;
- switch (ed->type) {
- case PIPE_CONTROL:
- ctrl = 1;
- break;
- case PIPE_BULK:
- bulk = 1;
- break;
+ /* but if there's work queued, reschedule */
+ tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
+ if (tdHeadP != tdTailP) {
+ if (completed)
+ goto rescan_this;
+ if (!ohci->disabled && !ohci->sleeping)
+ ed_schedule (ohci, ed);
}
+
+ if (modified)
+ goto rescan_all;
}
/* maybe reenable control and bulk lists */
- if (!ohci->disabled) {
- if (ctrl) /* reset control list */
- writel (0, &ohci->regs->ed_controlcurrent);
- if (bulk) /* reset bulk list */
- writel (0, &ohci->regs->ed_bulkcurrent);
- if (!ohci->ed_rm_list) {
- if (ohci->ed_controltail)
- ohci->hc_control |= OHCI_CTRL_CLE;
- if (ohci->ed_bulktail)
- ohci->hc_control |= OHCI_CTRL_BLE;
- writel (ohci->hc_control, &ohci->regs->control);
+ if (!ohci->disabled && !ohci->ed_rm_list) {
+ u32 command = 0, control = 0;
+
+ if (ohci->ed_controltail) {
+ command |= OHCI_CLF;
+ if (!(ohci->hc_control & OHCI_CTRL_CLE)) {
+ control |= OHCI_CTRL_CLE;
+ writel (0, &ohci->regs->ed_controlcurrent);
+ }
}
- }
+ if (ohci->ed_bulktail) {
+ command |= OHCI_BLF;
+ if (!(ohci->hc_control & OHCI_CTRL_BLE)) {
+ control |= OHCI_CTRL_BLE;
+ writel (0, &ohci->regs->ed_bulkcurrent);
+ }
+ }
+
+ /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */
+ if (control) {
+ ohci->hc_control |= control;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ if (command)
+ writel (command, &ohci->regs->cmdstatus);
+ }
}
if ((ed->hwHeadP & __constant_cpu_to_le32 (TD_MASK))
== ed->hwTailP
&& (ed->state == ED_OPER))
- start_ed_unlink (ohci, ed);
+ ed_deschedule (ohci, ed);
td = td_next;
}
spin_unlock_irqrestore (&ohci->lock, flags);