![[LWN Logo]](/images/lcorner.png) |
|
![[LWN.net]](/images/Included.png) |
From: Greg KH <greg@kroah.com>
To: marcelo@conectiva.com.br
Subject: [linux-usb-devel] [PATCH 1 of 6] USB 2.0 support
Date: Tue, 26 Feb 2002 11:08:15 -0800
Cc: linux-usb-devel@lists.sourceforge.net
Hi,
Here's a patch against 2.4.19-pre1 that adds USB 2.0 support. The 2.0
driver and work was done by David Brownell.
The hub driver also has a patch from Martin Diehl that makes the USB core
work the way the USB spec says it should work :)
thanks,
greg k-h
diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help
--- a/Documentation/Configure.help Mon Feb 25 16:54:36 2002
+++ b/Documentation/Configure.help Mon Feb 25 16:54:36 2002
@@ -12702,6 +12702,30 @@
If you have an MGE Ellipse UPS, or you see timeouts in HID
transactions, say Y; otherwise say N.
+EHCI (USB 2.0) support
+CONFIG_USB_EHCI_HCD
+ The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
+ "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
+ If your USB host controller supports USB 2.0, you will likely want to
+ configure this Host Controller Driver. At this writing, the primary
+ implementation of EHCI is a chip from NEC, widely available in add-on
+ PCI cards, but implementations are in the works from other vendors
+ including Intel and Philips. Motherboard support is appearing.
+
+ EHCI controllers are packaged with "companion" host controllers (OHCI
+ or UHCI) to handle USB 1.1 devices connected to root hub ports. Ports
+ will connect to EHCI if it the device is high speed, otherwise they
+ connect to a companion controller. If you configure EHCI, you should
+ probably configure the OHCI (for NEC and some other vendors) USB Host
+ Controller Driver too.
+
+ You may want to read <file:Documentation/usb/ehci.txt>.
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called ehci-hcd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
UHCI (Intel PIIX4, VIA, ...) support
CONFIG_USB_UHCI
The Universal Host Controller Interface is a standard by Intel for
diff -Nru a/Documentation/usb/ehci.txt b/Documentation/usb/ehci.txt
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/Documentation/usb/ehci.txt Mon Feb 25 16:54:37 2002
@@ -0,0 +1,164 @@
+18-Dec-2001
+
+The EHCI driver is used to talk to high speed USB 2.0 devices using
+USB 2.0-capable host controller hardware. The USB 2.0 standard is
+compatible with the USB 1.1 standard. It defines three transfer speeds:
+
+ - "High Speed" 480 Mbit/sec (60 MByte/sec)
+ - "Full Speed" 12 Mbit/sec (1.5 MByte/sec)
+ - "Low Speed" 1.5 Mbit/sec
+
+USB 1.1 only addressed full speed and low speed. High speed devices
+can be used on USB 1.1 systems, but they slow down to USB 1.1 speeds.
+
+USB 1.1 devices may also be used on USB 2.0 systems. When plugged
+into an EHCI controller, they are given to a USB 1.1 "companion"
+controller, which is a OHCI or UHCI controller as normally used with
+such devices. When USB 1.1 devices plug into USB 2.0 hubs, they
+interact with the EHCI controller through a "Transaction Translator"
+(TT) in the hub, which turns low or full speed transactions into
+high speed "split transactions" that don't waste transfer bandwidth.
+
+At this writing, high speed devices are finally beginning to appear.
+While usb-storage devices have been available for some time (working
+quite speedily on the 2.4 version of this driver), hubs have only
+very recently become available.
+
+Note that USB 2.0 support involves more than just EHCI. It requires
+other changes to the Linux-USB core APIs, including the hub driver,
+but those changes haven't needed to really change the basic "usbcore"
+APIs exposed to USB device drivers.
+
+- David Brownell
+ <dbrownell@users.sourceforge.net>
+
+
+FUNCTIONALITY
+
+This driver is regularly tested on x86 hardware, and has also been
+used on PPC hardware so big/little endianneess issues should be gone.
+It's believed to do all the right PCI magic so that I/O works even on
+systems with interesting DMA mapping issues.
+
+At this writing the driver should comfortably handle all control and bulk
+transfers, including requests to USB 1.1 devices through transaction
+translators (TTs) in USB 2.0 hubs. However, there some situations where
+the hub driver needs to clear TT error state, which it doesn't yet do.
+
+Interrupt transfer support is newly functional and not yet as robust as
+control and bulk traffic. As yet there is no support for split transaction
+scheduling for interrupt transfers, which means among other things that
+connecting USB 1.1 hubs, keyboards, and mice to USB 2.0 hubs won't work.
+Connect them to USB 1.1 hubs, or to a root hub.
+
+Isochronous (ISO) transfer support is not yet working. No production
+high speed devices are available which would need it (though high quality
+webcams are in the works!). Note that split transaction support for ISO
+transfers can't share much code with the code for high speed ISO transfers,
+since EHCI represents these with a different data structure.
+
+The EHCI root hub code should hand off USB 1.1 devices to its companion
+controller. This driver doesn't need to know anything about those
+drivers; a OHCI or UHCI driver that works already doesn't need to change
+just because the EHCI driver is also present.
+
+There are some issues with power management; suspend/resume doesn't
+behave quite right at the moment.
+
+
+USE BY
+
+Assuming you have an EHCI controller (on a PCI card or motherboard)
+and have compiled this driver as a module, load this like:
+
+ # modprobe ehci-hcd
+
+and remove it by:
+
+ # rmmod ehci-hcd
+
+You should also have a driver for a "companion controller", such as
+"ohci-hcd", "usb-ohci", "usb-uhci", or "uhci". In case of any trouble
+with the EHCI driver, remove its module and then the driver for that
+companion controller will take over (at lower speed) all the devices
+that were previously handled by the EHCI driver.
+
+Module parameters (pass to "modprobe") include:
+
+ log2_irq_thresh (default 0):
+ Log2 of default interrupt delay, in microframes. The default
+ value is 0, indicating 1 microframe (125 usec). Maximum value
+ is 6, indicating 2^6 = 64 microframes. This controls how often
+ the EHCI controller can issue interrupts.
+
+The EHCI interrupt handler just acknowledges interrupts and schedules
+a tasklet to handle whatever needs handling. That keeps latencies low,
+no matter how often interrupts are issued.
+
+Device drivers shouldn't care whether they're running over EHCI or not,
+but they may want to check for "usb_device->speed == USB_SPEED_HIGH".
+High speed devices can do things that full speed (or low speed) ones
+can't, such as "high bandwidth" periodic (interrupt or ISO) transfers.
+
+
+PERFORMANCE
+
+USB 2.0 throughput is gated by two main factors: how fast the host
+controller can process requests, and how fast devices can respond to
+them. The 480 Mbit/sec "raw transfer rate" is obeyed by all devices,
+but aggregate throughput is also affected by issues like delays between
+individual high speed packets, driver intelligence, and of course the
+overall system load. Latency is also a performance concern.
+
+Bulk transfers are most often used where throughput is an issue. It's
+good to keep in mind that bulk transfers are always in 512 byte packets,
+and at most 13 of those fit into one USB 2.0 microframe. Eight USB 2.0
+microframes fit in a USB 1.1 frame; a microframe is 1 msec/8 = 125 usec.
+
+Hardware Performance
+
+At this writing, individual USB 2.0 devices tend to max out at around
+20 MByte/sec transfer rates. This is of course subject to change;
+and some devices now go faster, while others go slower.
+
+The NEC implementation of EHCI seems to have a hardware bottleneck
+at around 28 MByte/sec aggregate transfer rate. While this is clearly
+enough for a single device at 20 MByte/sec, putting three such devices
+onto one bus does not get you 60 MByte/sec. The issue appears to be
+that the controller hardware won't do concurrent USB and PCI access,
+so that it's only trying six (or maybe seven) USB transactions each
+microframe rather than thirteen. (Seems like a reasonable trade off
+for a product that beat all the others to market by over a year!)
+It's expected that newer implementations will better this, throwing
+more silicon real estate at the problem so that new motherboard chip
+sets will get closer to that 60 MByte/sec target.
+
+There's a minimum latency of one microframe (125 usec) for the host
+to receive interrupts from the EHCI controller indicating completion
+of requests. That latency is tunable; there's a module option. By
+default ehci-hcd driver uses the minimum latency, which means that if
+you issue a control or bulk request you can often expect to learn that
+it completed in less than 250 usec (depending on transfer size).
+
+Software Performance
+
+To get even 20 MByte/sec transfer rates, Linux-USB device drivers will
+need to keep the EHCI queue full. That means issuing large requests,
+or using bulk queuing if a series of small requests needs to be issued.
+When drivers don't do that, their performance results will show it.
+
+In typical situations, a usb_bulk_msg() loop writing out 4 KB chunks is
+going to waste more than half the USB 2.0 bandwidth. Delays between the
+I/O completion and the driver issuing the next request will take longer
+than the I/O. If that same loop used 16 KB chunks, it'd be better; a
+sequence of 128 KB chunks would waste a lot less.
+
+But rather than depending on such large I/O buffers to make synchronous
+I/O be efficient, it's better to just queue all several (bulk) requests
+to the HC, and wait for them all to complete (or be canceled on error).
+Such URB queuing should work with all the USB 1.1 HC drivers too.
+
+TBD: Interrupt and ISO transfer performance issues. Those periodic
+transfers are fully scheduled, so the main issue is likely to be how
+to trigger "high bandwidth" modes.
+
diff -Nru a/drivers/usb/hcd/Config.in b/drivers/usb/hcd/Config.in
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/Config.in Mon Feb 25 16:54:37 2002
@@ -0,0 +1,7 @@
+#
+# USB Host Controller Drivers
+#
+dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+# dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+# dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+
diff -Nru a/drivers/usb/hcd/Makefile b/drivers/usb/hcd/Makefile
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/Makefile Mon Feb 25 16:54:37 2002
@@ -0,0 +1,27 @@
+#
+# Makefile for USB Host Controller Driver
+# framework and drivers
+#
+
+O_TARGET :=
+
+obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
+# obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
+# obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+multi-y := $(filter $(list-multi), $(obj-y))
+multi-m := $(filter $(list-multi), $(obj-m))
+int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Take multi-part drivers out of obj-y and put components in.
+obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y)
+
+# Translate to Rules.make lists.
+OX_OBJS := $(obj-y)
+MX_OBJS := $(obj-m)
+MIX_OBJS := $(int-m)
+
+include $(TOPDIR)/Rules.make
diff -Nru a/drivers/usb/hcd/ehci-dbg.c b/drivers/usb/hcd/ehci-dbg.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci-dbg.c Mon Feb 25 16:54:37 2002
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+#ifdef EHCI_VERBOSE_DEBUG
+# define vdbg dbg
+#else
+ static inline void vdbg (char *fmt, ...) { }
+#endif
+
+#ifdef DEBUG
+
+/* check the values in the HCSPARAMS register - host controller structural parameters */
+/* see EHCI 0.95 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);
+
+ dbg ("%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d",
+ label, params,
+ HCS_DEBUG_PORT (params),
+ HCS_INDICATOR (params) ? " ind" : "",
+ HCS_N_CC (params),
+ HCS_N_PCC (params),
+ HCS_PORTROUTED (params) ? "" : " ordered",
+ HCS_PPC (params) ? "" : " !ppc",
+ HCS_N_PORTS (params)
+ );
+ /* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
+ if (HCS_PORTROUTED (params)) {
+ int i;
+ char buf [46], tmp [7], byte;
+
+ buf[0] = 0;
+ for (i = 0; i < HCS_N_PORTS (params); i++) {
+ byte = readb (&ehci->caps->portroute[(i>>1)]);
+ sprintf(tmp, "%d ",
+ ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
+ strcat(buf, tmp);
+ }
+ dbg ("%s: %s portroute %s",
+ ehci->hcd.bus_name, label,
+ buf);
+ }
+}
+#else
+
+static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef DEBUG
+
+/* check the values in the HCCPARAMS register - host controller capability parameters */
+/* see EHCI 0.95 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",
+ label, HCC_EXT_CAPS (params));
+ }
+ if (HCC_ISOC_CACHE (params)) {
+ dbg ("%s hcc_params 0x%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",
+ label,
+ params,
+ HCC_ISOC_THRES (params),
+ HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
+ HCC_CANPARK (params) ? " park" : "",
+ HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+ }
+}
+#else
+
+static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef DEBUG
+
+#if 0
+static void 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,
+ qh->hw_current, qh->hw_qtd_next);
+ dbg (" alt+errs= %x, token= %x, page0= %x, page1= %x",
+ qh->hw_alt_next, qh->hw_token,
+ qh->hw_buf [0], qh->hw_buf [1]);
+ if (qh->hw_buf [2]) {
+ dbg (" page2= %x, page3= %x, page4= %x",
+ qh->hw_buf [2], qh->hw_buf [3],
+ qh->hw_buf [4]);
+ }
+}
+#endif
+
+static const char *const fls_strings [] =
+ { "1024", "512", "256", "??" };
+
+#else
+#if 0
+static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
+#endif
+#endif /* DEBUG */
+
+/* functions have the "wrong" filename when they're output... */
+
+#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" : "" \
+ )
+
diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci-hcd.c Mon Feb 25 16:54:37 2002
@@ -0,0 +1,761 @@
+/*
+ * Copyright (c) 2000-2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#ifndef CONFIG_USB_DEBUG
+ #define CONFIG_USB_DEBUG /* this is still experimental! */
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "../hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+//#undef KERN_DEBUG
+//#define KERN_DEBUG ""
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hc_driver implementation ... experimental, incomplete.
+ * Based on the 0.96 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
+ * ...
+ *
+ * HISTORY:
+ * 2002-01-14 Minor cleanup; version synch.
+ * 2002-01-08 Fix roothub handoff of FS/LS to companion controllers.
+ * 2002-01-04 Control/Bulk queuing behaves.
+ * 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
+ */
+
+#define DRIVER_VERSION "$Revision: 0.26 $"
+#define DRIVER_AUTHOR "David Brownell"
+#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
+
+
+// #define EHCI_VERBOSE_DEBUG
+// #define have_iso
+
+#ifdef CONFIG_DEBUG_SLAB
+# define EHCI_SLAB_FLAGS (SLAB_POISON)
+#else
+# define EHCI_SLAB_FLAGS 0
+#endif
+
+/* 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 */
+#define EHCI_TUNE_RL_TT 0
+#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define EHCI_TUNE_MULT_TT 1
+
+/* 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");
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci.h"
+#include "ehci-dbg.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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)
+{
+ 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;
+}
+
+/* idle the controller (from running) */
+static void ehci_ready (struct ehci_hcd *ehci)
+{
+ u32 command;
+
+#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);
+
+ // hardware can take 16 microframes to turn off ...
+ ehci->hcd.state = USB_STATE_READY;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci-hub.c"
+#include "ehci-mem.c"
+#include "ehci-q.c"
+#include "ehci-sched.c"
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_tasklet (unsigned long param);
+
+/* called by khubd or root hub init threads */
+
+static int ehci_start (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ struct usb_device *udev;
+ 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;
+ ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length);
+ dbg_hcs_params (ehci, "ehci_start");
+ dbg_hcc_params (ehci, "ehci_start");
+
+ /*
+ * hw default: 1K periodic list heads, one per frame.
+ * periodic_size can shrink by USBCMD update if hcc_params allows.
+ */
+ ehci->periodic_size = DEFAULT_I_TDPS;
+ if ((retval = ehci_mem_init (ehci, EHCI_SLAB_FLAGS | 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
+ ehci->i_thresh = 8;
+ else // N microframes cached
+ ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
+
+ ehci->async = 0;
+ ehci->reclaim = 0;
+ ehci->next_frame = -1;
+
+ /* controller state: unknown --> reset */
+
+ /* EHCI spec section 4.1 */
+ ehci_reset (ehci);
+ writel (INTR_MASK, &ehci->regs->intr_enable);
+ writel (ehci->periodic_dma, &ehci->regs->frame_list);
+
+ /*
+ * hcc_params controls whether ehci->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * By default, pci_alloc_consistent() won't hand out addresses
+ * above 4GB (via pdev->dma_mask) so we know this value.
+ *
+ * NOTE: that pdev->dma_mask setting means that all DMA mappings
+ * for I/O buffers will have the same restriction, though it's
+ * neither necessary nor desirable in that case.
+ */
+ if (HCC_64BIT_ADDR (hcc_params)) {
+ writel (0, &ehci->regs->segment);
+ info ("using segment 0 for 64bit DMA addresses ...");
+ }
+
+ /* 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;
+ temp |= 1 << (16 + log2_irq_thresh);
+ // keeping default periodic framelist size
+ temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
+ writel (temp, &ehci->regs->command);
+ dbg_cmd (ehci, "init", temp);
+
+ /* set async sleep time = 10 us ... ? */
+
+ ehci->tasklet.func = ehci_tasklet;
+ ehci->tasklet.data = (unsigned long) ehci;
+
+ /* wire up the root hub */
+ hcd->bus->root_hub = udev = usb_alloc_dev (NULL, hcd->bus);
+ if (!udev) {
+done2:
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /*
+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+ * are explicitly handed to companion controller(s), so no TT is
+ * involved with the root hub.
+ */
+ ehci->hcd.state = USB_STATE_READY;
+ writel (FLAG_CF, &ehci->regs->configured_flag);
+ readl (&ehci->regs->command); /* unblock posted write */
+
+ /* 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);
+
+ /*
+ * From here on, khubd concurrently accesses the root
+ * hub; drivers will be talking to enumerated devices.
+ *
+ * Before this point the HC was idle/ready. After, khubd
+ * and device drivers may start it running.
+ */
+ usb_connect (udev);
+ udev->speed = USB_SPEED_HIGH;
+ if (usb_new_device (udev) != 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;
+ usb_free_dev (udev);
+ retval = -ENODEV;
+ goto done2;
+ }
+
+ return 0;
+}
+
+/* always called by thread; normally rmmod */
+
+static void ehci_stop (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+
+ dbg ("%s: stop", hcd->bus_name);
+
+ if (hcd->state == USB_STATE_RUNNING)
+ ehci_ready (ehci);
+ while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS))
+ udelay (100);
+ ehci_reset (ehci);
+
+ // root hub is shut down separately (first, when possible)
+ scan_async (ehci);
+ if (ehci->next_frame != -1)
+ scan_periodic (ehci);
+ ehci_mem_cleanup (ehci);
+
+ dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
+}
+
+static int ehci_get_frame (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+static int ehci_suspend (struct usb_hcd *hcd, u32 state)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 params;
+ int ports;
+ int i;
+
+ dbg ("%s: suspend to %d", hcd->bus_name, state);
+
+ params = readl (&ehci->caps->hcs_params);
+ ports = HCS_N_PORTS (params);
+
+ // FIXME: This assumes what's probably a D3 level suspend...
+
+ // FIXME: usb wakeup events on this bus should resume the machine.
+ // pci config register PORTWAKECAP controls which ports can do it;
+ // bios may have initted the register...
+
+ /* suspend each port, then stop the hc */
+ for (i = 0; i < ports; i++) {
+ int temp = readl (&ehci->regs->port_status [i]);
+
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_OWNER) != 0)
+ continue;
+dbg ("%s: suspend port %d", 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
+
+ /* who tells PCI to reduce power consumption? */
+
+ return 0;
+}
+
+static int ehci_resume (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 params;
+ int ports;
+ int i;
+
+ dbg ("%s: resume", hcd->bus_name);
+
+ params = readl (&ehci->caps->hcs_params);
+ ports = HCS_N_PORTS (params);
+
+ // FIXME: if controller didn't retain state,
+ // return and let generic code clean it up
+ // test configured_flag ?
+
+ /* resume HC and each port */
+// restore pci FLADJ value
+ // khubd and drivers will set HC running, if needed;
+ hcd->state = USB_STATE_READY;
+ for (i = 0; i < ports; i++) {
+ int temp = readl (&ehci->regs->port_status [i]);
+
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_SUSPEND) != 0)
+ continue;
+dbg ("%s: resume port %d", hcd->bus_name, i);
+ temp |= PORT_RESUME;
+ writel (temp, &ehci->regs->port_status [i]);
+ readl (&ehci->regs->command); /* unblock posted writes */
+
+ wait_ms (20);
+ temp &= ~PORT_RESUME;
+ writel (temp, &ehci->regs->port_status [i]);
+ }
+ readl (&ehci->regs->command); /* unblock posted writes */
+ return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * tasklet scheduled by some interrupts and other events
+ * calls driver completion functions ... but not in_irq()
+ */
+static void ehci_tasklet (unsigned long param)
+{
+ struct ehci_hcd *ehci = (struct ehci_hcd *) param;
+
+ if (ehci->reclaim_ready)
+ end_unlink_async (ehci);
+ scan_async (ehci);
+ if (ehci->next_frame != -1)
+ scan_periodic (ehci);
+
+ // FIXME: when nothing is connected to the root hub,
+ // turn off the RUN bit so the host can enter C3 "sleep" power
+ // saving mode; make root hub code scan memory less often.
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_irq (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 status = readl (&ehci->regs->status);
+ int bh = 0;
+
+ /* clear (just) interrupts */
+ status &= INTR_MASK;
+ writel (status, &ehci->regs->status);
+ readl (&ehci->regs->command); /* unblock posted write */
+
+ if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
+ return;
+
+#ifdef EHCI_VERBOSE_DEBUG
+ /* unrequested/ignored: Port Change Detect, Frame List Rollover */
+ if (status & INTR_MASK)
+ dbg_status (ehci, "irq", status);
+#endif
+
+ /* 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))
+ bh = 1;
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+ 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);
+ ehci_reset (ehci);
+ // generic layer kills/unlinks all urbs
+ // then tasklet cleans up the rest
+ bh = 1;
+ }
+
+ /* most work doesn't need to be in_irq() */
+ if (likely (bh == 1))
+ tasklet_schedule (&ehci->tasklet);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd_dev.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: EHCI queues control and bulk requests transparently, like OHCI.
+ */
+static int ehci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct list_head qtd_list;
+
+ urb->transfer_flags &= ~EHCI_STATE_UNLINK;
+ INIT_LIST_HEAD (&qtd_list);
+ switch (usb_pipetype (urb->pipe)) {
+
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ submit_async (ehci, urb, &qtd_list, mem_flags);
+ break;
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit (ehci, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+#ifdef have_iso
+ if (urb->dev->speed == USB_SPEED_HIGH)
+ return itd_submit (ehci, urb);
+ else
+ return sitd_submit (ehci, urb);
+#else
+ // FIXME highspeed iso stuff is written but never run/tested.
+ // and the split iso support isn't even written yet.
+ dbg ("no iso support yet");
+ return -ENOSYS;
+#endif /* have_iso */
+
+ }
+ return 0;
+}
+
+/* remove from hardware lists
+ * completions normally happen asynchronously
+ */
+
+static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
+ unsigned long flags;
+
+ dbg ("%s urb_dequeue %p qh state %d",
+ hcd->bus_name, urb, qh->qh_state);
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (ehci->reclaim) {
+dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
+ if (in_interrupt ()) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return -EAGAIN;
+ }
+ while (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && ehci->hcd.state != USB_STATE_HALT
+ ) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+// 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);
+ 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;
+
+ case PIPE_INTERRUPT:
+ intr_deschedule (ehci, urb->start_frame, qh, urb->interval);
+ if (ehci->hcd.state == USB_STATE_HALT)
+ urb->status = -ESHUTDOWN;
+ qh_completions (ehci, &qh->qtd_list, 1);
+ return 0;
+
+ case PIPE_ISOCHRONOUS:
+ // itd or sitd ...
+
+ // wait till next completion, do it then.
+ // completion irqs can wait up to 128 msec,
+ urb->transfer_flags |= EHCI_STATE_UNLINK;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// bulk qh holds the data toggle
+
+static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct hcd_dev *dev = (struct hcd_dev *)udev->hcpriv;
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ int i;
+ unsigned long flags;
+
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+ dbg ("%s: free_config devnum %d", 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;
+
+ // FIXME: this might be an itd/sitd too ...
+ // or an interrupt urb (not on async list)
+ // can use "union ehci_shadow"
+
+ 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);
+ BUG ();
+ }
+ dev->ep [i] = 0;
+
+ /* wait_ms() won't spin here -- we're a thread */
+ while (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && ehci->hcd.state != USB_STATE_HALT
+ ) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ wait_ms (1);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
+ 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);
+ }
+ }
+ qh_unput (ehci, qh);
+ }
+ }
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const char hcd_name [] = "ehci-hcd";
+
+static const struct hc_driver ehci_driver = {
+ description: hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ irq: ehci_irq,
+ flags: HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ start: ehci_start,
+#ifdef CONFIG_PM
+ suspend: ehci_suspend,
+ resume: ehci_resume,
+#endif
+ stop: ehci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ 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,
+
+ /*
+ * scheduling support
+ */
+ get_frame_number: ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ hub_status_data: ehci_hub_status_data,
+ hub_control: ehci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI spec says PCI is required. */
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+ /* handle any USB 2.0 EHCI controller */
+
+ 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,
+
+}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* 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,
+
+ probe: usb_hcd_pci_probe,
+ remove: usb_hcd_pci_remove,
+
+#ifdef CONFIG_PM
+ 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");
+
+static int __init init (void)
+{
+ dbg (DRIVER_INFO);
+ dbg ("block sizes: qh %d qtd %d itd %d sitd %d",
+ sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
+ sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
+
+ return pci_module_init (&ehci_pci_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+ pci_unregister_driver (&ehci_pci_driver);
+}
+module_exit (cleanup);
diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci-hub.c Mon Feb 25 16:54:37 2002
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static int check_reset_complete (
+ struct ehci_hcd *ehci,
+ int index,
+ int port_status
+) {
+ if (!(port_status & PORT_CONNECT)) {
+ ehci->reset_done [index] = 0;
+ return port_status;
+ }
+
+ /* 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);
+
+ // 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);
+
+ return port_status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp, status = 0;
+ int ports, i, retval = 1;
+ unsigned long flags;
+
+ /* init status to no-changes */
+ buf [0] = 0;
+ temp = readl (&ehci->caps->hcs_params);
+ ports = HCS_N_PORTS (temp);
+ if (ports > 7) {
+ buf [1] = 0;
+ retval++;
+ }
+
+ /* no hub change reports (bit 0) for now (power, ...) */
+
+ /* port N changes (bit N)? */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < ports; i++) {
+ temp = readl (&ehci->regs->port_status [i]);
+ if (temp & PORT_OWNER) {
+ /* don't report this in GetPortStatus */
+ if (temp & PORT_CSC) {
+ temp &= ~PORT_CSC;
+ writel (temp, &ehci->regs->port_status [i]);
+ }
+ continue;
+ }
+ if (!(temp & PORT_CONNECT))
+ ehci->reset_done [i] = 0;
+ if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
+ set_bit (i, buf);
+ status = STS_PCD;
+ }
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ehci_hub_descriptor (
+ struct ehci_hcd *ehci,
+ struct usb_hub_descriptor *desc
+) {
+ u32 params = readl (&ehci->caps->hcs_params);
+ int ports = HCS_N_PORTS (params);
+ u16 temp;
+
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset (&desc->bitmap [0], 0, temp);
+ memset (&desc->bitmap [temp], 0xff, temp);
+
+ temp = 0x0008; /* per-port overcurrent reporting */
+ if (HCS_PPC (params)) /* per-port power control */
+ temp |= 0x0001;
+ if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */
+ temp |= 0x0080;
+ desc->wHubCharacteristics = cpu_to_le16 (temp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 params = readl (&ehci->caps->hcs_params);
+ int ports = HCS_N_PORTS (params);
+ u32 temp;
+ unsigned long flags;
+ int retval = 0;
+
+ /*
+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+ * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+ * (track current state ourselves) ... blink for diagnostics,
+ * power, "this is the one", etc. EHCI spec supports this.
+ */
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = readl (&ehci->regs->port_status [wIndex]);
+ if (temp & PORT_OWNER)
+ break;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ writel (temp & ~PORT_PE,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ writel (temp | PORT_PEC,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ case USB_PORT_FEAT_C_SUSPEND:
+ /* ? */
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC (params))
+ writel (temp & ~PORT_POWER,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ writel (temp | PORT_CSC,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ writel (temp | PORT_OCC,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* GetPortStatus clears reset */
+ break;
+ default:
+ goto error;
+ }
+ readl (&ehci->regs->command); /* unblock posted write */
+ break;
+ case GetHubDescriptor:
+ ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
+ buf);
+ break;
+ case GetHubStatus:
+ /* no hub-wide feature/status flags */
+ memset (buf, 0, 4);
+ //cpu_to_le32s ((u32 *) buf);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ memset (buf, 0, 4);
+ temp = readl (&ehci->regs->port_status [wIndex]);
+
+ // wPortChange bits
+ if (temp & PORT_CSC)
+ set_bit (USB_PORT_FEAT_C_CONNECTION, buf);
+ if (temp & PORT_PEC)
+ set_bit (USB_PORT_FEAT_C_ENABLE, buf);
+ // USB_PORT_FEAT_C_SUSPEND
+ if (temp & PORT_OCC)
+ set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf);
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESET)
+ && jiffies > ehci->reset_done [wIndex]) {
+ set_bit (USB_PORT_FEAT_C_RESET, buf);
+
+ /* force reset to complete */
+ writel (temp & ~PORT_RESET,
+ &ehci->regs->port_status [wIndex]);
+ do {
+ temp = readl (
+ &ehci->regs->port_status [wIndex]);
+ udelay (10);
+ } while (temp & PORT_RESET);
+
+ /* see what we found out */
+ temp = check_reset_complete (ehci, wIndex, temp);
+ }
+
+ // don't show wPortStatus if it's owned by a companion hc
+ if (!(temp & PORT_OWNER)) {
+ if (temp & PORT_CONNECT) {
+ set_bit (USB_PORT_FEAT_CONNECTION, buf);
+ set_bit (USB_PORT_FEAT_HIGHSPEED, buf);
+ }
+ if (temp & PORT_PE)
+ set_bit (USB_PORT_FEAT_ENABLE, buf);
+ if (temp & PORT_SUSPEND)
+ set_bit (USB_PORT_FEAT_SUSPEND, buf);
+ if (temp & PORT_OC)
+ set_bit (USB_PORT_FEAT_OVER_CURRENT, buf);
+ if (temp & PORT_RESET)
+ set_bit (USB_PORT_FEAT_RESET, buf);
+ if (temp & PORT_POWER)
+ set_bit (USB_PORT_FEAT_POWER, buf);
+ }
+
+#ifndef EHCI_VERBOSE_DEBUG
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ dbg_port (hcd, "GetStatus", wIndex + 1, temp);
+ cpu_to_le32s ((u32 *) buf);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = readl (&ehci->regs->port_status [wIndex]);
+ if (temp & PORT_OWNER)
+ break;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ writel (temp | PORT_SUSPEND,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC (params))
+ writel (temp | PORT_POWER,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ /* line status bits may report this as low speed */
+ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
+ && PORT_USB11 (temp)) {
+ dbg ("%s port %d low speed, give to companion",
+ hcd->bus_name, wIndex + 1);
+ temp |= PORT_OWNER;
+ } else {
+ vdbg ("%s port %d reset",
+ hcd->bus_name, wIndex + 1);
+ temp |= PORT_RESET;
+ temp &= ~PORT_PE;
+
+ /*
+ * caller must wait, then call GetPortStatus
+ * usb 2.0 spec says 50 ms resets on root
+ */
+ ehci->reset_done [wIndex] = jiffies
+ + ((50 /* msec */ * HZ) / 1000);
+ }
+ writel (temp, &ehci->regs->port_status [wIndex]);
+ break;
+ default:
+ goto error;
+ }
+ readl (&ehci->regs->command); /* unblock posted writes */
+ break;
+
+ default:
+error:
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return retval;
+}
diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci-mem.c Mon Feb 25 16:54:37 2002
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use pci_pool or pci_alloc_consistent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+/*
+ * Allocator / cleanup for the per device structure
+ * Called by hcd init / removal code
+ */
+static struct usb_hcd *ehci_hcd_alloc (void)
+{
+ struct ehci_hcd *ehci;
+
+ ehci = (struct ehci_hcd *)
+ kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL);
+ if (ehci != 0) {
+ memset (ehci, 0, sizeof (struct ehci_hcd));
+ return &ehci->hcd;
+ }
+ return 0;
+}
+
+static void ehci_hcd_free (struct usb_hcd *hcd)
+{
+ kfree (hcd_to_ehci (hcd));
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
+{
+ struct ehci_qtd *qtd;
+ dma_addr_t dma;
+
+ qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
+ if (qtd != 0) {
+ memset (qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ INIT_LIST_HEAD (&qtd->qtd_list);
+ }
+ return qtd;
+}
+
+static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
+{
+ struct ehci_qh *qh;
+ dma_addr_t dma;
+
+ qh = (struct ehci_qh *)
+ pci_pool_alloc (ehci->qh_pool, flags, &dma);
+ if (qh) {
+ memset (qh, 0, sizeof *qh);
+ atomic_set (&qh->refcount, 1);
+ qh->qh_dma = dma;
+ // INIT_LIST_HEAD (&qh->qh_list);
+ INIT_LIST_HEAD (&qh->qtd_list);
+ }
+ return qh;
+}
+
+/* to share a qh (cpu threads, or hc) */
+static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh)
+{
+ // dbg ("put %p (%d++)", qh, qh->refcount.counter);
+ atomic_inc (&qh->refcount);
+ return qh;
+}
+
+static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ // dbg ("unput %p (--%d)", qh, qh->refcount.counter);
+ if (!atomic_dec_and_test (&qh->refcount))
+ return;
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
+ dbg ("unused qh not empty!");
+ BUG ();
+ }
+ pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void ehci_mem_cleanup (struct ehci_hcd *ehci)
+{
+ /* PCI consistent memory and pools */
+ if (ehci->qtd_pool)
+ pci_pool_destroy (ehci->qtd_pool);
+ ehci->qtd_pool = 0;
+
+ if (ehci->qh_pool) {
+ pci_pool_destroy (ehci->qh_pool);
+ ehci->qh_pool = 0;
+ }
+
+ if (ehci->itd_pool)
+ pci_pool_destroy (ehci->itd_pool);
+ ehci->itd_pool = 0;
+
+ if (ehci->sitd_pool)
+ pci_pool_destroy (ehci->sitd_pool);
+ ehci->sitd_pool = 0;
+
+ if (ehci->periodic)
+ pci_free_consistent (ehci->hcd.pdev,
+ ehci->periodic_size * sizeof (u32),
+ ehci->periodic, ehci->periodic_dma);
+ ehci->periodic = 0;
+
+ /* shadow periodic table */
+ if (ehci->pshadow)
+ kfree (ehci->pshadow);
+ ehci->pshadow = 0;
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int ehci_mem_init (struct ehci_hcd *ehci, int flags)
+{
+ int i;
+
+ /* QTDs for control/bulk/intr transfers */
+ ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev,
+ sizeof (struct ehci_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->qtd_pool) {
+ dbg ("no qtd pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* QH for control/bulk/intr transfers */
+ ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
+ sizeof (struct ehci_qh),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->qh_pool) {
+ dbg ("no qh pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* ITD for high speed ISO transfers */
+ ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev,
+ sizeof (struct ehci_itd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->itd_pool) {
+ dbg ("no itd pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* SITD for full/low speed split ISO transfers */
+ ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev,
+ sizeof (struct ehci_sitd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->sitd_pool) {
+ dbg ("no sitd pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* Hardware periodic table */
+ ehci->periodic = (u32 *)
+ pci_alloc_consistent (ehci->hcd.pdev,
+ ehci->periodic_size * sizeof (u32),
+ &ehci->periodic_dma);
+ if (ehci->periodic == 0) {
+ dbg ("no hw periodic table");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ehci->periodic_size; i++)
+ ehci->periodic [i] = EHCI_LIST_END;
+
+ /* software shadow of hardware table */
+ ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *),
+ flags & ~EHCI_SLAB_FLAGS);
+ if (ehci->pshadow == 0) {
+ dbg ("no shadow periodic table");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+ memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
+
+ return 0;
+}
diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci-q.c Mon Feb 25 16:54:37 2002
@@ -0,0 +1,969 @@
+/*
+ * Copyright (c) 2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hardware queue manipulation
+ *
+ * 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 has one qtd, one urb.
+ *
+ * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge.
+ *
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries. TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+
+static int
+qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token)
+{
+ int i, count;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf [0] = cpu_to_le32 (buf);
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely (len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ u64 addr = buf;
+ qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
+ qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+ }
+ qtd->hw_token = cpu_to_le32 ((count << 16) | token);
+ qtd->length = count;
+
+#if 0
+ vdbg (" qtd_fill %p, token %8x bytes %d dma %x",
+ qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]);
+#endif
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* update halted (but potentially linked) qh */
+
+static inline void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ qh->hw_current = 0;
+ qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END;
+
+ /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
+ qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
+{
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely (QTD_PID (token) != 2))
+ urb->actual_length += length - QTD_LENGTH (token);
+
+ /* don't modify error codes */
+ if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) {
+ if (token & QTD_STS_BABBLE) {
+ urb->status = -EOVERFLOW;
+ } else if (!QTD_CERR (token)) {
+ if (token & QTD_STS_DBE)
+ urb->status = (QTD_PID (token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ else if (token & QTD_STS_MMF) /* missed tt uframe */
+ urb->status = -EPROTO;
+ else if (token & QTD_STS_XACT) {
+ if (QTD_LENGTH (token))
+ urb->status = -EPIPE;
+ else {
+ dbg ("3strikes");
+ urb->status = -EPROTO;
+ }
+ } else /* presumably a stall */
+ urb->status = -EPIPE;
+
+ /* CERR nonzero + data left + halt --> stall */
+ } else if (QTD_LENGTH (token))
+ urb->status = -EPIPE;
+ else /* unknown */
+ urb->status = -EPROTO;
+ dbg ("ep %d-%s qtd token %08x --> status %d",
+ /* devpath */
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ token, urb->status);
+
+ /* stall indicates some recovery action is needed */
+ if (urb->status == -EPIPE) {
+ int pipe = urb->pipe;
+
+ if (!usb_pipecontrol (pipe))
+ usb_endpoint_halt (urb->dev,
+ 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() ?
+ }
+ }
+ }
+}
+
+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);
+
+ /* 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;
+ }
+
+ /* only report unlinks once */
+ if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN))
+ urb->complete (urb);
+}
+
+/* urb->lock ignored from here on (hcd is done with urb) */
+
+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_unput (ehci, (struct ehci_qh *) urb->hcpriv);
+ urb->hcpriv = 0;
+ }
+
+ 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;
+ }
+
+ /* hand off urb ownership */
+ usb_hcd_giveback_urb (&ehci->hcd, urb);
+}
+
+
+/*
+ * 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.
+ */
+static int
+qh_completions (
+ struct ehci_hcd *ehci,
+ struct list_head *qtd_list,
+ int freeing
+) {
+ struct ehci_qtd *qtd, *last;
+ struct list_head *next;
+ struct ehci_qh *qh = 0;
+ int unlink = 0, halted = 0;
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely (list_empty (qtd_list))) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return retval;
+ }
+
+ /* scan QTDs till end of list, or we reach an active one */
+ for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),
+ last = 0, next = 0;
+ next != qtd_list;
+ last = qtd, qtd = list_entry (next,
+ struct ehci_qtd, qtd_list)) {
+ struct urb *urb = qtd->urb;
+ u32 token = 0;
+
+ /* qh is non-null iff these qtds were queued to the HC */
+ qh = (struct ehci_qh *) urb->hcpriv;
+
+ /* 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++;
+ }
+
+ /* qh overlays can have HC's old cached copies of
+ * next qtd ptrs, if an URB was queued afterwards.
+ */
+ if (qh && cpu_to_le32 (last->qtd_dma) == qh->hw_current
+ && last->hw_next != qh->hw_qtd_next) {
+ qh->hw_alt_next = last->hw_alt_next;
+ qh->hw_qtd_next = last->hw_next;
+ }
+
+ if (likely (freeing != 0))
+ ehci_qtd_free (ehci, last);
+ last = 0;
+ }
+ next = qtd->qtd_list.next;
+
+ /* if these qtds were queued to the HC, some may be active.
+ * else we're cleaning up after a failed URB submission.
+ */
+ if (likely (qh != 0)) {
+ int qh_halted;
+
+ qh_halted = __constant_cpu_to_le32 (QTD_STS_HALT)
+ & qh->hw_token;
+ token = le32_to_cpu (qtd->hw_token);
+ halted = halted
+ || qh_halted
+ || (ehci->hcd.state == USB_STATE_HALT)
+ || (qh->qh_state == QH_STATE_IDLE);
+
+ /* QH halts only because of fault or unlink; in both
+ * cases, queued URBs get unlinked. But for unlink,
+ * URBs at the head of the queue can stay linked.
+ */
+ if (unlikely (halted != 0)) {
+
+ /* unlink everything because of HC shutdown? */
+ if (ehci->hcd.state == USB_STATE_HALT) {
+ freeing = unlink = 1;
+ urb->status = -ESHUTDOWN;
+
+ /* explicit unlink, starting here? */
+ } else if (qh->qh_state == QH_STATE_IDLE
+ && (urb->status == -ECONNRESET
+ || urb->status == -ENOENT)) {
+ freeing = unlink = 1;
+
+ /* unlink everything because of error? */
+ } else if (qh_halted
+ && !(token & QTD_STS_HALT)) {
+ freeing = unlink = 1;
+ if (urb->status == -EINPROGRESS)
+ urb->status = -ECONNRESET;
+
+ /* unlink the rest? */
+ } else if (unlink) {
+ urb->status = -ECONNRESET;
+
+ /* QH halted to unlink urbs after this? */
+ } else if ((token & QTD_STS_ACTIVE) != 0) {
+ qtd = 0;
+ continue;
+ }
+
+ /* Else QH is active, so we must not modify QTDs
+ * that HC may be working on. Break from loop.
+ */
+ } else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) {
+ next = qtd_list;
+ qtd = 0;
+ continue;
+ }
+
+ spin_lock (&urb->lock);
+ 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_IOC
+ | (EHCI_TUNE_CERR << 10)
+ | QTD_STS_ACTIVE);
+ 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);
+ }
+
+#if 0
+ if (urb->status == -EINPROGRESS)
+ vdbg (" qtd %p ok, urb %p, token %8x, len %d",
+ qtd, urb, token, urb->actual_length);
+ else
+ vdbg ("urb %p status %d, qtd %p, token %8x, len %d",
+ urb, urb->status, qtd, token,
+ urb->actual_length);
+#endif
+
+ /* SETUP for control urb? */
+ if (unlikely (QTD_PID (token) == 2))
+ pci_unmap_single (ehci->hcd.pdev,
+ qtd->buf_dma, sizeof (devrequest),
+ PCI_DMA_TODEVICE);
+ }
+
+ /* patch up list head? */
+ if (unlikely (halted && qh && !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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *
+qh_urb_transaction (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *head,
+ int flags
+) {
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf, map_buf;
+ int len, maxpacket;
+ u32 token;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ return 0;
+ qtd_prev = 0;
+ list_add_tail (&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ if (usb_pipecontrol (urb->pipe)) {
+ /* control request data is passed in the "setup" pid */
+
+ /* NOTE: this isn't smart about 64bit DMA, since it uses the
+ * default (32bit) mask rather than using the whole address
+ * space. we could set pdev->dma_mask to all-ones while
+ * getting this mapping, locking it and restoring before
+ * allocating qtd/qh/... or maybe only do that for the main
+ * data phase (below).
+ */
+ qtd->buf_dma = pci_map_single (
+ ehci->hcd.pdev,
+ urb->setup_packet,
+ sizeof (devrequest),
+ PCI_DMA_TODEVICE);
+ if (unlikely (!qtd->buf_dma))
+ goto cleanup;
+
+ /* SETUP pid */
+ qtd_fill (qtd, qtd->buf_dma, sizeof (devrequest),
+ token | (2 /* "setup" */ << 8));
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ len = urb->transfer_buffer_length;
+ if (likely (len > 0)) {
+ /* NOTE: sub-optimal mapping with 64bit DMA (see above) */
+ 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;
+
+ if (!buf || usb_pipein (urb->pipe))
+ 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));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ 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;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely (len <= 0))
+ break;
+
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+ }
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * bulk ones may need a terminating short packet (zero length).
+ */
+ if (likely (buf != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol (urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipebulk (urb->pipe)
+ && (urb->transfer_flags & USB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill (qtd, 0, 0, token);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
+ return head;
+
+cleanup:
+ urb->status = -ENOMEM;
+ qh_completions (ehci, head, 1);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Hardware maintains data toggle (like OHCI) ... here we (re)initialize
+ * the hardware data toggle in the QH, and set the pseudo-toggle in udev
+ * so we can see if usb_clear_halt() was called. NOP for control, since
+ * we set up qh->hw_info1 to always use the QTD toggle bits.
+ */
+static inline void
+clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
+{
+ vdbg ("clear toggle, dev %d ep 0x%x-%s",
+ udev->devnum, ep, is_out ? "out" : "in");
+ qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
+ usb_settoggle (udev, ep, is_out, 1);
+}
+
+// Would be best to create all qh's from config descriptors,
+// when each interface/altsetting is established. Unlink
+// any previous qh and cancel its urbs first; endpoints are
+// implicitly reset then (data toggle too).
+// That'd mean updating how usbcore talks to HCDs. (2.5?)
+
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct ehci_qh *
+ehci_qh_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int flags
+) {
+ struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
+ u32 info1 = 0, info2 = 0;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint (urb->pipe) << 8;
+ info1 |= usb_pipedevice (urb->pipe) << 0;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= (1 << 12); /* EPS "low" */
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (usb_pipecontrol (urb->pipe)) {
+ info1 |= (1 << 27); /* for TT */
+ info1 |= 1 << 14; /* toggle from qtd */
+ }
+ info1 |= usb_maxpacket (urb->dev, urb->pipe,
+ usb_pipeout (urb->pipe)) << 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
+ */
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= (2 << 12); /* EPS "high" */
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ if (usb_pipecontrol (urb->pipe)) {
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= 1 << 14; /* toggle from qtd */
+ } else if (usb_pipebulk (urb->pipe)) {
+ info1 |= 512 << 16; /* usb2 fixed maxpacket */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else
+ info1 |= usb_maxpacket (urb->dev, urb->pipe,
+ usb_pipeout (urb->pipe)) << 16;
+ break;
+ default:
+#ifdef DEBUG
+ BUG ();
+#endif
+ }
+
+ /* NOTE: if (usb_pipeint (urb->pipe)) { 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));
+
+ /* initialize data toggle state */
+ if (!usb_pipecontrol (urb->pipe))
+ clear_toggle (urb->dev,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipeout (urb->pipe),
+ qh);
+
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* move qh (and its qtds) onto async queue; maybe enable queue. */
+
+static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ u32 dma = QH_NEXT (qh->qh_dma);
+ struct ehci_qh *q;
+
+ if (unlikely (!(q = ehci->async))) {
+ 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);
+
+ qh->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD); /* [4.8] */
+ qh->qh_next.qh = qh;
+ qh->hw_next = dma;
+ ehci->async = qh;
+ writel ((u32)qh->qh_dma, &ehci->regs->async_next);
+ cmd |= CMD_ASE | CMD_RUN;
+ writel (cmd, &ehci->regs->command);
+ ehci->hcd.state = USB_STATE_RUNNING;
+ /* posted write need not be known to HC yet ... */
+ } else {
+ /* splice right after "start" of ring */
+ qh->hw_info1 &= ~__constant_cpu_to_le32 (QH_HEAD); /* [4.8] */
+ qh->qh_next = q->qh_next;
+ qh->hw_next = q->hw_next;
+ q->qh_next.qh = qh;
+ q->hw_next = dma;
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+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))
+ 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];
+ if (likely (qh != 0)) {
+ u32 hw_next = QTD_NEXT (qtd->qtd_dma);
+
+ /* 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))) {
+ struct ehci_qtd *last_qtd;
+ int short_rx = 0;
+
+ /* 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);
+ last_qtd->hw_next = hw_next;
+
+ /* previous urb allows short rx? maybe optimize. */
+ if (!(last_qtd->urb->transfer_flags & USB_DISABLE_SPD)
+ && (epnum & 0x10)) {
+ // only the last QTD for now
+ last_qtd->hw_alt_next = hw_next;
+ short_rx = 1;
+ }
+
+ /* Adjust any old copies in qh overlay too.
+ * Interrupt code must cope with case of HC having it
+ * cached, and clobbering these updates.
+ * ... complicates getting rid of extra interrupts!
+ */
+ if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) {
+ wmb ();
+ qh->hw_qtd_next = hw_next;
+ if (short_rx)
+ qh->hw_alt_next = hw_next
+ | (qh->hw_alt_next & 0x1e);
+ vdbg ("queue to qh %p, patch", qh);
+ }
+
+ /* no URB queued */
+ } else {
+ // dbg_qh ("empty qh", ehci, qh);
+
+// FIXME: how handle usb_clear_halt() for an EP with queued URBs?
+// usbcore may not let us handle that cleanly...
+// likely must cancel them all first!
+
+ /* usb_clear_halt() means qh data toggle gets reset */
+ if (usb_pipebulk (urb->pipe)
+ && unlikely (!usb_gettoggle (urb->dev,
+ (epnum & 0x0f),
+ !(epnum & 0x10)))) {
+ clear_toggle (urb->dev,
+ epnum & 0x0f, !(epnum & 0x10), qh);
+ }
+ 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;
+ } else
+ urb->status = -ENOMEM;
+ }
+
+ /* 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_put (qh);
+ if (likely (qh->qh_state == QH_STATE_IDLE))
+ qh_link_async (ehci, qh_put (qh));
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (unlikely (!qh))
+ qh_completions (ehci, qtd_list, 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the async qh for the qtds being reclaimed are now unlinked from the HC */
+/* caller must not own ehci->lock */
+
+static void end_unlink_async (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh = ehci->reclaim;
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = 0;
+ qh_unput (ehci, qh); // refcount from reclaim
+ ehci->reclaim = 0;
+ ehci->reclaim_ready = 0;
+
+ qh_completions (ehci, &qh->qtd_list, 1);
+
+ // 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);
+ else
+ qh_unput (ehci, qh); // refcount from async list
+}
+
+
+/* makes sure the async qh will become idle */
+/* caller must own ehci->lock */
+
+static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int cmd = readl (&ehci->regs->command);
+ struct ehci_qh *prev;
+
+#ifdef DEBUG
+ if (ehci->reclaim
+ || !ehci->async
+ || qh->qh_state != QH_STATE_LINKED
+#ifdef CONFIG_SMP
+// this macro lies except on SMP compiles
+ || !spin_is_locked (&ehci->lock)
+#endif
+ )
+ BUG ();
+#endif
+
+ qh->qh_state = QH_STATE_UNLINK;
+ ehci->reclaim = qh = qh_put (qh);
+
+ // dbg_qh ("start unlink", ehci, qh);
+
+ /* Remove the last QH (qhead)? Stop async schedule first. */
+ if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) {
+ /* 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);
+ }
+ }
+ qh->qh_next.qh = ehci->async = 0;
+
+ ehci->reclaim_ready = 1;
+ tasklet_schedule (&ehci->tasklet);
+ return;
+ }
+
+ if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
+ ehci->reclaim_ready = 1;
+ tasklet_schedule (&ehci->tasklet);
+ return;
+ }
+
+ 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;
+ prev->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD);
+ }
+ prev->hw_next = qh->hw_next;
+ prev->qh_next = qh->qh_next;
+
+ ehci->reclaim_ready = 0;
+ cmd |= CMD_IAAD;
+ writel (cmd, &ehci->regs->command);
+ /* posted write need not be known to HC yet ... */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_async (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+rescan:
+ qh = ehci->async;
+ 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_put (qh);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ /* concurrent unlink could happen here */
+ qh_completions (ehci, &qh->qtd_list, 1);
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ qh_unput (ehci, qh);
+ }
+
+ /* unlink idle entries (reduces PCI usage) */
+ 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.
+ }
+ }
+ 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
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci-sched.c Mon Feb 25 16:54:37 2002
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI scheduled transaction support: interrupt, iso, split iso
+ * These are called "periodic" transactions in the EHCI spec.
+ */
+
+/*
+ * 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);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd/sitd
+ * @tag: hardware tag for type of this record
+ */
+static union ehci_shadow *
+periodic_next_shadow (union ehci_shadow *periodic, int tag)
+{
+ switch (tag) {
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ case Q_TYPE_FSTN:
+ return &periodic->fstn->fstn_next;
+#ifdef have_iso
+ case Q_TYPE_ITD:
+ return &periodic->itd->itd_next;
+ case Q_TYPE_SITD:
+ return &periodic->sitd->sitd_next;
+#endif /* have_iso */
+ }
+ dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
+ // BUG ();
+ return 0;
+}
+
+/* returns true after successful unlink */
+/* caller must hold ehci->lock */
+static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
+{
+ union ehci_shadow *prev_p = &ehci->pshadow [frame];
+ u32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow here = *prev_p;
+ union ehci_shadow *next_p;
+
+ /* find predecessor of "ptr"; hw and shadow lists are in sync */
+ while (here.ptr && here.ptr != ptr) {
+ prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
+ hw_p = &here.qh->hw_next;
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr) {
+ dbg ("entry %p no longer on frame [%d]", ptr, frame);
+ return 0;
+ }
+ // vdbg ("periodic unlink %p from frame %d", ptr, frame);
+
+ /* update hardware list ... HC may still know the old structure, so
+ * don't change hw_next until it'll have purged its cache
+ */
+ next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
+ *hw_p = here.qh->hw_next;
+
+ /* unlink from shadow list; HCD won't see old structure again */
+ *prev_p = *next_p;
+ next_p->ptr = 0;
+
+ return 1;
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short
+periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
+{
+ u32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow *q = &ehci->pshadow [frame];
+ unsigned usecs = 0;
+#ifdef have_iso
+ u32 temp = 0;
+#endif
+
+ while (q->ptr) {
+ switch (Q_NEXT_TYPE (*hw_p)) {
+ case Q_TYPE_QH:
+ /* is it in the S-mask? */
+ if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
+ usecs += q->qh->usecs;
+ q = &q->qh->qh_next;
+ break;
+ case Q_TYPE_FSTN:
+ /* for "save place" FSTNs, count the relevant INTR
+ * bandwidth from the previous frame
+ */
+ if (q->fstn->hw_prev != EHCI_LIST_END) {
+ dbg ("not counting FSTN bandwidth yet ...");
+ }
+ q = &q->fstn->fstn_next;
+ break;
+#ifdef have_iso
+ case Q_TYPE_ITD:
+ temp = le32_to_cpu (q->itd->transaction [uframe]);
+ temp >>= 16;
+ temp &= 0x0fff;
+ if (temp)
+ usecs += HS_USECS_ISO (temp);
+ q = &q->itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ temp = q->sitd->hw_fullspeed_ep &
+ __constant_cpu_to_le32 (1 << 31);
+
+ // FIXME: this doesn't count data bytes right...
+
+ /* is it in the S-mask? (count SPLIT, DATA) */
+ if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
+ if (temp)
+ usecs += HS_USECS (188);
+ else
+ usecs += HS_USECS (1);
+ }
+
+ /* ... C-mask? (count CSPLIT, DATA) */
+ if (q->sitd->hw_uframe &
+ cpu_to_le32 (1 << (8 + uframe))) {
+ if (temp)
+ usecs += HS_USECS (0);
+ else
+ usecs += HS_USECS (188);
+ }
+ q = &q->sitd->sitd_next;
+ break;
+#endif /* have_iso */
+ default:
+ BUG ();
+ }
+ }
+#ifdef DEBUG
+ if (usecs > 100)
+ err ("overallocated uframe %d, periodic is %d usecs",
+ frame * 8 + uframe, usecs);
+#endif
+ return usecs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void intr_deschedule (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ struct ehci_qh *qh,
+ unsigned period
+) {
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ do {
+ periodic_unlink (ehci, frame, qh);
+ qh_unput (ehci, qh);
+ frame += period;
+ } while (frame < ehci->periodic_size);
+
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = 0;
+ ehci->periodic_urbs--;
+
+ /* maybe turn off periodic schedule */
+ if (!ehci->periodic_urbs) {
+ u32 cmd = readl (&ehci->regs->command);
+
+ /* did setting PSE not take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ while (!(readl (&ehci->regs->status) & STS_PSS))
+ udelay (20);
+
+ cmd &= ~CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... */
+
+ ehci->next_frame = -1;
+ } else
+ 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);
+
+ 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);
+}
+
+static int intr_submit (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int mem_flags
+) {
+ unsigned epnum, period;
+ unsigned temp;
+ unsigned short mult, usecs;
+ unsigned long flags;
+ struct ehci_qh *qh;
+ struct hcd_dev *dev;
+ int status = 0;
+
+ /* get endpoint and transfer data */
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein (urb->pipe)) {
+ temp = urb->dev->epmaxpacketin [epnum];
+ epnum |= 0x10;
+ } else
+ temp = urb->dev->epmaxpacketout [epnum];
+ mult = 1;
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ /* high speed "high bandwidth" is coded in ep maxpacket */
+ mult += (temp >> 11) & 0x03;
+ temp &= 0x03ff;
+ } else {
+ dbg ("no intr/tt scheduling yet");
+ status = -ENOSYS;
+ 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.
+ */
+ if (unlikely (qtd_list->next != qtd_list->prev)) {
+ dbg ("only one intr qtd per urb allowed");
+ status = -EINVAL;
+ goto done;
+ }
+
+ usecs = HS_USECS (urb->transfer_buffer_length);
+
+ /*
+ * force a power-of-two (frames) sized polling interval
+ *
+ * NOTE: endpoint->bInterval for highspeed is measured in uframes,
+ * while for full/low speeds it's in frames. Here we "know" that
+ * urb->interval doesn't give acccess to high interrupt rates.
+ */
+ period = ehci->periodic_size;
+ temp = period;
+ if (unlikely (urb->interval < 1))
+ urb->interval = 1;
+ while (temp > urb->interval)
+ temp >>= 1;
+ period = urb->interval = temp;
+
+ 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));
+ }
+ } else {
+ /* can't sleep here, we have ehci->lock... */
+ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
+ qtd_list = &qh->qtd_list;
+ if (likely (qh != 0)) {
+ // dbg ("new INTR qh %p", qh);
+ dev->ep [epnum] = qh;
+ } else
+ status = -ENOMEM;
+ }
+
+ /* Schedule this periodic QH. */
+ if (likely (status == 0)) {
+ unsigned frame = urb->interval;
+
+ qh->hw_next = EHCI_LIST_END;
+ qh->hw_info2 |= cpu_to_le32 (mult << 30);
+ qh->usecs = usecs;
+
+ urb->hcpriv = qh_put (qh);
+ status = -ENOSPC;
+
+ /* pick a set of schedule slots, link the QH into them */
+ do {
+ int uframe;
+
+ /* Select some frame 0..(urb->interval - 1) with a
+ * microframe that can hold this transaction.
+ *
+ * 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++) {
+ int claimed;
+ claimed = periodic_usecs (ehci, frame, uframe);
+ /* 80% periodic == 100 usec max committed */
+ if ((claimed + usecs) <= 100) {
+ vdbg ("frame %d.%d: %d usecs, plus %d",
+ frame, uframe, claimed, usecs);
+ break;
+ }
+ }
+ if (uframe == 8)
+ continue;
+// FIXME delete when code below handles non-empty queues
+ if (ehci->pshadow [frame].ptr)
+ continue;
+
+ /* 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 frame %d.%d",
+ qh, qh->usecs, period, frame, uframe);
+ do {
+ if (unlikely (ehci->pshadow [frame].ptr != 0)) {
+// FIXME -- just link to the end, before any qh with a shorter period,
+// AND handle it already being (implicitly) linked into this frame
+ BUG ();
+ } else {
+ ehci->pshadow [frame].qh = qh_put (qh);
+ ehci->periodic [frame] =
+ QH_NEXT (qh->qh_dma);
+ }
+ frame += period;
+ } while (frame < ehci->periodic_size);
+
+ /* update bandwidth utilization records (for usbfs) */
+ usb_claim_bandwidth (urb->dev, urb, usecs, 0);
+
+ /* maybe enable periodic schedule processing */
+ if (!ehci->periodic_urbs++) {
+ u32 cmd;
+
+ /* did clearing PSE did take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ while (readl (&ehci->regs->status) & STS_PSS)
+ udelay (20);
+
+ cmd = readl (&ehci->regs->command) | CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... PSS happens later */
+ ehci->hcd.state = USB_STATE_RUNNING;
+
+ /* make sure tasklet scans these */
+ ehci->next_frame = ehci_get_frame (&ehci->hcd);
+ }
+ break;
+
+ } while (frame);
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+done:
+ if (status) {
+ usb_complete_t complete = urb->complete;
+
+ urb->complete = 0;
+ urb->status = status;
+ qh_completions (ehci, qtd_list, 1);
+ urb->complete = complete;
+ }
+ return status;
+}
+
+static unsigned long
+intr_complete (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ struct ehci_qh *qh,
+ unsigned long flags /* caller owns ehci->lock ... */
+) {
+ struct ehci_qtd *qtd;
+ struct urb *urb;
+ int unlinking;
+
+ /* nothing to report? */
+ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
+ != 0))
+ return flags;
+
+ 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->qtd_list, 0) == 0)
+ urb = 0;
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* 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;
+
+ /* patch qh and restart */
+ qh_update (qh, qtd);
+ }
+ return flags;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef have_iso
+
+static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+ pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+}
+
+/*
+ * Create itd and allocate into uframes within specified frame.
+ * Caller must update the resulting uframe links.
+ */
+static struct ehci_itd *
+itd_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned index, // urb->iso_frame_desc [index]
+ unsigned frame, // scheduled start
+ dma_addr_t dma, // mapped transfer buffer
+ int mem_flags
+) {
+ struct ehci_itd *itd;
+ u64 temp;
+ u32 buf1;
+ unsigned epnum, maxp, multi, usecs;
+ unsigned length;
+ unsigned i, bufnum;
+
+ /* allocate itd, start to fill it */
+ itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma);
+ if (!itd)
+ return itd;
+
+ itd->hw_next = EHCI_LIST_END;
+ itd->urb = urb;
+ itd->index = index;
+ INIT_LIST_HEAD (&itd->itd_list);
+ itd->uframe = (frame * 8) % ehci->periodic_size;
+
+ /* tell itd about the buffer its transfers will consume */
+ length = urb->iso_frame_desc [index].length;
+ dma += urb->iso_frame_desc [index].offset;
+ temp = dma & ~0x0fff;
+ for (i = 0; i < 7; i++) {
+ itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
+ itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
+ temp += 0x0fff;
+ }
+
+ /*
+ * this might be a "high bandwidth" highspeed endpoint,
+ * as encoded in the ep descriptor's maxpacket field
+ */
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein (urb->pipe)) {
+ maxp = urb->dev->epmaxpacketin [epnum];
+ buf1 = (1 << 11) | maxp;
+ } else {
+ maxp = urb->dev->epmaxpacketout [epnum];
+ buf1 = maxp;
+ }
+ multi = 1;
+ multi += (temp >> 11) & 0x03;
+ maxp &= 0x03ff;
+
+ /* "plus" info in low order bits of buffer pointers */
+ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
+ itd->hw_bufp [1] |= cpu_to_le32 (buf1);
+ itd->hw_bufp [2] |= cpu_to_le32 (multi);
+
+ /* schedule as many uframes as needed */
+ maxp *= multi;
+ usecs = HS_USECS_ISO (maxp);
+ bufnum = 0;
+ for (i = 0; i < 8; i++) {
+ unsigned t, offset, scratch;
+
+ if (length <= 0) {
+ itd->hw_transaction [i] = 0;
+ continue;
+ }
+
+ /* don't commit more than 80% periodic == 100 usec */
+ if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100)
+ continue;
+
+ /* we'll use this uframe; figure hw_transaction */
+ t = EHCI_ISOC_ACTIVE;
+ t |= bufnum << 12; // which buffer?
+ offset = temp & 0x0fff; // offset therein
+ t |= offset;
+ if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page"
+ bufnum++;
+ if (length <= maxp) {
+ // interrupt only needed at end-of-urb
+ if ((index + 1) == urb->number_of_packets)
+ t |= EHCI_ITD_IOC;
+ scratch = length;
+ } else
+ scratch = maxp;
+ t |= scratch << 16;
+ t = cpu_to_le32 (t);
+
+ itd->hw_transaction [i] = itd->transaction [i] = t;
+ length -= scratch;
+ }
+ if (length > 0) {
+ dbg ("iso frame too big, urb %p [%d], %d extra (of %d)",
+ urb, index, length, urb->iso_frame_desc [index].length);
+ itd_free (ehci, itd);
+ itd = 0;
+ }
+ return itd;
+}
+
+static inline void
+itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
+{
+ u32 ptr;
+
+ ptr = cpu_to_le32 (itd->itd_dma); // type 0 == itd
+ if (ehci->pshadow [frame].ptr) {
+ if (!itd->itd_next.ptr) {
+ itd->itd_next = ehci->pshadow [frame];
+ itd->hw_next = ehci->periodic [frame];
+ } else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) {
+ dbg ("frame %d itd link goof", frame);
+ BUG ();
+ }
+ }
+ ehci->pshadow [frame].itd = itd;
+ ehci->periodic [frame] = ptr;
+}
+
+#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+static unsigned long
+itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags)
+{
+ struct urb *urb = itd->urb;
+
+ /* if not unlinking: */
+ if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
+ && ehci->hcd.state != USB_STATE_HALT) {
+ int i;
+ iso_packet_descriptor_t *desc;
+ struct ehci_itd *first_itd = urb->hcpriv;
+
+ /* update status for this frame's transfers */
+ desc = &urb->iso_frame_desc [itd->index];
+ desc->status = 0;
+ desc->actual_length = 0;
+ for (i = 0; i < 8; i++) {
+ u32 t = itd->hw_transaction [i];
+ if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) {
+ if (t & EHCI_ISOC_ACTIVE)
+ desc->status = -EXDEV;
+ else if (t & EHCI_ISOC_BUF_ERR)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* couldn't read */
+ : -ECOMM; /* couldn't write */
+ else if (t & EHCI_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & EHCI_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+ break;
+ }
+ desc->actual_length += EHCI_ITD_LENGTH (t);
+ }
+
+ /* handle completion now? */
+ if ((itd->index + 1) != urb->number_of_packets)
+ return flags;
+
+ i = usb_pipein (urb->pipe);
+ if (i)
+ pci_dma_sync_single (ehci->hcd.pdev,
+ first_itd->buf_dma,
+ urb->transfer_buffer_length,
+ PCI_DMA_FROMDEVICE);
+
+ /* call completion with no locks; it can unlink ... */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ urb->complete (urb);
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* re-activate this URB? or unlink? */
+ if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
+ && ehci->hcd.state != USB_STATE_HALT) {
+ if (!i)
+ pci_dma_sync_single (ehci->hcd.pdev,
+ first_itd->buf_dma,
+ urb->transfer_buffer_length,
+ PCI_DMA_TODEVICE);
+
+ itd = urb->hcpriv;
+ do {
+ for (i = 0; i < 8; i++)
+ itd->hw_transaction [i]
+ = itd->transaction [i];
+ itd = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ } while (itd != urb->hcpriv);
+ return flags;
+ }
+
+ /* unlink done only on the last itd */
+ } else if ((itd->index + 1) != urb->number_of_packets)
+ return flags;
+
+ /* we're unlinking ... */
+
+ /* decouple urb from the hcd */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (ehci->hcd.state == USB_STATE_HALT)
+ urb->status = -ESHUTDOWN;
+ itd = urb->hcpriv;
+ urb->hcpriv = 0;
+ ehci_urb_done (ehci, itd->buf_dma, urb);
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* take itds out of the hc's periodic schedule */
+ list_entry (itd->itd_list.prev, struct ehci_itd, itd_list)
+ ->itd_list.next = 0;
+ do {
+ struct ehci_itd *next;
+
+ if (itd->itd_list.next)
+ next = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ else
+ next = 0;
+
+ // FIXME: hc WILL (!) lap us here, if we get behind
+ // by 128 msec (or less, with smaller periodic_size).
+ // Reading/caching these itds will cause trouble...
+
+ periodic_unlink (ehci, itd->uframe, itd);
+ itd_free (ehci, itd);
+ itd = next;
+ } while (itd);
+ return flags;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb)
+{
+ struct ehci_itd *first_itd = 0, *itd;
+ unsigned frame_index;
+ dma_addr_t dma;
+ unsigned long flags;
+
+ dbg ("itd_submit");
+
+ /* set up one dma mapping for this urb */
+ 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 (dma == 0)
+ return -ENOMEM;
+
+ /*
+ * Schedule as needed. This is VERY optimistic about free
+ * bandwidth! But the API assumes drivers can pick frames
+ * intelligently (how?), so there's no other good option.
+ *
+ * FIXME this doesn't handle urb->next rings, or try to
+ * use the iso periodicity.
+ */
+ if (urb->transfer_flags & USB_ISO_ASAP) {
+ urb->start_frame = ehci_get_frame (&ehci->hcd);
+ urb->start_frame++;
+ }
+ urb->start_frame %= ehci->periodic_size;
+
+ /* create and populate itds (doing uframe scheduling) */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (frame_index = 0;
+ frame_index < urb->number_of_packets;
+ frame_index++) {
+ itd = itd_make (ehci, urb, frame_index,
+ urb->start_frame + frame_index,
+ dma, SLAB_ATOMIC);
+ if (itd) {
+ if (first_itd)
+ list_add_tail (&itd->itd_list,
+ &first_itd->itd_list);
+ else
+ first_itd = itd;
+ } else {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (first_itd) {
+ while (!list_empty (&first_itd->itd_list)) {
+ itd = list_entry (
+ first_itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ list_del (&itd->itd_list);
+ itd_free (ehci, itd);
+ }
+ itd_free (ehci, first_itd);
+ }
+ pci_unmap_single (ehci->hcd.pdev,
+ dma, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ return -ENOMEM;
+ }
+ }
+
+ /* stuff into the schedule */
+ itd = first_itd;
+ do {
+ unsigned i;
+
+ for (i = 0; i < 8; i++) {
+ if (!itd->hw_transaction [i])
+ continue;
+ itd_link (ehci, itd->uframe + i, itd);
+ }
+ itd = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ } while (itd != first_itd);
+ urb->hcpriv = first_itd;
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Split ISO TDs" ... used for USB 1.1 devices going through
+ * the TTs in USB 2.0 hubs.
+ */
+
+static inline 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 inline 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)
+{
+ // struct ehci_sitd *first_sitd = 0;
+ unsigned frame_index;
+ dma_addr_t dma;
+ int mem_flags;
+
+ dbg ("NYI -- sitd_submit");
+
+ // FIXME -- implement!
+
+ // FIXME: setup one big dma mapping
+ dma = 0;
+
+ mem_flags = SLAB_ATOMIC;
+
+ 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_iso */
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_periodic (struct ehci_hcd *ehci)
+{
+ unsigned frame;
+ unsigned clock;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * Touches as few pages as possible: cache-friendly.
+ * It's safe to scan entries more than once, though.
+ */
+ if (HCD_IS_RUNNING (ehci->hcd.state)) {
+ frame = ehci->next_frame;
+ clock = ehci_get_frame (&ehci->hcd);
+
+ /* when shutting down, scan everything for thoroughness */
+ } else {
+ frame = 0;
+ clock = ehci->periodic_size - 1;
+ }
+ for (;;) {
+ union ehci_shadow q;
+ u32 type;
+
+restart:
+ q.ptr = ehci->pshadow [frame].ptr;
+ type = Q_NEXT_TYPE (ehci->periodic [frame]);
+
+ /* scan each element in frame's queue for completions */
+ while (q.ptr != 0) {
+ int last;
+ union ehci_shadow temp;
+
+ switch (type) {
+ case Q_TYPE_QH:
+ last = (q.qh->hw_next == EHCI_LIST_END);
+ flags = intr_complete (ehci, frame,
+ qh_put (q.qh), flags);
+ type = Q_NEXT_TYPE (q.qh->hw_next);
+ temp = q.qh->qh_next;
+ qh_unput (ehci, q.qh);
+ q = temp;
+ break;
+ case Q_TYPE_FSTN:
+ last = (q.fstn->hw_next == EHCI_LIST_END);
+ /* for "save place" FSTNs, look at QH entries
+ * in the previous frame for completions.
+ */
+ if (q.fstn->hw_prev != EHCI_LIST_END) {
+ dbg ("ignoring completions from FSTNs");
+ }
+ type = Q_NEXT_TYPE (q.fstn->hw_next);
+ temp = q.fstn->fstn_next;
+ break;
+#ifdef have_iso
+ case Q_TYPE_ITD:
+ last = (q.itd->hw_next == EHCI_LIST_END);
+ flags = itd_complete (ehci, q.itd, flags);
+ type = Q_NEXT_TYPE (q.itd->hw_next);
+ q = q.itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ last = (q.sitd->hw_next == EHCI_LIST_END);
+ flags = sitd_complete (ehci, q.sitd, flags);
+ type = Q_NEXT_TYPE (q.sitd->hw_next);
+ q = q.sitd->sitd_next;
+ break;
+#endif /* have_iso */
+ default:
+ dbg ("corrupt type %d frame %d shadow %p",
+ type, frame, q.ptr);
+ // BUG ();
+ last = 1;
+ q.ptr = 0;
+ }
+
+ /* did completion remove an interior q entry? */
+ if (unlikely (q.ptr == 0 && !last))
+ goto restart;
+ }
+
+ /* stop when we catch up to the HC */
+
+ // FIXME: this assumes we won't get lapped when
+ // latencies climb; that should be rare, but...
+ // detect it, and just go all the way around.
+ // FLR might help detect this case, so long as latencies
+ // don't exceed periodic_size msec (default 1.024 sec).
+
+ // FIXME: likewise assumes HC doesn't halt mid-scan
+
+ if (frame == clock) {
+ unsigned now;
+
+ if (!HCD_IS_RUNNING (ehci->hcd.state))
+ break;
+ ehci->next_frame = clock;
+ now = ehci_get_frame (&ehci->hcd);
+ if (clock == now)
+ break;
+ clock = now;
+ } else if (++frame >= ehci->periodic_size)
+ frame = 0;
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ }
diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/hcd/ehci.h Mon Feb 25 16:54:37 2002
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2001 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_EHCI_HCD_H
+#define __LINUX_EHCI_HCD_H
+
+/* definitions used for the EHCI driver */
+
+/* ehci_hcd->lock guards shared data against other CPUs:
+ * ehci_hcd: async, reclaim, periodic (and shadow), ...
+ * hcd_dev: ep[]
+ * ehci_qh: qh_next, qtd_list
+ * ehci_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+
+struct ehci_hcd { /* one per controller */
+ spinlock_t lock;
+
+ /* async schedule support */
+ struct ehci_qh *async;
+ struct ehci_qh *reclaim;
+ int reclaim_ready;
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
+ unsigned periodic_size;
+ u32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ unsigned i_thresh; /* uframes HC might cache */
+
+ union ehci_shadow *pshadow; /* mirror hw periodic table */
+ int next_frame; /* scan periodic, start here */
+ unsigned periodic_urbs; /* how many urbs scheduled? */
+
+ /* deferred work from IRQ, etc */
+ struct tasklet_struct tasklet;
+
+ /* per root hub port */
+ unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+
+ /* glue to PCI and HCD framework */
+ struct usb_hcd hcd;
+ struct ehci_caps *caps;
+ struct ehci_regs *regs;
+
+ /* per-HC memory pools (could be per-PCI-bus, but ...) */
+ struct pci_pool *qh_pool; /* qh per active urb */
+ 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 */
+};
+
+/* unwrap an HCD pointer to get an EHCI_HCD pointer */
+#define hcd_to_ehci(hcd_ptr) list_entry(hcd_ptr, struct ehci_hcd, hcd)
+
+/* NOTE: urb->transfer_flags expected to not use this bit !!! */
+#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+ u8 length; /* CAPLENGTH - size of this struct */
+ u8 reserved; /* offset 0x1 */
+ u16 hci_version; /* HCIVERSION - offset 0x2 */
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
+#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
+#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
+#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
+ u8 portroute [8]; /* nibbles for routing - offset 0xC */
+} __attribute__ ((packed));
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct ehci_regs {
+
+ /* USBCMD: offset 0x00 */
+ u32 command;
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCICLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved [9];
+
+ /* CONFIGFLAG: offset 0x40 */
+ u32 configured_flag;
+#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
+
+ /* PORTSC: offset 0x44 */
+ u32 port_status [0]; /* up to N_PORTS */
+/* 31:23 reserved */
+#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
+#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
+#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
+/* 19:16 for port testing */
+/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
+#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
+#define PORT_POWER (1<<12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_OCC (1<<5) /* over current change */
+#define PORT_OC (1<<4) /* over current active */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct ehci_qtd {
+ /* first part defined by EHCI spec */
+ u32 hw_next; /* see EHCI 3.5.1 */
+ u32 hw_alt_next; /* see EHCI 3.5.2 */
+ u32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+ u32 hw_buf [5]; /* see EHCI 3.5.4 */
+ u32 hw_buf_hi [5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+
+ /* 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)));
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,sitd,fstn}->hw_next */
+#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
+
+/* values for that type tag */
+#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
+#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
+#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union ehci_shadow {
+ struct ehci_qh *qh; /* Q_TYPE_QH */
+ struct ehci_itd *itd; /* Q_TYPE_ITD */
+ struct ehci_sitd *sitd; /* Q_TYPE_SITD */
+ struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
+ void *ptr;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+struct ehci_qh {
+ /* first part defined by EHCI spec */
+ u32 hw_next; /* see EHCI 3.6.1 */
+ u32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_HEAD 0x00008000
+ u32 hw_info2; /* see EHCI 3.6.2 */
+ u32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ u32 hw_qtd_next;
+ u32 hw_alt_next;
+ u32 hw_token;
+ u32 hw_buf [5];
+ u32 hw_buf_hi [5];
+
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union ehci_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+
+ atomic_t refcount;
+ unsigned short usecs; /* intr bandwidth */
+ short 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
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/