[PATCH] usb: gadget: Add driver for PLX USB338x

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds the gadget driver for PLX USB338x USB 3.0 Controllers. This is
the latest version 3.0 from upstream
(http://www.plxtech.com/products/usbcontrollers/usb3380).
---
 drivers/usb/gadget/Kconfig   |   11 +
 drivers/usb/gadget/Makefile  |    1 +
 drivers/usb/gadget/usb338x.c | 4493 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/usb338x.h |  952 +++++++++
 4 files changed, 5457 insertions(+)
 create mode 100644 drivers/usb/gadget/usb338x.c
 create mode 100644 drivers/usb/gadget/usb338x.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 3557c7e..7e059b4 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -429,6 +429,17 @@ config USB_NET2280
 	   dynamically linked module called "net2280" and force all
 	   gadget drivers to also be dynamically linked.
 
+config USB_USB338X
+	tristate "PLX 338x"
+	depends on PCI
+	help
+	   PLX USB 338x is a PCI based USB peripheral controller which
+	   supports full/high/super speed USB 3.0 data transfers.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "usb338x" and force all
+	   gadget drivers to also be dynamically linked.
+
 config USB_GOKU
 	tristate "Toshiba TC86C001 'Goku-S'"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5f150bc..f1cbad3 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -11,6 +11,7 @@ libcomposite-y			+= composite.o functions.o configfs.o u_f.o
 obj-$(CONFIG_USB_DUMMY_HCD)	+= dummy_hcd.o
 obj-$(CONFIG_USB_NET2272)	+= net2272.o
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
+obj-$(CONFIG_USB_USB338X)	+= usb338x.o
 obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
 obj-$(CONFIG_USB_PXA25X)	+= pxa25x_udc.o
 obj-$(CONFIG_USB_PXA27X)	+= pxa27x_udc.o
diff --git a/drivers/usb/gadget/usb338x.c b/drivers/usb/gadget/usb338x.c
new file mode 100644
index 0000000..e9eabd7
--- /dev/null
+++ b/drivers/usb/gadget/usb338x.c
@@ -0,0 +1,4493 @@
+/******************************************************************************
+ * Driver for PLX USB3380/USB3382 USB device controller
+ *
+ * Copyright (c) PLX Technology, Inc.
+ *
+ * PLX Technology Inc. licenses this source file under the GNU Lesser General
+ * Public License (LGPL) version 2.  This program is free software; the source
+ * file may be modified or redistributed under the terms of the LGPL and
+ * without express permission from PLX Technology.
+ *
+ * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY,
+ * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee
+ * or representations regarding the use of, or the results of the use of,
+ * the software and documentation in terms of correctness, accuracy,
+ * reliability, currentness, or otherwise; and you rely on the software,
+ * documentation and results solely at your own risk.
+ *
+ * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS,
+ * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES
+ * OF ANY KIND.
+ *
+ * Specs and errata are available from http://www.plxtech.com.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * File Name:
+ *
+ *     usb338x.c
+ *
+ * Revision: v3.0, 2-5-2014
+ *
+ * Description:
+ *
+ *     Initializes the driver and claims system resources for USB3 and USB2 on
+ *     Linux kernel version 3.8.
+ *
+ *     This driver works with most Linux gadget drivers, including
+ *     the File Storage, Serial, and Ethernet/RNDIS gadget drivers
+ *     as well as Gadget Zero and Gadgetfs.
+ *
+ *     MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
+ *     be enabled.  DMA is enabled by default.  Drivers using transfer
+ *     queues might use DMA chaining to remove IRQ latencies between
+ *     transfers excep when short OUT transfers happen.
+ *
+ *****************************************************************************/
+
+#undef  DEBUG       /* messages on error and most fault paths */
+#undef  VERBOSE     /* extra debug messages (success too) */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/prefetch.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#define  DEBUG
+#define  VERBOSE
+
+#define DRIVER_DESC     "PLX USB3380/USB3382 USB Peripheral Controller"
+#define DRIVER_VERSION  "usb3380/usb3382 v3.0"
+
+#define DMA_ADDR_INVALID    (~(dma_addr_t)0)
+#define EP_DONTUSE          13  /* nonzero */
+
+#define USE_RDK_LEDS        /* GPIO pins control three LEDs */
+
+static const char driver_name [] = "usb338x";
+static const char driver_desc [] = DRIVER_DESC;
+
+static const char ep0name [] = "ep0";
+static const char *const ep_name [9] =
+{
+    ep0name,
+    "ep-1", "ep-2", "ep-3", "ep-4",
+    "ep-5", "ep-6", "ep-7", "ep-8",
+};
+
+static unsigned int u32_i = 0;
+static int enhanced_mode = 1;
+static int n_ep = 5;
+
+#define     FIFO_SIZE_64                0
+#define     FIFO_SIZE_128               1
+#define     FIFO_SIZE_256               2
+#define     FIFO_SIZE_512               3
+#define     FIFO_SIZE_1024              4
+#define     FIFO_SIZE_2048              5
+#define     FIFO_SIZE_4096              6
+
+/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
+ * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
+ *
+ * The usb338x DMA engines are not tightly integrated with their FIFOs;
+ * not all cases are (yet) handled well in this driver or the silicon.
+ * Some gadget drivers work better with the dma support here than others.
+ * These two parameters let you use PIO or more aggressive DMA.
+ */
+static bool use_dma = 1;
+static bool use_dma_chaining = 0;
+static bool use_msi   = 1;
+
+/* "modprobe usb338x use_dma=n" etc */
+module_param (use_msi, bool, S_IRUGO);
+module_param (use_dma, bool, S_IRUGO);
+module_param (use_dma_chaining, bool, S_IRUGO);
+
+/* enable_suspend -- When enabled, the driver will respond to
+ * USB suspend requests by powering down the usb338x.  Otherwise,
+ * USB suspend requests will be ignored.  This is acceptable for
+ * self-powered devices
+ */
+static bool enable_suspend = 0;
+
+/* "modprobe usb338x enable_suspend=1" etc */
+module_param (enable_suspend, bool, S_IRUGO);
+
+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG)
+static char *type_string (u8 bmAttributes)
+{
+    switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK)
+    {
+    case USB_ENDPOINT_XFER_BULK:    return "bulk";
+    case USB_ENDPOINT_XFER_ISOC:    return "iso";
+    case USB_ENDPOINT_XFER_INT:     return "intr";
+    };
+    return "control";
+}
+#endif
+
+#include "usb338x.h"
+
+#define valid_bit   cpu_to_le32 (1 << VALID_BIT)
+#define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)
+
+static u32 ep_bit[9]      = {0, 17, 2, 19, 4, 1, 18, 3, 20};
+static u32 ep_key[9]      = {1, 0, 1, 0, 1, 1, 0, 1, 0};
+static u32 ep_enhanced[9] = {0x10, 0x60, 0x30, 0x80, 0x50, 0x20, 0x70, 0x40, 0x90};
+static u32 ep_legacy[5]   = {0x10, 0x20, 0x30, 0x40, 0x50};
+static u32 ne[9]          = {0, 1, 2, 3, 4, 1, 2, 3, 4};
+static u32 ep_reg_addr[9] = {0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0xC0};
+static u32 ep_pl[9]       = {0, 3, 4, 7, 8, 2, 5, 6, 9};
+
+static u32 u32_pciirqenb1;
+/*-------------------------------------------------------------------------*/
+
+#ifdef  CONFIG_USB_GADGET_DEBUG_FILES
+
+/* FIXME move these into procfs, and use seq_file.
+ * Sysfs _still_ doesn't behave for arbitrarily sized files,
+ * and also doesn't help products using this with 2.4 kernels.
+ */
+
+/* "function" sysfs attribute */
+static ssize_t
+show_function (struct device *_dev, struct device_attribute *attr, char *buf)
+{
+    struct usb338x  *dev = dev_get_drvdata (_dev);
+
+    if (!dev->driver
+            || !dev->driver->function
+            || strlen (dev->driver->function) > PAGE_SIZE)
+        return 0;
+
+    return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+
+
+static ssize_t
+usb338x_show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+    struct usb338x      *dev;
+    char                *next;
+    unsigned            size, t;
+    unsigned long       flags;
+    int                 i;
+    u32                 t1, t2;
+    const char          *s;
+
+    dev = dev_get_drvdata (_dev);
+    next = buf;
+    size = PAGE_SIZE;
+    spin_lock_irqsave (&dev->lock, flags);
+
+    if (dev->driver)
+        s = dev->driver->driver.name;
+    else
+        s = "(none)";
+
+    /* Main Control Registers */
+    t = scnprintf (next, size, "%s version " DRIVER_VERSION
+                   ", chiprev %04x, dma %s\n\n"
+                   "devinit %03x fifoctl %08x gadget '%s'\n"
+                   "pci irqenb0 %02x irqenb1 %08x "
+                   "irqstat0 %04x irqstat1 %08x\n",
+                   driver_name, dev->chiprev,
+                   use_dma
+                   ? (use_dma_chaining ? "chaining" : "enabled")
+                       : "disabled",
+                       readl (&dev->regs->devinit),
+                       readl (&dev->regs->fifoctl),
+                       s,
+                       readl (&dev->regs->pciirqenb0),
+                       readl (&dev->regs->pciirqenb1),
+                       readl (&dev->regs->irqstat0),
+                       readl (&dev->regs->irqstat1));
+    size -= t;
+    next += t;
+
+    /* USB Control Registers */
+    t1 = readl (&dev->usb->usbctl);
+    t2 = readl (&dev->usb->usbstat);
+
+    if (t1 & (1 << VBUS_PIN))
+    {
+        if (t2 & (1 << HIGH_SPEED_MODE))
+            s = "high speed";
+        else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+            s = "powered";
+        else
+            s = "full speed";
+        /* full speed bit (6) not working?? */
+    }
+    else
+        s = "not attached";
+
+    t = scnprintf (next, size,
+                   "stdrsp %08x usbctl %08x usbstat %08x "
+                   "addr 0x%02x (%s)\n",
+                   readl (&dev->usb->stdrsp), t1, t2,
+                   readl (&dev->usb->ouraddr), s);
+    size -= t;
+    next += t;
+
+    /* PCI Master Control Registers */
+
+    /* DMA Control Registers */
+
+    /* Configurable EP Control Registers */
+    for (i = 0; i < n_ep; i++)
+    {
+        struct usb338x_ep   *ep;
+
+        ep = &dev->ep [i];
+
+        if (i && !ep->desc)
+            continue;
+
+        t1 = readl (&ep->cfg->ep_cfg);
+        t2 = readl (&ep->regs->ep_rsp) & 0xff;
+        t = scnprintf (next, size,
+                       "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
+                       "irqenb %02x\n",
+                       ep->ep.name, t1, t2,
+                       (t2 & (1 << CLEAR_NAK_OUT_PACKETS))
+                       ? "NAK " : "",
+                       (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE))
+                       ? "hide " : "",
+                       (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR))
+                       ? "CRC " : "",
+                       (t2 & (1 << CLEAR_INTERRUPT_MODE))
+                       ? "interrupt " : "",
+                       (t2 & (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
+                       ? "status " : "",
+                       (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE))
+                       ? "NAKmode " : "",
+                       (t2 & (1 << CLEAR_ENDPOINT_TOGGLE))
+                       ? "DATA1 " : "DATA0 ",
+                       (t2 & (1 << CLEAR_ENDPOINT_HALT))
+                       ? "HALT " : "",
+                       readl (&ep->regs->ep_irqenb));
+        size -= t;
+        next += t;
+
+        t = scnprintf (next, size,
+                       "\tstat %08x avail %04x "
+                       "(ep%d%s-%s)%s\n",
+                       readl (&ep->regs->ep_stat),
+                       readl (&ep->regs->ep_avail),
+                       t1 & 0x0f, DIR_STRING (t1),
+                       type_string (t1 >> 8),
+                       ep->stopped ? "*" : "");
+        size -= t;
+        next += t;
+
+        if (!ep->dma)
+            continue;
+
+        t = scnprintf (next, size,
+                       "  dma\tctl %08x stat %08x count %08x\n"
+                       "\taddr %08x desc %08x\n",
+                       readl (&ep->dma->dmactl),
+                       readl (&ep->dma->dmastat),
+                       readl (&ep->dma->dmacount),
+                       readl (&ep->dma->dmaaddr),
+                       readl (&ep->dma->dmadesc));
+        size -= t;
+        next += t;
+
+    }
+
+    /* Indexed Registers */
+    // none yet
+
+    /* Statistics */
+    t = scnprintf (next, size, "\nirqs:  ");
+    size -= t;
+    next += t;
+
+    for (i = 0; i < n_ep; i++)
+    {
+        struct usb338x_ep   *ep;
+
+        ep = &dev->ep [i];
+
+        if (i && !ep->irqs)
+            continue;
+
+        t = scnprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs);
+        size -= t;
+        next += t;
+
+    }
+
+    t = scnprintf (next, size, "\n");
+    size -= t;
+    next += t;
+
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(registers, S_IRUGO, usb338x_show_registers, NULL);
+
+
+static ssize_t
+show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
+{
+    struct usb338x      *dev;
+    char                *next;
+    unsigned            size;
+    unsigned long       flags;
+    int                 i;
+
+    dev = dev_get_drvdata (_dev);
+    next = buf;
+    size = PAGE_SIZE;
+    spin_lock_irqsave (&dev->lock, flags);
+
+    for (i = 0; i < n_ep; i++)
+    {
+        struct usb338x_ep       *ep = &dev->ep [i];
+        struct usb338x_request  *req;
+        int                     t;
+
+        if (i != 0)
+        {
+            const struct usb_endpoint_descriptor    *d;
+
+            d = ep->desc;
+
+            if (!d)
+                continue;
+
+            t = d->bEndpointAddress;
+            t = scnprintf (next, size,
+                           "\n%s (ep%d%s-%s) max %04x %s fifo %d\n",
+                           ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
+                           (t & USB_DIR_IN) ? "in" : "out",
+                           ( {  char *val;
+                                switch (d->bmAttributes & 0x03)
+                                {
+                                case USB_ENDPOINT_XFER_BULK:
+                                    val = "bulk"; break;
+                                case USB_ENDPOINT_XFER_INT:
+                                    val = "intr"; break;
+                                default:
+                                    val = "iso"; break;
+                                }; val;
+                         } ),
+        usb_endpoint_maxp (d) & 0x1fff,
+        ep->dma ? "dma" : "pio", ep->fifo_size
+                      );
+        }
+        else   /* ep0 should only have one transfer queued */
+            t = scnprintf (next, size, "ep0 max 64 pio %s\n",
+                           ep->is_in ? "in" : "out");
+        if (t <= 0 || t > size)
+            goto done;
+
+        size -= t;
+        next += t;
+
+        if (list_empty (&ep->queue))
+        {
+            t = scnprintf (next, size, "\t(nothing queued)\n");
+
+            if (t <= 0 || t > size)
+                goto done;
+
+            size -= t;
+            next += t;
+            continue;
+
+        }
+
+        list_for_each_entry (req, &ep->queue, queue)
+        {
+            if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc))
+                t = scnprintf (next, size,
+                               "\treq %p len %d/%d "
+                               "buf %p (dmacount %08x)\n",
+                               &req->req, req->req.actual,
+                               req->req.length, req->req.buf,
+                               readl (&ep->dma->dmacount));
+            else
+                t = scnprintf (next, size,
+                               "\treq %p len %d/%d buf %p\n",
+                               &req->req, req->req.actual,
+                               req->req.length, req->req.buf);
+            if (t <= 0 || t > size)
+                goto done;
+
+            size -= t;
+            next += t;
+
+            if (ep->dma)
+            {
+                struct usb338x_dma  *td;
+
+                td = req->td;
+                t = scnprintf (next, size, "\t    td %08x "
+                               " count %08x buf %08x desc %08x\n",
+                               (u32) req->td_dma,
+                               le32_to_cpu (td->dmacount),
+                               le32_to_cpu (td->dmaaddr),
+                               le32_to_cpu (td->dmadesc));
+
+                if (t <= 0 || t > size)
+                    goto done;
+
+                size -= t;
+                next += t;
+            }
+        }
+    }
+
+done:
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL);
+
+
+#else
+
+#define device_create_file(a,b) (0)
+#define device_remove_file(a,b) do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void write_fifo (struct usb338x_ep *ep, struct usb_request *req);
+static void complete_req (struct usb338x_ep *ep, struct usb338x_request *req, int status);
+static void scan_dma_completions (struct usb338x_ep *ep);
+static inline void spin_stop_dma (struct usb338x_dma_regs __iomem *dma);
+static inline void dma_done (struct usb338x_ep *ep, struct usb338x_request *req, u32 dmacount, int status);
+static void out_flush (struct usb338x_ep *ep);
+static void  restart_dma (struct usb338x_ep *ep);
+static int read_fifo (struct usb338x_ep *ep, struct usb338x_request *req);
+static void abort_dma (struct usb338x_ep *ep);
+
+/* handle with 64 byte packets: packet per irq.
+ * also works for dma-capable endpoints, in pio mode or just
+ * to manually advance the queue after short OUT transfers.
+ */
+static void
+handle_ep_small (struct usb338x_ep *ep)
+{
+    struct usb338x_request  *req;
+    u32                     t;
+    /* 0 error, 1 mid-data, 2 done */
+    int                     mode = 1;
+
+    if (!list_empty (&ep->queue))
+        req = list_entry (ep->queue.next,
+                          struct usb338x_request, queue);
+    else
+        req = NULL;
+
+    /* ack all, and handle what we care about */
+    t = readl (&ep->regs->ep_stat);
+
+    ep->irqs++;
+
+#if 0
+    VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n",
+            ep->ep.name, t, req ? &req->req : 0);
+#endif
+
+    if (!ep->is_in)
+    {
+        writel (t & ~(1 << NAK_PACKETS), &ep->regs->ep_stat);
+    }
+    else
+    {
+        /* Added for 2282 */
+        writel (t, &ep->regs->ep_stat);
+    }
+
+    /* for ep0, monitor token irqs to catch data stage length errors
+     * and to synchronize on status.
+     *
+     * also, to defer reporting of protocol stalls ... here's where
+     * data or status first appears, handling stalls here should never
+     * cause trouble on the host side..
+     *
+     * control requests could be slightly faster without token synch for
+     * status, but status can jam up that way.
+     */
+    if (unlikely (ep->num == 0))
+    {
+        if (ep->is_in)
+        {
+            /* status; stop NAKing */
+            if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
+            {
+                if (ep->dev->protocol_stall)
+                {
+                    ep->stopped = 1;
+
+                    writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                              ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+                              (1 << SET_ENDPOINT_HALT),
+                              &ep->regs->ep_rsp);
+                }
+
+                if (!req)
+                {
+                    writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                              (1 << CLEAR_NAK_OUT_PACKETS) |
+                              (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                              &ep->regs->ep_rsp);
+
+                    ep->stopped = 1;
+                }
+
+                mode = 2;
+                /* reply to extra IN data tokens with a zlp */
+            }
+            else if (t & (1 << DATA_IN_TOKEN_INTERRUPT))
+            {
+                if (ep->dev->protocol_stall)
+                {
+                    ep->stopped = 1;
+
+                    writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                              ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+                              (1 << SET_ENDPOINT_HALT),
+                              &ep->regs->ep_rsp);
+
+                    mode = 2;
+                }
+                else if (ep->responded &&
+                         !req && !ep->stopped)
+                    write_fifo (ep, NULL);
+            }
+        }
+        else
+        {
+            /* status; stop NAKing */
+            if (t & (1 << DATA_IN_TOKEN_INTERRUPT))
+            {
+                if (ep->dev->protocol_stall)
+                {
+                    ep->stopped = 1;
+
+                    writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                              ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+                              (1 << SET_ENDPOINT_HALT),
+                              &ep->regs->ep_rsp);
+                }
+                mode = 2;
+                /* an extra OUT token is an error */
+            }
+            else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
+                      && req
+                      && req->req.actual == req->req.length)
+                     || (ep->responded && !req))
+            {
+                ep->dev->protocol_stall = 1;
+
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+                          (1 << SET_ENDPOINT_HALT),
+                          &ep->regs->ep_rsp);
+
+                ep->stopped = 1;
+
+                if (req)
+                    complete_req (ep, req, -EOVERFLOW);
+
+                req = NULL;
+            }
+        }
+    }
+
+    if (unlikely (!req))
+        return;
+
+    /* manual DMA queue advance after short OUT */
+    if (likely (ep->dma != 0))
+    {
+        if (t & (1 << SHORT_OUT_PACKET_RECEIVED_INTERRUPT))
+        {
+            u32 count;
+            int stopped = ep->stopped;
+
+            /* TRANSFERRED works around OUT_DONE erratum 0112.
+             * we expect (N <= maxpacket) bytes; host wrote M.
+             * iff (M < N) we won't ever see a DMA interrupt.
+             */
+            ep->stopped = 1;
+
+            for (count = 0; ; t = readl (&ep->regs->ep_stat))
+            {
+
+                /* any preceding dma transfers must finish.
+                 * dma handles (M >= N), may empty the queue
+                 */
+                scan_dma_completions (ep);
+                if (unlikely (list_empty (&ep->queue)
+                              || ep->out_overflow))
+                {
+                    req = NULL;
+                    break;
+                }
+                req = list_entry (ep->queue.next,
+                                  struct usb338x_request, queue);
+
+                /* here either (M < N), a "real" short rx;
+                 * or (M == N) and the queue didn't empty
+                 */
+                if (likely (t & (1 << FIFO_EMPTY)))
+                {
+                    count = readl (&ep->dma->dmacount);
+                    count &= DMA_TRANSFER_MAX_LENGTH;
+
+                    if (readl (&ep->dma->dmadesc) != req->td_dma)
+                        req = NULL;
+
+                    break;
+                }
+                udelay(1);
+            }
+
+            /* stop DMA, leave ep NAKing */
+            writel ((1 << DMA_ABORT), &ep->dma->dmastat);
+            spin_stop_dma (ep->dma);
+
+            if (likely (req))
+            {
+                req->td->dmacount = 0;
+                t = readl (&ep->regs->ep_avail);
+                dma_done (ep, req, count,
+                          (ep->out_overflow || t)
+                          ? -EOVERFLOW : 0);
+            }
+
+            /* also flush to prevent erratum 0106 trouble */
+            if (unlikely (ep->out_overflow
+                          || (ep->dev->chiprev == 0x0100
+                              && ep->dev->gadget.speed
+                              == USB_SPEED_FULL)))
+            {
+                out_flush (ep);
+                ep->out_overflow = 0;
+            }
+
+            /* (re)start dma if needed, stop NAKing */
+            ep->stopped = stopped;
+
+            if (!list_empty (&ep->queue))
+                restart_dma (ep);
+        }
+        else
+            DEBUG (ep->dev, "%s dma ep_stat %08x ??\n",
+                   ep->ep.name, t);
+
+        return;
+
+        /* data packet(s) received (in the fifo, OUT) */
+    }
+    else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT))
+    {
+        if (read_fifo (ep, req) && ep->num != 0)
+            mode = 2;
+
+        /* data packet(s) transmitted (IN) */
+    }
+    else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
+    {
+        unsigned    len;
+
+        len = req->req.length - req->req.actual;
+
+        if (len > ep->ep.maxpacket)
+            len = ep->ep.maxpacket;
+
+        req->req.actual += len;
+
+        /* if we wrote it all, we're usually done */
+        if (req->req.actual == req->req.length)
+        {
+            if (ep->num == 0)
+            {
+                /* send zlps until the status stage */
+            }
+            else if (!req->req.zero || len != ep->ep.maxpacket)
+                mode = 2;
+        }
+
+        /* there was nothing to do ...  */
+    }
+    else if (mode == 1)
+        return;
+
+    /* done */
+    if (mode == 2)
+    {
+        /* stream endpoints often resubmit/unlink in completion */
+        complete_req (ep, req, 0);
+
+        /* maybe advance queue to next request */
+        if (ep->num == 0)
+        {
+            /* NOTE:  usb338x could let gadget driver start the
+             * status stage later. since not all controllers let
+             * them control that, the api doesn't (yet) allow it.
+             */
+            if (!ep->stopped)
+            {
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          (1 << CLEAR_NAK_OUT_PACKETS) |
+                          (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                          &ep->regs->ep_rsp);
+
+                ep->stopped = 1;
+            }
+
+            req = NULL;
+        }
+        else
+        {
+            if (!list_empty (&ep->queue) && !ep->stopped)
+                req = list_entry (ep->queue.next,
+                                  struct usb338x_request, queue);
+            else
+                req = NULL;
+
+            if (req && !ep->is_in)
+            {
+                u32 reg_value;
+
+                reg_value = readl (&ep->regs->ep_stat);
+
+                if ((reg_value & (1 << NAK_PACKETS)) != 0)
+                    writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+            }
+        }
+    }
+
+    /* is there a buffer for the next packet?
+     * for best streaming performance, make sure there is one.
+     */
+    if (req && !ep->stopped)
+    {
+
+        /* load IN fifo with next packet (may be zlp) */
+        if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
+            write_fifo (ep, &req->req);
+    }
+}
+
+
+static struct usb338x_ep *
+get_ep_by_addr (struct usb338x *dev, u16 wIndex)
+{
+    struct usb338x_ep   *ep;
+
+    if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+        return &dev->ep [0];
+
+    list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list)
+    {
+        u8  bEndpointAddress;
+
+        if (!ep->desc)
+            continue;
+
+        bEndpointAddress = ep->desc->bEndpointAddress;
+
+        if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+            continue;
+
+        if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+            return ep;
+    }
+
+    return NULL;
+}
+
+
+static void Defect7374_DisableDataEps (struct usb338x *dev);
+static void set_fifo_bytecount (struct usb338x_ep *ep, unsigned count);
+static void allow_status (struct usb338x_ep *ep);
+static void ep_stdrsp (struct usb338x_ep *ep, int value, int wedged);
+static void ep_stall (struct usb338x_ep *ep, int stall);
+static void resume_dma (struct usb338x_ep *ep);
+static void ep_stop_dma (struct usb338x_ep *ep);
+
+
+static void
+handle_stat0_irqs (struct usb338x *dev, u32 stat)
+{
+    struct usb338x_ep   *ep;
+    u32                 num, scratch;
+    u32                 value;
+    u32                 reg_value;
+    u32                 Scratch, FsmValue;
+    u32                 AckWaitTimeout, State;
+
+    /* most of these don't need individual acks */
+    stat &= ~(1 << INTA_ASSERTED);
+
+    if (!stat)
+        return;
+
+    // DEBUG (dev, "irqstat0 %04x\n", stat);
+
+    /* starting a control request? */
+    if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT_STATUS)))
+    {
+        union
+        {
+            u32                     raw [2];
+            struct usb_ctrlrequest  r;
+        } u;
+
+        int                         tmp;
+        struct usb338x_request      *req;
+        u32                         val;
+
+        if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+        {
+            val = readl (&dev->usb->usbstat);
+
+            if (val & (1 << SUPER_SPEED_MODE))
+            {
+                dev->gadget.speed = USB_SPEED_SUPER;
+                dev->ep[0].ep.maxpacket = EP0_SS_MAX_PACKET_SIZE;
+            }
+            else if (val & (1 << HIGH_SPEED_MODE))
+            {
+                dev->gadget.speed = USB_SPEED_HIGH;
+                dev->ep[0].ep.maxpacket = EP0_HS_MAX_PACKET_SIZE;
+            }
+            else
+            {
+                dev->gadget.speed = USB_SPEED_FULL;
+                dev->ep[0].ep.maxpacket = EP0_HS_MAX_PACKET_SIZE;
+            }
+
+            value = readl (&dev->regs->gpioctl);
+
+            switch (dev->gadget.speed)
+            {
+            case USB_SPEED_HIGH:        /* green */
+                value &= ~(1 << GPIO0_DATA);
+                value |= (1 << GPIO1_DATA);
+
+                break;
+
+            case USB_SPEED_FULL:        /* red */
+                value &= ~(1 << GPIO1_DATA);
+                value |= (1 << GPIO0_DATA);
+
+                break;
+
+            default:            /* (off/black) */
+                value &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA));
+
+                break;
+            }
+
+            writel (value, &dev->regs->gpioctl);
+
+            DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed));
+        }
+
+        ep = &dev->ep [0];
+        ep->irqs++;
+
+        /* make sure any leftover request state is cleared */
+        stat &= ~(1 << ENDPOINT_0_INTERRUPT_STATUS);
+
+        while (!list_empty (&ep->queue))
+        {
+            req = list_entry (ep->queue.next,
+                              struct usb338x_request, queue);
+
+            complete_req (ep, req, (req->req.actual == req->req.length)
+                          ? 0 : -EPROTO);
+        }
+
+        ep->stopped = 0;
+        ep->is_halt = 0;
+        dev->protocol_stall = 0;
+
+#if 0
+        tmp = 0;
+
+        writel (tmp | (1 << TIMEOUT)
+                | (1 << USB_STALL_SENT)
+                | (1 << USB_IN_NAK_SENT)
+                | (1 << USB_IN_ACK_RCVD)
+                | (1 << USB_OUT_NAK_SENT)
+                | (1 << USB_OUT_ACK_SENT)
+                | (1 << SHORT_OUT_PACKET_DONE_INTERRUPT)
+                | (1 << SHORT_OUT_PACKET_RECEIVED_INTERRUPT)
+                | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+                | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+                | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+                | (1 << DATA_IN_TOKEN_INTERRUPT)
+                , &ep->regs->ep_stat);
+#endif
+
+        u.raw [0] = readl (&dev->usb->setupdw0);
+        u.raw [1] = readl (&dev->usb->setupdw1);
+
+        cpu_to_le32s (&u.raw [0]);
+        cpu_to_le32s (&u.raw [1]);
+
+        tmp = 0;
+
+#define w_value     le16_to_cpu(u.r.wValue)
+#define w_index     le16_to_cpu(u.r.wIndex)
+#define w_length    le16_to_cpu(u.r.wLength)
+
+        /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */
+        writel (SCRATCH, &dev->regs->idxaddr);
+        Scratch = readl (&dev->regs->idxdata);
+
+        FsmValue = Scratch & 0xf << DEFECT7374_FSM_FIELD;
+        Scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+        if ((FsmValue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) &&
+                (u.r.bRequestType & USB_DIR_IN))
+        {
+            /* This is the first Control Read for this connection: */
+            if ( readl(&dev->usb->usbstat) & ( 1 << SUPER_SPEED_MODE))
+            {
+
+                // Connection is SS:
+                for (AckWaitTimeout = 0; AckWaitTimeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; AckWaitTimeout++)
+                {
+                    State = readl (&dev->plregs->pl_ep_status_1) & (0xff << STATE);
+
+                    if ((State >= (ACK_GOOD_NORMAL << STATE)) && (State <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE)))
+                    {
+                        Scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+
+                        break;
+                    }
+
+                    // We have not yet received host's Data Phase ACK
+                    //  - Wait and try again.
+                    udelay (DEFECT_7374_PROCESSOR_WAIT_TIME);
+
+                    continue;
+                }
+
+                printk ("INFO: Defect 7374 workaround waited about %duSec for Control Read Data Phase ACK\n", DEFECT_7374_PROCESSOR_WAIT_TIME * AckWaitTimeout);
+
+                if (AckWaitTimeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS)
+                    printk ("FAIL: Defect 7374 workaround waited but failed to detect SS host's data phase ACK. PL_EP_STATUS_1(23:16): Expected from 0x11 to 0x16 got 0x%2.2x.\n", State >> STATE);
+            }
+            else
+            {
+                // Connection is NOT SS:
+                //  - Connection must be FS or HS.
+                //  - This FSM state should allow workaround software to
+                // run after the next USB connection.
+                Scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+            }
+
+            // Restore data EPs to their pre-workaround settings (disabled,
+            // initialized, and other details).
+            Defect7374_DisableDataEps (dev);
+
+            writel (SCRATCH, &dev->regs->idxaddr);
+            writel (Scratch, &dev->regs->idxdata);
+        }
+
+        /* ack the irq */
+        writel (1 << SETUP_PACKET_INTERRUPT_STATUS, &dev->regs->irqstat0);
+        stat ^= (1 << SETUP_PACKET_INTERRUPT_STATUS);
+
+        /* watch control traffic at the token level, and force
+         * synchronization before letting the status stage happen.
+         * FIXME ignore tokens we'll NAK, until driver responds.
+         * that'll mean a lot less irqs for some drivers.
+         */
+        ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+
+        if (ep->is_in)
+        {
+            scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+                      | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+                      | (1 << DATA_IN_TOKEN_INTERRUPT);
+
+            reg_value = readl (&ep->regs->ep_stat);
+
+            if ((reg_value & (1 << NAK_PACKETS)) != 0)
+                writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+        }
+        else
+            scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+                      | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+                      | (1 << DATA_IN_TOKEN_INTERRUPT);
+
+        writel (scratch, &dev->epregs [0].ep_irqenb);
+
+        /* we made the hardware handle most lowlevel requests;
+         * everything else goes uplevel to the gadget code.
+         */
+        ep->responded = 1;
+
+        if (dev->gadget.speed == USB_SPEED_SUPER)
+        {
+            switch (u.r.bRequest)
+            {
+                struct usb338x_ep   *e;
+                u16                 status;
+
+            case USB_REQ_SET_CONFIGURATION:
+
+                if (w_value == 0)
+                {
+                    dev->addressed_state = true;
+                }
+                else
+                {
+                    dev->addressed_state = false;
+                }
+
+                goto usb3_delegate;
+
+            case USB_REQ_GET_STATUS:
+
+                switch (u.r.bRequestType)
+                {
+
+                case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+
+                    status = dev->wakeup_enable ? 0x02 : 0x00;
+
+                    if (dev->selfpowered)
+                    {
+                        status |= 1 << 0;
+                    }
+
+                    if (dev->gadget.speed == USB_SPEED_SUPER)
+                    {
+                        status |= ( dev->u1_enable << 2 |
+                                    dev->u2_enable << 3 |
+                                    dev->ltm_enable << 4);
+                    }
+                    else if (dev->wakeup_enable)
+                    {
+                        status |= 0x01;
+                    }
+
+                    writel (0, &dev->epregs[0].ep_irqenb);
+
+                    set_fifo_bytecount (ep, sizeof(status));
+
+                    writel ((__force u32)status, &dev->epregs[0].ep_data);
+
+                    allow_status(ep);
+
+                    break;
+
+                case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+
+                    e = get_ep_by_addr (dev, w_index);
+
+                    if (!e)
+                    {
+                        goto do_stall;
+                    }
+
+                    status = readl (&e->regs->ep_rsp) &
+                             (1 << CLEAR_ENDPOINT_HALT);
+
+                    writel (0, &dev->epregs [0].ep_irqenb);
+
+                    set_fifo_bytecount(ep, sizeof(status));
+
+                    writel ((__force u32)status, &dev->epregs[0].ep_data);
+
+                    allow_status(ep);
+
+                    break;
+
+                default:
+                    goto usb3_delegate;
+                }
+
+                break;
+
+            case USB_REQ_CLEAR_FEATURE:
+
+                switch (u.r.bRequestType)
+                {
+
+                case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+
+                    if (dev->gadget.speed == USB_SPEED_SUPER && !dev->addressed_state)
+                    {
+                        switch (w_value)
+                        {
+
+                        case USB_DEVICE_U1_ENABLE:
+
+                            dev->u1_enable = 0;
+
+                            writel (readl(&dev->usb->usbctl2) &
+                                    ~(1 << U1_ENABLE),
+                                    &dev->usb->usbctl2);
+
+                            allow_status(ep);
+
+                            goto next_endpoints;
+
+                        case USB_DEVICE_U2_ENABLE:
+
+                            dev->u2_enable = 0;
+
+                            writel (readl(&dev->usb->usbctl2) &
+                                    ~(1 << U2_ENABLE),
+                                    &dev->usb->usbctl2);
+
+                            allow_status(ep);
+
+                            goto next_endpoints;
+
+                        case USB_DEVICE_LTM_ENABLE:
+
+                            dev->ltm_enable = 0;
+
+                            writel (readl(&dev->usb->usbctl2) &
+                                    ~(1 << LTM_ENABLE),
+                                    &dev->usb->usbctl2);
+
+                            allow_status(ep);
+
+                            goto next_endpoints;
+
+                        default:
+                            break;
+                        }
+                    }
+
+                    if (w_value == USB_DEVICE_REMOTE_WAKEUP)
+                    {
+                        dev->wakeup_enable = 0;
+
+                        writel (readl(&dev->usb->usbctl) &
+                                ~(1 << REMOTE_WAKEUP_ENABLE),
+                                &dev->usb->usbctl);
+
+                        allow_status(ep);
+
+                        break;
+                    }
+
+                    goto usb3_delegate;
+
+                case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+
+                    e = get_ep_by_addr (dev, w_index);
+
+                    if (!e)
+                        goto do_stall;
+
+                    if (w_value != USB_ENDPOINT_HALT)
+                        goto do_stall;
+
+                    VDEBUG(dev, "%s clear halt\n", e->ep.name);
+
+                    ep_stall (e, false);
+
+                    if (!list_empty(&e->queue) && e->td_dma)
+                    {
+                        restart_dma (e);
+                    }
+
+                    writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                              (1 << CLEAR_NAK_OUT_PACKETS) |
+                              (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                              &ep->regs->ep_rsp);
+
+                    ep->stopped = 1;
+
+                    break;
+
+                default:
+                    goto usb3_delegate;
+                }
+
+                break;
+
+            case USB_REQ_SET_FEATURE:
+
+                switch (u.r.bRequestType)
+                {
+
+                case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+
+                    if (dev->gadget.speed == USB_SPEED_SUPER && !dev->addressed_state)
+                    {
+                        switch (w_value)
+                        {
+
+                        case USB_DEVICE_U1_ENABLE:
+
+                            dev->u1_enable = 1;
+
+                            writel (readl(&dev->usb->usbctl2) |
+                                    (1 << U1_ENABLE),
+                                    &dev->usb->usbctl2);
+
+                            allow_status(ep);
+
+                            goto next_endpoints;
+
+                        case USB_DEVICE_U2_ENABLE:
+
+                            dev->u2_enable = 1;
+
+                            writel (readl(&dev->usb->usbctl2) |
+                                    (1 << U2_ENABLE),
+                                    &dev->usb->usbctl2);
+
+                            allow_status(ep);
+
+                            goto next_endpoints;
+
+                        case USB_DEVICE_LTM_ENABLE:
+
+                            dev->ltm_enable = 1;
+
+                            writel (readl(&dev->usb->usbctl2) |
+                                    (1 << LTM_ENABLE),
+                                    &dev->usb->usbctl2);
+
+                            allow_status(ep);
+
+                            goto next_endpoints;
+
+                        default:
+                            break;
+                        }
+                    }
+
+                    if (w_value == USB_DEVICE_REMOTE_WAKEUP)
+                    {
+
+                        dev->wakeup_enable = 1;
+
+                        writel (readl(&dev->usb->usbctl) |
+                                (1 << REMOTE_WAKEUP_ENABLE),
+                                &dev->usb->usbctl);
+
+                        allow_status(ep);
+
+                        break;
+                    }
+
+                    goto usb3_delegate;
+
+                case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+
+                    e = get_ep_by_addr (dev, w_index);
+
+                    if (!e)
+                    {
+                        goto do_stall;
+                    }
+
+                    if (w_value != USB_ENDPOINT_HALT)
+                    {
+                        goto do_stall;
+                    }
+
+                    ep_stdrsp (e, true, false);
+
+                    allow_status(ep);
+
+                    break;
+
+                default:
+                    goto usb3_delegate;
+                }
+
+                break;
+
+            default:
+
+usb3_delegate:
+                VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x "
+                        "ep_cfg %08x\n",
+                        u.r.bRequestType, u.r.bRequest,
+                        w_value, w_index, w_length,
+                        readl (&ep->cfg->ep_cfg));
+
+                ep->responded = 0;
+
+                spin_unlock (&dev->lock);
+                tmp = dev->driver->setup (&dev->gadget, &u.r);
+                spin_lock (&dev->lock);
+            }
+        }
+        else if (dev->gadget.speed == USB_SPEED_HIGH ||
+                 dev->gadget.speed == USB_SPEED_FULL )
+        {
+
+            switch (u.r.bRequest)
+            {
+            case USB_REQ_GET_STATUS:
+            {
+                struct usb338x_ep   *e;
+                __le32              status;
+
+                /* hw handles device and interface status */
+                if (u.r.bRequestType != (USB_DIR_IN | USB_RECIP_ENDPOINT))
+                    goto delegate;
+
+                if ((e = get_ep_by_addr (dev, w_index)) == 0
+                        || w_length > 2)
+                    goto do_stall;
+
+                if (readl (&e->regs->ep_rsp)
+                        & (1 << SET_ENDPOINT_HALT))
+                    status = cpu_to_le32 (1);
+                else
+                    status = cpu_to_le32 (0);
+
+                /* don't bother with a request object! */
+                writel (0, &dev->epregs [0].ep_irqenb);
+
+                reg_value = readl(&ep->cfg->ep_cfg) & (~(0x07 << EP_FIFO_BYTE_COUNT));
+                writel (reg_value | (w_length << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
+
+                writel ((__force u32)status, &dev->epregs [0].ep_data);
+
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          (1 << CLEAR_NAK_OUT_PACKETS) |
+                          (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                          &ep->regs->ep_rsp);
+
+                ep->stopped = 1;
+
+                VDEBUG (dev, "%s stat %02x\n", e->ep.name, status);
+
+                goto next_endpoints;
+            }
+
+            break;
+
+            case USB_REQ_CLEAR_FEATURE:
+            {
+                struct usb338x_ep   *e;
+
+                /* hw handles device features */
+                if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+                    goto delegate;
+
+                if (w_value != USB_ENDPOINT_HALT
+                        || w_length != 0)
+                    goto do_stall;
+
+                if ((e = get_ep_by_addr (dev, w_index)) == 0)
+                    goto do_stall;
+
+                if (e->wedged)
+                {
+                    VDEBUG(dev, "%s wedged, halt not cleared\n",
+                           e->ep.name);
+                }
+                else
+                {
+                    VDEBUG(dev, "%s clear halt\n", e->ep.name);
+
+                    writel (  (1 << CLEAR_ENDPOINT_HALT) |
+                              (1 << CLEAR_ENDPOINT_TOGGLE) |
+                              ((e->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS),
+                              &e->regs->ep_rsp);
+
+                    if (!list_empty(&e->queue) && e->td_dma)
+                    {
+                        restart_dma (e);
+                    }
+
+                }
+
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          (1 << CLEAR_NAK_OUT_PACKETS) |
+                          (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                          &ep->regs->ep_rsp);
+
+                ep->stopped = 1;
+
+                goto next_endpoints;
+            }
+
+            break;
+
+            case USB_REQ_SET_FEATURE:
+            {
+                struct usb338x_ep   *e;
+
+                /* hw handles device features */
+                if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+                    goto delegate;
+
+                if (w_value != USB_ENDPOINT_HALT
+                        || w_length != 0)
+                    goto do_stall;
+
+                if ((e = get_ep_by_addr (dev, w_index)) == 0)
+                    goto do_stall;
+
+                if (e->ep.name == ep0name)
+                    goto do_stall;
+
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          ((e->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+                          (1 << SET_ENDPOINT_HALT),
+                          &e->regs->ep_rsp);
+
+                if (e->dma)
+                    abort_dma (e);
+
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          (1 << CLEAR_NAK_OUT_PACKETS) |
+                          (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                          &ep->regs->ep_rsp);
+
+                ep->stopped = 1;
+
+                VDEBUG (dev, "%s set halt\n", e->ep.name);
+
+                goto next_endpoints;
+            }
+
+            break;
+
+            default:
+delegate:
+                VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x "
+                        "ep_cfg %08x\n",
+                        u.r.bRequestType, u.r.bRequest,
+                        w_value, w_index, w_length,
+                        readl (&ep->cfg->ep_cfg));
+
+                ep->responded = 0;
+
+                spin_unlock (&dev->lock);
+                tmp = dev->driver->setup (&dev->gadget, &u.r);
+                spin_lock (&dev->lock);
+            }
+        }
+        else
+        {
+            VDEBUG (dev, "gadget speed UNKNOWN\n");
+        }
+
+        /* stall ep0 on error */
+        if (tmp < 0)
+        {
+
+do_stall:
+            VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n",
+                    u.r.bRequestType, u.r.bRequest, tmp);
+
+            dev->protocol_stall = 1;
+
+            if (dev->gadget.speed == USB_SPEED_SUPER)
+            {
+                /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
+                ep_stall (ep, true);
+
+                //ep->responded = 1;
+            }
+        }
+
+        /* some in/out token irq should follow; maybe stall then.
+         * driver must queue a request (even zlp) or halt ep0
+         * before the host times out.
+         */
+    }
+
+#undef  w_value
+#undef  w_index
+#undef  w_length
+
+next_endpoints:
+
+    /* endpoint data irq ? */
+    scratch = stat & 0x7f;
+    stat &= ~0x7f;
+
+    for (num = 0; scratch; num++)
+    {
+        u32     t;
+
+        /* do this endpoint's FIFO and queue need tending? */
+        t = 1 << num;
+
+        if ((scratch & t) == 0)
+            continue;
+        scratch ^= t;
+
+        ep = &dev->ep [num];
+
+        handle_ep_small (ep);
+    }
+
+    if (stat)
+        DEBUG (dev, "unhandled irqstat0 %08x\n", stat);
+}
+
+
+static void stop_activity (struct usb338x *dev, struct usb_gadget_driver *driver);
+static void ep0_start (struct usb338x *dev);
+static inline void stop_dma (struct usb338x_dma_regs __iomem *dma);
+
+#define DMA_INTERRUPTS ( \
+                         (1 << DMA_3_INTERRUPT) \
+                         | (1 << DMA_2_INTERRUPT) \
+                         | (1 << DMA_1_INTERRUPT) \
+                         | (1 << DMA_0_INTERRUPT))
+#define PCI_ERROR_INTERRUPTS ( \
+                               (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \
+                               | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \
+                               | (1 << 17))
+
+
+static void
+handle_stat1_irqs (struct usb338x *dev, u32 stat)
+{
+    struct usb338x_ep   *ep;
+    u32                 tmp, num, mask, scratch;
+    u32                 r_dmacount;
+
+    /* after disconnect there's nothing else to do! */
+    tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
+    mask = (1 << SUPER_SPEED_MODE) | (1 << HIGH_SPEED_MODE) | (1 << FULL_SPEED_MODE);
+
+    /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
+     * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
+     * both HIGH_SPEED_MODE and FULL_SPEED_MODE clear (as ROOT_PORT_RESET_INTERRUPT
+     * only indicates a change in the reset state).
+     */
+    if (stat & tmp)
+    {
+        writel (tmp, &dev->regs->irqstat1);
+
+        if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
+                && ((readl (&dev->usb->usbstat) & mask) == 0))
+                || ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) )
+                && ( dev->gadget.speed != USB_SPEED_UNKNOWN))
+        {
+            DEBUG (dev, "disconnect %s\n",
+                   dev->driver->driver.name);
+
+            stop_activity (dev, dev->driver);
+
+            ep0_start (dev);
+
+            return;
+        }
+
+        stat &= ~tmp;
+
+        /* vBUS can bounce ... one of many reasons to ignore the
+         * notion of hotplug events on bus connect/disconnect!
+         */
+        if (!stat)
+            return;
+    }
+
+    /* NOTE: chip stays in PCI D0 state for now, but it could
+     * enter D1 to save more power
+     */
+    tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
+
+    if (stat & tmp)
+    {
+        writel (tmp, &dev->regs->irqstat1);
+
+        if (stat & (1 << SUSPEND_REQUEST_INTERRUPT))
+        {
+            if (dev->driver->suspend)
+                dev->driver->suspend (&dev->gadget);
+
+            if (!enable_suspend)
+                stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
+        }
+        else
+        {
+            if (dev->driver->resume)
+                dev->driver->resume (&dev->gadget);
+        }
+
+        stat &= ~tmp;
+    }
+
+    /* clear any other status/irqs */
+    if (stat)
+    {
+        writel (stat, &dev->regs->irqstat1);
+    }
+
+    stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
+              | (1 << RESUME_INTERRUPT)
+              | (1 << SOF_DOWNCOUNT_INTERRUPT)
+              | (1 << SOF_INTERRUPT));
+
+    if (!stat)
+    {
+        return;
+    }
+
+    // DEBUG (dev, "irqstat1 %08x\n", stat);
+
+    /* DMA status */
+    scratch = stat & DMA_INTERRUPTS;
+    stat &= ~DMA_INTERRUPTS;
+    scratch >>= 9;
+
+    for (num = 0; scratch; num++)
+    {
+        struct usb338x_dma_regs __iomem *dma;
+        tmp = 1 << num;
+
+        if ((tmp & scratch) == 0)
+            continue;
+
+        scratch ^= tmp;
+
+        ep = &dev->ep [num + 1];
+        dma = ep->dma;
+
+        if (!dma)
+            continue;
+
+        /* clear ep's dma status */
+        tmp = readl (&dma->dmastat);
+        writel (tmp, &dma->dmastat);
+
+        // dma sync
+        r_dmacount = readl (&dma->dmacount);
+
+        if (!ep->is_in &&
+                (tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) &&
+                ((r_dmacount & 0x00FFFFFF) != 0))
+        {
+            continue;
+        }
+
+        /* chaining should stop on abort, short OUT from fifo,
+         * or (stat0 codepath) short OUT transfer.
+         */
+        if (!use_dma_chaining)
+        {
+            if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))
+                    == 0)
+            {
+                DEBUG (ep->dev, "DMA_TRANSACTION_DONE_INTERRUPT - %s no xact done? %08x\n",
+                       ep->ep.name, tmp);
+
+                continue;
+            }
+
+            stop_dma (ep->dma);
+        }
+
+        /* OUT transfers terminate when the data from the
+         * host is in our memory.  Process whatever's done.
+         * On this path, we know transfer's last packet wasn't
+         * less than req->length. NAK_PACKETS may be set,
+         * or the FIFO may already be holding new packets.
+         *
+         * IN transfers can linger in the FIFO for a very
+         * long time ... we ignore that for now, accounting
+         * precisely (like PIO does) needs per-packet irqs
+         */
+        scan_dma_completions (ep);
+
+        /* disable dma on inactive queues; else maybe restart */
+        if (list_empty (&ep->queue))
+        {
+            if (use_dma_chaining)
+                stop_dma (ep->dma);
+        }
+        else
+        {
+            tmp = readl (&dma->dmactl);
+            if (!use_dma_chaining || (tmp & (1 << DMA_ENABLE)) == 0)
+                restart_dma (ep);
+
+            else if (ep->is_in && use_dma_chaining)
+            {
+                struct usb338x_request  *req;
+                __le32          dmacount;
+
+                /* the descriptor at the head of the chain
+                 * may still have VALID_BIT clear; that's
+                 * used to trigger changing DMA_FIFO_VALIDATE
+                 * (affects automagic zlp writes).
+                 */
+                req = list_entry (ep->queue.next,
+                                  struct usb338x_request, queue);
+                dmacount = req->td->dmacount;
+                dmacount &= cpu_to_le32 (
+                                (1 << VALID_BIT)
+                                | DMA_TRANSFER_MAX_LENGTH);
+
+                if (dmacount && (dmacount & valid_bit) == 0)
+                    restart_dma (ep);
+            }
+        }
+        ep->irqs++;
+    }
+
+    /* NOTE:  there are other PCI errors we might usefully notice.
+     * if they appear very often, here's where to try recovering.
+     */
+    if (stat & PCI_ERROR_INTERRUPTS)
+    {
+        ERROR (dev, "PCI_ERROR_INTERRUPTS pci dma error; stat %08x\n", stat);
+
+        stat &= ~PCI_ERROR_INTERRUPTS;
+        /* these are fatal errors, but "maybe" they won't
+         * happen again ...
+         */
+        stop_activity (dev, dev->driver);
+
+        ep0_start (dev);
+
+        stat = 0;
+    }
+
+    if (stat)
+        DEBUG (dev, "unhandled irqstat1 %08x\n", stat);
+}
+
+
+static irqreturn_t
+usb338x_irq (int irq, void *_dev)
+{
+    struct usb338x      *dev = _dev;
+
+    spin_lock (&dev->lock);
+
+    /* handle disconnect, dma, and more */
+    handle_stat1_irqs (dev, readl (&dev->regs->irqstat1));
+
+    /* control requests and PIO */
+    handle_stat0_irqs (dev, readl (&dev->regs->irqstat0));
+
+    /* re-enable interrupt to trigger any possible new interrupt */
+    u32_pciirqenb1 = readl (&dev->regs->pciirqenb1);
+    writel (u32_pciirqenb1 & 0x7FFFFFFF,   &dev->regs->pciirqenb1);
+    writel (u32_pciirqenb1, &dev->regs->pciirqenb1);
+
+    spin_unlock (&dev->lock);
+
+    return IRQ_HANDLED;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int
+usb338x_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+    struct usb338x      *dev;
+    struct usb338x_ep   *ep;
+    u32                 max, tmp;
+    unsigned long       flags;
+    u32 max_pkt;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || !desc || ep->desc || _ep->name == ep0name
+            || desc->bDescriptorType != USB_DT_ENDPOINT)
+        return -EINVAL;
+
+    dev = ep->dev;
+
+    if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+        return -ESHUTDOWN;
+
+    if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
+        return -EDOM;
+
+    ep->is_in = usb_endpoint_dir_in(desc) ? 1 : 0;
+
+    if (enhanced_mode && ep->is_in && ep_key[ep->num])
+        return -EINVAL;
+
+    max = usb_endpoint_maxp (desc) & 0x1fff;
+
+    spin_lock_irqsave (&dev->lock, flags);
+    _ep->maxpacket = max & 0x7ff;
+    ep->desc = desc;
+
+    /* ep_reset() has already been called */
+    ep->stopped = 0;
+    ep->wedged = 0;
+    ep->out_overflow = 0;
+
+    max_pkt = (enhanced_mode) ? ep_enhanced[ep->num] : ep_legacy[ep->num];
+
+    writel (max_pkt, &dev->regs->idxaddr);
+    writel (max, &dev->regs->idxdata);
+
+    /* FIFO lines can't go to different packets.  PIO is ok, so
+     * use it instead of troublesome (non-bulk) multi-packet DMA.
+     */
+    if (ep->dma && (max % 4) != 0 && use_dma_chaining)
+    {
+        DEBUG (ep->dev, "%s, no dma for maxpacket %d\n",
+               ep->ep.name, ep->ep.maxpacket);
+        ep->dma = NULL;
+    }
+
+    /* set type, direction, address; reset fifo counters */
+    writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
+
+    tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+
+    if (tmp == USB_ENDPOINT_XFER_INT)
+    {
+        /* erratum 0105 workaround prevents hs NYET */
+        if (dev->chiprev == 0100
+                && dev->gadget.speed == USB_SPEED_HIGH
+                && !(desc->bEndpointAddress & USB_DIR_IN))
+        {
+            writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                    &ep->regs->ep_rsp);
+        }
+    }
+    else if (tmp == USB_ENDPOINT_XFER_BULK)
+    {
+        /* catch some particularly blatant driver bugs */
+        if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
+            (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
+            (dev->gadget.speed == USB_SPEED_FULL && max > 64))
+        {
+            spin_unlock_irqrestore (&dev->lock, flags);
+            return -ERANGE;
+        }
+    }
+
+    ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
+
+    /* Enable this endpoint */
+    /* In Legacy mode, only OUT endpoints are used */
+    if (enhanced_mode && ep->is_in)
+    {
+        tmp <<= IN_ENDPOINT_TYPE;
+        tmp |= (1 << IN_ENDPOINT_ENABLE);
+        tmp |= (1 << ENDPOINT_DIRECTION); /* Not applicable to Legacy */
+    }
+    else
+    {
+        tmp <<= OUT_ENDPOINT_TYPE;
+        tmp |= (1 << OUT_ENDPOINT_ENABLE);
+        tmp |= (ep->is_in << ENDPOINT_DIRECTION);
+    }
+
+    tmp |= usb_endpoint_num(desc);
+    tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
+
+    wmb ();
+
+    /* for OUT transfers, block the rx fifo until a read is posted */
+
+    if (!ep->is_in)
+        writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+    else
+        /* use nak packets on an in endpoint */
+        writel ((1 << CLEAR_NAK_OUT_PACKETS) |
+                (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
+
+    writel (tmp, &ep->cfg->ep_cfg);
+
+    /* enable irqs */
+    if (!ep->dma)               /* pio, per-packet */
+    {
+        tmp = (1 << ep_bit[ep->num]) | readl (&dev->regs->pciirqenb0);
+
+        writel (tmp, &dev->regs->pciirqenb0);
+
+        tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) |
+              (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
+
+        writel (tmp, &ep->regs->ep_irqenb);
+    }
+    else                    /* dma, per-request */
+    {
+        tmp = (1 << (8 + ep->num)); /* completion */
+        tmp |= readl (&dev->regs->pciirqenb1);
+
+        writel (tmp, &dev->regs->pciirqenb1);
+
+        /* for short OUT transfers, dma completions can't
+         * advance the queue; do it pio-style, by hand.
+         * NOTE erratum 0112 workaround #2
+         */
+        if ((desc->bEndpointAddress & USB_DIR_IN) == 0)
+        {
+            tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
+
+            writel (tmp, &ep->regs->ep_irqenb);
+
+            tmp = (1 << ep_bit[ep->num]) | readl (&dev->regs->pciirqenb0);
+
+            writel (tmp, &dev->regs->pciirqenb0);
+        }
+    }
+
+    tmp = desc->bEndpointAddress;
+
+    DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
+           _ep->name, tmp & 0x0f, DIR_STRING (tmp),
+           type_string (desc->bmAttributes),
+           ep->dma ? "dma" : "pio", max);
+
+    /* pci writes may still be posted */
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    return 0;
+}
+
+
+static int
+handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
+{
+    u32 result;
+
+    do
+    {
+        result = readl (ptr);
+        if (result == ~(u32)0)      /* "device unplugged" */
+            return -ENODEV;
+        result &= mask;
+        if (result == done)
+            return 0;
+        udelay (1);
+        usec--;
+    }
+    while (usec > 0);
+
+    return -ETIMEDOUT;
+}
+
+
+static const struct usb_ep_ops usb338x_ep_ops;
+
+
+static void
+ep_reset (struct usb338x_regs __iomem *regs, struct usb338x_ep *ep)
+{
+    u32     tmp, dmastat;
+
+    ep->desc = NULL;
+    INIT_LIST_HEAD (&ep->queue);
+
+    ep->ep.maxpacket = ~0;
+    ep->ep.ops = &usb338x_ep_ops;
+
+    /* disable the dma, irqs, endpoint... */
+    if (ep->dma)
+    {
+        writel (0, &ep->dma->dmactl);
+        writel (  (1 << DMA_ABORT_DONE_INTERRUPT)
+                  | (1 << DMA_PAUSE_DONE_INTERRUPT)
+                  | (1 << DMA_LAST_DESCRIPTOR_DONE_INTERRUPT)
+                  | (1 << DMA_TRANSACTION_DONE_INTERRUPT)
+                  // | (1 << DMA_ABORT)
+                  , &ep->dma->dmastat);
+
+        dmastat = readl (&ep->dma->dmastat);
+
+        if (dmastat == 0x5002)
+        {
+            printk(KERN_WARNING "The dmastat return = %x!!\n", dmastat);
+            writel( 0x5a, &ep->dma->dmastat);
+        }
+
+        tmp = readl (&regs->pciirqenb0);
+
+        tmp &= ~(1 << ep_bit[ep->num]);
+
+        writel (tmp, &regs->pciirqenb0);
+    }
+    else
+    {
+        if (ep->num < 5)
+        {
+            tmp = readl (&regs->pciirqenb1);
+            tmp &= ~(1 << (8 + ep->num));   /* completion */
+            writel (tmp, &regs->pciirqenb1);
+        }
+    }
+
+    writel (0, &ep->regs->ep_irqenb);
+
+    writel ((1 << SHORT_OUT_PACKET_DONE_INTERRUPT)
+            | (1 << SHORT_OUT_PACKET_RECEIVED_INTERRUPT)
+            | (1 << ZLP_INTERRUPT)
+            | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+            | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+            | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+            | (1 << DATA_IN_TOKEN_INTERRUPT),
+            &ep->regs->ep_stat);
+}
+
+
+static void nuke (struct usb338x_ep *);
+
+static int
+usb338x_disable (struct usb_ep *_ep)
+{
+    struct usb338x_ep   *ep;
+    unsigned long       flags;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || !ep->desc || _ep->name == ep0name)
+        return -EINVAL;
+
+    spin_lock_irqsave (&ep->dev->lock, flags);
+
+    nuke (ep);
+    ep_reset (ep->dev->regs, ep);
+
+    VDEBUG (ep->dev, "disabled %s %s\n",
+            ep->dma ? "dma" : "pio", _ep->name);
+
+    /* synch memory views with the device */
+    (void) readl (&ep->cfg->ep_cfg);
+
+    if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+        ep->dma = &ep->dev->dma [ep->num - 1];
+
+    spin_unlock_irqrestore (&ep->dev->lock, flags);
+
+    return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+usb338x_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
+{
+    struct usb338x_ep       *ep;
+    struct usb338x_request  *req;
+
+    if (!_ep)
+        return NULL;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    req = kzalloc(sizeof(*req), gfp_flags);
+
+    if (!req)
+        return NULL;
+
+    req->req.dma = DMA_ADDR_INVALID;
+
+    INIT_LIST_HEAD (&req->queue);
+
+    /* this dma descriptor may be swapped with the previous dummy */
+    if (ep->dma)
+    {
+        struct usb338x_dma  *td;
+
+        td = pci_pool_alloc (ep->dev->requests, gfp_flags,
+                             &req->td_dma);
+        if (!td)
+        {
+            kfree (req);
+            return NULL;
+        }
+
+        td->dmacount = 0;   /* not VALID */
+        td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID);
+        td->dmadesc = td->dmaaddr;
+        req->td = td;
+    }
+
+    return &req->req;
+}
+
+
+static void
+usb338x_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+    struct usb338x_ep       *ep;
+    struct usb338x_request  *req;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || !_req)
+        return;
+
+    req = container_of (_req, struct usb338x_request, req);
+
+    WARN_ON (!list_empty (&req->queue));
+
+    if (req->td)
+        pci_pool_free (ep->dev->requests, req->td, req->td_dma);
+
+    kfree (req);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* load a packet into the fifo we use for usb IN transfers.
+ * works for all endpoints.
+ *
+ * NOTE: pio with ep1..ep4 could stuff multiple packets into the fifo
+ * at a time, but this code is simpler because it knows it only writes
+ * one packet.  ep1..ep4 should use dma instead.
+ */
+static void
+write_fifo (struct usb338x_ep *ep, struct usb_request *req)
+{
+    struct usb338x_ep_regs  __iomem *regs = ep->regs;
+    u8          *buf;
+    u32         tmp;
+    unsigned    count, total;
+    u32         reg_value;
+
+    /* INVARIANT:  fifo is currently empty. (testable) */
+    if (req)
+    {
+        buf = req->buf + req->actual;
+        prefetch (buf);
+        total = req->length - req->actual;
+    }
+    else
+    {
+        total = 0;
+        buf = NULL;
+    }
+
+    /* write just one packet at a time */
+    count = ep->ep.maxpacket;
+
+    if (count > total)  /* min() cannot be used on a bitfield */
+        count = total;
+
+    VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
+            ep->ep.name, count,
+            (count != ep->ep.maxpacket) ? " (short)" : "",
+            req);
+
+    while (count >= 4)
+    {
+        /* NOTE be careful if you try to align these. fifo lines
+         * should normally be full (4 bytes) and successive partial
+         * lines are ok only in certain cases.
+         */
+        tmp = get_unaligned ((u32 *)buf);
+        cpu_to_le32s (&tmp);
+        writel (tmp, &regs->ep_data);
+        buf += 4;
+        count -= 4;
+    }
+
+    /* last fifo entry is "short" unless we wrote a full packet.
+     * also explicitly validate last word in (periodic) transfers
+     * when maxpacket is not a multiple of 4 bytes.
+     */
+    if (count || total < ep->ep.maxpacket)
+    {
+        tmp = count ? get_unaligned ((u32 *)buf) : count;
+        cpu_to_le32s (&tmp);
+
+        reg_value = readl(&ep->cfg->ep_cfg) & (~(0x07 << EP_FIFO_BYTE_COUNT));
+
+        writel (reg_value | ((count  & 0x03) << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
+
+        writel (tmp, &regs->ep_data);
+    }
+
+    /* pci writes may still be posted */
+}
+
+
+/* work around erratum 0106: PCI and USB race over the OUT fifo.
+ * caller guarantees chiprev 0100, out endpoint is NAKing, and
+ * there's no real data in the fifo.
+ *
+ * NOTE:  also used in cases where that erratum doesn't apply:
+ * where the host wrote "too much" data to us.
+ */
+static void
+out_flush (struct usb338x_ep *ep)
+{
+    u32 __iomem *statp;
+    u32 tmp;
+
+    statp = &ep->regs->ep_stat;
+
+    writel (  (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+              | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+              , statp);
+
+    writel ((1 << FIFO_FLUSH), statp);
+
+    mb ();
+
+    tmp = readl (statp);
+
+    if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+            /* high speed did bulk NYET; fifo isn't filling */
+            && ep->dev->gadget.speed == USB_SPEED_FULL)
+    {
+        unsigned    usec;
+
+        usec = 50;      /* 64 byte bulk/interrupt */
+        handshake (statp, (1 << USB_OUT_NAK_SENT),
+                   (1 << USB_OUT_NAK_SENT), usec);
+        /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
+    }
+}
+
+
+/* unload packet(s) from the fifo we use for usb OUT transfers.
+ * returns true iff the request completed, because of short packet
+ * or the request buffer having filled with full packets.
+ *
+ * for ep1..ep4 this will read multiple packets out when they
+ * have been accepted.
+ */
+static int
+read_fifo (struct usb338x_ep *ep, struct usb338x_request *req)
+{
+    struct usb338x_ep_regs  __iomem *regs = ep->regs;
+    u8          *buf = req->req.buf + req->req.actual;
+    unsigned    count, tmp, is_short;
+    unsigned    cleanup = 0, prevent = 0;
+
+    /* erratum 0106 ... packets coming in during fifo reads might
+     * be incompletely rejected.  not all cases have workarounds.
+     */
+    if (ep->dev->chiprev == 0x0100
+            && ep->dev->gadget.speed == USB_SPEED_FULL)
+    {
+        udelay (1);
+        tmp = readl (&ep->regs->ep_stat);
+
+        if ((tmp & (1 << NAK_PACKETS)))
+            cleanup = 1;
+
+        else if ((tmp & (1 << FIFO_FULL)))
+        {
+            writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+            readl (&ep->regs->ep_rsp);
+
+            prevent = 1;
+        }
+        /* else: hope we don't see the problem */
+    }
+
+    /* never overflow the rx buffer. the fifo reads packets until
+     * it sees a short one; we might not be ready for them all.
+     */
+    prefetchw (buf);
+    count = readl (&regs->ep_avail);
+
+    if (unlikely (count == 0))
+    {
+        udelay (1);
+        tmp = readl (&ep->regs->ep_stat);
+        count = readl (&regs->ep_avail);
+
+        /* handled that data already? */
+        if (count == 0 && (tmp & (1 << NAK_PACKETS)) == 0)
+            return 0;
+    }
+
+    tmp = req->req.length - req->req.actual;
+
+    if (count > tmp)
+    {
+        /* as with DMA, data overflow gets flushed */
+        if ((tmp % ep->ep.maxpacket) != 0)
+        {
+            ERROR (ep->dev,
+                   "%s out fifo %d bytes, expected %d\n",
+                   ep->ep.name, count, tmp);
+
+            req->req.status = -EOVERFLOW;
+            cleanup = 1;
+            /* NAK_PACKETS will be set, so flushing is safe;
+             * the next read will start with the next packet
+             */
+        } /* else it's a ZLP, no worries */
+        count = tmp;
+    }
+
+    req->req.actual += count;
+
+    is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+
+    VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
+            ep->ep.name, count, is_short ? " (short)" : "",
+            cleanup ? " flush" : "", prevent ? " nak" : "",
+            req, req->req.actual, req->req.length);
+
+    while (count >= 4)
+    {
+        tmp = readl (&regs->ep_data);
+        cpu_to_le32s (&tmp);
+        put_unaligned (tmp, (u32 *)buf);
+        buf += 4;
+        count -= 4;
+    }
+
+    if (count)
+    {
+        tmp = readl (&regs->ep_data);
+        /* LE conversion is implicit here: */
+        do
+        {
+            *buf++ = (u8) tmp;
+            tmp >>= 8;
+        }
+        while (--count);
+    }
+
+    if (cleanup)
+        out_flush (ep);
+
+    if (prevent)
+    {
+        writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+        (void) readl (&ep->regs->ep_rsp);
+    }
+
+    return is_short || ((req->req.actual == req->req.length)
+                        && !req->req.zero);
+}
+
+
+/* fill out dma descriptor to match a given request */
+static void
+fill_dma_desc (struct usb338x_ep *ep, struct usb338x_request *req, int valid)
+{
+    struct usb338x_dma  *td = req->td;
+    u32            dmacount = req->req.length;
+
+    /* don't let DMA continue after a short OUT packet,
+     * so overruns can't affect the next transfer.
+     * in case of overruns on max-size packets, we can't
+     * stop the fifo from filling but we can flush it.
+     */
+    if (ep->is_in)
+        dmacount |= (1 << DMA_DIRECTION);
+
+    /* if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)) */
+    dmacount |= (1 << LAST_DESCRIPTOR);
+
+    req->valid = valid;
+
+    if (valid)
+        dmacount |= (1 << VALID_BIT);
+
+    if (likely(!req->req.no_interrupt || !use_dma_chaining))
+    {
+        dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE);
+    }
+
+    /* td->dmadesc = previously set by caller */
+    td->dmaaddr = cpu_to_le32 (req->req.dma);
+
+    wmb ();
+    td->dmacount = cpu_to_le32(dmacount);
+}
+
+static const u32 dmactl_default =
+    (1 << DMA_LAST_DESCRIPTOR_DONE_INTERRUPT)
+    | (1 << DMA_CLEAR_COUNT_ENABLE)
+    | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE)
+    | (1 << DMA_VALID_BIT_POLLING_ENABLE)
+    | (1 << DMA_VALID_BIT_ENABLE)
+    | (1 << DMA_DESCRIPTOR_MODE)
+    | (1 << DMA_ENABLE);
+
+
+static inline void
+spin_stop_dma (struct usb338x_dma_regs __iomem *dma)
+{
+    int     usec = 50;
+    u32     result;
+
+    do
+    {
+        result = readl (&dma->dmactl);
+
+        if (result == ~(u32)0) return;  /* "device unplugged" */
+
+        result &= (1 << DMA_ENABLE);
+
+        if (result == 0) return;
+
+        udelay (1);
+        usec--;
+
+    }
+    while (usec > 0);
+
+    return;
+}
+
+
+static inline void
+stop_dma (struct usb338x_dma_regs __iomem *dma)
+{
+    writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl);
+    spin_stop_dma (dma);
+}
+
+
+static void
+start_queue (struct usb338x_ep *ep, u32 dmactl, u32 td_dma)
+{
+    u32   reg_value;
+
+    struct usb338x_dma_regs __iomem *dma = ep->dma;
+    unsigned int tmp = (1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION);
+
+    tmp |= (1 << LAST_DESCRIPTOR);
+
+    writel (tmp, &dma->dmacount);
+    writel (readl (&dma->dmastat), &dma->dmastat);
+
+    writel (td_dma, &dma->dmadesc);
+
+    dmactl |=  (0x01 << DMA_REQUEST_OUTSTANDING);
+
+    writel (dmactl, &dma->dmactl);
+
+    /* erratum 0116 workaround part 3:  pci arbiter away from usb338x */
+    (void) readl (&ep->dev->pci->pcimstctl);
+
+    writel ((1 << DMA_START), &dma->dmastat);
+
+    if (!ep->is_in)
+    {
+        reg_value = readl (&ep->regs->ep_stat);
+
+        if ((reg_value & (1 << NAK_PACKETS)) != 0)
+            writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+    }
+}
+
+
+static void
+start_dma (struct usb338x_ep *ep, struct usb338x_request *req)
+{
+    u32         tmp;
+    struct usb338x_dma_regs __iomem *dma = ep->dma;
+
+    /* FIXME can't use DMA for ZLPs */
+
+    /* on this path we "know" there's no dma active (yet) */
+    WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE));
+    writel (0, &ep->dma->dmactl);
+
+    /* previous OUT packet might have been short */
+    if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
+                       & (1 << NAK_PACKETS)) != 0)
+    {
+        writel ((1 << SHORT_OUT_PACKET_RECEIVED_INTERRUPT),
+                &ep->regs->ep_stat);
+
+        tmp = readl (&ep->regs->ep_avail);
+
+        if (tmp)
+        {
+            writel (readl (&dma->dmastat), &dma->dmastat);
+
+            /* transfer all/some fifo data */
+            writel (req->req.dma, &dma->dmaaddr);
+            tmp = min (tmp, req->req.length);
+
+            /* dma irq, faking scatterlist status */
+            req->td->dmacount = cpu_to_le32 (req->req.length - tmp);
+            writel ((1 << DMA_DONE_INTERRUPT_ENABLE)
+                    | tmp, &dma->dmacount);
+            req->td->dmadesc = 0;
+            req->valid = 1;
+
+            writel ((1 << DMA_ENABLE), &dma->dmactl);
+            writel ((1 << DMA_START), &dma->dmastat);
+
+            return;
+        }
+    }
+
+    tmp = dmactl_default;
+
+    /* force packet boundaries between dma requests, but prevent the
+     * controller from automagically writing a last "short" packet
+     * (zero length) unless the driver explicitly said to do that.
+     */
+    if (ep->is_in)
+    {
+        if (likely ((req->req.length % ep->ep.maxpacket) != 0
+                    || req->req.zero))
+        {
+            tmp |= (1 << DMA_FIFO_VALIDATE);
+            ep->in_fifo_validate = 1;
+        }
+        else
+            ep->in_fifo_validate = 0;
+    }
+
+    /* init req->td, pointing to the current dummy */
+    req->td->dmadesc = cpu_to_le32 (ep->td_dma);
+    fill_dma_desc (ep, req, 1);
+
+    if (!use_dma_chaining)
+        req->td->dmacount |= cpu_to_le32 (1 << LAST_DESCRIPTOR);
+
+    start_queue (ep, tmp, req->td_dma);
+}
+
+
+static inline void
+queue_dma (struct usb338x_ep *ep, struct usb338x_request *req, int valid)
+{
+    struct usb338x_dma  *end;
+    dma_addr_t          tmp;
+
+    /* swap new dummy for old, link; fill and maybe activate */
+    end = ep->dummy;
+    ep->dummy = req->td;
+    req->td = end;
+
+    tmp = ep->td_dma;
+    ep->td_dma = req->td_dma;
+    req->td_dma = tmp;
+
+    end->dmadesc = cpu_to_le32 (ep->td_dma);
+
+    fill_dma_desc (ep, req, valid);
+}
+
+
+static void
+complete_req (struct usb338x_ep *ep, struct usb338x_request *req, int status)
+{
+    struct usb338x    *dev;
+    unsigned          stopped = ep->stopped;
+
+    list_del_init (&req->queue);
+
+    if (req->req.status == -EINPROGRESS)
+        req->req.status = status;
+    else
+        status = req->req.status;
+
+    dev = ep->dev;
+
+    if (ep->dma)
+        usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+
+    if (status && status != -ESHUTDOWN)
+        VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n",
+                ep->ep.name, &req->req, status,
+                req->req.actual, req->req.length);
+
+    /* don't modify queue heads during completion callback */
+    ep->stopped = 1;
+
+    spin_unlock (&dev->lock);
+    req->req.complete (&ep->ep, &req->req);
+    spin_lock (&dev->lock);
+
+    ep->stopped = stopped;
+}
+
+
+static inline void
+dma_done (struct usb338x_ep *ep, struct usb338x_request *req, u32 dmacount, int status)
+{
+    req->req.actual = req->req.length - (DMA_TRANSFER_MAX_LENGTH & dmacount);
+
+    complete_req (ep, req, status);
+}
+
+
+static void
+scan_dma_completions (struct usb338x_ep *ep)
+{
+    /* only look at descriptors that were "naturally" retired,
+     * so fifo and list head state won't matter
+     */
+    while (!list_empty (&ep->queue))
+    {
+        struct usb338x_request  *req;
+        u32         tmp;
+
+        req = list_entry (ep->queue.next,
+                          struct usb338x_request, queue);
+        if (!req->valid)
+            break;
+
+        rmb ();
+
+        tmp = le32_to_cpup (&req->td->dmacount);
+
+        if ((tmp & (1 << VALID_BIT)) != 0)
+            break;
+
+        /* SHORT_OUT_PACKET_RECEIVED_INTERRUPT handles "usb-short"
+         * cases where DMA must be aborted; this code handles
+         * all non-abort DMA completions.
+         */
+        if (unlikely (req->td->dmadesc == 0))
+        {
+            /* paranoia */
+            tmp = readl (&ep->dma->dmacount);
+
+            if (tmp & DMA_TRANSFER_MAX_LENGTH)
+                break;
+
+            /* single transfer mode */
+            dma_done (ep, req, tmp, 0);
+
+            break;
+        }
+        else if (!ep->is_in
+                 && (req->req.length % ep->ep.maxpacket) != 0)
+        {
+            tmp = readl (&ep->regs->ep_stat);
+
+        }
+
+        dma_done (ep, req, tmp, 0);
+    }
+}
+
+
+static void
+restart_dma (struct usb338x_ep *ep)
+{
+    struct usb338x_request  *req;
+    u32         dmactl = dmactl_default;
+
+    if (ep->stopped)
+        return;
+
+    req = list_entry (ep->queue.next, struct usb338x_request, queue);
+
+    if (!use_dma_chaining)
+    {
+        start_dma (ep, req);
+
+        return;
+    }
+
+
+    /*  IN:   wanted automagic zlp, head doesn't (or vice versa)
+     *        DMA_FIFO_VALIDATE doesn't init from dma descriptors.
+     *  OUT:  was "usb-short", we must restart.
+     */
+    if (ep->is_in && !req->valid)
+    {
+        struct usb338x_request  *entry, *prev = NULL;
+        int         reqmode, done = 0;
+
+        DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
+
+        ep->in_fifo_validate = likely (req->req.zero
+                                       || (req->req.length % ep->ep.maxpacket) != 0);
+        if (ep->in_fifo_validate)
+            dmactl |= (1 << DMA_FIFO_VALIDATE);
+
+        list_for_each_entry (entry, &ep->queue, queue)
+        {
+            __le32      dmacount;
+
+            if (entry == req)
+                continue;
+
+            dmacount = entry->td->dmacount;
+
+            if (!done)
+            {
+                reqmode = likely (entry->req.zero
+                                  || (entry->req.length
+                                      % ep->ep.maxpacket) != 0);
+
+                if (reqmode == ep->in_fifo_validate)
+                {
+                    entry->valid = 1;
+                    dmacount |= valid_bit;
+                    entry->td->dmacount = dmacount;
+                    prev = entry;
+
+                    continue;
+                }
+                else
+                {
+                    /* force a hiccup */
+                    prev->td->dmacount |= dma_done_ie;
+                    done = 1;
+                }
+            }
+
+            /* walk the rest of the queue so unlinks behave */
+            entry->valid = 0;
+            dmacount &= ~valid_bit;
+            entry->td->dmacount = dmacount;
+            prev = entry;
+        }
+    }
+
+    writel (0, &ep->dma->dmactl);
+
+    start_queue (ep, dmactl, req->td_dma);
+}
+
+
+static void
+abort_dma (struct usb338x_ep *ep)
+{
+    /* abort the current transfer */
+    writel ((1 << DMA_ABORT), &ep->dma->dmastat);
+
+    spin_stop_dma (ep->dma);
+}
+
+
+/* dequeue ALL requests */
+static void
+nuke (struct usb338x_ep *ep)
+{
+    struct usb338x_request  *req;
+
+    /* called with spinlock held */
+    ep->stopped = 1;
+    if (ep->dma)
+        abort_dma (ep);
+
+    while (!list_empty (&ep->queue))
+    {
+        req = list_entry (ep->queue.next,
+                          struct usb338x_request,
+                          queue);
+
+        complete_req (ep, req, -ESHUTDOWN);
+    }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int
+usb338x_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+    struct usb338x_request  *req;
+    struct usb338x_ep   *ep;
+    struct usb338x      *dev;
+    unsigned long       flags;
+
+    /* we always require a cpu-view buffer, so that we can
+     * always use pio (as fallback or whatever).
+     */
+    req = container_of (_req, struct usb338x_request, req);
+
+    if (!_req || !_req->complete || !_req->buf
+            || !list_empty (&req->queue))
+        return -EINVAL;
+
+    if (_req->length > (~0 & DMA_TRANSFER_MAX_LENGTH))
+        return -EDOM;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || (!ep->desc && ep->num != 0))
+        return -EINVAL;
+
+    dev = ep->dev;
+
+    if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+        return -ESHUTDOWN;
+
+    /* FIXME implement PIO fallback for ZLPs with DMA */
+    if (ep->dma && _req->length == 0)
+        return -EOPNOTSUPP;
+
+    /* set up dma mapping in case the caller didn't */
+    if (ep->dma)
+    {
+        int ret;
+
+        ret = usb_gadget_map_request(&dev->gadget, _req,
+                                     ep->is_in);
+        if (ret)
+            return ret;
+    }
+
+#if 0
+    VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
+            _ep->name, _req, _req->length, _req->buf);
+#endif
+
+    spin_lock_irqsave (&dev->lock, flags);
+
+    _req->status = -EINPROGRESS;
+    _req->actual = 0;
+
+    /* kickstart this i/o queue? */
+    if (list_empty (&ep->queue) && !ep->stopped)
+    {
+
+        /* DMA request while EP halted */
+        if (ep->dma && (readl(&ep->regs->ep_rsp) & (1 << CLEAR_ENDPOINT_HALT)))
+        {
+            int valid = 1;
+
+            if (ep->is_in)
+            {
+                int expect;
+
+                expect = likely (req->req.zero ||
+                                 ((req->req.length % ep->ep.maxpacket) != 0));
+
+                if (expect != ep->in_fifo_validate)
+                {
+                    valid = 0;
+                }
+            }
+
+            queue_dma (ep, req, valid);
+        }
+
+        /* use DMA if the endpoint supports it, else pio */
+        else if (ep->dma)
+            start_dma (ep, req);
+        else
+        {
+            /* maybe there's no control data, just status ack */
+            if (ep->num == 0 && _req->length == 0)
+            {
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          (1 << CLEAR_NAK_OUT_PACKETS) |
+                          (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                          &ep->regs->ep_rsp);
+
+                ep->stopped = 1;
+
+                complete_req (ep, req, 0);
+
+                VDEBUG (dev, "%s status ack\n", ep->ep.name);
+
+                goto done;
+            }
+
+            /* PIO ... stuff the fifo, or unblock it.  */
+            if (ep->is_in)
+                write_fifo (ep, _req);
+            else if (list_empty (&ep->queue))
+            {
+                u32 s;
+
+                /* OUT FIFO might have packet(s) buffered */
+                s = readl (&ep->regs->ep_stat);
+
+                if ((s & (1 << FIFO_EMPTY)) == 0)
+                {
+                    /* note:  _req->short_not_ok is
+                     * ignored here since PIO _always_
+                     * stops queue advance here, and
+                     * _req->status doesn't change for
+                     * short reads (only _req->actual)
+                     */
+                    if (read_fifo (ep, req))
+                    {
+                        complete_req (ep, req, 0);
+
+                        if (ep->num == 0)
+                        {
+                            writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                                      (1 << CLEAR_NAK_OUT_PACKETS) |
+                                      (1 << CLEAR_NAK_OUT_PACKETS_MODE),
+                                      &ep->regs->ep_rsp);
+
+                            ep->stopped = 1;
+                        }
+
+                        /* don't queue it */
+                        req = NULL;
+                    }
+                    else
+                        s = readl (&ep->regs->ep_stat);
+                }
+
+                /* don't NAK, let the fifo fill */
+                if (req && (s & (1 << NAK_PACKETS)))
+                {
+                    writel ((1 << CLEAR_NAK_OUT_PACKETS),
+                            &ep->regs->ep_rsp);
+                }
+            }
+        }
+
+    }
+    else if (ep->dma)
+    {
+        int valid = 1;
+
+        if (ep->is_in)
+        {
+            int expect;
+
+            /* preventing magic zlps is per-engine state, not
+             * per-transfer; irq logic must recover hiccups.
+             */
+            expect = likely (req->req.zero
+                             || (req->req.length % ep->ep.maxpacket) != 0);
+            if (expect != ep->in_fifo_validate)
+                valid = 0;
+        }
+
+        queue_dma (ep, req, valid);
+
+    } /* else the irq handler advances the queue. */
+
+    ep->responded = 1;
+
+    if (req)
+        list_add_tail (&req->queue, &ep->queue);
+done:
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    /* pci writes may still be posted */
+    return 0;
+}
+
+
+/* dequeue JUST ONE request */
+static int
+usb338x_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+    struct usb338x_ep   *ep;
+    struct usb338x_request  *req;
+    unsigned long       flags;
+    u32         dmactl;
+    int         stopped;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+        return -EINVAL;
+
+    spin_lock_irqsave (&ep->dev->lock, flags);
+
+    stopped = ep->stopped;
+
+    /* quiesce dma while we patch the queue */
+    dmactl = 0;
+    ep->stopped = 1;
+
+    if (ep->dma)
+    {
+        dmactl = readl (&ep->dma->dmactl);
+
+        /* WARNING erratum 0127 may kick in ... */
+        stop_dma (ep->dma);
+
+        scan_dma_completions (ep);
+    }
+
+    /* make sure it's still queued on this endpoint */
+    list_for_each_entry (req, &ep->queue, queue)
+    {
+        if (&req->req == _req)
+            break;
+    }
+
+    if (&req->req != _req)
+    {
+        spin_unlock_irqrestore (&ep->dev->lock, flags);
+
+        return -EINVAL;
+    }
+
+    /* queue head may be partially complete. */
+    if (ep->queue.next == &req->queue)
+    {
+        if (ep->dma)
+        {
+            DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name);
+            _req->status = -ECONNRESET;
+
+            abort_dma (ep);
+
+            if (likely (ep->queue.next == &req->queue))
+            {
+                // NOTE: misreports single-transfer mode
+                req->td->dmacount = 0;  /* invalidate */
+
+                dma_done (ep, req,
+                          readl (&ep->dma->dmacount),
+                          -ECONNRESET);
+            }
+        }
+        else
+        {
+            DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
+            complete_req (ep, req, -ECONNRESET);
+        }
+
+        req = NULL;
+
+        /* patch up hardware chaining data */
+    }
+    else if (ep->dma && use_dma_chaining)
+    {
+        if (req->queue.prev == ep->queue.next)
+        {
+            writel (le32_to_cpu (req->td->dmadesc),
+                    &ep->dma->dmadesc);
+
+            if (req->td->dmacount & dma_done_ie)
+                writel (readl (&ep->dma->dmacount)
+                        | le32_to_cpu(dma_done_ie),
+                        &ep->dma->dmacount);
+        }
+        else
+        {
+            struct usb338x_request  *prev;
+
+            prev = list_entry (req->queue.prev,
+                               struct usb338x_request, queue);
+            prev->td->dmadesc = req->td->dmadesc;
+
+            if (req->td->dmacount & dma_done_ie)
+                prev->td->dmacount |= dma_done_ie;
+        }
+    }
+
+    if (req)
+        complete_req (ep, req, -ECONNRESET);
+
+    ep->stopped = stopped;
+
+    if (ep->dma)
+    {
+        /* turn off dma on inactive queues */
+        if (list_empty (&ep->queue))
+            stop_dma (ep->dma);
+
+        else if (!ep->stopped)
+        {
+            /* resume current request, or start new one */
+            if (req)
+            {
+                writel (dmactl, &ep->dma->dmactl);
+            }
+            else
+                start_dma (ep, list_entry (ep->queue.next,
+                                           struct usb338x_request, queue));
+        }
+    }
+
+    spin_unlock_irqrestore (&ep->dev->lock, flags);
+
+    return 0;
+}
+
+
+static int usb338x_fifo_status (struct usb_ep *_ep);
+
+static int
+usb338x_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+    struct usb338x_ep   *ep;
+    unsigned long       flags;
+    int         retval = 0;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || (!ep->desc && ep->num != 0))
+        return -EINVAL;
+
+    if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+        return -ESHUTDOWN;
+
+    /* not ep0 */
+    if (ep->desc && (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC)
+        return -EINVAL;
+
+    spin_lock_irqsave (&ep->dev->lock, flags);
+
+    if (!list_empty (&ep->queue))
+        retval = -EAGAIN;
+    else if (ep->is_in && value && usb338x_fifo_status (_ep) != 0)
+        retval = -EAGAIN;
+    else
+    {
+        VDEBUG (ep->dev, "%s %s %s\n", _ep->name,
+                value ? "set" : "clear",
+                wedged ? "wedge" : "halt");
+
+        /* set/clear, then synch memory views with the device */
+        if (value)
+        {
+            if (ep->num == 0)
+                ep->dev->protocol_stall = 1;
+            else
+                writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+                          ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+                          (1 << SET_ENDPOINT_HALT),
+                          &ep->regs->ep_rsp);
+
+            if (wedged)
+                ep->wedged = 1;
+        }
+        else
+        {
+            writel (  (1 << CLEAR_ENDPOINT_HALT) |
+                      (1 << CLEAR_ENDPOINT_TOGGLE) |
+                      ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS),
+                      &ep->regs->ep_rsp);
+
+            if (!list_empty(&ep->queue) && ep->td_dma)
+            {
+                restart_dma (ep);
+            }
+
+            ep->wedged = 0;
+        }
+
+        (void) readl (&ep->regs->ep_rsp);
+    }
+
+    spin_unlock_irqrestore (&ep->dev->lock, flags);
+
+    return retval;
+}
+
+
+static int
+usb338x_set_halt(struct usb_ep *_ep, int value)
+{
+    return usb338x_set_halt_and_wedge(_ep, value, 0);
+}
+
+
+static int
+usb338x_set_wedge(struct usb_ep *_ep)
+{
+    if (!_ep || _ep->name == ep0name)
+        return -EINVAL;
+
+    return usb338x_set_halt_and_wedge(_ep, 1, 1);
+}
+
+
+static int
+usb338x_fifo_status (struct usb_ep *_ep)
+{
+    struct usb338x_ep   *ep;
+    u32         avail;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || (!ep->desc && ep->num != 0))
+        return -ENODEV;
+
+    if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+        return -ESHUTDOWN;
+
+    avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1);
+
+    if (avail > ep->fifo_size)
+        return -EOVERFLOW;
+
+    if (ep->is_in)
+        avail = ep->fifo_size - avail;
+
+    return avail;
+}
+
+
+static void
+usb338x_fifo_flush (struct usb_ep *_ep)
+{
+    struct usb338x_ep   *ep;
+
+    ep = container_of (_ep, struct usb338x_ep, ep);
+
+    if (!_ep || (!ep->desc && ep->num != 0))
+        return;
+
+    if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+        return;
+
+    writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
+
+    (void) readl (&ep->regs->ep_rsp);
+}
+
+
+static const struct usb_ep_ops usb338x_ep_ops =
+{
+    .enable        = usb338x_enable,
+    .disable       = usb338x_disable,
+
+    .alloc_request = usb338x_alloc_request,
+    .free_request  = usb338x_free_request,
+
+    .queue         = usb338x_queue,
+    .dequeue       = usb338x_dequeue,
+
+    .set_halt      = usb338x_set_halt,
+    .set_wedge     = usb338x_set_wedge,
+    .fifo_status   = usb338x_fifo_status,
+    .fifo_flush    = usb338x_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int
+usb338x_get_frame (struct usb_gadget *_gadget)
+{
+    struct usb338x      *dev;
+    unsigned long       flags;
+    u16                 retval;
+
+    if (!_gadget)
+        return -ENODEV;
+
+    dev = container_of (_gadget, struct usb338x, gadget);
+
+    spin_lock_irqsave (&dev->lock, flags);
+
+    writel (REG_FRAME, &dev->regs->idxaddr);
+
+    retval =  readl (&dev->regs->idxdata)  & 0x03ff;
+
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    return retval;
+}
+
+
+static int
+usb338x_wakeup (struct usb_gadget *_gadget)
+{
+    struct usb338x      *dev;
+    u32                 tmp;
+    unsigned long       flags;
+
+    if (!_gadget)
+        return 0;
+
+    dev = container_of (_gadget, struct usb338x, gadget);
+
+    spin_lock_irqsave (&dev->lock, flags);
+
+    tmp = readl (&dev->usb->usbctl);
+
+    if (tmp & (1 << REMOTE_WAKEUP_ENABLE))
+    {
+        writel (1 << GENERATE_RESUME, &dev->usb->usbstat);
+    }
+
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    /* pci writes may still be posted */
+    return 0;
+}
+
+
+static int
+usb338x_set_selfpowered (struct usb_gadget *_gadget, int value)
+{
+    struct usb338x      *dev;
+    u32                 tmp;
+    unsigned long       flags;
+
+    if (!_gadget)
+        return 0;
+
+    dev = container_of (_gadget, struct usb338x, gadget);
+
+    spin_lock_irqsave (&dev->lock, flags);
+
+    tmp = readl (&dev->usb->usbctl);
+
+    if (value)
+    {
+        tmp |= (1 << SELF_POWERED_STATUS);
+        dev->selfpowered = 1;
+    }
+
+    else
+    {
+        tmp &= ~(1 << SELF_POWERED_STATUS);
+        dev->selfpowered = 0;
+    }
+
+    writel (tmp, &dev->usb->usbctl);
+
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    return 0;
+}
+
+
+static int
+usb338x_pullup(struct usb_gadget *_gadget, int is_on)
+{
+    struct usb338x  *dev;
+    u32             tmp;
+    unsigned long   flags;
+
+    if (!_gadget)
+        return -ENODEV;
+
+    dev = container_of (_gadget, struct usb338x, gadget);
+
+    spin_lock_irqsave (&dev->lock, flags);
+
+    tmp = readl (&dev->usb->usbctl);
+    dev->softconnect = (is_on != 0);
+
+    if (is_on)
+        tmp |= (1 << USB_DETECT_ENABLE);
+    else
+        tmp &= ~(1 << USB_DETECT_ENABLE);
+
+    writel (tmp, &dev->usb->usbctl);
+
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    return 0;
+}
+
+static void Defect7374_DisableDataEps (struct usb338x *dev)
+{
+    /* For Defect 7374, disable data EPs (and more):
+     *  - This phase undoes the earlier phase of the Defect 7374 workaround,
+     *    returing ep regs back to normal.
+     */
+    struct usb338x_ep   *ep;
+    int                 i;
+    unsigned char       ep_sel;
+    u32 tmp_reg;
+
+    for (i = 1; i < 5; i++)
+    {
+        ep = &dev->ep[i];
+        writel (0, &ep->cfg->ep_cfg);
+    }
+
+    // CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN
+    for (i = 0; i < 6; i++)
+    {
+        writel (0, &dev->dep[i].dep_cfg);
+    }
+
+    for (ep_sel = 0; ep_sel <= 21; ep_sel++)
+    {
+        // Select an endpoint for subsequent operations:
+        tmp_reg = readl (&dev->plregs->pl_ep_ctrl);
+        writel (((tmp_reg & ~ 0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl);
+
+        // Change settings on some selected endpoints
+        switch (ep_sel)
+        {
+            // Undo earlier protocol layer EP programming:
+        case 2:     // GPEP0 OUT
+        case 3:     // GPEP0 IN
+        case 4:     // GPEP1 OUT
+        case 5:     // GPEP1 IN
+        case 6:     // GPEP2 OUT
+        case 7:     // GPEP2 IN
+        case 8:     // GPEP3 OUT
+        case 9:     // GPEP3 IN
+        case 14:    // CSROUT
+        case 15:    // CSRIN
+        case 16:    // PCIOUT
+        case 17:    // PCIIN
+        case 19:    // STATIN
+        case 21:    // RCIN
+
+            tmp_reg = readl(&dev->plregs->pl_ep_cfg_4);
+            tmp_reg &=  ~(1 << NON_CTRL_IN_TOLERATE_BAD_DIR);
+            writel (tmp_reg, &dev->plregs->pl_ep_cfg_4);
+
+            tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+            tmp_reg |=  (1 << EP_INITIALIZED);
+            writel (tmp_reg, &dev->plregs->pl_ep_ctrl);
+
+            break;
+        }
+    }
+}
+
+static void Defect7374_EnableDataEpsAtZero (struct usb338x *dev)
+{
+    u32 tmp = 0, tmp_reg;
+    u32 FsmValue, Scratch;
+    int i;
+    unsigned char ep_sel;
+
+    writel (SCRATCH, &dev->regs->idxaddr);
+    Scratch = readl (&dev->regs->idxdata);
+
+    FsmValue = Scratch & (0xf << DEFECT7374_FSM_FIELD);
+    Scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+    ///See if firmware needs to set up for workaround:
+    if (FsmValue != DEFECT7374_FSM_SS_CONTROL_READ)
+    {
+        printk (KERN_WARNING "Operate Defect 7374 workaround software this time. (It will operate on cold-reboot and SS connection.)\n");
+
+        //GPEPs:
+        tmp = ( (0 << ENDPOINT_NUMBER) |
+                (1 << ENDPOINT_DIRECTION) |
+                (2 << OUT_ENDPOINT_TYPE) |
+                ((enhanced_mode) ? 1 << OUT_ENDPOINT_ENABLE : 1 << ENDPOINT_ENABLE) |
+                (2 << IN_ENDPOINT_TYPE) |
+                (1 << IN_ENDPOINT_ENABLE) );
+
+        for (i = 1; i < 5; i++)
+        {
+            struct usb338x_ep *ep = &dev->ep[i];
+            writel (tmp, &ep->cfg->ep_cfg);
+        }
+
+        // CSRIN, PCIIN, STATIN, RCIN
+        tmp = ((0 << ENDPOINT_NUMBER) | (1 << ENDPOINT_ENABLE));
+        writel (tmp, &dev->dep[1].dep_cfg);
+        writel (tmp, &dev->dep[3].dep_cfg);
+        writel (tmp, &dev->dep[4].dep_cfg);
+        writel (tmp, &dev->dep[5].dep_cfg);
+
+        //XXXXXXXXXXXXX Implemented for development and debug. Can be refined/tuned later.
+        for (ep_sel = 0; ep_sel <= 21; ep_sel++)
+        {
+            // Select an endpoint for subsequent operations:
+            tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+            writel (((tmp_reg & ~ 0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl);
+
+            // Change settings on some selected endpoints
+            switch (ep_sel)
+            {
+            case 1:     // EP0 IN
+
+                tmp = (readl(&dev->plregs->pl_ep_ctrl) | (1 << CLEAR_ACK_ERROR_CODE) | 0);
+                writel (tmp, &dev->plregs->pl_ep_ctrl);
+
+                break;
+
+                // Allow all EPs except control EP (EP0) to tolerate bad direction:
+            case 2:     // GPEP0 OUT
+            case 3:     // GPEP0 IN
+            case 4:     // GPEP1 OUT
+            case 5:     // GPEP1 IN
+            case 6:     // GPEP2 OUT
+            case 7:     // GPEP2 IN
+            case 8:     // GPEP3 OUT
+            case 9:     // GPEP3 IN
+            case 14:    // CSROUT
+            case 15:    // CSRIN
+            case 16:    // PCIOUT
+            case 17:    // PCIIN
+            case 19:    // STATIN
+            case 21:    // RCIN
+
+                tmp = (readl(&dev->plregs->pl_ep_cfg_4) | (1 << NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+                writel (tmp, &dev->plregs->pl_ep_cfg_4);
+
+                tmp = readl(&dev->plregs->pl_ep_ctrl) & ~(1 << EP_INITIALIZED);
+                writel (tmp, &dev->plregs->pl_ep_ctrl);
+
+                break;
+            }
+        }
+
+        // Set FSM to focus on the first Control Read:
+        //  - Tip: Connection speed is known upon the first setup request.
+        Scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+
+        writel (SCRATCH, &dev->regs->idxaddr);
+        writel (Scratch, &dev->regs->idxdata);
+    }
+    else
+    {
+        printk (KERN_WARNING "Defect 7374 workaround software will NOT operate this time. (It will operate on cold-reboot and SS connection.)\n");
+    }
+}
+
+static int usb338x_start(struct usb_gadget *_gadget,
+                         struct usb_gadget_driver *driver);
+static int usb338x_stop(struct usb_gadget *_gadget,
+                        struct usb_gadget_driver *driver);
+
+static const struct usb_gadget_ops usb338x_ops =
+{
+    .get_frame       = usb338x_get_frame,
+    .wakeup          = usb338x_wakeup,
+    .set_selfpowered = usb338x_set_selfpowered,
+    .pullup          = usb338x_pullup,
+    .udc_start       = usb338x_start,
+    .udc_stop        = usb338x_stop,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ *
+ * most of the work to support multiple usb338x controllers would
+ * be to associate this gadget driver (yes?) with all of them, or
+ * perhaps to bind specific drivers to specific devices.
+ */
+
+static void
+usb_reset (struct usb338x *dev)
+{
+    u32 tmp;
+    u32 Scratch, FsmValue;
+
+    dev->gadget.speed = USB_SPEED_UNKNOWN;
+    (void) readl (&dev->usb->usbctl);
+
+    writel ((1 << GPIO3_LED_SELECT) |
+            (1 << GPIO3_OUTPUT_ENABLE) |
+            (1 << GPIO2_OUTPUT_ENABLE) |
+            (1 << GPIO1_OUTPUT_ENABLE) |
+            (1 << GPIO0_OUTPUT_ENABLE),
+            &dev->regs->gpioctl);
+
+    writel (SCRATCH, &dev->regs->idxaddr);
+    Scratch = readl (&dev->regs->idxdata);
+
+    FsmValue = Scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+    /* See if firmware needs to set up for workaround: */
+    if (FsmValue != DEFECT7374_FSM_SS_CONTROL_READ)
+    {
+        INFO (dev, "%s: Defect 7374 FsmValue 0x%08X \n", __FUNCTION__ , FsmValue);
+    }
+    else
+    {
+        /* disable automatic responses, and irqs */
+        writel (0, &dev->usb->stdrsp);
+        writel (0, &dev->regs->pciirqenb0);
+        writel (0, &dev->regs->pciirqenb1);
+    }
+
+    /* clear old dma and irq state */
+    for (tmp = 0; tmp < 4; tmp++)
+    {
+        struct usb338x_ep   *ep = &dev->ep [tmp + 1];
+
+        if (ep->dma)
+            abort_dma (ep);
+    }
+
+    writel (~0, &dev->regs->irqstat0),
+           writel (~0, &dev->regs->irqstat1);
+
+    if (FsmValue == DEFECT7374_FSM_SS_CONTROL_READ)
+    {
+        /* reset, and enable pci */
+        tmp = readl (&dev->regs->devinit)
+              | (1 << PCI_ENABLE)
+              | (1 << FIFO_SOFT_RESET)
+              | (1 << USB_SOFT_RESET)
+              | (1 << M8051_RESET);
+
+        writel (tmp, &dev->regs->devinit);
+
+    }
+
+#if 0
+    /* keeping high bits preserves BAR2 */
+    writel ((0xffff << PCI_BASE2_RANGE), &dev->regs->fifoctl);
+#endif
+
+    /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */
+    INIT_LIST_HEAD (&dev->gadget.ep_list);
+
+    for (tmp = 1; tmp < n_ep; tmp++)
+    {
+        list_add_tail (&dev->ep [tmp].ep.ep_list, &dev->gadget.ep_list);
+    }
+}
+
+
+static void
+usb_reinit (struct usb338x *dev)
+{
+    int init_dma;
+    int i;
+    u32 Scratch, FsmValue, tmp, val;
+
+    /* use_dma changes are ignored till next device re-init */
+    init_dma = use_dma;
+
+    /* basic endpoint init */
+    for (i = 0; i < n_ep; i++)
+    {
+        struct usb338x_ep *ep = &dev->ep [i];
+
+        ep->ep.name = ep_name [i];
+        ep->dev = dev;
+        ep->num = i;
+
+        if (i > 0 && i <= 4)
+        {
+            if (init_dma)
+                ep->dma = &dev->dma [i - 1];
+        }
+
+        if (enhanced_mode)
+        {
+            ep->cfg = &dev->epcfg[ne[i]];
+            ep->regs = (struct usb338x_ep_regs __iomem *) (((unsigned char *)&dev->epregs[ne[i]]) + ep_reg_addr[i]);
+            ep->fiforegs = &dev->fiforegs[i];
+        }
+        else
+        {
+            ep->cfg = &dev->epcfg[i];
+            ep->regs = &dev->epregs[i];
+            ep->fiforegs = &dev->fiforegs[i];
+        }
+
+        ep->fifo_size = (i != 0) ? 2048 : 512;
+
+        ep_reset (dev->regs, ep);
+    }
+
+    dev->ep [0].ep.maxpacket = 512;
+
+    dev->gadget.ep0 = &dev->ep [0].ep;
+    dev->ep [0].stopped = 0;
+
+    /* Link layer set up */
+    writel (SCRATCH, &dev->regs->idxaddr);
+    Scratch = readl (&dev->regs->idxdata);
+
+    FsmValue = Scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+    /* See if driver needs to set up for workaround: */
+    if (FsmValue != DEFECT7374_FSM_SS_CONTROL_READ)
+    {
+        INFO (dev, "%s: Defect 7374 FsmValue %08x \n", __FUNCTION__ , FsmValue);
+    }
+    else
+    {
+        tmp = readl(&dev->usb->usbctl2) &
+              ~((1 << U1_ENABLE) | (1 << U2_ENABLE) | (1 << LTM_ENABLE));
+        writel(tmp, &dev->usb->usbctl2);
+    }
+
+    // Hardware Defect and Workaround
+    val = readl(&dev->ll_lfps_regs->ll_lfps_5);
+    val &= ~(0xf << TIMER_LFPS_6US);
+    val |=   0x5 << TIMER_LFPS_6US;
+    writel (val, &dev->ll_lfps_regs->ll_lfps_5);
+
+    val = readl(&dev->ll_lfps_regs->ll_lfps_6);
+    val &= ~(0xffff << TIMER_LFPS_80US);
+    val |=   0x0100 << TIMER_LFPS_80US;
+    writel (val, &dev->ll_lfps_regs->ll_lfps_6);
+
+    /* AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
+       Hot Reset Exit Handshake may Fail in Specific Case using
+       Default Register Settings. Workaround for Enumeration test.
+    */
+    val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
+    val &= ~(0x1f << HOT_TX_NORESET_TS2);
+    val |=   0x10 << HOT_TX_NORESET_TS2;
+    writel (val, &dev->ll_tsn_regs->ll_tsn_counters_2);
+
+    val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
+    val &= ~(0x1f << HOT_RX_RESET_TS2);
+    val |=   0x3 << HOT_RX_RESET_TS2;
+    writel (val, &dev->ll_tsn_regs->ll_tsn_counters_3);
+
+    // Set Recovery Idle to Recover bit:
+    //  - On SS connections, setting Recovery Idle to Recover Fmw improves
+    //    link robustness with various hosts and hubs.
+    //  - It is safe to set for all connection speeds; all chip revisions.
+    //  - R-M-W to leave other bits undisturbed.
+    //  - Reference PLX TT-7372
+    val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
+    val |= (1 << RECOVERY_IDLE_TO_RECOVER_FMW);
+    writel (val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
+
+#if 0 // to test with differnt USB3 host controllers
+    // USB3CV 9.23: Reference Caution, Defect or Errata number.
+    val = readl(&dev->factory_regs);
+    val &= 0xFFFCFFFF; // bit[17:16] 11b
+    val |= 0x00030000;
+    writel (val, &dev->factory_regs);
+
+    val = readl(&dev->phy_regs);
+    val &= 0xFF3FFFFF; // bit[23:22] 10b
+    val |= 0x00800000;
+    writel (val, &dev->phy_regs);
+#endif
+
+    INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+
+    /* disable dedicated endpoints */
+    writel (0x0D, &dev->dep [0].dep_cfg);
+    writel (0x0D, &dev->dep [1].dep_cfg);
+    writel (0x0E, &dev->dep [2].dep_cfg);
+    writel (0x0E, &dev->dep [3].dep_cfg);
+    writel (0x0F, &dev->dep [4].dep_cfg);
+    writel (0x0C, &dev->dep [5].dep_cfg);
+}
+
+
+static void
+ep0_start (struct usb338x *dev)
+{
+    u32 Scratch, FsmValue;
+
+    writel (SCRATCH, &dev->regs->idxaddr);
+    Scratch = readl (&dev->regs->idxdata);
+
+    FsmValue = Scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+    if (FsmValue != DEFECT7374_FSM_SS_CONTROL_READ)
+    {
+        INFO (dev, "%s: Defect 7374 FsmValue %08x \n", __FUNCTION__ , FsmValue);
+    }
+    else
+    {
+        writel ( (1 << CLEAR_NAK_OUT_PACKETS_MODE)
+                 | (1 << SET_EP_HIDE_STATUS_PHASE)
+                 , &dev->epregs[0].ep_rsp);
+    }
+
+    /*
+     * hardware optionally handles a bunch of standard requests
+     * that the API hides from drivers anyway.  have it do so.
+     * endpoint status/features are handled in software, to
+     * help pass tests for some dubious behavior.
+     */
+    writel ( (1 << SET_ISOCHRONOUS_DELAY)
+             | (1 << SET_SEL)
+             | (1 << SET_TEST_MODE)
+             | (1 << SET_ADDRESS)
+             | (1 << GET_INTERFACE_STATUS)
+             | (1 << GET_DEVICE_STATUS)
+             , &dev->usb->stdrsp);
+
+
+    dev->wakeup_enable = 1;
+
+    writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
+             | (dev->softconnect << USB_DETECT_ENABLE)
+             | (1 << REMOTE_WAKEUP_ENABLE)
+             , &dev->usb->usbctl);
+
+    /* enable irqs so we can see ep0 and general operation  */
+    writel ( (1 << SETUP_PACKET_PCIE_INTERRUPT_ENABLE)
+             | (1 << ENDPOINT_0_PCIE_INTERRUPT_ENABLE)
+             , &dev->regs->pciirqenb0);
+
+    writel ( (1 << GLOBAL_PCIE_INTERRUPT_ENABLE)
+             | (1 << ROOT_PORT_RESET_PCIE_INTERRUPT_ENABLE)
+             | (1 << SUSPEND_REQUEST_CHANGE_PCIE_INTERRUPT_ENABLE)
+             | (1 << VBUS_PCIE_INTERRUPT_ENABLE)
+             , &dev->regs->pciirqenb1);
+
+    /* don't leave any writes posted */
+    (void) readl (&dev->usb->usbctl);
+}
+
+static void Defect7374_EnableDataEpsAtZero (struct usb338x *dev);
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ */
+static int
+usb338x_start(struct usb_gadget *_gadget, struct usb_gadget_driver *driver)
+{
+    struct usb338x  *dev;
+    int             retval;
+    unsigned        i;
+    u32             value;
+
+    /* insist on high speed support from the driver, since
+     * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
+     * "must not be used in normal operation"
+     */
+    if (!driver || driver->max_speed < USB_SPEED_HIGH || !driver->setup)
+        return -EINVAL;
+
+    dev = container_of (_gadget, struct usb338x, gadget);
+
+    for (i = 0; i < n_ep; i++)
+        dev->ep [i].irqs = 0;
+
+    /* hook up the driver ... */
+    dev->softconnect = 1;
+    driver->driver.bus = NULL;
+    dev->driver = driver;
+    dev->gadget.dev.driver = &driver->driver;
+
+    retval = device_create_file (&dev->pdev->dev, &dev_attr_function);
+
+    if (retval) goto err_unbind;
+
+    retval = device_create_file (&dev->pdev->dev, &dev_attr_queues);
+
+    if (retval) goto err_func;
+
+    /* ... then enable host detection and ep0; and we're ready
+     * for set_configuration as well as eventual disconnect.
+     */
+
+    value = readl (&dev->regs->gpioctl);
+    value |= GPIO2_DATA;
+
+    writel (value, &dev->regs->gpioctl);
+
+    Defect7374_EnableDataEpsAtZero(dev);
+
+    ep0_start (dev);
+
+    DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
+           driver->driver.name,
+           readl (&dev->usb->usbctl),
+           readl (&dev->usb->stdrsp));
+
+    /* pci writes may still be posted */
+    return 0;
+
+err_func:
+    device_remove_file (&dev->pdev->dev, &dev_attr_function);
+
+err_unbind:
+    driver->unbind (&dev->gadget);
+    dev->gadget.dev.driver = NULL;
+    dev->driver = NULL;
+
+    return retval;
+}
+
+
+static void
+stop_activity (struct usb338x *dev, struct usb_gadget_driver *driver)
+{
+    int     i;
+
+    /* don't disconnect if it's not connected */
+    if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+        driver = NULL;
+
+    /* stop hardware; prevent new request submissions;
+     * and kill any outstanding requests.
+     */
+    usb_reset (dev);
+
+    for (i = 0; i < n_ep; i++)
+        nuke (&dev->ep [i]);
+
+    usb_reinit (dev);
+}
+
+
+static int
+usb338x_stop(struct usb_gadget *_gadget, struct usb_gadget_driver *driver)
+{
+    struct usb338x  *dev;
+    unsigned long   flags;
+    u32             value;
+
+    dev = container_of (_gadget, struct usb338x, gadget);
+
+    spin_lock_irqsave (&dev->lock, flags);
+
+    stop_activity (dev, driver);
+
+    spin_unlock_irqrestore (&dev->lock, flags);
+
+    dev->gadget.dev.driver = NULL;
+    dev->driver = NULL;
+
+    value = readl (&dev->regs->gpioctl);
+    value &= ~GPIO2_DATA;
+
+    writel (value, &dev->regs->gpioctl);
+
+    device_remove_file (&dev->pdev->dev, &dev_attr_function);
+    device_remove_file (&dev->pdev->dev, &dev_attr_queues);
+
+    DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
+
+    return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void
+gadget_release (struct device *_dev)
+{
+    struct usb338x  *dev = dev_get_drvdata (_dev);
+
+    kfree (dev);
+}
+
+
+/* tear down the binding between this driver and the pci device */
+static void
+usb338x_remove (struct pci_dev *pdev)
+{
+    struct usb338x      *dev = pci_get_drvdata (pdev);
+
+    usb_del_gadget_udc(&dev->gadget);
+
+    BUG_ON(dev->driver);
+
+    /* then clean up the resources we allocated during probe() */
+    writel (readl (&dev->regs->gpioctl) & ~0x0f, &dev->regs->gpioctl);
+
+    if (dev->requests)
+    {
+        int     i;
+        for (i = 1; i < 5; i++)
+        {
+            if (!dev->ep [i].dummy)
+                continue;
+
+            pci_pool_free (dev->requests, dev->ep [i].dummy,
+                           dev->ep [i].td_dma);
+        }
+        pci_pool_destroy (dev->requests);
+    }
+
+    if (dev->got_irq)
+        free_irq (pdev->irq, dev);
+
+    if (use_msi)
+        pci_disable_msi(pdev);
+
+    if (dev->regs)
+        iounmap (dev->regs);
+
+    if (dev->region)
+        release_mem_region (pci_resource_start (pdev, 0),
+                            pci_resource_len (pdev, 0));
+
+    if (dev->enabled)
+        pci_disable_device (pdev);
+
+    device_unregister (&dev->gadget.dev);
+    device_remove_file (&pdev->dev, &dev_attr_registers);
+    pci_set_drvdata (pdev, NULL);
+
+    INFO (dev, "unbind\n");
+}
+
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int
+usb338x_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+    struct usb338x      *dev;
+    unsigned long       resource, len;
+    void       __iomem  *base = NULL;
+    int                 retval, i;
+    u32                 Scratch, FsmValue;
+
+
+    /* alloc, and start init */
+    dev = kzalloc (sizeof * dev, GFP_KERNEL);
+
+    if (dev == NULL)
+    {
+        retval = -ENOMEM;
+
+        goto done;
+    }
+
+    pci_set_drvdata (pdev, dev);
+
+    spin_lock_init (&dev->lock);
+
+    dev->pdev             = pdev;
+    dev->gadget.ops       = &usb338x_ops;
+    //   dev->gadget.max_speed = USB_SPEED_HIGH;
+    dev->gadget.max_speed = USB_SPEED_SUPER;
+
+    /* the "gadget" abstracts/virtualizes the controller */
+    dev_set_name(&dev->gadget.dev, "gadget");
+
+    dev->gadget.dev.parent   = &pdev->dev;
+    dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+    dev->gadget.dev.release  = gadget_release;
+    dev->gadget.name         = driver_name;
+
+    /* now all the pci goodies ... */
+    if (pci_enable_device (pdev) < 0)
+    {
+        retval = -ENODEV;
+        goto done;
+    }
+    dev->enabled = 1;
+
+    resource = pci_resource_start (pdev, 0);
+    len = pci_resource_len (pdev, 0);
+
+    if (!request_mem_region (resource, len, driver_name))
+    {
+        DEBUG (dev, "controller already in use\n");
+
+        retval = -EBUSY;
+
+        goto done;
+    }
+
+    dev->region = 1;
+
+    base = ioremap_nocache (resource, len);
+
+    if (base == NULL)
+    {
+        DEBUG (dev, "can't map memory\n");
+        retval = -EFAULT;
+        goto done;
+    }
+
+    dev->regs           = (struct usb338x_regs __iomem *) base;
+    dev->usb            = (struct usb338x_usb_regs __iomem *) (base + 0x0080);
+    dev->pci            = (struct usb338x_pci_regs __iomem *) (base + 0x0100);
+    dev->dma            = (struct usb338x_dma_regs __iomem *) (base + 0x0180);
+    dev->dep            = (struct usb338x_dep_regs __iomem *) (base + 0x0200);
+    dev->epcfg          = (struct usb338x_ep_cfg __iomem *) (base + 0x0300);
+    dev->epregs         = (struct usb338x_ep_regs __iomem *) (base + 0x0304);
+    dev->fiforegs       = (struct usb338x_fifo_regs __iomem *) (base + 0x0500);
+    dev->llregs         = (struct usb338x_ll_regs __iomem *) (base + 0x0700);
+    dev->ll_lfps_regs   = (struct usb338x_ll_lfps_regs __iomem *) (base + 0x0748);
+    dev->ll_tsn_regs    = (struct usb338x_ll_tsn_regs __iomem *) (base + 0x077c);
+    dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) (base + 0x079c);
+    dev->plregs         = (struct usb338x_pl_regs __iomem *) (base + 0x0800);
+    dev->factory_regs   = (struct usb338x_factory_regs __iomem *) (base + 0x0B80);
+    dev->phy_regs       = (struct usb338x_phy_regs __iomem *) (base + 0x0BE0);
+
+    u32_i = readl(&dev->usb->usbstat);
+
+    if (u32_i == 0xFFFFFFFF)
+    {
+        DEBUG (dev, "usb338x not existed\n");
+        retval = -ENODEV;
+
+        goto done;
+    }
+
+    // Enhanced or Legacy Mode
+    enhanced_mode = (u32_i & (1 << 11)) ? 1 : 0;
+    n_ep = (enhanced_mode) ? 9 : 5;
+    // n_ep = (enhanced_mode) ? 5 : 5;
+
+    /* put into initial config, link up all endpoints */
+
+    writel (SCRATCH, &dev->regs->idxaddr);
+    Scratch = readl (&dev->regs->idxdata);
+
+    FsmValue = Scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+    /* See if firmware needs to set up for workaround: */
+    if (FsmValue == DEFECT7374_FSM_SS_CONTROL_READ)
+    {
+        writel (0, &dev->usb->usbctl);
+    }
+
+    usb_reset (dev);
+    usb_reinit (dev);
+
+    /* irq setup after old hardware is cleaned up */
+    if (!pdev->irq)
+    {
+        ERROR (dev, "No IRQ.  Check PCI setup!\n");
+        retval = -ENODEV;
+        goto done;
+    }
+
+    if (use_msi)
+    {
+        if (pci_enable_msi(pdev))
+        {
+            printk ("Failed to enable MSI mode\n");
+        }
+    }
+
+    if (request_irq (pdev->irq, usb338x_irq, IRQF_SHARED, driver_name, dev) != 0)
+    {
+        ERROR (dev, "request interrupt %d failed\n", pdev->irq);
+        retval = -EBUSY;
+
+        goto done;
+    }
+
+    dev->got_irq = 1;
+
+    /* DMA setup */
+    /* NOTE:  we know only the 32 LSBs of dma addresses may be nonzero */
+    dev->requests = pci_pool_create ("requests", pdev,
+                                     sizeof (struct usb338x_dma),
+                                     0 /* no alignment requirements */,
+                                     0 /* or page-crossing issues */);
+
+    if (!dev->requests)
+    {
+        DEBUG (dev, "can't get request pool\n");
+        retval = -ENOMEM;
+        goto done;
+    }
+
+    for (i = 1; i < 5; i++)
+    {
+        struct usb338x_dma  *td;
+
+        td = pci_pool_alloc (dev->requests, GFP_KERNEL,
+                             &dev->ep [i].td_dma);
+        if (!td)
+        {
+            DEBUG (dev, "can't get dummy %d\n", i);
+            retval = -ENOMEM;
+            goto done;
+        }
+
+        td->dmacount = 0;   /* not VALID */
+        td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID);
+        td->dmadesc = td->dmaaddr;
+        dev->ep [i].dummy = td;
+    }
+
+    pci_set_master (pdev);
+    pci_try_set_mwi (pdev);
+
+    /* ... also flushes any posted pci writes */
+    writel (REG_CHIPREV, &dev->regs->idxaddr);
+
+    dev->chiprev = readl (&dev->regs->idxdata) & 0xffff;
+
+    /* done */
+    INFO (dev, "%s\n", driver_desc);
+    INFO (dev, "irq %d, pci mem %p, chip rev %04x\n",
+          pdev->irq, base, dev->chiprev);
+    INFO (dev, "version: " DRIVER_VERSION "; dma %s %s\n",
+          use_dma
+          ? (use_dma_chaining ? "chaining" : "enabled")
+              : "disabled", enhanced_mode ? "enhanced mode" : "legacy mode");
+
+    retval = device_register (&dev->gadget.dev);
+
+    if (retval) goto done;
+
+    retval = device_create_file (&pdev->dev, &dev_attr_registers);
+
+    if (retval) goto done;
+
+    retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+
+    if (retval)
+        goto done;
+
+    return 0;
+
+done:
+
+    if (dev)
+        usb338x_remove (pdev);
+
+    return retval;
+}
+
+
+/* make sure the board is quiescent; otherwise it will continue
+ * generating IRQs across the upcoming reboot.
+ */
+
+static void
+usb338x_shutdown (struct pci_dev *pdev)
+{
+    struct usb338x    *dev = pci_get_drvdata (pdev);
+
+    /* disable IRQs */
+    writel (0, &dev->regs->pciirqenb0);
+    writel (0, &dev->regs->pciirqenb1);
+
+    /* disable the pullup so the host will think we're gone */
+    writel (0, &dev->usb->usbctl);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+allow_status (struct usb338x_ep *ep)
+{
+    /* Control Status Phase Handshake was set by the chip when the setup
+     * packet arrived. While set, the chip automatically NAKs the host's
+     * Status Phase tokens.
+     */
+    writel (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE, &ep->regs->ep_rsp);
+
+    ep->stopped = 1;
+
+    /* TD 9.9 Halt Endpoint test.  TD 9.22 set feature test. */
+    ep->responded = 0;
+}
+
+
+static void
+set_fifo_bytecount (struct usb338x_ep *ep, unsigned count)
+{
+    u32 tmp;
+
+    tmp = readl (&ep->cfg->ep_cfg) & (~(0x07 << EP_FIFO_BYTE_COUNT));
+
+    writel (tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
+}
+
+
+static void
+ep_stdrsp (struct usb338x_ep *ep, int value, int wedged)
+{
+    /* set/clear, then synch memory views with the device */
+    if (value)
+    {
+        ep->stopped = 1;
+
+        if (ep->num == 0)
+        {
+            ep->dev->protocol_stall = 1;
+        }
+        else
+        {
+            if (ep->dma)
+                ep_stop_dma (ep);
+
+            ep_stall (ep, true);
+        }
+
+        if (wedged)
+            ep->wedged = 1;
+    }
+    else
+    {
+        ep->stopped = 0;
+        ep->wedged = 0;
+
+        ep_stall (ep, false);
+
+        /* Flush the queue */
+        if (!list_empty(&ep->queue))
+        {
+            struct usb338x_request *req = list_entry(ep->queue.next, struct usb338x_request, queue);
+
+            if (ep->dma)
+                resume_dma (ep);
+            else
+            {
+                if (ep->is_in)
+                    write_fifo(ep, &req->req);
+                else
+                {
+                    if (read_fifo(ep, req))
+                        complete_req (ep, req, 0);
+                }
+            }
+        }
+    }
+}
+
+
+static void
+ep_stall (struct usb338x_ep *ep, int stall)
+{
+    struct usb338x *dev = ep->dev;
+    u32             val;
+
+    if (stall)
+    {
+        writel ((1 << SET_ENDPOINT_HALT) |
+                // (1 << SET_NAK_PACKETS) |
+                (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+                &ep->regs->ep_rsp);
+
+        ep->is_halt = 1;
+    }
+    else
+    {
+
+        if (dev->gadget.speed == USB_SPEED_SUPER)
+        {
+            /* Workaround for SS SeqNum not cleared via Endpoint Halt (Clear) bit.*/
+            // select endpoint
+            val = readl(&dev->plregs->pl_ep_ctrl);
+            val = (val & ~ 0x1f) | ep_pl[ep->num];
+            writel (val, &dev->plregs->pl_ep_ctrl);
+
+            val |= (1 << SEQUENCE_NUMBER_RESET);
+            writel (val, &dev->plregs->pl_ep_ctrl);
+        }
+
+        val = readl(&ep->regs->ep_rsp);
+        val |= (1 << CLEAR_ENDPOINT_HALT) | (1 << CLEAR_ENDPOINT_TOGGLE);
+        writel (val
+                // | (1 << CLEAR_NAK_PACKETS)
+                // | (1 << CLEAR_ENDPOINT_TOGGLE)
+                , &ep->regs->ep_rsp);
+
+        ep->is_halt = 0;
+
+        val = readl(&ep->regs->ep_rsp);
+    }
+
+}
+
+
+static void
+resume_dma (struct usb338x_ep *ep)
+{
+    writel (readl (&ep->dma->dmactl) | (1 << DMA_ENABLE), &ep->dma->dmactl);
+
+    ep->dma_started = true;
+}
+
+
+static void
+ep_stop_dma (struct usb338x_ep *ep)
+{
+    writel (readl (&ep->dma->dmactl) & ~(1 << DMA_ENABLE), &ep->dma->dmactl);
+    spin_stop_dma (ep->dma);
+
+    ep->dma_started = false;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids [] =
+{
+    {
+        .class      =  ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+        .class_mask =  ~0,
+        .vendor     =  0x10b5,
+        .device     =  0x3380,
+        .subvendor  =  PCI_ANY_ID,
+        .subdevice  =  PCI_ANY_ID,
+    },
+    {
+        .class      =  ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+        .class_mask =  ~0,
+        .vendor     =  0x10b5,
+        .device     =  0x3382,
+        .subvendor  =  PCI_ANY_ID,
+        .subdevice  =  PCI_ANY_ID,
+
+    },
+    { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver usb338x_pci_driver =
+{
+    .name     = (char *) driver_name,
+    .id_table = pci_ids,
+
+    .probe    = usb338x_probe,
+    .remove   = usb338x_remove,
+    .shutdown = usb338x_shutdown,
+
+};
+
+
+static int
+usb338x_pdc_register(void)
+{
+    return pci_register_driver(&usb338x_pci_driver);
+}
+
+
+static void
+usb338x_pdc_unregister(void)
+{
+    pci_unregister_driver(&usb338x_pci_driver);
+}
+
+
+static int __init
+init (void)
+{
+    int r_val;
+
+    if (!use_dma)
+        use_dma_chaining = 0;
+
+    r_val = usb338x_pdc_register();
+
+    return r_val;
+}
+module_init (init);
+
+
+static void __exit
+cleanup (void)
+{
+    usb338x_pdc_unregister();
+}
+module_exit (cleanup);
+
+
+MODULE_DESCRIPTION ("usb338x");
+MODULE_AUTHOR ("PLX Technology, Inc.");
+MODULE_LICENSE ("GPL");
diff --git a/drivers/usb/gadget/usb338x.h b/drivers/usb/gadget/usb338x.h
new file mode 100644
index 0000000..93c6301
--- /dev/null
+++ b/drivers/usb/gadget/usb338x.h
@@ -0,0 +1,952 @@
+/******************************************************************************
+ * Driver for PLX USB3380/USB3382 USB device controller
+ *
+ * Copyright (c) PLX Technology, Inc.
+ *
+ * PLX Technology Inc. licenses this source file under the GNU Lesser General
+ * Public License (LGPL) version 2.  This program is free software; the source
+ * file may be modified or redistributed under the terms of the LGPL and
+ * without express permission from PLX Technology.
+ *
+ * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY,
+ * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee
+ * or representations regarding the use of, or the results of the use of,
+ * the software and documentation in terms of correctness, accuracy,
+ * reliability, currentness, or otherwise; and you rely on the software,
+ * documentation and results solely at your own risk.
+ *
+ * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS,
+ * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES
+ * OF ANY KIND.
+ *
+ * Specs and errata are available from http://www.plxtech.com.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * File Name:
+ *
+ *     usb338x.h
+ *
+ * Revision: v3.0, 2-5-2014
+ *
+ * Description:
+ *
+ *     The header file for the PLX usb3380/3382 device driver
+ *
+ *****************************************************************************/
+
+#ifndef __USB338x_H
+#define __USB338x_H
+
+/* USB338x MEMORY MAPPED REGISTERS
+ *
+ * The register layout came from the chip documentation, and the bit
+ * number definitions were extracted from chip specification.
+ *
+ * Use the shift operator ('<<') to build bit masks, with readl/writel
+ * to access the registers through PCI.
+ */
+
+/* main registers, BAR0 + 0x0000 */
+struct usb338x_regs
+{
+    /* offset 0x0000 */
+    u32   devinit;
+#define     LOCAL_CLOCK_FREQUENCY                               8  /* 11:8 */
+#define     FORCE_PCI_RESET                                     7
+#define     PCI_ID                                              6
+#define     PCI_ENABLE                                          5
+#define     FIFO_SOFT_RESET                                     4
+#define     CONFIGURATION_SOFT_RESET                            3
+#define     PCI_SOFT_RESET                                      2
+#define     USB_SOFT_RESET                                      1
+#define     M8051_RESET                                         0
+    u32   eectl;
+#define     EEPROM_ADDRESS_WIDTH                                23
+#define     EEPROM_CHIP_SELECT_ACTIVE                           22
+#define     EEPROM_PRESENT                                      21
+#define     EEPROM_VALID                                        20
+#define     EEPROM_BUSY                                         19
+#define     EEPROM_CHIP_SELECT_ENABLE                           18
+#define     EEPROM_BYTE_READ_START                              17
+#define     EEPROM_BYTE_WRITE_START                             16
+#define     EEPROM_READ_DATA                                    8
+#define     EEPROM_WRITE_DATA                                   0
+    u32   eeclkfreq;
+    u32   _unused0;
+    /* offset 0x0010 */
+    u32   pciirqenb0;  /* PCIE Interrupt Request Enable 0 */
+#define     GPEP3_IN_PCIE_INTERRUPT_ENABLE                      20
+#define     GPEP2_IN_PCIE_INTERRUPT_ENABLE                      19
+#define     GPEP1_IN_PCIE_INTERRUPT_ENABLE                      18
+#define     GPEP0_IN_PCIE_INTERRUPT_ENABLE                      17
+#define     USB_IN_FIFO_TIMEOUT_PCIE_INTERRUPT_ENABLE           11
+#define     USB_CONFIGURATION_RETRY_INTERRUPT_ENABLE            10
+#define     USB2PCIE_TLP_DRAINED_PORT_1_PCIE_INTERRUPT_ENABLE   9
+#define     USB2PCIE_TLP_DRAINED_PORT_0_PCIE_INTERRUPT_ENABLE   8
+#define     SETUP_PACKET_PCIE_INTERRUPT_ENABLE                  7
+#define     GPEP3_OUT_PCIE_INTERRUPT_ENABLE                     4
+#define     GPEP2_OUT_PCIE_INTERRUPT_ENABLE                     3
+#define     GPEP1_OUT_PCIE_INTERRUPT_ENABLE                     2
+#define     GPEP0_OUT_PCIE_INTERRUPT_ENABLE                     1
+#define     ENDPOINT_0_PCIE_INTERRUPT_ENABLE                    0
+    u32   pciirqenb1; /* PCIE Interrupt Request Enable 1 */
+#define     GLOBAL_PCIE_INTERRUPT_ENABLE                            31
+#define     PCIE_ENDPOINT_POWER_MANAGEMENT_PCIE_INTERRUPT_ENABLE    30
+#define     PCIE_HOT_RESET_PCIE_INTERRUPT_ENABLE                    29
+#define     PCIE_DL_DOWN_STATE_CHANGE_PCIE_INTERRUPT_ENABLE         28
+#define     POWER_STATE_CHANGE_PCIE_INTERRUPT_ENABLE                27
+#define     PCI_PARITY_ERROR_PCIE_INTERRUPT_ENABLE                  25
+#define     PCI_MASTER_ABORT_RECEIVED_PCIE_INTERRUPT_ENABLE         20
+#define     PCI_TARGET_ABORT_RECEIVED_PCIE_INTERRUPT_ENABLE         19
+#define     PCIE_HOT_PLUG_PCIE_INTERRUPT_ENABLE                     18
+#define     PCI_MASTER_CYCLE_DONE_PCIE_INTERRUPT_ENABLE             16
+#define     SOF_DOWNCOUNT_PCIE_INTERRUPT_ENABLE                     14
+#define     GPIO_PCIE_INTERRUPT_ENABLE                              13
+#define     DMA_3_PCIE_INTERRUPT_ENABLE                             12
+#define     DMA_2_PCIE_INTERRUPT_ENABLE                             11
+#define     DMA_1_PCIE_INTERRUPT_ENABLE                             10
+#define     DMA_0_PCIE_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_PCIE_INTERRUPT_ENABLE                        8
+#define     VBUS_PCIE_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_PCIE_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_PCIE_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_PCIE_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_PCIE_INTERRUPT_ENABLE             2
+#define     RESUME_PCIE_INTERRUPT_ENABLE                             1
+#define     SOF_PCIE_INTERRUPT_ENABLE                                0
+    u32   cpuirqenb0;    /* 8051 Interrupt Request Enable 0 */
+#define     GPEP3_IN_8051_INTERRUPT_ENABLE                      20
+#define     GPEP2_IN_8051_INTERRUPT_ENABLE                      19
+#define     GPEP1_IN_8051_INTERRUPT_ENABLE                      18
+#define     GPEP0_IN_8051_INTERRUPT_ENABLE                      17
+#define     USB_IN_FIFO_TIMEOUT_8051_INTERRUPT_ENABLE           11
+#define     USB_CFG_RETRY_8051_INTERRUPT_ENABLE                 10
+#define     USB2PCIE_TLP_DRAINED_PORT_1_8051_INTERRUPT_ENABLE   9
+#define     USB2PCIE_TLP_DRAINED_PORT_0_8051_INTERRUPT_ENABLE   8
+#define     SETUP_PACKET_8051_INTERRUPT_ENABLE                  7
+#define     GPEP3_OUT_8051_INTERRUPT_ENABLE                     4
+#define     GPEP2_OUT_8051_INTERRUPT_ENABLE                     3
+#define     GPEP1_OUT_8051_INTERRUPT_ENABLE                     2
+#define     GPEP0_OUT_8051_INTERRUPT_ENABLE                     1
+#define     ENDPOINT_0_8051_INTERRUPT_ENABLE                    0
+    u32       cpuirqenb1;
+#define     GLOBAL_8051_INTERRUPT_ENABLE                        31
+#define     PCIE_ENDPOINT_POWER_MGMT_8051_INTERRUPT_ENABLE      30
+#define     PCIE_HOT_RESET_8051_INTERRUPT_ENABLE                29
+#define     PCIE_DL_DOWN_STATE_CHANGE_8051_INTERRUPT_ENABLE     28
+#define     POWER_STATE_CHANGE_8051_INTERRUPT_ENABLE            27
+#define     PCI_PARITY_ERROR_8051_INTERRUPT_ENABLE              25
+#define     PCI_INTA_8051_INTERRUPT_ENABLE                      24
+#define     PCI_MASTER_ABORT_RECEIVED_8051_INTERRUPT_ENABLE     20
+#define     PCI_TARGET_ABORT_RECEIVED_8051_INTERRUPT_ENABLE     19
+#define     PCIE_HOT_PLUG_8051_INTERRUPT_ENABLE                 18
+#define     PCI_MASTER_CYCLE_DONE_8051_INTERRUPT_ENABLE         16
+#define     SOF_DOWNCOUNT_8051_INTERRUPT_ENABLE                 14
+#define     GPIO_8051_INTERRUPT_ENABLE                          13
+#define     DMA_3_8051_INTERRUPT_ENABLE                         12
+#define     DMA_2_8051_INTERRUPT_ENABLE                         11
+#define     DMA_1_8051_INTERRUPT_ENABLE                         10
+#define     DMA_0_8051_INTERRUPT_ENABLE                         9
+#define     SERIAL_EEPROM_DONE_8051_INTERRUPT_ENABLE            8
+#define     VBUS_8051_INTERRUPT_ENABLE                          7
+#define     CONTROL_STATUS_8051_INTERRUPT_ENABLE                6
+#define     ROOT_PORT_RESET_8051_INTERRUPT_ENABLE               4
+#define     SUSPEND_REQUEST_8051_INTERRUPT_ENABLE               3
+#define     SUSPEND_REQUEST_CHANGE_8051_INTERRUPT_ENABLE        2
+#define     RESUME_8051_INTERRUPT_ENABLE                        1
+#define     SOF_8051_INTERRUPT_ENABLE                           0
+    /* offset 0x0020 */
+    u32       usbirqenb0; /* Used only by firmware running on the 8051 */
+    u32       usbirqenb1;     /* Reserved in Legacy Adapter mode */
+#define     USB_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_INTA_INTERRUPT_ENABLE                           24
+#define     PCI_PME_INTERRUPT_ENABLE                            23
+#define     PCI_SERR_INTERRUPT_ENABLE                           22
+#define     PCI_PERR_INTERRUPT_ENABLE                           21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_3_INTERRUPT_ENABLE                              12
+#define     DMA_2_INTERRUPT_ENABLE                              11
+#define     DMA_1_INTERRUPT_ENABLE                              10
+#define     DMA_0_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+    u32   irqstat0;
+    /* GPEP[0..3]_IN_PCIE_INTERRUPT_STATUS - Enahnced Mode only */
+#define     GPEP3_IN_PCIE_INTERRUPT_STATUS                      20
+#define     GPEP2_IN_PCIE_INTERRUPT_STATUS                      19
+#define     GPEP1_IN_PCIE_INTERRUPT_STATUS                      18
+#define     GPEP0_IN_PCIE_INTERRUPT_STATUS                      17
+#define     INTA_ASSERTED                                       12
+#define     USB_IN_FIFO_TIMEOUT_INTERRUPT_STATUS                11
+#define     USB_CONFIGURATION_RETRY_INTERRUPT_STATUS            10
+#define     USB2PCIE_TLP_DRAINED_PORT_1_STATUS                  9
+#define     USB2PCIE_TLP_DRAINED_PORT_0_STATUS                  8
+#define     SETUP_PACKET_INTERRUPT_STATUS                       7
+#define     GPEP3_OUT_PCIE_INTERRUPT_STATUS                     4
+#define     GPEP2_OUT_PCIE_INTERRUPT_STATUS                     3
+#define     GPEP1_OUT_PCIE_INTERRUPT_STATUS                     2
+#define     GPEP0_OUT_PCIE_INTERRUPT_STATUS                     1
+#define     ENDPOINT_0_INTERRUPT_STATUS                         0
+    u32   irqstat1;
+#define     PCIE_ENDPOINT_POWER_MGMT_INTERRUPT                  30
+#define     PCIE_HOT_RESET_INTERRUPT                            29
+#define     PCIE_DL_DOWN_STATE_CHANGE_INTERRUPT                 28
+#define     POWER_STATE_CHANGE_INTERRUPT                        27
+#define     PCI_PARITY_ERROR_INTERRUPT                          25
+#define     PCI_INTA_INTERRUPT                                  24
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT                 20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT                 19
+#define     PCIE_HOT_PLUG_INTERRUPT                             18
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT                     16
+#define     SOF_DOWNCOUNT_INTERRUPT                             14
+#define     GPIO_INTERRUPT                                      13
+#define     DMA_3_INTERRUPT                                     12
+#define     DMA_2_INTERRUPT                                     11
+#define     DMA_1_INTERRUPT                                     10
+#define     DMA_0_INTERRUPT                                     9
+#define     SERIAL_EEPROM_DONE_INTERRUPT                        8
+#define     VBUS_INTERRUPT                                      7
+#define     CONTROL_STATUS_INTERRUPT                            6
+#define     ROOT_PORT_RESET_INTERRUPT                           4
+#define     SUSPEND_REQUEST_INTERRUPT                           3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT                    2
+#define     RESUME_INTERRUPT                                    1
+#define     SOF_INTERRUPT                                       0
+    /* offset 0x0030 */
+    u32   idxaddr;
+#define     IDXREG_DIAG            0x00
+#define     IDXREG_PKTLEN          0x01
+#define     IDXREG_FRAME           0x02
+#define     IDXREG_CHIPREV         0x03
+#define     IDXREG_UFRAME          0x04
+#define     IDXREG_FRAME_COUNT     0x05
+#define     IDXREG_HS_MAXPOWER     0x06
+#define     IDXREG_FS_MAXPOWER     0x07
+#define     IDXREG_HS_INTPOLL_RATE 0x08
+#define     IDXREG_FS_INTPOLL_RATE 0x09
+#define     HS_NAK_RATE            0x0a
+#define     SCRATCH                0x0b
+    /* Index 0ch - 1fh: Reserved */
+    /* Index 20h + n * 10h: GPEP[3:0/Out/In]_HS_MAXPKT */
+    u32   idxdata;
+    u32   fifoctl;
+#define     PCI_BASE2_RANGE                                     16  /* 31:16 */
+#define     IGNORE_FIFO_AVAILABILITY                            3
+#define     PCI_BASE2_SELECT                                    2
+#define     FIFO_CONFIGURATION_SELECT                           0   /* 1:0 */
+    u32   bar2ctl;
+#define     PCI_BAR2_RANGE                                      16
+    /* offset 0x0040 */
+    u32   memaddr;
+#define     START                                               28
+#define     DIRECTION                                           27
+#define     FIFO_DIAGNOSTIC_SELECT                              24
+#define     MEMORY_ADDRESS                                      0
+    u32   memdata0;
+    u32   memdata1;
+    u32   _unused3;
+    /* offset 0x0050 */
+    u32   gpioctl;
+#define     GPIO3_PWM_ENABLE                                    27
+#define     GPIO2_PWM_ENABLE                                    26
+#define     GPIO1_PWM_ENABLE                                    25
+#define     GPIO0_PWM_ENABLE                                    24
+#define     GPIO3_INPUT_DEBOUNCE_ENABLE                         19
+#define     GPIO2_INPUT_DEBOUNCE_ENABLE                         18
+#define     GPIO1_INPUT_DEBOUNCE_ENABLE                         17
+#define     GPIO0_INPUT_DEBOUNCE_ENABLE                         16
+#define     GPIO3_LED_SELECT                                    12
+#define     GPIO3_INTERRUPT_ENABLE                              11
+#define     GPIO2_INTERRUPT_ENABLE                              10
+#define     GPIO1_INTERRUPT_ENABLE                              9
+#define     GPIO0_INTERRUPT_ENABLE                              8
+#define     GPIO3_OUTPUT_ENABLE                                 7
+#define     GPIO2_OUTPUT_ENABLE                                 6
+#define     GPIO1_OUTPUT_ENABLE                                 5
+#define     GPIO0_OUTPUT_ENABLE                                 4
+#define     GPIO3_DATA                                          3
+#define     GPIO2_DATA                                          2
+#define     GPIO1_DATA                                          1
+#define     GPIO0_DATA                                          0
+    u32   gpiostat;
+#define     GPIO3_INTERRUPT                                     3
+#define     GPIO2_INTERRUPT                                     2
+#define     GPIO1_INTERRUPT                                     1
+#define     GPIO0_INTERRUPT                                     0
+} __attribute__ ((packed));
+
+/* usb control, BAR0 + 0x0080 */
+struct usb338x_usb_regs
+{
+    /* offset 0x0080 */
+    u32   stdrsp;
+#define     STALL_UNSUPPORTED_REQUESTS                          31
+#define     SET_ISOCHRONOUS_DELAY                               24
+#define     GET_STRING_DESCRIPTOR_3                             23
+#define     SET_SEL                                             22
+#define     GET_BOS_DESCRIPTOR                                  21
+#define     SET_CLR_LTM_ENABLE                                  20
+#define     SET_CLR_U2_ENABLE                                   19
+#define     SET_CLR_U1_ENABLE                                   18
+#define     SET_CLR_FUNCTION_SUSPEND                            17
+#define     SET_TEST_MODE                                       16
+#define     GET_OTHER_SPEED_CONFIGURATION                       15
+#define     GET_DEVICE_QUALIFIER                                14
+#define     SET_ADDRESS                                         13
+#define     ENDPOINT_SET_CLEAR_HALT                             12
+#define     DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP               11
+#define     GET_STRING_DESCRIPTOR_2                             10
+#define     GET_STRING_DESCRIPTOR_1                             9
+#define     GET_STRING_DESCRIPTOR_0                             8
+#define     GET_SET_INTERFACE                                   6
+#define     GET_SET_CONFIGURATION                               5
+#define     GET_CONFIGURATION_DESCRIPTOR                        4
+#define     GET_DEVICE_DESCRIPTOR                               3
+#define     GET_ENDPOINT_STATUS                                 2
+#define     GET_INTERFACE_STATUS                                1
+#define     GET_DEVICE_STATUS                                   0
+    u32   prodvendid;
+#define     PRODUCT_ID                                          16
+#define     VENDOR_ID                                           0
+    u32   relnum;
+#define     DEVICE_RELEASE_NUMBER                               0
+    u32   usbctl;
+#define usbctl_def    (unsigned int) 0x0x00003841
+#define     PRODUCT_ID_STRING_ENABLE                            13
+#define     VENDOR_ID_STRING_ENABLE                             12
+#define     USB_ROOT_PORT_WAKEUP_ENABLE                         11
+#define     VBUS_PIN                                            10
+#define     TIMED_DISCONNECT                                    9
+#define     IMMEDIATELY_SUSPEND                                 7
+#define     SELF_POWERED_USB_DEVICE                             6
+#define     REMOTE_WAKEUP_SUPPORT                               5
+#define     PME_POLARITY                                        4
+#define     USB_DETECT_ENABLE                                   3
+#define     PCIE_WAKEUP_ENABLE                                  2
+#define     REMOTE_WAKEUP_ENABLE                                1
+#define     SELF_POWERED_STATUS                                 0
+    /* offset 0x0090 */
+    u32   usbstat;
+#define     HOST_MODE                                           12
+#define     ENHANCED_MODE                                       11
+#define     REMOTE_WAKEUP_STATUS                                10
+#define     SUSPEND_STATUS                                      9
+#define     SUPER_SPEED_MODE                                    8
+#define     HIGH_SPEED_MODE                                     7
+#define     FULL_SPEED_MODE                                     6
+#define     GENERATE_RESUME                                     5
+#define     GENERATE_DEVICE_REMOTE_WAKEUP                       4
+    u32   xcvrdiag;
+#define     FORCE_HIGH_SPEED_MODE                               31
+#define     FORCE_FULL_SPEED_MODE                               30
+#define     USB_TEST_MODE                                       24  /* 26:24 */
+#define     LINE_STATE                                          16  /* 17:16 */
+#define     TRANSCEIVER_OPERATION_MODE                          2   /* 3:2 */
+#define     TRANSCEIVER_SELECT                                  1
+#define     TERMINATION_SELECT                                  0
+    u32   setupdw0;
+    u32   setupdw1;
+    /* offset 0x0090 */
+    u32   _unused0;
+    u32   ouraddr;
+#define     FORCE_IMMEDIATE                                     7
+#define     OUR_USB_ADDRESS                                     0   /* 6:0 */
+    u32   ourconfig;
+    u32       _unused1[2];
+    /* offset 0x00B4 */
+    u32     usbclass;
+#define     DEVICE_PROTOCOL                     16  /* 23:16 */
+#define     DEVICE_SUB_CLASS                     8  /* 15:8 */
+#define     DEVICE_CLASS                         0  /* 7:0 */
+    u32     ss_sel;
+#define     U2_SYSTEM_EXIT_LATENCY                 8    /* 23:8 */
+#define     U1_SYSTEM_EXIT_LATENCY                 0    /* 7:0 */
+    u32     ss_del;
+#define     U2_DEVICE_EXIT_LATENCY                 8    /* 23:8 */
+#define     U1_DEVICE_EXIT_LATENCY                 0    /* 7:0 */
+    u32     usb2lpm;
+#define     USB_L1_LPM_HIRD                        2    /* 5:2 */
+#define     USB_L1_LPM_REMOTE_WAKE                 1
+#define     USB_L1_LPM_SUPPORT                     0
+    u32     usb3belt;
+#define     BELT_MULTIPLIER                       10    /* 11:10 */
+#define     BEST_EFFORT_LATENCY_TOLERANCE          0    /* 9:0 */
+    u32     usbctl2;
+#define     LTM_ENABLE                          7
+#define     U2_ENABLE                           6
+#define     U1_ENABLE                           5
+#define     FUNCTION_SUSPEND                    4
+#define     USB3_CORE_ENABLE                    3
+#define     USB2_CORE_ENABLE                    2
+#define     SERIAL_NUMBER_STRING_ENABLE         0
+    u32     in_timeout;
+#define     GPEP3_TIMEOUT             19
+#define     GPEP2_TIMEOUT             18
+#define     GPEP1_TIMEOUT             17
+#define     GPEP0_TIMEOUT             16
+#define     GPEP3_TIMEOUT_VALUE             13  /* 15:13 */
+#define     GPEP3_TIMEOUT_ENABLE      12
+#define     GPEP2_TIMEOUT_VALUE             9   /* 11:9 */
+#define     GPEP2_TIMEOUT_ENABLE      8
+#define     GPEP1_TIMEOUT_VALUE             5   /* 7:5 */
+#define     GPEP1_TIMEOUT_ENABLE      4
+#define     GPEP0_TIMEOUT_VALUE             1   /* 3:1 */
+#define     GPEP0_TIMEOUT_ENABLE      0
+    u32     isodelay;
+#define     ISOCHRONOUS_DELAY               0
+} __attribute__ ((packed));
+
+/* pci control, BAR0 + 0x0100 */
+struct usb338x_pci_regs
+{
+    /* offset 0x0100 */
+    u32    pcimstctl;
+#define     PCIE_DW_LENGTH             24   /* 30:24 */
+#define     PCIE_PORT                  20
+#define     MESSAGE_TYPE               19
+#define     MESSAGE_ROUTING            16   /* 18:16 */
+#define     MESSAGE_CODE                8   /* 15:8 */
+#define     PCIE_MASTER_READ_WRITE      7
+#define     PCIE_MASTER_START           6
+#define     PCIE_MASTER_COMMAND_SELECT  4   /* 5:4 */
+#define     PCIE_FIRST_BYTE_ENABLES     0   /* 3:0 */
+    u32    pcimstaddr;
+    u32    pcimstdata;
+    u32    pcimststat;
+#define     ROOT_COMPLEX_MODE                   0
+} __attribute__ ((packed));
+
+/* dma control, BAR0 + 0x0180 ... array of four structs like this,
+ * for channels 0..3.  see also struct usb338x_dma:  descriptor
+ * that can be loaded into some of these registers.
+ */
+struct usb338x_dma_regs
+{
+    /* [11.7] */
+    /* offset 0x0180, 0x01a0, 0x01c0, 0x01e0, */
+    u32   dmactl;
+#define     DMA_ABORT_DONE_INTERRUPT_ENABLE                     27
+#define     DMA_PAUSE_DONE_INTERRUPT_ENABLE                     26
+#define     DMA_DESCRIPTOR_DONE_INTERRUPT_ENABLE                25
+#define     PAUSE_MODE                                          23
+#define     PREFETCH_DISABLE                                    22
+#define     DMA_CLEAR_COUNT_ENABLE                              21
+#define     DESCRIPTOR_POLLING_RATE                             19  /* 20:19 */
+#define         POLL_CONTINUOUS                                     0
+#define         POLL_1_USEC                                         1
+#define         POLL_100_USEC                                       2
+#define         POLL_1_MSEC                                         3
+#define     DMA_VALID_BIT_POLLING_ENABLE                        18
+#define     DMA_VALID_BIT_ENABLE                                17
+#define     DMA_DESCRIPTOR_MODE                                 16
+#define     DMA_REQUEST_OUTSTANDING                             5   /* 7:5 */
+#define     DMA_OUT_AUTO_START_ENABLE                           4
+#define     DMA_FIFO_VALIDATE                                   2
+#define     DMA_ENABLE                                          1
+#define     DMA_ADDRESS_CONSTANT                                0
+    u32   dmastat;
+#define     DMA_COMPLETION_SEQUENCE_ERROR_STATUS                31
+#define     DMA_ABORT_DONE_INTERRUPT                            27
+#define     DMA_PAUSE_DONE_INTERRUPT                            26
+#define     DMA_LAST_DESCRIPTOR_DONE_INTERRUPT                  25
+#define     DMA_TRANSACTION_DONE_INTERRUPT                      24
+#define     DMA_ABORT                                           1
+#define     DMA_START                                           0
+    u32   _unused0[2];
+    /* offset 0x0190, 0x01b0, 0x01d0, 0x01f0, */
+    u32   dmacount;
+#define     VALID_BIT                                           31
+#define     DMA_DIRECTION                                       30
+#define     DMA_DONE_INTERRUPT_ENABLE                           29
+#define     LAST_DESCRIPTOR                                     28
+#define     DMA_DESCRIPTOR_FIFO_VALIDATE                        27
+#define     DMA_OUT_CONTINUE                                    24
+#define     DMA_TRANSFER_MAX_LENGTH                             ((1UL<<24)-1)
+#define     DMA_TRANSFER_LENGTH                                 0   /* 23:0 */
+    /* offset 0x194, 0x1b4, 0x1d4, 0x1f4 */
+    u32   dmaaddr;
+    /* offset 0x198, 0x1b8, 0x1d8, 0x1f8 */
+    u32   dmadesc;
+#define     DMA_DESC_ONCHIP                                     0
+    u32   _unused1;
+} __attribute__ ((packed));
+
+#define INTERNAL_DESCRIPTOR_OFFSET                              0x4000
+
+/* dma control Prefetch, BAR0 + 0x0680 */
+struct usb338x_dmap_regs
+{
+    u32     dmacountp;
+    u32     dmaaddrp;
+    u32     dmadescp;
+    u32     _unused1;
+} __attribute__ ((packed));
+
+/* dedicated endpoint registers, BAR0 + 0x0200 */
+
+struct usb338x_dep_regs
+{
+    /* [11.8] */
+    /* offset 0x0200, 0x0210, 0x220, 0x230, 0x240 */
+    u32   dep_cfg;
+    /* offset 0x0204, 0x0214, 0x224, 0x234, 0x244 */
+    u32   dep_rsp;
+    u32   _unused[2];
+} __attribute__ ((packed));
+
+/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
+ * like this, for ep0 then the configurable endpoints 1..8
+ * ep0 reserved for control
+ */
+struct usb338x_ep_cfg
+{
+    /* offset 0x300, 0x320, 0x340, 0x360, 0x380 */
+    u32   ep_cfg;
+#define     USAGE_TYPE                      30  /* Legacy/Enhanced: 31:30 */
+#define     SYNC_TYPE                       28  /* Legacy/Enhanced: 29:28 */
+#define     MAX_BURST_SIZE                  24  /* Legacy/Enhanced: 27:24 */
+#define     SERVICE_INTERVAL                19  /* Legacy/Enhanced: 23:19 */
+#define     EP_FIFO_BYTE_COUNT              16  /* 18:16 */
+#define     IN_EP_FORMAT                    15  /* Legacy/Enhanced */
+#define     IN_ENDPOINT_ENABLE              14  /* Enhanced */
+#define     IN_ENDPOINT_TYPE                12  /* Enhanced: 13:12 */
+#define     BYTE_PACKING_ENABLE             11
+#define     ENDPOINT_ENABLE                 10  /* Legacy/ep0 */
+#define     OUT_ENDPOINT_ENABLE             10  /* Enhanced */
+#define     ENDPOINT_TYPE                    8  /* Legacy/ep0: 9:8 */
+#define     OUT_ENDPOINT_TYPE                8  /* Enhanced: 9:8 */
+#define     ENDPOINT_DIRECTION               7  /* Legacy/ep0 */
+#define     ENDPOINT_NUMBER                  0  /* 3:0 */
+    u32   _unused0[7];
+};
+
+struct usb338x_ep_regs
+{
+    /* [11.9] */
+    /* ep0, ep1-out/in, ep2-out/in, ep3-out/in, ep4-out/in */
+    /* offset 0x304, 0x324/0x3e4, 0x344/0x404, 0x364/0x424, 0x384/0x444 */
+    u32   ep_rsp;
+#define     SET_NAK_OUT_PACKETS                                 15
+#define     SET_EP_HIDE_STATUS_PHASE                            14
+#define     SET_EP_FORCE_CRC_ERROR                              13
+#define     SET_INTERRUPT_MODE                                  12
+#define     SET_CONTROL_STATUS_PHASE_HANDSHAKE                  11
+#define     SET_NAK_OUT_PACKETS_MODE                            10
+#define     SET_ENDPOINT_TOGGLE                                 9
+#define     SET_ENDPOINT_HALT                                   8
+#define     CLEAR_NAK_OUT_PACKETS                               7
+#define     CLEAR_EP_HIDE_STATUS_PHASE                          6
+#define     CLEAR_EP_FORCE_CRC_ERROR                            5
+#define     CLEAR_INTERRUPT_MODE                                4
+#define     CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE                3
+#define     CLEAR_NAK_OUT_PACKETS_MODE                          2
+#define     CLEAR_ENDPOINT_TOGGLE                               1
+#define     CLEAR_ENDPOINT_HALT                                 0
+    u32   ep_irqenb;
+#define     SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE              6
+#define     SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE           5
+#define     DATA_PACKET_RECEIVED_INTERRUPT_ENABLE               3
+#define     DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE            2
+#define     DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE                1
+#define     DATA_IN_TOKEN_INTERRUPT_ENABLE                      0
+    u32   ep_stat;
+#define     FIFO_VALID_COUNT                                    24
+#define     HIGH_BANDWIDTH_OUT_TRANSACTION_PID                  22
+#define     TIMEOUT                                             21
+#define     USB_STALL_SENT                                      20
+#define     USB_IN_NAK_SENT                                     19
+#define     USB_IN_ACK_RCVD                                     18
+#define     USB_OUT_NAK_SENT                                    17
+#define     USB_OUT_ACK_SENT                                    16
+#define     ZLP_INTERRUPT                                       13
+#define     FIFO_FULL                                           11
+#define     FIFO_EMPTY                                          10
+#define     FIFO_FLUSH                                          9
+#define     FIFO_VALIDATE                                       8
+#define     SHORT_PACKET_TRANSFERRED_STATUS                     7
+#define     SHORT_OUT_PACKET_DONE_INTERRUPT                     6
+#define     SHORT_OUT_PACKET_RECEIVED_INTERRUPT                 5
+#define     NAK_PACKETS                                         4
+#define     DATA_PACKET_RECEIVED_INTERRUPT                      3
+#define     DATA_PACKET_TRANSMITTED_INTERRUPT                   2
+#define     DATA_OUT_PING_TOKEN_INTERRUPT                       1
+#define     DATA_IN_TOKEN_INTERRUPT                             0
+    /* offset 0x310, 0x330/0x3f0, 0x350/0x410, 0x370/0x430, 0x390/0x450 */
+    u32   ep_avail;
+    u32   ep_data;
+    u32   ep_data1;
+    u32   ep_val;
+    u32   _unused0;
+} __attribute__ ((packed));
+
+struct usb338x_fifo_regs
+{
+    /* offset 0x0500, 0x0520, 0x0540, 0x0560, 0x0580 */
+    u32     ep_fifo_size_base;
+#define     IN_FIFO_BASE_ADDRESS                                22  /* 30:22 */
+#define     IN_FIFO_SIZE                                        16  /* 18:16 */
+#define     OUT_FIFO_BASE_ADDRESS                               6   /* 14:6 */
+#define     OUT_FIFO_SIZE                                       0   /* 2:0 */
+    u32     ep_fifo_out_wrptr;
+    u32     ep_fifo_out_rdptr;
+    u32     ep_fifo_in_wrptr;
+    u32     ep_fifo_in_rdptr;
+    u32     unused[3];
+} __attribute__ ((packed));
+
+// Link layer -----------------------------------------------------------------
+struct usb338x_ll_regs
+{
+    /* offset 0x700 */
+    u32   ll_ltssm_ctrl1;
+    u32   ll_ltssm_ctrl2;
+    u32   ll_ltssm_ctrl3;
+    u32   unused[2];
+    /* offset 0x714 */
+    u32   ll_general_ctrl0;
+    u32   ll_general_ctrl1;
+#define     PM_U3_AUTO_EXIT                                     29
+#define     PM_U2_AUTO_EXIT                                     28
+#define     PM_U1_AUTO_EXIT                                     27
+#define     PM_FORCE_U2_ENTRY                                   26
+#define     PM_FORCE_U1_ENTRY                                   25
+#define     PM_LGO_COLLISION_SEND_LAU                           24
+#define     PM_DIR_LINK_REJECT                                  23
+#define     PM_FORCE_LINK_ACCEPT                                22
+#define     PM_DIR_ENTRY_U3                                     20
+#define     PM_DIR_ENTRY_U2                                     19
+#define     PM_DIR_ENTRY_U1                                     18
+#define     PM_U2_ENABLE                                        17
+#define     PM_U1_ENABLE                                        16
+#define     SKP_THRESHOLD_ADJUST_FMW                            8   /* 13:8 */
+#define     RESEND_DPP_ON_LRTY_FMW                              7
+#define     DL_BIT_VALUE_FMW                                    6
+#define     FORCE_DL_BIT                                        5
+    u32   ll_general_ctrl2;
+#define     SELECT_INVERT_LANE_POLARITY                         7
+#define     FORCE_INVERT_LANE_POLARITY                          6
+    u32   ll_general_ctrl3;
+    u32   ll_general_ctrl4;
+    u32   ll_error_gen;
+} __attribute__ ((packed));
+
+struct usb338x_ll_lfps_regs
+{
+    /* offset 0x748 */
+    u32   ll_lfps_5;
+#define     TIMER_LFPS_6US                                      16  /* 19:16 */
+    u32   ll_lfps_6; // 0x74c
+#define     TIMER_LFPS_80US                                     0   /* 15:0 */
+} __attribute__ ((packed));
+
+struct usb338x_ll_tsn_regs
+{
+    /* offset 0x77C */
+    u32   ll_tsn_counters_2;
+#define     HOT_TX_NORESET_TS2                                  24  /* 28:24 */
+    u32   ll_tsn_counters_3;
+#define     HOT_RX_RESET_TS2                                    0   /* 4:0 */
+} __attribute__ ((packed));
+
+struct usb338x_ll_chi_regs
+{
+    /* offset 0x79C */
+    u32   ll_tsn_chicken_bit;
+#define     RECOVERY_IDLE_TO_RECOVER_FMW                        3   /* 28:24 */
+} __attribute__ ((packed));
+
+// protocol layer -------------------------------------------------------------
+
+struct usb338x_pl_regs
+{
+    /* offset 0x800 */
+    u32   pl_reg_1;
+    u32   pl_reg_2;
+    u32   pl_reg_3;
+    u32   pl_reg_4;
+    u32   pl_ep_ctrl;     //0x810
+    // Protocol Layer Endpoint Control
+#define     PL_EP_CTRL                                  0x810
+#define     ENDPOINT_SELECT                             0      // [4:0]
+#define     EP_INITIALIZED                              16
+#define     SEQUENCE_NUMBER_RESET                       17
+#define     CLEAR_ACK_ERROR_CODE                        20
+    u32   pl_reg_6;
+    u32   pl_reg_7;
+    u32   pl_reg_8;
+    u32   pl_ep_status_1; //0x820
+    // Protocol Layer Endpoint Status 1
+#define     PL_EP_STATUS_1                              0x820
+#define     STATE                                       16     // [23:16] Tip: Subdivided. See spec
+#define     ACK_GOOD_NORMAL                             0x11   // for bits [20:16]
+#define     ACK_GOOD_MORE_ACKS_TO_COME                  0x16   // for bits [20:16]
+    u32   pl_ep_status_2;
+    u32   pl_ep_status_3; //0x828
+    // Protocol Layer Endpoint Status 3
+#define     PL_EP_STATUS_3                              0x828
+#define     SEQUENCE_NUMBER                             0      // [7:0]
+    u32   pl_ep_status_4;
+    // Protocol Layer Endpoint Status 4
+#define     PL_EP_STATUS_4                              0x82c
+    u32   pl_ep_cfg_4;
+    // Protocol Layer Endpoint Configuration 4
+#define     PL_EP_CFG_4                                 0x830
+#define     NON_CTRL_IN_TOLERATE_BAD_DIR                6
+} __attribute__ ((packed));
+
+struct usb338x_factory_regs
+{
+    /* 0xB80 */
+    u32   factory_test_only;
+} __attribute__ ((packed));
+
+struct usb338x_phy_regs
+{
+    /* 0x0xBE0 */
+    u32   phy_threshold;
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+#define N_EPS_LEGCY    4 /* 4 endpoints in legacy mode */
+#define N_EPS_ENHANCE  8 /* 8 endpoints in enhanced mode */
+#define N_DMA_CHANNEL  4 /* Number of DMA channel */
+
+#define REG_DIAG      0x0
+#define RETRY_COUNTER                                       16
+#define FORCE_PCI_SERR                                      11
+#define FORCE_PCI_INTERRUPT                                 10
+#define FORCE_USB_INTERRUPT                                 9
+#define FORCE_CPU_INTERRUPT                                 8
+#define ILLEGAL_BYTE_ENABLES                                5
+#define FAST_TIMES                                          4
+#define FORCE_RECEIVE_ERROR                                 2
+#define FORCE_TRANSMIT_CRC_ERROR                            0
+#define REG_FRAME     0x02    /* from last sof */
+#define REG_CHIPREV   0x03    /* in bcd */
+#define REG_HS_NAK_RATE   0x0a  /* NAK per N uframes */
+
+#define CHIPREV_1   0x0100
+#define CHIPREV_1A  0x0110
+
+/* ep 1-8 highspeed and fullspeed maxpacket, addresses
+ * computed from ep->num
+ */
+#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \
+                                (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1))
+
+/* DEFECT 7374 */
+#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS         200
+#define DEFECT_7374_PROCESSOR_WAIT_TIME             10
+
+/* ep0 max packet size */
+#define EP0_SS_MAX_PACKET_SIZE  0x200   /* 512 */
+#define EP0_HS_MAX_PACKET_SIZE  0x40    /* 64 */
+/*-------------------------------------------------------------------------*/
+
+/* [8.3] for scatter/gather i/o
+ * use struct usb338x_dma_regs bitfields
+ */
+struct usb338x_dma
+{
+    __le32      dmacount;
+    __le32      dmaaddr;        /* the buffer */
+    __le32      dmadesc;        /* next dma descriptor */
+    __le32      _reserved;
+} __attribute__ ((aligned (16)));
+
+/*-------------------------------------------------------------------------*/
+
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct usb338x_ep
+{
+    struct usb_ep               ep;
+    struct usb338x_ep_cfg       __iomem *cfg;
+    struct usb338x_ep_regs      __iomem *regs;
+    struct usb338x_dma_regs     __iomem *dma;
+    struct usb338x_dma          *dummy;
+    struct usb338x_fifo_regs    __iomem *fiforegs;
+    dma_addr_t                  td_dma;     /* of dummy */
+    struct usb338x              *dev;
+    unsigned long               irqs;
+    unsigned                    is_halt : 1,
+                                dma_started : 1;
+
+    /* analogous to a host-side qh */
+    struct list_head            queue;
+    const struct usb_endpoint_descriptor    *desc;
+    unsigned                num : 8,
+                            fifo_size : 12,
+                            in_fifo_validate : 1,
+                            out_overflow : 1,
+                            stopped : 1,
+                            wedged : 1,
+                            is_in : 1,
+                            is_iso : 1,
+                            responded : 1;
+};
+
+
+struct usb338x_request
+{
+    struct usb_request  req;
+    struct usb338x_dma  *td;
+    dma_addr_t          td_dma;
+    struct list_head    queue;
+    unsigned            mapped : 1,
+                        valid : 1;
+};
+
+
+struct usb338x
+{
+    /* each pci device provides one gadget, several endpoints */
+    struct usb_gadget   gadget;
+    spinlock_t          lock;
+    struct usb338x_ep   ep [9];
+    struct usb_gadget_driver  *driver;
+    unsigned            enabled : 1,
+                        protocol_stall : 1,
+                        softconnect : 1,
+                        got_irq : 1,
+                        region : 1,
+                        u1_enable: 1,
+                        u2_enable: 1,
+                        ltm_enable: 1,
+                        wakeup_enable: 1,
+                        selfpowered: 1,
+                        addressed_state: 1;
+
+    u16                 chiprev;
+
+    /* pci state used to access those endpoints */
+    struct pci_dev              *pdev;
+    struct usb338x_regs         __iomem *regs;
+    struct usb338x_usb_regs     __iomem *usb;
+    struct usb338x_pci_regs     __iomem *pci;
+    struct usb338x_dma_regs     __iomem *dma;
+    struct usb338x_dep_regs     __iomem *dep;
+    struct usb338x_ep_cfg       __iomem *epcfg;
+    struct usb338x_ep_regs      __iomem *epregs;
+    struct usb338x_fifo_regs    __iomem *fiforegs;
+    struct usb338x_ll_regs      __iomem *llregs;
+    struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
+    struct usb338x_ll_tsn_regs  __iomem *ll_tsn_regs;
+    struct usb338x_ll_chi_regs  __iomem *ll_chicken_reg;
+    struct usb338x_pl_regs      __iomem *plregs;
+    struct usb338x_factory_regs __iomem *factory_regs;
+    struct usb338x_phy_regs     __iomem *phy_regs;
+    struct pci_pool             *requests;
+    // statistics...
+};
+
+/*-------------------------------------------------------------------------*/
+// DEFECT7374_FSM
+/* FSM value for Defect 7374 (U1U2 Test) is managed in chip's SCRATCH register:
+ * Reg/Bit/Val      Reg /// Bit /// Val ////
+ */
+#define DEFECT7374_FSM_FIELD    28  //31:28
+
+typedef enum _DEFECT7374_FSM
+{
+    /* A finite state machine for Defect 7374 workaround is managed
+     * in the chip's SCRATCH register:
+     *  - The nature of the Defect 7374 is that it must be executed only
+     *    once, after the chip powers up.
+     *  - Using a chip register allows the FSM state to persist even when
+     *    the driver unloads and reloads.
+     *  - Using a chip register allows the FSM state to automatically
+     *    reset when the system recovers from hibernation.
+     */
+
+    /* The FSM's reset state is the chip register's power-on value:
+     *  - The FSM state is managed in SCRATCH[31:28]
+     *  - The chip's SCRATCH register power-on value is 0xFEEDFACE
+     */
+    DEFECT7374_FSM_RESET = (0xFEEDFACE & (0xf << 28)),
+
+    // Undefined state applies to errors, debug and development.
+    DEFECT7374_FSM_UNDEFINED = (0 << DEFECT7374_FSM_FIELD),
+
+    /* Waiting for Control Read:
+     *  - A transition to this state indicates a fresh USB connection,
+     *    before the first Setup Packet. The connection speed is not
+     *    known. Firmware is waiting for the first Control Read.
+     *  - Starting state: This state can be thought of as the FSM's typical
+     *    starting state.
+     *  - Tip: Upon the first SS Control Read the FSM never
+     *    returns to this state.
+     */
+    DEFECT7374_FSM_WAITING_FOR_CONTROL_READ = (1 << DEFECT7374_FSM_FIELD),
+
+    /* Non-SS Control Read:
+     *  - A transition to this state indicates detection of the first HS
+     *    or FS Control Read.
+     *  - Tip: Upon the first SS Control Read the FSM never
+     *    returns to this state.
+     */
+    DEFECT7374_FSM_NON_SS_CONTROL_READ = (2 << DEFECT7374_FSM_FIELD),
+
+    /* SS Control Read:
+     *  - A transition to this state indicates detection of the
+     *    first SS Control Read.
+     *  - This state indicates workaround completion. Workarounds no longer
+     *    need to be applied (as long as the chip remains powered up).
+     *  - Tip: Once in this state the FSM state does not change (until
+     *    the chip's power is lost and restored).
+     *  - This can be thought of as the final state of the FSM;
+     *    the FSM 'locks-up' in this state until the chip loses power.
+     */
+    DEFECT7374_FSM_SS_CONTROL_READ = (3 << DEFECT7374_FSM_FIELD),
+} DEFECT7374_FSM, * PDEFECT7374_FSM;
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(dev,level,fmt,args...) \
+    printk(level "     DEBUG: %s %s: " fmt , driver_name , \
+           pci_name(dev->pdev) , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+    xprintk(dev , KERN_INFO , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+    do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+    do { } while (0)
+#endif  /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+    xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARNING(dev,fmt,args...) \
+    xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+    xprintk(dev , KERN_INFO , fmt , ## args)
+
+#endif
+/*-------------------------------------------------------------------------*/
\ No newline at end of file
-- 
2.0.0.rc0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux