ChangeSet 1.893.2.6, 2002/12/23 11:48:06-08:00, david-b@pacbell.net [PATCH] ehci updates This patch basically brings the 2.4 code up to the level of the latest 2.5.52bk4+ patches ... so while not perfect (I know of a hub issue) I think it's worth circulating: - Fixes "it hangs" ... at least for the storage cases I could reproduce. - Longer reset timeout for Intel EHCI ... this bug had been lurking since July in the 2.5 series, but was only reported (and most of the fix identified!) by a user after 2.4.21pre1 came out. - Better fixes for a problem first seen with VT8235. Had to dedicate a QH as list head, never unlinked. Oopsable, but NEC hardware seemingly implemented the EHCI spec in a way that they didn't require that. - Significant updates to QTD and URB queuing. Much more robust, fewer hardware races. Uses "dummy td" scheme. Most USB device drivers in 2.4 won't stress that logic as much as usb-storage does in 2.5, but the robustness should help all around. - Diagnostics updates. Both 2.4 and 2.5 versions should emit the same "new style" diagnostics, but 2.4 can't use to do it. - DaveM's "remove tasklet" patch. Bubbled up to the "hcd" glue, but completions on 2.4 don't receive the 'struct pt_regs *' so it stops there. - Other less notable fixes and cleanups too. One way to look at the patch is: (a) a smallish set of "hcd" updates, which can help 2.4 backports of the other 2.5 "*hcd" drivers too; (b) the 2.5 ehci-hcd with the addition of about 4K of patches to handle things like "2.4 needs interrupt automagic". diff -Nru a/drivers/usb/hcd/ehci-dbg.c b/drivers/usb/hcd/ehci-dbg.c --- a/drivers/usb/hcd/ehci-dbg.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci-dbg.c Mon Jan 6 11:32:04 2003 @@ -18,10 +18,45 @@ /* this file is part of ehci-hcd.c */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,50) + +#define ehci_dbg(ehci, fmt, args...) \ + dev_dbg (*(ehci)->hcd.controller, fmt, ## args ) +#define ehci_err(ehci, fmt, args...) \ + dev_err (*(ehci)->hcd.controller, fmt, ## args ) +#define ehci_info(ehci, fmt, args...) \ + dev_info (*(ehci)->hcd.controller, fmt, ## args ) +#define ehci_warn(ehci, fmt, args...) \ + dev_warn (*(ehci)->hcd.controller, fmt, ## args ) + +#else + +#ifdef DEBUG +#define ehci_dbg(ehci, fmt, args...) \ + printk(KERN_DEBUG "%s %s: " fmt, hcd_name, \ + (ehci)->hcd.pdev->slot_name, ## args ) +#else +#define ehci_dbg(ehci, fmt, args...) do { } while (0) +#endif + +#define ehci_err(ehci, fmt, args...) \ + printk(KERN_ERR "%s %s: " fmt, hcd_name, \ + (ehci)->hcd.pdev->slot_name, ## args ) +#define ehci_info(ehci, fmt, args...) \ + printk(KERN_INFO "%s %s: " fmt, hcd_name, \ + (ehci)->hcd.pdev->slot_name, ## args ) +#define ehci_warn(ehci, fmt, args...) \ + printk(KERN_WARNING "%s %s: " fmt, hcd_name, \ + (ehci)->hcd.pdev->slot_name, ## args ) +#endif + + #ifdef EHCI_VERBOSE_DEBUG # define vdbg dbg +# define ehci_vdbg ehci_dbg #else - static inline void vdbg (char *fmt, ...) { } +# define vdbg(fmt,args...) do { } while (0) +# define ehci_vdbg(ehci, fmt, args...) do { } while (0) #endif #ifdef DEBUG @@ -34,7 +69,8 @@ { u32 params = readl (&ehci->caps->hcs_params); - dbg ("%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d", + ehci_dbg (ehci, + "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n", label, params, HCS_DEBUG_PORT (params), HCS_INDICATOR (params) ? " ind" : "", @@ -56,9 +92,8 @@ ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf))); strcat(buf, tmp); } - dbg ("%s: %s portroute %s", - hcd_to_bus (&ehci->hcd)->bus_name, label, - buf); + ehci_dbg (ehci, "%s portroute %s\n", + label, buf); } } #else @@ -77,19 +112,16 @@ { u32 params = readl (&ehci->caps->hcc_params); - if (HCC_EXT_CAPS (params)) { - // EHCI 0.96 ... could interpret these (legacy?) - dbg ("%s extended capabilities at pci %2x", - label, HCC_EXT_CAPS (params)); - } if (HCC_ISOC_CACHE (params)) { - dbg ("%s hcc_params %04x caching frame %s%s%s", + ehci_dbg (ehci, + "%s hcc_params %04x caching frame %s%s%s\n", label, params, HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", HCC_CANPARK (params) ? " park" : "", HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); } else { - dbg ("%s hcc_params %04x caching %d uframes %s%s%s", + ehci_dbg (ehci, + "%s hcc_params %04x thresh %d uframes %s%s%s\n", label, params, HCC_ISOC_THRES (params), @@ -235,19 +267,19 @@ #define dbg_status(ehci, label, status) { \ char _buf [80]; \ dbg_status_buf (_buf, sizeof _buf, label, status); \ - dbg ("%s", _buf); \ + ehci_dbg (ehci, "%s\n", _buf); \ } #define dbg_cmd(ehci, label, command) { \ char _buf [80]; \ dbg_command_buf (_buf, sizeof _buf, label, command); \ - dbg ("%s", _buf); \ + ehci_dbg (ehci, "%s\n", _buf); \ } -#define dbg_port(hcd, label, port, status) { \ +#define dbg_port(ehci, label, port, status) { \ char _buf [80]; \ dbg_port_buf (_buf, sizeof _buf, label, port, status); \ - dbg ("%s", _buf); \ + ehci_dbg (ehci, "%s\n", _buf); \ } /*-------------------------------------------------------------------------*/ @@ -272,6 +304,7 @@ static void qh_lines (struct ehci_qh *qh, char **nextp, unsigned *sizep) { u32 scratch; + u32 hw_curr; struct list_head *entry; struct ehci_qtd *td; unsigned temp; @@ -279,20 +312,23 @@ char *next = *nextp; scratch = cpu_to_le32p (&qh->hw_info1); - temp = snprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x", + hw_curr = cpu_to_le32p (&qh->hw_current); + temp = snprintf (next, size, + "qh/%p dev%d %cs ep%d %08x %08x (%08x %08x)", qh, scratch & 0x007f, speed_char (scratch), (scratch >> 8) & 0x000f, - scratch, cpu_to_le32p (&qh->hw_info2)); + scratch, cpu_to_le32p (&qh->hw_info2), + hw_curr, cpu_to_le32p (&qh->hw_token)); size -= temp; next += temp; list_for_each (entry, &qh->qtd_list) { - td = list_entry (entry, struct ehci_qtd, - qtd_list); + td = list_entry (entry, struct ehci_qtd, qtd_list); scratch = cpu_to_le32p (&td->hw_token); temp = snprintf (next, size, - "\n\ttd/%p %s len=%d %08x urb %p", + "\n\t%std/%p %s len=%d %08x urb %p", + (hw_curr == td->qtd_dma) ? "*" : "", td, ({ char *tmp; switch ((scratch>>8)&0x03) { case 0: tmp = "out"; break; @@ -335,12 +371,8 @@ * one QH per line, and TDs we know about */ spin_lock_irqsave (&ehci->lock, flags); - if (ehci->async) { - qh = ehci->async; - do { - qh_lines (qh, &next, &size); - } while ((qh = qh->qh_next.qh) != ehci->async); - } + for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) + qh_lines (qh, &next, &size); if (ehci->reclaim) { temp = snprintf (next, size, "\nreclaim =\n"); size -= temp; @@ -552,8 +584,8 @@ size -= temp; next += temp; - temp = snprintf (next, size, "complete %ld unlink %ld qpatch %ld\n", - ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch); + temp = snprintf (next, size, "complete %ld unlink %ld\n", + ehci->stats.complete, ehci->stats.unlink); size -= temp; next += temp; #endif diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci-hcd.c Mon Jan 6 11:32:04 2003 @@ -63,13 +63,14 @@ * First was PCMCIA, like ISA; then CardBus, which is PCI. * Next comes "CardBay", using USB 2.0 signals. * - * Contains additional contributions by: Brad Hards, Rory Bolt, and more. + * Contains additional contributions by Brad Hards, Rory Bolt, and others. * Special thanks to Intel and VIA for providing host controllers to * test this driver on, and Cypress (including In-System Design) for * providing early devices for those host controllers to talk to! * * HISTORY: * + * 2002-11-29 Correct handling for hw async_next register. * 2002-08-06 Handling for bulk and interrupt transfers is mostly shared; * only scheduling is different, no arbitrary limitations. * 2002-07-25 Sanity check PCI reads, mostly for better cardbus support, @@ -92,7 +93,7 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2002-Sep-23" +#define DRIVER_VERSION "2002-Dec-20" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -116,19 +117,13 @@ #define EHCI_TUNE_MULT_TT 1 #define EHCI_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ -#define EHCI_ASYNC_JIFFIES (HZ/3) /* async idle timeout */ +#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ /* Initial IRQ latency: lower than default */ static int log2_irq_thresh = 0; // 0 to 6 MODULE_PARM (log2_irq_thresh, "i"); 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) /*-------------------------------------------------------------------------*/ @@ -202,7 +197,7 @@ dbg_cmd (ehci, "reset", command); writel (command, &ehci->regs->command); ehci->hcd.state = USB_STATE_HALT; - return handshake (&ehci->regs->command, CMD_RESET, 0, 250); + return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); } /* idle the controller (from running) */ @@ -217,7 +212,7 @@ /* wait for any schedule enables/disables to take effect */ temp = 0; - if (ehci->async) + if (ehci->async->qh_next.qh) temp = STS_ASS; if (ehci->next_uframe != -1) temp |= STS_PSS; @@ -250,9 +245,7 @@ /*-------------------------------------------------------------------------*/ -static void ehci_tasklet (unsigned long param); - -static void ehci_irq (struct usb_hcd *hcd); +static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); static void ehci_watchdog (unsigned long param) { @@ -260,10 +253,25 @@ unsigned long flags; spin_lock_irqsave (&ehci->lock, flags); - /* guard against lost IAA, which wedges everything */ - ehci_irq (&ehci->hcd); - /* unlink the last qh after it's idled a while */ - if (ehci->async_idle) { + + /* lost IAA irqs wedge things badly; seen with a vt8235 */ + if (ehci->reclaim) { + u32 status = readl (&ehci->regs->status); + + if (status & STS_IAA) { + ehci_vdbg (ehci, "lost IAA\n"); + writel (STS_IAA, &ehci->regs->status); + ehci->reclaim_ready = 1; + } + } + + ehci_work (ehci, NULL); + if (ehci->reclaim && !timer_pending (&ehci->watchdog)) + mod_timer (&ehci->watchdog, + jiffies + EHCI_WATCHDOG_JIFFIES); + + /* stop async processing after it's idled a while */ + else if (ehci->async_idle) { start_unlink_async (ehci, ehci->async); ehci->async_idle = 0; } @@ -289,12 +297,12 @@ pci_read_config_dword (ehci->hcd.pdev, where, &cap); } while ((cap & (1 << 16)) && msec); if (cap & (1 << 16)) { - info ("BIOS handoff failed (%d, %04x)", where, cap); + ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n", + where, cap); return 1; } - dbg ("BIOS handoff succeeded"); - } else - dbg ("BIOS handoff not needed"); + ehci_dbg (ehci, "BIOS handoff succeeded\n"); + } return 0; } @@ -325,14 +333,14 @@ u32 cap; pci_read_config_dword (ehci->hcd.pdev, temp, &cap); - dbg ("capability %04x at %02x", cap, temp); + ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp); switch (cap & 0xff) { case 1: /* BIOS/SMM/... handoff */ if (bios_handoff (ehci, temp, cap) != 0) return -EOPNOTSUPP; break; case 0: /* illegal reserved capability */ - warn ("illegal capability!"); + ehci_warn (ehci, "illegal capability!\n"); cap = 0; /* FALLTHROUGH */ default: /* unknown */ @@ -362,7 +370,6 @@ else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params); - ehci->async = 0; ehci->reclaim = 0; ehci->next_uframe = -1; @@ -377,6 +384,22 @@ writel (ehci->periodic_dma, &ehci->regs->frame_list); /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + ehci->async->qh_next.qh = 0; + ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma); + ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD); + ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT); + ehci->async->hw_qtd_next = EHCI_LIST_END; + ehci->async->qh_state = QH_STATE_LINKED; + ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma); + writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); + + /* * hcc_params controls whether ehci->regs->segment must (!!!) * be used; it constrains QH/ITD/SITD and QTD locations. * pci_pool consistent memory always uses segment zero. @@ -390,7 +413,7 @@ if (HCC_64BIT_ADDR (hcc_params)) { writel (0, &ehci->regs->segment); if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL)) - info ("enabled 64bit PCI DMA (DAC)"); + ehci_info (ehci, "enabled 64bit PCI DMA\n"); } /* clear interrupt enables, set irq latency */ @@ -409,9 +432,6 @@ /* set async sleep time = 10 us ... ? */ - ehci->tasklet.func = ehci_tasklet; - ehci->tasklet.data = (unsigned long) ehci; - init_timer (&ehci->watchdog); ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; @@ -437,10 +457,10 @@ /* PCI Serial Bus Release Number is at 0x60 offset */ pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); temp = readw (&ehci->caps->hci_version); - info ("USB %x.%x support enabled, EHCI rev %x.%02x, %s %s", - ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f), - temp >> 8, temp & 0xff, - hcd_name, DRIVER_VERSION); + ehci_info (ehci, + "USB %x.%x enabled, EHCI %x.%02x, driver %s\n", + ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f), + temp >> 8, temp & 0xff, DRIVER_VERSION); /* * From here on, khubd concurrently accesses the root @@ -472,15 +492,16 @@ { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - dbg ("%s: stop", hcd_to_bus (hcd)->bus_name); + ehci_dbg (ehci, "stop\n"); /* no more interrupts ... */ if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - if (in_interrupt ()) /* should not happen!! */ - err ("stopped %s!", RUN_CONTEXT); - else - del_timer_sync (&ehci->watchdog); + if (in_interrupt ()) { /* must not happen!! */ + ehci_err (ehci, "stopped in_interrupt!\n"); + return; + } + del_timer_sync (&ehci->watchdog); ehci_reset (ehci); /* let companion controllers work when we aren't */ @@ -489,15 +510,16 @@ remove_debug_files (ehci); /* root hub is shut down separately (first, when possible) */ - tasklet_disable (&ehci->tasklet); - ehci_tasklet ((unsigned long) ehci); + spin_lock_irq (&ehci->lock); + ehci_work (ehci, NULL); + spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); #ifdef EHCI_STATS - dbg ("irq normal %ld err %ld reclaim %ld", + ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld\n", 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); + ehci_dbg (ehci, "complete %ld unlink %ld\n", + ehci->stats.complete, ehci->stats.unlink); #endif dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status)); @@ -598,27 +620,21 @@ /*-------------------------------------------------------------------------*/ /* - * tasklet scheduled by some interrupts and other events - * calls driver completion functions ... but not in_irq() + * ehci_work is called from some interrupts, timers, and so on. + * it calls driver completion functions, after dropping ehci->lock. */ -static void ehci_tasklet (unsigned long param) +static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) { - struct ehci_hcd *ehci = (struct ehci_hcd *) param; - - spin_lock_irq (&ehci->lock); - if (ehci->reclaim_ready) - end_unlink_async (ehci); - scan_async (ehci); + end_unlink_async (ehci, regs); + scan_async (ehci, regs); if (ehci->next_uframe != -1) - scan_periodic (ehci); - - spin_unlock_irq (&ehci->lock); + scan_periodic (ehci, regs); } /*-------------------------------------------------------------------------*/ -static void ehci_irq (struct usb_hcd *hcd) +static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status = readl (&ehci->regs->status); @@ -626,7 +642,7 @@ /* e.g. cardbus physical eject */ if (status == ~(u32) 0) { - dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name); + ehci_dbg (ehci, "device removed\n"); goto dead; } @@ -634,6 +650,8 @@ if (!status) /* irq sharing? */ return; + spin_lock (&ehci->lock); + /* clear (just) interrupts */ writel (status, &ehci->regs->status); readl (&ehci->regs->command); /* unblock posted write */ @@ -664,8 +682,7 @@ /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { - err ("%s: fatal error, state %x", - hcd_to_bus (hcd)->bus_name, hcd->state); + ehci_err (ehci, "fatal error\n"); dead: ehci_reset (ehci); /* generic layer kills/unlinks all urbs, then @@ -674,9 +691,9 @@ bh = 1; } - /* most work doesn't need to be in_irq() */ - if (likely (bh == 1)) - tasklet_schedule (&ehci->tasklet); + if (bh) + ehci_work (ehci, regs); + spin_unlock (&ehci->lock); } /*-------------------------------------------------------------------------*/ @@ -737,52 +754,52 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; + struct ehci_qh *qh; unsigned long flags; + int maybe_irq = 1; - dbg ("%s urb_dequeue %p qh %p state %d", - hcd_to_bus (hcd)->bus_name, urb, qh, qh->qh_state); - + spin_lock_irqsave (&ehci->lock, flags); switch (usb_pipetype (urb->pipe)) { // case PIPE_CONTROL: // case PIPE_BULK: default: - spin_lock_irqsave (&ehci->lock, flags); - if (ehci->reclaim) { - dbg ("dq %p: reclaim = %p, %s", - qh, ehci->reclaim, RUN_CONTEXT); - if (qh == ehci->reclaim) { - /* unlinking qh for another queued urb? */ - spin_unlock_irqrestore (&ehci->lock, flags); - return 0; - } - if (in_interrupt ()) { - spin_unlock_irqrestore (&ehci->lock, flags); - return -EAGAIN; - } - while (qh->qh_state == QH_STATE_LINKED - && ehci->reclaim - && ehci->hcd.state != USB_STATE_HALT - ) { - spin_unlock_irqrestore (&ehci->lock, flags); - /* let pending unlinks complete */ - wait_ms (1); - spin_lock_irqsave (&ehci->lock, flags); + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + while (qh->qh_state == QH_STATE_LINKED + && ehci->reclaim + && HCD_IS_RUNNING (ehci->hcd.state) + ) { + spin_unlock_irqrestore (&ehci->lock, flags); + + if (maybe_irq) { + if (in_interrupt ()) + return -EAGAIN; + maybe_irq = 0; } + /* let pending unlinks complete, so this can start */ + wait_ms (1); + + spin_lock_irqsave (&ehci->lock, flags); } + if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim) + end_unlink_async (ehci, NULL); + + /* something else might have unlinked the qh by now */ if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); - spin_unlock_irqrestore (&ehci->lock, flags); break; case PIPE_INTERRUPT: - spin_lock_irqsave (&ehci->lock, flags); + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; if (qh->qh_state == QH_STATE_LINKED) { /* messy, can spin or block a microframe ... */ intr_deschedule (ehci, qh, 1); /* qh_state == IDLE */ } - qh_completions (ehci, qh); + qh_completions (ehci, qh, NULL); /* reschedule QH iff another request is queued */ if (!list_empty (&qh->qtd_list) @@ -800,7 +817,6 @@ } return status; } - spin_unlock_irqrestore (&ehci->lock, flags); break; case PIPE_ISOCHRONOUS: @@ -811,6 +827,7 @@ urb->transfer_flags |= EHCI_STATE_UNLINK; break; } + spin_unlock_irqrestore (&ehci->lock, flags); return 0; } @@ -868,11 +885,11 @@ */ while (qh->qh_state == QH_STATE_LINKED && ehci->reclaim - && ehci->hcd.state != USB_STATE_HALT + && HCD_IS_RUNNING (ehci->hcd.state) ) { spin_unlock_irqrestore (&ehci->lock, flags); /* wait_ms() won't spin, we're a thread; - * and we know IRQ+tasklet can progress + * and we know IRQ/timer/... can progress */ wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci-hub.c Mon Jan 6 11:32:04 2003 @@ -40,18 +40,15 @@ /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { - dbg ("%s port %d full speed, give to companion, 0x%x", - hcd_to_bus (&ehci->hcd)->bus_name, - index + 1, port_status); + ehci_dbg (ehci, "port %d full speed --> companion\n", + index + 1); // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; writel (port_status, &ehci->regs->port_status [index]); } else - dbg ("%s port %d high speed", - hcd_to_bus (&ehci->hcd)->bus_name, - index + 1); + ehci_dbg (ehci, "port %d high speed\n", index + 1); return port_status; } @@ -277,7 +274,7 @@ #ifndef EHCI_VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ #endif - dbg_port (hcd, "GetStatus", wIndex + 1, temp); + dbg_port (ehci, "GetStatus", wIndex + 1, temp); // we "know" this alignment is good, caller used kmalloc()... *((u32 *) buf) = cpu_to_le32 (status); break; @@ -313,14 +310,12 @@ /* line status bits may report this as low speed */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && PORT_USB11 (temp)) { - dbg ("%s port %d low speed, give to companion", - hcd_to_bus (&ehci->hcd)->bus_name, + ehci_dbg (ehci, + "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; } else { - vdbg ("%s port %d reset", - hcd_to_bus (&ehci->hcd)->bus_name, - wIndex + 1); + ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c --- a/drivers/usb/hcd/ehci-mem.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci-mem.c Mon Jan 6 11:32:04 2003 @@ -58,6 +58,15 @@ /* Allocate the key transfer structures from the previously allocated pool */ +static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) +{ + memset (qtd, 0, sizeof *qtd); + qtd->qtd_dma = dma; + qtd->hw_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END; + INIT_LIST_HEAD (&qtd->qtd_list); +} + static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags) { struct ehci_qtd *qtd; @@ -65,11 +74,9 @@ qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma); if (qtd != 0) { - memset (qtd, 0, sizeof *qtd); - qtd->qtd_dma = dma; - qtd->hw_next = EHCI_LIST_END; - qtd->hw_alt_next = EHCI_LIST_END; - INIT_LIST_HEAD (&qtd->qtd_list); + ehci_qtd_init (qtd, dma); + if (ehci->async) + qtd->hw_alt_next = ehci->async->hw_alt_next; } return qtd; } @@ -87,12 +94,21 @@ qh = (struct ehci_qh *) pci_pool_alloc (ehci->qh_pool, flags, &dma); - if (qh) { - memset (qh, 0, sizeof *qh); - atomic_set (&qh->refcount, 1); - qh->qh_dma = dma; - // INIT_LIST_HEAD (&qh->qh_list); - INIT_LIST_HEAD (&qh->qtd_list); + if (!qh) + return qh; + + memset (qh, 0, sizeof *qh); + atomic_set (&qh->refcount, 1); + qh->qh_dma = dma; + // INIT_LIST_HEAD (&qh->qh_list); + INIT_LIST_HEAD (&qh->qtd_list); + + /* dummy td enables safe urb queuing */ + qh->dummy = ehci_qtd_alloc (ehci, flags); + if (qh->dummy == 0) { + ehci_dbg (ehci, "no dummy td\n"); + pci_pool_free (ehci->qh_pool, qh, qh->qh_dma); + qh = 0; } return qh; } @@ -100,21 +116,21 @@ /* to share a qh (cpu threads, or hc) */ static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) { - // dbg ("get %p (%d++)", qh, qh->refcount.counter); atomic_inc (&qh->refcount); return qh; } static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) { - // dbg ("put %p (--%d)", qh, qh->refcount.counter); if (!atomic_dec_and_test (&qh->refcount)) return; /* clean qtds first, and know this is not linked */ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { - dbg ("unused qh not empty!"); + ehci_dbg (ehci, "unused qh not empty!\n"); BUG (); } + if (qh->dummy) + ehci_qtd_free (ehci, qh->dummy); pci_pool_free (ehci->qh_pool, qh, qh->qh_dma); } @@ -127,6 +143,10 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci) { + if (ehci->async) + qh_put (ehci, ehci->async); + ehci->async = 0; + /* PCI consistent memory and pools */ if (ehci->qtd_pool) pci_pool_destroy (ehci->qtd_pool); @@ -169,21 +189,21 @@ 4096 /* can't cross 4K */, flags); if (!ehci->qtd_pool) { - dbg ("no qtd pool"); - ehci_mem_cleanup (ehci); - return -ENOMEM; + goto fail; } - /* QH for control/bulk/intr transfers */ + /* QHs for control/bulk/intr transfers */ ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev, sizeof (struct ehci_qh), 32 /* byte alignment (for hw parts) */, 4096 /* can't cross 4K */, flags); if (!ehci->qh_pool) { - dbg ("no qh pool"); - ehci_mem_cleanup (ehci); - return -ENOMEM; + goto fail; + } + ehci->async = ehci_qh_alloc (ehci, flags); + if (!ehci->async) { + goto fail; } /* ITD for high speed ISO transfers */ @@ -193,9 +213,7 @@ 4096 /* can't cross 4K */, flags); if (!ehci->itd_pool) { - dbg ("no itd pool"); - ehci_mem_cleanup (ehci); - return -ENOMEM; + goto fail; } /* SITD for full/low speed split ISO transfers */ @@ -205,9 +223,7 @@ 4096 /* can't cross 4K */, flags); if (!ehci->sitd_pool) { - dbg ("no sitd pool"); - ehci_mem_cleanup (ehci); - return -ENOMEM; + goto fail; } /* Hardware periodic table */ @@ -216,9 +232,7 @@ ehci->periodic_size * sizeof (u32), &ehci->periodic_dma); if (ehci->periodic == 0) { - dbg ("no hw periodic table"); - ehci_mem_cleanup (ehci); - return -ENOMEM; + goto fail; } for (i = 0; i < ehci->periodic_size; i++) ehci->periodic [i] = EHCI_LIST_END; @@ -226,11 +240,14 @@ /* software shadow of hardware table */ ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags); if (ehci->pshadow == 0) { - dbg ("no shadow periodic table"); - ehci_mem_cleanup (ehci); - return -ENOMEM; + goto fail; } memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *)); return 0; + +fail: + ehci_dbg (ehci, "couldn't init memory\n"); + ehci_mem_cleanup (ehci); + return -ENOMEM; } diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci-q.c Mon Jan 6 11:32:04 2003 @@ -73,11 +73,6 @@ qtd->hw_token = cpu_to_le32 ((count << 16) | token); qtd->length = count; -#if 0 - vdbg (" qtd_fill %p, token %8x bytes %d dma %x", - qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]); -#endif - return count; } @@ -85,11 +80,12 @@ /* update halted (but potentially linked) qh */ -static inline void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd) +static inline void +qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { qh->hw_current = 0; qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); - qh->hw_alt_next = EHCI_LIST_END; + qh->hw_alt_next = ehci->async->hw_alt_next; /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); @@ -98,14 +94,29 @@ /*-------------------------------------------------------------------------*/ -static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token) +#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) + +static inline void qtd_copy_status ( + struct ehci_hcd *ehci, + struct urb *urb, + size_t length, + u32 token +) { /* count IN/OUT bytes, not SETUP (even short packets) */ if (likely (QTD_PID (token) != 2)) urb->actual_length += length - QTD_LENGTH (token); /* don't modify error codes */ - if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) { + if (unlikely (urb->status != -EINPROGRESS)) + return; + + /* force cleanup after short read; not always an error */ + if (unlikely (IS_SHORT_READ (token))) + urb->status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ urb->status = -EOVERFLOW; @@ -130,8 +141,9 @@ else /* unknown */ urb->status = -EPROTO; - dbg ("ep %d-%s qtd token %08x --> status %d", - /* devpath */ + ehci_vdbg (ehci, + "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice (urb->pipe), usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", token, urb->status); @@ -158,7 +170,8 @@ } } -static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) +static void +ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) { #ifdef INTR_AUTOMAGIC struct urb *resubmit = 0; @@ -185,27 +198,31 @@ #endif } qh_put (ehci, qh); - urb->hcpriv = 0; } - if (likely (urb->status == -EINPROGRESS)) { - if (urb->actual_length != urb->transfer_buffer_length - && (urb->transfer_flags & URB_SHORT_NOT_OK)) - urb->status = -EREMOTEIO; - else + spin_lock (&urb->lock); + urb->hcpriv = 0; + switch (urb->status) { + case -EINPROGRESS: /* success */ + urb->status = 0; + default: /* fault */ + COUNT (ehci->stats.complete); + break; + case -EREMOTEIO: /* fault or normal */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) urb->status = 0; - } - - if (likely (urb->status == 0)) COUNT (ehci->stats.complete); - else if (urb->status == -ECONNRESET || urb->status == -ENOENT) + break; + case -ECONNRESET: /* canceled */ + case -ENOENT: COUNT (ehci->stats.unlink); - else - COUNT (ehci->stats.error); + break; + } + spin_unlock (&urb->lock); /* complete() can reenter this HCD */ spin_unlock (&ehci->lock); - usb_hcd_giveback_urb (&ehci->hcd, urb); + usb_hcd_giveback_urb (&ehci->hcd, urb, regs); #ifdef INTR_AUTOMAGIC if (resubmit && ((urb->status == -ENOENT) @@ -235,139 +252,115 @@ * Chases up to qh->hw_current. Returns number of completions called, * indicating how much "real" work we did. */ +#define HALT_BIT cpu_to_le32(QTD_STS_HALT) static unsigned -qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) +qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) { - struct ehci_qtd *qtd, *last; - struct list_head *next, *qtd_list = &qh->qtd_list; - int unlink = 0, halted = 0; + struct ehci_qtd *last = 0; + struct list_head *entry, *tmp; + int stopped = 0; unsigned count = 0; - if (unlikely (list_empty (qtd_list))) + if (unlikely (list_empty (&qh->qtd_list))) return count; - /* scan QTDs till end of list, or we reach an active one */ - for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list), - last = 0, next = 0; - next != qtd_list; - last = qtd, qtd = list_entry (next, - struct ehci_qtd, qtd_list)) { - struct urb *urb = qtd->urb; + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe (entry, tmp, &qh->qtd_list) { + struct ehci_qtd *qtd; + struct urb *urb; u32 token = 0; + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + /* clean up any state from previous QTD ...*/ if (last) { if (likely (last->urb != urb)) { - ehci_urb_done (ehci, last->urb); + ehci_urb_done (ehci, last->urb, regs); count++; } - - /* qh overlays can have HC's old cached copies of - * next qtd ptrs, if an URB was queued afterwards. - */ - if (cpu_to_le32 (last->qtd_dma) == qh->hw_current - && last->hw_next != qh->hw_qtd_next) { - qh->hw_alt_next = last->hw_alt_next; - qh->hw_qtd_next = last->hw_next; - COUNT (ehci->stats.qpatch); - } - ehci_qtd_free (ehci, last); last = 0; } - next = qtd->qtd_list.next; - /* QTDs at tail may be active if QH+HC are running, - * or when unlinking some urbs queued to this QH - */ + /* hardware copies qtd out of qh overlay */ + rmb (); token = le32_to_cpu (qtd->hw_token); - halted = halted - || (__constant_cpu_to_le32 (QTD_STS_HALT) - & qh->hw_token) != 0 - || (ehci->hcd.state == USB_STATE_HALT) - || (qh->qh_state == QH_STATE_IDLE); - - // FIXME Remove the automagic unlink mode. - // Drivers can now clean up safely; it's their job. - // - // FIXME Removing it should fix the short read scenarios - // with "huge" urb data (more than one 16+KByte td) with - // the short read someplace other than the last data TD. - // Except the control case: 'retrigger' status ACKs. - - /* fault: unlink the rest, since this qtd saw an error? */ - if (unlikely ((token & QTD_STS_HALT) != 0)) { - unlink = 1; - /* status copied below */ - - /* QH halts only because of fault (above) or unlink (here). */ - } else if (unlikely (halted != 0)) { - - /* unlinking everything because of HC shutdown? */ - if (ehci->hcd.state == USB_STATE_HALT) { - unlink = 1; - - /* explicit unlink, maybe starting here? */ - } else if (qh->qh_state == QH_STATE_IDLE - && (urb->status == -ECONNRESET - || urb->status == -ESHUTDOWN - || urb->status == -ENOENT)) { - unlink = 1; - - /* QH halted to unlink urbs _after_ this? */ - } else if (!unlink && (token & QTD_STS_ACTIVE) != 0) { - qtd = 0; - continue; - } + stopped = stopped + || (qh->qh_state == QH_STATE_IDLE) + || (HALT_BIT & qh->hw_token) != 0 + || (ehci->hcd.state == USB_STATE_HALT); + + /* always clean up qtds the hc de-activated */ + if ((token & QTD_STS_ACTIVE) == 0) { + + /* magic dummy for short reads; won't advance */ + if (IS_SHORT_READ (token) + && !(token & QTD_STS_HALT) + && ehci->async->hw_alt_next + == qh->hw_alt_next) + goto halt; - /* unlink the rest? once we start unlinking, after - * a fault or explicit unlink, we unlink all later - * urbs. usb spec requires that for faults... - */ - if (unlink && urb->status == -EINPROGRESS) - urb->status = -ECONNRESET; + /* stop scanning when we reach qtds the hc is using */ + } else if (likely (!stopped)) { + last = 0; + break; - /* Else QH is active, so we must not modify QTDs - * that HC may be working on. No more qtds to check. - */ - } else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) { - next = qtd_list; - qtd = 0; - continue; + } else { + /* ignore active qtds unless some previous qtd + * for the urb faulted (including short read) or + * its urb was canceled. we may patch qh or qtds. + */ + if ((token & QTD_STS_ACTIVE) + && urb->status == -EINPROGRESS) { + last = 0; + continue; + } + if ((HALT_BIT & qh->hw_token) == 0) { +halt: + qh->hw_token |= HALT_BIT; + wmb (); + stopped = 1; + } } - + + /* remove it from the queue */ spin_lock (&urb->lock); - qtd_copy_status (urb, qtd->length, token); + qtd_copy_status (ehci, urb, qtd->length, token); spin_unlock (&urb->lock); + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry (qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } list_del (&qtd->qtd_list); - -#if 0 - if (urb->status == -EINPROGRESS) - vdbg (" qtd %p ok, urb %p, token %8x, len %d", - qtd, urb, token, urb->actual_length); - else - vdbg ("urb %p status %d, qtd %p, token %8x, len %d", - urb, urb->status, qtd, token, - urb->actual_length); -#endif + last = qtd; } /* last urb's completion might still need calling */ if (likely (last != 0)) { - ehci_urb_done (ehci, last->urb); + ehci_urb_done (ehci, last->urb, regs); count++; ehci_qtd_free (ehci, last); } - /* reactivate queue after error and driver's cleanup */ - if (unlikely (halted && !list_empty (qtd_list))) { - qh_update (qh, list_entry (qtd_list->next, - struct ehci_qtd, qtd_list)); + /* update qh after fault cleanup */ + if (unlikely ((HALT_BIT & qh->hw_token) != 0)) { + qh_update (ehci, qh, + list_empty (&qh->qtd_list) + ? qh->dummy + : list_entry (qh->qtd_list.next, + struct ehci_qtd, qtd_list)); } return count; } +#undef HALT_BIT /*-------------------------------------------------------------------------*/ @@ -413,7 +406,6 @@ qtd = ehci_qtd_alloc (ehci, flags); if (unlikely (!qtd)) return 0; - qtd_prev = 0; list_add_tail (&qtd->qtd_list, head); qtd->urb = urb; @@ -421,6 +413,8 @@ token |= (EHCI_TUNE_CERR << 10); /* for split transactions, SplitXState initialized to zero */ + len = urb->transfer_buffer_length; + is_input = usb_pipein (urb->pipe); if (usb_pipecontrol (urb->pipe)) { /* SETUP pid */ qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), @@ -436,15 +430,14 @@ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); - if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + if (len > 0 && is_input + && !(urb->transfer_flags & URB_SHORT_NOT_OK)) status_patch = 1; } /* * data transfer stage: buffer setup */ - len = urb->transfer_buffer_length; - is_input = usb_pipein (urb->pipe); if (likely (len > 0)) buf = urb->transfer_dma; else @@ -464,7 +457,6 @@ for (;;) { int this_qtd_len; - qtd->urb = urb; this_qtd_len = qtd_fill (qtd, buf, len, token); len -= this_qtd_len; buf += this_qtd_len; @@ -497,7 +489,7 @@ token ^= 0x0100; /* "in" <--> "out" */ token |= QTD_TOGGLE; /* force DATA1 */ } else if (usb_pipebulk (urb->pipe) - && (urb->transfer_flags & USB_ZERO_PACKET) + && (urb->transfer_flags & URB_ZERO_PACKET) && !(urb->transfer_buffer_length % maxpacket)) { one_more = 1; } @@ -576,10 +568,9 @@ * there are additional complications: c-mask, maybe FSTNs. */ static struct ehci_qh * -ehci_qh_make ( +qh_make ( struct ehci_hcd *ehci, struct urb *urb, - struct list_head *qtd_list, int flags ) { struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); @@ -622,7 +613,6 @@ if (qh->period < 1) { dbg ("intr period %d uframes, NYET!", urb->interval); - qh = 0; goto done; } } else { @@ -683,27 +673,20 @@ break; default: dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); - return 0; +done: + qh_put (ehci, qh); + return 0; } /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + /* init as halted, toggle clear, advance to dummy */ qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_le32 (info1); qh->hw_info2 = cpu_to_le32 (info2); - - /* initialize sw and hw queues with these qtds */ - if (!list_empty (qtd_list)) { - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); - } else { - qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END; - } - - /* initialize data toggle state */ - clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh); - -done: + qh_update (ehci, qh, qh->dummy); + qh->hw_token = cpu_to_le32 (QTD_STS_HALT); + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); return qh; } #undef hb_mult @@ -716,33 +699,35 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { u32 dma = QH_NEXT (qh->qh_dma); - struct ehci_qh *q; + struct ehci_qh *head; - if (unlikely (!(q = ehci->async))) { + /* (re)start the async schedule? */ + head = ehci->async; + if (ehci->async_idle) + del_timer (&ehci->watchdog); + if (!head->qh_next.qh) { u32 cmd = readl (&ehci->regs->command); - /* in case a clear of CMD_ASE didn't take yet */ - (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); - - qh->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD); /* [4.8] */ - qh->qh_next.qh = qh; - qh->hw_next = dma; - wmb (); - ehci->async = qh; - writel ((u32)qh->qh_dma, &ehci->regs->async_next); - cmd |= CMD_ASE | CMD_RUN; - writel (cmd, &ehci->regs->command); - ehci->hcd.state = USB_STATE_RUNNING; - /* posted write need not be known to HC yet ... */ - } else { - /* splice right after "start" of ring */ - qh->hw_info1 &= ~__constant_cpu_to_le32 (QH_HEAD); /* [4.8] */ - qh->qh_next = q->qh_next; - qh->hw_next = q->hw_next; - wmb (); - q->qh_next.qh = qh; - q->hw_next = dma; + if (!(cmd & CMD_ASE)) { + /* in case a clear of CMD_ASE didn't take yet */ + (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); + cmd |= CMD_ASE | CMD_RUN; + writel (cmd, &ehci->regs->command); + ehci->hcd.state = USB_STATE_RUNNING; + /* posted write need not be known to HC yet ... */ + } } + + qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT); + + /* splice right after start */ + qh->qh_next = head->qh_next; + qh->hw_next = head->hw_next; + wmb (); + + head->qh_next.qh = qh; + head->hw_next = dma; + qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ @@ -768,6 +753,11 @@ struct ehci_qh *qh = 0; qh = (struct ehci_qh *) *ptr; + if (unlikely (qh == 0)) { + /* can't sleep here, we have ehci->lock... */ + qh = qh_make (ehci, urb, SLAB_ATOMIC); + *ptr = qh; + } if (likely (qh != 0)) { struct ehci_qtd *qtd; @@ -777,75 +767,94 @@ qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - /* maybe patch the qh used for set_address */ - if (unlikely (epnum == 0 - && le32_to_cpu (qh->hw_info1 & 0x7f) == 0)) - qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe)); - - /* append to tds already queued to this qh? */ - if (unlikely (!list_empty (&qh->qtd_list) && qtd)) { - struct ehci_qtd *last_qtd; - int short_rx = 0; - u32 hw_next; - - /* update the last qtd's "next" pointer */ - // dbg_qh ("non-empty qh", ehci, qh); - last_qtd = list_entry (qh->qtd_list.prev, - struct ehci_qtd, qtd_list); - hw_next = QTD_NEXT (qtd->qtd_dma); - last_qtd->hw_next = hw_next; - - /* previous urb allows short rx? maybe optimize. */ - if (!(last_qtd->urb->transfer_flags & URB_SHORT_NOT_OK) - && (epnum & 0x10)) { - // only the last QTD for now - last_qtd->hw_alt_next = hw_next; - short_rx = 1; - } - - /* Adjust any old copies in qh overlay too. - * Interrupt code must cope with case of HC having it - * cached, and clobbering these updates. - * ... complicates getting rid of extra interrupts! - * (Or: use dummy td, so cache always stays valid.) - */ - if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) { - wmb (); - qh->hw_qtd_next = hw_next; - if (short_rx) - qh->hw_alt_next = hw_next - | (qh->hw_alt_next & 0x1e); - vdbg ("queue to qh %p, patch", qh); + /* control qh may need patching after enumeration */ + if (unlikely (epnum == 0)) { + /* set_address changes the address */ + if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0) + qh->hw_info1 |= cpu_to_le32 ( + usb_pipedevice (urb->pipe)); + + /* for full speed, ep0 maxpacket can grow */ + else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) { + u32 info, max; + + info = le32_to_cpu (qh->hw_info1); + max = urb->dev->descriptor.bMaxPacketSize0; + if (max > (0x07ff & (info >> 16))) { + info &= ~(0x07ff << 16); + info |= max << 16; + qh->hw_info1 = cpu_to_le32 (info); + } } + } - /* no URB queued */ - } else { - // dbg_qh ("empty qh", ehci, qh); + /* FIXME: changing config or interface setting is not + * supported yet. preferred fix is for usbcore to tell + * us to clear out each endpoint's state, but... + */ - /* NOTE: we already canceled any queued URBs - * when the endpoint halted. + /* usb_clear_halt() means qh data toggle gets reset */ + if (unlikely (!usb_gettoggle (urb->dev, + (epnum & 0x0f), !(epnum & 0x10))) + && !usb_pipecontrol (urb->pipe)) { + /* "never happens": drivers do stall cleanup right */ + if (qh->qh_state != QH_STATE_IDLE + && (cpu_to_le32 (QTD_STS_HALT) + & qh->hw_token) == 0) + ehci_warn (ehci, "clear toggle dev%d " + "ep%d%s: not idle\n", + usb_pipedevice (urb->pipe), + epnum & 0x0f, + usb_pipein (urb->pipe) + ? "in" : "out"); + /* else we know this overlay write is safe */ + clear_toggle (urb->dev, + epnum & 0x0f, !(epnum & 0x10), qh); + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_completions() usually modify the overlay. + */ + if (likely (qtd != 0)) { + struct ehci_qtd *dummy; + dma_addr_t dma; + u32 token; + + /* to avoid racing the HC, use the dummy td instead of + * the first td of our list (becomes new dummy). both + * tds stay deactivated until we're done, when the + * HC is allowed to fetch the old dummy (4.10.2). */ + token = qtd->hw_token; + qtd->hw_token = 0; + wmb (); + dummy = qh->dummy; + + dma = dummy->qtd_dma; + *dummy = *qtd; + dummy->qtd_dma = dma; + + list_del (&qtd->qtd_list); + list_add (&dummy->qtd_list, qtd_list); + __list_splice (qtd_list, qh->qtd_list.prev); + + ehci_qtd_init (qtd, qtd->qtd_dma); + qtd->hw_alt_next = ehci->async->hw_alt_next; + qh->dummy = qtd; + + /* hc must see the new dummy at list end */ + dma = qtd->qtd_dma; + qtd = list_entry (qh->qtd_list.prev, + struct ehci_qtd, qtd_list); + qtd->hw_next = QTD_NEXT (dma); - /* usb_clear_halt() means qh data toggle gets reset */ - if (unlikely (!usb_gettoggle (urb->dev, - (epnum & 0x0f), - !(epnum & 0x10)))) { - clear_toggle (urb->dev, - epnum & 0x0f, !(epnum & 0x10), qh); - } - if (qtd) - qh_update (qh, qtd); - } - list_splice (qtd_list, qh->qtd_list.prev); + /* let the hc process these next qtds */ + wmb (); + dummy->hw_token = token; - } else { - /* can't sleep here, we have ehci->lock... */ - qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - // if (qh) dbg_qh ("new qh", ehci, qh); - *ptr = qh; + urb->hcpriv = qh_get (qh); + } } - if (qh) - urb->hcpriv = qh_get (qh); return qh; } @@ -897,27 +906,39 @@ /*-------------------------------------------------------------------------*/ /* the async qh for the qtds being reclaimed are now unlinked from the HC */ -/* caller must not own ehci->lock */ -static void end_unlink_async (struct ehci_hcd *ehci) +static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) { struct ehci_qh *qh = ehci->reclaim; del_timer (&ehci->watchdog); + qh->hw_next = cpu_to_le32 (qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = 0; qh_put (ehci, qh); // refcount from reclaim ehci->reclaim = 0; ehci->reclaim_ready = 0; - qh_completions (ehci, qh); + qh_completions (ehci, qh, regs); if (!list_empty (&qh->qtd_list) && HCD_IS_RUNNING (ehci->hcd.state)) qh_link_async (ehci, qh); - else + else { qh_put (ehci, qh); // refcount from async list + + /* it's not free to turn the async schedule on/off; leave it + * active but idle for a while once it empties. + */ + if (HCD_IS_RUNNING (ehci->hcd.state) + && ehci->async->qh_next.qh == 0 + && !timer_pending (&ehci->watchdog)) { + ehci->async_idle = 1; + mod_timer (&ehci->watchdog, + jiffies + EHCI_ASYNC_JIFFIES); + } + } } /* makes sure the async qh will become idle */ @@ -930,7 +951,6 @@ #ifdef DEBUG if (ehci->reclaim - || !ehci->async || qh->qh_state != QH_STATE_LINKED #ifdef CONFIG_SMP // this macro lies except on SMP compiles @@ -940,49 +960,33 @@ BUG (); #endif - qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh = qh_get (qh); - - // dbg_qh ("start unlink", ehci, qh); - - /* Remove the last QH (qhead)? Stop async schedule first. */ - if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) { + /* stop async schedule right now? */ + if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ if (ehci->hcd.state != USB_STATE_HALT) { writel (cmd & ~CMD_ASE, &ehci->regs->command); - (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); -#if 0 - // one VT8235 system wants to die with STS_FATAL - // unless this qh is leaked here. others seem ok... - qh = qh_get (qh); - dbg_qh ("async/off", ehci, qh); -#endif + wmb (); + // handshake later, if we need to } - qh->qh_next.qh = ehci->async = 0; - - ehci->reclaim_ready = 1; - tasklet_schedule (&ehci->tasklet); return; } - if (unlikely (ehci->hcd.state == USB_STATE_HALT)) { - ehci->reclaim_ready = 1; - tasklet_schedule (&ehci->tasklet); - return; - } + qh->qh_state = QH_STATE_UNLINK; + ehci->reclaim = qh = qh_get (qh); prev = ehci->async; - while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async) + while (prev->qh_next.qh != qh) prev = prev->qh_next.qh; - if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) { - ehci->async = prev; - prev->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD); - } prev->hw_next = qh->hw_next; prev->qh_next = qh->qh_next; wmb (); + if (unlikely (ehci->hcd.state == USB_STATE_HALT)) { + end_unlink_async (ehci, NULL); + return; + } + ehci->reclaim_ready = 0; cmd |= CMD_IAAD; writel (cmd, &ehci->regs->command); @@ -994,56 +998,43 @@ /*-------------------------------------------------------------------------*/ static void -scan_async (struct ehci_hcd *ehci) +scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) { struct ehci_qh *qh; unsigned count; rescan: - qh = ehci->async; + qh = ehci->async->qh_next.qh; count = 0; if (likely (qh != 0)) { do { /* clean any finished work for this qh */ if (!list_empty (&qh->qtd_list)) { - // dbg_qh ("scan_async", ehci, qh); - qh = qh_get (qh); + int temp; - /* concurrent unlink could happen here */ - count += qh_completions (ehci, qh); + /* unlinks could happen here; completion + * reporting drops the lock. + */ + qh = qh_get (qh); + temp = qh_completions (ehci, qh, regs); qh_put (ehci, qh); + if (temp != 0) { + count += temp; + goto rescan; + } } /* unlink idle entries, reducing HC PCI usage as - * well as HCD schedule-scanning costs. removing - * the last qh is deferred, since it's costly. + * well as HCD schedule-scanning costs. * * FIXME don't unlink idle entries so quickly; it * can penalize (common) half duplex protocols. */ if (list_empty (&qh->qtd_list) && !ehci->reclaim) { - if (qh->qh_next.qh != qh) { - // dbg ("irq/empty"); - start_unlink_async (ehci, qh); - } else if (!timer_pending (&ehci->watchdog)) { - /* can't use IAA for last entry */ - ehci->async_idle = 1; - mod_timer (&ehci->watchdog, - 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; + start_unlink_async (ehci, qh); } qh = qh->qh_next.qh; - if (!qh) /* unlinked? */ - goto rescan; - } while (qh != ehci->async); + } while (qh); } } diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci-sched.c Mon Jan 6 11:32:04 2003 @@ -190,7 +190,7 @@ /* posted write ... PSS happens later */ ehci->hcd.state = USB_STATE_RUNNING; - /* make sure tasklet scans these */ + /* make sure ehci_work scans these */ ehci->next_uframe = readl (&ehci->regs->frame_index) % (ehci->periodic_size << 3); return 0; @@ -495,7 +495,8 @@ intr_complete ( struct ehci_hcd *ehci, unsigned frame, - struct ehci_qh *qh + struct ehci_qh *qh, + struct pt_regs *regs ) { unsigned count; @@ -509,7 +510,7 @@ } /* handle any completions */ - count = qh_completions (ehci, qh); + count = qh_completions (ehci, qh, regs); if (unlikely (list_empty (&qh->qtd_list))) intr_deschedule (ehci, qh, 0); @@ -738,7 +739,7 @@ } /* explicit start frame? */ - if (!(urb->transfer_flags & USB_ISO_ASAP)) { + if (!(urb->transfer_flags & URB_ISO_ASAP)) { unsigned temp; /* sanity check: must be in range */ @@ -867,7 +868,8 @@ itd_complete ( struct ehci_hcd *ehci, struct ehci_itd *itd, - unsigned uframe + unsigned uframe, + struct pt_regs *regs ) { struct urb *urb = itd->urb; struct usb_iso_packet_descriptor *desc; @@ -922,7 +924,7 @@ /* complete() can reenter this HCD */ spin_unlock (&ehci->lock); - usb_hcd_giveback_urb (&ehci->hcd, urb); + usb_hcd_giveback_urb (&ehci->hcd, urb, regs); spin_lock (&ehci->lock); /* defer stopping schedule; completion can submit */ @@ -973,7 +975,7 @@ /*-------------------------------------------------------------------------*/ static void -scan_periodic (struct ehci_hcd *ehci) +scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) { unsigned frame, clock, now_uframe, mod; unsigned count = 0; @@ -999,14 +1001,6 @@ u32 type, *hw_p; 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: /* scan schedule to _before_ current frame index */ if (frame == clock) @@ -1031,7 +1025,7 @@ temp = q.qh->qh_next; type = Q_NEXT_TYPE (q.qh->hw_next); count += intr_complete (ehci, frame, - qh_get (q.qh)); + qh_get (q.qh), regs); qh_put (ehci, q.qh); q = temp; break; @@ -1064,7 +1058,7 @@ /* might free q.itd ... */ count += itd_complete (ehci, - temp.itd, uf); + temp.itd, uf, regs); break; } } diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h --- a/drivers/usb/hcd/ehci.h Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd/ehci.h Mon Jan 6 11:32:04 2003 @@ -31,11 +31,6 @@ /* 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: @@ -70,9 +65,6 @@ int next_uframe; /* scan periodic, start here */ unsigned periodic_sched; /* periodic activity count */ - /* deferred work from IRQ, etc */ - struct tasklet_struct tasklet; - /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; @@ -311,6 +303,7 @@ dma_addr_t qh_dma; /* address of qh */ union ehci_shadow qh_next; /* ptr to qh; or periodic */ struct list_head qtd_list; /* sw qtd list */ + struct ehci_qtd *dummy; atomic_t refcount; diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c --- a/drivers/usb/hcd.c Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd.c Mon Jan 6 11:32:04 2003 @@ -437,7 +437,7 @@ } /* any errors get returned through the urb completion */ - usb_hcd_giveback_urb (hcd, urb); + usb_hcd_giveback_urb (hcd, urb, 0); return 0; } @@ -518,7 +518,7 @@ urb->hcpriv = 0; spin_unlock_irqrestore (&urb->lock, flags); - usb_hcd_giveback_urb (hcd, urb); + usb_hcd_giveback_urb (hcd, urb, 0); } } @@ -553,7 +553,7 @@ spin_unlock_irqrestore (&hcd_data_lock, flags); /* we rely on RH callback code not unlinking its URB! */ - usb_hcd_giveback_urb (hcd, urb); + usb_hcd_giveback_urb (hcd, urb, 0); } /*-------------------------------------------------------------------------*/ @@ -663,7 +663,8 @@ hcd->driver = driver; hcd->description = driver->description; hcd->pdev = dev; - info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name); + printk (KERN_INFO "%s %s: %s\n", + hcd->description, dev->slot_name, dev->name); #ifndef __sparc__ sprintf (buf, "%d", dev->irq); @@ -682,7 +683,8 @@ hcd->regs = base; hcd->region = region; - info ("irq %s, %s %p", bufp, + printk (KERN_INFO "%s %s: irq %s, %s %p\n", + hcd->description, dev->slot_name, bufp, (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", base); @@ -739,7 +741,8 @@ hcd = pci_get_drvdata(dev); if (!hcd) return; - info ("remove: %s, state %x", hcd->bus->bus_name, hcd->state); + printk (KERN_INFO "%s %s: remove state %x\n", + hcd->description, dev->slot_name, hcd->state); if (in_interrupt ()) BUG (); @@ -817,7 +820,8 @@ int retval; hcd = pci_get_drvdata(dev); - info ("suspend %s to state %d", hcd->bus->bus_name, state); + printk (KERN_INFO "%s %s: suspend to state %d\n", + hcd->description, dev->slot_name, state); pci_save_state (dev, hcd->pci_state); @@ -846,7 +850,8 @@ int retval; hcd = pci_get_drvdata(dev); - info ("resume %s", hcd->bus->bus_name); + printk (KERN_INFO "%s %s: resume\n", + hcd->description, dev->slot_name); /* guard against multiple resumes (APM bug?) */ atomic_inc (&hcd->resume_count); @@ -1309,7 +1314,7 @@ if (usb_pipetype (urb->pipe) == PIPE_INTERRUPT) retval = -EAGAIN; else - retval = -EINVAL; + retval = -EBUSY; goto done; } @@ -1349,8 +1354,6 @@ if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED)) && HCD_IS_RUNNING (hcd->state) && !retval) { - dbg ("%s: wait for giveback urb %p", - hcd->bus->bus_name, urb); wait_for_completion (&splice.done); } else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) { return -EINPROGRESS; @@ -1428,7 +1431,7 @@ if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ return; - hcd->driver->irq (hcd); + hcd->driver->irq (hcd, r); if (hcd->state != start && hcd->state == USB_STATE_HALT) hc_died (hcd); } @@ -1439,6 +1442,7 @@ * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB * @urb: urb being returned to the USB device driver. + * @regs: saved hardware registers (ignored on 2.4 kernels) * Context: in_interrupt() * * This hands the URB from HCD to its USB device driver, using its @@ -1456,7 +1460,7 @@ * ISO streaming functionality can be achieved by having completion handlers * re-queue URBs. Such explicit queuing doesn't discard error reports. */ -void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) +void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) { urb_unlink (urb); @@ -1465,10 +1469,6 @@ // It would catch exit/unlink paths for all urbs, but non-exit // completions for periodic urbs need hooks inside the HCD. // hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev) - - if (urb->status) - dbg ("giveback urb %p status %d len %d", - urb, urb->status, urb->actual_length); // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag if (usb_pipecontrol (urb->pipe)) diff -Nru a/drivers/usb/hcd.h b/drivers/usb/hcd.h --- a/drivers/usb/hcd.h Mon Jan 6 11:32:04 2003 +++ b/drivers/usb/hcd.h Mon Jan 6 11:32:04 2003 @@ -103,7 +103,7 @@ const char *description; /* "ehci-hcd" etc */ /* irq handler */ - void (*irq) (struct usb_hcd *hcd); + void (*irq) (struct usb_hcd *hcd, struct pt_regs *regs); int flags; #define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ @@ -148,7 +148,8 @@ char *buf, u16 wLength); }; -extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb); +extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, + struct pt_regs *regs); #ifdef CONFIG_PCI @@ -282,3 +283,5 @@ usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe) { } +#define URB_ZERO_PACKET USB_ZERO_PACKET +#define URB_ISO_ASAP USB_ISO_ASAP