# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.687 -> 1.688 # drivers/usb/hcd/ehci-q.c 1.6 -> 1.7 # drivers/usb/hcd/ehci-hub.c 1.2 -> 1.3 # drivers/usb/hcd/ehci.h 1.2 -> 1.3 # drivers/usb/hcd/ehci-sched.c 1.5 -> 1.6 # drivers/usb/hcd/ehci-hcd.c 1.6 -> 1.7 # drivers/usb/hcd/ehci-dbg.c 1.1 -> 1.2 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/09/26 david-b@pacbell.net 1.688 # [PATCH] EHCI matching 2.5.38bk3 # # This basically brings the 2.4 code into sync with the current # 2.5 code. There've been a number of bugfixes and other changes, # code cleanup and so on, so I'll just summarize some of the # functional changes: # # - Fixes races and other qh unlink problems # # - Copes with rude eject from cardbus # # - Supports USB 1.1 interrupt transactions # through USB 2.0 hubs # # - Should work with VIA VT8235 (KT333/KT400) # # - Filed down a lot of rough ends # -------------------------------------------- # diff -Nru a/drivers/usb/hcd/ehci-dbg.c b/drivers/usb/hcd/ehci-dbg.c --- a/drivers/usb/hcd/ehci-dbg.c Mon Sep 30 10:47:10 2002 +++ b/drivers/usb/hcd/ehci-dbg.c Mon Sep 30 10:47:10 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -26,8 +26,10 @@ #ifdef DEBUG -/* check the values in the HCSPARAMS register - host controller structural parameters */ -/* see EHCI 0.95 Spec, Table 2-4 for each value */ +/* check the values in the HCSPARAMS register + * (host controller _Structural_ parameters) + * see EHCI spec, Table 2-4 for each value + */ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) { u32 params = readl (&ehci->caps->hcs_params); @@ -55,7 +57,7 @@ strcat(buf, tmp); } dbg ("%s: %s portroute %s", - ehci->hcd.bus_name, label, + hcd_to_bus (&ehci->hcd)->bus_name, label, buf); } } @@ -67,25 +69,27 @@ #ifdef DEBUG -/* check the values in the HCCPARAMS register - host controller capability parameters */ -/* see EHCI 0.95 Spec, Table 2-5 for each value */ +/* check the values in the HCCPARAMS register + * (host controller _Capability_ parameters) + * see EHCI Spec, Table 2-5 for each value + * */ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) { 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 %d", + dbg ("%s extended capabilities at pci %2x", label, HCC_EXT_CAPS (params)); } if (HCC_ISOC_CACHE (params)) { - dbg ("%s hcc_params 0x%04x caching frame %s%s%s", + dbg ("%s hcc_params %04x caching frame %s%s%s", 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 0x%04x caching %d uframes %s%s%s", + dbg ("%s hcc_params %04x caching %d uframes %s%s%s", label, params, HCC_ISOC_THRES (params), @@ -102,8 +106,8 @@ #ifdef DEBUG -#if 0 -static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +static void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) { dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label, qh, qh->hw_info1, qh->hw_info2, @@ -117,63 +121,462 @@ qh->hw_buf [4]); } } -#endif + +static int __attribute__((__unused__)) +dbg_status_buf (char *buf, unsigned len, char *label, u32 status) +{ + return snprintf (buf, len, + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __attribute__((__unused__)) +dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) +{ + return snprintf (buf, len, + "%s%sintrenable %02x%s%s%s%s%s%s", + label, label [0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} static const char *const fls_strings [] = { "1024", "512", "256", "??" }; +static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command) +{ + return snprintf (buf, len, + "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", + label, label [0] ? " " : "", command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings [(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int +dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: sig = "se0"; break; + case 1 << 10: sig = "k"; break; /* low speed */ + case 2 << 10: sig = "j"; break; + default: sig = "?"; break; + } + + return snprintf (buf, len, + "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", port, status, + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_OCC) ? " OCC" : "", + (status & PORT_OC) ? " OC" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : "" + ); +} + #else -#if 0 -static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} -#endif +static inline void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +{} + +static inline int __attribute__((__unused__)) +dbg_status_buf (char *buf, unsigned len, char *label, u32 status) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_command_buf (char *buf, unsigned len, char *label, u32 command) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) +{ return 0; } + #endif /* DEBUG */ /* functions have the "wrong" filename when they're output... */ +#define dbg_status(ehci, label, status) { \ + char _buf [80]; \ + dbg_status_buf (_buf, sizeof _buf, label, status); \ + dbg ("%s", _buf); \ +} + +#define dbg_cmd(ehci, label, command) { \ + char _buf [80]; \ + dbg_command_buf (_buf, sizeof _buf, label, command); \ + dbg ("%s", _buf); \ +} + +#define dbg_port(hcd, label, port, status) { \ + char _buf [80]; \ + dbg_port_buf (_buf, sizeof _buf, label, port, status); \ + dbg ("%s", _buf); \ +} + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILES + +static inline void create_debug_files (struct ehci_hcd *bus) { } +static inline void remove_debug_files (struct ehci_hcd *bus) { } + +#else + +/* troubleshooting help: expose state in driverfs */ + +#define speed_char(info1) ({ char tmp; \ + switch (info1 & (3 << 12)) { \ + case 0 << 12: tmp = 'f'; break; \ + case 1 << 12: tmp = 'l'; break; \ + case 2 << 12: tmp = 'h'; break; \ + default: tmp = '?'; break; \ + }; tmp; }) + +static void qh_lines (struct ehci_qh *qh, char **nextp, unsigned *sizep) +{ + u32 scratch; + struct list_head *entry; + struct ehci_qtd *td; + unsigned temp; + unsigned size = *sizep; + char *next = *nextp; + + scratch = cpu_to_le32p (&qh->hw_info1); + temp = snprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x", + qh, scratch & 0x007f, + speed_char (scratch), + (scratch >> 8) & 0x000f, + scratch, cpu_to_le32p (&qh->hw_info2)); + size -= temp; + next += temp; + + list_for_each (entry, &qh->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", + td, ({ char *tmp; + switch ((scratch>>8)&0x03) { + case 0: tmp = "out"; break; + case 1: tmp = "in"; break; + case 2: tmp = "setup"; break; + default: tmp = "?"; break; + } tmp;}), + (scratch >> 16) & 0x7fff, + scratch, + td->urb); + size -= temp; + next += temp; + } + + temp = snprintf (next, size, "\n"); + *sizep = size - temp; + *nextp = next + temp; +} + +static ssize_t +show_async (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size; + char *next; + struct ehci_qh *qh; + + if (off != 0) + return 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = count; + + /* dumps a snapshot of the async schedule. + * usually empty except for long-term bulk reads, or head. + * 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); + } + if (ehci->reclaim) { + temp = snprintf (next, size, "\nreclaim =\n"); + size -= temp; + next += temp; + + qh_lines (ehci->reclaim, &next, &size); + } + spin_unlock_irqrestore (&ehci->lock, flags); + + return count - size; +} +static DEVICE_ATTR (async, S_IRUGO, show_async, NULL); + +#define DBG_SCHED_LIMIT 64 + +static ssize_t +show_periodic (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + union ehci_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i, tag; + + if (off != 0) + return 0; + if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC))) + return 0; + seen_count = 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = count; + + temp = snprintf (next, size, "size = %d\n", ehci->periodic_size); + size -= temp; + next += temp; + + /* dump a snapshot of the periodic schedule. + * iso changes, interrupt usually doesn't. + */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < ehci->periodic_size; i++) { + p = ehci->pshadow [i]; + if (!p.ptr) + continue; + tag = Q_NEXT_TYPE (ehci->periodic [i]); + + temp = snprintf (next, size, "%4d: ", i); + size -= temp; + next += temp; + + do { + switch (tag) { + case Q_TYPE_QH: + temp = snprintf (next, size, " qh%d/%p", + p.qh->period, p.qh); + size -= temp; + next += temp; + for (temp = 0; temp < seen_count; temp++) { + if (seen [temp].ptr == p.ptr) + break; + } + /* show more info the first time around */ + if (temp == seen_count) { + u32 scratch = cpu_to_le32p ( + &p.qh->hw_info1); + + temp = snprintf (next, size, + " (%cs dev%d ep%d [%d/%d] %d)", + speed_char (scratch), + scratch & 0x007f, + (scratch >> 8) & 0x000f, + p.qh->usecs, p.qh->c_usecs, + scratch >> 16); + + /* FIXME TD info too */ + + if (seen_count < DBG_SCHED_LIMIT) + seen [seen_count++].qh = p.qh; + } else + temp = 0; + tag = Q_NEXT_TYPE (p.qh->hw_next); + p = p.qh->qh_next; + break; + case Q_TYPE_FSTN: + temp = snprintf (next, size, + " fstn-%8x/%p", p.fstn->hw_prev, + p.fstn); + tag = Q_NEXT_TYPE (p.fstn->hw_next); + p = p.fstn->fstn_next; + break; + case Q_TYPE_ITD: + temp = snprintf (next, size, + " itd/%p", p.itd); + tag = Q_NEXT_TYPE (p.itd->hw_next); + p = p.itd->itd_next; + break; + case Q_TYPE_SITD: + temp = snprintf (next, size, + " sitd/%p", p.sitd); + tag = Q_NEXT_TYPE (p.sitd->hw_next); + p = p.sitd->sitd_next; + break; + } + size -= temp; + next += temp; + } while (p.ptr); + + temp = snprintf (next, size, "\n"); + size -= temp; + next += temp; + } + spin_unlock_irqrestore (&ehci->lock, flags); + kfree (seen); + + return count - size; +} +static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); + +#undef DBG_SCHED_LIMIT + +static ssize_t +show_registers (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size, i; + char *next, scratch [80]; + static char fmt [] = "%*s\n"; + static char label [] = ""; + + if (off != 0) + return 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + + next = buf; + size = count; + + spin_lock_irqsave (&ehci->lock, flags); + + /* Capability Registers */ + i = readw (&ehci->caps->hci_version); + temp = snprintf (next, size, "EHCI %x.%02x, hcd state %d\n", + i >> 8, i & 0x0ff, ehci->hcd.state); + size -= temp; + next += temp; + + // FIXME interpret both types of params + i = readl (&ehci->caps->hcs_params); + temp = snprintf (next, size, "structural params 0x%08x\n", i); + size -= temp; + next += temp; + + i = readl (&ehci->caps->hcc_params); + temp = snprintf (next, size, "capability params 0x%08x\n", i); + size -= temp; + next += temp; + + /* Operational Registers */ + temp = dbg_status_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->status)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_command_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->command)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_intr_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->intr_enable)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = snprintf (next, size, "uframe %04x\n", + readl (&ehci->regs->frame_index)); + size -= temp; + next += temp; + + for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { + temp = dbg_port_buf (scratch, sizeof scratch, label, i, + readl (&ehci->regs->port_status [i])); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + } + + if (ehci->reclaim) { + temp = snprintf (next, size, "reclaim qh %p%s\n", + ehci->reclaim, + ehci->reclaim_ready ? " ready" : ""); + size -= 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); + + return count - size; +} +static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); + +static inline void create_debug_files (struct ehci_hcd *bus) +{ + device_create_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_registers); +} + +static inline void remove_debug_files (struct ehci_hcd *bus) +{ + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers); +} -#define dbg_status(ehci, label, status) \ - dbg ("%s status 0x%x%s%s%s%s%s%s%s%s%s%s", \ - label, status, \ - (status & STS_ASS) ? " Async" : "", \ - (status & STS_PSS) ? " Periodic" : "", \ - (status & STS_RECL) ? " Recl" : "", \ - (status & STS_HALT) ? " Halt" : "", \ - (status & STS_IAA) ? " IAA" : "", \ - (status & STS_FATAL) ? " FATAL" : "", \ - (status & STS_FLR) ? " FLR" : "", \ - (status & STS_PCD) ? " PCD" : "", \ - (status & STS_ERR) ? " ERR" : "", \ - (status & STS_INT) ? " INT" : "" \ - ) - -#define dbg_cmd(ehci, label, command) \ - dbg ("%s %x cmd %s=%d ithresh=%d%s%s%s%s period=%s%s %s", \ - label, command, \ - (command & CMD_PARK) ? "park" : "(park)", \ - CMD_PARK_CNT (command), \ - (command >> 16) & 0x3f, \ - (command & CMD_LRESET) ? " LReset" : "", \ - (command & CMD_IAAD) ? " IAAD" : "", \ - (command & CMD_ASE) ? " Async" : "", \ - (command & CMD_PSE) ? " Periodic" : "", \ - fls_strings [(command >> 2) & 0x3], \ - (command & CMD_RESET) ? " Reset" : "", \ - (command & CMD_RUN) ? "RUN" : "HALT" \ - ) - -#define dbg_port(hcd, label, port, status) \ - dbg ("%s port %d status 0x%x%s%s speed=%d%s%s%s%s%s%s%s%s%s", \ - label, port, status, \ - (status & PORT_OWNER) ? " OWNER" : "", \ - (status & PORT_POWER) ? " POWER" : "", \ - (status >> 10) & 3, \ - (status & PORT_RESET) ? " RESET" : "", \ - (status & PORT_SUSPEND) ? " SUSPEND" : "", \ - (status & PORT_RESUME) ? " RESUME" : "", \ - (status & PORT_OCC) ? " OCC" : "", \ - (status & PORT_OC) ? " OC" : "", \ - (status & PORT_PEC) ? " PEC" : "", \ - (status & PORT_PE) ? " PE" : "", \ - (status & PORT_CSC) ? " CSC" : "", \ - (status & PORT_CONNECT) ? " CONNECT" : "" \ - ) +#endif /* STUB_DEBUG_FILES */ diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Mon Sep 30 10:47:10 2002 +++ b/drivers/usb/hcd/ehci-hcd.c Mon Sep 30 10:47:10 2002 @@ -38,7 +38,13 @@ #endif #include + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) #include "../hcd.h" +#else +#include "../core/hcd.h" +#endif #include #include @@ -46,8 +52,6 @@ #include #include -//#undef KERN_DEBUG -//#define KERN_DEBUG "" /*-------------------------------------------------------------------------*/ @@ -55,21 +59,24 @@ * EHCI hc_driver implementation ... experimental, incomplete. * Based on the final 1.0 register interface specification. * - * There are lots of things to help out with here ... notably - * everything "periodic", and of course testing with all sorts - * of usb 2.0 devices and configurations. - * * USB 2.0 shows up in upcoming www.pcmcia.org technology. * 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 - * ... + * Contains additional contributions by: Brad Hards, Rory Bolt, and more. + * 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-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, + * clean up HC run state handshaking. + * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts + * 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other + * missing pieces: enabling 64bit dma, handoff from BIOS/SMM. * 2002-05-07 Some error path cleanups to report better errors; wmb(); * use non-CVS version id; better iso bandwidth claim. * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on @@ -85,14 +92,22 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2002-May-07" +#define DRIVER_VERSION "2002-Sep-23" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" +static const char hcd_name [] = "ehci-hcd"; + // #define EHCI_VERBOSE_DEBUG // #define have_split_iso +#ifdef DEBUG +#define EHCI_STATS +#endif + +#define INTR_AUTOMAGIC /* to be removed later in 2.5 */ + /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */ @@ -100,11 +115,20 @@ #define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ #define EHCI_TUNE_MULT_TT 1 +#define EHCI_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_ASYNC_JIFFIES (HZ/3) /* 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) /*-------------------------------------------------------------------------*/ @@ -115,42 +139,105 @@ /*-------------------------------------------------------------------------*/ /* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake (u32 *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl (ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay (1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* * hc states include: unknown, halted, ready, running * transitional states are messy just now * trying to avoid "running" unless urbs are active * a "ready" hc can be finishing prefetched work */ -/* halt a non-running controller */ -static void ehci_reset (struct ehci_hcd *ehci) +/* force HC to halt state from unknown (EHCI spec section 2.3) */ +static int ehci_halt (struct ehci_hcd *ehci) +{ + u32 temp = readl (&ehci->regs->status); + + if ((temp & STS_HALT) != 0) + return 0; + + temp = readl (&ehci->regs->command); + temp &= ~CMD_RUN; + writel (temp, &ehci->regs->command); + return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); +} + +/* reset a non-running (STS_HALT == 1) controller */ +static int ehci_reset (struct ehci_hcd *ehci) { u32 command = readl (&ehci->regs->command); command |= CMD_RESET; dbg_cmd (ehci, "reset", command); writel (command, &ehci->regs->command); - while (readl (&ehci->regs->command) & CMD_RESET) - continue; ehci->hcd.state = USB_STATE_HALT; + return handshake (&ehci->regs->command, CMD_RESET, 0, 250); } /* idle the controller (from running) */ static void ehci_ready (struct ehci_hcd *ehci) { - u32 command; + u32 temp; #ifdef DEBUG if (!HCD_IS_RUNNING (ehci->hcd.state)) BUG (); #endif - while (!(readl (&ehci->regs->status) & (STS_ASS | STS_PSS))) - udelay (100); - command = readl (&ehci->regs->command); - command &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); - writel (command, &ehci->regs->command); + /* wait for any schedule enables/disables to take effect */ + temp = 0; + if (ehci->async) + temp = STS_ASS; + if (ehci->next_uframe != -1) + temp |= STS_PSS; + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + temp, 16 * 125) != 0) { + ehci->hcd.state = USB_STATE_HALT; + return; + } + + /* then disable anything that's still active */ + temp = readl (&ehci->regs->command); + temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); + writel (temp, &ehci->regs->command); - // hardware can take 16 microframes to turn off ... + /* hardware can take 16 microframes to turn off ... */ + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + 0, 16 * 125) != 0) { + ehci->hcd.state = USB_STATE_HALT; + return; + } ehci->hcd.state = USB_STATE_READY; } @@ -165,6 +252,52 @@ static void ehci_tasklet (unsigned long param); +static void ehci_irq (struct usb_hcd *hcd); + +static void ehci_watchdog (unsigned long param) +{ + struct ehci_hcd *ehci = (struct ehci_hcd *) param; + 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) { + start_unlink_async (ehci, ehci->async); + ehci->async_idle = 0; + } + spin_unlock_irqrestore (&ehci->lock, flags); +} + +/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... + * off the controller (maybe it can boot from highspeed USB disks). + */ +static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap) +{ + if (cap & (1 << 16)) { + int msec = 500; + + /* request handoff to OS */ + cap &= 1 << 24; + pci_write_config_dword (ehci->hcd.pdev, where, cap); + + /* and wait a while for it to happen */ + do { + wait_ms (10); + msec -= 10; + 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); + return 1; + } + dbg ("BIOS handoff succeeded"); + } else + dbg ("BIOS handoff not needed"); + return 0; +} + /* called by khubd or root hub init threads */ static int ehci_start (struct usb_hcd *hcd) @@ -172,14 +305,11 @@ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; struct usb_device *udev; + struct usb_bus *bus; int retval; u32 hcc_params; u8 tempbyte; - // FIXME: given EHCI 0.96 or later, and a controller with - // the USBLEGSUP/USBLEGCTLSTS extended capability, make sure - // the BIOS doesn't still own this controller. - spin_lock_init (&ehci->lock); ehci->caps = (struct ehci_caps *) hcd->regs; @@ -187,9 +317,37 @@ dbg_hcs_params (ehci, "ehci_start"); dbg_hcc_params (ehci, "ehci_start"); + hcc_params = readl (&ehci->caps->hcc_params); + + /* EHCI 0.96 and later may have "extended capabilities" */ + temp = HCC_EXT_CAPS (hcc_params); + while (temp) { + u32 cap; + + pci_read_config_dword (ehci->hcd.pdev, temp, &cap); + dbg ("capability %04x at %02x", 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!"); + cap = 0; + /* FALLTHROUGH */ + default: /* unknown */ + break; + } + temp = (cap >> 8) & 0xff; + } + /* cache this readonly data; minimize PCI reads */ ehci->hcs_params = readl (&ehci->caps->hcs_params); + /* force HC to halt state */ + if ((retval = ehci_halt (ehci)) != 0) + return retval; + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -197,7 +355,6 @@ ehci->periodic_size = DEFAULT_I_TDPS; if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0) return retval; - hcc_params = readl (&ehci->caps->hcc_params); /* controllers may cache some of the periodic schedule ... */ if (HCC_ISOC_CACHE (hcc_params)) // full frame cache @@ -212,8 +369,10 @@ /* controller state: unknown --> reset */ /* EHCI spec section 4.1 */ - // FIXME require STS_HALT before reset... - ehci_reset (ehci); + if ((retval = ehci_reset (ehci)) != 0) { + ehci_mem_cleanup (ehci); + return retval; + } writel (INTR_MASK, &ehci->regs->intr_enable); writel (ehci->periodic_dma, &ehci->regs->frame_list); @@ -221,23 +380,25 @@ * 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. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: layered drivers can't yet tell when we enable that, + * so they can't pass this info along (like NETIF_F_HIGHDMA) + * (or like Scsi_Host.highmem_io) ... usb_bus.flags? */ if (HCC_64BIT_ADDR (hcc_params)) { writel (0, &ehci->regs->segment); - /* - * FIXME Enlarge pci_set_dma_mask() when possible. The DMA - * mapping API spec now says that'll affect only single shot - * mappings, and the pci_pool data will stay safe in seg 0. - * That's what we want: no extra copies for USB transfers. - */ - info ("restricting 64bit DMA mappings to segment 0 ..."); + if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL)) + info ("enabled 64bit PCI DMA (DAC)"); } /* clear interrupt enables, set irq latency */ temp = readl (&ehci->regs->command) & 0xff; if (log2_irq_thresh < 0 || log2_irq_thresh > 6) - log2_irq_thresh = 0; + log2_irq_thresh = 0; temp |= 1 << (16 + log2_irq_thresh); + // if hc can park (ehci >= 0.96), default is 3 packets per async QH // keeping default periodic framelist size temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE), // Philips, Intel, and maybe others need CMD_RUN before the @@ -251,8 +412,13 @@ 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; + /* wire up the root hub */ - hcd->bus->root_hub = udev = usb_alloc_dev (NULL, hcd->bus); + bus = hcd_to_bus (hcd); + bus->root_hub = udev = usb_alloc_dev (NULL, bus); if (!udev) { done2: ehci_mem_cleanup (ehci); @@ -271,11 +437,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.%2x", - ((tempbyte & 0xf0)>>4), - (tempbyte & 0x0f), - temp >> 8, - temp & 0xff); + 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); /* * From here on, khubd concurrently accesses the root @@ -286,19 +451,18 @@ */ usb_connect (udev); udev->speed = USB_SPEED_HIGH; - if (usb_new_device (udev) != 0) { + if (hcd_register_root (hcd) != 0) { if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) - udelay (100); ehci_reset (ehci); - // usb_disconnect (udev); - hcd->bus->root_hub = 0; + bus->root_hub = 0; usb_free_dev (udev); retval = -ENODEV; goto done2; } + create_debug_files (ehci); + return 0; } @@ -308,20 +472,34 @@ { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - dbg ("%s: stop", hcd->bus_name); + dbg ("%s: stop", hcd_to_bus (hcd)->bus_name); + /* no more interrupts ... */ if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) - udelay (100); + if (in_interrupt ()) /* should not happen!! */ + err ("stopped %s!", RUN_CONTEXT); + else + del_timer_sync (&ehci->watchdog); ehci_reset (ehci); - // root hub is shut down separately (first, when possible) - scan_async (ehci); - if (ehci->next_uframe != -1) - scan_periodic (ehci); + /* let companion controllers work when we aren't */ + writel (0, &ehci->regs->configured_flag); + + remove_debug_files (ehci); + + /* root hub is shut down separately (first, when possible) */ + tasklet_disable (&ehci->tasklet); + ehci_tasklet ((unsigned long) 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)); } @@ -343,7 +521,7 @@ int ports; int i; - dbg ("%s: suspend to %d", hcd->bus_name, state); + dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state); ports = HCS_N_PORTS (ehci->hcs_params); @@ -360,15 +538,13 @@ if ((temp & PORT_PE) == 0 || (temp & PORT_OWNER) != 0) continue; -dbg ("%s: suspend port %d", hcd->bus_name, i); +dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i); temp |= PORT_SUSPEND; writel (temp, &ehci->regs->port_status [i]); } if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) - udelay (100); writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command); // save pci FLADJ value @@ -384,7 +560,7 @@ int ports; int i; - dbg ("%s: resume", hcd->bus_name); + dbg ("%s: resume", hcd_to_bus (hcd)->bus_name); ports = HCS_N_PORTS (ehci->hcs_params); @@ -404,7 +580,7 @@ if ((temp & PORT_PE) == 0 || (temp & PORT_SUSPEND) != 0) continue; -dbg ("%s: resume port %d", hcd->bus_name, i); +dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i); temp |= PORT_RESUME; writel (temp, &ehci->regs->port_status [i]); readl (&ehci->regs->command); /* unblock posted writes */ @@ -429,11 +605,15 @@ { struct ehci_hcd *ehci = (struct ehci_hcd *) param; + spin_lock_irq (&ehci->lock); + if (ehci->reclaim_ready) end_unlink_async (ehci); scan_async (ehci); if (ehci->next_uframe != -1) scan_periodic (ehci); + + spin_unlock_irq (&ehci->lock); } /*-------------------------------------------------------------------------*/ @@ -444,6 +624,12 @@ u32 status = readl (&ehci->regs->status); int bh; + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name); + goto dead; + } + status &= INTR_MASK; if (!status) /* irq sharing? */ return; @@ -461,21 +647,30 @@ /* INT, ERR, and IAA interrupt rates can be throttled */ /* 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; + } /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { + COUNT (ehci->stats.reclaim); ehci->reclaim_ready = 1; bh = 1; } /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { - err ("%s: fatal error, state %x", hcd->bus_name, hcd->state); + err ("%s: fatal error, state %x", + hcd_to_bus (hcd)->bus_name, hcd->state); +dead: ehci_reset (ehci); - // generic layer kills/unlinks all urbs - // then tasklet cleans up the rest + /* generic layer kills/unlinks all urbs, then + * uses ehci_stop to clean up the rest + */ bh = 1; } @@ -495,7 +690,8 @@ * * hcd-specific init for hcpriv hasn't been done yet * - * NOTE: EHCI queues control and bulk requests transparently, like OHCI. + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. */ static int ehci_urb_enqueue ( struct usb_hcd *hcd, @@ -507,10 +703,11 @@ urb->transfer_flags &= ~EHCI_STATE_UNLINK; INIT_LIST_HEAD (&qtd_list); - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: + switch (usb_pipetype (urb->pipe)) { + // case PIPE_CONTROL: + // case PIPE_BULK: + default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return submit_async (ehci, urb, &qtd_list, mem_flags); @@ -530,9 +727,6 @@ dbg ("no split iso support yet"); return -ENOSYS; #endif /* have_split_iso */ - - default: /* can't happen */ - return -ENOSYS; } } @@ -546,15 +740,22 @@ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; unsigned long flags; - dbg ("%s urb_dequeue %p qh state %d", - hcd->bus_name, urb, qh->qh_state); + dbg ("%s urb_dequeue %p qh %p state %d", + hcd_to_bus (hcd)->bus_name, urb, qh, qh->qh_state); switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: + // case PIPE_CONTROL: + // case PIPE_BULK: + default: spin_lock_irqsave (&ehci->lock, flags); if (ehci->reclaim) { -dbg ("dq: reclaim busy, %s", RUN_CONTEXT); + 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; @@ -564,28 +765,43 @@ && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); -// yeech ... this could spin for up to two frames! -dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", - qh->qh_state, ehci->reclaim, ehci->hcd.state -); - udelay (100); + /* let pending unlinks complete */ + wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } } if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + break; case PIPE_INTERRUPT: - intr_deschedule (ehci, urb->start_frame, qh, - (urb->dev->speed == USB_SPEED_HIGH) - ? urb->interval - : (urb->interval << 3)); - if (ehci->hcd.state == USB_STATE_HALT) - urb->status = -ESHUTDOWN; - qh_completions (ehci, qh, 1); - return 0; + spin_lock_irqsave (&ehci->lock, flags); + 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); + + /* reschedule QH iff another request is queued */ + if (!list_empty (&qh->qtd_list) + && HCD_IS_RUNNING (ehci->hcd.state)) { + int status; + + status = qh_schedule (ehci, qh); + spin_unlock_irqrestore (&ehci->lock, flags); + + if (status != 0) { + // shouldn't happen often, but ... + // FIXME kill those tds' urbs + err ("can't reschedule qh %p, err %d", + qh, status); + } + return status; + } + spin_unlock_irqrestore (&ehci->lock, flags); + break; case PIPE_ISOCHRONOUS: // itd or sitd ... @@ -593,9 +809,9 @@ // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, urb->transfer_flags |= EHCI_STATE_UNLINK; - return 0; + break; } - return -EINVAL; + return 0; } /*-------------------------------------------------------------------------*/ @@ -609,42 +825,67 @@ int i; unsigned long flags; + /* ASSERT: no requests/urbs are still linked (so no TDs) */ /* ASSERT: nobody can be submitting urbs for this any more */ - dbg ("%s: free_config devnum %d", hcd->bus_name, udev->devnum); + dbg ("%s: free_config devnum %d", + hcd_to_bus (hcd)->bus_name, udev->devnum); spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < 32; i++) { if (dev->ep [i]) { struct ehci_qh *qh; + char *why; /* dev->ep never has ITDs or SITDs */ qh = (struct ehci_qh *) dev->ep [i]; - vdbg ("free_config, ep 0x%02x qh %p", i, qh); - if (!list_empty (&qh->qtd_list)) { - dbg ("ep 0x%02x qh %p not empty!", i, qh); + + /* detect/report non-recoverable errors */ + if (in_interrupt ()) + why = "disconnect() didn't"; + else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0 + && qh->qh_state != QH_STATE_IDLE) + why = "(active periodic)"; + else + why = 0; + if (why) { + err ("dev %s-%s ep %d-%s error: %s", + hcd_to_bus (hcd)->bus_name, + udev->devpath, + i & 0xf, (i & 0x10) ? "IN" : "OUT", + why); BUG (); } - dev->ep [i] = 0; - /* wait_ms() won't spin here -- we're a thread */ + dev->ep [i] = 0; + if (qh->qh_state == QH_STATE_IDLE) + goto idle; + dbg ("free_config, async ep 0x%02x qh %p", i, qh); + + /* scan_async() empties the ring as it does its work, + * using IAA, but doesn't (yet?) turn it off. if it + * doesn't empty this qh, likely it's the last entry. + */ while (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); + /* wait_ms() won't spin, we're a thread; + * and we know IRQ+tasklet can progress + */ wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } - if (qh->qh_state == QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); - while (qh->qh_state != QH_STATE_IDLE) { - spin_unlock_irqrestore (&ehci->lock, - flags); - wait_ms (1); - spin_lock_irqsave (&ehci->lock, flags); - } + while (qh->qh_state != QH_STATE_IDLE + && ehci->hcd.state != USB_STATE_HALT) { + spin_unlock_irqrestore (&ehci->lock, flags); + wait_ms (1); + spin_lock_irqsave (&ehci->lock, flags); } +idle: qh_put (ehci, qh); } } @@ -654,50 +895,48 @@ /*-------------------------------------------------------------------------*/ -static const char hcd_name [] = "ehci-hcd"; - static const struct hc_driver ehci_driver = { - description: hcd_name, + .description = hcd_name, /* * generic hardware linkage */ - irq: ehci_irq, - flags: HCD_MEMORY | HCD_USB2, + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, /* * basic lifecycle operations */ - start: ehci_start, + .start = ehci_start, #ifdef CONFIG_PM - suspend: ehci_suspend, - resume: ehci_resume, + .suspend = ehci_suspend, + .resume = ehci_resume, #endif - stop: ehci_stop, + .stop = ehci_stop, /* * memory lifecycle (except per-request) */ - hcd_alloc: ehci_hcd_alloc, - hcd_free: ehci_hcd_free, + .hcd_alloc = ehci_hcd_alloc, + .hcd_free = ehci_hcd_free, /* * managing i/o requests and associated device resources */ - urb_enqueue: ehci_urb_enqueue, - urb_dequeue: ehci_urb_dequeue, - free_config: ehci_free_config, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .free_config = ehci_free_config, /* * scheduling support */ - get_frame_number: ehci_get_frame, + .get_frame_number = ehci_get_frame, /* * root hub support */ - hub_status_data: ehci_hub_status_data, - hub_control: ehci_hub_control, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, }; /*-------------------------------------------------------------------------*/ @@ -709,15 +948,15 @@ /* handle any USB 2.0 EHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x20), - class_mask: ~0, - driver_data: (unsigned long) &ehci_driver, + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0x20), + .class_mask = ~0, + .driver_data = (unsigned long) &ehci_driver, /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* end: all zeroes */ } }; @@ -725,21 +964,20 @@ /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ehci_pci_driver = { - name: (char *) hcd_name, - id_table: pci_ids, + .name = (char *) hcd_name, + .id_table = pci_ids, - probe: usb_hcd_pci_probe, - remove: usb_hcd_pci_remove, + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, #ifdef CONFIG_PM - suspend: usb_hcd_pci_suspend, - resume: usb_hcd_pci_resume, + .suspend = usb_hcd_pci_suspend, + .resume = usb_hcd_pci_resume, #endif }; #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC -EXPORT_NO_SYMBOLS; MODULE_DESCRIPTION (DRIVER_INFO); MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_LICENSE ("GPL"); diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Mon Sep 30 10:47:10 2002 +++ b/drivers/usb/hcd/ehci-hub.c Mon Sep 30 10:47:10 2002 @@ -41,14 +41,17 @@ /* 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", - ehci->hcd.bus_name, index + 1, port_status); + hcd_to_bus (&ehci->hcd)->bus_name, + index + 1, port_status); // 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", ehci->hcd.bus_name, index + 1); + dbg ("%s port %d high speed", + hcd_to_bus (&ehci->hcd)->bus_name, + index + 1); return port_status; } @@ -236,7 +239,8 @@ /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) - && jiffies > ehci->reset_done [wIndex]) { + && time_after (jiffies, + ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; /* force reset to complete */ @@ -310,11 +314,13 @@ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && PORT_USB11 (temp)) { dbg ("%s port %d low speed, give to companion", - hcd->bus_name, wIndex + 1); + hcd_to_bus (&ehci->hcd)->bus_name, + wIndex + 1); temp |= PORT_OWNER; } else { vdbg ("%s port %d reset", - hcd->bus_name, wIndex + 1); + hcd_to_bus (&ehci->hcd)->bus_name, + wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Mon Sep 30 10:47:10 2002 +++ b/drivers/usb/hcd/ehci-q.c Mon Sep 30 10:47:10 2002 @@ -26,8 +26,7 @@ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * buffers needed for the larger number). We use one QH per endpoint, queue - * multiple (bulk or control) urbs per endpoint. URBs may need several qtds. - * A scheduled interrupt qh always (for now) has one qtd, one urb. + * multiple urbs (all three types) per endpoint. URBs may need several qtds. * * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling. Performance improvements can be @@ -47,9 +46,11 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token) { int i, count; + u64 addr = buf; /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf [0] = cpu_to_le32 (buf); + qtd->hw_buf [0] = cpu_to_le32 ((u32)addr); + qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32)); count = 0x1000 - (buf & 0x0fff); /* rest of that page */ if (likely (len < count)) /* ... iff needed */ count = len; @@ -59,7 +60,7 @@ /* per-qtd limit: from 16K to 20K (best alignment) */ for (i = 1; count < len && i < 5; i++) { - u64 addr = buf; + addr = buf; qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); buf += 0x1000; @@ -144,97 +145,106 @@ usb_pipeendpoint (pipe), usb_pipeout (pipe)); if (urb->dev->tt && !usb_pipeint (pipe)) { -err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d", - urb->dev->ttport, /* devpath */ - urb->dev->tt->multi ? "" : " (all-ports TT)", - urb->dev->devnum, usb_pipeendpoint (urb->pipe)); - // FIXME something (khubd?) should make the hub - // CLEAR_TT_BUFFER ASAP, it's blocking other - // fs/ls requests... hub_tt_clear_buffer() ? +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dbg ("clear tt %s-%s p%d buffer, a%d ep%d", + tt->bus->bus_name, tt->devpath, + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint (pipe)); +#endif /* DEBUG */ + usb_hub_tt_clear_buffer (urb->dev, pipe); } } } } -static void ehci_urb_complete ( - struct ehci_hcd *ehci, - dma_addr_t addr, - struct urb *urb -) { - if (urb->transfer_buffer_length && usb_pipein (urb->pipe)) - pci_dma_sync_single (ehci->hcd.pdev, addr, - urb->transfer_buffer_length, - PCI_DMA_FROMDEVICE); +static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) +{ +#ifdef INTR_AUTOMAGIC + struct urb *resubmit = 0; + struct usb_device *dev = 0; - /* cleanse status if we saw no error */ - if (likely (urb->status == -EINPROGRESS)) { - if (urb->actual_length != urb->transfer_buffer_length - && (urb->transfer_flags & USB_DISABLE_SPD)) - urb->status = -EREMOTEIO; - else - urb->status = 0; - } + static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int); +#endif - /* only report unlinks once */ - if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN)) - urb->complete (urb); -} + if (likely (urb->hcpriv != 0)) { + struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; -/* urb->lock ignored from here on (hcd is done with urb) */ + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { -static void ehci_urb_done ( - struct ehci_hcd *ehci, - dma_addr_t addr, - struct urb *urb -) { - if (urb->transfer_buffer_length) - pci_unmap_single (ehci->hcd.pdev, - addr, - urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (likely (urb->hcpriv != 0)) { - qh_put (ehci, (struct ehci_qh *) urb->hcpriv); + /* ... update hc-wide periodic stats (for usbfs) */ + hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; + +#ifdef INTR_AUTOMAGIC + if (!((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + resubmit = usb_get_urb (urb); + dev = urb->dev; + } +#endif + } + qh_put (ehci, qh); urb->hcpriv = 0; } if (likely (urb->status == -EINPROGRESS)) { if (urb->actual_length != urb->transfer_buffer_length - && (urb->transfer_flags & USB_DISABLE_SPD)) + && (urb->transfer_flags & URB_SHORT_NOT_OK)) urb->status = -EREMOTEIO; else urb->status = 0; } - /* hand off urb ownership */ + 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 */ + spin_unlock (&ehci->lock); usb_hcd_giveback_urb (&ehci->hcd, urb); + +#ifdef INTR_AUTOMAGIC + if (resubmit && ((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + usb_put_urb (resubmit); + resubmit = 0; + } + // device drivers will soon be doing something like this + if (resubmit) { + int status; + + resubmit->dev = dev; + status = SUBMIT_URB (resubmit, SLAB_KERNEL); + if (status != 0) + err ("can't resubmit interrupt urb %p: status %d", + resubmit, status); + usb_put_urb (resubmit); + } +#endif + + spin_lock (&ehci->lock); } /* - * Process completed qtds for a qh, issuing completions if needed. - * When freeing: frees qtds, unmaps buf, returns URB to driver. - * When not freeing (queued periodic qh): retain qtds, mapping, and urb. - * Races up to qh->hw_current; returns number of urb completions. + * Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. */ -static int -qh_completions ( - struct ehci_hcd *ehci, - struct ehci_qh *qh, - int freeing -) { +static unsigned +qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ struct ehci_qtd *qtd, *last; struct list_head *next, *qtd_list = &qh->qtd_list; int unlink = 0, halted = 0; - unsigned long flags; - int retval = 0; + unsigned count = 0; - spin_lock_irqsave (&ehci->lock, flags); - if (unlikely (list_empty (qtd_list))) { - spin_unlock_irqrestore (&ehci->lock, flags); - return retval; - } + if (unlikely (list_empty (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), @@ -248,16 +258,8 @@ /* clean up any state from previous QTD ...*/ if (last) { if (likely (last->urb != urb)) { - /* complete() can reenter this HCD */ - spin_unlock_irqrestore (&ehci->lock, flags); - if (likely (freeing != 0)) - ehci_urb_done (ehci, last->buf_dma, - last->urb); - else - ehci_urb_complete (ehci, last->buf_dma, - last->urb); - spin_lock_irqsave (&ehci->lock, flags); - retval++; + ehci_urb_done (ehci, last->urb); + count++; } /* qh overlays can have HC's old cached copies of @@ -267,10 +269,10 @@ && 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); } - if (likely (freeing != 0)) - ehci_qtd_free (ehci, last); + ehci_qtd_free (ehci, last); last = 0; } next = qtd->qtd_list.next; @@ -285,9 +287,17 @@ || (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)) { - freeing = unlink = 1; + unlink = 1; /* status copied below */ /* QH halts only because of fault (above) or unlink (here). */ @@ -295,13 +305,14 @@ /* unlinking everything because of HC shutdown? */ if (ehci->hcd.state == USB_STATE_HALT) { - freeing = unlink = 1; + unlink = 1; /* explicit unlink, maybe starting here? */ } else if (qh->qh_state == QH_STATE_IDLE && (urb->status == -ECONNRESET + || urb->status == -ESHUTDOWN || urb->status == -ENOENT)) { - freeing = unlink = 1; + unlink = 1; /* QH halted to unlink urbs _after_ this? */ } else if (!unlink && (token & QTD_STS_ACTIVE) != 0) { @@ -311,7 +322,7 @@ /* unlink the rest? once we start unlinking, after * a fault or explicit unlink, we unlink all later - * urbs. usb spec requires that. + * urbs. usb spec requires that for faults... */ if (unlink && urb->status == -EINPROGRESS) urb->status = -ECONNRESET; @@ -329,31 +340,7 @@ qtd_copy_status (urb, qtd->length, token); spin_unlock (&urb->lock); - /* - * NOTE: this won't work right with interrupt urbs that - * need multiple qtds ... only the first scan of qh->qtd_list - * starts at the right qtd, yet multiple scans could happen - * for transfers that are scheduled across multiple uframes. - * (Such schedules are not currently allowed!) - */ - if (likely (freeing != 0)) - list_del (&qtd->qtd_list); - else { - /* restore everything the HC could change - * from an interrupt QTD - */ - qtd->hw_token = (qtd->hw_token - & __constant_cpu_to_le32 (0x8300)) - | cpu_to_le32 (qtd->length << 16) - | __constant_cpu_to_le32 (QTD_STS_ACTIVE - | (EHCI_TUNE_CERR << 10)); - qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff); - - /* this offset, and the length above, - * are likely wrong on QTDs #2..N - */ - qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma); - } + list_del (&qtd->qtd_list); #if 0 if (urb->status == -EINPROGRESS) @@ -364,31 +351,22 @@ urb, urb->status, qtd, token, urb->actual_length); #endif + } - /* SETUP for control urb? */ - if (unlikely (QTD_PID (token) == 2)) - pci_unmap_single (ehci->hcd.pdev, - qtd->buf_dma, sizeof (struct usb_ctrlrequest), - PCI_DMA_TODEVICE); + /* last urb's completion might still need calling */ + if (likely (last != 0)) { + ehci_urb_done (ehci, last->urb); + count++; + ehci_qtd_free (ehci, last); } - /* patch up list head? */ + /* 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)); } - spin_unlock_irqrestore (&ehci->lock, flags); - /* last urb's completion might still need calling */ - if (likely (last != 0)) { - if (likely (freeing != 0)) { - ehci_urb_done (ehci, last->buf_dma, last->urb); - ehci_qtd_free (ehci, last); - } else - ehci_urb_complete (ehci, last->buf_dma, last->urb); - retval++; - } - return retval; + return count; } /*-------------------------------------------------------------------------*/ @@ -403,35 +381,12 @@ struct list_head *qtd_list ) { struct list_head *entry, *temp; - int unmapped = 0; list_for_each_safe (entry, temp, qtd_list) { struct ehci_qtd *qtd; qtd = list_entry (entry, struct ehci_qtd, qtd_list); list_del (&qtd->qtd_list); - if (unmapped != 2) { - int direction; - size_t size; - - /* for ctrl unmap twice: SETUP and DATA; - * else (bulk, intr) just once: DATA - */ - if (!unmapped++ && usb_pipecontrol (urb->pipe)) { - direction = PCI_DMA_TODEVICE; - size = sizeof (struct usb_ctrlrequest); - } else { - direction = usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE; - size = qtd->urb->transfer_buffer_length; - unmapped++; - } - if (qtd->buf_dma) - pci_unmap_single (ehci->hcd.pdev, - qtd->buf_dma, - size, direction); - } ehci_qtd_free (ehci, qtd); } } @@ -447,8 +402,9 @@ int flags ) { struct ehci_qtd *qtd, *qtd_prev; - dma_addr_t buf, map_buf; + dma_addr_t buf; int len, maxpacket; + int is_input, status_patch = 0; u32 token; /* @@ -466,17 +422,8 @@ /* for split transactions, SplitXState initialized to zero */ if (usb_pipecontrol (urb->pipe)) { - /* control request data is passed in the "setup" pid */ - qtd->buf_dma = pci_map_single ( - ehci->hcd.pdev, - urb->setup_packet, - sizeof (struct usb_ctrlrequest), - PCI_DMA_TODEVICE); - if (unlikely (!qtd->buf_dma)) - goto cleanup; - /* SETUP pid */ - qtd_fill (qtd, qtd->buf_dma, sizeof (struct usb_ctrlrequest), + qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), token | (2 /* "setup" */ << 8)); /* ... and always at least one more pid */ @@ -488,29 +435,26 @@ qtd->urb = urb; qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); + + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + status_patch = 1; } /* * data transfer stage: buffer setup */ len = urb->transfer_buffer_length; - if (likely (len > 0)) { - buf = map_buf = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, len, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (unlikely (!buf)) - goto cleanup; - } else - buf = map_buf = 0; + is_input = usb_pipein (urb->pipe); + if (likely (len > 0)) + buf = urb->transfer_dma; + else + buf = 0; - if (!buf || usb_pipein (urb->pipe)) + if (!buf || is_input) token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)); + maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff; /* * buffer gets wrapped in one or more qtds; @@ -521,7 +465,6 @@ int this_qtd_len; qtd->urb = urb; - qtd->buf_dma = map_buf; this_qtd_len = qtd_fill (qtd, buf, len, token); len -= this_qtd_len; buf += this_qtd_len; @@ -572,6 +515,19 @@ } } + /* if we're permitting a short control read, we want the hardware to + * just continue after short data and send the status ack. it can do + * that on the last data packet (typically the only one). for other + * packets, software fixup is needed (in qh_completions). + */ + if (status_patch) { + struct ehci_qtd *prev; + + prev = list_entry (qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + prev->hw_alt_next = QTD_NEXT (qtd->qtd_dma); + } + /* by default, enable interrupt on urb completion */ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); @@ -606,6 +562,11 @@ // That'd mean updating how usbcore talks to HCDs. (2.5?) +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff) + /* * Each QH holds a qtd list; a QH is used for everything except iso. * @@ -623,6 +584,8 @@ ) { struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; if (!qh) return qh; @@ -633,6 +596,53 @@ info1 |= usb_pipeendpoint (urb->pipe) << 8; info1 |= usb_pipedevice (urb->pipe) << 0; + is_input = usb_pipein (urb->pipe); + type = usb_pipetype (urb->pipe); + maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input); + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, + hb_mult (maxp) * max_packet (maxp)); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + /* FIXME handle HS periods of less than 1 frame. */ + qh->period = urb->interval >> 3; + if (qh->period < 1) { + dbg ("intr period %d uframes, NYET!", + urb->interval); + qh = 0; + goto done; + } + } else { + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { // SPLIT, gap, CSPLIT+DATA + qh->c_usecs = qh->usecs + HS_USECS (0); + qh->usecs = HS_USECS (1); + } else { // SPLIT+DATA, gap, CSPLIT + qh->usecs += HS_USECS (1); + qh->c_usecs = HS_USECS (0); + } + + qh->period = urb->interval; + } + } + /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: @@ -642,69 +652,62 @@ case USB_SPEED_FULL: /* EPS 0 means "full" */ info1 |= (EHCI_TUNE_RL_TT << 28); - if (usb_pipecontrol (urb->pipe)) { + if (type == PIPE_CONTROL) { info1 |= (1 << 27); /* for TT */ info1 |= 1 << 14; /* toggle from qtd */ } - info1 |= usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)) << 16; + info1 |= maxp << 16; info2 |= (EHCI_TUNE_MULT_TT << 30); info2 |= urb->dev->ttport << 23; info2 |= urb->dev->tt->hub->devnum << 16; - /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets c-mask } - * ... and a 0.96 scheduler might use FSTN nodes too - */ + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + break; case USB_SPEED_HIGH: /* no TT involved */ info1 |= (2 << 12); /* EPS "high" */ info1 |= (EHCI_TUNE_RL_HS << 28); - if (usb_pipecontrol (urb->pipe)) { + if (type == PIPE_CONTROL) { info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 1 << 14; /* toggle from qtd */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else if (usb_pipebulk (urb->pipe)) { + } else if (type == PIPE_BULK) { info1 |= 512 << 16; /* usb2 fixed maxpacket */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else { - u32 temp; - temp = usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)); - info1 |= (temp & 0x3ff) << 16; /* maxpacket */ - /* HS intr can be "high bandwidth" */ - temp = 1 + ((temp >> 11) & 0x03); - info2 |= temp << 30; /* mult */ + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet (maxp) << 16; + info2 |= hb_mult (maxp) << 30; } break; default: -#ifdef DEBUG - BUG (); -#else - ; -#endif + dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); + return 0; } - /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets s-mask } */ + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ 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 */ - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + 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 */ - if (!usb_pipecontrol (urb->pipe)) - clear_toggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), - qh); + clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh); +done: return qh; } +#undef hb_mult +#undef hb_packet /*-------------------------------------------------------------------------*/ @@ -719,8 +722,7 @@ u32 cmd = readl (&ehci->regs->command); /* in case a clear of CMD_ASE didn't take yet */ - while (readl (&ehci->regs->status) & STS_ASS) - udelay (100); + (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; @@ -743,58 +745,58 @@ } qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ + + ehci->async_idle = 0; } /*-------------------------------------------------------------------------*/ -static int -submit_async ( +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct ehci_qh *qh_append_tds ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, - int mem_flags -) { - struct ehci_qtd *qtd; - struct hcd_dev *dev; - int epnum; - unsigned long flags; + int epnum, + void **ptr +) +{ struct ehci_qh *qh = 0; - qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - dev = (struct hcd_dev *)urb->dev->hcpriv; - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) - epnum |= 0x10; - - vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", - ehci->hcd.bus_name, urb, urb->transfer_buffer_length, - epnum & 0x0f, (epnum & 0x10) ? "in" : "out", - qtd, dev ? dev->ep [epnum] : (void *)~0); - - spin_lock_irqsave (&ehci->lock, flags); - - qh = (struct ehci_qh *) dev->ep [epnum]; + qh = (struct ehci_qh *) *ptr; if (likely (qh != 0)) { - u32 hw_next = QTD_NEXT (qtd->qtd_dma); + struct ehci_qtd *qtd; + + if (unlikely (list_empty (qtd_list))) + qtd = 0; + else + 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)); - /* is an URB is queued to this qh already? */ - if (unlikely (!list_empty (&qh->qtd_list))) { + /* 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 & USB_DISABLE_SPD) + 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; @@ -805,6 +807,7 @@ * 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 (); @@ -824,31 +827,62 @@ */ /* usb_clear_halt() means qh data toggle gets reset */ - if (usb_pipebulk (urb->pipe) - && unlikely (!usb_gettoggle (urb->dev, + if (unlikely (!usb_gettoggle (urb->dev, (epnum & 0x0f), !(epnum & 0x10)))) { clear_toggle (urb->dev, epnum & 0x0f, !(epnum & 0x10), qh); } - qh_update (qh, qtd); + if (qtd) + qh_update (qh, qtd); } list_splice (qtd_list, qh->qtd_list.prev); } else { /* can't sleep here, we have ehci->lock... */ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - if (likely (qh != 0)) { - // dbg_qh ("new qh", ehci, qh); - dev->ep [epnum] = qh; - } + // if (qh) dbg_qh ("new qh", ehci, qh); + *ptr = qh; } + if (qh) + urb->hcpriv = qh_get (qh); + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static int +submit_async ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + struct ehci_qtd *qtd; + struct hcd_dev *dev; + int epnum; + unsigned long flags; + struct ehci_qh *qh = 0; + + qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); + dev = (struct hcd_dev *)urb->dev->hcpriv; + epnum = usb_pipeendpoint (urb->pipe); + if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) + epnum |= 0x10; + + vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", + hcd_to_bus (&ehci->hcd)->bus_name, + urb, urb->transfer_buffer_length, + epnum & 0x0f, (epnum & 0x10) ? "in" : "out", + qtd, dev ? dev->ep [epnum] : (void *)~0); + + spin_lock_irqsave (&ehci->lock, flags); + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); /* Control/bulk operations through TTs don't need scheduling, * the HC and TT handle it when the TT has a buffer ready. */ if (likely (qh != 0)) { - urb->hcpriv = qh_get (qh); if (likely (qh->qh_state == QH_STATE_IDLE)) qh_link_async (ehci, qh_get (qh)); } @@ -869,16 +903,16 @@ { struct ehci_qh *qh = ehci->reclaim; + del_timer (&ehci->watchdog); + 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, 1); + qh_completions (ehci, qh); - // unlink any urb should now unlink all following urbs, so that - // relinking only happens for urbs before the unlinked ones. if (!list_empty (&qh->qtd_list) && HCD_IS_RUNNING (ehci->hcd.state)) qh_link_async (ehci, qh); @@ -886,7 +920,6 @@ qh_put (ehci, qh); // refcount from async list } - /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ @@ -916,13 +949,14 @@ if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) { /* can't get here without STS_ASS set */ if (ehci->hcd.state != USB_STATE_HALT) { - if (cmd & CMD_PSE) - writel (cmd & ~CMD_ASE, &ehci->regs->command); - else { - ehci_ready (ehci); - while (readl (&ehci->regs->status) & STS_ASS) - udelay (100); - } + 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 } qh->qh_next.qh = ehci->async = 0; @@ -940,10 +974,6 @@ prev = ehci->async; while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async) prev = prev->qh_next.qh; -#ifdef DEBUG - if (prev->qh_next.qh != qh) - BUG (); -#endif if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) { ehci->async = prev; @@ -957,48 +987,63 @@ cmd |= CMD_IAAD; writel (cmd, &ehci->regs->command); /* posted write need not be known to HC yet ... */ + + mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES); } /*-------------------------------------------------------------------------*/ -static void scan_async (struct ehci_hcd *ehci) +static void +scan_async (struct ehci_hcd *ehci) { struct ehci_qh *qh; - unsigned long flags; + unsigned count; - spin_lock_irqsave (&ehci->lock, flags); rescan: qh = ehci->async; + 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); - spin_unlock_irqrestore (&ehci->lock, flags); /* concurrent unlink could happen here */ - qh_completions (ehci, qh, 1); - - spin_lock_irqsave (&ehci->lock, flags); + count += qh_completions (ehci, qh); qh_put (ehci, qh); } - /* unlink idle entries (reduces PCI usage) */ + /* unlink idle entries, reducing HC PCI usage as + * well as HCD schedule-scanning costs. removing + * 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 (qh->qh_next.qh != qh) { // dbg ("irq/empty"); start_unlink_async (ehci, qh); - } else { - // FIXME: arrange to stop - // after it's been idle a while. + } 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; + } + qh = qh->qh_next.qh; if (!qh) /* unlinked? */ goto rescan; } while (qh != ehci->async); } - - spin_unlock_irqrestore (&ehci->lock, flags); } diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Mon Sep 30 10:47:10 2002 +++ b/drivers/usb/hcd/ehci-sched.c Mon Sep 30 10:47:10 2002 @@ -33,19 +33,6 @@ * or with "USB On The Go" additions to USB 2.0 ...) */ -/* - * Ceiling microseconds (typical) for that many bytes at high speed - * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed - * to preallocate bandwidth) - */ -#define EHCI_HOST_DELAY 5 /* nsec, guess */ -#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ - + EHCI_HOST_DELAY) -#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ - + EHCI_HOST_DELAY) - static int ehci_get_frame (struct usb_hcd *hcd); /*-------------------------------------------------------------------------*/ @@ -124,6 +111,9 @@ /* is it in the S-mask? */ if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) usecs += q->qh->usecs; + /* ... or C-mask? */ + if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe))) + usecs += q->qh->c_usecs; q = &q->qh->qh_next; break; case Q_TYPE_FSTN: @@ -181,15 +171,19 @@ /*-------------------------------------------------------------------------*/ -static void enable_periodic (struct ehci_hcd *ehci) +static int enable_periodic (struct ehci_hcd *ehci) { u32 cmd; + int status; /* did clearing PSE did take effect yet? * takes effect only at frame boundaries... */ - while (readl (&ehci->regs->status) & STS_PSS) - udelay (20); + status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125); + if (status != 0) { + ehci->hcd.state = USB_STATE_HALT; + return status; + } cmd = readl (&ehci->regs->command) | CMD_PSE; writel (cmd, &ehci->regs->command); @@ -199,80 +193,104 @@ /* make sure tasklet scans these */ ehci->next_uframe = readl (&ehci->regs->frame_index) % (ehci->periodic_size << 3); + return 0; } -static void disable_periodic (struct ehci_hcd *ehci) +static int disable_periodic (struct ehci_hcd *ehci) { u32 cmd; + int status; /* did setting PSE not take effect yet? * takes effect only at frame boundaries... */ - while (!(readl (&ehci->regs->status) & STS_PSS)) - udelay (20); + status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); + if (status != 0) { + ehci->hcd.state = USB_STATE_HALT; + return status; + } cmd = readl (&ehci->regs->command) & ~CMD_PSE; writel (cmd, &ehci->regs->command); /* posted write ... */ ehci->next_uframe = -1; + return 0; } /*-------------------------------------------------------------------------*/ +// FIXME microframe periods not yet handled + static void intr_deschedule ( struct ehci_hcd *ehci, - unsigned frame, struct ehci_qh *qh, - unsigned period + int wait ) { - unsigned long flags; - - period >>= 3; // FIXME microframe periods not handled yet - - spin_lock_irqsave (&ehci->lock, flags); + int status; + unsigned frame = qh->start; do { periodic_unlink (ehci, frame, qh); qh_put (ehci, qh); - frame += period; + frame += qh->period; } while (frame < ehci->periodic_size); qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = 0; - ehci->periodic_urbs--; + ehci->periodic_sched--; /* maybe turn off periodic schedule */ - if (!ehci->periodic_urbs) - disable_periodic (ehci); - else + if (!ehci->periodic_sched) + status = disable_periodic (ehci); + else { + status = 0; vdbg ("periodic schedule still enabled"); - - spin_unlock_irqrestore (&ehci->lock, flags); + } /* * If the hc may be looking at this qh, then delay a uframe * (yeech!) to be sure it's done. * No other threads may be mucking with this qh. */ - if (((ehci_get_frame (&ehci->hcd) - frame) % period) == 0) - udelay (125); + if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { + if (wait) { + udelay (125); + qh->hw_next = EHCI_LIST_END; + } else { + /* we may not be IDLE yet, but if the qh is empty + * the race is very short. then if qh also isn't + * rescheduled soon, it won't matter. otherwise... + */ + vdbg ("intr_deschedule..."); + } + } else + qh->hw_next = EHCI_LIST_END; qh->qh_state = QH_STATE_IDLE; - qh->hw_next = EHCI_LIST_END; - vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d", - qh, period, frame, - atomic_read (&qh->refcount), ehci->periodic_urbs); + /* update per-qh bandwidth utilization (for usbfs) */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= + (qh->usecs + qh->c_usecs) / qh->period; + + dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", + qh, qh->period, frame, + atomic_read (&qh->refcount), ehci->periodic_sched); } static int check_period ( struct ehci_hcd *ehci, unsigned frame, - int uframe, + unsigned uframe, unsigned period, unsigned usecs ) { + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + /* * 80% periodic == 100 usec/uframe available * convert "usecs we need" to "max already claimed" @@ -284,6 +302,8 @@ // FIXME delete when intr_submit handles non-empty queues // this gives us a one intr/frame limit (vs N/uframe) +// ... and also lets us avoid tracking split transactions +// that might collide at a given TT/hub. if (ehci->pshadow [frame].ptr) return 0; @@ -298,226 +318,203 @@ return 1; } -static int intr_submit ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *qtd_list, - int mem_flags -) { - unsigned epnum, period; - unsigned short usecs; - unsigned long flags; - struct ehci_qh *qh; - struct hcd_dev *dev; - int status = 0; +static int check_intr_schedule ( + struct ehci_hcd *ehci, + unsigned frame, + unsigned uframe, + const struct ehci_qh *qh, + u32 *c_maskp +) +{ + int retval = -ENOSPC; - /* get endpoint and transfer data */ - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) - epnum |= 0x10; - if (urb->dev->speed != USB_SPEED_HIGH) { - dbg ("no intr/tt scheduling yet"); - status = -ENOSYS; + if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = cpu_to_le32 (0); goto done; } - /* - * NOTE: current completion/restart logic doesn't handle more than - * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this. - * such big requests need many periods to transfer. + /* This is a split transaction; check the bandwidth available for + * the completion too. Check both worst and best case gaps: worst + * case is SPLIT near uframe end, and CSPLIT near start ... best is + * vice versa. Difference can be almost two uframe times, but we + * reserve unnecessary bandwidth (waste it) this way. (Actually + * even better cases exist, like immediate device NAK.) * - * FIXME want to change hcd core submit model to expect queuing - * for all transfer types ... not just ISO and (with flag) BULK. - * that means: getting rid of this check; handling the "interrupt - * urb already queued" case below like bulk queuing is handled (no - * errors possible!); and completly getting rid of that annoying - * qh restart logic. simpler/smaller overall, and more flexible. + * FIXME don't even bother unless we know this TT is idle in that + * range of uframes ... for now, check_period() allows only one + * interrupt transfer per frame, so needn't check "TT busy" status + * when scheduling a split (QH, SITD, or FSTN). + * + * FIXME ehci 0.96 and above can use FSTNs */ - if (unlikely (qtd_list->next != qtd_list->prev)) { - dbg ("only one intr qtd per urb allowed"); - status = -EINVAL; + if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) goto done; - } - - usecs = HS_USECS (urb->transfer_buffer_length); - - /* FIXME handle HS periods of less than 1 frame. */ - period = urb->interval >> 3; - if (period < 1) { - dbg ("intr period %d uframes, NYET!", urb->interval); - status = -EINVAL; + if (!check_period (ehci, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) goto done; - } - spin_lock_irqsave (&ehci->lock, flags); - - /* get the qh (must be empty and idle) */ - dev = (struct hcd_dev *)urb->dev->hcpriv; - qh = (struct ehci_qh *) dev->ep [epnum]; - if (qh) { - /* only allow one queued interrupt urb per EP */ - if (unlikely (qh->qh_state != QH_STATE_IDLE - || !list_empty (&qh->qtd_list))) { - dbg ("interrupt urb already queued"); - status = -EBUSY; - } else { - /* maybe reset hardware's data toggle in the qh */ - if (unlikely (!usb_gettoggle (urb->dev, epnum & 0x0f, - !(epnum & 0x10)))) { - qh->hw_token |= - __constant_cpu_to_le32 (QTD_TOGGLE); - usb_settoggle (urb->dev, epnum & 0x0f, - !(epnum & 0x10), 1); - } - /* trust the QH was set up as interrupt ... */ - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, - struct ehci_qtd, qtd_list)); - qtd_list = &qh->qtd_list; - } - } else { - /* can't sleep here, we have ehci->lock... */ - qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - if (likely (qh != 0)) { - // dbg ("new INTR qh %p", qh); - dev->ep [epnum] = qh; - qtd_list = &qh->qtd_list; - } else - status = -ENOMEM; - } + *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf)); + retval = 0; +done: + return retval; +} - /* Schedule this periodic QH. */ - if (likely (status == 0)) { - unsigned frame = period; +static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + int status; + unsigned uframe; + u32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ - qh->hw_next = EHCI_LIST_END; - qh->usecs = usecs; + qh->hw_next = EHCI_LIST_END; + frame = qh->start; - urb->hcpriv = qh_get (qh); + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); + status = check_intr_schedule (ehci, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; status = -ENOSPC; + } - /* pick a set of schedule slots, link the QH into them */ + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + frame = qh->period - 1; do { - int uframe; - - /* pick a set of slots such that all uframes have - * enough periodic bandwidth available. - * - * FIXME for TT splits, need uframes for start and end. - * FSTNs can put end into next frame (uframes 0 or 1). - */ - frame--; for (uframe = 0; uframe < 8; uframe++) { - if (check_period (ehci, frame, uframe, - period, usecs) != 0) + status = check_intr_schedule (ehci, + frame, uframe, qh, + &c_mask); + if (status == 0) break; } - if (uframe == 8) - continue; + } while (status && --frame); + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= ~0xffff; + qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; + } else + dbg ("reused previous qh %p schedule", qh); + + /* stuff into the periodic schedule */ + qh->qh_state = QH_STATE_LINKED; + dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", + qh, qh->usecs, qh->c_usecs, + qh->period, frame, uframe, qh->gap_uf); + do { + if (unlikely (ehci->pshadow [frame].ptr != 0)) { - /* QH will run once each period, starting there */ - urb->start_frame = frame; - status = 0; - - /* set S-frame mask */ - qh->hw_info2 |= cpu_to_le32 (1 << uframe); - // dbg_qh ("Schedule INTR qh", ehci, qh); - - /* stuff into the periodic schedule */ - qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d starting %d.%d", - qh, qh->usecs, period, frame, uframe); - do { - if (unlikely (ehci->pshadow [frame].ptr != 0)) { // FIXME -- just link toward the end, before any qh with a shorter period, -// AND handle it already being (implicitly) linked into this frame -// AS WELL AS updating the check_period() logic - BUG (); - } else { - ehci->pshadow [frame].qh = qh_get (qh); - ehci->periodic [frame] = - QH_NEXT (qh->qh_dma); - } - wmb (); - frame += period; - } while (frame < ehci->periodic_size); - - /* update bandwidth utilization records (for usbfs) */ - usb_claim_bandwidth (urb->dev, urb, usecs/period, 0); - - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) - enable_periodic (ehci); - break; +// AND accomodate it already having been linked here (after some other qh) +// AS WELL AS updating the schedule checking logic + + BUG (); + } else { + ehci->pshadow [frame].qh = qh_get (qh); + ehci->periodic [frame] = + QH_NEXT (qh->qh_dma); + } + wmb (); + frame += qh->period; + } while (frame < ehci->periodic_size); + + /* update per-qh bandwidth for usbfs */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated += + (qh->usecs + qh->c_usecs) / qh->period; + + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_sched++) + status = enable_periodic (ehci); +done: + return status; +} - } while (frame); +static int intr_submit ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + unsigned epnum; + unsigned long flags; + struct ehci_qh *qh; + struct hcd_dev *dev; + int is_input; + int status = 0; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = usb_pipeendpoint (urb->pipe); + is_input = usb_pipein (urb->pipe); + if (is_input) + epnum |= 0x10; + + spin_lock_irqsave (&ehci->lock, flags); + dev = (struct hcd_dev *)urb->dev->hcpriv; + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD (&empty); + qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]); + if (qh == 0) { + status = -ENOMEM; + goto done; } - spin_unlock_irqrestore (&ehci->lock, flags); + if (qh->qh_state == QH_STATE_IDLE) { + if ((status = qh_schedule (ehci, qh)) != 0) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); + BUG_ON (qh == 0); + + /* ... update usbfs periodic stats */ + hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++; + done: + spin_unlock_irqrestore (&ehci->lock, flags); if (status) qtd_list_free (ehci, urb, qtd_list); return status; } -static unsigned long +static unsigned intr_complete ( struct ehci_hcd *ehci, unsigned frame, - struct ehci_qh *qh, - unsigned long flags /* caller owns ehci->lock ... */ + struct ehci_qh *qh ) { - struct ehci_qtd *qtd; - struct urb *urb; - int unlinking; + unsigned count; /* nothing to report? */ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) != 0)) - return flags; + return 0; if (unlikely (list_empty (&qh->qtd_list))) { dbg ("intr qh %p no TDs?", qh); - return flags; + return 0; } - qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); - urb = qtd->urb; - unlinking = (urb->status == -ENOENT) || (urb->status == -ECONNRESET); - - /* call any completions, after patching for reactivation */ - spin_unlock_irqrestore (&ehci->lock, flags); - /* NOTE: currently restricted to one qtd per qh! */ - if (qh_completions (ehci, qh, 0) == 0) - urb = 0; - spin_lock_irqsave (&ehci->lock, flags); + /* handle any completions */ + count = qh_completions (ehci, qh); - /* never reactivate requests that were unlinked ... */ - if (likely (urb != 0)) { - if (unlinking - || urb->status == -ECONNRESET - || urb->status == -ENOENT - // || (urb->dev == null) - || ehci->hcd.state == USB_STATE_HALT) - urb = 0; - // FIXME look at all those unlink cases ... we always - // need exactly one completion that reports unlink. - // the one above might not have been it! - } - - /* normally reactivate */ - if (likely (urb != 0)) { - if (usb_pipeout (urb->pipe)) - pci_dma_sync_single (ehci->hcd.pdev, - qtd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_TODEVICE); - urb->status = -EINPROGRESS; - urb->actual_length = 0; + if (unlikely (list_empty (&qh->qtd_list))) + intr_deschedule (ehci, qh, 0); - /* patch qh and restart */ - qh_update (qh, qtd); - } - return flags; + return count; } /*-------------------------------------------------------------------------*/ @@ -527,11 +524,6 @@ { struct ehci_itd *first_itd = urb->hcpriv; - pci_unmap_single (ehci->hcd.pdev, - first_itd->buf_dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); while (!list_empty (&first_itd->itd_list)) { struct ehci_itd *itd; @@ -557,6 +549,7 @@ u32 buf1; unsigned i, epnum, maxp, multi; unsigned length; + int is_input; itd->hw_next = EHCI_LIST_END; itd->urb = urb; @@ -578,7 +571,8 @@ * as encoded in the ep descriptor's maxpacket field */ epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) { + is_input = usb_pipein (urb->pipe); + if (is_input) { maxp = urb->dev->epmaxpacketin [epnum]; buf1 = (1 << 11); } else { @@ -598,7 +592,7 @@ urb->iso_frame_desc [index].length); return -ENOSPC; } - itd->usecs = HS_USECS_ISO (length); + itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length); /* "plus" info in low order bits of buffer pointers */ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); @@ -625,16 +619,7 @@ int frame_index; struct ehci_itd *first_itd, *itd; int status; - dma_addr_t buf_dma, itd_dma; - - /* set up one dma mapping for this urb */ - buf_dma = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (buf_dma == 0) - return -ENOMEM; + dma_addr_t itd_dma; /* allocate/init ITDs */ for (frame_index = 0, first_itd = 0; @@ -648,7 +633,8 @@ memset (itd, 0, sizeof *itd); itd->itd_dma = itd_dma; - status = itd_fill (ehci, itd, urb, frame_index, buf_dma); + status = itd_fill (ehci, itd, urb, frame_index, + urb->transfer_dma); if (status != 0) goto fail; @@ -737,7 +723,7 @@ /* calculate the legal range [start,max) */ now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ - if (!ehci->periodic_urbs) + if (!ehci->periodic_sched) now += 8; /* startup delay */ now %= mod; end = now + mod; @@ -857,8 +843,12 @@ usb_claim_bandwidth (urb->dev, urb, usecs, 1); /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) - enable_periodic (ehci); + if (!ehci->periodic_sched++) { + if ((status = enable_periodic (ehci)) != 0) { + // FIXME deschedule right away + err ("itd_schedule, enable = %d", status); + } + } return 0; @@ -873,15 +863,14 @@ #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) -static unsigned long +static unsigned itd_complete ( struct ehci_hcd *ehci, struct ehci_itd *itd, - unsigned uframe, - unsigned long flags + unsigned uframe ) { struct urb *urb = itd->urb; - struct iso_packet_descriptor *desc; + struct usb_iso_packet_descriptor *desc; u32 t; /* update status for this uframe's transfers */ @@ -916,7 +905,7 @@ /* handle completion now? */ if ((itd->index + 1) != urb->number_of_packets) - return flags; + return 0; /* * Always give the urb back to the driver ... expect it to submit @@ -931,16 +920,17 @@ if (urb->status == -EINPROGRESS) 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); - spin_lock_irqsave (&ehci->lock, flags); + spin_lock (&ehci->lock); /* defer stopping schedule; completion can submit */ - ehci->periodic_urbs--; - if (!ehci->periodic_urbs) - disable_periodic (ehci); + ehci->periodic_sched--; + if (!ehci->periodic_sched) + (void) disable_periodic (ehci); - return flags; + return 1; } /*-------------------------------------------------------------------------*/ @@ -952,10 +942,6 @@ 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 */ status = itd_urb_transaction (ehci, urb, mem_flags); if (status < 0) @@ -978,130 +964,21 @@ /* * "Split ISO TDs" ... used for USB 1.1 devices going through * the TTs in USB 2.0 hubs. + * + * FIXME not yet implemented */ -static void -sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) -{ - pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); -} - -static struct ehci_sitd * -sitd_make ( - struct ehci_hcd *ehci, - struct urb *urb, - unsigned index, // urb->iso_frame_desc [index] - unsigned uframe, // scheduled start - dma_addr_t dma, // mapped transfer buffer - int mem_flags -) { - struct ehci_sitd *sitd; - unsigned length; - - sitd = pci_pool_alloc (ehci->sitd_pool, mem_flags, &dma); - if (!sitd) - return sitd; - sitd->urb = urb; - length = urb->iso_frame_desc [index].length; - dma += urb->iso_frame_desc [index].offset; - -#if 0 - // FIXME: do the rest! -#else - sitd_free (ehci, sitd); - return 0; -#endif - -} - -static void -sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) -{ - u32 ptr; - - ptr = cpu_to_le32 (sitd->sitd_dma | 2); // type 2 == sitd - if (ehci->pshadow [frame].ptr) { - if (!sitd->sitd_next.ptr) { - sitd->sitd_next = ehci->pshadow [frame]; - sitd->hw_next = ehci->periodic [frame]; - } else if (sitd->sitd_next.ptr != ehci->pshadow [frame].ptr) { - dbg ("frame %d sitd link goof", frame); - BUG (); - } - } - ehci->pshadow [frame].sitd = sitd; - ehci->periodic [frame] = ptr; -} - -static unsigned long -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd, - unsigned long flags -) { - // FIXME -- implement! - - dbg ("NYI -- sitd_complete"); - return flags; -} - -/*-------------------------------------------------------------------------*/ - -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) -{ - // struct ehci_sitd *first_sitd = 0; - unsigned frame_index; - dma_addr_t dma; - - dbg ("NYI -- sitd_submit"); - - // FIXME -- implement! - - // FIXME: setup one big dma mapping - dma = 0; - - for (frame_index = 0; - frame_index < urb->number_of_packets; - frame_index++) { - struct ehci_sitd *sitd; - unsigned uframe; - - // FIXME: use real arguments, schedule this! - uframe = -1; - - sitd = sitd_make (ehci, urb, frame_index, - uframe, dma, mem_flags); - - if (sitd) { - /* - if (first_sitd) - list_add_tail (&sitd->sitd_list, - &first_sitd->sitd_list); - else - first_sitd = sitd; - */ - } else { - // FIXME: clean everything up - } - } - - // if we have a first sitd, then - // store them all into the periodic schedule! - // urb->hcpriv = first sitd in sitd_list - - return -ENOSYS; -} #endif /* have_split_iso */ /*-------------------------------------------------------------------------*/ -static void scan_periodic (struct ehci_hcd *ehci) +static void +scan_periodic (struct ehci_hcd *ehci) { unsigned frame, clock, now_uframe, mod; - unsigned long flags; + unsigned count = 0; mod = ehci->periodic_size << 3; - spin_lock_irqsave (&ehci->lock, flags); /* * When running, scan from last scan point up to "now" @@ -1122,6 +999,14 @@ 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) @@ -1145,8 +1030,8 @@ last = (q.qh->hw_next == EHCI_LIST_END); temp = q.qh->qh_next; type = Q_NEXT_TYPE (q.qh->hw_next); - flags = intr_complete (ehci, frame, - qh_get (q.qh), flags); + count += intr_complete (ehci, frame, + qh_get (q.qh)); qh_put (ehci, q.qh); q = temp; break; @@ -1178,8 +1063,8 @@ type = Q_NEXT_TYPE (*hw_p); /* might free q.itd ... */ - flags = itd_complete (ehci, - temp.itd, uf, flags); + count += itd_complete (ehci, + temp.itd, uf); break; } } @@ -1195,7 +1080,7 @@ #ifdef have_split_iso case Q_TYPE_SITD: 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); // FIXME unlink SITD after split completes @@ -1241,5 +1126,4 @@ } else frame = (frame + 1) % ehci->periodic_size; } - spin_unlock_irqrestore (&ehci->lock, flags); } diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h --- a/drivers/usb/hcd/ehci.h Mon Sep 30 10:47:10 2002 +++ b/drivers/usb/hcd/ehci.h Mon Sep 30 10:47:10 2002 @@ -21,6 +21,23 @@ /* 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: async, reclaim, periodic (and shadow), ... * hcd_dev: ep[] @@ -39,7 +56,8 @@ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *reclaim; - int reclaim_ready; + int reclaim_ready : 1, + async_idle : 1; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ @@ -50,7 +68,7 @@ union ehci_shadow *pshadow; /* mirror hw periodic table */ int next_uframe; /* scan periodic, start here */ - unsigned periodic_urbs; /* how many urbs scheduled? */ + unsigned periodic_sched; /* periodic activity count */ /* deferred work from IRQ, etc */ struct tasklet_struct tasklet; @@ -69,10 +87,19 @@ struct pci_pool *qtd_pool; /* one or more per qh */ struct pci_pool *itd_pool; /* itd per iso urb */ struct pci_pool *sitd_pool; /* sitd per split iso urb */ + + 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 */ -#define hcd_to_ehci(hcd_ptr) list_entry(hcd_ptr, struct ehci_hcd, hcd) +#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) /* NOTE: urb->transfer_flags expected to not use this bit !!! */ #define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ @@ -219,7 +246,6 @@ /* dma same in urb's qtds, except 1st control qtd (setup buffer) */ struct urb *urb; /* qtd's urb */ - dma_addr_t buf_dma; /* buffer address */ size_t length; /* length of buffer */ } __attribute__ ((aligned (32))); @@ -287,15 +313,20 @@ struct list_head qtd_list; /* sw qtd list */ atomic_t refcount; - unsigned short usecs; /* intr bandwidth */ - short qh_state; + + u8 qh_state; #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */ -#ifdef EHCI_SOFT_RETRIES - int retries; -#endif + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -360,6 +391,9 @@ union ehci_shadow sitd_next; /* ptr to periodic q entry */ struct urb *urb; dma_addr_t buf_dma; /* buffer address */ + + unsigned short usecs; /* start bandwidth */ + unsigned short c_usecs; /* completion bandwidth */ } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -381,5 +415,40 @@ dma_addr_t fstn_dma; union ehci_shadow fstn_next; /* ptr to periodic q entry */ } __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) + +#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb) +#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 */ + +// hcd_to_bus() eventually moves to hcd.h on 2.5 too +static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) + { 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) + +#ifndef DEBUG +#define STUB_DEBUG_FILES +#endif /* DEBUG */ + +#endif /* LINUX_VERSION_CODE */ + +/*-------------------------------------------------------------------------*/ #endif /* __LINUX_EHCI_HCD_H */