/*
- * OHCI HCD (Host Controller Driver) for USB.
+ * ADM5120 HCD (Host Controller Driver) for USB
*
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org>
+ *
+ * This file was derived from: drivers/usb/host/ohci-q.c
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
*
- * This file is licenced under the GPL.
*/
#include <linux/irq.h>
&& urb->status == 0) {
urb->status = -EREMOTEIO;
#ifdef ADMHC_VERBOSE_DEBUG
- urb_print(urb, "SHORT", usb_pipeout (urb->pipe));
+ urb_print(ahcd, urb, "SHORT", usb_pipeout(urb->pipe));
#endif
}
spin_unlock(&urb->lock);
}
#ifdef ADMHC_VERBOSE_DEBUG
- urb_print(urb, "RET", usb_pipeout (urb->pipe));
+ urb_print(ahcd, urb, "RET", usb_pipeout (urb->pipe));
#endif
/* urb->complete() can reenter this HCD */
unsigned i;
admhc_vdbg (ahcd, "link %sed %p branch %d [%dus.], interval %d\n",
- (ed->hwINFO & cpu_to_hc32 (ahcd, ED_ISO)) ? "iso " : "",
+ (ed->hwINFO & cpu_to_hc32(ahcd, ED_ISO)) ? "iso " : "",
ed, ed->branch, ed->load, ed->interval);
for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
admhcd_to_hcd(ahcd)->self.bandwidth_allocated -= ed->load / ed->interval;
admhc_vdbg (ahcd, "unlink %sed %p branch %d [%dus.], interval %d\n",
- (ed->hwINFO & cpu_to_hc32 (ahcd, ED_ISO)) ? "iso " : "",
+ (ed->hwINFO & cpu_to_hc32(ahcd, ED_ISO)) ? "iso " : "",
ed, ed->branch, ed->load, ed->interval);
}
#endif
*/
static void ed_deschedule(struct admhcd *ahcd, struct ed *ed)
{
+
+#ifdef ADMHC_VERBOSE_DEBUG
+ admhc_dump_ed(ahcd, "ED-DESCHED", ed, 1);
+#endif
+
ed->hwINFO |= cpu_to_hc32(ahcd, ED_SKIP);
wmb();
ed->state = ED_UNLINK;
*/
static void start_ed_unlink(struct admhcd *ahcd, struct ed *ed)
{
- ed->hwINFO |= cpu_to_hc32 (ahcd, ED_DEQUEUE);
+
+#ifdef ADMHC_VERBOSE_DEBUG
+ admhc_dump_ed(ahcd, "ED-UNLINK", ed, 1);
+#endif
+
+ ed->hwINFO |= cpu_to_hc32(ahcd, ED_DEQUEUE);
ed_deschedule(ahcd, ed);
/* add this ED into the remove list */
int cnt = 0;
u32 info = 0;
int is_out = usb_pipeout(urb->pipe);
- int periodic = 0;
u32 toggle = 0;
- struct td *td;
/* OHCI handles the bulk/interrupt data toggles itself. We just
* use the device toggle bits for resetting, and rely on the fact
* we could often reduce the number of TDs here.
*/
case PIPE_ISOCHRONOUS:
- info = TD_SCC_NOTACCESSED;
+ info = is_out
+ ? TD_T_CARRY | TD_SCC_NOTACCESSED | TD_DP_OUT
+ : TD_T_CARRY | TD_SCC_NOTACCESSED | TD_DP_IN;
+
for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
int frame = urb->start_frame;
static int td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
{
struct urb_priv *urb_priv = urb->hcpriv;
- u32 info = hc32_to_cpup(ahcd, &td->hwINFO);
+ u32 info;
+ u32 bl;
+ u32 tdDBP;
int type = usb_pipetype(urb->pipe);
int cc;
+ info = hc32_to_cpup(ahcd, &td->hwINFO);
+ tdDBP = hc32_to_cpup(ahcd, &td->hwDBP);
+ bl = TD_BL_GET(hc32_to_cpup(ahcd, &td->hwCBL));
cc = TD_CC_GET(info);
/* ISO ... drivers see per-TD length/status */
if (type == PIPE_ISOCHRONOUS) {
-#if 0
/* TODO */
int dlen = 0;
/* NOTE: assumes FC in tdINFO == 0, and that
* only the first of 0..MAXPSW psws is used.
*/
-
- cc = TD_CC_GET(td);
+#if 0
if (tdINFO & TD_CC) /* hc didn't touch? */
return;
-
- if (usb_pipeout (urb->pipe))
- dlen = urb->iso_frame_desc [td->index].length;
+#endif
+ if (usb_pipeout(urb->pipe))
+ dlen = urb->iso_frame_desc[td->index].length;
else {
/* short reads are always OK for ISO */
- if (cc == TD_DATAUNDERRUN)
+ if (cc == TD_CC_DATAUNDERRUN)
cc = TD_CC_NOERROR;
- dlen = tdPSW & 0x3ff;
+ dlen = tdDBP - td->data_dma + bl;
}
+
urb->actual_length += dlen;
- urb->iso_frame_desc [td->index].actual_length = dlen;
- urb->iso_frame_desc [td->index].status = cc_to_error [cc];
+ urb->iso_frame_desc[td->index].actual_length = dlen;
+ urb->iso_frame_desc[td->index].status = cc_to_error[cc];
if (cc != TD_CC_NOERROR)
admhc_vdbg (ahcd,
"urb %p iso td %p (%d) len %d cc %d\n",
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
* might not be reported as errors.
*/
} else {
- u32 bl = TD_BL_GET(hc32_to_cpup(ahcd, &td->hwCBL));
- u32 tdDBP = hc32_to_cpup(ahcd, &td->hwDBP);
-
/* update packet status if needed (short is normally ok) */
if (cc == TD_CC_DATAUNDERRUN
&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
struct urb *urb = td->urb;
struct ed *ed = td->ed;
struct list_head *tmp = td->td_list.next;
- __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ahcd, ED_C);
+ __hc32 toggle = ed->hwHeadP & cpu_to_hc32(ahcd, ED_C);
admhc_dump_ed(ahcd, "ed halted", td->ed, 1);
/* clear ed halt; this is the td that caused it, but keep it inactive
* until its urb->complete() has a chance to clean up.
*/
- ed->hwINFO |= cpu_to_hc32 (ahcd, ED_SKIP);
+ ed->hwINFO |= cpu_to_hc32(ahcd, ED_SKIP);
wmb();
- ed->hwHeadP &= ~cpu_to_hc32 (ahcd, ED_H);
+ ed->hwHeadP &= ~cpu_to_hc32(ahcd, ED_H);
/* put any later tds from this urb onto the donelist, after 'td',
* order won't matter here: no errors, and nothing was transferred.
*/
info = next->hwINFO;
#if 0 /* FIXME */
- info |= cpu_to_hc32 (ahcd, TD_DONE);
+ info |= cpu_to_hc32(ahcd, TD_DONE);
#endif
- info &= ~cpu_to_hc32 (ahcd, TD_CC);
+ info &= ~cpu_to_hc32(ahcd, TD_CC);
next->hwINFO = info;
next->next_dl_td = rev;
last = &ed->ed_rm_next;
continue;
}
-
- if (!list_empty (&ed->td_list)) {
+#if 0
+ if (!list_empty(&ed->td_list)) {
struct td *td;
u32 head;
if (td->td_dma != head)
goto skip_ed;
}
+#endif
}
/* reentrancy: if we drop the schedule lock, someone might
rescan_this:
completed = 0;
prev = &ed->hwHeadP;
- list_for_each_safe (entry, tmp, &ed->td_list) {
+ list_for_each_safe(entry, tmp, &ed->td_list) {
struct td *td;
struct urb *urb;
struct urb_priv *urb_priv;
*prev = td->hwNextTD | savebits;
/* HC may have partly processed this TD */
- urb_print(urb, "PARTIAL", 1);
+#ifdef ADMHC_VERBOSE_DEBUG
+ urb_print(ahcd, urb, "PARTIAL", 0);
+#endif
td_done(ahcd, urb, td);
/* if URB is done, clean up */
finish_urb(ahcd, urb);
}
}
- if (completed && !list_empty (&ed->td_list))
+ if (completed && !list_empty(&ed->td_list))
goto rescan_this;
/* ED's now officially unlinked, hc doesn't see */
ed->state = ED_IDLE;
ed->hwHeadP &= ~cpu_to_hc32(ahcd, ED_H);
ed->hwNextED = 0;
- wmb ();
- ed->hwINFO &= ~cpu_to_hc32 (ahcd, ED_SKIP | ED_DEQUEUE);
+ wmb();
+ ed->hwINFO &= ~cpu_to_hc32(ahcd, ED_SKIP | ED_DEQUEUE);
/* but if there's work queued, reschedule */
- if (!list_empty (&ed->td_list)) {
+ if (!list_empty(&ed->td_list)) {
if (HC_IS_RUNNING(admhcd_to_hcd(ahcd)->state))
ed_schedule(ahcd, ed);
}
static void ed_unhalt(struct admhcd *ahcd, struct ed *ed, struct urb *urb)
{
struct list_head *entry,*tmp;
- struct urb_priv *urb_priv = urb->hcpriv;
- __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ahcd, ED_C);
-
+ __hc32 toggle = ed->hwHeadP & cpu_to_hc32(ahcd, ED_C);
#ifdef ADMHC_VERBOSE_DEBUG
admhc_dump_ed(ahcd, "UNHALT", ed, 0);
/* clear ed halt; this is the td that caused it, but keep it inactive
* until its urb->complete() has a chance to clean up.
*/
- ed->hwINFO |= cpu_to_hc32 (ahcd, ED_SKIP);
+ ed->hwINFO |= cpu_to_hc32(ahcd, ED_SKIP);
wmb();
- ed->hwHeadP &= ~cpu_to_hc32 (ahcd, ED_H);
+ ed->hwHeadP &= ~cpu_to_hc32(ahcd, ED_H);
list_for_each_safe(entry, tmp, &ed->td_list) {
struct td *td = list_entry(entry, struct td, td_list);
}
+static void ed_intr_refill(struct admhcd *ahcd, struct ed *ed)
+{
+ __hc32 toggle = ed->hwHeadP & cpu_to_hc32(ahcd, ED_C);
+
+ ed->hwHeadP = ed->hwTailP | toggle;
+}
+
+
static inline int is_ed_halted(struct admhcd *ahcd, struct ed *ed)
{
return ((hc32_to_cpup(ahcd, &ed->hwHeadP) & ED_H) == ED_H);
struct list_head *entry,*tmp;
#ifdef ADMHC_VERBOSE_DEBUG
- admhc_dump_ed(ahcd, "UPDATE", ed, 0);
+ admhc_dump_ed(ahcd, "UPDATE", ed, 1);
#endif
list_for_each_safe(entry, tmp, &ed->td_list) {
if (is_ed_halted(ahcd, ed) && is_td_halted(ahcd, ed, td))
ed_unhalt(ahcd, ed, urb);
+ if (ed->type == PIPE_INTERRUPT)
+ ed_intr_refill(ahcd,ed);
+
/* If all this urb's TDs are done, call complete() */
if (urb_priv->td_idx == urb_priv->td_cnt)
finish_urb(ahcd, urb);
start_ed_unlink(ahcd, ed);
/* ... reenabling halted EDs only after fault cleanup */
- } else if ((ed->hwINFO & cpu_to_hc32 (ahcd,
+ } else if ((ed->hwINFO & cpu_to_hc32(ahcd,
ED_SKIP | ED_DEQUEUE))
- == cpu_to_hc32 (ahcd, ED_SKIP)) {
+ == cpu_to_hc32(ahcd, ED_SKIP)) {
td = list_entry(ed->td_list.next, struct td, td_list);
#if 0
- if (!(td->hwINFO & cpu_to_hc32 (ahcd, TD_DONE))) {
- ed->hwINFO &= ~cpu_to_hc32 (ahcd, ED_SKIP);
+ if (!(td->hwINFO & cpu_to_hc32(ahcd, TD_DONE))) {
+ ed->hwINFO &= ~cpu_to_hc32(ahcd, ED_SKIP);
/* ... hc may need waking-up */
switch (ed->type) {
case PIPE_CONTROL: