Improve periodic USB transfers (device intr, isoc) used for input
authormglocker <mglocker@openbsd.org>
Sun, 4 Sep 2022 08:42:39 +0000 (08:42 +0000)
committermglocker <mglocker@openbsd.org>
Sun, 4 Sep 2022 08:42:39 +0000 (08:42 +0000)
devices, audio, and video.  It's still not perfect, and will need
further improvements.

High level, the diff contains following changes:

* Sync up with the Linux code base, which did re-work the periodic
  scheduling code path.
* Run the driver in IPL_VM instead of IPL_USB to prioritize us before
  lower/equal interrupts (same what NetBSD does).
* Add two new flags to our USB stack required by the updated driver
  code:
  - 'multi' flag in the usbd_hub structure to keep track whether
    a hub has one Transaction Translator for all ports (single TT)
    or one Transaction Translator per port (multi TT).
  - 'hcpriv' pointer in the usbd_tt structure for the HC driver to
    allocate memory for the scheduling depending on single or multi TT.

"go for it" kettenis@

19 files changed:
sys/arch/octeon/dev/octdwctwo.c
sys/dev/fdt/bcm2835_dwctwo.c
sys/dev/usb/dwc2/dwc2.c
sys/dev/usb/dwc2/dwc2.h
sys/dev/usb/dwc2/dwc2_core.c
sys/dev/usb/dwc2/dwc2_core.h
sys/dev/usb/dwc2/dwc2_coreintr.c
sys/dev/usb/dwc2/dwc2_hcd.c
sys/dev/usb/dwc2/dwc2_hcd.h
sys/dev/usb/dwc2/dwc2_hcdddma.c
sys/dev/usb/dwc2/dwc2_hcdintr.c
sys/dev/usb/dwc2/dwc2_hcdqueue.c
sys/dev/usb/dwc2/dwc2_hw.h
sys/dev/usb/dwc2/dwc2_params.c [new file with mode: 0644]
sys/dev/usb/dwc2/dwc2var.h
sys/dev/usb/dwc2/files.dwc2
sys/dev/usb/dwc2/gcd.h [new file with mode: 0644]
sys/dev/usb/uhub.c
sys/dev/usb/usbdivar.h

index 39a853a..252d138 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: octdwctwo.c,v 1.14 2021/07/24 14:43:53 mglocker Exp $ */
+/*     $OpenBSD: octdwctwo.c,v 1.15 2022/09/04 08:42:39 mglocker Exp $ */
 
 /*
  * Copyright (c) 2015 Masao Uebayashi <uebayasi@tombiinc.com>
@@ -76,9 +76,9 @@ struct cfdriver dwctwo_cd = {
 };
 
 static struct dwc2_core_params octdwctwo_params = {
-       .otg_cap = 2,
-       .otg_ver = 0,
-       .dma_enable = 1,
+       .otg_caps.hnp_support = 0,
+       .otg_caps.srp_support = 0,
+       .host_dma = 1,
        .dma_desc_enable = 0,
        .speed = 0,
        .enable_dynamic_fifo = 1,
@@ -101,8 +101,7 @@ static struct dwc2_core_params octdwctwo_params = {
        .reload_ctl = 0,
        .ahbcfg = 0x7,
        .uframe_sched = 1,
-       .external_id_pin_ctl = -1,
-       .hibernation = -1,
+       .external_id_pin_ctl = 0,
 };
 
 /*
@@ -223,7 +222,7 @@ octdwctwo_attach(struct device *parent, struct device *self, void *aux)
        sc->sc_dwc2.sc_child = config_found(&sc->sc_dwc2.sc_bus.bdev,
            &sc->sc_dwc2.sc_bus, usbctlprint);
 
-       sc->sc_ih = octeon_intr_establish(CIU_INT_USB, IPL_USB | IPL_MPSAFE,
+       sc->sc_ih = octeon_intr_establish(CIU_INT_USB, IPL_VM | IPL_MPSAFE,
            dwc2_intr, (void *)&sc->sc_dwc2, sc->sc_dwc2.sc_bus.bdev.dv_xname);
        KASSERT(sc->sc_ih != NULL);
 }
index 86567b9..cdfa9e7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bcm2835_dwctwo.c,v 1.3 2021/07/24 06:04:44 mglocker Exp $     */
+/*     $OpenBSD: bcm2835_dwctwo.c,v 1.4 2022/09/04 08:42:39 mglocker Exp $     */
 /*
  * Copyright (c) 2015 Masao Uebayashi <uebayasi@tombiinc.com>
  *
@@ -57,9 +57,9 @@ struct cfdriver dwctwo_cd = {
 };
 
 static struct dwc2_core_params bcm_dwctwo_params = {
-       .otg_cap                        = 0,    /* HNP/SRP capable */
-       .otg_ver                        = 0,    /* 1.3 */
-       .dma_enable                     = 1,
+       .otg_caps.hnp_support           = 0,    /* HNP/SRP capable */
+       .otg_caps.srp_support           = 0,
+       .host_dma                       = 1,
        .dma_desc_enable                = 0,
        .speed                          = 0,    /* High Speed */
        .enable_dynamic_fifo            = 1,
@@ -82,8 +82,7 @@ static struct dwc2_core_params bcm_dwctwo_params = {
        .reload_ctl                     = 0,
        .ahbcfg                         = 0x10,
        .uframe_sched                   = 1,
-       .external_id_pin_ctl            = -1,
-       .hibernation                    = -1,
+       .external_id_pin_ctl            = 0,
 };
 
 int
@@ -117,8 +116,9 @@ bcm_dwctwo_attach(struct device *parent, struct device *self, void *aux)
        if (idx == -1)
                idx = 1;
 
-       sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, idx, IPL_USB,
-           dwc2_intr, (void *)&sc->sc_dwc2, sc->sc_dwc2.sc_bus.bdev.dv_xname);
+       sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, idx,
+           IPL_VM | IPL_MPSAFE, dwc2_intr, (void *)&sc->sc_dwc2,
+           sc->sc_dwc2.sc_bus.bdev.dv_xname);
        if (sc->sc_ih == NULL)
                panic("%s: intr_establish failed!", __func__);
 
index 15ea161..7fa6fae 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2.c,v 1.62 2022/07/02 08:50:42 visa Exp $  */
+/*     $OpenBSD: dwc2.c,v 1.63 2022/09/04 08:42:39 mglocker Exp $      */
 /*     $NetBSD: dwc2.c,v 1.32 2014/09/02 23:26:20 macallan Exp $       */
 
 /*-
@@ -131,17 +131,7 @@ STATIC void                dwc2_rhc(void *);
 STATIC void            dwc2_timeout(void *);
 STATIC void            dwc2_timeout_task(void *);
 
-static inline void
-dwc2_allocate_bus_bandwidth(struct dwc2_hsotg *hsotg, u16 bw,
-                           struct usbd_xfer *xfer)
-{
-}
-
-static inline void
-dwc2_free_bus_bandwidth(struct dwc2_hsotg *hsotg, u16 bw,
-                       struct usbd_xfer *xfer)
-{
-}
+int                    dwc2_check_core_version(struct dwc2_hsotg *);
 
 #define DWC2_INTR_ENDPT 1
 
@@ -440,11 +430,8 @@ STATIC void
 dwc2_poll(struct usbd_bus *bus)
 {
        struct dwc2_softc *sc = DWC2_BUS2SC(bus);
-       struct dwc2_hsotg *hsotg = sc->sc_hsotg;
 
-       mtx_enter(&hsotg->lock);
        dwc2_interrupt(sc);
-       mtx_leave(&hsotg->lock);
 }
 
 /*
@@ -1162,7 +1149,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
        uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
        uint8_t epnum = UE_GET_ADDR(ed->bEndpointAddress);
        uint8_t dir = UE_GET_DIR(ed->bEndpointAddress);
-       uint16_t mps = UE_GET_SIZE(UGETW(ed->wMaxPacketSize));
+       uint32_t mps = UGETW(ed->wMaxPacketSize);
        uint32_t len;
 
        uint32_t flags = 0;
@@ -1192,7 +1179,8 @@ dwc2_device_start(struct usbd_xfer *xfer)
                    "mps=%d\n",
                    xfer, req->bmRequestType, req->bRequest, UGETW(req->wValue),
                    UGETW(req->wIndex), UGETW(req->wLength), dev->address,
-                   epnum, dir == UT_READ ? "in" :"out", dev->speed, mps);
+                   epnum, dir == UT_READ ? "in" :"out", dev->speed,
+                   UE_GET_SIZE(mps));
 
                /* Copy request packet to our DMA buffer */
                memcpy(KERNADDR(&dpipe->req_dma, 0), req, sizeof(*req));
@@ -1212,7 +1200,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
        } else if (xfertype == UE_ISOCHRONOUS) {
                DPRINTFN(3, "xfer=%p nframes=%d flags=%d addr=%d endpt=%d,"
                    " mps=%d dir %s\n", xfer, xfer->nframes, xfer->flags, addr,
-                   epnum, mps, dir == UT_READ ? "in" :"out");
+                   epnum, UE_GET_SIZE(mps), dir == UT_READ ? "in" :"out");
 
 #ifdef DIAGNOSTIC
                len = 0;
@@ -1226,7 +1214,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
         } else {
                 DPRINTFN(3, "xfer=%p len=%d flags=%d addr=%d endpt=%d,"
                     " mps=%d dir %s\n", xfer, xfer->length, xfer->flags, addr,
-                    epnum, mps, dir == UT_READ ? "in" :"out");
+                    epnum, UE_GET_SIZE(mps), dir == UT_READ ? "in" :"out");
 
                len = xfer->length;
        }
@@ -1243,7 +1231,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
        dwc2_urb->packet_count = xfer->nframes;
 
        dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, addr, epnum, xfertype, dir,
-           mps);
+           UE_GET_SIZE(mps), UE_GET_TRANS(mps) + 1);
 
        if (xfertype == UE_CONTROL) {
                dwc2_urb->setup_usbdma = &dpipe->req_dma;
@@ -1251,7 +1239,8 @@ dwc2_device_start(struct usbd_xfer *xfer)
                dwc2_urb->setup_dma = DMAADDR(&dpipe->req_dma, 0);
        } else {
                /* XXXNH - % mps required? */
-               if ((xfer->flags & USBD_FORCE_SHORT_XFER) && (len % mps) == 0)
+               if ((xfer->flags & USBD_FORCE_SHORT_XFER) && (len %
+                   UE_GET_SIZE(mps)) == 0)
                    flags |= URB_SEND_ZERO_PACKET;
        }
        flags |= URB_GIVEBACK_ASAP;
@@ -1332,7 +1321,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
 
        /* Create QH for the endpoint if it doesn't exist */
        if (!qh) {
-               qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, M_NOWAIT);
+               qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, M_ZERO | M_NOWAIT);
                if (!qh) {
                        retval = -ENOMEM;
                        goto fail;
@@ -1406,7 +1395,7 @@ int dwc2_intr(void *p)
                return 0;
 
        hsotg = sc->sc_hsotg;
-       mtx_enter(&hsotg->lock);
+//     mtx_enter(&hsotg->lock);
 
        if (sc->sc_bus.dying)
                goto done;
@@ -1415,13 +1404,13 @@ int dwc2_intr(void *p)
                uint32_t intrs;
 
                intrs = dwc2_read_core_intr(hsotg);
-               DWC2_WRITE_4(hsotg, GINTSTS, intrs);
+               dwc2_writel(hsotg, intrs, GINTSTS);
        } else {
                ret = dwc2_interrupt(sc);
        }
 
 done:
-       mtx_leave(&hsotg->lock);
+//     mtx_leave(&hsotg->lock);
 
        return ret;
 }
@@ -1470,11 +1459,11 @@ dwc2_init(struct dwc2_softc *sc)
 
        sc->sc_rhc_si = softintr_establish(IPL_SOFTUSB, dwc2_rhc, sc);
 
-       pool_init(&sc->sc_xferpool, sizeof(struct dwc2_xfer), 0, IPL_USB, 0,
+       pool_init(&sc->sc_xferpool, sizeof(struct dwc2_xfer), 0, IPL_VM, 0,
            "dwc2xfer", NULL);
-       pool_init(&sc->sc_qhpool, sizeof(struct dwc2_qh), 0, IPL_USB, 0,
+       pool_init(&sc->sc_qhpool, sizeof(struct dwc2_qh), 0, IPL_VM, 0,
            "dwc2qh", NULL);
-       pool_init(&sc->sc_qtdpool, sizeof(struct dwc2_qtd), 0, IPL_USB, 0,
+       pool_init(&sc->sc_qtdpool, sizeof(struct dwc2_qtd), 0, IPL_VM, 0,
            "dwc2qtd", NULL);
 
        sc->sc_hsotg = malloc(sizeof(struct dwc2_hsotg), M_USBHC,
@@ -1484,9 +1473,8 @@ dwc2_init(struct dwc2_softc *sc)
        sc->sc_hcdenabled = true;
 
        struct dwc2_hsotg *hsotg = sc->sc_hsotg;
-       struct dwc2_core_params defparams;
        int retval;
-
+#if 0
        if (sc->sc_params == NULL) {
                /* Default all params to autodetect */
                dwc2_set_all_params(&defparams, -1);
@@ -1498,30 +1486,41 @@ dwc2_init(struct dwc2_softc *sc)
                 */
                defparams.dma_desc_enable = 0;
        }
+#endif
        hsotg->dr_mode = USB_DR_MODE_HOST;
 
+       /*
+        * Before performing any core related operations
+        * check core version.
+        */
+       retval = dwc2_check_core_version(hsotg);
+       if (retval)
+               goto fail2;
+
        /*
         * Reset before dwc2_get_hwparams() then it could get power-on real
         * reset value form registers.
         */
-       dwc2_core_reset(hsotg);
-       usb_delay_ms(&sc->sc_bus, 500);
+       retval = dwc2_core_reset(hsotg, false);
+       if (retval)
+               goto fail2;
 
        /* Detect config values from hardware */
        retval = dwc2_get_hwparams(hsotg);
-       if (retval) {
+       if (retval)
                goto fail2;
-       }
-
-       hsotg->core_params = malloc(sizeof(*hsotg->core_params), M_USBHC,
-           M_ZERO | M_WAITOK);
-       dwc2_set_all_params(hsotg->core_params, -1);
 
-       /* Validate parameter values */
-       dwc2_set_parameters(hsotg, sc->sc_params);
+       /*
+        * For OTG cores, set the force mode bits to reflect the value
+        * of dr_mode. Force mode bits should not be touched at any
+        * other time after this.
+        */
+       dwc2_force_dr_mode(hsotg);
 
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
-    IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+       retval = dwc2_init_params(hsotg);
+       if (retval)
+               goto fail2;
+#if 0
        if (hsotg->dr_mode != USB_DR_MODE_HOST) {
                retval = dwc2_gadget_init(hsotg);
                if (retval)
@@ -1529,8 +1528,6 @@ dwc2_init(struct dwc2_softc *sc)
                hsotg->gadget_enabled = 1;
        }
 #endif
-#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || \
-    IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
        if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
                retval = dwc2_hcd_init(hsotg);
                if (retval) {
@@ -1540,14 +1537,8 @@ dwc2_init(struct dwc2_softc *sc)
                }
            hsotg->hcd_enabled = 1;
         }
-#endif
 
-#ifdef DWC2_DEBUG
-       uint32_t snpsid = hsotg->hw_params.snpsid;
-       dev_dbg(hsotg->dev, "Core Release: %x.%x%x%x (snpsid=%x)\n",
-           snpsid >> 12 & 0xf, snpsid >> 8 & 0xf,
-           snpsid >> 4 & 0xf, snpsid & 0xf, snpsid);
-#endif
+       hsotg->hibernated = 0;
 
        return 0;
 
@@ -1559,27 +1550,6 @@ fail2:
        return err;
 }
 
-#if 0
-/*
- * curmode is a mode indication bit 0 = device, 1 = host
- */
-STATIC const char * const intnames[32] = {
-       "curmode",      "modemis",      "otgint",       "sof",
-       "rxflvl",       "nptxfemp",     "ginnakeff",    "goutnakeff",
-       "ulpickint",    "i2cint",       "erlysusp",     "usbsusp",
-       "usbrst",       "enumdone",     "isooutdrop",   "eopf",
-       "restore_done", "epmis",        "iepint",       "oepint",
-       "incompisoin",  "incomplp",     "fetsusp",      "resetdet",
-       "prtint",       "hchint",       "ptxfemp",      "lpm",
-       "conidstschng", "disconnint",   "sessreqint",   "wkupint"
-};
-
-
-/***********************************************************************/
-
-#endif
-
-
 void
 dw_timeout(void *arg)
 {
@@ -1589,173 +1559,29 @@ dw_timeout(void *arg)
        task_add(dw->dw_wq, &dw->work);
 }
 
-void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr,
-                       int *hub_port)
-{
-       struct usbd_xfer *xfer = context;
-       struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
-       struct usbd_device *dev = dpipe->pipe.device;
+/*** platform.c ***************************************************************/
 
-       *hub_addr = dev->myhsport->parent->address;
-       *hub_port = dev->myhsport->portno;
-}
-
-int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
-{
-       struct usbd_xfer *xfer = context;
-       struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
-       struct usbd_device *dev = dpipe->pipe.device;
-
-       return dev->speed;
-}
-
-/*
- * Sets the final status of an URB and returns it to the upper layer. Any
- * required cleanup of the URB is performed.
- *
- * Must be called with interrupt disabled and spinlock held
- */
-void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
-    int status)
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
 {
-       struct usbd_xfer *xfer;
-       struct dwc2_xfer *dxfer;
-       struct dwc2_softc *sc;
-       usb_endpoint_descriptor_t *ed;
-       uint8_t xfertype;
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
 
-       if (!qtd) {
-               dev_dbg(hsotg->dev, "## %s: qtd is NULL ##\n", __func__);
-               return;
-       }
-
-       if (!qtd->urb) {
-               dev_dbg(hsotg->dev, "## %s: qtd->urb is NULL ##\n", __func__);
-               return;
-       }
-
-       xfer = qtd->urb->priv;
-       if (!xfer) {
-               dev_dbg(hsotg->dev, "## %s: urb->priv is NULL ##\n", __func__);
-               return;
-       }
-
-       dxfer = DWC2_XFER2DXFER(xfer);
-       sc = DWC2_XFER2SC(xfer);
-       ed = xfer->pipe->endpoint->edesc;
-       xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
-
-       struct dwc2_hcd_urb *urb = qtd->urb;
-       xfer->actlen = dwc2_hcd_urb_get_actual_length(urb);
-
-       DPRINTFN(3, "xfer=%p actlen=%d\n", xfer, xfer->actlen);
-
-       if (xfertype == UE_ISOCHRONOUS) {
-               xfer->actlen = 0;
-               for (size_t i = 0; i < xfer->nframes; ++i) {
-                       xfer->frlengths[i] =
-                               dwc2_hcd_urb_get_iso_desc_actual_length(
-                                               urb, i);
-                       DPRINTFN(1, "xfer=%p frame=%zu length=%d\n", xfer, i,
-                           xfer->frlengths[i]);
-                       xfer->actlen += xfer->frlengths[i];
-               }
-               DPRINTFN(1, "xfer=%p actlen=%d (isoc)\n", xfer, xfer->actlen);
-       }
-
-       if (xfertype == UE_ISOCHRONOUS && dbg_perio()) {
-               for (size_t i = 0; i < xfer->nframes; i++)
-                       dev_vdbg(hsotg->dev, " ISO Desc %zu status %d\n",
-                                i, urb->iso_descs[i].status);
-       }
-
-       if (!status) {
-               if (!(xfer->flags & USBD_SHORT_XFER_OK) &&
-                   xfer->actlen < xfer->length)
-                       status = -EIO;
-       }
-
-       switch (status) {
-       case 0:
-               dxfer->intr_status = USBD_NORMAL_COMPLETION;
-               break;
-       case -EPIPE:
-               dxfer->intr_status = USBD_STALLED;
-               break;
-       case -EPROTO:
-               dxfer->intr_status = USBD_INVAL;
-               break;
-       case -EIO:
-               dxfer->intr_status = USBD_IOERROR;
-               break;
-       case -EOVERFLOW:
-               dxfer->intr_status = USBD_IOERROR;
-               break;
-       default:
-               dxfer->intr_status = USBD_IOERROR;
-               printf("%s: unknown error status %d\n", __func__, status);
-       }
-
-       if (dxfer->intr_status == USBD_NORMAL_COMPLETION) {
-               /*
-                * control transfers with no data phase don't touch dmabuf, but
-                * everything else does.
-                */
-               if (!(xfertype == UE_CONTROL &&
-                   UGETW(xfer->request.wLength) == 0) &&
-                   xfer->actlen > 0 /* XXX PR/53503 */
-                   ) {
-                       int rd = usbd_xfer_isread(xfer);
-
-                       usb_syncmem(&xfer->dmabuf, 0, xfer->actlen,
-                           rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-               }
-       }
-
-       if (xfertype == UE_ISOCHRONOUS ||
-           xfertype == UE_INTERRUPT) {
-               struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
-
-               dwc2_free_bus_bandwidth(hsotg,
-                                       dwc2_hcd_get_ep_bandwidth(hsotg, dpipe),
-                                       xfer);
-       }
-
-       qtd->urb = NULL;
-       timeout_del(&xfer->timeout_handle);
-       usb_rem_task(xfer->device, &xfer->abort_task);
-       MUTEX_ASSERT_LOCKED(&hsotg->lock);
-
-       TAILQ_INSERT_TAIL(&sc->sc_complete, dxfer, xnext);
-
-       mtx_leave(&hsotg->lock);
-       usb_schedsoftintr(&sc->sc_bus);
-       mtx_enter(&hsotg->lock);
-}
-
-
-int
-_dwc2_hcd_start(struct dwc2_hsotg *hsotg)
-{
-       dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
-
-       mtx_enter(&hsotg->lock);
-
-       hsotg->lx_state = DWC2_L0;
+       /*
+        * Attempt to ensure this device is really a DWC_otg Controller.
+        * Read and verify the GSNPSID register contents. The value should be
+        * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
+        */
 
-       if (dwc2_is_device_mode(hsotg)) {
-               mtx_leave(&hsotg->lock);
-               return 0;       /* why 0 ?? */
+       hw->snpsid = dwc2_readl(hsotg, GSNPSID);
+       if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
+           (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
+           (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
+               dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
+                       hw->snpsid);
+               return -ENODEV;
        }
 
-       dwc2_hcd_reinit(hsotg);
-
-       mtx_leave(&hsotg->lock);
+       dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
+               hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
+               hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
        return 0;
 }
-
-int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg)
-{
-
-       return false;
-}
index 2f14470..61b84c6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2.h,v 1.15 2021/07/22 18:32:33 mglocker Exp $      */
+/*     $OpenBSD: dwc2.h,v 1.16 2022/09/04 08:42:39 mglocker Exp $      */
 /*     $NetBSD: dwc2.h,v 1.4 2014/12/23 16:20:06 macallan Exp $        */
 
 /*-
@@ -42,8 +42,8 @@
 
 #define STATIC
 
+// #define DWC2_DEBUG
 // #define VERBOSE_DEBUG
-// #define DWC2_DUMP_FRREM
 // #define CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 
 #define CONFIG_USB_DWC2_HOST           1
@@ -62,15 +62,8 @@ typedef int irqreturn_t;
 
 #define        dma_addr_t      bus_addr_t
 
-#define DWC2_READ_4(hsotg, reg) \
-    bus_space_read_4((hsotg)->hsotg_sc->sc_iot, (hsotg)->hsotg_sc->sc_ioh, (reg))
-#define DWC2_WRITE_4(hsotg, reg, data)  \
-    bus_space_write_4((hsotg)->hsotg_sc->sc_iot, (hsotg)->hsotg_sc->sc_ioh, (reg), (data));
-
 #ifdef DWC2_DEBUG
 extern int dwc2debug;
-#define WARN_ON(x)     KASSERT(!(x))
-
 #define        dev_info(d,fmt,...) do {                        \
        printf("%s: " fmt, device_xname(d),             \
            ## __VA_ARGS__);                            \
@@ -96,7 +89,6 @@ extern int dwc2debug;
        }                                               \
 } while (0)
 #else
-#define WARN_ON(x)
 #define        dev_info(...) do { } while (0)
 #define        dev_warn(...) do { } while (0)
 #define        dev_err(...) do { } while (0)
@@ -114,16 +106,16 @@ enum usb_otg_state {
        OTG_STATE_B_PERIPHERAL,
 };
 
-#define usleep_range(l, u)     do { DELAY(u); } while (0)
-
 #define spinlock_t             struct mutex
-#define spin_lock_init(lock)   mtx_init(lock, IPL_USB)
+#define spin_lock_init(lock)   mtx_init(lock, IPL_VM)
 #define spin_lock(l)           do { mtx_enter(l); } while (0)
 #define spin_unlock(l)         do { mtx_leave(l); } while (0)
 
 #define        spin_lock_irqsave(l, f)         \
        do { mtx_enter(l); (void)(f); } while (0)
 
+#define        spin_trylock_irqsave(l, f)      mtx_enter_try(l)
+
 #define        spin_unlock_irqrestore(l, f)    \
        do { mtx_leave(l); (void)(f); } while (0)
 
@@ -241,23 +233,29 @@ usb_disabled(void)
 static inline void
 udelay(unsigned long usecs)
 {
-
        DELAY(usecs);
 }
 
 static inline void
-ndelay(unsigned long nsecs)
+mdelay(unsigned long msecs)
 {
+       int loops = msecs;
+       while (loops--)
+               DELAY(1000);
+}
 
-       DELAY(nsecs / 1000);
+static inline void
+usleep_range(unsigned long min, unsigned long max)
+{
+       DELAY((min + max) / 2);
 }
 
+#define dwc2_msleep(x) mdelay(x)
+
 #define        EREMOTEIO       EIO
 #define        ECOMM           EIO
 #define        ENOTSUPP        ENOTSUP
 
-#define NS_TO_US(ns)   ((ns + 500L) / 1000L)
-
 void dw_timeout(void *);
 
 struct delayed_work {
index e7c5a14..f2eb065 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_core.c,v 1.11 2021/07/27 13:36:59 mglocker Exp $ */
+/*     $OpenBSD: dwc2_core.c,v 1.12 2022/09/04 08:42:39 mglocker Exp $ */
 /*     $NetBSD: dwc2_core.c,v 1.6 2014/04/03 06:34:58 skrll Exp $      */
 
 /*
@@ -42,7 +42,6 @@
  * DWC_otg hardware. These services are used by both the Host Controller
  * Driver and the Peripheral Controller Driver.
  */
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/signal.h>
 #include <dev/usb/dwc2/dwc2_core.h>
 #include <dev/usb/dwc2/dwc2_hcd.h>
 
-#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 /**
- * dwc2_backup_host_registers() - Backup controller host registers.
+ * dwc2_backup_global_registers() - Backup global controller registers.
  * When suspending usb bus, registers needs to be backuped
  * if controller power is disabled once suspended.
  *
  * @hsotg: Programming view of the DWC_otg controller
  */
-STATIC int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
+STATIC int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_hregs_backup *hr;
-       int i;
+       struct dwc2_gregs_backup *gr;
 
        dev_dbg(hsotg->dev, "%s\n", __func__);
 
-       /* Backup Host regs */
-       hr = &hsotg->hr_backup;
-       hr->hcfg = DWC2_READ_4(hsotg, HCFG);
-       hr->haintmsk = DWC2_READ_4(hsotg, HAINTMSK);
-       for (i = 0; i < hsotg->core_params->host_channels; ++i)
-               hr->hcintmsk[i] = DWC2_READ_4(hsotg, HCINTMSK(i));
+       /* Backup global regs */
+       gr = &hsotg->gr_backup;
 
-       hr->hprt0 = DWC2_READ_4(hsotg, HPRT0);
-       hr->hfir = DWC2_READ_4(hsotg, HFIR);
-       hr->valid = true;
+       gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
+       gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
+       gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);
+       gr->gusbcfg = dwc2_readl(hsotg, GUSBCFG);
+       gr->grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
+       gr->gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ);
+       gr->gdfifocfg = dwc2_readl(hsotg, GDFIFOCFG);
+       gr->pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1);
+       gr->glpmcfg = dwc2_readl(hsotg, GLPMCFG);
+       gr->gi2cctl = dwc2_readl(hsotg, GI2CCTL);
+       gr->pcgcctl = dwc2_readl(hsotg, PCGCTL);
 
+       gr->valid = true;
        return 0;
 }
 
 /**
- * dwc2_restore_host_registers() - Restore controller host registers.
+ * dwc2_restore_global_registers() - Restore controller global registers.
  * When resuming usb bus, device registers needs to be restored
  * if controller power were disabled.
  *
  * @hsotg: Programming view of the DWC_otg controller
  */
-STATIC int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
+STATIC int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_hregs_backup *hr;
-       int i;
+       struct dwc2_gregs_backup *gr;
 
        dev_dbg(hsotg->dev, "%s\n", __func__);
 
-       /* Restore host regs */
-       hr = &hsotg->hr_backup;
-       if (!hr->valid) {
-               dev_err(hsotg->dev, "%s: no host registers to restore\n",
-                               __func__);
+       /* Restore global regs */
+       gr = &hsotg->gr_backup;
+       if (!gr->valid) {
+               dev_err(hsotg->dev, "%s: no global registers to restore\n",
+                       __func__);
                return -EINVAL;
        }
-       hr->valid = false;
-
-       DWC2_WRITE_4(hsotg, HCFG, hr->hcfg);
-       DWC2_WRITE_4(hsotg, HAINTMSK, hr->haintmsk);
-
-       for (i = 0; i < hsotg->core_params->host_channels; ++i)
-               DWC2_WRITE_4(hsotg, HCINTMSK(i), hr->hcintmsk[i]);
+       gr->valid = false;
 
-       DWC2_WRITE_4(hsotg, HPRT0, hr->hprt0);
-       DWC2_WRITE_4(hsotg, HFIR, hr->hfir);
-       hsotg->frame_number = 0;
+       dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+       dwc2_writel(hsotg, gr->gotgctl, GOTGCTL);
+       dwc2_writel(hsotg, gr->gintmsk, GINTMSK);
+       dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
+       dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG);
+       dwc2_writel(hsotg, gr->grxfsiz, GRXFSIZ);
+       dwc2_writel(hsotg, gr->gnptxfsiz, GNPTXFSIZ);
+       dwc2_writel(hsotg, gr->gdfifocfg, GDFIFOCFG);
+       dwc2_writel(hsotg, gr->pcgcctl1, PCGCCTL1);
+       dwc2_writel(hsotg, gr->glpmcfg, GLPMCFG);
+       dwc2_writel(hsotg, gr->pcgcctl, PCGCTL);
+       dwc2_writel(hsotg, gr->gi2cctl, GI2CCTL);
 
        return 0;
 }
-#else
-static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
-{ return 0; }
-
-static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
-{ return 0; }
-#endif
 
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
-       IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 /**
- * dwc2_backup_device_registers() - Backup controller device registers.
- * When suspending usb bus, registers needs to be backuped
- * if controller power is disabled once suspended.
+ * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down.
  *
  * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Reset.
+ * @restore: Controller registers need to be restored
  */
-STATIC int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup,
+                                bool restore)
 {
-       struct dwc2_dregs_backup *dr;
-       int i;
-
-       dev_dbg(hsotg->dev, "%s\n", __func__);
-
-       /* Backup dev regs */
-       dr = &hsotg->dr_backup;
-
-       dr->dcfg = DWC2_READ_4(hsotg, DCFG);
-       dr->dctl = DWC2_READ_4(hsotg, DCTL);
-       dr->daintmsk = DWC2_READ_4(hsotg, DAINTMSK);
-       dr->diepmsk = DWC2_READ_4(hsotg, DIEPMSK);
-       dr->doepmsk = DWC2_READ_4(hsotg, DOEPMSK);
-
-       for (i = 0; i < hsotg->num_of_eps; i++) {
-               /* Backup IN EPs */
-               dr->diepctl[i] = DWC2_READ_4(hsotg, DIEPCTL(i));
-
-               /* Ensure DATA PID is correctly configured */
-               if (dr->diepctl[i] & DXEPCTL_DPID)
-                       dr->diepctl[i] |= DXEPCTL_SETD1PID;
-               else
-                       dr->diepctl[i] |= DXEPCTL_SETD0PID;
-
-               dr->dieptsiz[i] = DWC2_READ_4(hsotg, DIEPTSIZ(i));
-               dr->diepdma[i] = DWC2_READ_4(hsotg, DIEPDMA(i));
+       struct dwc2_gregs_backup *gr;
 
-               /* Backup OUT EPs */
-               dr->doepctl[i] = DWC2_READ_4(hsotg, DOEPCTL(i));
+       gr = &hsotg->gr_backup;
 
-               /* Ensure DATA PID is correctly configured */
-               if (dr->doepctl[i] & DXEPCTL_DPID)
-                       dr->doepctl[i] |= DXEPCTL_SETD1PID;
-               else
-                       dr->doepctl[i] |= DXEPCTL_SETD0PID;
+       /*
+        * Restore host or device regisers with the same mode core enterted
+        * to partial power down by checking "GOTGCTL_CURMODE_HOST" backup
+        * value of the "gotgctl" register.
+        */
+       if (gr->gotgctl & GOTGCTL_CURMODE_HOST)
+               return dwc2_host_exit_partial_power_down(hsotg, rem_wakeup,
+                                                        restore);
+       else
+               return dwc2_gadget_exit_partial_power_down(hsotg, restore);
+}
 
-               dr->doeptsiz[i] = DWC2_READ_4(hsotg, DOEPTSIZ(i));
-               dr->doepdma[i] = DWC2_READ_4(hsotg, DOEPDMA(i));
-       }
-       dr->valid = true;
-       return 0;
+/**
+ * dwc2_enter_partial_power_down() - Put controller in Partial Power Down.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
+{
+       if (dwc2_is_host_mode(hsotg))
+               return dwc2_host_enter_partial_power_down(hsotg);
+       else
+               return dwc2_gadget_enter_partial_power_down(hsotg);
 }
 
 /**
- * dwc2_restore_device_registers() - Restore controller device registers.
- * When resuming usb bus, device registers needs to be restored
- * if controller power were disabled.
+ * dwc2_restore_essential_regs() - Restore essiential regs of core.
  *
  * @hsotg: Programming view of the DWC_otg controller
+ * @rmode: Restore mode, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
  */
-STATIC int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode,
+                                       int is_host)
 {
+       u32 pcgcctl;
+       struct dwc2_gregs_backup *gr;
        struct dwc2_dregs_backup *dr;
-       u32 dctl;
-       int i;
-
-       dev_dbg(hsotg->dev, "%s\n", __func__);
+       struct dwc2_hregs_backup *hr;
 
-       /* Restore dev regs */
+       gr = &hsotg->gr_backup;
        dr = &hsotg->dr_backup;
-       if (!dr->valid) {
-               dev_err(hsotg->dev, "%s: no device registers to restore\n",
-                               __func__);
-               return -EINVAL;
-       }
-       dr->valid = false;
-
-       DWC2_WRITE_4(hsotg, DCFG, dr->dcfg);
-       DWC2_WRITE_4(hsotg, DCTL, dr->dctl);
-       DWC2_WRITE_4(hsotg, DAINTMSK, dr->daintmsk);
-       DWC2_WRITE_4(hsotg, DIEPMSK, dr->diepmsk);
-       DWC2_WRITE_4(hsotg, DOEPMSK, dr->doepmsk);
+       hr = &hsotg->hr_backup;
 
-       for (i = 0; i < hsotg->num_of_eps; i++) {
-               /* Restore IN EPs */
-               DWC2_WRITE_4(hsotg, DIEPCTL(i), dr->diepctl[i]);
-               DWC2_WRITE_4(hsotg, DIEPTSIZ(i), dr->dieptsiz[i]);
-               DWC2_WRITE_4(hsotg, DIEPDMA(i), dr->diepdma[i]);
+       dev_dbg(hsotg->dev, "%s: restoring essential regs\n", __func__);
 
-               /* Restore OUT EPs */
-               DWC2_WRITE_4(hsotg, DOEPCTL(i), dr->doepctl[i]);
-               DWC2_WRITE_4(hsotg, DOEPTSIZ(i), dr->doeptsiz[i]);
-               DWC2_WRITE_4(hsotg, DOEPDMA(i), dr->doepdma[i]);
+       /* Load restore values for [31:14] bits */
+       pcgcctl = (gr->pcgcctl & 0xffffc000);
+       /* If High Speed */
+       if (is_host) {
+               if (!(pcgcctl & PCGCTL_P2HD_PRT_SPD_MASK))
+                       pcgcctl |= (1 << 17);
+       } else {
+               if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK))
+                       pcgcctl |= (1 << 17);
        }
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
 
-       /* Set the Power-On Programming done bit */
-       dctl = DWC2_READ_4(hsotg, DCTL);
-       dctl |= DCTL_PWRONPRGDONE;
-       DWC2_WRITE_4(hsotg, DCTL, dctl);
+       /* Umnask global Interrupt in GAHBCFG and restore it */
+       dwc2_writel(hsotg, gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, GAHBCFG);
 
-       return 0;
-}
-#else
-static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
-{ return 0; }
+       /* Clear all pending interupts */
+       dwc2_writel(hsotg, 0xffffffff, GINTSTS);
 
-static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
-{ return 0; }
-#endif
+       /* Unmask restore done interrupt */
+       dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTMSK);
 
-/**
- * dwc2_backup_global_registers() - Backup global controller registers.
- * When suspending usb bus, registers needs to be backuped
- * if controller power is disabled once suspended.
- *
- * @hsotg: Programming view of the DWC_otg controller
- */
-STATIC int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
-{
-       struct dwc2_gregs_backup *gr;
-       int i;
+       /* Restore GUSBCFG and HCFG/DCFG */
+       dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
 
-       /* Backup global regs */
-       gr = &hsotg->gr_backup;
+       if (is_host) {
+               dwc2_writel(hsotg, hr->hcfg, HCFG);
+               if (rmode)
+                       pcgcctl |= PCGCTL_RESTOREMODE;
+               dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               udelay(10);
 
-       gr->gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
-       gr->gintmsk = DWC2_READ_4(hsotg, GINTMSK);
-       gr->gahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
-       gr->gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-       gr->grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
-       gr->gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
-       gr->hptxfsiz = DWC2_READ_4(hsotg, HPTXFSIZ);
-       gr->gdfifocfg = DWC2_READ_4(hsotg, GDFIFOCFG);
-       for (i = 0; i < MAX_EPS_CHANNELS; i++)
-               gr->dtxfsiz[i] = DWC2_READ_4(hsotg, DPTXFSIZN(i));
+               pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+               dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               udelay(10);
+       } else {
+               dwc2_writel(hsotg, dr->dcfg, DCFG);
+               if (!rmode)
+                       pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE;
+               dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               udelay(10);
 
-       gr->valid = true;
-       return 0;
+               pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+               dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               udelay(10);
+       }
 }
 
 /**
- * dwc2_restore_global_registers() - Restore controller global registers.
- * When resuming usb bus, device registers needs to be restored
- * if controller power were disabled.
+ * dwc2_hib_restore_common() - Common part of restore routine.
  *
  * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
  */
-STATIC int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
-{
-       struct dwc2_gregs_backup *gr;
-       int i;
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+                            int is_host)
+{
+       u32 gpwrdn;
+
+       /* Switch-on voltage to the core */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn &= ~GPWRDN_PWRDNSWTCH;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Reset core */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn &= ~GPWRDN_PWRDNRSTN;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Enable restore from PMU */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn |= GPWRDN_RESTORE;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Disable Power Down Clamp */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn &= ~GPWRDN_PWRDNCLMP;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(50);
+
+       if (!is_host && rem_wakeup)
+               udelay(70);
+
+       /* Deassert reset core */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn |= GPWRDN_PWRDNRSTN;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Disable PMU interrupt */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn &= ~GPWRDN_PMUINTSEL;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Set Restore Essential Regs bit in PCGCCTL register */
+       dwc2_restore_essential_regs(hsotg, rem_wakeup, is_host);
 
-       dev_dbg(hsotg->dev, "%s\n", __func__);
+       /*
+        * Wait For Restore_done Interrupt. This mechanism of polling the
+        * interrupt is introduced to avoid any possible race conditions
+        */
+       if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE,
+                                   20000)) {
+               dev_dbg(hsotg->dev,
+                       "%s: Restore Done wasn't generated here\n",
+                       __func__);
+       } else {
+               dev_dbg(hsotg->dev, "restore done  generated here\n");
 
-       /* Restore global regs */
-       gr = &hsotg->gr_backup;
-       if (!gr->valid) {
-               dev_err(hsotg->dev, "%s: no global registers to restore\n",
-                               __func__);
-               return -EINVAL;
+               /*
+                * To avoid restore done interrupt storm after restore is
+                * generated clear GINTSTS_RESTOREDONE bit.
+                */
+               dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTSTS);
        }
-       gr->valid = false;
-
-       DWC2_WRITE_4(hsotg, GINTSTS, 0xffffffff);
-       DWC2_WRITE_4(hsotg, GOTGCTL, gr->gotgctl);
-       DWC2_WRITE_4(hsotg, GINTMSK, gr->gintmsk);
-       DWC2_WRITE_4(hsotg, GUSBCFG, gr->gusbcfg);
-       DWC2_WRITE_4(hsotg, GAHBCFG, gr->gahbcfg);
-       DWC2_WRITE_4(hsotg, GRXFSIZ, gr->grxfsiz);
-       DWC2_WRITE_4(hsotg, GNPTXFSIZ, gr->gnptxfsiz);
-       DWC2_WRITE_4(hsotg, HPTXFSIZ, gr->hptxfsiz);
-       DWC2_WRITE_4(hsotg, GDFIFOCFG, gr->gdfifocfg);
-       for (i = 0; i < MAX_EPS_CHANNELS; i++)
-               DWC2_WRITE_4(hsotg, DPTXFSIZN(i), gr->dtxfsiz[i]);
-
-       return 0;
 }
 
 /**
- * dwc2_exit_hibernation() - Exit controller from Partial Power Down.
- *
- * @hsotg: Programming view of the DWC_otg controller
- * @restore: Controller registers need to be restored
+ * dwc2_wait_for_mode() - Waits for the controller mode.
+ * @hsotg:     Programming view of the DWC_otg controller.
+ * @host_mode: If true, waits for host mode, otherwise device mode.
  */
-int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
+static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg,
+                              bool host_mode)
 {
-       u32 pcgcctl;
-       int ret = 0;
-
-       if (!hsotg->core_params->hibernation)
-               return -ENOTSUP;
-
-       pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
-       pcgcctl &= ~PCGCTL_STOPPCLK;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-
-       pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
-       pcgcctl &= ~PCGCTL_PWRCLMP;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-
-       pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
-       pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-
-       udelay(100);
-       if (restore) {
-               ret = dwc2_restore_global_registers(hsotg);
-               if (ret) {
-                       dev_err(hsotg->dev, "%s: failed to restore registers\n",
-                                       __func__);
-                       return ret;
+       struct timeval start;
+       struct timeval end;
+       unsigned int timeout = 110;
+
+       dev_vdbg(hsotg->dev, "Waiting for %s mode\n",
+                host_mode ? "host" : "device");
+
+       getmicrotime(&start);
+
+       while (1) {
+               unsigned int ms;
+
+               if (dwc2_is_host_mode(hsotg) == host_mode) {
+                       dev_vdbg(hsotg->dev, "%s mode set\n",
+                                host_mode ? "Host" : "Device");
+                       break;
                }
-               if (dwc2_is_host_mode(hsotg)) {
-                       ret = dwc2_restore_host_registers(hsotg);
-                       if (ret) {
-                               dev_err(hsotg->dev, "%s: failed to restore host registers\n",
-                                               __func__);
-                               return ret;
-                       }
-               } else {
-                       ret = dwc2_restore_device_registers(hsotg);
-                       if (ret) {
-                               dev_err(hsotg->dev, "%s: failed to restore device registers\n",
-                                               __func__);
-                               return ret;
-                       }
+
+               getmicrotime(&end);
+               ms = (end.tv_usec - start.tv_usec) / 1000;
+
+               if (ms >= timeout) {
+                       dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n",
+                                __func__, host_mode ? "host" : "device");
+                       break;
                }
-       }
 
-       return ret;
+               usleep_range(1000, 2000);
+       }
 }
 
 /**
- * dwc2_enter_hibernation() - Put controller in Partial Power Down.
+ * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
+ * filter is enabled.
  *
- * @hsotg: Programming view of the DWC_otg controller
+ * @hsotg: Programming view of DWC_otg controller
  */
-int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
+static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
 {
-       u32 pcgcctl;
-       int ret = 0;
+       u32 gsnpsid;
+       u32 ghwcfg4;
 
-       if (!hsotg->core_params->hibernation)
-               return -ENOTSUP;
-
-       /* Backup all registers */
-       ret = dwc2_backup_global_registers(hsotg);
-       if (ret) {
-               dev_err(hsotg->dev, "%s: failed to backup global registers\n",
-                               __func__);
-               return ret;
-       }
+       if (!dwc2_hw_is_otg(hsotg))
+               return false;
 
-       if (dwc2_is_host_mode(hsotg)) {
-               ret = dwc2_backup_host_registers(hsotg);
-               if (ret) {
-                       dev_err(hsotg->dev, "%s: failed to backup host registers\n",
-                                       __func__);
-                       return ret;
-               }
-       } else {
-               ret = dwc2_backup_device_registers(hsotg);
-               if (ret) {
-                       dev_err(hsotg->dev, "%s: failed to backup device registers\n",
-                                       __func__);
-                       return ret;
-               }
-       }
+       /* Check if core configuration includes the IDDIG filter. */
+       ghwcfg4 = dwc2_readl(hsotg, GHWCFG4);
+       if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
+               return false;
 
        /*
-        * Clear any pending interrupts since dwc2 will not be able to
-        * clear them after entering hibernation.
+        * Check if the IDDIG debounce filter is bypassed. Available
+        * in core version >= 3.10a.
         */
-       DWC2_WRITE_4(hsotg, GINTSTS, 0xffffffff);
-
-       /* Put the controller in low power state */
-       pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
-
-       pcgcctl |= PCGCTL_PWRCLMP;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-       ndelay(20);
-
-       pcgcctl |= PCGCTL_RSTPDWNMODULE;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-       ndelay(20);
+       gsnpsid = dwc2_readl(hsotg, GSNPSID);
+       if (gsnpsid >= DWC2_CORE_REV_3_10a) {
+               u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
 
-       pcgcctl |= PCGCTL_STOPPCLK;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
+               if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
+                       return false;
+       }
 
-       return ret;
+       return true;
 }
 
-/**
- * dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
- * used in both device and host modes
+/*
+ * dwc2_enter_hibernation() - Common function to enter hibernation.
  *
  * @hsotg: Programming view of the DWC_otg controller
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
  */
-STATIC void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host)
 {
-       u32 intmsk;
-
-       /* Clear any pending OTG Interrupts */
-       DWC2_WRITE_4(hsotg, GOTGINT, 0xffffffff);
-
-       /* Clear any pending interrupts */
-       DWC2_WRITE_4(hsotg, GINTSTS, 0xffffffff);
-
-       /* Enable the interrupts in the GINTMSK */
-       intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT;
-
-       if (hsotg->core_params->dma_enable <= 0)
-               intmsk |= GINTSTS_RXFLVL;
-       if (hsotg->core_params->external_id_pin_ctl <= 0)
-               intmsk |= GINTSTS_CONIDSTSCHNG;
-
-       intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
-                 GINTSTS_SESSREQINT;
-
-       DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
+       if (is_host)
+               return dwc2_host_enter_hibernation(hsotg);
+       else
+               return dwc2_gadget_enter_hibernation(hsotg);
 }
 
 /*
- * Initializes the FSLSPClkSel field of the HCFG register depending on the
- * PHY type
+ * dwc2_exit_hibernation() - Common function to exit from hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @reset: Enabled in case of restore with reset.
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
  */
-STATIC void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+                         int reset, int is_host)
 {
-       u32 hcfg, val;
-
-       if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
-            hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
-            hsotg->core_params->ulpi_fs_ls > 0) ||
-           hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) {
-               /* Full speed PHY */
-               val = HCFG_FSLSPCLKSEL_48_MHZ;
-       } else {
-               /* High speed PHY running at full speed or high speed */
-               val = HCFG_FSLSPCLKSEL_30_60_MHZ;
-       }
-
-       dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
-       hcfg = DWC2_READ_4(hsotg, HCFG);
-       hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
-       hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
-       DWC2_WRITE_4(hsotg, HCFG, hcfg);
+       if (is_host)
+               return dwc2_host_exit_hibernation(hsotg, rem_wakeup, reset);
+       else
+               return dwc2_gadget_exit_hibernation(hsotg, rem_wakeup, reset);
 }
 
 /*
  * Do core a soft reset of the core.  Be careful with this because it
  * resets all the internal state machines of the core.
  */
-int dwc2_core_reset(struct dwc2_hsotg *hsotg)
+int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
 {
        u32 greset;
-       int count = 0;
+       bool wait_for_host_mode = false;
 
        dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
+       /*
+        * If the current mode is host, either due to the force mode
+        * bit being set (which persists after core reset) or the
+        * connector id pin, a core soft reset will temporarily reset
+        * the mode to device. A delay from the IDDIG debounce filter
+        * will occur before going back to host mode.
+        *
+        * Determine whether we will go back into host mode after a
+        * reset and account for this delay after the reset.
+        */
+       if (dwc2_iddig_filter_enabled(hsotg)) {
+               u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
+               u32 gusbcfg = dwc2_readl(hsotg, GUSBCFG);
+
+               if (!(gotgctl & GOTGCTL_CONID_B) ||
+                   (gusbcfg & GUSBCFG_FORCEHOSTMODE)) {
+                       wait_for_host_mode = true;
+               }
+       }
+
        /* Core Soft Reset */
-       greset = DWC2_READ_4(hsotg, GRSTCTL);
+       greset = dwc2_readl(hsotg, GRSTCTL);
        greset |= GRSTCTL_CSFTRST;
-       DWC2_WRITE_4(hsotg, GRSTCTL, greset);
-       do {
-               udelay(1);
-               greset = DWC2_READ_4(hsotg, GRSTCTL);
-               if (++count > 50) {
-                       dev_warn(hsotg->dev,
-                                "%s() HANG! Soft Reset GRSTCTL=%0x\n",
-                                __func__, greset);
+       dwc2_writel(hsotg, greset, GRSTCTL);
+
+       if ((hsotg->hw_params.snpsid & DWC2_CORE_REV_MASK) <
+               (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+               if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL,
+                                             GRSTCTL_CSFTRST, 10000)) {
+                       dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST\n",
+                                __func__);
                        return -EBUSY;
                }
-       } while (greset & GRSTCTL_CSFTRST);
-
-       /* Wait for AHB master IDLE state */
-       count = 0;
-       do {
-               udelay(1);
-               greset = DWC2_READ_4(hsotg, GRSTCTL);
-               if (++count > 50) {
-                       dev_warn(hsotg->dev,
-                                "%s() HANG! AHB Idle GRSTCTL=%0x\n",
-                                __func__, greset);
+       } else {
+               if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
+                                           GRSTCTL_CSFTRST_DONE, 10000)) {
+                       dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST_DONE\n",
+                                __func__);
                        return -EBUSY;
                }
-       } while (!(greset & GRSTCTL_AHBIDLE));
+               greset = dwc2_readl(hsotg, GRSTCTL);
+               greset &= ~GRSTCTL_CSFTRST;
+               greset |= GRSTCTL_CSFTRST_DONE;
+               dwc2_writel(hsotg, greset, GRSTCTL);
+       }
+
+       /*
+        * Switching from device mode to host mode by disconnecting
+        * device cable core enters and exits form hibernation.
+        * However, the fifo map remains not cleared. It results
+        * to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/
+        * gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2])
+        * if in host mode we disconnect the micro a to b host
+        * cable. Because core reset occurs.
+        * To avoid the WARNING, fifo_map should be cleared
+        * in dwc2_core_reset() function by taking into account configs.
+        * fifo_map must be cleared only if driver is configured in
+        * "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE"
+        * mode.
+        */
+       dwc2_clear_fifo_map(hsotg);
+
+       /* Wait for AHB master IDLE state */
+       if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) {
+               dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n",
+                        __func__);
+               return -EBUSY;
+       }
+
+       if (wait_for_host_mode && !skip_wait)
+               dwc2_wait_for_mode(hsotg, true);
 
        return 0;
 }
 
-/*
- * Force the mode of the controller.
+/**
+ * dwc2_force_mode() - Force the mode of the controller.
  *
  * Forcing the mode is needed for two cases:
  *
  * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
  * controller to stay in a particular mode regardless of ID pin
- * changes. We do this usually after a core reset.
+ * changes. We do this once during probe.
  *
  * 2) During probe we want to read reset values of the hw
  * configuration registers that are only available in either host or
@@ -548,13 +529,15 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
  * Checks are done in this function to determine whether doing a force
  * would be valid or not.
  *
- * If a force is done, it requires a 25ms delay to take effect.
+ * If a force is done, it requires a IDDIG debounce filter delay if
+ * the filter is configured and enabled. We poll the current mode of
+ * the controller to account for this delay.
  *
- * Returns true if the mode was forced.
+ * @hsotg: Programming view of DWC_otg controller
+ * @host: Host mode flag
  */
-STATIC bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
 {
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
        u32 gusbcfg;
        u32 set;
        u32 clear;
@@ -565,53 +548,58 @@ STATIC bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
         * Force mode has no effect if the hardware is not OTG.
         */
        if (!dwc2_hw_is_otg(hsotg))
-               return false;
+               return;
 
        /*
         * If dr_mode is either peripheral or host only, there is no
         * need to ever force the mode to the opposite mode.
         */
-       if (host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
-               WARN_ON(1);
-               return false;
-       }
+       if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
+               return;
 
-       if (!host && hsotg->dr_mode == USB_DR_MODE_HOST) {
-               WARN_ON(1);
-               return false;
-       }
+       if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
+               return;
 
-       gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+       gusbcfg = dwc2_readl(hsotg, GUSBCFG);
 
        set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
        clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
 
        gusbcfg &= ~clear;
        gusbcfg |= set;
-       DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
+       dwc2_writel(hsotg, gusbcfg, GUSBCFG);
 
-       usb_delay_ms(&sc->sc_bus, 25);
-       return true;
+       dwc2_wait_for_mode(hsotg, host);
+       return;
 }
 
-/*
- * Clears the force mode bits.
+/**
+ * dwc2_clear_force_mode() - Clears the force mode bits.
+ *
+ * After clearing the bits, wait up to 100 ms to account for any
+ * potential IDDIG filter delay. We can't know if we expect this delay
+ * or not because the value of the connector ID status is affected by
+ * the force mode. We only need to call this once during probe if
+ * dr_mode == OTG.
+ *
+ * @hsotg: Programming view of DWC_otg controller
  */
 STATIC void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
        u32 gusbcfg;
 
-       gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+       if (!dwc2_hw_is_otg(hsotg))
+               return;
+
+       dev_dbg(hsotg->dev, "Clearing force mode bits\n");
+
+       gusbcfg = dwc2_readl(hsotg, GUSBCFG);
        gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
        gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
-       DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
+       dwc2_writel(hsotg, gusbcfg, GUSBCFG);
 
-       /*
-        * NOTE: This long sleep is _very_ important, otherwise the core will
-        * not stay in host mode after a connector ID change!
-        */
-       usb_delay_ms(&sc->sc_bus, 25);
+       if (dwc2_iddig_filter_enabled(hsotg))
+               dwc2_msleep(100);
 }
 
 /*
@@ -621,7 +609,13 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
 {
        switch (hsotg->dr_mode) {
        case USB_DR_MODE_HOST:
-               dwc2_force_mode(hsotg, true);
+               /*
+                * NOTE: This is required for some rockchip soc based
+                * platforms on their host-only dwc2.
+                */
+               if (!dwc2_hw_is_otg(hsotg))
+                       dwc2_msleep(50);
+
                break;
        case USB_DR_MODE_PERIPHERAL:
                dwc2_force_mode(hsotg, false);
@@ -637,1686 +631,17 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
 }
 
 /*
- * Do core a soft reset of the core.  Be careful with this because it
- * resets all the internal state machines of the core.
- *
- * Additionally this will apply force mode as per the hsotg->dr_mode
- * parameter.
+ * dwc2_enable_acg - enable active clock gating feature
  */
-int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg)
 {
-       int retval;
-
-       retval = dwc2_core_reset(hsotg);
-       if (retval)
-               return retval;
+       if (hsotg->params.acg_enable) {
+               u32 pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1);
 
-       dwc2_force_dr_mode(hsotg);
-       return 0;
-}
-
-STATIC int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
-{
-       u32 usbcfg, i2cctl;
-       int retval = 0;
-
-       /*
-        * core_init() is now called on every switch so only call the
-        * following for the first time through
-        */
-       if (select_phy) {
-               dev_dbg(hsotg->dev, "FS PHY selected\n");
-
-               usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-               if (!(usbcfg & GUSBCFG_PHYSEL)) {
-                       usbcfg |= GUSBCFG_PHYSEL;
-                       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-
-                       /* Reset after a PHY select */
-                       retval = dwc2_core_reset_and_force_dr_mode(hsotg);
-
-                       if (retval) {
-                               dev_err(hsotg->dev,
-                                       "%s: Reset failed, aborting", __func__);
-                               return retval;
-                       }
-               }
-       }
-
-       /*
-        * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
-        * do this on HNP Dev/Host mode switches (done in dev_init and
-        * host_init).
-        */
-       if (dwc2_is_host_mode(hsotg))
-               dwc2_init_fs_ls_pclk_sel(hsotg);
-
-       if (hsotg->core_params->i2c_enable > 0) {
-               dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
-
-               /* Program GUSBCFG.OtgUtmiFsSel to I2C */
-               usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-               usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
-               DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-
-               /* Program GI2CCTL.I2CEn */
-               i2cctl = DWC2_READ_4(hsotg, GI2CCTL);
-               i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
-               i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
-               i2cctl &= ~GI2CCTL_I2CEN;
-               DWC2_WRITE_4(hsotg, GI2CCTL, i2cctl);
-               i2cctl |= GI2CCTL_I2CEN;
-               DWC2_WRITE_4(hsotg, GI2CCTL, i2cctl);
-       }
-
-       return retval;
-}
-
-STATIC int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
-{
-       u32 usbcfg, usbcfg_old;
-       int retval = 0;
-
-       if (!select_phy)
-               return 0;
-
-       usbcfg = usbcfg_old = DWC2_READ_4(hsotg, GUSBCFG);
-
-       /*
-        * HS PHY parameters. These parameters are preserved during soft reset
-        * so only program the first time. Do a soft reset immediately after
-        * setting phyif.
-        */
-       switch (hsotg->core_params->phy_type) {
-       case DWC2_PHY_TYPE_PARAM_ULPI:
-               /* ULPI interface */
-               dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
-               usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
-               usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
-               if (hsotg->core_params->phy_ulpi_ddr > 0)
-                       usbcfg |= GUSBCFG_DDRSEL;
-               break;
-       case DWC2_PHY_TYPE_PARAM_UTMI:
-               /* UTMI+ interface */
-               dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
-               usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
-               if (hsotg->core_params->phy_utmi_width == 16)
-                       usbcfg |= GUSBCFG_PHYIF16;
-               break;
-       default:
-               dev_err(hsotg->dev, "FS PHY selected at HS!\n");
-               break;
-       }
-
-       if (usbcfg != usbcfg_old) {
-               DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-
-               /* Reset after setting the PHY parameters */
-               retval = dwc2_core_reset_and_force_dr_mode(hsotg);
-               if (retval) {
-                       dev_err(hsotg->dev,
-                               "%s: Reset failed, aborting", __func__);
-                       return retval;
-               }
-       }
-
-       return retval;
-}
-
-STATIC int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
-{
-       u32 usbcfg;
-       int retval = 0;
-
-       if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL &&
-           hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) {
-               /* If FS mode with FS PHY */
-               retval = dwc2_fs_phy_init(hsotg, select_phy);
-               if (retval)
-                       return retval;
-       } else {
-               /* High speed PHY */
-               retval = dwc2_hs_phy_init(hsotg, select_phy);
-               if (retval)
-                       return retval;
-       }
-
-       if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
-           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
-           hsotg->core_params->ulpi_fs_ls > 0) {
-               dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
-               usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-               usbcfg |= GUSBCFG_ULPI_FS_LS;
-               usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
-               DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-       } else {
-               usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-               usbcfg &= ~GUSBCFG_ULPI_FS_LS;
-               usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
-               DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-       }
-
-       return retval;
-}
-
-STATIC int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
-{
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
-       u32 ahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
-
-       switch (hsotg->hw_params.arch) {
-       case GHWCFG2_EXT_DMA_ARCH:
-               dev_dbg(hsotg->dev, "External DMA Mode\n");
-               if (!sc->sc_set_dma_addr) {
-                       dev_err(hsotg->dev, "External DMA Mode not supported\n");
-                       return -EINVAL;
-               }
-               if (hsotg->core_params->ahbcfg != -1) {
-                       ahbcfg &= GAHBCFG_CTRL_MASK;
-                       ahbcfg |= hsotg->core_params->ahbcfg &
-                                 ~GAHBCFG_CTRL_MASK;
-               }
-               break;
-
-       case GHWCFG2_INT_DMA_ARCH:
-               dev_dbg(hsotg->dev, "Internal DMA Mode\n");
-               if (hsotg->core_params->ahbcfg != -1) {
-                       ahbcfg &= GAHBCFG_CTRL_MASK;
-                       ahbcfg |= hsotg->core_params->ahbcfg &
-                                 ~GAHBCFG_CTRL_MASK;
-               }
-               break;
-
-       case GHWCFG2_SLAVE_ONLY_ARCH:
-       default:
-               dev_dbg(hsotg->dev, "Slave Only Mode\n");
-               break;
-       }
-
-       dev_dbg(hsotg->dev, "dma_enable:%d dma_desc_enable:%d\n",
-               hsotg->core_params->dma_enable,
-               hsotg->core_params->dma_desc_enable);
-
-       if (hsotg->core_params->dma_enable > 0) {
-               if (hsotg->core_params->dma_desc_enable > 0)
-                       dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n");
-               else
-                       dev_dbg(hsotg->dev, "Using Buffer DMA mode\n");
-       } else {
-               dev_dbg(hsotg->dev, "Using Slave mode\n");
-               hsotg->core_params->dma_desc_enable = 0;
-       }
-
-       if (hsotg->core_params->dma_enable > 0)
-               ahbcfg |= GAHBCFG_DMA_EN;
-
-       DWC2_WRITE_4(hsotg, GAHBCFG, ahbcfg);
-
-       return 0;
-}
-
-STATIC void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
-{
-       u32 usbcfg;
-
-       usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-       usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP);
-
-       switch (hsotg->hw_params.op_mode) {
-       case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
-               if (hsotg->core_params->otg_cap ==
-                               DWC2_CAP_PARAM_HNP_SRP_CAPABLE)
-                       usbcfg |= GUSBCFG_HNPCAP;
-               if (hsotg->core_params->otg_cap !=
-                               DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
-                       usbcfg |= GUSBCFG_SRPCAP;
-               break;
-
-       case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
-       case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
-       case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
-               if (hsotg->core_params->otg_cap !=
-                               DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
-                       usbcfg |= GUSBCFG_SRPCAP;
-               break;
-
-       case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE:
-       case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE:
-       case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST:
-       default:
-               break;
-       }
-
-       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-}
-
-/**
- * dwc2_core_init() - Initializes the DWC_otg controller registers and
- * prepares the core for device mode or host mode operation
- *
- * @hsotg:         Programming view of the DWC_otg controller
- * @initial_setup: If true then this is the first init for this instance.
- */
-int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
-{
-       u32 usbcfg, otgctl;
-       int retval;
-
-       dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
-
-       usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-
-       /* Set ULPI External VBUS bit if needed */
-       usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV;
-       if (hsotg->core_params->phy_ulpi_ext_vbus ==
-                               DWC2_PHY_ULPI_EXTERNAL_VBUS)
-               usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV;
-
-       /* Set external TS Dline pulsing bit if needed */
-       usbcfg &= ~GUSBCFG_TERMSELDLPULSE;
-       if (hsotg->core_params->ts_dline > 0)
-               usbcfg |= GUSBCFG_TERMSELDLPULSE;
-
-       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
-
-       /*
-        * Reset the Controller
-        *
-        * We only need to reset the controller if this is a re-init.
-        * For the first init we know for sure that earlier code reset us (it
-        * needed to in order to properly detect various parameters).
-        */
-       if (!initial_setup) {
-               retval = dwc2_core_reset_and_force_dr_mode(hsotg);
-               if (retval) {
-                       dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
-                                       __func__);
-                       return retval;
-               }
-       }
-
-       /*
-        * This needs to happen in FS mode before any other programming occurs
-        */
-       retval = dwc2_phy_init(hsotg, initial_setup);
-       if (retval)
-               return retval;
-
-       /* Program the GAHBCFG Register */
-       retval = dwc2_gahbcfg_init(hsotg);
-       if (retval)
-               return retval;
-
-       /* Program the GUSBCFG register */
-       dwc2_gusbcfg_init(hsotg);
-
-       /* Program the GOTGCTL register */
-       otgctl = DWC2_READ_4(hsotg, GOTGCTL);
-       otgctl &= ~GOTGCTL_OTGVER;
-       if (hsotg->core_params->otg_ver > 0)
-               otgctl |= GOTGCTL_OTGVER;
-       DWC2_WRITE_4(hsotg, GOTGCTL, otgctl);
-       dev_dbg(hsotg->dev, "OTG VER PARAM: %d\n", hsotg->core_params->otg_ver);
-
-       /* Clear the SRP success bit for FS-I2c */
-       hsotg->srp_success = 0;
-
-       /* Enable common interrupts */
-       dwc2_enable_common_interrupts(hsotg);
-
-       /*
-        * Do device or host initialization based on mode during PCD and
-        * HCD initialization
-        */
-       if (dwc2_is_host_mode(hsotg)) {
-               dev_dbg(hsotg->dev, "Host Mode\n");
-               hsotg->op_state = OTG_STATE_A_HOST;
-       } else {
-               dev_dbg(hsotg->dev, "Device Mode\n");
-               hsotg->op_state = OTG_STATE_B_PERIPHERAL;
-       }
-
-       return 0;
-}
-
-/**
- * dwc2_enable_host_interrupts() - Enables the Host mode interrupts
- *
- * @hsotg: Programming view of DWC_otg controller
- */
-void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg)
-{
-       u32 intmsk;
-
-       dev_dbg(hsotg->dev, "%s()\n", __func__);
-
-       /* Disable all interrupts */
-       DWC2_WRITE_4(hsotg, GINTMSK, 0);
-       DWC2_WRITE_4(hsotg, HAINTMSK, 0);
-
-       /* Enable the common interrupts */
-       dwc2_enable_common_interrupts(hsotg);
-
-       /* Enable host mode interrupts without disturbing common interrupts */
-       intmsk = DWC2_READ_4(hsotg, GINTMSK);
-       intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT;
-       DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
-}
-
-/**
- * dwc2_disable_host_interrupts() - Disables the Host Mode interrupts
- *
- * @hsotg: Programming view of DWC_otg controller
- */
-void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
-{
-       u32 intmsk = DWC2_READ_4(hsotg, GINTMSK);
-
-       /* Disable host mode interrupts without disturbing common interrupts */
-       intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT |
-                   GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT);
-       DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
-}
-
-/*
- * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size
- * For system that have a total fifo depth that is smaller than the default
- * RX + TX fifo size.
- *
- * @hsotg: Programming view of DWC_otg controller
- */
-STATIC void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
-{
-       struct dwc2_core_params *params = hsotg->core_params;
-       struct dwc2_hw_params *hw = &hsotg->hw_params;
-       u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size;
-
-       total_fifo_size = hw->total_fifo_size;
-       rxfsiz = params->host_rx_fifo_size;
-       nptxfsiz = params->host_nperio_tx_fifo_size;
-       ptxfsiz = params->host_perio_tx_fifo_size;
-
-       /*
-        * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth
-        * allocation with support for high bandwidth endpoints. Synopsys
-        * defines MPS(Max Packet size) for a periodic EP=1024, and for
-        * non-periodic as 512.
-        */
-       if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) {
-               /*
-                * For Buffer DMA mode/Scatter Gather DMA mode
-                * 2 * ((Largest Packet size / 4) + 1 + 1) + n
-                * with n = number of host channel.
-                * 2 * ((1024/4) + 2) = 516
-                */
-               rxfsiz = 516 + hw->host_channels;
-
-               /*
-                * min non-periodic tx fifo depth
-                * 2 * (largest non-periodic USB packet used / 4)
-                * 2 * (512/4) = 256
-                */
-               nptxfsiz = 256;
-
-               /*
-                * min periodic tx fifo depth
-                * (largest packet size*MC)/4
-                * (1024 * 3)/4 = 768
-                */
-               ptxfsiz = 768;
-
-               params->host_rx_fifo_size = rxfsiz;
-               params->host_nperio_tx_fifo_size = nptxfsiz;
-               params->host_perio_tx_fifo_size = ptxfsiz;
-       }
-
-       /*
-        * If the summation of RX, NPTX and PTX fifo sizes is still
-        * bigger than the total_fifo_size, then we have a problem.
-        *
-        * We won't be able to allocate as many endpoints. Right now,
-        * we're just printing an error message, but ideally this FIFO
-        * allocation algorithm would be improved in the future.
-        *
-        * FIXME improve this FIFO allocation algorithm.
-        */
-       if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz))
-               dev_err(hsotg->dev, "invalid fifo sizes\n");
-}
-
-STATIC void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
-{
-       struct dwc2_core_params *params = hsotg->core_params;
-       u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz;
-
-       if (!params->enable_dynamic_fifo)
-               return;
-
-       dwc2_calculate_dynamic_fifo(hsotg);
-
-       /* Rx FIFO */
-       grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
-       dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
-       grxfsiz &= ~GRXFSIZ_DEPTH_MASK;
-       grxfsiz |= params->host_rx_fifo_size <<
-                  GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK;
-       DWC2_WRITE_4(hsotg, GRXFSIZ, grxfsiz);
-       dev_dbg(hsotg->dev, "new grxfsiz=%08x\n",
-               DWC2_READ_4(hsotg, GRXFSIZ));
-
-       /* Non-periodic Tx FIFO */
-       dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n",
-               DWC2_READ_4(hsotg, GNPTXFSIZ));
-       nptxfsiz = params->host_nperio_tx_fifo_size <<
-                  FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
-       nptxfsiz |= params->host_rx_fifo_size <<
-                   FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
-       DWC2_WRITE_4(hsotg, GNPTXFSIZ, nptxfsiz);
-       dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n",
-               DWC2_READ_4(hsotg, GNPTXFSIZ));
-
-       /* Periodic Tx FIFO */
-       dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n",
-               DWC2_READ_4(hsotg, HPTXFSIZ));
-       hptxfsiz = params->host_perio_tx_fifo_size <<
-                  FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
-       hptxfsiz |= (params->host_rx_fifo_size +
-                    params->host_nperio_tx_fifo_size) <<
-                   FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
-       DWC2_WRITE_4(hsotg, HPTXFSIZ, hptxfsiz);
-       dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n",
-               DWC2_READ_4(hsotg, HPTXFSIZ));
-
-       if (hsotg->core_params->en_multiple_tx_fifo > 0 &&
-           hsotg->hw_params.snpsid <= DWC2_CORE_REV_2_94a) {
-               /*
-                * Global DFIFOCFG calculation for Host mode -
-                * include RxFIFO, NPTXFIFO and HPTXFIFO
-                */
-               dfifocfg = DWC2_READ_4(hsotg, GDFIFOCFG);
-               dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK;
-               dfifocfg |= (params->host_rx_fifo_size +
-                            params->host_nperio_tx_fifo_size +
-                            params->host_perio_tx_fifo_size) <<
-                           GDFIFOCFG_EPINFOBASE_SHIFT &
-                           GDFIFOCFG_EPINFOBASE_MASK;
-               DWC2_WRITE_4(hsotg, GDFIFOCFG, dfifocfg);
-       }
-}
-
-/**
- * dwc2_core_host_init() - Initializes the DWC_otg controller registers for
- * Host mode
- *
- * @hsotg: Programming view of DWC_otg controller
- *
- * This function flushes the Tx and Rx FIFOs and flushes any entries in the
- * request queues. Host channels are reset to ensure that they are ready for
- * performing transfers.
- */
-void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
-{
-       u32 hcfg, hfir, otgctl;
-
-       dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
-
-       /* Restart the Phy Clock */
-       DWC2_WRITE_4(hsotg, PCGCTL, 0);
-
-       /* Initialize Host Configuration Register */
-       dwc2_init_fs_ls_pclk_sel(hsotg);
-       if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL) {
-               hcfg = DWC2_READ_4(hsotg, HCFG);
-               hcfg |= HCFG_FSLSSUPP;
-               DWC2_WRITE_4(hsotg, HCFG, hcfg);
-       }
-
-       /*
-        * This bit allows dynamic reloading of the HFIR register during
-        * runtime. This bit needs to be programmed during initial configuration
-        * and its value must not be changed during runtime.
-        */
-       if (hsotg->core_params->reload_ctl > 0) {
-               hfir = DWC2_READ_4(hsotg, HFIR);
-               hfir |= HFIR_RLDCTRL;
-               DWC2_WRITE_4(hsotg, HFIR, hfir);
-       }
-
-       if (hsotg->core_params->dma_desc_enable > 0) {
-               u32 op_mode = hsotg->hw_params.op_mode;
-               if (hsotg->hw_params.snpsid < DWC2_CORE_REV_2_90a ||
-                   !hsotg->hw_params.dma_desc_enable ||
-                   op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE ||
-                   op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE ||
-                   op_mode == GHWCFG2_OP_MODE_UNDEFINED) {
-                       dev_err(hsotg->dev,
-                               "Hardware does not support descriptor DMA mode -\n");
-                       dev_err(hsotg->dev,
-                               "falling back to buffer DMA mode.\n");
-                       hsotg->core_params->dma_desc_enable = 0;
-               } else {
-                       hcfg = DWC2_READ_4(hsotg, HCFG);
-                       hcfg |= HCFG_DESCDMA;
-                       DWC2_WRITE_4(hsotg, HCFG, hcfg);
-               }
-       }
-
-       /* Configure data FIFO sizes */
-       dwc2_config_fifos(hsotg);
-
-       /* TODO - check this */
-       /* Clear Host Set HNP Enable in the OTG Control Register */
-       otgctl = DWC2_READ_4(hsotg, GOTGCTL);
-       otgctl &= ~GOTGCTL_HSTSETHNPEN;
-       DWC2_WRITE_4(hsotg, GOTGCTL, otgctl);
-
-       /* Make sure the FIFOs are flushed */
-       dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */);
-       dwc2_flush_rx_fifo(hsotg);
-
-       /* Clear Host Set HNP Enable in the OTG Control Register */
-       otgctl = DWC2_READ_4(hsotg, GOTGCTL);
-       otgctl &= ~GOTGCTL_HSTSETHNPEN;
-       DWC2_WRITE_4(hsotg, GOTGCTL, otgctl);
-
-       if (hsotg->core_params->dma_desc_enable <= 0) {
-               int num_channels, i;
-               u32 hcchar;
-
-               /* Flush out any leftover queued requests */
-               num_channels = hsotg->core_params->host_channels;
-               for (i = 0; i < num_channels; i++) {
-                       hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
-                       hcchar &= ~HCCHAR_CHENA;
-                       hcchar |= HCCHAR_CHDIS;
-                       hcchar &= ~HCCHAR_EPDIR;
-                       DWC2_WRITE_4(hsotg, HCCHAR(i), hcchar);
-               }
-
-               /* Halt all channels to put them into a known state */
-               for (i = 0; i < num_channels; i++) {
-                       int count = 0;
-
-                       hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
-                       hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
-                       hcchar &= ~HCCHAR_EPDIR;
-                       DWC2_WRITE_4(hsotg, HCCHAR(i), hcchar);
-                       dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
-                               __func__, i);
-                       do {
-                               hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
-                               if (++count > 1000) {
-                                       dev_err(hsotg->dev,
-                                               "Unable to clear enable on channel %d\n",
-                                               i);
-                                       break;
-                               }
-                               udelay(1);
-                       } while (hcchar & HCCHAR_CHENA);
-               }
-       }
-
-       /* Turn on the vbus power */
-       dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state);
-       if (hsotg->op_state == OTG_STATE_A_HOST) {
-               u32 hprt0 = dwc2_read_hprt0(hsotg);
-
-               dev_dbg(hsotg->dev, "Init: Power Port (%d)\n",
-                       !!(hprt0 & HPRT0_PWR));
-               if (!(hprt0 & HPRT0_PWR)) {
-                       hprt0 |= HPRT0_PWR;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-               }
-       }
-
-       dwc2_enable_host_interrupts(hsotg);
-}
-
-STATIC void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg,
-                                     struct dwc2_host_chan *chan)
-{
-       u32 hcintmsk = HCINTMSK_CHHLTD;
-
-       switch (chan->ep_type) {
-       case USB_ENDPOINT_XFER_CONTROL:
-       case USB_ENDPOINT_XFER_BULK:
-               dev_vdbg(hsotg->dev, "control/bulk\n");
-               hcintmsk |= HCINTMSK_XFERCOMPL;
-               hcintmsk |= HCINTMSK_STALL;
-               hcintmsk |= HCINTMSK_XACTERR;
-               hcintmsk |= HCINTMSK_DATATGLERR;
-               if (chan->ep_is_in) {
-                       hcintmsk |= HCINTMSK_BBLERR;
-               } else {
-                       hcintmsk |= HCINTMSK_NAK;
-                       hcintmsk |= HCINTMSK_NYET;
-                       if (chan->do_ping)
-                               hcintmsk |= HCINTMSK_ACK;
-               }
-
-               if (chan->do_split) {
-                       hcintmsk |= HCINTMSK_NAK;
-                       if (chan->complete_split)
-                               hcintmsk |= HCINTMSK_NYET;
-                       else
-                               hcintmsk |= HCINTMSK_ACK;
-               }
-
-               if (chan->error_state)
-                       hcintmsk |= HCINTMSK_ACK;
-               break;
-
-       case USB_ENDPOINT_XFER_INT:
-               if (dbg_perio())
-                       dev_vdbg(hsotg->dev, "intr\n");
-               hcintmsk |= HCINTMSK_XFERCOMPL;
-               hcintmsk |= HCINTMSK_NAK;
-               hcintmsk |= HCINTMSK_STALL;
-               hcintmsk |= HCINTMSK_XACTERR;
-               hcintmsk |= HCINTMSK_DATATGLERR;
-               hcintmsk |= HCINTMSK_FRMOVRUN;
-
-               if (chan->ep_is_in)
-                       hcintmsk |= HCINTMSK_BBLERR;
-               if (chan->error_state)
-                       hcintmsk |= HCINTMSK_ACK;
-               if (chan->do_split) {
-                       if (chan->complete_split)
-                               hcintmsk |= HCINTMSK_NYET;
-                       else
-                               hcintmsk |= HCINTMSK_ACK;
-               }
-               break;
-
-       case USB_ENDPOINT_XFER_ISOC:
-               if (dbg_perio())
-                       dev_vdbg(hsotg->dev, "isoc\n");
-               hcintmsk |= HCINTMSK_XFERCOMPL;
-               hcintmsk |= HCINTMSK_FRMOVRUN;
-               hcintmsk |= HCINTMSK_ACK;
-
-               if (chan->ep_is_in) {
-                       hcintmsk |= HCINTMSK_XACTERR;
-                       hcintmsk |= HCINTMSK_BBLERR;
-               }
-               break;
-       default:
-               dev_err(hsotg->dev, "## Unknown EP type ##\n");
-               break;
-       }
-
-       DWC2_WRITE_4(hsotg, HCINTMSK(chan->hc_num), hcintmsk);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
-}
-
-STATIC void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg,
-                                   struct dwc2_host_chan *chan)
-{
-       u32 hcintmsk = HCINTMSK_CHHLTD;
-
-       /*
-        * For Descriptor DMA mode core halts the channel on AHB error.
-        * Interrupt is not required.
-        */
-       if (hsotg->core_params->dma_desc_enable <= 0) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "desc DMA disabled\n");
-               hcintmsk |= HCINTMSK_AHBERR;
-       } else {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "desc DMA enabled\n");
-               if (chan->ep_type == USB_ENDPOINT_XFER_ISOC)
-                       hcintmsk |= HCINTMSK_XFERCOMPL;
-       }
-
-       if (chan->error_state && !chan->do_split &&
-           chan->ep_type != USB_ENDPOINT_XFER_ISOC) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "setting ACK\n");
-               hcintmsk |= HCINTMSK_ACK;
-               if (chan->ep_is_in) {
-                       hcintmsk |= HCINTMSK_DATATGLERR;
-                       if (chan->ep_type != USB_ENDPOINT_XFER_INT)
-                               hcintmsk |= HCINTMSK_NAK;
-               }
-       }
-
-       DWC2_WRITE_4(hsotg, HCINTMSK(chan->hc_num), hcintmsk);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
-}
-
-STATIC void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg,
-                               struct dwc2_host_chan *chan)
-{
-       u32 intmsk;
-
-       if (hsotg->core_params->dma_enable > 0) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "DMA enabled\n");
-               dwc2_hc_enable_dma_ints(hsotg, chan);
-       } else {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "DMA disabled\n");
-               dwc2_hc_enable_slave_ints(hsotg, chan);
-       }
-
-       /* Enable the top level host channel interrupt */
-       intmsk = DWC2_READ_4(hsotg, HAINTMSK);
-       intmsk |= 1 << chan->hc_num;
-       DWC2_WRITE_4(hsotg, HAINTMSK, intmsk);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk);
-
-       /* Make sure host channel interrupts are enabled */
-       intmsk = DWC2_READ_4(hsotg, GINTMSK);
-       intmsk |= GINTSTS_HCHINT;
-       DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk);
-}
-
-/**
- * dwc2_hc_init() - Prepares a host channel for transferring packets to/from
- * a specific endpoint
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Information needed to initialize the host channel
- *
- * The HCCHARn register is set up with the characteristics specified in chan.
- * Host channel interrupts that may need to be serviced while this transfer is
- * in progress are enabled.
- */
-void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
-{
-       u8 hc_num = chan->hc_num;
-       u32 hcintmsk;
-       u32 hcchar;
-       u32 hcsplt = 0;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-       /* Clear old interrupt conditions for this host channel */
-       hcintmsk = 0xffffffff;
-       hcintmsk &= ~HCINTMSK_RESERVED14_31;
-       DWC2_WRITE_4(hsotg, HCINT(hc_num), hcintmsk);
-
-       /* Enable channel interrupts required for this transfer */
-       dwc2_hc_enable_ints(hsotg, chan);
-
-       /*
-        * Program the HCCHARn register with the endpoint characteristics for
-        * the current transfer
-        */
-       hcchar = chan->dev_addr << HCCHAR_DEVADDR_SHIFT & HCCHAR_DEVADDR_MASK;
-       hcchar |= chan->ep_num << HCCHAR_EPNUM_SHIFT & HCCHAR_EPNUM_MASK;
-       if (chan->ep_is_in)
-               hcchar |= HCCHAR_EPDIR;
-       if (chan->speed == USB_SPEED_LOW)
-               hcchar |= HCCHAR_LSPDDEV;
-       hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK;
-       hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK;
-       DWC2_WRITE_4(hsotg, HCCHAR(hc_num), hcchar);
-       if (dbg_hc(chan)) {
-               dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n",
-                        hc_num, hcchar);
-
-               dev_vdbg(hsotg->dev, "%s: Channel %d\n",
-                        __func__, hc_num);
-               dev_vdbg(hsotg->dev, "   Dev Addr: %d\n",
-                        chan->dev_addr);
-               dev_vdbg(hsotg->dev, "   Ep Num: %d\n",
-                        chan->ep_num);
-               dev_vdbg(hsotg->dev, "   Is In: %d\n",
-                        chan->ep_is_in);
-               dev_vdbg(hsotg->dev, "   Is Low Speed: %d\n",
-                        chan->speed == USB_SPEED_LOW);
-               dev_vdbg(hsotg->dev, "   Ep Type: %d\n",
-                        chan->ep_type);
-               dev_vdbg(hsotg->dev, "   Max Pkt: %d\n",
-                        chan->max_packet);
-       }
-
-       /* Program the HCSPLT register for SPLITs */
-       if (chan->do_split) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev,
-                                "Programming HC %d with split --> %s\n",
-                                hc_num,
-                                chan->complete_split ? "CSPLIT" : "SSPLIT");
-               if (chan->complete_split)
-                       hcsplt |= HCSPLT_COMPSPLT;
-               hcsplt |= chan->xact_pos << HCSPLT_XACTPOS_SHIFT &
-                         HCSPLT_XACTPOS_MASK;
-               hcsplt |= chan->hub_addr << HCSPLT_HUBADDR_SHIFT &
-                         HCSPLT_HUBADDR_MASK;
-               hcsplt |= chan->hub_port << HCSPLT_PRTADDR_SHIFT &
-                         HCSPLT_PRTADDR_MASK;
-               if (dbg_hc(chan)) {
-                       dev_vdbg(hsotg->dev, "    comp split %d\n",
-                                chan->complete_split);
-                       dev_vdbg(hsotg->dev, "    xact pos %d\n",
-                                chan->xact_pos);
-                       dev_vdbg(hsotg->dev, "    hub addr %d\n",
-                                chan->hub_addr);
-                       dev_vdbg(hsotg->dev, "    hub port %d\n",
-                                chan->hub_port);
-                       dev_vdbg(hsotg->dev, "    is_in %d\n",
-                                chan->ep_is_in);
-                       dev_vdbg(hsotg->dev, "    Max Pkt %d\n",
-                                chan->max_packet);
-                       dev_vdbg(hsotg->dev, "    xferlen %d\n",
-                                chan->xfer_len);
-               }
-       }
-
-       DWC2_WRITE_4(hsotg, HCSPLT(hc_num), hcsplt);
-}
-
-/**
- * dwc2_hc_halt() - Attempts to halt a host channel
- *
- * @hsotg:       Controller register interface
- * @chan:        Host channel to halt
- * @halt_status: Reason for halting the channel
- *
- * This function should only be called in Slave mode or to abort a transfer in
- * either Slave mode or DMA mode. Under normal circumstances in DMA mode, the
- * controller halts the channel when the transfer is complete or a condition
- * occurs that requires application intervention.
- *
- * In slave mode, checks for a free request queue entry, then sets the Channel
- * Enable and Channel Disable bits of the Host Channel Characteristics
- * register of the specified channel to intiate the halt. If there is no free
- * request queue entry, sets only the Channel Disable bit of the HCCHARn
- * register to flush requests for this channel. In the latter case, sets a
- * flag to indicate that the host channel needs to be halted when a request
- * queue slot is open.
- *
- * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
- * HCCHARn register. The controller ensures there is space in the request
- * queue before submitting the halt request.
- *
- * Some time may elapse before the core flushes any posted requests for this
- * host channel and halts. The Channel Halted interrupt handler completes the
- * deactivation of the host channel.
- */
-void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
-                 enum dwc2_halt_status halt_status)
-{
-       u32 nptxsts, hptxsts, hcchar;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "%s()\n", __func__);
-       if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS)
-               dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status);
-
-       if (halt_status == DWC2_HC_XFER_URB_DEQUEUE ||
-           halt_status == DWC2_HC_XFER_AHB_ERR) {
-               /*
-                * Disable all channel interrupts except Ch Halted. The QTD
-                * and QH state associated with this transfer has been cleared
-                * (in the case of URB_DEQUEUE), so the channel needs to be
-                * shut down carefully to prevent crashes.
-                */
-               u32 hcintmsk = HCINTMSK_CHHLTD;
-
-               dev_vdbg(hsotg->dev, "dequeue/error\n");
-               DWC2_WRITE_4(hsotg, HCINTMSK(chan->hc_num), hcintmsk);
-
-               /*
-                * Make sure no other interrupts besides halt are currently
-                * pending. Handling another interrupt could cause a crash due
-                * to the QTD and QH state.
-                */
-               DWC2_WRITE_4(hsotg, HCINT(chan->hc_num), ~hcintmsk);
-
-               /*
-                * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
-                * even if the channel was already halted for some other
-                * reason
-                */
-               chan->halt_status = halt_status;
-
-               hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-               if (!(hcchar & HCCHAR_CHENA)) {
-                       /*
-                        * The channel is either already halted or it hasn't
-                        * started yet. In DMA mode, the transfer may halt if
-                        * it finishes normally or a condition occurs that
-                        * requires driver intervention. Don't want to halt
-                        * the channel again. In either Slave or DMA mode,
-                        * it's possible that the transfer has been assigned
-                        * to a channel, but not started yet when an URB is
-                        * dequeued. Don't want to halt a channel that hasn't
-                        * started yet.
-                        */
-                       return;
-               }
-       }
-       if (chan->halt_pending) {
-               /*
-                * A halt has already been issued for this channel. This might
-                * happen when a transfer is aborted by a higher level in
-                * the stack.
-                */
-               dev_vdbg(hsotg->dev,
-                        "*** %s: Channel %d, chan->halt_pending already set ***\n",
-                        __func__, chan->hc_num);
-               return;
-       }
-
-       hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-
-       /* No need to set the bit in DDMA for disabling the channel */
-       /* TODO check it everywhere channel is disabled */
-       if (hsotg->core_params->dma_desc_enable <= 0) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "desc DMA disabled\n");
-               hcchar |= HCCHAR_CHENA;
-       } else {
-               if (dbg_hc(chan))
-                       dev_dbg(hsotg->dev, "desc DMA enabled\n");
-       }
-       hcchar |= HCCHAR_CHDIS;
-
-       if (hsotg->core_params->dma_enable <= 0) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "DMA not enabled\n");
-               hcchar |= HCCHAR_CHENA;
-
-               /* Check for space in the request queue to issue the halt */
-               if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
-                   chan->ep_type == USB_ENDPOINT_XFER_BULK) {
-                       dev_vdbg(hsotg->dev, "control/bulk\n");
-                       nptxsts = DWC2_READ_4(hsotg, GNPTXSTS);
-                       if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) {
-                               dev_vdbg(hsotg->dev, "Disabling channel\n");
-                               hcchar &= ~HCCHAR_CHENA;
-                       }
-               } else {
-                       if (dbg_perio())
-                               dev_vdbg(hsotg->dev, "isoc/intr\n");
-                       hptxsts = DWC2_READ_4(hsotg, HPTXSTS);
-                       if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 ||
-                           hsotg->queuing_high_bandwidth) {
-                               if (dbg_perio())
-                                       dev_vdbg(hsotg->dev, "Disabling channel\n");
-                               hcchar &= ~HCCHAR_CHENA;
-                       }
-               }
-       } else {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "DMA enabled\n");
-       }
-
-       DWC2_WRITE_4(hsotg, HCCHAR(chan->hc_num), hcchar);
-       chan->halt_status = halt_status;
-
-       if (hcchar & HCCHAR_CHENA) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "Channel enabled\n");
-               chan->halt_pending = 1;
-               chan->halt_on_queue = 0;
-       } else {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "Channel disabled\n");
-               chan->halt_on_queue = 1;
-       }
-
-       if (dbg_hc(chan)) {
-               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
-                        chan->hc_num);
-               dev_vdbg(hsotg->dev, "   hcchar: 0x%08x\n",
-                        hcchar);
-               dev_vdbg(hsotg->dev, "   halt_pending: %d\n",
-                        chan->halt_pending);
-               dev_vdbg(hsotg->dev, "   halt_on_queue: %d\n",
-                        chan->halt_on_queue);
-               dev_vdbg(hsotg->dev, "   halt_status: %d\n",
-                        chan->halt_status);
-       }
-}
-
-/**
- * dwc2_hc_cleanup() - Clears the transfer state for a host channel
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Identifies the host channel to clean up
- *
- * This function is normally called after a transfer is done and the host
- * channel is being released
- */
-void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
-{
-       u32 hcintmsk;
-
-       chan->xfer_started = 0;
-
-       list_del_init(&chan->split_order_list_entry);
-
-       /*
-        * Clear channel interrupt enables and any unhandled channel interrupt
-        * conditions
-        */
-       DWC2_WRITE_4(hsotg, HCINTMSK(chan->hc_num), 0);
-       hcintmsk = 0xffffffff;
-       hcintmsk &= ~HCINTMSK_RESERVED14_31;
-       DWC2_WRITE_4(hsotg, HCINT(chan->hc_num), hcintmsk);
-}
-
-/**
- * dwc2_hc_set_even_odd_frame() - Sets the channel property that indicates in
- * which frame a periodic transfer should occur
- *
- * @hsotg:  Programming view of DWC_otg controller
- * @chan:   Identifies the host channel to set up and its properties
- * @hcchar: Current value of the HCCHAR register for the specified host channel
- *
- * This function has no effect on non-periodic transfers
- */
-STATIC void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
-                                      struct dwc2_host_chan *chan, u32 *hcchar)
-{
-       if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
-           chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-               /* 1 if _next_ frame is odd, 0 if it's even */
-               if (!(dwc2_hcd_get_frame_number(hsotg) & 0x1))
-                       *hcchar |= HCCHAR_ODDFRM;
-       }
-}
-
-STATIC void dwc2_set_pid_isoc(struct dwc2_host_chan *chan)
-{
-       /* Set up the initial PID for the transfer */
-       if (chan->speed == USB_SPEED_HIGH) {
-               if (chan->ep_is_in) {
-                       if (chan->multi_count == 1)
-                               chan->data_pid_start = DWC2_HC_PID_DATA0;
-                       else if (chan->multi_count == 2)
-                               chan->data_pid_start = DWC2_HC_PID_DATA1;
-                       else
-                               chan->data_pid_start = DWC2_HC_PID_DATA2;
-               } else {
-                       if (chan->multi_count == 1)
-                               chan->data_pid_start = DWC2_HC_PID_DATA0;
-                       else
-                               chan->data_pid_start = DWC2_HC_PID_MDATA;
-               }
-       } else {
-               chan->data_pid_start = DWC2_HC_PID_DATA0;
-       }
-}
-
-/**
- * dwc2_hc_write_packet() - Writes a packet into the Tx FIFO associated with
- * the Host Channel
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Information needed to initialize the host channel
- *
- * This function should only be called in Slave mode. For a channel associated
- * with a non-periodic EP, the non-periodic Tx FIFO is written. For a channel
- * associated with a periodic EP, the periodic Tx FIFO is written.
- *
- * Upon return the xfer_buf and xfer_count fields in chan are incremented by
- * the number of bytes written to the Tx FIFO.
- */
-STATIC void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
-                                struct dwc2_host_chan *chan)
-{
-       u32 i;
-       u32 remaining_count;
-       u32 byte_count;
-       u32 dword_count;
-       u32 *data_buf = (u32 *)chan->xfer_buf;
-       u32 data_fifo;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-       data_fifo = HCFIFO(chan->hc_num);
-
-       remaining_count = chan->xfer_len - chan->xfer_count;
-       if (remaining_count > chan->max_packet)
-               byte_count = chan->max_packet;
-       else
-               byte_count = remaining_count;
-
-       dword_count = (byte_count + 3) / 4;
-
-       if (((unsigned long)data_buf & 0x3) == 0) {
-               /* xfer_buf is DWORD aligned */
-               for (i = 0; i < dword_count; i++, data_buf++)
-                       DWC2_WRITE_4(hsotg, data_fifo, *data_buf);
-       } else {
-               /* xfer_buf is not DWORD aligned */
-               for (i = 0; i < dword_count; i++, data_buf++) {
-                       u32 data = data_buf[0] | data_buf[1] << 8 |
-                                  data_buf[2] << 16 | data_buf[3] << 24;
-                       DWC2_WRITE_4(hsotg, data_fifo, data);
-               }
-       }
-
-       chan->xfer_count += byte_count;
-       chan->xfer_buf += byte_count;
-}
-
-/**
- * dwc2_hc_start_transfer() - Does the setup for a data transfer for a host
- * channel and starts the transfer
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Information needed to initialize the host channel. The xfer_len value
- *         may be reduced to accommodate the max widths of the XferSize and
- *         PktCnt fields in the HCTSIZn register. The multi_count value may be
- *         changed to reflect the final xfer_len value.
- *
- * This function may be called in either Slave mode or DMA mode. In Slave mode,
- * the caller must ensure that there is sufficient space in the request queue
- * and Tx Data FIFO.
- *
- * For an OUT transfer in Slave mode, it loads a data packet into the
- * appropriate FIFO. If necessary, additional data packets are loaded in the
- * Host ISR.
- *
- * For an IN transfer in Slave mode, a data packet is requested. The data
- * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
- * additional data packets are requested in the Host ISR.
- *
- * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
- * register along with a packet count of 1 and the channel is enabled. This
- * causes a single PING transaction to occur. Other fields in HCTSIZ are
- * simply set to 0 since no data transfer occurs in this case.
- *
- * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
- * all the information required to perform the subsequent data transfer. In
- * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
- * controller performs the entire PING protocol, then starts the data
- * transfer.
- */
-void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
-                           struct dwc2_host_chan *chan)
-{
-       u32 max_hc_xfer_size = hsotg->core_params->max_transfer_size;
-       u16 max_hc_pkt_count = hsotg->core_params->max_packet_count;
-       u32 hcchar;
-       u32 hctsiz = 0;
-       u16 num_packets;
-       u32 ec_mc;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-       if (chan->do_ping) {
-               if (hsotg->core_params->dma_enable <= 0) {
-                       if (dbg_hc(chan))
-                               dev_vdbg(hsotg->dev, "ping, no DMA\n");
-                       dwc2_hc_do_ping(hsotg, chan);
-                       chan->xfer_started = 1;
-                       return;
-               } else {
-                       if (dbg_hc(chan))
-                               dev_vdbg(hsotg->dev, "ping, DMA\n");
-                       hctsiz |= TSIZ_DOPNG;
-               }
-       }
-
-       if (chan->do_split) {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "split\n");
-               num_packets = 1;
-
-               if (chan->complete_split && !chan->ep_is_in)
-                       /*
-                        * For CSPLIT OUT Transfer, set the size to 0 so the
-                        * core doesn't expect any data written to the FIFO
-                        */
-                       chan->xfer_len = 0;
-               else if (chan->ep_is_in || chan->xfer_len > chan->max_packet)
-                       chan->xfer_len = chan->max_packet;
-               else if (!chan->ep_is_in && chan->xfer_len > 188)
-                       chan->xfer_len = 188;
-
-               hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
-                         TSIZ_XFERSIZE_MASK;
-
-               /* For split set ec_mc for immediate retries */
-               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
-                   chan->ep_type == USB_ENDPOINT_XFER_ISOC)
-                       ec_mc = 3;
-               else
-                       ec_mc = 1;
-       } else {
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "no split\n");
-               /*
-                * Ensure that the transfer length and packet count will fit
-                * in the widths allocated for them in the HCTSIZn register
-                */
-               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
-                   chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-                       /*
-                        * Make sure the transfer size is no larger than one
-                        * (micro)frame's worth of data. (A check was done
-                        * when the periodic transfer was accepted to ensure
-                        * that a (micro)frame's worth of data can be
-                        * programmed into a channel.)
-                        */
-                       u32 max_periodic_len =
-                               chan->multi_count * chan->max_packet;
-
-                       if (chan->xfer_len > max_periodic_len)
-                               chan->xfer_len = max_periodic_len;
-               } else if (chan->xfer_len > max_hc_xfer_size) {
-                       /*
-                        * Make sure that xfer_len is a multiple of max packet
-                        * size
-                        */
-                       chan->xfer_len =
-                               max_hc_xfer_size - chan->max_packet + 1;
-               }
-
-               if (chan->xfer_len > 0) {
-                       num_packets = (chan->xfer_len + chan->max_packet - 1) /
-                                       chan->max_packet;
-                       if (num_packets > max_hc_pkt_count) {
-                               num_packets = max_hc_pkt_count;
-                               chan->xfer_len = num_packets * chan->max_packet;
-                       }
-               } else {
-                       /* Need 1 packet for transfer length of 0 */
-                       num_packets = 1;
-               }
-
-               if (chan->ep_is_in)
-                       /*
-                        * Always program an integral # of max packets for IN
-                        * transfers
-                        */
-                       chan->xfer_len = num_packets * chan->max_packet;
-
-               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
-                   chan->ep_type == USB_ENDPOINT_XFER_ISOC)
-                       /*
-                        * Make sure that the multi_count field matches the
-                        * actual transfer length
-                        */
-                       chan->multi_count = num_packets;
-
-               if (chan->ep_type == USB_ENDPOINT_XFER_ISOC)
-                       dwc2_set_pid_isoc(chan);
-
-               hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
-                         TSIZ_XFERSIZE_MASK;
-
-               /* The ec_mc gets the multi_count for non-split */
-               ec_mc = chan->multi_count;
-       }
-
-       chan->start_pkt_count = num_packets;
-       hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK;
-       hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT &
-                 TSIZ_SC_MC_PID_MASK;
-       DWC2_WRITE_4(hsotg, HCTSIZ(chan->hc_num), hctsiz);
-       if (dbg_hc(chan)) {
-               dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n",
-                        hctsiz, chan->hc_num);
-
-               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
-                        chan->hc_num);
-               dev_vdbg(hsotg->dev, "   Xfer Size: %d\n",
-                        (hctsiz & TSIZ_XFERSIZE_MASK) >>
-                        TSIZ_XFERSIZE_SHIFT);
-               dev_vdbg(hsotg->dev, "   Num Pkts: %d\n",
-                        (hctsiz & TSIZ_PKTCNT_MASK) >>
-                        TSIZ_PKTCNT_SHIFT);
-               dev_vdbg(hsotg->dev, "   Start PID: %d\n",
-                        (hctsiz & TSIZ_SC_MC_PID_MASK) >>
-                        TSIZ_SC_MC_PID_SHIFT);
-       }
-
-       if (hsotg->core_params->dma_enable > 0) {
-               dma_addr_t dma_addr;
-
-               if (chan->align_buf) {
-                       if (dbg_hc(chan))
-                               dev_vdbg(hsotg->dev, "align_buf\n");
-                       dma_addr = chan->align_buf;
-               } else {
-                       dma_addr = chan->xfer_dma;
-               }
-               if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
-                       DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num),
-                           (u32)dma_addr);
-                       if (dbg_hc(chan))
-                               dev_vdbg(hsotg->dev,
-                                   "Wrote %08lx to HCDMA(%d)\n",
-                                    (unsigned long)dma_addr,
-                                   chan->hc_num);
-               } else {
-                       (void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
-                           hsotg->dev, dma_addr, chan->hc_num);
-               }
-       }
-
-       /* Start the split */
-       if (chan->do_split) {
-               u32 hcsplt = DWC2_READ_4(hsotg, HCSPLT(chan->hc_num));
-
-               hcsplt |= HCSPLT_SPLTENA;
-               DWC2_WRITE_4(hsotg, HCSPLT(chan->hc_num), hcsplt);
-       }
-
-       hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-       hcchar &= ~HCCHAR_MULTICNT_MASK;
-       hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
-       dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
-
-       if (hcchar & HCCHAR_CHDIS)
-               dev_warn(hsotg->dev,
-                        "%s: chdis set, channel %d, hcchar 0x%08x\n",
-                        __func__, chan->hc_num, hcchar);
-
-       /* Set host channel enable after all other setup is complete */
-       hcchar |= HCCHAR_CHENA;
-       hcchar &= ~HCCHAR_CHDIS;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "   Multi Cnt: %d\n",
-                        (hcchar & HCCHAR_MULTICNT_MASK) >>
-                        HCCHAR_MULTICNT_SHIFT);
-
-       DWC2_WRITE_4(hsotg, HCCHAR(chan->hc_num), hcchar);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
-                        chan->hc_num);
-
-       chan->xfer_started = 1;
-       chan->requests++;
-
-       if (hsotg->core_params->dma_enable <= 0 &&
-           !chan->ep_is_in && chan->xfer_len > 0)
-               /* Load OUT packet into the appropriate Tx FIFO */
-               dwc2_hc_write_packet(hsotg, chan);
-}
-
-/**
- * dwc2_hc_start_transfer_ddma() - Does the setup for a data transfer for a
- * host channel and starts the transfer in Descriptor DMA mode
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Information needed to initialize the host channel
- *
- * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set.
- * Sets PID and NTD values. For periodic transfers initializes SCHED_INFO field
- * with micro-frame bitmap.
- *
- * Initializes HCDMA register with descriptor list address and CTD value then
- * starts the transfer via enabling the channel.
- */
-void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
-                                struct dwc2_host_chan *chan)
-{
-       u32 hcchar;
-       u32 hctsiz = 0;
-
-       if (chan->do_ping)
-               hctsiz |= TSIZ_DOPNG;
-
-       if (chan->ep_type == USB_ENDPOINT_XFER_ISOC)
-               dwc2_set_pid_isoc(chan);
-
-       /* Packet Count and Xfer Size are not used in Descriptor DMA mode */
-       hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT &
-                 TSIZ_SC_MC_PID_MASK;
-
-       /* 0 - 1 descriptor, 1 - 2 descriptors, etc */
-       hctsiz |= (chan->ntd - 1) << TSIZ_NTD_SHIFT & TSIZ_NTD_MASK;
-
-       /* Non-zero only for high-speed interrupt endpoints */
-       hctsiz |= chan->schinfo << TSIZ_SCHINFO_SHIFT & TSIZ_SCHINFO_MASK;
-
-       if (dbg_hc(chan)) {
-               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
-                        chan->hc_num);
-               dev_vdbg(hsotg->dev, "   Start PID: %d\n",
-                        chan->data_pid_start);
-               dev_vdbg(hsotg->dev, "   NTD: %d\n", chan->ntd - 1);
-       }
-
-       DWC2_WRITE_4(hsotg, HCTSIZ(chan->hc_num), hctsiz);
-
-       usb_syncmem(&chan->desc_list_usbdma, 0, chan->desc_list_sz,
-           BUS_DMASYNC_PREWRITE);
-
-       if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
-               DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num), chan->desc_list_addr);
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
-                               &chan->desc_list_addr, chan->hc_num);
-       } else {
-               (void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
-                   hsotg->dev, chan->desc_list_addr, chan->hc_num);
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "Wrote %pad to ext dma(%d)\n",
-                               &chan->desc_list_addr, chan->hc_num);
-       }
-
-       hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-       hcchar &= ~HCCHAR_MULTICNT_MASK;
-       hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
-                 HCCHAR_MULTICNT_MASK;
-
-       if (hcchar & HCCHAR_CHDIS)
-               dev_warn(hsotg->dev,
-                        "%s: chdis set, channel %d, hcchar 0x%08x\n",
-                        __func__, chan->hc_num, hcchar);
-
-       /* Set host channel enable after all other setup is complete */
-       hcchar |= HCCHAR_CHENA;
-       hcchar &= ~HCCHAR_CHDIS;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "   Multi Cnt: %d\n",
-                        (hcchar & HCCHAR_MULTICNT_MASK) >>
-                        HCCHAR_MULTICNT_SHIFT);
-
-       DWC2_WRITE_4(hsotg, HCCHAR(chan->hc_num), hcchar);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
-                        chan->hc_num);
-
-       chan->xfer_started = 1;
-       chan->requests++;
-}
-
-/**
- * dwc2_hc_continue_transfer() - Continues a data transfer that was started by
- * a previous call to dwc2_hc_start_transfer()
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Information needed to initialize the host channel
- *
- * The caller must ensure there is sufficient space in the request queue and Tx
- * Data FIFO. This function should only be called in Slave mode. In DMA mode,
- * the controller acts autonomously to complete transfers programmed to a host
- * channel.
- *
- * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
- * if there is any data remaining to be queued. For an IN transfer, another
- * data packet is always requested. For the SETUP phase of a control transfer,
- * this function does nothing.
- *
- * Return: 1 if a new request is queued, 0 if no more requests are required
- * for this transfer
- */
-int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
-                             struct dwc2_host_chan *chan)
-{
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
-                        chan->hc_num);
-
-       if (chan->do_split)
-               /* SPLITs always queue just once per channel */
-               return 0;
-
-       if (chan->data_pid_start == DWC2_HC_PID_SETUP)
-               /* SETUPs are queued only once since they can't be NAK'd */
-               return 0;
-
-       if (chan->ep_is_in) {
-               /*
-                * Always queue another request for other IN transfers. If
-                * back-to-back INs are issued and NAKs are received for both,
-                * the driver may still be processing the first NAK when the
-                * second NAK is received. When the interrupt handler clears
-                * the NAK interrupt for the first NAK, the second NAK will
-                * not be seen. So we can't depend on the NAK interrupt
-                * handler to requeue a NAK'd request. Instead, IN requests
-                * are issued each time this function is called. When the
-                * transfer completes, the extra requests for the channel will
-                * be flushed.
-                */
-               u32 hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-
-               dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
-               hcchar |= HCCHAR_CHENA;
-               hcchar &= ~HCCHAR_CHDIS;
-               if (dbg_hc(chan))
-                       dev_vdbg(hsotg->dev, "   IN xfer: hcchar = 0x%08x\n",
-                                hcchar);
-               DWC2_WRITE_4(hsotg, HCCHAR(chan->hc_num), hcchar);
-               chan->requests++;
-               return 1;
-       }
-
-       /* OUT transfers */
-
-       if (chan->xfer_count < chan->xfer_len) {
-               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
-                   chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-                       u32 hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-
-                       dwc2_hc_set_even_odd_frame(hsotg, chan,
-                                                  &hcchar);
-               }
-
-               /* Load OUT packet into the appropriate Tx FIFO */
-               dwc2_hc_write_packet(hsotg, chan);
-               chan->requests++;
-               return 1;
-       }
-
-       return 0;
-}
-
-/**
- * dwc2_hc_do_ping() - Starts a PING transfer
- *
- * @hsotg: Programming view of DWC_otg controller
- * @chan:  Information needed to initialize the host channel
- *
- * This function should only be called in Slave mode. The Do Ping bit is set in
- * the HCTSIZ register, then the channel is enabled.
- */
-void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
-{
-       u32 hcchar;
-       u32 hctsiz;
-
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
-                        chan->hc_num);
-
-
-       hctsiz = TSIZ_DOPNG;
-       hctsiz |= 1 << TSIZ_PKTCNT_SHIFT;
-       DWC2_WRITE_4(hsotg, HCTSIZ(chan->hc_num), hctsiz);
-
-       hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-       hcchar |= HCCHAR_CHENA;
-       hcchar &= ~HCCHAR_CHDIS;
-       DWC2_WRITE_4(hsotg, HCCHAR(chan->hc_num), hcchar);
-}
-
-/**
- * dwc2_calc_frame_interval() - Calculates the correct frame Interval value for
- * the HFIR register according to PHY type and speed
- *
- * @hsotg: Programming view of DWC_otg controller
- *
- * NOTE: The caller can modify the value of the HFIR register only after the
- * Port Enable bit of the Host Port Control and Status register (HPRT.EnaPort)
- * has been set
- */
-u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
-{
-       u32 usbcfg;
-       u32 hprt0;
-       int clock = 60; /* default value */
-
-       usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-       hprt0 = DWC2_READ_4(hsotg, HPRT0);
-
-       if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) &&
-           !(usbcfg & GUSBCFG_PHYIF16))
-               clock = 60;
-       if ((usbcfg & GUSBCFG_PHYSEL) && hsotg->hw_params.fs_phy_type ==
-           GHWCFG2_FS_PHY_TYPE_SHARED_ULPI)
-               clock = 48;
-       if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) &&
-           !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16))
-               clock = 30;
-       if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) &&
-           !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16))
-               clock = 60;
-       if ((usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) &&
-           !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16))
-               clock = 48;
-       if ((usbcfg & GUSBCFG_PHYSEL) && !(usbcfg & GUSBCFG_PHYIF16) &&
-           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_SHARED_UTMI)
-               clock = 48;
-       if ((usbcfg & GUSBCFG_PHYSEL) &&
-           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
-               clock = 48;
-
-       if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED)
-               /* High speed case */
-               return 125 * clock;
-       else
-               /* FS/LS case */
-               return 1000 * clock;
-}
-
-/**
- * dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination
- * buffer
- *
- * @core_if: Programming view of DWC_otg controller
- * @dest:    Destination buffer for the packet
- * @bytes:   Number of bytes to copy to the destination
- */
-void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
-{
-       bus_size_t fifo = HCFIFO(0);
-       u32 *data_buf = (u32 *)dest;
-       int word_count = (bytes + 3) / 4;
-       int i;
-
-       /*
-        * Todo: Account for the case where dest is not dword aligned. This
-        * requires reading data from the FIFO into a u32 temp buffer, then
-        * moving it into the data buffer.
-        */
-
-       dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes);
-
-       for (i = 0; i < word_count; i++, data_buf++)
-               *data_buf = DWC2_READ_4(hsotg, fifo);
+               dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n");
+               pcgcctl1 |= PCGCCTL1_GATEEN;
+               dwc2_writel(hsotg, pcgcctl1, PCGCCTL1);
+       }
 }
 
 /**
@@ -2336,56 +661,57 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
        dev_dbg(hsotg->dev, "Host Global Registers\n");
        addr = HCFG;
        dev_dbg(hsotg->dev, "HCFG        @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HCFG));
        addr = HFIR;
        dev_dbg(hsotg->dev, "HFIR        @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HFIR));
        addr = HFNUM;
        dev_dbg(hsotg->dev, "HFNUM       @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HFNUM));
        addr = HPTXSTS;
        dev_dbg(hsotg->dev, "HPTXSTS     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HPTXSTS));
        addr = HAINT;
        dev_dbg(hsotg->dev, "HAINT       @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HAINT));
        addr = HAINTMSK;
        dev_dbg(hsotg->dev, "HAINTMSK    @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
-       if (hsotg->core_params->dma_desc_enable > 0) {
+               (unsigned long)addr, dwc2_readl(hsotg, HAINTMSK));
+       if (hsotg->params.dma_desc_enable) {
                addr = HFLBADDR;
                dev_dbg(hsotg->dev, "HFLBADDR @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                       (unsigned long)addr, dwc2_readl(hsotg, HFLBADDR));
        }
 
        addr = HPRT0;
        dev_dbg(hsotg->dev, "HPRT0       @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HPRT0));
 
-       for (i = 0; i < hsotg->core_params->host_channels; i++) {
+       for (i = 0; i < hsotg->params.host_channels; i++) {
                dev_dbg(hsotg->dev, "Host Channel %d Specific Registers\n", i);
                addr = HCCHAR(i);
                dev_dbg(hsotg->dev, "HCCHAR      @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                       (unsigned long)addr, dwc2_readl(hsotg, HCCHAR(i)));
                addr = HCSPLT(i);
                dev_dbg(hsotg->dev, "HCSPLT      @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                       (unsigned long)addr, dwc2_readl(hsotg, HCSPLT(i)));
                addr = HCINT(i);
                dev_dbg(hsotg->dev, "HCINT       @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                       (unsigned long)addr, dwc2_readl(hsotg, HCINT(i)));
                addr = HCINTMSK(i);
                dev_dbg(hsotg->dev, "HCINTMSK    @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                       (unsigned long)addr, dwc2_readl(hsotg, HCINTMSK(i)));
                addr = HCTSIZ(i);
                dev_dbg(hsotg->dev, "HCTSIZ      @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                       (unsigned long)addr, dwc2_readl(hsotg, HCTSIZ(i)));
                addr = HCDMA(i);
                dev_dbg(hsotg->dev, "HCDMA       @0x%08lX : 0x%08X\n",
-                       (unsigned long)addr, DWC2_READ_4(hsotg, addr));
-               if (hsotg->core_params->dma_desc_enable > 0) {
+                       (unsigned long)addr, dwc2_readl(hsotg, HCDMA(i)));
+               if (hsotg->params.dma_desc_enable) {
                        addr = HCDMAB(i);
                        dev_dbg(hsotg->dev, "HCDMAB      @0x%08lX : 0x%08X\n",
-                               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+                               (unsigned long)addr, dwc2_readl(hsotg,
+                                                               HCDMAB(i)));
                }
        }
 #endif
@@ -2407,80 +733,80 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
        dev_dbg(hsotg->dev, "Core Global Registers\n");
        addr = GOTGCTL;
        dev_dbg(hsotg->dev, "GOTGCTL     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GOTGCTL));
        addr = GOTGINT;
        dev_dbg(hsotg->dev, "GOTGINT     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GOTGINT));
        addr = GAHBCFG;
        dev_dbg(hsotg->dev, "GAHBCFG     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GAHBCFG));
        addr = GUSBCFG;
        dev_dbg(hsotg->dev, "GUSBCFG     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GUSBCFG));
        addr = GRSTCTL;
        dev_dbg(hsotg->dev, "GRSTCTL     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GRSTCTL));
        addr = GINTSTS;
        dev_dbg(hsotg->dev, "GINTSTS     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GINTSTS));
        addr = GINTMSK;
        dev_dbg(hsotg->dev, "GINTMSK     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GINTMSK));
        addr = GRXSTSR;
        dev_dbg(hsotg->dev, "GRXSTSR     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GRXSTSR));
        addr = GRXFSIZ;
        dev_dbg(hsotg->dev, "GRXFSIZ     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GRXFSIZ));
        addr = GNPTXFSIZ;
        dev_dbg(hsotg->dev, "GNPTXFSIZ   @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GNPTXFSIZ));
        addr = GNPTXSTS;
        dev_dbg(hsotg->dev, "GNPTXSTS    @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GNPTXSTS));
        addr = GI2CCTL;
        dev_dbg(hsotg->dev, "GI2CCTL     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GI2CCTL));
        addr = GPVNDCTL;
        dev_dbg(hsotg->dev, "GPVNDCTL    @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GPVNDCTL));
        addr = GGPIO;
        dev_dbg(hsotg->dev, "GGPIO       @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GGPIO));
        addr = GUID;
        dev_dbg(hsotg->dev, "GUID        @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GUID));
        addr = GSNPSID;
        dev_dbg(hsotg->dev, "GSNPSID     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GSNPSID));
        addr = GHWCFG1;
        dev_dbg(hsotg->dev, "GHWCFG1     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GHWCFG1));
        addr = GHWCFG2;
        dev_dbg(hsotg->dev, "GHWCFG2     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GHWCFG2));
        addr = GHWCFG3;
        dev_dbg(hsotg->dev, "GHWCFG3     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GHWCFG3));
        addr = GHWCFG4;
        dev_dbg(hsotg->dev, "GHWCFG4     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GHWCFG4));
        addr = GLPMCFG;
        dev_dbg(hsotg->dev, "GLPMCFG     @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GLPMCFG));
        addr = GPWRDN;
        dev_dbg(hsotg->dev, "GPWRDN      @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GPWRDN));
        addr = GDFIFOCFG;
        dev_dbg(hsotg->dev, "GDFIFOCFG   @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, GDFIFOCFG));
        addr = HPTXFSIZ;
        dev_dbg(hsotg->dev, "HPTXFSIZ    @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, HPTXFSIZ));
 
        addr = PCGCTL;
        dev_dbg(hsotg->dev, "PCGCTL      @0x%08lX : 0x%08X\n",
-               (unsigned long)addr, DWC2_READ_4(hsotg, addr));
+               (unsigned long)addr, dwc2_readl(hsotg, PCGCTL));
 #endif
 }
 
@@ -2493,25 +819,21 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
 void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
 {
        u32 greset;
-       int count = 0;
 
        dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num);
 
+       /* Wait for AHB master IDLE state */
+       if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+               dev_warn(hsotg->dev, "%s:  HANG! AHB Idle GRSCTL\n",
+                        __func__);
+
        greset = GRSTCTL_TXFFLSH;
        greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK;
-       DWC2_WRITE_4(hsotg, GRSTCTL, greset);
-
-       do {
-               greset = DWC2_READ_4(hsotg, GRSTCTL);
-               if (++count > 10000) {
-                       dev_warn(hsotg->dev,
-                                "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
-                                __func__, greset,
-                                DWC2_READ_4(hsotg, GNPTXSTS));
-                       break;
-               }
-               udelay(1);
-       } while (greset & GRSTCTL_TXFFLSH);
+       dwc2_writel(hsotg, greset, GRSTCTL);
+
+       if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000))
+               dev_warn(hsotg->dev, "%s:  HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n",
+                        __func__);
 
        /* Wait for at least 3 PHY Clocks */
        udelay(1);
@@ -2525,1038 +847,482 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
 void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
 {
        u32 greset;
-       int count = 0;
 
        dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
+       /* Wait for AHB master IDLE state */
+       if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+               dev_warn(hsotg->dev, "%s:  HANG! AHB Idle GRSCTL\n",
+                        __func__);
+
        greset = GRSTCTL_RXFFLSH;
-       DWC2_WRITE_4(hsotg, GRSTCTL, greset);
+       dwc2_writel(hsotg, greset, GRSTCTL);
 
-       do {
-               greset = DWC2_READ_4(hsotg, GRSTCTL);
-               if (++count > 10000) {
-                       dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n",
-                                __func__, greset);
-                       break;
-               }
-               udelay(1);
-       } while (greset & GRSTCTL_RXFFLSH);
+       /* Wait for RxFIFO flush done */
+       if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000))
+               dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_RXFFLSH\n",
+                        __func__);
 
        /* Wait for at least 3 PHY Clocks */
        udelay(1);
 }
 
-#define DWC2_OUT_OF_BOUNDS(a, b, c)    ((a) < (b) || (a) > (c))
-
-/* Parameter access functions */
-void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       switch (val) {
-       case DWC2_CAP_PARAM_HNP_SRP_CAPABLE:
-               if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE)
-                       valid = 0;
-               break;
-       case DWC2_CAP_PARAM_SRP_ONLY_CAPABLE:
-               switch (hsotg->hw_params.op_mode) {
-               case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
-               case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
-               case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
-               case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
-                       break;
-               default:
-                       valid = 0;
-                       break;
-               }
-               break;
-       case DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE:
-               /* always valid */
-               break;
-       default:
-               valid = 0;
-               break;
-       }
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for otg_cap parameter. Check HW configuration.\n",
-                               val);
-               switch (hsotg->hw_params.op_mode) {
-               case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
-                       val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE;
-                       break;
-               case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
-               case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
-               case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
-                       val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE;
-                       break;
-               default:
-                       val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
-                       break;
-               }
-               dev_dbg(hsotg->dev, "Setting otg_cap to %d\n", val);
-       }
-
-       hsotg->core_params->otg_cap = val;
-}
-
-void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       if (val > 0 && hsotg->hw_params.arch == GHWCFG2_SLAVE_ONLY_ARCH)
-               valid = 0;
-       if (val < 0)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for dma_enable parameter. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.arch != GHWCFG2_SLAVE_ONLY_ARCH;
-               dev_dbg(hsotg->dev, "Setting dma_enable to %d\n", val);
-       }
-
-       hsotg->core_params->dma_enable = val;
-}
-
-void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
-                       !hsotg->hw_params.dma_desc_enable))
-               valid = 0;
-       if (val < 0)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for dma_desc_enable parameter. Check HW configuration.\n",
-                               val);
-               val = (hsotg->core_params->dma_enable > 0 &&
-                       hsotg->hw_params.dma_desc_enable);
-               dev_dbg(hsotg->dev, "Setting dma_desc_enable to %d\n", val);
-       }
-
-       hsotg->core_params->dma_desc_enable = val;
-}
-
-void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
-                       !hsotg->hw_params.dma_desc_enable))
-               valid = 0;
-       if (val < 0)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
-                               val);
-               val = (hsotg->core_params->dma_enable > 0 &&
-                       hsotg->hw_params.dma_desc_enable);
-       }
-
-       hsotg->core_params->dma_desc_fs_enable = val;
-       dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
-}
-
-void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
-                                                int val)
-{
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "Wrong value for host_support_fs_low_power\n");
-                       dev_err(hsotg->dev,
-                               "host_support_fs_low_power must be 0 or 1\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev,
-                       "Setting host_support_fs_low_power to %d\n", val);
-       }
-
-       hsotg->core_params->host_support_fs_ls_low_power = val;
-}
-
-void dwc2_set_param_enable_dynamic_fifo(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       if (val > 0 && !hsotg->hw_params.enable_dynamic_fifo)
-               valid = 0;
-       if (val < 0)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for enable_dynamic_fifo parameter. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.enable_dynamic_fifo;
-               dev_dbg(hsotg->dev, "Setting enable_dynamic_fifo to %d\n", val);
-       }
-
-       hsotg->core_params->enable_dynamic_fifo = val;
-}
-
-void dwc2_set_param_host_rx_fifo_size(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       if (val < 16 || val > hsotg->hw_params.host_rx_fifo_size)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for host_rx_fifo_size. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.host_rx_fifo_size;
-               dev_dbg(hsotg->dev, "Setting host_rx_fifo_size to %d\n", val);
-       }
-
-       hsotg->core_params->host_rx_fifo_size = val;
-}
-
-void dwc2_set_param_host_nperio_tx_fifo_size(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
-
-       if (val < 16 || val > hsotg->hw_params.host_nperio_tx_fifo_size)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.host_nperio_tx_fifo_size;
-               dev_dbg(hsotg->dev, "Setting host_nperio_tx_fifo_size to %d\n",
-                       val);
-       }
-
-       hsotg->core_params->host_nperio_tx_fifo_size = val;
-}
-
-void dwc2_set_param_host_perio_tx_fifo_size(struct dwc2_hsotg *hsotg, int val)
+bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
 {
-       int valid = 1;
-
-       if (val < 16 || val > hsotg->hw_params.host_perio_tx_fifo_size)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.host_perio_tx_fifo_size;
-               dev_dbg(hsotg->dev, "Setting host_perio_tx_fifo_size to %d\n",
-                       val);
-       }
-
-       hsotg->core_params->host_perio_tx_fifo_size = val;
+       if (dwc2_readl(hsotg, GSNPSID) == 0xffffffff)
+               return false;
+       else
+               return true;
 }
 
-void dwc2_set_param_max_transfer_size(struct dwc2_hsotg *hsotg, int val)
+/**
+ * dwc2_enable_global_interrupts() - Enables the controller's Global
+ * Interrupt in the AHB Config register
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg)
 {
-       int valid = 1;
-
-       if (val < 2047 || val > hsotg->hw_params.max_transfer_size)
-               valid = 0;
+       u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
 
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for max_transfer_size. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.max_transfer_size;
-               dev_dbg(hsotg->dev, "Setting max_transfer_size to %d\n", val);
-       }
-
-       hsotg->core_params->max_transfer_size = val;
+       ahbcfg |= GAHBCFG_GLBL_INTR_EN;
+       dwc2_writel(hsotg, ahbcfg, GAHBCFG);
 }
 
-void dwc2_set_param_max_packet_count(struct dwc2_hsotg *hsotg, int val)
+/**
+ * dwc2_disable_global_interrupts() - Disables the controller's Global
+ * Interrupt in the AHB Config register
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
 {
-       int valid = 1;
-
-       if (val < 15 || val > hsotg->hw_params.max_packet_count)
-               valid = 0;
+       u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
 
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for max_packet_count. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.max_packet_count;
-               dev_dbg(hsotg->dev, "Setting max_packet_count to %d\n", val);
-       }
-
-       hsotg->core_params->max_packet_count = val;
+       ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
+       dwc2_writel(hsotg, ahbcfg, GAHBCFG);
 }
 
-void dwc2_set_param_host_channels(struct dwc2_hsotg *hsotg, int val)
+/* Returns the controller's GHWCFG2.OTG_MODE. */
+unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg)
 {
-       int valid = 1;
-
-       if (val < 1 || val > hsotg->hw_params.host_channels)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for host_channels. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.host_channels;
-               dev_dbg(hsotg->dev, "Setting host_channels to %d\n", val);
-       }
+       u32 ghwcfg2 = dwc2_readl(hsotg, GHWCFG2);
 
-       hsotg->core_params->host_channels = val;
+       return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+               GHWCFG2_OP_MODE_SHIFT;
 }
 
-void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val)
+/* Returns true if the controller is capable of DRD. */
+bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
 {
-       int valid = 0;
-       u32 hs_phy_type, fs_phy_type;
-
-       if (DWC2_OUT_OF_BOUNDS(val, DWC2_PHY_TYPE_PARAM_FS,
-                              DWC2_PHY_TYPE_PARAM_ULPI)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev, "Wrong value for phy_type\n");
-                       dev_err(hsotg->dev, "phy_type must be 0, 1 or 2\n");
-               }
-
-               valid = 0;
-       }
+       unsigned int op_mode = dwc2_op_mode(hsotg);
 
-       hs_phy_type = hsotg->hw_params.hs_phy_type;
-       fs_phy_type = hsotg->hw_params.fs_phy_type;
-       if (val == DWC2_PHY_TYPE_PARAM_UTMI &&
-           (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
-            hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
-               valid = 1;
-       else if (val == DWC2_PHY_TYPE_PARAM_ULPI &&
-                (hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI ||
-                 hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
-               valid = 1;
-       else if (val == DWC2_PHY_TYPE_PARAM_FS &&
-                fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
-               valid = 1;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for phy_type. Check HW configuration.\n",
-                               val);
-               val = DWC2_PHY_TYPE_PARAM_FS;
-               if (hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED) {
-                       if (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
-                           hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)
-                               val = DWC2_PHY_TYPE_PARAM_UTMI;
-                       else
-                               val = DWC2_PHY_TYPE_PARAM_ULPI;
-               }
-               dev_dbg(hsotg->dev, "Setting phy_type to %d\n", val);
-       }
-
-       hsotg->core_params->phy_type = val;
-}
-
-STATIC int dwc2_get_param_phy_type(struct dwc2_hsotg *hsotg)
-{
-       return hsotg->core_params->phy_type;
+       return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
+               (op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
 }
 
-void dwc2_set_param_speed(struct dwc2_hsotg *hsotg, int val)
+/* Returns true if the controller is host-only. */
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
 {
-       int valid = 1;
-
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev, "Wrong value for speed parameter\n");
-                       dev_err(hsotg->dev, "max_speed parameter must be 0 or 1\n");
-               }
-               valid = 0;
-       }
-
-       if (val == DWC2_SPEED_PARAM_HIGH &&
-           dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS)
-               valid = 0;
+       unsigned int op_mode = dwc2_op_mode(hsotg);
 
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for speed parameter. Check HW configuration.\n",
-                               val);
-               val = dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS ?
-                               DWC2_SPEED_PARAM_FULL : DWC2_SPEED_PARAM_HIGH;
-               dev_dbg(hsotg->dev, "Setting speed to %d\n", val);
-       }
-
-       hsotg->core_params->speed = val;
+       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
 }
 
-void dwc2_set_param_host_ls_low_power_phy_clk(struct dwc2_hsotg *hsotg, int val)
+/* Returns true if the controller is device-only. */
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
 {
-       int valid = 1;
-
-       if (DWC2_OUT_OF_BOUNDS(val, DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ,
-                              DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "Wrong value for host_ls_low_power_phy_clk parameter\n");
-                       dev_err(hsotg->dev,
-                               "host_ls_low_power_phy_clk must be 0 or 1\n");
-               }
-               valid = 0;
-       }
-
-       if (val == DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ &&
-           dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n",
-                               val);
-               val = dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS
-                       ? DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ
-                       : DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ;
-               dev_dbg(hsotg->dev, "Setting host_ls_low_power_phy_clk to %d\n",
-                       val);
-       }
+       unsigned int op_mode = dwc2_op_mode(hsotg);
 
-       hsotg->core_params->host_ls_low_power_phy_clk = val;
+       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
 }
 
-void dwc2_set_param_phy_ulpi_ddr(struct dwc2_hsotg *hsotg, int val)
+/**
+ * dwc2_hsotg_wait_bit_set - Waits for bit to be set.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+                           u32 timeout)
 {
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev, "Wrong value for phy_ulpi_ddr\n");
-                       dev_err(hsotg->dev, "phy_upli_ddr must be 0 or 1\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting phy_upli_ddr to %d\n", val);
-       }
-
-       hsotg->core_params->phy_ulpi_ddr = val;
-}
+       u32 i;
 
-void dwc2_set_param_phy_ulpi_ext_vbus(struct dwc2_hsotg *hsotg, int val)
-{
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "Wrong value for phy_ulpi_ext_vbus\n");
-                       dev_err(hsotg->dev,
-                               "phy_ulpi_ext_vbus must be 0 or 1\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting phy_ulpi_ext_vbus to %d\n", val);
+       for (i = 0; i < timeout; i++) {
+               if (dwc2_readl(hsotg, offset) & mask)
+                       return 0;
+               udelay(1);
        }
 
-       hsotg->core_params->phy_ulpi_ext_vbus = val;
+       return -ETIMEDOUT;
 }
 
-void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg, int val)
+/**
+ * dwc2_hsotg_wait_bit_clear - Waits for bit to be clear.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+                             u32 timeout)
 {
-       int valid = 0;
-
-       switch (hsotg->hw_params.utmi_phy_data_width) {
-       case GHWCFG4_UTMI_PHY_DATA_WIDTH_8:
-               valid = (val == 8);
-               break;
-       case GHWCFG4_UTMI_PHY_DATA_WIDTH_16:
-               valid = (val == 16);
-               break;
-       case GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16:
-               valid = (val == 8 || val == 16);
-               break;
-       }
+       u32 i;
 
-       if (!valid) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "%d invalid for phy_utmi_width. Check HW configuration.\n",
-                               val);
-               }
-               val = (hsotg->hw_params.utmi_phy_data_width ==
-                      GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16;
-               dev_dbg(hsotg->dev, "Setting phy_utmi_width to %d\n", val);
+       for (i = 0; i < timeout; i++) {
+               if (!(dwc2_readl(hsotg, offset) & mask))
+                       return 0;
+               udelay(1);
        }
 
-       hsotg->core_params->phy_utmi_width = val;
+       return -ETIMEDOUT;
 }
 
-void dwc2_set_param_ulpi_fs_ls(struct dwc2_hsotg *hsotg, int val)
+/*
+ * Initializes the FSLSPClkSel field of the HCFG register depending on the
+ * PHY type
+ */
+void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
 {
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev, "Wrong value for ulpi_fs_ls\n");
-                       dev_err(hsotg->dev, "ulpi_fs_ls must be 0 or 1\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting ulpi_fs_ls to %d\n", val);
-       }
-
-       hsotg->core_params->ulpi_fs_ls = val;
-}
+       u32 hcfg, val;
 
-void dwc2_set_param_ts_dline(struct dwc2_hsotg *hsotg, int val)
-{
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev, "Wrong value for ts_dline\n");
-                       dev_err(hsotg->dev, "ts_dline must be 0 or 1\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting ts_dline to %d\n", val);
+       if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
+            hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
+            hsotg->params.ulpi_fs_ls) ||
+           hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
+               /* Full speed PHY */
+               val = HCFG_FSLSPCLKSEL_48_MHZ;
+       } else {
+               /* High speed PHY running at full speed or high speed */
+               val = HCFG_FSLSPCLKSEL_30_60_MHZ;
        }
 
-       hsotg->core_params->ts_dline = val;
+       dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
+       hcfg = dwc2_readl(hsotg, HCFG);
+       hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
+       hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
+       dwc2_writel(hsotg, hcfg, HCFG);
 }
 
-void dwc2_set_param_i2c_enable(struct dwc2_hsotg *hsotg, int val)
+STATIC int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 {
-       int valid = 1;
-
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev, "Wrong value for i2c_enable\n");
-                       dev_err(hsotg->dev, "i2c_enable must be 0 or 1\n");
-               }
-
-               valid = 0;
-       }
-
-       if (val == 1 && !(hsotg->hw_params.i2c_enable))
-               valid = 0;
+       u32 usbcfg, ggpio, i2cctl;
+       int retval = 0;
 
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for i2c_enable. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.i2c_enable;
-               dev_dbg(hsotg->dev, "Setting i2c_enable to %d\n", val);
-       }
+       /*
+        * core_init() is now called on every switch so only call the
+        * following for the first time through
+        */
+       if (select_phy) {
+               dev_dbg(hsotg->dev, "FS PHY selected\n");
 
-       hsotg->core_params->i2c_enable = val;
-}
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               if (!(usbcfg & GUSBCFG_PHYSEL)) {
+                       usbcfg |= GUSBCFG_PHYSEL;
+                       dwc2_writel(hsotg, usbcfg, GUSBCFG);
 
-void dwc2_set_param_en_multiple_tx_fifo(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
+                       /* Reset after a PHY select */
+                       retval = dwc2_core_reset(hsotg, false);
 
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "Wrong value for en_multiple_tx_fifo,\n");
-                       dev_err(hsotg->dev,
-                               "en_multiple_tx_fifo must be 0 or 1\n");
+                       if (retval) {
+                               dev_err(hsotg->dev,
+                                       "%s: Reset failed, aborting", __func__);
+                               return retval;
+                       }
                }
-               valid = 0;
-       }
 
-       if (val == 1 && !hsotg->hw_params.en_multiple_tx_fifo)
-               valid = 0;
-
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.en_multiple_tx_fifo;
-               dev_dbg(hsotg->dev, "Setting en_multiple_tx_fifo to %d\n", val);
+               if (hsotg->params.activate_stm_fs_transceiver) {
+                       ggpio = dwc2_readl(hsotg, GGPIO);
+                       if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
+                               dev_dbg(hsotg->dev, "Activating transceiver\n");
+                               /*
+                                * STM32F4x9 uses the GGPIO register as general
+                                * core configuration register.
+                                */
+                               ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
+                               dwc2_writel(hsotg, ggpio, GGPIO);
+                       }
+               }
        }
 
-       hsotg->core_params->en_multiple_tx_fifo = val;
-}
-
-void dwc2_set_param_reload_ctl(struct dwc2_hsotg *hsotg, int val)
-{
-       int valid = 1;
+       /*
+        * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
+        * do this on HNP Dev/Host mode switches (done in dev_init and
+        * host_init).
+        */
+       if (dwc2_is_host_mode(hsotg))
+               dwc2_init_fs_ls_pclk_sel(hsotg);
 
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "'%d' invalid for parameter reload_ctl\n", val);
-                       dev_err(hsotg->dev, "reload_ctl must be 0 or 1\n");
-               }
-               valid = 0;
-       }
+       if (hsotg->params.i2c_enable) {
+               dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
 
-       if (val == 1 && hsotg->hw_params.snpsid < DWC2_CORE_REV_2_92a)
-               valid = 0;
+               /* Program GUSBCFG.OtgUtmiFsSel to I2C */
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
 
-       if (!valid) {
-               if (val >= 0)
-                       dev_err(hsotg->dev,
-                               "%d invalid for parameter reload_ctl. Check HW configuration.\n",
-                               val);
-               val = hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_92a;
-               dev_dbg(hsotg->dev, "Setting reload_ctl to %d\n", val);
+               /* Program GI2CCTL.I2CEn */
+               i2cctl = dwc2_readl(hsotg, GI2CCTL);
+               i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
+               i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
+               i2cctl &= ~GI2CCTL_I2CEN;
+               dwc2_writel(hsotg, i2cctl, GI2CCTL);
+               i2cctl |= GI2CCTL_I2CEN;
+               dwc2_writel(hsotg, i2cctl, GI2CCTL);
        }
 
-       hsotg->core_params->reload_ctl = val;
-}
-
-void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val)
-{
-       if (val != -1)
-               hsotg->core_params->ahbcfg = val;
-       else
-               hsotg->core_params->ahbcfg = GAHBCFG_HBSTLEN_INCR4 <<
-                                               GAHBCFG_HBSTLEN_SHIFT;
+       return retval;
 }
 
-void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val)
+STATIC int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 {
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "'%d' invalid for parameter otg_ver\n", val);
-                       dev_err(hsotg->dev,
-                               "otg_ver must be 0 (for OTG 1.3 support) or 1 (for OTG 2.0 support)\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting otg_ver to %d\n", val);
-       }
+       u32 usbcfg, usbcfg_old;
+       int retval = 0;
 
-       hsotg->core_params->otg_ver = val;
-}
+       if (!select_phy)
+               return 0;
 
-STATIC void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
-{
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "'%d' invalid for parameter uframe_sched\n",
-                               val);
-                       dev_err(hsotg->dev, "uframe_sched must be 0 or 1\n");
-               }
-               val = 1;
-               dev_dbg(hsotg->dev, "Setting uframe_sched to %d\n", val);
-       }
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
+       usbcfg_old = usbcfg;
 
-       hsotg->core_params->uframe_sched = val;
-}
+       /*
+        * HS PHY parameters. These parameters are preserved during soft reset
+        * so only program the first time. Do a soft reset immediately after
+        * setting phyif.
+        */
+       switch (hsotg->params.phy_type) {
+       case DWC2_PHY_TYPE_PARAM_ULPI:
+               /* ULPI interface */
+               dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
+               usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
+               usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
+               if (hsotg->params.phy_ulpi_ddr)
+                       usbcfg |= GUSBCFG_DDRSEL;
 
-STATIC void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg,
-               int val)
-{
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
-                       dev_err(hsotg->dev,
-                               "'%d' invalid for parameter external_id_pin_ctl\n",
-                               val);
-                       dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n");
-               }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val);
+               /* Set external VBUS indicator as needed. */
+               if (hsotg->params.oc_disable)
+                       usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND |
+                                  GUSBCFG_INDICATORPASSTHROUGH);
+               break;
+       case DWC2_PHY_TYPE_PARAM_UTMI:
+               /* UTMI+ interface */
+               dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
+               usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
+               if (hsotg->params.phy_utmi_width == 16)
+                       usbcfg |= GUSBCFG_PHYIF16;
+               break;
+       default:
+               dev_err(hsotg->dev, "FS PHY selected at HS!\n");
+               break;
        }
 
-       hsotg->core_params->external_id_pin_ctl = val;
-}
+       if (usbcfg != usbcfg_old) {
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
 
-STATIC void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg,
-               int val)
-{
-       if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
-               if (val >= 0) {
+               /* Reset after setting the PHY parameters */
+               retval = dwc2_core_reset(hsotg, false);
+               if (retval) {
                        dev_err(hsotg->dev,
-                               "'%d' invalid for parameter hibernation\n",
-                               val);
-                       dev_err(hsotg->dev, "hibernation must be 0 or 1\n");
+                               "%s: Reset failed, aborting", __func__);
+                       return retval;
                }
-               val = 0;
-               dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val);
        }
 
-       hsotg->core_params->hibernation = val;
-}
-
-/*
- * This function is called during module intialization to pass module parameters
- * for the DWC_otg core.
- */
-void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
-                        const struct dwc2_core_params *params)
-{
-       dev_dbg(hsotg->dev, "%s()\n", __func__);
-
-       dwc2_set_param_otg_cap(hsotg, params->otg_cap);
-       dwc2_set_param_dma_enable(hsotg, params->dma_enable);
-       dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
-       dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
-       dwc2_set_param_host_support_fs_ls_low_power(hsotg,
-                       params->host_support_fs_ls_low_power);
-       dwc2_set_param_enable_dynamic_fifo(hsotg,
-                       params->enable_dynamic_fifo);
-       dwc2_set_param_host_rx_fifo_size(hsotg,
-                       params->host_rx_fifo_size);
-       dwc2_set_param_host_nperio_tx_fifo_size(hsotg,
-                       params->host_nperio_tx_fifo_size);
-       dwc2_set_param_host_perio_tx_fifo_size(hsotg,
-                       params->host_perio_tx_fifo_size);
-       dwc2_set_param_max_transfer_size(hsotg,
-                       params->max_transfer_size);
-       dwc2_set_param_max_packet_count(hsotg,
-                       params->max_packet_count);
-       dwc2_set_param_host_channels(hsotg, params->host_channels);
-       dwc2_set_param_phy_type(hsotg, params->phy_type);
-       dwc2_set_param_speed(hsotg, params->speed);
-       dwc2_set_param_host_ls_low_power_phy_clk(hsotg,
-                       params->host_ls_low_power_phy_clk);
-       dwc2_set_param_phy_ulpi_ddr(hsotg, params->phy_ulpi_ddr);
-       dwc2_set_param_phy_ulpi_ext_vbus(hsotg,
-                       params->phy_ulpi_ext_vbus);
-       dwc2_set_param_phy_utmi_width(hsotg, params->phy_utmi_width);
-       dwc2_set_param_ulpi_fs_ls(hsotg, params->ulpi_fs_ls);
-       dwc2_set_param_ts_dline(hsotg, params->ts_dline);
-       dwc2_set_param_i2c_enable(hsotg, params->i2c_enable);
-       dwc2_set_param_en_multiple_tx_fifo(hsotg,
-                       params->en_multiple_tx_fifo);
-       dwc2_set_param_reload_ctl(hsotg, params->reload_ctl);
-       dwc2_set_param_ahbcfg(hsotg, params->ahbcfg);
-       dwc2_set_param_otg_ver(hsotg, params->otg_ver);
-       dwc2_set_param_uframe_sched(hsotg, params->uframe_sched);
-       dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl);
-       dwc2_set_param_hibernation(hsotg, params->hibernation);
-}
-
-/*
- * Forces either host or device mode if the controller is not
- * currently in that mode.
- *
- * Returns true if the mode was forced.
- */
-STATIC bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
-{
-       if (host && dwc2_is_host_mode(hsotg))
-               return false;
-       else if (!host && dwc2_is_device_mode(hsotg))
-               return false;
-
-       return dwc2_force_mode(hsotg, host);
+       return retval;
 }
 
-/*
- * Gets host hardware parameters. Forces host mode if not currently in
- * host mode. Should be called immediately after a core soft reset in
- * order to get the reset values.
- */
-STATIC void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
+static void dwc2_set_turnaround_time(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_hw_params *hw = &hsotg->hw_params;
-       u32 gnptxfsiz;
-       u32 hptxfsiz;
-       bool forced;
+       u32 usbcfg;
 
-       if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+       if (hsotg->params.phy_type != DWC2_PHY_TYPE_PARAM_UTMI)
                return;
 
-       forced = dwc2_force_mode_if_needed(hsotg, true);
-
-       gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
-       hptxfsiz = DWC2_READ_4(hsotg, HPTXFSIZ);
-       dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
-       dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
 
-       if (forced)
-               dwc2_clear_force_mode(hsotg);
+       usbcfg &= ~GUSBCFG_USBTRDTIM_MASK;
+       if (hsotg->params.phy_utmi_width == 16)
+               usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT;
+       else
+               usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT;
 
-       hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
-                                      FIFOSIZE_DEPTH_SHIFT;
-       hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
-                                     FIFOSIZE_DEPTH_SHIFT;
+       dwc2_writel(hsotg, usbcfg, GUSBCFG);
 }
 
-/*
- * Gets device hardware parameters. Forces device mode if not
- * currently in device mode. Should be called immediately after a core
- * soft reset in order to get the reset values.
- */
-STATIC void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
+STATIC int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 {
-       struct dwc2_hw_params *hw = &hsotg->hw_params;
-       bool forced;
-       u32 gnptxfsiz;
+       u32 usbcfg;
+       u32 otgctl;
+       int retval = 0;
 
-       if (hsotg->dr_mode == USB_DR_MODE_HOST)
-               return;
+       if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
+            hsotg->params.speed == DWC2_SPEED_PARAM_LOW) &&
+           hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
+               /* If FS/LS mode with FS/LS PHY */
+               retval = dwc2_fs_phy_init(hsotg, select_phy);
+               if (retval)
+                       return retval;
+       } else {
+               /* High speed PHY */
+               retval = dwc2_hs_phy_init(hsotg, select_phy);
+               if (retval)
+                       return retval;
 
-       forced = dwc2_force_mode_if_needed(hsotg, false);
+               if (dwc2_is_device_mode(hsotg))
+                       dwc2_set_turnaround_time(hsotg);
+       }
 
-       gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
-       dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+       if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
+           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
+           hsotg->params.ulpi_fs_ls) {
+               dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               usbcfg |= GUSBCFG_ULPI_FS_LS;
+               usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
+       } else {
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               usbcfg &= ~GUSBCFG_ULPI_FS_LS;
+               usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
+       }
 
-       if (forced)
-               dwc2_clear_force_mode(hsotg);
+       if (!hsotg->params.activate_ingenic_overcurrent_detection) {
+               if (dwc2_is_host_mode(hsotg)) {
+                       otgctl = dwc2_readl(hsotg, GOTGCTL);
+                       otgctl |= GOTGCTL_VBVALOEN | GOTGCTL_VBVALOVAL;
+                       dwc2_writel(hsotg, otgctl, GOTGCTL);
+               }
+       }
 
-       hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
-                                      FIFOSIZE_DEPTH_SHIFT;
+       return retval;
 }
 
+/*** gadget.c *****************************************************************/
+#if 0
 /**
- * During device initialization, read various hardware configuration
- * registers and interpret the contents.
+ * dwc2_backup_device_registers() - Backup controller device registers.
+ * When suspending usb bus, registers needs to be backuped
+ * if controller power is disabled once suspended.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
  */
-int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
+int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_hw_params *hw = &hsotg->hw_params;
-       unsigned width;
-       u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
-       u32 grxfsiz;
-
-       /*
-        * Attempt to ensure this device is really a DWC_otg Controller.
-        * Read and verify the GSNPSID register contents. The value should be
-        * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3",
-        * as in "OTG version 2.xx" or "OTG version 3.xx".
-        */
-       hw->snpsid = DWC2_READ_4(hsotg, GSNPSID);
-       if ((hw->snpsid & 0xfffff000) != 0x4f542000 &&
-           (hw->snpsid & 0xfffff000) != 0x4f543000) {
-               dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
-                       hw->snpsid);
-               return -ENODEV;
-       }
-
-       dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
-               hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
-               hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
+       struct dwc2_dregs_backup *dr;
+       int i;
 
-       hwcfg1 = DWC2_READ_4(hsotg, GHWCFG1);
-       hwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
-       hwcfg3 = DWC2_READ_4(hsotg, GHWCFG3);
-       hwcfg4 = DWC2_READ_4(hsotg, GHWCFG4);
-       grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
+       dev_dbg(hsotg->dev, "%s\n", __func__);
 
-       dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1);
-       dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2);
-       dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3);
-       dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
-       dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
+       /* Backup dev regs */
+       dr = &hsotg->dr_backup;
 
-       /*
-        * Host specific hardware parameters. Reading these parameters
-        * requires the controller to be in host mode. The mode will
-        * be forced, if necessary, to read these values.
-        */
-       dwc2_get_host_hwparams(hsotg);
-       dwc2_get_dev_hwparams(hsotg);
-
-       /* hwcfg1 */
-       hw->dev_ep_dirs = hwcfg1;
-
-       /* hwcfg2 */
-       hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
-                     GHWCFG2_OP_MODE_SHIFT;
-       hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >>
-                  GHWCFG2_ARCHITECTURE_SHIFT;
-       hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO);
-       hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >>
-                               GHWCFG2_NUM_HOST_CHAN_SHIFT);
-       hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >>
-                         GHWCFG2_HS_PHY_TYPE_SHIFT;
-       hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >>
-                         GHWCFG2_FS_PHY_TYPE_SHIFT;
-       hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >>
-                        GHWCFG2_NUM_DEV_EP_SHIFT;
-       hw->nperio_tx_q_depth =
-               (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >>
-               GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1;
-       hw->host_perio_tx_q_depth =
-               (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >>
-               GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1;
-       hw->dev_token_q_depth =
-               (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >>
-               GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT;
-
-       /* hwcfg3 */
-       width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
-               GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
-       hw->max_transfer_size = (1 << (width + 11)) - 1;
-       /*
-        * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
-        * coherent buffers with this size, and if it's too large we can
-        * exhaust the coherent DMA pool.
-        */
-       if (hw->max_transfer_size > 65535)
-               hw->max_transfer_size = 65535;
-       width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
-               GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
-       hw->max_packet_count = (1 << (width + 4)) - 1;
-       hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
-       hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
-                             GHWCFG3_DFIFO_DEPTH_SHIFT;
-
-       /* hwcfg4 */
-       hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
-       hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >>
-                                 GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT;
-       hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
-       hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
-       hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
-                                 GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
-
-       /* fifo sizes */
-       hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
-                               GRXFSIZ_DEPTH_SHIFT;
-
-       dev_dbg(hsotg->dev, "Detected values from hardware:\n");
-       dev_dbg(hsotg->dev, "  op_mode=%d\n",
-               hw->op_mode);
-       dev_dbg(hsotg->dev, "  arch=%d\n",
-               hw->arch);
-       dev_dbg(hsotg->dev, "  dma_desc_enable=%d\n",
-               hw->dma_desc_enable);
-       dev_dbg(hsotg->dev, "  power_optimized=%d\n",
-               hw->power_optimized);
-       dev_dbg(hsotg->dev, "  i2c_enable=%d\n",
-               hw->i2c_enable);
-       dev_dbg(hsotg->dev, "  hs_phy_type=%d\n",
-               hw->hs_phy_type);
-       dev_dbg(hsotg->dev, "  fs_phy_type=%d\n",
-               hw->fs_phy_type);
-       dev_dbg(hsotg->dev, "  utmi_phy_data_width=%d\n",
-               hw->utmi_phy_data_width);
-       dev_dbg(hsotg->dev, "  num_dev_ep=%d\n",
-               hw->num_dev_ep);
-       dev_dbg(hsotg->dev, "  num_dev_perio_in_ep=%d\n",
-               hw->num_dev_perio_in_ep);
-       dev_dbg(hsotg->dev, "  host_channels=%d\n",
-               hw->host_channels);
-       dev_dbg(hsotg->dev, "  max_transfer_size=%d\n",
-               hw->max_transfer_size);
-       dev_dbg(hsotg->dev, "  max_packet_count=%d\n",
-               hw->max_packet_count);
-       dev_dbg(hsotg->dev, "  nperio_tx_q_depth=0x%0x\n",
-               hw->nperio_tx_q_depth);
-       dev_dbg(hsotg->dev, "  host_perio_tx_q_depth=0x%0x\n",
-               hw->host_perio_tx_q_depth);
-       dev_dbg(hsotg->dev, "  dev_token_q_depth=0x%0x\n",
-               hw->dev_token_q_depth);
-       dev_dbg(hsotg->dev, "  enable_dynamic_fifo=%d\n",
-               hw->enable_dynamic_fifo);
-       dev_dbg(hsotg->dev, "  en_multiple_tx_fifo=%d\n",
-               hw->en_multiple_tx_fifo);
-       dev_dbg(hsotg->dev, "  total_fifo_size=%d\n",
-               hw->total_fifo_size);
-       dev_dbg(hsotg->dev, "  host_rx_fifo_size=%d\n",
-               hw->host_rx_fifo_size);
-       dev_dbg(hsotg->dev, "  host_nperio_tx_fifo_size=%d\n",
-               hw->host_nperio_tx_fifo_size);
-       dev_dbg(hsotg->dev, "  host_perio_tx_fifo_size=%d\n",
-               hw->host_perio_tx_fifo_size);
-       dev_dbg(hsotg->dev, "\n");
+       dr->dcfg = dwc2_readl(hsotg, DCFG);
+       dr->dctl = dwc2_readl(hsotg, DCTL);
+       dr->daintmsk = dwc2_readl(hsotg, DAINTMSK);
+       dr->diepmsk = dwc2_readl(hsotg, DIEPMSK);
+       dr->doepmsk = dwc2_readl(hsotg, DOEPMSK);
 
-       return 0;
-}
+       for (i = 0; i < hsotg->num_of_eps; i++) {
+               /* Backup IN EPs */
+               dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i));
 
-/*
- * Sets all parameters to the given value.
- *
- * Assumes that the dwc2_core_params struct contains only integers.
- */
-void dwc2_set_all_params(struct dwc2_core_params *params, int value)
-{
-       int *p = (int *)params;
-       size_t size = sizeof(*params) / sizeof(*p);
-       int i;
+               /* Ensure DATA PID is correctly configured */
+               if (dr->diepctl[i] & DXEPCTL_DPID)
+                       dr->diepctl[i] |= DXEPCTL_SETD1PID;
+               else
+                       dr->diepctl[i] |= DXEPCTL_SETD0PID;
 
-       for (i = 0; i < size; i++)
-               p[i] = value;
-}
+               dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i));
+               dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i));
 
+               /* Backup OUT EPs */
+               dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i));
 
-u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg)
-{
-       return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103;
-}
+               /* Ensure DATA PID is correctly configured */
+               if (dr->doepctl[i] & DXEPCTL_DPID)
+                       dr->doepctl[i] |= DXEPCTL_SETD1PID;
+               else
+                       dr->doepctl[i] |= DXEPCTL_SETD0PID;
 
-bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
-{
-       if (DWC2_READ_4(hsotg, GSNPSID) == 0xffffffff)
-               return false;
-       else
-               return true;
+               dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i));
+               dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i));
+       }
+       dr->valid = true;
+       return 0;
 }
 
 /**
- * dwc2_enable_global_interrupts() - Enables the controller's Global
- * Interrupt in the AHB Config register
+ * dwc2_restore_device_registers() - Restore controller device registers.
+ * When resuming usb bus, device registers needs to be restored
+ * if controller power were disabled.
  *
- * @hsotg: Programming view of DWC_otg controller
- */
-void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg)
-{
-       u32 ahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
-
-       ahbcfg |= GAHBCFG_GLBL_INTR_EN;
-       DWC2_WRITE_4(hsotg, GAHBCFG, ahbcfg);
-}
-
-/**
- * dwc2_disable_global_interrupts() - Disables the controller's Global
- * Interrupt in the AHB Config register
+ * @hsotg: Programming view of the DWC_otg controller
+ * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
  *
- * @hsotg: Programming view of DWC_otg controller
+ * Return: 0 if successful, negative error code otherwise
  */
-void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
-{
-       u32 ahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
-
-       ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
-       DWC2_WRITE_4(hsotg, GAHBCFG, ahbcfg);
-}
-
-/* Returns the controller's GHWCFG2.OTG_MODE. */
-unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
+STATIC int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
 {
-       u32 ghwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
-
-       return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
-               GHWCFG2_OP_MODE_SHIFT;
-}
+       struct dwc2_dregs_backup *dr;
+       u32 dctl;
+       int i;
 
-/* Returns true if the controller is capable of DRD. */
-bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
-{
-       unsigned op_mode = dwc2_op_mode(hsotg);
+       dev_dbg(hsotg->dev, "%s\n", __func__);
 
-       return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
-               (op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
-               (op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
-}
+       /* Restore dev regs */
+       dr = &hsotg->dr_backup;
+       if (!dr->valid) {
+               dev_err(hsotg->dev, "%s: no device registers to restore\n",
+                       __func__);
+               return -EINVAL;
+       }
+       dr->valid = false;
 
-/* Returns true if the controller is host-only. */
-bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
-{
-       unsigned op_mode = dwc2_op_mode(hsotg);
+       if (!remote_wakeup)
+               dwc2_writel(hsotg, dr->dctl, DCTL);
 
-       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
-               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
-}
+       dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
+       dwc2_writel(hsotg, dr->diepmsk, DIEPMSK);
+       dwc2_writel(hsotg, dr->doepmsk, DOEPMSK);
 
-/* Returns true if the controller is device-only. */
-bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
-{
-       unsigned op_mode = dwc2_op_mode(hsotg);
+       for (i = 0; i < hsotg->num_of_eps; i++) {
+               /* Restore IN EPs */
+               dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i));
+               dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i));
+               dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
+               /** WA for enabled EPx's IN in DDMA mode. On entering to
+                * hibernation wrong value read and saved from DIEPDMAx,
+                * as result BNA interrupt asserted on hibernation exit
+                * by restoring from saved area.
+                */
+               if (hsotg->params.g_dma_desc &&
+                   (dr->diepctl[i] & DXEPCTL_EPENA))
+                       dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma;
+               dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i));
+               dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i));
+               /* Restore OUT EPs */
+               dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
+               /* WA for enabled EPx's OUT in DDMA mode. On entering to
+                * hibernation wrong value read and saved from DOEPDMAx,
+                * as result BNA interrupt asserted on hibernation exit
+                * by restoring from saved area.
+                */
+               if (hsotg->params.g_dma_desc &&
+                   (dr->doepctl[i] & DXEPCTL_EPENA))
+                       dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma;
+               dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i));
+               dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i));
+       }
 
-       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
-               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
+       return 0;
 }
+#endif
index 2a4979d..b64491a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_core.h,v 1.11 2021/07/27 13:36:59 mglocker Exp $ */
+/*     $OpenBSD: dwc2_core.h,v 1.12 2022/09/04 08:42:39 mglocker Exp $ */
 /*     $NetBSD: dwc2_core.h,v 1.5 2014/04/03 06:34:58 skrll Exp $      */
 
 /*
 
 #include <dev/usb/dwc2/list.h>
 
+/*
+ * Suggested defines for tracers:
+ * - no_printk:    Disable tracing
+ * - pr_info:      Print this info to the console
+ * - trace_printk: Print this info to trace buffer (good for verbose logging)
+ */
+
+#ifdef DWC2_DEBUG
+/* Detailed scheduler tracing, but won't overwhelm console */
+#define dwc2_sch_dbg(hsotg,fmt,...) do {                               \
+       if (dwc2debug >= 1) {                                           \
+           printf("%s: " fmt, device_xname(hsotg->dev),                \
+                  ## __VA_ARGS__);                                     \
+       }                                                               \
+} while (0)
+
+/* Verbose scheduler tracing */
+#define dwc2_sch_vdbg(hsotg,fmt,...) do {                              \
+       if (dwc2debug >= 2) {                                           \
+           printf("%s: " fmt, device_xname(hsotg->dev),                \
+                  ## __VA_ARGS__);                                     \
+       }                                                               \
+} while (0)
+#else
+#define dwc2_sch_dbg(...) do { } while (0)
+#define dwc2_sch_vdbg(...) do { } while (0)
+#endif
+
 /* Maximum number of Endpoints/HostChannels */
 #define MAX_EPS_CHANNELS       16
 
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-
+#if 0
 /* dwc2-hsotg declarations */
 STATIC const char * const dwc2_hsotg_supply_names[] = {
        "vusb_d",               /* digital USB supply, 1.2V */
        "vusb_a",               /* analog USB supply, 1.1V */
 };
 
+#define DWC2_NUM_SUPPLIES ARRAY_SIZE(dwc2_hsotg_supply_names)
+#endif
+
 /*
  * EP0_MPS_LIMIT
  *
@@ -98,24 +128,33 @@ struct dwc2_hsotg_req;
  *       and has yet to be completed (maybe due to data move, or simply
  *       awaiting an ack from the core all the data has been completed).
  * @debugfs: File entry for debugfs file for this endpoint.
- * @lock: State lock to protect contents of endpoint.
  * @dir_in: Set to true if this endpoint is of the IN direction, which
  *          means that it is sending data to the Host.
+ * @map_dir: Set to the value of dir_in when the DMA buffer is mapped.
  * @index: The index for the endpoint registers.
  * @mc: Multi Count - number of transactions per microframe
- * @interval - Interval for periodic endpoints
+ * @interval: Interval for periodic endpoints, in frames or microframes.
  * @name: The name array passed to the USB core.
  * @halted: Set if the endpoint has been halted.
  * @periodic: Set if this is a periodic ep, such as Interrupt
  * @isochronous: Set if this is a isochronous ep
  * @send_zlp: Set if we need to send a zero-length packet.
+ * @wedged: Set if ep is wedged.
+ * @desc_list_dma: The DMA address of descriptor chain currently in use.
+ * @desc_list: Pointer to descriptor DMA chain head currently in use.
+ * @desc_count: Count of entries within the DMA descriptor chain of EP.
+ * @next_desc: index of next free descriptor in the ISOC chain under SW control.
+ * @compl_desc: index of next descriptor to be completed by xFerComplete
  * @total_data: The total number of data bytes done.
  * @fifo_size: The size of the FIFO (for periodic IN endpoints)
+ * @fifo_index: For Dedicated FIFO operation, only FIFO0 can be used for EP0.
  * @fifo_load: The amount of data loaded into the FIFO (periodic IN)
  * @last_load: The offset of data for the last start of request.
  * @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
+ * @target_frame: Targeted frame num to setup next ISOC transfer
+ * @frame_overrun: Indicates SOF number overrun in DSTS
  *
- * This is the driver's state for each registered enpoint, allowing it
+ * This is the driver's state for each registered endpoint, allowing it
  * to keep track of transactions that need doing. Each endpoint has a
  * lock to protect the state, to try and avoid using an overall lock
  * for the host controller as much as possible.
@@ -128,7 +167,7 @@ struct dwc2_hsotg_req;
  * buffer than a fifo)
  */
 struct dwc2_hsotg_ep {
-       struct usb_ep           ep;
+//     struct usb_ep           ep;
        struct list_head        queue;
        struct dwc2_hsotg       *parent;
        struct dwc2_hsotg_req    *req;
@@ -142,15 +181,26 @@ struct dwc2_hsotg_ep {
        unsigned short          fifo_index;
 
        unsigned char           dir_in;
+       unsigned char           map_dir;
        unsigned char           index;
        unsigned char           mc;
-       unsigned char           interval;
+       u16                     interval;
 
        unsigned int            halted:1;
        unsigned int            periodic:1;
        unsigned int            isochronous:1;
        unsigned int            send_zlp:1;
-       unsigned int            has_correct_parity:1;
+       unsigned int            wedged:1;
+       unsigned int            target_frame;
+#define TARGET_FRAME_INITIAL   0xFFFFFFFF
+       bool                    frame_overrun;
+
+//     dma_addr_t              desc_list_dma;
+       struct dwc2_dma_desc    *desc_list;
+       u8                      desc_count;
+
+       unsigned int            next_desc;
+       unsigned int            compl_desc;
 
        char                    name[10];
 };
@@ -162,11 +212,13 @@ struct dwc2_hsotg_ep {
  * @saved_req_buf: variable to save req.buf when bounce buffers are used.
  */
 struct dwc2_hsotg_req {
-       struct usb_request      req;
+//     struct usb_request      req;
        struct list_head        queue;
        void *saved_req_buf;
 };
 
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+       IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 #define call_gadget(_hs, _entry) \
 do { \
        if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
@@ -191,13 +243,6 @@ enum dwc2_lx_state {
        DWC2_L3,        /* Off state */
 };
 
-/*
- * Gadget periodic tx fifo sizes as used by legacy driver
- * EP0 is not included
- */
-#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
-                                          768, 0, 0, 0, 0, 0, 0, 0}
-
 /* Gadget ep0 states */
 enum dwc2_ep0_state {
        DWC2_EP0_SETUP,
@@ -207,18 +252,33 @@ enum dwc2_ep0_state {
        DWC2_EP0_STATUS_OUT,
 };
 
+/** XXX: From Linux USB stack.
+ * struct usb_otg_caps - describes the otg capabilities of the device
+ * @otg_rev: The OTG revision number the device is compliant with, it's
+ *              in binary-coded decimal (i.e. 2.0 is 0200H).
+ * @hnp_support: Indicates if the device supports HNP.
+ * @srp_support: Indicates if the device supports SRP.
+ * @adp_support: Indicates if the device supports ADP.
+ */
+struct usb_otg_caps {
+       u16 otg_rev;
+       bool hnp_support;
+       bool srp_support;
+       bool adp_support;
+};
+
 /**
  * struct dwc2_core_params - Parameters for configuring the core
  *
- * @otg_cap:            Specifies the OTG capabilities.
- *                       0 - HNP and SRP capable
- *                       1 - SRP Only capable
- *                       2 - No HNP/SRP capable (always available)
- *                      Defaults to best available option (0, 1, then 2)
- * @otg_ver:            OTG version supported
- *                       0 - 1.3 (default)
- *                       1 - 2.0
- * @dma_enable:         Specifies whether to use slave or DMA mode for accessing
+ * @otg_caps:           Specifies the OTG capabilities. OTG caps from the platform parameters,
+ *                      used to setup the:
+ *                       - HNP and SRP capable
+ *                       - SRP Only capable
+ *                       - No HNP/SRP capable (always available)
+ *                       Defaults to best available option
+ *                       - OTG revision number the device is compliant with, in binary-coded
+ *                         decimal (i.e. 2.0 is 0200H). (see struct usb_otg_caps)
+ * @host_dma:           Specifies whether to use slave or DMA mode for accessing
  *                      the data FIFOs. The driver will automatically detect the
  *                      value for this parameter if none is specified.
  *                       0 - Slave (always available)
@@ -246,7 +306,8 @@ enum dwc2_ep0_state {
  * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters
  *                       1 - Allow dynamic FIFO sizing (default, if available)
  * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs
- *                      are enabled
+ *                      are enabled for non-periodic IN endpoints in device
+ *                      mode.
  * @host_rx_fifo_size:  Number of 4-byte words in the Rx FIFO in host mode when
  *                      dynamic FIFO sizing is enabled
  *                       16 to 32768
@@ -305,6 +366,12 @@ enum dwc2_ep0_state {
  *                      is FS.
  *                       0 - No (default)
  *                       1 - Yes
+ * @ipg_isoc_en:        Indicates the IPG supports is enabled or disabled.
+ *                       0 - Disable (default)
+ *                       1 - Enable
+ * @acg_enable:                For enabling Active Clock Gating in the controller
+ *                       0 - No
+ *                       1 - Yes
  * @ulpi_fs_ls:         Make ULPI phy operate in FS/LS mode only
  *                       0 - No (default)
  *                       1 - Yes
@@ -321,6 +388,9 @@ enum dwc2_ep0_state {
  *                           (default when phy_type is UTMI+ or ULPI)
  *                       1 - 6 MHz
  *                           (default when phy_type is Full Speed)
+ * @oc_disable:                Flag to disable overcurrent condition.
+ *                     0 - Allow overcurrent condition to get detected
+ *                     1 - Disable overcurrent condtion to get detected
  * @ts_dline:           Enable Term Select Dline pulsing
  *                       0 - No (default)
  *                       1 - Yes
@@ -330,7 +400,7 @@ enum dwc2_ep0_state {
  * @ahbcfg:             This field allows the default value of the GAHBCFG
  *                      register to be overridden
  *                       -1         - GAHBCFG value will be set to 0x06
- *                                    (INCR4, default)
+ *                                    (INCR, default)
  *                       all others - GAHBCFG value will be overridden with
  *                                    this value
  *                      Not all bits can be controlled like this, the
@@ -343,12 +413,77 @@ enum dwc2_ep0_state {
  *                      case.
  *                      0 - No (default)
  *                      1 - Yes
- * @hibernation:       Specifies whether the controller support hibernation.
- *                     If hibernation is enabled, the controller will enter
- *                     hibernation in both peripheral and host mode when
+ * @power_down:         Specifies whether the controller support power_down.
+ *                     If power_down is enabled, the controller will enter
+ *                     power_down in both peripheral and host mode when
  *                     needed.
  *                     0 - No (default)
+ *                     1 - Partial power down
+ *                     2 - Hibernation
+ * @no_clock_gating:   Specifies whether to avoid clock gating feature.
+ *                     0 - No (use clock gating)
+ *                     1 - Yes (avoid it)
+ * @lpm:               Enable LPM support.
+ *                     0 - No
+ *                     1 - Yes
+ * @lpm_clock_gating:          Enable core PHY clock gating.
+ *                     0 - No
+ *                     1 - Yes
+ * @besl:              Enable LPM Errata support.
+ *                     0 - No
+ *                     1 - Yes
+ * @hird_threshold_en: HIRD or HIRD Threshold enable.
+ *                     0 - No
  *                     1 - Yes
+ * @hird_threshold:    Value of BESL or HIRD Threshold.
+ * @ref_clk_per:        Indicates in terms of pico seconds the period
+ *                      of ref_clk.
+ *                     62500 - 16MHz
+ *                      58823 - 17MHz
+ *                      52083 - 19.2MHz
+ *                     50000 - 20MHz
+ *                     41666 - 24MHz
+ *                     33333 - 30MHz (default)
+ *                     25000 - 40MHz
+ * @sof_cnt_wkup_alert: Indicates in term of number of SOF's after which
+ *                      the controller should generate an interrupt if the
+ *                      device had been in L1 state until that period.
+ *                      This is used by SW to initiate Remote WakeUp in the
+ *                      controller so as to sync to the uF number from the host.
+ * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
+ *                     register.
+ *                     0 - Deactivate the transceiver (default)
+ *                     1 - Activate the transceiver
+ * @activate_stm_id_vb_detection: Activate external ID pin and Vbus level
+ *                     detection using GGPIO register.
+ *                     0 - Deactivate the external level detection (default)
+ *                     1 - Activate the external level detection
+ * @activate_ingenic_overcurrent_detection: Activate Ingenic overcurrent
+ *                     detection.
+ *                     0 - Deactivate the overcurrent detection
+ *                     1 - Activate the overcurrent detection (default)
+ * @g_dma:              Enables gadget dma usage (default: autodetect).
+ * @g_dma_desc:         Enables gadget descriptor DMA (default: autodetect).
+ * @g_rx_fifo_size:    The periodic rx fifo size for the device, in
+ *                     DWORDS from 16-32768 (default: 2048 if
+ *                     possible, otherwise autodetect).
+ * @g_np_tx_fifo_size: The non-periodic tx fifo size for the device in
+ *                     DWORDS from 16-32768 (default: 1024 if
+ *                     possible, otherwise autodetect).
+ * @g_tx_fifo_size:    An array of TX fifo sizes in dedicated fifo
+ *                     mode. Each value corresponds to one EP
+ *                     starting from EP1 (max 15 values). Sizes are
+ *                     in DWORDS with possible values from
+ *                     16-32768 (default: 256, 256, 256, 256, 768,
+ *                     768, 768, 768, 0, 0, 0, 0, 0, 0, 0).
+ * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL
+ *                      while full&low speed device connect. And change speed
+ *                      back to DWC2_SPEED_PARAM_HIGH while device is gone.
+ *                     0 - No (default)
+ *                     1 - Yes
+ * @service_interval:   Enable service interval based scheduling.
+ *                      0 - No
+ *                      1 - Yes
  *
  * The following parameters may be specified when starting the module. These
  * parameters define how the DWC_otg controller should be configured. A
@@ -357,38 +492,75 @@ enum dwc2_ep0_state {
  * default described above.
  */
 struct dwc2_core_params {
-       /*
-        * Don't add any non-int members here, this will break
-        * dwc2_set_all_params!
-        */
-       int otg_cap;
-       int otg_ver;
-       int dma_enable;
-       int dma_desc_enable;
-       int dma_desc_fs_enable;
-       int speed;
-       int enable_dynamic_fifo;
-       int en_multiple_tx_fifo;
-       int host_rx_fifo_size;
-       int host_nperio_tx_fifo_size;
-       int host_perio_tx_fifo_size;
-       int max_transfer_size;
-       int max_packet_count;
-       int host_channels;
-       int phy_type;
-       int phy_utmi_width;
-       int phy_ulpi_ddr;
-       int phy_ulpi_ext_vbus;
-       int i2c_enable;
-       int ulpi_fs_ls;
-       int host_support_fs_ls_low_power;
-       int host_ls_low_power_phy_clk;
-       int ts_dline;
-       int reload_ctl;
-       int ahbcfg;
-       int uframe_sched;
-       int external_id_pin_ctl;
-       int hibernation;
+       struct usb_otg_caps otg_caps;
+       u8 phy_type;
+#define DWC2_PHY_TYPE_PARAM_FS         0
+#define DWC2_PHY_TYPE_PARAM_UTMI       1
+#define DWC2_PHY_TYPE_PARAM_ULPI       2
+
+       u8 speed;
+#define DWC2_SPEED_PARAM_HIGH  0
+#define DWC2_SPEED_PARAM_FULL  1
+#define DWC2_SPEED_PARAM_LOW   2
+
+       u8 phy_utmi_width;
+       bool phy_ulpi_ddr;
+       bool phy_ulpi_ext_vbus;
+       bool enable_dynamic_fifo;
+       bool en_multiple_tx_fifo;
+       bool i2c_enable;
+       bool acg_enable;
+       bool ulpi_fs_ls;
+       bool ts_dline;
+       bool reload_ctl;
+       bool uframe_sched;
+       bool external_id_pin_ctl;
+
+       int power_down;
+#define DWC2_POWER_DOWN_PARAM_NONE             0
+#define DWC2_POWER_DOWN_PARAM_PARTIAL          1
+#define DWC2_POWER_DOWN_PARAM_HIBERNATION      2
+       bool no_clock_gating;
+
+       bool lpm;
+       bool lpm_clock_gating;
+       bool besl;
+       bool hird_threshold_en;
+       bool service_interval;
+       u8 hird_threshold;
+       bool activate_stm_fs_transceiver;
+       bool activate_stm_id_vb_detection;
+       bool activate_ingenic_overcurrent_detection;
+       bool ipg_isoc_en;
+       u16 max_packet_count;
+       u32 max_transfer_size;
+       u32 ahbcfg;
+
+       /* GREFCLK parameters */
+       u32 ref_clk_per;
+       u16 sof_cnt_wkup_alert;
+
+       /* Host parameters */
+       bool host_dma;
+       bool dma_desc_enable;
+       bool dma_desc_fs_enable;
+       bool host_support_fs_ls_low_power;
+       bool host_ls_low_power_phy_clk;
+       bool oc_disable;
+
+       u8 host_channels;
+       u16 host_rx_fifo_size;
+       u16 host_nperio_tx_fifo_size;
+       u16 host_perio_tx_fifo_size;
+
+       /* Gadget parameters */
+       bool g_dma;
+       bool g_dma_desc;
+       u32 g_rx_fifo_size;
+       u32 g_np_tx_fifo_size;
+       u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
+
+       bool change_speed_quirk;
 };
 
 /**
@@ -401,7 +573,7 @@ struct dwc2_core_params {
  *
  * The values that are not in dwc2_core_params are documented below.
  *
- * @op_mode             Mode of Operation
+ * @op_mode:             Mode of Operation
  *                       0 - HNP- and SRP-Capable OTG (Host & Device)
  *                       1 - SRP-Capable OTG (Host & Device)
  *                       2 - Non-HNP and Non-SRP Capable OTG (Host & Device)
@@ -409,49 +581,114 @@ struct dwc2_core_params {
  *                       4 - Non-OTG Device
  *                       5 - SRP-Capable Host
  *                       6 - Non-OTG Host
- * @arch                Architecture
+ * @arch:                Architecture
  *                       0 - Slave only
  *                       1 - External DMA
  *                       2 - Internal DMA
- * @power_optimized     Are power optimizations enabled?
- * @num_dev_ep          Number of device endpoints available
- * @num_dev_perio_in_ep Number of device periodic IN endpoints
- *                      available
- * @dev_token_q_depth   Device Mode IN Token Sequence Learning Queue
+ * @ipg_isoc_en:        This feature indicates that the controller supports
+ *                      the worst-case scenario of Rx followed by Rx
+ *                      Interpacket Gap (IPG) (32 bitTimes) as per the utmi
+ *                      specification for any token following ISOC OUT token.
+ *                       0 - Don't support
+ *                       1 - Support
+ * @power_optimized:    Are power optimizations enabled?
+ * @num_dev_ep:         Number of device endpoints available
+ * @num_dev_in_eps:     Number of device IN endpoints available
+ * @num_dev_perio_in_ep: Number of device periodic IN endpoints
+ *                       available
+ * @dev_token_q_depth:  Device Mode IN Token Sequence Learning Queue
  *                      Depth
  *                       0 to 30
- * @host_perio_tx_q_depth
+ * @host_perio_tx_q_depth:
  *                      Host Mode Periodic Request Queue Depth
  *                       2, 4 or 8
- * @nperio_tx_q_depth
+ * @nperio_tx_q_depth:
  *                      Non-Periodic Request Queue Depth
  *                       2, 4 or 8
- * @hs_phy_type         High-speed PHY interface type
+ * @hs_phy_type:         High-speed PHY interface type
  *                       0 - High-speed interface not supported
  *                       1 - UTMI+
  *                       2 - ULPI
  *                       3 - UTMI+ and ULPI
- * @fs_phy_type         Full-speed PHY interface type
+ * @fs_phy_type:         Full-speed PHY interface type
  *                       0 - Full speed interface not supported
  *                       1 - Dedicated full speed interface
  *                       2 - FS pins shared with UTMI+ pins
  *                       3 - FS pins shared with ULPI pins
  * @total_fifo_size:    Total internal RAM for FIFOs (bytes)
- * @utmi_phy_data_width UTMI+ PHY data width
+ * @hibernation:       Is hibernation enabled?
+ * @utmi_phy_data_width: UTMI+ PHY data width
  *                       0 - 8 bits
  *                       1 - 16 bits
  *                       2 - 8 or 16 bits
  * @snpsid:             Value from SNPSID register
  * @dev_ep_dirs:        Direction of device endpoints (GHWCFG1)
+ * @g_tx_fifo_size:    Power-on values of TxFIFO sizes
+ * @dma_desc_enable:    When DMA mode is enabled, specifies whether to use
+ *                      address DMA mode or descriptor DMA mode for accessing
+ *                      the data FIFOs. The driver will automatically detect the
+ *                      value for this if none is specified.
+ *                       0 - Address DMA
+ *                       1 - Descriptor DMA (default, if available)
+ * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters
+ *                       1 - Allow dynamic FIFO sizing (default, if available)
+ * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs
+ *                      are enabled for non-periodic IN endpoints in device
+ *                      mode.
+ * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO
+ *                      in host mode when dynamic FIFO sizing is enabled
+ *                       16 to 32768
+ *                      Actual maximum value is autodetected and also
+ *                      the default.
+ * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in
+ *                      host mode when dynamic FIFO sizing is enabled
+ *                       16 to 32768
+ *                      Actual maximum value is autodetected and also
+ *                      the default.
+ * @max_transfer_size:  The maximum transfer size supported, in bytes
+ *                       2047 to 65,535
+ *                      Actual maximum value is autodetected and also
+ *                      the default.
+ * @max_packet_count:   The maximum number of packets in a transfer
+ *                       15 to 511
+ *                      Actual maximum value is autodetected and also
+ *                      the default.
+ * @host_channels:      The number of host channel registers to use
+ *                       1 to 16
+ *                      Actual maximum value is autodetected and also
+ *                      the default.
+ * @dev_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO
+ *                          in device mode when dynamic FIFO sizing is enabled
+ *                          16 to 32768
+ *                          Actual maximum value is autodetected and also
+ *                          the default.
+ * @i2c_enable:         Specifies whether to use the I2Cinterface for a full
+ *                      speed PHY. This parameter is only applicable if phy_type
+ *                      is FS.
+ *                       0 - No (default)
+ *                       1 - Yes
+ * @acg_enable:                For enabling Active Clock Gating in the controller
+ *                       0 - Disable
+ *                       1 - Enable
+ * @lpm_mode:          For enabling Link Power Management in the controller
+ *                       0 - Disable
+ *                       1 - Enable
+ * @rx_fifo_size:      Number of 4-byte words in the  Rx FIFO when dynamic
+ *                     FIFO sizing is enabled 16 to 32768
+ *                     Actual maximum value is autodetected and also
+ *                     the default.
+ * @service_interval_mode: For enabling service interval based scheduling in the
+ *                         controller.
+ *                           0 - Disable
+ *                           1 - Enable
  */
 struct dwc2_hw_params {
        unsigned op_mode:3;
        unsigned arch:2;
        unsigned dma_desc_enable:1;
-       unsigned dma_desc_fs_enable:1;
        unsigned enable_dynamic_fifo:1;
        unsigned en_multiple_tx_fifo:1;
-       unsigned host_rx_fifo_size:16;
+       unsigned rx_fifo_size:16;
        unsigned host_nperio_tx_fifo_size:16;
        unsigned dev_nperio_tx_fifo_size:16;
        unsigned host_perio_tx_fifo_size:16;
@@ -464,21 +701,28 @@ struct dwc2_hw_params {
        unsigned hs_phy_type:2;
        unsigned fs_phy_type:2;
        unsigned i2c_enable:1;
+       unsigned acg_enable:1;
        unsigned num_dev_ep:4;
+       unsigned num_dev_in_eps : 4;
        unsigned num_dev_perio_in_ep:4;
        unsigned total_fifo_size:16;
        unsigned power_optimized:1;
+       unsigned hibernation:1;
        unsigned utmi_phy_data_width:2;
+       unsigned lpm_mode:1;
+       unsigned ipg_isoc_en:1;
+       unsigned service_interval_mode:1;
        u32 snpsid;
        u32 dev_ep_dirs;
+       u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
 };
 
 /* Size of control and EP0 buffers */
 #define DWC2_CTRL_BUFF_SIZE 8
 
 /**
- * struct dwc2_gregs_backup - Holds global registers state before entering partial
- * power down
+ * struct dwc2_gregs_backup - Holds global registers state before
+ * entering partial power down
  * @gotgctl:           Backup of GOTGCTL register
  * @gintmsk:           Backup of GINTMSK register
  * @gahbcfg:           Backup of GAHBCFG register
@@ -486,10 +730,13 @@ struct dwc2_hw_params {
  * @grxfsiz:           Backup of GRXFSIZ register
  * @gnptxfsiz:         Backup of GNPTXFSIZ register
  * @gi2cctl:           Backup of GI2CCTL register
- * @hptxfsiz:          Backup of HPTXFSIZ register
+ * @glpmcfg:           Backup of GLPMCFG register
  * @gdfifocfg:         Backup of GDFIFOCFG register
+ * @pcgcctl:           Backup of PCGCCTL register
+ * @pcgcctl1:          Backup of PCGCCTL1 register
  * @dtxfsiz:           Backup of DTXFSIZ registers for each endpoint
  * @gpwrdn:            Backup of GPWRDN register
+ * @valid:             True if registers values backuped.
  */
 struct dwc2_gregs_backup {
        u32 gotgctl;
@@ -499,17 +746,17 @@ struct dwc2_gregs_backup {
        u32 grxfsiz;
        u32 gnptxfsiz;
        u32 gi2cctl;
-       u32 hptxfsiz;
+       u32 glpmcfg;
        u32 pcgcctl;
+       u32 pcgcctl1;
        u32 gdfifocfg;
-       u32 dtxfsiz[MAX_EPS_CHANNELS];
        u32 gpwrdn;
        bool valid;
 };
 
 /**
- * struct  dwc2_dregs_backup - Holds device registers state before entering partial
- * power down
+ * struct dwc2_dregs_backup - Holds device registers state before
+ * entering partial power down
  * @dcfg:              Backup of DCFG register
  * @dctl:              Backup of DCTL register
  * @daintmsk:          Backup of DAINTMSK register
@@ -521,6 +768,8 @@ struct dwc2_gregs_backup {
  * @doepctl:           Backup of DOEPCTL register
  * @doeptsiz:          Backup of DOEPTSIZ register
  * @doepdma:           Backup of DOEPDMA register
+ * @dtxfsiz:           Backup of DTXFSIZ registers for each endpoint
+ * @valid:      True if registers values backuped.
  */
 struct dwc2_dregs_backup {
        u32 dcfg;
@@ -534,17 +783,20 @@ struct dwc2_dregs_backup {
        u32 doepctl[MAX_EPS_CHANNELS];
        u32 doeptsiz[MAX_EPS_CHANNELS];
        u32 doepdma[MAX_EPS_CHANNELS];
+       u32 dtxfsiz[MAX_EPS_CHANNELS];
        bool valid;
 };
 
 /**
- * struct  dwc2_hregs_backup - Holds host registers state before entering partial
- * power down
+ * struct dwc2_hregs_backup - Holds host registers state before
+ * entering partial power down
  * @hcfg:              Backup of HCFG register
  * @haintmsk:          Backup of HAINTMSK register
  * @hcintmsk:          Backup of HCINTMSK register
- * @hptr0:             Backup of HPTR0 register
+ * @hprt0:             Backup of HPTR0 register
  * @hfir:              Backup of HFIR register
+ * @hptxfsiz:          Backup of HPTXFSIZ register
+ * @valid:      True if registers values backuped.
  */
 struct dwc2_hregs_backup {
        u32 hcfg;
@@ -552,9 +804,88 @@ struct dwc2_hregs_backup {
        u32 hcintmsk[MAX_EPS_CHANNELS];
        u32 hprt0;
        u32 hfir;
+       u32 hptxfsiz;
        bool valid;
 };
 
+/*
+ * Constants related to high speed periodic scheduling
+ *
+ * We have a periodic schedule that is DWC2_HS_SCHEDULE_UFRAMES long.  From a
+ * reservation point of view it's assumed that the schedule goes right back to
+ * the beginning after the end of the schedule.
+ *
+ * What does that mean for scheduling things with a long interval?  It means
+ * we'll reserve time for them in every possible microframe that they could
+ * ever be scheduled in.  ...but we'll still only actually schedule them as
+ * often as they were requested.
+ *
+ * We keep our schedule in a "bitmap" structure.  This simplifies having
+ * to keep track of and merge intervals: we just let the bitmap code do most
+ * of the heavy lifting.  In a way scheduling is much like memory allocation.
+ *
+ * We schedule 100us per uframe or 80% of 125us (the maximum amount you're
+ * supposed to schedule for periodic transfers).  That's according to spec.
+ *
+ * Note that though we only schedule 80% of each microframe, the bitmap that we
+ * keep the schedule in is tightly packed (AKA it doesn't have 100us worth of
+ * space for each uFrame).
+ *
+ * Requirements:
+ * - DWC2_HS_SCHEDULE_UFRAMES must even divide 0x4000 (HFNUM_MAX_FRNUM + 1)
+ * - DWC2_HS_SCHEDULE_UFRAMES must be 8 times DWC2_LS_SCHEDULE_FRAMES (probably
+ *   could be any multiple of 8 times DWC2_LS_SCHEDULE_FRAMES, but there might
+ *   be bugs).  The 8 comes from the USB spec: number of microframes per frame.
+ */
+#define DWC2_US_PER_UFRAME             125
+#define DWC2_HS_PERIODIC_US_PER_UFRAME 100
+
+#define DWC2_HS_SCHEDULE_UFRAMES       8
+#define DWC2_HS_SCHEDULE_US            (DWC2_HS_SCHEDULE_UFRAMES * \
+                                        DWC2_HS_PERIODIC_US_PER_UFRAME)
+
+/*
+ * Constants related to low speed scheduling
+ *
+ * For high speed we schedule every 1us.  For low speed that's a bit overkill,
+ * so we make up a unit called a "slice" that's worth 25us.  There are 40
+ * slices in a full frame and we can schedule 36 of those (90%) for periodic
+ * transfers.
+ *
+ * Our low speed schedule can be as short as 1 frame or could be longer.  When
+ * we only schedule 1 frame it means that we'll need to reserve a time every
+ * frame even for things that only transfer very rarely, so something that runs
+ * every 2048 frames will get time reserved in every frame.  Our low speed
+ * schedule can be longer and we'll be able to handle more overlap, but that
+ * will come at increased memory cost and increased time to schedule.
+ *
+ * Note: one other advantage of a short low speed schedule is that if we mess
+ * up and miss scheduling we can jump in and use any of the slots that we
+ * happened to reserve.
+ *
+ * With 25 us per slice and 1 frame in the schedule, we only need 4 bytes for
+ * the schedule.  There will be one schedule per TT.
+ *
+ * Requirements:
+ * - DWC2_US_PER_SLICE must evenly divide DWC2_LS_PERIODIC_US_PER_FRAME.
+ */
+#define DWC2_US_PER_SLICE      25
+#define DWC2_SLICES_PER_UFRAME (DWC2_US_PER_UFRAME / DWC2_US_PER_SLICE)
+
+#define DWC2_ROUND_US_TO_SLICE(us) \
+                               (DIV_ROUND_UP((us), DWC2_US_PER_SLICE) * \
+                                DWC2_US_PER_SLICE)
+
+#define DWC2_LS_PERIODIC_US_PER_FRAME \
+                               900
+#define DWC2_LS_PERIODIC_SLICES_PER_FRAME \
+                               (DWC2_LS_PERIODIC_US_PER_FRAME / \
+                                DWC2_US_PER_SLICE)
+
+#define DWC2_LS_SCHEDULE_FRAMES        1
+#define DWC2_LS_SCHEDULE_SLICES        (DWC2_LS_SCHEDULE_FRAMES * \
+                                DWC2_LS_PERIODIC_SLICES_PER_FRAME)
+
 /**
  * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
  * and periodic schedules
@@ -565,7 +896,7 @@ struct dwc2_hregs_backup {
  * @regs:              Pointer to controller regs
  * @hw_params:          Parameters that were autodetected from the
  *                      hardware registers
- * @core_params:       Parameters that define how the core should be configured
+ * @params:    Parameters that define how the core should be configured
  * @op_state:           The operational State, during transitions (a_host=>
  *                      a_peripheral and b_device=>b_host) this may not match
  *                      the core, but allows the software to determine
@@ -574,15 +905,32 @@ struct dwc2_hregs_backup {
  *                      - USB_DR_MODE_PERIPHERAL
  *                      - USB_DR_MODE_HOST
  *                      - USB_DR_MODE_OTG
- * @hcd_enabled                Host mode sub-driver initialization indicator.
- * @gadget_enabled     Peripheral mode sub-driver initialization indicator.
- * @ll_hw_enabled      Status of low-level hardware resources.
+ * @role_sw:           usb_role_switch handle
+ * @role_sw_default_mode: default operation mode of controller while usb role
+ *                     is USB_ROLE_NONE
+ * @hcd_enabled:       Host mode sub-driver initialization indicator.
+ * @gadget_enabled:    Peripheral mode sub-driver initialization indicator.
+ * @ll_hw_enabled:     Status of low-level hardware resources.
+ * @hibernated:                True if core is hibernated
+ * @in_ppd:            True if core is partial power down mode.
+ * @bus_suspended:     True if bus is suspended
+ * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a
+ *                     remote wakeup.
+ * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend.
+ * @need_phy_for_wake: Quirk saying that we should keep the PHY on at
+ *                     suspend if we need USB to wake us up.
+ * @frame_number:       Frame number read from the core. For both device
+ *                     and host modes. The value ranges are from 0
+ *                     to HFNUM_MAX_FRNUM.
  * @phy:                The otg phy transceiver structure for phy control.
- * @uphy:               The otg phy transceiver structure for old USB phy control.
- * @plat:               The platform specific configuration data. This can be removed once
- *                      all SoCs support usb transceiver.
+ * @uphy:               The otg phy transceiver structure for old USB phy
+ *                      control.
+ * @plat:               The platform specific configuration data. This can be
+ *                      removed once all SoCs support usb transceiver.
  * @supplies:           Definition of USB power supplies
- * @phyif:              PHY interface width
+ * @vbus_supply:        Regulator supplying vbus.
+ * @usb33d:            Optional 3.3v regulator used on some stm32 devices to
+ *                     supply ID and VBUS detection hardware.
  * @lock:              Spinlock that protects all the driver data structures
  * @priv:              Stores a pointer to the struct usb_hcd
  * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
@@ -594,13 +942,26 @@ struct dwc2_hregs_backup {
  *                      interrupt
  * @wkp_timer:          Timer object for handling Wakeup Detected interrupt
  * @lx_state:           Lx state of connected device
- * @gregs_backup: Backup of global registers during suspend
- * @dregs_backup: Backup of device registers during suspend
- * @hregs_backup: Backup of host registers during suspend
+ * @gr_backup: Backup of global registers during suspend
+ * @dr_backup: Backup of device registers during suspend
+ * @hr_backup: Backup of host registers during suspend
+ * @needs_byte_swap:           Specifies whether the opposite endianness.
  *
  * These are for host mode:
  *
  * @flags:              Flags for handling root port state changes
+ * @flags.d32:          Contain all root port flags
+ * @flags.b:            Separate root port flags from each other
+ * @flags.b.port_connect_status_change: True if root port connect status
+ *                      changed
+ * @flags.b.port_connect_status: True if device connected to root port
+ * @flags.b.port_reset_change: True if root port reset status changed
+ * @flags.b.port_enable_change: True if root port enable status changed
+ * @flags.b.port_suspend_change: True if root port suspend status changed
+ * @flags.b.port_over_current_change: True if root port over current state
+ *                       changed.
+ * @flags.b.port_l1_change: True if root port l1 status changed
+ * @flags.b.reserved:   Reserved bits of root port register
  * @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
  *                      Transfers associated with these QHs are not currently
  *                      assigned to a host channel.
@@ -609,6 +970,9 @@ struct dwc2_hregs_backup {
  *                      assigned to a host channel.
  * @non_periodic_qh_ptr: Pointer to next QH to process in the active
  *                      non-periodic schedule
+ * @non_periodic_sched_waiting: Waiting QHs in the non-periodic schedule.
+ *                      Transfers associated with these QHs are not currently
+ *                      assigned to a host channel.
  * @periodic_sched_inactive: Inactive QHs in the periodic schedule. This is a
  *                      list of QHs for periodic transfers that are _not_
  *                      scheduled for the next frame. Each QH in the list has an
@@ -645,9 +1009,9 @@ struct dwc2_hregs_backup {
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
  *                      the same (micro)frame.
- * @frame_usecs:        Internal variable used by the microframe scheduler
- * @frame_number:       Frame number read from the core at SOF. The value ranges
- *                      from 0 to HFNUM_MAX_FRNUM.
+ * @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
+ *                      host is in high speed mode; low speed schedules are
+ *                      stored elsewhere since we need one per TT.
  * @periodic_qh_count:  Count of periodic QHs, if using several eps. Used for
  *                      SOF enable/disable.
  * @free_hc_list:       Free host channels in the controller. This is a list of
@@ -658,8 +1022,8 @@ struct dwc2_hregs_backup {
  *                      host channel is available for non-periodic transactions.
  * @non_periodic_channels: Number of host channels assigned to non-periodic
  *                      transfers
- * @available_host_channels Number of host channels available for the microframe
- *                      scheduler to use
+ * @available_host_channels: Number of host channels available for the
+ *                          microframe scheduler to use
  * @hc_ptr_array:       Array of pointers to the host channel descriptors.
  *                      Allows accessing a host channel descriptor given the
  *                      host channel number. This is useful in interrupt
@@ -669,12 +1033,14 @@ struct dwc2_hregs_backup {
  * @status_buf_dma:     DMA address for status_buf
  * @start_work:         Delayed work for handling host A-cable connection
  * @reset_work:         Delayed work for handling a port reset
+ * @phy_reset_work:     Work structure for doing a PHY reset
  * @otg_port:           OTG port number
  * @frame_list:         Frame list
  * @frame_list_dma:     Frame list DMA address
  * @frame_list_sz:      Frame list size
  * @desc_gen_cache:     Kmem cache for generic descriptors
  * @desc_hsisoc_cache:  Kmem cache for hs isochronous descriptors
+ * @unaligned_cache:    Kmem cache for DMA mode to handle non-aligned buf
  *
  * These are for peripheral mode:
  *
@@ -682,47 +1048,88 @@ struct dwc2_hregs_backup {
  * @dedicated_fifos:    Set if the hardware has dedicated IN-EP fifos.
  * @num_of_eps:         Number of available EPs (excluding EP0)
  * @debug_root:         Root directrory for debugfs.
- * @debug_file:         Main status file for debugfs.
- * @debug_testmode:     Testmode status file for debugfs.
- * @debug_fifo:         FIFO status file for debugfs.
  * @ep0_reply:          Request used for ep0 reply.
  * @ep0_buff:           Buffer for EP0 reply data, if needed.
  * @ctrl_buff:          Buffer for EP0 control requests.
  * @ctrl_req:           Request for EP0 control packets.
  * @ep0_state:          EP0 control transfers state
+ * @delayed_status:            true when gadget driver asks for delayed status
  * @test_mode:          USB test mode requested by the host
- * @eps:                The endpoints being supplied to the gadget framework
- * @g_using_dma:          Indicate if dma usage is enabled
- * @g_rx_fifo_sz:         Contains rx fifo size value
- * @g_np_g_tx_fifo_sz:      Contains Non-Periodic tx fifo size value
- * @g_tx_fifo_sz:         Contains tx fifo size value per endpoints
+ * @remote_wakeup_allowed: True if device is allowed to wake-up host by
+ *                      remote-wakeup signalling
+ * @setup_desc_dma:    EP0 setup stage desc chain DMA address
+ * @setup_desc:                EP0 setup stage desc chain pointer
+ * @ctrl_in_desc_dma:  EP0 IN data phase desc chain DMA address
+ * @ctrl_in_desc:      EP0 IN data phase desc chain pointer
+ * @ctrl_out_desc_dma: EP0 OUT data phase desc chain DMA address
+ * @ctrl_out_desc:     EP0 OUT data phase desc chain pointer
+ * @irq:               Interrupt request line number
+ * @clk:               Pointer to otg clock
+ * @reset:             Pointer to dwc2 reset controller
+ * @reset_ecc:          Pointer to dwc2 optional reset controller in Stratix10.
+ * @regset:            A pointer to a struct debugfs_regset32, which contains
+ *                     a pointer to an array of register definitions, the
+ *                     array size and the base address where the register bank
+ *                     is to be found.
+ * @last_frame_num:    Number of last frame. Range from 0 to  32768
+ * @frame_num_array:    Used only  if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is
+ *                     defined, for missed SOFs tracking. Array holds that
+ *                     frame numbers, which not equal to last_frame_num +1
+ * @last_frame_num_array:   Used only  if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is
+ *                         defined, for missed SOFs tracking.
+ *                         If current_frame_number != last_frame_num+1
+ *                         then last_frame_num added to this array
+ * @frame_num_idx:     Actual size of frame_num_array and last_frame_num_array
+ * @dumped_frame_num_array:    1 - if missed SOFs frame numbers dumbed
+ *                             0 - if missed SOFs frame numbers not dumbed
+ * @fifo_mem:                  Total internal RAM for FIFOs (bytes)
+ * @fifo_map:          Each bit intend for concrete fifo. If that bit is set,
+ *                     then that fifo is used
+ * @gadget:            Represents a usb gadget device
+ * @connected:         Used in slave mode. True if device connected with host
+ * @eps_in:            The IN endpoints being supplied to the gadget framework
+ * @eps_out:           The OUT endpoints being supplied to the gadget framework
+ * @new_connection:    Used in host mode. True if there are new connected
+ *                     device
+ * @enabled:           Indicates the enabling state of controller
+ *
  */
 struct dwc2_hsotg {
        struct device *dev;
+//     void __iomem *regs;
        struct dwc2_softc *hsotg_sc;
        /** Params detected from hardware */
        struct dwc2_hw_params hw_params;
        /** Params to actually use */
-       struct dwc2_core_params *core_params;
+       struct dwc2_core_params params;
        enum usb_otg_state op_state;
        enum usb_dr_mode dr_mode;
+//     struct usb_role_switch *role_sw;
+//     enum usb_dr_mode role_sw_default_mode;
        unsigned int hcd_enabled:1;
        unsigned int gadget_enabled:1;
        unsigned int ll_hw_enabled:1;
+       unsigned int hibernated:1;
+       unsigned int in_ppd:1;
+       bool bus_suspended;
+       unsigned int reset_phy_on_wake:1;
+       unsigned int need_phy_for_wake:1;
+       unsigned int phy_off_for_suspend:1;
+       u16 frame_number;
 
-       spinlock_t lock;
-       void *priv;
-       struct usb_phy *uphy;
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
        struct phy *phy;
        struct usb_phy *uphy;
        struct dwc2_hsotg_plat *plat;
-       struct regulator_bulk_data supplies[ARRAY_SIZE(dwc2_hsotg_supply_names)];
-       u32 phyif;
+//     struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
+       struct regulator *vbus_supply;
+       struct regulator *usb33d;
 
+       spinlock_t lock;
+       void *priv;
        int     irq;
-       struct clk *clk;
-#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
+//     struct clk clk;
+//     struct reset_control *reset;
+//     struct reset_control *reset_ecc;
 
        unsigned int queuing_high_bandwidth:1;
        unsigned int srp_success:1;
@@ -737,13 +1144,28 @@ struct dwc2_hsotg {
 
        struct dentry *debug_root;
        struct debugfs_regset32 *regset;
+       bool needs_byte_swap;
 
        /* DWC OTG HW Release versions */
 #define DWC2_CORE_REV_2_71a    0x4f54271a
+#define DWC2_CORE_REV_2_72a     0x4f54272a
+#define DWC2_CORE_REV_2_80a    0x4f54280a
 #define DWC2_CORE_REV_2_90a    0x4f54290a
+#define DWC2_CORE_REV_2_91a    0x4f54291a
 #define DWC2_CORE_REV_2_92a    0x4f54292a
 #define DWC2_CORE_REV_2_94a    0x4f54294a
 #define DWC2_CORE_REV_3_00a    0x4f54300a
+#define DWC2_CORE_REV_3_10a    0x4f54310a
+#define DWC2_CORE_REV_4_00a    0x4f54400a
+#define DWC2_CORE_REV_4_20a    0x4f54420a
+#define DWC2_FS_IOT_REV_1_00a  0x5531100a
+#define DWC2_HS_IOT_REV_1_00a  0x5532100a
+#define DWC2_CORE_REV_MASK     0x0000ffff
+
+       /* DWC OTG HW Core ID */
+#define DWC2_OTG_ID            0x4f540000
+#define DWC2_FS_IOT_ID         0x55310000
+#define DWC2_HS_IOT_ID         0x55320000
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
        union dwc2_hcd_internal_flags {
@@ -770,15 +1192,15 @@ struct dwc2_hsotg {
        struct list_head periodic_sched_queued;
        struct list_head split_order;
        u16 periodic_usecs;
-       u16 frame_usecs[8];
-       u16 frame_number;
+       unsigned long hs_periodic_bitmap[
+               DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
        u16 periodic_qh_count;
-       bool bus_suspended;
        bool new_connection;
 
+       u16 last_frame_num;
+
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 #define FRAME_NUM_ARRAY_SIZE 1000
-       u16 last_frame_num;
        u16 *frame_num_array;
        u16 *last_frame_num_array;
        int frame_num_idx;
@@ -790,13 +1212,14 @@ struct dwc2_hsotg {
        int non_periodic_channels;
        int available_host_channels;
        struct dwc2_host_chan *hc_ptr_array[MAX_EPS_CHANNELS];
-       struct usb_dma status_buf_usbdma;
        u8 *status_buf;
+       struct usb_dma status_buf_dma_usb;
        dma_addr_t status_buf_dma;
 #define DWC2_HCD_STATUS_BUF_SIZE 64
 
        struct delayed_work start_work;
        struct delayed_work reset_work;
+//     struct work_struct phy_reset_work;
        u8 otg_port;
        struct usb_dma frame_list_usbdma;
        u32 *frame_list;
@@ -804,28 +1227,13 @@ struct dwc2_hsotg {
        u32 frame_list_sz;
        struct kmem_cache *desc_gen_cache;
        struct kmem_cache *desc_hsisoc_cache;
+       struct kmem_cache *unaligned_cache;
+#define DWC2_KMEM_UNALIGNED_BUF_SIZE 1024
 
-#ifdef DEBUG
-       u32 frrem_samples;
-       u64 frrem_accum;
-
-       u32 hfnum_7_samples_a;
-       u64 hfnum_7_frrem_accum_a;
-       u32 hfnum_0_samples_a;
-       u64 hfnum_0_frrem_accum_a;
-       u32 hfnum_other_samples_a;
-       u64 hfnum_other_frrem_accum_a;
-
-       u32 hfnum_7_samples_b;
-       u64 hfnum_7_frrem_accum_b;
-       u32 hfnum_0_samples_b;
-       u64 hfnum_0_frrem_accum_b;
-       u32 hfnum_other_samples_b;
-       u64 hfnum_other_frrem_accum_b;
-#endif
 #endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
 
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+       IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
        /* Gadget structures */
        struct usb_gadget_driver *driver;
        int fifo_mem;
@@ -838,20 +1246,63 @@ struct dwc2_hsotg {
        void *ep0_buff;
        void *ctrl_buff;
        enum dwc2_ep0_state ep0_state;
+       unsigned delayed_status : 1;
        u8 test_mode;
 
+//     dma_addr_t setup_desc_dma[2];
+//     struct dwc2_dma_desc *setup_desc[2];
+//     dma_addr_t ctrl_in_desc_dma;
+//     struct dwc2_dma_desc *ctrl_in_desc;
+//     dma_addr_t ctrl_out_desc_dma;
+//     struct dwc2_dma_desc *ctrl_out_desc;
+
        struct usb_gadget gadget;
        unsigned int enabled:1;
        unsigned int connected:1;
+       unsigned int remote_wakeup_allowed:1;
        struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
        struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
-       u32 g_using_dma;
-       u32 g_rx_fifo_sz;
-       u32 g_np_g_tx_fifo_sz;
-       u32 g_tx_fifo_sz[MAX_EPS_CHANNELS];
 #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
 };
 
+#define dwc2_readl(hsotg,  reg)        \
+    bus_space_read_4((hsotg)->hsotg_sc->sc_iot, (hsotg)->hsotg_sc->sc_ioh, \
+    (reg))
+#define dwc2_writel(hsotg, data, reg)  \
+    bus_space_write_4((hsotg)->hsotg_sc->sc_iot, (hsotg)->hsotg_sc->sc_ioh, \
+    (reg), (data))
+
+#ifdef DWC2_LOG_WRITES
+       pr_info("info:: wrote %08x to %p\n", value, hsotg->regs + offset);
+#endif
+
+#if 0
+static inline void dwc2_readl_rep(struct dwc2_hsotg *hsotg, u32 offset,
+                                 void *buffer, unsigned int count)
+{
+       if (count) {
+               u32 *buf = buffer;
+
+               do {
+                       u32 x = dwc2_readl(hsotg, offset);
+                       *buf++ = x;
+               } while (--count);
+       }
+}
+
+static inline void dwc2_writel_rep(struct dwc2_hsotg *hsotg, u32 offset,
+                                  const void *buffer, unsigned int count)
+{
+       if (count) {
+               const u32 *buf = buffer;
+
+               do {
+                       dwc2_writel(hsotg, *buf++, offset);
+               } while (--count);
+       }
+}
+#endif
+
 /* Reasons for halting a host channel */
 enum dwc2_halt_status {
        DWC2_HC_XFER_NO_HALT_STATUS,
@@ -870,268 +1321,82 @@ enum dwc2_halt_status {
        DWC2_HC_XFER_URB_DEQUEUE,
 };
 
+/* Core version information */
+static inline bool dwc2_is_iot(struct dwc2_hsotg *hsotg)
+{
+       return (hsotg->hw_params.snpsid & 0xfff00000) == 0x55300000;
+}
+
+static inline bool dwc2_is_fs_iot(struct dwc2_hsotg *hsotg)
+{
+       return (hsotg->hw_params.snpsid & 0xffff0000) == 0x55310000;
+}
+
+static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg)
+{
+       return (hsotg->hw_params.snpsid & 0xffff0000) == 0x55320000;
+}
+
 /*
  * The following functions support initialization of the core driver component
  * and the DWC_otg controller
  */
-extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
-extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
-extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
-extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
-extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
-
+int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait);
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg);
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup,
+                                bool restore);
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host);
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+               int reset, int is_host);
+void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg);
+int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy);
+
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host);
 void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
 
-/*
- * Host core Functions.
- * The following functions support managing the DWC_otg controller in host
- * mode.
- */
-extern void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
-extern void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
-                        enum dwc2_halt_status halt_status);
-extern void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg,
-                           struct dwc2_host_chan *chan);
-extern void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
-                                  struct dwc2_host_chan *chan);
-extern void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
-                                       struct dwc2_host_chan *chan);
-extern int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
-                                    struct dwc2_host_chan *chan);
-extern void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
-                           struct dwc2_host_chan *chan);
-extern void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg);
-extern void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg);
-
-extern u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
-extern bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
+bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
+
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg);
 
 /*
  * Common core Functions.
  * The following functions support managing the DWC_otg controller in either
  * device or host mode.
  */
-extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
-extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
-extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
-
-extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
-extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
-extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
-
-/* This function should be called on every hardware interrupt. */
-extern irqreturn_t dwc2_handle_common_intr(void *dev);
-
-/* OTG Core Parameters */
-
-/*
- * Specifies the OTG capabilities. The driver will automatically
- * detect the value for this parameter if none is specified.
- * 0 - HNP and SRP capable (default)
- * 1 - SRP Only capable
- * 2 - No HNP/SRP capable
- */
-extern void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE         0
-#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE                1
-#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE      2
-
-/*
- * Specifies whether to use slave or DMA mode for accessing the data
- * FIFOs. The driver will automatically detect the value for this
- * parameter if none is specified.
- * 0 - Slave
- * 1 - DMA (default, if available)
- */
-extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * When DMA mode is enabled specifies whether to use
- * address DMA or DMA Descritor mode for accessing the data
- * FIFOs in device mode. The driver will automatically detect
- * the value for this parameter if none is specified.
- * 0 - address DMA
- * 1 - DMA Descriptor(default, if available)
- */
-extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * When DMA mode is enabled specifies whether to use
- * address DMA or DMA Descritor mode with full speed devices
- * for accessing the data FIFOs in host mode.
- * 0 - address DMA
- * 1 - FS DMA Descriptor(default, if available)
- */
-extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
-                                             int val);
-
-/*
- * Specifies the maximum speed of operation in host and device mode.
- * The actual speed depends on the speed of the attached device and
- * the value of phy_type. The actual speed depends on the speed of the
- * attached device.
- * 0 - High Speed (default)
- * 1 - Full Speed
- */
-extern void dwc2_set_param_speed(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_SPEED_PARAM_HIGH  0
-#define DWC2_SPEED_PARAM_FULL  1
-
-/*
- * Specifies whether low power mode is supported when attached
- * to a Full Speed or Low Speed device in host mode.
- *
- * 0 - Don't support low power mode (default)
- * 1 - Support low power mode
- */
-extern void dwc2_set_param_host_support_fs_ls_low_power(
-               struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies the PHY clock rate in low power mode when connected to a
- * Low Speed device in host mode. This parameter is applicable only if
- * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
- * then defaults to 6 MHZ otherwise 48 MHZ.
- *
- * 0 - 48 MHz
- * 1 - 6 MHz
- */
-extern void dwc2_set_param_host_ls_low_power_phy_clk(struct dwc2_hsotg *hsotg,
-                                                    int val);
-#define DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ     0
-#define DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ      1
-
-/*
- * 0 - Use cC FIFO size parameters
- * 1 - Allow dynamic FIFO sizing (default)
- */
-extern void dwc2_set_param_enable_dynamic_fifo(struct dwc2_hsotg *hsotg,
-                                              int val);
-
-/*
- * Number of 4-byte words in the Rx FIFO in host mode when dynamic
- * FIFO sizing is enabled.
- * 16 to 32768 (default 1024)
- */
-extern void dwc2_set_param_host_rx_fifo_size(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Number of 4-byte words in the non-periodic Tx FIFO in host mode
- * when Dynamic FIFO sizing is enabled in the core.
- * 16 to 32768 (default 256)
- */
-extern void dwc2_set_param_host_nperio_tx_fifo_size(struct dwc2_hsotg *hsotg,
-                                                   int val);
-
-/*
- * Number of 4-byte words in the host periodic Tx FIFO when dynamic
- * FIFO sizing is enabled.
- * 16 to 32768 (default 256)
- */
-extern void dwc2_set_param_host_perio_tx_fifo_size(struct dwc2_hsotg *hsotg,
-                                                  int val);
-
-/*
- * The maximum transfer size supported in bytes.
- * 2047 to 65,535  (default 65,535)
- */
-extern void dwc2_set_param_max_transfer_size(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * The maximum number of packets in a transfer.
- * 15 to 511  (default 511)
- */
-extern void dwc2_set_param_max_packet_count(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * The number of host channel registers to use.
- * 1 to 16 (default 11)
- * Note: The FPGA configuration supports a maximum of 11 host channels.
- */
-extern void dwc2_set_param_host_channels(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies the type of PHY interface to use. By default, the driver
- * will automatically detect the phy_type.
- *
- * 0 - Full Speed PHY
- * 1 - UTMI+ (default)
- * 2 - ULPI
- */
-extern void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_PHY_TYPE_PARAM_FS         0
-#define DWC2_PHY_TYPE_PARAM_UTMI       1
-#define DWC2_PHY_TYPE_PARAM_ULPI       2
-
-/*
- * Specifies the UTMI+ Data Width. This parameter is
- * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
- * PHY_TYPE, this parameter indicates the data width between
- * the MAC and the ULPI Wrapper.) Also, this parameter is
- * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
- * to "8 and 16 bits", meaning that the core has been
- * configured to work at either data path width.
- *
- * 8 or 16 bits (default 16)
- */
-extern void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies whether the ULPI operates at double or single
- * data rate. This parameter is only applicable if PHY_TYPE is
- * ULPI.
- *
- * 0 - single data rate ULPI interface with 8 bit wide data
- * bus (default)
- * 1 - double data rate ULPI interface with 4 bit wide data
- * bus
- */
-extern void dwc2_set_param_phy_ulpi_ddr(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies whether to use the internal or external supply to
- * drive the vbus with a ULPI phy.
- */
-extern void dwc2_set_param_phy_ulpi_ext_vbus(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_PHY_ULPI_INTERNAL_VBUS    0
-#define DWC2_PHY_ULPI_EXTERNAL_VBUS    1
-
-/*
- * Specifies whether to use the I2Cinterface for full speed PHY. This
- * parameter is only applicable if PHY_TYPE is FS.
- * 0 - No (default)
- * 1 - Yes
- */
-extern void dwc2_set_param_i2c_enable(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_param_ulpi_fs_ls(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_param_ts_dline(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies whether dedicated transmit FIFOs are
- * enabled for non periodic IN endpoints in device mode
- * 0 - No
- * 1 - Yes
- */
-extern void dwc2_set_param_en_multiple_tx_fifo(struct dwc2_hsotg *hsotg,
-                                              int val);
+void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
+void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
+void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
 
-extern void dwc2_set_param_reload_ctl(struct dwc2_hsotg *hsotg, int val);
+void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
+void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
 
-extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val);
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+                            int is_host);
+int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg);
 
-extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val);
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
 
-extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
-                               const struct dwc2_core_params *params);
-
-extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
-
-extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
-
-extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
-extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
+/* This function should be called on every hardware interrupt. */
+irqreturn_t dwc2_handle_common_intr(void *dev);
+
+/* The device ID match table */
+//extern const struct of_device_id dwc2_of_match_table[];
+//extern const struct acpi_device_id dwc2_acpi_match[];
+
+int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
+int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
+
+/* Common polling functions */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
+                           u32 timeout);
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
+                             u32 timeout);
+/* Parameters */
+int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
+int dwc2_init_params(struct dwc2_hsotg *hsotg);
+void dwc2_set_default_params(struct dwc2_hsotg *);
+void dwc2_check_params(struct dwc2_hsotg *);
 
 /*
  * The following functions check the controller's OTG operation mode
@@ -1141,7 +1406,7 @@ extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
  * are read in and cached so they always read directly from the
  * GHWCFG2 register.
  */
-unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
+unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg);
 bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
 bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
 bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
@@ -1151,38 +1416,59 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
  */
 static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
 {
-       return (DWC2_READ_4(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
+       return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
 }
 
 static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
 {
-       return (DWC2_READ_4(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
+       return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
 }
 
-/*
- * Dump core registers and SPRAM
- */
-extern void dwc2_dump_dev_registers(struct dwc2_hsotg *hsotg);
-extern void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg);
-extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
+int dwc2_drd_init(struct dwc2_hsotg *hsotg);
+void dwc2_drd_suspend(struct dwc2_hsotg *hsotg);
+void dwc2_drd_resume(struct dwc2_hsotg *hsotg);
+void dwc2_drd_exit(struct dwc2_hsotg *hsotg);
 
 /*
- * Return OTG version - either 1.3 or 2.0
+ * Dump core registers and SPRAM
  */
-extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg);
+void dwc2_dump_dev_registers(struct dwc2_hsotg *hsotg);
+void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg);
+void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
 
 /* Gadget defines */
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-extern int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
-extern int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
-extern int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
-extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
-extern void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
-               bool reset);
-extern void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
-extern void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
-extern int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+       IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
+int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
+int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
+void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+                                      bool reset);
+void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg);
+void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
+void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
+int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
 #define dwc2_is_device_connected(hsotg) (hsotg->connected)
+#define dwc2_is_device_enabled(hsotg) (hsotg->enabled)
+int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
+int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+                                int rem_wakeup, int reset);
+int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
+                                       bool restore);
+void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg);
+void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg,
+                                  int rem_wakeup);
+int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
+int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
+int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
+void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
+void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg);
+static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg)
+{ hsotg->fifo_map = 0; }
 #else
 static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
 { return 0; }
@@ -1190,32 +1476,110 @@ static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2)
 { return 0; }
 static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2)
 { return 0; }
-static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
+static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
 { return 0; }
 static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
-               bool reset) {}
+                                                    bool reset) {}
+static inline void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
 static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
-                                                       int testmode)
+                                          int testmode)
 { return 0; }
 #define dwc2_is_device_connected(hsotg) (0)
+#define dwc2_is_device_enabled(hsotg) (0)
+static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
+                                               int remote_wakeup)
+{ return 0; }
+static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+                                              int rem_wakeup, int reset)
+{ return 0; }
+static inline int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
+                                                     bool restore)
+{ return 0; }
+static inline void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg,
+                                                int rem_wakeup) {}
+static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {}
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
-extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
+int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
+void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
+void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
+void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
+int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex);
+int dwc2_port_resume(struct dwc2_hsotg *hsotg);
+int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
+int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
+                              int rem_wakeup, int reset);
+int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg);
+int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
+                                     int rem_wakeup, bool restore);
+void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg);
+void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup);
+bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2);
+static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
+{
+//     schedule_work(&hsotg->phy_reset_work);
+}
 #else
 static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 { return 0; }
+static inline int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg,
+                                                  int us)
+{ return 0; }
 static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
 static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
-static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
+//static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+{ return 0; }
+static inline int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
+{ return 0; }
+static inline int dwc2_port_resume(struct dwc2_hsotg *hsotg)
+{ return 0; }
 static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 { return 0; }
+static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
+                                            int rem_wakeup, int reset)
+{ return 0; }
+static inline int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
+                                                   int rem_wakeup, bool restore)
+{ return 0; }
+static inline void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg,
+                                              int rem_wakeup) {}
+static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
+{ return false; }
+static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
+
 #endif
 
 #endif /* __DWC2_CORE_H__ */
index fcd5810..395572e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_coreintr.c,v 1.11 2021/07/22 18:32:33 mglocker Exp $     */
+/*     $OpenBSD: dwc2_coreintr.c,v 1.12 2022/09/04 08:42:40 mglocker Exp $     */
 /*     $NetBSD: dwc2_coreintr.c,v 1.8 2014/04/04 05:40:57 skrll Exp $  */
 
 /*
@@ -40,7 +40,6 @@
 /*
  * This file contains the common interrupt handlers
  */
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -63,7 +62,6 @@
 #include <dev/usb/dwc2/dwc2_core.h>
 #include <dev/usb/dwc2/dwc2_hcd.h>
 
-#ifdef DWC2_DEBUG
 STATIC const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
 {
        switch (hsotg->op_state) {
@@ -81,7 +79,6 @@ STATIC const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
                return "unknown";
        }
 }
-#endif
 
 /**
  * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
@@ -92,11 +89,11 @@ STATIC const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
 {
-       u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);
+       u32 hprt0 = dwc2_readl(hsotg, HPRT0);
 
        if (hprt0 & HPRT0_ENACHG) {
                hprt0 &= ~HPRT0_ENA;
-               DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+               dwc2_writel(hsotg, hprt0, HPRT0);
        }
 }
 
@@ -108,7 +105,7 @@ STATIC void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
 STATIC void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
 {
        /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_MODEMIS);
+       dwc2_writel(hsotg, GINTSTS_MODEMIS, GINTSTS);
 
        dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
                 dwc2_is_host_mode(hsotg) ? "Host" : "Device");
@@ -126,8 +123,8 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
        u32 gotgctl;
        u32 gintmsk;
 
-       gotgint = DWC2_READ_4(hsotg, GOTGINT);
-       gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+       gotgint = dwc2_readl(hsotg, GOTGINT);
+       gotgctl = dwc2_readl(hsotg, GOTGCTL);
        dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
                dwc2_op_state_str(hsotg));
 
@@ -135,7 +132,7 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                dev_dbg(hsotg->dev,
                        " ++OTG Interrupt: Session End Detected++ (%s)\n",
                        dwc2_op_state_str(hsotg));
-               gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+               gotgctl = dwc2_readl(hsotg, GOTGCTL);
 
                if (dwc2_is_device_mode(hsotg))
                        dwc2_hsotg_disconnect(hsotg);
@@ -161,25 +158,24 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                        hsotg->lx_state = DWC2_L0;
                }
 
-               gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+               gotgctl = dwc2_readl(hsotg, GOTGCTL);
                gotgctl &= ~GOTGCTL_DEVHNPEN;
-               DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
+               dwc2_writel(hsotg, gotgctl, GOTGCTL);
        }
 
        if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
                dev_dbg(hsotg->dev,
                        " ++OTG Interrupt: Session Request Success Status Change++\n");
-               gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+               gotgctl = dwc2_readl(hsotg, GOTGCTL);
                if (gotgctl & GOTGCTL_SESREQSCS) {
-                       if (hsotg->core_params->phy_type ==
-                                       DWC2_PHY_TYPE_PARAM_FS
-                           && hsotg->core_params->i2c_enable > 0) {
+                       if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
+                           hsotg->params.i2c_enable) {
                                hsotg->srp_success = 1;
                        } else {
                                /* Clear Session Request */
-                               gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+                               gotgctl = dwc2_readl(hsotg, GOTGCTL);
                                gotgctl &= ~GOTGCTL_SESREQ;
-                               DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
+                               dwc2_writel(hsotg, gotgctl, GOTGCTL);
                        }
                }
        }
@@ -189,7 +185,7 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                 * Print statements during the HNP interrupt handling
                 * can cause it to fail
                 */
-               gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+               gotgctl = dwc2_readl(hsotg, GOTGCTL);
                /*
                 * WA for 3.00a- HW is not setting cur_mode, even sometimes
                 * this does not help
@@ -209,9 +205,9 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                                 * interrupt does not get handled and Linux
                                 * complains loudly.
                                 */
-                               gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+                               gintmsk = dwc2_readl(hsotg, GINTMSK);
                                gintmsk &= ~GINTSTS_SOF;
-                               DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+                               dwc2_writel(hsotg, gintmsk, GINTMSK);
 
                                /*
                                 * Call callback function with spin lock
@@ -225,9 +221,9 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                                hsotg->op_state = OTG_STATE_B_HOST;
                        }
                } else {
-                       gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
+                       gotgctl = dwc2_readl(hsotg, GOTGCTL);
                        gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
-                       DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
+                       dwc2_writel(hsotg, gotgctl, GOTGCTL);
                        dev_dbg(hsotg->dev, "HNP Failed\n");
                        dev_err(hsotg->dev,
                                "Device Not Connected/Responding\n");
@@ -253,9 +249,9 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                        hsotg->op_state = OTG_STATE_A_PERIPHERAL;
                } else {
                        /* Need to disable SOF interrupt immediately */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+                       gintmsk = dwc2_readl(hsotg, GINTMSK);
                        gintmsk &= ~GINTSTS_SOF;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
                        spin_unlock(&hsotg->lock);
                        dwc2_hcd_start(hsotg);
                        spin_lock(&hsotg->lock);
@@ -270,7 +266,7 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");
 
        /* Clear GOTGINT */
-       DWC2_WRITE_4(hsotg, GOTGINT, gotgint);
+       dwc2_writel(hsotg, gotgint, GOTGINT);
 }
 
 /**
@@ -288,26 +284,21 @@ STATIC void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
        u32 gintmsk;
 
        /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
+       dwc2_writel(hsotg, GINTSTS_CONIDSTSCHNG, GINTSTS);
 
        /* Need to disable SOF interrupt immediately */
-       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+       gintmsk = dwc2_readl(hsotg, GINTMSK);
        gintmsk &= ~GINTSTS_SOF;
-       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+       dwc2_writel(hsotg, gintmsk, GINTMSK);
 
        dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++  (%s)\n",
                dwc2_is_host_mode(hsotg) ? "Host" : "Device");
 
        /*
         * Need to schedule a work, as there are possible DELAY function calls.
-        * Release lock before scheduling workq as it holds spinlock during
-        * scheduling.
         */
-       if (hsotg->wq_otg) {
-               spin_unlock(&hsotg->lock);
+       if (hsotg->wq_otg)
                task_add(hsotg->wq_otg, &hsotg->wf_otg);
-               spin_lock(&hsotg->lock);
-       }
 }
 
 /**
@@ -324,19 +315,28 @@ STATIC void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
 STATIC void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
 {
        int ret;
+       u32 hprt0;
 
        /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SESSREQINT);
+       dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS);
 
        dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
-                                                       hsotg->lx_state);
+               hsotg->lx_state);
 
        if (dwc2_is_device_mode(hsotg)) {
                if (hsotg->lx_state == DWC2_L2) {
-                       ret = dwc2_exit_hibernation(hsotg, true);
-                       if (ret && (ret != -ENOTSUP))
-                               dev_err(hsotg->dev,
-                                       "exit hibernation failed\n");
+                       if (hsotg->in_ppd) {
+                               ret = dwc2_exit_partial_power_down(hsotg, 0,
+                                                                  true);
+                               if (ret)
+                                       dev_err(hsotg->dev,
+                                               "exit power_down failed\n");
+                       }
+
+                       /* Exit gadget mode clock gating. */
+                       if (hsotg->params.power_down ==
+                           DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
+                               dwc2_gadget_exit_clock_gating(hsotg, 0);
                }
 
                /*
@@ -344,9 +344,67 @@ STATIC void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
                 * established
                 */
                dwc2_hsotg_disconnect(hsotg);
+       } else {
+               /* Turn on the port power bit. */
+               hprt0 = dwc2_read_hprt0(hsotg);
+               hprt0 |= HPRT0_PWR;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+               /* Connect hcd after port power is set. */
+               dwc2_hcd_connect(hsotg);
        }
 }
 
+/**
+ * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
+{
+       u32 glpmcfg;
+       u32 i = 0;
+
+       if (hsotg->lx_state != DWC2_L1) {
+               dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n");
+               return;
+       }
+
+       glpmcfg = dwc2_readl(hsotg, GLPMCFG);
+       if (dwc2_is_device_mode(hsotg)) {
+               dev_dbg(hsotg->dev, "Exit from L1 state\n");
+               glpmcfg &= ~GLPMCFG_ENBLSLPM;
+               glpmcfg &= ~GLPMCFG_HIRD_THRES_EN;
+               dwc2_writel(hsotg, glpmcfg, GLPMCFG);
+
+               do {
+                       glpmcfg = dwc2_readl(hsotg, GLPMCFG);
+
+                       if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK |
+                                        GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS)))
+                               break;
+
+                       udelay(1);
+               } while (++i < 200);
+
+               if (i == 200) {
+                       dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n");
+                       return;
+               }
+               dwc2_gadget_init_lpm(hsotg);
+       } else {
+               /* TODO */
+               dev_err(hsotg->dev, "Host side LPM is not supported.\n");
+               return;
+       }
+
+       /* Change to L0 state */
+       hsotg->lx_state = DWC2_L0;
+
+       /* Inform gadget to exit from L1 */
+//     call_gadget(hsotg, resume);
+}
+
 /*
  * This interrupt indicates that the DWC_otg controller has detected a
  * resume or remote wakeup sequence. If the DWC_otg controller is in
@@ -359,39 +417,68 @@ STATIC void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
        int ret;
 
        /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
+       dwc2_writel(hsotg, GINTSTS_WKUPINT, GINTSTS);
 
        dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
        dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
 
+       if (hsotg->lx_state == DWC2_L1) {
+               dwc2_wakeup_from_lpm_l1(hsotg);
+               return;
+       }
+
        if (dwc2_is_device_mode(hsotg)) {
-               dev_dbg(hsotg->dev, "DSTS=0x%0x\n", DWC2_READ_4(hsotg, DSTS));
+               dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
+                       dwc2_readl(hsotg, DSTS));
                if (hsotg->lx_state == DWC2_L2) {
-                       u32 dctl = DWC2_READ_4(hsotg, DCTL);
-
-                       /* Clear Remote Wakeup Signaling */
-                       dctl &= ~DCTL_RMTWKUPSIG;
-                       DWC2_WRITE_4(hsotg, DCTL, dctl);
-                       ret = dwc2_exit_hibernation(hsotg, true);
-                       if (ret && (ret != -ENOTSUP))
-                               dev_err(hsotg->dev, "exit hibernation failed\n");
+                       if (hsotg->in_ppd) {
+                               u32 dctl = dwc2_readl(hsotg, DCTL);
+                               /* Clear Remote Wakeup Signaling */
+                               dctl &= ~DCTL_RMTWKUPSIG;
+                               dwc2_writel(hsotg, dctl, DCTL);
+                               ret = dwc2_exit_partial_power_down(hsotg, 1,
+                                                                  true);
+                               if (ret)
+                                       dev_err(hsotg->dev,
+                                               "exit partial_power_down failed\n");
 
-                       call_gadget(hsotg, resume);
+//                             call_gadget(hsotg, resume);
+                       }
+#if 0
+                       /* Exit gadget mode clock gating. */
+                       if (hsotg->params.power_down ==
+                           DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
+                               dwc2_gadget_exit_clock_gating(hsotg, 0);
+#endif
+               } else {
+                       /* Change to L0 state */
+                       hsotg->lx_state = DWC2_L0;
                }
-               /* Change to L0 state */
-               hsotg->lx_state = DWC2_L0;
        } else {
-               if (hsotg->core_params->hibernation)
-                       return;
+               if (hsotg->lx_state == DWC2_L2) {
+                       if (hsotg->in_ppd) {
+                               ret = dwc2_exit_partial_power_down(hsotg, 1,
+                                                                  true);
+                               if (ret)
+                                       dev_err(hsotg->dev,
+                                               "exit partial_power_down failed\n");
+                       }
 
-               if (hsotg->lx_state != DWC2_L1) {
-                       u32 pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
+                       if (hsotg->params.power_down ==
+                           DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
+                               dwc2_host_exit_clock_gating(hsotg, 1);
+
+                       /*
+                        * If we've got this quirk then the PHY is stuck upon
+                        * wakeup.  Assert reset.  This will propagate out and
+                        * eventually we'll re-enumerate the device.  Not great
+                        * but the best we can do.  We can't call phy_reset()
+                        * at interrupt time but there's no hurry, so we'll
+                        * schedule it for later.
+                        */
+                       if (hsotg->reset_phy_on_wake)
+                               dwc2_host_schedule_phy_reset(hsotg);
 
-                       /* Restart the Phy Clock */
-                       pcgcctl &= ~PCGCTL_STOPPCLK;
-                       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-                       timeout_set(&hsotg->wkp_timer, dwc2_wakeup_detected,
-                           hsotg);
                        timeout_add_msec(&hsotg->wkp_timer, 71);
                } else {
                        /* Change to L0 state */
@@ -406,7 +493,7 @@ STATIC void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
 {
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_DISCONNINT);
+       dwc2_writel(hsotg, GINTSTS_DISCONNINT, GINTSTS);
 
        dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
                dwc2_is_host_mode(hsotg) ? "Host" : "Device",
@@ -430,7 +517,7 @@ STATIC void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
        int ret;
 
        /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
+       dwc2_writel(hsotg, GINTSTS_USBSUSP, GINTSTS);
 
        dev_dbg(hsotg->dev, "USB SUSPEND\n");
 
@@ -439,34 +526,54 @@ STATIC void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
                 * Check the Device status register to determine if the Suspend
                 * state is active
                 */
-               dsts = DWC2_READ_4(hsotg, DSTS);
-               dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
+               dsts = dwc2_readl(hsotg, DSTS);
+               dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts);
                dev_dbg(hsotg->dev,
-                       "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
+                       "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n",
                        !!(dsts & DSTS_SUSPSTS),
-                       hsotg->hw_params.power_optimized);
-               if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
-                       /* Ignore suspend request before enumeration */
-                       if (!dwc2_is_device_connected(hsotg)) {
-                               dev_dbg(hsotg->dev,
-                                               "ignore suspend request before enumeration\n");
-                               return;
-                       }
+                       hsotg->hw_params.power_optimized,
+                       hsotg->hw_params.hibernation);
 
-                       ret = dwc2_enter_hibernation(hsotg);
-                       if (ret) {
-                               if (ret != -ENOTSUP)
+               /* Ignore suspend request before enumeration */
+               if (!dwc2_is_device_connected(hsotg)) {
+                       dev_dbg(hsotg->dev,
+                               "ignore suspend request before enumeration\n");
+                       return;
+               }
+               if (dsts & DSTS_SUSPSTS) {
+                       switch (hsotg->params.power_down) {
+                       case DWC2_POWER_DOWN_PARAM_PARTIAL:
+                               ret = dwc2_enter_partial_power_down(hsotg);
+                               if (ret)
                                        dev_err(hsotg->dev,
-                                                       "enter hibernation failed\n");
-                               goto skip_power_saving;
-                       }
+                                               "enter partial_power_down failed\n");
 
-                       udelay(100);
+                               udelay(100);
+
+                               /* Ask phy to be suspended */
+#if 0
+                               if (!IS_ERR_OR_NULL(hsotg->uphy))
+                                       usb_phy_set_suspend(hsotg->uphy, true);
+#endif
+                               break;
+                       case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+                               ret = dwc2_enter_hibernation(hsotg, 0);
+                               if (ret)
+                                       dev_err(hsotg->dev,
+                                               "enter hibernation failed\n");
+                               break;
+                       case DWC2_POWER_DOWN_PARAM_NONE:
+                               /*
+                                * If neither hibernation nor partial power down are supported,
+                                * clock gating is used to save power.
+                                */
+#if 0
+                               if (!hsotg->params.no_clock_gating)
+                                       dwc2_gadget_enter_clock_gating(hsotg);
+#endif
+                               break;
+                       }
 
-                       /* Ask phy to be suspended */
-                       if (hsotg->uphy != NULL)
-                               usb_phy_set_suspend(hsotg->uphy, true);
-skip_power_saving:
                        /*
                         * Change to L2 (suspend) state before releasing
                         * spinlock
@@ -474,7 +581,7 @@ skip_power_saving:
                        hsotg->lx_state = DWC2_L2;
 
                        /* Call gadget suspend callback */
-                       call_gadget(hsotg, suspend);
+//                     call_gadget(hsotg, suspend);
                }
        } else {
                if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
@@ -491,10 +598,75 @@ skip_power_saving:
        }
 }
 
+/**
+ * dwc2_handle_lpm_intr - GINTSTS_LPMTRANRCVD Interrupt handler
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg)
+{
+       u32 glpmcfg;
+       u32 pcgcctl;
+       u32 hird;
+       u32 hird_thres;
+       u32 hird_thres_en;
+       u32 enslpm;
+
+       /* Clear interrupt */
+       dwc2_writel(hsotg, GINTSTS_LPMTRANRCVD, GINTSTS);
+
+       glpmcfg = dwc2_readl(hsotg, GLPMCFG);
+
+       if (!(glpmcfg & GLPMCFG_LPMCAP)) {
+               dev_err(hsotg->dev, "Unexpected LPM interrupt\n");
+               return;
+       }
+
+       hird = (glpmcfg & GLPMCFG_HIRD_MASK) >> GLPMCFG_HIRD_SHIFT;
+       hird_thres = (glpmcfg & GLPMCFG_HIRD_THRES_MASK &
+                       ~GLPMCFG_HIRD_THRES_EN) >> GLPMCFG_HIRD_THRES_SHIFT;
+       hird_thres_en = glpmcfg & GLPMCFG_HIRD_THRES_EN;
+       enslpm = glpmcfg & GLPMCFG_ENBLSLPM;
+
+       if (dwc2_is_device_mode(hsotg)) {
+               dev_dbg(hsotg->dev, "HIRD_THRES_EN = %d\n", hird_thres_en);
+
+               if (hird_thres_en && hird >= hird_thres) {
+                       dev_dbg(hsotg->dev, "L1 with utmi_l1_suspend_n\n");
+               } else if (enslpm) {
+                       dev_dbg(hsotg->dev, "L1 with utmi_sleep_n\n");
+               } else {
+                       dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n");
+
+                       pcgcctl = dwc2_readl(hsotg, PCGCTL);
+                       pcgcctl |= PCGCTL_ENBL_SLEEP_GATING;
+                       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               }
+               /**
+                * Examine prt_sleep_sts after TL1TokenTetry period max (10 us)
+                */
+               udelay(10);
+
+               glpmcfg = dwc2_readl(hsotg, GLPMCFG);
+
+               if (glpmcfg & GLPMCFG_SLPSTS) {
+                       /* Save the current state */
+                       hsotg->lx_state = DWC2_L1;
+                       dev_dbg(hsotg->dev,
+                               "Core is in L1 sleep glpmcfg=%08x\n", glpmcfg);
+
+                       /* Inform gadget that we are in L1 state */
+//                     call_gadget(hsotg, suspend);
+               }
+       }
+}
+
 #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT |         \
                         GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT |        \
                         GINTSTS_MODEMIS | GINTSTS_DISCONNINT |         \
-                        GINTSTS_USBSUSP | GINTSTS_PRTINT)
+                        GINTSTS_USBSUSP | GINTSTS_PRTINT |             \
+                        GINTSTS_LPMTRANRCVD)
 
 /*
  * This function returns the Core Interrupt register
@@ -506,9 +678,9 @@ STATIC u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
        u32 gahbcfg;
        u32 gintmsk_common = GINTMSK_COMMON;
 
-       gintsts = DWC2_READ_4(hsotg, GINTSTS);
-       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
-       gahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
+       gintsts = dwc2_readl(hsotg, GINTSTS);
+       gintmsk = dwc2_readl(hsotg, GINTMSK);
+       gahbcfg = dwc2_readl(hsotg, GAHBCFG);
 
        /* If any common interrupts set */
        if (gintsts & gintmsk_common)
@@ -521,6 +693,141 @@ STATIC u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
                return 0;
 }
 
+/**
+ * dwc_handle_gpwrdn_disc_det() - Handles the gpwrdn disconnect detect.
+ * Exits hibernation without restoring registers.
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ * @gpwrdn: GPWRDN register
+ */
+static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg,
+                                             u32 gpwrdn)
+{
+       u32 gpwrdn_tmp;
+
+       /* Switch-on voltage to the core */
+       gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH;
+       dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+       udelay(5);
+
+       /* Reset core */
+       gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN;
+       dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+       udelay(5);
+
+       /* Disable Power Down Clamp */
+       gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP;
+       dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+       udelay(5);
+
+       /* Deassert reset core */
+       gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn_tmp |= GPWRDN_PWRDNRSTN;
+       dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+       udelay(5);
+
+       /* Disable PMU interrupt */
+       gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
+       dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+
+       /* De-assert Wakeup Logic */
+       gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn_tmp &= ~GPWRDN_PMUACTV;
+       dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+
+       hsotg->hibernated = 0;
+       hsotg->bus_suspended = 0;
+
+       if (gpwrdn & GPWRDN_IDSTS) {
+               hsotg->op_state = OTG_STATE_B_PERIPHERAL;
+               dwc2_core_init(hsotg, false);
+               dwc2_enable_global_interrupts(hsotg);
+               dwc2_hsotg_core_init_disconnected(hsotg, false);
+               dwc2_hsotg_core_connect(hsotg);
+       } else {
+               hsotg->op_state = OTG_STATE_A_HOST;
+
+               /* Initialize the Core for Host mode */
+               dwc2_core_init(hsotg, false);
+               dwc2_enable_global_interrupts(hsotg);
+               dwc2_hcd_start(hsotg);
+       }
+}
+
+/*
+ * GPWRDN interrupt handler.
+ *
+ * The GPWRDN interrupts are those that occur in both Host and
+ * Device mode while core is in hibernated state.
+ */
+static int dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
+{
+       u32 gpwrdn;
+       int linestate;
+       int ret = 0;
+
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       /* clear all interrupt */
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT;
+       dev_dbg(hsotg->dev,
+               "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__,
+               gpwrdn);
+
+       if ((gpwrdn & GPWRDN_DISCONN_DET) &&
+           (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__);
+               /*
+                * Call disconnect detect function to exit from
+                * hibernation
+                */
+               dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn);
+       } else if ((gpwrdn & GPWRDN_LNSTSCHG) &&
+                  (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__);
+               if (hsotg->hw_params.hibernation &&
+                   hsotg->hibernated) {
+                       if (gpwrdn & GPWRDN_IDSTS) {
+                               ret = dwc2_exit_hibernation(hsotg, 0, 0, 0);
+                               if (ret)
+                                       dev_err(hsotg->dev,
+                                               "exit hibernation failed.\n");
+//                             call_gadget(hsotg, resume);
+                       } else {
+                               ret = dwc2_exit_hibernation(hsotg, 1, 0, 1);
+                               if (ret)
+                                       dev_err(hsotg->dev,
+                                               "exit hibernation failed.\n");
+                       }
+               }
+       } else if ((gpwrdn & GPWRDN_RST_DET) &&
+                  (gpwrdn & GPWRDN_RST_DET_MSK)) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__);
+               if (!linestate) {
+                       ret = dwc2_exit_hibernation(hsotg, 0, 1, 0);
+                       if (ret)
+                               dev_err(hsotg->dev,
+                                       "exit hibernation failed.\n");
+               }
+       } else if ((gpwrdn & GPWRDN_STS_CHGINT) &&
+                  (gpwrdn & GPWRDN_STS_CHGINT_MSK)) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__);
+               /*
+                * As GPWRDN_STS_CHGINT exit from hibernation flow is
+                * the same as in GPWRDN_DISCONN_DET flow. Call
+                * disconnect detect helper function to exit from
+                * hibernation.
+                */
+               dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn);
+       }
+
+       return ret;
+}
+
 /*
  * Common interrupt handler
  *
@@ -540,17 +847,32 @@ irqreturn_t dwc2_handle_common_intr(void *dev)
        u32 gintsts;
        irqreturn_t retval = IRQ_NONE;
 
+       spin_lock(&hsotg->lock);
+
        if (!dwc2_is_controller_alive(hsotg)) {
                dev_warn(hsotg->dev, "Controller is dead\n");
                goto out;
        }
 
-       MUTEX_ASSERT_LOCKED(&hsotg->lock);
+       /* Reading current frame number value in device or host modes. */
+       if (dwc2_is_device_mode(hsotg))
+               hsotg->frame_number = (dwc2_readl(hsotg, DSTS)
+                                      & DSTS_SOFFN_MASK) >> DSTS_SOFFN_SHIFT;
+       else
+               hsotg->frame_number = (dwc2_readl(hsotg, HFNUM)
+                                      & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
 
        gintsts = dwc2_read_common_intr(hsotg);
        if (gintsts & ~GINTSTS_PRTINT)
                retval = IRQ_HANDLED;
 
+       /* In case of hibernated state gintsts must not work */
+       if (hsotg->hibernated) {
+               dwc2_handle_gpwrdn_intr(hsotg);
+               retval = IRQ_HANDLED;
+               goto out;
+       }
+
        if (gintsts & GINTSTS_MODEMIS)
                dwc2_handle_mode_mismatch_intr(hsotg);
        if (gintsts & GINTSTS_OTGINT)
@@ -565,6 +887,8 @@ irqreturn_t dwc2_handle_common_intr(void *dev)
                dwc2_handle_wakeup_detected_intr(hsotg);
        if (gintsts & GINTSTS_USBSUSP)
                dwc2_handle_usb_suspend_intr(hsotg);
+       if (gintsts & GINTSTS_LPMTRANRCVD)
+               dwc2_handle_lpm_intr(hsotg);
 
        if (gintsts & GINTSTS_PRTINT) {
                /*
@@ -580,5 +904,6 @@ irqreturn_t dwc2_handle_common_intr(void *dev)
        }
 
 out:
+       spin_unlock(&hsotg->lock);
        return retval;
 }
index a58de6b..8d5a2e2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcd.c,v 1.26 2021/11/28 09:25:02 mglocker Exp $  */
+/*     $OpenBSD: dwc2_hcd.c,v 1.27 2022/09/04 08:42:40 mglocker Exp $  */
 /*     $NetBSD: dwc2_hcd.c,v 1.15 2014/11/24 10:14:14 skrll Exp $      */
 
 /*
@@ -41,7 +41,6 @@
  * This file contains the core HCD code, and implements the Linux hc_driver
  * API
  */
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <dev/usb/dwc2/dwc2_core.h>
 #include <dev/usb/dwc2/dwc2_hcd.h>
 
+/*
+ * =========================================================================
+ *  Host Core Layer Functions
+ * =========================================================================
+ */
+
+/**
+ * dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
+ * used in both device and host modes
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+STATIC void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
+{
+       u32 intmsk;
+
+       /* Clear any pending OTG Interrupts */
+       dwc2_writel(hsotg, 0xffffffff, GOTGINT);
+
+       /* Clear any pending interrupts */
+       dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+
+       /* Enable the interrupts in the GINTMSK */
+       intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT;
+
+       if (!hsotg->params.host_dma)
+               intmsk |= GINTSTS_RXFLVL;
+       if (!hsotg->params.external_id_pin_ctl)
+               intmsk |= GINTSTS_CONIDSTSCHNG;
+
+       intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
+                 GINTSTS_SESSREQINT;
+
+       if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm)
+               intmsk |= GINTSTS_LPMTRANRCVD;
+
+       dwc2_writel(hsotg, intmsk, GINTMSK);
+}
+
+STATIC int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+       u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
+
+       switch (hsotg->hw_params.arch) {
+       case GHWCFG2_EXT_DMA_ARCH:
+               dev_dbg(hsotg->dev, "External DMA Mode\n");
+               if (!sc->sc_set_dma_addr) {
+                       dev_err(hsotg->dev,
+                               "External DMA Mode not supported\n");
+                       return -EINVAL;
+               }
+               if (hsotg->params.ahbcfg != -1) {
+                       ahbcfg &= GAHBCFG_CTRL_MASK;
+                       ahbcfg |= hsotg->params.ahbcfg &
+                                 ~GAHBCFG_CTRL_MASK;
+               }
+               break;
+
+       case GHWCFG2_INT_DMA_ARCH:
+               dev_dbg(hsotg->dev, "Internal DMA Mode\n");
+               if (hsotg->params.ahbcfg != -1) {
+                       ahbcfg &= GAHBCFG_CTRL_MASK;
+                       ahbcfg |= hsotg->params.ahbcfg &
+                                 ~GAHBCFG_CTRL_MASK;
+               }
+               break;
+
+       case GHWCFG2_SLAVE_ONLY_ARCH:
+       default:
+               dev_dbg(hsotg->dev, "Slave Only Mode\n");
+               break;
+       }
+
+       if (hsotg->params.host_dma)
+               ahbcfg |= GAHBCFG_DMA_EN;
+       else
+               hsotg->params.dma_desc_enable = false;
+
+       dwc2_writel(hsotg, ahbcfg, GAHBCFG);
+
+       return 0;
+}
+
+STATIC void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
+{
+       u32 usbcfg;
+
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
+       usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP);
+
+       switch (hsotg->hw_params.op_mode) {
+       case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
+               if (hsotg->params.otg_caps.hnp_support &&
+                   hsotg->params.otg_caps.srp_support)
+                       usbcfg |= GUSBCFG_HNPCAP;
+//             fallthrough;
+
+       case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
+       case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
+       case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
+               if (hsotg->params.otg_caps.srp_support)
+                       usbcfg |= GUSBCFG_SRPCAP;
+               break;
+
+       case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE:
+       case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE:
+       case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST:
+       default:
+               break;
+       }
+
+       dwc2_writel(hsotg, usbcfg, GUSBCFG);
+}
+
+static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       if (hsotg->vbus_supply)
+               return regulator_enable(hsotg->vbus_supply);
+#endif
+
+       return 0;
+}
+
+static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       if (hsotg->vbus_supply)
+               return regulator_disable(hsotg->vbus_supply);
+#endif
+
+       return 0;
+}
+
+/**
+ * dwc2_enable_host_interrupts() - Enables the Host mode interrupts
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg)
+{
+       u32 intmsk;
+
+       dev_dbg(hsotg->dev, "%s()\n", __func__);
+
+       /* Disable all interrupts */
+       dwc2_writel(hsotg, 0, GINTMSK);
+       dwc2_writel(hsotg, 0, HAINTMSK);
+
+       /* Enable the common interrupts */
+       dwc2_enable_common_interrupts(hsotg);
+
+       /* Enable host mode interrupts without disturbing common interrupts */
+       intmsk = dwc2_readl(hsotg, GINTMSK);
+       intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT;
+       dwc2_writel(hsotg, intmsk, GINTMSK);
+}
+
+/**
+ * dwc2_disable_host_interrupts() - Disables the Host Mode interrupts
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
+{
+       u32 intmsk = dwc2_readl(hsotg, GINTMSK);
+
+       /* Disable host mode interrupts without disturbing common interrupts */
+       intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT |
+                   GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT);
+       dwc2_writel(hsotg, intmsk, GINTMSK);
+}
+
+/*
+ * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size
+ * For system that have a total fifo depth that is smaller than the default
+ * RX + TX fifo size.
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+STATIC void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *params = &hsotg->params;
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size;
+
+       total_fifo_size = hw->total_fifo_size;
+       rxfsiz = params->host_rx_fifo_size;
+       nptxfsiz = params->host_nperio_tx_fifo_size;
+       ptxfsiz = params->host_perio_tx_fifo_size;
+
+       /*
+        * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth
+        * allocation with support for high bandwidth endpoints. Synopsys
+        * defines MPS(Max Packet size) for a periodic EP=1024, and for
+        * non-periodic as 512.
+        */
+       if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) {
+               /*
+                * For Buffer DMA mode/Scatter Gather DMA mode
+                * 2 * ((Largest Packet size / 4) + 1 + 1) + n
+                * with n = number of host channel.
+                * 2 * ((1024/4) + 2) = 516
+                */
+               rxfsiz = 516 + hw->host_channels;
+
+               /*
+                * min non-periodic tx fifo depth
+                * 2 * (largest non-periodic USB packet used / 4)
+                * 2 * (512/4) = 256
+                */
+               nptxfsiz = 256;
+
+               /*
+                * min periodic tx fifo depth
+                * (largest packet size*MC)/4
+                * (1024 * 3)/4 = 768
+                */
+               ptxfsiz = 768;
+
+               params->host_rx_fifo_size = rxfsiz;
+               params->host_nperio_tx_fifo_size = nptxfsiz;
+               params->host_perio_tx_fifo_size = ptxfsiz;
+       }
+
+       /*
+        * If the summation of RX, NPTX and PTX fifo sizes is still
+        * bigger than the total_fifo_size, then we have a problem.
+        *
+        * We won't be able to allocate as many endpoints. Right now,
+        * we're just printing an error message, but ideally this FIFO
+        * allocation algorithm would be improved in the future.
+        *
+        * FIXME improve this FIFO allocation algorithm.
+        */
+       if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)))
+               dev_err(hsotg->dev, "invalid fifo sizes\n");
+}
+
+STATIC void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *params = &hsotg->params;
+       u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz;
+
+       if (!params->enable_dynamic_fifo)
+               return;
+
+       dwc2_calculate_dynamic_fifo(hsotg);
+
+       /* Rx FIFO */
+       grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
+       dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
+       grxfsiz &= ~GRXFSIZ_DEPTH_MASK;
+       grxfsiz |= params->host_rx_fifo_size <<
+                  GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK;
+       dwc2_writel(hsotg, grxfsiz, GRXFSIZ);
+       dev_dbg(hsotg->dev, "new grxfsiz=%08x\n",
+               dwc2_readl(hsotg, GRXFSIZ));
+
+       /* Non-periodic Tx FIFO */
+       dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n",
+               dwc2_readl(hsotg, GNPTXFSIZ));
+       nptxfsiz = params->host_nperio_tx_fifo_size <<
+                  FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
+       nptxfsiz |= params->host_rx_fifo_size <<
+                   FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
+       dwc2_writel(hsotg, nptxfsiz, GNPTXFSIZ);
+       dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n",
+               dwc2_readl(hsotg, GNPTXFSIZ));
+
+       /* Periodic Tx FIFO */
+       dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n",
+               dwc2_readl(hsotg, HPTXFSIZ));
+       hptxfsiz = params->host_perio_tx_fifo_size <<
+                  FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
+       hptxfsiz |= (params->host_rx_fifo_size +
+                    params->host_nperio_tx_fifo_size) <<
+                   FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
+       dwc2_writel(hsotg, hptxfsiz, HPTXFSIZ);
+       dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n",
+               dwc2_readl(hsotg, HPTXFSIZ));
+
+       if (hsotg->params.en_multiple_tx_fifo &&
+           hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_91a) {
+               /*
+                * This feature was implemented in 2.91a version
+                * Global DFIFOCFG calculation for Host mode -
+                * include RxFIFO, NPTXFIFO and HPTXFIFO
+                */
+               dfifocfg = dwc2_readl(hsotg, GDFIFOCFG);
+               dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK;
+               dfifocfg |= (params->host_rx_fifo_size +
+                            params->host_nperio_tx_fifo_size +
+                            params->host_perio_tx_fifo_size) <<
+                           GDFIFOCFG_EPINFOBASE_SHIFT &
+                           GDFIFOCFG_EPINFOBASE_MASK;
+               dwc2_writel(hsotg, dfifocfg, GDFIFOCFG);
+       }
+}
+
+/**
+ * dwc2_calc_frame_interval() - Calculates the correct frame Interval value for
+ * the HFIR register according to PHY type and speed
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ * NOTE: The caller can modify the value of the HFIR register only after the
+ * Port Enable bit of the Host Port Control and Status register (HPRT.EnaPort)
+ * has been set
+ */
+u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
+{
+       u32 usbcfg;
+       u32 hprt0;
+       int clock = 60; /* default value */
+
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
+       hprt0 = dwc2_readl(hsotg, HPRT0);
+
+       if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) &&
+           !(usbcfg & GUSBCFG_PHYIF16))
+               clock = 60;
+       if ((usbcfg & GUSBCFG_PHYSEL) && hsotg->hw_params.fs_phy_type ==
+           GHWCFG2_FS_PHY_TYPE_SHARED_ULPI)
+               clock = 48;
+       if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) &&
+           !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16))
+               clock = 30;
+       if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) &&
+           !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16))
+               clock = 60;
+       if ((usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) &&
+           !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16))
+               clock = 48;
+       if ((usbcfg & GUSBCFG_PHYSEL) && !(usbcfg & GUSBCFG_PHYIF16) &&
+           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_SHARED_UTMI)
+               clock = 48;
+       if ((usbcfg & GUSBCFG_PHYSEL) &&
+           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
+               clock = 48;
+
+       if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED)
+               /* High speed case */
+               return 125 * clock - 1;
+
+       /* FS/LS case */
+       return 1000 * clock - 1;
+}
+
+/**
+ * dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination
+ * buffer
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ * @dest:    Destination buffer for the packet
+ * @bytes:   Number of bytes to copy to the destination
+ */
+void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
+{
+       u32 *data_buf = (u32 *)dest;
+       int word_count = (bytes + 3) / 4;
+       int i;
+
+       /*
+        * Todo: Account for the case where dest is not dword aligned. This
+        * requires reading data from the FIFO into a u32 temp buffer, then
+        * moving it into the data buffer.
+        */
+
+       dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes);
+
+       for (i = 0; i < word_count; i++, data_buf++)
+               *data_buf = dwc2_readl(hsotg, HCFIFO(0));
+}
+
 /**
  * dwc2_dump_channel_info() - Prints the state of a host channel
  *
  * NOTE: This function will be removed once the peripheral controller code
  * is integrated and the driver is stable
  */
-#ifdef VERBOSE_DEBUG
 STATIC void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
                                   struct dwc2_host_chan *chan)
 {
-       int num_channels = hsotg->core_params->host_channels;
+#ifdef VERBOSE_DEBUG
+       int num_channels = hsotg->params.host_channels;
        struct dwc2_qh *qh;
        u32 hcchar;
        u32 hcsplt;
@@ -86,13 +461,13 @@ STATIC void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
        u32 hc_dma;
        int i;
 
-       if (chan == NULL)
+       if (!chan)
                return;
 
-       hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
-       hcsplt = DWC2_READ_4(hsotg, HCSPLT(chan->hc_num));
-       hctsiz = DWC2_READ_4(hsotg, HCTSIZ(chan->hc_num));
-       hc_dma = DWC2_READ_4(hsotg, HCDMA(chan->hc_num));
+       hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+       hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num));
+       hctsiz = dwc2_readl(hsotg, HCTSIZ(chan->hc_num));
+       hc_dma = dwc2_readl(hsotg, HCDMA(chan->hc_num));
 
        dev_dbg(hsotg->dev, "  Assigned to channel %p:\n", chan);
        dev_dbg(hsotg->dev, "    hcchar 0x%08x, hcsplt 0x%08x\n",
@@ -125,2087 +500,4683 @@ STATIC void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
                dev_dbg(hsotg->dev, "    %p\n", qh);
        dev_dbg(hsotg->dev, "  Channels:\n");
        for (i = 0; i < num_channels; i++) {
-               struct dwc2_host_chan *ch = hsotg->hc_ptr_array[i];
+               struct dwc2_host_chan *chan = hsotg->hc_ptr_array[i];
 
-               dev_dbg(hsotg->dev, "    %2d: %p\n", i, ch);
+               dev_dbg(hsotg->dev, "    %2d: %p\n", i, chan);
        }
-}
 #endif /* VERBOSE_DEBUG */
+}
+
+static int _dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+
+static void dwc2_host_start(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
+
+       hcd->self.is_b_host = dwc2_hcd_is_b_host(hsotg);
+#endif
+       _dwc2_hcd_start(hsotg);
+}
+
+static void dwc2_host_disconnect(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
+
+       hcd->self.is_b_host = 0;
+#endif
+}
+
+void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
+                       int *hub_addr, int *hub_port)
+{
+       struct usbd_xfer *xfer = context;
+       struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
+       struct usbd_device *dev = dpipe->pipe.device;
+
+       if (dev->myhsport->tt)
+               *hub_addr = dev->myhsport->parent->address;
+       else
+               *hub_addr = 0;
+       *hub_port = dev->myhsport->portno;
+}
 
 /*
- * Processes all the URBs in a single list of QHs. Completes them with
- * -ETIMEDOUT and frees the QTD.
- *
- * Must be called with interrupt disabled and spinlock held
+ * =========================================================================
+ *  Low Level Host Channel Access Functions
+ * =========================================================================
  */
-STATIC void dwc2_kill_urbs_in_qh_list(struct dwc2_hsotg *hsotg,
-                                     struct list_head *qh_list)
+
+STATIC void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg,
+                                     struct dwc2_host_chan *chan)
 {
-       struct dwc2_qh *qh, *qh_tmp;
-       struct dwc2_qtd *qtd, *qtd_tmp;
+       u32 hcintmsk = HCINTMSK_CHHLTD;
 
-       list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) {
-               list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
-                                        qtd_list_entry) {
-                       dwc2_host_complete(hsotg, qtd, -ECONNRESET);
-                       dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
+       switch (chan->ep_type) {
+       case USB_ENDPOINT_XFER_CONTROL:
+       case USB_ENDPOINT_XFER_BULK:
+               dev_vdbg(hsotg->dev, "control/bulk\n");
+               hcintmsk |= HCINTMSK_XFERCOMPL;
+               hcintmsk |= HCINTMSK_STALL;
+               hcintmsk |= HCINTMSK_XACTERR;
+               hcintmsk |= HCINTMSK_DATATGLERR;
+               if (chan->ep_is_in) {
+                       hcintmsk |= HCINTMSK_BBLERR;
+               } else {
+                       hcintmsk |= HCINTMSK_NAK;
+                       hcintmsk |= HCINTMSK_NYET;
+                       if (chan->do_ping)
+                               hcintmsk |= HCINTMSK_ACK;
                }
-       }
-}
 
-STATIC void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
-                             struct list_head *qh_list)
-{
-       struct dwc2_qtd *qtd, *qtd_tmp;
-       struct dwc2_qh *qh, *qh_tmp;
-       unsigned long flags;
+               if (chan->do_split) {
+                       hcintmsk |= HCINTMSK_NAK;
+                       if (chan->complete_split)
+                               hcintmsk |= HCINTMSK_NYET;
+                       else
+                               hcintmsk |= HCINTMSK_ACK;
+               }
 
-       if (!qh_list->next)
-               /* The list hasn't been initialized yet */
-               return;
+               if (chan->error_state)
+                       hcintmsk |= HCINTMSK_ACK;
+               break;
 
-       spin_lock_irqsave(&hsotg->lock, flags);
+       case USB_ENDPOINT_XFER_INT:
+               if (dbg_perio())
+                       dev_vdbg(hsotg->dev, "intr\n");
+               hcintmsk |= HCINTMSK_XFERCOMPL;
+               hcintmsk |= HCINTMSK_NAK;
+               hcintmsk |= HCINTMSK_STALL;
+               hcintmsk |= HCINTMSK_XACTERR;
+               hcintmsk |= HCINTMSK_DATATGLERR;
+               hcintmsk |= HCINTMSK_FRMOVRUN;
+
+               if (chan->ep_is_in)
+                       hcintmsk |= HCINTMSK_BBLERR;
+               if (chan->error_state)
+                       hcintmsk |= HCINTMSK_ACK;
+               if (chan->do_split) {
+                       if (chan->complete_split)
+                               hcintmsk |= HCINTMSK_NYET;
+                       else
+                               hcintmsk |= HCINTMSK_ACK;
+               }
+               break;
 
-       /* Ensure there are no QTDs or URBs left */
-       dwc2_kill_urbs_in_qh_list(hsotg, qh_list);
+       case USB_ENDPOINT_XFER_ISOC:
+               if (dbg_perio())
+                       dev_vdbg(hsotg->dev, "isoc\n");
+               hcintmsk |= HCINTMSK_XFERCOMPL;
+               hcintmsk |= HCINTMSK_FRMOVRUN;
+               hcintmsk |= HCINTMSK_ACK;
+
+               if (chan->ep_is_in) {
+                       hcintmsk |= HCINTMSK_XACTERR;
+                       hcintmsk |= HCINTMSK_BBLERR;
+               }
+               break;
+       default:
+               dev_err(hsotg->dev, "## Unknown EP type ##\n");
+               break;
+       }
 
-       list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) {
-               dwc2_hcd_qh_unlink(hsotg, qh);
+       dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
+}
 
-               /* Free each QTD in the QH's QTD list */
-               list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
-                                        qtd_list_entry)
-                       dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
+STATIC void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg,
+                                   struct dwc2_host_chan *chan)
+{
+       u32 hcintmsk = HCINTMSK_CHHLTD;
 
-               spin_unlock_irqrestore(&hsotg->lock, flags);
-               dwc2_hcd_qh_free(hsotg, qh);
-               spin_lock_irqsave(&hsotg->lock, flags);
+       /*
+        * For Descriptor DMA mode core halts the channel on AHB error.
+        * Interrupt is not required.
+        */
+       if (!hsotg->params.dma_desc_enable) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "desc DMA disabled\n");
+               hcintmsk |= HCINTMSK_AHBERR;
+       } else {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "desc DMA enabled\n");
+               if (chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+                       hcintmsk |= HCINTMSK_XFERCOMPL;
        }
 
-       spin_unlock_irqrestore(&hsotg->lock, flags);
+       if (chan->error_state && !chan->do_split &&
+           chan->ep_type != USB_ENDPOINT_XFER_ISOC) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "setting ACK\n");
+               hcintmsk |= HCINTMSK_ACK;
+               if (chan->ep_is_in) {
+                       hcintmsk |= HCINTMSK_DATATGLERR;
+                       if (chan->ep_type != USB_ENDPOINT_XFER_INT)
+                               hcintmsk |= HCINTMSK_NAK;
+               }
+       }
+
+       dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
 }
 
-/*
- * Responds with an error status of -ETIMEDOUT to all URBs in the non-periodic
- * and periodic schedules. The QTD associated with each URB is removed from
- * the schedule and freed. This function may be called when a disconnect is
- * detected or when the HCD is being stopped.
- *
- * Must be called with interrupt disabled and spinlock held
- */
-STATIC void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg)
+STATIC void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg,
+                               struct dwc2_host_chan *chan)
 {
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive);
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting);
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active);
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive);
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready);
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_assigned);
-       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_queued);
+       u32 intmsk;
+
+       if (hsotg->params.host_dma) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "DMA enabled\n");
+               dwc2_hc_enable_dma_ints(hsotg, chan);
+       } else {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "DMA disabled\n");
+               dwc2_hc_enable_slave_ints(hsotg, chan);
+       }
+
+       /* Enable the top level host channel interrupt */
+       intmsk = dwc2_readl(hsotg, HAINTMSK);
+       intmsk |= 1 << chan->hc_num;
+       dwc2_writel(hsotg, intmsk, HAINTMSK);
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk);
+
+       /* Make sure host channel interrupts are enabled */
+       intmsk = dwc2_readl(hsotg, GINTMSK);
+       intmsk |= GINTSTS_HCHINT;
+       dwc2_writel(hsotg, intmsk, GINTMSK);
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk);
 }
 
 /**
- * dwc2_hcd_start() - Starts the HCD when switching to Host mode
+ * dwc2_hc_init() - Prepares a host channel for transferring packets to/from
+ * a specific endpoint
  *
- * @hsotg: Pointer to struct dwc2_hsotg
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Information needed to initialize the host channel
+ *
+ * The HCCHARn register is set up with the characteristics specified in chan.
+ * Host channel interrupts that may need to be serviced while this transfer is
+ * in progress are enabled.
  */
-void dwc2_hcd_start(struct dwc2_hsotg *hsotg)
+void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
 {
-       u32 hprt0;
+       u8 hc_num = chan->hc_num;
+       u32 hcintmsk;
+       u32 hcchar;
+       u32 hcsplt = 0;
 
-       if (hsotg->op_state == OTG_STATE_B_HOST) {
-               /*
-                * Reset the port. During a HNP mode switch the reset
-                * needs to occur within 1ms and have a duration of at
-                * least 50ms.
-                */
-               hprt0 = dwc2_read_hprt0(hsotg);
-               hprt0 |= HPRT0_RST;
-               DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+       /* Clear old interrupt conditions for this host channel */
+       hcintmsk = 0xffffffff;
+       hcintmsk &= ~HCINTMSK_RESERVED14_31;
+       dwc2_writel(hsotg, hcintmsk, HCINT(hc_num));
+
+       /* Enable channel interrupts required for this transfer */
+       dwc2_hc_enable_ints(hsotg, chan);
+
+       /*
+        * Program the HCCHARn register with the endpoint characteristics for
+        * the current transfer
+        */
+       hcchar = chan->dev_addr << HCCHAR_DEVADDR_SHIFT & HCCHAR_DEVADDR_MASK;
+       hcchar |= chan->ep_num << HCCHAR_EPNUM_SHIFT & HCCHAR_EPNUM_MASK;
+       if (chan->ep_is_in)
+               hcchar |= HCCHAR_EPDIR;
+       if (chan->speed == USB_SPEED_LOW)
+               hcchar |= HCCHAR_LSPDDEV;
+       hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK;
+       hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK;
+       dwc2_writel(hsotg, hcchar, HCCHAR(hc_num));
+       if (dbg_hc(chan)) {
+               dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n",
+                        hc_num, hcchar);
+
+               dev_vdbg(hsotg->dev, "%s: Channel %d\n",
+                        __func__, hc_num);
+               dev_vdbg(hsotg->dev, "   Dev Addr: %d\n",
+                        chan->dev_addr);
+               dev_vdbg(hsotg->dev, "   Ep Num: %d\n",
+                        chan->ep_num);
+               dev_vdbg(hsotg->dev, "   Is In: %d\n",
+                        chan->ep_is_in);
+               dev_vdbg(hsotg->dev, "   Is Low Speed: %d\n",
+                        chan->speed == USB_SPEED_LOW);
+               dev_vdbg(hsotg->dev, "   Ep Type: %d\n",
+                        chan->ep_type);
+               dev_vdbg(hsotg->dev, "   Max Pkt: %d\n",
+                        chan->max_packet);
        }
 
-       queue_delayed_work(hsotg->wq_otg, &hsotg->start_work,
-                          msecs_to_jiffies(50));
+       /* Program the HCSPLT register for SPLITs */
+       if (chan->do_split) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev,
+                                "Programming HC %d with split --> %s\n",
+                                hc_num,
+                                chan->complete_split ? "CSPLIT" : "SSPLIT");
+               if (chan->complete_split)
+                       hcsplt |= HCSPLT_COMPSPLT;
+               hcsplt |= chan->xact_pos << HCSPLT_XACTPOS_SHIFT &
+                         HCSPLT_XACTPOS_MASK;
+               hcsplt |= chan->hub_addr << HCSPLT_HUBADDR_SHIFT &
+                         HCSPLT_HUBADDR_MASK;
+               hcsplt |= chan->hub_port << HCSPLT_PRTADDR_SHIFT &
+                         HCSPLT_PRTADDR_MASK;
+               if (dbg_hc(chan)) {
+                       dev_vdbg(hsotg->dev, "    comp split %d\n",
+                                chan->complete_split);
+                       dev_vdbg(hsotg->dev, "    xact pos %d\n",
+                                chan->xact_pos);
+                       dev_vdbg(hsotg->dev, "    hub addr %d\n",
+                                chan->hub_addr);
+                       dev_vdbg(hsotg->dev, "    hub port %d\n",
+                                chan->hub_port);
+                       dev_vdbg(hsotg->dev, "    is_in %d\n",
+                                chan->ep_is_in);
+                       dev_vdbg(hsotg->dev, "    Max Pkt %d\n",
+                                chan->max_packet);
+                       dev_vdbg(hsotg->dev, "    xferlen %d\n",
+                                chan->xfer_len);
+               }
+       }
+
+       dwc2_writel(hsotg, hcsplt, HCSPLT(hc_num));
 }
 
-/* Must be called with interrupt disabled and spinlock held */
-STATIC void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
+/**
+ * dwc2_hc_halt() - Attempts to halt a host channel
+ *
+ * @hsotg:       Controller register interface
+ * @chan:        Host channel to halt
+ * @halt_status: Reason for halting the channel
+ *
+ * This function should only be called in Slave mode or to abort a transfer in
+ * either Slave mode or DMA mode. Under normal circumstances in DMA mode, the
+ * controller halts the channel when the transfer is complete or a condition
+ * occurs that requires application intervention.
+ *
+ * In slave mode, checks for a free request queue entry, then sets the Channel
+ * Enable and Channel Disable bits of the Host Channel Characteristics
+ * register of the specified channel to intiate the halt. If there is no free
+ * request queue entry, sets only the Channel Disable bit of the HCCHARn
+ * register to flush requests for this channel. In the latter case, sets a
+ * flag to indicate that the host channel needs to be halted when a request
+ * queue slot is open.
+ *
+ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
+ * HCCHARn register. The controller ensures there is space in the request
+ * queue before submitting the halt request.
+ *
+ * Some time may elapse before the core flushes any posted requests for this
+ * host channel and halts. The Channel Halted interrupt handler completes the
+ * deactivation of the host channel.
+ */
+void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
+                 enum dwc2_halt_status halt_status)
 {
-       int num_channels = hsotg->core_params->host_channels;
-       struct dwc2_host_chan *channel;
-       u32 hcchar;
-       int i;
+       u32 nptxsts, hptxsts, hcchar;
 
-       if (hsotg->core_params->dma_enable <= 0) {
-               /* Flush out any channel requests in slave mode */
-               for (i = 0; i < num_channels; i++) {
-                       channel = hsotg->hc_ptr_array[i];
-                       if (!list_empty(&channel->hc_list_entry))
-                               continue;
-                       hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
-                       if (hcchar & HCCHAR_CHENA) {
-                               hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR);
-                               hcchar |= HCCHAR_CHDIS;
-                               DWC2_WRITE_4(hsotg, HCCHAR(i), hcchar);
-                       }
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+       /*
+        * In buffer DMA or external DMA mode channel can't be halted
+        * for non-split periodic channels. At the end of the next
+        * uframe/frame (in the worst case), the core generates a channel
+        * halted and disables the channel automatically.
+        */
+       if ((hsotg->params.g_dma && !hsotg->params.g_dma_desc) ||
+           hsotg->hw_params.arch == GHWCFG2_EXT_DMA_ARCH) {
+               if (!chan->do_split &&
+                   (chan->ep_type == USB_ENDPOINT_XFER_ISOC ||
+                    chan->ep_type == USB_ENDPOINT_XFER_INT)) {
+                       dev_err(hsotg->dev, "%s() Channel can't be halted\n",
+                               __func__);
+                       return;
                }
        }
 
-       for (i = 0; i < num_channels; i++) {
-               channel = hsotg->hc_ptr_array[i];
-               if (!list_empty(&channel->hc_list_entry))
-                       continue;
-               hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
-               if (hcchar & HCCHAR_CHENA) {
-                       /* Halt the channel */
-                       hcchar |= HCCHAR_CHDIS;
-                       DWC2_WRITE_4(hsotg, HCCHAR(i), hcchar);
-               }
+       if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS)
+               dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status);
 
-               dwc2_hc_cleanup(hsotg, channel);
-               list_add_tail(&channel->hc_list_entry, &hsotg->free_hc_list);
+       if (halt_status == DWC2_HC_XFER_URB_DEQUEUE ||
+           halt_status == DWC2_HC_XFER_AHB_ERR) {
                /*
-                * Added for Descriptor DMA to prevent channel double cleanup in
-                * release_channel_ddma(), which is called from ep_disable when
-                * device disconnects
+                * Disable all channel interrupts except Ch Halted. The QTD
+                * and QH state associated with this transfer has been cleared
+                * (in the case of URB_DEQUEUE), so the channel needs to be
+                * shut down carefully to prevent crashes.
                 */
-               channel->qh = NULL;
+               u32 hcintmsk = HCINTMSK_CHHLTD;
+
+               dev_vdbg(hsotg->dev, "dequeue/error\n");
+               dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
+
+               /*
+                * Make sure no other interrupts besides halt are currently
+                * pending. Handling another interrupt could cause a crash due
+                * to the QTD and QH state.
+                */
+               dwc2_writel(hsotg, ~hcintmsk, HCINT(chan->hc_num));
+
+               /*
+                * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
+                * even if the channel was already halted for some other
+                * reason
+                */
+               chan->halt_status = halt_status;
+
+               hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+               if (!(hcchar & HCCHAR_CHENA)) {
+                       /*
+                        * The channel is either already halted or it hasn't
+                        * started yet. In DMA mode, the transfer may halt if
+                        * it finishes normally or a condition occurs that
+                        * requires driver intervention. Don't want to halt
+                        * the channel again. In either Slave or DMA mode,
+                        * it's possible that the transfer has been assigned
+                        * to a channel, but not started yet when an URB is
+                        * dequeued. Don't want to halt a channel that hasn't
+                        * started yet.
+                        */
+                       return;
+               }
        }
-       /* All channels have been freed, mark them available */
-       if (hsotg->core_params->uframe_sched > 0) {
-               hsotg->available_host_channels =
-                       hsotg->core_params->host_channels;
+       if (chan->halt_pending) {
+               /*
+                * A halt has already been issued for this channel. This might
+                * happen when a transfer is aborted by a higher level in
+                * the stack.
+                */
+               dev_vdbg(hsotg->dev,
+                        "*** %s: Channel %d, chan->halt_pending already set ***\n",
+                        __func__, chan->hc_num);
+               return;
+       }
+
+       hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+
+       /* No need to set the bit in DDMA for disabling the channel */
+       /* TODO check it everywhere channel is disabled */
+       if (!hsotg->params.dma_desc_enable) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "desc DMA disabled\n");
+               hcchar |= HCCHAR_CHENA;
        } else {
-               hsotg->non_periodic_channels = 0;
-               hsotg->periodic_channels = 0;
+               if (dbg_hc(chan))
+                       dev_dbg(hsotg->dev, "desc DMA enabled\n");
+       }
+       hcchar |= HCCHAR_CHDIS;
+
+       if (!hsotg->params.host_dma) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "DMA not enabled\n");
+               hcchar |= HCCHAR_CHENA;
+
+               /* Check for space in the request queue to issue the halt */
+               if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
+                   chan->ep_type == USB_ENDPOINT_XFER_BULK) {
+                       dev_vdbg(hsotg->dev, "control/bulk\n");
+                       nptxsts = dwc2_readl(hsotg, GNPTXSTS);
+                       if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) {
+                               dev_vdbg(hsotg->dev, "Disabling channel\n");
+                               hcchar &= ~HCCHAR_CHENA;
+                       }
+               } else {
+                       if (dbg_perio())
+                               dev_vdbg(hsotg->dev, "isoc/intr\n");
+                       hptxsts = dwc2_readl(hsotg, HPTXSTS);
+                       if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 ||
+                           hsotg->queuing_high_bandwidth) {
+                               if (dbg_perio())
+                                       dev_vdbg(hsotg->dev, "Disabling channel\n");
+                               hcchar &= ~HCCHAR_CHENA;
+                       }
+               }
+       } else {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "DMA enabled\n");
+       }
+
+       dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
+       chan->halt_status = halt_status;
+
+       if (hcchar & HCCHAR_CHENA) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "Channel enabled\n");
+               chan->halt_pending = 1;
+               chan->halt_on_queue = 0;
+       } else {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "Channel disabled\n");
+               chan->halt_on_queue = 1;
+       }
+
+       if (dbg_hc(chan)) {
+               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
+                        chan->hc_num);
+               dev_vdbg(hsotg->dev, "   hcchar: 0x%08x\n",
+                        hcchar);
+               dev_vdbg(hsotg->dev, "   halt_pending: %d\n",
+                        chan->halt_pending);
+               dev_vdbg(hsotg->dev, "   halt_on_queue: %d\n",
+                        chan->halt_on_queue);
+               dev_vdbg(hsotg->dev, "   halt_status: %d\n",
+                        chan->halt_status);
        }
 }
 
 /**
- * dwc2_hcd_connect() - Handles connect of the HCD
+ * dwc2_hc_cleanup() - Clears the transfer state for a host channel
  *
- * @hsotg: Pointer to struct dwc2_hsotg
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Identifies the host channel to clean up
  *
- * Must be called with interrupt disabled and spinlock held
+ * This function is normally called after a transfer is done and the host
+ * channel is being released
  */
-void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
+void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
 {
-       if (hsotg->lx_state != DWC2_L0)
-               usb_hcd_resume_root_hub(hsotg->priv);
+       u32 hcintmsk;
 
-       hsotg->flags.b.port_connect_status_change = 1;
-       hsotg->flags.b.port_connect_status = 1;
+       chan->xfer_started = 0;
+
+       list_del_init(&chan->split_order_list_entry);
+
+       /*
+        * Clear channel interrupt enables and any unhandled channel interrupt
+        * conditions
+        */
+       dwc2_writel(hsotg, 0, HCINTMSK(chan->hc_num));
+       hcintmsk = 0xffffffff;
+       hcintmsk &= ~HCINTMSK_RESERVED14_31;
+       dwc2_writel(hsotg, hcintmsk, HCINT(chan->hc_num));
 }
 
 /**
- * dwc2_hcd_disconnect() - Handles disconnect of the HCD
+ * dwc2_hc_set_even_odd_frame() - Sets the channel property that indicates in
+ * which frame a periodic transfer should occur
  *
- * @hsotg: Pointer to struct dwc2_hsotg
- * @force: If true, we won't try to reconnect even if we see device connected.
+ * @hsotg:  Programming view of DWC_otg controller
+ * @chan:   Identifies the host channel to set up and its properties
+ * @hcchar: Current value of the HCCHAR register for the specified host channel
  *
- * Must be called with interrupt disabled and spinlock held
+ * This function has no effect on non-periodic transfers
  */
-void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
+STATIC void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
+                                      struct dwc2_host_chan *chan, u32 *hcchar)
 {
-       u32 intr;
-       u32 hprt0;
+       if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+           chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
+               int host_speed;
+               int xfer_ns;
+               int xfer_us;
+               int bytes_in_fifo;
+               u16 fifo_space;
+               u16 frame_number;
+               u16 wire_frame;
 
-       /* Set status flags for the hub driver */
-       hsotg->flags.b.port_connect_status_change = 1;
-       hsotg->flags.b.port_connect_status = 0;
+               /*
+                * Try to figure out if we're an even or odd frame. If we set
+                * even and the current frame number is even the the transfer
+                * will happen immediately.  Similar if both are odd. If one is
+                * even and the other is odd then the transfer will happen when
+                * the frame number ticks.
+                *
+                * There's a bit of a balancing act to get this right.
+                * Sometimes we may want to send data in the current frame (AK
+                * right away).  We might want to do this if the frame number
+                * _just_ ticked, but we might also want to do this in order
+                * to continue a split transaction that happened late in a
+                * microframe (so we didn't know to queue the next transfer
+                * until the frame number had ticked).  The problem is that we
+                * need a lot of knowledge to know if there's actually still
+                * time to send things or if it would be better to wait until
+                * the next frame.
+                *
+                * We can look at how much time is left in the current frame
+                * and make a guess about whether we'll have time to transfer.
+                * We'll do that.
+                */
 
-       /*
-        * Shutdown any transfers in process by clearing the Tx FIFO Empty
-        * interrupt mask and status bits and disabling subsequent host
-        * channel interrupts.
-        */
-       intr = DWC2_READ_4(hsotg, GINTMSK);
-       intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT);
-       DWC2_WRITE_4(hsotg, GINTMSK, intr);
-       intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT;
-       DWC2_WRITE_4(hsotg, GINTSTS, intr);
+               /* Get speed host is running at */
+               host_speed = (chan->speed != USB_SPEED_HIGH &&
+                             !chan->do_split) ? chan->speed : USB_SPEED_HIGH;
 
-       /*
-        * Turn off the vbus power only if the core has transitioned to device
-        * mode. If still in host mode, need to keep power on to detect a
-        * reconnection.
-        */
-       if (dwc2_is_device_mode(hsotg)) {
-               if (hsotg->op_state != OTG_STATE_A_SUSPEND) {
-                       dev_dbg(hsotg->dev, "Disconnect: PortPower off\n");
-                       DWC2_WRITE_4(hsotg, HPRT0, 0);
-               }
+               /* See how many bytes are in the periodic FIFO right now */
+               fifo_space = (dwc2_readl(hsotg, HPTXSTS) &
+                             TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
+               bytes_in_fifo = sizeof(u32) *
+                               (hsotg->params.host_perio_tx_fifo_size -
+                                fifo_space);
 
-               dwc2_disable_host_interrupts(hsotg);
-       }
+               /*
+                * Roughly estimate bus time for everything in the periodic
+                * queue + our new transfer.  This is "rough" because we're
+                * using a function that makes takes into account IN/OUT
+                * and INT/ISO and we're just slamming in one value for all
+                * transfers.  This should be an over-estimate and that should
+                * be OK, but we can probably tighten it.
+                */
+               xfer_ns = dwc2_usb_calc_bus_time(host_speed, false, false,
+                                           chan->xfer_len + bytes_in_fifo);
+               xfer_us = NS_TO_US(xfer_ns);
 
-       /* Respond with an error status to all URBs in the schedule */
-       dwc2_kill_all_urbs(hsotg);
+               /* See what frame number we'll be at by the time we finish */
+               frame_number = dwc2_hcd_get_future_frame_number(hsotg, xfer_us);
 
-       if (dwc2_is_host_mode(hsotg))
-               /* Clean up any host channels that were in use */
-               dwc2_hcd_cleanup_channels(hsotg);
+               /* This is when we were scheduled to be on the wire */
+               wire_frame = dwc2_frame_num_inc(chan->qh->next_active_frame, 1);
 
-       dwc2_host_disconnect(hsotg);
+               /*
+                * If we'd finish _after_ the frame we're scheduled in then
+                * it's hopeless.  Just schedule right away and hope for the
+                * best.  Note that it _might_ be wise to call back into the
+                * scheduler to pick a better frame, but this is better than
+                * nothing.
+                */
+               if (dwc2_frame_num_gt(frame_number, wire_frame)) {
+                       dwc2_sch_vdbg(hsotg,
+                                     "QH=%p EO MISS fr=%04x=>%04x (%+d)\n",
+                                     chan->qh, wire_frame, frame_number,
+                                     dwc2_frame_num_dec(frame_number,
+                                                        wire_frame));
+                       wire_frame = frame_number;
 
-       dwc2_root_intr(hsotg->hsotg_sc);
+                       /*
+                        * We picked a different frame number; communicate this
+                        * back to the scheduler so it doesn't try to schedule
+                        * another in the same frame.
+                        *
+                        * Remember that next_active_frame is 1 before the wire
+                        * frame.
+                        */
+                       chan->qh->next_active_frame =
+                               dwc2_frame_num_dec(frame_number, 1);
+               }
 
-       /*
-        * Add an extra check here to see if we're actually connected but
-        * we don't have a detection interrupt pending.  This can happen if:
-        *   1. hardware sees connect
-        *   2. hardware sees disconnect
-        *   3. hardware sees connect
-        *   4. dwc2_port_intr() - clears connect interrupt
-        *   5. dwc2_handle_common_intr() - calls here
-        *
-        * Without the extra check here we will end calling disconnect
-        * and won't get any future interrupts to handle the connect.
-        */
-       if (!force) {
-               hprt0 = DWC2_READ_4(hsotg, HPRT0);
-               if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
-                       dwc2_hcd_connect(hsotg);
+               if (wire_frame & 1)
+                       *hcchar |= HCCHAR_ODDFRM;
+               else
+                       *hcchar &= ~HCCHAR_ODDFRM;
+       }
+}
+
+STATIC void dwc2_set_pid_isoc(struct dwc2_host_chan *chan)
+{
+       /* Set up the initial PID for the transfer */
+       if (chan->speed == USB_SPEED_HIGH) {
+               if (chan->ep_is_in) {
+                       if (chan->multi_count == 1)
+                               chan->data_pid_start = DWC2_HC_PID_DATA0;
+                       else if (chan->multi_count == 2)
+                               chan->data_pid_start = DWC2_HC_PID_DATA1;
+                       else
+                               chan->data_pid_start = DWC2_HC_PID_DATA2;
+               } else {
+                       if (chan->multi_count == 1)
+                               chan->data_pid_start = DWC2_HC_PID_DATA0;
+                       else
+                               chan->data_pid_start = DWC2_HC_PID_MDATA;
+               }
+       } else {
+               chan->data_pid_start = DWC2_HC_PID_DATA0;
        }
 }
 
 /**
- * dwc2_hcd_rem_wakeup() - Handles Remote Wakeup
+ * dwc2_hc_write_packet() - Writes a packet into the Tx FIFO associated with
+ * the Host Channel
  *
- * @hsotg: Pointer to struct dwc2_hsotg
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Information needed to initialize the host channel
+ *
+ * This function should only be called in Slave mode. For a channel associated
+ * with a non-periodic EP, the non-periodic Tx FIFO is written. For a channel
+ * associated with a periodic EP, the periodic Tx FIFO is written.
+ *
+ * Upon return the xfer_buf and xfer_count fields in chan are incremented by
+ * the number of bytes written to the Tx FIFO.
  */
-STATIC void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
+STATIC void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
+                                struct dwc2_host_chan *chan)
 {
-       if (hsotg->bus_suspended) {
-               hsotg->flags.b.port_suspend_change = 1;
-               usb_hcd_resume_root_hub(hsotg->priv);
-       }
+       u32 i;
+       u32 remaining_count;
+       u32 byte_count;
+       u32 dword_count;
+       u32 *data_buf = (u32 *)chan->xfer_buf;
+
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+       remaining_count = chan->xfer_len - chan->xfer_count;
+       if (remaining_count > chan->max_packet)
+               byte_count = chan->max_packet;
+       else
+               byte_count = remaining_count;
 
-       if (hsotg->lx_state == DWC2_L1)
-               hsotg->flags.b.port_l1_change = 1;
+       dword_count = (byte_count + 3) / 4;
+
+       if (((unsigned long)data_buf & 0x3) == 0) {
+               /* xfer_buf is DWORD aligned */
+               for (i = 0; i < dword_count; i++, data_buf++)
+                       dwc2_writel(hsotg, *data_buf, HCFIFO(chan->hc_num));
+       } else {
+               /* xfer_buf is not DWORD aligned */
+               for (i = 0; i < dword_count; i++, data_buf++) {
+                       u32 data = data_buf[0] | data_buf[1] << 8 |
+                                  data_buf[2] << 16 | data_buf[3] << 24;
+                       dwc2_writel(hsotg, data, HCFIFO(chan->hc_num));
+               }
+       }
 
-       dwc2_root_intr(hsotg->hsotg_sc);
+       chan->xfer_count += byte_count;
+       chan->xfer_buf += byte_count;
 }
 
 /**
- * dwc2_hcd_stop() - Halts the DWC_otg host mode operations in a clean manner
+ * dwc2_hc_do_ping() - Starts a PING transfer
  *
- * @hsotg: Pointer to struct dwc2_hsotg
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Information needed to initialize the host channel
  *
- * Must be called with interrupt disabled and spinlock held
+ * This function should only be called in Slave mode. The Do Ping bit is set in
+ * the HCTSIZ register, then the channel is enabled.
  */
-void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
+STATIC void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
+                           struct dwc2_host_chan *chan)
 {
-       dev_dbg(hsotg->dev, "DWC OTG HCD STOP\n");
+       u32 hcchar;
+       u32 hctsiz;
 
-       /*
-        * The root hub should be disconnected before this function is called.
-        * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
-        * and the QH lists (via ..._hcd_endpoint_disable).
-        */
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
+                        chan->hc_num);
 
-       /* Turn off all host-specific interrupts */
-       dwc2_disable_host_interrupts(hsotg);
+       hctsiz = TSIZ_DOPNG;
+       hctsiz |= 1 << TSIZ_PKTCNT_SHIFT;
+       dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
 
-       /* Turn off the vbus power */
-       dev_dbg(hsotg->dev, "PortPower off\n");
-       DWC2_WRITE_4(hsotg, HPRT0, 0);
+       hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+       hcchar |= HCCHAR_CHENA;
+       hcchar &= ~HCCHAR_CHDIS;
+       dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
 }
 
-/* Caller must hold driver lock */
-int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
-                               struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
-                               struct dwc2_qtd *qtd)
+/**
+ * dwc2_hc_start_transfer() - Does the setup for a data transfer for a host
+ * channel and starts the transfer
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Information needed to initialize the host channel. The xfer_len value
+ *         may be reduced to accommodate the max widths of the XferSize and
+ *         PktCnt fields in the HCTSIZn register. The multi_count value may be
+ *         changed to reflect the final xfer_len value.
+ *
+ * This function may be called in either Slave mode or DMA mode. In Slave mode,
+ * the caller must ensure that there is sufficient space in the request queue
+ * and Tx Data FIFO.
+ *
+ * For an OUT transfer in Slave mode, it loads a data packet into the
+ * appropriate FIFO. If necessary, additional data packets are loaded in the
+ * Host ISR.
+ *
+ * For an IN transfer in Slave mode, a data packet is requested. The data
+ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
+ * additional data packets are requested in the Host ISR.
+ *
+ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
+ * register along with a packet count of 1 and the channel is enabled. This
+ * causes a single PING transaction to occur. Other fields in HCTSIZ are
+ * simply set to 0 since no data transfer occurs in this case.
+ *
+ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
+ * all the information required to perform the subsequent data transfer. In
+ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
+ * controller performs the entire PING protocol, then starts the data
+ * transfer.
+ */
+STATIC void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
+                                  struct dwc2_host_chan *chan)
 {
-       u32 intr_mask;
-       int retval;
-       int dev_speed;
+       u32 max_hc_xfer_size = hsotg->params.max_transfer_size;
+       u16 max_hc_pkt_count = hsotg->params.max_packet_count;
+       u32 hcchar;
+       u32 hctsiz = 0;
+       u16 num_packets = 0;
+       u32 ec_mc = 0;
+
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+       if (chan->do_ping) {
+               if (!hsotg->params.host_dma) {
+                       if (dbg_hc(chan))
+                               dev_vdbg(hsotg->dev, "ping, no DMA\n");
+                       dwc2_hc_do_ping(hsotg, chan);
+                       chan->xfer_started = 1;
+                       return;
+               }
 
-       if (!hsotg->flags.b.port_connect_status) {
-               /* No longer connected */
-               dev_err(hsotg->dev, "Not connected\n");
-               return -ENODEV;
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "ping, DMA\n");
+
+               hctsiz |= TSIZ_DOPNG;
        }
 
-       dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+       if (chan->do_split) {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "split\n");
+               num_packets = 1;
 
-       /* Some configurations cannot support LS traffic on a FS root port */
-       if ((dev_speed == USB_SPEED_LOW) &&
-           (hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) &&
-           (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) {
-               u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);
-               u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+               if (chan->complete_split && !chan->ep_is_in)
+                       /*
+                        * For CSPLIT OUT Transfer, set the size to 0 so the
+                        * core doesn't expect any data written to the FIFO
+                        */
+                       chan->xfer_len = 0;
+               else if (chan->ep_is_in || chan->xfer_len > chan->max_packet)
+                       chan->xfer_len = chan->max_packet;
+               else if (!chan->ep_is_in && chan->xfer_len > 188)
+                       chan->xfer_len = 188;
+
+               hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
+                         TSIZ_XFERSIZE_MASK;
+
+               /* For split set ec_mc for immediate retries */
+               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+                   chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+                       ec_mc = 3;
+               else
+                       ec_mc = 1;
+       } else {
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "no split\n");
+               /*
+                * Ensure that the transfer length and packet count will fit
+                * in the widths allocated for them in the HCTSIZn register
+                */
+               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+                   chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
+                       /*
+                        * Make sure the transfer size is no larger than one
+                        * (micro)frame's worth of data. (A check was done
+                        * when the periodic transfer was accepted to ensure
+                        * that a (micro)frame's worth of data can be
+                        * programmed into a channel.)
+                        */
+                       u32 max_periodic_len =
+                               chan->multi_count * chan->max_packet;
 
-               if (prtspd == HPRT0_SPD_FULL_SPEED) {
-                       dev_err(hsotg->dev,
-                               "DWC OTG HCD URB Enqueue unsupported\n");
-                       return -ENODEV;
+                       if (chan->xfer_len > max_periodic_len)
+                               chan->xfer_len = max_periodic_len;
+               } else if (chan->xfer_len > max_hc_xfer_size) {
+                       /*
+                        * Make sure that xfer_len is a multiple of max packet
+                        * size
+                        */
+                       chan->xfer_len =
+                               max_hc_xfer_size - chan->max_packet + 1;
                }
-       }
 
-       if (!qtd)
-               return -EINVAL;
+               if (chan->xfer_len > 0) {
+                       num_packets = (chan->xfer_len + chan->max_packet - 1) /
+                                       chan->max_packet;
+                       if (num_packets > max_hc_pkt_count) {
+                               num_packets = max_hc_pkt_count;
+                               chan->xfer_len = num_packets * chan->max_packet;
+                       } else if (chan->ep_is_in) {
+                               /*
+                                * Always program an integral # of max packets
+                                * for IN transfers.
+                                * Note: This assumes that the input buffer is
+                                * aligned and sized accordingly.
+                                */
+                               chan->xfer_len = num_packets * chan->max_packet;
+                       }
+               } else {
+                       /* Need 1 packet for transfer length of 0 */
+                       num_packets = 1;
+               }
 
-       memset(qtd, 0, sizeof(*qtd));
+               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+                   chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+                       /*
+                        * Make sure that the multi_count field matches the
+                        * actual transfer length
+                        */
+                       chan->multi_count = num_packets;
 
-       dwc2_hcd_qtd_init(qtd, urb);
-       retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);
-       if (retval) {
-               dev_err(hsotg->dev,
-                       "DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n",
-                       retval);
-               return retval;
-       }
+               if (chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+                       dwc2_set_pid_isoc(chan);
 
-       intr_mask = DWC2_READ_4(hsotg, GINTMSK);
-       if (!(intr_mask & GINTSTS_SOF)) {
-               enum dwc2_transaction_type tr_type;
-
-               if (qtd->qh->ep_type == USB_ENDPOINT_XFER_BULK &&
-                   !(qtd->urb->flags & URB_GIVEBACK_ASAP))
-                       /*
-                        * Do not schedule SG transactions until qtd has
-                        * URB_GIVEBACK_ASAP set
-                        */
-                       return 0;
+               hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
+                         TSIZ_XFERSIZE_MASK;
 
-               tr_type = dwc2_hcd_select_transactions(hsotg);
-               if (tr_type != DWC2_TRANSACTION_NONE)
-                       dwc2_hcd_queue_transactions(hsotg, tr_type);
+               /* The ec_mc gets the multi_count for non-split */
+               ec_mc = chan->multi_count;
        }
 
-       return 0;
-}
-
-/* Must be called with interrupt disabled and spinlock held */
-int
-dwc2_hcd_urb_dequeue(struct dwc2_hsotg *hsotg,
-                               struct dwc2_hcd_urb *urb)
-{
-       struct dwc2_qh *qh;
-       struct dwc2_qtd *urb_qtd;
-
-       urb_qtd = urb->qtd;
-       if (!urb_qtd) {
-               dev_dbg(hsotg->dev, "## Urb QTD is NULL ##\n");
-               return -EINVAL;
+       chan->start_pkt_count = num_packets;
+       hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK;
+       hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT &
+                 TSIZ_SC_MC_PID_MASK;
+       dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
+       if (dbg_hc(chan)) {
+               dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n",
+                        hctsiz, chan->hc_num);
+
+               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
+                        chan->hc_num);
+               dev_vdbg(hsotg->dev, "   Xfer Size: %d\n",
+                        (hctsiz & TSIZ_XFERSIZE_MASK) >>
+                        TSIZ_XFERSIZE_SHIFT);
+               dev_vdbg(hsotg->dev, "   Num Pkts: %d\n",
+                        (hctsiz & TSIZ_PKTCNT_MASK) >>
+                        TSIZ_PKTCNT_SHIFT);
+               dev_vdbg(hsotg->dev, "   Start PID: %d\n",
+                        (hctsiz & TSIZ_SC_MC_PID_MASK) >>
+                        TSIZ_SC_MC_PID_SHIFT);
        }
 
-       qh = urb_qtd->qh;
-       if (!qh) {
-               dev_dbg(hsotg->dev, "## Urb QTD QH is NULL ##\n");
-               return -EINVAL;
-       }
+       if (hsotg->params.host_dma) {
+               dma_addr_t dma_addr;
 
-       urb->priv = NULL;
+               if (chan->align_buf) {
+                       if (dbg_hc(chan))
+                               dev_vdbg(hsotg->dev, "align_buf\n");
+                       dma_addr = chan->align_buf;
+               } else {
+                       dma_addr = chan->xfer_dma;
+               }
 
-       if (urb_qtd->in_process && qh->channel) {
-#ifdef VERBOSE_DEBUG
-               dwc2_dump_channel_info(hsotg, qh->channel);
-#endif
-               /* The QTD is in process (it has been assigned to a channel) */
-               if (hsotg->flags.b.port_connect_status)
-                       /*
-                        * If still connected (i.e. in host mode), halt the
-                        * channel so it can be used for other transfers. If
-                        * no longer connected, the host registers can't be
-                        * written to halt the channel since the core is in
-                        * device mode.
-                        */
-                       dwc2_hc_halt(hsotg, qh->channel,
-                                    DWC2_HC_XFER_URB_DEQUEUE);
+               if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
+                       dwc2_writel(hsotg, (u32)dma_addr, HCDMA(chan->hc_num));
+
+                       if (dbg_hc(chan))
+                               dev_vdbg(hsotg->dev,
+                                        "Wrote %08lx to HCDMA(%d)\n",
+                                        (unsigned long)dma_addr, chan->hc_num);
+               } else {
+                       (void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
+                           hsotg->dev, dma_addr, chan->hc_num);
+               }
        }
 
-       /*
-        * Free the QTD and clean up the associated QH. Leave the QH in the
-        * schedule if it has any remaining QTDs.
-        */
-       if (hsotg->core_params->dma_desc_enable <= 0) {
-               u8 in_process = urb_qtd->in_process;
+       /* Start the split */
+       if (chan->do_split) {
+               u32 hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num));
 
-               dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh);
-               if (in_process) {
-                       dwc2_hcd_qh_deactivate(hsotg, qh, 0);
-                       qh->channel = NULL;
-               } else if (list_empty(&qh->qtd_list)) {
-                       dwc2_hcd_qh_unlink(hsotg, qh);
-               }
-       } else {
-               dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh);
+               hcsplt |= HCSPLT_SPLTENA;
+               dwc2_writel(hsotg, hcsplt, HCSPLT(chan->hc_num));
        }
 
-       return 0;
+       hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+       hcchar &= ~HCCHAR_MULTICNT_MASK;
+       hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
+       dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
+
+       if (hcchar & HCCHAR_CHDIS)
+               dev_warn(hsotg->dev,
+                        "%s: chdis set, channel %d, hcchar 0x%08x\n",
+                        __func__, chan->hc_num, hcchar);
+
+       /* Set host channel enable after all other setup is complete */
+       hcchar |= HCCHAR_CHENA;
+       hcchar &= ~HCCHAR_CHDIS;
+
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "   Multi Cnt: %d\n",
+                        (hcchar & HCCHAR_MULTICNT_MASK) >>
+                        HCCHAR_MULTICNT_SHIFT);
+
+       dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
+                        chan->hc_num);
+
+       chan->xfer_started = 1;
+       chan->requests++;
+
+       if (!hsotg->params.host_dma &&
+           !chan->ep_is_in && chan->xfer_len > 0)
+               /* Load OUT packet into the appropriate Tx FIFO */
+               dwc2_hc_write_packet(hsotg, chan);
 }
 
-
-/*
- * Initializes dynamic portions of the DWC_otg HCD state
+/**
+ * dwc2_hc_start_transfer_ddma() - Does the setup for a data transfer for a
+ * host channel and starts the transfer in Descriptor DMA mode
  *
- * Must be called with interrupt disabled and spinlock held
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Information needed to initialize the host channel
+ *
+ * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set.
+ * Sets PID and NTD values. For periodic transfers initializes SCHED_INFO field
+ * with micro-frame bitmap.
+ *
+ * Initializes HCDMA register with descriptor list address and CTD value then
+ * starts the transfer via enabling the channel.
  */
-void
-dwc2_hcd_reinit(struct dwc2_hsotg *hsotg)
+void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
+                                struct dwc2_host_chan *chan)
 {
-       struct dwc2_host_chan *chan, *chan_tmp;
-       int num_channels;
-       int i;
+       u32 hcchar;
+       u32 hctsiz = 0;
 
-       hsotg->flags.d32 = 0;
-       hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active;
+       if (chan->do_ping)
+               hctsiz |= TSIZ_DOPNG;
 
-       if (hsotg->core_params->uframe_sched > 0) {
-               hsotg->available_host_channels =
-                       hsotg->core_params->host_channels;
-       } else {
-               hsotg->non_periodic_channels = 0;
-               hsotg->periodic_channels = 0;
-       }
+       if (chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+               dwc2_set_pid_isoc(chan);
 
-       /*
-        * Put all channels in the free channel list and clean up channel
-        * states
-        */
-       list_for_each_entry_safe(chan, chan_tmp, &hsotg->free_hc_list,
-                                hc_list_entry)
-               list_del_init(&chan->hc_list_entry);
+       /* Packet Count and Xfer Size are not used in Descriptor DMA mode */
+       hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT &
+                 TSIZ_SC_MC_PID_MASK;
 
-       num_channels = hsotg->core_params->host_channels;
-       for (i = 0; i < num_channels; i++) {
-               chan = hsotg->hc_ptr_array[i];
-               list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
-               dwc2_hc_cleanup(hsotg, chan);
-       }
+       /* 0 - 1 descriptor, 1 - 2 descriptors, etc */
+       hctsiz |= (chan->ntd - 1) << TSIZ_NTD_SHIFT & TSIZ_NTD_MASK;
 
-       /* Initialize the DWC core for host mode operation */
-       dwc2_core_host_init(hsotg);
-}
+       /* Non-zero only for high-speed interrupt endpoints */
+       hctsiz |= chan->schinfo << TSIZ_SCHINFO_SHIFT & TSIZ_SCHINFO_MASK;
 
-STATIC void dwc2_hc_init_split(struct dwc2_hsotg *hsotg,
-                              struct dwc2_host_chan *chan,
-                              struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
-{
-       int hub_addr, hub_port;
+       if (dbg_hc(chan)) {
+               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
+                        chan->hc_num);
+               dev_vdbg(hsotg->dev, "   Start PID: %d\n",
+                        chan->data_pid_start);
+               dev_vdbg(hsotg->dev, "   NTD: %d\n", chan->ntd - 1);
+       }
 
-       chan->do_split = 1;
-       chan->xact_pos = qtd->isoc_split_pos;
-       chan->complete_split = qtd->complete_split;
-       dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
-       chan->hub_addr = (u8)hub_addr;
-       chan->hub_port = (u8)hub_port;
-}
+       dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
 
-STATIC void *dwc2_hc_init_xfer_data(struct dwc2_hsotg *hsotg,
-                              struct dwc2_host_chan *chan,
-                              struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
-{
-       if (hsotg->core_params->dma_enable > 0) {
-               chan->xfer_dma = DMAADDR(urb->usbdma, urb->actual_length);
+       usb_syncmem(&chan->desc_list_usbdma, 0, chan->desc_list_sz,
+           BUS_DMASYNC_PREWRITE);
 
-               /* For non-dword aligned case */
-               if (hsotg->core_params->dma_desc_enable <= 0 &&
-                   (chan->xfer_dma & 0x3))
-                       return (u8 *)urb->buf + urb->actual_length;
+       if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
+               dwc2_writel(hsotg, chan->desc_list_addr, HCDMA(chan->hc_num));
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
+                                &chan->desc_list_addr, chan->hc_num);
        } else {
-               chan->xfer_buf = (u8 *)urb->buf + urb->actual_length;
+               (void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
+                   hsotg->dev, chan->desc_list_addr, chan->hc_num);
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "Wrote %pad to ext dma(%d)\n",
+                                &chan->desc_list_addr, chan->hc_num);
        }
 
-       return NULL;
-}
-
-STATIC void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
-                              struct dwc2_host_chan *chan,
-                              struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
-{
-       struct dwc2_hcd_iso_packet_desc *frame_desc;
-       void *bufptr = NULL;
+       hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+       hcchar &= ~HCCHAR_MULTICNT_MASK;
+       hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
+                 HCCHAR_MULTICNT_MASK;
 
-       switch (dwc2_hcd_get_pipe_type(&urb->pipe_info)) {
-       case USB_ENDPOINT_XFER_CONTROL:
-               chan->ep_type = USB_ENDPOINT_XFER_CONTROL;
+       if (hcchar & HCCHAR_CHDIS)
+               dev_warn(hsotg->dev,
+                        "%s: chdis set, channel %d, hcchar 0x%08x\n",
+                        __func__, chan->hc_num, hcchar);
 
-               switch (qtd->control_phase) {
-               case DWC2_CONTROL_SETUP:
-                       dev_vdbg(hsotg->dev, "  Control setup transaction\n");
-                       chan->do_ping = 0;
-                       chan->ep_is_in = 0;
-                       chan->data_pid_start = DWC2_HC_PID_SETUP;
-                       if (hsotg->core_params->dma_enable > 0)
-                               chan->xfer_dma = urb->setup_dma;
-                       else
-                               chan->xfer_buf = urb->setup_packet;
-                       chan->xfer_len = 8;
-                       break;
+       /* Set host channel enable after all other setup is complete */
+       hcchar |= HCCHAR_CHENA;
+       hcchar &= ~HCCHAR_CHDIS;
 
-               case DWC2_CONTROL_DATA:
-                       dev_vdbg(hsotg->dev, "  Control data transaction\n");
-                       chan->data_pid_start = qtd->data_toggle;
-                       bufptr = dwc2_hc_init_xfer_data(hsotg, chan, qtd, urb);
-                       break;
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "   Multi Cnt: %d\n",
+                        (hcchar & HCCHAR_MULTICNT_MASK) >>
+                        HCCHAR_MULTICNT_SHIFT);
 
-               case DWC2_CONTROL_STATUS:
-                       /*
-                        * Direction is opposite of data direction or IN if no
-                        * data
-                        */
-                       dev_vdbg(hsotg->dev, "  Control status transaction\n");
-                       if (urb->length == 0)
-                               chan->ep_is_in = 1;
-                       else
-                               chan->ep_is_in =
-                                       dwc2_hcd_is_pipe_out(&urb->pipe_info);
-                       if (chan->ep_is_in)
-                               chan->do_ping = 0;
-                       chan->data_pid_start = DWC2_HC_PID_DATA1;
-                       chan->xfer_len = 0;
-                       if (hsotg->core_params->dma_enable > 0)
-                               chan->xfer_dma = hsotg->status_buf_dma;
-                       else
-                               chan->xfer_buf = hsotg->status_buf;
-                       break;
-               }
-               break;
+       dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
+                        chan->hc_num);
 
-       case USB_ENDPOINT_XFER_BULK:
-               chan->ep_type = USB_ENDPOINT_XFER_BULK;
-               bufptr = dwc2_hc_init_xfer_data(hsotg, chan, qtd, urb);
-               break;
+       chan->xfer_started = 1;
+       chan->requests++;
+}
 
-       case USB_ENDPOINT_XFER_INT:
-               chan->ep_type = USB_ENDPOINT_XFER_INT;
-               bufptr = dwc2_hc_init_xfer_data(hsotg, chan, qtd, urb);
-               break;
+/**
+ * dwc2_hc_continue_transfer() - Continues a data transfer that was started by
+ * a previous call to dwc2_hc_start_transfer()
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ * @chan:  Information needed to initialize the host channel
+ *
+ * The caller must ensure there is sufficient space in the request queue and Tx
+ * Data FIFO. This function should only be called in Slave mode. In DMA mode,
+ * the controller acts autonomously to complete transfers programmed to a host
+ * channel.
+ *
+ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
+ * if there is any data remaining to be queued. For an IN transfer, another
+ * data packet is always requested. For the SETUP phase of a control transfer,
+ * this function does nothing.
+ *
+ * Return: 1 if a new request is queued, 0 if no more requests are required
+ * for this transfer
+ */
+STATIC int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
+                                    struct dwc2_host_chan *chan)
+{
+       if (dbg_hc(chan))
+               dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__,
+                        chan->hc_num);
 
-       case USB_ENDPOINT_XFER_ISOC:
-               chan->ep_type = USB_ENDPOINT_XFER_ISOC;
-               if (hsotg->core_params->dma_desc_enable > 0)
-                       break;
+       if (chan->do_split)
+               /* SPLITs always queue just once per channel */
+               return 0;
 
-               frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
-               frame_desc->status = 0;
+       if (chan->data_pid_start == DWC2_HC_PID_SETUP)
+               /* SETUPs are queued only once since they can't be NAK'd */
+               return 0;
 
-               if (hsotg->core_params->dma_enable > 0) {
-                       chan->xfer_dma = urb->dma;
-                       chan->xfer_dma += frame_desc->offset +
-                                       qtd->isoc_split_offset;
-               } else {
-                       chan->xfer_buf = urb->buf;
-                       chan->xfer_buf += frame_desc->offset +
-                                       qtd->isoc_split_offset;
-               }
+       if (chan->ep_is_in) {
+               /*
+                * Always queue another request for other IN transfers. If
+                * back-to-back INs are issued and NAKs are received for both,
+                * the driver may still be processing the first NAK when the
+                * second NAK is received. When the interrupt handler clears
+                * the NAK interrupt for the first NAK, the second NAK will
+                * not be seen. So we can't depend on the NAK interrupt
+                * handler to requeue a NAK'd request. Instead, IN requests
+                * are issued each time this function is called. When the
+                * transfer completes, the extra requests for the channel will
+                * be flushed.
+                */
+               u32 hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+
+               dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
+               hcchar |= HCCHAR_CHENA;
+               hcchar &= ~HCCHAR_CHDIS;
+               if (dbg_hc(chan))
+                       dev_vdbg(hsotg->dev, "   IN xfer: hcchar = 0x%08x\n",
+                                hcchar);
+               dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
+               chan->requests++;
+               return 1;
+       }
 
-               chan->xfer_len = frame_desc->length - qtd->isoc_split_offset;
+       /* OUT transfers */
 
-               /* For non-dword aligned buffers */
-               if (hsotg->core_params->dma_enable > 0 &&
-                   (chan->xfer_dma & 0x3))
-                       bufptr = (u8 *)urb->buf + frame_desc->offset +
-                                       qtd->isoc_split_offset;
+       if (chan->xfer_count < chan->xfer_len) {
+               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+                   chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
+                       u32 hcchar = dwc2_readl(hsotg,
+                                               HCCHAR(chan->hc_num));
 
-               if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) {
-                       if (chan->xfer_len <= 188)
-                               chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL;
-                       else
-                               chan->xact_pos = DWC2_HCSPLT_XACTPOS_BEGIN;
+                       dwc2_hc_set_even_odd_frame(hsotg, chan,
+                                                  &hcchar);
                }
-               break;
+
+               /* Load OUT packet into the appropriate Tx FIFO */
+               dwc2_hc_write_packet(hsotg, chan);
+               chan->requests++;
+               return 1;
        }
 
-       return bufptr;
+       return 0;
 }
 
-STATIC int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-                                  struct dwc2_host_chan *chan,
-                                  struct dwc2_hcd_urb *urb, void *bufptr)
-{
-       u32 buf_size;
-
-       if (!qh->dw_align_buf) {
-               int err;
-
-               if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
-                       buf_size = hsotg->core_params->max_transfer_size;
-               else
-                       /* 3072 = 3 max-size Isoc packets */
-                       buf_size = 3072;
+/*
+ * =========================================================================
+ *  HCD
+ * =========================================================================
+ */
 
-               qh->dw_align_buf = NULL;
-               qh->dw_align_buf_dma = 0;
-               err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, buf_size, 0,
-                   USB_DMA_COHERENT, &qh->dw_align_buf_usbdma);
-               if (!err) {
-                       struct usb_dma *ud = &qh->dw_align_buf_usbdma;
+/*
+ * Processes all the URBs in a single list of QHs. Completes them with
+ * -ETIMEDOUT and frees the QTD.
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+STATIC void dwc2_kill_urbs_in_qh_list(struct dwc2_hsotg *hsotg,
+                                     struct list_head *qh_list)
+{
+       struct dwc2_qh *qh, *qh_tmp;
+       struct dwc2_qtd *qtd, *qtd_tmp;
 
-                       qh->dw_align_buf = KERNADDR(ud, 0);
-                       qh->dw_align_buf_dma = DMAADDR(ud, 0);
+       list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) {
+               list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
+                                        qtd_list_entry) {
+                       dwc2_host_complete(hsotg, qtd, -ECONNRESET);
+                       dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
                }
-               if (!qh->dw_align_buf)
-                       return -ENOMEM;
-               qh->dw_align_buf_size = buf_size;
        }
+}
 
-       if (chan->xfer_len) {
-               dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-               void *usb_urb = urb->priv;
+STATIC void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
+                             struct list_head *qh_list)
+{
+       struct dwc2_qtd *qtd, *qtd_tmp;
+       struct dwc2_qh *qh, *qh_tmp;
+       unsigned long flags;
 
-               if (usb_urb) {
-                       if (!chan->ep_is_in) {
-                               memcpy(qh->dw_align_buf, bufptr,
-                                      chan->xfer_len);
-                       }
-               } else {
-                       dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
-               }
-       }
+       if (!qh_list->next)
+               /* The list hasn't been initialized yet */
+               return;
 
-       usb_syncmem(&qh->dw_align_buf_usbdma, 0, qh->dw_align_buf_size,
-           chan->ep_is_in ?  BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+       spin_lock_irqsave(&hsotg->lock, flags);
 
-       chan->align_buf = qh->dw_align_buf_dma;
-       return 0;
-}
+       /* Ensure there are no QTDs or URBs left */
+       dwc2_kill_urbs_in_qh_list(hsotg, qh_list);
 
-/**
- * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host
- * channel and initializes the host channel to perform the transactions. The
- * host channel is removed from the free list.
- *
- * @hsotg: The HCD state structure
- * @qh:    Transactions from the first QTD for this QH are selected and assigned
- *         to a free host channel
- */
-STATIC int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-       struct dwc2_host_chan *chan;
-       struct dwc2_hcd_urb *urb;
-       struct dwc2_qtd *qtd;
-       void *bufptr = NULL;
+       list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) {
+               dwc2_hcd_qh_unlink(hsotg, qh);
 
-       if (dbg_qh(qh))
-               dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh);
+               /* Free each QTD in the QH's QTD list */
+               list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
+                                        qtd_list_entry)
+                       dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
-       if (list_empty(&qh->qtd_list)) {
-               dev_dbg(hsotg->dev, "No QTDs in QH list\n");
-               return -ENOMEM;
-       }
+               if (qh->channel && qh->channel->qh == qh)
+                       qh->channel->qh = NULL;
 
-       if (list_empty(&hsotg->free_hc_list)) {
-               dev_dbg(hsotg->dev, "No free channel to assign\n");
-               return -ENOMEM;
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               dwc2_hcd_qh_free(hsotg, qh);
+               spin_lock_irqsave(&hsotg->lock, flags);
        }
 
-       chan = list_first_entry(&hsotg->free_hc_list, struct dwc2_host_chan,
-                               hc_list_entry);
-
-       /* Remove host channel from free list */
-       list_del_init(&chan->hc_list_entry);
-
-       qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry);
-       urb = qtd->urb;
-       qh->channel = chan;
-       qtd->in_process = 1;
-
-       /*
-        * Use usb_pipedevice to determine device address. This address is
-        * 0 before the SET_ADDRESS command and the correct address afterward.
-        */
-       chan->dev_addr = dwc2_hcd_get_dev_addr(&urb->pipe_info);
-       chan->ep_num = dwc2_hcd_get_ep_num(&urb->pipe_info);
-       chan->speed = qh->dev_speed;
-       chan->max_packet = dwc2_max_packet(qh->maxp);
-
-       chan->xfer_started = 0;
-       chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS;
-       chan->error_state = (qtd->error_count > 0);
-       chan->halt_on_queue = 0;
-       chan->halt_pending = 0;
-       chan->requests = 0;
-
-       /*
-        * The following values may be modified in the transfer type section
-        * below. The xfer_len value may be reduced when the transfer is
-        * started to accommodate the max widths of the XferSize and PktCnt
-        * fields in the HCTSIZn register.
-        */
-
-       chan->ep_is_in = (dwc2_hcd_is_pipe_in(&urb->pipe_info) != 0);
-       if (chan->ep_is_in)
-               chan->do_ping = 0;
-       else
-               chan->do_ping = qh->ping_state;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+}
 
-       chan->data_pid_start = qh->data_toggle;
-       chan->multi_count = 1;
+/*
+ * Responds with an error status of -ETIMEDOUT to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+STATIC void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg)
+{
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_assigned);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_queued);
+}
 
-       if (urb->actual_length > urb->length &&
-               !dwc2_hcd_is_pipe_in(&urb->pipe_info))
-               urb->actual_length = urb->length;
+/**
+ * dwc2_hcd_start() - Starts the HCD when switching to Host mode
+ *
+ * @hsotg: Pointer to struct dwc2_hsotg
+ */
+void dwc2_hcd_start(struct dwc2_hsotg *hsotg)
+{
+       u32 hprt0;
 
-       chan->xfer_len = urb->length - urb->actual_length;
-       chan->xfer_count = 0;
+       if (hsotg->op_state == OTG_STATE_B_HOST) {
+               /*
+                * Reset the port. During a HNP mode switch the reset
+                * needs to occur within 1ms and have a duration of at
+                * least 50ms.
+                */
+               hprt0 = dwc2_read_hprt0(hsotg);
+               hprt0 |= HPRT0_RST;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+       }
 
-       /* Set the split attributes if required */
-       if (qh->do_split)
-               dwc2_hc_init_split(hsotg, chan, qtd, urb);
-       else
-               chan->do_split = 0;
+       queue_delayed_work(hsotg->wq_otg, &hsotg->start_work,
+                          msecs_to_jiffies(50));
+}
 
-       /* Set the transfer attributes */
-       bufptr = dwc2_hc_init_xfer(hsotg, chan, qtd, urb);
+/* Must be called with interrupt disabled and spinlock held */
+STATIC void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
+{
+       int num_channels = hsotg->params.host_channels;
+       struct dwc2_host_chan *channel;
+       u32 hcchar;
+       int i;
 
-       /* Non DWORD-aligned buffer case */
-       if (bufptr) {
-               dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
-               if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
-                       dev_err(hsotg->dev,
-                               "%s: Failed to allocate memory to handle non-dword aligned buffer\n",
-                               __func__);
-                       /* Add channel back to free list */
-                       chan->align_buf = 0;
-                       chan->multi_count = 0;
-                       list_add_tail(&chan->hc_list_entry,
-                                     &hsotg->free_hc_list);
-                       qtd->in_process = 0;
-                       qh->channel = NULL;
-                       return -ENOMEM;
+       if (!hsotg->params.host_dma) {
+               /* Flush out any channel requests in slave mode */
+               for (i = 0; i < num_channels; i++) {
+                       channel = hsotg->hc_ptr_array[i];
+                       if (!list_empty(&channel->hc_list_entry))
+                               continue;
+                       hcchar = dwc2_readl(hsotg, HCCHAR(i));
+                       if (hcchar & HCCHAR_CHENA) {
+                               hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR);
+                               hcchar |= HCCHAR_CHDIS;
+                               dwc2_writel(hsotg, hcchar, HCCHAR(i));
+                       }
                }
-       } else {
-               chan->align_buf = 0;
        }
 
-       if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
-           chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+       for (i = 0; i < num_channels; i++) {
+               channel = hsotg->hc_ptr_array[i];
+               if (!list_empty(&channel->hc_list_entry))
+                       continue;
+               hcchar = dwc2_readl(hsotg, HCCHAR(i));
+               if (hcchar & HCCHAR_CHENA) {
+                       /* Halt the channel */
+                       hcchar |= HCCHAR_CHDIS;
+                       dwc2_writel(hsotg, hcchar, HCCHAR(i));
+               }
+
+               dwc2_hc_cleanup(hsotg, channel);
+               list_add_tail(&channel->hc_list_entry, &hsotg->free_hc_list);
                /*
-                * This value may be modified when the transfer is started
-                * to reflect the actual transfer length
+                * Added for Descriptor DMA to prevent channel double cleanup in
+                * release_channel_ddma(), which is called from ep_disable when
+                * device disconnects
                 */
-               chan->multi_count = dwc2_hb_mult(qh->maxp);
-
-       if (hsotg->core_params->dma_desc_enable > 0) {
-               chan->desc_list_usbdma = qh->desc_list_usbdma;
-               chan->desc_list_addr = qh->desc_list_dma;
-               chan->desc_list_sz = qh->desc_list_sz;
+               channel->qh = NULL;
        }
+       /* All channels have been freed, mark them available */
+       if (hsotg->params.uframe_sched) {
+               hsotg->available_host_channels =
+                       hsotg->params.host_channels;
+       } else {
+               hsotg->non_periodic_channels = 0;
+               hsotg->periodic_channels = 0;
+       }
+}
 
-       dwc2_hc_init(hsotg, chan);
-       chan->qh = qh;
+/**
+ * dwc2_hcd_connect() - Handles connect of the HCD
+ *
+ * @hsotg: Pointer to struct dwc2_hsotg
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
+{
+       if (hsotg->lx_state != DWC2_L0)
+               usb_hcd_resume_root_hub(hsotg->priv);
 
-       return 0;
+       hsotg->flags.b.port_connect_status_change = 1;
+       hsotg->flags.b.port_connect_status = 1;
 }
 
 /**
- * dwc2_hcd_select_transactions() - Selects transactions from the HCD transfer
- * schedule and assigns them to available host channels. Called from the HCD
- * interrupt handler functions.
+ * dwc2_hcd_disconnect() - Handles disconnect of the HCD
  *
- * @hsotg: The HCD state structure
+ * @hsotg: Pointer to struct dwc2_hsotg
+ * @force: If true, we won't try to reconnect even if we see device connected.
  *
- * Return: The types of new transactions that were assigned to host channels
+ * Must be called with interrupt disabled and spinlock held
  */
-enum dwc2_transaction_type dwc2_hcd_select_transactions(
-               struct dwc2_hsotg *hsotg)
+void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
 {
-       enum dwc2_transaction_type ret_val = DWC2_TRANSACTION_NONE;
-       struct list_head *qh_ptr;
-       struct dwc2_qh *qh;
-       int num_channels;
-
-#ifdef DWC2_DEBUG_SOF
-       dev_vdbg(hsotg->dev, "  Select Transactions\n");
-#endif
+       u32 intr;
+       u32 hprt0;
 
-       /* Process entries in the periodic ready list */
-       qh_ptr = hsotg->periodic_sched_ready.next;
-       while (qh_ptr != &hsotg->periodic_sched_ready) {
-               if (list_empty(&hsotg->free_hc_list))
-                       break;
-               if (hsotg->core_params->uframe_sched > 0) {
-                       if (hsotg->available_host_channels <= 1)
-                               break;
-                       hsotg->available_host_channels--;
-               }
-               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
-               if (dwc2_assign_and_init_hc(hsotg, qh))
-                       break;
+       /* Set status flags for the hub driver */
+       hsotg->flags.b.port_connect_status_change = 1;
+       hsotg->flags.b.port_connect_status = 0;
 
-               /*
-                * Move the QH from the periodic ready schedule to the
-                * periodic assigned schedule
-                */
-               qh_ptr = qh_ptr->next;
-               list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned);
-               ret_val = DWC2_TRANSACTION_PERIODIC;
-       }
+       /*
+        * Shutdown any transfers in process by clearing the Tx FIFO Empty
+        * interrupt mask and status bits and disabling subsequent host
+        * channel interrupts.
+        */
+       intr = dwc2_readl(hsotg, GINTMSK);
+       intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT);
+       dwc2_writel(hsotg, intr, GINTMSK);
+       intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT;
+       dwc2_writel(hsotg, intr, GINTSTS);
 
        /*
-        * Process entries in the inactive portion of the non-periodic
-        * schedule. Some free host channels may not be used if they are
-        * reserved for periodic transfers.
+        * Turn off the vbus power only if the core has transitioned to device
+        * mode. If still in host mode, need to keep power on to detect a
+        * reconnection.
         */
-       num_channels = hsotg->core_params->host_channels;
-       qh_ptr = hsotg->non_periodic_sched_inactive.next;
-       while (qh_ptr != &hsotg->non_periodic_sched_inactive) {
-               if (hsotg->core_params->uframe_sched <= 0 &&
-                   hsotg->non_periodic_channels >= num_channels -
-                                               hsotg->periodic_channels)
-                       break;
-               if (list_empty(&hsotg->free_hc_list))
-                       break;
-               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
-
-               /*
-                * Check to see if this is a NAK'd retransmit, in which case
-                * ignore for retransmission. We hold off on bulk/control
-                * retransmissions to reduce NAK interrupt overhead for
-                * cheeky devices that just hold off using NAKs.
-                */
-               if (qh->nak_frame != 0xffff &&
-                   dwc2_full_frame_num(qh->nak_frame) ==
-                   dwc2_full_frame_num(dwc2_hcd_get_frame_number(hsotg))) {
-                       qh_ptr = qh_ptr->next;
-                       continue;
-               } else {
-                       qh->nak_frame = 0xffff;
+       if (dwc2_is_device_mode(hsotg)) {
+               if (hsotg->op_state != OTG_STATE_A_SUSPEND) {
+                       dev_dbg(hsotg->dev, "Disconnect: PortPower off\n");
+                       dwc2_writel(hsotg, 0, HPRT0);
                }
 
-               if (hsotg->core_params->uframe_sched > 0) {
-                       if (hsotg->available_host_channels < 1)
-                               break;
-                       hsotg->available_host_channels--;
-               }
+               dwc2_disable_host_interrupts(hsotg);
+       }
 
-               if (dwc2_assign_and_init_hc(hsotg, qh))
-                       break;
+       /* Respond with an error status to all URBs in the schedule */
+       dwc2_kill_all_urbs(hsotg);
 
-               /*
-                * Move the QH from the non-periodic inactive schedule to the
-                * non-periodic active schedule
-                */
-               qh_ptr = qh_ptr->next;
-               list_move(&qh->qh_list_entry,
-                         &hsotg->non_periodic_sched_active);
+       if (dwc2_is_host_mode(hsotg))
+               /* Clean up any host channels that were in use */
+               dwc2_hcd_cleanup_channels(hsotg);
 
-               if (ret_val == DWC2_TRANSACTION_NONE)
-                       ret_val = DWC2_TRANSACTION_NON_PERIODIC;
-               else
-                       ret_val = DWC2_TRANSACTION_ALL;
+       dwc2_host_disconnect(hsotg);
 
-               if (hsotg->core_params->uframe_sched <= 0)
-                       hsotg->non_periodic_channels++;
-       }
+       dwc2_root_intr(hsotg->hsotg_sc); /* Required for OpenBSD */
 
-       return ret_val;
+       /*
+        * Add an extra check here to see if we're actually connected but
+        * we don't have a detection interrupt pending.  This can happen if:
+        *   1. hardware sees connect
+        *   2. hardware sees disconnect
+        *   3. hardware sees connect
+        *   4. dwc2_port_intr() - clears connect interrupt
+        *   5. dwc2_handle_common_intr() - calls here
+        *
+        * Without the extra check here we will end calling disconnect
+        * and won't get any future interrupts to handle the connect.
+        */
+       if (!force) {
+               hprt0 = dwc2_readl(hsotg, HPRT0);
+               if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
+                       dwc2_hcd_connect(hsotg);
+       }
 }
 
 /**
- * dwc2_queue_transaction() - Attempts to queue a single transaction request for
- * a host channel associated with either a periodic or non-periodic transfer
- *
- * @hsotg: The HCD state structure
- * @chan:  Host channel descriptor associated with either a periodic or
- *         non-periodic transfer
- * @fifo_dwords_avail: Number of DWORDs available in the periodic Tx FIFO
- *                     for periodic transfers or the non-periodic Tx FIFO
- *                     for non-periodic transfers
- *
- * Return: 1 if a request is queued and more requests may be needed to
- * complete the transfer, 0 if no more requests are required for this
- * transfer, -1 if there is insufficient space in the Tx FIFO
- *
- * This function assumes that there is space available in the appropriate
- * request queue. For an OUT transfer or SETUP transaction in Slave mode,
- * it checks whether space is available in the appropriate Tx FIFO.
+ * dwc2_hcd_rem_wakeup() - Handles Remote Wakeup
  *
- * Must be called with interrupt disabled and spinlock held
+ * @hsotg: Pointer to struct dwc2_hsotg
  */
-STATIC int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
-                                 struct dwc2_host_chan *chan,
-                                 u16 fifo_dwords_avail)
+STATIC void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
 {
-       int retval = 0;
-
-       if (chan->do_split)
-               /* Put ourselves on the list to keep order straight */
-               list_move(&chan->split_order_list_entry, &hsotg->split_order);
-
-       if (hsotg->core_params->dma_enable > 0 && chan->qh) {
-               if (hsotg->core_params->dma_desc_enable > 0) {
-                       if (!chan->xfer_started ||
-                           chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-                               dwc2_hcd_start_xfer_ddma(hsotg, chan->qh);
-                               chan->qh->ping_state = 0;
-                       }
-               } else if (!chan->xfer_started) {
-                       dwc2_hc_start_transfer(hsotg, chan);
-                       chan->qh->ping_state = 0;
-               }
-       } else if (chan->halt_pending) {
-               /* Don't queue a request if the channel has been halted */
-       } else if (chan->halt_on_queue) {
-               dwc2_hc_halt(hsotg, chan, chan->halt_status);
-       } else if (chan->do_ping) {
-               if (!chan->xfer_started)
-                       dwc2_hc_start_transfer(hsotg, chan);
-       } else if (!chan->ep_is_in ||
-                  chan->data_pid_start == DWC2_HC_PID_SETUP) {
-               if ((fifo_dwords_avail * 4) >= chan->max_packet) {
-                       if (!chan->xfer_started) {
-                               dwc2_hc_start_transfer(hsotg, chan);
-                               retval = 1;
-                       } else {
-                               retval = dwc2_hc_continue_transfer(hsotg, chan);
-                       }
-               } else {
-                       retval = -1;
-               }
-       } else {
-               if (!chan->xfer_started) {
-                       dwc2_hc_start_transfer(hsotg, chan);
-                       retval = 1;
-               } else {
-                       retval = dwc2_hc_continue_transfer(hsotg, chan);
-               }
+       if (hsotg->bus_suspended) {
+               hsotg->flags.b.port_suspend_change = 1;
+               usb_hcd_resume_root_hub(hsotg->priv);
        }
 
-       return retval;
+       if (hsotg->lx_state == DWC2_L1)
+               hsotg->flags.b.port_l1_change = 1;
+
+       dwc2_root_intr(hsotg->hsotg_sc); /* Required for OpenBSD */
 }
 
-/*
- * Processes periodic channels for the next frame and queues transactions for
- * these channels to the DWC_otg controller. After queueing transactions, the
- * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
- * to queue as Periodic Tx FIFO or request queue space becomes available.
- * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
+/**
+ * dwc2_hcd_stop() - Halts the DWC_otg host mode operations in a clean manner
+ *
+ * @hsotg: Pointer to struct dwc2_hsotg
  *
  * Must be called with interrupt disabled and spinlock held
  */
-STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
+void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
 {
-       struct list_head *qh_ptr;
-       struct dwc2_qh *qh;
-       u32 tx_status;
-       u32 fspcavail;
-       u32 gintmsk;
-       int status;
-       int no_queue_space = 0;
-       int no_fifo_space = 0;
-       u32 qspcavail;
+       dev_dbg(hsotg->dev, "DWC OTG HCD STOP\n");
 
-       if (dbg_perio())
-               dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
+       /*
+        * The root hub should be disconnected before this function is called.
+        * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
+        * and the QH lists (via ..._hcd_endpoint_disable).
+        */
 
-       tx_status = DWC2_READ_4(hsotg, HPTXSTS);
-       qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-                   TXSTS_QSPCAVAIL_SHIFT;
-       fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-                   TXSTS_FSPCAVAIL_SHIFT;
+       /* Turn off all host-specific interrupts */
+       dwc2_disable_host_interrupts(hsotg);
 
-       if (dbg_perio()) {
-               dev_vdbg(hsotg->dev, "  P Tx Req Queue Space Avail (before queue): %d\n",
-                        qspcavail);
-               dev_vdbg(hsotg->dev, "  P Tx FIFO Space Avail (before queue): %d\n",
-                        fspcavail);
+       /* Turn off the vbus power */
+       dev_dbg(hsotg->dev, "PortPower off\n");
+       dwc2_writel(hsotg, 0, HPRT0);
+}
+
+/* Caller must hold driver lock */
+STATIC int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
+                               struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
+                               struct dwc2_qtd *qtd)
+{
+       u32 intr_mask;
+       int retval;
+       int dev_speed;
+
+       if (!hsotg->flags.b.port_connect_status) {
+               /* No longer connected */
+               dev_err(hsotg->dev, "Not connected\n");
+               return -ENODEV;
        }
 
-       qh_ptr = hsotg->periodic_sched_assigned.next;
-       while (qh_ptr != &hsotg->periodic_sched_assigned) {
-               tx_status = DWC2_READ_4(hsotg, HPTXSTS);
-               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-                           TXSTS_QSPCAVAIL_SHIFT;
-               if (qspcavail == 0) {
-                       no_queue_space = 1;
-                       break;
-               }
+       dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
 
-               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
-               if (!qh->channel) {
-                       qh_ptr = qh_ptr->next;
-                       continue;
-               }
+       /* Some configurations cannot support LS traffic on a FS root port */
+       if ((dev_speed == USB_SPEED_LOW) &&
+           (hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) &&
+           (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) {
+               u32 hprt0 = dwc2_readl(hsotg, HPRT0);
+               u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
 
-               /* Make sure EP's TT buffer is clean before queueing qtds */
-               if (qh->tt_buffer_dirty) {
-                       qh_ptr = qh_ptr->next;
-                       continue;
-               }
+               if (prtspd == HPRT0_SPD_FULL_SPEED)
+                       return -ENODEV;
+       }
 
-               /*
-                * Set a flag if we're queuing high-bandwidth in slave mode.
-                * The flag prevents any halts to get into the request queue in
-                * the middle of multiple high-bandwidth packets getting queued.
-                */
-               if (hsotg->core_params->dma_enable <= 0 &&
-                               qh->channel->multi_count > 1)
-                       hsotg->queuing_high_bandwidth = 1;
+       if (!qtd)
+               return -EINVAL;
 
-               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-                           TXSTS_FSPCAVAIL_SHIFT;
-               status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail);
-               if (status < 0) {
-                       no_fifo_space = 1;
-                       break;
-               }
+       memset(qtd, 0, sizeof(*qtd)); /* Required for OpenBSD */
 
-               /*
-                * In Slave mode, stay on the current transfer until there is
-                * nothing more to do or the high-bandwidth request count is
-                * reached. In DMA mode, only need to queue one request. The
-                * controller automatically handles multiple packets for
-                * high-bandwidth transfers.
-                */
-               if (hsotg->core_params->dma_enable > 0 || status == 0 ||
-                   qh->channel->requests == qh->channel->multi_count) {
-                       qh_ptr = qh_ptr->next;
+       dwc2_hcd_qtd_init(qtd, urb);
+       retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);
+       if (retval) {
+               dev_err(hsotg->dev,
+                       "DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n",
+                       retval);
+               return retval;
+       }
+
+       intr_mask = dwc2_readl(hsotg, GINTMSK);
+       if (!(intr_mask & GINTSTS_SOF)) {
+               enum dwc2_transaction_type tr_type;
+
+               if (qtd->qh->ep_type == USB_ENDPOINT_XFER_BULK &&
+                   !(qtd->urb->flags & URB_GIVEBACK_ASAP))
                        /*
-                        * Move the QH from the periodic assigned schedule to
-                        * the periodic queued schedule
+                        * Do not schedule SG transactions until qtd has
+                        * URB_GIVEBACK_ASAP set
                         */
-                       list_move(&qh->qh_list_entry,
-                                 &hsotg->periodic_sched_queued);
+                       return 0;
 
-                       /* done queuing high bandwidth */
-                       hsotg->queuing_high_bandwidth = 0;
-               }
+               tr_type = dwc2_hcd_select_transactions(hsotg);
+               if (tr_type != DWC2_TRANSACTION_NONE)
+                       dwc2_hcd_queue_transactions(hsotg, tr_type);
        }
 
-       if (hsotg->core_params->dma_enable <= 0) {
-               tx_status = DWC2_READ_4(hsotg, HPTXSTS);
-               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-                           TXSTS_QSPCAVAIL_SHIFT;
-               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-                           TXSTS_FSPCAVAIL_SHIFT;
-               if (dbg_perio()) {
-                       dev_vdbg(hsotg->dev,
-                                "  P Tx Req Queue Space Avail (after queue): %d\n",
-                                qspcavail);
-                       dev_vdbg(hsotg->dev,
-                                "  P Tx FIFO Space Avail (after queue): %d\n",
-                                fspcavail);
-               }
+       return 0;
+}
 
-               if (!list_empty(&hsotg->periodic_sched_assigned) ||
-                   no_queue_space || no_fifo_space) {
-                       /*
-                        * May need to queue more transactions as the request
-                        * queue or Tx FIFO empties. Enable the periodic Tx
-                        * FIFO empty interrupt. (Always use the half-empty
-                        * level to ensure that new requests are loaded as
-                        * soon as possible.)
-                        */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
-                       gintmsk |= GINTSTS_PTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
-               } else {
+/* Must be called with interrupt disabled and spinlock held */
+STATIC int dwc2_hcd_urb_dequeue(struct dwc2_hsotg *hsotg,
+                               struct dwc2_hcd_urb *urb)
+{
+       struct dwc2_qh *qh;
+       struct dwc2_qtd *urb_qtd;
+
+       urb_qtd = urb->qtd;
+       if (!urb_qtd) {
+               dev_dbg(hsotg->dev, "## Urb QTD is NULL ##\n");
+               return -EINVAL;
+       }
+
+       qh = urb_qtd->qh;
+       if (!qh) {
+               dev_dbg(hsotg->dev, "## Urb QTD QH is NULL ##\n");
+               return -EINVAL;
+       }
+
+       urb->priv = NULL;
+
+       if (urb_qtd->in_process && qh->channel) {
+               dwc2_dump_channel_info(hsotg, qh->channel);
+
+               /* The QTD is in process (it has been assigned to a channel) */
+               if (hsotg->flags.b.port_connect_status)
                        /*
-                        * Disable the Tx FIFO empty interrupt since there are
-                        * no more transactions that need to be queued right
-                        * now. This function is called from interrupt
-                        * handlers to queue more transactions as transfer
-                        * states change.
+                        * If still connected (i.e. in host mode), halt the
+                        * channel so it can be used for other transfers. If
+                        * no longer connected, the host registers can't be
+                        * written to halt the channel since the core is in
+                        * device mode.
                         */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
-                       gintmsk &= ~GINTSTS_PTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+                       dwc2_hc_halt(hsotg, qh->channel,
+                                    DWC2_HC_XFER_URB_DEQUEUE);
+       }
+
+       /*
+        * Free the QTD and clean up the associated QH. Leave the QH in the
+        * schedule if it has any remaining QTDs.
+        */
+       if (!hsotg->params.dma_desc_enable) {
+               u8 in_process = urb_qtd->in_process;
+
+               dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh);
+               if (in_process) {
+                       dwc2_hcd_qh_deactivate(hsotg, qh, 0);
+                       qh->channel = NULL;
+               } else if (list_empty(&qh->qtd_list)) {
+                       dwc2_hcd_qh_unlink(hsotg, qh);
                }
+       } else {
+               dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh);
        }
+
+       return 0;
 }
 
-/*
- * Processes active non-periodic channels and queues transactions for these
- * channels to the DWC_otg controller. After queueing transactions, the NP Tx
- * FIFO Empty interrupt is enabled if there are more transactions to queue as
- * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
- * FIFO Empty interrupt is disabled.
- *
- * Must be called with interrupt disabled and spinlock held
- */
-STATIC void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
+#if 0
+/* Must NOT be called with interrupt disabled or spinlock held */
+static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
+                                    struct usb_host_endpoint *ep, int retry)
 {
-       struct list_head *orig_qh_ptr;
+       struct dwc2_qtd *qtd, *qtd_tmp;
        struct dwc2_qh *qh;
-       u32 tx_status;
-       u32 qspcavail;
-       u32 fspcavail;
-       u32 gintmsk;
-       int status;
-       int no_queue_space = 0;
-       int no_fifo_space = 0;
-       int more_to_do = 0;
+       unsigned long flags;
+       int rc;
 
-       dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n");
+       spin_lock_irqsave(&hsotg->lock, flags);
 
-       tx_status = DWC2_READ_4(hsotg, GNPTXSTS);
-       qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-                   TXSTS_QSPCAVAIL_SHIFT;
-       fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-                   TXSTS_FSPCAVAIL_SHIFT;
-       dev_vdbg(hsotg->dev, "  NP Tx Req Queue Space Avail (before queue): %d\n",
-                qspcavail);
-       dev_vdbg(hsotg->dev, "  NP Tx FIFO Space Avail (before queue): %d\n",
-                fspcavail);
+       qh = ep->hcpriv;
+       if (!qh) {
+               rc = -EINVAL;
+               goto err;
+       }
 
-       /*
-        * Keep track of the starting point. Skip over the start-of-list
-        * entry.
-        */
-       if (hsotg->non_periodic_qh_ptr == &hsotg->non_periodic_sched_active)
-               hsotg->non_periodic_qh_ptr = hsotg->non_periodic_qh_ptr->next;
-       orig_qh_ptr = hsotg->non_periodic_qh_ptr;
+       while (!list_empty(&qh->qtd_list) && retry--) {
+               if (retry == 0) {
+                       dev_err(hsotg->dev,
+                               "## timeout in dwc2_hcd_endpoint_disable() ##\n");
+                       rc = -EBUSY;
+                       goto err;
+               }
 
-       /*
-        * Process once through the active list or until no more space is
-        * available in the request queue or the Tx FIFO
-        */
-       do {
-               tx_status = DWC2_READ_4(hsotg, GNPTXSTS);
-               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-                           TXSTS_QSPCAVAIL_SHIFT;
-               if (hsotg->core_params->dma_enable <= 0 && qspcavail == 0) {
-                       no_queue_space = 1;
-                       break;
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               dwc2_msleep(20);
+               spin_lock_irqsave(&hsotg->lock, flags);
+               qh = ep->hcpriv;
+               if (!qh) {
+                       rc = -EINVAL;
+                       goto err;
                }
+       }
 
-               qh = list_entry(hsotg->non_periodic_qh_ptr, struct dwc2_qh,
-                               qh_list_entry);
-               if (!qh->channel)
-                       goto next;
+       dwc2_hcd_qh_unlink(hsotg, qh);
 
-               /* Make sure EP's TT buffer is clean before queueing qtds */
-               if (qh->tt_buffer_dirty)
-                       goto next;
+       /* Free each QTD in the QH's QTD list */
+       list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry)
+               dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
-               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-                           TXSTS_FSPCAVAIL_SHIFT;
-               status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail);
+       ep->hcpriv = NULL;
 
-               if (status > 0) {
-                       more_to_do = 1;
-               } else if (status < 0) {
-                       no_fifo_space = 1;
-                       break;
-               }
-next:
-               /* Advance to next QH, skipping start-of-list entry */
-               hsotg->non_periodic_qh_ptr = hsotg->non_periodic_qh_ptr->next;
-               if (hsotg->non_periodic_qh_ptr ==
-                               &hsotg->non_periodic_sched_active)
-                       hsotg->non_periodic_qh_ptr =
-                                       hsotg->non_periodic_qh_ptr->next;
-       } while (hsotg->non_periodic_qh_ptr != orig_qh_ptr);
+       if (qh->channel && qh->channel->qh == qh)
+               qh->channel->qh = NULL;
 
-       if (hsotg->core_params->dma_enable <= 0) {
-               tx_status = DWC2_READ_4(hsotg, GNPTXSTS);
-               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-                           TXSTS_QSPCAVAIL_SHIFT;
-               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-                           TXSTS_FSPCAVAIL_SHIFT;
-               dev_vdbg(hsotg->dev,
-                        "  NP Tx Req Queue Space Avail (after queue): %d\n",
-                        qspcavail);
-               dev_vdbg(hsotg->dev,
-                        "  NP Tx FIFO Space Avail (after queue): %d\n",
-                        fspcavail);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 
-               if (more_to_do || no_queue_space || no_fifo_space) {
-                       /*
-                        * May need to queue more transactions as the request
-                        * queue or Tx FIFO empties. Enable the non-periodic
-                        * Tx FIFO empty interrupt. (Always use the half-empty
-                        * level to ensure that new requests are loaded as
-                        * soon as possible.)
-                        */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
-                       gintmsk |= GINTSTS_NPTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
-               } else {
-                       /*
-                        * Disable the Tx FIFO empty interrupt since there are
-                        * no more transactions that need to be queued right
-                        * now. This function is called from interrupt
-                        * handlers to queue more transactions as transfer
-                        * states change.
-                        */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
-                       gintmsk &= ~GINTSTS_NPTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
-               }
-       }
+       dwc2_hcd_qh_free(hsotg, qh);
+
+       return 0;
+
+err:
+       ep->hcpriv = NULL;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return rc;
 }
 
+/* Must be called with interrupt disabled and spinlock held */
+static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg,
+                                  struct usb_host_endpoint *ep)
+{
+       struct dwc2_qh *qh = ep->hcpriv;
+
+       if (!qh)
+               return -EINVAL;
+
+       qh->data_toggle = DWC2_HC_PID_DATA0;
+
+       return 0;
+}
+#endif
+
 /**
- * dwc2_hcd_queue_transactions() - Processes the currently active host channels
- * and queues transactions for these channels to the DWC_otg controller. Called
- * from the HCD interrupt handler functions.
- *
- * @hsotg:   The HCD state structure
- * @tr_type: The type(s) of transactions to queue (non-periodic, periodic,
- *           or both)
+ * dwc2_core_init() - Initializes the DWC_otg controller registers and
+ * prepares the core for device mode or host mode operation
  *
- * Must be called with interrupt disabled and spinlock held
+ * @hsotg:         Programming view of the DWC_otg controller
+ * @initial_setup: If true then this is the first init for this instance.
  */
-void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
-                                enum dwc2_transaction_type tr_type)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
 {
-#ifdef DWC2_DEBUG_SOF
-       dev_vdbg(hsotg->dev, "Queue Transactions\n");
-#endif
-       /* Process host channels associated with periodic transfers */
-       if ((tr_type == DWC2_TRANSACTION_PERIODIC ||
-            tr_type == DWC2_TRANSACTION_ALL) &&
-           !list_empty(&hsotg->periodic_sched_assigned))
-               dwc2_process_periodic_channels(hsotg);
+       u32 usbcfg, otgctl;
+       int retval;
 
-       /* Process host channels associated with non-periodic transfers */
-       if (tr_type == DWC2_TRANSACTION_NON_PERIODIC ||
-           tr_type == DWC2_TRANSACTION_ALL) {
-               if (!list_empty(&hsotg->non_periodic_sched_active)) {
-                       dwc2_process_non_periodic_channels(hsotg);
-               } else {
-                       /*
-                        * Ensure NP Tx FIFO empty interrupt is disabled when
-                        * there are no non-periodic transfers to process
-                        */
-                       u32 gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+       dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
 
-                       gintmsk &= ~GINTSTS_NPTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
+
+       /* Set ULPI External VBUS bit if needed */
+       usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV;
+       if (hsotg->params.phy_ulpi_ext_vbus)
+               usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV;
+
+       /* Set external TS Dline pulsing bit if needed */
+       usbcfg &= ~GUSBCFG_TERMSELDLPULSE;
+       if (hsotg->params.ts_dline)
+               usbcfg |= GUSBCFG_TERMSELDLPULSE;
+
+       dwc2_writel(hsotg, usbcfg, GUSBCFG);
+
+       /*
+        * Reset the Controller
+        *
+        * We only need to reset the controller if this is a re-init.
+        * For the first init we know for sure that earlier code reset us (it
+        * needed to in order to properly detect various parameters).
+        */
+       if (!initial_setup) {
+               retval = dwc2_core_reset(hsotg, false);
+               if (retval) {
+                       dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
+                               __func__);
+                       return retval;
                }
        }
-}
 
+       /*
+        * This needs to happen in FS mode before any other programming occurs
+        */
+       retval = dwc2_phy_init(hsotg, initial_setup);
+       if (retval)
+               return retval;
 
-STATIC void dwc2_conn_id_status_change(void *data)
-{
-       struct dwc2_hsotg *hsotg = data;
-       u32 count = 0;
-       u32 gotgctl;
-       unsigned long flags;
+       /* Program the GAHBCFG Register */
+       retval = dwc2_gahbcfg_init(hsotg);
+       if (retval)
+               return retval;
 
-       dev_dbg(hsotg->dev, "%s()\n", __func__);
+       /* Program the GUSBCFG register */
+       dwc2_gusbcfg_init(hsotg);
 
-       gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
-       dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl);
-       dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n",
-               !!(gotgctl & GOTGCTL_CONID_B));
+       /* Program the GOTGCTL register */
+       otgctl = dwc2_readl(hsotg, GOTGCTL);
+       otgctl &= ~GOTGCTL_OTGVER;
+       dwc2_writel(hsotg, otgctl, GOTGCTL);
 
-       /* B-Device connector (Device Mode) */
-       if (gotgctl & GOTGCTL_CONID_B) {
-               /* Wait for switch to device mode */
-               dev_dbg(hsotg->dev, "connId B\n");
-               while (!dwc2_is_device_mode(hsotg)) {
-                       dev_info(hsotg->dev,
-                                "Waiting for Peripheral Mode, Mode=%s\n",
-                                dwc2_is_host_mode(hsotg) ? "Host" :
-                                "Peripheral");
-                       usleep_range(20000, 40000);
-                       if (++count > 250)
-                               break;
-               }
-               if (count > 250)
-                       dev_err(hsotg->dev,
-                               "Connection id status change timed out\n");
-               hsotg->op_state = OTG_STATE_B_PERIPHERAL;
-               dwc2_core_init(hsotg, false);
-               dwc2_enable_global_interrupts(hsotg);
-               spin_lock_irqsave(&hsotg->lock, flags);
-               dwc2_hsotg_core_init_disconnected(hsotg, false);
-               spin_unlock_irqrestore(&hsotg->lock, flags);
-               dwc2_hsotg_core_connect(hsotg);
-       } else {
-               /* A-Device connector (Host Mode) */
-               dev_dbg(hsotg->dev, "connId A\n");
-               while (!dwc2_is_host_mode(hsotg)) {
-                       dev_info(hsotg->dev, "Waiting for Host Mode, Mode=%s\n",
-                                dwc2_is_host_mode(hsotg) ?
-                                "Host" : "Peripheral");
-                       usleep_range(20000, 40000);
-                       if (++count > 250)
-                               break;
-               }
-               if (count > 250)
-                       dev_err(hsotg->dev,
-                               "Connection id status change timed out\n");
-               hsotg->op_state = OTG_STATE_A_HOST;
+       /* Clear the SRP success bit for FS-I2c */
+       hsotg->srp_success = 0;
 
-               /* Initialize the Core for Host mode */
-               dwc2_core_init(hsotg, false);
-               dwc2_enable_global_interrupts(hsotg);
-               dwc2_hcd_start(hsotg);
+       /* Enable common interrupts */
+       dwc2_enable_common_interrupts(hsotg);
+
+       /*
+        * Do device or host initialization based on mode during PCD and
+        * HCD initialization
+        */
+       if (dwc2_is_host_mode(hsotg)) {
+               dev_dbg(hsotg->dev, "Host Mode\n");
+               hsotg->op_state = OTG_STATE_A_HOST;
+       } else {
+               dev_dbg(hsotg->dev, "Device Mode\n");
+               hsotg->op_state = OTG_STATE_B_PERIPHERAL;
        }
+
+       return 0;
 }
 
-void dwc2_wakeup_detected(void *data)
+/**
+ * dwc2_core_host_init() - Initializes the DWC_otg controller registers for
+ * Host mode
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ * This function flushes the Tx and Rx FIFOs and flushes any entries in the
+ * request queues. Host channels are reset to ensure that they are ready for
+ * performing transfers.
+ */
+static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)data;
-       u32 hprt0;
+       u32 hcfg, hfir, otgctl, usbcfg;
 
-       dev_dbg(hsotg->dev, "%s()\n", __func__);
+       dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
+
+       /* Set HS/FS Timeout Calibration to 7 (max available value).
+        * The number of PHY clocks that the application programs in
+        * this field is added to the high/full speed interpacket timeout
+        * duration in the core to account for any additional delays
+        * introduced by the PHY. This can be required, because the delay
+        * introduced by the PHY in generating the linestate condition
+        * can vary from one PHY to another.
+        */
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
+       usbcfg |= GUSBCFG_TOUTCAL(7);
+       dwc2_writel(hsotg, usbcfg, GUSBCFG);
+
+       /* Restart the Phy Clock */
+       dwc2_writel(hsotg, 0, PCGCTL);
+
+       /* Initialize Host Configuration Register */
+       dwc2_init_fs_ls_pclk_sel(hsotg);
+       if (hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
+           hsotg->params.speed == DWC2_SPEED_PARAM_LOW) {
+               hcfg = dwc2_readl(hsotg, HCFG);
+               hcfg |= HCFG_FSLSSUPP;
+               dwc2_writel(hsotg, hcfg, HCFG);
+       }
 
        /*
-        * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
-        * so that OPT tests pass with all PHYs.)
+        * This bit allows dynamic reloading of the HFIR register during
+        * runtime. This bit needs to be programmed during initial configuration
+        * and its value must not be changed during runtime.
         */
-       hprt0 = dwc2_read_hprt0(hsotg);
-       dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0);
-       hprt0 &= ~HPRT0_RES;
-       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-       dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n",
-               DWC2_READ_4(hsotg, HPRT0));
+       if (hsotg->params.reload_ctl) {
+               hfir = dwc2_readl(hsotg, HFIR);
+               hfir |= HFIR_RLDCTRL;
+               dwc2_writel(hsotg, hfir, HFIR);
+       }
 
-       dwc2_hcd_rem_wakeup(hsotg);
-       hsotg->bus_suspended = 0;
+       if (hsotg->params.dma_desc_enable) {
+               u32 op_mode = hsotg->hw_params.op_mode;
 
-       /* Change to L0 state */
-       hsotg->lx_state = DWC2_L0;
-}
+               if (hsotg->hw_params.snpsid < DWC2_CORE_REV_2_90a ||
+                   !hsotg->hw_params.dma_desc_enable ||
+                   op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE ||
+                   op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE ||
+                   op_mode == GHWCFG2_OP_MODE_UNDEFINED) {
+                       dev_err(hsotg->dev,
+                               "Hardware does not support descriptor DMA mode -\n");
+                       dev_err(hsotg->dev,
+                               "falling back to buffer DMA mode.\n");
+                       hsotg->params.dma_desc_enable = false;
+               } else {
+                       hcfg = dwc2_readl(hsotg, HCFG);
+                       hcfg |= HCFG_DESCDMA;
+                       dwc2_writel(hsotg, hcfg, HCFG);
+               }
+       }
 
-/* Must NOT be called with interrupt disabled or spinlock held */
-STATIC void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
-{
-       unsigned long flags;
-       u32 hprt0;
-       u32 pcgctl;
-       u32 gotgctl;
+       /* Configure data FIFO sizes */
+       dwc2_config_fifos(hsotg);
 
-       dev_dbg(hsotg->dev, "%s()\n", __func__);
+       /* TODO - check this */
+       /* Clear Host Set HNP Enable in the OTG Control Register */
+       otgctl = dwc2_readl(hsotg, GOTGCTL);
+       otgctl &= ~GOTGCTL_HSTSETHNPEN;
+       dwc2_writel(hsotg, otgctl, GOTGCTL);
 
-       spin_lock_irqsave(&hsotg->lock, flags);
+       /* Make sure the FIFOs are flushed */
+       dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */);
+       dwc2_flush_rx_fifo(hsotg);
 
-       if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) {
-               gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
-               gotgctl |= GOTGCTL_HSTSETHNPEN;
-               DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
-               hsotg->op_state = OTG_STATE_A_SUSPEND;
-       }
+       /* Clear Host Set HNP Enable in the OTG Control Register */
+       otgctl = dwc2_readl(hsotg, GOTGCTL);
+       otgctl &= ~GOTGCTL_HSTSETHNPEN;
+       dwc2_writel(hsotg, otgctl, GOTGCTL);
 
-       hprt0 = dwc2_read_hprt0(hsotg);
-       hprt0 |= HPRT0_SUSP;
-       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+       if (!hsotg->params.dma_desc_enable) {
+               int num_channels, i;
+               u32 hcchar;
 
-       hsotg->bus_suspended = 1;
+               /* Flush out any leftover queued requests */
+               num_channels = hsotg->params.host_channels;
+               for (i = 0; i < num_channels; i++) {
+                       hcchar = dwc2_readl(hsotg, HCCHAR(i));
+                       if (hcchar & HCCHAR_CHENA) {
+                               hcchar &= ~HCCHAR_CHENA;
+                               hcchar |= HCCHAR_CHDIS;
+                               hcchar &= ~HCCHAR_EPDIR;
+                               dwc2_writel(hsotg, hcchar, HCCHAR(i));
+                       }
+               }
 
-       /*
-        * If hibernation is supported, Phy clock will be suspended
-        * after registers are backuped.
-        */
-       if (!hsotg->core_params->hibernation) {
-               /* Suspend the Phy Clock */
-               pcgctl = DWC2_READ_4(hsotg, PCGCTL);
-               pcgctl |= PCGCTL_STOPPCLK;
-               DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
-               udelay(10);
+               /* Halt all channels to put them into a known state */
+               for (i = 0; i < num_channels; i++) {
+                       hcchar = dwc2_readl(hsotg, HCCHAR(i));
+                       if (hcchar & HCCHAR_CHENA) {
+                               hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
+                               hcchar &= ~HCCHAR_EPDIR;
+                               dwc2_writel(hsotg, hcchar, HCCHAR(i));
+                               dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
+                                       __func__, i);
+
+                               if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i),
+                                                             HCCHAR_CHENA,
+                                                             1000)) {
+                                       dev_warn(hsotg->dev,
+                                                "Unable to clear enable on channel %d\n",
+                                                i);
+                               }
+                       }
+               }
        }
 
-       /* For HNP the bus must be suspended for at least 200ms */
-       if (dwc2_host_is_b_hnp_enabled(hsotg)) {
-               pcgctl = DWC2_READ_4(hsotg, PCGCTL);
-               pcgctl &= ~PCGCTL_STOPPCLK;
-               DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
+       /* Enable ACG feature in host mode, if supported */
+       dwc2_enable_acg(hsotg);
 
-               spin_unlock_irqrestore(&hsotg->lock, flags);
+       /* Turn on the vbus power */
+       dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state);
+       if (hsotg->op_state == OTG_STATE_A_HOST) {
+               u32 hprt0 = dwc2_read_hprt0(hsotg);
 
-               usleep_range(200000, 250000);
-       } else {
-               spin_unlock_irqrestore(&hsotg->lock, flags);
+               dev_dbg(hsotg->dev, "Init: Power Port (%d)\n",
+                       !!(hprt0 & HPRT0_PWR));
+               if (!(hprt0 & HPRT0_PWR)) {
+                       hprt0 |= HPRT0_PWR;
+                       dwc2_writel(hsotg, hprt0, HPRT0);
+               }
        }
+
+       dwc2_enable_host_interrupts(hsotg);
 }
 
-/* Must NOT be called with interrupt disabled or spinlock held */
-STATIC void dwc2_port_resume(struct dwc2_hsotg *hsotg)
+/*
+ * Initializes dynamic portions of the DWC_otg HCD state
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+STATIC void dwc2_hcd_reinit(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
-       unsigned long flags;
-       u32 hprt0;
-       u32 pcgctl;
+       struct dwc2_host_chan *chan, *chan_tmp;
+       int num_channels;
+       int i;
 
-       spin_lock_irqsave(&hsotg->lock, flags);
+       hsotg->flags.d32 = 0;
+       hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active;
+
+       if (hsotg->params.uframe_sched) {
+               hsotg->available_host_channels =
+                       hsotg->params.host_channels;
+       } else {
+               hsotg->non_periodic_channels = 0;
+               hsotg->periodic_channels = 0;
+       }
 
        /*
-        * If hibernation is supported, Phy clock is already resumed
-        * after registers restore.
+        * Put all channels in the free channel list and clean up channel
+        * states
         */
-       if (!hsotg->core_params->hibernation) {
-               pcgctl = DWC2_READ_4(hsotg, PCGCTL);
-               pcgctl &= ~PCGCTL_STOPPCLK;
-               DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
-               spin_unlock_irqrestore(&hsotg->lock, flags);
-               usleep_range(20000, 40000);
-               spin_lock_irqsave(&hsotg->lock, flags);
+       list_for_each_entry_safe(chan, chan_tmp, &hsotg->free_hc_list,
+                                hc_list_entry)
+               list_del_init(&chan->hc_list_entry);
+
+       num_channels = hsotg->params.host_channels;
+       for (i = 0; i < num_channels; i++) {
+               chan = hsotg->hc_ptr_array[i];
+               list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
+               dwc2_hc_cleanup(hsotg, chan);
        }
 
-       hprt0 = dwc2_read_hprt0(hsotg);
-       hprt0 |= HPRT0_RES;
-       hprt0 &= ~HPRT0_SUSP;
-       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-       spin_unlock_irqrestore(&hsotg->lock, flags);
+       /* Initialize the DWC core for host mode operation */
+       dwc2_core_host_init(hsotg);
+}
 
-       usb_delay_ms(&sc->sc_bus, USB_RESUME_TIMEOUT);
+STATIC void dwc2_hc_init_split(struct dwc2_hsotg *hsotg,
+                              struct dwc2_host_chan *chan,
+                              struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
+{
+       int hub_addr, hub_port;
 
-       spin_lock_irqsave(&hsotg->lock, flags);
-       hprt0 = dwc2_read_hprt0(hsotg);
-       hprt0 &= ~(HPRT0_RES | HPRT0_SUSP);
-       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-       hsotg->bus_suspended = 0;
-       spin_unlock_irqrestore(&hsotg->lock, flags);
+       chan->do_split = 1;
+       chan->xact_pos = qtd->isoc_split_pos;
+       chan->complete_split = qtd->complete_split;
+       dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+       chan->hub_addr = (u8)hub_addr;
+       chan->hub_port = (u8)hub_port;
 }
 
-/* Handles hub class-specific requests */
-int
-dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
-                               u16 wvalue, u16 windex, char *buf, u16 wlength)
+STATIC void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
+                             struct dwc2_host_chan *chan,
+                             struct dwc2_qtd *qtd)
 {
-       usb_hub_descriptor_t *hub_desc;
-       usb_port_status_t ps;
-       int retval = 0;
-       u32 hprt0;
-       u32 port_status;
-       u32 speed;
-       u32 pcgctl;
+       struct dwc2_hcd_urb *urb = qtd->urb;
+       struct dwc2_hcd_iso_packet_desc *frame_desc;
 
-       switch (typereq) {
-       case ClearHubFeature:
-               dev_dbg(hsotg->dev, "ClearHubFeature %1xh\n", wvalue);
+       switch (dwc2_hcd_get_pipe_type(&urb->pipe_info)) {
+       case USB_ENDPOINT_XFER_CONTROL:
+               chan->ep_type = USB_ENDPOINT_XFER_CONTROL;
 
-               switch (wvalue) {
-               case C_HUB_LOCAL_POWER:
-               case C_HUB_OVER_CURRENT:
-                       /* Nothing required here */
+               switch (qtd->control_phase) {
+               case DWC2_CONTROL_SETUP:
+                       dev_vdbg(hsotg->dev, "  Control setup transaction\n");
+                       chan->do_ping = 0;
+                       chan->ep_is_in = 0;
+                       chan->data_pid_start = DWC2_HC_PID_SETUP;
+                       if (hsotg->params.host_dma)
+                               chan->xfer_dma = urb->setup_dma;
+                       else
+                               chan->xfer_buf = urb->setup_packet;
+                       chan->xfer_len = 8;
                        break;
 
-               default:
-                       retval = -EINVAL;
-                       dev_err(hsotg->dev,
-                               "ClearHubFeature request %1xh unknown\n",
-                               wvalue);
+               case DWC2_CONTROL_DATA:
+                       dev_vdbg(hsotg->dev, "  Control data transaction\n");
+                       chan->data_pid_start = qtd->data_toggle;
+                       break;
+
+               case DWC2_CONTROL_STATUS:
+                       /*
+                        * Direction is opposite of data direction or IN if no
+                        * data
+                        */
+                       dev_vdbg(hsotg->dev, "  Control status transaction\n");
+                       if (urb->length == 0)
+                               chan->ep_is_in = 1;
+                       else
+                               chan->ep_is_in =
+                                       dwc2_hcd_is_pipe_out(&urb->pipe_info);
+                       if (chan->ep_is_in)
+                               chan->do_ping = 0;
+                       chan->data_pid_start = DWC2_HC_PID_DATA1;
+                       chan->xfer_len = 0;
+                       if (hsotg->params.host_dma)
+                               chan->xfer_dma = hsotg->status_buf_dma;
+                       else
+                               chan->xfer_buf = hsotg->status_buf;
+                       break;
                }
                break;
 
-       case ClearPortFeature:
-//             if (wvalue != USB_PORT_FEAT_L1)
-                       if (!windex || windex > 1)
-                               goto error;
-               switch (wvalue) {
-               case USB_PORT_FEAT_ENABLE:
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_ENABLE\n");
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       hprt0 |= HPRT0_ENA;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-                       break;
+       case USB_ENDPOINT_XFER_BULK:
+               chan->ep_type = USB_ENDPOINT_XFER_BULK;
+               break;
 
-               case USB_PORT_FEAT_SUSPEND:
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
-                       if (hsotg->bus_suspended)
-                               dwc2_port_resume(hsotg);
-                       break;
+       case USB_ENDPOINT_XFER_INT:
+               chan->ep_type = USB_ENDPOINT_XFER_INT;
+               break;
 
-               case USB_PORT_FEAT_POWER:
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_POWER\n");
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       hprt0 &= ~HPRT0_PWR;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+       case USB_ENDPOINT_XFER_ISOC:
+               chan->ep_type = USB_ENDPOINT_XFER_ISOC;
+               if (hsotg->params.dma_desc_enable)
                        break;
 
-               case USB_PORT_FEAT_INDICATOR:
+               frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
+               frame_desc->status = 0;
+
+               if (hsotg->params.host_dma) {
+                       chan->xfer_dma = urb->dma;
+                       chan->xfer_dma += frame_desc->offset +
+                                       qtd->isoc_split_offset;
+               } else {
+                       chan->xfer_buf = urb->buf;
+                       chan->xfer_buf += frame_desc->offset +
+                                       qtd->isoc_split_offset;
+               }
+
+               chan->xfer_len = frame_desc->length - qtd->isoc_split_offset;
+
+               if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) {
+                       if (chan->xfer_len <= 188)
+                               chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL;
+                       else
+                               chan->xact_pos = DWC2_HCSPLT_XACTPOS_BEGIN;
+               }
+               break;
+       }
+}
+
+static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
+                                           struct dwc2_qh *qh,
+                                           struct dwc2_host_chan *chan)
+{
+#if 0
+       if (!hsotg->unaligned_cache ||
+           chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE)
+               return -ENOMEM;
+
+       if (!qh->dw_align_buf) {
+               qh->dw_align_buf = kmem_cache_alloc(hsotg->unaligned_cache,
+                                                   GFP_ATOMIC | GFP_DMA);
+               if (!qh->dw_align_buf)
+                       return -ENOMEM;
+       }
+
+       qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf,
+                                             DWC2_KMEM_UNALIGNED_BUF_SIZE,
+                                             DMA_FROM_DEVICE);
+
+       if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
+               dev_err(hsotg->dev, "can't map align_buf\n");
+               chan->align_buf = 0;
+               return -EINVAL;
+       }
+
+       chan->align_buf = qh->dw_align_buf_dma;
+#endif
+       return 0;
+}
+
+#define DWC2_USB_DMA_ALIGN 4
+
+#if 0
+static void dwc2_free_dma_aligned_buffer(struct urb *urb)
+{
+       void *stored_xfer_buffer;
+       size_t length;
+
+       if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+               return;
+
+       /* Restore urb->transfer_buffer from the end of the allocated area */
+       memcpy(&stored_xfer_buffer,
+              PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length,
+                        dma_get_cache_alignment()),
+              sizeof(urb->transfer_buffer));
+
+       if (usb_urb_dir_in(urb)) {
+               if (usb_pipeisoc(urb->pipe))
+                       length = urb->transfer_buffer_length;
+               else
+                       length = urb->actual_length;
+
+               memcpy(stored_xfer_buffer, urb->transfer_buffer, length);
+       }
+       kfree(urb->transfer_buffer);
+       urb->transfer_buffer = stored_xfer_buffer;
+
+       urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+}
+
+static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
+{
+       void *kmalloc_ptr;
+       size_t kmalloc_size;
+
+       if (urb->num_sgs || urb->sg ||
+           urb->transfer_buffer_length == 0 ||
+           !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
+               return 0;
+
+       /*
+        * Allocate a buffer with enough padding for original transfer_buffer
+        * pointer. This allocation is guaranteed to be aligned properly for
+        * DMA
+        */
+       kmalloc_size = urb->transfer_buffer_length +
+               (dma_get_cache_alignment() - 1) +
+               sizeof(urb->transfer_buffer);
+
+       kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+       if (!kmalloc_ptr)
+               return -ENOMEM;
+
+       /*
+        * Position value of original urb->transfer_buffer pointer to the end
+        * of allocation for later referencing
+        */
+       memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length,
+                        dma_get_cache_alignment()),
+              &urb->transfer_buffer, sizeof(urb->transfer_buffer));
+
+       if (usb_urb_dir_out(urb))
+               memcpy(kmalloc_ptr, urb->transfer_buffer,
+                      urb->transfer_buffer_length);
+       urb->transfer_buffer = kmalloc_ptr;
+
+       urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
+
+       return 0;
+}
+
+static int dwc2_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+                               gfp_t mem_flags)
+{
+       int ret;
+
+       /* We assume setup_dma is always aligned; warn if not */
+       WARN_ON_ONCE(urb->setup_dma &&
+                    (urb->setup_dma & (DWC2_USB_DMA_ALIGN - 1)));
+
+       ret = dwc2_alloc_dma_aligned_buffer(urb, mem_flags);
+       if (ret)
+               return ret;
+
+       ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+       if (ret)
+               dwc2_free_dma_aligned_buffer(urb);
+
+       return ret;
+}
+
+static void dwc2_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+       usb_hcd_unmap_urb_for_dma(hcd, urb);
+       dwc2_free_dma_aligned_buffer(urb);
+}
+#endif
+
+/**
+ * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host
+ * channel and initializes the host channel to perform the transactions. The
+ * host channel is removed from the free list.
+ *
+ * @hsotg: The HCD state structure
+ * @qh:    Transactions from the first QTD for this QH are selected and assigned
+ *         to a free host channel
+ */
+STATIC int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       struct dwc2_host_chan *chan;
+       struct dwc2_hcd_urb *urb;
+       struct dwc2_qtd *qtd;
+
+       if (dbg_qh(qh))
+               dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh);
+
+       if (list_empty(&qh->qtd_list)) {
+               dev_dbg(hsotg->dev, "No QTDs in QH list\n");
+               return -ENOMEM;
+       }
+
+       if (list_empty(&hsotg->free_hc_list)) {
+               dev_dbg(hsotg->dev, "No free channel to assign\n");
+               return -ENOMEM;
+       }
+
+       chan = list_first_entry(&hsotg->free_hc_list, struct dwc2_host_chan,
+                               hc_list_entry);
+
+       /* Remove host channel from free list */
+       list_del_init(&chan->hc_list_entry);
+
+       qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry);
+       urb = qtd->urb;
+       qh->channel = chan;
+       qtd->in_process = 1;
+
+       /*
+        * Use usb_pipedevice to determine device address. This address is
+        * 0 before the SET_ADDRESS command and the correct address afterward.
+        */
+       chan->dev_addr = dwc2_hcd_get_dev_addr(&urb->pipe_info);
+       chan->ep_num = dwc2_hcd_get_ep_num(&urb->pipe_info);
+       chan->speed = qh->dev_speed;
+       chan->max_packet = qh->maxp;
+
+       chan->xfer_started = 0;
+       chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS;
+       chan->error_state = (qtd->error_count > 0);
+       chan->halt_on_queue = 0;
+       chan->halt_pending = 0;
+       chan->requests = 0;
+
+       /*
+        * The following values may be modified in the transfer type section
+        * below. The xfer_len value may be reduced when the transfer is
+        * started to accommodate the max widths of the XferSize and PktCnt
+        * fields in the HCTSIZn register.
+        */
+
+       chan->ep_is_in = (dwc2_hcd_is_pipe_in(&urb->pipe_info) != 0);
+       if (chan->ep_is_in)
+               chan->do_ping = 0;
+       else
+               chan->do_ping = qh->ping_state;
+
+       chan->data_pid_start = qh->data_toggle;
+       chan->multi_count = 1;
+
+       if (urb->actual_length > urb->length &&
+           !dwc2_hcd_is_pipe_in(&urb->pipe_info))
+               urb->actual_length = urb->length;
+
+       if (hsotg->params.host_dma)
+               chan->xfer_dma = urb->dma + urb->actual_length;
+       else
+               chan->xfer_buf = (u8 *)urb->buf + urb->actual_length;
+
+       chan->xfer_len = urb->length - urb->actual_length;
+       chan->xfer_count = 0;
+
+       /* Set the split attributes if required */
+       if (qh->do_split)
+               dwc2_hc_init_split(hsotg, chan, qtd, urb);
+       else
+               chan->do_split = 0;
+
+       /* Set the transfer attributes */
+       dwc2_hc_init_xfer(hsotg, chan, qtd);
+
+       /* For non-dword aligned buffers */
+       if (hsotg->params.host_dma && qh->do_split &&
+           chan->ep_is_in && (chan->xfer_dma & 0x3)) {
+               dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
+               if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) {
+                       dev_err(hsotg->dev,
+                               "Failed to allocate memory to handle non-aligned buffer\n");
+                       /* Add channel back to free list */
+                       chan->align_buf = 0;
+                       chan->multi_count = 0;
+                       list_add_tail(&chan->hc_list_entry,
+                                     &hsotg->free_hc_list);
+                       qtd->in_process = 0;
+                       qh->channel = NULL;
+                       return -ENOMEM;
+               }
+       } else {
+               /*
+                * We assume that DMA is always aligned in non-split
+                * case or split out case. Warn if not.
+                */
+               WARN_ON_ONCE(hsotg->params.host_dma &&
+                            (chan->xfer_dma & 0x3));
+               chan->align_buf = 0;
+       }
+
+       if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+           chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+               /*
+                * This value may be modified when the transfer is started
+                * to reflect the actual transfer length
+                */
+               chan->multi_count = qh->maxp_mult;
+
+       if (hsotg->params.dma_desc_enable) {
+               chan->desc_list_addr = qh->desc_list_dma;
+               chan->desc_list_sz = qh->desc_list_sz;
+       }
+
+       dwc2_hc_init(hsotg, chan);
+       chan->qh = qh;
+
+       return 0;
+}
+
+/**
+ * dwc2_hcd_select_transactions() - Selects transactions from the HCD transfer
+ * schedule and assigns them to available host channels. Called from the HCD
+ * interrupt handler functions.
+ *
+ * @hsotg: The HCD state structure
+ *
+ * Return: The types of new transactions that were assigned to host channels
+ */
+enum dwc2_transaction_type dwc2_hcd_select_transactions(
+               struct dwc2_hsotg *hsotg)
+{
+       enum dwc2_transaction_type ret_val = DWC2_TRANSACTION_NONE;
+       struct list_head *qh_ptr;
+       struct dwc2_qh *qh;
+       int num_channels;
+
+#ifdef DWC2_DEBUG_SOF
+       dev_vdbg(hsotg->dev, "  Select Transactions\n");
+#endif
+
+       /* Process entries in the periodic ready list */
+       qh_ptr = hsotg->periodic_sched_ready.next;
+       while (qh_ptr != &hsotg->periodic_sched_ready) {
+               if (list_empty(&hsotg->free_hc_list))
+                       break;
+               if (hsotg->params.uframe_sched) {
+                       if (hsotg->available_host_channels <= 1)
+                               break;
+                       hsotg->available_host_channels--;
+               }
+               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
+               if (dwc2_assign_and_init_hc(hsotg, qh))
+                       break;
+
+               /*
+                * Move the QH from the periodic ready schedule to the
+                * periodic assigned schedule
+                */
+               qh_ptr = qh_ptr->next;
+               list_move_tail(&qh->qh_list_entry,
+                              &hsotg->periodic_sched_assigned);
+               ret_val = DWC2_TRANSACTION_PERIODIC;
+       }
+
+       /*
+        * Process entries in the inactive portion of the non-periodic
+        * schedule. Some free host channels may not be used if they are
+        * reserved for periodic transfers.
+        */
+       num_channels = hsotg->params.host_channels;
+       qh_ptr = hsotg->non_periodic_sched_inactive.next;
+       while (qh_ptr != &hsotg->non_periodic_sched_inactive) {
+               if (!hsotg->params.uframe_sched &&
+                   hsotg->non_periodic_channels >= num_channels -
+                                               hsotg->periodic_channels)
+                       break;
+               if (list_empty(&hsotg->free_hc_list))
+                       break;
+               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
+               if (hsotg->params.uframe_sched) {
+                       if (hsotg->available_host_channels < 1)
+                               break;
+                       hsotg->available_host_channels--;
+               }
+
+               if (dwc2_assign_and_init_hc(hsotg, qh))
+                       break;
+
+               /*
+                * Move the QH from the non-periodic inactive schedule to the
+                * non-periodic active schedule
+                */
+               qh_ptr = qh_ptr->next;
+               list_move_tail(&qh->qh_list_entry,
+                              &hsotg->non_periodic_sched_active);
+
+               if (ret_val == DWC2_TRANSACTION_NONE)
+                       ret_val = DWC2_TRANSACTION_NON_PERIODIC;
+               else
+                       ret_val = DWC2_TRANSACTION_ALL;
+
+               if (!hsotg->params.uframe_sched)
+                       hsotg->non_periodic_channels++;
+       }
+
+       return ret_val;
+}
+
+/**
+ * dwc2_queue_transaction() - Attempts to queue a single transaction request for
+ * a host channel associated with either a periodic or non-periodic transfer
+ *
+ * @hsotg: The HCD state structure
+ * @chan:  Host channel descriptor associated with either a periodic or
+ *         non-periodic transfer
+ * @fifo_dwords_avail: Number of DWORDs available in the periodic Tx FIFO
+ *                     for periodic transfers or the non-periodic Tx FIFO
+ *                     for non-periodic transfers
+ *
+ * Return: 1 if a request is queued and more requests may be needed to
+ * complete the transfer, 0 if no more requests are required for this
+ * transfer, -1 if there is insufficient space in the Tx FIFO
+ *
+ * This function assumes that there is space available in the appropriate
+ * request queue. For an OUT transfer or SETUP transaction in Slave mode,
+ * it checks whether space is available in the appropriate Tx FIFO.
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+STATIC int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
+                                 struct dwc2_host_chan *chan,
+                                 u16 fifo_dwords_avail)
+{
+       int retval = 0;
+
+       if (chan->do_split)
+               /* Put ourselves on the list to keep order straight */
+               list_move_tail(&chan->split_order_list_entry,
+                              &hsotg->split_order);
+
+       if (hsotg->params.host_dma && chan->qh) {
+               if (hsotg->params.dma_desc_enable) {
+                       if (!chan->xfer_started ||
+                           chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
+                               dwc2_hcd_start_xfer_ddma(hsotg, chan->qh);
+                               chan->qh->ping_state = 0;
+                       }
+               } else if (!chan->xfer_started) {
+                       dwc2_hc_start_transfer(hsotg, chan);
+                       chan->qh->ping_state = 0;
+               }
+       } else if (chan->halt_pending) {
+               /* Don't queue a request if the channel has been halted */
+       } else if (chan->halt_on_queue) {
+               dwc2_hc_halt(hsotg, chan, chan->halt_status);
+       } else if (chan->do_ping) {
+               if (!chan->xfer_started)
+                       dwc2_hc_start_transfer(hsotg, chan);
+       } else if (!chan->ep_is_in ||
+                  chan->data_pid_start == DWC2_HC_PID_SETUP) {
+               if ((fifo_dwords_avail * 4) >= chan->max_packet) {
+                       if (!chan->xfer_started) {
+                               dwc2_hc_start_transfer(hsotg, chan);
+                               retval = 1;
+                       } else {
+                               retval = dwc2_hc_continue_transfer(hsotg, chan);
+                       }
+               } else {
+                       retval = -1;
+               }
+       } else {
+               if (!chan->xfer_started) {
+                       dwc2_hc_start_transfer(hsotg, chan);
+                       retval = 1;
+               } else {
+                       retval = dwc2_hc_continue_transfer(hsotg, chan);
+               }
+       }
+
+       return retval;
+}
+
+/*
+ * Processes periodic channels for the next frame and queues transactions for
+ * these channels to the DWC_otg controller. After queueing transactions, the
+ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
+ * to queue as Periodic Tx FIFO or request queue space becomes available.
+ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
+{
+       struct list_head *qh_ptr;
+       struct dwc2_qh *qh;
+       u32 tx_status;
+       u32 fspcavail;
+       u32 gintmsk;
+       int status;
+       bool no_queue_space = false;
+       bool no_fifo_space = false;
+       u32 qspcavail;
+
+       /* If empty list then just adjust interrupt enables */
+       if (list_empty(&hsotg->periodic_sched_assigned))
+               goto exit;
+
+       if (dbg_perio())
+               dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
+
+       tx_status = dwc2_readl(hsotg, HPTXSTS);
+       qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
+                   TXSTS_QSPCAVAIL_SHIFT;
+       fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
+                   TXSTS_FSPCAVAIL_SHIFT;
+
+       if (dbg_perio()) {
+               dev_vdbg(hsotg->dev, "  P Tx Req Queue Space Avail (before queue): %d\n",
+                        qspcavail);
+               dev_vdbg(hsotg->dev, "  P Tx FIFO Space Avail (before queue): %d\n",
+                        fspcavail);
+       }
+
+       qh_ptr = hsotg->periodic_sched_assigned.next;
+       while (qh_ptr != &hsotg->periodic_sched_assigned) {
+               tx_status = dwc2_readl(hsotg, HPTXSTS);
+               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
+                           TXSTS_QSPCAVAIL_SHIFT;
+               if (qspcavail == 0) {
+                       no_queue_space = true;
+                       break;
+               }
+
+               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
+               if (!qh->channel) {
+                       qh_ptr = qh_ptr->next;
+                       continue;
+               }
+
+               /* Make sure EP's TT buffer is clean before queueing qtds */
+               if (qh->tt_buffer_dirty) {
+                       qh_ptr = qh_ptr->next;
+                       continue;
+               }
+
+               /*
+                * Set a flag if we're queuing high-bandwidth in slave mode.
+                * The flag prevents any halts to get into the request queue in
+                * the middle of multiple high-bandwidth packets getting queued.
+                */
+               if (!hsotg->params.host_dma &&
+                   qh->channel->multi_count > 1)
+                       hsotg->queuing_high_bandwidth = 1;
+
+               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
+                           TXSTS_FSPCAVAIL_SHIFT;
+               status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail);
+               if (status < 0) {
+                       no_fifo_space = true;
+                       break;
+               }
+
+               /*
+                * In Slave mode, stay on the current transfer until there is
+                * nothing more to do or the high-bandwidth request count is
+                * reached. In DMA mode, only need to queue one request. The
+                * controller automatically handles multiple packets for
+                * high-bandwidth transfers.
+                */
+               if (hsotg->params.host_dma || status == 0 ||
+                   qh->channel->requests == qh->channel->multi_count) {
+                       qh_ptr = qh_ptr->next;
+                       /*
+                        * Move the QH from the periodic assigned schedule to
+                        * the periodic queued schedule
+                        */
+                       list_move_tail(&qh->qh_list_entry,
+                                      &hsotg->periodic_sched_queued);
+
+                       /* done queuing high bandwidth */
+                       hsotg->queuing_high_bandwidth = 0;
+               }
+       }
+
+exit:
+       if (no_queue_space || no_fifo_space ||
+           (!hsotg->params.host_dma &&
+            !list_empty(&hsotg->periodic_sched_assigned))) {
+               /*
+                * May need to queue more transactions as the request
+                * queue or Tx FIFO empties. Enable the periodic Tx
+                * FIFO empty interrupt. (Always use the half-empty
+                * level to ensure that new requests are loaded as
+                * soon as possible.)
+                */
+               gintmsk = dwc2_readl(hsotg, GINTMSK);
+               if (!(gintmsk & GINTSTS_PTXFEMP)) {
+                       gintmsk |= GINTSTS_PTXFEMP;
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
+               }
+       } else {
+               /*
+                * Disable the Tx FIFO empty interrupt since there are
+                * no more transactions that need to be queued right
+                * now. This function is called from interrupt
+                * handlers to queue more transactions as transfer
+                * states change.
+                */
+               gintmsk = dwc2_readl(hsotg, GINTMSK);
+               if (gintmsk & GINTSTS_PTXFEMP) {
+                       gintmsk &= ~GINTSTS_PTXFEMP;
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
+               }
+       }
+}
+
+/*
+ * Processes active non-periodic channels and queues transactions for these
+ * channels to the DWC_otg controller. After queueing transactions, the NP Tx
+ * FIFO Empty interrupt is enabled if there are more transactions to queue as
+ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
+ * FIFO Empty interrupt is disabled.
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+STATIC void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
+{
+       struct list_head *orig_qh_ptr;
+       struct dwc2_qh *qh;
+       u32 tx_status;
+       u32 qspcavail;
+       u32 fspcavail;
+       u32 gintmsk;
+       int status;
+       int no_queue_space = 0;
+       int no_fifo_space = 0;
+       int more_to_do = 0;
+
+       dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n");
+
+       tx_status = dwc2_readl(hsotg, GNPTXSTS);
+       qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
+                   TXSTS_QSPCAVAIL_SHIFT;
+       fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
+                   TXSTS_FSPCAVAIL_SHIFT;
+       dev_vdbg(hsotg->dev, "  NP Tx Req Queue Space Avail (before queue): %d\n",
+                qspcavail);
+       dev_vdbg(hsotg->dev, "  NP Tx FIFO Space Avail (before queue): %d\n",
+                fspcavail);
+
+       /*
+        * Keep track of the starting point. Skip over the start-of-list
+        * entry.
+        */
+       if (hsotg->non_periodic_qh_ptr == &hsotg->non_periodic_sched_active)
+               hsotg->non_periodic_qh_ptr = hsotg->non_periodic_qh_ptr->next;
+       orig_qh_ptr = hsotg->non_periodic_qh_ptr;
+
+       /*
+        * Process once through the active list or until no more space is
+        * available in the request queue or the Tx FIFO
+        */
+       do {
+               tx_status = dwc2_readl(hsotg, GNPTXSTS);
+               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
+                           TXSTS_QSPCAVAIL_SHIFT;
+               if (!hsotg->params.host_dma && qspcavail == 0) {
+                       no_queue_space = 1;
+                       break;
+               }
+
+               qh = list_entry(hsotg->non_periodic_qh_ptr, struct dwc2_qh,
+                               qh_list_entry);
+               if (!qh->channel)
+                       goto next;
+
+               /* Make sure EP's TT buffer is clean before queueing qtds */
+               if (qh->tt_buffer_dirty)
+                       goto next;
+
+               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
+                           TXSTS_FSPCAVAIL_SHIFT;
+               status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail);
+
+               if (status > 0) {
+                       more_to_do = 1;
+               } else if (status < 0) {
+                       no_fifo_space = 1;
+                       break;
+               }
+next:
+               /* Advance to next QH, skipping start-of-list entry */
+               hsotg->non_periodic_qh_ptr = hsotg->non_periodic_qh_ptr->next;
+               if (hsotg->non_periodic_qh_ptr ==
+                               &hsotg->non_periodic_sched_active)
+                       hsotg->non_periodic_qh_ptr =
+                                       hsotg->non_periodic_qh_ptr->next;
+       } while (hsotg->non_periodic_qh_ptr != orig_qh_ptr);
+
+       if (!hsotg->params.host_dma) {
+               tx_status = dwc2_readl(hsotg, GNPTXSTS);
+               qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
+                           TXSTS_QSPCAVAIL_SHIFT;
+               fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
+                           TXSTS_FSPCAVAIL_SHIFT;
+               dev_vdbg(hsotg->dev,
+                        "  NP Tx Req Queue Space Avail (after queue): %d\n",
+                        qspcavail);
+               dev_vdbg(hsotg->dev,
+                        "  NP Tx FIFO Space Avail (after queue): %d\n",
+                        fspcavail);
+
+               if (more_to_do || no_queue_space || no_fifo_space) {
+                       /*
+                        * May need to queue more transactions as the request
+                        * queue or Tx FIFO empties. Enable the non-periodic
+                        * Tx FIFO empty interrupt. (Always use the half-empty
+                        * level to ensure that new requests are loaded as
+                        * soon as possible.)
+                        */
+                       gintmsk = dwc2_readl(hsotg, GINTMSK);
+                       gintmsk |= GINTSTS_NPTXFEMP;
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
+               } else {
+                       /*
+                        * Disable the Tx FIFO empty interrupt since there are
+                        * no more transactions that need to be queued right
+                        * now. This function is called from interrupt
+                        * handlers to queue more transactions as transfer
+                        * states change.
+                        */
+                       gintmsk = dwc2_readl(hsotg, GINTMSK);
+                       gintmsk &= ~GINTSTS_NPTXFEMP;
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
+               }
+       }
+}
+
+/**
+ * dwc2_hcd_queue_transactions() - Processes the currently active host channels
+ * and queues transactions for these channels to the DWC_otg controller. Called
+ * from the HCD interrupt handler functions.
+ *
+ * @hsotg:   The HCD state structure
+ * @tr_type: The type(s) of transactions to queue (non-periodic, periodic,
+ *           or both)
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
+                                enum dwc2_transaction_type tr_type)
+{
+#ifdef DWC2_DEBUG_SOF
+       dev_vdbg(hsotg->dev, "Queue Transactions\n");
+#endif
+       /* Process host channels associated with periodic transfers */
+       if (tr_type == DWC2_TRANSACTION_PERIODIC ||
+           tr_type == DWC2_TRANSACTION_ALL)
+               dwc2_process_periodic_channels(hsotg);
+
+       /* Process host channels associated with non-periodic transfers */
+       if (tr_type == DWC2_TRANSACTION_NON_PERIODIC ||
+           tr_type == DWC2_TRANSACTION_ALL) {
+               if (!list_empty(&hsotg->non_periodic_sched_active)) {
+                       dwc2_process_non_periodic_channels(hsotg);
+               } else {
+                       /*
+                        * Ensure NP Tx FIFO empty interrupt is disabled when
+                        * there are no non-periodic transfers to process
+                        */
+                       u32 gintmsk = dwc2_readl(hsotg, GINTMSK);
+
+                       gintmsk &= ~GINTSTS_NPTXFEMP;
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
+               }
+       }
+}
+
+STATIC void dwc2_conn_id_status_change(void *data)
+{
+       struct dwc2_hsotg *hsotg = data;
+
+       u32 count = 0;
+       u32 gotgctl;
+       unsigned long flags;
+
+       dev_dbg(hsotg->dev, "%s()\n", __func__);
+
+       gotgctl = dwc2_readl(hsotg, GOTGCTL);
+       dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl);
+       dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n",
+               !!(gotgctl & GOTGCTL_CONID_B));
+
+       /* B-Device connector (Device Mode) */
+       if (gotgctl & GOTGCTL_CONID_B) {
+               dwc2_vbus_supply_exit(hsotg);
+               /* Wait for switch to device mode */
+               dev_dbg(hsotg->dev, "connId B\n");
+               if (hsotg->bus_suspended) {
+                       dev_info(hsotg->dev,
+                                "Do port resume before switching to device mode\n");
+                       dwc2_port_resume(hsotg);
+               }
+               while (!dwc2_is_device_mode(hsotg)) {
+                       dev_info(hsotg->dev,
+                                "Waiting for Peripheral Mode, Mode=%s\n",
+                                dwc2_is_host_mode(hsotg) ? "Host" :
+                                "Peripheral");
+                       dwc2_msleep(20);
+                       /*
+                        * Sometimes the initial GOTGCTRL read is wrong, so
+                        * check it again and jump to host mode if that was
+                        * the case.
+                        */
+                       gotgctl = dwc2_readl(hsotg, GOTGCTL);
+                       if (!(gotgctl & GOTGCTL_CONID_B))
+                               goto host;
+                       if (++count > 250)
+                               break;
+               }
+               if (count > 250)
+                       dev_err(hsotg->dev,
+                               "Connection id status change timed out\n");
+
+               /*
+                * Exit Partial Power Down without restoring registers.
+                * No need to check the return value as registers
+                * are not being restored.
+                */
+               if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
+                       dwc2_exit_partial_power_down(hsotg, 0, false);
+
+               hsotg->op_state = OTG_STATE_B_PERIPHERAL;
+               dwc2_core_init(hsotg, false);
+               dwc2_enable_global_interrupts(hsotg);
+               spin_lock_irqsave(&hsotg->lock, flags);
+               dwc2_hsotg_core_init_disconnected(hsotg, false);
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               /* Enable ACG feature in device mode,if supported */
+               dwc2_enable_acg(hsotg);
+               dwc2_hsotg_core_connect(hsotg);
+       } else {
+host:
+               /* A-Device connector (Host Mode) */
+               dev_dbg(hsotg->dev, "connId A\n");
+               while (!dwc2_is_host_mode(hsotg)) {
+                       dev_info(hsotg->dev, "Waiting for Host Mode, Mode=%s\n",
+                                dwc2_is_host_mode(hsotg) ?
+                                "Host" : "Peripheral");
+                       dwc2_msleep(20);
+                       if (++count > 250)
+                               break;
+               }
+               if (count > 250)
+                       dev_err(hsotg->dev,
+                               "Connection id status change timed out\n");
+
+               spin_lock_irqsave(&hsotg->lock, flags);
+               dwc2_hsotg_disconnect(hsotg);
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+
+               hsotg->op_state = OTG_STATE_A_HOST;
+               /* Initialize the Core for Host mode */
+               dwc2_core_init(hsotg, false);
+               dwc2_enable_global_interrupts(hsotg);
+               dwc2_hcd_start(hsotg);
+       }
+}
+
+void dwc2_wakeup_detected(void *data)
+{
+       struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)data;
+       u32 hprt0;
+
+       dev_dbg(hsotg->dev, "%s()\n", __func__);
+
+       /*
+        * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
+        * so that OPT tests pass with all PHYs.)
+        */
+       hprt0 = dwc2_read_hprt0(hsotg);
+       dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0);
+       hprt0 &= ~HPRT0_RES;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+       dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n",
+               dwc2_readl(hsotg, HPRT0));
+
+       dwc2_hcd_rem_wakeup(hsotg);
+       hsotg->bus_suspended = false;
+
+       /* Change to L0 state */
+       hsotg->lx_state = DWC2_L0;
+}
+
+static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
+
+       return hcd->self.b_hnp_enable;
+#endif
+       return 0;
+}
+
+/**
+ * dwc2_port_suspend() - Put controller in suspend mode for host.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @windex: The control request wIndex field
+ *
+ * Return: non-zero if failed to enter suspend mode for host.
+ *
+ * This function is for entering Host mode suspend.
+ * Must NOT be called with interrupt disabled or spinlock held.
+ */
+int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
+{
+       unsigned long flags;
+       u32 pcgctl;
+       u32 gotgctl;
+       int ret = 0;
+
+       dev_dbg(hsotg->dev, "%s()\n", __func__);
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) {
+               gotgctl = dwc2_readl(hsotg, GOTGCTL);
+               gotgctl |= GOTGCTL_HSTSETHNPEN;
+               dwc2_writel(hsotg, gotgctl, GOTGCTL);
+               hsotg->op_state = OTG_STATE_A_SUSPEND;
+       }
+
+       switch (hsotg->params.power_down) {
+       case DWC2_POWER_DOWN_PARAM_PARTIAL:
+               ret = dwc2_enter_partial_power_down(hsotg);
+               if (ret)
+                       dev_err(hsotg->dev,
+                               "enter partial_power_down failed.\n");
+               break;
+       case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+               /*
+                * Perform spin unlock and lock because in
+                * "dwc2_host_enter_hibernation()" function there is a spinlock
+                * logic which prevents servicing of any IRQ during entering
+                * hibernation.
+                */
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               ret = dwc2_enter_hibernation(hsotg, 1);
+               if (ret)
+                       dev_err(hsotg->dev, "enter hibernation failed.\n");
+               spin_lock_irqsave(&hsotg->lock, flags);
+               break;
+       case DWC2_POWER_DOWN_PARAM_NONE:
+               /*
+                * If not hibernation nor partial power down are supported,
+                * clock gating is used to save power.
+                */
+               if (!hsotg->params.no_clock_gating)
+                       dwc2_host_enter_clock_gating(hsotg);
+               break;
+       }
+
+       /* For HNP the bus must be suspended for at least 200ms */
+       if (dwc2_host_is_b_hnp_enabled(hsotg)) {
+               pcgctl = dwc2_readl(hsotg, PCGCTL);
+               pcgctl &= ~PCGCTL_STOPPCLK;
+               dwc2_writel(hsotg, pcgctl, PCGCTL);
+
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+
+               dwc2_msleep(200);
+       } else {
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+       }
+
+       return ret;
+}
+
+/**
+ * dwc2_port_resume() - Exit controller from suspend mode for host.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Return: non-zero if failed to exit suspend mode for host.
+ *
+ * This function is for exiting Host mode suspend.
+ * Must NOT be called with interrupt disabled or spinlock held.
+ */
+int dwc2_port_resume(struct dwc2_hsotg *hsotg)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       switch (hsotg->params.power_down) {
+       case DWC2_POWER_DOWN_PARAM_PARTIAL:
+               ret = dwc2_exit_partial_power_down(hsotg, 0, true);
+               if (ret)
+                       dev_err(hsotg->dev,
+                               "exit partial_power_down failed.\n");
+               break;
+       case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+               /* Exit host hibernation. */
+               ret = dwc2_exit_hibernation(hsotg, 0, 0, 1);
+               if (ret)
+                       dev_err(hsotg->dev, "exit hibernation failed.\n");
+               break;
+       case DWC2_POWER_DOWN_PARAM_NONE:
+               /*
+                * If not hibernation nor partial power down are supported,
+                * port resume is done using the clock gating programming flow.
+                */
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               dwc2_host_exit_clock_gating(hsotg, 0);
+               spin_lock_irqsave(&hsotg->lock, flags);
+               break;
+       }
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return ret;
+}
+
+/* Handles hub class-specific requests */
+int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
+                        u16 wvalue, u16 windex, char *buf, u16 wlength)
+{
+       usb_hub_descriptor_t *hub_desc;
+       usb_port_status_t ps;
+       int retval = 0;
+       u32 hprt0;
+       u32 port_status;
+       u32 speed;
+       u32 pcgctl;
+       u32 pwr;
+
+       switch (typereq) {
+       case ClearHubFeature:
+               dev_dbg(hsotg->dev, "ClearHubFeature %1xh\n", wvalue);
+
+               switch (wvalue) {
+               case C_HUB_LOCAL_POWER:
+               case C_HUB_OVER_CURRENT:
+                       /* Nothing required here */
+                       break;
+
+               default:
+                       retval = -EINVAL;
+                       dev_err(hsotg->dev,
+                               "ClearHubFeature request %1xh unknown\n",
+                               wvalue);
+               }
+               break;
+
+       case ClearPortFeature:
+//             if (wvalue != USB_PORT_FEAT_L1)
+                       if (!windex || windex > 1)
+                               goto error;
+               switch (wvalue) {
+               case USB_PORT_FEAT_ENABLE:
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_ENABLE\n");
+                       hprt0 = dwc2_read_hprt0(hsotg);
+                       hprt0 |= HPRT0_ENA;
+                       dwc2_writel(hsotg, hprt0, HPRT0);
+                       break;
+
+               case USB_PORT_FEAT_SUSPEND:
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
+
+                       if (hsotg->bus_suspended)
+                               retval = dwc2_port_resume(hsotg);
+                       break;
+
+               case USB_PORT_FEAT_POWER:
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_POWER\n");
+                       hprt0 = dwc2_read_hprt0(hsotg);
+                       pwr = hprt0 & HPRT0_PWR;
+                       hprt0 &= ~HPRT0_PWR;
+                       dwc2_writel(hsotg, hprt0, HPRT0);
+                       if (pwr)
+                               dwc2_vbus_supply_exit(hsotg);
+                       break;
+
+               case USB_PORT_FEAT_INDICATOR:
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_INDICATOR\n");
+                       /* Port indicator not supported */
+                       break;
+
+               case USB_PORT_FEAT_C_CONNECTION:
+                       /*
+                        * Clears driver's internal Connect Status Change flag
+                        */
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n");
+                       hsotg->flags.b.port_connect_status_change = 0;
+                       break;
+
+               case USB_PORT_FEAT_C_RESET:
+                       /* Clears driver's internal Port Reset Change flag */
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_C_RESET\n");
+                       hsotg->flags.b.port_reset_change = 0;
+                       break;
+
+               case USB_PORT_FEAT_C_ENABLE:
+                       /*
+                        * Clears the driver's internal Port Enable/Disable
+                        * Change flag
+                        */
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n");
+                       hsotg->flags.b.port_enable_change = 0;
+                       break;
+
+               case USB_PORT_FEAT_C_SUSPEND:
+                       /*
+                        * Clears the driver's internal Port Suspend Change
+                        * flag, which is set when resume signaling on the host
+                        * port is complete
+                        */
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n");
+                       hsotg->flags.b.port_suspend_change = 0;
+                       break;
+
+               case USB_PORT_FEAT_C_PORT_L1:
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_C_PORT_L1\n");
+                       hsotg->flags.b.port_l1_change = 0;
+                       break;
+
+               case USB_PORT_FEAT_C_OVER_CURRENT:
+                       dev_dbg(hsotg->dev,
+                               "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n");
+                       hsotg->flags.b.port_over_current_change = 0;
+                       break;
+
+               default:
+                       retval = -EINVAL;
+                       dev_err(hsotg->dev,
+                               "ClearPortFeature request %1xh unknown or unsupported\n",
+                               wvalue);
+               }
+               break;
+
+       case GetHubDescriptor:
+               dev_dbg(hsotg->dev, "GetHubDescriptor\n");
+               hub_desc = (usb_hub_descriptor_t *)buf;
+               hub_desc->bDescLength = 9;
+               hub_desc->bDescriptorType = USB_DT_HUB;
+               hub_desc->bNbrPorts = 1;
+               USETW(hub_desc->wHubCharacteristics, HUB_CHAR_COMMON_LPSM |
+                                   HUB_CHAR_INDV_PORT_OCPM);
+               hub_desc->bPwrOn2PwrGood = 1;
+               hub_desc->bHubContrCurrent = 0;
+               hub_desc->DeviceRemovable[0] = 0;
+               hub_desc->DeviceRemovable[1] = 0xff;
+               break;
+
+       case GetHubStatus:
+               dev_dbg(hsotg->dev, "GetHubStatus\n");
+               memset(buf, 0, 4);
+               break;
+
+       case GetPortStatus:
+               dev_vdbg(hsotg->dev,
+                        "GetPortStatus wIndex=0x%04x flags=0x%08x\n", windex,
+                        hsotg->flags.d32);
+               if (!windex || windex > 1)
+                       goto error;
+
+               port_status = 0;
+               if (hsotg->flags.b.port_connect_status_change)
+                       port_status |= USB_PORT_STAT_C_CONNECTION;
+               if (hsotg->flags.b.port_enable_change)
+                       port_status |= USB_PORT_STAT_C_ENABLE;
+               if (hsotg->flags.b.port_suspend_change)
+                       port_status |= USB_PORT_STAT_C_SUSPEND;
+               if (hsotg->flags.b.port_l1_change)
+                       port_status |= USB_PORT_STAT_C_L1;
+               if (hsotg->flags.b.port_reset_change)
+                       port_status |= USB_PORT_STAT_C_RESET;
+               if (hsotg->flags.b.port_over_current_change) {
+                       dev_warn(hsotg->dev, "Overcurrent change detected\n");
+                       port_status |= USB_PORT_STAT_C_OVERCURRENT;
+               }
+               USETW(ps.wPortChange, port_status);
+               dev_vdbg(hsotg->dev, "wPortChange=%04x\n", port_status);
+
+               if (!hsotg->flags.b.port_connect_status) {
+                       /*
+                        * The port is disconnected, which means the core is
+                        * either in device mode or it soon will be. Just
+                        * return 0's for the remainder of the port status
+                        * since the port register can't be read if the core
+                        * is in device mode.
+                        */
+                       USETW(ps.wPortStatus, 0);
+                       memcpy(buf, &ps, sizeof(ps));
+                       break;
+               }
+
+               port_status = 0;
+               hprt0 = dwc2_readl(hsotg, HPRT0);
+               dev_vdbg(hsotg->dev, "  HPRT0: 0x%08x\n", hprt0);
+
+               if (hprt0 & HPRT0_CONNSTS)
+                       port_status |= USB_PORT_STAT_CONNECTION;
+               if (hprt0 & HPRT0_ENA)
+                       port_status |= USB_PORT_STAT_ENABLE;
+               if (hprt0 & HPRT0_SUSP)
+                       port_status |= USB_PORT_STAT_SUSPEND;
+               if (hprt0 & HPRT0_OVRCURRACT)
+                       port_status |= USB_PORT_STAT_OVERCURRENT;
+               if (hprt0 & HPRT0_RST)
+                       port_status |= USB_PORT_STAT_RESET;
+               if (hprt0 & HPRT0_PWR)
+                       port_status |= USB_PORT_STAT_POWER;
+
+               speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+               if (speed == HPRT0_SPD_HIGH_SPEED)
+                       port_status |= USB_PORT_STAT_HIGH_SPEED;
+               else if (speed == HPRT0_SPD_LOW_SPEED)
+                       port_status |= USB_PORT_STAT_LOW_SPEED;
+
+               if (hprt0 & HPRT0_TSTCTL_MASK)
+                       port_status |= USB_PORT_STAT_TEST;
+               /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
+               USETW(ps.wPortStatus, port_status);
+
+               if (hsotg->params.dma_desc_fs_enable) {
+                       /*
+                        * Enable descriptor DMA only if a full speed
+                        * device is connected.
+                        */
+                       if (hsotg->new_connection &&
+                           ((port_status &
+                             (USB_PORT_STAT_CONNECTION |
+                              USB_PORT_STAT_HIGH_SPEED |
+                              USB_PORT_STAT_LOW_SPEED)) ==
+                              USB_PORT_STAT_CONNECTION)) {
+                               u32 hcfg;
+
+                               dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
+                               hsotg->params.dma_desc_enable = true;
+                               hcfg = dwc2_readl(hsotg, HCFG);
+                               hcfg |= HCFG_DESCDMA;
+                               dwc2_writel(hsotg, hcfg, HCFG);
+                               hsotg->new_connection = false;
+                       }
+               }
+
+               dev_vdbg(hsotg->dev, "port_status=%08x\n", port_status);
+               memcpy(buf, &ps, sizeof(ps));
+               break;
+
+       case SetHubFeature:
+               dev_dbg(hsotg->dev, "SetHubFeature\n");
+               /* No HUB features supported */
+               break;
+
+       case SetPortFeature:
+               dev_dbg(hsotg->dev, "SetPortFeature\n");
+               if (wvalue != USB_PORT_FEAT_TEST && (!windex || windex > 1))
+                       goto error;
+
+               if (!hsotg->flags.b.port_connect_status) {
+                       /*
+                        * The port is disconnected, which means the core is
+                        * either in device mode or it soon will be. Just
+                        * return without doing anything since the port
+                        * register can't be written if the core is in device
+                        * mode.
+                        */
+                       break;
+               }
+
+               switch (wvalue) {
+               case USB_PORT_FEAT_SUSPEND:
                        dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_INDICATOR\n");
-                       /* Port indicator not supported */
+                               "SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
+                       if (windex != hsotg->otg_port)
+                               goto error;
+                       if (!hsotg->bus_suspended)
+                               retval = dwc2_port_suspend(hsotg, windex);
+                       break;
+
+               case USB_PORT_FEAT_POWER:
+                       dev_dbg(hsotg->dev,
+                               "SetPortFeature - USB_PORT_FEAT_POWER\n");
+                       hprt0 = dwc2_read_hprt0(hsotg);
+                       pwr = hprt0 & HPRT0_PWR;
+                       hprt0 |= HPRT0_PWR;
+                       dwc2_writel(hsotg, hprt0, HPRT0);
+                       if (!pwr)
+                               dwc2_vbus_supply_init(hsotg);
+                       break;
+
+               case USB_PORT_FEAT_RESET:
+                       dev_dbg(hsotg->dev,
+                               "SetPortFeature - USB_PORT_FEAT_RESET\n");
+
+                       hprt0 = dwc2_read_hprt0(hsotg);
+
+                       if (hsotg->hibernated) {
+                               retval = dwc2_exit_hibernation(hsotg, 0, 1, 1);
+                               if (retval)
+                                       dev_err(hsotg->dev,
+                                               "exit hibernation failed\n");
+                       }
+
+                       if (hsotg->in_ppd) {
+                               retval = dwc2_exit_partial_power_down(hsotg, 1,
+                                                                     true);
+                               if (retval)
+                                       dev_err(hsotg->dev,
+                                               "exit partial_power_down failed\n");
+                       }
+
+                       if (hsotg->params.power_down ==
+                           DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
+                               dwc2_host_exit_clock_gating(hsotg, 0);
+
+                       pcgctl = dwc2_readl(hsotg, PCGCTL);
+                       pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK);
+                       dwc2_writel(hsotg, pcgctl, PCGCTL);
+                       /* ??? Original driver does this */
+                       dwc2_writel(hsotg, 0, PCGCTL);
+
+                       hprt0 = dwc2_read_hprt0(hsotg);
+                       pwr = hprt0 & HPRT0_PWR;
+                       /* Clear suspend bit if resetting from suspend state */
+                       hprt0 &= ~HPRT0_SUSP;
+
+                       /*
+                        * When B-Host the Port reset bit is set in the Start
+                        * HCD Callback function, so that the reset is started
+                        * within 1ms of the HNP success interrupt
+                        */
+                       if (!dwc2_hcd_is_b_host(hsotg)) {
+                               hprt0 |= HPRT0_PWR | HPRT0_RST;
+                               dev_dbg(hsotg->dev,
+                                       "In host mode, hprt0=%08x\n", hprt0);
+                               dwc2_writel(hsotg, hprt0, HPRT0);
+                               if (!pwr)
+                                       dwc2_vbus_supply_init(hsotg);
+                       }
+
+                       /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
+                       dwc2_msleep(50);
+                       hprt0 &= ~HPRT0_RST;
+                       dwc2_writel(hsotg, hprt0, HPRT0);
+                       hsotg->lx_state = DWC2_L0; /* Now back to On state */
+                       break;
+
+               case USB_PORT_FEAT_INDICATOR:
+                       dev_dbg(hsotg->dev,
+                               "SetPortFeature - USB_PORT_FEAT_INDICATOR\n");
+                       /* Not supported */
+                       break;
+
+               case USB_PORT_FEAT_TEST:
+                       hprt0 = dwc2_read_hprt0(hsotg);
+                       dev_dbg(hsotg->dev,
+                               "SetPortFeature - USB_PORT_FEAT_TEST\n");
+                       hprt0 &= ~HPRT0_TSTCTL_MASK;
+                       hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
+                       dwc2_writel(hsotg, hprt0, HPRT0);
+                       break;
+
+               default:
+                       retval = -EINVAL;
+                       dev_err(hsotg->dev,
+                               "SetPortFeature %1xh unknown or unsupported\n",
+                               wvalue);
                        break;
+               }
+               break;
+
+       default:
+error:
+               retval = -EINVAL;
+               dev_dbg(hsotg->dev,
+                       "Unknown hub control request: %1xh wIndex: %1xh wValue: %1xh\n",
+                       typereq, windex, wvalue);
+               break;
+       }
+
+       return retval;
+}
+
+#if 0
+static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port)
+{
+       int retval;
+
+       if (port != 1)
+               return -EINVAL;
+
+       retval = (hsotg->flags.b.port_connect_status_change ||
+                 hsotg->flags.b.port_reset_change ||
+                 hsotg->flags.b.port_enable_change ||
+                 hsotg->flags.b.port_suspend_change ||
+                 hsotg->flags.b.port_over_current_change);
+
+       if (retval) {
+               dev_dbg(hsotg->dev,
+                       "DWC OTG HCD HUB STATUS DATA: Root port status changed\n");
+               dev_dbg(hsotg->dev, "  port_connect_status_change: %d\n",
+                       hsotg->flags.b.port_connect_status_change);
+               dev_dbg(hsotg->dev, "  port_reset_change: %d\n",
+                       hsotg->flags.b.port_reset_change);
+               dev_dbg(hsotg->dev, "  port_enable_change: %d\n",
+                       hsotg->flags.b.port_enable_change);
+               dev_dbg(hsotg->dev, "  port_suspend_change: %d\n",
+                       hsotg->flags.b.port_suspend_change);
+               dev_dbg(hsotg->dev, "  port_over_current_change: %d\n",
+                       hsotg->flags.b.port_over_current_change);
+       }
+
+       return retval;
+}
+#endif
+
+int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
+{
+       u32 hfnum = dwc2_readl(hsotg, HFNUM);
+
+#ifdef DWC2_DEBUG_SOF
+       dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n",
+                (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT);
+#endif
+       return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+}
+
+int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us)
+{
+       u32 hprt = dwc2_readl(hsotg, HPRT0);
+       u32 hfir = dwc2_readl(hsotg, HFIR);
+       u32 hfnum = dwc2_readl(hsotg, HFNUM);
+       unsigned int us_per_frame;
+       unsigned int frame_number;
+       unsigned int remaining;
+       unsigned int interval;
+       unsigned int phy_clks;
+
+       /* High speed has 125 us per (micro) frame; others are 1 ms per */
+       us_per_frame = (hprt & HPRT0_SPD_MASK) ? 1000 : 125;
+
+       /* Extract fields */
+       frame_number = (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+       remaining = (hfnum & HFNUM_FRREM_MASK) >> HFNUM_FRREM_SHIFT;
+       interval = (hfir & HFIR_FRINT_MASK) >> HFIR_FRINT_SHIFT;
+
+       /*
+        * Number of phy clocks since the last tick of the frame number after
+        * "us" has passed.
+        */
+       phy_clks = (interval - remaining) +
+                  DIV_ROUND_UP(interval * us, us_per_frame);
+
+       return dwc2_frame_num_inc(frame_number, phy_clks / interval);
+}
+
+int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg)
+{
+       return hsotg->op_state == OTG_STATE_B_HOST;
+}
+
+STATIC struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg,
+                                              int iso_desc_count,
+                                              gfp_t mem_flags)
+{
+       struct dwc2_hcd_urb *urb;
+
+       u32 size = sizeof(*urb) + iso_desc_count *
+                  sizeof(struct dwc2_hcd_iso_packet_desc);
+       urb = malloc(size, M_USBHC, M_ZERO | mem_flags);
+       if (urb)
+               urb->packet_count = iso_desc_count;
+       return urb;
+}
+
+/* Required for OpenBSD */
+void dwc2_hcd_urb_free(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
+    int iso_desc_count)
+{
+       u32 size = sizeof(*urb) + iso_desc_count *
+                  sizeof(struct dwc2_hcd_iso_packet_desc);
+
+       free(urb, M_USBHC, size);
+}
+
+void dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *hsotg,
+                              struct dwc2_hcd_urb *urb, u8 dev_addr,
+                              u8 ep_num, u8 ep_type, u8 ep_dir,
+                              u16 maxp, u16 maxp_mult)
+{
+       if (dbg_perio() ||
+           ep_type == USB_ENDPOINT_XFER_BULK ||
+           ep_type == USB_ENDPOINT_XFER_CONTROL)
+               dev_vdbg(hsotg->dev,
+                        "addr=%d, ep_num=%d, ep_dir=%1x, ep_type=%1x, maxp=%d (%d mult)\n",
+                        dev_addr, ep_num, ep_dir, ep_type, maxp, maxp_mult);
+       urb->pipe_info.dev_addr = dev_addr;
+       urb->pipe_info.ep_num = ep_num;
+       urb->pipe_info.pipe_type = ep_type;
+       urb->pipe_info.pipe_dir = ep_dir;
+       urb->pipe_info.maxp = maxp;
+       urb->pipe_info.maxp_mult = maxp_mult;
+}
+
+/*
+ * NOTE: This function will be removed once the peripheral controller code
+ * is integrated and the driver is stable
+ */
+void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
+{
+#ifdef DWC2_DEBUG
+       struct dwc2_host_chan *chan;
+       struct dwc2_hcd_urb *urb;
+       struct dwc2_qtd *qtd;
+       int num_channels;
+       u32 np_tx_status;
+       u32 p_tx_status;
+       int i;
+
+       num_channels = hsotg->params.host_channels;
+       dev_dbg(hsotg->dev, "\n");
+       dev_dbg(hsotg->dev,
+               "************************************************************\n");
+       dev_dbg(hsotg->dev, "HCD State:\n");
+       dev_dbg(hsotg->dev, "  Num channels: %d\n", num_channels);
+
+       for (i = 0; i < num_channels; i++) {
+               chan = hsotg->hc_ptr_array[i];
+               dev_dbg(hsotg->dev, "  Channel %d:\n", i);
+               dev_dbg(hsotg->dev,
+                       "    dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+                       chan->dev_addr, chan->ep_num, chan->ep_is_in);
+               dev_dbg(hsotg->dev, "    speed: %d\n", chan->speed);
+               dev_dbg(hsotg->dev, "    ep_type: %d\n", chan->ep_type);
+               dev_dbg(hsotg->dev, "    max_packet: %d\n", chan->max_packet);
+               dev_dbg(hsotg->dev, "    data_pid_start: %d\n",
+                       chan->data_pid_start);
+               dev_dbg(hsotg->dev, "    multi_count: %d\n", chan->multi_count);
+               dev_dbg(hsotg->dev, "    xfer_started: %d\n",
+                       chan->xfer_started);
+               dev_dbg(hsotg->dev, "    xfer_buf: %p\n", chan->xfer_buf);
+               dev_dbg(hsotg->dev, "    xfer_dma: %08lx\n",
+                       (unsigned long)chan->xfer_dma);
+               dev_dbg(hsotg->dev, "    xfer_len: %d\n", chan->xfer_len);
+               dev_dbg(hsotg->dev, "    xfer_count: %d\n", chan->xfer_count);
+               dev_dbg(hsotg->dev, "    halt_on_queue: %d\n",
+                       chan->halt_on_queue);
+               dev_dbg(hsotg->dev, "    halt_pending: %d\n",
+                       chan->halt_pending);
+               dev_dbg(hsotg->dev, "    halt_status: %d\n", chan->halt_status);
+               dev_dbg(hsotg->dev, "    do_split: %d\n", chan->do_split);
+               dev_dbg(hsotg->dev, "    complete_split: %d\n",
+                       chan->complete_split);
+               dev_dbg(hsotg->dev, "    hub_addr: %d\n", chan->hub_addr);
+               dev_dbg(hsotg->dev, "    hub_port: %d\n", chan->hub_port);
+               dev_dbg(hsotg->dev, "    xact_pos: %d\n", chan->xact_pos);
+               dev_dbg(hsotg->dev, "    requests: %d\n", chan->requests);
+               dev_dbg(hsotg->dev, "    qh: %p\n", chan->qh);
+
+               if (chan->xfer_started) {
+                       u32 hfnum, hcchar, hctsiz, hcint, hcintmsk;
+
+                       hfnum = dwc2_readl(hsotg, HFNUM);
+                       hcchar = dwc2_readl(hsotg, HCCHAR(i));
+                       hctsiz = dwc2_readl(hsotg, HCTSIZ(i));
+                       hcint = dwc2_readl(hsotg, HCINT(i));
+                       hcintmsk = dwc2_readl(hsotg, HCINTMSK(i));
+                       dev_dbg(hsotg->dev, "    hfnum: 0x%08x\n", hfnum);
+                       dev_dbg(hsotg->dev, "    hcchar: 0x%08x\n", hcchar);
+                       dev_dbg(hsotg->dev, "    hctsiz: 0x%08x\n", hctsiz);
+                       dev_dbg(hsotg->dev, "    hcint: 0x%08x\n", hcint);
+                       dev_dbg(hsotg->dev, "    hcintmsk: 0x%08x\n", hcintmsk);
+               }
+
+               if (!(chan->xfer_started && chan->qh))
+                       continue;
+
+               list_for_each_entry(qtd, &chan->qh->qtd_list, qtd_list_entry) {
+                       if (!qtd->in_process)
+                               break;
+                       urb = qtd->urb;
+                       dev_dbg(hsotg->dev, "    URB Info:\n");
+                       dev_dbg(hsotg->dev, "      qtd: %p, urb: %p\n",
+                               qtd, urb);
+                       if (urb) {
+                               dev_dbg(hsotg->dev,
+                                       "      Dev: %d, EP: %d %s\n",
+                                       dwc2_hcd_get_dev_addr(&urb->pipe_info),
+                                       dwc2_hcd_get_ep_num(&urb->pipe_info),
+                                       dwc2_hcd_is_pipe_in(&urb->pipe_info) ?
+                                       "IN" : "OUT");
+                               dev_dbg(hsotg->dev,
+                                       "      Max packet size: %d (%d mult)\n",
+                                       dwc2_hcd_get_maxp(&urb->pipe_info),
+                                       dwc2_hcd_get_maxp_mult(&urb->pipe_info));
+                               dev_dbg(hsotg->dev,
+                                       "      transfer_buffer: %p\n",
+                                       urb->buf);
+                               dev_dbg(hsotg->dev,
+                                       "      transfer_dma: %08lx\n",
+                                       (unsigned long)urb->dma);
+                               dev_dbg(hsotg->dev,
+                                       "      transfer_buffer_length: %d\n",
+                                       urb->length);
+                               dev_dbg(hsotg->dev, "      actual_length: %d\n",
+                                       urb->actual_length);
+                       }
+               }
+       }
+
+       dev_dbg(hsotg->dev, "  non_periodic_channels: %d\n",
+               hsotg->non_periodic_channels);
+       dev_dbg(hsotg->dev, "  periodic_channels: %d\n",
+               hsotg->periodic_channels);
+       dev_dbg(hsotg->dev, "  periodic_usecs: %d\n", hsotg->periodic_usecs);
+       np_tx_status = dwc2_readl(hsotg, GNPTXSTS);
+       dev_dbg(hsotg->dev, "  NP Tx Req Queue Space Avail: %d\n",
+               (np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
+       dev_dbg(hsotg->dev, "  NP Tx FIFO Space Avail: %d\n",
+               (np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
+       p_tx_status = dwc2_readl(hsotg, HPTXSTS);
+       dev_dbg(hsotg->dev, "  P Tx Req Queue Space Avail: %d\n",
+               (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
+       dev_dbg(hsotg->dev, "  P Tx FIFO Space Avail: %d\n",
+               (p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
+       dwc2_dump_global_registers(hsotg);
+       dwc2_dump_host_registers(hsotg);
+       dev_dbg(hsotg->dev,
+               "************************************************************\n");
+       dev_dbg(hsotg->dev, "\n");
+#endif
+}
+
+struct wrapper_priv_data {
+       struct dwc2_hsotg *hsotg;
+};
+
+#if 0
+/* Gets the dwc2_hsotg from a usb_hcd */
+static struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *hcd)
+{
+       struct wrapper_priv_data *p;
+
+       p = (struct wrapper_priv_data *)&hcd->hcd_priv;
+       return p->hsotg;
+}
+#endif
+
+/**
+ * dwc2_host_get_tt_info() - Get the dwc2_tt associated with context
+ *
+ * This will get the dwc2_tt structure (and ttport) associated with the given
+ * context (which is really just a struct urb pointer).
+ *
+ * The first time this is called for a given TT we allocate memory for our
+ * structure.  When everyone is done and has called dwc2_host_put_tt_info()
+ * then the refcount for the structure will go to 0 and we'll free it.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @context:   The priv pointer from a struct dwc2_hcd_urb.
+ * @mem_flags: Flags for allocating memory.
+ * @ttport:    We'll return this device's port number here.  That's used to
+ *             reference into the bitmap if we're on a multi_tt hub.
+ *
+ * Return: a pointer to a struct dwc2_tt.  Don't forget to call
+ *         dwc2_host_put_tt_info()!  Returns NULL upon memory alloc failure.
+ */
+
+struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
+                                     gfp_t mem_flags, int *ttport)
+{
+       struct usbd_xfer *xfer = context;
+       struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
+       struct usbd_device *dev = dpipe->pipe.device;
+       struct dwc2_tt *dwc_tt = NULL;
+
+       if (dev->myhsport->tt) {
+               *ttport = dev->myhsport->portno;
+
+               dwc_tt = dev->myhsport->tt->hcpriv;
+               if (!dwc_tt) {
+                       size_t bitmap_size;
 
-               case USB_PORT_FEAT_C_CONNECTION:
                        /*
-                        * Clears driver's internal Connect Status Change flag
+                        * For single_tt we need one schedule.  For multi_tt
+                        * we need one per port.
                         */
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n");
-                       hsotg->flags.b.port_connect_status_change = 0;
-                       break;
+                       bitmap_size = DWC2_ELEMENTS_PER_LS_BITMAP *
+                                     sizeof(dwc_tt->periodic_bitmaps[0]);
+                       if (dev->myhsport->tt->hub->multi)
+                               bitmap_size *= USB_MAXCHILDREN; /* XXX */
+
+                       dwc_tt = malloc(sizeof(*dwc_tt) + bitmap_size, M_USBHC,
+                           mem_flags);
+
+                       if (!dwc_tt)
+                               return NULL;
+
+                       dwc_tt->usb_tt = dev->myhsport->tt;
+                       dwc_tt->usb_tt->hcpriv = dwc_tt;
+               }
+
+               dwc_tt->refcount++;
+       }
+
+       return dwc_tt;
+}
+
+/**
+ * dwc2_host_put_tt_info() - Put the dwc2_tt from dwc2_host_get_tt_info()
+ *
+ * Frees resources allocated by dwc2_host_get_tt_info() if all current holders
+ * of the structure are done.
+ *
+ * It's OK to call this with NULL.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @dwc_tt:    The pointer returned by dwc2_host_get_tt_info.
+ */
+void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg, struct dwc2_tt *dwc_tt)
+{
+       /* Model kfree and make put of NULL a no-op */
+       if (!dwc_tt)
+               return;
+
+       WARN_ON(dwc_tt->refcount < 1);
+
+       dwc_tt->refcount--;
+       if (!dwc_tt->refcount) {
+               dwc_tt->usb_tt->hcpriv = NULL;
+               free(dwc_tt, M_USBHC, 0);
+       }
+}
+
+int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
+{
+       struct usbd_xfer *xfer = context;
+       struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
+       struct usbd_device *dev = dpipe->pipe.device;
+
+       return dev->speed;
+}
+
+STATIC void dwc2_allocate_bus_bandwidth(struct dwc2_hsotg *hsotg, u16 bw,
+                                       struct usbd_xfer *xfer)
+{
+}
+
+STATIC void dwc2_free_bus_bandwidth(struct dwc2_hsotg *hsotg, u16 bw,
+                                   struct usbd_xfer *xfer)
+{
+}
+
+/*
+ * Sets the final status of an URB and returns it to the upper layer. Any
+ * required cleanup of the URB is performed.
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
+                       int status)
+{
+       struct usbd_xfer *xfer;
+       struct dwc2_xfer *dxfer;
+       struct dwc2_softc *sc;
+       usb_endpoint_descriptor_t *ed;
+       uint8_t xfertype;
+
+       if (!qtd) {
+               dev_dbg(hsotg->dev, "## %s: qtd is NULL ##\n", __func__);
+               return;
+       }
+
+       if (!qtd->urb) {
+               dev_dbg(hsotg->dev, "## %s: qtd->urb is NULL ##\n", __func__);
+               return;
+       }
+
+       xfer = qtd->urb->priv;
+       if (!xfer) {
+               dev_dbg(hsotg->dev, "## %s: urb->priv is NULL ##\n", __func__);
+               return;
+       }
+
+       dxfer = DWC2_XFER2DXFER(xfer);
+       sc = DWC2_XFER2SC(xfer);
+       ed = xfer->pipe->endpoint->edesc;
+       xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+
+       struct dwc2_hcd_urb *urb = qtd->urb;
+       xfer->actlen = dwc2_hcd_urb_get_actual_length(urb);
+#if 0
+       DPRINTFN(3, "xfer=%p actlen=%d\n", xfer, xfer->actlen);
+#endif
+       if (xfertype == UE_ISOCHRONOUS) {
+               xfer->actlen = 0;
+               for (size_t i = 0; i < xfer->nframes; ++i) {
+                       xfer->frlengths[i] =
+                           dwc2_hcd_urb_get_iso_desc_actual_length(urb, i);
+#if 0
+                       DPRINTFN(1, "xfer=%p frame=%zu length=%d\n", xfer, i,
+                           xfer->frlengths[i]);
+#endif
+                       xfer->actlen += xfer->frlengths[i];
+               }
+#if 0
+               DPRINTFN(1, "xfer=%p actlen=%d (isoc)\n", xfer, xfer->actlen);
+#endif
+       }
+
+       if (xfertype == UE_ISOCHRONOUS && dbg_perio()) {
+               for (size_t i = 0; i < xfer->nframes; i++)
+                       dev_vdbg(hsotg->dev, " ISO Desc %zu status %d\n",
+                           i, urb->iso_descs[i].status);
+       }
+
+       if (!status) {
+               if (!(xfer->flags & USBD_SHORT_XFER_OK) &&
+                   xfer->actlen < xfer->length)
+               status = -EIO;
+       }
+
+       switch (status) {
+       case 0:
+               dxfer->intr_status = USBD_NORMAL_COMPLETION;
+               break;
+       case -EPIPE:
+               dxfer->intr_status = USBD_STALLED;
+               break;
+       case -EPROTO:
+               dxfer->intr_status = USBD_INVAL;
+               break;
+       case -EIO:
+               dxfer->intr_status = USBD_IOERROR;
+               break;
+       case -EOVERFLOW:
+               dxfer->intr_status = USBD_IOERROR;
+               break;
+       default:
+               dxfer->intr_status = USBD_IOERROR;
+               printf("%s: unknown error status %d\n", __func__, status);
+       }
+
+       if (dxfer->intr_status == USBD_NORMAL_COMPLETION) {
+               /*
+                * control transfers with no data phase don't touch dmabuf, but
+                * everything else does.
+                */
+               if (!(xfertype == UE_CONTROL &&
+                   UGETW(xfer->request.wLength) == 0) &&
+                   xfer->actlen > 0 /* XXX PR/53503 */
+                   ) {
+                       int rd = usbd_xfer_isread(xfer);
+
+                       usb_syncmem(&xfer->dmabuf, 0, xfer->actlen,
+                           rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+               }
+       }
+
+       if (xfertype == UE_ISOCHRONOUS ||
+           xfertype == UE_INTERRUPT) {
+               struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
+
+               dwc2_free_bus_bandwidth(hsotg,
+                                       dwc2_hcd_get_ep_bandwidth(hsotg, dpipe),
+                                       xfer);
+       }
+
+       qtd->urb = NULL;
+       timeout_del(&xfer->timeout_handle);
+       usb_rem_task(xfer->device, &xfer->abort_task);
+       MUTEX_ASSERT_LOCKED(&hsotg->lock);
+
+       TAILQ_INSERT_TAIL(&sc->sc_complete, dxfer, xnext);
+
+       mtx_leave(&hsotg->lock);
+       usb_schedsoftintr(&sc->sc_bus);
+       mtx_enter(&hsotg->lock);
+}
+
+/*
+ * Work queue function for starting the HCD when A-Cable is connected
+ */
+STATIC void dwc2_hcd_start_func(void *data)
+{
+       struct dwc2_hsotg *hsotg = data;
+
+       dev_dbg(hsotg->dev, "%s() %p\n", __func__, hsotg);
+       dwc2_host_start(hsotg);
+}
+
+/*
+ * Reset work queue function
+ */
+STATIC void dwc2_hcd_reset_func(void *data)
+{
+       struct dwc2_hsotg *hsotg = data;
+       unsigned long flags;
+       u32 hprt0;
+
+       dev_dbg(hsotg->dev, "USB RESET function called\n");
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       hprt0 = dwc2_read_hprt0(hsotg);
+       hprt0 &= ~HPRT0_RST;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+       hsotg->flags.b.port_reset_change = 1;
+
+       dwc2_root_intr(hsotg->hsotg_sc); /* Required for OpenBSD */
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
+#if 0
+static void dwc2_hcd_phy_reset_func(struct work_struct *work)
+{
+       struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
+                                               phy_reset_work);
+       int ret;
+
+       ret = phy_reset(hsotg->phy);
+       if (ret)
+               dev_warn(hsotg->dev, "PHY reset failed\n");
+}
+#endif
+
+/*
+ * =========================================================================
+ *  Linux HC Driver Functions
+ * =========================================================================
+ */
+
+static int
+_dwc2_hcd_start(struct dwc2_hsotg *hsotg)
+{
+       unsigned long flags;
+
+       dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       hsotg->lx_state = DWC2_L0;
+
+       if (dwc2_is_device_mode(hsotg)) {
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               return 0;       /* why 0 ?? */
+       }
+
+       dwc2_hcd_reinit(hsotg);
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return 0;
+}
+#if 0
+/*
+ * Initializes the DWC_otg controller and its root hub and prepares it for host
+ * mode operation. Activates the root port. Returns 0 on success and a negative
+ * error code on failure.
+ */
+static int _dwc2_hcd_start(struct dwc2_hsotg *hsotg)
+{
+       struct usb_bus *bus = hcd_to_bus(hcd);
+       unsigned long flags;
+       u32 hprt0;
+       int ret;
+
+       dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       hsotg->lx_state = DWC2_L0;
+       hcd->state = HC_STATE_RUNNING;
+       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       if (dwc2_is_device_mode(hsotg)) {
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               return 0;       /* why 0 ?? */
+       }
+
+       dwc2_hcd_reinit(hsotg);
+
+       hprt0 = dwc2_read_hprt0(hsotg);
+       /* Has vbus power been turned on in dwc2_core_host_init ? */
+       if (hprt0 & HPRT0_PWR) {
+               /* Enable external vbus supply before resuming root hub */
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               ret = dwc2_vbus_supply_init(hsotg);
+               if (ret)
+                       return ret;
+               spin_lock_irqsave(&hsotg->lock, flags);
+       }
+
+       /* Initialize and connect root hub if one is not already attached */
+       if (bus->root_hub) {
+               dev_dbg(hsotg->dev, "DWC OTG HCD Has Root Hub\n");
+               /* Inform the HUB driver to resume */
+               usb_hcd_resume_root_hub(hcd);
+       }
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return 0;
+}
+
+/*
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+static void _dwc2_hcd_stop(struct usb_hcd *hcd)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       unsigned long flags;
+       u32 hprt0;
+
+       /* Turn off all host-specific interrupts */
+       dwc2_disable_host_interrupts(hsotg);
+
+       /* Wait for interrupt processing to finish */
+       synchronize_irq(hcd->irq);
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       hprt0 = dwc2_read_hprt0(hsotg);
+       /* Ensure hcd is disconnected */
+       dwc2_hcd_disconnect(hsotg, true);
+       dwc2_hcd_stop(hsotg);
+       hsotg->lx_state = DWC2_L3;
+       hcd->state = HC_STATE_HALT;
+       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       /* keep balanced supply init/exit by checking HPRT0_PWR */
+       if (hprt0 & HPRT0_PWR)
+               dwc2_vbus_supply_exit(hsotg);
+
+       usleep_range(1000, 3000);
+}
+
+static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       if (dwc2_is_device_mode(hsotg))
+               goto unlock;
+
+       if (hsotg->lx_state != DWC2_L0)
+               goto unlock;
+
+       if (!HCD_HW_ACCESSIBLE(hcd))
+               goto unlock;
+
+       if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
+               goto unlock;
+
+       if (hsotg->bus_suspended)
+               goto skip_power_saving;
+
+       if (hsotg->flags.b.port_connect_status == 0)
+               goto skip_power_saving;
+
+       switch (hsotg->params.power_down) {
+       case DWC2_POWER_DOWN_PARAM_PARTIAL:
+               /* Enter partial_power_down */
+               ret = dwc2_enter_partial_power_down(hsotg);
+               if (ret)
+                       dev_err(hsotg->dev,
+                               "enter partial_power_down failed\n");
+               /* After entering suspend, hardware is not accessible */
+               clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+               break;
+       case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+               /* Enter hibernation */
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               ret = dwc2_enter_hibernation(hsotg, 1);
+               if (ret)
+                       dev_err(hsotg->dev, "enter hibernation failed\n");
+               spin_lock_irqsave(&hsotg->lock, flags);
+
+               /* After entering suspend, hardware is not accessible */
+               clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+               break;
+       case DWC2_POWER_DOWN_PARAM_NONE:
+               /*
+                * If not hibernation nor partial power down are supported,
+                * clock gating is used to save power.
+                */
+               if (!hsotg->params.no_clock_gating) {
+                       dwc2_host_enter_clock_gating(hsotg);
+
+                       /* After entering suspend, hardware is not accessible */
+                       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+               }
+               break;
+       default:
+               goto skip_power_saving;
+       }
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       dwc2_vbus_supply_exit(hsotg);
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       /* Ask phy to be suspended */
+       if (!IS_ERR_OR_NULL(hsotg->uphy)) {
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               usb_phy_set_suspend(hsotg->uphy, true);
+               spin_lock_irqsave(&hsotg->lock, flags);
+       }
+
+skip_power_saving:
+       hsotg->lx_state = DWC2_L2;
+unlock:
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return ret;
+}
+
+static int _dwc2_hcd_resume(struct usb_hcd *hcd)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       unsigned long flags;
+       u32 hprt0;
+       int ret = 0;
 
-               case USB_PORT_FEAT_C_RESET:
-                       /* Clears driver's internal Port Reset Change flag */
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_C_RESET\n");
-                       hsotg->flags.b.port_reset_change = 0;
-                       break;
+       spin_lock_irqsave(&hsotg->lock, flags);
 
-               case USB_PORT_FEAT_C_ENABLE:
-                       /*
-                        * Clears the driver's internal Port Enable/Disable
-                        * Change flag
-                        */
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n");
-                       hsotg->flags.b.port_enable_change = 0;
-                       break;
+       if (dwc2_is_device_mode(hsotg))
+               goto unlock;
 
-               case USB_PORT_FEAT_C_SUSPEND:
-                       /*
-                        * Clears the driver's internal Port Suspend Change
-                        * flag, which is set when resume signaling on the host
-                        * port is complete
-                        */
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n");
-                       hsotg->flags.b.port_suspend_change = 0;
-                       break;
+       if (hsotg->lx_state != DWC2_L2)
+               goto unlock;
 
-               case USB_PORT_FEAT_C_PORT_L1:
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_C_PORT_L1\n");
-                       hsotg->flags.b.port_l1_change = 0;
-                       break;
+       hprt0 = dwc2_read_hprt0(hsotg);
 
-               case USB_PORT_FEAT_C_OVER_CURRENT:
-                       dev_dbg(hsotg->dev,
-                               "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n");
-                       hsotg->flags.b.port_over_current_change = 0;
-                       break;
+       /*
+        * Added port connection status checking which prevents exiting from
+        * Partial Power Down mode from _dwc2_hcd_resume() if not in Partial
+        * Power Down mode.
+        */
+       if (hprt0 & HPRT0_CONNSTS) {
+               hsotg->lx_state = DWC2_L0;
+               goto unlock;
+       }
 
-               default:
-                       retval = -EINVAL;
+       switch (hsotg->params.power_down) {
+       case DWC2_POWER_DOWN_PARAM_PARTIAL:
+               ret = dwc2_exit_partial_power_down(hsotg, 0, true);
+               if (ret)
                        dev_err(hsotg->dev,
-                               "ClearPortFeature request %1xh unknown or unsupported\n",
-                               wvalue);
-               }
+                               "exit partial_power_down failed\n");
+               /*
+                * Set HW accessible bit before powering on the controller
+                * since an interrupt may rise.
+                */
+               set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
                break;
+       case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+               ret = dwc2_exit_hibernation(hsotg, 0, 0, 1);
+               if (ret)
+                       dev_err(hsotg->dev, "exit hibernation failed.\n");
 
-       case GetHubDescriptor:
-               dev_dbg(hsotg->dev, "GetHubDescriptor\n");
-               hub_desc = (usb_hub_descriptor_t *)buf;
-               hub_desc->bDescLength = 9;
-               hub_desc->bDescriptorType = USB_DT_HUB;
-               hub_desc->bNbrPorts = 1;
-               USETW(hub_desc->wHubCharacteristics, HUB_CHAR_COMMON_LPSM |
-                                   HUB_CHAR_INDV_PORT_OCPM);
-               hub_desc->bPwrOn2PwrGood = 1;
-               hub_desc->bHubContrCurrent = 0;
-               hub_desc->DeviceRemovable[0] = 0;
-               hub_desc->DeviceRemovable[1] = 0xff;
+               /*
+                * Set HW accessible bit before powering on the controller
+                * since an interrupt may rise.
+                */
+               set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
                break;
+       case DWC2_POWER_DOWN_PARAM_NONE:
+               /*
+                * If not hibernation nor partial power down are supported,
+                * port resume is done using the clock gating programming flow.
+                */
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               dwc2_host_exit_clock_gating(hsotg, 0);
 
-       case GetHubStatus:
-               dev_dbg(hsotg->dev, "GetHubStatus\n");
-               memset(buf, 0, 4);
+               /*
+                * Initialize the Core for Host mode, as after system resume
+                * the global interrupts are disabled.
+                */
+               dwc2_core_init(hsotg, false);
+               dwc2_enable_global_interrupts(hsotg);
+               dwc2_hcd_reinit(hsotg);
+               spin_lock_irqsave(&hsotg->lock, flags);
+
+               /*
+                * Set HW accessible bit before powering on the controller
+                * since an interrupt may rise.
+                */
+               set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
                break;
+       default:
+               hsotg->lx_state = DWC2_L0;
+               goto unlock;
+       }
 
-       case GetPortStatus:
-               dev_vdbg(hsotg->dev,
-                        "GetPortStatus wIndex=0x%04x flags=0x%08x\n", windex,
-                        hsotg->flags.d32);
-               if (!windex || windex > 1)
-                       goto error;
+       /* Change Root port status, as port status change occurred after resume.*/
+       hsotg->flags.b.port_suspend_change = 1;
 
-               port_status = 0;
-               if (hsotg->flags.b.port_connect_status_change)
-                       port_status |= USB_PORT_STAT_C_CONNECTION;
-               if (hsotg->flags.b.port_enable_change)
-                       port_status |= USB_PORT_STAT_C_ENABLE;
-               if (hsotg->flags.b.port_suspend_change)
-                       port_status |= USB_PORT_STAT_C_SUSPEND;
-               if (hsotg->flags.b.port_l1_change)
-                       port_status |= USB_PORT_STAT_C_L1;
-               if (hsotg->flags.b.port_reset_change)
-                       port_status |= USB_PORT_STAT_C_RESET;
-               if (hsotg->flags.b.port_over_current_change) {
-                       dev_warn(hsotg->dev, "Overcurrent change detected\n");
-                       port_status |= USB_PORT_STAT_C_OVERCURRENT;
-               }
-               USETW(ps.wPortChange, port_status);
+       /*
+        * Enable power if not already done.
+        * This must not be spinlocked since duration
+        * of this call is unknown.
+        */
+       if (!IS_ERR_OR_NULL(hsotg->uphy)) {
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+               usb_phy_set_suspend(hsotg->uphy, false);
+               spin_lock_irqsave(&hsotg->lock, flags);
+       }
 
-               dev_vdbg(hsotg->dev, "wPortChange=%04x\n", port_status);
-               if (!hsotg->flags.b.port_connect_status) {
-                       /*
-                        * The port is disconnected, which means the core is
-                        * either in device mode or it soon will be. Just
-                        * return 0's for the remainder of the port status
-                        * since the port register can't be read if the core
-                        * is in device mode.
-                        */
-                       USETW(ps.wPortStatus, 0);
-                       memcpy(buf, &ps, sizeof(ps));
-                       break;
-               }
+       /* Enable external vbus supply after resuming the port. */
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       dwc2_vbus_supply_init(hsotg);
 
-               port_status = 0;
-               hprt0 = DWC2_READ_4(hsotg, HPRT0);
-               dev_vdbg(hsotg->dev, "  HPRT0: 0x%08x\n", hprt0);
+       /* Wait for controller to correctly update D+/D- level */
+       usleep_range(3000, 5000);
+       spin_lock_irqsave(&hsotg->lock, flags);
 
-               if (hprt0 & HPRT0_CONNSTS)
-                       port_status |= USB_PORT_STAT_CONNECTION;
-               if (hprt0 & HPRT0_ENA)
-                       port_status |= USB_PORT_STAT_ENABLE;
-               if (hprt0 & HPRT0_SUSP)
-                       port_status |= USB_PORT_STAT_SUSPEND;
-               if (hprt0 & HPRT0_OVRCURRACT)
-                       port_status |= USB_PORT_STAT_OVERCURRENT;
-               if (hprt0 & HPRT0_RST)
-                       port_status |= USB_PORT_STAT_RESET;
-               if (hprt0 & HPRT0_PWR)
-                       port_status |= USB_PORT_STAT_POWER;
+       /*
+        * Clear Port Enable and Port Status changes.
+        * Enable Port Power.
+        */
+       dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
+                       HPRT0_ENACHG, HPRT0);
 
-               speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-               if (speed == HPRT0_SPD_HIGH_SPEED)
-                       port_status |= USB_PORT_STAT_HIGH_SPEED;
-               else if (speed == HPRT0_SPD_LOW_SPEED)
-                       port_status |= USB_PORT_STAT_LOW_SPEED;
+       /* Wait for controller to detect Port Connect */
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       usleep_range(5000, 7000);
+       spin_lock_irqsave(&hsotg->lock, flags);
+unlock:
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 
-               if (hprt0 & HPRT0_TSTCTL_MASK)
-                       port_status |= USB_PORT_STAT_TEST;
-               /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
-               USETW(ps.wPortStatus, port_status);
+       return ret;
+}
 
-               if (hsotg->core_params->dma_desc_fs_enable) {
-                       /*
-                        * Enable descriptor DMA only if a full speed
-                        * device is connected.
-                        */
-                       if (hsotg->new_connection &&
-                           ((port_status &
-                             (USB_PORT_STAT_CONNECTION |
-                              USB_PORT_STAT_HIGH_SPEED |
-                              USB_PORT_STAT_LOW_SPEED)) ==
-                              USB_PORT_STAT_CONNECTION)) {
-                               u32 hcfg;
+/* Returns the current frame number */
+static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
 
-                               dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
-                               hsotg->core_params->dma_desc_enable = 1;
-                               hcfg = DWC2_READ_4(hsotg, HCFG);
-                               hcfg |= HCFG_DESCDMA;
-                               DWC2_WRITE_4(hsotg, HCFG, hcfg);
-                               hsotg->new_connection = false;
-                       }
-               }
-               dev_vdbg(hsotg->dev, "wPortStatus=%04x\n", port_status);
-               memcpy(buf, &ps, sizeof(ps));
-               break;
+       return dwc2_hcd_get_frame_number(hsotg);
+}
 
-       case SetHubFeature:
-               dev_dbg(hsotg->dev, "SetHubFeature\n");
-               /* No HUB features supported */
+static void dwc2_dump_urb_info(struct usb_hcd *hcd, struct urb *urb,
+                              char *fn_name)
+{
+#ifdef VERBOSE_DEBUG
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       char *pipetype = NULL;
+       char *speed = NULL;
+
+       dev_vdbg(hsotg->dev, "%s, urb %p\n", fn_name, urb);
+       dev_vdbg(hsotg->dev, "  Device address: %d\n",
+                usb_pipedevice(urb->pipe));
+       dev_vdbg(hsotg->dev, "  Endpoint: %d, %s\n",
+                usb_pipeendpoint(urb->pipe),
+                usb_pipein(urb->pipe) ? "IN" : "OUT");
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_CONTROL:
+               pipetype = "CONTROL";
                break;
+       case PIPE_BULK:
+               pipetype = "BULK";
+               break;
+       case PIPE_INTERRUPT:
+               pipetype = "INTERRUPT";
+               break;
+       case PIPE_ISOCHRONOUS:
+               pipetype = "ISOCHRONOUS";
+               break;
+       }
 
-       case SetPortFeature:
-               dev_dbg(hsotg->dev, "SetPortFeature\n");
-               if (wvalue != USB_PORT_FEAT_TEST && (!windex || windex > 1))
-                       goto error;
+       dev_vdbg(hsotg->dev, "  Endpoint type: %s %s (%s)\n", pipetype,
+                usb_urb_dir_in(urb) ? "IN" : "OUT", usb_pipein(urb->pipe) ?
+                "IN" : "OUT");
 
-               if (!hsotg->flags.b.port_connect_status) {
-                       /*
-                        * The port is disconnected, which means the core is
-                        * either in device mode or it soon will be. Just
-                        * return without doing anything since the port
-                        * register can't be written if the core is in device
-                        * mode.
-                        */
-                       break;
+       switch (urb->dev->speed) {
+       case USB_SPEED_HIGH:
+               speed = "HIGH";
+               break;
+       case USB_SPEED_FULL:
+               speed = "FULL";
+               break;
+       case USB_SPEED_LOW:
+               speed = "LOW";
+               break;
+       default:
+               speed = "UNKNOWN";
+               break;
+       }
+
+       dev_vdbg(hsotg->dev, "  Speed: %s\n", speed);
+       dev_vdbg(hsotg->dev, "  Max packet size: %d (%d mult)\n",
+                usb_endpoint_maxp(&urb->ep->desc),
+                usb_endpoint_maxp_mult(&urb->ep->desc));
+
+       dev_vdbg(hsotg->dev, "  Data buffer length: %d\n",
+                urb->transfer_buffer_length);
+       dev_vdbg(hsotg->dev, "  Transfer buffer: %p, Transfer DMA: %08lx\n",
+                urb->transfer_buffer, (unsigned long)urb->transfer_dma);
+       dev_vdbg(hsotg->dev, "  Setup buffer: %p, Setup DMA: %08lx\n",
+                urb->setup_packet, (unsigned long)urb->setup_dma);
+       dev_vdbg(hsotg->dev, "  Interval: %d\n", urb->interval);
+
+       if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+               int i;
+
+               for (i = 0; i < urb->number_of_packets; i++) {
+                       dev_vdbg(hsotg->dev, "  ISO Desc %d:\n", i);
+                       dev_vdbg(hsotg->dev, "    offset: %d, length %d\n",
+                                urb->iso_frame_desc[i].offset,
+                                urb->iso_frame_desc[i].length);
                }
+       }
+#endif
+}
 
-               switch (wvalue) {
-               case USB_PORT_FEAT_SUSPEND:
-                       dev_dbg(hsotg->dev,
-                               "SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
-                       if (windex != hsotg->otg_port)
-                               goto error;
-                       dwc2_port_suspend(hsotg, windex);
-                       break;
+/*
+ * Starts processing a USB transfer request specified by a USB Request Block
+ * (URB). mem_flags indicates the type of memory allocation to use while
+ * processing this URB.
+ */
+static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+                                gfp_t mem_flags)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       struct usb_host_endpoint *ep = urb->ep;
+       struct dwc2_hcd_urb *dwc2_urb;
+       int i;
+       int retval;
+       int alloc_bandwidth = 0;
+       u8 ep_type = 0;
+       u32 tflags = 0;
+       void *buf;
+       unsigned long flags;
+       struct dwc2_qh *qh;
+       bool qh_allocated = false;
+       struct dwc2_qtd *qtd;
+       struct dwc2_gregs_backup *gr;
 
-               case USB_PORT_FEAT_POWER:
-                       dev_dbg(hsotg->dev,
-                               "SetPortFeature - USB_PORT_FEAT_POWER\n");
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       hprt0 |= HPRT0_PWR;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-                       break;
+       gr = &hsotg->gr_backup;
+
+       if (dbg_urb(urb)) {
+               dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n");
+               dwc2_dump_urb_info(hcd, urb, "urb_enqueue");
+       }
+
+       if (hsotg->hibernated) {
+               if (gr->gotgctl & GOTGCTL_CURMODE_HOST)
+                       retval = dwc2_exit_hibernation(hsotg, 0, 0, 1);
+               else
+                       retval = dwc2_exit_hibernation(hsotg, 0, 0, 0);
+
+               if (retval)
+                       dev_err(hsotg->dev,
+                               "exit hibernation failed.\n");
+       }
+
+       if (hsotg->in_ppd) {
+               retval = dwc2_exit_partial_power_down(hsotg, 0, true);
+               if (retval)
+                       dev_err(hsotg->dev,
+                               "exit partial_power_down failed\n");
+       }
+
+       if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
+           hsotg->bus_suspended) {
+               if (dwc2_is_device_mode(hsotg))
+                       dwc2_gadget_exit_clock_gating(hsotg, 0);
+               else
+                       dwc2_host_exit_clock_gating(hsotg, 0);
+       }
+
+       if (!ep)
+               return -EINVAL;
+
+       if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS ||
+           usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+               spin_lock_irqsave(&hsotg->lock, flags);
+               if (!dwc2_hcd_is_bandwidth_allocated(hsotg, ep))
+                       alloc_bandwidth = 1;
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+       }
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_CONTROL:
+               ep_type = USB_ENDPOINT_XFER_CONTROL;
+               break;
+       case PIPE_ISOCHRONOUS:
+               ep_type = USB_ENDPOINT_XFER_ISOC;
+               break;
+       case PIPE_BULK:
+               ep_type = USB_ENDPOINT_XFER_BULK;
+               break;
+       case PIPE_INTERRUPT:
+               ep_type = USB_ENDPOINT_XFER_INT;
+               break;
+       }
+
+       dwc2_urb = dwc2_hcd_urb_alloc(hsotg, urb->number_of_packets,
+                                     mem_flags);
+       if (!dwc2_urb)
+               return -ENOMEM;
+
+       dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, usb_pipedevice(urb->pipe),
+                                 usb_pipeendpoint(urb->pipe), ep_type,
+                                 usb_pipein(urb->pipe),
+                                 usb_endpoint_maxp(&ep->desc),
+                                 usb_endpoint_maxp_mult(&ep->desc));
+
+       buf = urb->transfer_buffer;
+
+       if (hcd_uses_dma(hcd)) {
+               if (!buf && (urb->transfer_dma & 3)) {
+                       dev_err(hsotg->dev,
+                               "%s: unaligned transfer with no transfer_buffer",
+                               __func__);
+                       retval = -EINVAL;
+                       goto fail0;
+               }
+       }
 
-               case USB_PORT_FEAT_RESET:
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       dev_dbg(hsotg->dev,
-                               "SetPortFeature - USB_PORT_FEAT_RESET\n");
-                       pcgctl = DWC2_READ_4(hsotg, PCGCTL);
-                       pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK);
-                       DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
-                       /* ??? Original driver does this */
-                       DWC2_WRITE_4(hsotg, PCGCTL, 0);
+       if (!(urb->transfer_flags & URB_NO_INTERRUPT))
+               tflags |= URB_GIVEBACK_ASAP;
+       if (urb->transfer_flags & URB_ZERO_PACKET)
+               tflags |= URB_SEND_ZERO_PACKET;
+
+       dwc2_urb->priv = urb;
+       dwc2_urb->buf = buf;
+       dwc2_urb->dma = urb->transfer_dma;
+       dwc2_urb->length = urb->transfer_buffer_length;
+       dwc2_urb->setup_packet = urb->setup_packet;
+       dwc2_urb->setup_dma = urb->setup_dma;
+       dwc2_urb->flags = tflags;
+       dwc2_urb->interval = urb->interval;
+       dwc2_urb->status = -EINPROGRESS;
+
+       for (i = 0; i < urb->number_of_packets; ++i)
+               dwc2_hcd_urb_set_iso_desc_params(dwc2_urb, i,
+                                                urb->iso_frame_desc[i].offset,
+                                                urb->iso_frame_desc[i].length);
+
+       urb->hcpriv = dwc2_urb;
+       qh = (struct dwc2_qh *)ep->hcpriv;
+       /* Create QH for the endpoint if it doesn't exist */
+       if (!qh) {
+               qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, mem_flags);
+               if (!qh) {
+                       retval = -ENOMEM;
+                       goto fail0;
+               }
+               ep->hcpriv = qh;
+               qh_allocated = true;
+       }
 
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       /* Clear suspend bit if resetting from suspend state */
-                       hprt0 &= ~HPRT0_SUSP;
+       qtd = kzalloc(sizeof(*qtd), mem_flags);
+       if (!qtd) {
+               retval = -ENOMEM;
+               goto fail1;
+       }
 
-                       /*
-                        * When B-Host the Port reset bit is set in the Start
-                        * HCD Callback function, so that the reset is started
-                        * within 1ms of the HNP success interrupt
-                        */
-                       if (!dwc2_hcd_is_b_host(hsotg)) {
-                               hprt0 |= HPRT0_PWR | HPRT0_RST;
-                               dev_dbg(hsotg->dev,
-                                       "In host mode, hprt0=%08x\n", hprt0);
-                               DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-                       }
+       spin_lock_irqsave(&hsotg->lock, flags);
+       retval = usb_hcd_link_urb_to_ep(hcd, urb);
+       if (retval)
+               goto fail2;
 
-                       /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
-                       usleep_range(50000, 70000);
-                       hprt0 &= ~HPRT0_RST;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-                       hsotg->lx_state = DWC2_L0; /* Now back to On state */
-                       break;
+       retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, qtd);
+       if (retval)
+               goto fail3;
 
-               case USB_PORT_FEAT_INDICATOR:
-                       dev_dbg(hsotg->dev,
-                               "SetPortFeature - USB_PORT_FEAT_INDICATOR\n");
-                       /* Not supported */
-                       break;
+       if (alloc_bandwidth) {
+               dwc2_allocate_bus_bandwidth(hcd,
+                               dwc2_hcd_get_ep_bandwidth(hsotg, ep),
+                               urb);
+       }
 
-               case USB_PORT_FEAT_TEST:
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       dev_dbg(hsotg->dev,
-                               "SetPortFeature - USB_PORT_FEAT_TEST\n");
-                       hprt0 &= ~HPRT0_TSTCTL_MASK;
-                       hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-                       break;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 
-               default:
-                       retval = -EINVAL;
-                       dev_err(hsotg->dev,
-                               "SetPortFeature %1xh unknown or unsupported\n",
-                               wvalue);
-                       break;
-               }
-               break;
+       return 0;
 
-       default:
-error:
-               retval = -EINVAL;
-               dev_dbg(hsotg->dev,
-                       "Unknown hub control request: %1xh wIndex: %1xh wValue: %1xh\n",
-                       typereq, windex, wvalue);
-               break;
+fail3:
+       dwc2_urb->priv = NULL;
+       usb_hcd_unlink_urb_from_ep(hcd, urb);
+       if (qh_allocated && qh->channel && qh->channel->qh == qh)
+               qh->channel->qh = NULL;
+fail2:
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       urb->hcpriv = NULL;
+       kfree(qtd);
+fail1:
+       if (qh_allocated) {
+               struct dwc2_qtd *qtd2, *qtd2_tmp;
+
+               ep->hcpriv = NULL;
+               dwc2_hcd_qh_unlink(hsotg, qh);
+               /* Free each QTD in the QH's QTD list */
+               list_for_each_entry_safe(qtd2, qtd2_tmp, &qh->qtd_list,
+                                        qtd_list_entry)
+                       dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh);
+               dwc2_hcd_qh_free(hsotg, qh);
        }
+fail0:
+       kfree(dwc2_urb);
 
        return retval;
 }
 
-int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
+/*
+ * Aborts/cancels a USB transfer request. Always returns 0 to indicate success.
+ */
+static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+                                int status)
 {
-       u32 hfnum = DWC2_READ_4(hsotg, HFNUM);
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       int rc;
+       unsigned long flags;
 
-#ifdef DWC2_DEBUG_SOF
-       dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n",
-                (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT);
-#endif
-       return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
-}
+       dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n");
+       dwc2_dump_urb_info(hcd, urb, "urb_dequeue");
 
-int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg)
-{
-       return hsotg->op_state == OTG_STATE_B_HOST;
-}
+       spin_lock_irqsave(&hsotg->lock, flags);
 
-struct dwc2_hcd_urb *
-dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg, int iso_desc_count,
-                  gfp_t mem_flags)
-{
-       struct dwc2_hcd_urb *urb;
-       u32 size = sizeof(*urb) + iso_desc_count *
-                  sizeof(struct dwc2_hcd_iso_packet_desc);
+       rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+       if (rc)
+               goto out;
 
-       urb = malloc(size, M_USBHC, M_ZERO | mem_flags);
-       if (urb)
-               urb->packet_count = iso_desc_count;
-       return urb;
-}
+       if (!urb->hcpriv) {
+               dev_dbg(hsotg->dev, "## urb->hcpriv is NULL ##\n");
+               goto out;
+       }
 
-void
-dwc2_hcd_urb_free(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
-    int iso_desc_count)
-{
+       rc = dwc2_hcd_urb_dequeue(hsotg, urb->hcpriv);
 
-       u32 size = sizeof(*urb) + iso_desc_count *
-                  sizeof(struct dwc2_hcd_iso_packet_desc);
+       usb_hcd_unlink_urb_from_ep(hcd, urb);
 
-       free(urb, M_USBHC, size);
-}
+       kfree(urb->hcpriv);
+       urb->hcpriv = NULL;
 
-void
-dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
-                         u8 dev_addr, u8 ep_num, u8 ep_type, u8 ep_dir,
-                         u16 mps)
-{
-       if (dbg_perio() ||
-           ep_type == USB_ENDPOINT_XFER_BULK ||
-           ep_type == USB_ENDPOINT_XFER_CONTROL)
-               dev_dbg(hsotg->dev, "urb=%p, xfer=%p\n", urb, urb->priv);
-       urb->pipe_info.dev_addr = dev_addr;
-       urb->pipe_info.ep_num = ep_num;
-       urb->pipe_info.pipe_type = ep_type;
-       urb->pipe_info.pipe_dir = ep_dir;
-       urb->pipe_info.mps = mps;
+       /* Higher layer software sets URB status */
+       spin_unlock(&hsotg->lock);
+       usb_hcd_giveback_urb(hcd, urb, status);
+       spin_lock(&hsotg->lock);
+
+       dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n");
+       dev_dbg(hsotg->dev, "  urb->status = %d\n", urb->status);
+out:
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return rc;
 }
 
 /*
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
+ * Frees resources in the DWC_otg controller related to a given endpoint. Also
+ * clears state in the HCD related to the endpoint. Any URBs for the endpoint
+ * must already be dequeued.
  */
-void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
+static void _dwc2_hcd_endpoint_disable(struct usb_hcd *hcd,
+                                      struct usb_host_endpoint *ep)
 {
-#ifdef DWC2_DEBUG
-       struct dwc2_host_chan *chan;
-       struct dwc2_hcd_urb *urb;
-       struct dwc2_qtd *qtd;
-       int num_channels;
-       u32 np_tx_status;
-       u32 p_tx_status;
-       int i;
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
 
-       num_channels = hsotg->core_params->host_channels;
-       dev_dbg(hsotg->dev, "\n");
        dev_dbg(hsotg->dev,
-               "************************************************************\n");
-       dev_dbg(hsotg->dev, "HCD State:\n");
-       dev_dbg(hsotg->dev, "  Num channels: %d\n", num_channels);
-
-       for (i = 0; i < num_channels; i++) {
-               chan = hsotg->hc_ptr_array[i];
-               dev_dbg(hsotg->dev, "  Channel %d:\n", i);
-               dev_dbg(hsotg->dev,
-                       "    dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
-                       chan->dev_addr, chan->ep_num, chan->ep_is_in);
-               dev_dbg(hsotg->dev, "    speed: %d\n", chan->speed);
-               dev_dbg(hsotg->dev, "    ep_type: %d\n", chan->ep_type);
-               dev_dbg(hsotg->dev, "    max_packet: %d\n", chan->max_packet);
-               dev_dbg(hsotg->dev, "    data_pid_start: %d\n",
-                       chan->data_pid_start);
-               dev_dbg(hsotg->dev, "    multi_count: %d\n", chan->multi_count);
-               dev_dbg(hsotg->dev, "    xfer_started: %d\n",
-                       chan->xfer_started);
-               dev_dbg(hsotg->dev, "    xfer_buf: %p\n", chan->xfer_buf);
-               dev_dbg(hsotg->dev, "    xfer_dma: %08lx\n",
-                       (unsigned long)chan->xfer_dma);
-               dev_dbg(hsotg->dev, "    xfer_len: %d\n", chan->xfer_len);
-               dev_dbg(hsotg->dev, "    xfer_count: %d\n", chan->xfer_count);
-               dev_dbg(hsotg->dev, "    halt_on_queue: %d\n",
-                       chan->halt_on_queue);
-               dev_dbg(hsotg->dev, "    halt_pending: %d\n",
-                       chan->halt_pending);
-               dev_dbg(hsotg->dev, "    halt_status: %d\n", chan->halt_status);
-               dev_dbg(hsotg->dev, "    do_split: %d\n", chan->do_split);
-               dev_dbg(hsotg->dev, "    complete_split: %d\n",
-                       chan->complete_split);
-               dev_dbg(hsotg->dev, "    hub_addr: %d\n", chan->hub_addr);
-               dev_dbg(hsotg->dev, "    hub_port: %d\n", chan->hub_port);
-               dev_dbg(hsotg->dev, "    xact_pos: %d\n", chan->xact_pos);
-               dev_dbg(hsotg->dev, "    requests: %d\n", chan->requests);
-               dev_dbg(hsotg->dev, "    qh: %p\n", chan->qh);
-
-               if (chan->xfer_started) {
-                       dev_dbg(hsotg->dev, "    hfnum: 0x%08x\n",
-                           DWC2_READ_4(hsotg, HFNUM));
-                       dev_dbg(hsotg->dev, "    hcchar: 0x%08x\n",
-                           DWC2_READ_4(hsotg, HCCHAR(i)));
-                       dev_dbg(hsotg->dev, "    hctsiz: 0x%08x\n",
-                           DWC2_READ_4(hsotg, HCTSIZ(i)));
-                       dev_dbg(hsotg->dev, "    hcint: 0x%08x\n",
-                           DWC2_READ_4(hsotg, HCINT(i)));
-                       dev_dbg(hsotg->dev, "    hcintmsk: 0x%08x\n",
-                           DWC2_READ_4(hsotg, HCINTMSK(i)));
-               }
-
-               if (!(chan->xfer_started && chan->qh))
-                       continue;
-
-               list_for_each_entry(qtd, &chan->qh->qtd_list, qtd_list_entry) {
-                       if (!qtd->in_process)
-                               break;
-                       urb = qtd->urb;
-                       dev_dbg(hsotg->dev, "    URB Info:\n");
-                       dev_dbg(hsotg->dev, "      qtd: %p, urb: %p\n",
-                               qtd, urb);
-                       if (urb) {
-                               dev_dbg(hsotg->dev,
-                                       "      Dev: %d, EP: %d %s\n",
-                                       dwc2_hcd_get_dev_addr(&urb->pipe_info),
-                                       dwc2_hcd_get_ep_num(&urb->pipe_info),
-                                       dwc2_hcd_is_pipe_in(&urb->pipe_info) ?
-                                       "IN" : "OUT");
-                               dev_dbg(hsotg->dev,
-                                       "      Max packet size: %d\n",
-                                       dwc2_hcd_get_mps(&urb->pipe_info));
-                               dev_dbg(hsotg->dev,
-                                       "      transfer_buffer: %p\n",
-                                       urb->buf);
-                               dev_dbg(hsotg->dev,
-                                       "      transfer_dma: %08lx\n",
-                                       (unsigned long)urb->dma);
-                               dev_dbg(hsotg->dev,
-                                       "      transfer_buffer_length: %d\n",
-                                       urb->length);
-                               dev_dbg(hsotg->dev, "      actual_length: %d\n",
-                                       urb->actual_length);
-                       }
-               }
-       }
+               "DWC OTG HCD EP DISABLE: bEndpointAddress=0x%02x, ep->hcpriv=%p\n",
+               ep->desc.bEndpointAddress, ep->hcpriv);
+       dwc2_hcd_endpoint_disable(hsotg, ep, 250);
+}
 
-       dev_dbg(hsotg->dev, "  non_periodic_channels: %d\n",
-               hsotg->non_periodic_channels);
-       dev_dbg(hsotg->dev, "  periodic_channels: %d\n",
-               hsotg->periodic_channels);
-       dev_dbg(hsotg->dev, "  periodic_usecs: %d\n", hsotg->periodic_usecs);
-       np_tx_status = DWC2_READ_4(hsotg, GNPTXSTS);
-       dev_dbg(hsotg->dev, "  NP Tx Req Queue Space Avail: %d\n",
-               (np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
-       dev_dbg(hsotg->dev, "  NP Tx FIFO Space Avail: %d\n",
-               (np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
-       p_tx_status = DWC2_READ_4(hsotg, HPTXSTS);
-       dev_dbg(hsotg->dev, "  P Tx Req Queue Space Avail: %d\n",
-               (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
-       dev_dbg(hsotg->dev, "  P Tx FIFO Space Avail: %d\n",
-               (p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
-       dwc2_hcd_dump_frrem(hsotg);
+/*
+ * Resets endpoint specific parameter values, in current version used to reset
+ * the data toggle (as a WA). This function can be called from usb_clear_halt
+ * routine.
+ */
+static void _dwc2_hcd_endpoint_reset(struct usb_hcd *hcd,
+                                    struct usb_host_endpoint *ep)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       unsigned long flags;
 
-       dwc2_dump_global_registers(hsotg);
-       dwc2_dump_host_registers(hsotg);
        dev_dbg(hsotg->dev,
-               "************************************************************\n");
-       dev_dbg(hsotg->dev, "\n");
-#endif
+               "DWC OTG HCD EP RESET: bEndpointAddress=0x%02x\n",
+               ep->desc.bEndpointAddress);
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       dwc2_hcd_endpoint_reset(hsotg, ep);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
 /*
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
+ * Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if
+ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid
+ * interrupt.
+ *
+ * This function is called by the USB core when an interrupt occurs
  */
-void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg)
+static irqreturn_t _dwc2_hcd_irq(struct usb_hcd *hcd)
 {
-#ifdef DWC2_DUMP_FRREM
-       dev_dbg(hsotg->dev, "Frame remaining at SOF:\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->frrem_samples, hsotg->frrem_accum,
-               hsotg->frrem_samples > 0 ?
-               hsotg->frrem_accum / hsotg->frrem_samples : 0);
-       dev_dbg(hsotg->dev, "\n");
-       dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 7):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_7_samples,
-               hsotg->hfnum_7_frrem_accum,
-               hsotg->hfnum_7_samples > 0 ?
-               hsotg->hfnum_7_frrem_accum / hsotg->hfnum_7_samples : 0);
-       dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 0):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_0_samples,
-               hsotg->hfnum_0_frrem_accum,
-               hsotg->hfnum_0_samples > 0 ?
-               hsotg->hfnum_0_frrem_accum / hsotg->hfnum_0_samples : 0);
-       dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 1-6):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_other_samples,
-               hsotg->hfnum_other_frrem_accum,
-               hsotg->hfnum_other_samples > 0 ?
-               hsotg->hfnum_other_frrem_accum / hsotg->hfnum_other_samples :
-               0);
-       dev_dbg(hsotg->dev, "\n");
-       dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 7):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_7_samples_a, hsotg->hfnum_7_frrem_accum_a,
-               hsotg->hfnum_7_samples_a > 0 ?
-               hsotg->hfnum_7_frrem_accum_a / hsotg->hfnum_7_samples_a : 0);
-       dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 0):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_0_samples_a, hsotg->hfnum_0_frrem_accum_a,
-               hsotg->hfnum_0_samples_a > 0 ?
-               hsotg->hfnum_0_frrem_accum_a / hsotg->hfnum_0_samples_a : 0);
-       dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 1-6):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_other_samples_a, hsotg->hfnum_other_frrem_accum_a,
-               hsotg->hfnum_other_samples_a > 0 ?
-               hsotg->hfnum_other_frrem_accum_a / hsotg->hfnum_other_samples_a
-               : 0);
-       dev_dbg(hsotg->dev, "\n");
-       dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 7):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_7_samples_b, hsotg->hfnum_7_frrem_accum_b,
-               hsotg->hfnum_7_samples_b > 0 ?
-               hsotg->hfnum_7_frrem_accum_b / hsotg->hfnum_7_samples_b : 0);
-       dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 0):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_0_samples_b, hsotg->hfnum_0_frrem_accum_b,
-               (hsotg->hfnum_0_samples_b > 0) ?
-               hsotg->hfnum_0_frrem_accum_b / hsotg->hfnum_0_samples_b : 0);
-       dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 1-6):\n");
-       dev_dbg(hsotg->dev, "  samples %u, accum %llu, avg %llu\n",
-               hsotg->hfnum_other_samples_b, hsotg->hfnum_other_frrem_accum_b,
-               (hsotg->hfnum_other_samples_b > 0) ?
-               hsotg->hfnum_other_frrem_accum_b / hsotg->hfnum_other_samples_b
-               : 0);
-#endif
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+
+       return dwc2_handle_hcd_intr(hsotg);
 }
 
-struct wrapper_priv_data {
-       struct dwc2_hsotg *hsotg;
-};
+/*
+ * Creates Status Change bitmap for the root hub and root port. The bitmap is
+ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1
+ * is the status change indicator for the single root port. Returns 1 if either
+ * change indicator is 1, otherwise returns 0.
+ */
+static int _dwc2_hcd_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
 
+       buf[0] = dwc2_hcd_is_status_changed(hsotg, 1) << 1;
+       return buf[0] != 0;
+}
 
-void dwc2_host_start(struct dwc2_hsotg *hsotg)
+/* Handles hub class-specific requests */
+static int _dwc2_hcd_hub_control(struct usb_hcd *hcd, u16 typereq, u16 wvalue,
+                                u16 windex, char *buf, u16 wlength)
 {
-//     struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
-
-//     hcd->self.is_b_host = dwc2_hcd_is_b_host(hsotg);
-       _dwc2_hcd_start(hsotg);
+       int retval = dwc2_hcd_hub_control(dwc2_hcd_to_hsotg(hcd), typereq,
+                                         wvalue, windex, buf, wlength);
+       return retval;
 }
 
-void dwc2_host_disconnect(struct dwc2_hsotg *hsotg)
+/* Handles hub TT buffer clear completions */
+static void _dwc2_hcd_clear_tt_buffer_complete(struct usb_hcd *hcd,
+                                              struct usb_host_endpoint *ep)
 {
-//     struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+       struct dwc2_qh *qh;
+       unsigned long flags;
+
+       qh = ep->hcpriv;
+       if (!qh)
+               return;
 
-//     hcd->self.is_b_host = 0;
+       spin_lock_irqsave(&hsotg->lock, flags);
+       qh->tt_buffer_dirty = 0;
+
+       if (hsotg->flags.b.port_connect_status)
+               dwc2_hcd_queue_transactions(hsotg, DWC2_TRANSACTION_ALL);
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
 /*
- * Work queue function for starting the HCD when A-Cable is connected
+ * HPRT0_SPD_HIGH_SPEED: high speed
+ * HPRT0_SPD_FULL_SPEED: full speed
  */
-STATIC void dwc2_hcd_start_func(void *data)
+static void dwc2_change_bus_speed(struct usb_hcd *hcd, int speed)
+{
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+
+       if (hsotg->params.speed == speed)
+               return;
+
+       hsotg->params.speed = speed;
+       queue_work(hsotg->wq_otg, &hsotg->wf_otg);
+}
+
+static void dwc2_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
 {
-       struct dwc2_hsotg *hsotg = data;
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
 
-       dev_dbg(hsotg->dev, "%s() %p\n", __func__, hsotg);
-       dwc2_host_start(hsotg);
+       if (!hsotg->params.change_speed_quirk)
+               return;
+
+       /*
+        * On removal, set speed to default high-speed.
+        */
+       if (udev->parent && udev->parent->speed > USB_SPEED_UNKNOWN &&
+           udev->parent->speed < USB_SPEED_HIGH) {
+               dev_info(hsotg->dev, "Set speed to default high-speed\n");
+               dwc2_change_bus_speed(hcd, HPRT0_SPD_HIGH_SPEED);
+       }
 }
 
-/*
- * Reset work queue function
- */
-STATIC void dwc2_hcd_reset_func(void *data)
+static int dwc2_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
-       struct dwc2_hsotg *hsotg = data;
-       unsigned long flags;
-       u32 hprt0;
+       struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
 
-       dev_dbg(hsotg->dev, "USB RESET function called\n");
+       if (!hsotg->params.change_speed_quirk)
+               return 0;
 
-       spin_lock_irqsave(&hsotg->lock, flags);
+       if (udev->speed == USB_SPEED_HIGH) {
+               dev_info(hsotg->dev, "Set speed to high-speed\n");
+               dwc2_change_bus_speed(hcd, HPRT0_SPD_HIGH_SPEED);
+       } else if ((udev->speed == USB_SPEED_FULL ||
+                               udev->speed == USB_SPEED_LOW)) {
+               /*
+                * Change speed setting to full-speed if there's
+                * a full-speed or low-speed device plugged in.
+                */
+               dev_info(hsotg->dev, "Set speed to full-speed\n");
+               dwc2_change_bus_speed(hcd, HPRT0_SPD_FULL_SPEED);
+       }
 
-       hprt0 = dwc2_read_hprt0(hsotg);
-       hprt0 &= ~HPRT0_RST;
-       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-       hsotg->flags.b.port_reset_change = 1;
+       return 0;
+}
 
-       dwc2_root_intr(hsotg->hsotg_sc);
+/* XXX: Linux USB Stack */
+static struct hc_driver dwc2_hc_driver = {
+       .description = "dwc2_hsotg",
+       .product_desc = "DWC OTG Controller",
+       .hcd_priv_size = sizeof(struct wrapper_priv_data),
 
-       spin_unlock_irqrestore(&hsotg->lock, flags);
-}
+       .irq = _dwc2_hcd_irq,
+       .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
-/*
- * =========================================================================
- *  Linux HC Driver Functions
- * =========================================================================
- */
+       .start = _dwc2_hcd_start,
+       .stop = _dwc2_hcd_stop,
+       .urb_enqueue = _dwc2_hcd_urb_enqueue,
+       .urb_dequeue = _dwc2_hcd_urb_dequeue,
+       .endpoint_disable = _dwc2_hcd_endpoint_disable,
+       .endpoint_reset = _dwc2_hcd_endpoint_reset,
+       .get_frame_number = _dwc2_hcd_get_frame_number,
 
-/*
- * Initializes the DWC_otg controller and its root hub and prepares it for host
- * mode operation. Activates the root port. Returns 0 on success and a negative
- * error code on failure.
- */
+       .hub_status_data = _dwc2_hcd_hub_status_data,
+       .hub_control = _dwc2_hcd_hub_control,
+       .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
+
+       .bus_suspend = _dwc2_hcd_suspend,
+       .bus_resume = _dwc2_hcd_resume,
+
+       .map_urb_for_dma        = dwc2_map_urb_for_dma,
+       .unmap_urb_for_dma      = dwc2_unmap_urb_for_dma,
+};
+#endif
 
 /*
  * Frees secondary storage associated with the dwc2_hsotg structure contained
@@ -2232,7 +5203,7 @@ STATIC void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
        for (i = 0; i < MAX_EPS_CHANNELS; i++) {
                struct dwc2_host_chan *chan = hsotg->hc_ptr_array[i];
 
-               if (chan != NULL) {
+               if (chan) {
                        dev_dbg(hsotg->dev, "HCD Free channel #%i, chan=%p\n",
                                i, chan);
                        hsotg->hc_ptr_array[i] = NULL;
@@ -2240,10 +5211,10 @@ STATIC void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
                }
        }
 
-       if (hsotg->core_params->dma_enable > 0) {
+       if (hsotg->params.host_dma) {
                if (hsotg->status_buf) {
                        usb_freemem(&hsotg->hsotg_sc->sc_bus,
-                                   &hsotg->status_buf_usbdma);
+                                   &hsotg->status_buf_dma_usb);
                        hsotg->status_buf = NULL;
                }
        } else {
@@ -2251,28 +5222,29 @@ STATIC void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
                hsotg->status_buf = NULL;
        }
 
-       ahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
+       ahbcfg = dwc2_readl(hsotg, GAHBCFG);
 
        /* Disable all interrupts */
        ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
-       DWC2_WRITE_4(hsotg, GAHBCFG, ahbcfg);
-       DWC2_WRITE_4(hsotg, GINTMSK, 0);
+       dwc2_writel(hsotg, ahbcfg, GAHBCFG);
+       dwc2_writel(hsotg, 0, GINTMSK);
 
        if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) {
-               dctl = DWC2_READ_4(hsotg, DCTL);
+               dctl = dwc2_readl(hsotg, DCTL);
                dctl |= DCTL_SFTDISCON;
-               DWC2_WRITE_4(hsotg, DCTL, dctl);
+               dwc2_writel(hsotg, dctl, DCTL);
        }
 
        if (hsotg->wq_otg) {
                taskq_destroy(hsotg->wq_otg);
        }
 
-       free(hsotg->core_params, M_USBHC, sizeof(*hsotg->core_params));
-       hsotg->core_params = NULL;
+       //free(hsotg->core_params, M_USBHC, sizeof(*hsotg->core_params));
+       //hsotg->core_params = NULL;
        timeout_del(&hsotg->wkp_timer);
 }
 
+
 STATIC void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
 {
        /* Turn off all host-specific interrupts */
@@ -2290,6 +5262,7 @@ STATIC void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
 int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_host_chan *channel;
+       u32 hcfg;
        int i, num_channels;
        int retval;
 
@@ -2300,7 +5273,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 
        retval = -ENOMEM;
 
-       dev_dbg(hsotg->dev, "hcfg=%08x\n", DWC2_READ_4(hsotg, HCFG));
+       hcfg = dwc2_readl(hsotg, HCFG);
+       dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg);
 
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
        hsotg->frame_num_array = malloc(sizeof(*hsotg->frame_num_array) *
@@ -2313,8 +5287,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
                        FRAME_NUM_ARRAY_SIZE, M_USBHC, M_ZERO | M_WAITOK);
        if (!hsotg->last_frame_num_array)
                goto error1;
-       hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 #endif
+       hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 
        spin_lock_init(&hsotg->lock);
 
@@ -2331,7 +5305,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 
        /* Create new workqueue and init work */
        retval = -ENOMEM;
-       hsotg->wq_otg = taskq_create("dwc2", 1, IPL_USB, 0);
+       hsotg->wq_otg = taskq_create("dwc2", 1, IPL_VM, 0);
        if (!hsotg->wq_otg) {
                dev_err(hsotg->dev, "Failed to create workqueue\n");
                goto error2;
@@ -2358,21 +5332,18 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
         * in the controller. Initialize the channel descriptor array.
         */
        INIT_LIST_HEAD(&hsotg->free_hc_list);
-       num_channels = hsotg->core_params->host_channels;
+       num_channels = hsotg->params.host_channels;
        memset(&hsotg->hc_ptr_array[0], 0, sizeof(hsotg->hc_ptr_array));
 
        for (i = 0; i < num_channels; i++) {
                channel = malloc(sizeof(*channel), M_USBHC, M_ZERO | M_WAITOK);
-               if (channel == NULL)
+               if (!channel)
                        goto error3;
                channel->hc_num = i;
                INIT_LIST_HEAD(&channel->split_order_list_entry);
                hsotg->hc_ptr_array[i] = channel;
        }
 
-       if (hsotg->core_params->uframe_sched > 0)
-               dwc2_hcd_init_usecs(hsotg);
-
        /* Initialize hsotg start work */
        INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func, hsotg);
 
@@ -2386,13 +5357,15 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
         * pool.
         */
        hsotg->status_buf = NULL;
-       if (hsotg->core_params->dma_enable > 0) {
+       if (hsotg->params.host_dma) {
                int error = usb_allocmem(&hsotg->hsotg_sc->sc_bus,
                    DWC2_HCD_STATUS_BUF_SIZE, 0, USB_DMA_COHERENT,
-                   &hsotg->status_buf_usbdma);
+                   &hsotg->status_buf_dma_usb);
                if (!error) {
-                       hsotg->status_buf = KERNADDR(&hsotg->status_buf_usbdma, 0);
-                       hsotg->status_buf_dma = DMAADDR(&hsotg->status_buf_usbdma, 0);
+                       hsotg->status_buf =
+                           KERNADDR(&hsotg->status_buf_dma_usb, 0);
+                       hsotg->status_buf_dma =
+                           DMAADDR(&hsotg->status_buf_dma_usb, 0);
                }
        } else
                hsotg->status_buf = malloc(DWC2_HCD_STATUS_BUF_SIZE, M_USBHC,
@@ -2410,7 +5383,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
        /* Initiate lx_state to L3 disconnected state */
        hsotg->lx_state = DWC2_L3;
 
-       _dwc2_hcd_start(hsotg);
+       _dwc2_hcd_start(hsotg);
 
        dwc2_hcd_dump_state(hsotg);
 
@@ -2421,9 +5394,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 error3:
        dwc2_hcd_release(hsotg);
 error2:
+#if 0
        if (hsotg->core_params != NULL)
                free(hsotg->core_params, M_USBHC, sizeof(*hsotg->core_params));
-
+#endif
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
        if (hsotg->last_frame_num_array != NULL)
                free(hsotg->last_frame_num_array, M_USBHC,
@@ -2437,6 +5411,7 @@ error2:
        return retval;
 }
 
+#if 0
 /*
  * Removes the HCD.
  * Frees memory and resources associated with the HCD and deregisters the bus.
@@ -2464,3 +5439,568 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
        free(hsotg->frame_num_array, M_USBHC, sizeof(*hsotg->frame_num_array) * FRAME_NUM_ARRAY_SIZE);
 #endif
 }
+#endif
+
+/**
+ * dwc2_backup_host_registers() - Backup controller host registers.
+ * When suspending usb bus, registers needs to be backuped
+ * if controller power is disabled once suspended.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+STATIC int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hregs_backup *hr;
+       int i;
+
+       dev_dbg(hsotg->dev, "%s\n", __func__);
+
+       /* Backup Host regs */
+       hr = &hsotg->hr_backup;
+       hr->hcfg = dwc2_readl(hsotg, HCFG);
+       hr->haintmsk = dwc2_readl(hsotg, HAINTMSK);
+       for (i = 0; i < hsotg->params.host_channels; ++i)
+               hr->hcintmsk[i] = dwc2_readl(hsotg, HCINTMSK(i));
+
+       hr->hprt0 = dwc2_read_hprt0(hsotg);
+       hr->hfir = dwc2_readl(hsotg, HFIR);
+       hr->hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ);
+       hr->valid = true;
+
+       return 0;
+}
+
+/**
+ * dwc2_restore_host_registers() - Restore controller host registers.
+ * When resuming usb bus, device registers needs to be restored
+ * if controller power were disabled.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+STATIC int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hregs_backup *hr;
+       int i;
+
+       dev_dbg(hsotg->dev, "%s\n", __func__);
+
+       /* Restore host regs */
+       hr = &hsotg->hr_backup;
+       if (!hr->valid) {
+               dev_err(hsotg->dev, "%s: no host registers to restore\n",
+                       __func__);
+               return -EINVAL;
+       }
+       hr->valid = false;
+
+       dwc2_writel(hsotg, hr->hcfg, HCFG);
+       dwc2_writel(hsotg, hr->haintmsk, HAINTMSK);
+
+       for (i = 0; i < hsotg->params.host_channels; ++i)
+               dwc2_writel(hsotg, hr->hcintmsk[i], HCINTMSK(i));
+
+       dwc2_writel(hsotg, hr->hprt0, HPRT0);
+       dwc2_writel(hsotg, hr->hfir, HFIR);
+       dwc2_writel(hsotg, hr->hptxfsiz, HPTXFSIZ);
+       hsotg->frame_number = 0;
+
+       return 0;
+}
+
+/**
+ * dwc2_host_enter_hibernation() - Put controller in Hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
+{
+       unsigned long flags;
+       int ret = 0;
+       u32 hprt0;
+       u32 pcgcctl;
+       u32 gusbcfg;
+       u32 gpwrdn;
+
+       dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
+       ret = dwc2_backup_global_registers(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+                       __func__);
+               return ret;
+       }
+       ret = dwc2_backup_host_registers(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to backup host registers\n",
+                       __func__);
+               return ret;
+       }
+
+       /* Enter USB Suspend Mode */
+       hprt0 = dwc2_readl(hsotg, HPRT0);
+       hprt0 |= HPRT0_SUSP;
+       hprt0 &= ~HPRT0_ENA;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+
+       /* Wait for the HPRT0.PrtSusp register field to be set */
+       if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 5000))
+               dev_warn(hsotg->dev, "Suspend wasn't generated\n");
+
+       /*
+        * We need to disable interrupts to prevent servicing of any IRQ
+        * during going to hibernation
+        */
+       spin_lock_irqsave(&hsotg->lock, flags);
+       hsotg->lx_state = DWC2_L2;
+
+       gusbcfg = dwc2_readl(hsotg, GUSBCFG);
+       if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
+               /* ULPI interface */
+               /* Suspend the Phy Clock */
+               pcgcctl = dwc2_readl(hsotg, PCGCTL);
+               pcgcctl |= PCGCTL_STOPPCLK;
+               dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               udelay(10);
+
+               gpwrdn = dwc2_readl(hsotg, GPWRDN);
+               gpwrdn |= GPWRDN_PMUACTV;
+               dwc2_writel(hsotg, gpwrdn, GPWRDN);
+               udelay(10);
+       } else {
+               /* UTMI+ Interface */
+               gpwrdn = dwc2_readl(hsotg, GPWRDN);
+               gpwrdn |= GPWRDN_PMUACTV;
+               dwc2_writel(hsotg, gpwrdn, GPWRDN);
+               udelay(10);
+
+               pcgcctl = dwc2_readl(hsotg, PCGCTL);
+               pcgcctl |= PCGCTL_STOPPCLK;
+               dwc2_writel(hsotg, pcgcctl, PCGCTL);
+               udelay(10);
+       }
+
+       /* Enable interrupts from wake up logic */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn |= GPWRDN_PMUINTSEL;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Unmask host mode interrupts in GPWRDN */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn |= GPWRDN_DISCONN_DET_MSK;
+       gpwrdn |= GPWRDN_LNSTSCHG_MSK;
+       gpwrdn |= GPWRDN_STS_CHGINT_MSK;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Enable Power Down Clamp */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn |= GPWRDN_PWRDNCLMP;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Switch off VDD */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn |= GPWRDN_PWRDNSWTCH;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+
+       hsotg->hibernated = 1;
+       hsotg->bus_suspended = 1;
+       dev_dbg(hsotg->dev, "Host hibernation completed\n");
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+       return ret;
+}
+
+/*
+ * dwc2_host_exit_hibernation()
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Device or Host.
+ * @param reset: indicates whether resume is initiated by Reset.
+ *
+ * Return: non-zero if failed to enter to hibernation.
+ *
+ * This function is for exiting from Host mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ */
+int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+                              int reset)
+{
+       u32 gpwrdn;
+       u32 hprt0;
+       int ret = 0;
+       struct dwc2_gregs_backup *gr;
+       struct dwc2_hregs_backup *hr;
+
+       gr = &hsotg->gr_backup;
+       hr = &hsotg->hr_backup;
+
+       dev_dbg(hsotg->dev,
+               "%s: called with rem_wakeup = %d reset = %d\n",
+               __func__, rem_wakeup, reset);
+
+       dwc2_hib_restore_common(hsotg, rem_wakeup, 1);
+       hsotg->hibernated = 0;
+
+       /*
+        * This step is not described in functional spec but if not wait for
+        * this delay, mismatch interrupts occurred because just after restore
+        * core is in Device mode(gintsts.curmode == 0)
+        */
+       mdelay(100);
+
+       /* Clear all pending interupts */
+       dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+
+       /* De-assert Restore */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn &= ~GPWRDN_RESTORE;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       /* Restore GUSBCFG, HCFG */
+       dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
+       dwc2_writel(hsotg, hr->hcfg, HCFG);
+
+       /* De-assert Wakeup Logic */
+       gpwrdn = dwc2_readl(hsotg, GPWRDN);
+       gpwrdn &= ~GPWRDN_PMUACTV;
+       dwc2_writel(hsotg, gpwrdn, GPWRDN);
+       udelay(10);
+
+       hprt0 = hr->hprt0;
+       hprt0 |= HPRT0_PWR;
+       hprt0 &= ~HPRT0_ENA;
+       hprt0 &= ~HPRT0_SUSP;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+
+       hprt0 = hr->hprt0;
+       hprt0 |= HPRT0_PWR;
+       hprt0 &= ~HPRT0_ENA;
+       hprt0 &= ~HPRT0_SUSP;
+
+       if (reset) {
+               hprt0 |= HPRT0_RST;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+
+               /* Wait for Resume time and then program HPRT again */
+               mdelay(60);
+               hprt0 &= ~HPRT0_RST;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+       } else {
+               hprt0 |= HPRT0_RES;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+
+               /* Wait for Resume time and then program HPRT again */
+               mdelay(100);
+               hprt0 &= ~HPRT0_RES;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+       }
+       /* Clear all interrupt status */
+       hprt0 = dwc2_readl(hsotg, HPRT0);
+       hprt0 |= HPRT0_CONNDET;
+       hprt0 |= HPRT0_ENACHG;
+       hprt0 &= ~HPRT0_ENA;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+
+       hprt0 = dwc2_readl(hsotg, HPRT0);
+
+       /* Clear all pending interupts */
+       dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+
+       /* Restore global registers */
+       ret = dwc2_restore_global_registers(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to restore registers\n",
+                       __func__);
+               return ret;
+       }
+
+       /* Restore host registers */
+       ret = dwc2_restore_host_registers(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+                       __func__);
+               return ret;
+       }
+
+       if (rem_wakeup) {
+               dwc2_hcd_rem_wakeup(hsotg);
+               /*
+                * Change "port_connect_status_change" flag to re-enumerate,
+                * because after exit from hibernation port connection status
+                * is not detected.
+                */
+               hsotg->flags.b.port_connect_status_change = 1;
+       }
+
+       hsotg->hibernated = 0;
+       hsotg->bus_suspended = 0;
+       hsotg->lx_state = DWC2_L0;
+       dev_dbg(hsotg->dev, "Host hibernation restore complete\n");
+       return ret;
+}
+
+bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
+{
+#if 0
+       struct usb_device *root_hub = dwc2_hsotg_to_hcd(dwc2)->self.root_hub;
+
+       /* If the controller isn't allowed to wakeup then we can power off. */
+       if (!device_may_wakeup(dwc2->dev))
+               return true;
+
+       /*
+        * We don't want to power off the PHY if something under the
+        * root hub has wakeup enabled.
+        */
+       if (usb_wakeup_enabled_descendants(root_hub))
+               return false;
+#endif
+
+       /* No reason to keep the PHY powered, so allow poweroff */
+       return true;
+}
+
+/**
+ * dwc2_host_enter_partial_power_down() - Put controller in partial
+ * power down.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Return: non-zero if failed to enter host partial power down.
+ *
+ * This function is for entering Host mode partial power down.
+ */
+int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
+{
+       u32 pcgcctl;
+       u32 hprt0;
+       int ret = 0;
+
+       dev_dbg(hsotg->dev, "Entering host partial power down started.\n");
+
+       /* Put this port in suspend mode. */
+       hprt0 = dwc2_read_hprt0(hsotg);
+       hprt0 |= HPRT0_SUSP;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+       udelay(5);
+
+       /* Wait for the HPRT0.PrtSusp register field to be set */
+       if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000))
+               dev_warn(hsotg->dev, "Suspend wasn't generated\n");
+
+       /* Backup all registers */
+       ret = dwc2_backup_global_registers(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+                       __func__);
+               return ret;
+       }
+
+       ret = dwc2_backup_host_registers(hsotg);
+       if (ret) {
+               dev_err(hsotg->dev, "%s: failed to backup host registers\n",
+                       __func__);
+               return ret;
+       }
+
+       /*
+        * Clear any pending interrupts since dwc2 will not be able to
+        * clear them after entering partial_power_down.
+        */
+       dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+
+       /* Put the controller in low power state */
+       pcgcctl = dwc2_readl(hsotg, PCGCTL);
+
+       pcgcctl |= PCGCTL_PWRCLMP;
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+       udelay(5);
+
+       pcgcctl |= PCGCTL_RSTPDWNMODULE;
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+       udelay(5);
+
+       pcgcctl |= PCGCTL_STOPPCLK;
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+
+       /* Set in_ppd flag to 1 as here core enters suspend. */
+       hsotg->in_ppd = 1;
+       hsotg->lx_state = DWC2_L2;
+       hsotg->bus_suspended = true;
+
+       dev_dbg(hsotg->dev, "Entering host partial power down completed.\n");
+
+       return ret;
+}
+
+/*
+ * dwc2_host_exit_partial_power_down() - Exit controller from host partial
+ * power down.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Reset.
+ * @restore: indicates whether need to restore the registers or not.
+ *
+ * Return: non-zero if failed to exit host partial power down.
+ *
+ * This function is for exiting from Host mode partial power down.
+ */
+int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
+                                     int rem_wakeup, bool restore)
+{
+       u32 pcgcctl;
+       int ret = 0;
+       u32 hprt0;
+
+       dev_dbg(hsotg->dev, "Exiting host partial power down started.\n");
+
+       pcgcctl = dwc2_readl(hsotg, PCGCTL);
+       pcgcctl &= ~PCGCTL_STOPPCLK;
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+       udelay(5);
+
+       pcgcctl = dwc2_readl(hsotg, PCGCTL);
+       pcgcctl &= ~PCGCTL_PWRCLMP;
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+       udelay(5);
+
+       pcgcctl = dwc2_readl(hsotg, PCGCTL);
+       pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
+       dwc2_writel(hsotg, pcgcctl, PCGCTL);
+
+       udelay(100);
+       if (restore) {
+               ret = dwc2_restore_global_registers(hsotg);
+               if (ret) {
+                       dev_err(hsotg->dev, "%s: failed to restore registers\n",
+                               __func__);
+                       return ret;
+               }
+
+               ret = dwc2_restore_host_registers(hsotg);
+               if (ret) {
+                       dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+                               __func__);
+                       return ret;
+               }
+       }
+
+       /* Drive resume signaling and exit suspend mode on the port. */
+       hprt0 = dwc2_read_hprt0(hsotg);
+       hprt0 |= HPRT0_RES;
+       hprt0 &= ~HPRT0_SUSP;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+       udelay(5);
+
+       if (!rem_wakeup) {
+               /* Stop driveing resume signaling on the port. */
+               hprt0 = dwc2_read_hprt0(hsotg);
+               hprt0 &= ~HPRT0_RES;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+
+               hsotg->bus_suspended = false;
+       } else {
+               /* Turn on the port power bit. */
+               hprt0 = dwc2_read_hprt0(hsotg);
+               hprt0 |= HPRT0_PWR;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+
+               /* Connect hcd. */
+               dwc2_hcd_connect(hsotg);
+
+               timeout_add_msec(&hsotg->wkp_timer, 71);
+       }
+
+       /* Set lx_state to and in_ppd to 0 as here core exits from suspend. */
+       hsotg->in_ppd = 0;
+       hsotg->lx_state = DWC2_L0;
+
+       dev_dbg(hsotg->dev, "Exiting host partial power down completed.\n");
+       return ret;
+}
+
+/**
+ * dwc2_host_enter_clock_gating() - Put controller in clock gating.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * This function is for entering Host mode clock gating.
+ */
+void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg)
+{
+       u32 hprt0;
+       u32 pcgctl;
+
+       dev_dbg(hsotg->dev, "Entering host clock gating.\n");
+
+       /* Put this port in suspend mode. */
+       hprt0 = dwc2_read_hprt0(hsotg);
+       hprt0 |= HPRT0_SUSP;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+
+       /* Set the Phy Clock bit as suspend is received. */
+       pcgctl = dwc2_readl(hsotg, PCGCTL);
+       pcgctl |= PCGCTL_STOPPCLK;
+       dwc2_writel(hsotg, pcgctl, PCGCTL);
+       udelay(5);
+
+       /* Set the Gate hclk as suspend is received. */
+       pcgctl = dwc2_readl(hsotg, PCGCTL);
+       pcgctl |= PCGCTL_GATEHCLK;
+       dwc2_writel(hsotg, pcgctl, PCGCTL);
+       udelay(5);
+
+       hsotg->bus_suspended = true;
+       hsotg->lx_state = DWC2_L2;
+}
+
+/**
+ * dwc2_host_exit_clock_gating() - Exit controller from clock gating.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by remote wakeup
+ *
+ * This function is for exiting Host mode clock gating.
+ */
+void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup)
+{
+       u32 hprt0;
+       u32 pcgctl;
+
+       dev_dbg(hsotg->dev, "Exiting host clock gating.\n");
+
+       /* Clear the Gate hclk. */
+       pcgctl = dwc2_readl(hsotg, PCGCTL);
+       pcgctl &= ~PCGCTL_GATEHCLK;
+       dwc2_writel(hsotg, pcgctl, PCGCTL);
+       udelay(5);
+
+       /* Phy Clock bit. */
+       pcgctl = dwc2_readl(hsotg, PCGCTL);
+       pcgctl &= ~PCGCTL_STOPPCLK;
+       dwc2_writel(hsotg, pcgctl, PCGCTL);
+       udelay(5);
+
+       /* Drive resume signaling and exit suspend mode on the port. */
+       hprt0 = dwc2_read_hprt0(hsotg);
+       hprt0 |= HPRT0_RES;
+       hprt0 &= ~HPRT0_SUSP;
+       dwc2_writel(hsotg, hprt0, HPRT0);
+       udelay(5);
+
+       if (!rem_wakeup) {
+               /* In case of port resume need to wait for 40 ms */
+               dwc2_msleep(USB_RESUME_TIMEOUT);
+
+               /* Stop driveing resume signaling on the port. */
+               hprt0 = dwc2_read_hprt0(hsotg);
+               hprt0 &= ~HPRT0_RES;
+               dwc2_writel(hsotg, hprt0, HPRT0);
+
+               hsotg->bus_suspended = false;
+               hsotg->lx_state = DWC2_L0;
+       } else {
+               timeout_add_msec(&hsotg->wkp_timer, 71);
+       }
+}
index c822b0f..9bbcdc8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcd.h,v 1.15 2021/07/27 13:36:59 mglocker Exp $  */
+/*     $OpenBSD: dwc2_hcd.h,v 1.16 2022/09/04 08:42:40 mglocker Exp $  */
 /*     $NetBSD: dwc2_hcd.h,v 1.9 2014/09/03 10:00:08 skrll Exp $       */
 
 /*
@@ -84,7 +84,7 @@ struct dwc2_qh;
  * @xfer_count:         Number of bytes transferred so far
  * @start_pkt_count:    Packet count at start of transfer
  * @xfer_started:       True if the transfer has been started
- * @ping:               True if a PING request should be issued on this channel
+ * @do_ping:            True if a PING request should be issued on this channel
  * @error_state:        True if the error count for this transaction is non-zero
  * @halt_on_queue:      True if this channel should be halted the next time a
  *                      request is queued for the channel. This is necessary in
@@ -106,7 +106,7 @@ struct dwc2_qh;
  * @schinfo:            Scheduling micro-frame bitmap
  * @ntd:                Number of transfer descriptors for the transfer
  * @halt_status:        Reason for halting the host channel
- * @hcint               Contents of the HCINT register when the interrupt came
+ * @hcint:               Contents of the HCINT register when the interrupt came
  * @qh:                 QH for the transfer being processed by this channel
  * @hc_list_entry:      For linking to list of host channels
  * @desc_list_addr:     Current QH's descriptor list DMA address
@@ -175,7 +175,8 @@ struct dwc2_hcd_pipe_info {
        u8 ep_num;
        u8 pipe_type;
        u8 pipe_dir;
-       u16 mps;
+       u16 maxp;
+       u16 maxp_mult;
 };
 
 struct dwc2_hcd_iso_packet_desc {
@@ -188,10 +189,10 @@ struct dwc2_hcd_iso_packet_desc {
 struct dwc2_qtd;
 
 struct dwc2_hcd_urb {
-       void *priv;             /* the xfer handle */
+       void *priv;
        struct dwc2_qtd *qtd;
        struct usb_dma *usbdma;
-       u8 *buf;
+       void *buf;
        dma_addr_t dma;
        struct usb_dma *setup_usbdma;
        void *setup_packet;
@@ -204,7 +205,7 @@ struct dwc2_hcd_urb {
        u32 flags;
        u16 interval;
        struct dwc2_hcd_pipe_info pipe_info;
-       struct dwc2_hcd_iso_packet_desc iso_descs[0];
+       struct dwc2_hcd_iso_packet_desc iso_descs[];
 };
 
 /* Phases for control transfers */
@@ -222,6 +223,43 @@ enum dwc2_transaction_type {
        DWC2_TRANSACTION_ALL,
 };
 
+/* The number of elements per LS bitmap (per port on multi_tt) */
+#define DWC2_ELEMENTS_PER_LS_BITMAP    DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
+                                                    BITS_PER_LONG)
+
+/**
+ * struct dwc2_tt - dwc2 data associated with a usb_tt
+ *
+ * @refcount:           Number of Queue Heads (QHs) holding a reference.
+ * @usb_tt:             Pointer back to the official usb_tt.
+ * @periodic_bitmaps:   Bitmap for which parts of the 1ms frame are accounted
+ *                      for already.  Each is DWC2_ELEMENTS_PER_LS_BITMAP
+ *                     elements (so sizeof(long) times that in bytes).
+ *
+ * This structure is stored in the hcpriv of the official usb_tt.
+ */
+struct dwc2_tt {
+       int refcount;
+       struct usbd_tt *usb_tt;
+       unsigned long periodic_bitmaps[];
+};
+
+/**
+ * struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
+ *
+ * @start_schedule_us:  The start time on the main bus schedule.  Note that
+ *                         the main bus schedule is tightly packed and this
+ *                        time should be interpreted as tightly packed (so
+ *                        uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
+ *                        instead of 125 us).
+ * @duration_us:           How long this transfer goes.
+ */
+
+struct dwc2_hs_transfer_time {
+       u32 start_schedule_us;
+       u16 duration_us;
+};
+
 /**
  * struct dwc2_qh - Software queue head structure
  *
@@ -233,6 +271,7 @@ enum dwc2_transaction_type {
  *                       - USB_ENDPOINT_XFER_ISOC
  * @ep_is_in:           Endpoint direction
  * @maxp:               Value from wMaxPacketSize field of Endpoint Descriptor
+ * @maxp_mult:          Multiplier for maxp
  * @dev_speed:          Device speed. One of the following values:
  *                       - USB_SPEED_LOW
  *                       - USB_SPEED_FULL
@@ -246,17 +285,36 @@ enum dwc2_transaction_type {
  * @do_split:           Full/low speed endpoint on high-speed hub requires split
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
- * @usecs:              Bandwidth in microseconds per (micro)frame
- * @interval:           Interval between transfers in (micro)frames
- * @sched_frame:        (Micro)frame to initialize a periodic transfer.
- *                      The transfer executes in the following (micro)frame.
- * @nak_frame:          Internal variable used by the NAK holdoff code
- * @frame_usecs:        Internal variable used by the microframe scheduler
- * @start_split_frame:  (Micro)frame at which last start split was initialized
+ * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @device_us:          Bandwidth in microseconds per transfer as seen by device
+ * @host_interval:      Interval between transfers as seen by the host.  If
+ *                      the host is high speed and the device is low speed this
+ *                      will be 8 times device interval.
+ * @device_interval:    Interval between transfers as seen by the device.
+ *                      interval.
+ * @next_active_frame:  (Micro)frame _before_ we next need to put something on
+ *                      the bus.  We'll move the qh to active here.  If the
+ *                      host is in high speed mode this will be a uframe.  If
+ *                      the host is in low speed mode this will be a full frame.
+ * @start_active_frame: If we are partway through a split transfer, this will be
+ *                     what next_active_frame was when we started.  Otherwise
+ *                     it should always be the same as next_active_frame.
+ * @num_hs_transfers:   Number of transfers in hs_transfers.
+ *                      Normally this is 1 but can be more than one for splits.
+ *                      Always >= 1 unless the host is in low/full speed mode.
+ * @hs_transfers:       Transfers that are scheduled as seen by the high speed
+ *                      bus.  Not used if host is in low or full speed mode (but
+ *                      note that it IS USED if the device is low or full speed
+ *                      as long as the HOST is in high speed mode).
+ * @ls_start_schedule_slice: Start time (in slices) on the low speed bus
+ *                           schedule that's being used by this device.  This
+ *                          will be on the periodic_bitmap in a
+ *                           "struct dwc2_tt".  Not used if this device is high
+ *                           speed.  Note that this is in "schedule slice" which
+ *                           is tightly packed.
  * @ntd:                Actual number of transfer descriptors in a list
  * @dw_align_buf:       Used instead of original buffer if its physical address
  *                      is not dword-aligned
- * @dw_align_buf_size:  Size of dw_align_buf
  * @dw_align_buf_dma:   DMA address for dw_align_buf
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -268,12 +326,19 @@ enum dwc2_transaction_type {
  * @n_bytes:            Xfer Bytes array. Each element corresponds to a transfer
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
+ * @unreserve_timer:    Timer for releasing periodic reservation.
  * @wait_timer:         Timer used to wait before re-queuing.
+ * @dwc_tt:            Pointer to our tt info (or NULL if no tt).
+ * @ttport:             Port number within our tt.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
+ * @unreserve_pending:  True if we planned to unreserve but haven't yet.
+ * @schedule_low_speed: True if we have a low/full speed component (either the
+ *                     host is in low/full speed mode or do_split).
  * @want_wait:          We should wait before re-queuing; only matters for non-
  *                      periodic transfers and is ignored for periodic ones.
  * @wait_timer_cancel:  Set to true to cancel the wait_timer.
  *
+ * @tt_buffer_dirty:   True if EP's TT buffer is not clean.
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
  * be entered in either the non-periodic or periodic schedule.
@@ -283,34 +348,42 @@ struct dwc2_qh {
        u8 ep_type;
        u8 ep_is_in;
        u16 maxp;
+       u16 maxp_mult;
        u8 dev_speed;
        u8 data_toggle;
        u8 ping_state;
        u8 do_split;
        u8 td_first;
        u8 td_last;
-       u16 usecs;
-       u16 interval;
-       u16 sched_frame;
-       u16 nak_frame;
-       u16 frame_usecs[8];
-       u16 start_split_frame;
+       u16 host_us;
+       u16 device_us;
+       u16 host_interval;
+       u16 device_interval;
+       u16 next_active_frame;
+       u16 start_active_frame;
+       s16 num_hs_transfers;
+       struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
+       u32 ls_start_schedule_slice;
        u16 ntd;
-       struct usb_dma dw_align_buf_usbdma;
        u8 *dw_align_buf;
        int dw_align_buf_size;
        dma_addr_t dw_align_buf_dma;
+       struct usb_dma dw_align_buf_usbdma;
        struct list_head qtd_list;
        struct dwc2_host_chan *channel;
        struct list_head qh_list_entry;
        struct usb_dma desc_list_usbdma;
-       struct dwc2_hcd_dma_desc *desc_list;
+       struct dwc2_dma_desc *desc_list;
        dma_addr_t desc_list_dma;
        u32 desc_list_sz;
        u32 *n_bytes;
-       /* XXX struct timer_list wait_timer; */
+       struct timeout unreserve_timer;
        struct timeout wait_timer;
+       struct dwc2_tt *dwc_tt;
+       int ttport;
        unsigned tt_buffer_dirty:1;
+       unsigned unreserve_pending:1;
+       unsigned schedule_low_speed:1;
        unsigned want_wait:1;
        unsigned wait_timer_cancel:1;
 };
@@ -347,6 +420,10 @@ struct dwc2_qh {
  * @urb:                URB for this transfer
  * @qh:                 Queue head for this QTD
  * @qtd_list_entry:     For linking to the QH's list of QTDs
+ * @isoc_td_first:     Index of first activated isochronous transfer
+ *                     descriptor in Descriptor DMA mode
+ * @isoc_td_last:      Index of last activated isochronous transfer
+ *                     descriptor in Descriptor DMA mode
  *
  * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
  * interrupt, or isochronous transfer. A single QTD is created for each URB
@@ -387,11 +464,15 @@ struct hc_xfer_info {
 };
 #endif
 
+u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
+
+#if 0
 /* Gets the struct usb_hcd that contains a struct dwc2_hsotg */
 static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
 {
        return (struct usb_hcd *)hsotg->priv;
 }
+#endif
 
 /*
  * Inline used to disable one channel interrupt. Channel interrupts are
@@ -402,19 +483,25 @@ static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
  */
 static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
 {
-       u32 mask = DWC2_READ_4(hsotg, HCINTMSK(chnum));
+       u32 mask = dwc2_readl(hsotg, HCINTMSK(chnum));
 
        mask &= ~intr;
-       DWC2_WRITE_4(hsotg, HCINTMSK(chnum), mask);
+       dwc2_writel(hsotg, mask, HCINTMSK(chnum));
 }
 
+void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
+void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
+                 enum dwc2_halt_status halt_status);
+void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
+                                struct dwc2_host_chan *chan);
+
 /*
  * Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
  * are read as 1, they won't clear when written back.
  */
 static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg)
 {
-       u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);
+       u32 hprt0 = dwc2_readl(hsotg, HPRT0);
 
        hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG);
        return hprt0;
@@ -430,9 +517,14 @@ static inline u8 dwc2_hcd_get_pipe_type(struct dwc2_hcd_pipe_info *pipe)
        return pipe->pipe_type;
 }
 
-static inline u16 dwc2_hcd_get_mps(struct dwc2_hcd_pipe_info *pipe)
+static inline u16 dwc2_hcd_get_maxp(struct dwc2_hcd_pipe_info *pipe)
+{
+       return pipe->maxp;
+}
+
+static inline u16 dwc2_hcd_get_maxp_mult(struct dwc2_hcd_pipe_info *pipe)
 {
-       return pipe->mps;
+       return pipe->maxp_mult;
 }
 
 static inline u8 dwc2_hcd_get_dev_addr(struct dwc2_hcd_pipe_info *pipe)
@@ -471,7 +563,7 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
 }
 
 extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
+//extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
 
 /* Transaction Execution Functions */
 extern enum dwc2_transaction_type dwc2_hcd_select_transactions(
@@ -495,10 +587,16 @@ extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
 extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
                            struct dwc2_qh *qh);
 
-/* Removes and frees a QTD */
-extern void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
-                                        struct dwc2_qtd *qtd,
-                                        struct dwc2_qh *qh);
+/* Unlinks and frees a QTD */
+static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
+                                               struct dwc2_qtd *qtd,
+                                               struct dwc2_qh *qh)
+{
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+
+       list_del(&qtd->qtd_list_entry);
+       pool_put(&sc->sc_qtdpool, qtd);
+}
 
 /* Descriptor DMA support functions */
 extern void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg,
@@ -519,6 +617,7 @@ extern void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
 #ifdef CONFIG_USB_DWC2_DEBUG_PERIODIC
 static inline bool dbg_hc(struct dwc2_host_chan *hc) { return true; }
 static inline bool dbg_qh(struct dwc2_qh *qh) { return true; }
+static inline bool dbg_urb(struct urb *urb) { return true; }
 static inline bool dbg_perio(void) { return true; }
 #else /* !CONFIG_USB_DWC2_DEBUG_PERIODIC */
 static inline bool dbg_hc(struct dwc2_host_chan *hc)
@@ -533,16 +632,17 @@ static inline bool dbg_qh(struct dwc2_qh *qh)
               qh->ep_type == USB_ENDPOINT_XFER_CONTROL;
 }
 
+#if 0
+static inline bool dbg_urb(struct urb *urb)
+{
+       return usb_pipetype(urb->pipe) == PIPE_BULK ||
+              usb_pipetype(urb->pipe) == PIPE_CONTROL;
+}
+#endif
 
 static inline bool dbg_perio(void) { return false; }
 #endif
 
-/* High bandwidth multiplier as encoded in highspeed endpoint descriptors */
-#define dwc2_hb_mult(wmaxpacketsize) (1 + (((wmaxpacketsize) >> 11) & 0x03))
-
-/* Packet size for any kind of endpoint descriptor */
-#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
-
 /*
  * Returns true if frame1 index is greater than frame2 index. The comparison
  * is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
@@ -586,6 +686,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
        return (frame + inc) & HFNUM_MAX_FRNUM;
 }
 
+static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
+{
+       return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
+}
+
 static inline u16 dwc2_full_frame_num(u16 frame)
 {
        return (frame & HFNUM_MAX_FRNUM) >> 3;
@@ -602,7 +707,8 @@ static inline u16 dwc2_micro_frame_num(u16 frame)
  */
 static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg)
 {
-       return DWC2_READ_4(hsotg, GINTSTS) & DWC2_READ_4(hsotg, GINTMSK);
+       return dwc2_readl(hsotg, GINTSTS) &
+              dwc2_readl(hsotg, GINTMSK);
 }
 
 static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb)
@@ -663,11 +769,11 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
                return 0;
        }
 
-       return qh->usecs;
+       return qh->host_us;
 }
 
 extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
-                                     struct dwc2_host_chan *chan, int chnum,
+                              struct dwc2_host_chan *chan, int chnum,
                                      struct dwc2_qtd *qtd);
 
 /* HCD Core API */
@@ -707,19 +813,6 @@ extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
  */
 extern void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
 
-/**
- * dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF
- *
- * @hsotg: The DWC2 HCD
- *
- * This can be used to determine average interrupt latency. Frame remaining is
- * also shown for start transfer and two additional sample points.
- *
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
- */
-extern void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
-
 /* URB interface */
 
 /* Transfer flags */
@@ -727,76 +820,30 @@ extern void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
 #define URB_SEND_ZERO_PACKET   0x2
 
 /* Host driver callbacks */
+struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
+                                     void *context, gfp_t mem_flags,
+                                     int *ttport);
 
-extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
-extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
-extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
-                              int *hub_addr, int *hub_port);
-extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
-extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
-                              int status);
-
-#ifdef DEBUG
-/*
- * Macro to sample the remaining PHY clocks left in the current frame. This
- * may be used during debugging to determine the average time it takes to
- * execute sections of code. There are two possible sample points, "a" and
- * "b", so the _letter_ argument must be one of these values.
- *
- * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
- * example, "cat /sys/devices/lm0/hcd_frrem".
- */
-#define dwc2_sample_frrem(_hcd_, _qh_, _letter_)                       \
-do {                                                                   \
-       struct hfnum_data _hfnum_;                                      \
-       struct dwc2_qtd *_qtd_;                                         \
-                                                                       \
-       _qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd,      \
-                          qtd_list_entry);                             \
-       if (usb_pipeint(_qtd_->urb->pipe) &&                            \
-           (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) { \
-               _hfnum_.d32 = DWC2_READ_4((_hcd_), HFNUM);              \
-               switch (_hfnum_.b.frnum & 0x7) {                        \
-               case 7:                                                 \
-                       (_hcd_)->hfnum_7_samples_##_letter_++;          \
-                       (_hcd_)->hfnum_7_frrem_accum_##_letter_ +=      \
-                               _hfnum_.b.frrem;                        \
-                       break;                                          \
-               case 0:                                                 \
-                       (_hcd_)->hfnum_0_samples_##_letter_++;          \
-                       (_hcd_)->hfnum_0_frrem_accum_##_letter_ +=      \
-                               _hfnum_.b.frrem;                        \
-                       break;                                          \
-               default:                                                \
-                       (_hcd_)->hfnum_other_samples_##_letter_++;      \
-                       (_hcd_)->hfnum_other_frrem_accum_##_letter_ +=  \
-                               _hfnum_.b.frrem;                        \
-                       break;                                          \
-               }                                                       \
-       }                                                               \
-} while (0)
-#else
-#define dwc2_sample_frrem(_hcd_, _qh_, _letter_)       do {} while (0)
-#endif
-
-
-void dwc2_wakeup_detected(void *);
+void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
+                          struct dwc2_tt *dwc_tt);
+int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
+void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
+                       int status);
 
+/* Required for OpenBSD */
+struct dwc2_hcd_urb * dwc2_hcd_urb_alloc(struct dwc2_hsotg *, int, gfp_t);
+void dwc2_hcd_urb_free(struct dwc2_hsotg *, struct dwc2_hcd_urb *, int);
 int dwc2_hcd_urb_dequeue(struct dwc2_hsotg *, struct dwc2_hcd_urb *);
-void dwc2_hcd_reinit(struct dwc2_hsotg *);
 int dwc2_hcd_hub_control(struct dwc2_hsotg *, u16, u16, u16, char *, u16);
-struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *);
-int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
-                               struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
-                               struct dwc2_qtd *qtd);
 void dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *, struct dwc2_hcd_urb *,
-                              u8 ,u8, u8, u8, u16);
+                              u8, u8, u8, u8, u16, u16);
 
-struct dwc2_hcd_urb * dwc2_hcd_urb_alloc(struct dwc2_hsotg *, int, gfp_t);
-void dwc2_hcd_urb_free(struct dwc2_hsotg *, struct dwc2_hcd_urb *, int);
-
-int _dwc2_hcd_start(struct dwc2_hsotg *);
+int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
+                                struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
+                                struct dwc2_qtd *qtd);
+void dwc2_allocate_bus_bandwidth(struct dwc2_hsotg *, u16, struct usbd_xfer *);
 
-int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *);
+long dwc2_usb_calc_bus_time(int, int, int, int);
+int dwc2_ttthink_to_ns(struct dwc2_hsotg *, void *, int);
 
 #endif /* __DWC2_HCD_H__ */
index 78a33ad..c562213 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcdddma.c,v 1.20 2021/11/28 09:25:02 mglocker Exp $      */
+/*     $OpenBSD: dwc2_hcdddma.c,v 1.21 2022/09/04 08:42:40 mglocker Exp $      */
 /*     $NetBSD: dwc2_hcdddma.c,v 1.6 2014/04/03 06:34:58 skrll Exp $   */
 
 /*
@@ -40,7 +40,6 @@
 /*
  * This file contains the Descriptor DMA implementation for Host mode
  */
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -88,21 +87,28 @@ STATIC u16 dwc2_max_desc_num(struct dwc2_qh *qh)
 STATIC u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
 {
        return qh->dev_speed == USB_SPEED_HIGH ?
-              (qh->interval + 8 - 1) / 8 : qh->interval;
+              (qh->host_interval + 8 - 1) / 8 : qh->host_interval;
 }
 
 STATIC int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                                gfp_t flags)
 {
        int err;
+#if 0
+       struct kmem_cache *desc_cache;
 
-       qh->desc_list = NULL;
-       qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
+       if (qh->ep_type == USB_ENDPOINT_XFER_ISOC &&
+           qh->dev_speed == USB_SPEED_HIGH)
+               desc_cache = hsotg->desc_hsisoc_cache;
+       else
+               desc_cache = hsotg->desc_gen_cache;
+#endif
+
+       qh->desc_list_sz = sizeof(struct dwc2_dma_desc) *
                                                dwc2_max_desc_num(qh);
 
        err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, qh->desc_list_sz, 0,
            USB_DMA_COHERENT, &qh->desc_list_usbdma);
-
        if (err)
                return -ENOMEM;
 
@@ -111,7 +117,6 @@ STATIC int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
        qh->n_bytes = malloc(sizeof(u32) * dwc2_max_desc_num(qh), M_USBHC,
            M_ZERO | M_WAITOK);
-
        if (!qh->n_bytes) {
                usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->desc_list_usbdma);
                qh->desc_list = NULL;
@@ -123,6 +128,15 @@ STATIC int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
 STATIC void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
+#if 0
+       struct kmem_cache *desc_cache;
+
+       if (qh->ep_type == USB_ENDPOINT_XFER_ISOC &&
+           qh->dev_speed == USB_SPEED_HIGH)
+               desc_cache = hsotg->desc_hsisoc_cache;
+       else
+               desc_cache = hsotg->desc_gen_cache;
+#endif
 
        if (qh->desc_list) {
                usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->desc_list_usbdma);
@@ -145,7 +159,6 @@ STATIC int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags)
        hsotg->frame_list = NULL;
        err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, hsotg->frame_list_sz,
            0, USB_DMA_COHERENT, &hsotg->frame_list_usbdma);
-
        if (!err) {
                hsotg->frame_list = KERNADDR(&hsotg->frame_list_usbdma, 0);
                hsotg->frame_list_dma = DMAADDR(&hsotg->frame_list_usbdma, 0);
@@ -184,19 +197,19 @@ STATIC void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en)
 
        spin_lock_irqsave(&hsotg->lock, flags);
 
-       hcfg = DWC2_READ_4(hsotg, HCFG);
+       hcfg = dwc2_readl(hsotg, HCFG);
        if (hcfg & HCFG_PERSCHEDENA) {
                /* already enabled */
                spin_unlock_irqrestore(&hsotg->lock, flags);
                return;
        }
 
-       DWC2_WRITE_4(hsotg, HFLBADDR, hsotg->frame_list_dma);
+       dwc2_writel(hsotg, hsotg->frame_list_dma, HFLBADDR);
 
        hcfg &= ~HCFG_FRLISTEN_MASK;
        hcfg |= fr_list_en | HCFG_PERSCHEDENA;
        dev_vdbg(hsotg->dev, "Enabling Periodic schedule\n");
-       DWC2_WRITE_4(hsotg, HCFG, hcfg);
+       dwc2_writel(hsotg, hcfg, HCFG);
 
        spin_unlock_irqrestore(&hsotg->lock, flags);
 }
@@ -208,7 +221,7 @@ STATIC void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg)
 
        spin_lock_irqsave(&hsotg->lock, flags);
 
-       hcfg = DWC2_READ_4(hsotg, HCFG);
+       hcfg = dwc2_readl(hsotg, HCFG);
        if (!(hcfg & HCFG_PERSCHEDENA)) {
                /* already disabled */
                spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -217,7 +230,7 @@ STATIC void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg)
 
        hcfg &= ~HCFG_PERSCHEDENA;
        dev_vdbg(hsotg->dev, "Disabling Periodic schedule\n");
-       DWC2_WRITE_4(hsotg, HCFG, hcfg);
+       dwc2_writel(hsotg, hcfg, HCFG);
 
        spin_unlock_irqrestore(&hsotg->lock, flags);
 }
@@ -251,7 +264,7 @@ STATIC void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
        chan = qh->channel;
        inc = dwc2_frame_incr_val(qh);
        if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
-               i = dwc2_frame_list_idx(qh->sched_frame);
+               i = dwc2_frame_list_idx(qh->next_active_frame);
        else
                i = 0;
 
@@ -275,13 +288,13 @@ STATIC void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                return;
 
        chan->schinfo = 0;
-       if (chan->speed == USB_SPEED_HIGH && qh->interval) {
+       if (chan->speed == USB_SPEED_HIGH && qh->host_interval) {
                j = 1;
                /* TODO - check this */
-               inc = (8 + qh->interval - 1) / qh->interval;
+               inc = (8 + qh->host_interval - 1) / qh->host_interval;
                for (i = 0; i < inc; i++) {
                        chan->schinfo |= j;
-                       j = j << qh->interval;
+                       j = j << qh->host_interval;
                }
        } else {
                chan->schinfo = 0xff;
@@ -294,7 +307,7 @@ STATIC void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
        struct dwc2_host_chan *chan = qh->channel;
 
        if (dwc2_qh_is_non_per(qh)) {
-               if (hsotg->core_params->uframe_sched > 0)
+               if (hsotg->params.uframe_sched)
                        hsotg->available_host_channels++;
                else
                        hsotg->non_periodic_channels--;
@@ -319,7 +332,7 @@ STATIC void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
        qh->ntd = 0;
 
        if (qh->desc_list)
-               memset(qh->desc_list, 0, sizeof(struct dwc2_hcd_dma_desc) *
+               memset(qh->desc_list, 0, sizeof(struct dwc2_dma_desc) *
                       dwc2_max_desc_num(qh));
 }
 
@@ -329,6 +342,7 @@ STATIC void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
  *
  * @hsotg: The HCD state structure for the DWC OTG controller
  * @qh:    The QH to init
+ * @mem_flags: Indicates the type of memory allocation
  *
  * Return: 0 if successful, negative error code otherwise
  *
@@ -401,7 +415,7 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
        if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
             qh->ep_type == USB_ENDPOINT_XFER_INT) &&
-           (hsotg->core_params->uframe_sched > 0 ||
+           (hsotg->params.uframe_sched ||
             !hsotg->periodic_channels) && hsotg->frame_list) {
                dwc2_per_sched_disable(hsotg);
                dwc2_frame_list_free(hsotg);
@@ -428,7 +442,10 @@ STATIC u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg,
 
        hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 
-       /* sched_frame is always frame number (not uFrame) both in FS and HS! */
+       /*
+        * next_active_frame is always frame number (not uFrame) both in FS
+        * and HS!
+        */
 
        /*
         * skip_frames is used to limit activated descriptors number
@@ -511,13 +528,13 @@ STATIC u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg,
                 */
                fr_idx_tmp = dwc2_frame_list_idx(frame);
                fr_idx = (FRLISTEN_64_SIZE +
-                         dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp)
-                        % dwc2_frame_incr_val(qh);
+                         dwc2_frame_list_idx(qh->next_active_frame) -
+                         fr_idx_tmp) % dwc2_frame_incr_val(qh);
                fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE;
        } else {
-               qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh,
+               qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh,
                                                           &skip_frames);
-               fr_idx = dwc2_frame_list_idx(qh->sched_frame);
+               fr_idx = dwc2_frame_list_idx(qh->next_active_frame);
        }
 
        qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx);
@@ -536,7 +553,7 @@ STATIC void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                                         struct dwc2_qh *qh, u32 max_xfer_size,
                                         u16 idx)
 {
-       struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx];
+       struct dwc2_dma_desc *dma_desc = &qh->desc_list[idx];
        struct dwc2_hcd_iso_packet_desc *frame_desc;
 
        memset(dma_desc, 0, sizeof(*dma_desc));
@@ -564,8 +581,8 @@ STATIC void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 #endif
 
        usb_syncmem(&qh->desc_list_usbdma,
-           (idx * sizeof(struct dwc2_hcd_dma_desc)),
-           sizeof(struct dwc2_hcd_dma_desc),
+           (idx * sizeof(struct dwc2_dma_desc)),
+           sizeof(struct dwc2_dma_desc),
            BUS_DMASYNC_PREWRITE);
 }
 
@@ -579,7 +596,7 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
        u16 next_idx;
 
        idx = qh->td_last;
-       inc = qh->interval;
+       inc = qh->host_interval;
        hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
        cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
        next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
@@ -601,11 +618,11 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                }
        }
 
-       if (qh->interval) {
-               ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
-                               qh->interval;
+       if (qh->host_interval) {
+               ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) /
+                               qh->host_interval;
                if (skip_frames && !qh->channel)
-                       ntd_max -= skip_frames / qh->interval;
+                       ntd_max -= skip_frames / qh->host_interval;
        }
 
        max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
@@ -636,10 +653,9 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
        if (qh->ntd == ntd_max) {
                idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
                qh->desc_list[idx].status |= HOST_DMA_IOC;
-
                usb_syncmem(&qh->desc_list_usbdma,
-                   (idx * sizeof(struct dwc2_hcd_dma_desc)),
-                   sizeof(struct dwc2_hcd_dma_desc),
+                   (idx * sizeof(struct dwc2_dma_desc)),
+                   sizeof(struct dwc2_dma_desc),
                    BUS_DMASYNC_PREWRITE);
        }
 #else
@@ -671,8 +687,8 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 
        qh->desc_list[idx].status |= HOST_DMA_IOC;
        usb_syncmem(&qh->desc_list_usbdma,
-           (idx * sizeof(struct dwc2_hcd_dma_desc)),
-           sizeof(struct dwc2_hcd_dma_desc),
+           (idx * sizeof(struct dwc2_dma_desc)),
+           sizeof(struct dwc2_dma_desc),
            BUS_DMASYNC_PREWRITE);
 #endif
 }
@@ -682,11 +698,11 @@ STATIC void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
                                    struct dwc2_qtd *qtd, struct dwc2_qh *qh,
                                    int n_desc)
 {
-       struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[n_desc];
+       struct dwc2_dma_desc *dma_desc = &qh->desc_list[n_desc];
        int len = chan->xfer_len;
 
-       if (len > MAX_DMA_DESC_SIZE - (chan->max_packet - 1))
-               len = MAX_DMA_DESC_SIZE - (chan->max_packet - 1);
+       if (len > HOST_DMA_NBYTES_LIMIT - (chan->max_packet - 1))
+               len = HOST_DMA_NBYTES_LIMIT - (chan->max_packet - 1);
 
        if (chan->ep_is_in) {
                int num_packets;
@@ -712,8 +728,8 @@ STATIC void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
        dma_desc->buf = (u32)chan->xfer_dma;
 
        usb_syncmem(&qh->desc_list_usbdma,
-           (n_desc * sizeof(struct dwc2_hcd_dma_desc)),
-           sizeof(struct dwc2_hcd_dma_desc),
+           (n_desc * sizeof(struct dwc2_dma_desc)),
+           sizeof(struct dwc2_dma_desc),
            BUS_DMASYNC_PREWRITE);
 
        /*
@@ -723,7 +739,7 @@ STATIC void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
        if (len > chan->xfer_len) {
                chan->xfer_len = 0;
        } else {
-               chan->xfer_dma += len;          /* XXXNH safe */
+               chan->xfer_dma += len;
                chan->xfer_len -= len;
        }
 }
@@ -768,8 +784,8 @@ STATIC void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                                         &qh->desc_list[n_desc - 1]);
                                usb_syncmem(&qh->desc_list_usbdma,
                                    ((n_desc - 1) *
-                                   sizeof(struct dwc2_hcd_dma_desc)),
-                                   sizeof(struct dwc2_hcd_dma_desc),
+                                   sizeof(struct dwc2_dma_desc)),
+                                   sizeof(struct dwc2_dma_desc),
                                    BUS_DMASYNC_PREWRITE);
                        }
                        dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
@@ -797,15 +813,15 @@ STATIC void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n",
                         n_desc - 1, &qh->desc_list[n_desc - 1]);
                usb_syncmem(&qh->desc_list_usbdma,
-                   ((n_desc - 1) * sizeof(struct dwc2_hcd_dma_desc)),
-                   sizeof(struct dwc2_hcd_dma_desc),
+                   ((n_desc - 1) * sizeof(struct dwc2_dma_desc)),
+                   sizeof(struct dwc2_dma_desc),
                    BUS_DMASYNC_PREWRITE);
                if (n_desc > 1) {
                        qh->desc_list[0].status |= HOST_DMA_A;
                        dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n",
                                 &qh->desc_list[0]);
                        usb_syncmem(&qh->desc_list_usbdma, 0,
-                           sizeof(struct dwc2_hcd_dma_desc),
+                           sizeof(struct dwc2_dma_desc),
                            BUS_DMASYNC_PREWRITE);
                }
                chan->ntd = n_desc;
@@ -881,7 +897,7 @@ STATIC int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                                        struct dwc2_qtd *qtd,
                                        struct dwc2_qh *qh, u16 idx)
 {
-       struct dwc2_hcd_dma_desc *dma_desc;
+       struct dwc2_dma_desc *dma_desc;
        struct dwc2_hcd_iso_packet_desc *frame_desc;
        u16 remain = 0;
        int rc = 0;
@@ -890,8 +906,8 @@ STATIC int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                return -EINVAL;
 
        usb_syncmem(&qh->desc_list_usbdma,
-           (idx * sizeof(struct dwc2_hcd_dma_desc)),
-           sizeof(struct dwc2_hcd_dma_desc),
+           (idx * sizeof(struct dwc2_dma_desc)),
+           sizeof(struct dwc2_dma_desc),
            BUS_DMASYNC_POSTREAD);
 
        dma_desc = &qh->desc_list[idx];
@@ -1020,7 +1036,7 @@ STATIC void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
                                                          idx);
                        if (rc < 0)
                                return;
-                       idx = dwc2_desclist_idx_inc(idx, qh->interval,
+                       idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
                                                    chan->speed);
                        if (!rc)
                                continue;
@@ -1030,7 +1046,7 @@ STATIC void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 
                        /* rc == DWC2_CMPL_STOP */
 
-                       if (qh->interval >= 32)
+                       if (qh->host_interval >= 32)
                                goto stop_scan;
 
                        qh->td_first = idx;
@@ -1052,9 +1068,9 @@ stop_scan:
 }
 
 STATIC int dwc2_update_non_isoc_urb_state_ddma(struct dwc2_hsotg *hsotg,
-                                       struct dwc2_host_chan *chan,
+                                              struct dwc2_host_chan *chan,
                                        struct dwc2_qtd *qtd,
-                                       struct dwc2_hcd_dma_desc *dma_desc,
+                                       struct dwc2_dma_desc *dma_desc,
                                        enum dwc2_halt_status halt_status,
                                        u32 n_bytes, int *xfer_done)
 {
@@ -1142,7 +1158,7 @@ STATIC int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
 {
        struct dwc2_qh *qh = chan->qh;
        struct dwc2_hcd_urb *urb = qtd->urb;
-       struct dwc2_hcd_dma_desc *dma_desc;
+       struct dwc2_dma_desc *dma_desc;
        u32 n_bytes;
        int failed;
 
@@ -1152,8 +1168,8 @@ STATIC int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
                return -EINVAL;
 
        usb_syncmem(&qh->desc_list_usbdma,
-           (desc_num * sizeof(struct dwc2_hcd_dma_desc)),
-           sizeof(struct dwc2_hcd_dma_desc),
+           (desc_num * sizeof(struct dwc2_dma_desc)),
+           sizeof(struct dwc2_dma_desc),
            BUS_DMASYNC_POSTREAD);
 
        dma_desc = &qh->desc_list[desc_num];
@@ -1164,14 +1180,11 @@ STATIC int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
        failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
                                                     halt_status, n_bytes,
                                                     xfer_done);
-       if (*xfer_done && urb->status != -EINPROGRESS)
-               failed = 1;
-
-       if (failed) {
+       if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
                dwc2_host_complete(hsotg, qtd, urb->status);
                dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
-               dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n",
-                        failed, *xfer_done, urb->status);
+               dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x\n",
+                        failed, *xfer_done);
                return failed;
        }
 
@@ -1226,21 +1239,25 @@ STATIC void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 
        list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) {
                int i;
+               int qtd_desc_count;
 
                qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry);
                xfer_done = 0;
+               qtd_desc_count = qtd->n_desc;
 
-               for (i = 0; i < qtd->n_desc; i++) {
+               for (i = 0; i < qtd_desc_count; i++) {
                        if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd,
                                                       desc_num, halt_status,
                                                       &xfer_done)) {
                                qtd = NULL;
-                               break;
+                               goto stop_scan;
                        }
+
                        desc_num++;
                }
        }
 
+stop_scan:
        if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) {
                /*
                 * Resetting the data toggle for bulk and interrupt endpoints
@@ -1248,8 +1265,8 @@ STATIC void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
                 */
                if (halt_status == DWC2_HC_XFER_STALL)
                        qh->data_toggle = DWC2_HC_PID_DATA0;
-               else if (qtd)
-                       dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
+               else
+                       dwc2_hcd_save_data_toggle(hsotg, chan, chnum, NULL);
        }
 
        if (halt_status == DWC2_HC_XFER_COMPLETE) {
@@ -1317,7 +1334,7 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
                        dwc2_hcd_qh_unlink(hsotg, qh);
                } else {
                        /* Keep in assigned schedule to continue transfer */
-                       list_move(&qh->qh_list_entry,
+                       list_move_tail(&qh->qh_list_entry,
                                       &hsotg->periodic_sched_assigned);
                        /*
                         * If channel has been halted during giveback of urb
index 871564a..892ec63 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcdintr.c,v 1.13 2021/09/04 10:19:28 mglocker Exp $      */
+/*     $OpenBSD: dwc2_hcdintr.c,v 1.14 2022/09/04 08:42:40 mglocker Exp $      */
 /*     $NetBSD: dwc2_hcdintr.c,v 1.11 2014/11/24 10:14:14 skrll Exp $  */
 
 /*
  * retransmission.  A 1 here means delay after the first NAK.
  */
 #define DWC2_NAKS_BEFORE_DELAY         3
-int dwc2_naks_before_delay = DWC2_NAKS_BEFORE_DELAY;
-
-#define DWC2_OUT_NAKS_BEFORE_DELAY     1
-int dwc2_out_naks_before_delay = DWC2_OUT_NAKS_BEFORE_DELAY;
 
 /* This function is for debug only */
 STATIC void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 {
-#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
        u16 curr_frame_number = hsotg->frame_number;
+       u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
+
+       if (expected != curr_frame_number)
+               dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
+                             expected, curr_frame_number);
 
+#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
        if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
-               if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) !=
-                   curr_frame_number) {
+               if (expected != curr_frame_number) {
                        hsotg->frame_num_array[hsotg->frame_num_idx] =
                                        curr_frame_number;
                        hsotg->last_frame_num_array[hsotg->frame_num_idx] =
@@ -95,15 +95,16 @@ STATIC void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
                }
                hsotg->dumped_frame_num_array = 1;
        }
-       hsotg->last_frame_num = curr_frame_number;
 #endif
+       hsotg->last_frame_num = curr_frame_number;
 }
 
 STATIC void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
                                    struct dwc2_host_chan *chan,
                                    struct dwc2_qtd *qtd)
 {
-//     struct urb *usb_urb;
+       //struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub;
+       //struct urb *usb_urb;
 
        if (!chan->qh)
                return;
@@ -114,9 +115,27 @@ STATIC void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
        if (!qtd->urb)
                return;
 
+#if 0
+       usb_urb = qtd->urb->priv;
+       if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt)
+               return;
+
+       /*
+        * The root hub doesn't really have a TT, but Linux thinks it
+        * does because how could you have a "high speed hub" that
+        * directly talks directly to low speed devices without a TT?
+        * It's all lies.  Lies, I tell you.
+        */
+       if (usb_urb->dev->tt->hub == root_hub)
+               return;
+#endif
 
        if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) {
                chan->qh->tt_buffer_dirty = 1;
+#if 0
+               if (usb_hub_clear_tt_buffer(usb_urb))
+                       /* Clear failed; let's hope things work anyway */
+#endif
                        chan->qh->tt_buffer_dirty = 0;
        }
 }
@@ -134,7 +153,7 @@ STATIC void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
        enum dwc2_transaction_type tr_type;
 
        /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SOF);
+       dwc2_writel(hsotg, GINTSTS_SOF, GINTSTS);
 
 #ifdef DEBUG_SOF
        dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
@@ -149,13 +168,19 @@ STATIC void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
        while (qh_entry != &hsotg->periodic_sched_inactive) {
                qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
                qh_entry = qh_entry->next;
-               if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
+               if (dwc2_frame_num_le(qh->next_active_frame,
+                                     hsotg->frame_number)) {
+                       dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
+                                     qh, hsotg->frame_number,
+                                     qh->next_active_frame);
+
                        /*
                         * Move QH to the ready list to be executed next
                         * (micro)frame
                         */
-                       list_move(&qh->qh_list_entry,
-                                 &hsotg->periodic_sched_ready);
+                       list_move_tail(&qh->qh_list_entry,
+                                      &hsotg->periodic_sched_ready);
+               }
        }
        tr_type = dwc2_hcd_select_transactions(hsotg);
        if (tr_type != DWC2_TRANSACTION_NONE)
@@ -169,13 +194,13 @@ STATIC void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg)
 {
-       u32 grxsts, chnum, bcnt, pktsts;
+       u32 grxsts, chnum, bcnt, dpid, pktsts;
        struct dwc2_host_chan *chan;
 
        if (dbg_perio())
                dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n");
 
-       grxsts = DWC2_READ_4(hsotg, GRXSTSP);
+       grxsts = dwc2_readl(hsotg, GRXSTSP);
        chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT;
        chan = hsotg->hc_ptr_array[chnum];
        if (!chan) {
@@ -184,14 +209,14 @@ STATIC void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg)
        }
 
        bcnt = (grxsts & GRXSTS_BYTECNT_MASK) >> GRXSTS_BYTECNT_SHIFT;
+       dpid = (grxsts & GRXSTS_DPID_MASK) >> GRXSTS_DPID_SHIFT;
        pktsts = (grxsts & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT;
 
        /* Packet Status */
        if (dbg_perio()) {
                dev_vdbg(hsotg->dev, "    Ch num = %d\n", chnum);
                dev_vdbg(hsotg->dev, "    Count = %d\n", bcnt);
-               dev_vdbg(hsotg->dev, "    DPID = %d, chan.dpid = %d\n",
-                        (grxsts & GRXSTS_DPID_MASK) >> GRXSTS_DPID_SHIFT,
+               dev_vdbg(hsotg->dev, "    DPID = %d, chan.dpid = %d\n", dpid,
                         chan->data_pid_start);
                dev_vdbg(hsotg->dev, "    PStatus = %d\n", pktsts);
        }
@@ -247,7 +272,7 @@ STATIC void dwc2_perio_tx_fifo_empty_intr(struct dwc2_hsotg *hsotg)
 STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
                              u32 *hprt0_modify)
 {
-       struct dwc2_core_params *params = hsotg->core_params;
+       struct dwc2_core_params *params = &hsotg->params;
        int do_reset = 0;
        u32 usbcfg;
        u32 prtspd;
@@ -258,22 +283,22 @@ STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
        dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
 
        /* Every time when port enables calculate HFIR.FrInterval */
-       hfir = DWC2_READ_4(hsotg, HFIR);
+       hfir = dwc2_readl(hsotg, HFIR);
        hfir &= ~HFIR_FRINT_MASK;
        hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT &
                HFIR_FRINT_MASK;
-       DWC2_WRITE_4(hsotg, HFIR, hfir);
+       dwc2_writel(hsotg, hfir, HFIR);
 
        /* Check if we need to adjust the PHY clock speed for low power */
        if (!params->host_support_fs_ls_low_power) {
                /* Port has been enabled, set the reset change flag */
                hsotg->flags.b.port_reset_change = 1;
 
-               dwc2_root_intr(hsotg->hsotg_sc);
+               dwc2_root_intr(hsotg->hsotg_sc); /* Required for OpenBSD */
                return;
        }
 
-       usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
        prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
 
        if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) {
@@ -281,17 +306,16 @@ STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
                if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) {
                        /* Set PHY low power clock select for FS/LS devices */
                        usbcfg |= GUSBCFG_PHY_LP_CLK_SEL;
-                       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
+                       dwc2_writel(hsotg, usbcfg, GUSBCFG);
                        do_reset = 1;
                }
 
-               hcfg = DWC2_READ_4(hsotg, HCFG);
+               hcfg = dwc2_readl(hsotg, HCFG);
                fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >>
                              HCFG_FSLSPCLKSEL_SHIFT;
 
                if (prtspd == HPRT0_SPD_LOW_SPEED &&
-                   params->host_ls_low_power_phy_clk ==
-                   DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) {
+                   params->host_ls_low_power_phy_clk) {
                        /* 6 MHZ */
                        dev_vdbg(hsotg->dev,
                                 "FS_PHY programming HCFG to 6 MHz\n");
@@ -299,7 +323,7 @@ STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
                                fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ;
                                hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
                                hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT;
-                               DWC2_WRITE_4(hsotg, HCFG, hcfg);
+                               dwc2_writel(hsotg, hcfg, HCFG);
                                do_reset = 1;
                        }
                } else {
@@ -310,7 +334,7 @@ STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
                                fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ;
                                hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
                                hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT;
-                               DWC2_WRITE_4(hsotg, HCFG, hcfg);
+                               dwc2_writel(hsotg, hcfg, HCFG);
                                do_reset = 1;
                        }
                }
@@ -318,21 +342,20 @@ STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
                /* Not low power */
                if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) {
                        usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL;
-                       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
+                       dwc2_writel(hsotg, usbcfg, GUSBCFG);
                        do_reset = 1;
                }
        }
 
        if (do_reset) {
                *hprt0_modify |= HPRT0_RST;
-               DWC2_WRITE_4(hsotg, HPRT0, *hprt0_modify);
+               dwc2_writel(hsotg, *hprt0_modify, HPRT0);
                queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
                                   msecs_to_jiffies(60));
        } else {
                /* Port has been enabled, set the reset change flag */
                hsotg->flags.b.port_reset_change = 1;
-               dwc2_root_intr(hsotg->hsotg_sc);
-
+               dwc2_root_intr(hsotg->hsotg_sc); /* Required for OpenBSD */
        }
 }
 
@@ -348,7 +371,7 @@ STATIC void dwc2_port_intr(struct dwc2_hsotg *hsotg)
 
        dev_vdbg(hsotg->dev, "--Port Interrupt--\n");
 
-       hprt0 = DWC2_READ_4(hsotg, HPRT0);
+       hprt0 = dwc2_readl(hsotg, HPRT0);
        hprt0_modify = hprt0;
 
        /*
@@ -363,7 +386,7 @@ STATIC void dwc2_port_intr(struct dwc2_hsotg *hsotg)
         * Set flag and clear if detected
         */
        if (hprt0 & HPRT0_CONNDET) {
-               DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_CONNDET);
+               dwc2_writel(hsotg, hprt0_modify | HPRT0_CONNDET, HPRT0);
 
                dev_vdbg(hsotg->dev,
                         "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
@@ -381,7 +404,7 @@ STATIC void dwc2_port_intr(struct dwc2_hsotg *hsotg)
         * Clear if detected - Set internal flag if disabled
         */
        if (hprt0 & HPRT0_ENACHG) {
-               DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_ENACHG);
+               dwc2_writel(hsotg, hprt0_modify | HPRT0_ENACHG, HPRT0);
                dev_vdbg(hsotg->dev,
                         "  --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
                         hprt0, !!(hprt0 & HPRT0_ENA));
@@ -390,27 +413,29 @@ STATIC void dwc2_port_intr(struct dwc2_hsotg *hsotg)
                        dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
                } else {
                        hsotg->flags.b.port_enable_change = 1;
-                       if (hsotg->core_params->dma_desc_fs_enable) {
+                       if (hsotg->params.dma_desc_fs_enable) {
                                u32 hcfg;
 
-                               hsotg->core_params->dma_desc_enable = 0;
+                               hsotg->params.dma_desc_enable = false;
                                hsotg->new_connection = false;
-                               hcfg = DWC2_READ_4(hsotg, HCFG);
+                               hcfg = dwc2_readl(hsotg, HCFG);
                                hcfg &= ~HCFG_DESCDMA;
-                               DWC2_WRITE_4(hsotg, HCFG, hcfg);
+                               dwc2_writel(hsotg, hcfg, HCFG);
                        }
                }
        }
 
        /* Overcurrent Change Interrupt */
        if (hprt0 & HPRT0_OVRCURRCHG) {
-               DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_OVRCURRCHG);
+               dwc2_writel(hsotg, hprt0_modify | HPRT0_OVRCURRCHG,
+                           HPRT0);
                dev_vdbg(hsotg->dev,
                         "  --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
                         hprt0);
                hsotg->flags.b.port_over_current_change = 1;
        }
 
+       /* Required for OpenBSD */
        if (hsotg->flags.b.port_connect_status_change ||
            hsotg->flags.b.port_enable_change ||
            hsotg->flags.b.port_over_current_change)
@@ -434,14 +459,14 @@ STATIC u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg,
 {
        u32 hctsiz, count, length;
 
-       hctsiz = DWC2_READ_4(hsotg, HCTSIZ(chnum));
+       hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
 
        if (halt_status == DWC2_HC_XFER_COMPLETE) {
                if (chan->ep_is_in) {
                        count = (hctsiz & TSIZ_XFERSIZE_MASK) >>
                                TSIZ_XFERSIZE_SHIFT;
                        length = chan->xfer_len - count;
-                       if (short_read != NULL)
+                       if (short_read)
                                *short_read = (count != 0);
                } else if (chan->qh->do_split) {
                        length = qtd->ssplit_out_xfer_count;
@@ -471,6 +496,12 @@ STATIC u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg,
  * of the URB based on the number of bytes transferred via the host channel.
  * Sets the URB status if the data transfer is finished.
  *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @chan: Programming view of host channel
+ * @chnum: Channel number
+ * @urb: Processing URB
+ * @qtd: Queue transfer descriptor
+ *
  * Return: 1 if the data transfer specified by the URB is completely finished,
  * 0 otherwise
  */
@@ -479,6 +510,7 @@ STATIC int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
                                 struct dwc2_hcd_urb *urb,
                                 struct dwc2_qtd *qtd)
 {
+       u32 hctsiz;
        int xfer_done = 0;
        int short_read = 0;
        int xfer_length = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd,
@@ -486,24 +518,10 @@ STATIC int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
                                                      &short_read);
 
        if (urb->actual_length + xfer_length > urb->length) {
-               dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__);
+               dev_dbg(hsotg->dev, "%s(): trimming xfer length\n", __func__);
                xfer_length = urb->length - urb->actual_length;
        }
 
-       /* Non DWORD-aligned buffer case handling */
-       if (chan->align_buf && xfer_length) {
-               dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-               usb_syncmem(urb->usbdma, 0, chan->qh->dw_align_buf_size,
-                   chan->ep_is_in ?
-                   BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-               if (chan->ep_is_in)
-                       memcpy(urb->buf + urb->actual_length,
-                                       chan->qh->dw_align_buf, xfer_length);
-               usb_syncmem(urb->usbdma, 0, chan->qh->dw_align_buf_size,
-                   chan->ep_is_in ?
-                   BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
-       }
-
        dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
                 urb->actual_length, xfer_length);
        urb->actual_length += xfer_length;
@@ -518,11 +536,12 @@ STATIC int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
                urb->status = 0;
        }
 
+       hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
        dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n",
                 __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum);
        dev_vdbg(hsotg->dev, "  chan->xfer_len %d\n", chan->xfer_len);
        dev_vdbg(hsotg->dev, "  hctsiz.xfersize %d\n",
-                (DWC2_READ_4(hsotg, HCTSIZ(chnum)) & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT);
+                (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT);
        dev_vdbg(hsotg->dev, "  urb->transfer_buffer_length %d\n", urb->length);
        dev_vdbg(hsotg->dev, "  urb->actual_length %d\n", urb->actual_length);
        dev_vdbg(hsotg->dev, "  short_read %d, xfer_done %d\n", short_read,
@@ -540,15 +559,27 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
                               struct dwc2_host_chan *chan, int chnum,
                               struct dwc2_qtd *qtd)
 {
-       u32 hctsiz = DWC2_READ_4(hsotg, HCTSIZ(chnum));
+       u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
        u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
 
        if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) {
+#if 0
+               if (WARN(!chan || !chan->qh,
+                        "chan->qh must be specified for non-control eps\n"))
+                       return;
+#endif
+
                if (pid == TSIZ_SC_MC_PID_DATA0)
                        chan->qh->data_toggle = DWC2_HC_PID_DATA0;
                else
                        chan->qh->data_toggle = DWC2_HC_PID_DATA1;
        } else {
+#if 0
+               if (WARN(!qtd,
+                        "qtd must be specified for control eps\n"))
+                       return;
+#endif
+
                if (pid == TSIZ_SC_MC_PID_DATA0)
                        qtd->data_toggle = DWC2_HC_PID_DATA0;
                else
@@ -563,6 +594,12 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
  * halt_status. Completes the Isochronous URB if all the URB frames have been
  * completed.
  *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @chan: Programming view of host channel
+ * @chnum: Channel number
+ * @halt_status: Reason for halting a host channel
+ * @qtd: Queue transfer descriptor
+ *
  * Return: DWC2_HC_XFER_COMPLETE if there are more frames remaining to be
  * transferred in the URB. Otherwise return DWC2_HC_XFER_URB_COMPLETE.
  */
@@ -584,25 +621,6 @@ STATIC enum dwc2_halt_status dwc2_update_isoc_urb_state(
                frame_desc->status = 0;
                frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
                                        chan, chnum, qtd, halt_status, NULL);
-
-               /* Non DWORD-aligned buffer case handling */
-               if (chan->align_buf && frame_desc->actual_length) {
-                       dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-                                __func__);
-                       struct usb_dma *ud = &chan->qh->dw_align_buf_usbdma;
-
-                       usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
-                           chan->ep_is_in ?
-                           BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-                       if (chan->ep_is_in)
-                               memcpy(urb->buf + frame_desc->offset +
-                                       qtd->isoc_split_offset,
-                                       chan->qh->dw_align_buf,
-                                       frame_desc->actual_length);
-                       usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
-                           chan->ep_is_in ?
-                           BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
-               }
                break;
        case DWC2_HC_XFER_FRAME_OVERRUN:
                urb->error_count++;
@@ -623,29 +641,10 @@ STATIC enum dwc2_halt_status dwc2_update_isoc_urb_state(
                frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
                                        chan, chnum, qtd, halt_status, NULL);
 
-               /* Non DWORD-aligned buffer case handling */
-               if (chan->align_buf && frame_desc->actual_length) {
-                       dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-                                __func__);
-                       struct usb_dma *ud = &chan->qh->dw_align_buf_usbdma;
-
-                       usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
-                           chan->ep_is_in ?
-                           BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-                       if (chan->ep_is_in)
-                               memcpy(urb->buf + frame_desc->offset +
-                                       qtd->isoc_split_offset,
-                                       chan->qh->dw_align_buf,
-                                       frame_desc->actual_length);
-                       usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
-                           chan->ep_is_in ?
-                           BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
-               }
-
                /* Skip whole frame */
                if (chan->qh->do_split &&
                    chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
-                   hsotg->core_params->dma_enable > 0) {
+                   hsotg->params.host_dma) {
                        qtd->complete_split = 0;
                        qtd->isoc_split_offset = 0;
                }
@@ -707,8 +706,6 @@ STATIC void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
        }
 
 no_qtd:
-       if (qh->channel)
-               qh->channel->align_buf = 0;
        qh->channel = NULL;
        dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
 }
@@ -786,7 +783,7 @@ cleanup:
        dwc2_hc_cleanup(hsotg, chan);
        list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
 
-       if (hsotg->core_params->uframe_sched > 0) {
+       if (hsotg->params.uframe_sched) {
                hsotg->available_host_channels++;
        } else {
                switch (chan->ep_type) {
@@ -805,9 +802,9 @@ cleanup:
                }
        }
 
-       haintmsk = DWC2_READ_4(hsotg, HAINTMSK);
+       haintmsk = dwc2_readl(hsotg, HAINTMSK);
        haintmsk &= ~(1 << chan->hc_num);
-       DWC2_WRITE_4(hsotg, HAINTMSK, haintmsk);
+       dwc2_writel(hsotg, haintmsk, HAINTMSK);
 
        /* Try to queue more transfers now that there's a free channel */
        tr_type = dwc2_hcd_select_transactions(hsotg);
@@ -832,7 +829,7 @@ STATIC void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
        if (dbg_hc(chan))
                dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
-       if (hsotg->core_params->dma_enable > 0) {
+       if (hsotg->params.host_dma) {
                if (dbg_hc(chan))
                        dev_vdbg(hsotg->dev, "DMA enabled\n");
                dwc2_release_channel(hsotg, chan, qtd, halt_status);
@@ -854,9 +851,9 @@ STATIC void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
                         * is enabled so that the non-periodic schedule will
                         * be processed
                         */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+                       gintmsk = dwc2_readl(hsotg, GINTMSK);
                        gintmsk |= GINTSTS_NPTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
                } else {
                        dev_vdbg(hsotg->dev, "isoc/intr\n");
                        /*
@@ -865,17 +862,17 @@ STATIC void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
                         * halt to be queued when the periodic schedule is
                         * processed.
                         */
-                       list_move(&chan->qh->qh_list_entry,
-                                 &hsotg->periodic_sched_assigned);
+                       list_move_tail(&chan->qh->qh_list_entry,
+                                      &hsotg->periodic_sched_assigned);
 
                        /*
                         * Make sure the Periodic Tx FIFO Empty interrupt is
                         * enabled so that the periodic schedule will be
                         * processed
                         */
-                       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+                       gintmsk = dwc2_readl(hsotg, GINTMSK);
                        gintmsk |= GINTSTS_PTXFEMP;
-                       DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
+                       dwc2_writel(hsotg, gintmsk, GINTMSK);
                }
        }
 }
@@ -940,7 +937,7 @@ STATIC void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg,
                                        struct dwc2_qtd *qtd,
                                        enum dwc2_halt_status halt_status)
 {
-       u32 hctsiz = DWC2_READ_4(hsotg, HCTSIZ(chnum));
+       u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
 
        qtd->error_count = 0;
 
@@ -958,6 +955,8 @@ STATIC int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
 {
        struct dwc2_hcd_iso_packet_desc *frame_desc;
        u32 len;
+       u32 hctsiz;
+       u32 pid;
 
        if (!qtd->urb)
                return 0;
@@ -965,16 +964,15 @@ STATIC int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
        frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
        len = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd,
                                          DWC2_HC_XFER_COMPLETE, NULL);
-       if (!len) {
+       if (!len && !qtd->isoc_split_offset) {
                qtd->complete_split = 0;
-               qtd->isoc_split_offset = 0;
                return 0;
        }
 
        frame_desc->actual_length += len;
 
        if (chan->align_buf) {
-               dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
+               dev_vdbg(hsotg->dev, "non-aligned buffer\n");
                usb_syncmem(qtd->urb->usbdma, chan->qh->dw_align_buf_dma,
                    chan->qh->dw_align_buf_size, BUS_DMASYNC_POSTREAD);
                memcpy(qtd->urb->buf + frame_desc->offset +
@@ -985,7 +983,10 @@ STATIC int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
 
        qtd->isoc_split_offset += len;
 
-       if (frame_desc->actual_length >= frame_desc->length) {
+       hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
+       pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
+
+       if (frame_desc->actual_length >= frame_desc->length || pid == 0) {
                frame_desc->status = 0;
                qtd->isoc_frame_index++;
                qtd->complete_split = 0;
@@ -1027,7 +1028,7 @@ STATIC void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
 
        pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
 
-       if (hsotg->core_params->dma_desc_enable > 0) {
+       if (hsotg->params.dma_desc_enable) {
                dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status);
                if (pipe_type == USB_ENDPOINT_XFER_ISOC)
                        /* Do not disable the interrupt, just clear it */
@@ -1038,7 +1039,7 @@ STATIC void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
        /* Handle xfer complete on CSPLIT */
        if (chan->qh->do_split) {
                if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
-                   hsotg->core_params->dma_enable > 0) {
+                   hsotg->params.host_dma) {
                        if (qtd->complete_split &&
                            dwc2_xfercomp_isoc_split_in(hsotg, chan, chnum,
                                                        qtd))
@@ -1126,7 +1127,8 @@ STATIC void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
                        dev_vdbg(hsotg->dev, "  Isochronous transfer complete\n");
                if (qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_ALL)
                        halt_status = dwc2_update_isoc_urb_state(hsotg, chan,
-                                       chnum, qtd, DWC2_HC_XFER_COMPLETE);
+                                                       chnum, qtd,
+                                                       DWC2_HC_XFER_COMPLETE);
                dwc2_complete_periodic_xfer(hsotg, chan, chnum, qtd,
                                            halt_status);
                break;
@@ -1150,7 +1152,7 @@ STATIC void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
        dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n",
                chnum);
 
-       if (hsotg->core_params->dma_desc_enable > 0) {
+       if (hsotg->params.dma_desc_enable) {
                dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
                                            DWC2_HC_XFER_STALL);
                goto handle_stall_done;
@@ -1198,38 +1200,22 @@ STATIC void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
 {
        u32 xfer_length = dwc2_get_actual_xfer_length(hsotg, chan, chnum,
                                                      qtd, halt_status, NULL);
+       u32 hctsiz;
 
        if (urb->actual_length + xfer_length > urb->length) {
                dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__);
                xfer_length = urb->length - urb->actual_length;
        }
 
-       /* Non DWORD-aligned buffer case handling */
-       if (chan->align_buf && xfer_length && chan->ep_is_in) {
-               dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-
-               struct usb_dma *ud = &chan->qh->dw_align_buf_usbdma;
-
-               usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
-                   chan->ep_is_in ?
-                   BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-               if (chan->ep_is_in)
-                       memcpy(urb->buf + urb->actual_length,
-                                       chan->qh->dw_align_buf,
-                                       xfer_length);
-               usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
-                   chan->ep_is_in ?
-                   BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
-       }
-
        urb->actual_length += xfer_length;
 
+       hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
        dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n",
                 __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum);
        dev_vdbg(hsotg->dev, "  chan->start_pkt_count %d\n",
                 chan->start_pkt_count);
        dev_vdbg(hsotg->dev, "  hctsiz.pktcnt %d\n",
-                (DWC2_READ_4(hsotg, HCTSIZ(chnum)) & TSIZ_PKTCNT_MASK) >> TSIZ_PKTCNT_SHIFT);
+                (hctsiz & TSIZ_PKTCNT_MASK) >> TSIZ_PKTCNT_SHIFT);
        dev_vdbg(hsotg->dev, "  chan->max_packet %d\n", chan->max_packet);
        dev_vdbg(hsotg->dev, "  bytes_transferred %d\n",
                 xfer_length);
@@ -1295,7 +1281,7 @@ STATIC void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
        switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
        case USB_ENDPOINT_XFER_CONTROL:
        case USB_ENDPOINT_XFER_BULK:
-               if (hsotg->core_params->dma_enable > 0 && chan->ep_is_in) {
+               if (hsotg->params.host_dma && chan->ep_is_in) {
                        /*
                         * NAK interrupts are enabled on bulk/control IN
                         * transfers in DMA mode for the sole purpose of
@@ -1441,7 +1427,7 @@ STATIC void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
         */
        if (chan->do_split && chan->complete_split) {
                if (chan->ep_is_in && chan->ep_type == USB_ENDPOINT_XFER_ISOC &&
-                   hsotg->core_params->dma_enable > 0) {
+                   hsotg->params.host_dma) {
                        qtd->complete_split = 0;
                        qtd->isoc_split_offset = 0;
                        qtd->isoc_frame_index++;
@@ -1459,14 +1445,55 @@ STATIC void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 
                if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
                    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-                       int frnum = dwc2_hcd_get_frame_number(hsotg);
+                       struct dwc2_qh *qh = chan->qh;
+                       bool past_end;
+
+                       if (!hsotg->params.uframe_sched) {
+                               int frnum = dwc2_hcd_get_frame_number(hsotg);
+
+                               /* Don't have num_hs_transfers; simple logic */
+                               past_end = dwc2_full_frame_num(frnum) !=
+                                    dwc2_full_frame_num(qh->next_active_frame);
+                       } else {
+                               int end_frnum;
 
-                       if (dwc2_full_frame_num(frnum) !=
-                           dwc2_full_frame_num(chan->qh->sched_frame)) {
                                /*
-                                * No longer in the same full speed frame.
-                                * Treat this as a transaction error.
+                                * Figure out the end frame based on
+                                * schedule.
+                                *
+                                * We don't want to go on trying again
+                                * and again forever. Let's stop when
+                                * we've done all the transfers that
+                                * were scheduled.
+                                *
+                                * We're going to be comparing
+                                * start_active_frame and
+                                * next_active_frame, both of which
+                                * are 1 before the time the packet
+                                * goes on the wire, so that cancels
+                                * out. Basically if had 1 transfer
+                                * and we saw 1 NYET then we're done.
+                                * We're getting a NYET here so if
+                                * next >= (start + num_transfers)
+                                * we're done. The complexity is that
+                                * for all but ISOC_OUT we skip one
+                                * slot.
                                 */
+                               end_frnum = dwc2_frame_num_inc(
+                                       qh->start_active_frame,
+                                       qh->num_hs_transfers);
+
+                               if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
+                                   qh->ep_is_in)
+                                       end_frnum =
+                                              dwc2_frame_num_inc(end_frnum, 1);
+
+                               past_end = dwc2_frame_num_le(
+                                       end_frnum, qh->next_active_frame);
+                       }
+
+                       if (past_end) {
+                               /* Treat this as a transaction error. */
 #if 0
                                /*
                                 * Todo: Fix system performance so this can
@@ -1517,9 +1544,9 @@ STATIC void dwc2_hc_babble_intr(struct dwc2_hsotg *hsotg,
        dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: Babble Error--\n",
                chnum);
 
-//     dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
+       dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
 
-       if (hsotg->core_params->dma_desc_enable > 0) {
+       if (hsotg->params.dma_desc_enable) {
                dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
                                            DWC2_HC_XFER_BABBLE_ERR);
                goto disable_int;
@@ -1549,6 +1576,11 @@ STATIC void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg,
                                struct dwc2_qtd *qtd)
 {
        struct dwc2_hcd_urb *urb = qtd->urb;
+       char *pipetype, *speed;
+       u32 hcchar;
+       u32 hcsplt;
+       u32 hctsiz;
+       u32 hc_dma;
 
        dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: AHB Error--\n",
                chnum);
@@ -1556,15 +1588,12 @@ STATIC void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg,
        if (!urb)
                goto handle_ahberr_halt;
 
-//     dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
-
-#ifdef DWC2_DEBUG
-       const char *pipetype, *speed;
+       dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
 
-       u32 hcchar = DWC2_READ_4(hsotg, HCCHAR(chnum));
-       u32 hcsplt = DWC2_READ_4(hsotg, HCSPLT(chnum));
-       u32 hctsiz = DWC2_READ_4(hsotg, HCTSIZ(chnum));
-       u32 hc_dma = DWC2_READ_4(hsotg, HCDMA(chnum));
+       hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
+       hcsplt = dwc2_readl(hsotg, HCSPLT(chnum));
+       hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
+       hc_dma = dwc2_readl(hsotg, HCDMA(chnum));
 
        dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum);
        dev_err(hsotg->dev, "  hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt);
@@ -1612,18 +1641,18 @@ STATIC void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg,
 
        dev_err(hsotg->dev, "  Speed: %s\n", speed);
 
-       dev_err(hsotg->dev, "  Max packet size: %d\n",
-               dwc2_hcd_get_mps(&urb->pipe_info));
+       dev_err(hsotg->dev, "  Max packet size: %d (mult %d)\n",
+               dwc2_hcd_get_maxp(&urb->pipe_info),
+               dwc2_hcd_get_maxp_mult(&urb->pipe_info));
        dev_err(hsotg->dev, "  Data buffer length: %d\n", urb->length);
        dev_err(hsotg->dev, "  Transfer buffer: %p, Transfer DMA: %08lx\n",
                urb->buf, (unsigned long)urb->dma);
        dev_err(hsotg->dev, "  Setup buffer: %p, Setup DMA: %08lx\n",
                urb->setup_packet, (unsigned long)urb->setup_dma);
        dev_err(hsotg->dev, "  Interval: %d\n", urb->interval);
-#endif
 
        /* Core halts the channel for Descriptor DMA mode */
-       if (hsotg->core_params->dma_desc_enable > 0) {
+       if (hsotg->params.dma_desc_enable) {
                dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
                                            DWC2_HC_XFER_AHB_ERR);
                goto handle_ahberr_done;
@@ -1653,9 +1682,9 @@ STATIC void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
        dev_dbg(hsotg->dev,
                "--Host Channel %d Interrupt: Transaction Error--\n", chnum);
 
-//     dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
+       dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
 
-       if (hsotg->core_params->dma_desc_enable > 0) {
+       if (hsotg->params.dma_desc_enable) {
                dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
                                            DWC2_HC_XFER_XACT_ERR);
                goto handle_xacterr_done;
@@ -1666,7 +1695,6 @@ STATIC void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
        case USB_ENDPOINT_XFER_BULK:
                qtd->error_count++;
                if (!chan->qh->ping_state) {
-
                        dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
                                                  qtd, DWC2_HC_XFER_XACT_ERR);
                        dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
@@ -1691,7 +1719,7 @@ STATIC void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
                        enum dwc2_halt_status halt_status;
 
                        halt_status = dwc2_update_isoc_urb_state(hsotg, chan,
-                                       chnum, qtd, DWC2_HC_XFER_XACT_ERR);
+                                        chnum, qtd, DWC2_HC_XFER_XACT_ERR);
                        dwc2_halt_channel(hsotg, chan, qtd, halt_status);
                }
                break;
@@ -1752,7 +1780,7 @@ STATIC void dwc2_hc_datatglerr_intr(struct dwc2_hsotg *hsotg,
                        "Data Toggle Error on OUT transfer, channel %d\n",
                        chnum);
 
-//     dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
+       dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
        disable_hc_int(hsotg, chnum, HCINTMSK_DATATGLERR);
 }
 
@@ -1778,10 +1806,10 @@ STATIC bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg,
                 * This code is here only as a check. This condition should
                 * never happen. Ignore the halt if it does occur.
                 */
-               hcchar = DWC2_READ_4(hsotg, HCCHAR(chnum));
-               hctsiz = DWC2_READ_4(hsotg, HCTSIZ(chnum));
-               hcintmsk = DWC2_READ_4(hsotg, HCINTMSK(chnum));
-               hcsplt = DWC2_READ_4(hsotg, HCSPLT(chnum));
+               hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
+               hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
+               hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
+               hcsplt = dwc2_readl(hsotg, HCSPLT(chnum));
                dev_dbg(hsotg->dev,
                        "%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n",
                         __func__);
@@ -1805,7 +1833,7 @@ STATIC bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg,
         * when the halt interrupt occurs. Halt the channel again if it does
         * occur.
         */
-       hcchar = DWC2_READ_4(hsotg, HCCHAR(chnum));
+       hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
        if (hcchar & HCCHAR_CHDIS) {
                dev_warn(hsotg->dev,
                         "%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n",
@@ -1849,8 +1877,8 @@ STATIC void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
 
        if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE ||
            (chan->halt_status == DWC2_HC_XFER_AHB_ERR &&
-            hsotg->core_params->dma_desc_enable <= 0)) {
-               if (hsotg->core_params->dma_desc_enable > 0)
+            !hsotg->params.dma_desc_enable)) {
+               if (hsotg->params.dma_desc_enable)
                        dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
                                                    chan->halt_status);
                else
@@ -1865,7 +1893,7 @@ STATIC void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
                return;
        }
 
-       hcintmsk = DWC2_READ_4(hsotg, HCINTMSK(chnum));
+       hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
 
        if (chan->hcint & HCINTMSK_XFERCOMPL) {
                /*
@@ -1881,7 +1909,7 @@ STATIC void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
        } else if (chan->hcint & HCINTMSK_STALL) {
                dwc2_hc_stall_intr(hsotg, chan, chnum, qtd);
        } else if ((chan->hcint & HCINTMSK_XACTERR) &&
-                  hsotg->core_params->dma_desc_enable <= 0) {
+                  !hsotg->params.dma_desc_enable) {
                if (out_nak_enh) {
                        if (chan->hcint &
                            (HCINTMSK_NYET | HCINTMSK_NAK | HCINTMSK_ACK)) {
@@ -1901,10 +1929,10 @@ STATIC void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
                 */
                dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
        } else if ((chan->hcint & HCINTMSK_XCS_XACT) &&
-                  hsotg->core_params->dma_desc_enable > 0) {
+                  hsotg->params.dma_desc_enable) {
                dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
        } else if ((chan->hcint & HCINTMSK_AHBERR) &&
-                  hsotg->core_params->dma_desc_enable > 0) {
+                  hsotg->params.dma_desc_enable) {
                dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd);
        } else if (chan->hcint & HCINTMSK_BBLERR) {
                dwc2_hc_babble_intr(hsotg, chan, chnum, qtd);
@@ -1960,7 +1988,7 @@ STATIC void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
                                dev_err(hsotg->dev,
                                        "hcint 0x%08x, intsts 0x%08x\n",
                                        chan->hcint,
-                                       DWC2_READ_4(hsotg, GINTSTS));
+                                       dwc2_readl(hsotg, GINTSTS));
                                goto error;
                        }
                }
@@ -2009,7 +2037,7 @@ STATIC void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg,
                dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: Channel Halted--\n",
                         chnum);
 
-       if (hsotg->core_params->dma_enable > 0) {
+       if (hsotg->params.host_dma) {
                dwc2_hc_chhltd_intr_dma(hsotg, chan, chnum, qtd);
        } else {
                if (!dwc2_halt_status_ok(hsotg, chan, chnum, qtd))
@@ -2028,7 +2056,7 @@ STATIC bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh)
 {
        struct dwc2_qtd *cur_head;
 
-       if (qh == NULL)
+       if (!qh)
                return false;
 
        cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd,
@@ -2045,11 +2073,11 @@ STATIC void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
 
        chan = hsotg->hc_ptr_array[chnum];
 
-       hcint = DWC2_READ_4(hsotg, HCINT(chnum));
-       hcintmsk = DWC2_READ_4(hsotg, HCINTMSK(chnum));
+       hcint = dwc2_readl(hsotg, HCINT(chnum));
+       hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
        if (!chan) {
                dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n");
-               DWC2_WRITE_4(hsotg, HCINT(chnum), hcint);
+               dwc2_writel(hsotg, hcint, HCINT(chnum));
                return;
        }
 
@@ -2061,7 +2089,17 @@ STATIC void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
                         hcint, hcintmsk, hcint & hcintmsk);
        }
 
-       DWC2_WRITE_4(hsotg, HCINT(chnum), hcint);
+       dwc2_writel(hsotg, hcint, HCINT(chnum));
+
+       /*
+        * If we got an interrupt after someone called
+        * dwc2_hcd_endpoint_disable() we don't want to crash below
+        */
+       if (!chan->qh) {
+               dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
+               return;
+       }
+
        chan->hcint = hcint;
        hcint &= hcintmsk;
 
@@ -2076,7 +2114,7 @@ STATIC void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
                 * interrupt unmasked
                 */
                WARN_ON(hcint != HCINTMSK_CHHLTD);
-               if (hsotg->core_params->dma_desc_enable > 0)
+               if (hsotg->params.dma_desc_enable)
                        dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
                                                    chan->halt_status);
                else
@@ -2104,7 +2142,7 @@ STATIC void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
        qtd = list_first_entry(&chan->qh->qtd_list, struct dwc2_qtd,
                               qtd_list_entry);
 
-       if (hsotg->core_params->dma_enable <= 0) {
+       if (!hsotg->params.host_dma) {
                if ((hcint & HCINTMSK_CHHLTD) && hcint != HCINTMSK_CHHLTD)
                        hcint &= ~HCINTMSK_CHHLTD;
        }
@@ -2186,7 +2224,7 @@ STATIC void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
        int i;
        struct dwc2_host_chan *chan, *chan_tmp;
 
-       haint = DWC2_READ_4(hsotg, HAINT);
+       haint = dwc2_readl(hsotg, HAINT);
        if (dbg_perio()) {
                dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
@@ -2209,7 +2247,7 @@ STATIC void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
                }
        }
 
-       for (i = 0; i < hsotg->core_params->host_channels; i++) {
+       for (i = 0; i < hsotg->params.host_channels; i++) {
                if (haint & (1 << i))
                        dwc2_hc_n_intr(hsotg, i);
        }
@@ -2226,12 +2264,13 @@ irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
                return retval;
        }
 
-       MUTEX_ASSERT_LOCKED(&hsotg->lock);
+       spin_lock(&hsotg->lock);
 
        /* Check if HOST Mode */
        if (dwc2_is_host_mode(hsotg)) {
                gintsts = dwc2_read_core_intr(hsotg);
                if (!gintsts) {
+                       spin_unlock(&hsotg->lock);
                        return retval;
                }
 
@@ -2269,10 +2308,12 @@ irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
                                 "DWC OTG HCD Finished Servicing Interrupts\n");
                        dev_vdbg(hsotg->dev,
                                 "DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n",
-                                DWC2_READ_4(hsotg, GINTSTS),
-                                DWC2_READ_4(hsotg, GINTMSK));
+                                dwc2_readl(hsotg, GINTSTS),
+                                dwc2_readl(hsotg, GINTMSK));
                }
        }
 
+       spin_unlock(&hsotg->lock);
+
        return retval;
 }
index 172e418..9f56c26 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcdqueue.c,v 1.12 2021/09/04 10:19:28 mglocker Exp $     */
+/*     $OpenBSD: dwc2_hcdqueue.c,v 1.13 2022/09/04 08:42:40 mglocker Exp $     */
 /*     $NetBSD: dwc2_hcdqueue.c,v 1.11 2014/09/03 10:00:08 skrll Exp $ */
 
 /*
@@ -41,7 +41,6 @@
  * This file contains the functions to manage Queue Heads and Queue
  * Transfer Descriptors for Host mode
  */
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <dev/usb/dwc2/dwc2_core.h>
 #include <dev/usb/dwc2/dwc2_hcd.h>
 
-STATIC u32 dwc2_calc_bus_time(struct dwc2_hsotg *, int, int, int, int);
-STATIC void dwc2_wait_timer_fn(void *);
-
-/* If we get a NAK, wait this long before retrying */
-#define DWC2_RETRY_WAIT_DELAY 1        /* msec */
+#include <dev/usb/dwc2/gcd.h>
 
-/**
- * dwc2_qh_init() - Initializes a QH structure
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    The QH to init
- * @urb:   Holds the information about the device/endpoint needed to initialize
- *         the QH
+/*
+ * XXX: Include from dev/pci/drm/include/linux?  Although,
+ * bitmap_find_next_zero_area() is missing there currently ...
  */
-#define SCHEDULE_SLOP 10
-STATIC void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-                        struct dwc2_hcd_urb *urb)
+static inline void
+__set_bit(u_int b, volatile void *p)
 {
-       int dev_speed, hub_addr, hub_port;
-
-       dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-       /* Initialize QH */
-       qh->hsotg = hsotg;
-       /* XXX timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0); */
-       timeout_set(&qh->wait_timer, dwc2_wait_timer_fn, qh);
-       qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-       qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
-
-       qh->data_toggle = DWC2_HC_PID_DATA0;
-       qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
-       INIT_LIST_HEAD(&qh->qtd_list);
-       INIT_LIST_HEAD(&qh->qh_list_entry);
-
-       /* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-       dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
-
-       dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
-       qh->nak_frame = 0xffff;
+       volatile u_int *ptr = (volatile u_int *)p;
+       ptr[b >> 5] |= (1 << (b & 0x1f));
+}
 
-       if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-           hub_addr != 0 && hub_addr != 1) {
-               dev_vdbg(hsotg->dev,
-                        "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-                        dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-                        hub_port);
-               qh->do_split = 1;
-       }
+static inline void
+__clear_bit(u_int b, volatile void *p)
+{
+       volatile u_int *ptr = (volatile u_int *)p;
+       ptr[b >> 5] &= ~(1 << (b & 0x1f));
+}
 
-       if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-           qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-               /* Compute scheduling parameters once and save them */
-               u32 hprt, prtspd;
-
-               /* Todo: Account for split transfers in the bus time */
-               int bytecount =
-                       dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-               qh->usecs = dwc2_calc_bus_time(hsotg, qh->do_split ?
-                               USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-                               qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-                               bytecount);
-
-               /* Ensure frame_number corresponds to the reality */
-               hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-               /* Start in a slightly future (micro)frame */
-               qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
-                                                    SCHEDULE_SLOP);
-               qh->interval = urb->interval;
-#if 0
-               /* Increase interrupt polling rate for debugging */
-               if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-                       qh->interval = 8;
-#endif
-               hprt = DWC2_READ_4(hsotg, HPRT0);
-               prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-               if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-                   (dev_speed == USB_SPEED_LOW ||
-                    dev_speed == USB_SPEED_FULL)) {
-                       qh->interval *= 8;
-                       qh->sched_frame |= 0x7;
-                       qh->start_split_frame = qh->sched_frame;
+static inline int
+find_next_bit(volatile void *p, int max, int b)
+{
+       volatile u_int *ptr = (volatile u_int *)p;
+
+       for (; b < max; b+= 32) {
+               if (ptr[b >> 5] != 0) {
+                       for (;;) {
+                               if (ptr[b >> 5] & (1 << (b & 0x1f)))
+                                       return b;
+                               b++;
+                       }
                }
-               dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
-       }
-
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-                dwc2_hcd_get_dev_addr(&urb->pipe_info));
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-                dwc2_hcd_get_ep_num(&urb->pipe_info),
-                dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
-
-       qh->dev_speed = dev_speed;
-
-#ifdef DWC2_DEBUG
-       const char *speed, *type;
-       switch (dev_speed) {
-       case USB_SPEED_LOW:
-               speed = "low";
-               break;
-       case USB_SPEED_FULL:
-               speed = "full";
-               break;
-       case USB_SPEED_HIGH:
-               speed = "high";
-               break;
-       default:
-               speed = "?";
-               break;
        }
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
+       return max;
+}
 
-       switch (qh->ep_type) {
-       case USB_ENDPOINT_XFER_ISOC:
-               type = "isochronous";
-               break;
-       case USB_ENDPOINT_XFER_INT:
-               type = "interrupt";
-               break;
-       case USB_ENDPOINT_XFER_CONTROL:
-               type = "control";
-               break;
-       case USB_ENDPOINT_XFER_BULK:
-               type = "bulk";
-               break;
-       default:
-               type = "?";
-               break;
+static inline int
+find_next_zero_bit(volatile void *p, int max, int b)
+{
+       volatile u_int *ptr = (volatile u_int *)p;
+
+       for (; b < max; b += 32) {
+               if (ptr[b >> 5] != ~0) {
+                       for (;;) {
+                               if ((ptr[b >> 5] & (1 << (b & 0x1f))) == 0)
+                                       return b;
+                               b++;
+                       }
+               }
        }
+       return max;
+}
 
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-#endif
-
-       if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-               dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-                        qh->usecs);
-               dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-                        qh->interval);
+unsigned long
+bitmap_find_next_zero_area_off(unsigned long *,
+                              unsigned long,
+                              unsigned long,
+                              unsigned int,
+                              unsigned long,
+                              unsigned long);
+
+#define __ALIGN_KERNEL_MASK(x, mask)   (((x) + (mask)) & ~(mask))
+#define __ALIGN_MASK(x, mask)          __ALIGN_KERNEL_MASK((x), (mask))
+
+unsigned long
+bitmap_find_next_zero_area_off(unsigned long *map,
+                              unsigned long size,
+                              unsigned long start,
+                              unsigned int nr,
+                              unsigned long align_mask,
+                              unsigned long align_offset)
+{
+       unsigned long index, end, i;
+again:
+       index = find_next_zero_bit(map, size, start);
+
+       /* Align allocation */
+       index = __ALIGN_MASK(index + align_offset, align_mask) - align_offset;
+
+       end = index + nr;
+       if (end > size)
+               return end;
+       i = find_next_bit(map, end, index);
+       if (i < end) {
+               start = i + 1;
+               goto again;
        }
+       return index;
 }
 
-/**
- * dwc2_hcd_qh_create() - Allocates and initializes a QH
- *
- * @hsotg:     The HCD state structure for the DWC OTG controller
- * @urb:       Holds the information about the device/endpoint needed
- *             to initialize the QH
- * @mem_flags: Flag to do atomic allocation if needed
- *
- * Return: Pointer to the newly allocated QH, or NULL on error
- */
-struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
-                                         struct dwc2_hcd_urb *urb,
-                                         gfp_t mem_flags)
+static inline unsigned long
+bitmap_find_next_zero_area(unsigned long *map,
+                          unsigned long size,
+                          unsigned long start,
+                          unsigned int nr,
+                          unsigned long align_mask)
 {
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
-       struct dwc2_qh *qh;
-
-       if (!urb->priv)
-               return NULL;
-
-       /* Allocate memory */
-       qh = pool_get(&sc->sc_qhpool, PR_NOWAIT);
-       if (!qh)
-               return NULL;
-
-       memset(qh, 0, sizeof(*qh));
-       dwc2_qh_init(hsotg, qh, urb);
+       return bitmap_find_next_zero_area_off(map, size, start, nr,
+                                             align_mask, 0);
+}
 
-       if (hsotg->core_params->dma_desc_enable > 0 &&
-           dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
-               dwc2_hcd_qh_free(hsotg, qh);
-               return NULL;
-       }
+static inline void
+bitmap_set(void *p, int b, u_int n)
+{
+       u_int end = b + n;
 
-       return qh;
+       for (; b < end; b++)
+               __set_bit(b, p);
 }
 
-/**
- * dwc2_hcd_qh_free() - Frees the QH
- *
- * @hsotg: HCD instance
- * @qh:    The QH to free
- *
- * QH should already be removed from the list. QTD list should already be empty
- * if called from URB Dequeue.
- *
- * Must NOT be called with interrupt disabled or spinlock held
- */
-void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+static inline void
+bitmap_clear(void *p, int b, u_int n)
 {
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
+       u_int end = b + n;
 
-       /*
-        * We don't have the lock so we can safely wait until the wait timer
-        * finishes.  Of course, at this point in time we'd better have set
-        * wait_timer_active to false so if this timer was still pending it
-        * won't do anything anyway, but we want it to finish before we free
-        * memory.
-        */
-       /* XXX del_timer_sync(&qh->wait_timer); */
+       for (; b < end; b++)
+               __clear_bit(b, p);
+}
 
-       timeout_del(&qh->wait_timer);
-       if (qh->desc_list) {
-               dwc2_hcd_qh_free_ddma(hsotg, qh);
-       } else if (qh->dw_align_buf) {
-               usb_freemem(&sc->sc_bus, &qh->dw_align_buf_usbdma);
-               qh->dw_align_buf_dma = (dma_addr_t)0;
-       }
+STATIC void dwc2_wait_timer_fn(void *);
 
-       pool_put(&sc->sc_qhpool, qh);
-}
+/* Wait this long before releasing periodic reservation */
+#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
+
+/* If we get a NAK, wait this long before retrying */
+#define DWC2_RETRY_WAIT_DELAY 1        /* msec */
 
 /**
  * dwc2_periodic_channel_available() - Checks that a channel is available for a
@@ -293,15 +205,14 @@ STATIC int dwc2_periodic_channel_available(struct dwc2_hsotg *hsotg)
        int status;
        int num_channels;
 
-       num_channels = hsotg->core_params->host_channels;
-       if (hsotg->periodic_channels + hsotg->non_periodic_channels <
-                                                               num_channels
-           && hsotg->periodic_channels < num_channels - 1) {
+       num_channels = hsotg->params.host_channels;
+       if ((hsotg->periodic_channels + hsotg->non_periodic_channels <
+            num_channels) && (hsotg->periodic_channels < num_channels - 1)) {
                status = 0;
        } else {
                dev_dbg(hsotg->dev,
-                       "%s: Total channels: %d, Periodic: %d, "
-                       "Non-periodic: %d\n", __func__, num_channels,
+                       "%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
+                       __func__, num_channels,
                        hsotg->periodic_channels, hsotg->non_periodic_channels);
                status = -ENOSPC;
        }
@@ -334,19 +245,19 @@ STATIC int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
                 * High speed mode
                 * Max periodic usecs is 80% x 125 usec = 100 usec
                 */
-               max_claimed_usecs = 100 - qh->usecs;
+               max_claimed_usecs = 100 - qh->host_us;
        } else {
                /*
                 * Full speed mode
                 * Max periodic usecs is 90% x 1000 usec = 900 usec
                 */
-               max_claimed_usecs = 900 - qh->usecs;
+               max_claimed_usecs = 900 - qh->host_us;
        }
 
        if (hsotg->periodic_usecs > max_claimed_usecs) {
                dev_err(hsotg->dev,
                        "%s: already claimed usecs %d, required usecs %d\n",
-                       __func__, hsotg->periodic_usecs, qh->usecs);
+                       __func__, hsotg->periodic_usecs, qh->host_us);
                status = -ENOSPC;
        }
 
@@ -354,194 +265,1225 @@ STATIC int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 }
 
 /**
- * Microframe scheduler
- * track the total use in hsotg->frame_usecs
- * keep each qh use in qh->frame_usecs
- * when surrendering the qh then donate the time back
+ * pmap_schedule() - Schedule time in a periodic bitmap (pmap).
+ *
+ * @map:             The bitmap representing the schedule; will be updated
+ *                   upon success.
+ * @bits_per_period: The schedule represents several periods.  This is how many
+ *                   bits are in each period.  It's assumed that the beginning
+ *                   of the schedule will repeat after its end.
+ * @periods_in_map:  The number of periods in the schedule.
+ * @num_bits:        The number of bits we need per period we want to reserve
+ *                   in this function call.
+ * @interval:        How often we need to be scheduled for the reservation this
+ *                   time.  1 means every period.  2 means every other period.
+ *                   ...you get the picture?
+ * @start:           The bit number to start at.  Normally 0.  Must be within
+ *                   the interval or we return failure right away.
+ * @only_one_period: Normally we'll allow picking a start anywhere within the
+ *                   first interval, since we can still make all repetition
+ *                   requirements by doing that.  However, if you pass true
+ *                   here then we'll return failure if we can't fit within
+ *                   the period that "start" is in.
+ *
+ * The idea here is that we want to schedule time for repeating events that all
+ * want the same resource.  The resource is divided into fixed-sized periods
+ * and the events want to repeat every "interval" periods.  The schedule
+ * granularity is one bit.
+ *
+ * To keep things "simple", we'll represent our schedule with a bitmap that
+ * contains a fixed number of periods.  This gets rid of a lot of complexity
+ * but does mean that we need to handle things specially (and non-ideally) if
+ * the number of the periods in the schedule doesn't match well with the
+ * intervals that we're trying to schedule.
+ *
+ * Here's an explanation of the scheme we'll implement, assuming 8 periods.
+ * - If interval is 1, we need to take up space in each of the 8
+ *   periods we're scheduling.  Easy.
+ * - If interval is 2, we need to take up space in half of the
+ *   periods.  Again, easy.
+ * - If interval is 3, we actually need to fall back to interval 1.
+ *   Why?  Because we might need time in any period.  AKA for the
+ *   first 8 periods, we'll be in slot 0, 3, 6.  Then we'll be
+ *   in slot 1, 4, 7.  Then we'll be in 2, 5.  Then we'll be back to
+ *   0, 3, and 6.  Since we could be in any frame we need to reserve
+ *   for all of them.  Sucks, but that's what you gotta do.  Note that
+ *   if we were instead scheduling 8 * 3 = 24 we'd do much better, but
+ *   then we need more memory and time to do scheduling.
+ * - If interval is 4, easy.
+ * - If interval is 5, we again need interval 1.  The schedule will be
+ *   0, 5, 2, 7, 4, 1, 6, 3, 0
+ * - If interval is 6, we need interval 2.  0, 6, 4, 2.
+ * - If interval is 7, we need interval 1.
+ * - If interval is 8, we need interval 8.
+ *
+ * If you do the math, you'll see that we need to pretend that interval is
+ * equal to the greatest_common_divisor(interval, periods_in_map).
+ *
+ * Note that at the moment this function tends to front-pack the schedule.
+ * In some cases that's really non-ideal (it's hard to schedule things that
+ * need to repeat every period).  In other cases it's perfect (you can easily
+ * schedule bigger, less often repeating things).
+ *
+ * Here's the algorithm in action (8 periods, 5 bits per period):
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |*****|  ***|*****|  ***|*****|  ***|*****|  ***|   OK 3 bits, intv 3 at 2
+ *  |*****|* ***|*****|  ***|*****|* ***|*****|  ***|   OK 1 bits, intv 4 at 5
+ *  |**   |*    |**   |     |**   |*    |**   |     | Remv 3 bits, intv 3 at 2
+ *  |***  |*    |***  |     |***  |*    |***  |     |   OK 1 bits, intv 6 at 2
+ *  |**** |*  * |**** |   * |**** |*  * |**** |   * |   OK 1 bits, intv 1 at 3
+ *  |**** |**** |**** | *** |**** |**** |**** | *** |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 1 at 4
+ *  |*****|*****|*****| ****|*****|*****|*****| ****| FAIL 1 bits, intv 1
+ *  |  ***|*****|  ***| ****|  ***|*****|  ***| ****| Remv 2 bits, intv 2 at 0
+ *  |  ***| ****|  ***| ****|  ***| ****|  ***| ****| Remv 1 bits, intv 4 at 5
+ *  |   **| ****|   **| ****|   **| ****|   **| ****| Remv 1 bits, intv 6 at 2
+ *  |    *| ** *|    *| ** *|    *| ** *|    *| ** *| Remv 1 bits, intv 1 at 3
+ *  |    *|    *|    *|    *|    *|    *|    *|    *| Remv 2 bits, intv 2 at 6
+ *  |     |     |     |     |     |     |     |     | Remv 1 bits, intv 1 at 4
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |***  |     |**   |     |***  |     |**   |     |   OK 1 bits, intv 4 at 2
+ *  |*****|     |** **|     |*****|     |** **|     |   OK 2 bits, intv 2 at 3
+ *  |*****|*    |** **|     |*****|*    |** **|     |   OK 1 bits, intv 4 at 5
+ *  |*****|***  |** **| **  |*****|***  |** **| **  |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|** **| ****|*****|*****|** **| ****|   OK 2 bits, intv 2 at 8
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 4 at 12
+ *
+ * This function is pretty generic and could be easily abstracted if anything
+ * needed similar scheduling.
+ *
+ * Returns either -ENOSPC or a >= 0 start bit which should be passed to the
+ * unschedule routine.  The map bitmap will be updated on a non-error result.
  */
-STATIC const unsigned short max_uframe_usecs[] = {
-       100, 100, 100, 100, 100, 100, 30, 0
-};
-
-void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
+static int pmap_schedule(unsigned long *map, int bits_per_period,
+                        int periods_in_map, int num_bits,
+                        int interval, int start, bool only_one_period)
 {
+       int interval_bits;
+       int to_reserve;
+       int first_end;
        int i;
 
-       for (i = 0; i < 8; i++)
-               hsotg->frame_usecs[i] = max_uframe_usecs[i];
-}
+       if (num_bits > bits_per_period)
+               return -ENOSPC;
 
-STATIC int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-       unsigned short utime = qh->usecs;
-       int i;
+       /* Adjust interval as per description */
+       interval = gcd(interval, periods_in_map);
 
-       for (i = 0; i < 8; i++) {
-               /* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
-               if (utime <= hsotg->frame_usecs[i]) {
-                       hsotg->frame_usecs[i] -= utime;
-                       qh->frame_usecs[i] += utime;
-                       return i;
-               }
-       }
-       return -ENOSPC;
-}
+       interval_bits = bits_per_period * interval;
+       to_reserve = periods_in_map / interval;
 
-/*
- * use this for FS apps that can span multiple uframes
- */
-STATIC int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-       unsigned short utime = qh->usecs;
-       unsigned short xtime;
-       int t_left;
-       int i;
-       int j;
-       int k;
+       /* If start has gotten us past interval then we can't schedule */
+       if (start >= interval_bits)
+               return -ENOSPC;
 
-       for (i = 0; i < 8; i++) {
-               if (hsotg->frame_usecs[i] <= 0)
-                       continue;
+       if (only_one_period)
+               /* Must fit within same period as start; end at begin of next */
+               first_end = (start / bits_per_period + 1) * bits_per_period;
+       else
+               /* Can fit anywhere in the first interval */
+               first_end = interval_bits;
+
+       /*
+        * We'll try to pick the first repetition, then see if that time
+        * is free for each of the subsequent repetitions.  If it's not
+        * we'll adjust the start time for the next search of the first
+        * repetition.
+        */
+       while (start + num_bits <= first_end) {
+               int end;
+
+               /* Need to stay within this period */
+               end = (start / bits_per_period + 1) * bits_per_period;
+
+               /* Look for num_bits us in this microframe starting at start */
+               start = bitmap_find_next_zero_area(map, end, start, num_bits,
+                                                  0);
 
                /*
-                * we need n consecutive slots so use j as a start slot
-                * j plus j+1 must be enough time (for now)
+                * We should get start >= end if we fail.  We might be
+                * able to check the next microframe depending on the
+                * interval, so continue on (start already updated).
                 */
-               xtime = hsotg->frame_usecs[i];
-               for (j = i + 1; j < 8; j++) {
-                       /*
-                        * if we add this frame remaining time to xtime we may
-                        * be OK, if not we need to test j for a complete frame
-                        */
-                       if (xtime + hsotg->frame_usecs[j] < utime) {
-                               if (hsotg->frame_usecs[j] <
-                                                       max_uframe_usecs[j])
-                                       continue;
-                       }
-                       if (xtime >= utime) {
-                               t_left = utime;
-                               for (k = i; k < 8; k++) {
-                                       t_left -= hsotg->frame_usecs[k];
-                                       if (t_left <= 0) {
-                                               qh->frame_usecs[k] +=
-                                                       hsotg->frame_usecs[k]
-                                                               + t_left;
-                                               hsotg->frame_usecs[k] = -t_left;
-                                               return i;
-                                       } else {
-                                               qh->frame_usecs[k] +=
-                                                       hsotg->frame_usecs[k];
-                                               hsotg->frame_usecs[k] = 0;
-                                       }
-                               }
-                       }
-                       /* add the frame time to x time */
-                       xtime += hsotg->frame_usecs[j];
-                       /* we must have a fully available next frame or break */
-                       if (xtime < utime &&
-                          hsotg->frame_usecs[j] == max_uframe_usecs[j])
+               if (start >= end) {
+                       start = end;
+                       continue;
+               }
+
+               /* At this point we have a valid point for first one */
+               for (i = 1; i < to_reserve; i++) {
+                       int ith_start = start + interval_bits * i;
+                       int ith_end = end + interval_bits * i;
+                       int ret;
+
+                       /* Use this as a dumb "check if bits are 0" */
+                       ret = bitmap_find_next_zero_area(
+                               map, ith_start + num_bits, ith_start, num_bits,
+                               0);
+
+                       /* We got the right place, continue checking */
+                       if (ret == ith_start)
                                continue;
+
+                       /* Move start up for next time and exit for loop */
+                       ith_start = bitmap_find_next_zero_area(
+                               map, ith_end, ith_start, num_bits, 0);
+                       if (ith_start >= ith_end)
+                               /* Need a while new period next time */
+                               start = end;
+                       else
+                               start = ith_start - interval_bits * i;
+                       break;
                }
+
+               /* If didn't exit the for loop with a break, we have success */
+               if (i == to_reserve)
+                       break;
+       }
+
+       if (start + num_bits > first_end)
+               return -ENOSPC;
+
+       for (i = 0; i < to_reserve; i++) {
+               int ith_start = start + interval_bits * i;
+
+               bitmap_set(map, ith_start, num_bits);
        }
-       return -ENOSPC;
+
+       return start;
 }
 
-STATIC int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/**
+ * pmap_unschedule() - Undo work done by pmap_schedule()
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @num_bits:        The number of bits that was passed to schedule.
+ * @interval:        The interval that was passed to schedule.
+ * @start:           The return value from pmap_schedule().
+ */
+static void pmap_unschedule(unsigned long *map, int bits_per_period,
+                           int periods_in_map, int num_bits,
+                           int interval, int start)
 {
-       int ret;
+       int interval_bits;
+       int to_release;
+       int i;
 
-       if (qh->dev_speed == USB_SPEED_HIGH) {
-               /* if this is a hs transaction we need a full frame */
-               ret = dwc2_find_single_uframe(hsotg, qh);
-       } else {
-               /*
-                * if this is a fs transaction we may need a sequence
-                * of frames
-                */
-               ret = dwc2_find_multi_uframe(hsotg, qh);
+       /* Adjust interval as per description in pmap_schedule() */
+       interval = gcd(interval, periods_in_map);
+
+       interval_bits = bits_per_period * interval;
+       to_release = periods_in_map / interval;
+
+       for (i = 0; i < to_release; i++) {
+               int ith_start = start + interval_bits * i;
+
+               bitmap_clear(map, ith_start, num_bits);
        }
-       return ret;
 }
 
 /**
- * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a
- * host channel is large enough to handle the maximum data transfer in a single
- * (micro)frame for a periodic transfer
+ * dwc2_get_ls_map() - Get the map used for the given qh
  *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    QH for a periodic endpoint
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH for the periodic transfer.
  *
- * Return: 0 if successful, negative error code otherwise
+ * We'll always get the periodic map out of our TT.  Note that even if we're
+ * running the host straight in low speed / full speed mode it appears as if
+ * a TT is allocated for us, so we'll use it.  If that ever changes we can
+ * add logic here to get a map out of "hsotg" if !qh->do_split.
+ *
+ * Returns: the map or NULL if a map couldn't be found.
  */
-STATIC int dwc2_check_max_xfer_size(struct dwc2_hsotg *hsotg,
-                                   struct dwc2_qh *qh)
+static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
+                                     struct dwc2_qh *qh)
 {
-       u32 max_xfer_size;
-       u32 max_channel_xfer_size;
-       int status = 0;
+       unsigned long *map;
 
-       max_xfer_size = dwc2_max_packet(qh->maxp) * dwc2_hb_mult(qh->maxp);
-       max_channel_xfer_size = hsotg->core_params->max_transfer_size;
+       /* Don't expect to be missing a TT and be doing low speed scheduling */
+       if (WARN_ON(!qh->dwc_tt))
+               return NULL;
 
-       if (max_xfer_size > max_channel_xfer_size) {
-               dev_err(hsotg->dev,
-                       "%s: Periodic xfer length %d > max xfer length for channel %d\n",
-                       __func__, max_xfer_size, max_channel_xfer_size);
-               status = -ENOSPC;
-       }
+       /* Get the map and adjust if this is a multi_tt hub */
+       map = qh->dwc_tt->periodic_bitmaps;
+       if (qh->dwc_tt->usb_tt->hub->multi)
+               map += DWC2_ELEMENTS_PER_LS_BITMAP * (qh->ttport - 1);
 
-       return status;
+       return map;
 }
 
-/**
- * dwc2_schedule_periodic() - Schedules an interrupt or isochronous transfer in
- * the periodic schedule
+#ifdef DWC2_PRINT_SCHEDULE
+/*
+ * cat_printf() - A printf() + strcat() helper
  *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    QH for the periodic transfer. The QH should already contain the
- *         scheduling information.
+ * This is useful for concatenating a bunch of strings where each string is
+ * constructed using printf.
  *
- * Return: 0 if successful, negative error code otherwise
+ * @buf:   The destination buffer; will be updated to point after the printed
+ *         data.
+ * @size:  The number of bytes in the buffer (includes space for '\0').
+ * @fmt:   The format for printf.
+ * @...:   The args for printf.
  */
-STATIC int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+static __printf(3, 4)
+void cat_printf(char **buf, size_t *size, const char *fmt, ...)
 {
-       int status;
-
-       if (hsotg->core_params->uframe_sched > 0) {
-               int frame = -1;
+       va_list args;
+       int i;
 
-               status = dwc2_find_uframe(hsotg, qh);
-               if (status == 0)
-                       frame = 7;
-               else if (status > 0)
-                       frame = status - 1;
+       if (*size == 0)
+               return;
 
-               /* Set the new frame up */
-               if (frame >= 0) {
-                       qh->sched_frame &= ~0x7;
-                       qh->sched_frame |= (frame & 7);
-               }
+       va_start(args, fmt);
+       i = vsnprintf(*buf, *size, fmt, args);
+       va_end(args);
 
-               if (status > 0)
-                       status = 0;
+       if (i >= *size) {
+               (*buf)[*size - 1] = '\0';
+               *buf += *size;
+               *size = 0;
        } else {
-               status = dwc2_periodic_channel_available(hsotg);
-               if (status) {
-                       dev_info(hsotg->dev,
-                                "%s: No host channel available for periodic transfer\n",
-                                __func__);
-                       return status;
+               *buf += i;
+               *size -= i;
+       }
+}
+
+/*
+ * pmap_print() - Print the given periodic map
+ *
+ * Will attempt to print out the periodic schedule.
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @period_name:     The name of 1 period, like "uFrame"
+ * @units:           The name of the units, like "us".
+ * @print_fn:        The function to call for printing.
+ * @print_data:      Opaque data to pass to the print function.
+ */
+static void pmap_print(unsigned long *map, int bits_per_period,
+                      int periods_in_map, const char *period_name,
+                      const char *units,
+                      void (*print_fn)(const char *str, void *data),
+                      void *print_data)
+{
+       int period;
+
+       for (period = 0; period < periods_in_map; period++) {
+               char tmp[64];
+               char *buf = tmp;
+               size_t buf_size = sizeof(tmp);
+               int period_start = period * bits_per_period;
+               int period_end = period_start + bits_per_period;
+               int start = 0;
+               int count = 0;
+               bool printed = false;
+               int i;
+
+               for (i = period_start; i < period_end + 1; i++) {
+                       /* Handle case when ith bit is set */
+                       if (i < period_end &&
+                           bitmap_find_next_zero_area(map, i + 1,
+                                                      i, 1, 0) != i) {
+                               if (count == 0)
+                                       start = i - period_start;
+                               count++;
+                               continue;
+                       }
+
+                       /* ith bit isn't set; don't care if count == 0 */
+                       if (count == 0)
+                               continue;
+
+                       if (!printed)
+                               cat_printf(&buf, &buf_size, "%s %d: ",
+                                          period_name, period);
+                       else
+                               cat_printf(&buf, &buf_size, ", ");
+                       printed = true;
+
+                       cat_printf(&buf, &buf_size, "%d %s -%3d %s", start,
+                                  units, start + count - 1, units);
+                       count = 0;
                }
 
-               status = dwc2_check_periodic_bandwidth(hsotg, qh);
+               if (printed)
+                       print_fn(tmp, print_data);
        }
+}
 
-       if (status) {
-               dev_dbg(hsotg->dev,
-                       "%s: Insufficient periodic bandwidth for periodic transfer\n",
-                       __func__);
-               return status;
-       }
+struct dwc2_qh_print_data {
+       struct dwc2_hsotg *hsotg;
+       struct dwc2_qh *qh;
+};
+
+/**
+ * dwc2_qh_print() - Helper function for dwc2_qh_schedule_print()
+ *
+ * @str:  The string to print
+ * @data: A pointer to a struct dwc2_qh_print_data
+ */
+static void dwc2_qh_print(const char *str, void *data)
+{
+       struct dwc2_qh_print_data *print_data = data;
+
+       dwc2_sch_dbg(print_data->hsotg, "QH=%p ...%s\n", print_data->qh, str);
+}
+
+/**
+ * dwc2_qh_schedule_print() - Print the periodic schedule
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH to print.
+ */
+static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
+                                  struct dwc2_qh *qh)
+{
+       struct dwc2_qh_print_data print_data = { hsotg, qh };
+       int i;
+
+       /*
+        * The printing functions are quite slow and inefficient.
+        * If we don't have tracing turned on, don't run unless the special
+        * define is turned on.
+        */
+
+       if (qh->schedule_low_speed) {
+               unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+               dwc2_sch_dbg(hsotg, "QH=%p LS/FS trans: %d=>%d us @ %d us",
+                            qh, qh->device_us,
+                            DWC2_ROUND_US_TO_SLICE(qh->device_us),
+                            DWC2_US_PER_SLICE * qh->ls_start_schedule_slice);
+
+               if (map) {
+                       dwc2_sch_dbg(hsotg,
+                                    "QH=%p Whole low/full speed map %p now:\n",
+                                    qh, map);
+                       pmap_print(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+                                  DWC2_LS_SCHEDULE_FRAMES, "Frame ", "slices",
+                                  dwc2_qh_print, &print_data);
+               }
+       }
+
+       for (i = 0; i < qh->num_hs_transfers; i++) {
+               struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + i;
+               int uframe = trans_time->start_schedule_us /
+                            DWC2_HS_PERIODIC_US_PER_UFRAME;
+               int rel_us = trans_time->start_schedule_us %
+                            DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+               dwc2_sch_dbg(hsotg,
+                            "QH=%p HS trans #%d: %d us @ uFrame %d + %d us\n",
+                            qh, i, trans_time->duration_us, uframe, rel_us);
+       }
+       if (qh->num_hs_transfers) {
+               dwc2_sch_dbg(hsotg, "QH=%p Whole high speed map now:\n", qh);
+               pmap_print(hsotg->hs_periodic_bitmap,
+                          DWC2_HS_PERIODIC_US_PER_UFRAME,
+                          DWC2_HS_SCHEDULE_UFRAMES, "uFrame", "us",
+                          dwc2_qh_print, &print_data);
+       }
+}
+#else
+static inline void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
+                                         struct dwc2_qh *qh) {};
+#endif
+
+/**
+ * dwc2_ls_pmap_schedule() - Schedule a low speed QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller.
+ * @qh:           QH for the periodic transfer.
+ * @search_slice: We'll start trying to schedule at the passed slice.
+ *                Remember that slices are the units of the low speed
+ *                schedule (think 25us or so).
+ *
+ * Wraps pmap_schedule() with the right parameters for low speed scheduling.
+ *
+ * Normally we schedule low speed devices on the map associated with the TT.
+ *
+ * Returns: 0 for success or an error code.
+ */
+static int dwc2_ls_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+                                int search_slice)
+{
+       int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+       unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+       int slice;
+
+       if (!map)
+               return -EINVAL;
+
+       /*
+        * Schedule on the proper low speed map with our low speed scheduling
+        * parameters.  Note that we use the "device_interval" here since
+        * we want the low speed interval and the only way we'd be in this
+        * function is if the device is low speed.
+        *
+        * If we happen to be doing low speed and high speed scheduling for the
+        * same transaction (AKA we have a split) we always do low speed first.
+        * That means we can always pass "false" for only_one_period (that
+        * parameters is only useful when we're trying to get one schedule to
+        * match what we already planned in the other schedule).
+        */
+       slice = pmap_schedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+                             DWC2_LS_SCHEDULE_FRAMES, slices,
+                             qh->device_interval, search_slice, false);
+
+       if (slice < 0)
+               return slice;
+
+       qh->ls_start_schedule_slice = slice;
+       return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_ls_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_ls_pmap_unschedule(struct dwc2_hsotg *hsotg,
+                                   struct dwc2_qh *qh)
+{
+       int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+       unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+       /* Schedule should have failed, so no worries about no error code */
+       if (!map)
+               return;
+
+       pmap_unschedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+                       DWC2_LS_SCHEDULE_FRAMES, slices, qh->device_interval,
+                       qh->ls_start_schedule_slice);
+}
+
+/**
+ * dwc2_hs_pmap_schedule - Schedule in the main high speed schedule
+ *
+ * This will schedule something on the main dwc2 schedule.
+ *
+ * We'll start looking in qh->hs_transfers[index].start_schedule_us.  We'll
+ * update this with the result upon success.  We also use the duration from
+ * the same structure.
+ *
+ * @hsotg:           The HCD state structure for the DWC OTG controller.
+ * @qh:              QH for the periodic transfer.
+ * @only_one_period: If true we will limit ourselves to just looking at
+ *                   one period (aka one 100us chunk).  This is used if we have
+ *                   already scheduled something on the low speed schedule and
+ *                   need to find something that matches on the high speed one.
+ * @index:           The index into qh->hs_transfers that we're working with.
+ *
+ * Returns: 0 for success or an error code.  Upon success the
+ *          dwc2_hs_transfer_time specified by "index" will be updated.
+ */
+static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+                                bool only_one_period, int index)
+{
+       struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+       int us;
+
+       us = pmap_schedule(hsotg->hs_periodic_bitmap,
+                          DWC2_HS_PERIODIC_US_PER_UFRAME,
+                          DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+                          qh->host_interval, trans_time->start_schedule_us,
+                          only_one_period);
+
+       if (us < 0)
+               return us;
+
+       trans_time->start_schedule_us = us;
+       return 0;
+}
+
+/**
+ * dwc2_hs_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ * @index:       Transfer index
+ */
+static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg,
+                                   struct dwc2_qh *qh, int index)
+{
+       struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+
+       pmap_unschedule(hsotg->hs_periodic_bitmap,
+                       DWC2_HS_PERIODIC_US_PER_UFRAME,
+                       DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+                       qh->host_interval, trans_time->start_schedule_us);
+}
+
+/**
+ * dwc2_uframe_schedule_split - Schedule a QH for a periodic split xfer.
+ *
+ * This is the most complicated thing in USB.  We have to find matching time
+ * in both the global high speed schedule for the port and the low speed
+ * schedule for the TT associated with the given device.
+ *
+ * Being here means that the host must be running in high speed mode and the
+ * device is in low or full speed mode (and behind a hub).
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg,
+                                     struct dwc2_qh *qh)
+{
+       int bytecount = qh->maxp_mult * qh->maxp;
+       int ls_search_slice;
+       int err = 0;
+       int host_interval_in_sched;
+
+       /*
+        * The interval (how often to repeat) in the actual host schedule.
+        * See pmap_schedule() for gcd() explanation.
+        */
+       host_interval_in_sched = gcd(qh->host_interval,
+                                    DWC2_HS_SCHEDULE_UFRAMES);
+
+       /*
+        * We always try to find space in the low speed schedule first, then
+        * try to find high speed time that matches.  If we don't, we'll bump
+        * up the place we start searching in the low speed schedule and try
+        * again.  To start we'll look right at the beginning of the low speed
+        * schedule.
+        *
+        * Note that this will tend to front-load the high speed schedule.
+        * We may eventually want to try to avoid this by either considering
+        * both schedules together or doing some sort of round robin.
+        */
+       ls_search_slice = 0;
+
+       while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) {
+               int start_s_uframe;
+               int ssplit_s_uframe;
+               int second_s_uframe;
+               int rel_uframe;
+               int first_count;
+               int middle_count;
+               int end_count;
+               int first_data_bytes;
+               int other_data_bytes;
+               int i;
+
+               if (qh->schedule_low_speed) {
+                       err = dwc2_ls_pmap_schedule(hsotg, qh, ls_search_slice);
+
+                       /*
+                        * If we got an error here there's no other magic we
+                        * can do, so bail.  All the looping above is only
+                        * helpful to redo things if we got a low speed slot
+                        * and then couldn't find a matching high speed slot.
+                        */
+                       if (err)
+                               return err;
+               } else {
+                       /* Must be missing the tt structure?  Why? */
+                       WARN_ON_ONCE(1);
+               }
+
+               /*
+                * This will give us a number 0 - 7 if
+                * DWC2_LS_SCHEDULE_FRAMES == 1, or 0 - 15 if == 2, or ...
+                */
+               start_s_uframe = qh->ls_start_schedule_slice /
+                                DWC2_SLICES_PER_UFRAME;
+
+               /* Get a number that's always 0 - 7 */
+               rel_uframe = (start_s_uframe % 8);
+
+               /*
+                * If we were going to start in uframe 7 then we would need to
+                * issue a start split in uframe 6, which spec says is not OK.
+                * Move on to the next full frame (assuming there is one).
+                *
+                * See 11.18.4 Host Split Transaction Scheduling Requirements
+                * bullet 1.
+                */
+               if (rel_uframe == 7) {
+                       if (qh->schedule_low_speed)
+                               dwc2_ls_pmap_unschedule(hsotg, qh);
+                       ls_search_slice =
+                               (qh->ls_start_schedule_slice /
+                                DWC2_LS_PERIODIC_SLICES_PER_FRAME + 1) *
+                               DWC2_LS_PERIODIC_SLICES_PER_FRAME;
+                       continue;
+               }
+
+               /*
+                * For ISOC in:
+                * - start split            (frame -1)
+                * - complete split w/ data (frame +1)
+                * - complete split w/ data (frame +2)
+                * - ...
+                * - complete split w/ data (frame +num_data_packets)
+                * - complete split w/ data (frame +num_data_packets+1)
+                * - complete split w/ data (frame +num_data_packets+2, max 8)
+                *   ...though if frame was "0" then max is 7...
+                *
+                * For ISOC out we might need to do:
+                * - start split w/ data    (frame -1)
+                * - start split w/ data    (frame +0)
+                * - ...
+                * - start split w/ data    (frame +num_data_packets-2)
+                *
+                * For INTERRUPT in we might need to do:
+                * - start split            (frame -1)
+                * - complete split w/ data (frame +1)
+                * - complete split w/ data (frame +2)
+                * - complete split w/ data (frame +3, max 8)
+                *
+                * For INTERRUPT out we might need to do:
+                * - start split w/ data    (frame -1)
+                * - complete split         (frame +1)
+                * - complete split         (frame +2)
+                * - complete split         (frame +3, max 8)
+                *
+                * Start adjusting!
+                */
+               ssplit_s_uframe = (start_s_uframe +
+                                  host_interval_in_sched - 1) %
+                                 host_interval_in_sched;
+               if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)
+                       second_s_uframe = start_s_uframe;
+               else
+                       second_s_uframe = start_s_uframe + 1;
+
+               /* First data transfer might not be all 188 bytes. */
+               first_data_bytes = 188 -
+                       DIV_ROUND_UP(188 * (qh->ls_start_schedule_slice %
+                                           DWC2_SLICES_PER_UFRAME),
+                                    DWC2_SLICES_PER_UFRAME);
+               if (first_data_bytes > bytecount)
+                       first_data_bytes = bytecount;
+               other_data_bytes = bytecount - first_data_bytes;
+
+               /*
+                * For now, skip OUT xfers where first xfer is partial
+                *
+                * Main dwc2 code assumes:
+                * - INT transfers never get split in two.
+                * - ISOC transfers can always transfer 188 bytes the first
+                *   time.
+                *
+                * Until that code is fixed, try again if the first transfer
+                * couldn't transfer everything.
+                *
+                * This code can be removed if/when the rest of dwc2 handles
+                * the above cases.  Until it's fixed we just won't be able
+                * to schedule quite as tightly.
+                */
+               if (!qh->ep_is_in &&
+                   (first_data_bytes != min_t(int, 188, bytecount))) {
+                       dwc2_sch_dbg(hsotg,
+                                    "QH=%p avoiding broken 1st xfer (%d, %d)\n",
+                                    qh, first_data_bytes, bytecount);
+                       if (qh->schedule_low_speed)
+                               dwc2_ls_pmap_unschedule(hsotg, qh);
+                       ls_search_slice = (start_s_uframe + 1) *
+                               DWC2_SLICES_PER_UFRAME;
+                       continue;
+               }
+
+               /* Start by assuming transfers for the bytes */
+               qh->num_hs_transfers = 1 + DIV_ROUND_UP(other_data_bytes, 188);
+
+               /*
+                * Everything except ISOC OUT has extra transfers.  Rules are
+                * complicated.  See 11.18.4 Host Split Transaction Scheduling
+                * Requirements bullet 3.
+                */
+               if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+                       if (rel_uframe == 6)
+                               qh->num_hs_transfers += 2;
+                       else
+                               qh->num_hs_transfers += 3;
+
+                       if (qh->ep_is_in) {
+                               /*
+                                * First is start split, middle/end is data.
+                                * Allocate full data bytes for all data.
+                                */
+                               first_count = 4;
+                               middle_count = bytecount;
+                               end_count = bytecount;
+                       } else {
+                               /*
+                                * First is data, middle/end is complete.
+                                * First transfer and second can have data.
+                                * Rest should just have complete split.
+                                */
+                               first_count = first_data_bytes;
+                               middle_count = max_t(int, 4, other_data_bytes);
+                               end_count = 4;
+                       }
+               } else {
+                       if (qh->ep_is_in) {
+                               int last;
+
+                               /* Account for the start split */
+                               qh->num_hs_transfers++;
+
+                               /* Calculate "L" value from spec */
+                               last = rel_uframe + qh->num_hs_transfers + 1;
+
+                               /* Start with basic case */
+                               if (last <= 6)
+                                       qh->num_hs_transfers += 2;
+                               else
+                                       qh->num_hs_transfers += 1;
+
+                               /* Adjust downwards */
+                               if (last >= 6 && rel_uframe == 0)
+                                       qh->num_hs_transfers--;
+
+                               /* 1st = start; rest can contain data */
+                               first_count = 4;
+                               middle_count = min_t(int, 188, bytecount);
+                               end_count = middle_count;
+                       } else {
+                               /* All contain data, last might be smaller */
+                               first_count = first_data_bytes;
+                               middle_count = min_t(int, 188,
+                                                    other_data_bytes);
+                               end_count = other_data_bytes % 188;
+                       }
+               }
+
+               /* Assign durations per uFrame */
+               qh->hs_transfers[0].duration_us = HS_USECS_ISO(first_count);
+               for (i = 1; i < qh->num_hs_transfers - 1; i++)
+                       qh->hs_transfers[i].duration_us =
+                               HS_USECS_ISO(middle_count);
+               if (qh->num_hs_transfers > 1)
+                       qh->hs_transfers[qh->num_hs_transfers - 1].duration_us =
+                               HS_USECS_ISO(end_count);
+
+               /*
+                * Assign start us.  The call below to dwc2_hs_pmap_schedule()
+                * will start with these numbers but may adjust within the same
+                * microframe.
+                */
+               qh->hs_transfers[0].start_schedule_us =
+                       ssplit_s_uframe * DWC2_HS_PERIODIC_US_PER_UFRAME;
+               for (i = 1; i < qh->num_hs_transfers; i++)
+                       qh->hs_transfers[i].start_schedule_us =
+                               ((second_s_uframe + i - 1) %
+                                DWC2_HS_SCHEDULE_UFRAMES) *
+                               DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+               /* Try to schedule with filled in hs_transfers above */
+               for (i = 0; i < qh->num_hs_transfers; i++) {
+                       err = dwc2_hs_pmap_schedule(hsotg, qh, true, i);
+                       if (err)
+                               break;
+               }
+
+               /* If we scheduled all w/out breaking out then we're all good */
+               if (i == qh->num_hs_transfers)
+                       break;
+
+               for (; i >= 0; i--)
+                       dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+               if (qh->schedule_low_speed)
+                       dwc2_ls_pmap_unschedule(hsotg, qh);
+
+               /* Try again starting in the next microframe */
+               ls_search_slice = (start_s_uframe + 1) * DWC2_SLICES_PER_UFRAME;
+       }
+
+       if (ls_search_slice >= DWC2_LS_SCHEDULE_SLICES)
+               return -ENOSPC;
+
+       return 0;
+}
+
+/**
+ * dwc2_uframe_schedule_hs - Schedule a QH for a periodic high speed xfer.
+ *
+ * Basically this just wraps dwc2_hs_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_hs(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       /* In non-split host and device time are the same */
+       WARN_ON(qh->host_us != qh->device_us);
+       WARN_ON(qh->host_interval != qh->device_interval);
+       WARN_ON(qh->num_hs_transfers != 1);
+
+       /* We'll have one transfer; init start to 0 before calling scheduler */
+       qh->hs_transfers[0].start_schedule_us = 0;
+       qh->hs_transfers[0].duration_us = qh->host_us;
+
+       return dwc2_hs_pmap_schedule(hsotg, qh, false, 0);
+}
+
+/**
+ * dwc2_uframe_schedule_ls - Schedule a QH for a periodic low/full speed xfer.
+ *
+ * Basically this just wraps dwc2_ls_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_ls(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       /* In non-split host and device time are the same */
+       WARN_ON(qh->host_us != qh->device_us);
+       WARN_ON(qh->host_interval != qh->device_interval);
+       WARN_ON(!qh->schedule_low_speed);
+
+       /* Run on the main low speed schedule (no split = no hub = no TT) */
+       return dwc2_ls_pmap_schedule(hsotg, qh, 0);
+}
+
+/**
+ * dwc2_uframe_schedule - Schedule a QH for a periodic xfer.
+ *
+ * Calls one of the 3 sub-function depending on what type of transfer this QH
+ * is for.  Also adds some printing.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       int ret;
+
+       if (qh->dev_speed == USB_SPEED_HIGH)
+               ret = dwc2_uframe_schedule_hs(hsotg, qh);
+       else if (!qh->do_split)
+               ret = dwc2_uframe_schedule_ls(hsotg, qh);
+       else
+               ret = dwc2_uframe_schedule_split(hsotg, qh);
+
+       if (ret)
+               dwc2_sch_dbg(hsotg, "QH=%p Failed to schedule %d\n", qh, ret);
+       else
+               dwc2_qh_schedule_print(hsotg, qh);
+
+       return ret;
+}
+
+/**
+ * dwc2_uframe_unschedule - Undoes dwc2_uframe_schedule().
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_uframe_unschedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       int i;
+
+       for (i = 0; i < qh->num_hs_transfers; i++)
+               dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+       if (qh->schedule_low_speed)
+               dwc2_ls_pmap_unschedule(hsotg, qh);
+
+       dwc2_sch_dbg(hsotg, "QH=%p Unscheduled\n", qh);
+}
+
+/**
+ * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
+ *
+ * Takes a qh that has already been scheduled (which means we know we have the
+ * bandwdith reserved for us) and set the next_active_frame and the
+ * start_active_frame.
+ *
+ * This is expected to be called on qh's that weren't previously actively
+ * running.  It just picks the next frame that we can fit into without any
+ * thought about the past.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for a periodic endpoint
+ *
+ */
+static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       u16 frame_number;
+       u16 earliest_frame;
+       u16 next_active_frame;
+       u16 relative_frame;
+       u16 interval;
+
+       /*
+        * Use the real frame number rather than the cached value as of the
+        * last SOF to give us a little extra slop.
+        */
+       frame_number = dwc2_hcd_get_frame_number(hsotg);
+
+       /*
+        * We wouldn't want to start any earlier than the next frame just in
+        * case the frame number ticks as we're doing this calculation.
+        *
+        * NOTE: if we could quantify how long till we actually get scheduled
+        * we might be able to avoid the "+ 1" by looking at the upper part of
+        * HFNUM (the FRREM field).  For now we'll just use the + 1 though.
+        */
+       earliest_frame = dwc2_frame_num_inc(frame_number, 1);
+       next_active_frame = earliest_frame;
+
+       /* Get the "no microframe schduler" out of the way... */
+       if (!hsotg->params.uframe_sched) {
+               if (qh->do_split)
+                       /* Splits are active at microframe 0 minus 1 */
+                       next_active_frame |= 0x7;
+               goto exit;
+       }
+
+       if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) {
+               /*
+                * We're either at high speed or we're doing a split (which
+                * means we're talking high speed to a hub).  In any case
+                * the first frame should be based on when the first scheduled
+                * event is.
+                */
+               WARN_ON(qh->num_hs_transfers < 1);
+
+               relative_frame = qh->hs_transfers[0].start_schedule_us /
+                                DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+               /* Adjust interval as per high speed schedule */
+               interval = gcd(qh->host_interval, DWC2_HS_SCHEDULE_UFRAMES);
+
+       } else {
+               /*
+                * Low or full speed directly on dwc2.  Just about the same
+                * as high speed but on a different schedule and with slightly
+                * different adjustments.  Note that this works because when
+                * the host and device are both low speed then frames in the
+                * controller tick at low speed.
+                */
+               relative_frame = qh->ls_start_schedule_slice /
+                                DWC2_LS_PERIODIC_SLICES_PER_FRAME;
+               interval = gcd(qh->host_interval, DWC2_LS_SCHEDULE_FRAMES);
+       }
+
+       /* Scheduler messed up if frame is past interval */
+       WARN_ON(relative_frame >= interval);
+
+       /*
+        * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
+        * done the gcd(), so it's safe to move to the beginning of the current
+        * interval like this.
+        *
+        * After this we might be before earliest_frame, but don't worry,
+        * we'll fix it...
+        */
+       next_active_frame = (next_active_frame / interval) * interval;
+
+       /*
+        * Actually choose to start at the frame number we've been
+        * scheduled for.
+        */
+       next_active_frame = dwc2_frame_num_inc(next_active_frame,
+                                              relative_frame);
+
+       /*
+        * We actually need 1 frame before since the next_active_frame is
+        * the frame number we'll be put on the ready list and we won't be on
+        * the bus until 1 frame later.
+        */
+       next_active_frame = dwc2_frame_num_dec(next_active_frame, 1);
+
+       /*
+        * By now we might actually be before the earliest_frame.  Let's move
+        * up intervals until we're not.
+        */
+       while (dwc2_frame_num_gt(earliest_frame, next_active_frame))
+               next_active_frame = dwc2_frame_num_inc(next_active_frame,
+                                                      interval);
+
+exit:
+       qh->next_active_frame = next_active_frame;
+       qh->start_active_frame = next_active_frame;
+
+       dwc2_sch_vdbg(hsotg, "QH=%p First fn=%04x nxt=%04x\n",
+                     qh, frame_number, qh->next_active_frame);
+}
+
+/**
+ * dwc2_do_reserve() - Make a periodic reservation
+ *
+ * Try to allocate space in the periodic schedule.  Depending on parameters
+ * this might use the microframe scheduler or the dumb scheduler.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ *
+ * Returns: 0 upon success; error upon failure.
+ */
+static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       int status;
+
+       if (hsotg->params.uframe_sched) {
+               status = dwc2_uframe_schedule(hsotg, qh);
+       } else {
+               status = dwc2_periodic_channel_available(hsotg);
+               if (status) {
+                       dev_info(hsotg->dev,
+                                "%s: No host channel available for periodic transfer\n",
+                                __func__);
+                       return status;
+               }
+
+               status = dwc2_check_periodic_bandwidth(hsotg, qh);
+       }
+
+       if (status) {
+               dev_dbg(hsotg->dev,
+                       "%s: Insufficient periodic bandwidth for periodic transfer\n",
+                       __func__);
+               return status;
+       }
+
+       if (!hsotg->params.uframe_sched)
+               /* Reserve periodic channel */
+               hsotg->periodic_channels++;
+
+       /* Update claimed usecs per (micro)frame */
+       hsotg->periodic_usecs += qh->host_us;
+
+       dwc2_pick_first_frame(hsotg, qh);
+
+       return 0;
+}
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       MUTEX_ASSERT_LOCKED(&hsotg->lock);
+
+       WARN_ON(!qh->unreserve_pending);
+
+       /* No more unreserve pending--we're doing it */
+       qh->unreserve_pending = false;
+
+       if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+               list_del_init(&qh->qh_list_entry);
+
+       /* Update claimed usecs per (micro)frame */
+       hsotg->periodic_usecs -= qh->host_us;
+
+       if (hsotg->params.uframe_sched) {
+               dwc2_uframe_unschedule(hsotg, qh);
+       } else {
+               /* Release periodic channel reservation */
+               hsotg->periodic_channels--;
+       }
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @t: Address to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(void *arg)
+{
+       struct dwc2_qh *qh = arg;
+       struct dwc2_hsotg *hsotg = qh->hsotg;
+       unsigned long flags;
+
+       /*
+        * Wait for the lock, or for us to be scheduled again.  We
+        * could be scheduled again if:
+        * - We started executing but didn't get the lock yet.
+        * - A new reservation came in, but cancel didn't take effect
+        *   because we already started executing.
+        * - The timer has been kicked again.
+        * In that case cancel and wait for the next call.
+        */
+       while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+               if (timeout_pending(&qh->unreserve_timer))
+                       return;
+       }
+
+       /*
+        * Might be no more unreserve pending if:
+        * - We started executing but didn't get the lock yet.
+        * - A new reservation came in, but cancel didn't take effect
+        *   because we already started executing.
+        *
+        * We can't put this in the loop above because unreserve_pending needs
+        * to be accessed under lock, so we can only check it once we got the
+        * lock.
+        */
+       if (qh->unreserve_pending)
+               dwc2_do_unreserve(hsotg, qh);
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
+/**
+ * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a
+ * host channel is large enough to handle the maximum data transfer in a single
+ * (micro)frame for a periodic transfer
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for a periodic endpoint
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+STATIC int dwc2_check_max_xfer_size(struct dwc2_hsotg *hsotg,
+                                   struct dwc2_qh *qh)
+{
+       u32 max_xfer_size;
+       u32 max_channel_xfer_size;
+       int status = 0;
+
+       max_xfer_size = qh->maxp * qh->maxp_mult;
+       max_channel_xfer_size = hsotg->params.max_transfer_size;
+
+       if (max_xfer_size > max_channel_xfer_size) {
+               dev_err(hsotg->dev,
+                       "%s: Periodic xfer length %d > max xfer length for channel %d\n",
+                       __func__, max_xfer_size, max_channel_xfer_size);
+               status = -ENOSPC;
+       }
+
+       return status;
+}
+
+/**
+ * dwc2_schedule_periodic() - Schedules an interrupt or isochronous transfer in
+ * the periodic schedule
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer. The QH should already contain the
+ *         scheduling information.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+STATIC int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       int status;
 
        status = dwc2_check_max_xfer_size(hsotg, qh);
        if (status) {
@@ -551,7 +1493,36 @@ STATIC int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                return status;
        }
 
-       if (hsotg->core_params->dma_desc_enable > 0)
+       /* Cancel pending unreserve; if canceled OK, unreserve was pending */
+       if (timeout_del(&qh->unreserve_timer))
+               WARN_ON(!qh->unreserve_pending);
+
+       /*
+        * Only need to reserve if there's not an unreserve pending, since if an
+        * unreserve is pending then by definition our old reservation is still
+        * valid.  Unreserve might still be pending even if we didn't cancel if
+        * dwc2_unreserve_timer_fn() already started.  Code in the timer handles
+        * that case.
+        */
+       if (!qh->unreserve_pending) {
+               status = dwc2_do_reserve(hsotg, qh);
+               if (status)
+                       return status;
+       } else {
+               /*
+                * It might have been a while, so make sure that frame_number
+                * is still good.  Note: we could also try to use the similar
+                * dwc2_next_periodic_start() but that schedules much more
+                * tightly and we might need to hurry and queue things up.
+                */
+               if (dwc2_frame_num_le(qh->next_active_frame,
+                                     hsotg->frame_number))
+                       dwc2_pick_first_frame(hsotg, qh);
+       }
+
+       qh->unreserve_pending = 0;
+
+       if (hsotg->params.dma_desc_enable)
                /* Don't rely on SOF and start in ready schedule */
                list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
        else
@@ -559,14 +1530,7 @@ STATIC int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                list_add_tail(&qh->qh_list_entry,
                              &hsotg->periodic_sched_inactive);
 
-       if (hsotg->core_params->uframe_sched <= 0)
-               /* Reserve periodic channel */
-               hsotg->periodic_channels++;
-
-       /* Update claimed usecs per (micro)frame */
-       hsotg->periodic_usecs += qh->usecs;
-
-       return status;
+       return 0;
 }
 
 /**
@@ -579,22 +1543,27 @@ STATIC int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 STATIC void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
                                     struct dwc2_qh *qh)
 {
-       int i;
-
-       list_del_init(&qh->qh_list_entry);
+       MUTEX_ASSERT_LOCKED(&hsotg->lock);
 
-       /* Update claimed usecs per (micro)frame */
-       hsotg->periodic_usecs -= qh->usecs;
+       /*
+        * Schedule the unreserve to happen in a little bit.  Cases here:
+        * - Unreserve worker might be sitting there waiting to grab the lock.
+        *   In this case it will notice it's been schedule again and will
+        *   quit.
+        * - Unreserve worker might not be scheduled.
+        *
+        * We should never already be scheduled since dwc2_schedule_periodic()
+        * should have canceled the scheduled unreserve timer (hence the
+        * warning on did_modify).
+        *
+        * We add + 1 to the timer to guarantee that at least 1 jiffy has
+        * passed (otherwise if the jiffy counter might tick right after we
+        * read it and we'll get no delay).
+        */
+       timeout_add(&qh->unreserve_timer, DWC2_UNRESERVE_DELAY + 1);
+       qh->unreserve_pending = 1;
 
-       if (hsotg->core_params->uframe_sched > 0) {
-               for (i = 0; i < 8; i++) {
-                       hsotg->frame_usecs[i] += qh->frame_usecs[i];
-                       qh->frame_usecs[i] = 0;
-               }
-       } else {
-               /* Release periodic channel reservation */
-               hsotg->periodic_channels--;
-       }
+       list_del_init(&qh->qh_list_entry);
 }
 
 /**
@@ -617,6 +1586,8 @@ STATIC void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * qh back to the "inactive" list, then queues transactions.
  *
  * @t: Pointer to wait_timer in a qh.
+ *
+ * Return: HRTIMER_NORESTART to not automatically restart this timer.
  */
 STATIC void dwc2_wait_timer_fn(void *arg)
 {
@@ -646,6 +1617,223 @@ STATIC void dwc2_wait_timer_fn(void *arg)
        spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
+/**
+ * dwc2_qh_init() - Initializes a QH structure
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    The QH to init
+ * @urb:   Holds the information about the device/endpoint needed to initialize
+ *         the QH
+ * @mem_flags: Flags for allocating memory.
+ */
+STATIC void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+                        struct dwc2_hcd_urb *urb, gfp_t mem_flags)
+{
+       int dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+       u8 ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+       bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info);
+       bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC);
+       bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT);
+       u32 hprt = dwc2_readl(hsotg, HPRT0);
+       u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+       bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED &&
+                        dev_speed != USB_SPEED_HIGH);
+       int maxp = dwc2_hcd_get_maxp(&urb->pipe_info);
+       int maxp_mult = dwc2_hcd_get_maxp_mult(&urb->pipe_info);
+       int bytecount = maxp_mult * maxp;
+       char *speed, *type;
+
+       /* Initialize QH */
+       qh->hsotg = hsotg;
+       timeout_set(&qh->unreserve_timer, dwc2_unreserve_timer_fn, qh);
+       timeout_set(&qh->wait_timer, dwc2_wait_timer_fn, qh);
+       qh->ep_type = ep_type;
+       qh->ep_is_in = ep_is_in;
+
+       qh->data_toggle = DWC2_HC_PID_DATA0;
+       qh->maxp = maxp;
+       qh->maxp_mult = maxp_mult;
+       INIT_LIST_HEAD(&qh->qtd_list);
+       INIT_LIST_HEAD(&qh->qh_list_entry);
+
+       qh->do_split = do_split;
+       qh->dev_speed = dev_speed;
+
+       if (ep_is_int || ep_is_isoc) {
+               /* Compute scheduling parameters once and save them */
+               int host_speed = do_split ? USB_SPEED_HIGH : dev_speed;
+               struct dwc2_tt *dwc_tt = dwc2_host_get_tt_info(hsotg, urb->priv,
+                                                              mem_flags,
+                                                              &qh->ttport);
+               int device_ns;
+
+               qh->dwc_tt = dwc_tt;
+
+               qh->host_us = NS_TO_US(dwc2_usb_calc_bus_time(host_speed,
+                                      ep_is_in, ep_is_isoc, bytecount));
+               device_ns = dwc2_usb_calc_bus_time(dev_speed, ep_is_in,
+                                                  ep_is_isoc, bytecount);
+
+               if (do_split && dwc_tt)
+                       device_ns += dwc2_ttthink_to_ns(hsotg, urb->priv,
+                           dwc_tt->usb_tt->hub->ttthink);
+               qh->device_us = NS_TO_US(device_ns);
+
+               qh->device_interval = urb->interval;
+               qh->host_interval = urb->interval * (do_split ? 8 : 1);
+
+               /*
+                * Schedule low speed if we're running the host in low or
+                * full speed OR if we've got a "TT" to deal with to access this
+                * device.
+                */
+               qh->schedule_low_speed = prtspd != HPRT0_SPD_HIGH_SPEED ||
+                                        dwc_tt;
+
+               if (do_split) {
+                       /* We won't know num transfers until we schedule */
+                       qh->num_hs_transfers = -1;
+               } else if (dev_speed == USB_SPEED_HIGH) {
+                       qh->num_hs_transfers = 1;
+               } else {
+                       qh->num_hs_transfers = 0;
+               }
+
+               /* We'll schedule later when we have something to do */
+       }
+
+       switch (dev_speed) {
+       case USB_SPEED_LOW:
+               speed = "low";
+               break;
+       case USB_SPEED_FULL:
+               speed = "full";
+               break;
+       case USB_SPEED_HIGH:
+               speed = "high";
+               break;
+       default:
+               speed = "?";
+               break;
+       }
+
+       switch (qh->ep_type) {
+       case USB_ENDPOINT_XFER_ISOC:
+               type = "isochronous";
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               type = "interrupt";
+               break;
+       case USB_ENDPOINT_XFER_CONTROL:
+               type = "control";
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               type = "bulk";
+               break;
+       default:
+               type = "?";
+               break;
+       }
+
+       dwc2_sch_dbg(hsotg, "QH=%p Init %s, %s speed, %d bytes:\n", qh, type,
+                    speed, bytecount);
+       dwc2_sch_dbg(hsotg, "QH=%p ...addr=%d, ep=%d, %s\n", qh,
+                    dwc2_hcd_get_dev_addr(&urb->pipe_info),
+                    dwc2_hcd_get_ep_num(&urb->pipe_info),
+                    ep_is_in ? "IN" : "OUT");
+       if (ep_is_int || ep_is_isoc) {
+               dwc2_sch_dbg(hsotg,
+                            "QH=%p ...duration: host=%d us, device=%d us\n",
+                            qh, qh->host_us, qh->device_us);
+               dwc2_sch_dbg(hsotg, "QH=%p ...interval: host=%d, device=%d\n",
+                            qh, qh->host_interval, qh->device_interval);
+               if (qh->schedule_low_speed)
+                       dwc2_sch_dbg(hsotg, "QH=%p ...low speed schedule=%p\n",
+                                    qh, dwc2_get_ls_map(hsotg, qh));
+       }
+}
+
+/**
+ * dwc2_hcd_qh_create() - Allocates and initializes a QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller
+ * @urb:          Holds the information about the device/endpoint needed
+ *                to initialize the QH
+ * @mem_flags:   Flags for allocating memory.
+ *
+ * Return: Pointer to the newly allocated QH, or NULL on error
+ */
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+                                  struct dwc2_hcd_urb *urb,
+                                         gfp_t mem_flags)
+{
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+       struct dwc2_qh *qh;
+
+       if (!urb->priv)
+               return NULL;
+
+       /* Allocate memory */
+       qh = pool_get(&sc->sc_qhpool, PR_ZERO | PR_NOWAIT);
+       if (!qh)
+               return NULL;
+
+       dwc2_qh_init(hsotg, qh, urb, mem_flags);
+
+       if (hsotg->params.dma_desc_enable &&
+           dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
+               dwc2_hcd_qh_free(hsotg, qh);
+               return NULL;
+       }
+
+       return qh;
+}
+
+/**
+ * dwc2_hcd_qh_free() - Frees the QH
+ *
+ * @hsotg: HCD instance
+ * @qh:    The QH to free
+ *
+ * QH should already be removed from the list. QTD list should already be empty
+ * if called from URB Dequeue.
+ *
+ * Must NOT be called with interrupt disabled or spinlock held
+ */
+void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+
+       /* Make sure any unreserve work is finished. */
+       if (timeout_del(&qh->unreserve_timer)) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&hsotg->lock, flags);
+               dwc2_do_unreserve(hsotg, qh);
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+       }
+
+       /*
+        * We don't have the lock so we can safely wait until the wait timer
+        * finishes.  Of course, at this point in time we'd better have set
+        * wait_timer_active to false so if this timer was still pending it
+        * won't do anything anyway, but we want it to finish before we free
+        * memory.
+        */
+       timeout_del(&qh->wait_timer);
+
+       dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
+
+       if (qh->desc_list)
+               dwc2_hcd_qh_free_ddma(hsotg, qh);
+       else if (hsotg->unaligned_cache && qh->dw_align_buf) {
+               usb_freemem(&sc->sc_bus, &qh->dw_align_buf_usbdma);
+               qh->dw_align_buf_dma = (dma_addr_t)0;
+       }
+
+       pool_put(&sc->sc_qhpool, qh);
+}
+
 /**
  * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic
  * schedule if it is not already in the schedule. If the QH is already in
@@ -668,22 +1856,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                /* QH already in a schedule */
                return 0;
 
-       if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
-                       !hsotg->frame_number) {
-               dev_dbg(hsotg->dev,
-                               "reset frame number counter\n");
-               qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
-                               SCHEDULE_SLOP);
-       }
-
        /* Add the new QH to the appropriate schedule */
        if (dwc2_qh_is_non_per(qh)) {
+               /* Schedule right away */
+               qh->start_active_frame = hsotg->frame_number;
+               qh->next_active_frame = qh->start_active_frame;
+
                if (qh->want_wait) {
                        list_add_tail(&qh->qh_list_entry,
                                      &hsotg->non_periodic_sched_waiting);
                        qh->wait_timer_cancel = false;
-                       /* XXX mod_timer(&qh->wait_timer,
-                                 jiffies + DWC2_RETRY_WAIT_DELAY + 1); */
                        timeout_add_msec(&qh->wait_timer,
                            DWC2_RETRY_WAIT_DELAY);
                } else {
@@ -697,9 +1879,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
        if (status)
                return status;
        if (!hsotg->periodic_qh_count) {
-               intr_mask = DWC2_READ_4(hsotg, GINTMSK);
+               intr_mask = dwc2_readl(hsotg, GINTMSK);
                intr_mask |= GINTSTS_SOF;
-               DWC2_WRITE_4(hsotg, GINTMSK, intr_mask);
+               dwc2_writel(hsotg, intr_mask, GINTMSK);
        }
        hsotg->periodic_qh_count++;
 
@@ -736,46 +1918,172 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
        dwc2_deschedule_periodic(hsotg, qh);
        hsotg->periodic_qh_count--;
-       if (!hsotg->periodic_qh_count) {
-               intr_mask = DWC2_READ_4(hsotg, GINTMSK);
+       if (!hsotg->periodic_qh_count &&
+           !hsotg->params.dma_desc_enable) {
+               intr_mask = dwc2_readl(hsotg, GINTMSK);
                intr_mask &= ~GINTSTS_SOF;
-               DWC2_WRITE_4(hsotg, GINTMSK, intr_mask);
+               dwc2_writel(hsotg, intr_mask, GINTMSK);
        }
 }
 
-/*
- * Schedule the next continuing periodic split transfer
+/**
+ * dwc2_next_for_periodic_split() - Set next_active_frame midway thru a split.
+ *
+ * This is called for setting next_active_frame for periodic splits for all but
+ * the first packet of the split.  Confusing?  I thought so...
+ *
+ * Periodic splits are single low/full speed transfers that we end up splitting
+ * up into several high speed transfers.  They always fit into one full (1 ms)
+ * frame but might be split over several microframes (125 us each).  We to put
+ * each of the parts on a very specific high speed frame.
+ *
+ * This function figures out where the next active uFrame needs to be.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
  */
-STATIC void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
-                                     struct dwc2_qh *qh, u16 frame_number,
-                                     int sched_next_periodic_split)
+static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
+                                       struct dwc2_qh *qh, u16 frame_number)
 {
+       u16 old_frame = qh->next_active_frame;
+       u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+       int missed = 0;
        u16 incr;
 
-       if (sched_next_periodic_split) {
-               qh->sched_frame = frame_number;
-               incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
-               if (dwc2_frame_num_le(frame_number, incr)) {
-                       /*
-                        * Allow one frame to elapse after start split
-                        * microframe before scheduling complete split, but
-                        * DON'T if we are doing the next start split in the
-                        * same frame for an ISOC out
-                        */
-                       if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
-                           qh->ep_is_in != 0) {
-                               qh->sched_frame =
-                                       dwc2_frame_num_inc(qh->sched_frame, 1);
-                       }
-               }
-       } else {
-               qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame,
-                                                    qh->interval);
-               if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-                       qh->sched_frame = frame_number;
-               qh->sched_frame |= 0x7;
-               qh->start_split_frame = qh->sched_frame;
+       /*
+        * See dwc2_uframe_schedule_split() for split scheduling.
+        *
+        * Basically: increment 1 normally, but 2 right after the start split
+        * (except for ISOC out).
+        */
+       if (old_frame == qh->start_active_frame &&
+           !(qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in))
+               incr = 2;
+       else
+               incr = 1;
+
+       qh->next_active_frame = dwc2_frame_num_inc(old_frame, incr);
+
+       /*
+        * Note that it's OK for frame_number to be 1 frame past
+        * next_active_frame.  Remember that next_active_frame is supposed to
+        * be 1 frame _before_ when we want to be scheduled.  If we're 1 frame
+        * past it just means schedule ASAP.
+        *
+        * It's _not_ OK, however, if we're more than one frame past.
+        */
+       if (dwc2_frame_num_gt(prev_frame_number, qh->next_active_frame)) {
+               /*
+                * OOPS, we missed.  That's actually pretty bad since
+                * the hub will be unhappy; try ASAP I guess.
+                */
+               missed = dwc2_frame_num_dec(prev_frame_number,
+                                           qh->next_active_frame);
+               qh->next_active_frame = frame_number;
+       }
+
+       return missed;
+}
+
+/**
+ * dwc2_next_periodic_start() - Set next_active_frame for next transfer start
+ *
+ * This is called for setting next_active_frame for a periodic transfer for
+ * all cases other than midway through a periodic split.  This will also update
+ * start_active_frame.
+ *
+ * Since we _always_ keep start_active_frame as the start of the previous
+ * transfer this is normally pretty easy: we just add our interval to
+ * start_active_frame and we've got our answer.
+ *
+ * The tricks come into play if we miss.  In that case we'll look for the next
+ * slot we can fit into.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
+ */
+static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
+                                   struct dwc2_qh *qh, u16 frame_number)
+{
+       int missed = 0;
+       u16 interval = qh->host_interval;
+       u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+
+       qh->start_active_frame = dwc2_frame_num_inc(qh->start_active_frame,
+                                                   interval);
+
+       /*
+        * The dwc2_frame_num_gt() function used below won't work terribly well
+        * with if we just incremented by a really large intervals since the
+        * frame counter only goes to 0x3fff.  It's terribly unlikely that we
+        * will have missed in this case anyway.  Just go to exit.  If we want
+        * to try to do better we'll need to keep track of a bigger counter
+        * somewhere in the driver and handle overflows.
+        */
+       if (interval >= 0x1000)
+               goto exit;
+
+       /*
+        * Test for misses, which is when it's too late to schedule.
+        *
+        * A few things to note:
+        * - We compare against prev_frame_number since start_active_frame
+        *   and next_active_frame are always 1 frame before we want things
+        *   to be active and we assume we can still get scheduled in the
+        *   current frame number.
+        * - It's possible for start_active_frame (now incremented) to be
+        *   next_active_frame if we got an EO MISS (even_odd miss) which
+        *   basically means that we detected there wasn't enough time for
+        *   the last packet and dwc2_hc_set_even_odd_frame() rescheduled us
+        *   at the last second.  We want to make sure we don't schedule
+        *   another transfer for the same frame.  My test webcam doesn't seem
+        *   terribly upset by missing a transfer but really doesn't like when
+        *   we do two transfers in the same frame.
+        * - Some misses are expected.  Specifically, in order to work
+        *   perfectly dwc2 really needs quite spectacular interrupt latency
+        *   requirements.  It needs to be able to handle its interrupts
+        *   completely within 125 us of them being asserted. That not only
+        *   means that the dwc2 interrupt handler needs to be fast but it
+        *   means that nothing else in the system has to block dwc2 for a long
+        *   time.  We can help with the dwc2 parts of this, but it's hard to
+        *   guarantee that a system will have interrupt latency < 125 us, so
+        *   we have to be robust to some misses.
+        */
+       if (qh->start_active_frame == qh->next_active_frame ||
+           dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+               u16 ideal_start = qh->start_active_frame;
+               int periods_in_map;
+
+               /*
+                * Adjust interval as per gcd with map size.
+                * See pmap_schedule() for more details here.
+                */
+               if (qh->do_split || qh->dev_speed == USB_SPEED_HIGH)
+                       periods_in_map = DWC2_HS_SCHEDULE_UFRAMES;
+               else
+                       periods_in_map = DWC2_LS_SCHEDULE_FRAMES;
+               interval = gcd(interval, periods_in_map);
+
+               do {
+                       qh->start_active_frame = dwc2_frame_num_inc(
+                               qh->start_active_frame, interval);
+               } while (dwc2_frame_num_gt(prev_frame_number,
+                                          qh->start_active_frame));
+
+               missed = dwc2_frame_num_dec(qh->start_active_frame,
+                                           ideal_start);
        }
+
+exit:
+       qh->next_active_frame = qh->start_active_frame;
+
+       return missed;
 }
 
 /*
@@ -794,7 +2102,11 @@ STATIC void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                            int sched_next_periodic_split)
 {
+#ifdef DWC2_DEBUG
+       u16 old_frame = qh->next_active_frame;
+#endif
        u16 frame_number;
+       int missed;
 
        if (dbg_qh(qh))
                dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -807,33 +2119,44 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                return;
        }
 
+       /*
+        * Use the real frame number rather than the cached value as of the
+        * last SOF just to get us a little closer to reality.  Note that
+        * means we don't actually know if we've already handled the SOF
+        * interrupt for this frame.
+        */
        frame_number = dwc2_hcd_get_frame_number(hsotg);
 
-       if (qh->do_split) {
-               dwc2_sched_periodic_split(hsotg, qh, frame_number,
-                                         sched_next_periodic_split);
-       } else {
-               qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame,
-                                                    qh->interval);
-               if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-                       qh->sched_frame = frame_number;
-       }
+       if (sched_next_periodic_split)
+               missed = dwc2_next_for_periodic_split(hsotg, qh, frame_number);
+       else
+               missed = dwc2_next_periodic_start(hsotg, qh, frame_number);
+
+       dwc2_sch_vdbg(hsotg,
+                     "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n",
+                    qh, sched_next_periodic_split, frame_number, old_frame,
+                    qh->next_active_frame,
+                    dwc2_frame_num_dec(qh->next_active_frame, old_frame),
+               missed, missed ? "MISS" : "");
 
        if (list_empty(&qh->qtd_list)) {
                dwc2_hcd_qh_unlink(hsotg, qh);
                return;
        }
+
        /*
         * Remove from periodic_sched_queued and move to
         * appropriate queue
+        *
+        * Note: we purposely use the frame_number from the "hsotg" structure
+        * since we know SOF interrupt will handle future frames.
         */
-       if ((hsotg->core_params->uframe_sched > 0 &&
-            dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
-           (hsotg->core_params->uframe_sched <= 0 &&
-            qh->sched_frame == frame_number))
-               list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
+       if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
+               list_move_tail(&qh->qh_list_entry,
+                              &hsotg->periodic_sched_ready);
        else
-               list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
+               list_move_tail(&qh->qh_list_entry,
+                              &hsotg->periodic_sched_inactive);
 }
 
 /**
@@ -882,11 +2205,9 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
 int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
                     struct dwc2_qh *qh)
 {
-
-       MUTEX_ASSERT_LOCKED(&hsotg->lock);
        int retval;
 
-       if (!qh) {
+       if (unlikely(!qh)) {
                dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
                retval = -EINVAL;
                goto fail;
@@ -904,74 +2225,78 @@ fail:
        return retval;
 }
 
-void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
-                                 struct dwc2_qtd *qtd,
-                                 struct dwc2_qh *qh)
-{
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
-
-       list_del_init(&qtd->qtd_list_entry);
-       pool_put(&sc->sc_qtdpool, qtd);
-}
+/*** XXX: Include following functions in to our USB stack?  *******************/
 
-#define BITSTUFFTIME(bytecount)        ((8 * 7 * (bytecount)) / 6)
-#define HS_HOST_DELAY          5       /* nanoseconds */
-#define FS_LS_HOST_DELAY       1000    /* nanoseconds */
-#define HUB_LS_SETUP           333     /* nanoseconds */
+#define BW_HUB_LS_SETUP        333L    /* nanoseconds */
+#define BW_HOST_DELAY  1000L   /* nanoseconds */
 
-STATIC u32 dwc2_calc_bus_time(struct dwc2_hsotg *hsotg, int speed, int is_in,
-                             int is_isoc, int bytecount)
+long
+dwc2_usb_calc_bus_time(int speed, int is_input, int isoc, int bytecount)
 {
-       unsigned long retval;
+       unsigned long   tmp;
 
        switch (speed) {
-       case USB_SPEED_HIGH:
-               if (is_isoc)
-                       retval =
-                           ((38 * 8 * 2083) +
-                            (2083 * (3 + BITSTUFFTIME(bytecount)))) / 1000 +
-                           HS_HOST_DELAY;
-               else
-                       retval =
-                           ((55 * 8 * 2083) +
-                            (2083 * (3 + BITSTUFFTIME(bytecount)))) / 1000 +
-                           HS_HOST_DELAY;
-               break;
-       case USB_SPEED_FULL:
-               if (is_isoc) {
-                       retval =
-                           (8354 * (31 + 10 * BITSTUFFTIME(bytecount))) / 1000;
-                       if (is_in)
-                               retval = 7268 + FS_LS_HOST_DELAY + retval;
-                       else
-                               retval = 6265 + FS_LS_HOST_DELAY + retval;
+       case USB_SPEED_LOW:     /* INTR only */
+               if (is_input) {
+                       tmp = (67667L * (31L + 10L * BitTime (bytecount))) /
+                           1000L;
+                       return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY +
+                           tmp;
                } else {
-                       retval =
-                           (8354 * (31 + 10 * BITSTUFFTIME(bytecount))) / 1000;
-                       retval = 9107 + FS_LS_HOST_DELAY + retval;
+                       tmp = (66700L * (31L + 10L * BitTime (bytecount))) /
+                           1000L;
+                       return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY +
+                           tmp;
                }
-               break;
-       case USB_SPEED_LOW:
-               if (is_in) {
-                       retval =
-                           (67667 * (31 + 10 * BITSTUFFTIME(bytecount))) /
-                           1000;
-                       retval =
-                           64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
-                           retval;
+       case USB_SPEED_FULL:    /* ISOC or INTR */
+               if (isoc) { 
+                       tmp = (8354L * (31L + 10L * BitTime (bytecount))) /
+                           1000L;
+                       return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY +
+                           tmp;
                } else {
-                       retval =
-                           (66700 * (31 + 10 * BITSTUFFTIME(bytecount))) /
-                           1000;
-                       retval =
-                           64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
-                           retval;
+                       tmp = (8354L * (31L + 10L * BitTime (bytecount))) /
+                           1000L;
+                       return 9107L + BW_HOST_DELAY + tmp;
                }
-               break;
+       case USB_SPEED_HIGH:    /* ISOC or INTR */
+               /* FIXME adjust for input vs output */
+               if (isoc)
+                       tmp = HS_NSECS_ISO (bytecount);
+               else
+                       tmp = HS_NSECS (bytecount);
+               return tmp;
        default:
-               dev_warn(hsotg->dev, "Unknown device speed\n");
-               retval = -1;
+               printf ("%s: bogus device speed!\n", __func__);
+               return -1;
+       }
+}
+
+int
+dwc2_ttthink_to_ns(struct dwc2_hsotg *hsotg, void *context, int ttthink)
+{
+       struct usbd_xfer *xfer = context;
+       struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
+       struct usbd_device *dev = dpipe->pipe.device;
+
+       /* 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
+       switch (ttthink) {
+       case UHD_TT_THINK_8:
+               if (dev->ddesc.bDeviceProtocol != 0)
+                       return 666;
+               else
+                       return 0;
+       case UHD_TT_THINK_16:
+               return 666 * 2;
+       case UHD_TT_THINK_24:
+               return 666 * 3;
+       case UHD_TT_THINK_32:
+               return 666 * 4;
+       default:
+               dev_dbg(hsotg->dev, "%s: Invalid TT Think Time (0x%04x)!\n",
+                   __func__, ttthink);
+               break;
        }
 
-       return NS_TO_US(retval);
+       return 0;
 }
index 05f53b6..a551c2d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hw.h,v 1.3 2021/07/22 18:32:33 mglocker Exp $    */
+/*     $OpenBSD: dwc2_hw.h,v 1.4 2022/09/04 08:42:40 mglocker Exp $    */
 /*     $NetBSD: dwc2_hw.h,v 1.2 2013/09/25 06:19:22 skrll Exp $        */
 
 /*
 #define GOTGCTL_CHIRPEN                        (1 << 27)
 #define GOTGCTL_MULT_VALID_BC_MASK     (0x1f << 22)
 #define GOTGCTL_MULT_VALID_BC_SHIFT    22
+#define GOTGCTL_CURMODE_HOST            (1 << 21)
 #define GOTGCTL_OTGVER                 (1 << 20)
 #define GOTGCTL_BSESVLD                        (1 << 19)
 #define GOTGCTL_ASESVLD                        (1 << 18)
 #define GOTGCTL_DBNC_SHORT             (1 << 17)
 #define GOTGCTL_CONID_B                        (1 << 16)
+#define GOTGCTL_DBNCE_FLTR_BYPASS       (1 << 15)
 #define GOTGCTL_DEVHNPEN               (1 << 11)
 #define GOTGCTL_HSTSETHNPEN            (1 << 10)
 #define GOTGCTL_HNPREQ                 (1 << 9)
 #define GOTGCTL_HSTNEGSCS              (1 << 8)
+#define GOTGCTL_BVALOVAL               (1 << 7)
+#define GOTGCTL_BVALOEN                        (1 << 6)
+#define GOTGCTL_AVALOVAL               (1 << 5)
+#define GOTGCTL_AVALOEN                        (1 << 4)
+#define GOTGCTL_VBVALOVAL              (1 << 3)
+#define GOTGCTL_VBVALOEN               (1 << 2)
 #define GOTGCTL_SESREQ                 (1 << 1)
 #define GOTGCTL_SESREQSCS              (1 << 0)
 
 #define GRSTCTL                                HSOTG_REG(0x010)
 #define GRSTCTL_AHBIDLE                        (1 << 31)
 #define GRSTCTL_DMAREQ                 (1 << 30)
+#define GRSTCTL_CSFTRST_DONE           (1 << 29)
 #define GRSTCTL_TXFNUM_MASK            (0x1f << 6)
 #define GRSTCTL_TXFNUM_SHIFT           6
 #define GRSTCTL_TXFNUM_LIMIT           0x1f
 
 #define GPVNDCTL                       HSOTG_REG(0x0034)
 #define GGPIO                          HSOTG_REG(0x0038)
+#define GGPIO_STM32_OTG_GCCFG_PWRDWN   (1 << 16)
+#define GGPIO_STM32_OTG_GCCFG_VBDEN    (1 << 21)
+#define GGPIO_STM32_OTG_GCCFG_IDEN     (1 << 22)
+
 #define GUID                           HSOTG_REG(0x003c)
 #define GSNPSID                                HSOTG_REG(0x0040)
 #define GHWCFG1                                HSOTG_REG(0x0044)
+#define GSNPSID_ID_MASK                        0xffff0000
 
 #define GHWCFG2                                HSOTG_REG(0x0048)
 #define GHWCFG2_OTG_ENABLE_IC_USB              (1 << 31)
 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8          0
 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16         1
 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16    2
+#define GHWCFG4_ACG_SUPPORTED                  (1 << 12)
+#define GHWCFG4_IPG_ISOC_SUPPORTED             (1 << 11)
+#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED     (1 << 10)
 #define GHWCFG4_XHIBER                         (1 << 7)
 #define GHWCFG4_HIBER                          (1 << 6)
 #define GHWCFG4_MIN_AHB_FREQ                   (1 << 5)
 #define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT      0
 
 #define GLPMCFG                                HSOTG_REG(0x0054)
-#define GLPMCFG_INV_SEL_HSIC           (1 << 31)
-#define GLPMCFG_HSIC_CONNECT           (1 << 30)
+#define GLPMCFG_INVSELHSIC             (1 << 31)
+#define GLPMCFG_HSICCON                        (1 << 30)
+#define GLPMCFG_RSTRSLPSTS             (1 << 29)
+#define GLPMCFG_ENBESL                 (1 << 28)
 #define GLPMCFG_RETRY_COUNT_STS_MASK   (0x7 << 25)
 #define GLPMCFG_RETRY_COUNT_STS_SHIFT  25
-#define GLPMCFG_SEND_LPM               (1 << 24)
-#define GLPMCFG_RETRY_COUNT_MASK       (0x7 << 21)
-#define GLPMCFG_RETRY_COUNT_SHIFT      21
+#define GLPMCFG_SNDLPM                 (1 << 24)
+#define GLPMCFG_RETRY_CNT_MASK         (0x7 << 21)
+#define GLPMCFG_RETRY_CNT_SHIFT                21
+#define GLPMCFG_LPM_REJECT_CTRL_CONTROL (1 << 21)
+#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC    (1 << 22)
 #define GLPMCFG_LPM_CHAN_INDEX_MASK    (0xf << 17)
 #define GLPMCFG_LPM_CHAN_INDEX_SHIFT   17
-#define GLPMCFG_SLEEP_STATE_RESUMEOK   (1 << 16)
-#define GLPMCFG_PRT_SLEEP_STS          (1 << 15)
-#define GLPMCFG_LPM_RESP_MASK          (0x3 << 13)
-#define GLPMCFG_LPM_RESP_SHIFT         13
+#define GLPMCFG_L1RESUMEOK             (1 << 16)
+#define GLPMCFG_SLPSTS                 (1 << 15)
+#define GLPMCFG_COREL1RES_MASK         (0x3 << 13)
+#define GLPMCFG_COREL1RES_SHIFT                13
 #define GLPMCFG_HIRD_THRES_MASK                (0x1f << 8)
 #define GLPMCFG_HIRD_THRES_SHIFT       8
-#define GLPMCFG_HIRD_THRES_EN                  (0x10 << 8)
-#define GLPMCFG_EN_UTMI_SLEEP          (1 << 7)
-#define GLPMCFG_REM_WKUP_EN            (1 << 6)
+#define GLPMCFG_HIRD_THRES_EN          (0x10 << 8)
+#define GLPMCFG_ENBLSLPM               (1 << 7)
+#define GLPMCFG_BREMOTEWAKE            (1 << 6)
 #define GLPMCFG_HIRD_MASK              (0xf << 2)
 #define GLPMCFG_HIRD_SHIFT             2
-#define GLPMCFG_APPL_RESP              (1 << 1)
-#define GLPMCFG_LPM_CAP_EN             (1 << 0)
+#define GLPMCFG_APPL1RES               (1 << 1)
+#define GLPMCFG_LPMCAP                 (1 << 0)
 
 #define GPWRDN                         HSOTG_REG(0x0058)
 #define GPWRDN_MULT_VAL_ID_BC_MASK     (0x1f << 24)
 #define ADPCTL_PRB_DSCHRG_MASK         (0x3 << 0)
 #define ADPCTL_PRB_DSCHRG_SHIFT                0
 
+#define GREFCLK                                HSOTG_REG(0x0064)
+#define GREFCLK_REFCLKPER_MASK         (0x1ffff << 15)
+#define GREFCLK_REFCLKPER_SHIFT                15
+#define GREFCLK_REF_CLK_MODE           (1 << 14)
+#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK        (0x3ff)
+#define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT    0
+
+#define GINTMSK2                       HSOTG_REG(0x0068)
+#define GINTMSK2_WKUP_ALERT_INT_MSK    (1 << 0)
+
+#define GINTSTS2                       HSOTG_REG(0x006c)
+#define GINTSTS2_WKUP_ALERT_INT                (1 << 0)
+
 #define HPTXFSIZ                       HSOTG_REG(0x100)
 /* Use FIFOSIZE_* constants to access this register */
 
 /* Device mode registers */
 
 #define DCFG                           HSOTG_REG(0x800)
+#define DCFG_DESCDMA_EN                        (1 << 23)
 #define DCFG_EPMISCNT_MASK             (0x1f << 18)
 #define DCFG_EPMISCNT_SHIFT            18
 #define DCFG_EPMISCNT_LIMIT            0x1f
 #define DCFG_EPMISCNT(_x)              ((_x) << 18)
+#define DCFG_IPG_ISOC_SUPPORDED         (1 << 17)
 #define DCFG_PERFRINT_MASK             (0x3 << 11)
 #define DCFG_PERFRINT_SHIFT            11
 #define DCFG_PERFRINT_LIMIT            0x3
 #define DCFG_DEVSPD_FS48               3
 
 #define DCTL                           HSOTG_REG(0x804)
+#define DCTL_SERVICE_INTERVAL_SUPPORTED (1 << 19)
 #define DCTL_PWRONPRGDONE              (1 << 11)
 #define DCTL_CGOUTNAK                  (1 << 10)
 #define DCTL_SGOUTNAK                  (1 << 9)
 #define DSTS_SUSPSTS                   (1 << 0)
 
 #define DIEPMSK                                HSOTG_REG(0x810)
+#define DIEPMSK_NAKMSK                 (1 << 13)
+#define DIEPMSK_BNAININTRMSK           (1 << 9)
+#define DIEPMSK_TXFIFOUNDRNMSK         (1 << 8)
 #define DIEPMSK_TXFIFOEMPTY            (1 << 7)
 #define DIEPMSK_INEPNAKEFFMSK          (1 << 6)
 #define DIEPMSK_INTKNEPMISMSK          (1 << 5)
 #define DIEPMSK_XFERCOMPLMSK           (1 << 0)
 
 #define DOEPMSK                                HSOTG_REG(0x814)
+#define DOEPMSK_BNAMSK                  (1 << 9)
 #define DOEPMSK_BACK2BACKSETUP         (1 << 6)
+#define DOEPMSK_STSPHSERCVDMSK          (1 << 5)
 #define DOEPMSK_OUTTKNEPDISMSK         (1 << 4)
 #define DOEPMSK_SETUPMSK               (1 << 3)
 #define DOEPMSK_AHBERRMSK              (1 << 2)
 #define DTKNQR2                                HSOTG_REG(0x824)
 #define DTKNQR3                                HSOTG_REG(0x830)
 #define DTKNQR4                                HSOTG_REG(0x834)
+#define DIEPEMPMSK                     HSOTG_REG(0x834)
 
 #define DVBUSDIS                       HSOTG_REG(0x828)
 #define DVBUSPULSE                     HSOTG_REG(0x82C)
 #define DIEPINT(_a)                    HSOTG_REG(0x908 + ((_a) * 0x20))
 #define DOEPINT(_a)                    HSOTG_REG(0xB08 + ((_a) * 0x20))
 #define DXEPINT_SETUP_RCVD             (1 << 15)
+#define DXEPINT_NYETINTRPT             (1 << 14)
+#define DXEPINT_NAKINTRPT              (1 << 13)
+#define DXEPINT_BBLEERRINTRPT          (1 << 12)
+#define DXEPINT_PKTDRPSTS              (1 << 11)
+#define DXEPINT_BNAINTR                        (1 << 9)
+#define DXEPINT_TXFIFOUNDRN            (1 << 8)
+#define DXEPINT_OUTPKTERR              (1 << 8)
+#define DXEPINT_TXFEMP                 (1 << 7)
 #define DXEPINT_INEPNAKEFF             (1 << 6)
 #define DXEPINT_BACK2BACKSETUP         (1 << 6)
 #define DXEPINT_INTKNEPMIS             (1 << 5)
+#define DXEPINT_STSPHSERCVD            (1 << 5)
 #define DXEPINT_INTKNTXFEMP            (1 << 4)
 #define DXEPINT_OUTTKNEPDIS            (1 << 4)
 #define DXEPINT_TIMEOUT                        (1 << 3)
 #define PCGCTL_GATEHCLK                        (1 << 1)
 #define PCGCTL_STOPPCLK                        (1 << 0)
 
+#define PCGCCTL1                        HSOTG_REG(0xe04)
+#define PCGCCTL1_TIMER                  (0x3 << 1)
+#define PCGCCTL1_GATEEN                 (1 << 0)
+
 #define EPFIFO(_a)                     HSOTG_REG(0x1000 + ((_a) * 0x1000))
 
 /* Host Mode Registers */
 #define HCFIFO(_ch)                    HSOTG_REG(0x1000 + 0x1000 * (_ch))
 
 /**
- * struct dwc2_hcd_dma_desc - Host-mode DMA descriptor structure
+ * struct dwc2_dma_desc - DMA descriptor structure,
+ * used for both host and gadget modes
  *
  * @status: DMA descriptor status quadlet
  * @buf:    DMA descriptor data buffer pointer
  * DMA Descriptor structure contains two quadlets:
  * Status quadlet and Data buffer pointer.
  */
-struct dwc2_hcd_dma_desc {
+struct dwc2_dma_desc {
        u32 status;
        u32 buf;
-};
+} __packed;
+
+/* Host Mode DMA descriptor status quadlet */
 
 #define HOST_DMA_A                     (1 << 31)
 #define HOST_DMA_STS_MASK              (0x3 << 28)
@@ -805,8 +864,43 @@ struct dwc2_hcd_dma_desc {
 #define HOST_DMA_ISOC_NBYTES_SHIFT     0
 #define HOST_DMA_NBYTES_MASK           (0x1ffff << 0)
 #define HOST_DMA_NBYTES_SHIFT          0
+#define HOST_DMA_NBYTES_LIMIT          131071
+
+/* Device Mode DMA descriptor status quadlet */
+
+#define DEV_DMA_BUFF_STS_MASK          (0x3 << 30)
+#define DEV_DMA_BUFF_STS_SHIFT         30
+#define DEV_DMA_BUFF_STS_HREADY                0
+#define DEV_DMA_BUFF_STS_DMABUSY       1
+#define DEV_DMA_BUFF_STS_DMADONE       2
+#define DEV_DMA_BUFF_STS_HBUSY         3
+#define DEV_DMA_STS_MASK               (0x3 << 28)
+#define DEV_DMA_STS_SHIFT              28
+#define DEV_DMA_STS_SUCC               0
+#define DEV_DMA_STS_BUFF_FLUSH         1
+#define DEV_DMA_STS_BUFF_ERR           3
+#define DEV_DMA_L                      (1 << 27)
+#define DEV_DMA_SHORT                  (1 << 26)
+#define DEV_DMA_IOC                    (1 << 25)
+#define DEV_DMA_SR                     (1 << 24)
+#define DEV_DMA_MTRF                   (1 << 23)
+#define DEV_DMA_ISOC_PID_MASK          (0x3 << 23)
+#define DEV_DMA_ISOC_PID_SHIFT         23
+#define DEV_DMA_ISOC_PID_DATA0         0
+#define DEV_DMA_ISOC_PID_DATA2         1
+#define DEV_DMA_ISOC_PID_DATA1         2
+#define DEV_DMA_ISOC_PID_MDATA         3
+#define DEV_DMA_ISOC_FRNUM_MASK                (0x7ff << 12)
+#define DEV_DMA_ISOC_FRNUM_SHIFT       12
+#define DEV_DMA_ISOC_TX_NBYTES_MASK    (0xfff << 0)
+#define DEV_DMA_ISOC_TX_NBYTES_LIMIT   0xfff
+#define DEV_DMA_ISOC_RX_NBYTES_MASK    (0x7ff << 0)
+#define DEV_DMA_ISOC_RX_NBYTES_LIMIT   0x7ff
+#define DEV_DMA_ISOC_NBYTES_SHIFT      0
+#define DEV_DMA_NBYTES_MASK            (0xffff << 0)
+#define DEV_DMA_NBYTES_SHIFT           0
+#define DEV_DMA_NBYTES_LIMIT           0xffff
 
-#define MAX_DMA_DESC_SIZE              131071
 #define MAX_DMA_DESC_NUM_GENERIC       64
 #define MAX_DMA_DESC_NUM_HS_ISOC       256
 
diff --git a/sys/dev/usb/dwc2/dwc2_params.c b/sys/dev/usb/dwc2/dwc2_params.c
new file mode 100644 (file)
index 0000000..6fa5202
--- /dev/null
@@ -0,0 +1,1030 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) 2004-2016 Synopsys, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/signal.h>
+#include <sys/proc.h>
+#include <sys/timeout.h>
+#include <sys/mutex.h>
+#include <sys/pool.h>
+#include <sys/task.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/dwc2/dwc2.h>
+#include <dev/usb/dwc2/dwc2var.h>
+
+#include <dev/usb/dwc2/dwc2_core.h>
+#include <dev/usb/dwc2/dwc2_hcd.h>
+
+STATIC void dwc2_set_dwctwo_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+
+       p->otg_caps.hnp_support = sc->sc_params->otg_caps.hnp_support;
+       p->otg_caps.srp_support = sc->sc_params->otg_caps.srp_support;
+       p->host_dma = sc->sc_params->host_dma;
+       p->dma_desc_enable = sc->sc_params->dma_desc_enable;
+       p->speed = sc->sc_params->speed;
+       p->enable_dynamic_fifo = sc->sc_params->enable_dynamic_fifo;
+       p->en_multiple_tx_fifo = sc->sc_params->en_multiple_tx_fifo;
+       p->host_rx_fifo_size = sc->sc_params->host_rx_fifo_size;
+       p->host_nperio_tx_fifo_size = sc->sc_params->host_nperio_tx_fifo_size;
+       p->host_perio_tx_fifo_size = sc->sc_params->host_perio_tx_fifo_size;
+       p->max_transfer_size = sc->sc_params->max_transfer_size;
+       p->max_packet_count = sc->sc_params->max_packet_count;
+       p->host_channels = sc->sc_params->host_channels;
+       p->phy_type = sc->sc_params->phy_type;
+       p->phy_utmi_width = sc->sc_params->phy_utmi_width;
+       p->phy_ulpi_ddr = sc->sc_params->phy_ulpi_ddr;
+       p->phy_ulpi_ext_vbus = sc->sc_params->phy_ulpi_ext_vbus;
+       p->i2c_enable = sc->sc_params->i2c_enable;
+       p->ulpi_fs_ls = sc->sc_params->ulpi_fs_ls;
+       p->host_support_fs_ls_low_power = sc->sc_params->host_support_fs_ls_low_power;
+       p->host_ls_low_power_phy_clk = sc->sc_params->host_ls_low_power_phy_clk;
+       p->ts_dline = sc->sc_params->ts_dline;
+       p->reload_ctl = sc->sc_params->reload_ctl;
+       p->ahbcfg = sc->sc_params->ahbcfg;
+       p->uframe_sched = sc->sc_params->uframe_sched;
+       p->external_id_pin_ctl = sc->sc_params->external_id_pin_ctl;
+}
+
+#if 0
+static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->host_rx_fifo_size = 774;
+       p->max_transfer_size = 65535;
+       p->max_packet_count = 511;
+       p->ahbcfg = 0x10;
+}
+
+static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->host_rx_fifo_size = 512;
+       p->host_nperio_tx_fifo_size = 512;
+       p->host_perio_tx_fifo_size = 512;
+       p->max_transfer_size = 65535;
+       p->max_packet_count = 511;
+       p->host_channels = 16;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 8;
+       p->i2c_enable = false;
+       p->reload_ctl = false;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+               GAHBCFG_HBSTLEN_SHIFT;
+       p->change_speed_quirk = true;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
+static void dwc2_set_jz4775_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 16;
+       p->activate_ingenic_overcurrent_detection =
+               !device_property_read_bool(hsotg->dev, "disable-over-current");
+}
+
+static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->host_channels = 16;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 16;
+       p->activate_ingenic_overcurrent_detection =
+               !device_property_read_bool(hsotg->dev, "disable-over-current");
+}
+
+static void dwc2_set_x2000_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->host_rx_fifo_size = 1024;
+       p->host_nperio_tx_fifo_size = 1024;
+       p->host_perio_tx_fifo_size = 1024;
+       p->host_channels = 16;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 16;
+       p->activate_ingenic_overcurrent_detection =
+               !device_property_read_bool(hsotg->dev, "disable-over-current");
+}
+
+static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+       p->no_clock_gating = true;
+       p->phy_utmi_width = 8;
+}
+
+static void dwc2_set_socfpga_agilex_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+       p->no_clock_gating = true;
+}
+
+static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->host_rx_fifo_size = 525;
+       p->host_nperio_tx_fifo_size = 128;
+       p->host_perio_tx_fifo_size = 256;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+               GAHBCFG_HBSTLEN_SHIFT;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
+static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->host_rx_fifo_size = 288;
+       p->host_nperio_tx_fifo_size = 128;
+       p->host_perio_tx_fifo_size = 96;
+       p->max_transfer_size = 65535;
+       p->max_packet_count = 511;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+               GAHBCFG_HBSTLEN_SHIFT;
+}
+
+static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->host_rx_fifo_size = 512;
+       p->host_nperio_tx_fifo_size = 500;
+       p->host_perio_tx_fifo_size = 500;
+       p->host_channels = 16;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 <<
+               GAHBCFG_HBSTLEN_SHIFT;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
+static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->lpm = false;
+       p->lpm_clock_gating = false;
+       p->besl = false;
+       p->hird_threshold_en = false;
+}
+
+static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+}
+
+static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->speed = DWC2_SPEED_PARAM_FULL;
+       p->host_rx_fifo_size = 128;
+       p->host_nperio_tx_fifo_size = 96;
+       p->host_perio_tx_fifo_size = 96;
+       p->max_packet_count = 256;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
+       p->i2c_enable = false;
+       p->activate_stm_fs_transceiver = true;
+}
+
+static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->host_rx_fifo_size = 622;
+       p->host_nperio_tx_fifo_size = 128;
+       p->host_perio_tx_fifo_size = 256;
+}
+
+static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->otg_caps.otg_rev = 0x200;
+       p->speed = DWC2_SPEED_PARAM_FULL;
+       p->host_rx_fifo_size = 128;
+       p->host_nperio_tx_fifo_size = 96;
+       p->host_perio_tx_fifo_size = 96;
+       p->max_packet_count = 256;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
+       p->i2c_enable = false;
+       p->activate_stm_fs_transceiver = true;
+       p->activate_stm_id_vb_detection = true;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+       p->host_support_fs_ls_low_power = true;
+       p->host_ls_low_power_phy_clk = true;
+}
+
+static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->otg_caps.srp_support = false;
+       p->otg_caps.otg_rev = 0x200;
+       p->activate_stm_id_vb_detection = !device_property_read_bool(hsotg->dev, "usb-role-switch");
+       p->host_rx_fifo_size = 440;
+       p->host_nperio_tx_fifo_size = 256;
+       p->host_perio_tx_fifo_size = 256;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+       p->lpm = false;
+       p->lpm_clock_gating = false;
+       p->besl = false;
+       p->hird_threshold_en = false;
+}
+
+const struct of_device_id dwc2_of_match_table[] = {
+       { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
+       { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
+       { .compatible = "ingenic,jz4775-otg", .data = dwc2_set_jz4775_params },
+       { .compatible = "ingenic,jz4780-otg", .data = dwc2_set_jz4775_params },
+       { .compatible = "ingenic,x1000-otg", .data = dwc2_set_jz4775_params },
+       { .compatible = "ingenic,x1600-otg", .data = dwc2_set_x1600_params },
+       { .compatible = "ingenic,x1700-otg", .data = dwc2_set_x1600_params },
+       { .compatible = "ingenic,x1830-otg", .data = dwc2_set_x1600_params },
+       { .compatible = "ingenic,x2000-otg", .data = dwc2_set_x2000_params },
+       { .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params },
+       { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
+       { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
+       { .compatible = "snps,dwc2" },
+       { .compatible = "samsung,s3c6400-hsotg",
+         .data = dwc2_set_s3c6400_params },
+       { .compatible = "amlogic,meson8-usb",
+         .data = dwc2_set_amlogic_params },
+       { .compatible = "amlogic,meson8b-usb",
+         .data = dwc2_set_amlogic_params },
+       { .compatible = "amlogic,meson-gxbb-usb",
+         .data = dwc2_set_amlogic_params },
+       { .compatible = "amlogic,meson-g12a-usb",
+         .data = dwc2_set_amlogic_g12a_params },
+       { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
+       { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
+       { .compatible = "st,stm32f4x9-fsotg",
+         .data = dwc2_set_stm32f4x9_fsotg_params },
+       { .compatible = "st,stm32f4x9-hsotg" },
+       { .compatible = "st,stm32f7-hsotg",
+         .data = dwc2_set_stm32f7_hsotg_params },
+       { .compatible = "st,stm32mp15-fsotg",
+         .data = dwc2_set_stm32mp15_fsotg_params },
+       { .compatible = "st,stm32mp15-hsotg",
+         .data = dwc2_set_stm32mp15_hsotg_params },
+       { .compatible = "intel,socfpga-agilex-hsotg",
+         .data = dwc2_set_socfpga_agilex_params },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
+
+const struct acpi_device_id dwc2_acpi_match[] = {
+       { "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, dwc2_acpi_match);
+#endif
+
+STATIC void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg)
+{
+       switch (hsotg->hw_params.op_mode) {
+       case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
+               hsotg->params.otg_caps.hnp_support = true;
+               hsotg->params.otg_caps.srp_support = true;
+               break;
+       case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
+       case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
+       case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
+               hsotg->params.otg_caps.hnp_support = false;
+               hsotg->params.otg_caps.srp_support = true;
+               break;
+       default:
+               hsotg->params.otg_caps.hnp_support = false;
+               hsotg->params.otg_caps.srp_support = false;
+               break;
+       }
+}
+
+STATIC void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg)
+{
+       int val;
+       u32 hs_phy_type = hsotg->hw_params.hs_phy_type;
+
+       val = DWC2_PHY_TYPE_PARAM_FS;
+       if (hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED) {
+               if (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
+                   hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)
+                       val = DWC2_PHY_TYPE_PARAM_UTMI;
+               else
+                       val = DWC2_PHY_TYPE_PARAM_ULPI;
+       }
+
+       if (dwc2_is_fs_iot(hsotg))
+               hsotg->params.phy_type = DWC2_PHY_TYPE_PARAM_FS;
+
+       hsotg->params.phy_type = val;
+}
+
+STATIC void dwc2_set_param_speed(struct dwc2_hsotg *hsotg)
+{
+       int val;
+
+       val = hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS ?
+               DWC2_SPEED_PARAM_FULL : DWC2_SPEED_PARAM_HIGH;
+
+       if (dwc2_is_fs_iot(hsotg))
+               val = DWC2_SPEED_PARAM_FULL;
+
+       if (dwc2_is_hs_iot(hsotg))
+               val = DWC2_SPEED_PARAM_HIGH;
+
+       hsotg->params.speed = val;
+}
+
+void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       int val;
+
+       val = (hsotg->hw_params.utmi_phy_data_width ==
+              GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16;
+
+       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(hsotg->phy) == 8)
+                       val = 8;
+       }
+
+       hsotg->params.phy_utmi_width = val;
+#endif
+       hsotg->params.phy_utmi_width = 8;
+}
+
+static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+       int depth_average;
+       int fifo_count;
+       int i;
+
+       fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
+
+       memset(p->g_tx_fifo_size, 0, sizeof(p->g_tx_fifo_size));
+       depth_average = dwc2_hsotg_tx_fifo_average_depth(hsotg);
+       for (i = 1; i <= fifo_count; i++)
+               p->g_tx_fifo_size[i] = depth_average;
+}
+
+static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg)
+{
+       int val;
+
+       if (hsotg->hw_params.hibernation)
+               val = DWC2_POWER_DOWN_PARAM_HIBERNATION;
+       else if (hsotg->hw_params.power_optimized)
+               val = DWC2_POWER_DOWN_PARAM_PARTIAL;
+       else
+               val = DWC2_POWER_DOWN_PARAM_NONE;
+
+       hsotg->params.power_down = val;
+}
+
+static void dwc2_set_param_lpm(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->lpm = hsotg->hw_params.lpm_mode;
+       if (p->lpm) {
+               p->lpm_clock_gating = true;
+               p->besl = true;
+               p->hird_threshold_en = true;
+               p->hird_threshold = 4;
+       } else {
+               p->lpm_clock_gating = false;
+               p->besl = false;
+               p->hird_threshold_en = false;
+       }
+}
+
+/**
+ * dwc2_set_default_params() - Set all core parameters to their
+ * auto-detected default values.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ */
+STATIC void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       struct dwc2_core_params *p = &hsotg->params;
+       bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH);
+
+       dwc2_set_param_otg_cap(hsotg);
+       dwc2_set_param_phy_type(hsotg);
+       dwc2_set_param_speed(hsotg);
+       dwc2_set_param_phy_utmi_width(hsotg);
+       dwc2_set_param_power_down(hsotg);
+       dwc2_set_param_lpm(hsotg);
+       p->phy_ulpi_ddr = false;
+       p->phy_ulpi_ext_vbus = false;
+
+       p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
+       p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
+       p->i2c_enable = hw->i2c_enable;
+       p->acg_enable = hw->acg_enable;
+       p->ulpi_fs_ls = false;
+       p->ts_dline = false;
+       p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a);
+       p->uframe_sched = true;
+       p->external_id_pin_ctl = false;
+       p->ipg_isoc_en = false;
+       p->service_interval = false;
+       p->max_packet_count = hw->max_packet_count;
+       p->max_transfer_size = hw->max_transfer_size;
+       p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT;
+       p->ref_clk_per = 33333;
+       p->sof_cnt_wkup_alert = 100;
+
+       if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
+           (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+               p->host_dma = dma_capable;
+               p->dma_desc_enable = false;
+               p->dma_desc_fs_enable = false;
+               p->host_support_fs_ls_low_power = false;
+               p->host_ls_low_power_phy_clk = false;
+               p->host_channels = hw->host_channels;
+               p->host_rx_fifo_size = hw->rx_fifo_size;
+               p->host_nperio_tx_fifo_size = hw->host_nperio_tx_fifo_size;
+               p->host_perio_tx_fifo_size = hw->host_perio_tx_fifo_size;
+       }
+
+       if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+           (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+               p->g_dma = dma_capable;
+               p->g_dma_desc = hw->dma_desc_enable;
+
+               /*
+                * The values for g_rx_fifo_size (2048) and
+                * g_np_tx_fifo_size (1024) come from the legacy s3c
+                * gadget driver. These defaults have been hard-coded
+                * for some time so many platforms depend on these
+                * values. Leave them as defaults for now and only
+                * auto-detect if the hardware does not support the
+                * default.
+                */
+               p->g_rx_fifo_size = 2048;
+               p->g_np_tx_fifo_size = 1024;
+               dwc2_set_param_tx_fifo_sizes(hsotg);
+       }
+}
+
+#if 0
+/**
+ * dwc2_get_device_properties() - Read in device properties.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Read in the device properties and adjust core parameters if needed.
+ */
+static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+       int num;
+
+       if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+           (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+               device_property_read_u32(hsotg->dev, "g-rx-fifo-size",
+                                        &p->g_rx_fifo_size);
+
+               device_property_read_u32(hsotg->dev, "g-np-tx-fifo-size",
+                                        &p->g_np_tx_fifo_size);
+
+               num = device_property_count_u32(hsotg->dev, "g-tx-fifo-size");
+               if (num > 0) {
+                       num = min(num, 15);
+                       memset(p->g_tx_fifo_size, 0,
+                              sizeof(p->g_tx_fifo_size));
+                       device_property_read_u32_array(hsotg->dev,
+                                                      "g-tx-fifo-size",
+                                                      &p->g_tx_fifo_size[1],
+                                                      num);
+               }
+
+               of_usb_update_otg_caps(hsotg->dev->of_node, &p->otg_caps);
+       }
+
+       if (of_find_property(hsotg->dev->of_node, "disable-over-current", NULL))
+               p->oc_disable = true;
+}
+#endif
+
+static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg)
+{
+       int valid = 1;
+
+       if (hsotg->params.otg_caps.hnp_support && hsotg->params.otg_caps.srp_support) {
+               /* check HNP && SRP capable */
+               if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE)
+                       valid = 0;
+       } else if (!hsotg->params.otg_caps.hnp_support) {
+               /* check SRP only capable */
+               if (hsotg->params.otg_caps.srp_support) {
+                       switch (hsotg->hw_params.op_mode) {
+                       case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
+                       case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
+                       case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
+                       case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
+                               break;
+                       default:
+                               valid = 0;
+                               break;
+                       }
+               }
+               /* else: NO HNP && NO SRP capable: always valid */
+       } else {
+               valid = 0;
+       }
+
+       if (!valid)
+               dwc2_set_param_otg_cap(hsotg);
+}
+
+static void dwc2_check_param_phy_type(struct dwc2_hsotg *hsotg)
+{
+       int valid = 0;
+       u32 hs_phy_type;
+       u32 fs_phy_type;
+
+       hs_phy_type = hsotg->hw_params.hs_phy_type;
+       fs_phy_type = hsotg->hw_params.fs_phy_type;
+
+       switch (hsotg->params.phy_type) {
+       case DWC2_PHY_TYPE_PARAM_FS:
+               if (fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
+                       valid = 1;
+               break;
+       case DWC2_PHY_TYPE_PARAM_UTMI:
+               if ((hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI) ||
+                   (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
+                       valid = 1;
+               break;
+       case DWC2_PHY_TYPE_PARAM_ULPI:
+               if ((hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI) ||
+                   (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
+                       valid = 1;
+               break;
+       default:
+               break;
+       }
+
+       if (!valid)
+               dwc2_set_param_phy_type(hsotg);
+}
+
+static void dwc2_check_param_speed(struct dwc2_hsotg *hsotg)
+{
+       int valid = 1;
+       int phy_type = hsotg->params.phy_type;
+       int speed = hsotg->params.speed;
+
+       switch (speed) {
+       case DWC2_SPEED_PARAM_HIGH:
+               if ((hsotg->params.speed == DWC2_SPEED_PARAM_HIGH) &&
+                   (phy_type == DWC2_PHY_TYPE_PARAM_FS))
+                       valid = 0;
+               break;
+       case DWC2_SPEED_PARAM_FULL:
+       case DWC2_SPEED_PARAM_LOW:
+               break;
+       default:
+               valid = 0;
+               break;
+       }
+
+       if (!valid)
+               dwc2_set_param_speed(hsotg);
+}
+
+static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
+{
+       int valid = 0;
+       int param = hsotg->params.phy_utmi_width;
+       int width = hsotg->hw_params.utmi_phy_data_width;
+
+       switch (width) {
+       case GHWCFG4_UTMI_PHY_DATA_WIDTH_8:
+               valid = (param == 8);
+               break;
+       case GHWCFG4_UTMI_PHY_DATA_WIDTH_16:
+               valid = (param == 16);
+               break;
+       case GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16:
+               valid = (param == 8 || param == 16);
+               break;
+       }
+
+       if (!valid)
+               dwc2_set_param_phy_utmi_width(hsotg);
+}
+
+static void dwc2_check_param_power_down(struct dwc2_hsotg *hsotg)
+{
+       int param = hsotg->params.power_down;
+
+       switch (param) {
+       case DWC2_POWER_DOWN_PARAM_NONE:
+               break;
+       case DWC2_POWER_DOWN_PARAM_PARTIAL:
+               if (hsotg->hw_params.power_optimized)
+                       break;
+               dev_dbg(hsotg->dev,
+                       "Partial power down isn't supported by HW\n");
+               param = DWC2_POWER_DOWN_PARAM_NONE;
+               break;
+       case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+               if (hsotg->hw_params.hibernation)
+                       break;
+               dev_dbg(hsotg->dev,
+                       "Hibernation isn't supported by HW\n");
+               param = DWC2_POWER_DOWN_PARAM_NONE;
+               break;
+       default:
+               dev_err(hsotg->dev,
+                       "%s: Invalid parameter power_down=%d\n",
+                       __func__, param);
+               param = DWC2_POWER_DOWN_PARAM_NONE;
+               break;
+       }
+
+       hsotg->params.power_down = param;
+}
+
+static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
+{
+       int fifo_count;
+       int fifo;
+       int min;
+       u32 total = 0;
+       u32 dptxfszn;
+
+       fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
+       min = hsotg->hw_params.en_multiple_tx_fifo ? 16 : 4;
+
+       for (fifo = 1; fifo <= fifo_count; fifo++)
+               total += hsotg->params.g_tx_fifo_size[fifo];
+
+       if (total > dwc2_hsotg_tx_fifo_total_depth(hsotg) || !total) {
+               dev_warn(hsotg->dev, "%s: Invalid parameter g-tx-fifo-size, setting to default average\n",
+                        __func__);
+               dwc2_set_param_tx_fifo_sizes(hsotg);
+       }
+
+       for (fifo = 1; fifo <= fifo_count; fifo++) {
+               dptxfszn = hsotg->hw_params.g_tx_fifo_size[fifo];
+
+               if (hsotg->params.g_tx_fifo_size[fifo] < min ||
+                   hsotg->params.g_tx_fifo_size[fifo] >  dptxfszn) {
+                       dev_warn(hsotg->dev, "%s: Invalid parameter g_tx_fifo_size[%d]=%d\n",
+                                __func__, fifo,
+                                hsotg->params.g_tx_fifo_size[fifo]);
+                       hsotg->params.g_tx_fifo_size[fifo] = dptxfszn;
+               }
+       }
+}
+
+#define CHECK_RANGE(_param, _min, _max, _def) do {                     \
+               if ((int)(hsotg->params._param) < (_min) ||             \
+                   (hsotg->params._param) > (_max)) {                  \
+                       dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
+                                __func__, #_param, hsotg->params._param); \
+                       hsotg->params._param = (_def);                  \
+               }                                                       \
+       } while (0)
+
+#define CHECK_BOOL(_param, _check) do {                                        \
+               if (hsotg->params._param && !(_check)) {                \
+                       dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
+                                __func__, #_param, hsotg->params._param); \
+                       hsotg->params._param = false;                   \
+               }                                                       \
+       } while (0)
+
+STATIC void dwc2_check_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       struct dwc2_core_params *p = &hsotg->params;
+       bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH);
+
+       dwc2_check_param_otg_cap(hsotg);
+       dwc2_check_param_phy_type(hsotg);
+       dwc2_check_param_speed(hsotg);
+       dwc2_check_param_phy_utmi_width(hsotg);
+       dwc2_check_param_power_down(hsotg);
+       CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo);
+       CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo);
+       CHECK_BOOL(i2c_enable, hw->i2c_enable);
+       CHECK_BOOL(ipg_isoc_en, hw->ipg_isoc_en);
+       CHECK_BOOL(acg_enable, hw->acg_enable);
+       CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a));
+       CHECK_BOOL(lpm, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_80a));
+       CHECK_BOOL(lpm, hw->lpm_mode);
+       CHECK_BOOL(lpm_clock_gating, hsotg->params.lpm);
+       CHECK_BOOL(besl, hsotg->params.lpm);
+       CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a));
+       CHECK_BOOL(hird_threshold_en, hsotg->params.lpm);
+       CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0);
+       CHECK_BOOL(service_interval, hw->service_interval_mode);
+       CHECK_RANGE(max_packet_count,
+                   15, hw->max_packet_count,
+                   hw->max_packet_count);
+       CHECK_RANGE(max_transfer_size,
+                   2047, hw->max_transfer_size,
+                   hw->max_transfer_size);
+
+       if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
+           (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+               CHECK_BOOL(host_dma, dma_capable);
+               CHECK_BOOL(dma_desc_enable, p->host_dma);
+               CHECK_BOOL(dma_desc_fs_enable, p->dma_desc_enable);
+               CHECK_BOOL(host_ls_low_power_phy_clk,
+                          p->phy_type == DWC2_PHY_TYPE_PARAM_FS);
+               CHECK_RANGE(host_channels,
+                           1, hw->host_channels,
+                           hw->host_channels);
+               CHECK_RANGE(host_rx_fifo_size,
+                           16, hw->rx_fifo_size,
+                           hw->rx_fifo_size);
+               CHECK_RANGE(host_nperio_tx_fifo_size,
+                           16, hw->host_nperio_tx_fifo_size,
+                           hw->host_nperio_tx_fifo_size);
+               CHECK_RANGE(host_perio_tx_fifo_size,
+                           16, hw->host_perio_tx_fifo_size,
+                           hw->host_perio_tx_fifo_size);
+       }
+
+       if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+           (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+               CHECK_BOOL(g_dma, dma_capable);
+               CHECK_BOOL(g_dma_desc, (p->g_dma && hw->dma_desc_enable));
+               CHECK_RANGE(g_rx_fifo_size,
+                           16, hw->rx_fifo_size,
+                           hw->rx_fifo_size);
+               CHECK_RANGE(g_np_tx_fifo_size,
+                           16, hw->dev_nperio_tx_fifo_size,
+                           hw->dev_nperio_tx_fifo_size);
+               dwc2_check_param_tx_fifo_sizes(hsotg);
+       }
+}
+
+/*
+ * Gets host hardware parameters. Forces host mode if not currently in
+ * host mode. Should be called immediately after a core soft reset in
+ * order to get the reset values.
+ */
+STATIC void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       u32 gnptxfsiz;
+       u32 hptxfsiz;
+
+       if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+               return;
+
+       dwc2_force_mode(hsotg, true);
+
+       gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ);
+       hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ);
+
+       hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                      FIFOSIZE_DEPTH_SHIFT;
+       hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                     FIFOSIZE_DEPTH_SHIFT;
+}
+
+/*
+ * Gets device hardware parameters. Forces device mode if not
+ * currently in device mode. Should be called immediately after a core
+ * soft reset in order to get the reset values.
+ */
+STATIC void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       u32 gnptxfsiz;
+       int fifo, fifo_count;
+
+       if (hsotg->dr_mode == USB_DR_MODE_HOST)
+               return;
+
+       dwc2_force_mode(hsotg, false);
+
+       gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ);
+
+       fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
+
+       for (fifo = 1; fifo <= fifo_count; fifo++) {
+               hw->g_tx_fifo_size[fifo] =
+                       (dwc2_readl(hsotg, DPTXFSIZN(fifo)) &
+                        FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT;
+       }
+
+       hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                      FIFOSIZE_DEPTH_SHIFT;
+}
+
+/**
+ * dwc2_get_hwparams() - During device initialization, read various hardware
+ *                       configuration registers and interpret the contents.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ */
+int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       unsigned int width;
+       u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
+       u32 grxfsiz;
+
+       hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
+       hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
+       hwcfg3 = dwc2_readl(hsotg, GHWCFG3);
+       hwcfg4 = dwc2_readl(hsotg, GHWCFG4);
+       grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
+
+       /* hwcfg1 */
+       hw->dev_ep_dirs = hwcfg1;
+
+       /* hwcfg2 */
+       hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+                     GHWCFG2_OP_MODE_SHIFT;
+       hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >>
+                  GHWCFG2_ARCHITECTURE_SHIFT;
+       hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO);
+       hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >>
+                               GHWCFG2_NUM_HOST_CHAN_SHIFT);
+       hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >>
+                         GHWCFG2_HS_PHY_TYPE_SHIFT;
+       hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >>
+                         GHWCFG2_FS_PHY_TYPE_SHIFT;
+       hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >>
+                        GHWCFG2_NUM_DEV_EP_SHIFT;
+       hw->nperio_tx_q_depth =
+               (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >>
+               GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1;
+       hw->host_perio_tx_q_depth =
+               (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >>
+               GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1;
+       hw->dev_token_q_depth =
+               (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >>
+               GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT;
+
+       /* hwcfg3 */
+       width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
+               GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
+       hw->max_transfer_size = (1 << (width + 11)) - 1;
+       width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
+               GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
+       hw->max_packet_count = (1 << (width + 4)) - 1;
+       hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
+       hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
+                             GHWCFG3_DFIFO_DEPTH_SHIFT;
+       hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN);
+
+       /* hwcfg4 */
+       hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
+       hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >>
+                                 GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT;
+       hw->num_dev_in_eps = (hwcfg4 & GHWCFG4_NUM_IN_EPS_MASK) >>
+                            GHWCFG4_NUM_IN_EPS_SHIFT;
+       hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
+       hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
+       hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER);
+       hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
+                                 GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
+       hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED);
+       hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED);
+       hw->service_interval_mode = !!(hwcfg4 &
+                                      GHWCFG4_SERVICE_INTERVAL_SUPPORTED);
+
+       /* fifo sizes */
+       hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
+                               GRXFSIZ_DEPTH_SHIFT;
+       /*
+        * Host specific hardware parameters. Reading these parameters
+        * requires the controller to be in host mode. The mode will
+        * be forced, if necessary, to read these values.
+        */
+       dwc2_get_host_hwparams(hsotg);
+       dwc2_get_dev_hwparams(hsotg);
+
+       return 0;
+}
+
+typedef void (*set_params_cb)(struct dwc2_hsotg *data);
+
+int dwc2_init_params(struct dwc2_hsotg *hsotg)
+{
+#if 0
+       const struct of_device_id *match;
+       set_params_cb set_params;
+#endif
+
+       dwc2_set_default_params(hsotg);
+       dwc2_set_dwctwo_params(hsotg);
+#if 0
+       dwc2_get_device_properties(hsotg);
+
+       match = of_match_device(dwc2_of_match_table, hsotg->dev);
+       if (match && match->data) {
+               set_params = match->data;
+               set_params(hsotg);
+       } else {
+               const struct acpi_device_id *amatch;
+
+               amatch = acpi_match_device(dwc2_acpi_match, hsotg->dev);
+               if (amatch && amatch->driver_data) {
+                       set_params = (set_params_cb)amatch->driver_data;
+                       set_params(hsotg);
+               }
+       }
+#endif
+
+       dwc2_check_params(hsotg);
+
+       return 0;
+}
index 425889a..b9a9d95 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2var.h,v 1.22 2021/07/30 18:05:24 mglocker Exp $   */
+/*     $OpenBSD: dwc2var.h,v 1.23 2022/09/04 08:42:40 mglocker Exp $   */
 /*     $NetBSD: dwc2var.h,v 1.3 2013/10/22 12:57:40 skrll Exp $        */
 
 /*-
@@ -125,15 +125,55 @@ dwc2_root_intr(dwc2_softc_t *sc)
 /*
  * XXX Compat
  */
+#define USB_MAXCHILDREN                31      /* XXX: Include in to our USB stack */
 #define DWC2_MAXISOCPACKETS    40      /* XXX: Fix nframes handling */
 #define ENOSR                  90
 #define device_xname(d)                ((d)->dv_xname)
 #define jiffies                        hardclock_ticks
-#define mstohz(ms) \
-       (__predict_false((ms) >= 0x20000) ? \
-           ((ms +0u) / 1000u) * hz : \
-           ((ms +0u) * hz) / 1000u)
-#define msecs_to_jiffies       mstohz
+#define msecs_to_jiffies(x)    (((uint64_t)(x)) * hz / 1000)
 #define IS_ENABLED(option)     (option)
+#define DIV_ROUND_UP(x, y)     (((x) + ((y) - 1)) / (y))
+#define NS_TO_US(ns)           DIV_ROUND_UP(ns, 1000L)
+#define BitTime(bytecount)     (7 * 8 * bytecount / 6)
+#define BITS_PER_LONG          64
+#define unlikely(x)             __builtin_expect(!!(x), 0)
+
+#define USB2_HOST_DELAY                5
+#define HS_NSECS(bytes)                (((55 * 8 * 2083)               \
+       + (2083UL * (3 + BitTime(bytes))))/1000                 \
+       + USB2_HOST_DELAY)
+#define HS_NSECS_ISO(bytes)    (((38 * 8 * 2083)               \
+       + (2083UL * (3 + BitTime(bytes))))/1000                 \
+       + USB2_HOST_DELAY)
+#define HS_USECS(bytes)                NS_TO_US(HS_NSECS(bytes))
+#define HS_USECS_ISO(bytes)    NS_TO_US(HS_NSECS_ISO(bytes))
+
+#define min_t(t, a, b) ({                                      \
+       t __min_a = (a);                                        \
+       t __min_b = (b);                                        \
+       __min_a < __min_b ? __min_a : __min_b; })
+#define max_t(t, a, b) ({                                      \
+        t __max_a = (a);                                       \
+        t __max_b = (b);                                       \
+        __max_a > __max_b ? __max_a : __max_b; })
+
+#define _WARN_STR(x)           #x
+#define WARN_ON(condition) ({                                  \
+       int __ret = !!(condition);                              \
+       if (__ret)                                              \
+               printf("WARNING %s failed at %s:%d\n",          \
+                   _WARN_STR(condition), __FILE__, __LINE__);  \
+       unlikely(__ret);                                        \
+})
+#define WARN_ON_ONCE(condition) ({                             \
+       static int __warned;                                    \
+       int __ret = !!(condition);                              \
+       if (__ret && !__warned) {                               \
+               printf("WARNING %s failed at %s:%d\n",          \
+                   _WARN_STR(condition), __FILE__, __LINE__);  \
+               __warned = 1;                                   \
+       }                                                       \
+       unlikely(__ret);                                        \
+})
 
 #endif /* _DWC_OTGVAR_H_ */
index 88d5038..17fcfa6 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.dwc2,v 1.2 2015/02/10 12:51:42 uebayasi Exp $
+#      $OpenBSD: files.dwc2,v 1.3 2022/09/04 08:42:40 mglocker Exp $
 #      $NetBSD: files.dwc2,v 1.2 2014/09/12 16:40:38 skrll Exp $
 
 # DesignWare HS OTG Controller
@@ -12,3 +12,4 @@ file  dev/usb/dwc2/dwc2_hcd.c                 dwctwo
 file   dev/usb/dwc2/dwc2_hcdddma.c             dwctwo
 file   dev/usb/dwc2/dwc2_hcdintr.c             dwctwo
 file   dev/usb/dwc2/dwc2_hcdqueue.c            dwctwo
+file   dev/usb/dwc2/dwc2_params.c              dwctwo
diff --git a/sys/dev/usb/dwc2/gcd.h b/sys/dev/usb/dwc2/gcd.h
new file mode 100644 (file)
index 0000000..efc890f
--- /dev/null
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LINUX_GCD_H
+#define _LINUX_GCD_H
+
+/*
+ * Compute the greatest common divisor of a and b.
+ * from libc getopt_long.c
+ */
+static inline unsigned long
+gcd(unsigned long a, unsigned long b)
+{
+       unsigned long c;
+
+       c = a % b;
+       while (c != 0) {
+               a = b;
+               b = c;
+               c = a % b;
+       }
+
+       return (b);
+}
+
+#endif
index a301eb5..d9f944f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uhub.c,v 1.96 2022/07/10 20:15:31 mlarkin Exp $ */
+/*     $OpenBSD: uhub.c,v 1.97 2022/09/04 08:42:39 mglocker Exp $ */
 /*     $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */
 /*     $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $       */
 
@@ -224,6 +224,7 @@ uhub_attach(struct device *parent, struct device *self, void *aux)
        hub->nports = nports;
        hub->powerdelay = powerdelay;
        hub->ttthink = ttthink >> 5;
+       hub->multi = UHUB_IS_SINGLE_TT(sc) ? 0 : 1;
 
        if (!dev->self_powered && dev->powersrc->parent != NULL &&
            !dev->powersrc->parent->self_powered) {
@@ -307,6 +308,7 @@ uhub_attach(struct device *parent, struct device *self, void *aux)
                if (UHUB_IS_HIGH_SPEED(sc)) {
                        up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
                        up->tt->hub = hub;
+                       up->tt->hcpriv = NULL;
                } else {
                        up->tt = NULL;
                }
index e84d8f7..45cfe20 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: usbdivar.h,v 1.82 2022/04/12 19:41:11 naddy Exp $ */
+/*     $OpenBSD: usbdivar.h,v 1.83 2022/09/04 08:42:39 mglocker Exp $ */
 /*     $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $   */
 /*     $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $   */
 
@@ -79,6 +79,7 @@ struct usbd_pipe_methods {
 
 struct usbd_tt {
        struct usbd_hub        *hub;
+       void                   *hcpriv;
 };
 
 struct usbd_port {
@@ -100,6 +101,7 @@ struct usbd_hub {
        int                     nports;
        u_int8_t                powerdelay;
        u_int8_t                ttthink;
+       u_int8_t                multi;
 };
 
 struct usbd_bus {