summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2015-11-12 13:01:25 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2015-11-12 13:01:25 +0100
commitc5a141a78655017f94642e5781d79cc5978e46ab (patch)
tree4402ba93028db8cd1e496f488250da45ac3e4f13
parentUSB: Update to FreeBSD trunk 2015-11-10 (diff)
downloadrtems-libbsd-c5a141a78655017f94642e5781d79cc5978e46ab.tar.bz2
dwc_otg: Import from FreeBSD trunk 2015-11-10
-rw-r--r--freebsd/sys/dev/usb/controller/dwc_otg.c4969
-rw-r--r--freebsd/sys/dev/usb/controller/dwc_otg.h218
-rw-r--r--freebsd/sys/dev/usb/controller/dwc_otgreg.h802
3 files changed, 5989 insertions, 0 deletions
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 00000000..2110b94d
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otg.c
@@ -0,0 +1,4969 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2015 Daisuke Aoyama. All rights reserved.
+ * Copyright (c) 2012-2015 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.
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#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>
+#endif /* USB_GLOBAL_INCLUDE_FILE */
+
+#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 */
+
+static const struct usb_bus_methods dwc_otg_bus_methods;
+static const struct usb_pipe_methods dwc_otg_device_non_isoc_methods;
+static const 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 *);
+
+/*
+ * Here is a configuration that the chip supports.
+ */
+static const 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 void
+dwc_otg_write_fifo(struct dwc_otg_softc *sc, struct usb_page_cache *pc,
+ uint32_t offset, uint32_t fifo, uint32_t count)
+{
+ uint32_t temp;
+
+ /* round down length to nearest 4-bytes */
+ temp = count & ~3;
+
+ /* check if we can write the data directly */
+ if (temp != 0 && usb_pc_buffer_is_aligned(pc, offset, temp, 3)) {
+ struct usb_page_search buf_res;
+
+ /* pre-subtract length */
+ count -= temp;
+
+ /* iterate buffer list */
+ do {
+ /* get current buffer pointer */
+ usbd_get_page(pc, offset, &buf_res);
+
+ if (buf_res.length > temp)
+ buf_res.length = temp;
+
+ /* transfer data into FIFO */
+ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ fifo, buf_res.buffer, buf_res.length / 4);
+
+ offset += buf_res.length;
+ fifo += buf_res.length;
+ temp -= buf_res.length;
+ } while (temp != 0);
+ }
+
+ /* check for remainder */
+ if (count != 0) {
+ /* clear topmost word before copy */
+ sc->sc_bounce_buffer[(count - 1) / 4] = 0;
+
+ /* copy out data */
+ usbd_copy_out(pc, offset,
+ sc->sc_bounce_buffer, count);
+
+ /* transfer data into FIFO */
+ bus_space_write_region_4(sc->sc_io_tag,
+ sc->sc_io_hdl, fifo, sc->sc_bounce_buffer,
+ (count + 3) / 4);
+ }
+}
+
+static void
+dwc_otg_read_fifo(struct dwc_otg_softc *sc, struct usb_page_cache *pc,
+ uint32_t offset, uint32_t count)
+{
+ uint32_t temp;
+
+ /* round down length to nearest 4-bytes */
+ temp = count & ~3;
+
+ /* check if we can read the data directly */
+ if (temp != 0 && usb_pc_buffer_is_aligned(pc, offset, temp, 3)) {
+ struct usb_page_search buf_res;
+
+ /* pre-subtract length */
+ count -= temp;
+
+ /* iterate buffer list */
+ do {
+ /* get current buffer pointer */
+ usbd_get_page(pc, offset, &buf_res);
+
+ if (buf_res.length > temp)
+ buf_res.length = temp;
+
+ /* transfer data from FIFO */
+ bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ sc->sc_current_rx_fifo, buf_res.buffer, buf_res.length / 4);
+
+ offset += buf_res.length;
+ sc->sc_current_rx_fifo += buf_res.length;
+ sc->sc_current_rx_bytes -= buf_res.length;
+ temp -= buf_res.length;
+ } while (temp != 0);
+ }
+
+ /* check for remainder */
+ if (count != 0) {
+ /* read data into bounce buffer */
+ bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ sc->sc_current_rx_fifo,
+ sc->sc_bounce_buffer, (count + 3) / 4);
+
+ /* store data into proper buffer */
+ usbd_copy_in(pc, offset, sc->sc_bounce_buffer, count);
+
+ /* round length up to nearest 4 bytes */
+ count = (count + 3) & ~3;
+
+ /* update counters */
+ sc->sc_current_rx_bytes -= count;
+ sc->sc_current_rx_fifo += count;
+ }
+}
+
+static void
+dwc_otg_tx_fifo_reset(struct dwc_otg_softc *sc, uint32_t value)
+{
+ uint32_t temp;
+
+ /* reset FIFO */
+ DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, value);
+
+ /* wait for reset to complete */
+ for (temp = 0; temp != 16; temp++) {
+ value = DWC_OTG_READ_4(sc, DOTG_GRSTCTL);
+ if (!(value & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)))
+ break;
+ }
+}
+
+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 (refer to PGM) */
+ 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);
+ }
+
+ if (mode == DWC_MODE_HOST) {
+
+ /* reset active endpoints */
+ sc->sc_active_rx_ep = 0;
+
+ /* split equally for periodic and non-periodic */
+ fifo_size /= 2;
+
+ DPRINTF("PTX/NPTX FIFO=%u\n", fifo_size);
+
+ /* 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++) {
+ /* enable 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));
+
+ /* 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_tx_fifo_reset(sc, GRSTCTL_RXFFLSH);
+
+ if (mode != DWC_MODE_OTG) {
+ /* reset all TX FIFOs */
+ dwc_otg_tx_fifo_reset(sc,
+ GRSTCTL_TXFIFO(0x10) |
+ GRSTCTL_TXFFLSH);
+ } else {
+ /* reset active endpoints */
+ sc->sc_active_rx_ep = 0;
+
+ /* reset host channel state */
+ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
+ }
+ 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)
+ return;
+ /* Ensure the SOF interrupt is not disabled */
+ sc->sc_needsof = 1;
+ /* Check if the SOF interrupt is already enabled */
+ if ((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);
+
+ if (sc->sc_current_rx_bytes != 0) {
+ /* need to dump remaining data */
+ bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ sc->sc_current_rx_fifo, sc->sc_bounce_buffer,
+ sc->sc_current_rx_bytes / 4);
+ /* clear number of active bytes to receive */
+ sc->sc_current_rx_bytes = 0;
+ }
+ /* 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_check_tx_fifo_empty(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ uint32_t temp;
+
+ temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
+
+ if (td->ep_type == UE_ISOCHRONOUS) {
+ /*
+ * NOTE: USB INTERRUPT transactions are executed like
+ * USB CONTROL transactions! See the setup standard
+ * chain function for more information.
+ */
+ if (!(temp & GINTSTS_PTXFEMP)) {
+ DPRINTF("Periodic TX FIFO is not empty\n");
+ if (!(sc->sc_irq_mask & GINTMSK_PTXFEMPMSK)) {
+ sc->sc_irq_mask |= GINTMSK_PTXFEMPMSK;
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+ }
+ return (1); /* busy */
+ }
+ } else {
+ if (!(temp & GINTSTS_NPTXFEMP)) {
+ DPRINTF("Non-periodic TX FIFO is not empty\n");
+ if (!(sc->sc_irq_mask & GINTMSK_NPTXFEMPMSK)) {
+ sc->sc_irq_mask |= GINTMSK_NPTXFEMPMSK;
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+ }
+ return (1); /* busy */
+ }
+ }
+ return (0); /* ready for transmit */
+}
+
+static uint8_t
+dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc,
+ struct dwc_otg_td *td, uint8_t is_out)
+{
+ uint8_t x;
+ uint8_t y;
+ uint8_t z;
+
+ if (td->channel[0] < 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 (dwc_otg_host_check_tx_fifo_empty(sc, td) != 0)
+ return (1); /* busy - cannot transfer data */
+ }
+ z = td->max_packet_count;
+ for (x = y = 0; x != sc->sc_host_ch_max; x++) {
+ /* check if channel is allocated */
+ if (sc->sc_chan_state[x].allocated != 0)
+ continue;
+ /* check if channel is still enabled */
+ if (sc->sc_chan_state[x].wait_halted != 0)
+ continue;
+ /* store channel number */
+ td->channel[y++] = x;
+ /* check if we got all channels */
+ if (y == z)
+ break;
+ }
+ if (y != z) {
+ /* reset channel variable */
+ td->channel[0] = DWC_OTG_MAX_CHANNELS;
+ td->channel[1] = DWC_OTG_MAX_CHANNELS;
+ td->channel[2] = DWC_OTG_MAX_CHANNELS;
+ /* wait a bit */
+ dwc_otg_enable_sof_irq(sc);
+ return (1); /* busy - not enough channels */
+ }
+
+ for (y = 0; y != z; y++) {
+ x = td->channel[y];
+
+ /* set allocated */
+ sc->sc_chan_state[x].allocated = 1;
+
+ /* set wait halted */
+ sc->sc_chan_state[x].wait_halted = 1;
+
+ /* 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);
+ }
+ return (0); /* allocated */
+}
+
+static void
+dwc_otg_host_channel_free_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t index)
+{
+ uint32_t hcchar;
+ uint8_t x;
+
+ if (td->channel[index] >= DWC_OTG_MAX_CHANNELS)
+ return; /* already freed */
+
+ /* free channel */
+ x = td->channel[index];
+ td->channel[index] = 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.
+ */
+ 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);
+
+ /* check if already halted */
+ if (sc->sc_chan_state[x].wait_halted == 0)
+ return;
+
+ /* disable host channel */
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
+ if (hcchar & HCCHAR_CHENA) {
+ DPRINTF("Halting channel %d\n", x);
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
+ hcchar | HCCHAR_CHDIS);
+ /* don't write HCCHAR until the channel is halted */
+ } else {
+ sc->sc_chan_state[x].wait_halted = 0;
+ }
+}
+
+static void
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ uint8_t x;
+ for (x = 0; x != td->max_packet_count; x++)
+ dwc_otg_host_channel_free_sub(sc, td, x);
+}
+
+static void
+dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ uint8_t x;
+ /* dump any pending messages */
+ if (sc->sc_last_rx_status == 0)
+ return;
+ for (x = 0; x != td->max_packet_count; x++) {
+ if (td->channel[x] >= DWC_OTG_MAX_CHANNELS ||
+ td->channel[x] != GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status))
+ continue;
+ dwc_otg_common_rx_ack(sc);
+ break;
+ }
+}
+
+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[0] < DWC_OTG_MAX_CHANNELS) {
+ hcint = sc->sc_chan_state[td->channel[0]].hcint;
+
+ DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+ td->channel[0], td->state, hcint,
+ DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])),
+ DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0])));
+ } 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[0]);
+ td->error_stall = 1;
+ td->error_any = 1;
+ goto complete;
+ } else if (hcint & HCINT_ERRORS) {
+ DPRINTF("CH=%d ERROR\n", td->channel[0]);
+ 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 = 1;
+ 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 = 1;
+ 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 = 1;
+ 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;
+ }
+
+ /* copy out control request */
+ usbd_copy_out(td->pc, 0, &req, sizeof(req));
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
+ (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
+ (1 << HCTSIZ_PKTCNT_SHIFT) |
+ (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), 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[0]), hcchar);
+
+ /* transfer data into FIFO */
+ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ DOTG_DFIFO(td->channel[0]), (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[0]),
+ (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), 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[0]), 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_PKTSTS_MASK) !=
+ GRXSTSRD_STP_DATA) {
+ if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+ GRXSTSRD_STP_COMPLETE || td->remainder != 0) {
+ /* release FIFO */
+ dwc_otg_common_rx_ack(sc);
+ goto not_complete;
+ }
+ /* release FIFO */
+ dwc_otg_common_rx_ack(sc);
+ return (0); /* complete */
+ }
+
+ if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+ GRXSTSRD_DPID_DATA0) {
+ /* 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);
+
+ 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;
+ }
+
+ /* read FIFO */
+ dwc_otg_read_fifo(sc, td->pc, 0, sizeof(req));
+
+ /* copy out control request */
+ usbd_copy_out(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), DIEPCTL_EPDIS);
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DOEPCTL_EPDIS);
+
+ /* reset IN endpoint buffer */
+ dwc_otg_tx_fifo_reset(sc,
+ GRSTCTL_TXFIFO(0) |
+ GRSTCTL_TXFFLSH);
+
+ /* acknowledge RX status */
+ dwc_otg_common_rx_ack(sc);
+ td->did_stall = 1;
+
+not_complete:
+ /* abort any ongoing transfer, before enabling again */
+ if (!td->did_stall) {
+ td->did_stall = 1;
+
+ DPRINTFN(5, "stalling IN and OUT direction\n");
+
+ temp = sc->sc_out_ctl[0];
+
+ /* 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);
+ }
+ 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)
+{
+ uint8_t frame_num = (uint8_t)sc->sc_last_frame_num;
+
+ if (td->ep_type == UE_ISOCHRONOUS) {
+ /* non TT isochronous traffic */
+ if (frame_num & (td->tmr_res - 1))
+ goto busy;
+ if ((frame_num ^ td->tmr_val) & td->tmr_res)
+ goto busy;
+ td->tmr_val = td->tmr_res + sc->sc_last_frame_num;
+ td->toggle = 0;
+ return (0);
+ } else if (td->ep_type == UE_INTERRUPT) {
+ if (!td->tt_scheduled)
+ goto busy;
+ td->tt_scheduled = 0;
+ return (0);
+ } else if (td->did_nak != 0) {
+ /* check if we should pause sending queries for 125us */
+ if (td->tmr_res == frame_num) {
+ /* wait a bit */
+ dwc_otg_enable_sof_irq(sc);
+ goto busy;
+ }
+ } else if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+ /* query for data one more time */
+ td->tmr_res = frame_num;
+ td->did_nak = 0;
+ return (0);
+busy:
+ return (1);
+}
+
+static uint8_t
+dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td,
+ uint8_t channel)
+{
+ uint32_t count;
+
+ /* check endpoint status */
+ if (sc->sc_last_rx_status == 0)
+ goto busy;
+
+ 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 endpoint */
+ if (td->ep_type == UE_ISOCHRONOUS) {
+ if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+ GRXSTSRD_DPID_DATA0) {
+ /* more data to be received */
+ td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
+ } else {
+ /* all data received */
+ td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
+ /* verify the packet byte count */
+ if (count != td->remainder) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ td->got_short = 1;
+ }
+ }
+ } 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;
+ }
+
+ /* read data from FIFO */
+ dwc_otg_read_fifo(sc, td->pc, td->offset, 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 = 0;
+ uint32_t hcchar;
+ uint8_t delta;
+ uint8_t channel;
+ uint8_t x;
+
+ for (x = 0; x != td->max_packet_count; x++) {
+ channel = td->channel[x];
+ if (channel >= DWC_OTG_MAX_CHANNELS)
+ continue;
+ 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, channel))
+ 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;
+ }
+ }
+
+ 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;
+ }
+ } else if (td->ep_type == UE_ISOCHRONOUS) {
+ goto complete;
+ }
+ td->did_nak = 1;
+ 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->tt_xactpos == HCSPLT_XACTPOS_BEGIN) {
+ goto complete;
+ } else {
+ /* get more packets */
+ goto busy;
+ }
+ } 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 = 1;
+ 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 (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;
+ }
+
+ /* set toggle, if any */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+
+ td->state = DWC_CHAN_ST_WAIT_ANE;
+
+ for (x = 0; x != td->max_packet_count; x++) {
+ channel = td->channel[x];
+
+ /* 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 &&
+ td->ep_type == UE_ISOCHRONOUS)
+ 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[0];
+
+ 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->ep_type == UE_ISOCHRONOUS)
+ 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 ||
+ (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
+ GRXSTSRD_STP_COMPLETE) {
+ 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 */
+ }
+
+ /* read data from FIFO */
+ dwc_otg_read_fifo(sc, td->pc, td->offset, 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:
+
+ /* enable SETUP and transfer complete interrupt */
+ if (td->ep_no == 0) {
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
+ DXEPTSIZ_SET_MULTI(3) |
+ 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)));
+ }
+ temp = sc->sc_out_ctl[td->ep_no];
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp |
+ DOEPCTL_EPENA | DOEPCTL_CNAK);
+
+ 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;
+ uint8_t x;
+
+ dwc_otg_host_dump_rx(sc, td);
+
+ /* check that last channel is complete */
+ channel = td->channel[td->npkt];
+
+ 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 = 1;
+ 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;
+ /* check if next response will be a NAK */
+ if (hcint & HCINT_NYET)
+ td->did_nak = 1;
+ else
+ 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 = 1;
+ 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 = 1;
+ 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;
+ goto complete;
+ 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;
+ }
+
+ /* set toggle, if any */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+
+ if (td->ep_type == UE_ISOCHRONOUS) {
+ /* 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 if (td->hcsplt != 0) {
+ td->hcsplt &= ~HCSPLT_COMPSPLT;
+ /* Wait for ACK/NAK/ERR from TT */
+ td->state = DWC_CHAN_ST_WAIT_S_ANE;
+ } else {
+ /* Wait for ACK/NAK/STALL from device */
+ td->state = DWC_CHAN_ST_WAIT_ANE;
+ }
+
+ td->tx_bytes = 0;
+
+ for (x = 0; x != td->max_packet_count; x++) {
+ uint32_t rem_bytes;
+
+ channel = td->channel[x];
+
+ /* send one packet at a time */
+ count = td->max_packet_size;
+ rem_bytes = td->remainder - td->tx_bytes;
+ if (rem_bytes < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = rem_bytes;
+ }
+ if (count == rem_bytes) {
+ /* last packet */
+ switch (x) {
+ case 0:
+ 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)));
+ break;
+ case 1:
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+ (count << HCTSIZ_XFERSIZE_SHIFT) |
+ (1 << HCTSIZ_PKTCNT_SHIFT) |
+ (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT));
+ break;
+ default:
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+ (count << HCTSIZ_XFERSIZE_SHIFT) |
+ (1 << HCTSIZ_PKTCNT_SHIFT) |
+ (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT));
+ break;
+ }
+ } else if (td->ep_type == UE_ISOCHRONOUS &&
+ td->max_packet_count > 1) {
+ /* ISOCHRONOUS multi packet */
+ 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 */
+ /* standard BULK/INTERRUPT/CONTROL packet */
+ 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 &&
+ td->ep_type == UE_ISOCHRONOUS)
+ 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) {
+ /* write data into FIFO */
+ dwc_otg_write_fifo(sc, td->pc, td->offset +
+ td->tx_bytes, DOTG_DFIFO(channel), count);
+ }
+
+ /* store number of bytes transmitted */
+ td->tx_bytes += count;
+
+ /* store last packet index */
+ td->npkt = x;
+
+ /* check for last packet */
+ if (count == rem_bytes)
+ break;
+ }
+ 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[0];
+
+ 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 &&
+ td->ep_type == UE_ISOCHRONOUS)
+ 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 &&
+ (temp & GRXSTSRD_PKTSTS_MASK) !=
+ GRXSTSRD_STP_COMPLETE) {
+
+ /* 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) {
+ /* write data into FIFO */
+ dwc_otg_write_fifo(sc, td->pc, td->offset,
+ DOTG_DFIFO(td->ep_no), count);
+
+ 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 ||
+ (temp & GRXSTSRD_PKTSTS_MASK) ==
+ GRXSTSRD_STP_COMPLETE) {
+ 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;
+
+ 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++;
+
+ /* 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 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;
+
+ 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);
+
+ 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;
+
+ 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;
+
+ 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) {
+ 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) {
+ 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 non-ISOCHRONOUS 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_ISOCHRONOUS)
+ 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 count;
+ uint32_t temp;
+ uint32_t haint;
+ uint8_t got_rx_status;
+ uint8_t x;
+
+ if (sc->sc_flags.status_device_mode == 0) {
+ /*
+ * Update host transfer schedule, so that new
+ * transfers can be issued:
+ */
+ dwc_otg_update_host_transfer_schedule_locked(sc);
+ }
+ count = 0;
+repeat:
+ if (++count == 16) {
+ /* give other interrupts a chance */
+ DPRINTF("Yield\n");
+ return;
+ }
+
+ /* get all host channel interrupts */
+ haint = DWC_OTG_READ_4(sc, DOTG_HAINT);
+ while (1) {
+ x = ffs(haint) - 1;
+ if (x >= sc->sc_host_ch_max)
+ break;
+ temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+ DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
+ temp &= ~HCINT_SOFTWARE_ONLY;
+ sc->sc_chan_state[x].hcint |= temp;
+ haint &= ~(1U << x);
+ }
+
+ 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_STP_COMPLETE &&
+ temp != GRXSTSRD_OUT_DATA) {
+ /* check for halted channel */
+ if (temp == GRXSTSRH_HALTED) {
+ ep_no = GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status);
+ sc->sc_chan_state[ep_no].wait_halted = 0;
+ DPRINTFN(5, "channel halt complete ch=%u\n", ep_no);
+ }
+ /* store bytes and FIFO offset */
+ sc->sc_current_rx_bytes = 0;
+ sc->sc_current_rx_fifo = 0;
+
+ /* acknowledge status */
+ 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);
+
+ /* store bytes and FIFO offset */
+ sc->sc_current_rx_bytes = (temp + 3) & ~3;
+ sc->sc_current_rx_fifo = DOTG_DFIFO(ep_no);
+
+ DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no);
+
+ /* 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);
+ }
+}
+
+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 FIFO empty interrupts */
+ if (status & sc->sc_irq_mask &
+ (GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP)) {
+ sc->sc_irq_mask &= ~(GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP);
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+ }
+ /* 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);
+ }
+ 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[0] = DWC_OTG_MAX_CHANNELS;
+ td->channel[1] = DWC_OTG_MAX_CHANNELS;
+ td->channel[2] = 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_res = 1;
+ td->tmr_val = sc->sc_last_frame_num;
+ if (td->hcchar & HCCHAR_EPDIR_IN)
+ td->tmr_val++;
+ } else {
+ td->tmr_val = 0;
+ td->tmr_res = (uint8_t)sc->sc_last_frame_num;
+ }
+ break;
+ case USB_SPEED_HIGH:
+ hcsplt = 0;
+ if (td->ep_type == UE_INTERRUPT) {
+ uint32_t ival;
+ hcchar |= ((xfer->max_packet_count & 3)
+ << HCCHAR_MC_SHIFT);
+ 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_res = 1 << usbd_xfer_get_fps_shift(xfer);
+ td->tmr_val = sc->sc_last_frame_num;
+ if (td->hcchar & HCCHAR_EPDIR_IN)
+ td->tmr_val += td->tmr_res;
+
+ } else {
+ td->tmr_val = 0;
+ td->tmr_res = (uint8_t)sc->sc_last_frame_num;
+ }
+ 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);
+
+ 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;
+ } else {
+ struct dwc_otg_td *td = xfer->td_transfer_cache;
+ if (td->ep_type == UE_ISOCHRONOUS &&
+ (td->hcchar & HCCHAR_EPDIR_IN) == 0) {
+ /*
+ * Need to start ISOCHRONOUS OUT transfer ASAP
+ * because execution is delayed by one 125us
+ * microframe:
+ */
+ 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);
+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);
+}
+
+static void
+dwc_otg_set_stall(struct usb_device *udev,
+ struct usb_endpoint *ep, uint8_t *did_stall)
+{
+ 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);
+}
+
+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_tx_fifo_reset(sc,
+ 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);
+ 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);
+}
+
+static const 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);
+}
+
+static const 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.
+ *------------------------------------------------------------------------*/
+
+static const 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,
+};
+
+static const 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) }
+
+static const 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;
+ /* range check */
+ if (td->max_packet_count == 0 || td->max_packet_count > 3)
+ td->max_packet_count = 1;
+ 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 &&
+ (edesc->wMaxPacketSize[1] & 0x18) != 0 &&
+ (edesc->bmAttributes & UE_XFERTYPE) != UE_ISOCHRONOUS) {
+ /* not supported */
+ DPRINTFN(-1, "Non-isochronous high bandwidth "
+ "endpoint not supported\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");
+}
+
+static const 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,
+ .xfer_stall = &dwc_otg_xfer_stall,
+ .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 100644
index 00000000..038b072a
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otg.h
@@ -0,0 +1,218 @@
+/* $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
+#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 ep_no;
+ uint8_t ep_type;
+ uint8_t channel[3];
+ 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_WAIT_ISOC 5
+ 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;
+ uint8_t did_nak: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_halted;
+ uint32_t hcint;
+};
+
+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_bounce_buffer[MAX(512 * DWC_OTG_MAX_TXP, 1024) / 4];
+
+ uint32_t sc_fifo_size;
+ 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_current_rx_bytes;
+ uint16_t sc_current_rx_fifo;
+
+ 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_otgreg.h b/freebsd/sys/dev/usb/controller/dwc_otgreg.h
new file mode 100644
index 00000000..8b9538ae
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/dwc_otgreg.h
@@ -0,0 +1,802 @@
+/* $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_GSNPSID_REV_2_80a 0x4f54280a /* RPi model B/RPi2 */
+#define DOTG_GSNPSID_REV_3_10a 0x4f54310a /* ODROID-C1 */
+#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_ */