aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-06-04 11:09:45 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-04 11:09:45 -0700
commit8fb27ba3e8bedc65d94dad70455e507043faf6b1 (patch)
tree7ad1eccaa1421ccb54a1133373fd1bfa3fd66e4d /usb
parenta071bc3b65bcad3b691b868652930b4cadb09d15 (diff)
downloadpatches-8fb27ba3e8bedc65d94dad70455e507043faf6b1.tar.gz
2 usb drivers and some staging stuff
Diffstat (limited to 'usb')
-rw-r--r--usb/usb-add-intel-langwell-usb-device-controller-driver.patch4047
-rw-r--r--usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch2173
2 files changed, 6220 insertions, 0 deletions
diff --git a/usb/usb-add-intel-langwell-usb-device-controller-driver.patch b/usb/usb-add-intel-langwell-usb-device-controller-driver.patch
new file mode 100644
index 00000000000000..5ce6a79ac7f08e
--- /dev/null
+++ b/usb/usb-add-intel-langwell-usb-device-controller-driver.patch
@@ -0,0 +1,4047 @@
+From xiaochen.shen@intel.com Thu Jun 4 10:58:51 2009
+From: Xiaochen Shen <xiaochen.shen@intel.com>
+Date: Thu, 4 Jun 2009 15:34:49 +0800
+Subject: USB: Add Intel Langwell USB Device Controller driver
+Message-ID: <20090604073449.GA24218@intel.com>
+
+
+From: Xiaochen Shen <xiaochen.shen@intel.com>
+
+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@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ 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(+)
+
+--- 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_
+ return 0x22;
+ else if (gadget_is_ci13xxx(gadget))
+ return 0x23;
++ else if (gadget_is_langwell(gadget))
++ return 0x24;
+ return -ENOENT;
+ }
+
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -474,6 +474,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
+--- /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@intel.com>");
++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);
++
+--- /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;
++};
++
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -26,6 +26,7 @@ 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_S3C_HSOTG) += s3c-hsotg.o
++obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
+
+ #
+ # USB gadget drivers
+--- /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 */
++
diff --git a/usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch b/usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch
new file mode 100644
index 00000000000000..c2e01521cd8be4
--- /dev/null
+++ b/usb/usb-add-intel-langwell-usb-otg-transceiver-drive.patch
@@ -0,0 +1,2173 @@
+From hao.wu@intel.com Thu Jun 4 11:01:04 2009
+From: Hao Wu <hao.wu@intel.com>
+Date: Thu, 4 Jun 2009 16:06:50 +0800
+Subject: USB: Add Intel Langwell USB OTG Transceiver Drive
+Message-ID: <20090604080650.GA15482@intel.com>
+
+From: Hao Wu <hao.wu@intel.com>
+
+Description:
+This driver is used for Intel Langwell* USB OTG controller in Intel
+Moorestown* platform. It tries to implement host/device role switch
+according to OTG spec. The actual hsot and device functions are
+accomplished in modified EHCI driver and Intel Langwell USB OTG client
+controller driver.
+
+* Langwell and Moorestown are names used in development. They are not
+ approved official name.
+
+Note:
+This patch is the first version Intel Langwell USB OTG Transceiver
+driver. The development is not finished, and the bug fixing is on going
+for some hardware and software issues. The main purpose of this
+submission is for code view.
+
+Supported features:
+- Data-line Pulsing SRP
+- Support HNP to switch roles
+- PCI D0/D3 power management support
+
+Known issues:
+- HNP is only tested with another Moorestown platform.
+- PCI D0/D3 power management support is not fully tested.
+- VBus Pulsing SRP is not support in current version.
+
+Signed-off-by: Hao Wu <hao.wu@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/otg/Kconfig | 14
+ drivers/usb/otg/Makefile | 1
+ drivers/usb/otg/langwell_otg.c | 1915 +++++++++++++++++++++++++++++++++++++++
+ include/linux/usb/langwell_otg.h | 177 +++
+ 4 files changed, 2107 insertions(+)
+
+--- a/drivers/usb/otg/Kconfig
++++ b/drivers/usb/otg/Kconfig
+@@ -59,4 +59,18 @@ config NOP_USB_XCEIV
+ built-in with usb ip or which are autonomous and doesn't require any
+ phy programming such as ISP1x04 etc.
+
++config USB_LANGWELL_OTG
++ tristate "Intel Langwell USB OTG dual-role support"
++ depends on USB && MRST
++ select USB_OTG
++ select USB_OTG_UTILS
++ help
++ Say Y here if you want to build Intel Langwell USB OTG
++ transciever driver in kernel. This driver implements role
++ switch between EHCI host driver and Langwell USB OTG
++ client driver.
++
++ To compile this driver as a module, choose M here: the
++ module will be called langwell_otg.
++
+ endif # USB || OTG
+--- /dev/null
++++ b/drivers/usb/otg/langwell_otg.c
+@@ -0,0 +1,1915 @@
++/*
++ * Intel Langwell USB OTG transceiver 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.
++ *
++ */
++/* This driver helps to switch Langwell OTG controller function between host
++ * and peripheral. It works with EHCI driver and Langwell client controller
++ * driver together.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/moduleparam.h>
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++#include <linux/usb.h>
++#include <linux/usb/otg.h>
++#include <linux/notifier.h>
++#include <asm/ipc_defs.h>
++#include <linux/delay.h>
++#include "../core/hcd.h"
++
++#include <linux/usb/langwell_otg.h>
++
++#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver"
++#define DRIVER_VERSION "3.0.0.32L.0002"
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>");
++MODULE_VERSION(DRIVER_VERSION);
++MODULE_LICENSE("GPL");
++
++static const char driver_name[] = "langwell_otg";
++
++static int langwell_otg_probe(struct pci_dev *pdev,
++ const struct pci_device_id *id);
++static void langwell_otg_remove(struct pci_dev *pdev);
++static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message);
++static int langwell_otg_resume(struct pci_dev *pdev);
++
++static int langwell_otg_set_host(struct otg_transceiver *otg,
++ struct usb_bus *host);
++static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
++ struct usb_gadget *gadget);
++static int langwell_otg_start_srp(struct otg_transceiver *otg);
++
++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 */ }
++};
++
++static struct pci_driver otg_pci_driver = {
++ .name = (char *) driver_name,
++ .id_table = pci_ids,
++
++ .probe = langwell_otg_probe,
++ .remove = langwell_otg_remove,
++
++ .suspend = langwell_otg_suspend,
++ .resume = langwell_otg_resume,
++};
++
++static const char *state_string(enum usb_otg_state state)
++{
++ switch (state) {
++ case OTG_STATE_A_IDLE:
++ return "a_idle";
++ case OTG_STATE_A_WAIT_VRISE:
++ return "a_wait_vrise";
++ case OTG_STATE_A_WAIT_BCON:
++ return "a_wait_bcon";
++ case OTG_STATE_A_HOST:
++ return "a_host";
++ case OTG_STATE_A_SUSPEND:
++ return "a_suspend";
++ case OTG_STATE_A_PERIPHERAL:
++ return "a_peripheral";
++ case OTG_STATE_A_WAIT_VFALL:
++ return "a_wait_vfall";
++ case OTG_STATE_A_VBUS_ERR:
++ return "a_vbus_err";
++ case OTG_STATE_B_IDLE:
++ return "b_idle";
++ case OTG_STATE_B_SRP_INIT:
++ return "b_srp_init";
++ case OTG_STATE_B_PERIPHERAL:
++ return "b_peripheral";
++ case OTG_STATE_B_WAIT_ACON:
++ return "b_wait_acon";
++ case OTG_STATE_B_HOST:
++ return "b_host";
++ default:
++ return "UNDEFINED";
++ }
++}
++
++/* HSM timers */
++static inline struct langwell_otg_timer *otg_timer_initializer
++(void (*function)(unsigned long), unsigned long expires, unsigned long data)
++{
++ struct langwell_otg_timer *timer;
++ timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL);
++ timer->function = function;
++ timer->expires = expires;
++ timer->data = data;
++ return timer;
++}
++
++static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr,
++ *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr,
++ *b_bus_suspend_tmr;
++
++static struct list_head active_timers;
++
++static struct langwell_otg *the_transceiver;
++
++/* host/client notify transceiver when event affects HNP state */
++void langwell_update_transceiver()
++{
++ otg_dbg("transceiver driver is notified\n");
++ queue_work(the_transceiver->qwork, &the_transceiver->work);
++}
++EXPORT_SYMBOL(langwell_update_transceiver);
++
++static int langwell_otg_set_host(struct otg_transceiver *otg,
++ struct usb_bus *host)
++{
++ otg->host = host;
++
++ return 0;
++}
++
++static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
++ struct usb_gadget *gadget)
++{
++ otg->gadget = gadget;
++
++ return 0;
++}
++
++static int langwell_otg_set_power(struct otg_transceiver *otg,
++ unsigned mA)
++{
++ return 0;
++}
++
++/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/
++static void langwell_otg_drv_vbus(int on)
++{
++ struct ipc_pmic_reg_data pmic_data = {0};
++ struct ipc_pmic_reg_data battery_data;
++
++ /* Check if battery is attached or not */
++ battery_data.pmic_reg_data[0].register_address = 0xd2;
++ battery_data.ioc = 0;
++ battery_data.num_entries = 1;
++ if (ipc_pmic_register_read(&battery_data)) {
++ otg_dbg("Failed to read PMIC register 0xd2.\n");
++ return;
++ }
++
++ if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) {
++ otg_dbg("no battery attached\n");
++ return;
++ }
++
++ /* Workaround for battery attachment issue */
++ if (battery_data.pmic_reg_data[0].value == 0x34) {
++ otg_dbg("battery \n");
++ return;
++ }
++
++ otg_dbg("battery attached\n");
++
++ pmic_data.ioc = 0;
++ pmic_data.pmic_reg_data[0].register_address = 0xD4;
++ pmic_data.num_entries = 1;
++ if (on)
++ pmic_data.pmic_reg_data[0].value = 0x20;
++ else
++ pmic_data.pmic_reg_data[0].value = 0xc0;
++
++ if (ipc_pmic_register_write(&pmic_data, TRUE))
++ otg_dbg("Failed to write PMIC.\n");
++
++}
++
++/* charge vbus or discharge vbus through a resistor to ground */
++static void langwell_otg_chrg_vbus(int on)
++{
++
++ u32 val;
++
++ val = readl(the_transceiver->regs + CI_OTGSC);
++
++ if (on)
++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC,
++ the_transceiver->regs + CI_OTGSC);
++ else
++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD,
++ the_transceiver->regs + CI_OTGSC);
++
++}
++
++/* Start SRP */
++static int langwell_otg_start_srp(struct otg_transceiver *otg)
++{
++ u32 val;
++
++ otg_dbg("Start SRP ->\n");
++
++ val = readl(the_transceiver->regs + CI_OTGSC);
++
++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP,
++ the_transceiver->regs + CI_OTGSC);
++
++ /* Check if the data plus is finished or not */
++ msleep(8);
++ val = readl(the_transceiver->regs + CI_OTGSC);
++ if (val & (OTGSC_HADP | OTGSC_DP))
++ otg_dbg("DataLine SRP Error\n");
++
++ /* FIXME: VBus SRP */
++
++ return 0;
++}
++
++
++/* stop SOF via bus_suspend */
++static void langwell_otg_loc_sof(int on)
++{
++ struct usb_hcd *hcd;
++ int err;
++
++ otg_dbg("loc_sof -> %d\n", on);
++
++ hcd = bus_to_hcd(the_transceiver->otg.host);
++ if (on)
++ err = hcd->driver->bus_resume(hcd);
++ else
++ err = hcd->driver->bus_suspend(hcd);
++
++ if (err)
++ otg_dbg("Failed to resume/suspend bus - %d\n", err);
++}
++
++static void langwell_otg_phy_low_power(int on)
++{
++ u32 val;
++
++ otg_dbg("phy low power mode-> %d\n", on);
++
++ val = readl(the_transceiver->regs + CI_HOSTPC1);
++ if (on)
++ writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1);
++ else
++ writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1);
++}
++
++/* Enable/Disable OTG interrupt */
++static void langwell_otg_intr(int on)
++{
++ u32 val;
++
++ otg_dbg("interrupt -> %d\n", on);
++
++ val = readl(the_transceiver->regs + CI_OTGSC);
++ if (on) {
++ val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU);
++ writel(val, the_transceiver->regs + CI_OTGSC);
++ } else {
++ val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU);
++ writel(val, the_transceiver->regs + CI_OTGSC);
++ }
++}
++
++/* set HAAR: Hardware Assist Auto-Reset */
++static void langwell_otg_HAAR(int on)
++{
++ u32 val;
++
++ otg_dbg("HAAR -> %d\n", on);
++
++ val = readl(the_transceiver->regs + CI_OTGSC);
++ if (on)
++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR,
++ the_transceiver->regs + CI_OTGSC);
++ else
++ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR,
++ the_transceiver->regs + CI_OTGSC);
++}
++
++/* set HABA: Hardware Assist B-Disconnect to A-Connect */
++static void langwell_otg_HABA(int on)
++{
++ u32 val;
++
++ otg_dbg("HABA -> %d\n", on);
++
++ val = readl(the_transceiver->regs + CI_OTGSC);
++ if (on)
++ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA,
++ the_transceiver->regs + CI_OTGSC);
++ else
++ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA,
++ the_transceiver->regs + CI_OTGSC);
++}
++
++static int langwell_otg_check_se0_srp(int on)
++{
++ u32 val;
++
++ int delay_time = TB_SE0_SRP * 10; /* step is 100us */
++
++ otg_dbg("check_se0_srp -> \n");
++
++ do {
++ udelay(100);
++ if (!delay_time--)
++ break;
++ val = readl(the_transceiver->regs + CI_PORTSC1);
++ val &= PORTSC_LS;
++ } while (!val);
++
++ otg_dbg("check_se0_srp <- \n");
++ return val;
++}
++
++/* The timeout callback function to set time out bit */
++static void set_tmout(unsigned long indicator)
++{
++ *(int *)indicator = 1;
++}
++
++void langwell_otg_nsf_msg(unsigned long indicator)
++{
++ switch (indicator) {
++ case 2:
++ case 4:
++ case 6:
++ case 7:
++ printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n",
++ indicator);
++ break;
++ case 3:
++ printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n",
++ indicator);
++ break;
++ default:
++ printk(KERN_ERR "Do not have this kind of NSF\n");
++ break;
++ }
++}
++
++/* Initialize timers */
++static void langwell_otg_init_timers(struct otg_hsm *hsm)
++{
++ /* HSM used timers */
++ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
++ (unsigned long)&hsm->a_wait_vrise_tmout);
++ a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
++ (unsigned long)&hsm->a_wait_bcon_tmout);
++ a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
++ (unsigned long)&hsm->a_aidl_bdis_tmout);
++ b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
++ (unsigned long)&hsm->b_ase0_brst_tmout);
++ b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
++ (unsigned long)&hsm->b_se0_srp);
++ b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES,
++ (unsigned long)&hsm->b_srp_res_tmout);
++ b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND,
++ (unsigned long)&hsm->b_bus_suspend_tmout);
++}
++
++/* Free timers */
++static void langwell_otg_free_timers(void)
++{
++ kfree(a_wait_vrise_tmr);
++ kfree(a_wait_bcon_tmr);
++ kfree(a_aidl_bdis_tmr);
++ kfree(b_ase0_brst_tmr);
++ kfree(b_se0_srp_tmr);
++ kfree(b_srp_res_tmr);
++ kfree(b_bus_suspend_tmr);
++}
++
++/* Add timer to timer list */
++static void langwell_otg_add_timer(void *gtimer)
++{
++ struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
++ struct langwell_otg_timer *tmp_timer;
++ u32 val32;
++
++ /* Check if the timer is already in the active list,
++ * if so update timer count
++ */
++ list_for_each_entry(tmp_timer, &active_timers, list)
++ if (tmp_timer == timer) {
++ timer->count = timer->expires;
++ return;
++ }
++ timer->count = timer->expires;
++
++ if (list_empty(&active_timers)) {
++ val32 = readl(the_transceiver->regs + CI_OTGSC);
++ writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC);
++ }
++
++ list_add_tail(&timer->list, &active_timers);
++}
++
++/* Remove timer from the timer list; clear timeout status */
++static void langwell_otg_del_timer(void *gtimer)
++{
++ struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
++ struct langwell_otg_timer *tmp_timer, *del_tmp;
++ u32 val32;
++
++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
++ if (tmp_timer == timer)
++ list_del(&timer->list);
++
++ if (list_empty(&active_timers)) {
++ val32 = readl(the_transceiver->regs + CI_OTGSC);
++ writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC);
++ }
++}
++
++/* Reduce timer count by 1, and find timeout conditions.*/
++static int langwell_otg_tick_timer(u32 *int_sts)
++{
++ struct langwell_otg_timer *tmp_timer, *del_tmp;
++ int expired = 0;
++
++ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
++ tmp_timer->count--;
++ /* check if timer expires */
++ if (!tmp_timer->count) {
++ list_del(&tmp_timer->list);
++ tmp_timer->function(tmp_timer->data);
++ expired = 1;
++ }
++ }
++
++ if (list_empty(&active_timers)) {
++ otg_dbg("tick timer: disable 1ms int\n");
++ *int_sts = *int_sts & ~OTGSC_1MSE;
++ }
++ return expired;
++}
++
++static void reset_otg(void)
++{
++ u32 val;
++ int delay_time = 1000;
++
++ otg_dbg("reseting OTG controller ...\n");
++ val = readl(the_transceiver->regs + CI_USBCMD);
++ writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD);
++ do {
++ udelay(100);
++ if (!delay_time--)
++ otg_dbg("reset timeout\n");
++ val = readl(the_transceiver->regs + CI_USBCMD);
++ val &= USBCMD_RST;
++ } while (val != 0);
++ otg_dbg("reset done.\n");
++}
++
++static void set_host_mode(void)
++{
++ u32 val;
++
++ reset_otg();
++ val = readl(the_transceiver->regs + CI_USBMODE);
++ val = (val & (~USBMODE_CM)) | USBMODE_HOST;
++ writel(val, the_transceiver->regs + CI_USBMODE);
++}
++
++static void set_client_mode(void)
++{
++ u32 val;
++
++ reset_otg();
++ val = readl(the_transceiver->regs + CI_USBMODE);
++ val = (val & (~USBMODE_CM)) | USBMODE_DEVICE;
++ writel(val, the_transceiver->regs + CI_USBMODE);
++}
++
++static void init_hsm(void)
++{
++ struct langwell_otg *langwell = the_transceiver;
++ u32 val32;
++
++ /* read OTGSC after reset */
++ val32 = readl(langwell->regs + CI_OTGSC);
++ otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32);
++
++ /* set init state */
++ if (val32 & OTGSC_ID) {
++ langwell->hsm.id = 1;
++ langwell->otg.default_a = 0;
++ set_client_mode();
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ langwell_otg_drv_vbus(0);
++ } else {
++ langwell->hsm.id = 0;
++ langwell->otg.default_a = 1;
++ set_host_mode();
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ }
++
++ /* set session indicator */
++ if (val32 & OTGSC_BSE)
++ langwell->hsm.b_sess_end = 1;
++ if (val32 & OTGSC_BSV)
++ langwell->hsm.b_sess_vld = 1;
++ if (val32 & OTGSC_ASV)
++ langwell->hsm.a_sess_vld = 1;
++ if (val32 & OTGSC_AVV)
++ langwell->hsm.a_vbus_vld = 1;
++
++ /* defautly power the bus */
++ langwell->hsm.a_bus_req = 1;
++ langwell->hsm.a_bus_drop = 0;
++ /* defautly don't request bus as B device */
++ langwell->hsm.b_bus_req = 0;
++ /* no system error */
++ langwell->hsm.a_clr_err = 0;
++}
++
++static irqreturn_t otg_dummy_irq(int irq, void *_dev)
++{
++ void __iomem *reg_base = _dev;
++ u32 val;
++ u32 int_mask = 0;
++
++ val = readl(reg_base + CI_USBMODE);
++ if ((val & USBMODE_CM) != USBMODE_DEVICE)
++ return IRQ_NONE;
++
++ val = readl(reg_base + CI_USBSTS);
++ int_mask = val & INTR_DUMMY_MASK;
++
++ if (int_mask == 0)
++ return IRQ_NONE;
++
++ /* clear hsm.b_conn here since host driver can't detect it
++ * otg_dummy_irq called means B-disconnect happened.
++ */
++ if (the_transceiver->hsm.b_conn) {
++ the_transceiver->hsm.b_conn = 0;
++ if (spin_trylock(&the_transceiver->wq_lock)) {
++ queue_work(the_transceiver->qwork,
++ &the_transceiver->work);
++ spin_unlock(&the_transceiver->wq_lock);
++ }
++ }
++ /* Clear interrupts */
++ writel(int_mask, reg_base + CI_USBSTS);
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t otg_irq(int irq, void *_dev)
++{
++ struct langwell_otg *langwell = _dev;
++ u32 int_sts, int_en;
++ u32 int_mask = 0;
++ int flag = 0;
++
++ int_sts = readl(langwell->regs + CI_OTGSC);
++ int_en = (int_sts & OTGSC_INTEN_MASK) >> 8;
++ int_mask = int_sts & int_en;
++ if (int_mask == 0)
++ return IRQ_NONE;
++
++ if (int_mask & OTGSC_IDIS) {
++ otg_dbg("%s: id change int\n", __func__);
++ langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0;
++ flag = 1;
++ }
++ if (int_mask & OTGSC_DPIS) {
++ otg_dbg("%s: data pulse int\n", __func__);
++ langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0;
++ flag = 1;
++ }
++ if (int_mask & OTGSC_BSEIS) {
++ otg_dbg("%s: b session end int\n", __func__);
++ langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0;
++ flag = 1;
++ }
++ if (int_mask & OTGSC_BSVIS) {
++ otg_dbg("%s: b session valid int\n", __func__);
++ langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0;
++ flag = 1;
++ }
++ if (int_mask & OTGSC_ASVIS) {
++ otg_dbg("%s: a session valid int\n", __func__);
++ langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0;
++ flag = 1;
++ }
++ if (int_mask & OTGSC_AVVIS) {
++ otg_dbg("%s: a vbus valid int\n", __func__);
++ langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0;
++ flag = 1;
++ }
++
++ if (int_mask & OTGSC_1MSS) {
++ /* need to schedule otg_work if any timer is expired */
++ if (langwell_otg_tick_timer(&int_sts))
++ flag = 1;
++ }
++
++ writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask,
++ langwell->regs + CI_OTGSC);
++ if (flag)
++ queue_work(langwell->qwork, &langwell->work);
++
++ return IRQ_HANDLED;
++}
++
++static void langwell_otg_work(struct work_struct *work)
++{
++ struct langwell_otg *langwell = container_of(work,
++ struct langwell_otg, work);
++ int retval;
++
++ otg_dbg("%s: old state = %s\n", __func__,
++ state_string(langwell->otg.state));
++
++ switch (langwell->otg.state) {
++ case OTG_STATE_UNDEFINED:
++ case OTG_STATE_B_IDLE:
++ if (!langwell->hsm.id) {
++ langwell_otg_del_timer(b_srp_res_tmr);
++ langwell->otg.default_a = 1;
++ langwell->hsm.a_srp_det = 0;
++
++ langwell_otg_chrg_vbus(0);
++ langwell_otg_drv_vbus(0);
++
++ set_host_mode();
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.b_srp_res_tmout) {
++ langwell->hsm.b_srp_res_tmout = 0;
++ langwell->hsm.b_bus_req = 0;
++ langwell_otg_nsf_msg(6);
++ } else if (langwell->hsm.b_sess_vld) {
++ langwell_otg_del_timer(b_srp_res_tmr);
++ langwell->hsm.b_sess_end = 0;
++ langwell->hsm.a_bus_suspend = 0;
++
++ langwell_otg_chrg_vbus(0);
++ if (langwell->client_ops) {
++ langwell->client_ops->resume(langwell->pdev);
++ langwell->otg.state = OTG_STATE_B_PERIPHERAL;
++ } else
++ otg_dbg("client driver not loaded.\n");
++
++ } else if (langwell->hsm.b_bus_req &&
++ (langwell->hsm.b_sess_end)) {
++ /* workaround for b_se0_srp detection */
++ retval = langwell_otg_check_se0_srp(0);
++ if (retval) {
++ langwell->hsm.b_bus_req = 0;
++ otg_dbg("LS is not SE0, try again later\n");
++ } else {
++ /* Start SRP */
++ langwell_otg_start_srp(&langwell->otg);
++ langwell_otg_add_timer(b_srp_res_tmr);
++ }
++ }
++ break;
++ case OTG_STATE_B_SRP_INIT:
++ if (!langwell->hsm.id) {
++ langwell->otg.default_a = 1;
++ langwell->hsm.a_srp_det = 0;
++
++ langwell_otg_drv_vbus(0);
++ langwell_otg_chrg_vbus(0);
++
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.b_sess_vld) {
++ langwell_otg_chrg_vbus(0);
++ if (langwell->client_ops) {
++ langwell->client_ops->resume(langwell->pdev);
++ langwell->otg.state = OTG_STATE_B_PERIPHERAL;
++ } else
++ otg_dbg("client driver not loaded.\n");
++ }
++ break;
++ case OTG_STATE_B_PERIPHERAL:
++ if (!langwell->hsm.id) {
++ langwell->otg.default_a = 1;
++ langwell->hsm.a_srp_det = 0;
++
++ langwell_otg_drv_vbus(0);
++ langwell_otg_chrg_vbus(0);
++ set_host_mode();
++
++ if (langwell->client_ops) {
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ } else
++ otg_dbg("client driver has been removed.\n");
++
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (!langwell->hsm.b_sess_vld) {
++ langwell->hsm.b_hnp_enable = 0;
++
++ if (langwell->client_ops) {
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ } else
++ otg_dbg("client driver has been removed.\n");
++
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable
++ && langwell->hsm.a_bus_suspend) {
++
++ if (langwell->client_ops) {
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ } else
++ otg_dbg("client driver has been removed.\n");
++
++ langwell_otg_HAAR(1);
++ langwell->hsm.a_conn = 0;
++
++ if (langwell->host_ops) {
++ langwell->host_ops->probe(langwell->pdev,
++ langwell->host_ops->id_table);
++ langwell->otg.state = OTG_STATE_B_WAIT_ACON;
++ } else
++ otg_dbg("host driver not loaded.\n");
++
++ langwell->hsm.a_bus_resume = 0;
++ langwell->hsm.b_ase0_brst_tmout = 0;
++ langwell_otg_add_timer(b_ase0_brst_tmr);
++ }
++ break;
++
++ case OTG_STATE_B_WAIT_ACON:
++ if (!langwell->hsm.id) {
++ langwell_otg_del_timer(b_ase0_brst_tmr);
++ langwell->otg.default_a = 1;
++ langwell->hsm.a_srp_det = 0;
++
++ langwell_otg_drv_vbus(0);
++ langwell_otg_chrg_vbus(0);
++ set_host_mode();
++
++ langwell_otg_HAAR(0);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (!langwell->hsm.b_sess_vld) {
++ langwell_otg_del_timer(b_ase0_brst_tmr);
++ langwell->hsm.b_hnp_enable = 0;
++ langwell->hsm.b_bus_req = 0;
++ langwell_otg_chrg_vbus(0);
++ langwell_otg_HAAR(0);
++
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ } else if (langwell->hsm.a_conn) {
++ langwell_otg_del_timer(b_ase0_brst_tmr);
++ langwell_otg_HAAR(0);
++ langwell->otg.state = OTG_STATE_B_HOST;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.a_bus_resume ||
++ langwell->hsm.b_ase0_brst_tmout) {
++ langwell_otg_del_timer(b_ase0_brst_tmr);
++ langwell_otg_HAAR(0);
++ langwell_otg_nsf_msg(7);
++
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++
++ langwell->hsm.a_bus_suspend = 0;
++ langwell->hsm.b_bus_req = 0;
++
++ if (langwell->client_ops)
++ langwell->client_ops->resume(langwell->pdev);
++ else
++ otg_dbg("client driver not loaded.\n");
++
++ langwell->otg.state = OTG_STATE_B_PERIPHERAL;
++ }
++ break;
++
++ case OTG_STATE_B_HOST:
++ if (!langwell->hsm.id) {
++ langwell->otg.default_a = 1;
++ langwell->hsm.a_srp_det = 0;
++
++ langwell_otg_drv_vbus(0);
++ langwell_otg_chrg_vbus(0);
++ set_host_mode();
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (!langwell->hsm.b_sess_vld) {
++ langwell->hsm.b_hnp_enable = 0;
++ langwell->hsm.b_bus_req = 0;
++ langwell_otg_chrg_vbus(0);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ } else if ((!langwell->hsm.b_bus_req) ||
++ (!langwell->hsm.a_conn)) {
++ langwell->hsm.b_bus_req = 0;
++ langwell_otg_loc_sof(0);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++
++ langwell->hsm.a_bus_suspend = 0;
++
++ if (langwell->client_ops)
++ langwell->client_ops->resume(langwell->pdev);
++ else
++ otg_dbg("client driver not loaded.\n");
++
++ langwell->otg.state = OTG_STATE_B_PERIPHERAL;
++ }
++ break;
++
++ case OTG_STATE_A_IDLE:
++ langwell->otg.default_a = 1;
++ if (langwell->hsm.id) {
++ langwell->otg.default_a = 0;
++ langwell->hsm.b_bus_req = 0;
++ langwell_otg_drv_vbus(0);
++ langwell_otg_chrg_vbus(0);
++
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.a_sess_vld) {
++ langwell_otg_drv_vbus(1);
++ langwell->hsm.a_srp_det = 1;
++ langwell->hsm.a_wait_vrise_tmout = 0;
++ langwell_otg_add_timer(a_wait_vrise_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (!langwell->hsm.a_bus_drop &&
++ (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) {
++ langwell_otg_drv_vbus(1);
++ langwell->hsm.a_wait_vrise_tmout = 0;
++ langwell_otg_add_timer(a_wait_vrise_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
++ queue_work(langwell->qwork, &langwell->work);
++ }
++ break;
++ case OTG_STATE_A_WAIT_VRISE:
++ if (langwell->hsm.id) {
++ langwell_otg_del_timer(a_wait_vrise_tmr);
++ langwell->hsm.b_bus_req = 0;
++ langwell->otg.default_a = 0;
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ } else if (langwell->hsm.a_vbus_vld) {
++ langwell_otg_del_timer(a_wait_vrise_tmr);
++ if (langwell->host_ops)
++ langwell->host_ops->probe(langwell->pdev,
++ langwell->host_ops->id_table);
++ else
++ otg_dbg("host driver not loaded.\n");
++ langwell->hsm.b_conn = 0;
++ langwell->hsm.a_set_b_hnp_en = 0;
++ langwell->hsm.a_wait_bcon_tmout = 0;
++ langwell_otg_add_timer(a_wait_bcon_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_BCON;
++ } else if (langwell->hsm.a_wait_vrise_tmout) {
++ if (langwell->hsm.a_vbus_vld) {
++ if (langwell->host_ops)
++ langwell->host_ops->probe(
++ langwell->pdev,
++ langwell->host_ops->id_table);
++ else
++ otg_dbg("host driver not loaded.\n");
++ langwell->hsm.b_conn = 0;
++ langwell->hsm.a_set_b_hnp_en = 0;
++ langwell->hsm.a_wait_bcon_tmout = 0;
++ langwell_otg_add_timer(a_wait_bcon_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_BCON;
++ } else {
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_VBUS_ERR;
++ }
++ }
++ break;
++ case OTG_STATE_A_WAIT_BCON:
++ if (langwell->hsm.id) {
++ langwell_otg_del_timer(a_wait_bcon_tmr);
++
++ langwell->otg.default_a = 0;
++ langwell->hsm.b_bus_req = 0;
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (!langwell->hsm.a_vbus_vld) {
++ langwell_otg_del_timer(a_wait_bcon_tmr);
++
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_VBUS_ERR;
++ } else if (langwell->hsm.a_bus_drop ||
++ (langwell->hsm.a_wait_bcon_tmout &&
++ !langwell->hsm.a_bus_req)) {
++ langwell_otg_del_timer(a_wait_bcon_tmr);
++
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ } else if (langwell->hsm.b_conn) {
++ langwell_otg_del_timer(a_wait_bcon_tmr);
++
++ langwell->hsm.a_suspend_req = 0;
++ langwell->otg.state = OTG_STATE_A_HOST;
++ if (!langwell->hsm.a_bus_req &&
++ langwell->hsm.a_set_b_hnp_en) {
++ /* It is not safe enough to do a fast
++ * transistion from A_WAIT_BCON to
++ * A_SUSPEND */
++ msleep(10000);
++ if (langwell->hsm.a_bus_req)
++ break;
++
++ if (request_irq(langwell->pdev->irq,
++ otg_dummy_irq, IRQF_SHARED,
++ driver_name, langwell->regs) != 0) {
++ otg_dbg("request interrupt %d fail\n",
++ langwell->pdev->irq);
++ }
++
++ langwell_otg_HABA(1);
++ langwell->hsm.b_bus_resume = 0;
++ langwell->hsm.a_aidl_bdis_tmout = 0;
++ langwell_otg_add_timer(a_aidl_bdis_tmr);
++
++ langwell_otg_loc_sof(0);
++ langwell->otg.state = OTG_STATE_A_SUSPEND;
++ } else if (!langwell->hsm.a_bus_req &&
++ !langwell->hsm.a_set_b_hnp_en) {
++ struct pci_dev *pdev = langwell->pdev;
++ if (langwell->host_ops)
++ langwell->host_ops->remove(pdev);
++ else
++ otg_dbg("host driver removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ }
++ }
++ break;
++ case OTG_STATE_A_HOST:
++ if (langwell->hsm.id) {
++ langwell->otg.default_a = 0;
++ langwell->hsm.b_bus_req = 0;
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.a_bus_drop ||
++ (!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) {
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ } else if (!langwell->hsm.a_vbus_vld) {
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_VBUS_ERR;
++ } else if (langwell->hsm.a_set_b_hnp_en
++ && !langwell->hsm.a_bus_req) {
++ /* Set HABA to enable hardware assistance to signal
++ * A-connect after receiver B-disconnect. Hardware
++ * will then set client mode and enable URE, SLE and
++ * PCE after the assistance. otg_dummy_irq is used to
++ * clean these ints when client driver is not resumed.
++ */
++ if (request_irq(langwell->pdev->irq,
++ otg_dummy_irq, IRQF_SHARED, driver_name,
++ langwell->regs) != 0) {
++ otg_dbg("request interrupt %d failed\n",
++ langwell->pdev->irq);
++ }
++
++ /* set HABA */
++ langwell_otg_HABA(1);
++ langwell->hsm.b_bus_resume = 0;
++ langwell->hsm.a_aidl_bdis_tmout = 0;
++ langwell_otg_add_timer(a_aidl_bdis_tmr);
++ langwell_otg_loc_sof(0);
++ langwell->otg.state = OTG_STATE_A_SUSPEND;
++ } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) {
++ langwell->hsm.a_wait_bcon_tmout = 0;
++ langwell->hsm.a_set_b_hnp_en = 0;
++ langwell_otg_add_timer(a_wait_bcon_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_BCON;
++ }
++ break;
++ case OTG_STATE_A_SUSPEND:
++ if (langwell->hsm.id) {
++ langwell_otg_del_timer(a_aidl_bdis_tmr);
++ langwell_otg_HABA(0);
++ free_irq(langwell->pdev->irq, langwell->regs);
++ langwell->otg.default_a = 0;
++ langwell->hsm.b_bus_req = 0;
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.a_bus_req ||
++ langwell->hsm.b_bus_resume) {
++ langwell_otg_del_timer(a_aidl_bdis_tmr);
++ langwell_otg_HABA(0);
++ free_irq(langwell->pdev->irq, langwell->regs);
++ langwell->hsm.a_suspend_req = 0;
++ langwell_otg_loc_sof(1);
++ langwell->otg.state = OTG_STATE_A_HOST;
++ } else if (langwell->hsm.a_aidl_bdis_tmout ||
++ langwell->hsm.a_bus_drop) {
++ langwell_otg_del_timer(a_aidl_bdis_tmr);
++ langwell_otg_HABA(0);
++ free_irq(langwell->pdev->irq, langwell->regs);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ } else if (!langwell->hsm.b_conn &&
++ langwell->hsm.a_set_b_hnp_en) {
++ langwell_otg_del_timer(a_aidl_bdis_tmr);
++ langwell_otg_HABA(0);
++ free_irq(langwell->pdev->irq, langwell->regs);
++
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++
++ langwell->hsm.b_bus_suspend = 0;
++ langwell->hsm.b_bus_suspend_vld = 0;
++ langwell->hsm.b_bus_suspend_tmout = 0;
++
++ /* msleep(200); */
++ if (langwell->client_ops)
++ langwell->client_ops->resume(langwell->pdev);
++ else
++ otg_dbg("client driver not loaded.\n");
++
++ langwell_otg_add_timer(b_bus_suspend_tmr);
++ langwell->otg.state = OTG_STATE_A_PERIPHERAL;
++ break;
++ } else if (!langwell->hsm.a_vbus_vld) {
++ langwell_otg_del_timer(a_aidl_bdis_tmr);
++ langwell_otg_HABA(0);
++ free_irq(langwell->pdev->irq, langwell->regs);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(langwell->pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_VBUS_ERR;
++ }
++ break;
++ case OTG_STATE_A_PERIPHERAL:
++ if (langwell->hsm.id) {
++ langwell_otg_del_timer(b_bus_suspend_tmr);
++ langwell->otg.default_a = 0;
++ langwell->hsm.b_bus_req = 0;
++ if (langwell->client_ops)
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ else
++ otg_dbg("client driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (!langwell->hsm.a_vbus_vld) {
++ langwell_otg_del_timer(b_bus_suspend_tmr);
++ if (langwell->client_ops)
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ else
++ otg_dbg("client driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_VBUS_ERR;
++ } else if (langwell->hsm.a_bus_drop) {
++ langwell_otg_del_timer(b_bus_suspend_tmr);
++ if (langwell->client_ops)
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ else
++ otg_dbg("client driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ } else if (langwell->hsm.b_bus_suspend) {
++ langwell_otg_del_timer(b_bus_suspend_tmr);
++ if (langwell->client_ops)
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ else
++ otg_dbg("client driver has been removed.\n");
++
++ if (langwell->host_ops)
++ langwell->host_ops->probe(langwell->pdev,
++ langwell->host_ops->id_table);
++ else
++ otg_dbg("host driver not loaded.\n");
++ langwell->hsm.a_set_b_hnp_en = 0;
++ langwell->hsm.a_wait_bcon_tmout = 0;
++ langwell_otg_add_timer(a_wait_bcon_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_BCON;
++ } else if (langwell->hsm.b_bus_suspend_tmout) {
++ u32 val;
++ val = readl(langwell->regs + CI_PORTSC1);
++ if (!(val & PORTSC_SUSP))
++ break;
++ if (langwell->client_ops)
++ langwell->client_ops->suspend(langwell->pdev,
++ PMSG_FREEZE);
++ else
++ otg_dbg("client driver has been removed.\n");
++ if (langwell->host_ops)
++ langwell->host_ops->probe(langwell->pdev,
++ langwell->host_ops->id_table);
++ else
++ otg_dbg("host driver not loaded.\n");
++ langwell->hsm.a_set_b_hnp_en = 0;
++ langwell->hsm.a_wait_bcon_tmout = 0;
++ langwell_otg_add_timer(a_wait_bcon_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_BCON;
++ }
++ break;
++ case OTG_STATE_A_VBUS_ERR:
++ if (langwell->hsm.id) {
++ langwell->otg.default_a = 0;
++ langwell->hsm.a_clr_err = 0;
++ langwell->hsm.a_srp_det = 0;
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.a_clr_err) {
++ langwell->hsm.a_clr_err = 0;
++ langwell->hsm.a_srp_det = 0;
++ reset_otg();
++ init_hsm();
++ if (langwell->otg.state == OTG_STATE_A_IDLE)
++ queue_work(langwell->qwork, &langwell->work);
++ }
++ break;
++ case OTG_STATE_A_WAIT_VFALL:
++ if (langwell->hsm.id) {
++ langwell->otg.default_a = 0;
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ queue_work(langwell->qwork, &langwell->work);
++ } else if (langwell->hsm.a_bus_req) {
++ langwell_otg_drv_vbus(1);
++ langwell->hsm.a_wait_vrise_tmout = 0;
++ langwell_otg_add_timer(a_wait_vrise_tmr);
++ langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
++ } else if (!langwell->hsm.a_sess_vld) {
++ langwell->hsm.a_srp_det = 0;
++ langwell_otg_drv_vbus(0);
++ set_host_mode();
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ }
++ break;
++ default:
++ ;
++ }
++
++ otg_dbg("%s: new state = %s\n", __func__,
++ state_string(langwell->otg.state));
++}
++
++ static ssize_t
++show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
++{
++ struct langwell_otg *langwell;
++ char *next;
++ unsigned size;
++ unsigned t;
++
++ langwell = the_transceiver;
++ next = buf;
++ size = PAGE_SIZE;
++
++ t = scnprintf(next, size,
++ "\n"
++ "USBCMD = 0x%08x \n"
++ "USBSTS = 0x%08x \n"
++ "USBINTR = 0x%08x \n"
++ "ASYNCLISTADDR = 0x%08x \n"
++ "PORTSC1 = 0x%08x \n"
++ "HOSTPC1 = 0x%08x \n"
++ "OTGSC = 0x%08x \n"
++ "USBMODE = 0x%08x \n",
++ readl(langwell->regs + 0x30),
++ readl(langwell->regs + 0x34),
++ readl(langwell->regs + 0x38),
++ readl(langwell->regs + 0x48),
++ readl(langwell->regs + 0x74),
++ readl(langwell->regs + 0xb4),
++ readl(langwell->regs + 0xf4),
++ readl(langwell->regs + 0xf8)
++ );
++ size -= t;
++ next += t;
++
++ return PAGE_SIZE - size;
++}
++static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
++
++static ssize_t
++show_hsm(struct device *_dev, struct device_attribute *attr, char *buf)
++{
++ struct langwell_otg *langwell;
++ char *next;
++ unsigned size;
++ unsigned t;
++
++ langwell = the_transceiver;
++ next = buf;
++ size = PAGE_SIZE;
++
++ t = scnprintf(next, size,
++ "\n"
++ "current state = %s\n"
++ "a_bus_resume = \t%d\n"
++ "a_bus_suspend = \t%d\n"
++ "a_conn = \t%d\n"
++ "a_sess_vld = \t%d\n"
++ "a_srp_det = \t%d\n"
++ "a_vbus_vld = \t%d\n"
++ "b_bus_resume = \t%d\n"
++ "b_bus_suspend = \t%d\n"
++ "b_conn = \t%d\n"
++ "b_se0_srp = \t%d\n"
++ "b_sess_end = \t%d\n"
++ "b_sess_vld = \t%d\n"
++ "id = \t%d\n"
++ "a_set_b_hnp_en = \t%d\n"
++ "b_srp_done = \t%d\n"
++ "b_hnp_enable = \t%d\n"
++ "a_wait_vrise_tmout = \t%d\n"
++ "a_wait_bcon_tmout = \t%d\n"
++ "a_aidl_bdis_tmout = \t%d\n"
++ "b_ase0_brst_tmout = \t%d\n"
++ "a_bus_drop = \t%d\n"
++ "a_bus_req = \t%d\n"
++ "a_clr_err = \t%d\n"
++ "a_suspend_req = \t%d\n"
++ "b_bus_req = \t%d\n"
++ "b_bus_suspend_tmout = \t%d\n"
++ "b_bus_suspend_vld = \t%d\n",
++ state_string(langwell->otg.state),
++ langwell->hsm.a_bus_resume,
++ langwell->hsm.a_bus_suspend,
++ langwell->hsm.a_conn,
++ langwell->hsm.a_sess_vld,
++ langwell->hsm.a_srp_det,
++ langwell->hsm.a_vbus_vld,
++ langwell->hsm.b_bus_resume,
++ langwell->hsm.b_bus_suspend,
++ langwell->hsm.b_conn,
++ langwell->hsm.b_se0_srp,
++ langwell->hsm.b_sess_end,
++ langwell->hsm.b_sess_vld,
++ langwell->hsm.id,
++ langwell->hsm.a_set_b_hnp_en,
++ langwell->hsm.b_srp_done,
++ langwell->hsm.b_hnp_enable,
++ langwell->hsm.a_wait_vrise_tmout,
++ langwell->hsm.a_wait_bcon_tmout,
++ langwell->hsm.a_aidl_bdis_tmout,
++ langwell->hsm.b_ase0_brst_tmout,
++ langwell->hsm.a_bus_drop,
++ langwell->hsm.a_bus_req,
++ langwell->hsm.a_clr_err,
++ langwell->hsm.a_suspend_req,
++ langwell->hsm.b_bus_req,
++ langwell->hsm.b_bus_suspend_tmout,
++ langwell->hsm.b_bus_suspend_vld
++ );
++ size -= t;
++ next += t;
++
++ return PAGE_SIZE - size;
++}
++static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL);
++
++static ssize_t
++get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct langwell_otg *langwell;
++ char *next;
++ unsigned size;
++ unsigned t;
++
++ langwell = the_transceiver;
++ next = buf;
++ size = PAGE_SIZE;
++
++ t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req);
++ size -= t;
++ next += t;
++
++ return PAGE_SIZE - size;
++}
++
++static ssize_t
++set_a_bus_req(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct langwell_otg *langwell;
++ langwell = the_transceiver;
++ if (!langwell->otg.default_a)
++ return -1;
++ if (count > 2)
++ return -1;
++
++ if (buf[0] == '0') {
++ langwell->hsm.a_bus_req = 0;
++ otg_dbg("a_bus_req = 0\n");
++ } else if (buf[0] == '1') {
++ /* If a_bus_drop is TRUE, a_bus_req can't be set */
++ if (langwell->hsm.a_bus_drop)
++ return -1;
++ langwell->hsm.a_bus_req = 1;
++ otg_dbg("a_bus_req = 1\n");
++ }
++ if (spin_trylock(&langwell->wq_lock)) {
++ queue_work(langwell->qwork, &langwell->work);
++ spin_unlock(&langwell->wq_lock);
++ }
++ return count;
++}
++static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req);
++
++static ssize_t
++get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct langwell_otg *langwell;
++ char *next;
++ unsigned size;
++ unsigned t;
++
++ langwell = the_transceiver;
++ next = buf;
++ size = PAGE_SIZE;
++
++ t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop);
++ size -= t;
++ next += t;
++
++ return PAGE_SIZE - size;
++}
++
++static ssize_t
++set_a_bus_drop(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct langwell_otg *langwell;
++ langwell = the_transceiver;
++ if (!langwell->otg.default_a)
++ return -1;
++ if (count > 2)
++ return -1;
++
++ if (buf[0] == '0') {
++ langwell->hsm.a_bus_drop = 0;
++ otg_dbg("a_bus_drop = 0\n");
++ } else if (buf[0] == '1') {
++ langwell->hsm.a_bus_drop = 1;
++ langwell->hsm.a_bus_req = 0;
++ otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n");
++ }
++ if (spin_trylock(&langwell->wq_lock)) {
++ queue_work(langwell->qwork, &langwell->work);
++ spin_unlock(&langwell->wq_lock);
++ }
++ return count;
++}
++static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO,
++ get_a_bus_drop, set_a_bus_drop);
++
++static ssize_t
++get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct langwell_otg *langwell;
++ char *next;
++ unsigned size;
++ unsigned t;
++
++ langwell = the_transceiver;
++ next = buf;
++ size = PAGE_SIZE;
++
++ t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req);
++ size -= t;
++ next += t;
++
++ return PAGE_SIZE - size;
++}
++
++static ssize_t
++set_b_bus_req(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct langwell_otg *langwell;
++ langwell = the_transceiver;
++
++ if (langwell->otg.default_a)
++ return -1;
++
++ if (count > 2)
++ return -1;
++
++ if (buf[0] == '0') {
++ langwell->hsm.b_bus_req = 0;
++ otg_dbg("b_bus_req = 0\n");
++ } else if (buf[0] == '1') {
++ langwell->hsm.b_bus_req = 1;
++ otg_dbg("b_bus_req = 1\n");
++ }
++ if (spin_trylock(&langwell->wq_lock)) {
++ queue_work(langwell->qwork, &langwell->work);
++ spin_unlock(&langwell->wq_lock);
++ }
++ return count;
++}
++static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req);
++
++static ssize_t
++set_a_clr_err(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct langwell_otg *langwell;
++ langwell = the_transceiver;
++
++ if (!langwell->otg.default_a)
++ return -1;
++ if (count > 2)
++ return -1;
++
++ if (buf[0] == '1') {
++ langwell->hsm.a_clr_err = 1;
++ otg_dbg("a_clr_err = 1\n");
++ }
++ if (spin_trylock(&langwell->wq_lock)) {
++ queue_work(langwell->qwork, &langwell->work);
++ spin_unlock(&langwell->wq_lock);
++ }
++ return count;
++}
++static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err);
++
++static struct attribute *inputs_attrs[] = {
++ &dev_attr_a_bus_req.attr,
++ &dev_attr_a_bus_drop.attr,
++ &dev_attr_b_bus_req.attr,
++ &dev_attr_a_clr_err.attr,
++ NULL,
++};
++
++static struct attribute_group debug_dev_attr_group = {
++ .name = "inputs",
++ .attrs = inputs_attrs,
++};
++
++int langwell_register_host(struct pci_driver *host_driver)
++{
++ int ret = 0;
++
++ the_transceiver->host_ops = host_driver;
++ queue_work(the_transceiver->qwork, &the_transceiver->work);
++ otg_dbg("host controller driver is registered\n");
++
++ return ret;
++}
++EXPORT_SYMBOL(langwell_register_host);
++
++void langwell_unregister_host(struct pci_driver *host_driver)
++{
++ if (the_transceiver->host_ops)
++ the_transceiver->host_ops->remove(the_transceiver->pdev);
++ the_transceiver->host_ops = NULL;
++ the_transceiver->hsm.a_bus_drop = 1;
++ queue_work(the_transceiver->qwork, &the_transceiver->work);
++ otg_dbg("host controller driver is unregistered\n");
++}
++EXPORT_SYMBOL(langwell_unregister_host);
++
++int langwell_register_peripheral(struct pci_driver *client_driver)
++{
++ int ret = 0;
++
++ if (client_driver)
++ ret = client_driver->probe(the_transceiver->pdev,
++ client_driver->id_table);
++ if (!ret) {
++ the_transceiver->client_ops = client_driver;
++ queue_work(the_transceiver->qwork, &the_transceiver->work);
++ otg_dbg("client controller driver is registered\n");
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(langwell_register_peripheral);
++
++void langwell_unregister_peripheral(struct pci_driver *client_driver)
++{
++ if (the_transceiver->client_ops)
++ the_transceiver->client_ops->remove(the_transceiver->pdev);
++ the_transceiver->client_ops = NULL;
++ the_transceiver->hsm.b_bus_req = 0;
++ queue_work(the_transceiver->qwork, &the_transceiver->work);
++ otg_dbg("client controller driver is unregistered\n");
++}
++EXPORT_SYMBOL(langwell_unregister_peripheral);
++
++static int langwell_otg_probe(struct pci_dev *pdev,
++ const struct pci_device_id *id)
++{
++ unsigned long resource, len;
++ void __iomem *base = NULL;
++ int retval;
++ u32 val32;
++ struct langwell_otg *langwell;
++ char qname[] = "langwell_otg_queue";
++
++ retval = 0;
++ otg_dbg("\notg controller is detected.\n");
++ if (pci_enable_device(pdev) < 0) {
++ retval = -ENODEV;
++ goto done;
++ }
++
++ langwell = kzalloc(sizeof *langwell, GFP_KERNEL);
++ if (langwell == NULL) {
++ retval = -ENOMEM;
++ goto done;
++ }
++ the_transceiver = langwell;
++
++ /* control register: BAR 0 */
++ resource = pci_resource_start(pdev, 0);
++ len = pci_resource_len(pdev, 0);
++ if (!request_mem_region(resource, len, driver_name)) {
++ retval = -EBUSY;
++ goto err;
++ }
++ langwell->region = 1;
++
++ base = ioremap_nocache(resource, len);
++ if (base == NULL) {
++ retval = -EFAULT;
++ goto err;
++ }
++ langwell->regs = base;
++
++ if (!pdev->irq) {
++ otg_dbg("No IRQ.\n");
++ retval = -ENODEV;
++ goto err;
++ }
++
++ langwell->qwork = create_workqueue(qname);
++ if (!langwell->qwork) {
++ otg_dbg("cannot create workqueue %s\n", qname);
++ retval = -ENOMEM;
++ goto err;
++ }
++ INIT_WORK(&langwell->work, langwell_otg_work);
++
++ /* OTG common part */
++ langwell->pdev = pdev;
++ langwell->otg.dev = &pdev->dev;
++ langwell->otg.label = driver_name;
++ langwell->otg.set_host = langwell_otg_set_host;
++ langwell->otg.set_peripheral = langwell_otg_set_peripheral;
++ langwell->otg.set_power = langwell_otg_set_power;
++ langwell->otg.start_srp = langwell_otg_start_srp;
++ langwell->otg.state = OTG_STATE_UNDEFINED;
++ if (otg_set_transceiver(&langwell->otg)) {
++ otg_dbg("can't set transceiver\n");
++ retval = -EBUSY;
++ goto err;
++ }
++
++ reset_otg();
++ init_hsm();
++
++ spin_lock_init(&langwell->lock);
++ spin_lock_init(&langwell->wq_lock);
++ INIT_LIST_HEAD(&active_timers);
++ langwell_otg_init_timers(&langwell->hsm);
++
++ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
++ driver_name, langwell) != 0) {
++ otg_dbg("request interrupt %d failed\n", pdev->irq);
++ retval = -EBUSY;
++ goto err;
++ }
++
++ /* enable OTGSC int */
++ val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE |
++ OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU;
++ writel(val32, langwell->regs + CI_OTGSC);
++
++ retval = device_create_file(&pdev->dev, &dev_attr_registers);
++ if (retval < 0) {
++ otg_dbg("Can't register sysfs attribute: %d\n", retval);
++ goto err;
++ }
++
++ retval = device_create_file(&pdev->dev, &dev_attr_hsm);
++ if (retval < 0) {
++ otg_dbg("Can't hsm sysfs attribute: %d\n", retval);
++ goto err;
++ }
++
++ retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group);
++ if (retval < 0) {
++ otg_dbg("Can't register sysfs attr group: %d\n", retval);
++ goto err;
++ }
++
++ if (langwell->otg.state == OTG_STATE_A_IDLE)
++ queue_work(langwell->qwork, &langwell->work);
++
++ return 0;
++
++err:
++ if (the_transceiver)
++ langwell_otg_remove(pdev);
++done:
++ return retval;
++}
++
++static void langwell_otg_remove(struct pci_dev *pdev)
++{
++ struct langwell_otg *langwell;
++
++ langwell = the_transceiver;
++
++ if (langwell->qwork) {
++ flush_workqueue(langwell->qwork);
++ destroy_workqueue(langwell->qwork);
++ }
++ langwell_otg_free_timers();
++
++ /* disable OTGSC interrupt as OTGSC doesn't change in reset */
++ writel(0, langwell->regs + CI_OTGSC);
++
++ if (pdev->irq)
++ free_irq(pdev->irq, langwell);
++ if (langwell->regs)
++ iounmap(langwell->regs);
++ if (langwell->region)
++ release_mem_region(pci_resource_start(pdev, 0),
++ pci_resource_len(pdev, 0));
++
++ otg_set_transceiver(NULL);
++ pci_disable_device(pdev);
++ sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group);
++ device_remove_file(&pdev->dev, &dev_attr_hsm);
++ device_remove_file(&pdev->dev, &dev_attr_registers);
++ kfree(langwell);
++ langwell = NULL;
++}
++
++static void transceiver_suspend(struct pci_dev *pdev)
++{
++ pci_save_state(pdev);
++ pci_set_power_state(pdev, PCI_D3hot);
++ langwell_otg_phy_low_power(1);
++}
++
++static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
++{
++ int ret = 0;
++ struct langwell_otg *langwell;
++
++ langwell = the_transceiver;
++
++ /* Disbale OTG interrupts */
++ langwell_otg_intr(0);
++
++ if (pdev->irq)
++ free_irq(pdev->irq, langwell);
++
++ /* Prevent more otg_work */
++ flush_workqueue(langwell->qwork);
++ spin_lock(&langwell->wq_lock);
++
++ /* start actions */
++ switch (langwell->otg.state) {
++ case OTG_STATE_A_IDLE:
++ case OTG_STATE_B_IDLE:
++ case OTG_STATE_A_WAIT_VFALL:
++ case OTG_STATE_A_VBUS_ERR:
++ transceiver_suspend(pdev);
++ break;
++ case OTG_STATE_A_WAIT_VRISE:
++ langwell_otg_del_timer(a_wait_vrise_tmr);
++ langwell->hsm.a_srp_det = 0;
++ langwell_otg_drv_vbus(0);
++ langwell->otg.state = OTG_STATE_A_IDLE;
++ transceiver_suspend(pdev);
++ break;
++ case OTG_STATE_A_WAIT_BCON:
++ langwell_otg_del_timer(a_wait_bcon_tmr);
++ if (langwell->host_ops)
++ ret = langwell->host_ops->suspend(pdev, message);
++ langwell_otg_drv_vbus(0);
++ break;
++ case OTG_STATE_A_HOST:
++ if (langwell->host_ops)
++ ret = langwell->host_ops->suspend(pdev, message);
++ langwell_otg_drv_vbus(0);
++ langwell_otg_phy_low_power(1);
++ break;
++ case OTG_STATE_A_SUSPEND:
++ langwell_otg_del_timer(a_aidl_bdis_tmr);
++ langwell_otg_HABA(0);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ transceiver_suspend(pdev);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ break;
++ case OTG_STATE_A_PERIPHERAL:
++ if (langwell->client_ops)
++ ret = langwell->client_ops->suspend(pdev, message);
++ else
++ otg_dbg("client driver has been removed.\n");
++ langwell_otg_drv_vbus(0);
++ transceiver_suspend(pdev);
++ langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
++ break;
++ case OTG_STATE_B_HOST:
++ if (langwell->host_ops)
++ langwell->host_ops->remove(pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell->hsm.b_bus_req = 0;
++ transceiver_suspend(pdev);
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ break;
++ case OTG_STATE_B_PERIPHERAL:
++ if (langwell->client_ops)
++ ret = langwell->client_ops->suspend(pdev, message);
++ else
++ otg_dbg("client driver has been removed.\n");
++ break;
++ case OTG_STATE_B_WAIT_ACON:
++ langwell_otg_del_timer(b_ase0_brst_tmr);
++ langwell_otg_HAAR(0);
++ if (langwell->host_ops)
++ langwell->host_ops->remove(pdev);
++ else
++ otg_dbg("host driver has been removed.\n");
++ langwell->hsm.b_bus_req = 0;
++ langwell->otg.state = OTG_STATE_B_IDLE;
++ transceiver_suspend(pdev);
++ break;
++ default:
++ otg_dbg("error state before suspend\n ");
++ break;
++ }
++ spin_unlock(&langwell->wq_lock);
++
++ return ret;
++}
++
++static void transceiver_resume(struct pci_dev *pdev)
++{
++ pci_restore_state(pdev);
++ pci_set_power_state(pdev, PCI_D0);
++ langwell_otg_phy_low_power(0);
++}
++
++static int langwell_otg_resume(struct pci_dev *pdev)
++{
++ int ret = 0;
++ struct langwell_otg *langwell;
++
++ langwell = the_transceiver;
++
++ spin_lock(&langwell->wq_lock);
++
++ switch (langwell->otg.state) {
++ case OTG_STATE_A_IDLE:
++ case OTG_STATE_B_IDLE:
++ case OTG_STATE_A_WAIT_VFALL:
++ case OTG_STATE_A_VBUS_ERR:
++ transceiver_resume(pdev);
++ break;
++ case OTG_STATE_A_WAIT_BCON:
++ langwell_otg_add_timer(a_wait_bcon_tmr);
++ langwell_otg_drv_vbus(1);
++ if (langwell->host_ops)
++ ret = langwell->host_ops->resume(pdev);
++ break;
++ case OTG_STATE_A_HOST:
++ langwell_otg_drv_vbus(1);
++ langwell_otg_phy_low_power(0);
++ if (langwell->host_ops)
++ ret = langwell->host_ops->resume(pdev);
++ break;
++ case OTG_STATE_B_PERIPHERAL:
++ if (langwell->client_ops)
++ ret = langwell->client_ops->resume(pdev);
++ else
++ otg_dbg("client driver not loaded.\n");
++ break;
++ default:
++ otg_dbg("error state before suspend\n ");
++ break;
++ }
++
++ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
++ driver_name, the_transceiver) != 0) {
++ otg_dbg("request interrupt %d failed\n", pdev->irq);
++ ret = -EBUSY;
++ }
++
++ /* enable OTG interrupts */
++ langwell_otg_intr(1);
++
++ spin_unlock(&langwell->wq_lock);
++
++ queue_work(langwell->qwork, &langwell->work);
++
++
++ return ret;
++}
++
++static int __init langwell_otg_init(void)
++{
++ return pci_register_driver(&otg_pci_driver);
++}
++module_init(langwell_otg_init);
++
++static void __exit langwell_otg_cleanup(void)
++{
++ pci_unregister_driver(&otg_pci_driver);
++}
++module_exit(langwell_otg_cleanup);
+--- a/drivers/usb/otg/Makefile
++++ b/drivers/usb/otg/Makefile
+@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o
+ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
+ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+ obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
++obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
+ obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
+
+ ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
+--- /dev/null
++++ b/include/linux/usb/langwell_otg.h
+@@ -0,0 +1,177 @@
++/*
++ * Intel Langwell USB OTG transceiver driver
++ * Copyright (C) 2008, 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_OTG_H__
++#define __LANGWELL_OTG_H__
++
++/* notify transceiver driver about OTG events */
++extern void langwell_update_transceiver(void);
++/* HCD register bus driver */
++extern int langwell_register_host(struct pci_driver *host_driver);
++/* HCD unregister bus driver */
++extern void langwell_unregister_host(struct pci_driver *host_driver);
++/* DCD register bus driver */
++extern int langwell_register_peripheral(struct pci_driver *client_driver);
++/* DCD unregister bus driver */
++extern void langwell_unregister_peripheral(struct pci_driver *client_driver);
++/* No silent failure, output warning message */
++extern void langwell_otg_nsf_msg(unsigned long message);
++
++#define CI_USBCMD 0x30
++# define USBCMD_RST BIT(1)
++# define USBCMD_RS BIT(0)
++#define CI_USBSTS 0x34
++# define USBSTS_SLI BIT(8)
++# define USBSTS_URI BIT(6)
++# define USBSTS_PCI BIT(2)
++#define CI_PORTSC1 0x74
++# define PORTSC_PP BIT(12)
++# define PORTSC_LS (BIT(11) | BIT(10))
++# define PORTSC_SUSP BIT(7)
++# define PORTSC_CCS BIT(0)
++#define CI_HOSTPC1 0xb4
++# define HOSTPC1_PHCD BIT(22)
++#define CI_OTGSC 0xf4
++# define OTGSC_DPIE BIT(30)
++# define OTGSC_1MSE BIT(29)
++# define OTGSC_BSEIE BIT(28)
++# define OTGSC_BSVIE BIT(27)
++# define OTGSC_ASVIE BIT(26)
++# define OTGSC_AVVIE BIT(25)
++# define OTGSC_IDIE BIT(24)
++# define OTGSC_DPIS BIT(22)
++# define OTGSC_1MSS BIT(21)
++# define OTGSC_BSEIS BIT(20)
++# define OTGSC_BSVIS BIT(19)
++# define OTGSC_ASVIS BIT(18)
++# define OTGSC_AVVIS BIT(17)
++# define OTGSC_IDIS BIT(16)
++# define OTGSC_DPS BIT(14)
++# define OTGSC_1MST BIT(13)
++# define OTGSC_BSE BIT(12)
++# define OTGSC_BSV BIT(11)
++# define OTGSC_ASV BIT(10)
++# define OTGSC_AVV BIT(9)
++# define OTGSC_ID BIT(8)
++# define OTGSC_HABA BIT(7)
++# define OTGSC_HADP BIT(6)
++# define OTGSC_IDPU BIT(5)
++# define OTGSC_DP BIT(4)
++# define OTGSC_OT BIT(3)
++# define OTGSC_HAAR BIT(2)
++# define OTGSC_VC BIT(1)
++# define OTGSC_VD BIT(0)
++# define OTGSC_INTEN_MASK (0x7f << 24)
++# define OTGSC_INTSTS_MASK (0x7f << 16)
++#define CI_USBMODE 0xf8
++# define USBMODE_CM (BIT(1) | BIT(0))
++# define USBMODE_IDLE 0
++# define USBMODE_DEVICE 0x2
++# define USBMODE_HOST 0x3
++
++#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI)
++
++struct otg_hsm {
++ /* Input */
++ int a_bus_resume;
++ int a_bus_suspend;
++ int a_conn;
++ int a_sess_vld;
++ int a_srp_det;
++ int a_vbus_vld;
++ int b_bus_resume;
++ int b_bus_suspend;
++ int b_conn;
++ int b_se0_srp;
++ int b_sess_end;
++ int b_sess_vld;
++ int id;
++
++ /* Internal variables */
++ int a_set_b_hnp_en;
++ int b_srp_done;
++ int b_hnp_enable;
++
++ /* Timeout indicator for timers */
++ int a_wait_vrise_tmout;
++ int a_wait_bcon_tmout;
++ int a_aidl_bdis_tmout;
++ int b_ase0_brst_tmout;
++ int b_bus_suspend_tmout;
++ int b_srp_res_tmout;
++
++ /* Informative variables */
++ int a_bus_drop;
++ int a_bus_req;
++ int a_clr_err;
++ int a_suspend_req;
++ int b_bus_req;
++
++ /* Output */
++ int drv_vbus;
++ int loc_conn;
++ int loc_sof;
++
++ /* Others */
++ int b_bus_suspend_vld;
++};
++
++#define TA_WAIT_VRISE 100
++#define TA_WAIT_BCON 30000
++#define TA_AIDL_BDIS 15000
++#define TB_ASE0_BRST 5000
++#define TB_SE0_SRP 2
++#define TB_SRP_RES 100
++#define TB_BUS_SUSPEND 500
++
++struct langwell_otg_timer {
++ unsigned long expires; /* Number of count increase to timeout */
++ unsigned long count; /* Tick counter */
++ void (*function)(unsigned long); /* Timeout function */
++ unsigned long data; /* Data passed to function */
++ struct list_head list;
++};
++
++struct langwell_otg {
++ struct otg_transceiver otg;
++ struct otg_hsm hsm;
++ void __iomem *regs;
++ unsigned region;
++ struct pci_driver *host_ops;
++ struct pci_driver *client_ops;
++ struct pci_dev *pdev;
++ struct work_struct work;
++ struct workqueue_struct *qwork;
++ spinlock_t lock;
++ spinlock_t wq_lock;
++};
++
++static inline struct langwell_otg *otg_to_langwell(struct otg_transceiver *otg)
++{
++ return container_of(otg, struct langwell_otg, otg);
++}
++
++#ifdef DEBUG
++#define otg_dbg(fmt, args...) \
++ printk(KERN_DEBUG fmt , ## args)
++#else
++#define otg_dbg(fmt, args...) \
++ do { } while (0)
++#endif /* DEBUG */
++#endif /* __LANGWELL_OTG_H__ */