[PATCH] Add Intel Langwell USB Device Controller driver

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

 



>From 9a1401ff4fa21ffa80ba64d2b6d4b382e64f496f Mon Sep 17 00:00:00 2001
From: Xiaochen Shen <xiaochen.shen@xxxxxxxxx>
Date: Sat, 16 May 2009 07:12:04 +0800
Subject: [PATCH] Add Intel Langwell USB Device Controller driver

Intel Langwell USB Device Controller is a High-Speed USB OTG device
controller in Intel Moorestown platform. It can work in OTG device mode
with Intel Langwell USB OTG transceiver driver as well as device-only
mode. The number of programmable endpoints is different through
controller revision.

NOTE:
This patch is the first version Intel Langwell USB OTG device
controller driver. The bug fixing is on going for some hardware and
software issues.
Intel Langwell USB OTG transceiver driver and EHCI driver patches will
be submitted later.

Supported features:
 - USB OTG protocol support with Intel Langwell USB OTG transceiver
   driver (turn on CONFIG_USB_LANGWELL_OTG)
 - Support control, bulk, interrupt and isochronous endpoints
   (isochronous not tested)
 - PCI D0/D3 power management support
 - Link Power Management (LPM) support

Tested gadget drivers:
 - g_file_storage
 - g_ether
 - g_zero

The passed tests:
 - g_file_storage: USBCV Chapter 9 tests
 - g_file_storage: USBCV MSC tests
 - g_file_storage: from/to host files copying
 - g_ether: ping, ftp and scp files from/to host
 - Hotplug, with and without hubs

Known issues:
 - g_ether: failed part of USBCV chap9 tests
 - LPM support not fully tested

TODO:
 - g_ether: pass all USBCV chap9 tests
 - g_zero: pass usbtest tests
 - Stress tests on different gadget drivers
 - On-chip private SRAM caching support

Signed-off-by: Xiaochen Shen <xiaochen.shen@xxxxxxxxx>
---
 drivers/usb/gadget/Kconfig        |   21 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/langwell_udc.c | 3373 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/langwell_udc.h |  228 +++
 include/linux/usb/langwell_udc.h  |  310 ++++
 6 files changed, 3941 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/langwell_udc.c
 create mode 100644 drivers/usb/gadget/langwell_udc.h
 create mode 100644 include/linux/usb/langwell_udc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 080bb1e..db40b96 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -460,6 +460,27 @@ config USB_GOKU
        default USB_GADGET
        select USB_GADGET_SELECTED

+config USB_GADGET_LANGWELL
+       boolean "Intel Langwell USB Device Controller"
+       depends on PCI
+       select USB_GADGET_DUALSPEED
+       help
+          Intel Langwell USB Device Controller is a High-Speed USB
+          On-The-Go device controller.
+
+          The number of programmable endpoints is different through
+          controller revision.
+
+          Say "y" to link the driver statically, or "m" to build a
+          dynamically linked module called "langwell_udc" and force all
+          gadget drivers to also be dynamically linked.
+
+config USB_LANGWELL
+       tristate
+       depends on USB_GADGET_LANGWELL
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+

 #
 # LAST -- dummy/emulated controller
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 39a51d7..5ff5ead 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_USB_FSL_USB2)    += fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 obj-$(CONFIG_USB_FSL_QE)       += fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)      += ci13xxx_udc.o
+obj-$(CONFIG_USB_LANGWELL)      += langwell_udc.o

 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index ec6d439..8e0e9a0 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -137,6 +137,12 @@
 #define gadget_is_musbhdrc(g)  0
 #endif

+#ifdef CONFIG_USB_GADGET_LANGWELL
+#define gadget_is_langwell(g)  (!strcmp("langwell_udc", (g)->name))
+#else
+#define gadget_is_langwell(g)  0
+#endif
+
 /* from Montavista kernel (?) */
 #ifdef CONFIG_USB_GADGET_MPC8272
 #define gadget_is_mpc8272(g)   !strcmp("mpc8272_udc", (g)->name)
@@ -231,6 +237,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
                return 0x22;
        else if (gadget_is_ci13xxx(gadget))
                return 0x23;
+       else if (gadget_is_langwell(gadget))
+               return 0x24;
        return -ENOENT;
 }

diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
new file mode 100644
index 0000000..6829d59
--- /dev/null
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -0,0 +1,3373 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+/* #undef      DEBUG */
+/* #undef      VERBOSE */
+
+#if defined(CONFIG_USB_LANGWELL_OTG)
+#define        OTG_TRANSCEIVER
+#endif
+
+
+#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/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "langwell_udc.h"
+
+
+#define        DRIVER_DESC             "Intel Langwell USB Device Controller driver"
+#define        DRIVER_VERSION          "16 May 2009"
+
+static const char driver_name[] = "langwell_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+
+/* controller device global variable */
+static struct langwell_udc     *the_controller;
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor
+langwell_ep0_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     0,
+       .bmAttributes =         USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize =       EP0_MAX_PKT_SIZE,
+};
+
+
+/*-------------------------------------------------------------------------*/
+/* debugging */
+
+#ifdef DEBUG
+#define        DBG(dev, fmt, args...) \
+       pr_debug("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+#else
+#define        DBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+
+#ifdef VERBOSE
+#define        VDBG DBG
+#else
+#define        VDBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* VERBOSE */
+
+
+#define        ERROR(dev, fmt, args...) \
+       pr_err("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+#define        WARNING(dev, fmt, args...) \
+       pr_warning("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+#define        INFO(dev, fmt, args...) \
+       pr_info("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+
+#ifdef VERBOSE
+static inline void print_all_registers(struct langwell_udc *dev)
+{
+       int     i;
+
+       /* Capability Registers */
+       printk(KERN_DEBUG "Capability Registers (offset: "
+                       "0x%04x, length: 0x%08x)\n",
+                       CAP_REG_OFFSET,
+                       (u32)sizeof(struct langwell_cap_regs));
+       printk(KERN_DEBUG "caplength=0x%02x\n",
+                       readb(&dev->cap_regs->caplength));
+       printk(KERN_DEBUG "hciversion=0x%04x\n",
+                       readw(&dev->cap_regs->hciversion));
+       printk(KERN_DEBUG "hcsparams=0x%08x\n",
+                       readl(&dev->cap_regs->hcsparams));
+       printk(KERN_DEBUG "hccparams=0x%08x\n",
+                       readl(&dev->cap_regs->hccparams));
+       printk(KERN_DEBUG "dciversion=0x%04x\n",
+                       readw(&dev->cap_regs->dciversion));
+       printk(KERN_DEBUG "dccparams=0x%08x\n",
+                       readl(&dev->cap_regs->dccparams));
+
+       /* Operational Registers */
+       printk(KERN_DEBUG "Operational Registers (offset: "
+                       "0x%04x, length: 0x%08x)\n",
+                       OP_REG_OFFSET,
+                       (u32)sizeof(struct langwell_op_regs));
+       printk(KERN_DEBUG "extsts=0x%08x\n",
+                       readl(&dev->op_regs->extsts));
+       printk(KERN_DEBUG "extintr=0x%08x\n",
+                       readl(&dev->op_regs->extintr));
+       printk(KERN_DEBUG "usbcmd=0x%08x\n",
+                       readl(&dev->op_regs->usbcmd));
+       printk(KERN_DEBUG "usbsts=0x%08x\n",
+                       readl(&dev->op_regs->usbsts));
+       printk(KERN_DEBUG "usbintr=0x%08x\n",
+                       readl(&dev->op_regs->usbintr));
+       printk(KERN_DEBUG "frindex=0x%08x\n",
+                       readl(&dev->op_regs->frindex));
+       printk(KERN_DEBUG "ctrldssegment=0x%08x\n",
+                       readl(&dev->op_regs->ctrldssegment));
+       printk(KERN_DEBUG "deviceaddr=0x%08x\n",
+                       readl(&dev->op_regs->deviceaddr));
+       printk(KERN_DEBUG "endpointlistaddr=0x%08x\n",
+                       readl(&dev->op_regs->endpointlistaddr));
+       printk(KERN_DEBUG "ttctrl=0x%08x\n",
+                       readl(&dev->op_regs->ttctrl));
+       printk(KERN_DEBUG "burstsize=0x%08x\n",
+                       readl(&dev->op_regs->burstsize));
+       printk(KERN_DEBUG "txfilltuning=0x%08x\n",
+                       readl(&dev->op_regs->txfilltuning));
+       printk(KERN_DEBUG "txttfilltuning=0x%08x\n",
+                       readl(&dev->op_regs->txttfilltuning));
+       printk(KERN_DEBUG "ic_usb=0x%08x\n",
+                       readl(&dev->op_regs->ic_usb));
+       printk(KERN_DEBUG "ulpi_viewport=0x%08x\n",
+                       readl(&dev->op_regs->ulpi_viewport));
+       printk(KERN_DEBUG "configflag=0x%08x\n",
+                       readl(&dev->op_regs->configflag));
+       printk(KERN_DEBUG "portsc1=0x%08x\n",
+                       readl(&dev->op_regs->portsc1));
+       printk(KERN_DEBUG "devlc=0x%08x\n",
+                       readl(&dev->op_regs->devlc));
+       printk(KERN_DEBUG "otgsc=0x%08x\n",
+                       readl(&dev->op_regs->otgsc));
+       printk(KERN_DEBUG "usbmode=0x%08x\n",
+                       readl(&dev->op_regs->usbmode));
+       printk(KERN_DEBUG "endptnak=0x%08x\n",
+                       readl(&dev->op_regs->endptnak));
+       printk(KERN_DEBUG "endptnaken=0x%08x\n",
+                       readl(&dev->op_regs->endptnaken));
+       printk(KERN_DEBUG "endptsetupstat=0x%08x\n",
+                       readl(&dev->op_regs->endptsetupstat));
+       printk(KERN_DEBUG "endptprime=0x%08x\n",
+                       readl(&dev->op_regs->endptprime));
+       printk(KERN_DEBUG "endptflush=0x%08x\n",
+                       readl(&dev->op_regs->endptflush));
+       printk(KERN_DEBUG "endptstat=0x%08x\n",
+                       readl(&dev->op_regs->endptstat));
+       printk(KERN_DEBUG "endptcomplete=0x%08x\n",
+                       readl(&dev->op_regs->endptcomplete));
+
+       for (i = 0; i < dev->ep_max / 2; i++) {
+               printk(KERN_DEBUG "endptctrl[%d]=0x%08x\n",
+                               i, readl(&dev->op_regs->endptctrl[i]));
+       }
+}
+#endif /* VERBOSE */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define        DIR_STRING(bAddress)    (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#define is_in(ep)      (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \
+                       USB_DIR_IN) : ((ep)->desc->bEndpointAddress \
+                       & USB_DIR_IN) == USB_DIR_IN)
+
+
+#ifdef 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 "int";
+       };
+
+       return "control";
+}
+#endif
+
+
+/* configure endpoint control registers */
+static void ep_reset(struct langwell_ep *ep, unsigned char ep_num,
+               unsigned char is_in, unsigned char ep_type)
+{
+       struct langwell_udc     *dev;
+       u32                     endptctrl;
+
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in) {    /* TX */
+               if (ep_num)
+                       endptctrl |= EPCTRL_TXR;
+               endptctrl |= EPCTRL_TXE;
+               endptctrl |= ep_type << EPCTRL_TXT_SHIFT;
+       } else {        /* RX */
+               if (ep_num)
+                       endptctrl |= EPCTRL_RXR;
+               endptctrl |= EPCTRL_RXE;
+               endptctrl |= ep_type << EPCTRL_RXT_SHIFT;
+       }
+
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* reset ep0 dQH and endptctrl */
+static void ep0_reset(struct langwell_udc *dev)
+{
+       struct langwell_ep      *ep;
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 in and out */
+       for (i = 0; i < 2; i++) {
+               ep = &dev->ep[i];
+               ep->dev = dev;
+
+               /* ep0 dQH */
+               ep->dqh = &dev->ep_dqh[i];
+
+               /* configure ep0 endpoint capabilities in dQH */
+               ep->dqh->dqh_ios = 1;
+               ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE;
+
+               /* FIXME: enable ep0-in HW zero length termination select */
+               if (is_in(ep))
+                       ep->dqh->dqh_zlt = 0;
+               ep->dqh->dqh_mult = 0;
+
+               /* configure ep0 control registers */
+               ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL);
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoints operations */
+
+/* configure endpoint, making it usable */
+static int langwell_ep_enable(struct usb_ep *_ep,
+               const struct usb_endpoint_descriptor *desc)
+{
+       struct langwell_udc     *dev;
+       struct langwell_ep      *ep;
+       u16                     max = 0;
+       unsigned long           flags;
+       int                     retval = 0;
+       unsigned char           zlt, ios = 0, mult = 0;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !desc || ep->desc
+                       || desc->bDescriptorType != USB_DT_ENDPOINT)
+               return -EINVAL;
+
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       max = le16_to_cpu(desc->wMaxPacketSize);
+
+       /*
+        * disable HW zero length termination select
+        * driver handles zero length packet through req->req.zero
+        */
+       zlt = 1;
+
+       /*
+        * sanity check type, direction, address, and then
+        * initialize the endpoint capabilities fields in dQH
+        */
+       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_CONTROL:
+               ios = 1;
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               if ((dev->gadget.speed == USB_SPEED_HIGH
+                                       && max != 512)
+                               || (dev->gadget.speed == USB_SPEED_FULL
+                                       && max > 64)) {
+                       goto done;
+               }
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
+                       goto done;
+
+               switch (dev->gadget.speed) {
+               case USB_SPEED_HIGH:
+                       if (max <= 1024)
+                               break;
+               case USB_SPEED_FULL:
+                       if (max <= 64)
+                               break;
+               default:
+                       if (max <= 8)
+                               break;
+                       goto done;
+               }
+               break;
+       case USB_ENDPOINT_XFER_ISOC:
+               if (strstr(ep->ep.name, "-bulk")
+                               || strstr(ep->ep.name, "-int"))
+                       goto done;
+
+               switch (dev->gadget.speed) {
+               case USB_SPEED_HIGH:
+                       if (max <= 1024)
+                               break;
+               case USB_SPEED_FULL:
+                       if (max <= 1023)
+                               break;
+               default:
+                       goto done;
+               }
+               /*
+                * FIXME:
+                * calculate transactions needed for high bandwidth iso
+                */
+               mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+               max = max & 0x8ff;      /* bit 0~10 */
+               /* 3 transactions at most */
+               if (mult > 3)
+                       goto done;
+               break;
+       default:
+               goto done;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* configure endpoint capabilities in dQH */
+       ep->dqh->dqh_ios = ios;
+       ep->dqh->dqh_mpl = cpu_to_le16(max);
+       ep->dqh->dqh_zlt = zlt;
+       ep->dqh->dqh_mult = mult;
+
+       ep->ep.maxpacket = max;
+       ep->desc = desc;
+       ep->stopped = 0;
+       ep->ep_num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+       /* ep_type */
+       ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+       /* configure endpoint control registers */
+       ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type);
+
+       DBG(dev, "enabled %s (ep%d%s-%s), max %04x\n",
+                       _ep->name,
+                       ep->ep_num,
+                       DIR_STRING(desc->bEndpointAddress),
+                       type_string(desc->bmAttributes),
+                       max);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* retire a request */
+static void done(struct langwell_ep *ep, struct langwell_request *req,
+               int status)
+{
+       struct langwell_udc     *dev = ep->dev;
+       unsigned                stopped = ep->stopped;
+       struct langwell_dtd     *curr_dtd, *next_dtd;
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* remove the req from ep->queue */
+       list_del_init(&req->queue);
+
+       if (req->req.status == -EINPROGRESS)
+               req->req.status = status;
+       else
+               status = req->req.status;
+
+       /* free dTD for the request */
+       next_dtd = req->head;
+       for (i = 0; i < req->dtd_count; i++) {
+               curr_dtd = next_dtd;
+               if (i != req->dtd_count - 1)
+                       next_dtd = curr_dtd->next_dtd_virt;
+               dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma);
+       }
+
+       if (req->mapped) {
+               dma_unmap_single(&dev->pdev->dev, req->req.dma, req->req.length,
+                       is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+               req->req.dma = DMA_ADDR_INVALID;
+               req->mapped = 0;
+       } else
+               dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma,
+                               req->req.length,
+                               is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+       if (status != -ESHUTDOWN)
+               DBG(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);
+       /* complete routine from gadget driver */
+       if (req->req.complete)
+               req->req.complete(&ep->ep, &req->req);
+
+       spin_lock(&dev->lock);
+       ep->stopped = stopped;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+static void langwell_ep_fifo_flush(struct usb_ep *_ep);
+
+/* delete all endpoint requests, called with spinlock held */
+static void nuke(struct langwell_ep *ep, int status)
+{
+       /* called with spinlock held */
+       ep->stopped = 1;
+
+       /* endpoint fifo flush */
+       if (&ep->ep && ep->desc)
+               langwell_ep_fifo_flush(&ep->ep);
+
+       while (!list_empty(&ep->queue)) {
+               struct langwell_request *req = NULL;
+               req = list_entry(ep->queue.next, struct langwell_request,
+                               queue);
+               done(ep, req, status);
+       }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint is no longer usable */
+static int langwell_ep_disable(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       unsigned long           flags;
+       struct langwell_udc     *dev;
+       int                     ep_num;
+       u32                     endptctrl;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* disable endpoint control register */
+       ep_num = ep->ep_num;
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl &= ~EPCTRL_TXE;
+       else
+               endptctrl &= ~EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       /* nuke all pending requests (does flush) */
+       nuke(ep, -ESHUTDOWN);
+
+       ep->desc = NULL;
+       ep->stopped = 1;
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       DBG(dev, "disabled %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+
+       return 0;
+}
+
+
+/* allocate a request object to use with this endpoint */
+static struct usb_request *langwell_alloc_request(struct usb_ep *_ep,
+               gfp_t gfp_flags)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req = NULL;
+
+       if (!_ep)
+               return NULL;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       req = kzalloc(sizeof(*req), gfp_flags);
+       if (!req)
+               return NULL;
+
+       req->req.dma = DMA_ADDR_INVALID;
+       INIT_LIST_HEAD(&req->queue);
+
+       VDBG(dev, "alloc request for %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return &req->req;
+}
+
+
+/* free a request object */
+static void langwell_free_request(struct usb_ep *_ep,
+               struct usb_request *_req)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req = NULL;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !_req)
+               return;
+
+       req = container_of(_req, struct langwell_request, req);
+       WARN_ON(!list_empty(&req->queue));
+
+       if (_req)
+               kfree(req);
+
+       VDBG(dev, "free request for %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* queue dTD and PRIME endpoint */
+static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req)
+{
+       u32                     bit_mask, usbcmd, endptstat, dtd_dma;
+       u8                      dtd_status;
+       int                     i;
+       struct langwell_dqh     *dqh;
+       struct langwell_udc     *dev;
+
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       i = ep->ep_num * 2 + is_in(ep);
+       dqh = &dev->ep_dqh[i];
+
+       if (ep->ep_num)
+               VDBG(dev, "%s\n", ep->name);
+       else
+               /* ep0 */
+               VDBG(dev, "%s-%s\n", ep->name, is_in(ep) ? "in" : "out");
+
+       VDBG(dev, "ep_dqh[%d] addr: 0x%08x\n", i, (u32)&(dev->ep_dqh[i]));
+
+       bit_mask = is_in(ep) ?
+               (1 << (ep->ep_num + 16)) : (1 << (ep->ep_num));
+
+       VDBG(dev, "bit_mask = 0x%08x\n", bit_mask);
+
+       /* check if the pipe is empty */
+       if (!(list_empty(&ep->queue))) {
+               /* add dTD to the end of linked list */
+               struct langwell_request *lastreq;
+               lastreq = list_entry(ep->queue.prev,
+                               struct langwell_request, queue);
+
+               lastreq->tail->dtd_next =
+                       cpu_to_le32(req->head->dtd_dma & DTD_NEXT_MASK);
+
+               /* read prime bit, if 1 goto out */
+               if (readl(&dev->op_regs->endptprime) & bit_mask)
+                       goto out;
+
+               do {
+                       /* set ATDTW bit in USBCMD */
+                       usbcmd = readl(&dev->op_regs->usbcmd);
+                       writel(usbcmd | CMD_ATDTW, &dev->op_regs->usbcmd);
+
+                       /* read correct status bit */
+                       endptstat = readl(&dev->op_regs->endptstat) & bit_mask;
+
+               } while (!(readl(&dev->op_regs->usbcmd) & CMD_ATDTW));
+
+               /* write ATDTW bit to 0 */
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               writel(usbcmd & ~CMD_ATDTW, &dev->op_regs->usbcmd);
+
+               if (endptstat)
+                       goto out;
+       }
+
+       /* write dQH next pointer and terminate bit to 0 */
+       dtd_dma = req->head->dtd_dma & DTD_NEXT_MASK;
+       dqh->dtd_next = cpu_to_le32(dtd_dma);
+
+       /* clear active and halt bit */
+       dtd_status = (u8) ~(DTD_STS_ACTIVE | DTD_STS_HALTED);
+       dqh->dtd_status &= dtd_status;
+       VDBG(dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status);
+
+       /* write 1 to endptprime register to PRIME endpoint */
+       bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num);
+       VDBG(dev, "endprime bit_mask = 0x%08x\n", bit_mask);
+       writel(bit_mask, &dev->op_regs->endptprime);
+out:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* fill in the dTD structure to build a transfer descriptor */
+static struct langwell_dtd *build_dtd(struct langwell_request *req,
+               unsigned *length, dma_addr_t *dma, int *is_last)
+{
+       u32                      buf_ptr;
+       struct langwell_dtd     *dtd;
+       struct langwell_udc     *dev;
+       int                     i;
+
+       dev = req->ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* the maximum transfer length, up to 16k bytes */
+       *length = min(req->req.length - req->req.actual,
+                       (unsigned)DTD_MAX_TRANSFER_LENGTH);
+
+       /* create dTD dma_pool resource */
+       dtd = dma_pool_alloc(dev->dtd_pool, GFP_KERNEL, dma);
+       if (dtd == NULL)
+               return dtd;
+       dtd->dtd_dma = *dma;
+
+       /* initialize buffer page pointers */
+       buf_ptr = (u32)(req->req.dma + req->req.actual);
+       for (i = 0; i < 5; i++)
+               dtd->dtd_buf[i] = cpu_to_le32(buf_ptr + i * PAGE_SIZE);
+
+       req->req.actual += *length;
+
+       /* fill in total bytes with transfer size */
+       dtd->dtd_total = cpu_to_le16(*length);
+       VDBG(dev, "dtd->dtd_total = %d\n", dtd->dtd_total);
+
+       /* set is_last flag if req->req.zero is set or not */
+       if (req->req.zero) {
+               if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+                       *is_last = 1;
+               else
+                       *is_last = 0;
+       } else if (req->req.length == req->req.actual) {
+               *is_last = 1;
+       } else
+               *is_last = 0;
+
+       if (*is_last == 0)
+               VDBG(dev, "multi-dtd request!\n");
+
+       /* set interrupt on complete bit for the last dTD */
+       if (*is_last && !req->req.no_interrupt)
+               dtd->dtd_ioc = 1;
+
+       /* set multiplier override 0 for non-ISO and non-TX endpoint */
+       dtd->dtd_multo = 0;
+
+       /* set the active bit of status field to 1 */
+       dtd->dtd_status = DTD_STS_ACTIVE;
+       VDBG(dev, "dtd->dtd_status = 0x%02x\n", dtd->dtd_status);
+
+       VDBG(dev, "length = %d, dma addr= 0x%08x\n", *length, (int)*dma);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return dtd;
+}
+
+
+/* generate dTD linked list for a request */
+static int req_to_dtd(struct langwell_request *req)
+{
+       unsigned                count;
+       int                     is_last, is_first = 1;
+       struct langwell_dtd     *dtd, *last_dtd = NULL;
+       struct langwell_udc     *dev;
+       dma_addr_t              dma;
+
+       dev = req->ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+       do {
+               dtd = build_dtd(req, &count, &dma, &is_last);
+               if (dtd == NULL)
+                       return -ENOMEM;
+
+               if (is_first) {
+                       is_first = 0;
+                       req->head = dtd;
+               } else {
+                       last_dtd->dtd_next = cpu_to_le32(dma);
+                       last_dtd->next_dtd_virt = dtd;
+               }
+               last_dtd = dtd;
+               req->dtd_count++;
+       } while (!is_last);
+
+       /* set terminate bit to 1 for the last dTD */
+       dtd->dtd_next = DTD_TERM;
+
+       req->tail = dtd;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* queue (submits) an I/O requests to an endpoint */
+static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+               gfp_t gfp_flags)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       int                     is_iso = 0, zlflag = 0;
+
+       /* always require a cpu-view buffer */
+       req = container_of(_req, struct langwell_request, req);
+       ep = container_of(_ep, struct langwell_ep, ep);
+
+       if (!_req || !_req->complete || !_req->buf
+                       || !list_empty(&req->queue)) {
+               return -EINVAL;
+       }
+
+       if (unlikely(!_ep || !ep->desc))
+               return -EINVAL;
+
+       dev = ep->dev;
+       req->ep = ep;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+               if (req->req.length > ep->ep.maxpacket)
+                       return -EMSGSIZE;
+               is_iso = 1;
+       }
+
+       if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
+               return -ESHUTDOWN;
+
+       /* set up dma mapping in case the caller didn't */
+       if (_req->dma == DMA_ADDR_INVALID) {
+               /* WORKAROUND: WARN_ON(size == 0) */
+               if (_req->length == 0) {
+                       VDBG(dev, "req->length: 0->1\n");
+                       zlflag = 1;
+                       _req->length++;
+               }
+
+               _req->dma = dma_map_single(&dev->pdev->dev,
+                               _req->buf, _req->length,
+                               is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               if (zlflag && (_req->length == 1)) {
+                       VDBG(dev, "req->length: 1->0\n");
+                       zlflag = 0;
+                       _req->length = 0;
+               }
+
+               req->mapped = 1;
+               VDBG(dev, "req->mapped = 1\n");
+       } else {
+               dma_sync_single_for_device(&dev->pdev->dev,
+                               _req->dma, _req->length,
+                               is_in(ep) ?  DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               req->mapped = 0;
+               VDBG(dev, "req->mapped = 0\n");
+       }
+
+       DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
+                       _ep->name,
+                       _req, _req->length, _req->buf, _req->dma);
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+       req->dtd_count = 0;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* build and put dTDs to endpoint queue */
+       if (!req_to_dtd(req)) {
+               queue_dtd(ep, req);
+       } else {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return -ENOMEM;
+       }
+
+       /* update ep0 state */
+       if (ep->ep_num == 0)
+               dev->ep0_state = DATA_STATE_XMIT;
+
+       if (likely(req != NULL)) {
+               list_add_tail(&req->queue, &ep->queue);
+               VDBG(dev, "list_add_tail() \n");
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* dequeue (cancels, unlinks) an I/O request from an endpoint */
+static int langwell_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req;
+       unsigned long           flags;
+       int                     stopped, ep_num, retval = 0;
+       u32                     endptctrl;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc || !_req)
+               return -EINVAL;
+
+       if (!dev->driver)
+               return -ESHUTDOWN;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       stopped = ep->stopped;
+
+       /* quiesce dma while we patch the queue */
+       ep->stopped = 1;
+       ep_num = ep->ep_num;
+
+       /* disable endpoint control register */
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl &= ~EPCTRL_TXE;
+       else
+               endptctrl &= ~EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       /* 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) {
+               retval = -EINVAL;
+               goto done;
+       }
+
+       /* queue head may be partially complete. */
+       if (ep->queue.next == &req->queue) {
+               DBG(dev, "unlink (%s) dma\n", _ep->name);
+               _req->status = -ECONNRESET;
+               langwell_ep_fifo_flush(&ep->ep);
+
+               /* not the last request in endpoint queue */
+               if (likely(ep->queue.next == &req->queue)) {
+                       struct langwell_dqh     *dqh;
+                       struct langwell_request *next_req;
+
+                       dqh = ep->dqh;
+                       next_req = list_entry(req->queue.next,
+                                       struct langwell_request, queue);
+
+                       /* point the dQH to the first dTD of next request */
+                       writel((u32) next_req->head, &dqh->dqh_current);
+               }
+       } else {
+               struct langwell_request *prev_req;
+
+               prev_req = list_entry(req->queue.prev,
+                               struct langwell_request, queue);
+               writel(readl(&req->tail->dtd_next),
+                               &prev_req->tail->dtd_next);
+       }
+
+       done(ep, req, -ECONNRESET);
+
+done:
+       /* enable endpoint again */
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl |= EPCTRL_TXE;
+       else
+               endptctrl |= EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       ep->stopped = stopped;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint set/clear halt */
+static void ep_set_halt(struct langwell_ep *ep, int value)
+{
+       u32                     endptctrl = 0;
+       int                     ep_num;
+       struct langwell_udc     *dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       ep_num = ep->ep_num;
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+
+       /* value: 1 - set halt, 0 - clear halt */
+       if (value) {
+               /* set the stall bit */
+               if (is_in(ep))
+                       endptctrl |= EPCTRL_TXS;
+               else
+                       endptctrl |= EPCTRL_RXS;
+       } else {
+               /* clear the stall bit and reset data toggle */
+               if (is_in(ep)) {
+                       endptctrl &= ~EPCTRL_TXS;
+                       endptctrl |= EPCTRL_TXR;
+               } else {
+                       endptctrl &= ~EPCTRL_RXS;
+                       endptctrl |= EPCTRL_RXR;
+               }
+       }
+
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* set the endpoint halt feature */
+static int langwell_ep_set_halt(struct usb_ep *_ep, int value)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       int                     retval = 0;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       if (ep->desc && (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                       == USB_ENDPOINT_XFER_ISOC)
+               return  -EOPNOTSUPP;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /*
+        * attempt to halt IN ep will fail if any transfer requests
+        * are still queue
+        */
+       if (!list_empty(&ep->queue) && is_in(ep) && value) {
+               /* IN endpoint FIFO holds bytes */
+               DBG(dev, "%s FIFO holds bytes\n", _ep->name);
+               retval = -EAGAIN;
+               goto done;
+       }
+
+       /* endpoint set/clear halt */
+       if (ep->ep_num) {
+               ep_set_halt(ep, value);
+       } else { /* endpoint 0 */
+               dev->ep0_state = WAIT_FOR_SETUP;
+               dev->ep0_dir = USB_DIR_OUT;
+       }
+done:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       DBG(dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* set the halt feature and ignores clear requests */
+static int langwell_ep_set_wedge(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return usb_ep_set_halt(_ep);
+}
+
+
+/* flush contents of a fifo */
+static void langwell_ep_fifo_flush(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       u32                     flush_bit;
+       unsigned long           timeout;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc) {
+               VDBG(dev, "ep or ep->desc is NULL\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return;
+       }
+
+       VDBG(dev, "%s-%s fifo flush\n", _ep->name, is_in(ep) ? "in" : "out");
+
+       /* flush endpoint buffer */
+       if (ep->ep_num == 0)
+               flush_bit = (1 << 16) | 1;
+       else if (is_in(ep))
+               flush_bit = 1 << (ep->ep_num + 16);     /* TX */
+       else
+               flush_bit = 1 << ep->ep_num;            /* RX */
+
+       /* wait until flush complete */
+       timeout = jiffies + FLUSH_TIMEOUT;
+       do {
+               writel(flush_bit, &dev->op_regs->endptflush);
+               while (readl(&dev->op_regs->endptflush)) {
+                       if (time_after(jiffies, timeout)) {
+                               ERROR(dev, "ep flush timeout\n");
+                               goto done;
+                       }
+                       cpu_relax();
+               }
+       } while (readl(&dev->op_regs->endptstat) & flush_bit);
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* endpoints operations structure */
+static const struct usb_ep_ops langwell_ep_ops = {
+
+       /* configure endpoint, making it usable */
+       .enable         = langwell_ep_enable,
+
+       /* endpoint is no longer usable */
+       .disable        = langwell_ep_disable,
+
+       /* allocate a request object to use with this endpoint */
+       .alloc_request  = langwell_alloc_request,
+
+       /* free a request object */
+       .free_request   = langwell_free_request,
+
+       /* queue (submits) an I/O requests to an endpoint */
+       .queue          = langwell_ep_queue,
+
+       /* dequeue (cancels, unlinks) an I/O request from an endpoint */
+       .dequeue        = langwell_ep_dequeue,
+
+       /* set the endpoint halt feature */
+       .set_halt       = langwell_ep_set_halt,
+
+       /* set the halt feature and ignores clear requests */
+       .set_wedge      = langwell_ep_set_wedge,
+
+       /* flush contents of a fifo */
+       .fifo_flush     = langwell_ep_fifo_flush,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device controller usb_gadget_ops structure */
+
+/* returns the current frame number */
+static int langwell_get_frame(struct usb_gadget *_gadget)
+{
+       struct langwell_udc     *dev;
+       u16                     retval;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       retval = readl(&dev->op_regs->frindex) & FRINDEX_MASK;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* tries to wake up the host connected to this gadget */
+static int langwell_wakeup(struct usb_gadget *_gadget)
+{
+       struct langwell_udc     *dev;
+       u32                     portsc1, devlc;
+       unsigned long           flags;
+
+       if (!_gadget)
+               return 0;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* Remote Wakeup feature not enabled by host */
+       if (!dev->remote_wakeup)
+               return -ENOTSUPP;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       portsc1 = readl(&dev->op_regs->portsc1);
+       if (!(portsc1 & PORTS_SUSP)) {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return 0;
+       }
+
+       /* LPM L1 to L0, remote wakeup */
+       if (dev->lpm && dev->lpm_state == LPM_L1) {
+               portsc1 |= PORTS_SLP;
+               writel(portsc1, &dev->op_regs->portsc1);
+       }
+
+       /* force port resume */
+       if (dev->usb_state == USB_STATE_SUSPENDED) {
+               portsc1 |= PORTS_FPR;
+               writel(portsc1, &dev->op_regs->portsc1);
+       }
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* notify controller that VBUS is powered or not */
+static int langwell_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       u32                     usbcmd;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       VDBG(dev, "VBUS status: %s\n", is_active ? "on" : "off");
+
+       dev->vbus_active = (is_active != 0);
+       if (dev->driver && dev->softconnected && dev->vbus_active) {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd |= CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       } else {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd &= ~CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* constrain controller's VBUS power usage */
+static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+       struct langwell_udc     *dev;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->transceiver) {
+               VDBG(dev, "otg_set_power\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return otg_set_power(dev->transceiver, mA);
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return -ENOTSUPP;
+}
+
+
+/* D+ pullup, software-controlled connect/disconnect to USB host */
+static int langwell_pullup(struct usb_gadget *_gadget, int is_on)
+{
+       struct langwell_udc     *dev;
+       u32                     usbcmd;
+       unsigned long           flags;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->softconnected = (is_on != 0);
+
+       if (dev->driver && dev->softconnected && dev->vbus_active) {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd |= CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       } else {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd &= ~CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops langwell_ops = {
+
+       /* returns the current frame number */
+       .get_frame      = langwell_get_frame,
+
+       /* tries to wake up the host connected to this gadget */
+       .wakeup         = langwell_wakeup,
+
+       /* set the device selfpowered feature, always selfpowered */
+       /* .set_selfpowered = langwell_set_selfpowered, */
+
+       /* notify controller that VBUS is powered or not */
+       .vbus_session   = langwell_vbus_session,
+
+       /* constrain controller's VBUS power usage */
+       .vbus_draw      = langwell_vbus_draw,
+
+       /* D+ pullup, software-controlled connect/disconnect to USB host */
+       .pullup         = langwell_pullup,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device controller operations */
+
+/* reset device controller */
+static int langwell_udc_reset(struct langwell_udc *dev)
+{
+       u32             usbcmd, usbmode, devlc, endpointlistaddr;
+       unsigned long   timeout;
+
+       if (!dev)
+               return -EINVAL;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* set controller to stop state */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd &= ~CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       /* reset device controller */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd |= CMD_RST;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       /* wait for reset to complete */
+       timeout = jiffies + RESET_TIMEOUT;
+       while (readl(&dev->op_regs->usbcmd) & CMD_RST) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "device reset timeout\n");
+                       return -ETIMEDOUT;
+               }
+               cpu_relax();
+       }
+
+       /* set controller to device mode */
+       usbmode = readl(&dev->op_regs->usbmode);
+       usbmode |= MODE_DEVICE;
+
+       /* turn setup lockout off, require setup tripwire in usbcmd */
+       usbmode |= MODE_SLOM;
+
+       writel(usbmode, &dev->op_regs->usbmode);
+       usbmode = readl(&dev->op_regs->usbmode);
+       VDBG(dev, "usbmode=0x%08x\n", usbmode);
+
+       /* Write-Clear setup status */
+       writel(0, &dev->op_regs->usbsts);
+
+       /* if support USB LPM, ACK all LPM token */
+       if (dev->lpm) {
+               devlc = readl(&dev->op_regs->devlc);
+               devlc &= ~LPM_STL;      /* don't STALL LPM token */
+               devlc &= ~LPM_NYT_ACK;  /* ACK LPM token */
+               writel(devlc, &dev->op_regs->devlc);
+       }
+
+       /* fill endpointlistaddr register */
+       endpointlistaddr = dev->ep_dqh_dma;
+       endpointlistaddr &= ENDPOINTLISTADDR_MASK;
+       writel(endpointlistaddr, &dev->op_regs->endpointlistaddr);
+
+       VDBG(dev, "dQH base (vir: %p, phy: 0x%08x), endpointlistaddr=0x%08x\n",
+                       dev->ep_dqh, endpointlistaddr,
+                       readl(&dev->op_regs->endpointlistaddr));
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* reinitialize device controller endpoints */
+static int eps_reinit(struct langwell_udc *dev)
+{
+       struct langwell_ep      *ep;
+       char                    name[14];
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* initialize ep0 */
+       ep = &dev->ep[0];
+       ep->dev = dev;
+       strncpy(ep->name, "ep0", sizeof(ep->name));
+       ep->ep.name = ep->name;
+       ep->ep.ops = &langwell_ep_ops;
+       ep->stopped = 0;
+       ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
+       ep->ep_num = 0;
+       ep->desc = &langwell_ep0_desc;
+       INIT_LIST_HEAD(&ep->queue);
+
+       ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+       /* initialize other endpoints */
+       for (i = 2; i < dev->ep_max; i++) {
+               ep = &dev->ep[i];
+               if (i % 2)
+                       snprintf(name, sizeof(name), "ep%din", i / 2);
+               else
+                       snprintf(name, sizeof(name), "ep%dout", i / 2);
+               ep->dev = dev;
+               strncpy(ep->name, name, sizeof(ep->name));
+               ep->ep.name = ep->name;
+
+               ep->ep.ops = &langwell_ep_ops;
+               ep->stopped = 0;
+               ep->ep.maxpacket = (unsigned short) ~0;
+               ep->ep_num = i / 2;
+
+               INIT_LIST_HEAD(&ep->queue);
+               list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+               ep->dqh = &dev->ep_dqh[i];
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* enable interrupt and set controller to run state */
+static void langwell_udc_start(struct langwell_udc *dev)
+{
+       u32     usbintr, usbcmd;
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* enable interrupts */
+       usbintr = INTR_ULPIE    /* ULPI */
+               | INTR_SLE      /* suspend */
+               /* | INTR_SRE   SOF received */
+               | INTR_URE      /* USB reset */
+               | INTR_AAE      /* async advance */
+               | INTR_SEE      /* system error */
+               | INTR_FRE      /* frame list rollover */
+               | INTR_PCE      /* port change detect */
+               | INTR_UEE      /* USB error interrupt */
+               | INTR_UE;      /* USB interrupt */
+       writel(usbintr, &dev->op_regs->usbintr);
+
+       /* clear stopped bit */
+       dev->stopped = 0;
+
+       /* set controller to run */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd |= CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* disable interrupt and set controller to stop state */
+static void langwell_udc_stop(struct langwell_udc *dev)
+{
+       u32     usbcmd;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* disable all interrupts */
+       writel(0, &dev->op_regs->usbintr);
+
+       /* set stopped bit */
+       dev->stopped = 1;
+
+       /* set controller to stop state */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd &= ~CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* stop all USB activities */
+static void stop_activity(struct langwell_udc *dev,
+               struct usb_gadget_driver *driver)
+{
+       struct langwell_ep      *ep;
+       DBG(dev, "---> %s()\n", __func__);
+
+       nuke(&dev->ep[0], -ESHUTDOWN);
+
+       list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+               nuke(ep, -ESHUTDOWN);
+       }
+
+       /* report disconnect; the driver is already quiesced */
+       if (driver) {
+               spin_unlock(&dev->lock);
+               driver->disconnect(&dev->gadget);
+               spin_lock(&dev->lock);
+       }
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device "function" sysfs attribute file */
+static ssize_t show_function(struct device *_dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct langwell_udc     *dev = the_controller;
+
+       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);
+
+
+/* device "langwell_udc" sysfs attribute file */
+static ssize_t show_langwell_udc(struct device *_dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct langwell_udc     *dev = the_controller;
+       struct langwell_request *req;
+       struct langwell_ep      *ep = NULL;
+       char                    *next;
+       unsigned                size;
+       unsigned                t;
+       unsigned                i;
+       unsigned long           flags;
+       u32                     tmp_reg;
+
+       next = buf;
+       size = PAGE_SIZE;
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* driver basic information */
+       t = scnprintf(next, size,
+                       DRIVER_DESC "\n"
+                       "%s version: %s\n"
+                       "Gadget driver: %s\n\n",
+                       driver_name, DRIVER_VERSION,
+                       dev->driver ? dev->driver->driver.name : "(none)");
+       size -= t;
+       next += t;
+
+       /* device registers */
+       tmp_reg = readl(&dev->op_regs->usbcmd);
+       t = scnprintf(next, size,
+                       "USBCMD reg:\n"
+                       "SetupTW: %d\n"
+                       "Run/Stop: %s\n\n",
+                       (tmp_reg & CMD_SUTW) ? 1 : 0,
+                       (tmp_reg & CMD_RUNSTOP) ? "Run" : "Stop");
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->usbsts);
+       t = scnprintf(next, size,
+                       "USB Status Reg:\n"
+                       "Device Suspend: %d\n"
+                       "Reset Received: %d\n"
+                       "System Error: %s\n"
+                       "USB Error Interrupt: %s\n\n",
+                       (tmp_reg & STS_SLI) ? 1 : 0,
+                       (tmp_reg & STS_URI) ? 1 : 0,
+                       (tmp_reg & STS_SEI) ? "Error" : "No error",
+                       (tmp_reg & STS_UEI) ? "Error detected" : "No error");
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->usbintr);
+       t = scnprintf(next, size,
+                       "USB Intrrupt Enable Reg:\n"
+                       "Sleep Enable: %d\n"
+                       "SOF Received Enable: %d\n"
+                       "Reset Enable: %d\n"
+                       "System Error Enable: %d\n"
+                       "Port Change Dectected Enable: %d\n"
+                       "USB Error Intr Enable: %d\n"
+                       "USB Intr Enable: %d\n\n",
+                       (tmp_reg & INTR_SLE) ? 1 : 0,
+                       (tmp_reg & INTR_SRE) ? 1 : 0,
+                       (tmp_reg & INTR_URE) ? 1 : 0,
+                       (tmp_reg & INTR_SEE) ? 1 : 0,
+                       (tmp_reg & INTR_PCE) ? 1 : 0,
+                       (tmp_reg & INTR_UEE) ? 1 : 0,
+                       (tmp_reg & INTR_UE) ? 1 : 0);
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->frindex);
+       t = scnprintf(next, size,
+                       "USB Frame Index Reg:\n"
+                       "Frame Number is 0x%08x\n\n",
+                       (tmp_reg & FRINDEX_MASK));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->deviceaddr);
+       t = scnprintf(next, size,
+                       "USB Device Address Reg:\n"
+                       "Device Addr is 0x%x\n\n",
+                       USBADR(tmp_reg));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->endpointlistaddr);
+       t = scnprintf(next, size,
+                       "USB Endpoint List Address Reg:\n"
+                       "Endpoint List Pointer is 0x%x\n\n",
+                       EPBASE(tmp_reg));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->portsc1);
+       t = scnprintf(next, size,
+               "USB Port Status & Control Reg:\n"
+               "Port Reset: %s\n"
+               "Port Suspend Mode: %s\n"
+               "Over-current Change: %s\n"
+               "Port Enable/Disable Change: %s\n"
+               "Port Enabled/Disabled: %s\n"
+               "Current Connect Status: %s\n\n",
+               (tmp_reg & PORTS_PR) ? "Reset" : "Not Reset",
+               (tmp_reg & PORTS_SUSP) ? "Suspend " : "Not Suspend",
+               (tmp_reg & PORTS_OCC) ? "Detected" : "No",
+               (tmp_reg & PORTS_PEC) ? "Changed" : "Not Changed",
+               (tmp_reg & PORTS_PE) ? "Enable" : "Not Correct",
+               (tmp_reg & PORTS_CCS) ?  "Attached" : "Not Attached");
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->devlc);
+       t = scnprintf(next, size,
+               "Device LPM Control Reg:\n"
+               "Parallel Transceiver : %d\n"
+               "Serial Transceiver : %d\n"
+               "Port Speed: %s\n"
+               "Port Force Full Speed Connenct: %s\n"
+               "PHY Low Power Suspend Clock Disable: %s\n"
+               "BmAttributes: %d\n\n",
+               LPM_PTS(tmp_reg),
+               (tmp_reg & LPM_STS) ? 1 : 0,
+               ({
+                       char    *s;
+                       switch (LPM_PSPD(tmp_reg)) {
+                       case LPM_SPEED_FULL:
+                               s = "Full Speed"; break;
+                       case LPM_SPEED_LOW:
+                               s = "Low Speed"; break;
+                       case LPM_SPEED_HIGH:
+                               s = "High Speed"; break;
+                       default:
+                               s = "Unknown Speed"; break;
+                       }
+                       s;
+               }),
+               (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force",
+               (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled",
+               LPM_BA(tmp_reg));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->usbmode);
+       t = scnprintf(next, size,
+                       "USB Mode Reg:\n"
+                       "Controller Mode is : %s\n\n", ({
+                               char *s;
+                               switch (MODE_CM(tmp_reg)) {
+                               case MODE_IDLE:
+                                       s = "Idle"; break;
+                               case MODE_DEVICE:
+                                       s = "Device Controller"; break;
+                               case MODE_HOST:
+                                       s = "Host Controller"; break;
+                               default:
+                                       s = "None"; break;
+                               }
+                               s;
+                       }));
+       size -= t;
+       next += t;
+
+       tmp_reg = readl(&dev->op_regs->endptsetupstat);
+       t = scnprintf(next, size,
+                       "Endpoint Setup Status Reg:\n"
+                       "SETUP on ep 0x%04x\n\n",
+                       tmp_reg & SETUPSTAT_MASK);
+       size -= t;
+       next += t;
+
+       for (i = 0; i < dev->ep_max / 2; i++) {
+               tmp_reg = readl(&dev->op_regs->endptctrl[i]);
+               t = scnprintf(next, size, "EP Ctrl Reg [%d]: 0x%08x\n",
+                               i, tmp_reg);
+               size -= t;
+               next += t;
+       }
+       tmp_reg = readl(&dev->op_regs->endptprime);
+       t = scnprintf(next, size, "EP Prime Reg: 0x%08x\n\n", tmp_reg);
+       size -= t;
+       next += t;
+
+       /* langwell_udc, langwell_ep, langwell_request structure information */
+       ep = &dev->ep[0];
+       t = scnprintf(next, size, "%s MaxPacketSize: 0x%x, ep_num: %d\n",
+                       ep->ep.name, ep->ep.maxpacket, ep->ep_num);
+       size -= t;
+       next += t;
+
+       if (list_empty(&ep->queue)) {
+               t = scnprintf(next, size, "its req queue is empty\n\n");
+               size -= t;
+               next += t;
+       } else {
+               list_for_each_entry(req, &ep->queue, queue) {
+                       t = scnprintf(next, size,
+                               "req %p actual 0x%x length 0x%x  buf %p\n",
+                               &req->req, req->req.actual,
+                               req->req.length, req->req.buf);
+                       size -= t;
+                       next += t;
+               }
+       }
+       /* other gadget->eplist ep */
+       list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+               if (ep->desc) {
+                       t = scnprintf(next, size,
+                                       "\n%s MaxPacketSize: 0x%x, "
+                                       "ep_num: %d\n",
+                                       ep->ep.name, ep->ep.maxpacket,
+                                       ep->ep_num);
+                       size -= t;
+                       next += t;
+
+                       if (list_empty(&ep->queue)) {
+                               t = scnprintf(next, size,
+                                               "its req queue is empty\n\n");
+                               size -= t;
+                               next += t;
+                       } else {
+                               list_for_each_entry(req, &ep->queue, queue) {
+                                       t = scnprintf(next, size,
+                                               "req %p actual 0x%x length "
+                                               "0x%x  buf %p\n",
+                                               &req->req, req->req.actual,
+                                               req->req.length, req->req.buf);
+                                       size -= t;
+                                       next += t;
+                               }
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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.
+ */
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+       struct langwell_udc     *dev = the_controller;
+       unsigned long           flags;
+       int                     retval;
+
+       if (!dev)
+               return -ENODEV;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       if (dev->driver)
+               return -EBUSY;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* hook up the driver ... */
+       driver->driver.bus = NULL;
+       dev->driver = driver;
+       dev->gadget.dev.driver = &driver->driver;
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       retval = driver->bind(&dev->gadget);
+       if (retval) {
+               DBG(dev, "bind to driver %s --> %d\n",
+                               driver->driver.name, retval);
+               dev->driver = NULL;
+               dev->gadget.dev.driver = NULL;
+               return retval;
+       }
+
+       retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
+       if (retval)
+               goto err_unbind;
+
+       dev->usb_state = USB_STATE_ATTACHED;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       /* enable interrupt and set controller to run state */
+       if (dev->got_irq)
+               langwell_udc_start(dev);
+
+       VDBG(dev, "After langwell_udc_start(), print all registers:\n");
+#ifdef VERBOSE
+       print_all_registers(dev);
+#endif
+
+       INFO(dev, "register driver: %s\n", driver->driver.name);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+
+err_unbind:
+       driver->unbind(&dev->gadget);
+       dev->gadget.dev.driver = NULL;
+       dev->driver = NULL;
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+
+/* unregister gadget driver */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+       struct langwell_udc     *dev = the_controller;
+       unsigned long           flags;
+
+       if (!dev)
+               return -ENODEV;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       if (unlikely(!driver || !driver->bind || !driver->unbind))
+               return -EINVAL;
+
+       /* unbind OTG transceiver */
+       if (dev->transceiver)
+               (void)otg_set_peripheral(dev->transceiver, 0);
+
+       /* disable interrupt and set controller to stop state */
+       langwell_udc_stop(dev);
+
+       dev->usb_state = USB_STATE_ATTACHED;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* stop all usb activities */
+       dev->gadget.speed = USB_SPEED_UNKNOWN;
+       stop_activity(dev, driver);
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       /* unbind gadget driver */
+       driver->unbind(&dev->gadget);
+       dev->gadget.dev.driver = NULL;
+       dev->driver = NULL;
+
+       device_remove_file(&dev->pdev->dev, &dev_attr_function);
+
+       INFO(dev, "unregistered driver '%s'\n", driver->driver.name);
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * setup tripwire is used as a semaphore to ensure that the setup data
+ * payload is extracted from a dQH without being corrupted
+ */
+static void setup_tripwire(struct langwell_udc *dev)
+{
+       u32                     usbcmd,
+                               endptsetupstat;
+       unsigned long           timeout;
+       struct langwell_dqh     *dqh;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 OUT dQH */
+       dqh = &dev->ep_dqh[EP_DIR_OUT];
+
+       /* Write-Clear endptsetupstat */
+       endptsetupstat = readl(&dev->op_regs->endptsetupstat);
+       writel(endptsetupstat, &dev->op_regs->endptsetupstat);
+
+       /* wait until endptsetupstat is cleared */
+       timeout = jiffies + SETUPSTAT_TIMEOUT;
+       while (readl(&dev->op_regs->endptsetupstat)) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "setup_tripwire timeout\n");
+                       break;
+               }
+               cpu_relax();
+       }
+
+       /* while a hazard exists when setup packet arrives */
+       do {
+               /* set setup tripwire bit */
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               writel(usbcmd | CMD_SUTW, &dev->op_regs->usbcmd);
+
+               /* copy the setup packet to local buffer */
+               memcpy(&dev->local_setup_buff, &dqh->dqh_setup, 8);
+       } while (!(readl(&dev->op_regs->usbcmd) & CMD_SUTW));
+
+       /* Write-Clear setup tripwire bit */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       writel(usbcmd & ~CMD_SUTW, &dev->op_regs->usbcmd);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* protocol ep0 stall, will automatically be cleared on new transaction */
+static void ep0_stall(struct langwell_udc *dev)
+{
+       u32     endptctrl;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* set TX and RX to stall */
+       endptctrl = readl(&dev->op_regs->endptctrl[0]);
+       endptctrl |= EPCTRL_TXS | EPCTRL_RXS;
+       writel(endptctrl, &dev->op_regs->endptctrl[0]);
+
+       /* update ep0 state */
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* PRIME a status phase for ep0 */
+static int prime_status_phase(struct langwell_udc *dev, int dir)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       int                     status = 0;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dir == EP_DIR_IN)
+               dev->ep0_dir = USB_DIR_IN;
+       else
+               dev->ep0_dir = USB_DIR_OUT;
+
+       ep = &dev->ep[0];
+       dev->ep0_state = WAIT_FOR_OUT_STATUS;
+
+       req = dev->status_req;
+
+       req->ep = ep;
+       req->req.length = 0;
+       req->req.status = -EINPROGRESS;
+       req->req.actual = 0;
+       req->req.complete = NULL;
+       req->dtd_count = 0;
+
+       if (!req_to_dtd(req))
+               status = queue_dtd(ep, req);
+       else
+               return -ENOMEM;
+
+       if (status)
+               ERROR(dev, "can't queue ep0 status request\n");
+
+       list_add_tail(&req->queue, &ep->queue);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return status;
+}
+
+
+/* SET_ADDRESS request routine */
+static void set_address(struct langwell_udc *dev, u16 value,
+               u16 index, u16 length)
+{
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* save the new address to device struct */
+       dev->dev_addr = (u8) value;
+       VDBG(dev, "dev->dev_addr = %d\n", dev->dev_addr);
+
+       /* update usb state */
+       dev->usb_state = USB_STATE_ADDRESS;
+
+       /* STATUS phase */
+       if (prime_status_phase(dev, EP_DIR_IN))
+               ep0_stall(dev);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* return endpoint by windex */
+static struct langwell_ep *get_ep_by_windex(struct langwell_udc *dev,
+               u16 wIndex)
+{
+       struct langwell_ep              *ep;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       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 & USB_ENDPOINT_NUMBER_MASK)
+                       == (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
+                       return ep;
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return NULL;
+}
+
+
+/* return whether endpoint is stalled, 0: not stalled; 1: stalled */
+static int ep_is_stall(struct langwell_ep *ep)
+{
+       struct langwell_udc     *dev = ep->dev;
+       u32                     endptctrl;
+       int                     retval;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       endptctrl = readl(&dev->op_regs->endptctrl[ep->ep_num]);
+       if (is_in(ep))
+               retval = endptctrl & EPCTRL_TXS ? 1 : 0;
+       else
+               retval = endptctrl & EPCTRL_RXS ? 1 : 0;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* GET_STATUS request routine */
+static void get_status(struct langwell_udc *dev, u8 request_type, u16 value,
+               u16 index, u16 length)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       u16     status_data = 0;        /* 16 bits cpu view status data */
+       int     status = 0;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       ep = &dev->ep[0];
+
+       if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+               /* get device status */
+               status_data = 1 << USB_DEVICE_SELF_POWERED;
+               status_data |= dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+       } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+               /* get interface status */
+               status_data = 0;
+       } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+               /* get endpoint status */
+               struct langwell_ep      *epn;
+               epn = get_ep_by_windex(dev, index);
+               /* stall if endpoint doesn't exist */
+               if (!epn)
+                       goto stall;
+
+               status_data = ep_is_stall(epn) << USB_ENDPOINT_HALT;
+       }
+
+       dev->ep0_dir = USB_DIR_IN;
+
+       /* borrow the per device status_req */
+       req = dev->status_req;
+
+       /* fill in the reqest structure */
+       *((u16 *) req->req.buf) = cpu_to_le16(status_data);
+       req->ep = ep;
+       req->req.length = 2;
+       req->req.status = -EINPROGRESS;
+       req->req.actual = 0;
+       req->req.complete = NULL;
+       req->dtd_count = 0;
+
+       /* prime the data phase */
+       if (!req_to_dtd(req))
+               status = queue_dtd(ep, req);
+       else                    /* no mem */
+               goto stall;
+
+       if (status) {
+               ERROR(dev, "response error on GET_STATUS request\n");
+               goto stall;
+       }
+
+       list_add_tail(&req->queue, &ep->queue);
+       dev->ep0_state = DATA_STATE_XMIT;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+stall:
+       ep0_stall(dev);
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* setup packet interrupt handler */
+static void handle_setup_packet(struct langwell_udc *dev,
+               struct usb_ctrlrequest *setup)
+{
+       u16     wValue = le16_to_cpu(setup->wValue);
+       u16     wIndex = le16_to_cpu(setup->wIndex);
+       u16     wLength = le16_to_cpu(setup->wLength);
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 fifo flush */
+       nuke(&dev->ep[0], -ESHUTDOWN);
+
+       DBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+                       setup->bRequestType, setup->bRequest,
+                       wValue, wIndex, wLength);
+
+       /* RNDIS gadget delegate */
+       if ((setup->bRequestType == 0x21) && (setup->bRequest == 0x00)) {
+               /* USB_CDC_SEND_ENCAPSULATED_COMMAND */
+               goto delegate;
+       }
+
+       /* USB_CDC_GET_ENCAPSULATED_RESPONSE */
+       if ((setup->bRequestType == 0xa1) && (setup->bRequest == 0x01)) {
+               /* USB_CDC_GET_ENCAPSULATED_RESPONSE */
+               goto delegate;
+       }
+
+       /* We process some stardard setup requests here */
+       switch (setup->bRequest) {
+       case USB_REQ_GET_STATUS:
+               DBG(dev, "SETUP: USB_REQ_GET_STATUS\n");
+               /* get status, DATA and STATUS phase */
+               if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+                                       != (USB_DIR_IN | USB_TYPE_STANDARD))
+                       break;
+               get_status(dev, setup->bRequestType, wValue, wIndex, wLength);
+               goto end;
+
+       case USB_REQ_SET_ADDRESS:
+               DBG(dev, "SETUP: USB_REQ_SET_ADDRESS\n");
+               /* STATUS phase */
+               if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+                                               | USB_RECIP_DEVICE))
+                       break;
+               set_address(dev, wValue, wIndex, wLength);
+               goto end;
+
+       case USB_REQ_CLEAR_FEATURE:
+       case USB_REQ_SET_FEATURE:
+               /* STATUS phase */
+       {
+               int rc = -EOPNOTSUPP;
+               if (setup->bRequest == USB_REQ_SET_FEATURE)
+                       DBG(dev, "SETUP: USB_REQ_SET_FEATURE\n");
+               else if (setup->bRequest == USB_REQ_CLEAR_FEATURE)
+                       DBG(dev, "SETUP: USB_REQ_CLEAR_FEATURE\n");
+
+               if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
+                               == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
+                       struct langwell_ep      *epn;
+                       epn = get_ep_by_windex(dev, wIndex);
+                       /* stall if endpoint doesn't exist */
+                       if (!epn) {
+                               ep0_stall(dev);
+                               goto end;
+                       }
+
+                       if (wValue != 0 || wLength != 0
+                                       || epn->ep_num > dev->ep_max)
+                               break;
+
+                       spin_unlock(&dev->lock);
+                       rc = langwell_ep_set_halt(&epn->ep,
+                                       (setup->bRequest == USB_REQ_SET_FEATURE)
+                                               ? 1 : 0);
+                       spin_lock(&dev->lock);
+
+               } else if ((setup->bRequestType & (USB_RECIP_MASK
+                               | USB_TYPE_MASK)) == (USB_RECIP_DEVICE
+                               | USB_TYPE_STANDARD)) {
+                       if (!gadget_is_otg(&dev->gadget))
+                               break;
+                       else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) {
+                               dev->gadget.b_hnp_enable = 1;
+#ifdef OTG_TRANSCEIVER
+                               if (!dev->lotg->otg.default_a)
+                                       dev->lotg->hsm.b_hnp_enable = 1;
+#endif
+                       } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
+                               dev->gadget.a_hnp_support = 1;
+                       else if (setup->bRequest ==
+                                       USB_DEVICE_A_ALT_HNP_SUPPORT)
+                               dev->gadget.a_alt_hnp_support = 1;
+                       else
+                               break;
+                       rc = 0;
+               } else
+                       break;
+
+               if (rc == 0) {
+                       if (prime_status_phase(dev, EP_DIR_IN))
+                               ep0_stall(dev);
+               }
+               goto end;
+       }
+
+       case USB_REQ_GET_DESCRIPTOR:
+               DBG(dev, "SETUP: USB_REQ_GET_DESCRIPTOR\n");
+               goto delegate;
+
+       case USB_REQ_SET_DESCRIPTOR:
+               DBG(dev, "SETUP: USB_REQ_SET_DESCRIPTOR unsupported\n");
+               goto delegate;
+
+       case USB_REQ_GET_CONFIGURATION:
+               DBG(dev, "SETUP: USB_REQ_GET_CONFIGURATION\n");
+               goto delegate;
+
+       case USB_REQ_SET_CONFIGURATION:
+               DBG(dev, "SETUP: USB_REQ_SET_CONFIGURATION\n");
+               goto delegate;
+
+       case USB_REQ_GET_INTERFACE:
+               DBG(dev, "SETUP: USB_REQ_GET_INTERFACE\n");
+               goto delegate;
+
+       case USB_REQ_SET_INTERFACE:
+               DBG(dev, "SETUP: USB_REQ_SET_INTERFACE\n");
+               goto delegate;
+
+       case USB_REQ_SYNCH_FRAME:
+               DBG(dev, "SETUP: USB_REQ_SYNCH_FRAME unsupported\n");
+               goto delegate;
+
+       default:
+               /* delegate USB standard requests to the gadget driver */
+               goto delegate;
+delegate:
+               /* USB requests handled by gadget */
+               if (wLength) {
+                       /* DATA phase from gadget, STATUS phase from udc */
+                       dev->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+                                       ?  USB_DIR_IN : USB_DIR_OUT;
+                       VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n",
+                                       dev->ep0_dir, wLength);
+                       spin_unlock(&dev->lock);
+                       if (dev->driver->setup(&dev->gadget,
+                                       &dev->local_setup_buff) < 0)
+                               ep0_stall(dev);
+                       spin_lock(&dev->lock);
+                       dev->ep0_state = (setup->bRequestType & USB_DIR_IN)
+                                       ?  DATA_STATE_XMIT : DATA_STATE_RECV;
+               } else {
+                       /* no DATA phase, IN STATUS phase from gadget */
+                       dev->ep0_dir = USB_DIR_IN;
+                       VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n",
+                                       dev->ep0_dir, wLength);
+                       spin_unlock(&dev->lock);
+                       if (dev->driver->setup(&dev->gadget,
+                                       &dev->local_setup_buff) < 0)
+                               ep0_stall(dev);
+                       spin_lock(&dev->lock);
+                       dev->ep0_state = WAIT_FOR_OUT_STATUS;
+               }
+               break;
+       }
+end:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* transfer completion, process endpoint request and free the completed dTDs
+ * for this request
+ */
+static int process_ep_req(struct langwell_udc *dev, int index,
+               struct langwell_request *curr_req)
+{
+       struct langwell_dtd     *curr_dtd;
+       struct langwell_dqh     *curr_dqh;
+       int                     td_complete, actual, remaining_length;
+       int                     i, dir;
+       u8                      dtd_status = 0;
+       int                     retval = 0;
+
+       curr_dqh = &dev->ep_dqh[index];
+       dir = index % 2;
+
+       curr_dtd = curr_req->head;
+       td_complete = 0;
+       actual = curr_req->req.length;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       for (i = 0; i < curr_req->dtd_count; i++) {
+               remaining_length = le16_to_cpu(curr_dtd->dtd_total);
+               actual -= remaining_length;
+
+               /* command execution states by dTD */
+               dtd_status = curr_dtd->dtd_status;
+
+               if (!dtd_status) {
+                       /* transfers completed successfully */
+                       if (!remaining_length) {
+                               td_complete++;
+                               VDBG(dev, "dTD transmitted successfully\n");
+                       } else {
+                               if (dir) {
+                                       VDBG(dev, "TX dTD remains data\n");
+                                       retval = -EPROTO;
+                                       break;
+
+                               } else {
+                                       td_complete++;
+                                       break;
+                               }
+                       }
+               } else {
+                       /* transfers completed with errors */
+                       if (dtd_status & DTD_STS_ACTIVE) {
+                               DBG(dev, "request not completed\n");
+                               retval = 1;
+                               return retval;
+                       } else if (dtd_status & DTD_STS_HALTED) {
+                               ERROR(dev, "dTD error %08x dQH[%d]\n",
+                                               dtd_status, index);
+                               /* clear the errors and halt condition */
+                               curr_dqh->dtd_status = 0;
+                               retval = -EPIPE;
+                               break;
+                       } else if (dtd_status & DTD_STS_DBE) {
+                               DBG(dev, "data buffer (overflow) error\n");
+                               retval = -EPROTO;
+                               break;
+                       } else if (dtd_status & DTD_STS_TRE) {
+                               DBG(dev, "transaction(ISO) error\n");
+                               retval = -EILSEQ;
+                               break;
+                       } else
+                               ERROR(dev, "unknown error (0x%x)!\n",
+                                               dtd_status);
+               }
+
+               if (i != curr_req->dtd_count - 1)
+                       curr_dtd = (struct langwell_dtd *)
+                               curr_dtd->next_dtd_virt;
+       }
+
+       if (retval)
+               return retval;
+
+       curr_req->req.actual = actual;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* complete DATA or STATUS phase of ep0 prime status phase if needed */
+static void ep0_req_complete(struct langwell_udc *dev,
+               struct langwell_ep *ep0, struct langwell_request *req)
+{
+       u32     new_addr;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->usb_state == USB_STATE_ADDRESS) {
+               /* set the new address */
+               new_addr = (u32)dev->dev_addr;
+               writel(new_addr << USBADR_SHIFT, &dev->op_regs->deviceaddr);
+
+               new_addr = USBADR(readl(&dev->op_regs->deviceaddr));
+               VDBG(dev, "new_addr = %d\n", new_addr);
+       }
+
+       done(ep0, req, 0);
+
+       switch (dev->ep0_state) {
+       case DATA_STATE_XMIT:
+               /* receive status phase */
+               if (prime_status_phase(dev, EP_DIR_OUT))
+                       ep0_stall(dev);
+               break;
+       case DATA_STATE_RECV:
+               /* send status phase */
+               if (prime_status_phase(dev, EP_DIR_IN))
+                       ep0_stall(dev);
+               break;
+       case WAIT_FOR_OUT_STATUS:
+               dev->ep0_state = WAIT_FOR_SETUP;
+               break;
+       case WAIT_FOR_SETUP:
+               ERROR(dev, "unexpect ep0 packets\n");
+               break;
+       default:
+               ep0_stall(dev);
+               break;
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB transfer completion interrupt */
+static void handle_trans_complete(struct langwell_udc *dev)
+{
+       u32                     complete_bits;
+       int                     i, ep_num, dir, bit_mask, status;
+       struct langwell_ep      *epn;
+       struct langwell_request *curr_req, *temp_req;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       complete_bits = readl(&dev->op_regs->endptcomplete);
+       VDBG(dev, "endptcomplete register: 0x%08x\n", complete_bits);
+
+       /* Write-Clear the bits in endptcomplete register */
+       writel(complete_bits, &dev->op_regs->endptcomplete);
+
+       if (!complete_bits) {
+               DBG(dev, "complete_bits = 0\n");
+               goto done;
+       }
+
+       for (i = 0; i < dev->ep_max; i++) {
+               ep_num = i / 2;
+               dir = i % 2;
+
+               bit_mask = 1 << (ep_num + 16 * dir);
+
+               if (!(complete_bits & bit_mask))
+                       continue;
+
+               /* ep0 */
+               if (i == 1)
+                       epn = &dev->ep[0];
+               else
+                       epn = &dev->ep[i];
+
+               if (epn->name == NULL) {
+                       WARNING(dev, "invalid endpoint\n");
+                       continue;
+               }
+
+               if (i < 2)
+                       /* ep0 in and out */
+                       DBG(dev, "%s-%s transfer completed\n",
+                                       epn->name,
+                                       is_in(epn) ? "in" : "out");
+               else
+                       DBG(dev, "%s transfer completed\n", epn->name);
+
+               /* process the req queue until an uncomplete request */
+               list_for_each_entry_safe(curr_req, temp_req,
+                               &epn->queue, queue) {
+                       status = process_ep_req(dev, i, curr_req);
+                       VDBG(dev, "%s req status: %d\n", epn->name, status);
+
+                       if (status)
+                               break;
+
+                       /* write back status to req */
+                       curr_req->req.status = status;
+
+                       /* ep0 request completion */
+                       if (ep_num == 0) {
+                               ep0_req_complete(dev, epn, curr_req);
+                               break;
+                       } else {
+                               done(epn, curr_req, status);
+                       }
+               }
+       }
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* port change detect interrupt handler */
+static void handle_port_change(struct langwell_udc *dev)
+{
+       u32     portsc1, devlc;
+       u32     speed;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->bus_reset)
+               dev->bus_reset = 0;
+
+       portsc1 = readl(&dev->op_regs->portsc1);
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "portsc1 = 0x%08x, devlc = 0x%08x\n",
+                       portsc1, devlc);
+
+       /* bus reset is finished */
+       if (!(portsc1 & PORTS_PR)) {
+               /* get the speed */
+               speed = LPM_PSPD(devlc);
+               switch (speed) {
+               case LPM_SPEED_HIGH:
+                       dev->gadget.speed = USB_SPEED_HIGH;
+                       break;
+               case LPM_SPEED_FULL:
+                       dev->gadget.speed = USB_SPEED_FULL;
+                       break;
+               case LPM_SPEED_LOW:
+                       dev->gadget.speed = USB_SPEED_LOW;
+                       break;
+               default:
+                       dev->gadget.speed = USB_SPEED_UNKNOWN;
+                       break;
+               }
+               VDBG(dev, "speed = %d, dev->gadget.speed = %d\n",
+                               speed, dev->gadget.speed);
+       }
+
+       /* LPM L0 to L1 */
+       if (dev->lpm && dev->lpm_state == LPM_L0)
+               if (portsc1 & PORTS_SUSP && portsc1 & PORTS_SLP) {
+                               INFO(dev, "LPM L0 to L1\n");
+                               dev->lpm_state = LPM_L1;
+               }
+
+       /* LPM L1 to L0, force resume or remote wakeup finished */
+       if (dev->lpm && dev->lpm_state == LPM_L1)
+               if (!(portsc1 & PORTS_SUSP)) {
+                       if (portsc1 & PORTS_SLP)
+                               INFO(dev, "LPM L1 to L0, force resume\n");
+                       else
+                               INFO(dev, "LPM L1 to L0, remote wakeup\n");
+
+                       dev->lpm_state = LPM_L0;
+               }
+
+       /* update USB state */
+       if (!dev->resume_state)
+               dev->usb_state = USB_STATE_DEFAULT;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB reset interrupt handler */
+static void handle_usb_reset(struct langwell_udc *dev)
+{
+       u32             deviceaddr,
+                       endptsetupstat,
+                       endptcomplete;
+       unsigned long   timeout;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* Write-Clear the device address */
+       deviceaddr = readl(&dev->op_regs->deviceaddr);
+       writel(deviceaddr & ~USBADR_MASK, &dev->op_regs->deviceaddr);
+
+       dev->dev_addr = 0;
+
+       /* clear usb state */
+       dev->resume_state = 0;
+
+       /* LPM L1 to L0, reset */
+       if (dev->lpm)
+               dev->lpm_state = LPM_L0;
+
+       dev->ep0_dir = USB_DIR_OUT;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->remote_wakeup = 0;         /* default to 0 on reset */
+       dev->gadget.b_hnp_enable = 0;
+       dev->gadget.a_hnp_support = 0;
+       dev->gadget.a_alt_hnp_support = 0;
+
+       /* Write-Clear all the setup token semaphores */
+       endptsetupstat = readl(&dev->op_regs->endptsetupstat);
+       writel(endptsetupstat, &dev->op_regs->endptsetupstat);
+
+       /* Write-Clear all the endpoint complete status bits */
+       endptcomplete = readl(&dev->op_regs->endptcomplete);
+       writel(endptcomplete, &dev->op_regs->endptcomplete);
+
+       /* wait until all endptprime bits cleared */
+       timeout = jiffies + PRIME_TIMEOUT;
+       while (readl(&dev->op_regs->endptprime)) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "USB reset timeout\n");
+                       break;
+               }
+               cpu_relax();
+       }
+
+       /* write 1s to endptflush register to clear any primed buffers */
+       writel((u32) ~0, &dev->op_regs->endptflush);
+
+       if (readl(&dev->op_regs->portsc1) & PORTS_PR) {
+               VDBG(dev, "USB bus reset\n");
+               /* bus is reseting */
+               dev->bus_reset = 1;
+
+               /* reset all the queues, stop all USB activities */
+               stop_activity(dev, dev->driver);
+               dev->usb_state = USB_STATE_DEFAULT;
+       } else {
+               VDBG(dev, "device controller reset\n");
+               /* controller reset */
+               langwell_udc_reset(dev);
+
+               /* reset all the queues, stop all USB activities */
+               stop_activity(dev, dev->driver);
+
+               /* reset ep0 dQH and endptctrl */
+               ep0_reset(dev);
+
+               /* enable interrupt and set controller to run state */
+               langwell_udc_start(dev);
+
+               dev->usb_state = USB_STATE_ATTACHED;
+       }
+
+#ifdef OTG_TRANSCEIVER
+       /* refer to USB OTG 6.6.2.3 b_hnp_en is cleared */
+       if (!dev->lotg->otg.default_a)
+               dev->lotg->hsm.b_hnp_enable = 0;
+#endif
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB bus suspend/resume interrupt */
+static void handle_bus_suspend(struct langwell_udc *dev)
+{
+       u32             devlc;
+       DBG(dev, "---> %s()\n", __func__);
+
+       dev->resume_state = dev->usb_state;
+       dev->usb_state = USB_STATE_SUSPENDED;
+
+#ifdef OTG_TRANSCEIVER
+       if (dev->lotg->otg.default_a) {
+               if (dev->lotg->hsm.b_bus_suspend_vld == 1) {
+                       dev->lotg->hsm.b_bus_suspend = 1;
+                       /* notify transceiver the state changes */
+                       if (spin_trylock(&dev->lotg->wq_lock)) {
+                               langwell_update_transceiver();
+                               spin_unlock(&dev->lotg->wq_lock);
+                       }
+               }
+               dev->lotg->hsm.b_bus_suspend_vld++;
+       } else {
+               if (!dev->lotg->hsm.a_bus_suspend) {
+                       dev->lotg->hsm.a_bus_suspend = 1;
+                       /* notify transceiver the state changes */
+                       if (spin_trylock(&dev->lotg->wq_lock)) {
+                               langwell_update_transceiver();
+                               spin_unlock(&dev->lotg->wq_lock);
+                       }
+               }
+       }
+#endif
+
+       /* report suspend to the driver */
+       if (dev->driver) {
+               if (dev->driver->suspend) {
+                       spin_unlock(&dev->lock);
+                       dev->driver->suspend(&dev->gadget);
+                       spin_lock(&dev->lock);
+                       DBG(dev, "suspend %s\n", dev->driver->driver.name);
+               }
+       }
+
+       /* enter PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc |= LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+
+static void handle_bus_resume(struct langwell_udc *dev)
+{
+       u32             devlc;
+       DBG(dev, "---> %s()\n", __func__);
+
+       dev->usb_state = dev->resume_state;
+       dev->resume_state = 0;
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+#ifdef OTG_TRANSCEIVER
+       if (dev->lotg->otg.default_a == 0)
+               dev->lotg->hsm.a_bus_suspend = 0;
+#endif
+
+       /* report resume to the driver */
+       if (dev->driver) {
+               if (dev->driver->resume) {
+                       spin_unlock(&dev->lock);
+                       dev->driver->resume(&dev->gadget);
+                       spin_lock(&dev->lock);
+                       DBG(dev, "resume %s\n", dev->driver->driver.name);
+               }
+       }
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* USB device controller interrupt handler */
+static irqreturn_t langwell_irq(int irq, void *_dev)
+{
+       struct langwell_udc     *dev = _dev;
+       u32                     usbsts,
+                               usbintr,
+                               irq_sts,
+                               portsc1;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->stopped) {
+               VDBG(dev, "handle IRQ_NONE\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return IRQ_NONE;
+       }
+
+       spin_lock(&dev->lock);
+
+       /* USB status */
+       usbsts = readl(&dev->op_regs->usbsts);
+
+       /* USB interrupt enable */
+       usbintr = readl(&dev->op_regs->usbintr);
+
+       irq_sts = usbsts & usbintr;
+       VDBG(dev, "usbsts = 0x%08x, usbintr = 0x%08x, irq_sts = 0x%08x\n",
+                       usbsts, usbintr, irq_sts);
+
+       if (!irq_sts) {
+               VDBG(dev, "handle IRQ_NONE\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               spin_unlock(&dev->lock);
+               return IRQ_NONE;
+       }
+
+       /* Write-Clear interrupt status bits */
+       writel(irq_sts, &dev->op_regs->usbsts);
+
+       /* resume from suspend */
+       portsc1 = readl(&dev->op_regs->portsc1);
+       if (dev->usb_state == USB_STATE_SUSPENDED)
+               if (!(portsc1 & PORTS_SUSP))
+                       handle_bus_resume(dev);
+
+       /* USB interrupt */
+       if (irq_sts & STS_UI) {
+               VDBG(dev, "USB interrupt\n");
+
+               /* setup packet received from ep0 */
+               if (readl(&dev->op_regs->endptsetupstat)
+                               & EP0SETUPSTAT_MASK) {
+                       VDBG(dev, "USB SETUP packet received interrupt\n");
+                       /* setup tripwire semaphone */
+                       setup_tripwire(dev);
+                       handle_setup_packet(dev, &dev->local_setup_buff);
+               }
+
+               /* USB transfer completion */
+               if (readl(&dev->op_regs->endptcomplete)) {
+                       VDBG(dev, "USB transfer completion interrupt\n");
+                       handle_trans_complete(dev);
+               }
+       }
+
+       /* SOF received interrupt (for ISO transfer) */
+       if (irq_sts & STS_SRI) {
+               /* FIXME */
+               /* VDBG(dev, "SOF received interrupt\n"); */
+       }
+
+       /* port change detect interrupt */
+       if (irq_sts & STS_PCI) {
+               VDBG(dev, "port change detect interrupt\n");
+               handle_port_change(dev);
+       }
+
+       /* suspend interrrupt */
+       if (irq_sts & STS_SLI) {
+               VDBG(dev, "suspend interrupt\n");
+               handle_bus_suspend(dev);
+       }
+
+       /* USB reset interrupt */
+       if (irq_sts & STS_URI) {
+               VDBG(dev, "USB reset interrupt\n");
+               handle_usb_reset(dev);
+       }
+
+       /* USB error or system error interrupt */
+       if (irq_sts & (STS_UEI | STS_SEI)) {
+               /* FIXME */
+               WARNING(dev, "error IRQ, irq_sts: %x\n", irq_sts);
+       }
+
+       spin_unlock(&dev->lock);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return IRQ_HANDLED;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* release device structure */
+static void gadget_release(struct device *_dev)
+{
+       struct langwell_udc     *dev = the_controller;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       complete(dev->done);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       kfree(dev);
+}
+
+
+/* tear down the binding between this driver and the pci device */
+static void langwell_udc_remove(struct pci_dev *pdev)
+{
+       struct langwell_udc     *dev = the_controller;
+
+       DECLARE_COMPLETION(done);
+
+       BUG_ON(dev->driver);
+       DBG(dev, "---> %s()\n", __func__);
+
+       dev->done = &done;
+
+       /* free memory allocated in probe */
+       if (dev->dtd_pool)
+               dma_pool_destroy(dev->dtd_pool);
+
+       if (dev->status_req) {
+               kfree(dev->status_req->req.buf);
+               kfree(dev->status_req);
+       }
+
+       if (dev->ep_dqh)
+               dma_free_coherent(&pdev->dev, dev->ep_dqh_size,
+                       dev->ep_dqh, dev->ep_dqh_dma);
+
+       kfree(dev->ep);
+
+       /* diable IRQ handler */
+       if (dev->got_irq)
+               free_irq(pdev->irq, dev);
+
+#ifndef        OTG_TRANSCEIVER
+       if (dev->cap_regs)
+               iounmap(dev->cap_regs);
+
+       if (dev->region)
+               release_mem_region(pci_resource_start(pdev, 0),
+                               pci_resource_len(pdev, 0));
+
+       if (dev->enabled)
+               pci_disable_device(pdev);
+#else
+       if (dev->transceiver) {
+               otg_put_transceiver(dev->transceiver);
+               dev->transceiver = NULL;
+               dev->lotg = NULL;
+       }
+#endif
+
+       dev->cap_regs = NULL;
+
+       INFO(dev, "unbind\n");
+       DBG(dev, "<--- %s()\n", __func__);
+
+       device_unregister(&dev->gadget.dev);
+       device_remove_file(&pdev->dev, &dev_attr_langwell_udc);
+
+#ifndef        OTG_TRANSCEIVER
+       pci_set_drvdata(pdev, NULL);
+#endif
+
+       /* free dev, wait for the release() finished */
+       wait_for_completion(&done);
+
+       the_controller = NULL;
+}
+
+
+/*
+ * wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+static int langwell_udc_probe(struct pci_dev *pdev,
+               const struct pci_device_id *id)
+{
+       struct langwell_udc     *dev;
+#ifndef        OTG_TRANSCEIVER
+       unsigned long           resource, len;
+#endif
+       void                    __iomem *base = NULL;
+       size_t                  size;
+       int                     retval;
+
+       if (the_controller) {
+               dev_warn(&pdev->dev, "ignoring\n");
+               return -EBUSY;
+       }
+
+       /* alloc, and start init */
+       dev = kzalloc(sizeof *dev, GFP_KERNEL);
+       if (dev == NULL) {
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* initialize device spinlock */
+       spin_lock_init(&dev->lock);
+
+       dev->pdev = pdev;
+       DBG(dev, "---> %s()\n", __func__);
+
+#ifdef OTG_TRANSCEIVER
+       /* PCI device is already enabled by otg_transceiver driver */
+       dev->enabled = 1;
+
+       /* mem region and register base */
+       dev->region = 1;
+       dev->transceiver = otg_get_transceiver();
+       dev->lotg = otg_to_langwell(dev->transceiver);
+       base = dev->lotg->regs;
+#else
+       pci_set_drvdata(pdev, dev);
+
+       /* now all the pci goodies ... */
+       if (pci_enable_device(pdev) < 0) {
+               retval = -ENODEV;
+               goto error;
+       }
+       dev->enabled = 1;
+
+       /* control register: BAR 0 */
+       resource = pci_resource_start(pdev, 0);
+       len = pci_resource_len(pdev, 0);
+       if (!request_mem_region(resource, len, driver_name)) {
+               ERROR(dev, "controller already in use\n");
+               retval = -EBUSY;
+               goto error;
+       }
+       dev->region = 1;
+
+       base = ioremap_nocache(resource, len);
+#endif
+       if (base == NULL) {
+               ERROR(dev, "can't map memory\n");
+               retval = -EFAULT;
+               goto error;
+       }
+
+       dev->cap_regs = (struct langwell_cap_regs __iomem *) base;
+       VDBG(dev, "dev->cap_regs: %p\n", dev->cap_regs);
+       dev->op_regs = (struct langwell_op_regs __iomem *)
+               (base + OP_REG_OFFSET);
+       VDBG(dev, "dev->op_regs: %p\n", dev->op_regs);
+
+       /* irq setup after old hardware is cleaned up */
+       if (!pdev->irq) {
+               ERROR(dev, "No IRQ. Check PCI setup!\n");
+               retval = -ENODEV;
+               goto error;
+       }
+
+#ifndef        OTG_TRANSCEIVER
+       INFO(dev, "irq %d, io mem: 0x%08lx, len: 0x%08lx, pci mem 0x%p\n",
+                       pdev->irq, resource, len, base);
+       /* enables bus-mastering for device dev */
+       pci_set_master(pdev);
+
+       if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED,
+                               driver_name, dev) != 0) {
+               ERROR(dev, "request interrupt %d failed\n", pdev->irq);
+               retval = -EBUSY;
+               goto error;
+       }
+       dev->got_irq = 1;
+#endif
+
+       /* set stopped bit */
+       dev->stopped = 1;
+
+       /* capabilities and endpoint number */
+       dev->lpm = (readl(&dev->cap_regs->hccparams) & HCC_LEN) ? 1 : 0;
+       dev->dciversion = readw(&dev->cap_regs->dciversion);
+       dev->devcap = (readl(&dev->cap_regs->dccparams) & DEVCAP) ? 1 : 0;
+       VDBG(dev, "dev->lpm: %d\n", dev->lpm);
+       VDBG(dev, "dev->dciversion: 0x%04x\n", dev->dciversion);
+       VDBG(dev, "dccparams: 0x%08x\n", readl(&dev->cap_regs->dccparams));
+       VDBG(dev, "dev->devcap: %d\n", dev->devcap);
+       if (!dev->devcap) {
+               ERROR(dev, "can't support device mode\n");
+               retval = -ENODEV;
+               goto error;
+       }
+
+       /* a pair of endpoints (out/in) for each address */
+       dev->ep_max = DEN(readl(&dev->cap_regs->dccparams)) * 2;
+       VDBG(dev, "dev->ep_max: %d\n", dev->ep_max);
+
+       /* allocate endpoints memory */
+       dev->ep = kzalloc(sizeof(struct langwell_ep) * dev->ep_max,
+                       GFP_KERNEL);
+       if (!dev->ep) {
+               ERROR(dev, "allocate endpoints memory failed\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* allocate device dQH memory */
+       size = dev->ep_max * sizeof(struct langwell_dqh);
+       VDBG(dev, "orig size = %d\n", size);
+       if (size < DQH_ALIGNMENT)
+               size = DQH_ALIGNMENT;
+       else if ((size % DQH_ALIGNMENT) != 0) {
+               size += DQH_ALIGNMENT + 1;
+               size &= ~(DQH_ALIGNMENT - 1);
+       }
+       dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
+                                       &dev->ep_dqh_dma, GFP_KERNEL);
+       if (!dev->ep_dqh) {
+               ERROR(dev, "allocate dQH memory failed\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+       dev->ep_dqh_size = size;
+       VDBG(dev, "ep_dqh_size = %d\n", dev->ep_dqh_size);
+
+       /* initialize ep0 status request structure */
+       dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL);
+       if (!dev->status_req) {
+               ERROR(dev, "allocate status_req memory failed\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+       INIT_LIST_HEAD(&dev->status_req->queue);
+
+       /* allocate a small amount of memory to get valid address */
+       dev->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+       dev->status_req->req.dma = virt_to_phys(dev->status_req->req.buf);
+
+       dev->resume_state = USB_STATE_NOTATTACHED;
+       dev->usb_state = USB_STATE_POWERED;
+       dev->ep0_dir = USB_DIR_OUT;
+       dev->remote_wakeup = 0; /* default to 0 on reset */
+
+#ifndef        OTG_TRANSCEIVER
+       /* reset device controller */
+       langwell_udc_reset(dev);
+#endif
+
+       /* initialize gadget structure */
+       dev->gadget.ops = &langwell_ops;        /* usb_gadget_ops */
+       dev->gadget.ep0 = &dev->ep[0].ep;       /* gadget ep0 */
+       INIT_LIST_HEAD(&dev->gadget.ep_list);   /* ep_list */
+       dev->gadget.speed = USB_SPEED_UNKNOWN;  /* speed */
+       dev->gadget.is_dualspeed = 1;           /* support dual speed */
+#ifdef OTG_TRANSCEIVER
+       dev->gadget.is_otg = 1;                 /* support otg mode */
+#endif
+
+       /* 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;         /* gadget name */
+
+       /* controller endpoints reinit */
+       eps_reinit(dev);
+
+#ifndef        OTG_TRANSCEIVER
+       /* reset ep0 dQH and endptctrl */
+       ep0_reset(dev);
+#endif
+
+       /* create dTD dma_pool resource */
+       dev->dtd_pool = dma_pool_create("langwell_dtd",
+                       &dev->pdev->dev,
+                       sizeof(struct langwell_dtd),
+                       DTD_ALIGNMENT,
+                       DMA_BOUNDARY);
+
+       if (!dev->dtd_pool) {
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* done */
+       INFO(dev, "%s\n", driver_desc);
+       INFO(dev, "irq %d, pci mem %p\n", pdev->irq, base);
+       INFO(dev, "Driver version: " DRIVER_VERSION "\n");
+       INFO(dev, "Support (max) %d endpoints\n", dev->ep_max);
+       INFO(dev, "Device interface version: 0x%04x\n", dev->dciversion);
+       INFO(dev, "Controller mode: %s\n", dev->devcap ? "Device" : "Host");
+       INFO(dev, "Support USB LPM: %s\n", dev->lpm ? "Yes" : "No");
+
+       VDBG(dev, "After langwell_udc_probe(), print all registers:\n");
+#ifdef VERBOSE
+       print_all_registers(dev);
+#endif
+
+       the_controller = dev;
+
+       retval = device_register(&dev->gadget.dev);
+       if (retval)
+               goto error;
+
+       retval = device_create_file(&pdev->dev, &dev_attr_langwell_udc);
+       if (retval)
+               goto error;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+
+error:
+       if (dev) {
+               DBG(dev, "<--- %s()\n", __func__);
+               langwell_udc_remove(pdev);
+       }
+
+       return retval;
+}
+
+
+/* device controller suspend */
+static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct langwell_udc     *dev = the_controller;
+       u32                     devlc;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* disable interrupt and set controller to stop state */
+       langwell_udc_stop(dev);
+
+       /* diable IRQ handler */
+       if (dev->got_irq)
+               free_irq(pdev->irq, dev);
+       dev->got_irq = 0;
+
+
+       /* save PCI state */
+       pci_save_state(pdev);
+
+       /* set device power state */
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       /* enter PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc |= LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* device controller resume */
+static int langwell_udc_resume(struct pci_dev *pdev)
+{
+       struct langwell_udc     *dev = the_controller;
+       u32                     devlc;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       /* set device D0 power state */
+       pci_set_power_state(pdev, PCI_D0);
+
+       /* restore PCI state */
+       pci_restore_state(pdev);
+
+       /* enable IRQ handler */
+       if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, driver_name, dev)
+                       != 0) {
+               ERROR(dev, "request interrupt %d failed\n", pdev->irq);
+               return -1;
+       }
+       dev->got_irq = 1;
+
+       /* reset and start controller to run state */
+       if (dev->stopped) {
+               /* reset device controller */
+               langwell_udc_reset(dev);
+
+               /* reset ep0 dQH and endptctrl */
+               ep0_reset(dev);
+
+               /* start device if gadget is loaded */
+               if (dev->driver)
+                       langwell_udc_start(dev);
+       }
+
+       /* reset USB status */
+       dev->usb_state = USB_STATE_ATTACHED;
+       dev->ep0_state = WAIT_FOR_SETUP;
+       dev->ep0_dir = USB_DIR_OUT;
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* pci driver shutdown */
+static void langwell_udc_shutdown(struct pci_dev *pdev)
+{
+       struct langwell_udc     *dev = the_controller;
+       u32                     usbmode;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* reset controller mode to IDLE */
+       usbmode = readl(&dev->op_regs->usbmode);
+       DBG(dev, "usbmode = 0x%08x\n", usbmode);
+       usbmode &= (~3 | MODE_IDLE);
+       writel(usbmode, &dev->op_regs->usbmode);
+
+       DBG(dev, "<--- %s()\n", __func__);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids[] = { {
+       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class_mask =   ~0,
+       .vendor =       0x8086,
+       .device =       0x0811,
+       .subvendor =    PCI_ANY_ID,
+       .subdevice =    PCI_ANY_ID,
+}, { /* end: all zeroes */ }
+};
+
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+
+static struct pci_driver langwell_pci_driver = {
+       .name =         (char *) driver_name,
+       .id_table =     pci_ids,
+
+       .probe =        langwell_udc_probe,
+       .remove =       langwell_udc_remove,
+
+       /* device controller suspend/resume */
+       .suspend =      langwell_udc_suspend,
+       .resume =       langwell_udc_resume,
+
+       .shutdown =     langwell_udc_shutdown,
+};
+
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Xiaochen Shen <xiaochen.shen@xxxxxxxxx>");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+
+static int __init init(void)
+{
+#ifdef OTG_TRANSCEIVER
+       return langwell_register_peripheral(&langwell_pci_driver);
+#else
+       return pci_register_driver(&langwell_pci_driver);
+#endif
+}
+module_init(init);
+
+
+static void __exit cleanup(void)
+{
+#ifdef OTG_TRANSCEIVER
+       return langwell_unregister_peripheral(&langwell_pci_driver);
+#else
+       pci_unregister_driver(&langwell_pci_driver);
+#endif
+}
+module_exit(cleanup);
+
diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h
new file mode 100644
index 0000000..9719934
--- /dev/null
+++ b/drivers/usb/gadget/langwell_udc.h
@@ -0,0 +1,228 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/usb/langwell_udc.h>
+
+#if defined(CONFIG_USB_LANGWELL_OTG)
+#include <linux/usb/langwell_otg.h>
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+/* driver data structures and utilities */
+
+/*
+ * dTD: Device Endpoint Transfer Descriptor
+ * describe to the device controller the location and quantity of
+ * data to be send/received for given transfer
+ */
+struct langwell_dtd {
+       u32     dtd_next;
+/* bits 31:5, next transfer element pointer */
+#define        DTD_NEXT(d)     (((d)>>5)&0x7ffffff)
+#define        DTD_NEXT_MASK   (0x7ffffff << 5)
+/* terminate */
+#define        DTD_TERM        BIT(0)
+       /* bits 7:0, execution back states */
+       u32     dtd_status:8;
+#define        DTD_STATUS(d)   (((d)>>0)&0xff)
+#define        DTD_STS_ACTIVE  BIT(7)  /* active */
+#define        DTD_STS_HALTED  BIT(6)  /* halted */
+#define        DTD_STS_DBE     BIT(5)  /* data buffer error */
+#define        DTD_STS_TRE     BIT(3)  /* transaction error  */
+       /* bits 9:8 */
+       u32     dtd_res0:2;
+       /* bits 11:10, multipier override */
+       u32     dtd_multo:2;
+#define        DTD_MULTO       (BIT(11) | BIT(10))
+       /* bits 14:12 */
+       u32     dtd_res1:3;
+       /* bit 15, interrupt on complete */
+       u32     dtd_ioc:1;
+#define        DTD_IOC         BIT(15)
+       /* bits 30:16, total bytes */
+       u32     dtd_total:15;
+#define        DTD_TOTAL(d)    (((d)>>16)&0x7fff)
+#define        DTD_MAX_TRANSFER_LENGTH 0x4000
+       /* bit 31 */
+       u32     dtd_res2:1;
+       /* dTD buffer pointer page 0 to 4 */
+       u32     dtd_buf[5];
+#define        DTD_OFFSET_MASK 0xfff
+/* bits 31:12, buffer pointer */
+#define        DTD_BUFFER(d)   (((d)>>12)&0x3ff)
+/* bits 11:0, current offset */
+#define        DTD_C_OFFSET(d) (((d)>>0)&0xfff)
+/* bits 10:0, frame number */
+#define        DTD_FRAME(d)    (((d)>>0)&0x7ff)
+
+       /* driver-private parts */
+
+       /* dtd dma address */
+       dma_addr_t              dtd_dma;
+       /* next dtd virtual address */
+       struct langwell_dtd     *next_dtd_virt;
+};
+
+
+/*
+ * dQH: Device Endpoint Queue Head
+ * describe where all transfers are managed
+ * 48-byte data structure, aligned on 64-byte boundary
+ *
+ * These are associated with dTD structure
+ */
+struct langwell_dqh {
+       /* endpoint capabilities and characteristics */
+       u32     dqh_res0:15;    /* bits 14:0 */
+       u32     dqh_ios:1;      /* bit 15, interrupt on setup */
+#define        DQH_IOS         BIT(15)
+       u32     dqh_mpl:11;     /* bits 26:16, maximum packet length */
+#define        DQH_MPL         (0x7ff << 16)
+       u32     dqh_res1:2;     /* bits 28:27 */
+       u32     dqh_zlt:1;      /* bit 29, zero length termination */
+#define        DQH_ZLT         BIT(29)
+       u32     dqh_mult:2;     /* bits 31:30 */
+#define        DQH_MULT        (BIT(30) | BIT(31))
+
+       /* current dTD pointer */
+       u32     dqh_current;    /* locate the transfer in progress */
+#define DQH_C_DTD(e)   \
+       (((e)>>5)&0x7ffffff)    /* bits 31:5, current dTD pointer */
+
+       /* transfer overlay, hardware parts of a struct langwell_dtd */
+       u32     dtd_next;
+       u32     dtd_status:8;   /* bits 7:0, execution back states */
+       u32     dtd_res0:2;     /* bits 9:8 */
+       u32     dtd_multo:2;    /* bits 11:10, multipier override */
+       u32     dtd_res1:3;     /* bits 14:12 */
+       u32     dtd_ioc:1;      /* bit 15, interrupt on complete */
+       u32     dtd_total:15;   /* bits 30:16, total bytes */
+       u32     dtd_res2:1;     /* bit 31 */
+       u32     dtd_buf[5];     /* dTD buffer pointer page 0 to 4 */
+
+       u32     dqh_res2;
+       struct usb_ctrlrequest  dqh_setup;      /* setup packet buffer */
+} __attribute__ ((aligned(64)));
+
+
+/* endpoint data structure */
+struct langwell_ep {
+       struct usb_ep           ep;
+       dma_addr_t              dma;
+       struct langwell_udc     *dev;
+       unsigned long           irqs;
+       struct list_head        queue;
+       struct langwell_dqh     *dqh;
+       const struct usb_endpoint_descriptor    *desc;
+       char                    name[14];
+       unsigned                stopped:1,
+                               ep_type:2,
+                               ep_num:8;
+};
+
+
+/* request data structure */
+struct langwell_request {
+       struct usb_request      req;
+       struct langwell_dtd     *dtd, *head, *tail;
+       struct langwell_ep      *ep;
+       dma_addr_t              dtd_dma;
+       struct list_head        queue;
+       unsigned                dtd_count;
+       unsigned                mapped:1;
+};
+
+
+/* ep0 transfer state */
+enum ep0_state {
+       WAIT_FOR_SETUP,
+       DATA_STATE_XMIT,
+       DATA_STATE_NEED_ZLP,
+       WAIT_FOR_OUT_STATUS,
+       DATA_STATE_RECV,
+};
+
+
+/* device suspend state */
+enum lpm_state {
+       LPM_L0, /* on */
+       LPM_L1, /* LPM L1 sleep */
+       LPM_L2, /* suspend */
+       LPM_L3, /* off */
+};
+
+
+/* device data structure */
+struct langwell_udc {
+       /* each pci device provides one gadget, several endpoints */
+       struct usb_gadget       gadget;
+       spinlock_t              lock;   /* device lock */
+       struct langwell_ep      *ep;
+       struct usb_gadget_driver        *driver;
+       struct otg_transceiver  *transceiver;
+       u8                      dev_addr;
+       u32                     usb_state;
+       u32                     resume_state;
+       u32                     bus_reset;
+       enum lpm_state          lpm_state;
+       enum ep0_state          ep0_state;
+       u32                     ep0_dir;
+       u16                     dciversion;
+       unsigned                ep_max;
+       unsigned                devcap:1,
+                               enabled:1,
+                               region:1,
+                               got_irq:1,
+                               powered:1,
+                               remote_wakeup:1,
+                               rate:1,
+                               is_reset:1,
+                               softconnected:1,
+                               vbus_active:1,
+                               suspended:1,
+                               stopped:1,
+                               lpm:1;  /* LPM capability */
+
+       /* pci state used to access those endpoints */
+       struct pci_dev          *pdev;
+
+       /* Langwell otg transceiver */
+       struct langwell_otg     *lotg;
+
+       /* control registers */
+       struct langwell_cap_regs        __iomem *cap_regs;
+       struct langwell_op_regs         __iomem *op_regs;
+
+       struct usb_ctrlrequest  local_setup_buff;
+       struct langwell_dqh     *ep_dqh;
+       size_t                  ep_dqh_size;
+       dma_addr_t              ep_dqh_dma;
+
+       /* ep0 status request */
+       struct langwell_request *status_req;
+
+       /* dma pool */
+       struct dma_pool         *dtd_pool;
+
+       /* make sure release() is done */
+       struct completion       *done;
+};
+
diff --git a/include/linux/usb/langwell_udc.h b/include/linux/usb/langwell_udc.h
new file mode 100644
index 0000000..c949178
--- /dev/null
+++ b/include/linux/usb/langwell_udc.h
@@ -0,0 +1,310 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef __LANGWELL_UDC_H
+#define __LANGWELL_UDC_H
+
+
+/* MACRO defines */
+#define        CAP_REG_OFFSET          0x0
+#define        OP_REG_OFFSET           0x28
+
+#define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+#define        DQH_ALIGNMENT           2048
+#define        DTD_ALIGNMENT           64
+#define        DMA_BOUNDARY            4096
+
+#define        EP0_MAX_PKT_SIZE        64
+#define EP_DIR_IN              1
+#define EP_DIR_OUT             0
+
+#define FLUSH_TIMEOUT          1000
+#define RESET_TIMEOUT          1000
+#define SETUPSTAT_TIMEOUT      100
+#define PRIME_TIMEOUT          100
+
+
+/* device memory space registers */
+
+/* Capability Registers, BAR0 + CAP_REG_OFFSET */
+struct langwell_cap_regs {
+       /* offset: 0x0 */
+       u8      caplength;      /* offset of Operational Register */
+       u8      _reserved3;
+       u16     hciversion;     /* H: BCD encoding of host version */
+       u32     hcsparams;      /* H: host port steering logic capability */
+       u32     hccparams;      /* H: host multiple mode control capability */
+#define        HCC_LEN BIT(17)         /* Link power management (LPM) capability */
+       u8      _reserved4[0x20-0xc];
+       /* offset: 0x20 */
+       u16     dciversion;     /* BCD encoding of device version */
+       u8      _reserved5[0x24-0x22];
+       u32     dccparams;      /* overall device controller capability */
+#define        HOSTCAP BIT(8)          /* host capable */
+#define        DEVCAP  BIT(7)          /* device capable */
+#define DEN(d) \
+       (((d)>>0)&0x1f)         /* bits 4:0, device endpoint number */
+} __attribute__ ((packed));
+
+
+/* Operational Registers, BAR0 + OP_REG_OFFSET */
+struct langwell_op_regs {
+       /* offset: 0x28 */
+       u32     extsts;
+#define        EXTS_TI1        BIT(4)  /* general purpose timer interrupt 1 */
+#define        EXTS_TI1TI0     BIT(3)  /* general purpose timer interrupt 0 */
+#define        EXTS_TI1UPI     BIT(2)  /* USB host periodic interrupt */
+#define        EXTS_TI1UAI     BIT(1)  /* USB host asynchronous interrupt */
+#define        EXTS_TI1NAKI    BIT(0)  /* NAK interrupt */
+       u32     extintr;
+#define        EXTI_TIE1       BIT(4)  /* general purpose timer interrupt enable 1 */
+#define        EXTI_TIE0       BIT(3)  /* general purpose timer interrupt enable 0 */
+#define        EXTI_UPIE       BIT(2)  /* USB host periodic interrupt enable */
+#define        EXTI_UAIE       BIT(1)  /* USB host asynchronous interrupt enable */
+#define        EXTI_NAKE       BIT(0)  /* NAK interrupt enable */
+       /* offset: 0x30 */
+       u32     usbcmd;
+#define        CMD_HIRD(u)     \
+       (((u)>>24)&0xf)         /* bits 27:24, host init resume duration */
+#define        CMD_ITC(u)      \
+       (((u)>>16)&0xff)        /* bits 23:16, interrupt threshold control */
+#define        CMD_PPE         BIT(15) /* per-port change events enable */
+#define        CMD_ATDTW       BIT(14) /* add dTD tripwire */
+#define        CMD_SUTW        BIT(13) /* setup tripwire */
+#define        CMD_ASPE        BIT(11) /* asynchronous schedule park mode enable */
+#define        CMD_FS2         BIT(10) /* frame list size */
+#define        CMD_ASP1        BIT(9)  /* asynchronous schedule park mode count */
+#define        CMD_ASP0        BIT(8)
+#define        CMD_LR          BIT(7)  /* light host/device controller reset */
+#define        CMD_IAA         BIT(6)  /* interrupt on async advance doorbell */
+#define        CMD_ASE         BIT(5)  /* asynchronous schedule enable */
+#define        CMD_PSE         BIT(4)  /* periodic schedule enable */
+#define        CMD_FS1         BIT(3)
+#define        CMD_FS0         BIT(2)
+#define        CMD_RST         BIT(1)  /* controller reset */
+#define        CMD_RUNSTOP     BIT(0)  /* run/stop */
+       u32     usbsts;
+#define        STS_PPCI(u)     \
+       (((u)>>16)&0xffff)      /* bits 31:16, port-n change detect */
+#define        STS_AS          BIT(15) /* asynchronous schedule status */
+#define        STS_PS          BIT(14) /* periodic schedule status */
+#define        STS_RCL         BIT(13) /* reclamation */
+#define        STS_HCH         BIT(12) /* HC halted */
+#define        STS_ULPII       BIT(10) /* ULPI interrupt */
+#define        STS_SLI         BIT(8)  /* DC suspend */
+#define        STS_SRI         BIT(7)  /* SOF received */
+#define        STS_URI         BIT(6)  /* USB reset received */
+#define        STS_AAI         BIT(5)  /* interrupt on async advance */
+#define        STS_SEI         BIT(4)  /* system error */
+#define        STS_FRI         BIT(3)  /* frame list rollover */
+#define        STS_PCI         BIT(2)  /* port change detect */
+#define        STS_UEI         BIT(1)  /* USB error interrupt */
+#define        STS_UI          BIT(0)  /* USB interrupt */
+       u32     usbintr;
+/* bits 31:16, per-port interrupt enable */
+#define        INTR_PPCE(u)    (((u)>>16)&0xffff)
+#define        INTR_ULPIE      BIT(10) /* ULPI enable */
+#define        INTR_SLE        BIT(8)  /* DC sleep/suspend enable */
+#define        INTR_SRE        BIT(7)  /* SOF received enable */
+#define        INTR_URE        BIT(6)  /* USB reset enable */
+#define        INTR_AAE        BIT(5)  /* interrupt on async advance enable */
+#define        INTR_SEE        BIT(4)  /* system error enable */
+#define        INTR_FRE        BIT(3)  /* frame list rollover enable */
+#define        INTR_PCE        BIT(2)  /* port change detect enable */
+#define        INTR_UEE        BIT(1)  /* USB error interrupt enable */
+#define        INTR_UE         BIT(0)  /* USB interrupt enable */
+       u32     frindex;        /* frame index */
+#define        FRINDEX_MASK    (0x3fff << 0)
+       u32     ctrldssegment;  /* not used */
+       u32     deviceaddr;
+#define USBADR_SHIFT   25
+#define        USBADR(d)       \
+       (((d)>>25)&0x7f)        /* bits 31:25, device address */
+#define USBADR_MASK    (0x7f << 25)
+#define        USBADRA         BIT(24) /* device address advance */
+       u32     endpointlistaddr;/* endpoint list top memory address */
+/* bits 31:11, endpoint list pointer */
+#define        EPBASE(d)       (((d)>>11)&0x1fffff)
+#define        ENDPOINTLISTADDR_MASK   (0x1fffff << 11)
+       u32     ttctrl;         /* H: TT operatin, not used */
+       /* offset: 0x50 */
+       u32     burstsize;      /* burst size of data movement */
+#define        TXPBURST(b)     \
+       (((b)>>8)&0xff)         /* bits 15:8, TX burst length */
+#define        RXPBURST(b)     \
+       (((b)>>0)&0xff)         /* bits 7:0, RX burst length */
+       u32     txfilltuning;   /* TX tuning */
+       u32     txttfilltuning; /* H: TX TT tuning */
+       u32     ic_usb;         /* control the IC_USB FS/LS transceiver */
+       /* offset: 0x60 */
+       u32     ulpi_viewport;  /* indirect access to ULPI PHY */
+#define        ULPIWU          BIT(31) /* ULPI wakeup */
+#define        ULPIRUN         BIT(30) /* ULPI read/write run */
+#define        ULPIRW          BIT(29) /* ULPI read/write control */
+#define        ULPISS          BIT(27) /* ULPI sync state */
+#define        ULPIPORT(u)     \
+       (((u)>>24)&7)           /* bits 26:24, ULPI port number */
+#define        ULPIADDR(u)     \
+       (((u)>>16)&0xff)        /* bits 23:16, ULPI data address */
+#define        ULPIDATRD(u)    \
+       (((u)>>8)&0xff)         /* bits 15:8, ULPI data read */
+#define        ULPIDATWR(u)    \
+       (((u)>>0)&0xff)         /* bits 7:0, ULPI date write */
+       u8      _reserved6[0x70-0x64];
+       /* offset: 0x70 */
+       u32     configflag;     /* H: not used */
+       u32     portsc1;        /* port status */
+#define        DA(p)   \
+       (((p)>>25)&0x7f)        /* bits 31:25, device address */
+#define        PORTS_SSTS      (BIT(24) | BIT(23))     /* suspend status */
+#define        PORTS_WKOC      BIT(22) /* wake on over-current enable */
+#define        PORTS_WKDS      BIT(21) /* wake on disconnect enable */
+#define        PORTS_WKCN      BIT(20) /* wake on connect enable */
+#define        PORTS_PTC(p)    (((p)>>16)&0xf) /* bits 19:16, port test control */
+#define        PORTS_PIC       (BIT(15) | BIT(14))     /* port indicator control */
+#define        PORTS_PO        BIT(13) /* port owner */
+#define        PORTS_PP        BIT(12) /* port power */
+#define        PORTS_LS        (BIT(11) | BIT(10))     /* line status */
+#define        PORTS_SLP       BIT(9)  /* suspend using L1 */
+#define        PORTS_PR        BIT(8)  /* port reset */
+#define        PORTS_SUSP      BIT(7)  /* suspend */
+#define        PORTS_FPR       BIT(6)  /* force port resume */
+#define        PORTS_OCC       BIT(5)  /* over-current change */
+#define        PORTS_OCA       BIT(4)  /* over-current active */
+#define        PORTS_PEC       BIT(3)  /* port enable/disable change */
+#define        PORTS_PE        BIT(2)  /* port enable/disable */
+#define        PORTS_CSC       BIT(1)  /* connect status change */
+#define        PORTS_CCS       BIT(0)  /* current connect status */
+       u8      _reserved7[0xb4-0x78];
+       /* offset: 0xb4 */
+       u32     devlc;          /* control LPM and each USB port behavior */
+/* bits 31:29, parallel transceiver select */
+#define        LPM_PTS(d)      (((d)>>29)&7)
+#define        LPM_STS         BIT(28) /* serial transceiver select */
+#define        LPM_PTW         BIT(27) /* parallel transceiver width */
+#define        LPM_PSPD(d)     (((d)>>25)&3)   /* bits 26:25, port speed */
+#define LPM_PSPD_MASK  (BIT(26) | BIT(25))
+#define LPM_SPEED_FULL 0
+#define LPM_SPEED_LOW  1
+#define LPM_SPEED_HIGH 2
+#define        LPM_SRT         BIT(24) /* shorten reset time */
+#define        LPM_PFSC        BIT(23) /* port force full speed connect */
+#define        LPM_PHCD        BIT(22) /* PHY low power suspend clock disable */
+#define        LPM_STL         BIT(16) /* STALL reply to LPM token */
+#define        LPM_BA(d)       \
+       (((d)>>1)&0x7ff)        /* bits 11:1, BmAttributes */
+#define        LPM_NYT_ACK     BIT(0)  /* NYET/ACK reply to LPM token */
+       u8      _reserved8[0xf4-0xb8];
+       /* offset: 0xf4 */
+       u32     otgsc;          /* On-The-Go status and control */
+#define        OTGSC_DPIE      BIT(30) /* data pulse interrupt enable */
+#define        OTGSC_MSE       BIT(29) /* 1 ms timer interrupt enable */
+#define        OTGSC_BSEIE     BIT(28) /* B session end interrupt enable */
+#define        OTGSC_BSVIE     BIT(27) /* B session valid interrupt enable */
+#define        OTGSC_ASVIE     BIT(26) /* A session valid interrupt enable */
+#define        OTGSC_AVVIE     BIT(25) /* A VBUS valid interrupt enable */
+#define        OTGSC_IDIE      BIT(24) /* USB ID interrupt enable */
+#define        OTGSC_DPIS      BIT(22) /* data pulse interrupt status */
+#define        OTGSC_MSS       BIT(21) /* 1 ms timer interrupt status */
+#define        OTGSC_BSEIS     BIT(20) /* B session end interrupt status */
+#define        OTGSC_BSVIS     BIT(19) /* B session valid interrupt status */
+#define        OTGSC_ASVIS     BIT(18) /* A session valid interrupt status */
+#define        OTGSC_AVVIS     BIT(17) /* A VBUS valid interrupt status */
+#define        OTGSC_IDIS      BIT(16) /* USB ID interrupt status */
+#define        OTGSC_DPS       BIT(14) /* data bus pulsing status */
+#define        OTGSC_MST       BIT(13) /* 1 ms timer toggle */
+#define        OTGSC_BSE       BIT(12) /* B session end */
+#define        OTGSC_BSV       BIT(11) /* B session valid */
+#define        OTGSC_ASV       BIT(10) /* A session valid */
+#define        OTGSC_AVV       BIT(9)  /* A VBUS valid */
+#define        OTGSC_USBID     BIT(8)  /* USB ID */
+#define        OTGSC_HABA      BIT(7)  /* hw assist B-disconnect to A-connect */
+#define        OTGSC_HADP      BIT(6)  /* hw assist data pulse */
+#define        OTGSC_IDPU      BIT(5)  /* ID pullup */
+#define        OTGSC_DP        BIT(4)  /* data pulsing */
+#define        OTGSC_OT        BIT(3)  /* OTG termination */
+#define        OTGSC_HAAR      BIT(2)  /* hw assist auto reset */
+#define        OTGSC_VC        BIT(1)  /* VBUS charge */
+#define        OTGSC_VD        BIT(0)  /* VBUS discharge */
+       u32     usbmode;
+#define        MODE_VBPS       BIT(5)  /* R/W VBUS power select */
+#define        MODE_SDIS       BIT(4)  /* R/W stream disable mode */
+#define        MODE_SLOM       BIT(3)  /* R/W setup lockout mode */
+#define        MODE_ENSE       BIT(2)  /* endian select */
+#define        MODE_CM(u)      (((u)>>0)&3)    /* bits 1:0, controller mode */
+#define        MODE_IDLE       0
+#define        MODE_DEVICE     2
+#define        MODE_HOST       3
+       u8      _reserved9[0x100-0xfc];
+       /* offset: 0x100 */
+       u32     endptnak;
+#define        EPTN(e)         \
+       (((e)>>16)&0xffff)      /* bits 31:16, TX endpoint NAK */
+#define        EPRN(e)         \
+       (((e)>>0)&0xffff)       /* bits 15:0, RX endpoint NAK */
+       u32     endptnaken;
+#define        EPTNE(e)        \
+       (((e)>>16)&0xffff)      /* bits 31:16, TX endpoint NAK enable */
+#define        EPRNE(e)        \
+       (((e)>>0)&0xffff)       /* bits 15:0, RX endpoint NAK enable */
+       u32     endptsetupstat;
+#define        SETUPSTAT_MASK          (0xffff << 0)   /* bits 15:0 */
+#define EP0SETUPSTAT_MASK      1
+       u32     endptprime;
+/* bits 31:16, prime endpoint transmit buffer */
+#define        PETB(e)         (((e)>>16)&0xffff)
+/* bits 15:0, prime endpoint receive buffer */
+#define        PERB(e)         (((e)>>0)&0xffff)
+       /* offset: 0x110 */
+       u32     endptflush;
+/* bits 31:16, flush endpoint transmit buffer */
+#define        FETB(e)         (((e)>>16)&0xffff)
+/* bits 15:0, flush endpoint receive buffer */
+#define        FERB(e)         (((e)>>0)&0xffff)
+       u32     endptstat;
+/* bits 31:16, endpoint transmit buffer ready */
+#define        ETBR(e)         (((e)>>16)&0xffff)
+/* bits 15:0, endpoint receive buffer ready */
+#define        ERBR(e)         (((e)>>0)&0xffff)
+       u32     endptcomplete;
+/* bits 31:16, endpoint transmit complete event */
+#define        ETCE(e)         (((e)>>16)&0xffff)
+/* bits 15:0, endpoint receive complete event */
+#define        ERCE(e)         (((e)>>0)&0xffff)
+       /* offset: 0x11c */
+       u32     endptctrl[16];
+#define        EPCTRL_TXE      BIT(23) /* TX endpoint enable */
+#define        EPCTRL_TXR      BIT(22) /* TX data toggle reset */
+#define        EPCTRL_TXI      BIT(21) /* TX data toggle inhibit */
+#define        EPCTRL_TXT(e)   (((e)>>18)&3)   /* bits 19:18, TX endpoint type */
+#define        EPCTRL_TXT_SHIFT        18
+#define        EPCTRL_TXD      BIT(17) /* TX endpoint data source */
+#define        EPCTRL_TXS      BIT(16) /* TX endpoint STALL */
+#define        EPCTRL_RXE      BIT(7)  /* RX endpoint enable */
+#define        EPCTRL_RXR      BIT(6)  /* RX data toggle reset */
+#define        EPCTRL_RXI      BIT(5)  /* RX data toggle inhibit */
+#define        EPCTRL_RXT(e)   (((e)>>2)&3)    /* bits 3:2, RX endpoint type */
+#define        EPCTRL_RXT_SHIFT        2       /* bits 19:18, TX endpoint type */
+#define        EPCTRL_RXD      BIT(1)  /* RX endpoint data sink */
+#define        EPCTRL_RXS      BIT(0)  /* RX endpoint STALL */
+} __attribute__ ((packed));
+
+#endif /* __LANGWELL_UDC_H */
+
--
1.5.2.2
--
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