git: b02419fc409c - stable/13 - Revert "mips: remove saf1761"

From: Warner Losh <imp_at_FreeBSD.org>
Date: Wed, 25 Jan 2023 04:46:00 UTC
The branch stable/13 has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=b02419fc409c4857ef263b30f823674c6f6d834e

commit b02419fc409c4857ef263b30f823674c6f6d834e
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2023-01-25 04:00:23 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-01-25 04:00:23 +0000

    Revert "mips: remove saf1761"
    
    Mips is still in stable/13, so this should not have been merged.
    
    This reverts commit 622ab2c4834068312b6cd33fd7ac961f31240350.
    
    Sponsored by:           Netflix
---
 stand/usb/usbcore.mk                      |    8 +
 sys/dev/usb/controller/saf1761_otg.c      | 3657 +++++++++++++++++++++++++++++
 sys/dev/usb/controller/saf1761_otg.h      |  175 ++
 sys/dev/usb/controller/saf1761_otg_boot.c |  140 ++
 sys/dev/usb/controller/saf1761_otg_fdt.c  |  269 +++
 sys/dev/usb/controller/saf1761_otg_reg.h  |  274 +++
 sys/modules/usb/Makefile                  |    9 +-
 sys/modules/usb/saf1761otg/Makefile       |   42 +
 8 files changed, 4573 insertions(+), 1 deletion(-)

diff --git a/stand/usb/usbcore.mk b/stand/usb/usbcore.mk
index ae80d06a24e7..7e0cc989228b 100644
--- a/stand/usb/usbcore.mk
+++ b/stand/usb/usbcore.mk
@@ -126,6 +126,14 @@ CFLAGS += -DUSB_PCI_PROBE_LIST="\"uss820dci\""
 KSRCS+=	uss820dci.c
 .endif
 
+.if defined(HAVE_SAF1761OTG)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"saf1761otg\""
+CFLAGS += -DUSB_PCI_MEMORY_ADDRESS=0x900000007f100000ULL
+CFLAGS += -DUSB_PCI_MEMORY_SIZE=0x40000U
+KSRCS+=	saf1761_otg.c
+KSRCS+=	saf1761_otg_boot.c
+.endif
+
 #
 # USB core and templates
 #
diff --git a/sys/dev/usb/controller/saf1761_otg.c b/sys/dev/usb/controller/saf1761_otg.c
new file mode 100644
index 000000000000..f5725a3cb48a
--- /dev/null
+++ b/sys/dev/usb/controller/saf1761_otg.c
@@ -0,0 +1,3657 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 SAF1761 series USB OTG
+ * controller.
+ *
+ * Datasheet is available from:
+ * http://www.nxp.com/products/automotive/multimedia/usb/SAF1761BE.html
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/libkern.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define	USB_DEBUG_VAR saf1761_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/saf1761_otg.h>
+#include <dev/usb/controller/saf1761_otg_reg.h>
+
+#define	SAF1761_OTG_BUS2SC(bus) \
+   ((struct saf1761_otg_softc *)(((uint8_t *)(bus)) - \
+    ((uint8_t *)&(((struct saf1761_otg_softc *)0)->sc_bus))))
+
+#define	SAF1761_OTG_PC2UDEV(pc) \
+   (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev)
+
+#define	SAF1761_DCINTERRUPT_THREAD_IRQ			\
+  (SOTG_DCINTERRUPT_IEVBUS | SOTG_DCINTERRUPT_IEBRST |	\
+  SOTG_DCINTERRUPT_IERESM | SOTG_DCINTERRUPT_IESUSP)
+
+#ifdef USB_DEBUG
+static int saf1761_otg_debug = 0;
+static int saf1761_otg_forcefs = 0;
+
+static 
+SYSCTL_NODE(_hw_usb, OID_AUTO, saf1761_otg, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+    "USB SAF1761 DCI");
+
+SYSCTL_INT(_hw_usb_saf1761_otg, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &saf1761_otg_debug, 0, "SAF1761 DCI debug level");
+SYSCTL_INT(_hw_usb_saf1761_otg, OID_AUTO, forcefs, CTLFLAG_RWTUN,
+    &saf1761_otg_forcefs, 0, "SAF1761 DCI force FULL speed");
+#endif
+
+#define	SAF1761_OTG_INTR_ENDPT 1
+
+/* prototypes */
+
+static const struct usb_bus_methods saf1761_otg_bus_methods;
+static const struct usb_pipe_methods saf1761_otg_non_isoc_methods;
+static const struct usb_pipe_methods saf1761_otg_device_isoc_methods;
+static const struct usb_pipe_methods saf1761_otg_host_isoc_methods;
+
+static saf1761_otg_cmd_t saf1761_host_setup_tx;
+static saf1761_otg_cmd_t saf1761_host_bulk_data_rx;
+static saf1761_otg_cmd_t saf1761_host_bulk_data_tx;
+static saf1761_otg_cmd_t saf1761_host_intr_data_rx;
+static saf1761_otg_cmd_t saf1761_host_intr_data_tx;
+static saf1761_otg_cmd_t saf1761_host_isoc_data_rx;
+static saf1761_otg_cmd_t saf1761_host_isoc_data_tx;
+static saf1761_otg_cmd_t saf1761_device_setup_rx;
+static saf1761_otg_cmd_t saf1761_device_data_rx;
+static saf1761_otg_cmd_t saf1761_device_data_tx;
+static saf1761_otg_cmd_t saf1761_device_data_tx_sync;
+static void saf1761_otg_device_done(struct usb_xfer *, usb_error_t);
+static void saf1761_otg_do_poll(struct usb_bus *);
+static void saf1761_otg_standard_done(struct usb_xfer *);
+static void saf1761_otg_intr_set(struct usb_xfer *, uint8_t);
+static void saf1761_otg_root_intr(struct saf1761_otg_softc *);
+static void saf1761_otg_enable_psof(struct saf1761_otg_softc *, uint8_t);
+
+/*
+ * Here is a list of what the SAF1761 chip can support. The main
+ * limitation is that the sum of the buffer sizes must be less than
+ * 8192 bytes.
+ */
+static const struct usb_hw_ep_profile saf1761_otg_ep_profile[] = {
+	[0] = {
+		.max_in_frame_size = 64,
+		.max_out_frame_size = 64,
+		.is_simplex = 0,
+		.support_control = 1,
+	},
+	[1] = {
+		.max_in_frame_size = SOTG_HS_MAX_PACKET_SIZE,
+		.max_out_frame_size = SOTG_HS_MAX_PACKET_SIZE,
+		.is_simplex = 0,
+		.support_interrupt = 1,
+		.support_bulk = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+};
+
+static void
+saf1761_otg_get_hw_ep_profile(struct usb_device *udev,
+    const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+	if (ep_addr == 0) {
+		*ppf = saf1761_otg_ep_profile + 0;
+	} else if (ep_addr < 8) {
+		*ppf = saf1761_otg_ep_profile + 1;
+	} else {
+		*ppf = NULL;
+	}
+}
+
+static void
+saf1761_otg_pull_up(struct saf1761_otg_softc *sc)
+{
+	/* activate pullup on D+, if possible */
+
+	if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) {
+		DPRINTF("\n");
+
+		sc->sc_flags.d_pulled_up = 1;
+	}
+}
+
+static void
+saf1761_otg_pull_down(struct saf1761_otg_softc *sc)
+{
+	/* release pullup on D+, if possible */
+
+	if (sc->sc_flags.d_pulled_up) {
+		DPRINTF("\n");
+
+		sc->sc_flags.d_pulled_up = 0;
+	}
+}
+
+static void
+saf1761_otg_wakeup_peer(struct saf1761_otg_softc *sc)
+{
+	uint16_t temp;
+
+	if (!(sc->sc_flags.status_suspend))
+		return;
+
+	DPRINTFN(5, "\n");
+
+	temp = SAF1761_READ_LE_4(sc, SOTG_MODE);
+	SAF1761_WRITE_LE_4(sc, SOTG_MODE, temp | SOTG_MODE_SNDRSU);
+	SAF1761_WRITE_LE_4(sc, SOTG_MODE, temp & ~SOTG_MODE_SNDRSU);
+
+	/* Wait 8ms for remote wakeup to complete. */
+	usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+}
+
+static uint8_t
+saf1761_host_channel_alloc(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t map;
+	int x;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX)
+		return (0);
+
+	/* check if device is suspended */
+	if (SAF1761_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0)
+		return (1);		/* busy - cannot transfer data */
+
+	switch (td->ep_type) {
+	case UE_INTERRUPT:
+		map = ~(sc->sc_host_intr_map |
+		    sc->sc_host_intr_busy_map[0] |
+		    sc->sc_host_intr_busy_map[1]);
+		/* find first set bit */
+		x = ffs(map) - 1;
+		if (x < 0 || x > 31)
+			break;
+		sc->sc_host_intr_map |= (1U << x);
+		td->channel = 32 + x;
+		return (0);
+	case UE_ISOCHRONOUS:
+		map = ~(sc->sc_host_isoc_map |
+		    sc->sc_host_isoc_busy_map[0] |
+		    sc->sc_host_isoc_busy_map[1]);
+		/* find first set bit */
+		x = ffs(map) - 1;
+		if (x < 0 || x > 31)
+			break;
+		sc->sc_host_isoc_map |= (1U << x);
+		td->channel = x;
+		return (0);
+	default:
+		map = ~(sc->sc_host_async_map |
+		    sc->sc_host_async_busy_map[0] |
+		    sc->sc_host_async_busy_map[1]);
+		/* find first set bit */
+		x = ffs(map) - 1;
+		if (x < 0 || x > 31)
+			break;
+		sc->sc_host_async_map |= (1U << x);
+		td->channel = 64 + x;
+		return (0);
+	}
+	return (1);
+}
+
+static void
+saf1761_host_channel_free(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t x;
+
+	if (td->channel >= SOTG_HOST_CHANNEL_MAX)
+		return;
+
+	switch (td->ep_type) {
+	case UE_INTERRUPT:
+		x = td->channel - 32;
+		td->channel = SOTG_HOST_CHANNEL_MAX;
+		sc->sc_host_intr_map &= ~(1U << x);
+		sc->sc_host_intr_suspend_map &= ~(1U << x);
+		sc->sc_host_intr_busy_map[0] |= (1U << x);
+		SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
+		    (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+		break;
+	case UE_ISOCHRONOUS:
+		x = td->channel;
+		td->channel = SOTG_HOST_CHANNEL_MAX;
+		sc->sc_host_isoc_map &= ~(1U << x);
+		sc->sc_host_isoc_suspend_map &= ~(1U << x);
+		sc->sc_host_isoc_busy_map[0] |= (1U << x);
+		SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
+		    (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
+		break;
+	default:
+		x = td->channel - 64;
+		td->channel = SOTG_HOST_CHANNEL_MAX;
+		sc->sc_host_async_map &= ~(1U << x);
+		sc->sc_host_async_suspend_map &= ~(1U << x);
+		sc->sc_host_async_busy_map[0] |= (1U << x);
+		SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+		    (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+		break;
+	}
+	saf1761_otg_enable_psof(sc, 1);
+}
+
+static uint32_t
+saf1761_peek_host_status_le_4(struct saf1761_otg_softc *sc, uint32_t offset)
+{
+	uint32_t x = 0;
+	while (1) {
+		uint32_t retval;
+
+		SAF1761_WRITE_LE_4(sc, SOTG_MEMORY_REG, offset);
+		SAF1761_90NS_DELAY(sc);	/* read prefetch time is 90ns */
+		retval = SAF1761_READ_LE_4(sc, offset);
+		if (retval != 0)
+			return (retval);
+		if (++x == 8) {
+			DPRINTF("STAUS is zero at offset 0x%x\n", offset);
+			break;
+		}
+	}
+	return (0);
+}
+
+static void
+saf1761_read_host_memory(struct saf1761_otg_softc *sc,
+    struct saf1761_otg_td *td, uint32_t len)
+{
+	struct usb_page_search buf_res;
+	uint32_t offset;
+	uint32_t count;
+
+	if (len == 0)
+		return;
+
+	offset = SOTG_DATA_ADDR(td->channel);
+	SAF1761_WRITE_LE_4(sc, SOTG_MEMORY_REG, offset);
+	SAF1761_90NS_DELAY(sc);	/* read prefetch time is 90ns */
+
+	/* optimised read first */
+	while (len > 0) {
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > len)
+			buf_res.length = len;
+
+		/* check buffer alignment */
+		if (((uintptr_t)buf_res.buffer) & 3)
+			break;
+
+		count = buf_res.length & ~3;
+		if (count == 0)
+			break;
+
+		bus_space_read_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+		    offset, buf_res.buffer, count / 4);
+
+		len -= count;
+		offset += count;
+
+		/* update remainder and offset */
+		td->remainder -= count;
+		td->offset += count;
+	}
+
+	if (len > 0) {
+		/* use bounce buffer */
+		bus_space_read_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+		    offset, sc->sc_bounce_buffer, (len + 3) / 4);
+		usbd_copy_in(td->pc, td->offset,
+		    sc->sc_bounce_buffer, len);
+
+		/* update remainder and offset */
+		td->remainder -= len;
+		td->offset += len;
+	}
+}
+
+static void
+saf1761_write_host_memory(struct saf1761_otg_softc *sc,
+    struct saf1761_otg_td *td, uint32_t len)
+{
+	struct usb_page_search buf_res;
+	uint32_t offset;
+	uint32_t count;
+
+	if (len == 0)
+		return;
+
+	offset = SOTG_DATA_ADDR(td->channel);
+
+	/* optimised write first */
+	while (len > 0) {
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > len)
+			buf_res.length = len;
+
+		/* check buffer alignment */
+		if (((uintptr_t)buf_res.buffer) & 3)
+			break;
+
+		count = buf_res.length & ~3;
+		if (count == 0)
+			break;
+
+		bus_space_write_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+		    offset, buf_res.buffer, count / 4);
+
+		len -= count;
+		offset += count;
+
+		/* update remainder and offset */
+		td->remainder -= count;
+		td->offset += count;
+	}
+	if (len > 0) {
+		/* use bounce buffer */
+		usbd_copy_out(td->pc, td->offset, sc->sc_bounce_buffer, len);
+		bus_space_write_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+		    offset, sc->sc_bounce_buffer, (len + 3) / 4);
+
+		/* update remainder and offset */
+		td->remainder -= len;
+		td->offset += len;
+	}
+}
+
+static uint8_t
+saf1761_host_setup_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t pdt_addr;
+	uint32_t status;
+	uint32_t count;
+	uint32_t temp;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+		pdt_addr = SOTG_PTD(td->channel);
+
+		status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
+
+		DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+		if (status & SOTG_PTD_DW3_ACTIVE) {
+			goto busy;
+		} else if (status & SOTG_PTD_DW3_HALTED) {
+			td->error_any = 1;
+		}
+		goto complete;
+	}
+	if (saf1761_host_channel_alloc(sc, td))
+		goto busy;
+
+	count = 8;
+
+	if (count != td->remainder) {
+		td->error_any = 1;
+		goto complete;
+	}
+
+	saf1761_write_host_memory(sc, td, count);
+
+	pdt_addr = SOTG_PTD(td->channel);
+
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, 0);
+
+	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+	    
+	temp = SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+	temp = td->dw1_value | (2 << 10) /* SETUP PID */ | (td->ep_index >> 1);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+	temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+	    (td->max_packet_size << 18) /* wMaxPacketSize */ |
+	    (count << 3) /* transfer count */ |
+	    SOTG_PTD_DW0_VALID;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
+	/* activate PTD */
+	SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+	    (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+
+	td->toggle = 1;
+busy:
+	return (1);	/* busy */
+complete:
+	saf1761_host_channel_free(sc, td);
+	return (0);	/* complete */
+}
+
+static uint8_t
+saf1761_host_bulk_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t pdt_addr;
+	uint32_t temp;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+		uint32_t status;
+		uint32_t count;
+		uint8_t got_short;
+
+		pdt_addr = SOTG_PTD(td->channel);
+
+		status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
+
+		DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+		if (status & SOTG_PTD_DW3_ACTIVE) {
+			temp = saf1761_peek_host_status_le_4(sc,
+			    pdt_addr + SOTG_PTD_DW0);
+			if (temp & SOTG_PTD_DW0_VALID) {
+				goto busy;
+			} else {
+				status = saf1761_peek_host_status_le_4(sc,
+				    pdt_addr + SOTG_PTD_DW3);
+
+				/* check if still active */
+				if (status & SOTG_PTD_DW3_ACTIVE) {
+					saf1761_host_channel_free(sc, td);
+					goto retry;
+				} else if (status & SOTG_PTD_DW3_HALTED) {
+					if (!(status & SOTG_PTD_DW3_ERRORS))
+						td->error_stall = 1;
+					td->error_any = 1;
+					goto complete;
+				}
+			}
+		} else if (status & SOTG_PTD_DW3_HALTED) {
+			if (!(status & SOTG_PTD_DW3_ERRORS))
+				td->error_stall = 1;
+			td->error_any = 1;
+			goto complete;
+		}
+		if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT)
+			count = (status & SOTG_PTD_DW3_XFER_COUNT_SPLIT);
+		else
+			count = (status & SOTG_PTD_DW3_XFER_COUNT_HS);
+		got_short = 0;
+
+		/* 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;
+				goto complete;
+			}
+		}
+		td->toggle ^= 1;
+
+		/* verify the packet byte count */
+		if (count > td->remainder) {
+			/* invalid USB packet */
+			td->error_any = 1;
+			goto complete;
+		}
+
+		saf1761_read_host_memory(sc, td, count);
+
+		/* check if we are complete */
+		if ((td->remainder == 0) || got_short) {
+			if (td->short_pkt)
+				goto complete;
+			/* else need to receive a zero length packet */
+		}
+		saf1761_host_channel_free(sc, td);
+	}
+retry:
+	if (saf1761_host_channel_alloc(sc, td))
+		goto busy;
+
+	/* set toggle, if any */
+	if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+
+	/* receive one more packet */
+
+	pdt_addr = SOTG_PTD(td->channel);
+
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, 0);
+
+	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) |
+	    SOTG_PTD_DW3_CERR_2;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+
+	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+	temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+	temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+	    (td->max_packet_size << 18) /* wMaxPacketSize */ |
+	    (td->max_packet_size << 3) /* transfer count */ |
+	    SOTG_PTD_DW0_VALID;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
+	/* activate PTD */
+	SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+	    (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+busy:
+	return (1);	/* busy */
+complete:
+	saf1761_host_channel_free(sc, td);
+	return (0);	/* complete */
+}
+
+static uint8_t
+saf1761_host_bulk_data_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t pdt_addr;
+	uint32_t temp;
+	uint32_t count;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+		uint32_t status;
+
+		pdt_addr = SOTG_PTD(td->channel);
+
+		status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
+
+		DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+		if (status & SOTG_PTD_DW3_ACTIVE) {
+			goto busy;
+		} else if (status & SOTG_PTD_DW3_HALTED) {
+			if (!(status & SOTG_PTD_DW3_ERRORS))
+				td->error_stall = 1;
+			td->error_any = 1;
+			goto complete;
+		}
+		/* check remainder */
+		if (td->remainder == 0) {
+			if (td->short_pkt)
+				goto complete;
+			/* else we need to transmit a short packet */
+		}
+		saf1761_host_channel_free(sc, td);
+	}
+	if (saf1761_host_channel_alloc(sc, td))
+		goto busy;
+
+	count = td->max_packet_size;
+	if (td->remainder < count) {
+		/* we have a short packet */
+		td->short_pkt = 1;
+		count = td->remainder;
+	}
+
+	saf1761_write_host_memory(sc, td, count);
+
+	/* set toggle, if any */
+	if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+
+	/* send one more packet */
+
+	pdt_addr = SOTG_PTD(td->channel);
+
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, 0);
+
+	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) |
+	    SOTG_PTD_DW3_CERR_2;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+
+	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+	temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+	temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+	    (td->max_packet_size << 18) /* wMaxPacketSize */ |
+	    (count << 3) /* transfer count */ |
+	    SOTG_PTD_DW0_VALID;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
+	/* activate PTD */
+	SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+	    (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+
+	td->toggle ^= 1;
+busy:
+	return (1);	/* busy */
+complete:
+	saf1761_host_channel_free(sc, td);
+	return (0);	/* complete */
+}
+
+static uint8_t
+saf1761_host_intr_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t pdt_addr;
+	uint32_t temp;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+		uint32_t status;
+		uint32_t count;
+		uint8_t got_short;
+
+		pdt_addr = SOTG_PTD(td->channel);
+
+		status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
+
+		DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+		if (status & SOTG_PTD_DW3_ACTIVE) {
+			goto busy;
+		} else if (status & SOTG_PTD_DW3_HALTED) {
+			if (!(status & SOTG_PTD_DW3_ERRORS))
+				td->error_stall = 1;
+			td->error_any = 1;
+			goto complete;
+		}
+		if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT)
+			count = (status & SOTG_PTD_DW3_XFER_COUNT_SPLIT);
+		else
+			count = (status & SOTG_PTD_DW3_XFER_COUNT_HS);
+		got_short = 0;
+
+		/* 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;
+				goto complete;
+			}
+		}
+		td->toggle ^= 1;
+
+		/* verify the packet byte count */
+		if (count > td->remainder) {
+			/* invalid USB packet */
+			td->error_any = 1;
+			goto complete;
+		}
+
+		saf1761_read_host_memory(sc, td, count);
+
+		/* check if we are complete */
+		if ((td->remainder == 0) || got_short) {
+			if (td->short_pkt)
+				goto complete;
+			/* else need to receive a zero length packet */
+		}
+		saf1761_host_channel_free(sc, td);
+	}
+	if (saf1761_host_channel_alloc(sc, td))
+		goto busy;
+
+	/* set toggle, if any */
+	if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+
+	/* receive one more packet */
+
+	pdt_addr = SOTG_PTD(td->channel);
+
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+
+	if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT) {
+		temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	} else {
+		temp = 0;
+	}
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
+
+	temp = (1U << td->uframe);		/* start mask or start split */
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
+
+	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+
+	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
+	    (td->interval & 0xF8);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+	temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+	temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+	    (td->max_packet_size << 18) /* wMaxPacketSize */ |
+	    (td->max_packet_size << 3) /* transfer count */ |
+	    SOTG_PTD_DW0_VALID;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
+	/* activate PTD */
+	SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
+	    (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+busy:
+	return (1);	/* busy */
+complete:
+	saf1761_host_channel_free(sc, td);
+	return (0);	/* complete */
+}
+
+static uint8_t
+saf1761_host_intr_data_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t pdt_addr;
+	uint32_t temp;
+	uint32_t count;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+		uint32_t status;
+
+		pdt_addr = SOTG_PTD(td->channel);
+
+		status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
+
+		DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+		if (status & SOTG_PTD_DW3_ACTIVE) {
+			goto busy;
+		} else if (status & SOTG_PTD_DW3_HALTED) {
+			if (!(status & SOTG_PTD_DW3_ERRORS))
+				td->error_stall = 1;
+			td->error_any = 1;
+			goto complete;
+		}
+
+		/* check remainder */
+		if (td->remainder == 0) {
+			if (td->short_pkt)
+				goto complete;
+			/* else we need to transmit a short packet */
+		}
+		saf1761_host_channel_free(sc, td);
+	}
+	if (saf1761_host_channel_alloc(sc, td))
+		goto busy;
+
+	count = td->max_packet_size;
+	if (td->remainder < count) {
+		/* we have a short packet */
+		td->short_pkt = 1;
+		count = td->remainder;
+	}
+
+	saf1761_write_host_memory(sc, td, count);
+
+	/* set toggle, if any */
+	if (td->set_toggle) {
+		td->set_toggle = 0;
+		td->toggle = 1;
+	}
+
+	/* send one more packet */
+
+	pdt_addr = SOTG_PTD(td->channel);
+
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+
+	if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT) {
+		temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	} else {
+		temp = 0;
+	}
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
+
+	temp = (1U << td->uframe);		/* start mask or start split */
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
+
+	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+
+	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
+	    (td->interval & 0xF8);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+	temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+	temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+	    (td->max_packet_size << 18) /* wMaxPacketSize */ |
+	    (count << 3) /* transfer count */ |
+	    SOTG_PTD_DW0_VALID;
+	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
+	/* activate PTD */
+	SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
+	    (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+
+	td->toggle ^= 1;
+busy:
+	return (1);	/* busy */
+complete:
+	saf1761_host_channel_free(sc, td);
+	return (0);	/* complete */
+}
+
+static uint8_t
+saf1761_host_isoc_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+	uint32_t pdt_addr;
+	uint32_t temp;
+
+	if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+		uint32_t status;
+		uint32_t count;
+
+		pdt_addr = SOTG_PTD(td->channel);
+
+		status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
*** 3690 LINES SKIPPED ***