[PATCH v4] usb: add Intel Poulsbo USB client controller driver [IUSBC]

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

 



Intel Poulsbo chipset is one for Intel MID(Mobile Internet Device)
platform. The chipset has a USB client controller on it, which is a
PCI peripheral and supports full speed and high speed USB data
transfers. It has three IN and three OUT configurable endpoints, in
addition to endpoint zero. The driver adds device mode support of the
controller to Linux USB gadget.

The gadgets supported includes:
 -- file storage gadget
 -- ether gadget
 -- g_zero gadget

 The tests passed include:
 - hotplug, with and without hubs.
 - g_file_storage: file copying for 12 hours at full speed and high
 			speed.
 - g_file_storage: USBCV chap9 tests.
 - g_file_storage: USBCV MSC tests.
 - g_ether: scp files for 12 hours at full speed and high speed.
 - g_ether: USBCV chap9 tests.

Signed-off-by: Xiaochen Shen <xiaochen.shen@xxxxxxxxx>
Signed-off-by: Helen Chen <helen.chen@xxxxxxxxx>
---
 drivers/usb/gadget/Kconfig        |   21 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    7 +
 drivers/usb/gadget/iusbc.c        | 2574 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/iusbc.h        |  207 +++
 5 files changed, 2810 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/iusbc.c
 create mode 100644 drivers/usb/gadget/iusbc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 3219d13..5b8f62b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -416,6 +416,27 @@ config USB_CI13XXX
 	default USB_GADGET
 	select USB_GADGET_SELECTED

+config USB_GADGET_IUSBC
+	boolean "Intel USB Client Controller"
+	depends on PCI
+	select USB_GADGET_DUALSPEED
+	help
+		Intel USB client controller is a PCI peripheral
+		which supports both full speed and high speed USB 2.0
+		data transfers.
+		It has three IN and three OUT configurable endpoints,
+		as well as endpoint zero (for control transfers).
+
+		Say "y" to link the driver statically, or "m" to build
+		a dynamically linked module called "iusbc" and force
+		all gadget drivers to also be dynamically linked.
+
+config USB_IUSBC
+	tristate
+	depends on USB_GADGET_IUSBC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_NET2280
 	boolean "NetChip 228x"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 39a51d7..8c71d80 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)	+= ci13xxx_udc.o
+obj-$(CONFIG_USB_IUSBC)		+= iusbc.o

 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/gadget_chips.h
b/drivers/usb/gadget/gadget_chips.h
index ec6d439..3ce80b1 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -163,6 +163,12 @@
 #define gadget_is_ci13xxx(g)	0
 #endif

+#ifdef CONFIG_USB_GADGET_IUSBC
+#define gadget_is_iusbc(g)	(!strcmp("iusbc", (g)->name))
+#else
+#define gadget_is_iusbc(g)	0
+#endif
+
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
 // ...
@@ -231,6 +237,8 @@ static inline int
usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x22;
 	else if (gadget_is_ci13xxx(gadget))
 		return 0x23;
+	else if (gadget_is_iusbc(gadget))
+		return 0x24;
 	return -ENOENT;
 }

diff --git a/drivers/usb/gadget/iusbc.c b/drivers/usb/gadget/iusbc.c
new file mode 100644
index 0000000..c1fe662
--- /dev/null
+++ b/drivers/usb/gadget/iusbc.c
@@ -0,0 +1,2574 @@
+/*
+ * Intel Poulsbo USB Client Controller Driver
+ * Copyright (C) 2006-08, Intel Corporation.
+ *
+ * Author: Xiaochen Shen <xiaochen.shen@xxxxxxxxx>
+ *	   Helen Chen <helen.chen@xxxxxxxxx>
+ *
+ * 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		/* messages on error and most fault paths */
+#undef	VERBOSE_DEBUG	/* extra debug messages (success too) */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/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>
+
+/* Power management */
+#include <linux/pm.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "iusbc.h"
+
+#define DRIVER_DESC	"Intel Poulsbo USB Client Controller Driver"
+#define DRIVER_VERSION	"Dec 2008"
+#define DRIVER_AUTHOR	"Xiaochen Shen, Helen Chen"
+
+#define DMA_ADDR_INVALID	(~(dma_addr_t)0)
+#define STEPPING_D2		0x07
+
+#define USBC_DELAY_POST_RESET 	5
+
+static const char driver_name[] = "iusbc";
+static const char driver_desc[] = DRIVER_DESC;
+
+static const char *const ep_name[] = {
+	"ep0-in", "ep0-out",
+	"ep1in-bulk", "ep1out-bulk",
+	"ep2in-int", "ep2out-int",
+	"ep3in-iso", "ep3out-iso",
+};
+
+/* module parameter */
+
+/*
+ * force_fullspeed -- device will be forced to full speed operation
+ * default value: 0 for high speed
+ */
+static int force_fullspeed;
+
+/* modprobe iusbc force_fullspeed=n */
+module_param(force_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(force_fullspeed, "true to force full speed mode");
+
+#ifdef VERBOSE_DEBUG
+static inline void print_all_registers(struct iusbc_regs *regs)
+{
+	unsigned	i, j;
+	/* device */
+	printk(KERN_DEBUG "Intel USB-C Memory Space Registers\n");
+	printk(KERN_DEBUG "Register Length: 0x%x\n",
+			sizeof(struct iusbc_regs));
+	printk(KERN_DEBUG "gcap=0x%08x\n", readl(&regs->gcap));
+	printk(KERN_DEBUG "dev_sts=0x%08x\n", readl(&regs->dev_sts));
+	printk(KERN_DEBUG "frame=0x%04x\n", readw(&regs->frame));
+	printk(KERN_DEBUG "int_sts=0x%08x\n", readl(&regs->int_sts));
+	printk(KERN_DEBUG "int_ctrl=0x%08x\n", readl(&regs->int_ctrl));
+	printk(KERN_DEBUG "dev_ctrl=0x%08x\n", readl(&regs->dev_ctrl));
+
+	/* endpoints */
+	for (i = 0; i < 5; i++) {
+		printk(KERN_DEBUG "ep[%d]_base_low_32=0x%08x\n",
+				i, readl(&regs->ep[i].ep_base_low_32));
+		printk(KERN_DEBUG "ep[%d]_base_hi_32=0x%08x\n",
+				i, readl(&regs->ep[i].ep_base_hi_32));
+		printk(KERN_DEBUG "ep[%d]_len=0x%04x\n",
+				i, readw(&regs->ep[i].ep_len));
+		printk(KERN_DEBUG "ep[%d]_pib=0x%04x\n",
+				i, readw(&regs->ep[i].ep_pib));
+		printk(KERN_DEBUG "ep[%d]_dil=0x%04x\n",
+				i, readw(&regs->ep[i].ep_dil));
+		printk(KERN_DEBUG "ep[%d]_tiq=0x%04x\n",
+				i, readw(&regs->ep[i].ep_tiq));
+		printk(KERN_DEBUG "ep[%d]_max=0x%04x\n",
+				i, readw(&regs->ep[i].ep_max));
+		printk(KERN_DEBUG "ep[%d]_sts=0x%04x\n",
+				i, readw(&regs->ep[i].ep_sts));
+		printk(KERN_DEBUG "ep[%d]_cfg=0x%04x\n",
+				i, readw(&regs->ep[i].ep_cfg));
+
+		if (1 == i) {	/* ep0 out */
+			printk(KERN_DEBUG
+				"ep-out setup_pkt_sts=0x%02x\n",
+				readb(&regs->ep[i].setup_pkt_sts));
+			for (j = 0; j < 8; j++) {
+				printk(KERN_DEBUG "ep0 out "
+				"setup_pkt[%d]=0x%02x\n", j,
+				readb(&regs->ep[i].setup_pkt[j]));
+			}
+		}
+	}
+}
+#endif /* VERBOSE_DEBUG */
+
+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined(DEBUG)
+static char *type_string(u8 bmAttributes)
+{
+	switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:
+		return "bulk";
+	case USB_ENDPOINT_XFER_ISOC:
+		return "iso";
+	case USB_ENDPOINT_XFER_INT:
+		return "int";
+	};
+	return "control";
+}
+#endif
+
+/* configure endpoint, making it usable */
+static int iusbc_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct iusbc		*dev;
+	struct iusbc_ep		*ep;
+	u16			val_16, max;
+	u8			val_8;
+	unsigned long		flags;
+	unsigned		i;
+	int			retval;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+
+	if (!_ep || !desc || strcmp(_ep->name, ep_name[0]) == 0
+			|| strcmp(_ep->name, ep_name[1]) == 0
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* wMaxPacketSize up to 1024 bytes */
+	max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ep->ep.maxpacket = max;
+
+	if (!ep->desc)
+		ep->desc = desc;
+
+	/* ep_reset() has already been called */
+	ep->stopped = 0;
+	ep->wedged = 0;
+	ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
+
+	/* sanity check type, direction, address */
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	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;
+		}
+		break;
+	default:
+		goto done;
+	}
+
+	/* ep_type */
+	ep->ep_type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+
+	/* DMA modes, only support Linear Mode now */
+	if (ep->dev->sg_mode_dma)
+		ep->dma_mode = SCATTER_GATHER_MODE;
+	else
+		ep->dma_mode = LINEAR_MODE;
+
+	/* reset ep registers to default value */
+	i = ep->num;
+	writew(0, &dev->regs->ep[i].ep_cfg);
+
+	/* set endpoints valid */
+	val_16 = readw(&dev->regs->ep[i].ep_cfg);
+	val_16 |= INTR_BAD_PID_TYPE
+		| INTR_CRC_ERROR
+		| INTR_FIFO_ERROR
+		| INTR_DMA_ERROR
+		| INTR_TRANS_COMPLETE
+		/* | INTR_PING_NAK_SENT */
+		| INTR_DMA_IOC
+		| ep->dma_mode << 6
+		| ep->ep_type << 4
+		/* | EP_ENABLE */
+		| EP_VALID;
+
+	/* will set EP_ENABLE later to start dma */
+	writew(val_16, &dev->regs->ep[i].ep_cfg);
+
+	val_16 = readw(&dev->regs->ep[i].ep_cfg);
+	dev_vdbg(&dev->pdev->dev, "%s.ep_cfg = 0x%04x\n",
+		_ep->name, val_16);
+
+	val_8 = desc->bEndpointAddress;
+	retval = 0;
+done:
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return retval;
+}
+
+static const struct usb_ep_ops iusbc_ep_ops;
+
+static void
+ep_reset(struct iusbc_regs __iomem *regs, struct iusbc_ep *ep)
+{
+	unsigned	i = ep->num;
+
+	/* reset ep values */
+	ep->stopped = 1;
+	ep->ep.maxpacket = ~0;
+	ep->ep.ops = &iusbc_ep_ops;
+
+	/* reset ep registers to default value
+	 * clear all interrupt and status
+	 * clear and reset all DMA FIFOs and state machine
+	 * hardware shall minimize power usage
+	 */
+	writew(0, &regs->ep[i].ep_cfg);
+}
+
+static void nuke(struct iusbc_ep *);
+
+static int iusbc_ep_disable(struct usb_ep *_ep)
+{
+	struct iusbc_ep		*ep;
+	unsigned long		flags;
+	struct iusbc		*dev;
+	unsigned		i;
+	u16			val_16;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+
+	if (!_ep || !ep->desc
+		|| strcmp(_ep->name, ep_name[0]) == 0
+		|| strcmp(_ep->name, ep_name[1]) == 0)
+		return -EINVAL;
+
+	dev = ep->dev;
+	if (dev->ep0state == EP0_SUSPEND)
+		return -EBUSY;
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	nuke(ep);
+	ep_reset(ep->dev->regs, ep);
+
+	i = ep->num;
+	/* display endpoint configuration register */
+	val_16 = readw(&dev->regs->ep[i].ep_cfg);
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+/* allocate a request object to use with this endpoint */
+static struct usb_request *
+iusbc_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct iusbc_ep		*ep;
+	struct iusbc_request	*req;
+	struct iusbc_dma	*td;
+
+	if (!_ep)
+		return NULL;
+	ep = container_of(_ep, struct iusbc_ep, ep);
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->req.dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD(&req->queue);
+
+	/* this dma descriptor may be swapped with the previous dummy */
+	td = pci_pool_alloc(ep->dev->requests,
+			gfp_flags,
+			&req->td_dma);
+
+	if (!td) {
+		kfree(req);
+		return NULL;
+	}
+
+	td->dmacount = 0;	/* not VALID */
+	td->dmaaddr = __constant_cpu_to_le32(DMA_ADDR_INVALID);
+	req->td = td;
+
+	return &req->req;
+}
+
+/* frees a request object */
+static void
+iusbc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct iusbc_ep		*ep;
+	struct iusbc_request	*req;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+	if (!_ep || !_req)
+		return;
+
+	req = container_of(_req, struct iusbc_request, req);
+
+	WARN_ON(!list_empty(&req->queue));
+
+	if (req->td)
+		pci_pool_free(ep->dev->requests, req->td, req->td_dma);
+	kfree(req);
+}
+
+struct free_record {
+	struct list_head	list;
+	struct device		*dev;
+	unsigned		bytes;
+	dma_addr_t		dma;
+};
+
+/* fill out dma descriptor to match a given request */
+static void fill_dma(struct iusbc_ep *ep, struct iusbc_request *req)
+{
+	struct iusbc_dma	*td = req->td;
+	u16			dmacount;
+
+	dmacount = req->req.length;
+
+	td->dmaaddr = cpu_to_le32(req->req.dma);
+	td->dmacount = cpu_to_le16(dmacount);
+}
+
+static void start_dma(struct iusbc_ep *ep, struct iusbc_request *req)
+{
+	u16		val_16;
+	u32		val_32;
+	unsigned	i;
+
+	i = ep->num;
+
+	/* init req->td, pointing to the current dummy */
+	fill_dma(ep, req);
+
+	/* ep_base_low_32 */
+	writel(cpu_to_le32(req->req.dma),
+		&ep->dev->regs->ep[i].ep_base_low_32);
+	val_32 = readl(&ep->dev->regs->ep[i].ep_base_low_32);
+
+	/* ep_base_hi_32 */
+	writel(0, &ep->dev->regs->ep[i].ep_base_hi_32);
+	val_32 = readl(&ep->dev->regs->ep[i].ep_base_hi_32);
+
+	writew(le16_to_cpu(req->td->dmacount),
+		&ep->dev->regs->ep[i].ep_len);
+	val_16 = readw(&ep->dev->regs->ep[i].ep_len);
+
+	/* endpoint maximum transaction size, up to 1024 Bytes */
+	writew((ep->ep.maxpacket & 0x3ff),
+		&ep->dev->regs->ep[i].ep_max);
+	val_16 = readw(&ep->dev->regs->ep[i].ep_max);
+
+	/* validate endpoint, enable DMA */
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+	val_16 |= EP_VALID | EP_ENABLE;
+	writew(val_16, &ep->dev->regs->ep[i].ep_cfg);
+
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+}
+
+/* queues I/O requests in endpoint queue  */
+static inline void
+queue_dma(struct iusbc_ep *ep, struct iusbc_request *req)
+{
+	struct iusbc_dma	*end;
+	dma_addr_t		tmp_dma_addr;
+
+	/* swap new dummy for old, link; fill and maybe activate */
+	end = ep->dummy;
+	ep->dummy = req->td;
+	req->td = end;
+
+	tmp_dma_addr = ep->td_dma;
+	ep->td_dma = req->td_dma;
+	req->td_dma = tmp_dma_addr;
+
+	fill_dma(ep, req);
+}
+
+static void
+done(struct iusbc_ep *ep, struct iusbc_request *req, int status)
+__releases(ep->dev->lock)
+__acquires(ep->dev->lock)
+{
+	struct iusbc		*dev;
+	unsigned		stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	if (req->mapped) {
+		pci_unmap_single(dev->pdev, req->req.dma,
+		req->req.length,
+		ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	}
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+
+	spin_unlock(&dev->lock);
+	if (req->req.complete)
+		req->req.complete(&ep->ep, &req->req);
+	spin_lock(&dev->lock);
+	ep->stopped = stopped;
+}
+
+/* queues (submits) an I/O requests to an endpoint */
+static int iusbc_queue(struct usb_ep *_ep,
+			struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct iusbc_request	*req;
+	struct iusbc_ep		*ep;
+	struct iusbc		*dev;
+	unsigned long		flags;
+	u16			val_16;
+	unsigned		zlflag = 0;
+
+	/* always require a cpu-view buffer */
+	req = container_of(_req, struct iusbc_request, req);
+	ep = container_of(_ep, struct iusbc_ep, ep);
+	dev = ep->dev;
+
+	if (!_req || !_req->complete || !_req->buf
+			|| !list_empty(&req->queue)) {
+		return -EINVAL;
+	}
+
+	if (!_ep || (!ep->desc && ep->num > 1))
+		return -ESHUTDOWN;
+
+	if (dev->ep0state == EP0_DISCONNECT)
+		return -ESHUTDOWN;
+
+	if (unlikely(!dev->driver ||
+			dev->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	/* can't touch registers when suspended */
+	if (dev->ep0state == EP0_SUSPEND)
+		return -EBUSY;
+
+	/* 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) {
+			zlflag = 1;
+			_req->length++;
+		}
+
+		_req->dma = pci_map_single(dev->pdev, _req->buf,
+		_req->length,
+		ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+
+		if (zlflag && (_req->length == 1)) {
+			zlflag = 0;
+			_req->length = 0;
+		}
+
+		req->mapped = 1;
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* set ZLP flag for ep0 in, when writting data,
+	 * makes the last packet be "short" by adding a zero
+	 * length packet as needed
+	 */
+	if (unlikely(ep->num == 0 && ep->is_in))
+		_req->zero = 1;
+
+	/* kickstart this I/O queue */
+	if (list_empty(&ep->queue) && !ep->stopped) {
+		start_dma(ep, req);
+		val_16 = readw(&ep->dev->regs->ep[ep->num].ep_pib);
+		val_16 = readw(&ep->dev->regs->ep[ep->num].ep_sts);
+	} else
+		queue_dma(ep, req);
+	if (likely(req != NULL))
+		list_add_tail(&req->queue, &ep->queue);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 0;
+}
+
+static inline void
+dma_done(struct iusbc_ep *ep, struct iusbc_request *req, int status)
+{
+	unsigned	i;
+
+	i = ep->num;
+	req->req.actual = readw(&ep->dev->regs->ep[i].ep_pib);
+	done(ep, req, status);
+}
+
+/* restart dma in endpoint */
+static void restart_dma(struct iusbc_ep *ep)
+{
+	struct iusbc_request	*req;
+	if (ep->stopped)
+		return;
+	req = list_entry(ep->queue.next, struct iusbc_request, queue);
+	start_dma(ep, req);
+	return;
+}
+
+/* dequeue ALL requests */
+static void nuke(struct iusbc_ep *ep)
+{
+	struct iusbc_request	*req;
+	/* called with spinlock held */
+	ep->stopped = 1;
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next,
+				struct iusbc_request,
+				queue);
+		done(ep, req, -ESHUTDOWN);
+	}
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int iusbc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct iusbc_ep		*ep;
+	struct iusbc		*dev;
+	struct iusbc_request	*req;
+	unsigned long		flags;
+	int			stopped;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+	if (!_ep || (!ep->desc && ep->num > 1) || !_req)
+		return -EINVAL;
+
+	dev = ep->dev;
+	if (!dev->driver)
+		return -ESHUTDOWN;
+
+	/* can't touch registers when suspended */
+	if (dev->ep0state == EP0_SUSPEND)
+		return -EBUSY;
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	stopped = ep->stopped;
+
+	/* quiesce dma while we patch the queue */
+	ep->stopped = 1;
+
+	/* make sure it's still queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->dev->lock, flags);
+		return -EINVAL;
+	}
+
+	/* queue head may be partially complete. */
+	if (ep->queue.next == &req->queue) {
+		dev_dbg(&ep->dev->pdev->dev, "unlink (%s) dma\n",
+			_ep->name);
+		_req->status = -ECONNRESET;
+		if (likely(ep->queue.next == &req->queue)) {
+			req->td->dmacount = 0;	/* invalidate */
+			dma_done(ep, req, -ECONNRESET);
+		}
+		req = NULL;
+	}
+
+	if (req)
+		done(ep, req, -ECONNRESET);
+
+	ep->stopped = stopped;
+
+	if (!list_empty(&ep->queue) && (!ep->stopped)) {
+		/* resume current request, or start new one */
+		if (!req)
+			start_dma(ep, list_entry(ep->queue.next,
+				struct iusbc_request, queue));
+	}
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+static void ep0_start(struct iusbc *dev);
+static void ep_ack(struct iusbc_ep *ep);
+static int iusbc_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc);
+
+static void clear_halt(struct iusbc_ep *ep)
+__releases(ep->dev->lock)
+__acquires(ep->dev->lock)
+{
+	u16		 	val_16;
+	unsigned		i = ep->num;
+	int			rc = 0;
+	struct iusbc_request	*req;
+	const struct usb_endpoint_descriptor	*desc;
+
+	/* validate and enable endpoint */
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+	val_16 |= EP_VALID | EP_ENABLE;
+	writew(val_16, &ep->dev->regs->ep[i].ep_cfg);
+
+	/* re-enable endpoint */
+	if (i < 2) {	/* ep0 in and out */
+		ep0_start(ep->dev);
+	} else {
+		spin_unlock_irq(&ep->dev->lock);
+		/* remember ep->desc */
+		desc = ep->desc;
+		rc = iusbc_ep_enable(&ep->ep, desc);
+		spin_lock_irq(&ep->dev->lock);
+	}
+
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+	ep->stopped = 0;
+	if (list_empty(&ep->queue))
+		return;
+	req = list_entry(ep->queue.next, struct iusbc_request, queue);
+	start_dma(ep, req);
+}
+
+static void set_halt(struct iusbc_ep *ep)
+{
+	u16		val_16;
+	unsigned	i = ep->num;
+
+	/* reset data buffer zero length */
+	writew(0, &ep->dev->regs->ep[i].ep_len);
+	/* invalidate and disable endpoint */
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+	val_16 &= (~EP_VALID & ~EP_ENABLE);
+	writew(val_16, &ep->dev->regs->ep[i].ep_cfg);
+	ep->stopped = 1;
+}
+
+static int iusbc_fifo_status(struct usb_ep *_ep);
+
+static int
+iusbc_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+	struct iusbc_ep		*ep;
+	unsigned long		flags;
+	int			retval = 0;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+	if (!_ep || (!ep->desc && ep->num > 1))
+		return -EINVAL;
+	if (!ep->dev->driver ||
+		ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+	if (ep->desc && (ep->desc->bmAttributes & 0x03)
+			== USB_ENDPOINT_XFER_ISOC)
+		return -EINVAL;
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	/* transfer requests are still queued */
+	if (!list_empty(&ep->queue))
+		retval = -EAGAIN;
+	else if (ep->is_in && value && iusbc_fifo_status(_ep) != 0) {
+		/* FIFO holds bytes, the host hasn't collected */
+		dev_dbg(&ep->dev->pdev->dev, "%s FIFO holds bytes\n",
+			_ep->name);
+
+		/* reset position in buffer register */
+		writew(0, &ep->dev->regs->ep[ep->num].ep_pib);
+
+		retval = -EAGAIN;
+	} else {
+		if (value) {
+			if (ep->num < 2) /* ep0 should not halt. */
+				ep->dev->ep0state = EP0_STALL;
+			else
+				set_halt(ep);
+			if (wedged)
+				ep->wedged = 1;
+		} else {
+			clear_halt(ep);
+			ep->wedged = 0;
+		}
+
+	}
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return retval;
+}
+
+static int iusbc_set_halt(struct usb_ep *_ep, int value)
+{
+	return iusbc_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int iusbc_set_wedge(struct usb_ep *_ep)
+{
+	if (!_ep || strcmp(_ep->name, ep_name[0]) == 0
+		|| strcmp(_ep->name, ep_name[1]) == 0)
+		return -EINVAL;
+	return iusbc_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static int iusbc_fifo_status(struct usb_ep *_ep)
+{
+	struct iusbc_ep		*ep;
+	unsigned		i;
+	u16			nbytes, fifo_size;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+	if (!_ep || (!ep->desc && ep->num > 1))
+		return -ENODEV;
+	if (!ep->dev->driver ||
+		ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+	i = ep->num;
+	fifo_size = readw(&ep->dev->regs->ep[i].ep_len);
+	nbytes = readw(&ep->dev->regs->ep[i].ep_pib);
+
+	if (nbytes > fifo_size)
+		return -EOVERFLOW;
+	return nbytes;
+}
+
+static void ep_nak(struct iusbc_ep *ep);
+
+static void iusbc_fifo_flush(struct usb_ep *_ep)
+{
+	struct iusbc_ep		*ep;
+	unsigned		i;
+	u16			val_16;
+
+	ep = container_of(_ep, struct iusbc_ep, ep);
+	if (!_ep || (!ep->desc && ep->num > 1))
+		return;
+	if (!ep->dev->driver ||
+		ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return;
+	i = ep->num;
+	ep_nak(ep);
+
+	/* reset position in buffer register */
+	val_16 = readw(&ep->dev->regs->ep[i].ep_pib);
+	dev_dbg(&ep->dev->pdev->dev, "%s.ep_pib = 0x%04x\n",
+		_ep->name, val_16);
+	writew(0, &ep->dev->regs->ep[i].ep_pib);
+}
+
+static const struct usb_ep_ops iusbc_ep_ops = {
+	.enable		= iusbc_ep_enable,
+	.disable	= iusbc_ep_disable,
+
+	.alloc_request	= iusbc_alloc_request,
+	.free_request	= iusbc_free_request,
+
+	.queue		= iusbc_queue,
+	.dequeue	= iusbc_dequeue,
+
+	.set_halt	= iusbc_set_halt,
+	.set_wedge	= iusbc_set_wedge,
+
+	.fifo_status	= iusbc_fifo_status,
+	.fifo_flush	= iusbc_fifo_flush,
+};
+
+/* returns the current frame number */
+static int iusbc_get_frame(struct usb_gadget *_gadget)
+{
+	struct iusbc		*dev;
+	unsigned long		flags;
+	u16			retval;
+
+	if (!_gadget)
+		return -ENODEV;
+
+	dev = container_of(_gadget, struct iusbc, gadget);
+	spin_lock_irqsave(&dev->lock, flags);
+	retval = readw(&dev->regs->frame);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return retval;
+}
+
+/* software-controlled connect/disconnect to USB host */
+static int iusbc_pullup(struct usb_gadget *_gadget, int is_on)
+{
+	struct iusbc	*dev;
+	u32             val_32;
+	unsigned long   flags;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of(_gadget, struct iusbc, gadget);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	val_32 = readl(&dev->regs->dev_ctrl);
+	dev->connected = (is_on != 0);
+	if (is_on)
+		val_32 |= CONNECTION_ENABLE;
+	else
+		val_32 &= ~CONNECTION_ENABLE;
+
+	writel(val_32, &dev->regs->dev_ctrl);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+
+static const struct usb_gadget_ops iusbc_ops = {
+
+	/* returns the current frame number */
+	.get_frame	= iusbc_get_frame,
+
+	/* software-controlled connect/disconnect to USB host */
+	.pullup		= iusbc_pullup,
+};
+
+#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
+
+/* "function" sysfs attribute */
+static ssize_t show_function(struct device *_dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct iusbc	*dev = dev_get_drvdata(_dev);
+
+	if (!dev->driver
+		|| !dev->driver->function
+		|| strlen(dev->driver->function) > PAGE_SIZE)
+		return 0;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+
+
+static ssize_t show_registers(struct device *_dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct iusbc		*dev;
+	char			*next;
+	unsigned		size;
+	unsigned		t;
+	unsigned		i;
+	unsigned long		flags;
+	const char		*name;
+	const char		*speed;
+	u32			dev_sts;
+	const char		*stepping;
+
+	dev = dev_get_drvdata(_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->stepping >= STEPPING_D2)
+		stepping = "D2";
+	else
+		stepping = "D1";
+
+	if (dev->driver)
+		name = dev->driver->driver.name;
+	else
+		name = "(none)";
+
+
+	dev_sts = readl(&dev->regs->dev_sts);
+	if (dev->gadget.speed == USB_SPEED_HIGH
+			&& (dev_sts & CONNECTED))
+		speed = "high speed";
+	else if (dev->gadget.speed == USB_SPEED_FULL
+			&& (dev_sts & CONNECTED))
+		speed = "full speed";
+	else
+		speed = "unknown speed";
+
+	/* device information */
+	t = scnprintf(next, size,
+			"%s %s - %s\n"
+			"Stepping: %s\n"
+			"Version: %s\n"
+			"Gadget driver: %s\n"
+			"Speed mode: %s\n",
+			driver_name, pci_name(dev->pdev), driver_desc,
+			stepping,
+			DRIVER_VERSION,
+			name,
+			speed);
+	size -= t;
+	next += t;
+
+	/* device memory space registers */
+	t = scnprintf(next, size,
+			"\nDevice registers:\n"
+			"\tgcap=0x%08x\n"
+			"\tdev_sts=0x%08x\n"
+			"\tframe=0x%04x\n"
+			"\tint_sts=0x%08x\n"
+			"\tint_ctrl=0x%08x\n"
+			"\tdev_ctrl=0x%08x\n",
+			readl(&dev->regs->gcap),
+			readl(&dev->regs->dev_sts),
+			readw(&dev->regs->frame),
+			readl(&dev->regs->int_sts),
+			readl(&dev->regs->int_ctrl),
+			readl(&dev->regs->dev_ctrl)
+			);
+	size -= t;
+	next += t;
+
+	/* endpoints memory space registers */
+	t = scnprintf(next, size, "\nEndpoints registers:\n");
+	size -= t;
+	next += t;
+
+	for (i = 0; i < 5; i++) {
+		struct iusbc_ep		*ep;
+		ep = &dev->ep[i];
+
+		if (i > 1 && !ep->desc)
+			continue;
+
+		name = ep->ep.name;
+		t = scnprintf(next, size,
+			"\t%s.ep_base_low_32=0x%08x\n"
+			"\t%s.ep_base_hi_32=0x%08x\n"
+			"\t%s.ep_len=0x%04x\n"
+			"\t%s.ep_pib=0x%04x\n"
+			"\t%s.ep_dil=0x%04x\n"
+			"\t%s.ep_tiq=0x%04x\n"
+			"\t%s.ep_max=0x%04x\n"
+			"\t%s.ep_sts=0x%04x\n"
+			"\t%s.ep_cfg=0x%04x\n",
+			name, readl(&dev->regs->ep[i].ep_base_low_32),
+			name, readl(&dev->regs->ep[i].ep_base_hi_32),
+			name, readw(&dev->regs->ep[i].ep_len),
+			name, readw(&dev->regs->ep[i].ep_pib),
+			name, readw(&dev->regs->ep[i].ep_dil),
+			name, readw(&dev->regs->ep[i].ep_tiq),
+			name, readw(&dev->regs->ep[i].ep_max),
+			name, readw(&dev->regs->ep[i].ep_sts),
+			name, readw(&dev->regs->ep[i].ep_cfg));
+		size -= t;
+		next += t;
+	}
+
+	/* ep0 out setup packet registers */
+	t = scnprintf(next, size,
+			"\tsetup_pkt_sts=0x%02x\n",
+			readb(&dev->regs->ep[1].setup_pkt_sts));
+	size -= t;
+	next += t;
+
+	for (i = 0; i < 8; i++) {
+		t = scnprintf(next, size,
+				"\tsetup_pkt[%d]=0x%02x\n",
+				i,
+				readb(&dev->regs->ep[1].setup_pkt[i])
+				);
+		size -= t;
+		next += t;
+	}
+
+	/* Irq statistics */
+	t = scnprintf(next, size, "\nIrq statistics:\n");
+	size -= t;
+	next += t;
+
+	for (i = 0; i < 5; i++) {
+		struct iusbc_ep	*ep;
+		ep = &dev->ep[i];
+
+		if (i && !ep->irqs)
+			continue;
+
+		t = scnprintf(next, size,
+				"\t%s/%lu\n",
+				ep->ep.name, ep->irqs);
+		size -= t;
+		next += t;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+
+
+static ssize_t show_queues(struct device *_dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct iusbc		*dev;
+	char			*next;
+	unsigned		size, i;
+	unsigned long		flags;
+
+	dev = dev_get_drvdata(_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave(&dev->lock, flags);
+
+	for (i = 0; i < 5; i++) {
+		struct iusbc_ep		*ep = &dev->ep[i];
+		struct iusbc_request	*req;
+		struct iusbc_dma	*td;
+		int			t;
+		int			addr;
+
+		if (i > 1 && !ep->desc)
+			continue;
+
+		/* ep0 in/out */
+		if (i == 0 || i == 1) {
+			t = scnprintf(next, size,
+				"%s (ep%d%s-%s), "
+				"max %04x, dma_mode: %02x\n",
+				ep->ep.name,
+				0,
+				ep->is_in ? "in" : "out",
+				"control",
+				ep->ep.maxpacket,
+				ep->dma_mode);
+		} else {
+			addr = ep->desc->bEndpointAddress;
+			t = scnprintf(next, size,
+				"\n%s (ep%d%s-%s), "
+				"max %04x, dma_mode: %02x\n",
+				ep->ep.name,
+				addr & USB_ENDPOINT_NUMBER_MASK,
+				DIR_STRING(addr),
+				type_string(ep->desc->bmAttributes),
+				ep->ep.maxpacket,
+				ep->dma_mode);
+		}
+
+		if (t <= 0 || t > size)
+			goto done;
+
+		size -= t;
+		next += t;
+
+		if (list_empty(&ep->queue)) {
+			t = scnprintf(next, size,
+					"\t(nothing queued)\n");
+			if (t <= 0 || t > size)
+				goto done;
+
+			size -= t;
+			next += t;
+			continue;
+		}
+
+		list_for_each_entry(req, &ep->queue, queue) {
+			t = scnprintf(next, size,
+				"\treq %p, len %u/%u, "
+				"buf %p, dma 0x%08x)\n",
+				&req->req, req->req.actual,
+				req->req.length, req->req.buf,
+				req->req.dma);
+
+			if (t <= 0 || t > size)
+				goto done;
+
+			size -= t;
+			next += t;
+
+			td = req->td;
+			t = scnprintf(next, size, "\ttd 0x%08x, "
+				" count 0x%08x, buf 0x%08x\n",
+				(u32) req->td_dma,
+				le32_to_cpu(td->dmacount),
+				le32_to_cpu(td->dmaaddr));
+
+			if (t <= 0 || t > size)
+				goto done;
+
+			size -= t;
+			next += t;
+		}
+	}
+
+done:
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(queues, S_IRUGO, show_queues, NULL);
+
+#else
+
+#define device_create_file(a, b)	(0)
+#define device_remove_file(a, b)	do { } while (0)
+
+#endif	/*CONFIG_USB_GADGET_DEBUG_FILES */
+
+/* global variable */
+static struct iusbc	*the_controller;
+
+static void iusbc_reset(struct iusbc *dev)
+{
+	/* disable irqs */
+	writel(0, &dev->regs->int_ctrl);
+
+	/* set device power stauts */
+	dev->powered = 1;
+
+	/* set device remote wakeup flag */
+	dev->enable_wakeup = 0;
+
+	/* 16 bits status data for GET_STATUS */
+	dev->status_d = 0;
+}
+
+static void iusbc_reinit(struct iusbc *dev)
+{
+	unsigned	i;
+
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+	/* ep0-in */
+	dev->gadget.ep0 = &dev->ep[0].ep;
+
+	/* init ep0 in and ep0 out driver_data */
+	dev->ep[0].ep.driver_data = get_gadget_data(&dev->gadget);
+	dev->ep[1].ep.driver_data = get_gadget_data(&dev->gadget);
+
+	dev->ep0state = EP0_DISCONNECT;
+
+	/* basic endpoint init */
+	/* 2 ep0, 3 data ep */
+	for (i = 0; i < 5; i++) {
+		struct iusbc_ep	*ep = &dev->ep[i];
+		ep->dev = dev;
+
+		INIT_LIST_HEAD(&ep->queue);
+
+		ep->desc = NULL;
+		ep->num = i;
+		ep->stopped = 1;
+		ep->ep.name = ep_name[i];
+		ep->ep.maxpacket = ~0;
+		ep->ep.ops = &iusbc_ep_ops;
+
+		list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+		ep_reset(dev->regs, ep);
+	}
+
+	/* set ep0 maxpacket */
+	dev->ep[0].ep.maxpacket = 64; /* ep0_in */
+	dev->ep[1].ep.maxpacket = 64; /* ep0_out */
+
+	dev->ep[0].stopped = 0;
+	dev->ep[1].stopped = 0;
+
+	list_del_init(&dev->ep[0].ep.ep_list);
+	list_del_init(&dev->ep[1].ep.ep_list);
+}
+
+
+static void ep0_start(struct iusbc *dev)
+{
+	u16		val_16;
+	u32		val_32;
+	unsigned	i;
+
+	iusbc_reset(dev);
+	iusbc_reinit(dev);
+
+	for (i = 0; i < 2; i++) {
+		struct iusbc_ep	*ep = &dev->ep[i];
+
+		/* ep[0]: ep0-in, ep[1]: ep0-out */
+		ep->is_in = (i == 0 ? 1 : 0);
+
+		/* ep0 ep_type */
+		ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+		/* linear mode only, control mode is useless */
+		ep->dma_mode = LINEAR_MODE;
+
+		/* reset ep0 in/out registers to default value */
+		writew(0, &dev->regs->ep[i].ep_cfg);
+
+		/* set ep0 in/out endpoints valid */
+		val_16 = readw(&dev->regs->ep[i].ep_cfg);
+		val_16 |= INTR_BAD_PID_TYPE
+			| INTR_CRC_ERROR
+			| INTR_FIFO_ERROR
+			| INTR_DMA_ERROR
+			| INTR_TRANS_COMPLETE
+			/* | INTR_PING_NAK_SENT */
+			| INTR_DMA_IOC
+			| ep->dma_mode << 6
+			| ep->ep_type << 4
+			/* | EP_ENABLE */
+			| EP_VALID;
+
+		writew(val_16, &dev->regs->ep[i].ep_cfg);
+
+		val_16 = readw(&dev->regs->ep[i].ep_cfg);
+	}
+
+	/* enable irqs */
+	val_32 = readl(&dev->regs->int_ctrl);
+	val_32 |= RESET_INTR_ENABLE
+		| CONNECT_INTR_ENABLE
+		| SUSPEND_INTR_ENABLE
+		/* | EP3_OUT_INTR_ENABLE */
+		/* | EP3_IN_INTR_ENABLE */
+		/* | EP2_OUT_INTR_ENABLE */
+		| EP2_IN_INTR_ENABLE
+		| EP1_OUT_INTR_ENABLE
+		| EP1_IN_INTR_ENABLE
+		| EP0_OUT_INTR_ENABLE
+		| EP0_IN_INTR_ENABLE;
+
+	writel(val_32, &dev->regs->int_ctrl);
+	val_32 = readl(&dev->regs->int_ctrl);
+	dev->ep0state = EP0_IDLE;
+}
+
+static void device_start(struct iusbc *dev)
+{
+	u32		val_32;
+	struct device	*dbg_dev;
+
+	/* reset all registers */
+	writel(0, &dev->regs->dev_ctrl);
+
+	/* PCI enable: write 1 to DeviceEnable */
+	writel(DEVICE_ENABLE, &dev->regs->dev_ctrl);
+	/* within 5 ms the DeviceEnable bit is suppose to be 1. */
+	mdelay(USBC_DELAY_POST_RESET);
+	val_32 = readl(&dev->regs->dev_ctrl);
+	dbg_dev = &dev->pdev->dev;
+	if (!(val_32 & DEVICE_ENABLE))
+		dev_err(dbg_dev, "hardware reset error\n");
+
+	/* hardware transfer to running state now */
+	val_32 |= DEVICE_ENABLE
+		/* | CONNECTION_ENABLE */
+		/* | SIGNAL_RESUME */
+		| CHARGE_ENABLE;
+
+	/* module parameter: force_fullspeed */
+	if (force_fullspeed) {
+		val_32 |= FORCE_FULLSPEED;
+		dev->force_fullspeed = 1;
+	} else
+		dev->force_fullspeed = 0;
+
+	writel(val_32, &dev->regs->dev_ctrl);
+
+	if (val_32 & CONNECTED) {
+		dev->connected = 1;
+		dev_vdbg(dbg_dev, "device_start: USB attached\n");
+	} else {
+		dev->connected = 0;
+		dev_vdbg(dbg_dev, "device_start: USB detached\n");
+	}
+	if (val_32 & SUSPEND)
+		dev->suspended = 1;
+	else
+		dev->suspended = 0;
+
+	/* set device reset flag */
+	dev->is_reset = 0;
+	iusbc_pullup(&dev->gadget, 1);
+	/* enable ep0 and host detection */
+	ep0_start(dev);
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct iusbc		*dev = the_controller;
+	int			retval;
+	unsigned		i;
+
+	if (!driver || !driver->bind
+			|| !driver->disconnect || !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+
+	dev->irqs = 0;
+
+	/* 2 ep0, 3 data eps */
+	for (i = 0; i < 5; i++)
+		dev->ep[i].irqs = 0;
+
+	/* hook up the driver ... */
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+	dev->gadget.dev.driver = &driver->driver;
+
+	retval = driver->bind(&dev->gadget);
+	if (retval) {
+		dev_dbg(&dev->pdev->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;
+
+	retval = device_create_file(&dev->pdev->dev, &dev_attr_queues);
+	if (retval)
+		goto err_func;
+
+	device_start(dev);
+	return 0;
+
+err_func:
+	device_remove_file(&dev->pdev->dev, &dev_attr_function);
+
+err_unbind:
+	driver->unbind(&dev->gadget);
+	dev->gadget.dev.driver = NULL;
+	dev->driver = NULL;
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void
+stop_activity(struct iusbc *dev, struct usb_gadget_driver *driver)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	unsigned	i;
+
+	/* don't disconnect if it's not connected */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+
+	/* stop hardware; prevent new request submissions;
+	 * and kill any outstanding requests.
+	 */
+	iusbc_reset(dev);
+
+	/* Mark ep0state to disconnect to avoid upper layer to start
+	 * next transfer immediately after the following nuke operation
+	 */
+	dev->ep0state = EP0_DISCONNECT;
+
+	/* 2 ep0, 3 data ep */
+	for (i = 0; i < 5; i++)
+		nuke(&dev->ep[i]);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&dev->lock);
+		driver->disconnect(&dev->gadget);
+		spin_lock(&dev->lock);
+	}
+
+	iusbc_reinit(dev);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct iusbc	*dev = the_controller;
+	unsigned long	flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	stop_activity(dev, driver);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	iusbc_pullup(&dev->gadget, 0);
+
+	driver->unbind(&dev->gadget);
+	dev->gadget.dev.driver = NULL;
+	dev->driver = NULL;
+
+	device_remove_file(&dev->pdev->dev, &dev_attr_function);
+	device_remove_file(&dev->pdev->dev, &dev_attr_queues);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static struct iusbc_ep *
+get_ep_by_addr(struct iusbc *dev, u16 wIndex)
+{
+	struct iusbc_ep		*ep;
+
+	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+		return &dev->ep[0];
+
+	list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+		u8	bEndpointAddress;
+		if (!ep->desc)
+			continue;
+
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+
+		if ((wIndex & USB_ENDPOINT_NUMBER_MASK) ==
+			(bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
+			return ep;
+	}
+	return NULL;
+}
+
+/* NAK an endpoint */
+static void ep_nak(struct iusbc_ep *ep)
+{
+	u16		val_16;
+	unsigned	i = ep->num;
+
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+	val_16 &= ~EP_ENABLE;
+	val_16 |= EP_VALID;
+	writew(val_16, &ep->dev->regs->ep[i].ep_cfg);
+
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+}
+
+
+/* ACK an out transfer with a zero length packet (ZLP) */
+static void ep_ack(struct iusbc_ep *ep)
+{
+	u16		val_16;
+	unsigned	i = ep->num;
+
+	/* reset data buffer zero length */
+	writew(0, &ep->dev->regs->ep[i].ep_len);
+	val_16 = readw(&ep->dev->regs->ep[i].ep_len);
+
+	/* validate endpoint, enable DMA */
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+	val_16 |= (EP_VALID | EP_ENABLE);
+	writew(val_16, &ep->dev->regs->ep[i].ep_cfg);
+
+	val_16 = readw(&ep->dev->regs->ep[i].ep_cfg);
+}
+
+static void ep0_setup(struct iusbc *dev)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	struct usb_ctrlrequest		ctrl;
+	struct iusbc_ep			*epn;
+	unsigned			i = 0;
+	int 				tmp = 0;
+	u8				addr_new, setup_pkt[8];
+	struct device			*dbg_dev;
+
+	dbg_dev = &dev->pdev->dev;
+
+	for (i = 0; i < 8; i++)
+		setup_pkt[i] = readb(&dev->regs->ep[1].setup_pkt[i]);
+
+	/* read SETUP packet and enter DATA stage */
+	ctrl.bRequestType = setup_pkt[0];
+	ctrl.bRequest = setup_pkt[1];
+	ctrl.wValue  = cpu_to_le16((setup_pkt[3] << 8)
+			| setup_pkt[2]);
+	ctrl.wIndex  = cpu_to_le16((setup_pkt[5] << 8)
+			| setup_pkt[4]);
+	ctrl.wLength = cpu_to_le16((setup_pkt[7] << 8)
+			| setup_pkt[6]);
+
+	dev->ep[1].stopped = 0;
+
+	/* data stage direction */
+	if (ctrl.bRequestType & USB_DIR_IN)
+		dev->ep0state = EP0_IN;
+	else
+		dev->ep0state = EP0_OUT;
+
+	/* For RNDIS */
+	/* SEND_ENCAPSULATED_COMMAND and GET_ENCAPSULATED_RESPONSE */
+	if ((ctrl.bRequestType == 0x21 && ctrl.bRequest == 0x00)
+		|| (ctrl.bRequestType == 0xa1 && ctrl.bRequest == 0x01))
+		goto delegate;
+
+	switch (ctrl.bRequest) {
+	/* GET_STATUS is handled by software */
+	case USB_REQ_GET_STATUS:
+		switch (ctrl.bRequestType &
+			__constant_cpu_to_le16(USB_RECIP_MASK)) {
+		case USB_RECIP_DEVICE:
+			/* bit 0: USB_DEVICE_SELF_POWERED
+			 * bit 1: USB_DEVICE_REMOTE_WAKEUP
+			 */
+			if (dev->enable_wakeup)
+				dev->status_d =
+				__constant_cpu_to_le16(1 << 1 | 1);
+			else
+				dev->status_d =
+				__constant_cpu_to_le16(1);
+			break;
+		case USB_RECIP_INTERFACE:
+			/* all bits zero */
+			dev->status_d = __constant_cpu_to_le16(0);
+			break;
+		case USB_RECIP_ENDPOINT:
+			epn = get_ep_by_addr(dev,
+					le16_to_cpu(ctrl.wIndex));
+			if (epn->stopped)	/* halted */
+				dev->status_d =
+					__constant_cpu_to_le16(1);
+			else
+				dev->status_d =
+					__constant_cpu_to_le16(0);
+			break;
+		}
+		/* GET_STATUS is partially handled in gadget driver */
+		goto delegate;
+
+	case USB_REQ_CLEAR_FEATURE:
+		switch (ctrl.bRequestType) {
+		case USB_RECIP_DEVICE:
+			if (ctrl.wValue == __constant_cpu_to_le16(
+					USB_DEVICE_REMOTE_WAKEUP)) {
+				dev->enable_wakeup = 0;
+				goto end;
+			} else
+				goto stall;
+			break;
+		case USB_RECIP_INTERFACE:
+			goto stall;
+		case USB_RECIP_ENDPOINT:
+			if (ctrl.wValue != __constant_cpu_to_le16(
+						USB_ENDPOINT_HALT)
+				|| le16_to_cpu(ctrl.wLength) != 0)
+				goto stall;
+			epn = get_ep_by_addr(dev,
+					le16_to_cpu(ctrl.wIndex));
+			if (epn == NULL)
+				goto stall;
+			if (epn->wedged) {
+				dev_vdbg(dbg_dev,
+					"%s wedged, halt not cleared\n",
+					epn->ep.name);
+			} else {
+				dev_vdbg(dbg_dev, "%s clear halt\n",
+					epn->ep.name);
+				clear_halt(epn);
+				ep_ack(&dev->ep[0]);
+				dev->ep0state = EP0_STATUS;
+			}
+			goto end;
+		}
+		break;
+
+	case USB_REQ_SET_FEATURE:
+		switch (ctrl.bRequestType) {
+		case USB_RECIP_DEVICE:
+			if (ctrl.wValue == __constant_cpu_to_le16(
+					USB_DEVICE_REMOTE_WAKEUP)) {
+				dev->enable_wakeup = 1;
+				goto end;
+			} else
+				goto stall;
+			break;
+		case USB_RECIP_INTERFACE:
+			goto stall;
+		case USB_RECIP_ENDPOINT:
+			if (ctrl.wValue != __constant_cpu_to_le16(
+						USB_ENDPOINT_HALT)
+				|| le16_to_cpu(ctrl.wLength) != 0)
+				goto stall;
+			epn = get_ep_by_addr(dev,
+					le16_to_cpu(ctrl.wIndex));
+			if (epn == NULL)
+				goto stall;
+			if (epn->ep.name == ep_name[0]
+				|| epn->ep.name == ep_name[1])
+				goto stall;
+			set_halt(epn);
+			ep_ack(&dev->ep[0]);
+			goto end;
+		}
+		break;
+
+	case USB_REQ_SET_ADDRESS:
+		/* hw handles set_address, address range: 1-127 */
+		if (setup_pkt[1] == USB_REQ_SET_ADDRESS) {
+			addr_new = le16_to_cpu(ctrl.wValue) & 0x7f;
+			/* hardware didn't ACK SET_ADDRESS */
+			ep_ack(&dev->ep[0]);
+		}
+		goto end;
+
+	case USB_REQ_GET_DESCRIPTOR:
+		goto delegate;
+
+	case USB_REQ_SET_DESCRIPTOR:
+		goto stall;
+		break;
+
+	case USB_REQ_GET_CONFIGURATION:
+		goto delegate;
+
+	case USB_REQ_SET_CONFIGURATION:
+		goto delegate;
+
+	case USB_REQ_GET_INTERFACE:
+		goto delegate;
+
+	case USB_REQ_SET_INTERFACE:
+		goto delegate;
+
+	case USB_REQ_SYNCH_FRAME:
+		goto stall;
+		break;
+	default:
+		/* delegate usb standard requests to the gadget driver.
+		 * it may respond after this irq handler returns.
+		 */
+		goto delegate;
+delegate:
+		dev_vdbg(dbg_dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+			ctrl.bRequestType, ctrl.bRequest,
+			le16_to_cpu(ctrl.wValue),
+			le16_to_cpu(ctrl.wIndex),
+			le16_to_cpu(ctrl.wLength));
+
+		/* For RNDIS */
+		/* CDC: SEND_ENCAPSULATED_COMMAND */
+		if ((ctrl.bRequestType == 0x21)
+				&& (ctrl.bRequest == 0x00)) {
+			/* CDC: SEND_ENCAPSULATED_COMMAND */
+			dev_dbg(dbg_dev,
+				"SEND_ENCAPSULATED_COMMAND\n");
+			dev->gadget.ep0 = &dev->ep[1].ep;
+			spin_unlock(&dev->lock);
+			tmp = dev->driver->setup(&dev->gadget, &ctrl);
+			spin_lock(&dev->lock);
+
+			/* switch back to ep0 in */
+			dev->gadget.ep0 = &dev->ep[0].ep;
+
+			/* hardware didn't ACK */
+			ep_ack(&dev->ep[0]);
+		} else {
+			dev->gadget.ep0 = &dev->ep[0].ep;
+			spin_unlock(&dev->lock);
+			tmp = dev->driver->setup(&dev->gadget, &ctrl);
+			/* spin_lock(&dev->lock);*/
+
+			dev_dbg(dbg_dev, "tmp = %d\n. ", tmp);
+			/* CDC: GET_ENCAPSULATED_RESPONSE */
+			if ((ctrl.bRequestType == 0xa1)
+				&& (ctrl.bRequest == 0x01))
+				dev_vdbg(dbg_dev,
+					"GET_ENCAPSULATED_RESPONSE\n");
+			spin_lock(&dev->lock);
+		}
+		break;
+	}
+
+	if (tmp < 0) {
+stall:
+		dev_dbg(dbg_dev,
+			"req %02x.%02x protocol STALL; err %d\n",
+			ctrl.bRequestType, ctrl.bRequest, tmp);
+		dev->ep0state = EP0_STALL;
+		dev_vdbg(dbg_dev, "ep0state: EP0_STALL\n");
+	}
+end:
+	dev_vdbg(dbg_dev, "<--- ep0_setup() \n");
+}
+
+static void handle_device_irqs(struct iusbc *dev)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	u32			stat;
+	u32			dev_sts;
+	unsigned		is_connected = 0;
+	struct device		*dbg_dev;
+
+	dbg_dev = &dev->pdev->dev;
+	stat = dev->int_sts;
+
+	/* get device status, set USB speed */
+	if ((stat & RESET_INTR) || (stat & CONNECT_INTR)
+			|| (stat & SUSPEND_INTR)) {
+		/* check device status */
+		dev_sts = readl(&dev->regs->dev_sts);
+		dev_vdbg(dbg_dev,
+			"handle_device_irqs: dev_sts = 0x%08x\n",
+			dev_sts);
+
+		if (dev_sts & CONNECTED) {
+			is_connected = 1;
+			dev_dbg(dbg_dev, "device connected\n");
+
+			/* check suspend/resume status now */
+			if (dev_sts & SUSPEND)
+				dev->suspended = 1;
+			else
+				dev->suspended = 0;
+
+			/* RATE: high/full speed flag
+			 * 1 clock cycle after CONNECT,
+			 * read one more time until RATE bit set
+			 */
+			dev_sts = readl(&dev->regs->dev_sts);
+			dev_dbg(dbg_dev,
+				"read for RATE: dev_sts = 0x%08x\n",
+				dev_sts);
+			if (dev_sts & RATE) {
+				if (dev->force_fullspeed) {
+					dev->gadget.speed =
+						USB_SPEED_FULL;
+					dev_dbg(dbg_dev,
+						"speed: Full Speed\n");
+				} else {
+					dev->gadget.speed =
+						USB_SPEED_HIGH;
+					dev_dbg(dbg_dev,
+						"speed: High Speed\n");
+				}
+			} else {
+				dev->gadget.speed = USB_SPEED_FULL;
+				dev_dbg(dbg_dev,
+					"speed: Full Speed\n");
+			}
+		} else {	/* disconnected */
+			is_connected = 0;
+			dev_dbg(dbg_dev, "device disconnected\n");
+		}
+	}
+
+	/* USB reset interrupt indication */
+	if ((stat & RESET_INTR) && (!dev->is_reset)) {
+		dev_dbg(dbg_dev,
+			"USB Reset interrupt: stat = 0x%08x\n", stat);
+		/* ACK RESET_INTR */
+		stat &= ~RESET_INTR;
+		dev->irqs++;
+		dev_vdbg(dbg_dev, "dev->irqs: %lu\n", dev->irqs);
+
+		/* set device reset flag */
+		dev->is_reset = 1;
+
+		stop_activity(dev, dev->driver);
+		ep0_start(dev);
+		dev_dbg(dbg_dev, "reset: ep0_start()\n");
+
+		goto end;
+	}
+
+	/* connect interrupt indication */
+	if (stat & CONNECT_INTR) {
+		dev_dbg(dbg_dev,
+			"CONNECT interrupt: stat = 0x%08x\n", stat);
+		/* ACK CONNECT_INTR */
+		stat &= ~CONNECT_INTR;
+		dev->irqs++;
+		dev_dbg(dbg_dev, "dev->irqs: %lu\n", dev->irqs);
+
+		/* connected status has changed */
+		if (dev->connected != is_connected) {
+			dev_dbg(dbg_dev, "connect status changed\n");
+			dev->connected = is_connected;
+			if (is_connected) {
+				ep0_start(dev);
+				dev_dbg(dbg_dev, "connect %s\n",
+					dev->driver->driver.name);
+			} else { /* disconnected */
+				/* set device reset flag */
+				dev->is_reset = 0;
+				stop_activity(dev, dev->driver);
+				dev_dbg(dbg_dev, "disconnect %s\n",
+					dev->driver->driver.name);
+				dev->ep0state = EP0_DISCONNECT;
+				dev_vdbg(dbg_dev,
+					"ep0state: EP0_DISCONNECT\n");
+			}
+		}
+	}
+
+	/* host suspend interrupt indication */
+	if (stat & SUSPEND_INTR) {
+		dev_dbg(dbg_dev,
+			"SUSPEND interrupt: stat = 0x%08x\n", stat);
+		/* ACK SUSPEND_INTR */
+		stat &= ~SUSPEND_INTR;
+		dev->irqs++;
+		dev_vdbg(dbg_dev, "dev->irqs: %lu\n", dev->irqs);
+
+		/* call gadget driver suspend/resume routines */
+		if (dev->suspended) {
+			if (dev->driver->suspend) {
+				spin_unlock(&dev->lock);
+				dev->driver->suspend(&dev->gadget);
+				spin_lock(&dev->lock);
+			}
+			dev_dbg(dbg_dev, "suspend %s\n",
+				dev->driver->driver.name);
+		} else {
+			if (dev->driver->resume) {
+				spin_unlock(&dev->lock);
+				dev->driver->resume(&dev->gadget);
+				spin_lock(&dev->lock);
+			}
+			dev_dbg(dbg_dev, "resume %s\n",
+			dev->driver->driver.name);
+		}
+	}
+
+	/* if haven't USB Reset yet, wait for the next*/
+	if (!dev->is_reset)
+		dev_dbg(dbg_dev,
+			"Skip other interrupts until RESET\n");
+
+end:
+	dev_vdbg(dbg_dev,
+		"handle device_irq finish: int_sts = 0x%08x, "
+		"stat = 0x%08x\n", dev->int_sts, stat);
+}
+
+static void handle_ep0_irqs(struct iusbc *dev)
+{
+	u16			ep_sts, val_16;
+	u32			stat;
+	u8			setup_pkt_sts;
+	struct iusbc_request	*req;
+	struct device		*dbg_dev;
+
+	dbg_dev = &dev->pdev->dev;
+	stat = dev->int_sts;
+	dev_vdbg(dbg_dev, "stat = 0x%08x\n", stat);
+
+	/* ep0 in interrupt */
+	if (stat & EP0_IN_INTR) {
+		dev->ep[0].irqs++;
+		dev_vdbg(dbg_dev, "%s.irqs = %lu\n",
+			dev->ep[0].ep.name, dev->ep[0].irqs);
+
+		ep_sts = readw(&dev->regs->ep[0].ep_sts);
+		dev_vdbg(dbg_dev, "%s.ep_sts = 0x%04x\n",
+			dev->ep[0].ep.name, ep_sts);
+
+		/* W1C ep0 in status register */
+		writew(ep_sts, &dev->regs->ep[0].ep_sts);
+
+		if ((ep_sts & BAD_PID_TYPE)
+				|| (ep_sts & CRC_ERROR)
+				|| (ep_sts & FIFO_ERROR)
+				|| (ep_sts & DMA_ERROR)
+				|| (ep_sts & DMA_IOC))
+			dev_dbg(dbg_dev, "%s error: 0x%04x \n",
+				dev->ep[0].ep.name, ep_sts);
+
+		if (ep_sts & TRANS_COMPLETE) {
+			dev_vdbg(dbg_dev, "handle ep0 in interrupt\n");
+			if (!list_empty(&dev->ep[0].queue)) {
+				req = list_entry(dev->ep[0].queue.next,
+						struct iusbc_request,
+						queue);
+				dev_dbg(dbg_dev, "dmacount = %d\n",
+					req->td->dmacount);
+				dma_done(&dev->ep[0], req, 0);
+				dev_dbg(dbg_dev, "%s dma_done()\n",
+					dev->ep[0].ep.name);
+			}
+
+			/* handle next standard IN packet */
+			if (!list_empty(&dev->ep[0].queue)) {
+				restart_dma(&dev->ep[0]);
+				dev_vdbg(dbg_dev, "%s restart_dma()\n",
+					dev->ep[0].ep.name);
+			}
+		} else {
+			if (ep_sts & FIFO_ERROR)
+				dev_dbg(dbg_dev,
+					"ep0 in FIFO_ERROR,TC=0\n");
+		}
+	}
+
+	/* ep0 out interrupt */
+	if (stat & EP0_OUT_INTR) {
+		dev->ep[1].irqs++;
+		dev_dbg(dbg_dev, "%s.irqs = %lu\n",
+			dev->ep[1].ep.name, dev->ep[1].irqs);
+
+		ep_sts = readw(&dev->regs->ep[1].ep_sts);
+		dev_dbg(dbg_dev, "%s.ep_sts = 0x%04x\n",
+			dev->ep[1].ep.name, ep_sts);
+
+		/* W1C ep0 out status register */
+		writew(ep_sts, &dev->regs->ep[1].ep_sts);
+
+		if ((ep_sts & BAD_PID_TYPE)
+				|| (ep_sts & CRC_ERROR)
+				|| (ep_sts & FIFO_ERROR)
+				|| (ep_sts & DMA_ERROR)
+				|| (ep_sts & DMA_IOC)) {
+			dev_dbg(dbg_dev, "%s error: 0x%04x \n",
+				dev->ep[1].ep.name, ep_sts);
+		}
+
+		if (ep_sts & TRANS_COMPLETE) {
+			dev_dbg(dbg_dev, "handle ep0 out interrupt\n");
+
+			setup_pkt_sts = readb(
+				&dev->regs->ep[1].setup_pkt_sts);
+			dev_dbg(dbg_dev, "setup_pkt_sts = 0x%02x\n",
+				setup_pkt_sts);
+			/* ep0 out SETUP packet */
+			if (setup_pkt_sts) {
+				dev_dbg(dbg_dev,
+					"ep0 out SETUP packet\n");
+
+				/* W1C ep0 out setup_pkt_sts Register*/
+				writeb(1,
+				&dev->regs->ep[1].setup_pkt_sts);
+
+				/* read ep0 out Setup Packet Register
+				 * then handle ep0 out SETUP packet
+				 */
+				ep0_setup(dev);
+			} else {
+				/* ep0 standard OUT packet */
+				dev_dbg(dbg_dev, "ep0 OUT packet\n");
+				if (!list_empty(&dev->ep[1].queue)) {
+					req = list_entry(
+						dev->ep[1].queue.next,
+						struct iusbc_request,
+						queue);
+					dev_vdbg(dbg_dev,
+						"dmacount = %d\n",
+						req->td->dmacount);
+					dma_done(&dev->ep[1], req, 0);
+					dev_dbg(dbg_dev,
+						"%s dma_done()\n",
+						dev->ep[1].ep.name);
+				}
+
+				/* handle next standard OUT packet */
+				if (!list_empty(&dev->ep[1].queue)) {
+					restart_dma(&dev->ep[1]);
+					dev_vdbg(dbg_dev,
+						"%s restart_dma()\n",
+						dev->ep[1].ep.name);
+				}
+			}
+		} else {
+			if (ep_sts & FIFO_ERROR)
+				dev_dbg(dbg_dev,
+					"ep0 out FIFO_ERROR, TC=0\n");
+		}
+
+		/* enable DMA again */
+		val_16 = readw(&dev->regs->ep[1].ep_cfg);
+		val_16 |= EP_ENABLE;
+		writew(val_16, &dev->regs->ep[1].ep_cfg);
+
+		val_16 = readw(&dev->regs->ep[1].ep_cfg);
+		dev_vdbg(dbg_dev, "enable %s DMA transfer...\n",
+			dev->ep[1].ep.name);
+		dev_vdbg(dbg_dev,
+			"%s EP_ENABLE again, ep_cfg=0x%04x\n",
+			dev->ep[1].ep.name, val_16);
+	}
+}
+
+static void handle_ep_irqs(struct iusbc *dev)
+{
+	u16			ep_sts;
+	u32			stat;
+	unsigned		i;
+	struct iusbc_request	*req;
+	struct device		*dbg_dev;
+
+	dbg_dev = &dev->pdev->dev;
+	stat = dev->int_sts;
+	dev_vdbg(dbg_dev, "stat = 0x%08x\n", stat);
+
+	/* ep1in-bulk, ep1out-bulk and ep2in-int */
+	for (i = 2; i < 5; i++) {
+		if ((1 << i) & stat) {
+			dev->ep[i].irqs++;
+			ep_sts = readw(&dev->regs->ep[i].ep_sts);
+			if (ep_sts)
+				dev_vdbg(dbg_dev,
+					"%s.ep_sts = 0x%04x\n",
+					dev->ep[i].ep.name, ep_sts);
+
+			/* W1C ep status register */
+			writew(ep_sts, &dev->regs->ep[i].ep_sts);
+
+			if ((ep_sts & BAD_PID_TYPE)
+					|| (ep_sts & CRC_ERROR)
+					|| (ep_sts & FIFO_ERROR)
+					|| (ep_sts & DMA_ERROR)
+					|| (ep_sts & DMA_IOC)) {
+				dev_dbg(dbg_dev, "%s error: 0x%04x \n",
+					dev->ep[i].ep.name, ep_sts);
+			}
+
+			if (ep_sts & TRANS_COMPLETE) {
+				dev_vdbg(dbg_dev,
+					"handle %s interrupt\n",
+					dev->ep[i].ep.name);
+				dev_vdbg(dbg_dev,
+					"data ep TRANS_COMPLETE\n");
+				if (!list_empty(&dev->ep[i].queue)) {
+					req = list_entry(
+						dev->ep[i].queue.next,
+						struct iusbc_request,
+						queue);
+					dev_vdbg(dbg_dev,
+						"dmacount = %d\n",
+						req->td->dmacount);
+					dma_done(&dev->ep[i], req, 0);
+					dev_vdbg(dbg_dev,
+						"%s dma_done()\n",
+						dev->ep[i].ep.name);
+				}
+
+				/* handle next IN/OUT packet */
+				if (!list_empty(&dev->ep[i].queue)) {
+					restart_dma(&dev->ep[i]);
+					dev_vdbg(dbg_dev,
+						"%s restart_dma()\n",
+						dev->ep[i].ep.name);
+				}
+			} else
+				if (ep_sts & FIFO_ERROR)
+					dev_dbg(dbg_dev,
+						"%s FIFO_ERROR,TC=0\n",
+						dev->ep[i].ep.name);
+		}
+	}
+}
+
+static irqreturn_t iusbc_irq(int irq, void *_dev)
+{
+	struct iusbc	*dev = _dev;
+	u32		int_sts,
+			int_ctrl,
+			val_32;
+	u32 		dev_sts;
+	struct device	*dbg_dev;
+
+	dbg_dev = &dev->pdev->dev;
+
+	/* interrupt control */
+	int_ctrl = readl(&dev->regs->int_ctrl);
+	dev_vdbg(dbg_dev, "int_ctrl = 0x%08x\n", int_ctrl);
+
+	/* interrupt status */
+	int_sts = readl(&dev->regs->int_sts);
+	dev_vdbg(dbg_dev, "int_sts = 0x%08x\n", int_sts);
+
+	if (!int_sts || !int_ctrl) {
+		dev_vdbg(dbg_dev, "handle IRQ_NONE\n");
+		return IRQ_NONE;
+	}
+
+	spin_lock(&dev->lock);
+
+	/* disable irqs, will re-enable later */
+	writel(0, &dev->regs->int_ctrl);
+
+	/* W1C interrupt status register */
+	writel(int_sts, &dev->regs->int_sts);
+	val_32 = readl(&dev->regs->int_sts);
+	dev_vdbg(dbg_dev, "W1C: regs->int_sts = 0x%08x\n", val_32);
+
+	dev->int_sts = int_sts;
+
+	/* check device status */
+	val_32 = readl(&dev->regs->dev_sts);
+	dev_vdbg(dbg_dev, "iusbc_irq: dev_sts = 0x%08x\n", val_32);
+
+	/* device-wide reset, connect, suspend */
+	if (dev->int_sts &
+			(RESET_INTR | CONNECT_INTR | SUSPEND_INTR)) {
+		dev_vdbg(dbg_dev, "iusbc_irq -> handle_device_irqs\n");
+		handle_device_irqs(dev);
+	}
+
+	/* Full Speed is disabled for D1, and enabled for D2. */
+	dev_sts = readl(&dev->regs->dev_sts);
+	if (dev->stepping >= STEPPING_D2) {
+		dev_dbg(dbg_dev, "iusbc_irq -> D2 boards\n");
+
+		/* ep0 in/out control requests and interrupt */
+		if (dev->int_sts & (EP0_IN_INTR | EP0_OUT_INTR)) {
+			dev_dbg(dbg_dev, "handle_ep0_irqs\n");
+			handle_ep0_irqs(dev);
+		}
+
+		if (dev->int_sts &
+			(EP1_IN_INTR | EP1_OUT_INTR | EP2_IN_INTR)) {
+			dev_dbg(dbg_dev, "handle_ep_irqs.\n");
+			handle_ep_irqs(dev);
+		}
+	} else {
+		dev_vdbg(dbg_dev, "iusbc_irq -> D1 boards\n");
+		if ((dev_sts & RATE) && (dev_sts & CONNECTED)) {
+			/* ep0 in/out control requests and interrupt */
+			if (dev->int_sts &
+				(EP0_IN_INTR | EP0_OUT_INTR)) {
+				dev_dbg(dbg_dev, "handle_ep0_irqs\n");
+				handle_ep0_irqs(dev);
+			}
+
+			if (dev->int_sts & (EP1_IN_INTR | EP1_OUT_INTR
+					| EP2_IN_INTR)) {
+				/* data endpoint requests interrupt */
+				dev_dbg(dbg_dev, "handle_ep_irqs\n");
+				handle_ep_irqs(dev);
+			}
+		} else if (dev_sts & CONNECTED) {
+			stop_activity(dev, dev->driver);
+			dev->ep0state = EP0_DISCONNECT;
+		}
+	}
+	/* enable irqs again */
+	val_32 = 0;
+	val_32 |= RESET_INTR_ENABLE
+		| CONNECT_INTR_ENABLE
+		| SUSPEND_INTR_ENABLE
+		/* | EP3_OUT_INTR_ENABLE */
+		/* | EP3_IN_INTR_ENABLE */
+		/* | EP2_OUT_INTR_ENABLE */
+		| EP2_IN_INTR_ENABLE
+		| EP1_OUT_INTR_ENABLE
+		| EP1_IN_INTR_ENABLE
+		| EP0_OUT_INTR_ENABLE
+		| EP0_IN_INTR_ENABLE;
+
+	writel(val_32, &dev->regs->int_ctrl);
+	val_32 = readl(&dev->regs->int_ctrl);
+	spin_unlock(&dev->lock);
+
+	dev_vdbg(dbg_dev,
+		"enable irqs again, int_ctrl = 0x%08x\n", val_32);
+
+	return IRQ_HANDLED;
+}
+
+static void gadget_release(struct device *_dev)
+{
+	struct iusbc	*dev;
+
+	dev_vdbg(_dev, "---> gadget_release() \n");
+
+	dev = dev_get_drvdata(_dev);
+	kfree(dev);
+
+	dev_vdbg(_dev, "<--- gadget_release() \n");
+}
+
+/* tear down the binding between this driver and the pci device */
+static void iusbc_remove(struct pci_dev *pdev)
+{
+	struct iusbc		*dev;
+
+	dev = pci_get_drvdata(pdev);
+	BUG_ON(dev->driver);
+
+	dev_vdbg(&pdev->dev, "---> iusbc_remove() \n");
+
+	/* then clean up the resources we allocated during probe() */
+	if (dev->requests) {
+		unsigned	i;
+		/* 2 ep0, 3 data ep */
+		for (i = 0; i < 5; i++) {
+			if (!dev->ep[i].dummy)
+				continue;
+
+			pci_pool_free(dev->requests, dev->ep[i].dummy,
+					dev->ep[i].td_dma);
+		}
+		pci_pool_destroy(dev->requests);
+	}
+
+	if (dev->got_irq)
+		free_irq(pdev->irq, dev);
+
+	if (dev->regs)
+		iounmap(dev->regs);
+
+	if (dev->region)
+		release_mem_region(pci_resource_start(pdev, 0),
+				pci_resource_len(pdev, 0));
+
+	if (dev->enabled)
+		pci_disable_device(pdev);
+
+	device_unregister(&dev->gadget.dev);
+	device_remove_file(&pdev->dev, &dev_attr_registers);
+	pci_set_drvdata(pdev, NULL);
+
+	dev_vdbg(&pdev->dev, "unbind\n");
+
+	the_controller = NULL;
+
+	dev_vdbg(&pdev->dev, "<--- iusbc_remove() \n");
+}
+
+static int
+iusbc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct iusbc		*dev;
+	unsigned long		resource, len;
+	void			__iomem *base = NULL;
+	int			retval;
+	unsigned		i;
+	u32			val_32;
+	u8			stepping;
+
+	/* if you want to support more than one controller in a system,
+	 * usb_gadget_{register,unregister}_driver() must change.
+	 */
+	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 done;
+	}
+
+	pci_set_drvdata(pdev, dev);
+	spin_lock_init(&dev->lock);
+	dev->pdev = pdev;
+
+	dev_dbg(&pdev->dev, "---> iusbc_probe() \n");
+
+	dev->gadget.ops = &iusbc_ops;
+	dev->gadget.is_dualspeed = 1; /* support high/full speed */
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	dev_set_name(&dev->gadget.dev, "gadget");
+	dev->gadget.dev.parent = &pdev->dev;
+	dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	dev->gadget.dev.release = gadget_release;
+	dev->gadget.name = driver_name;
+
+	/* now all the pci goodies ... */
+	if (pci_enable_device(pdev) < 0) {
+		retval = -ENODEV;
+		goto done;
+	}
+
+	dev->enabled = 1;
+
+	/* control register: BAR 0 */
+	resource = pci_resource_start(pdev, 0);
+	len = pci_resource_len(pdev, 0);
+	if (!request_mem_region(resource, len, driver_name)) {
+		dev_dbg(&pdev->dev, "controller already in use\n");
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->region = 1;
+
+	base = ioremap_nocache(resource, len);
+	if (base == NULL) {
+		dev_dbg(&pdev->dev, "can't map memory\n");
+		retval = -EFAULT;
+		goto done;
+	}
+	dev->regs = (struct iusbc_regs __iomem *) base;
+
+	/* global capabilities, ep number and dma modes */
+	val_32 = readl(&dev->regs->gcap);
+	if (val_32 & TRANSFER_MODE_CAP)
+		dev->transfer_mode_dma = 1;
+
+	if (val_32 & SCATTER_GATHER_MODE_CAP)
+		dev->sg_mode_dma = 1;
+
+	if (val_32 & LINEAR_MODE_CAP)
+		dev->linear_mode_dma = 1;
+
+	if (val_32 & CONTROL_MODE_CAP)
+		dev->control_mode_dma = 1;
+
+	dev->ep_cap = (val_32 >> 28);
+
+	/* reset all registers */
+	writel(0, &dev->regs->dev_ctrl);
+
+	/* init usb client controller device */
+	iusbc_reset(dev);
+	iusbc_reinit(dev);
+
+	/* irq setup after old hardware is cleaned up */
+	if (!pdev->irq) {
+		dev_err(&pdev->dev, "No IRQ. Check PCI setup!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+
+	if (request_irq(pdev->irq, iusbc_irq, IRQF_SHARED,
+			driver_name, dev) != 0) {
+		dev_err(&pdev->dev, "request interrupt %d failed\n",
+			pdev->irq);
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->got_irq = 1;
+
+	/* DMA setup
+	 * NOTE: only the 32 LSBs of dma addresses may be nonzero
+	 */
+	dev->requests = pci_pool_create("requests", pdev,
+		sizeof(struct iusbc_dma),
+		0 /* no alignment requirements */,
+		0 /* or page-crossing issues */);
+
+	if (!dev->requests) {
+		dev_dbg(&pdev->dev, "can't get request pool\n");
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	/* assgined DMA */
+	/* 2 ep0, 3 data ep */
+	for (i = 0; i < 5 ; i++) {
+		struct iusbc_dma	*td;
+		td = pci_pool_alloc(dev->requests,
+				GFP_KERNEL,
+				&dev->ep[i].td_dma);
+
+		if (!td) {
+			dev_dbg(&pdev->dev, "can't get dummy %d\n", i);
+			retval = -ENOMEM;
+			goto done;
+		}
+
+		td->dmacount = 0;	/* not VALID */
+		td->dmaaddr = __constant_cpu_to_le32(DMA_ADDR_INVALID);
+
+		dev->ep[i].dummy = td;
+	}
+
+	/* enables bus-mastering for device dev */
+	pci_set_master(pdev);
+
+	pci_read_config_byte(pdev, 0x08, &stepping);
+	dev->stepping = stepping;
+
+	/* done */
+	dev_dbg(&pdev->dev, "%s\n", driver_desc);
+	dev_dbg(&pdev->dev, "irq %d, pci mem %p\n", pdev->irq, base);
+	dev_dbg(&pdev->dev, "version: " DRIVER_VERSION "\n");
+	dev_dbg(&pdev->dev, "support (max) %d endpoints\n",
+		dev->ep_cap * 2);
+
+#ifdef VERBOSE_DEBUG
+	/* only for debugging, print mapped memory registers */
+	dev_vdbg(&pdev->dev,
+		"After iusbc_probe(), print register values:\n");
+	print_all_registers(dev->regs);
+#endif
+
+	the_controller = dev;
+	retval = device_register(&dev->gadget.dev);
+
+	if (retval)
+		goto done;
+
+	retval = device_create_file(&pdev->dev, &dev_attr_registers);
+
+	if (retval)
+		goto done;
+
+	dev_dbg(&pdev->dev, "<--- iusbc_probe() \n");
+
+	return 0;
+
+done:
+	if (dev)
+		iusbc_remove(pdev);
+	return retval;
+}
+
+#ifdef CONFIG_PM
+/* client suspend */
+static int iusbc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct iusbc	*dev;
+	unsigned long	flags;
+
+	dev = pci_get_drvdata(pdev);
+	spin_lock_irqsave(&dev->lock, flags);
+	stop_activity(dev, dev->driver);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	iusbc_pullup(&dev->gadget, 0);
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, PCI_D3hot);
+	return 0;
+}
+
+/* client resume */
+static int iusbc_resume(struct pci_dev *pdev)
+{
+	struct iusbc	*dev;
+
+	dev = pci_get_drvdata(pdev);
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	if (dev->driver)
+		device_start(dev);
+	return 0;
+}
+#endif
+
+static void iusbc_shutdown(struct pci_dev *pdev)
+{
+	struct iusbc	*dev;
+	u32		val_32;
+
+	dev = pci_get_drvdata(pdev);
+
+	/* disable irqs */
+	writel(0, &dev->regs->int_ctrl);
+
+	/* reset all registers */
+	writel(0, &dev->regs->dev_ctrl);
+	val_32 = readl(&dev->regs->dev_ctrl);
+	dev_dbg(&pdev->dev, "dev_ctrl = 0x%08x\n", val_32);
+}
+
+static const struct pci_device_id pci_ids[] = { {
+	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0x80),
+	.class_mask =	~0,
+	.vendor =	0x8086, /* Intel */
+	.device =	0x8118, /* Poulsbo USB Client Controller */
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver iusbc_pci_driver = {
+	.name =		(char *) driver_name,
+	.id_table =	pci_ids,
+
+	.probe =	iusbc_probe,
+	.remove =	iusbc_remove,
+
+#ifdef CONFIG_PM
+	.suspend =	iusbc_suspend,
+	.resume =	iusbc_resume,
+#endif
+
+	.shutdown =	iusbc_shutdown,
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+static int __init init(void)
+{
+	return pci_register_driver(&iusbc_pci_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+	pci_unregister_driver(&iusbc_pci_driver);
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/iusbc.h b/drivers/usb/gadget/iusbc.h
new file mode 100644
index 0000000..09009a7
--- /dev/null
+++ b/drivers/usb/gadget/iusbc.h
@@ -0,0 +1,207 @@
+/*
+ * Intel Poulsbo USB Client Controller Driver
+ * Copyright (C) 2006-07, 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.
+ *
+ */
+
+
+/*
+ * MEMORY SPACE REGISTERS
+ */
+
+struct iusbc_ep_regs {	/* 0x20 bytes */
+	u32		ep_base_low_32;
+	u32		ep_base_hi_32;
+	u16		ep_len;
+	u16		ep_pib;
+	u16		ep_dil;
+	u16		ep_tiq;
+	u16		ep_max;
+	u16		ep_sts;
+#define		BAD_PID_TYPE		(1 << 15)
+#define		CRC_ERROR		(1 << 14)
+#define		FIFO_ERROR		(1 << 13)
+#define		DMA_ERROR		(1 << 12)
+#define		TRANS_COMPLETE		(1 << 11)
+#define		PING_NAK_SENT		(1 << 10)
+#define		DMA_IOC			(1 << 9)
+	u16		ep_cfg;
+#define		INTR_BAD_PID_TYPE	(1 << 15)
+#define		INTR_CRC_ERROR		(1 << 14)
+#define		INTR_FIFO_ERROR		(1 << 13)
+#define		INTR_DMA_ERROR		(1 << 12)
+#define		INTR_TRANS_COMPLETE	(1 << 11)
+#define		INTR_PING_NAK_SENT	(1 << 10)
+#define		INTR_DMA_IOC		(1 << 9)
+#define		LINEAR_MODE		0
+#define		SCATTER_GATHER_MODE	1
+#define		TRANSFER_MODE		2
+#define		CONTROL_MODE		3
+#define		EP_ENABLE		(1 << 1)
+#define		EP_VALID		(1 << 0)
+	u8		_unused;
+	u8		setup_pkt_sts;
+#define		SETUPPACKET_VALID	(1 << 0)
+	u8		setup_pkt[8];
+} __attribute__((packed));
+
+
+struct iusbc_regs {
+	/* offset 0x0000 */
+	u32		gcap;
+#define		DMA_IOC_CAP		(1 << 4)
+#define		TRANSFER_MODE_CAP	(1 << 3)
+#define		SCATTER_GATHER_MODE_CAP	(1 << 2)
+#define		LINEAR_MODE_CAP		(1 << 1)
+#define		CONTROL_MODE_CAP	(1 << 0)
+	u8		_unused0[0x100-0x004];
+
+	/* offset 0x0100 */
+	u32		dev_sts;
+#define		RATE			(1 << 3)
+#define		CONNECTED		(1 << 1)
+#define		SUSPEND			(1 << 0)
+	u16		frame;
+	u8		_unused1[0x10c-0x106];
+
+	/* offset 0x010c */
+	u32		int_sts;
+#define		RESET_INTR		(1 << 18)
+#define		CONNECT_INTR		(1 << 17)
+#define		SUSPEND_INTR		(1 << 16)
+#define		EP3_OUT_INTR		(1 << 7)
+#define		EP3_IN_INTR		(1 << 6)
+#define		EP2_OUT_INTR		(1 << 5)
+#define		EP2_IN_INTR		(1 << 4)
+#define		EP1_OUT_INTR		(1 << 3)
+#define		EP1_IN_INTR		(1 << 2)
+#define		EP0_OUT_INTR		(1 << 1)
+#define		EP0_IN_INTR		(1 << 0)
+	u32		int_ctrl;
+#define		RESET_INTR_ENABLE	(1 << 18)
+#define		CONNECT_INTR_ENABLE	(1 << 17)
+#define		SUSPEND_INTR_ENABLE	(1 << 16)
+#define		EP3_OUT_INTR_ENABLE	(1 << 7)
+#define		EP3_IN_INTR_ENABLE	(1 << 6)
+#define		EP2_OUT_INTR_ENABLE	(1 << 5)
+#define		EP2_IN_INTR_ENABLE	(1 << 4)
+#define		EP1_OUT_INTR_ENABLE	(1 << 3)
+#define		EP1_IN_INTR_ENABLE	(1 << 2)
+#define		EP0_OUT_INTR_ENABLE	(1 << 1)
+#define		EP0_IN_INTR_ENABLE	(1 << 0)
+	u32		dev_ctrl;
+#define		DEVICE_ENABLE		(1 << 31)
+#define		CONNECTION_ENABLE	(1 << 30)
+#define		DMA3_DISABLED		(1 << 15)
+#define		DMA2_DISABLED		(1 << 14)
+#define		DMA1_DISABLED		(1 << 13)
+#define		DMA0_DISABLED		(1 << 12)
+#define		CPU_SET_ADDRESS		(1 << 11)
+#define		DISABLE_NYET		(1 << 9)
+#define		TEST_MODE		(1 << 8)
+#define		SIGNAL_RESUME		(1 << 4)
+#define		CHARGE_ENABLE		(5 << 1)
+#define		FORCE_FULLSPEED		(1 << 0)
+	u8		_unused2[0x200-0x118];
+
+	/* offset: 0x200, 0x220, ..., 0x2e0 */
+	struct iusbc_ep_regs	ep[8];
+} __attribute__((packed));
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+struct iusbc_dma {
+	__le16		dmacount;
+	__le32		dmaaddr;	/* the buffer */
+	__le32		dmadesc;	/* next dma descriptor */
+	__le32		_reserved;
+} __attribute__((aligned(16)));
+
+struct iusbc_ep {
+	struct usb_ep				ep;
+	struct iusbc_dma			*dummy;
+	dma_addr_t				td_dma;	/* of dummy */
+	struct iusbc				*dev;
+	unsigned long				irqs;
+
+	/* analogous to a host-side qh */
+	struct list_head			queue;
+	const struct usb_endpoint_descriptor	*desc;
+	unsigned				num:8,
+						stopped : 1,
+						wedged : 1,
+						is_in : 1,
+						dma_mode : 2,
+						ep_type : 2;
+};
+
+struct iusbc_request {
+	struct usb_request			req;
+	struct iusbc_dma			*td;
+	dma_addr_t				td_dma;
+	struct list_head			queue;
+	unsigned				mapped:1,
+						valid : 1;
+};
+
+enum ep0state {
+	EP0_DISCONNECT,		/* no host */
+	EP0_IDLE,		/* between STATUS ack and SETUP */
+	EP0_IN, EP0_OUT, 	/* data stage */
+	EP0_STATUS,		/* status stage */
+	EP0_STALL,		/* data or status stages */
+	EP0_SUSPEND,		/* usb suspend */
+};
+
+struct iusbc {
+	/* each pci device provides one gadget, several endpoints */
+	struct usb_gadget		gadget;
+	spinlock_t			lock;
+	struct iusbc_ep			ep[8];
+	struct usb_gadget_driver 	*driver;
+	enum ep0state			ep0state;
+	unsigned			ep_cap;
+	unsigned			enabled:1,
+					powered : 1,
+					enable_wakeup : 1,
+					force_fullspeed : 1,
+					rate : 1,
+					connected : 1,
+					suspended : 1,
+					got_irq : 1,
+					region : 1,
+					transfer_mode_dma : 1,
+					sg_mode_dma : 1,
+					linear_mode_dma : 1,
+					control_mode_dma : 1,
+					is_reset : 1;
+
+	/* pci state used to access those endpoints */
+	struct pci_dev			*pdev;
+	struct iusbc_regs		__iomem *regs;
+	struct pci_pool			*requests;
+
+	/* statistics... */
+	unsigned long			irqs;
+
+	/* 16 bits status data for GET_STATUS */
+	__le16				status_d;
+
+	/* interrupt status register value */
+	u32				int_sts;
+
+	/* Poulsbo USB-c Chipset stepping */
+	u8				stepping;
+};
-- 
1.5.6.3
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

  Powered by Linux