Commit df42f7cf authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] ehci-hcd: update

Here's an EHCI update, I'll send separate patches to sync 2.4 with
this version.  Changes in this version include:

  - An earlier locking update would give trouble on SPARC, where
    irqsave "flags" aren't flags.  This resolves that issue by
    adding a module parameter to limit work done with irqs off.
    (Some net drivers do the same thing.)

  - Optionally (now #ifdef DEBUG) collects some statistics on IRQs
    and URBs.  There are more IAA interrupts than I want to see,
    during extended usb-storage loading.

  - Adds a commented-out workaround for a problem I've seen on one
    VT8235.  Seems likely an issue with this specific motherboard;
    another tester hasn't reported such issues.

  - Includes the jiffies time_after() patch from Tim Schmielau.

  - Minor tweaks to the hcd portability (get rid of another #if).

  - Minor doc/diagnostic/... updates
parent 65e2da7e
...@@ -79,7 +79,7 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) ...@@ -79,7 +79,7 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
if (HCC_EXT_CAPS (params)) { if (HCC_EXT_CAPS (params)) {
// EHCI 0.96 ... could interpret these (legacy?) // EHCI 0.96 ... could interpret these (legacy?)
dbg ("%s extended capabilities at pci %d", dbg ("%s extended capabilities at pci %2x",
label, HCC_EXT_CAPS (params)); label, HCC_EXT_CAPS (params));
} }
if (HCC_ISOC_CACHE (params)) { if (HCC_ISOC_CACHE (params)) {
...@@ -546,6 +546,18 @@ show_registers (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -546,6 +546,18 @@ show_registers (struct device *dev, char *buf, size_t count, loff_t off)
next += temp; next += temp;
} }
#ifdef EHCI_STATS
temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
size -= temp;
next += temp;
temp = snprintf (next, size, "complete %ld unlink %ld qpatch %ld\n",
ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch);
size -= temp;
next += temp;
#endif
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return count - size; return count - size;
......
...@@ -63,8 +63,7 @@ ...@@ -63,8 +63,7 @@
* First was PCMCIA, like ISA; then CardBus, which is PCI. * First was PCMCIA, like ISA; then CardBus, which is PCI.
* Next comes "CardBay", using USB 2.0 signals. * Next comes "CardBay", using USB 2.0 signals.
* *
* Contains additional contributions by: Brad Hards, Rory Bolt, ... * Contains additional contributions by Brad Hards, Rory Bolt, and others.
*
* Special thanks to Intel and VIA for providing host controllers to * Special thanks to Intel and VIA for providing host controllers to
* test this driver on, and Cypress (including In-System Design) for * test this driver on, and Cypress (including In-System Design) for
* providing early devices for those host controllers to talk to! * providing early devices for those host controllers to talk to!
...@@ -93,14 +92,20 @@ ...@@ -93,14 +92,20 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "2002-Aug-28" #define DRIVER_VERSION "2002-Sep-23"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
static const char hcd_name [] = "ehci-hcd";
// #define EHCI_VERBOSE_DEBUG // #define EHCI_VERBOSE_DEBUG
// #define have_split_iso // #define have_split_iso
#ifdef DEBUG
#define EHCI_STATS
#endif
#define INTR_AUTOMAGIC /* to be removed later in 2.5 */ #define INTR_AUTOMAGIC /* to be removed later in 2.5 */
/* magic numbers that can affect system performance */ /* magic numbers that can affect system performance */
...@@ -118,6 +123,12 @@ static int log2_irq_thresh = 0; // 0 to 6 ...@@ -118,6 +123,12 @@ static int log2_irq_thresh = 0; // 0 to 6
MODULE_PARM (log2_irq_thresh, "i"); MODULE_PARM (log2_irq_thresh, "i");
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
/* allow irqs at least every N URB completions */
static int max_completions = 16;
MODULE_PARM (max_completions, "i");
MODULE_PARM_DESC (max_completions,
"limit for urb completions called with irqs disenabled");
#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) #define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -426,11 +437,10 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -426,11 +437,10 @@ static int ehci_start (struct usb_hcd *hcd)
/* PCI Serial Bus Release Number is at 0x60 offset */ /* PCI Serial Bus Release Number is at 0x60 offset */
pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
temp = readw (&ehci->caps->hci_version); temp = readw (&ehci->caps->hci_version);
info ("USB %x.%x support enabled, EHCI rev %x.%02x", info ("USB %x.%x support enabled, EHCI rev %x.%02x, %s %s",
((tempbyte & 0xf0)>>4), ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
(tempbyte & 0x0f), temp >> 8, temp & 0xff,
temp >> 8, hcd_name, DRIVER_VERSION);
temp & 0xff);
/* /*
* From here on, khubd concurrently accesses the root * From here on, khubd concurrently accesses the root
...@@ -441,11 +451,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -441,11 +451,7 @@ static int ehci_start (struct usb_hcd *hcd)
*/ */
usb_connect (udev); usb_connect (udev);
udev->speed = USB_SPEED_HIGH; udev->speed = USB_SPEED_HIGH;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) if (hcd_register_root (hcd) != 0) {
if (usb_new_device (udev) != 0) {
#else
if (usb_register_root_hub (udev, &ehci->hcd.pdev->dev) != 0) {
#endif
if (hcd->state == USB_STATE_RUNNING) if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci); ehci_ready (ehci);
ehci_reset (ehci); ehci_reset (ehci);
...@@ -487,6 +493,13 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -487,6 +493,13 @@ static void ehci_stop (struct usb_hcd *hcd)
ehci_tasklet ((unsigned long) ehci); ehci_tasklet ((unsigned long) ehci);
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
#ifdef EHCI_STATS
dbg ("irq normal %ld err %ld reclaim %ld",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
dbg ("complete %ld unlink %ld qpatch %ld",
ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch);
#endif
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status)); dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
} }
...@@ -591,21 +604,16 @@ dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i); ...@@ -591,21 +604,16 @@ dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i);
static void ehci_tasklet (unsigned long param) static void ehci_tasklet (unsigned long param)
{ {
struct ehci_hcd *ehci = (struct ehci_hcd *) param; struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
// FIXME don't pass flags; on sparc they aren't really flags. spin_lock_irq (&ehci->lock);
// qh_completions can just leave irqs blocked,
// then have scan_async() allow IRQs if it's very busy
spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim_ready) if (ehci->reclaim_ready)
flags = end_unlink_async (ehci, flags); end_unlink_async (ehci);
flags = scan_async (ehci, flags); scan_async (ehci);
if (ehci->next_uframe != -1) if (ehci->next_uframe != -1)
flags = scan_periodic (ehci, flags); scan_periodic (ehci);
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irq (&ehci->lock);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -639,11 +647,17 @@ static void ehci_irq (struct usb_hcd *hcd) ...@@ -639,11 +647,17 @@ static void ehci_irq (struct usb_hcd *hcd)
/* INT, ERR, and IAA interrupt rates can be throttled */ /* INT, ERR, and IAA interrupt rates can be throttled */
/* normal [4.15.1.2] or error [4.15.1.1] completion */ /* normal [4.15.1.2] or error [4.15.1.1] completion */
if (likely ((status & (STS_INT|STS_ERR)) != 0)) if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
if (likely ((status & STS_ERR) == 0))
COUNT (ehci->stats.normal);
else
COUNT (ehci->stats.error);
bh = 1; bh = 1;
}
/* complete the unlinking of some qh [4.15.2.3] */ /* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) { if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
ehci->reclaim_ready = 1; ehci->reclaim_ready = 1;
bh = 1; bh = 1;
} }
...@@ -765,10 +779,10 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -765,10 +779,10 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
if (qh->qh_state == QH_STATE_LINKED) { if (qh->qh_state == QH_STATE_LINKED) {
/* messy, can spin or block a microframe ... */ /* messy, can spin or block a microframe ... */
flags = intr_deschedule (ehci, qh, 1, flags); intr_deschedule (ehci, qh, 1);
/* qh_state == IDLE */ /* qh_state == IDLE */
} }
flags = qh_completions (ehci, qh, flags); qh_completions (ehci, qh);
/* reschedule QH iff another request is queued */ /* reschedule QH iff another request is queued */
if (!list_empty (&qh->qtd_list) if (!list_empty (&qh->qtd_list)
...@@ -881,8 +895,6 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -881,8 +895,6 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ehci-hcd";
static const struct hc_driver ehci_driver = { static const struct hc_driver ehci_driver = {
.description = hcd_name, .description = hcd_name,
......
...@@ -239,7 +239,8 @@ static int ehci_hub_control ( ...@@ -239,7 +239,8 @@ static int ehci_hub_control (
/* whoever resets must GetPortStatus to complete it!! */ /* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET) if ((temp & PORT_RESET)
&& jiffies > ehci->reset_done [wIndex]) { && time_after (jiffies,
ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_RESET; status |= 1 << USB_PORT_FEAT_C_RESET;
/* force reset to complete */ /* force reset to complete */
......
...@@ -158,16 +158,13 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token) ...@@ -158,16 +158,13 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
} }
} }
/* urb->lock ignored from here on (hcd is done with urb) */ static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
{
static unsigned long ehci_urb_done (
struct ehci_hcd *ehci,
struct urb *urb,
unsigned long flags
) {
#ifdef INTR_AUTOMAGIC #ifdef INTR_AUTOMAGIC
struct urb *resubmit = 0; struct urb *resubmit = 0;
struct usb_device *dev = 0; struct usb_device *dev = 0;
static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int);
#endif #endif
if (likely (urb->hcpriv != 0)) { if (likely (urb->hcpriv != 0)) {
...@@ -199,8 +196,15 @@ static unsigned long ehci_urb_done ( ...@@ -199,8 +196,15 @@ static unsigned long ehci_urb_done (
urb->status = 0; urb->status = 0;
} }
if (likely (urb->status == 0))
COUNT (ehci->stats.complete);
else if (urb->status == -ECONNRESET || urb->status == -ENOENT)
COUNT (ehci->stats.unlink);
else
COUNT (ehci->stats.error);
/* complete() can reenter this HCD */ /* complete() can reenter this HCD */
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb); usb_hcd_giveback_urb (&ehci->hcd, urb);
#ifdef INTR_AUTOMAGIC #ifdef INTR_AUTOMAGIC
...@@ -222,24 +226,25 @@ static unsigned long ehci_urb_done ( ...@@ -222,24 +226,25 @@ static unsigned long ehci_urb_done (
} }
#endif #endif
spin_lock_irqsave (&ehci->lock, flags); spin_lock (&ehci->lock);
return flags;
} }
/* /*
* Process and free completed qtds for a qh, returning URBs to drivers. * Process and free completed qtds for a qh, returning URBs to drivers.
* Chases up to qh->hw_current, returns irqsave flags (maybe modified). * Chases up to qh->hw_current. Returns number of completions called,
* indicating how much "real" work we did.
*/ */
static unsigned long static unsigned
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags) qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
{ {
struct ehci_qtd *qtd, *last; struct ehci_qtd *qtd, *last;
struct list_head *next, *qtd_list = &qh->qtd_list; struct list_head *next, *qtd_list = &qh->qtd_list;
int unlink = 0, halted = 0; int unlink = 0, halted = 0;
unsigned count = 0;
if (unlikely (list_empty (qtd_list))) if (unlikely (list_empty (qtd_list)))
return flags; return count;
/* scan QTDs till end of list, or we reach an active one */ /* scan QTDs till end of list, or we reach an active one */
for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list), for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),
...@@ -252,8 +257,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags) ...@@ -252,8 +257,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
/* clean up any state from previous QTD ...*/ /* clean up any state from previous QTD ...*/
if (last) { if (last) {
if (likely (last->urb != urb)) if (likely (last->urb != urb)) {
flags = ehci_urb_done (ehci, last->urb, flags); ehci_urb_done (ehci, last->urb);
count++;
}
/* qh overlays can have HC's old cached copies of /* qh overlays can have HC's old cached copies of
* next qtd ptrs, if an URB was queued afterwards. * next qtd ptrs, if an URB was queued afterwards.
...@@ -262,6 +269,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags) ...@@ -262,6 +269,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
&& last->hw_next != qh->hw_qtd_next) { && last->hw_next != qh->hw_qtd_next) {
qh->hw_alt_next = last->hw_alt_next; qh->hw_alt_next = last->hw_alt_next;
qh->hw_qtd_next = last->hw_next; qh->hw_qtd_next = last->hw_next;
COUNT (ehci->stats.qpatch);
} }
ehci_qtd_free (ehci, last); ehci_qtd_free (ehci, last);
...@@ -347,7 +355,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags) ...@@ -347,7 +355,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
/* last urb's completion might still need calling */ /* last urb's completion might still need calling */
if (likely (last != 0)) { if (likely (last != 0)) {
flags = ehci_urb_done (ehci, last->urb, flags); ehci_urb_done (ehci, last->urb);
count++;
ehci_qtd_free (ehci, last); ehci_qtd_free (ehci, last);
} }
...@@ -357,7 +366,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags) ...@@ -357,7 +366,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
struct ehci_qtd, qtd_list)); struct ehci_qtd, qtd_list));
} }
return flags; return count;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -890,8 +899,7 @@ submit_async ( ...@@ -890,8 +899,7 @@ submit_async (
/* the async qh for the qtds being reclaimed are now unlinked from the HC */ /* the async qh for the qtds being reclaimed are now unlinked from the HC */
/* caller must not own ehci->lock */ /* caller must not own ehci->lock */
static unsigned long static void end_unlink_async (struct ehci_hcd *ehci)
end_unlink_async (struct ehci_hcd *ehci, unsigned long flags)
{ {
struct ehci_qh *qh = ehci->reclaim; struct ehci_qh *qh = ehci->reclaim;
...@@ -903,18 +911,15 @@ end_unlink_async (struct ehci_hcd *ehci, unsigned long flags) ...@@ -903,18 +911,15 @@ end_unlink_async (struct ehci_hcd *ehci, unsigned long flags)
ehci->reclaim = 0; ehci->reclaim = 0;
ehci->reclaim_ready = 0; ehci->reclaim_ready = 0;
flags = qh_completions (ehci, qh, flags); qh_completions (ehci, qh);
if (!list_empty (&qh->qtd_list) if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state)) && HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh); qh_link_async (ehci, qh);
else else
qh_put (ehci, qh); // refcount from async list qh_put (ehci, qh); // refcount from async list
return flags;
} }
/* makes sure the async qh will become idle */ /* makes sure the async qh will become idle */
/* caller must own ehci->lock */ /* caller must own ehci->lock */
...@@ -944,12 +949,14 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -944,12 +949,14 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) { if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) {
/* can't get here without STS_ASS set */ /* can't get here without STS_ASS set */
if (ehci->hcd.state != USB_STATE_HALT) { if (ehci->hcd.state != USB_STATE_HALT) {
if (cmd & CMD_PSE) { writel (cmd & ~CMD_ASE, &ehci->regs->command);
writel (cmd & ~CMD_ASE, &ehci->regs->command); (void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
(void) handshake (&ehci->regs->status, #if 0
STS_ASS, 0, 150); // one VT8235 system wants to die with STS_FATAL
} else // unless this qh is leaked here. others seem ok...
ehci_ready (ehci); qh = qh_get (qh);
dbg_qh ("async/off", ehci, qh);
#endif
} }
qh->qh_next.qh = ehci->async = 0; qh->qh_next.qh = ehci->async = 0;
...@@ -986,13 +993,15 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -986,13 +993,15 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static unsigned long static void
scan_async (struct ehci_hcd *ehci, unsigned long flags) scan_async (struct ehci_hcd *ehci)
{ {
struct ehci_qh *qh; struct ehci_qh *qh;
unsigned count;
rescan: rescan:
qh = ehci->async; qh = ehci->async;
count = 0;
if (likely (qh != 0)) { if (likely (qh != 0)) {
do { do {
/* clean any finished work for this qh */ /* clean any finished work for this qh */
...@@ -1001,13 +1010,16 @@ scan_async (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1001,13 +1010,16 @@ scan_async (struct ehci_hcd *ehci, unsigned long flags)
qh = qh_get (qh); qh = qh_get (qh);
/* concurrent unlink could happen here */ /* concurrent unlink could happen here */
flags = qh_completions (ehci, qh, flags); count += qh_completions (ehci, qh);
qh_put (ehci, qh); qh_put (ehci, qh);
} }
/* unlink idle entries, reducing HC PCI usage as /* unlink idle entries, reducing HC PCI usage as
* well as HCD schedule-scanning costs. removing * well as HCD schedule-scanning costs. removing
* the last qh is deferred, since it's costly. * the last qh is deferred, since it's costly.
*
* FIXME don't unlink idle entries so quickly; it
* can penalize (common) half duplex protocols.
*/ */
if (list_empty (&qh->qtd_list) && !ehci->reclaim) { if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
if (qh->qh_next.qh != qh) { if (qh->qh_next.qh != qh) {
...@@ -1020,10 +1032,18 @@ scan_async (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1020,10 +1032,18 @@ scan_async (struct ehci_hcd *ehci, unsigned long flags)
jiffies + EHCI_ASYNC_JIFFIES); jiffies + EHCI_ASYNC_JIFFIES);
} }
} }
/* keep latencies down: let any irqs in */
if (count > max_completions) {
spin_unlock_irq (&ehci->lock);
cpu_relax ();
spin_lock_irq (&ehci->lock);
goto rescan;
}
qh = qh->qh_next.qh; qh = qh->qh_next.qh;
if (!qh) /* unlinked? */ if (!qh) /* unlinked? */
goto rescan; goto rescan;
} while (qh != ehci->async); } while (qh != ehci->async);
} }
return flags;
} }
...@@ -222,11 +222,10 @@ static int disable_periodic (struct ehci_hcd *ehci) ...@@ -222,11 +222,10 @@ static int disable_periodic (struct ehci_hcd *ehci)
// FIXME microframe periods not yet handled // FIXME microframe periods not yet handled
static unsigned long intr_deschedule ( static void intr_deschedule (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct ehci_qh *qh, struct ehci_qh *qh,
int wait, int wait
unsigned long flags
) { ) {
int status; int status;
unsigned frame = qh->start; unsigned frame = qh->start;
...@@ -256,10 +255,8 @@ static unsigned long intr_deschedule ( ...@@ -256,10 +255,8 @@ static unsigned long intr_deschedule (
*/ */
if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
if (wait) { if (wait) {
spin_unlock_irqrestore (&ehci->lock, flags);
udelay (125); udelay (125);
qh->hw_next = EHCI_LIST_END; qh->hw_next = EHCI_LIST_END;
spin_lock_irqsave (&ehci->lock, flags);
} else { } else {
/* we may not be IDLE yet, but if the qh is empty /* we may not be IDLE yet, but if the qh is empty
* the race is very short. then if qh also isn't * the race is very short. then if qh also isn't
...@@ -276,10 +273,9 @@ static unsigned long intr_deschedule ( ...@@ -276,10 +273,9 @@ static unsigned long intr_deschedule (
hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= hcd_to_bus (&ehci->hcd)->bandwidth_allocated -=
(qh->usecs + qh->c_usecs) / qh->period; (qh->usecs + qh->c_usecs) / qh->period;
vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d", dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d",
qh, qh->period, frame, qh, qh->period, frame,
atomic_read (&qh->refcount), ehci->periodic_sched); atomic_read (&qh->refcount), ehci->periodic_sched);
return flags;
} }
static int check_period ( static int check_period (
...@@ -414,7 +410,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -414,7 +410,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* stuff into the periodic schedule */ /* stuff into the periodic schedule */
qh->qh_state = QH_STATE_LINKED; qh->qh_state = QH_STATE_LINKED;
dbg ("qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
qh, qh->usecs, qh->c_usecs, qh, qh->usecs, qh->c_usecs,
qh->period, frame, uframe, qh->gap_uf); qh->period, frame, uframe, qh->gap_uf);
do { do {
...@@ -495,29 +491,30 @@ static int intr_submit ( ...@@ -495,29 +491,30 @@ static int intr_submit (
return status; return status;
} }
static unsigned long static unsigned
intr_complete ( intr_complete (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
unsigned frame, unsigned frame,
struct ehci_qh *qh, struct ehci_qh *qh
unsigned long flags /* caller owns ehci->lock ... */
) { ) {
unsigned count;
/* nothing to report? */ /* nothing to report? */
if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
!= 0)) != 0))
return flags; return 0;
if (unlikely (list_empty (&qh->qtd_list))) { if (unlikely (list_empty (&qh->qtd_list))) {
dbg ("intr qh %p no TDs?", qh); dbg ("intr qh %p no TDs?", qh);
return flags; return 0;
} }
/* handle any completions */ /* handle any completions */
flags = qh_completions (ehci, qh, flags); count = qh_completions (ehci, qh);
if (unlikely (list_empty (&qh->qtd_list))) if (unlikely (list_empty (&qh->qtd_list)))
flags = intr_deschedule (ehci, qh, 0, flags); intr_deschedule (ehci, qh, 0);
return flags; return count;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -866,12 +863,11 @@ itd_schedule (struct ehci_hcd *ehci, struct urb *urb) ...@@ -866,12 +863,11 @@ itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
static unsigned long static unsigned
itd_complete ( itd_complete (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct ehci_itd *itd, struct ehci_itd *itd,
unsigned uframe, unsigned uframe
unsigned long flags
) { ) {
struct urb *urb = itd->urb; struct urb *urb = itd->urb;
struct usb_iso_packet_descriptor *desc; struct usb_iso_packet_descriptor *desc;
...@@ -909,7 +905,7 @@ itd_complete ( ...@@ -909,7 +905,7 @@ itd_complete (
/* handle completion now? */ /* handle completion now? */
if ((itd->index + 1) != urb->number_of_packets) if ((itd->index + 1) != urb->number_of_packets)
return flags; return 0;
/* /*
* Always give the urb back to the driver ... expect it to submit * Always give the urb back to the driver ... expect it to submit
...@@ -924,16 +920,17 @@ itd_complete ( ...@@ -924,16 +920,17 @@ itd_complete (
if (urb->status == -EINPROGRESS) if (urb->status == -EINPROGRESS)
urb->status = 0; urb->status = 0;
spin_unlock_irqrestore (&ehci->lock, flags); /* complete() can reenter this HCD */
spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb); usb_hcd_giveback_urb (&ehci->hcd, urb);
spin_lock_irqsave (&ehci->lock, flags); spin_lock (&ehci->lock);
/* defer stopping schedule; completion can submit */ /* defer stopping schedule; completion can submit */
ehci->periodic_sched--; ehci->periodic_sched--;
if (!ehci->periodic_sched) if (!ehci->periodic_sched)
(void) disable_periodic (ehci); (void) disable_periodic (ehci);
return flags; return 1;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -945,10 +942,6 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) ...@@ -945,10 +942,6 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
dbg ("itd_submit urb %p", urb); dbg ("itd_submit urb %p", urb);
/* NOTE DMA mapping assumes this ... */
if (urb->iso_frame_desc [0].offset != 0)
return -EINVAL;
/* allocate ITDs w/o locking anything */ /* allocate ITDs w/o locking anything */
status = itd_urb_transaction (ehci, urb, mem_flags); status = itd_urb_transaction (ehci, urb, mem_flags);
if (status < 0) if (status < 0)
...@@ -979,10 +972,11 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) ...@@ -979,10 +972,11 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static unsigned long static void
scan_periodic (struct ehci_hcd *ehci, unsigned long flags) scan_periodic (struct ehci_hcd *ehci)
{ {
unsigned frame, clock, now_uframe, mod; unsigned frame, clock, now_uframe, mod;
unsigned count = 0;
mod = ehci->periodic_size << 3; mod = ehci->periodic_size << 3;
...@@ -1005,6 +999,14 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1005,6 +999,14 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
u32 type, *hw_p; u32 type, *hw_p;
unsigned uframes; unsigned uframes;
/* keep latencies down: let any irqs in */
if (count > max_completions) {
spin_unlock_irq (&ehci->lock);
cpu_relax ();
count = 0;
spin_lock_irq (&ehci->lock);
}
restart: restart:
/* scan schedule to _before_ current frame index */ /* scan schedule to _before_ current frame index */
if (frame == clock) if (frame == clock)
...@@ -1028,8 +1030,8 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1028,8 +1030,8 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
last = (q.qh->hw_next == EHCI_LIST_END); last = (q.qh->hw_next == EHCI_LIST_END);
temp = q.qh->qh_next; temp = q.qh->qh_next;
type = Q_NEXT_TYPE (q.qh->hw_next); type = Q_NEXT_TYPE (q.qh->hw_next);
flags = intr_complete (ehci, frame, count += intr_complete (ehci, frame,
qh_get (q.qh), flags); qh_get (q.qh));
qh_put (ehci, q.qh); qh_put (ehci, q.qh);
q = temp; q = temp;
break; break;
...@@ -1061,8 +1063,8 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1061,8 +1063,8 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
type = Q_NEXT_TYPE (*hw_p); type = Q_NEXT_TYPE (*hw_p);
/* might free q.itd ... */ /* might free q.itd ... */
flags = itd_complete (ehci, count += itd_complete (ehci,
temp.itd, uf, flags); temp.itd, uf);
break; break;
} }
} }
...@@ -1078,7 +1080,7 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1078,7 +1080,7 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
#ifdef have_split_iso #ifdef have_split_iso
case Q_TYPE_SITD: case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END); last = (q.sitd->hw_next == EHCI_LIST_END);
flags = sitd_complete (ehci, q.sitd, flags); sitd_complete (ehci, q.sitd);
type = Q_NEXT_TYPE (q.sitd->hw_next); type = Q_NEXT_TYPE (q.sitd->hw_next);
// FIXME unlink SITD after split completes // FIXME unlink SITD after split completes
...@@ -1124,5 +1126,4 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags) ...@@ -1124,5 +1126,4 @@ scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
} else } else
frame = (frame + 1) % ehci->periodic_size; frame = (frame + 1) % ehci->periodic_size;
} }
return flags;
} }
...@@ -21,6 +21,23 @@ ...@@ -21,6 +21,23 @@
/* definitions used for the EHCI driver */ /* definitions used for the EHCI driver */
/* statistics can be kept for for tuning/monitoring */
struct ehci_stats {
/* irq usage */
unsigned long normal;
unsigned long error;
unsigned long reclaim;
/* termination of urbs from core */
unsigned long complete;
unsigned long unlink;
/* qhs patched to recover from td queueing race
* (can avoid by using 'dummy td', allowing fewer irqs)
*/
unsigned long qpatch;
};
/* ehci_hcd->lock guards shared data against other CPUs: /* ehci_hcd->lock guards shared data against other CPUs:
* ehci_hcd: async, reclaim, periodic (and shadow), ... * ehci_hcd: async, reclaim, periodic (and shadow), ...
* hcd_dev: ep[] * hcd_dev: ep[]
...@@ -72,6 +89,13 @@ struct ehci_hcd { /* one per controller */ ...@@ -72,6 +89,13 @@ struct ehci_hcd { /* one per controller */
struct pci_pool *sitd_pool; /* sitd per split iso urb */ struct pci_pool *sitd_pool; /* sitd per split iso urb */
struct timer_list watchdog; struct timer_list watchdog;
#ifdef EHCI_STATS
struct ehci_stats stats;
# define COUNT(x) do { (x)++; } while (0)
#else
# define COUNT(x) do {} while (0)
#endif
}; };
/* unwrap an HCD pointer to get an EHCI_HCD pointer */ /* unwrap an HCD pointer to get an EHCI_HCD pointer */
...@@ -400,10 +424,22 @@ struct ehci_fstn { ...@@ -400,10 +424,22 @@ struct ehci_fstn {
#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb) #define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb)
#define STUB_DEBUG_FILES #define STUB_DEBUG_FILES
static inline int hcd_register_root (struct usb_hcd *hcd)
{
return usb_new_device (hcd_to_bus (hcd)->root_hub);
}
#else /* LINUX_VERSION_CODE */ #else /* LINUX_VERSION_CODE */
// hcd_to_bus() eventually moves to hcd.h on 2.5 too
static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
{ return &hcd->self; } { return &hcd->self; }
// ... as does hcd_register_root()
static inline int hcd_register_root (struct usb_hcd *hcd)
{
return usb_register_root_hub (
hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev);
}
#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags) #define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)
......
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