USB: add imx udc gadget driver

Implementation of USB device driver integrated in Freescale's i.MXL
processor.

Adds USB device driver for i.MXL.

Signed-off-by: Darius Augulis <augulis.darius@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/arch/arm/plat-mxc/include/mach/usb.h b/arch/arm/plat-mxc/include/mach/usb.h
new file mode 100644
index 0000000..2dacb308
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/usb.h
@@ -0,0 +1,23 @@
+/*
+ *	Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MXC_USB
+#define __ASM_ARCH_MXC_USB
+
+struct imxusb_platform_data {
+	int (*init)(struct device *);
+	int (*exit)(struct device *);
+};
+
+#endif /* __ASM_ARCH_MXC_USB */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c322024..3219d13 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -305,6 +305,27 @@
 	  This OTG-capable silicon IP is used in dual designs including
 	  the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
 
+config USB_GADGET_IMX
+	boolean "Freescale IMX USB Peripheral Controller"
+	depends on ARCH_MX1
+	help
+	   Freescale's IMX series include an integrated full speed
+	   USB 1.1 device controller.  The controller in the IMX series
+	   is register-compatible.
+
+	   It has Six fixed-function 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 "imx_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_IMX
+	tristate
+	depends on USB_GADGET_IMX
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_M66592
 	boolean "Renesas M66592 USB Peripheral Controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5d64a2a..39a51d7 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -10,6 +10,7 @@
 obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
 obj-$(CONFIG_USB_PXA25X)	+= pxa25x_udc.o
 obj-$(CONFIG_USB_PXA27X)	+= pxa27x_udc.o
+obj-$(CONFIG_USB_IMX)		+= imx_udc.o
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
 obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index c0679b1..ec6d439 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -110,7 +110,6 @@
 #define gadget_is_at91(g)	0
 #endif
 
-/* status unclear */
 #ifdef CONFIG_USB_GADGET_IMX
 #define gadget_is_imx(g)	!strcmp("imx_udc", (g)->name)
 #else
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
new file mode 100644
index 0000000..cde8fdf
--- /dev/null
+++ b/drivers/usb/gadget/imx_udc.c
@@ -0,0 +1,1516 @@
+/*
+ *	driver/usb/gadget/imx_udc.c
+ *
+ *	Copyright (C) 2005 Mike Lee(eemike@gmail.com)
+ *	Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/usb.h>
+#include <mach/hardware.h>
+
+#include "imx_udc.h"
+
+static const char driver_name[] = "imx_udc";
+static const char ep0name[] = "ep0";
+
+void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb,
+							enum ep0_state stat);
+
+/*******************************************************************************
+ * IMX UDC hardware related functions
+ *******************************************************************************
+ */
+
+void imx_udc_enable(struct imx_udc_struct *imx_usb)
+{
+	int temp = __raw_readl(imx_usb->base + USB_CTRL);
+	__raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, imx_usb->base + USB_CTRL);
+	imx_usb->gadget.speed = USB_SPEED_FULL;
+}
+
+void imx_udc_disable(struct imx_udc_struct *imx_usb)
+{
+	int temp = __raw_readl(imx_usb->base + USB_CTRL);
+
+	__raw_writel(temp & ~(CTRL_FE_ENA | CTRL_AFE_ENA),
+		 imx_usb->base + USB_CTRL);
+
+	ep0_chg_stat(__func__, imx_usb, EP0_IDLE);
+	imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+void imx_udc_reset(struct imx_udc_struct *imx_usb)
+{
+	int temp = __raw_readl(imx_usb->base + USB_ENAB);
+
+	/* set RST bit */
+	__raw_writel(temp | ENAB_RST, imx_usb->base + USB_ENAB);
+
+	/* wait RST bit to clear */
+	do {} while (__raw_readl(imx_usb->base + USB_ENAB) & ENAB_RST);
+
+	/* wait CFG bit to assert */
+	do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG));
+
+	/* udc module is now ready */
+}
+
+void imx_udc_config(struct imx_udc_struct *imx_usb)
+{
+	u8 ep_conf[5];
+	u8 i, j, cfg;
+	struct imx_ep_struct *imx_ep;
+
+	/* wait CFG bit to assert */
+	do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG));
+
+	/* Download the endpoint buffer for endpoint 0. */
+	for (j = 0; j < 5; j++) {
+		i = (j == 2 ? imx_usb->imx_ep[0].fifosize : 0x00);
+		__raw_writeb(i, imx_usb->base + USB_DDAT);
+		do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_BSY);
+	}
+
+	/* Download the endpoint buffers for endpoints 1-5.
+	 * We specify two configurations, one interface
+	 */
+	for (cfg = 1; cfg < 3; cfg++) {
+		for (i = 1; i < IMX_USB_NB_EP; i++) {
+			imx_ep = &imx_usb->imx_ep[i];
+			/* EP no | Config no */
+			ep_conf[0] = (i << 4) | (cfg << 2);
+			/* Type | Direction */
+			ep_conf[1] = (imx_ep->bmAttributes << 3) |
+					(EP_DIR(imx_ep) << 2);
+			/* Max packet size */
+			ep_conf[2] = imx_ep->fifosize;
+			/* TRXTYP */
+			ep_conf[3] = 0xC0;
+			/* FIFO no */
+			ep_conf[4] = i;
+
+			D_INI(imx_usb->dev,
+				"<%s> ep%d_conf[%d]:"
+				"[%02x-%02x-%02x-%02x-%02x]\n",
+				__func__, i, cfg,
+				ep_conf[0], ep_conf[1], ep_conf[2],
+				ep_conf[3], ep_conf[4]);
+
+			for (j = 0; j < 5; j++) {
+				__raw_writeb(ep_conf[j],
+					imx_usb->base + USB_DDAT);
+				do {} while (__raw_readl(imx_usb->base + USB_DADR)
+					& DADR_BSY);
+			}
+		}
+	}
+
+	/* wait CFG bit to clear */
+	do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG);
+}
+
+void imx_udc_init_irq(struct imx_udc_struct *imx_usb)
+{
+	int i;
+
+	/* Mask and clear all irqs */
+	__raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK);
+	__raw_writel(0xFFFFFFFF, imx_usb->base + USB_INTR);
+	for (i = 0; i < IMX_USB_NB_EP; i++) {
+		__raw_writel(0x1FF, imx_usb->base + USB_EP_MASK(i));
+		__raw_writel(0x1FF, imx_usb->base + USB_EP_INTR(i));
+	}
+
+	/* Enable USB irqs */
+	__raw_writel(INTR_MSOF | INTR_FRAME_MATCH, imx_usb->base + USB_MASK);
+
+	/* Enable EP0 irqs */
+	__raw_writel(0x1FF & ~(EPINTR_DEVREQ | EPINTR_MDEVREQ | EPINTR_EOT
+		| EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL),
+		imx_usb->base + USB_EP_MASK(0));
+}
+
+void imx_udc_init_ep(struct imx_udc_struct *imx_usb)
+{
+	int i, max, temp;
+	struct imx_ep_struct *imx_ep;
+	for (i = 0; i < IMX_USB_NB_EP; i++) {
+		imx_ep = &imx_usb->imx_ep[i];
+		switch (imx_ep->fifosize) {
+		case 8:
+			max = 0;
+			break;
+		case 16:
+			max = 1;
+			break;
+		case 32:
+			max = 2;
+			break;
+		case 64:
+			max = 3;
+			break;
+		default:
+			max = 1;
+			break;
+		}
+		temp = (EP_DIR(imx_ep) << 7) | (max << 5)
+			| (imx_ep->bmAttributes << 3);
+		__raw_writel(temp, imx_usb->base + USB_EP_STAT(i));
+		__raw_writel(temp | EPSTAT_FLUSH, imx_usb->base + USB_EP_STAT(i));
+		D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i,
+			__raw_readl(imx_usb->base + USB_EP_STAT(i)));
+	}
+}
+
+void imx_udc_init_fifo(struct imx_udc_struct *imx_usb)
+{
+	int i, temp;
+	struct imx_ep_struct *imx_ep;
+	for (i = 0; i < IMX_USB_NB_EP; i++) {
+		imx_ep = &imx_usb->imx_ep[i];
+
+		/* Fifo control */
+		temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000;
+		__raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i));
+		D_INI(imx_usb->dev, "<%s> ep%d_fctrl %08x\n", __func__, i,
+			__raw_readl(imx_usb->base + USB_EP_FCTRL(i)));
+
+		/* Fifo alarm */
+		temp = (i ? imx_ep->fifosize / 2 : 0);
+		__raw_writel(temp, imx_usb->base + USB_EP_FALRM(i));
+		D_INI(imx_usb->dev, "<%s> ep%d_falrm %08x\n", __func__, i,
+			__raw_readl(imx_usb->base + USB_EP_FALRM(i)));
+	}
+}
+
+static void imx_udc_init(struct imx_udc_struct *imx_usb)
+{
+	/* Reset UDC */
+	imx_udc_reset(imx_usb);
+
+	/* Download config to enpoint buffer */
+	imx_udc_config(imx_usb);
+
+	/* Setup interrups */
+	imx_udc_init_irq(imx_usb);
+
+	/* Setup endpoints */
+	imx_udc_init_ep(imx_usb);
+
+	/* Setup fifos */
+	imx_udc_init_fifo(imx_usb);
+}
+
+void imx_ep_irq_enable(struct imx_ep_struct *imx_ep)
+{
+
+	int i = EP_NO(imx_ep);
+
+	__raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i));
+	__raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i));
+	__raw_writel(0x1FF & ~(EPINTR_EOT | EPINTR_EOF),
+		imx_ep->imx_usb->base + USB_EP_MASK(i));
+}
+
+void imx_ep_irq_disable(struct imx_ep_struct *imx_ep)
+{
+
+	int i = EP_NO(imx_ep);
+
+	__raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i));
+	__raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i));
+}
+
+int imx_ep_empty(struct imx_ep_struct *imx_ep)
+{
+	struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
+
+	return __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)))
+			& FSTAT_EMPTY;
+}
+
+unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep)
+{
+	struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
+
+	return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)))
+			& EPSTAT_BCOUNT) >> 16;
+}
+
+void imx_flush(struct imx_ep_struct *imx_ep)
+{
+	struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
+
+	int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+	__raw_writel(temp | EPSTAT_FLUSH,
+		imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+}
+
+void imx_ep_stall(struct imx_ep_struct *imx_ep)
+{
+	struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
+	int temp, i;
+
+	D_ERR(imx_usb->dev, "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name);
+
+	imx_flush(imx_ep);
+
+	/* Special care for ep0 */
+	if (EP_NO(imx_ep)) {
+		temp = __raw_readl(imx_usb->base + USB_CTRL);
+		__raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, imx_usb->base + USB_CTRL);
+		do { } while (__raw_readl(imx_usb->base + USB_CTRL) & CTRL_CMDOVER);
+		temp = __raw_readl(imx_usb->base + USB_CTRL);
+		__raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL);
+	}
+	else {
+		temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+		__raw_writel(temp | EPSTAT_STALL,
+			imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+
+		for (i = 0; i < 100; i ++) {
+			temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+	 		if (!temp & EPSTAT_STALL)
+	 			break;
+	 		udelay(20);
+	 	}
+		if (i == 50)
+			D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n",
+				__func__, imx_ep->ep.name);
+	}
+}
+
+static int imx_udc_get_frame(struct usb_gadget *_gadget)
+{
+	struct imx_udc_struct *imx_usb = container_of(_gadget,
+			struct imx_udc_struct, gadget);
+
+	return __raw_readl(imx_usb->base + USB_FRAME) & 0x7FF;
+}
+
+static int imx_udc_wakeup(struct usb_gadget *_gadget)
+{
+	return 0;
+}
+
+/*******************************************************************************
+ * USB request control functions
+ *******************************************************************************
+ */
+
+static void ep_add_request(struct imx_ep_struct *imx_ep, struct imx_request *req)
+{
+	if (unlikely(!req))
+		return;
+
+	req->in_use = 1;
+	list_add_tail(&req->queue, &imx_ep->queue);
+}
+
+static void ep_del_request(struct imx_ep_struct *imx_ep, struct imx_request *req)
+{
+	if (unlikely(!req))
+		return;
+
+	list_del_init(&req->queue);
+	req->in_use = 0;
+}
+
+static void done(struct imx_ep_struct *imx_ep, struct imx_request *req, int status)
+{
+	ep_del_request(imx_ep, req);
+
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		D_ERR(imx_ep->imx_usb->dev,
+			"<%s> complete %s req %p stat %d len %u/%u\n", __func__,
+			imx_ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	req->req.complete(&imx_ep->ep, &req->req);
+}
+
+static void nuke(struct imx_ep_struct *imx_ep, int status)
+{
+	struct imx_request *req;
+
+	while (!list_empty(&imx_ep->queue)) {
+		req = list_entry(imx_ep->queue.next, struct imx_request, queue);
+		done(imx_ep, req, status);
+	}
+}
+
+/*******************************************************************************
+ * Data tansfer over USB functions
+ *******************************************************************************
+ */
+static int read_packet(struct imx_ep_struct *imx_ep, struct imx_request *req)
+{
+	u8	*buf;
+	int	bytes_ep, bufferspace, count, i;
+
+	bytes_ep = imx_fifo_bcount(imx_ep);
+	bufferspace = req->req.length - req->req.actual;
+
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+
+	if (unlikely(imx_ep_empty(imx_ep)))
+		count = 0;	/* zlp */
+	else
+		count = min(bytes_ep, bufferspace);
+
+	for (i = count; i > 0; i--)
+		*buf++ = __raw_readb(imx_ep->imx_usb->base
+						+ USB_EP_FDAT0(EP_NO(imx_ep)));
+	req->req.actual += count;
+
+	return count;
+}
+
+static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req)
+{
+	u8	*buf;
+	int	length, count, temp;
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize);
+
+	if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) {
+		D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n",
+			__func__, imx_ep->ep.name);
+		return -1;
+	}
+
+	req->req.actual += length;
+	count = length;
+
+	if (!count && req->req.zero) {	/* zlp */
+		temp = __raw_readl(imx_ep->imx_usb->base
+			+ USB_EP_STAT(EP_NO(imx_ep)));
+		__raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base
+			+ USB_EP_STAT(EP_NO(imx_ep)));
+		D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__);
+		return 0;
+	}
+
+	while (count--) {
+		if (count == 0) {	/* last byte */
+			temp = __raw_readl(imx_ep->imx_usb->base
+				+ USB_EP_FCTRL(EP_NO(imx_ep)));
+			__raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base
+				+ USB_EP_FCTRL(EP_NO(imx_ep)));
+		}
+		__raw_writeb(*buf++,
+			imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep)));
+	}
+
+	return length;
+}
+
+static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req)
+{
+	int 	bytes = 0,
+		count,
+		completed = 0;
+
+	while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)))
+		& FSTAT_FR) {
+			count = read_packet(imx_ep, req);
+			bytes += count;
+
+			completed = (count != imx_ep->fifosize);
+			if (completed || req->req.actual == req->req.length) {
+				completed = 1;
+				break;
+			}
+	}
+
+	if (completed || !req->req.length) {
+		done(imx_ep, req, 0);
+		D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n",
+			__func__, imx_ep->ep.name, req,
+			completed ? "completed" : "not completed");
+		if (!EP_NO(imx_ep))
+			ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE);
+	}
+
+	D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes);
+
+	return completed;
+}
+
+static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req)
+{
+	int	bytes = 0,
+		count,
+		completed = 0;
+
+	while (!completed) {
+		count = write_packet(imx_ep, req);
+		if (count < 0)
+			break; /* busy */
+		bytes += count;
+
+		/* last packet "must be" short (or a zlp) */
+		completed = (count != imx_ep->fifosize);
+
+		if (unlikely(completed)) {
+			done(imx_ep, req, 0);
+			D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n",
+				__func__, imx_ep->ep.name, req,
+				completed ? "completed" : "not completed");
+			if (!EP_NO(imx_ep))
+				ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE);
+		}
+	}
+
+	D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes);
+
+	return completed;
+}
+
+/*******************************************************************************
+ * Endpoint handlers
+ *******************************************************************************
+ */
+static int handle_ep(struct imx_ep_struct *imx_ep)
+{
+	struct imx_request *req;
+	int completed = 0;
+
+	do {
+		if (!list_empty(&imx_ep->queue))
+			req = list_entry(imx_ep->queue.next,
+				struct imx_request, queue);
+		else {
+			D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n",
+				__func__, imx_ep->ep.name);
+			return 0;
+		}
+
+		if (EP_DIR(imx_ep))	/* to host */
+			completed = write_fifo(imx_ep, req);
+		else			/* to device */
+			completed = read_fifo(imx_ep, req);
+
+		dump_ep_stat(__func__, imx_ep);
+
+	} while (completed);
+
+	return 0;
+}
+
+static int handle_ep0(struct imx_ep_struct *imx_ep)
+{
+	struct imx_request *req = NULL;
+	int ret = 0;
+
+	if (!list_empty(&imx_ep->queue))
+		req = list_entry(imx_ep->queue.next, struct imx_request, queue);
+
+	if (req) {
+		switch (imx_ep->imx_usb->ep0state) {
+
+		case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR */
+			write_fifo(imx_ep, req);
+			break;
+		case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR */
+			read_fifo(imx_ep, req);
+			break;
+		default:
+			D_EP0(imx_ep->imx_usb->dev,
+				"<%s> ep0 i/o, odd state %d\n",
+				__func__, imx_ep->imx_usb->ep0state);
+			ep_del_request(imx_ep, req);
+			ret = -EL2HLT;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void handle_ep0_devreq(struct imx_udc_struct *imx_usb)
+{
+	struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0];
+	union {
+		struct usb_ctrlrequest	r;
+		u8			raw[8];
+		u32			word[2];
+	} u;
+	int temp, i;
+
+	nuke(imx_ep, -EPROTO);
+
+	/* read SETUP packet */
+	for (i = 0; i < 2; i++) {
+		if (imx_ep_empty(imx_ep)) {
+			D_ERR(imx_usb->dev,
+				"<%s> no setup packet received\n", __func__);
+			goto stall;
+		}
+		u.word[i] = __raw_readl(imx_usb->base +	USB_EP_FDAT(EP_NO(imx_ep)));
+	}
+
+	temp = imx_ep_empty(imx_ep);
+	while (!imx_ep_empty(imx_ep)) {
+		i = __raw_readl(imx_usb->base +	USB_EP_FDAT(EP_NO(imx_ep)));
+		D_ERR(imx_usb->dev,
+			"<%s> wrong to have extra bytes for setup : 0x%08x\n",
+			__func__, i);
+	}
+	if (!temp)
+		goto stall;
+
+	le16_to_cpus(&u.r.wValue);
+	le16_to_cpus(&u.r.wIndex);
+	le16_to_cpus(&u.r.wLength);
+
+	D_REQ(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n",
+		__func__, u.r.bRequestType, u.r.bRequest,
+		u.r.wValue, u.r.wIndex, u.r.wLength);
+
+	if (imx_usb->set_config) {
+		/* NACK the host by using CMDOVER */
+		temp = __raw_readl(imx_usb->base + USB_CTRL);
+		__raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL);
+
+		D_ERR(imx_usb->dev,
+			"<%s> set config req is pending, NACK the host\n",
+			__func__);
+		return;
+	}
+
+	if (u.r.bRequestType & USB_DIR_IN)
+		ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE);
+	else
+		ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE);
+
+	i = imx_usb->driver->setup(&imx_usb->gadget, &u.r);
+	if (i < 0) {
+		D_ERR(imx_usb->dev, "<%s> device setup error %d\n",
+			__func__, i);
+		goto stall;
+	}
+
+	return;
+stall:
+	D_ERR(imx_usb->dev, "<%s> protocol STALL\n", __func__);
+	imx_ep_stall(imx_ep);
+	ep0_chg_stat(__func__, imx_usb, EP0_STALL);
+	return;
+}
+
+/*******************************************************************************
+ * USB gadget callback functions
+ *******************************************************************************
+ */
+
+static int imx_ep_enable(struct usb_ep *usb_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct imx_ep_struct *imx_ep = container_of(usb_ep,
+						struct imx_ep_struct, ep);
+	struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
+	unsigned long flags;
+
+	if (!usb_ep
+		|| !desc
+		|| !EP_NO(imx_ep)
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| imx_ep->bEndpointAddress != desc->bEndpointAddress) {
+			D_ERR(imx_usb->dev,
+				"<%s> bad ep or descriptor\n", __func__);
+			return -EINVAL;
+	}
+
+	if (imx_ep->bmAttributes != desc->bmAttributes) {
+		D_ERR(imx_usb->dev,
+			"<%s> %s type mismatch\n", __func__, usb_ep->name);
+		return -EINVAL;
+	}
+
+	if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) {
+		D_ERR(imx_usb->dev,
+			"<%s> bad %s maxpacket\n", __func__, usb_ep->name);
+		return -ERANGE;
+	}
+
+	if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) {
+		D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	local_irq_save(flags);
+
+	imx_ep->stopped = 0;
+	imx_flush(imx_ep);
+	imx_ep_irq_enable(imx_ep);
+
+	local_irq_restore(flags);
+
+	D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name);
+	return 0;
+}
+
+static int imx_ep_disable(struct usb_ep *usb_ep)
+{
+	struct imx_ep_struct *imx_ep = container_of(usb_ep,
+						struct imx_ep_struct, ep);
+	unsigned long flags;
+
+	if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) {
+		D_ERR(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n",
+			__func__, usb_ep ? imx_ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	imx_ep->stopped = 1;
+	nuke(imx_ep, -ESHUTDOWN);
+	imx_flush(imx_ep);
+	imx_ep_irq_disable(imx_ep);
+
+	local_irq_restore(flags);
+
+	D_EPX(imx_ep->imx_usb->dev,
+		"<%s> DISABLED %s\n", __func__, usb_ep->name);
+	return 0;
+}
+
+static struct usb_request *imx_ep_alloc_request
+					(struct usb_ep *usb_ep, gfp_t gfp_flags)
+{
+	struct imx_request *req;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req || !usb_ep)
+		return 0;
+
+	INIT_LIST_HEAD(&req->queue);
+	req->in_use = 0;
+
+	return &req->req;
+}
+
+static void imx_ep_free_request
+			(struct usb_ep *usb_ep, struct usb_request *usb_req)
+{
+	struct imx_request *req;
+
+	req = container_of(usb_req, struct imx_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static int imx_ep_queue
+	(struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags)
+{
+	struct imx_ep_struct	*imx_ep;
+	struct imx_udc_struct	*imx_usb;
+	struct imx_request	*req;
+	unsigned long		flags;
+	int			ret = 0;
+
+	imx_ep = container_of(usb_ep, struct imx_ep_struct, ep);
+	imx_usb = imx_ep->imx_usb;
+	req = container_of(usb_req, struct imx_request, req);
+
+	/*
+	  Special care on IMX udc.
+	  Ignore enqueue when after set configuration from the
+	  host. This assume all gadget drivers reply set
+	  configuration with the next ep0 req enqueue.
+	*/
+	if (imx_usb->set_config && !EP_NO(imx_ep)) {
+		imx_usb->set_config = 0;
+		D_EPX(imx_usb->dev,
+			"<%s> gadget reply set config\n", __func__);
+		return 0;
+	}
+
+	if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) {
+		D_ERR(imx_usb->dev, "<%s> bad params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (unlikely(!usb_ep || !imx_ep)) {
+		D_ERR(imx_usb->dev, "<%s> bad ep\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) {
+		D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	local_irq_save(flags);
+
+	/* Debug */
+	D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n",
+		__func__, EP_NO(imx_ep),
+		((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
+		|| (EP_NO(imx_ep) && EP_DIR(imx_ep))) ? "IN" : "OUT", usb_req->length);
+	dump_req(__func__, imx_ep, usb_req);
+
+	if (imx_ep->stopped) {
+		usb_req->status = -ESHUTDOWN;
+		ret = -ESHUTDOWN;
+		goto out;
+	}
+
+	if (req->in_use) {
+		D_ERR(imx_usb->dev,
+			"<%s> refusing to queue req %p (already queued)\n",
+			__func__, req);
+		goto out;
+	}
+
+	usb_req->status = -EINPROGRESS;
+	usb_req->actual = 0;
+
+	ep_add_request(imx_ep, req);
+
+	if (!EP_NO(imx_ep))
+		ret = handle_ep0(imx_ep);
+	else
+		ret = handle_ep(imx_ep);
+out:
+	local_irq_restore(flags);
+	return ret;
+}
+
+static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
+{
+
+	struct imx_ep_struct *imx_ep = container_of
+					(usb_ep, struct imx_ep_struct, ep);
+	struct imx_request *req;
+	unsigned long flags;
+
+	if (unlikely(!usb_ep || !EP_NO(imx_ep))) {
+		D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &imx_ep->queue, queue) {
+		if (&req->req == usb_req)
+			break;
+	}
+	if (&req->req != usb_req) {
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	done(imx_ep, req, -ECONNRESET);
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int imx_ep_set_halt(struct usb_ep *usb_ep, int value)
+{
+	struct imx_ep_struct *imx_ep = container_of
+					(usb_ep, struct imx_ep_struct, ep);
+	unsigned long flags;
+
+	if (unlikely(!usb_ep || !EP_NO(imx_ep))) {
+		D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	if ((imx_ep->bEndpointAddress & USB_DIR_IN)
+		&& !list_empty(&imx_ep->queue)) {
+			local_irq_restore(flags);
+			return -EAGAIN;
+	}
+
+	imx_ep_stall(imx_ep);
+
+	local_irq_restore(flags);
+
+	D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name);
+	return 0;
+}
+
+static int imx_ep_fifo_status(struct usb_ep *usb_ep)
+{
+	struct imx_ep_struct *imx_ep = container_of
+					(usb_ep, struct imx_ep_struct, ep);
+
+	if (!usb_ep) {
+		D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
+		return -ENODEV;
+	}
+
+	if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN)
+		return 0;
+	else
+		return imx_fifo_bcount(imx_ep);
+}
+
+static void imx_ep_fifo_flush(struct usb_ep *usb_ep)
+{
+	struct imx_ep_struct *imx_ep = container_of
+					(usb_ep, struct imx_ep_struct, ep);
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) {
+		D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
+		local_irq_restore(flags);
+		return;
+	}
+
+	/* toggle and halt bits stay unchanged */
+	imx_flush(imx_ep);
+
+	local_irq_restore(flags);
+}
+
+static struct usb_ep_ops imx_ep_ops = {
+	.enable		= imx_ep_enable,
+	.disable	= imx_ep_disable,
+
+	.alloc_request	= imx_ep_alloc_request,
+	.free_request	= imx_ep_free_request,
+
+	.queue		= imx_ep_queue,
+	.dequeue	= imx_ep_dequeue,
+
+	.set_halt	= imx_ep_set_halt,
+	.fifo_status	= imx_ep_fifo_status,
+	.fifo_flush	= imx_ep_fifo_flush,
+};
+
+/*******************************************************************************
+ * USB endpoint control functions
+ *******************************************************************************
+ */
+
+void ep0_chg_stat(const char *label,
+			struct imx_udc_struct *imx_usb, enum ep0_state stat)
+{
+	D_EP0(imx_usb->dev, "<%s> from %15s to %15s\n",
+		label, state_name[imx_usb->ep0state], state_name[stat]);
+
+	if (imx_usb->ep0state == stat)
+		return;
+
+	imx_usb->ep0state = stat;
+}
+
+static void usb_init_data(struct imx_udc_struct *imx_usb)
+{
+	struct imx_ep_struct *imx_ep;
+	u8 i;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&imx_usb->gadget.ep_list);
+	INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list);
+	ep0_chg_stat(__func__, imx_usb, EP0_IDLE);
+
+	/* basic endpoint records init */
+	for (i = 0; i < IMX_USB_NB_EP; i++) {
+		imx_ep = &imx_usb->imx_ep[i];
+
+		if (i) {
+			list_add_tail(&imx_ep->ep.ep_list,
+				&imx_usb->gadget.ep_list);
+			imx_ep->stopped = 1;
+		} else
+			imx_ep->stopped = 0;
+
+		INIT_LIST_HEAD(&imx_ep->queue);
+	}
+}
+
+static void udc_stop_activity(struct imx_udc_struct *imx_usb,
+					struct usb_gadget_driver *driver)
+{
+	struct imx_ep_struct *imx_ep;
+	int i;
+
+	if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 1; i < IMX_USB_NB_EP; i++) {
+		imx_ep = &imx_usb->imx_ep[i];
+		imx_flush(imx_ep);
+		imx_ep->stopped = 1;
+		imx_ep_irq_disable(imx_ep);
+		nuke(imx_ep, -ESHUTDOWN);
+	}
+
+	imx_usb->cfg = 0;
+	imx_usb->intf = 0;
+	imx_usb->alt = 0;
+
+	if (driver)
+		driver->disconnect(&imx_usb->gadget);
+}
+
+/*******************************************************************************
+ * Interrupt handlers
+ *******************************************************************************
+ */
+
+static irqreturn_t imx_udc_irq(int irq, void *dev)
+{
+	struct imx_udc_struct *imx_usb = dev;
+	struct usb_ctrlrequest u;
+	int temp, cfg, intf, alt;
+	int intr = __raw_readl(imx_usb->base + USB_INTR);
+
+	if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
+			| INTR_RESET_STOP | INTR_CFG_CHG)) {
+				dump_intr(__func__, intr, imx_usb->dev);
+				dump_usb_stat(__func__, imx_usb);
+	}
+
+	if (!imx_usb->driver) {
+		/*imx_udc_disable(imx_usb);*/
+		goto end_irq;
+	}
+
+	if (intr & INTR_WAKEUP) {
+		if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
+			&& imx_usb->driver && imx_usb->driver->resume)
+				imx_usb->driver->resume(&imx_usb->gadget);
+		imx_usb->set_config = 0;
+		imx_usb->gadget.speed = USB_SPEED_FULL;
+	}
+
+	if (intr & INTR_SUSPEND) {
+		if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
+			&& imx_usb->driver && imx_usb->driver->suspend)
+				imx_usb->driver->suspend(&imx_usb->gadget);
+		imx_usb->set_config = 0;
+		imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
+	}
+
+	if (intr & INTR_RESET_START) {
+		__raw_writel(intr, imx_usb->base + USB_INTR);
+		udc_stop_activity(imx_usb, imx_usb->driver);
+		imx_usb->set_config = 0;
+		imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
+	}
+
+	if (intr & INTR_RESET_STOP)
+		imx_usb->gadget.speed = USB_SPEED_FULL;
+
+	if (intr & INTR_CFG_CHG) {
+		__raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
+		temp = __raw_readl(imx_usb->base + USB_STAT);
+		cfg  = (temp & STAT_CFG) >> 5;
+		intf = (temp & STAT_INTF) >> 3;
+		alt  =  temp & STAT_ALTSET;
+
+		D_REQ(imx_usb->dev,
+			"<%s> orig config C=%d, I=%d, A=%d / "
+			"req config C=%d, I=%d, A=%d\n",
+			__func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
+			cfg, intf, alt);
+
+		if (cfg != 1 && cfg != 2)
+			goto end_irq;
+
+		imx_usb->set_config = 0;
+
+		/* Config setup */
+		if (imx_usb->cfg != cfg) {
+			D_REQ(imx_usb->dev, "<%s> Change config start\n",__func__);
+			u.bRequest = USB_REQ_SET_CONFIGURATION;
+			u.bRequestType = USB_DIR_OUT |
+					USB_TYPE_STANDARD |
+					USB_RECIP_DEVICE;
+			u.wValue = cfg;
+			u.wIndex = 0;
+			u.wLength = 0;
+			imx_usb->cfg = cfg;
+			imx_usb->set_config = 1;
+			imx_usb->driver->setup(&imx_usb->gadget, &u);
+			imx_usb->set_config = 0;
+			D_REQ(imx_usb->dev, "<%s> Change config done\n",__func__);
+
+		}
+		if (imx_usb->intf != intf || imx_usb->alt != alt) {
+			D_REQ(imx_usb->dev, "<%s> Change interface start\n",__func__);
+			u.bRequest = USB_REQ_SET_INTERFACE;
+			u.bRequestType = USB_DIR_OUT |
+					  USB_TYPE_STANDARD |
+					  USB_RECIP_INTERFACE;
+			u.wValue = alt;
+			u.wIndex = intf;
+			u.wLength = 0;
+			imx_usb->intf = intf;
+			imx_usb->alt = alt;
+			imx_usb->set_config = 1;
+			imx_usb->driver->setup(&imx_usb->gadget, &u);
+			imx_usb->set_config = 0;
+			D_REQ(imx_usb->dev, "<%s> Change interface done\n",__func__);
+		}
+	}
+
+	if (intr & INTR_SOF) {
+		if (imx_usb->ep0state == EP0_IDLE) {
+			temp = __raw_readl(imx_usb->base + USB_CTRL);
+			__raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL);
+		}
+	}
+
+end_irq:
+	__raw_writel(intr, imx_usb->base + USB_INTR);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
+{
+	struct imx_udc_struct *imx_usb = dev;
+	int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0));
+
+	dump_ep_intr(__func__, 0, intr, imx_usb->dev);
+
+	if (!imx_usb->driver) {
+		__raw_writel(intr, imx_usb->base + USB_EP_INTR(0));
+		return IRQ_HANDLED;
+	}
+
+	/* DEVREQ IRQ has highest priority */
+	if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ))
+		handle_ep0_devreq(imx_usb);
+	/* Seem i.MX is missing EOF interrupt sometimes.
+	 * Therefore we monitor both EOF and FIFO_EMPTY interrups
+	 * when transmiting, and both EOF and FIFO_FULL when
+	 * receiving data.
+	 */
+	else if (intr & (EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL))
+		handle_ep0(&imx_usb->imx_ep[0]);
+
+	__raw_writel(intr, imx_usb->base + USB_EP_INTR(0));
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_udc_bulk_irq(int irq, void *dev)
+{
+	struct imx_udc_struct *imx_usb = dev;
+	struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0];
+	int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
+
+	dump_ep_intr(__func__, irq - USBD_INT0, intr, imx_usb->dev);
+
+	if (!imx_usb->driver) {
+		__raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
+		return IRQ_HANDLED;
+	}
+
+	handle_ep(imx_ep);
+
+	__raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
+
+	return IRQ_HANDLED;
+}
+
+irq_handler_t intr_handler(int i)
+{
+	switch (i) {
+	case 0:
+		return imx_udc_ctrl_irq;
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+		return imx_udc_bulk_irq;
+	default:
+		return imx_udc_irq;
+	}
+}
+
+/*******************************************************************************
+ * Static defined IMX UDC structure
+ *******************************************************************************
+ */
+
+static const struct usb_gadget_ops imx_udc_ops = {
+	.get_frame	 = imx_udc_get_frame,
+	.wakeup		 = imx_udc_wakeup,
+};
+
+static struct imx_udc_struct controller = {
+	.gadget = {
+		.ops		= &imx_udc_ops,
+		.ep0		= &controller.imx_ep[0].ep,
+		.name		= driver_name,
+		.dev = {
+			 .bus_id	= "gadget",
+		 },
+	},
+
+	.imx_ep[0] = {
+		.ep = {
+			.name		= ep0name,
+			.ops		= &imx_ep_ops,
+			.maxpacket	= 32,
+		},
+		.imx_usb		= &controller,
+		.fifosize		= 32,
+		.bEndpointAddress	= 0,
+		.bmAttributes		= USB_ENDPOINT_XFER_CONTROL,
+	 },
+	.imx_ep[1] = {
+		.ep = {
+			.name		= "ep1in-bulk",
+			.ops		= &imx_ep_ops,
+			.maxpacket	= 64,
+		},
+		.imx_usb		= &controller,
+		.fifosize		= 64,
+		.bEndpointAddress	= USB_DIR_IN | 1,
+		.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	 },
+	.imx_ep[2] = {
+		.ep = {
+			.name		= "ep2out-bulk",
+			.ops		= &imx_ep_ops,
+			.maxpacket	= 64,
+		},
+		.imx_usb		= &controller,
+		.fifosize		= 64,
+		.bEndpointAddress	= USB_DIR_OUT | 2,
+		.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	 },
+	.imx_ep[3] = {
+		.ep = {
+			.name		= "ep3out-bulk",
+			.ops		= &imx_ep_ops,
+			.maxpacket	= 32,
+		},
+		.imx_usb		= &controller,
+		.fifosize		= 32,
+		.bEndpointAddress 	= USB_DIR_OUT | 3,
+		.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	 },
+	.imx_ep[4] = {
+		.ep = {
+			.name		= "ep4in-int",
+			.ops		= &imx_ep_ops,
+			.maxpacket	= 32,
+		 },
+		.imx_usb		= &controller,
+		.fifosize		= 32,
+		.bEndpointAddress 	= USB_DIR_IN | 4,
+		.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	 },
+	.imx_ep[5] = {
+		.ep = {
+			.name		= "ep5out-int",
+			.ops		= &imx_ep_ops,
+			.maxpacket	= 32,
+		},
+		.imx_usb		= &controller,
+		.fifosize		= 32,
+		.bEndpointAddress 	= USB_DIR_OUT | 5,
+		.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	 },
+};
+
+/*******************************************************************************
+ * USB gadged driver functions
+ *******************************************************************************
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct imx_udc_struct *imx_usb = &controller;
+	int retval;
+
+	if (!driver
+		|| driver->speed < USB_SPEED_FULL
+		|| !driver->bind
+		|| !driver->disconnect
+		|| !driver->setup)
+			return -EINVAL;
+	if (!imx_usb)
+		return -ENODEV;
+	if (imx_usb->driver)
+		return -EBUSY;
+
+	/* first hook up the driver ... */
+	imx_usb->driver = driver;
+	imx_usb->gadget.dev.driver = &driver->driver;
+
+	retval = device_add(&imx_usb->gadget.dev);
+	if (retval)
+		goto fail;
+	retval = driver->bind(&imx_usb->gadget);
+	if (retval) {
+		D_ERR(imx_usb->dev, "<%s> bind to driver %s --> error %d\n",
+			__func__, driver->driver.name, retval);
+		device_del(&imx_usb->gadget.dev);
+
+		goto fail;
+	}
+
+	D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n",
+		__func__, driver->driver.name);
+
+	imx_udc_enable(imx_usb);
+
+	return 0;
+fail:
+	imx_usb->driver = NULL;
+	imx_usb->gadget.dev.driver = NULL;
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct imx_udc_struct *imx_usb = &controller;
+
+	if (!imx_usb)
+		return -ENODEV;
+	if (!driver || driver != imx_usb->driver || !driver->unbind)
+		return -EINVAL;
+
+	udc_stop_activity(imx_usb, driver);
+	imx_udc_disable(imx_usb);
+
+	driver->unbind(&imx_usb->gadget);
+	imx_usb->gadget.dev.driver = NULL;
+	imx_usb->driver = NULL;
+
+	device_del(&imx_usb->gadget.dev);
+
+	D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n",
+		__func__, driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*******************************************************************************
+ * Module functions
+ *******************************************************************************
+ */
+
+static int __init imx_udc_probe(struct platform_device *pdev)
+{
+	struct imx_udc_struct *imx_usb = &controller;
+	struct resource *res;
+	struct imxusb_platform_data *pdata;
+	struct clk *clk;
+	void __iomem *base;
+	int ret = 0;
+	int i, res_size;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "can't get device resources\n");
+		return -ENODEV;
+	}
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "driver needs platform data\n");
+		return -ENODEV;
+	}
+
+	res_size = res->end - res->start + 1;
+	if (!request_mem_region(res->start, res_size, res->name)) {
+		dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
+			res_size, res->start);
+		return -ENOMEM;
+	}
+
+	if (pdata->init) {
+		ret = pdata->init(&pdev->dev);
+		if (ret)
+			goto fail0;
+	}
+
+	base = ioremap(res->start, res_size);
+	if (!base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -EIO;
+		goto fail1;
+	}
+
+	clk = clk_get(NULL, "usbd_clk");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		dev_err(&pdev->dev, "can't get USB clock\n");
+		goto fail2;
+	}
+	clk_enable(clk);
+
+	if (clk_get_rate(clk) != 48000000) {
+		D_INI(&pdev->dev,
+			"Bad USB clock (%d Hz), changing to 48000000 Hz\n",
+			(int)clk_get_rate(clk));
+		if (clk_set_rate(clk, 48000000)) {
+			dev_err(&pdev->dev,
+				"Unable to set correct USB clock (48MHz)\n");
+			ret = -EIO;
+			goto fail3;
+		}
+	}
+
+	for (i = 0; i < IMX_USB_NB_EP + 1; i++) {
+		imx_usb->usbd_int[i] = platform_get_irq(pdev, i);
+		if (imx_usb->usbd_int[i] < 0) {
+			dev_err(&pdev->dev, "can't get irq number\n");
+			ret = -ENODEV;
+			goto fail3;
+		}
+	}
+
+	for (i = 0; i < IMX_USB_NB_EP + 1; i++) {
+		ret = request_irq(imx_usb->usbd_int[i], intr_handler(i),
+				     IRQF_DISABLED, driver_name, imx_usb);
+		if (ret) {
+			dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+				imx_usb->usbd_int[i], ret);
+			for (--i; i >= 0; i--)
+				free_irq(imx_usb->usbd_int[i], imx_usb);
+			goto fail3;
+		}
+	}
+
+	imx_usb->res = res;
+	imx_usb->base = base;
+	imx_usb->clk = clk;
+	imx_usb->dev = &pdev->dev;
+
+	device_initialize(&imx_usb->gadget.dev);
+
+	imx_usb->gadget.dev.parent = &pdev->dev;
+	imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+	platform_set_drvdata(pdev, imx_usb);
+
+	usb_init_data(imx_usb);
+	imx_udc_init(imx_usb);
+
+	return 0;
+
+fail3:
+	clk_put(clk);
+	clk_disable(clk);
+fail2:
+	iounmap(base);
+fail1:
+	if (pdata->exit)
+		pdata->exit(&pdev->dev);
+fail0:
+	release_mem_region(res->start, res_size);
+	return ret;
+}
+
+static int __exit imx_udc_remove(struct platform_device *pdev)
+{
+	struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev);
+	struct imxusb_platform_data *pdata = pdev->dev.platform_data;
+	int i;
+
+	imx_udc_disable(imx_usb);
+
+	for (i = 0; i < IMX_USB_NB_EP + 1; i++)
+		free_irq(imx_usb->usbd_int[i], imx_usb);
+
+	clk_put(imx_usb->clk);
+	clk_disable(imx_usb->clk);
+	iounmap(imx_usb->base);
+
+	release_mem_region(imx_usb->res->start,
+		imx_usb->res->end - imx_usb->res->start + 1);
+
+	if (pdata->exit)
+		pdata->exit(&pdev->dev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+#define	imx_udc_suspend	NULL
+#define	imx_udc_resume	NULL
+#else
+#define	imx_udc_suspend	NULL
+#define	imx_udc_resume	NULL
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+	.driver		= {
+		.name	= driver_name,
+		.owner	= THIS_MODULE,
+	},
+	.remove		= __exit_p(imx_udc_remove),
+	.suspend	= imx_udc_suspend,
+	.resume		= imx_udc_resume,
+};
+
+static int __init udc_init(void)
+{
+	return platform_driver_probe(&udc_driver, imx_udc_probe);
+}
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+	platform_driver_unregister(&udc_driver);
+}
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION("IMX USB Device Controller driver");
+MODULE_AUTHOR("Darius Augulis <augulis.darius@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx_udc");
diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h
new file mode 100644
index 0000000..8500769
--- /dev/null
+++ b/drivers/usb/gadget/imx_udc.h
@@ -0,0 +1,344 @@
+/*
+ *	Copyright (C) 2005 Mike Lee(eemike@gmail.com)
+ *
+ *	This udc driver is now under testing and code is based on pxa2xx_udc.h
+ *	Please use it with your own risk!
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_USB_GADGET_IMX_H
+#define __LINUX_USB_GADGET_IMX_H
+
+#include <linux/types.h>
+
+/* Helper macros */
+#define EP_NO(ep)	((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
+#define EP_DIR(ep)	((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
+#define irq_to_ep(irq)	(((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
+#define ep_to_irq(ep)	(EP_NO((ep)) + USBD_INT0)
+#define IMX_USB_NB_EP	6
+
+/* Driver structures */
+struct imx_request {
+	struct usb_request			req;
+	struct list_head			queue;
+	unsigned int				in_use;
+};
+
+enum ep0_state {
+	EP0_IDLE,
+	EP0_IN_DATA_PHASE,
+	EP0_OUT_DATA_PHASE,
+	EP0_CONFIG,
+	EP0_STALL,
+};
+
+struct imx_ep_struct {
+	struct usb_ep				ep;
+	struct imx_udc_struct			*imx_usb;
+	struct list_head			queue;
+	unsigned char				stopped;
+	unsigned char				fifosize;
+	unsigned char				bEndpointAddress;
+	unsigned char				bmAttributes;
+};
+
+struct imx_udc_struct {
+	struct usb_gadget			gadget;
+	struct usb_gadget_driver		*driver;
+	struct device				*dev;
+	struct imx_ep_struct			imx_ep[IMX_USB_NB_EP];
+	struct clk				*clk;
+	enum ep0_state				ep0state;
+	struct resource				*res;
+	void __iomem				*base;
+	unsigned char				set_config;
+	int					cfg,
+						intf,
+						alt,
+						usbd_int[7];
+};
+
+/* USB registers */
+#define  USB_FRAME		(0x00)	/* USB frame */
+#define  USB_SPEC		(0x04)	/* USB Spec */
+#define  USB_STAT		(0x08)	/* USB Status */
+#define  USB_CTRL		(0x0C)	/* USB Control */
+#define  USB_DADR		(0x10)	/* USB Desc RAM addr */
+#define  USB_DDAT		(0x14)	/* USB Desc RAM/EP buffer data */
+#define  USB_INTR		(0x18)	/* USB interrupt */
+#define  USB_MASK		(0x1C)	/* USB Mask */
+#define  USB_ENAB		(0x24)	/* USB Enable */
+#define  USB_EP_STAT(x)		(0x30 + (x*0x30)) /* USB status/control */
+#define  USB_EP_INTR(x)		(0x34 + (x*0x30)) /* USB interrupt */
+#define  USB_EP_MASK(x)		(0x38 + (x*0x30)) /* USB mask */
+#define  USB_EP_FDAT(x)		(0x3C + (x*0x30)) /* USB FIFO data */
+#define  USB_EP_FDAT0(x)	(0x3C + (x*0x30)) /* USB FIFO data */
+#define  USB_EP_FDAT1(x)	(0x3D + (x*0x30)) /* USB FIFO data */
+#define  USB_EP_FDAT2(x)	(0x3E + (x*0x30)) /* USB FIFO data */
+#define  USB_EP_FDAT3(x)	(0x3F + (x*0x30)) /* USB FIFO data */
+#define  USB_EP_FSTAT(x)	(0x40 + (x*0x30)) /* USB FIFO status */
+#define  USB_EP_FCTRL(x)	(0x44 + (x*0x30)) /* USB FIFO control */
+#define  USB_EP_LRFP(x)		(0x48 + (x*0x30)) /* USB last read frame pointer */
+#define  USB_EP_LWFP(x)		(0x4C + (x*0x30)) /* USB last write frame pointer */
+#define  USB_EP_FALRM(x)	(0x50 + (x*0x30)) /* USB FIFO alarm */
+#define  USB_EP_FRDP(x)		(0x54 + (x*0x30)) /* USB FIFO read pointer */
+#define  USB_EP_FWRP(x)		(0x58 + (x*0x30)) /* USB FIFO write pointer */
+/* USB Control Register Bit Fields.*/
+#define CTRL_CMDOVER		(1<<6)	/* UDC status */
+#define CTRL_CMDERROR		(1<<5)	/* UDC status */
+#define CTRL_FE_ENA		(1<<3)	/* Enable Font End logic */
+#define CTRL_UDC_RST		(1<<2)	/* UDC reset */
+#define CTRL_AFE_ENA		(1<<1)	/* Analog Font end enable */
+#define CTRL_RESUME		(1<<0)	/* UDC resume */
+/* USB Status Register Bit Fields.*/
+#define STAT_RST		(1<<8)
+#define STAT_SUSP		(1<<7)
+#define STAT_CFG		(3<<5)
+#define STAT_INTF		(3<<3)
+#define STAT_ALTSET		(7<<0)
+/* USB Interrupt Status/Mask Registers Bit fields */
+#define INTR_WAKEUP		(1<<31)	/* Wake up Interrupt */
+#define INTR_MSOF		(1<<7)	/* Missed Start of Frame */
+#define INTR_SOF		(1<<6)	/* Start of Frame */
+#define INTR_RESET_STOP		(1<<5)	/* Reset Signaling stop */
+#define INTR_RESET_START	(1<<4)	/* Reset Signaling start */
+#define INTR_RESUME		(1<<3)	/* Suspend to resume */
+#define INTR_SUSPEND		(1<<2)	/* Active to suspend */
+#define INTR_FRAME_MATCH	(1<<1)	/* Frame matched */
+#define INTR_CFG_CHG		(1<<0)	/* Configuration change occurred */
+/* USB Enable Register Bit Fields.*/
+#define ENAB_RST		(1<<31)	/* Reset USB modules */
+#define ENAB_ENAB		(1<<30)	/* Enable USB modules*/
+#define ENAB_SUSPEND		(1<<29)	/* Suspend USB modules */
+#define ENAB_ENDIAN		(1<<28)	/* Endian of USB modules */
+#define ENAB_PWRMD		(1<<0)	/* Power mode of USB modules */
+/* USB Descriptor Ram Address Register bit fields */
+#define DADR_CFG		(1<<31)	/* Configuration */
+#define DADR_BSY		(1<<30)	/* Busy status */
+#define DADR_DADR		(0x1FF)	/* Descriptor Ram Address */
+/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */
+#define DDAT_DDAT		(0xFF)	/* Descriptor Endpoint Buffer */
+/* USB Endpoint Status Register bit fields */
+#define EPSTAT_BCOUNT		(0x7F<<16)	/* Endpoint FIFO byte count */
+#define EPSTAT_SIP		(1<<8)	/* Endpoint setup in progress */
+#define EPSTAT_DIR		(1<<7)	/* Endpoint transfer direction */
+#define EPSTAT_MAX		(3<<5)	/* Endpoint Max packet size */
+#define EPSTAT_TYP		(3<<3)	/* Endpoint type */
+#define EPSTAT_ZLPS		(1<<2)	/* Send zero length packet */
+#define EPSTAT_FLUSH		(1<<1)	/* Endpoint FIFO Flush */
+#define EPSTAT_STALL		(1<<0)	/* Force stall */
+/* USB Endpoint FIFO Status Register bit fields */
+#define FSTAT_FRAME_STAT	(0xF<<24)	/* Frame status bit [0-3] */
+#define FSTAT_ERR		(1<<22)	/* FIFO error */
+#define FSTAT_UF		(1<<21)	/* FIFO underflow */
+#define FSTAT_OF		(1<<20)	/* FIFO overflow */
+#define FSTAT_FR		(1<<19)	/* FIFO frame ready */
+#define FSTAT_FULL		(1<<18)	/* FIFO full */
+#define FSTAT_ALRM		(1<<17)	/* FIFO alarm */
+#define FSTAT_EMPTY		(1<<16)	/* FIFO empty */
+/* USB Endpoint FIFO Control Register bit fields */
+#define FCTRL_WFR		(1<<29)	/* Write frame end */
+/* USB Endpoint Interrupt Status Regsiter bit fields */
+#define EPINTR_FIFO_FULL	(1<<8)	/* fifo full */
+#define EPINTR_FIFO_EMPTY	(1<<7)	/* fifo empty */
+#define EPINTR_FIFO_ERROR	(1<<6)	/* fifo error */
+#define EPINTR_FIFO_HIGH	(1<<5)	/* fifo high */
+#define EPINTR_FIFO_LOW		(1<<4)	/* fifo low */
+#define EPINTR_MDEVREQ		(1<<3)	/* multi Device request */
+#define EPINTR_EOT		(1<<2)	/* fifo end of transfer */
+#define EPINTR_DEVREQ		(1<<1)	/* Device request */
+#define EPINTR_EOF		(1<<0)	/* fifo end of frame */
+
+/* Debug macros */
+#ifdef DEBUG
+
+/* #define DEBUG_REQ */
+/* #define DEBUG_TRX */
+/* #define DEBUG_INIT */
+/* #define DEBUG_EP0 */
+/* #define DEBUG_EPX */
+/* #define DEBUG_IRQ */
+/* #define DEBUG_EPIRQ */
+/* #define DEBUG_DUMP */
+#define DEBUG_ERR
+
+#ifdef DEBUG_REQ
+	#define D_REQ(dev, args...)	dev_dbg(dev, ## args)
+#else
+	#define D_REQ(dev, args...)	do {} while (0)
+#endif /* DEBUG_REQ */
+
+#ifdef DEBUG_TRX
+	#define D_TRX(dev, args...)	dev_dbg(dev, ## args)
+#else
+	#define D_TRX(dev, args...)	do {} while (0)
+#endif /* DEBUG_TRX */
+
+#ifdef DEBUG_INIT
+	#define D_INI(dev, args...)	dev_dbg(dev, ## args)
+#else
+	#define D_INI(dev, args...)	do {} while (0)
+#endif /* DEBUG_INIT */
+
+#ifdef DEBUG_EP0
+	static const char *state_name[] = {
+		"EP0_IDLE",
+		"EP0_IN_DATA_PHASE",
+		"EP0_OUT_DATA_PHASE",
+		"EP0_CONFIG",
+		"EP0_STALL"
+	};
+	#define D_EP0(dev, args...)	dev_dbg(dev, ## args)
+#else
+	#define D_EP0(dev, args...)	do {} while (0)
+#endif /* DEBUG_EP0 */
+
+#ifdef DEBUG_EPX
+	#define D_EPX(dev, args...)	dev_dbg(dev, ## args)
+#else
+	#define D_EPX(dev, args...)	do {} while (0)
+#endif /* DEBUG_EP0 */
+
+#ifdef DEBUG_IRQ
+	static void dump_intr(const char *label, int irqreg, struct device *dev)
+	{
+		dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label,
+			(irqreg & INTR_WAKEUP) ? " wake" : "",
+			(irqreg & INTR_MSOF) ? " msof" : "",
+			(irqreg & INTR_SOF) ? " sof" : "",
+			(irqreg & INTR_RESUME) ? " resume" : "",
+			(irqreg & INTR_SUSPEND) ? " suspend" : "",
+			(irqreg & INTR_RESET_STOP) ? " noreset" : "",
+			(irqreg & INTR_RESET_START) ? " reset" : "",
+			(irqreg & INTR_FRAME_MATCH) ? " fmatch" : "",
+			(irqreg & INTR_CFG_CHG) ? " config" : "");
+	}
+#else
+	#define dump_intr(x, y, z)		do {} while (0)
+#endif /* DEBUG_IRQ */
+
+#ifdef DEBUG_EPIRQ
+	static void dump_ep_intr(const char *label, int nr, int irqreg, struct device *dev)
+	{
+		dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr,
+			(irqreg & EPINTR_FIFO_FULL) ? " full" : "",
+			(irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "",
+			(irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "",
+			(irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "",
+			(irqreg & EPINTR_FIFO_LOW) ? " flow" : "",
+			(irqreg & EPINTR_MDEVREQ) ? " mreq" : "",
+			(irqreg & EPINTR_EOF) ? " eof" : "",
+			(irqreg & EPINTR_DEVREQ) ? " devreq" : "",
+			(irqreg & EPINTR_EOT) ? " eot" : "");
+	}
+#else
+	#define dump_ep_intr(x, y, z, i)	do {} while (0)
+#endif /* DEBUG_IRQ */
+
+#ifdef DEBUG_DUMP
+	static void dump_usb_stat(const char *label, struct imx_udc_struct *imx_usb)
+	{
+		int temp = __raw_readl(imx_usb->base + USB_STAT);
+
+		dev_dbg(imx_usb->dev,
+			"<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label,
+			(temp & STAT_RST) ? " reset" : "",
+			(temp & STAT_SUSP) ? " suspend" : "",
+			(temp & STAT_CFG) >> 5,
+			(temp & STAT_INTF) >> 3,
+			(temp & STAT_ALTSET));
+	}
+
+	static void dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep)
+	{
+		int temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
+
+		dev_dbg(imx_ep->imx_usb->dev,
+			"<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
+			(temp & EPINTR_FIFO_FULL) ? " full" : "",
+			(temp & EPINTR_FIFO_EMPTY) ? " fempty" : "",
+			(temp & EPINTR_FIFO_ERROR) ? " ferr" : "",
+			(temp & EPINTR_FIFO_HIGH) ? " fhigh" : "",
+			(temp & EPINTR_FIFO_LOW) ? " flow" : "",
+			(temp & EPINTR_MDEVREQ) ? " mreq" : "",
+			(temp & EPINTR_EOF) ? " eof" : "",
+			(temp & EPINTR_DEVREQ) ? " devreq" : "",
+			(temp & EPINTR_EOT) ? " eot" : "");
+
+		temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+
+		dev_dbg(imx_ep->imx_usb->dev,
+			"<%s> EP%d_STAT=[%s%s bcount=%d]\n", label, EP_NO(imx_ep),
+			(temp & EPSTAT_SIP) ? " sip" : "",
+			(temp & EPSTAT_STALL) ? " stall" : "",
+			(temp & EPSTAT_BCOUNT) >> 16);
+
+		temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)));
+
+		dev_dbg(imx_ep->imx_usb->dev,
+			"<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
+			(temp & FSTAT_ERR) ? " ferr" : "",
+			(temp & FSTAT_UF) ? " funder" : "",
+			(temp & FSTAT_OF) ? " fover" : "",
+			(temp & FSTAT_FR) ? " fready" : "",
+			(temp & FSTAT_FULL) ? " ffull" : "",
+			(temp & FSTAT_ALRM) ? " falarm" : "",
+			(temp & FSTAT_EMPTY) ? " fempty" : "");
+	}
+
+	static void dump_req(const char *label, struct imx_ep_struct *imx_ep, struct usb_request *req)
+	{
+		int i;
+
+		if (!req || !req->buf) {
+			dev_dbg(imx_ep->imx_usb->dev, "<%s> req or req buf is free\n", label);
+			return;
+		}
+
+		if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
+			|| (EP_NO(imx_ep) && EP_DIR(imx_ep))) {
+
+			dev_dbg(imx_ep->imx_usb->dev, "<%s> request dump <", label);
+			for (i = 0; i < req->length; i++)
+				printk("%02x-", *((u8 *)req->buf + i));
+			printk(">\n");
+		}
+	}
+
+#else
+	#define dump_ep_stat(x, y)		do {} while (0)
+	#define dump_usb_stat(x, y)		do {} while (0)
+	#define dump_req(x, y, z)		do {} while (0)
+#endif /* DEBUG_DUMP */
+
+#ifdef DEBUG_ERR
+	#define D_ERR(dev, args...)	dev_dbg(dev, ## args)
+#else
+	#define D_ERR(dev, args...)	do {} while (0)
+#endif
+
+#else
+	#define D_REQ(dev, args...)		do {} while (0)
+	#define D_TRX(dev, args...)		do {} while (0)
+	#define D_INI(dev, args...)		do {} while (0)
+	#define D_EP0(dev, args...)		do {} while (0)
+	#define D_EPX(dev, args...)		do {} while (0)
+	#define dump_ep_intr(x, y, z, i)	do {} while (0)
+	#define dump_intr(x, y, z)		do {} while (0)
+	#define dump_ep_stat(x, y)		do {} while (0)
+	#define dump_usb_stat(x, y)		do {} while (0)
+	#define dump_req(x, y, z)		do {} while (0)
+	#define D_ERR(dev, args...)		do {} while (0)
+#endif /* DEBUG */
+
+#endif /* __LINUX_USB_GADGET_IMX_H */