git: b02419fc409c - stable/13 - Revert "mips: remove saf1761"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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 ***