USB: add imx udc gadget driver
authorDarius Augulis <augulis.darius@gmail.com>
Wed, 12 Nov 2008 21:38:31 +0000 (13:38 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 7 Jan 2009 18:00:11 +0000 (10:00 -0800)
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>
arch/arm/plat-mxc/include/mach/usb.h [new file with mode: 0644]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/gadget_chips.h
drivers/usb/gadget/imx_udc.c [new file with mode: 0644]
drivers/usb/gadget/imx_udc.h [new file with mode: 0644]

diff --git a/arch/arm/plat-mxc/include/mach/usb.h b/arch/arm/plat-mxc/include/mach/usb.h
new file mode 100644 (file)
index 0000000..2dacb30
--- /dev/null
@@ -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 */
index c322024bedf0d122112cf544d350d44138ebf363..3219d137340a31009893184301b801068fe7e62a 100644 (file)
@@ -305,6 +305,27 @@ config USB_GADGET_MUSB_HDRC
          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
index 5d64a2ab188997f7fa9f6571d278603d3fde1f54..39a51d746cb76d1eb796fb9798e121b9409ab771 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_USB_NET2280)     += net2280.o
 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
index c0679b1760276d40b890c9119dcb8fcce4124525..ec6d439a2aa588c0872e86335ff22079f6463f6c 100644 (file)
 #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 (file)
index 0000000..cde8fdf
--- /dev/null
@@ -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 (file)
index 0000000..8500769
--- /dev/null
@@ -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 */