usb: gadget: add Faraday fotg210_udc driver
authorYuan-Hsin Chen <yhchen@faraday-tech.com>
Thu, 30 May 2013 15:41:01 +0000 (15:41 +0000)
committerFelipe Balbi <balbi@ti.com>
Mon, 10 Jun 2013 14:58:11 +0000 (17:58 +0300)
Faraday fotg210 udc driver supports only Bulk transfer so far.
fotg210 could be configured as an USB2.0 peripheral.

This driver is tested with mass storage gadget driver on Faraday
EVB a369.

Signed-off-by: Yuan-Hsin Chen <yhchen@faraday-tech.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/fotg210-udc.c [new file with mode: 0644]
drivers/usb/gadget/fotg210.h [new file with mode: 0644]

index b6cd4bd74cb234f3f6eafa3208366579affbb054..2b2a11c8977a881e279fbf20fed2bf6148058fa5 100644 (file)
@@ -192,6 +192,16 @@ config USB_FUSB300
        help
           Faraday usb device controller FUSB300 driver
 
+config USB_FOTG210_UDC
+       tristate "Faraday FOTG210 USB Peripheral Controller"
+       help
+          Faraday USB2.0 OTG controller which can be configured as
+          high speed or full speed USB device. This driver supppors
+          Bulk Transfer so far.
+
+          Say "y" to link the driver statically, or "m" to build a
+          dynamically linked module called "fotg210_udc".
+
 config USB_OMAP
        tristate "OMAP USB Device Controller"
        depends on ARCH_OMAP1
index b41776065f277a62656b8bdc5be337fe4db7db1c..bad08e66f3697abacb3327eb3f197f61ad388ad7 100644 (file)
@@ -33,6 +33,7 @@ obj-$(CONFIG_USB_EG20T)               += pch_udc.o
 obj-$(CONFIG_USB_MV_UDC)       += mv_udc.o
 mv_udc-y                       := mv_udc_core.o
 obj-$(CONFIG_USB_FUSB300)      += fusb300_udc.o
+obj-$(CONFIG_USB_FOTG210_UDC)  += fotg210-udc.o
 obj-$(CONFIG_USB_MV_U3D)       += mv_u3d_core.o
 
 # USB Functions
diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c
new file mode 100644 (file)
index 0000000..cce5535
--- /dev/null
@@ -0,0 +1,1219 @@
+/*
+ * FOTG210 UDC Driver supports Bulk transfer so far
+ *
+ * Copyright (C) 2013 Faraday Technology Corporation
+ *
+ * Author : Yuan-Hsin Chen <yhchen@faraday-tech.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; version 2 of the License.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "fotg210.h"
+
+#define        DRIVER_DESC     "FOTG210 USB Device Controller Driver"
+#define        DRIVER_VERSION  "30-April-2013"
+
+static const char udc_name[] = "fotg210_udc";
+static const char * const fotg210_ep_name[] = {
+       "ep0", "ep1", "ep2", "ep3", "ep4"};
+
+static void fotg210_disable_fifo_int(struct fotg210_ep *ep)
+{
+       u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1);
+
+       if (ep->dir_in)
+               value |= DMISGR1_MF_IN_INT(ep->epnum - 1);
+       else
+               value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1);
+       iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1);
+}
+
+static void fotg210_enable_fifo_int(struct fotg210_ep *ep)
+{
+       u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1);
+
+       if (ep->dir_in)
+               value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1);
+       else
+               value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1);
+       iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1);
+}
+
+static void fotg210_set_cxdone(struct fotg210_udc *fotg210)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_DCFESR);
+
+       value |= DCFESR_CX_DONE;
+       iowrite32(value, fotg210->reg + FOTG210_DCFESR);
+}
+
+static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req,
+                       int status)
+{
+       list_del_init(&req->queue);
+
+       /* don't modify queue heads during completion callback */
+       if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN)
+               req->req.status = -ESHUTDOWN;
+       else
+               req->req.status = status;
+
+       spin_unlock(&ep->fotg210->lock);
+       req->req.complete(&ep->ep, &req->req);
+       spin_lock(&ep->fotg210->lock);
+
+       if (ep->epnum) {
+               if (list_empty(&ep->queue))
+                       fotg210_disable_fifo_int(ep);
+       } else {
+               fotg210_set_cxdone(ep->fotg210);
+       }
+}
+
+static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum,
+                               u32 dir_in)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 val;
+
+       /* Driver should map an ep to a fifo and then map the fifo
+        * to the ep. What a brain-damaged design!
+        */
+
+       /* map a fifo to an ep */
+       val = ioread32(fotg210->reg + FOTG210_EPMAP);
+       val &= ~EPMAP_FIFONOMSK(epnum, dir_in);
+       val |= EPMAP_FIFONO(epnum, dir_in);
+       iowrite32(val, fotg210->reg + FOTG210_EPMAP);
+
+       /* map the ep to the fifo */
+       val = ioread32(fotg210->reg + FOTG210_FIFOMAP);
+       val &= ~FIFOMAP_EPNOMSK(epnum);
+       val |= FIFOMAP_EPNO(epnum);
+       iowrite32(val, fotg210->reg + FOTG210_FIFOMAP);
+
+       /* enable fifo */
+       val = ioread32(fotg210->reg + FOTG210_FIFOCF);
+       val |= FIFOCF_FIFO_EN(epnum - 1);
+       iowrite32(val, fotg210->reg + FOTG210_FIFOCF);
+}
+
+static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 val;
+
+       val = ioread32(fotg210->reg + FOTG210_FIFOMAP);
+       val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1));
+       iowrite32(val, fotg210->reg + FOTG210_FIFOMAP);
+}
+
+static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 val;
+
+       val = ioread32(fotg210->reg + FOTG210_FIFOCF);
+       val |= FIFOCF_TYPE(type, epnum - 1);
+       iowrite32(val, fotg210->reg + FOTG210_FIFOCF);
+}
+
+static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps,
+                               u32 dir_in)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 val;
+       u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) :
+                               FOTG210_OUTEPMPSR(epnum);
+
+       val = ioread32(fotg210->reg + offset);
+       val |= INOUTEPMPSR_MPS(mps);
+       iowrite32(val, fotg210->reg + offset);
+}
+
+static int fotg210_config_ep(struct fotg210_ep *ep,
+                    const struct usb_endpoint_descriptor *desc)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+
+       fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in);
+       fotg210_set_tfrtype(ep, ep->epnum, ep->type);
+       fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in);
+       fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in);
+
+       fotg210->ep[ep->epnum] = ep;
+
+       return 0;
+}
+
+static int fotg210_ep_enable(struct usb_ep *_ep,
+                         const struct usb_endpoint_descriptor *desc)
+{
+       struct fotg210_ep *ep;
+
+       ep = container_of(_ep, struct fotg210_ep, ep);
+
+       ep->desc = desc;
+       ep->epnum = usb_endpoint_num(desc);
+       ep->type = usb_endpoint_type(desc);
+       ep->dir_in = usb_endpoint_dir_in(desc);
+       ep->ep.maxpacket = usb_endpoint_maxp(desc);
+
+       return fotg210_config_ep(ep, desc);
+}
+
+static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum)
+{
+       struct fotg210_ep *ep = fotg210->ep[epnum];
+       u32 value;
+       void __iomem *reg;
+
+       reg = (ep->dir_in) ?
+               fotg210->reg + FOTG210_INEPMPSR(epnum) :
+               fotg210->reg + FOTG210_OUTEPMPSR(epnum);
+
+       /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ
+        *       bit. Controller wouldn't clear this bit. WTF!!!
+        */
+
+       value = ioread32(reg);
+       value |= INOUTEPMPSR_RESET_TSEQ;
+       iowrite32(value, reg);
+
+       value = ioread32(reg);
+       value &= ~INOUTEPMPSR_RESET_TSEQ;
+       iowrite32(value, reg);
+}
+
+static int fotg210_ep_release(struct fotg210_ep *ep)
+{
+       if (!ep->epnum)
+               return 0;
+       ep->epnum = 0;
+       ep->stall = 0;
+       ep->wedged = 0;
+
+       fotg210_reset_tseq(ep->fotg210, ep->epnum);
+
+       return 0;
+}
+
+static int fotg210_ep_disable(struct usb_ep *_ep)
+{
+       struct fotg210_ep *ep;
+       struct fotg210_request *req;
+       unsigned long flags;
+
+       BUG_ON(!_ep);
+
+       ep = container_of(_ep, struct fotg210_ep, ep);
+
+       while (!list_empty(&ep->queue)) {
+               req = list_entry(ep->queue.next,
+                       struct fotg210_request, queue);
+               spin_lock_irqsave(&ep->fotg210->lock, flags);
+               fotg210_done(ep, req, -ECONNRESET);
+               spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+       }
+
+       return fotg210_ep_release(ep);
+}
+
+static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep,
+                                               gfp_t gfp_flags)
+{
+       struct fotg210_request *req;
+
+       req = kzalloc(sizeof(struct fotg210_request), gfp_flags);
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->queue);
+
+       return &req->req;
+}
+
+static void fotg210_ep_free_request(struct usb_ep *_ep,
+                                       struct usb_request *_req)
+{
+       struct fotg210_request *req;
+
+       req = container_of(_req, struct fotg210_request, req);
+       kfree(req);
+}
+
+static void fotg210_enable_dma(struct fotg210_ep *ep,
+                             dma_addr_t d, u32 len)
+{
+       u32 value;
+       struct fotg210_udc *fotg210 = ep->fotg210;
+
+       /* set transfer length and direction */
+       value = ioread32(fotg210->reg + FOTG210_DMACPSR1);
+       value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1));
+       value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in);
+       iowrite32(value, fotg210->reg + FOTG210_DMACPSR1);
+
+       /* set device DMA target FIFO number */
+       value = ioread32(fotg210->reg + FOTG210_DMATFNR);
+       if (ep->epnum)
+               value |= DMATFNR_ACC_FN(ep->epnum - 1);
+       else
+               value |= DMATFNR_ACC_CXF;
+       iowrite32(value, fotg210->reg + FOTG210_DMATFNR);
+
+       /* set DMA memory address */
+       iowrite32(d, fotg210->reg + FOTG210_DMACPSR2);
+
+       /* enable MDMA_EROR and MDMA_CMPLT interrupt */
+       value = ioread32(fotg210->reg + FOTG210_DMISGR2);
+       value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR);
+       iowrite32(value, fotg210->reg + FOTG210_DMISGR2);
+
+       /* start DMA */
+       value = ioread32(fotg210->reg + FOTG210_DMACPSR1);
+       value |= DMACPSR1_DMA_START;
+       iowrite32(value, fotg210->reg + FOTG210_DMACPSR1);
+}
+
+static void fotg210_disable_dma(struct fotg210_ep *ep)
+{
+       iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR);
+}
+
+static void fotg210_wait_dma_done(struct fotg210_ep *ep)
+{
+       u32 value;
+
+       do {
+               value = ioread32(ep->fotg210->reg + FOTG210_DISGR2);
+               if ((value & DISGR2_USBRST_INT) ||
+                   (value & DISGR2_DMA_ERROR))
+                       goto dma_reset;
+       } while (!(value & DISGR2_DMA_CMPLT));
+
+       value &= ~DISGR2_DMA_CMPLT;
+       iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2);
+       return;
+
+dma_reset:
+       value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1);
+       value |= DMACPSR1_DMA_ABORT;
+       iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1);
+
+       /* reset fifo */
+       if (ep->epnum) {
+               value = ioread32(ep->fotg210->reg +
+                               FOTG210_FIBCR(ep->epnum - 1));
+               value |= FIBCR_FFRST;
+               iowrite32(value, ep->fotg210->reg +
+                               FOTG210_FIBCR(ep->epnum - 1));
+       } else {
+               value = ioread32(ep->fotg210->reg + FOTG210_DCFESR);
+               value |= DCFESR_CX_CLR;
+               iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR);
+       }
+}
+
+static void fotg210_start_dma(struct fotg210_ep *ep,
+                       struct fotg210_request *req)
+{
+       dma_addr_t d;
+       u8 *buffer;
+       u32 length;
+
+       if (ep->epnum) {
+               if (ep->dir_in) {
+                       buffer = req->req.buf;
+                       length = req->req.length;
+               } else {
+                       buffer = req->req.buf + req->req.actual;
+                       length = ioread32(ep->fotg210->reg +
+                                       FOTG210_FIBCR(ep->epnum - 1));
+                       length &= FIBCR_BCFX;
+               }
+       } else {
+               buffer = req->req.buf + req->req.actual;
+               if (req->req.length - req->req.actual > ep->ep.maxpacket)
+                       length = ep->ep.maxpacket;
+               else
+                       length = req->req.length;
+       }
+
+       d = dma_map_single(NULL, buffer, length,
+                       ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+       if (dma_mapping_error(NULL, d)) {
+               pr_err("dma_mapping_error\n");
+               return;
+       }
+
+       dma_sync_single_for_device(NULL, d, length,
+                                  ep->dir_in ? DMA_TO_DEVICE :
+                                       DMA_FROM_DEVICE);
+
+       fotg210_enable_dma(ep, d, length);
+
+       /* check if dma is done */
+       fotg210_wait_dma_done(ep);
+
+       fotg210_disable_dma(ep);
+
+       /* update actual transfer length */
+       req->req.actual += length;
+
+       dma_unmap_single(NULL, d, length, DMA_TO_DEVICE);
+}
+
+static void fotg210_ep0_queue(struct fotg210_ep *ep,
+                               struct fotg210_request *req)
+{
+       if (!req->req.length) {
+               fotg210_done(ep, req, 0);
+               return;
+       }
+       if (ep->dir_in) { /* if IN */
+               if (req->req.length) {
+                       fotg210_start_dma(ep, req);
+               } else {
+                       pr_err("%s : req->req.length = 0x%x\n",
+                              __func__, req->req.length);
+               }
+               if ((req->req.length == req->req.actual) ||
+                   (req->req.actual < ep->ep.maxpacket))
+                       fotg210_done(ep, req, 0);
+       } else { /* OUT */
+               if (!req->req.length) {
+                       fotg210_done(ep, req, 0);
+               } else {
+                       u32 value = ioread32(ep->fotg210->reg +
+                                               FOTG210_DMISGR0);
+
+                       value &= ~DMISGR0_MCX_OUT_INT;
+                       iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0);
+               }
+       }
+}
+
+static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+                               gfp_t gfp_flags)
+{
+       struct fotg210_ep *ep;
+       struct fotg210_request *req;
+       unsigned long flags;
+       int request = 0;
+
+       ep = container_of(_ep, struct fotg210_ep, ep);
+       req = container_of(_req, struct fotg210_request, req);
+
+       if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       spin_lock_irqsave(&ep->fotg210->lock, flags);
+
+       if (list_empty(&ep->queue))
+               request = 1;
+
+       list_add_tail(&req->queue, &ep->queue);
+
+       req->req.actual = 0;
+       req->req.status = -EINPROGRESS;
+
+       if (!ep->epnum) /* ep0 */
+               fotg210_ep0_queue(ep, req);
+       else if (request && !ep->stall)
+               fotg210_enable_fifo_int(ep);
+
+       spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+
+       return 0;
+}
+
+static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct fotg210_ep *ep;
+       struct fotg210_request *req;
+       unsigned long flags;
+
+       ep = container_of(_ep, struct fotg210_ep, ep);
+       req = container_of(_req, struct fotg210_request, req);
+
+       spin_lock_irqsave(&ep->fotg210->lock, flags);
+       if (!list_empty(&ep->queue))
+               fotg210_done(ep, req, -ECONNRESET);
+       spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+
+       return 0;
+}
+
+static void fotg210_set_epnstall(struct fotg210_ep *ep)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 value;
+       void __iomem *reg;
+
+       /* check if IN FIFO is empty before stall */
+       if (ep->dir_in) {
+               do {
+                       value = ioread32(fotg210->reg + FOTG210_DCFESR);
+               } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1)));
+       }
+
+       reg = (ep->dir_in) ?
+               fotg210->reg + FOTG210_INEPMPSR(ep->epnum) :
+               fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum);
+       value = ioread32(reg);
+       value |= INOUTEPMPSR_STL_EP;
+       iowrite32(value, reg);
+}
+
+static void fotg210_clear_epnstall(struct fotg210_ep *ep)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 value;
+       void __iomem *reg;
+
+       reg = (ep->dir_in) ?
+               fotg210->reg + FOTG210_INEPMPSR(ep->epnum) :
+               fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum);
+       value = ioread32(reg);
+       value &= ~INOUTEPMPSR_STL_EP;
+       iowrite32(value, reg);
+}
+
+static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge)
+{
+       struct fotg210_ep *ep;
+       struct fotg210_udc *fotg210;
+       unsigned long flags;
+       int ret = 0;
+
+       ep = container_of(_ep, struct fotg210_ep, ep);
+
+       fotg210 = ep->fotg210;
+
+       spin_lock_irqsave(&ep->fotg210->lock, flags);
+
+       if (value) {
+               fotg210_set_epnstall(ep);
+               ep->stall = 1;
+               if (wedge)
+                       ep->wedged = 1;
+       } else {
+               fotg210_reset_tseq(fotg210, ep->epnum);
+               fotg210_clear_epnstall(ep);
+               ep->stall = 0;
+               ep->wedged = 0;
+               if (!list_empty(&ep->queue))
+                       fotg210_enable_fifo_int(ep);
+       }
+
+       spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+       return ret;
+}
+
+static int fotg210_ep_set_halt(struct usb_ep *_ep, int value)
+{
+       return fotg210_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int fotg210_ep_set_wedge(struct usb_ep *_ep)
+{
+       return fotg210_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static void fotg210_ep_fifo_flush(struct usb_ep *_ep)
+{
+}
+
+static struct usb_ep_ops fotg210_ep_ops = {
+       .enable         = fotg210_ep_enable,
+       .disable        = fotg210_ep_disable,
+
+       .alloc_request  = fotg210_ep_alloc_request,
+       .free_request   = fotg210_ep_free_request,
+
+       .queue          = fotg210_ep_queue,
+       .dequeue        = fotg210_ep_dequeue,
+
+       .set_halt       = fotg210_ep_set_halt,
+       .fifo_flush     = fotg210_ep_fifo_flush,
+       .set_wedge      = fotg210_ep_set_wedge,
+};
+
+static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE);
+
+       value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3
+                  | TX0BYTE_EP4);
+       iowrite32(value, fotg210->reg + FOTG210_TX0BYTE);
+}
+
+static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE);
+
+       value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3
+                  | RX0BYTE_EP4);
+       iowrite32(value, fotg210->reg + FOTG210_RX0BYTE);
+}
+
+/* read 8-byte setup packet only */
+static void fotg210_rdsetupp(struct fotg210_udc *fotg210,
+                  u8 *buffer)
+{
+       int i = 0;
+       u8 *tmp = buffer;
+       u32 data;
+       u32 length = 8;
+
+       iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR);
+
+       for (i = (length >> 2); i > 0; i--) {
+               data = ioread32(fotg210->reg + FOTG210_CXPORT);
+               *tmp = data & 0xFF;
+               *(tmp + 1) = (data >> 8) & 0xFF;
+               *(tmp + 2) = (data >> 16) & 0xFF;
+               *(tmp + 3) = (data >> 24) & 0xFF;
+               tmp = tmp + 4;
+       }
+
+       switch (length % 4) {
+       case 1:
+               data = ioread32(fotg210->reg + FOTG210_CXPORT);
+               *tmp = data & 0xFF;
+               break;
+       case 2:
+               data = ioread32(fotg210->reg + FOTG210_CXPORT);
+               *tmp = data & 0xFF;
+               *(tmp + 1) = (data >> 8) & 0xFF;
+               break;
+       case 3:
+               data = ioread32(fotg210->reg + FOTG210_CXPORT);
+               *tmp = data & 0xFF;
+               *(tmp + 1) = (data >> 8) & 0xFF;
+               *(tmp + 2) = (data >> 16) & 0xFF;
+               break;
+       default:
+               break;
+       }
+
+       iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR);
+}
+
+static void fotg210_set_configuration(struct fotg210_udc *fotg210)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_DAR);
+
+       value |= DAR_AFT_CONF;
+       iowrite32(value, fotg210->reg + FOTG210_DAR);
+}
+
+static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_DAR);
+
+       value |= (addr & 0x7F);
+       iowrite32(value, fotg210->reg + FOTG210_DAR);
+}
+
+static void fotg210_set_cxstall(struct fotg210_udc *fotg210)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_DCFESR);
+
+       value |= DCFESR_CX_STL;
+       iowrite32(value, fotg210->reg + FOTG210_DCFESR);
+}
+
+static void fotg210_request_error(struct fotg210_udc *fotg210)
+{
+       fotg210_set_cxstall(fotg210);
+       pr_err("request error!!\n");
+}
+
+static void fotg210_set_address(struct fotg210_udc *fotg210,
+                               struct usb_ctrlrequest *ctrl)
+{
+       if (ctrl->wValue >= 0x0100) {
+               fotg210_request_error(fotg210);
+       } else {
+               fotg210_set_dev_addr(fotg210, ctrl->wValue);
+               fotg210_set_cxdone(fotg210);
+       }
+}
+
+static void fotg210_set_feature(struct fotg210_udc *fotg210,
+                               struct usb_ctrlrequest *ctrl)
+{
+       switch (ctrl->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               fotg210_set_cxdone(fotg210);
+               break;
+       case USB_RECIP_INTERFACE:
+               fotg210_set_cxdone(fotg210);
+               break;
+       case USB_RECIP_ENDPOINT: {
+               u8 epnum;
+               epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+               if (epnum)
+                       fotg210_set_epnstall(fotg210->ep[epnum]);
+               else
+                       fotg210_set_cxstall(fotg210);
+               fotg210_set_cxdone(fotg210);
+               }
+               break;
+       default:
+               fotg210_request_error(fotg210);
+               break;
+       }
+}
+
+static void fotg210_clear_feature(struct fotg210_udc *fotg210,
+                               struct usb_ctrlrequest *ctrl)
+{
+       struct fotg210_ep *ep =
+               fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
+
+       switch (ctrl->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               fotg210_set_cxdone(fotg210);
+               break;
+       case USB_RECIP_INTERFACE:
+               fotg210_set_cxdone(fotg210);
+               break;
+       case USB_RECIP_ENDPOINT:
+               if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
+                       if (ep->wedged) {
+                               fotg210_set_cxdone(fotg210);
+                               break;
+                       }
+                       if (ep->stall)
+                               fotg210_set_halt_and_wedge(&ep->ep, 0, 0);
+               }
+               fotg210_set_cxdone(fotg210);
+               break;
+       default:
+               fotg210_request_error(fotg210);
+               break;
+       }
+}
+
+static int fotg210_is_epnstall(struct fotg210_ep *ep)
+{
+       struct fotg210_udc *fotg210 = ep->fotg210;
+       u32 value;
+       void __iomem *reg;
+
+       reg = (ep->dir_in) ?
+               fotg210->reg + FOTG210_INEPMPSR(ep->epnum) :
+               fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum);
+       value = ioread32(reg);
+       return value & INOUTEPMPSR_STL_EP ? 1 : 0;
+}
+
+static void fotg210_get_status(struct fotg210_udc *fotg210,
+                               struct usb_ctrlrequest *ctrl)
+{
+       u8 epnum;
+
+       switch (ctrl->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               fotg210->ep0_data = 1 << USB_DEVICE_SELF_POWERED;
+               break;
+       case USB_RECIP_INTERFACE:
+               fotg210->ep0_data = 0;
+               break;
+       case USB_RECIP_ENDPOINT:
+               epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+               if (epnum)
+                       fotg210->ep0_data =
+                               fotg210_is_epnstall(fotg210->ep[epnum])
+                               << USB_ENDPOINT_HALT;
+               else
+                       fotg210_request_error(fotg210);
+               break;
+
+       default:
+               fotg210_request_error(fotg210);
+               return;         /* exit */
+       }
+
+       fotg210->ep0_req->buf = &fotg210->ep0_data;
+       fotg210->ep0_req->length = 2;
+
+       spin_unlock(&fotg210->lock);
+       fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL);
+       spin_lock(&fotg210->lock);
+}
+
+static int fotg210_setup_packet(struct fotg210_udc *fotg210,
+                               struct usb_ctrlrequest *ctrl)
+{
+       u8 *p = (u8 *)ctrl;
+       u8 ret = 0;
+
+       fotg210_rdsetupp(fotg210, p);
+
+       fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN;
+
+       if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) {
+               u32 value = ioread32(fotg210->reg + FOTG210_DMCR);
+               fotg210->gadget.speed = value & DMCR_HS_EN ?
+                               USB_SPEED_HIGH : USB_SPEED_FULL;
+       }
+
+       /* check request */
+       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+               switch (ctrl->bRequest) {
+               case USB_REQ_GET_STATUS:
+                       fotg210_get_status(fotg210, ctrl);
+                       break;
+               case USB_REQ_CLEAR_FEATURE:
+                       fotg210_clear_feature(fotg210, ctrl);
+                       break;
+               case USB_REQ_SET_FEATURE:
+                       fotg210_set_feature(fotg210, ctrl);
+                       break;
+               case USB_REQ_SET_ADDRESS:
+                       fotg210_set_address(fotg210, ctrl);
+                       break;
+               case USB_REQ_SET_CONFIGURATION:
+                       fotg210_set_configuration(fotg210);
+                       ret = 1;
+                       break;
+               default:
+                       ret = 1;
+                       break;
+               }
+       } else {
+               ret = 1;
+       }
+
+       return ret;
+}
+
+static void fotg210_ep0out(struct fotg210_udc *fotg210)
+{
+       struct fotg210_ep *ep = fotg210->ep[0];
+
+       if (!list_empty(&ep->queue) && !ep->dir_in) {
+               struct fotg210_request *req;
+
+               req = list_first_entry(&ep->queue,
+                       struct fotg210_request, queue);
+
+               if (req->req.length)
+                       fotg210_start_dma(ep, req);
+
+               if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
+                       fotg210_done(ep, req, 0);
+       } else {
+               pr_err("%s : empty queue\n", __func__);
+       }
+}
+
+static void fotg210_ep0in(struct fotg210_udc *fotg210)
+{
+       struct fotg210_ep *ep = fotg210->ep[0];
+
+       if ((!list_empty(&ep->queue)) && (ep->dir_in)) {
+               struct fotg210_request *req;
+
+               req = list_entry(ep->queue.next,
+                               struct fotg210_request, queue);
+
+               if (req->req.length)
+                       fotg210_start_dma(ep, req);
+
+               if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
+                       fotg210_done(ep, req, 0);
+       } else {
+               fotg210_set_cxdone(fotg210);
+       }
+}
+
+static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210)
+{
+       u32 value = ioread32(fotg210->reg + FOTG210_DISGR0);
+
+       value &= ~DISGR0_CX_COMABT_INT;
+       iowrite32(value, fotg210->reg + FOTG210_DISGR0);
+}
+
+static void fotg210_in_fifo_handler(struct fotg210_ep *ep)
+{
+       struct fotg210_request *req = list_entry(ep->queue.next,
+                                       struct fotg210_request, queue);
+
+       if (req->req.length)
+               fotg210_start_dma(ep, req);
+       fotg210_done(ep, req, 0);
+}
+
+static void fotg210_out_fifo_handler(struct fotg210_ep *ep)
+{
+       struct fotg210_request *req = list_entry(ep->queue.next,
+                                                struct fotg210_request, queue);
+
+       fotg210_start_dma(ep, req);
+
+       /* finish out transfer */
+       if (req->req.length == req->req.actual ||
+           req->req.actual < ep->ep.maxpacket)
+               fotg210_done(ep, req, 0);
+}
+
+static irqreturn_t fotg210_irq(int irq, void *_fotg210)
+{
+       struct fotg210_udc *fotg210 = _fotg210;
+       u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR);
+       u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR);
+
+       int_grp &= ~int_msk;
+
+       spin_lock(&fotg210->lock);
+
+       if (int_grp & DIGR_INT_G2) {
+               void __iomem *reg = fotg210->reg + FOTG210_DISGR2;
+               u32 int_grp2 = ioread32(reg);
+               u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2);
+               u32 value;
+
+               int_grp2 &= ~int_msk2;
+
+               if (int_grp2 & DISGR2_USBRST_INT) {
+                       value = ioread32(reg);
+                       value &= ~DISGR2_USBRST_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 udc reset\n");
+               }
+               if (int_grp2 & DISGR2_SUSP_INT) {
+                       value = ioread32(reg);
+                       value &= ~DISGR2_SUSP_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 udc suspend\n");
+               }
+               if (int_grp2 & DISGR2_RESM_INT) {
+                       value = ioread32(reg);
+                       value &= ~DISGR2_RESM_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 udc resume\n");
+               }
+               if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) {
+                       value = ioread32(reg);
+                       value &= ~DISGR2_ISO_SEQ_ERR_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 iso sequence error\n");
+               }
+               if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) {
+                       value = ioread32(reg);
+                       value &= ~DISGR2_ISO_SEQ_ABORT_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 iso sequence abort\n");
+               }
+               if (int_grp2 & DISGR2_TX0BYTE_INT) {
+                       fotg210_clear_tx0byte(fotg210);
+                       value = ioread32(reg);
+                       value &= ~DISGR2_TX0BYTE_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 transferred 0 byte\n");
+               }
+               if (int_grp2 & DISGR2_RX0BYTE_INT) {
+                       fotg210_clear_rx0byte(fotg210);
+                       value = ioread32(reg);
+                       value &= ~DISGR2_RX0BYTE_INT;
+                       iowrite32(value, reg);
+                       pr_info("fotg210 received 0 byte\n");
+               }
+               if (int_grp2 & DISGR2_DMA_ERROR) {
+                       value = ioread32(reg);
+                       value &= ~DISGR2_DMA_ERROR;
+                       iowrite32(value, reg);
+               }
+       }
+
+       if (int_grp & DIGR_INT_G0) {
+               void __iomem *reg = fotg210->reg + FOTG210_DISGR0;
+               u32 int_grp0 = ioread32(reg);
+               u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0);
+               struct usb_ctrlrequest ctrl;
+
+               int_grp0 &= ~int_msk0;
+
+               /* the highest priority in this source register */
+               if (int_grp0 & DISGR0_CX_COMABT_INT) {
+                       fotg210_clear_comabt_int(fotg210);
+                       pr_info("fotg210 CX command abort\n");
+               }
+
+               if (int_grp0 & DISGR0_CX_SETUP_INT) {
+                       if (fotg210_setup_packet(fotg210, &ctrl)) {
+                               spin_unlock(&fotg210->lock);
+                               if (fotg210->driver->setup(&fotg210->gadget,
+                                                          &ctrl) < 0)
+                                       fotg210_set_cxstall(fotg210);
+                               spin_lock(&fotg210->lock);
+                       }
+               }
+               if (int_grp0 & DISGR0_CX_COMEND_INT)
+                       pr_info("fotg210 cmd end\n");
+
+               if (int_grp0 & DISGR0_CX_IN_INT)
+                       fotg210_ep0in(fotg210);
+
+               if (int_grp0 & DISGR0_CX_OUT_INT)
+                       fotg210_ep0out(fotg210);
+
+               if (int_grp0 & DISGR0_CX_COMFAIL_INT) {
+                       fotg210_set_cxstall(fotg210);
+                       pr_info("fotg210 ep0 fail\n");
+               }
+       }
+
+       if (int_grp & DIGR_INT_G1) {
+               void __iomem *reg = fotg210->reg + FOTG210_DISGR1;
+               u32 int_grp1 = ioread32(reg);
+               u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1);
+               int fifo;
+
+               int_grp1 &= ~int_msk1;
+
+               for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) {
+                       if (int_grp1 & DISGR1_IN_INT(fifo))
+                               fotg210_in_fifo_handler(fotg210->ep[fifo + 1]);
+
+                       if ((int_grp1 & DISGR1_OUT_INT(fifo)) ||
+                           (int_grp1 & DISGR1_SPK_INT(fifo)))
+                               fotg210_out_fifo_handler(fotg210->ep[fifo + 1]);
+               }
+       }
+
+       spin_unlock(&fotg210->lock);
+
+       return IRQ_HANDLED;
+}
+
+static void fotg210_disable_unplug(struct fotg210_udc *fotg210)
+{
+       u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR);
+
+       reg &= ~PHYTMSR_UNPLUG;
+       iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR);
+}
+
+static int fotg210_udc_start(struct usb_gadget *g,
+               struct usb_gadget_driver *driver)
+{
+       struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
+       u32 value;
+
+       /* hook up the driver */
+       driver->driver.bus = NULL;
+       fotg210->driver = driver;
+
+       /* enable device global interrupt */
+       value = ioread32(fotg210->reg + FOTG210_DMCR);
+       value |= DMCR_GLINT_EN;
+       iowrite32(value, fotg210->reg + FOTG210_DMCR);
+
+       return 0;
+}
+
+static void fotg210_init(struct fotg210_udc *fotg210)
+{
+       u32 value;
+
+       /* disable global interrupt and set int polarity to active high */
+       iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY,
+                 fotg210->reg + FOTG210_GMIR);
+
+       /* disable device global interrupt */
+       value = ioread32(fotg210->reg + FOTG210_DMCR);
+       value &= ~DMCR_GLINT_EN;
+       iowrite32(value, fotg210->reg + FOTG210_DMCR);
+
+       /* disable all fifo interrupt */
+       iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1);
+
+       /* disable cmd end */
+       value = ioread32(fotg210->reg + FOTG210_DMISGR0);
+       value |= DMISGR0_MCX_COMEND;
+       iowrite32(value, fotg210->reg + FOTG210_DMISGR0);
+}
+
+static int fotg210_udc_stop(struct usb_gadget *g,
+               struct usb_gadget_driver *driver)
+{
+       struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
+       unsigned long   flags;
+
+       spin_lock_irqsave(&fotg210->lock, flags);
+
+       fotg210_init(fotg210);
+       fotg210->driver = NULL;
+
+       spin_unlock_irqrestore(&fotg210->lock, flags);
+
+       return 0;
+}
+
+static struct usb_gadget_ops fotg210_gadget_ops = {
+       .udc_start              = fotg210_udc_start,
+       .udc_stop               = fotg210_udc_stop,
+};
+
+static int __exit fotg210_udc_remove(struct platform_device *pdev)
+{
+       struct fotg210_udc *fotg210 = dev_get_drvdata(&pdev->dev);
+
+       usb_del_gadget_udc(&fotg210->gadget);
+       iounmap(fotg210->reg);
+       free_irq(platform_get_irq(pdev, 0), fotg210);
+
+       fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
+       kfree(fotg210);
+
+       return 0;
+}
+
+static int __init fotg210_udc_probe(struct platform_device *pdev)
+{
+       struct resource *res, *ires;
+       struct fotg210_udc *fotg210 = NULL;
+       struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP];
+       int ret = 0;
+       int i;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               pr_err("platform_get_resource error.\n");
+               return -ENODEV;
+       }
+
+       ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!ires) {
+               pr_err("platform_get_resource IORESOURCE_IRQ error.\n");
+               return -ENODEV;
+       }
+
+       ret = -ENOMEM;
+
+       /* initialize udc */
+       fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL);
+       if (fotg210 == NULL) {
+               pr_err("kzalloc error\n");
+               goto err_alloc;
+       }
+
+       for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
+               _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
+               if (_ep[i] == NULL) {
+                       pr_err("_ep kzalloc error\n");
+                       goto err_alloc;
+               }
+               fotg210->ep[i] = _ep[i];
+       }
+
+       fotg210->reg = ioremap(res->start, resource_size(res));
+       if (fotg210->reg == NULL) {
+               pr_err("ioremap error.\n");
+               goto err_map;
+       }
+
+       spin_lock_init(&fotg210->lock);
+
+       dev_set_drvdata(&pdev->dev, fotg210);
+
+       fotg210->gadget.ops = &fotg210_gadget_ops;
+
+       fotg210->gadget.max_speed = USB_SPEED_HIGH;
+       fotg210->gadget.dev.parent = &pdev->dev;
+       fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask;
+       fotg210->gadget.name = udc_name;
+
+       INIT_LIST_HEAD(&fotg210->gadget.ep_list);
+
+       for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
+               struct fotg210_ep *ep = fotg210->ep[i];
+
+               if (i) {
+                       INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list);
+                       list_add_tail(&fotg210->ep[i]->ep.ep_list,
+                                     &fotg210->gadget.ep_list);
+               }
+               ep->fotg210 = fotg210;
+               INIT_LIST_HEAD(&ep->queue);
+               ep->ep.name = fotg210_ep_name[i];
+               ep->ep.ops = &fotg210_ep_ops;
+       }
+       fotg210->ep[0]->ep.maxpacket = 0x40;
+       fotg210->gadget.ep0 = &fotg210->ep[0]->ep;
+       INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list);
+
+       fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep,
+                               GFP_KERNEL);
+       if (fotg210->ep0_req == NULL)
+               goto err_req;
+
+       fotg210_init(fotg210);
+
+       fotg210_disable_unplug(fotg210);
+
+       ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED,
+                         udc_name, fotg210);
+       if (ret < 0) {
+               pr_err("request_irq error (%d)\n", ret);
+               goto err_irq;
+       }
+
+       ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget);
+       if (ret)
+               goto err_add_udc;
+
+       dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
+
+       return 0;
+
+err_add_udc:
+err_irq:
+       free_irq(ires->start, fotg210);
+
+err_req:
+       fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
+
+err_map:
+       if (fotg210->reg)
+               iounmap(fotg210->reg);
+
+err_alloc:
+       kfree(fotg210);
+
+       return ret;
+}
+
+static struct platform_driver fotg210_driver = {
+       .driver         = {
+               .name = (char *)udc_name,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = fotg210_udc_probe,
+       .remove         = fotg210_udc_remove,
+};
+
+module_platform_driver(fotg210_driver);
+
+MODULE_AUTHOR("Yuan-Hsin Chen <yhchen@faraday-tech.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/fotg210.h
new file mode 100644 (file)
index 0000000..bbf991b
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Faraday FOTG210 USB OTG controller
+ *
+ * Copyright (C) 2013 Faraday Technology Corporation
+ * Author: Yuan-Hsin Chen <yhchen@faraday-tech.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.
+ */
+
+#include <linux/kernel.h>
+
+#define FOTG210_MAX_NUM_EP     5 /* ep0...ep4 */
+#define FOTG210_MAX_FIFO_NUM   4 /* fifo0...fifo4 */
+
+/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */
+#define FOTG210_GMIR           0xC4
+#define GMIR_INT_POLARITY      0x8 /*Active High*/
+#define GMIR_MHC_INT           0x4
+#define GMIR_MOTG_INT          0x2
+#define GMIR_MDEV_INT          0x1
+
+/*  Device Main Control Register(0x100) */
+#define FOTG210_DMCR           0x100
+#define DMCR_HS_EN             (1 << 6)
+#define DMCR_CHIP_EN           (1 << 5)
+#define DMCR_SFRST             (1 << 4)
+#define DMCR_GOSUSP            (1 << 3)
+#define DMCR_GLINT_EN          (1 << 2)
+#define DMCR_HALF_SPEED                (1 << 1)
+#define DMCR_CAP_RMWAKUP       (1 << 0)
+
+/* Device Address Register(0x104) */
+#define FOTG210_DAR            0x104
+#define DAR_AFT_CONF           (1 << 7)
+
+/* Device Test Register(0x108) */
+#define FOTG210_DTR            0x108
+#define DTR_TST_CLRFF          (1 << 0)
+
+/* PHY Test Mode Selector register(0x114) */
+#define FOTG210_PHYTMSR                0x114
+#define PHYTMSR_TST_PKT                (1 << 4)
+#define PHYTMSR_TST_SE0NAK     (1 << 3)
+#define PHYTMSR_TST_KSTA       (1 << 2)
+#define PHYTMSR_TST_JSTA       (1 << 1)
+#define PHYTMSR_UNPLUG         (1 << 0)
+
+/* Cx configuration and FIFO Empty Status register(0x120) */
+#define FOTG210_DCFESR         0x120
+#define DCFESR_FIFO_EMPTY(fifo)        (1 << 8 << (fifo))
+#define DCFESR_CX_EMP          (1 << 5)
+#define DCFESR_CX_CLR          (1 << 3)
+#define DCFESR_CX_STL          (1 << 2)
+#define DCFESR_TST_PKDONE      (1 << 1)
+#define DCFESR_CX_DONE         (1 << 0)
+
+/* Device IDLE Counter Register(0x124) */
+#define FOTG210_DICR           0x124
+
+/* Device Mask of Interrupt Group Register (0x130) */
+#define FOTG210_DMIGR          0x130
+#define DMIGR_MINT_G0          (1 << 0)
+
+/* Device Mask of Interrupt Source Group 0(0x134) */
+#define FOTG210_DMISGR0                0x134
+#define DMISGR0_MCX_COMEND     (1 << 3)
+#define DMISGR0_MCX_OUT_INT    (1 << 2)
+#define DMISGR0_MCX_IN_INT     (1 << 1)
+#define DMISGR0_MCX_SETUP_INT  (1 << 0)
+
+/* Device Mask of Interrupt Source Group 1 Register(0x138)*/
+#define FOTG210_DMISGR1                0x138
+#define DMISGR1_MF3_IN_INT     (1 << 19)
+#define DMISGR1_MF2_IN_INT     (1 << 18)
+#define DMISGR1_MF1_IN_INT     (1 << 17)
+#define DMISGR1_MF0_IN_INT     (1 << 16)
+#define DMISGR1_MF_IN_INT(fifo)        (1 << (16 + (fifo)))
+#define DMISGR1_MF3_SPK_INT    (1 << 7)
+#define DMISGR1_MF3_OUT_INT    (1 << 6)
+#define DMISGR1_MF2_SPK_INT    (1 << 5)
+#define DMISGR1_MF2_OUT_INT    (1 << 4)
+#define DMISGR1_MF1_SPK_INT    (1 << 3)
+#define DMISGR1_MF1_OUT_INT    (1 << 2)
+#define DMISGR1_MF0_SPK_INT    (1 << 1)
+#define DMISGR1_MF0_OUT_INT    (1 << 0)
+#define DMISGR1_MF_OUTSPK_INT(fifo)    (0x3 << (fifo) * 2)
+
+/* Device Mask of Interrupt Source Group 2 Register (0x13C) */
+#define FOTG210_DMISGR2                0x13C
+#define DMISGR2_MDMA_ERROR     (1 << 8)
+#define DMISGR2_MDMA_CMPLT     (1 << 7)
+
+/* Device Interrupt group Register (0x140) */
+#define FOTG210_DIGR           0x140
+#define DIGR_INT_G2            (1 << 2)
+#define DIGR_INT_G1            (1 << 1)
+#define DIGR_INT_G0            (1 << 0)
+
+/* Device Interrupt Source Group 0 Register (0x144) */
+#define FOTG210_DISGR0         0x144
+#define DISGR0_CX_COMABT_INT   (1 << 5)
+#define DISGR0_CX_COMFAIL_INT  (1 << 4)
+#define DISGR0_CX_COMEND_INT   (1 << 3)
+#define DISGR0_CX_OUT_INT      (1 << 2)
+#define DISGR0_CX_IN_INT       (1 << 1)
+#define DISGR0_CX_SETUP_INT    (1 << 0)
+
+/* Device Interrupt Source Group 1 Register (0x148) */
+#define FOTG210_DISGR1         0x148
+#define DISGR1_OUT_INT(fifo)   (1 << ((fifo) * 2))
+#define DISGR1_SPK_INT(fifo)   (1 << 1 << ((fifo) * 2))
+#define DISGR1_IN_INT(fifo)    (1 << 16 << (fifo))
+
+/* Device Interrupt Source Group 2 Register (0x14C) */
+#define FOTG210_DISGR2         0x14C
+#define DISGR2_DMA_ERROR       (1 << 8)
+#define DISGR2_DMA_CMPLT       (1 << 7)
+#define DISGR2_RX0BYTE_INT     (1 << 6)
+#define DISGR2_TX0BYTE_INT     (1 << 5)
+#define DISGR2_ISO_SEQ_ABORT_INT       (1 << 4)
+#define DISGR2_ISO_SEQ_ERR_INT (1 << 3)
+#define DISGR2_RESM_INT                (1 << 2)
+#define DISGR2_SUSP_INT                (1 << 1)
+#define DISGR2_USBRST_INT      (1 << 0)
+
+/* Device Receive Zero-Length Data Packet Register (0x150)*/
+#define FOTG210_RX0BYTE                0x150
+#define RX0BYTE_EP8            (1 << 7)
+#define RX0BYTE_EP7            (1 << 6)
+#define RX0BYTE_EP6            (1 << 5)
+#define RX0BYTE_EP5            (1 << 4)
+#define RX0BYTE_EP4            (1 << 3)
+#define RX0BYTE_EP3            (1 << 2)
+#define RX0BYTE_EP2            (1 << 1)
+#define RX0BYTE_EP1            (1 << 0)
+
+/* Device Transfer Zero-Length Data Packet Register (0x154)*/
+#define FOTG210_TX0BYTE                0x154
+#define TX0BYTE_EP8            (1 << 7)
+#define TX0BYTE_EP7            (1 << 6)
+#define TX0BYTE_EP6            (1 << 5)
+#define TX0BYTE_EP5            (1 << 4)
+#define TX0BYTE_EP4            (1 << 3)
+#define TX0BYTE_EP3            (1 << 2)
+#define TX0BYTE_EP2            (1 << 1)
+#define TX0BYTE_EP1            (1 << 0)
+
+/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */
+#define FOTG210_INEPMPSR(ep)   (0x160 + 4 * ((ep) - 1))
+#define INOUTEPMPSR_MPS(mps)   ((mps) & 0x2FF)
+#define INOUTEPMPSR_STL_EP     (1 << 11)
+#define INOUTEPMPSR_RESET_TSEQ (1 << 12)
+
+/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */
+#define FOTG210_OUTEPMPSR(ep)  (0x180 + 4 * ((ep) - 1))
+
+/* Device Endpoint 1~4 Map Register (0x1A0) */
+#define FOTG210_EPMAP          0x1A0
+#define EPMAP_FIFONO(ep, dir)          \
+       ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4))
+#define EPMAP_FIFONOMSK(ep, dir)       \
+       ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4))
+
+/* Device FIFO Map Register (0x1A8) */
+#define FOTG210_FIFOMAP                0x1A8
+#define FIFOMAP_DIROUT(fifo)   (0x0 << 4 << (fifo) * 8)
+#define FIFOMAP_DIRIN(fifo)    (0x1 << 4 << (fifo) * 8)
+#define FIFOMAP_BIDIR(fifo)    (0x2 << 4 << (fifo) * 8)
+#define FIFOMAP_NA(fifo)       (0x3 << 4 << (fifo) * 8)
+#define FIFOMAP_EPNO(ep)       ((ep) << ((ep) - 1) * 8)
+#define FIFOMAP_EPNOMSK(ep)    (0xF << ((ep) - 1) * 8)
+
+/* Device FIFO Confuguration Register (0x1AC) */
+#define FOTG210_FIFOCF         0x1AC
+#define FIFOCF_TYPE(type, fifo)        ((type) << (fifo) * 8)
+#define FIFOCF_BLK_SIN(fifo)   (0x0 << (fifo) * 8 << 2)
+#define FIFOCF_BLK_DUB(fifo)   (0x1 << (fifo) * 8 << 2)
+#define FIFOCF_BLK_TRI(fifo)   (0x2 << (fifo) * 8 << 2)
+#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4)
+#define FIFOCF_BLKSZ_1024(fifo)        (0x1 << (fifo) * 8 << 4)
+#define FIFOCF_FIFO_EN(fifo)   (0x1 << (fifo) * 8 << 5)
+
+/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */
+#define FOTG210_FIBCR(fifo)    (0x1B0 + (fifo) * 4)
+#define FIBCR_BCFX             0x7FF
+#define FIBCR_FFRST            (1 << 12)
+
+/* Device DMA Target FIFO Number Register (0x1C0) */
+#define FOTG210_DMATFNR                0x1C0
+#define DMATFNR_ACC_CXF                (1 << 4)
+#define DMATFNR_ACC_F3         (1 << 3)
+#define DMATFNR_ACC_F2         (1 << 2)
+#define DMATFNR_ACC_F1         (1 << 1)
+#define DMATFNR_ACC_F0         (1 << 0)
+#define DMATFNR_ACC_FN(fifo)   (1 << (fifo))
+#define DMATFNR_DISDMA         0
+
+/* Device DMA Controller Parameter setting 1 Register (0x1C8) */
+#define FOTG210_DMACPSR1       0x1C8
+#define DMACPSR1_DMA_LEN(len)  (((len) & 0xFFFF) << 8)
+#define DMACPSR1_DMA_ABORT     (1 << 3)
+#define DMACPSR1_DMA_TYPE(dir_in)      (((dir_in) ? 1 : 0) << 1)
+#define DMACPSR1_DMA_START     (1 << 0)
+
+/* Device DMA Controller Parameter setting 2 Register (0x1CC) */
+#define FOTG210_DMACPSR2       0x1CC
+
+/* Device DMA Controller Parameter setting 3 Register (0x1CC) */
+#define FOTG210_CXPORT         0x1D0
+
+struct fotg210_request {
+       struct usb_request      req;
+       struct list_head        queue;
+};
+
+struct fotg210_ep {
+       struct usb_ep           ep;
+       struct fotg210_udc      *fotg210;
+
+       struct list_head        queue;
+       unsigned                stall:1;
+       unsigned                wedged:1;
+       unsigned                use_dma:1;
+
+       unsigned char           epnum;
+       unsigned char           type;
+       unsigned char           dir_in;
+       unsigned int            maxp;
+       const struct usb_endpoint_descriptor    *desc;
+};
+
+struct fotg210_udc {
+       spinlock_t              lock; /* protect the struct */
+       void __iomem            *reg;
+
+       unsigned long           irq_trigger;
+
+       struct usb_gadget               gadget;
+       struct usb_gadget_driver        *driver;
+
+       struct fotg210_ep       *ep[FOTG210_MAX_NUM_EP];
+
+       struct usb_request      *ep0_req;       /* for internal request */
+       __le16                  ep0_data;
+       u8                      ep0_dir;        /* 0/0x80  out/in */
+
+       u8                      reenum;         /* if re-enumeration */
+};
+
+#define gadget_to_fotg210(g)   container_of((g), struct fotg210_udc, gadget)