This is driver for Synopsys Designware Cores USB Device
Controller (UDC) Subsystem with the AMBA Advanced High-Performance
Bus (AHB). This driver works with Synopsys UDC20 products.

Signed-off-by: Raviteja Garimella <raviteja.garime...@broadcom.com>
---
 drivers/usb/gadget/udc/Kconfig    |   12 +
 drivers/usb/gadget/udc/Makefile   |    1 +
 drivers/usb/gadget/udc/snps_udc.c | 1751 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/udc/snps_udc.h | 1071 +++++++++++++++++++++++
 4 files changed, 2835 insertions(+)
 create mode 100644 drivers/usb/gadget/udc/snps_udc.c
 create mode 100644 drivers/usb/gadget/udc/snps_udc.h

diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 658b8da..28cd679 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -239,6 +239,18 @@ config USB_MV_U3D
          MARVELL PXA2128 Processor series include a super speed USB3.0 device
          controller, which support super speed USB peripheral.
 
+config USB_SNP_UDC
+       tristate "Synopsys USB 2.0 Device controller"
+       select USB_GADGET_DUALSPEED
+       depends on (ARM || ARM64) && USB_GADGET
+       default ARCH_BCM_IPROC
+       help
+         This adds Device support for Synopsys Designware core
+         AHB subsystem USB2.0 Device Controller(UDC) .
+
+         This driver works with Synopsys UDC20 products.
+         If unsure, say N.
+
 #
 # Controllers available in both integrated and discrete versions
 #
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 98e74ed..2b63a2b 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -36,4 +36,5 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
 obj-$(CONFIG_USB_MV_U3D)       += mv_u3d_core.o
 obj-$(CONFIG_USB_GR_UDC)       += gr_udc.o
 obj-$(CONFIG_USB_GADGET_XILINX)        += udc-xilinx.o
+obj-$(CONFIG_USB_SNP_UDC)      += snps_udc.o
 obj-$(CONFIG_USB_BDC_UDC)      += bdc/
diff --git a/drivers/usb/gadget/udc/snps_udc.c 
b/drivers/usb/gadget/udc/snps_udc.c
new file mode 100644
index 0000000..d8c46ce
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc.c
@@ -0,0 +1,1751 @@
+/*
+ * snps_udc.c - Synopsys USB 2.0 Device Controller driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/extcon.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/proc_fs.h>
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/version.h>
+#include "snps_udc.h"
+
+#define DRIVER_DESC "Driver for Synopsys Designware core UDC"
+
+static void ep0_setup_init(struct snps_udc_ep *ep, int status)
+{
+       struct snps_udc *udc = ep->udc;
+
+       ep->dma.virt->setup.status = DMA_STS_BUF_HOST_READY;
+       ep->dirn = USB_DIR_OUT;
+       ep->stopped = 0;
+
+       if (!status) {
+               clear_ep_nak(udc->regs, ep->num, USB_DIR_OUT);
+               clear_ep_nak(udc->regs, ep->num, USB_DIR_IN);
+       } else {
+               enable_ep_stall(udc->regs, ep->num, USB_DIR_IN);
+               enable_ep_stall(udc->regs, ep->num, USB_DIR_OUT);
+       }
+
+       enable_udc_ep_irq(udc->regs, ep->num, USB_DIR_OUT);
+       enable_ep_dma(udc->regs, ep->num, USB_DIR_OUT);
+
+       dev_dbg(udc->dev, "%s setup buffer initialized\n", ep->name);
+}
+
+static void ep_dma_init(struct snps_udc_ep *ep)
+{
+       struct snps_udc *udc = ep->udc;
+       u32 desc_cnt = (DESC_CNT - 1);
+       u32 i;
+
+       ep->dma.virt = &ep->udc->dma.virt->ep[ep->num];
+       ep->dma.phys = &ep->udc->dma.phys->ep[ep->num];
+
+       ep->dma.virt->setup.status = DMA_STS_BUF_HOST_BUSY;
+       set_setup_buf_ptr(udc->regs, ep->num, USB_DIR_OUT,
+                         &ep->dma.phys->setup);
+
+       for (i = 0; i < DESC_CNT; i++) {
+               ep->dma.virt->desc[i].status = DMA_STS_BUF_HOST_BUSY;
+               ep->dma.virt->desc[i].next_desc_addr =
+                               (dma_addr_t)&ep->dma.phys->desc[i + 1];
+       }
+       ep->dma.virt->desc[desc_cnt].next_desc_addr =
+                               (dma_addr_t)&ep->dma.phys->desc[0];
+
+       set_data_desc_ptr(udc->regs, ep->num, USB_DIR_OUT,
+                         &ep->dma.phys->desc[0]);
+       set_data_desc_ptr(udc->regs, ep->num, USB_DIR_IN,
+                         &ep->dma.phys->desc[0]);
+
+       dev_dbg(udc->dev, " %s dma initialized\n", ep->name);
+}
+
+static void ep_data_dma_init(struct snps_udc_ep *ep)
+{
+       struct ep_xfer_req *ep_req;
+
+       dev_dbg(ep->udc->dev, "enter: %s\n", __func__);
+
+       ep_req = list_first_entry(&ep->queue, struct ep_xfer_req, queue);
+
+       if (ep_req->dma_aligned) {
+               ep_req->dma_addr_orig = ep_req->usb_req.dma;
+               ep_req->usb_req.dma = ep->dma.aligned_addr;
+               if (ep->dirn == USB_DIR_IN)
+                       memcpy(ep->dma.aligned_buf, ep_req->usb_req.buf,
+                              ep_req->usb_req.length);
+       }
+
+       ep->dma.done = 0;
+       ep->dma.len_done = 0;
+       ep->dma.len_rem = ep->dma.usb_req->length;
+       ep->dma.buf_addr = ep->dma.usb_req->dma;
+       ep->dma.status = DMA_STS_RX_SUCCESS;
+
+       if ((ep->dirn == USB_DIR_IN) &&
+           (ep->type != USB_ENDPOINT_XFER_ISOC)) {
+               if (in_bf_mode)
+                       ep->dma.len_max = ep->dma.usb_req->length;
+               else
+                       ep->dma.len_max = ep->usb_ep.maxpacket;
+       } else {
+               if (out_bf_mode)
+                       ep->dma.len_max = ep->dma.usb_req->length;
+               else
+                       ep->dma.len_max = ep->usb_ep.maxpacket;
+       }
+
+       dma_desc_chain_reset(ep);
+}
+
+static void ep_data_dma_finish(struct snps_udc_ep *ep)
+{
+       struct snps_udc *udc = ep->udc;
+       struct ep_xfer_req *ep_req;
+
+       disable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+       disable_ep_dma(udc->regs, ep->num, ep->dirn);
+
+       ep_req = list_first_entry(&ep->queue, struct ep_xfer_req, queue);
+
+       if (ep_req->dma_aligned) {
+               if (ep->dirn == USB_DIR_OUT)
+                       memcpy(ep_req->usb_req.buf,
+                              ep->dma.aligned_buf, ep_req->usb_req.length);
+               ep_req->usb_req.dma = ep_req->dma_addr_orig;
+       }
+       dev_dbg(udc->dev, "%s dma finished\n", ep->name);
+}
+
+static void ep_data_dma_add(struct snps_udc_ep *ep)
+{
+       struct data_desc *desc = NULL;
+       u32 status;
+       u32 len;
+
+       if (!ep->dma.len_rem)
+               ep->dma.usb_req->zero = 1;
+
+       ep->dma.last = ep->dma.usb_req->zero;
+
+       while (!dma_desc_chain_is_full(ep) &&
+              (ep->dma.len_rem || ep->dma.usb_req->zero)) {
+               desc = dma_desc_chain_alloc(ep);
+               len = (ep->dma.len_rem < ep->dma.len_max) ?
+                       ep->dma.len_rem : ep->dma.len_max;
+               ep->dma.len_rem -= len;
+               status = 0;
+
+               if (len <= ep->dma.len_max ||
+                   (out_bf_mode && (len <= ep->dma.len_max))) {
+                       if (in_bf_mode ||
+                           !((ep->dirn == USB_DIR_IN) &&
+                             (ep->type == USB_ENDPOINT_XFER_BULK) &&
+                             (len != 0) &&
+                             (len % ep->usb_ep.maxpacket == 0)))
+                               ep->dma.usb_req->zero = 0;
+               }
+
+               if ((ep->dirn == USB_DIR_IN) &&
+                   (ep->type == USB_ENDPOINT_XFER_ISOC)) {
+                       ep->dma.frame_num += ep->dma.frame_incr;
+                       dev_dbg(ep->udc->dev, "%s: DMA started: 
frame_num=%d.%d\n",
+                               ep->name, (ep->dma.frame_num >> 3),
+                               (ep->dma.frame_num & 0x7));
+                       status |= ((ep->dma.frame_num <<
+                                 DMA_STS_FRAME_NUM_SHIFT)
+                                 & DMA_STS_FRAME_NUM_MASK);
+               }
+
+               desc->buf_addr = ep->dma.buf_addr;
+               status |= (len << DMA_STS_BYTE_CNT_SHIFT);
+               desc->status = status | DMA_STS_BUF_HOST_READY;
+               /* Ensure all writes are done before going for next descriptor*/
+               wmb();
+               ep->dma.buf_addr += len;
+
+               if ((ep->dirn == USB_DIR_IN) &&
+                   (ep->type == USB_ENDPOINT_XFER_ISOC))
+                       break;
+       }
+
+       if (desc)
+               desc->status |= DMA_STS_LAST_DESC;
+
+       dev_dbg(ep->udc->dev, "%s dma data added\n", ep->name);
+}
+
+static void ep_data_dma_remove(struct snps_udc_ep *ep)
+{
+       struct data_desc *desc;
+       u32 status;
+       u32 len = 0;
+
+       while (!dma_desc_chain_is_empty(ep)) {
+               desc = dma_desc_chain_head(ep);
+               status = desc->status;
+               desc->status = DMA_STS_BUF_HOST_BUSY;
+               /* Ensure all writes are done before going for next descriptor*/
+               wmb();
+               len = (status & DMA_STS_NISO_BYTE_CNT_MASK) >>
+                       DMA_STS_NISO_BYTE_CNT_SHIFT;
+
+               if ((ep->dirn == USB_DIR_IN) || (status &
+                                       DMA_STS_LAST_DESC)) {
+                       ep->dma.len_done += len;
+                       ep->dma.usb_req->actual += len;
+               }
+
+               if ((status & DMA_STS_RX_MASK) != DMA_STS_RX_SUCCESS) {
+                       ep->dma.status = status & DMA_STS_RX_MASK;
+                       ep->dma.usb_req->status = -EIO;
+                       dev_warn(ep->udc->dev, "%s: DMA error\n", ep->name);
+               }
+
+               if ((ep->dirn == USB_DIR_IN) &&
+                   (ep->type == USB_ENDPOINT_XFER_ISOC)) {
+                       if (ep->dma.usb_req->actual ==
+                                       ep->dma.usb_req->length)
+                               ep->dma.usb_req->status = 0;
+                       dma_desc_chain_reset(ep);
+               } else {
+                       dma_desc_chain_free(ep);
+               }
+       }
+
+       if ((!ep->dma.len_rem || (len < ep->usb_ep.maxpacket)) &&
+           (ep->dma.usb_req->status == -EINPROGRESS))
+               ep->dma.usb_req->status = 0;
+
+       dev_dbg(ep->udc->dev, "%s dma data removed\n", ep->name);
+}
+
+static int fifo_ram_alloc(struct snps_udc_ep *ep, u32 max_pkt_size)
+{
+       u32 rx_cnt;
+       u32 tx_cnt;
+
+       switch (EP_DIRN_TYPE(ep->dirn, ep->type)) {
+       case EP_DIRN_TYPE(USB_DIR_OUT, USB_ENDPOINT_XFER_BULK):
+       case EP_DIRN_TYPE(USB_DIR_OUT, USB_ENDPOINT_XFER_INT):
+       case EP_DIRN_TYPE(USB_DIR_OUT, USB_ENDPOINT_XFER_ISOC):
+               rx_cnt = FIFO_SZ_U8(max_pkt_size);
+               tx_cnt = 0;
+               break;
+
+       case EP_DIRN_TYPE(USB_DIR_IN, USB_ENDPOINT_XFER_BULK):
+       case EP_DIRN_TYPE(USB_DIR_IN, USB_ENDPOINT_XFER_INT):
+               rx_cnt = 0;
+               tx_cnt = FIFO_SZ_U8(max_pkt_size);
+               break;
+
+       case EP_DIRN_TYPE(USB_DIR_IN, USB_ENDPOINT_XFER_ISOC):
+               rx_cnt = 0;
+               tx_cnt = 2 * FIFO_SZ_U8(max_pkt_size);
+               break;
+
+       case EP_DIRN_TYPE(USB_DIR_IN,  USB_ENDPOINT_XFER_CONTROL):
+       case EP_DIRN_TYPE(USB_DIR_OUT, USB_ENDPOINT_XFER_CONTROL):
+               rx_cnt = FIFO_SZ_U8(max_pkt_size);
+               tx_cnt = rx_cnt;
+               break;
+
+       default:
+               dev_err(ep->udc->dev, "%s: invalid EP attributes\n", ep->name);
+               return -ENODEV;
+       }
+
+       dev_dbg(ep->udc->dev, "rx req=%u free=%u: tx req=%u free=%u\n",
+               rx_cnt, ep->udc->rx_fifo_space, tx_cnt, ep->udc->tx_fifo_space);
+
+       if ((ep->udc->rx_fifo_space < rx_cnt) ||
+           (ep->udc->tx_fifo_space < tx_cnt)) {
+               dev_err(ep->udc->dev, "%s: fifo alloc failed\n", ep->name);
+               return -ENOSPC;
+       }
+
+       ep->rx_fifo_size = rx_cnt;
+       ep->tx_fifo_size = tx_cnt;
+
+       if (mrx_fifo)
+               ep->udc->rx_fifo_space -= rx_cnt;
+
+       ep->udc->tx_fifo_space -= tx_cnt;
+
+       return 0;
+}
+
+static void fifo_ram_free(struct snps_udc_ep *ep)
+{
+       if (mrx_fifo)
+               ep->udc->rx_fifo_space += ep->rx_fifo_size;
+
+       ep->udc->tx_fifo_space += ep->tx_fifo_size;
+
+       ep->rx_fifo_size = 0;
+       ep->tx_fifo_size = 0;
+}
+
+static int ep_cfg(struct snps_udc_ep *ep, u32 type,
+                 u32 max_pkt_size)
+{
+       struct snps_udc *udc = ep->udc;
+
+       ep->type = type;
+       if (fifo_ram_alloc(ep, max_pkt_size) != 0)
+               return -ENOSPC;
+
+       ep->type = type;
+       ep->usb_ep.maxpacket = max_pkt_size;
+
+       if (ep->udc->conn_type)
+               init_ep_reg(udc->regs, ep->num, ep->type, ep->dirn,
+                           max_pkt_size);
+       dev_dbg(udc->dev, "ep_cfg: %s: type=%u dirn=0x%x pkt=%u\n",
+               ep->usb_ep.name, type, ep->dirn, max_pkt_size);
+
+       return 0;
+}
+
+static void epreq_xfer_done(struct snps_udc_ep *ep,
+                           struct ep_xfer_req *ep_req, int status)
+{
+       struct snps_udc *udc = ep->udc;
+       u32 stopped;
+
+       list_del_init(&ep_req->queue);
+
+       if (ep_req->usb_req.status == -EINPROGRESS)
+               ep_req->usb_req.status = status;
+
+       if (ep_req->dma_aligned) {
+               ep_req->dma_aligned = 0;
+       } else if (ep_req->dma_mapped) {
+               dma_unmap_single(ep->udc->gadget.dev.parent,
+                                ep_req->usb_req.dma,
+                                (ep_req->usb_req.length ?
+                                ep_req->usb_req.length : 1),
+                                (ep->dirn == USB_DIR_IN ? DMA_TO_DEVICE :
+                                DMA_FROM_DEVICE));
+               ep_req->dma_mapped = 0;
+               ep_req->usb_req.dma = DMA_ADDR_INVALID;
+       }
+
+       dev_dbg(udc->dev, "%s xfer done req=0x%p buf=0x%p len=%d actual=%d\n",
+               ep->name, &ep_req->usb_req, ep_req->usb_req.buf,
+               ep_req->usb_req.length, ep_req->usb_req.actual);
+
+       stopped = ep->stopped;
+       ep->stopped = 1;
+       spin_unlock(&ep->udc->lock);
+       ep_req->usb_req.complete(&ep->usb_ep, &ep_req->usb_req);
+       spin_lock(&ep->udc->lock);
+       ep->stopped = stopped;
+}
+
+static void epreq_xfer_process(struct snps_udc_ep *ep)
+{
+       struct snps_udc *udc = ep->udc;
+       struct ep_xfer_req *ep_req;
+
+       dev_dbg(udc->dev, "%s: xfer request\n", ep->name);
+
+       if (!ep->dma.usb_req) {
+               dev_dbg(udc->dev, "%s: No dma usb request\n", ep->name);
+               return;
+       }
+
+       disable_ep_dma(udc->regs, ep->num, ep->dirn);
+       ep_data_dma_remove(ep);
+
+       if (ep->dma.usb_req->status != -EINPROGRESS) {
+               ep_data_dma_finish(ep);
+
+               if ((ep->type == USB_ENDPOINT_XFER_CONTROL) &&
+                   (ep->dirn == USB_DIR_IN) &&
+                   (ep->dma.usb_req->status == 0)) {
+                       ep->dirn = USB_DIR_OUT;
+                       ep->b_ep_addr = ep->num | ep->dirn;
+                       ep->dma.usb_req->status = -EINPROGRESS;
+                       ep->dma.usb_req->actual = 0;
+                       ep->dma.usb_req->length = 0;
+                       ep_data_dma_init(ep);
+               } else {
+                       if (in_bf_mode && is_ep_in() && is_ep_bulk() &&
+                           (ep->dma.usb_req->length != 0) &&
+                           (ep->dma.usb_req->length %
+                           ep->usb_ep.maxpacket == 0) &&
+                           (ep->dma.last)) {
+                               ep->dma.usb_req->status = -EINPROGRESS;
+                               ep->dma.usb_req->actual = 0;
+                               ep->dma.usb_req->length = 0;
+                       } else if (!list_empty(&ep->queue))
+                               epreq_xfer_done(ep,
+                                               list_first_entry(&ep->queue,
+                                                                struct
+                                                                ep_xfer_req,
+                                                                queue), 0);
+
+                       if (ep->type == USB_ENDPOINT_XFER_CONTROL)
+                               ep0_setup_init(ep, 0);
+
+                       if (is_ep_in() && is_ep_bulk() &&
+                           !list_empty(&ep->queue)) {
+                               ep->in_xfer_done = true;
+                               clear_ep_nak(udc->regs, ep->num, ep->dirn);
+                               enable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+                               return;
+                       }
+
+                       if (list_empty(&ep->queue)) {
+                               ep->dma.usb_req = NULL;
+                       } else {
+                               ep_req = list_first_entry(&ep->queue,
+                                                         struct ep_xfer_req,
+                                                         queue);
+                               ep->dma.usb_req = &ep_req->usb_req;
+                               ep_data_dma_init(ep);
+                       }
+               }
+       }
+
+       if (ep->dma.usb_req) {
+               ep_data_dma_add(ep);
+               enable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+               clear_ep_nak(udc->regs, ep->num, ep->dirn);
+               enable_ep_dma(udc->regs, ep->num, ep->dirn);
+       }
+}
+
+static void epreq_xfer_error(struct snps_udc_ep *ep, int status)
+{
+       if (!ep->dma.usb_req) {
+               dev_err(ep->udc->dev, "%s: No DMA usb request\n", ep->name);
+               return;
+       }
+
+       ep->dma.usb_req->status = status;
+       epreq_xfer_process(ep);
+}
+
+static void epreq_xfer_add(struct snps_udc_ep *ep,
+                          struct ep_xfer_req *ep_req)
+{
+       struct snps_udc *udc = ep->udc;
+
+       list_add_tail(&ep_req->queue, &ep->queue);
+       if (ep->stopped)
+               return;
+
+       if ((ep->dirn == USB_DIR_IN) &&
+           (ep->type == USB_ENDPOINT_XFER_ISOC) &&
+           (ep->dma.usb_req) &&
+           (ep->dma.frame_num == FRAME_NUM_INVALID)) {
+               ep_data_dma_finish(ep);
+               ep->dma.usb_req = NULL;
+               epreq_xfer_done(ep,
+                               list_first_entry(&ep->queue,
+                                                struct ep_xfer_req,
+                                                queue),
+                               -EREMOTEIO);
+       }
+
+       if (ep->dma.usb_req) {
+               dev_dbg(udc->dev, "%s: busy\n", ep->name);
+       } else if (!in_isoc_delay_disabled && (ep->dirn == USB_DIR_IN) &&
+                  (ep->type == USB_ENDPOINT_XFER_ISOC) &&
+                  (ep->dma.frame_num == FRAME_NUM_INVALID)) {
+               dev_dbg(udc->dev, "%s: ISOC delay xfer start\n", ep->name);
+               ep->dma.usb_req = &(list_first_entry(&ep->queue,
+                               struct ep_xfer_req, queue))->usb_req;
+               ep_data_dma_init(ep);
+               clear_ep_nak(udc->regs, ep->num, ep->dirn);
+               enable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+
+       } else {
+               if (in_isoc_delay_disabled && (ep->dirn == USB_DIR_IN) &&
+                   (ep->type == USB_ENDPOINT_XFER_ISOC) &&
+                   (ep->dma.frame_num == FRAME_NUM_INVALID)) {
+                       ep->dma.frame_num = get_last_rx_frnum(udc->regs);
+               }
+
+               if (is_ep_in() && is_ep_bulk() && !ep->dma.usb_req) {
+                       ep->in_xfer_done = true;
+                       clear_ep_nak(udc->regs, ep->num, ep->dirn);
+                       enable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+                       return;
+               }
+
+               ep_req = list_first_entry(&ep->queue,
+                                         struct ep_xfer_req, queue);
+               ep->dma.usb_req = &ep_req->usb_req;
+               ep_data_dma_init(ep);
+               ep_data_dma_add(ep);
+               enable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+               clear_ep_nak(udc->regs, ep->num, ep->dirn);
+               enable_ep_dma(udc->regs, ep->num, ep->dirn);
+       }
+
+       dev_dbg(udc->dev, "%s: xfer add ep request\n", ep->name);
+}
+
+static void epreq_queue_flush(struct snps_udc_ep *ep, int status)
+{
+       struct snps_udc *udc = ep->udc;
+       struct ep_xfer_req *ep_req;
+
+       ep->stopped = 1;
+
+       while (!list_empty(&ep->queue)) {
+               ep_req = list_first_entry(&ep->queue,
+                                         struct ep_xfer_req, queue);
+               epreq_xfer_done(ep, ep_req, status);
+       }
+
+       ep->dma.usb_req = NULL;
+       if ((is_ep_in() && is_ep_bulk()) || !ep->num) {
+               set_ep_fifo_flush(udc->regs, ep->num, ep->dirn);
+               clear_ep_fifo_flush(udc->regs, ep->num, ep->dirn);
+       }
+
+       dev_dbg(udc->dev, "%s: EP queue flushed\n", ep->usb_ep.name);
+}
+
+static void ep0_setup_rx(struct snps_udc_ep *ep,
+                        struct usb_ctrlrequest *setup)
+{
+       struct snps_udc *udc = ep->udc;
+       int status;
+       u32 val;
+       u32 idx;
+       u32 len;
+
+       val = le16_to_cpu(setup->wValue);
+       idx = le16_to_cpu(setup->wIndex);
+       len = le16_to_cpu(setup->wLength);
+
+       ep->dirn = setup->bRequestType & USB_ENDPOINT_DIR_MASK;
+
+       dev_dbg(udc->dev, "%s: SETUP %02x.%02x v%04x i%04x l %04x\n",
+               ep->name, setup->bRequestType, setup->bRequest,
+               val, idx, len);
+
+       if (ep->num != 0) {
+               status = -EOPNOTSUPP;
+       } else {
+               spin_unlock(&udc->lock);
+               status = udc->gadget_driver->setup(&udc->gadget, setup);
+               spin_lock(&udc->lock);
+       }
+
+       if (status < 0)
+               ep0_setup_init(ep, status);
+       else if (len == 0)
+               ep0_setup_init(ep, 0);
+}
+
+static void irq_ep_out_setup(struct snps_udc_ep *ep)
+{
+       struct setup_desc *desc = &ep->dma.virt->setup;
+       u32 status = desc->status;
+
+       dev_dbg(ep->udc->dev, "irq set up %s desc status: 0x%x\n",
+               ep->name, status);
+
+       if ((status & DMA_STS_BUF_MASK) != DMA_STS_BUF_DMA_DONE) {
+               ep0_setup_init(ep, 0);
+       } else if ((status & DMA_STS_RX_MASK) != DMA_STS_RX_SUCCESS) {
+               ep0_setup_init(ep, 0);
+       } else {
+               desc->status = (status & ~DMA_STS_BUF_MASK)
+                                       | DMA_STS_BUF_HOST_BUSY;
+               ep0_setup_rx(ep, (struct usb_ctrlrequest *)&desc->data1);
+       }
+}
+
+static void irq_process_epout(struct snps_udc_ep *ep)
+{
+       struct snps_udc *udc = ep->udc;
+       u32 status;
+
+       status = get_ep_status(udc->regs, ep->num, USB_DIR_OUT);
+       clear_ep_status(udc->regs, ep->num, USB_DIR_OUT, status);
+
+       status &= EP_STS_ALL;
+
+       if (!status)
+               return;
+
+       if ((ep->dirn != USB_DIR_OUT) &&
+           (ep->type != USB_ENDPOINT_XFER_CONTROL)) {
+               dev_err(udc->dev, "%s: unexpected interrupt\n", ep->name);
+               return;
+       }
+
+       if (status & OUT_DMA_DATA_DONE) {
+               status &= ~OUT_DMA_DATA_DONE;
+               epreq_xfer_process(ep);
+       }
+
+       if (status & OUT_DMA_SETUP_DONE) {
+               status &= ~OUT_DMA_SETUP_DONE;
+               irq_ep_out_setup(ep);
+       }
+
+       if (status & DMA_BUF_NOT_AVAIL) {
+               status &= ~DMA_BUF_NOT_AVAIL;
+               dev_dbg(udc->dev, "%s: DMA BUF NOT AVAIL\n", ep->name);
+               epreq_xfer_process(ep);
+       }
+
+       if (status & DMA_ERROR) {
+               status &= ~DMA_ERROR;
+               dev_err(udc->dev, "%s: DMA ERROR\n", ep->usb_ep.name);
+               epreq_xfer_error(ep, -EIO);
+       }
+
+       if (status)
+               dev_err(udc->dev, "%s: unknown status=0x%x\n",
+                       ep->name, status);
+}
+
+static void irq_process_epin(struct snps_udc_ep *ep)
+{
+       struct snps_udc *udc = ep->udc;
+       struct ep_xfer_req *ep_req;
+       u32 status;
+
+       status = get_ep_status(udc->regs, ep->num, USB_DIR_IN);
+       clear_ep_status(udc->regs, ep->num, USB_DIR_IN, status);
+
+       if (!status)
+               return;
+
+       if (ep->dirn != USB_DIR_IN) {
+               dev_err(udc->dev, "%s: unexpected OUT endpoint\n", ep->name);
+               return;
+       }
+
+       if ((ep->type == USB_ENDPOINT_XFER_ISOC) &&
+           (status & (IN_XFER_DONE | DMA_BUF_NOT_AVAIL))) {
+               dev_warn(ep->udc->dev, "%s: ISOC IN unexpected status=0x%x\n",
+                        ep->name, status);
+       }
+
+       if (status & IN_TOKEN_RX) {
+               status &= ~IN_TOKEN_RX;
+               if (!ep->dma.usb_req && list_empty(&ep->queue))
+                       enable_ep_nak(udc->regs, ep->num, USB_DIR_IN);
+
+               if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+                       ep->dma.frame_num = get_frnum_last_rx(udc->regs);
+                       dev_dbg(udc->dev, "%s: ISOC IN\n", ep->name);
+                       if (ep->dma.usb_req) {
+                               ep->dma.usb_req->status = -EREMOTEIO;
+                               epreq_xfer_process(ep);
+                       }
+               }
+       }
+
+       if (is_ep_bulk() && !list_empty(&ep->queue) &&
+           ep->in_xfer_done) {
+               ep->in_xfer_done = false;
+               ep_req = list_first_entry(&ep->queue,
+                                         struct ep_xfer_req, queue);
+               ep->dma.usb_req = &ep_req->usb_req;
+
+               ep_data_dma_init(ep);
+               ep_data_dma_add(ep);
+               clear_ep_nak(udc->regs, ep->num, ep->dirn);
+               enable_udc_ep_irq(udc->regs, ep->num, ep->dirn);
+               enable_ep_dma(udc->regs, ep->num, ep->dirn);
+       }
+
+       if (status & IN_DMA_DONE) {
+               status &= ~IN_DMA_DONE;
+               clear_ep_nak(udc->regs, ep->num, USB_DIR_IN);
+
+               if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+                       dev_dbg(udc->dev, "%s: ISOC IN\n", ep->usb_ep.name);
+                       epreq_xfer_process(ep);
+               } else if (ep->dma.done & IN_XFER_DONE) {
+                       dev_dbg(udc->dev, "%s: late IN DMA done rec'd\n",
+                               ep->name);
+                       epreq_xfer_process(ep);
+               } else {
+                       ep->dma.done = IN_DMA_DONE;
+               }
+       }
+
+       if (status & IN_XFER_DONE) {
+               status &= ~(IN_XFER_DONE);
+               status &= ~(IN_FIFO_EMPTY);
+
+               if (ep->dma.done & IN_DMA_DONE)
+                       epreq_xfer_process(ep);
+               else
+                       ep->dma.done = IN_XFER_DONE;
+       }
+
+       status &= ~(IN_FIFO_EMPTY);
+
+       if (status & DMA_BUF_NOT_AVAIL) {
+               dev_err(udc->dev, "%s: DMA BUF NOT AVAIL\n", ep->name);
+               status &= ~(DMA_BUF_NOT_AVAIL);
+               epreq_xfer_process(ep);
+       }
+
+       if (status & DMA_ERROR) {
+               status &= ~DMA_ERROR;
+               dev_err(udc->dev, "%s: DMA ERROR\n", ep->name);
+               epreq_xfer_error(ep, -EIO);
+       }
+
+       if (status)
+               dev_err(udc->dev, "%s: unknown status=0x%x\n",
+                       ep->name, status);
+}
+
+static void ep_irq_process(struct snps_udc *udc, u32 irq_in, u32 irq_out)
+{
+       u32 mask = 1;
+       u32 num;
+
+       for (num = 0; num < UDC_MAX_EP; num++) {
+               if (irq_in & mask)
+                       irq_process_epin(&udc->ep[num]);
+
+               if (irq_out & mask)
+                       irq_process_epout(&udc->ep[num]);
+
+               mask <<= 1;
+       }
+}
+
+static void irq_process_set_intf(struct snps_udc *udc)
+{
+       struct usb_ctrlrequest setup;
+       u32 ep_num;
+       u16 intf;
+       u16 alt;
+
+       intf = (uint16_t)get_intf_num(udc->regs);
+       alt =  (uint16_t)get_alt_num(udc->regs);
+
+       setup.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD
+                                        | USB_RECIP_INTERFACE;
+       setup.bRequest = USB_REQ_SET_INTERFACE;
+       setup.wValue = cpu_to_le16(alt);
+       setup.wIndex = cpu_to_le16(intf);
+       setup.wLength = 0;
+
+       for (ep_num = 0; ep_num < UDC_MAX_EP; ep_num++) {
+               set_ep_alt_num(udc->regs, ep_num, alt);
+               set_ep_intf_num(udc->regs, ep_num, intf);
+       }
+       dev_info(udc->dev, "SET INTF=%d ALT=%d\n", intf, alt);
+
+       ep0_setup_rx(&udc->ep[0], &setup);
+       set_setup_done(udc->regs);
+}
+
+static void irq_process_set_cfg(struct snps_udc *udc)
+{
+       struct usb_ctrlrequest setup;
+       u32 ep_num;
+       u16 cfg;
+
+       cfg = (u16)get_cfg_num(udc->regs);
+
+       setup.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD
+                                        | USB_RECIP_DEVICE;
+       setup.bRequest = USB_REQ_SET_CONFIGURATION;
+       setup.wValue = cpu_to_le16(cfg);
+       setup.wIndex = 0;
+       setup.wLength = 0;
+
+       for (ep_num = 0; ep_num < UDC_MAX_EP; ep_num++)
+               set_epcfg_reg(udc->regs, ep_num, cfg);
+
+       dev_info(udc->dev, "SET CFG=%d\n", cfg);
+
+       ep0_setup_rx(&udc->ep[0], &setup);
+       set_setup_done(udc->regs);
+}
+
+static void irq_process_speed_enum(struct snps_udc *udc)
+{
+       u32 speed = udc->gadget.speed;
+
+       switch (get_enum_speed(udc->regs)) {
+       case SPEED_HIGH:
+               dev_info(udc->dev, "HIGH SPEED\n");
+               udc->gadget.speed = USB_SPEED_HIGH;
+               break;
+       case SPEED_FULL:
+               dev_info(udc->dev, "FULL SPEED\n");
+               udc->gadget.speed = USB_SPEED_FULL;
+               break;
+       case SPEED_LOW:
+               dev_warn(udc->dev, "LOW SPEED not supported\n");
+               udc->gadget.speed = USB_SPEED_LOW;
+               break;
+       default:
+               dev_err(udc->dev, "Unknown SPEED = 0x%x\n",
+                       get_enum_speed(udc->regs));
+               break;
+       }
+
+       if ((speed == USB_SPEED_UNKNOWN) &&
+           (udc->gadget.speed != USB_SPEED_UNKNOWN)) {
+               ep0_setup_init(&udc->ep[0], 0);
+               clear_devnak(udc->regs);
+       }
+}
+
+static void irq_process_bus_idle(struct snps_udc *udc)
+{
+       int num;
+
+       for (num = 0; num < UDC_MAX_EP; num++) {
+               set_ep_fifo_flush(udc->regs, num, EP_DIRN_IN);
+               clear_ep_fifo_flush(udc->regs, num, EP_DIRN_IN);
+       }
+}
+
+static void dev_irq_process(struct snps_udc *udc, u32 irq)
+{
+       if (irq & IRQ_BUS_RESET)
+               dev_info(udc->dev, "BUS RESET\n");
+
+       if (irq & IRQ_BUS_SUSPEND)
+               dev_dbg(udc->dev, "BUS SUSPEND\n");
+
+       if (irq & IRQ_BUS_IDLE) {
+               dev_dbg(udc->dev, "BUS IDLE\n");
+               irq_process_bus_idle(udc);
+       }
+
+       if (irq & IRQ_SPEED_ENUM_DONE) {
+               dev_dbg(udc->dev, "BUS speed enum done\n");
+               irq_process_speed_enum(udc);
+       }
+
+       if (irq & IRQ_SET_CFG) {
+               dev_dbg(udc->dev, "SET CFG\n");
+               irq_process_set_cfg(udc);
+       }
+
+       if (irq & IRQ_SET_INTF) {
+               dev_dbg(udc->dev, "SET INTF\n");
+               irq_process_set_intf(udc);
+       }
+}
+
+static irqreturn_t snps_udc_irq(int irq, void *dev)
+{
+       struct snps_udc *udc = (struct snps_udc *)dev;
+       u32 devintr, epin_intr, epout_intr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       devintr = get_irq_active(udc->regs);
+       epin_intr = get_ep_irq_active(udc->regs, USB_DIR_IN);
+       epout_intr = get_ep_irq_active(udc->regs, USB_DIR_OUT);
+
+       clear_udc_dev_irq(udc->regs, devintr);
+       clear_udc_ep_irq_list(udc->regs, USB_DIR_IN, epin_intr);
+       clear_udc_ep_irq_list(udc->regs, USB_DIR_OUT, epout_intr);
+
+       if (!udc->gadget_driver) {
+               spin_unlock_irqrestore(&udc->lock, flags);
+               return IRQ_NONE;
+       }
+
+       /* SET_CFG and SET_INTF interrupts are handled last */
+       dev_irq_process(udc, devintr & ~(IRQ_SET_CFG | IRQ_SET_INTF));
+       ep_irq_process(udc, epin_intr, epout_intr);
+       dev_irq_process(udc, devintr & (IRQ_SET_CFG | IRQ_SET_INTF));
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+       dev_dbg(udc->dev, "UDC interrupts: Dev=0x%x EpIn=0x%x EpOut=0x%x\n",
+               devintr, epin_intr, epout_intr);
+
+       return IRQ_HANDLED;
+}
+
+static int snps_ep_enable(struct usb_ep *usb_ep,
+                         const struct usb_endpoint_descriptor *desc)
+{
+       struct snps_udc_ep *ep;
+       struct snps_udc *udc;
+       unsigned long flags;
+       u32 max_pkt_size;
+       u32 xfertype;
+
+       ep = container_of(usb_ep, struct snps_udc_ep, usb_ep);
+       udc = ep->udc;
+
+       if (!usb_ep || (ep->b_ep_addr != desc->bEndpointAddress)) {
+               dev_err(udc->dev, "invalid endpoint (%p)\n", usb_ep);
+               return -EINVAL;
+       }
+
+       if (!desc || (desc->bDescriptorType != USB_DT_ENDPOINT)) {
+               dev_err(udc->dev, "ep%d: invalid descriptor=%p\n",
+                       ep->num, desc);
+               return -EINVAL;
+       }
+
+       if (desc == ep->desc) {
+               dev_err(udc->dev, "ep%d: already enabled\n", ep->num);
+               return -EEXIST;
+       }
+
+       if (ep->desc) {
+               dev_err(udc->dev, "ep%d:already enabled wth other descr\n",
+                       ep->num);
+               return -EBUSY;
+       }
+
+       if (!udc->gadget_driver) {
+               dev_warn(udc->dev, "%s: invalid device state\n", ep->name);
+               return -ESHUTDOWN;
+       }
+
+       xfertype = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+       max_pkt_size = le16_to_cpu(desc->wMaxPacketSize) & 0x7FF;
+
+       if (!max_pkt_size || (max_pkt_size > ep->max_pkt_size)) {
+               dev_err(udc->dev, "%s: invalid max pkt size\n", ep->name);
+               return -ERANGE;
+       }
+
+       if ((ep->dirn == USB_DIR_IN) &&
+           (xfertype == USB_ENDPOINT_XFER_ISOC)) {
+               if ((desc->bInterval < 1) || (desc->bInterval > 16)) {
+                       dev_err(udc->dev, "%s: invalid binterval\n", ep->name);
+                       return -ERANGE;
+               }
+               ep->dma.frame_num = FRAME_NUM_INVALID;
+               ep->dma.frame_incr = 1 << (desc->bInterval - 1);
+       }
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       if (ep_cfg(ep, xfertype, max_pkt_size) != 0) {
+               spin_unlock_irqrestore(&udc->lock, flags);
+               dev_err(udc->dev, "%s: not enough FIFO space\n", ep->name);
+               return -ENOSPC;
+       }
+
+       set_epcfg_reg(udc->regs, ep->num, get_cfg_num(udc->regs));
+
+       ep->desc = desc;
+       ep->stopped = 0;
+       ep->usb_ep.maxpacket = max_pkt_size;
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       dev_dbg(udc->dev, "%s: enabled: type: 0x%x, max_pkt_size: %d\n",
+               ep->name, xfertype, max_pkt_size);
+
+       return 0;
+}
+
+static int snps_ep_disable(struct usb_ep *usb_ep)
+{
+       struct snps_udc_ep *ep;
+       struct snps_udc *udc;
+       unsigned long flags;
+
+       ep = container_of(usb_ep, struct snps_udc_ep, usb_ep);
+       udc = ep->udc;
+
+       if (!usb_ep || !ep->desc) {
+               dev_err(udc->dev, "%s: invalid endpoint\n", ep->usb_ep.name);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       epreq_queue_flush(ep, -ESHUTDOWN);
+       ep->desc = NULL;
+       ep->usb_ep.maxpacket = ep->max_pkt_size;
+       fifo_ram_free(ep);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return 0;
+}
+
+static struct usb_request *
+snps_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp_flags)
+{
+       struct ep_xfer_req *ep_req;
+
+       if (!usb_ep)
+               return NULL;
+
+       ep_req = kzalloc(sizeof(*ep_req), gfp_flags);
+       if (ep_req) {
+               INIT_LIST_HEAD(&ep_req->queue);
+               ep_req->usb_req.dma = DMA_ADDR_INVALID;
+               pr_debug("%s: ep alloc req\n", usb_ep->name);
+               return &ep_req->usb_req;
+       }
+
+       return NULL;
+}
+
+static void snps_ep_free_request(struct usb_ep *usb_ep,
+                                struct usb_request *usb_req)
+{
+       struct ep_xfer_req *ep_req;
+
+       ep_req = container_of(usb_req, struct ep_xfer_req, usb_req);
+
+       if (usb_req) {
+               pr_debug("%s: freed\n", usb_ep->name);
+               kfree(ep_req);
+       }
+}
+
+static int snps_ep_queue(struct usb_ep *usb_ep,
+                        struct usb_request *usb_req, gfp_t gfp_flags)
+{
+       struct ep_xfer_req *ep_req;
+       struct snps_udc_ep *ep;
+       struct snps_udc *udc;
+       unsigned long flags;
+
+       ep = container_of(usb_ep, struct snps_udc_ep, usb_ep);
+       ep_req = container_of(usb_req, struct ep_xfer_req, usb_req);
+
+       dev_dbg(ep->udc->dev, "%s: %s\n", __func__, ep->usb_ep.name);
+       if (!usb_ep || !usb_req || !ep_req->usb_req.complete ||
+           !ep_req->usb_req.buf || !list_empty(&ep_req->queue)) {
+               dev_dbg(ep->udc->dev, "%s:invalid queue request\n", ep->name);
+               return -EINVAL;
+       }
+
+       if (!ep->desc && (ep->num != 0)) {
+               dev_err(ep->udc->dev, "%s: invalid EP state\n", ep->name);
+               return -EFAULT;
+       }
+
+       if ((ep->type == USB_ENDPOINT_XFER_CONTROL) &&
+           !list_empty(&ep->queue)) {
+               dev_err(ep->udc->dev, "%s: EP queue not empty\n", ep->name);
+               return -EPERM;
+       }
+
+       if (usb_req->length > 0xffff) {
+               dev_err(ep->udc->dev, "%s: request too big\n", ep->name);
+               return -E2BIG;
+       }
+
+       if ((ep->type == USB_ENDPOINT_XFER_ISOC) &&
+           (ep->dirn == USB_DIR_IN) &&
+           (usb_req->length > ep->usb_ep.maxpacket)) {
+               dev_err(ep->udc->dev, "%s: request > scheduled bandwidth, 
length=%u\n",
+                       ep->name, usb_req->length);
+               return -EFBIG;
+       }
+
+       udc = ep->udc;
+       if (!udc->gadget_driver) {
+               dev_err(udc->dev, "%s: invalid device state\n", ep->name);
+               return -ESHUTDOWN;
+       }
+
+       if (((unsigned long)ep_req->usb_req.buf) & 0x3UL) {
+               dev_dbg(udc->dev, "%s: invalid buffer alignment: addr=0x%p\n",
+                       ep->usb_ep.name, ep_req->usb_req.buf);
+
+               if ((ep->dma.aligned_buf) &&
+                   (ep->dma.aligned_len < ep_req->usb_req.length)) {
+                       dma_free_coherent(NULL, ep->dma.aligned_len,
+                                         ep->dma.aligned_buf,
+                                         ep->dma.aligned_addr);
+                       ep->dma.aligned_buf = NULL;
+               }
+
+               if (!ep->dma.aligned_buf) {
+                       ep->dma.aligned_len = ep_req->usb_req.length;
+                       ep->dma.aligned_buf = dma_alloc_coherent(NULL,
+                               ep->dma.aligned_len, &ep->dma.aligned_addr,
+                               GFP_ATOMIC);
+               }
+
+               if (!ep->dma.aligned_buf) {
+                       dev_err(udc->dev, "%s: ep dma alloc failed\n",
+                               ep->name);
+                       return -ENOMEM;
+               }
+
+               ep_req->dma_aligned = 1;
+       } else if ((ep_req->usb_req.dma == DMA_ADDR_INVALID) ||
+                  (ep_req->usb_req.dma == 0)) {
+               ep_req->dma_mapped = 1;
+               ep_req->usb_req.dma = dma_map_single(
+                                       ep->udc->gadget.dev.parent,
+                                       ep_req->usb_req.buf,
+                                       (ep_req->usb_req.length ?
+                                       ep_req->usb_req.length : 1),
+               (ep->dirn == USB_DIR_IN ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+               if (dma_mapping_error(ep->udc->gadget.dev.parent,
+                                     ep_req->usb_req.dma)) {
+                       dev_err(ep->udc->gadget.dev.parent,
+                               "failed to map buffer\n");
+                       return -EFAULT;
+               }
+       }
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       ep_req->usb_req.status = -EINPROGRESS;
+       ep_req->usb_req.actual = 0;
+
+       if ((ep->type == USB_ENDPOINT_XFER_CONTROL) &&
+           (ep->dirn == USB_DIR_OUT) &&
+           (ep_req->usb_req.length == 0)) {
+               epreq_xfer_done(ep, ep_req, 0);
+       } else {
+               if (ep_req->usb_req.length == 0)
+                       ep_req->usb_req.zero = 1;
+
+               epreq_xfer_add(ep, ep_req);
+       }
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return 0;
+}
+
+static int snps_ep_dequeue(struct usb_ep *usb_ep,
+                          struct usb_request *usb_req)
+{
+       struct ep_xfer_req *ep_req;
+       struct snps_udc_ep *ep;
+       unsigned long flags;
+
+       ep = container_of(usb_ep, struct snps_udc_ep, usb_ep);
+       ep_req = container_of(usb_req, struct ep_xfer_req, usb_req);
+
+       if (!usb_ep || !usb_req) {
+               dev_err(ep->udc->dev, "%s: invalid dequeue request\n",
+                       ep->name);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&ep->udc->lock, flags);
+
+       list_for_each_entry(ep_req, &ep->queue, queue) {
+               if (&ep_req->usb_req == usb_req)
+                       break;
+       }
+
+       if (&ep_req->usb_req != usb_req) {
+               spin_unlock_irqrestore(&ep->udc->lock, flags);
+               dev_err(ep->udc->dev, "%s: request not queued\n", ep->name);
+               return -ENOLINK;
+       }
+
+       epreq_xfer_done(ep, ep_req, -ECONNRESET);
+       spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+       dev_dbg(ep->udc->dev, "%s: req=0x%p\n", ep->name, usb_req);
+       return 0;
+}
+
+static int snps_ep_set_halt(struct usb_ep *usb_ep, int halt)
+{
+       struct snps_udc_ep *ep;
+       unsigned long flags;
+       struct snps_udc *udc;
+
+       ep = container_of(usb_ep, struct snps_udc_ep, usb_ep);
+       udc = ep->udc;
+       if (!usb_ep) {
+               dev_err(udc->dev, "%s: invalid halt request\n", ep->name);
+               return -EINVAL;
+       }
+
+       if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+               dev_err(udc->dev, "%s: unsupported halt req\n", ep->name);
+               return -EOPNOTSUPP;
+       }
+
+       if (halt && (ep->dirn == USB_DIR_IN) &&
+           !list_empty(&ep->queue)) {
+               dev_err(udc->dev, "%s: EP IN queue not empty\n", ep->name);
+               return -EAGAIN;
+       }
+
+       if (!halt && (ep->type == USB_ENDPOINT_XFER_CONTROL)) {
+               dev_err(udc->dev, "%s: CTRL HALT clear\n", ep->name);
+               return -EPROTO;
+       }
+
+       spin_lock_irqsave(&ep->udc->lock, flags);
+
+       if (!halt) {
+               disable_ep_stall(udc->regs, ep->num, ep->dirn);
+       } else if (ep->type != USB_ENDPOINT_XFER_CONTROL) {
+               enable_ep_stall(udc->regs, ep->num, ep->dirn);
+       } else {
+               enable_ep_stall(udc->regs, ep->num, USB_DIR_IN);
+               enable_ep_stall(udc->regs, ep->num, USB_DIR_OUT);
+       }
+
+       spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+       dev_dbg(udc->dev, "%s: HALT %s done\n", ep->name,
+               halt ? "SET" : "CLR");
+
+       return 0;
+}
+
+static struct usb_ep_ops snps_ep_ops = {
+       .enable         = snps_ep_enable,
+       .disable        = snps_ep_disable,
+
+       .alloc_request  = snps_ep_alloc_request,
+       .free_request   = snps_ep_free_request,
+
+       .queue          = snps_ep_queue,
+       .dequeue        = snps_ep_dequeue,
+
+       .set_halt       = snps_ep_set_halt,
+};
+
+static int eps_init(struct snps_udc *udc)
+{
+       struct snps_udc_ep *ep;
+       int i, ret;
+
+       /* Initialize Endpoint 0 */
+       ep = &udc->ep[0];
+       ep->udc = udc;
+       ep->num = 0;
+       ep->in_xfer_done = true;
+       ep->dirn = USB_DIR_OUT;
+       ep->b_ep_addr = ep->num | ep->dirn;
+       strncpy(ep->name, "ep0", sizeof(ep->name));
+       ep->usb_ep.name = ep->name;
+       ep->max_pkt_size = EP_CTRL_MAX_PKT_SIZE;
+       usb_ep_set_maxpacket_limit(&ep->usb_ep, EP_CTRL_MAX_PKT_SIZE);
+       ep->usb_ep.ops = &snps_ep_ops;
+       ep->stopped = 0;
+       ep->usb_ep.caps.type_control = true;
+       ep->usb_ep.caps.dir_in = true;
+       ep->usb_ep.caps.dir_out = true;
+       INIT_LIST_HEAD(&ep->queue);
+       ep->type = USB_ENDPOINT_XFER_CONTROL;
+       ep->usb_ep.maxpacket = EP_CTRL_MAX_PKT_SIZE;
+
+       if (udc->conn_type)
+               ep_dma_init(ep);
+
+       dev_dbg(udc->dev, "%s: type: 0x%x, Dir:0x%x, Max Size: %d\n",
+               ep->name, ep->type, ep->dirn, ep->max_pkt_size);
+
+       /* Initialize remaining endpoints */
+       for (i = 1; i < UDC_MAX_EP; i++) {
+               ep = &udc->ep[i];
+               ep->udc = udc;
+               ep->max_pkt_size = EP_MAX_PKT_SIZE;
+               usb_ep_set_maxpacket_limit(&ep->usb_ep, EP_MAX_PKT_SIZE);
+               ep->usb_ep.ops = &snps_ep_ops;
+               ep->in_xfer_done = true;
+               ep->num = i;
+               if (i % 2) {
+                       snprintf(ep->name, sizeof(ep->name), "ep%din", i);
+                       ep->dirn = EP_DIRN_IN;
+                       ep->usb_ep.caps.dir_in = true;
+               } else {
+                       snprintf(ep->name, sizeof(ep->name), "ep%dout", i);
+                       ep->dirn = EP_DIRN_OUT;
+                       ep->usb_ep.caps.dir_out = true;
+               }
+               ep->usb_ep.name = ep->name;
+               ep->b_ep_addr = ep->num | ep->dirn;
+
+               ep->usb_ep.caps.type_iso = true;
+               ep->usb_ep.caps.type_bulk = true;
+               ep->usb_ep.caps.type_int = true;
+               ep->stopped = 0;
+               ep->usb_ep.maxpacket = EP_MAX_PKT_SIZE;
+
+               INIT_LIST_HEAD(&ep->queue);
+               if (udc->conn_type)
+                       ep_dma_init(ep);
+
+               dev_dbg(udc->dev, "%s: type: 0x%x, Dir: 0x%x, Max Size: %d\n",
+                       ep->name, ep->type, ep->dirn, ep->max_pkt_size);
+       }
+
+       udc->rx_fifo_space = OUT_RX_FIFO_MEM_SIZE;
+       udc->tx_fifo_space = IN_TX_FIFO_MEM_SIZE;
+       ret = ep_cfg(&udc->ep[0], USB_ENDPOINT_XFER_CONTROL,
+                    EP_CTRL_MAX_PKT_SIZE);
+       if (ret) {
+               dev_err(udc->dev, "Synopsys-UDC: error configuring 
endpoints\n");
+               return ret;
+       }
+
+       dev_dbg(udc->dev, "Synopsys UDC Endpoints initialized\n");
+       return 0;
+}
+
+static void start_udc(struct snps_udc *udc)
+{
+       int i;
+
+       init_udc_reg(udc->regs);
+
+       udc->rx_fifo_space = OUT_RX_FIFO_MEM_SIZE;
+       udc->tx_fifo_space = IN_TX_FIFO_MEM_SIZE;
+
+       eps_init(udc);
+       enable_self_pwr(udc->regs);
+
+       enable_udc_dev_irq(udc->regs, IRQ_SPEED_ENUM_DONE | IRQ_BUS_SUSPEND |
+                          IRQ_BUS_IDLE | IRQ_BUS_RESET | IRQ_SET_INTF |
+                          IRQ_SET_CFG);
+
+       for (i = 0; i < UDC_MAX_EP; ++i) {
+               if (udc->ep[i].usb_ep.name) {
+                       enable_udc_ep_irq(udc->regs,
+                                         udc->ep[i].num, USB_DIR_OUT);
+                       enable_udc_ep_irq(udc->regs,
+                                         udc->ep[i].num, USB_DIR_IN);
+               }
+       }
+
+       clear_devnak(udc->regs);
+       enable_ctrl_dma(udc->regs);
+       bus_connect(udc->regs);
+
+       dev_dbg(udc->dev, "Synopsys UDC started\n");
+}
+
+static void stop_udc(struct snps_udc *udc)
+{
+       finish_udc(udc->regs);
+
+       udc->gadget.speed = USB_SPEED_UNKNOWN;
+       epreq_queue_flush(&udc->ep[0], -ESHUTDOWN);
+       udc->ep[0].desc = NULL;
+
+       bus_disconnect(udc->regs);
+
+       if (udc->gadget_driver && udc->gadget_driver->disconnect) {
+               spin_unlock(&udc->lock);
+               udc->gadget_driver->disconnect(&udc->gadget);
+               spin_lock(&udc->lock);
+       }
+
+       dev_dbg(udc->dev, "Synopsys UDC stopped\n");
+}
+
+static int snps_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+       struct snps_udc *udc;
+       unsigned long flags;
+
+       udc = container_of(gadget, struct snps_udc, gadget);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       if (!udc->gadget_driver) {
+               spin_unlock_irqrestore(&udc->lock, flags);
+               return 0;
+       }
+
+       if (is_on && udc->pullup_on) {
+               start_udc(udc);
+               udc->ep[0].stopped = 0;
+               dev_info(udc->dev, "Synopsys UDC device connected\n");
+       } else if (!is_on && !udc->pullup_on) {
+               stop_udc(udc);
+               udc->ep[0].stopped = 1;
+               dev_info(udc->dev, "Synopsys UDC device Disconnected\n");
+       }
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return 0;
+}
+
+static int snps_gadget_start(struct usb_gadget *gadget,
+                            struct usb_gadget_driver *driver)
+{
+       struct snps_udc *udc;
+       unsigned long flags;
+
+       udc = container_of(gadget, struct snps_udc, gadget);
+
+       if (udc->gadget_driver)
+               return -EBUSY;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       driver->driver.bus = NULL;
+       udc->gadget_driver = driver;
+       udc->gadget.dev.driver = &driver->driver;
+       udc->ep[0].stopped = 0;
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       /* when cable is connected at boot time */
+       if (udc->conn_type)
+               schedule_delayed_work(&udc->drd_work, USBD_WQ_DELAY_MS);
+       dev_dbg(udc->dev, "%s: Done\n", __func__);
+
+       return 0;
+}
+
+static int snps_gadget_stop(struct usb_gadget *gadget)
+{
+       struct snps_udc_ep *ep;
+       struct snps_udc *udc;
+       unsigned long flags;
+
+       udc = container_of(gadget, struct snps_udc, gadget);
+
+       spin_lock_irqsave(&udc->lock, flags);
+       stop_udc(udc);
+       udc->gadget.dev.driver = NULL;
+       udc->gadget_driver = NULL;
+
+       list_for_each_entry(ep, &udc->gadget.ep_list, usb_ep.ep_list) {
+               epreq_queue_flush(ep, -ESHUTDOWN);
+               if (ep->desc)
+                       ep->desc = NULL;
+       }
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       dev_dbg(udc->dev, "%s: Done\n", __func__);
+
+       return 0;
+}
+
+static struct usb_gadget_ops snps_gadget_ops = {
+       .pullup         = snps_gadget_pullup,
+       .udc_start      = snps_gadget_start,
+       .udc_stop       = snps_gadget_stop,
+};
+
+void snps_udc_drd_work(struct work_struct *work)
+{
+       struct snps_udc *udc;
+
+       udc = container_of(to_delayed_work(work),
+                          struct snps_udc, drd_work);
+
+       if (udc->conn_type) {
+               dev_dbg(udc->dev, "idle -> device\n");
+               if (udc->gadget_driver) {
+                       udc->pullup_on = 1;
+                       snps_gadget_pullup(&udc->gadget, 1);
+               }
+       } else {
+               dev_dbg(udc->dev, "device -> idle\n");
+               udc->pullup_on = 0;
+               snps_gadget_pullup(&udc->gadget, 0);
+       }
+}
+
+static int usbd_connect_notify(struct notifier_block *self,
+                              unsigned long event, void *ptr)
+{
+       struct snps_udc *udc = container_of(self, struct snps_udc, nb);
+
+       dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
+
+       udc->conn_type = event;
+
+       schedule_delayed_work(&udc->drd_work, USBD_WQ_DELAY_MS);
+
+       return NOTIFY_OK;
+}
+
+static void free_udc_dma(struct platform_device *pdev, struct snps_udc *udc)
+{
+       u32 num;
+
+       dma_free_coherent(&pdev->dev, sizeof(struct ep_desc_array),
+                         udc->dma.virt, (dma_addr_t)udc->dma.phys);
+
+       for (num = 0; num < UDC_MAX_EP; num++) {
+               if (udc->ep[num].dma.aligned_buf) {
+                       dma_free_coherent(NULL, udc->ep[num].dma.aligned_len,
+                                         udc->ep[num].dma.aligned_buf,
+                                         udc->ep[num].dma.aligned_addr);
+                       udc->ep[num].dma.aligned_buf = NULL;
+               }
+       }
+}
+
+static int snps_udc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct snps_udc *udc;
+       int i, ret;
+
+       udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+       if (!udc)
+               return -ENOMEM;
+
+       spin_lock_init(&udc->lock);
+       udc->dev = dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       udc->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(udc->regs))
+               return PTR_ERR(udc->regs);
+
+       udc->irq = irq_of_parse_and_map(dev->of_node, 0);
+       if (udc->irq <= 0) {
+               dev_err(dev, "Can't parse and map interrupt\n");
+               return -EINVAL;
+       }
+
+       udc->udc_phy = devm_phy_get(dev, "usb2drd");
+       if (IS_ERR(udc->udc_phy)) {
+               dev_err(dev, "Failed to obtain phy from device tree\n");
+               return PTR_ERR(udc->udc_phy);
+       }
+
+       ret = phy_init(udc->udc_phy);
+       if (ret) {
+               dev_err(dev, "UDC phy init failed");
+               return ret;
+       }
+
+       ret = phy_power_on(udc->udc_phy);
+       if (ret) {
+               dev_err(dev, "UDC phy power on failed");
+               phy_exit(udc->udc_phy);
+               return ret;
+       }
+
+       udc->edev = extcon_get_edev_by_phandle(dev, 0);
+       if (IS_ERR(udc->edev)) {
+               if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               dev_err(dev, "Invalid or missing extcon\n");
+               ret = PTR_ERR(udc->edev);
+               goto exit_phy;
+       }
+
+       udc->nb.notifier_call = usbd_connect_notify;
+       ret = extcon_register_notifier(udc->edev, EXTCON_USB, &udc->nb);
+       if (ret < 0) {
+               dev_err(dev, "Can't register extcon device\n");
+               goto exit_phy;
+       }
+
+       ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
+       if (ret < 0) {
+               dev_err(dev, "Can't get cable state\n");
+               goto exit_extcon;
+       } else if (ret) {
+               udc->conn_type = ret;
+       }
+
+       udc->dma.virt = dma_alloc_coherent(&pdev->dev,
+                               sizeof(struct ep_desc_array),
+                               (dma_addr_t *)&udc->dma.phys,
+                                GFP_KERNEL);
+       if (!udc->dma.virt) {
+               dev_err(dev, "Failed to allocate memory for ep\n");
+               ret = -ENOMEM;
+               goto exit_extcon;
+       }
+
+       INIT_DELAYED_WORK(&udc->drd_work, snps_udc_drd_work);
+
+       ret = devm_request_irq(dev, udc->irq, snps_udc_irq, IRQF_SHARED,
+                              "snps-udc", udc);
+       if (ret < 0) {
+               dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
+               goto exit_dma;
+       }
+
+       /* Gagdet structure init */
+       udc->gadget.name        = "snps-udc";
+       udc->gadget.speed       = USB_SPEED_UNKNOWN;
+       udc->gadget.max_speed   = USB_SPEED_HIGH;
+       udc->gadget.ops         = &snps_gadget_ops;
+       udc->gadget.ep0         = &udc->ep[0].usb_ep;
+       INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+       eps_init(udc);
+       for (i = 1; i < UDC_MAX_EP; i++) {
+               list_add_tail(&udc->ep[i].usb_ep.ep_list,
+                             &udc->gadget.ep_list);
+       }
+
+       ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+       if (ret) {
+               dev_err(dev, "Error adding gadget udc: %d\n", ret);
+               goto exit_dma;
+       }
+
+       platform_set_drvdata(pdev, udc);
+       dev_info(dev, "Synopsys UDC driver probe successful\n");
+
+       return 0;
+exit_dma:
+       free_udc_dma(pdev, udc);
+exit_extcon:
+       extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
+exit_phy:
+       phy_power_off(udc->udc_phy);
+       phy_exit(udc->udc_phy);
+
+       return ret;
+}
+
+static int snps_udc_remove(struct platform_device *pdev)
+{
+       struct snps_udc *udc;
+
+       udc = platform_get_drvdata(pdev);
+
+       usb_del_gadget_udc(&udc->gadget);
+
+       platform_set_drvdata(pdev, NULL);
+
+       if (udc->drd_wq) {
+               flush_workqueue(udc->drd_wq);
+               destroy_workqueue(udc->drd_wq);
+       }
+
+       free_udc_dma(pdev, udc);
+       phy_power_off(udc->udc_phy);
+       phy_exit(udc->udc_phy);
+       extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
+
+       dev_info(&pdev->dev, "Synopsys UDC driver removed\n");
+
+       return 0;
+}
+
+static void snps_udc_shutdown(struct platform_device *pdev)
+{
+       struct snps_udc *udc = platform_get_drvdata(pdev);
+
+       snps_gadget_stop(&udc->gadget);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snps_udc_suspend(struct device *dev)
+{
+       struct snps_udc *udc;
+
+       udc = dev_get_drvdata(dev);
+
+       if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+               dev_dbg(udc->dev, "device -> idle\n");
+               snps_gadget_pullup(&udc->gadget, 0);
+       }
+       phy_power_off(udc->udc_phy);
+       phy_exit(udc->udc_phy);
+
+       return 0;
+}
+
+static int snps_udc_resume(struct device *dev)
+{
+       struct snps_udc *udc;
+       int ret;
+
+       udc = dev_get_drvdata(dev);
+
+       ret = phy_init(udc->udc_phy);
+       if (ret) {
+               dev_err(udc->dev, "UDC phy init failure");
+               return ret;
+       }
+
+       ret = phy_power_on(udc->udc_phy);
+       if (ret) {
+               dev_err(udc->dev, "UDC phy power on failure");
+               phy_exit(udc->udc_phy);
+               return ret;
+       }
+
+       if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
+               dev_dbg(udc->dev, "idle -> device\n");
+               snps_gadget_pullup(&udc->gadget, 1);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops snps_udc_pm_ops = {
+       .suspend        = snps_udc_suspend,
+       .resume         = snps_udc_resume,
+};
+#endif
+
+static const struct of_device_id of_udc_match[] = {
+       { .compatible = "snps,dw-ahb-udc", },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, of_udc_match);
+
+static struct platform_driver snps_udc_driver = {
+       .probe          = snps_udc_probe,
+       .remove         = snps_udc_remove,
+       .shutdown       = snps_udc_shutdown,
+       .driver         = {
+               .name   = "snps-udc",
+               .of_match_table = of_match_ptr(of_udc_match),
+#ifdef CONFIG_PM_SLEEP
+               .pm     = &snps_udc_pm_ops,
+#endif
+       },
+};
+
+module_platform_driver(snps_udc_driver);
+
+MODULE_ALIAS("platform:snps-udc");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/udc/snps_udc.h 
b/drivers/usb/gadget/udc/snps_udc.h
new file mode 100644
index 0000000..0355d59
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc.h
@@ -0,0 +1,1071 @@
+/*
+ * Copyright (C) 2016 Broadcom
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SNPS_UDC_H
+#define __SNPS_UDC_H
+
+/* UDC speeds */
+#define SPEED_UNKNOWN                  (0)
+#define SPEED_LOW                      (1)
+#define SPEED_FULL                     (2)
+#define SPEED_HIGH                     (3)
+
+/* Endpoint directions */
+#define EP_DIRN_IN                     (0x80)
+#define EP_DIRN_OUT                    (0x00)
+#define EP_DIRN_MASK                   (0x80)
+
+/* Endpoint types */
+#define EP_TYPE_CTRL                   (0)
+#define EP_TYPE_ISOC                   (1)
+#define EP_TYPE_BULK                   (2)
+#define EP_TYPE_INTR                   (3)
+#define EP_TYPE_MASK                   (0x03)
+
+/* Max supported endpoints */
+#define UDC_MAX_EP                     (10)
+
+#define EP_MAX_PKT_SIZE                        512
+#define EP_CTRL_MAX_PKT_SIZE           64
+#define OUT_RX_FIFO_MEM_SIZE           4096
+#define IN_TX_FIFO_MEM_SIZE            4096
+
+#define is_ep_in()                     ((ep->dirn) == USB_DIR_IN)
+#define is_ep_out()                    ((ep->dirn) == USB_DIR_OUT)
+#define is_ep_bulk()                   ((ep->type) == USB_ENDPOINT_XFER_BULK)
+
+#define DESC_CNT (1)
+
+#define EP_DMA_DESC_IDX_MASK           (DESC_CNT - 1)
+#define EP_DMA_DESC_IDX(num)           ((num) & EP_DMA_DESC_IDX_MASK)
+
+#define USB_MODE_IDLE                  (1)
+#define USB_MODE_DEVICE                        (2)
+
+#define FIFO_SZ_U32(pkt_sz)            (((pkt_sz) + 3) / sizeof(u32))
+#define FIFO_SZ_U8(sz)                 (FIFO_SZ_U32(sz) * sizeof(u32))
+#define USBD_WQ_DELAY_MS               msecs_to_jiffies(100)
+/* Register Masks and definitions */
+
+/* Endpoint Control Registers*/
+#define EP_CTRL_OUT_FLUSH_ENABLE       BIT(12)
+#define EP_CTRL_OUT_CLOSE_DESC         BIT(11)
+#define EP_CTRL_IN_SEND_NULL           BIT(10)
+#define EP_CTRL_OUT_DMA_ENABLE         BIT(9)
+#define EP_CTRL_NAK_CLEAR              BIT(8)
+#define EP_CTRL_NAK_SET                        BIT(7)
+#define EP_CTRL_NAK_IN_PROGRESS                BIT(6)
+#define EP_CTRL_TYPE_SHIFT             (4)
+#define EP_CTRL_TYPE_MASK              (3 << EP_CTRL_TYPE_SHIFT)
+#define EP_CTRL_IN_DMA_ENABLE          BIT(3)
+#define EP_CTRL_SNOOP_ENABLE           BIT(2)
+#define EP_CTRL_IN_FLUSH_ENABLE                BIT(1)
+#define EP_CTRL_STALL_ENABLE           BIT(0)
+
+/* Endpoint Status Registers */
+#define EP_STS_CLOSE_DESC_CLEAR                BIT(28)
+#define EP_STS_IN_XFER_DONE            BIT(27)
+#define EP_STS_STALL_SET_RX            BIT(26)
+#define EP_STS_STALL_CLEAR_RX          BIT(25)
+#define EP_STS_IN_FIFO_EMPTY           BIT(24)
+#define EP_STS_IN_DMA_DONE             BIT(10)
+#define EP_STS_AHB_BUS_ERROR           BIT(9)
+#define EP_STS_OUT_FIFO_EMPTY          BIT(8)
+#define EP_STS_DMA_BUF_NOT_AVAIL       BIT(7)
+#define EP_STS_IN_TOKEN_RX             BIT(6)
+#define EP_STS_OUT_DMA_SETUP_DONE      BIT(5)
+#define EP_STS_OUT_DMA_DATA_DONE       BIT(4)
+
+/* Buffer Regs for EP In, Receive Packet Frame Num Regs for EP Out */
+#define EP_REG2_OUT_ISOC_PID_SHIFT     (16)
+#define EP_REG2_OUT_ISOC_PID_MASK      (3 << EP_REG2_OUT_ISOC_PID_SHIFT)
+#define EP_REG2_IN_DEPTH_SHIFT         (0)
+#define EP_REG2_IN_DEPTH_MASK          (0xffff << EP_REG2_IN_DEPTH_SHIFT)
+#define EP_REG2_OUT_FRAME_NUM_SHIFT    EP_REG2_IN_DEPTH_SHIFT
+#define EP_REG2_OUT_FRAME_NUM_MASK     EP_REG2_IN_DEPTH_MASK
+
+/* Max Packet Size Regs for EP In, Buffer Size Regs for EP Out */
+#define EP_REG3_OUT_DEPTH_SHIFT                (16)
+#define EP_REG3_OUT_DEPTH_MASK         (0xffff << EP_REG3_OUT_DEPTH_SHIFT)
+#define EP_REG3_PKT_MAX_SHIFT          (0)
+#define EP_REG3_PKT_MAX_MASK           (0xffff << EP_REG3_PKT_MAX_SHIFT)
+
+/* Endpoint Config Registers */
+#define EP_CFG_DIRN_IN                 BIT(4)
+#define EP_CFG_DIRN_OUT                        (0)
+#define EP_CFG_PKT_MAX_SHIFT           (19)
+#define EP_CFG_PKT_MAX_MASK            (0x7ff << EP_CFG_PKT_MAX_SHIFT)
+#define EP_CFG_ALT_NUM_SHIFT           (15)
+#define EP_CFG_ALT_NUM_MASK            (0xf << EP_CFG_ALT_NUM_SHIFT)
+#define EP_CFG_INTF_NUM_SHIFT          (11)
+#define EP_CFG_INTF_NUM_MASK           (0xf << EP_CFG_INTF_NUM_SHIFT)
+#define EP_CFG_CFG_NUM_SHIFT           (7)
+#define EP_CFG_CFG_NUM_MASK            (0xf << EP_CFG_CFG_NUM_SHIFT)
+#define EP_CFG_TYPE_SHIFT              (5)
+#define EP_CFG_TYPE_MASK               (0x3 << EP_CFG_TYPE_SHIFT)
+#define EP_CFG_FIFO_NUM_SHIFT          (0)
+#define EP_CFG_FIFO_NUM_MASK           (0xf << EP_CFG_FIFO_NUM_SHIFT)
+
+/* Endpoint Interrupt Registers */
+#define EP_INTR_OUT_SHIFT              (16)
+#define EP_INTR_OUT_MASK               (0xffff << EP_INTR_OUT_SHIFT)
+#define EP_INTR_IN_SHIFT               (0)
+#define EP_INTR_IN_MASK                        (0xffff << EP_INTR_IN_SHIFT)
+
+/* Device Config Register */
+#define CFG_ULPI_DDR_ENABLE            BIT(19)
+#define CFG_SET_DESCRIPTOR_ENABLE      BIT(18)
+#define CFG_CSR_PROGRAM_ENABLE         BIT(17)
+#define CFG_HALT_STALL_ENABLE          BIT(16)
+#define CFG_HS_TIMEOUT_CALIB_SHIFT     (13)
+#define CFG_HS_TIMEOUT_CALIB_MASK      (7 << CFG_HS_TIMEOUT_CALIB_SHIFT)
+#define CFG_FS_TIMEOUT_CALIB_SHIFT     (10)
+#define CFG_FS_TIMEOUT_CALIB_MASK      (7 << CFG_FS_TIMEOUT_CALIB_SHIFT)
+#define CFG_STS_1_ENABLE               BIT(8)
+#define CFG_STS_ENABLE                 BIT(7)
+#define CFG_UTMI_BI_DIRN_ENABLE                BIT(6)
+#define CFG_UTMI_8BIT_ENABLE           BIT(5)
+#define CFG_SYNC_FRAME_ENABLE          BIT(4)
+#define CFG_SELF_PWR_ENABLE            BIT(3)
+#define CFG_REMOTE_WAKEUP_ENABLE       BIT(2)
+#define CFG_SPD_SHIFT                  (0)
+#define CFG_SPD_MASK                   (3 << CFG_SPD_SHIFT)
+#define CFG_SPD_HS                     (0 << CFG_SPD_SHIFT)
+#define CFG_SPD_FS                     BIT(0)
+#define CFG_SPD_LS                     (2 << CFG_SPD_SHIFT)
+#define CFG_SPD_FS_48MHZ               (3 << CFG_SPD_SHIFT)
+
+/* Device Control Register*/
+#define CTRL_DMA_OUT_THRESH_LEN_SHIFT  (24)
+#define CTRL_DMA_OUT_THRESH_LEN_MASK   (0xff << CTRL_DMA_OUT_THRESH_LEN_SHIFT)
+#define CTRL_DMA_BURST_LEN_SHIFT       (16)
+#define CTRL_DMA_BURST_LEN_MASK                (0xff << 
CTRL_DMA_BURST_LEN_SHIFT)
+#define CTRL_OUT_FIFO_FLUSH_ENABLE     BIT(14)
+#define CTRL_CSR_DONE                  BIT(13)
+#define CTRL_OUT_ALL_NAK               BIT(12)
+#define CTRL_DISCONNECT_ENABLE         BIT(10)
+#define CTRL_DMA_MODE_ENABLE           BIT(9)
+#define CTRL_DMA_BURST_ENABLE          BIT(8)
+#define CTRL_DMA_OUT_THRESH_ENABLE     BIT(7)
+#define CTRL_DMA_BUFF_FILL_MODE_ENABLE BIT(6)
+#define CTRL_ENDIAN_BIG_ENABLE         BIT(5)
+#define CTRL_DMA_DESC_UPDATE_ENABLE    BIT(4)
+#define CTRL_DMA_IN_ENABLE             BIT(3)
+#define CTRL_DMA_OUT_ENABLE            BIT(2)
+#define CTRL_RESUME_SIGNAL_ENABLE      BIT(0)
+#define CTRL_LE_ENABLE                 (0)
+
+/* Device Status Register */
+#define STS_SOF_FRAME_NUM_SHIFT                (18)
+#define STS_SOF_FRAME_NUM_MASK         (0x3ffff << STS_SOF_FRAME_NUM_SHIFT)
+#define STS_REMOTE_WAKEUP_ALLOWED      BIT(17)
+#define STS_PHY_ERROR                  BIT(16)
+#define STS_OUT_FIFO_EMPTY             BIT(15)
+#define STS_SPD_SHIFT                  (13)
+#define STS_SPD_MASK                   (3 << STS_SPD_SHIFT)
+#define STS_SPD_HS                     (0 << STS_SPD_SHIFT)
+#define STS_SPD_FS                     BIT(13)
+#define STS_SPD_LS                     (2 << STS_SPD_SHIFT)
+#define STS_SPD_FS_48MHZ               (3 << STS_SPD_SHIFT)
+#define STS_BUS_SUSPENDED              BIT(12)
+#define STS_ALT_NUM_SHIFT              (8)
+#define STS_ALT_NUM_MASK               (0xf << STS_SPD_SHIFT)
+#define STS_INTF_NUM_SHIFT             (4)
+#define STS_INTF_NUM_MASK              (0xf << STS_INTF_NUM_SHIFT)
+#define STS_CFG_NUM_SHIFT              (0)
+#define STS_CFG_NUM_MASK               (0xf << STS_CFG_NUM_SHIFT)
+
+/* Device Interrupt Register */
+#define INTR_REMOTE_WAKEUP_DELTA       BIT(7)
+#define INTR_SPD_ENUM_DONE             BIT(6)
+#define INTR_SOF_RX                    BIT(5)
+#define INTR_BUS_SUSPEND               BIT(4)
+#define INTR_BUS_RESET                 BIT(3)
+#define INTR_BUS_IDLE                  BIT(2)
+#define INTR_SET_INTF_RX               BIT(1)
+#define INTR_SET_CFG_RX                        BIT(0)
+
+#define DMA_STS_BUF_SHIFT              (30)
+#define DMA_STS_BUF_HOST_READY         (0 << DMA_STS_BUF_SHIFT)
+#define DMA_STS_BUF_DMA_BUSY           BIT(30)
+#define DMA_STS_BUF_DMA_DONE           (2 << DMA_STS_BUF_SHIFT)
+#define DMA_STS_BUF_HOST_BUSY          (3 << DMA_STS_BUF_SHIFT)
+#define DMA_STS_BUF_MASK               (3 << DMA_STS_BUF_SHIFT)
+#define DMA_STS_RX_SHIFT               (28)
+#define DMA_STS_RX_SUCCESS             (0 << DMA_STS_RX_SHIFT)
+#define DMA_STS_RX_ERR_DESC            BIT(28)
+#define DMA_STS_RX_ERR_BUF             (3 << DMA_STS_RX_SHIFT)
+#define DMA_STS_RX_MASK                        (3 << DMA_STS_RX_SHIFT)
+#define DMA_STS_CFG_NUM_SHIFT          (24)
+#define DMA_STS_CFG_NUM_MASK           (0xf << DMA_STS_CFG_NUM_SHIFT)
+#define DMA_STS_INTF_NUM_SHIFT         (20)
+#define DMA_STS_INTF_NUM_MASK          (0xf << DMA_STS_INTF_NUM_SHIFT)
+#define DMA_STS_LAST_DESC              BIT(27)
+#define DMA_STS_FRAME_NUM_SHIFT                (16)
+#define DMA_STS_FRAME_NUM_MASK         (0x7ff << DMA_STS_FRAME_NUM_SHIFT)
+#define DMA_STS_BYTE_CNT_SHIFT         (0)
+#define DMA_STS_ISO_PID_SHIFT          (14)
+#define DMA_STS_ISO_PID_MASK           (0x3 << DMA_STS_ISO_PID_SHIFT)
+#define DMA_STS_ISO_BYTE_CNT_SHIFT     (DMA_STS_BYTE_CNT_SHIFT)
+#define DMA_STS_ISO_BYTE_CNT_MASK      (0x3fff << DMA_STS_ISO_BYTE_CNT_SHIFT)
+#define DMA_STS_NISO_BYTE_CNT_SHIFT    (DMA_STS_BYTE_CNT_SHIFT)
+#define DMA_STS_NISO_BYTE_CNT_MASK     (0xffff << DMA_STS_NISO_BYTE_CNT_SHIFT)
+
+/* UDC Interrupts */
+#define UDC_IRQ_ALL                    (IRQ_REMOTEWAKEUP_DELTA | \
+                                       IRQ_SPEED_ENUM_DONE | \
+                                       IRQ_BUS_SUSPEND | \
+                                       IRQ_BUS_RESET | \
+                                       IRQ_BUS_IDLE | \
+                                       IRQ_SET_INTF | \
+                                       IRQ_SET_CFG)
+#define IRQ_REMOTEWAKEUP_DELTA         INTR_REMOTE_WAKEUP_DELTA
+#define IRQ_SPEED_ENUM_DONE            INTR_SPD_ENUM_DONE
+#define IRQ_SOF_DETECTED               INTR_SOF_RX
+#define IRQ_BUS_SUSPEND                        INTR_BUS_SUSPEND
+#define IRQ_BUS_RESET                  INTR_BUS_RESET
+#define IRQ_BUS_IDLE                   INTR_BUS_IDLE
+#define IRQ_SET_INTF                   INTR_SET_INTF_RX
+#define IRQ_SET_CFG                    INTR_SET_CFG_RX
+
+/* Endpoint status */
+#define EP_STS_ALL                     (DMA_ERROR | \
+                                       DMA_BUF_NOT_AVAIL | \
+                                       IN_TOKEN_RX | \
+                                       IN_DMA_DONE | \
+                                       IN_XFER_DONE | \
+                                       OUT_DMA_DATA_DONE | \
+                                       OUT_DMA_SETUP_DONE)
+
+#define DMA_ERROR                      EP_STS_AHB_BUS_ERROR
+#define DMA_BUF_NOT_AVAIL              EP_STS_DMA_BUF_NOT_AVAIL
+#define IN_TOKEN_RX                    EP_STS_IN_TOKEN_RX
+#define IN_DMA_DONE                    EP_STS_IN_DMA_DONE
+#define IN_FIFO_EMPTY                  EP_STS_IN_FIFO_EMPTY
+#define IN_XFER_DONE                   EP_STS_IN_XFER_DONE
+#define OUT_DMA_DATA_DONE              EP_STS_OUT_DMA_DATA_DONE
+#define OUT_DMA_SETUP_DONE             EP_STS_OUT_DMA_SETUP_DONE
+
+#define DMA_ADDR_INVALID       (~(dma_addr_t)0)
+#define DIRN_STR(dirn)         ((dirn) == USB_DIR_IN ? "IN" : "OUT")
+#define EP_DIRN_TYPE(d, t)     (((d) << 8) | (t))
+
+/* Used for ISOC IN transfers for frame alignment. */
+#define FRAME_NUM_INVALID      (~(u32)0)
+
+/* UDC config parameters */
+
+/* If multiple RX FIFO controllers are implemented for
+ * OUT Endpoints, MRX_FIFO is enabled.
+ * Multi RX FIFO controllers are not implemented in RTL.
+ */
+#define MRX_FIFO 0
+#if MRX_FIFO
+static bool mrx_fifo = true;
+#else
+static bool mrx_fifo;
+#endif
+
+/* Buffer Fill mode is enabled for IN transfers,
+ * disabled for OUT transfers.
+ */
+#define IN_DMA_BUF_FILL_EN 1
+#if IN_DMA_BUF_FILL_EN
+static bool in_bf_mode = true;
+#else
+static bool in_bf_mode;
+#endif
+
+#define OUT_DMA_BUF_FILL_EN 0
+#if OUT_DMA_BUF_FILL_EN
+static bool out_bf_mode = true;
+#else
+static bool out_bf_mode;
+#endif
+/*
+ * If it desired that frames start being DMA'd w/o frame
+ * alignment, define ISOC_IN_XFER_DELAY_DISABLE.
+ * If frame alignment is used, this delay is not disabled.
+ */
+#define ISOC_IN_XFER_DELAY_DISABLE 0
+#if ISOC_IN_XFER_DELAY_DISABLE
+static bool in_isoc_delay_disabled = true;
+#else
+static bool in_isoc_delay_disabled;
+#endif
+
+/* Endpoint IN/OUT registers
+ * Register space is reserved for 16 endpoints, but the controller
+ * actually supports 10 endpoints only.
+ */
+#define EP_CNT (16)
+struct snps_ep_regs {
+       u32 ctrl;       /* EP control */
+       u32 status;     /* EP status */
+       u32 epreg2;     /* Buffer for IN, Rec Pkt Frame num for OUT */
+       u32 epreg3;     /* Max pkt size for IN, Buf size for OUT */
+       u32 setupbuf;   /* Rsvd for IN, EP setup buffer ptr for OUT */
+       u32 datadesc;   /* EP data descriptor pointer */
+       u32 rsvd[2];
+};
+
+/* UDC registers */
+struct snps_udc_regs {
+       struct snps_ep_regs ep_in[EP_CNT];
+       struct snps_ep_regs ep_out[EP_CNT];
+       u32 devcfg;
+       u32 devctrl;
+       u32 devstatus;
+       u32 devintrstat;
+       u32 devintrmask;
+       u32 epintrstat;
+       u32 epintrmask;
+       u32 testmode;
+       u32 releasenum;
+       u32 rsvd[56];
+       u32 epcfg[EP_CNT];
+       u32 rsvd1[175];
+       u32 rx_fifo[256];
+       u32 tx_fifo[256];
+       u32 strap;
+};
+
+/* Endpoint SETUP buffer */
+struct setup_desc {
+       u32 status;
+       u32 reserved;
+       u32 data1;
+       u32 data2;
+};
+
+/* Endpoint In/Out data descriptor */
+struct data_desc {
+       u32 status;
+       u32 reserved;
+       u32 buf_addr;
+       u32 next_desc_addr;
+};
+
+/* Endpoint descriptor layout. */
+struct ep_dma_desc {
+       struct setup_desc setup;
+       struct data_desc  desc[DESC_CNT];
+};
+
+/* Endpoint descriptor array for Synopsys UDC */
+struct ep_desc_array {
+       struct ep_dma_desc ep[UDC_MAX_EP];
+};
+
+struct snps_udc;
+
+/* Endpoint data structure (for each endpoint) */
+struct snps_udc_ep {
+       struct usb_ep usb_ep;
+       const struct usb_endpoint_descriptor *desc;
+       struct list_head queue;
+       struct snps_udc *udc;
+       char name[14];
+       bool in_xfer_done;
+       u32 num;
+       u32 dirn;
+       u32 type;                       /* USB_ENDPOINT_XFER_xxx */
+       u32 b_ep_addr;                  /* dirn | type */
+       u32 max_pkt_size;
+       u32 rx_fifo_size;               /* Rx FIFO ram allocated */
+       u32 tx_fifo_size;               /* Tx FIFO ram allocated */
+       u32 stopped:1;
+       struct {
+               struct ep_dma_desc *virt;
+               struct ep_dma_desc *phys;
+               struct usb_request *usb_req;/* Current request being DMA'd */
+               u32 len_max;            /* to use with a descriptor */
+               u32 len_done;           /* Length of request DMA'd so far */
+               u32 len_rem;            /* Length of request left to DMA */
+               u32 add_idx;            /* descriptor chain index */
+               u32 remove_idx;         /* descriptor chain index */
+               u32 buf_addr;           /* Location in request to DMA */
+               u32 frame_num;          /* Frame number for ISOC transfers */
+               u32 frame_incr;         /* Frame number increment (period) */
+               u32 status;
+               u32 done;               /* DMA/USB xfer completion indication */
+               void *aligned_buf;      /* used if usb_req buf not aligned */
+               dma_addr_t aligned_addr;/* Aligned buffer physical address */
+               u32 aligned_len;        /* Aligned buffer length */
+               u32 last;
+       } dma;
+};
+
+/* Endpoint xfer request structure */
+struct ep_xfer_req {
+       struct usb_request      usb_req;
+       struct list_head        queue;
+       dma_addr_t              dma_addr_orig;
+       u32                     dma_mapped:1;
+       u32                     dma_aligned:1;
+};
+
+/* Controller data structure */
+struct snps_udc {
+       struct usb_gadget               gadget;
+       struct usb_gadget_driver        *gadget_driver;
+       struct device                   *dev;
+       void __iomem                    *regs;
+       int                             irq;
+       struct completion               *dev_release;
+       spinlock_t                      lock; /* UDC spin lock variable */
+       u32                             rx_fifo_space;
+       u32                             tx_fifo_space;
+       struct snps_udc_ep              ep[UDC_MAX_EP];
+       struct {
+               struct ep_desc_array    *virt;
+               struct ep_desc_array    *phys;
+       } dma;
+       struct gpio_desc                *vbus_gpiod;
+       u32                             vbus_active:1;
+       u32                             pullup_on:1;
+       struct phy                      *udc_phy;
+       u32                             mode;
+       struct extcon_dev               *edev;
+       struct extcon_specific_cable_nb extcon_nb;
+       struct notifier_block           nb;
+       struct delayed_work             drd_work;
+       struct workqueue_struct         *drd_wq;
+       u32                             conn_type;
+};
+
+#define REG_WR(reg, val)               writel(val, &reg)
+#define REG_MOD_AND(reg, val)          writel(val & readl(&reg), &reg)
+#define REG_MOD_OR(reg, val)           writel(val | readl(&reg), &reg)
+#define REG_MOD_MASK(reg, mask, val)   writel(val | (mask & readl(&reg)), &reg)
+#define REG_RD(reg)                    readl(&reg)
+
+static inline void dump_regs(struct snps_udc_regs *regs)
+{
+       pr_debug("DEVCFG: 0x%x\n", REG_RD(regs->devcfg));
+       pr_debug("DEVCTRL: 0x%x\n", REG_RD(regs->devctrl));
+       pr_debug("DEVSTS: 0x%x\n", REG_RD(regs->devstatus));
+       pr_debug("DEVINTRMASK: 0x%x\n", REG_RD(regs->devintrmask));
+       pr_debug("DEVINTRSTS: 0x%x\n", REG_RD(regs->devintrstat));
+       pr_debug("EPINTRMASK: 0x%x\n", REG_RD(regs->epintrmask));
+       pr_debug("EPINTRSTS: 0x%x\n", REG_RD(regs->epintrstat));
+}
+
+static inline void bus_connect(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devctrl, ~CTRL_DISCONNECT_ENABLE);
+}
+
+static inline void bus_disconnect(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devctrl, CTRL_DISCONNECT_ENABLE);
+}
+
+static inline bool is_bus_suspend(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devstatus) &
+               STS_BUS_SUSPENDED ? true : false;
+}
+
+static inline u32 get_alt_num(struct snps_udc_regs *regs)
+{
+       return (REG_RD(regs->devstatus) & STS_ALT_NUM_MASK)
+                                               >> STS_ALT_NUM_SHIFT;
+}
+
+static inline u32 get_cfg_num(struct snps_udc_regs *regs)
+{
+       return (REG_RD(regs->devstatus) & STS_CFG_NUM_MASK)
+                                               >> STS_CFG_NUM_SHIFT;
+}
+
+static inline u32 get_intf_num(struct snps_udc_regs *regs)
+{
+       return (REG_RD(regs->devstatus) & STS_INTF_NUM_MASK)
+                                               >> STS_INTF_NUM_SHIFT;
+}
+
+static inline void disable_ctrl_dma(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devctrl, ~(CTRL_DMA_IN_ENABLE |
+                                            CTRL_DMA_OUT_ENABLE));
+}
+
+static inline void enable_ctrl_dma(struct snps_udc_regs *regs)
+{
+               REG_MOD_OR(regs->devctrl, (CTRL_DMA_IN_ENABLE |
+                                          CTRL_DMA_OUT_ENABLE));
+}
+
+static inline bool is_ctrl_dma_enable(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devctrl) &
+                               CTRL_DMA_OUT_ENABLE ? true : false;
+}
+
+static inline void disable_epin_dma(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devctrl, ~(CTRL_DMA_IN_ENABLE));
+}
+
+static inline void enable_epin_dma(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devctrl, (CTRL_DMA_IN_ENABLE));
+}
+
+static inline bool is_epin_dma_enable(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devctrl) &
+                               CTRL_DMA_IN_ENABLE ? true : false;
+}
+
+static inline void disable_epout_dma(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devctrl, ~(CTRL_DMA_OUT_ENABLE));
+}
+
+static inline void enable_epout_dma(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devctrl, (CTRL_DMA_OUT_ENABLE));
+}
+
+static inline bool is_epout_dma_enable(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devctrl) &
+                               CTRL_DMA_OUT_ENABLE ? true : false;
+}
+
+static inline u32 get_frnum_last_rx(struct snps_udc_regs *regs)
+{
+       return (REG_RD(regs->devstatus) &
+               STS_SOF_FRAME_NUM_MASK) >> STS_SOF_FRAME_NUM_SHIFT;
+}
+
+static inline u32 get_irq_active(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devintrstat);
+}
+
+static inline void clear_udc_dev_irq(struct snps_udc_regs *regs, u32 mask)
+{
+       REG_WR(regs->devintrstat, mask);
+}
+
+static inline void disable_udc_dev_irq(struct snps_udc_regs *regs, u32 mask)
+{
+       REG_MOD_OR(regs->devintrmask, mask);
+}
+
+static inline void enable_udc_dev_irq(struct snps_udc_regs *regs, u32 mask)
+{
+       REG_MOD_AND(regs->devintrmask, ~mask);
+}
+
+static inline u32 mask_irq(struct snps_udc_regs *regs)
+{
+       return (~REG_RD(regs->devintrmask)) & UDC_IRQ_ALL;
+}
+
+static inline void clear_devnak(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devctrl, ~CTRL_OUT_ALL_NAK);
+}
+
+static inline void set_devnak(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devctrl, CTRL_OUT_ALL_NAK);
+}
+
+static inline bool is_phy_error(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devstatus) &
+               STS_PHY_ERROR ? true : false;
+}
+
+static inline bool is_rmtwkp(struct snps_udc_regs *regs)
+{
+       return REG_RD(regs->devstatus) &
+               STS_REMOTE_WAKEUP_ALLOWED ? true : false;
+}
+
+static inline void clear_rmtwkup(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devcfg, ~CFG_REMOTE_WAKEUP_ENABLE);
+}
+
+static inline void set_rmtwkp(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devcfg, CFG_REMOTE_WAKEUP_ENABLE);
+}
+
+static inline void start_rmtwkp(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devctrl, CTRL_RESUME_SIGNAL_ENABLE);
+}
+
+static inline void stop_rmtwkp(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devctrl, ~CTRL_RESUME_SIGNAL_ENABLE);
+}
+
+static inline void disable_self_pwr(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devcfg, ~CFG_SELF_PWR_ENABLE);
+}
+
+static inline void enable_self_pwr(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devcfg, CFG_SELF_PWR_ENABLE);
+}
+
+static inline void disable_set_desc(struct snps_udc_regs *regs)
+{
+       REG_MOD_AND(regs->devcfg, ~CFG_SET_DESCRIPTOR_ENABLE);
+}
+
+static inline void enable_set_desc(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devcfg, CFG_SET_DESCRIPTOR_ENABLE);
+}
+
+static inline void set_setup_done(struct snps_udc_regs *regs)
+{
+       REG_MOD_OR(regs->devctrl, CTRL_CSR_DONE);
+}
+
+static inline u32 get_enum_speed(struct snps_udc_regs *regs)
+{
+       switch (REG_RD(regs->devstatus) & STS_SPD_MASK) {
+       case STS_SPD_LS:
+               return SPEED_LOW;
+       case STS_SPD_HS:
+               return SPEED_HIGH;
+       case STS_SPD_FS:
+       case STS_SPD_FS_48MHZ:
+               return SPEED_FULL;
+       default:
+               return 0;
+       }
+}
+
+static inline void set_speed_requested(struct snps_udc_regs *regs, u32 speed)
+{
+       REG_MOD_AND(regs->devcfg, ~CFG_SPD_MASK);
+
+       switch (speed) {
+       case SPEED_LOW:
+               REG_MOD_OR(regs->devcfg, CFG_SPD_LS);
+               break;
+
+       case SPEED_HIGH:
+               REG_MOD_OR(regs->devcfg, CFG_SPD_HS);
+               break;
+
+       case SPEED_FULL:
+       default:
+               REG_MOD_OR(regs->devcfg, CFG_SPD_FS);
+               break;
+       }
+}
+
+static inline void init_ep_reg(struct snps_udc_regs *regs, u32 num, u32 type,
+                              u32 dirn, u32 max_pkt_size)
+{
+       if ((type == EP_TYPE_CTRL) || (dirn == EP_DIRN_OUT)) {
+               REG_WR(regs->ep_out[num].ctrl,
+                      (type << EP_CTRL_TYPE_SHIFT));
+               REG_WR(regs->ep_out[num].status,
+                      regs->ep_out[num].status);
+               REG_WR(regs->ep_out[num].epreg2,  0);
+               REG_WR(regs->ep_out[num].epreg3,
+                      ((max_pkt_size >> 2) << 16) | max_pkt_size);
+
+               if (mrx_fifo)
+                       REG_MOD_OR(regs->ep_out[num].epreg3,
+                                  (FIFO_SZ_U32(max_pkt_size) <<
+                                  EP_REG3_OUT_DEPTH_SHIFT));
+       }
+       if ((type == EP_TYPE_CTRL) || (dirn == EP_DIRN_IN)) {
+               REG_WR(regs->ep_in[num].ctrl,
+                      (type << EP_CTRL_TYPE_SHIFT));
+               REG_WR(regs->ep_in[num].epreg3,
+                      (max_pkt_size << EP_REG3_PKT_MAX_SHIFT));
+               REG_WR(regs->ep_in[num].epreg2,
+                      (max_pkt_size >> 2));
+               REG_MOD_OR(regs->ep_in[num].ctrl,
+                          EP_CTRL_IN_FLUSH_ENABLE);
+               REG_MOD_AND(regs->ep_in[num].ctrl,
+                           ~EP_CTRL_IN_FLUSH_ENABLE);
+               REG_MOD_AND(regs->ep_in[num].ctrl,
+                           EP_CTRL_NAK_SET);
+       }
+       REG_WR(regs->epcfg[num],
+              (num << EP_CFG_FIFO_NUM_SHIFT) |
+              (type << EP_CFG_TYPE_SHIFT) |
+              (max_pkt_size << EP_CFG_PKT_MAX_SHIFT) |
+              ((dirn == EP_DIRN_OUT) ? EP_CFG_DIRN_OUT : EP_CFG_DIRN_IN));
+}
+
+static inline void set_ep_alt_num(struct snps_udc_regs *regs, u32 num, u32 alt)
+{
+       REG_MOD_MASK(regs->epcfg[num], ~EP_CFG_ALT_NUM_MASK,
+                    (alt << EP_CFG_ALT_NUM_SHIFT));
+}
+
+static inline void set_epcfg_reg(struct snps_udc_regs *regs, u32 num, u32 cfg)
+{
+       REG_MOD_MASK(regs->epcfg[num], ~EP_CFG_CFG_NUM_MASK,
+                    (cfg << EP_CFG_CFG_NUM_SHIFT));
+}
+
+static inline void set_ep_intf_num(struct snps_udc_regs *regs, u32 num,
+                                  u32 intf)
+{
+       REG_MOD_MASK(regs->epcfg[num], ~EP_CFG_INTF_NUM_MASK,
+                    (intf << EP_CFG_INTF_NUM_SHIFT));
+}
+
+static inline void disable_ep_dma(struct snps_udc_regs *regs, u32 num, u32 
dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               if (mrx_fifo)
+                       REG_MOD_AND(regs->ep_out[num].ctrl,
+                                   ~EP_CTRL_OUT_DMA_ENABLE);
+       } else {
+               REG_MOD_AND(regs->ep_in[num].ctrl,
+                           ~EP_CTRL_IN_DMA_ENABLE);
+       }
+}
+
+static inline void enable_ep_dma(struct snps_udc_regs *regs,
+                                u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               if (mrx_fifo)
+                       REG_MOD_OR(regs->ep_out[num].ctrl,
+                                  EP_CTRL_OUT_DMA_ENABLE);
+               else
+                       REG_MOD_OR(regs->devctrl,
+                                  CTRL_DMA_OUT_ENABLE);
+       } else
+               REG_MOD_OR(regs->ep_in[num].ctrl,
+                          EP_CTRL_IN_DMA_ENABLE);
+}
+
+static inline void set_setup_buf_ptr(struct snps_udc_regs *regs,
+                                    u32 num, u32 dirn, void *addr)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_WR(regs->ep_out[num].setupbuf, (dma_addr_t)addr);
+}
+
+static inline void set_data_desc_ptr(struct snps_udc_regs *regs,
+                                    u32 num, u32 dirn, void *addr)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_WR(regs->ep_out[num].datadesc, (dma_addr_t)addr);
+       else
+               REG_WR(regs->ep_in[num].datadesc, (dma_addr_t)addr);
+}
+
+static inline bool is_ep_fifo_empty(struct snps_udc_regs *regs,
+                                   u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               if (mrx_fifo)
+                       return REG_RD(regs->ep_out[num].status) &
+                               EP_STS_OUT_FIFO_EMPTY ? true : false;
+               else
+                       return REG_RD(regs->devstatus) &
+                               STS_OUT_FIFO_EMPTY ? true : false;
+       }
+       return REG_RD(regs->ep_in[num].status) &
+                       EP_STS_IN_FIFO_EMPTY ? true : false;
+}
+
+static inline void clear_ep_fifo_flush(struct snps_udc_regs *regs,
+                                      u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               if (mrx_fifo)
+                       REG_MOD_AND(regs->ep_out[num].ctrl,
+                                   ~EP_CTRL_OUT_FLUSH_ENABLE);
+               else
+                       REG_MOD_AND(regs->devctrl,
+                                   ~CTRL_OUT_FIFO_FLUSH_ENABLE);
+       } else {
+               REG_MOD_AND(regs->ep_in[num].ctrl,
+                           ~EP_CTRL_IN_FLUSH_ENABLE);
+       }
+}
+
+static inline void set_ep_fifo_flush(struct snps_udc_regs *regs,
+                                    u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               if (mrx_fifo)
+                       REG_MOD_OR(regs->ep_out[num].ctrl,
+                                  EP_CTRL_OUT_FLUSH_ENABLE);
+               else
+                       REG_MOD_OR(regs->devctrl,
+                                  CTRL_OUT_FIFO_FLUSH_ENABLE);
+       } else {
+               REG_MOD_OR(regs->ep_in[num].ctrl,
+                          EP_CTRL_IN_FLUSH_ENABLE);
+       }
+}
+
+static inline u32 get_ep_frnum(struct snps_udc_regs *regs,
+                              u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               return (regs->ep_out[num].epreg2 &
+                               EP_REG2_OUT_FRAME_NUM_MASK) >>
+                               EP_REG2_OUT_FRAME_NUM_SHIFT;
+       return 0;
+}
+
+static inline void clear_udc_ep_irq(struct snps_udc_regs *regs,
+                                   u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_WR(regs->epintrstat, (1 << num) <<
+                      EP_INTR_OUT_SHIFT);
+       else
+               REG_WR(regs->epintrstat, (1 << num) <<
+                      EP_INTR_IN_SHIFT);
+}
+
+static inline void disable_udc_ep_irq(struct snps_udc_regs *regs,
+                                     u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               REG_MOD_OR(regs->epintrmask, ((1 << num) <<
+                          EP_INTR_OUT_SHIFT));
+       } else {
+               REG_MOD_OR(regs->epintrmask, ((1 << num) <<
+                          EP_INTR_IN_SHIFT));
+       }
+}
+
+static inline void enable_udc_ep_irq(struct snps_udc_regs *regs,
+                                    u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT) {
+               REG_MOD_AND(regs->epintrmask, ~((1 << num) <<
+                           EP_INTR_OUT_SHIFT));
+       } else {
+               REG_MOD_AND(regs->epintrmask, ~((1 << num) <<
+                           EP_INTR_IN_SHIFT));
+       }
+}
+
+static inline u32 get_ep_irq_active(struct snps_udc_regs *regs, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               return (REG_RD(regs->epintrstat) & EP_INTR_OUT_MASK)
+                                >> EP_INTR_OUT_SHIFT;
+
+       return (REG_RD(regs->epintrstat) & EP_INTR_IN_MASK)
+                                >> EP_INTR_IN_SHIFT;
+}
+
+static inline void clear_udc_ep_irq_list(struct snps_udc_regs *regs,
+                                        u32 dirn, u32 mask)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_WR(regs->epintrstat, (mask << EP_INTR_OUT_SHIFT));
+       else
+               REG_WR(regs->epintrstat, (mask << EP_INTR_IN_SHIFT));
+}
+
+static inline u32 get_ep_status(struct snps_udc_regs *regs, u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               return REG_RD(regs->ep_out[num].status);
+
+       return REG_RD(regs->ep_in[num].status);
+}
+
+static inline void clear_ep_status(struct snps_udc_regs *regs,
+                                  u32 num, u32 dirn, u32 mask)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_WR(regs->ep_out[num].status, mask);
+       else
+               REG_WR(regs->ep_in[num].status, mask);
+}
+
+static inline void clear_ep_nak(struct snps_udc_regs *regs,
+                               u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_MOD_OR(regs->ep_out[num].ctrl, EP_CTRL_NAK_CLEAR);
+       else
+               REG_MOD_OR(regs->ep_in[num].ctrl, EP_CTRL_NAK_CLEAR);
+}
+
+static inline void enable_ep_nak(struct snps_udc_regs *regs,
+                                u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_MOD_OR(regs->ep_out[num].ctrl, EP_CTRL_NAK_SET);
+       else
+               REG_MOD_OR(regs->ep_in[num].ctrl, EP_CTRL_NAK_SET);
+}
+
+static inline void disable_ep_nak(struct snps_udc_regs *regs,
+                                 u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_MOD_AND(regs->ep_out[num].ctrl, ~EP_CTRL_NAK_SET);
+       else
+               REG_MOD_AND(regs->ep_in[num].ctrl, ~EP_CTRL_NAK_SET);
+}
+
+static inline bool is_ep_nak_inprog(struct snps_udc_regs *regs,
+                                   u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               return REG_RD(regs->ep_out[num].ctrl) &
+                       EP_CTRL_NAK_IN_PROGRESS ? true : false;
+
+       return REG_RD(regs->ep_in[num].ctrl) &
+                       EP_CTRL_NAK_IN_PROGRESS ? true : false;
+}
+
+static inline void disable_ep_stall(struct snps_udc_regs *regs,
+                                   u32 num, u32 dirn)
+{
+       if (dirn == EP_DIRN_OUT)
+               REG_MOD_AND(regs->ep_out[num].ctrl,
+                           ~EP_CTRL_STALL_ENABLE);
+       else
+               REG_MOD_AND(regs->ep_in[num].ctrl,
+                           ~EP_CTRL_STALL_ENABLE);
+}
+
+static inline void enable_ep_stall(struct snps_udc_regs *regs,
+                                  u32 num, u32 dirn)
+{
+       if (mrx_fifo && !(REG_RD(regs->ep_out[num].status) &
+                       EP_STS_OUT_FIFO_EMPTY))
+               return;
+       else if (!mrx_fifo && !(REG_RD(regs->devstatus) &
+                       STS_OUT_FIFO_EMPTY))
+               return;
+
+       if (dirn == EP_DIRN_OUT)
+               REG_MOD_OR(regs->ep_out[num].ctrl,
+                          EP_CTRL_STALL_ENABLE);
+       else
+               REG_MOD_OR(regs->ep_in[num].ctrl,
+                          EP_CTRL_STALL_ENABLE);
+}
+
+static inline u32 get_last_rx_frnum(struct snps_udc_regs *regs)
+{
+       return (REG_RD(regs->devstatus) & STS_SOF_FRAME_NUM_MASK)
+                       >> STS_SOF_FRAME_NUM_SHIFT;
+}
+
+static inline void finish_udc(struct snps_udc_regs *regs)
+{
+       u32 ep_num;
+
+       disable_ctrl_dma(regs);
+       disable_udc_dev_irq(regs, UDC_IRQ_ALL);
+       clear_udc_dev_irq(regs, UDC_IRQ_ALL);
+
+       for (ep_num = 0; ep_num < UDC_MAX_EP; ep_num++) {
+               disable_udc_ep_irq(regs, ep_num, EP_DIRN_IN);
+               clear_udc_ep_irq(regs, ep_num, EP_DIRN_IN);
+               clear_ep_status(regs, ep_num, EP_DIRN_IN,
+                               get_ep_status(regs, ep_num,
+                                             EP_DIRN_IN));
+
+               disable_udc_ep_irq(regs, ep_num, EP_DIRN_OUT);
+               clear_udc_ep_irq(regs, ep_num, EP_DIRN_OUT);
+               clear_ep_status(regs, ep_num, EP_DIRN_OUT,
+                               get_ep_status(regs, ep_num,
+                                             EP_DIRN_OUT));
+       }
+}
+
+static inline void init_udc_reg(struct snps_udc_regs *regs)
+{
+       finish_udc(regs);
+       REG_WR(regs->devcfg, CFG_SET_DESCRIPTOR_ENABLE
+                                       | CFG_UTMI_8BIT_ENABLE
+                                       | CFG_CSR_PROGRAM_ENABLE
+                                       | CFG_SPD_HS);
+       REG_WR(regs->devctrl, CTRL_LE_ENABLE
+                                       | CTRL_DISCONNECT_ENABLE
+                                       | CTRL_DMA_MODE_ENABLE
+                                       | CTRL_DMA_DESC_UPDATE_ENABLE
+                                       | CTRL_OUT_ALL_NAK
+                                       | CTRL_DMA_OUT_THRESH_LEN_MASK
+                                       | CTRL_DMA_BURST_LEN_MASK
+                                       | CTRL_DMA_BURST_ENABLE
+                                       | CTRL_OUT_FIFO_FLUSH_ENABLE
+                       );
+
+       if (mrx_fifo)
+               REG_MOD_AND(regs->devctrl, ~CTRL_OUT_FIFO_FLUSH_ENABLE);
+
+       if (out_bf_mode)
+               REG_MOD_OR(regs->devctrl, CTRL_DMA_BUFF_FILL_MODE_ENABLE);
+
+       REG_WR(regs->devintrmask, IRQ_BUS_IDLE | IRQ_SOF_DETECTED);
+       REG_WR(regs->epintrmask, 0);
+}
+
+static inline struct data_desc *dma_desc_chain_alloc(struct snps_udc_ep *ep)
+{
+       u32 idx;
+
+       idx = ep->dma.add_idx++;
+
+       return &ep->dma.virt->desc[EP_DMA_DESC_IDX(idx)];
+}
+
+static inline int dma_desc_chain_is_empty(struct snps_udc_ep *ep)
+{
+       return ep->dma.add_idx == ep->dma.remove_idx;
+}
+
+static inline void dma_desc_chain_free(struct snps_udc_ep *ep)
+{
+       ep->dma.remove_idx++;
+}
+
+static inline int dma_desc_chain_is_full(struct snps_udc_ep *ep)
+{
+       return !dma_desc_chain_is_empty(ep) &&
+               (EP_DMA_DESC_IDX(ep->dma.add_idx) ==
+                EP_DMA_DESC_IDX(ep->dma.remove_idx));
+}
+
+static inline struct data_desc *dma_desc_chain_head(struct snps_udc_ep *ep)
+{
+       u32 index = EP_DMA_DESC_IDX(ep->dma.remove_idx);
+
+       return &ep->dma.virt->desc[index];
+}
+
+static inline void dma_desc_chain_reset(struct snps_udc_ep *ep)
+{
+       ep->dma.add_idx = 0;
+       ep->dma.remove_idx = 0;
+}
+#endif
-- 
2.1.0

Reply via email to