OSDN Git Service

Merge tag 'usb-for-v3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi...
[android-x86/kernel.git] / drivers / usb / dwc2 / gadget.c
index c24cb3b..6a30887 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/phy.h>
 #include <linux/platform_data/s3c-hsotg.h>
+#include <linux/uaccess.h>
 
 #include "core.h"
 #include "hw.h"
@@ -65,6 +66,15 @@ static inline void __bic32(void __iomem *ptr, u32 val)
        writel(readl(ptr) & ~val, ptr);
 }
 
+static inline struct s3c_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg,
+                                               u32 ep_index, u32 dir_in)
+{
+       if (dir_in)
+               return hsotg->eps_in[ep_index];
+       else
+               return hsotg->eps_out[ep_index];
+}
+
 /* forward declaration of functions */
 static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
 
@@ -165,15 +175,18 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
 {
        unsigned int ep;
        unsigned int addr;
-       unsigned int size;
        int timeout;
        u32 val;
 
-       /* set FIFO sizes to 2048/1024 */
+       /* Reset fifo map if not correctly cleared during previous session */
+       WARN_ON(hsotg->fifo_map);
+       hsotg->fifo_map = 0;
 
-       writel(2048, hsotg->regs + GRXFSIZ);
-       writel((2048 << FIFOSIZE_STARTADDR_SHIFT) |
-               (1024 << FIFOSIZE_DEPTH_SHIFT), hsotg->regs + GNPTXFSIZ);
+       /* set RX/NPTX FIFO sizes */
+       writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ);
+       writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) |
+               (hsotg->g_np_g_tx_fifo_sz << FIFOSIZE_DEPTH_SHIFT),
+               hsotg->regs + GNPTXFSIZ);
 
        /*
         * arange all the rest of the TX FIFOs, as some versions of this
@@ -183,35 +196,21 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
         */
 
        /* start at the end of the GNPTXFSIZ, rounded up */
-       addr = 2048 + 1024;
+       addr = hsotg->g_rx_fifo_sz + hsotg->g_np_g_tx_fifo_sz;
 
        /*
-        * Because we have not enough memory to have each TX FIFO of size at
-        * least 3072 bytes (the maximum single packet size), we create four
-        * FIFOs of lenght 1024, and four of length 3072 bytes, and assing
+        * Configure fifos sizes from provided configuration and assign
         * them to endpoints dynamically according to maxpacket size value of
         * given endpoint.
         */
-
-       /* 256*4=1024 bytes FIFO length */
-       size = 256;
-       for (ep = 1; ep <= 4; ep++) {
-               val = addr;
-               val |= size << FIFOSIZE_DEPTH_SHIFT;
-               WARN_ONCE(addr + size > hsotg->fifo_mem,
-                         "insufficient fifo memory");
-               addr += size;
-
-               writel(val, hsotg->regs + DPTXFSIZN(ep));
-       }
-       /* 768*4=3072 bytes FIFO length */
-       size = 768;
-       for (ep = 5; ep <= 8; ep++) {
+       for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
+               if (!hsotg->g_tx_fifo_sz[ep])
+                       continue;
                val = addr;
-               val |= size << FIFOSIZE_DEPTH_SHIFT;
-               WARN_ONCE(addr + size > hsotg->fifo_mem,
+               val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
+               WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
                          "insufficient fifo memory");
-               addr += size;
+               addr += hsotg->g_tx_fifo_sz[ep];
 
                writel(val, hsotg->regs + DPTXFSIZN(ep));
        }
@@ -236,6 +235,7 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
                        dev_err(hsotg->dev,
                                "%s: timeout flushing fifos (GRSTCTL=%08x)\n",
                                __func__, val);
+                       break;
                }
 
                udelay(1);
@@ -566,11 +566,6 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        length = ureq->length - ureq->actual;
        dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
                ureq->length, ureq->actual);
-       if (0)
-               dev_dbg(hsotg->dev,
-                       "REQ buf %p len %d dma %pad noi=%d zp=%d snok=%d\n",
-                       ureq->buf, length, &ureq->dma,
-                       ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
 
        maxreq = get_ep_limit(hs_ep);
        if (length > maxreq) {
@@ -604,14 +599,15 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        else
                epsize = 0;
 
-       if (index != 0 && ureq->zero) {
-               /*
-                * test for the packets being exactly right for the
-                * transfer
-                */
-
-               if (length == (packets * hs_ep->ep.maxpacket))
-                       packets++;
+       /*
+        * zero length packet should be programmed on its own and should not
+        * be counted in DIEPTSIZ.PktCnt with other packets.
+        */
+       if (dir_in && ureq->zero && !continuing) {
+               /* Test if zlp is actually required. */
+               if ((ureq->length >= hs_ep->ep.maxpacket) &&
+                                       !(ureq->length % hs_ep->ep.maxpacket))
+                       hs_ep->send_zlp = 1;
        }
 
        epsize |= DXEPTSIZ_PKTCNT(packets);
@@ -644,15 +640,12 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        ctrl |= DXEPCTL_EPENA;  /* ensure ep enabled */
        ctrl |= DXEPCTL_USBACTEP;
 
-       dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup);
+       dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state);
 
        /* For Setup request do not clear NAK */
-       if (hsotg->setup && index == 0)
-               hsotg->setup = 0;
-       else
+       if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP))
                ctrl |= DXEPCTL_CNAK;   /* clear NAK set by core */
 
-
        dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
        writel(ctrl, hsotg->regs + epctrl_reg);
 
@@ -686,7 +679,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
 
        /* check ep is enabled */
        if (!(readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA))
-               dev_warn(hsotg->dev,
+               dev_dbg(hsotg->dev,
                         "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
                         index, readl(hsotg->regs + epctrl_reg));
 
@@ -733,6 +726,59 @@ dma_error:
        return -EIO;
 }
 
+static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
+       struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+       void *req_buf = hs_req->req.buf;
+
+       /* If dma is not being used or buffer is aligned */
+       if (!using_dma(hsotg) || !((long)req_buf & 3))
+               return 0;
+
+       WARN_ON(hs_req->saved_req_buf);
+
+       dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
+                       hs_ep->ep.name, req_buf, hs_req->req.length);
+
+       hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
+       if (!hs_req->req.buf) {
+               hs_req->req.buf = req_buf;
+               dev_err(hsotg->dev,
+                       "%s: unable to allocate memory for bounce buffer\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       /* Save actual buffer */
+       hs_req->saved_req_buf = req_buf;
+
+       if (hs_ep->dir_in)
+               memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
+       return 0;
+}
+
+static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
+       struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+       /* If dma is not being used or buffer was aligned */
+       if (!using_dma(hsotg) || !hs_req->saved_req_buf)
+               return;
+
+       dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
+               hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);
+
+       /* Copy data from bounce buffer on successful out transfer */
+       if (!hs_ep->dir_in && !hs_req->req.status)
+               memcpy(hs_req->saved_req_buf, hs_req->req.buf,
+                                                       hs_req->req.actual);
+
+       /* Free bounce buffer */
+       kfree(hs_req->req.buf);
+
+       hs_req->req.buf = hs_req->saved_req_buf;
+       hs_req->saved_req_buf = NULL;
+}
+
 static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
                              gfp_t gfp_flags)
 {
@@ -740,6 +786,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
        struct dwc2_hsotg *hs = hs_ep->parent;
        bool first;
+       int ret;
 
        dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
                ep->name, req, req->length, req->buf, req->no_interrupt,
@@ -750,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
        req->actual = 0;
        req->status = -EINPROGRESS;
 
+       ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
+       if (ret)
+               return ret;
+
        /* if we're using DMA, sync the buffers as necessary */
        if (using_dma(hs)) {
-               int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+               ret = s3c_hsotg_map_dma(hs, hs_ep, req);
                if (ret)
                        return ret;
        }
@@ -819,7 +870,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
 static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
                                           u32 windex)
 {
-       struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
+       struct s3c_hsotg_ep *ep;
        int dir = (windex & USB_DIR_IN) ? 1 : 0;
        int idx = windex & 0x7F;
 
@@ -829,6 +880,8 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
        if (idx > hsotg->num_of_eps)
                return NULL;
 
+       ep = index_to_ep(hsotg, idx, dir);
+
        if (idx && ep->dir_in != dir)
                return NULL;
 
@@ -836,6 +889,32 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
 }
 
 /**
+ * s3c_hsotg_set_test_mode - Enable usb Test Modes
+ * @hsotg: The driver state.
+ * @testmode: requested usb test mode
+ * Enable usb Test Mode requested by the Host.
+ */
+static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
+{
+       int dctl = readl(hsotg->regs + DCTL);
+
+       dctl &= ~DCTL_TSTCTL_MASK;
+       switch (testmode) {
+       case TEST_J:
+       case TEST_K:
+       case TEST_SE0_NAK:
+       case TEST_PACKET:
+       case TEST_FORCE_EN:
+               dctl |= testmode << DCTL_TSTCTL_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+       writel(dctl, hsotg->regs + DCTL);
+       return 0;
+}
+
+/**
  * s3c_hsotg_send_reply - send reply to control request
  * @hsotg: The device state
  * @ep: Endpoint 0
@@ -864,13 +943,15 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
 
        req->buf = hsotg->ep0_buff;
        req->length = length;
-       req->zero = 1; /* always do zero-length final transfer */
+       /*
+        * zero flag is for sending zlp in DATA IN stage. It has no impact on
+        * STATUS stage.
+        */
+       req->zero = 0;
        req->complete = s3c_hsotg_complete_oursetup;
 
        if (length)
                memcpy(req->buf, buff, length);
-       else
-               ep->sent_zlp = 1;
 
        ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
        if (ret) {
@@ -889,7 +970,7 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
 static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
                                        struct usb_ctrlrequest *ctrl)
 {
-       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
        struct s3c_hsotg_ep *ep;
        __le16 reply;
        int ret;
@@ -953,33 +1034,62 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
 }
 
 /**
- * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
+ * s3c_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE
  * @hsotg: The device state
  * @ctrl: USB control request
  */
 static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
                                         struct usb_ctrlrequest *ctrl)
 {
-       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
        struct s3c_hsotg_req *hs_req;
        bool restart;
        bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
        struct s3c_hsotg_ep *ep;
        int ret;
        bool halted;
+       u32 recip;
+       u32 wValue;
+       u32 wIndex;
 
        dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
                __func__, set ? "SET" : "CLEAR");
 
-       if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
-               ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
+       wValue = le16_to_cpu(ctrl->wValue);
+       wIndex = le16_to_cpu(ctrl->wIndex);
+       recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+       switch (recip) {
+       case USB_RECIP_DEVICE:
+               switch (wValue) {
+               case USB_DEVICE_TEST_MODE:
+                       if ((wIndex & 0xff) != 0)
+                               return -EINVAL;
+                       if (!set)
+                               return -EINVAL;
+
+                       hsotg->test_mode = wIndex >> 8;
+                       ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
+                       if (ret) {
+                               dev_err(hsotg->dev,
+                                       "%s: failed to send reply\n", __func__);
+                               return ret;
+                       }
+                       break;
+               default:
+                       return -ENOENT;
+               }
+               break;
+
+       case USB_RECIP_ENDPOINT:
+               ep = ep_from_windex(hsotg, wIndex);
                if (!ep) {
                        dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
-                               __func__, le16_to_cpu(ctrl->wIndex));
+                               __func__, wIndex);
                        return -ENOENT;
                }
 
-               switch (le16_to_cpu(ctrl->wValue)) {
+               switch (wValue) {
                case USB_ENDPOINT_HALT:
                        halted = ep->halted;
 
@@ -1006,16 +1116,22 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
                                        hs_req = ep->req;
                                        ep->req = NULL;
                                        list_del_init(&hs_req->queue);
-                                       usb_gadget_giveback_request(&ep->ep,
-                                                                   &hs_req->req);
+                                       if (hs_req->req.complete) {
+                                               spin_unlock(&hsotg->lock);
+                                               usb_gadget_giveback_request(
+                                                       &ep->ep, &hs_req->req);
+                                               spin_lock(&hsotg->lock);
+                                       }
                                }
 
                                /* If we have pending request, then start it */
-                               restart = !list_empty(&ep->queue);
-                               if (restart) {
-                                       hs_req = get_ep_head(ep);
-                                       s3c_hsotg_start_req(hsotg, ep,
-                                                           hs_req, false);
+                               if (!ep->req) {
+                                       restart = !list_empty(&ep->queue);
+                                       if (restart) {
+                                               hs_req = get_ep_head(ep);
+                                               s3c_hsotg_start_req(hsotg, ep,
+                                                               hs_req, false);
+                                       }
                                }
                        }
 
@@ -1024,9 +1140,10 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
                default:
                        return -ENOENT;
                }
-       } else
-               return -ENOENT;  /* currently only deal with endpoint */
-
+               break;
+       default:
+               return -ENOENT;
+       }
        return 1;
 }
 
@@ -1040,7 +1157,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
  */
 static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
 {
-       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
        u32 reg;
        u32 ctrl;
 
@@ -1080,34 +1197,29 @@ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
 static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
                                      struct usb_ctrlrequest *ctrl)
 {
-       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
        int ret = 0;
        u32 dcfg;
 
-       ep0->sent_zlp = 0;
-
        dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
                 ctrl->bRequest, ctrl->bRequestType,
                 ctrl->wValue, ctrl->wLength);
 
-       /*
-        * record the direction of the request, for later use when enquing
-        * packets onto EP0.
-        */
-
-       ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0;
-       dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in);
-
-       /*
-        * if we've no data with this request, then the last part of the
-        * transaction is going to implicitly be IN.
-        */
-       if (ctrl->wLength == 0)
+       if (ctrl->wLength == 0) {
+               ep0->dir_in = 1;
+               hsotg->ep0_state = DWC2_EP0_STATUS_IN;
+       } else if (ctrl->bRequestType & USB_DIR_IN) {
                ep0->dir_in = 1;
+               hsotg->ep0_state = DWC2_EP0_DATA_IN;
+       } else {
+               ep0->dir_in = 0;
+               hsotg->ep0_state = DWC2_EP0_DATA_OUT;
+       }
 
        if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
                switch (ctrl->bRequest) {
                case USB_REQ_SET_ADDRESS:
+                       hsotg->connected = 1;
                        dcfg = readl(hsotg->regs + DCFG);
                        dcfg &= ~DCFG_DEVADDR_MASK;
                        dcfg |= (le16_to_cpu(ctrl->wValue) <<
@@ -1201,9 +1313,11 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
                return;
        }
 
-       hsotg->eps[0].dir_in = 0;
+       hsotg->eps_out[0]->dir_in = 0;
+       hsotg->eps_out[0]->send_zlp = 0;
+       hsotg->ep0_state = DWC2_EP0_SETUP;
 
-       ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC);
+       ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);
        if (ret < 0) {
                dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
                /*
@@ -1213,6 +1327,32 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
        }
 }
 
+static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
+                                       struct s3c_hsotg_ep *hs_ep)
+{
+       u32 ctrl;
+       u8 index = hs_ep->index;
+       u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
+       u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
+
+       if (hs_ep->dir_in)
+               dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n",
+                                                                       index);
+       else
+               dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
+                                                                       index);
+
+       writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+                       DXEPTSIZ_XFERSIZE(0), hsotg->regs +
+                       epsiz_reg);
+
+       ctrl = readl(hsotg->regs + epctl_reg);
+       ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
+       ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
+       ctrl |= DXEPCTL_USBACTEP;
+       writel(ctrl, hsotg->regs + epctl_reg);
+}
+
 /**
  * s3c_hsotg_complete_request - complete a request given to us
  * @hsotg: The device state.
@@ -1249,6 +1389,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
        if (hs_req->req.status == -EINPROGRESS)
                hs_req->req.status = result;
 
+       s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
+
        hs_ep->req = NULL;
        list_del_init(&hs_req->queue);
 
@@ -1293,7 +1435,7 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
  */
 static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
 {
-       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
+       struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx];
        struct s3c_hsotg_req *hs_req = hs_ep->req;
        void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
        int to_read;
@@ -1345,9 +1487,9 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
 }
 
 /**
- * s3c_hsotg_send_zlp - send zero-length packet on control endpoint
+ * s3c_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint
  * @hsotg: The device instance
- * @req: The request currently on this endpoint
+ * @dir_in: If IN zlp
  *
  * Generate a zero-length IN packet request for terminating a SETUP
  * transaction.
@@ -1356,53 +1498,28 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
  * currently believed that we do not need to wait for any space in
  * the TxFIFO.
  */
-static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
-                              struct s3c_hsotg_req *req)
+static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
 {
-       u32 ctrl;
-
-       if (!req) {
-               dev_warn(hsotg->dev, "%s: no request?\n", __func__);
-               return;
-       }
-
-       if (req->req.length == 0) {
-               hsotg->eps[0].sent_zlp = 1;
-               s3c_hsotg_enqueue_setup(hsotg);
-               return;
-       }
-
-       hsotg->eps[0].dir_in = 1;
-       hsotg->eps[0].sent_zlp = 1;
-
-       dev_dbg(hsotg->dev, "sending zero-length packet\n");
+       /* eps_out[0] is used in both directions */
+       hsotg->eps_out[0]->dir_in = dir_in;
+       hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT;
 
-       /* issue a zero-sized packet to terminate this */
-       writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
-              DXEPTSIZ_XFERSIZE(0), hsotg->regs + DIEPTSIZ(0));
-
-       ctrl = readl(hsotg->regs + DIEPCTL0);
-       ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
-       ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
-       ctrl |= DXEPCTL_USBACTEP;
-       writel(ctrl, hsotg->regs + DIEPCTL0);
+       s3c_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
 }
 
 /**
  * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
  * @hsotg: The device instance
  * @epnum: The endpoint received from
- * @was_setup: Set if processing a SetupDone event.
  *
  * The RXFIFO has delivered an OutDone event, which means that the data
  * transfer for an OUT endpoint has been completed, either by a short
  * packet or by the finish of a transfer.
  */
-static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
-                                    int epnum, bool was_setup)
+static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
 {
        u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
-       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
+       struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum];
        struct s3c_hsotg_req *hs_req = hs_ep->req;
        struct usb_request *req = &hs_req->req;
        unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
@@ -1413,6 +1530,13 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
                return;
        }
 
+       if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) {
+               dev_dbg(hsotg->dev, "zlp packet received\n");
+               s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+               s3c_hsotg_enqueue_setup(hsotg);
+               return;
+       }
+
        if (using_dma(hsotg)) {
                unsigned size_done;
 
@@ -1435,12 +1559,6 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
        if (req->actual < req->length && size_left == 0) {
                s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
                return;
-       } else if (epnum == 0) {
-               /*
-                * After was_setup = 1 =>
-                * set CNAK for non Setup requests
-                */
-               hsotg->setup = was_setup ? 0 : 1;
        }
 
        if (req->actual < req->length && req->short_not_ok) {
@@ -1453,13 +1571,10 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
                 */
        }
 
-       if (epnum == 0) {
-               /*
-                * Condition req->complete != s3c_hsotg_complete_setup says:
-                * send ZLP when we have an asynchronous request from gadget
-                */
-               if (!was_setup && req->complete != s3c_hsotg_complete_setup)
-                       s3c_hsotg_send_zlp(hsotg, hs_req);
+       if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
+               /* Move to STATUS IN */
+               s3c_hsotg_ep0_zlp(hsotg, true);
+               return;
        }
 
        s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
@@ -1511,8 +1626,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
        size = grxstsr & GRXSTS_BYTECNT_MASK;
        size >>= GRXSTS_BYTECNT_SHIFT;
 
-       if (1)
-               dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
+       dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
                        __func__, grxstsr, size, epnum);
 
        switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
@@ -1525,7 +1639,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        s3c_hsotg_read_frameno(hsotg));
 
                if (!using_dma(hsotg))
-                       s3c_hsotg_handle_outdone(hsotg, epnum, false);
+                       s3c_hsotg_handle_outdone(hsotg, epnum);
                break;
 
        case GRXSTS_PKTSTS_SETUPDONE:
@@ -1533,8 +1647,13 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
                        s3c_hsotg_read_frameno(hsotg),
                        readl(hsotg->regs + DOEPCTL(0)));
-
-               s3c_hsotg_handle_outdone(hsotg, epnum, true);
+               /*
+                * Call s3c_hsotg_handle_outdone here if it was not called from
+                * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't
+                * generate GRXSTS_PKTSTS_OUTDONE for setup packet.
+                */
+               if (hsotg->ep0_state == DWC2_EP0_SETUP)
+                       s3c_hsotg_handle_outdone(hsotg, epnum);
                break;
 
        case GRXSTS_PKTSTS_OUTRX:
@@ -1547,6 +1666,8 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        s3c_hsotg_read_frameno(hsotg),
                        readl(hsotg->regs + DOEPCTL(0)));
 
+               WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
+
                s3c_hsotg_rx_data(hsotg, epnum, size);
                break;
 
@@ -1591,14 +1712,18 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
  * the hardware control registers to reflect this.
  */
 static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
-                                      unsigned int ep, unsigned int mps)
+                       unsigned int ep, unsigned int mps, unsigned int dir_in)
 {
-       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
+       struct s3c_hsotg_ep *hs_ep;
        void __iomem *regs = hsotg->regs;
        u32 mpsval;
        u32 mcval;
        u32 reg;
 
+       hs_ep = index_to_ep(hsotg, ep, dir_in);
+       if (!hs_ep)
+               return;
+
        if (ep == 0) {
                /* EP0 is a special case */
                mpsval = s3c_hsotg_ep0_mps(mps);
@@ -1617,17 +1742,12 @@ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
                hs_ep->ep.maxpacket = mpsval;
        }
 
-       /*
-        * update both the in and out endpoint controldir_ registers, even
-        * if one of the directions may not be in use.
-        */
-
-       reg = readl(regs + DIEPCTL(ep));
-       reg &= ~DXEPCTL_MPS_MASK;
-       reg |= mpsval;
-       writel(reg, regs + DIEPCTL(ep));
-
-       if (ep) {
+       if (dir_in) {
+               reg = readl(regs + DIEPCTL(ep));
+               reg &= ~DXEPCTL_MPS_MASK;
+               reg |= mpsval;
+               writel(reg, regs + DIEPCTL(ep));
+       } else {
                reg = readl(regs + DOEPCTL(ep));
                reg &= ~DXEPCTL_MPS_MASK;
                reg |= mpsval;
@@ -1727,9 +1847,21 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
        }
 
        /* Finish ZLP handling for IN EP0 transactions */
-       if (hsotg->eps[0].sent_zlp) {
-               dev_dbg(hsotg->dev, "zlp packet received\n");
+       if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
+               dev_dbg(hsotg->dev, "zlp packet sent\n");
                s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+               if (hsotg->test_mode) {
+                       int ret;
+
+                       ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode);
+                       if (ret < 0) {
+                               dev_dbg(hsotg->dev, "Invalid Test #%d\n",
+                                               hsotg->test_mode);
+                               s3c_hsotg_stall_ep0(hsotg);
+                               return;
+                       }
+               }
+               s3c_hsotg_enqueue_setup(hsotg);
                return;
        }
 
@@ -1756,31 +1888,27 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
        dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
                hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
 
-       /*
-        * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0
-        * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B
-        * ,256B ... ), after last MPS sized packet send IN ZLP packet to
-        * inform the host that no more data is available.
-        * The state of req.zero member is checked to be sure that the value to
-        * send is smaller than wValue expected from host.
-        * Check req.length to NOT send another ZLP when the current one is
-        * under completion (the one for which this completion has been called).
-        */
-       if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero &&
-           hs_req->req.length == hs_req->req.actual &&
-           !(hs_req->req.length % hs_ep->ep.maxpacket)) {
+       if (!size_left && hs_req->req.actual < hs_req->req.length) {
+               dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
+               s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
+               return;
+       }
 
-               dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n");
-               s3c_hsotg_send_zlp(hsotg, hs_req);
+       /* Zlp for all endpoints, for ep0 only in DATA IN stage */
+       if (hs_ep->send_zlp) {
+               s3c_hsotg_program_zlp(hsotg, hs_ep);
+               hs_ep->send_zlp = 0;
+               /* transfer will be completed on next complete interrupt */
+               return;
+       }
 
+       if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) {
+               /* Move to STATUS OUT */
+               s3c_hsotg_ep0_zlp(hsotg, false);
                return;
        }
 
-       if (!size_left && hs_req->req.actual < hs_req->req.length) {
-               dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
-               s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
-       } else
-               s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+       s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
 }
 
 /**
@@ -1794,7 +1922,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
 static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
                            int dir_in)
 {
-       struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
+       struct s3c_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in);
        u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
        u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
        u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
@@ -1807,6 +1935,12 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
        /* Clear endpoint interrupts */
        writel(ints, hsotg->regs + epint_reg);
 
+       if (!hs_ep) {
+               dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
+                                       __func__, idx, dir_in ? "in" : "out");
+               return;
+       }
+
        dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
                __func__, idx, dir_in ? "in" : "out", ints);
 
@@ -1843,7 +1977,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
                         * as we ignore the RXFIFO.
                         */
 
-                       s3c_hsotg_handle_outdone(hsotg, idx, false);
+                       s3c_hsotg_handle_outdone(hsotg, idx);
                }
        }
 
@@ -1882,7 +2016,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
                        if (dir_in)
                                WARN_ON_ONCE(1);
                        else
-                               s3c_hsotg_handle_outdone(hsotg, 0, true);
+                               s3c_hsotg_handle_outdone(hsotg, 0);
                }
        }
 
@@ -1973,9 +2107,15 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
 
        if (ep0_mps) {
                int i;
-               s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps);
-               for (i = 1; i < hsotg->num_of_eps; i++)
-                       s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps);
+               /* Initialize ep0 for both in and out directions */
+               s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1);
+               s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0);
+               for (i = 1; i < hsotg->num_of_eps; i++) {
+                       if (hsotg->eps_in[i])
+                               s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1);
+                       if (hsotg->eps_out[i])
+                               s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0);
+               }
        }
 
        /* ensure after enumeration our EP0 is active */
@@ -2032,8 +2172,16 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
                return;
 
        hsotg->connected = 0;
-       for (ep = 0; ep < hsotg->num_of_eps; ep++)
-               kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN);
+       hsotg->test_mode = 0;
+
+       for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+               if (hsotg->eps_in[ep])
+                       kill_all_requests(hsotg, hsotg->eps_in[ep],
+                                                               -ESHUTDOWN);
+               if (hsotg->eps_out[ep])
+                       kill_all_requests(hsotg, hsotg->eps_out[ep],
+                                                               -ESHUTDOWN);
+       }
 
        call_gadget(hsotg, disconnect);
 }
@@ -2050,9 +2198,11 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
        int epno, ret;
 
        /* look through for any more data to transmit */
-
        for (epno = 0; epno < hsotg->num_of_eps; epno++) {
-               ep = &hsotg->eps[epno];
+               ep = index_to_ep(hsotg, epno, 1);
+
+               if (!ep)
+                       continue;
 
                if (!ep->dir_in)
                        continue;
@@ -2126,9 +2276,13 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
  *
  * Issue a soft reset to the core, and await the core finishing it.
  */
-void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
+void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
+                                               bool is_usb_reset)
 {
-       s3c_hsotg_corereset(hsotg);
+       u32 val;
+
+       if (!is_usb_reset)
+               s3c_hsotg_corereset(hsotg);
 
        /*
         * we must now enable ep0 ready for host detection and then
@@ -2136,14 +2290,16 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
         */
 
        /* set the PLL on, remove the HNP/SRP and set the PHY */
+       val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
        writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
-              (0x5 << 10), hsotg->regs + GUSBCFG);
+              (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG);
 
        s3c_hsotg_init_fifo(hsotg);
 
-       __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+       if (!is_usb_reset)
+               __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
 
-       writel(1 << 18 | DCFG_DEVSPD_HS,  hsotg->regs + DCFG);
+       writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS,  hsotg->regs + DCFG);
 
        /* Clear any pending OTG interrupts */
        writel(0xffffffff, hsotg->regs + GOTGINT);
@@ -2212,9 +2368,11 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
        s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
        s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
 
-       __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
-       udelay(10);  /* see openiboot */
-       __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+       if (!is_usb_reset) {
+               __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+               udelay(10);  /* see openiboot */
+               __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+       }
 
        dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL));
 
@@ -2227,13 +2385,13 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
        writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
               DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0);
 
-       writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+       writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
               DXEPCTL_CNAK | DXEPCTL_EPENA |
               DXEPCTL_USBACTEP,
               hsotg->regs + DOEPCTL0);
 
        /* enable, but don't activate EP0in */
-       writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+       writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
               DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
 
        s3c_hsotg_enqueue_setup(hsotg);
@@ -2243,8 +2401,10 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
                readl(hsotg->regs + DOEPCTL0));
 
        /* clear global NAKs */
-       writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
-              hsotg->regs + DCTL);
+       val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
+       if (!is_usb_reset)
+               val |= DCTL_SFTDISCON;
+       __orr32(hsotg->regs + DCTL, val);
 
        /* must be at-least 3ms to allow bus to see disconnect */
        mdelay(3);
@@ -2290,7 +2450,6 @@ irq_retry:
                writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
 
                s3c_hsotg_irq_enumdone(hsotg);
-               hsotg->connected = 1;
        }
 
        if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
@@ -2305,12 +2464,14 @@ irq_retry:
 
                dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
 
-               for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
+               for (ep = 0; ep < hsotg->num_of_eps && daint_out;
+                                               ep++, daint_out >>= 1) {
                        if (daint_out & 1)
                                s3c_hsotg_epint(hsotg, ep, 0);
                }
 
-               for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) {
+               for (ep = 0; ep < hsotg->num_of_eps  && daint_in;
+                                               ep++, daint_in >>= 1) {
                        if (daint_in & 1)
                                s3c_hsotg_epint(hsotg, ep, 1);
                }
@@ -2326,15 +2487,17 @@ irq_retry:
 
                writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
 
+               /* Report disconnection if it is not already done. */
+               s3c_hsotg_disconnect(hsotg);
+
                if (usb_status & GOTGCTL_BSESVLD) {
                        if (time_after(jiffies, hsotg->last_rst +
                                       msecs_to_jiffies(200))) {
 
-                               kill_all_requests(hsotg, &hsotg->eps[0],
+                               kill_all_requests(hsotg, hsotg->eps_out[0],
                                                          -ECONNRESET);
 
-                               s3c_hsotg_core_init_disconnected(hsotg);
-                               s3c_hsotg_core_connect(hsotg);
+                               s3c_hsotg_core_init_disconnected(hsotg, true);
                        }
                }
        }
@@ -2426,12 +2589,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
        struct dwc2_hsotg *hsotg = hs_ep->parent;
        unsigned long flags;
-       int index = hs_ep->index;
+       unsigned int index = hs_ep->index;
        u32 epctrl_reg;
        u32 epctrl;
        u32 mps;
-       int dir_in;
-       int i, val, size;
+       unsigned int dir_in;
+       unsigned int i, val, size;
        int ret = 0;
 
        dev_dbg(hsotg->dev,
@@ -2479,7 +2642,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        epctrl |= DXEPCTL_SNAK;
 
        /* update the endpoint state */
-       s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps);
+       s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in);
 
        /* default, set to non-periodic */
        hs_ep->isochronous = 0;
@@ -2515,30 +2678,48 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
                break;
        }
 
+       /* If fifo is already allocated for this ep */
+       if (hs_ep->fifo_index) {
+               size =  hs_ep->ep.maxpacket * hs_ep->mc;
+               /* If bigger fifo is required deallocate current one */
+               if (size > hs_ep->fifo_size) {
+                       hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
+                       hs_ep->fifo_index = 0;
+                       hs_ep->fifo_size = 0;
+               }
+       }
+
        /*
         * if the hardware has dedicated fifos, we must give each IN EP
         * a unique tx-fifo even if it is non-periodic.
         */
-       if (dir_in && hsotg->dedicated_fifos) {
+       if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) {
+               u32 fifo_index = 0;
+               u32 fifo_size = UINT_MAX;
                size = hs_ep->ep.maxpacket*hs_ep->mc;
-               for (i = 1; i <= 8; ++i) {
+               for (i = 1; i < hsotg->num_of_eps; ++i) {
                        if (hsotg->fifo_map & (1<<i))
                                continue;
                        val = readl(hsotg->regs + DPTXFSIZN(i));
                        val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
                        if (val < size)
                                continue;
-                       hsotg->fifo_map |= 1<<i;
-
-                       epctrl |= DXEPCTL_TXFNUM(i);
-                       hs_ep->fifo_index = i;
-                       hs_ep->fifo_size = val;
-                       break;
+                       /* Search for smallest acceptable fifo */
+                       if (val < fifo_size) {
+                               fifo_size = val;
+                               fifo_index = i;
+                       }
                }
-               if (i == 8) {
+               if (!fifo_index) {
+                       dev_err(hsotg->dev,
+                               "%s: No suitable fifo found\n", __func__);
                        ret = -ENOMEM;
                        goto error;
                }
+               hsotg->fifo_map |= 1 << fifo_index;
+               epctrl |= DXEPCTL_TXFNUM(fifo_index);
+               hs_ep->fifo_index = fifo_index;
+               hs_ep->fifo_size = fifo_size;
        }
 
        /* for non control endpoints, set PID to D0 */
@@ -2564,7 +2745,7 @@ error:
  * s3c_hsotg_ep_disable - disable given endpoint
  * @ep: The endpoint to disable.
  */
-static int s3c_hsotg_ep_disable(struct usb_ep *ep)
+static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
        struct dwc2_hsotg *hsotg = hs_ep->parent;
@@ -2576,7 +2757,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
 
        dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
 
-       if (ep == &hsotg->eps[0].ep) {
+       if (ep == &hsotg->eps_out[0]->ep) {
                dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
                return -EINVAL;
        }
@@ -2584,8 +2765,6 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
        epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
 
        spin_lock_irqsave(&hsotg->lock, flags);
-       /* terminate all requests with shutdown */
-       kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
 
        hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
        hs_ep->fifo_index = 0;
@@ -2602,10 +2781,17 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
        /* disable endpoint interrupts */
        s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
 
+       /* terminate all requests with shutdown */
+       kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
+
        spin_unlock_irqrestore(&hsotg->lock, flags);
        return 0;
 }
 
+static int s3c_hsotg_ep_disable(struct usb_ep *ep)
+{
+       return s3c_hsotg_ep_disable_force(ep, false);
+}
 /**
  * on_list - check request is on the given endpoint
  * @ep: The endpoint to check.
@@ -2675,40 +2861,39 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
                return 0;
        }
 
-       /* write both IN and OUT control registers */
-
-       epreg = DIEPCTL(index);
-       epctl = readl(hs->regs + epreg);
-
-       if (value) {
-               epctl |= DXEPCTL_STALL + DXEPCTL_SNAK;
-               if (epctl & DXEPCTL_EPENA)
-                       epctl |= DXEPCTL_EPDIS;
+       if (hs_ep->dir_in) {
+               epreg = DIEPCTL(index);
+               epctl = readl(hs->regs + epreg);
+
+               if (value) {
+                       epctl |= DXEPCTL_STALL + DXEPCTL_SNAK;
+                       if (epctl & DXEPCTL_EPENA)
+                               epctl |= DXEPCTL_EPDIS;
+               } else {
+                       epctl &= ~DXEPCTL_STALL;
+                       xfertype = epctl & DXEPCTL_EPTYPE_MASK;
+                       if (xfertype == DXEPCTL_EPTYPE_BULK ||
+                               xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+                                       epctl |= DXEPCTL_SETD0PID;
+               }
+               writel(epctl, hs->regs + epreg);
        } else {
-               epctl &= ~DXEPCTL_STALL;
-               xfertype = epctl & DXEPCTL_EPTYPE_MASK;
-               if (xfertype == DXEPCTL_EPTYPE_BULK ||
-                       xfertype == DXEPCTL_EPTYPE_INTERRUPT)
-                               epctl |= DXEPCTL_SETD0PID;
-       }
-
-       writel(epctl, hs->regs + epreg);
 
-       epreg = DOEPCTL(index);
-       epctl = readl(hs->regs + epreg);
+               epreg = DOEPCTL(index);
+               epctl = readl(hs->regs + epreg);
 
-       if (value)
-               epctl |= DXEPCTL_STALL;
-       else {
-               epctl &= ~DXEPCTL_STALL;
-               xfertype = epctl & DXEPCTL_EPTYPE_MASK;
-               if (xfertype == DXEPCTL_EPTYPE_BULK ||
-                       xfertype == DXEPCTL_EPTYPE_INTERRUPT)
-                               epctl |= DXEPCTL_SETD0PID;
+               if (value)
+                       epctl |= DXEPCTL_STALL;
+               else {
+                       epctl &= ~DXEPCTL_STALL;
+                       xfertype = epctl & DXEPCTL_EPTYPE_MASK;
+                       if (xfertype == DXEPCTL_EPTYPE_BULK ||
+                               xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+                                       epctl |= DXEPCTL_SETD0PID;
+               }
+               writel(epctl, hs->regs + epreg);
        }
 
-       writel(epctl, hs->regs + epreg);
-
        hs_ep->halted = value;
 
        return 0;
@@ -2794,6 +2979,7 @@ static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
  */
 static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
 {
+       u32 trdtim;
        /* unmask subset of endpoint interrupts */
 
        writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
@@ -2809,12 +2995,6 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
        /* Be in disconnected state until gadget is registered */
        __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
 
-       if (0) {
-               /* post global nak until we're ready */
-               writel(DCTL_SGNPINNAK | DCTL_SGOUTNAK,
-                      hsotg->regs + DCTL);
-       }
-
        /* setup fifos */
 
        dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
@@ -2824,8 +3004,10 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
        s3c_hsotg_init_fifo(hsotg);
 
        /* set the PLL on, remove the HNP/SRP and set the PHY */
-       writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10),
-              hsotg->regs + GUSBCFG);
+       trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
+       writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
+               (trdtim << GUSBCFG_USBTRDTIM_SHIFT),
+               hsotg->regs + GUSBCFG);
 
        if (using_dma(hsotg))
                __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
@@ -2887,7 +3069,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
 
        spin_lock_irqsave(&hsotg->lock, flags);
        s3c_hsotg_init(hsotg);
-       s3c_hsotg_core_init_disconnected(hsotg);
+       s3c_hsotg_core_init_disconnected(hsotg, false);
        hsotg->enabled = 0;
        spin_unlock_irqrestore(&hsotg->lock, flags);
 
@@ -2922,8 +3104,12 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
        mutex_lock(&hsotg->init_mutex);
 
        /* all endpoints should be shutdown */
-       for (ep = 1; ep < hsotg->num_of_eps; ep++)
-               s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+       for (ep = 1; ep < hsotg->num_of_eps; ep++) {
+               if (hsotg->eps_in[ep])
+                       s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+               if (hsotg->eps_out[ep])
+                       s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+       }
 
        spin_lock_irqsave(&hsotg->lock, flags);
 
@@ -2976,9 +3162,11 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
        if (is_on) {
                clk_enable(hsotg->clk);
                hsotg->enabled = 1;
+               s3c_hsotg_core_init_disconnected(hsotg, false);
                s3c_hsotg_core_connect(hsotg);
        } else {
                s3c_hsotg_core_disconnect(hsotg);
+               s3c_hsotg_disconnect(hsotg);
                hsotg->enabled = 0;
                clk_disable(hsotg->clk);
        }
@@ -2990,11 +3178,52 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
        return 0;
 }
 
+static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+       struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+       unsigned long flags;
+
+       dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active);
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       if (is_active) {
+               /* Kill any ep0 requests as controller will be reinitialized */
+               kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
+               s3c_hsotg_core_init_disconnected(hsotg, false);
+               if (hsotg->enabled)
+                       s3c_hsotg_core_connect(hsotg);
+       } else {
+               s3c_hsotg_core_disconnect(hsotg);
+               s3c_hsotg_disconnect(hsotg);
+       }
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       return 0;
+}
+
+/**
+ * s3c_hsotg_vbus_draw - report bMaxPower field
+ * @gadget: The usb gadget state
+ * @mA: Amount of current
+ *
+ * Report how much power the device may consume to the phy.
+ */
+static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+       struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+
+       if (IS_ERR_OR_NULL(hsotg->uphy))
+               return -ENOTSUPP;
+       return usb_phy_set_power(hsotg->uphy, mA);
+}
+
 static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
        .get_frame      = s3c_hsotg_gadget_getframe,
        .udc_start              = s3c_hsotg_udc_start,
        .udc_stop               = s3c_hsotg_udc_stop,
        .pullup                 = s3c_hsotg_pullup,
+       .vbus_session           = s3c_hsotg_vbus_session,
+       .vbus_draw              = s3c_hsotg_vbus_draw,
 };
 
 /**
@@ -3009,19 +3238,19 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
  */
 static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
                                       struct s3c_hsotg_ep *hs_ep,
-                                      int epnum)
+                                      int epnum,
+                                      bool dir_in)
 {
        char *dir;
 
        if (epnum == 0)
                dir = "";
-       else if ((epnum % 2) == 0) {
-               dir = "out";
-       } else {
+       else if (dir_in)
                dir = "in";
-               hs_ep->dir_in = 1;
-       }
+       else
+               dir = "out";
 
+       hs_ep->dir_in = dir_in;
        hs_ep->index = epnum;
 
        snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
@@ -3045,8 +3274,10 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
 
        if (using_dma(hsotg)) {
                u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
-               writel(next, hsotg->regs + DIEPCTL(epnum));
-               writel(next, hsotg->regs + DOEPCTL(epnum));
+               if (dir_in)
+                       writel(next, hsotg->regs + DIEPCTL(epnum));
+               else
+                       writel(next, hsotg->regs + DOEPCTL(epnum));
        }
 }
 
@@ -3056,24 +3287,56 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
  *
  * Read the USB core HW configuration registers
  */
-static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
+static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
 {
-       u32 cfg2, cfg3, cfg4;
+       u32 cfg;
+       u32 ep_type;
+       u32 i;
+
        /* check hardware configuration */
 
-       cfg2 = readl(hsotg->regs + 0x48);
-       hsotg->num_of_eps = (cfg2 >> 10) & 0xF;
+       cfg = readl(hsotg->regs + GHWCFG2);
+       hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
+       /* Add ep0 */
+       hsotg->num_of_eps++;
+
+       hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct s3c_hsotg_ep),
+                                                               GFP_KERNEL);
+       if (!hsotg->eps_in[0])
+               return -ENOMEM;
+       /* Same s3c_hsotg_ep is used in both directions for ep0 */
+       hsotg->eps_out[0] = hsotg->eps_in[0];
+
+       cfg = readl(hsotg->regs + GHWCFG1);
+       for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
+               ep_type = cfg & 3;
+               /* Direction in or both */
+               if (!(ep_type & 2)) {
+                       hsotg->eps_in[i] = devm_kzalloc(hsotg->dev,
+                               sizeof(struct s3c_hsotg_ep), GFP_KERNEL);
+                       if (!hsotg->eps_in[i])
+                               return -ENOMEM;
+               }
+               /* Direction out or both */
+               if (!(ep_type & 1)) {
+                       hsotg->eps_out[i] = devm_kzalloc(hsotg->dev,
+                               sizeof(struct s3c_hsotg_ep), GFP_KERNEL);
+                       if (!hsotg->eps_out[i])
+                               return -ENOMEM;
+               }
+       }
 
-       cfg3 = readl(hsotg->regs + 0x4C);
-       hsotg->fifo_mem = (cfg3 >> 16);
+       cfg = readl(hsotg->regs + GHWCFG3);
+       hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
 
-       cfg4 = readl(hsotg->regs + 0x50);
-       hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+       cfg = readl(hsotg->regs + GHWCFG4);
+       hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
 
        dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
                 hsotg->num_of_eps,
                 hsotg->dedicated_fifos ? "dedicated" : "shared",
                 hsotg->fifo_mem);
+       return 0;
 }
 
 /**
@@ -3092,22 +3355,22 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
                 readl(regs + DCFG), readl(regs + DCTL),
                 readl(regs + DIEPMSK));
 
-       dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n",
-                readl(regs + GAHBCFG), readl(regs + 0x44));
+       dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
+                readl(regs + GAHBCFG), readl(regs + GHWCFG1));
 
        dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
                 readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ));
 
        /* show periodic fifo settings */
 
-       for (idx = 1; idx <= 15; idx++) {
+       for (idx = 1; idx < hsotg->num_of_eps; idx++) {
                val = readl(regs + DPTXFSIZN(idx));
                dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
                         val >> FIFOSIZE_DEPTH_SHIFT,
                         val & FIFOSIZE_STARTADDR_MASK);
        }
 
-       for (idx = 0; idx < 15; idx++) {
+       for (idx = 0; idx < hsotg->num_of_eps; idx++) {
                dev_info(dev,
                         "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
                         readl(regs + DIEPCTL(idx)),
@@ -3129,6 +3392,103 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
 }
 
 /**
+ * testmode_write - debugfs: change usb test mode
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry modify the current usb test mode.
+ */
+static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
+               count, loff_t *ppos)
+{
+       struct seq_file         *s = file->private_data;
+       struct dwc2_hsotg       *hsotg = s->private;
+       unsigned long           flags;
+       u32                     testmode = 0;
+       char                    buf[32];
+
+       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+               return -EFAULT;
+
+       if (!strncmp(buf, "test_j", 6))
+               testmode = TEST_J;
+       else if (!strncmp(buf, "test_k", 6))
+               testmode = TEST_K;
+       else if (!strncmp(buf, "test_se0_nak", 12))
+               testmode = TEST_SE0_NAK;
+       else if (!strncmp(buf, "test_packet", 11))
+               testmode = TEST_PACKET;
+       else if (!strncmp(buf, "test_force_enable", 17))
+               testmode = TEST_FORCE_EN;
+       else
+               testmode = 0;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       s3c_hsotg_set_test_mode(hsotg, testmode);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       return count;
+}
+
+/**
+ * testmode_show - debugfs: show usb test mode state
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry shows which usb test mode is currently enabled.
+ */
+static int testmode_show(struct seq_file *s, void *unused)
+{
+       struct dwc2_hsotg *hsotg = s->private;
+       unsigned long flags;
+       int dctl;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       dctl = readl(hsotg->regs + DCTL);
+       dctl &= DCTL_TSTCTL_MASK;
+       dctl >>= DCTL_TSTCTL_SHIFT;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       switch (dctl) {
+       case 0:
+               seq_puts(s, "no test\n");
+               break;
+       case TEST_J:
+               seq_puts(s, "test_j\n");
+               break;
+       case TEST_K:
+               seq_puts(s, "test_k\n");
+               break;
+       case TEST_SE0_NAK:
+               seq_puts(s, "test_se0_nak\n");
+               break;
+       case TEST_PACKET:
+               seq_puts(s, "test_packet\n");
+               break;
+       case TEST_FORCE_EN:
+               seq_puts(s, "test_force_enable\n");
+               break;
+       default:
+               seq_printf(s, "UNKNOWN %d\n", dctl);
+       }
+
+       return 0;
+}
+
+static int testmode_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, testmode_show, inode->i_private);
+}
+
+static const struct file_operations testmode_fops = {
+       .owner          = THIS_MODULE,
+       .open           = testmode_open,
+       .write          = testmode_write,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/**
  * state_show - debugfs: show overall driver and device state.
  * @seq: The seq file to write to.
  * @v: Unused parameter.
@@ -3165,7 +3525,7 @@ static int state_show(struct seq_file *seq, void *v)
 
        seq_puts(seq, "\nEndpoint status:\n");
 
-       for (idx = 0; idx < 15; idx++) {
+       for (idx = 0; idx < hsotg->num_of_eps; idx++) {
                u32 in, out;
 
                in = readl(regs + DIEPCTL(idx));
@@ -3224,7 +3584,7 @@ static int fifo_show(struct seq_file *seq, void *v)
 
        seq_puts(seq, "\nPeriodic TXFIFOs:\n");
 
-       for (idx = 1; idx <= 15; idx++) {
+       for (idx = 1; idx < hsotg->num_of_eps; idx++) {
                val = readl(regs + DPTXFSIZN(idx));
 
                seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
@@ -3356,29 +3716,53 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
 
        /* create general state file */
 
-       hsotg->debug_file = debugfs_create_file("state", 0444, root,
+       hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
                                                hsotg, &state_fops);
 
        if (IS_ERR(hsotg->debug_file))
                dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
 
-       hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root,
+       hsotg->debug_testmode = debugfs_create_file("testmode",
+                                       S_IRUGO | S_IWUSR, root,
+                                       hsotg, &testmode_fops);
+
+       if (IS_ERR(hsotg->debug_testmode))
+               dev_err(hsotg->dev, "%s: failed to create testmode\n",
+                               __func__);
+
+       hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root,
                                                hsotg, &fifo_fops);
 
        if (IS_ERR(hsotg->debug_fifo))
                dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
 
-       /* create one file for each endpoint */
-
+       /* Create one file for each out endpoint */
        for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
-               struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
+               struct s3c_hsotg_ep *ep;
 
-               ep->debugfs = debugfs_create_file(ep->name, 0444,
-                                                 root, ep, &ep_fops);
+               ep = hsotg->eps_out[epidx];
+               if (ep) {
+                       ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
+                                                         root, ep, &ep_fops);
 
-               if (IS_ERR(ep->debugfs))
-                       dev_err(hsotg->dev, "failed to create %s debug file\n",
-                               ep->name);
+                       if (IS_ERR(ep->debugfs))
+                               dev_err(hsotg->dev, "failed to create %s debug file\n",
+                                       ep->name);
+               }
+       }
+       /* Create one file for each in endpoint. EP0 is handled with out eps */
+       for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
+               struct s3c_hsotg_ep *ep;
+
+               ep = hsotg->eps_in[epidx];
+               if (ep) {
+                       ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
+                                                         root, ep, &ep_fops);
+
+                       if (IS_ERR(ep->debugfs))
+                               dev_err(hsotg->dev, "failed to create %s debug file\n",
+                                       ep->name);
+               }
        }
 }
 
@@ -3393,11 +3777,14 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
        unsigned epidx;
 
        for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
-               struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
-               debugfs_remove(ep->debugfs);
+               if (hsotg->eps_in[epidx])
+                       debugfs_remove(hsotg->eps_in[epidx]->debugfs);
+               if (hsotg->eps_out[epidx])
+                       debugfs_remove(hsotg->eps_out[epidx]->debugfs);
        }
 
        debugfs_remove(hsotg->debug_file);
+       debugfs_remove(hsotg->debug_testmode);
        debugfs_remove(hsotg->debug_fifo);
        debugfs_remove(hsotg->debug_root);
 }
@@ -3406,9 +3793,42 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
 static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg)
 {
        struct device_node *np = hsotg->dev->of_node;
+       u32 len = 0;
+       u32 i = 0;
 
        /* Enable dma if requested in device tree */
        hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
+
+       /*
+       * Register TX periodic fifo size per endpoint.
+       * EP0 is excluded since it has no fifo configuration.
+       */
+       if (!of_find_property(np, "g-tx-fifo-size", &len))
+               goto rx_fifo;
+
+       len /= sizeof(u32);
+
+       /* Read tx fifo sizes other than ep0 */
+       if (of_property_read_u32_array(np, "g-tx-fifo-size",
+                                               &hsotg->g_tx_fifo_sz[1], len))
+               goto rx_fifo;
+
+       /* Add ep0 */
+       len++;
+
+       /* Make remaining TX fifos unavailable */
+       if (len < MAX_EPS_CHANNELS) {
+               for (i = len; i < MAX_EPS_CHANNELS; i++)
+                       hsotg->g_tx_fifo_sz[i] = 0;
+       }
+
+rx_fifo:
+       /* Register RX fifo size */
+       of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
+
+       /* Register NPTX fifo size */
+       of_property_read_u32(np, "g-np-tx-fifo-size",
+                                               &hsotg->g_np_g_tx_fifo_sz);
 }
 #else
 static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { }
@@ -3423,16 +3843,29 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 {
        struct device *dev = hsotg->dev;
        struct s3c_hsotg_plat *plat = dev->platform_data;
-       struct s3c_hsotg_ep *eps;
        int epnum;
        int ret;
        int i;
+       u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
 
        /* Set default UTMI width */
        hsotg->phyif = GUSBCFG_PHYIF16;
 
        s3c_hsotg_of_probe(hsotg);
 
+       /* Initialize to legacy fifo configuration values */
+       hsotg->g_rx_fifo_sz = 2048;
+       hsotg->g_np_g_tx_fifo_sz = 1024;
+       memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
+       /* Device tree specific probe */
+       s3c_hsotg_of_probe(hsotg);
+       /* Dump fifo information */
+       dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
+                                               hsotg->g_np_g_tx_fifo_sz);
+       dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
+       for (i = 0; i < MAX_EPS_CHANNELS; i++)
+               dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
+                                               hsotg->g_tx_fifo_sz[i]);
        /*
         * If platform probe couldn't find a generic PHY or an old style
         * USB PHY, fall back to pdata
@@ -3496,10 +3929,31 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
        /* usb phy enable */
        s3c_hsotg_phy_enable(hsotg);
 
+       /*
+        * Force Device mode before initialization.
+        * This allows correctly configuring fifo for device mode.
+        */
+       __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
+       __orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
+       /*
+        * According to Synopsys databook, this sleep is needed for the force
+        * device mode to take effect.
+        */
+       msleep(25);
+
        s3c_hsotg_corereset(hsotg);
-       s3c_hsotg_hw_cfg(hsotg);
+       ret = s3c_hsotg_hw_cfg(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
+               goto err_clk;
+       }
+
        s3c_hsotg_init(hsotg);
 
+       /* Switch back to default configuration */
+       __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
        hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
                        DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
        if (!hsotg->ctrl_buff) {
@@ -3535,33 +3989,30 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
                goto err_supplies;
        }
 
-       eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep),
-                     GFP_KERNEL);
-       if (!eps) {
-               ret = -ENOMEM;
-               goto err_supplies;
-       }
-
-       hsotg->eps = eps;
-
        /* setup endpoint information */
 
        INIT_LIST_HEAD(&hsotg->gadget.ep_list);
-       hsotg->gadget.ep0 = &hsotg->eps[0].ep;
+       hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;
 
        /* allocate EP0 request */
 
-       hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep,
+       hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep,
                                                     GFP_KERNEL);
        if (!hsotg->ctrl_req) {
                dev_err(dev, "failed to allocate ctrl req\n");
                ret = -ENOMEM;
-               goto err_ep_mem;
+               goto err_supplies;
        }
 
        /* initialise the endpoints now the core has been initialised */
-       for (epnum = 0; epnum < hsotg->num_of_eps; epnum++)
-               s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
+       for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
+               if (hsotg->eps_in[epnum])
+                       s3c_hsotg_initep(hsotg, hsotg->eps_in[epnum],
+                                                               epnum, 1);
+               if (hsotg->eps_out[epnum])
+                       s3c_hsotg_initep(hsotg, hsotg->eps_out[epnum],
+                                                               epnum, 0);
+       }
 
        /* disable power and clock */
        s3c_hsotg_phy_disable(hsotg);
@@ -3570,12 +4021,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
                                    hsotg->supplies);
        if (ret) {
                dev_err(dev, "failed to disable supplies: %d\n", ret);
-               goto err_ep_mem;
+               goto err_supplies;
        }
 
        ret = usb_add_gadget_udc(dev, &hsotg->gadget);
        if (ret)
-               goto err_ep_mem;
+               goto err_supplies;
 
        s3c_hsotg_create_debug(hsotg);
 
@@ -3583,8 +4034,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 
        return 0;
 
-err_ep_mem:
-       kfree(eps);
 err_supplies:
        s3c_hsotg_phy_disable(hsotg);
 err_clk:
@@ -3630,8 +4079,12 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
 
                s3c_hsotg_phy_disable(hsotg);
 
-               for (ep = 0; ep < hsotg->num_of_eps; ep++)
-                       s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+               for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+                       if (hsotg->eps_in[ep])
+                               s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+                       if (hsotg->eps_out[ep])
+                               s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+               }
 
                ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
                                             hsotg->supplies);
@@ -3662,7 +4115,7 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
                s3c_hsotg_phy_enable(hsotg);
 
                spin_lock_irqsave(&hsotg->lock, flags);
-               s3c_hsotg_core_init_disconnected(hsotg);
+               s3c_hsotg_core_init_disconnected(hsotg, false);
                if (hsotg->enabled)
                        s3c_hsotg_core_connect(hsotg);
                spin_unlock_irqrestore(&hsotg->lock, flags);