Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
new file mode 100644
index 0000000..3196c32
--- /dev/null
+++ b/drivers/usb/host/Kconfig
@@ -0,0 +1,126 @@
+#
+# USB Host Controller Drivers
+#
+comment "USB Host Controller Drivers"
+ depends on USB
+
+config USB_EHCI_HCD
+ tristate "EHCI HCD (USB 2.0) support"
+ depends on USB && PCI
+ ---help---
+ 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 or UHCI (for Via motherboards) Host Controller
+ Driver too.
+
+ You may want to read <file:Documentation/usb/ehci.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ehci-hcd.
+
+config USB_EHCI_SPLIT_ISO
+ bool "Full speed ISO transactions (EXPERIMENTAL)"
+ depends on USB_EHCI_HCD && EXPERIMENTAL
+ default n
+ ---help---
+ This code is new and hasn't been used with many different
+ EHCI or USB 2.0 transaction translator implementations.
+ It should work for ISO-OUT transfers, like audio.
+
+config USB_EHCI_ROOT_HUB_TT
+ bool "Root Hub Transaction Translators (EXPERIMENTAL)"
+ depends on USB_EHCI_HCD && EXPERIMENTAL
+ ---help---
+ Some EHCI chips have vendor-specific extensions to integrate
+ transaction translators, so that no OHCI or UHCI companion
+ controller is needed. It's safe to say "y" even if your
+ controller doesn't support this feature.
+
+ This supports the EHCI implementation from TransDimension Inc.
+
+config USB_OHCI_HCD
+ tristate "OHCI HCD support"
+ depends on USB && USB_ARCH_HAS_OHCI
+ select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+ ---help---
+ The Open Host Controller Interface (OHCI) is a standard for accessing
+ USB 1.1 host controller hardware. It does more in hardware than Intel's
+ UHCI specification. If your USB host controller follows the OHCI spec,
+ say Y. On most non-x86 systems, and on x86 hardware that's not using a
+ USB controller from Intel or VIA, this is appropriate. If your host
+ controller doesn't use PCI, this is probably appropriate. For a PCI
+ based system where you're not sure, the "lspci -v" entry will list the
+ right "prog-if" for your USB controller(s): EHCI, OHCI, or UHCI.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ohci-hcd.
+
+config USB_OHCI_HCD_PPC_SOC
+ bool "OHCI support for on-chip PPC USB controller"
+ depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
+ default y
+ select USB_OHCI_BIG_ENDIAN
+ ---help---
+ Enables support for the USB controller on the MPC52xx or
+ STB03xxx processor chip. If unsure, say Y.
+
+config USB_OHCI_HCD_PCI
+ bool "OHCI support for PCI-bus USB controllers"
+ depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx)
+ default y
+ select USB_OHCI_LITTLE_ENDIAN
+ ---help---
+ Enables support for PCI-bus plug-in USB controller cards.
+ If unsure, say Y.
+
+config USB_OHCI_BIG_ENDIAN
+ bool
+ depends on USB_OHCI_HCD
+ default n
+
+config USB_OHCI_LITTLE_ENDIAN
+ bool
+ depends on USB_OHCI_HCD
+ default n if STB03xxx || PPC_MPC52xx
+ default y
+
+config USB_UHCI_HCD
+ tristate "UHCI HCD (most Intel and VIA) support"
+ depends on USB && PCI
+ ---help---
+ The Universal Host Controller Interface is a standard by Intel for
+ accessing the USB hardware in the PC (which is also called the USB
+ host controller). If your USB host controller conforms to this
+ standard, you may want to say Y, but see below. All recent boards
+ with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
+ i810, i820) conform to this standard. Also all VIA PCI chipsets
+ (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
+ 133). If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called uhci-hcd.
+
+config USB_SL811_HCD
+ tristate "SL811HS HCD support"
+ depends on USB
+ default N
+ help
+ The SL811HS is a single-port USB controller that supports either
+ host side or peripheral side roles. Enable this option if your
+ board has this chip, and you want to use it as a host controller.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sl811-hcd.
+
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
new file mode 100644
index 0000000..a574ca0
--- /dev/null
+++ b/drivers/usb/host/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for USB Host Controller Driver
+# framework and drivers
+#
+
+obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
+obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
+obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
+obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
+obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
new file mode 100644
index 0000000..495e2a3
--- /dev/null
+++ b/drivers/usb/host/ehci-dbg.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * 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 */
+
+#define ehci_dbg(ehci, fmt, args...) \
+ dev_dbg (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
+#define ehci_err(ehci, fmt, args...) \
+ dev_err (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
+#define ehci_info(ehci, fmt, args...) \
+ dev_info (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
+#define ehci_warn(ehci, fmt, args...) \
+ dev_warn (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
+
+#ifdef EHCI_VERBOSE_DEBUG
+# define vdbg dbg
+# define ehci_vdbg ehci_dbg
+#else
+# define vdbg(fmt,args...) do { } while (0)
+# define ehci_vdbg(ehci, fmt, args...) do { } while (0)
+#endif
+
+#ifdef DEBUG
+
+/* check the values in the HCSPARAMS register
+ * (host controller _Structural_ parameters)
+ * see EHCI spec, Table 2-4 for each value
+ */
+static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
+{
+ u32 params = readl (&ehci->caps->hcs_params);
+
+ ehci_dbg (ehci,
+ "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
+ label, params,
+ HCS_DEBUG_PORT (params),
+ HCS_INDICATOR (params) ? " ind" : "",
+ 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++) {
+ // FIXME MIPS won't readb() ...
+ byte = readb (&ehci->caps->portroute[(i>>1)]);
+ sprintf(tmp, "%d ",
+ ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
+ strcat(buf, tmp);
+ }
+ ehci_dbg (ehci, "%s portroute %s\n",
+ 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 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_ISOC_CACHE (params)) {
+ ehci_dbg (ehci,
+ "%s hcc_params %04x caching frame %s%s%s\n",
+ label, params,
+ HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
+ HCC_CANPARK (params) ? " park" : "",
+ HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+ } else {
+ ehci_dbg (ehci,
+ "%s hcc_params %04x thresh %d uframes %s%s%s\n",
+ 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
+
+static void __attribute__((__unused__))
+dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ le32_to_cpup (&qtd->hw_next),
+ le32_to_cpup (&qtd->hw_alt_next),
+ le32_to_cpup (&qtd->hw_token),
+ le32_to_cpup (&qtd->hw_buf [0]));
+ if (qtd->hw_buf [1])
+ ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ le32_to_cpup (&qtd->hw_buf [1]),
+ le32_to_cpup (&qtd->hw_buf [2]),
+ le32_to_cpup (&qtd->hw_buf [3]),
+ le32_to_cpup (&qtd->hw_buf [4]));
+}
+
+static void __attribute__((__unused__))
+dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
+ qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
+ qh->hw_current);
+ dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
+}
+
+static void __attribute__((__unused__))
+dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+ ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
+ label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
+ ehci_dbg (ehci,
+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ le32_to_cpu(itd->hw_transaction[0]),
+ le32_to_cpu(itd->hw_transaction[1]),
+ le32_to_cpu(itd->hw_transaction[2]),
+ le32_to_cpu(itd->hw_transaction[3]),
+ le32_to_cpu(itd->hw_transaction[4]),
+ le32_to_cpu(itd->hw_transaction[5]),
+ le32_to_cpu(itd->hw_transaction[6]),
+ le32_to_cpu(itd->hw_transaction[7]));
+ ehci_dbg (ehci,
+ " buf: %08x %08x %08x %08x %08x %08x %08x\n",
+ le32_to_cpu(itd->hw_bufp[0]),
+ le32_to_cpu(itd->hw_bufp[1]),
+ le32_to_cpu(itd->hw_bufp[2]),
+ le32_to_cpu(itd->hw_bufp[3]),
+ le32_to_cpu(itd->hw_bufp[4]),
+ le32_to_cpu(itd->hw_bufp[5]),
+ le32_to_cpu(itd->hw_bufp[6]));
+ ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
+ itd->index[0], itd->index[1], itd->index[2],
+ itd->index[3], itd->index[4], itd->index[5],
+ itd->index[6], itd->index[7]);
+}
+
+static void __attribute__((__unused__))
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+{
+ ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
+ label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
+ ehci_dbg (ehci,
+ " addr %08x sched %04x result %08x buf %08x %08x\n",
+ le32_to_cpu(sitd->hw_fullspeed_ep),
+ le32_to_cpu(sitd->hw_uframe),
+ le32_to_cpu(sitd->hw_results),
+ le32_to_cpu(sitd->hw_buf [0]),
+ le32_to_cpu(sitd->hw_buf [1]));
+}
+
+static int __attribute__((__unused__))
+dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
+{
+ return scnprintf (buf, len,
+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", status,
+ (status & STS_ASS) ? " Async" : "",
+ (status & STS_PSS) ? " Periodic" : "",
+ (status & STS_RECL) ? " Recl" : "",
+ (status & STS_HALT) ? " Halt" : "",
+ (status & STS_IAA) ? " IAA" : "",
+ (status & STS_FATAL) ? " FATAL" : "",
+ (status & STS_FLR) ? " FLR" : "",
+ (status & STS_PCD) ? " PCD" : "",
+ (status & STS_ERR) ? " ERR" : "",
+ (status & STS_INT) ? " INT" : ""
+ );
+}
+
+static int __attribute__((__unused__))
+dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
+{
+ return scnprintf (buf, len,
+ "%s%sintrenable %02x%s%s%s%s%s%s",
+ label, label [0] ? " " : "", enable,
+ (enable & STS_IAA) ? " IAA" : "",
+ (enable & STS_FATAL) ? " FATAL" : "",
+ (enable & STS_FLR) ? " FLR" : "",
+ (enable & STS_PCD) ? " PCD" : "",
+ (enable & STS_ERR) ? " ERR" : "",
+ (enable & STS_INT) ? " INT" : ""
+ );
+}
+
+static const char *const fls_strings [] =
+ { "1024", "512", "256", "??" };
+
+static int
+dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
+{
+ return scnprintf (buf, len,
+ "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
+ label, label [0] ? " " : "", command,
+ (command & CMD_PARK) ? "park" : "(park)",
+ CMD_PARK_CNT (command),
+ (command >> 16) & 0x3f,
+ (command & CMD_LRESET) ? " LReset" : "",
+ (command & CMD_IAAD) ? " IAAD" : "",
+ (command & CMD_ASE) ? " Async" : "",
+ (command & CMD_PSE) ? " Periodic" : "",
+ fls_strings [(command >> 2) & 0x3],
+ (command & CMD_RESET) ? " Reset" : "",
+ (command & CMD_RUN) ? "RUN" : "HALT"
+ );
+}
+
+static int
+dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
+{
+ char *sig;
+
+ /* signaling state */
+ switch (status & (3 << 10)) {
+ case 0 << 10: sig = "se0"; break;
+ case 1 << 10: sig = "k"; break; /* low speed */
+ case 2 << 10: sig = "j"; break;
+ default: sig = "?"; break;
+ }
+
+ return scnprintf (buf, len,
+ "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s",
+ label, label [0] ? " " : "", port, status,
+ (status & PORT_POWER) ? " POWER" : "",
+ (status & PORT_OWNER) ? " OWNER" : "",
+ sig,
+ (status & PORT_RESET) ? " RESET" : "",
+ (status & PORT_SUSPEND) ? " SUSPEND" : "",
+ (status & PORT_RESUME) ? " RESUME" : "",
+ (status & PORT_OCC) ? " OCC" : "",
+ (status & PORT_OC) ? " OC" : "",
+ (status & PORT_PEC) ? " PEC" : "",
+ (status & PORT_PE) ? " PE" : "",
+ (status & PORT_CSC) ? " CSC" : "",
+ (status & PORT_CONNECT) ? " CONNECT" : ""
+ );
+}
+
+#else
+static inline void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{}
+
+static inline int __attribute__((__unused__))
+dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
+{ return 0; }
+
+#endif /* DEBUG */
+
+/* functions have the "wrong" filename when they're output... */
+#define dbg_status(ehci, label, status) { \
+ char _buf [80]; \
+ dbg_status_buf (_buf, sizeof _buf, label, status); \
+ ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+#define dbg_cmd(ehci, label, command) { \
+ char _buf [80]; \
+ dbg_command_buf (_buf, sizeof _buf, label, command); \
+ ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+#define dbg_port(ehci, label, port, status) { \
+ char _buf [80]; \
+ dbg_port_buf (_buf, sizeof _buf, label, port, status); \
+ ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILES
+
+static inline void create_debug_files (struct ehci_hcd *bus) { }
+static inline void remove_debug_files (struct ehci_hcd *bus) { }
+
+#else
+
+/* troubleshooting help: expose state in driverfs */
+
+#define speed_char(info1) ({ char tmp; \
+ switch (info1 & (3 << 12)) { \
+ case 0 << 12: tmp = 'f'; break; \
+ case 1 << 12: tmp = 'l'; break; \
+ case 2 << 12: tmp = 'h'; break; \
+ default: tmp = '?'; break; \
+ }; tmp; })
+
+static inline char token_mark (__le32 token)
+{
+ __u32 v = le32_to_cpu (token);
+ if (v & QTD_STS_ACTIVE)
+ return '*';
+ if (v & QTD_STS_HALT)
+ return '-';
+ if (!IS_SHORT_READ (v))
+ return ' ';
+ /* tries to advance through hw_alt_next */
+ return '/';
+}
+
+static void qh_lines (
+ struct ehci_hcd *ehci,
+ struct ehci_qh *qh,
+ char **nextp,
+ unsigned *sizep
+)
+{
+ u32 scratch;
+ u32 hw_curr;
+ struct list_head *entry;
+ struct ehci_qtd *td;
+ unsigned temp;
+ unsigned size = *sizep;
+ char *next = *nextp;
+ char mark;
+
+ if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */
+ mark = '@';
+ else
+ mark = token_mark (qh->hw_token);
+ if (mark == '/') { /* qh_alt_next controls qh advance? */
+ if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
+ mark = '#'; /* blocked */
+ else if (qh->hw_alt_next == EHCI_LIST_END)
+ mark = '.'; /* use hw_qtd_next */
+ /* else alt_next points to some other qtd */
+ }
+ scratch = le32_to_cpup (&qh->hw_info1);
+ hw_curr = (mark == '*') ? le32_to_cpup (&qh->hw_current) : 0;
+ temp = scnprintf (next, size,
+ "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
+ qh, scratch & 0x007f,
+ speed_char (scratch),
+ (scratch >> 8) & 0x000f,
+ scratch, le32_to_cpup (&qh->hw_info2),
+ le32_to_cpup (&qh->hw_token), mark,
+ (__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
+ ? "data1" : "data0",
+ (le32_to_cpup (&qh->hw_alt_next) >> 1) & 0x0f);
+ size -= temp;
+ next += temp;
+
+ /* hc may be modifying the list as we read it ... */
+ list_for_each (entry, &qh->qtd_list) {
+ td = list_entry (entry, struct ehci_qtd, qtd_list);
+ scratch = le32_to_cpup (&td->hw_token);
+ mark = ' ';
+ if (hw_curr == td->qtd_dma)
+ mark = '*';
+ else if (qh->hw_qtd_next == td->qtd_dma)
+ mark = '+';
+ else if (QTD_LENGTH (scratch)) {
+ if (td->hw_alt_next == ehci->async->hw_alt_next)
+ mark = '#';
+ else if (td->hw_alt_next != EHCI_LIST_END)
+ mark = '/';
+ }
+ temp = snprintf (next, size,
+ "\n\t%p%c%s len=%d %08x urb %p",
+ td, mark, ({ char *tmp;
+ switch ((scratch>>8)&0x03) {
+ case 0: tmp = "out"; break;
+ case 1: tmp = "in"; break;
+ case 2: tmp = "setup"; break;
+ default: tmp = "?"; break;
+ } tmp;}),
+ (scratch >> 16) & 0x7fff,
+ scratch,
+ td->urb);
+ if (temp < 0)
+ temp = 0;
+ else if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+ if (temp == size)
+ goto done;
+ }
+
+ temp = snprintf (next, size, "\n");
+ if (temp < 0)
+ temp = 0;
+ else if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+
+done:
+ *sizep = size;
+ *nextp = next;
+}
+
+static ssize_t
+show_async (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ struct ehci_qh *qh;
+
+ *buf = 0;
+
+ bus = to_usb_bus(class_dev);
+ hcd = bus->hcpriv;
+ ehci = hcd_to_ehci (hcd);
+ next = buf;
+ size = PAGE_SIZE;
+
+ /* dumps a snapshot of the async schedule.
+ * usually empty except for long-term bulk reads, or head.
+ * one QH per line, and TDs we know about
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
+ qh_lines (ehci, qh, &next, &size);
+ if (ehci->reclaim && size > 0) {
+ temp = scnprintf (next, size, "\nreclaim =\n");
+ size -= temp;
+ next += temp;
+
+ for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
+ qh_lines (ehci, qh, &next, &size);
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ return strlen (buf);
+}
+static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
+
+#define DBG_SCHED_LIMIT 64
+
+static ssize_t
+show_periodic (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ union ehci_shadow p, *seen;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+ __le32 tag;
+
+ if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
+ return 0;
+ seen_count = 0;
+
+ bus = to_usb_bus(class_dev);
+ hcd = bus->hcpriv;
+ ehci = hcd_to_ehci (hcd);
+ next = buf;
+ size = PAGE_SIZE;
+
+ temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule.
+ * iso changes, interrupt usually doesn't.
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < ehci->periodic_size; i++) {
+ p = ehci->pshadow [i];
+ if (likely (!p.ptr))
+ continue;
+ tag = Q_NEXT_TYPE (ehci->periodic [i]);
+
+ temp = scnprintf (next, size, "%4d: ", i);
+ size -= temp;
+ next += temp;
+
+ do {
+ switch (tag) {
+ case Q_TYPE_QH:
+ temp = scnprintf (next, size, " qh%d-%04x/%p",
+ p.qh->period,
+ le32_to_cpup (&p.qh->hw_info2)
+ /* uframe masks */
+ & 0xffff,
+ p.qh);
+ size -= temp;
+ next += temp;
+ /* don't repeat what follows this qh */
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen [temp].ptr != p.ptr)
+ continue;
+ if (p.qh->qh_next.ptr)
+ temp = scnprintf (next, size,
+ " ...");
+ p.ptr = NULL;
+ break;
+ }
+ /* show more info the first time around */
+ if (temp == seen_count && p.ptr) {
+ u32 scratch = le32_to_cpup (
+ &p.qh->hw_info1);
+ struct ehci_qtd *qtd;
+ char *type = "";
+
+ /* count tds, get ep direction */
+ temp = 0;
+ list_for_each_entry (qtd,
+ &p.qh->qtd_list,
+ qtd_list) {
+ temp++;
+ switch (0x03 & (le32_to_cpu (
+ qtd->hw_token) >> 8)) {
+ case 0: type = "out"; continue;
+ case 1: type = "in"; continue;
+ }
+ }
+
+ temp = scnprintf (next, size,
+ " (%c%d ep%d%s "
+ "[%d/%d] q%d p%d)",
+ speed_char (scratch),
+ scratch & 0x007f,
+ (scratch >> 8) & 0x000f, type,
+ p.qh->usecs, p.qh->c_usecs,
+ temp,
+ 0x7ff & (scratch >> 16));
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen [seen_count++].qh = p.qh;
+ } else
+ temp = 0;
+ if (p.qh) {
+ tag = Q_NEXT_TYPE (p.qh->hw_next);
+ p = p.qh->qh_next;
+ }
+ break;
+ case Q_TYPE_FSTN:
+ temp = scnprintf (next, size,
+ " fstn-%8x/%p", p.fstn->hw_prev,
+ p.fstn);
+ tag = Q_NEXT_TYPE (p.fstn->hw_next);
+ p = p.fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ temp = scnprintf (next, size,
+ " itd/%p", p.itd);
+ tag = Q_NEXT_TYPE (p.itd->hw_next);
+ p = p.itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ temp = scnprintf (next, size,
+ " sitd%d-%04x/%p",
+ p.sitd->stream->interval,
+ le32_to_cpup (&p.sitd->hw_uframe)
+ & 0x0000ffff,
+ p.sitd);
+ tag = Q_NEXT_TYPE (p.sitd->hw_next);
+ p = p.sitd->sitd_next;
+ break;
+ }
+ size -= temp;
+ next += temp;
+ } while (p.ptr);
+
+ temp = scnprintf (next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ kfree (seen);
+
+ return PAGE_SIZE - size;
+}
+static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
+
+#undef DBG_SCHED_LIMIT
+
+static ssize_t
+show_registers (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ unsigned temp, size, i;
+ char *next, scratch [80];
+ static char fmt [] = "%*s\n";
+ static char label [] = "";
+
+ bus = to_usb_bus(class_dev);
+ hcd = bus->hcpriv;
+ ehci = hcd_to_ehci (hcd);
+ next = buf;
+ size = PAGE_SIZE;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ if (bus->controller->power.power_state) {
+ size = scnprintf (next, size,
+ "bus %s, device %s (driver " DRIVER_VERSION ")\n"
+ "SUSPENDED (no register access)\n",
+ hcd->self.controller->bus->name,
+ hcd->self.controller->bus_id);
+ goto done;
+ }
+
+ /* Capability Registers */
+ i = HC_VERSION(readl (&ehci->caps->hc_capbase));
+ temp = scnprintf (next, size,
+ "bus %s, device %s (driver " DRIVER_VERSION ")\n"
+ "EHCI %x.%02x, hcd state %d\n",
+ hcd->self.controller->bus->name,
+ hcd->self.controller->bus_id,
+ i >> 8, i & 0x0ff, hcd->state);
+ size -= temp;
+ next += temp;
+
+ // FIXME interpret both types of params
+ i = readl (&ehci->caps->hcs_params);
+ temp = scnprintf (next, size, "structural params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ i = readl (&ehci->caps->hcc_params);
+ temp = scnprintf (next, size, "capability params 0x%08x\n", i);
+ size -= temp;
+ next += temp;
+
+ /* Operational Registers */
+ temp = dbg_status_buf (scratch, sizeof scratch, label,
+ readl (&ehci->regs->status));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_command_buf (scratch, sizeof scratch, label,
+ readl (&ehci->regs->command));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = dbg_intr_buf (scratch, sizeof scratch, label,
+ readl (&ehci->regs->intr_enable));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "uframe %04x\n",
+ readl (&ehci->regs->frame_index));
+ size -= temp;
+ next += temp;
+
+ for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
+ temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1,
+ readl (&ehci->regs->port_status [i]));
+ temp = scnprintf (next, size, fmt, temp, scratch);
+ size -= temp;
+ next += temp;
+ }
+
+ if (ehci->reclaim) {
+ temp = scnprintf (next, size, "reclaim qh %p%s\n",
+ ehci->reclaim,
+ ehci->reclaim_ready ? " ready" : "");
+ size -= temp;
+ next += temp;
+ }
+
+#ifdef EHCI_STATS
+ temp = scnprintf (next, size,
+ "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
+ ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+ ehci->stats.lost_iaa);
+ size -= temp;
+ next += temp;
+
+ temp = scnprintf (next, size, "complete %ld unlink %ld\n",
+ ehci->stats.complete, ehci->stats.unlink);
+ size -= temp;
+ next += temp;
+#endif
+
+done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ return PAGE_SIZE - size;
+}
+static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+static inline void create_debug_files (struct ehci_hcd *ehci)
+{
+ struct class_device *cldev = &ehci_to_hcd(ehci)->self.class_dev;
+
+ class_device_create_file(cldev, &class_device_attr_async);
+ class_device_create_file(cldev, &class_device_attr_periodic);
+ class_device_create_file(cldev, &class_device_attr_registers);
+}
+
+static inline void remove_debug_files (struct ehci_hcd *ehci)
+{
+ struct class_device *cldev = &ehci_to_hcd(ehci)->self.class_dev;
+
+ class_device_remove_file(cldev, &class_device_attr_async);
+ class_device_remove_file(cldev, &class_device_attr_periodic);
+ class_device_remove_file(cldev, &class_device_attr_registers);
+}
+
+#endif /* STUB_DEBUG_FILES */
+
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
new file mode 100644
index 0000000..a63bb79
--- /dev/null
+++ b/drivers/usb/host/ehci-hcd.c
@@ -0,0 +1,1261 @@
+/*
+ * Copyright (c) 2000-2004 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>
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dmapool.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>
+#include <linux/reboot.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+
+#include "../core/hcd.h"
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hc_driver implementation ... experimental, incomplete.
+ * Based on the final 1.0 register interface specification.
+ *
+ * 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, and others.
+ * Special thanks to Intel and VIA for providing host controllers to
+ * test this driver on, and Cypress (including In-System Design) for
+ * providing early devices for those host controllers to talk to!
+ *
+ * HISTORY:
+ *
+ * 2004-05-10 Root hub and PCI suspend/resume support; remote wakeup. (db)
+ * 2004-02-24 Replace pci_* with generic dma_* API calls (dsaxena@plexity.net)
+ * 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka,
+ * <sojkam@centrum.cz>, updates by DB).
+ *
+ * 2002-11-29 Correct handling for hw async_next register.
+ * 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
+ * only scheduling is different, no arbitrary limitations.
+ * 2002-07-25 Sanity check PCI reads, mostly for better cardbus support,
+ * clean up HC run state handshaking.
+ * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
+ * 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other
+ * missing pieces: enabling 64bit dma, handoff from BIOS/SMM.
+ * 2002-05-07 Some error path cleanups to report better errors; wmb();
+ * use non-CVS version id; better iso bandwidth claim.
+ * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on
+ * errors in submit path. Bugfixes to interrupt scheduling/processing.
+ * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift
+ * more checking to generic hcd framework (db). Make it work with
+ * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt).
+ * 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.
+ * 2001-June Works with usb-storage and NEC EHCI on 2.4
+ */
+
+#define DRIVER_VERSION "10 Dec 2004"
+#define DRIVER_AUTHOR "David Brownell"
+#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
+
+static const char hcd_name [] = "ehci_hcd";
+
+
+#undef EHCI_VERBOSE_DEBUG
+#undef EHCI_URB_TRACE
+
+#ifdef DEBUG
+#define EHCI_STATS
+#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 4 /* 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
+#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
+
+#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
+#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
+#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
+#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
+
+/* Initial IRQ latency: faster than hw default */
+static int log2_irq_thresh = 0; // 0 to 6
+module_param (log2_irq_thresh, int, S_IRUGO);
+MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+/* initial park setting: slower than hw default */
+static unsigned park = 0;
+module_param (park, uint, S_IRUGO);
+MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci.h"
+#include "ehci-dbg.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * handshake - spin reading hc until handshake completes or fails
+ * @ptr: address of hc register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done). There are two failure modes: "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ *
+ * That last failure should_only happen in cases like physical cardbus eject
+ * before driver shutdown. But it also seems to be caused by bugs in cardbus
+ * bridge shutdown: shutting down the bridge before the devices using it.
+ */
+static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = readl (ptr);
+ if (result == ~(u32)0) /* card removed */
+ return -ENODEV;
+ result &= mask;
+ if (result == done)
+ return 0;
+ udelay (1);
+ usec--;
+ } while (usec > 0);
+ return -ETIMEDOUT;
+}
+
+/* force HC to halt state from unknown (EHCI spec section 2.3) */
+static int ehci_halt (struct ehci_hcd *ehci)
+{
+ u32 temp = readl (&ehci->regs->status);
+
+ if ((temp & STS_HALT) != 0)
+ return 0;
+
+ temp = readl (&ehci->regs->command);
+ temp &= ~CMD_RUN;
+ writel (temp, &ehci->regs->command);
+ return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125);
+}
+
+/* put TDI/ARC silicon into EHCI mode */
+static void tdi_reset (struct ehci_hcd *ehci)
+{
+ u32 __iomem *reg_ptr;
+ u32 tmp;
+
+ reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68);
+ tmp = readl (reg_ptr);
+ tmp |= 0x3;
+ writel (tmp, reg_ptr);
+}
+
+/* reset a non-running (STS_HALT == 1) controller */
+static int ehci_reset (struct ehci_hcd *ehci)
+{
+ int retval;
+ u32 command = readl (&ehci->regs->command);
+
+ command |= CMD_RESET;
+ dbg_cmd (ehci, "reset", command);
+ writel (command, &ehci->regs->command);
+ ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ ehci->next_statechange = jiffies;
+ retval = handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
+
+ if (retval)
+ return retval;
+
+ if (ehci_is_TDI(ehci))
+ tdi_reset (ehci);
+
+ return retval;
+}
+
+/* idle the controller (from running) */
+static void ehci_quiesce (struct ehci_hcd *ehci)
+{
+ u32 temp;
+
+#ifdef DEBUG
+ if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ BUG ();
+#endif
+
+ /* wait for any schedule enables/disables to take effect */
+ temp = readl (&ehci->regs->command) << 10;
+ temp &= STS_ASS | STS_PSS;
+ if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
+ temp, 16 * 125) != 0) {
+ ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ return;
+ }
+
+ /* then disable anything that's still active */
+ temp = readl (&ehci->regs->command);
+ temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
+ writel (temp, &ehci->regs->command);
+
+ /* hardware can take 16 microframes to turn off ... */
+ if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
+ 0, 16 * 125) != 0) {
+ ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ return;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
+
+#include "ehci-hub.c"
+#include "ehci-mem.c"
+#include "ehci-q.c"
+#include "ehci-sched.c"
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_watchdog (unsigned long param)
+{
+ struct ehci_hcd *ehci = (struct ehci_hcd *) param;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* lost IAA irqs wedge things badly; seen with a vt8235 */
+ if (ehci->reclaim) {
+ u32 status = readl (&ehci->regs->status);
+
+ if (status & STS_IAA) {
+ ehci_vdbg (ehci, "lost IAA\n");
+ COUNT (ehci->stats.lost_iaa);
+ writel (STS_IAA, &ehci->regs->status);
+ ehci->reclaim_ready = 1;
+ }
+ }
+
+ /* stop async processing after it's idled a bit */
+ if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
+ start_unlink_async (ehci, ehci->async);
+
+ /* ehci could run by timer, without IRQs ... */
+ ehci_work (ehci, NULL);
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+#ifdef CONFIG_PCI
+
+/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
+ * off the controller (maybe it can boot from highspeed USB disks).
+ */
+static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
+{
+ if (cap & (1 << 16)) {
+ int msec = 5000;
+ struct pci_dev *pdev =
+ to_pci_dev(ehci_to_hcd(ehci)->self.controller);
+
+ /* request handoff to OS */
+ cap |= 1 << 24;
+ pci_write_config_dword(pdev, where, cap);
+
+ /* and wait a while for it to happen */
+ do {
+ msleep(10);
+ msec -= 10;
+ pci_read_config_dword(pdev, where, &cap);
+ } while ((cap & (1 << 16)) && msec);
+ if (cap & (1 << 16)) {
+ ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
+ where, cap);
+ // some BIOS versions seem buggy...
+ // return 1;
+ ehci_warn (ehci, "continuing after BIOS bug...\n");
+ return 0;
+ }
+ ehci_dbg (ehci, "BIOS handoff succeeded\n");
+ }
+ return 0;
+}
+
+#endif
+
+static int
+ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+{
+ struct ehci_hcd *ehci;
+
+ ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+
+ /* make BIOS/etc use companion controller during reboot */
+ writel (0, &ehci->regs->configured_flag);
+ return 0;
+}
+
+
+/* called by khubd or root hub init threads */
+
+static int ehci_hc_reset (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ unsigned count = 256/4;
+
+ spin_lock_init (&ehci->lock);
+
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase));
+ dbg_hcs_params (ehci, "reset");
+ dbg_hcc_params (ehci, "reset");
+
+#ifdef CONFIG_PCI
+ /* EHCI 0.96 and later may have "extended capabilities" */
+ if (hcd->self.controller->bus == &pci_bus_type) {
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ switch (pdev->vendor) {
+ case PCI_VENDOR_ID_TDI:
+ if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
+ ehci->is_tdi_rh_tt = 1;
+ tdi_reset (ehci);
+ }
+ break;
+ case PCI_VENDOR_ID_AMD:
+ /* AMD8111 EHCI doesn't work, according to AMD errata */
+ if (pdev->device == 0x7463) {
+ ehci_info (ehci, "ignoring AMD8111 (errata)\n");
+ return -EIO;
+ }
+ break;
+ }
+
+ temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
+ } else
+ temp = 0;
+ while (temp && count--) {
+ u32 cap;
+
+ pci_read_config_dword (to_pci_dev(hcd->self.controller),
+ temp, &cap);
+ ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
+ switch (cap & 0xff) {
+ case 1: /* BIOS/SMM/... handoff */
+ if (bios_handoff (ehci, temp, cap) != 0)
+ return -EOPNOTSUPP;
+ break;
+ case 0: /* illegal reserved capability */
+ ehci_warn (ehci, "illegal capability!\n");
+ cap = 0;
+ /* FALLTHROUGH */
+ default: /* unknown */
+ break;
+ }
+ temp = (cap >> 8) & 0xff;
+ }
+ if (!count) {
+ ehci_err (ehci, "bogus capabilities ... PCI problems!\n");
+ return -EIO;
+ }
+ if (ehci_is_TDI(ehci))
+ ehci_reset (ehci);
+#endif
+
+ /* cache this readonly data; minimize PCI reads */
+ ehci->hcs_params = readl (&ehci->caps->hcs_params);
+
+ /* at least the Genesys GL880S needs fixup here */
+ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
+ temp &= 0x0f;
+ if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) {
+ ehci_dbg (ehci, "bogus port configuration: "
+ "cc=%d x pcc=%d < ports=%d\n",
+ HCS_N_CC(ehci->hcs_params),
+ HCS_N_PCC(ehci->hcs_params),
+ HCS_N_PORTS(ehci->hcs_params));
+
+#ifdef CONFIG_PCI
+ if (hcd->self.controller->bus == &pci_bus_type) {
+ struct pci_dev *pdev;
+
+ pdev = to_pci_dev(hcd->self.controller);
+ switch (pdev->vendor) {
+ case 0x17a0: /* GENESYS */
+ /* GL880S: should be PORTS=2 */
+ temp |= (ehci->hcs_params & ~0xf);
+ ehci->hcs_params = temp;
+ break;
+ case PCI_VENDOR_ID_NVIDIA:
+ /* NF4: should be PCC=10 */
+ break;
+ }
+ }
+#endif
+ }
+
+ /* force HC to halt state */
+ return ehci_halt (ehci);
+}
+
+static int ehci_start (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ struct usb_device *udev;
+ struct usb_bus *bus;
+ int retval;
+ u32 hcc_params;
+ u8 sbrn = 0;
+ int first;
+
+ /* skip some things on restart paths */
+ first = (ehci->watchdog.data == 0);
+ if (first) {
+ init_timer (&ehci->watchdog);
+ ehci->watchdog.function = ehci_watchdog;
+ ehci->watchdog.data = (unsigned long) ehci;
+ }
+
+ /*
+ * 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 (first && (retval = ehci_mem_init (ehci, GFP_KERNEL)) < 0)
+ return retval;
+
+ /* controllers may cache some of the periodic schedule ... */
+ hcc_params = readl (&ehci->caps->hcc_params);
+ 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->reclaim = NULL;
+ ehci->reclaim_ready = 0;
+ ehci->next_uframe = -1;
+
+ /* controller state: unknown --> reset */
+
+ /* EHCI spec section 4.1 */
+ if ((retval = ehci_reset (ehci)) != 0) {
+ ehci_mem_cleanup (ehci);
+ return retval;
+ }
+ writel (ehci->periodic_dma, &ehci->regs->frame_list);
+
+#ifdef CONFIG_PCI
+ if (hcd->self.controller->bus == &pci_bus_type) {
+ struct pci_dev *pdev;
+ u16 port_wake;
+
+ pdev = to_pci_dev(hcd->self.controller);
+
+ /* Serial Bus Release Number is at PCI 0x60 offset */
+ pci_read_config_byte(pdev, 0x60, &sbrn);
+
+ /* port wake capability, reported by boot firmware */
+ pci_read_config_word(pdev, 0x62, &port_wake);
+ hcd->can_wakeup = (port_wake & 1) != 0;
+
+ /* help hc dma work well with cachelines */
+ pci_set_mwi (pdev);
+ }
+#endif
+
+ /*
+ * dedicate a qh for the async ring head, since we couldn't unlink
+ * a 'real' qh without stopping the async schedule [4.8]. use it
+ * as the 'reclamation list head' too.
+ * its dummy is used in hw_alt_next of many tds, to prevent the qh
+ * from automatically advancing to the next td after short reads.
+ */
+ if (first) {
+ ehci->async->qh_next.qh = NULL;
+ ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
+ ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
+ ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
+ ehci->async->hw_qtd_next = EHCI_LIST_END;
+ ehci->async->qh_state = QH_STATE_LINKED;
+ ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
+ }
+ writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+ /*
+ * hcc_params controls whether ehci->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * pci_pool consistent memory always uses segment zero.
+ * streaming mappings for I/O buffers, like pci_map_single(),
+ * can return segments above 4GB, if the device allows.
+ *
+ * NOTE: the dma mask is visible through dma_supported(), so
+ * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+ * Scsi_Host.highmem_io, and so forth. It's readonly to all
+ * host side drivers though.
+ */
+ if (HCC_64BIT_ADDR (hcc_params)) {
+ writel (0, &ehci->regs->segment);
+#if 0
+// this is deeply broken on almost all architectures
+ if (!pci_set_dma_mask (to_pci_dev(hcd->self.controller), 0xffffffffffffffffULL))
+ ehci_info (ehci, "enabled 64bit PCI DMA\n");
+#endif
+ }
+
+ /* clear interrupt enables, set irq latency */
+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+ log2_irq_thresh = 0;
+ temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_CANPARK(hcc_params)) {
+ /* HW default park == 3, on hardware that supports it (like
+ * NVidia and ALI silicon), maximizes throughput on the async
+ * schedule by avoiding QH fetches between transfers.
+ *
+ * With fast usb storage devices and NForce2, "park" seems to
+ * make problems: throughput reduction (!), data errors...
+ */
+ if (park) {
+ park = min (park, (unsigned) 3);
+ temp |= CMD_PARK;
+ temp |= park << 8;
+ }
+ ehci_info (ehci, "park %d\n", park);
+ }
+ if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ temp &= ~(3 << 2);
+ temp |= (EHCI_TUNE_FLS << 2);
+ switch (EHCI_TUNE_FLS) {
+ case 0: ehci->periodic_size = 1024; break;
+ case 1: ehci->periodic_size = 512; break;
+ case 2: ehci->periodic_size = 256; break;
+ default: BUG ();
+ }
+ }
+ // Philips, Intel, and maybe others need CMD_RUN before the
+ // root hub will detect new devices (why?); NEC doesn't
+ temp |= CMD_RUN;
+ writel (temp, &ehci->regs->command);
+ dbg_cmd (ehci, "init", temp);
+
+ /* set async sleep time = 10 us ... ? */
+
+ /* wire up the root hub */
+ bus = hcd_to_bus (hcd);
+ udev = first ? usb_alloc_dev (NULL, bus, 0) : bus->root_hub;
+ if (!udev) {
+done2:
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+ udev->speed = USB_SPEED_HIGH;
+ udev->state = first ? USB_STATE_ATTACHED : USB_STATE_CONFIGURED;
+
+ /*
+ * 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. (Except where one is integrated,
+ * and there's no companion controller unless maybe for USB OTG.)
+ */
+ if (first) {
+ ehci->reboot_notifier.notifier_call = ehci_reboot;
+ register_reboot_notifier (&ehci->reboot_notifier);
+ }
+
+ hcd->state = HC_STATE_RUNNING;
+ writel (FLAG_CF, &ehci->regs->configured_flag);
+ readl (&ehci->regs->command); /* unblock posted write */
+
+ temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
+ ehci_info (ehci,
+ "USB %x.%x %s, EHCI %x.%02x, driver %s\n",
+ ((sbrn & 0xf0)>>4), (sbrn & 0x0f),
+ first ? "initialized" : "restarted",
+ temp >> 8, temp & 0xff, DRIVER_VERSION);
+
+ /*
+ * From here on, khubd concurrently accesses the root
+ * hub; drivers will be talking to enumerated devices.
+ * (On restart paths, khubd already knows about the root
+ * hub and could find work as soon as we wrote FLAG_CF.)
+ *
+ * Before this point the HC was idle/ready. After, khubd
+ * and device drivers may start it running.
+ */
+ if (first && usb_hcd_register_root_hub (udev, hcd) != 0) {
+ if (hcd->state == HC_STATE_RUNNING)
+ ehci_quiesce (ehci);
+ ehci_reset (ehci);
+ usb_put_dev (udev);
+ retval = -ENODEV;
+ goto done2;
+ }
+
+ writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
+
+ if (first)
+ create_debug_files (ehci);
+
+ return 0;
+}
+
+/* always called by thread; normally rmmod */
+
+static void ehci_stop (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u8 rh_ports, port;
+
+ ehci_dbg (ehci, "stop\n");
+
+ /* Turn off port power on all root hub ports. */
+ rh_ports = HCS_N_PORTS (ehci->hcs_params);
+ for (port = 1; port <= rh_ports; port++)
+ (void) ehci_hub_control(hcd,
+ ClearPortFeature, USB_PORT_FEAT_POWER,
+ port, NULL, 0);
+
+ /* no more interrupts ... */
+ del_timer_sync (&ehci->watchdog);
+
+ spin_lock_irq(&ehci->lock);
+ if (HC_IS_RUNNING (hcd->state))
+ ehci_quiesce (ehci);
+
+ ehci_reset (ehci);
+ writel (0, &ehci->regs->intr_enable);
+ spin_unlock_irq(&ehci->lock);
+
+ /* let companion controllers work when we aren't */
+ writel (0, &ehci->regs->configured_flag);
+ unregister_reboot_notifier (&ehci->reboot_notifier);
+
+ remove_debug_files (ehci);
+
+ /* root hub is shut down separately (first, when possible) */
+ spin_lock_irq (&ehci->lock);
+ if (ehci->async)
+ ehci_work (ehci, NULL);
+ spin_unlock_irq (&ehci->lock);
+ ehci_mem_cleanup (ehci);
+
+#ifdef EHCI_STATS
+ ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
+ ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+ ehci->stats.lost_iaa);
+ ehci_dbg (ehci, "complete %ld unlink %ld\n",
+ ehci->stats.complete, ehci->stats.unlink);
+#endif
+
+ dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
+}
+
+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 */
+
+/* These routines rely on the bus (pci, platform, etc)
+ * to handle powerdown and wakeup, and currently also on
+ * transceivers that don't need any software attention to set up
+ * the right sort of wakeup.
+ */
+
+static int ehci_suspend (struct usb_hcd *hcd, u32 state)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+
+ if (time_before (jiffies, ehci->next_statechange))
+ msleep (100);
+
+#ifdef CONFIG_USB_SUSPEND
+ (void) usb_suspend_device (hcd->self.root_hub, state);
+#else
+ usb_lock_device (hcd->self.root_hub);
+ (void) ehci_hub_suspend (hcd);
+ usb_unlock_device (hcd->self.root_hub);
+#endif
+
+ // save (PCI) FLADJ in case of Vaux power loss
+ // ... we'd only use it to handle clock skew
+
+ return 0;
+}
+
+static int ehci_resume (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ unsigned port;
+ struct usb_device *root = hcd->self.root_hub;
+ int retval = -EINVAL;
+ int powerup = 0;
+
+ // maybe restore (PCI) FLADJ
+
+ if (time_before (jiffies, ehci->next_statechange))
+ msleep (100);
+
+ /* If any port is suspended, we know we can/must resume the HC. */
+ for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) {
+ u32 status;
+ port--;
+ status = readl (&ehci->regs->port_status [port]);
+ if (status & PORT_SUSPEND) {
+ down (&hcd->self.root_hub->serialize);
+ retval = ehci_hub_resume (hcd);
+ up (&hcd->self.root_hub->serialize);
+ break;
+ }
+ if ((status & PORT_POWER) == 0)
+ powerup = 1;
+ if (!root->children [port])
+ continue;
+ dbg_port (ehci, __FUNCTION__, port + 1, status);
+ usb_set_device_state (root->children[port],
+ USB_STATE_NOTATTACHED);
+ }
+
+ /* Else reset, to cope with power loss or flush-to-storage
+ * style "resume" having activated BIOS during reboot.
+ */
+ if (port == 0) {
+ (void) ehci_halt (ehci);
+ (void) ehci_reset (ehci);
+ (void) ehci_hc_reset (hcd);
+
+ /* emptying the schedule aborts any urbs */
+ spin_lock_irq (&ehci->lock);
+ if (ehci->reclaim)
+ ehci->reclaim_ready = 1;
+ ehci_work (ehci, NULL);
+ spin_unlock_irq (&ehci->lock);
+
+ /* restart; khubd will disconnect devices */
+ retval = ehci_start (hcd);
+
+ /* here we "know" root ports should always stay powered;
+ * but some controllers may lost all power.
+ */
+ if (powerup) {
+ ehci_dbg (ehci, "...powerup ports...\n");
+ for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
+ (void) ehci_hub_control(hcd,
+ SetPortFeature, USB_PORT_FEAT_POWER,
+ port--, NULL, 0);
+ msleep(20);
+ }
+ }
+
+ return retval;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * ehci_work is called from some interrupts, timers, and so on.
+ * it calls driver completion functions, after dropping ehci->lock.
+ */
+static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+ timer_action_done (ehci, TIMER_IO_WATCHDOG);
+ if (ehci->reclaim_ready)
+ end_unlink_async (ehci, regs);
+
+ /* another CPU may drop ehci->lock during a schedule scan while
+ * it reports urb completions. this flag guards against bogus
+ * attempts at re-entrant schedule scanning.
+ */
+ if (ehci->scanning)
+ return;
+ ehci->scanning = 1;
+ scan_async (ehci, regs);
+ if (ehci->next_uframe != -1)
+ scan_periodic (ehci, regs);
+ ehci->scanning = 0;
+
+ /* the IO watchdog guards against hardware or driver bugs that
+ * misplace IRQs, and should let us run completely without IRQs.
+ * such lossage has been observed on both VT6202 and VT8235.
+ */
+ if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) &&
+ (ehci->async->qh_next.ptr != NULL ||
+ ehci->periodic_sched != 0))
+ timer_action (ehci, TIMER_IO_WATCHDOG);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 status;
+ int bh;
+
+ spin_lock (&ehci->lock);
+
+ status = readl (&ehci->regs->status);
+
+ /* e.g. cardbus physical eject */
+ if (status == ~(u32) 0) {
+ ehci_dbg (ehci, "device removed\n");
+ goto dead;
+ }
+
+ status &= INTR_MASK;
+ if (!status) { /* irq sharing? */
+ spin_unlock(&ehci->lock);
+ return IRQ_NONE;
+ }
+
+ /* clear (just) interrupts */
+ writel (status, &ehci->regs->status);
+ readl (&ehci->regs->command); /* unblock posted write */
+ bh = 0;
+
+#ifdef EHCI_VERBOSE_DEBUG
+ /* unrequested/ignored: Frame List Rollover */
+ 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)) {
+ if (likely ((status & STS_ERR) == 0))
+ COUNT (ehci->stats.normal);
+ else
+ COUNT (ehci->stats.error);
+ bh = 1;
+ }
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+ COUNT (ehci->stats.reclaim);
+ ehci->reclaim_ready = 1;
+ bh = 1;
+ }
+
+ /* remote wakeup [4.3.1] */
+ if ((status & STS_PCD) && hcd->remote_wakeup) {
+ unsigned i = HCS_N_PORTS (ehci->hcs_params);
+
+ /* resume root hub? */
+ status = readl (&ehci->regs->command);
+ if (!(status & CMD_RUN))
+ writel (status | CMD_RUN, &ehci->regs->command);
+
+ while (i--) {
+ status = readl (&ehci->regs->port_status [i]);
+ if (status & PORT_OWNER)
+ continue;
+ if (!(status & PORT_RESUME)
+ || ehci->reset_done [i] != 0)
+ continue;
+
+ /* start 20 msec resume signaling from this port,
+ * and make khubd collect PORT_STAT_C_SUSPEND to
+ * stop that signaling.
+ */
+ ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
+ mod_timer (&hcd->rh_timer,
+ ehci->reset_done [i] + 1);
+ ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
+ }
+ }
+
+ /* PCI errors [4.15.2.4] */
+ if (unlikely ((status & STS_FATAL) != 0)) {
+ /* bogus "fatal" IRQs appear on some chips... why? */
+ status = readl (&ehci->regs->status);
+ dbg_cmd (ehci, "fatal", readl (&ehci->regs->command));
+ dbg_status (ehci, "fatal", status);
+ if (status & STS_HALT) {
+ ehci_err (ehci, "fatal error\n");
+dead:
+ ehci_reset (ehci);
+ writel (0, &ehci->regs->configured_flag);
+ /* generic layer kills/unlinks all urbs, then
+ * uses ehci_stop to clean up the rest
+ */
+ bh = 1;
+ }
+ }
+
+ if (bh)
+ ehci_work (ehci, regs);
+ spin_unlock (&ehci->lock);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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.self.controller.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: control, bulk, and interrupt share the same code to append TDs
+ * to a (possibly active) QH, and the same QH scanning code.
+ */
+static int ehci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct list_head qtd_list;
+
+ INIT_LIST_HEAD (&qtd_list);
+
+ switch (usb_pipetype (urb->pipe)) {
+ // case PIPE_CONTROL:
+ // case PIPE_BULK:
+ default:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return submit_async (ehci, ep, urb, &qtd_list, mem_flags);
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit (ehci, ep, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+ if (urb->dev->speed == USB_SPEED_HIGH)
+ return itd_submit (ehci, urb, mem_flags);
+ else
+ return sitd_submit (ehci, urb, mem_flags);
+ }
+}
+
+static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ /* if we need to use IAA and it's busy, defer */
+ if (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
+ struct ehci_qh *last;
+
+ for (last = ehci->reclaim;
+ last->reclaim;
+ last = last->reclaim)
+ continue;
+ qh->qh_state = QH_STATE_UNLINK_WAIT;
+ last->reclaim = qh;
+
+ /* bypass IAA if the hc can't care */
+ } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
+ end_unlink_async (ehci, NULL);
+
+ /* something else might have unlinked the qh by now */
+ if (qh->qh_state == QH_STATE_LINKED)
+ start_unlink_async (ehci, qh);
+}
+
+/* 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;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ switch (usb_pipetype (urb->pipe)) {
+ // case PIPE_CONTROL:
+ // case PIPE_BULK:
+ default:
+ qh = (struct ehci_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ unlink_async (ehci, qh);
+ break;
+
+ case PIPE_INTERRUPT:
+ qh = (struct ehci_qh *) urb->hcpriv;
+ if (!qh)
+ break;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ intr_deschedule (ehci, qh);
+ /* FALL THROUGH */
+ case QH_STATE_IDLE:
+ qh_completions (ehci, qh, NULL);
+ break;
+ default:
+ ehci_dbg (ehci, "bogus qh %p state %d\n",
+ qh, qh->qh_state);
+ goto done;
+ }
+
+ /* reschedule QH iff another request is queued */
+ if (!list_empty (&qh->qtd_list)
+ && HC_IS_RUNNING (hcd->state)) {
+ int status;
+
+ status = qh_schedule (ehci, qh);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ if (status != 0) {
+ // shouldn't happen often, but ...
+ // FIXME kill those tds' urbs
+ err ("can't reschedule qh %p, err %d",
+ qh, status);
+ }
+ return status;
+ }
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ // itd or sitd ...
+
+ // wait till next completion, do it then.
+ // completion irqs can wait up to 1024 msec,
+ break;
+ }
+done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// bulk qh holds the data toggle
+
+static void
+ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ unsigned long flags;
+ struct ehci_qh *qh, *tmp;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+rescan:
+ spin_lock_irqsave (&ehci->lock, flags);
+ qh = ep->hcpriv;
+ if (!qh)
+ goto done;
+
+ /* endpoints can be iso streams. for now, we don't
+ * accelerate iso completions ... so spin a while.
+ */
+ if (qh->hw_info1 == 0) {
+ ehci_vdbg (ehci, "iso delay\n");
+ goto idle_timeout;
+ }
+
+ if (!HC_IS_RUNNING (hcd->state))
+ qh->qh_state = QH_STATE_IDLE;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ for (tmp = ehci->async->qh_next.qh;
+ tmp && tmp != qh;
+ tmp = tmp->qh_next.qh)
+ continue;
+ /* periodic qh self-unlinks on empty */
+ if (!tmp)
+ goto nogood;
+ unlink_async (ehci, qh);
+ /* FALL THROUGH */
+ case QH_STATE_UNLINK: /* wait for hw to finish? */
+idle_timeout:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ schedule_timeout (1);
+ goto rescan;
+ case QH_STATE_IDLE: /* fully unlinked */
+ if (list_empty (&qh->qtd_list)) {
+ qh_put (qh);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+nogood:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. just leak this memory.
+ */
+ ehci_err (ehci, "qh %p (#%02x) state %d%s\n",
+ qh, ep->desc.bEndpointAddress, qh->qh_state,
+ list_empty (&qh->qtd_list) ? "" : "(has tds)");
+ break;
+ }
+ ep->hcpriv = NULL;
+done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_driver = {
+ .description = hcd_name,
+ .product_desc = "EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_hc_reset,
+ .start = ehci_start,
+#ifdef CONFIG_PM
+ .suspend = ehci_suspend,
+ .resume = ehci_resume,
+#endif
+ .stop = ehci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .hub_suspend = ehci_hub_suspend,
+ .hub_resume = ehci_hub_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI 1.0 doesn't require PCI */
+
+#ifdef CONFIG_PCI
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id pci_ids [] = { {
+ /* handle any USB 2.0 EHCI controller */
+ PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0),
+ .driver_data = (unsigned long) &ehci_driver,
+ },
+ { /* 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
+};
+
+#endif /* PCI */
+
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_LICENSE ("GPL");
+
+static int __init init (void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+ hcd_name,
+ sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
+ sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
+
+ return pci_register_driver (&ehci_pci_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+ pci_unregister_driver (&ehci_pci_driver);
+}
+module_exit (cleanup);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
new file mode 100644
index 0000000..2373537
--- /dev/null
+++ b/drivers/usb/host/ehci-hub.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * 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
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+static int ehci_hub_suspend (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ int port;
+
+ if (time_before (jiffies, ehci->next_statechange))
+ msleep(5);
+
+ port = HCS_N_PORTS (ehci->hcs_params);
+ spin_lock_irq (&ehci->lock);
+
+ /* stop schedules, clean any completed work */
+ if (HC_IS_RUNNING(hcd->state)) {
+ ehci_quiesce (ehci);
+ hcd->state = HC_STATE_QUIESCING;
+ }
+ ehci->command = readl (&ehci->regs->command);
+ if (ehci->reclaim)
+ ehci->reclaim_ready = 1;
+ ehci_work(ehci, NULL);
+
+ /* suspend any active/unsuspended ports, maybe allow wakeup */
+ while (port--) {
+ u32 __iomem *reg = &ehci->regs->port_status [port];
+ u32 t1 = readl (reg);
+ u32 t2 = t1;
+
+ if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
+ t2 |= PORT_SUSPEND;
+ if (hcd->remote_wakeup)
+ t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
+ else
+ t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
+
+ if (t1 != t2) {
+ ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
+ port + 1, t1, t2);
+ writel (t2, reg);
+ }
+ }
+
+ /* turn off now-idle HC */
+ ehci_halt (ehci);
+ hcd->state = HC_STATE_SUSPENDED;
+
+ ehci->next_statechange = jiffies + msecs_to_jiffies(10);
+ spin_unlock_irq (&ehci->lock);
+ return 0;
+}
+
+
+/* caller has locked the root hub, and should reset/reinit on error */
+static int ehci_hub_resume (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ int i;
+ int intr_enable;
+
+ if (time_before (jiffies, ehci->next_statechange))
+ msleep(5);
+ spin_lock_irq (&ehci->lock);
+
+ /* re-init operational registers in case we lost power */
+ if (readl (&ehci->regs->intr_enable) == 0) {
+ /* at least some APM implementations will try to deliver
+ * IRQs right away, so delay them until we're ready.
+ */
+ intr_enable = 1;
+ writel (0, &ehci->regs->segment);
+ writel (ehci->periodic_dma, &ehci->regs->frame_list);
+ writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
+ } else
+ intr_enable = 0;
+ ehci_dbg(ehci, "resume root hub%s\n",
+ intr_enable ? " after power loss" : "");
+
+ /* restore CMD_RUN, framelist size, and irq threshold */
+ writel (ehci->command, &ehci->regs->command);
+
+ /* take ports out of suspend */
+ i = HCS_N_PORTS (ehci->hcs_params);
+ while (i--) {
+ temp = readl (&ehci->regs->port_status [i]);
+ temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
+ if (temp & PORT_SUSPEND) {
+ ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
+ temp |= PORT_RESUME;
+ }
+ writel (temp, &ehci->regs->port_status [i]);
+ }
+ i = HCS_N_PORTS (ehci->hcs_params);
+ mdelay (20);
+ while (i--) {
+ temp = readl (&ehci->regs->port_status [i]);
+ if ((temp & PORT_SUSPEND) == 0)
+ continue;
+ temp &= ~PORT_RESUME;
+ writel (temp, &ehci->regs->port_status [i]);
+ ehci_vdbg (ehci, "resumed port %d\n", i + 1);
+ }
+ (void) readl (&ehci->regs->command);
+
+ /* maybe re-activate the schedule(s) */
+ temp = 0;
+ if (ehci->async->qh_next.qh)
+ temp |= CMD_ASE;
+ if (ehci->periodic_sched)
+ temp |= CMD_PSE;
+ if (temp) {
+ ehci->command |= temp;
+ writel (ehci->command, &ehci->regs->command);
+ }
+
+ ehci->next_statechange = jiffies + msecs_to_jiffies(5);
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Now we can safely re-enable irqs */
+ if (intr_enable)
+ writel (INTR_MASK, &ehci->regs->intr_enable);
+
+ spin_unlock_irq (&ehci->lock);
+ return 0;
+}
+
+#else
+
+#define ehci_hub_suspend NULL
+#define ehci_hub_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+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)) {
+
+ /* with integrated TT, there's nobody to hand it to! */
+ if (ehci_is_TDI(ehci)) {
+ ehci_dbg (ehci,
+ "Failed to enable port %d on root hub TT\n",
+ index+1);
+ return port_status;
+ }
+
+ ehci_dbg (ehci, "port %d full speed --> companion\n",
+ index + 1);
+
+ // what happens if HCS_N_CC(params) == 0 ?
+ port_status |= PORT_OWNER;
+ writel (port_status, &ehci->regs->port_status [index]);
+
+ } else
+ ehci_dbg (ehci, "port %d high speed\n", 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;
+
+ /* if !USB_SUSPEND, root hub timers won't get shut down ... */
+ if (!HC_IS_RUNNING(hcd->state))
+ return 0;
+
+ /* init status to no-changes */
+ buf [0] = 0;
+ ports = HCS_N_PORTS (ehci->hcs_params);
+ 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
+ // PORT_STAT_C_SUSPEND?
+ || ((temp & PORT_RESUME) != 0
+ && time_after (jiffies,
+ ehci->reset_done [i]))) {
+ if (i < 7)
+ buf [0] |= 1 << (i + 1);
+ else
+ buf [1] |= 1 << (i - 7);
+ status = STS_PCD;
+ }
+ }
+ /* FIXME autosuspend idle root hubs */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ehci_hub_descriptor (
+ struct ehci_hcd *ehci,
+ struct usb_hub_descriptor *desc
+) {
+ int ports = HCS_N_PORTS (ehci->hcs_params);
+ u16 temp;
+
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
+ 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 (ehci->hcs_params))
+ temp |= 0x0001; /* per-port power control */
+#if 0
+// re-enable when we support USB_PORT_FEAT_INDICATOR below.
+ if (HCS_INDICATOR (ehci->hcs_params))
+ temp |= 0x0080; /* per-port indicators (LEDs) */
+#endif
+ desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+
+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);
+ int ports = HCS_N_PORTS (ehci->hcs_params);
+ u32 temp, status;
+ 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:
+ if (temp & PORT_RESET)
+ goto error;
+ if (temp & PORT_SUSPEND) {
+ if ((temp & PORT_PE) == 0)
+ goto error;
+ /* resume signaling for 20 msec */
+ writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
+ &ehci->regs->port_status [wIndex]);
+ ehci->reset_done [wIndex] = jiffies
+ + msecs_to_jiffies (20);
+ }
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ /* we auto-clear this feature */
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC (ehci->hcs_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--;
+ status = 0;
+ temp = readl (&ehci->regs->port_status [wIndex]);
+
+ // wPortChange bits
+ if (temp & PORT_CSC)
+ status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+ if (temp & PORT_PEC)
+ status |= 1 << USB_PORT_FEAT_C_ENABLE;
+ if (temp & PORT_OCC)
+ status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
+
+ /* whoever resumes must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESUME)
+ && time_after (jiffies,
+ ehci->reset_done [wIndex])) {
+ status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+ ehci->reset_done [wIndex] = 0;
+
+ /* stop resume signaling */
+ temp = readl (&ehci->regs->port_status [wIndex]);
+ writel (temp & ~PORT_RESUME,
+ &ehci->regs->port_status [wIndex]);
+ retval = handshake (
+ &ehci->regs->port_status [wIndex],
+ PORT_RESUME, 0, 2000 /* 2msec */);
+ if (retval != 0) {
+ ehci_err (ehci, "port %d resume error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
+ }
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESET)
+ && time_after (jiffies,
+ ehci->reset_done [wIndex])) {
+ status |= 1 << USB_PORT_FEAT_C_RESET;
+ ehci->reset_done [wIndex] = 0;
+
+ /* force reset to complete */
+ writel (temp & ~PORT_RESET,
+ &ehci->regs->port_status [wIndex]);
+ retval = handshake (
+ &ehci->regs->port_status [wIndex],
+ PORT_RESET, 0, 500);
+ if (retval != 0) {
+ ehci_err (ehci, "port %d reset error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+
+ /* see what we found out */
+ temp = check_reset_complete (ehci, wIndex,
+ readl (&ehci->regs->port_status [wIndex]));
+ }
+
+ // don't show wPortStatus if it's owned by a companion hc
+ if (!(temp & PORT_OWNER)) {
+ if (temp & PORT_CONNECT) {
+ status |= 1 << USB_PORT_FEAT_CONNECTION;
+ // status may be from integrated TT
+ status |= ehci_port_speed(ehci, temp);
+ }
+ if (temp & PORT_PE)
+ status |= 1 << USB_PORT_FEAT_ENABLE;
+ if (temp & (PORT_SUSPEND|PORT_RESUME))
+ status |= 1 << USB_PORT_FEAT_SUSPEND;
+ if (temp & PORT_OC)
+ status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+ if (temp & PORT_RESET)
+ status |= 1 << USB_PORT_FEAT_RESET;
+ if (temp & PORT_POWER)
+ status |= 1 << USB_PORT_FEAT_POWER;
+ }
+
+#ifndef EHCI_VERBOSE_DEBUG
+ if (status & ~0xffff) /* only if wPortChange is interesting */
+#endif
+ dbg_port (ehci, "GetStatus", wIndex + 1, temp);
+ // we "know" this alignment is good, caller used kmalloc()...
+ *((__le32 *) buf) = cpu_to_le32 (status);
+ 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:
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_RESET) != 0)
+ goto error;
+ if (hcd->remote_wakeup)
+ temp |= PORT_WAKE_BITS;
+ writel (temp | PORT_SUSPEND,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC (ehci->hcs_params))
+ writel (temp | PORT_POWER,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (temp & PORT_RESUME)
+ goto error;
+ /* line status bits may report this as low speed,
+ * which can be fine if this root hub has a
+ * transaction translator built in.
+ */
+ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
+ && !ehci_is_TDI(ehci)
+ && PORT_USB11 (temp)) {
+ ehci_dbg (ehci,
+ "port %d low speed --> companion\n",
+ wIndex + 1);
+ temp |= PORT_OWNER;
+ } else {
+ ehci_vdbg (ehci, "port %d reset\n", 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
+ + msecs_to_jiffies (50);
+ }
+ 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 --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
new file mode 100644
index 0000000..9938697
--- /dev/null
+++ b/drivers/usb/host/ehci-mem.c
@@ -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 dma_pool or dma_alloc_coherent
+ * - 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 pageable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
+{
+ memset (qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ INIT_LIST_HEAD (&qtd->qtd_list);
+}
+
+static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
+{
+ struct ehci_qtd *qtd;
+ dma_addr_t dma;
+
+ qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
+ if (qtd != NULL) {
+ ehci_qtd_init (qtd, dma);
+ }
+ return qtd;
+}
+
+static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ dma_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static void qh_destroy (struct kref *kref)
+{
+ struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
+ struct ehci_hcd *ehci = qh->ehci;
+
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
+ ehci_dbg (ehci, "unused qh not empty!\n");
+ BUG ();
+ }
+ if (qh->dummy)
+ ehci_qtd_free (ehci, qh->dummy);
+ usb_put_dev (qh->dev);
+ dma_pool_free (ehci->qh_pool, qh, qh->qh_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 *)
+ dma_pool_alloc (ehci->qh_pool, flags, &dma);
+ if (!qh)
+ return qh;
+
+ memset (qh, 0, sizeof *qh);
+ kref_init(&qh->kref);
+ qh->ehci = ehci;
+ qh->qh_dma = dma;
+ // INIT_LIST_HEAD (&qh->qh_list);
+ INIT_LIST_HEAD (&qh->qtd_list);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = ehci_qtd_alloc (ehci, flags);
+ if (qh->dummy == NULL) {
+ ehci_dbg (ehci, "no dummy td\n");
+ dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+ qh = NULL;
+ }
+ return qh;
+}
+
+/* to share a qh (cpu threads, or hc) */
+static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
+{
+ kref_get(&qh->kref);
+ return qh;
+}
+
+static inline void qh_put (struct ehci_qh *qh)
+{
+ kref_put(&qh->kref, qh_destroy);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 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)
+{
+ if (ehci->async)
+ qh_put (ehci->async);
+ ehci->async = NULL;
+
+ /* DMA consistent memory and pools */
+ if (ehci->qtd_pool)
+ dma_pool_destroy (ehci->qtd_pool);
+ ehci->qtd_pool = NULL;
+
+ if (ehci->qh_pool) {
+ dma_pool_destroy (ehci->qh_pool);
+ ehci->qh_pool = NULL;
+ }
+
+ if (ehci->itd_pool)
+ dma_pool_destroy (ehci->itd_pool);
+ ehci->itd_pool = NULL;
+
+ if (ehci->sitd_pool)
+ dma_pool_destroy (ehci->sitd_pool);
+ ehci->sitd_pool = NULL;
+
+ if (ehci->periodic)
+ dma_free_coherent (ehci_to_hcd(ehci)->self.controller,
+ ehci->periodic_size * sizeof (u32),
+ ehci->periodic, ehci->periodic_dma);
+ ehci->periodic = NULL;
+
+ /* shadow periodic table */
+ if (ehci->pshadow)
+ kfree (ehci->pshadow);
+ ehci->pshadow = NULL;
+}
+
+/* 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 = dma_pool_create ("ehci_qtd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->qtd_pool) {
+ goto fail;
+ }
+
+ /* QHs for control/bulk/intr transfers */
+ ehci->qh_pool = dma_pool_create ("ehci_qh",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_qh),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->qh_pool) {
+ goto fail;
+ }
+ ehci->async = ehci_qh_alloc (ehci, flags);
+ if (!ehci->async) {
+ goto fail;
+ }
+
+ /* ITD for high speed ISO transfers */
+ ehci->itd_pool = dma_pool_create ("ehci_itd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_itd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->itd_pool) {
+ goto fail;
+ }
+
+ /* SITD for full/low speed split ISO transfers */
+ ehci->sitd_pool = dma_pool_create ("ehci_sitd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_sitd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
+ if (!ehci->sitd_pool) {
+ goto fail;
+ }
+
+ /* Hardware periodic table */
+ ehci->periodic = (__le32 *)
+ dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
+ ehci->periodic_size * sizeof(__le32),
+ &ehci->periodic_dma, 0);
+ if (ehci->periodic == NULL) {
+ goto fail;
+ }
+ 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);
+ if (ehci->pshadow == NULL) {
+ goto fail;
+ }
+ memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
+
+ return 0;
+
+fail:
+ ehci_dbg (ehci, "couldn't init memory\n");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+}
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
new file mode 100644
index 0000000..7df9b9a
--- /dev/null
+++ b/drivers/usb/host/ehci-q.c
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * 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 ... the core. QH/QTD 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 urbs (all three types) per endpoint. URBs may need several qtds.
+ *
+ * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge. That's in "ehci-sched.c".
+ *
+ * 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 maxpacket)
+{
+ int i, count;
+ u64 addr = buf;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
+ qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely (len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ 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;
+ }
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
+ }
+ qtd->hw_token = cpu_to_le32 ((count << 16) | token);
+ qtd->length = count;
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ /* writes to an active overlay are unsafe */
+ BUG_ON(qh->qh_state != QH_STATE_IDLE);
+
+ qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END;
+
+ /* Except for control endpoints, we make hardware maintain data
+ * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
+ * and set the pseudo-toggle in udev. Only usb_clear_halt() will
+ * ever clear it.
+ */
+ if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) {
+ unsigned is_out, epnum;
+
+ is_out = !(qtd->hw_token & cpu_to_le32(1 << 8));
+ epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f;
+ if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
+ qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
+ usb_settoggle (qh->dev, epnum, is_out, 1);
+ }
+ }
+
+ /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
+ wmb ();
+ qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
+}
+
+/* if it weren't for a common silicon quirk (writing the dummy into the qh
+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * recovery (including urb dequeue) would need software changes to a QH...
+ */
+static void
+qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qtd *qtd;
+
+ if (list_empty (&qh->qtd_list))
+ qtd = qh->dummy;
+ else {
+ qtd = list_entry (qh->qtd_list.next,
+ struct ehci_qtd, qtd_list);
+ /* first qtd may already be partially processed */
+ if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current)
+ qtd = NULL;
+ }
+
+ if (qtd)
+ qh_update (ehci, qh, qtd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void qtd_copy_status (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ size_t length,
+ u32 token
+)
+{
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely (QTD_PID (token) != 2))
+ urb->actual_length += length - QTD_LENGTH (token);
+
+ /* don't modify error codes */
+ if (unlikely (urb->status != -EINPROGRESS))
+ return;
+
+ /* force cleanup after short read; not always an error */
+ if (unlikely (IS_SHORT_READ (token)))
+ urb->status = -EREMOTEIO;
+
+ /* serious "can't proceed" faults reported by the hardware */
+ if (token & QTD_STS_HALT) {
+ if (token & QTD_STS_BABBLE) {
+ /* FIXME "must" disable babbling device's port too */
+ urb->status = -EOVERFLOW;
+ } else if (token & QTD_STS_MMF) {
+ /* fs/ls interrupt xfer missed the complete-split */
+ urb->status = -EPROTO;
+ } else 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_XACT) {
+ /* timeout, bad crc, wrong PID, etc; retried */
+ if (QTD_CERR (token))
+ urb->status = -EPIPE;
+ else {
+ ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n",
+ urb->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out");
+ urb->status = -EPROTO;
+ }
+ /* CERR nonzero + no errors + halt --> stall */
+ } else if (QTD_CERR (token))
+ urb->status = -EPIPE;
+ else /* unknown */
+ urb->status = -EPROTO;
+
+ ehci_vdbg (ehci,
+ "dev%d ep%d%s qtd token %08x --> status %d\n",
+ usb_pipedevice (urb->pipe),
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ token, urb->status);
+
+ /* if async CSPLIT failed, try cleaning out the TT buffer */
+ if (urb->status != -EPIPE
+ && urb->dev->tt && !usb_pipeint (urb->pipe)
+ && ((token & QTD_STS_MMF) != 0
+ || QTD_CERR(token) == 0)
+ && (!ehci_is_TDI(ehci)
+ || urb->dev->tt->hub !=
+ ehci_to_hcd(ehci)->self.root_hub)) {
+#ifdef DEBUG
+ struct usb_device *tt = urb->dev->tt->hub;
+ dev_dbg (&tt->dev,
+ "clear tt buffer port %d, a%d ep%d t%08x\n",
+ urb->dev->ttport, urb->dev->devnum,
+ usb_pipeendpoint (urb->pipe), token);
+#endif /* DEBUG */
+ usb_hub_tt_clear_buffer (urb->dev, urb->pipe);
+ }
+ }
+}
+
+static void
+ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
+__releases(ehci->lock)
+__acquires(ehci->lock)
+{
+ if (likely (urb->hcpriv != NULL)) {
+ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
+
+ /* S-mask in a QH means it's an interrupt urb */
+ if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) {
+
+ /* ... update hc-wide periodic stats (for usbfs) */
+ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
+ }
+ qh_put (qh);
+ }
+
+ spin_lock (&urb->lock);
+ urb->hcpriv = NULL;
+ switch (urb->status) {
+ case -EINPROGRESS: /* success */
+ urb->status = 0;
+ default: /* fault */
+ COUNT (ehci->stats.complete);
+ break;
+ case -EREMOTEIO: /* fault or normal */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ urb->status = 0;
+ COUNT (ehci->stats.complete);
+ break;
+ case -ECONNRESET: /* canceled */
+ case -ENOENT:
+ COUNT (ehci->stats.unlink);
+ break;
+ }
+ spin_unlock (&urb->lock);
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "%s %s urb %p ep%d%s status %d len %d/%d\n",
+ __FUNCTION__, urb->dev->devpath, urb,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->status,
+ urb->actual_length, urb->transfer_buffer_length);
+#endif
+
+ /* complete() can reenter this HCD */
+ spin_unlock (&ehci->lock);
+ usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb, regs);
+ spin_lock (&ehci->lock);
+}
+
+static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
+static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
+static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+/*
+ * Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current. Returns number of completions called,
+ * indicating how much "real" work we did.
+ */
+#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT)
+static unsigned
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
+{
+ struct ehci_qtd *last = NULL, *end = qh->dummy;
+ struct list_head *entry, *tmp;
+ int stopped;
+ unsigned count = 0;
+ int do_status = 0;
+ u8 state;
+
+ if (unlikely (list_empty (&qh->qtd_list)))
+ return count;
+
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
+ /* remove de-activated QTDs from front of queue.
+ * after faults (including short reads), cleanup this urb
+ * then let the queue advance.
+ * if queue is stopped, handles unlinks.
+ */
+ list_for_each_safe (entry, tmp, &qh->qtd_list) {
+ struct ehci_qtd *qtd;
+ struct urb *urb;
+ u32 token = 0;
+
+ qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+ urb = qtd->urb;
+
+ /* clean up any state from previous QTD ...*/
+ if (last) {
+ if (likely (last->urb != urb)) {
+ ehci_urb_done (ehci, last->urb, regs);
+ count++;
+ }
+ ehci_qtd_free (ehci, last);
+ last = NULL;
+ }
+
+ /* ignore urbs submitted during completions we reported */
+ if (qtd == end)
+ break;
+
+ /* hardware copies qtd out of qh overlay */
+ rmb ();
+ token = le32_to_cpu (qtd->hw_token);
+
+ /* always clean up qtds the hc de-activated */
+ if ((token & QTD_STS_ACTIVE) == 0) {
+
+ if ((token & QTD_STS_HALT) != 0) {
+ stopped = 1;
+
+ /* magic dummy for some short reads; qh won't advance.
+ * that silicon quirk can kick in with this dummy too.
+ */
+ } else if (IS_SHORT_READ (token)
+ && !(qtd->hw_alt_next & EHCI_LIST_END)) {
+ stopped = 1;
+ goto halt;
+ }
+
+ /* stop scanning when we reach qtds the hc is using */
+ } else if (likely (!stopped
+ && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) {
+ break;
+
+ } else {
+ stopped = 1;
+
+ if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)))
+ urb->status = -ESHUTDOWN;
+
+ /* ignore active urbs unless some previous qtd
+ * for the urb faulted (including short read) or
+ * its urb was canceled. we may patch qh or qtds.
+ */
+ if (likely (urb->status == -EINPROGRESS))
+ continue;
+
+ /* issue status after short control reads */
+ if (unlikely (do_status != 0)
+ && QTD_PID (token) == 0 /* OUT */) {
+ do_status = 0;
+ continue;
+ }
+
+ /* token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_le32 (qtd->qtd_dma)
+ == qh->hw_current)
+ token = le32_to_cpu (qh->hw_token);
+
+ /* force halt for unlinked or blocked qh, so we'll
+ * patch the qh later and so that completions can't
+ * activate it while we "know" it's stopped.
+ */
+ if ((HALT_BIT & qh->hw_token) == 0) {
+halt:
+ qh->hw_token |= HALT_BIT;
+ wmb ();
+ }
+ }
+
+ /* remove it from the queue */
+ spin_lock (&urb->lock);
+ qtd_copy_status (ehci, urb, qtd->length, token);
+ do_status = (urb->status == -EREMOTEIO)
+ && usb_pipecontrol (urb->pipe);
+ spin_unlock (&urb->lock);
+
+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+ last = list_entry (qtd->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ last->hw_next = qtd->hw_next;
+ }
+ list_del (&qtd->qtd_list);
+ last = qtd;
+ }
+
+ /* last urb's completion might still need calling */
+ if (likely (last != NULL)) {
+ ehci_urb_done (ehci, last->urb, regs);
+ count++;
+ ehci_qtd_free (ehci, last);
+ }
+
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
+ /* be sure the hardware's done with the qh before refreshing
+ * it after fault cleanup, or recovering from silicon wrongly
+ * overlaying the dummy qtd (which reduces DMA chatter).
+ */
+ if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) {
+ switch (state) {
+ case QH_STATE_IDLE:
+ qh_refresh(ehci, qh);
+ break;
+ case QH_STATE_LINKED:
+ /* should be rare for periodic transfers,
+ * except maybe high bandwidth ...
+ */
+ if (qh->period) {
+ intr_deschedule (ehci, qh);
+ (void) qh_schedule (ehci, qh);
+ } else
+ unlink_async (ehci, qh);
+ break;
+ /* otherwise, unlink already started */
+ }
+ }
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+// ... and packet size, for any kind of endpoint descriptor
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list
+) {
+ struct list_head *entry, *temp;
+
+ list_for_each_safe (entry, temp, qtd_list) {
+ struct ehci_qtd *qtd;
+
+ qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+ list_del (&qtd->qtd_list);
+ ehci_qtd_free (ehci, qtd);
+ }
+}
+
+/*
+ * 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;
+ int len, maxpacket;
+ int is_input;
+ u32 token;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ return NULL;
+ 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 */
+
+ len = urb->transfer_buffer_length;
+ is_input = usb_pipein (urb->pipe);
+ if (usb_pipecontrol (urb->pipe)) {
+ /* SETUP pid */
+ qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 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
+ */
+ if (likely (len > 0))
+ buf = urb->transfer_dma;
+ else
+ buf = 0;
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (!buf || is_input)
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+ /*
+ * 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;
+
+ this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+ if (is_input)
+ qtd->hw_alt_next = ehci->async->hw_alt_next;
+
+ /* 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);
+ }
+
+ /* unless the bulk/interrupt caller wants a chance to clean
+ * up after short reads, hc should advance qh past this urb
+ */
+ if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol (urb->pipe)))
+ qtd->hw_alt_next = EHCI_LIST_END;
+
+ /*
+ * 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 & URB_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, 0);
+ }
+ }
+
+ /* 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:
+ qtd_list_free (ehci, urb, head);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// 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.7?)
+
+
+/*
+ * 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 *
+qh_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ int flags
+) {
+ struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint (urb->pipe) << 8;
+ info1 |= usb_pipedevice (urb->pipe) << 0;
+
+ is_input = usb_pipein (urb->pipe);
+ type = usb_pipetype (urb->pipe);
+ maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
+ hb_mult (maxp) * max_packet (maxp));
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+
+ qh->period = urb->interval >> 3;
+ if (qh->period == 0 && urb->interval != 1) {
+ /* NOTE interval 2 or 4 uframes could work.
+ * But interval 1 scheduling is simpler, and
+ * includes high bandwidth.
+ */
+ dbg ("intr period %d uframes, NYET!",
+ urb->interval);
+ goto done;
+ }
+ } else {
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) { // SPLIT, gap, CSPLIT+DATA
+ qh->c_usecs = qh->usecs + HS_USECS (0);
+ qh->usecs = HS_USECS (1);
+ } else { // SPLIT+DATA, gap, CSPLIT
+ qh->usecs += HS_USECS (1);
+ qh->c_usecs = HS_USECS (0);
+ }
+
+ qh->period = urb->interval;
+ }
+ }
+
+ /* support for tt scheduling, and access to toggles */
+ qh->dev = usb_get_dev (urb->dev);
+
+ /* 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" */
+ if (type != PIPE_INTERRUPT)
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (type == PIPE_CONTROL) {
+ info1 |= (1 << 27); /* for TT */
+ info1 |= 1 << 14; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (EHCI_TUNE_MULT_TT << 30);
+ info2 |= urb->dev->ttport << 23;
+
+ /* set the address of the TT; for TDI's integrated
+ * root hub tt, leave it zeroed.
+ */
+ if (!ehci_is_TDI(ehci)
+ || urb->dev->tt->hub !=
+ ehci_to_hcd(ehci)->self.root_hub)
+ info2 |= urb->dev->tt->hub->devnum << 16;
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= (2 << 12); /* EPS "high" */
+ if (type == PIPE_CONTROL) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= 1 << 14; /* toggle from qtd */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 512 << 16; /* usb2 fixed maxpacket */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet (maxp) << 16;
+ info2 |= hb_mult (maxp) << 30;
+ }
+ break;
+ default:
+ dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+done:
+ qh_put (qh);
+ return NULL;
+ }
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as live, toggle clear, advance to dummy */
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_info1 = cpu_to_le32 (info1);
+ qh->hw_info2 = cpu_to_le32 (info2);
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
+ qh_refresh (ehci, 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)
+{
+ __le32 dma = QH_NEXT (qh->qh_dma);
+ struct ehci_qh *head;
+
+ /* (re)start the async schedule? */
+ head = ehci->async;
+ timer_action_done (ehci, TIMER_ASYNC_OFF);
+ if (!head->qh_next.qh) {
+ u32 cmd = readl (&ehci->regs->command);
+
+ if (!(cmd & CMD_ASE)) {
+ /* in case a clear of CMD_ASE didn't take yet */
+ (void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
+ cmd |= CMD_ASE | CMD_RUN;
+ writel (cmd, &ehci->regs->command);
+ ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
+ /* posted write need not be known to HC yet ... */
+ }
+ }
+
+ /* clear halt and/or toggle; and maybe recover from silicon quirk */
+ if (qh->qh_state == QH_STATE_IDLE)
+ qh_refresh (ehci, qh);
+
+ /* splice right after start */
+ qh->qh_next = head->qh_next;
+ qh->hw_next = head->hw_next;
+ wmb ();
+
+ head->qh_next.qh = qh;
+ head->hw_next = dma;
+
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define QH_ADDR_MASK __constant_cpu_to_le32(0x7f)
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct ehci_qh *qh_append_tds (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int epnum,
+ void **ptr
+)
+{
+ struct ehci_qh *qh = NULL;
+
+ qh = (struct ehci_qh *) *ptr;
+ if (unlikely (qh == NULL)) {
+ /* can't sleep here, we have ehci->lock... */
+ qh = qh_make (ehci, urb, GFP_ATOMIC);
+ *ptr = qh;
+ }
+ if (likely (qh != NULL)) {
+ struct ehci_qtd *qtd;
+
+ if (unlikely (list_empty (qtd_list)))
+ qtd = NULL;
+ else
+ qtd = list_entry (qtd_list->next, struct ehci_qtd,
+ qtd_list);
+
+ /* control qh may need patching ... */
+ if (unlikely (epnum == 0)) {
+
+ /* usb_reset_device() briefly reverts to address 0 */
+ if (usb_pipedevice (urb->pipe) == 0)
+ qh->hw_info1 &= ~QH_ADDR_MASK;
+ }
+
+ /* just one way to queue requests: swap with the dummy qtd.
+ * only hc or qh_refresh() ever modify the overlay.
+ */
+ if (likely (qtd != NULL)) {
+ struct ehci_qtd *dummy;
+ dma_addr_t dma;
+ __le32 token;
+
+ /* to avoid racing the HC, use the dummy td instead of
+ * the first td of our list (becomes new dummy). both
+ * tds stay deactivated until we're done, when the
+ * HC is allowed to fetch the old dummy (4.10.2).
+ */
+ token = qtd->hw_token;
+ qtd->hw_token = HALT_BIT;
+ wmb ();
+ dummy = qh->dummy;
+
+ dma = dummy->qtd_dma;
+ *dummy = *qtd;
+ dummy->qtd_dma = dma;
+
+ list_del (&qtd->qtd_list);
+ list_add (&dummy->qtd_list, qtd_list);
+ __list_splice (qtd_list, qh->qtd_list.prev);
+
+ ehci_qtd_init (qtd, qtd->qtd_dma);
+ qh->dummy = qtd;
+
+ /* hc must see the new dummy at list end */
+ dma = qtd->qtd_dma;
+ qtd = list_entry (qh->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ qtd->hw_next = QTD_NEXT (dma);
+
+ /* let the hc process these next qtds */
+ wmb ();
+ dummy->hw_token = token;
+
+ urb->hcpriv = qh_get (qh);
+ }
+ }
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async (
+ struct ehci_hcd *ehci,
+ struct usb_host_endpoint *ep,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int mem_flags
+) {
+ struct ehci_qtd *qtd;
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = NULL;
+
+ qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
+ epnum = ep->desc.bEndpointAddress;
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __FUNCTION__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd, ep->hcpriv);
+#endif
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv);
+
+ /* 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 != NULL)) {
+ if (likely (qh->qh_state == QH_STATE_IDLE))
+ qh_link_async (ehci, qh_get (qh));
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (unlikely (qh == NULL)) {
+ qtd_list_free (ehci, urb, qtd_list);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the async qh for the qtds being reclaimed are now unlinked from the HC */
+
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+ struct ehci_qh *qh = ehci->reclaim;
+ struct ehci_qh *next;
+
+ timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+
+ // qh->hw_next = cpu_to_le32 (qh->qh_dma);
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = NULL;
+ qh_put (qh); // refcount from reclaim
+
+ /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
+ next = qh->reclaim;
+ ehci->reclaim = next;
+ ehci->reclaim_ready = 0;
+ qh->reclaim = NULL;
+
+ qh_completions (ehci, qh, regs);
+
+ if (!list_empty (&qh->qtd_list)
+ && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ qh_link_async (ehci, qh);
+ else {
+ qh_put (qh); // refcount from async list
+
+ /* it's not free to turn the async schedule on/off; leave it
+ * active but idle for a while once it empties.
+ */
+ if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)
+ && ehci->async->qh_next.qh == NULL)
+ timer_action (ehci, TIMER_ASYNC_OFF);
+ }
+
+ if (next) {
+ ehci->reclaim = NULL;
+ start_unlink_async (ehci, next);
+ }
+}
+
+/* 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
+ assert_spin_locked(&ehci->lock);
+ if (ehci->reclaim
+ || (qh->qh_state != QH_STATE_LINKED
+ && qh->qh_state != QH_STATE_UNLINK_WAIT)
+ )
+ BUG ();
+#endif
+
+ /* stop async schedule right now? */
+ if (unlikely (qh == ehci->async)) {
+ /* can't get here without STS_ASS set */
+ if (ehci_to_hcd(ehci)->state != HC_STATE_HALT) {
+ writel (cmd & ~CMD_ASE, &ehci->regs->command);
+ wmb ();
+ // handshake later, if we need to
+ }
+ timer_action_done (ehci, TIMER_ASYNC_OFF);
+ return;
+ }
+
+ qh->qh_state = QH_STATE_UNLINK;
+ ehci->reclaim = qh = qh_get (qh);
+
+ prev = ehci->async;
+ while (prev->qh_next.qh != qh)
+ prev = prev->qh_next.qh;
+
+ prev->hw_next = qh->hw_next;
+ prev->qh_next = qh->qh_next;
+ wmb ();
+
+ if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
+ /* if (unlikely (qh->reclaim != 0))
+ * this will recurse, probably not much
+ */
+ end_unlink_async (ehci, NULL);
+ return;
+ }
+
+ ehci->reclaim_ready = 0;
+ cmd |= CMD_IAAD;
+ writel (cmd, &ehci->regs->command);
+ (void) readl (&ehci->regs->command);
+ timer_action (ehci, TIMER_IAA_WATCHDOG);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+ struct ehci_qh *qh;
+ enum ehci_timer_action action = TIMER_IO_WATCHDOG;
+
+ if (!++(ehci->stamp))
+ ehci->stamp++;
+ timer_action_done (ehci, TIMER_ASYNC_SHRINK);
+rescan:
+ qh = ehci->async->qh_next.qh;
+ if (likely (qh != NULL)) {
+ do {
+ /* clean any finished work for this qh */
+ if (!list_empty (&qh->qtd_list)
+ && qh->stamp != ehci->stamp) {
+ int temp;
+
+ /* unlinks could happen here; completion
+ * reporting drops the lock. rescan using
+ * the latest schedule, but don't rescan
+ * qhs we already finished (no looping).
+ */
+ qh = qh_get (qh);
+ qh->stamp = ehci->stamp;
+ temp = qh_completions (ehci, qh, regs);
+ qh_put (qh);
+ if (temp != 0) {
+ goto rescan;
+ }
+ }
+
+ /* unlink idle entries, reducing HC PCI usage as well
+ * as HCD schedule-scanning costs. delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ * (plus, avoids some kind of re-activation race.)
+ */
+ if (list_empty (&qh->qtd_list)) {
+ if (qh->stamp == ehci->stamp)
+ action = TIMER_ASYNC_SHRINK;
+ else if (!ehci->reclaim
+ && qh->qh_state == QH_STATE_LINKED)
+ start_unlink_async (ehci, qh);
+ }
+
+ qh = qh->qh_next.qh;
+ } while (qh);
+ }
+ if (action == TIMER_ASYNC_SHRINK)
+ timer_action (ehci, TIMER_ASYNC_SHRINK);
+}
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
new file mode 100644
index 0000000..f6c8635
--- /dev/null
+++ b/drivers/usb/host/ehci-sched.c
@@ -0,0 +1,1999 @@
+/*
+ * Copyright (c) 2001-2004 by David Brownell
+ * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
+ *
+ * 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.
+ *
+ * Note that for interrupt transfers, the QH/QTD manipulation is shared
+ * with the "asynchronous" transaction support (control/bulk transfers).
+ * The only real difference is in how interrupt transfers are scheduled.
+ *
+ * For ISO, we make an "iso_stream" head to serve the same role as a QH.
+ * It keeps track of every ITD (or SITD) that's linked, and holds enough
+ * pre-calculated schedule data to make appending to the queue be quick.
+ */
+
+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, __le32 tag)
+{
+ switch (tag) {
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ case Q_TYPE_FSTN:
+ return &periodic->fstn->fstn_next;
+ case Q_TYPE_ITD:
+ return &periodic->itd->itd_next;
+ // case Q_TYPE_SITD:
+ default:
+ return &periodic->sitd->sitd_next;
+ }
+}
+
+/* caller must hold ehci->lock */
+static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
+{
+ union ehci_shadow *prev_p = &ehci->pshadow [frame];
+ __le32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow here = *prev_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.hw_next;
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr)
+ return;
+
+ /* update shadow and hardware lists ... the old "next" pointers
+ * from ptr may still be in use, the caller updates them.
+ */
+ *prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
+ *hw_p = *here.hw_next;
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short
+periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
+{
+ __le32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow *q = &ehci->pshadow [frame];
+ unsigned usecs = 0;
+
+ 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;
+ /* ... or C-mask? */
+ if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
+ usecs += q->qh->c_usecs;
+ hw_p = &q->qh->hw_next;
+ q = &q->qh->qh_next;
+ break;
+ // case Q_TYPE_FSTN:
+ default:
+ /* for "save place" FSTNs, count the relevant INTR
+ * bandwidth from the previous frame
+ */
+ if (q->fstn->hw_prev != EHCI_LIST_END) {
+ ehci_dbg (ehci, "ignoring FSTN cost ...\n");
+ }
+ hw_p = &q->fstn->hw_next;
+ q = &q->fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ usecs += q->itd->usecs [uframe];
+ hw_p = &q->itd->hw_next;
+ q = &q->itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ /* is it in the S-mask? (count SPLIT, DATA) */
+ if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
+ if (q->sitd->hw_fullspeed_ep &
+ __constant_cpu_to_le32 (1<<31))
+ usecs += q->sitd->stream->usecs;
+ else /* worst case for OUT start-split */
+ usecs += HS_USECS_ISO (188);
+ }
+
+ /* ... C-mask? (count CSPLIT, DATA) */
+ if (q->sitd->hw_uframe &
+ cpu_to_le32 (1 << (8 + uframe))) {
+ /* worst case for IN complete-split */
+ usecs += q->sitd->stream->c_usecs;
+ }
+
+ hw_p = &q->sitd->hw_next;
+ q = &q->sitd->sitd_next;
+ break;
+ }
+ }
+#ifdef DEBUG
+ if (usecs > 100)
+ ehci_err (ehci, "uframe %d sched overrun: %d usecs\n",
+ frame * 8 + uframe, usecs);
+#endif
+ return usecs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int same_tt (struct usb_device *dev1, struct usb_device *dev2)
+{
+ if (!dev1->tt || !dev2->tt)
+ return 0;
+ if (dev1->tt != dev2->tt)
+ return 0;
+ if (dev1->tt->multi)
+ return dev1->ttport == dev2->ttport;
+ else
+ return 1;
+}
+
+/* return true iff the device's transaction translator is available
+ * for a periodic transfer starting at the specified frame, using
+ * all the uframes in the mask.
+ */
+static int tt_no_collision (
+ struct ehci_hcd *ehci,
+ unsigned period,
+ struct usb_device *dev,
+ unsigned frame,
+ u32 uf_mask
+)
+{
+ if (period == 0) /* error */
+ return 0;
+
+ /* note bandwidth wastage: split never follows csplit
+ * (different dev or endpoint) until the next uframe.
+ * calling convention doesn't make that distinction.
+ */
+ for (; frame < ehci->periodic_size; frame += period) {
+ union ehci_shadow here;
+ __le32 type;
+
+ here = ehci->pshadow [frame];
+ type = Q_NEXT_TYPE (ehci->periodic [frame]);
+ while (here.ptr) {
+ switch (type) {
+ case Q_TYPE_ITD:
+ type = Q_NEXT_TYPE (here.itd->hw_next);
+ here = here.itd->itd_next;
+ continue;
+ case Q_TYPE_QH:
+ if (same_tt (dev, here.qh->dev)) {
+ u32 mask;
+
+ mask = le32_to_cpu (here.qh->hw_info2);
+ /* "knows" no gap is needed */
+ mask |= mask >> 8;
+ if (mask & uf_mask)
+ break;
+ }
+ type = Q_NEXT_TYPE (here.qh->hw_next);
+ here = here.qh->qh_next;
+ continue;
+ case Q_TYPE_SITD:
+ if (same_tt (dev, here.sitd->urb->dev)) {
+ u16 mask;
+
+ mask = le32_to_cpu (here.sitd
+ ->hw_uframe);
+ /* FIXME assumes no gap for IN! */
+ mask |= mask >> 8;
+ if (mask & uf_mask)
+ break;
+ }
+ type = Q_NEXT_TYPE (here.sitd->hw_next);
+ here = here.sitd->sitd_next;
+ continue;
+ // case Q_TYPE_FSTN:
+ default:
+ ehci_dbg (ehci,
+ "periodic frame %d bogus type %d\n",
+ frame, type);
+ }
+
+ /* collision or error */
+ return 0;
+ }
+ }
+
+ /* no collision */
+ return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int enable_periodic (struct ehci_hcd *ehci)
+{
+ u32 cmd;
+ int status;
+
+ /* did clearing PSE did take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125);
+ if (status != 0) {
+ ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ return status;
+ }
+
+ cmd = readl (&ehci->regs->command) | CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... PSS happens later */
+ ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
+
+ /* make sure ehci_work scans these */
+ ehci->next_uframe = readl (&ehci->regs->frame_index)
+ % (ehci->periodic_size << 3);
+ return 0;
+}
+
+static int disable_periodic (struct ehci_hcd *ehci)
+{
+ u32 cmd;
+ int status;
+
+ /* did setting PSE not take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125);
+ if (status != 0) {
+ ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ return status;
+ }
+
+ cmd = readl (&ehci->regs->command) & ~CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... */
+
+ ehci->next_uframe = -1;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* periodic schedule slots have iso tds (normal or split) first, then a
+ * sparse tree for active interrupt transfers.
+ *
+ * this just links in a qh; caller guarantees uframe masks are set right.
+ * no FSTN support (yet; ehci 0.96+)
+ */
+static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ unsigned i;
+ unsigned period = qh->period;
+
+ dev_dbg (&qh->dev->dev,
+ "link qh%d-%04x/%p start %d [%d/%d us]\n",
+ period, le32_to_cpup (&qh->hw_info2) & 0xffff,
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* high bandwidth, or otherwise every microframe */
+ if (period == 0)
+ period = 1;
+
+ for (i = qh->start; i < ehci->periodic_size; i += period) {
+ union ehci_shadow *prev = &ehci->pshadow [i];
+ u32 *hw_p = &ehci->periodic [i];
+ union ehci_shadow here = *prev;
+ u32 type = 0;
+
+ /* skip the iso nodes at list head */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE (*hw_p);
+ if (type == Q_TYPE_QH)
+ break;
+ prev = periodic_next_shadow (prev, type);
+ hw_p = &here.qh->hw_next;
+ here = *prev;
+ }
+
+ /* sorting each branch by period (slow-->fast)
+ * enables sharing interior tree nodes
+ */
+ while (here.ptr && qh != here.qh) {
+ if (qh->period > here.qh->period)
+ break;
+ prev = &here.qh->qh_next;
+ hw_p = &here.qh->hw_next;
+ here = *prev;
+ }
+ /* link in this qh, unless some earlier pass did that */
+ if (qh != here.qh) {
+ qh->qh_next = here;
+ if (here.qh)
+ qh->hw_next = *hw_p;
+ wmb ();
+ prev->qh = qh;
+ *hw_p = QH_NEXT (qh->qh_dma);
+ }
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ qh_get (qh);
+
+ /* update per-qh bandwidth for usbfs */
+ ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ /* maybe enable periodic schedule processing */
+ if (!ehci->periodic_sched++)
+ return enable_periodic (ehci);
+
+ return 0;
+}
+
+static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ unsigned i;
+ unsigned period;
+
+ // FIXME:
+ // IF this isn't high speed
+ // and this qh is active in the current uframe
+ // (and overlay token SplitXstate is false?)
+ // THEN
+ // qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */);
+
+ /* high bandwidth, or otherwise part of every microframe */
+ if ((period = qh->period) == 0)
+ period = 1;
+
+ for (i = qh->start; i < ehci->periodic_size; i += period)
+ periodic_unlink (ehci, i, qh);
+
+ /* update per-qh bandwidth for usbfs */
+ ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period
+ ? ((qh->usecs + qh->c_usecs) / qh->period)
+ : (qh->usecs * 8);
+
+ dev_dbg (&qh->dev->dev,
+ "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
+ qh->period, le32_to_cpup (&qh->hw_info2) & 0xffff,
+ qh, qh->start, qh->usecs, qh->c_usecs);
+
+ /* qh->qh_next still "live" to HC */
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = NULL;
+ qh_put (qh);
+
+ /* maybe turn off periodic schedule */
+ ehci->periodic_sched--;
+ if (!ehci->periodic_sched)
+ (void) disable_periodic (ehci);
+}
+
+static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ unsigned wait;
+
+ qh_unlink_periodic (ehci, qh);
+
+ /* simple/paranoid: always delay, expecting the HC needs to read
+ * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and
+ * expect khubd to clean up after any CSPLITs we won't issue.
+ * active high speed queues may need bigger delays...
+ */
+ if (list_empty (&qh->qtd_list)
+ || (__constant_cpu_to_le32 (0x0ff << 8)
+ & qh->hw_info2) != 0)
+ wait = 2;
+ else
+ wait = 55; /* worst case: 3 * 1024 */
+
+ udelay (wait);
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_next = EHCI_LIST_END;
+ wmb ();
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int check_period (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ unsigned uframe,
+ unsigned period,
+ unsigned usecs
+) {
+ int claimed;
+
+ /* complete split running into next frame?
+ * given FSTN support, we could sometimes check...
+ */
+ if (uframe >= 8)
+ return 0;
+
+ /*
+ * 80% periodic == 100 usec/uframe available
+ * convert "usecs we need" to "max already claimed"
+ */
+ usecs = 100 - usecs;
+
+ /* we "know" 2 and 4 uframe intervals were rejected; so
+ * for period 0, check _every_ microframe in the schedule.
+ */
+ if (unlikely (period == 0)) {
+ do {
+ for (uframe = 0; uframe < 7; uframe++) {
+ claimed = periodic_usecs (ehci, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ }
+ } while ((frame += 1) < ehci->periodic_size);
+
+ /* just check the specified uframe, at that period */
+ } else {
+ do {
+ claimed = periodic_usecs (ehci, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+ } while ((frame += period) < ehci->periodic_size);
+ }
+
+ // success!
+ return 1;
+}
+
+static int check_intr_schedule (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ unsigned uframe,
+ const struct ehci_qh *qh,
+ __le32 *c_maskp
+)
+{
+ int retval = -ENOSPC;
+ u8 mask;
+
+ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
+ goto done;
+
+ if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
+ goto done;
+ if (!qh->c_usecs) {
+ retval = 0;
+ *c_maskp = 0;
+ goto done;
+ }
+
+ /* Make sure this tt's buffer is also available for CSPLITs.
+ * We pessimize a bit; probably the typical full speed case
+ * doesn't need the second CSPLIT.
+ *
+ * NOTE: both SPLIT and CSPLIT could be checked in just
+ * one smart pass...
+ */
+ mask = 0x03 << (uframe + qh->gap_uf);
+ *c_maskp = cpu_to_le32 (mask << 8);
+
+ mask |= 1 << uframe;
+ if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
+ if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
+ qh->period, qh->c_usecs))
+ goto done;
+ if (!check_period (ehci, frame, uframe + qh->gap_uf,
+ qh->period, qh->c_usecs))
+ goto done;
+ retval = 0;
+ }
+done:
+ return retval;
+}
+
+/* "first fit" scheduling policy used the first time through,
+ * or when the previous schedule slot can't be re-used.
+ */
+static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int status;
+ unsigned uframe;
+ __le32 c_mask;
+ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
+
+ qh_refresh(ehci, qh);
+ qh->hw_next = EHCI_LIST_END;
+ frame = qh->start;
+
+ /* reuse the previous schedule slots, if we can */
+ if (frame < qh->period) {
+ uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
+ status = check_intr_schedule (ehci, frame, --uframe,
+ qh, &c_mask);
+ } else {
+ uframe = 0;
+ c_mask = 0;
+ status = -ENOSPC;
+ }
+
+ /* else scan the schedule to find a group of slots such that all
+ * uframes have enough periodic bandwidth available.
+ */
+ if (status) {
+ /* "normal" case, uframing flexible except with splits */
+ if (qh->period) {
+ frame = qh->period - 1;
+ do {
+ for (uframe = 0; uframe < 8; uframe++) {
+ status = check_intr_schedule (ehci,
+ frame, uframe, qh,
+ &c_mask);
+ if (status == 0)
+ break;
+ }
+ } while (status && frame--);
+
+ /* qh->period == 0 means every uframe */
+ } else {
+ frame = 0;
+ status = check_intr_schedule (ehci, 0, 0, qh, &c_mask);
+ }
+ if (status)
+ goto done;
+ qh->start = frame;
+
+ /* reset S-frame and (maybe) C-frame masks */
+ qh->hw_info2 &= __constant_cpu_to_le32 (~0xffff);
+ qh->hw_info2 |= qh->period
+ ? cpu_to_le32 (1 << uframe)
+ : __constant_cpu_to_le32 (0xff);
+ qh->hw_info2 |= c_mask;
+ } else
+ ehci_dbg (ehci, "reused qh %p schedule\n", qh);
+
+ /* stuff into the periodic schedule */
+ status = qh_link_periodic (ehci, qh);
+done:
+ return status;
+}
+
+static int intr_submit (
+ struct ehci_hcd *ehci,
+ struct usb_host_endpoint *ep,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int mem_flags
+) {
+ unsigned epnum;
+ unsigned long flags;
+ struct ehci_qh *qh;
+ int status = 0;
+ struct list_head empty;
+
+ /* get endpoint and transfer/schedule data */
+ epnum = ep->desc.bEndpointAddress;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* get qh and force any scheduling errors */
+ INIT_LIST_HEAD (&empty);
+ qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv);
+ if (qh == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+ if (qh->qh_state == QH_STATE_IDLE) {
+ if ((status = qh_schedule (ehci, qh)) != 0)
+ goto done;
+ }
+
+ /* then queue the urb's tds to the qh */
+ qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv);
+ BUG_ON (qh == NULL);
+
+ /* ... update usbfs periodic stats */
+ ehci_to_hcd(ehci)->self.bandwidth_int_reqs++;
+
+done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (status)
+ qtd_list_free (ehci, urb, qtd_list);
+
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ehci_iso_stream ops work with both ITD and SITD */
+
+static struct ehci_iso_stream *
+iso_stream_alloc (int mem_flags)
+{
+ struct ehci_iso_stream *stream;
+
+ stream = kmalloc(sizeof *stream, mem_flags);
+ if (likely (stream != NULL)) {
+ memset (stream, 0, sizeof(*stream));
+ INIT_LIST_HEAD(&stream->td_list);
+ INIT_LIST_HEAD(&stream->free_list);
+ stream->next_uframe = -1;
+ stream->refcount = 1;
+ }
+ return stream;
+}
+
+static void
+iso_stream_init (
+ struct ehci_hcd *ehci,
+ struct ehci_iso_stream *stream,
+ struct usb_device *dev,
+ int pipe,
+ unsigned interval
+)
+{
+ static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
+
+ u32 buf1;
+ unsigned epnum, maxp;
+ int is_input;
+ long bandwidth;
+
+ /*
+ * this might be a "high bandwidth" highspeed endpoint,
+ * as encoded in the ep descriptor's wMaxPacket field
+ */
+ epnum = usb_pipeendpoint (pipe);
+ is_input = usb_pipein (pipe) ? USB_DIR_IN : 0;
+ maxp = usb_maxpacket(dev, pipe, !is_input);
+ if (is_input) {
+ buf1 = (1 << 11);
+ } else {
+ buf1 = 0;
+ }
+
+ /* knows about ITD vs SITD */
+ if (dev->speed == USB_SPEED_HIGH) {
+ unsigned multi = hb_mult(maxp);
+
+ stream->highspeed = 1;
+
+ maxp = max_packet(maxp);
+ buf1 |= maxp;
+ maxp *= multi;
+
+ stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum);
+ stream->buf1 = cpu_to_le32 (buf1);
+ stream->buf2 = cpu_to_le32 (multi);
+
+ /* usbfs wants to report the average usecs per frame tied up
+ * when transfers on this endpoint are scheduled ...
+ */
+ stream->usecs = HS_USECS_ISO (maxp);
+ bandwidth = stream->usecs * 8;
+ bandwidth /= 1 << (interval - 1);
+
+ } else {
+ u32 addr;
+
+ addr = dev->ttport << 24;
+ if (!ehci_is_TDI(ehci)
+ || (dev->tt->hub !=
+ ehci_to_hcd(ehci)->self.root_hub))
+ addr |= dev->tt->hub->devnum << 16;
+ addr |= epnum << 8;
+ addr |= dev->devnum;
+ stream->usecs = HS_USECS_ISO (maxp);
+ if (is_input) {
+ u32 tmp;
+
+ addr |= 1 << 31;
+ stream->c_usecs = stream->usecs;
+ stream->usecs = HS_USECS_ISO (1);
+ stream->raw_mask = 1;
+
+ /* pessimistic c-mask */
+ tmp = usb_calc_bus_time (USB_SPEED_FULL, 1, 0, maxp)
+ / (125 * 1000);
+ stream->raw_mask |= 3 << (tmp + 9);
+ } else
+ stream->raw_mask = smask_out [maxp / 188];
+ bandwidth = stream->usecs + stream->c_usecs;
+ bandwidth /= 1 << (interval + 2);
+
+ /* stream->splits gets created from raw_mask later */
+ stream->address = cpu_to_le32 (addr);
+ }
+ stream->bandwidth = bandwidth;
+
+ stream->udev = dev;
+
+ stream->bEndpointAddress = is_input | epnum;
+ stream->interval = interval;
+ stream->maxp = maxp;
+}
+
+static void
+iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
+{
+ stream->refcount--;
+
+ /* free whenever just a dev->ep reference remains.
+ * not like a QH -- no persistent state (toggle, halt)
+ */
+ if (stream->refcount == 1) {
+ int is_in;
+
+ // BUG_ON (!list_empty(&stream->td_list));
+
+ while (!list_empty (&stream->free_list)) {
+ struct list_head *entry;
+
+ entry = stream->free_list.next;
+ list_del (entry);
+
+ /* knows about ITD vs SITD */
+ if (stream->highspeed) {
+ struct ehci_itd *itd;
+
+ itd = list_entry (entry, struct ehci_itd,
+ itd_list);
+ dma_pool_free (ehci->itd_pool, itd,
+ itd->itd_dma);
+ } else {
+ struct ehci_sitd *sitd;
+
+ sitd = list_entry (entry, struct ehci_sitd,
+ sitd_list);
+ dma_pool_free (ehci->sitd_pool, sitd,
+ sitd->sitd_dma);
+ }
+ }
+
+ is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
+ stream->bEndpointAddress &= 0x0f;
+ stream->ep->hcpriv = NULL;
+
+ if (stream->rescheduled) {
+ ehci_info (ehci, "ep%d%s-iso rescheduled "
+ "%lu times in %lu seconds\n",
+ stream->bEndpointAddress, is_in ? "in" : "out",
+ stream->rescheduled,
+ ((jiffies - stream->start)/HZ)
+ );
+ }
+
+ kfree(stream);
+ }
+}
+
+static inline struct ehci_iso_stream *
+iso_stream_get (struct ehci_iso_stream *stream)
+{
+ if (likely (stream != NULL))
+ stream->refcount++;
+ return stream;
+}
+
+static struct ehci_iso_stream *
+iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
+{
+ unsigned epnum;
+ struct ehci_iso_stream *stream;
+ struct usb_host_endpoint *ep;
+ unsigned long flags;
+
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein(urb->pipe))
+ ep = urb->dev->ep_in[epnum];
+ else
+ ep = urb->dev->ep_out[epnum];
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ stream = ep->hcpriv;
+
+ if (unlikely (stream == NULL)) {
+ stream = iso_stream_alloc(GFP_ATOMIC);
+ if (likely (stream != NULL)) {
+ /* dev->ep owns the initial refcount */
+ ep->hcpriv = stream;
+ stream->ep = ep;
+ iso_stream_init(ehci, stream, urb->dev, urb->pipe,
+ urb->interval);
+ }
+
+ /* if dev->ep [epnum] is a QH, info1.maxpacket is nonzero */
+ } else if (unlikely (stream->hw_info1 != 0)) {
+ ehci_dbg (ehci, "dev %s ep%d%s, not iso??\n",
+ urb->dev->devpath, epnum,
+ usb_pipein(urb->pipe) ? "in" : "out");
+ stream = NULL;
+ }
+
+ /* caller guarantees an eventual matching iso_stream_put */
+ stream = iso_stream_get (stream);
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return stream;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ehci_iso_sched ops can be ITD-only or SITD-only */
+
+static struct ehci_iso_sched *
+iso_sched_alloc (unsigned packets, int mem_flags)
+{
+ struct ehci_iso_sched *iso_sched;
+ int size = sizeof *iso_sched;
+
+ size += packets * sizeof (struct ehci_iso_packet);
+ iso_sched = kmalloc (size, mem_flags);
+ if (likely (iso_sched != NULL)) {
+ memset(iso_sched, 0, size);
+ INIT_LIST_HEAD (&iso_sched->td_list);
+ }
+ return iso_sched;
+}
+
+static inline void
+itd_sched_init (
+ struct ehci_iso_sched *iso_sched,
+ struct ehci_iso_stream *stream,
+ struct urb *urb
+)
+{
+ unsigned i;
+ dma_addr_t dma = urb->transfer_dma;
+
+ /* how many uframes are needed for these transfers */
+ iso_sched->span = urb->number_of_packets * stream->interval;
+
+ /* figure out per-uframe itd fields that we'll need later
+ * when we fit new itds into the schedule.
+ */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct ehci_iso_packet *uframe = &iso_sched->packet [i];
+ unsigned length;
+ dma_addr_t buf;
+ u32 trans;
+
+ length = urb->iso_frame_desc [i].length;
+ buf = dma + urb->iso_frame_desc [i].offset;
+
+ trans = EHCI_ISOC_ACTIVE;
+ trans |= buf & 0x0fff;
+ if (unlikely (((i + 1) == urb->number_of_packets))
+ && !(urb->transfer_flags & URB_NO_INTERRUPT))
+ trans |= EHCI_ITD_IOC;
+ trans |= length << 16;
+ uframe->transaction = cpu_to_le32 (trans);
+
+ /* might need to cross a buffer page within a td */
+ uframe->bufp = (buf & ~(u64)0x0fff);
+ buf += length;
+ if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff))))
+ uframe->cross = 1;
+ }
+}
+
+static void
+iso_sched_free (
+ struct ehci_iso_stream *stream,
+ struct ehci_iso_sched *iso_sched
+)
+{
+ if (!iso_sched)
+ return;
+ // caller must hold ehci->lock!
+ list_splice (&iso_sched->td_list, &stream->free_list);
+ kfree (iso_sched);
+}
+
+static int
+itd_urb_transaction (
+ struct ehci_iso_stream *stream,
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ int mem_flags
+)
+{
+ struct ehci_itd *itd;
+ dma_addr_t itd_dma;
+ int i;
+ unsigned num_itds;
+ struct ehci_iso_sched *sched;
+ unsigned long flags;
+
+ sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+ if (unlikely (sched == NULL))
+ return -ENOMEM;
+
+ itd_sched_init (sched, stream, urb);
+
+ if (urb->interval < 8)
+ num_itds = 1 + (sched->span + 7) / 8;
+ else
+ num_itds = urb->number_of_packets;
+
+ /* allocate/init ITDs */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < num_itds; i++) {
+
+ /* free_list.next might be cache-hot ... but maybe
+ * the HC caches it too. avoid that issue for now.
+ */
+
+ /* prefer previously-allocated itds */
+ if (likely (!list_empty(&stream->free_list))) {
+ itd = list_entry (stream->free_list.prev,
+ struct ehci_itd, itd_list);
+ list_del (&itd->itd_list);
+ itd_dma = itd->itd_dma;
+ } else
+ itd = NULL;
+
+ if (!itd) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
+ &itd_dma);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
+
+ if (unlikely (NULL == itd)) {
+ iso_sched_free (stream, sched);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return -ENOMEM;
+ }
+ memset (itd, 0, sizeof *itd);
+ itd->itd_dma = itd_dma;
+ list_add (&itd->itd_list, &sched->td_list);
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ /* temporarily store schedule info in hcpriv */
+ urb->hcpriv = sched;
+ urb->error_count = 0;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+itd_slot_ok (
+ struct ehci_hcd *ehci,
+ u32 mod,
+ u32 uframe,
+ u8 usecs,
+ u32 period
+)
+{
+ uframe %= period;
+ do {
+ /* can't commit more than 80% periodic == 100 usec */
+ if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
+ > (100 - usecs))
+ return 0;
+
+ /* we know urb->interval is 2^N uframes */
+ uframe += period;
+ } while (uframe < mod);
+ return 1;
+}
+
+static inline int
+sitd_slot_ok (
+ struct ehci_hcd *ehci,
+ u32 mod,
+ struct ehci_iso_stream *stream,
+ u32 uframe,
+ struct ehci_iso_sched *sched,
+ u32 period_uframes
+)
+{
+ u32 mask, tmp;
+ u32 frame, uf;
+
+ mask = stream->raw_mask << (uframe & 7);
+
+ /* for IN, don't wrap CSPLIT into the next frame */
+ if (mask & ~0xffff)
+ return 0;
+
+ /* this multi-pass logic is simple, but performance may
+ * suffer when the schedule data isn't cached.
+ */
+
+ /* check bandwidth */
+ uframe %= period_uframes;
+ do {
+ u32 max_used;
+
+ frame = uframe >> 3;
+ uf = uframe & 7;
+
+ /* tt must be idle for start(s), any gap, and csplit.
+ * assume scheduling slop leaves 10+% for control/bulk.
+ */
+ if (!tt_no_collision (ehci, period_uframes << 3,
+ stream->udev, frame, mask))
+ return 0;
+
+ /* check starts (OUT uses more than one) */
+ max_used = 100 - stream->usecs;
+ for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) {
+ if (periodic_usecs (ehci, frame, uf) > max_used)
+ return 0;
+ }
+
+ /* for IN, check CSPLIT */
+ if (stream->c_usecs) {
+ max_used = 100 - stream->c_usecs;
+ do {
+ tmp = 1 << uf;
+ tmp <<= 8;
+ if ((stream->raw_mask & tmp) == 0)
+ continue;
+ if (periodic_usecs (ehci, frame, uf)
+ > max_used)
+ return 0;
+ } while (++uf < 8);
+ }
+
+ /* we know urb->interval is 2^N uframes */
+ uframe += period_uframes;
+ } while (uframe < mod);
+
+ stream->splits = cpu_to_le32(stream->raw_mask << (uframe & 7));
+ return 1;
+}
+
+/*
+ * This scheduler plans almost as far into the future as it has actual
+ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to
+ * "as small as possible" to be cache-friendlier.) That limits the size
+ * transfers you can stream reliably; avoid more than 64 msec per urb.
+ * Also avoid queue depths of less than ehci's worst irq latency (affected
+ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter,
+ * and other factors); or more than about 230 msec total (for portability,
+ * given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
+ */
+
+#define SCHEDULE_SLOP 10 /* frames */
+
+static int
+iso_stream_schedule (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct ehci_iso_stream *stream
+)
+{
+ u32 now, start, max, period;
+ int status;
+ unsigned mod = ehci->periodic_size << 3;
+ struct ehci_iso_sched *sched = urb->hcpriv;
+
+ if (sched->span > (mod - 8 * SCHEDULE_SLOP)) {
+ ehci_dbg (ehci, "iso request %p too long\n", urb);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ if ((stream->depth + sched->span) > mod) {
+ ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n",
+ urb, stream->depth, sched->span, mod);
+ status = -EFBIG;
+ goto fail;
+ }
+
+ now = readl (&ehci->regs->frame_index) % mod;
+
+ /* when's the last uframe this urb could start? */
+ max = now + mod;
+
+ /* typical case: reuse current schedule. stream is still active,
+ * and no gaps from host falling behind (irq delays etc)
+ */
+ if (likely (!list_empty (&stream->td_list))) {
+ start = stream->next_uframe;
+ if (start < now)
+ start += mod;
+ if (likely ((start + sched->span) < max))
+ goto ready;
+ /* else fell behind; someday, try to reschedule */
+ status = -EL2NSYNC;
+ goto fail;
+ }
+
+ /* need to schedule; when's the next (u)frame we could start?
+ * this is bigger than ehci->i_thresh allows; scheduling itself
+ * isn't free, the slop should handle reasonably slow cpus. it
+ * can also help high bandwidth if the dma and irq loads don't
+ * jump until after the queue is primed.
+ */
+ start = SCHEDULE_SLOP * 8 + (now & ~0x07);
+ start %= mod;
+ stream->next_uframe = start;
+
+ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
+
+ period = urb->interval;
+ if (!stream->highspeed)
+ period <<= 3;
+
+ /* find a uframe slot with enough bandwidth */
+ for (; start < (stream->next_uframe + period); start++) {
+ int enough_space;
+
+ /* check schedule: enough space? */
+ if (stream->highspeed)
+ enough_space = itd_slot_ok (ehci, mod, start,
+ stream->usecs, period);
+ else {
+ if ((start % 8) >= 6)
+ continue;
+ enough_space = sitd_slot_ok (ehci, mod, stream,
+ start, sched, period);
+ }
+
+ /* schedule it here if there's enough bandwidth */
+ if (enough_space) {
+ stream->next_uframe = start % mod;
+ goto ready;
+ }
+ }
+
+ /* no room in the schedule */
+ ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
+ list_empty (&stream->td_list) ? "" : "re",
+ urb, now, max);
+ status = -ENOSPC;
+
+fail:
+ iso_sched_free (stream, sched);
+ urb->hcpriv = NULL;
+ return status;
+
+ready:
+ /* report high speed start in uframes; full speed, in frames */
+ urb->start_frame = stream->next_uframe;
+ if (!stream->highspeed)
+ urb->start_frame >>= 3;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
+{
+ int i;
+
+ itd->hw_next = EHCI_LIST_END;
+ itd->hw_bufp [0] = stream->buf0;
+ itd->hw_bufp [1] = stream->buf1;
+ itd->hw_bufp [2] = stream->buf2;
+
+ for (i = 0; i < 8; i++)
+ itd->index[i] = -1;
+
+ /* All other fields are filled when scheduling */
+}
+
+static inline void
+itd_patch (
+ struct ehci_itd *itd,
+ struct ehci_iso_sched *iso_sched,
+ unsigned index,
+ u16 uframe,
+ int first
+)
+{
+ struct ehci_iso_packet *uf = &iso_sched->packet [index];
+ unsigned pg = itd->pg;
+
+ // BUG_ON (pg == 6 && uf->cross);
+
+ uframe &= 0x07;
+ itd->index [uframe] = index;
+
+ itd->hw_transaction [uframe] = uf->transaction;
+ itd->hw_transaction [uframe] |= cpu_to_le32 (pg << 12);
+ itd->hw_bufp [pg] |= cpu_to_le32 (uf->bufp & ~(u32)0);
+ itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));
+
+ /* iso_frame_desc[].offset must be strictly increasing */
+ if (unlikely (!first && uf->cross)) {
+ u64 bufp = uf->bufp + 4096;
+ itd->pg = ++pg;
+ itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
+ itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(bufp >> 32));
+ }
+}
+
+static inline void
+itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
+{
+ /* always prepend ITD/SITD ... only QH tree is order-sensitive */
+ itd->itd_next = ehci->pshadow [frame];
+ itd->hw_next = ehci->periodic [frame];
+ ehci->pshadow [frame].itd = itd;
+ itd->frame = frame;
+ wmb ();
+ ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
+}
+
+/* fit urb's itds into the selected schedule slot; activate as needed */
+static int
+itd_link_urb (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned mod,
+ struct ehci_iso_stream *stream
+)
+{
+ int packet, first = 1;
+ unsigned next_uframe, uframe, frame;
+ struct ehci_iso_sched *iso_sched = urb->hcpriv;
+ struct ehci_itd *itd;
+
+ next_uframe = stream->next_uframe % mod;
+
+ if (unlikely (list_empty(&stream->td_list))) {
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ += stream->bandwidth;
+ ehci_vdbg (ehci,
+ "schedule devp %s ep%d%s-iso period %d start %d.%d\n",
+ urb->dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+ urb->interval,
+ next_uframe >> 3, next_uframe & 0x7);
+ stream->start = jiffies;
+ }
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
+
+ /* fill iTDs uframe by uframe */
+ for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) {
+ if (itd == NULL) {
+ /* ASSERT: we have all necessary itds */
+ // BUG_ON (list_empty (&iso_sched->td_list));
+
+ /* ASSERT: no itds for this endpoint in this uframe */
+
+ itd = list_entry (iso_sched->td_list.next,
+ struct ehci_itd, itd_list);
+ list_move_tail (&itd->itd_list, &stream->td_list);
+ itd->stream = iso_stream_get (stream);
+ itd->urb = usb_get_urb (urb);
+ first = 1;
+ itd_init (stream, itd);
+ }
+
+ uframe = next_uframe & 0x07;
+ frame = next_uframe >> 3;
+
+ itd->usecs [uframe] = stream->usecs;
+ itd_patch (itd, iso_sched, packet, uframe, first);
+ first = 0;
+
+ next_uframe += stream->interval;
+ stream->depth += stream->interval;
+ next_uframe %= mod;
+ packet++;
+
+ /* link completed itds into the schedule */
+ if (((next_uframe >> 3) != frame)
+ || packet == urb->number_of_packets) {
+ itd_link (ehci, frame % ehci->periodic_size, itd);
+ itd = NULL;
+ }
+ }
+ stream->next_uframe = next_uframe;
+
+ /* don't need that schedule data any more */
+ iso_sched_free (stream, iso_sched);
+ urb->hcpriv = NULL;
+
+ timer_action (ehci, TIMER_IO_WATCHDOG);
+ if (unlikely (!ehci->periodic_sched++))
+ return enable_periodic (ehci);
+ return 0;
+}
+
+#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+static unsigned
+itd_complete (
+ struct ehci_hcd *ehci,
+ struct ehci_itd *itd,
+ struct pt_regs *regs
+) {
+ struct urb *urb = itd->urb;
+ struct usb_iso_packet_descriptor *desc;
+ u32 t;
+ unsigned uframe;
+ int urb_index = -1;
+ struct ehci_iso_stream *stream = itd->stream;
+ struct usb_device *dev;
+
+ /* for each uframe with a packet */
+ for (uframe = 0; uframe < 8; uframe++) {
+ if (likely (itd->index[uframe] == -1))
+ continue;
+ urb_index = itd->index[uframe];
+ desc = &urb->iso_frame_desc [urb_index];
+
+ t = le32_to_cpup (&itd->hw_transaction [uframe]);
+ itd->hw_transaction [uframe] = 0;
+ stream->depth -= stream->interval;
+
+ /* report transfer status */
+ if (unlikely (t & ISO_ERRS)) {
+ urb->error_count++;
+ if (t & EHCI_ISOC_BUF_ERR)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* hc couldn't read */
+ : -ECOMM; /* hc couldn't write */
+ else if (t & EHCI_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & EHCI_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+
+ /* HC need not update length with this error */
+ if (!(t & EHCI_ISOC_BABBLE))
+ desc->actual_length = EHCI_ITD_LENGTH (t);
+ } else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
+ desc->status = 0;
+ desc->actual_length = EHCI_ITD_LENGTH (t);
+ }
+ }
+
+ usb_put_urb (urb);
+ itd->urb = NULL;
+ itd->stream = NULL;
+ list_move (&itd->itd_list, &stream->free_list);
+ iso_stream_put (ehci, stream);
+
+ /* handle completion now? */
+ if (likely ((urb_index + 1) != urb->number_of_packets))
+ return 0;
+
+ /* ASSERT: it's really the last itd for this urb
+ list_for_each_entry (itd, &stream->td_list, itd_list)
+ BUG_ON (itd->urb == urb);
+ */
+
+ /* give urb back to the driver ... can be out-of-order */
+ dev = usb_get_dev (urb->dev);
+ ehci_urb_done (ehci, urb, regs);
+ urb = NULL;
+
+ /* defer stopping schedule; completion can submit */
+ ehci->periodic_sched--;
+ if (unlikely (!ehci->periodic_sched))
+ (void) disable_periodic (ehci);
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+
+ if (unlikely (list_empty (&stream->td_list))) {
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ -= stream->bandwidth;
+ ehci_vdbg (ehci,
+ "deschedule devp %s ep%d%s-iso\n",
+ dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+ }
+ iso_stream_put (ehci, stream);
+ usb_put_dev (dev);
+
+ return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+ int status = -EINVAL;
+ unsigned long flags;
+ struct ehci_iso_stream *stream;
+
+ /* Get iso_stream head */
+ stream = iso_stream_find (ehci, urb);
+ if (unlikely (stream == NULL)) {
+ ehci_dbg (ehci, "can't get iso stream\n");
+ return -ENOMEM;
+ }
+ if (unlikely (urb->interval != stream->interval)) {
+ ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
+ stream->interval, urb->interval);
+ goto done;
+ }
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes [%p]\n",
+ __FUNCTION__, urb->dev->devpath, urb,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->transfer_buffer_length,
+ urb->number_of_packets, urb->interval,
+ stream);
+#endif
+
+ /* allocate ITDs w/o locking anything */
+ status = itd_urb_transaction (stream, ehci, urb, mem_flags);
+ if (unlikely (status < 0)) {
+ ehci_dbg (ehci, "can't init itds\n");
+ goto done;
+ }
+
+ /* schedule ... need to lock */
+ spin_lock_irqsave (&ehci->lock, flags);
+ status = iso_stream_schedule (ehci, urb, stream);
+ if (likely (status == 0))
+ itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+done:
+ if (unlikely (status < 0))
+ iso_stream_put (ehci, stream);
+ return status;
+}
+
+#ifdef CONFIG_USB_EHCI_SPLIT_ISO
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Split ISO TDs" ... used for USB 1.1 devices going through the
+ * TTs in USB 2.0 hubs. These need microframe scheduling.
+ */
+
+static inline void
+sitd_sched_init (
+ struct ehci_iso_sched *iso_sched,
+ struct ehci_iso_stream *stream,
+ struct urb *urb
+)
+{
+ unsigned i;
+ dma_addr_t dma = urb->transfer_dma;
+
+ /* how many frames are needed for these transfers */
+ iso_sched->span = urb->number_of_packets * stream->interval;
+
+ /* figure out per-frame sitd fields that we'll need later
+ * when we fit new sitds into the schedule.
+ */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct ehci_iso_packet *packet = &iso_sched->packet [i];
+ unsigned length;
+ dma_addr_t buf;
+ u32 trans;
+
+ length = urb->iso_frame_desc [i].length & 0x03ff;
+ buf = dma + urb->iso_frame_desc [i].offset;
+
+ trans = SITD_STS_ACTIVE;
+ if (((i + 1) == urb->number_of_packets)
+ && !(urb->transfer_flags & URB_NO_INTERRUPT))
+ trans |= SITD_IOC;
+ trans |= length << 16;
+ packet->transaction = cpu_to_le32 (trans);
+
+ /* might need to cross a buffer page within a td */
+ packet->bufp = buf;
+ packet->buf1 = (buf + length) & ~0x0fff;
+ if (packet->buf1 != (buf & ~(u64)0x0fff))
+ packet->cross = 1;
+
+ /* OUT uses multiple start-splits */
+ if (stream->bEndpointAddress & USB_DIR_IN)
+ continue;
+ length = (length + 187) / 188;
+ if (length > 1) /* BEGIN vs ALL */
+ length |= 1 << 3;
+ packet->buf1 |= length;
+ }
+}
+
+static int
+sitd_urb_transaction (
+ struct ehci_iso_stream *stream,
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ int mem_flags
+)
+{
+ struct ehci_sitd *sitd;
+ dma_addr_t sitd_dma;
+ int i;
+ struct ehci_iso_sched *iso_sched;
+ unsigned long flags;
+
+ iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+ if (iso_sched == NULL)
+ return -ENOMEM;
+
+ sitd_sched_init (iso_sched, stream, urb);
+
+ /* allocate/init sITDs */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < urb->number_of_packets; i++) {
+
+ /* NOTE: for now, we don't try to handle wraparound cases
+ * for IN (using sitd->hw_backpointer, like a FSTN), which
+ * means we never need two sitds for full speed packets.
+ */
+
+ /* free_list.next might be cache-hot ... but maybe
+ * the HC caches it too. avoid that issue for now.
+ */
+
+ /* prefer previously-allocated sitds */
+ if (!list_empty(&stream->free_list)) {
+ sitd = list_entry (stream->free_list.prev,
+ struct ehci_sitd, sitd_list);
+ list_del (&sitd->sitd_list);
+ sitd_dma = sitd->sitd_dma;
+ } else
+ sitd = NULL;
+
+ if (!sitd) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
+ &sitd_dma);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
+
+ if (!sitd) {
+ iso_sched_free (stream, iso_sched);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return -ENOMEM;
+ }
+ memset (sitd, 0, sizeof *sitd);
+ sitd->sitd_dma = sitd_dma;
+ list_add (&sitd->sitd_list, &iso_sched->td_list);
+ }
+
+ /* temporarily store schedule info in hcpriv */
+ urb->hcpriv = iso_sched;
+ urb->error_count = 0;
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+sitd_patch (
+ struct ehci_iso_stream *stream,
+ struct ehci_sitd *sitd,
+ struct ehci_iso_sched *iso_sched,
+ unsigned index
+)
+{
+ struct ehci_iso_packet *uf = &iso_sched->packet [index];
+ u64 bufp = uf->bufp;
+
+ sitd->hw_next = EHCI_LIST_END;
+ sitd->hw_fullspeed_ep = stream->address;
+ sitd->hw_uframe = stream->splits;
+ sitd->hw_results = uf->transaction;
+ sitd->hw_backpointer = EHCI_LIST_END;
+
+ bufp = uf->bufp;
+ sitd->hw_buf [0] = cpu_to_le32 (bufp);
+ sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
+
+ sitd->hw_buf [1] = cpu_to_le32 (uf->buf1);
+ if (uf->cross)
+ bufp += 4096;
+ sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32);
+ sitd->index = index;
+}
+
+static inline void
+sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
+{
+ /* note: sitd ordering could matter (CSPLIT then SSPLIT) */
+ sitd->sitd_next = ehci->pshadow [frame];
+ sitd->hw_next = ehci->periodic [frame];
+ ehci->pshadow [frame].sitd = sitd;
+ sitd->frame = frame;
+ wmb ();
+ ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
+}
+
+/* fit urb's sitds into the selected schedule slot; activate as needed */
+static int
+sitd_link_urb (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned mod,
+ struct ehci_iso_stream *stream
+)
+{
+ int packet;
+ unsigned next_uframe;
+ struct ehci_iso_sched *sched = urb->hcpriv;
+ struct ehci_sitd *sitd;
+
+ next_uframe = stream->next_uframe;
+
+ if (list_empty(&stream->td_list)) {
+ /* usbfs ignores TT bandwidth */
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ += stream->bandwidth;
+ ehci_vdbg (ehci,
+ "sched devp %s ep%d%s-iso [%d] %dms/%04x\n",
+ urb->dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+ (next_uframe >> 3) % ehci->periodic_size,
+ stream->interval, le32_to_cpu (stream->splits));
+ stream->start = jiffies;
+ }
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
+
+ /* fill sITDs frame by frame */
+ for (packet = 0, sitd = NULL;
+ packet < urb->number_of_packets;
+ packet++) {
+
+ /* ASSERT: we have all necessary sitds */
+ BUG_ON (list_empty (&sched->td_list));
+
+ /* ASSERT: no itds for this endpoint in this frame */
+
+ sitd = list_entry (sched->td_list.next,
+ struct ehci_sitd, sitd_list);
+ list_move_tail (&sitd->sitd_list, &stream->td_list);
+ sitd->stream = iso_stream_get (stream);
+ sitd->urb = usb_get_urb (urb);
+
+ sitd_patch (stream, sitd, sched, packet);
+ sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
+ sitd);
+
+ next_uframe += stream->interval << 3;
+ stream->depth += stream->interval << 3;
+ }
+ stream->next_uframe = next_uframe % mod;
+
+ /* don't need that schedule data any more */
+ iso_sched_free (stream, sched);
+ urb->hcpriv = NULL;
+
+ timer_action (ehci, TIMER_IO_WATCHDOG);
+ if (!ehci->periodic_sched++)
+ return enable_periodic (ehci);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
+ | SITD_STS_XACT | SITD_STS_MMF)
+
+static unsigned
+sitd_complete (
+ struct ehci_hcd *ehci,
+ struct ehci_sitd *sitd,
+ struct pt_regs *regs
+) {
+ struct urb *urb = sitd->urb;
+ struct usb_iso_packet_descriptor *desc;
+ u32 t;
+ int urb_index = -1;
+ struct ehci_iso_stream *stream = sitd->stream;
+ struct usb_device *dev;
+
+ urb_index = sitd->index;
+ desc = &urb->iso_frame_desc [urb_index];
+ t = le32_to_cpup (&sitd->hw_results);
+
+ /* report transfer status */
+ if (t & SITD_ERRS) {
+ urb->error_count++;
+ if (t & SITD_STS_DBE)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* hc couldn't read */
+ : -ECOMM; /* hc couldn't write */
+ else if (t & SITD_STS_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* XACT, MMF, etc */
+ desc->status = -EPROTO;
+ } else {
+ desc->status = 0;
+ desc->actual_length = desc->length - SITD_LENGTH (t);
+ }
+
+ usb_put_urb (urb);
+ sitd->urb = NULL;
+ sitd->stream = NULL;
+ list_move (&sitd->sitd_list, &stream->free_list);
+ stream->depth -= stream->interval << 3;
+ iso_stream_put (ehci, stream);
+
+ /* handle completion now? */
+ if ((urb_index + 1) != urb->number_of_packets)
+ return 0;
+
+ /* ASSERT: it's really the last sitd for this urb
+ list_for_each_entry (sitd, &stream->td_list, sitd_list)
+ BUG_ON (sitd->urb == urb);
+ */
+
+ /* give urb back to the driver */
+ dev = usb_get_dev (urb->dev);
+ ehci_urb_done (ehci, urb, regs);
+ urb = NULL;
+
+ /* defer stopping schedule; completion can submit */
+ ehci->periodic_sched--;
+ if (!ehci->periodic_sched)
+ (void) disable_periodic (ehci);
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+
+ if (list_empty (&stream->td_list)) {
+ ehci_to_hcd(ehci)->self.bandwidth_allocated
+ -= stream->bandwidth;
+ ehci_vdbg (ehci,
+ "deschedule devp %s ep%d%s-iso\n",
+ dev->devpath, stream->bEndpointAddress & 0x0f,
+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+ }
+ iso_stream_put (ehci, stream);
+ usb_put_dev (dev);
+
+ return 1;
+}
+
+
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+ int status = -EINVAL;
+ unsigned long flags;
+ struct ehci_iso_stream *stream;
+
+ /* Get iso_stream head */
+ stream = iso_stream_find (ehci, urb);
+ if (stream == NULL) {
+ ehci_dbg (ehci, "can't get iso stream\n");
+ return -ENOMEM;
+ }
+ if (urb->interval != stream->interval) {
+ ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
+ stream->interval, urb->interval);
+ goto done;
+ }
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg (ehci,
+ "submit %p dev%s ep%d%s-iso len %d\n",
+ urb, urb->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->transfer_buffer_length);
+#endif
+
+ /* allocate SITDs */
+ status = sitd_urb_transaction (stream, ehci, urb, mem_flags);
+ if (status < 0) {
+ ehci_dbg (ehci, "can't init sitds\n");
+ goto done;
+ }
+
+ /* schedule ... need to lock */
+ spin_lock_irqsave (&ehci->lock, flags);
+ status = iso_stream_schedule (ehci, urb, stream);
+ if (status == 0)
+ sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+done:
+ if (status < 0)
+ iso_stream_put (ehci, stream);
+ return status;
+}
+
+#else
+
+static inline int
+sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+ ehci_dbg (ehci, "split iso support is disabled\n");
+ return -ENOSYS;
+}
+
+static inline unsigned
+sitd_complete (
+ struct ehci_hcd *ehci,
+ struct ehci_sitd *sitd,
+ struct pt_regs *regs
+) {
+ ehci_err (ehci, "sitd_complete %p?\n", sitd);
+ return 0;
+}
+
+#endif /* USB_EHCI_SPLIT_ISO */
+
+/*-------------------------------------------------------------------------*/
+
+static void
+scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+ unsigned frame, clock, now_uframe, mod;
+ unsigned modified;
+
+ mod = ehci->periodic_size << 3;
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * else clean up by scanning everything that's left.
+ * Touches as few pages as possible: cache-friendly.
+ */
+ now_uframe = ehci->next_uframe;
+ if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ clock = readl (&ehci->regs->frame_index);
+ else
+ clock = now_uframe + mod - 1;
+ clock %= mod;
+
+ for (;;) {
+ union ehci_shadow q, *q_p;
+ __le32 type, *hw_p;
+ unsigned uframes;
+
+ /* don't scan past the live uframe */
+ frame = now_uframe >> 3;
+ if (frame == (clock >> 3))
+ uframes = now_uframe & 0x07;
+ else {
+ /* safe to scan the whole frame at once */
+ now_uframe |= 0x07;
+ uframes = 8;
+ }
+
+restart:
+ /* scan each element in frame's queue for completions */
+ q_p = &ehci->pshadow [frame];
+ hw_p = &ehci->periodic [frame];
+ q.ptr = q_p->ptr;
+ type = Q_NEXT_TYPE (*hw_p);
+ modified = 0;
+
+ while (q.ptr != NULL) {
+ unsigned uf;
+ union ehci_shadow temp;
+ int live;
+
+ live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
+ switch (type) {
+ case Q_TYPE_QH:
+ /* handle any completions */
+ temp.qh = qh_get (q.qh);
+ type = Q_NEXT_TYPE (q.qh->hw_next);
+ q = q.qh->qh_next;
+ modified = qh_completions (ehci, temp.qh, regs);
+ if (unlikely (list_empty (&temp.qh->qtd_list)))
+ intr_deschedule (ehci, temp.qh);
+ qh_put (temp.qh);
+ break;
+ case Q_TYPE_FSTN:
+ /* 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);
+ q = q.fstn->fstn_next;
+ break;
+ case Q_TYPE_ITD:
+ /* skip itds for later in the frame */
+ rmb ();
+ for (uf = live ? uframes : 8; uf < 8; uf++) {
+ if (0 == (q.itd->hw_transaction [uf]
+ & ITD_ACTIVE))
+ continue;
+ q_p = &q.itd->itd_next;
+ hw_p = &q.itd->hw_next;
+ type = Q_NEXT_TYPE (q.itd->hw_next);
+ q = *q_p;
+ break;
+ }
+ if (uf != 8)
+ break;
+
+ /* this one's ready ... HC won't cache the
+ * pointer for much longer, if at all.
+ */
+ *q_p = q.itd->itd_next;
+ *hw_p = q.itd->hw_next;
+ type = Q_NEXT_TYPE (q.itd->hw_next);
+ wmb();
+ modified = itd_complete (ehci, q.itd, regs);
+ q = *q_p;
+ break;
+ case Q_TYPE_SITD:
+ if ((q.sitd->hw_results & SITD_ACTIVE)
+ && live) {
+ q_p = &q.sitd->sitd_next;
+ hw_p = &q.sitd->hw_next;
+ type = Q_NEXT_TYPE (q.sitd->hw_next);
+ q = *q_p;
+ break;
+ }
+ *q_p = q.sitd->sitd_next;
+ *hw_p = q.sitd->hw_next;
+ type = Q_NEXT_TYPE (q.sitd->hw_next);
+ wmb();
+ modified = sitd_complete (ehci, q.sitd, regs);
+ q = *q_p;
+ break;
+ default:
+ dbg ("corrupt type %d frame %d shadow %p",
+ type, frame, q.ptr);
+ // BUG ();
+ q.ptr = NULL;
+ }
+
+ /* assume completion callbacks modify the queue */
+ if (unlikely (modified))
+ 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 (now_uframe == clock) {
+ unsigned now;
+
+ if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ break;
+ ehci->next_uframe = now_uframe;
+ now = readl (&ehci->regs->frame_index) % mod;
+ if (now_uframe == now)
+ break;
+
+ /* rescan the rest of this frame, then ... */
+ clock = now;
+ } else {
+ now_uframe++;
+ now_uframe %= mod;
+ }
+ }
+}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
new file mode 100644
index 0000000..67988db
--- /dev/null
+++ b/drivers/usb/host/ehci.h
@@ -0,0 +1,637 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * 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 */
+
+/* statistics can be kept for for tuning/monitoring */
+struct ehci_stats {
+ /* irq usage */
+ unsigned long normal;
+ unsigned long error;
+ unsigned long reclaim;
+ unsigned long lost_iaa;
+
+ /* termination of urbs from core */
+ unsigned long complete;
+ unsigned long unlink;
+};
+
+/* ehci_hcd->lock guards shared data against other CPUs:
+ * ehci_hcd: async, reclaim, periodic (and shadow), ...
+ * usb_host_endpoint: hcpriv
+ * 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;
+ unsigned reclaim_ready : 1;
+ unsigned scanning : 1;
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
+ unsigned periodic_size;
+ __le32 *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_uframe; /* scan periodic, start here */
+ unsigned periodic_sched; /* periodic activity count */
+
+ /* per root hub port */
+ unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+
+ /* per-HC memory pools (could be per-bus, but ...) */
+ struct dma_pool *qh_pool; /* qh per active urb */
+ struct dma_pool *qtd_pool; /* one or more per qh */
+ struct dma_pool *itd_pool; /* itd per iso urb */
+ struct dma_pool *sitd_pool; /* sitd per split iso urb */
+
+ struct timer_list watchdog;
+ struct notifier_block reboot_notifier;
+ unsigned long actions;
+ unsigned stamp;
+ unsigned long next_statechange;
+ u32 command;
+
+ unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
+
+ /* glue to PCI and HCD framework */
+ struct ehci_caps __iomem *caps;
+ struct ehci_regs __iomem *regs;
+ __u32 hcs_params; /* cached register copy */
+
+ /* irq statistics */
+#ifdef EHCI_STATS
+ struct ehci_stats stats;
+# define COUNT(x) do { (x)++; } while (0)
+#else
+# define COUNT(x) do {} while (0)
+#endif
+};
+
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
+static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
+{
+ return (struct ehci_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
+{
+ return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
+}
+
+
+enum ehci_timer_action {
+ TIMER_IO_WATCHDOG,
+ TIMER_IAA_WATCHDOG,
+ TIMER_ASYNC_SHRINK,
+ TIMER_ASYNC_OFF,
+};
+
+static inline void
+timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
+{
+ clear_bit (action, &ehci->actions);
+}
+
+static inline void
+timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
+{
+ if (!test_and_set_bit (action, &ehci->actions)) {
+ unsigned long t;
+
+ switch (action) {
+ case TIMER_IAA_WATCHDOG:
+ t = EHCI_IAA_JIFFIES;
+ break;
+ case TIMER_IO_WATCHDOG:
+ t = EHCI_IO_JIFFIES;
+ break;
+ case TIMER_ASYNC_OFF:
+ t = EHCI_ASYNC_JIFFIES;
+ break;
+ // case TIMER_ASYNC_SHRINK:
+ default:
+ t = EHCI_SHRINK_JIFFIES;
+ break;
+ }
+ t += jiffies;
+ // all timings except IAA watchdog can be overridden.
+ // async queue SHRINK often precedes IAA. while it's ready
+ // to go OFF neither can matter, and afterwards the IO
+ // watchdog stops unless there's still periodic traffic.
+ if (action != TIMER_IAA_WATCHDOG
+ && t > ehci->watchdog.expires
+ && timer_pending (&ehci->watchdog))
+ return;
+ mod_timer (&ehci->watchdog, t);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+ /* these fields are specified as 8 and 16 bit registers,
+ * but some hosts can't perform 8 or 16 bit PCI accesses.
+ */
+ u32 hc_capbase;
+#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
+#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
+ 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 */
+ /* ASYNCLISTADDR: 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 */
+#define PORT_LED_OFF (0<<14)
+#define PORT_LED_AMBER (1<<14)
+#define PORT_LED_GREEN (2<<14)
+#define PORT_LED_MASK (3<<14)
+#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));
+
+/* Appendix C, Debug port ... intended for use with special "debug devices"
+ * that can help if there's no serial console. (nonstandard enumeration.)
+ */
+struct ehci_dbg_port {
+ u32 control;
+#define DBGP_OWNER (1<<30)
+#define DBGP_ENABLED (1<<28)
+#define DBGP_DONE (1<<16)
+#define DBGP_INUSE (1<<10)
+#define DBGP_ERRCODE(x) (((x)>>7)&0x0f)
+# define DBGP_ERR_BAD 1
+# define DBGP_ERR_SIGNAL 2
+#define DBGP_ERROR (1<<6)
+#define DBGP_GO (1<<5)
+#define DBGP_OUT (1<<4)
+#define DBGP_LEN(x) (((x)>>0)&0x0f)
+ u32 pids;
+#define DBGP_PID_GET(x) (((x)>>16)&0xff)
+#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok));
+ u32 data03;
+ u32 data47;
+ u32 address;
+#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep));
+} __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 */
+ __le32 hw_next; /* see EHCI 3.5.1 */
+ __le32 hw_alt_next; /* see EHCI 3.5.2 */
+ __le32 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? */
+ __le32 hw_buf [5]; /* see EHCI 3.5.4 */
+ __le32 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 */
+ struct urb *urb; /* qtd's urb */
+ size_t length; /* length of buffer */
+} __attribute__ ((aligned (32)));
+
+/* mask NakCnt+T in qh->hw_alt_next */
+#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
+
+#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
+
+/*-------------------------------------------------------------------------*/
+
+/* 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 */
+ u32 *hw_next; /* (all types) */
+ 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 */
+ __le32 hw_next; /* see EHCI 3.6.1 */
+ __le32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_HEAD 0x00008000
+ __le32 hw_info2; /* see EHCI 3.6.2 */
+ __le32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ __le32 hw_qtd_next;
+ __le32 hw_alt_next;
+ __le32 hw_token;
+ __le32 hw_buf [5];
+ __le32 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 */
+ struct ehci_qtd *dummy;
+ struct ehci_qh *reclaim; /* next to reclaim */
+
+ struct ehci_hcd *ehci;
+ struct kref kref;
+ unsigned stamp;
+
+ u8 qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+
+ /* periodic schedule info */
+ u8 usecs; /* intr bandwidth */
+ u8 gap_uf; /* uframes split/csplit gap */
+ u8 c_usecs; /* ... split completion bw */
+ unsigned short period; /* polling interval */
+ unsigned short start; /* where polling starts */
+#define NO_FRAME ((unsigned short)~0) /* pick new start */
+ struct usb_device *dev; /* access to TT */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/* description of one iso transaction (up to 3 KB data if highspeed) */
+struct ehci_iso_packet {
+ /* These will be copied to iTD when scheduling */
+ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
+ __le32 transaction; /* itd->hw_transaction[i] |= */
+ u8 cross; /* buf crosses pages */
+ /* for full speed OUT splits */
+ u32 buf1;
+};
+
+/* temporary schedule data for packets from iso urbs (both speeds)
+ * each packet is one logical usb transaction to the device (not TT),
+ * beginning at stream->next_uframe
+ */
+struct ehci_iso_sched {
+ struct list_head td_list;
+ unsigned span;
+ struct ehci_iso_packet packet [0];
+};
+
+/*
+ * ehci_iso_stream - groups all (s)itds for this endpoint.
+ * acts like a qh would, if EHCI had them for ISO.
+ */
+struct ehci_iso_stream {
+ /* first two fields match QH, but info1 == 0 */
+ __le32 hw_next;
+ __le32 hw_info1;
+
+ u32 refcount;
+ u8 bEndpointAddress;
+ u8 highspeed;
+ u16 depth; /* depth in uframes */
+ struct list_head td_list; /* queued itds/sitds */
+ struct list_head free_list; /* list of unused itds/sitds */
+ struct usb_device *udev;
+ struct usb_host_endpoint *ep;
+
+ /* output of (re)scheduling */
+ unsigned long start; /* jiffies */
+ unsigned long rescheduled;
+ int next_uframe;
+ __le32 splits;
+
+ /* the rest is derived from the endpoint descriptor,
+ * trusting urb->interval == f(epdesc->bInterval) and
+ * including the extra info for hw_bufp[0..2]
+ */
+ u8 interval;
+ u8 usecs, c_usecs;
+ u16 maxp;
+ u16 raw_mask;
+ unsigned bandwidth;
+
+ /* This is used to initialize iTD's hw_bufp fields */
+ __le32 buf0;
+ __le32 buf1;
+ __le32 buf2;
+
+ /* this is used to initialize sITD's tt info */
+ __le32 address;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+struct ehci_itd {
+ /* first part defined by EHCI spec */
+ __le32 hw_next; /* see EHCI 3.3.1 */
+ __le32 hw_transaction [8]; /* see EHCI 3.3.2 */
+#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
+#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
+#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
+#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
+#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
+
+#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
+
+ __le32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ __le32 hw_bufp_hi [7]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t itd_dma; /* for this itd */
+ union ehci_shadow itd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ struct ehci_iso_stream *stream; /* endpoint's queue */
+ struct list_head itd_list; /* list of stream's itds */
+
+ /* any/all hw_transactions here may be used by that urb */
+ unsigned frame; /* where scheduled */
+ unsigned pg;
+ unsigned index[8]; /* in urb->iso_frame_desc */
+ u8 usecs[8];
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.4
+ * siTD, aka split-transaction isochronous Transfer Descriptor
+ * ... describe full speed iso xfers through TT in hubs
+ * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
+ */
+struct ehci_sitd {
+ /* first part defined by EHCI spec */
+ __le32 hw_next;
+/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
+ __le32 hw_fullspeed_ep; /* EHCI table 3-9 */
+ __le32 hw_uframe; /* EHCI table 3-10 */
+ __le32 hw_results; /* EHCI table 3-11 */
+#define SITD_IOC (1 << 31) /* interrupt on completion */
+#define SITD_PAGE (1 << 30) /* buffer 0/1 */
+#define SITD_LENGTH(x) (0x3ff & ((x)>>16))
+#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define SITD_STS_ERR (1 << 6) /* error from TT */
+#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define SITD_STS_BABBLE (1 << 4) /* device was babbling */
+#define SITD_STS_XACT (1 << 3) /* illegal IN response */
+#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define SITD_STS_STS (1 << 1) /* split transaction state */
+
+#define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE)
+
+ __le32 hw_buf [2]; /* EHCI table 3-12 */
+ __le32 hw_backpointer; /* EHCI table 3-13 */
+ __le32 hw_buf_hi [2]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t sitd_dma;
+ union ehci_shadow sitd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ struct ehci_iso_stream *stream; /* endpoint's queue */
+ struct list_head sitd_list; /* list of stream's sitds */
+ unsigned frame;
+ unsigned index;
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+struct ehci_fstn {
+ __le32 hw_next; /* any periodic q entry */
+ __le32 hw_prev; /* qh or EHCI_LIST_END */
+
+ /* the rest is HCD-private */
+ dma_addr_t fstn_dma;
+ union ehci_shadow fstn_next; /* ptr to periodic q entry */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
+
+/*
+ * Some EHCI controllers have a Transaction Translator built into the
+ * root hub. This is a non-standard feature. Each controller will need
+ * to add code to the following inline functions, and call them as
+ * needed (mostly in root hub code).
+ */
+
+#define ehci_is_TDI(e) ((e)->is_tdi_rh_tt)
+
+/* Returns the speed of a device attached to a port on the root hub. */
+static inline unsigned int
+ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
+{
+ if (ehci_is_TDI(ehci)) {
+ switch ((portsc>>26)&3) {
+ case 0:
+ return 0;
+ case 1:
+ return (1<<USB_PORT_FEAT_LOWSPEED);
+ case 2:
+ default:
+ return (1<<USB_PORT_FEAT_HIGHSPEED);
+ }
+ }
+ return (1<<USB_PORT_FEAT_HIGHSPEED);
+}
+
+#else
+
+#define ehci_is_TDI(e) (0)
+
+#define ehci_port_speed(ehci, portsc) (1<<USB_PORT_FEAT_HIGHSPEED)
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef DEBUG
+#define STUB_DEBUG_FILES
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*/
+
+#endif /* __LINUX_EHCI_HCD_H */
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
new file mode 100644
index 0000000..4b12be8
--- /dev/null
+++ b/drivers/usb/host/hc_crisv10.c
@@ -0,0 +1,4556 @@
+/*
+ * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
+ *
+ * Copyright (c) 2002, 2003 Axis Communications AB.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/arch/svinto.h>
+
+#include <linux/usb.h>
+/* Ugly include because we don't live with the other host drivers. */
+#include <../drivers/usb/core/hcd.h>
+#include <../drivers/usb/core/usb.h>
+
+#include "hc_crisv10.h"
+
+#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
+#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
+#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
+
+static const char *usb_hcd_version = "$Revision: 1.2 $";
+
+#undef KERN_DEBUG
+#define KERN_DEBUG ""
+
+
+#undef USB_DEBUG_RH
+#undef USB_DEBUG_EPID
+#undef USB_DEBUG_SB
+#undef USB_DEBUG_DESC
+#undef USB_DEBUG_URB
+#undef USB_DEBUG_TRACE
+#undef USB_DEBUG_BULK
+#undef USB_DEBUG_CTRL
+#undef USB_DEBUG_INTR
+#undef USB_DEBUG_ISOC
+
+#ifdef USB_DEBUG_RH
+#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
+#else
+#define dbg_rh(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_EPID
+#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
+#else
+#define dbg_epid(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_SB
+#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
+#else
+#define dbg_sb(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_CTRL
+#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
+#else
+#define dbg_ctrl(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_BULK
+#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
+#else
+#define dbg_bulk(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_INTR
+#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
+#else
+#define dbg_intr(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_ISOC
+#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
+#else
+#define dbg_isoc(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_TRACE
+#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__))
+#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__))
+#else
+#define DBFENTER do {} while (0)
+#define DBFEXIT do {} while (0)
+#endif
+
+#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
+
+/*-------------------------------------------------------------------
+ Virtual Root Hub
+ -------------------------------------------------------------------*/
+
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, /* __le16 bcdUSB; v1.0 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __le16 idVendor; */
+ 0x00,
+ 0x00, /* __le16 idProduct; */
+ 0x00,
+ 0x00, /* __le16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __le16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x08, /* __le16 ep_wMaxPacketSize; 8 Bytes */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+static __u8 root_hub_hub_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x29, /* __u8 bDescriptorType; Hub-descriptor */
+ 0x02, /* __u8 bNbrPorts; */
+ 0x00, /* __u16 wHubCharacteristics; */
+ 0x00,
+ 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
+ 0x00, /* __u8 bHubContrCurrent; 0 mA */
+ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
+ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+static struct timer_list bulk_start_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static struct timer_list bulk_eot_timer = TIMER_INITIALIZER(NULL, 0, 0);
+
+/* We want the start timer to expire before the eot timer, because the former might start
+ traffic, thus making it unnecessary for the latter to time out. */
+#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
+#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
+
+#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
+#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
+{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
+
+#define SLAB_FLAG (in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+
+/* Most helpful debugging aid */
+#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
+
+/* Alternative assert define which stops after a failed assert. */
+/*
+#define assert(expr) \
+{ \
+ if (!(expr)) { \
+ err("assert failed at line %d",__LINE__); \
+ while (1); \
+ } \
+}
+*/
+
+
+/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
+ To adjust it dynamically we would have to get an interrupt when we reach the end
+ of the rx descriptor list, or when we get close to the end, and then allocate more
+ descriptors. */
+
+#define NBR_OF_RX_DESC 512
+#define RX_DESC_BUF_SIZE 1024
+#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
+
+/* The number of epids is, among other things, used for pre-allocating
+ ctrl, bulk and isoc EP descriptors (one for each epid).
+ Assumed to be > 1 when initiating the DMA lists. */
+#define NBR_OF_EPIDS 32
+
+/* Support interrupt traffic intervals up to 128 ms. */
+#define MAX_INTR_INTERVAL 128
+
+/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
+ must be "invalid". By this we mean that we shouldn't care about epid attentions
+ for this epid, or at least handle them differently from epid attentions for "valid"
+ epids. This define determines which one to use (don't change it). */
+#define INVALID_EPID 31
+/* A special epid for the bulk dummys. */
+#define DUMMY_EPID 30
+
+/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
+static __u32 epid_usage_bitmask;
+
+/* A bitfield to keep information on in/out traffic is needed to uniquely identify
+ an endpoint on a device, since the most significant bit which indicates traffic
+ direction is lacking in the ep_id field (ETRAX epids can handle both in and
+ out traffic on endpoints that are otherwise identical). The USB framework, however,
+ relies on them to be handled separately. For example, bulk IN and OUT urbs cannot
+ be queued in the same list, since they would block each other. */
+static __u32 epid_out_traffic;
+
+/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
+ Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
+static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
+static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
+
+/* Pointers into RxDescList. */
+static volatile USB_IN_Desc_t *myNextRxDesc;
+static volatile USB_IN_Desc_t *myLastRxDesc;
+static volatile USB_IN_Desc_t *myPrevRxDesc;
+
+/* EP descriptors must be 32-bit aligned. */
+static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
+ causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
+ gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
+ EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
+ in each frame. */
+static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
+
+/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
+ this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
+ results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
+ it to this buffer. */
+static int zout_buffer[4] __attribute__ ((aligned (4)));
+
+/* Cache for allocating new EP and SB descriptors. */
+static kmem_cache_t *usb_desc_cache;
+
+/* Cache for the registers allocated in the top half. */
+static kmem_cache_t *top_half_reg_cache;
+
+/* Cache for the data allocated in the isoc descr top half. */
+static kmem_cache_t *isoc_compl_cache;
+
+static struct usb_bus *etrax_usb_bus;
+
+/* This is a circular (double-linked) list of the active urbs for each epid.
+ The head is never removed, and new urbs are linked onto the list as
+ urb_entry_t elements. Don't reference urb_list directly; use the wrapper
+ functions instead. Note that working with these lists might require spinlock
+ protection. */
+static struct list_head urb_list[NBR_OF_EPIDS];
+
+/* Read about the need and usage of this lock in submit_ctrl_urb. */
+static spinlock_t urb_list_lock;
+
+/* Used when unlinking asynchronously. */
+static struct list_head urb_unlink_list;
+
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+ int retval;
+
+ for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+ *utf++ = *ascii++ & 0x7f;
+ *utf++ = 0;
+ }
+ return retval;
+}
+
+static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+ char buf [30];
+
+ // assert (len > (2 * (sizeof (buf) + 1)));
+ // assert (strlen (type) <= 8);
+
+ // language ids
+ if (id == 0) {
+ *data++ = 4; *data++ = 3; /* 4 bytes data */
+ *data++ = 0; *data++ = 0; /* some language id */
+ return 4;
+
+ // serial number
+ } else if (id == 1) {
+ sprintf (buf, "%x", serial);
+
+ // product description
+ } else if (id == 2) {
+ sprintf (buf, "USB %s Root Hub", type);
+
+ // id 3 == vendor description
+
+ // unsupported IDs --> "stall"
+ } else
+ return 0;
+
+ data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+ data [1] = 3;
+ return data [0];
+}
+
+/* Wrappers around the list functions (include/linux/list.h). */
+
+static inline int urb_list_empty(int epid)
+{
+ return list_empty(&urb_list[epid]);
+}
+
+/* Returns first urb for this epid, or NULL if list is empty. */
+static inline struct urb *urb_list_first(int epid)
+{
+ struct urb *first_urb = 0;
+
+ if (!urb_list_empty(epid)) {
+ /* Get the first urb (i.e. head->next). */
+ urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
+ first_urb = urb_entry->urb;
+ }
+ return first_urb;
+}
+
+/* Adds an urb_entry last in the list for this epid. */
+static inline void urb_list_add(struct urb *urb, int epid)
+{
+ urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+ assert(urb_entry);
+
+ urb_entry->urb = urb;
+ list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Search through the list for an element that contains this urb. (The list
+ is expected to be short and the one we are about to delete will often be
+ the first in the list.) */
+static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid)
+{
+ struct list_head *entry;
+ struct list_head *tmp;
+ urb_entry_t *urb_entry;
+
+ list_for_each_safe(entry, tmp, &urb_list[epid]) {
+ urb_entry = list_entry(entry, urb_entry_t, list);
+ assert(urb_entry);
+ assert(urb_entry->urb);
+
+ if (urb_entry->urb == urb) {
+ return urb_entry;
+ }
+ }
+ return 0;
+}
+
+/* Delete an urb from the list. */
+static inline void urb_list_del(struct urb *urb, int epid)
+{
+ urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+ assert(urb_entry);
+
+ /* Delete entry and free. */
+ list_del(&urb_entry->list);
+ kfree(urb_entry);
+}
+
+/* Move an urb to the end of the list. */
+static inline void urb_list_move_last(struct urb *urb, int epid)
+{
+ urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+ assert(urb_entry);
+
+ list_del(&urb_entry->list);
+ list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Get the next urb in the list. */
+static inline struct urb *urb_list_next(struct urb *urb, int epid)
+{
+ urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+
+ assert(urb_entry);
+
+ if (urb_entry->list.next != &urb_list[epid]) {
+ struct list_head *elem = urb_entry->list.next;
+ urb_entry = list_entry(elem, urb_entry_t, list);
+ return urb_entry->urb;
+ } else {
+ return NULL;
+ }
+}
+
+
+
+/* For debug purposes only. */
+static inline void urb_list_dump(int epid)
+{
+ struct list_head *entry;
+ struct list_head *tmp;
+ urb_entry_t *urb_entry;
+ int i = 0;
+
+ info("Dumping urb list for epid %d", epid);
+
+ list_for_each_safe(entry, tmp, &urb_list[epid]) {
+ urb_entry = list_entry(entry, urb_entry_t, list);
+ info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
+ }
+}
+
+static void init_rx_buffers(void);
+static int etrax_rh_unlink_urb(struct urb *urb);
+static void etrax_rh_send_irq(struct urb *urb);
+static void etrax_rh_init_int_timer(struct urb *urb);
+static void etrax_rh_int_timer_do(unsigned long ptr);
+
+static int etrax_usb_setup_epid(struct urb *urb);
+static int etrax_usb_lookup_epid(struct urb *urb);
+static int etrax_usb_allocate_epid(void);
+static void etrax_usb_free_epid(int epid);
+
+static int etrax_remove_from_sb_list(struct urb *urb);
+
+static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, int mem_flags, dma_addr_t *dma);
+static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma);
+
+static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid);
+static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid);
+static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid);
+static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid);
+
+static int etrax_usb_submit_bulk_urb(struct urb *urb);
+static int etrax_usb_submit_ctrl_urb(struct urb *urb);
+static int etrax_usb_submit_intr_urb(struct urb *urb);
+static int etrax_usb_submit_isoc_urb(struct urb *urb);
+
+static int etrax_usb_submit_urb(struct urb *urb, int mem_flags);
+static int etrax_usb_unlink_urb(struct urb *urb, int status);
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
+
+static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs);
+static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
+static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_interrupt_bottom_half(void *data);
+
+static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
+
+
+/* The following is a list of interrupt handlers for the host controller interrupts we use.
+ They are called from etrax_usb_hc_interrupt_bottom_half. */
+static void etrax_usb_hc_isoc_eof_interrupt(void);
+static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
+static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
+
+static int etrax_rh_submit_urb (struct urb *urb);
+
+/* Forward declaration needed because they are used in the rx interrupt routine. */
+static void etrax_usb_complete_urb(struct urb *urb, int status);
+static void etrax_usb_complete_bulk_urb(struct urb *urb, int status);
+static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status);
+static void etrax_usb_complete_intr_urb(struct urb *urb, int status);
+static void etrax_usb_complete_isoc_urb(struct urb *urb, int status);
+
+static int etrax_usb_hc_init(void);
+static void etrax_usb_hc_cleanup(void);
+
+static struct usb_operations etrax_usb_device_operations =
+{
+ .get_frame_number = etrax_usb_get_frame_number,
+ .submit_urb = etrax_usb_submit_urb,
+ .unlink_urb = etrax_usb_unlink_urb,
+ .buffer_alloc = etrax_usb_buffer_alloc,
+ .buffer_free = etrax_usb_buffer_free
+};
+
+/* Note that these functions are always available in their "__" variants, for use in
+ error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
+ USB_DEBUG_URB macros. */
+static void __dump_urb(struct urb* purb)
+{
+ printk("\nurb :0x%08lx\n", (unsigned long)purb);
+ printk("dev :0x%08lx\n", (unsigned long)purb->dev);
+ printk("pipe :0x%08x\n", purb->pipe);
+ printk("status :%d\n", purb->status);
+ printk("transfer_flags :0x%08x\n", purb->transfer_flags);
+ printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer);
+ printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+ printk("actual_length :%d\n", purb->actual_length);
+ printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet);
+ printk("start_frame :%d\n", purb->start_frame);
+ printk("number_of_packets :%d\n", purb->number_of_packets);
+ printk("interval :%d\n", purb->interval);
+ printk("error_count :%d\n", purb->error_count);
+ printk("context :0x%08lx\n", (unsigned long)purb->context);
+ printk("complete :0x%08lx\n\n", (unsigned long)purb->complete);
+}
+
+static void __dump_in_desc(volatile USB_IN_Desc_t *in)
+{
+ printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
+ printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len);
+ printk(" command : 0x%04x\n", in->command);
+ printk(" next : 0x%08lx\n", in->next);
+ printk(" buf : 0x%08lx\n", in->buf);
+ printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len);
+ printk(" status : 0x%04x\n\n", in->status);
+}
+
+static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
+{
+ char tt = (sb->command & 0x30) >> 4;
+ char *tt_string;
+
+ switch (tt) {
+ case 0:
+ tt_string = "zout";
+ break;
+ case 1:
+ tt_string = "in";
+ break;
+ case 2:
+ tt_string = "out";
+ break;
+ case 3:
+ tt_string = "setup";
+ break;
+ default:
+ tt_string = "unknown (weird)";
+ }
+
+ printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb);
+ printk(" command : 0x%04x\n", sb->command);
+ printk(" rem : %d\n", (sb->command & 0x3f00) >> 8);
+ printk(" full : %d\n", (sb->command & 0x40) >> 6);
+ printk(" tt : %d (%s)\n", tt, tt_string);
+ printk(" intr : %d\n", (sb->command & 0x8) >> 3);
+ printk(" eot : %d\n", (sb->command & 0x2) >> 1);
+ printk(" eol : %d\n", sb->command & 0x1);
+ printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
+ printk(" next : 0x%08lx\n", sb->next);
+ printk(" buf : 0x%08lx\n\n", sb->buf);
+}
+
+
+static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
+{
+ printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
+ printk(" command : 0x%04x\n", ep->command);
+ printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8);
+ printk(" enable : %d\n", (ep->command & 0x10) >> 4);
+ printk(" intr : %d\n", (ep->command & 0x8) >> 3);
+ printk(" eof : %d\n", (ep->command & 0x2) >> 1);
+ printk(" eol : %d\n", ep->command & 0x1);
+ printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
+ printk(" next : 0x%08lx\n", ep->next);
+ printk(" sub : 0x%08lx\n\n", ep->sub);
+}
+
+static inline void __dump_ep_list(int pipe_type)
+{
+ volatile USB_EP_Desc_t *ep;
+ volatile USB_EP_Desc_t *first_ep;
+ volatile USB_SB_Desc_t *sb;
+
+ switch (pipe_type)
+ {
+ case PIPE_BULK:
+ first_ep = &TxBulkEPList[0];
+ break;
+ case PIPE_CONTROL:
+ first_ep = &TxCtrlEPList[0];
+ break;
+ case PIPE_INTERRUPT:
+ first_ep = &TxIntrEPList[0];
+ break;
+ case PIPE_ISOCHRONOUS:
+ first_ep = &TxIsocEPList[0];
+ break;
+ default:
+ warn("Cannot dump unknown traffic type");
+ return;
+ }
+ ep = first_ep;
+
+ printk("\n\nDumping EP list...\n\n");
+
+ do {
+ __dump_ep_desc(ep);
+ /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
+ sb = ep->sub ? phys_to_virt(ep->sub) : 0;
+ while (sb) {
+ __dump_sb_desc(sb);
+ sb = sb->next ? phys_to_virt(sb->next) : 0;
+ }
+ ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
+
+ } while (ep != first_ep);
+}
+
+static inline void __dump_ept_data(int epid)
+{
+ unsigned long flags;
+ __u32 r_usb_ept_data;
+
+ if (epid < 0 || epid > 31) {
+ printk("Cannot dump ept data for invalid epid %d\n", epid);
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ r_usb_ept_data = *R_USB_EPT_DATA;
+ restore_flags(flags);
+
+ printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
+ if (r_usb_ept_data == 0) {
+ /* No need for more detailed printing. */
+ return;
+ }
+ printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
+ printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
+ printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
+ printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
+ printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
+ printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
+ printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
+ printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
+ printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
+ printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
+ printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
+ printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f));
+}
+
+static inline void __dump_ept_data_list(void)
+{
+ int i;
+
+ printk("Dumping the whole R_USB_EPT_DATA list\n");
+
+ for (i = 0; i < 32; i++) {
+ __dump_ept_data(i);
+ }
+}
+#ifdef USB_DEBUG_DESC
+#define dump_in_desc(...) __dump_in_desc(...)
+#define dump_sb_desc(...) __dump_sb_desc(...)
+#define dump_ep_desc(...) __dump_ep_desc(...)
+#else
+#define dump_in_desc(...) do {} while (0)
+#define dump_sb_desc(...) do {} while (0)
+#define dump_ep_desc(...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_URB
+#define dump_urb(x) __dump_urb(x)
+#else
+#define dump_urb(x) do {} while (0)
+#endif
+
+static void init_rx_buffers(void)
+{
+ int i;
+
+ DBFENTER;
+
+ for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+ RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+ RxDescList[i].command = 0;
+ RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
+ RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+ RxDescList[i].hw_len = 0;
+ RxDescList[i].status = 0;
+
+ /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
+ for the relevant fields.) */
+ prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
+
+ }
+
+ RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+ RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
+ RxDescList[i].next = virt_to_phys(&RxDescList[0]);
+ RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+ RxDescList[i].hw_len = 0;
+ RxDescList[i].status = 0;
+
+ myNextRxDesc = &RxDescList[0];
+ myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+ myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+
+ *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
+ *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
+
+ DBFEXIT;
+}
+
+static void init_tx_bulk_ep(void)
+{
+ int i;
+
+ DBFENTER;
+
+ for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+ CHECK_ALIGN(&TxBulkEPList[i]);
+ TxBulkEPList[i].hw_len = 0;
+ TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+ TxBulkEPList[i].sub = 0;
+ TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
+
+ /* Initiate two EPs, disabled and with the eol flag set. No need for any
+ preserved epid. */
+
+ /* The first one has the intr flag set so we get an interrupt when the DMA
+ channel is about to become disabled. */
+ CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
+ TxBulkDummyEPList[i][0].hw_len = 0;
+ TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+ IO_STATE(USB_EP_command, eol, yes) |
+ IO_STATE(USB_EP_command, intr, yes));
+ TxBulkDummyEPList[i][0].sub = 0;
+ TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
+
+ /* The second one. */
+ CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
+ TxBulkDummyEPList[i][1].hw_len = 0;
+ TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+ IO_STATE(USB_EP_command, eol, yes));
+ TxBulkDummyEPList[i][1].sub = 0;
+ /* The last dummy's next pointer is the same as the current EP's next pointer. */
+ TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
+ }
+
+ /* Configure the last one. */
+ CHECK_ALIGN(&TxBulkEPList[i]);
+ TxBulkEPList[i].hw_len = 0;
+ TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+ IO_FIELD(USB_EP_command, epid, i));
+ TxBulkEPList[i].sub = 0;
+ TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
+
+ /* No need configuring dummy EPs for the last one as it will never be used for
+ bulk traffic (i == INVALD_EPID at this point). */
+
+ /* Set up to start on the last EP so we will enable it when inserting traffic
+ for the first time (imitating the situation where the DMA has stopped
+ because there was no more traffic). */
+ *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
+ /* No point in starting the bulk channel yet.
+ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
+ DBFEXIT;
+}
+
+static void init_tx_ctrl_ep(void)
+{
+ int i;
+
+ DBFENTER;
+
+ for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+ CHECK_ALIGN(&TxCtrlEPList[i]);
+ TxCtrlEPList[i].hw_len = 0;
+ TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+ TxCtrlEPList[i].sub = 0;
+ TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
+ }
+
+ CHECK_ALIGN(&TxCtrlEPList[i]);
+ TxCtrlEPList[i].hw_len = 0;
+ TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+ IO_FIELD(USB_EP_command, epid, i));
+
+ TxCtrlEPList[i].sub = 0;
+ TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
+
+ *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
+ *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+
+ DBFEXIT;
+}
+
+
+static void init_tx_intr_ep(void)
+{
+ int i;
+
+ DBFENTER;
+
+ /* Read comment at zout_buffer declaration for an explanation to this. */
+ TxIntrSB_zout.sw_len = 1;
+ TxIntrSB_zout.next = 0;
+ TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+ TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, zout) |
+ IO_STATE(USB_SB_command, full, yes) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
+ CHECK_ALIGN(&TxIntrEPList[i]);
+ TxIntrEPList[i].hw_len = 0;
+ TxIntrEPList[i].command =
+ (IO_STATE(USB_EP_command, eof, yes) |
+ IO_STATE(USB_EP_command, enable, yes) |
+ IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+ TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+ TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
+ }
+
+ CHECK_ALIGN(&TxIntrEPList[i]);
+ TxIntrEPList[i].hw_len = 0;
+ TxIntrEPList[i].command =
+ (IO_STATE(USB_EP_command, eof, yes) |
+ IO_STATE(USB_EP_command, eol, yes) |
+ IO_STATE(USB_EP_command, enable, yes) |
+ IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+ TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+ TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
+
+ *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
+ *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+ DBFEXIT;
+}
+
+static void init_tx_isoc_ep(void)
+{
+ int i;
+
+ DBFENTER;
+
+ /* Read comment at zout_buffer declaration for an explanation to this. */
+ TxIsocSB_zout.sw_len = 1;
+ TxIsocSB_zout.next = 0;
+ TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+ TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, zout) |
+ IO_STATE(USB_SB_command, full, yes) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ /* The last isochronous EP descriptor is a dummy. */
+
+ for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+ CHECK_ALIGN(&TxIsocEPList[i]);
+ TxIsocEPList[i].hw_len = 0;
+ TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+ TxIsocEPList[i].sub = 0;
+ TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
+ }
+
+ CHECK_ALIGN(&TxIsocEPList[i]);
+ TxIsocEPList[i].hw_len = 0;
+
+ /* Must enable the last EP descr to get eof interrupt. */
+ TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
+ IO_STATE(USB_EP_command, eof, yes) |
+ IO_STATE(USB_EP_command, eol, yes) |
+ IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+ TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
+ TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
+
+ *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
+ *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+
+ DBFEXIT;
+}
+
+static void etrax_usb_unlink_intr_urb(struct urb *urb)
+{
+ volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */
+ volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */
+ volatile USB_EP_Desc_t *next_ep; /* The EP after current. */
+ volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
+
+ int epid;
+
+ /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
+
+ DBFENTER;
+
+ epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
+
+ first_ep = &TxIntrEPList[0];
+ curr_ep = first_ep;
+
+
+ /* Note that this loop removes all EP descriptors with this epid. This assumes
+ that all EP descriptors belong to the one and only urb for this epid. */
+
+ do {
+ next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
+
+ if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
+
+ dbg_intr("Found EP to unlink for epid %d", epid);
+
+ /* This is the one we should unlink. */
+ unlink_ep = next_ep;
+
+ /* Actually unlink the EP from the DMA list. */
+ curr_ep->next = unlink_ep->next;
+
+ /* Wait until the DMA is no longer at this descriptor. */
+ while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
+
+ /* Now we are free to remove it and its SB descriptor.
+ Note that it is assumed here that there is only one sb in the
+ sb list for this ep. */
+ kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
+ kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
+ }
+
+ curr_ep = phys_to_virt(curr_ep->next);
+
+ } while (curr_ep != first_ep);
+ urb->hcpriv = NULL;
+}
+
+void etrax_usb_do_intr_recover(int epid)
+{
+ USB_EP_Desc_t *first_ep, *tmp_ep;
+
+ DBFENTER;
+
+ first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+ tmp_ep = first_ep;
+
+ /* What this does is simply to walk the list of interrupt
+ ep descriptors and enable those that are disabled. */
+
+ do {
+ if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
+ !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
+ tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
+ }
+
+ tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+
+ } while (tmp_ep != first_ep);
+
+
+ DBFEXIT;
+}
+
+static int etrax_rh_unlink_urb (struct urb *urb)
+{
+ etrax_hc_t *hc;
+
+ DBFENTER;
+
+ hc = urb->dev->bus->hcpriv;
+
+ if (hc->rh.urb == urb) {
+ hc->rh.send = 0;
+ del_timer(&hc->rh.rh_int_timer);
+ }
+
+ DBFEXIT;
+ return 0;
+}
+
+static void etrax_rh_send_irq(struct urb *urb)
+{
+ __u16 data = 0;
+ etrax_hc_t *hc = urb->dev->bus->hcpriv;
+ DBFENTER;
+
+/*
+ dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER);
+ dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
+*/
+
+ data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
+ data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
+
+ *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
+ /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
+ Since only 1 byte is used, why not declare data as __u8? */
+ urb->actual_length = 1;
+ urb->status = 0;
+
+ if (hc->rh.send && urb->complete) {
+ dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1);
+ dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
+
+ urb->complete(urb, NULL);
+ }
+
+ DBFEXIT;
+}
+
+static void etrax_rh_init_int_timer(struct urb *urb)
+{
+ etrax_hc_t *hc;
+
+ DBFENTER;
+
+ hc = urb->dev->bus->hcpriv;
+ hc->rh.interval = urb->interval;
+ init_timer(&hc->rh.rh_int_timer);
+ hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
+ hc->rh.rh_int_timer.data = (unsigned long)urb;
+ /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
+ to 0, and the rest to the nearest lower 10 ms. */
+ hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
+ add_timer(&hc->rh.rh_int_timer);
+
+ DBFEXIT;
+}
+
+static void etrax_rh_int_timer_do(unsigned long ptr)
+{
+ struct urb *urb;
+ etrax_hc_t *hc;
+
+ DBFENTER;
+
+ urb = (struct urb*)ptr;
+ hc = urb->dev->bus->hcpriv;
+
+ if (hc->rh.send) {
+ etrax_rh_send_irq(urb);
+ }
+
+ DBFEXIT;
+}
+
+static int etrax_usb_setup_epid(struct urb *urb)
+{
+ int epid;
+ char devnum, endpoint, out_traffic, slow;
+ int maxlen;
+ unsigned long flags;
+
+ DBFENTER;
+
+ epid = etrax_usb_lookup_epid(urb);
+ if ((epid != -1)){
+ /* An epid that fits this urb has been found. */
+ DBFEXIT;
+ return epid;
+ }
+
+ /* We must find and initiate a new epid for this urb. */
+ epid = etrax_usb_allocate_epid();
+
+ if (epid == -1) {
+ /* Failed to allocate a new epid. */
+ DBFEXIT;
+ return epid;
+ }
+
+ /* We now have a new epid to use. Initiate it. */
+ set_bit(epid, (void *)&epid_usage_bitmask);
+
+ devnum = usb_pipedevice(urb->pipe);
+ endpoint = usb_pipeendpoint(urb->pipe);
+ slow = usb_pipeslow(urb->pipe);
+ maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+ /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+ out_traffic = 1;
+ } else {
+ out_traffic = usb_pipeout(urb->pipe);
+ }
+
+ save_flags(flags);
+ cli();
+
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
+ /* FIXME: Change any to the actual port? */
+ IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
+ IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
+ IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
+ IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
+ } else {
+ *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
+ IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
+ /* FIXME: Change any to the actual port? */
+ IO_STATE(R_USB_EPT_DATA, port, any) |
+ IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
+ IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
+ IO_FIELD(R_USB_EPT_DATA, dev, devnum);
+ }
+
+ restore_flags(flags);
+
+ if (out_traffic) {
+ set_bit(epid, (void *)&epid_out_traffic);
+ } else {
+ clear_bit(epid, (void *)&epid_out_traffic);
+ }
+
+ dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
+ epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
+
+ DBFEXIT;
+ return epid;
+}
+
+static void etrax_usb_free_epid(int epid)
+{
+ unsigned long flags;
+
+ DBFENTER;
+
+ if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
+ warn("Trying to free unused epid %d", epid);
+ DBFEXIT;
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
+ /* This will, among other things, set the valid field to 0. */
+ *R_USB_EPT_DATA = 0;
+ restore_flags(flags);
+
+ clear_bit(epid, (void *)&epid_usage_bitmask);
+
+
+ dbg_epid("Freed epid %d", epid);
+
+ DBFEXIT;
+}
+
+static int etrax_usb_lookup_epid(struct urb *urb)
+{
+ int i;
+ __u32 data;
+ char devnum, endpoint, slow, out_traffic;
+ int maxlen;
+ unsigned long flags;
+
+ DBFENTER;
+
+ devnum = usb_pipedevice(urb->pipe);
+ endpoint = usb_pipeendpoint(urb->pipe);
+ slow = usb_pipeslow(urb->pipe);
+ maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+ /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+ out_traffic = 1;
+ } else {
+ out_traffic = usb_pipeout(urb->pipe);
+ }
+
+ /* Step through att epids. */
+ for (i = 0; i < NBR_OF_EPIDS; i++) {
+ if (test_bit(i, (void *)&epid_usage_bitmask) &&
+ test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
+
+ save_flags(flags);
+ cli();
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
+ nop();
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ data = *R_USB_EPT_DATA_ISO;
+ restore_flags(flags);
+
+ if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
+ (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
+ (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
+ (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
+ dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+ i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+ DBFEXIT;
+ return i;
+ }
+ } else {
+ data = *R_USB_EPT_DATA;
+ restore_flags(flags);
+
+ if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
+ (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
+ (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
+ (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
+ (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {
+ dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+ i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+ DBFEXIT;
+ return i;
+ }
+ }
+ }
+ }
+
+ DBFEXIT;
+ return -1;
+}
+
+static int etrax_usb_allocate_epid(void)
+{
+ int i;
+
+ DBFENTER;
+
+ for (i = 0; i < NBR_OF_EPIDS; i++) {
+ if (!test_bit(i, (void *)&epid_usage_bitmask)) {
+ dbg_epid("Found free epid %d", i);
+ DBFEXIT;
+ return i;
+ }
+ }
+
+ dbg_epid("Found no free epids");
+ DBFEXIT;
+ return -1;
+}
+
+static int etrax_usb_submit_urb(struct urb *urb, int mem_flags)
+{
+ etrax_hc_t *hc;
+ int ret = -EINVAL;
+
+ DBFENTER;
+
+ if (!urb->dev || !urb->dev->bus) {
+ return -ENODEV;
+ }
+ if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
+ info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
+ return -EMSGSIZE;
+ }
+
+ if (urb->timeout) {
+ /* FIXME. */
+ warn("urb->timeout specified, ignoring.");
+ }
+
+ hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
+
+ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+ /* This request is for the Virtual Root Hub. */
+ ret = etrax_rh_submit_urb(urb);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+ ret = etrax_usb_submit_bulk_urb(urb);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+ ret = etrax_usb_submit_ctrl_urb(urb);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+ int bustime;
+
+ if (urb->bandwidth == 0) {
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+ } else {
+ ret = etrax_usb_submit_intr_urb(urb);
+ if (ret == 0)
+ usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+ }
+ } else {
+ /* Bandwidth already set. */
+ ret = etrax_usb_submit_intr_urb(urb);
+ }
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ int bustime;
+
+ if (urb->bandwidth == 0) {
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+ } else {
+ ret = etrax_usb_submit_isoc_urb(urb);
+ if (ret == 0)
+ usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+ }
+ } else {
+ /* Bandwidth already set. */
+ ret = etrax_usb_submit_isoc_urb(urb);
+ }
+ }
+
+ DBFEXIT;
+
+ if (ret != 0)
+ printk("Submit URB error %d\n", ret);
+
+ return ret;
+}
+
+static int etrax_usb_unlink_urb(struct urb *urb, int status)
+{
+ etrax_hc_t *hc;
+ etrax_urb_priv_t *urb_priv;
+ int epid;
+ unsigned int flags;
+
+ DBFENTER;
+
+ if (!urb) {
+ return -EINVAL;
+ }
+
+ /* Disable interrupts here since a descriptor interrupt for the isoc epid
+ will modify the sb list. This could possibly be done more granular, but
+ unlink_urb should not be used frequently anyway.
+ */
+
+ save_flags(flags);
+ cli();
+
+ if (!urb->dev || !urb->dev->bus) {
+ restore_flags(flags);
+ return -ENODEV;
+ }
+ if (!urb->hcpriv) {
+ /* This happens if a device driver calls unlink on an urb that
+ was never submitted (lazy driver) or if the urb was completed
+ while unlink was being called. */
+ restore_flags(flags);
+ return 0;
+ }
+ if (urb->transfer_flags & URB_ASYNC_UNLINK) {
+ /* FIXME. */
+ /* If URB_ASYNC_UNLINK is set:
+ unlink
+ move to a separate urb list
+ call complete at next sof with ECONNRESET
+
+ If not:
+ wait 1 ms
+ unlink
+ call complete with ENOENT
+ */
+ warn("URB_ASYNC_UNLINK set, ignoring.");
+ }
+
+ /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
+ but that doesn't work for interrupt and isochronous traffic since they are completed
+ repeatedly, and urb->status is set then. That may in itself be a bug though. */
+
+ hc = urb->dev->bus->hcpriv;
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ epid = urb_priv->epid;
+
+ /* Set the urb status (synchronous unlink). */
+ urb->status = -ENOENT;
+ urb_priv->urb_state = UNLINK;
+
+ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+ int ret;
+ ret = etrax_rh_unlink_urb(urb);
+ DBFEXIT;
+ restore_flags(flags);
+ return ret;
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+ dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+
+ if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+ /* The EP was enabled, disable it and wait. */
+ TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+ /* Ah, the luxury of busy-wait. */
+ while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+ }
+ /* Kicking dummy list out of the party. */
+ TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+ dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
+
+ if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+ /* The EP was enabled, disable it and wait. */
+ TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+ /* Ah, the luxury of busy-wait. */
+ while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+ }
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+ dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
+
+ /* Separate function because it's a tad more complicated. */
+ etrax_usb_unlink_intr_urb(urb);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+ dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+
+ if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+ /* The EP was enabled, disable it and wait. */
+ TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+ /* Ah, the luxury of busy-wait. */
+ while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+ }
+ }
+
+ /* Note that we need to remove the urb from the urb list *before* removing its SB
+ descriptors. (This means that the isoc eof handler might get a null urb when we
+ are unlinking the last urb.) */
+
+ if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+ urb_list_del(urb, epid);
+ TxBulkEPList[epid].sub = 0;
+ etrax_remove_from_sb_list(urb);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+ urb_list_del(urb, epid);
+ TxCtrlEPList[epid].sub = 0;
+ etrax_remove_from_sb_list(urb);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+ urb_list_del(urb, epid);
+ /* Sanity check (should never happen). */
+ assert(urb_list_empty(epid));
+
+ /* Release allocated bandwidth. */
+ usb_release_bandwidth(urb->dev, urb, 0);
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+ if (usb_pipeout(urb->pipe)) {
+
+ USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
+
+ if (__urb_list_entry(urb, epid)) {
+
+ urb_list_del(urb, epid);
+ iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+ prev_sb = 0;
+ while (iter_sb && (iter_sb != urb_priv->first_sb)) {
+ prev_sb = iter_sb;
+ iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+ }
+
+ if (iter_sb == 0) {
+ /* Unlink of the URB currently being transmitted. */
+ prev_sb = 0;
+ iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+ }
+
+ while (iter_sb && (iter_sb != urb_priv->last_sb)) {
+ iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+ }
+ if (iter_sb) {
+ next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+ } else {
+ /* This should only happen if the DMA has completed
+ processing the SB list for this EP while interrupts
+ are disabled. */
+ dbg_isoc("Isoc urb not found, already sent?");
+ next_sb = 0;
+ }
+ if (prev_sb) {
+ prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
+ } else {
+ TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
+ }
+
+ etrax_remove_from_sb_list(urb);
+ if (urb_list_empty(epid)) {
+ TxIsocEPList[epid].sub = 0;
+ dbg_isoc("Last isoc out urb epid %d", epid);
+ } else if (next_sb || prev_sb) {
+ dbg_isoc("Re-enable isoc out epid %d", epid);
+
+ TxIsocEPList[epid].hw_len = 0;
+ TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+ } else {
+ TxIsocEPList[epid].sub = 0;
+ dbg_isoc("URB list non-empty and no SB list, EP disabled");
+ }
+ } else {
+ dbg_isoc("Urb 0x%p not found, completed already?", urb);
+ }
+ } else {
+
+ urb_list_del(urb, epid);
+
+ /* For in traffic there is only one SB descriptor for each EP even
+ though there may be several urbs (all urbs point at the same SB). */
+ if (urb_list_empty(epid)) {
+ /* No more urbs, remove the SB. */
+ TxIsocEPList[epid].sub = 0;
+ etrax_remove_from_sb_list(urb);
+ } else {
+ TxIsocEPList[epid].hw_len = 0;
+ TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+ }
+ }
+ /* Release allocated bandwidth. */
+ usb_release_bandwidth(urb->dev, urb, 1);
+ }
+ /* Free the epid if urb list is empty. */
+ if (urb_list_empty(epid)) {
+ etrax_usb_free_epid(epid);
+ }
+ restore_flags(flags);
+
+ /* Must be done before calling completion handler. */
+ kfree(urb_priv);
+ urb->hcpriv = 0;
+
+ if (urb->complete) {
+ urb->complete(urb, NULL);
+ }
+
+ DBFEXIT;
+ return 0;
+}
+
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+{
+ DBFENTER;
+ DBFEXIT;
+ return (*R_USB_FM_NUMBER & 0x7ff);
+}
+
+static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+ DBFENTER;
+
+ /* This interrupt handler could be used when unlinking EP descriptors. */
+
+ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
+ USB_EP_Desc_t *ep;
+
+ //dbg_bulk("dma8_sub0_descr (BULK) intr.");
+
+ /* It should be safe clearing the interrupt here, since we don't expect to get a new
+ one until we restart the bulk channel. */
+ *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
+
+ /* Wait while the DMA is running (though we don't expect it to be). */
+ while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
+
+ /* Advance the DMA to the next EP descriptor. */
+ ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+
+ //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
+
+ /* ep->next is already a physical address; no need for a virt_to_phys. */
+ *R_DMA_CH8_SUB0_EP = ep->next;
+
+ /* Start the DMA bulk channel again. */
+ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+ }
+ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+ struct urb *urb;
+ int epid;
+ etrax_urb_priv_t *urb_priv;
+ unsigned long int flags;
+
+ dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
+ *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+
+ /* The complete callback gets called so we cli. */
+ save_flags(flags);
+ cli();
+
+ for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+ if ((TxCtrlEPList[epid].sub == 0) ||
+ (epid == DUMMY_EPID) ||
+ (epid == INVALID_EPID)) {
+ /* Nothing here to see. */
+ continue;
+ }
+
+ /* Get the first urb (if any). */
+ urb = urb_list_first(epid);
+
+ if (urb) {
+
+ /* Sanity check. */
+ assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
+
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
+ assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+
+ etrax_usb_complete_urb(urb, 0);
+ }
+ }
+ }
+ restore_flags(flags);
+ }
+ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
+ dbg_intr("dma8_sub2_descr (INTR) intr.");
+ *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
+ }
+ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
+ struct urb *urb;
+ int epid;
+ int epid_done;
+ etrax_urb_priv_t *urb_priv;
+ USB_SB_Desc_t *sb_desc;
+
+ usb_isoc_complete_data_t *comp_data = NULL;
+
+ /* One or more isoc out transfers are done. */
+ dbg_isoc("dma8_sub3_descr (ISOC) intr.");
+
+ /* For each isoc out EP search for the first sb_desc with the intr flag
+ set. This descriptor must be the last packet from an URB. Then
+ traverse the URB list for the EP until the URB with urb_priv->last_sb
+ matching the intr-marked sb_desc is found. All URBs before this have
+ been sent.
+ */
+
+ for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+ /* Skip past epids with no SB lists, epids used for in traffic,
+ and special (dummy, invalid) epids. */
+ if ((TxIsocEPList[epid].sub == 0) ||
+ (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
+ (epid == DUMMY_EPID) ||
+ (epid == INVALID_EPID)) {
+ /* Nothing here to see. */
+ continue;
+ }
+ sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+
+ /* Find the last descriptor of the currently active URB for this ep.
+ This is the first descriptor in the sub list marked for a descriptor
+ interrupt. */
+ while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
+ sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
+ }
+ assert(sb_desc);
+
+ dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
+ epid,
+ phys_to_virt(TxIsocEPList[epid].sub),
+ sb_desc);
+
+ epid_done = 0;
+
+ /* Get the first urb (if any). */
+ urb = urb_list_first(epid);
+ assert(urb);
+
+ while (urb && !epid_done) {
+
+ /* Sanity check. */
+ assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+
+ if (!usb_pipeout(urb->pipe)) {
+ /* descr interrupts are generated only for out pipes. */
+ epid_done = 1;
+ continue;
+ }
+
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ if (sb_desc != urb_priv->last_sb) {
+
+ /* This urb has been sent. */
+ dbg_isoc("out URB 0x%p sent", urb);
+
+ urb_priv->urb_state = TRANSFER_DONE;
+
+ } else if ((sb_desc == urb_priv->last_sb) &&
+ !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+
+ assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
+ assert(sb_desc->next == 0);
+
+ dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
+ TxIsocEPList[epid].sub = 0;
+ TxIsocEPList[epid].hw_len = 0;
+ urb_priv->urb_state = TRANSFER_DONE;
+
+ epid_done = 1;
+
+ } else {
+ epid_done = 1;
+ }
+ if (!epid_done) {
+ urb = urb_list_next(urb, epid);
+ }
+ }
+
+ }
+
+ *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
+
+ comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, SLAB_ATOMIC);
+ assert(comp_data != NULL);
+
+ INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data);
+ schedule_work(&comp_data->usb_bh);
+ }
+
+ DBFEXIT;
+ return IRQ_HANDLED;
+}
+
+static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
+{
+ usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
+
+ struct urb *urb;
+ int epid;
+ int epid_done;
+ etrax_urb_priv_t *urb_priv;
+
+ DBFENTER;
+
+ dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
+
+ for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ epid_done = 0;
+
+ /* The descriptor interrupt handler has marked all transmitted isoch. out
+ URBs with TRANSFER_DONE. Now we traverse all epids and for all that
+ have isoch. out traffic traverse its URB list and complete the
+ transmitted URB.
+ */
+
+ while (!epid_done) {
+
+ /* Get the first urb (if any). */
+ urb = urb_list_first(epid);
+ if (urb == 0) {
+ epid_done = 1;
+ continue;
+ }
+
+ if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+ epid_done = 1;
+ continue;
+ }
+
+ if (!usb_pipeout(urb->pipe)) {
+ /* descr interrupts are generated only for out pipes. */
+ epid_done = 1;
+ continue;
+ }
+
+ dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
+
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ if (urb_priv->urb_state == TRANSFER_DONE) {
+ int i;
+ struct usb_iso_packet_descriptor *packet;
+
+ /* This urb has been sent. */
+ dbg_isoc("Completing isoc out URB 0x%p", urb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ packet = &urb->iso_frame_desc[i];
+ packet->status = 0;
+ packet->actual_length = packet->length;
+ }
+
+ etrax_usb_complete_isoc_urb(urb, 0);
+
+ if (urb_list_empty(epid)) {
+ etrax_usb_free_epid(epid);
+ epid_done = 1;
+ }
+ } else {
+ epid_done = 1;
+ }
+ }
+ restore_flags(flags);
+
+ }
+ kmem_cache_free(isoc_compl_cache, comp_data);
+
+ DBFEXIT;
+}
+
+
+
+static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+ struct urb *urb;
+ etrax_urb_priv_t *urb_priv;
+ int epid = 0;
+ unsigned long flags;
+
+ /* Isoc diagnostics. */
+ static int curr_fm = 0;
+ static int prev_fm = 0;
+
+ DBFENTER;
+
+ /* Clear this interrupt. */
+ *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+
+ /* Note that this while loop assumes that all packets span only
+ one rx descriptor. */
+
+ /* The reason we cli here is that we call the driver's callback functions. */
+ save_flags(flags);
+ cli();
+
+ while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+
+ epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+ urb = urb_list_first(epid);
+
+ //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
+
+ if (!urb) {
+ err("No urb for epid %d in rx interrupt", epid);
+ __dump_ept_data(epid);
+ goto skip_out;
+ }
+
+ /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
+ ctrl pipes are not. */
+
+ if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+ __u32 r_usb_ept_data;
+ int no_error = 0;
+
+ assert(test_bit(epid, (void *)&epid_usage_bitmask));
+
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ r_usb_ept_data = *R_USB_EPT_DATA_ISO;
+
+ if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
+ (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
+ (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
+ /* Not an error, just a failure to receive an expected iso
+ in packet in this frame. This is not documented
+ in the designers reference.
+ */
+ no_error++;
+ } else {
+ warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
+ }
+ } else {
+ r_usb_ept_data = *R_USB_EPT_DATA;
+ warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
+ }
+
+ if (!no_error){
+ warn("error in rx desc->status, epid %d, first urb = 0x%lx",
+ epid, (unsigned long)urb);
+ __dump_in_desc(myNextRxDesc);
+
+ warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
+
+ /* Check that ept was disabled when error occurred. */
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_BULK:
+ assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+ break;
+ case PIPE_CONTROL:
+ assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+ break;
+ case PIPE_INTERRUPT:
+ assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+ break;
+ case PIPE_ISOCHRONOUS:
+ assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+ break;
+ default:
+ warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
+ usb_pipetype(urb->pipe),
+ urb);
+ }
+ etrax_usb_complete_urb(urb, -EPROTO);
+ goto skip_out;
+ }
+ }
+
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ if ((usb_pipetype(urb->pipe) == PIPE_BULK) ||
+ (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
+ (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+
+ if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+ /* We get nodata for empty data transactions, and the rx descriptor's
+ hw_len field is not valid in that case. No data to copy in other
+ words. */
+ } else {
+ /* Make sure the data fits in the buffer. */
+ assert(urb_priv->rx_offset + myNextRxDesc->hw_len
+ <= urb->transfer_buffer_length);
+
+ memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+ phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
+ urb_priv->rx_offset += myNextRxDesc->hw_len;
+ }
+
+ if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+ if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
+ ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
+ IO_STATE(USB_EP_command, enable, yes))) {
+ /* The EP is still enabled, so the OUT packet used to ack
+ the in data is probably not processed yet. If the EP
+ sub pointer has not moved beyond urb_priv->last_sb mark
+ it for a descriptor interrupt and complete the urb in
+ the descriptor interrupt handler.
+ */
+ USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
+
+ while ((sub != NULL) && (sub != urb_priv->last_sb)) {
+ sub = sub->next ? phys_to_virt(sub->next) : 0;
+ }
+ if (sub != NULL) {
+ /* The urb has not been fully processed. */
+ urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
+ } else {
+ warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
+ etrax_usb_complete_urb(urb, 0);
+ }
+ } else {
+ etrax_usb_complete_urb(urb, 0);
+ }
+ }
+
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+ struct usb_iso_packet_descriptor *packet;
+
+ if (urb_priv->urb_state == UNLINK) {
+ info("Ignoring rx data for urb being unlinked.");
+ goto skip_out;
+ } else if (urb_priv->urb_state == NOT_STARTED) {
+ info("What? Got rx data for urb that isn't started?");
+ goto skip_out;
+ }
+
+ packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
+ packet->status = 0;
+
+ if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+ /* We get nodata for empty data transactions, and the rx descriptor's
+ hw_len field is not valid in that case. We copy 0 bytes however to
+ stay in synch. */
+ packet->actual_length = 0;
+ } else {
+ packet->actual_length = myNextRxDesc->hw_len;
+ /* Make sure the data fits in the buffer. */
+ assert(packet->actual_length <= packet->length);
+ memcpy(urb->transfer_buffer + packet->offset,
+ phys_to_virt(myNextRxDesc->buf), packet->actual_length);
+ }
+
+ /* Increment the packet counter. */
+ urb_priv->isoc_packet_counter++;
+
+ /* Note that we don't care about the eot field in the rx descriptor's status.
+ It will always be set for isoc traffic. */
+ if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+
+ /* Out-of-synch diagnostics. */
+ curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
+ if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
+ /* This test is wrong, if there is more than one isoc
+ in endpoint active it will always calculate wrong
+ since prev_fm is shared by all endpoints.
+
+ FIXME Make this check per URB using urb->start_frame.
+ */
+ dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
+ prev_fm, curr_fm);
+
+ }
+ prev_fm = curr_fm;
+
+ /* Complete the urb with status OK. */
+ etrax_usb_complete_isoc_urb(urb, 0);
+ }
+ }
+
+ skip_out:
+
+ /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
+ has the same layout as USB_IN_Desc for the relevant fields.) */
+ prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+
+ myPrevRxDesc = myNextRxDesc;
+ myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+ myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+ myLastRxDesc = myPrevRxDesc;
+
+ myNextRxDesc->status = 0;
+ myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+ }
+
+ restore_flags(flags);
+
+ DBFEXIT;
+
+ return IRQ_HANDLED;
+}
+
+
+/* This function will unlink the SB descriptors associated with this urb. */
+static int etrax_remove_from_sb_list(struct urb *urb)
+{
+ USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+ etrax_urb_priv_t *urb_priv;
+ int i = 0;
+
+ DBFENTER;
+
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
+ doesn't really need to be disabled, it's just that we expect it to be. */
+ if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+ assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+ } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+ assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+ }
+
+ first_sb = urb_priv->first_sb;
+ last_sb = urb_priv->last_sb;
+
+ assert(first_sb);
+ assert(last_sb);
+
+ while (first_sb != last_sb) {
+ next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
+ kmem_cache_free(usb_desc_cache, first_sb);
+ first_sb = next_sb;
+ i++;
+ }
+ kmem_cache_free(usb_desc_cache, last_sb);
+ i++;
+ dbg_sb("%d SB descriptors freed", i);
+ /* Compare i with urb->number_of_packets for Isoc traffic.
+ Should be same when calling unlink_urb */
+
+ DBFEXIT;
+
+ return i;
+}
+
+static int etrax_usb_submit_bulk_urb(struct urb *urb)
+{
+ int epid;
+ int empty;
+ unsigned long flags;
+ etrax_urb_priv_t *urb_priv;
+
+ DBFENTER;
+
+ /* Epid allocation, empty check and list add must be protected.
+ Read about this in etrax_usb_submit_ctrl_urb. */
+
+ spin_lock_irqsave(&urb_list_lock, flags);
+ epid = etrax_usb_setup_epid(urb);
+ if (epid == -1) {
+ DBFEXIT;
+ spin_unlock_irqrestore(&urb_list_lock, flags);
+ return -ENOMEM;
+ }
+ empty = urb_list_empty(epid);
+ urb_list_add(urb, epid);
+ spin_unlock_irqrestore(&urb_list_lock, flags);
+
+ dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d",
+ usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+
+ /* Mark the urb as being in progress. */
+ urb->status = -EINPROGRESS;
+
+ /* Setup the hcpriv data. */
+ urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+ assert(urb_priv != NULL);
+ /* This sets rx_offset to 0. */
+ memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+ urb_priv->urb_state = NOT_STARTED;
+ urb->hcpriv = urb_priv;
+
+ if (empty) {
+ etrax_usb_add_to_bulk_sb_list(urb, epid);
+ }
+
+ DBFEXIT;
+
+ return 0;
+}
+
+static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid)
+{
+ USB_SB_Desc_t *sb_desc;
+ etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ unsigned long flags;
+ char maxlen;
+
+ DBFENTER;
+
+ dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+
+ maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ assert(sb_desc != NULL);
+ memset(sb_desc, 0, sizeof(USB_SB_Desc_t));
+
+
+ if (usb_pipeout(urb->pipe)) {
+
+ dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+
+ /* This is probably a sanity check of the bulk transaction length
+ not being larger than 64 kB. */
+ if (urb->transfer_buffer_length > 0xffff) {
+ panic("urb->transfer_buffer_length > 0xffff");
+ }
+
+ sb_desc->sw_len = urb->transfer_buffer_length;
+
+ /* The rem field is don't care if it's not a full-length transfer, so setting
+ it shouldn't hurt. Also, rem isn't used for OUT traffic. */
+ sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, out) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ /* The full field is set to yes, even if we don't actually check that this is
+ a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
+ Setting full prevents the USB controller from sending an empty packet in
+ that case. However, if URB_ZERO_PACKET was set we want that. */
+ if (!(urb->transfer_flags & URB_ZERO_PACKET)) {
+ sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+ }
+
+ sb_desc->buf = virt_to_phys(urb->transfer_buffer);
+ sb_desc->next = 0;
+
+ } else if (usb_pipein(urb->pipe)) {
+
+ dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+
+ sb_desc->sw_len = urb->transfer_buffer_length ?
+ (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+
+ /* The rem field is don't care if it's not a full-length transfer, so setting
+ it shouldn't hurt. */
+ sb_desc->command =
+ (IO_FIELD(USB_SB_command, rem,
+ urb->transfer_buffer_length % maxlen) |
+ IO_STATE(USB_SB_command, tt, in) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ sb_desc->buf = 0;
+ sb_desc->next = 0;
+ }
+
+ urb_priv->first_sb = sb_desc;
+ urb_priv->last_sb = sb_desc;
+ urb_priv->epid = epid;
+
+ urb->hcpriv = urb_priv;
+
+ /* Reset toggle bits and reset error count. */
+ save_flags(flags);
+ cli();
+
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+
+ /* FIXME: Is this a special case since the hold field is checked,
+ or should we check hold in a lot of other cases as well? */
+ if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+ panic("Hold was set in %s", __FUNCTION__);
+ }
+
+ /* Reset error counters (regardless of which direction this traffic is). */
+ *R_USB_EPT_DATA &=
+ ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+ IO_MASK(R_USB_EPT_DATA, error_count_out));
+
+ /* Software must preset the toggle bits. */
+ if (usb_pipeout(urb->pipe)) {
+ char toggle =
+ usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+ *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
+ *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
+ } else {
+ char toggle =
+ usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+ *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
+ *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
+ }
+
+ /* Assert that the EP descriptor is disabled. */
+ assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+ /* The reason we set the EP's sub pointer directly instead of
+ walking the SB list and linking it last in the list is that we only
+ have one active urb at a time (the rest are queued). */
+
+ /* Note that we cannot have interrupts running when we have set the SB descriptor
+ but the EP is not yet enabled. If a bulk eot happens for another EP, we will
+ find this EP disabled and with a SB != 0, which will make us think that it's done. */
+ TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
+ TxBulkEPList[epid].hw_len = 0;
+ /* Note that we don't have to fill in the ep_id field since this
+ was done when we allocated the EP descriptors in init_tx_bulk_ep. */
+
+ /* Check if the dummy list is already with us (if several urbs were queued). */
+ if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
+
+ dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d",
+ (unsigned long)urb, epid);
+
+ /* The last EP in the dummy list already has its next pointer set to
+ TxBulkEPList[epid].next. */
+
+ /* We don't need to check if the DMA is at this EP or not before changing the
+ next pointer, since we will do it in one 32-bit write (EP descriptors are
+ 32-bit aligned). */
+ TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
+ }
+ /* Enable the EP descr. */
+ dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
+ TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+ /* Everything is set up, safe to enable interrupts again. */
+ restore_flags(flags);
+
+ /* If the DMA bulk channel isn't running, we need to restart it if it
+ has stopped at the last EP descriptor (DMA stopped because there was
+ no more traffic) or if it has stopped at a dummy EP with the intr flag
+ set (DMA stopped because we were too slow in inserting new traffic). */
+ if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+
+ USB_EP_Desc_t *ep;
+ ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+ dbg_bulk("DMA channel not running in add");
+ dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
+
+ if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
+ (ep->command & 0x8) >> 3) {
+ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+ /* Update/restart the bulk start timer since we just started the channel. */
+ mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+ /* Update/restart the bulk eot timer since we just inserted traffic. */
+ mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+ }
+ }
+
+ DBFEXIT;
+}
+
+static void etrax_usb_complete_bulk_urb(struct urb *urb, int status)
+{
+ etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ int epid = urb_priv->epid;
+ unsigned long flags;
+
+ DBFENTER;
+
+ if (status)
+ warn("Completing bulk urb with status %d.", status);
+
+ dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
+
+ /* Update the urb list. */
+ urb_list_del(urb, epid);
+
+ /* For an IN pipe, we always set the actual length, regardless of whether there was
+ an error or not (which means the device driver can use the data if it wants to). */
+ if (usb_pipein(urb->pipe)) {
+ urb->actual_length = urb_priv->rx_offset;
+ } else {
+ /* Set actual_length for OUT urbs also; the USB mass storage driver seems
+ to want that. We wouldn't know of any partial writes if there was an error. */
+ if (status == 0) {
+ urb->actual_length = urb->transfer_buffer_length;
+ } else {
+ urb->actual_length = 0;
+ }
+ }
+
+ /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+ Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
+
+ save_flags(flags);
+ cli();
+
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+
+ /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
+ if (usb_pipeout(urb->pipe)) {
+ char toggle =
+ IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), toggle);
+ } else {
+ char toggle =
+ IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), toggle);
+ }
+ restore_flags(flags);
+
+ /* Remember to free the SBs. */
+ etrax_remove_from_sb_list(urb);
+ kfree(urb_priv);
+ urb->hcpriv = 0;
+
+ /* If there are any more urb's in the list we'd better start sending */
+ if (!urb_list_empty(epid)) {
+
+ struct urb *new_urb;
+
+ /* Get the first urb. */
+ new_urb = urb_list_first(epid);
+ assert(new_urb);
+
+ dbg_bulk("More bulk for epid %d", epid);
+
+ etrax_usb_add_to_bulk_sb_list(new_urb, epid);
+ }
+
+ urb->status = status;
+
+ /* We let any non-zero status from the layer above have precedence. */
+ if (status == 0) {
+ /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+ is to be treated as an error. */
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ if (usb_pipein(urb->pipe) &&
+ (urb->actual_length !=
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+ urb->status = -EREMOTEIO;
+ }
+ }
+ }
+
+ if (urb->complete) {
+ urb->complete(urb, NULL);
+ }
+
+ if (urb_list_empty(epid)) {
+ /* This means that this EP is now free, deconfigure it. */
+ etrax_usb_free_epid(epid);
+
+ /* No more traffic; time to clean up.
+ Must set sub pointer to 0, since we look at the sub pointer when handling
+ the bulk eot interrupt. */
+
+ dbg_bulk("No bulk for epid %d", epid);
+
+ TxBulkEPList[epid].sub = 0;
+
+ /* Unlink the dummy list. */
+
+ dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d",
+ (unsigned long)urb, epid);
+
+ /* No need to wait for the DMA before changing the next pointer.
+ The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
+ the last one (INVALID_EPID) for actual traffic. */
+ TxBulkEPList[epid].next =
+ virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+ }
+
+ DBFEXIT;
+}
+
+static int etrax_usb_submit_ctrl_urb(struct urb *urb)
+{
+ int epid;
+ int empty;
+ unsigned long flags;
+ etrax_urb_priv_t *urb_priv;
+
+ DBFENTER;
+
+ /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
+
+ /* Epid allocation, empty check and list add must be protected.
+
+ Epid allocation because if we find an existing epid for this endpoint an urb might be
+ completed (emptying the list) before we add the new urb to the list, causing the epid
+ to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
+
+ Empty check and add because otherwise we might conclude that the list is not empty,
+ after which it becomes empty before we add the new urb to the list, causing us not to
+ insert the new traffic into the SB list. */
+
+ spin_lock_irqsave(&urb_list_lock, flags);
+ epid = etrax_usb_setup_epid(urb);
+ if (epid == -1) {
+ spin_unlock_irqrestore(&urb_list_lock, flags);
+ DBFEXIT;
+ return -ENOMEM;
+ }
+ empty = urb_list_empty(epid);
+ urb_list_add(urb, epid);
+ spin_unlock_irqrestore(&urb_list_lock, flags);
+
+ dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d",
+ (unsigned long)urb, empty ? "empty" : "", epid);
+
+ /* Mark the urb as being in progress. */
+ urb->status = -EINPROGRESS;
+
+ /* Setup the hcpriv data. */
+ urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+ assert(urb_priv != NULL);
+ /* This sets rx_offset to 0. */
+ memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+ urb_priv->urb_state = NOT_STARTED;
+ urb->hcpriv = urb_priv;
+
+ if (empty) {
+ etrax_usb_add_to_ctrl_sb_list(urb, epid);
+ }
+
+ DBFEXIT;
+
+ return 0;
+}
+
+static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid)
+{
+ USB_SB_Desc_t *sb_desc_setup;
+ USB_SB_Desc_t *sb_desc_data;
+ USB_SB_Desc_t *sb_desc_status;
+
+ etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+
+ unsigned long flags;
+ char maxlen;
+
+ DBFENTER;
+
+ maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ assert(sb_desc_setup != NULL);
+ sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ assert(sb_desc_status != NULL);
+
+ /* Initialize the mandatory setup SB descriptor (used only in control transfers) */
+ sb_desc_setup->sw_len = 8;
+ sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, setup) |
+ IO_STATE(USB_SB_command, full, yes) |
+ IO_STATE(USB_SB_command, eot, yes));
+
+ sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
+
+ if (usb_pipeout(urb->pipe)) {
+ dbg_ctrl("Transfer for epid %d is OUT", epid);
+
+ /* If this Control OUT transfer has an optional data stage we add an OUT token
+ before the mandatory IN (status) token, hence the reordered SB list */
+
+ sb_desc_setup->next = virt_to_phys(sb_desc_status);
+ if (urb->transfer_buffer) {
+
+ dbg_ctrl("This OUT transfer has an extra data stage");
+
+ sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ assert(sb_desc_data != NULL);
+
+ sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+ sb_desc_data->sw_len = urb->transfer_buffer_length;
+ sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
+ IO_STATE(USB_SB_command, full, yes) |
+ IO_STATE(USB_SB_command, eot, yes));
+ sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
+ sb_desc_data->next = virt_to_phys(sb_desc_status);
+ }
+
+ sb_desc_status->sw_len = 1;
+ sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, in) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, intr, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ sb_desc_status->buf = 0;
+ sb_desc_status->next = 0;
+
+ } else if (usb_pipein(urb->pipe)) {
+
+ dbg_ctrl("Transfer for epid %d is IN", epid);
+ dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
+ dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
+
+ sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ assert(sb_desc_data != NULL);
+
+ sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+ sb_desc_data->sw_len = urb->transfer_buffer_length ?
+ (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+ dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
+
+ sb_desc_data->command =
+ (IO_FIELD(USB_SB_command, rem,
+ urb->transfer_buffer_length % maxlen) |
+ IO_STATE(USB_SB_command, tt, in) |
+ IO_STATE(USB_SB_command, eot, yes));
+
+ sb_desc_data->buf = 0;
+ sb_desc_data->next = virt_to_phys(sb_desc_status);
+
+ /* Read comment at zout_buffer declaration for an explanation to this. */
+ sb_desc_status->sw_len = 1;
+ sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, zout) |
+ IO_STATE(USB_SB_command, full, yes) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, intr, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
+ sb_desc_status->next = 0;
+ }
+
+ urb_priv->first_sb = sb_desc_setup;
+ urb_priv->last_sb = sb_desc_status;
+ urb_priv->epid = epid;
+
+ urb_priv->urb_state = STARTED;
+
+ /* Reset toggle bits and reset error count, remember to di and ei */
+ /* Warning: it is possible that this locking doesn't work with bottom-halves */
+
+ save_flags(flags);
+ cli();
+
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+ panic("Hold was set in %s", __FUNCTION__);
+ }
+
+
+ /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
+ are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
+ in Designer's Reference, p. 8 - 11. */
+ *R_USB_EPT_DATA &=
+ ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+ IO_MASK(R_USB_EPT_DATA, error_count_out) |
+ IO_MASK(R_USB_EPT_DATA, t_in) |
+ IO_MASK(R_USB_EPT_DATA, t_out));
+
+ /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
+ (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
+ restore_flags(flags);
+
+ /* Assert that the EP descriptor is disabled. */
+ assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+ /* Set up and enable the EP descriptor. */
+ TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
+ TxCtrlEPList[epid].hw_len = 0;
+ TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+ /* We start the DMA sub channel without checking if it's running or not, because:
+ 1) If it's already running, issuing the start command is a nop.
+ 2) We avoid a test-and-set race condition. */
+ *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+
+ DBFEXIT;
+}
+
+static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status)
+{
+ etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ int epid = urb_priv->epid;
+
+ DBFENTER;
+
+ if (status)
+ warn("Completing ctrl urb with status %d.", status);
+
+ dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
+
+ /* Remove this urb from the list. */
+ urb_list_del(urb, epid);
+
+ /* For an IN pipe, we always set the actual length, regardless of whether there was
+ an error or not (which means the device driver can use the data if it wants to). */
+ if (usb_pipein(urb->pipe)) {
+ urb->actual_length = urb_priv->rx_offset;
+ }
+
+ /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+ Like, maybe we shouldn't insert more traffic. */
+
+ /* Remember to free the SBs. */
+ etrax_remove_from_sb_list(urb);
+ kfree(urb_priv);
+ urb->hcpriv = 0;
+
+ /* If there are any more urbs in the list we'd better start sending. */
+ if (!urb_list_empty(epid)) {
+ struct urb *new_urb;
+
+ /* Get the first urb. */
+ new_urb = urb_list_first(epid);
+ assert(new_urb);
+
+ dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
+
+ etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
+ }
+
+ urb->status = status;
+
+ /* We let any non-zero status from the layer above have precedence. */
+ if (status == 0) {
+ /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+ is to be treated as an error. */
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ if (usb_pipein(urb->pipe) &&
+ (urb->actual_length !=
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+ urb->status = -EREMOTEIO;
+ }
+ }
+ }
+
+ if (urb->complete) {
+ urb->complete(urb, NULL);
+ }
+
+ if (urb_list_empty(epid)) {
+ /* No more traffic. Time to clean up. */
+ etrax_usb_free_epid(epid);
+ /* Must set sub pointer to 0. */
+ dbg_ctrl("No ctrl for epid %d", epid);
+ TxCtrlEPList[epid].sub = 0;
+ }
+
+ DBFEXIT;
+}
+
+static int etrax_usb_submit_intr_urb(struct urb *urb)
+{
+
+ int epid;
+
+ DBFENTER;
+
+ if (usb_pipeout(urb->pipe)) {
+ /* Unsupported transfer type.
+ We don't support interrupt out traffic. (If we do, we can't support
+ intervals for neither in or out traffic, but are forced to schedule all
+ interrupt traffic in one frame.) */
+ return -EINVAL;
+ }
+
+ epid = etrax_usb_setup_epid(urb);
+ if (epid == -1) {
+ DBFEXIT;
+ return -ENOMEM;
+ }
+
+ if (!urb_list_empty(epid)) {
+ /* There is already a queued urb for this endpoint. */
+ etrax_usb_free_epid(epid);
+ return -ENXIO;
+ }
+
+ urb->status = -EINPROGRESS;
+
+ dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
+
+ urb_list_add(urb, epid);
+ etrax_usb_add_to_intr_sb_list(urb, epid);
+
+ return 0;
+
+ DBFEXIT;
+}
+
+static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
+{
+
+ volatile USB_EP_Desc_t *tmp_ep;
+ volatile USB_EP_Desc_t *first_ep;
+
+ char maxlen;
+ int interval;
+ int i;
+
+ etrax_urb_priv_t *urb_priv;
+
+ DBFENTER;
+
+ maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ interval = urb->interval;
+
+ urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+ assert(urb_priv != NULL);
+ memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+ urb->hcpriv = urb_priv;
+
+ first_ep = &TxIntrEPList[0];
+
+ /* Round of the interval to 2^n, it is obvious that this code favours
+ smaller numbers, but that is actually a good thing */
+ /* FIXME: The "rounding error" for larger intervals will be quite
+ large. For in traffic this shouldn't be a problem since it will only
+ mean that we "poll" more often. */
+ for (i = 0; interval; i++) {
+ interval = interval >> 1;
+ }
+ interval = 1 << (i - 1);
+
+ dbg_intr("Interval rounded to %d", interval);
+
+ tmp_ep = first_ep;
+ i = 0;
+ do {
+ if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
+ if ((i % interval) == 0) {
+ /* Insert the traffic ep after tmp_ep */
+ USB_EP_Desc_t *ep_desc;
+ USB_SB_Desc_t *sb_desc;
+
+ dbg_intr("Inserting EP for epid %d", epid);
+
+ ep_desc = (USB_EP_Desc_t *)
+ kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ sb_desc = (USB_SB_Desc_t *)
+ kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+ assert(ep_desc != NULL);
+ CHECK_ALIGN(ep_desc);
+ assert(sb_desc != NULL);
+
+ ep_desc->sub = virt_to_phys(sb_desc);
+ ep_desc->hw_len = 0;
+ ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
+ IO_STATE(USB_EP_command, enable, yes));
+
+
+ /* Round upwards the number of packets of size maxlen
+ that this SB descriptor should receive. */
+ sb_desc->sw_len = urb->transfer_buffer_length ?
+ (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+ sb_desc->next = 0;
+ sb_desc->buf = 0;
+ sb_desc->command =
+ (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
+ IO_STATE(USB_SB_command, tt, in) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ ep_desc->next = tmp_ep->next;
+ tmp_ep->next = virt_to_phys(ep_desc);
+ }
+ i++;
+ }
+ tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+ } while (tmp_ep != first_ep);
+
+
+ /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
+ urb_priv->epid = epid;
+
+ /* We start the DMA sub channel without checking if it's running or not, because:
+ 1) If it's already running, issuing the start command is a nop.
+ 2) We avoid a test-and-set race condition. */
+ *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+
+ DBFEXIT;
+}
+
+
+
+static void etrax_usb_complete_intr_urb(struct urb *urb, int status)
+{
+ etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ int epid = urb_priv->epid;
+
+ DBFENTER;
+
+ if (status)
+ warn("Completing intr urb with status %d.", status);
+
+ dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
+
+ urb->status = status;
+ urb->actual_length = urb_priv->rx_offset;
+
+ dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
+
+ /* We let any non-zero status from the layer above have precedence. */
+ if (status == 0) {
+ /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+ is to be treated as an error. */
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ if (urb->actual_length !=
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+ urb->status = -EREMOTEIO;
+ }
+ }
+ }
+
+ /* The driver will resubmit the URB so we need to remove it first */
+ etrax_usb_unlink_urb(urb, 0);
+ if (urb->complete) {
+ urb->complete(urb, NULL);
+ }
+
+ DBFEXIT;
+}
+
+
+static int etrax_usb_submit_isoc_urb(struct urb *urb)
+{
+ int epid;
+ unsigned long flags;
+
+ DBFENTER;
+
+ dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
+
+ /* Epid allocation, empty check and list add must be protected.
+ Read about this in etrax_usb_submit_ctrl_urb. */
+
+ spin_lock_irqsave(&urb_list_lock, flags);
+ /* Is there an active epid for this urb ? */
+ epid = etrax_usb_setup_epid(urb);
+ if (epid == -1) {
+ DBFEXIT;
+ spin_unlock_irqrestore(&urb_list_lock, flags);
+ return -ENOMEM;
+ }
+
+ /* Ok, now we got valid endpoint, lets insert some traffic */
+
+ urb->status = -EINPROGRESS;
+
+ /* Find the last urb in the URB_List and add this urb after that one.
+ Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list. This
+ is important to make this in "real time" since isochronous traffic is
+ time sensitive. */
+
+ dbg_isoc("Adding isoc urb to (possibly empty) list");
+ urb_list_add(urb, epid);
+ etrax_usb_add_to_isoc_sb_list(urb, epid);
+ spin_unlock_irqrestore(&urb_list_lock, flags);
+
+ DBFEXIT;
+
+ return 0;
+}
+
+static void etrax_usb_check_error_isoc_ep(const int epid)
+{
+ unsigned long int flags;
+ int error_code;
+ __u32 r_usb_ept_data;
+
+ /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
+ bulk_eot and epid_attn interrupts. So we just check the status of
+ the epid without testing if for it in R_USB_EPID_ATTN. */
+
+
+ save_flags(flags);
+ cli();
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+ registers, they are located at the same address and are of the same size.
+ In other words, this read should be ok for isoc also. */
+ r_usb_ept_data = *R_USB_EPT_DATA;
+ restore_flags(flags);
+
+ error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+
+ if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+ warn("Hold was set for epid %d.", epid);
+ return;
+ }
+
+ if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
+
+ /* This indicates that the SB list of the ept was completed before
+ new data was appended to it. This is not an error, but indicates
+ large system or USB load and could possibly cause trouble for
+ very timing sensitive USB device drivers so we log it.
+ */
+ info("Isoc. epid %d disabled with no error", epid);
+ return;
+
+ } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
+ /* Not really a protocol error, just says that the endpoint gave
+ a stall response. Note that error_code cannot be stall for isoc. */
+ panic("Isoc traffic cannot stall");
+
+ } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
+ /* Two devices responded to a transaction request. Must be resolved
+ by software. FIXME: Reset ports? */
+ panic("Bus error for epid %d."
+ " Two devices responded to transaction request",
+ epid);
+
+ } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+ /* DMA overrun or underrun. */
+ warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+
+ /* It seems that error_code = buffer_error in
+ R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+ are the same error. */
+ }
+}
+
+
+static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
+{
+
+ int i = 0;
+
+ etrax_urb_priv_t *urb_priv;
+ USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc;
+
+ DBFENTER;
+
+ prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
+
+ urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
+ assert(urb_priv != NULL);
+ memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+
+ urb->hcpriv = urb_priv;
+ urb_priv->epid = epid;
+
+ if (usb_pipeout(urb->pipe)) {
+
+ if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
+
+ dbg_isoc("Transfer for epid %d is OUT", epid);
+ dbg_isoc("%d packets in URB", urb->number_of_packets);
+
+ /* Create one SB descriptor for each packet and link them together. */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (!urb->iso_frame_desc[i].length)
+ continue;
+
+ next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_ATOMIC);
+ assert(next_sb_desc != NULL);
+
+ if (urb->iso_frame_desc[i].length > 0) {
+
+ next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
+ IO_STATE(USB_SB_command, eot, yes));
+
+ next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
+ next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+
+ /* Check if full length transfer. */
+ if (urb->iso_frame_desc[i].length ==
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+ next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+ }
+ } else {
+ dbg_isoc("zero len packet");
+ next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+ IO_STATE(USB_SB_command, tt, zout) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, full, yes));
+
+ next_sb_desc->sw_len = 1;
+ next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
+ }
+
+ /* First SB descriptor that belongs to this urb */
+ if (i == 0)
+ urb_priv->first_sb = next_sb_desc;
+ else
+ prev_sb_desc->next = virt_to_phys(next_sb_desc);
+
+ prev_sb_desc = next_sb_desc;
+ }
+
+ next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+ next_sb_desc->next = 0;
+ urb_priv->last_sb = next_sb_desc;
+
+ } else if (usb_pipein(urb->pipe)) {
+
+ dbg_isoc("Transfer for epid %d is IN", epid);
+ dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
+ dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
+
+ /* Note that in descriptors for periodic traffic are not consumed. This means that
+ the USB controller never propagates in the SB list. In other words, if there already
+ is an SB descriptor in the list for this EP we don't have to do anything. */
+ if (TxIsocEPList[epid].sub == 0) {
+ dbg_isoc("Isoc traffic not already running, allocating SB");
+
+ next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_ATOMIC);
+ assert(next_sb_desc != NULL);
+
+ next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
+ IO_STATE(USB_SB_command, eot, yes) |
+ IO_STATE(USB_SB_command, eol, yes));
+
+ next_sb_desc->next = 0;
+ next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
+ for periodic in traffic as long as it is more
+ than zero. Set to 1 always. */
+ next_sb_desc->buf = 0;
+
+ /* The rem field is don't care for isoc traffic, so we don't set it. */
+
+ /* Only one SB descriptor that belongs to this urb. */
+ urb_priv->first_sb = next_sb_desc;
+ urb_priv->last_sb = next_sb_desc;
+
+ } else {
+
+ dbg_isoc("Isoc traffic already running, just setting first/last_sb");
+
+ /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
+ already active urb. Note that even though we may have several first_sb/last_sb
+ pointing at the same SB descriptor, they are freed only once (when the list has
+ become empty). */
+ urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
+ urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
+ return;
+ }
+
+ }
+
+ /* Find the spot to insert this urb and add it. */
+ if (TxIsocEPList[epid].sub == 0) {
+ /* First SB descriptor inserted in this list (in or out). */
+ dbg_isoc("Inserting SB desc first in list");
+ TxIsocEPList[epid].hw_len = 0;
+ TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+
+ } else {
+ /* Isochronous traffic is already running, insert new traffic last (only out). */
+ dbg_isoc("Inserting SB desc last in list");
+ temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+ while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
+ IO_STATE(USB_SB_command, eol, yes)) {
+ assert(temp_sb_desc->next);
+ temp_sb_desc = phys_to_virt(temp_sb_desc->next);
+ }
+ dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
+
+ /* Next pointer must be set before eol is removed. */
+ temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
+ /* Clear the previous end of list flag since there is a new in the
+ added SB descriptor list. */
+ temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
+
+ if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+ /* 8.8.5 in Designer's Reference says we should check for and correct
+ any errors in the EP here. That should not be necessary if epid_attn
+ is handled correctly, so we assume all is ok. */
+ dbg_isoc("EP disabled");
+ etrax_usb_check_error_isoc_ep(epid);
+
+ /* The SB list was exhausted. */
+ if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
+ /* The new sublist did not get processed before the EP was
+ disabled. Setup the EP again. */
+ dbg_isoc("Set EP sub to new list");
+ TxIsocEPList[epid].hw_len = 0;
+ TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+ }
+ }
+ }
+
+ if (urb->transfer_flags & URB_ISO_ASAP) {
+ /* The isoc transfer should be started as soon as possible. The start_frame
+ field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
+ with a USB Chief trace shows that the first isoc IN token is sent 2 frames
+ later. I'm not sure how this affects usage of the start_frame field by the
+ device driver, or how it affects things when USB_ISO_ASAP is not set, so
+ therefore there's no compensation for the 2 frame "lag" here. */
+ urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
+ TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+ urb_priv->urb_state = STARTED;
+ dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
+ } else {
+ /* Not started yet. */
+ urb_priv->urb_state = NOT_STARTED;
+ dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
+ }
+
+ /* We start the DMA sub channel without checking if it's running or not, because:
+ 1) If it's already running, issuing the start command is a nop.
+ 2) We avoid a test-and-set race condition. */
+ *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+
+ DBFEXIT;
+}
+
+static void etrax_usb_complete_isoc_urb(struct urb *urb, int status)
+{
+ etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ int epid = urb_priv->epid;
+ int auto_resubmit = 0;
+
+ DBFENTER;
+ dbg_isoc("complete urb 0x%p, status %d", urb, status);
+
+ if (status)
+ warn("Completing isoc urb with status %d.", status);
+
+ if (usb_pipein(urb->pipe)) {
+ int i;
+
+ /* Make that all isoc packets have status and length set before
+ completing the urb. */
+ for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = -EPROTO;
+ }
+
+ urb_list_del(urb, epid);
+
+ if (!list_empty(&urb_list[epid])) {
+ ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+ } else {
+ unsigned long int flags;
+ if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+ /* The EP was enabled, disable it and wait. */
+ TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+ /* Ah, the luxury of busy-wait. */
+ while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+ }
+
+ etrax_remove_from_sb_list(urb);
+ TxIsocEPList[epid].sub = 0;
+ TxIsocEPList[epid].hw_len = 0;
+
+ save_flags(flags);
+ cli();
+ etrax_usb_free_epid(epid);
+ restore_flags(flags);
+ }
+
+ urb->hcpriv = 0;
+ kfree(urb_priv);
+
+ /* Release allocated bandwidth. */
+ usb_release_bandwidth(urb->dev, urb, 0);
+ } else if (usb_pipeout(urb->pipe)) {
+ int freed_descr;
+
+ dbg_isoc("Isoc out urb complete 0x%p", urb);
+
+ /* Update the urb list. */
+ urb_list_del(urb, epid);
+
+ freed_descr = etrax_remove_from_sb_list(urb);
+ dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
+ assert(freed_descr == urb->number_of_packets);
+ urb->hcpriv = 0;
+ kfree(urb_priv);
+
+ /* Release allocated bandwidth. */
+ usb_release_bandwidth(urb->dev, urb, 0);
+ }
+
+ urb->status = status;
+ if (urb->complete) {
+ urb->complete(urb, NULL);
+ }
+
+ if (auto_resubmit) {
+ /* Check that urb was not unlinked by the complete callback. */
+ if (__urb_list_entry(urb, epid)) {
+ /* Move this one down the list. */
+ urb_list_move_last(urb, epid);
+
+ /* Mark the now first urb as started (may already be). */
+ ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+
+ /* Must set this to 0 since this urb is still active after
+ completion. */
+ urb_priv->isoc_packet_counter = 0;
+ } else {
+ warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
+ }
+ }
+
+ DBFEXIT;
+}
+
+static void etrax_usb_complete_urb(struct urb *urb, int status)
+{
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_BULK:
+ etrax_usb_complete_bulk_urb(urb, status);
+ break;
+ case PIPE_CONTROL:
+ etrax_usb_complete_ctrl_urb(urb, status);
+ break;
+ case PIPE_INTERRUPT:
+ etrax_usb_complete_intr_urb(urb, status);
+ break;
+ case PIPE_ISOCHRONOUS:
+ etrax_usb_complete_isoc_urb(urb, status);
+ break;
+ default:
+ err("Unknown pipetype");
+ }
+}
+
+
+
+static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs)
+{
+ usb_interrupt_registers_t *reg;
+ unsigned long flags;
+ __u32 irq_mask;
+ __u8 status;
+ __u32 epid_attn;
+ __u16 port_status_1;
+ __u16 port_status_2;
+ __u32 fm_number;
+
+ DBFENTER;
+
+ /* Read critical registers into local variables, do kmalloc afterwards. */
+ save_flags(flags);
+ cli();
+
+ irq_mask = *R_USB_IRQ_MASK_READ;
+ /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
+ must be read before R_USB_EPID_ATTN since reading the latter clears the
+ ourun and perror fields of R_USB_STATUS. */
+ status = *R_USB_STATUS;
+
+ /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
+ epid_attn = *R_USB_EPID_ATTN;
+
+ /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
+ port_status interrupt. */
+ port_status_1 = *R_USB_RH_PORT_STATUS_1;
+ port_status_2 = *R_USB_RH_PORT_STATUS_2;
+
+ /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
+ /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
+ fm_number = *R_USB_FM_NUMBER;
+
+ restore_flags(flags);
+
+ reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, SLAB_ATOMIC);
+
+ assert(reg != NULL);
+
+ reg->hc = (etrax_hc_t *)vhc;
+
+ /* Now put register values into kmalloc'd area. */
+ reg->r_usb_irq_mask_read = irq_mask;
+ reg->r_usb_status = status;
+ reg->r_usb_epid_attn = epid_attn;
+ reg->r_usb_rh_port_status_1 = port_status_1;
+ reg->r_usb_rh_port_status_2 = port_status_2;
+ reg->r_usb_fm_number = fm_number;
+
+ INIT_WORK(®->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg);
+ schedule_work(®->usb_bh);
+
+ DBFEXIT;
+
+ return IRQ_HANDLED;
+}
+
+static void etrax_usb_hc_interrupt_bottom_half(void *data)
+{
+ usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
+ __u32 irq_mask = reg->r_usb_irq_mask_read;
+
+ DBFENTER;
+
+ /* Interrupts are handled in order of priority. */
+ if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
+ etrax_usb_hc_epid_attn_interrupt(reg);
+ }
+ if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+ etrax_usb_hc_port_status_interrupt(reg);
+ }
+ if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
+ etrax_usb_hc_ctl_status_interrupt(reg);
+ }
+ if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
+ etrax_usb_hc_isoc_eof_interrupt();
+ }
+ if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
+ /* Update/restart the bulk start timer since obviously the channel is running. */
+ mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+ /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
+ mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+ etrax_usb_hc_bulk_eot_interrupt(0);
+ }
+
+ kmem_cache_free(top_half_reg_cache, reg);
+
+ DBFEXIT;
+}
+
+
+void etrax_usb_hc_isoc_eof_interrupt(void)
+{
+ struct urb *urb;
+ etrax_urb_priv_t *urb_priv;
+ int epid;
+ unsigned long flags;
+
+ DBFENTER;
+
+ /* Do not check the invalid epid (it has a valid sub pointer). */
+ for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+
+ /* Do not check the invalid epid (it has a valid sub pointer). */
+ if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
+ continue;
+
+ /* Disable interrupts to block the isoc out descriptor interrupt handler
+ from being called while the isoc EPID list is being checked.
+ */
+ save_flags(flags);
+ cli();
+
+ if (TxIsocEPList[epid].sub == 0) {
+ /* Nothing here to see. */
+ restore_flags(flags);
+ continue;
+ }
+
+ /* Get the first urb (if any). */
+ urb = urb_list_first(epid);
+ if (urb == 0) {
+ warn("Ignoring NULL urb");
+ restore_flags(flags);
+ continue;
+ }
+ if (usb_pipein(urb->pipe)) {
+
+ /* Sanity check. */
+ assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ if (urb_priv->urb_state == NOT_STARTED) {
+
+ /* If ASAP is not set and urb->start_frame is the current frame,
+ start the transfer. */
+ if (!(urb->transfer_flags & URB_ISO_ASAP) &&
+ (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
+
+ dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
+ TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+ /* This urb is now active. */
+ urb_priv->urb_state = STARTED;
+ continue;
+ }
+ }
+ }
+ restore_flags(flags);
+ }
+
+ DBFEXIT;
+
+}
+
+void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
+{
+ int epid;
+
+ /* The technique is to run one urb at a time, wait for the eot interrupt at which
+ point the EP descriptor has been disabled. */
+
+ DBFENTER;
+ dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
+
+ for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+ if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
+ (TxBulkEPList[epid].sub != 0)) {
+
+ struct urb *urb;
+ etrax_urb_priv_t *urb_priv;
+ unsigned long flags;
+ __u32 r_usb_ept_data;
+
+ /* Found a disabled EP descriptor which has a non-null sub pointer.
+ Verify that this ctrl EP descriptor got disabled no errors.
+ FIXME: Necessary to check error_code? */
+ dbg_bulk("for epid %d?", epid);
+
+ /* Get the first urb. */
+ urb = urb_list_first(epid);
+
+ /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
+ wrong unlinking? */
+ if (!urb) {
+ warn("NULL urb for epid %d", epid);
+ continue;
+ }
+
+ assert(urb);
+ urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+ assert(urb_priv);
+
+ /* Sanity checks. */
+ assert(usb_pipetype(urb->pipe) == PIPE_BULK);
+ if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
+ err("bulk endpoint got disabled before reaching last sb");
+ }
+
+ /* For bulk IN traffic, there seems to be a race condition between
+ between the bulk eot and eop interrupts, or rather an uncertainty regarding
+ the order in which they happen. Normally we expect the eop interrupt from
+ DMA channel 9 to happen before the eot interrupt.
+
+ Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
+
+ if (usb_pipein(urb->pipe)) {
+ dbg_bulk("in urb, continuing");
+ continue;
+ }
+
+ save_flags(flags);
+ cli();
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ r_usb_ept_data = *R_USB_EPT_DATA;
+ restore_flags(flags);
+
+ if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+ IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+ /* This means that the endpoint has no error, is disabled
+ and had inserted traffic, i.e. transfer successfully completed. */
+ etrax_usb_complete_bulk_urb(urb, 0);
+ } else {
+ /* Shouldn't happen. We expect errors to be caught by epid attention. */
+ err("Found disabled bulk EP desc, error_code != no_error");
+ }
+ }
+ }
+
+ /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
+ However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
+ not. Also, we might find two disabled EPs when handling an eot interrupt, and then find
+ none the next time. */
+
+ DBFEXIT;
+
+}
+
+void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
+{
+ /* This function handles the epid attention interrupt. There are a variety of reasons
+ for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+
+ invalid ep_id - Invalid epid in an EP (EP disabled).
+ stall - Not strictly an error condition (EP disabled).
+ 3rd error - Three successive transaction errors (EP disabled).
+ buffer ourun - Buffer overrun or underrun (EP disabled).
+ past eof1 - Intr or isoc transaction proceeds past EOF1.
+ near eof - Intr or isoc transaction would not fit inside the frame.
+ zout transfer - If zout transfer for a bulk endpoint (EP disabled).
+ setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
+
+ int epid;
+
+
+ DBFENTER;
+
+ assert(reg != NULL);
+
+ /* Note that we loop through all epids. We still want to catch errors for
+ the invalid one, even though we might handle them differently. */
+ for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+ if (test_bit(epid, (void *)®->r_usb_epid_attn)) {
+
+ struct urb *urb;
+ __u32 r_usb_ept_data;
+ unsigned long flags;
+ int error_code;
+
+ save_flags(flags);
+ cli();
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+ nop();
+ /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+ registers, they are located at the same address and are of the same size.
+ In other words, this read should be ok for isoc also. */
+ r_usb_ept_data = *R_USB_EPT_DATA;
+ restore_flags(flags);
+
+ /* First some sanity checks. */
+ if (epid == INVALID_EPID) {
+ /* FIXME: What if it became disabled? Could seriously hurt interrupt
+ traffic. (Use do_intr_recover.) */
+ warn("Got epid_attn for INVALID_EPID (%d).", epid);
+ err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+ err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+ continue;
+ } else if (epid == DUMMY_EPID) {
+ /* We definitely don't care about these ones. Besides, they are
+ always disabled, so any possible disabling caused by the
+ epid attention interrupt is irrelevant. */
+ warn("Got epid_attn for DUMMY_EPID (%d).", epid);
+ continue;
+ }
+
+ /* Get the first urb in the urb list for this epid. We blatantly assume
+ that only the first urb could have caused the epid attention.
+ (For bulk and ctrl, only one urb is active at any one time. For intr
+ and isoc we remove them once they are completed.) */
+ urb = urb_list_first(epid);
+
+ if (urb == NULL) {
+ err("Got epid_attn for epid %i with no urb.", epid);
+ err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+ err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+ continue;
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_BULK:
+ warn("Got epid attn for bulk endpoint, epid %d", epid);
+ break;
+ case PIPE_CONTROL:
+ warn("Got epid attn for control endpoint, epid %d", epid);
+ break;
+ case PIPE_INTERRUPT:
+ warn("Got epid attn for interrupt endpoint, epid %d", epid);
+ break;
+ case PIPE_ISOCHRONOUS:
+ warn("Got epid attn for isochronous endpoint, epid %d", epid);
+ break;
+ }
+
+ if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+ if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+ warn("Hold was set for epid %d.", epid);
+ continue;
+ }
+ }
+
+ /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and
+ R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+ } else {
+ error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
+ }
+
+ /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
+ if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+
+ /* Isoc traffic doesn't have error_count_in/error_count_out. */
+ if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
+ (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
+ IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
+ /* 3rd error. */
+ warn("3rd error for epid %i", epid);
+ etrax_usb_complete_urb(urb, -EPROTO);
+
+ } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+ warn("Perror for epid %d", epid);
+
+ if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+ /* invalid ep_id */
+ panic("Perror because of invalid epid."
+ " Deconfigured too early?");
+ } else {
+ /* past eof1, near eof, zout transfer, setup transfer */
+
+ /* Dump the urb and the relevant EP descriptor list. */
+
+ __dump_urb(urb);
+ __dump_ept_data(epid);
+ __dump_ep_list(usb_pipetype(urb->pipe));
+
+ panic("Something wrong with DMA descriptor contents."
+ " Too much traffic inserted?");
+ }
+ } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+ /* buffer ourun */
+ panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+ }
+
+ } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
+ /* Not really a protocol error, just says that the endpoint gave
+ a stall response. Note that error_code cannot be stall for isoc. */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ panic("Isoc traffic cannot stall");
+ }
+
+ warn("Stall for epid %d", epid);
+ etrax_usb_complete_urb(urb, -EPIPE);
+
+ } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
+ /* Two devices responded to a transaction request. Must be resolved
+ by software. FIXME: Reset ports? */
+ panic("Bus error for epid %d."
+ " Two devices responded to transaction request",
+ epid);
+
+ } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+ /* DMA overrun or underrun. */
+ warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+
+ /* It seems that error_code = buffer_error in
+ R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+ are the same error. */
+ etrax_usb_complete_urb(urb, -EPROTO);
+ }
+ }
+ }
+
+ DBFEXIT;
+
+}
+
+void etrax_usb_bulk_start_timer_func(unsigned long dummy)
+{
+
+ /* We might enable an EP descriptor behind the current DMA position when it's about
+ to decide that there are no more bulk traffic and it should stop the bulk channel.
+ Therefore we periodically check if the bulk channel is stopped and there is an
+ enabled bulk EP descriptor, in which case we start the bulk channel. */
+ dbg_bulk("bulk_start_timer timed out.");
+
+ if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+ int epid;
+
+ dbg_bulk("Bulk DMA channel not running.");
+
+ for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+ if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+ dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
+ epid);
+ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+
+ /* Restart the bulk eot timer since we just started the bulk channel. */
+ mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+ /* No need to search any further. */
+ break;
+ }
+ }
+ } else {
+ dbg_bulk("Bulk DMA channel running.");
+ }
+}
+
+void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
+{
+ etrax_hc_t *hc = reg->hc;
+ __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+ __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+
+ DBFENTER;
+
+ /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
+ (by saving the old port status value for comparison when the port status interrupt happens).
+ See section 11.16.2.6.2 in the USB 1.1 spec for details. */
+
+ dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
+ dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
+ dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
+ dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
+
+ /* C_PORT_CONNECTION is set on any transition. */
+ hc->rh.wPortChange_1 |=
+ ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+ (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+ (1 << RH_PORT_CONNECTION) : 0;
+
+ hc->rh.wPortChange_2 |=
+ ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+ (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+ (1 << RH_PORT_CONNECTION) : 0;
+
+ /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
+ the port is disabled, not when it's enabled. */
+ hc->rh.wPortChange_1 |=
+ ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+ && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+ (1 << RH_PORT_ENABLE) : 0;
+
+ hc->rh.wPortChange_2 |=
+ ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+ && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+ (1 << RH_PORT_ENABLE) : 0;
+
+ /* C_PORT_SUSPEND is set to one when the device has transitioned out
+ of the suspended state, i.e. when suspend goes from one to zero. */
+ hc->rh.wPortChange_1 |=
+ ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
+ && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
+ (1 << RH_PORT_SUSPEND) : 0;
+
+ hc->rh.wPortChange_2 |=
+ ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
+ && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
+ (1 << RH_PORT_SUSPEND) : 0;
+
+
+ /* C_PORT_RESET is set when reset processing on this port is complete. */
+ hc->rh.wPortChange_1 |=
+ ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+ && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
+ (1 << RH_PORT_RESET) : 0;
+
+ hc->rh.wPortChange_2 |=
+ ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+ && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
+ (1 << RH_PORT_RESET) : 0;
+
+ /* Save the new values for next port status change. */
+ hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+ hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;
+
+ dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
+ dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
+
+ DBFEXIT;
+
+}
+
+void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
+{
+ DBFENTER;
+
+ /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
+ list for the corresponding epid? */
+ if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+ panic("USB controller got ourun.");
+ }
+ if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+ /* Before, etrax_usb_do_intr_recover was called on this epid if it was
+ an interrupt pipe. I don't see how re-enabling all EP descriptors
+ will help if there was a programming error. */
+ panic("USB controller got perror.");
+ }
+
+ if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
+ /* We should never operate in device mode. */
+ panic("USB controller in device mode.");
+ }
+
+ /* These if-statements could probably be nested. */
+ if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
+ info("USB controller in host mode.");
+ }
+ if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
+ info("USB controller started.");
+ }
+ if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
+ info("USB controller running.");
+ }
+
+ DBFEXIT;
+
+}
+
+
+static int etrax_rh_submit_urb(struct urb *urb)
+{
+ struct usb_device *usb_dev = urb->dev;
+ etrax_hc_t *hc = usb_dev->bus->hcpriv;
+ unsigned int pipe = urb->pipe;
+ struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+ void *data = urb->transfer_buffer;
+ int leni = urb->transfer_buffer_length;
+ int len = 0;
+ int stat = 0;
+
+ __u16 bmRType_bReq;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+
+ DBFENTER;
+
+ /* FIXME: What is this interrupt urb that is sent to the root hub? */
+ if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+ dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
+ hc->rh.urb = urb;
+ hc->rh.send = 1;
+ /* FIXME: We could probably remove this line since it's done
+ in etrax_rh_init_int_timer. (Don't remove it from
+ etrax_rh_init_int_timer though.) */
+ hc->rh.interval = urb->interval;
+ etrax_rh_init_int_timer(urb);
+ DBFEXIT;
+
+ return 0;
+ }
+
+ bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
+ wValue = le16_to_cpu(cmd->wValue);
+ wIndex = le16_to_cpu(cmd->wIndex);
+ wLength = le16_to_cpu(cmd->wLength);
+
+ dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
+ dbg_rh("wValue : 0x%04x (%d)", wValue, wValue);
+ dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex);
+ dbg_rh("wLength : 0x%04x (%d)", wLength, wLength);
+
+ switch (bmRType_bReq) {
+
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
+
+ case RH_GET_STATUS:
+ *(__u16 *) data = cpu_to_le16 (1);
+ OK (2);
+
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *) data = cpu_to_le16 (0);
+ OK (2);
+
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *) data = cpu_to_le16 (0);
+ OK (2);
+
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *) data = cpu_to_le32 (0);
+ OK (4); /* hub power ** */
+
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ if (wIndex == 1) {
+ *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
+ *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
+ } else if (wIndex == 2) {
+ *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
+ *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
+ } else {
+ dbg_rh("RH_GET_STATUS whith invalid wIndex!");
+ OK(0);
+ }
+
+ OK(4);
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ case (RH_C_HUB_OVER_CURRENT):
+ OK (0); /* hub power over current ** */
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ if (wIndex == 1) {
+
+ dbg_rh("trying to do disable port 1");
+
+ *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+
+ while (hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
+ *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+ dbg_rh("Port 1 is disabled");
+
+ } else if (wIndex == 2) {
+
+ dbg_rh("trying to do disable port 2");
+
+ *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+
+ while (hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
+ *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+ dbg_rh("Port 2 is disabled");
+
+ } else {
+ dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
+ "with invalid wIndex == %d!", wIndex);
+ }
+
+ OK (0);
+ case (RH_PORT_SUSPEND):
+ /* Opposite to suspend should be resume, so we'll do a resume. */
+ /* FIXME: USB 1.1, 11.16.2.2 says:
+ "Clearing the PORT_SUSPEND feature causes a host-initiated resume
+ on the specified port. If the port is not in the Suspended state,
+ the hub should treat this request as a functional no-operation."
+ Shouldn't we check if the port is in a suspended state before
+ resuming? */
+
+ /* Make sure the controller isn't busy. */
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ if (wIndex == 1) {
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, port1) |
+ IO_STATE(R_USB_COMMAND, port_cmd, resume) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+ } else if (wIndex == 2) {
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, port2) |
+ IO_STATE(R_USB_COMMAND, port_cmd, resume) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+ } else {
+ dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
+ "with invalid wIndex == %d!", wIndex);
+ }
+
+ OK (0);
+ case (RH_PORT_POWER):
+ OK (0); /* port power ** */
+ case (RH_C_PORT_CONNECTION):
+ if (wIndex == 1) {
+ hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
+ } else if (wIndex == 2) {
+ hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
+ } else {
+ dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
+ "with invalid wIndex == %d!", wIndex);
+ }
+
+ OK (0);
+ case (RH_C_PORT_ENABLE):
+ if (wIndex == 1) {
+ hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
+ } else if (wIndex == 2) {
+ hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
+ } else {
+ dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
+ "with invalid wIndex == %d!", wIndex);
+ }
+ OK (0);
+ case (RH_C_PORT_SUSPEND):
+/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+ OK (0);
+ case (RH_C_PORT_OVER_CURRENT):
+ OK (0); /* port power over current ** */
+ case (RH_C_PORT_RESET):
+ if (wIndex == 1) {
+ hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
+ } else if (wIndex == 2) {
+ hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
+ } else {
+ dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
+ "with invalid index == %d!", wIndex);
+ }
+
+ OK (0);
+
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+
+ /* Make sure the controller isn't busy. */
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ if (wIndex == 1) {
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, port1) |
+ IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+ } else if (wIndex == 2) {
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, port2) |
+ IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+ } else {
+ dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
+ "with invalid wIndex == %d!", wIndex);
+ }
+
+ OK (0);
+ case (RH_PORT_RESET):
+ if (wIndex == 1) {
+
+ port_1_reset:
+ dbg_rh("Doing reset of port 1");
+
+ /* Make sure the controller isn't busy. */
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, port1) |
+ IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+
+ /* We must wait at least 10 ms for the device to recover.
+ 15 ms should be enough. */
+ udelay(15000);
+
+ /* Wait for reset bit to go low (should be done by now). */
+ while (hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
+
+ /* If the port status is
+ 1) connected and enabled then there is a device and everything is fine
+ 2) neither connected nor enabled then there is no device, also fine
+ 3) connected and not enabled then we try again
+ (Yes, there are other port status combinations besides these.) */
+
+ if ((hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+ (hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+ dbg_rh("Connected device on port 1, but port not enabled?"
+ " Trying reset again.");
+ goto port_2_reset;
+ }
+
+ /* Diagnostic printouts. */
+ if ((hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
+ (hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+ dbg_rh("No connected device on port 1");
+ } else if ((hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+ (hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
+ dbg_rh("Connected device on port 1, port 1 enabled");
+ }
+
+ } else if (wIndex == 2) {
+
+ port_2_reset:
+ dbg_rh("Doing reset of port 2");
+
+ /* Make sure the controller isn't busy. */
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ /* Issue the reset command. */
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, port2) |
+ IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+
+ /* We must wait at least 10 ms for the device to recover.
+ 15 ms should be enough. */
+ udelay(15000);
+
+ /* Wait for reset bit to go low (should be done by now). */
+ while (hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
+
+ /* If the port status is
+ 1) connected and enabled then there is a device and everything is fine
+ 2) neither connected nor enabled then there is no device, also fine
+ 3) connected and not enabled then we try again
+ (Yes, there are other port status combinations besides these.) */
+
+ if ((hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+ (hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+ dbg_rh("Connected device on port 2, but port not enabled?"
+ " Trying reset again.");
+ goto port_2_reset;
+ }
+
+ /* Diagnostic printouts. */
+ if ((hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
+ (hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+ dbg_rh("No connected device on port 2");
+ } else if ((hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+ (hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
+ dbg_rh("Connected device on port 2, port 2 enabled");
+ }
+
+ } else {
+ dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
+ }
+
+ /* Make sure the controller isn't busy. */
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ /* If all enabled ports were disabled the host controller goes down into
+ started mode, so we need to bring it back into the running state.
+ (This is safe even if it's already in the running state.) */
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, nop) |
+ IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+
+ dbg_rh("...Done");
+ OK(0);
+
+ case (RH_PORT_POWER):
+ OK (0); /* port power ** */
+ case (RH_PORT_ENABLE):
+ /* There is no port enable command in the host controller, so if the
+ port is already enabled, we do nothing. If not, we reset the port
+ (with an ugly goto). */
+
+ if (wIndex == 1) {
+ if (hc->rh.prev_wPortStatus_1 &
+ IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
+ goto port_1_reset;
+ }
+ } else if (wIndex == 2) {
+ if (hc->rh.prev_wPortStatus_2 &
+ IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
+ goto port_2_reset;
+ }
+ } else {
+ dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
+ }
+ OK (0);
+ }
+ break;
+
+ case RH_SET_ADDRESS:
+ hc->rh.devnum = wValue;
+ dbg_rh("RH address set to: %d", hc->rh.devnum);
+ OK (0);
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
+ memcpy (data, root_hub_dev_des, len);
+ OK (len);
+ case (0x02): /* configuration descriptor */
+ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength));
+ memcpy (data, root_hub_config_des, len);
+ OK (len);
+ case (0x03): /* string descriptors */
+ len = usb_root_hub_string (wValue & 0xff,
+ 0xff, "ETRAX 100LX",
+ data, wLength);
+ if (len > 0) {
+ OK(min(leni, len));
+ } else {
+ stat = -EPIPE;
+ }
+
+ }
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ root_hub_hub_des[2] = hc->rh.numports;
+ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
+ memcpy (data, root_hub_hub_des, len);
+ OK (len);
+
+ case RH_GET_CONFIGURATION:
+ *(__u8 *) data = 0x01;
+ OK (1);
+
+ case RH_SET_CONFIGURATION:
+ OK (0);
+
+ default:
+ stat = -EPIPE;
+ }
+
+ urb->actual_length = len;
+ urb->status = stat;
+ urb->dev = NULL;
+ if (urb->complete) {
+ urb->complete(urb, NULL);
+ }
+ DBFEXIT;
+
+ return 0;
+}
+
+static void
+etrax_usb_bulk_eot_timer_func(unsigned long dummy)
+{
+ /* Because of a race condition in the top half, we might miss a bulk eot.
+ This timer "simulates" a bulk eot if we don't get one for a while, hopefully
+ correcting the situation. */
+ dbg_bulk("bulk_eot_timer timed out.");
+ etrax_usb_hc_bulk_eot_interrupt(1);
+}
+
+static void*
+etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, int mem_flags, dma_addr_t *dma)
+{
+ return kmalloc(size, mem_flags);
+}
+
+static void
+etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma)
+{
+ kfree(addr);
+}
+
+
+static struct device fake_device;
+
+static int __init etrax_usb_hc_init(void)
+{
+ static etrax_hc_t *hc;
+ struct usb_bus *bus;
+ struct usb_device *usb_rh;
+ int i;
+
+ DBFENTER;
+
+ info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
+
+ hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+ assert(hc != NULL);
+
+ /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
+ /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
+ SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
+ sizeof(USB_SB_Desc_t). */
+
+ usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0,
+ SLAB_HWCACHE_ALIGN, 0, 0);
+ assert(usb_desc_cache != NULL);
+
+ top_half_reg_cache = kmem_cache_create("top_half_reg_cache",
+ sizeof(usb_interrupt_registers_t),
+ 0, SLAB_HWCACHE_ALIGN, 0, 0);
+ assert(top_half_reg_cache != NULL);
+
+ isoc_compl_cache = kmem_cache_create("isoc_compl_cache",
+ sizeof(usb_isoc_complete_data_t),
+ 0, SLAB_HWCACHE_ALIGN, 0, 0);
+ assert(isoc_compl_cache != NULL);
+
+ etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
+ hc->bus = bus;
+ bus->bus_name="ETRAX 100LX";
+ bus->hcpriv = hc;
+
+ /* Initalize RH to the default address.
+ And make sure that we have no status change indication */
+ hc->rh.numports = 2; /* The RH has two ports */
+ hc->rh.devnum = 1;
+ hc->rh.wPortChange_1 = 0;
+ hc->rh.wPortChange_2 = 0;
+
+ /* Also initate the previous values to zero */
+ hc->rh.prev_wPortStatus_1 = 0;
+ hc->rh.prev_wPortStatus_2 = 0;
+
+ /* Initialize the intr-traffic flags */
+ /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
+ hc->intr.sleeping = 0;
+ hc->intr.wq = NULL;
+
+ epid_usage_bitmask = 0;
+ epid_out_traffic = 0;
+
+ /* Mark the invalid epid as being used. */
+ set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
+ nop();
+ /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
+ *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
+ IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+ /* Mark the dummy epid as being used. */
+ set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
+ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
+ nop();
+ *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
+ IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+ /* Initialize the urb list by initiating a head for each list. */
+ for (i = 0; i < NBR_OF_EPIDS; i++) {
+ INIT_LIST_HEAD(&urb_list[i]);
+ }
+ spin_lock_init(&urb_list_lock);
+
+ INIT_LIST_HEAD(&urb_unlink_list);
+
+
+ /* Initiate the bulk start timer. */
+ init_timer(&bulk_start_timer);
+ bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
+ bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
+ add_timer(&bulk_start_timer);
+
+
+ /* Initiate the bulk eot timer. */
+ init_timer(&bulk_eot_timer);
+ bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
+ bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
+ add_timer(&bulk_eot_timer);
+
+ /* Set up the data structures for USB traffic. Note that this must be done before
+ any interrupt that relies on sane DMA list occurrs. */
+ init_rx_buffers();
+ init_tx_bulk_ep();
+ init_tx_ctrl_ep();
+ init_tx_intr_ep();
+ init_tx_isoc_ep();
+
+ device_initialize(&fake_device);
+ kobject_set_name(&fake_device.kobj, "etrax_usb");
+ kobject_add(&fake_device.kobj);
+ hc->bus->controller = &fake_device;
+ usb_register_bus(hc->bus);
+
+ *R_IRQ_MASK2_SET =
+ /* Note that these interrupts are not used. */
+ IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
+ /* Sub channel 1 (ctrl) descr. interrupts are used. */
+ IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
+ IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
+ /* Sub channel 3 (isoc) descr. interrupts are used. */
+ IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
+
+ /* Note that the dma9_descr interrupt is not used. */
+ *R_IRQ_MASK2_SET =
+ IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
+ IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
+
+ /* FIXME: Enable iso_eof only when isoc traffic is running. */
+ *R_USB_IRQ_MASK_SET =
+ IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
+ IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
+ IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
+ IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
+ IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+
+
+ if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
+ "ETRAX 100LX built-in USB (HC)", hc)) {
+ err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
+ etrax_usb_hc_cleanup();
+ DBFEXIT;
+ return -1;
+ }
+
+ if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
+ "ETRAX 100LX built-in USB (Rx)", hc)) {
+ err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
+ etrax_usb_hc_cleanup();
+ DBFEXIT;
+ return -1;
+ }
+
+ if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
+ "ETRAX 100LX built-in USB (Tx)", hc)) {
+ err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
+ etrax_usb_hc_cleanup();
+ DBFEXIT;
+ return -1;
+ }
+
+ /* R_USB_COMMAND:
+ USB commands in host mode. The fields in this register should all be
+ written to in one write. Do not read-modify-write one field at a time. A
+ write to this register will trigger events in the USB controller and an
+ incomplete command may lead to unpredictable results, and in worst case
+ even to a deadlock in the controller.
+ (Note however that the busy field is read-only, so no need to write to it.) */
+
+ /* Check the busy bit before writing to R_USB_COMMAND. */
+
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ /* Reset the USB interface. */
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, nop) |
+ IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
+
+ /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
+ to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
+ allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
+
+ While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
+ behaviour, it doesn't solve this problem. What happens is that a control transfer will not
+ be interrupted in its data stage when PSTART happens (the point at which periodic traffic
+ is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
+ PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
+ there may be too little time left for an isochronous transfer, causing an epid attention
+ interrupt due to perror. The work-around for this is to let the control transfers run at the
+ end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
+ fit into the frame. However, since there will *always* be a control transfer at the beginning
+ of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
+ which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
+ this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
+ sure that the periodic transfers that are inserted will always fit in the frame.
+
+ The idea was suggested that a control transfer could be split up into several 8 byte transfers,
+ so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
+ hasn't been implemented.
+
+ The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
+ for possible bit stuffing. */
+
+ *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
+
+#ifdef CONFIG_ETRAX_USB_HOST_PORT1
+ *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+#endif
+
+#ifdef CONFIG_ETRAX_USB_HOST_PORT2
+ *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+#endif
+
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ /* Configure the USB interface as a host controller. */
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, nop) |
+ IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
+
+ /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
+ sequence of resetting the ports. If we reset both ports now, and there are devices
+ on both ports, we will get a bus error because both devices will answer the set address
+ request. */
+
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ /* Start processing of USB traffic. */
+ *R_USB_COMMAND =
+ IO_STATE(R_USB_COMMAND, port_sel, nop) |
+ IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+ IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+
+ while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+ usb_rh = usb_alloc_dev(NULL, hc->bus, 0);
+ hc->bus->root_hub = usb_rh;
+ usb_rh->state = USB_STATE_ADDRESS;
+ usb_rh->speed = USB_SPEED_FULL;
+ usb_rh->devnum = 1;
+ hc->bus->devnum_next = 2;
+ usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64);
+ usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE);
+ usb_new_device(usb_rh);
+
+ DBFEXIT;
+
+ return 0;
+}
+
+static void etrax_usb_hc_cleanup(void)
+{
+ DBFENTER;
+
+ free_irq(ETRAX_USB_HC_IRQ, NULL);
+ free_irq(ETRAX_USB_RX_IRQ, NULL);
+ free_irq(ETRAX_USB_TX_IRQ, NULL);
+
+ usb_deregister_bus(etrax_usb_bus);
+
+ /* FIXME: call kmem_cache_destroy here? */
+
+ DBFEXIT;
+}
+
+module_init(etrax_usb_hc_init);
+module_exit(etrax_usb_hc_cleanup);
diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h
new file mode 100644
index 0000000..62f7711
--- /dev/null
+++ b/drivers/usb/host/hc_crisv10.h
@@ -0,0 +1,289 @@
+#ifndef __LINUX_ETRAX_USB_H
+#define __LINUX_ETRAX_USB_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+typedef struct USB_IN_Desc {
+ volatile __u16 sw_len;
+ volatile __u16 command;
+ volatile unsigned long next;
+ volatile unsigned long buf;
+ volatile __u16 hw_len;
+ volatile __u16 status;
+} USB_IN_Desc_t;
+
+typedef struct USB_SB_Desc {
+ volatile __u16 sw_len;
+ volatile __u16 command;
+ volatile unsigned long next;
+ volatile unsigned long buf;
+ __u32 dummy;
+} USB_SB_Desc_t;
+
+typedef struct USB_EP_Desc {
+ volatile __u16 hw_len;
+ volatile __u16 command;
+ volatile unsigned long sub;
+ volatile unsigned long next;
+ __u32 dummy;
+} USB_EP_Desc_t;
+
+struct virt_root_hub {
+ int devnum;
+ void *urb;
+ void *int_addr;
+ int send;
+ int interval;
+ int numports;
+ struct timer_list rh_int_timer;
+ volatile __u16 wPortChange_1;
+ volatile __u16 wPortChange_2;
+ volatile __u16 prev_wPortStatus_1;
+ volatile __u16 prev_wPortStatus_2;
+};
+
+struct etrax_usb_intr_traffic {
+ int sleeping;
+ int error;
+ struct wait_queue *wq;
+};
+
+typedef struct etrax_usb_hc {
+ struct usb_bus *bus;
+ struct virt_root_hub rh;
+ struct etrax_usb_intr_traffic intr;
+} etrax_hc_t;
+
+typedef enum {
+ STARTED,
+ NOT_STARTED,
+ UNLINK,
+ TRANSFER_DONE,
+ WAITING_FOR_DESCR_INTR
+} etrax_usb_urb_state_t;
+
+
+
+typedef struct etrax_usb_urb_priv {
+ /* The first_sb field is used for freeing all SB descriptors belonging
+ to an urb. The corresponding ep descriptor's sub pointer cannot be
+ used for this since the DMA advances the sub pointer as it processes
+ the sb list. */
+ USB_SB_Desc_t *first_sb;
+ /* The last_sb field referes to the last SB descriptor that belongs to
+ this urb. This is important to know so we can free the SB descriptors
+ that ranges between first_sb and last_sb. */
+ USB_SB_Desc_t *last_sb;
+
+ /* The rx_offset field is used in ctrl and bulk traffic to keep track
+ of the offset in the urb's transfer_buffer where incoming data should be
+ copied to. */
+ __u32 rx_offset;
+
+ /* Counter used in isochronous transfers to keep track of the
+ number of packets received/transmitted. */
+ __u32 isoc_packet_counter;
+
+ /* This field is used to pass information about the urb's current state between
+ the various interrupt handlers (thus marked volatile). */
+ volatile etrax_usb_urb_state_t urb_state;
+
+ /* Connection between the submitted urb and ETRAX epid number */
+ __u8 epid;
+
+ /* The rx_data_list field is used for periodic traffic, to hold
+ received data for later processing in the the complete_urb functions,
+ where the data us copied to the urb's transfer_buffer. Basically, we
+ use this intermediate storage because we don't know when it's safe to
+ reuse the transfer_buffer (FIXME?). */
+ struct list_head rx_data_list;
+} etrax_urb_priv_t;
+
+/* This struct is for passing data from the top half to the bottom half. */
+typedef struct usb_interrupt_registers
+{
+ etrax_hc_t *hc;
+ __u32 r_usb_epid_attn;
+ __u8 r_usb_status;
+ __u16 r_usb_rh_port_status_1;
+ __u16 r_usb_rh_port_status_2;
+ __u32 r_usb_irq_mask_read;
+ __u32 r_usb_fm_number;
+ struct work_struct usb_bh;
+} usb_interrupt_registers_t;
+
+/* This struct is for passing data from the isoc top half to the isoc bottom half. */
+typedef struct usb_isoc_complete_data
+{
+ struct urb *urb;
+ struct work_struct usb_bh;
+} usb_isoc_complete_data_t;
+
+/* This struct holds data we get from the rx descriptors for DMA channel 9
+ for periodic traffic (intr and isoc). */
+typedef struct rx_data
+{
+ void *data;
+ int length;
+ struct list_head list;
+} rx_data_t;
+
+typedef struct urb_entry
+{
+ struct urb *urb;
+ struct list_head list;
+} urb_entry_t;
+
+/* ---------------------------------------------------------------------------
+ Virtual Root HUB
+ ------------------------------------------------------------------------- */
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP 0x00
+
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+/* Field definitions for */
+
+#define USB_IN_command__eol__BITNR 0 /* command macros */
+#define USB_IN_command__eol__WIDTH 1
+#define USB_IN_command__eol__no 0
+#define USB_IN_command__eol__yes 1
+
+#define USB_IN_command__intr__BITNR 3
+#define USB_IN_command__intr__WIDTH 1
+#define USB_IN_command__intr__no 0
+#define USB_IN_command__intr__yes 1
+
+#define USB_IN_status__eop__BITNR 1 /* status macros. */
+#define USB_IN_status__eop__WIDTH 1
+#define USB_IN_status__eop__no 0
+#define USB_IN_status__eop__yes 1
+
+#define USB_IN_status__eot__BITNR 5
+#define USB_IN_status__eot__WIDTH 1
+#define USB_IN_status__eot__no 0
+#define USB_IN_status__eot__yes 1
+
+#define USB_IN_status__error__BITNR 6
+#define USB_IN_status__error__WIDTH 1
+#define USB_IN_status__error__no 0
+#define USB_IN_status__error__yes 1
+
+#define USB_IN_status__nodata__BITNR 7
+#define USB_IN_status__nodata__WIDTH 1
+#define USB_IN_status__nodata__no 0
+#define USB_IN_status__nodata__yes 1
+
+#define USB_IN_status__epid__BITNR 8
+#define USB_IN_status__epid__WIDTH 5
+
+#define USB_EP_command__eol__BITNR 0
+#define USB_EP_command__eol__WIDTH 1
+#define USB_EP_command__eol__no 0
+#define USB_EP_command__eol__yes 1
+
+#define USB_EP_command__eof__BITNR 1
+#define USB_EP_command__eof__WIDTH 1
+#define USB_EP_command__eof__no 0
+#define USB_EP_command__eof__yes 1
+
+#define USB_EP_command__intr__BITNR 3
+#define USB_EP_command__intr__WIDTH 1
+#define USB_EP_command__intr__no 0
+#define USB_EP_command__intr__yes 1
+
+#define USB_EP_command__enable__BITNR 4
+#define USB_EP_command__enable__WIDTH 1
+#define USB_EP_command__enable__no 0
+#define USB_EP_command__enable__yes 1
+
+#define USB_EP_command__hw_valid__BITNR 5
+#define USB_EP_command__hw_valid__WIDTH 1
+#define USB_EP_command__hw_valid__no 0
+#define USB_EP_command__hw_valid__yes 1
+
+#define USB_EP_command__epid__BITNR 8
+#define USB_EP_command__epid__WIDTH 5
+
+#define USB_SB_command__eol__BITNR 0 /* command macros. */
+#define USB_SB_command__eol__WIDTH 1
+#define USB_SB_command__eol__no 0
+#define USB_SB_command__eol__yes 1
+
+#define USB_SB_command__eot__BITNR 1
+#define USB_SB_command__eot__WIDTH 1
+#define USB_SB_command__eot__no 0
+#define USB_SB_command__eot__yes 1
+
+#define USB_SB_command__intr__BITNR 3
+#define USB_SB_command__intr__WIDTH 1
+#define USB_SB_command__intr__no 0
+#define USB_SB_command__intr__yes 1
+
+#define USB_SB_command__tt__BITNR 4
+#define USB_SB_command__tt__WIDTH 2
+#define USB_SB_command__tt__zout 0
+#define USB_SB_command__tt__in 1
+#define USB_SB_command__tt__out 2
+#define USB_SB_command__tt__setup 3
+
+
+#define USB_SB_command__rem__BITNR 8
+#define USB_SB_command__rem__WIDTH 6
+
+#define USB_SB_command__full__BITNR 6
+#define USB_SB_command__full__WIDTH 1
+#define USB_SB_command__full__no 0
+#define USB_SB_command__full__yes 1
+
+#endif
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
new file mode 100644
index 0000000..3981bf1
--- /dev/null
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -0,0 +1,284 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for AMD Alchemy Au1xxx
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Rusell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ * Modified for AMD Alchemy Au1xxx
+ * by Matt Porter <mporter@kernel.crashing.org>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/mach-au1x00/au1000.h>
+
+#define USBH_ENABLE_BE (1<<0)
+#define USBH_ENABLE_C (1<<1)
+#define USBH_ENABLE_E (1<<2)
+#define USBH_ENABLE_CE (1<<3)
+#define USBH_ENABLE_RD (1<<4)
+
+#ifdef __LITTLE_ENDIAN
+#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
+#elif __BIG_ENDIAN
+#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE)
+#else
+#error not byte order defined
+#endif
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void au1xxx_start_hc(struct platform_device *dev)
+{
+ printk(KERN_DEBUG __FILE__
+ ": starting Au1xxx OHCI USB Controller\n");
+
+ /* enable host controller */
+ au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
+ udelay(1000);
+ au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
+ udelay(1000);
+
+ /* wait for reset complete (read register twice; see au1500 errata) */
+ while (au_readl(USB_HOST_CONFIG),
+ !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
+ udelay(1000);
+
+ printk(KERN_DEBUG __FILE__
+ ": Clock to USB host has been enabled \n");
+}
+
+static void au1xxx_stop_hc(struct platform_device *dev)
+{
+ printk(KERN_DEBUG __FILE__
+ ": stopping Au1xxx OHCI USB Controller\n");
+
+ /* Disable clock */
+ au_writel(readl((void *)USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_au1xxx_probe - initialize Au1xxx-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+
+ if(dev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug ("resource[1] is not IORESOURCE_IRQ");
+ return -ENOMEM;
+ }
+
+ hcd = usb_create_hcd(driver, &dev->dev, "au1xxx");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = dev->resource[0].start;
+ hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ au1xxx_start_hc(dev);
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ au1xxx_stop_hc(dev);
+ iounmap(hcd->regs);
+ err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_au1xxx_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ au1xxx_stop_hc(dev);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_au1xxx_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci);
+
+ if ((ret = ohci_init (ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ err ("can't start %s", hcd->self.bus_name);
+ ohci_stop (hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_au1xxx_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Au1xxx OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_au1xxx_start,
+#ifdef CONFIG_PM
+ /* suspend: ohci_au1xxx_suspend, -- tbd */
+ /* resume: ohci_au1xxx_resume, -- tbd */
+#endif /*CONFIG_PM*/
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_au1xxx_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int ret;
+
+ pr_debug ("In ohci_hcd_au1xxx_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
+ return ret;
+}
+
+static int ohci_hcd_au1xxx_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_au1xxx_remove(hcd, pdev);
+ return 0;
+}
+ /*TBD*/
+/*static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+*/
+
+static struct device_driver ohci_hcd_au1xxx_driver = {
+ .name = "au1xxx-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_au1xxx_drv_probe,
+ .remove = ohci_hcd_au1xxx_drv_remove,
+ /*.suspend = ohci_hcd_au1xxx_drv_suspend, */
+ /*.resume = ohci_hcd_au1xxx_drv_resume, */
+};
+
+static int __init ohci_hcd_au1xxx_init (void)
+{
+ pr_debug (DRIVER_INFO " (Au1xxx)");
+ pr_debug ("block sizes: ed %d td %d\n",
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_au1xxx_driver);
+}
+
+static void __exit ohci_hcd_au1xxx_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_au1xxx_driver);
+}
+
+module_init (ohci_hcd_au1xxx_init);
+module_exit (ohci_hcd_au1xxx_cleanup);
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
new file mode 100644
index 0000000..62f53a2
--- /dev/null
+++ b/drivers/usb/host/ohci-dbg.c
@@ -0,0 +1,707 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+
+#define edstring(ed_type) ({ char *temp; \
+ switch (ed_type) { \
+ case PIPE_CONTROL: temp = "ctrl"; break; \
+ case PIPE_BULK: temp = "bulk"; break; \
+ case PIPE_INTERRUPT: temp = "intr"; break; \
+ default: temp = "isoc"; break; \
+ }; temp;})
+#define pipestring(pipe) edstring(usb_pipetype(pipe))
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header
+ */
+static void __attribute__((unused))
+urb_print (struct urb * urb, char * str, int small)
+{
+ unsigned int pipe= urb->pipe;
+
+ if (!urb->dev || !urb->dev->bus) {
+ dbg("%s URB: no dev", str);
+ return;
+ }
+
+#ifndef OHCI_VERBOSE_DEBUG
+ if (urb->status != 0)
+#endif
+ dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",
+ str,
+ urb,
+ usb_pipedevice (pipe),
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)? "out" : "in",
+ pipestring (pipe),
+ urb->transfer_flags,
+ urb->actual_length,
+ urb->transfer_buffer_length,
+ urb->status);
+
+#ifdef OHCI_VERBOSE_DEBUG
+ if (!small) {
+ int i, len;
+
+ if (usb_pipecontrol (pipe)) {
+ printk (KERN_DEBUG __FILE__ ": setup(8):");
+ for (i = 0; i < 8 ; i++)
+ printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
+ printk ("\n");
+ }
+ if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+ printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ len = usb_pipeout (pipe)?
+ urb->transfer_buffer_length: urb->actual_length;
+ for (i = 0; i < 16 && i < len; i++)
+ printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
+ printk ("%s stat:%d\n", i < len? "...": "", urb->status);
+ }
+ }
+#endif
+}
+
+#define ohci_dbg_sw(ohci, next, size, format, arg...) \
+ do { \
+ if (next) { \
+ unsigned s_len; \
+ s_len = scnprintf (*next, *size, format, ## arg ); \
+ *size -= s_len; *next += s_len; \
+ } else \
+ ohci_dbg(ohci,format, ## arg ); \
+ } while (0);
+
+
+static void ohci_dump_intr_mask (
+ struct ohci_hcd *ohci,
+ char *label,
+ u32 mask,
+ char **next,
+ unsigned *size)
+{
+ ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (
+ struct ohci_hcd *ohci,
+ char *label,
+ u32 value,
+ char **next,
+ unsigned *size)
+{
+ if (value)
+ ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+// dump control and status registers
+static void
+ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
+{
+ struct ohci_regs __iomem *regs = controller->regs;
+ u32 temp;
+
+ temp = ohci_readl (controller, ®s->revision) & 0xff;
+ ohci_dbg_sw (controller, next, size,
+ "OHCI %d.%d, %s legacy support registers\n",
+ 0x03 & (temp >> 4), (temp & 0x0f),
+ (temp & 0x0100) ? "with" : "NO");
+
+ temp = ohci_readl (controller, ®s->control);
+ ohci_dbg_sw (controller, next, size,
+ "control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",
+ temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = ohci_readl (controller, ®s->cmdstatus);
+ ohci_dbg_sw (controller, next, size,
+ "cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask (controller, "intrstatus",
+ ohci_readl (controller, ®s->intrstatus),
+ next, size);
+ ohci_dump_intr_mask (controller, "intrenable",
+ ohci_readl (controller, ®s->intrenable),
+ next, size);
+ // intrdisable always same as intrenable
+
+ maybe_print_eds (controller, "ed_periodcurrent",
+ ohci_readl (controller, ®s->ed_periodcurrent),
+ next, size);
+
+ maybe_print_eds (controller, "ed_controlhead",
+ ohci_readl (controller, ®s->ed_controlhead),
+ next, size);
+ maybe_print_eds (controller, "ed_controlcurrent",
+ ohci_readl (controller, ®s->ed_controlcurrent),
+ next, size);
+
+ maybe_print_eds (controller, "ed_bulkhead",
+ ohci_readl (controller, ®s->ed_bulkhead),
+ next, size);
+ maybe_print_eds (controller, "ed_bulkcurrent",
+ ohci_readl (controller, ®s->ed_bulkcurrent),
+ next, size);
+
+ maybe_print_eds (controller, "donehead",
+ ohci_readl (controller, ®s->donehead), next, size);
+
+ /* broken fminterval means traffic won't flow! */
+ ohci_dbg (controller, "fminterval %08x\n",
+ ohci_readl (controller, ®s->fminterval));
+}
+
+#define dbg_port_sw(hc,num,value,next,size) \
+ ohci_dbg_sw (hc, next, size, \
+ "roothub.portstatus [%d] " \
+ "0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
+ num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+
+static void
+ohci_dump_roothub (
+ struct ohci_hcd *controller,
+ int verbose,
+ char **next,
+ unsigned *size)
+{
+ u32 temp, ndp, i;
+
+ temp = roothub_a (controller);
+ if (temp == ~(u32)0)
+ return;
+ ndp = (temp & RH_A_NDP);
+
+ if (verbose) {
+ ohci_dbg_sw (controller, next, size,
+ "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = roothub_b (controller);
+ ohci_dbg_sw (controller, next, size,
+ "roothub.b %08x PPCM=%04x DR=%04x\n",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status (controller);
+ ohci_dbg_sw (controller, next, size,
+ "roothub.status %08x%s%s%s%s%s%s\n",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ dbg_port_sw (controller, i, temp, next, size);
+ }
+}
+
+static void ohci_dump (struct ohci_hcd *controller, int verbose)
+{
+ ohci_dbg (controller, "OHCI controller state\n");
+
+ // dumps some of the state we know about
+ ohci_dump_status (controller, NULL, NULL);
+ if (controller->hcca)
+ ohci_dbg (controller,
+ "hcca frame #%04x\n", ohci_frame_no(controller));
+ ohci_dump_roothub (controller, 1, NULL, NULL);
+}
+
+static const char data0 [] = "DATA0";
+static const char data1 [] = "DATA1";
+
+static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
+ const struct td *td)
+{
+ u32 tmp = hc32_to_cpup (ohci, &td->hwINFO);
+
+ ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n",
+ label, td,
+ (tmp & TD_DONE) ? " (DONE)" : "",
+ td->urb, td->index,
+ hc32_to_cpup (ohci, &td->hwNextTD));
+ if ((tmp & TD_ISO) == 0) {
+ const char *toggle, *pid;
+ u32 cbp, be;
+
+ switch (tmp & TD_T) {
+ case TD_T_DATA0: toggle = data0; break;
+ case TD_T_DATA1: toggle = data1; break;
+ case TD_T_TOGGLE: toggle = "(CARRY)"; break;
+ default: toggle = "(?)"; break;
+ }
+ switch (tmp & TD_DP) {
+ case TD_DP_SETUP: pid = "SETUP"; break;
+ case TD_DP_IN: pid = "IN"; break;
+ case TD_DP_OUT: pid = "OUT"; break;
+ default: pid = "(bad pid)"; break;
+ }
+ ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s\n", tmp,
+ TD_CC_GET(tmp), /* EC, */ toggle,
+ (tmp & TD_DI) >> 21, pid,
+ (tmp & TD_R) ? "R" : "");
+ cbp = hc32_to_cpup (ohci, &td->hwCBP);
+ be = hc32_to_cpup (ohci, &td->hwBE);
+ ohci_dbg (ohci, " cbp %08x be %08x (len %d)\n", cbp, be,
+ cbp ? (be + 1 - cbp) : 0);
+ } else {
+ unsigned i;
+ ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp,
+ TD_CC_GET(tmp),
+ (tmp >> 24) & 0x07,
+ (tmp & TD_DI) >> 21,
+ tmp & 0x0000ffff);
+ ohci_dbg (ohci, " bp0 %08x be %08x\n",
+ hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
+ hc32_to_cpup (ohci, &td->hwBE));
+ for (i = 0; i < MAXPSW; i++) {
+ u16 psw = ohci_hwPSW (ohci, td, i);
+ int cc = (psw >> 12) & 0x0f;
+ ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i,
+ psw, cc,
+ (cc >= 0x0e) ? "OFFSET" : "SIZE",
+ psw & 0x0fff);
+ }
+ }
+}
+
+/* caller MUST own hcd spinlock if verbose is set! */
+static void __attribute__((unused))
+ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
+ const struct ed *ed, int verbose)
+{
+ u32 tmp = hc32_to_cpu (ohci, ed->hwINFO);
+ char *type = "";
+
+ ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n",
+ label,
+ ed, ed->state, edstring (ed->type),
+ hc32_to_cpup (ohci, &ed->hwNextED));
+ switch (tmp & (ED_IN|ED_OUT)) {
+ case ED_OUT: type = "-OUT"; break;
+ case ED_IN: type = "-IN"; break;
+ /* else from TDs ... control */
+ }
+ ohci_dbg (ohci,
+ " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", tmp,
+ 0x03ff & (tmp >> 16),
+ (tmp & ED_DEQUEUE) ? " DQ" : "",
+ (tmp & ED_ISO) ? " ISO" : "",
+ (tmp & ED_SKIP) ? " SKIP" : "",
+ (tmp & ED_LOWSPEED) ? " LOW" : "",
+ 0x000f & (tmp >> 7),
+ type,
+ 0x007f & tmp);
+ tmp = hc32_to_cpup (ohci, &ed->hwHeadP);
+ ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s\n",
+ tmp,
+ (tmp & ED_C) ? data1 : data0,
+ (tmp & ED_H) ? " HALT" : "",
+ hc32_to_cpup (ohci, &ed->hwTailP),
+ verbose ? "" : " (not listing)");
+ if (verbose) {
+ struct list_head *tmp;
+
+ /* use ed->td_list because HC concurrently modifies
+ * hwNextTD as it accumulates ed_donelist.
+ */
+ list_for_each (tmp, &ed->td_list) {
+ struct td *td;
+ td = list_entry (tmp, struct td, td_list);
+ ohci_dump_td (ohci, " ->", td);
+ }
+ }
+}
+
+#else
+static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {}
+
+#undef OHCI_VERBOSE_DEBUG
+
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILES
+
+static inline void create_debug_files (struct ohci_hcd *bus) { }
+static inline void remove_debug_files (struct ohci_hcd *bus) { }
+
+#else
+
+static ssize_t
+show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
+{
+ unsigned temp, size = count;
+
+ if (!ed)
+ return 0;
+
+ /* print first --> last */
+ while (ed->ed_prev)
+ ed = ed->ed_prev;
+
+ /* dump a snapshot of the bulk or control schedule */
+ while (ed) {
+ u32 info = hc32_to_cpu (ohci, ed->hwINFO);
+ u32 headp = hc32_to_cpu (ohci, ed->hwHeadP);
+ struct list_head *entry;
+ struct td *td;
+
+ temp = scnprintf (buf, size,
+ "ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",
+ ed,
+ (info & ED_LOWSPEED) ? 'l' : 'f',
+ info & 0x7f,
+ (info >> 7) & 0xf,
+ (info & ED_IN) ? "in" : "out",
+ 0x03ff & (info >> 16),
+ info,
+ (info & ED_SKIP) ? " s" : "",
+ (headp & ED_H) ? " H" : "",
+ (headp & ED_C) ? data1 : data0);
+ size -= temp;
+ buf += temp;
+
+ list_for_each (entry, &ed->td_list) {
+ u32 cbp, be;
+
+ td = list_entry (entry, struct td, td_list);
+ info = hc32_to_cpup (ohci, &td->hwINFO);
+ cbp = hc32_to_cpup (ohci, &td->hwCBP);
+ be = hc32_to_cpup (ohci, &td->hwBE);
+ temp = scnprintf (buf, size,
+ "\n\ttd %p %s %d cc=%x urb %p (%08x)",
+ td,
+ ({ char *pid;
+ switch (info & TD_DP) {
+ case TD_DP_SETUP: pid = "setup"; break;
+ case TD_DP_IN: pid = "in"; break;
+ case TD_DP_OUT: pid = "out"; break;
+ default: pid = "(?)"; break;
+ } pid;}),
+ cbp ? (be + 1 - cbp) : 0,
+ TD_CC_GET (info), td->urb, info);
+ size -= temp;
+ buf += temp;
+ }
+
+ temp = scnprintf (buf, size, "\n");
+ size -= temp;
+ buf += temp;
+
+ ed = ed->ed_next;
+ }
+ return count - size;
+}
+
+static ssize_t
+show_async (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ size_t temp;
+ unsigned long flags;
+
+ bus = to_usb_bus(class_dev);
+ hcd = bus->hcpriv;
+ ohci = hcd_to_ohci(hcd);
+
+ /* display control and bulk lists together, for simplicity */
+ spin_lock_irqsave (&ohci->lock, flags);
+ temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail);
+ temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return temp;
+}
+static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
+
+
+#define DBG_SCHED_LIMIT 64
+
+static ssize_t
+show_periodic (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ struct ed **seen, *ed;
+ unsigned long flags;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+
+ if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
+ return 0;
+ seen_count = 0;
+
+ bus = to_usb_bus(class_dev);
+ hcd = bus->hcpriv;
+ ohci = hcd_to_ohci(hcd);
+ next = buf;
+ size = PAGE_SIZE;
+
+ temp = scnprintf (next, size, "size = %d\n", NUM_INTS);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule (and load) */
+ spin_lock_irqsave (&ohci->lock, flags);
+ for (i = 0; i < NUM_INTS; i++) {
+ if (!(ed = ohci->periodic [i]))
+ continue;
+
+ temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
+ size -= temp;
+ next += temp;
+
+ do {
+ temp = scnprintf (next, size, " ed%d/%p",
+ ed->interval, ed);
+ size -= temp;
+ next += temp;
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen [temp] == ed)
+ break;
+ }
+
+ /* show more info the first time around */
+ if (temp == seen_count) {
+ u32 info = hc32_to_cpu (ohci, ed->hwINFO);
+ struct list_head *entry;
+ unsigned qlen = 0;
+
+ /* qlen measured here in TDs, not urbs */
+ list_for_each (entry, &ed->td_list)
+ qlen++;
+
+ temp = scnprintf (next, size,
+ " (%cs dev%d ep%d%s-%s qlen %u"
+ " max %d %08x%s%s)",
+ (info & ED_LOWSPEED) ? 'l' : 'f',
+ info & 0x7f,
+ (info >> 7) & 0xf,
+ (info & ED_IN) ? "in" : "out",
+ (info & ED_ISO) ? "iso" : "int",
+ qlen,
+ 0x03ff & (info >> 16),
+ info,
+ (info & ED_SKIP) ? " K" : "",
+ (ed->hwHeadP &
+ cpu_to_hc32(ohci, ED_H)) ?
+ " H" : "");
+ size -= temp;
+ next += temp;
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen [seen_count++] = ed;
+
+ ed = ed->ed_next;
+
+ } else {
+ /* we've seen it and what's after */
+ temp = 0;
+ ed = NULL;
+ }
+
+ } while (ed);
+
+ temp = scnprintf (next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ kfree (seen);
+
+ return PAGE_SIZE - size;
+}
+static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
+
+
+#undef DBG_SCHED_LIMIT
+
+static ssize_t
+show_registers (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ struct ohci_regs __iomem *regs;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ u32 rdata;
+
+ bus = to_usb_bus(class_dev);
+ hcd = bus->hcpriv;
+ ohci = hcd_to_ohci(hcd);
+ regs = ohci->regs;
+ next = buf;
+ size = PAGE_SIZE;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* dump driver info, then registers in spec order */
+
+ ohci_dbg_sw (ohci, &next, &size,
+ "bus %s, device %s\n"
+ "%s\n"
+ "%s version " DRIVER_VERSION "\n",
+ hcd->self.controller->bus->name,
+ hcd->self.controller->bus_id,
+ hcd->product_desc,
+ hcd_name);
+
+ if (bus->controller->power.power_state) {
+ size -= scnprintf (next, size,
+ "SUSPENDED (no register access)\n");
+ goto done;
+ }
+
+ ohci_dump_status(ohci, &next, &size);
+
+ /* hcca */
+ if (ohci->hcca)
+ ohci_dbg_sw (ohci, &next, &size,
+ "hcca frame 0x%04x\n", ohci_frame_no(ohci));
+
+ /* other registers mostly affect frame timings */
+ rdata = ohci_readl (ohci, ®s->fminterval);
+ temp = scnprintf (next, size,
+ "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
+ rdata, (rdata >> 31) ? "FIT " : "",
+ (rdata >> 16) & 0xefff, rdata & 0xffff);
+ size -= temp;
+ next += temp;
+
+ rdata = ohci_readl (ohci, ®s->fmremaining);
+ temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
+ rdata, (rdata >> 31) ? "FRT " : "",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ rdata = ohci_readl (ohci, ®s->periodicstart);
+ temp = scnprintf (next, size, "periodicstart 0x%04x\n",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ rdata = ohci_readl (ohci, ®s->lsthresh);
+ temp = scnprintf (next, size, "lsthresh 0x%04x\n",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ /* roothub */
+ ohci_dump_roothub (ohci, 1, &next, &size);
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return PAGE_SIZE - size;
+}
+static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+
+static inline void create_debug_files (struct ohci_hcd *ohci)
+{
+ struct class_device *cldev = &ohci_to_hcd(ohci)->self.class_dev;
+
+ class_device_create_file(cldev, &class_device_attr_async);
+ class_device_create_file(cldev, &class_device_attr_periodic);
+ class_device_create_file(cldev, &class_device_attr_registers);
+ ohci_dbg (ohci, "created debug files\n");
+}
+
+static inline void remove_debug_files (struct ohci_hcd *ohci)
+{
+ struct class_device *cldev = &ohci_to_hcd(ohci)->self.class_dev;
+
+ class_device_remove_file(cldev, &class_device_attr_async);
+ class_device_remove_file(cldev, &class_device_attr_periodic);
+ class_device_remove_file(cldev, &class_device_attr_registers);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
new file mode 100644
index 0000000..1e27f10
--- /dev/null
+++ b/drivers/usb/host/ohci-hcd.c
@@ -0,0 +1,925 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ *
+ * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
+ * interfaces (though some non-x86 Intel chips use it). It supports
+ * smarter hardware than UHCI. A download link for the spec available
+ * through the http://www.usb.org website.
+ *
+ * History:
+ *
+ * 2004/03/24 LH7A404 support (Durgesh Pattamatta & Marc Singer)
+ * 2004/02/04 use generic dma_* functions instead of pci_* (dsaxena@plexity.net)
+ * 2003/02/24 show registers in sysfs (Kevin Brosius)
+ *
+ * 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
+ * bandwidth accounting; if debugging, show schedules in driverfs
+ * 2002/07/19 fixes to management of ED and schedule state.
+ * 2002/06/09 SA-1111 support (Christopher Hoover)
+ * 2002/06/01 remember frame when HC won't see EDs any more; use that info
+ * to fix urb unlink races caused by interrupt latency assumptions;
+ * minor ED field and function naming updates
+ * 2002/01/18 package as a patch for 2.5.3; this should match the
+ * 2.4.17 kernel modulo some bugs being fixed.
+ *
+ * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
+ * from post-2.4.5 patches.
+ * 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning
+ * 2001/09/07 match PCI PM changes, errnos from Linus' tree
+ * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
+ * pbook pci quirks gone (please fix pbook pci sw!) (db)
+ *
+ * 2001/04/08 Identify version on module load (gb)
+ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
+ pci_map_single (db)
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
+ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
+ *
+ * 2000/09/26 fixed races in removing the private portion of the urb
+ * 2000/09/07 disable bulk and control lists when unlinking the last
+ * endpoint descriptor in order to avoid unrecoverable errors on
+ * the Lucent chips. (rwc@sgi)
+ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
+ * urb unlink probs, indentation fixes
+ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
+ * device unplugs.
+ * 2000/06/28 use PCI hotplug framework, for better power management
+ * and for Cardbus support (David Brownell)
+ * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
+ * when the controller loses power; handle UE; cleanup; ...
+ *
+ * v5.2 1999/12/07 URB 3rd preview,
+ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
+ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
+ * i386: HUB, Keyboard, Mouse, Printer
+ *
+ * v4.3 1999/10/27 multiple HCs, bulk_request
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18
+ * v3.0 1999/06/25
+ * v2.1 1999/05/09 code clean up
+ * v2.0 1999/05/04
+ * v1.0 1999/04/27 initial release
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+# define DEBUG
+#else
+# undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.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> /* for in_interrupt () */
+#include <linux/usb.h>
+#include <linux/usb_otg.h>
+#include "../core/hcd.h"
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h> /* needed by ohci-mem.c when no PCI */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+
+#define DRIVER_VERSION "2004 Nov 08"
+#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
+#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
+
+/*-------------------------------------------------------------------------*/
+
+// #define OHCI_VERBOSE_DEBUG /* not always helpful */
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT \
+ (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+
+#ifdef __hppa__
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#define IR_DISABLE
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+/* OMAP doesn't support IR (no SMM; not needed) */
+#define IR_DISABLE
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static const char hcd_name [] = "ohci_hcd";
+
+#include "ohci.h"
+
+static void ohci_dump (struct ohci_hcd *ohci, int verbose);
+static int ohci_init (struct ohci_hcd *ohci);
+static void ohci_stop (struct usb_hcd *hcd);
+
+#include "ohci-hub.c"
+#include "ohci-dbg.c"
+#include "ohci-mem.c"
+#include "ohci-q.c"
+
+
+/*
+ * On architectures with edge-triggered interrupts we must never return
+ * IRQ_NONE.
+ */
+#if defined(CONFIG_SA1111) /* ... or other edge-triggered systems */
+#define IRQ_NOTMINE IRQ_HANDLED
+#else
+#define IRQ_NOTMINE IRQ_NONE
+#endif
+
+
+/* Some boards misreport power switching/overcurrent */
+static int distrust_firmware = 1;
+module_param (distrust_firmware, bool, 0);
+MODULE_PARM_DESC (distrust_firmware,
+ "true to distrust firmware power/overcurrent setup");
+
+/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
+static int no_handshake = 0;
+module_param (no_handshake, bool, 0);
+MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * queue up an urb for anything except the root hub
+ */
+static int ohci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ed *ed;
+ urb_priv_t *urb_priv;
+ unsigned int pipe = urb->pipe;
+ int i, size = 0;
+ unsigned long flags;
+ int retval = 0;
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+
+ /* every endpoint has a ed, locate and maybe (re)initialize it */
+ if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval)))
+ return -ENOMEM;
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ /* td_submit_urb() doesn't yet handle these */
+ if (urb->transfer_buffer_length > 4096)
+ return -EMSGSIZE;
+
+ /* 1 TD for setup, 1 for ACK, plus ... */
+ size = 2;
+ /* FALLTHROUGH */
+ // case PIPE_INTERRUPT:
+ // case PIPE_BULK:
+ default:
+ /* one TD for every 4096 Bytes (can be upto 8K) */
+ size += urb->transfer_buffer_length / 4096;
+ /* ... and for any remaining bytes ... */
+ if ((urb->transfer_buffer_length % 4096) != 0)
+ size++;
+ /* ... and maybe a zero length packet to wrap it up */
+ if (size == 0)
+ size++;
+ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
+ && (urb->transfer_buffer_length
+ % usb_maxpacket (urb->dev, pipe,
+ usb_pipeout (pipe))) == 0)
+ size++;
+ break;
+ case PIPE_ISOCHRONOUS: /* number of packets from URB */
+ size = urb->number_of_packets;
+ break;
+ }
+
+ /* allocate the private part of the URB */
+ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
+ mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+ memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
+ INIT_LIST_HEAD (&urb_priv->pending);
+ urb_priv->length = size;
+ urb_priv->ed = ed;
+
+ /* allocate the TDs (deferring hash chain updates) */
+ for (i = 0; i < size; i++) {
+ urb_priv->td [i] = td_alloc (ohci, mem_flags);
+ if (!urb_priv->td [i]) {
+ urb_priv->length = i;
+ urb_free_priv (ohci, urb_priv);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* don't submit to a dead HC */
+ if (!HC_IS_RUNNING(hcd->state)) {
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ /* in case of unlink-during-submit */
+ spin_lock (&urb->lock);
+ if (urb->status != -EINPROGRESS) {
+ spin_unlock (&urb->lock);
+ urb->hcpriv = urb_priv;
+ finish_urb (ohci, urb, NULL);
+ retval = 0;
+ goto fail;
+ }
+
+ /* schedule the ed if needed */
+ if (ed->state == ED_IDLE) {
+ retval = ed_schedule (ohci, ed);
+ if (retval < 0)
+ goto fail0;
+ if (ed->type == PIPE_ISOCHRONOUS) {
+ u16 frame = ohci_frame_no(ohci);
+
+ /* delay a few frames before the first TD */
+ frame += max_t (u16, 8, ed->interval);
+ frame &= ~(ed->interval - 1);
+ frame |= ed->branch;
+ urb->start_frame = frame;
+
+ /* yes, only URB_ISO_ASAP is supported, and
+ * urb->start_frame is never used as input.
+ */
+ }
+ } else if (ed->type == PIPE_ISOCHRONOUS)
+ urb->start_frame = ed->last_iso + ed->interval;
+
+ /* fill the TDs and link them to the ed; and
+ * enable that part of the schedule, if needed
+ * and update count of queued periodic urbs
+ */
+ urb->hcpriv = urb_priv;
+ td_submit_urb (ohci, urb);
+
+fail0:
+ spin_unlock (&urb->lock);
+fail:
+ if (retval)
+ urb_free_priv (ohci, urb_priv);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return retval;
+}
+
+/*
+ * decouple the URB from the HC queues (TDs, urb_priv); it's
+ * already marked using urb->status. reporting is always done
+ * asynchronously, and we might be dealing with an urb that's
+ * partially transferred, or an ED with other urbs being unlinked.
+ */
+static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "UNLINK", 1);
+#endif
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ if (HC_IS_RUNNING(hcd->state)) {
+ urb_priv_t *urb_priv;
+
+ /* Unless an IRQ completed the unlink while it was being
+ * handed to us, flag it for unlink and giveback, and force
+ * some upcoming INTR_SF to call finish_unlinks()
+ */
+ urb_priv = urb->hcpriv;
+ if (urb_priv) {
+ if (urb_priv->ed->state == ED_OPER)
+ start_ed_unlink (ohci, urb_priv->ed);
+ }
+ } else {
+ /*
+ * with HC dead, we won't respect hc queue pointers
+ * any more ... just clean up every urb's memory.
+ */
+ if (urb->hcpriv)
+ finish_urb (ohci, urb, NULL);
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* frees config/altsetting state for endpoints,
+ * including ED memory, dummy TD, and bulk/intr data toggle
+ */
+
+static void
+ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ struct ed *ed = ep->hcpriv;
+ unsigned limit = 1000;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+ if (!ed)
+ return;
+
+rescan:
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ if (!HC_IS_RUNNING (hcd->state)) {
+sanitize:
+ ed->state = ED_IDLE;
+ finish_unlinks (ohci, 0, NULL);
+ }
+
+ switch (ed->state) {
+ case ED_UNLINK: /* wait for hw to finish? */
+ /* major IRQ delivery trouble loses INTR_SF too... */
+ if (limit-- == 0) {
+ ohci_warn (ohci, "IRQ INTR_SF lossage\n");
+ goto sanitize;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ schedule_timeout (1);
+ goto rescan;
+ case ED_IDLE: /* fully unlinked */
+ if (list_empty (&ed->td_list)) {
+ td_free (ohci, ed->dummy);
+ ed_free (ohci, ed);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. can't recover; must leak ed.
+ */
+ ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n",
+ ed, ep->desc.bEndpointAddress, ed->state,
+ list_empty (&ed->td_list) ? "" : " (has tds)");
+ td_free (ohci, ed->dummy);
+ break;
+ }
+ ep->hcpriv = NULL;
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return;
+}
+
+static int ohci_get_frame (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ return ohci_frame_no(ohci);
+}
+
+static void ohci_usb_reset (struct ohci_hcd *ohci)
+{
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* init memory, and kick BIOS/SMM off */
+
+static int ohci_init (struct ohci_hcd *ohci)
+{
+ int ret;
+
+ disable (ohci);
+ ohci->regs = ohci_to_hcd(ohci)->regs;
+ ohci->next_statechange = jiffies;
+
+#ifndef IR_DISABLE
+ /* SMM owns the HC? not for long! */
+ if (!no_handshake && ohci_readl (ohci,
+ &ohci->regs->control) & OHCI_CTRL_IR) {
+ u32 temp;
+
+ ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
+
+ /* this timeout is arbitrary. we make it long, so systems
+ * depending on usb keyboards may be usable even if the
+ * BIOS/SMM code seems pretty broken.
+ */
+ temp = 500; /* arbitrary: five seconds */
+
+ ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);
+ ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);
+ while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {
+ msleep (10);
+ if (--temp == 0) {
+ ohci_err (ohci, "USB HC takeover failed!"
+ " (BIOS/SMM bug)\n");
+ return -EBUSY;
+ }
+ }
+ ohci_usb_reset (ohci);
+ }
+#endif
+
+ /* Disable HC interrupts */
+ ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ // flush the writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ if (ohci->hcca)
+ return 0;
+
+ ohci->hcca = dma_alloc_coherent (ohci_to_hcd(ohci)->self.controller,
+ sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ if ((ret = ohci_mem_init (ohci)) < 0)
+ ohci_stop (ohci_to_hcd(ohci));
+
+ return ret;
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * resets USB and controller
+ * enable interrupts
+ * connect the virtual root hub
+ */
+static int ohci_run (struct ohci_hcd *ohci)
+{
+ u32 mask, temp;
+ struct usb_device *udev;
+ struct usb_bus *bus;
+ int first = ohci->fminterval == 0;
+
+ disable (ohci);
+
+ /* boot firmware should have set this up (5.1.1.3.1) */
+ if (first) {
+
+ temp = ohci_readl (ohci, &ohci->regs->fminterval);
+ ohci->fminterval = temp & 0x3fff;
+ if (ohci->fminterval != FI)
+ ohci_dbg (ohci, "fminterval delta %d\n",
+ ohci->fminterval - FI);
+ ohci->fminterval |= FSMP (ohci->fminterval) << 16;
+ /* also: power/overcurrent flags in roothub.a */
+ }
+
+ /* Reset USB nearly "by the book". RemoteWakeupConnected
+ * saved if boot firmware (BIOS/SMM/...) told us it's connected
+ * (for OHCI integrated on mainboard, it normally is)
+ */
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ ohci->hc_control);
+
+ if (ohci->hc_control & OHCI_CTRL_RWC
+ && !(ohci->flags & OHCI_QUIRK_AMD756))
+ ohci_to_hcd(ohci)->can_wakeup = 1;
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ temp = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESUME;
+ temp = 10 /* msec wait */;
+ break;
+ // case OHCI_USB_RESET:
+ default:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESET;
+ temp = 50 /* msec wait */;
+ break;
+ }
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ // flush the writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ msleep(temp);
+ temp = roothub_a (ohci);
+ if (!(temp & RH_A_NPS)) {
+ unsigned ports = temp & RH_A_NDP;
+
+ /* power down each port */
+ for (temp = 0; temp < ports; temp++)
+ ohci_writel (ohci, RH_PS_LSDA,
+ &ohci->regs->roothub.portstatus [temp]);
+ }
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+ /* 2msec timelimit here means no irqs/preempt */
+ spin_lock_irq (&ohci->lock);
+
+retry:
+ /* HC Reset requires max 10 us delay */
+ ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus);
+ temp = 30; /* ... allow extra time */
+ while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--temp == 0) {
+ spin_unlock_irq (&ohci->lock);
+ ohci_err (ohci, "USB HC reset timed out!\n");
+ return -1;
+ }
+ udelay (1);
+ }
+
+ /* now we're in the SUSPEND state ... must go OPERATIONAL
+ * within 2msec else HC enters RESUME
+ *
+ * ... but some hardware won't init fmInterval "by the book"
+ * (SiS, OPTi ...), so reset again instead. SiS doesn't need
+ * this if we write fmInterval after we're OPERATIONAL.
+ * Unclear about ALi, ServerWorks, and others ... this could
+ * easily be a longstanding bug in chip init on Linux.
+ */
+ if (ohci->flags & OHCI_QUIRK_INITRESET) {
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ }
+
+ /* Tell the controller where the control and bulk lists are
+ * The lists are empty now. */
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
+
+ /* a reset clears this */
+ ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
+
+ periodic_reinit (ohci);
+
+ /* some OHCI implementations are finicky about how they init.
+ * bogus values here mean not even enumeration could work.
+ */
+ if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
+ || !ohci_readl (ohci, &ohci->regs->periodicstart)) {
+ if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
+ ohci->flags |= OHCI_QUIRK_INITRESET;
+ ohci_dbg (ohci, "enabling initreset quirk\n");
+ goto retry;
+ }
+ spin_unlock_irq (&ohci->lock);
+ ohci_err (ohci, "init err (%08x %04x)\n",
+ ohci_readl (ohci, &ohci->regs->fminterval),
+ ohci_readl (ohci, &ohci->regs->periodicstart));
+ return -EOVERFLOW;
+ }
+
+ /* start controller operations */
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
+
+ /* wake on ConnectStatusChange, matching external hubs */
+ ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_INIT;
+ ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+ ohci_writel (ohci, mask, &ohci->regs->intrenable);
+
+ /* handle root hub init quirks ... */
+ temp = roothub_a (ohci);
+ temp &= ~(RH_A_PSM | RH_A_OCPM);
+ if (ohci->flags & OHCI_QUIRK_SUPERIO) {
+ /* NSC 87560 and maybe others */
+ temp |= RH_A_NOCP;
+ temp &= ~(RH_A_POTPGT | RH_A_NPS);
+ ohci_writel (ohci, temp, &ohci->regs->roothub.a);
+ } else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
+ /* hub power always on; required for AMD-756 and some
+ * Mac platforms. ganged overcurrent reporting, if any.
+ */
+ temp |= RH_A_NPS;
+ ohci_writel (ohci, temp, &ohci->regs->roothub.a);
+ }
+ ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
+ ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
+ &ohci->regs->roothub.b);
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ spin_unlock_irq (&ohci->lock);
+
+ // POTPGT delay is bits 24-31, in 2 ms units.
+ mdelay ((temp >> 23) & 0x1fe);
+ bus = &ohci_to_hcd(ohci)->self;
+ ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
+
+ ohci_dump (ohci, 1);
+
+ udev = bus->root_hub;
+ if (udev) {
+ return 0;
+ }
+
+ /* connect the virtual root hub */
+ udev = usb_alloc_dev (NULL, bus, 0);
+ if (!udev) {
+ disable (ohci);
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ return -ENOMEM;
+ }
+
+ udev->speed = USB_SPEED_FULL;
+ if (usb_hcd_register_root_hub (udev, ohci_to_hcd(ohci)) != 0) {
+ usb_put_dev (udev);
+ disable (ohci);
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ return -ENODEV;
+ }
+ if (ohci->power_budget)
+ hub_set_power_budget(udev, ohci->power_budget);
+
+ create_debug_files (ohci);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ohci_regs __iomem *regs = ohci->regs;
+ int ints;
+
+ /* we can eliminate a (slow) ohci_readl()
+ if _only_ WDH caused this irq */
+ if ((ohci->hcca->done_head != 0)
+ && ! (hc32_to_cpup (ohci, &ohci->hcca->done_head)
+ & 0x01)) {
+ ints = OHCI_INTR_WDH;
+
+ /* cardbus/... hardware gone before remove() */
+ } else if ((ints = ohci_readl (ohci, ®s->intrstatus)) == ~(u32)0) {
+ disable (ohci);
+ ohci_dbg (ohci, "device removed!\n");
+ return IRQ_HANDLED;
+
+ /* interrupt for some other device? */
+ } else if ((ints &= ohci_readl (ohci, ®s->intrenable)) == 0) {
+ return IRQ_NOTMINE;
+ }
+
+ if (ints & OHCI_INTR_UE) {
+ disable (ohci);
+ ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
+ // e.g. due to PCI Master/Target Abort
+
+ ohci_dump (ohci, 1);
+ ohci_usb_reset (ohci);
+ }
+
+ if (ints & OHCI_INTR_RD) {
+ ohci_vdbg (ohci, "resume detect\n");
+ if (hcd->state != HC_STATE_QUIESCING)
+ schedule_work(&ohci->rh_resume);
+ }
+
+ if (ints & OHCI_INTR_WDH) {
+ if (HC_IS_RUNNING(hcd->state))
+ ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrdisable);
+ spin_lock (&ohci->lock);
+ dl_done_list (ohci, ptregs);
+ spin_unlock (&ohci->lock);
+ if (HC_IS_RUNNING(hcd->state))
+ ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrenable);
+ }
+
+ /* could track INTR_SO to reduce available PCI/... bandwidth */
+
+ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
+ * when there's still unlinking to be done (next frame).
+ */
+ spin_lock (&ohci->lock);
+ if (ohci->ed_rm_list)
+ finish_unlinks (ohci, ohci_frame_no(ohci), ptregs);
+ if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
+ && HC_IS_RUNNING(hcd->state))
+ ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);
+ spin_unlock (&ohci->lock);
+
+ if (HC_IS_RUNNING(hcd->state)) {
+ ohci_writel (ohci, ints, ®s->intrstatus);
+ ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable);
+ // flush those writes
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_stop (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ hcd->state);
+ ohci_dump (ohci, 1);
+
+ flush_scheduled_work();
+
+ ohci_usb_reset (ohci);
+ ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ remove_debug_files (ohci);
+ ohci_mem_cleanup (ohci);
+ if (ohci->hcca) {
+ dma_free_coherent (hcd->self.controller,
+ sizeof *ohci->hcca,
+ ohci->hcca, ohci->hcca_dma);
+ ohci->hcca = NULL;
+ ohci->hcca_dma = 0;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* must not be called from interrupt context */
+
+#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+
+static int ohci_restart (struct ohci_hcd *ohci)
+{
+ int temp;
+ int i;
+ struct urb_priv *priv;
+ struct usb_device *root = ohci_to_hcd(ohci)->self.root_hub;
+
+ /* mark any devices gone, so they do nothing till khubd disconnects.
+ * recycle any "live" eds/tds (and urbs) right away.
+ * later, khubd disconnect processing will recycle the other state,
+ * (either as disconnect/reconnect, or maybe someday as a reset).
+ */
+ spin_lock_irq(&ohci->lock);
+ disable (ohci);
+ for (i = 0; i < root->maxchild; i++) {
+ if (root->children [i])
+ usb_set_device_state (root->children[i],
+ USB_STATE_NOTATTACHED);
+ }
+ if (!list_empty (&ohci->pending))
+ ohci_dbg(ohci, "abort schedule...\n");
+ list_for_each_entry (priv, &ohci->pending, pending) {
+ struct urb *urb = priv->td[0]->urb;
+ struct ed *ed = priv->ed;
+
+ switch (ed->state) {
+ case ED_OPER:
+ ed->state = ED_UNLINK;
+ ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE);
+ ed_deschedule (ohci, ed);
+
+ ed->ed_next = ohci->ed_rm_list;
+ ed->ed_prev = NULL;
+ ohci->ed_rm_list = ed;
+ /* FALLTHROUGH */
+ case ED_UNLINK:
+ break;
+ default:
+ ohci_dbg(ohci, "bogus ed %p state %d\n",
+ ed, ed->state);
+ }
+
+ spin_lock (&urb->lock);
+ urb->status = -ESHUTDOWN;
+ spin_unlock (&urb->lock);
+ }
+ finish_unlinks (ohci, 0, NULL);
+ spin_unlock_irq(&ohci->lock);
+
+ /* paranoia, in case that didn't work: */
+
+ /* empty the interrupt branches */
+ for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
+ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
+
+ /* no EDs to remove */
+ ohci->ed_rm_list = NULL;
+
+ /* empty control and bulk lists */
+ ohci->ed_controltail = NULL;
+ ohci->ed_bulktail = NULL;
+
+ if ((temp = ohci_run (ohci)) < 0) {
+ ohci_err (ohci, "can't restart, %d\n", temp);
+ return temp;
+ } else {
+ /* here we "know" root ports should always stay powered,
+ * and that if we try to turn them back on the root hub
+ * will respond to CSC processing.
+ */
+ i = roothub_a (ohci) & RH_A_NDP;
+ while (i--)
+ ohci_writel (ohci, RH_PS_PSS,
+ &ohci->regs->roothub.portstatus [temp]);
+ ohci_dbg (ohci, "restart complete\n");
+ }
+ return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_LICENSE ("GPL");
+
+#ifdef CONFIG_PCI
+#include "ohci-pci.c"
+#endif
+
+#ifdef CONFIG_SA1111
+#include "ohci-sa1111.c"
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+#include "ohci-omap.c"
+#endif
+
+#ifdef CONFIG_ARCH_LH7A404
+#include "ohci-lh7a404.c"
+#endif
+
+#ifdef CONFIG_PXA27x
+#include "ohci-pxa27x.c"
+#endif
+
+#ifdef CONFIG_SOC_AU1X00
+#include "ohci-au1xxx.c"
+#endif
+
+#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC
+#include "ohci-ppc-soc.c"
+#endif
+
+#if !(defined(CONFIG_PCI) \
+ || defined(CONFIG_SA1111) \
+ || defined(CONFIG_ARCH_OMAP) \
+ || defined (CONFIG_ARCH_LH7A404) \
+ || defined (CONFIG_PXA27x) \
+ || defined (CONFIG_SOC_AU1X00) \
+ || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
+ )
+#error "missing bus glue for ohci-hcd"
+#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
new file mode 100644
index 0000000..e2fc412
--- /dev/null
+++ b/drivers/usb/host/ohci-hub.c
@@ -0,0 +1,643 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * OHCI Root Hub ... the nonsharable stuff
+ */
+
+#define dbg_port(hc,label,num,value) \
+ ohci_dbg (hc, \
+ "%s roothub.portstatus [%d] " \
+ "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
+ label, num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+
+#define OHCI_SCHED_ENABLES \
+ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
+
+static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
+static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
+static int ohci_restart (struct ohci_hcd *ohci);
+
+static int ohci_hub_suspend (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int status = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESUME:
+ ohci_dbg (ohci, "resume/suspend?\n");
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ ohci->hc_control |= OHCI_USB_RESET;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ /* FALL THROUGH */
+ case OHCI_USB_RESET:
+ status = -EBUSY;
+ ohci_dbg (ohci, "needs reinit!\n");
+ goto done;
+ case OHCI_USB_SUSPEND:
+ ohci_dbg (ohci, "already suspended\n");
+ goto done;
+ }
+ ohci_dbg (ohci, "suspend root hub\n");
+
+ /* First stop any processing */
+ hcd->state = HC_STATE_QUIESCING;
+ if (ohci->hc_control & OHCI_SCHED_ENABLES) {
+ int limit;
+
+ ohci->hc_control &= ~OHCI_SCHED_ENABLES;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
+
+ /* sched disables take effect on the next frame,
+ * then the last WDH could take 6+ msec
+ */
+ ohci_dbg (ohci, "stopping schedules ...\n");
+ limit = 2000;
+ while (limit > 0) {
+ udelay (250);
+ limit =- 250;
+ if (ohci_readl (ohci, &ohci->regs->intrstatus)
+ & OHCI_INTR_SF)
+ break;
+ }
+ dl_done_list (ohci, NULL);
+ mdelay (7);
+ }
+ dl_done_list (ohci, NULL);
+ finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
+ ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
+ &ohci->regs->intrstatus);
+
+ /* maybe resume can wake root hub */
+ if (hcd->remote_wakeup)
+ ohci->hc_control |= OHCI_CTRL_RWE;
+ else
+ ohci->hc_control &= ~OHCI_CTRL_RWE;
+
+ /* Suspend hub */
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ ohci->hc_control |= OHCI_USB_SUSPEND;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* no resumes until devices finish suspending */
+ ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+
+done:
+ if (status == 0)
+ hcd->state = HC_STATE_SUSPENDED;
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return status;
+}
+
+static inline struct ed *find_head (struct ed *ed)
+{
+ /* for bulk and control lists */
+ while (ed->ed_prev)
+ ed = ed->ed_prev;
+ return ed;
+}
+
+/* caller has locked the root hub */
+static int ohci_hub_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ u32 temp, enables;
+ int status = -EINPROGRESS;
+
+ if (time_before (jiffies, ohci->next_statechange))
+ msleep(5);
+
+ spin_lock_irq (&ohci->lock);
+ ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+
+ if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+ /* this can happen after suspend-to-disk */
+ if (hcd->state == HC_STATE_RESUMING) {
+ ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
+ ohci->hc_control);
+ status = -EBUSY;
+ /* this happens when pmcore resumes HC then root */
+ } else {
+ ohci_dbg (ohci, "duplicate resume\n");
+ status = 0;
+ }
+ } else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_SUSPEND:
+ ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
+ ohci->hc_control |= OHCI_USB_RESUME;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ ohci_dbg (ohci, "resume root hub\n");
+ break;
+ case OHCI_USB_RESUME:
+ /* HCFS changes sometime after INTR_RD */
+ ohci_info (ohci, "wakeup\n");
+ break;
+ case OHCI_USB_OPER:
+ ohci_dbg (ohci, "already resumed\n");
+ status = 0;
+ break;
+ default: /* RESET, we lost power */
+ ohci_dbg (ohci, "root hub hardware reset\n");
+ status = -EBUSY;
+ }
+ spin_unlock_irq (&ohci->lock);
+ if (status == -EBUSY) {
+ (void) ohci_init (ohci);
+ return ohci_restart (ohci);
+ }
+ if (status != -EINPROGRESS)
+ return status;
+
+ temp = roothub_a (ohci) & RH_A_NDP;
+ enables = 0;
+ while (temp--) {
+ u32 stat = ohci_readl (ohci,
+ &ohci->regs->roothub.portstatus [temp]);
+
+ /* force global, not selective, resume */
+ if (!(stat & RH_PS_PSS))
+ continue;
+ ohci_writel (ohci, RH_PS_POCI,
+ &ohci->regs->roothub.portstatus [temp]);
+ }
+
+ /* Some controllers (lucent erratum) need extra-long delays */
+ hcd->state = HC_STATE_RESUMING;
+ mdelay (20 /* usb 11.5.1.10 */ + 15);
+
+ temp = ohci_readl (ohci, &ohci->regs->control);
+ temp &= OHCI_CTRL_HCFS;
+ if (temp != OHCI_USB_RESUME) {
+ ohci_err (ohci, "controller won't resume\n");
+ return -EBUSY;
+ }
+
+ /* disable old schedule state, reinit from scratch */
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
+ ohci_writel (ohci, 0, &ohci->regs->ed_periodcurrent);
+ ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
+
+ /* Sometimes PCI D3 suspend trashes frame timings ... */
+ periodic_reinit (ohci);
+
+ /* interrupts might have been disabled */
+ ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
+ if (ohci->ed_rm_list)
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
+ ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
+ &ohci->regs->intrstatus);
+
+ /* Then re-enable operations */
+ ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ msleep (3);
+
+ temp = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ if (hcd->can_wakeup)
+ temp |= OHCI_CTRL_RWC;
+ ohci->hc_control = temp;
+ ohci_writel (ohci, temp, &ohci->regs->control);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* TRSMRCY */
+ msleep (10);
+
+ /* keep it alive for ~5x suspend + resume costs */
+ ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+
+ /* maybe turn schedules back on */
+ enables = 0;
+ temp = 0;
+ if (!ohci->ed_rm_list) {
+ if (ohci->ed_controltail) {
+ ohci_writel (ohci,
+ find_head (ohci->ed_controltail)->dma,
+ &ohci->regs->ed_controlhead);
+ enables |= OHCI_CTRL_CLE;
+ temp |= OHCI_CLF;
+ }
+ if (ohci->ed_bulktail) {
+ ohci_writel (ohci, find_head (ohci->ed_bulktail)->dma,
+ &ohci->regs->ed_bulkhead);
+ enables |= OHCI_CTRL_BLE;
+ temp |= OHCI_BLF;
+ }
+ }
+ if (hcd->self.bandwidth_isoc_reqs || hcd->self.bandwidth_int_reqs)
+ enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
+ if (enables) {
+ ohci_dbg (ohci, "restarting schedules ... %08x\n", enables);
+ ohci->hc_control |= enables;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ if (temp)
+ ohci_writel (ohci, temp, &ohci->regs->cmdstatus);
+ (void) ohci_readl (ohci, &ohci->regs->control);
+ }
+
+ hcd->state = HC_STATE_RUNNING;
+ return 0;
+}
+
+static void ohci_rh_resume (void *_hcd)
+{
+ struct usb_hcd *hcd = _hcd;
+
+ usb_lock_device (hcd->self.root_hub);
+ (void) ohci_hub_resume (hcd);
+ usb_unlock_device (hcd->self.root_hub);
+}
+
+#else
+
+static void ohci_rh_resume (void *_hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (_hcd);
+ ohci_dbg(ohci, "rh_resume ??\n");
+}
+
+#endif /* CONFIG_USB_SUSPEND || CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports, i, changed = 0, length = 1;
+ int can_suspend = hcd->can_wakeup;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* handle autosuspended root: finish resuming before
+ * letting khubd or root hub timer see state changes.
+ */
+ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
+ || !HC_IS_RUNNING(hcd->state)) {
+ can_suspend = 0;
+ goto done;
+ }
+
+ ports = roothub_a (ohci) & RH_A_NDP;
+ if (ports > MAX_ROOT_PORTS) {
+ ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports,
+ ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
+ /* retry later; "should not happen" */
+ goto done;
+ }
+
+ /* init status */
+ if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
+ buf [0] = changed = 1;
+ else
+ buf [0] = 0;
+ if (ports > 7) {
+ buf [1] = 0;
+ length++;
+ }
+
+ /* look at each port */
+ for (i = 0; i < ports; i++) {
+ u32 status = roothub_portstatus (ohci, i);
+
+ if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+ | RH_PS_OCIC | RH_PS_PRSC)) {
+ changed = 1;
+ if (i < 7)
+ buf [0] |= 1 << (i + 1);
+ else
+ buf [1] |= 1 << (i - 7);
+ continue;
+ }
+
+ /* can suspend if no ports are enabled; or if all all
+ * enabled ports are suspended AND remote wakeup is on.
+ */
+ if (!(status & RH_PS_CCS))
+ continue;
+ if ((status & RH_PS_PSS) && hcd->remote_wakeup)
+ continue;
+ can_suspend = 0;
+ }
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+#ifdef CONFIG_PM
+ /* save power by suspending idle root hubs;
+ * INTR_RD wakes us when there's work
+ * NOTE: if we can do this, we don't need a root hub timer!
+ */
+ if (can_suspend
+ && !changed
+ && !ohci->ed_rm_list
+ && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
+ & ohci->hc_control)
+ == OHCI_USB_OPER
+ && time_after (jiffies, ohci->next_statechange)
+ && usb_trylock_device (hcd->self.root_hub)
+ ) {
+ ohci_vdbg (ohci, "autosuspend\n");
+ (void) ohci_hub_suspend (hcd);
+ hcd->state = HC_STATE_RUNNING;
+ usb_unlock_device (hcd->self.root_hub);
+ }
+#endif
+
+ return changed ? length : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_hub_descriptor (
+ struct ohci_hcd *ohci,
+ struct usb_hub_descriptor *desc
+) {
+ u32 rh = roothub_a (ohci);
+ int ports = rh & RH_A_NDP;
+ u16 temp;
+
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ temp = 0;
+ if (rh & RH_A_NPS) /* no power switching? */
+ temp |= 0x0002;
+ if (rh & RH_A_PSM) /* per-port power switching? */
+ temp |= 0x0001;
+ if (rh & RH_A_NOCP) /* no overcurrent reporting? */
+ temp |= 0x0010;
+ else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
+ temp |= 0x0008;
+ desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ rh = roothub_b (ohci);
+ desc->bitmap [0] = rh & RH_B_DR;
+ if (ports > 7) {
+ desc->bitmap [1] = (rh & RH_B_DR) >> 8;
+ desc->bitmap [2] = desc->bitmap [3] = 0xff;
+ } else
+ desc->bitmap [1] = 0xff;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_OTG
+
+static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ u32 status;
+
+ if (!port)
+ return -EINVAL;
+ port--;
+
+ /* start port reset before HNP protocol times out */
+ status = ohci_readl(ohci, &ohci->regs->roothub.portstatus [port]);
+ if (!(status & RH_PS_CCS))
+ return -ENODEV;
+
+ /* khubd will finish the reset later */
+ ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
+ return 0;
+}
+
+static void start_hnp(struct ohci_hcd *ohci);
+
+#else
+
+#define ohci_start_port_reset NULL
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+
+/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling,
+ * not necessarily continuous ... to guard against resume signaling.
+ * The short timeout is safe for non-root hubs, and is backward-compatible
+ * with earlier Linux hosts.
+ */
+#ifdef CONFIG_USB_SUSPEND
+#define PORT_RESET_MSEC 50
+#else
+#define PORT_RESET_MSEC 10
+#endif
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+
+/* called from some task, normally khubd */
+static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
+{
+ __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
+ u32 temp;
+ u16 now = ohci_readl(ohci, &ohci->regs->fmnumber);
+ u16 reset_done = now + PORT_RESET_MSEC;
+
+ /* build a "continuous enough" reset signal, with up to
+ * 3msec gap between pulses. scheduler HZ==100 must work;
+ * this might need to be deadline-scheduled.
+ */
+ do {
+ /* spin until any current reset finishes */
+ for (;;) {
+ temp = ohci_readl (ohci, portstat);
+ if (!(temp & RH_PS_PRS))
+ break;
+ udelay (500);
+ }
+
+ if (!(temp & RH_PS_CCS))
+ break;
+ if (temp & RH_PS_PRSC)
+ ohci_writel (ohci, RH_PS_PRSC, portstat);
+
+ /* start the next reset, sleep till it's probably done */
+ ohci_writel (ohci, RH_PS_PRS, portstat);
+ msleep(PORT_RESET_HW_MSEC);
+ now = ohci_readl(ohci, &ohci->regs->fmnumber);
+ } while (tick_before(now, reset_done));
+ /* caller synchronizes using PRSC */
+}
+
+static int ohci_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports = hcd_to_bus (hcd)->root_hub->maxchild;
+ u32 temp;
+ int retval = 0;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ ohci_writel (ohci, RH_HS_OCIC,
+ &ohci->regs->roothub.status);
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ if ((ohci->hc_control & OHCI_CTRL_HCFS)
+ != OHCI_USB_OPER)
+ schedule_work (&ohci->rh_resume);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+ ohci_writel (ohci, temp,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ // ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case GetHubDescriptor:
+ ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
+ *(__le32 *) buf = cpu_to_le32 (temp);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = roothub_portstatus (ohci, wIndex);
+ *(__le32 *) buf = cpu_to_le32 (temp);
+
+#ifndef OHCI_VERBOSE_DEBUG
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ dbg_port (ohci, "GetStatus", wIndex, temp);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ // FIXME: this can be cleared, yes?
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+#ifdef CONFIG_USB_OTG
+ if (hcd->self.otg_port == (wIndex + 1)
+ && hcd->self.b_hnp_enable)
+ start_hnp(ohci);
+ else
+#endif
+ ohci_writel (ohci, RH_PS_PSS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ ohci_writel (ohci, RH_PS_PPS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ root_port_reset (ohci, wIndex);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+ return retval;
+}
+
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
new file mode 100644
index 0000000..817620d
--- /dev/null
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -0,0 +1,266 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for Sharp LH7A404
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Rusell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void lh7a404_start_hc(struct platform_device *dev)
+{
+ printk(KERN_DEBUG __FILE__
+ ": starting LH7A404 OHCI USB Controller\n");
+
+ /*
+ * Now, carefully enable the USB clock, and take
+ * the USB host controller out of reset.
+ */
+ CSC_PWRCNT |= CSC_PWRCNT_USBH_EN; /* Enable clock */
+ udelay(1000);
+ USBH_CMDSTATUS = OHCI_HCR;
+
+ printk(KERN_DEBUG __FILE__
+ ": Clock to USB host has been enabled \n");
+}
+
+static void lh7a404_stop_hc(struct platform_device *dev)
+{
+ printk(KERN_DEBUG __FILE__
+ ": stopping LH7A404 OHCI USB Controller\n");
+
+ CSC_PWRCNT &= ~CSC_PWRCNT_USBH_EN; /* Disable clock */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_lh7a404_probe - initialize LH7A404-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_lh7a404_probe (const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+
+ if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug("resource[1] is not IORESOURCE_IRQ");
+ return -ENOMEM;
+ }
+
+ hcd = usb_create_hcd(driver, &dev->dev, "lh7a404");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = dev->resource[0].start;
+ hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ lh7a404_start_hc(dev);
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ lh7a404_stop_hc(dev);
+ iounmap(hcd->regs);
+ err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_lh7a404_remove - shutdown processing for LH7A404-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_lh7a404_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_lh7a404_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ lh7a404_stop_hc(dev);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_lh7a404_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ err ("can't start %s", hcd->self.bus_name);
+ ohci_stop (hcd);
+ return ret;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_lh7a404_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "LH7A404 OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_lh7a404_start,
+#ifdef CONFIG_PM
+ /* suspend: ohci_lh7a404_suspend, -- tbd */
+ /* resume: ohci_lh7a404_resume, -- tbd */
+#endif /*CONFIG_PM*/
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_lh7a404_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int ret;
+
+ pr_debug ("In ohci_hcd_lh7a404_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_lh7a404_probe(&ohci_lh7a404_hc_driver, pdev);
+ return ret;
+}
+
+static int ohci_hcd_lh7a404_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_lh7a404_remove(hcd, pdev);
+ return 0;
+}
+ /*TBD*/
+/*static int ohci_hcd_lh7a404_drv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+static int ohci_hcd_lh7a404_drv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+
+ return 0;
+}
+*/
+
+static struct device_driver ohci_hcd_lh7a404_driver = {
+ .name = "lh7a404-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_lh7a404_drv_probe,
+ .remove = ohci_hcd_lh7a404_drv_remove,
+ /*.suspend = ohci_hcd_lh7a404_drv_suspend, */
+ /*.resume = ohci_hcd_lh7a404_drv_resume, */
+};
+
+static int __init ohci_hcd_lh7a404_init (void)
+{
+ pr_debug (DRIVER_INFO " (LH7A404)");
+ pr_debug ("block sizes: ed %d td %d\n",
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_lh7a404_driver);
+}
+
+static void __exit ohci_hcd_lh7a404_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_lh7a404_driver);
+}
+
+module_init (ohci_hcd_lh7a404_init);
+module_exit (ohci_hcd_lh7a404_cleanup);
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
new file mode 100644
index 0000000..e55682b
--- /dev/null
+++ b/drivers/usb/host/ohci-mem.c
@@ -0,0 +1,139 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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 dma_pool or dma_alloc_coherent
+ * - driver buffers, read/written by HC ... the hcd glue or the
+ * device driver provides us with dma addresses
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_hcd_init (struct ohci_hcd *ohci)
+{
+ ohci->next_statechange = jiffies;
+ spin_lock_init (&ohci->lock);
+ INIT_LIST_HEAD (&ohci->pending);
+ INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_mem_init (struct ohci_hcd *ohci)
+{
+ ohci->td_cache = dma_pool_create ("ohci_td",
+ ohci_to_hcd(ohci)->self.controller,
+ sizeof (struct td),
+ 32 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+ if (!ohci->td_cache)
+ return -ENOMEM;
+ ohci->ed_cache = dma_pool_create ("ohci_ed",
+ ohci_to_hcd(ohci)->self.controller,
+ sizeof (struct ed),
+ 16 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+ if (!ohci->ed_cache) {
+ dma_pool_destroy (ohci->td_cache);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void ohci_mem_cleanup (struct ohci_hcd *ohci)
+{
+ if (ohci->td_cache) {
+ dma_pool_destroy (ohci->td_cache);
+ ohci->td_cache = NULL;
+ }
+ if (ohci->ed_cache) {
+ dma_pool_destroy (ohci->ed_cache);
+ ohci->ed_cache = NULL;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ohci "done list" processing needs this mapping */
+static inline struct td *
+dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
+{
+ struct td *td;
+
+ td_dma &= TD_MASK;
+ td = hc->td_hash [TD_HASH_FUNC(td_dma)];
+ while (td && td->td_dma != td_dma)
+ td = td->td_hash;
+ return td;
+}
+
+/* TDs ... */
+static struct td *
+td_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+ dma_addr_t dma;
+ struct td *td;
+
+ td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
+ if (td) {
+ /* in case hc fetches it, make it look dead */
+ memset (td, 0, sizeof *td);
+ td->hwNextTD = cpu_to_hc32 (hc, dma);
+ td->td_dma = dma;
+ /* hashed in td_fill */
+ }
+ return td;
+}
+
+static void
+td_free (struct ohci_hcd *hc, struct td *td)
+{
+ struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
+
+ while (*prev && *prev != td)
+ prev = &(*prev)->td_hash;
+ if (*prev)
+ *prev = td->td_hash;
+ else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
+ ohci_dbg (hc, "no hash for td %p\n", td);
+ dma_pool_free (hc->td_cache, td, td->td_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EDs ... */
+static struct ed *
+ed_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+ dma_addr_t dma;
+ struct ed *ed;
+
+ ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ if (ed) {
+ memset (ed, 0, sizeof (*ed));
+ INIT_LIST_HEAD (&ed->td_list);
+ ed->dma = dma;
+ }
+ return ed;
+}
+
+static void
+ed_free (struct ohci_hcd *hc, struct ed *ed)
+{
+ dma_pool_free (hc->ed_cache, ed, ed->dma);
+}
+
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
new file mode 100644
index 0000000..90285f1
--- /dev/null
+++ b/drivers/usb/host/ohci-omap.c
@@ -0,0 +1,560 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2005 David Brownell
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * OMAP Bus Glue
+ *
+ * Modified for OMAP by Tony Lindgren <tony@atomide.com>
+ * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
+ * and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/fpga.h>
+#include <asm/arch/usb.h>
+#include <asm/hardware/clock.h>
+
+
+/* OMAP-1510 OHCI has its own MMU for DMA */
+#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */
+#define OMAP1510_LB_CLOCK_DIV 0xfffec10c
+#define OMAP1510_LB_MMU_CTL 0xfffec208
+#define OMAP1510_LB_MMU_LCK 0xfffec224
+#define OMAP1510_LB_MMU_LD_TLB 0xfffec228
+#define OMAP1510_LB_MMU_CAM_H 0xfffec22c
+#define OMAP1510_LB_MMU_CAM_L 0xfffec230
+#define OMAP1510_LB_MMU_RAM_H 0xfffec234
+#define OMAP1510_LB_MMU_RAM_L 0xfffec238
+
+
+#ifndef CONFIG_ARCH_OMAP
+#error "This file is OMAP bus glue. CONFIG_OMAP must be defined."
+#endif
+
+#ifdef CONFIG_TPS65010
+#include <asm/arch/tps65010.h>
+#else
+
+#define LOW 0
+#define HIGH 1
+
+#define GPIO1 1
+
+static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
+{
+ return 0;
+}
+
+#endif
+
+extern int usb_disabled(void);
+extern int ocpi_enable(void);
+
+static struct clk *usb_host_ck;
+
+static void omap_ohci_clock_power(int on)
+{
+ if (on) {
+ clk_enable(usb_host_ck);
+ /* guesstimate for T5 == 1x 32K clock + APLL lock time */
+ udelay(100);
+ } else {
+ clk_disable(usb_host_ck);
+ }
+}
+
+/*
+ * Board specific gang-switched transceiver power on/off.
+ * NOTE: OSK supplies power from DC, not battery.
+ */
+static int omap_ohci_transceiver_power(int on)
+{
+ if (on) {
+ if (machine_is_omap_innovator() && cpu_is_omap1510())
+ fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
+ | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ INNOVATOR_FPGA_CAM_USB_CONTROL);
+ else if (machine_is_omap_osk())
+ tps65010_set_gpio_out_value(GPIO1, LOW);
+ } else {
+ if (machine_is_omap_innovator() && cpu_is_omap1510())
+ fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
+ & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ INNOVATOR_FPGA_CAM_USB_CONTROL);
+ else if (machine_is_omap_osk())
+ tps65010_set_gpio_out_value(GPIO1, HIGH);
+ }
+
+ return 0;
+}
+
+/*
+ * OMAP-1510 specific Local Bus clock on/off
+ */
+static int omap_1510_local_bus_power(int on)
+{
+ if (on) {
+ omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL);
+ udelay(200);
+ } else {
+ omap_writel(0, OMAP1510_LB_MMU_CTL);
+ }
+
+ return 0;
+}
+
+/*
+ * OMAP-1510 specific Local Bus initialization
+ * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
+ * See also arch/mach-omap/memory.h for __virt_to_dma() and
+ * __dma_to_virt() which need to match with the physical
+ * Local Bus address below.
+ */
+static int omap_1510_local_bus_init(void)
+{
+ unsigned int tlb;
+ unsigned long lbaddr, physaddr;
+
+ omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
+ OMAP1510_LB_CLOCK_DIV);
+
+ /* Configure the Local Bus MMU table */
+ for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) {
+ lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
+ physaddr = tlb * 0x00100000 + PHYS_OFFSET;
+ omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
+ omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
+ OMAP1510_LB_MMU_CAM_L);
+ omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
+ omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
+ omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK);
+ omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB);
+ }
+
+ /* Enable the walking table */
+ omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL);
+ udelay(200);
+
+ return 0;
+}
+
+#ifdef CONFIG_USB_OTG
+
+static void start_hnp(struct ohci_hcd *ohci)
+{
+ const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1;
+ unsigned long flags;
+
+ otg_start_hnp(ohci->transceiver);
+
+ local_irq_save(flags);
+ ohci->transceiver->state = OTG_STATE_A_SUSPEND;
+ writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
+ OTG_CTRL_REG &= ~OTG_A_BUSREQ;
+ local_irq_restore(flags);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
+{
+ struct omap_usb_config *config = pdev->dev.platform_data;
+ int need_transceiver = (config->otg != 0);
+ int ret;
+
+ dev_dbg(&pdev->dev, "starting USB Controller\n");
+
+ if (config->otg) {
+ ohci_to_hcd(ohci)->self.otg_port = config->otg;
+ /* default/minimum OTG power budget: 8 mA */
+ ohci->power_budget = 8;
+ }
+
+ /* boards can use OTG transceivers in non-OTG modes */
+ need_transceiver = need_transceiver
+ || machine_is_omap_h2() || machine_is_omap_h3();
+
+ if (cpu_is_omap16xx())
+ ocpi_enable();
+
+#ifdef CONFIG_ARCH_OMAP_OTG
+ if (need_transceiver) {
+ ohci->transceiver = otg_get_transceiver();
+ if (ohci->transceiver) {
+ int status = otg_set_host(ohci->transceiver,
+ &ohci_to_hcd(ohci)->self);
+ dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
+ ohci->transceiver->label, status);
+ if (status) {
+ if (ohci->transceiver)
+ put_device(ohci->transceiver->dev);
+ return status;
+ }
+ } else {
+ dev_err(&pdev->dev, "can't find transceiver\n");
+ return -ENODEV;
+ }
+ }
+#endif
+
+ omap_ohci_clock_power(1);
+
+ if (cpu_is_omap1510()) {
+ omap_1510_local_bus_power(1);
+ omap_1510_local_bus_init();
+ }
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ /* board-specific power switching and overcurrent support */
+ if (machine_is_omap_osk() || machine_is_omap_innovator()) {
+ u32 rh = roothub_a (ohci);
+
+ /* power switching (ganged by default) */
+ rh &= ~RH_A_NPS;
+
+ /* TPS2045 switch for internal transceiver (port 1) */
+ if (machine_is_omap_osk()) {
+ ohci->power_budget = 250;
+
+ rh &= ~RH_A_NOCP;
+
+ /* gpio9 for overcurrent detction */
+ omap_cfg_reg(W8_1610_GPIO9);
+ omap_request_gpio(9);
+ omap_set_gpio_direction(9, 1 /* IN */);
+
+ /* for paranoia's sake: disable USB.PUEN */
+ omap_cfg_reg(W4_USB_HIGHZ);
+ }
+ ohci_writel(ohci, rh, &ohci->regs->roothub.a);
+ distrust_firmware = 0;
+ }
+
+ /* FIXME khubd hub requests should manage power switching */
+ omap_ohci_transceiver_power(1);
+
+ /* board init will have already handled HMC and mux setup.
+ * any external transceiver should already be initialized
+ * too, so all configured ports use the right signaling now.
+ */
+
+ return 0;
+}
+
+static void omap_stop_hc(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "stopping USB Controller\n");
+ omap_ohci_clock_power(0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_omap_probe - initialize OMAP-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+int usb_hcd_omap_probe (const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ int retval;
+ struct usb_hcd *hcd = 0;
+ struct ohci_hcd *ohci;
+
+ if (pdev->num_resources != 2) {
+ printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
+ pdev->num_resources);
+ return -ENODEV;
+ }
+
+ if (pdev->resource[0].flags != IORESOURCE_MEM
+ || pdev->resource[1].flags != IORESOURCE_IRQ) {
+ printk(KERN_ERR "hcd probe: invalid resource type\n");
+ return -ENODEV;
+ }
+
+ usb_host_ck = clk_get(0, "usb_hhc_ck");
+ if (IS_ERR(usb_host_ck))
+ return PTR_ERR(usb_host_ck);
+
+ hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err0;
+ }
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start);
+
+ ohci = hcd_to_ohci(hcd);
+ ohci_hcd_init(ohci);
+
+ retval = omap_start_hc(ohci, pdev);
+ if (retval < 0)
+ goto err2;
+
+ retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ omap_stop_hc(pdev);
+err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+ usb_put_hcd(hcd);
+err0:
+ clk_put(usb_host_ck);
+ return retval;
+}
+
+
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_omap_remove - shutdown processing for OMAP-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_omap_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ if (machine_is_omap_osk())
+ omap_free_gpio(9);
+ omap_stop_hc(pdev);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ clk_put(usb_host_ck);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_omap_start (struct usb_hcd *hcd)
+{
+ struct omap_usb_config *config;
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ config = hcd->self.controller->platform_data;
+ if (config->otg || config->rwc)
+ writel(OHCI_CTRL_RWC, &ohci->regs->control);
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ dev_err(hcd->self.controller, "can't start\n");
+ ohci_stop (hcd);
+ return ret;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_omap_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OMAP OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_omap_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_omap_drv_probe(struct device *dev)
+{
+ return usb_hcd_omap_probe(&ohci_omap_hc_driver,
+ to_platform_device(dev));
+}
+
+static int ohci_hcd_omap_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ usb_hcd_omap_remove(hcd, pdev);
+ if (ohci->transceiver) {
+ (void) otg_set_host(ohci->transceiver, 0);
+ put_device(ohci->transceiver->dev);
+ }
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* states match PCI usage, always suspending the root hub except that
+ * 4 ~= D3cold (ACPI D3) with clock off (resume sees reset).
+ */
+
+static int ohci_omap_suspend(struct device *dev, u32 state, u32 level)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
+ int status = -EINVAL;
+
+ if (level != SUSPEND_POWER_DOWN)
+ return 0;
+ if (state <= dev->power.power_state)
+ return 0;
+
+ dev_dbg(dev, "suspend to %d\n", state);
+ down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+ status = ohci_hub_suspend(ohci_to_hcd(ohci));
+ if (status == 0) {
+ if (state >= 4) {
+ omap_ohci_clock_power(0);
+ ohci_to_hcd(ohci)->self.root_hub->state =
+ USB_STATE_SUSPENDED;
+ state = 4;
+ }
+ ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
+ dev->power.power_state = state;
+ }
+ up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+ return status;
+}
+
+static int ohci_omap_resume(struct device *dev, u32 level)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
+ int status = 0;
+
+ if (level != RESUME_POWER_ON)
+ return 0;
+
+ switch (dev->power.power_state) {
+ case 0:
+ break;
+ case 4:
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+ omap_ohci_clock_power(1);
+ /* FALLTHROUGH */
+ default:
+ dev_dbg(dev, "resume from %d\n", dev->power.power_state);
+#ifdef CONFIG_USB_SUSPEND
+ /* get extra cleanup even if remote wakeup isn't in use */
+ status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
+#else
+ down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+ status = ohci_hub_resume(ohci_to_hcd(ohci));
+ up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+#endif
+ if (status == 0)
+ dev->power.power_state = 0;
+ break;
+ }
+ return status;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Driver definition to register with the OMAP bus
+ */
+static struct device_driver ohci_hcd_omap_driver = {
+ .name = "ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_omap_drv_probe,
+ .remove = ohci_hcd_omap_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = ohci_omap_suspend,
+ .resume = ohci_omap_resume,
+#endif
+};
+
+static int __init ohci_hcd_omap_init (void)
+{
+ printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name);
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_omap_driver);
+}
+
+static void __exit ohci_hcd_omap_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_omap_driver);
+}
+
+module_init (ohci_hcd_omap_init);
+module_exit (ohci_hcd_omap_cleanup);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
new file mode 100644
index 0000000..b611582
--- /dev/null
+++ b/drivers/usb/host/ohci-pci.c
@@ -0,0 +1,264 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ * PCI Bus Glue
+ *
+ * This file is licenced under the GPL.
+ */
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#include <asm/prom.h>
+#ifndef CONFIG_PM
+# define CONFIG_PM
+#endif
+#endif
+
+#ifndef CONFIG_PCI
+#error "This file is PCI bus glue. CONFIG_PCI must be defined."
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static int
+ohci_pci_reset (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_hcd_init (ohci);
+ return ohci_init (ohci);
+}
+
+static int __devinit
+ohci_pci_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) {
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ /* AMD 756, for most chips (early revs), corrupts register
+ * values on read ... so enable the vendor workaround.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_AMD
+ && pdev->device == 0x740c) {
+ ohci->flags = OHCI_QUIRK_AMD756;
+ ohci_info (ohci, "AMD756 erratum 4 workaround\n");
+ // also somewhat erratum 10 (suspend/resume issues)
+ }
+
+ /* FIXME for some of the early AMD 760 southbridges, OHCI
+ * won't work at all. blacklist them.
+ */
+
+ /* Apple's OHCI driver has a lot of bizarre workarounds
+ * for this chip. Evidently control and bulk lists
+ * can get confused. (B&W G3 models, and ...)
+ */
+ else if (pdev->vendor == PCI_VENDOR_ID_OPTI
+ && pdev->device == 0xc861) {
+ ohci_info (ohci,
+ "WARNING: OPTi workarounds unavailable\n");
+ }
+
+ /* Check for NSC87560. We have to look at the bridge (fn1) to
+ * identify the USB (fn2). This quirk might apply to more or
+ * even all NSC stuff.
+ */
+ else if (pdev->vendor == PCI_VENDOR_ID_NS) {
+ struct pci_dev *b;
+
+ b = pci_find_slot (pdev->bus->number,
+ PCI_DEVFN (PCI_SLOT (pdev->devfn), 1));
+ if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
+ && b->vendor == PCI_VENDOR_ID_NS) {
+ ohci->flags |= OHCI_QUIRK_SUPERIO;
+ ohci_info (ohci, "Using NSC SuperIO setup\n");
+ }
+ }
+ }
+
+ /* NOTE: there may have already been a first reset, to
+ * keep bios/smm irqs from making trouble
+ */
+ if ((ret = ohci_run (ohci)) < 0) {
+ ohci_err (ohci, "can't start\n");
+ ohci_stop (hcd);
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ /* suspend root hub, hoping it keeps power during suspend */
+ if (time_before (jiffies, ohci->next_statechange))
+ msleep (100);
+
+#ifdef CONFIG_USB_SUSPEND
+ (void) usb_suspend_device (hcd->self.root_hub, state);
+#else
+ usb_lock_device (hcd->self.root_hub);
+ (void) ohci_hub_suspend (hcd);
+ usb_unlock_device (hcd->self.root_hub);
+#endif
+
+ /* let things settle down a bit */
+ msleep (100);
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac) {
+ struct device_node *of_node;
+
+ /* Disable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller));
+ if (of_node)
+ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
+ }
+#endif /* CONFIG_PMAC_PBOOK */
+ return 0;
+}
+
+
+static int ohci_pci_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int retval = 0;
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac) {
+ struct device_node *of_node;
+
+ /* Re-enable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller));
+ if (of_node)
+ pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+ }
+#endif /* CONFIG_PMAC_PBOOK */
+
+ /* resume root hub */
+ if (time_before (jiffies, ohci->next_statechange))
+ msleep (100);
+#ifdef CONFIG_USB_SUSPEND
+ /* get extra cleanup even if remote wakeup isn't in use */
+ retval = usb_resume_device (hcd->self.root_hub);
+#else
+ usb_lock_device (hcd->self.root_hub);
+ retval = ohci_hub_resume (hcd);
+ usb_unlock_device (hcd->self.root_hub);
+#endif
+
+ return retval;
+}
+
+#endif /* CONFIG_PM */
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ohci_pci_reset,
+ .start = ohci_pci_start,
+#ifdef CONFIG_PM
+ .suspend = ohci_pci_suspend,
+ .resume = ohci_pci_resume,
+#endif
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+/*-------------------------------------------------------------------------*/
+
+
+static const struct pci_device_id pci_ids [] = { {
+ /* handle any USB OHCI controller */
+ PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0),
+ .driver_data = (unsigned long) &ohci_pci_hc_driver,
+ }, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver ohci_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
+};
+
+
+static int __init ohci_hcd_pci_init (void)
+{
+ printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name);
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
+ sizeof (struct ed), sizeof (struct td));
+ return pci_register_driver (&ohci_pci_driver);
+}
+module_init (ohci_hcd_pci_init);
+
+/*-------------------------------------------------------------------------*/
+
+static void __exit ohci_hcd_pci_cleanup (void)
+{
+ pci_unregister_driver (&ohci_pci_driver);
+}
+module_exit (ohci_hcd_pci_cleanup);
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
new file mode 100644
index 0000000..17964c3
--- /dev/null
+++ b/drivers/usb/host/ohci-ppc-soc.c
@@ -0,0 +1,234 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ * (C) Copyright 2003-2005 MontaVista Software Inc.
+ *
+ * Bus Glue for PPC On-Chip OHCI driver
+ * Tested on Freescale MPC5200 and IBM STB04xxx
+ *
+ * Modified by Dale Farnsworth <dale@farnsworth.org> from ohci-sa1111.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/usb.h>
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_ppc_soc_probe - initialize On-Chip HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+ struct ohci_hcd *ohci;
+ struct resource *res;
+ int irq;
+ struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+ pr_debug("initializing PPC-SOC USB Controller\n");
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ pr_debug(__FILE__ ": no irq\n");
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_debug(__FILE__ ": no reg addr\n");
+ return -ENODEV;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, "PPC-SOC USB");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug(__FILE__ ": request_mem_region failed\n");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug(__FILE__ ": ioremap failed\n");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ if (pd->start && (retval = pd->start(pdev)))
+ goto err3;
+
+ ohci = hcd_to_ohci(hcd);
+ ohci->flags |= OHCI_BIG_ENDIAN;
+ ohci_hcd_init(ohci);
+
+ retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ pr_debug("Removing PPC-SOC USB Controller\n");
+ if (pd && pd->stop)
+ pd->stop(pdev);
+ err3:
+ iounmap(hcd->regs);
+ err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs
+ * @pdev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd,
+ struct platform_device *pdev)
+{
+ struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+ usb_remove_hcd(hcd);
+
+ pr_debug("stopping PPC-SOC USB Controller\n");
+ if (pd && pd->stop)
+ pd->stop(pdev);
+
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_hcd_put(hcd);
+}
+
+static int __devinit
+ohci_ppc_soc_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ err("can't start %s", ohci_to_hcd(ohci)->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct hc_driver ohci_ppc_soc_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_ppc_soc_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int ohci_hcd_ppc_soc_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, pdev);
+ return ret;
+}
+
+static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_ppc_soc_remove(hcd, pdev);
+ return 0;
+}
+
+static struct device_driver ohci_hcd_ppc_soc_driver = {
+ .name = "ppc-soc-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_ppc_soc_drv_probe,
+ .remove = ohci_hcd_ppc_soc_drv_remove,
+#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+ /*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
+ /*.resume = ohci_hcd_ppc_soc_drv_resume,*/
+#endif
+};
+
+static int __init ohci_hcd_ppc_soc_init(void)
+{
+ pr_debug(DRIVER_INFO " (PPC SOC)\n");
+ pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
+ sizeof(struct td));
+
+ return driver_register(&ohci_hcd_ppc_soc_driver);
+}
+
+static void __exit ohci_hcd_ppc_soc_cleanup(void)
+{
+ driver_unregister(&ohci_hcd_ppc_soc_driver);
+}
+
+module_init(ohci_hcd_ppc_soc_init);
+module_exit(ohci_hcd_ppc_soc_cleanup);
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
new file mode 100644
index 0000000..6f3464a
--- /dev/null
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -0,0 +1,383 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for pxa27x
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * Modified for pxa27x from ohci-lh7a404.c
+ * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/device.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+
+#define PMM_NPS_MODE 1
+#define PMM_GLOBAL_MODE 2
+#define PMM_PERPORT_MODE 3
+
+#define PXA_UHC_MAX_PORTNUM 3
+
+#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
+
+static int pxa27x_ohci_pmm_state;
+
+/*
+ PMM_NPS_MODE -- PMM Non-power switching mode
+ Ports are powered continuously.
+
+ PMM_GLOBAL_MODE -- PMM global switching mode
+ All ports are powered at the same time.
+
+ PMM_PERPORT_MODE -- PMM per port switching mode
+ Ports are powered individually.
+ */
+static int pxa27x_ohci_select_pmm( int mode )
+{
+ pxa27x_ohci_pmm_state = mode;
+
+ switch ( mode ) {
+ case PMM_NPS_MODE:
+ UHCRHDA |= RH_A_NPS;
+ break;
+ case PMM_GLOBAL_MODE:
+ UHCRHDA &= ~(RH_A_NPS & RH_A_PSM);
+ break;
+ case PMM_PERPORT_MODE:
+ UHCRHDA &= ~(RH_A_NPS);
+ UHCRHDA |= RH_A_PSM;
+
+ /* Set port power control mask bits, only 3 ports. */
+ UHCRHDB |= (0x7<<17);
+ break;
+ default:
+ printk( KERN_ERR
+ "Invalid mode %d, set to non-power switch mode.\n",
+ mode );
+
+ pxa27x_ohci_pmm_state = PMM_NPS_MODE;
+ UHCRHDA |= RH_A_NPS;
+ }
+
+ return 0;
+}
+
+/*
+ If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_set_port_power( int port )
+{
+ if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+ && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+ UHCRHPS(port) |= 0x100;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_clear_port_power( int port )
+{
+ if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+ && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+ UHCRHPS(port) |= 0x200;
+ return 0;
+ }
+
+ return -1;
+}
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void pxa27x_start_hc(struct platform_device *dev)
+{
+ pxa_set_cken(CKEN10_USBHOST, 1);
+
+ UHCHR |= UHCHR_FHR;
+ udelay(11);
+ UHCHR &= ~UHCHR_FHR;
+
+ UHCHR |= UHCHR_FSBIR;
+ while (UHCHR & UHCHR_FSBIR)
+ cpu_relax();
+
+ /* This could be properly abstracted away through the
+ device data the day more machines are supported and
+ their differences can be figured out correctly. */
+ if (machine_is_mainstone()) {
+ /* setup Port1 GPIO pin. */
+ pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
+ pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
+
+ /* Set the Power Control Polarity Low and Power Sense
+ Polarity Low to active low. Supply power to USB ports. */
+ UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
+ ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+ }
+
+ UHCHR &= ~UHCHR_SSE;
+
+ UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
+}
+
+static void pxa27x_stop_hc(struct platform_device *dev)
+{
+ UHCHR |= UHCHR_FHR;
+ udelay(11);
+ UHCHR &= ~UHCHR_FHR;
+
+ UHCCOMS |= 1;
+ udelay(10);
+
+ pxa_set_cken(CKEN10_USBHOST, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+
+ if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug ("resource[1] is not IORESOURCE_IRQ");
+ return -ENOMEM;
+ }
+
+ hcd = usb_create_hcd (driver, &dev->dev, "pxa27x");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = dev->resource[0].start;
+ hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ pxa27x_start_hc(dev);
+
+ /* Select Power Management Mode */
+ pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
+
+ /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
+ if (pxa27x_ohci_set_port_power(1) < 0)
+ printk(KERN_ERR "Setting port 1 power failed.\n");
+
+ if (pxa27x_ohci_clear_port_power(2) < 0)
+ printk(KERN_ERR "Setting port 2 power failed.\n");
+
+ if (pxa27x_ohci_clear_port_power(3) < 0)
+ printk(KERN_ERR "Setting port 3 power failed.\n");
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ pxa27x_stop_hc(dev);
+ iounmap(hcd->regs);
+ err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ pxa27x_stop_hc(dev);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_pxa27x_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ err ("can't start %s", hcd->self.bus_name);
+ ohci_stop (hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pxa27x_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "PXA27x OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_pxa27x_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_pxa27x_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int ret;
+
+ pr_debug ("In ohci_hcd_pxa27x_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
+ return ret;
+}
+
+static int ohci_hcd_pxa27x_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_pxa27x_remove(hcd, pdev);
+ return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+// struct usb_hcd *hcd = dev_get_drvdata(dev);
+ printk("%s: not implemented yet\n", __FUNCTION__);
+
+ return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+// struct usb_hcd *hcd = dev_get_drvdata(dev);
+ printk("%s: not implemented yet\n", __FUNCTION__);
+
+ return 0;
+}
+
+
+static struct device_driver ohci_hcd_pxa27x_driver = {
+ .name = "pxa27x-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_pxa27x_drv_probe,
+ .remove = ohci_hcd_pxa27x_drv_remove,
+ .suspend = ohci_hcd_pxa27x_drv_suspend,
+ .resume = ohci_hcd_pxa27x_drv_resume,
+};
+
+static int __init ohci_hcd_pxa27x_init (void)
+{
+ pr_debug (DRIVER_INFO " (pxa27x)");
+ pr_debug ("block sizes: ed %d td %d\n",
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_pxa27x_driver);
+}
+
+static void __exit ohci_hcd_pxa27x_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+
+module_init (ohci_hcd_pxa27x_init);
+module_exit (ohci_hcd_pxa27x_cleanup);
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
new file mode 100644
index 0000000..c90114a
--- /dev/null
+++ b/drivers/usb/host/ohci-q.c
@@ -0,0 +1,1107 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
+{
+ int last = urb_priv->length - 1;
+
+ if (last >= 0) {
+ int i;
+ struct td *td;
+
+ for (i = 0; i <= last; i++) {
+ td = urb_priv->td [i];
+ if (td)
+ td_free (hc, td);
+ }
+ }
+
+ list_del (&urb_priv->pending);
+ kfree (urb_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * URB goes back to driver, and isn't reissued.
+ * It's completely gone from HC data structures.
+ * PRECONDITION: ohci lock held, irqs blocked.
+ */
+static void
+finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs)
+__releases(ohci->lock)
+__acquires(ohci->lock)
+{
+ // ASSERT (urb->hcpriv != 0);
+
+ urb_free_priv (ohci, urb->hcpriv);
+ urb->hcpriv = NULL;
+
+ spin_lock (&urb->lock);
+ if (likely (urb->status == -EINPROGRESS))
+ urb->status = 0;
+ /* report short control reads right even though the data TD always
+ * has TD_R set. (much simpler, but creates the 1-td limit.)
+ */
+ if (unlikely (urb->transfer_flags & URB_SHORT_NOT_OK)
+ && unlikely (usb_pipecontrol (urb->pipe))
+ && urb->actual_length < urb->transfer_buffer_length
+ && usb_pipein (urb->pipe)
+ && urb->status == 0) {
+ urb->status = -EREMOTEIO;
+ }
+ spin_unlock (&urb->lock);
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
+ break;
+ case PIPE_INTERRUPT:
+ ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;
+ break;
+ }
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+
+ /* urb->complete() can reenter this HCD */
+ spin_unlock (&ohci->lock);
+ usb_hcd_giveback_urb (ohci_to_hcd(ohci), urb, regs);
+ spin_lock (&ohci->lock);
+
+ /* stop periodic dma if it's not needed */
+ if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
+ && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0) {
+ ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ }
+}
+
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right schedule branch to use for a periodic ed.
+ * does some load balancing; returns the branch, or negative errno.
+ */
+static int balance (struct ohci_hcd *ohci, int interval, int load)
+{
+ int i, branch = -ENOSPC;
+
+ /* iso periods can be huge; iso tds specify frame numbers */
+ if (interval > NUM_INTS)
+ interval = NUM_INTS;
+
+ /* search for the least loaded schedule branch of that period
+ * that has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < interval ; i++) {
+ if (branch < 0 || ohci->load [branch] > ohci->load [i]) {
+#if 1 /* CONFIG_USB_BANDWIDTH */
+ int j;
+
+ /* usb 1.1 says 90% of one frame */
+ for (j = i; j < NUM_INTS; j += interval) {
+ if ((ohci->load [j] + load) > 900)
+ break;
+ }
+ if (j < NUM_INTS)
+ continue;
+#endif
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* both iso and interrupt requests have periods; this routine puts them
+ * into the schedule tree in the apppropriate place. most iso devices use
+ * 1msec periods, but that's not required.
+ */
+static void periodic_link (struct ohci_hcd *ohci, struct ed *ed)
+{
+ unsigned i;
+
+ ohci_vdbg (ohci, "link %sed %p branch %d [%dus.], interval %d\n",
+ (ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "",
+ ed, ed->branch, ed->load, ed->interval);
+
+ for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+ struct ed **prev = &ohci->periodic [i];
+ __hc32 *prev_p = &ohci->hcca->int_table [i];
+ struct ed *here = *prev;
+
+ /* sorting each branch by period (slow before fast)
+ * lets us share the faster parts of the tree.
+ * (plus maybe: put interrupt eds before iso)
+ */
+ while (here && ed != here) {
+ if (ed->interval > here->interval)
+ break;
+ prev = &here->ed_next;
+ prev_p = &here->hwNextED;
+ here = *prev;
+ }
+ if (ed != here) {
+ ed->ed_next = here;
+ if (here)
+ ed->hwNextED = *prev_p;
+ wmb ();
+ *prev = ed;
+ *prev_p = cpu_to_hc32(ohci, ed->dma);
+ wmb();
+ }
+ ohci->load [i] += ed->load;
+ }
+ ohci_to_hcd(ohci)->self.bandwidth_allocated += ed->load / ed->interval;
+}
+
+/* link an ed into one of the HC chains */
+
+static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int branch;
+
+ if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING)
+ return -EAGAIN;
+
+ ed->state = ED_OPER;
+ ed->ed_prev = NULL;
+ ed->ed_next = NULL;
+ ed->hwNextED = 0;
+ wmb ();
+
+ /* we care about rm_list when setting CLE/BLE in case the HC was at
+ * work on some TD when CLE/BLE was turned off, and isn't quiesced
+ * yet. finish_unlinks() restarts as needed, some upcoming INTR_SF.
+ *
+ * control and bulk EDs are doubly linked (ed_next, ed_prev), but
+ * periodic ones are singly linked (ed_next). that's because the
+ * periodic schedule encodes a tree like figure 3-5 in the ohci
+ * spec: each qh can have several "previous" nodes, and the tree
+ * doesn't have unused/idle descriptors.
+ */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ohci->ed_controltail == NULL) {
+ WARN_ON (ohci->hc_control & OHCI_CTRL_CLE);
+ ohci_writel (ohci, ed->dma,
+ &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->ed_next = ed;
+ ohci->ed_controltail->hwNextED = cpu_to_hc32 (ohci,
+ ed->dma);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail && !ohci->ed_rm_list) {
+ wmb();
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ }
+ ohci->ed_controltail = ed;
+ break;
+
+ case PIPE_BULK:
+ if (ohci->ed_bulktail == NULL) {
+ WARN_ON (ohci->hc_control & OHCI_CTRL_BLE);
+ ohci_writel (ohci, ed->dma, &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->ed_next = ed;
+ ohci->ed_bulktail->hwNextED = cpu_to_hc32 (ohci,
+ ed->dma);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list) {
+ wmb();
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ }
+ ohci->ed_bulktail = ed;
+ break;
+
+ // case PIPE_INTERRUPT:
+ // case PIPE_ISOCHRONOUS:
+ default:
+ branch = balance (ohci, ed->interval, ed->load);
+ if (branch < 0) {
+ ohci_dbg (ohci,
+ "ERR %d, interval %d msecs, load %d\n",
+ branch, ed->interval, ed->load);
+ // FIXME if there are TDs queued, fail them!
+ return branch;
+ }
+ ed->branch = branch;
+ periodic_link (ohci, ed);
+ }
+
+ /* the HC may not see the schedule updates yet, but if it does
+ * then they'll be properly ordered.
+ */
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int i;
+
+ for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+ struct ed *temp;
+ struct ed **prev = &ohci->periodic [i];
+ __hc32 *prev_p = &ohci->hcca->int_table [i];
+
+ while (*prev && (temp = *prev) != ed) {
+ prev_p = &temp->hwNextED;
+ prev = &temp->ed_next;
+ }
+ if (*prev) {
+ *prev_p = ed->hwNextED;
+ *prev = ed->ed_next;
+ }
+ ohci->load [i] -= ed->load;
+ }
+ ohci_to_hcd(ohci)->self.bandwidth_allocated -= ed->load / ed->interval;
+
+ ohci_vdbg (ohci, "unlink %sed %p branch %d [%dus.], interval %d\n",
+ (ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "",
+ ed, ed->branch, ed->load, ed->interval);
+}
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed
+ * (assuming it already started that, which needn't be true).
+ *
+ * ED_UNLINK is a transient state: the HC may still see this ED, but soon
+ * it won't. ED_SKIP means the HC will finish its current transaction,
+ * but won't start anything new. The TD queue may still grow; device
+ * drivers don't know about this HCD-internal state.
+ *
+ * When the HC can't see the ED, something changes ED_UNLINK to one of:
+ *
+ * - ED_OPER: when there's any request queued, the ED gets rescheduled
+ * immediately. HC should be working on them.
+ *
+ * - ED_IDLE: when there's no TD queue. there's no reason for the HC
+ * to care about this ED; safe to disable the endpoint.
+ *
+ * When finish_unlinks() runs later, after SOF interrupt, it will often
+ * complete one or more URB unlinks before making that state change.
+ */
+static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed)
+{
+ ed->hwINFO |= cpu_to_hc32 (ohci, ED_SKIP);
+ wmb ();
+ ed->state = ED_UNLINK;
+
+ /* To deschedule something from the control or bulk list, just
+ * clear CLE/BLE and wait. There's no safe way to scrub out list
+ * head/current registers until later, and "later" isn't very
+ * tightly specified. Figure 6-5 and Section 6.4.2.2 show how
+ * the HC is reading the ED queues (while we modify them).
+ *
+ * For now, ed_schedule() is "later". It might be good paranoia
+ * to scrub those registers in finish_unlinks(), in case of bugs
+ * that make the HC try to use them.
+ */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ /* remove ED from the HC's list: */
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ // a ohci_readl() later syncs CLE with the HC
+ } else
+ ohci_writel (ohci,
+ hc32_to_cpup (ohci, &ed->hwNextED),
+ &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->ed_next = ed->ed_next;
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ /* remove ED from the HCD's list: */
+ if (ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ if (ohci->ed_controltail)
+ ohci->ed_controltail->ed_next = NULL;
+ } else if (ed->ed_next) {
+ ed->ed_next->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_BULK:
+ /* remove ED from the HC's list: */
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ // a ohci_readl() later syncs BLE with the HC
+ } else
+ ohci_writel (ohci,
+ hc32_to_cpup (ohci, &ed->hwNextED),
+ &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->ed_next = ed->ed_next;
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ /* remove ED from the HCD's list: */
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ if (ohci->ed_bulktail)
+ ohci->ed_bulktail->ed_next = NULL;
+ } else if (ed->ed_next) {
+ ed->ed_next->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ // case PIPE_INTERRUPT:
+ // case PIPE_ISOCHRONOUS:
+ default:
+ periodic_unlink (ohci, ed);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* get and maybe (re)init an endpoint. init _should_ be done only as part
+ * of enumeration, usb_set_configuration() or usb_set_interface().
+ */
+static struct ed *ed_get (
+ struct ohci_hcd *ohci,
+ struct usb_host_endpoint *ep,
+ struct usb_device *udev,
+ unsigned int pipe,
+ int interval
+) {
+ struct ed *ed;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ if (!(ed = ep->hcpriv)) {
+ struct td *td;
+ int is_out;
+ u32 info;
+
+ ed = ed_alloc (ohci, GFP_ATOMIC);
+ if (!ed) {
+ /* out of memory */
+ goto done;
+ }
+
+ /* dummy td; end of td list for ed */
+ td = td_alloc (ohci, GFP_ATOMIC);
+ if (!td) {
+ /* out of memory */
+ ed_free (ohci, ed);
+ ed = NULL;
+ goto done;
+ }
+ ed->dummy = td;
+ ed->hwTailP = cpu_to_hc32 (ohci, td->td_dma);
+ ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
+ ed->state = ED_IDLE;
+
+ is_out = !(ep->desc.bEndpointAddress & USB_DIR_IN);
+
+ /* FIXME usbcore changes dev->devnum before SET_ADDRESS
+ * suceeds ... otherwise we wouldn't need "pipe".
+ */
+ info = usb_pipedevice (pipe);
+ ed->type = usb_pipetype(pipe);
+
+ info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7;
+ info |= le16_to_cpu(ep->desc.wMaxPacketSize) << 16;
+ if (udev->speed == USB_SPEED_LOW)
+ info |= ED_LOWSPEED;
+ /* only control transfers store pids in tds */
+ if (ed->type != PIPE_CONTROL) {
+ info |= is_out ? ED_OUT : ED_IN;
+ if (ed->type != PIPE_BULK) {
+ /* periodic transfers... */
+ if (ed->type == PIPE_ISOCHRONOUS)
+ info |= ED_ISO;
+ else if (interval > 32) /* iso can be bigger */
+ interval = 32;
+ ed->interval = interval;
+ ed->load = usb_calc_bus_time (
+ udev->speed, !is_out,
+ ed->type == PIPE_ISOCHRONOUS,
+ le16_to_cpu(ep->desc.wMaxPacketSize))
+ / 1000;
+ }
+ }
+ ed->hwINFO = cpu_to_hc32(ohci, info);
+
+ ep->hcpriv = ed;
+ }
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return ed;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* request unlinking of an endpoint from an operational HC.
+ * put the ep on the rm_list
+ * real work is done at the next start frame (SF) hardware interrupt
+ * caller guarantees HCD is running, so hardware access is safe,
+ * and that ed->state is ED_OPER
+ */
+static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ ed->hwINFO |= cpu_to_hc32 (ohci, ED_DEQUEUE);
+ ed_deschedule (ohci, ed);
+
+ /* rm_list is just singly linked, for simplicity */
+ ed->ed_next = ohci->ed_rm_list;
+ ed->ed_prev = NULL;
+ ohci->ed_rm_list = ed;
+
+ /* enable SOF interrupt */
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
+ ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
+ // flush those writes, and get latest HCCA contents
+ (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* SF interrupt might get delayed; record the frame counter value that
+ * indicates when the HC isn't looking at it, so concurrent unlinks
+ * behave. frame_no wraps every 2^16 msec, and changes right before
+ * SF is triggered.
+ */
+ ed->tick = ohci_frame_no(ohci) + 1;
+
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void
+td_fill (struct ohci_hcd *ohci, u32 info,
+ dma_addr_t data, int len,
+ struct urb *urb, int index)
+{
+ struct td *td, *td_pt;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ int is_iso = info & TD_ISO;
+ int hash;
+
+ // ASSERT (index < urb_priv->length);
+
+ /* aim for only one interrupt per urb. mostly applies to control
+ * and iso; other urbs rarely need more than one TD per urb.
+ * this way, only final tds (or ones with an error) cause IRQs.
+ * at least immediately; use DI=6 in case any control request is
+ * tempted to die part way through. (and to force the hc to flush
+ * its donelist soonish, even on unlink paths.)
+ *
+ * NOTE: could delay interrupts even for the last TD, and get fewer
+ * interrupts ... increasing per-urb latency by sharing interrupts.
+ * Drivers that queue bulk urbs may request that behavior.
+ */
+ if (index != (urb_priv->length - 1)
+ || (urb->transfer_flags & URB_NO_INTERRUPT))
+ info |= TD_DI_SET (6);
+
+ /* use this td as the next dummy */
+ td_pt = urb_priv->td [index];
+
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = urb_priv->ed->dummy;
+ urb_priv->ed->dummy = td_pt;
+
+ td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
+ td->index = index;
+ td->urb = urb;
+ td->data_dma = data;
+ if (!len)
+ data = 0;
+
+ td->hwINFO = cpu_to_hc32 (ohci, info);
+ if (is_iso) {
+ td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
+ *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
+ (data & 0x0FFF) | 0xE000);
+ td->ed->last_iso = info & 0xffff;
+ } else {
+ td->hwCBP = cpu_to_hc32 (ohci, data);
+ }
+ if (data)
+ td->hwBE = cpu_to_hc32 (ohci, data + len - 1);
+ else
+ td->hwBE = 0;
+ td->hwNextTD = cpu_to_hc32 (ohci, td_pt->td_dma);
+
+ /* append to queue */
+ list_add_tail (&td->td_list, &td->ed->td_list);
+
+ /* hash it for later reverse mapping */
+ hash = TD_HASH_FUNC (td->td_dma);
+ td->td_hash = ohci->td_hash [hash];
+ ohci->td_hash [hash] = td;
+
+ /* HC might read the TD (or cachelines) right away ... */
+ wmb ();
+ td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Prepare all TDs of a transfer, and queue them onto the ED.
+ * Caller guarantees HC is active.
+ * Usually the ED is already on the schedule, so TDs might be
+ * processed as soon as they're queued.
+ */
+static void td_submit_urb (
+ struct ohci_hcd *ohci,
+ struct urb *urb
+) {
+ struct urb_priv *urb_priv = urb->hcpriv;
+ dma_addr_t data;
+ int data_len = urb->transfer_buffer_length;
+ int cnt = 0;
+ u32 info = 0;
+ int is_out = usb_pipeout (urb->pipe);
+ int periodic = 0;
+
+ /* OHCI handles the bulk/interrupt data toggles itself. We just
+ * use the device toggle bits for resetting, and rely on the fact
+ * that resetting toggle is meaningless if the endpoint is active.
+ */
+ if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+ is_out, 1);
+ urb_priv->ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_C);
+ }
+
+ urb_priv->td_cnt = 0;
+ list_add (&urb_priv->pending, &ohci->pending);
+
+ if (data_len)
+ data = urb->transfer_dma;
+ else
+ data = 0;
+
+ /* NOTE: TD_CC is set so we can tell which TDs the HC processed by
+ * using TD_CC_GET, as well as by seeing them on the done list.
+ * (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)
+ */
+ switch (urb_priv->ed->type) {
+
+ /* Bulk and interrupt are identical except for where in the schedule
+ * their EDs live.
+ */
+ case PIPE_INTERRUPT:
+ /* ... and periodic urbs have extra accounting */
+ periodic = ohci_to_hcd(ohci)->self.bandwidth_int_reqs++ == 0
+ && ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0;
+ /* FALLTHROUGH */
+ case PIPE_BULK:
+ info = is_out
+ ? TD_T_TOGGLE | TD_CC | TD_DP_OUT
+ : TD_T_TOGGLE | TD_CC | TD_DP_IN;
+ /* TDs _could_ transfer up to 8K each */
+ while (data_len > 4096) {
+ td_fill (ohci, info, data, 4096, urb, cnt);
+ data += 4096;
+ data_len -= 4096;
+ cnt++;
+ }
+ /* maybe avoid ED halt on final TD short read */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ info |= TD_R;
+ td_fill (ohci, info, data, data_len, urb, cnt);
+ cnt++;
+ if ((urb->transfer_flags & URB_ZERO_PACKET)
+ && cnt < urb_priv->length) {
+ td_fill (ohci, info, 0, 0, urb, cnt);
+ cnt++;
+ }
+ /* maybe kickstart bulk list */
+ if (urb_priv->ed->type == PIPE_BULK) {
+ wmb ();
+ ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus);
+ }
+ break;
+
+ /* control manages DATA0/DATA1 toggle per-request; SETUP resets it,
+ * any DATA phase works normally, and the STATUS ack is special.
+ */
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
+ if (data_len > 0) {
+ info = TD_CC | TD_R | TD_T_DATA1;
+ info |= is_out ? TD_DP_OUT : TD_DP_IN;
+ /* NOTE: mishandles transfers >8K, some >4K */
+ td_fill (ohci, info, data, data_len, urb, cnt++);
+ }
+ info = (is_out || data_len == 0)
+ ? TD_CC | TD_DP_IN | TD_T_DATA1
+ : TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (ohci, info, data, 0, urb, cnt++);
+ /* maybe kickstart control list */
+ wmb ();
+ ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus);
+ break;
+
+ /* ISO has no retransmit, so no toggle; and it uses special TDs.
+ * Each TD could handle multiple consecutive frames (interval 1);
+ * we could often reduce the number of TDs here.
+ */
+ case PIPE_ISOCHRONOUS:
+ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+ int frame = urb->start_frame;
+
+ // FIXME scheduling should handle frame counter
+ // roll-around ... exotic case (and OHCI has
+ // a 2^16 iso range, vs other HCs max of 2^10)
+ frame += cnt * urb->interval;
+ frame &= 0xffff;
+ td_fill (ohci, TD_CC | TD_ISO | frame,
+ data + urb->iso_frame_desc [cnt].offset,
+ urb->iso_frame_desc [cnt].length, urb, cnt);
+ }
+ periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0
+ && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0;
+ break;
+ }
+
+ /* start periodic dma if needed */
+ if (periodic) {
+ wmb ();
+ ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ }
+
+ // ASSERT (urb_priv->length == cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* calculate transfer length/status and update the urb
+ * PRECONDITION: irqsafe (only for urb->status locking)
+ */
+static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
+{
+ u32 tdINFO = hc32_to_cpup (ohci, &td->hwINFO);
+ int cc = 0;
+
+ list_del (&td->td_list);
+
+ /* ISO ... drivers see per-TD length/status */
+ if (tdINFO & TD_ISO) {
+ u16 tdPSW = ohci_hwPSW (ohci, td, 0);
+ int dlen = 0;
+
+ /* NOTE: assumes FC in tdINFO == 0, and that
+ * only the first of 0..MAXPSW psws is used.
+ */
+
+ cc = (tdPSW >> 12) & 0xF;
+ if (tdINFO & TD_CC) /* hc didn't touch? */
+ return;
+
+ if (usb_pipeout (urb->pipe))
+ dlen = urb->iso_frame_desc [td->index].length;
+ else {
+ /* short reads are always OK for ISO */
+ if (cc == TD_DATAUNDERRUN)
+ cc = TD_CC_NOERROR;
+ dlen = tdPSW & 0x3ff;
+ }
+ urb->actual_length += dlen;
+ urb->iso_frame_desc [td->index].actual_length = dlen;
+ urb->iso_frame_desc [td->index].status = cc_to_error [cc];
+
+ if (cc != TD_CC_NOERROR)
+ ohci_vdbg (ohci,
+ "urb %p iso td %p (%d) len %d cc %d\n",
+ urb, td, 1 + td->index, dlen, cc);
+
+ /* BULK, INT, CONTROL ... drivers see aggregate length/status,
+ * except that "setup" bytes aren't counted and "short" transfers
+ * might not be reported as errors.
+ */
+ } else {
+ int type = usb_pipetype (urb->pipe);
+ u32 tdBE = hc32_to_cpup (ohci, &td->hwBE);
+
+ cc = TD_CC_GET (tdINFO);
+
+ /* update packet status if needed (short is normally ok) */
+ if (cc == TD_DATAUNDERRUN
+ && !(urb->transfer_flags & URB_SHORT_NOT_OK))
+ cc = TD_CC_NOERROR;
+ if (cc != TD_CC_NOERROR && cc < 0x0E) {
+ spin_lock (&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = cc_to_error [cc];
+ spin_unlock (&urb->lock);
+ }
+
+ /* count all non-empty packets except control SETUP packet */
+ if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
+ if (td->hwCBP == 0)
+ urb->actual_length += tdBE - td->data_dma + 1;
+ else
+ urb->actual_length +=
+ hc32_to_cpup (ohci, &td->hwCBP)
+ - td->data_dma;
+ }
+
+ if (cc != TD_CC_NOERROR && cc < 0x0E)
+ ohci_vdbg (ohci,
+ "urb %p td %p (%d) cc %d, len=%d/%d\n",
+ urb, td, 1 + td->index, cc,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct td *
+ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
+{
+ struct urb *urb = td->urb;
+ struct ed *ed = td->ed;
+ struct list_head *tmp = td->td_list.next;
+ __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ohci, ED_C);
+
+ /* clear ed halt; this is the td that caused it, but keep it inactive
+ * until its urb->complete() has a chance to clean up.
+ */
+ ed->hwINFO |= cpu_to_hc32 (ohci, ED_SKIP);
+ wmb ();
+ ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_H);
+
+ /* put any later tds from this urb onto the donelist, after 'td',
+ * order won't matter here: no errors, and nothing was transferred.
+ * also patch the ed so it looks as if those tds completed normally.
+ */
+ while (tmp != &ed->td_list) {
+ struct td *next;
+ __hc32 info;
+
+ next = list_entry (tmp, struct td, td_list);
+ tmp = next->td_list.next;
+
+ if (next->urb != urb)
+ break;
+
+ /* NOTE: if multi-td control DATA segments get supported,
+ * this urb had one of them, this td wasn't the last td
+ * in that segment (TD_R clear), this ed halted because
+ * of a short read, _and_ URB_SHORT_NOT_OK is clear ...
+ * then we need to leave the control STATUS packet queued
+ * and clear ED_SKIP.
+ */
+ info = next->hwINFO;
+ info |= cpu_to_hc32 (ohci, TD_DONE);
+ info &= ~cpu_to_hc32 (ohci, TD_CC);
+ next->hwINFO = info;
+
+ next->next_dl_td = rev;
+ rev = next;
+
+ ed->hwHeadP = next->hwNextTD | toggle;
+ }
+
+ /* help for troubleshooting: report anything that
+ * looks odd ... that doesn't include protocol stalls
+ * (or maybe some other things)
+ */
+ switch (cc) {
+ case TD_DATAUNDERRUN:
+ if ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)
+ break;
+ /* fallthrough */
+ case TD_CC_STALL:
+ if (usb_pipecontrol (urb->pipe))
+ break;
+ /* fallthrough */
+ default:
+ ohci_dbg (ohci,
+ "urb %p path %s ep%d%s %08x cc %d --> status %d\n",
+ urb, urb->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ hc32_to_cpu (ohci, td->hwINFO),
+ cc, cc_to_error [cc]);
+ }
+
+ return rev;
+}
+
+/* replies to the request have to be on a FIFO basis so
+ * we unreverse the hc-reversed done-list
+ */
+static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
+{
+ u32 td_dma;
+ struct td *td_rev = NULL;
+ struct td *td = NULL;
+
+ td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head);
+ ohci->hcca->done_head = 0;
+ wmb();
+
+ /* get TD from hc's singly linked list, and
+ * prepend to ours. ed->td_list changes later.
+ */
+ while (td_dma) {
+ int cc;
+
+ td = dma_to_td (ohci, td_dma);
+ if (!td) {
+ ohci_err (ohci, "bad entry %8x\n", td_dma);
+ break;
+ }
+
+ td->hwINFO |= cpu_to_hc32 (ohci, TD_DONE);
+ cc = TD_CC_GET (hc32_to_cpup (ohci, &td->hwINFO));
+
+ /* Non-iso endpoints can halt on error; un-halt,
+ * and dequeue any other TDs from this urb.
+ * No other TD could have caused the halt.
+ */
+ if (cc != TD_CC_NOERROR
+ && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H)))
+ td_rev = ed_halted (ohci, td, cc, td_rev);
+
+ td->next_dl_td = td_rev;
+ td_rev = td;
+ td_dma = hc32_to_cpup (ohci, &td->hwNextTD);
+ }
+ return td_rev;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
+static void
+finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs)
+{
+ struct ed *ed, **last;
+
+rescan_all:
+ for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
+ struct list_head *entry, *tmp;
+ int completed, modified;
+ __hc32 *prev;
+
+ /* only take off EDs that the HC isn't using, accounting for
+ * frame counter wraps and EDs with partially retired TDs
+ */
+ if (likely (regs && HC_IS_RUNNING(ohci_to_hcd(ohci)->state))) {
+ if (tick_before (tick, ed->tick)) {
+skip_ed:
+ last = &ed->ed_next;
+ continue;
+ }
+
+ if (!list_empty (&ed->td_list)) {
+ struct td *td;
+ u32 head;
+
+ td = list_entry (ed->td_list.next, struct td,
+ td_list);
+ head = hc32_to_cpu (ohci, ed->hwHeadP) &
+ TD_MASK;
+
+ /* INTR_WDH may need to clean up first */
+ if (td->td_dma != head)
+ goto skip_ed;
+ }
+ }
+
+ /* reentrancy: if we drop the schedule lock, someone might
+ * have modified this list. normally it's just prepending
+ * entries (which we'd ignore), but paranoia won't hurt.
+ */
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ modified = 0;
+
+ /* unlink urbs as requested, but rescan the list after
+ * we call a completion since it might have unlinked
+ * another (earlier) urb
+ *
+ * When we get here, the HC doesn't see this ed. But it
+ * must not be rescheduled until all completed URBs have
+ * been given back to the driver.
+ */
+rescan_this:
+ completed = 0;
+ prev = &ed->hwHeadP;
+ list_for_each_safe (entry, tmp, &ed->td_list) {
+ struct td *td;
+ struct urb *urb;
+ urb_priv_t *urb_priv;
+ __hc32 savebits;
+
+ td = list_entry (entry, struct td, td_list);
+ urb = td->urb;
+ urb_priv = td->urb->hcpriv;
+
+ if (urb->status == -EINPROGRESS) {
+ prev = &td->hwNextTD;
+ continue;
+ }
+
+ /* patch pointer hc uses */
+ savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK);
+ *prev = td->hwNextTD | savebits;
+
+ /* HC may have partly processed this TD */
+ td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* if URB is done, clean up */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ modified = completed = 1;
+ finish_urb (ohci, urb, regs);
+ }
+ }
+ if (completed && !list_empty (&ed->td_list))
+ goto rescan_this;
+
+ /* ED's now officially unlinked, hc doesn't see */
+ ed->state = ED_IDLE;
+ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
+ ed->hwNextED = 0;
+ wmb ();
+ ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE);
+
+ /* but if there's work queued, reschedule */
+ if (!list_empty (&ed->td_list)) {
+ if (HC_IS_RUNNING(ohci_to_hcd(ohci)->state))
+ ed_schedule (ohci, ed);
+ }
+
+ if (modified)
+ goto rescan_all;
+ }
+
+ /* maybe reenable control and bulk lists */
+ if (HC_IS_RUNNING(ohci_to_hcd(ohci)->state)
+ && ohci_to_hcd(ohci)->state != HC_STATE_QUIESCING
+ && !ohci->ed_rm_list) {
+ u32 command = 0, control = 0;
+
+ if (ohci->ed_controltail) {
+ command |= OHCI_CLF;
+ if (!(ohci->hc_control & OHCI_CTRL_CLE)) {
+ control |= OHCI_CTRL_CLE;
+ ohci_writel (ohci, 0,
+ &ohci->regs->ed_controlcurrent);
+ }
+ }
+ if (ohci->ed_bulktail) {
+ command |= OHCI_BLF;
+ if (!(ohci->hc_control & OHCI_CTRL_BLE)) {
+ control |= OHCI_CTRL_BLE;
+ ohci_writel (ohci, 0,
+ &ohci->regs->ed_bulkcurrent);
+ }
+ }
+
+ /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */
+ if (control) {
+ ohci->hc_control |= control;
+ ohci_writel (ohci, ohci->hc_control,
+ &ohci->regs->control);
+ }
+ if (command)
+ ohci_writel (ohci, command, &ohci->regs->cmdstatus);
+ }
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Process normal completions (error or success) and clean the schedules.
+ *
+ * This is the main path for handing urbs back to drivers. The only other
+ * path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of
+ * scanning the (re-reversed) donelist as this does.
+ */
+static void
+dl_done_list (struct ohci_hcd *ohci, struct pt_regs *regs)
+{
+ struct td *td = dl_reverse_done_list (ohci);
+
+ while (td) {
+ struct td *td_next = td->next_dl_td;
+ struct urb *urb = td->urb;
+ urb_priv_t *urb_priv = urb->hcpriv;
+ struct ed *ed = td->ed;
+
+ /* update URB's length and status from TD */
+ td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* If all this urb's TDs are done, call complete() */
+ if (urb_priv->td_cnt == urb_priv->length)
+ finish_urb (ohci, urb, regs);
+
+ /* clean schedule: unlink EDs that are no longer busy */
+ if (list_empty (&ed->td_list)) {
+ if (ed->state == ED_OPER)
+ start_ed_unlink (ohci, ed);
+
+ /* ... reenabling halted EDs only after fault cleanup */
+ } else if ((ed->hwINFO & cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE))
+ == cpu_to_hc32 (ohci, ED_SKIP)) {
+ td = list_entry (ed->td_list.next, struct td, td_list);
+ if (!(td->hwINFO & cpu_to_hc32 (ohci, TD_DONE))) {
+ ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP);
+ /* ... hc may need waking-up */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ohci_writel (ohci, OHCI_CLF,
+ &ohci->regs->cmdstatus);
+ break;
+ case PIPE_BULK:
+ ohci_writel (ohci, OHCI_BLF,
+ &ohci->regs->cmdstatus);
+ break;
+ }
+ }
+ }
+
+ td = td_next;
+ }
+}
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
new file mode 100644
index 0000000..814d2be
--- /dev/null
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -0,0 +1,289 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * SA1111 Bus Glue
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Rusell King et al.
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/assabet.h>
+#include <asm/arch/badge4.h>
+#include <asm/hardware/sa1111.h>
+
+#ifndef CONFIG_SA1111
+#error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined."
+#endif
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void sa1111_start_hc(struct sa1111_dev *dev)
+{
+ unsigned int usb_rst = 0;
+
+ printk(KERN_DEBUG __FILE__
+ ": starting SA-1111 OHCI USB Controller\n");
+
+#ifdef CONFIG_SA1100_BADGE4
+ if (machine_is_badge4()) {
+ badge4_set_5V(BADGE4_5V_USB, 1);
+ }
+#endif
+
+ if (machine_is_xp860() ||
+ machine_has_neponset() ||
+ machine_is_pfs168() ||
+ machine_is_badge4())
+ usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
+
+ /*
+ * Configure the power sense and control lines. Place the USB
+ * host controller in reset.
+ */
+ sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+ dev->mapbase + SA1111_USB_RESET);
+
+ /*
+ * Now, carefully enable the USB clock, and take
+ * the USB host controller out of reset.
+ */
+ sa1111_enable_device(dev);
+ udelay(11);
+ sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET);
+}
+
+static void sa1111_stop_hc(struct sa1111_dev *dev)
+{
+ unsigned int usb_rst;
+ printk(KERN_DEBUG __FILE__
+ ": stopping SA-1111 OHCI USB Controller\n");
+
+ /*
+ * Put the USB host controller into reset.
+ */
+ usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET);
+ sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+ dev->mapbase + SA1111_USB_RESET);
+
+ /*
+ * Stop the USB clock.
+ */
+ sa1111_disable_device(dev);
+
+#ifdef CONFIG_SA1100_BADGE4
+ if (machine_is_badge4()) {
+ /* Disable power to the USB bus */
+ badge4_set_5V(BADGE4_5V_USB, 0);
+ }
+#endif
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+#if 0
+static void dump_hci_status(struct usb_hcd *hcd, const char *label)
+{
+ unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS);
+
+ dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
+ ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
+ ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
+ ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
+ ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
+ ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_sa1111_probe - initialize SA-1111-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int usb_hcd_sa1111_probe (const struct hc_driver *driver,
+ struct sa1111_dev *dev)
+{
+ struct usb_hcd *hcd;
+ int retval;
+
+ hcd = usb_create_hcd (driver, &dev->dev, "sa1111");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = dev->res.start;
+ hcd->rsrc_len = dev->res.end - dev->res.start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dbg("request_mem_region failed");
+ retval = -EBUSY;
+ goto err1;
+ }
+ hcd->regs = dev->mapbase;
+
+ sa1111_start_hc(dev);
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ retval = usb_add_hcd(hcd, dev->irq[1], SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ sa1111_stop_hc(dev);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_sa1111_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev)
+{
+ usb_remove_hcd(hcd);
+ sa1111_stop_hc(dev);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_sa1111_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ err ("can't start %s", hcd->self.bus_name);
+ ohci_stop (hcd);
+ return ret;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_sa1111_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "SA-1111 OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_sa1111_start,
+#ifdef CONFIG_PM
+ /* suspend: ohci_sa1111_suspend, -- tbd */
+ /* resume: ohci_sa1111_resume, -- tbd */
+#endif
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_sa1111_drv_probe(struct sa1111_dev *dev)
+{
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, dev);
+ return ret;
+}
+
+static int ohci_hcd_sa1111_drv_remove(struct sa1111_dev *dev)
+{
+ struct usb_hcd *hcd = sa1111_get_drvdata(dev);
+
+ usb_hcd_sa1111_remove(hcd, dev);
+ return 0;
+}
+
+static struct sa1111_driver ohci_hcd_sa1111_driver = {
+ .drv = {
+ .name = "sa1111-ohci",
+ },
+ .devid = SA1111_DEVID_USB,
+ .probe = ohci_hcd_sa1111_drv_probe,
+ .remove = ohci_hcd_sa1111_drv_remove,
+};
+
+static int __init ohci_hcd_sa1111_init (void)
+{
+ dbg (DRIVER_INFO " (SA-1111)");
+ dbg ("block sizes: ed %d td %d",
+ sizeof (struct ed), sizeof (struct td));
+
+ return sa1111_driver_register(&ohci_hcd_sa1111_driver);
+}
+
+static void __exit ohci_hcd_sa1111_cleanup (void)
+{
+ sa1111_driver_unregister(&ohci_hcd_sa1111_driver);
+}
+
+module_init (ohci_hcd_sa1111_init);
+module_exit (ohci_hcd_sa1111_cleanup);
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
new file mode 100644
index 0000000..2ba6e2b
--- /dev/null
+++ b/drivers/usb/host/ohci.h
@@ -0,0 +1,636 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given OHCI_BIG_ENDIAN), depending on the
+ * host controller implementation.
+ */
+typedef __u32 __bitwise __hc32;
+typedef __u16 __bitwise __hc16;
+
+/*
+ * OHCI Endpoint Descriptor (ED) ... holds TD queue
+ * See OHCI spec, section 4.2
+ *
+ * This is a "Queue Head" for those transfers, which is why
+ * both EHCI and UHCI call similar structures a "QH".
+ */
+struct ed {
+ /* first fields are hardware-specified */
+ __hc32 hwINFO; /* endpoint config bitmap */
+ /* info bits defined by hcd */
+#define ED_DEQUEUE (1 << 27)
+ /* info bits defined by the hardware */
+#define ED_ISO (1 << 15)
+#define ED_SKIP (1 << 14)
+#define ED_LOWSPEED (1 << 13)
+#define ED_OUT (0x01 << 11)
+#define ED_IN (0x02 << 11)
+ __hc32 hwTailP; /* tail of TD list */
+ __hc32 hwHeadP; /* head of TD list (hc r/w) */
+#define ED_C (0x02) /* toggle carry */
+#define ED_H (0x01) /* halted */
+ __hc32 hwNextED; /* next ED in list */
+
+ /* rest are purely for the driver's use */
+ dma_addr_t dma; /* addr of ED */
+ struct td *dummy; /* next TD to activate */
+
+ /* host's view of schedule */
+ struct ed *ed_next; /* on schedule or rm_list */
+ struct ed *ed_prev; /* for non-interrupt EDs */
+ struct list_head td_list; /* "shadow list" of our TDs */
+
+ /* create --> IDLE --> OPER --> ... --> IDLE --> destroy
+ * usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
+ */
+ u8 state; /* ED_{IDLE,UNLINK,OPER} */
+#define ED_IDLE 0x00 /* NOT linked to HC */
+#define ED_UNLINK 0x01 /* being unlinked from hc */
+#define ED_OPER 0x02 /* IS linked to hc */
+
+ u8 type; /* PIPE_{BULK,...} */
+
+ /* periodic scheduling params (for intr and iso) */
+ u8 branch;
+ u16 interval;
+ u16 load;
+ u16 last_iso; /* iso only */
+
+ /* HC may see EDs on rm_list until next frame (frame_no == tick) */
+ u16 tick;
+} __attribute__ ((aligned(16)));
+
+#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
+
+
+/*
+ * OHCI Transfer Descriptor (TD) ... one per transfer segment
+ * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
+ * and 4.3.2 (iso)
+ */
+struct td {
+ /* first fields are hardware-specified */
+ __hc32 hwINFO; /* transfer info bitmask */
+
+ /* hwINFO bits for both general and iso tds: */
+#define TD_CC 0xf0000000 /* condition code */
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_DI 0x00E00000 /* frames before interrupt */
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+ /* these two bits are available for definition/use by HCDs in both
+ * general and iso tds ... others are available for only one type
+ */
+#define TD_DONE 0x00020000 /* retired to donelist */
+#define TD_ISO 0x00010000 /* copy of ED_ISO */
+
+ /* hwINFO bits for general tds: */
+#define TD_EC 0x0C000000 /* error count */
+#define TD_T 0x03000000 /* data toggle state */
+#define TD_T_DATA0 0x02000000 /* DATA0 */
+#define TD_T_DATA1 0x03000000 /* DATA1 */
+#define TD_T_TOGGLE 0x00000000 /* uses ED_C */
+#define TD_DP 0x00180000 /* direction/pid */
+#define TD_DP_SETUP 0x00000000 /* SETUP pid */
+#define TD_DP_IN 0x00100000 /* IN pid */
+#define TD_DP_OUT 0x00080000 /* OUT pid */
+ /* 0x00180000 rsvd */
+#define TD_R 0x00040000 /* round: short packets OK? */
+
+ /* (no hwINFO #defines yet for iso tds) */
+
+ __hc32 hwCBP; /* Current Buffer Pointer (or 0) */
+ __hc32 hwNextTD; /* Next TD Pointer */
+ __hc32 hwBE; /* Memory Buffer End Pointer */
+
+ /* PSW is only for ISO. Only 1 PSW entry is used, but on
+ * big-endian PPC hardware that's the second entry.
+ */
+#define MAXPSW 2
+ __hc16 hwPSW [MAXPSW];
+
+ /* rest are purely for the driver's use */
+ __u8 index;
+ struct ed *ed;
+ struct td *td_hash; /* dma-->td hashtable */
+ struct td *next_dl_td;
+ struct urb *urb;
+
+ dma_addr_t td_dma; /* addr of this TD */
+ dma_addr_t data_dma; /* addr of data it points to */
+
+ struct list_head td_list; /* "shadow list", TDs on same ED */
+} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
+
+#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */
+
+/*
+ * Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW
+ */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED 0x0F
+
+
+/* map OHCI TD status codes (CC) to errno values */
+static const int cc_to_error [16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined section 4.4.1 of the OHCI spec. The HC is
+ * told the base address of it. It must be 256-byte aligned.
+ */
+struct ohci_hcca {
+#define NUM_INTS 32
+ __hc32 int_table [NUM_INTS]; /* periodic schedule */
+
+ /*
+ * OHCI defines u16 frame_no, followed by u16 zero pad.
+ * Since some processors can't do 16 bit bus accesses,
+ * portable access must be a 32 bits wide.
+ */
+ __hc32 frame_no; /* current frame number */
+ __hc32 done_head; /* info returned for an interrupt */
+ u8 reserved_for_hc [116];
+ u8 what [4]; /* spec only identifies 252 bytes :) */
+} __attribute__ ((aligned(256)));
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O region.
+ * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
+ * Layout is in section 7 (and appendix B) of the spec.
+ */
+struct ohci_regs {
+ /* control and status registers (section 7.1) */
+ __hc32 revision;
+ __hc32 control;
+ __hc32 cmdstatus;
+ __hc32 intrstatus;
+ __hc32 intrenable;
+ __hc32 intrdisable;
+
+ /* memory pointers (section 7.2) */
+ __hc32 hcca;
+ __hc32 ed_periodcurrent;
+ __hc32 ed_controlhead;
+ __hc32 ed_controlcurrent;
+ __hc32 ed_bulkhead;
+ __hc32 ed_bulkcurrent;
+ __hc32 donehead;
+
+ /* frame counters (section 7.3) */
+ __hc32 fminterval;
+ __hc32 fmremaining;
+ __hc32 fmnumber;
+ __hc32 periodicstart;
+ __hc32 lsthresh;
+
+ /* Root hub ports (section 7.4) */
+ struct ohci_roothub_regs {
+ __hc32 a;
+ __hc32 b;
+ __hc32 status;
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */
+ __hc32 portstatus [MAX_ROOT_PORTS];
+ } roothub;
+
+ /* and optional "legacy support" registers (appendix B) at 0x0100 */
+
+} __attribute__ ((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+
+/* hcd-private per-urb state */
+typedef struct urb_priv {
+ struct ed *ed;
+ u16 length; // # tds in this request
+ u16 td_cnt; // tds already serviced
+ struct list_head pending;
+ struct td *td [0]; // all TDs in this request
+
+} urb_priv_t;
+
+#define TD_HASH_SIZE 64 /* power'o'two */
+// sizeof (struct td) ~= 64 == 2^6 ...
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
+
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+struct ohci_hcd {
+ spinlock_t lock;
+
+ /*
+ * I/O memory used to communicate with the HC (dma-consistent)
+ */
+ struct ohci_regs __iomem *regs;
+
+ /*
+ * main memory used to communicate with the HC (dma-consistent).
+ * hcd adds to schedule for a live hc any time, but removals finish
+ * only at the start of the next frame.
+ */
+ struct ohci_hcca *hcca;
+ dma_addr_t hcca_dma;
+
+ struct ed *ed_rm_list; /* to be removed */
+
+ struct ed *ed_bulktail; /* last in bulk list */
+ struct ed *ed_controltail; /* last in ctrl list */
+ struct ed *periodic [NUM_INTS]; /* shadow int_table */
+
+ /*
+ * OTG controllers and transceivers need software interaction;
+ * other external transceivers should be software-transparent
+ */
+ struct otg_transceiver *transceiver;
+ unsigned power_budget;
+
+ /*
+ * memory management for queue data structures
+ */
+ struct dma_pool *td_cache;
+ struct dma_pool *ed_cache;
+ struct td *td_hash [TD_HASH_SIZE];
+ struct list_head pending;
+
+ /*
+ * driver state
+ */
+ int load [NUM_INTS];
+ u32 hc_control; /* copy of hc control reg */
+ unsigned long next_statechange; /* suspend/resume */
+ u32 fminterval; /* saved register */
+
+ struct work_struct rh_resume;
+
+ unsigned long flags; /* for HC bugs */
+#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
+#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
+#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
+#define OHCI_BIG_ENDIAN 0x08 /* big endian HC */
+ // there are also chip quirks/bugs in init logic
+
+};
+
+/* convert between an hcd pointer and the corresponding ohci_hcd */
+static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd)
+{
+ return (struct ohci_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)
+{
+ return container_of ((void *) ohci, struct usb_hcd, hcd_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef DEBUG
+#define STUB_DEBUG_FILES
+#endif /* DEBUG */
+
+#define ohci_dbg(ohci, fmt, args...) \
+ dev_dbg (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+#define ohci_err(ohci, fmt, args...) \
+ dev_err (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+#define ohci_info(ohci, fmt, args...) \
+ dev_info (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+#define ohci_warn(ohci, fmt, args...) \
+ dev_warn (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
+
+#ifdef OHCI_VERBOSE_DEBUG
+# define ohci_vdbg ohci_dbg
+#else
+# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * While most USB host controllers implement their registers and
+ * in-memory communication descriptors in little-endian format,
+ * a minority (notably the IBM STB04XXX and the Motorola MPC5200
+ * processors) implement them in big endian format.
+ *
+ * This attempts to support either format at compile time without a
+ * runtime penalty, or both formats with the additional overhead
+ * of checking a flag bit.
+ */
+
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN
+
+#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
+#define big_endian(ohci) (ohci->flags & OHCI_BIG_ENDIAN) /* either */
+#else
+#define big_endian(ohci) 1 /* only big endian */
+#endif
+
+/*
+ * Big-endian read/write functions are arch-specific.
+ * Other arches can be added if/when they're needed.
+ */
+#if defined(CONFIG_PPC)
+#define readl_be(addr) in_be32((__force unsigned *)addr)
+#define writel_be(val, addr) out_be32((__force unsigned *)addr, val)
+#endif
+
+static inline unsigned int ohci_readl (const struct ohci_hcd *ohci,
+ __hc32 __iomem * regs)
+{
+ return big_endian(ohci) ? readl_be (regs) : readl ((__force u32 *)regs);
+}
+
+static inline void ohci_writel (const struct ohci_hcd *ohci,
+ const unsigned int val, __hc32 __iomem *regs)
+{
+ big_endian(ohci) ? writel_be (val, regs) :
+ writel (val, (__force u32 *)regs);
+}
+
+#else /* !CONFIG_USB_OHCI_BIG_ENDIAN */
+
+#define big_endian(ohci) 0 /* only little endian */
+
+#ifdef CONFIG_ARCH_LH7A404
+ /* Marc Singer: at the time this code was written, the LH7A404
+ * had a problem reading the USB host registers. This
+ * implementation of the ohci_readl function performs the read
+ * twice as a work-around.
+ */
+static inline unsigned int
+ohci_readl (const struct ohci_hcd *ohci, const __hc32 *regs)
+{
+ *(volatile __force unsigned int*) regs;
+ return *(volatile __force unsigned int*) regs;
+}
+#else
+ /* Standard version of ohci_readl uses standard, platform
+ * specific implementation. */
+static inline unsigned int
+ohci_readl (const struct ohci_hcd *ohci, __hc32 __iomem * regs)
+{
+ return readl(regs);
+}
+#endif
+
+static inline void ohci_writel (const struct ohci_hcd *ohci,
+ const unsigned int val, __hc32 __iomem *regs)
+{
+ writel (val, regs);
+}
+
+#endif /* !CONFIG_USB_OHCI_BIG_ENDIAN */
+
+/*-------------------------------------------------------------------------*/
+
+/* cpu to ohci */
+static inline __hc16 cpu_to_hc16 (const struct ohci_hcd *ohci, const u16 x)
+{
+ return big_endian(ohci) ? (__force __hc16)cpu_to_be16(x) : (__force __hc16)cpu_to_le16(x);
+}
+
+static inline __hc16 cpu_to_hc16p (const struct ohci_hcd *ohci, const u16 *x)
+{
+ return big_endian(ohci) ? cpu_to_be16p(x) : cpu_to_le16p(x);
+}
+
+static inline __hc32 cpu_to_hc32 (const struct ohci_hcd *ohci, const u32 x)
+{
+ return big_endian(ohci) ? (__force __hc32)cpu_to_be32(x) : (__force __hc32)cpu_to_le32(x);
+}
+
+static inline __hc32 cpu_to_hc32p (const struct ohci_hcd *ohci, const u32 *x)
+{
+ return big_endian(ohci) ? cpu_to_be32p(x) : cpu_to_le32p(x);
+}
+
+/* ohci to cpu */
+static inline u16 hc16_to_cpu (const struct ohci_hcd *ohci, const __hc16 x)
+{
+ return big_endian(ohci) ? be16_to_cpu((__force __be16)x) : le16_to_cpu((__force __le16)x);
+}
+
+static inline u16 hc16_to_cpup (const struct ohci_hcd *ohci, const __hc16 *x)
+{
+ return big_endian(ohci) ? be16_to_cpup((__force __be16 *)x) : le16_to_cpup((__force __le16 *)x);
+}
+
+static inline u32 hc32_to_cpu (const struct ohci_hcd *ohci, const __hc32 x)
+{
+ return big_endian(ohci) ? be32_to_cpu((__force __be32)x) : le32_to_cpu((__force __le32)x);
+}
+
+static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
+{
+ return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) : le32_to_cpup((__force __le32 *)x);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all
+ * hardware handles 16 bit reads. That creates a different confusion on
+ * some big-endian SOC implementations. Same thing happens with PSW access.
+ */
+
+#ifdef CONFIG_STB03xxx
+#define OHCI_BE_FRAME_NO_SHIFT 16
+#else
+#define OHCI_BE_FRAME_NO_SHIFT 0
+#endif
+
+static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
+{
+ u32 tmp;
+ if (big_endian(ohci)) {
+ tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
+ tmp >>= OHCI_BE_FRAME_NO_SHIFT;
+ } else
+ tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
+
+ return (u16)tmp;
+}
+
+static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
+ const struct td *td, int index)
+{
+ return (__hc16 *)(big_endian(ohci) ?
+ &td->hwPSW[index ^ 1] : &td->hwPSW[index]);
+}
+
+static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
+ const struct td *td, int index)
+{
+ return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void disable (struct ohci_hcd *ohci)
+{
+ ohci_to_hcd(ohci)->state = HC_STATE_HALT;
+}
+
+#define FI 0x2edf /* 12000 bits per frame (-1) */
+#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
+#define FIT (1 << 31)
+#define LSTHRESH 0x628 /* lowspeed bit threshold */
+
+static void periodic_reinit (struct ohci_hcd *ohci)
+{
+ u32 fi = ohci->fminterval & 0x03fff;
+ u32 fit = ohci_readl(ohci, &ohci->regs->fminterval) & FIT;
+
+ ohci_writel (ohci, (fit ^ FIT) | ohci->fminterval,
+ &ohci->regs->fminterval);
+ ohci_writel (ohci, ((9 * fi) / 10) & 0x3fff,
+ &ohci->regs->periodicstart);
+}
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = ohci_readl (hc, &hc->regs->roothub.register); \
+ if (temp == -1) \
+ disable (hc); \
+ else if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = ohci_readl (hc, &hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci_hcd *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+ { return ohci_readl (hc, &hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+ { return ohci_readl (hc, &hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
new file mode 100644
index 0000000..d309e29
--- /dev/null
+++ b/drivers/usb/host/sl811-hcd.c
@@ -0,0 +1,1851 @@
+/*
+ * SL811HS HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ *
+ * Periodic scheduling is based on Roman's OHCI code
+ * Copyright (C) 1999 Roman Weissgaerber
+ *
+ * The SL811HS controller handles host side USB (like the SL11H, but with
+ * another register set and SOF generation) as well as peripheral side USB
+ * (like the SL811S). This driver version doesn't implement the Gadget API
+ * for the peripheral role; or OTG (that'd need much external circuitry).
+ *
+ * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
+ * document (providing significant pieces missing from that spec); plus
+ * the SL811S spec if you want peripheral side info.
+ */
+
+/*
+ * Status: Passed basic stress testing, works with hubs, mice, keyboards,
+ * and usb-storage.
+ *
+ * TODO:
+ * - usb suspend/resume triggered by sl811 (with USB_SUSPEND)
+ * - various issues noted in the code
+ * - performance work; use both register banks; ...
+ * - use urb->iso_frame_desc[] with ISO transfers
+ */
+
+#undef VERBOSE
+#undef PACKET_TRACE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+# define DEBUG
+#else
+# undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.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>
+#include <linux/usb.h>
+#include <linux/usb_sl811.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "../core/hcd.h"
+#include "sl811.h"
+
+
+MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_VERSION "15 Dec 2004"
+
+
+#ifndef DEBUG
+# define STUB_DEBUG_FILE
+#endif
+
+/* for now, use only one transfer register bank */
+#undef USE_B
+
+/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
+ * that just queued one ISO frame per URB then iso transfers "should" work
+ * using the normal urb status fields.
+ */
+#define DISABLE_ISO
+
+// #define QUIRK2
+#define QUIRK3
+
+static const char hcd_name[] = "sl811-hcd";
+
+/*-------------------------------------------------------------------------*/
+
+static void port_power(struct sl811 *sl811, int is_on)
+{
+ struct usb_hcd *hcd = sl811_to_hcd(sl811);
+
+ /* hub is inactive unless the port is powered */
+ if (is_on) {
+ if (sl811->port1 & (1 << USB_PORT_FEAT_POWER))
+ return;
+
+ sl811->port1 = (1 << USB_PORT_FEAT_POWER);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ hcd->self.controller->power.power_state = PMSG_ON;
+ } else {
+ sl811->port1 = 0;
+ sl811->irq_enable = 0;
+ hcd->state = HC_STATE_HALT;
+ hcd->self.controller->power.power_state = PMSG_SUSPEND;
+ }
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+ sl811_write(sl811, SL11H_IRQ_STATUS, ~0);
+
+ if (sl811->board && sl811->board->port_power) {
+ /* switch VBUS, at 500mA unless hub power budget gets set */
+ DBG("power %s\n", is_on ? "on" : "off");
+ sl811->board->port_power(hcd->self.controller, is_on);
+ }
+
+ /* reset as thoroughly as we can */
+ if (sl811->board && sl811->board->reset)
+ sl811->board->reset(hcd->self.controller);
+
+ sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ // if !is_on, put into lowpower mode now
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue,
+ * and may start I/O. Endpoint queues are scanned during completion irq
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation.
+ *
+ * Using an external DMA engine to copy a packet at a time could work,
+ * though setup/teardown costs may be too big to make it worthwhile.
+ */
+
+/* SETUP starts a new control request. Devices are not allowed to
+ * STALL or NAK these; they must cancel any pending control requests.
+ */
+static void setup_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ len = sizeof(struct usb_ctrlrequest);
+ data_reg = sl811->data_reg;
+ sl811_write_buf(sl811, addr, urb->setup_packet, len);
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_SETUP /* | ep->epnum */, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ /* always OUT/data0 */ ;
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+ control | SL11H_HCTLMASK_OUT);
+ ep->length = 0;
+ PACKET("SETUP qh%p\n", ep);
+}
+
+/* STATUS finishes control requests, often after IN or OUT data packets */
+static void status_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ int do_out;
+ void __iomem *data_reg;
+
+ do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe);
+ data_reg = sl811->data_reg;
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, 0);
+ writeb(0, data_reg);
+ writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ /* always data1; sometimes IN */
+ control |= SL11H_HCTLMASK_TOGGLE;
+ if (do_out)
+ control |= SL11H_HCTLMASK_OUT;
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+ ep->length = 0;
+ PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
+ do_out ? "out" : "in", ep);
+}
+
+/* IN packets can be used with any type of endpoint. here we just
+ * start the transfer, data from the peripheral may arrive later.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void in_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ /* avoid losing data on overflow */
+ len = ep->maxpacket;
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ if (!(control & SL11H_HCTLMASK_ISOCH)
+ && usb_gettoggle(urb->dev, ep->epnum, 0))
+ control |= SL11H_HCTLMASK_TOGGLE;
+ data_reg = sl811->data_reg;
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_IN | ep->epnum, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+ ep->length = min((int)len,
+ urb->transfer_buffer_length - urb->actual_length);
+ PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+ !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len);
+}
+
+/* OUT packets can be used with any type of endpoint.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void out_packet(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ u8 bank,
+ u8 control
+)
+{
+ void *buf;
+ u8 addr;
+ u8 len;
+ void __iomem *data_reg;
+
+ buf = urb->transfer_buffer + urb->actual_length;
+ prefetch(buf);
+
+ len = min((int)ep->maxpacket,
+ urb->transfer_buffer_length - urb->actual_length);
+
+ if (!(control & SL11H_HCTLMASK_ISOCH)
+ && usb_gettoggle(urb->dev, ep->epnum, 1))
+ control |= SL11H_HCTLMASK_TOGGLE;
+ addr = SL811HS_PACKET_BUF(bank == 0);
+ data_reg = sl811->data_reg;
+
+ sl811_write_buf(sl811, addr, buf, len);
+
+ /* autoincrementing */
+ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+ writeb(len, data_reg);
+ writeb(SL_OUT | ep->epnum, data_reg);
+ writeb(usb_pipedevice(urb->pipe), data_reg);
+
+ sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+ control | SL11H_HCTLMASK_OUT);
+ ep->length = len;
+ PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+ !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* caller updates on-chip enables later */
+
+static inline void sofirq_on(struct sl811 *sl811)
+{
+ if (sl811->irq_enable & SL11H_INTMASK_SOFINTR)
+ return;
+ VDBG("sof irq on\n");
+ sl811->irq_enable |= SL11H_INTMASK_SOFINTR;
+}
+
+static inline void sofirq_off(struct sl811 *sl811)
+{
+ if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR))
+ return;
+ VDBG("sof irq off\n");
+ sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* pick the next endpoint for a transaction, and issue it.
+ * frames start with periodic transfers (after whatever is pending
+ * from the previous frame), and the rest of the time is async
+ * transfers, scheduled round-robin.
+ */
+static struct sl811h_ep *start(struct sl811 *sl811, u8 bank)
+{
+ struct sl811h_ep *ep;
+ struct urb *urb;
+ int fclock;
+ u8 control;
+
+ /* use endpoint at schedule head */
+ if (sl811->next_periodic) {
+ ep = sl811->next_periodic;
+ sl811->next_periodic = ep->next;
+ } else {
+ if (sl811->next_async)
+ ep = sl811->next_async;
+ else if (!list_empty(&sl811->async))
+ ep = container_of(sl811->async.next,
+ struct sl811h_ep, schedule);
+ else {
+ /* could set up the first fullspeed periodic
+ * transfer for the next frame ...
+ */
+ return NULL;
+ }
+
+#ifdef USE_B
+ if ((bank && sl811->active_b == ep) || sl811->active_a == ep)
+ return NULL;
+#endif
+
+ if (ep->schedule.next == &sl811->async)
+ sl811->next_async = NULL;
+ else
+ sl811->next_async = container_of(ep->schedule.next,
+ struct sl811h_ep, schedule);
+ }
+
+ if (unlikely(list_empty(&ep->hep->urb_list))) {
+ DBG("empty %p queue?\n", ep);
+ return NULL;
+ }
+
+ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+ control = ep->defctrl;
+
+ /* if this frame doesn't have enough time left to transfer this
+ * packet, wait till the next frame. too-simple algorithm...
+ */
+ fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
+ fclock -= 100; /* setup takes not much time */
+ if (urb->dev->speed == USB_SPEED_LOW) {
+ if (control & SL11H_HCTLMASK_PREAMBLE) {
+ /* also note erratum 1: some hubs won't work */
+ fclock -= 800;
+ }
+ fclock -= ep->maxpacket << 8;
+
+ /* erratum 2: AFTERSOF only works for fullspeed */
+ if (fclock < 0) {
+ if (ep->period)
+ sl811->stat_overrun++;
+ sofirq_on(sl811);
+ return NULL;
+ }
+ } else {
+ fclock -= 12000 / 19; /* 19 64byte packets/msec */
+ if (fclock < 0) {
+ if (ep->period)
+ sl811->stat_overrun++;
+ control |= SL11H_HCTLMASK_AFTERSOF;
+
+ /* throttle bulk/control irq noise */
+ } else if (ep->nak_count)
+ control |= SL11H_HCTLMASK_AFTERSOF;
+ }
+
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ in_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_OUT:
+ out_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_SETUP:
+ setup_packet(sl811, ep, urb, bank, control);
+ break;
+ case USB_PID_ACK: /* for control status */
+ status_packet(sl811, ep, urb, bank, control);
+ break;
+ default:
+ DBG("bad ep%p pid %02x\n", ep, ep->nextpid);
+ ep = NULL;
+ }
+ return ep;
+}
+
+#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2)
+
+static inline void start_transfer(struct sl811 *sl811)
+{
+ if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+ return;
+ if (sl811->active_a == NULL) {
+ sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));
+ if (sl811->active_a != NULL)
+ sl811->jiffies_a = jiffies + MIN_JIFFIES;
+ }
+#ifdef USE_B
+ if (sl811->active_b == NULL) {
+ sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF));
+ if (sl811->active_b != NULL)
+ sl811->jiffies_b = jiffies + MIN_JIFFIES;
+ }
+#endif
+}
+
+static void finish_request(
+ struct sl811 *sl811,
+ struct sl811h_ep *ep,
+ struct urb *urb,
+ struct pt_regs *regs,
+ int status
+) __releases(sl811->lock) __acquires(sl811->lock)
+{
+ unsigned i;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ spin_lock(&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = status;
+ spin_unlock(&urb->lock);
+
+ spin_unlock(&sl811->lock);
+ usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, regs);
+ spin_lock(&sl811->lock);
+
+ /* leave active endpoints in the schedule */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule? */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ if (ep == sl811->next_async)
+ sl811->next_async = NULL;
+ return;
+ }
+
+ /* periodic deschedule */
+ DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct sl811h_ep *temp;
+ struct sl811h_ep **prev = &sl811->periodic[i];
+
+ while (*prev && ((temp = *prev) != ep))
+ prev = &temp->next;
+ if (*prev)
+ *prev = ep->next;
+ sl811->load[i] -= ep->load;
+ }
+ ep->branch = PERIODIC_SIZE;
+ sl811->periodic_count--;
+ sl811_to_hcd(sl811)->self.bandwidth_allocated
+ -= ep->load / ep->period;
+ if (ep == sl811->next_periodic)
+ sl811->next_periodic = ep->next;
+
+ /* we might turn SOFs back on again for the async schedule */
+ if (sl811->periodic_count == 0)
+ sofirq_off(sl811);
+}
+
+static void
+done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
+{
+ u8 status;
+ struct urb *urb;
+ int urbstat = -EINPROGRESS;
+
+ if (unlikely(!ep))
+ return;
+
+ status = sl811_read(sl811, bank + SL11H_PKTSTATREG);
+
+ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+
+ /* we can safely ignore NAKs */
+ if (status & SL11H_STATMASK_NAK) {
+ // PACKET("...NAK_%02x qh%p\n", bank, ep);
+ if (!ep->period)
+ ep->nak_count++;
+ ep->error_count = 0;
+
+ /* ACK advances transfer, toggle, and maybe queue */
+ } else if (status & SL11H_STATMASK_ACK) {
+ struct usb_device *udev = urb->dev;
+ int len;
+ unsigned char *buf;
+
+ /* urb->iso_frame_desc is currently ignored here... */
+
+ ep->nak_count = ep->error_count = 0;
+ switch (ep->nextpid) {
+ case USB_PID_OUT:
+ // PACKET("...ACK/out_%02x qh%p\n", bank, ep);
+ urb->actual_length += ep->length;
+ usb_dotoggle(udev, ep->epnum, 1);
+ if (urb->actual_length
+ == urb->transfer_buffer_length) {
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_ACK;
+
+ /* some bulk protocols terminate OUT transfers
+ * by a short packet, using ZLPs not padding.
+ */
+ else if (ep->length < ep->maxpacket
+ || !(urb->transfer_flags
+ & URB_ZERO_PACKET))
+ urbstat = 0;
+ }
+ break;
+ case USB_PID_IN:
+ // PACKET("...ACK/in_%02x qh%p\n", bank, ep);
+ buf = urb->transfer_buffer + urb->actual_length;
+ prefetchw(buf);
+ len = ep->maxpacket - sl811_read(sl811,
+ bank + SL11H_XFERCNTREG);
+ if (len > ep->length) {
+ len = ep->length;
+ urb->status = -EOVERFLOW;
+ }
+ urb->actual_length += len;
+ sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),
+ buf, len);
+ usb_dotoggle(udev, ep->epnum, 0);
+ if (urb->actual_length == urb->transfer_buffer_length)
+ urbstat = 0;
+ else if (len < ep->maxpacket) {
+ if (urb->transfer_flags & URB_SHORT_NOT_OK)
+ urbstat = -EREMOTEIO;
+ else
+ urbstat = 0;
+ }
+ if (usb_pipecontrol(urb->pipe)
+ && (urbstat == -EREMOTEIO
+ || urbstat == 0)) {
+
+ /* NOTE if the status stage STALLs (why?),
+ * this reports the wrong urb status.
+ */
+ spin_lock(&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = urbstat;
+ spin_unlock(&urb->lock);
+
+ urb = NULL;
+ ep->nextpid = USB_PID_ACK;
+ }
+ break;
+ case USB_PID_SETUP:
+ // PACKET("...ACK/setup_%02x qh%p\n", bank, ep);
+ if (urb->transfer_buffer_length == urb->actual_length)
+ ep->nextpid = USB_PID_ACK;
+ else if (usb_pipeout(urb->pipe)) {
+ usb_settoggle(udev, 0, 1, 1);
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ usb_settoggle(udev, 0, 0, 1);
+ ep->nextpid = USB_PID_IN;
+ }
+ break;
+ case USB_PID_ACK:
+ // PACKET("...ACK/status_%02x qh%p\n", bank, ep);
+ urbstat = 0;
+ break;
+ }
+
+ /* STALL stops all transfers */
+ } else if (status & SL11H_STATMASK_STALL) {
+ PACKET("...STALL_%02x qh%p\n", bank, ep);
+ ep->nak_count = ep->error_count = 0;
+ urbstat = -EPIPE;
+
+ /* error? retry, until "3 strikes" */
+ } else if (++ep->error_count >= 3) {
+ if (status & SL11H_STATMASK_TMOUT)
+ urbstat = -ETIMEDOUT;
+ else if (status & SL11H_STATMASK_OVF)
+ urbstat = -EOVERFLOW;
+ else
+ urbstat = -EPROTO;
+ ep->error_count = 0;
+ PACKET("...3STRIKES_%02x %02x qh%p stat %d\n",
+ bank, status, ep, urbstat);
+ }
+
+ if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS))
+ finish_request(sl811, ep, urb, regs, urbstat);
+}
+
+static inline u8 checkdone(struct sl811 *sl811)
+{
+ u8 ctl;
+ u8 irqstat = 0;
+
+ if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {
+ ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));
+ if (ctl & SL11H_HCTLMASK_ARM)
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+ DBG("%s DONE_A: ctrl %02x sts %02x\n",
+ (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+ ctl,
+ sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+ irqstat |= SL11H_INTMASK_DONE_A;
+ }
+#ifdef USE_B
+ if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {
+ ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));
+ if (ctl & SL11H_HCTLMASK_ARM)
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+ DBG("%s DONE_B: ctrl %02x sts %02x\n",
+ (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+ ctl,
+ sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+ irqstat |= SL11H_INTMASK_DONE_A;
+ }
+#endif
+ return irqstat;
+}
+
+static irqreturn_t sl811h_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ u8 irqstat;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned retries = 5;
+
+ spin_lock(&sl811->lock);
+
+retry:
+ irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP;
+ if (irqstat) {
+ sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+ irqstat &= sl811->irq_enable;
+ }
+
+#ifdef QUIRK2
+ /* this may no longer be necessary ... */
+ if (irqstat == 0 && ret == IRQ_NONE) {
+ irqstat = checkdone(sl811);
+ if (irqstat /* && irq != ~0 */ )
+ sl811->stat_lost++;
+ }
+#endif
+
+ /* USB packets, not necessarily handled in the order they're
+ * issued ... that's fine if they're different endpoints.
+ */
+ if (irqstat & SL11H_INTMASK_DONE_A) {
+ done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs);
+ sl811->active_a = NULL;
+ sl811->stat_a++;
+ }
+#ifdef USE_B
+ if (irqstat & SL11H_INTMASK_DONE_B) {
+ done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs);
+ sl811->active_b = NULL;
+ sl811->stat_b++;
+ }
+#endif
+ if (irqstat & SL11H_INTMASK_SOFINTR) {
+ unsigned index;
+
+ index = sl811->frame++ % (PERIODIC_SIZE - 1);
+ sl811->stat_sof++;
+
+ /* be graceful about almost-inevitable periodic schedule
+ * overruns: continue the previous frame's transfers iff
+ * this one has nothing scheduled.
+ */
+ if (sl811->next_periodic) {
+ // ERR("overrun to slot %d\n", index);
+ sl811->stat_overrun++;
+ }
+ if (sl811->periodic[index])
+ sl811->next_periodic = sl811->periodic[index];
+ }
+
+ /* khubd manages debouncing and wakeup */
+ if (irqstat & SL11H_INTMASK_INSRMV) {
+ sl811->stat_insrmv++;
+
+ /* most stats are reset for each VBUS session */
+ sl811->stat_wake = 0;
+ sl811->stat_sof = 0;
+ sl811->stat_a = 0;
+ sl811->stat_b = 0;
+ sl811->stat_lost = 0;
+
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ /* usbcore nukes other pending transactions on disconnect */
+ if (sl811->active_a) {
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+ finish_request(sl811, sl811->active_a,
+ container_of(sl811->active_a->hep->urb_list.next,
+ struct urb, urb_list),
+ NULL, -ESHUTDOWN);
+ sl811->active_a = NULL;
+ }
+#ifdef USE_B
+ if (sl811->active_b) {
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+ finish_request(sl811, sl811->active_b,
+ container_of(sl811->active_b->hep->urb_list.next,
+ struct urb, urb_list),
+ NULL, -ESHUTDOWN);
+ sl811->active_b = NULL;
+ }
+#endif
+
+ /* port status seems wierd until after reset, so
+ * force the reset and make khubd clean up later.
+ */
+ sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+ | (1 << USB_PORT_FEAT_CONNECTION);
+
+ } else if (irqstat & SL11H_INTMASK_RD) {
+ if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) {
+ DBG("wakeup\n");
+ sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND;
+ sl811->stat_wake++;
+ } else
+ irqstat &= ~SL11H_INTMASK_RD;
+ }
+
+ if (irqstat) {
+ if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+ start_transfer(sl811);
+ ret = IRQ_HANDLED;
+ if (retries--)
+ goto retry;
+ }
+
+ if (sl811->periodic_count == 0 && list_empty(&sl811->async))
+ sofirq_off(sl811);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+ spin_unlock(&sl811->lock);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* usb 1.1 says max 90% of a frame is available for periodic transfers.
+ * this driver doesn't promise that much since it's got to handle an
+ * IRQ per packet; irq handling latencies also use up that time.
+ */
+#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */
+
+static int balance(struct sl811 *sl811, u16 period, u16 load)
+{
+ int i, branch = -ENOSPC;
+
+ /* search for the least loaded schedule branch of that period
+ * which has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < period ; i++) {
+ if (branch < 0 || sl811->load[branch] > sl811->load[i]) {
+ int j;
+
+ for (j = i; j < PERIODIC_SIZE; j += period) {
+ if ((sl811->load[j] + load)
+ > MAX_PERIODIC_LOAD)
+ break;
+ }
+ if (j < PERIODIC_SIZE)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sl811h_urb_enqueue(
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_device *udev = urb->dev;
+ unsigned int pipe = urb->pipe;
+ int is_out = !usb_pipein(pipe);
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ struct sl811h_ep *ep = NULL;
+ unsigned long flags;
+ int i;
+ int retval = 0;
+
+#ifdef DISABLE_ISO
+ if (type == PIPE_ISOCHRONOUS)
+ return -ENOSPC;
+#endif
+
+ /* avoid all allocations within spinlocks */
+ if (!hep->hcpriv)
+ ep = kcalloc(1, sizeof *ep, mem_flags);
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ /* don't submit to a dead or disabled port */
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+ || !HC_IS_RUNNING(hcd->state)) {
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ if (hep->hcpriv) {
+ kfree(ep);
+ ep = hep->hcpriv;
+ } else if (!ep) {
+ retval = -ENOMEM;
+ goto fail;
+
+ } else {
+ INIT_LIST_HEAD(&ep->schedule);
+ ep->udev = usb_get_dev(udev);
+ ep->epnum = epnum;
+ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+ ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
+ usb_settoggle(udev, epnum, is_out, 0);
+
+ if (type == PIPE_CONTROL)
+ ep->nextpid = USB_PID_SETUP;
+ else if (is_out)
+ ep->nextpid = USB_PID_OUT;
+ else
+ ep->nextpid = USB_PID_IN;
+
+ if (ep->maxpacket > H_MAXPACKET) {
+ /* iso packets up to 240 bytes could work... */
+ DBG("dev %d ep%d maxpacket %d\n",
+ udev->devnum, epnum, ep->maxpacket);
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ if (udev->speed == USB_SPEED_LOW) {
+ /* send preamble for external hub? */
+ if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))
+ ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;
+ }
+ switch (type) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ if (urb->interval > PERIODIC_SIZE)
+ urb->interval = PERIODIC_SIZE;
+ ep->period = urb->interval;
+ ep->branch = PERIODIC_SIZE;
+ if (type == PIPE_ISOCHRONOUS)
+ ep->defctrl |= SL11H_HCTLMASK_ISOCH;
+ ep->load = usb_calc_bus_time(udev->speed, !is_out,
+ (type == PIPE_ISOCHRONOUS),
+ usb_maxpacket(udev, pipe, is_out))
+ / 1000;
+ break;
+ }
+
+ hep->hcpriv = ep;
+ }
+
+ /* maybe put endpoint into schedule */
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (list_empty(&ep->schedule))
+ list_add_tail(&ep->schedule, &sl811->async);
+ break;
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ urb->interval = ep->period;
+ if (ep->branch < PERIODIC_SIZE)
+ break;
+
+ retval = balance(sl811, ep->period, ep->load);
+ if (retval < 0)
+ goto fail;
+ ep->branch = retval;
+ retval = 0;
+ urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+ + ep->branch;
+
+ /* sort each schedule branch by period (slow before fast)
+ * to share the faster parts of the tree without needing
+ * dummy/placeholder nodes
+ */
+ DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct sl811h_ep **prev = &sl811->periodic[i];
+ struct sl811h_ep *here = *prev;
+
+ while (here && ep != here) {
+ if (ep->period > here->period)
+ break;
+ prev = &here->next;
+ here = *prev;
+ }
+ if (ep != here) {
+ ep->next = here;
+ *prev = ep;
+ }
+ sl811->load[i] += ep->load;
+ }
+ sl811->periodic_count++;
+ hcd->self.bandwidth_allocated += ep->load / ep->period;
+ sofirq_on(sl811);
+ }
+
+ /* in case of unlink-during-submit */
+ spin_lock(&urb->lock);
+ if (urb->status != -EINPROGRESS) {
+ spin_unlock(&urb->lock);
+ finish_request(sl811, ep, urb, NULL, 0);
+ retval = 0;
+ goto fail;
+ }
+ urb->hcpriv = hep;
+ spin_unlock(&urb->lock);
+
+ start_transfer(sl811);
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+fail:
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_host_endpoint *hep = urb->hcpriv;
+ unsigned long flags;
+ struct sl811h_ep *ep;
+ int retval = 0;
+
+ if (!hep)
+ return -EINVAL;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+ ep = hep->hcpriv;
+ if (ep) {
+ /* finish right away if this urb can't be active ...
+ * note that some drivers wrongly expect delays
+ */
+ if (ep->hep->urb_list.next != &urb->urb_list) {
+ /* not front of queue? never active */
+
+ /* for active transfers, we expect an IRQ */
+ } else if (sl811->active_a == ep) {
+ if (time_before_eq(sl811->jiffies_a, jiffies)) {
+ /* happens a lot with lowspeed?? */
+ DBG("giveup on DONE_A: ctrl %02x sts %02x\n",
+ sl811_read(sl811,
+ SL811_EP_A(SL11H_HOSTCTLREG)),
+ sl811_read(sl811,
+ SL811_EP_A(SL11H_PKTSTATREG)));
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+ 0);
+ sl811->active_a = NULL;
+ } else
+ urb = NULL;
+#ifdef USE_B
+ } else if (sl811->active_b == ep) {
+ if (time_before_eq(sl811->jiffies_a, jiffies)) {
+ /* happens a lot with lowspeed?? */
+ DBG("giveup on DONE_B: ctrl %02x sts %02x\n",
+ sl811_read(sl811,
+ SL811_EP_B(SL11H_HOSTCTLREG)),
+ sl811_read(sl811,
+ SL811_EP_B(SL11H_PKTSTATREG)));
+ sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG),
+ 0);
+ sl811->active_b = NULL;
+ } else
+ urb = NULL;
+#endif
+ } else {
+ /* front of queue for inactive endpoint */
+ }
+
+ if (urb)
+ finish_request(sl811, ep, urb, NULL, 0);
+ else
+ VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
+ (sl811->active_a == ep) ? "A" : "B");
+ } else
+ retval = -EINVAL;
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+static void
+sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+{
+ struct sl811h_ep *ep = hep->hcpriv;
+
+ if (!ep)
+ return;
+
+ /* assume we'd just wait for the irq */
+ if (!list_empty(&hep->urb_list))
+ msleep(3);
+ if (!list_empty(&hep->urb_list))
+ WARN("ep %p not empty?\n", ep);
+
+ usb_put_dev(ep->udev);
+ kfree(ep);
+ hep->hcpriv = NULL;
+}
+
+static int
+sl811h_get_frame(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ /* wrong except while periodic transfers are scheduled;
+ * never matches the on-the-wire frame;
+ * subject to overruns.
+ */
+ return sl811->frame;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* the virtual root hub timer IRQ checks for hub status */
+static int
+sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+#ifdef QUIRK3
+ unsigned long flags;
+
+ /* non-SMP HACK: use root hub timer as i/o watchdog
+ * this seems essential when SOF IRQs aren't in use...
+ */
+ local_irq_save(flags);
+ if (!timer_pending(&sl811->timer)) {
+ if (sl811h_irq( /* ~0, */ hcd, NULL) != IRQ_NONE)
+ sl811->stat_lost++;
+ }
+ local_irq_restore(flags);
+#endif
+
+ if (!(sl811->port1 & (0xffff << 16)))
+ return 0;
+
+ /* tell khubd port 1 changed */
+ *buf = (1 << 1);
+ return 1;
+}
+
+static void
+sl811h_hub_descriptor (
+ struct sl811 *sl811,
+ struct usb_hub_descriptor *desc
+) {
+ u16 temp = 0;
+
+ desc->bDescriptorType = 0x29;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = 1;
+ desc->bDescLength = 9;
+
+ /* per-port power switching (gang of one!), or none */
+ desc->bPwrOn2PwrGood = 0;
+ if (sl811->board && sl811->board->port_power) {
+ desc->bPwrOn2PwrGood = sl811->board->potpg;
+ if (!desc->bPwrOn2PwrGood)
+ desc->bPwrOn2PwrGood = 10;
+ temp = 0x0001;
+ } else
+ temp = 0x0002;
+
+ /* no overcurrent errors detection/handling */
+ temp |= 0x0010;
+
+ desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
+
+ /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
+ desc->bitmap[0] = 1 << 1;
+ desc->bitmap[1] = ~0;
+}
+
+static void
+sl811h_timer(unsigned long _sl811)
+{
+ struct sl811 *sl811 = (void *) _sl811;
+ unsigned long flags;
+ u8 irqstat;
+ u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
+ const u32 mask = (1 << USB_PORT_FEAT_CONNECTION)
+ | (1 << USB_PORT_FEAT_ENABLE)
+ | (1 << USB_PORT_FEAT_LOWSPEED);
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ /* stop special signaling */
+ sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ udelay(3);
+
+ irqstat = sl811_read(sl811, SL11H_IRQ_STATUS);
+
+ switch (signaling) {
+ case SL11H_CTL1MASK_SE0:
+ DBG("end reset\n");
+ sl811->port1 = (1 << USB_PORT_FEAT_C_RESET)
+ | (1 << USB_PORT_FEAT_POWER);
+ sl811->ctrl1 = 0;
+ /* don't wrongly ack RD */
+ if (irqstat & SL11H_INTMASK_INSRMV)
+ irqstat &= ~SL11H_INTMASK_RD;
+ break;
+ case SL11H_CTL1MASK_K:
+ DBG("end resume\n");
+ sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND);
+ break;
+ default:
+ DBG("odd timer signaling: %02x\n", signaling);
+ break;
+ }
+ sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+
+ if (irqstat & SL11H_INTMASK_RD) {
+ /* usbcore nukes all pending transactions on disconnect */
+ if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION))
+ sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+ | (1 << USB_PORT_FEAT_C_ENABLE);
+ sl811->port1 &= ~mask;
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ } else {
+ sl811->port1 |= mask;
+ if (irqstat & SL11H_INTMASK_DP)
+ sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD;
+ }
+
+ if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) {
+ u8 ctrl2 = SL811HS_CTL2_INIT;
+
+ sl811->irq_enable |= SL11H_INTMASK_DONE_A;
+#ifdef USE_B
+ sl811->irq_enable |= SL11H_INTMASK_DONE_B;
+#endif
+ if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) {
+ sl811->ctrl1 |= SL11H_CTL1MASK_LSPD;
+ ctrl2 |= SL811HS_CTL2MASK_DSWAP;
+ }
+
+ /* start SOFs flowing, kickstarting with A registers */
+ sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA;
+ sl811_write(sl811, SL11H_SOFLOWREG, 0xe0);
+ sl811_write(sl811, SL811HS_CTLREG2, ctrl2);
+
+ /* autoincrementing */
+ sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0);
+ writeb(SL_SOF, sl811->data_reg);
+ writeb(0, sl811->data_reg);
+ sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+ SL11H_HCTLMASK_ARM);
+
+ /* khubd provides debounce delay */
+ } else {
+ sl811->ctrl1 = 0;
+ }
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ /* reenable irqs */
+ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+ spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sl811->lock, flags);
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ sl811->port1 &= (1 << USB_PORT_FEAT_POWER);
+ sl811->ctrl1 = 0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811->irq_enable = SL11H_INTMASK_INSRMV;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)))
+ break;
+
+ /* 20 msec of resume/K signaling, other irqs blocked */
+ DBG("start resume...\n");
+ sl811->irq_enable = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ sl811->ctrl1 |= SL11H_CTL1MASK_K;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+ mod_timer(&sl811->timer, jiffies
+ + msecs_to_jiffies(20));
+ break;
+ case USB_PORT_FEAT_POWER:
+ port_power(sl811, 0);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ break;
+ default:
+ goto error;
+ }
+ sl811->port1 &= ~(1 << wValue);
+ break;
+ case GetHubDescriptor:
+ sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ *(__le32 *) buf = cpu_to_le32(0);
+ break;
+ case GetPortStatus:
+ if (wIndex != 1)
+ goto error;
+ *(__le32 *) buf = cpu_to_le32(sl811->port1);
+
+#ifndef VERBOSE
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ DBG("GetPortStatus %08x\n", sl811->port1);
+ break;
+ case SetPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (sl811->port1 & (1 << USB_PORT_FEAT_RESET))
+ goto error;
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)))
+ goto error;
+
+ DBG("suspend...\n");
+ sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ break;
+ case USB_PORT_FEAT_POWER:
+ port_power(sl811, 1);
+ break;
+ case USB_PORT_FEAT_RESET:
+ if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+ goto error;
+ if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER)))
+ break;
+
+ /* 50 msec of reset/SE0 signaling, irqs blocked */
+ sl811->irq_enable = 0;
+ sl811_write(sl811, SL11H_IRQ_ENABLE,
+ sl811->irq_enable);
+ sl811->ctrl1 = SL11H_CTL1MASK_SE0;
+ sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+ sl811->port1 |= (1 << USB_PORT_FEAT_RESET);
+ mod_timer(&sl811->timer, jiffies
+ + msecs_to_jiffies(50));
+ break;
+ default:
+ goto error;
+ }
+ sl811->port1 |= 1 << wValue;
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+
+ spin_unlock_irqrestore(&sl811->lock, flags);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+
+static int
+sl811h_hub_suspend(struct usb_hcd *hcd)
+{
+ // SOFs off
+ DBG("%s\n", __FUNCTION__);
+ return 0;
+}
+
+static int
+sl811h_hub_resume(struct usb_hcd *hcd)
+{
+ // SOFs on
+ DBG("%s\n", __FUNCTION__);
+ return 0;
+}
+
+#else
+
+#define sl811h_hub_suspend NULL
+#define sl811h_hub_resume NULL
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILE
+
+static inline void create_debug_file(struct sl811 *sl811) { }
+static inline void remove_debug_file(struct sl811 *sl811) { }
+
+#else
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void dump_irq(struct seq_file *s, char *label, u8 mask)
+{
+ seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask,
+ (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "",
+ (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "",
+ (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "",
+ (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "",
+ (mask & SL11H_INTMASK_RD) ? " rd" : "",
+ (mask & SL11H_INTMASK_DP) ? " dp" : "");
+}
+
+static int proc_sl811h_show(struct seq_file *s, void *unused)
+{
+ struct sl811 *sl811 = s->private;
+ struct sl811h_ep *ep;
+ unsigned i;
+
+ seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n",
+ sl811_to_hcd(sl811)->product_desc,
+ hcd_name, DRIVER_VERSION,
+ sl811->port1);
+
+ seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv);
+ seq_printf(s, "current session: done_a %ld done_b %ld "
+ "wake %ld sof %ld overrun %ld lost %ld\n\n",
+ sl811->stat_a, sl811->stat_b,
+ sl811->stat_wake, sl811->stat_sof,
+ sl811->stat_overrun, sl811->stat_lost);
+
+ spin_lock_irq(&sl811->lock);
+
+ if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND)
+ seq_printf(s, "(suspended)\n\n");
+ else {
+ u8 t = sl811_read(sl811, SL11H_CTLREG1);
+
+ seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t,
+ (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "",
+ ({char *s; switch (t & SL11H_CTL1MASK_FORCE) {
+ case SL11H_CTL1MASK_NORMAL: s = ""; break;
+ case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
+ case SL11H_CTL1MASK_K: s = " k/resume"; break;
+ default: s = "j"; break;
+ }; s; }),
+ (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
+ (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
+
+ dump_irq(s, "irq_enable",
+ sl811_read(sl811, SL11H_IRQ_ENABLE));
+ dump_irq(s, "irq_status",
+ sl811_read(sl811, SL11H_IRQ_STATUS));
+ seq_printf(s, "frame clocks remaining: %d\n",
+ sl811_read(sl811, SL11H_SOFTMRREG) << 6);
+ }
+
+ seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a,
+ sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)),
+ sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+ seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b,
+ sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)),
+ sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+ seq_printf(s, "\n");
+ list_for_each_entry (ep, &sl811->async, schedule) {
+ struct urb *urb;
+
+ seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d"
+ " nak %d err %d\n",
+ (ep == sl811->active_a) ? "(A) " : "",
+ (ep == sl811->active_b) ? "(B) " : "",
+ ep, ep->epnum,
+ ({ char *s; switch (ep->nextpid) {
+ case USB_PID_IN: s = "in"; break;
+ case USB_PID_OUT: s = "out"; break;
+ case USB_PID_SETUP: s = "setup"; break;
+ case USB_PID_ACK: s = "status"; break;
+ default: s = "?"; break;
+ }; s;}),
+ ep->maxpacket,
+ ep->nak_count, ep->error_count);
+ list_for_each_entry (urb, &ep->hep->urb_list, urb_list) {
+ seq_printf(s, " urb%p, %d/%d\n", urb,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+ }
+ if (!list_empty(&sl811->async))
+ seq_printf(s, "\n");
+
+ seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+ for (i = 0; i < PERIODIC_SIZE; i++) {
+ ep = sl811->periodic[i];
+ if (!ep)
+ continue;
+ seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]);
+
+ /* DUMB: prints shared entries multiple times */
+ do {
+ seq_printf(s,
+ " %s%sqh%d/%p (%sdev%d ep%d%s max %d) "
+ "err %d\n",
+ (ep == sl811->active_a) ? "(A) " : "",
+ (ep == sl811->active_b) ? "(B) " : "",
+ ep->period, ep,
+ (ep->udev->speed == USB_SPEED_FULL)
+ ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? ""
+ : ((ep->nextpid == USB_PID_IN)
+ ? "in"
+ : "out"),
+ ep->maxpacket, ep->error_count);
+ ep = ep->next;
+ } while (ep);
+ }
+
+ spin_unlock_irq(&sl811->lock);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int proc_sl811h_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_sl811h_show, PDE(inode)->data);
+}
+
+static struct file_operations proc_ops = {
+ .open = proc_sl811h_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* expect just one sl811 per system */
+static const char proc_filename[] = "driver/sl811h";
+
+static void create_debug_file(struct sl811 *sl811)
+{
+ struct proc_dir_entry *pde;
+
+ pde = create_proc_entry(proc_filename, 0, NULL);
+ if (pde == NULL)
+ return;
+
+ pde->proc_fops = &proc_ops;
+ pde->data = sl811;
+ sl811->pde = pde;
+}
+
+static void remove_debug_file(struct sl811 *sl811)
+{
+ if (sl811->pde)
+ remove_proc_entry(proc_filename, NULL);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void
+sl811h_stop(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ unsigned long flags;
+
+ del_timer_sync(&hcd->rh_timer);
+
+ spin_lock_irqsave(&sl811->lock, flags);
+ port_power(sl811, 0);
+ spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_start(struct usb_hcd *hcd)
+{
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct usb_device *udev;
+
+ /* chip has been reset, VBUS power is off */
+
+ udev = usb_alloc_dev(NULL, &hcd->self, 0);
+ if (!udev)
+ return -ENOMEM;
+
+ udev->speed = USB_SPEED_FULL;
+ hcd->state = HC_STATE_RUNNING;
+
+ if (sl811->board)
+ hcd->can_wakeup = sl811->board->can_wakeup;
+
+ if (usb_hcd_register_root_hub(udev, hcd) != 0) {
+ usb_put_dev(udev);
+ sl811h_stop(hcd);
+ return -ENODEV;
+ }
+
+ if (sl811->board && sl811->board->power)
+ hub_set_power_budget(udev, sl811->board->power * 2);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver sl811h_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct sl811),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = sl811h_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /* Basic lifecycle operations */
+ .start = sl811h_start,
+ .stop = sl811h_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = sl811h_urb_enqueue,
+ .urb_dequeue = sl811h_urb_dequeue,
+ .endpoint_disable = sl811h_endpoint_disable,
+
+ /*
+ * periodic schedule support
+ */
+ .get_frame_number = sl811h_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = sl811h_hub_status_data,
+ .hub_control = sl811h_hub_control,
+ .hub_suspend = sl811h_hub_suspend,
+ .hub_resume = sl811h_hub_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init_or_module
+sl811h_remove(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ struct platform_device *pdev;
+ struct resource *res;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ remove_debug_file(sl811);
+ usb_remove_hcd(hcd);
+
+ iounmap(sl811->data_reg);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(res->start, 1);
+
+ iounmap(sl811->addr_reg);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, 1);
+
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+static int __init
+sl811h_probe(struct device *dev)
+{
+ struct usb_hcd *hcd;
+ struct sl811 *sl811;
+ struct platform_device *pdev;
+ struct resource *addr, *data;
+ int irq;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ int retval;
+ u8 tmp;
+
+ /* basic sanity checks first. board-specific init logic should
+ * have initialized these three resources and probably board
+ * specific platform_data. we don't probe for IRQs, and do only
+ * minimal sanity checking.
+ */
+ pdev = container_of(dev, struct platform_device, dev);
+ if (pdev->num_resources < 3)
+ return -ENODEV;
+
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq = platform_get_irq(pdev, 0);
+ if (!addr || !data || irq < 0)
+ return -ENODEV;
+
+ /* refuse to confuse usbcore */
+ if (dev->dma_mask) {
+ DBG("no we won't dma\n");
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(addr->start, 1, hcd_name)) {
+ retval = -EBUSY;
+ goto err1;
+ }
+ addr_reg = ioremap(addr->start, resource_len(addr));
+ if (addr_reg == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ if (!request_mem_region(data->start, 1, hcd_name)) {
+ retval = -EBUSY;
+ goto err3;
+ }
+ data_reg = ioremap(data->start, resource_len(addr));
+ if (data_reg == NULL) {
+ retval = -ENOMEM;
+ goto err4;
+ }
+
+ /* allocate and initialize hcd */
+ hcd = usb_create_hcd(&sl811h_hc_driver, dev, dev->bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err5;
+ }
+ hcd->rsrc_start = addr->start;
+ sl811 = hcd_to_sl811(hcd);
+
+ spin_lock_init(&sl811->lock);
+ INIT_LIST_HEAD(&sl811->async);
+ sl811->board = dev->platform_data;
+ init_timer(&sl811->timer);
+ sl811->timer.function = sl811h_timer;
+ sl811->timer.data = (unsigned long) sl811;
+ sl811->addr_reg = addr_reg;
+ sl811->data_reg = data_reg;
+
+ spin_lock_irq(&sl811->lock);
+ port_power(sl811, 0);
+ spin_unlock_irq(&sl811->lock);
+ msleep(200);
+
+ tmp = sl811_read(sl811, SL11H_HWREVREG);
+ switch (tmp >> 4) {
+ case 1:
+ hcd->product_desc = "SL811HS v1.2";
+ break;
+ case 2:
+ hcd->product_desc = "SL811HS v1.5";
+ break;
+ default:
+ /* reject case 0, SL11S is less functional */
+ DBG("chiprev %02x\n", tmp);
+ retval = -ENXIO;
+ goto err6;
+ }
+
+ /* sl811s would need a different handler for this irq */
+#ifdef CONFIG_ARM
+ /* Cypress docs say the IRQ is IRQT_HIGH ... */
+ set_irq_type(irq, IRQT_RISING);
+#endif
+ retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+ if (retval != 0)
+ goto err6;
+
+ create_debug_file(sl811);
+ return retval;
+
+ err6:
+ usb_put_hcd(hcd);
+ err5:
+ iounmap(data_reg);
+ err4:
+ release_mem_region(data->start, 1);
+ err3:
+ iounmap(addr_reg);
+ err2:
+ release_mem_region(addr->start, 1);
+ err1:
+ DBG("init error, %d\n", retval);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+
+/* for this device there's no useful distinction between the controller
+ * and its root hub, except that the root hub only gets direct PM calls
+ * when CONFIG_USB_SUSPEND is enabled.
+ */
+
+static int
+sl811h_suspend(struct device *dev, pm_message_t state, u32 phase)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+ int retval = 0;
+
+ if (phase != SUSPEND_POWER_DOWN)
+ return retval;
+
+ if (state <= PM_SUSPEND_MEM)
+ retval = sl811h_hub_suspend(hcd);
+ else
+ port_power(sl811, 0);
+ if (retval == 0)
+ dev->power.power_state = state;
+ return retval;
+}
+
+static int
+sl811h_resume(struct device *dev, u32 phase)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+ if (phase != RESUME_POWER_ON)
+ return 0;
+
+ /* with no "check to see if VBUS is still powered" board hook,
+ * let's assume it'd only be powered to enable remote wakeup.
+ */
+ if (dev->power.power_state > PM_SUSPEND_MEM
+ || !hcd->can_wakeup) {
+ sl811->port1 = 0;
+ port_power(sl811, 1);
+ return 0;
+ }
+
+ dev->power.power_state = PMSG_ON;
+ return sl811h_hub_resume(hcd);
+}
+
+#else
+
+#define sl811h_suspend NULL
+#define sl811h_resume NULL
+
+#endif
+
+
+static struct device_driver sl811h_driver = {
+ .name = (char *) hcd_name,
+ .bus = &platform_bus_type,
+
+ .probe = sl811h_probe,
+ .remove = sl811h_remove,
+
+ .suspend = sl811h_suspend,
+ .resume = sl811h_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init sl811h_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+ return driver_register(&sl811h_driver);
+}
+module_init(sl811h_init);
+
+static void __exit sl811h_cleanup(void)
+{
+ driver_unregister(&sl811h_driver);
+}
+module_exit(sl811h_cleanup);
diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
new file mode 100644
index 0000000..7690d98
--- /dev/null
+++ b/drivers/usb/host/sl811.h
@@ -0,0 +1,266 @@
+/*
+ * SL811HS register declarations and HCD data structures
+ *
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ * Copyright (C) 2001 Cypress Semiconductor Inc.
+ */
+
+/*
+ * SL811HS has transfer registers, and control registers. In host/master
+ * mode one set of registers is used; in peripheral/slave mode, another.
+ * - SL11H only has some "A" transfer registers from 0x00-0x04
+ * - SL811HS also has "B" registers from 0x08-0x0c
+ * - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
+ */
+
+#define SL811_EP_A(base) ((base) + 0)
+#define SL811_EP_B(base) ((base) + 8)
+
+#define SL811_HOST_BUF 0x00
+#define SL811_PERIPH_EP0 0x00
+#define SL811_PERIPH_EP1 0x10
+#define SL811_PERIPH_EP2 0x20
+#define SL811_PERIPH_EP3 0x30
+
+
+/* TRANSFER REGISTERS: host and peripheral sides are similar
+ * except for the control models (master vs slave).
+ */
+#define SL11H_HOSTCTLREG 0
+# define SL11H_HCTLMASK_ARM 0x01
+# define SL11H_HCTLMASK_ENABLE 0x02
+# define SL11H_HCTLMASK_IN 0x00
+# define SL11H_HCTLMASK_OUT 0x04
+# define SL11H_HCTLMASK_ISOCH 0x10
+# define SL11H_HCTLMASK_AFTERSOF 0x20
+# define SL11H_HCTLMASK_TOGGLE 0x40
+# define SL11H_HCTLMASK_PREAMBLE 0x80
+#define SL11H_BUFADDRREG 1
+#define SL11H_BUFLNTHREG 2
+#define SL11H_PKTSTATREG 3 /* read */
+# define SL11H_STATMASK_ACK 0x01
+# define SL11H_STATMASK_ERROR 0x02
+# define SL11H_STATMASK_TMOUT 0x04
+# define SL11H_STATMASK_SEQ 0x08
+# define SL11H_STATMASK_SETUP 0x10
+# define SL11H_STATMASK_OVF 0x20
+# define SL11H_STATMASK_NAK 0x40
+# define SL11H_STATMASK_STALL 0x80
+#define SL11H_PIDEPREG 3 /* write */
+# define SL_SETUP 0xd0
+# define SL_IN 0x90
+# define SL_OUT 0x10
+# define SL_SOF 0x50
+# define SL_PREAMBLE 0xc0
+# define SL_NAK 0xa0
+# define SL_STALL 0xe0
+# define SL_DATA0 0x30
+# define SL_DATA1 0xb0
+#define SL11H_XFERCNTREG 4 /* read */
+#define SL11H_DEVADDRREG 4 /* write */
+
+
+/* CONTROL REGISTERS: host and peripheral are very different.
+ */
+#define SL11H_CTLREG1 5
+# define SL11H_CTL1MASK_SOF_ENA 0x01
+# define SL11H_CTL1MASK_FORCE 0x18
+# define SL11H_CTL1MASK_NORMAL 0x00
+# define SL11H_CTL1MASK_SE0 0x08 /* reset */
+# define SL11H_CTL1MASK_J 0x10
+# define SL11H_CTL1MASK_K 0x18 /* resume */
+# define SL11H_CTL1MASK_LSPD 0x20
+# define SL11H_CTL1MASK_SUSPEND 0x40
+#define SL11H_IRQ_ENABLE 6
+# define SL11H_INTMASK_DONE_A 0x01
+# define SL11H_INTMASK_DONE_B 0x02
+# define SL11H_INTMASK_SOFINTR 0x10
+# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */
+# define SL11H_INTMASK_RD 0x40
+# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */
+#define SL11S_ADDRESS 7
+
+/* 0x08-0x0c are for the B buffer (not in SL11) */
+
+#define SL11H_IRQ_STATUS 0x0D /* write to ack */
+#define SL11H_HWREVREG 0x0E /* read */
+# define SL11H_HWRMASK_HWREV 0xF0
+#define SL11H_SOFLOWREG 0x0E /* write */
+#define SL11H_SOFTMRREG 0x0F /* read */
+
+/* a write to this register enables SL811HS features.
+ * HOST flag presumably overrides the chip input signal?
+ */
+#define SL811HS_CTLREG2 0x0F
+# define SL811HS_CTL2MASK_SOF_MASK 0x3F
+# define SL811HS_CTL2MASK_DSWAP 0x40
+# define SL811HS_CTL2MASK_HOST 0x80
+
+#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e)
+
+
+/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
+ * that's 240 bytes, which we'll split evenly between A and B sides.
+ * Only ISO can use more than 64 bytes per packet.
+ * (The SL11S has 0x40..0xff for buffers.)
+ */
+#define H_MAXPACKET 120 /* bytes in A or B fifos */
+
+#define SL11H_DATA_START 0x10
+#define SL811HS_PACKET_BUF(is_a) ((is_a) \
+ ? SL11H_DATA_START \
+ : (SL11H_DATA_START + H_MAXPACKET))
+
+/*-------------------------------------------------------------------------*/
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+struct sl811 {
+ spinlock_t lock;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ struct sl811_platform_data *board;
+ struct proc_dir_entry *pde;
+
+ unsigned long stat_insrmv;
+ unsigned long stat_wake;
+ unsigned long stat_sof;
+ unsigned long stat_a;
+ unsigned long stat_b;
+ unsigned long stat_lost;
+ unsigned long stat_overrun;
+
+ /* sw model */
+ struct timer_list timer;
+ struct sl811h_ep *next_periodic;
+ struct sl811h_ep *next_async;
+
+ struct sl811h_ep *active_a;
+ unsigned long jiffies_a;
+ struct sl811h_ep *active_b;
+ unsigned long jiffies_b;
+
+ u32 port1;
+ u8 ctrl1, ctrl2, irq_enable;
+ u16 frame;
+
+ /* async schedule: control, bulk */
+ struct list_head async;
+
+ /* periodic schedule: interrupt, iso */
+ u16 load[PERIODIC_SIZE];
+ struct sl811h_ep *periodic[PERIODIC_SIZE];
+ unsigned periodic_count;
+};
+
+static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
+{
+ return (struct sl811 *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *sl811_to_hcd(struct sl811 *sl811)
+{
+ return container_of((void *) sl811, struct usb_hcd, hcd_priv);
+}
+
+struct sl811h_ep {
+ struct usb_host_endpoint *hep;
+ struct usb_device *udev;
+
+ u8 defctrl;
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+
+ u16 error_count;
+ u16 nak_count;
+ u16 length; /* of current packet */
+
+ /* periodic schedule */
+ u16 period;
+ u16 branch;
+ u16 load;
+ struct sl811h_ep *next;
+
+ /* async schedule */
+ struct list_head schedule;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* These register utilities should work for the SL811S register API too
+ * NOTE: caller must hold sl811->lock.
+ */
+
+static inline u8 sl811_read(struct sl811 *sl811, int reg)
+{
+ writeb(reg, sl811->addr_reg);
+ return readb(sl811->data_reg);
+}
+
+static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
+{
+ writeb(reg, sl811->addr_reg);
+ writeb(val, sl811->data_reg);
+}
+
+static inline void
+sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
+{
+ const u8 *data;
+ void __iomem *data_reg;
+
+ if (!count)
+ return;
+ writeb(addr, sl811->addr_reg);
+
+ data = buf;
+ data_reg = sl811->data_reg;
+ do {
+ writeb(*data++, data_reg);
+ } while (--count);
+}
+
+static inline void
+sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
+{
+ u8 *data;
+ void __iomem *data_reg;
+
+ if (!count)
+ return;
+ writeb(addr, sl811->addr_reg);
+
+ data = buf;
+ data_reg = sl811->data_reg;
+ do {
+ *data++ = readb(data_reg);
+ } while (--count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff)
+#else
+#define DBG(stuff...) do{}while(0)
+#endif
+
+#ifdef VERBOSE
+# define VDBG DBG
+#else
+# define VDBG(stuff...) do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+# define PACKET VDBG
+#else
+# define PACKET(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff)
+#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff)
+
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
new file mode 100644
index 0000000..24c73c5
--- /dev/null
+++ b/drivers/usb/host/uhci-debug.c
@@ -0,0 +1,587 @@
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * Kernel visible pointers are surrounded in []'s and bus
+ * visible pointers are surrounded in ()'s
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2001 Johannes Erdfelt
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+
+#include "uhci-hcd.h"
+
+static struct dentry *uhci_debugfs_root = NULL;
+
+/* Handle REALLY large printk's so we don't overflow buffers */
+static inline void lprintk(char *buf)
+{
+ char *p;
+
+ /* Just write one line at a time */
+ while (buf) {
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+ printk(KERN_DEBUG "%s\n", buf);
+ buf = p;
+ if (buf)
+ buf++;
+ }
+}
+
+static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
+{
+ char *out = buf;
+ char *spid;
+ u32 status, token;
+
+ /* Try to make sure there's enough memory */
+ if (len < 160)
+ return 0;
+
+ status = td_status(td);
+ out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
+ out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+ ((status >> 27) & 3),
+ (status & TD_CTRL_SPD) ? "SPD " : "",
+ (status & TD_CTRL_LS) ? "LS " : "",
+ (status & TD_CTRL_IOC) ? "IOC " : "",
+ (status & TD_CTRL_ACTIVE) ? "Active " : "",
+ (status & TD_CTRL_STALLED) ? "Stalled " : "",
+ (status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
+ (status & TD_CTRL_BABBLE) ? "Babble " : "",
+ (status & TD_CTRL_NAK) ? "NAK " : "",
+ (status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+ (status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
+ status & 0x7ff);
+
+ token = td_token(td);
+ switch (uhci_packetid(token)) {
+ case USB_PID_SETUP:
+ spid = "SETUP";
+ break;
+ case USB_PID_OUT:
+ spid = "OUT";
+ break;
+ case USB_PID_IN:
+ spid = "IN";
+ break;
+ default:
+ spid = "?";
+ break;
+ }
+
+ out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
+ token >> 21,
+ ((token >> 19) & 1),
+ (token >> 15) & 15,
+ (token >> 8) & 127,
+ (token & 0xff),
+ spid);
+ out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
+
+ return out - buf;
+}
+
+static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+{
+ char *out = buf;
+ struct urb_priv *urbp;
+ struct list_head *head, *tmp;
+ struct uhci_td *td;
+ int i = 0, checked = 0, prevactive = 0;
+ __le32 element = qh_element(qh);
+
+ /* Try to make sure there's enough memory */
+ if (len < 80 * 6)
+ return 0;
+
+ out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
+ qh, le32_to_cpu(qh->link), le32_to_cpu(element));
+
+ if (element & UHCI_PTR_QH)
+ out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
+
+ if (element & UHCI_PTR_DEPTH)
+ out += sprintf(out, "%*s Depth traverse\n", space, "");
+
+ if (element & cpu_to_le32(8))
+ out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, "");
+
+ if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
+ out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
+
+ if (!qh->urbp) {
+ out += sprintf(out, "%*s urbp == NULL\n", space, "");
+ goto out;
+ }
+
+ urbp = qh->urbp;
+
+ head = &urbp->td_list;
+ tmp = head->next;
+
+ td = list_entry(tmp, struct uhci_td, list);
+
+ if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
+ out += sprintf(out, "%*s Element != First TD\n", space, "");
+
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
+ out += sprintf(out, "%*s%d: ", space + 2, "", i++);
+ out += uhci_show_td(td, out, len - (out - buf), 0);
+
+ if (i > 10 && !checked && prevactive && tmp != head &&
+ debug <= 2) {
+ struct list_head *ntmp = tmp;
+ struct uhci_td *ntd = td;
+ int active = 1, ni = i;
+
+ checked = 1;
+
+ while (ntmp != head && ntmp->next != head && active) {
+ ntd = list_entry(ntmp, struct uhci_td, list);
+
+ ntmp = ntmp->next;
+
+ active = td_status(ntd) & TD_CTRL_ACTIVE;
+
+ ni++;
+ }
+
+ if (active && ni > i) {
+ out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
+ tmp = ntmp;
+ td = ntd;
+ i = ni;
+ }
+ }
+
+ prevactive = td_status(td) & TD_CTRL_ACTIVE;
+ }
+
+ if (list_empty(&urbp->queue_list) || urbp->queued)
+ goto out;
+
+ out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
+
+ head = &urbp->queue_list;
+ tmp = head->next;
+
+ while (tmp != head) {
+ struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
+ queue_list);
+ tmp = tmp->next;
+
+ out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
+ }
+
+out:
+ return out - buf;
+}
+
+#define show_frame_num() \
+ if (!shown) { \
+ shown = 1; \
+ out += sprintf(out, "- Frame %d\n", i); \
+ }
+
+#ifdef CONFIG_PROC_FS
+static const char *qh_names[] = {
+ "skel_int128_qh", "skel_int64_qh",
+ "skel_int32_qh", "skel_int16_qh",
+ "skel_int8_qh", "skel_int4_qh",
+ "skel_int2_qh", "skel_int1_qh",
+ "skel_ls_control_qh", "skel_fs_control_qh",
+ "skel_bulk_qh", "skel_term_qh"
+};
+
+#define show_qh_name() \
+ if (!shown) { \
+ shown = 1; \
+ out += sprintf(out, "- %s\n", qh_names[i]); \
+ }
+
+static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
+{
+ char *out = buf;
+
+ /* Try to make sure there's enough memory */
+ if (len < 160)
+ return 0;
+
+ out += sprintf(out, " stat%d = %04x %s%s%s%s%s%s%s%s%s%s\n",
+ port,
+ status,
+ (status & USBPORTSC_SUSP) ? " Suspend" : "",
+ (status & USBPORTSC_OCC) ? " OverCurrentChange" : "",
+ (status & USBPORTSC_OC) ? " OverCurrent" : "",
+ (status & USBPORTSC_PR) ? " Reset" : "",
+ (status & USBPORTSC_LSDA) ? " LowSpeed" : "",
+ (status & USBPORTSC_RD) ? " ResumeDetect" : "",
+ (status & USBPORTSC_PEC) ? " EnableChange" : "",
+ (status & USBPORTSC_PE) ? " Enabled" : "",
+ (status & USBPORTSC_CSC) ? " ConnectChange" : "",
+ (status & USBPORTSC_CCS) ? " Connected" : "");
+
+ return out - buf;
+}
+
+static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
+{
+ char *out = buf;
+ unsigned long io_addr = uhci->io_addr;
+ unsigned short usbcmd, usbstat, usbint, usbfrnum;
+ unsigned int flbaseadd;
+ unsigned char sof;
+ unsigned short portsc1, portsc2;
+
+ /* Try to make sure there's enough memory */
+ if (len < 80 * 6)
+ return 0;
+
+ usbcmd = inw(io_addr + 0);
+ usbstat = inw(io_addr + 2);
+ usbint = inw(io_addr + 4);
+ usbfrnum = inw(io_addr + 6);
+ flbaseadd = inl(io_addr + 8);
+ sof = inb(io_addr + 12);
+ portsc1 = inw(io_addr + 16);
+ portsc2 = inw(io_addr + 18);
+
+ out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n",
+ usbcmd,
+ (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
+ (usbcmd & USBCMD_CF) ? "CF " : "",
+ (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
+ (usbcmd & USBCMD_FGR) ? "FGR " : "",
+ (usbcmd & USBCMD_EGSM) ? "EGSM " : "",
+ (usbcmd & USBCMD_GRESET) ? "GRESET " : "",
+ (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+ (usbcmd & USBCMD_RS) ? "RS " : "");
+
+ out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n",
+ usbstat,
+ (usbstat & USBSTS_HCH) ? "HCHalted " : "",
+ (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
+ (usbstat & USBSTS_HSE) ? "HostSystemError " : "",
+ (usbstat & USBSTS_RD) ? "ResumeDetect " : "",
+ (usbstat & USBSTS_ERROR) ? "USBError " : "",
+ (usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+ out += sprintf(out, " usbint = %04x\n", usbint);
+ out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1,
+ 0xfff & (4*(unsigned int)usbfrnum));
+ out += sprintf(out, " flbaseadd = %08x\n", flbaseadd);
+ out += sprintf(out, " sof = %02x\n", sof);
+ out += uhci_show_sc(1, portsc1, out, len - (out - buf));
+ out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+
+ return out - buf;
+}
+
+static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
+{
+ struct list_head *tmp;
+ char *out = buf;
+ int count = 0;
+
+ if (len < 200)
+ return 0;
+
+ out += sprintf(out, "urb_priv [%p] ", urbp);
+ out += sprintf(out, "urb [%p] ", urbp->urb);
+ out += sprintf(out, "qh [%p] ", urbp->qh);
+ out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
+ out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
+
+ switch (usb_pipetype(urbp->urb->pipe)) {
+ case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break;
+ case PIPE_INTERRUPT: out += sprintf(out, "INT "); break;
+ case PIPE_BULK: out += sprintf(out, "BLK "); break;
+ case PIPE_CONTROL: out += sprintf(out, "CTL "); break;
+ }
+
+ out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : ""));
+ out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));
+
+ if (urbp->urb->status != -EINPROGRESS)
+ out += sprintf(out, "Status=%d ", urbp->urb->status);
+ //out += sprintf(out, "Inserttime=%lx ",urbp->inserttime);
+ //out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);
+
+ count = 0;
+ list_for_each(tmp, &urbp->td_list)
+ count++;
+ out += sprintf(out, "TDs=%d ",count);
+
+ if (urbp->queued)
+ out += sprintf(out, "queued\n");
+ else {
+ count = 0;
+ list_for_each(tmp, &urbp->queue_list)
+ count++;
+ out += sprintf(out, "queued URBs=%d\n", count);
+ }
+
+ return out - buf;
+}
+
+static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
+{
+ char *out = buf;
+ struct list_head *head, *tmp;
+ int count;
+
+ out += sprintf(out, "Main list URBs:");
+ if (list_empty(&uhci->urb_list))
+ out += sprintf(out, " Empty\n");
+ else {
+ out += sprintf(out, "\n");
+ count = 0;
+ head = &uhci->urb_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+
+ out += sprintf(out, " %d: ", ++count);
+ out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
+ tmp = tmp->next;
+ }
+ }
+
+ out += sprintf(out, "Remove list URBs:");
+ if (list_empty(&uhci->urb_remove_list))
+ out += sprintf(out, " Empty\n");
+ else {
+ out += sprintf(out, "\n");
+ count = 0;
+ head = &uhci->urb_remove_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+
+ out += sprintf(out, " %d: ", ++count);
+ out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
+ tmp = tmp->next;
+ }
+ }
+
+ out += sprintf(out, "Complete list URBs:");
+ if (list_empty(&uhci->complete_list))
+ out += sprintf(out, " Empty\n");
+ else {
+ out += sprintf(out, "\n");
+ count = 0;
+ head = &uhci->complete_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+
+ out += sprintf(out, " %d: ", ++count);
+ out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
+ tmp = tmp->next;
+ }
+ }
+
+ return out - buf;
+}
+
+static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
+{
+ unsigned long flags;
+ char *out = buf;
+ int i, j;
+ struct uhci_qh *qh;
+ struct uhci_td *td;
+ struct list_head *tmp, *head;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+
+ out += sprintf(out, "HC status\n");
+ out += uhci_show_status(uhci, out, len - (out - buf));
+
+ out += sprintf(out, "Frame List\n");
+ for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+ int shown = 0;
+ td = uhci->fl->frame_cpu[i];
+ if (!td)
+ continue;
+
+ if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
+ show_frame_num();
+ out += sprintf(out, " frame list does not match td->dma_handle!\n");
+ }
+ show_frame_num();
+
+ head = &td->fl_list;
+ tmp = head;
+ do {
+ td = list_entry(tmp, struct uhci_td, fl_list);
+ tmp = tmp->next;
+ out += uhci_show_td(td, out, len - (out - buf), 4);
+ } while (tmp != head);
+ }
+
+ out += sprintf(out, "Skeleton QH's\n");
+
+ for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
+ int shown = 0;
+
+ qh = uhci->skelqh[i];
+
+ if (debug > 1) {
+ show_qh_name();
+ out += uhci_show_qh(qh, out, len - (out - buf), 4);
+ }
+
+ /* Last QH is the Terminating QH, it's different */
+ if (i == UHCI_NUM_SKELQH - 1) {
+ if (qh->link != UHCI_PTR_TERM)
+ out += sprintf(out, " bandwidth reclamation on!\n");
+
+ if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
+ out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
+
+ continue;
+ }
+
+ j = (i < 7) ? 7 : i+1; /* Next skeleton */
+ if (list_empty(&qh->list)) {
+ if (i < UHCI_NUM_SKELQH - 1) {
+ if (qh->link !=
+ (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) {
+ show_qh_name();
+ out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n");
+ }
+ }
+
+ continue;
+ }
+
+ show_qh_name();
+
+ head = &qh->list;
+ tmp = head->next;
+
+ while (tmp != head) {
+ qh = list_entry(tmp, struct uhci_qh, list);
+
+ tmp = tmp->next;
+
+ out += uhci_show_qh(qh, out, len - (out - buf), 4);
+ }
+
+ if (i < UHCI_NUM_SKELQH - 1) {
+ if (qh->link !=
+ (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
+ out += sprintf(out, " last QH not linked to next skeleton!\n");
+ }
+ }
+
+ if (debug > 2)
+ out += uhci_show_lists(uhci, out, len - (out - buf));
+
+ spin_unlock_irqrestore(&uhci->lock, flags);
+
+ return out - buf;
+}
+
+#define MAX_OUTPUT (64 * 1024)
+
+struct uhci_debug {
+ int size;
+ char *data;
+ struct uhci_hcd *uhci;
+};
+
+static int uhci_debug_open(struct inode *inode, struct file *file)
+{
+ struct uhci_hcd *uhci = inode->u.generic_ip;
+ struct uhci_debug *up;
+ int ret = -ENOMEM;
+
+ lock_kernel();
+ up = kmalloc(sizeof(*up), GFP_KERNEL);
+ if (!up)
+ goto out;
+
+ up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
+ if (!up->data) {
+ kfree(up);
+ goto out;
+ }
+
+ up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+
+ file->private_data = up;
+
+ ret = 0;
+out:
+ unlock_kernel();
+ return ret;
+}
+
+static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
+{
+ struct uhci_debug *up;
+ loff_t new = -1;
+
+ lock_kernel();
+ up = file->private_data;
+
+ switch (whence) {
+ case 0:
+ new = off;
+ break;
+ case 1:
+ new = file->f_pos + off;
+ break;
+ }
+ if (new < 0 || new > up->size) {
+ unlock_kernel();
+ return -EINVAL;
+ }
+ unlock_kernel();
+ return (file->f_pos = new);
+}
+
+static ssize_t uhci_debug_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct uhci_debug *up = file->private_data;
+ return simple_read_from_buffer(buf, nbytes, ppos, up->data, up->size);
+}
+
+static int uhci_debug_release(struct inode *inode, struct file *file)
+{
+ struct uhci_debug *up = file->private_data;
+
+ kfree(up->data);
+ kfree(up);
+
+ return 0;
+}
+
+static struct file_operations uhci_debug_operations = {
+ .open = uhci_debug_open,
+ .llseek = uhci_debug_lseek,
+ .read = uhci_debug_read,
+ .release = uhci_debug_release,
+};
+
+#else /* CONFIG_DEBUG_FS */
+
+#define uhci_debug_operations (* (struct file_operations *) NULL)
+
+#endif
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
new file mode 100644
index 0000000..324a1a9
--- /dev/null
+++ b/drivers/usb/host/uhci-hcd.c
@@ -0,0 +1,919 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ * - the low-level protocol (fairly simple but lots of small details)
+ * - working around the horridness of the rest
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#else
+#undef DEBUG
+#endif
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.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/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/pm.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include <linux/bitops.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "../core/hcd.h"
+#include "uhci-hcd.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v2.2"
+#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
+Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
+Alan Stern"
+#define DRIVER_DESC "USB Universal Host Controller Interface driver"
+
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URB's except for stalls
+ * debug = 2, dump all failed URB's (including stalls)
+ * show all queues in /debug/uhci/[pci_addr]
+ * debug = 3, show all TD's in URB's when dumping
+ */
+#ifdef DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+#define ERRBUF_LEN (32 * 1024)
+
+static kmem_cache_t *uhci_up_cachep; /* urb_priv */
+
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
+static void hc_state_transitions(struct uhci_hcd *uhci);
+
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT msecs_to_jiffies(50)
+#define FSBR_DELAY msecs_to_jiffies(50)
+
+/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+/* depth first traversal. We'll do it in groups of this number of TD's */
+/* to make sure it doesn't hog all of the bandwidth */
+#define DEPTH_INTERVAL 5
+
+#include "uhci-hub.c"
+#include "uhci-debug.c"
+#include "uhci-q.c"
+
+static int init_stall_timer(struct usb_hcd *hcd);
+
+static void stall_callback(unsigned long ptr)
+{
+ struct usb_hcd *hcd = (struct usb_hcd *)ptr;
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ struct urb_priv *up;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ uhci_scan_schedule(uhci, NULL);
+
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ struct urb *u = up->urb;
+
+ spin_lock(&u->lock);
+
+ /* Check if the FSBR timed out */
+ if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
+ uhci_fsbr_timeout(uhci, u);
+
+ spin_unlock(&u->lock);
+ }
+
+ /* Really disable FSBR */
+ if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
+ uhci->fsbrtimeout = 0;
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
+ }
+
+ /* Poll for and perform state transitions */
+ hc_state_transitions(uhci);
+ if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
+ uhci_check_ports(uhci);
+
+ init_stall_timer(hcd);
+ spin_unlock_irqrestore(&uhci->lock, flags);
+}
+
+static int init_stall_timer(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ init_timer(&uhci->stall_timer);
+ uhci->stall_timer.function = stall_callback;
+ uhci->stall_timer.data = (unsigned long)hcd;
+ uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
+ add_timer(&uhci->stall_timer);
+
+ return 0;
+}
+
+static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long io_addr = uhci->io_addr;
+ unsigned short status;
+
+ /*
+ * Read the interrupt status, and write it back to clear the
+ * interrupt cause. Contrary to the UHCI specification, the
+ * "HC Halted" status bit is persistent: it is RO, not R/WC.
+ */
+ status = inw(io_addr + USBSTS);
+ if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */
+ return IRQ_NONE;
+ outw(status, io_addr + USBSTS); /* Clear it */
+
+ if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
+ if (status & USBSTS_HSE)
+ dev_err(uhci_dev(uhci), "host system error, "
+ "PCI problems?\n");
+ if (status & USBSTS_HCPE)
+ dev_err(uhci_dev(uhci), "host controller process "
+ "error, something bad happened!\n");
+ if ((status & USBSTS_HCH) && uhci->state > 0) {
+ dev_err(uhci_dev(uhci), "host controller halted, "
+ "very bad!\n");
+ /* FIXME: Reset the controller, fix the offending TD */
+ }
+ }
+
+ if (status & USBSTS_RD)
+ uhci->resume_detect = 1;
+
+ spin_lock(&uhci->lock);
+ uhci_scan_schedule(uhci, regs);
+ spin_unlock(&uhci->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void reset_hc(struct uhci_hcd *uhci)
+{
+ unsigned long io_addr = uhci->io_addr;
+
+ /* Turn off PIRQ, SMI, and all interrupts. This also turns off
+ * the BIOS's USB Legacy Support.
+ */
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
+ outw(0, uhci->io_addr + USBINTR);
+
+ /* Global reset for 50ms */
+ uhci->state = UHCI_RESET;
+ outw(USBCMD_GRESET, io_addr + USBCMD);
+ msleep(50);
+ outw(0, io_addr + USBCMD);
+
+ /* Another 10ms delay */
+ msleep(10);
+ uhci->resume_detect = 0;
+ uhci->is_stopped = UHCI_IS_STOPPED;
+}
+
+static void suspend_hc(struct uhci_hcd *uhci)
+{
+ unsigned long io_addr = uhci->io_addr;
+
+ dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
+ uhci->state = UHCI_SUSPENDED;
+ uhci->resume_detect = 0;
+ outw(USBCMD_EGSM, io_addr + USBCMD);
+
+ /* FIXME: Wait for the controller to actually stop */
+ uhci_get_current_frame_number(uhci);
+ uhci->is_stopped = UHCI_IS_STOPPED;
+
+ uhci_scan_schedule(uhci, NULL);
+}
+
+static void wakeup_hc(struct uhci_hcd *uhci)
+{
+ unsigned long io_addr = uhci->io_addr;
+
+ switch (uhci->state) {
+ case UHCI_SUSPENDED: /* Start the resume */
+ dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
+
+ /* Global resume for >= 20ms */
+ outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
+ uhci->state = UHCI_RESUMING_1;
+ uhci->state_end = jiffies + msecs_to_jiffies(20);
+ uhci->is_stopped = 0;
+ break;
+
+ case UHCI_RESUMING_1: /* End global resume */
+ uhci->state = UHCI_RESUMING_2;
+ outw(0, io_addr + USBCMD);
+ /* Falls through */
+
+ case UHCI_RESUMING_2: /* Wait for EOP to be sent */
+ if (inw(io_addr + USBCMD) & USBCMD_FGR)
+ break;
+
+ /* Run for at least 1 second, and
+ * mark it configured with a 64-byte max packet */
+ uhci->state = UHCI_RUNNING_GRACE;
+ uhci->state_end = jiffies + HZ;
+ outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP,
+ io_addr + USBCMD);
+ break;
+
+ case UHCI_RUNNING_GRACE: /* Now allowed to suspend */
+ uhci->state = UHCI_RUNNING;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int ports_active(struct uhci_hcd *uhci)
+{
+ unsigned long io_addr = uhci->io_addr;
+ int connection = 0;
+ int i;
+
+ for (i = 0; i < uhci->rh_numports; i++)
+ connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
+
+ return connection;
+}
+
+static int suspend_allowed(struct uhci_hcd *uhci)
+{
+ unsigned long io_addr = uhci->io_addr;
+ int i;
+
+ if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
+ return 1;
+
+ /* Some of Intel's USB controllers have a bug that causes false
+ * resume indications if any port has an over current condition.
+ * To prevent problems, we will not allow a global suspend if
+ * any ports are OC.
+ *
+ * Some motherboards using Intel's chipsets (but not using all
+ * the USB ports) appear to hardwire the over current inputs active
+ * to disable the USB ports.
+ */
+
+ /* check for over current condition on any port */
+ for (i = 0; i < uhci->rh_numports; i++) {
+ if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
+ return 0;
+ }
+
+ return 1;
+}
+
+static void hc_state_transitions(struct uhci_hcd *uhci)
+{
+ switch (uhci->state) {
+ case UHCI_RUNNING:
+
+ /* global suspend if nothing connected for 1 second */
+ if (!ports_active(uhci) && suspend_allowed(uhci)) {
+ uhci->state = UHCI_SUSPENDING_GRACE;
+ uhci->state_end = jiffies + HZ;
+ }
+ break;
+
+ case UHCI_SUSPENDING_GRACE:
+ if (ports_active(uhci))
+ uhci->state = UHCI_RUNNING;
+ else if (time_after_eq(jiffies, uhci->state_end))
+ suspend_hc(uhci);
+ break;
+
+ case UHCI_SUSPENDED:
+
+ /* wakeup if requested by a device */
+ if (uhci->resume_detect)
+ wakeup_hc(uhci);
+ break;
+
+ case UHCI_RESUMING_1:
+ case UHCI_RESUMING_2:
+ case UHCI_RUNNING_GRACE:
+ if (time_after_eq(jiffies, uhci->state_end))
+ wakeup_hc(uhci);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Store the current frame number in uhci->frame_number if the controller
+ * is runnning
+ */
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
+{
+ if (!uhci->is_stopped)
+ uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
+}
+
+static int start_hc(struct uhci_hcd *uhci)
+{
+ unsigned long io_addr = uhci->io_addr;
+ int timeout = 10;
+
+ /*
+ * Reset the HC - this will force us to get a
+ * new notification of any already connected
+ * ports due to the virtual disconnect that it
+ * implies.
+ */
+ outw(USBCMD_HCRESET, io_addr + USBCMD);
+ while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
+ if (--timeout < 0) {
+ dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ /* Mark controller as running before we enable interrupts */
+ uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
+
+ /* Turn on PIRQ and all interrupts */
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+ USBLEGSUP_DEFAULT);
+ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+ io_addr + USBINTR);
+
+ /* Start at frame 0 */
+ outw(0, io_addr + USBFRNUM);
+ outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
+
+ /* Run and mark it configured with a 64-byte max packet */
+ uhci->state = UHCI_RUNNING_GRACE;
+ uhci->state_end = jiffies + HZ;
+ outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+ uhci->is_stopped = 0;
+
+ return 0;
+}
+
+/*
+ * De-allocate all resources
+ */
+static void release_uhci(struct uhci_hcd *uhci)
+{
+ int i;
+
+ for (i = 0; i < UHCI_NUM_SKELQH; i++)
+ if (uhci->skelqh[i]) {
+ uhci_free_qh(uhci, uhci->skelqh[i]);
+ uhci->skelqh[i] = NULL;
+ }
+
+ if (uhci->term_td) {
+ uhci_free_td(uhci, uhci->term_td);
+ uhci->term_td = NULL;
+ }
+
+ if (uhci->qh_pool) {
+ dma_pool_destroy(uhci->qh_pool);
+ uhci->qh_pool = NULL;
+ }
+
+ if (uhci->td_pool) {
+ dma_pool_destroy(uhci->td_pool);
+ uhci->td_pool = NULL;
+ }
+
+ if (uhci->fl) {
+ dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
+ uhci->fl, uhci->fl->dma_handle);
+ uhci->fl = NULL;
+ }
+
+ if (uhci->dentry) {
+ debugfs_remove(uhci->dentry);
+ uhci->dentry = NULL;
+ }
+}
+
+static int uhci_reset(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ uhci->io_addr = (unsigned long) hcd->rsrc_start;
+
+ /* Kick BIOS off this hardware and reset, so we won't get
+ * interrupts from any previous setup.
+ */
+ reset_hc(uhci);
+ return 0;
+}
+
+/*
+ * Allocate a frame list, and then setup the skeleton
+ *
+ * The hardware doesn't really know any difference
+ * in the queues, but the order does matter for the
+ * protocols higher up. The order is:
+ *
+ * - any isochronous events handled before any
+ * of the queues. We don't do that here, because
+ * we'll create the actual TD entries on demand.
+ * - The first queue is the interrupt queue.
+ * - The second queue is the control queue, split into low- and full-speed
+ * - The third queue is bulk queue.
+ * - The fourth queue is the bandwidth reclamation queue, which loops back
+ * to the full-speed control queue.
+ */
+static int uhci_start(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int retval = -EBUSY;
+ int i, port;
+ unsigned io_size;
+ dma_addr_t dma_handle;
+ struct usb_device *udev;
+ struct dentry *dentry;
+
+ io_size = (unsigned) hcd->rsrc_len;
+
+ dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, &uhci_debug_operations);
+ if (!dentry) {
+ dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
+ retval = -ENOMEM;
+ goto err_create_debug_entry;
+ }
+ uhci->dentry = dentry;
+
+ uhci->fsbr = 0;
+ uhci->fsbrtimeout = 0;
+
+ spin_lock_init(&uhci->lock);
+ INIT_LIST_HEAD(&uhci->qh_remove_list);
+
+ INIT_LIST_HEAD(&uhci->td_remove_list);
+
+ INIT_LIST_HEAD(&uhci->urb_remove_list);
+
+ INIT_LIST_HEAD(&uhci->urb_list);
+
+ INIT_LIST_HEAD(&uhci->complete_list);
+
+ init_waitqueue_head(&uhci->waitqh);
+
+ uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
+ &dma_handle, 0);
+ if (!uhci->fl) {
+ dev_err(uhci_dev(uhci), "unable to allocate "
+ "consistent memory for frame list\n");
+ goto err_alloc_fl;
+ }
+
+ memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
+
+ uhci->fl->dma_handle = dma_handle;
+
+ uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
+ sizeof(struct uhci_td), 16, 0);
+ if (!uhci->td_pool) {
+ dev_err(uhci_dev(uhci), "unable to create td dma_pool\n");
+ goto err_create_td_pool;
+ }
+
+ uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),
+ sizeof(struct uhci_qh), 16, 0);
+ if (!uhci->qh_pool) {
+ dev_err(uhci_dev(uhci), "unable to create qh dma_pool\n");
+ goto err_create_qh_pool;
+ }
+
+ /* Initialize the root hub */
+
+ /* UHCI specs says devices must have 2 ports, but goes on to say */
+ /* they may have more but give no way to determine how many they */
+ /* have. However, according to the UHCI spec, Bit 7 is always set */
+ /* to 1. So we try to use this to our advantage */
+ for (port = 0; port < (io_size - 0x10) / 2; port++) {
+ unsigned int portstatus;
+
+ portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
+ if (!(portstatus & 0x0080))
+ break;
+ }
+ if (debug)
+ dev_info(uhci_dev(uhci), "detected %d ports\n", port);
+
+ /* This is experimental so anything less than 2 or greater than 8 is */
+ /* something weird and we'll ignore it */
+ if (port < 2 || port > UHCI_RH_MAXCHILD) {
+ dev_info(uhci_dev(uhci), "port count misdetected? "
+ "forcing to 2 ports\n");
+ port = 2;
+ }
+
+ uhci->rh_numports = port;
+
+ udev = usb_alloc_dev(NULL, &hcd->self, 0);
+ if (!udev) {
+ dev_err(uhci_dev(uhci), "unable to allocate root hub\n");
+ goto err_alloc_root_hub;
+ }
+
+ uhci->term_td = uhci_alloc_td(uhci, udev);
+ if (!uhci->term_td) {
+ dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");
+ goto err_alloc_term_td;
+ }
+
+ for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+ uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
+ if (!uhci->skelqh[i]) {
+ dev_err(uhci_dev(uhci), "unable to allocate QH\n");
+ goto err_alloc_skelqh;
+ }
+ }
+
+ /*
+ * 8 Interrupt queues; link all higher int queues to int1,
+ * then link int1 to control and control to bulk
+ */
+ uhci->skel_int128_qh->link =
+ uhci->skel_int64_qh->link =
+ uhci->skel_int32_qh->link =
+ uhci->skel_int16_qh->link =
+ uhci->skel_int8_qh->link =
+ uhci->skel_int4_qh->link =
+ uhci->skel_int2_qh->link =
+ cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
+ uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
+
+ uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+ uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
+ uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
+
+ /* This dummy TD is to work around a bug in Intel PIIX controllers */
+ uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
+ (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+ uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
+
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
+ uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
+
+ /*
+ * Fill the frame list: make all entries point to the proper
+ * interrupt queue.
+ *
+ * The interrupt queues will be interleaved as evenly as possible.
+ * There's not much to be done about period-1 interrupts; they have
+ * to occur in every frame. But we can schedule period-2 interrupts
+ * in odd-numbered frames, period-4 interrupts in frames congruent
+ * to 2 (mod 4), and so on. This way each frame only has two
+ * interrupt QHs, which will help spread out bandwidth utilization.
+ */
+ for (i = 0; i < UHCI_NUMFRAMES; i++) {
+ int irq;
+
+ /*
+ * ffs (Find First bit Set) does exactly what we need:
+ * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[6],
+ * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc.
+ * ffs > 6 => not on any high-period queue, so use
+ * skel_int1_qh = skelqh[7].
+ * Add UHCI_NUMFRAMES to insure at least one bit is set.
+ */
+ irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES);
+ if (irq < 0)
+ irq = 7;
+
+ /* Only place we don't use the frame list routines */
+ uhci->fl->frame[i] = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skelqh[irq]->dma_handle);
+ }
+
+ /*
+ * Some architectures require a full mb() to enforce completion of
+ * the memory writes above before the I/O transfers in start_hc().
+ */
+ mb();
+ if ((retval = start_hc(uhci)) != 0)
+ goto err_alloc_skelqh;
+
+ init_stall_timer(hcd);
+
+ udev->speed = USB_SPEED_FULL;
+
+ if (usb_hcd_register_root_hub(udev, hcd) != 0) {
+ dev_err(uhci_dev(uhci), "unable to start root hub\n");
+ retval = -ENOMEM;
+ goto err_start_root_hub;
+ }
+
+ return 0;
+
+/*
+ * error exits:
+ */
+err_start_root_hub:
+ reset_hc(uhci);
+
+ del_timer_sync(&uhci->stall_timer);
+
+err_alloc_skelqh:
+ for (i = 0; i < UHCI_NUM_SKELQH; i++)
+ if (uhci->skelqh[i]) {
+ uhci_free_qh(uhci, uhci->skelqh[i]);
+ uhci->skelqh[i] = NULL;
+ }
+
+ uhci_free_td(uhci, uhci->term_td);
+ uhci->term_td = NULL;
+
+err_alloc_term_td:
+ usb_put_dev(udev);
+
+err_alloc_root_hub:
+ dma_pool_destroy(uhci->qh_pool);
+ uhci->qh_pool = NULL;
+
+err_create_qh_pool:
+ dma_pool_destroy(uhci->td_pool);
+ uhci->td_pool = NULL;
+
+err_create_td_pool:
+ dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
+ uhci->fl, uhci->fl->dma_handle);
+ uhci->fl = NULL;
+
+err_alloc_fl:
+ debugfs_remove(uhci->dentry);
+ uhci->dentry = NULL;
+
+err_create_debug_entry:
+ return retval;
+}
+
+static void uhci_stop(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ del_timer_sync(&uhci->stall_timer);
+ reset_hc(uhci);
+
+ spin_lock_irq(&uhci->lock);
+ uhci_scan_schedule(uhci, NULL);
+ spin_unlock_irq(&uhci->lock);
+
+ release_uhci(uhci);
+}
+
+#ifdef CONFIG_PM
+static int uhci_suspend(struct usb_hcd *hcd, u32 state)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ spin_lock_irq(&uhci->lock);
+
+ /* Don't try to suspend broken motherboards, reset instead */
+ if (suspend_allowed(uhci))
+ suspend_hc(uhci);
+ else {
+ spin_unlock_irq(&uhci->lock);
+ reset_hc(uhci);
+ spin_lock_irq(&uhci->lock);
+ uhci_scan_schedule(uhci, NULL);
+ }
+
+ spin_unlock_irq(&uhci->lock);
+ return 0;
+}
+
+static int uhci_resume(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int rc;
+
+ pci_set_master(to_pci_dev(uhci_dev(uhci)));
+
+ spin_lock_irq(&uhci->lock);
+
+ if (uhci->state == UHCI_SUSPENDED) {
+
+ /*
+ * Some systems don't maintain the UHCI register values
+ * during a PM suspend/resume cycle, so reinitialize
+ * the Frame Number, Framelist Base Address, Interrupt
+ * Enable, and Legacy Support registers.
+ */
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+ 0);
+ outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
+ outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
+ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
+ USBINTR_SP, uhci->io_addr + USBINTR);
+ uhci->resume_detect = 1;
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+ USBLEGSUP_DEFAULT);
+ } else {
+ spin_unlock_irq(&uhci->lock);
+ reset_hc(uhci);
+ if ((rc = start_hc(uhci)) != 0)
+ return rc;
+ spin_lock_irq(&uhci->lock);
+ }
+ hcd->state = HC_STATE_RUNNING;
+
+ spin_unlock_irq(&uhci->lock);
+ return 0;
+}
+#endif
+
+/* Wait until all the URBs for a particular device/endpoint are gone */
+static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list));
+}
+
+static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int frame_number;
+ unsigned long flags;
+
+ /* Minimize latency by avoiding the spinlock */
+ local_irq_save(flags);
+ rmb();
+ frame_number = (uhci->is_stopped ? uhci->frame_number :
+ inw(uhci->io_addr + USBFRNUM));
+ local_irq_restore(flags);
+ return frame_number;
+}
+
+static const char hcd_name[] = "uhci_hcd";
+
+static const struct hc_driver uhci_driver = {
+ .description = hcd_name,
+ .product_desc = "UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
+
+ /* Generic hardware linkage */
+ .irq = uhci_irq,
+ .flags = HCD_USB11,
+
+ /* Basic lifecycle operations */
+ .reset = uhci_reset,
+ .start = uhci_start,
+#ifdef CONFIG_PM
+ .suspend = uhci_suspend,
+ .resume = uhci_resume,
+#endif
+ .stop = uhci_stop,
+
+ .urb_enqueue = uhci_urb_enqueue,
+ .urb_dequeue = uhci_urb_dequeue,
+
+ .endpoint_disable = uhci_hcd_endpoint_disable,
+ .get_frame_number = uhci_hcd_get_frame_number,
+
+ .hub_status_data = uhci_hub_status_data,
+ .hub_control = uhci_hub_control,
+};
+
+static const struct pci_device_id uhci_pci_ids[] = { {
+ /* handle any USB UHCI controller */
+ PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0),
+ .driver_data = (unsigned long) &uhci_driver,
+ }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
+
+static struct pci_driver uhci_pci_driver = {
+ .name = (char *)hcd_name,
+ .id_table = uhci_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 /* PM */
+};
+
+static int __init uhci_hcd_init(void)
+{
+ int retval = -ENOMEM;
+
+ printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (debug) {
+ errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+ if (!errbuf)
+ goto errbuf_failed;
+ }
+
+ uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
+ if (!uhci_debugfs_root)
+ goto debug_failed;
+
+ uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+ sizeof(struct urb_priv), 0, 0, NULL, NULL);
+ if (!uhci_up_cachep)
+ goto up_failed;
+
+ retval = pci_register_driver(&uhci_pci_driver);
+ if (retval)
+ goto init_failed;
+
+ return 0;
+
+init_failed:
+ if (kmem_cache_destroy(uhci_up_cachep))
+ warn("not all urb_priv's were freed!");
+
+up_failed:
+ debugfs_remove(uhci_debugfs_root);
+
+debug_failed:
+ if (errbuf)
+ kfree(errbuf);
+
+errbuf_failed:
+
+ return retval;
+}
+
+static void __exit uhci_hcd_cleanup(void)
+{
+ pci_unregister_driver(&uhci_pci_driver);
+
+ if (kmem_cache_destroy(uhci_up_cachep))
+ warn("not all urb_priv's were freed!");
+
+ debugfs_remove(uhci_debugfs_root);
+
+ if (errbuf)
+ kfree(errbuf);
+}
+
+module_init(uhci_hcd_init);
+module_exit(uhci_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
new file mode 100644
index 0000000..02255d6
--- /dev/null
+++ b/drivers/usb/host/uhci-hcd.h
@@ -0,0 +1,454 @@
+#ifndef __LINUX_UHCI_HCD_H
+#define __LINUX_UHCI_HCD_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+
+#define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT)
+#define PIPE_DEVEP_MASK 0x0007ff00
+
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD 0
+#define USBCMD_RS 0x0001 /* Run/Stop */
+#define USBCMD_HCRESET 0x0002 /* Host reset */
+#define USBCMD_GRESET 0x0004 /* Global reset */
+#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
+#define USBCMD_FGR 0x0010 /* Force Global Resume */
+#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
+#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
+#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS 2
+#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
+#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
+#define USBSTS_RD 0x0004 /* Resume Detect */
+#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
+#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
+#define USBSTS_HCH 0x0020 /* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR 4
+#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
+#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
+#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
+#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
+
+#define USBFRNUM 6
+#define USBFLBASEADD 8
+#define USBSOF 12
+
+/* USB port status and control registers */
+#define USBPORTSC1 16
+#define USBPORTSC2 18
+#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
+#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
+#define USBPORTSC_PE 0x0004 /* Port Enable */
+#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
+#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */
+#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */
+#define USBPORTSC_RD 0x0040 /* Resume Detect */
+#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */
+#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
+#define USBPORTSC_PR 0x0200 /* Port Reset */
+/* OC and OCC from Intel 430TX and later (not UHCI 1.1d spec) */
+#define USBPORTSC_OC 0x0400 /* Over Current condition */
+#define USBPORTSC_OCC 0x0800 /* Over Current Change R/WC */
+#define USBPORTSC_SUSP 0x1000 /* Suspend */
+#define USBPORTSC_RES2 0x2000 /* reserved, write zeroes */
+#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
+#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
+
+/* Legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */
+
+#define UHCI_PTR_BITS cpu_to_le32(0x000F)
+#define UHCI_PTR_TERM cpu_to_le32(0x0001)
+#define UHCI_PTR_QH cpu_to_le32(0x0002)
+#define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
+#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
+
+#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
+
+struct uhci_frame_list {
+ __le32 frame[UHCI_NUMFRAMES];
+
+ void *frame_cpu[UHCI_NUMFRAMES];
+
+ dma_addr_t dma_handle;
+};
+
+struct urb_priv;
+
+/*
+ * One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
+ * used with one URB, and qh->element (updated by the HC) is either:
+ * - the next unprocessed TD for the URB, or
+ * - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or
+ * - the QH for the next URB queued to the same endpoint.
+ *
+ * The other role of a QH is to serve as a "skeleton" framelist entry, so we
+ * can easily splice a QH for some endpoint into the schedule at the right
+ * place. Then qh->element is UHCI_PTR_TERM.
+ *
+ * In the frame list, qh->link maintains a list of QHs seen by the HC:
+ * skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
+ */
+struct uhci_qh {
+ /* Hardware fields */
+ __le32 link; /* Next queue */
+ __le32 element; /* Queue element pointer */
+
+ /* Software fields */
+ dma_addr_t dma_handle;
+
+ struct usb_device *dev;
+ struct urb_priv *urbp;
+
+ struct list_head list; /* P: uhci->frame_list_lock */
+ struct list_head remove_list; /* P: uhci->remove_list_lock */
+} __attribute__((aligned(16)));
+
+/*
+ * We need a special accessor for the element pointer because it is
+ * subject to asynchronous updates by the controller
+ */
+static __le32 inline qh_element(struct uhci_qh *qh) {
+ __le32 element = qh->element;
+
+ barrier();
+ return element;
+}
+
+/*
+ * for TD <status>:
+ */
+#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT 27
+#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
+#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
+#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
+#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
+#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
+#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
+#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
+#define TD_CTRL_NAK (1 << 19) /* NAK Received */
+#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+ TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
+#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000)
+#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define td_token(td) le32_to_cpu((td)->token)
+#define TD_TOKEN_DEVADDR_SHIFT 8
+#define TD_TOKEN_TOGGLE_SHIFT 19
+#define TD_TOKEN_TOGGLE (1 << 19)
+#define TD_TOKEN_EXPLEN_SHIFT 21
+#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
+#define TD_TOKEN_PID_MASK 0xFF
+
+#define uhci_explen(len) ((len) << TD_TOKEN_EXPLEN_SHIFT)
+
+#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK)
+#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
+#define uhci_endpoint(token) (((token) >> 15) & 0xf)
+#define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
+#define uhci_devep(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
+#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
+#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
+
+/*
+ * The documentation says "4 words for hardware, 4 words for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry as far as I can tell.
+ *
+ * But let's just go with the documentation, at least for 32-bit machines.
+ * On 64-bit machines we probably want to take advantage of the fact that
+ * hw doesn't really care about the size of the sw-only area.
+ *
+ * Alas, not anymore, we have more than 4 words for software, woops.
+ * Everything still works tho, surprise! -jerdfelt
+ *
+ * td->link points to either another TD (not necessarily for the same urb or
+ * even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs)
+ */
+struct uhci_td {
+ /* Hardware fields */
+ __le32 link;
+ __le32 status;
+ __le32 token;
+ __le32 buffer;
+
+ /* Software fields */
+ dma_addr_t dma_handle;
+
+ struct usb_device *dev;
+ struct urb *urb;
+
+ struct list_head list; /* P: urb->lock */
+ struct list_head remove_list; /* P: uhci->td_remove_list_lock */
+
+ int frame; /* for iso: what frame? */
+ struct list_head fl_list; /* P: uhci->frame_list_lock */
+} __attribute__((aligned(16)));
+
+/*
+ * We need a special accessor for the control/status word because it is
+ * subject to asynchronous updates by the controller
+ */
+static u32 inline td_status(struct uhci_td *td) {
+ __le32 status = td->status;
+
+ barrier();
+ return le32_to_cpu(status);
+}
+
+
+/*
+ * The UHCI driver places Interrupt, Control and Bulk into QH's both
+ * to group together TD's for one transfer, and also to faciliate queuing
+ * of URB's. To make it easy to insert entries into the schedule, we have
+ * a skeleton of QH's for each predefined Interrupt latency, low-speed
+ * control, full-speed control and terminating QH (see explanation for
+ * the terminating QH below).
+ *
+ * When we want to add a new QH, we add it to the end of the list for the
+ * skeleton QH.
+ *
+ * For instance, the queue can look like this:
+ *
+ * skel int128 QH
+ * dev 1 interrupt QH
+ * dev 5 interrupt QH
+ * skel int64 QH
+ * skel int32 QH
+ * ...
+ * skel int1 QH
+ * skel low-speed control QH
+ * dev 5 control QH
+ * skel full-speed control QH
+ * skel bulk QH
+ * dev 1 bulk QH
+ * dev 2 bulk QH
+ * skel terminating QH
+ *
+ * The terminating QH is used for 2 reasons:
+ * - To place a terminating TD which is used to workaround a PIIX bug
+ * (see Intel errata for explanation)
+ * - To loop back to the full-speed control queue for full-speed bandwidth
+ * reclamation
+ *
+ * Isochronous transfers are stored before the start of the skeleton
+ * schedule and don't use QH's. While the UHCI spec doesn't forbid the
+ * use of QH's for Isochronous, it doesn't use them either. Since we don't
+ * need to use them either, we follow the spec diagrams in hope that it'll
+ * be more compatible with future UHCI implementations.
+ */
+
+#define UHCI_NUM_SKELQH 12
+#define skel_int128_qh skelqh[0]
+#define skel_int64_qh skelqh[1]
+#define skel_int32_qh skelqh[2]
+#define skel_int16_qh skelqh[3]
+#define skel_int8_qh skelqh[4]
+#define skel_int4_qh skelqh[5]
+#define skel_int2_qh skelqh[6]
+#define skel_int1_qh skelqh[7]
+#define skel_ls_control_qh skelqh[8]
+#define skel_fs_control_qh skelqh[9]
+#define skel_bulk_qh skelqh[10]
+#define skel_term_qh skelqh[11]
+
+/*
+ * Search tree for determining where <interval> fits in the skelqh[]
+ * skeleton.
+ *
+ * An interrupt request should be placed into the slowest skelqh[]
+ * which meets the interval/period/frequency requirement.
+ * An interrupt request is allowed to be faster than <interval> but not slower.
+ *
+ * For a given <interval>, this function returns the appropriate/matching
+ * skelqh[] index value.
+ */
+static inline int __interval_to_skel(int interval)
+{
+ if (interval < 16) {
+ if (interval < 4) {
+ if (interval < 2)
+ return 7; /* int1 for 0-1 ms */
+ return 6; /* int2 for 2-3 ms */
+ }
+ if (interval < 8)
+ return 5; /* int4 for 4-7 ms */
+ return 4; /* int8 for 8-15 ms */
+ }
+ if (interval < 64) {
+ if (interval < 32)
+ return 3; /* int16 for 16-31 ms */
+ return 2; /* int32 for 32-63 ms */
+ }
+ if (interval < 128)
+ return 1; /* int64 for 64-127 ms */
+ return 0; /* int128 for 128-255 ms (Max.) */
+}
+
+/*
+ * Device states for the host controller.
+ *
+ * To prevent "bouncing" in the presence of electrical noise,
+ * we insist on a 1-second "grace" period, before switching to
+ * the RUNNING or SUSPENDED states, during which the state is
+ * not allowed to change.
+ *
+ * The resume process is divided into substates in order to avoid
+ * potentially length delays during the timer handler.
+ *
+ * States in which the host controller is halted must have values <= 0.
+ */
+enum uhci_state {
+ UHCI_RESET,
+ UHCI_RUNNING_GRACE, /* Before RUNNING */
+ UHCI_RUNNING, /* The normal state */
+ UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */
+ UHCI_SUSPENDED = -10, /* When no devices are attached */
+ UHCI_RESUMING_1,
+ UHCI_RESUMING_2
+};
+
+/*
+ * This describes the full uhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct uhci_hcd {
+
+ /* debugfs */
+ struct dentry *dentry;
+
+ /* Grabbed from PCI */
+ unsigned long io_addr;
+
+ struct dma_pool *qh_pool;
+ struct dma_pool *td_pool;
+
+ struct usb_bus *bus;
+
+ struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
+ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
+
+ spinlock_t lock;
+ struct uhci_frame_list *fl; /* P: uhci->lock */
+ int fsbr; /* Full-speed bandwidth reclamation */
+ unsigned long fsbrtimeout; /* FSBR delay */
+
+ enum uhci_state state; /* FIXME: needs a spinlock */
+ unsigned long state_end; /* Time of next transition */
+ unsigned int frame_number; /* As of last check */
+ unsigned int is_stopped;
+#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
+
+ unsigned int scan_in_progress:1; /* Schedule scan is running */
+ unsigned int need_rescan:1; /* Redo the schedule scan */
+ unsigned int resume_detect:1; /* Need a Global Resume */
+
+ /* Support for port suspend/resume/reset */
+ unsigned long port_c_suspend; /* Bit-arrays of ports */
+ unsigned long suspended_ports;
+ unsigned long resuming_ports;
+ unsigned long ports_timeout; /* Time to stop signalling */
+
+ /* Main list of URB's currently controlled by this HC */
+ struct list_head urb_list; /* P: uhci->lock */
+
+ /* List of QH's that are done, but waiting to be unlinked (race) */
+ struct list_head qh_remove_list; /* P: uhci->lock */
+ unsigned int qh_remove_age; /* Age in frames */
+
+ /* List of TD's that are done, but waiting to be freed (race) */
+ struct list_head td_remove_list; /* P: uhci->lock */
+ unsigned int td_remove_age; /* Age in frames */
+
+ /* List of asynchronously unlinked URB's */
+ struct list_head urb_remove_list; /* P: uhci->lock */
+ unsigned int urb_remove_age; /* Age in frames */
+
+ /* List of URB's awaiting completion callback */
+ struct list_head complete_list; /* P: uhci->lock */
+
+ int rh_numports;
+
+ struct timer_list stall_timer;
+
+ wait_queue_head_t waitqh; /* endpoint_disable waiters */
+};
+
+/* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
+static inline struct uhci_hcd *hcd_to_uhci(struct usb_hcd *hcd)
+{
+ return (struct uhci_hcd *) (hcd->hcd_priv);
+}
+static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
+{
+ return container_of((void *) uhci, struct usb_hcd, hcd_priv);
+}
+
+#define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
+
+struct urb_priv {
+ struct list_head urb_list;
+
+ struct urb *urb;
+
+ struct uhci_qh *qh; /* QH for this URB */
+ struct list_head td_list; /* P: urb->lock */
+
+ unsigned fsbr : 1; /* URB turned on FSBR */
+ unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
+ unsigned queued : 1; /* QH was queued (not linked in) */
+ unsigned short_control_packet : 1; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
+
+ unsigned long inserttime; /* In jiffies */
+ unsigned long fsbrtime; /* In jiffies */
+
+ struct list_head queue_list; /* P: uhci->frame_list_lock */
+};
+
+/*
+ * Locking in uhci.c
+ *
+ * Almost everything relating to the hardware schedule and processing
+ * of URBs is protected by uhci->lock. urb->status is protected by
+ * urb->lock; that's the one exception.
+ *
+ * To prevent deadlocks, never lock uhci->lock while holding urb->lock.
+ * The safe order of locking is:
+ *
+ * #1 uhci->lock
+ * #2 urb->lock
+ */
+
+#endif
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
new file mode 100644
index 0000000..4c45ba8
--- /dev/null
+++ b/drivers/usb/host/uhci-hub.c
@@ -0,0 +1,299 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ */
+
+static __u8 root_hub_hub_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x29, /* __u8 bDescriptorType; Hub-descriptor */
+ 0x02, /* __u8 bNbrPorts; */
+ 0x0a, /* __u16 wHubCharacteristics; */
+ 0x00, /* (per-port OC, no power switching) */
+ 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
+ 0x00, /* __u8 bHubContrCurrent; 0 mA */
+ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
+ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+#define UHCI_RH_MAXCHILD 7
+
+/* must write as zeroes */
+#define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
+
+/* status change bits: nonzero writes will clear */
+#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
+
+static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int port;
+
+ *buf = 0;
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
+ test_bit(port, &uhci->port_c_suspend))
+ *buf |= (1 << (port + 1));
+ }
+ if (*buf && uhci->state == UHCI_SUSPENDED)
+ uhci->resume_detect = 1;
+ return !!*buf;
+}
+
+#define OK(x) len = (x); break
+
+#define CLR_RH_PORTSTAT(x) \
+ status = inw(port_addr); \
+ status &= ~(RWC_BITS|WZ_BITS); \
+ status &= ~(x); \
+ status |= RWC_BITS & (x); \
+ outw(status, port_addr)
+
+#define SET_RH_PORTSTAT(x) \
+ status = inw(port_addr); \
+ status |= (x); \
+ status &= ~(RWC_BITS|WZ_BITS); \
+ outw(status, port_addr)
+
+/* UHCI controllers don't automatically stop resume signalling after 20 msec,
+ * so we have to poll and check timeouts in order to take care of it.
+ */
+static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
+ unsigned long port_addr)
+{
+ int status;
+
+ if (test_bit(port, &uhci->suspended_ports)) {
+ CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
+ clear_bit(port, &uhci->suspended_ports);
+ clear_bit(port, &uhci->resuming_ports);
+ set_bit(port, &uhci->port_c_suspend);
+
+ /* The controller won't actually turn off the RD bit until
+ * it has had a chance to send a low-speed EOP sequence,
+ * which takes 3 bit times (= 2 microseconds). We'll delay
+ * slightly longer for good luck. */
+ udelay(4);
+ }
+}
+
+static void uhci_check_ports(struct uhci_hcd *uhci)
+{
+ unsigned int port;
+ unsigned long port_addr;
+ int status;
+
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
+ status = inw(port_addr);
+ if (unlikely(status & USBPORTSC_PR)) {
+ if (time_after_eq(jiffies, uhci->ports_timeout)) {
+ CLR_RH_PORTSTAT(USBPORTSC_PR);
+ udelay(10);
+
+ /* If the port was enabled before, turning
+ * reset on caused a port enable change.
+ * Turning reset off causes a port connect
+ * status change. Clear these changes. */
+ CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
+ SET_RH_PORTSTAT(USBPORTSC_PE);
+ }
+ }
+ if (unlikely(status & USBPORTSC_RD)) {
+ if (!test_bit(port, &uhci->resuming_ports)) {
+
+ /* Port received a wakeup request */
+ set_bit(port, &uhci->resuming_ports);
+ uhci->ports_timeout = jiffies +
+ msecs_to_jiffies(20);
+ } else if (time_after_eq(jiffies,
+ uhci->ports_timeout)) {
+ uhci_finish_suspend(uhci, port, port_addr);
+ }
+ }
+ }
+}
+
+/* size of returned buffer is part of USB spec */
+static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int status, lstatus, retval = 0, len = 0;
+ unsigned int port = wIndex - 1;
+ unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
+ u16 wPortChange, wPortStatus;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ switch (typeReq) {
+
+ case GetHubStatus:
+ *(__le32 *)buf = cpu_to_le32(0);
+ OK(4); /* hub power */
+ case GetPortStatus:
+ if (port >= uhci->rh_numports)
+ goto err;
+
+ uhci_check_ports(uhci);
+ status = inw(port_addr);
+
+ /* Intel controllers report the OverCurrent bit active on.
+ * VIA controllers report it active off, so we'll adjust the
+ * bit value. (It's not standardized in the UHCI spec.)
+ */
+ if (to_pci_dev(hcd->self.controller)->vendor ==
+ PCI_VENDOR_ID_VIA)
+ status ^= USBPORTSC_OC;
+
+ /* UHCI doesn't support C_RESET (always false) */
+ wPortChange = lstatus = 0;
+ if (status & USBPORTSC_CSC)
+ wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ if (status & USBPORTSC_PEC)
+ wPortChange |= USB_PORT_STAT_C_ENABLE;
+ if (status & USBPORTSC_OCC)
+ wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
+
+ if (test_bit(port, &uhci->port_c_suspend)) {
+ wPortChange |= USB_PORT_STAT_C_SUSPEND;
+ lstatus |= 1;
+ }
+ if (test_bit(port, &uhci->suspended_ports))
+ lstatus |= 2;
+ if (test_bit(port, &uhci->resuming_ports))
+ lstatus |= 4;
+
+ /* UHCI has no power switching (always on) */
+ wPortStatus = USB_PORT_STAT_POWER;
+ if (status & USBPORTSC_CCS)
+ wPortStatus |= USB_PORT_STAT_CONNECTION;
+ if (status & USBPORTSC_PE) {
+ wPortStatus |= USB_PORT_STAT_ENABLE;
+ if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
+ wPortStatus |= USB_PORT_STAT_SUSPEND;
+ }
+ if (status & USBPORTSC_OC)
+ wPortStatus |= USB_PORT_STAT_OVERCURRENT;
+ if (status & USBPORTSC_PR)
+ wPortStatus |= USB_PORT_STAT_RESET;
+ if (status & USBPORTSC_LSDA)
+ wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+
+ if (wPortChange)
+ dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
+ wIndex, status, lstatus);
+
+ *(__le16 *)buf = cpu_to_le16(wPortStatus);
+ *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
+ OK(4);
+ case SetHubFeature: /* We don't implement these */
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ OK(0);
+ default:
+ goto err;
+ }
+ break;
+ case SetPortFeature:
+ if (port >= uhci->rh_numports)
+ goto err;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ set_bit(port, &uhci->suspended_ports);
+ SET_RH_PORTSTAT(USBPORTSC_SUSP);
+ OK(0);
+ case USB_PORT_FEAT_RESET:
+ SET_RH_PORTSTAT(USBPORTSC_PR);
+
+ /* Reset terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
+
+ /* USB v2.0 7.1.7.5 */
+ uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
+ OK(0);
+ case USB_PORT_FEAT_POWER:
+ /* UHCI has no power switching */
+ OK(0);
+ default:
+ goto err;
+ }
+ break;
+ case ClearPortFeature:
+ if (port >= uhci->rh_numports)
+ goto err;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ CLR_RH_PORTSTAT(USBPORTSC_PE);
+
+ /* Disable terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
+ OK(0);
+ case USB_PORT_FEAT_C_ENABLE:
+ CLR_RH_PORTSTAT(USBPORTSC_PEC);
+ OK(0);
+ case USB_PORT_FEAT_SUSPEND:
+ if (test_bit(port, &uhci->suspended_ports) &&
+ !test_and_set_bit(port,
+ &uhci->resuming_ports)) {
+ SET_RH_PORTSTAT(USBPORTSC_RD);
+
+ /* The controller won't allow RD to be set
+ * if the port is disabled. When this happens
+ * just skip the Resume signalling.
+ */
+ if (!(inw(port_addr) & USBPORTSC_RD))
+ uhci_finish_suspend(uhci, port,
+ port_addr);
+ else
+ /* USB v2.0 7.1.7.7 */
+ uhci->ports_timeout = jiffies +
+ msecs_to_jiffies(20);
+ }
+ OK(0);
+ case USB_PORT_FEAT_C_SUSPEND:
+ clear_bit(port, &uhci->port_c_suspend);
+ OK(0);
+ case USB_PORT_FEAT_POWER:
+ /* UHCI has no power switching */
+ goto err;
+ case USB_PORT_FEAT_C_CONNECTION:
+ CLR_RH_PORTSTAT(USBPORTSC_CSC);
+ OK(0);
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ CLR_RH_PORTSTAT(USBPORTSC_OCC);
+ OK(0);
+ case USB_PORT_FEAT_C_RESET:
+ /* this driver won't report these */
+ OK(0);
+ default:
+ goto err;
+ }
+ break;
+ case GetHubDescriptor:
+ len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
+ memcpy(buf, root_hub_hub_des, len);
+ if (len > 2)
+ buf[2] = uhci->rh_numports;
+ OK(len);
+ default:
+err:
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore(&uhci->lock, flags);
+
+ return retval;
+}
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
new file mode 100644
index 0000000..2a7c195
--- /dev/null
+++ b/drivers/usb/host/uhci-q.c
@@ -0,0 +1,1539 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ */
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci);
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci);
+static void uhci_free_pending_tds(struct uhci_hcd *uhci);
+
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurious interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
+static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
+{
+ uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
+}
+
+static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
+{
+ uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
+}
+
+static inline void uhci_moveto_complete(struct uhci_hcd *uhci,
+ struct urb_priv *urbp)
+{
+ list_move_tail(&urbp->urb_list, &uhci->complete_list);
+}
+
+static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+ dma_addr_t dma_handle;
+ struct uhci_td *td;
+
+ td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);
+ if (!td)
+ return NULL;
+
+ td->dma_handle = dma_handle;
+
+ td->link = UHCI_PTR_TERM;
+ td->buffer = 0;
+
+ td->frame = -1;
+ td->dev = dev;
+
+ INIT_LIST_HEAD(&td->list);
+ INIT_LIST_HEAD(&td->remove_list);
+ INIT_LIST_HEAD(&td->fl_list);
+
+ usb_get_dev(dev);
+
+ return td;
+}
+
+static inline void uhci_fill_td(struct uhci_td *td, u32 status,
+ u32 token, u32 buffer)
+{
+ td->status = cpu_to_le32(status);
+ td->token = cpu_to_le32(token);
+ td->buffer = cpu_to_le32(buffer);
+}
+
+/*
+ * We insert Isochronous URB's directly into the frame list at the beginning
+ */
+static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
+{
+ framenum &= (UHCI_NUMFRAMES - 1);
+
+ td->frame = framenum;
+
+ /* Is there a TD already mapped there? */
+ if (uhci->fl->frame_cpu[framenum]) {
+ struct uhci_td *ftd, *ltd;
+
+ ftd = uhci->fl->frame_cpu[framenum];
+ ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+
+ list_add_tail(&td->fl_list, &ftd->fl_list);
+
+ td->link = ltd->link;
+ wmb();
+ ltd->link = cpu_to_le32(td->dma_handle);
+ } else {
+ td->link = uhci->fl->frame[framenum];
+ wmb();
+ uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
+ uhci->fl->frame_cpu[framenum] = td;
+ }
+}
+
+static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+ /* If it's not inserted, don't remove it */
+ if (td->frame == -1 && list_empty(&td->fl_list))
+ return;
+
+ if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
+ if (list_empty(&td->fl_list)) {
+ uhci->fl->frame[td->frame] = td->link;
+ uhci->fl->frame_cpu[td->frame] = NULL;
+ } else {
+ struct uhci_td *ntd;
+
+ ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
+ uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
+ uhci->fl->frame_cpu[td->frame] = ntd;
+ }
+ } else {
+ struct uhci_td *ptd;
+
+ ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+ ptd->link = td->link;
+ }
+
+ wmb();
+ td->link = UHCI_PTR_TERM;
+
+ list_del_init(&td->fl_list);
+ td->frame = -1;
+}
+
+/*
+ * Inserts a td list into qh.
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci_td *td;
+ __le32 *plink;
+
+ /* Ordering isn't important here yet since the QH hasn't been */
+ /* inserted into the schedule yet */
+ plink = &qh->element;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ *plink = cpu_to_le32(td->dma_handle) | breadth;
+ plink = &td->link;
+ }
+ *plink = UHCI_PTR_TERM;
+}
+
+static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+ if (!list_empty(&td->list))
+ dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
+ if (!list_empty(&td->remove_list))
+ dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
+ if (!list_empty(&td->fl_list))
+ dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+
+ if (td->dev)
+ usb_put_dev(td->dev);
+
+ dma_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+ dma_addr_t dma_handle;
+ struct uhci_qh *qh;
+
+ qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
+ if (!qh)
+ return NULL;
+
+ qh->dma_handle = dma_handle;
+
+ qh->element = UHCI_PTR_TERM;
+ qh->link = UHCI_PTR_TERM;
+
+ qh->dev = dev;
+ qh->urbp = NULL;
+
+ INIT_LIST_HEAD(&qh->list);
+ INIT_LIST_HEAD(&qh->remove_list);
+
+ usb_get_dev(dev);
+
+ return qh;
+}
+
+static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ if (!list_empty(&qh->list))
+ dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+ if (!list_empty(&qh->remove_list))
+ dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh);
+
+ if (qh->dev)
+ usb_put_dev(qh->dev);
+
+ dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
+}
+
+/*
+ * Append this urb's qh after the last qh in skelqh->list
+ *
+ * Note that urb_priv.queue_list doesn't have a separate queue head;
+ * it's a ring with every element "live".
+ */
+static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct urb_priv *turbp;
+ struct uhci_qh *lqh;
+
+ /* Grab the last QH */
+ lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
+
+ /* Point to the next skelqh */
+ urbp->qh->link = lqh->link;
+ wmb(); /* Ordering is important */
+
+ /*
+ * Patch QHs for previous endpoint's queued URBs? HC goes
+ * here next, not to the next skelqh it now points to.
+ *
+ * lqh --> td ... --> qh ... --> td --> qh ... --> td
+ * | | |
+ * v v v
+ * +<----------------+-----------------+
+ * v
+ * newqh --> td ... --> td
+ * |
+ * v
+ * ...
+ *
+ * The HC could see (and use!) any of these as we write them.
+ */
+ lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+ if (lqh->urbp) {
+ list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
+ turbp->qh->link = lqh->link;
+ }
+
+ list_add_tail(&urbp->qh->list, &skelqh->list);
+}
+
+/*
+ * Start removal of QH from schedule; it finishes next frame.
+ * TDs should be unlinked before this is called.
+ */
+static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
+ __le32 newlink;
+
+ if (!qh)
+ return;
+
+ /*
+ * Only go through the hoops if it's actually linked in
+ */
+ if (!list_empty(&qh->list)) {
+
+ /* If our queue is nonempty, make the next URB the head */
+ if (!list_empty(&qh->urbp->queue_list)) {
+ struct urb_priv *nurbp;
+
+ nurbp = list_entry(qh->urbp->queue_list.next,
+ struct urb_priv, queue_list);
+ nurbp->queued = 0;
+ list_add(&nurbp->qh->list, &qh->list);
+ newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+ } else
+ newlink = qh->link;
+
+ /* Fix up the previous QH's queue to link to either
+ * the new head of this queue or the start of the
+ * next endpoint's queue. */
+ pqh = list_entry(qh->list.prev, struct uhci_qh, list);
+ pqh->link = newlink;
+ if (pqh->urbp) {
+ struct urb_priv *turbp;
+
+ list_for_each_entry(turbp, &pqh->urbp->queue_list,
+ queue_list)
+ turbp->qh->link = newlink;
+ }
+ wmb();
+
+ /* Leave qh->link in case the HC is on the QH now, it will */
+ /* continue the rest of the schedule */
+ qh->element = UHCI_PTR_TERM;
+
+ list_del_init(&qh->list);
+ }
+
+ list_del_init(&qh->urbp->queue_list);
+ qh->urbp = NULL;
+
+ uhci_get_current_frame_number(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) {
+ uhci_free_pending_qhs(uhci);
+ uhci->qh_remove_age = uhci->frame_number;
+ }
+
+ /* Check to see if the remove list is empty. Set the IOC bit */
+ /* to force an interrupt so we can remove the QH */
+ if (list_empty(&uhci->qh_remove_list))
+ uhci_set_next_interrupt(uhci);
+
+ list_add(&qh->remove_list, &uhci->qh_remove_list);
+}
+
+static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci_td *td;
+
+ list_for_each_entry(td, &urbp->td_list, list) {
+ if (toggle)
+ td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
+ else
+ td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
+
+ toggle ^= 1;
+ }
+
+ return toggle;
+}
+
+/* This function will append one URB's QH to another URB's QH. This is for */
+/* queuing interrupt, control or bulk transfers */
+static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
+{
+ struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
+ struct uhci_td *lltd;
+
+ eurbp = eurb->hcpriv;
+ urbp = urb->hcpriv;
+
+ /* Find the first URB in the queue */
+ furbp = eurbp;
+ if (eurbp->queued) {
+ list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
+ if (!furbp->queued)
+ break;
+ }
+
+ lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
+
+ lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
+
+ /* Control transfers always start with toggle 0 */
+ if (!usb_pipecontrol(urb->pipe))
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe),
+ uhci_fixup_toggle(urb,
+ uhci_toggle(td_token(lltd)) ^ 1));
+
+ /* All qh's in the queue need to link to the next queue */
+ urbp->qh->link = eurbp->qh->link;
+
+ wmb(); /* Make sure we flush everything */
+
+ lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+
+ list_add_tail(&urbp->queue_list, &furbp->queue_list);
+
+ urbp->queued = 1;
+}
+
+static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp, *nurbp, *purbp, *turbp;
+ struct uhci_td *pltd;
+ unsigned int toggle;
+
+ urbp = urb->hcpriv;
+
+ if (list_empty(&urbp->queue_list))
+ return;
+
+ nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+
+ /*
+ * Fix up the toggle for the following URBs in the queue.
+ * Only needed for bulk and interrupt: control and isochronous
+ * endpoints don't propagate toggles between messages.
+ */
+ if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) {
+ if (!urbp->queued)
+ /* We just set the toggle in uhci_unlink_generic */
+ toggle = usb_gettoggle(urb->dev,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+ else {
+ /* If we're in the middle of the queue, grab the */
+ /* toggle from the TD previous to us */
+ purbp = list_entry(urbp->queue_list.prev,
+ struct urb_priv, queue_list);
+ pltd = list_entry(purbp->td_list.prev,
+ struct uhci_td, list);
+ toggle = uhci_toggle(td_token(pltd)) ^ 1;
+ }
+
+ list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
+ if (!turbp->queued)
+ break;
+ toggle = uhci_fixup_toggle(turbp->urb, toggle);
+ }
+
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), toggle);
+ }
+
+ if (urbp->queued) {
+ /* We're somewhere in the middle (or end). The case where
+ * we're at the head is handled in uhci_remove_qh(). */
+ purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+ queue_list);
+
+ pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+ if (nurbp->queued)
+ pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+ else
+ /* The next URB happens to be the beginning, so */
+ /* we're the last, end the chain */
+ pltd->link = UHCI_PTR_TERM;
+ }
+
+ /* urbp->queue_list is handled in uhci_remove_qh() */
+}
+
+static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp;
+
+ urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
+ if (!urbp)
+ return NULL;
+
+ memset((void *)urbp, 0, sizeof(*urbp));
+
+ urbp->inserttime = jiffies;
+ urbp->fsbrtime = jiffies;
+ urbp->urb = urb;
+
+ INIT_LIST_HEAD(&urbp->td_list);
+ INIT_LIST_HEAD(&urbp->queue_list);
+ INIT_LIST_HEAD(&urbp->urb_list);
+
+ list_add_tail(&urbp->urb_list, &uhci->urb_list);
+
+ urb->hcpriv = urbp;
+
+ return urbp;
+}
+
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+ td->urb = urb;
+
+ list_add_tail(&td->list, &urbp->td_list);
+}
+
+static void uhci_remove_td_from_urb(struct uhci_td *td)
+{
+ if (list_empty(&td->list))
+ return;
+
+ list_del_init(&td->list);
+
+ td->urb = NULL;
+}
+
+static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct uhci_td *td, *tmp;
+ struct urb_priv *urbp;
+
+ urbp = (struct urb_priv *)urb->hcpriv;
+ if (!urbp)
+ return;
+
+ if (!list_empty(&urbp->urb_list))
+ dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
+ "or uhci->remove_list!\n", urb);
+
+ uhci_get_current_frame_number(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) {
+ uhci_free_pending_tds(uhci);
+ uhci->td_remove_age = uhci->frame_number;
+ }
+
+ /* Check to see if the remove list is empty. Set the IOC bit */
+ /* to force an interrupt so we can remove the TD's*/
+ if (list_empty(&uhci->td_remove_list))
+ uhci_set_next_interrupt(uhci);
+
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ uhci_remove_td_from_urb(td);
+ uhci_remove_td(uhci, td);
+ list_add(&td->remove_list, &uhci->td_remove_list);
+ }
+
+ urb->hcpriv = NULL;
+ kmem_cache_free(uhci_up_cachep, urbp);
+}
+
+static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+ if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
+ urbp->fsbr = 1;
+ if (!uhci->fsbr++ && !uhci->fsbrtimeout)
+ uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+ }
+}
+
+static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+ if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
+ urbp->fsbr = 0;
+ if (!--uhci->fsbr)
+ uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+ }
+}
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td_status(td) & 0xF60000), a.k.a.
+ * uhci_status_bits(td_status(td)).
+ * Note: <status> does not include the TD_CTRL_NAK bit.
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+ if (!status)
+ return 0;
+ if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */
+ return -EPROTO;
+ if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */
+ if (dir_out)
+ return -EPROTO;
+ else
+ return -EILSEQ;
+ }
+ if (status & TD_CTRL_BABBLE) /* Babble */
+ return -EOVERFLOW;
+ if (status & TD_CTRL_DBUFERR) /* Buffer error */
+ return -ENOSR;
+ if (status & TD_CTRL_STALLED) /* Stalled */
+ return -EPIPE;
+ WARN_ON(status & TD_CTRL_ACTIVE); /* Active */
+ return 0;
+}
+
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci_td *td;
+ struct uhci_qh *qh, *skelqh;
+ unsigned long destination, status;
+ int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ int len = urb->transfer_buffer_length;
+ dma_addr_t data = urb->transfer_dma;
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+ /* 3 errors */
+ status = TD_CTRL_ACTIVE | uhci_maxerr(3);
+ if (urb->dev->speed == USB_SPEED_LOW)
+ status |= TD_CTRL_LS;
+
+ /*
+ * Build the TD for the control request setup packet
+ */
+ td = uhci_alloc_td(uhci, urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ uhci_add_td_to_urb(urb, td);
+ uhci_fill_td(td, status, destination | uhci_explen(7),
+ urb->setup_dma);
+
+ /*
+ * If direction is "send", change the packet ID from SETUP (0x2D)
+ * to OUT (0xE1). Else change it from SETUP to IN (0x69) and
+ * set Short Packet Detect (SPD) for all data packets.
+ */
+ if (usb_pipeout(urb->pipe))
+ destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
+ else {
+ destination ^= (USB_PID_SETUP ^ USB_PID_IN);
+ status |= TD_CTRL_SPD;
+ }
+
+ /*
+ * Build the DATA TD's
+ */
+ while (len > 0) {
+ int pktsze = len;
+
+ if (pktsze > maxsze)
+ pktsze = maxsze;
+
+ td = uhci_alloc_td(uhci, urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ /* Alternate Data0/1 (start with Data1) */
+ destination ^= TD_TOKEN_TOGGLE;
+
+ uhci_add_td_to_urb(urb, td);
+ uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
+ data);
+
+ data += pktsze;
+ len -= pktsze;
+ }
+
+ /*
+ * Build the final TD for control status
+ */
+ td = uhci_alloc_td(uhci, urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ /*
+ * It's IN if the pipe is an output pipe or we're not expecting
+ * data back.
+ */
+ destination &= ~TD_TOKEN_PID_MASK;
+ if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
+ destination |= USB_PID_IN;
+ else
+ destination |= USB_PID_OUT;
+
+ destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
+
+ status &= ~TD_CTRL_SPD;
+
+ uhci_add_td_to_urb(urb, td);
+ uhci_fill_td(td, status | TD_CTRL_IOC,
+ destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
+
+ qh = uhci_alloc_qh(uhci, urb->dev);
+ if (!qh)
+ return -ENOMEM;
+
+ urbp->qh = qh;
+ qh->urbp = urbp;
+
+ uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+
+ /* Low-speed transfers get a different queue, and won't hog the bus.
+ * Also, some devices enumerate better without FSBR; the easiest way
+ * to do that is to put URBs on the low-speed queue while the device
+ * is in the DEFAULT state. */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->state == USB_STATE_DEFAULT)
+ skelqh = uhci->skel_ls_control_qh;
+ else {
+ skelqh = uhci->skel_fs_control_qh;
+ uhci_inc_fsbr(uhci, urb);
+ }
+
+ if (eurb)
+ uhci_append_queued_urb(uhci, eurb, urb);
+ else
+ uhci_insert_qh(uhci, skelqh, urb);
+
+ return -EINPROGRESS;
+}
+
+/*
+ * If control-IN transfer was short, the status packet wasn't sent.
+ * This routine changes the element pointer in the QH to point at the
+ * status TD. It's safe to do this even while the QH is live, because
+ * the hardware only updates the element pointer following a successful
+ * transfer. The inactive TD for the short packet won't cause an update,
+ * so the pointer won't get overwritten. The next time the controller
+ * sees this QH, it will send the status packet.
+ */
+static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci_td *td;
+
+ urbp->short_control_packet = 1;
+
+ td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+ urbp->qh->element = cpu_to_le32(td->dma_handle);
+
+ return -EINPROGRESS;
+}
+
+
+static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct list_head *tmp, *head;
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_td *td;
+ unsigned int status;
+ int ret = 0;
+
+ if (list_empty(&urbp->td_list))
+ return -EINVAL;
+
+ head = &urbp->td_list;
+
+ if (urbp->short_control_packet) {
+ tmp = head->prev;
+ goto status_stage;
+ }
+
+ tmp = head->next;
+ td = list_entry(tmp, struct uhci_td, list);
+
+ /* The first TD is the SETUP stage, check the status, but skip */
+ /* the count */
+ status = uhci_status_bits(td_status(td));
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ if (status)
+ goto td_error;
+
+ urb->actual_length = 0;
+
+ /* The rest of the TD's (but the last) are data */
+ tmp = tmp->next;
+ while (tmp != head && tmp->next != head) {
+ unsigned int ctrlstat;
+
+ td = list_entry(tmp, struct uhci_td, list);
+ tmp = tmp->next;
+
+ ctrlstat = td_status(td);
+ status = uhci_status_bits(ctrlstat);
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ urb->actual_length += uhci_actual_length(ctrlstat);
+
+ if (status)
+ goto td_error;
+
+ /* Check to see if we received a short packet */
+ if (uhci_actual_length(ctrlstat) <
+ uhci_expected_length(td_token(td))) {
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ if (uhci_packetid(td_token(td)) == USB_PID_IN)
+ return usb_control_retrigger_status(uhci, urb);
+ else
+ return 0;
+ }
+ }
+
+status_stage:
+ td = list_entry(tmp, struct uhci_td, list);
+
+ /* Control status stage */
+ status = td_status(td);
+
+#ifdef I_HAVE_BUGGY_APC_BACKUPS
+ /* APC BackUPS Pro kludge */
+ /* It tries to send all of the descriptor instead of the amount */
+ /* we requested */
+ if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
+ status & TD_CTRL_ACTIVE &&
+ status & TD_CTRL_NAK)
+ return 0;
+#endif
+
+ status = uhci_status_bits(status);
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ if (status)
+ goto td_error;
+
+ return 0;
+
+td_error:
+ ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+
+err:
+ if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+ /* Some debugging code */
+ dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+ __FUNCTION__, status);
+
+ if (errbuf) {
+ /* Print the chain for debugging purposes */
+ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+ lprintk(errbuf);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Common submit for bulk and interrupt
+ */
+static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
+{
+ struct uhci_td *td;
+ struct uhci_qh *qh;
+ unsigned long destination, status;
+ int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ int len = urb->transfer_buffer_length;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ dma_addr_t data = urb->transfer_dma;
+
+ if (len < 0)
+ return -EINVAL;
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+ status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
+ if (urb->dev->speed == USB_SPEED_LOW)
+ status |= TD_CTRL_LS;
+ if (usb_pipein(urb->pipe))
+ status |= TD_CTRL_SPD;
+
+ /*
+ * Build the DATA TD's
+ */
+ do { /* Allow zero length packets */
+ int pktsze = maxsze;
+
+ if (pktsze >= len) {
+ pktsze = len;
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ status &= ~TD_CTRL_SPD;
+ }
+
+ td = uhci_alloc_td(uhci, urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ uhci_add_td_to_urb(urb, td);
+ uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
+ (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+ data);
+
+ data += pktsze;
+ len -= maxsze;
+
+ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+ } while (len > 0);
+
+ /*
+ * URB_ZERO_PACKET means adding a 0-length packet, if direction
+ * is OUT and the transfer_length was an exact multiple of maxsze,
+ * hence (len = transfer_length - N * maxsze) == 0
+ * however, if transfer_length == 0, the zero packet was already
+ * prepared above.
+ */
+ if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
+ !len && urb->transfer_buffer_length) {
+ td = uhci_alloc_td(uhci, urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ uhci_add_td_to_urb(urb, td);
+ uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
+ (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+ data);
+
+ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+ }
+
+ /* Set the interrupt-on-completion flag on the last packet.
+ * A more-or-less typical 4 KB URB (= size of one memory page)
+ * will require about 3 ms to transfer; that's a little on the
+ * fast side but not enough to justify delaying an interrupt
+ * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
+ * flag setting. */
+ td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+ qh = uhci_alloc_qh(uhci, urb->dev);
+ if (!qh)
+ return -ENOMEM;
+
+ urbp->qh = qh;
+ qh->urbp = urbp;
+
+ /* Always breadth first */
+ uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+
+ if (eurb)
+ uhci_append_queued_urb(uhci, eurb, urb);
+ else
+ uhci_insert_qh(uhci, skelqh, urb);
+
+ return -EINPROGRESS;
+}
+
+/*
+ * Common result for bulk and interrupt
+ */
+static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_td *td;
+ unsigned int status = 0;
+ int ret = 0;
+
+ urb->actual_length = 0;
+
+ list_for_each_entry(td, &urbp->td_list, list) {
+ unsigned int ctrlstat = td_status(td);
+
+ status = uhci_status_bits(ctrlstat);
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ urb->actual_length += uhci_actual_length(ctrlstat);
+
+ if (status)
+ goto td_error;
+
+ if (uhci_actual_length(ctrlstat) <
+ uhci_expected_length(td_token(td))) {
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ ret = -EREMOTEIO;
+ goto err;
+ } else
+ return 0;
+ }
+ }
+
+ return 0;
+
+td_error:
+ ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+
+err:
+ /*
+ * Enable this chunk of code if you want to see some more debugging.
+ * But be careful, it has the tendancy to starve out khubd and prevent
+ * disconnects from happening successfully if you have a slow debug
+ * log interface (like a serial console.
+ */
+#if 0
+ if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+ /* Some debugging code */
+ dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+ __FUNCTION__, status);
+
+ if (errbuf) {
+ /* Print the chain for debugging purposes */
+ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+ lprintk(errbuf);
+ }
+ }
+#endif
+ return ret;
+}
+
+static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+ int ret;
+
+ /* Can't have low-speed bulk transfers */
+ if (urb->dev->speed == USB_SPEED_LOW)
+ return -EINVAL;
+
+ ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
+ if (ret == -EINPROGRESS)
+ uhci_inc_fsbr(uhci, urb);
+
+ return ret;
+}
+
+static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+ /* USB 1.1 interrupt transfers only involve one packet per interval;
+ * that's the uhci_submit_common() "breadth first" policy. Drivers
+ * can submit urbs of any length, but longer ones might need many
+ * intervals to complete.
+ */
+ return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
+}
+
+/*
+ * Isochronous transfers
+ */
+static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
+{
+ struct urb *last_urb = NULL;
+ struct urb_priv *up;
+ int ret = 0;
+
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ struct urb *u = up->urb;
+
+ /* look for pending URB's with identical pipe handle */
+ if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
+ (u->status == -EINPROGRESS) && (u != urb)) {
+ if (!last_urb)
+ *start = u->start_frame;
+ last_urb = u;
+ }
+ }
+
+ if (last_urb) {
+ *end = (last_urb->start_frame + last_urb->number_of_packets *
+ last_urb->interval) & (UHCI_NUMFRAMES-1);
+ ret = 0;
+ } else
+ ret = -1; /* no previous urb found */
+
+ return ret;
+}
+
+static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
+{
+ int limits;
+ unsigned int start = 0, end = 0;
+
+ if (urb->number_of_packets > 900) /* 900? Why? */
+ return -EFBIG;
+
+ limits = isochronous_find_limits(uhci, urb, &start, &end);
+
+ if (urb->transfer_flags & URB_ISO_ASAP) {
+ if (limits) {
+ uhci_get_current_frame_number(uhci);
+ urb->start_frame = (uhci->frame_number + 10)
+ & (UHCI_NUMFRAMES - 1);
+ } else
+ urb->start_frame = end;
+ } else {
+ urb->start_frame &= (UHCI_NUMFRAMES - 1);
+ /* FIXME: Sanity check */
+ }
+
+ return 0;
+}
+
+/*
+ * Isochronous transfers
+ */
+static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct uhci_td *td;
+ int i, ret, frame;
+ int status, destination;
+
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+ ret = isochronous_find_start(uhci, urb);
+ if (ret)
+ return ret;
+
+ frame = urb->start_frame;
+ for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
+ if (!urb->iso_frame_desc[i].length)
+ continue;
+
+ td = uhci_alloc_td(uhci, urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ uhci_add_td_to_urb(urb, td);
+ uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
+ urb->transfer_dma + urb->iso_frame_desc[i].offset);
+
+ if (i + 1 >= urb->number_of_packets)
+ td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+ uhci_insert_td_frame_list(uhci, td, frame);
+ }
+
+ return -EINPROGRESS;
+}
+
+static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct uhci_td *td;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ int status;
+ int i, ret = 0;
+
+ urb->actual_length = 0;
+
+ i = 0;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ int actlength;
+ unsigned int ctrlstat = td_status(td);
+
+ if (ctrlstat & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
+ actlength = uhci_actual_length(ctrlstat);
+ urb->iso_frame_desc[i].actual_length = actlength;
+ urb->actual_length += actlength;
+
+ status = uhci_map_status(uhci_status_bits(ctrlstat),
+ usb_pipeout(urb->pipe));
+ urb->iso_frame_desc[i].status = status;
+ if (status) {
+ urb->error_count++;
+ ret = status;
+ }
+
+ i++;
+ }
+
+ return ret;
+}
+
+static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *up;
+
+ /* We don't match Isoc transfers since they are special */
+ if (usb_pipeisoc(urb->pipe))
+ return NULL;
+
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ struct urb *u = up->urb;
+
+ if (u->dev == urb->dev && u->status == -EINPROGRESS) {
+ /* For control, ignore the direction */
+ if (usb_pipecontrol(urb->pipe) &&
+ (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
+ return u;
+ else if (u->pipe == urb->pipe)
+ return u;
+ }
+ }
+
+ return NULL;
+}
+
+static int uhci_urb_enqueue(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb, int mem_flags)
+{
+ int ret;
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
+ struct urb *eurb;
+ int bustime;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+
+ ret = urb->status;
+ if (ret != -EINPROGRESS) /* URB already unlinked! */
+ goto out;
+
+ eurb = uhci_find_urb_ep(uhci, urb);
+
+ if (!uhci_alloc_urb_priv(uhci, urb)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ ret = uhci_submit_control(uhci, urb, eurb);
+ break;
+ case PIPE_INTERRUPT:
+ if (!eurb) {
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0)
+ ret = bustime;
+ else {
+ ret = uhci_submit_interrupt(uhci, urb, eurb);
+ if (ret == -EINPROGRESS)
+ usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+ }
+ } else { /* inherit from parent */
+ urb->bandwidth = eurb->bandwidth;
+ ret = uhci_submit_interrupt(uhci, urb, eurb);
+ }
+ break;
+ case PIPE_BULK:
+ ret = uhci_submit_bulk(uhci, urb, eurb);
+ break;
+ case PIPE_ISOCHRONOUS:
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+ break;
+ }
+
+ ret = uhci_submit_isochronous(uhci, urb);
+ if (ret == -EINPROGRESS)
+ usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+ break;
+ }
+
+ if (ret != -EINPROGRESS) {
+ /* Submit failed, so delete it from the urb_list */
+ struct urb_priv *urbp = urb->hcpriv;
+
+ list_del_init(&urbp->urb_list);
+ uhci_destroy_urb_priv(uhci, urb);
+ } else
+ ret = 0;
+
+out:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return ret;
+}
+
+/*
+ * Return the result of a transfer
+ */
+static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
+{
+ int ret = -EINPROGRESS;
+ struct urb_priv *urbp;
+
+ spin_lock(&urb->lock);
+
+ urbp = (struct urb_priv *)urb->hcpriv;
+
+ if (urb->status != -EINPROGRESS) /* URB already dequeued */
+ goto out;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ ret = uhci_result_control(uhci, urb);
+ break;
+ case PIPE_BULK:
+ case PIPE_INTERRUPT:
+ ret = uhci_result_common(uhci, urb);
+ break;
+ case PIPE_ISOCHRONOUS:
+ ret = uhci_result_isochronous(uhci, urb);
+ break;
+ }
+
+ if (ret == -EINPROGRESS)
+ goto out;
+ urb->status = ret;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ case PIPE_ISOCHRONOUS:
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ if (urb->bandwidth)
+ usb_release_bandwidth(urb->dev, urb, 1);
+ uhci_unlink_generic(uhci, urb);
+ break;
+ case PIPE_INTERRUPT:
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Make sure we don't release if we have a queued URB */
+ if (list_empty(&urbp->queue_list) && urb->bandwidth)
+ usb_release_bandwidth(urb->dev, urb, 0);
+ else
+ /* bandwidth was passed on to queued URB, */
+ /* so don't let usb_unlink_urb() release it */
+ urb->bandwidth = 0;
+ uhci_unlink_generic(uhci, urb);
+ break;
+ default:
+ dev_info(uhci_dev(uhci), "%s: unknown pipe type %d "
+ "for urb %p\n",
+ __FUNCTION__, usb_pipetype(urb->pipe), urb);
+ }
+
+ /* Move it from uhci->urb_list to uhci->complete_list */
+ uhci_moveto_complete(uhci, urbp);
+
+out:
+ spin_unlock(&urb->lock);
+}
+
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct list_head *head;
+ struct uhci_td *td;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ int prevactive = 0;
+
+ uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
+
+ /*
+ * Now we need to find out what the last successful toggle was
+ * so we can update the local data toggle for the next transfer
+ *
+ * There are 2 ways the last successful completed TD is found:
+ *
+ * 1) The TD is NOT active and the actual length < expected length
+ * 2) The TD is NOT active and it's the last TD in the chain
+ *
+ * and a third way the first uncompleted TD is found:
+ *
+ * 3) The TD is active and the previous TD is NOT active
+ *
+ * Control and Isochronous ignore the toggle, so this is safe
+ * for all types
+ *
+ * FIXME: The toggle fixups won't be 100% reliable until we
+ * change over to using a single queue for each endpoint and
+ * stop the queue before unlinking.
+ */
+ head = &urbp->td_list;
+ list_for_each_entry(td, head, list) {
+ unsigned int ctrlstat = td_status(td);
+
+ if (!(ctrlstat & TD_CTRL_ACTIVE) &&
+ (uhci_actual_length(ctrlstat) <
+ uhci_expected_length(td_token(td)) ||
+ td->list.next == head))
+ usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+ uhci_packetout(td_token(td)),
+ uhci_toggle(td_token(td)) ^ 1);
+ else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
+ usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+ uhci_packetout(td_token(td)),
+ uhci_toggle(td_token(td)));
+
+ prevactive = ctrlstat & TD_CTRL_ACTIVE;
+ }
+
+ uhci_delete_queued_urb(uhci, urb);
+
+ /* The interrupt loop will reclaim the QH's */
+ uhci_remove_qh(uhci, urbp->qh);
+ urbp->qh = NULL;
+}
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
+ struct urb_priv *urbp;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ urbp = urb->hcpriv;
+ if (!urbp) /* URB was never linked! */
+ goto done;
+ list_del_init(&urbp->urb_list);
+
+ uhci_unlink_generic(uhci, urb);
+
+ uhci_get_current_frame_number(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) {
+ uhci_remove_pending_urbps(uhci);
+ uhci->urb_remove_age = uhci->frame_number;
+ }
+
+ /* If we're the first, set the next interrupt bit */
+ if (list_empty(&uhci->urb_remove_list))
+ uhci_set_next_interrupt(uhci);
+ list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
+
+done:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return 0;
+}
+
+static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct list_head *head;
+ struct uhci_td *td;
+ int count = 0;
+
+ uhci_dec_fsbr(uhci, urb);
+
+ urbp->fsbr_timeout = 1;
+
+ /*
+ * Ideally we would want to fix qh->element as well, but it's
+ * read/write by the HC, so that can introduce a race. It's not
+ * really worth the hassle
+ */
+
+ head = &urbp->td_list;
+ list_for_each_entry(td, head, list) {
+ /*
+ * Make sure we don't do the last one (since it'll have the
+ * TERM bit set) as well as we skip every so many TD's to
+ * make sure it doesn't hog the bandwidth
+ */
+ if (td->list.next != head && (count % DEPTH_INTERVAL) ==
+ (DEPTH_INTERVAL - 1))
+ td->link |= UHCI_PTR_DEPTH;
+
+ count++;
+ }
+
+ return 0;
+}
+
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
+{
+ struct uhci_qh *qh, *tmp;
+
+ list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
+ list_del_init(&qh->remove_list);
+
+ uhci_free_qh(uhci, qh);
+ }
+}
+
+static void uhci_free_pending_tds(struct uhci_hcd *uhci)
+{
+ struct uhci_td *td, *tmp;
+
+ list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
+ list_del_init(&td->remove_list);
+
+ uhci_free_td(uhci, td);
+ }
+}
+
+static void
+uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+__releases(uhci->lock)
+__acquires(uhci->lock)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ uhci_destroy_urb_priv(uhci, urb);
+
+ spin_unlock(&uhci->lock);
+ usb_hcd_giveback_urb(hcd, urb, regs);
+ spin_lock(&uhci->lock);
+}
+
+static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs)
+{
+ struct urb_priv *urbp, *tmp;
+
+ list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
+ struct urb *urb = urbp->urb;
+
+ list_del_init(&urbp->urb_list);
+ uhci_finish_urb(uhci_to_hcd(uhci), urb, regs);
+ }
+}
+
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
+{
+
+ /* Splice the urb_remove_list onto the end of the complete_list */
+ list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
+}
+
+/* Process events in the schedule, but only in one thread at a time */
+static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
+{
+ struct urb_priv *urbp, *tmp;
+
+ /* Don't allow re-entrant calls */
+ if (uhci->scan_in_progress) {
+ uhci->need_rescan = 1;
+ return;
+ }
+ uhci->scan_in_progress = 1;
+ rescan:
+ uhci->need_rescan = 0;
+
+ uhci_get_current_frame_number(uhci);
+
+ if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)
+ uhci_free_pending_qhs(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)
+ uhci_free_pending_tds(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age)
+ uhci_remove_pending_urbps(uhci);
+
+ /* Walk the list of pending URBs to see which ones completed
+ * (must be _safe because uhci_transfer_result() dequeues URBs) */
+ list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {
+ struct urb *urb = urbp->urb;
+
+ /* Checks the status and does all of the magic necessary */
+ uhci_transfer_result(uhci, urb);
+ }
+ uhci_finish_completion(uhci, regs);
+
+ /* If the controller is stopped, we can finish these off right now */
+ if (uhci->is_stopped) {
+ uhci_free_pending_qhs(uhci);
+ uhci_free_pending_tds(uhci);
+ uhci_remove_pending_urbps(uhci);
+ }
+
+ if (uhci->need_rescan)
+ goto rescan;
+ uhci->scan_in_progress = 0;
+
+ if (list_empty(&uhci->urb_remove_list) &&
+ list_empty(&uhci->td_remove_list) &&
+ list_empty(&uhci->qh_remove_list))
+ uhci_clear_next_interrupt(uhci);
+ else
+ uhci_set_next_interrupt(uhci);
+
+ /* Wake up anyone waiting for an URB to complete */
+ wake_up_all(&uhci->waitqh);
+}