Sync dwctwo(4) with the NetBSD-current code base.
authormglocker <mglocker@openbsd.org>
Thu, 22 Jul 2021 18:32:33 +0000 (18:32 +0000)
committermglocker <mglocker@openbsd.org>
Thu, 22 Jul 2021 18:32:33 +0000 (18:32 +0000)
On the Raspberry Pi 3 Model B+ this does as a benefit:

* Enable the USB on-board Ethernet controller through mue(4).
* Enable the two USB uhub2 ports for removable devices.

Feedback incorporated from kettenis@ and jsg@.

ok kettenis@

13 files changed:
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/dwc2var.h
sys/dev/usb/dwc2/list.h [new file with mode: 0644]

index a7ea0ff..6439fea 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2.c,v 1.53 2021/01/28 01:48:54 kurt Exp $  */
+/*     $OpenBSD: dwc2.c,v 1.54 2021/07/22 18:32:33 mglocker Exp $      */
 /*     $NetBSD: dwc2.c,v 1.32 2014/09/02 23:26:20 macallan Exp $       */
 
 /*-
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#if 0
-#include "opt_usb.h"
-#endif
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
@@ -43,9 +39,6 @@
 #include <sys/proc.h>
 #include <sys/queue.h>
 #include <sys/endian.h>
-#if 0
-#include <sys/cpu.h>
-#endif
 
 #include <machine/bus.h>
 
 #include <dev/usb/usbdivar.h>
 #include <dev/usb/usb_mem.h>
 
-#if 0
-#include <dev/usb/usbroothub_subr.h>
-#endif
-
 #include <dev/usb/dwc2/dwc2.h>
 #include <dev/usb/dwc2/dwc2var.h>
 
@@ -90,16 +79,8 @@ STATIC int           dwc2_setaddr(struct usbd_device *, int);
 STATIC void            dwc2_poll(struct usbd_bus *);
 STATIC void            dwc2_softintr(void *);
 
-#if 0
-STATIC usbd_status     dwc2_allocm(struct usbd_bus *, struct usb_dma *, uint32_t);
-STATIC void            dwc2_freem(struct usbd_bus *, struct usb_dma *);
-#endif
-
 STATIC struct usbd_xfer        *dwc2_allocx(struct usbd_bus *);
 STATIC void            dwc2_freex(struct usbd_bus *, struct usbd_xfer *);
-#if 0
-STATIC void            dwc2_get_lock(struct usbd_bus *, struct mutex **);
-#endif
 
 STATIC usbd_status     dwc2_root_ctrl_transfer(struct usbd_xfer *);
 STATIC usbd_status     dwc2_root_ctrl_start(struct usbd_xfer *);
@@ -151,13 +132,13 @@ STATIC void               dwc2_rhc(void *);
 STATIC void            dwc2_timeout(void *);
 STATIC void            dwc2_timeout_task(void *);
 
-STATIC_INLINE void
+static inline void
 dwc2_allocate_bus_bandwidth(struct dwc2_hsotg *hsotg, u16 bw,
                            struct usbd_xfer *xfer)
 {
 }
 
-STATIC_INLINE void
+static inline void
 dwc2_free_bus_bandwidth(struct dwc2_hsotg *hsotg, u16 bw,
                        struct usbd_xfer *xfer)
 {
@@ -170,15 +151,8 @@ STATIC struct usbd_bus_methods dwc2_bus_methods = {
        .dev_setaddr =  dwc2_setaddr,
        .soft_intr =    dwc2_softintr,
        .do_poll =      dwc2_poll,
-#if 0
-       .allocm =       dwc2_allocm,
-       .freem =        dwc2_freem,
-#endif
        .allocx =       dwc2_allocx,
        .freex =        dwc2_freex,
-#if 0
-       .get_lock =     dwc2_get_lock,
-#endif
 };
 
 STATIC struct usbd_pipe_methods dwc2_root_ctrl_methods = {
@@ -235,36 +209,6 @@ STATIC struct usbd_pipe_methods dwc2_device_isoc_methods = {
        .done =         dwc2_device_isoc_done,
 };
 
-#if 0
-STATIC usbd_status
-dwc2_allocm(struct usbd_bus *bus, struct usb_dma *dma, uint32_t size)
-{
-       struct dwc2_softc *sc = DWC2_BUS2SC(bus);
-       usbd_status status;
-
-       DPRINTFN(10, "\n");
-
-       status = usb_allocmem(&sc->sc_bus, size, 0, dma);
-       if (status == USBD_NOMEM)
-               status = usb_reserve_allocm(&sc->sc_dma_reserve, dma, size);
-       return status;
-}
-
-STATIC void
-dwc2_freem(struct usbd_bus *bus, struct usb_dma *dma)
-{
-       struct dwc2_softc *sc = DWC2_BUS2SC(bus);
-
-       DPRINTFN(10, "\n");
-
-       if (dma->block->flags & USB_DMA_RESERVE) {
-               usb_reserve_freem(&sc->sc_dma_reserve, dma);
-               return;
-       }
-       usb_freemem(&sc->sc_bus, dma);
-}
-#endif
-
 /*
  * Work around the half configured control (default) pipe when setting
  * the address of a device.
@@ -297,18 +241,12 @@ dwc2_allocx(struct usbd_bus *bus)
        DPRINTFN(10, "\n");
 
        DWC2_EVCNT_INCR(sc->sc_ev_xferpoolget);
-       dxfer = pool_get(&sc->sc_xferpool, PR_NOWAIT);
+       dxfer = pool_get(&sc->sc_xferpool, PR_WAITOK);
        if (dxfer != NULL) {
                memset(dxfer, 0, sizeof(*dxfer));
-
                dxfer->urb = dwc2_hcd_urb_alloc(sc->sc_hsotg,
-                   DWC2_MAXISOCPACKETS, GFP_ATOMIC);
-               if (dxfer->urb == NULL) {
-                       pool_put(&sc->sc_xferpool, dxfer);
-                       return NULL;
-               }
-
-#ifdef DWC2_DEBUG
+                   DWC2_MAXISOCPACKETS, M_NOWAIT);
+#ifdef DIAGNOSTIC
                dxfer->xfer.busy_free = XFER_ONQU;
 #endif
        }
@@ -323,8 +261,9 @@ dwc2_freex(struct usbd_bus *bus, struct usbd_xfer *xfer)
 
        DPRINTFN(10, "\n");
 
-#ifdef DWC2_DEBUG
-       if (xfer->busy_free != XFER_ONQU) {
+#ifdef DIAGNOSTIC
+       if (xfer->busy_free != XFER_ONQU &&
+           xfer->status != USBD_NOT_STARTED) {
                DPRINTF("xfer=%p not busy, 0x%08x\n", xfer, xfer->busy_free);
        }
        xfer->busy_free = XFER_FREE;
@@ -334,16 +273,6 @@ dwc2_freex(struct usbd_bus *bus, struct usbd_xfer *xfer)
        pool_put(&sc->sc_xferpool, xfer);
 }
 
-#if 0
-STATIC void
-dwc2_get_lock(struct usbd_bus *bus, struct mutex **lock)
-{
-       struct dwc2_softc *sc = DWC2_BUS2SC(bus);
-
-       *lock = &sc->sc_lock;
-}
-#endif
-
 STATIC void
 dwc2_rhc(void *addr)
 {
@@ -352,13 +281,16 @@ dwc2_rhc(void *addr)
        u_char *p;
 
        DPRINTF("\n");
+       mtx_enter(&sc->sc_lock);
        xfer = sc->sc_intrxfer;
 
        if (xfer == NULL) {
                /* Just ignore the change. */
+               mtx_leave(&sc->sc_lock);
                return;
 
        }
+
        /* set port bit */
        p = KERNADDR(&xfer->dmabuf, 0);
 
@@ -368,6 +300,7 @@ dwc2_rhc(void *addr)
        xfer->status = USBD_NORMAL_COMPLETION;
 
        usb_transfer_complete(xfer);
+       mtx_leave(&sc->sc_lock);
 }
 
 STATIC void
@@ -376,42 +309,48 @@ dwc2_softintr(void *v)
        struct usbd_bus *bus = v;
        struct dwc2_softc *sc = DWC2_BUS2SC(bus);
        struct dwc2_hsotg *hsotg = sc->sc_hsotg;
-       struct dwc2_xfer *dxfer;
+       struct dwc2_xfer *dxfer, *next;
+       TAILQ_HEAD(, dwc2_xfer) claimed = TAILQ_HEAD_INITIALIZER(claimed);
 
+       /*
+        * Grab all the xfers that have not been aborted or timed out.
+        * Do so under a single lock -- without dropping it to run
+        * usb_transfer_complete as we go -- so that dwc2_abortx won't
+        * remove next out from under us during iteration when we've
+        * dropped the lock.
+        */
        mtx_enter(&hsotg->lock);
-       while ((dxfer = TAILQ_FIRST(&sc->sc_complete)) != NULL) {
-
-               KASSERTMSG(!timeout_pending(&dxfer->xfer.timeout_handle), 
-                   "xfer %p pipe %p\n", dxfer, dxfer->xfer.pipe);
-
-               /*
-                * dwc2_abort_xfer will remove this transfer from the
-                * sc_complete queue
-                */
-               /*XXXNH not tested */
-               if (dxfer->flags & DWC2_XFER_ABORTING) {
-                       wakeup(&dxfer->flags);
-                       continue;
-               }
-
+       TAILQ_FOREACH_SAFE(dxfer, &sc->sc_complete, xnext, next) {
+               KASSERT(dxfer->xfer.status == USBD_IN_PROGRESS);
+               KASSERT(dxfer->intr_status != USBD_CANCELLED);
+               KASSERT(dxfer->intr_status != USBD_TIMEOUT);
                TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext);
+               TAILQ_INSERT_TAIL(&claimed, dxfer, xnext);
+       }
+       mtx_leave(&hsotg->lock);
 
-               mtx_leave(&hsotg->lock);
+       /* Now complete them.  */
+       while (!TAILQ_EMPTY(&claimed)) {
+               dxfer = TAILQ_FIRST(&claimed);
+               KASSERT(dxfer->xfer.status == USBD_IN_PROGRESS);
+               KASSERT(dxfer->intr_status != USBD_CANCELLED);
+               KASSERT(dxfer->intr_status != USBD_TIMEOUT);
+               TAILQ_REMOVE(&claimed, dxfer, xnext);
+
+               dxfer->xfer.status = dxfer->intr_status;
                usb_transfer_complete(&dxfer->xfer);
-               mtx_enter(&hsotg->lock);
        }
-       mtx_leave(&hsotg->lock);
 }
 
 STATIC void
 dwc2_timeout(void *addr)
 {
        struct usbd_xfer *xfer = addr;
-       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
 
        DPRINTF("xfer=%p\n", xfer);
 
-       if (sc->sc_dying) {
+       if (sc->sc_bus.dying) {
                dwc2_timeout_task(addr);
                return;
        }
@@ -449,7 +388,7 @@ dwc2_open(struct usbd_pipe *pipe)
        DPRINTF("pipe %p addr %d xfertype %d dir %s\n", pipe, addr, xfertype,
            UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in" : "out");
 
-       if (sc->sc_dying) {
+       if (sc->sc_bus.dying) {
                return USBD_IOERROR;
        }
 
@@ -476,7 +415,7 @@ dwc2_open(struct usbd_pipe *pipe)
                err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
                    0, USB_DMA_COHERENT, &dpipe->req_dma);
                if (err)
-                       return err;
+                       return USBD_NOMEM;
                break;
        case UE_INTERRUPT:
                pipe->methods = &dwc2_device_intr_methods;
@@ -492,7 +431,8 @@ dwc2_open(struct usbd_pipe *pipe)
                return USBD_INVAL;
        }
 
-       dpipe->priv = NULL;     /* QH */
+       /* QH */
+       dpipe->priv = NULL;
 
        return USBD_NORMAL_COMPLETION;
 }
@@ -535,7 +475,7 @@ dwc2_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
 
        DPRINTF("xfer=%p\n", xfer);
 
-       if (sc->sc_dying) {
+       if (sc->sc_bus.dying) {
                xfer->status = status;
                timeout_del(&xfer->timeout_handle);
                usb_rem_task(xfer->device, &xfer->abort_task);
@@ -670,20 +610,6 @@ STATIC const struct dwc2_config_desc dwc2_confd = {
        },
 };
 
-#define        HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
-#if 0
-/* appears to be unused */
-STATIC const usb_hub_descriptor_t dwc2_hubd = {
-       .bDescLength = USB_HUB_DESCRIPTOR_SIZE,
-       .bDescriptorType = UDESC_HUB,
-       .bNbrPorts = 1,
-       HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)),
-       .bPwrOn2PwrGood = 50,
-       .bHubContrCurrent = 0,
-       .DeviceRemovable = {0},         /* port is removable */
-};
-#endif
-
 STATIC usbd_status
 dwc2_root_ctrl_transfer(struct usbd_xfer *xfer)
 {
@@ -706,7 +632,7 @@ dwc2_root_ctrl_start(struct usbd_xfer *xfer)
        int value, index, l, s, totlen;
        usbd_status err = USBD_IOERROR;
 
-       if (sc->sc_dying)
+       if (sc->sc_bus.dying)
                return USBD_IOERROR;
 
        req = &xfer->request;
@@ -746,7 +672,7 @@ dwc2_root_ctrl_start(struct usbd_xfer *xfer)
                switch (value) {
                case C(0, UDESC_DEVICE):
                        l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
-//                     USETW(dwc2_devd.idVendor, sc->sc_id_vendor);
+//                     USETW(dwc2_devd.idVendor, sc->sc_id_vendor);
                        memcpy(buf, &dwc2_devd, l);
                        buf += l;
                        len -= l;
@@ -870,12 +796,15 @@ dwc2_root_ctrl_done(struct usbd_xfer *xfer)
 STATIC usbd_status
 dwc2_root_intr_transfer(struct usbd_xfer *xfer)
 {
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
        usbd_status err;
 
        DPRINTF("\n");
 
        /* Insert last in queue. */
+       mtx_enter(&sc->sc_lock);
        err = usb_insert_transfer(xfer);
+       mtx_leave(&sc->sc_lock);
        if (err)
                return err;
 
@@ -887,14 +816,20 @@ STATIC usbd_status
 dwc2_root_intr_start(struct usbd_xfer *xfer)
 {
        struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
+       const bool polling = sc->sc_bus.use_polling;
 
        DPRINTF("\n");
 
-       if (sc->sc_dying)
+       if (sc->sc_bus.dying)
                return USBD_IOERROR;
 
+       if (!polling)
+               mtx_enter(&sc->sc_lock);
        KASSERT(sc->sc_intrxfer == NULL);
        sc->sc_intrxfer = xfer;
+       xfer->status = USBD_IN_PROGRESS;
+       if (!polling)
+               mtx_leave(&sc->sc_lock);
 
        return USBD_IN_PROGRESS;
 }
@@ -907,10 +842,16 @@ dwc2_root_intr_abort(struct usbd_xfer *xfer)
 
        DPRINTF("xfer=%p\n", xfer);
 
-       KASSERT(xfer->pipe->intrxfer == xfer);
-
-       sc->sc_intrxfer = NULL;
+       /* If xfer has already completed, nothing to do here.  */
+       if (sc->sc_intrxfer == NULL)
+               return;
 
+       /*
+        * Otherwise, sc->sc_intrxfer had better be this transfer.
+        * Cancel it.
+        */
+       KASSERT(sc->sc_intrxfer == xfer);
+       KASSERT(xfer->status == USBD_IN_PROGRESS);
        xfer->status = USBD_CANCELLED;
        usb_transfer_complete(xfer);
 }
@@ -922,14 +863,24 @@ dwc2_root_intr_close(struct usbd_pipe *pipe)
 
        DPRINTF("\n");
 
-       sc->sc_intrxfer = NULL;
+       /*
+        * Caller must guarantee the xfer has completed first, by
+        * closing the pipe only after normal completion or an abort.
+        */
+       KASSERT(sc->sc_intrxfer == NULL);
 }
 
 STATIC void
 dwc2_root_intr_done(struct usbd_xfer *xfer)
 {
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
 
        DPRINTF("\n");
+
+       /* Claim the xfer so it doesn't get completed again.  */
+       KASSERT(sc->sc_intrxfer == xfer);
+       KASSERT(xfer->status != USBD_IN_PROGRESS);
+       sc->sc_intrxfer = NULL;
 }
 
 /***********************************************************************/
@@ -937,12 +888,15 @@ dwc2_root_intr_done(struct usbd_xfer *xfer)
 STATIC usbd_status
 dwc2_device_ctrl_transfer(struct usbd_xfer *xfer)
 {
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
        usbd_status err;
 
        DPRINTF("\n");
 
        /* Insert last in queue. */
+       mtx_enter(&sc->sc_lock);
        err = usb_insert_transfer(xfer);
+       mtx_leave(&sc->sc_lock);
        if (err)
                return err;
 
@@ -953,14 +907,23 @@ dwc2_device_ctrl_transfer(struct usbd_xfer *xfer)
 STATIC usbd_status
 dwc2_device_ctrl_start(struct usbd_xfer *xfer)
 {
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
        usbd_status err;
+       const bool polling = sc->sc_bus.use_polling;
 
        DPRINTF("\n");
 
+       if (!polling)
+               mtx_enter(&sc->sc_lock);
        xfer->status = USBD_IN_PROGRESS;
        err = dwc2_device_start(xfer);
+       if (!polling)
+               mtx_leave(&sc->sc_lock);
 
-       return err;
+       if (err)
+               return err;
+
+       return USBD_IN_PROGRESS;
 }
 
 STATIC void
@@ -973,9 +936,13 @@ dwc2_device_ctrl_abort(struct usbd_xfer *xfer)
 STATIC void
 dwc2_device_ctrl_close(struct usbd_pipe *pipe)
 {
+       struct dwc2_softc * const sc = DWC2_PIPE2SC(pipe);
+       struct dwc2_pipe * const dpipe = DWC2_PIPE2DPIPE(pipe);
 
        DPRINTF("pipe=%p\n", pipe);
        dwc2_close_pipe(pipe);
+
+       usb_freemem(&sc->sc_bus, &dpipe->req_dma);
 }
 
 STATIC void
@@ -1045,12 +1012,15 @@ dwc2_device_bulk_done(struct usbd_xfer *xfer)
 STATIC usbd_status
 dwc2_device_intr_transfer(struct usbd_xfer *xfer)
 {
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
        usbd_status err;
 
        DPRINTF("xfer=%p\n", xfer);
 
        /* Insert last in queue. */
+       mtx_enter(&sc->sc_lock);
        err = usb_insert_transfer(xfer);
+       mtx_leave(&sc->sc_lock);
        if (err)
                return err;
 
@@ -1061,12 +1031,21 @@ dwc2_device_intr_transfer(struct usbd_xfer *xfer)
 STATIC usbd_status
 dwc2_device_intr_start(struct usbd_xfer *xfer)
 {
+       struct dwc2_softc *sc = DWC2_XFER2SC(xfer);
        usbd_status err;
+       const bool polling = sc->sc_bus.use_polling;
 
+       if (!polling)
+               mtx_enter(&sc->sc_lock);
        xfer->status = USBD_IN_PROGRESS;
        err = dwc2_device_start(xfer);
+       if (!polling)
+               mtx_leave(&sc->sc_lock);
 
-       return err;
+       if (err)
+               return err;
+
+       return USBD_IN_PROGRESS;
 }
 
 /* Abort a device interrupt request. */
@@ -1180,9 +1159,8 @@ dwc2_device_start(struct usbd_xfer *xfer)
 
        uint32_t flags = 0;
        uint32_t off = 0;
-       int retval, err = USBD_IN_PROGRESS;
+       int retval, err;
        int alloc_bandwidth = 0;
-       int i;
 
        DPRINTFN(1, "xfer=%p pipe=%p\n", xfer, xfer->pipe);
 
@@ -1211,7 +1189,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
                /* Copy request packet to our DMA buffer */
                memcpy(KERNADDR(&dpipe->req_dma, 0), req, sizeof(*req));
                usb_syncmem(&dpipe->req_dma, 0, sizeof(*req),
-                           BUS_DMASYNC_PREWRITE);
+                   BUS_DMASYNC_PREWRITE);
                len = UGETW(req->wLength);
                if ((req->bmRequestType & UT_READ) == UT_READ) {
                        dir = UE_DIR_IN;
@@ -1223,11 +1201,25 @@ dwc2_device_start(struct usbd_xfer *xfer)
                    KERNADDR(&dpipe->req_dma, 0),
                    (long long)DMAADDR(&dpipe->req_dma, 0),
                    len, dir == UE_DIR_IN ? "in" : "out");
-       } else {
-               DPRINTFN(3, "xfer=%p len=%d flags=%d addr=%d endpt=%d,"
-                   " mps=%d dir %s\n", xfer, xfer->length, xfer->flags, addr,
+       } 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");
 
+#ifdef DIAGNOSTIC
+               len = 0;
+               for (size_t i = 0; i < xfer->nframes; i++)
+                       len += xfer->frlengths[i];
+               if (len != xfer->length)
+                       panic("len (%d) != xfer->length (%d)", len,
+                           xfer->length);
+#endif
+               len = xfer->length;
+        } 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");
+
                len = xfer->length;
        }
 
@@ -1235,11 +1227,15 @@ dwc2_device_start(struct usbd_xfer *xfer)
        if (!dwc2_urb)
                return USBD_NOMEM;
 
+//     KASSERT(dwc2_urb->packet_count == xfer->nframes);
        memset(dwc2_urb, 0, sizeof(*dwc2_urb) +
            sizeof(dwc2_urb->iso_descs[0]) * DWC2_MAXISOCPACKETS);
 
+       dwc2_urb->priv = xfer;
+       dwc2_urb->packet_count = xfer->nframes;
+
        dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, addr, epnum, xfertype, dir,
-                                 mps);
+           mps);
 
        if (xfertype == UE_CONTROL) {
                dwc2_urb->setup_usbdma = &dpipe->req_dma;
@@ -1248,7 +1244,7 @@ dwc2_device_start(struct usbd_xfer *xfer)
        } else {
                /* XXXNH - % mps required? */
                if ((xfer->flags & USBD_FORCE_SHORT_XFER) && (len % mps) == 0)
-                       flags |= URB_SEND_ZERO_PACKET;
+                   flags |= URB_SEND_ZERO_PACKET;
        }
        flags |= URB_GIVEBACK_ASAP;
 
@@ -1260,14 +1256,14 @@ dwc2_device_start(struct usbd_xfer *xfer)
                dwc2_urb->usbdma = &xfer->dmabuf;
                dwc2_urb->buf = KERNADDR(dwc2_urb->usbdma, 0);
                dwc2_urb->dma = DMAADDR(dwc2_urb->usbdma, 0);
-               usb_syncmem(&xfer->dmabuf, 0, xfer->length,
-                   usbd_xfer_isread(xfer) ?
-                   BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+               usb_syncmem(&xfer->dmabuf, 0, len,
+                   dir == UE_DIR_IN ?
+                       BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
        }
        dwc2_urb->length = len;
        dwc2_urb->flags = flags;
        dwc2_urb->status = -EINPROGRESS;
-       dwc2_urb->packet_count = xfer->nframes;
 
        if (xfertype == UE_INTERRUPT ||
            xfertype == UE_ISOCHRONOUS) {
@@ -1302,15 +1298,19 @@ dwc2_device_start(struct usbd_xfer *xfer)
                dwc2_urb->interval = ival;
        }
 
+       /* XXXNH bring down from callers?? */
+//     mtx_enter(&sc->sc_lock);
+
        xfer->actlen = 0;
 
        KASSERT(xfertype != UE_ISOCHRONOUS ||
-           xfer->nframes < DWC2_MAXISOCPACKETS);
+           xfer->nframes <= DWC2_MAXISOCPACKETS);
        KASSERTMSG(xfer->nframes == 0 || xfertype == UE_ISOCHRONOUS,
            "nframes %d xfertype %d\n", xfer->nframes, xfertype);
 
-       for (off = i = 0; i < xfer->nframes; ++i) {
-               DPRINTFN(3, "xfer=%p frame=%d offset=%d length=%d\n", xfer, i,
+       off = 0;
+       for (size_t i = 0; i < xfer->nframes; ++i) {
+               DPRINTFN(3, "xfer=%p frame=%zu offset=%d length=%d\n", xfer, i,
                    off, xfer->frlengths[i]);
 
                dwc2_hcd_urb_set_iso_desc_params(dwc2_urb, i, off,
@@ -1318,18 +1318,38 @@ dwc2_device_start(struct usbd_xfer *xfer)
                off += xfer->frlengths[i];
        }
 
-       /* might need to check cpu_intr_p */
-       mtx_enter(&hsotg->lock);
+       struct dwc2_qh *qh = dpipe->priv;
+       struct dwc2_qtd *qtd;
+       bool qh_allocated = false;
 
-       if (xfer->timeout && !sc->sc_bus.use_polling) {
-               timeout_reset(&xfer->timeout_handle, mstohz(xfer->timeout),
-                   dwc2_timeout, xfer);
+       /* Create QH for the endpoint if it doesn't exist */
+       if (!qh) {
+               qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, M_NOWAIT);
+               if (!qh) {
+                       retval = -ENOMEM;
+                       goto fail;
+               }
+               dpipe->priv = qh;
+               qh_allocated = true;
        }
 
-       dwc2_urb->priv = xfer;
-       retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &dpipe->priv, 0);
+       qtd = pool_get(&sc->sc_qtdpool, PR_NOWAIT);
+       if (!qtd) {
+               retval = -ENOMEM;
+               goto fail1;
+       }
+       memset(qtd, 0, sizeof(*qtd));
+
+       /* might need to check cpu_intr_p */
+       mtx_enter(&hsotg->lock);
+       retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, qtd);
        if (retval)
-               goto fail;
+               goto fail2;
+       if (xfer->timeout && !sc->sc_bus.use_polling) {
+               timeout_set(&xfer->timeout_handle, dwc2_timeout, xfer);
+               timeout_add_msec(&xfer->timeout_handle, xfer->timeout);
+       }
+       xfer->status = USBD_IN_PROGRESS;
 
        if (alloc_bandwidth) {
                dwc2_allocate_bus_bandwidth(hsotg,
@@ -1337,12 +1357,25 @@ dwc2_device_start(struct usbd_xfer *xfer)
                                xfer);
        }
 
-fail:
        mtx_leave(&hsotg->lock);
+//     mtx_exit(&sc->sc_lock);
+
+       return USBD_IN_PROGRESS;
+
+fail2:
+       dwc2_urb->priv = NULL;
+       mtx_leave(&hsotg->lock);
+       pool_put(&sc->sc_qtdpool, qtd);
+
+fail1:
+       if (qh_allocated) {
+               dpipe->priv = NULL;
+               dwc2_hcd_qh_free(hsotg, qh);
+       }
+fail:
 
        switch (retval) {
-       case 0:
-               break;
+       case -EINVAL:
        case -ENODEV:
                err = USBD_INVAL;
                break;
@@ -1357,51 +1390,19 @@ fail:
 
 }
 
-void
-dwc2_worker(struct task *wk, void *priv)
-{
-       struct dwc2_softc *sc = priv;
-       struct dwc2_hsotg *hsotg = sc->sc_hsotg;
-
-/* db_enter(); */
-#if 0
-       struct usbd_xfer *xfer = dwork->xfer;
-       struct dwc2_xfer *dxfer = DWC2_XFER2DXFER(xfer);
-
-       dwc2_hcd_endpoint_disable(sc->dwc_dev.hcd, dpipe->priv, 250);
-       dwc_free(NULL, dpipe->urb);
-#endif
-
-       if (wk == &hsotg->wf_otg) {
-               dwc2_conn_id_status_change(wk);
-       } else if (wk == &hsotg->start_work.work) {
-               dwc2_hcd_start_func(wk);
-       } else if (wk == &hsotg->reset_work.work) {
-               dwc2_hcd_reset_func(wk);
-       } else {
-#if 0
-               KASSERT(dwork->xfer != NULL);
-               KASSERT(dxfer->queued == true);
-
-               if (!(dxfer->flags & DWC2_XFER_ABORTING)) {
-                       dwc2_start_standard_chain(xfer);
-               }
-               dxfer->queued = false;
-               wakeup(&dxfer->flags);
-#endif
-       }
-}
-
-int
-dwc2_intr(void *p)
+int dwc2_intr(void *p)
 {
        struct dwc2_softc *sc = p;
-       struct dwc2_hsotg *hsotg = sc->sc_hsotg;
+       struct dwc2_hsotg *hsotg;
        int ret = 0;
 
+       if (sc == NULL)
+               return 0;
+
+       hsotg = sc->sc_hsotg;
        mtx_enter(&hsotg->lock);
 
-       if (sc->sc_dying)
+       if (sc->sc_bus.dying)
                goto done;
 
        if (sc->sc_bus.use_polling) {
@@ -1457,15 +1458,12 @@ dwc2_init(struct dwc2_softc *sc)
        sc->sc_bus.pipe_size = sizeof(struct dwc2_pipe);
        sc->sc_hcdenabled = false;
 
+       mtx_init(&sc->sc_lock, IPL_SOFTUSB);
+
        TAILQ_INIT(&sc->sc_complete);
 
        sc->sc_rhc_si = softintr_establish(IPL_SOFTUSB, dwc2_rhc, sc);
 
-#if 0
-       usb_setup_reserve(&sc->sc_bus, &sc->sc_dma_reserve, sc->sc_bus.dmatag,
-           USB_MEM_RESERVE);
-#endif
-
        pool_init(&sc->sc_xferpool, sizeof(struct dwc2_xfer), 0, IPL_USB, 0,
            "dwc2xfer", NULL);
        pool_init(&sc->sc_qhpool, sizeof(struct dwc2_qh), 0, IPL_USB, 0,
@@ -1475,33 +1473,86 @@ dwc2_init(struct dwc2_softc *sc)
 
        sc->sc_hsotg = malloc(sizeof(struct dwc2_hsotg), M_DEVBUF,
            M_ZERO | M_WAITOK);
-
        sc->sc_hsotg->hsotg_sc = sc;
        sc->sc_hsotg->dev = &sc->sc_bus.bdev;
        sc->sc_hcdenabled = true;
 
-       err = dwc2_hcd_init(sc->sc_hsotg, sc->sc_params);
-       if (err) {
-               err = -err;
-               goto fail;
+       struct dwc2_hsotg *hsotg = sc->sc_hsotg;
+       struct dwc2_core_params defparams;
+       int retval;
+
+       if (sc->sc_params == NULL) {
+               /* Default all params to autodetect */
+               dwc2_set_all_params(&defparams, -1);
+               sc->sc_params = &defparams;
+
+               /*
+                * Disable descriptor dma mode by default as the HW can support
+                * it, but does not support it for SPLIT transactions.
+                */
+               defparams.dma_desc_enable = 0;
        }
+       hsotg->dr_mode = USB_DR_MODE_HOST;
+
+       /*
+        * 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);
+
+       /* Detect config values from hardware */
+       retval = dwc2_get_hwparams(hsotg);
+       if (retval) {
+               goto fail2;
+       }
+
+       hsotg->core_params = malloc(sizeof(*hsotg->core_params), M_DEVBUF,
+           M_ZERO | M_WAITOK);
+       dwc2_set_all_params(hsotg->core_params, -1);
+
+       /* Validate parameter values */
+       dwc2_set_parameters(hsotg, sc->sc_params);
+
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+    IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+       if (hsotg->dr_mode != USB_DR_MODE_HOST) {
+               retval = dwc2_gadget_init(hsotg);
+               if (retval)
+                       goto fail2;
+               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) {
+                       if (hsotg->gadget_enabled)
+                               dwc2_hsotg_remove(hsotg);
+                       goto fail2;
+               }
+           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
 
        return 0;
 
-fail:
+fail2:
+       err = -retval;
        free(sc->sc_hsotg, M_DEVBUF, sizeof(struct dwc2_hsotg));
        softintr_disestablish(sc->sc_rhc_si);
 
        return err;
 }
 
-int
-dwc2_dma_config(struct dwc2_softc *sc, struct dwc2_core_dma_config *config)
-{
-       sc->sc_dma_config = config;
-       return dwc2_hcd_dma_config(sc->sc_hsotg, sc->sc_dma_config);
-}
-
 #if 0
 /*
  * curmode is a mode indication bit 0 = device, 1 = host
@@ -1539,10 +1590,8 @@ void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr,
        struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
        struct usbd_device *dev = dpipe->pipe.device;
 
-       if (dev->myhsport != NULL) {
-               *hub_addr = dev->myhsport->parent->address;
-               *hub_port = dev->myhsport->portno;
-       }
+       *hub_addr = dev->myhsport->parent->address;
+       *hub_port = dev->myhsport->portno;
 }
 
 int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
@@ -1561,7 +1610,7 @@ int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
  * Must be called with interrupt disabled and spinlock held
  */
 void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
-                       int status)
+    int status)
 {
        struct usbd_xfer *xfer;
        struct dwc2_xfer *dxfer;
@@ -1590,28 +1639,28 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
        ed = xfer->pipe->endpoint->edesc;
        xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
 
-       xfer->actlen = dwc2_hcd_urb_get_actual_length(qtd->urb);
+       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 && dbg_perio()) {
-               int i;
-
-               for (i = 0; i < xfer->nframes; i++)
-                       dev_vdbg(hsotg->dev, " ISO Desc %d status %d\n",
-                                i, qtd->urb->iso_descs[i].status);
-       }
-
        if (xfertype == UE_ISOCHRONOUS) {
-               int i;
-
                xfer->actlen = 0;
-               for (i = 0; i < xfer->nframes; ++i) {
+               for (size_t i = 0; i < xfer->nframes; ++i) {
                        xfer->frlengths[i] =
                                dwc2_hcd_urb_get_iso_desc_actual_length(
-                                               qtd->urb, i);
+                                               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) {
@@ -1622,27 +1671,41 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 
        switch (status) {
        case 0:
-               xfer->status = USBD_NORMAL_COMPLETION;
+               dxfer->intr_status = USBD_NORMAL_COMPLETION;
                break;
        case -EPIPE:
-               xfer->status = USBD_STALLED;
-               break;
-       case -ETIMEDOUT:
-               xfer->status = USBD_TIMEOUT;
+               dxfer->intr_status = USBD_STALLED;
                break;
        case -EPROTO:
-               xfer->status = USBD_INVAL;
+               dxfer->intr_status = USBD_INVAL;
                break;
        case -EIO:
-               xfer->status = USBD_IOERROR;
+               dxfer->intr_status = USBD_IOERROR;
                break;
        case -EOVERFLOW:
-               xfer->status = USBD_IOERROR;
+               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 &&
+                   xfer->length == 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);
@@ -1652,21 +1715,9 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
                                        xfer);
        }
 
-       if (xfer->status == USBD_NORMAL_COMPLETION) {
-               if (xfertype == UE_ISOCHRONOUS)
-                       usb_syncmem(&xfer->dmabuf, 0, xfer->length,
-                           usbd_xfer_isread(xfer) ?
-                           BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-               else if (xfer->actlen)
-                       usb_syncmem(&xfer->dmabuf, 0, xfer->actlen,
-                           usbd_xfer_isread(xfer) ?
-                           BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-       }
-
        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);
@@ -1684,15 +1735,14 @@ _dwc2_hcd_start(struct dwc2_hsotg *hsotg)
 
        mtx_enter(&hsotg->lock);
 
-       hsotg->op_state = OTG_STATE_A_HOST;
-
-       dwc2_hcd_reinit(hsotg);
+       hsotg->lx_state = DWC2_L0;
 
-       /* Try to enable port. */
-       dwc2_handle_hcd_intr(hsotg);
+       if (dwc2_is_device_mode(hsotg)) {
+               mtx_leave(&hsotg->lock);
+               return 0;       /* why 0 ?? */
+       }
 
-       /*XXXNH*/
-       delay(50);
+       dwc2_hcd_reinit(hsotg);
 
        mtx_leave(&hsotg->lock);
        return 0;
index a25bfc8..2f14470 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2.h,v 1.14 2017/02/15 14:49:13 visa Exp $  */
+/*     $OpenBSD: dwc2.h,v 1.15 2021/07/22 18:32:33 mglocker Exp $      */
 /*     $NetBSD: dwc2.h,v 1.4 2014/12/23 16:20:06 macallan Exp $        */
 
 /*-
 #include <sys/kernel.h>
 
 #include <sys/task.h>
-#include <sys/timeout.h>
 
 #include <lib/libkern/libkern.h>
 
-#if 0
-#include "opt_usb.h"
-#endif
-
-#define        STATIC_INLINE           static inline
-#define        STATIC
+#define STATIC
 
 // #define VERBOSE_DEBUG
 // #define DWC2_DUMP_FRREM
 // #define CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 
+#define CONFIG_USB_DWC2_HOST           1
+#define CONFIG_USB_DWC2_DUAL_ROLE      0
+#define CONFIG_USB_DWC2_PERIPHERAL     0
+
 typedef int irqreturn_t;
 #define        IRQ_NONE 0
 #define IRQ_HANDLED 1
@@ -64,6 +62,11 @@ 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))
@@ -101,13 +104,6 @@ extern int dwc2debug;
 #define        dev_vdbg(...) do { } while (0)
 #endif
 
-#define jiffies                        hardclock_ticks
-#define msecs_to_jiffies       mstohz
-
-#define gfp_t          int
-#define GFP_KERNEL      M_WAITOK
-#define GFP_ATOMIC      M_NOWAIT
-
 enum usb_otg_state {
        OTG_STATE_RESERVED = 0,
 
@@ -122,8 +118,8 @@ enum usb_otg_state {
 
 #define spinlock_t             struct mutex
 #define spin_lock_init(lock)   mtx_init(lock, IPL_USB)
-#define        spin_lock(l)            do { mtx_enter(l); } while (0)
-#define        spin_unlock(l)          do { mtx_leave(l); } while (0)
+#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)
@@ -193,19 +189,76 @@ enum usb_otg_state {
 #define        USB_PORT_STAT_C_RESET           UPS_C_PORT_RESET
 #define        USB_PORT_STAT_C_L1              UPS_C_PORT_L1
 
-STATIC_INLINE void
+#define        USB_DT_HUB                      UDESC_HUB
+
+/* See USB 2.0 spec Table 11-13, offset 3 */
+#define HUB_CHAR_LPSM          UHD_PWR
+#define HUB_CHAR_COMMON_LPSM   UHD_PWR_GANGED
+#define HUB_CHAR_INDV_PORT_LPSM        UHD_PWR_INDIVIDUAL
+#define HUB_CHAR_NO_LPSM       UHD_PWR_NO_SWITCH
+
+#define HUB_CHAR_COMPOUND      UHD_COMPOUND
+
+#define HUB_CHAR_OCPM          UHD_OC
+#define HUB_CHAR_COMMON_OCPM   UHD_OC_GLOBAL
+#define HUB_CHAR_INDV_PORT_OCPM        UHD_OC_INDIVIDUAL
+#define HUB_CHAR_NO_OCPM       UHD_OC_NONE
+
+#define HUB_CHAR_TTTT          UHD_TT_THINK
+#define HUB_CHAR_PORTIND       UHD_PORT_IND
+
+enum usb_dr_mode {
+       USB_DR_MODE_UNKNOWN,
+       USB_DR_MODE_HOST,
+       USB_DR_MODE_PERIPHERAL,
+       USB_DR_MODE_OTG,
+};
+
+struct usb_phy;
+struct usb_hcd;
+
+static inline int
+usb_phy_set_suspend(struct usb_phy *x, int suspend)
+{
+
+       return 0;
+}
+
+static inline void
+usb_hcd_resume_root_hub(struct usb_hcd *hcd)
+{
+
+       return;
+}
+
+static inline int
+usb_disabled(void)
+{
+
+       return 0;
+}
+
+static inline void
 udelay(unsigned long usecs)
 {
+
        DELAY(usecs);
 }
 
+static inline void
+ndelay(unsigned long nsecs)
+{
+
+       DELAY(nsecs / 1000);
+}
+
 #define        EREMOTEIO       EIO
 #define        ECOMM           EIO
+#define        ENOTSUPP        ENOTSUP
 
 #define NS_TO_US(ns)   ((ns + 500L) / 1000L)
 
 void dw_timeout(void *);
-void dwc2_worker(struct task *, void *);
 
 struct delayed_work {
        struct task work;
@@ -216,7 +269,7 @@ struct delayed_work {
        void *dw_arg;
 };
 
-STATIC_INLINE void
+static inline void
 INIT_DELAYED_WORK(struct delayed_work *dw, void (*fn)(void *), void *arg)
 {
        dw->dw_fn = fn;
@@ -224,11 +277,13 @@ INIT_DELAYED_WORK(struct delayed_work *dw, void (*fn)(void *), void *arg)
        timeout_set(&dw->dw_timer, dw_timeout, dw);
 }
 
-STATIC_INLINE void
+static inline void
 queue_delayed_work(struct taskq *wq, struct delayed_work *dw, int j)
 {
        dw->dw_wq = wq;
        timeout_add(&dw->dw_timer, j);
 }
 
+#define USB_RESUME_TIMEOUT     40 /* ms */
+
 #endif
index 04cbc87..08fcd46 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_core.c,v 1.9 2017/09/08 05:36:52 deraadt Exp $   */
+/*     $OpenBSD: dwc2_core.c,v 1.10 2021/07/22 18:32:33 mglocker Exp $ */
 /*     $NetBSD: dwc2_core.c,v 1.6 2014/04/03 06:34:58 skrll Exp $      */
 
 /*
 #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.
+ * 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_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));
+
+       hr->hprt0 = DWC2_READ_4(hsotg, HPRT0);
+       hr->hfir = DWC2_READ_4(hsotg, HFIR);
+       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_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]);
+
+       DWC2_WRITE_4(hsotg, HPRT0, hr->hprt0);
+       DWC2_WRITE_4(hsotg, HFIR, hr->hfir);
+       hsotg->frame_number = 0;
+
+       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.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+STATIC int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
+{
+       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));
+
+               /* Backup OUT EPs */
+               dr->doepctl[i] = DWC2_READ_4(hsotg, DOEPCTL(i));
+
+               /* Ensure DATA PID is correctly configured */
+               if (dr->doepctl[i] & DXEPCTL_DPID)
+                       dr->doepctl[i] |= DXEPCTL_SETD1PID;
+               else
+                       dr->doepctl[i] |= DXEPCTL_SETD0PID;
+
+               dr->doeptsiz[i] = DWC2_READ_4(hsotg, DOEPTSIZ(i));
+               dr->doepdma[i] = DWC2_READ_4(hsotg, DOEPDMA(i));
+       }
+       dr->valid = true;
+       return 0;
+}
+
+/**
+ * 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 the DWC_otg controller
+ */
+STATIC int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_dregs_backup *dr;
+       u32 dctl;
+       int i;
+
+       dev_dbg(hsotg->dev, "%s\n", __func__);
+
+       /* 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;
+
+       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);
+
+       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]);
+
+               /* 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]);
+       }
+
+       /* Set the Power-On Programming done bit */
+       dctl = DWC2_READ_4(hsotg, DCTL);
+       dctl |= DCTL_PWRONPRGDONE;
+       DWC2_WRITE_4(hsotg, DCTL, dctl);
+
+       return 0;
+}
+#else
+static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+
+static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+#endif
+
+/**
+ * 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;
+
+       /* Backup global regs */
+       gr = &hsotg->gr_backup;
+
+       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));
+
+       gr->valid = true;
+       return 0;
+}
+
+/**
+ * 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_global_registers(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_gregs_backup *gr;
+       int i;
+
+       dev_dbg(hsotg->dev, "%s\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;
+       }
+       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
+ */
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
+{
+       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;
+               }
+               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;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * dwc2_enter_hibernation() - Put controller in Partial Power Down.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
+{
+       u32 pcgcctl;
+       int ret = 0;
+
+       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_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;
+               }
+       }
+
+       /*
+        * Clear any pending interrupts since dwc2 will not be able to
+        * clear them after entering hibernation.
+        */
+       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);
+
+       pcgcctl |= PCGCTL_STOPPCLK;
+       DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
+
+       return ret;
+}
+
 /**
  * dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
  * used in both device and host modes
@@ -86,8 +451,10 @@ STATIC void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
 
        if (hsotg->core_params->dma_enable <= 0)
                intmsk |= GINTSTS_RXFLVL;
+       if (hsotg->core_params->external_id_pin_ctl <= 0)
+               intmsk |= GINTSTS_CONIDSTSCHNG;
 
-       intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP |
+       intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
                  GINTSTS_SESSREQINT;
 
        DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
@@ -123,46 +490,168 @@ STATIC void dwc2_init_fs_ls_pclk_sel(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.
  */
-STATIC int dwc2_core_reset(struct dwc2_hsotg *hsotg)
+int dwc2_core_reset(struct dwc2_hsotg *hsotg)
 {
        u32 greset;
        int count = 0;
 
        dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
-       /* Wait for AHB master IDLE state */
+       /* Core Soft Reset */
+       greset = DWC2_READ_4(hsotg, GRSTCTL);
+       greset |= GRSTCTL_CSFTRST;
+       DWC2_WRITE_4(hsotg, GRSTCTL, greset);
        do {
-               usleep_range(20000, 40000);
+               udelay(1);
                greset = DWC2_READ_4(hsotg, GRSTCTL);
                if (++count > 50) {
                        dev_warn(hsotg->dev,
-                                "%s() HANG! AHB Idle GRSTCTL=%0x\n",
+                                "%s() HANG! Soft Reset GRSTCTL=%0x\n",
                                 __func__, greset);
                        return -EBUSY;
                }
-       } while (!(greset & GRSTCTL_AHBIDLE));
+       } while (greset & GRSTCTL_CSFTRST);
 
-       /* Core Soft Reset */
+       /* Wait for AHB master IDLE state */
        count = 0;
-       greset |= GRSTCTL_CSFTRST;
-       DWC2_WRITE_4(hsotg, GRSTCTL, greset);
        do {
-               usleep_range(20000, 40000);
+               udelay(1);
                greset = DWC2_READ_4(hsotg, GRSTCTL);
                if (++count > 50) {
                        dev_warn(hsotg->dev,
-                                "%s() HANG! Soft Reset GRSTCTL=%0x\n",
+                                "%s() HANG! AHB Idle GRSTCTL=%0x\n",
                                 __func__, greset);
                        return -EBUSY;
                }
-       } while (greset & GRSTCTL_CSFTRST);
+       } while (!(greset & GRSTCTL_AHBIDLE));
+
+       return 0;
+}
+
+/*
+ * 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.
+ *
+ * 2) During probe we want to read reset values of the hw
+ * configuration registers that are only available in either host or
+ * device mode. We may need to force the mode if the current mode does
+ * not allow us to access the register in the mode that we want.
+ *
+ * In either case it only makes sense to force the mode if the
+ * controller hardware is OTG capable.
+ *
+ * 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.
+ *
+ * Returns true if the mode was forced.
+ */
+STATIC bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+{
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+       u32 gusbcfg;
+       u32 set;
+       u32 clear;
+
+       dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
+
+       /*
+        * Force mode has no effect if the hardware is not OTG.
+        */
+       if (!dwc2_hw_is_otg(hsotg))
+               return false;
+
+       /*
+        * 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 (!host && hsotg->dr_mode == USB_DR_MODE_HOST) {
+               WARN_ON(1);
+               return false;
+       }
+
+       gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+
+       set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
+       clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
+
+       gusbcfg &= ~clear;
+       gusbcfg |= set;
+       DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
+
+       usb_delay_ms(&sc->sc_bus, 25);
+       return true;
+}
+
+/*
+ * Clears the force mode bits.
+ */
+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);
+       gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+       gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+       DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
 
        /*
         * NOTE: This long sleep is _very_ important, otherwise the core will
         * not stay in host mode after a connector ID change!
         */
-       usleep_range(150000, 200000);
+       usb_delay_ms(&sc->sc_bus, 25);
+}
 
+/*
+ * Sets or clears force mode based on the dr_mode parameter.
+ */
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+       switch (hsotg->dr_mode) {
+       case USB_DR_MODE_HOST:
+               dwc2_force_mode(hsotg, true);
+               break;
+       case USB_DR_MODE_PERIPHERAL:
+               dwc2_force_mode(hsotg, false);
+               break;
+       case USB_DR_MODE_OTG:
+               dwc2_clear_force_mode(hsotg);
+               break;
+       default:
+               dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
+                        __func__, hsotg->dr_mode);
+               break;
+       }
+}
+
+/*
+ * 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.
+ */
+int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+       int retval;
+
+       retval = dwc2_core_reset(hsotg);
+       if (retval)
+               return retval;
+
+       dwc2_force_dr_mode(hsotg);
        return 0;
 }
 
@@ -177,16 +666,20 @@ STATIC int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
         */
        if (select_phy) {
                dev_dbg(hsotg->dev, "FS PHY selected\n");
+
                usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-               usbcfg |= GUSBCFG_PHYSEL;
-               DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
+               if (!(usbcfg & GUSBCFG_PHYSEL)) {
+                       usbcfg |= GUSBCFG_PHYSEL;
+                       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
 
-               /* Reset after a PHY select */
-               retval = dwc2_core_reset(hsotg);
-               if (retval) {
-                       dev_err(hsotg->dev, "%s() Reset failed, aborting",
-                                       __func__);
-                       return retval;
+                       /* 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;
+                       }
                }
        }
 
@@ -221,13 +714,13 @@ STATIC int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 
 STATIC int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 {
-       u32 usbcfg;
+       u32 usbcfg, usbcfg_old;
        int retval = 0;
 
        if (!select_phy)
                return 0;
 
-       usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+       usbcfg = usbcfg_old = DWC2_READ_4(hsotg, GUSBCFG);
 
        /*
         * HS PHY parameters. These parameters are preserved during soft reset
@@ -255,14 +748,16 @@ STATIC int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
                break;
        }
 
-       DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
+       if (usbcfg != usbcfg_old) {
+               DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
 
-       /* Reset after setting the PHY parameters */
-       retval = dwc2_core_reset(hsotg);
-       if (retval) {
-               dev_err(hsotg->dev, "%s() Reset failed, aborting",
-                               __func__);
-               return retval;
+               /* 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;
@@ -306,11 +801,16 @@ STATIC int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 
 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_err(hsotg->dev, "External DMA Mode\n");
+               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 &
@@ -394,11 +894,10 @@ STATIC void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
  * 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
- * @select_phy: If true then also set the Phy type
- * @irq:        If >= 0, the irq to register
+ * @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 select_phy)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
 {
        u32 usbcfg, otgctl;
        int retval;
@@ -420,18 +919,26 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy)
 
        DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
 
-       /* Reset the Controller */
-       retval = dwc2_core_reset(hsotg);
-       if (retval) {
-               dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
-                               __func__);
-               return retval;
+       /*
+        * 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, select_phy);
+       retval = dwc2_phy_init(hsotg, initial_setup);
        if (retval)
                return retval;
 
@@ -458,7 +965,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy)
        dwc2_enable_common_interrupts(hsotg);
 
        /*
-        * Do device or host intialization based on mode during PCD and
+        * Do device or host initialization based on mode during PCD and
         * HCD initialization
         */
        if (dwc2_is_host_mode(hsotg)) {
@@ -507,10 +1014,76 @@ void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
 
        /* Disable host mode interrupts without disturbing common interrupts */
        intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT |
-                   GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP);
+                   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;
@@ -519,6 +1092,8 @@ STATIC void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
        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);
@@ -526,7 +1101,8 @@ STATIC void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
        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));
+       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",
@@ -1267,6 +1843,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
        u32 hcchar;
        u32 hctsiz = 0;
        u16 num_packets;
+       u32 ec_mc;
 
        if (dbg_hc(chan))
                dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -1303,6 +1880,13 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 
                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");
@@ -1365,6 +1949,9 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 
                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;
@@ -1399,9 +1986,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
                } else {
                        dma_addr = chan->xfer_dma;
                }
-               struct dwc2_core_dma_config *dma_config =
-                   hsotg->core_dma_config;
-               if (dma_config == NULL) {
+               if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
                        DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num),
                            (u32)dma_addr);
                        if (dbg_hc(chan))
@@ -1410,9 +1995,8 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
                                     (unsigned long)dma_addr,
                                    chan->hc_num);
                } else {
-                       (void)(*dma_config->set_dma_addr)(
-                           dma_config->set_dma_addr_data, dma_addr,
-                           chan->hc_num);
+                       (void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
+                           hsotg->dev, dma_addr, chan->hc_num);
                }
        }
 
@@ -1426,8 +2010,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 
        hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
        hcchar &= ~HCCHAR_MULTICNT_MASK;
-       hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
-                 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)
@@ -1476,7 +2059,6 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
                                 struct dwc2_host_chan *chan)
 {
        u32 hcchar;
-       u32 hc_dma;
        u32 hctsiz = 0;
 
        if (chan->do_ping)
@@ -1505,14 +2087,21 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
 
        DWC2_WRITE_4(hsotg, HCTSIZ(chan->hc_num), hctsiz);
 
-       hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK;
+       usb_syncmem(&chan->desc_list_usbdma, 0, chan->desc_list_sz,
+           BUS_DMASYNC_PREWRITE);
 
-       /* Always start from first descriptor */
-       hc_dma &= ~HCDMA_CTD_MASK;
-       DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num), hc_dma);
-       if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n",
-                        hc_dma, chan->hc_num);
+       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;
@@ -1695,10 +2284,10 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
 
        if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED)
                /* High speed case */
-               return 125 * clock - 1;
+               return 125 * clock;
        else
                /* FS/LS case */
-               return 1000 * clock - 1;
+               return 1000 * clock;
 }
 
 /**
@@ -2055,6 +2644,29 @@ void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int 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)
 {
@@ -2540,6 +3152,40 @@ STATIC void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
        hsotg->core_params->uframe_sched = val;
 }
 
+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);
+       }
+
+       hsotg->core_params->external_id_pin_ctl = val;
+}
+
+STATIC void dwc2_set_param_hibernation(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 hibernation\n",
+                               val);
+                       dev_err(hsotg->dev, "hibernation must be 0 or 1\n");
+               }
+               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.
@@ -2552,6 +3198,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
        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,
@@ -2584,6 +3231,81 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
        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);
+}
+
+/*
+ * 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;
+       bool forced;
+
+       if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+               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);
+
+       if (forced)
+               dwc2_clear_force_mode(hsotg);
+
+       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;
+       bool forced;
+       u32 gnptxfsiz;
+
+       if (hsotg->dr_mode == USB_DR_MODE_HOST)
+               return;
+
+       forced = dwc2_force_mode_if_needed(hsotg, false);
+
+       gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
+       dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+
+       if (forced)
+               dwc2_clear_force_mode(hsotg);
+
+       hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                      FIFOSIZE_DEPTH_SHIFT;
 }
 
 /**
@@ -2594,9 +3316,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_hw_params *hw = &hsotg->hw_params;
        unsigned width;
-       u32 hwcfg2, hwcfg3, hwcfg4;
-       u32 hptxfsiz, grxfsiz, gnptxfsiz;
-       u32 gusbcfg;
+       u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
+       u32 grxfsiz;
 
        /*
         * Attempt to ensure this device is really a DWC_otg Controller.
@@ -2616,31 +3337,28 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
                hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
                hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
 
+       hwcfg1 = DWC2_READ_4(hsotg, GHWCFG1);
        hwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
        hwcfg3 = DWC2_READ_4(hsotg, GHWCFG3);
        hwcfg4 = DWC2_READ_4(hsotg, GHWCFG4);
-       gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
        grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
 
-       dev_dbg(hsotg->dev, "hwcfg1=%08x\n", DWC2_READ_4(hsotg, GHWCFG1));
+       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, "gnptxfsiz=%08x\n", gnptxfsiz);
        dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
 
-       /* Force host mode to get HPTXFSIZ exact power on value */
-       gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-       gusbcfg |= GUSBCFG_FORCEHOSTMODE;
-       DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
-       usleep_range(100000, 150000);
+       /*
+        * 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);
 
-       hptxfsiz = DWC2_READ_4(hsotg, HPTXFSIZ);
-       dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
-       gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
-       gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
-       DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
-       usleep_range(100000, 150000);
+       /* hwcfg1 */
+       hw->dev_ep_dirs = hwcfg1;
 
        /* hwcfg2 */
        hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
@@ -2670,6 +3388,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
        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;
@@ -2689,10 +3414,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
        /* fifo sizes */
        hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
                                GRXFSIZ_DEPTH_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;
 
        dev_dbg(hsotg->dev, "Detected values from hardware:\n");
        dev_dbg(hsotg->dev, "  op_mode=%d\n",
@@ -2709,7 +3430,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
                hw->hs_phy_type);
        dev_dbg(hsotg->dev, "  fs_phy_type=%d\n",
                hw->fs_phy_type);
-       dev_dbg(hsotg->dev, "  utmi_phy_data_wdith=%d\n",
+       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);
@@ -2744,6 +3465,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
        return 0;
 }
 
+/*
+ * 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;
+
+       for (i = 0; i < size; i++)
+               p[i] = value;
+}
+
+
 u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg)
 {
        return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103;
@@ -2784,3 +3521,40 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
        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)
+{
+       u32 ghwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
+
+       return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+               GHWCFG2_OP_MODE_SHIFT;
+}
+
+/* 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);
+
+       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);
+}
+
+/* Returns true if the controller is host-only. */
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
+{
+       unsigned op_mode = dwc2_op_mode(hsotg);
+
+       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
+}
+
+/* Returns true if the controller is device-only. */
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
+{
+       unsigned op_mode = dwc2_op_mode(hsotg);
+
+       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
+}
index c12e91a..0a66b8e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_core.h,v 1.9 2015/06/28 11:48:18 jmatthew Exp $  */
+/*     $OpenBSD: dwc2_core.h,v 1.10 2021/07/22 18:32:33 mglocker Exp $ */
 /*     $NetBSD: dwc2_core.h,v 1.5 2014/04/03 06:34:58 skrll Exp $      */
 
 /*
 
 #include <dev/usb/dwc2/dwc2_hw.h>
 
+#include <dev/usb/dwc2/list.h>
+
 /* Maximum number of Endpoints/HostChannels */
 #define MAX_EPS_CHANNELS       16
 
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+
+/* 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 */
+};
+
+/*
+ * EP0_MPS_LIMIT
+ *
+ * Unfortunately there seems to be a limit of the amount of data that can
+ * be transferred by IN transactions on EP0. This is either 127 bytes or 3
+ * packets (which practically means 1 packet and 63 bytes of data) when the
+ * MPS is set to 64.
+ *
+ * This means if we are wanting to move >127 bytes of data, we need to
+ * split the transactions up, but just doing one packet at a time does
+ * not work (this may be an implicit DATA0 PID on first packet of the
+ * transaction) and doing 2 packets is outside the controller's limits.
+ *
+ * If we try to lower the MPS size for EP0, then no transfers work properly
+ * for EP0, and the system will fail basic enumeration. As no cause for this
+ * has currently been found, we cannot support any large IN transfers for
+ * EP0.
+ */
+#define EP0_MPS_LIMIT   64
+
+struct dwc2_hsotg;
+struct dwc2_hsotg_req;
+
+/**
+ * struct dwc2_hsotg_ep - driver endpoint definition.
+ * @ep: The gadget layer representation of the endpoint.
+ * @name: The driver generated name for the endpoint.
+ * @queue: Queue of requests for this endpoint.
+ * @parent: Reference back to the parent device structure.
+ * @req: The current request that the endpoint is processing. This is
+ *       used to indicate an request has been loaded onto the endpoint
+ *       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.
+ * @index: The index for the endpoint registers.
+ * @mc: Multi Count - number of transactions per microframe
+ * @interval - Interval for periodic endpoints
+ * @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.
+ * @total_data: The total number of data bytes done.
+ * @fifo_size: The size of the FIFO (for periodic IN endpoints)
+ * @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
+ *
+ * This is the driver's state for each registered enpoint, 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.
+ *
+ * For periodic IN endpoints, we have fifo_size and fifo_load to try
+ * and keep track of the amount of data in the periodic FIFO for each
+ * of these as we don't have a status register that tells us how much
+ * is in each of them. (note, this may actually be useless information
+ * as in shared-fifo mode periodic in acts like a single-frame packet
+ * buffer than a fifo)
+ */
+struct dwc2_hsotg_ep {
+       struct usb_ep           ep;
+       struct list_head        queue;
+       struct dwc2_hsotg       *parent;
+       struct dwc2_hsotg_req    *req;
+       struct dentry           *debugfs;
+
+       unsigned long           total_data;
+       unsigned int            size_loaded;
+       unsigned int            last_load;
+       unsigned int            fifo_load;
+       unsigned short          fifo_size;
+       unsigned short          fifo_index;
+
+       unsigned char           dir_in;
+       unsigned char           index;
+       unsigned char           mc;
+       unsigned char           interval;
+
+       unsigned int            halted:1;
+       unsigned int            periodic:1;
+       unsigned int            isochronous:1;
+       unsigned int            send_zlp:1;
+       unsigned int            has_correct_parity:1;
+
+       char                    name[10];
+};
+
+/**
+ * struct dwc2_hsotg_req - data transfer request
+ * @req: The USB gadget request
+ * @queue: The list of requests for the endpoint this is queued for.
+ * @saved_req_buf: variable to save req.buf when bounce buffers are used.
+ */
+struct dwc2_hsotg_req {
+       struct usb_request      req;
+       struct list_head        queue;
+       void *saved_req_buf;
+};
+
+#define call_gadget(_hs, _entry) \
+do { \
+       if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
+               (_hs)->driver && (_hs)->driver->_entry) { \
+               spin_unlock(&_hs->lock); \
+               (_hs)->driver->_entry(&(_hs)->gadget); \
+               spin_lock(&_hs->lock); \
+       } \
+} while (0)
+#else
+#define call_gadget(_hs, _entry)       do {} while (0)
+#endif
+
 struct dwc2_hsotg;
 struct dwc2_host_chan;
 
@@ -65,6 +191,22 @@ 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,
+       DWC2_EP0_DATA_IN,
+       DWC2_EP0_DATA_OUT,
+       DWC2_EP0_STATUS_IN,
+       DWC2_EP0_STATUS_OUT,
+};
+
 /**
  * struct dwc2_core_params - Parameters for configuring the core
  *
@@ -87,6 +229,13 @@ enum dwc2_lx_state {
  *                      value for this if none is specified.
  *                       0 - Address DMA
  *                       1 - Descriptor DMA (default, if available)
+ * @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use
+ *                      address DMA mode or descriptor DMA mode for accessing
+ *                      the data FIFOs in Full Speed mode only. The driver
+ *                      will automatically detect the value for this if none is
+ *                      specified.
+ *                       0 - Address DMA
+ *                       1 - Descriptor DMA in FS (default, if available)
  * @speed:              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.
@@ -189,6 +338,17 @@ enum dwc2_lx_state {
  *                      by the driver and are ignored in this
  *                      configuration value.
  * @uframe_sched:       True to enable the microframe scheduler
+ * @external_id_pin_ctl: Specifies whether ID pin is handled externally.
+ *                      Disable CONIDSTSCHNG controller interrupt in such
+ *                      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
+ *                     needed.
+ *                     0 - No (default)
+ *                     1 - Yes
  *
  * The following parameters may be specified when starting the module. These
  * parameters define how the DWC_otg controller should be configured. A
@@ -205,6 +365,7 @@ struct dwc2_core_params {
        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;
@@ -226,6 +387,8 @@ struct dwc2_core_params {
        int reload_ctl;
        int ahbcfg;
        int uframe_sched;
+       int external_id_pin_ctl;
+       int hibernation;
 };
 
 /**
@@ -253,7 +416,7 @@ struct dwc2_core_params {
  * @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
- *                      avaialable
+ *                      available
  * @dev_token_q_depth   Device Mode IN Token Sequence Learning Queue
  *                      Depth
  *                       0 to 30
@@ -279,15 +442,18 @@ struct dwc2_core_params {
  *                       1 - 16 bits
  *                       2 - 8 or 16 bits
  * @snpsid:             Value from SNPSID register
+ * @dev_ep_dirs:        Direction of device endpoints (GHWCFG1)
  */
 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 host_nperio_tx_fifo_size:16;
+       unsigned dev_nperio_tx_fifo_size:16;
        unsigned host_perio_tx_fifo_size:16;
        unsigned nperio_tx_q_depth:3;
        unsigned host_perio_tx_q_depth:3;
@@ -304,28 +470,121 @@ struct dwc2_hw_params {
        unsigned power_optimized:1;
        unsigned utmi_phy_data_width:2;
        u32 snpsid;
+       u32 dev_ep_dirs;
+};
+
+/* 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
+ * @gotgctl:           Backup of GOTGCTL register
+ * @gintmsk:           Backup of GINTMSK register
+ * @gahbcfg:           Backup of GAHBCFG register
+ * @gusbcfg:           Backup of GUSBCFG register
+ * @grxfsiz:           Backup of GRXFSIZ register
+ * @gnptxfsiz:         Backup of GNPTXFSIZ register
+ * @gi2cctl:           Backup of GI2CCTL register
+ * @hptxfsiz:          Backup of HPTXFSIZ register
+ * @gdfifocfg:         Backup of GDFIFOCFG register
+ * @dtxfsiz:           Backup of DTXFSIZ registers for each endpoint
+ * @gpwrdn:            Backup of GPWRDN register
+ */
+struct dwc2_gregs_backup {
+       u32 gotgctl;
+       u32 gintmsk;
+       u32 gahbcfg;
+       u32 gusbcfg;
+       u32 grxfsiz;
+       u32 gnptxfsiz;
+       u32 gi2cctl;
+       u32 hptxfsiz;
+       u32 pcgcctl;
+       u32 gdfifocfg;
+       u32 dtxfsiz[MAX_EPS_CHANNELS];
+       u32 gpwrdn;
+       bool valid;
 };
 
-struct dwc2_core_dma_config {
-       int (*set_dma_addr)(void *, dma_addr_t, int);
-       void *set_dma_addr_data;
+/**
+ * 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
+ * @diepmsk:           Backup of DIEPMSK register
+ * @doepmsk:           Backup of DOEPMSK register
+ * @diepctl:           Backup of DIEPCTL register
+ * @dieptsiz:          Backup of DIEPTSIZ register
+ * @diepdma:           Backup of DIEPDMA register
+ * @doepctl:           Backup of DOEPCTL register
+ * @doeptsiz:          Backup of DOEPTSIZ register
+ * @doepdma:           Backup of DOEPDMA register
+ */
+struct dwc2_dregs_backup {
+       u32 dcfg;
+       u32 dctl;
+       u32 daintmsk;
+       u32 diepmsk;
+       u32 doepmsk;
+       u32 diepctl[MAX_EPS_CHANNELS];
+       u32 dieptsiz[MAX_EPS_CHANNELS];
+       u32 diepdma[MAX_EPS_CHANNELS];
+       u32 doepctl[MAX_EPS_CHANNELS];
+       u32 doeptsiz[MAX_EPS_CHANNELS];
+       u32 doepdma[MAX_EPS_CHANNELS];
+       bool valid;
 };
 
-TAILQ_HEAD(dwc2_qh_list, dwc2_qh);
+/**
+ * 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
+ * @hfir:              Backup of HFIR register
+ */
+struct dwc2_hregs_backup {
+       u32 hcfg;
+       u32 haintmsk;
+       u32 hcintmsk[MAX_EPS_CHANNELS];
+       u32 hprt0;
+       u32 hfir;
+       bool valid;
+};
 
 /**
  * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
  * and periodic schedules
  *
+ * These are common for both host and peripheral modes:
+ *
  * @dev:                The struct device pointer
  * @regs:              Pointer to controller regs
- * @core_params:        Parameters that define how the core should be configured
  * @hw_params:          Parameters that were autodetected from the
  *                      hardware registers
+ * @core_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
  *                      transitions
+ * @dr_mode:            Requested mode of operation, one of following:
+ *                      - 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.
+ * @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.
+ * @supplies:           Definition of USB power supplies
+ * @phyif:              PHY interface width
+ * @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
  *                      transfer are in process of being queued
  * @srp_success:        Stores status of SRP request in the case of a FS PHY
@@ -335,6 +594,12 @@ TAILQ_HEAD(dwc2_qh_list, dwc2_qh);
  *                      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
+ *
+ * These are for host mode:
+ *
  * @flags:              Flags for handling root port state changes
  * @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
  *                      Transfers associated with these QHs are not currently
@@ -403,11 +668,33 @@ TAILQ_HEAD(dwc2_qh_list, dwc2_qh);
  * @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
- * @lock:               Spinlock that protects all the driver data structures
- * @priv:               Stores a pointer to the struct usb_hcd
  * @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
+ *
+ * These are for peripheral mode:
+ *
+ * @driver:             USB gadget driver
+ * @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
+ * @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
  */
 struct dwc2_hsotg {
        struct device *dev;
@@ -416,8 +703,25 @@ struct dwc2_hsotg {
        struct dwc2_hw_params hw_params;
        /** Params to actually use */
        struct dwc2_core_params *core_params;
-       struct dwc2_core_dma_config *core_dma_config;
        enum usb_otg_state op_state;
+       enum usb_dr_mode dr_mode;
+       unsigned int hcd_enabled:1;
+       unsigned int gadget_enabled:1;
+       unsigned int ll_hw_enabled:1;
+
+       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;
+
+       int     irq;
+       struct clk *clk;
+#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
 
        unsigned int queuing_high_bandwidth:1;
        unsigned int srp_success:1;
@@ -426,7 +730,21 @@ struct dwc2_hsotg {
        struct task wf_otg;
        struct timeout wkp_timer;
        enum dwc2_lx_state lx_state;
+       struct dwc2_gregs_backup gr_backup;
+       struct dwc2_dregs_backup dr_backup;
+       struct dwc2_hregs_backup hr_backup;
+
+       struct dentry *debug_root;
+       struct debugfs_regset32 *regset;
+
+       /* DWC OTG HW Release versions */
+#define DWC2_CORE_REV_2_71a    0x4f54271a
+#define DWC2_CORE_REV_2_90a    0x4f54290a
+#define DWC2_CORE_REV_2_92a    0x4f54292a
+#define DWC2_CORE_REV_2_94a    0x4f54294a
+#define DWC2_CORE_REV_3_00a    0x4f54300a
 
+#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
        union dwc2_hcd_internal_flags {
                u32 d32;
                struct {
@@ -437,21 +755,24 @@ struct dwc2_hsotg {
                        unsigned port_suspend_change:1;
                        unsigned port_over_current_change:1;
                        unsigned port_l1_change:1;
-                       unsigned reserved:26;
+                       unsigned reserved:25;
                } b;
        } flags;
 
-       struct dwc2_qh_list non_periodic_sched_inactive;
-       struct dwc2_qh_list non_periodic_sched_active;
-       struct dwc2_qh *non_periodic_qh_ptr;
-       struct dwc2_qh_list periodic_sched_inactive;
-       struct dwc2_qh_list periodic_sched_ready;
-       struct dwc2_qh_list periodic_sched_assigned;
-       struct dwc2_qh_list periodic_sched_queued;
+       struct list_head non_periodic_sched_inactive;
+       struct list_head non_periodic_sched_waiting;
+       struct list_head non_periodic_sched_active;
+       struct list_head *non_periodic_qh_ptr;
+       struct list_head periodic_sched_inactive;
+       struct list_head periodic_sched_ready;
+       struct list_head periodic_sched_assigned;
+       struct list_head periodic_sched_queued;
        u16 periodic_usecs;
        u16 frame_usecs[8];
        u16 frame_number;
        u16 periodic_qh_count;
+       bool bus_suspended;
+       bool new_connection;
 
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 #define FRAME_NUM_ARRAY_SIZE 1000
@@ -462,7 +783,7 @@ struct dwc2_hsotg {
        int dumped_frame_num_array;
 #endif
 
-       LIST_HEAD(, dwc2_host_chan) free_hc_list;
+       struct list_head free_hc_list;
        int periodic_channels;
        int non_periodic_channels;
        int available_host_channels;
@@ -474,19 +795,13 @@ struct dwc2_hsotg {
 
        struct delayed_work start_work;
        struct delayed_work reset_work;
-       spinlock_t lock;
-       void *priv;
        u8 otg_port;
        struct usb_dma frame_list_usbdma;
        u32 *frame_list;
        dma_addr_t frame_list_dma;
-
-       /* DWC OTG HW Release versions */
-#define DWC2_CORE_REV_2_71a    0x4f54271a
-#define DWC2_CORE_REV_2_90a    0x4f54290a
-#define DWC2_CORE_REV_2_92a    0x4f54292a
-#define DWC2_CORE_REV_2_94a    0x4f54294a
-#define DWC2_CORE_REV_3_00a    0x4f54300a
+       u32 frame_list_sz;
+       struct kmem_cache *desc_gen_cache;
+       struct kmem_cache *desc_hsisoc_cache;
 
 #ifdef DEBUG
        u32 frrem_samples;
@@ -506,6 +821,33 @@ struct dwc2_hsotg {
        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)
+       /* Gadget structures */
+       struct usb_gadget_driver *driver;
+       int fifo_mem;
+       unsigned int dedicated_fifos:1;
+       unsigned char num_of_eps;
+       u32 fifo_map;
+
+       struct usb_request *ep0_reply;
+       struct usb_request *ctrl_req;
+       void *ep0_buff;
+       void *ctrl_buff;
+       enum dwc2_ep0_state ep0_state;
+       u8 test_mode;
+
+       struct usb_gadget gadget;
+       unsigned int enabled:1;
+       unsigned int connected: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 */
 };
 
 /* Reasons for halting a host channel */
@@ -530,7 +872,13 @@ enum dwc2_halt_status {
  * 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);
+
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
 
 /*
  * Host core Functions.
@@ -565,7 +913,7 @@ 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 select_phy);
+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);
 
@@ -605,6 +953,16 @@ extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
  */
 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
@@ -763,6 +1121,42 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val);
 
 extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val);
 
+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);
+
+/*
+ * The following functions check the controller's OTG operation mode
+ * capability (GHWCFG2.OTG_MODE).
+ *
+ * These functions can be used before the internal hsotg->hw_params
+ * are read in and cached so they always read directly from the
+ * GHWCFG2 register.
+ */
+unsigned 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);
+
+/*
+ * Returns the mode of operation, host or device
+ */
+static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
+{
+       return (DWC2_READ_4(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;
+}
+
 /*
  * Dump core registers and SPRAM
  */
@@ -775,4 +1169,51 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
  */
 extern u16 dwc2_get_otg_version(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);
+#define dwc2_is_device_connected(hsotg) (hsotg->connected)
+#else
+static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
+{ return 0; }
+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)
+{ return 0; }
+static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+               bool reset) {}
+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)
+{ return 0; }
+#define dwc2_is_device_connected(hsotg) (0)
+#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);
+#else
+static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
+{ 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 int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
+{ return 0; }
+#endif
+
 #endif /* __DWC2_CORE_H__ */
index a1e5aa1..fcd5810 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_coreintr.c,v 1.10 2017/06/29 17:36:16 deraadt Exp $      */
+/*     $OpenBSD: dwc2_coreintr.c,v 1.11 2021/07/22 18:32:33 mglocker Exp $     */
 /*     $NetBSD: dwc2_coreintr.c,v 1.8 2014/04/04 05:40:57 skrll Exp $  */
 
 /*
@@ -98,9 +98,6 @@ STATIC void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
                hprt0 &= ~HPRT0_ENA;
                DWC2_WRITE_4(hsotg, HPRT0, hprt0);
        }
-
-       /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_PRTINT);
 }
 
 /**
@@ -110,11 +107,11 @@ STATIC void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
 {
-       dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
-                dwc2_is_host_mode(hsotg) ? "Host" : "Device");
-
        /* Clear interrupt */
        DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_MODEMIS);
+
+       dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
+                dwc2_is_host_mode(hsotg) ? "Host" : "Device");
 }
 
 /**
@@ -140,6 +137,9 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                        dwc2_op_state_str(hsotg));
                gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
 
+               if (dwc2_is_device_mode(hsotg))
+                       dwc2_hsotg_disconnect(hsotg);
+
                if (hsotg->op_state == OTG_STATE_B_HOST) {
                        hsotg->op_state = OTG_STATE_B_PERIPHERAL;
                } else {
@@ -248,7 +248,7 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                        dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
                                hsotg->op_state);
                        spin_unlock(&hsotg->lock);
-                       dwc2_hcd_disconnect(hsotg);
+                       dwc2_hcd_disconnect(hsotg, false);
                        spin_lock(&hsotg->lock);
                        hsotg->op_state = OTG_STATE_A_PERIPHERAL;
                } else {
@@ -285,9 +285,13 @@ STATIC void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
 {
-       u32 gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+       u32 gintmsk;
+
+       /* Clear interrupt */
+       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
 
        /* Need to disable SOF interrupt immediately */
+       gintmsk = DWC2_READ_4(hsotg, GINTMSK);
        gintmsk &= ~GINTSTS_SOF;
        DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
 
@@ -299,13 +303,11 @@ STATIC void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
         * Release lock before scheduling workq as it holds spinlock during
         * scheduling.
         */
-       spin_unlock(&hsotg->lock);
-       task_set(&hsotg->wf_otg, dwc2_conn_id_status_change, hsotg);
-       task_add(hsotg->wq_otg, &hsotg->wf_otg);
-       spin_lock(&hsotg->lock);
-
-       /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
+       if (hsotg->wq_otg) {
+               spin_unlock(&hsotg->lock);
+               task_add(hsotg->wq_otg, &hsotg->wf_otg);
+               spin_lock(&hsotg->lock);
+       }
 }
 
 /**
@@ -321,10 +323,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)
 {
-       dev_dbg(hsotg->dev, "++Session Request Interrupt++\n");
+       int ret;
 
        /* Clear interrupt */
        DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SESSREQINT);
+
+       dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
+                                                       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");
+               }
+
+               /*
+                * Report disconnect if there is any previous session
+                * established
+                */
+               dwc2_hsotg_disconnect(hsotg);
+       }
 }
 
 /*
@@ -336,6 +356,11 @@ STATIC void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
 {
+       int ret;
+
+       /* Clear interrupt */
+       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
+
        dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
        dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
 
@@ -347,26 +372,32 @@ STATIC void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
                        /* 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");
+
+                       call_gadget(hsotg, resume);
                }
                /* Change to L0 state */
                hsotg->lx_state = DWC2_L0;
        } else {
+               if (hsotg->core_params->hibernation)
+                       return;
+
                if (hsotg->lx_state != DWC2_L1) {
                        u32 pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
 
                        /* Restart the Phy Clock */
                        pcgcctl &= ~PCGCTL_STOPPCLK;
                        DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
-                       timeout_reset(&hsotg->wkp_timer, mstohz(71),
-                           dwc2_wakeup_detected, hsotg);
+                       timeout_set(&hsotg->wkp_timer, dwc2_wakeup_detected,
+                           hsotg);
+                       timeout_add_msec(&hsotg->wkp_timer, 71);
                } else {
                        /* Change to L0 state */
                        hsotg->lx_state = DWC2_L0;
                }
        }
-
-       /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
 }
 
 /*
@@ -375,14 +406,14 @@ 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);
+
        dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
                dwc2_is_host_mode(hsotg) ? "Host" : "Device",
                dwc2_op_state_str(hsotg));
 
-       /* Change to L3 (OFF) state */
-       hsotg->lx_state = DWC2_L3;
-
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_DISCONNINT);
+       if (hsotg->op_state == OTG_STATE_A_HOST)
+               dwc2_hcd_disconnect(hsotg, false);
 }
 
 /*
@@ -395,12 +426,15 @@ STATIC void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
 {
+       u32 dsts;
+       int ret;
+
+       /* Clear interrupt */
+       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
+
        dev_dbg(hsotg->dev, "USB SUSPEND\n");
 
        if (dwc2_is_device_mode(hsotg)) {
-#ifdef DWC2_DEBUG
-               u32 dsts;
-
                /*
                 * Check the Device status register to determine if the Suspend
                 * state is active
@@ -411,11 +445,43 @@ STATIC void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
                        "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
                        !!(dsts & DSTS_SUSPSTS),
                        hsotg->hw_params.power_optimized);
-#endif
+               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;
+                       }
+
+                       ret = dwc2_enter_hibernation(hsotg);
+                       if (ret) {
+                               if (ret != -ENOTSUP)
+                                       dev_err(hsotg->dev,
+                                                       "enter hibernation failed\n");
+                               goto skip_power_saving;
+                       }
+
+                       udelay(100);
+
+                       /* 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
+                        */
+                       hsotg->lx_state = DWC2_L2;
+
+                       /* Call gadget suspend callback */
+                       call_gadget(hsotg, suspend);
+               }
        } else {
                if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
                        dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
 
+                       /* Change to L2 (suspend) state */
+                       hsotg->lx_state = DWC2_L2;
                        /* Clear the a_peripheral flag, back to a_host */
                        spin_unlock(&hsotg->lock);
                        dwc2_hcd_start(hsotg);
@@ -423,12 +489,6 @@ STATIC void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
                        hsotg->op_state = OTG_STATE_A_HOST;
                }
        }
-
-       /* Change to L2 (suspend) state */
-       hsotg->lx_state = DWC2_L2;
-
-       /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
 }
 
 #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT |         \
index ab76de7..1ddf2cd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcd.c,v 1.22 2020/03/21 12:08:31 patrick Exp $   */
+/*     $OpenBSD: dwc2_hcd.c,v 1.23 2021/07/22 18:32:33 mglocker Exp $  */
 /*     $NetBSD: dwc2_hcd.c,v 1.15 2014/11/24 10:14:14 skrll Exp $      */
 
 /*
@@ -112,10 +112,16 @@ STATIC void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
        dev_dbg(hsotg->dev, "    xfer_len: %d\n", chan->xfer_len);
        dev_dbg(hsotg->dev, "    qh: %p\n", chan->qh);
        dev_dbg(hsotg->dev, "  NP inactive sched:\n");
-       TAILQ_FOREACH(qh, &hsotg->non_periodic_sched_inactive, qh_list_entry)
+       list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive,
+                           qh_list_entry)
+               dev_dbg(hsotg->dev, "    %p\n", qh);
+       dev_dbg(hsotg->dev, "  NP waiting sched:\n");
+       list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting,
+                           qh_list_entry)
                dev_dbg(hsotg->dev, "    %p\n", qh);
        dev_dbg(hsotg->dev, "  NP active sched:\n");
-       TAILQ_FOREACH(qh, &hsotg->non_periodic_sched_active, qh_list_entry)
+       list_for_each_entry(qh, &hsotg->non_periodic_sched_active,
+                           qh_list_entry)
                dev_dbg(hsotg->dev, "    %p\n", qh);
        dev_dbg(hsotg->dev, "  Channels:\n");
        for (i = 0; i < num_channels; i++) {
@@ -133,43 +139,43 @@ STATIC void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
  * Must be called with interrupt disabled and spinlock held
  */
 STATIC void dwc2_kill_urbs_in_qh_list(struct dwc2_hsotg *hsotg,
-                                     struct dwc2_qh_list *qh_list)
+                                     struct list_head *qh_list)
 {
        struct dwc2_qh *qh, *qh_tmp;
        struct dwc2_qtd *qtd, *qtd_tmp;
 
-       TAILQ_FOREACH_SAFE(qh, qh_list, qh_list_entry, qh_tmp) {
-               TAILQ_FOREACH_SAFE(qtd, &qh->qtd_list, qtd_list_entry, qtd_tmp) {
-                       dwc2_host_complete(hsotg, qtd, -ETIMEDOUT);
+       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);
                }
        }
 }
 
 STATIC void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
-                             struct dwc2_qh_list *qh_list)
+                             struct list_head *qh_list)
 {
        struct dwc2_qtd *qtd, *qtd_tmp;
        struct dwc2_qh *qh, *qh_tmp;
        unsigned long flags;
 
-       if (TAILQ_EMPTY(qh_list)) {
+       if (!qh_list->next)
                /* The list hasn't been initialized yet */
                return;
-       }
 
        spin_lock_irqsave(&hsotg->lock, flags);
 
        /* Ensure there are no QTDs or URBs left */
        dwc2_kill_urbs_in_qh_list(hsotg, qh_list);
 
-       TAILQ_FOREACH_SAFE(qh, qh_list, qh_list_entry, qh_tmp) {
+       list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) {
                dwc2_hcd_qh_unlink(hsotg, qh);
 
                /* Free each QTD in the QH's QTD list */
-               TAILQ_FOREACH_SAFE(qtd, &qh->qtd_list, qtd_list_entry, qtd_tmp) {
+               list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
+                                        qtd_list_entry)
                        dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
-               }
 
                spin_unlock_irqrestore(&hsotg->lock, flags);
                dwc2_hcd_qh_free(hsotg, qh);
@@ -190,6 +196,7 @@ STATIC void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
 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);
@@ -233,7 +240,7 @@ STATIC void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
                /* Flush out any channel requests in slave mode */
                for (i = 0; i < num_channels; i++) {
                        channel = hsotg->hc_ptr_array[i];
-                       if (channel->in_freelist == 0)
+                       if (!list_empty(&channel->hc_list_entry))
                                continue;
                        hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
                        if (hcchar & HCCHAR_CHENA) {
@@ -246,7 +253,7 @@ STATIC void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
 
        for (i = 0; i < num_channels; i++) {
                channel = hsotg->hc_ptr_array[i];
-               if (channel->in_freelist != 0)
+               if (!list_empty(&channel->hc_list_entry))
                        continue;
                hcchar = DWC2_READ_4(hsotg, HCCHAR(i));
                if (hcchar & HCCHAR_CHENA) {
@@ -256,8 +263,7 @@ STATIC void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
                }
 
                dwc2_hc_cleanup(hsotg, channel);
-               LIST_INSERT_HEAD(&hsotg->free_hc_list, channel, hc_list_entry);
-               channel->in_freelist = 1;
+               list_add_tail(&channel->hc_list_entry, &hsotg->free_hc_list);
                /*
                 * Added for Descriptor DMA to prevent channel double cleanup in
                 * release_channel_ddma(), which is called from ep_disable when
@@ -265,18 +271,44 @@ STATIC void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
                 */
                channel->qh = NULL;
        }
+       /* All channels have been freed, mark them available */
+       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;
+       }
+}
+
+/**
+ * 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);
+
+       hsotg->flags.b.port_connect_status_change = 1;
+       hsotg->flags.b.port_connect_status = 1;
 }
 
 /**
  * dwc2_hcd_disconnect() - Handles disconnect of the HCD
  *
  * @hsotg: Pointer to struct dwc2_hsotg
+ * @force: If true, we won't try to reconnect even if we see device connected.
  *
  * Must be called with interrupt disabled and spinlock held
  */
-void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
+void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
 {
        u32 intr;
+       u32 hprt0;
 
        /* Set status flags for the hub driver */
        hsotg->flags.b.port_connect_status_change = 1;
@@ -317,6 +349,24 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
        dwc2_host_disconnect(hsotg);
 
        dwc2_root_intr(hsotg->hsotg_sc);
+
+       /*
+        * 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);
+       }
 }
 
 /**
@@ -326,9 +376,12 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
  */
 STATIC void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
 {
-       if (hsotg->lx_state == DWC2_L2)
+       if (hsotg->bus_suspended) {
                hsotg->flags.b.port_suspend_change = 1;
-       else
+               usb_hcd_resume_root_hub(hsotg->priv);
+       }
+
+       if (hsotg->lx_state == DWC2_L1)
                hsotg->flags.b.port_l1_change = 1;
 
        dwc2_root_intr(hsotg->hsotg_sc);
@@ -359,12 +412,11 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
        DWC2_WRITE_4(hsotg, HPRT0, 0);
 }
 
-int
-dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
-                    void **ep_handle, gfp_t mem_flags)
+/* 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)
 {
-       struct dwc2_softc *sc = hsotg->hsotg_sc;
-       struct dwc2_qtd *qtd;
        u32 intr_mask;
        int retval;
        int dev_speed;
@@ -377,7 +429,7 @@ dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
 
        dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
 
-       /* Some core configurations cannot support LS traffic on a FS root port */
+       /* 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)) {
@@ -385,24 +437,23 @@ dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
                u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
 
                if (prtspd == HPRT0_SPD_FULL_SPEED) {
+                       dev_err(hsotg->dev,
+                               "DWC OTG HCD URB Enqueue unsupported\n");
                        return -ENODEV;
                }
        }
 
-       qtd = pool_get(&sc->sc_qtdpool, PR_NOWAIT);
        if (!qtd)
-               return -ENOMEM;
+               return -EINVAL;
 
        memset(qtd, 0, sizeof(*qtd));
 
        dwc2_hcd_qtd_init(qtd, urb);
-       retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle,
-                                 mem_flags);
+       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);
-               pool_put(&sc->sc_qtdpool, qtd);
                return retval;
        }
 
@@ -476,7 +527,7 @@ dwc2_hcd_urb_dequeue(struct dwc2_hsotg *hsotg,
                if (in_process) {
                        dwc2_hcd_qh_deactivate(hsotg, qh, 0);
                        qh->channel = NULL;
-               } else if (TAILQ_EMPTY(&qh->qtd_list)) {
+               } else if (list_empty(&qh->qtd_list)) {
                        dwc2_hcd_qh_unlink(hsotg, qh);
                }
        } else {
@@ -500,7 +551,7 @@ dwc2_hcd_reinit(struct dwc2_hsotg *hsotg)
        int i;
 
        hsotg->flags.d32 = 0;
-       hsotg->non_periodic_qh_ptr = NULL;
+       hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active;
 
        if (hsotg->core_params->uframe_sched > 0) {
                hsotg->available_host_channels =
@@ -514,16 +565,14 @@ dwc2_hcd_reinit(struct dwc2_hsotg *hsotg)
         * Put all channels in the free channel list and clean up channel
         * states
         */
-       LIST_FOREACH_SAFE(chan, &hsotg->free_hc_list, hc_list_entry, chan_tmp) {
-               LIST_REMOVE(chan, hc_list_entry);
-               chan->in_freelist = 0;
-       }
+       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->core_params->host_channels;
        for (i = 0; i < num_channels; i++) {
                chan = hsotg->hc_ptr_array[i];
-               LIST_INSERT_HEAD(&hsotg->free_hc_list, chan, hc_list_entry);
-               chan->in_freelist = 1;
+               list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
                dwc2_hc_cleanup(hsotg, chan);
        }
 
@@ -665,22 +714,24 @@ STATIC void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 }
 
 STATIC int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-                                  struct dwc2_host_chan *chan, void *bufptr)
+                                  struct dwc2_host_chan *chan,
+                                  struct dwc2_hcd_urb *urb, void *bufptr)
 {
        u32 buf_size;
 
-       if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
-               buf_size = hsotg->core_params->max_transfer_size;
-       else
-               buf_size = 4096;
-
        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;
+
                qh->dw_align_buf = NULL;
                qh->dw_align_buf_dma = 0;
-               err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, buf_size, buf_size,
-                                  USB_DMA_COHERENT, &qh->dw_align_buf_usbdma);
+               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;
 
@@ -689,16 +740,26 @@ STATIC int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                }
                if (!qh->dw_align_buf)
                        return -ENOMEM;
+               qh->dw_align_buf_size = buf_size;
        }
 
-       if (!chan->ep_is_in && chan->xfer_len) {
-               usb_syncmem(chan->xfer_usbdma, 0, buf_size,
-                           BUS_DMASYNC_POSTWRITE);
-               memcpy(qh->dw_align_buf, bufptr, chan->xfer_len);
-               usb_syncmem(chan->xfer_usbdma, 0, buf_size,
-                           BUS_DMASYNC_PREWRITE);
+       if (chan->xfer_len) {
+               dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
+               void *usb_urb = urb->priv;
+
+               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");
+               }
        }
 
+       usb_syncmem(&qh->dw_align_buf_usbdma, 0, qh->dw_align_buf_size,
+           chan->ep_is_in ?  BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
        chan->align_buf = qh->dw_align_buf_dma;
        return 0;
 }
@@ -722,23 +783,23 @@ STATIC int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
        if (dbg_qh(qh))
                dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh);
 
-       if (TAILQ_EMPTY(&qh->qtd_list)) {
+       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)) {
+       if (list_empty(&hsotg->free_hc_list)) {
                dev_dbg(hsotg->dev, "No free channel to assign\n");
                return -ENOMEM;
        }
 
-       chan = LIST_FIRST(&hsotg->free_hc_list);
+       chan = list_first_entry(&hsotg->free_hc_list, struct dwc2_host_chan,
+                               hc_list_entry);
 
        /* Remove host channel from free list */
-       LIST_REMOVE(chan, hc_list_entry);
-       chan->in_freelist = 0;
+       list_del_init(&chan->hc_list_entry);
 
-       qtd = TAILQ_FIRST(&qh->qtd_list);
+       qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry);
        urb = qtd->urb;
        qh->channel = chan;
        qtd->in_process = 1;
@@ -793,16 +854,16 @@ STATIC int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
        /* Non DWORD-aligned buffer case */
        if (bufptr) {
-               dev_vdbg(hsotg->dev, "Non-aligned buffer%p\n", bufptr);
-               if (dwc2_hc_setup_align_buf(hsotg, qh, chan, 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_INSERT_HEAD(&hsotg->free_hc_list, chan, hc_list_entry);
-                       chan->in_freelist = 1;
+                       list_add_tail(&chan->hc_list_entry,
+                                     &hsotg->free_hc_list);
                        qtd->in_process = 0;
                        qh->channel = NULL;
                        return -ENOMEM;
@@ -819,8 +880,11 @@ STATIC int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                 */
                chan->multi_count = dwc2_hb_mult(qh->maxp);
 
-       if (hsotg->core_params->dma_desc_enable > 0)
+       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;
+       }
 
        dwc2_hc_init(hsotg, chan);
        chan->qh = qh;
@@ -841,24 +905,25 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
                struct dwc2_hsotg *hsotg)
 {
        enum dwc2_transaction_type ret_val = DWC2_TRANSACTION_NONE;
-       struct dwc2_qh *qh, *qhn;
+       struct list_head *qh_ptr;
+       struct dwc2_qh *qh;
        int num_channels;
 
-#ifdef DWC2_DEBUG
+#ifdef DWC2_DEBUG_SOF
        dev_vdbg(hsotg->dev, "  Select Transactions\n");
 #endif
 
        /* Process entries in the periodic ready list */
-       /* TAILQ_FOREACH_SAFE? */
-       qh = TAILQ_FIRST(&hsotg->periodic_sched_ready);
-       while (qh != NULL) {
-               if (LIST_EMPTY(&hsotg->free_hc_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;
 
@@ -866,11 +931,9 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
                 * Move the QH from the periodic ready schedule to the
                 * periodic assigned schedule
                 */
-               qhn = TAILQ_NEXT(qh, qh_list_entry);
-               TAILQ_REMOVE(&hsotg->periodic_sched_ready, qh, qh_list_entry);
-               TAILQ_INSERT_TAIL(&hsotg->periodic_sched_assigned, qh, qh_list_entry);
+               qh_ptr = qh_ptr->next;
+               list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned);
                ret_val = DWC2_TRANSACTION_PERIODIC;
-               qh = qhn;
        }
 
        /*
@@ -879,14 +942,15 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
         * reserved for periodic transfers.
         */
        num_channels = hsotg->core_params->host_channels;
-       qh = TAILQ_FIRST(&hsotg->non_periodic_sched_inactive);
-       while (qh != NULL) {
+       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))
+               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
@@ -897,7 +961,7 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
                if (qh->nak_frame != 0xffff &&
                    dwc2_full_frame_num(qh->nak_frame) ==
                    dwc2_full_frame_num(dwc2_hcd_get_frame_number(hsotg))) {
-                       qh = TAILQ_NEXT(qh, qh_list_entry);
+                       qh_ptr = qh_ptr->next;
                        continue;
                } else {
                        qh->nak_frame = 0xffff;
@@ -916,10 +980,9 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
                 * Move the QH from the non-periodic inactive schedule to the
                 * non-periodic active schedule
                 */
-               qhn = TAILQ_NEXT(qh, qh_list_entry);
-               TAILQ_REMOVE(&hsotg->non_periodic_sched_inactive, qh, qh_list_entry);
-               TAILQ_INSERT_TAIL(&hsotg->non_periodic_sched_active, qh, qh_list_entry);
-               qh = qhn;
+               qh_ptr = qh_ptr->next;
+               list_move(&qh->qh_list_entry,
+                         &hsotg->non_periodic_sched_active);
 
                if (ret_val == DWC2_TRANSACTION_NONE)
                        ret_val = DWC2_TRANSACTION_NON_PERIODIC;
@@ -1013,7 +1076,8 @@ STATIC int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
  */
 STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_qh *qh, *qhn;
+       struct list_head *qh_ptr;
+       struct dwc2_qh *qh;
        u32 tx_status;
        u32 fspcavail;
        u32 gintmsk;
@@ -1038,8 +1102,8 @@ STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
                         fspcavail);
        }
 
-       qh = TAILQ_FIRST(&hsotg->periodic_sched_assigned);
-       while (qh != NULL) {
+       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;
@@ -1048,14 +1112,15 @@ STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
                        break;
                }
 
+               qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
                if (!qh->channel) {
-                       qh = TAILQ_NEXT(qh, qh_list_entry);
+                       qh_ptr = qh_ptr->next;
                        continue;
                }
 
                /* Make sure EP's TT buffer is clean before queueing qtds */
                if (qh->tt_buffer_dirty) {
-                       qh = TAILQ_NEXT(qh, qh_list_entry);
+                       qh_ptr = qh_ptr->next;
                        continue;
                }
 
@@ -1085,18 +1150,16 @@ STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
                 */
                if (hsotg->core_params->dma_enable > 0 || status == 0 ||
                    qh->channel->requests == qh->channel->multi_count) {
-                       qhn = TAILQ_NEXT(qh, qh_list_entry);
+                       qh_ptr = qh_ptr->next;
                        /*
                         * Move the QH from the periodic assigned schedule to
                         * the periodic queued schedule
                         */
-                       TAILQ_REMOVE(&hsotg->periodic_sched_assigned, qh, qh_list_entry);
-                       TAILQ_INSERT_TAIL(&hsotg->periodic_sched_queued, qh, qh_list_entry);
+                       list_move(&qh->qh_list_entry,
+                                 &hsotg->periodic_sched_queued);
 
                        /* done queuing high bandwidth */
                        hsotg->queuing_high_bandwidth = 0;
-
-                       qh = qhn;
                }
        }
 
@@ -1115,7 +1178,7 @@ STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
                                 fspcavail);
                }
 
-               if (!TAILQ_EMPTY(&hsotg->periodic_sched_assigned) ||
+               if (!list_empty(&hsotg->periodic_sched_assigned) ||
                    no_queue_space || no_fifo_space) {
                        /*
                         * May need to queue more transactions as the request
@@ -1153,6 +1216,7 @@ STATIC void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
  */
 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;
@@ -1179,10 +1243,9 @@ STATIC void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
         * Keep track of the starting point. Skip over the start-of-list
         * entry.
         */
-       if (hsotg->non_periodic_qh_ptr == NULL) {
-               hsotg->non_periodic_qh_ptr = TAILQ_FIRST(&hsotg->non_periodic_sched_active);
-       }
-       qh = hsotg->non_periodic_qh_ptr;
+       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
@@ -1197,6 +1260,8 @@ STATIC void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
                        break;
                }
 
+               qh = list_entry(hsotg->non_periodic_qh_ptr, struct dwc2_qh,
+                               qh_list_entry);
                if (!qh->channel)
                        goto next;
 
@@ -1215,12 +1280,13 @@ STATIC void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
                        break;
                }
 next:
-               /* Advance to next QH, wrapping to the start if we hit the end */
-               qh = TAILQ_NEXT(qh, qh_list_entry);
-               if (qh == NULL)
-                       qh = TAILQ_FIRST(&hsotg->non_periodic_sched_active);
-       } while ((qh != hsotg->non_periodic_qh_ptr) && (hsotg->non_periodic_qh_ptr != NULL));
-       hsotg->non_periodic_qh_ptr = qh;
+               /* 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->core_params->dma_enable <= 0) {
                tx_status = DWC2_READ_4(hsotg, GNPTXSTS);
@@ -1275,19 +1341,19 @@ next:
 void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
                                 enum dwc2_transaction_type tr_type)
 {
-#ifdef DWC2_DEBUG
+#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) &&
-           !TAILQ_EMPTY(&hsotg->periodic_sched_assigned))
+           !list_empty(&hsotg->periodic_sched_assigned))
                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 (!TAILQ_EMPTY(&hsotg->non_periodic_sched_active)) {
+               if (!list_empty(&hsotg->non_periodic_sched_active)) {
                        dwc2_process_non_periodic_channels(hsotg);
                } else {
                        /*
@@ -1303,12 +1369,12 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 }
 
 
-void
-dwc2_conn_id_status_change(void *data)
+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__);
 
@@ -1336,6 +1402,10 @@ dwc2_conn_id_status_change(void *data)
                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");
@@ -1359,7 +1429,7 @@ dwc2_conn_id_status_change(void *data)
        }
 }
 
-void dwc2_wakeup_detected(void * data)
+void dwc2_wakeup_detected(void *data)
 {
        struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)data;
        u32 hprt0;
@@ -1378,6 +1448,7 @@ void dwc2_wakeup_detected(void * data)
                DWC2_READ_4(hsotg, HPRT0));
 
        dwc2_hcd_rem_wakeup(hsotg);
+       hsotg->bus_suspended = 0;
 
        /* Change to L0 state */
        hsotg->lx_state = DWC2_L0;
@@ -1406,14 +1477,19 @@ STATIC void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
        hprt0 |= HPRT0_SUSP;
        DWC2_WRITE_4(hsotg, HPRT0, hprt0);
 
-       /* Update lx_state */
-       hsotg->lx_state = DWC2_L2;
+       hsotg->bus_suspended = 1;
 
-       /* Suspend the Phy Clock */
-       pcgctl = DWC2_READ_4(hsotg, PCGCTL);
-       pcgctl |= PCGCTL_STOPPCLK;
-       DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
-       udelay(10);
+       /*
+        * 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);
+       }
 
        /* For HNP the bus must be suspended for at least 200ms */
        if (dwc2_host_is_b_hnp_enabled(hsotg)) {
@@ -1429,6 +1505,45 @@ STATIC void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
        }
 }
 
+/* Must NOT be called with interrupt disabled or spinlock held */
+STATIC void dwc2_port_resume(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_softc *sc = hsotg->hsotg_sc;
+       unsigned long flags;
+       u32 hprt0;
+       u32 pcgctl;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       /*
+        * If hibernation is supported, Phy clock is already resumed
+        * after registers restore.
+        */
+       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);
+       }
+
+       hprt0 = dwc2_read_hprt0(hsotg);
+       hprt0 |= HPRT0_RES;
+       hprt0 &= ~HPRT0_SUSP;
+       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       usb_delay_ms(&sc->sc_bus, USB_RESUME_TIMEOUT);
+
+       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);
+}
+
 /* Handles hub class-specific requests */
 int
 dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
@@ -1476,17 +1591,8 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
                case USB_PORT_FEAT_SUSPEND:
                        dev_dbg(hsotg->dev,
                                "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
-                       DWC2_WRITE_4(hsotg, PCGCTL, 0);
-                       usleep_range(20000, 40000);
-
-                       hprt0 = dwc2_read_hprt0(hsotg);
-                       hprt0 |= HPRT0_RES;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
-                       hprt0 &= ~HPRT0_SUSP;
-                       usleep_range(100000, 150000);
-
-                       hprt0 &= ~HPRT0_RES;
-                       DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+                       if (hsotg->bus_suspended)
+                               dwc2_port_resume(hsotg);
                        break;
 
                case USB_PORT_FEAT_POWER:
@@ -1564,9 +1670,10 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
                dev_dbg(hsotg->dev, "GetHubDescriptor\n");
                hub_desc = (usb_hub_descriptor_t *)buf;
                hub_desc->bDescLength = 9;
-               hub_desc->bDescriptorType = 0x29;
+               hub_desc->bDescriptorType = USB_DT_HUB;
                hub_desc->bNbrPorts = 1;
-               USETW(hub_desc->wHubCharacteristics, 0x08);
+               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;
@@ -1644,6 +1751,27 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
                /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
                USETW(ps.wPortStatus, port_status);
 
+               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;
+
+                               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;
@@ -1725,6 +1853,15 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
                        /* 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_WRITE_4(hsotg, HPRT0, hprt0);
+                       break;
+
                default:
                        retval = -EINVAL;
                        dev_err(hsotg->dev,
@@ -1750,7 +1887,7 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 {
        u32 hfnum = DWC2_READ_4(hsotg, HFNUM);
 
-#ifdef DWC2_DEBUG
+#ifdef DWC2_DEBUG_SOF
        dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n",
                 (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT);
 #endif
@@ -1874,7 +2011,7 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
                if (!(chan->xfer_started && chan->qh))
                        continue;
 
-               TAILQ_FOREACH(qtd, &chan->qh->qtd_list, qtd_list_entry) {
+               list_for_each_entry(qtd, &chan->qh->qtd_list, qtd_list_entry) {
                        if (!qtd->in_process)
                                break;
                        urb = qtd->urb;
@@ -2020,12 +2157,10 @@ void dwc2_host_disconnect(struct dwc2_hsotg *hsotg)
 //     hcd->self.is_b_host = 0;
 }
 
-
 /*
  * Work queue function for starting the HCD when A-Cable is connected
  */
-void
-dwc2_hcd_start_func(void *data)
+STATIC void dwc2_hcd_start_func(void *data)
 {
        struct dwc2_hsotg *hsotg = data;
 
@@ -2036,19 +2171,24 @@ dwc2_hcd_start_func(void *data)
 /*
  * Reset work queue function
  */
-void
-dwc2_hcd_reset_func(void *data)
+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_WRITE_4(hsotg, HPRT0, hprt0);
        hsotg->flags.b.port_reset_change = 1;
 
        dwc2_root_intr(hsotg->hsotg_sc);
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
 /*
@@ -2063,7 +2203,6 @@ dwc2_hcd_reset_func(void *data)
  * error code on failure.
  */
 
-
 /*
  * Frees secondary storage associated with the dwc2_hsotg structure contained
  * in the struct usb_hcd field
@@ -2078,6 +2217,7 @@ STATIC void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
 
        /* Free memory for QH/QTD lists */
        dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive);
+       dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting);
        dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active);
        dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive);
        dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready);
@@ -2137,41 +2277,22 @@ STATIC void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
        dwc2_hcd_free(hsotg);
 }
 
-/*
- * 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;
-
-       for (i = 0; i < size; i++)
-               p[i] = value;
-}
-
 /*
  * Initializes the HCD. This function allocates memory for and initializes the
  * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the
  * USB bus with the core and calls the hc_driver->start() function. It returns
  * a negative error on failure.
  */
-int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
-                 const struct dwc2_core_params *params)
+int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_host_chan *channel;
        int i, num_channels;
        int retval;
 
-       dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
-
-       /* Detect config values from hardware */
-       retval = dwc2_get_hwparams(hsotg);
+       if (usb_disabled())
+               return -ENODEV;
 
-       if (retval)
-               return retval;
+       dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
 
        retval = -ENOMEM;
 
@@ -2191,16 +2312,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
        hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 #endif
 
-       hsotg->core_params = malloc(sizeof(*hsotg->core_params), M_DEVBUF,
-                                   M_ZERO | M_WAITOK);
-       if (!hsotg->core_params)
-               goto error1;
-
-       dwc2_set_all_params(hsotg->core_params, -1);
-
-       /* Validate parameter values */
-       dwc2_set_parameters(hsotg, params);
-
        spin_lock_init(&hsotg->lock);
 
        /*
@@ -2217,28 +2328,30 @@ 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);
-       if (hsotg->wq_otg == NULL) {
+       if (!hsotg->wq_otg) {
                dev_err(hsotg->dev, "Failed to create workqueue\n");
                goto error2;
        }
+       task_set(&hsotg->wf_otg, dwc2_conn_id_status_change, hsotg);
 
        timeout_set(&hsotg->wkp_timer, dwc2_wakeup_detected, hsotg);
 
        /* Initialize the non-periodic schedule */
-       TAILQ_INIT(&hsotg->non_periodic_sched_inactive);
-       TAILQ_INIT(&hsotg->non_periodic_sched_active);
+       INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive);
+       INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting);
+       INIT_LIST_HEAD(&hsotg->non_periodic_sched_active);
 
        /* Initialize the periodic schedule */
-       TAILQ_INIT(&hsotg->periodic_sched_inactive);
-       TAILQ_INIT(&hsotg->periodic_sched_ready);
-       TAILQ_INIT(&hsotg->periodic_sched_assigned);
-       TAILQ_INIT(&hsotg->periodic_sched_queued);
+       INIT_LIST_HEAD(&hsotg->periodic_sched_inactive);
+       INIT_LIST_HEAD(&hsotg->periodic_sched_ready);
+       INIT_LIST_HEAD(&hsotg->periodic_sched_assigned);
+       INIT_LIST_HEAD(&hsotg->periodic_sched_queued);
 
        /*
         * Create a host channel descriptor for each host channel implemented
         * in the controller. Initialize the channel descriptor array.
         */
-       LIST_INIT(&hsotg->free_hc_list);
+       INIT_LIST_HEAD(&hsotg->free_hc_list);
        num_channels = hsotg->core_params->host_channels;
        memset(&hsotg->hc_ptr_array[0], 0, sizeof(hsotg->hc_ptr_array));
 
@@ -2267,11 +2380,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
         */
        hsotg->status_buf = NULL;
        if (hsotg->core_params->dma_enable > 0) {
-               retval = usb_allocmem(&hsotg->hsotg_sc->sc_bus,
-                                     DWC2_HCD_STATUS_BUF_SIZE, 0,
-                                     USB_DMA_COHERENT,
-                                     &hsotg->status_buf_usbdma);
-               if (!retval) {
+               int error = usb_allocmem(&hsotg->hsotg_sc->sc_bus,
+                   DWC2_HCD_STATUS_BUF_SIZE, 0, USB_DMA_COHERENT,
+                   &hsotg->status_buf_usbdma);
+               if (!error) {
                        hsotg->status_buf = KERNADDR(&hsotg->status_buf_usbdma, 0);
                        hsotg->status_buf_dma = DMAADDR(&hsotg->status_buf_usbdma, 0);
                }
@@ -2279,6 +2391,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
                hsotg->status_buf = malloc(DWC2_HCD_STATUS_BUF_SIZE, M_DEVBUF,
                                           M_ZERO | M_WAITOK);
 
+       /* retval is already -ENOMEM */
        if (!hsotg->status_buf)
                goto error3;
 
@@ -2301,23 +2414,46 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
 error3:
        dwc2_hcd_release(hsotg);
 error2:
-error1:
-       free(hsotg->core_params, M_DEVBUF, sizeof(*hsotg->core_params));
+       if (hsotg->core_params != NULL)
+               free(hsotg->core_params, M_DEVBUF, sizeof(*hsotg->core_params));
 
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
-       free(hsotg->last_frame_num_array, M_DEVBUF,
-             sizeof(*hsotg->last_frame_num_array) * FRAME_NUM_ARRAY_SIZE);
-       free(hsotg->frame_num_array, M_DEVBUF,
-                 sizeof(*hsotg->frame_num_array) * FRAME_NUM_ARRAY_SIZE);
+       if (hsotg->last_frame_num_array != NULL)
+               free(hsotg->last_frame_num_array, M_DEVBUF,
+                   sizeof(*hsotg->last_frame_num_array) * FRAME_NUM_ARRAY_SIZE);
+       if (hsotg->frame_num_array != NULL)
+               free(hsotg->frame_num_array, M_DEVBUF,
+                   sizeof(*hsotg->frame_num_array) * FRAME_NUM_ARRAY_SIZE);
 #endif
 
        dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval);
        return retval;
 }
 
-int dwc2_hcd_dma_config(struct dwc2_hsotg *hsotg,
-                       struct dwc2_core_dma_config *config)
+/*
+ * Removes the HCD.
+ * Frees memory and resources associated with the HCD and deregisters the bus.
+ */
+void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
 {
-       hsotg->core_dma_config = config;
-       return 0;
+       struct usb_hcd *hcd;
+
+       dev_dbg(hsotg->dev, "DWC OTG HCD REMOVE\n");
+
+       hcd = dwc2_hsotg_to_hcd(hsotg);
+       dev_dbg(hsotg->dev, "hsotg->hcd = %p\n", hcd);
+
+       if (!hcd) {
+               dev_dbg(hsotg->dev, "%s: dwc2_hsotg_to_hcd(hsotg) NULL!\n",
+                       __func__);
+               return;
+       }
+       hsotg->priv = NULL;
+
+       dwc2_hcd_release(hsotg);
+
+#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
+       free(hsotg->last_frame_num_array, M_DEVBUF, sizeof(*hsotg->last_frame_num_array) * FRAME_NUM_ARRAY_SIZE);
+       free(hsotg->frame_num_array, M_DEVBUF, sizeof(*hsotg->frame_num_array) * FRAME_NUM_ARRAY_SIZE);
+#endif
 }
index fe74427..f25c534 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcd.h,v 1.13 2015/06/28 11:48:18 jmatthew Exp $  */
+/*     $OpenBSD: dwc2_hcd.h,v 1.14 2021/07/22 18:32:33 mglocker Exp $  */
 /*     $NetBSD: dwc2_hcd.h,v 1.9 2014/09/03 10:00:08 skrll Exp $       */
 
 /*
@@ -110,6 +110,7 @@ struct dwc2_qh;
  * @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
+ * @desc_list_sz:       Current QH's descriptor list size
  *
  * This structure represents the state of a single host channel when acting in
  * host mode. It contains the data items needed to transfer packets to an
@@ -161,9 +162,10 @@ struct dwc2_host_chan {
        enum dwc2_halt_status halt_status;
        u32 hcint;
        struct dwc2_qh *qh;
-       LIST_ENTRY(dwc2_host_chan) hc_list_entry;
+       struct list_head hc_list_entry;
+       struct usb_dma desc_list_usbdma;
        dma_addr_t desc_list_addr;
-       int in_freelist;
+       u32 desc_list_sz;
 };
 
 struct dwc2_hcd_pipe_info {
@@ -221,6 +223,7 @@ enum dwc2_transaction_type {
 /**
  * struct dwc2_qh - Software queue head structure
  *
+ * @hsotg:              The HCD state structure for the DWC OTG controller
  * @ep_type:            Endpoint type. One of the following values:
  *                       - USB_ENDPOINT_XFER_CONTROL
  *                       - USB_ENDPOINT_XFER_BULK
@@ -251,23 +254,30 @@ enum dwc2_transaction_type {
  * @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_dma:   DMA address for align_buf
+ * @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
  * @qh_list_entry:      Entry for QH in either the periodic or non-periodic
  *                      schedule
  * @desc_list:          List of transfer descriptors
  * @desc_list_dma:      Physical address of desc_list
+ * @desc_list_sz:       Size of descriptors list
  * @n_bytes:            Xfer Bytes array. Each element corresponds to a transfer
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
+ * @wait_timer:         Timer used to wait before re-queuing.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
+ * @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.
  *
  * 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.
  */
 struct dwc2_qh {
+       struct dwc2_hsotg *hsotg;
        u8 ep_type;
        u8 ep_is_in;
        u16 maxp;
@@ -286,16 +296,21 @@ struct dwc2_qh {
        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;
-       TAILQ_HEAD(, dwc2_qtd) qtd_list;
+       struct list_head qtd_list;
        struct dwc2_host_chan *channel;
-       TAILQ_ENTRY(dwc2_qh) qh_list_entry;
+       struct list_head qh_list_entry;
        struct usb_dma desc_list_usbdma;
        struct dwc2_hcd_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 wait_timer;
        unsigned tt_buffer_dirty:1;
-       unsigned linked:1;
+       unsigned want_wait:1;
+       unsigned wait_timer_cancel:1;
 };
 
 /**
@@ -326,6 +341,7 @@ struct dwc2_qh {
  * @n_desc:             Number of DMA descriptors for this QTD
  * @isoc_frame_index_last: Last activated frame (packet) index, used in
  *                      descriptor DMA mode only
+ * @num_naks:           Number of NAKs received on this QTD.
  * @urb:                URB for this transfer
  * @qh:                 Queue head for this QTD
  * @qtd_list_entry:     For linking to the QH's list of QTDs
@@ -350,13 +366,16 @@ struct dwc2_qtd {
        u8 isoc_split_pos;
        u16 isoc_frame_index;
        u16 isoc_split_offset;
+       u16 isoc_td_last;
+       u16 isoc_td_first;
        u32 ssplit_out_xfer_count;
        u8 error_count;
        u8 n_desc;
        u16 isoc_frame_index_last;
+       u16 num_naks;
        struct dwc2_hcd_urb *urb;
        struct dwc2_qh *qh;
-       TAILQ_ENTRY(dwc2_qtd) qtd_list_entry;
+       struct list_head qtd_list_entry;
 };
 
 #ifdef DEBUG
@@ -367,7 +386,7 @@ struct hc_xfer_info {
 #endif
 
 /* Gets the struct usb_hcd that contains a struct dwc2_hsotg */
-STATIC_INLINE struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
+static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
 {
        return (struct usb_hcd *)hsotg->priv;
 }
@@ -379,7 +398,7 @@ STATIC_INLINE struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
  * channel is re-assigned. In fact, subsequent handling may cause crashes
  * because the channel structures are cleaned up when the channel is released.
  */
-STATIC_INLINE void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
+static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
 {
        u32 mask = DWC2_READ_4(hsotg, HCINTMSK(chnum));
 
@@ -387,24 +406,11 @@ STATIC_INLINE void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
        DWC2_WRITE_4(hsotg, HCINTMSK(chnum), mask);
 }
 
-/*
- * Returns the mode of operation, host or device
- */
-STATIC_INLINE int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
-{
-       return (DWC2_READ_4(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;
-}
-
 /*
  * 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)
+static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg)
 {
        u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);
 
@@ -412,65 +418,58 @@ STATIC_INLINE u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg)
        return hprt0;
 }
 
-STATIC_INLINE u8 dwc2_hcd_get_ep_num(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_get_ep_num(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->ep_num;
 }
 
-STATIC_INLINE u8 dwc2_hcd_get_pipe_type(struct dwc2_hcd_pipe_info *pipe)
+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_mps(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->mps;
 }
 
-STATIC_INLINE u8 dwc2_hcd_get_dev_addr(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_get_dev_addr(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->dev_addr;
 }
 
-STATIC_INLINE u8 dwc2_hcd_is_pipe_isoc(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_is_pipe_isoc(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->pipe_type == USB_ENDPOINT_XFER_ISOC;
 }
 
-STATIC_INLINE u8 dwc2_hcd_is_pipe_int(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_is_pipe_int(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->pipe_type == USB_ENDPOINT_XFER_INT;
 }
 
-STATIC_INLINE u8 dwc2_hcd_is_pipe_bulk(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_is_pipe_bulk(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->pipe_type == USB_ENDPOINT_XFER_BULK;
 }
 
-STATIC_INLINE u8 dwc2_hcd_is_pipe_control(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_is_pipe_control(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->pipe_type == USB_ENDPOINT_XFER_CONTROL;
 }
 
-STATIC_INLINE u8 dwc2_hcd_is_pipe_in(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_is_pipe_in(struct dwc2_hcd_pipe_info *pipe)
 {
        return pipe->pipe_dir == USB_DIR_IN;
 }
 
-STATIC_INLINE u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
+static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
 {
        return !dwc2_hcd_is_pipe_in(pipe);
 }
 
-extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
-                        const struct dwc2_core_params *params);
-extern int dwc2_hcd_dma_config(struct dwc2_hsotg *hsotg,
-                              struct dwc2_core_dma_config *config);
+extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg);
 extern void dwc2_hcd_remove(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);
 
 /* Transaction Execution Functions */
 extern enum dwc2_transaction_type dwc2_hcd_select_transactions(
@@ -481,6 +480,9 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 /* Schedule Queue Functions */
 /* Implemented in hcd_queue.c */
 extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
+extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+                                         struct dwc2_hcd_urb *urb,
+                                         gfp_t mem_flags);
 extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
 extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
 extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
@@ -489,7 +491,7 @@ extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
 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, int mem_flags);
+                           struct dwc2_qh *qh);
 
 /* Removes and frees a QTD */
 extern void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
@@ -512,25 +514,25 @@ extern void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
        ((_qh_ptr_)->ep_type == USB_ENDPOINT_XFER_BULK || \
         (_qh_ptr_)->ep_type == USB_ENDPOINT_XFER_CONTROL)
 
-#ifdef DWC2_DEBUG
-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_perio(void) { return true; }
-#else /* !DWC2_DEBUG */
-STATIC_INLINE bool dbg_hc(struct dwc2_host_chan *hc)
+#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_perio(void) { return true; }
+#else /* !CONFIG_USB_DWC2_DEBUG_PERIODIC */
+static inline bool dbg_hc(struct dwc2_host_chan *hc)
 {
        return hc->ep_type == USB_ENDPOINT_XFER_BULK ||
               hc->ep_type == USB_ENDPOINT_XFER_CONTROL;
 }
 
-STATIC_INLINE bool dbg_qh(struct dwc2_qh *qh)
+static inline bool dbg_qh(struct dwc2_qh *qh)
 {
        return qh->ep_type == USB_ENDPOINT_XFER_BULK ||
               qh->ep_type == USB_ENDPOINT_XFER_CONTROL;
 }
 
 
-STATIC_INLINE bool dbg_perio(void) { return false; }
+static inline bool dbg_perio(void) { return false; }
 #endif
 
 /* High bandwidth multiplier as encoded in highspeed endpoint descriptors */
@@ -539,12 +541,25 @@ STATIC_INLINE bool dbg_perio(void) { return false; }
 /* 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
+ * frame number when the max index frame number is reached.
+ */
+static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
+{
+       u16 diff = fr_idx1 - fr_idx2;
+       u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
+
+       return diff && !sign;
+}
+
 /*
  * Returns true if frame1 is less than or equal to frame2. The comparison is
  * done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
  * frame number when the max frame number is reached.
  */
-STATIC_INLINE int dwc2_frame_num_le(u16 frame1, u16 frame2)
+static inline int dwc2_frame_num_le(u16 frame1, u16 frame2)
 {
        return ((frame2 - frame1) & HFNUM_MAX_FRNUM) <= (HFNUM_MAX_FRNUM >> 1);
 }
@@ -554,7 +569,7 @@ STATIC_INLINE int dwc2_frame_num_le(u16 frame1, u16 frame2)
  * modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
  * number when the max frame number is reached.
  */
-STATIC_INLINE int dwc2_frame_num_gt(u16 frame1, u16 frame2)
+static inline int dwc2_frame_num_gt(u16 frame1, u16 frame2)
 {
        return (frame1 != frame2) &&
               ((frame1 - frame2) & HFNUM_MAX_FRNUM) < (HFNUM_MAX_FRNUM >> 1);
@@ -564,17 +579,17 @@ STATIC_INLINE int dwc2_frame_num_gt(u16 frame1, u16 frame2)
  * Increments frame by the amount specified by inc. The addition is done
  * modulo HFNUM_MAX_FRNUM. Returns the incremented value.
  */
-STATIC_INLINE u16 dwc2_frame_num_inc(u16 frame, u16 inc)
+static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
 {
        return (frame + inc) & HFNUM_MAX_FRNUM;
 }
 
-STATIC_INLINE u16 dwc2_full_frame_num(u16 frame)
+static inline u16 dwc2_full_frame_num(u16 frame)
 {
        return (frame & HFNUM_MAX_FRNUM) >> 3;
 }
 
-STATIC_INLINE u16 dwc2_micro_frame_num(u16 frame)
+static inline u16 dwc2_micro_frame_num(u16 frame)
 {
        return frame & 0x7;
 }
@@ -583,28 +598,28 @@ STATIC_INLINE u16 dwc2_micro_frame_num(u16 frame)
  * Returns the Core Interrupt Status register contents, ANDed with the Core
  * Interrupt Mask register contents
  */
-STATIC_INLINE u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg)
+static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg)
 {
        return DWC2_READ_4(hsotg, GINTSTS) & DWC2_READ_4(hsotg, GINTMSK);
 }
 
-STATIC_INLINE u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb)
+static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb)
 {
        return dwc2_urb->status;
 }
 
-STATIC_INLINE u32 dwc2_hcd_urb_get_actual_length(
+static inline u32 dwc2_hcd_urb_get_actual_length(
                struct dwc2_hcd_urb *dwc2_urb)
 {
        return dwc2_urb->actual_length;
 }
 
-STATIC_INLINE u32 dwc2_hcd_urb_get_error_count(struct dwc2_hcd_urb *dwc2_urb)
+static inline u32 dwc2_hcd_urb_get_error_count(struct dwc2_hcd_urb *dwc2_urb)
 {
        return dwc2_urb->error_count;
 }
 
-STATIC_INLINE void dwc2_hcd_urb_set_iso_desc_params(
+static inline void dwc2_hcd_urb_set_iso_desc_params(
                struct dwc2_hcd_urb *dwc2_urb, int desc_num, u32 offset,
                u32 length)
 {
@@ -612,31 +627,31 @@ STATIC_INLINE void dwc2_hcd_urb_set_iso_desc_params(
        dwc2_urb->iso_descs[desc_num].length = length;
 }
 
-STATIC_INLINE u32 dwc2_hcd_urb_get_iso_desc_status(
+static inline u32 dwc2_hcd_urb_get_iso_desc_status(
                struct dwc2_hcd_urb *dwc2_urb, int desc_num)
 {
        return dwc2_urb->iso_descs[desc_num].status;
 }
 
-STATIC_INLINE u32 dwc2_hcd_urb_get_iso_desc_actual_length(
+static inline u32 dwc2_hcd_urb_get_iso_desc_actual_length(
                struct dwc2_hcd_urb *dwc2_urb, int desc_num)
 {
        return dwc2_urb->iso_descs[desc_num].actual_length;
 }
 
-STATIC_INLINE int dwc2_hcd_is_bandwidth_allocated(struct dwc2_hsotg *hsotg,
+static inline int dwc2_hcd_is_bandwidth_allocated(struct dwc2_hsotg *hsotg,
                                                  struct usbd_xfer *xfer)
 {
        struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer);
        struct dwc2_qh *qh = dpipe->priv;
 
-       if (qh && qh->linked)
+       if (qh && !list_empty(&qh->qh_list_entry))
                return 1;
 
        return 0;
 }
 
-STATIC_INLINE u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
+static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
                                            struct dwc2_pipe *dpipe)
 {
        struct dwc2_qh *qh = dpipe->priv;
@@ -672,9 +687,6 @@ extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
  */
 extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
 
-extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
-
 /**
  * dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
  * and 0 otherwise
@@ -683,13 +695,6 @@ extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
  */
 extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
 
-/**
- * dwc2_hcd_get_frame_number() - Returns current frame number
- *
- * @hsotg: The DWC2 HCD
- */
-extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
-
 /**
  * dwc2_hcd_dump_state() - Dumps hsotg state
  *
@@ -748,7 +753,7 @@ do {                                                                        \
                           qtd_list_entry);                             \
        if (usb_pipeint(_qtd_->urb->pipe) &&                            \
            (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) { \
-               _hfnum_.d32 = DWC2_READ_4(hsotg, (_hcd_)->regs + HFNUM);                \
+               _hfnum_.d32 = DWC2_READ_4((_hcd_), HFNUM);              \
                switch (_hfnum_.b.frnum & 0x7) {                        \
                case 7:                                                 \
                        (_hcd_)->hfnum_7_samples_##_letter_++;          \
@@ -779,15 +784,12 @@ 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 *, struct dwc2_hcd_urb *, void **,
-                        gfp_t);
+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);
 
-void dwc2_conn_id_status_change(void *);
-void dwc2_hcd_start_func(void *);
-void dwc2_hcd_reset_func(void *);
-
 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);
 
index 4355fd2..de4b0d0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcdddma.c,v 1.16 2020/03/21 12:08:31 patrick Exp $       */
+/*     $OpenBSD: dwc2_hcdddma.c,v 1.17 2021/07/22 18:32:33 mglocker Exp $      */
 /*     $NetBSD: dwc2_hcdddma.c,v 1.6 2014/04/03 06:34:58 skrll Exp $   */
 
 /*
@@ -45,9 +45,6 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
-#if 0
-#include <sys/cpu.h>
-#endif
 
 #include <machine/bus.h>
 
@@ -99,32 +96,34 @@ STATIC int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 {
        int err;
 
-       //KASSERT(!cpu_intr_p() && !cpu_softintr_p());
-
        qh->desc_list = NULL;
-       err = usb_allocmem(&hsotg->hsotg_sc->sc_bus,
-           sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh), 0,
-           USB_DMA_COHERENT, &qh->desc_list_usbdma);
+       qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
+                                               dwc2_max_desc_num(qh);
 
-       if (!err) {
-               qh->desc_list = KERNADDR(&qh->desc_list_usbdma, 0);
-               qh->desc_list_dma = DMAADDR(&qh->desc_list_usbdma, 0);
-       }
+       err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, qh->desc_list_sz, 0,
+           USB_DMA_COHERENT, &qh->desc_list_usbdma);
 
-       if (!qh->desc_list)
+       if (err)
                return -ENOMEM;
 
-       memset(qh->desc_list, 0,
-              sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh));
+       qh->desc_list = KERNADDR(&qh->desc_list_usbdma, 0);
+       qh->desc_list_dma = DMAADDR(&qh->desc_list_usbdma, 0);
 
-       qh->n_bytes = mallocarray(dwc2_max_desc_num(qh), sizeof(u32), M_DEVBUF,
+       qh->n_bytes = malloc(sizeof(u32) * dwc2_max_desc_num(qh), M_DEVBUF,
            M_ZERO | M_WAITOK);
 
+       if (!qh->n_bytes) {
+               usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->desc_list_usbdma);
+               qh->desc_list = NULL;
+               return -ENOMEM;
+       }
+
        return 0;
 }
 
 STATIC void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
+
        if (qh->desc_list) {
                usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->desc_list_usbdma);
                qh->desc_list = NULL;
@@ -141,9 +140,10 @@ STATIC int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags)
        if (hsotg->frame_list)
                return 0;
 
-       /* XXXNH - struct pool */
+       /* XXXNH - pool_cache_t */
+       hsotg->frame_list_sz = 4 * FRLISTEN_64_SIZE;
        hsotg->frame_list = NULL;
-       err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, 4 * FRLISTEN_64_SIZE,
+       err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, hsotg->frame_list_sz,
            0, USB_DMA_COHERENT, &hsotg->frame_list_usbdma);
 
        if (!err) {
@@ -154,7 +154,6 @@ STATIC int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags)
        if (!hsotg->frame_list)
                return -ENOMEM;
 
-       memset(hsotg->frame_list, 0, 4 * FRLISTEN_64_SIZE);
        return 0;
 }
 
@@ -265,6 +264,13 @@ STATIC void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                j = (j + inc) & (FRLISTEN_64_SIZE - 1);
        } while (j != i);
 
+       /*
+        * Sync frame list since controller will access it if periodic
+        * channel is currently enabled.
+        */
+       usb_syncmem(&hsotg->frame_list_usbdma, 0, hsotg->frame_list_sz,
+           BUS_DMASYNC_PREWRITE);
+
        if (!enable)
                return;
 
@@ -294,6 +300,7 @@ STATIC void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
                        hsotg->non_periodic_channels--;
        } else {
                dwc2_update_frame_list(hsotg, qh, 0);
+               hsotg->available_host_channels++;
        }
 
        /*
@@ -301,12 +308,11 @@ STATIC void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
         * device disconnect. See channel cleanup in dwc2_hcd_disconnect().
         */
        if (chan->qh) {
-               if (chan->in_freelist != 0)
-                       LIST_REMOVE(chan, hc_list_entry);
+               if (!list_empty(&chan->hc_list_entry))
+                       list_del(&chan->hc_list_entry);
                dwc2_hc_cleanup(hsotg, chan);
-               LIST_INSERT_HEAD(&hsotg->free_hc_list, chan, hc_list_entry);
+               list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
                chan->qh = NULL;
-               chan->in_freelist = 1;
        }
 
        qh->channel = NULL;
@@ -377,6 +383,8 @@ err0:
  */
 void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
+       unsigned long flags;
+
        dwc2_desc_list_free(hsotg, qh);
 
        /*
@@ -386,8 +394,10 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
         * when it comes here from endpoint disable routine
         * channel remains assigned.
         */
+       spin_lock_irqsave(&hsotg->lock, flags);
        if (qh->channel)
                dwc2_release_channel_ddma(hsotg, qh);
+       spin_unlock_irqrestore(&hsotg->lock, flags);
 
        if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
             qh->ep_type == USB_ENDPOINT_XFER_INT) &&
@@ -541,14 +551,22 @@ STATIC void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
        dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT &
                           HOST_DMA_ISOC_NBYTES_MASK;
 
+       /* Set active bit */
+       dma_desc->status |= HOST_DMA_A;
+
+       qh->ntd++;
+       qtd->isoc_frame_index_last++;
+
 #ifdef ISOC_URB_GIVEBACK_ASAP
        /* Set IOC for each descriptor corresponding to last frame of URB */
        if (qtd->isoc_frame_index_last == qtd->urb->packet_count)
                dma_desc->status |= HOST_DMA_IOC;
 #endif
 
-       qh->ntd++;
-       qtd->isoc_frame_index_last++;
+       usb_syncmem(&qh->desc_list_usbdma,
+           (idx * sizeof(struct dwc2_hcd_dma_desc)),
+           sizeof(struct dwc2_hcd_dma_desc),
+           BUS_DMASYNC_PREWRITE);
 }
 
 STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
@@ -556,11 +574,32 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 {
        struct dwc2_qtd *qtd;
        u32 max_xfer_size;
-       u16 idx, inc, n_desc, ntd_max = 0;
+       u16 idx, inc, n_desc = 0, ntd_max = 0;
+       u16 cur_idx;
+       u16 next_idx;
 
        idx = qh->td_last;
        inc = qh->interval;
-       n_desc = 0;
+       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);
+
+       /*
+        * Ensure current frame number didn't overstep last scheduled
+        * descriptor. If it happens, the only way to recover is to move
+        * qh->td_last to current frame number + 1.
+        * So that next isoc descriptor will be scheduled on frame number + 1
+        * and not on a past frame.
+        */
+       if (dwc2_frame_idx_num_gt(cur_idx, next_idx) || (cur_idx == next_idx)) {
+               if (inc < 32) {
+                       dev_vdbg(hsotg->dev,
+                                "current frame number overstep last descriptor\n");
+                       qh->td_last = dwc2_desclist_idx_inc(cur_idx, inc,
+                                                           qh->dev_speed);
+                       idx = qh->td_last;
+               }
+       }
 
        if (qh->interval) {
                ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
@@ -572,16 +611,21 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
        max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
                        MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS;
 
-       TAILQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
+       list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) {
+               if (qtd->in_process &&
+                   qtd->isoc_frame_index_last ==
+                   qtd->urb->packet_count)
+                       continue;
+
+               qtd->isoc_td_first = idx;
                while (qh->ntd < ntd_max && qtd->isoc_frame_index_last <
                                                qtd->urb->packet_count) {
-                       if (n_desc > 1)
-                               qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
                        dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh,
                                                     max_xfer_size, idx);
                        idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed);
                        n_desc++;
                }
+               qtd->isoc_td_last = idx;
                qtd->in_process = 1;
        }
 
@@ -592,6 +636,11 @@ 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),
+                   BUS_DMASYNC_PREWRITE);
        }
 #else
        /*
@@ -621,13 +670,11 @@ STATIC void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                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),
+           BUS_DMASYNC_PREWRITE);
 #endif
-
-       if (n_desc) {
-               qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
-               if (n_desc > 1)
-                       qh->desc_list[0].status |= HOST_DMA_A;
-       }
 }
 
 STATIC void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
@@ -664,6 +711,11 @@ 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),
+           BUS_DMASYNC_PREWRITE);
+
        /*
         * Last (or only) descriptor of IN transfer with actual size less
         * than MaxPacket
@@ -693,7 +745,7 @@ STATIC void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
         * there is always one QTD active.
         */
 
-       TAILQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
+       list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) {
                dev_vdbg(hsotg->dev, "qtd=%p\n", qtd);
 
                if (n_desc) {
@@ -714,6 +766,11 @@ STATIC void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                                         "set A bit 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),
+                                   BUS_DMASYNC_PREWRITE);
                        }
                        dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
                        dev_vdbg(hsotg->dev,
@@ -739,10 +796,17 @@ STATIC void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
                                HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A;
                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),
+                   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),
+                           BUS_DMASYNC_PREWRITE);
                }
                chan->ntd = n_desc;
        }
@@ -817,7 +881,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 = &qh->desc_list[idx];
+       struct dwc2_hcd_dma_desc *dma_desc;
        struct dwc2_hcd_iso_packet_desc *frame_desc;
        u16 remain = 0;
        int rc = 0;
@@ -825,6 +889,13 @@ STATIC int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
        if (!qtd->urb)
                return -EINVAL;
 
+       usb_syncmem(&qh->desc_list_usbdma,
+           (idx * sizeof(struct dwc2_hcd_dma_desc)),
+           sizeof(struct dwc2_hcd_dma_desc),
+           BUS_DMASYNC_POSTREAD);
+
+       dma_desc = &qh->desc_list[idx];
+
        frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
        dma_desc->buf = (u32)(DMAADDR(qtd->urb->usbdma, frame_desc->offset));
        if (chan->ep_is_in)
@@ -888,7 +959,7 @@ STATIC void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
        idx = qh->td_first;
 
        if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) {
-               TAILQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry)
+               list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry)
                        qtd->in_process = 0;
                return;
        }
@@ -907,7 +978,8 @@ STATIC void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
                int err = halt_status == DWC2_HC_XFER_AHB_ERR ?
                          -EIO : -EOVERFLOW;
 
-               TAILQ_FOREACH_SAFE(qtd, &qh->qtd_list, qtd_list_entry, qtd_tmp) {
+               list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
+                                        qtd_list_entry) {
                        if (qtd->urb) {
                                for (idx = 0; idx < qtd->urb->packet_count;
                                     idx++) {
@@ -924,20 +996,54 @@ STATIC void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
                return;
        }
 
-       TAILQ_FOREACH_SAFE(qtd, &qh->qtd_list, qtd_list_entry, qtd_tmp) {
+       list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
                if (!qtd->in_process)
                        break;
+
+               /*
+                * Ensure idx corresponds to descriptor where first urb of this
+                * qtd was added. In fact, during isoc desc init, dwc2 may skip
+                * an index if current frame number is already over this index.
+                */
+               if (idx != qtd->isoc_td_first) {
+                       dev_vdbg(hsotg->dev,
+                                "try to complete %d instead of %d\n",
+                                idx, qtd->isoc_td_first);
+                       idx = qtd->isoc_td_first;
+               }
+
                do {
+                       struct dwc2_qtd *qtd_next;
+                       u16 cur_idx;
+
                        rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh,
                                                          idx);
                        if (rc < 0)
                                return;
                        idx = dwc2_desclist_idx_inc(idx, qh->interval,
                                                    chan->speed);
-                       if (rc == DWC2_CMPL_STOP)
-                               goto stop_scan;
+                       if (!rc)
+                               continue;
+
                        if (rc == DWC2_CMPL_DONE)
                                break;
+
+                       /* rc == DWC2_CMPL_STOP */
+
+                       if (qh->interval >= 32)
+                               goto stop_scan;
+
+                       qh->td_first = idx;
+                       cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
+                       qtd_next = list_first_entry(&qh->qtd_list,
+                                                   struct dwc2_qtd,
+                                                   qtd_list_entry);
+                       if (dwc2_frame_idx_num_gt(cur_idx,
+                                                 qtd_next->isoc_td_last))
+                               break;
+
+                       goto stop_scan;
+
                } while (idx != qh->td_first);
        }
 
@@ -1045,6 +1151,11 @@ STATIC int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
        if (!urb)
                return -EINVAL;
 
+       usb_syncmem(&qh->desc_list_usbdma,
+           (desc_num * sizeof(struct dwc2_hcd_dma_desc)),
+           sizeof(struct dwc2_hcd_dma_desc),
+           BUS_DMASYNC_POSTREAD);
+
        dma_desc = &qh->desc_list[desc_num];
        n_bytes = qh->n_bytes[desc_num];
        dev_vdbg(hsotg->dev,
@@ -1053,7 +1164,10 @@ 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 (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
+       if (*xfer_done && urb->status != -EINPROGRESS)
+               failed = 1;
+
+       if (failed) {
                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",
@@ -1098,20 +1212,22 @@ STATIC void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
                                             int chnum,
                                             enum dwc2_halt_status halt_status)
 {
+       struct list_head *qtd_item, *qtd_tmp;
        struct dwc2_qh *qh = chan->qh;
-       struct dwc2_qtd *qtd = NULL, *qtd_tmp;
+       struct dwc2_qtd *qtd = NULL;
        int xfer_done;
        int desc_num = 0;
 
        if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) {
-               TAILQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry)
+               list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry)
                        qtd->in_process = 0;
                return;
        }
 
-       TAILQ_FOREACH_SAFE(qtd, &qh->qtd_list, qtd_list_entry, qtd_tmp) {
+       list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) {
                int i;
 
+               qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry);
                xfer_done = 0;
 
                for (i = 0; i < qtd->n_desc; i++) {
@@ -1178,7 +1294,22 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
 
                /* Release the channel if halted or session completed */
                if (halt_status != DWC2_HC_XFER_COMPLETE ||
-                   TAILQ_EMPTY(&qh->qtd_list)) {
+                   list_empty(&qh->qtd_list)) {
+                       struct dwc2_qtd *qtd, *qtd_tmp;
+
+                       /*
+                        * Kill all remainings QTDs since channel has been
+                        * halted.
+                        */
+                       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);
+                       }
+
                        /* Halt the channel if session completed */
                        if (halt_status == DWC2_HC_XFER_COMPLETE)
                                dwc2_hc_halt(hsotg, chan, halt_status);
@@ -1186,9 +1317,14 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
                        dwc2_hcd_qh_unlink(hsotg, qh);
                } else {
                        /* Keep in assigned schedule to continue transfer */
-                       TAILQ_REMOVE(&hsotg->periodic_sched_queued, qh, qh_list_entry);
-                       TAILQ_INSERT_TAIL(&hsotg->periodic_sched_assigned, qh, qh_list_entry);
-                       continue_isoc_xfer = 1;
+                       list_move(&qh->qh_list_entry,
+                                 &hsotg->periodic_sched_assigned);
+                       /*
+                        * If channel has been halted during giveback of urb
+                        * then prevent any new scheduling.
+                        */
+                       if (!chan->halt_status)
+                               continue_isoc_xfer = 1;
                }
                /*
                 * Todo: Consider the case when period exceeds FrameList size.
@@ -1204,7 +1340,7 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
                dwc2_release_channel_ddma(hsotg, qh);
                dwc2_hcd_qh_unlink(hsotg, qh);
 
-               if (!TAILQ_EMPTY(&qh->qtd_list)) {
+               if (!list_empty(&qh->qtd_list)) {
                        /*
                         * Add back to inactive non-periodic schedule on normal
                         * completion
index 49e020a..3e17e34 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcdintr.c,v 1.10 2017/09/08 05:36:53 deraadt Exp $       */
+/*     $OpenBSD: dwc2_hcdintr.c,v 1.11 2021/07/22 18:32:33 mglocker Exp $      */
 /*     $NetBSD: dwc2_hcdintr.c,v 1.11 2014/11/24 10:14:14 skrll Exp $  */
 
 /*
 #include <dev/usb/dwc2/dwc2_core.h>
 #include <dev/usb/dwc2/dwc2_hcd.h>
 
+/*
+ * If we get this many NAKs on a split transaction we'll slow down
+ * 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)
 {
@@ -119,9 +129,13 @@ STATIC void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
  */
 STATIC void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 {
-       struct dwc2_qh *qh, *qhn;
+       struct list_head *qh_entry;
+       struct dwc2_qh *qh;
        enum dwc2_transaction_type tr_type;
 
+       /* Clear interrupt */
+       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SOF);
+
 #ifdef DEBUG_SOF
        dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
 #endif
@@ -131,25 +145,21 @@ STATIC void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
        dwc2_track_missed_sofs(hsotg);
 
        /* Determine whether any periodic QHs should be executed */
-       qh = TAILQ_FIRST(&hsotg->periodic_sched_inactive);
-       while (qh != NULL) {
-               qhn = TAILQ_NEXT(qh, qh_list_entry);
-               if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
+       qh_entry = hsotg->periodic_sched_inactive.next;
+       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))
                        /*
                         * Move QH to the ready list to be executed next
                         * (micro)frame
                         */
-                       TAILQ_REMOVE(&hsotg->periodic_sched_inactive, qh, qh_list_entry);
-                       TAILQ_INSERT_TAIL(&hsotg->periodic_sched_ready, qh, qh_list_entry);
-               }
-               qh = qhn;
+                       list_move(&qh->qh_list_entry,
+                                 &hsotg->periodic_sched_ready);
        }
        tr_type = dwc2_hcd_select_transactions(hsotg);
        if (tr_type != DWC2_TRANSACTION_NONE)
                dwc2_hcd_queue_transactions(hsotg, tr_type);
-
-       /* Clear interrupt */
-       DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SOF);
 }
 
 /*
@@ -315,6 +325,7 @@ STATIC void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
 
        if (do_reset) {
                *hprt0_modify |= HPRT0_RST;
+               DWC2_WRITE_4(hsotg, HPRT0, *hprt0_modify);
                queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
                                   msecs_to_jiffies(60));
        } else {
@@ -352,12 +363,12 @@ 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);
+
                dev_vdbg(hsotg->dev,
                         "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
                         hprt0);
-               hsotg->flags.b.port_connect_status_change = 1;
-               hsotg->flags.b.port_connect_status = 1;
-               hprt0_modify |= HPRT0_CONNDET;
+               dwc2_hcd_connect(hsotg);
 
                /*
                 * The Hub driver asserts a reset when it sees port connect
@@ -370,28 +381,36 @@ 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);
                dev_vdbg(hsotg->dev,
                         "  --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
                         hprt0, !!(hprt0 & HPRT0_ENA));
-               hprt0_modify |= HPRT0_ENACHG;
-               if (hprt0 & HPRT0_ENA)
+               if (hprt0 & HPRT0_ENA) {
+                       hsotg->new_connection = true;
                        dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
-               else
+               } else {
                        hsotg->flags.b.port_enable_change = 1;
+                       if (hsotg->core_params->dma_desc_fs_enable) {
+                               u32 hcfg;
+
+                               hsotg->core_params->dma_desc_enable = 0;
+                               hsotg->new_connection = false;
+                               hcfg = DWC2_READ_4(hsotg, HCFG);
+                               hcfg &= ~HCFG_DESCDMA;
+                               DWC2_WRITE_4(hsotg, HCFG, hcfg);
+                       }
+               }
        }
 
        /* Overcurrent Change Interrupt */
        if (hprt0 & HPRT0_OVRCURRCHG) {
+               DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_OVRCURRCHG);
                dev_vdbg(hsotg->dev,
                         "  --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
                         hprt0);
                hsotg->flags.b.port_over_current_change = 1;
-               hprt0_modify |= HPRT0_OVRCURRCHG;
        }
 
-       /* Clear Port Interrupts */
-       DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify);
-
        if (hsotg->flags.b.port_connect_status_change ||
            hsotg->flags.b.port_enable_change ||
            hsotg->flags.b.port_over_current_change)
@@ -472,12 +491,17 @@ STATIC int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
        }
 
        /* Non DWORD-aligned buffer case handling */
-       if (chan->align_buf && xfer_length && chan->ep_is_in) {
+       if (chan->align_buf && xfer_length) {
                dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-               usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_POSTREAD);
-               memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
-                      xfer_length);
-               usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_PREREAD);
+               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",
@@ -562,17 +586,22 @@ STATIC enum dwc2_halt_status dwc2_update_isoc_urb_state(
                                        chan, chnum, qtd, halt_status, NULL);
 
                /* Non DWORD-aligned buffer case handling */
-               if (chan->align_buf && frame_desc->actual_length &&
-                   chan->ep_is_in) {
+               if (chan->align_buf && frame_desc->actual_length) {
                        dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
                                 __func__);
-                       usb_syncmem(urb->usbdma, 0, urb->length,
-                                   BUS_DMASYNC_POSTREAD);
-                       memcpy(urb->buf + frame_desc->offset +
-                              qtd->isoc_split_offset, chan->qh->dw_align_buf,
-                              frame_desc->actual_length);
-                       usb_syncmem(urb->usbdma, 0, urb->length,
-                                   BUS_DMASYNC_PREREAD);
+                       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:
@@ -595,17 +624,22 @@ STATIC enum dwc2_halt_status dwc2_update_isoc_urb_state(
                                        chan, chnum, qtd, halt_status, NULL);
 
                /* Non DWORD-aligned buffer case handling */
-               if (chan->align_buf && frame_desc->actual_length &&
-                   chan->ep_is_in) {
+               if (chan->align_buf && frame_desc->actual_length) {
                        dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
                                 __func__);
-                       usb_syncmem(urb->usbdma, 0, urb->length,
-                                   BUS_DMASYNC_POSTREAD);
-                       memcpy(urb->buf + frame_desc->offset +
-                              qtd->isoc_split_offset, chan->qh->dw_align_buf,
-                              frame_desc->actual_length);
-                       usb_syncmem(urb->usbdma, 0, urb->length,
-                                   BUS_DMASYNC_PREREAD);
+                       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 */
@@ -654,12 +688,12 @@ STATIC void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                dev_vdbg(hsotg->dev, "  %s(%p,%p,%d)\n", __func__,
                         hsotg, qh, free_qtd);
 
-       if (TAILQ_EMPTY(&qh->qtd_list)) {
+       if (list_empty(&qh->qtd_list)) {
                dev_dbg(hsotg->dev, "## QTD list empty ##\n");
                goto no_qtd;
        }
 
-       qtd = TAILQ_FIRST(&qh->qtd_list);
+       qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry);
 
        if (qtd->complete_split)
                continue_split = 1;
@@ -675,8 +709,8 @@ STATIC void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 no_qtd:
        if (qh->channel)
                qh->channel->align_buf = 0;
-       dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
        qh->channel = NULL;
+       dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
 }
 
 /**
@@ -747,11 +781,10 @@ cleanup:
         * function clears the channel interrupt enables and conditions, so
         * there's no need to clear the Channel Halted interrupt separately.
         */
-       if (chan->in_freelist != 0)
-               LIST_REMOVE(chan, hc_list_entry);
+       if (!list_empty(&chan->hc_list_entry))
+               list_del(&chan->hc_list_entry);
        dwc2_hc_cleanup(hsotg, chan);
-       LIST_INSERT_HEAD(&hsotg->free_hc_list, chan, hc_list_entry);
-       chan->in_freelist = 1;
+       list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
 
        if (hsotg->core_params->uframe_sched > 0) {
                hsotg->available_host_channels++;
@@ -832,8 +865,8 @@ STATIC void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
                         * halt to be queued when the periodic schedule is
                         * processed.
                         */
-                       TAILQ_REMOVE(&hsotg->periodic_sched_queued, chan->qh, qh_list_entry);
-                       TAILQ_INSERT_TAIL(&hsotg->periodic_sched_assigned, chan->qh, qh_list_entry);
+                       list_move(&chan->qh->qh_list_entry,
+                                 &hsotg->periodic_sched_assigned);
 
                        /*
                         * Make sure the Periodic Tx FIFO Empty interrupt is
@@ -942,12 +975,12 @@ STATIC int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
 
        if (chan->align_buf) {
                dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-               usb_syncmem(qtd->urb->usbdma, 0, qtd->urb->length,
-                           BUS_DMASYNC_POSTREAD);
+               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 +
                       qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
-               usb_syncmem(qtd->urb->usbdma, 0, qtd->urb->length,
-                           BUS_DMASYNC_PREREAD);
+               usb_syncmem(qtd->urb->usbdma, chan->qh->dw_align_buf_dma,
+                   chan->qh->dw_align_buf_size, BUS_DMASYNC_PREREAD);
        }
 
        qtd->isoc_split_offset += len;
@@ -1174,10 +1207,19 @@ STATIC void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
        /* 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__);
-               usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_POSTREAD);
-               memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
-                      xfer_length);
-               usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_PREREAD);
+
+               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;
@@ -1205,6 +1247,16 @@ STATIC void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
                             struct dwc2_host_chan *chan, int chnum,
                             struct dwc2_qtd *qtd)
 {
+       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;
+       }
+
        if (dbg_hc(chan))
                dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n",
                         chnum);
@@ -1212,6 +1264,16 @@ STATIC void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
        /*
         * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
         * interrupt. Re-start the SSPLIT transfer.
+        *
+        * Normally for non-periodic transfers we'll retry right away, but to
+        * avoid interrupt storms we'll wait before retrying if we've got
+        * several NAKs. If we didn't do this we'd retry directly from the
+        * interrupt handler and could end up quickly getting another
+        * interrupt (another NAK), which we'd retry.
+        *
+        * Note that in DMA mode software only gets involved to re-send NAKed
+        * transfers for split transactions unless the core is missing OUT NAK
+        * enhancement.
         */
        if (chan->do_split) {
                /*
@@ -1228,6 +1290,8 @@ STATIC void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
                if (chan->complete_split)
                        qtd->error_count = 0;
                qtd->complete_split = 0;
+               qtd->num_naks++;
+               qtd->qh->want_wait = qtd->num_naks >= dwc2_naks_before_delay;
                dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
                goto handle_nak_done;
        }
@@ -1253,6 +1317,13 @@ STATIC void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
                 */
                qtd->error_count = 0;
 
+               if (hsotg->core_params->dma_enable > 0 && !chan->ep_is_in) {
+                       /*
+                        * Avoid interrupt storms.
+                        */
+                       qtd->num_naks++;
+                       qtd->qh->want_wait = qtd->num_naks >= dwc2_out_naks_before_delay;
+               }
                if (!chan->qh->ping_state) {
                        dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
                                                  qtd, DWC2_HC_XFER_NAK);
@@ -1909,10 +1980,10 @@ STATIC void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
                         "NYET/NAK/ACK/other in non-error case, 0x%08x\n",
                         chan->hcint);
 error:
-               /* use the 3-strikes rule */
+               /* Failthrough: use 3-strikes rule */
                qtd->error_count++;
                dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
-                                           qtd, DWC2_HC_XFER_XACT_ERR);
+                                         qtd, DWC2_HC_XFER_XACT_ERR);
                dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
                dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
        }
@@ -1954,10 +2025,14 @@ STATIC void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg,
  */
 STATIC bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh)
 {
-       if (!qh)
+       struct dwc2_qtd *cur_head;
+
+       if (qh == NULL)
                return false;
 
-       return (TAILQ_FIRST(&qh->qtd_list) == qtd);
+       cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd,
+                                   qtd_list_entry);
+       return (cur_head == qtd);
 }
 
 /* Handles interrupt for a specific Host Channel */
@@ -2009,7 +2084,7 @@ STATIC void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
                return;
        }
 
-       if (TAILQ_EMPTY(&chan->qh->qtd_list)) {
+       if (list_empty(&chan->qh->qtd_list)) {
                /*
                 * TODO: Will this ever happen with the
                 * DWC2_HC_XFER_URB_DEQUEUE handling above?
@@ -2025,7 +2100,8 @@ STATIC void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
                return;
        }
 
-       qtd = TAILQ_FIRST(&chan->qh->qtd_list);
+       qtd = list_first_entry(&chan->qh->qtd_list, struct dwc2_qtd,
+                              qtd_list_entry);
 
        if (hsotg->core_params->dma_enable <= 0) {
                if ((hcint & HCINTMSK_CHHLTD) && hcint != HCINTMSK_CHHLTD)
index 94452e8..ba5bb61 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hcdqueue.c,v 1.9 2017/09/08 05:36:53 deraadt Exp $       */
+/*     $OpenBSD: dwc2_hcdqueue.c,v 1.10 2021/07/22 18:32:33 mglocker Exp $     */
 /*     $NetBSD: dwc2_hcdqueue.c,v 1.11 2014/09/03 10:00:08 skrll Exp $ */
 
 /*
 #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 */
 
 /**
  * dwc2_qh_init() - Initializes a QH structure
@@ -79,13 +83,16 @@ STATIC void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
        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);
-       TAILQ_INIT(&qh->qtd_list);
-       qh->linked = 0;
+       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);
@@ -115,6 +122,9 @@ STATIC void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                                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);
@@ -203,7 +213,7 @@ STATIC void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
  *
  * Return: Pointer to the newly allocated QH, or NULL on error
  */
-STATIC struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
                                          struct dwc2_hcd_urb *urb,
                                          gfp_t mem_flags)
 {
@@ -245,11 +255,21 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
        struct dwc2_softc *sc = hsotg->hsotg_sc;
 
-       if (hsotg->core_params->dma_desc_enable > 0) {
+       /*
+        * 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); */
+
+       timeout_del(&qh->wait_timer);
+       if (qh->desc_list) {
                dwc2_hcd_qh_free_ddma(hsotg, qh);
        } else if (qh->dw_align_buf) {
-               /* XXXNH */
-               usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->dw_align_buf_usbdma);
+               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);
@@ -533,11 +553,11 @@ STATIC int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
        if (hsotg->core_params->dma_desc_enable > 0)
                /* Don't rely on SOF and start in ready schedule */
-               TAILQ_INSERT_TAIL(&hsotg->periodic_sched_ready, qh, qh_list_entry);
+               list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
        else
                /* Always start in inactive schedule */
-               TAILQ_INSERT_TAIL(&hsotg->periodic_sched_inactive, qh, qh_list_entry);
-       qh->linked = 1;
+               list_add_tail(&qh->qh_list_entry,
+                             &hsotg->periodic_sched_inactive);
 
        if (hsotg->core_params->uframe_sched <= 0)
                /* Reserve periodic channel */
@@ -561,7 +581,7 @@ STATIC void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 {
        int i;
 
-       qh->linked = 0;
+       list_del_init(&qh->qh_list_entry);
 
        /* Update claimed usecs per (micro)frame */
        hsotg->periodic_usecs -= qh->usecs;
@@ -577,6 +597,55 @@ STATIC void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
        }
 }
 
+/**
+ * dwc2_wait_timer_fn() - Timer function to re-queue after waiting
+ *
+ * As per the spec, a NAK indicates that "a function is temporarily unable to
+ * transmit or receive data, but will eventually be able to do so without need
+ * of host intervention".
+ *
+ * That means that when we encounter a NAK we're supposed to retry.
+ *
+ * ...but if we retry right away (from the interrupt handler that saw the NAK)
+ * then we can end up with an interrupt storm (if the other side keeps NAKing
+ * us) because on slow enough CPUs it could take us longer to get out of the
+ * interrupt routine than it takes for the device to send another NAK.  That
+ * leads to a constant stream of NAK interrupts and the CPU locks.
+ *
+ * ...so instead of retrying right away in the case of a NAK we'll set a timer
+ * to retry some time later.  This function handles that timer and moves the
+ * qh back to the "inactive" list, then queues transactions.
+ *
+ * @t: Pointer to wait_timer in a qh.
+ */
+STATIC void dwc2_wait_timer_fn(void *arg)
+{
+       struct dwc2_qh *qh = arg;
+       struct dwc2_hsotg *hsotg = qh->hsotg;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       /*
+        * We'll set wait_timer_cancel to true if we want to cancel this
+        * operation in dwc2_hcd_qh_unlink().
+        */
+       if (!qh->wait_timer_cancel) {
+               enum dwc2_transaction_type tr_type;
+
+               qh->want_wait = false;
+
+               list_move(&qh->qh_list_entry,
+                         &hsotg->non_periodic_sched_inactive);
+
+               tr_type = dwc2_hcd_select_transactions(hsotg);
+               if (tr_type != DWC2_TRANSACTION_NONE)
+                       dwc2_hcd_queue_transactions(hsotg, tr_type);
+       }
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
 /**
  * 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
@@ -595,18 +664,35 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
        if (dbg_qh(qh))
                dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
-       if (qh->linked != 0) {
+       if (!list_empty(&qh->qh_list_entry))
                /* 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)) {
-               /* Always start in inactive schedule */
-               TAILQ_INSERT_TAIL(&hsotg->non_periodic_sched_inactive, qh, qh_list_entry);
-               qh->linked = 1;
+               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 {
+                       list_add_tail(&qh->qh_list_entry,
+                                     &hsotg->non_periodic_sched_inactive);
+               }
                return 0;
        }
+
        status = dwc2_schedule_periodic(hsotg, qh);
        if (status)
                return status;
@@ -633,28 +719,21 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
        dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
-       if (qh->linked == 0) {
+       /* If the wait_timer is pending, this will stop it from acting */
+       qh->wait_timer_cancel = true;
+
+       if (list_empty(&qh->qh_list_entry))
                /* QH is not in a schedule */
                return;
-       }
 
        if (dwc2_qh_is_non_per(qh)) {
-               if (hsotg->non_periodic_qh_ptr == qh) {
+               if (hsotg->non_periodic_qh_ptr == &qh->qh_list_entry)
                        hsotg->non_periodic_qh_ptr =
-                                       TAILQ_NEXT(qh, qh_list_entry);
-               }
-
-               if (qh->channel) {
-                       TAILQ_REMOVE(&hsotg->non_periodic_sched_active, qh, qh_list_entry);
-               } else {
-                       TAILQ_REMOVE(&hsotg->non_periodic_sched_inactive, qh, qh_list_entry);
-               }
-               qh->linked = 0;
-
-               if (hsotg->non_periodic_qh_ptr == NULL)
-                       hsotg->non_periodic_qh_ptr = TAILQ_FIRST(&hsotg->non_periodic_sched_active);
+                                       hsotg->non_periodic_qh_ptr->next;
+               list_del_init(&qh->qh_list_entry);
                return;
        }
+
        dwc2_deschedule_periodic(hsotg, qh);
        hsotg->periodic_qh_count--;
        if (!hsotg->periodic_qh_count) {
@@ -722,8 +801,8 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
        if (dwc2_qh_is_non_per(qh)) {
                dwc2_hcd_qh_unlink(hsotg, qh);
-               if (!TAILQ_EMPTY(&qh->qtd_list))
-                       /* Add back to inactive non-periodic schedule */
+               if (!list_empty(&qh->qtd_list))
+                       /* Add back to inactive/waiting non-periodic schedule */
                        dwc2_hcd_qh_add(hsotg, qh);
                return;
        }
@@ -740,7 +819,7 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                        qh->sched_frame = frame_number;
        }
 
-       if (TAILQ_EMPTY(&qh->qtd_list)) {
+       if (list_empty(&qh->qtd_list)) {
                dwc2_hcd_qh_unlink(hsotg, qh);
                return;
        }
@@ -748,15 +827,13 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
         * Remove from periodic_sched_queued and move to
         * appropriate queue
         */
-       TAILQ_REMOVE(&hsotg->periodic_sched_queued, qh, qh_list_entry);
        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)) {
-               TAILQ_INSERT_TAIL(&hsotg->periodic_sched_ready, qh, qh_list_entry);
-       } else {
-               TAILQ_INSERT_TAIL(&hsotg->periodic_sched_inactive, qh, qh_list_entry);
-       }
+            qh->sched_frame == frame_number))
+               list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
+       else
+               list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
 }
 
 /**
@@ -791,62 +868,39 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
 
 /**
  * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
+ *                     Caller must hold driver lock.
  *
- * @hsotg:     The DWC HCD structure
- * @qtd:       The QTD to add
- * @qh:        Out parameter to return queue head
- * @mem_flags: Flag to do atomic alloc if needed
+ * @hsotg:        The DWC HCD structure
+ * @qtd:          The QTD to add
+ * @qh:           Queue head to add qtd to
  *
  * Return: 0 if successful, negative error code otherwise
  *
- * Finds the correct QH to place the QTD into. If it does not find a QH, it
- * will create a new QH. If the QH to which the QTD is added is not currently
- * scheduled, it is placed into the proper schedule based on its EP type.
- *
- * HCD lock must be held and interrupts must be disabled on entry
+ * If the QH to which the QTD is added is not currently scheduled, it is placed
+ * into the proper schedule based on its EP type.
  */
 int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
-                    struct dwc2_qh **qh, gfp_t mem_flags)
+                    struct dwc2_qh *qh)
 {
-       struct dwc2_hcd_urb *urb = qtd->urb;
-       int allocated = 0;
+
+       MUTEX_ASSERT_LOCKED(&hsotg->lock);
        int retval;
 
-       /*
-        * Get the QH which holds the QTD-list to insert to. Create QH if it
-        * doesn't exist.
-        */
-       if (*qh == NULL) {
-               *qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags);
-               if (*qh == NULL)
-                       return -ENOMEM;
-               allocated = 1;
+       if (!qh) {
+               dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
+               retval = -EINVAL;
+               goto fail;
        }
 
-       retval = dwc2_hcd_qh_add(hsotg, *qh);
+       retval = dwc2_hcd_qh_add(hsotg, qh);
        if (retval)
                goto fail;
 
-       qtd->qh = *qh;
-       TAILQ_INSERT_TAIL(&(*qh)->qtd_list, qtd, qtd_list_entry);
+       qtd->qh = qh;
+       list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
 
        return 0;
-
 fail:
-       if (allocated) {
-               struct dwc2_qtd *qtd2, *qtd2_tmp;
-               struct dwc2_qh *qh_tmp = *qh;
-
-               *qh = NULL;
-               dwc2_hcd_qh_unlink(hsotg, qh_tmp);
-
-               /* Free each QTD in the QH's QTD list */
-               TAILQ_FOREACH_SAFE(qtd2, &qh_tmp->qtd_list, qtd_list_entry, qtd2_tmp)
-                       dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
-
-               dwc2_hcd_qh_free(hsotg, qh_tmp);
-       }
-
        return retval;
 }
 
@@ -856,7 +910,7 @@ void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
 {
        struct dwc2_softc *sc = hsotg->hsotg_sc;
 
-       TAILQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
+       list_del_init(&qtd->qtd_list_entry);
        pool_put(&sc->sc_qtdpool, qtd);
 }
 
index 8490802..05f53b6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2_hw.h,v 1.2 2015/02/10 12:58:47 uebayasi Exp $    */
+/*     $OpenBSD: dwc2_hw.h,v 1.3 2021/07/22 18:32:33 mglocker Exp $    */
 /*     $NetBSD: dwc2_hw.h,v 1.2 2013/09/25 06:19:22 skrll Exp $        */
 
 /*
 #define GUSBCFG_FSINTF                 (1 << 5)
 #define GUSBCFG_ULPI_UTMI_SEL          (1 << 4)
 #define GUSBCFG_PHYIF16                        (1 << 3)
+#define GUSBCFG_PHYIF8                 (0 << 3)
 #define GUSBCFG_TOUTCAL_MASK           (0x7 << 0)
 #define GUSBCFG_TOUTCAL_SHIFT          0
 #define GUSBCFG_TOUTCAL_LIMIT          0x7
 #define GINTSTS_RESETDET               (1 << 23)
 #define GINTSTS_FET_SUSP               (1 << 22)
 #define GINTSTS_INCOMPL_IP             (1 << 21)
+#define GINTSTS_INCOMPL_SOOUT          (1 << 21)
 #define GINTSTS_INCOMPL_SOIN           (1 << 20)
 #define GINTSTS_OEPINT                 (1 << 19)
 #define GINTSTS_IEPINT                 (1 << 18)
 #define GHWCFG4_NUM_IN_EPS_MASK                        (0xf << 26)
 #define GHWCFG4_NUM_IN_EPS_SHIFT               26
 #define GHWCFG4_DED_FIFO_EN                    (1 << 25)
+#define GHWCFG4_DED_FIFO_SHIFT         25
 #define GHWCFG4_SESSION_END_FILT_EN            (1 << 24)
 #define GHWCFG4_B_VALID_FILT_EN                        (1 << 23)
 #define GHWCFG4_A_VALID_FILT_EN                        (1 << 22)
 #define FIFOSIZE_DEPTH_SHIFT           16
 #define FIFOSIZE_STARTADDR_MASK                (0xffff << 0)
 #define FIFOSIZE_STARTADDR_SHIFT       0
+#define FIFOSIZE_DEPTH_GET(_x)         (((_x) >> 16) & 0xffff)
 
 /* Device mode registers */
 
 #define DXEPCTL_STALL                  (1 << 21)
 #define DXEPCTL_SNP                    (1 << 20)
 #define DXEPCTL_EPTYPE_MASK            (0x3 << 18)
-#define DXEPCTL_EPTYPE_SHIFT           18
-#define DXEPCTL_EPTYPE_CONTROL         0
-#define DXEPCTL_EPTYPE_ISO             1
-#define DXEPCTL_EPTYPE_BULK            2
-#define DXEPCTL_EPTYPE_INTTERUPT       3
+#define DXEPCTL_EPTYPE_CONTROL         (0x0 << 18)
+#define DXEPCTL_EPTYPE_ISO             (0x1 << 18)
+#define DXEPCTL_EPTYPE_BULK            (0x2 << 18)
+#define DXEPCTL_EPTYPE_INTERRUPT       (0x3 << 18)
+
 #define DXEPCTL_NAKSTS                 (1 << 17)
 #define DXEPCTL_DPID                   (1 << 16)
 #define DXEPCTL_EOFRNUM                        (1 << 16)
 
 #define DIEPINT(_a)                    HSOTG_REG(0x908 + ((_a) * 0x20))
 #define DOEPINT(_a)                    HSOTG_REG(0xB08 + ((_a) * 0x20))
+#define DXEPINT_SETUP_RCVD             (1 << 15)
 #define DXEPINT_INEPNAKEFF             (1 << 6)
 #define DXEPINT_BACK2BACKSETUP         (1 << 6)
 #define DXEPINT_INTKNEPMIS             (1 << 5)
 #define TSIZ_XFERSIZE_SHIFT            0
 
 #define HCDMA(_ch)                     HSOTG_REG(0x0514 + 0x20 * (_ch))
-#define HCDMA_DMA_ADDR_MASK            (0x1fffff << 11)
-#define HCDMA_DMA_ADDR_SHIFT           11
-#define HCDMA_CTD_MASK                 (0xff << 3)
-#define HCDMA_CTD_SHIFT                        3
 
 #define HCDMAB(_ch)                    HSOTG_REG(0x051c + 0x20 * (_ch))
 
index c00fe69..8cd76c7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dwc2var.h,v 1.19 2017/09/05 14:01:03 otto Exp $       */
+/*     $OpenBSD: dwc2var.h,v 1.20 2021/07/22 18:32:33 mglocker Exp $   */
 /*     $NetBSD: dwc2var.h,v 1.3 2013/10/22 12:57:40 skrll Exp $        */
 
 /*-
 #define        _DWC2VAR_H_
 
 #include <sys/pool.h>
+#include <sys/task.h>
 
-struct task;
-
-#define DWC2_MAXISOCPACKETS    16
 struct dwc2_hsotg;
 struct dwc2_qtd;
 
@@ -45,13 +43,12 @@ struct dwc2_xfer {
        struct usbd_xfer xfer;                  /* Needs to be first */
 
        struct dwc2_hcd_urb *urb;
-       int packet_count;
 
        TAILQ_ENTRY(dwc2_xfer) xnext;           /* list of complete xfers */
-
+       usbd_status intr_status;
        u_int32_t flags;
-#define DWC2_XFER_ABORTING     0x0001  /* xfer is aborting. */
-#define DWC2_XFER_ABORTWAIT    0x0002  /* abort completion is being awaited. */
+#define DWC2_XFER_ABORTING      0x0001  /* xfer is aborting. */
+#define DWC2_XFER_ABORTWAIT     0x0002  /* abort completion is being awaited. */
 };
 
 struct dwc2_pipe {
@@ -82,7 +79,7 @@ typedef struct dwc2_softc {
        bus_space_tag_t         sc_iot;
        bus_space_handle_t      sc_ioh;
        struct dwc2_core_params *sc_params;
-       struct dwc2_core_dma_config *sc_dma_config;
+       int                     (*sc_set_dma_addr)(struct device *, bus_addr_t, int);
 
        /*
         * Private
@@ -90,34 +87,29 @@ typedef struct dwc2_softc {
 
        struct dwc2_hsotg *sc_hsotg;
 
+       struct mutex sc_lock;
+
        bool sc_hcdenabled;
        void *sc_rhc_si;
 
        struct usbd_xfer *sc_intrxfer;
 
-       struct device *sc_child;                /* /dev/usb# device */
-       char sc_dying;
-#if 0
-       struct usb_dma_reserve sc_dma_reserve;
-#endif
+       struct device *sc_child;        /* /dev/usb# device */
 
        char sc_vendor[32];             /* vendor string for root hub */
-       int sc_id_vendor;               /* vendor ID for root hub */
 
        TAILQ_HEAD(, dwc2_xfer) sc_complete;    /* complete transfers */
 
-       uint8_t sc_addr;                /* device address */
-       uint8_t sc_conf;                /* device configuration */
-
        struct pool sc_xferpool;
        struct pool sc_qhpool;
        struct pool sc_qtdpool;
 
+       uint8_t sc_addr;                /* device address */
+       uint8_t sc_conf;                /* device configuration */
+
 } dwc2_softc_t;
 
 int            dwc2_init(struct dwc2_softc *);
-int            dwc2_dma_config(struct dwc2_softc *,
-                               struct dwc2_core_dma_config *);
 int            dwc2_intr(void *);
 int            dwc2_detach(dwc2_softc_t *, int);
 
@@ -126,13 +118,6 @@ void               dwc2_worker(struct task *, void *);
 void           dwc2_host_complete(struct dwc2_hsotg *, struct dwc2_qtd *,
                                   int);
 
-#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))
-
 static inline void
 dwc2_root_intr(dwc2_softc_t *sc)
 {
@@ -140,23 +125,18 @@ dwc2_root_intr(dwc2_softc_t *sc)
        softintr_schedule(sc->sc_rhc_si);
 }
 
-// XXX compat
-
-#define        ENOSR           90
-
-#ifndef mstohz
+/*
+ * XXX Compat
+ */
+#define DWC2_MAXISOCPACKETS    16      /* 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)
-#endif
-
-#define        timeout_reset(x, t, f, a) \
-do { \
-       timeout_set((x), (f), (a)); \
-       timeout_add((x), (t)); \
-} while (0)
-
-#define        device_xname(d) ((d)->dv_xname)
+#define msecs_to_jiffies       mstohz
+#define IS_ENABLED(option)     (option)
 
 #endif /* _DWC_OTGVAR_H_ */
diff --git a/sys/dev/usb/dwc2/list.h b/sys/dev/usb/dwc2/list.h
new file mode 100644 (file)
index 0000000..65435a2
--- /dev/null
@@ -0,0 +1,125 @@
+/*     $OpenBSD: list.h,v 1.1 2021/07/22 18:32:33 mglocker Exp $       */
+/* linux list functions for the BSDs, cut down for dwctwo(4).
+ * Created: Mon Apr 7 14:30:16 1999 by anholt@FreeBSD.org
+ */
+/*-
+ * Copyright 2003 Eric Anholt
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Eric Anholt <anholt@FreeBSD.org>
+ *
+ */
+
+#ifndef _DWC2_LINUX_LIST_H_
+#define _DWC2_LINUX_LIST_H_
+
+/*
+ * From <dev/pci/drm/include/linux/kernel.h>
+ */
+#define container_of(ptr, type, member) ({                      \
+        const __typeof( ((type *)0)->member ) *__mptr = (ptr);  \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * From <dev/pci/drm/include/linux/types.h>
+ */
+typedef unsigned int gfp_t;
+
+struct list_head {
+        struct list_head *next, *prev;
+};
+
+/*
+ * From <dev/pci/drm/include/linux/list.h>
+ */
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
+
+static inline void
+INIT_LIST_HEAD(struct list_head *head) {
+       (head)->next = head;
+       (head)->prev = head;
+}
+
+static inline int
+list_empty(const struct list_head *head) {
+       return (head)->next == head;
+}
+
+static inline void
+list_add(struct list_head *new, struct list_head *head) {
+        (head)->next->prev = new;
+        (new)->next = (head)->next;
+        (new)->prev = head;
+        (head)->next = new;
+}
+
+static inline void
+list_add_tail(struct list_head *entry, struct list_head *head) {
+       (entry)->prev = (head)->prev;
+       (entry)->next = head;
+       (head)->prev->next = entry;
+       (head)->prev = entry;
+}
+
+static inline void
+list_del(struct list_head *entry) {
+       (entry)->next->prev = (entry)->prev;
+       (entry)->prev->next = (entry)->next;
+}
+
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+       list_del(list);
+       list_add(list, head);
+}
+
+static inline void
+list_del_init(struct list_head *entry) {
+       (entry)->next->prev = (entry)->prev;
+       (entry)->prev->next = (entry)->next;
+       INIT_LIST_HEAD(entry);
+}
+
+#define list_for_each(entry, head)                             \
+    for (entry = (head)->next; entry != head; entry = (entry)->next)
+
+#define list_for_each_safe(entry, temp, head)                  \
+    for (entry = (head)->next, temp = (entry)->next;           \
+       entry != head;                                          \
+       entry = temp, temp = entry->next)
+
+#define list_for_each_entry(pos, head, member)                         \
+    for (pos = list_entry((head)->next, __typeof(*pos), member);       \
+       &pos->member != (head);                                         \
+       pos = list_entry(pos->member.next, __typeof(*pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, __typeof(*pos), member),    \
+           n = list_entry(pos->member.next, __typeof(*pos), member);   \
+           &pos->member != (head);                                     \
+           pos = n, n = list_entry(n->member.next, __typeof(*n), member))
+
+#define list_first_entry(ptr, type, member) \
+        list_entry((ptr)->next, type, member)
+
+#endif /* _DWC2_LINUX_LIST_H_ */