[PATCH] [libbsd] Pulled and ported DWC OTG driver for RPi from FreeBSD. Modified USB sources during porting. Added driver to build by waf and make

Yurii Shevtsov ungetch at gmail.com
Fri Aug 21 17:47:01 UTC 2015


From: Yurii Shevtsov <ungetch at gmail.com>
Subject: [PATCH] Pulled and ported DWC OTG driver for RPi from FreeBSD.
Modified USB sources during porting. Added driver to build by waf and make

---
 Makefile                                           |    3 +
 freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c |  116 +
 freebsd/sys/dev/usb/controller/dwc_otg.c           | 4903
++++++++++++++++++++
 freebsd/sys/dev/usb/controller/dwc_otg.h           |  223 +
 freebsd/sys/dev/usb/controller/dwc_otg_fdt.c       |  235 +
 freebsd/sys/dev/usb/controller/dwc_otg_fdt.h       |   39 +
 freebsd/sys/dev/usb/controller/dwc_otgreg.h        |  800 ++++
 freebsd/sys/dev/usb/usb.h                          |    9 +
 freebsd/sys/dev/usb/usb_bus.h                      |    3 +
 freebsd/sys/dev/usb/usb_core.c                     |    7 +
 freebsd/sys/dev/usb/usb_core.h                     |    8 +
 rtemsbsd/include/bsp/nexus-devices.h               |   19 +
 rtemsbsd/include/machine/rtems-bsd-sysinit.h       |    3 +
 wscript                                            |    3 +
 14 files changed, 6371 insertions(+), 0 deletions(-)
 create mode 100644 freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
 create mode 100644 freebsd/sys/dev/usb/controller/dwc_otg.c
 create mode 100644 freebsd/sys/dev/usb/controller/dwc_otg.h
 create mode 100644 freebsd/sys/dev/usb/controller/dwc_otg_fdt.c
 create mode 100644 freebsd/sys/dev/usb/controller/dwc_otg_fdt.h
 create mode 100644 freebsd/sys/dev/usb/controller/dwc_otgreg.h

diff --git a/Makefile b/Makefile
index da2a5ef..02964d3 100644
--- a/Makefile
+++ b/Makefile
@@ -186,6 +186,7 @@ LIB_C_FILES += freebsd/sys/dev/tsec/if_tsec.c
 LIB_C_FILES += freebsd/sys/dev/cadence/if_cgem.c
 LIB_C_FILES += freebsd/sys/dev/dwc/if_dwc.c
 LIB_C_FILES += freebsd/sys/arm/xilinx/zy7_slcr.c
+LIB_C_FILES += freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
 LIB_C_FILES += freebsd/sys/dev/random/harvest.c
 LIB_C_FILES += freebsd/sys/netinet/tcp_hostcache.c
 LIB_C_FILES += freebsd/sys/dev/led/led.c
@@ -244,6 +245,8 @@ LIB_C_FILES += freebsd/sys/dev/usb/usb_process.c
 LIB_C_FILES += freebsd/sys/dev/usb/usb_request.c
 LIB_C_FILES += freebsd/sys/dev/usb/usb_transfer.c
 LIB_C_FILES += freebsd/sys/dev/usb/usb_util.c
+LIB_C_FILES += freebsd/sys/dev/usb/controller/dwc_otg.c
+LIB_C_FILES += freebsd/sys/dev/usb/controller/dwc_otg_fdt.c
 LIB_C_FILES += freebsd/sys/dev/usb/controller/ohci.c
 LIB_C_FILES += freebsd/sys/dev/usb/controller/ehci.c
 LIB_C_FILES += freebsd/sys/dev/usb/controller/usb_controller.c
diff --git a/freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
b/freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
new file mode 100644
index 0000000..71ff670
--- /dev/null
+++ b/freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c
@@ -0,0 +1,116 @@
+#include <machine/rtems-bsd-kernel-space.h>
+/*
+ * Copyright 2015 Andrew Turner.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/condvar.h>
+#include <sys/module.h>
+
+#ifndef __rtems__
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+#include <dev/usb/controller/dwc_otg_fdt.h>
+
+#ifndef __rtems__
+#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
+#else
+#include <bsp/vc.h>
+#endif
+
+static device_probe_t bcm283x_dwc_otg_probe;
+static device_attach_t bcm283x_dwc_otg_attach;
+
+static int
+bcm283x_dwc_otg_probe(device_t dev)
+{
+
+    #ifndef __rtems__
+    if (!ofw_bus_status_okay(dev))
+        return (ENXIO);
+
+    if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-usb"))
+        return (ENXIO);
+    #endif
+
+    device_set_desc(dev, "DWC OTG 2.0 integrated USB controller
(bcm283x)");
+
+    return (BUS_PROBE_VENDOR);
+}
+
+static int
+bcm283x_dwc_otg_attach(device_t dev)
+{
+    #ifndef __rtems__
+    bcm2835_mbox_set_power_state(dev, BCM2835_MBOX_POWER_ID_USB_HCD, TRUE);
+    #else
+    bcm2835_set_power_state_entries entries;
+    entries.dev_id = bcm2835_mailbox_power_udid_usb_hcd;
+    entries.state = BCM2835_MAILBOX_SET_POWER_STATE_REQ_ON
+                    | BCM2835_MAILBOX_SET_POWER_STATE_REQ_WAIT;
+    int ret = bcm2835_mailbox_set_power_state(&entries);
+    if (ret) return ENXIO;
+    #endif
+    return (dwc_otg_attach(dev));
+}
+
+static device_method_t bcm283x_dwc_otg_methods[] = {
+    /* bus interface */
+    DEVMETHOD(device_probe, bcm283x_dwc_otg_probe),
+    DEVMETHOD(device_attach, bcm283x_dwc_otg_attach),
+
+    DEVMETHOD_END
+};
+
+static devclass_t bcm283x_dwc_otg_devclass;
+
+DEFINE_CLASS_1(bcm283x_dwcotg, bcm283x_dwc_otg_driver,
bcm283x_dwc_otg_methods,
+    sizeof(struct dwc_otg_fdt_softc), dwc_otg_driver);
+#ifndef __rtems__
+DRIVER_MODULE(bcm283x_dwcotg, simplebus, bcm283x_dwc_otg_driver,
+    bcm283x_dwc_otg_devclass, 0, 0);
+#else
+DRIVER_MODULE(bcm283x_dwcotg, nexus, bcm283x_dwc_otg_driver,
+    bcm283x_dwc_otg_devclass, 0, 0);
+#endif
+MODULE_DEPEND(bcm283x_dwcotg, usb, 1, 1, 1);
diff --git a/freebsd/sys/dev/usb/controller/dwc_otg.c
b/freebsd/sys/dev/usb/controller/dwc_otg.c
new file mode 100644
index 0000000..f84a896
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otg.c
@@ -0,0 +1,4903 @@
+#include <machine/rtems-bsd-kernel-space.h>
+/*-
+ * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the driver for the DesignWare series USB 2.0 OTG
+ * Controller.
+ */
+
+/*
+ * LIMITATION: Drivers must be bound to all OUT endpoints in the
+ * active configuration for this driver to work properly. Blocking any
+ * OUT endpoint will block all OUT endpoints including the control
+ * endpoint. Usually this is not a problem.
+ */
+
+/*
+ * NOTE: Writing to non-existing registers appears to cause an
+ * internal reset.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <rtems/bsd/sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define    USB_DEBUG_VAR dwc_otg_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+#include <dev/usb/controller/dwc_otgreg.h>
+
+#define    DWC_OTG_BUS2SC(bus) \
+   ((struct dwc_otg_softc *)(((uint8_t *)(bus)) - \
+    ((uint8_t *)&(((struct dwc_otg_softc *)0)->sc_bus))))
+
+#define    DWC_OTG_PC2UDEV(pc) \
+   (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev)
+
+#define    DWC_OTG_MSK_GINT_ENABLED    \
+   (GINTMSK_ENUMDONEMSK |        \
+   GINTMSK_USBRSTMSK |            \
+   GINTMSK_USBSUSPMSK |            \
+   GINTMSK_IEPINTMSK |            \
+   GINTMSK_SESSREQINTMSK |        \
+   GINTMSK_RXFLVLMSK |            \
+   GINTMSK_HCHINTMSK |            \
+   GINTMSK_OTGINTMSK |            \
+   GINTMSK_PRTINTMSK)
+
+#define    DWC_OTG_MSK_GINT_THREAD_IRQ                \
+   (GINTSTS_USBRST | GINTSTS_ENUMDONE | GINTSTS_PRTINT |    \
+   GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTMSK_OTGINTMSK |    \
+   GINTSTS_SESSREQINT)
+
+#define    DWC_OTG_PHY_ULPI 0
+#define    DWC_OTG_PHY_HSIC 1
+#define    DWC_OTG_PHY_INTERNAL 2
+
+#ifndef DWC_OTG_PHY_DEFAULT
+#define    DWC_OTG_PHY_DEFAULT DWC_OTG_PHY_ULPI
+#endif
+
+static int dwc_otg_phy_type = DWC_OTG_PHY_DEFAULT;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW, 0, "USB DWC
OTG");
+SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, phy_type, CTLFLAG_RDTUN,
+    &dwc_otg_phy_type, 0, "DWC OTG PHY TYPE - 0/1/2 - ULPI/HSIC/INTERNAL");
+
+#ifdef USB_DEBUG
+static int dwc_otg_debug;
+
+SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &dwc_otg_debug, 0, "DWC OTG debug level");
+#endif
+
+#define    DWC_OTG_INTR_ENDPT 1
+
+/* prototypes */
+
+extern struct usb_bus_methods dwc_otg_bus_methods;
+extern struct usb_pipe_methods dwc_otg_device_non_isoc_methods;
+extern struct usb_pipe_methods dwc_otg_device_isoc_methods;
+
+static dwc_otg_cmd_t dwc_otg_setup_rx;
+static dwc_otg_cmd_t dwc_otg_data_rx;
+static dwc_otg_cmd_t dwc_otg_data_tx;
+static dwc_otg_cmd_t dwc_otg_data_tx_sync;
+
+static dwc_otg_cmd_t dwc_otg_host_setup_tx;
+static dwc_otg_cmd_t dwc_otg_host_data_tx;
+static dwc_otg_cmd_t dwc_otg_host_data_rx;
+
+static void dwc_otg_device_done(struct usb_xfer *, usb_error_t);
+static void dwc_otg_do_poll(struct usb_bus *);
+static void dwc_otg_standard_done(struct usb_xfer *);
+static void dwc_otg_root_intr(struct dwc_otg_softc *);
+static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *);
+static void dwc_otg_host_channel_disable(struct dwc_otg_softc *, uint8_t);
+
+/*
+ * Here is a configuration that the chip supports.
+ */
+struct usb_hw_ep_profile dwc_otg_ep_profile[1] = {
+
+    [0] = {
+        .max_in_frame_size = 64,/* fixed */
+        .max_out_frame_size = 64,    /* fixed */
+        .is_simplex = 1,
+        .support_control = 1,
+    }
+};
+
+static void
+dwc_otg_get_hw_ep_profile(struct usb_device *udev,
+    const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+    struct dwc_otg_softc *sc;
+
+    sc = DWC_OTG_BUS2SC(udev->bus);
+
+    if (ep_addr < sc->sc_dev_ep_max)
+        *ppf = &sc->sc_hw_ep_profile[ep_addr].usb;
+    else
+        *ppf = NULL;
+}
+
+static int
+dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
+{
+    struct dwc_otg_profile *pf;
+    uint32_t fifo_size;
+    uint32_t fifo_regs;
+    uint32_t tx_start;
+    uint8_t x;
+
+    fifo_size = sc->sc_fifo_size;
+
+    /*
+     * NOTE: Reserved fixed size area at end of RAM, which must
+     * not be allocated to the FIFOs:
+     */
+    fifo_regs = 4 * 16;
+
+    if (fifo_size < fifo_regs) {
+        DPRINTF("Too little FIFO\n");
+        return (EINVAL);
+    }
+
+    /* subtract FIFO regs from total once */
+    fifo_size -= fifo_regs;
+
+    /* split equally for IN and OUT */
+    fifo_size /= 2;
+
+    /* align to 4 bytes boundary */
+    fifo_size &= ~3;
+
+    /* set global receive FIFO size */
+    DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4);
+
+    tx_start = fifo_size;
+
+    if (fifo_size < 64) {
+        DPRINTFN(-1, "Not enough data space for EP0 FIFO.\n");
+        return (EINVAL);
+    }
+
+    /* disable any leftover host channels */
+    for (x = 0; x != sc->sc_host_ch_max; x++) {
+        if (sc->sc_chan_state[x].wait_sof == 0)
+            continue;
+        dwc_otg_host_channel_disable(sc, x);
+    }
+
+    if (mode == DWC_MODE_HOST) {
+
+        /* reset active endpoints */
+        sc->sc_active_rx_ep = 0;
+
+        /* split equally for periodic and non-periodic */
+        fifo_size /= 2;
+
+        /* align to 4 bytes boundary */
+        fifo_size &= ~3;
+
+        DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
+            ((fifo_size / 4) << 16) |
+            (tx_start / 4));
+
+        tx_start += fifo_size;
+
+        for (x = 0; x != sc->sc_host_ch_max; x++) {
+            /* disable all host interrupts */
+            DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+                HCINT_DEFAULT_MASK);
+        }
+
+        DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ,
+            ((fifo_size / 4) << 16) |
+            (tx_start / 4));
+
+        /* reset host channel state */
+        memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
+
+        /* reset FIFO TX levels */
+        sc->sc_tx_cur_p_level = 0;
+        sc->sc_tx_cur_np_level = 0;
+
+        /* store maximum periodic and non-periodic FIFO TX size */
+        sc->sc_tx_max_size = fifo_size;
+
+        /* enable all host channel interrupts */
+        DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
+            (1U << sc->sc_host_ch_max) - 1U);
+    }
+
+    if (mode == DWC_MODE_DEVICE) {
+
+        DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
+        (0x10 << 16) | (tx_start / 4));
+        fifo_size -= 0x40;
+        tx_start += 0x40;
+
+        /* setup control endpoint profile */
+        sc->sc_hw_ep_profile[0].usb = dwc_otg_ep_profile[0];
+
+        /* reset active endpoints */
+        sc->sc_active_rx_ep = 1;
+
+        for (x = 1; x != sc->sc_dev_ep_max; x++) {
+
+        pf = sc->sc_hw_ep_profile + x;
+
+        pf->usb.max_out_frame_size = 1024 * 3;
+        pf->usb.is_simplex = 0;    /* assume duplex */
+        pf->usb.support_bulk = 1;
+        pf->usb.support_interrupt = 1;
+        pf->usb.support_isochronous = 1;
+        pf->usb.support_out = 1;
+
+        if (x < sc->sc_dev_in_ep_max) {
+            uint32_t limit;
+
+            limit = (x == 1) ? MIN(DWC_OTG_TX_MAX_FIFO_SIZE,
+                DWC_OTG_MAX_TXN) : MIN(DWC_OTG_MAX_TXN / 2,
+                DWC_OTG_TX_MAX_FIFO_SIZE);
+
+            /* see if there is enough FIFO space */
+            if (limit <= fifo_size) {
+                pf->max_buffer = limit;
+                pf->usb.support_in = 1;
+            } else {
+                limit = MIN(DWC_OTG_TX_MAX_FIFO_SIZE, 0x40);
+                if (limit <= fifo_size) {
+                    pf->usb.support_in = 1;
+                } else {
+                    pf->usb.is_simplex = 1;
+                    limit = 0;
+                }
+            }
+            /* set FIFO size */
+            DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x),
+                ((limit / 4) << 16) | (tx_start / 4));
+            tx_start += limit;
+            fifo_size -= limit;
+            pf->usb.max_in_frame_size = limit;
+        } else {
+            pf->usb.is_simplex = 1;
+        }
+
+        DPRINTF("FIFO%d = IN:%d / OUT:%d\n", x,
+            pf->usb.max_in_frame_size,
+            pf->usb.max_out_frame_size);
+        }
+    }
+
+    /* reset RX FIFO */
+    DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+        GRSTCTL_RXFFLSH);
+
+    if (mode != DWC_MODE_OTG) {
+        /* reset all TX FIFOs */
+        DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+            GRSTCTL_TXFIFO(0x10) |
+            GRSTCTL_TXFFLSH);
+    } else {
+        /* reset active endpoints */
+        sc->sc_active_rx_ep = 0;
+
+        /* reset periodic and non-periodic FIFO TX size */
+        sc->sc_tx_max_size = fifo_size;
+
+        /* reset host channel state */
+        memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
+
+        /* reset FIFO TX levels */
+        sc->sc_tx_cur_p_level = 0;
+        sc->sc_tx_cur_np_level = 0;
+    }
+    return (0);
+}
+
+static void
+dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc)
+{
+
+  /*
+   * Disabled until further. Assuming that the register is already
+   * programmed correctly by the boot loader.
+   */
+#if 0
+    uint32_t temp;
+
+    /* setup HOST frame interval register, based on existing value */
+    temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK;
+    if (temp >= 10000)
+        temp /= 1000;
+    else
+        temp /= 125;
+
+    /* figure out nearest X-tal value */
+    if (temp >= 54)
+        temp = 60;    /* MHz */
+    else if (temp >= 39)
+        temp = 48;    /* MHz */
+    else
+        temp = 30;    /* MHz */
+
+    if (sc->sc_flags.status_high_speed)
+        temp *= 125;
+    else
+        temp *= 1000;
+
+    DPRINTF("HFIR=0x%08x\n", temp);
+
+    DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp);
+#endif
+}
+
+static void
+dwc_otg_clocks_on(struct dwc_otg_softc *sc)
+{
+    if (sc->sc_flags.clocks_off &&
+        sc->sc_flags.port_powered) {
+
+        DPRINTFN(5, "\n");
+
+        /* TODO - platform specific */
+
+        sc->sc_flags.clocks_off = 0;
+    }
+}
+
+static void
+dwc_otg_clocks_off(struct dwc_otg_softc *sc)
+{
+    if (!sc->sc_flags.clocks_off) {
+
+        DPRINTFN(5, "\n");
+
+        /* TODO - platform specific */
+
+        sc->sc_flags.clocks_off = 1;
+    }
+}
+
+static void
+dwc_otg_pull_up(struct dwc_otg_softc *sc)
+{
+    uint32_t temp;
+
+    /* pullup D+, if possible */
+
+    if (!sc->sc_flags.d_pulled_up &&
+        sc->sc_flags.port_powered) {
+        sc->sc_flags.d_pulled_up = 1;
+
+        temp = DWC_OTG_READ_4(sc, DOTG_DCTL);
+        temp &= ~DCTL_SFTDISCON;
+        DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+    }
+}
+
+static void
+dwc_otg_pull_down(struct dwc_otg_softc *sc)
+{
+    uint32_t temp;
+
+    /* pulldown D+, if possible */
+
+    if (sc->sc_flags.d_pulled_up) {
+        sc->sc_flags.d_pulled_up = 0;
+
+        temp = DWC_OTG_READ_4(sc, DOTG_DCTL);
+        temp |= DCTL_SFTDISCON;
+        DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+    }
+}
+
+static void
+dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc)
+{
+    /* In device mode we don't use the SOF interrupt */
+    if (sc->sc_flags.status_device_mode != 0 ||
+        (sc->sc_irq_mask & GINTMSK_SOFMSK) != 0)
+        return;
+    sc->sc_irq_mask |= GINTMSK_SOFMSK;
+    DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+}
+
+static void
+dwc_otg_resume_irq(struct dwc_otg_softc *sc)
+{
+    if (sc->sc_flags.status_suspend) {
+        /* update status bits */
+        sc->sc_flags.status_suspend = 0;
+        sc->sc_flags.change_suspend = 1;
+
+        if (sc->sc_flags.status_device_mode) {
+            /*
+             * Disable resume interrupt and enable suspend
+             * interrupt:
+             */
+            sc->sc_irq_mask &= ~GINTMSK_WKUPINTMSK;
+            sc->sc_irq_mask |= GINTMSK_USBSUSPMSK;
+            DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+        }
+
+        /* complete root HUB interrupt endpoint */
+        dwc_otg_root_intr(sc);
+    }
+}
+
+static void
+dwc_otg_suspend_irq(struct dwc_otg_softc *sc)
+{
+    if (!sc->sc_flags.status_suspend) {
+        /* update status bits */
+        sc->sc_flags.status_suspend = 1;
+        sc->sc_flags.change_suspend = 1;
+
+        if (sc->sc_flags.status_device_mode) {
+            /*
+             * Disable suspend interrupt and enable resume
+             * interrupt:
+             */
+            sc->sc_irq_mask &= ~GINTMSK_USBSUSPMSK;
+            sc->sc_irq_mask |= GINTMSK_WKUPINTMSK;
+            DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+        }
+
+        /* complete root HUB interrupt endpoint */
+        dwc_otg_root_intr(sc);
+    }
+}
+
+static void
+dwc_otg_wakeup_peer(struct dwc_otg_softc *sc)
+{
+    if (!sc->sc_flags.status_suspend)
+        return;
+
+    DPRINTFN(5, "Remote wakeup\n");
+
+    if (sc->sc_flags.status_device_mode) {
+        uint32_t temp;
+
+        /* enable remote wakeup signalling */
+        temp = DWC_OTG_READ_4(sc, DOTG_DCTL);
+        temp |= DCTL_RMTWKUPSIG;
+        DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+
+        /* Wait 8ms for remote wakeup to complete. */
+        usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+
+        temp &= ~DCTL_RMTWKUPSIG;
+        DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+    } else {
+        /* enable USB port */
+        DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0);
+
+        /* wait 10ms */
+        usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+        /* resume port */
+        sc->sc_hprt_val |= HPRT_PRTRES;
+        DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+        /* Wait 100ms for resume signalling to complete. */
+        usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10);
+
+        /* clear suspend and resume */
+        sc->sc_hprt_val &= ~(HPRT_PRTSUSP | HPRT_PRTRES);
+        DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+        /* Wait 4ms */
+        usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250);
+    }
+
+    /* need to fake resume IRQ */
+    dwc_otg_resume_irq(sc);
+}
+
+static void
+dwc_otg_set_address(struct dwc_otg_softc *sc, uint8_t addr)
+{
+    uint32_t temp;
+
+    DPRINTFN(5, "addr=%d\n", addr);
+
+    temp = DWC_OTG_READ_4(sc, DOTG_DCFG);
+    temp &= ~DCFG_DEVADDR_SET(0x7F);
+    temp |= DCFG_DEVADDR_SET(addr);
+    DWC_OTG_WRITE_4(sc, DOTG_DCFG, temp);
+}
+
+static void
+dwc_otg_common_rx_ack(struct dwc_otg_softc *sc)
+{
+    DPRINTFN(5, "RX status clear\n");
+
+    /* enable RX FIFO level interrupt */
+    sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
+    DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+
+    /* clear cached status */
+    sc->sc_last_rx_status = 0;
+}
+
+static void
+dwc_otg_clear_hcint(struct dwc_otg_softc *sc, uint8_t x)
+{
+    uint32_t hcint;
+
+    /* clear all pending interrupts */
+    hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+    DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint);
+
+    /* clear buffered interrupts */
+    sc->sc_chan_state[x].hcint = 0;
+}
+
+static uint8_t
+dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td
*td, uint8_t is_out)
+{
+    uint32_t tx_p_size;
+    uint32_t tx_np_size;
+    uint8_t x;
+
+    if (td->channel < DWC_OTG_MAX_CHANNELS)
+        return (0);        /* already allocated */
+
+    /* check if device is suspended */
+    if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0)
+        return (1);        /* busy - cannot transfer data */
+
+    /* compute needed TX FIFO size */
+    if (is_out != 0) {
+        if (td->ep_type == UE_ISOCHRONOUS) {
+            tx_p_size = td->max_packet_size;
+            tx_np_size = 0;
+            if (td->hcsplt != 0 && tx_p_size > HCSPLT_XACTLEN_BURST)
+                tx_p_size = HCSPLT_XACTLEN_BURST;
+            if ((sc->sc_tx_cur_p_level + tx_p_size) > sc->sc_tx_max_size) {
+                DPRINTF("Too little FIFO space\n");
+                return (1);    /* too little FIFO */
+            }
+        } else {
+            tx_p_size = 0;
+            tx_np_size = td->max_packet_size;
+            if (td->hcsplt != 0 && tx_np_size > HCSPLT_XACTLEN_BURST)
+                tx_np_size = HCSPLT_XACTLEN_BURST;
+            if ((sc->sc_tx_cur_np_level + tx_np_size) >
sc->sc_tx_max_size) {
+                DPRINTF("Too little FIFO space\n");
+                return (1);    /* too little FIFO */
+            }
+        }
+    } else {
+        /* not a TX transaction */
+        tx_p_size = 0;
+        tx_np_size = 0;
+    }
+
+    for (x = 0; x != sc->sc_host_ch_max; x++) {
+        if (sc->sc_chan_state[x].allocated != 0)
+            continue;
+        /* check if channel is still enabled */
+        if (sc->sc_chan_state[x].wait_sof != 0)
+            continue;
+
+        sc->sc_chan_state[x].allocated = 1;
+        sc->sc_chan_state[x].tx_p_size = tx_p_size;
+        sc->sc_chan_state[x].tx_np_size = tx_np_size;
+
+        /* keep track of used TX FIFO, if any */
+        sc->sc_tx_cur_p_level += tx_p_size;
+        sc->sc_tx_cur_np_level += tx_np_size;
+
+        /* clear interrupts */
+        dwc_otg_clear_hcint(sc, x);
+
+        DPRINTF("CH=%d HCCHAR=0x%08x "
+            "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt);
+
+        /* set active channel */
+        sc->sc_active_rx_ep |= (1 << x);
+
+        /* set channel */
+        td->channel = x;
+
+        return (0);    /* allocated */
+    }
+    /* wait a bit */
+    dwc_otg_enable_sof_irq(sc);
+    return (1);    /* busy */
+}
+
+static void
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint8_t x;
+
+    if (td->channel >= DWC_OTG_MAX_CHANNELS)
+        return;        /* already freed */
+
+    /* free channel */
+    x = td->channel;
+    td->channel = DWC_OTG_MAX_CHANNELS;
+
+    DPRINTF("CH=%d\n", x);
+
+    /*
+     * We need to let programmed host channels run till complete
+     * else the host channel will stop functioning. Assume that
+     * after a fixed given amount of time the host channel is no
+     * longer doing any USB traffic:
+     */
+    if (td->ep_type == UE_ISOCHRONOUS) {
+        /* double buffered */
+        sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX;
+    } else {
+        /* single buffered */
+        sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MIN;
+    }
+
+    sc->sc_chan_state[x].allocated = 0;
+
+    /* ack any pending messages */
+    if (sc->sc_last_rx_status != 0 &&
+        GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) {
+        dwc_otg_common_rx_ack(sc);
+    }
+
+    /* clear active channel */
+    sc->sc_active_rx_ep &= ~(1 << x);
+}
+
+static void
+dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    /* dump any pending messages */
+    if (sc->sc_last_rx_status != 0) {
+        if (td->channel < DWC_OTG_MAX_CHANNELS &&
+            td->channel == GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) {
+            dwc_otg_common_rx_ack(sc);
+        }
+    }
+}
+
+static uint8_t
+dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    struct usb_device_request req __aligned(4);
+    uint32_t hcint;
+    uint32_t hcchar;
+    uint8_t delta;
+
+    dwc_otg_host_dump_rx(sc, td);
+
+    if (td->channel < DWC_OTG_MAX_CHANNELS) {
+        hcint = sc->sc_chan_state[td->channel].hcint;
+
+        DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+            td->channel, td->state, hcint,
+            DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+    } else {
+        hcint = 0;
+        goto check_state;
+    }
+
+    if (hcint & (HCINT_RETRY |
+        HCINT_ACK | HCINT_NYET)) {
+        /* give success bits priority over failure bits */
+    } else if (hcint & HCINT_STALL) {
+        DPRINTF("CH=%d STALL\n", td->channel);
+        td->error_stall = 1;
+        td->error_any = 1;
+        goto complete;
+    } else if (hcint & HCINT_ERRORS) {
+        DPRINTF("CH=%d ERROR\n", td->channel);
+        td->errcnt++;
+        if (td->hcsplt != 0 || td->errcnt >= 3) {
+            td->error_any = 1;
+            goto complete;
+        }
+    }
+
+    if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+        HCINT_ACK | HCINT_NYET)) {
+        if (!(hcint & HCINT_ERRORS))
+            td->errcnt = 0;
+    }
+
+check_state:
+    switch (td->state) {
+    case DWC_CHAN_ST_START:
+        goto send_pkt;
+
+    case DWC_CHAN_ST_WAIT_ANE:
+        if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto send_pkt;
+        } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
+            td->offset += td->tx_bytes;
+            td->remainder -= td->tx_bytes;
+            td->toggle = 1;
+            td->tt_scheduled = 0;
+            goto complete;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_S_ANE:
+        if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto send_pkt;
+        } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
+            goto send_cpkt;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_C_ANE:
+        if (hcint & HCINT_NYET) {
+            goto send_cpkt;
+        } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto send_pkt;
+        } else if (hcint & HCINT_ACK) {
+            td->offset += td->tx_bytes;
+            td->remainder -= td->tx_bytes;
+            td->toggle = 1;
+            goto complete;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_C_PKT:
+        goto send_cpkt;
+
+    default:
+        break;
+    }
+    goto busy;
+
+send_pkt:
+    /* free existing channel, if any */
+    dwc_otg_host_channel_free(sc, td);
+
+    if (sizeof(req) != td->remainder) {
+        td->error_any = 1;
+        goto complete;
+    }
+
+    if (td->hcsplt != 0) {
+        delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+        if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+            td->state = DWC_CHAN_ST_START;
+            goto busy;
+        }
+        delta = sc->sc_last_frame_num - td->tt_start_slot;
+        if (delta > 5) {
+            /* missed it */
+            td->tt_scheduled = 0;
+            td->state = DWC_CHAN_ST_START;
+            goto busy;
+        }
+    }
+
+    /* allocate a new channel */
+    if (dwc_otg_host_channel_alloc(sc, td, 1)) {
+        td->state = DWC_CHAN_ST_START;
+        goto busy;
+    }
+
+    if (td->hcsplt != 0) {
+        td->hcsplt &= ~HCSPLT_COMPSPLT;
+        td->state = DWC_CHAN_ST_WAIT_S_ANE;
+    } else {
+        td->state = DWC_CHAN_ST_WAIT_ANE;
+    }
+
+    usbd_copy_out(td->pc, 0, &req, sizeof(req));
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+        (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
+        (1 << HCTSIZ_PKTCNT_SHIFT) |
+        (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+
+    hcchar = td->hcchar;
+    hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
+    hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
+
+    /* must enable channel before writing data to FIFO */
+    DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+
+    /* transfer data into FIFO */
+    bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+        DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
+
+    /* wait until next slot before trying complete split */
+    td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
+    /* store number of bytes transmitted */
+    td->tx_bytes = sizeof(req);
+    goto busy;
+
+send_cpkt:
+    /* free existing channel, if any */
+    dwc_otg_host_channel_free(sc, td);
+
+    delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+    if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+        td->state = DWC_CHAN_ST_WAIT_C_PKT;
+        goto busy;
+    }
+    delta = sc->sc_last_frame_num - td->tt_start_slot;
+    if (delta > DWC_OTG_TT_SLOT_MAX) {
+        /* we missed the service interval */
+        if (td->ep_type != UE_ISOCHRONOUS)
+            td->error_any = 1;
+        goto complete;
+    }
+    /* allocate a new channel */
+    if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+        td->state = DWC_CHAN_ST_WAIT_C_PKT;
+        goto busy;
+    }
+
+    /* wait until next slot before trying complete split */
+    td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
+    td->hcsplt |= HCSPLT_COMPSPLT;
+    td->state = DWC_CHAN_ST_WAIT_C_ANE;
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+        (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+
+    hcchar = td->hcchar;
+    hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
+    hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
+
+    /* must enable channel before writing data to FIFO */
+    DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+
+busy:
+    return (1);    /* busy */
+
+complete:
+    dwc_otg_host_channel_free(sc, td);
+    return (0);    /* complete */
+}
+
+static uint8_t
+dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    struct usb_device_request req __aligned(4);
+    uint32_t temp;
+    uint16_t count;
+
+    /* check endpoint status */
+
+    if (sc->sc_last_rx_status == 0)
+        goto not_complete;
+
+    if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != 0)
+        goto not_complete;
+
+    if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+        GRXSTSRD_DPID_DATA0) {
+        /* release FIFO */
+        dwc_otg_common_rx_ack(sc);
+        goto not_complete;
+    }
+
+    if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+        GRXSTSRD_STP_DATA) {
+        /* release FIFO */
+        dwc_otg_common_rx_ack(sc);
+        goto not_complete;
+    }
+
+    DPRINTFN(5, "GRXSTSR=0x%08x\n", sc->sc_last_rx_status);
+
+    /* clear did stall */
+    td->did_stall = 0;
+
+    /* get the packet byte count */
+    count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
+
+    /* verify data length */
+    if (count != td->remainder) {
+        DPRINTFN(0, "Invalid SETUP packet "
+            "length, %d bytes\n", count);
+        /* release FIFO */
+        dwc_otg_common_rx_ack(sc);
+        goto not_complete;
+    }
+    if (count != sizeof(req)) {
+        DPRINTFN(0, "Unsupported SETUP packet "
+            "length, %d bytes\n", count);
+        /* release FIFO */
+        dwc_otg_common_rx_ack(sc);
+        goto not_complete;
+    }
+
+    /* copy in control request */
+    memcpy(&req, sc->sc_rx_bounce_buffer, sizeof(req));
+
+    /* copy data into real buffer */
+    usbd_copy_in(td->pc, 0, &req, sizeof(req));
+
+    td->offset = sizeof(req);
+    td->remainder = 0;
+
+    /* sneak peek the set address */
+    if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+        (req.bRequest == UR_SET_ADDRESS)) {
+        /* must write address before ZLP */
+        dwc_otg_set_address(sc, req.wValue[0] & 0x7F);
+    }
+
+    /* don't send any data by default */
+    DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0),
+        DXEPTSIZ_SET_NPKT(0) |
+        DXEPTSIZ_SET_NBYTES(0));
+
+    temp = sc->sc_in_ctl[0];
+
+    /* enable IN endpoint */
+    DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
+        temp | DIEPCTL_EPENA);
+    DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
+        temp | DIEPCTL_SNAK);
+
+    /* reset IN endpoint buffer */
+    DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+        GRSTCTL_TXFIFO(0) |
+        GRSTCTL_TXFFLSH);
+
+    /* acknowledge RX status */
+    dwc_otg_common_rx_ack(sc);
+    return (0);            /* complete */
+
+not_complete:
+    /* abort any ongoing transfer, before enabling again */
+
+    temp = sc->sc_out_ctl[0];
+
+    temp |= DOEPCTL_EPENA |
+        DOEPCTL_SNAK;
+
+    /* enable OUT endpoint */
+    DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0), temp);
+
+    if (!td->did_stall) {
+        td->did_stall = 1;
+
+        DPRINTFN(5, "stalling IN and OUT direction\n");
+
+        /* set stall after enabling endpoint */
+        DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0),
+            temp | DOEPCTL_STALL);
+
+        temp = sc->sc_in_ctl[0];
+
+        /* set stall assuming endpoint is enabled */
+        DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
+            temp | DIEPCTL_STALL);
+    }
+
+    /* setup number of buffers to receive */
+    DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
+        DXEPTSIZ_SET_MULTI(3) |
+        DXEPTSIZ_SET_NPKT(1) |
+        DXEPTSIZ_SET_NBYTES(sizeof(req)));
+
+    return (1);            /* not complete */
+}
+
+static uint8_t
+dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct
dwc_otg_td *td)
+{
+    uint8_t delta;
+
+    delta = sc->sc_tmr_val - td->tmr_val;
+    if (delta >= 128)
+        return (1);    /* busy */
+
+    td->tmr_val = sc->sc_tmr_val + td->tmr_res;
+
+    /* set toggle, if any */
+    if (td->set_toggle) {
+        td->set_toggle = 0;
+        td->toggle = 1;
+    }
+    return (0);
+}
+
+static uint8_t
+dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    if (td->ep_type == UE_ISOCHRONOUS) {
+        /* non TT isochronous traffic */
+        if ((td->tmr_val != 0) ||
+            (sc->sc_last_frame_num & (td->tmr_res - 1))) {
+            goto busy;
+        }
+        td->tmr_val = 1;    /* executed */
+        td->toggle = 0;
+
+    } else if (td->ep_type == UE_INTERRUPT) {
+        if (!td->tt_scheduled)
+            goto busy;
+        td->tt_scheduled = 0;
+    } else if (td->did_nak >= DWC_OTG_NAK_MAX) {
+        goto busy;
+    } else if (td->set_toggle) {
+        td->set_toggle = 0;
+        td->toggle = 1;
+    }
+    return (0);
+busy:
+    return (1);
+}
+
+static uint8_t
+dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint32_t count;
+    uint8_t channel;
+
+    /* check endpoint status */
+    if (sc->sc_last_rx_status == 0)
+        goto busy;
+
+    channel = td->channel;
+    if (channel >= DWC_OTG_MAX_CHANNELS)
+        goto busy;
+
+    if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel)
+        goto busy;
+
+    switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
+    case GRXSTSRH_IN_DATA:
+
+        DPRINTF("DATA ST=%d STATUS=0x%08x\n",
+            (int)td->state, (int)sc->sc_last_rx_status);
+
+        if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) {
+            /*
+             * When using SPLIT transactions on interrupt
+             * endpoints, sometimes data occurs twice.
+             */
+            DPRINTF("Data already received\n");
+            break;
+        }
+
+        /* get the packet byte count */
+        count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
+
+        /* check for isochronous transfer or high-speed bandwidth endpoint
*/
+        if (td->ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) {
+            if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
GRXSTSRD_DPID_DATA0) {
+                td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
+            } else {
+                td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
+
+                /* verify the packet byte count */
+                if (count < td->max_packet_size) {
+                    /* we have a short packet */
+                    td->short_pkt = 1;
+                    td->got_short = 1;
+                }
+            }
+            td->toggle = 0;
+        } else {
+            /* verify the packet byte count */
+            if (count != td->max_packet_size) {
+                if (count < td->max_packet_size) {
+                    /* we have a short packet */
+                    td->short_pkt = 1;
+                    td->got_short = 1;
+                } else {
+                    /* invalid USB packet */
+                    td->error_any = 1;
+
+                    /* release FIFO */
+                    dwc_otg_common_rx_ack(sc);
+                    goto complete;
+                }
+            }
+            td->toggle ^= 1;
+            td->tt_scheduled = 0;
+        }
+
+        /* verify the packet byte count */
+        if (count > td->remainder) {
+            /* invalid USB packet */
+            td->error_any = 1;
+
+            /* release FIFO */
+            dwc_otg_common_rx_ack(sc);
+            goto complete;
+        }
+
+        usbd_copy_in(td->pc, td->offset,
+            sc->sc_rx_bounce_buffer, count);
+
+        td->remainder -= count;
+        td->offset += count;
+        sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY;
+        break;
+    default:
+        break;
+    }
+    /* release FIFO */
+    dwc_otg_common_rx_ack(sc);
+busy:
+    return (0);
+complete:
+    return (1);
+}
+
+static uint8_t
+dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint32_t hcint;
+    uint32_t hcchar;
+    uint8_t delta;
+    uint8_t channel;
+
+    channel = td->channel;
+
+    if (channel < DWC_OTG_MAX_CHANNELS) {
+        hcint = sc->sc_chan_state[channel].hcint;
+
+        DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+            channel, td->state, hcint,
+            DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
+
+        /* check interrupt bits */
+        if (hcint & (HCINT_RETRY |
+            HCINT_ACK | HCINT_NYET)) {
+            /* give success bits priority over failure bits */
+        } else if (hcint & HCINT_STALL) {
+            DPRINTF("CH=%d STALL\n", channel);
+            td->error_stall = 1;
+            td->error_any = 1;
+            goto complete;
+        } else if (hcint & HCINT_ERRORS) {
+            DPRINTF("CH=%d ERROR\n", channel);
+            td->errcnt++;
+            if (td->hcsplt != 0 || td->errcnt >= 3) {
+                if (td->ep_type != UE_ISOCHRONOUS) {
+                    td->error_any = 1;
+                    goto complete;
+                }
+            }
+        }
+
+        /* check channels for data, if any */
+        if (dwc_otg_host_data_rx_sub(sc, td))
+            goto complete;
+
+        /* refresh interrupt status */
+        hcint = sc->sc_chan_state[channel].hcint;
+
+        if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+            HCINT_ACK | HCINT_NYET)) {
+            if (!(hcint & HCINT_ERRORS))
+                td->errcnt = 0;
+        }
+    } else {
+        hcint = 0;
+    }
+
+    switch (td->state) {
+    case DWC_CHAN_ST_START:
+        if (td->hcsplt != 0)
+            goto receive_spkt;
+        else
+            goto receive_pkt;
+
+    case DWC_CHAN_ST_WAIT_ANE:
+        if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            if (td->ep_type == UE_INTERRUPT) {
+                /*
+                 * The USB specification does not
+                 * mandate a particular data toggle
+                 * value for USB INTERRUPT
+                 * transfers. Switch the data toggle
+                 * value to receive the packet
+                 * correctly:
+                 */
+                if (hcint & HCINT_DATATGLERR) {
+                    DPRINTF("Retrying packet due to "
+                        "data toggle error\n");
+                    td->toggle ^= 1;
+                    goto receive_pkt;
+                }
+            }
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            if (td->hcsplt != 0)
+                goto receive_spkt;
+            else
+                goto receive_pkt;
+        } else if (hcint & HCINT_NYET) {
+            if (td->hcsplt != 0) {
+                /* try again */
+                goto receive_pkt;
+            } else {
+                /* not a valid token for IN endpoints */
+                td->error_any = 1;
+                goto complete;
+            }
+        } else if (hcint & HCINT_ACK) {
+            /* wait for data - ACK arrived first */
+            if (!(hcint & HCINT_SOFTWARE_ONLY))
+                goto busy;
+
+            if (td->ep_type == UE_ISOCHRONOUS) {
+                /* check if we are complete */
+                if ((td->remainder == 0) ||
+                    (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) {
+                    goto complete;
+                }
+                /* get another packet */
+                goto receive_pkt;
+            } else {
+                /* check if we are complete */
+                if ((td->remainder == 0) || (td->got_short != 0)) {
+                    if (td->short_pkt)
+                        goto complete;
+
+                    /*
+                     * Else need to receive a zero length
+                     * packet.
+                     */
+                }
+                td->tt_scheduled = 0;
+                td->did_nak = 0;
+                if (td->hcsplt != 0)
+                    goto receive_spkt;
+                else
+                    goto receive_pkt;
+            }
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_S_ANE:
+        /*
+         * NOTE: The DWC OTG hardware provides a fake ACK in
+         * case of interrupt and isochronous transfers:
+         */
+        if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto receive_spkt;
+        } else if (hcint & HCINT_NYET) {
+            td->tt_scheduled = 0;
+            goto receive_spkt;
+        } else if (hcint & HCINT_ACK) {
+            td->did_nak = 0;
+            goto receive_pkt;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_C_PKT:
+        goto receive_pkt;
+
+    default:
+        break;
+    }
+    goto busy;
+
+receive_pkt:
+    /* free existing channel, if any */
+    dwc_otg_host_channel_free(sc, td);
+
+      if (td->hcsplt != 0) {
+        delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+        if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+            td->state = DWC_CHAN_ST_WAIT_C_PKT;
+            goto busy;
+        }
+        delta = sc->sc_last_frame_num - td->tt_start_slot;
+        if (delta > DWC_OTG_TT_SLOT_MAX) {
+            if (td->ep_type != UE_ISOCHRONOUS) {
+                /* we missed the service interval */
+                td->error_any = 1;
+            }
+            goto complete;
+        }
+        /* complete split */
+        td->hcsplt |= HCSPLT_COMPSPLT;
+    } else if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN &&
+        dwc_otg_host_rate_check(sc, td)) {
+        td->state = DWC_CHAN_ST_WAIT_C_PKT;
+        goto busy;
+    }
+
+    /* allocate a new channel */
+    if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+        td->state = DWC_CHAN_ST_WAIT_C_PKT;
+        goto busy;
+    }
+
+    channel = td->channel;
+
+    /* set toggle, if any */
+    if (td->set_toggle) {
+        td->set_toggle = 0;
+        td->toggle = 1;
+    }
+
+    td->state = DWC_CHAN_ST_WAIT_ANE;
+
+    /* receive one packet */
+    DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+        (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
+        (1 << HCTSIZ_PKTCNT_SHIFT) |
+        (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+        (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+
+    hcchar = td->hcchar;
+    hcchar |= HCCHAR_EPDIR_IN;
+
+    /* receive complete split ASAP */
+    if ((sc->sc_last_frame_num & 1) != 0)
+        hcchar |= HCCHAR_ODDFRM;
+    else
+        hcchar &= ~HCCHAR_ODDFRM;
+
+    /* must enable channel before data can be received */
+    DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+
+    /* wait until next slot before trying complete split */
+    td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
+    goto busy;
+
+receive_spkt:
+    /* free existing channel(s), if any */
+    dwc_otg_host_channel_free(sc, td);
+
+    delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+    if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+        td->state = DWC_CHAN_ST_START;
+        goto busy;
+    }
+    delta = sc->sc_last_frame_num - td->tt_start_slot;
+    if (delta > 5) {
+        /* missed it */
+        td->tt_scheduled = 0;
+        td->state = DWC_CHAN_ST_START;
+        goto busy;
+    }
+
+    /* allocate a new channel */
+    if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+        td->state = DWC_CHAN_ST_START;
+        goto busy;
+    }
+
+    channel = td->channel;
+
+    td->hcsplt &= ~HCSPLT_COMPSPLT;
+    td->state = DWC_CHAN_ST_WAIT_S_ANE;
+
+    /* receive one packet */
+    DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+        (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+
+    /* send after next SOF event */
+    if ((sc->sc_last_frame_num & 1) == 0)
+        td->hcchar |= HCCHAR_ODDFRM;
+    else
+        td->hcchar &= ~HCCHAR_ODDFRM;
+
+    hcchar = td->hcchar;
+    hcchar |= HCCHAR_EPDIR_IN;
+
+    /* wait until next slot before trying complete split */
+    td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
+    /* must enable channel before data can be received */
+    DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+busy:
+    return (1);    /* busy */
+
+complete:
+    dwc_otg_host_channel_free(sc, td);
+    return (0);    /* complete */
+}
+
+static uint8_t
+dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint32_t temp;
+    uint16_t count;
+    uint8_t got_short;
+
+    got_short = 0;
+
+    /* check endpoint status */
+    if (sc->sc_last_rx_status == 0)
+        goto not_complete;
+
+    if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->ep_no)
+        goto not_complete;
+
+    /* check for SETUP packet */
+    if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
+        GRXSTSRD_STP_DATA) {
+        if (td->remainder == 0) {
+            /*
+             * We are actually complete and have
+             * received the next SETUP
+             */
+            DPRINTFN(5, "faking complete\n");
+            return (0);    /* complete */
+        }
+        /*
+         * USB Host Aborted the transfer.
+         */
+        td->error_any = 1;
+        return (0);        /* complete */
+    }
+
+    if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+        GRXSTSRD_OUT_DATA) {
+        /* release FIFO */
+        dwc_otg_common_rx_ack(sc);
+        goto not_complete;
+    }
+
+    /* get the packet byte count */
+    count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
+
+    /* verify the packet byte count */
+    if (count != td->max_packet_size) {
+        if (count < td->max_packet_size) {
+            /* we have a short packet */
+            td->short_pkt = 1;
+            got_short = 1;
+        } else {
+            /* invalid USB packet */
+            td->error_any = 1;
+
+            /* release FIFO */
+            dwc_otg_common_rx_ack(sc);
+            return (0);    /* we are complete */
+        }
+    }
+    /* verify the packet byte count */
+    if (count > td->remainder) {
+        /* invalid USB packet */
+        td->error_any = 1;
+
+        /* release FIFO */
+        dwc_otg_common_rx_ack(sc);
+        return (0);        /* we are complete */
+    }
+
+    usbd_copy_in(td->pc, td->offset, sc->sc_rx_bounce_buffer, count);
+    td->remainder -= count;
+    td->offset += count;
+
+    /* release FIFO */
+    dwc_otg_common_rx_ack(sc);
+
+    temp = sc->sc_out_ctl[td->ep_no];
+
+    /* check for isochronous mode */
+    if ((temp & DIEPCTL_EPTYPE_MASK) ==
+        (DIEPCTL_EPTYPE_ISOC << DIEPCTL_EPTYPE_SHIFT)) {
+        /* toggle odd or even frame bit */
+        if (temp & DIEPCTL_SETD1PID) {
+            temp &= ~DIEPCTL_SETD1PID;
+            temp |= DIEPCTL_SETD0PID;
+        } else {
+            temp &= ~DIEPCTL_SETD0PID;
+            temp |= DIEPCTL_SETD1PID;
+        }
+        sc->sc_out_ctl[td->ep_no] = temp;
+    }
+
+    /* check if we are complete */
+    if ((td->remainder == 0) || got_short) {
+        if (td->short_pkt) {
+            /* we are complete */
+            return (0);
+        }
+        /* else need to receive a zero length packet */
+    }
+
+not_complete:
+
+    temp = sc->sc_out_ctl[td->ep_no];
+
+    temp |= DOEPCTL_EPENA | DOEPCTL_CNAK;
+
+    DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp);
+
+    /* enable SETUP and transfer complete interrupt */
+    if (td->ep_no == 0) {
+        DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
+            DXEPTSIZ_SET_NPKT(1) |
+            DXEPTSIZ_SET_NBYTES(td->max_packet_size));
+    } else {
+        /* allow reception of multiple packets */
+        DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(td->ep_no),
+            DXEPTSIZ_SET_MULTI(1) |
+            DXEPTSIZ_SET_NPKT(4) |
+            DXEPTSIZ_SET_NBYTES(4 *
+            ((td->max_packet_size + 3) & ~3)));
+    }
+    return (1);            /* not complete */
+}
+
+static uint8_t
+dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint32_t count;
+    uint32_t hcint;
+    uint32_t hcchar;
+    uint8_t delta;
+    uint8_t channel;
+
+    dwc_otg_host_dump_rx(sc, td);
+
+    channel = td->channel;
+
+    if (channel < DWC_OTG_MAX_CHANNELS) {
+        hcint = sc->sc_chan_state[channel].hcint;
+
+        DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+            channel, td->state, hcint,
+            DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
+
+        if (hcint & (HCINT_RETRY |
+            HCINT_ACK | HCINT_NYET)) {
+            /* give success bits priority over failure bits */
+        } else if (hcint & HCINT_STALL) {
+            DPRINTF("CH=%d STALL\n", channel);
+            td->error_stall = 1;
+            td->error_any = 1;
+            goto complete;
+        } else if (hcint & HCINT_ERRORS) {
+            DPRINTF("CH=%d ERROR\n", channel);
+            td->errcnt++;
+            if (td->hcsplt != 0 || td->errcnt >= 3) {
+                td->error_any = 1;
+                goto complete;
+            }
+        }
+
+        if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+            HCINT_ACK | HCINT_NYET)) {
+
+            if (!(hcint & HCINT_ERRORS))
+                td->errcnt = 0;
+        }
+    } else {
+        hcint = 0;
+    }
+
+    switch (td->state) {
+    case DWC_CHAN_ST_START:
+        goto send_pkt;
+
+    case DWC_CHAN_ST_WAIT_ANE:
+        if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto send_pkt;
+        } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
+            td->offset += td->tx_bytes;
+            td->remainder -= td->tx_bytes;
+            td->toggle ^= 1;
+            td->did_nak = 0;
+            td->tt_scheduled = 0;
+
+            /* check remainder */
+            if (td->remainder == 0) {
+                if (td->short_pkt)
+                    goto complete;
+
+                /*
+                 * Else we need to transmit a short
+                 * packet:
+                 */
+            }
+            goto send_pkt;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_S_ANE:
+        if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto send_pkt;
+        } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
+            td->did_nak = 0;
+            goto send_cpkt;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_C_ANE:
+        if (hcint & HCINT_NYET) {
+            goto send_cpkt;
+        } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+            td->did_nak++;
+            td->tt_scheduled = 0;
+            goto send_pkt;
+        } else if (hcint & HCINT_ACK) {
+            td->offset += td->tx_bytes;
+            td->remainder -= td->tx_bytes;
+            td->toggle ^= 1;
+            td->did_nak = 0;
+            td->tt_scheduled = 0;
+
+            /* check remainder */
+            if (td->remainder == 0) {
+                if (td->short_pkt)
+                    goto complete;
+
+                /* else we need to transmit a short packet */
+            }
+            goto send_pkt;
+        }
+        break;
+
+    case DWC_CHAN_ST_WAIT_C_PKT:
+        goto send_cpkt;
+
+    case DWC_CHAN_ST_TX_WAIT_ISOC:
+
+        /* Check if isochronous OUT traffic is complete */
+        if ((hcint & HCINT_HCH_DONE_MASK) == 0)
+            break;
+
+        td->offset += td->tx_bytes;
+        td->remainder -= td->tx_bytes;
+
+        if (td->hcsplt != 0 || td->remainder == 0)
+            goto complete;
+
+        /* check for next packet */
+        if (td->max_packet_count > 1)
+            td->tt_xactpos++;
+
+        /* free existing channel, if any */
+        dwc_otg_host_channel_free(sc, td);
+
+        td->state = DWC_CHAN_ST_TX_PKT_ISOC;
+
+        /* FALLTHROUGH */
+
+    case DWC_CHAN_ST_TX_PKT_ISOC:
+        if (dwc_otg_host_channel_alloc(sc, td, 1))
+            break;
+        channel = td->channel;
+        goto send_isoc_pkt;
+    default:
+        break;
+    }
+    goto busy;
+
+send_pkt:
+    /* free existing channel(s), if any */
+    dwc_otg_host_channel_free(sc, td);
+
+    if (td->hcsplt != 0) {
+        delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+        if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+            td->state = DWC_CHAN_ST_START;
+            goto busy;
+        }
+        delta = sc->sc_last_frame_num - td->tt_start_slot;
+        if (delta > 5) {
+            /* missed it */
+            td->tt_scheduled = 0;
+            td->state = DWC_CHAN_ST_START;
+            goto busy;
+        }
+    } else if (dwc_otg_host_rate_check(sc, td)) {
+        td->state = DWC_CHAN_ST_START;
+        goto busy;
+    }
+
+    /* allocate a new channel */
+    if (dwc_otg_host_channel_alloc(sc, td, 1)) {
+        td->state = DWC_CHAN_ST_START;
+        goto busy;
+    }
+
+    channel = td->channel;
+
+    /* set toggle, if any */
+    if (td->set_toggle) {
+        td->set_toggle = 0;
+        td->toggle = 1;
+    }
+
+    if (td->ep_type == UE_ISOCHRONOUS) {
+send_isoc_pkt:
+        /* Isochronous OUT transfers don't have any ACKs */
+        td->state = DWC_CHAN_ST_TX_WAIT_ISOC;
+        td->hcsplt &= ~HCSPLT_COMPSPLT;
+        if (td->hcsplt != 0) {
+            /* get maximum transfer length */
+            count = td->remainder;
+            if (count > HCSPLT_XACTLEN_BURST) {
+                DPRINTF("TT overflow\n");
+                td->error_any = 1;
+                goto complete;
+            }
+            /* Update transaction position */
+            td->hcsplt &= ~HCSPLT_XACTPOS_MASK;
+            td->hcsplt |= (HCSPLT_XACTPOS_ALL << HCSPLT_XACTPOS_SHIFT);
+        } else {
+            /* send one packet at a time */
+            count = td->max_packet_size;
+            if (td->remainder < count) {
+                /* we have a short packet */
+                td->short_pkt = 1;
+                count = td->remainder;
+            }
+        }
+    } else if (td->hcsplt != 0) {
+
+        td->hcsplt &= ~HCSPLT_COMPSPLT;
+
+        /* Wait for ACK/NAK/ERR from TT */
+        td->state = DWC_CHAN_ST_WAIT_S_ANE;
+
+        /* send one packet at a time */
+        count = td->max_packet_size;
+        if (td->remainder < count) {
+            /* we have a short packet */
+            td->short_pkt = 1;
+            count = td->remainder;
+        }
+    } else {
+        /* Wait for ACK/NAK/STALL from device */
+        td->state = DWC_CHAN_ST_WAIT_ANE;
+
+        /* send one packet at a time */
+        count = td->max_packet_size;
+        if (td->remainder < count) {
+            /* we have a short packet */
+            td->short_pkt = 1;
+            count = td->remainder;
+        }
+    }
+
+    /* check for High-Speed multi-packets */
+    if ((td->hcsplt == 0) && (td->max_packet_count > 1)) {
+        if (td->npkt == 0) {
+            if (td->remainder >= (3 * td->max_packet_size))
+                td->npkt = 3;
+            else if (td->remainder >= (2 * td->max_packet_size))
+                td->npkt = 2;
+            else
+                td->npkt = 1;
+
+            if (td->npkt > td->max_packet_count)
+                td->npkt = td->max_packet_count;
+
+            td->tt_xactpos = 1;    /* overload */
+        }
+        if (td->tt_xactpos == td->npkt) {
+            if (td->npkt == 1) {
+                DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+                    (count << HCTSIZ_XFERSIZE_SHIFT) |
+                    (1 << HCTSIZ_PKTCNT_SHIFT) |
+                    (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
+            } else if (td->npkt == 2) {
+                DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+                    (count << HCTSIZ_XFERSIZE_SHIFT) |
+                    (1 << HCTSIZ_PKTCNT_SHIFT) |
+                    (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT));
+            } else {
+                DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+                    (count << HCTSIZ_XFERSIZE_SHIFT) |
+                    (1 << HCTSIZ_PKTCNT_SHIFT) |
+                    (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT));
+            }
+            td->npkt = 0;
+        } else {
+            DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+                (count << HCTSIZ_XFERSIZE_SHIFT) |
+                (1 << HCTSIZ_PKTCNT_SHIFT) |
+                (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT));
+        }
+    } else {
+        /* TODO: HCTSIZ_DOPNG */
+
+        DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+            (count << HCTSIZ_XFERSIZE_SHIFT) |
+            (1 << HCTSIZ_PKTCNT_SHIFT) |
+            (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+            (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+    }
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+
+    hcchar = td->hcchar;
+    hcchar &= ~HCCHAR_EPDIR_IN;
+
+    /* send after next SOF event */
+    if ((sc->sc_last_frame_num & 1) == 0)
+        hcchar |= HCCHAR_ODDFRM;
+    else
+        hcchar &= ~HCCHAR_ODDFRM;
+
+    /* must enable before writing data to FIFO */
+    DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+
+    if (count != 0) {
+
+        /* clear topmost word before copy */
+        sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0;
+
+        /* copy out data */
+        usbd_copy_out(td->pc, td->offset,
+            sc->sc_tx_bounce_buffer, count);
+
+        /* transfer data into FIFO */
+        bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+            DOTG_DFIFO(channel),
+            sc->sc_tx_bounce_buffer, (count + 3) / 4);
+    }
+
+    /* store number of bytes transmitted */
+    td->tx_bytes = count;
+    goto busy;
+
+send_cpkt:
+    /* free existing channel, if any */
+    dwc_otg_host_channel_free(sc, td);
+
+    delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+    if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+        td->state = DWC_CHAN_ST_WAIT_C_PKT;
+        goto busy;
+    }
+    delta = sc->sc_last_frame_num - td->tt_start_slot;
+    if (delta > DWC_OTG_TT_SLOT_MAX) {
+        /* we missed the service interval */
+        if (td->ep_type != UE_ISOCHRONOUS)
+            td->error_any = 1;
+        goto complete;
+    }
+
+    /* allocate a new channel */
+    if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+        td->state = DWC_CHAN_ST_WAIT_C_PKT;
+        goto busy;
+    }
+
+    channel = td->channel;
+
+     td->hcsplt |= HCSPLT_COMPSPLT;
+    td->state = DWC_CHAN_ST_WAIT_C_ANE;
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+        (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
+
+    DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+
+    hcchar = td->hcchar;
+    hcchar &= ~HCCHAR_EPDIR_IN;
+
+    /* receive complete split ASAP */
+    if ((sc->sc_last_frame_num & 1) != 0)
+        hcchar |= HCCHAR_ODDFRM;
+    else
+        hcchar &= ~HCCHAR_ODDFRM;
+
+    /* must enable channel before data can be received */
+    DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+
+    /* wait until next slot before trying complete split */
+    td->tt_complete_slot = sc->sc_last_frame_num + 1;
+busy:
+    return (1);    /* busy */
+
+complete:
+    dwc_otg_host_channel_free(sc, td);
+    return (0);    /* complete */
+}
+
+static uint8_t
+dwc_otg_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint32_t max_buffer;
+    uint32_t count;
+    uint32_t fifo_left;
+    uint32_t mpkt;
+    uint32_t temp;
+    uint8_t to;
+
+    to = 3;                /* don't loop forever! */
+
+    max_buffer = sc->sc_hw_ep_profile[td->ep_no].max_buffer;
+
+repeat:
+    /* check for for endpoint 0 data */
+
+    temp = sc->sc_last_rx_status;
+
+    if ((td->ep_no == 0) && (temp != 0) &&
+        (GRXSTSRD_CHNUM_GET(temp) == 0)) {
+
+        if ((temp & GRXSTSRD_PKTSTS_MASK) !=
+            GRXSTSRD_STP_DATA) {
+
+            /* dump data - wrong direction */
+            dwc_otg_common_rx_ack(sc);
+        } else {
+            /*
+             * The current transfer was cancelled
+             * by the USB Host:
+             */
+            td->error_any = 1;
+            return (0);        /* complete */
+        }
+    }
+
+    /* fill in more TX data, if possible */
+    if (td->tx_bytes != 0) {
+
+        uint16_t cpkt;
+
+        /* check if packets have been transferred */
+        temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no));
+
+        /* get current packet number */
+        cpkt = DXEPTSIZ_GET_NPKT(temp);
+
+        if (cpkt >= td->npkt) {
+            fifo_left = 0;
+        } else {
+            if (max_buffer != 0) {
+                fifo_left = (td->npkt - cpkt) *
+                    td->max_packet_size;
+
+                if (fifo_left > max_buffer)
+                    fifo_left = max_buffer;
+            } else {
+                fifo_left = td->max_packet_size;
+            }
+        }
+
+        count = td->tx_bytes;
+        if (count > fifo_left)
+            count = fifo_left;
+
+        if (count != 0) {
+
+            /* clear topmost word before copy */
+            sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0;
+
+            /* copy out data */
+            usbd_copy_out(td->pc, td->offset,
+                sc->sc_tx_bounce_buffer, count);
+
+            /* transfer data into FIFO */
+            bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+                DOTG_DFIFO(td->ep_no),
+                sc->sc_tx_bounce_buffer, (count + 3) / 4);
+
+            td->tx_bytes -= count;
+            td->remainder -= count;
+            td->offset += count;
+            td->npkt = cpkt;
+        }
+        if (td->tx_bytes != 0)
+            goto not_complete;
+
+        /* check remainder */
+        if (td->remainder == 0) {
+            if (td->short_pkt)
+                return (0);    /* complete */
+
+            /* else we need to transmit a short packet */
+        }
+    }
+
+    if (!to--)
+        goto not_complete;
+
+    /* check if not all packets have been transferred */
+    temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no));
+
+    if (DXEPTSIZ_GET_NPKT(temp) != 0) {
+
+        DPRINTFN(5, "busy ep=%d npkt=%d DIEPTSIZ=0x%08x "
+            "DIEPCTL=0x%08x\n", td->ep_no,
+            DXEPTSIZ_GET_NPKT(temp),
+            temp, DWC_OTG_READ_4(sc, DOTG_DIEPCTL(td->ep_no)));
+
+        goto not_complete;
+    }
+
+    DPRINTFN(5, "rem=%u ep=%d\n", td->remainder, td->ep_no);
+
+    /* try to optimise by sending more data */
+    if ((max_buffer != 0) && ((td->max_packet_size & 3) == 0)) {
+
+        /* send multiple packets at the same time */
+        mpkt = max_buffer / td->max_packet_size;
+
+        if (mpkt > 0x3FE)
+            mpkt = 0x3FE;
+
+        count = td->remainder;
+        if (count > 0x7FFFFF)
+            count = 0x7FFFFF - (0x7FFFFF % td->max_packet_size);
+
+        td->npkt = count / td->max_packet_size;
+
+        /*
+         * NOTE: We could use 0x3FE instead of "mpkt" in the
+         * check below to get more throughput, but then we
+         * have a dependency towards non-generic chip features
+         * to disable the TX-FIFO-EMPTY interrupts on a per
+         * endpoint basis. Increase the maximum buffer size of
+         * the IN endpoint to increase the performance.
+         */
+        if (td->npkt > mpkt) {
+            td->npkt = mpkt;
+            count = td->max_packet_size * mpkt;
+        } else if ((count == 0) || (count % td->max_packet_size)) {
+            /* we are transmitting a short packet */
+            td->npkt++;
+            td->short_pkt = 1;
+        }
+    } else {
+        /* send one packet at a time */
+        mpkt = 1;
+        count = td->max_packet_size;
+        if (td->remainder < count) {
+            /* we have a short packet */
+            td->short_pkt = 1;
+            count = td->remainder;
+        }
+        td->npkt = 1;
+    }
+    DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(td->ep_no),
+        DXEPTSIZ_SET_MULTI(1) |
+        DXEPTSIZ_SET_NPKT(td->npkt) |
+        DXEPTSIZ_SET_NBYTES(count));
+
+    /* make room for buffering */
+    td->npkt += mpkt;
+
+    temp = sc->sc_in_ctl[td->ep_no];
+
+    /* check for isochronous mode */
+    if ((temp & DIEPCTL_EPTYPE_MASK) ==
+        (DIEPCTL_EPTYPE_ISOC << DIEPCTL_EPTYPE_SHIFT)) {
+        /* toggle odd or even frame bit */
+        if (temp & DIEPCTL_SETD1PID) {
+            temp &= ~DIEPCTL_SETD1PID;
+            temp |= DIEPCTL_SETD0PID;
+        } else {
+            temp &= ~DIEPCTL_SETD0PID;
+            temp |= DIEPCTL_SETD1PID;
+        }
+        sc->sc_in_ctl[td->ep_no] = temp;
+    }
+
+    /* must enable before writing data to FIFO */
+    DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(td->ep_no), temp |
+        DIEPCTL_EPENA | DIEPCTL_CNAK);
+
+    td->tx_bytes = count;
+
+    /* check remainder */
+    if (td->tx_bytes == 0 &&
+        td->remainder == 0) {
+        if (td->short_pkt)
+            return (0);    /* complete */
+
+        /* else we need to transmit a short packet */
+    }
+    goto repeat;
+
+not_complete:
+    return (1);            /* not complete */
+}
+
+static uint8_t
+dwc_otg_data_tx_sync(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+    uint32_t temp;
+
+    /*
+     * If all packets are transferred we are complete:
+     */
+    temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no));
+
+    /* check that all packets have been transferred */
+    if (DXEPTSIZ_GET_NPKT(temp) != 0) {
+        DPRINTFN(5, "busy ep=%d\n", td->ep_no);
+        goto not_complete;
+    }
+    return (0);
+
+not_complete:
+
+    /* we only want to know if there is a SETUP packet or free IN packet */
+
+    temp = sc->sc_last_rx_status;
+
+    if ((td->ep_no == 0) && (temp != 0) &&
+        (GRXSTSRD_CHNUM_GET(temp) == 0)) {
+
+        if ((temp & GRXSTSRD_PKTSTS_MASK) ==
+            GRXSTSRD_STP_DATA) {
+            DPRINTFN(5, "faking complete\n");
+            /*
+             * Race condition: We are complete!
+             */
+            return (0);
+        } else {
+            /* dump data - wrong direction */
+            dwc_otg_common_rx_ack(sc);
+        }
+    }
+    return (1);            /* not complete */
+}
+
+static void
+dwc_otg_xfer_do_fifo(struct dwc_otg_softc *sc, struct usb_xfer *xfer)
+{
+    struct dwc_otg_td *td;
+    uint8_t toggle;
+    uint8_t tmr_val;
+    uint8_t tmr_res;
+
+    DPRINTFN(9, "\n");
+
+    td = xfer->td_transfer_cache;
+    if (td == NULL)
+        return;
+
+    while (1) {
+        if ((td->func) (sc, td)) {
+            /* operation in progress */
+            break;
+        }
+        if (((void *)td) == xfer->td_transfer_last) {
+            goto done;
+        }
+        if (td->error_any) {
+            goto done;
+        } else if (td->remainder > 0) {
+            /*
+             * We had a short transfer. If there is no alternate
+             * next, stop processing !
+             */
+            if (!td->alt_next)
+                goto done;
+        }
+
+        /*
+         * Fetch the next transfer descriptor and transfer
+         * some flags to the next transfer descriptor
+         */
+        tmr_res = td->tmr_res;
+        tmr_val = td->tmr_val;
+        toggle = td->toggle;
+        td = td->obj_next;
+        xfer->td_transfer_cache = td;
+        td->toggle = toggle;    /* transfer toggle */
+        td->tmr_res = tmr_res;
+        td->tmr_val = tmr_val;
+    }
+    return;
+
+done:
+    xfer->td_transfer_cache = NULL;
+    sc->sc_xfer_complete = 1;
+}
+
+static uint8_t
+dwc_otg_xfer_do_complete_locked(struct dwc_otg_softc *sc, struct usb_xfer
*xfer)
+{
+    struct dwc_otg_td *td;
+
+    DPRINTFN(9, "\n");
+
+    td = xfer->td_transfer_cache;
+    if (td == NULL) {
+        /* compute all actual lengths */
+        dwc_otg_standard_done(xfer);
+        return (1);
+    }
+    return (0);
+}
+
+static void
+dwc_otg_timer(void *_sc)
+{
+    struct dwc_otg_softc *sc = _sc;
+    struct usb_xfer *xfer;
+    struct dwc_otg_td *td;
+
+    USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+    DPRINTF("\n");
+
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    /* increment timer value */
+    sc->sc_tmr_val++;
+
+    TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+        td = xfer->td_transfer_cache;
+        if (td != NULL) {
+            /* reset NAK counter */
+            td->did_nak = 0;
+        }
+    }
+
+    /* enable SOF interrupt, which will poll jobs */
+    dwc_otg_enable_sof_irq(sc);
+
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+
+    if (sc->sc_timer_active) {
+        /* restart timer */
+        usb_callout_reset(&sc->sc_timer,
+            hz / (1000 / DWC_OTG_HOST_TIMER_RATE),
+            &dwc_otg_timer, sc);
+    }
+}
+
+static void
+dwc_otg_timer_start(struct dwc_otg_softc *sc)
+{
+    if (sc->sc_timer_active != 0)
+        return;
+
+    sc->sc_timer_active = 1;
+
+    /* restart timer */
+    usb_callout_reset(&sc->sc_timer,
+        hz / (1000 / DWC_OTG_HOST_TIMER_RATE),
+        &dwc_otg_timer, sc);
+}
+
+static void
+dwc_otg_timer_stop(struct dwc_otg_softc *sc)
+{
+    if (sc->sc_timer_active == 0)
+        return;
+
+    sc->sc_timer_active = 0;
+
+    /* stop timer */
+    usb_callout_stop(&sc->sc_timer);
+}
+
+static void
+dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x)
+{
+    uint32_t hcchar;
+
+    hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
+
+    /* disable host channel, if any */
+    if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) {
+        /* disable channel */
+        DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
+            HCCHAR_CHENA | HCCHAR_CHDIS);
+        /* wait for chip to get its brains in order */
+        sc->sc_chan_state[x].wait_sof = 2;
+    }
+
+    /* release TX FIFO usage, if any */
+    sc->sc_tx_cur_p_level -= sc->sc_chan_state[x].tx_p_size;
+    sc->sc_tx_cur_np_level -= sc->sc_chan_state[x].tx_np_size;
+
+    /* don't release TX FIFO usage twice */
+    sc->sc_chan_state[x].tx_p_size = 0;
+    sc->sc_chan_state[x].tx_np_size = 0;
+}
+
+static uint16_t
+dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo)
+{
+    if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX)
+        pinfo->slot_index++;
+    return (pinfo->slot_index);
+}
+
+static uint8_t
+dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc)
+{
+    TAILQ_HEAD(, usb_xfer) head;
+    struct usb_xfer *xfer;
+    struct usb_xfer *xfer_next;
+    struct dwc_otg_td *td;
+    uint16_t temp;
+    uint16_t slot;
+    uint8_t x;
+
+    temp = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK;
+
+    if (sc->sc_last_frame_num == temp)
+        return (0);
+
+    sc->sc_last_frame_num = temp;
+
+    TAILQ_INIT(&head);
+
+    for (x = 0; x != sc->sc_host_ch_max; x++) {
+        if (sc->sc_chan_state[x].wait_sof == 0)
+            continue;
+
+        sc->sc_needsof = 1;
+        if (--(sc->sc_chan_state[x].wait_sof) == 0)
+            dwc_otg_host_channel_disable(sc, x);
+    }
+
+    if ((temp & 7) == 0) {
+
+        /* reset the schedule */
+        memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
+
+        TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+            td = xfer->td_transfer_cache;
+            if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
+                continue;
+
+            /* check for IN direction */
+            if ((td->hcchar & HCCHAR_EPDIR_IN) != 0)
+                continue;
+
+            /* execute more frames */
+            td->tmr_val = 0;
+
+            sc->sc_needsof = 1;
+
+            if (td->hcsplt == 0 || td->tt_scheduled != 0)
+                continue;
+
+            /* compute slot */
+            slot = dwc_otg_compute_isoc_rx_tt_slot(
+                sc->sc_tt_info + td->tt_index);
+            if (slot > 3) {
+                /*
+                 * Not enough time to get complete
+                 * split executed.
+                 */
+                continue;
+            }
+            /* Delayed start */
+            td->tt_start_slot = temp + slot;
+            td->tt_scheduled = 1;
+            TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+            TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+        }
+
+        TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+            td = xfer->td_transfer_cache;
+            if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
+                continue;
+
+            /* check for OUT direction */
+            if ((td->hcchar & HCCHAR_EPDIR_IN) == 0)
+                continue;
+
+            /* execute more frames */
+            td->tmr_val = 0;
+
+            sc->sc_needsof = 1;
+
+            if (td->hcsplt == 0 || td->tt_scheduled != 0)
+                continue;
+
+            /* Start ASAP */
+            td->tt_start_slot = temp;
+            td->tt_scheduled = 1;
+            TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+            TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+        }
+
+        TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+            td = xfer->td_transfer_cache;
+            if (td == NULL || td->ep_type != UE_INTERRUPT)
+                continue;
+
+            if (td->tt_scheduled != 0) {
+                sc->sc_needsof = 1;
+                continue;
+            }
+
+            if (dwc_otg_host_rate_check_interrupt(sc, td))
+                continue;
+
+            if (td->hcsplt == 0) {
+                sc->sc_needsof = 1;
+                td->tt_scheduled = 1;
+                continue;
+            }
+
+            /* start ASAP */
+            td->tt_start_slot = temp;
+            sc->sc_needsof = 1;
+            td->tt_scheduled = 1;
+            TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+            TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+        }
+
+        TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+            td = xfer->td_transfer_cache;
+            if (td == NULL ||
+                td->ep_type != UE_CONTROL ||
+                td->did_nak >= DWC_OTG_NAK_MAX) {
+                continue;
+            }
+
+            sc->sc_needsof = 1;
+
+            if (td->hcsplt == 0 || td->tt_scheduled != 0)
+                continue;
+
+            /* start ASAP */
+            td->tt_start_slot = temp;
+            td->tt_scheduled = 1;
+            TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+            TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+        }
+    }
+    if ((temp & 7) < 6) {
+        TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+            td = xfer->td_transfer_cache;
+            if (td == NULL ||
+                td->ep_type != UE_BULK ||
+                td->did_nak >= DWC_OTG_NAK_MAX) {
+                continue;
+            }
+
+            sc->sc_needsof = 1;
+
+            if (td->hcsplt == 0 || td->tt_scheduled != 0)
+                continue;
+
+            /* start ASAP */
+            td->tt_start_slot = temp;
+            td->tt_scheduled = 1;
+            TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+            TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+        }
+    }
+
+    /* Put TT transfers in execution order at the end */
+    TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
+
+    /* move all TT transfers in front, keeping the current order */
+    TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+        td = xfer->td_transfer_cache;
+        if (td == NULL || td->hcsplt == 0)
+            continue;
+        TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+        TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+    }
+    TAILQ_CONCAT(&head, &sc->sc_bus.intr_q.head, wait_entry);
+    TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
+
+    /* put non-TT BULK transfers last */
+    TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry,
xfer_next) {
+        td = xfer->td_transfer_cache;
+        if (td == NULL || td->hcsplt != 0 || td->ep_type != UE_BULK)
+            continue;
+        TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+        TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+    }
+    TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
+
+    if ((temp & 7) == 0) {
+
+        DPRINTFN(12, "SOF interrupt #%d, needsof=%d\n",
+            (int)temp, (int)sc->sc_needsof);
+
+        /* update SOF IRQ mask */
+        if (sc->sc_irq_mask & GINTMSK_SOFMSK) {
+            if (sc->sc_needsof == 0) {
+                sc->sc_irq_mask &= ~GINTMSK_SOFMSK;
+                DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+            }
+        } else {
+            if (sc->sc_needsof != 0) {
+                sc->sc_irq_mask |= GINTMSK_SOFMSK;
+                DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+            }
+        }
+
+        /* clear need SOF flag */
+        sc->sc_needsof = 0;
+    }
+    return (1);
+}
+
+static void
+dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc)
+{
+    struct usb_xfer *xfer;
+    uint32_t temp;
+    uint8_t got_rx_status;
+    uint8_t x;
+
+repeat:
+    /* get all channel interrupts */
+    for (x = 0; x != sc->sc_host_ch_max; x++) {
+        temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+        if (temp != 0) {
+            DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
+            temp &= ~HCINT_SOFTWARE_ONLY;
+            sc->sc_chan_state[x].hcint |= temp;
+        }
+    }
+
+    if (sc->sc_last_rx_status == 0) {
+
+        temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
+        if (temp & GINTSTS_RXFLVL) {
+            /* pop current status */
+            sc->sc_last_rx_status =
+                DWC_OTG_READ_4(sc, DOTG_GRXSTSPD);
+        }
+
+        if (sc->sc_last_rx_status != 0) {
+
+            uint8_t ep_no;
+
+            temp = sc->sc_last_rx_status &
+                GRXSTSRD_PKTSTS_MASK;
+
+            /* non-data messages we simply skip */
+            if (temp != GRXSTSRD_STP_DATA &&
+                temp != GRXSTSRD_OUT_DATA) {
+                dwc_otg_common_rx_ack(sc);
+                goto repeat;
+            }
+
+            temp = GRXSTSRD_BCNT_GET(
+                sc->sc_last_rx_status);
+            ep_no = GRXSTSRD_CHNUM_GET(
+                sc->sc_last_rx_status);
+
+            /* receive data, if any */
+            if (temp != 0) {
+                DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no);
+                bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+                    DOTG_DFIFO(ep_no),
+                    sc->sc_rx_bounce_buffer, (temp + 3) / 4);
+            }
+
+            /* check if we should dump the data */
+            if (!(sc->sc_active_rx_ep & (1U << ep_no))) {
+                dwc_otg_common_rx_ack(sc);
+                goto repeat;
+            }
+
+            got_rx_status = 1;
+
+            DPRINTFN(5, "RX status = 0x%08x: ch=%d pid=%d bytes=%d
sts=%d\n",
+                sc->sc_last_rx_status, ep_no,
+                (sc->sc_last_rx_status >> 15) & 3,
+                GRXSTSRD_BCNT_GET(sc->sc_last_rx_status),
+                (sc->sc_last_rx_status >> 17) & 15);
+        } else {
+            got_rx_status = 0;
+        }
+    } else {
+        uint8_t ep_no;
+
+        ep_no = GRXSTSRD_CHNUM_GET(
+            sc->sc_last_rx_status);
+
+        /* check if we should dump the data */
+        if (!(sc->sc_active_rx_ep & (1U << ep_no))) {
+            dwc_otg_common_rx_ack(sc);
+            goto repeat;
+        }
+
+        got_rx_status = 1;
+    }
+
+    /* execute FIFOs */
+    TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry)
+        dwc_otg_xfer_do_fifo(sc, xfer);
+
+    if (got_rx_status) {
+        /* check if data was consumed */
+        if (sc->sc_last_rx_status == 0)
+            goto repeat;
+
+        /* disable RX FIFO level interrupt */
+        sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK;
+        DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+    }
+
+    if (sc->sc_flags.status_device_mode == 0 && sc->sc_xfer_complete == 0)
{
+        /* update host transfer schedule, so that new transfers can be
issued */
+        if (dwc_otg_update_host_transfer_schedule_locked(sc))
+            goto repeat;
+    }
+}
+
+static void
+dwc_otg_interrupt_complete_locked(struct dwc_otg_softc *sc)
+{
+    struct usb_xfer *xfer;
+repeat:
+    /* scan for completion events */
+    TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+        if (dwc_otg_xfer_do_complete_locked(sc, xfer))
+            goto repeat;
+    }
+}
+
+static void
+dwc_otg_vbus_interrupt(struct dwc_otg_softc *sc, uint8_t is_on)
+{
+    DPRINTFN(5, "vbus = %u\n", is_on);
+
+    /*
+     * If the USB host mode is forced, then assume VBUS is always
+     * present else rely on the input to this function:
+     */
+    if ((is_on != 0) || (sc->sc_mode == DWC_MODE_HOST)) {
+
+        if (!sc->sc_flags.status_vbus) {
+            sc->sc_flags.status_vbus = 1;
+
+            /* complete root HUB interrupt endpoint */
+
+            dwc_otg_root_intr(sc);
+        }
+    } else {
+        if (sc->sc_flags.status_vbus) {
+            sc->sc_flags.status_vbus = 0;
+            sc->sc_flags.status_bus_reset = 0;
+            sc->sc_flags.status_suspend = 0;
+            sc->sc_flags.change_suspend = 0;
+            sc->sc_flags.change_connect = 1;
+
+            /* complete root HUB interrupt endpoint */
+
+            dwc_otg_root_intr(sc);
+        }
+    }
+}
+
+int
+dwc_otg_filter_interrupt(void *arg)
+{
+    struct dwc_otg_softc *sc = arg;
+    int retval = FILTER_HANDLED;
+    uint32_t status;
+
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    /* read and clear interrupt status */
+    status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
+
+    /* clear interrupts we are handling here */
+    DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status &
~DWC_OTG_MSK_GINT_THREAD_IRQ);
+
+    /* check for USB state change interrupts */
+    if ((status & DWC_OTG_MSK_GINT_THREAD_IRQ) != 0)
+        retval = FILTER_SCHEDULE_THREAD;
+
+    /* clear all IN endpoint interrupts */
+    if (status & GINTSTS_IEPINT) {
+        uint32_t temp;
+        uint8_t x;
+
+        for (x = 0; x != sc->sc_dev_in_ep_max; x++) {
+            temp = DWC_OTG_READ_4(sc, DOTG_DIEPINT(x));
+            if (temp & DIEPMSK_XFERCOMPLMSK) {
+                DWC_OTG_WRITE_4(sc, DOTG_DIEPINT(x),
+                    DIEPMSK_XFERCOMPLMSK);
+            }
+        }
+    }
+
+    /* poll FIFOs, if any */
+    dwc_otg_interrupt_poll_locked(sc);
+
+    if (sc->sc_xfer_complete != 0)
+        retval = FILTER_SCHEDULE_THREAD;
+
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+
+    return (retval);
+}
+
+void
+dwc_otg_interrupt(void *arg)
+{
+    struct dwc_otg_softc *sc = arg;
+    uint32_t status;
+
+    USB_BUS_LOCK(&sc->sc_bus);
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    /* read and clear interrupt status */
+    status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
+
+    /* clear interrupts we are handling here */
+    DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status &
DWC_OTG_MSK_GINT_THREAD_IRQ);
+
+    DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n",
+        status, DWC_OTG_READ_4(sc, DOTG_HAINT),
+        DWC_OTG_READ_4(sc, DOTG_HFNUM));
+
+    if (status & GINTSTS_USBRST) {
+
+        /* set correct state */
+        sc->sc_flags.status_device_mode = 1;
+        sc->sc_flags.status_bus_reset = 0;
+        sc->sc_flags.status_suspend = 0;
+        sc->sc_flags.change_suspend = 0;
+        sc->sc_flags.change_connect = 1;
+
+        /* Disable SOF interrupt */
+        sc->sc_irq_mask &= ~GINTMSK_SOFMSK;
+        DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+
+        /* complete root HUB interrupt endpoint */
+        dwc_otg_root_intr(sc);
+    }
+
+    /* check for any bus state change interrupts */
+    if (status & GINTSTS_ENUMDONE) {
+
+        uint32_t temp;
+
+        DPRINTFN(5, "end of reset\n");
+
+        /* set correct state */
+        sc->sc_flags.status_device_mode = 1;
+        sc->sc_flags.status_bus_reset = 1;
+        sc->sc_flags.status_suspend = 0;
+        sc->sc_flags.change_suspend = 0;
+        sc->sc_flags.change_connect = 1;
+        sc->sc_flags.status_low_speed = 0;
+        sc->sc_flags.port_enabled = 1;
+
+        /* reset FIFOs */
+        (void) dwc_otg_init_fifo(sc, DWC_MODE_DEVICE);
+
+        /* reset function address */
+        dwc_otg_set_address(sc, 0);
+
+        /* figure out enumeration speed */
+        temp = DWC_OTG_READ_4(sc, DOTG_DSTS);
+        if (DSTS_ENUMSPD_GET(temp) == DSTS_ENUMSPD_HI)
+            sc->sc_flags.status_high_speed = 1;
+        else
+            sc->sc_flags.status_high_speed = 0;
+
+        /*
+         * Disable resume and SOF interrupt, and enable
+         * suspend and RX frame interrupt:
+         */
+        sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK);
+        sc->sc_irq_mask |= GINTMSK_USBSUSPMSK;
+        DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+
+        /* complete root HUB interrupt endpoint */
+        dwc_otg_root_intr(sc);
+    }
+
+    if (status & GINTSTS_PRTINT) {
+        uint32_t hprt;
+
+        hprt = DWC_OTG_READ_4(sc, DOTG_HPRT);
+
+        /* clear change bits */
+        DWC_OTG_WRITE_4(sc, DOTG_HPRT, (hprt & (
+            HPRT_PRTPWR | HPRT_PRTENCHNG |
+            HPRT_PRTCONNDET | HPRT_PRTOVRCURRCHNG)) |
+            sc->sc_hprt_val);
+
+        DPRINTFN(12, "GINTSTS=0x%08x, HPRT=0x%08x\n", status, hprt);
+
+        sc->sc_flags.status_device_mode = 0;
+
+        if (hprt & HPRT_PRTCONNSTS)
+            sc->sc_flags.status_bus_reset = 1;
+        else
+            sc->sc_flags.status_bus_reset = 0;
+
+        if (hprt & HPRT_PRTENCHNG)
+            sc->sc_flags.change_enabled = 1;
+
+        if (hprt & HPRT_PRTENA)
+            sc->sc_flags.port_enabled = 1;
+        else
+            sc->sc_flags.port_enabled = 0;
+
+        if (hprt & HPRT_PRTOVRCURRCHNG)
+            sc->sc_flags.change_over_current = 1;
+
+        if (hprt & HPRT_PRTOVRCURRACT)
+            sc->sc_flags.port_over_current = 1;
+        else
+            sc->sc_flags.port_over_current = 0;
+
+        if (hprt & HPRT_PRTPWR)
+            sc->sc_flags.port_powered = 1;
+        else
+            sc->sc_flags.port_powered = 0;
+
+        if (((hprt & HPRT_PRTSPD_MASK)
+            >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_LOW)
+            sc->sc_flags.status_low_speed = 1;
+        else
+            sc->sc_flags.status_low_speed = 0;
+
+        if (((hprt & HPRT_PRTSPD_MASK)
+            >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_HIGH)
+            sc->sc_flags.status_high_speed = 1;
+        else
+            sc->sc_flags.status_high_speed = 0;
+
+        if (hprt & HPRT_PRTCONNDET)
+            sc->sc_flags.change_connect = 1;
+
+        if (hprt & HPRT_PRTSUSP)
+            dwc_otg_suspend_irq(sc);
+        else
+            dwc_otg_resume_irq(sc);
+
+        /* complete root HUB interrupt endpoint */
+        dwc_otg_root_intr(sc);
+
+        /* update host frame interval */
+        dwc_otg_update_host_frame_interval(sc);
+    }
+
+    /*
+     * If resume and suspend is set at the same time we interpret
+     * that like RESUME. Resume is set when there is at least 3
+     * milliseconds of inactivity on the USB BUS.
+     */
+    if (status & GINTSTS_WKUPINT) {
+
+        DPRINTFN(5, "resume interrupt\n");
+
+        dwc_otg_resume_irq(sc);
+
+    } else if (status & GINTSTS_USBSUSP) {
+
+        DPRINTFN(5, "suspend interrupt\n");
+
+        dwc_otg_suspend_irq(sc);
+    }
+    /* check VBUS */
+    if (status & (GINTSTS_USBSUSP |
+        GINTSTS_USBRST |
+        GINTMSK_OTGINTMSK |
+        GINTSTS_SESSREQINT)) {
+        uint32_t temp;
+
+        temp = DWC_OTG_READ_4(sc, DOTG_GOTGCTL);
+
+        DPRINTFN(5, "GOTGCTL=0x%08x\n", temp);
+
+        dwc_otg_vbus_interrupt(sc,
+            (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0);
+    }
+
+    if (sc->sc_xfer_complete != 0) {
+        sc->sc_xfer_complete = 0;
+
+        /* complete FIFOs, if any */
+        dwc_otg_interrupt_complete_locked(sc);
+
+        if (sc->sc_flags.status_device_mode == 0) {
+            /* update host transfer schedule, so that new transfers can be
issued */
+            if (dwc_otg_update_host_transfer_schedule_locked(sc))
+                dwc_otg_interrupt_poll_locked(sc);
+        }
+    }
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+    USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp)
+{
+    struct dwc_otg_td *td;
+
+    /* get current Transfer Descriptor */
+    td = temp->td_next;
+    temp->td = td;
+
+    /* prepare for next TD */
+    temp->td_next = td->obj_next;
+
+    /* fill out the Transfer Descriptor */
+    td->func = temp->func;
+    td->pc = temp->pc;
+    td->offset = temp->offset;
+    td->remainder = temp->len;
+    td->tx_bytes = 0;
+    td->error_any = 0;
+    td->error_stall = 0;
+    td->npkt = 0;
+    td->did_stall = temp->did_stall;
+    td->short_pkt = temp->short_pkt;
+    td->alt_next = temp->setup_alt_next;
+    td->set_toggle = 0;
+    td->got_short = 0;
+    td->did_nak = 0;
+    td->channel = DWC_OTG_MAX_CHANNELS;
+    td->state = 0;
+    td->errcnt = 0;
+    td->tt_scheduled = 0;
+    td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
+}
+
+static void
+dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
+{
+    struct dwc_otg_std_temp temp;
+    struct dwc_otg_td *td;
+    uint32_t x;
+    uint8_t need_sync;
+    uint8_t is_host;
+
+    DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+        xfer->address, UE_GET_ADDR(xfer->endpointno),
+        xfer->sumlen, usbd_get_speed(xfer->xroot->udev));
+
+    temp.max_frame_size = xfer->max_frame_size;
+
+    td = xfer->td_start[0];
+    xfer->td_transfer_first = td;
+    xfer->td_transfer_cache = td;
+
+    /* setup temp */
+
+    temp.pc = NULL;
+    temp.td = NULL;
+    temp.td_next = xfer->td_start[0];
+    temp.offset = 0;
+    temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+        xfer->flags_int.isochronous_xfr;
+    temp.did_stall = !xfer->flags_int.control_stall;
+
+    is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST);
+
+    /* check if we should prepend a setup message */
+
+    if (xfer->flags_int.control_xfr) {
+        if (xfer->flags_int.control_hdr) {
+
+            if (is_host)
+                temp.func = &dwc_otg_host_setup_tx;
+            else
+                temp.func = &dwc_otg_setup_rx;
+
+            temp.len = xfer->frlengths[0];
+            temp.pc = xfer->frbuffers + 0;
+            temp.short_pkt = temp.len ? 1 : 0;
+
+            /* check for last frame */
+            if (xfer->nframes == 1) {
+                /* no STATUS stage yet, SETUP is last */
+                if (xfer->flags_int.control_act)
+                    temp.setup_alt_next = 0;
+            }
+
+            dwc_otg_setup_standard_chain_sub(&temp);
+        }
+        x = 1;
+    } else {
+        x = 0;
+    }
+
+    if (x != xfer->nframes) {
+        if (xfer->endpointno & UE_DIR_IN) {
+            if (is_host) {
+                temp.func = &dwc_otg_host_data_rx;
+                need_sync = 0;
+            } else {
+                temp.func = &dwc_otg_data_tx;
+                need_sync = 1;
+            }
+        } else {
+            if (is_host) {
+                temp.func = &dwc_otg_host_data_tx;
+                need_sync = 0;
+            } else {
+                temp.func = &dwc_otg_data_rx;
+                need_sync = 0;
+            }
+        }
+
+        /* setup "pc" pointer */
+        temp.pc = xfer->frbuffers + x;
+    } else {
+        need_sync = 0;
+    }
+    while (x != xfer->nframes) {
+
+        /* DATA0 / DATA1 message */
+
+        temp.len = xfer->frlengths[x];
+
+        x++;
+
+        if (x == xfer->nframes) {
+            if (xfer->flags_int.control_xfr) {
+                if (xfer->flags_int.control_act) {
+                    temp.setup_alt_next = 0;
+                }
+            } else {
+                temp.setup_alt_next = 0;
+            }
+        }
+        if (temp.len == 0) {
+
+            /* make sure that we send an USB packet */
+
+            temp.short_pkt = 0;
+
+        } else {
+
+            /* regular data transfer */
+
+            temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1);
+        }
+
+        dwc_otg_setup_standard_chain_sub(&temp);
+
+        if (xfer->flags_int.isochronous_xfr) {
+            temp.offset += temp.len;
+        } else {
+            /* get next Page Cache pointer */
+            temp.pc = xfer->frbuffers + x;
+        }
+    }
+
+    if (xfer->flags_int.control_xfr) {
+
+        /* always setup a valid "pc" pointer for status and sync */
+        temp.pc = xfer->frbuffers + 0;
+        temp.len = 0;
+        temp.short_pkt = 0;
+        temp.setup_alt_next = 0;
+
+        /* check if we need to sync */
+        if (need_sync) {
+            /* we need a SYNC point after TX */
+            temp.func = &dwc_otg_data_tx_sync;
+            dwc_otg_setup_standard_chain_sub(&temp);
+        }
+
+        /* check if we should append a status stage */
+        if (!xfer->flags_int.control_act) {
+
+            /*
+             * Send a DATA1 message and invert the current
+             * endpoint direction.
+             */
+            if (xfer->endpointno & UE_DIR_IN) {
+                if (is_host) {
+                    temp.func = &dwc_otg_host_data_tx;
+                    need_sync = 0;
+                } else {
+                    temp.func = &dwc_otg_data_rx;
+                    need_sync = 0;
+                }
+            } else {
+                if (is_host) {
+                    temp.func = &dwc_otg_host_data_rx;
+                    need_sync = 0;
+                } else {
+                    temp.func = &dwc_otg_data_tx;
+                    need_sync = 1;
+                }
+            }
+
+            dwc_otg_setup_standard_chain_sub(&temp);
+
+            /* data toggle should be DATA1 */
+            td = temp.td;
+            td->set_toggle = 1;
+
+            if (need_sync) {
+                /* we need a SYNC point after TX */
+                temp.func = &dwc_otg_data_tx_sync;
+                dwc_otg_setup_standard_chain_sub(&temp);
+            }
+        }
+    } else {
+        /* check if we need to sync */
+        if (need_sync) {
+
+            temp.pc = xfer->frbuffers + 0;
+            temp.len = 0;
+            temp.short_pkt = 0;
+            temp.setup_alt_next = 0;
+
+            /* we need a SYNC point after TX */
+            temp.func = &dwc_otg_data_tx_sync;
+            dwc_otg_setup_standard_chain_sub(&temp);
+        }
+    }
+
+    /* must have at least one frame! */
+    td = temp.td;
+    xfer->td_transfer_last = td;
+
+    if (is_host) {
+
+        struct dwc_otg_softc *sc;
+        uint32_t hcchar;
+        uint32_t hcsplt;
+
+        sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
+
+        /* get first again */
+        td = xfer->td_transfer_first;
+        td->toggle = (xfer->endpoint->toggle_next ? 1 : 0);
+
+        hcchar =
+            (xfer->address << HCCHAR_DEVADDR_SHIFT) |
+            ((xfer->endpointno & UE_ADDR) << HCCHAR_EPNUM_SHIFT) |
+            (xfer->max_packet_size << HCCHAR_MPS_SHIFT) |
+            HCCHAR_CHENA;
+
+        /*
+         * We are not always able to meet the timing
+         * requirements of the USB interrupt endpoint's
+         * complete split token, when doing transfers going
+         * via a transaction translator. Use the CONTROL
+         * transfer type instead of the INTERRUPT transfer
+         * type in general, as a means to workaround
+         * that. This trick should work for both FULL and LOW
+         * speed USB traffic going through a TT. For non-TT
+         * traffic it works aswell. The reason for using
+         * CONTROL type instead of BULK is that some TTs might
+         * reject LOW speed BULK traffic.
+         */
+        if (td->ep_type == UE_INTERRUPT)
+            hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT);
+        else
+            hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT);
+
+        if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_LOW)
+            hcchar |= HCCHAR_LSPDDEV;
+        if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN)
+            hcchar |= HCCHAR_EPDIR_IN;
+
+        switch (xfer->xroot->udev->speed) {
+        case USB_SPEED_FULL:
+        case USB_SPEED_LOW:
+            /* check if root HUB port is running High Speed */
+            if (xfer->xroot->udev->parent_hs_hub != NULL) {
+                hcsplt = HCSPLT_SPLTENA |
+                    (xfer->xroot->udev->hs_port_no <<
+                    HCSPLT_PRTADDR_SHIFT) |
+                    (xfer->xroot->udev->hs_hub_addr <<
+                    HCSPLT_HUBADDR_SHIFT);
+            } else {
+                hcsplt = 0;
+            }
+            if (td->ep_type == UE_INTERRUPT) {
+                uint32_t ival;
+                ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
+                if (ival == 0)
+                    ival = 1;
+                else if (ival > 127)
+                    ival = 127;
+                td->tmr_val = sc->sc_tmr_val + ival;
+                td->tmr_res = ival;
+            } else if (td->ep_type == UE_ISOCHRONOUS) {
+                td->tmr_val = 0;
+                td->tmr_res = 1;
+            } else {
+                td->tmr_val = 0;
+                td->tmr_res = 0;
+            }
+            break;
+        case USB_SPEED_HIGH:
+            hcsplt = 0;
+            if (td->ep_type == UE_INTERRUPT) {
+                uint32_t ival;
+#if 0
+                hcchar |= ((xfer->max_packet_count & 3)
+                    << HCCHAR_MC_SHIFT);
+#endif
+                ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
+                if (ival == 0)
+                    ival = 1;
+                else if (ival > 127)
+                    ival = 127;
+                td->tmr_val = sc->sc_tmr_val + ival;
+                td->tmr_res = ival;
+            } else if (td->ep_type == UE_ISOCHRONOUS) {
+                hcchar |= ((xfer->max_packet_count & 3)
+                    << HCCHAR_MC_SHIFT);
+                td->tmr_val = 0;
+                td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer);
+            } else {
+                td->tmr_val = 0;
+                td->tmr_res = 0;
+            }
+            break;
+        default:
+            hcsplt = 0;
+            td->tmr_val = 0;
+            td->tmr_res = 0;
+            break;
+        }
+
+        /* store configuration in all TD's */
+        while (1) {
+            td->hcchar = hcchar;
+            td->hcsplt = hcsplt;
+
+            if (((void *)td) == xfer->td_transfer_last)
+                break;
+
+            td = td->obj_next;
+        }
+    }
+}
+
+static void
+dwc_otg_timeout(void *arg)
+{
+    struct usb_xfer *xfer = arg;
+
+    DPRINTF("xfer=%p\n", xfer);
+
+    USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+    /* transfer is transferred */
+    dwc_otg_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+dwc_otg_start_standard_chain(struct usb_xfer *xfer)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
+    struct usb_xfer_root *xroot;
+    struct dwc_otg_td *td;
+
+    DPRINTFN(9, "\n");
+
+    /*
+     * Poll one time in device mode, which will turn on the
+     * endpoint interrupts. Else wait for SOF interrupt in host
+     * mode.
+     */
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    if (sc->sc_flags.status_device_mode != 0) {
+        dwc_otg_xfer_do_fifo(sc, xfer);
+        if (dwc_otg_xfer_do_complete_locked(sc, xfer))
+            goto done;
+    }
+
+    /* put transfer on interrupt queue */
+    usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+    /* start timeout, if any */
+    if (xfer->timeout != 0) {
+        usbd_transfer_timeout_ms(xfer,
+            &dwc_otg_timeout, xfer->timeout);
+    }
+
+    if (sc->sc_flags.status_device_mode != 0)
+        goto done;
+
+    /* enable SOF interrupt, if any */
+    dwc_otg_enable_sof_irq(sc);
+
+    td = xfer->td_transfer_cache;
+    if (td->ep_type != UE_BULK)
+        goto done;
+
+    xroot = xfer->xroot;
+
+    /*
+     * Optimise the ping-pong effect by waking up other BULK
+     * transfers belonging to the same device group:
+     */
+    TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+        td = xfer->td_transfer_cache;
+        if (td == NULL || td->ep_type != UE_BULK || xfer->xroot != xroot)
+            continue;
+        /* reset NAK counter */
+        td->did_nak = 0;
+    }
+done:
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+static void
+dwc_otg_root_intr(struct dwc_otg_softc *sc)
+{
+    DPRINTFN(9, "\n");
+
+    USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+    /* set port bit */
+    sc->sc_hub_idata[0] = 0x02;    /* we only have one port */
+
+    uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
+        sizeof(sc->sc_hub_idata));
+}
+
+static usb_error_t
+dwc_otg_standard_done_sub(struct usb_xfer *xfer)
+{
+    struct dwc_otg_td *td;
+    uint32_t len;
+    usb_error_t error;
+
+    DPRINTFN(9, "\n");
+
+    td = xfer->td_transfer_cache;
+
+    do {
+        len = td->remainder;
+
+        /* store last data toggle */
+        xfer->endpoint->toggle_next = td->toggle;
+
+        if (xfer->aframes != xfer->nframes) {
+            /*
+             * Verify the length and subtract
+             * the remainder from "frlengths[]":
+             */
+            if (len > xfer->frlengths[xfer->aframes]) {
+                td->error_any = 1;
+            } else {
+                xfer->frlengths[xfer->aframes] -= len;
+            }
+        }
+        /* Check for transfer error */
+        if (td->error_any) {
+            /* the transfer is finished */
+            error = (td->error_stall ?
+                USB_ERR_STALLED : USB_ERR_IOERROR);
+            td = NULL;
+            break;
+        }
+        /* Check for short transfer */
+        if (len > 0) {
+            if (xfer->flags_int.short_frames_ok ||
+                xfer->flags_int.isochronous_xfr) {
+                /* follow alt next */
+                if (td->alt_next) {
+                    td = td->obj_next;
+                } else {
+                    td = NULL;
+                }
+            } else {
+                /* the transfer is finished */
+                td = NULL;
+            }
+            error = 0;
+            break;
+        }
+        td = td->obj_next;
+
+        /* this USB frame is complete */
+        error = 0;
+        break;
+
+    } while (0);
+
+    /* update transfer cache */
+
+    xfer->td_transfer_cache = td;
+
+    return (error);
+}
+
+static void
+dwc_otg_standard_done(struct usb_xfer *xfer)
+{
+    usb_error_t err = 0;
+
+    DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
+        xfer, xfer->endpoint);
+
+    /* reset scanner */
+
+    xfer->td_transfer_cache = xfer->td_transfer_first;
+
+    if (xfer->flags_int.control_xfr) {
+
+        if (xfer->flags_int.control_hdr) {
+
+            err = dwc_otg_standard_done_sub(xfer);
+        }
+        xfer->aframes = 1;
+
+        if (xfer->td_transfer_cache == NULL) {
+            goto done;
+        }
+    }
+    while (xfer->aframes != xfer->nframes) {
+
+        err = dwc_otg_standard_done_sub(xfer);
+        xfer->aframes++;
+
+        if (xfer->td_transfer_cache == NULL) {
+            goto done;
+        }
+    }
+
+    if (xfer->flags_int.control_xfr &&
+        !xfer->flags_int.control_act) {
+
+        err = dwc_otg_standard_done_sub(xfer);
+    }
+done:
+    dwc_otg_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ *    dwc_otg_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+
*------------------------------------------------------------------------*/
+static void
+dwc_otg_device_done(struct usb_xfer *xfer, usb_error_t error)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
+
+    DPRINTFN(9, "xfer=%p, endpoint=%p, error=%d\n",
+        xfer, xfer->endpoint, error);
+
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
+        /* Interrupts are cleared by the interrupt handler */
+    } else {
+        struct dwc_otg_td *td;
+
+        td = xfer->td_transfer_cache;
+         if (td != NULL)
+            dwc_otg_host_channel_free(sc, td);
+    }
+    /* dequeue transfer and start next transfer */
+    usbd_transfer_done(xfer, error);
+
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+static void
+dwc_otg_xfer_stall(struct usb_xfer *xfer)
+{
+    dwc_otg_device_done(xfer, USB_ERR_STALLED);
+}
+
+#ifndef __rtems__
+static void
+dwc_otg_set_stall(struct usb_device *udev,
+    struct usb_endpoint *ep, uint8_t *did_stall)
+#else
+static void
+dwc_otg_set_stall(struct usb_device *udev, struct usb_xfer *xfer,
+    struct usb_endpoint *ep, uint8_t *did_stall)
+#endif
+{
+    struct dwc_otg_softc *sc;
+    uint32_t temp;
+    uint32_t reg;
+    uint8_t ep_no;
+
+    USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+    /* check mode */
+    if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+        /* not supported */
+        return;
+    }
+
+    sc = DWC_OTG_BUS2SC(udev->bus);
+
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    /* get endpoint address */
+    ep_no = ep->edesc->bEndpointAddress;
+
+    DPRINTFN(5, "endpoint=0x%x\n", ep_no);
+
+    if (ep_no & UE_DIR_IN) {
+        reg = DOTG_DIEPCTL(ep_no & UE_ADDR);
+        temp = sc->sc_in_ctl[ep_no & UE_ADDR];
+    } else {
+        reg = DOTG_DOEPCTL(ep_no & UE_ADDR);
+        temp = sc->sc_out_ctl[ep_no & UE_ADDR];
+    }
+
+    /* disable and stall endpoint */
+    DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_EPDIS);
+    DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_STALL);
+
+    /* clear active OUT ep */
+    if (!(ep_no & UE_DIR_IN)) {
+
+        sc->sc_active_rx_ep &= ~(1U << (ep_no & UE_ADDR));
+
+        if (sc->sc_last_rx_status != 0 &&
+            (ep_no & UE_ADDR) == GRXSTSRD_CHNUM_GET(
+            sc->sc_last_rx_status)) {
+            /* dump data */
+            dwc_otg_common_rx_ack(sc);
+            /* poll interrupt */
+            dwc_otg_interrupt_poll_locked(sc);
+            dwc_otg_interrupt_complete_locked(sc);
+        }
+    }
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+    #ifdef __rtems__
+    if (xfer != NULL)
+        dwc_otg_xfer_stall(xfer);
+    #endif
+}
+
+static void
+dwc_otg_clear_stall_sub_locked(struct dwc_otg_softc *sc, uint32_t mps,
+    uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
+{
+    uint32_t reg;
+    uint32_t temp;
+
+    if (ep_type == UE_CONTROL) {
+        /* clearing stall is not needed */
+        return;
+    }
+
+    if (ep_dir) {
+        reg = DOTG_DIEPCTL(ep_no);
+    } else {
+        reg = DOTG_DOEPCTL(ep_no);
+        sc->sc_active_rx_ep |= (1U << ep_no);
+    }
+
+    /* round up and mask away the multiplier count */
+    mps = (mps + 3) & 0x7FC;
+
+    if (ep_type == UE_BULK) {
+        temp = DIEPCTL_EPTYPE_SET(
+            DIEPCTL_EPTYPE_BULK) |
+            DIEPCTL_USBACTEP;
+    } else if (ep_type == UE_INTERRUPT) {
+        temp = DIEPCTL_EPTYPE_SET(
+            DIEPCTL_EPTYPE_INTERRUPT) |
+            DIEPCTL_USBACTEP;
+    } else {
+        temp = DIEPCTL_EPTYPE_SET(
+            DIEPCTL_EPTYPE_ISOC) |
+            DIEPCTL_USBACTEP;
+    }
+
+    temp |= DIEPCTL_MPS_SET(mps);
+    temp |= DIEPCTL_TXFNUM_SET(ep_no);
+
+    if (ep_dir)
+        sc->sc_in_ctl[ep_no] = temp;
+    else
+        sc->sc_out_ctl[ep_no] = temp;
+
+    DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_EPDIS);
+    DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_SETD0PID);
+    DWC_OTG_WRITE_4(sc, reg, temp | DIEPCTL_SNAK);
+
+    /* we only reset the transmit FIFO */
+    if (ep_dir) {
+        DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+            GRSTCTL_TXFIFO(ep_no) |
+            GRSTCTL_TXFFLSH);
+
+        DWC_OTG_WRITE_4(sc,
+            DOTG_DIEPTSIZ(ep_no), 0);
+    }
+
+    /* poll interrupt */
+    dwc_otg_interrupt_poll_locked(sc);
+    dwc_otg_interrupt_complete_locked(sc);
+}
+
+static void
+dwc_otg_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
+{
+    struct dwc_otg_softc *sc;
+    struct usb_endpoint_descriptor *ed;
+
+    DPRINTFN(5, "endpoint=%p\n", ep);
+
+    USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+    /* check mode */
+    if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+        /* not supported */
+        return;
+    }
+    /* get softc */
+    sc = DWC_OTG_BUS2SC(udev->bus);
+
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+    /* get endpoint descriptor */
+    ed = ep->edesc;
+
+    /* reset endpoint */
+    dwc_otg_clear_stall_sub_locked(sc,
+        UGETW(ed->wMaxPacketSize),
+        (ed->bEndpointAddress & UE_ADDR),
+        (ed->bmAttributes & UE_XFERTYPE),
+        (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+static void
+dwc_otg_device_state_change(struct usb_device *udev)
+{
+    struct dwc_otg_softc *sc;
+    uint8_t x;
+
+    /* check mode */
+    if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+        /* not supported */
+        return;
+    }
+
+    /* get softc */
+    sc = DWC_OTG_BUS2SC(udev->bus);
+
+    /* deactivate all other endpoint but the control endpoint */
+    if (udev->state == USB_STATE_CONFIGURED ||
+        udev->state == USB_STATE_ADDRESSED) {
+
+        USB_BUS_LOCK(&sc->sc_bus);
+
+        for (x = 1; x != sc->sc_dev_ep_max; x++) {
+
+            if (x < sc->sc_dev_in_ep_max) {
+                DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(x),
+                    DIEPCTL_EPDIS);
+                DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(x), 0);
+            }
+
+            DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(x),
+                DOEPCTL_EPDIS);
+            DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(x), 0);
+        }
+        USB_BUS_UNLOCK(&sc->sc_bus);
+    }
+}
+
+int
+dwc_otg_init(struct dwc_otg_softc *sc)
+{
+    uint32_t temp;
+
+    DPRINTF("start\n");
+
+    /* set up the bus structure */
+    sc->sc_bus.usbrev = USB_REV_2_0;
+    sc->sc_bus.methods = &dwc_otg_bus_methods;
+
+    usb_callout_init_mtx(&sc->sc_timer,
+        &sc->sc_bus.bus_mtx, 0);
+
+    USB_BUS_LOCK(&sc->sc_bus);
+
+    /* turn on clocks */
+    dwc_otg_clocks_on(sc);
+
+    temp = DWC_OTG_READ_4(sc, DOTG_GSNPSID);
+    DPRINTF("Version = 0x%08x\n", temp);
+
+    /* disconnect */
+    DWC_OTG_WRITE_4(sc, DOTG_DCTL,
+        DCTL_SFTDISCON);
+
+    /* wait for host to detect disconnect */
+    usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 32);
+
+    DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+        GRSTCTL_CSFTRST);
+
+    /* wait a little bit for block to reset */
+    usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 128);
+
+    switch (sc->sc_mode) {
+    case DWC_MODE_DEVICE:
+        temp = GUSBCFG_FORCEDEVMODE;
+        break;
+    case DWC_MODE_HOST:
+        temp = GUSBCFG_FORCEHOSTMODE;
+        break;
+    default:
+        temp = 0;
+        break;
+    }
+
+    /* select HSIC, ULPI or internal PHY mode */
+    switch (dwc_otg_phy_type) {
+    case DWC_OTG_PHY_HSIC:
+        DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG,
+            GUSBCFG_PHYIF |
+            GUSBCFG_TRD_TIM_SET(5) | temp);
+        DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL,
+            0x000000EC);
+
+        temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG);
+        DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG,
+            temp & ~GLPMCFG_HSIC_CONN);
+        DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG,
+            temp | GLPMCFG_HSIC_CONN);
+        break;
+    case DWC_OTG_PHY_ULPI:
+        DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG,
+            GUSBCFG_ULPI_UTMI_SEL |
+            GUSBCFG_TRD_TIM_SET(5) | temp);
+        DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0);
+
+        temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG);
+        DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG,
+            temp & ~GLPMCFG_HSIC_CONN);
+        break;
+    case DWC_OTG_PHY_INTERNAL:
+        DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG,
+            GUSBCFG_PHYSEL |
+            GUSBCFG_TRD_TIM_SET(5) | temp);
+        DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0);
+
+        temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG);
+        DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG,
+            temp & ~GLPMCFG_HSIC_CONN);
+
+        temp = DWC_OTG_READ_4(sc, DOTG_GGPIO);
+        temp &= ~(DOTG_GGPIO_NOVBUSSENS | DOTG_GGPIO_I2CPADEN);
+        temp |= (DOTG_GGPIO_VBUSASEN | DOTG_GGPIO_VBUSBSEN |
+            DOTG_GGPIO_PWRDWN);
+        DWC_OTG_WRITE_4(sc, DOTG_GGPIO, temp);
+        break;
+    default:
+        break;
+    }
+
+    /* clear global nak */
+    DWC_OTG_WRITE_4(sc, DOTG_DCTL,
+        DCTL_CGOUTNAK |
+        DCTL_CGNPINNAK);
+
+    /* disable USB port */
+    DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0xFFFFFFFF);
+
+    /* wait 10ms */
+    usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+    /* enable USB port */
+    DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0);
+
+    /* wait 10ms */
+    usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+    temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG3);
+
+    sc->sc_fifo_size = 4 * GHWCFG3_DFIFODEPTH_GET(temp);
+
+    temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2);
+
+    sc->sc_dev_ep_max = GHWCFG2_NUMDEVEPS_GET(temp);
+
+    if (sc->sc_dev_ep_max > DWC_OTG_MAX_ENDPOINTS)
+        sc->sc_dev_ep_max = DWC_OTG_MAX_ENDPOINTS;
+
+    sc->sc_host_ch_max = GHWCFG2_NUMHSTCHNL_GET(temp);
+
+    if (sc->sc_host_ch_max > DWC_OTG_MAX_CHANNELS)
+        sc->sc_host_ch_max = DWC_OTG_MAX_CHANNELS;
+
+    temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG4);
+
+    sc->sc_dev_in_ep_max = GHWCFG4_NUM_IN_EP_GET(temp);
+
+    DPRINTF("Total FIFO size = %d bytes, Device EPs = %d/%d Host CHs =
%d\n",
+        sc->sc_fifo_size, sc->sc_dev_ep_max, sc->sc_dev_in_ep_max,
+        sc->sc_host_ch_max);
+
+    /* setup FIFO */
+    if (dwc_otg_init_fifo(sc, sc->sc_mode)) {
+        USB_BUS_UNLOCK(&sc->sc_bus);
+        return (EINVAL);
+    }
+
+    /* enable interrupts */
+    sc->sc_irq_mask = DWC_OTG_MSK_GINT_ENABLED;
+    DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+
+    if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_DEVICE) {
+
+        /* enable all endpoint interrupts */
+        temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2);
+        if (temp & GHWCFG2_MPI) {
+            uint8_t x;
+
+            DPRINTF("Disable Multi Process Interrupts\n");
+
+            for (x = 0; x != sc->sc_dev_in_ep_max; x++) {
+                DWC_OTG_WRITE_4(sc, DOTG_DIEPEACHINTMSK(x), 0);
+                DWC_OTG_WRITE_4(sc, DOTG_DOEPEACHINTMSK(x), 0);
+            }
+            DWC_OTG_WRITE_4(sc, DOTG_DEACHINTMSK, 0);
+        }
+        DWC_OTG_WRITE_4(sc, DOTG_DIEPMSK,
+            DIEPMSK_XFERCOMPLMSK);
+        DWC_OTG_WRITE_4(sc, DOTG_DOEPMSK, 0);
+        DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF);
+    }
+
+    if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_HOST) {
+        /* setup clocks */
+        temp = DWC_OTG_READ_4(sc, DOTG_HCFG);
+        temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK);
+        temp |= (1 << HCFG_FSLSPCLKSEL_SHIFT);
+        DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp);
+    }
+
+    /* only enable global IRQ */
+    DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG,
+        GAHBCFG_GLBLINTRMSK);
+
+    /* turn off clocks */
+    dwc_otg_clocks_off(sc);
+
+    /* read initial VBUS state */
+
+    temp = DWC_OTG_READ_4(sc, DOTG_GOTGCTL);
+
+    DPRINTFN(5, "GOTCTL=0x%08x\n", temp);
+
+    dwc_otg_vbus_interrupt(sc,
+        (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0);
+
+    USB_BUS_UNLOCK(&sc->sc_bus);
+
+    /* catch any lost interrupts */
+
+    dwc_otg_do_poll(&sc->sc_bus);
+
+    return (0);            /* success */
+}
+
+void
+dwc_otg_uninit(struct dwc_otg_softc *sc)
+{
+    USB_BUS_LOCK(&sc->sc_bus);
+
+    /* stop host timer */
+    dwc_otg_timer_stop(sc);
+
+    /* set disconnect */
+    DWC_OTG_WRITE_4(sc, DOTG_DCTL,
+        DCTL_SFTDISCON);
+
+    /* turn off global IRQ */
+    DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, 0);
+
+    sc->sc_flags.port_enabled = 0;
+    sc->sc_flags.port_powered = 0;
+    sc->sc_flags.status_vbus = 0;
+    sc->sc_flags.status_bus_reset = 0;
+    sc->sc_flags.status_suspend = 0;
+    sc->sc_flags.change_suspend = 0;
+    sc->sc_flags.change_connect = 1;
+
+    dwc_otg_pull_down(sc);
+    dwc_otg_clocks_off(sc);
+
+    USB_BUS_UNLOCK(&sc->sc_bus);
+
+    usb_callout_drain(&sc->sc_timer);
+}
+
+static void
+dwc_otg_suspend(struct dwc_otg_softc *sc)
+{
+    return;
+}
+
+static void
+dwc_otg_resume(struct dwc_otg_softc *sc)
+{
+    return;
+}
+
+static void
+dwc_otg_do_poll(struct usb_bus *bus)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus);
+
+    USB_BUS_LOCK(&sc->sc_bus);
+    USB_BUS_SPIN_LOCK(&sc->sc_bus);
+    dwc_otg_interrupt_poll_locked(sc);
+    dwc_otg_interrupt_complete_locked(sc);
+    if (sc->sc_flags.status_device_mode == 0) {
+        /* update host transfer schedule, so that new transfers can be
issued */
+        if (dwc_otg_update_host_transfer_schedule_locked(sc))
+            dwc_otg_interrupt_poll_locked(sc);
+    }
+    USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+    USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * DWC OTG bulk support
+ * DWC OTG control support
+ * DWC OTG interrupt support
+
*------------------------------------------------------------------------*/
+static void
+dwc_otg_device_non_isoc_open(struct usb_xfer *xfer)
+{
+}
+
+static void
+dwc_otg_device_non_isoc_close(struct usb_xfer *xfer)
+{
+    dwc_otg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+dwc_otg_device_non_isoc_enter(struct usb_xfer *xfer)
+{
+}
+
+static void
+dwc_otg_device_non_isoc_start(struct usb_xfer *xfer)
+{
+    /* setup TDs */
+    dwc_otg_setup_standard_chain(xfer);
+    dwc_otg_start_standard_chain(xfer);
+}
+
+struct usb_pipe_methods dwc_otg_device_non_isoc_methods =
+{
+    .open = dwc_otg_device_non_isoc_open,
+    .close = dwc_otg_device_non_isoc_close,
+    .enter = dwc_otg_device_non_isoc_enter,
+    .start = dwc_otg_device_non_isoc_start,
+};
+
+/*------------------------------------------------------------------------*
+ * DWC OTG full speed isochronous support
+
*------------------------------------------------------------------------*/
+static void
+dwc_otg_device_isoc_open(struct usb_xfer *xfer)
+{
+}
+
+static void
+dwc_otg_device_isoc_close(struct usb_xfer *xfer)
+{
+    dwc_otg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+dwc_otg_device_isoc_enter(struct usb_xfer *xfer)
+{
+}
+
+static void
+dwc_otg_device_isoc_start(struct usb_xfer *xfer)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
+    uint32_t temp;
+    uint32_t msframes;
+    uint32_t framenum;
+    uint8_t shift = usbd_xfer_get_fps_shift(xfer);
+
+    DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+        xfer, xfer->endpoint->isoc_next, xfer->nframes);
+
+    if (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST) {
+        temp = DWC_OTG_READ_4(sc, DOTG_HFNUM);
+
+        /* get the current frame index */
+        framenum = (temp & HFNUM_FRNUM_MASK);
+    } else {
+        temp = DWC_OTG_READ_4(sc, DOTG_DSTS);
+
+        /* get the current frame index */
+        framenum = DSTS_SOFFN_GET(temp);
+    }
+
+    if (xfer->xroot->udev->parent_hs_hub != NULL)
+        framenum /= 8;
+
+    framenum &= DWC_OTG_FRAME_MASK;
+
+    /*
+     * Compute number of milliseconds worth of data traffic for
+     * this USB transfer:
+     */
+    if (xfer->xroot->udev->speed == USB_SPEED_HIGH)
+        msframes = ((xfer->nframes << shift) + 7) / 8;
+    else
+        msframes = xfer->nframes;
+
+    /*
+     * check if the frame index is within the window where the frames
+     * will be inserted
+     */
+    temp = (framenum - xfer->endpoint->isoc_next) & DWC_OTG_FRAME_MASK;
+
+    if ((xfer->endpoint->is_synced == 0) || (temp < msframes)) {
+        /*
+         * If there is data underflow or the pipe queue is
+         * empty we schedule the transfer a few frames ahead
+         * of the current frame position. Else two isochronous
+         * transfers might overlap.
+         */
+        xfer->endpoint->isoc_next = (framenum + 3) & DWC_OTG_FRAME_MASK;
+        xfer->endpoint->is_synced = 1;
+        DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
+    }
+    /*
+     * compute how many milliseconds the insertion is ahead of the
+     * current frame position:
+     */
+    temp = (xfer->endpoint->isoc_next - framenum) & DWC_OTG_FRAME_MASK;
+
+    /*
+     * pre-compute when the isochronous transfer will be finished:
+     */
+    xfer->isoc_time_complete =
+        usb_isoc_time_expand(&sc->sc_bus, framenum) + temp + msframes;
+
+    /* setup TDs */
+    dwc_otg_setup_standard_chain(xfer);
+
+    /* compute frame number for next insertion */
+    xfer->endpoint->isoc_next += msframes;
+
+    /* start TD chain */
+    dwc_otg_start_standard_chain(xfer);
+}
+
+struct usb_pipe_methods dwc_otg_device_isoc_methods =
+{
+    .open = dwc_otg_device_isoc_open,
+    .close = dwc_otg_device_isoc_close,
+    .enter = dwc_otg_device_isoc_enter,
+    .start = dwc_otg_device_isoc_start,
+};
+
+/*------------------------------------------------------------------------*
+ * DWC OTG root control support
+ *------------------------------------------------------------------------*
+ * Simulate a hardware HUB by handling all the necessary requests.
+
*------------------------------------------------------------------------*/
+
+struct usb_device_descriptor dwc_otg_devd = {
+    .bLength = sizeof(struct usb_device_descriptor),
+    .bDescriptorType = UDESC_DEVICE,
+    .bcdUSB = {0x00, 0x02},
+    .bDeviceClass = UDCLASS_HUB,
+    .bDeviceSubClass = UDSUBCLASS_HUB,
+    .bDeviceProtocol = UDPROTO_HSHUBSTT,
+    .bMaxPacketSize = 64,
+    .bcdDevice = {0x00, 0x01},
+    .iManufacturer = 1,
+    .iProduct = 2,
+    .bNumConfigurations = 1,
+};
+
+struct dwc_otg_config_desc dwc_otg_confd = {
+    .confd = {
+        .bLength = sizeof(struct usb_config_descriptor),
+        .bDescriptorType = UDESC_CONFIG,
+        .wTotalLength[0] = sizeof(dwc_otg_confd),
+        .bNumInterface = 1,
+        .bConfigurationValue = 1,
+        .iConfiguration = 0,
+        .bmAttributes = UC_SELF_POWERED,
+        .bMaxPower = 0,
+    },
+    .ifcd = {
+        .bLength = sizeof(struct usb_interface_descriptor),
+        .bDescriptorType = UDESC_INTERFACE,
+        .bNumEndpoints = 1,
+        .bInterfaceClass = UICLASS_HUB,
+        .bInterfaceSubClass = UISUBCLASS_HUB,
+        .bInterfaceProtocol = 0,
+    },
+    .endpd = {
+        .bLength = sizeof(struct usb_endpoint_descriptor),
+        .bDescriptorType = UDESC_ENDPOINT,
+        .bEndpointAddress = (UE_DIR_IN | DWC_OTG_INTR_ENDPT),
+        .bmAttributes = UE_INTERRUPT,
+        .wMaxPacketSize[0] = 8,
+        .bInterval = 255,
+    },
+};
+
+#define    HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
+
+struct usb_hub_descriptor_min dwc_otg_hubd = {
+    .bDescLength = sizeof(dwc_otg_hubd),
+    .bDescriptorType = UDESC_HUB,
+    .bNbrPorts = 1,
+    HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)),
+    .bPwrOn2PwrGood = 50,
+    .bHubContrCurrent = 0,
+    .DeviceRemovable = {0},        /* port is removable */
+};
+
+#define    STRING_VENDOR \
+  "D\0W\0C\0O\0T\0G"
+
+#define    STRING_PRODUCT \
+  "O\0T\0G\0 \0R\0o\0o\0t\0 \0H\0U\0B"
+
+USB_MAKE_STRING_DESC(STRING_VENDOR, dwc_otg_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, dwc_otg_product);
+
+static usb_error_t
+dwc_otg_roothub_exec(struct usb_device *udev,
+    struct usb_device_request *req, const void **pptr, uint16_t *plength)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus);
+    const void *ptr;
+    uint16_t len;
+    uint16_t value;
+    uint16_t index;
+    usb_error_t err;
+
+    USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+    /* buffer reset */
+    ptr = (const void *)&sc->sc_hub_temp;
+    len = 0;
+    err = 0;
+
+    value = UGETW(req->wValue);
+    index = UGETW(req->wIndex);
+
+    /* demultiplex the control request */
+
+    switch (req->bmRequestType) {
+    case UT_READ_DEVICE:
+        switch (req->bRequest) {
+        case UR_GET_DESCRIPTOR:
+            goto tr_handle_get_descriptor;
+        case UR_GET_CONFIG:
+            goto tr_handle_get_config;
+        case UR_GET_STATUS:
+            goto tr_handle_get_status;
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_WRITE_DEVICE:
+        switch (req->bRequest) {
+        case UR_SET_ADDRESS:
+            goto tr_handle_set_address;
+        case UR_SET_CONFIG:
+            goto tr_handle_set_config;
+        case UR_CLEAR_FEATURE:
+            goto tr_valid;    /* nop */
+        case UR_SET_DESCRIPTOR:
+            goto tr_valid;    /* nop */
+        case UR_SET_FEATURE:
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_WRITE_ENDPOINT:
+        switch (req->bRequest) {
+        case UR_CLEAR_FEATURE:
+            switch (UGETW(req->wValue)) {
+            case UF_ENDPOINT_HALT:
+                goto tr_handle_clear_halt;
+            case UF_DEVICE_REMOTE_WAKEUP:
+                goto tr_handle_clear_wakeup;
+            default:
+                goto tr_stalled;
+            }
+            break;
+        case UR_SET_FEATURE:
+            switch (UGETW(req->wValue)) {
+            case UF_ENDPOINT_HALT:
+                goto tr_handle_set_halt;
+            case UF_DEVICE_REMOTE_WAKEUP:
+                goto tr_handle_set_wakeup;
+            default:
+                goto tr_stalled;
+            }
+            break;
+        case UR_SYNCH_FRAME:
+            goto tr_valid;    /* nop */
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_READ_ENDPOINT:
+        switch (req->bRequest) {
+        case UR_GET_STATUS:
+            goto tr_handle_get_ep_status;
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_WRITE_INTERFACE:
+        switch (req->bRequest) {
+        case UR_SET_INTERFACE:
+            goto tr_handle_set_interface;
+        case UR_CLEAR_FEATURE:
+            goto tr_valid;    /* nop */
+        case UR_SET_FEATURE:
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_READ_INTERFACE:
+        switch (req->bRequest) {
+        case UR_GET_INTERFACE:
+            goto tr_handle_get_interface;
+        case UR_GET_STATUS:
+            goto tr_handle_get_iface_status;
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_WRITE_CLASS_INTERFACE:
+    case UT_WRITE_VENDOR_INTERFACE:
+        /* XXX forward */
+        break;
+
+    case UT_READ_CLASS_INTERFACE:
+    case UT_READ_VENDOR_INTERFACE:
+        /* XXX forward */
+        break;
+
+    case UT_WRITE_CLASS_DEVICE:
+        switch (req->bRequest) {
+        case UR_CLEAR_FEATURE:
+            goto tr_valid;
+        case UR_SET_DESCRIPTOR:
+        case UR_SET_FEATURE:
+            break;
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_WRITE_CLASS_OTHER:
+        switch (req->bRequest) {
+        case UR_CLEAR_FEATURE:
+            goto tr_handle_clear_port_feature;
+        case UR_SET_FEATURE:
+            goto tr_handle_set_port_feature;
+        case UR_CLEAR_TT_BUFFER:
+        case UR_RESET_TT:
+        case UR_STOP_TT:
+            goto tr_valid;
+
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_READ_CLASS_OTHER:
+        switch (req->bRequest) {
+        case UR_GET_TT_STATE:
+            goto tr_handle_get_tt_state;
+        case UR_GET_STATUS:
+            goto tr_handle_get_port_status;
+        default:
+            goto tr_stalled;
+        }
+        break;
+
+    case UT_READ_CLASS_DEVICE:
+        switch (req->bRequest) {
+        case UR_GET_DESCRIPTOR:
+            goto tr_handle_get_class_descriptor;
+        case UR_GET_STATUS:
+            goto tr_handle_get_class_status;
+
+        default:
+            goto tr_stalled;
+        }
+        break;
+    default:
+        goto tr_stalled;
+    }
+    goto tr_valid;
+
+tr_handle_get_descriptor:
+    switch (value >> 8) {
+    case UDESC_DEVICE:
+        if (value & 0xff) {
+            goto tr_stalled;
+        }
+        len = sizeof(dwc_otg_devd);
+        ptr = (const void *)&dwc_otg_devd;
+        goto tr_valid;
+    case UDESC_CONFIG:
+        if (value & 0xff) {
+            goto tr_stalled;
+        }
+        len = sizeof(dwc_otg_confd);
+        ptr = (const void *)&dwc_otg_confd;
+        goto tr_valid;
+    case UDESC_STRING:
+        switch (value & 0xff) {
+        case 0:        /* Language table */
+            len = sizeof(usb_string_lang_en);
+            ptr = (const void *)&usb_string_lang_en;
+            goto tr_valid;
+
+        case 1:        /* Vendor */
+            len = sizeof(dwc_otg_vendor);
+            ptr = (const void *)&dwc_otg_vendor;
+            goto tr_valid;
+
+        case 2:        /* Product */
+            len = sizeof(dwc_otg_product);
+            ptr = (const void *)&dwc_otg_product;
+            goto tr_valid;
+        default:
+            break;
+        }
+        break;
+    default:
+        goto tr_stalled;
+    }
+    goto tr_stalled;
+
+tr_handle_get_config:
+    len = 1;
+    sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+    goto tr_valid;
+
+tr_handle_get_status:
+    len = 2;
+    USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+    goto tr_valid;
+
+tr_handle_set_address:
+    if (value & 0xFF00) {
+        goto tr_stalled;
+    }
+    sc->sc_rt_addr = value;
+    goto tr_valid;
+
+tr_handle_set_config:
+    if (value >= 2) {
+        goto tr_stalled;
+    }
+    sc->sc_conf = value;
+    goto tr_valid;
+
+tr_handle_get_interface:
+    len = 1;
+    sc->sc_hub_temp.wValue[0] = 0;
+    goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+    len = 2;
+    USETW(sc->sc_hub_temp.wValue, 0);
+    goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+    goto tr_valid;
+
+tr_handle_clear_port_feature:
+    if (index != 1)
+        goto tr_stalled;
+
+    DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
+
+    switch (value) {
+    case UHF_PORT_SUSPEND:
+        dwc_otg_wakeup_peer(sc);
+        break;
+
+    case UHF_PORT_ENABLE:
+        if (sc->sc_flags.status_device_mode == 0) {
+            DWC_OTG_WRITE_4(sc, DOTG_HPRT,
+                sc->sc_hprt_val | HPRT_PRTENA);
+        }
+        sc->sc_flags.port_enabled = 0;
+        break;
+
+    case UHF_C_PORT_RESET:
+        sc->sc_flags.change_reset = 0;
+        break;
+
+    case UHF_C_PORT_ENABLE:
+        sc->sc_flags.change_enabled = 0;
+        break;
+
+    case UHF_C_PORT_OVER_CURRENT:
+        sc->sc_flags.change_over_current = 0;
+        break;
+
+    case UHF_PORT_TEST:
+    case UHF_PORT_INDICATOR:
+        /* nops */
+        break;
+
+    case UHF_PORT_POWER:
+        sc->sc_flags.port_powered = 0;
+        if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) {
+            sc->sc_hprt_val = 0;
+            DWC_OTG_WRITE_4(sc, DOTG_HPRT, HPRT_PRTENA);
+        }
+        dwc_otg_pull_down(sc);
+        dwc_otg_clocks_off(sc);
+        break;
+
+    case UHF_C_PORT_CONNECTION:
+        /* clear connect change flag */
+        sc->sc_flags.change_connect = 0;
+        break;
+
+    case UHF_C_PORT_SUSPEND:
+        sc->sc_flags.change_suspend = 0;
+        break;
+
+    default:
+        err = USB_ERR_IOERROR;
+        goto done;
+    }
+    goto tr_valid;
+
+tr_handle_set_port_feature:
+    if (index != 1) {
+        goto tr_stalled;
+    }
+    DPRINTFN(9, "UR_SET_PORT_FEATURE\n");
+
+    switch (value) {
+    case UHF_PORT_ENABLE:
+        break;
+
+    case UHF_PORT_SUSPEND:
+        if (sc->sc_flags.status_device_mode == 0) {
+            /* set suspend BIT */
+            sc->sc_hprt_val |= HPRT_PRTSUSP;
+            DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+            /* generate HUB suspend event */
+            dwc_otg_suspend_irq(sc);
+        }
+        break;
+
+    case UHF_PORT_RESET:
+        if (sc->sc_flags.status_device_mode == 0) {
+
+            DPRINTF("PORT RESET\n");
+
+            /* enable PORT reset */
+            DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val | HPRT_PRTRST);
+
+            /* Wait 62.5ms for reset to complete */
+            usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16);
+
+            DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+            /* Wait 62.5ms for reset to complete */
+            usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16);
+
+            /* reset FIFOs */
+            (void) dwc_otg_init_fifo(sc, DWC_MODE_HOST);
+
+            sc->sc_flags.change_reset = 1;
+        } else {
+            err = USB_ERR_IOERROR;
+        }
+        break;
+
+    case UHF_PORT_TEST:
+    case UHF_PORT_INDICATOR:
+        /* nops */
+        break;
+    case UHF_PORT_POWER:
+        sc->sc_flags.port_powered = 1;
+        if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) {
+            sc->sc_hprt_val |= HPRT_PRTPWR;
+            DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+        }
+        if (sc->sc_mode == DWC_MODE_DEVICE || sc->sc_mode == DWC_MODE_OTG)
{
+            /* pull up D+, if any */
+            dwc_otg_pull_up(sc);
+        }
+        break;
+    default:
+        err = USB_ERR_IOERROR;
+        goto done;
+    }
+    goto tr_valid;
+
+tr_handle_get_port_status:
+
+    DPRINTFN(9, "UR_GET_PORT_STATUS\n");
+
+    if (index != 1)
+        goto tr_stalled;
+
+    if (sc->sc_flags.status_vbus)
+        dwc_otg_clocks_on(sc);
+    else
+        dwc_otg_clocks_off(sc);
+
+    /* Select Device Side Mode */
+
+    if (sc->sc_flags.status_device_mode) {
+        value = UPS_PORT_MODE_DEVICE;
+        dwc_otg_timer_stop(sc);
+    } else {
+        value = 0;
+        dwc_otg_timer_start(sc);
+    }
+
+    if (sc->sc_flags.status_high_speed)
+        value |= UPS_HIGH_SPEED;
+    else if (sc->sc_flags.status_low_speed)
+        value |= UPS_LOW_SPEED;
+
+    if (sc->sc_flags.port_powered)
+        value |= UPS_PORT_POWER;
+
+    if (sc->sc_flags.port_enabled)
+        value |= UPS_PORT_ENABLED;
+
+    if (sc->sc_flags.port_over_current)
+        value |= UPS_OVERCURRENT_INDICATOR;
+
+    if (sc->sc_flags.status_vbus &&
+        sc->sc_flags.status_bus_reset)
+        value |= UPS_CURRENT_CONNECT_STATUS;
+
+    if (sc->sc_flags.status_suspend)
+        value |= UPS_SUSPEND;
+
+    USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+    value = 0;
+
+    if (sc->sc_flags.change_connect)
+        value |= UPS_C_CONNECT_STATUS;
+    if (sc->sc_flags.change_suspend)
+        value |= UPS_C_SUSPEND;
+    if (sc->sc_flags.change_reset)
+        value |= UPS_C_PORT_RESET;
+    if (sc->sc_flags.change_over_current)
+        value |= UPS_C_OVERCURRENT_INDICATOR;
+
+    USETW(sc->sc_hub_temp.ps.wPortChange, value);
+    len = sizeof(sc->sc_hub_temp.ps);
+    goto tr_valid;
+
+tr_handle_get_class_descriptor:
+    if (value & 0xFF) {
+        goto tr_stalled;
+    }
+    ptr = (const void *)&dwc_otg_hubd;
+    len = sizeof(dwc_otg_hubd);
+    goto tr_valid;
+
+tr_stalled:
+    err = USB_ERR_STALLED;
+tr_valid:
+done:
+    *plength = len;
+    *pptr = ptr;
+    return (err);
+}
+
+static void
+dwc_otg_xfer_setup(struct usb_setup_params *parm)
+{
+    struct usb_xfer *xfer;
+    void *last_obj;
+    uint32_t ntd;
+    uint32_t n;
+    uint8_t ep_no;
+    uint8_t ep_type;
+
+    xfer = parm->curr_xfer;
+
+    /*
+     * NOTE: This driver does not use any of the parameters that
+     * are computed from the following values. Just set some
+     * reasonable dummies:
+     */
+    parm->hc_max_packet_size = 0x500;
+    parm->hc_max_packet_count = 3;
+    parm->hc_max_frame_size = 3 * 0x500;
+
+    usbd_transfer_setup_sub(parm);
+
+    /*
+     * compute maximum number of TDs
+     */
+    ep_type = (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE);
+
+    if (ep_type == UE_CONTROL) {
+
+        ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */
+            + 1 /* SYNC 2 */ + 1 /* SYNC 3 */;
+    } else {
+
+        ntd = xfer->nframes + 1 /* SYNC */ ;
+    }
+
+    /*
+     * check if "usbd_transfer_setup_sub" set an error
+     */
+    if (parm->err)
+        return;
+
+    /*
+     * allocate transfer descriptors
+     */
+    last_obj = NULL;
+
+    ep_no = xfer->endpointno & UE_ADDR;
+
+    /*
+     * Check for a valid endpoint profile in USB device mode:
+     */
+    if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
+        const struct usb_hw_ep_profile *pf;
+
+        dwc_otg_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+        if (pf == NULL) {
+            /* should not happen */
+            parm->err = USB_ERR_INVAL;
+            return;
+        }
+    }
+
+    /* align data */
+    parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+    for (n = 0; n != ntd; n++) {
+
+        struct dwc_otg_td *td;
+
+        if (parm->buf) {
+
+            td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+            /* compute shared bandwidth resource index for TT */
+            if (parm->udev->parent_hs_hub != NULL && parm->udev->speed !=
USB_SPEED_HIGH) {
+                if (parm->udev->parent_hs_hub->ddesc.bDeviceProtocol ==
UDPROTO_HSHUBMTT)
+                    td->tt_index = parm->udev->device_index;
+                else
+                    td->tt_index = parm->udev->parent_hs_hub->device_index;
+            } else {
+                td->tt_index = parm->udev->device_index;
+            }
+
+            /* init TD */
+            td->max_packet_size = xfer->max_packet_size;
+            td->max_packet_count = xfer->max_packet_count;
+            td->ep_no = ep_no;
+            td->ep_type = ep_type;
+            td->obj_next = last_obj;
+
+            last_obj = td;
+        }
+        parm->size[0] += sizeof(*td);
+    }
+
+    xfer->td_start[0] = last_obj;
+}
+
+static void
+dwc_otg_xfer_unsetup(struct usb_xfer *xfer)
+{
+    return;
+}
+
+static void
+dwc_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor
*edesc,
+    struct usb_endpoint *ep)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus);
+
+    DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d,%d)\n",
+        ep, udev->address,
+        edesc->bEndpointAddress, udev->flags.usb_mode,
+        sc->sc_rt_addr, udev->device_index);
+
+    if (udev->device_index != sc->sc_rt_addr) {
+
+        if (udev->flags.usb_mode == USB_MODE_DEVICE) {
+            if (udev->speed != USB_SPEED_FULL &&
+                udev->speed != USB_SPEED_HIGH) {
+                /* not supported */
+                return;
+            }
+        } else {
+            if (udev->speed == USB_SPEED_HIGH) {
+                if ((UGETW(edesc->wMaxPacketSize) >> 11) & 3) {
+                    /* high bandwidth endpoint - not tested */
+                    DPRINTF("High Bandwidth Endpoint - not tested\n");
+                    return;
+                }
+            }
+        }
+        if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS)
+            ep->methods = &dwc_otg_device_isoc_methods;
+        else
+            ep->methods = &dwc_otg_device_non_isoc_methods;
+    }
+}
+
+static void
+dwc_otg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
+{
+    struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus);
+
+    switch (state) {
+    case USB_HW_POWER_SUSPEND:
+        dwc_otg_suspend(sc);
+        break;
+    case USB_HW_POWER_SHUTDOWN:
+        dwc_otg_uninit(sc);
+        break;
+    case USB_HW_POWER_RESUME:
+        dwc_otg_resume(sc);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+dwc_otg_get_dma_delay(struct usb_device *udev, uint32_t *pus)
+{
+    /* DMA delay - wait until any use of memory is finished */
+    *pus = (2125);            /* microseconds */
+}
+
+static void
+dwc_otg_device_resume(struct usb_device *udev)
+{
+    DPRINTF("\n");
+
+    /* poll all transfers again to restart resumed ones */
+    dwc_otg_do_poll(udev->bus);
+}
+
+static void
+dwc_otg_device_suspend(struct usb_device *udev)
+{
+    DPRINTF("\n");
+}
+
+struct usb_bus_methods dwc_otg_bus_methods =
+{
+    .endpoint_init = &dwc_otg_ep_init,
+    .xfer_setup = &dwc_otg_xfer_setup,
+    .xfer_unsetup = &dwc_otg_xfer_unsetup,
+    .get_hw_ep_profile = &dwc_otg_get_hw_ep_profile,
+    #ifndef __rtems__
+    .xfer_stall = &dwc_otg_xfer_stall,
+    #endif
+    .set_stall = &dwc_otg_set_stall,
+    .clear_stall = &dwc_otg_clear_stall,
+    .roothub_exec = &dwc_otg_roothub_exec,
+    .xfer_poll = &dwc_otg_do_poll,
+    .device_state_change = &dwc_otg_device_state_change,
+    .set_hw_power_sleep = &dwc_otg_set_hw_power_sleep,
+    .get_dma_delay = &dwc_otg_get_dma_delay,
+    .device_resume = &dwc_otg_device_resume,
+    .device_suspend = &dwc_otg_device_suspend,
+};
diff --git a/freebsd/sys/dev/usb/controller/dwc_otg.h
b/freebsd/sys/dev/usb/controller/dwc_otg.h
new file mode 100755
index 0000000..52b3b91
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otg.h
@@ -0,0 +1,223 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DWC_OTG_H_
+#define    _DWC_OTG_H_
+
+#define    DWC_OTG_MAX_DEVICES MIN(USB_MAX_DEVICES, 32)
+#define    DWC_OTG_FRAME_MASK 0x7FF
+#define    DWC_OTG_MAX_TXP 4
+#define    DWC_OTG_MAX_TXN (0x200 * DWC_OTG_MAX_TXP)
+#define    DWC_OTG_MAX_CHANNELS 16
+#define    DWC_OTG_MAX_ENDPOINTS 16
+#define    DWC_OTG_HOST_TIMER_RATE 10 /* ms */
+#define    DWC_OTG_TT_SLOT_MAX 8
+#define    DWC_OTG_SLOT_IDLE_MAX 3
+#define    DWC_OTG_SLOT_IDLE_MIN 2
+#define    DWC_OTG_NAK_MAX 8    /* 1 ms */
+#ifndef DWC_OTG_TX_MAX_FIFO_SIZE
+#define    DWC_OTG_TX_MAX_FIFO_SIZE DWC_OTG_MAX_TXN
+#endif
+
+#define    DWC_OTG_READ_4(sc, reg) \
+  bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
+
+#define    DWC_OTG_WRITE_4(sc, reg, data)    \
+  bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+struct dwc_otg_td;
+struct dwc_otg_softc;
+
+typedef uint8_t (dwc_otg_cmd_t)(struct dwc_otg_softc *sc, struct
dwc_otg_td *td);
+
+struct dwc_otg_td {
+    struct dwc_otg_td *obj_next;
+    dwc_otg_cmd_t *func;
+    struct usb_page_cache *pc;
+    uint32_t tx_bytes;
+    uint32_t offset;
+    uint32_t remainder;
+    uint32_t hcchar;        /* HOST CFG */
+    uint32_t hcsplt;        /* HOST CFG */
+    uint16_t max_packet_size;    /* packet_size */
+    uint16_t npkt;
+    uint8_t max_packet_count;    /* packet_count */
+    uint8_t errcnt;
+    uint8_t tmr_res;
+    uint8_t tmr_val;
+    uint8_t did_nak;        /* NAK counter */
+    uint8_t    ep_no;
+    uint8_t ep_type;
+    uint8_t channel;
+    uint8_t tt_index;        /* TT data */
+    uint8_t tt_start_slot;        /* TT data */
+    uint8_t tt_complete_slot;    /* TT data */
+    uint8_t tt_xactpos;        /* TT data */
+    uint8_t state;
+#define    DWC_CHAN_ST_START 0
+#define    DWC_CHAN_ST_WAIT_ANE 1
+#define    DWC_CHAN_ST_WAIT_S_ANE 2
+#define    DWC_CHAN_ST_WAIT_C_ANE 3
+#define    DWC_CHAN_ST_WAIT_C_PKT 4
+#define    DWC_CHAN_ST_TX_PKT_ISOC 5
+#define    DWC_CHAN_ST_TX_WAIT_ISOC 6
+    uint8_t    error_any:1;
+    uint8_t    error_stall:1;
+    uint8_t    alt_next:1;
+    uint8_t    short_pkt:1;
+    uint8_t    did_stall:1;
+    uint8_t toggle:1;
+    uint8_t set_toggle:1;
+    uint8_t got_short:1;
+    uint8_t tt_scheduled:1;
+};
+
+struct dwc_otg_tt_info {
+    uint8_t slot_index;
+};
+
+struct dwc_otg_std_temp {
+    dwc_otg_cmd_t *func;
+    struct usb_page_cache *pc;
+    struct dwc_otg_td *td;
+    struct dwc_otg_td *td_next;
+    uint32_t len;
+    uint32_t offset;
+    uint16_t max_frame_size;
+    uint8_t    short_pkt;
+
+    /*
+     * short_pkt = 0: transfer should be short terminated
+     * short_pkt = 1: transfer should not be short terminated
+     */
+    uint8_t    setup_alt_next;
+    uint8_t did_stall;
+    uint8_t bulk_or_control;
+};
+
+struct dwc_otg_config_desc {
+    struct usb_config_descriptor confd;
+    struct usb_interface_descriptor ifcd;
+    struct usb_endpoint_descriptor endpd;
+} __packed;
+
+union dwc_otg_hub_temp {
+    uWord    wValue;
+    struct usb_port_status ps;
+};
+
+struct dwc_otg_flags {
+    uint8_t    change_connect:1;
+    uint8_t    change_suspend:1;
+    uint8_t change_reset:1;
+    uint8_t change_enabled:1;
+    uint8_t change_over_current:1;
+    uint8_t    status_suspend:1;    /* set if suspended */
+    uint8_t    status_vbus:1;        /* set if present */
+    uint8_t    status_bus_reset:1;    /* set if reset complete */
+    uint8_t    status_high_speed:1;    /* set if High Speed is selected */
+    uint8_t    status_low_speed:1;    /* set if Low Speed is selected */
+    uint8_t status_device_mode:1;    /* set if device mode */
+    uint8_t    self_powered:1;
+    uint8_t    clocks_off:1;
+    uint8_t    port_powered:1;
+    uint8_t    port_enabled:1;
+    uint8_t port_over_current:1;
+    uint8_t    d_pulled_up:1;
+};
+
+struct dwc_otg_profile {
+    struct usb_hw_ep_profile usb;
+    uint16_t max_buffer;
+};
+
+struct dwc_otg_chan_state {
+    uint16_t allocated;
+    uint16_t wait_sof;
+    uint32_t hcint;
+    uint16_t tx_p_size;    /* periodic */
+    uint16_t tx_np_size;    /* non-periodic */
+};
+
+struct dwc_otg_softc {
+    struct usb_bus sc_bus;
+    union dwc_otg_hub_temp sc_hub_temp;
+    struct dwc_otg_profile sc_hw_ep_profile[DWC_OTG_MAX_ENDPOINTS];
+    struct dwc_otg_tt_info sc_tt_info[DWC_OTG_MAX_DEVICES];
+    struct usb_callout sc_timer;
+
+    struct usb_device *sc_devices[DWC_OTG_MAX_DEVICES];
+    struct resource *sc_io_res;
+    struct resource *sc_irq_res;
+    void   *sc_intr_hdl;
+    bus_size_t sc_io_size;
+    bus_space_tag_t sc_io_tag;
+    bus_space_handle_t sc_io_hdl;
+
+    uint32_t sc_rx_bounce_buffer[1024 / 4];
+    uint32_t sc_tx_bounce_buffer[MAX(512 * DWC_OTG_MAX_TXP, 1024) / 4];
+
+    uint32_t sc_fifo_size;
+    uint32_t sc_tx_max_size;
+    uint32_t sc_tx_cur_p_level;    /* periodic */
+    uint32_t sc_tx_cur_np_level;    /* non-periodic */
+    uint32_t sc_irq_mask;
+    uint32_t sc_last_rx_status;
+    uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS];
+    uint32_t sc_in_ctl[DWC_OTG_MAX_ENDPOINTS];
+    struct dwc_otg_chan_state sc_chan_state[DWC_OTG_MAX_CHANNELS];
+    uint32_t sc_tmr_val;
+    uint32_t sc_hprt_val;
+    uint32_t sc_xfer_complete;
+
+    uint16_t sc_active_rx_ep;
+    uint16_t sc_last_frame_num;
+
+    uint8_t sc_timer_active;
+    uint8_t    sc_dev_ep_max;
+    uint8_t sc_dev_in_ep_max;
+    uint8_t    sc_host_ch_max;
+    uint8_t sc_needsof;
+    uint8_t    sc_rt_addr;        /* root HUB address */
+    uint8_t    sc_conf;        /* root HUB config */
+    uint8_t sc_mode;        /* mode of operation */
+#define    DWC_MODE_OTG 0        /* both modes */
+#define    DWC_MODE_DEVICE 1    /* device only */
+#define    DWC_MODE_HOST  2    /* host only */
+
+    uint8_t    sc_hub_idata[1];
+
+    struct dwc_otg_flags sc_flags;
+};
+
+/* prototypes */
+
+driver_filter_t dwc_otg_filter_interrupt;
+driver_intr_t dwc_otg_interrupt;
+int dwc_otg_init(struct dwc_otg_softc *);
+void dwc_otg_uninit(struct dwc_otg_softc *);
+
+#endif        /* _DWC_OTG_H_ */
diff --git a/freebsd/sys/dev/usb/controller/dwc_otg_fdt.c
b/freebsd/sys/dev/usb/controller/dwc_otg_fdt.c
new file mode 100644
index 0000000..1ebb6f0
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otg_fdt.c
@@ -0,0 +1,235 @@
+#include <machine/rtems-bsd-kernel-space.h>
+/*-
+ * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#ifndef __rtems__
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+#include <dev/usb/controller/dwc_otg_fdt.h>
+
+static device_probe_t dwc_otg_probe;
+static device_detach_t dwc_otg_detach;
+
+static int
+dwc_otg_probe(device_t dev)
+{
+
+    #ifndef __rtems__
+    if (!ofw_bus_status_okay(dev))
+        return (ENXIO);
+
+    if (!ofw_bus_is_compatible(dev, "synopsys,designware-hs-otg2"))
+        return (ENXIO);
+    #endif
+
+    device_set_desc(dev, "DWC OTG 2.0 integrated USB controller");
+
+    return (BUS_PROBE_DEFAULT);
+}
+
+int
+dwc_otg_attach(device_t dev)
+{
+    struct dwc_otg_fdt_softc *sc = device_get_softc(dev);
+    char usb_mode[24];
+    int err;
+    int rid;
+
+    /* initialise some bus fields */
+    sc->sc_otg.sc_bus.parent = dev;
+    sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices;
+    sc->sc_otg.sc_bus.devices_max = DWC_OTG_MAX_DEVICES;
+    #ifndef __rtems__
+    sc->sc_otg.sc_bus.dma_bits = 32;
+    #endif
+
+    #ifndef __rtems__
+    /* get USB mode, if any */
+    if (OF_getprop(ofw_bus_get_node(dev), "dr_mode",
+        &usb_mode, sizeof(usb_mode)) > 0) {
+
+        /* ensure proper zero termination */
+        usb_mode[sizeof(usb_mode) - 1] = 0;
+
+        if (strcasecmp(usb_mode, "host") == 0)
+            sc->sc_otg.sc_mode = DWC_MODE_HOST;
+        else if (strcasecmp(usb_mode, "peripheral") == 0)
+            sc->sc_otg.sc_mode = DWC_MODE_DEVICE;
+        else if (strcasecmp(usb_mode, "otg") != 0) {
+            device_printf(dev, "Invalid FDT dr_mode: %s\n",
+                usb_mode);
+        }
+    }
+    #else
+    sc->sc_otg.sc_mode = DWC_MODE_HOST;
+    #endif
+
+    /* get all DMA memory */
+    if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus,
+        USB_GET_DMA_TAG(dev), NULL)) {
+        return (ENOMEM);
+    }
+    rid = 0;
+    sc->sc_otg.sc_io_res =
+        bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+
+    if (!(sc->sc_otg.sc_io_res)) {
+        err = ENOMEM;
+        goto error;
+    }
+    sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res);
+    sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res);
+    sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res);
+
+    rid = 0;
+    sc->sc_otg.sc_irq_res =
+        bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+    if (sc->sc_otg.sc_irq_res == NULL)
+        goto error;
+
+    sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+    if (sc->sc_otg.sc_bus.bdev == NULL)
+        goto error;
+
+    device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus);
+
+    err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_TTY |
INTR_MPSAFE,
+        &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc,
&sc->sc_otg.sc_intr_hdl);
+    if (err) {
+        sc->sc_otg.sc_intr_hdl = NULL;
+        goto error;
+    }
+    err = dwc_otg_init(&sc->sc_otg);
+    if (err == 0) {
+        err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev);
+    }
+    if (err)
+        goto error;
+
+
+    return (0);
+
+error:
+    dwc_otg_detach(dev);
+    return (ENXIO);
+}
+
+static int
+dwc_otg_detach(device_t dev)
+{
+    struct dwc_otg_fdt_softc *sc = device_get_softc(dev);
+    device_t bdev;
+    int err;
+
+    if (sc->sc_otg.sc_bus.bdev) {
+        bdev = sc->sc_otg.sc_bus.bdev;
+        device_detach(bdev);
+        device_delete_child(dev, bdev);
+    }
+    /* during module unload there are lots of children leftover */
+    device_delete_children(dev);
+
+    if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) {
+        /*
+         * only call dwc_otg_uninit() after dwc_otg_init()
+         */
+        dwc_otg_uninit(&sc->sc_otg);
+
+        err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res,
+            sc->sc_otg.sc_intr_hdl);
+        sc->sc_otg.sc_intr_hdl = NULL;
+    }
+    /* free IRQ channel, if any */
+    if (sc->sc_otg.sc_irq_res) {
+        bus_release_resource(dev, SYS_RES_IRQ, 0,
+            sc->sc_otg.sc_irq_res);
+        sc->sc_otg.sc_irq_res = NULL;
+    }
+    /* free memory resource, if any */
+    if (sc->sc_otg.sc_io_res) {
+        bus_release_resource(dev, SYS_RES_MEMORY, 0,
+            sc->sc_otg.sc_io_res);
+        sc->sc_otg.sc_io_res = NULL;
+    }
+    usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL);
+
+    return (0);
+}
+
+static device_method_t dwc_otg_methods[] = {
+    /* Device interface */
+    DEVMETHOD(device_probe, dwc_otg_probe),
+    DEVMETHOD(device_attach, dwc_otg_attach),
+    DEVMETHOD(device_detach, dwc_otg_detach),
+    DEVMETHOD(device_suspend, bus_generic_suspend),
+    DEVMETHOD(device_resume, bus_generic_resume),
+    DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+    DEVMETHOD_END
+};
+
+driver_t dwc_otg_driver = {
+    .name = "dwcotg",
+    .methods = dwc_otg_methods,
+    .size = sizeof(struct dwc_otg_fdt_softc),
+};
+
+static devclass_t dwc_otg_devclass;
+
+#ifndef __rtems__
+DRIVER_MODULE(dwcotg, simplebus, dwc_otg_driver, dwc_otg_devclass, 0, 0);
+#else
+DRIVER_MODULE(dwcotg, nexus, dwc_otg_driver, dwc_otg_devclass, 0, 0);
+#endif
+MODULE_DEPEND(dwcotg, usb, 1, 1, 1);
diff --git a/freebsd/sys/dev/usb/controller/dwc_otg_fdt.h
b/freebsd/sys/dev/usb/controller/dwc_otg_fdt.h
new file mode 100755
index 0000000..9e01118
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otg_fdt.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DWC_OTG_FDT_H_
+#define    _DWC_OTG_FDT_H_
+
+struct dwc_otg_fdt_softc {
+    struct dwc_otg_softc sc_otg;    /* must be first */
+};
+
+extern driver_t dwc_otg_driver;
+
+device_attach_t dwc_otg_attach;
+
+#endif
diff --git a/freebsd/sys/dev/usb/controller/dwc_otgreg.h
b/freebsd/sys/dev/usb/controller/dwc_otgreg.h
new file mode 100755
index 0000000..8ab3582
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otgreg.h
@@ -0,0 +1,800 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2010,2011 Aleksandr Rybalko. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DWC_OTGREG_H_
+#define    _DWC_OTGREG_H_
+
+#define    DOTG_GOTGCTL        0x0000
+#define    DOTG_GOTGINT        0x0004
+#define    DOTG_GAHBCFG        0x0008
+#define    DOTG_GUSBCFG        0x000C
+#define    DOTG_GRSTCTL        0x0010
+#define    DOTG_GINTSTS        0x0014
+#define    DOTG_GINTMSK        0x0018
+#define    DOTG_GRXSTSRD        0x001C
+#define    DOTG_GRXSTSRH        0x001C
+#define    DOTG_GRXSTSPD        0x0020
+#define    DOTG_GRXSTSPH        0x0020
+#define    DOTG_GRXFSIZ        0x0024
+#define    DOTG_GNPTXFSIZ        0x0028
+#define    DOTG_GNPTXSTS        0x002C
+#define    DOTG_GI2CCTL        0x0030
+#define    DOTG_GPVNDCTL        0x0034
+#define    DOTG_GGPIO        0x0038
+#define    DOTG_GUID        0x003C
+#define    DOTG_GSNPSID        0x0040
+#define    DOTG_GHWCFG1        0x0044
+#define    DOTG_GHWCFG2        0x0048
+#define    DOTG_GHWCFG3        0x004C
+#define    DOTG_GHWCFG4        0x0050
+#define    DOTG_GLPMCFG        0x0054
+#define    DOTG_GPWRDN        0x0058
+#define    DOTG_GDFIFOCFG        0x005C
+#define    DOTG_GADPCTL        0x0060
+
+#define    DOTG_HPTXFSIZ        0x0100
+/* start from 0x104, but fifo0 not exists */
+#define    DOTG_DPTXFSIZ(fifo)    (0x0100 + (4*(fifo)))
+#define    DOTG_DIEPTXF(fifo)    (0x0100 + (4*(fifo)))
+
+#define    DOTG_HCFG        0x0400
+#define    DOTG_HFIR        0x0404
+#define    DOTG_HFNUM        0x0408
+#define    DOTG_HPTXSTS        0x0410
+#define    DOTG_HAINT        0x0414
+#define    DOTG_HAINTMSK        0x0418
+#define    DOTG_HPRT        0x0440
+
+#define    DOTG_HCCHAR(ch)        (0x0500 + (32*(ch)))
+#define    DOTG_HCSPLT(ch)        (0x0504 + (32*(ch)))
+#define    DOTG_HCINT(ch)        (0x0508 + (32*(ch)))
+#define    DOTG_HCINTMSK(ch)    (0x050C + (32*(ch)))
+#define    DOTG_HCTSIZ(ch)        (0x0510 + (32*(ch)))
+#define    DOTG_HCDMA(ch)        (0x0514 + (32*(ch)))
+#define    DOTG_HCDMAI(ch)        (0x0514 + (32*(ch)))
+#define    DOTG_HCDMAO(ch)        (0x0514 + (32*(ch)))
+#define    DOTG_HCDMAB(ch)        (0x051C + (32*(ch)))
+
+/* Device Mode */
+#define    DOTG_DCFG        0x0800
+#define    DOTG_DCTL        0x0804
+#define    DOTG_DSTS        0x0808
+#define    DOTG_DIEPMSK        0x0810
+#define    DOTG_DOEPMSK        0x0814
+#define    DOTG_DAINT        0x0818
+#define    DOTG_DAINTMSK        0x081C
+#define    DOTG_DTKNQR1        0x0820
+#define    DOTG_DTKNQR2        0x0824
+#define    DOTG_DVBUSDIS        0x0828
+#define    DOTG_DVBUSPULSE        0x082C
+#define    DOTG_DTHRCTL        0x0830
+#define    DOTG_DTKNQR4        0x0834
+#define    DOTG_DIEPEMPMSK        0x0834
+#define    DOTG_DEACHINT        0x0838
+#define    DOTG_DEACHINTMSK    0x083C
+#define    DOTG_DIEPEACHINTMSK(ch)    (0x0840 + (4*(ch)))
+#define    DOTG_DOEPEACHINTMSK(ch)    (0x0880 + (4*(ch)))
+
+#define    DOTG_DIEPCTL(ep)    (0x0900 + (32*(ep)))
+#define    DOTG_DIEPINT(ep)    (0x0908 + (32*(ep)))
+#define    DOTG_DIEPTSIZ(ep)    (0x0910 + (32*(ep)))
+#define    DOTG_DIEPDMA(ep)    (0x0914 + (32*(ep)))
+#define    DOTG_DTXFSTS(ep)    (0x0918 + (32*(ep)))
+#define    DOTG_DIEPDMAB(ep)    (0x091c + (32*(ep)))
+
+#define    DOTG_DOEPCTL(ep)    (0x0B00 + (32*(ep)))
+#define    DOTG_DOEPFN(ep)        (0x0B04 + (32*(ep)))
+#define    DOTG_DOEPINT(ep)    (0x0B08 + (32*(ep)))
+#define    DOTG_DOEPTSIZ(ep)    (0x0B10 + (32*(ep)))
+#define    DOTG_DOEPDMA(ep)    (0x0B14 + (32*(ep)))
+#define    DOTG_DOEPDMAB(ep)    (0x0B1c + (32*(ep)))
+/* End Device Mode */
+
+/* Host Mode
+#define    DOTG_CTL_STATUS        0x0800
+#define    DOTG_DMA0_INB_CHN0    0x0818
+#define    DOTG_DMA0_INB_CHN1    0x0820
+#define    DOTG_DMA0_INB_CHN2    0x0828
+#define    DOTG_DVBUSDIS        0x0828
+#define    DOTG_DVBUSPULSE        0x082c
+#define    DOTG_DMA0_INB_CHN3    0x0830
+#define    DOTG_DMA0_INB_CHN4    0x0838
+#define    DOTG_DMA0_INB_CHN5    0x0840
+#define    DOTG_DMA0_INB_CHN6    0x0848
+#define    DOTG_DMA0_INB_CHN7    0x0850
+#define    DOTG_DMA0_OUTB_CHN0    0x0858
+#define    DOTG_DMA0_OUTB_CHN1    0x0860
+#define    DOTG_DMA0_OUTB_CHN2    0x0868
+#define    DOTG_DMA0_OUTB_CHN3    0x0870
+#define    DOTG_DMA0_OUTB_CHN4    0x0878
+#define    DOTG_DMA0_OUTB_CHN5    0x0880
+#define    DOTG_DMA0_OUTB_CHN6    0x0888
+#define    DOTG_DMA0_OUTB_CHN7    0x0890
+ End Host Mode */
+
+/* Power and clock gating CSR */
+
+#define    DOTG_PCGCCTL        0x0E00
+
+/* FIFO access registers (PIO-mode) */
+
+#define    DOTG_DFIFO(n)        (0x1000 + (0x1000 * (n)))
+
+#define    GOTGCTL_CHIRP_ON        (1<<27)
+#define    GOTGCTL_BSESVLD            (1<<19)
+#define    GOTGCTL_ASESVLD            (1<<18)
+#define    GOTGCTL_DBNCTIME        (1<<17)
+#define    GOTGCTL_CONIDSTS        (1<<16)
+#define    GOTGCTL_DEVHNPEN        (1<<11)
+#define    GOTGCTL_HSTSETHNPEN        (1<<10)
+#define    GOTGCTL_HNPREQ            (1<<9)
+#define    GOTGCTL_HSTNEGSCS        (1<<8)
+#define    GOTGCTL_SESREQ            (1<<1)
+#define    GOTGCTL_SESREQSCS        (1<<0)
+
+#define    GOTGCTL_DBNCEDONE        (1<<19)
+#define    GOTGCTL_ADEVTOUTCHG        (1<<18)
+#define    GOTGCTL_HSTNEGDET        (1<<17)
+#define    GOTGCTL_HSTNEGSUCSTSCHG        (1<<9)
+#define    GOTGCTL_SESREQSUCSTSCHG        (1<<8)
+#define    GOTGCTL_SESENDDET        (1<<2)
+
+#define    GAHBCFG_PTXFEMPLVL        (1<<8)
+#define    GAHBCFG_NPTXFEMPLVL        (1<<7)
+#define    GAHBCFG_DMAEN            (1<<5)
+#define    GAHBCFG_HBSTLEN_MASK        0x0000001e
+#define    GAHBCFG_HBSTLEN_SHIFT        1
+#define    GAHBCFG_GLBLINTRMSK        (1<<0)
+
+#define    GUSBCFG_CORRUPTTXPACKET        (1<<31)
+#define    GUSBCFG_FORCEDEVMODE        (1<<30)
+#define    GUSBCFG_FORCEHOSTMODE        (1<<29)
+#define    GUSBCFG_NO_PULLUP        (1<<27)
+#define    GUSBCFG_IC_USB_CAP        (1<<26)
+#define    GUSBCFG_TERMSELDLPULSE        (1<<22)
+#define    GUSBCFG_ULPIEXTVBUSINDICATOR    (1<<21)
+#define    GUSBCFG_ULPIEXTVBUSDRV        (1<<20)
+#define    GUSBCFG_ULPICLKSUSM        (1<<19)
+#define    GUSBCFG_ULPIAUTORES        (1<<18)
+#define    GUSBCFG_ULPIFSLS        (1<<17)
+#define    GUSBCFG_OTGI2CSEL        (1<<16)
+#define    GUSBCFG_PHYLPWRCLKSEL        (1<<15)
+#define    GUSBCFG_USBTRDTIM_MASK        0x00003c00
+#define    GUSBCFG_USBTRDTIM_SHIFT        10
+#define    GUSBCFG_TRD_TIM_SET(x)        (((x) & 15) << 10)
+#define    GUSBCFG_HNPCAP            (1<<9)
+#define    GUSBCFG_SRPCAP            (1<<8)
+#define    GUSBCFG_DDRSEL            (1<<7)
+#define    GUSBCFG_PHYSEL            (1<<6)
+#define    GUSBCFG_FSINTF            (1<<5)
+#define    GUSBCFG_ULPI_UTMI_SEL        (1<<4)
+#define    GUSBCFG_PHYIF            (1<<3)
+#define    GUSBCFG_TOUTCAL_MASK        0x00000007
+#define    GUSBCFG_TOUTCAL_SHIFT        0
+
+/* STM32F4 */
+#define    DOTG_GGPIO_NOVBUSSENS        (1 << 21)
+#define    DOTG_GGPIO_SOFOUTEN        (1 << 20)
+#define    DOTG_GGPIO_VBUSBSEN        (1 << 19)
+#define    DOTG_GGPIO_VBUSASEN        (1 << 18)
+#define    DOTG_GGPIO_I2CPADEN        (1 << 17)
+#define    DOTG_GGPIO_PWRDWN        (1 << 16)
+
+#define    GRSTCTL_AHBIDLE            (1<<31)
+#define    GRSTCTL_DMAREQ            (1<<30)
+#define    GRSTCTL_TXFNUM_MASK        0x000007c0
+#define    GRSTCTL_TXFNUM_SHIFT        6
+#define    GRSTCTL_TXFIFO(n)        (((n) & 31) << 6)
+#define    GRSTCTL_TXFFLSH            (1<<5)
+#define    GRSTCTL_RXFFLSH            (1<<4)
+#define    GRSTCTL_INTKNQFLSH        (1<<3)
+#define    GRSTCTL_FRMCNTRRST        (1<<2)
+#define    GRSTCTL_HSFTRST            (1<<1)
+#define    GRSTCTL_CSFTRST            (1<<0)
+
+#define    GINTSTS_WKUPINT            (1<<31)
+#define    GINTSTS_SESSREQINT        (1<<30)
+#define    GINTSTS_DISCONNINT        (1<<29)
+#define    GINTSTS_CONIDSTSCHNG        (1<<28)
+#define    GINTSTS_LPM            (1<<27)
+#define    GINTSTS_PTXFEMP            (1<<26)
+#define    GINTSTS_HCHINT            (1<<25)
+#define    GINTSTS_PRTINT            (1<<24)
+#define    GINTSTS_RESETDET        (1<<23)
+#define    GINTSTS_FETSUSP            (1<<22)
+#define    GINTSTS_INCOMPLP        (1<<21)
+#define    GINTSTS_INCOMPISOIN        (1<<20)
+#define    GINTSTS_OEPINT            (1<<19)
+#define    GINTSTS_IEPINT            (1<<18)
+#define    GINTSTS_EPMIS            (1<<17)
+#define    GINTSTS_RESTORE_DONE        (1<<16)
+#define    GINTSTS_EOPF            (1<<15)
+#define    GINTSTS_ISOOUTDROP        (1<<14)
+#define    GINTSTS_ENUMDONE        (1<<13)
+#define    GINTSTS_USBRST            (1<<12)
+#define    GINTSTS_USBSUSP            (1<<11)
+#define    GINTSTS_ERLYSUSP        (1<<10)
+#define    GINTSTS_I2CINT            (1<<9)
+#define    GINTSTS_ULPICKINT        (1<<8)
+#define    GINTSTS_GOUTNAKEFF        (1<<7)
+#define    GINTSTS_GINNAKEFF        (1<<6)
+#define    GINTSTS_NPTXFEMP        (1<<5)
+#define    GINTSTS_RXFLVL            (1<<4)
+#define    GINTSTS_SOF            (1<<3)
+#define    GINTSTS_OTGINT            (1<<2)
+#define    GINTSTS_MODEMIS            (1<<1)
+#define    GINTSTS_CURMOD            (1<<0)
+
+#define    GINTMSK_WKUPINTMSK        (1<<31)
+#define    GINTMSK_SESSREQINTMSK        (1<<30)
+#define    GINTMSK_DISCONNINTMSK        (1<<29)
+#define    GINTMSK_CONIDSTSCHNGMSK        (1<<28)
+#define    GINTMSK_PTXFEMPMSK        (1<<26)
+#define    GINTMSK_HCHINTMSK        (1<<25)
+#define    GINTMSK_PRTINTMSK        (1<<24)
+#define    GINTMSK_FETSUSPMSK        (1<<22)
+#define    GINTMSK_INCOMPLPMSK        (1<<21)
+#define    GINTMSK_INCOMPISOINMSK        (1<<20)
+#define    GINTMSK_OEPINTMSK        (1<<19)
+#define    GINTMSK_IEPINTMSK        (1<<18)
+#define    GINTMSK_EPMISMSK        (1<<17)
+#define    GINTMSK_EOPFMSK            (1<<15)
+#define    GINTMSK_ISOOUTDROPMSK        (1<<14)
+#define    GINTMSK_ENUMDONEMSK        (1<<13)
+#define    GINTMSK_USBRSTMSK        (1<<12)
+#define    GINTMSK_USBSUSPMSK        (1<<11)
+#define    GINTMSK_ERLYSUSPMSK        (1<<10)
+#define    GINTMSK_I2CINTMSK        (1<<9)
+#define    GINTMSK_ULPICKINTMSK        (1<<8)
+#define    GINTMSK_GOUTNAKEFFMSK        (1<<7)
+#define    GINTMSK_GINNAKEFFMSK        (1<<6)
+#define    GINTMSK_NPTXFEMPMSK        (1<<5)
+#define    GINTMSK_RXFLVLMSK        (1<<4)
+#define    GINTMSK_SOFMSK            (1<<3)
+#define    GINTMSK_OTGINTMSK        (1<<2)
+#define    GINTMSK_MODEMISMSK        (1<<1)
+#define    GINTMSK_CURMODMSK        (1<<0)
+
+#define    GRXSTSRH_PKTSTS_MASK        0x001e0000
+#define    GRXSTSRH_PKTSTS_SHIFT        17
+#define    GRXSTSRH_DPID_MASK        0x00018000
+#define    GRXSTSRH_DPID_SHIFT        15
+#define    GRXSTSRH_BCNT_MASK        0x00007ff0
+#define    GRXSTSRH_BCNT_SHIFT        4
+#define    GRXSTSRH_CHNUM_MASK        0x0000000f
+#define    GRXSTSRH_CHNUM_SHIFT        0
+
+#define    GRXSTSRD_FN_MASK        0x01e00000
+#define    GRXSTSRD_FN_GET(x)        (((x) >> 21) & 15)
+#define    GRXSTSRD_FN_SHIFT        21
+#define    GRXSTSRD_PKTSTS_MASK        0x001e0000
+#define    GRXSTSRD_PKTSTS_SHIFT        17
+#define    GRXSTSRH_IN_DATA        (2<<17)
+#define    GRXSTSRH_IN_COMPLETE        (3<<17)
+#define    GRXSTSRH_DT_ERROR        (5<<17)
+#define    GRXSTSRH_HALTED            (7<<17)
+#define    GRXSTSRD_GLOB_OUT_NAK        (1<<17)
+#define    GRXSTSRD_OUT_DATA        (2<<17)
+#define    GRXSTSRD_OUT_COMPLETE        (3<<17)
+#define    GRXSTSRD_STP_COMPLETE        (4<<17)
+#define    GRXSTSRD_STP_DATA        (6<<17)
+#define    GRXSTSRD_DPID_MASK        0x00018000
+#define    GRXSTSRD_DPID_SHIFT        15
+#define    GRXSTSRD_DPID_DATA0        (0<<15)
+#define    GRXSTSRD_DPID_DATA1        (2<<15)
+#define    GRXSTSRD_DPID_DATA2        (1<<15)
+#define    GRXSTSRD_DPID_MDATA        (3<<15)
+#define    GRXSTSRD_BCNT_MASK        0x00007ff0
+#define    GRXSTSRD_BCNT_GET(x)        (((x) >> 4) & 0x7FF)
+#define    GRXSTSRD_BCNT_SHIFT        4
+#define    GRXSTSRD_CHNUM_MASK        0x0000000f
+#define    GRXSTSRD_CHNUM_GET(x)        ((x) & 15)
+#define    GRXSTSRD_CHNUM_SHIFT        0
+
+#define    GRXFSIZ_RXFDEP_MASK        0x0000ffff
+#define    GRXFSIZ_RXFDEP_SHIFT        0
+
+#define    GNPTXFSIZ_NPTXFDEP_MASK        0xffff0000
+#define    GNPTXFSIZ_NPTXFDEP_SHIFT    0
+#define    GNPTXFSIZ_NPTXFSTADDR_MASK    0x0000ffff
+#define    GNPTXFSIZ_NPTXFSTADDR_SHIFT    16
+
+#define    GNPTXSTS_NPTXQTOP_SHIFT        24
+#define    GNPTXSTS_NPTXQTOP_MASK        0x7f000000
+#define    GNPTXSTS_NPTXQSPCAVAIL_SHIFT    16
+#define    GNPTXSTS_NPTXQSPCAVAIL_MASK    0x00ff0000
+#define    GNPTXSTS_NPTXFSPCAVAIL_SHIFT    0
+#define    GNPTXSTS_NPTXFSPCAVAIL_MASK    0x0000ffff
+
+#define    GI2CCTL_BSYDNE_SC        (1<<31)
+#define    GI2CCTL_RW            (1<<30)
+#define    GI2CCTL_I2CDATSE0        (1<<28)
+#define    GI2CCTL_I2CDEVADR_SHIFT        26
+#define    GI2CCTL_I2CDEVADR_MASK        0x0c000000
+#define    GI2CCTL_I2CSUSPCTL        (1<<25)
+#define    GI2CCTL_ACK            (1<<24)
+#define    GI2CCTL_I2CEN            (1<<23)
+#define    GI2CCTL_ADDR_SHIFT        16
+#define    GI2CCTL_ADDR_MASK        0x007f0000
+#define    GI2CCTL_REGADDR_SHIFT        8
+#define    GI2CCTL_REGADDR_MASK        0x0000ff00
+#define    GI2CCTL_RWDATA_SHIFT        0
+#define    GI2CCTL_RWDATA_MASK        0x000000ff
+
+#define    GPVNDCTL_DISULPIDRVR        (1<<31)
+#define    GPVNDCTL_VSTSDONE        (1<<27)
+#define    GPVNDCTL_VSTSBSY        (1<<26)
+#define    GPVNDCTL_NEWREGREQ        (1<<25)
+#define    GPVNDCTL_REGWR            (1<<22)
+#define    GPVNDCTL_REGADDR_SHIFT        16
+#define    GPVNDCTL_REGADDR_MASK        0x003f0000
+#define    GPVNDCTL_VCTRL_SHIFT        8
+#define    GPVNDCTL_VCTRL_MASK        0x0000ff00
+#define    GPVNDCTL_REGDATA_SHIFT        0
+#define    GPVNDCTL_REGDATA_MASK        0x000000ff
+
+#define    GGPIO_GPO_SHIFT            16
+#define    GGPIO_GPO_MASK            0xffff0000
+#define    GGPIO_GPI_SHIFT            0
+#define    GGPIO_GPI_MASK            0x0000ffff
+
+#define    GHWCFG1_GET_DIR(x, n)        (((x) >> (2 * (n))) & 3)
+#define    GHWCFG1_BIDIR            0
+#define    GHWCFG1_IN            1
+#define    GHWCFG1_OUT            2
+
+#define    GHWCFG2_TKNQDEPTH_SHIFT        26
+#define    GHWCFG2_TKNQDEPTH_MASK        0x7c000000
+#define    GHWCFG2_PTXQDEPTH_SHIFT        24
+#define    GHWCFG2_PTXQDEPTH_MASK        0x03000000
+#define    GHWCFG2_NPTXQDEPTH_SHIFT    22
+#define    GHWCFG2_NPTXQDEPTH_MASK        0x00c00000
+#define    GHWCFG2_MPI            (1<<20)
+#define    GHWCFG2_DYNFIFOSIZING        (1<<19)
+#define    GHWCFG2_PERIOSUPPORT        (1<<18)
+#define    GHWCFG2_NUMHSTCHNL_SHIFT    14
+#define    GHWCFG2_NUMHSTCHNL_MASK        0x0003c000
+#define    GHWCFG2_NUMHSTCHNL_GET(x)    ((((x) >> 14) & 15) + 1)
+#define    GHWCFG2_NUMDEVEPS_SHIFT        10
+#define    GHWCFG2_NUMDEVEPS_MASK        0x00003c00
+#define    GHWCFG2_NUMDEVEPS_GET(x)    ((((x) >> 10) & 15) + 1)
+#define    GHWCFG2_FSPHYTYPE_SHIFT        8
+#define    GHWCFG2_FSPHYTYPE_MASK        0x00000300
+#define    GHWCFG2_HSPHYTYPE_SHIFT        6
+#define    GHWCFG2_HSPHYTYPE_MASK        0x000000c0
+#define    GHWCFG2_SINGPNT            (1<<5)
+#define    GHWCFG2_OTGARCH_SHIFT        3
+#define    GHWCFG2_OTGARCH_MASK        0x00000018
+#define    GHWCFG2_OTGMODE_SHIFT        0
+#define    GHWCFG2_OTGMODE_MASK        0x00000007
+
+#define    GHWCFG3_DFIFODEPTH_SHIFT    16
+#define    GHWCFG3_DFIFODEPTH_MASK        0xffff0000
+#define    GHWCFG3_DFIFODEPTH_GET(x)    ((x) >> 16)
+#define    GHWCFG3_RSTTYPE            (1<<11)
+#define    GHWCFG3_OPTFEATURE        (1<<10)
+#define    GHWCFG3_VNDCTLSUPT        (1<<9)
+#define    GHWCFG3_I2CINTSEL        (1<<8)
+#define    GHWCFG3_OTGEN            (1<<7)
+#define    GHWCFG3_PKTSIZEWIDTH_SHIFT    4
+#define    GHWCFG3_PKTSIZEWIDTH_MASK    0x00000070
+#define    GHWCFG3_PKTSIZE_GET(x)        (0x10<<(((x) >> 4) & 7))
+#define    GHWCFG3_XFERSIZEWIDTH_SHIFT    0
+#define    GHWCFG3_XFERSIZEWIDTH_MASK    0x0000000f
+#define    GHWCFG3_XFRRSIZE_GET(x)        (0x400<<(((x) >> 0) & 15))
+
+#define    GHWCFG4_NUM_IN_EP_GET(x)    ((((x) >> 26) & 15) + 1)
+#define    GHWCFG4_SESSENDFLTR        (1<<24)
+#define    GHWCFG4_BVALIDFLTR        (1<<23)
+#define    GHWCFG4_AVALIDFLTR        (1<<22)
+#define    GHWCFG4_VBUSVALIDFLTR        (1<<21)
+#define    GHWCFG4_IDDGFLTR        (1<<20)
+#define    GHWCFG4_NUMCTLEPS_SHIFT        16
+#define    GHWCFG4_NUMCTLEPS_MASK        0x000f0000
+#define    GHWCFG4_NUMCTLEPS_GET(x)    (((x) >> 16) & 15)
+#define    GHWCFG4_PHYDATAWIDTH_SHIFT    14
+#define    GHWCFG4_PHYDATAWIDTH_MASK    0x0000c000
+#define    GHWCFG4_AHBFREQ            (1<<5)
+#define    GHWCFG4_ENABLEPWROPT        (1<<4)
+#define    GHWCFG4_NUMDEVPERIOEPS_SHIFT    0
+#define    GHWCFG4_NUMDEVPERIOEPS_MASK    0x0000000f
+#define    GHWCFG4_NUMDEVPERIOEPS_GET(x)    (((x) >> 0) & 15)
+
+#define    GLPMCFG_HSIC_CONN        (1<<30)
+
+#define    GPWRDN_BVALID            (1<<22)
+#define    GPWRDN_IDDIG            (1<<21)
+#define    GPWRDN_CONNDET_INT        (1<<14)
+#define    GPWRDN_CONNDET            (1<<13)
+#define    GPWRDN_DISCONN_INT        (1<<12)
+#define    GPWRDN_DISCONN            (1<<11)
+#define    GPWRDN_RESETDET_INT        (1<<10)
+#define    GPWRDN_RESETDET            (1<<9)
+#define    GPWRDN_LINESTATE_INT        (1<<8)
+#define    GPWRDN_LINESTATE        (1<<7)
+#define    GPWRDN_DISABLE_VBUS        (1<<6)
+#define    GPWRDN_POWER_DOWN        (1<<5)
+#define    GPWRDN_POWER_DOWN_RST        (1<<4)
+#define    GPWRDN_POWER_DOWN_CLAMP        (1<<3)
+#define    GPWRDN_RESTORE            (1<<2)
+#define    GPWRDN_PMU_ACTIVE        (1<<1)
+#define    GPWRDN_PMU_IRQ_SEL        (1<<0)
+
+#define    HPTXFSIZ_PTXFSIZE_SHIFT        16
+#define    HPTXFSIZ_PTXFSIZE_MASK        0xffff0000
+#define    HPTXFSIZ_PTXFSTADDR_SHIFT    0
+#define    HPTXFSIZ_PTXFSTADDR_MASK    0x0000ffff
+
+#define    DPTXFSIZN_DPTXFSIZE_SHIFT    16
+#define    DPTXFSIZN_DPTXFSIZE_MASK    0xffff0000
+#define    DPTXFSIZN_PTXFSTADDR_SHIFT    0
+#define    DPTXFSIZN_PTXFSTADDR_MASK    0x0000ffff
+
+#define    DIEPTXFN_INEPNTXFDEP_SHIFT    16
+#define    DIEPTXFN_INEPNTXFDEP_MASK    0xffff0000
+#define    DIEPTXFN_INEPNTXFSTADDR_SHIFT    0
+#define    DIEPTXFN_INEPNTXFSTADDR_MASK    0x0000ffff
+
+#define    HCFG_MODECHANGERDY        (1<<31)
+#define    HCFG_PERSCHEDENABLE        (1<<26)
+#define    HCFG_FLENTRIES_SHIFT        24
+#define    HCFG_FLENTRIES_MASK        0x03000000
+#define    HCFG_FLENTRIES_8        (0)
+#define    HCFG_FLENTRIES_16        (1)
+#define    HCFG_FLENTRIES_32        (2)
+#define    HCFG_FLENTRIES_64        (3)
+#define    HCFG_MULTISEGDMA        (1<<23)
+#define    HCFG_32KHZSUSPEND        (1<<7)
+#define    HCFG_FSLSSUPP            (1<<2)
+#define    HCFG_FSLSPCLKSEL_SHIFT        0
+#define    HCFG_FSLSPCLKSEL_MASK        0x00000003
+
+#define    HFIR_RELOADCTRL            (1<<16)
+#define    HFIR_FRINT_SHIFT        0
+#define    HFIR_FRINT_MASK            0x0000ffff
+
+#define    HFNUM_FRREM_SHIFT        16
+#define    HFNUM_FRREM_MASK        0xffff0000
+#define    HFNUM_FRNUM_SHIFT        0
+#define    HFNUM_FRNUM_MASK        0x0000ffff
+
+#define    HPTXSTS_ODD            (1<<31)
+#define    HPTXSTS_CHAN_SHIFT        27
+#define    HPTXSTS_CHAN_MASK        0x78000000
+#define    HPTXSTS_TOKEN_SHIFT        25
+#define    HPTXSTS_TOKEN_MASK        0x06000000
+#define    HPTXSTS_TOKEN_ZL        0
+#define    HPTXSTS_TOKEN_PING        1
+#define    HPTXSTS_TOKEN_DISABLE        2
+#define    HPTXSTS_TERMINATE        (1<<24)
+#define    HPTXSTS_PTXQSPCAVAIL_SHIFT    16
+#define    HPTXSTS_PTXQSPCAVAIL_MASK    0x00ff0000
+#define    HPTXSTS_PTXFSPCAVAIL_SHIFT    0
+#define    HPTXSTS_PTXFSPCAVAIL_MASK    0x0000ffff
+
+#define    HAINT_HAINT_SHIFT        0
+#define    HAINT_HAINT_MASK        0x0000ffff
+#define    HAINTMSK_HAINTMSK_SHIFT        0
+#define    HAINTMSK_HAINTMSK_MASK        0x0000ffff
+
+#define    HPRT_PRTSPD_SHIFT        17
+#define    HPRT_PRTSPD_MASK        0x00060000
+#define    HPRT_PRTSPD_HIGH        0
+#define    HPRT_PRTSPD_FULL        1
+#define    HPRT_PRTSPD_LOW            2
+#define    HPRT_PRTSPD_MASK        0x00060000
+#define    HPRT_PRTTSTCTL_SHIFT        13
+#define    HPRT_PRTTSTCTL_MASK        0x0001e000
+#define    HPRT_PRTPWR            (1<<12)
+#define    HPRT_PRTLNSTS_SHIFT        10
+#define    HPRT_PRTLNSTS_MASK        0x00000c00
+#define    HPRT_PRTRST            (1<<8)
+#define    HPRT_PRTSUSP            (1<<7)
+#define    HPRT_PRTRES            (1<<6)
+#define    HPRT_PRTOVRCURRCHNG        (1<<5)
+#define    HPRT_PRTOVRCURRACT        (1<<4)
+#define    HPRT_PRTENCHNG            (1<<3)
+#define    HPRT_PRTENA            (1<<2)
+#define    HPRT_PRTCONNDET            (1<<1)
+#define    HPRT_PRTCONNSTS            (1<<0)
+
+#define    HCCHAR_CHENA            (1<<31)
+#define    HCCHAR_CHDIS            (1<<30)
+#define    HCCHAR_ODDFRM            (1<<29)
+#define    HCCHAR_DEVADDR_SHIFT        22
+#define    HCCHAR_DEVADDR_MASK        0x1fc00000
+#define    HCCHAR_MC_SHIFT            20
+#define    HCCHAR_MC_MASK            0x00300000
+#define    HCCHAR_EPTYPE_SHIFT        18
+#define    HCCHAR_EPTYPE_MASK        0x000c0000
+#define    HCCHAR_LSPDDEV            (1<<17)
+#define    HCCHAR_EPDIR            (1<<15)
+#define    HCCHAR_EPDIR_IN            (1<<15)
+#define    HCCHAR_EPDIR_OUT        0
+#define    HCCHAR_EPNUM_SHIFT        11
+#define    HCCHAR_EPNUM_MASK        0x00007800
+#define    HCCHAR_MPS_SHIFT        0
+#define    HCCHAR_MPS_MASK            0x000007ff
+
+#define    HCSPLT_SPLTENA            (1<<31)
+#define    HCSPLT_COMPSPLT            (1<<16)
+#define    HCSPLT_XACTPOS_SHIFT        14
+#define    HCSPLT_XACTPOS_MASK        0x0000c000
+#define    HCSPLT_XACTPOS_MIDDLE        0
+#define    HCSPLT_XACTPOS_LAST        1
+#define    HCSPLT_XACTPOS_BEGIN        2
+#define    HCSPLT_XACTPOS_ALL        3
+#define    HCSPLT_XACTLEN_BURST        1023    /* bytes */
+#define    HCSPLT_HUBADDR_SHIFT        7
+#define    HCSPLT_HUBADDR_MASK        0x00003f80
+#define    HCSPLT_PRTADDR_SHIFT        0
+#define    HCSPLT_PRTADDR_MASK        0x0000007f
+
+#define    HCINT_ERRORS \
+    (HCINT_BBLERR | HCINT_XACTERR)
+#define    HCINT_RETRY \
+    (HCINT_DATATGLERR | HCINT_FRMOVRUN | HCINT_NAK)
+#define    HCINT_DEFAULT_MASK \
+  (HCINT_STALL | HCINT_BBLERR | \
+   HCINT_XACTERR | HCINT_NAK | HCINT_ACK | HCINT_NYET | \
+   HCINT_CHHLTD | HCINT_FRMOVRUN | \
+   HCINT_DATATGLERR)
+#define    HCINT_HCH_DONE_MASK \
+  (HCINT_ACK | HCINT_RETRY | HCINT_NYET | \
+   HCINT_ERRORS | HCINT_STALL | HCINT_SOFTWARE_ONLY)
+
+#define    HCINT_SOFTWARE_ONLY        (1<<20)    /* BSD only */
+#define    HCINT_DATATGLERR        (1<<10)
+#define    HCINT_FRMOVRUN            (1<<9)
+#define    HCINT_BBLERR            (1<<8)
+#define    HCINT_XACTERR            (1<<7)
+#define    HCINT_NYET            (1<<6)
+#define    HCINT_ACK            (1<<5)
+#define    HCINT_NAK            (1<<4)
+#define    HCINT_STALL            (1<<3)
+#define    HCINT_AHBERR            (1<<2)
+#define    HCINT_CHHLTD            (1<<1)
+#define    HCINT_XFERCOMPL            (1<<0)
+
+#define    HCINTMSK_DATATGLERRMSK        (1<<10)
+#define    HCINTMSK_FRMOVRUNMSK        (1<<9)
+#define    HCINTMSK_BBLERRMSK        (1<<8)
+#define    HCINTMSK_XACTERRMSK        (1<<7)
+#define    HCINTMSK_NYETMSK        (1<<6)
+#define    HCINTMSK_ACKMSK            (1<<5)
+#define    HCINTMSK_NAKMSK            (1<<4)
+#define    HCINTMSK_STALLMSK        (1<<3)
+#define    HCINTMSK_AHBERRMSK        (1<<2)
+#define    HCINTMSK_CHHLTDMSK        (1<<1)
+#define    HCINTMSK_XFERCOMPLMSK        (1<<0)
+
+#define    HCTSIZ_DOPNG            (1<<31)
+#define    HCTSIZ_PID_SHIFT        29
+#define    HCTSIZ_PID_MASK            0x60000000
+#define    HCTSIZ_PID_DATA0        0
+#define    HCTSIZ_PID_DATA2        1
+#define    HCTSIZ_PID_DATA1        2
+#define    HCTSIZ_PID_MDATA        3
+#define    HCTSIZ_PID_SETUP        3
+#define    HCTSIZ_PKTCNT_SHIFT        19
+#define    HCTSIZ_PKTCNT_MASK        0x1ff80000
+#define    HCTSIZ_XFERSIZE_SHIFT        0
+#define    HCTSIZ_XFERSIZE_MASK        0x0007ffff
+
+#define    DCFG_EPMISCNT_SHIFT        18
+#define    DCFG_EPMISCNT_MASK        0x007c0000
+#define    DCFG_PERFRINT_SHIFT        11
+#define    DCFG_PERFRINT_MASK        0x00001800
+#define    DCFG_DEVADDR_SHIFT        4
+#define    DCFG_DEVADDR_MASK        0x000007f0
+#define    DCFG_DEVADDR_SET(x)        (((x) & 0x7F) << 4)
+#define    DCFG_NZSTSOUTHSHK        (1<<2)
+#define    DCFG_DEVSPD_SHIFT        0
+#define    DCFG_DEVSPD_MASK        0x00000003
+#define    DCFG_DEVSPD_SET(x)        ((x) & 0x3)
+#define    DCFG_DEVSPD_HI            0
+#define    DCFG_DEVSPD_FULL20        1
+#define    DCFG_DEVSPD_FULL10        3
+
+#define    DCTL_PWRONPRGDONE        (1<<11)
+#define    DCTL_CGOUTNAK            (1<<10)
+#define    DCTL_SGOUTNAK            (1<<9)
+#define    DCTL_CGNPINNAK            (1<<8)
+#define    DCTL_SGNPINNAK            (1<<7)
+#define    DCTL_TSTCTL_SHIFT        4
+#define    DCTL_TSTCTL_MASK        0x00000070
+#define    DCTL_GOUTNAKSTS            (1<<3)
+#define    DCTL_GNPINNAKSTS        (1<<2)
+#define    DCTL_SFTDISCON            (1<<1)
+#define    DCTL_RMTWKUPSIG            (1<<0)
+
+#define    DSTS_SOFFN_SHIFT        8
+#define    DSTS_SOFFN_MASK            0x003fff00
+#define    DSTS_SOFFN_GET(x)        (((x) >> 8) & 0x3FFF)
+#define    DSTS_ERRTICERR            (1<<3)
+#define    DSTS_ENUMSPD_SHIFT        1
+#define    DSTS_ENUMSPD_MASK        0x00000006
+#define    DSTS_ENUMSPD_GET(x)        (((x) >> 1) & 3)
+#define    DSTS_ENUMSPD_HI            0
+#define    DSTS_ENUMSPD_FULL20        1
+#define    DSTS_ENUMSPD_LOW10        2
+#define    DSTS_ENUMSPD_FULL10        3
+#define    DSTS_SUSPSTS            (1<<0)
+
+#define    DIEPMSK_TXFIFOUNDRNMSK        (1<<8)
+#define    DIEPMSK_INEPNAKEFFMSK        (1<<6)
+#define    DIEPMSK_INTKNEPMISMSK        (1<<5)
+#define    DIEPMSK_INTKNTXFEMPMSK        (1<<4)
+#define    DIEPMSK_FIFOEMPTY        (1<<4)
+#define    DIEPMSK_TIMEOUTMSK        (1<<3)
+#define    DIEPMSK_AHBERRMSK        (1<<2)
+#define    DIEPMSK_EPDISBLDMSK        (1<<1)
+#define    DIEPMSK_XFERCOMPLMSK        (1<<0)
+
+#define    DOEPMSK_OUTPKTERRMSK        (1<<8)
+#define    DOEPMSK_BACK2BACKSETUP        (1<<6)
+#define    DOEPMSK_OUTTKNEPDISMSK        (1<<4)
+#define    DOEPMSK_FIFOEMPTY        (1<<4)
+#define    DOEPMSK_SETUPMSK        (1<<3)
+#define    DOEPMSK_AHBERRMSK        (1<<2)
+#define    DOEPMSK_EPDISBLDMSK        (1<<1)
+#define    DOEPMSK_XFERCOMPLMSK        (1<<0)
+
+#define    DIEPINT_TXFIFOUNDRN        (1<<8)
+#define    DIEPINT_INEPNAKEFF        (1<<6)
+#define    DIEPINT_INTKNEPMIS        (1<<5)
+#define    DIEPINT_INTKNTXFEMP        (1<<4)
+#define    DIEPINT_TIMEOUT            (1<<3)
+#define    DIEPINT_AHBERR            (1<<2)
+#define    DIEPINT_EPDISBLD        (1<<1)
+#define    DIEPINT_XFERCOMPL        (1<<0)
+
+#define    DOEPINT_OUTPKTERR        (1<<8)
+#define    DOEPINT_BACK2BACKSETUP        (1<<6)
+#define    DOEPINT_OUTTKNEPDIS        (1<<4)
+#define    DOEPINT_SETUP            (1<<3)
+#define    DOEPINT_AHBERR            (1<<2)
+#define    DOEPINT_EPDISBLD        (1<<1)
+#define    DOEPINT_XFERCOMPL        (1<<0)
+
+#define    DAINT_INEPINT_MASK        0xffff0000
+#define    DAINT_INEPINT_SHIFT        0
+#define    DAINT_OUTEPINT_MASK        0x0000ffff
+#define    DAINT_OUTEPINT_SHIFT        16
+
+#define    DAINTMSK_INEPINT_MASK        0xffff0000
+#define    DAINTMSK_INEPINT_SHIFT        0
+#define    DAINTMSK_OUTEPINT_MASK        0x0000ffff
+#define    DAINTMSK_OUTEPINT_SHIFT        16
+
+#define    DTKNQR1_EPTKN_SHIFT        8
+#define    DTKNQR1_EPTKN_MASK        0xffffff00
+#define    DTKNQR1_WRAPBIT            (1<<7)
+#define    DTKNQR1_INTKNWPTR_SHIFT        0
+#define    DTKNQR1_INTKNWPTR_MASK        0x0000001f
+
+#define    DVBUSDIS_DVBUSDIS_SHIFT        0
+#define    DVBUSDIS_DVBUSDIS_MASK        0x0000ffff
+
+#define    DVBUSPULSE_DVBUSPULSE_SHIFT    0
+#define    DVBUSPULSE_DVBUSPULSE_MASK    0x00000fff
+
+#define    DTHRCTL_ARBPRKEN        (1<<27)
+#define    DTHRCTL_RXTHRLEN_SHIFT        17
+#define    DTHRCTL_RXTHRLEN_MASK        0x03fe0000
+#define    DTHRCTL_RXTHREN            (1<<16)
+#define    DTHRCTL_TXTHRLEN_SHIFT        2
+#define    DTHRCTL_TXTHRLEN_MASK        0x000007fc
+#define    DTHRCTL_ISOTHREN        (1<<1)
+#define    DTHRCTL_NONISOTHREN        (1<<0)
+
+#define    DIEPEMPMSK_INEPTXFEMPMSK_SHIFT    0
+#define    DIEPEMPMSK_INEPTXFEMPMSK_MASK    0x0000ffff
+
+#define    DIEPCTL_EPENA            (1<<31)
+#define    DIEPCTL_EPDIS            (1<<30)
+#define    DIEPCTL_SETD1PID        (1<<29)
+#define    DIEPCTL_SETD0PID        (1<<28)
+#define    DIEPCTL_SNAK            (1<<27)
+#define    DIEPCTL_CNAK            (1<<26)
+#define    DIEPCTL_TXFNUM_SHIFT        22
+#define    DIEPCTL_TXFNUM_MASK        0x03c00000
+#define    DIEPCTL_TXFNUM_SET(n)        (((n) & 15) << 22)
+#define    DIEPCTL_STALL            (1<<21)
+#define    DIEPCTL_EPTYPE_SHIFT        18
+#define    DIEPCTL_EPTYPE_MASK        0x000c0000
+#define    DIEPCTL_EPTYPE_SET(n)        (((n) & 3) << 18)
+#define    DIEPCTL_EPTYPE_CONTROL        0
+#define    DIEPCTL_EPTYPE_ISOC        1
+#define    DIEPCTL_EPTYPE_BULK        2
+#define    DIEPCTL_EPTYPE_INTERRUPT    3
+#define    DIEPCTL_NAKSTS            (1<<17)
+#define    DIEPCTL_USBACTEP        (1<<15)
+#define    DIEPCTL_NEXTEP_SHIFT        11
+#define    DIEPCTL_NEXTEP_MASK        0x00007800
+#define    DIEPCTL_MPS_SHIFT        0
+#define    DIEPCTL_MPS_MASK        0x000007ff
+#define    DIEPCTL_MPS_SET(n)        ((n) & 0x7FF)
+#define    DIEPCTL_MPS_64            (0<<0)
+#define    DIEPCTL_MPS_32            (1<<0)
+#define    DIEPCTL_MPS_16            (2<<0)
+#define    DIEPCTL_MPS_8            (3<<0)
+
+#define    DOEPCTL_EPENA            (1<<31)
+#define    DOEPCTL_EPDIS            (1<<30)
+#define    DOEPCTL_SETD1PID        (1<<29)
+#define    DOEPCTL_SETD0PID        (1<<28)
+#define    DOEPCTL_SNAK            (1<<27)
+#define    DOEPCTL_CNAK            (1<<26)
+#define    DOEPCTL_FNUM_SET(n)        (((n) & 15) << 22)
+#define    DOEPCTL_STALL            (1<<21)
+#define    DOEPCTL_EPTYPE_SHIFT        18
+#define    DOEPCTL_EPTYPE_MASK        0x000c0000
+#define    DOEPCTL_EPTYPE_SET(n)        (((n) & 3) << 18)
+#define    DOEPCTL_NAKSTS            (1<<17)
+#define    DOEPCTL_USBACTEP        (1<<15)
+#define    DOEPCTL_MPS_SHIFT        0
+#define    DOEPCTL_MPS_MASK        0x000007ff
+#define    DOEPCTL_MPS_SET(n)        ((n) & 0x7FF)
+#define    DOEPCTL_MPS_64            (0<<0)
+#define    DOEPCTL_MPS_32            (1<<0)
+#define    DOEPCTL_MPS_16            (2<<0)
+#define    DOEPCTL_MPS_8            (3<<0)
+
+/* common bits */
+#define    DXEPINT_TXFEMP            (1<<7)
+#define    DXEPINT_SETUP            (1<<3)
+#define    DXEPINT_XFER_COMPL        (1<<0)
+
+#define    DIEPTSIZ_XFERSIZE_MASK        0x0007ffff
+#define    DIEPTSIZ_XFERSIZE_SHIFT        0
+#define    DIEPTSIZ_PKTCNT_MASK        0x1ff80000
+#define    DIEPTSIZ_PKTCNT_SHIFT        19
+#define    DIEPTSIZ_MC_MASK        0x60000000
+#define    DIEPTSIZ_MC_SHIFT        29
+
+#define    DOEPTSIZ_XFERSIZE_MASK        0x0007ffff
+#define    DOEPTSIZ_XFERSIZE_SHIFT        0
+#define    DOEPTSIZ_PKTCNT_MASK        0x1ff80000
+#define    DOEPTSIZ_PKTCNT_SHIFT        19
+#define    DOEPTSIZ_MC_MASK        0x60000000
+#define    DOEPTSIZ_MC_SHIFT        29
+
+/* common bits */
+#define    DXEPTSIZ_SET_MULTI(n)        (((n) & 3) << 29)
+#define    DXEPTSIZ_SET_NPKT(n)        (((n) & 0x3FF) << 19)
+#define    DXEPTSIZ_GET_NPKT(n)        (((n) >> 19) & 0x3FF)
+#define    DXEPTSIZ_SET_NBYTES(n)        (((n) & 0x7FFFFF) << 0)
+#define    DXEPTSIZ_GET_NBYTES(n)        (((n) >> 0) & 0x7FFFFF)
+
+/* generic endpoint mask */
+
+#define    ENDPOINT_MASK(x,in) \
+    ((in) ? (1U << ((x) & 15U)) : \
+     (0x10000U << ((x) & 15U)))
+
+#endif                    /* _DWC_OTGREG_H_ */
diff --git a/freebsd/sys/dev/usb/usb.h b/freebsd/sys/dev/usb/usb.h
index bb5a60c..c090d71 100644
--- a/freebsd/sys/dev/usb/usb.h
+++ b/freebsd/sys/dev/usb/usb.h
@@ -572,6 +572,15 @@ static const struct name name = {    \
   .bData = { m },            \
 }

+#ifdef __rtems__
+struct usb_string_lang {
+    uByte bLength;
+    uByte bDescriptorType;
+    uByte bData[2];
+} __packed;
+typedef struct usb_string_lang usb_string_lang_t;
+#endif
+
 struct usb_hub_descriptor {
     uByte    bDescLength;
     uByte    bDescriptorType;
diff --git a/freebsd/sys/dev/usb/usb_bus.h b/freebsd/sys/dev/usb/usb_bus.h
index 0a7350c..e3d546f 100644
--- a/freebsd/sys/dev/usb/usb_bus.h
+++ b/freebsd/sys/dev/usb/usb_bus.h
@@ -85,6 +85,9 @@ struct usb_bus {
      * This mutex protects the USB hardware:
      */
     struct mtx bus_mtx;
+    #ifdef __rtems__
+    struct mtx bus_spin_lock;
+    #endif
     struct usb_xfer_queue intr_q;
     struct usb_callout power_wdog;    /* power management */

diff --git a/freebsd/sys/dev/usb/usb_core.c b/freebsd/sys/dev/usb/usb_core.c
index 80f5ecd..8d5c609 100644
--- a/freebsd/sys/dev/usb/usb_core.c
+++ b/freebsd/sys/dev/usb/usb_core.c
@@ -54,6 +54,13 @@
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>

+#ifdef __rtems__
+const struct usb_string_lang usb_string_lang_en = {
+    sizeof(usb_string_lang_en), UDESC_STRING,
+    { 0x09, 0x04 } /* American English */
+};
+#endif
+
 MALLOC_DEFINE(M_USB, "USB", "USB");
 MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
 MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
diff --git a/freebsd/sys/dev/usb/usb_core.h b/freebsd/sys/dev/usb/usb_core.h
index 287e69f..8a2ab19 100644
--- a/freebsd/sys/dev/usb/usb_core.h
+++ b/freebsd/sys/dev/usb/usb_core.h
@@ -44,6 +44,11 @@
 #define    USB_BUS_LOCK(_b)        mtx_lock(&(_b)->bus_mtx)
 #define    USB_BUS_UNLOCK(_b)        mtx_unlock(&(_b)->bus_mtx)
 #define    USB_BUS_LOCK_ASSERT(_b, _t)    mtx_assert(&(_b)->bus_mtx, _t)
+#ifdef __rtems__
+#define    USB_BUS_SPIN_LOCK(_b)        mtx_lock_spin(&(_b)->bus_spin_lock)
+#define    USB_BUS_SPIN_UNLOCK(_b)
mtx_unlock_spin(&(_b)->bus_spin_lock)
+#define    USB_BUS_SPIN_LOCK_ASSERT(_b, _t)
mtx_assert(&(_b)->bus_spin_lock, _t)
+#endif
 #define    USB_XFER_LOCK(_x)        mtx_lock((_x)->xroot->xfer_mtx)
 #define    USB_XFER_UNLOCK(_x)        mtx_unlock((_x)->xroot->xfer_mtx)
 #define    USB_XFER_LOCK_ASSERT(_x, _t)
mtx_assert((_x)->xroot->xfer_mtx, _t)
@@ -176,6 +181,9 @@ struct usb_xfer {
 /* external variables */

 extern struct mtx usb_ref_lock;
+#ifdef __rtems__
+extern const struct usb_string_lang usb_string_lang_en;
+#endif

 /* typedefs */

diff --git a/rtemsbsd/include/bsp/nexus-devices.h
b/rtemsbsd/include/bsp/nexus-devices.h
index 6ccf726..58e26b2 100644
--- a/rtemsbsd/include/bsp/nexus-devices.h
+++ b/rtemsbsd/include/bsp/nexus-devices.h
@@ -125,6 +125,25 @@ SYSINIT_DRIVER_REFERENCE(lem, pci);
 SYSINIT_DRIVER_REFERENCE(igb, pci);
 SYSINIT_DRIVER_REFERENCE(em, pci);

+#elif defined(LIBBSP_ARM_RASPBERRYPI_BSP_H)
+
+#include <bsp/irq.h>
+
+static const rtems_bsd_device_resource bcm283x_dwcotg_res[] = {
+    {
+        .type = RTEMS_BSD_RES_MEMORY,
+        .start_request = 0,
+        .start_actual = 0x00980000
+    }, {
+        .type = RTEMS_BSD_RES_IRQ,
+        .start_request = 0,
+        .start_actual = 17
+    }
+};
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(bcm283x_dwcotg, 0,
RTEMS_ARRAY_SIZE(bcm283x_dwcotg_res),
+    &bcm283x_dwcotg_res[0]);
+
 #elif defined(LIBBSP_POWERPC_QORIQ_BSP_H)

 #if !QORIQ_CHIP_IS_T_VARIANT(QORIQ_CHIP_VARIANT)
diff --git a/rtemsbsd/include/machine/rtems-bsd-sysinit.h
b/rtemsbsd/include/machine/rtems-bsd-sysinit.h
index c8af236..3e2fc02 100644
--- a/rtemsbsd/include/machine/rtems-bsd-sysinit.h
+++ b/rtemsbsd/include/machine/rtems-bsd-sysinit.h
@@ -56,6 +56,9 @@
     SYSINIT_DRIVER_REFERENCE(ehci, nexus); \
     SYSINIT_DRIVER_REFERENCE(usbus, ehci)

+#define SYSINIT_NEED_USB_BCM283X_DWC_OTG \
+    SYSINIT_DRIVER_REFERENCE(bcm283x_dwcotg, nexus)
+
 #define SYSINIT_NEED_USB_MASS_STORAGE \
     SYSINIT_DRIVER_REFERENCE(umass, uhub)

diff --git a/wscript b/wscript
index 8c0c242..85de476 100644
--- a/wscript
+++ b/wscript
@@ -626,6 +626,7 @@ def build(bld):
     libbsd_use += ["objs06"]

     source = ['freebsd/sys/arm/xilinx/zy7_slcr.c',
+              'freebsd/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c',
               'freebsd/sys/cam/cam.c',
               'freebsd/sys/cam/scsi/scsi_all.c',
               'freebsd/sys/contrib/altq/altq/altq_cbq.c',
@@ -710,6 +711,8 @@ def build(bld):
               'freebsd/sys/dev/sdhci/sdhci.c',
               'freebsd/sys/dev/smc/if_smc.c',
               'freebsd/sys/dev/tsec/if_tsec.c',
+              'freebsd/sys/dev/usb/controller/dwc_otg.c',
+              'freebsd/sys/dev/usb/controller/dwc_otg_fdt.c',
               'freebsd/sys/dev/usb/controller/ehci.c',
               'freebsd/sys/dev/usb/controller/ohci.c',
               'freebsd/sys/dev/usb/controller/usb_controller.c',
-- 
1.7.1
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20150821/4a7ac150/attachment-0001.html>


More information about the devel mailing list