]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/usb/dwc2/gadget.c
usb: dwc2: gadget: add bi-directional endpoint support
[mirror_ubuntu-zesty-kernel.git] / drivers / usb / dwc2 / gadget.c
index 8b5c079c7b7dde3fb1eb0e9739e117a835c23176..530c2468dd66da54f27a1aea60723e2db8de2595 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/debugfs.h>
+#include <linux/mutex.h>
 #include <linux/seq_file.h>
 #include <linux/delay.h>
 #include <linux/io.h>
@@ -36,6 +37,7 @@
 #include <linux/platform_data/s3c-hsotg.h>
 
 #include "core.h"
+#include "hw.h"
 
 /* conversion functions */
 static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
@@ -48,9 +50,9 @@ static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
        return container_of(ep, struct s3c_hsotg_ep, ep);
 }
 
-static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
+static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
 {
-       return container_of(gadget, struct s3c_hsotg, gadget);
+       return container_of(gadget, struct dwc2_hsotg, gadget);
 }
 
 static inline void __orr32(void __iomem *ptr, u32 val)
@@ -63,8 +65,17 @@ static inline void __bic32(void __iomem *ptr, u32 val)
        writel(readl(ptr) & ~val, ptr);
 }
 
-/* forward decleration of functions */
-static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
+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);
 
 /**
  * using_dma - return the DMA status of the driver.
@@ -83,11 +94,11 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
  * a core reset. This means we either need to fix the gadgets to take
  * account of DMA alignment, or add bounce buffers (yuerk).
  *
- * Until this issue is sorted out, we always return 'false'.
+ * g_using_dma is set depending on dts flag.
  */
-static inline bool using_dma(struct s3c_hsotg *hsotg)
+static inline bool using_dma(struct dwc2_hsotg *hsotg)
 {
-       return false;   /* support is not complete */
+       return hsotg->g_using_dma;
 }
 
 /**
@@ -95,7 +106,7 @@ static inline bool using_dma(struct s3c_hsotg *hsotg)
  * @hsotg: The device state
  * @ints: A bitmask of the interrupts to enable
  */
-static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
+static void s3c_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
 {
        u32 gsintmsk = readl(hsotg->regs + GINTMSK);
        u32 new_gsintmsk;
@@ -113,7 +124,7 @@ static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
  * @hsotg: The device state
  * @ints: A bitmask of the interrupts to enable
  */
-static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
+static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
 {
        u32 gsintmsk = readl(hsotg->regs + GINTMSK);
        u32 new_gsintmsk;
@@ -134,7 +145,7 @@ static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
  * Set or clear the mask for an individual endpoint's interrupt
  * request.
  */
-static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
                                 unsigned int ep, unsigned int dir_in,
                                 unsigned int en)
 {
@@ -159,7 +170,7 @@ static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
  * s3c_hsotg_init_fifo - initialise non-periodic FIFOs
  * @hsotg: The device instance.
  */
-static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
 {
        unsigned int ep;
        unsigned int addr;
@@ -283,7 +294,7 @@ static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
  * This is the reverse of s3c_hsotg_map_dma(), called for the completion
  * of a request to ensure the buffer is ready for access by the caller.
  */
-static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
                                struct s3c_hsotg_ep *hs_ep,
                                struct s3c_hsotg_req *hs_req)
 {
@@ -312,7 +323,7 @@ static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
  *
  * This routine is only needed for PIO
  */
-static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
                                struct s3c_hsotg_ep *hs_ep,
                                struct s3c_hsotg_req *hs_req)
 {
@@ -517,7 +528,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
  * Start the given request running by setting the endpoint registers
  * appropriately, and writing any data to the FIFOs.
  */
-static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
                                struct s3c_hsotg_ep *hs_ep,
                                struct s3c_hsotg_req *hs_req,
                                bool continuing)
@@ -707,7 +718,7 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
  * DMA memory, then we map the memory and mark our request to allow us to
  * cleanup on completion.
  */
-static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg,
                             struct s3c_hsotg_ep *hs_ep,
                             struct usb_request *req)
 {
@@ -736,7 +747,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
 {
        struct s3c_hsotg_req *hs_req = our_req(req);
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hs = hs_ep->parent;
+       struct dwc2_hsotg *hs = hs_ep->parent;
        bool first;
 
        dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
@@ -768,7 +779,7 @@ static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
                              gfp_t gfp_flags)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hs = hs_ep->parent;
+       struct dwc2_hsotg *hs = hs_ep->parent;
        unsigned long flags = 0;
        int ret = 0;
 
@@ -799,7 +810,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
                                        struct usb_request *req)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hsotg = hs_ep->parent;
+       struct dwc2_hsotg *hsotg = hs_ep->parent;
 
        dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
 
@@ -814,10 +825,10 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
  * Convert the given wIndex into a pointer to an driver endpoint
  * structure, or return NULL if it is not a valid endpoint.
  */
-static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
+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;
 
@@ -827,6 +838,8 @@ static struct s3c_hsotg_ep *ep_from_windex(struct s3c_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;
 
@@ -843,7 +856,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
  * Create a request and queue it on the given endpoint. This is useful as
  * an internal method of sending replies to certain control requests, etc.
  */
-static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
                                struct s3c_hsotg_ep *ep,
                                void *buff,
                                int length)
@@ -884,10 +897,10 @@ static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
  * @hsotg: The device state
  * @ctrl: USB control request
  */
-static int s3c_hsotg_process_req_status(struct s3c_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;
@@ -955,10 +968,10 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
  * @hsotg: The device state
  * @ctrl: USB control request
  */
-static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
+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);
@@ -1028,8 +1041,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
        return 1;
 }
 
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
-static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
+static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
 
 /**
  * s3c_hsotg_stall_ep0 - stall ep0
@@ -1037,9 +1049,9 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
  *
  * Set stall for ep0 as response for setup request.
  */
-static void s3c_hsotg_stall_ep0(struct s3c_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;
 
@@ -1076,10 +1088,10 @@ static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
  * needs to work out what to do next (and whether to pass it on to the
  * gadget driver).
  */
-static void s3c_hsotg_process_control(struct s3c_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;
 
@@ -1107,7 +1119,6 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
        if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
                switch (ctrl->bRequest) {
                case USB_REQ_SET_ADDRESS:
-                       s3c_hsotg_disconnect(hsotg);
                        dcfg = readl(hsotg->regs + DCFG);
                        dcfg &= ~DCFG_DEVADDR_MASK;
                        dcfg |= (le16_to_cpu(ctrl->wValue) <<
@@ -1161,7 +1172,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
                                     struct usb_request *req)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hsotg = hs_ep->parent;
+       struct dwc2_hsotg *hsotg = hs_ep->parent;
 
        if (req->status < 0) {
                dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
@@ -1183,7 +1194,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
  * Enqueue a request on EP0 if necessary to received any SETUP packets
  * received from the host.
  */
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
 {
        struct usb_request *req = hsotg->ctrl_req;
        struct s3c_hsotg_req *hs_req = our_req(req);
@@ -1201,9 +1212,9 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
                return;
        }
 
-       hsotg->eps[0].dir_in = 0;
+       hsotg->eps_out[0]->dir_in = 0;
 
-       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);
                /*
@@ -1226,7 +1237,7 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
  *
  * Note, expects the ep to already be locked as appropriate.
  */
-static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
                                       struct s3c_hsotg_ep *hs_ep,
                                       struct s3c_hsotg_req *hs_req,
                                       int result)
@@ -1291,9 +1302,9 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
  * endpoint, so sort out whether we need to read the data into a request
  * that has been made for that endpoint.
  */
-static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
+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;
@@ -1305,7 +1316,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
                u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
                int ptr;
 
-               dev_warn(hsotg->dev,
+               dev_dbg(hsotg->dev,
                         "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
                         __func__, size, ep_idx, epctl);
 
@@ -1356,7 +1367,7 @@ static void s3c_hsotg_rx_data(struct s3c_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 s3c_hsotg *hsotg,
+static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
                               struct s3c_hsotg_req *req)
 {
        u32 ctrl;
@@ -1367,13 +1378,14 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
        }
 
        if (req->req.length == 0) {
-               hsotg->eps[0].sent_zlp = 1;
+               hsotg->eps_out[0]->sent_zlp = 1;
                s3c_hsotg_enqueue_setup(hsotg);
                return;
        }
 
-       hsotg->eps[0].dir_in = 1;
-       hsotg->eps[0].sent_zlp = 1;
+       /* eps_out[0] is used in both directions */
+       hsotg->eps_out[0]->dir_in = 1;
+       hsotg->eps_out[0]->sent_zlp = 1;
 
        dev_dbg(hsotg->dev, "sending zero-length packet\n");
 
@@ -1398,11 +1410,11 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
  * 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 s3c_hsotg *hsotg,
+static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
                                     int epnum, bool was_setup)
 {
        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);
@@ -1471,7 +1483,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
  *
  * Return the current frame number
  */
-static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
+static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
 {
        u32 dsts;
 
@@ -1498,7 +1510,7 @@ static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
  * as the actual data should be sent to the memory directly and we turn
  * on the completion interrupts to get notifications of transfer completion.
  */
-static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
 {
        u32 grxstsr = readl(hsotg->regs + GRXSTSP);
        u32 epnum, status, size;
@@ -1590,15 +1602,19 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
  * Configure the maximum packet size for the given endpoint, updating
  * the hardware control registers to reflect this.
  */
-static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
-                                      unsigned int ep, unsigned int mps)
+static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
+                       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 +1633,12 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_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;
@@ -1645,7 +1656,7 @@ bad_mps:
  * @hsotg: The driver state
  * @idx: The index for the endpoint (0..15)
  */
-static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
+static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
 {
        int timeout;
        int val;
@@ -1681,7 +1692,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
  * Check to see if there is a request that has data to send, and if so
  * make an attempt to write data into the FIFO.
  */
-static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg,
                           struct s3c_hsotg_ep *hs_ep)
 {
        struct s3c_hsotg_req *hs_req = hs_ep->req;
@@ -1714,7 +1725,7 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
  * An IN transfer has been completed, update the transfer's state and then
  * call the relevant completion routines.
  */
-static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
                                  struct s3c_hsotg_ep *hs_ep)
 {
        struct s3c_hsotg_req *hs_req = hs_ep->req;
@@ -1727,7 +1738,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
        }
 
        /* Finish ZLP handling for IN EP0 transactions */
-       if (hsotg->eps[0].sent_zlp) {
+       if (hsotg->eps_out[0]->sent_zlp) {
                dev_dbg(hsotg->dev, "zlp packet received\n");
                s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
                return;
@@ -1791,10 +1802,10 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
  *
  * Process and clear any interrupt pending for an individual endpoint
  */
-static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
+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,9 +1818,19 @@ static void s3c_hsotg_epint(struct s3c_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);
 
+       /* Don't process XferCompl interrupt if it is a setup packet */
+       if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD)))
+               ints &= ~DXEPINT_XFERCOMPL;
+
        if (ints & DXEPINT_XFERCOMPL) {
                if (hs_ep->isochronous && hs_ep->interval == 1) {
                        if (ctrl & DXEPCTL_EOFRNUM)
@@ -1916,7 +1937,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
  * Handle updating the device settings after the enumeration phase has
  * been completed.
  */
-static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
 {
        u32 dsts = readl(hsotg->regs + DSTS);
        int ep0_mps = 0, ep_mps = 8;
@@ -1969,9 +1990,15 @@ static void s3c_hsotg_irq_enumdone(struct s3c_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 */
@@ -1988,30 +2015,23 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
  * @hsotg: The device state.
  * @ep: The endpoint the requests may be on.
  * @result: The result code to use.
- * @force: Force removal of any current requests
  *
  * Go through the requests on the given endpoint and mark them
  * completed with the given result code.
  */
-static void kill_all_requests(struct s3c_hsotg *hsotg,
+static void kill_all_requests(struct dwc2_hsotg *hsotg,
                              struct s3c_hsotg_ep *ep,
-                             int result, bool force)
+                             int result)
 {
        struct s3c_hsotg_req *req, *treq;
        unsigned size;
 
-       list_for_each_entry_safe(req, treq, &ep->queue, queue) {
-               /*
-                * currently, we can't do much about an already
-                * running request on an in endpoint
-                */
-
-               if (ep->req == req && ep->dir_in && !force)
-                       continue;
+       ep->req = NULL;
 
+       list_for_each_entry_safe(req, treq, &ep->queue, queue)
                s3c_hsotg_complete_request(hsotg, ep, req,
                                           result);
-       }
+
        if (!hsotg->dedicated_fifos)
                return;
        size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
@@ -2027,30 +2047,44 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
  * transactions and signal the gadget driver that this
  * has happened.
  */
-static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg)
+void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
 {
        unsigned ep;
 
-       for (ep = 0; ep < hsotg->num_of_eps; ep++)
-               kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
+       if (!hsotg->connected)
+               return;
+
+       hsotg->connected = 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);
 }
+EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect);
 
 /**
  * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
  * @hsotg: The device state:
  * @periodic: True if this is a periodic FIFO interrupt
  */
-static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
+static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
 {
        struct s3c_hsotg_ep *ep;
        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;
@@ -2076,7 +2110,7 @@ static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
  *
  * Issue a soft reset to the core, and await the core finishing it.
  */
-static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
+static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
 {
        int timeout;
        u32 grstctl;
@@ -2124,7 +2158,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
  *
  * Issue a soft reset to the core, and await the core finishing it.
  */
-static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
+void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 {
        s3c_hsotg_corereset(hsotg);
 
@@ -2158,7 +2192,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
 
        if (using_dma(hsotg))
                writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
-                      GAHBCFG_HBSTLEN_INCR4,
+                      (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
                       hsotg->regs + GAHBCFG);
        else
                writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL |
@@ -2172,8 +2206,8 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
         * interrupts.
         */
 
-       writel(((hsotg->dedicated_fifos) ? DIEPMSK_TXFIFOEMPTY |
-               DIEPMSK_INTKNTXFEMPMSK : 0) |
+       writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
+               DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
                DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
                DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
                DIEPMSK_INTKNEPMISMSK,
@@ -2225,13 +2259,13 @@ static void s3c_hsotg_core_init(struct s3c_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);
@@ -2241,12 +2275,23 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
                readl(hsotg->regs + DOEPCTL0));
 
        /* clear global NAKs */
-       writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK,
+       writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
               hsotg->regs + DCTL);
 
        /* must be at-least 3ms to allow bus to see disconnect */
        mdelay(3);
 
+       hsotg->last_rst = jiffies;
+}
+
+static void s3c_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
+{
+       /* set the soft-disconnect bit */
+       __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+}
+
+void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg)
+{
        /* remove the soft-disconnect and let's go */
        __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
 }
@@ -2258,7 +2303,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
  */
 static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
 {
-       struct s3c_hsotg *hsotg = pw;
+       struct dwc2_hsotg *hsotg = pw;
        int retry_count = 8;
        u32 gintsts;
        u32 gintmsk;
@@ -2273,31 +2318,11 @@ irq_retry:
 
        gintsts &= gintmsk;
 
-       if (gintsts & GINTSTS_OTGINT) {
-               u32 otgint = readl(hsotg->regs + GOTGINT);
-
-               dev_info(hsotg->dev, "OTGInt: %08x\n", otgint);
-
-               writel(otgint, hsotg->regs + GOTGINT);
-       }
-
-       if (gintsts & GINTSTS_SESSREQINT) {
-               dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__);
-               writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
-       }
-
        if (gintsts & GINTSTS_ENUMDONE) {
                writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
 
                s3c_hsotg_irq_enumdone(hsotg);
-       }
-
-       if (gintsts & GINTSTS_CONIDSTSCHNG) {
-               dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n",
-                       readl(hsotg->regs + DSTS),
-                       readl(hsotg->regs + GOTGCTL));
-
-               writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
+               hsotg->connected = 1;
        }
 
        if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
@@ -2337,11 +2362,11 @@ irq_retry:
                        if (time_after(jiffies, hsotg->last_rst +
                                       msecs_to_jiffies(200))) {
 
-                               kill_all_requests(hsotg, &hsotg->eps[0],
-                                                         -ECONNRESET, true);
+                               kill_all_requests(hsotg, hsotg->eps_out[0],
+                                                         -ECONNRESET);
 
-                               s3c_hsotg_core_init(hsotg);
-                               hsotg->last_rst = jiffies;
+                               s3c_hsotg_core_init_disconnected(hsotg);
+                               s3c_hsotg_core_connect(hsotg);
                        }
                }
        }
@@ -2380,25 +2405,6 @@ irq_retry:
                s3c_hsotg_handle_rx(hsotg);
        }
 
-       if (gintsts & GINTSTS_MODEMIS) {
-               dev_warn(hsotg->dev, "warning, mode mismatch triggered\n");
-               writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
-       }
-
-       if (gintsts & GINTSTS_USBSUSP) {
-               dev_info(hsotg->dev, "GINTSTS_USBSusp\n");
-               writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
-
-               call_gadget(hsotg, suspend);
-       }
-
-       if (gintsts & GINTSTS_WKUPINT) {
-               dev_info(hsotg->dev, "GINTSTS_WkUpIn\n");
-               writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
-
-               call_gadget(hsotg, resume);
-       }
-
        if (gintsts & GINTSTS_ERLYSUSP) {
                dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
                writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS);
@@ -2450,7 +2456,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
                               const struct usb_endpoint_descriptor *desc)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hsotg = hs_ep->parent;
+       struct dwc2_hsotg *hsotg = hs_ep->parent;
        unsigned long flags;
        int index = hs_ep->index;
        u32 epctrl_reg;
@@ -2505,7 +2511,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;
@@ -2593,7 +2599,7 @@ error:
 static int s3c_hsotg_ep_disable(struct usb_ep *ep)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hsotg = hs_ep->parent;
+       struct dwc2_hsotg *hsotg = hs_ep->parent;
        int dir_in = hs_ep->dir_in;
        int index = hs_ep->index;
        unsigned long flags;
@@ -2602,7 +2608,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;
        }
@@ -2611,7 +2617,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
 
        spin_lock_irqsave(&hsotg->lock, flags);
        /* terminate all requests with shutdown */
-       kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
+       kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
 
        hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
        hs_ep->fifo_index = 0;
@@ -2658,7 +2664,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 {
        struct s3c_hsotg_req *hs_req = our_req(req);
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hs = hs_ep->parent;
+       struct dwc2_hsotg *hs = hs_ep->parent;
        unsigned long flags;
 
        dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
@@ -2684,7 +2690,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hs = hs_ep->parent;
+       struct dwc2_hsotg *hs = hs_ep->parent;
        int index = hs_ep->index;
        u32 epreg;
        u32 epctl;
@@ -2701,40 +2707,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;
@@ -2748,7 +2753,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
 {
        struct s3c_hsotg_ep *hs_ep = our_ep(ep);
-       struct s3c_hsotg *hs = hs_ep->parent;
+       struct dwc2_hsotg *hs = hs_ep->parent;
        unsigned long flags = 0;
        int ret = 0;
 
@@ -2777,7 +2782,7 @@ static struct usb_ep_ops s3c_hsotg_ep_ops = {
  * A wrapper for platform code responsible for controlling
  * low-level USB code
  */
-static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_phy_enable(struct dwc2_hsotg *hsotg)
 {
        struct platform_device *pdev = to_platform_device(hsotg->dev);
 
@@ -2800,7 +2805,7 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
  * A wrapper for platform code responsible for controlling
  * low-level USB code
  */
-static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
 {
        struct platform_device *pdev = to_platform_device(hsotg->dev);
 
@@ -2818,7 +2823,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
  * s3c_hsotg_init - initalize the usb core
  * @hsotg: The driver state
  */
-static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
 {
        /* unmask subset of endpoint interrupts */
 
@@ -2853,8 +2858,8 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
        writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10),
               hsotg->regs + GUSBCFG);
 
-       writel(using_dma(hsotg) ? GAHBCFG_DMA_EN : 0x0,
-              hsotg->regs + GAHBCFG);
+       if (using_dma(hsotg))
+               __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
 }
 
 /**
@@ -2868,7 +2873,8 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
 static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
                           struct usb_gadget_driver *driver)
 {
-       struct s3c_hsotg *hsotg = to_hsotg(gadget);
+       struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+       unsigned long flags;
        int ret;
 
        if (!hsotg) {
@@ -2889,6 +2895,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
                return -EINVAL;
        }
 
+       mutex_lock(&hsotg->init_mutex);
        WARN_ON(hsotg->driver);
 
        driver->driver.bus = NULL;
@@ -2905,11 +2912,24 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
                goto err;
        }
 
-       hsotg->last_rst = jiffies;
+       s3c_hsotg_phy_enable(hsotg);
+       if (!IS_ERR_OR_NULL(hsotg->uphy))
+               otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       s3c_hsotg_init(hsotg);
+       s3c_hsotg_core_init_disconnected(hsotg);
+       hsotg->enabled = 0;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
        dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
+
+       mutex_unlock(&hsotg->init_mutex);
+
        return 0;
 
 err:
+       mutex_unlock(&hsotg->init_mutex);
        hsotg->driver = NULL;
        return ret;
 }
@@ -2921,31 +2941,43 @@ err:
  *
  * Stop udc hw block and stay tunned for future transmissions
  */
-static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
-                         struct usb_gadget_driver *driver)
+static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
 {
-       struct s3c_hsotg *hsotg = to_hsotg(gadget);
+       struct dwc2_hsotg *hsotg = to_hsotg(gadget);
        unsigned long flags = 0;
        int ep;
 
        if (!hsotg)
                return -ENODEV;
 
+       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);
 
        hsotg->driver = NULL;
        hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+       hsotg->enabled = 0;
 
        spin_unlock_irqrestore(&hsotg->lock, flags);
 
+       if (!IS_ERR_OR_NULL(hsotg->uphy))
+               otg_set_peripheral(hsotg->uphy->otg, NULL);
+       s3c_hsotg_phy_disable(hsotg);
+
        regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 
        clk_disable(hsotg->clk);
 
+       mutex_unlock(&hsotg->init_mutex);
+
        return 0;
 }
 
@@ -2969,23 +3001,26 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
  */
 static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
 {
-       struct s3c_hsotg *hsotg = to_hsotg(gadget);
+       struct dwc2_hsotg *hsotg = to_hsotg(gadget);
        unsigned long flags = 0;
 
        dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on);
 
+       mutex_lock(&hsotg->init_mutex);
        spin_lock_irqsave(&hsotg->lock, flags);
        if (is_on) {
-               s3c_hsotg_phy_enable(hsotg);
                clk_enable(hsotg->clk);
-               s3c_hsotg_core_init(hsotg);
+               hsotg->enabled = 1;
+               s3c_hsotg_core_connect(hsotg);
        } else {
+               s3c_hsotg_core_disconnect(hsotg);
+               hsotg->enabled = 0;
                clk_disable(hsotg->clk);
-               s3c_hsotg_phy_disable(hsotg);
        }
 
        hsotg->gadget.speed = USB_SPEED_UNKNOWN;
        spin_unlock_irqrestore(&hsotg->lock, flags);
+       mutex_unlock(&hsotg->init_mutex);
 
        return 0;
 }
@@ -3007,21 +3042,21 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
  * creation) to give to the gadget driver. Setup the endpoint name, any
  * direction information and other state that may be required.
  */
-static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
+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 +3080,10 @@ static void s3c_hsotg_initep(struct s3c_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,31 +3093,63 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
  *
  * Read the USB core HW configuration registers
  */
-static void s3c_hsotg_hw_cfg(struct s3c_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 >> 10) & 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; 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 >> 16);
 
-       cfg4 = readl(hsotg->regs + 0x50);
-       hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+       cfg = readl(hsotg->regs + GHWCFG4);
+       hsotg->dedicated_fifos = (cfg >> 25) & 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;
 }
 
 /**
  * s3c_hsotg_dump - dump state of the udc
  * @param: The device state
  */
-static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
 {
 #ifdef DEBUG
        struct device *dev = hsotg->dev;
@@ -3139,7 +3208,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
  */
 static int state_show(struct seq_file *seq, void *v)
 {
-       struct s3c_hsotg *hsotg = seq->private;
+       struct dwc2_hsotg *hsotg = seq->private;
        void __iomem *regs = hsotg->regs;
        int idx;
 
@@ -3209,7 +3278,7 @@ static const struct file_operations state_fops = {
  */
 static int fifo_show(struct seq_file *seq, void *v)
 {
-       struct s3c_hsotg *hsotg = seq->private;
+       struct dwc2_hsotg *hsotg = seq->private;
        void __iomem *regs = hsotg->regs;
        u32 val;
        int idx;
@@ -3265,7 +3334,7 @@ static const char *decode_direction(int is_in)
 static int ep_show(struct seq_file *seq, void *v)
 {
        struct s3c_hsotg_ep *ep = seq->private;
-       struct s3c_hsotg *hsotg = ep->parent;
+       struct dwc2_hsotg *hsotg = ep->parent;
        struct s3c_hsotg_req *req;
        void __iomem *regs = hsotg->regs;
        int index = ep->index;
@@ -3342,7 +3411,7 @@ static const struct file_operations ep_fops = {
  * with the same name as the device itself, in case we end up
  * with multiple blocks in future systems.
  */
-static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
 {
        struct dentry *root;
        unsigned epidx;
@@ -3368,17 +3437,33 @@ static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
        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, 0444,
+                                                         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, 0444,
+                                                         root, ep, &ep_fops);
+
+                       if (IS_ERR(ep->debugfs))
+                               dev_err(hsotg->dev, "failed to create %s debug file\n",
+                                       ep->name);
+               }
        }
 }
 
@@ -3388,13 +3473,15 @@ static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
  *
  * Cleanup (remove) the debugfs files for use on module exit.
  */
-static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
+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);
@@ -3402,96 +3489,75 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
        debugfs_remove(hsotg->debug_root);
 }
 
+#ifdef CONFIG_OF
+static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg)
+{
+       struct device_node *np = hsotg->dev->of_node;
+
+       /* Enable dma if requested in device tree */
+       hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
+}
+#else
+static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { }
+#endif
+
 /**
- * s3c_hsotg_probe - probe function for hsotg driver
- * @pdev: The platform information for the driver
+ * dwc2_gadget_init - init function for gadget
+ * @dwc2: The data structure for the DWC2 driver.
+ * @irq: The IRQ number for the controller.
  */
-
-static int s3c_hsotg_probe(struct platform_device *pdev)
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 {
-       struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev);
-       struct phy *phy;
-       struct usb_phy *uphy;
-       struct device *dev = &pdev->dev;
-       struct s3c_hsotg_ep *eps;
-       struct s3c_hsotg *hsotg;
-       struct resource *res;
+       struct device *dev = hsotg->dev;
+       struct s3c_hsotg_plat *plat = dev->platform_data;
        int epnum;
        int ret;
        int i;
 
-       hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
-       if (!hsotg)
-               return -ENOMEM;
-
        /* Set default UTMI width */
        hsotg->phyif = GUSBCFG_PHYIF16;
 
+       s3c_hsotg_of_probe(hsotg);
+
        /*
-        * Attempt to find a generic PHY, then look for an old style
-        * USB PHY, finally fall back to pdata
+        * If platform probe couldn't find a generic PHY or an old style
+        * USB PHY, fall back to pdata
         */
-       phy = devm_phy_get(&pdev->dev, "usb2-phy");
-       if (IS_ERR(phy)) {
-               uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-               if (IS_ERR(uphy)) {
-                       /* Fallback for pdata */
-                       plat = dev_get_platdata(&pdev->dev);
-                       if (!plat) {
-                               dev_err(&pdev->dev,
-                               "no platform data or transceiver defined\n");
-                               return -EPROBE_DEFER;
-                       }
-                       hsotg->plat = plat;
-               } else
-                       hsotg->uphy = uphy;
-       } else {
-               hsotg->phy = phy;
+       if (IS_ERR_OR_NULL(hsotg->phy) && IS_ERR_OR_NULL(hsotg->uphy)) {
+               plat = dev_get_platdata(dev);
+               if (!plat) {
+                       dev_err(dev,
+                       "no platform data or transceiver defined\n");
+                       return -EPROBE_DEFER;
+               }
+               hsotg->plat = plat;
+       } else if (hsotg->phy) {
                /*
                 * If using the generic PHY framework, check if the PHY bus
                 * width is 8-bit and set the phyif appropriately.
                 */
-               if (phy_get_bus_width(phy) == 8)
+               if (phy_get_bus_width(hsotg->phy) == 8)
                        hsotg->phyif = GUSBCFG_PHYIF8;
        }
 
-       hsotg->dev = dev;
-
-       hsotg->clk = devm_clk_get(&pdev->dev, "otg");
+       hsotg->clk = devm_clk_get(dev, "otg");
        if (IS_ERR(hsotg->clk)) {
-               dev_err(dev, "cannot get otg clock\n");
-               return PTR_ERR(hsotg->clk);
-       }
-
-       platform_set_drvdata(pdev, hsotg);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       hsotg->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(hsotg->regs)) {
-               ret = PTR_ERR(hsotg->regs);
-               goto err_clk;
-       }
-
-       ret = platform_get_irq(pdev, 0);
-       if (ret < 0) {
-               dev_err(dev, "cannot find IRQ\n");
-               goto err_clk;
+               hsotg->clk = NULL;
+               dev_dbg(dev, "cannot get otg clock\n");
        }
 
-       spin_lock_init(&hsotg->lock);
-
-       hsotg->irq = ret;
-
-       dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
-
        hsotg->gadget.max_speed = USB_SPEED_HIGH;
        hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
        hsotg->gadget.name = dev_name(dev);
 
        /* reset the system */
 
-       clk_prepare_enable(hsotg->clk);
+       ret = clk_prepare_enable(hsotg->clk);
+       if (ret) {
+               dev_err(dev, "failed to enable otg clk\n");
+               goto err_clk;
+       }
+
 
        /* regulators */
 
@@ -3509,26 +3575,47 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
                                    hsotg->supplies);
 
        if (ret) {
-               dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret);
-               goto err_supplies;
+               dev_err(dev, "failed to enable supplies: %d\n", ret);
+               goto err_clk;
        }
 
        /* usb phy enable */
        s3c_hsotg_phy_enable(hsotg);
 
        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);
 
-       ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
-                               dev_name(dev), hsotg);
+       hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
+                       DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
+       if (!hsotg->ctrl_buff) {
+               dev_err(dev, "failed to allocate ctrl request buff\n");
+               ret = -ENOMEM;
+               goto err_supplies;
+       }
+
+       hsotg->ep0_buff = devm_kzalloc(hsotg->dev,
+                       DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
+       if (!hsotg->ep0_buff) {
+               dev_err(dev, "failed to allocate ctrl reply buff\n");
+               ret = -ENOMEM;
+               goto err_supplies;
+       }
+
+       ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
+                               dev_name(hsotg->dev), hsotg);
        if (ret < 0) {
                s3c_hsotg_phy_disable(hsotg);
                clk_disable_unprepare(hsotg->clk);
                regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
                                       hsotg->supplies);
-               dev_err(dev, "cannot claim IRQ\n");
-               goto err_clk;
+               dev_err(dev, "cannot claim IRQ for gadget\n");
+               goto err_supplies;
        }
 
        /* hsotg->num_of_eps holds number of EPs other than ep0 */
@@ -3539,33 +3626,30 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
                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);
@@ -3573,13 +3657,13 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
        ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
                                    hsotg->supplies);
        if (ret) {
-               dev_err(hsotg->dev, "failed to disable supplies: %d\n", ret);
-               goto err_ep_mem;
+               dev_err(dev, "failed to disable supplies: %d\n", ret);
+               goto err_supplies;
        }
 
-       ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
+       ret = usb_add_gadget_udc(dev, &hsotg->gadget);
        if (ret)
-               goto err_ep_mem;
+               goto err_supplies;
 
        s3c_hsotg_create_debug(hsotg);
 
@@ -3587,8 +3671,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
 
        return 0;
 
-err_ep_mem:
-       kfree(eps);
 err_supplies:
        s3c_hsotg_phy_disable(hsotg);
 err_clk:
@@ -3596,106 +3678,87 @@ err_clk:
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(dwc2_gadget_init);
 
 /**
  * s3c_hsotg_remove - remove function for hsotg driver
  * @pdev: The platform information for the driver
  */
-static int s3c_hsotg_remove(struct platform_device *pdev)
+int s3c_hsotg_remove(struct dwc2_hsotg *hsotg)
 {
-       struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
-
        usb_del_gadget_udc(&hsotg->gadget);
-
        s3c_hsotg_delete_debug(hsotg);
-
-       if (hsotg->driver) {
-               /* should have been done already by driver model core */
-               usb_gadget_unregister_driver(hsotg->driver);
-       }
-
        clk_disable_unprepare(hsotg->clk);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(s3c_hsotg_remove);
 
-static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
+int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
 {
-       struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
        unsigned long flags;
        int ret = 0;
 
-       if (hsotg->driver)
+       mutex_lock(&hsotg->init_mutex);
+
+       if (hsotg->driver) {
+               int ep;
+
                dev_info(hsotg->dev, "suspending usb gadget %s\n",
                         hsotg->driver->driver.name);
 
-       spin_lock_irqsave(&hsotg->lock, flags);
-       s3c_hsotg_disconnect(hsotg);
-       s3c_hsotg_phy_disable(hsotg);
-       hsotg->gadget.speed = USB_SPEED_UNKNOWN;
-       spin_unlock_irqrestore(&hsotg->lock, flags);
+               spin_lock_irqsave(&hsotg->lock, flags);
+               if (hsotg->enabled)
+                       s3c_hsotg_core_disconnect(hsotg);
+               s3c_hsotg_disconnect(hsotg);
+               hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+               spin_unlock_irqrestore(&hsotg->lock, flags);
 
-       if (hsotg->driver) {
-               int ep;
-               for (ep = 0; ep < hsotg->num_of_eps; ep++)
-                       s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+               s3c_hsotg_phy_disable(hsotg);
+
+               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);
                clk_disable(hsotg->clk);
        }
 
+       mutex_unlock(&hsotg->init_mutex);
+
        return ret;
 }
+EXPORT_SYMBOL_GPL(s3c_hsotg_suspend);
 
-static int s3c_hsotg_resume(struct platform_device *pdev)
+int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
 {
-       struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
        unsigned long flags;
        int ret = 0;
 
+       mutex_lock(&hsotg->init_mutex);
+
        if (hsotg->driver) {
                dev_info(hsotg->dev, "resuming usb gadget %s\n",
                         hsotg->driver->driver.name);
 
                clk_enable(hsotg->clk);
                ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
-                                     hsotg->supplies);
-       }
+                                           hsotg->supplies);
 
-       spin_lock_irqsave(&hsotg->lock, flags);
-       hsotg->last_rst = jiffies;
-       s3c_hsotg_phy_enable(hsotg);
-       s3c_hsotg_core_init(hsotg);
-       spin_unlock_irqrestore(&hsotg->lock, flags);
+               s3c_hsotg_phy_enable(hsotg);
+
+               spin_lock_irqsave(&hsotg->lock, flags);
+               s3c_hsotg_core_init_disconnected(hsotg);
+               if (hsotg->enabled)
+                       s3c_hsotg_core_connect(hsotg);
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+       }
+       mutex_unlock(&hsotg->init_mutex);
 
        return ret;
 }
-
-#ifdef CONFIG_OF
-static const struct of_device_id s3c_hsotg_of_ids[] = {
-       { .compatible = "samsung,s3c6400-hsotg", },
-       { .compatible = "snps,dwc2", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids);
-#endif
-
-static struct platform_driver s3c_hsotg_driver = {
-       .driver         = {
-               .name   = "s3c-hsotg",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(s3c_hsotg_of_ids),
-       },
-       .probe          = s3c_hsotg_probe,
-       .remove         = s3c_hsotg_remove,
-       .suspend        = s3c_hsotg_suspend,
-       .resume         = s3c_hsotg_resume,
-};
-
-module_platform_driver(s3c_hsotg_driver);
-
-MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c-hsotg");
+EXPORT_SYMBOL_GPL(s3c_hsotg_resume);