1dd03e19cSHans Petter Selasky /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4a529288dSHans Petter Selasky * Copyright (c) 2015 Daisuke Aoyama. All rights reserved. 5ce842cecSHans Petter Selasky * Copyright (c) 2012-2015 Hans Petter Selasky. All rights reserved. 69cfd0731SHans Petter Selasky * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved. 7dd03e19cSHans Petter Selasky * 8dd03e19cSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 9dd03e19cSHans Petter Selasky * modification, are permitted provided that the following conditions 10dd03e19cSHans Petter Selasky * are met: 11dd03e19cSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 12dd03e19cSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 13dd03e19cSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 14dd03e19cSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 15dd03e19cSHans Petter Selasky * documentation and/or other materials provided with the distribution. 16dd03e19cSHans Petter Selasky * 17dd03e19cSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18dd03e19cSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19dd03e19cSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20dd03e19cSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21dd03e19cSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22dd03e19cSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23dd03e19cSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24dd03e19cSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25dd03e19cSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26dd03e19cSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27dd03e19cSHans Petter Selasky * SUCH DAMAGE. 28dd03e19cSHans Petter Selasky */ 29dd03e19cSHans Petter Selasky 30dd03e19cSHans Petter Selasky /* 31dd03e19cSHans Petter Selasky * This file contains the driver for the DesignWare series USB 2.0 OTG 329cfd0731SHans Petter Selasky * Controller. 33dd03e19cSHans Petter Selasky */ 34dd03e19cSHans Petter Selasky 35dd03e19cSHans Petter Selasky /* 36dd03e19cSHans Petter Selasky * LIMITATION: Drivers must be bound to all OUT endpoints in the 37dd03e19cSHans Petter Selasky * active configuration for this driver to work properly. Blocking any 38dd03e19cSHans Petter Selasky * OUT endpoint will block all OUT endpoints including the control 39dd03e19cSHans Petter Selasky * endpoint. Usually this is not a problem. 40dd03e19cSHans Petter Selasky */ 41dd03e19cSHans Petter Selasky 42dd03e19cSHans Petter Selasky /* 43dd03e19cSHans Petter Selasky * NOTE: Writing to non-existing registers appears to cause an 44dd03e19cSHans Petter Selasky * internal reset. 45dd03e19cSHans Petter Selasky */ 46dd03e19cSHans Petter Selasky 47d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 48d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 49d2b99310SHans Petter Selasky #else 50dd03e19cSHans Petter Selasky #include <sys/stdint.h> 51dd03e19cSHans Petter Selasky #include <sys/stddef.h> 52dd03e19cSHans Petter Selasky #include <sys/param.h> 53dd03e19cSHans Petter Selasky #include <sys/queue.h> 54dd03e19cSHans Petter Selasky #include <sys/types.h> 55dd03e19cSHans Petter Selasky #include <sys/systm.h> 56dd03e19cSHans Petter Selasky #include <sys/kernel.h> 57dd03e19cSHans Petter Selasky #include <sys/bus.h> 58dd03e19cSHans Petter Selasky #include <sys/module.h> 59dd03e19cSHans Petter Selasky #include <sys/lock.h> 60dd03e19cSHans Petter Selasky #include <sys/mutex.h> 61dd03e19cSHans Petter Selasky #include <sys/condvar.h> 62dd03e19cSHans Petter Selasky #include <sys/sysctl.h> 63dd03e19cSHans Petter Selasky #include <sys/sx.h> 64dd03e19cSHans Petter Selasky #include <sys/unistd.h> 65dd03e19cSHans Petter Selasky #include <sys/callout.h> 66dd03e19cSHans Petter Selasky #include <sys/malloc.h> 67dd03e19cSHans Petter Selasky #include <sys/priv.h> 68518da7acSAndrew Turner #include <sys/rman.h> 69dd03e19cSHans Petter Selasky 70dd03e19cSHans Petter Selasky #include <dev/usb/usb.h> 71dd03e19cSHans Petter Selasky #include <dev/usb/usbdi.h> 72dd03e19cSHans Petter Selasky 73dd03e19cSHans Petter Selasky #define USB_DEBUG_VAR dwc_otg_debug 74dd03e19cSHans Petter Selasky 75dd03e19cSHans Petter Selasky #include <dev/usb/usb_core.h> 76dd03e19cSHans Petter Selasky #include <dev/usb/usb_debug.h> 77dd03e19cSHans Petter Selasky #include <dev/usb/usb_busdma.h> 78dd03e19cSHans Petter Selasky #include <dev/usb/usb_process.h> 79dd03e19cSHans Petter Selasky #include <dev/usb/usb_transfer.h> 80dd03e19cSHans Petter Selasky #include <dev/usb/usb_device.h> 81dd03e19cSHans Petter Selasky #include <dev/usb/usb_hub.h> 82dd03e19cSHans Petter Selasky #include <dev/usb/usb_util.h> 83dd03e19cSHans Petter Selasky 84dd03e19cSHans Petter Selasky #include <dev/usb/usb_controller.h> 85dd03e19cSHans Petter Selasky #include <dev/usb/usb_bus.h> 86d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 87dd03e19cSHans Petter Selasky 88dd03e19cSHans Petter Selasky #include <dev/usb/controller/dwc_otg.h> 89710764f7SHans Petter Selasky #include <dev/usb/controller/dwc_otgreg.h> 90dd03e19cSHans Petter Selasky 91dd03e19cSHans Petter Selasky #define DWC_OTG_BUS2SC(bus) \ 92a3cea156SAndrew Turner __containerof(bus, struct dwc_otg_softc, sc_bus) 93dd03e19cSHans Petter Selasky 94db4300daSHans Petter Selasky #define DWC_OTG_PC2UDEV(pc) \ 95db4300daSHans Petter Selasky (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev) 96db4300daSHans Petter Selasky 972fe7ad87SHans Petter Selasky #define DWC_OTG_MSK_GINT_THREAD_IRQ \ 982fe7ad87SHans Petter Selasky (GINTSTS_USBRST | GINTSTS_ENUMDONE | GINTSTS_PRTINT | \ 992fe7ad87SHans Petter Selasky GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTMSK_OTGINTMSK | \ 1002fe7ad87SHans Petter Selasky GINTSTS_SESSREQINT) 1012fe7ad87SHans Petter Selasky 102f5757453SHans Petter Selasky #ifndef DWC_OTG_PHY_DEFAULT 103f5757453SHans Petter Selasky #define DWC_OTG_PHY_DEFAULT DWC_OTG_PHY_ULPI 104f5757453SHans Petter Selasky #endif 105f5757453SHans Petter Selasky 106f5757453SHans Petter Selasky static int dwc_otg_phy_type = DWC_OTG_PHY_DEFAULT; 107391d3f18SHans Petter Selasky 108f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 109f8d2b1f3SPawel Biernacki "USB DWC OTG"); 110f5757453SHans Petter Selasky SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, phy_type, CTLFLAG_RDTUN, 111cec8009cSRuslan Bukin &dwc_otg_phy_type, 0, "DWC OTG PHY TYPE - 0/1/2/3 - ULPI/HSIC/INTERNAL/UTMI+"); 112dd03e19cSHans Petter Selasky 113dd03e19cSHans Petter Selasky #ifdef USB_DEBUG 114cec8009cSRuslan Bukin static int dwc_otg_debug = 0; 115dd03e19cSHans Petter Selasky 116af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RWTUN, 117dd03e19cSHans Petter Selasky &dwc_otg_debug, 0, "DWC OTG debug level"); 118dd03e19cSHans Petter Selasky #endif 119dd03e19cSHans Petter Selasky 120dd03e19cSHans Petter Selasky #define DWC_OTG_INTR_ENDPT 1 121dd03e19cSHans Petter Selasky 122dd03e19cSHans Petter Selasky /* prototypes */ 123dd03e19cSHans Petter Selasky 124e892b3feSHans Petter Selasky static const struct usb_bus_methods dwc_otg_bus_methods; 125e892b3feSHans Petter Selasky static const struct usb_pipe_methods dwc_otg_device_non_isoc_methods; 126e892b3feSHans Petter Selasky static const struct usb_pipe_methods dwc_otg_device_isoc_methods; 127dd03e19cSHans Petter Selasky 128dd03e19cSHans Petter Selasky static dwc_otg_cmd_t dwc_otg_setup_rx; 129dd03e19cSHans Petter Selasky static dwc_otg_cmd_t dwc_otg_data_rx; 130dd03e19cSHans Petter Selasky static dwc_otg_cmd_t dwc_otg_data_tx; 131dd03e19cSHans Petter Selasky static dwc_otg_cmd_t dwc_otg_data_tx_sync; 1329cfd0731SHans Petter Selasky 1339cfd0731SHans Petter Selasky static dwc_otg_cmd_t dwc_otg_host_setup_tx; 1349cfd0731SHans Petter Selasky static dwc_otg_cmd_t dwc_otg_host_data_tx; 1359cfd0731SHans Petter Selasky static dwc_otg_cmd_t dwc_otg_host_data_rx; 1369cfd0731SHans Petter Selasky 137dd03e19cSHans Petter Selasky static void dwc_otg_device_done(struct usb_xfer *, usb_error_t); 138dd03e19cSHans Petter Selasky static void dwc_otg_do_poll(struct usb_bus *); 139dd03e19cSHans Petter Selasky static void dwc_otg_standard_done(struct usb_xfer *); 140db4300daSHans Petter Selasky static void dwc_otg_root_intr(struct dwc_otg_softc *); 1412994bac4SHans Petter Selasky static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *); 142dd03e19cSHans Petter Selasky 143dd03e19cSHans Petter Selasky /* 144dd03e19cSHans Petter Selasky * Here is a configuration that the chip supports. 145dd03e19cSHans Petter Selasky */ 146dd03e19cSHans Petter Selasky static const struct usb_hw_ep_profile dwc_otg_ep_profile[1] = { 147dd03e19cSHans Petter Selasky [0] = { 148dd03e19cSHans Petter Selasky .max_in_frame_size = 64,/* fixed */ 149dd03e19cSHans Petter Selasky .max_out_frame_size = 64, /* fixed */ 150dd03e19cSHans Petter Selasky .is_simplex = 1, 151dd03e19cSHans Petter Selasky .support_control = 1, 152dd03e19cSHans Petter Selasky } 153dd03e19cSHans Petter Selasky }; 154dd03e19cSHans Petter Selasky 155dd03e19cSHans Petter Selasky static void 156dd03e19cSHans Petter Selasky dwc_otg_get_hw_ep_profile(struct usb_device *udev, 157dd03e19cSHans Petter Selasky const struct usb_hw_ep_profile **ppf, uint8_t ep_addr) 158dd03e19cSHans Petter Selasky { 159dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc; 160dd03e19cSHans Petter Selasky 161dd03e19cSHans Petter Selasky sc = DWC_OTG_BUS2SC(udev->bus); 162dd03e19cSHans Petter Selasky 163dd03e19cSHans Petter Selasky if (ep_addr < sc->sc_dev_ep_max) 164dd03e19cSHans Petter Selasky *ppf = &sc->sc_hw_ep_profile[ep_addr].usb; 165dd03e19cSHans Petter Selasky else 166dd03e19cSHans Petter Selasky *ppf = NULL; 167dd03e19cSHans Petter Selasky } 168dd03e19cSHans Petter Selasky 169b4df5b00SHans Petter Selasky static void 170c2472ff8SHans Petter Selasky dwc_otg_write_fifo(struct dwc_otg_softc *sc, struct usb_page_cache *pc, 171c2472ff8SHans Petter Selasky uint32_t offset, uint32_t fifo, uint32_t count) 172c2472ff8SHans Petter Selasky { 173c2472ff8SHans Petter Selasky uint32_t temp; 174c2472ff8SHans Petter Selasky 175c2472ff8SHans Petter Selasky /* round down length to nearest 4-bytes */ 176c2472ff8SHans Petter Selasky temp = count & ~3; 177c2472ff8SHans Petter Selasky 178c2472ff8SHans Petter Selasky /* check if we can write the data directly */ 179c2472ff8SHans Petter Selasky if (temp != 0 && usb_pc_buffer_is_aligned(pc, offset, temp, 3)) { 180c2472ff8SHans Petter Selasky struct usb_page_search buf_res; 181c2472ff8SHans Petter Selasky 182c2472ff8SHans Petter Selasky /* pre-subtract length */ 183c2472ff8SHans Petter Selasky count -= temp; 184c2472ff8SHans Petter Selasky 185c2472ff8SHans Petter Selasky /* iterate buffer list */ 186c2472ff8SHans Petter Selasky do { 187c2472ff8SHans Petter Selasky /* get current buffer pointer */ 188c2472ff8SHans Petter Selasky usbd_get_page(pc, offset, &buf_res); 189c2472ff8SHans Petter Selasky 190c2472ff8SHans Petter Selasky if (buf_res.length > temp) 191c2472ff8SHans Petter Selasky buf_res.length = temp; 192c2472ff8SHans Petter Selasky 193c2472ff8SHans Petter Selasky /* transfer data into FIFO */ 194c2472ff8SHans Petter Selasky bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, 195c2472ff8SHans Petter Selasky fifo, buf_res.buffer, buf_res.length / 4); 196c2472ff8SHans Petter Selasky 197c2472ff8SHans Petter Selasky offset += buf_res.length; 198c2472ff8SHans Petter Selasky fifo += buf_res.length; 199c2472ff8SHans Petter Selasky temp -= buf_res.length; 200c2472ff8SHans Petter Selasky } while (temp != 0); 201c2472ff8SHans Petter Selasky } 202c2472ff8SHans Petter Selasky 203c2472ff8SHans Petter Selasky /* check for remainder */ 204c2472ff8SHans Petter Selasky if (count != 0) { 205c2472ff8SHans Petter Selasky /* clear topmost word before copy */ 206c2472ff8SHans Petter Selasky sc->sc_bounce_buffer[(count - 1) / 4] = 0; 207c2472ff8SHans Petter Selasky 208c2472ff8SHans Petter Selasky /* copy out data */ 209c2472ff8SHans Petter Selasky usbd_copy_out(pc, offset, 210c2472ff8SHans Petter Selasky sc->sc_bounce_buffer, count); 211c2472ff8SHans Petter Selasky 212c2472ff8SHans Petter Selasky /* transfer data into FIFO */ 213c2472ff8SHans Petter Selasky bus_space_write_region_4(sc->sc_io_tag, 214c2472ff8SHans Petter Selasky sc->sc_io_hdl, fifo, sc->sc_bounce_buffer, 215c2472ff8SHans Petter Selasky (count + 3) / 4); 216c2472ff8SHans Petter Selasky } 217c2472ff8SHans Petter Selasky } 218c2472ff8SHans Petter Selasky 219c2472ff8SHans Petter Selasky static void 220c2472ff8SHans Petter Selasky dwc_otg_read_fifo(struct dwc_otg_softc *sc, struct usb_page_cache *pc, 221c2472ff8SHans Petter Selasky uint32_t offset, uint32_t count) 222c2472ff8SHans Petter Selasky { 223c2472ff8SHans Petter Selasky uint32_t temp; 224c2472ff8SHans Petter Selasky 225c2472ff8SHans Petter Selasky /* round down length to nearest 4-bytes */ 226c2472ff8SHans Petter Selasky temp = count & ~3; 227c2472ff8SHans Petter Selasky 228c2472ff8SHans Petter Selasky /* check if we can read the data directly */ 229c2472ff8SHans Petter Selasky if (temp != 0 && usb_pc_buffer_is_aligned(pc, offset, temp, 3)) { 230c2472ff8SHans Petter Selasky struct usb_page_search buf_res; 231c2472ff8SHans Petter Selasky 232c2472ff8SHans Petter Selasky /* pre-subtract length */ 233c2472ff8SHans Petter Selasky count -= temp; 234c2472ff8SHans Petter Selasky 235c2472ff8SHans Petter Selasky /* iterate buffer list */ 236c2472ff8SHans Petter Selasky do { 237c2472ff8SHans Petter Selasky /* get current buffer pointer */ 238c2472ff8SHans Petter Selasky usbd_get_page(pc, offset, &buf_res); 239c2472ff8SHans Petter Selasky 240c2472ff8SHans Petter Selasky if (buf_res.length > temp) 241c2472ff8SHans Petter Selasky buf_res.length = temp; 242c2472ff8SHans Petter Selasky 243c2472ff8SHans Petter Selasky /* transfer data from FIFO */ 244c2472ff8SHans Petter Selasky bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, 245c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo, buf_res.buffer, buf_res.length / 4); 246c2472ff8SHans Petter Selasky 247c2472ff8SHans Petter Selasky offset += buf_res.length; 248c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo += buf_res.length; 249c2472ff8SHans Petter Selasky sc->sc_current_rx_bytes -= buf_res.length; 250c2472ff8SHans Petter Selasky temp -= buf_res.length; 251c2472ff8SHans Petter Selasky } while (temp != 0); 252c2472ff8SHans Petter Selasky } 253c2472ff8SHans Petter Selasky 254c2472ff8SHans Petter Selasky /* check for remainder */ 255c2472ff8SHans Petter Selasky if (count != 0) { 256c2472ff8SHans Petter Selasky /* read data into bounce buffer */ 257c2472ff8SHans Petter Selasky bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, 258c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo, 259c2472ff8SHans Petter Selasky sc->sc_bounce_buffer, (count + 3) / 4); 260c2472ff8SHans Petter Selasky 261c2472ff8SHans Petter Selasky /* store data into proper buffer */ 262c2472ff8SHans Petter Selasky usbd_copy_in(pc, offset, sc->sc_bounce_buffer, count); 263c2472ff8SHans Petter Selasky 264c2472ff8SHans Petter Selasky /* round length up to nearest 4 bytes */ 265c2472ff8SHans Petter Selasky count = (count + 3) & ~3; 266c2472ff8SHans Petter Selasky 267c2472ff8SHans Petter Selasky /* update counters */ 268c2472ff8SHans Petter Selasky sc->sc_current_rx_bytes -= count; 269c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo += count; 270c2472ff8SHans Petter Selasky } 271c2472ff8SHans Petter Selasky } 272c2472ff8SHans Petter Selasky 273c2472ff8SHans Petter Selasky static void 274b4df5b00SHans Petter Selasky dwc_otg_tx_fifo_reset(struct dwc_otg_softc *sc, uint32_t value) 275b4df5b00SHans Petter Selasky { 276b4df5b00SHans Petter Selasky uint32_t temp; 277b4df5b00SHans Petter Selasky 278b4df5b00SHans Petter Selasky /* reset FIFO */ 279b4df5b00SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, value); 280b4df5b00SHans Petter Selasky 281b4df5b00SHans Petter Selasky /* wait for reset to complete */ 282b4df5b00SHans Petter Selasky for (temp = 0; temp != 16; temp++) { 283b4df5b00SHans Petter Selasky value = DWC_OTG_READ_4(sc, DOTG_GRSTCTL); 284b4df5b00SHans Petter Selasky if (!(value & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH))) 285b4df5b00SHans Petter Selasky break; 286b4df5b00SHans Petter Selasky } 287b4df5b00SHans Petter Selasky } 288b4df5b00SHans Petter Selasky 289dd03e19cSHans Petter Selasky static int 2909cfd0731SHans Petter Selasky dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode) 291dd03e19cSHans Petter Selasky { 292dd03e19cSHans Petter Selasky struct dwc_otg_profile *pf; 293dd03e19cSHans Petter Selasky uint32_t fifo_size; 294dd03e19cSHans Petter Selasky uint32_t fifo_regs; 295dd03e19cSHans Petter Selasky uint32_t tx_start; 296dd03e19cSHans Petter Selasky uint8_t x; 297dd03e19cSHans Petter Selasky 298dd03e19cSHans Petter Selasky fifo_size = sc->sc_fifo_size; 299dd03e19cSHans Petter Selasky 300db4300daSHans Petter Selasky /* 301db4300daSHans Petter Selasky * NOTE: Reserved fixed size area at end of RAM, which must 302db4300daSHans Petter Selasky * not be allocated to the FIFOs: 303db4300daSHans Petter Selasky */ 304db4300daSHans Petter Selasky fifo_regs = 4 * 16; 305dd03e19cSHans Petter Selasky 306db4300daSHans Petter Selasky if (fifo_size < fifo_regs) { 307db4300daSHans Petter Selasky DPRINTF("Too little FIFO\n"); 308db4300daSHans Petter Selasky return (EINVAL); 309db4300daSHans Petter Selasky } 310db4300daSHans Petter Selasky 311db4300daSHans Petter Selasky /* subtract FIFO regs from total once */ 312dd03e19cSHans Petter Selasky fifo_size -= fifo_regs; 313dd03e19cSHans Petter Selasky 314dd03e19cSHans Petter Selasky /* split equally for IN and OUT */ 315dd03e19cSHans Petter Selasky fifo_size /= 2; 316dd03e19cSHans Petter Selasky 317a529288dSHans Petter Selasky /* Align to 4 bytes boundary (refer to PGM) */ 318dd03e19cSHans Petter Selasky fifo_size &= ~3; 319dd03e19cSHans Petter Selasky 320db4300daSHans Petter Selasky /* set global receive FIFO size */ 321db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4); 322db4300daSHans Petter Selasky 323dd03e19cSHans Petter Selasky tx_start = fifo_size; 324dd03e19cSHans Petter Selasky 325db4300daSHans Petter Selasky if (fifo_size < 64) { 326dd03e19cSHans Petter Selasky DPRINTFN(-1, "Not enough data space for EP0 FIFO.\n"); 327dd03e19cSHans Petter Selasky return (EINVAL); 328dd03e19cSHans Petter Selasky } 329dd03e19cSHans Petter Selasky 3309cfd0731SHans Petter Selasky if (mode == DWC_MODE_HOST) { 3319cfd0731SHans Petter Selasky /* reset active endpoints */ 3329cfd0731SHans Petter Selasky sc->sc_active_rx_ep = 0; 3339cfd0731SHans Petter Selasky 334db4300daSHans Petter Selasky /* split equally for periodic and non-periodic */ 335beefefd4SHans Petter Selasky fifo_size /= 2; 336beefefd4SHans Petter Selasky 337a529288dSHans Petter Selasky DPRINTF("PTX/NPTX FIFO=%u\n", fifo_size); 338a529288dSHans Petter Selasky 339db4300daSHans Petter Selasky /* align to 4 bytes boundary */ 340db4300daSHans Petter Selasky fifo_size &= ~3; 341db4300daSHans Petter Selasky 342beefefd4SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, 343beefefd4SHans Petter Selasky ((fifo_size / 4) << 16) | 344beefefd4SHans Petter Selasky (tx_start / 4)); 345beefefd4SHans Petter Selasky 346beefefd4SHans Petter Selasky tx_start += fifo_size; 347beefefd4SHans Petter Selasky 348bc990482SHans Petter Selasky for (x = 0; x != sc->sc_host_ch_max; x++) { 349a529288dSHans Petter Selasky /* enable all host interrupts */ 350dea9afcfSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 351dea9afcfSHans Petter Selasky HCINT_DEFAULT_MASK); 352bc990482SHans Petter Selasky } 353bc990482SHans Petter Selasky 3549cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ, 3559cfd0731SHans Petter Selasky ((fifo_size / 4) << 16) | 3569cfd0731SHans Petter Selasky (tx_start / 4)); 3573eabad25SHans Petter Selasky 358e2192fdfSHans Petter Selasky /* reset host channel state */ 359e2192fdfSHans Petter Selasky memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state)); 360e2192fdfSHans Petter Selasky 361dea9afcfSHans Petter Selasky /* enable all host channel interrupts */ 362dea9afcfSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 363dea9afcfSHans Petter Selasky (1U << sc->sc_host_ch_max) - 1U); 364e4d17320SHans Petter Selasky 365e4d17320SHans Petter Selasky /* enable proper host channel interrupts */ 366e4d17320SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_HCHINTMSK; 367e4d17320SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_IEPINTMSK; 368e4d17320SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 3699cfd0731SHans Petter Selasky } 370beefefd4SHans Petter Selasky 3719cfd0731SHans Petter Selasky if (mode == DWC_MODE_DEVICE) { 372beefefd4SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, 373beefefd4SHans Petter Selasky (0x10 << 16) | (tx_start / 4)); 374beefefd4SHans Petter Selasky fifo_size -= 0x40; 375beefefd4SHans Petter Selasky tx_start += 0x40; 376beefefd4SHans Petter Selasky 377beefefd4SHans Petter Selasky /* setup control endpoint profile */ 378beefefd4SHans Petter Selasky sc->sc_hw_ep_profile[0].usb = dwc_otg_ep_profile[0]; 379beefefd4SHans Petter Selasky 3809cfd0731SHans Petter Selasky /* reset active endpoints */ 3819cfd0731SHans Petter Selasky sc->sc_active_rx_ep = 1; 3829cfd0731SHans Petter Selasky 383dd03e19cSHans Petter Selasky for (x = 1; x != sc->sc_dev_ep_max; x++) { 384dd03e19cSHans Petter Selasky pf = sc->sc_hw_ep_profile + x; 385dd03e19cSHans Petter Selasky 386dd03e19cSHans Petter Selasky pf->usb.max_out_frame_size = 1024 * 3; 387dd03e19cSHans Petter Selasky pf->usb.is_simplex = 0; /* assume duplex */ 388dd03e19cSHans Petter Selasky pf->usb.support_bulk = 1; 389dd03e19cSHans Petter Selasky pf->usb.support_interrupt = 1; 390dd03e19cSHans Petter Selasky pf->usb.support_isochronous = 1; 391dd03e19cSHans Petter Selasky pf->usb.support_out = 1; 392dd03e19cSHans Petter Selasky 393dd03e19cSHans Petter Selasky if (x < sc->sc_dev_in_ep_max) { 394dd03e19cSHans Petter Selasky uint32_t limit; 395dd03e19cSHans Petter Selasky 3962624de5cSHans Petter Selasky limit = (x == 1) ? MIN(DWC_OTG_TX_MAX_FIFO_SIZE, 3972624de5cSHans Petter Selasky DWC_OTG_MAX_TXN) : MIN(DWC_OTG_MAX_TXN / 2, 3982624de5cSHans Petter Selasky DWC_OTG_TX_MAX_FIFO_SIZE); 399dd03e19cSHans Petter Selasky 4002624de5cSHans Petter Selasky /* see if there is enough FIFO space */ 4012624de5cSHans Petter Selasky if (limit <= fifo_size) { 402dd03e19cSHans Petter Selasky pf->max_buffer = limit; 403dd03e19cSHans Petter Selasky pf->usb.support_in = 1; 4042624de5cSHans Petter Selasky } else { 4052624de5cSHans Petter Selasky limit = MIN(DWC_OTG_TX_MAX_FIFO_SIZE, 0x40); 4062624de5cSHans Petter Selasky if (limit <= fifo_size) { 4072624de5cSHans Petter Selasky pf->usb.support_in = 1; 408dd03e19cSHans Petter Selasky } else { 409dd03e19cSHans Petter Selasky pf->usb.is_simplex = 1; 4102624de5cSHans Petter Selasky limit = 0; 411dd03e19cSHans Petter Selasky } 4122624de5cSHans Petter Selasky } 4132624de5cSHans Petter Selasky /* set FIFO size */ 4142624de5cSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPTXF(x), 4152624de5cSHans Petter Selasky ((limit / 4) << 16) | (tx_start / 4)); 4162624de5cSHans Petter Selasky tx_start += limit; 4172624de5cSHans Petter Selasky fifo_size -= limit; 4182624de5cSHans Petter Selasky pf->usb.max_in_frame_size = limit; 419dd03e19cSHans Petter Selasky } else { 420dd03e19cSHans Petter Selasky pf->usb.is_simplex = 1; 421dd03e19cSHans Petter Selasky } 422dd03e19cSHans Petter Selasky 423dd03e19cSHans Petter Selasky DPRINTF("FIFO%d = IN:%d / OUT:%d\n", x, 424dd03e19cSHans Petter Selasky pf->usb.max_in_frame_size, 425dd03e19cSHans Petter Selasky pf->usb.max_out_frame_size); 426dd03e19cSHans Petter Selasky } 427e4d17320SHans Petter Selasky 428e4d17320SHans Petter Selasky /* enable proper device channel interrupts */ 429e4d17320SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_HCHINTMSK; 430e4d17320SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_IEPINTMSK; 431e4d17320SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 4329cfd0731SHans Petter Selasky } 433dd03e19cSHans Petter Selasky 434dd03e19cSHans Petter Selasky /* reset RX FIFO */ 435b4df5b00SHans Petter Selasky dwc_otg_tx_fifo_reset(sc, GRSTCTL_RXFFLSH); 436dd03e19cSHans Petter Selasky 4379cfd0731SHans Petter Selasky if (mode != DWC_MODE_OTG) { 438dd03e19cSHans Petter Selasky /* reset all TX FIFOs */ 439b4df5b00SHans Petter Selasky dwc_otg_tx_fifo_reset(sc, 440710764f7SHans Petter Selasky GRSTCTL_TXFIFO(0x10) | 441710764f7SHans Petter Selasky GRSTCTL_TXFFLSH); 4429cfd0731SHans Petter Selasky } else { 4439cfd0731SHans Petter Selasky /* reset active endpoints */ 4449cfd0731SHans Petter Selasky sc->sc_active_rx_ep = 0; 445bc990482SHans Petter Selasky 446e2192fdfSHans Petter Selasky /* reset host channel state */ 447e2192fdfSHans Petter Selasky memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state)); 4489cfd0731SHans Petter Selasky } 449dd03e19cSHans Petter Selasky return (0); 450dd03e19cSHans Petter Selasky } 451dd03e19cSHans Petter Selasky 452076daedaSHans Petter Selasky static uint8_t 453076daedaSHans Petter Selasky dwc_otg_uses_split(struct usb_device *udev) 454076daedaSHans Petter Selasky { 455076daedaSHans Petter Selasky /* 456076daedaSHans Petter Selasky * When a LOW or FULL speed device is connected directly to 457076daedaSHans Petter Selasky * the USB port we don't use split transactions: 458076daedaSHans Petter Selasky */ 459076daedaSHans Petter Selasky return (udev->speed != USB_SPEED_HIGH && 460076daedaSHans Petter Selasky udev->parent_hs_hub != NULL && 461076daedaSHans Petter Selasky udev->parent_hs_hub->parent_hub != NULL); 462076daedaSHans Petter Selasky } 463076daedaSHans Petter Selasky 464dd03e19cSHans Petter Selasky static void 46542fabcc3SHans Petter Selasky dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc) 46642fabcc3SHans Petter Selasky { 467f5bf22f3SHans Petter Selasky 468f5bf22f3SHans Petter Selasky /* 469f5bf22f3SHans Petter Selasky * Disabled until further. Assuming that the register is already 470f5bf22f3SHans Petter Selasky * programmed correctly by the boot loader. 471f5bf22f3SHans Petter Selasky */ 472f5bf22f3SHans Petter Selasky #if 0 47342fabcc3SHans Petter Selasky uint32_t temp; 47442fabcc3SHans Petter Selasky 47542fabcc3SHans Petter Selasky /* setup HOST frame interval register, based on existing value */ 47642fabcc3SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK; 47742fabcc3SHans Petter Selasky if (temp >= 10000) 47842fabcc3SHans Petter Selasky temp /= 1000; 47942fabcc3SHans Petter Selasky else 48042fabcc3SHans Petter Selasky temp /= 125; 48142fabcc3SHans Petter Selasky 48242fabcc3SHans Petter Selasky /* figure out nearest X-tal value */ 48342fabcc3SHans Petter Selasky if (temp >= 54) 48442fabcc3SHans Petter Selasky temp = 60; /* MHz */ 48542fabcc3SHans Petter Selasky else if (temp >= 39) 48642fabcc3SHans Petter Selasky temp = 48; /* MHz */ 48742fabcc3SHans Petter Selasky else 48842fabcc3SHans Petter Selasky temp = 30; /* MHz */ 48942fabcc3SHans Petter Selasky 49042fabcc3SHans Petter Selasky if (sc->sc_flags.status_high_speed) 49142fabcc3SHans Petter Selasky temp *= 125; 49242fabcc3SHans Petter Selasky else 49342fabcc3SHans Petter Selasky temp *= 1000; 49442fabcc3SHans Petter Selasky 49542fabcc3SHans Petter Selasky DPRINTF("HFIR=0x%08x\n", temp); 49642fabcc3SHans Petter Selasky 49742fabcc3SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp); 498f5bf22f3SHans Petter Selasky #endif 49942fabcc3SHans Petter Selasky } 50042fabcc3SHans Petter Selasky 50142fabcc3SHans Petter Selasky static void 502dd03e19cSHans Petter Selasky dwc_otg_clocks_on(struct dwc_otg_softc *sc) 503dd03e19cSHans Petter Selasky { 504dd03e19cSHans Petter Selasky if (sc->sc_flags.clocks_off && 505dd03e19cSHans Petter Selasky sc->sc_flags.port_powered) { 506dd03e19cSHans Petter Selasky DPRINTFN(5, "\n"); 507dd03e19cSHans Petter Selasky 508dd03e19cSHans Petter Selasky /* TODO - platform specific */ 509dd03e19cSHans Petter Selasky 510dd03e19cSHans Petter Selasky sc->sc_flags.clocks_off = 0; 511dd03e19cSHans Petter Selasky } 512dd03e19cSHans Petter Selasky } 513dd03e19cSHans Petter Selasky 514dd03e19cSHans Petter Selasky static void 515dd03e19cSHans Petter Selasky dwc_otg_clocks_off(struct dwc_otg_softc *sc) 516dd03e19cSHans Petter Selasky { 517dd03e19cSHans Petter Selasky if (!sc->sc_flags.clocks_off) { 518dd03e19cSHans Petter Selasky DPRINTFN(5, "\n"); 519dd03e19cSHans Petter Selasky 520dd03e19cSHans Petter Selasky /* TODO - platform specific */ 521dd03e19cSHans Petter Selasky 522dd03e19cSHans Petter Selasky sc->sc_flags.clocks_off = 1; 523dd03e19cSHans Petter Selasky } 524dd03e19cSHans Petter Selasky } 525dd03e19cSHans Petter Selasky 526dd03e19cSHans Petter Selasky static void 527dd03e19cSHans Petter Selasky dwc_otg_pull_up(struct dwc_otg_softc *sc) 528dd03e19cSHans Petter Selasky { 529dd03e19cSHans Petter Selasky uint32_t temp; 530dd03e19cSHans Petter Selasky 531dd03e19cSHans Petter Selasky /* pullup D+, if possible */ 532dd03e19cSHans Petter Selasky 533dd03e19cSHans Petter Selasky if (!sc->sc_flags.d_pulled_up && 534dd03e19cSHans Petter Selasky sc->sc_flags.port_powered) { 535dd03e19cSHans Petter Selasky sc->sc_flags.d_pulled_up = 1; 536dd03e19cSHans Petter Selasky 537710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DCTL); 538710764f7SHans Petter Selasky temp &= ~DCTL_SFTDISCON; 539710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); 540dd03e19cSHans Petter Selasky } 541dd03e19cSHans Petter Selasky } 542dd03e19cSHans Petter Selasky 543dd03e19cSHans Petter Selasky static void 544dd03e19cSHans Petter Selasky dwc_otg_pull_down(struct dwc_otg_softc *sc) 545dd03e19cSHans Petter Selasky { 546dd03e19cSHans Petter Selasky uint32_t temp; 547dd03e19cSHans Petter Selasky 548dd03e19cSHans Petter Selasky /* pulldown D+, if possible */ 549dd03e19cSHans Petter Selasky 550dd03e19cSHans Petter Selasky if (sc->sc_flags.d_pulled_up) { 551dd03e19cSHans Petter Selasky sc->sc_flags.d_pulled_up = 0; 552dd03e19cSHans Petter Selasky 553710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DCTL); 554710764f7SHans Petter Selasky temp |= DCTL_SFTDISCON; 555710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); 556dd03e19cSHans Petter Selasky } 557dd03e19cSHans Petter Selasky } 558dd03e19cSHans Petter Selasky 559dd03e19cSHans Petter Selasky static void 560e7162865SHans Petter Selasky dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc) 561e7162865SHans Petter Selasky { 56242fabcc3SHans Petter Selasky /* In device mode we don't use the SOF interrupt */ 563ed0ed9b4SHans Petter Selasky if (sc->sc_flags.status_device_mode != 0) 564ed0ed9b4SHans Petter Selasky return; 565ed0ed9b4SHans Petter Selasky /* Ensure the SOF interrupt is not disabled */ 566ed0ed9b4SHans Petter Selasky sc->sc_needsof = 1; 567ed0ed9b4SHans Petter Selasky /* Check if the SOF interrupt is already enabled */ 568ed0ed9b4SHans Petter Selasky if ((sc->sc_irq_mask & GINTMSK_SOFMSK) != 0) 569e7162865SHans Petter Selasky return; 570bc990482SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_SOFMSK; 571e7162865SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 572e7162865SHans Petter Selasky } 573e7162865SHans Petter Selasky 574e7162865SHans Petter Selasky static void 575dd03e19cSHans Petter Selasky dwc_otg_resume_irq(struct dwc_otg_softc *sc) 576dd03e19cSHans Petter Selasky { 577dd03e19cSHans Petter Selasky if (sc->sc_flags.status_suspend) { 578dd03e19cSHans Petter Selasky /* update status bits */ 579dd03e19cSHans Petter Selasky sc->sc_flags.status_suspend = 0; 580dd03e19cSHans Petter Selasky sc->sc_flags.change_suspend = 1; 581dd03e19cSHans Petter Selasky 5829cfd0731SHans Petter Selasky if (sc->sc_flags.status_device_mode) { 583dd03e19cSHans Petter Selasky /* 584dd03e19cSHans Petter Selasky * Disable resume interrupt and enable suspend 585dd03e19cSHans Petter Selasky * interrupt: 586dd03e19cSHans Petter Selasky */ 58742fabcc3SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_WKUPINTMSK; 58842fabcc3SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; 589710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 5909cfd0731SHans Petter Selasky } 5919cfd0731SHans Petter Selasky 5929cfd0731SHans Petter Selasky /* complete root HUB interrupt endpoint */ 5939cfd0731SHans Petter Selasky dwc_otg_root_intr(sc); 5949cfd0731SHans Petter Selasky } 5959cfd0731SHans Petter Selasky } 5969cfd0731SHans Petter Selasky 5979cfd0731SHans Petter Selasky static void 5989cfd0731SHans Petter Selasky dwc_otg_suspend_irq(struct dwc_otg_softc *sc) 5999cfd0731SHans Petter Selasky { 6009cfd0731SHans Petter Selasky if (!sc->sc_flags.status_suspend) { 6019cfd0731SHans Petter Selasky /* update status bits */ 6029cfd0731SHans Petter Selasky sc->sc_flags.status_suspend = 1; 6039cfd0731SHans Petter Selasky sc->sc_flags.change_suspend = 1; 6049cfd0731SHans Petter Selasky 6059cfd0731SHans Petter Selasky if (sc->sc_flags.status_device_mode) { 6069cfd0731SHans Petter Selasky /* 6079cfd0731SHans Petter Selasky * Disable suspend interrupt and enable resume 6089cfd0731SHans Petter Selasky * interrupt: 6099cfd0731SHans Petter Selasky */ 61042fabcc3SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_USBSUSPMSK; 61142fabcc3SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_WKUPINTMSK; 6129cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 6139cfd0731SHans Petter Selasky } 614dd03e19cSHans Petter Selasky 615dd03e19cSHans Petter Selasky /* complete root HUB interrupt endpoint */ 616dd03e19cSHans Petter Selasky dwc_otg_root_intr(sc); 617dd03e19cSHans Petter Selasky } 618dd03e19cSHans Petter Selasky } 619dd03e19cSHans Petter Selasky 620dd03e19cSHans Petter Selasky static void 621dd03e19cSHans Petter Selasky dwc_otg_wakeup_peer(struct dwc_otg_softc *sc) 622dd03e19cSHans Petter Selasky { 623dd03e19cSHans Petter Selasky if (!sc->sc_flags.status_suspend) 624dd03e19cSHans Petter Selasky return; 625dd03e19cSHans Petter Selasky 626dd03e19cSHans Petter Selasky DPRINTFN(5, "Remote wakeup\n"); 627dd03e19cSHans Petter Selasky 6289cfd0731SHans Petter Selasky if (sc->sc_flags.status_device_mode) { 6299cfd0731SHans Petter Selasky uint32_t temp; 6309cfd0731SHans Petter Selasky 631dd03e19cSHans Petter Selasky /* enable remote wakeup signalling */ 632710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DCTL); 633710764f7SHans Petter Selasky temp |= DCTL_RMTWKUPSIG; 634710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); 635dd03e19cSHans Petter Selasky 636dd03e19cSHans Petter Selasky /* Wait 8ms for remote wakeup to complete. */ 637dd03e19cSHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); 638dd03e19cSHans Petter Selasky 639710764f7SHans Petter Selasky temp &= ~DCTL_RMTWKUPSIG; 640710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp); 6419cfd0731SHans Petter Selasky } else { 6429cfd0731SHans Petter Selasky /* enable USB port */ 6439cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0); 6449cfd0731SHans Petter Selasky 6459cfd0731SHans Petter Selasky /* wait 10ms */ 6469cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); 6479cfd0731SHans Petter Selasky 6489cfd0731SHans Petter Selasky /* resume port */ 6499cfd0731SHans Petter Selasky sc->sc_hprt_val |= HPRT_PRTRES; 6509cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); 6519cfd0731SHans Petter Selasky 6529cfd0731SHans Petter Selasky /* Wait 100ms for resume signalling to complete. */ 6539cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10); 6549cfd0731SHans Petter Selasky 6559cfd0731SHans Petter Selasky /* clear suspend and resume */ 6569cfd0731SHans Petter Selasky sc->sc_hprt_val &= ~(HPRT_PRTSUSP | HPRT_PRTRES); 6579cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); 6589cfd0731SHans Petter Selasky 6599cfd0731SHans Petter Selasky /* Wait 4ms */ 6609cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); 6619cfd0731SHans Petter Selasky } 662dd03e19cSHans Petter Selasky 663dd03e19cSHans Petter Selasky /* need to fake resume IRQ */ 664dd03e19cSHans Petter Selasky dwc_otg_resume_irq(sc); 665dd03e19cSHans Petter Selasky } 666dd03e19cSHans Petter Selasky 667dd03e19cSHans Petter Selasky static void 668dd03e19cSHans Petter Selasky dwc_otg_set_address(struct dwc_otg_softc *sc, uint8_t addr) 669dd03e19cSHans Petter Selasky { 670dd03e19cSHans Petter Selasky uint32_t temp; 671dd03e19cSHans Petter Selasky 672dd03e19cSHans Petter Selasky DPRINTFN(5, "addr=%d\n", addr); 673dd03e19cSHans Petter Selasky 674710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DCFG); 675710764f7SHans Petter Selasky temp &= ~DCFG_DEVADDR_SET(0x7F); 676710764f7SHans Petter Selasky temp |= DCFG_DEVADDR_SET(addr); 677710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCFG, temp); 678dd03e19cSHans Petter Selasky } 679dd03e19cSHans Petter Selasky 680dd03e19cSHans Petter Selasky static void 681dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(struct dwc_otg_softc *sc) 682dd03e19cSHans Petter Selasky { 683dd03e19cSHans Petter Selasky DPRINTFN(5, "RX status clear\n"); 684dd03e19cSHans Petter Selasky 685dd03e19cSHans Petter Selasky /* enable RX FIFO level interrupt */ 68642fabcc3SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; 687710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 688dd03e19cSHans Petter Selasky 689c2472ff8SHans Petter Selasky if (sc->sc_current_rx_bytes != 0) { 690c2472ff8SHans Petter Selasky /* need to dump remaining data */ 691c2472ff8SHans Petter Selasky bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, 692c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo, sc->sc_bounce_buffer, 693c2472ff8SHans Petter Selasky sc->sc_current_rx_bytes / 4); 694c2472ff8SHans Petter Selasky /* clear number of active bytes to receive */ 695c2472ff8SHans Petter Selasky sc->sc_current_rx_bytes = 0; 696c2472ff8SHans Petter Selasky } 697dd03e19cSHans Petter Selasky /* clear cached status */ 698dd03e19cSHans Petter Selasky sc->sc_last_rx_status = 0; 699dd03e19cSHans Petter Selasky } 700dd03e19cSHans Petter Selasky 7013eabad25SHans Petter Selasky static void 7023eabad25SHans Petter Selasky dwc_otg_clear_hcint(struct dwc_otg_softc *sc, uint8_t x) 7033eabad25SHans Petter Selasky { 7043eabad25SHans Petter Selasky uint32_t hcint; 7053eabad25SHans Petter Selasky 706bc990482SHans Petter Selasky /* clear all pending interrupts */ 7073eabad25SHans Petter Selasky hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); 7083eabad25SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint); 7093eabad25SHans Petter Selasky 7103eabad25SHans Petter Selasky /* clear buffered interrupts */ 7113eabad25SHans Petter Selasky sc->sc_chan_state[x].hcint = 0; 7123eabad25SHans Petter Selasky } 7133eabad25SHans Petter Selasky 7143eabad25SHans Petter Selasky static uint8_t 715ce842cecSHans Petter Selasky dwc_otg_host_check_tx_fifo_empty(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 7163eabad25SHans Petter Selasky { 717a529288dSHans Petter Selasky uint32_t temp; 718a529288dSHans Petter Selasky 719a529288dSHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); 720a529288dSHans Petter Selasky 721ce842cecSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS) { 722ce842cecSHans Petter Selasky /* 723ce842cecSHans Petter Selasky * NOTE: USB INTERRUPT transactions are executed like 724ce842cecSHans Petter Selasky * USB CONTROL transactions! See the setup standard 725ce842cecSHans Petter Selasky * chain function for more information. 726ce842cecSHans Petter Selasky */ 727a529288dSHans Petter Selasky if (!(temp & GINTSTS_PTXFEMP)) { 728a529288dSHans Petter Selasky DPRINTF("Periodic TX FIFO is not empty\n"); 729a529288dSHans Petter Selasky if (!(sc->sc_irq_mask & GINTMSK_PTXFEMPMSK)) { 730a529288dSHans Petter Selasky sc->sc_irq_mask |= GINTMSK_PTXFEMPMSK; 731a529288dSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 732a529288dSHans Petter Selasky } 733a529288dSHans Petter Selasky return (1); /* busy */ 734a529288dSHans Petter Selasky } 735a529288dSHans Petter Selasky } else { 736a529288dSHans Petter Selasky if (!(temp & GINTSTS_NPTXFEMP)) { 737a529288dSHans Petter Selasky DPRINTF("Non-periodic TX FIFO is not empty\n"); 738a529288dSHans Petter Selasky if (!(sc->sc_irq_mask & GINTMSK_NPTXFEMPMSK)) { 739a529288dSHans Petter Selasky sc->sc_irq_mask |= GINTMSK_NPTXFEMPMSK; 740a529288dSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 741a529288dSHans Petter Selasky } 742a529288dSHans Petter Selasky return (1); /* busy */ 743a529288dSHans Petter Selasky } 744a529288dSHans Petter Selasky } 745a529288dSHans Petter Selasky return (0); /* ready for transmit */ 746a529288dSHans Petter Selasky } 747a529288dSHans Petter Selasky 748a529288dSHans Petter Selasky static uint8_t 749a529288dSHans Petter Selasky dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, 750a529288dSHans Petter Selasky struct dwc_otg_td *td, uint8_t is_out) 751a529288dSHans Petter Selasky { 7523eabad25SHans Petter Selasky uint8_t x; 753ce842cecSHans Petter Selasky uint8_t y; 754ce842cecSHans Petter Selasky uint8_t z; 7553eabad25SHans Petter Selasky 756ce842cecSHans Petter Selasky if (td->channel[0] < DWC_OTG_MAX_CHANNELS) 7579cfd0731SHans Petter Selasky return (0); /* already allocated */ 7589cfd0731SHans Petter Selasky 759db4300daSHans Petter Selasky /* check if device is suspended */ 760db4300daSHans Petter Selasky if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0) 761db4300daSHans Petter Selasky return (1); /* busy - cannot transfer data */ 762db4300daSHans Petter Selasky 763db4300daSHans Petter Selasky /* compute needed TX FIFO size */ 764e2192fdfSHans Petter Selasky if (is_out != 0) { 765ce842cecSHans Petter Selasky if (dwc_otg_host_check_tx_fifo_empty(sc, td) != 0) 766a529288dSHans Petter Selasky return (1); /* busy - cannot transfer data */ 7679cfd0731SHans Petter Selasky } 768ce842cecSHans Petter Selasky z = td->max_packet_count; 769ce842cecSHans Petter Selasky for (x = y = 0; x != sc->sc_host_ch_max; x++) { 770a529288dSHans Petter Selasky /* check if channel is allocated */ 771db4300daSHans Petter Selasky if (sc->sc_chan_state[x].allocated != 0) 7723eabad25SHans Petter Selasky continue; 773db4300daSHans Petter Selasky /* check if channel is still enabled */ 774a529288dSHans Petter Selasky if (sc->sc_chan_state[x].wait_halted != 0) 77555df1601SHans Petter Selasky continue; 776ce842cecSHans Petter Selasky /* store channel number */ 777ce842cecSHans Petter Selasky td->channel[y++] = x; 778ce842cecSHans Petter Selasky /* check if we got all channels */ 779ce842cecSHans Petter Selasky if (y == z) 780ce842cecSHans Petter Selasky break; 781ce842cecSHans Petter Selasky } 782ce842cecSHans Petter Selasky if (y != z) { 783ce842cecSHans Petter Selasky /* reset channel variable */ 784ce842cecSHans Petter Selasky td->channel[0] = DWC_OTG_MAX_CHANNELS; 785ce842cecSHans Petter Selasky td->channel[1] = DWC_OTG_MAX_CHANNELS; 786ce842cecSHans Petter Selasky td->channel[2] = DWC_OTG_MAX_CHANNELS; 787ce842cecSHans Petter Selasky /* wait a bit */ 788ce842cecSHans Petter Selasky dwc_otg_enable_sof_irq(sc); 789ce842cecSHans Petter Selasky return (1); /* busy - not enough channels */ 790ce842cecSHans Petter Selasky } 79155df1601SHans Petter Selasky 792ce842cecSHans Petter Selasky for (y = 0; y != z; y++) { 793ce842cecSHans Petter Selasky x = td->channel[y]; 794ce842cecSHans Petter Selasky 795ce842cecSHans Petter Selasky /* set allocated */ 7963eabad25SHans Petter Selasky sc->sc_chan_state[x].allocated = 1; 7979cfd0731SHans Petter Selasky 798ce842cecSHans Petter Selasky /* set wait halted */ 799ce842cecSHans Petter Selasky sc->sc_chan_state[x].wait_halted = 1; 800ce842cecSHans Petter Selasky 8013eabad25SHans Petter Selasky /* clear interrupts */ 8023eabad25SHans Petter Selasky dwc_otg_clear_hcint(sc, x); 8039cfd0731SHans Petter Selasky 80419f9c619SHans Petter Selasky DPRINTF("CH=%d HCCHAR=0x%08x " 80519f9c619SHans Petter Selasky "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt); 806460febc7SHans Petter Selasky 8073eabad25SHans Petter Selasky /* set active channel */ 8083eabad25SHans Petter Selasky sc->sc_active_rx_ep |= (1 << x); 8099cfd0731SHans Petter Selasky } 810ce842cecSHans Petter Selasky return (0); /* allocated */ 8119cfd0731SHans Petter Selasky } 8129cfd0731SHans Petter Selasky 8133eabad25SHans Petter Selasky static void 814ce842cecSHans Petter Selasky dwc_otg_host_channel_free_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t index) 8153eabad25SHans Petter Selasky { 816a529288dSHans Petter Selasky uint32_t hcchar; 8173eabad25SHans Petter Selasky uint8_t x; 8183eabad25SHans Petter Selasky 819ce842cecSHans Petter Selasky if (td->channel[index] >= DWC_OTG_MAX_CHANNELS) 8203eabad25SHans Petter Selasky return; /* already freed */ 8213eabad25SHans Petter Selasky 8223eabad25SHans Petter Selasky /* free channel */ 823ce842cecSHans Petter Selasky x = td->channel[index]; 824ce842cecSHans Petter Selasky td->channel[index] = DWC_OTG_MAX_CHANNELS; 8253eabad25SHans Petter Selasky 8263eabad25SHans Petter Selasky DPRINTF("CH=%d\n", x); 8273eabad25SHans Petter Selasky 828e2192fdfSHans Petter Selasky /* 829e2192fdfSHans Petter Selasky * We need to let programmed host channels run till complete 830a529288dSHans Petter Selasky * else the host channel will stop functioning. 831e2192fdfSHans Petter Selasky */ 832e2192fdfSHans Petter Selasky sc->sc_chan_state[x].allocated = 0; 833bc990482SHans Petter Selasky 8343eabad25SHans Petter Selasky /* ack any pending messages */ 8353eabad25SHans Petter Selasky if (sc->sc_last_rx_status != 0 && 8363eabad25SHans Petter Selasky GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) { 8373eabad25SHans Petter Selasky dwc_otg_common_rx_ack(sc); 8383eabad25SHans Petter Selasky } 8393eabad25SHans Petter Selasky 8403eabad25SHans Petter Selasky /* clear active channel */ 8413eabad25SHans Petter Selasky sc->sc_active_rx_ep &= ~(1 << x); 842a529288dSHans Petter Selasky 843ce842cecSHans Petter Selasky /* check if already halted */ 844ce842cecSHans Petter Selasky if (sc->sc_chan_state[x].wait_halted == 0) 845ce842cecSHans Petter Selasky return; 846ce842cecSHans Petter Selasky 847a529288dSHans Petter Selasky /* disable host channel */ 848a529288dSHans Petter Selasky hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); 849a529288dSHans Petter Selasky if (hcchar & HCCHAR_CHENA) { 850a529288dSHans Petter Selasky DPRINTF("Halting channel %d\n", x); 851a529288dSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), 852a529288dSHans Petter Selasky hcchar | HCCHAR_CHDIS); 853a529288dSHans Petter Selasky /* don't write HCCHAR until the channel is halted */ 854ce842cecSHans Petter Selasky } else { 855ce842cecSHans Petter Selasky sc->sc_chan_state[x].wait_halted = 0; 856a529288dSHans Petter Selasky } 8573eabad25SHans Petter Selasky } 8583eabad25SHans Petter Selasky 85908aa4c94SHans Petter Selasky static void 860ce842cecSHans Petter Selasky dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 861ce842cecSHans Petter Selasky { 862ce842cecSHans Petter Selasky uint8_t x; 863ce842cecSHans Petter Selasky for (x = 0; x != td->max_packet_count; x++) 864ce842cecSHans Petter Selasky dwc_otg_host_channel_free_sub(sc, td, x); 865ce842cecSHans Petter Selasky } 866ce842cecSHans Petter Selasky 867ce842cecSHans Petter Selasky static void 86808aa4c94SHans Petter Selasky dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 86908aa4c94SHans Petter Selasky { 870ce842cecSHans Petter Selasky uint8_t x; 87108aa4c94SHans Petter Selasky /* dump any pending messages */ 872ce842cecSHans Petter Selasky if (sc->sc_last_rx_status == 0) 873ce842cecSHans Petter Selasky return; 874ce842cecSHans Petter Selasky for (x = 0; x != td->max_packet_count; x++) { 875ce842cecSHans Petter Selasky if (td->channel[x] >= DWC_OTG_MAX_CHANNELS || 876ce842cecSHans Petter Selasky td->channel[x] != GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) 877ce842cecSHans Petter Selasky continue; 87808aa4c94SHans Petter Selasky dwc_otg_common_rx_ack(sc); 879c2472ff8SHans Petter Selasky break; 88008aa4c94SHans Petter Selasky } 88108aa4c94SHans Petter Selasky } 88208aa4c94SHans Petter Selasky 8839cfd0731SHans Petter Selasky static uint8_t 8842fe7ad87SHans Petter Selasky dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 8859cfd0731SHans Petter Selasky { 8869cfd0731SHans Petter Selasky struct usb_device_request req __aligned(4); 8873eabad25SHans Petter Selasky uint32_t hcint; 8883eabad25SHans Petter Selasky uint32_t hcchar; 889db4300daSHans Petter Selasky uint8_t delta; 8909cfd0731SHans Petter Selasky 89108aa4c94SHans Petter Selasky dwc_otg_host_dump_rx(sc, td); 89208aa4c94SHans Petter Selasky 893ce842cecSHans Petter Selasky if (td->channel[0] < DWC_OTG_MAX_CHANNELS) { 894ce842cecSHans Petter Selasky hcint = sc->sc_chan_state[td->channel[0]].hcint; 895537aca95SHans Petter Selasky 896b792f659SHans Petter Selasky DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", 897ce842cecSHans Petter Selasky td->channel[0], td->state, hcint, 898ce842cecSHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])), 899ce842cecSHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0]))); 900db4300daSHans Petter Selasky } else { 901db4300daSHans Petter Selasky hcint = 0; 902db4300daSHans Petter Selasky goto check_state; 903db4300daSHans Petter Selasky } 904537aca95SHans Petter Selasky 905e7162865SHans Petter Selasky if (hcint & (HCINT_RETRY | 906e7162865SHans Petter Selasky HCINT_ACK | HCINT_NYET)) { 907e7162865SHans Petter Selasky /* give success bits priority over failure bits */ 908e7162865SHans Petter Selasky } else if (hcint & HCINT_STALL) { 909ce842cecSHans Petter Selasky DPRINTF("CH=%d STALL\n", td->channel[0]); 910537aca95SHans Petter Selasky td->error_stall = 1; 911537aca95SHans Petter Selasky td->error_any = 1; 91242fabcc3SHans Petter Selasky goto complete; 913e7162865SHans Petter Selasky } else if (hcint & HCINT_ERRORS) { 914ce842cecSHans Petter Selasky DPRINTF("CH=%d ERROR\n", td->channel[0]); 9153eabad25SHans Petter Selasky td->errcnt++; 9163eabad25SHans Petter Selasky if (td->hcsplt != 0 || td->errcnt >= 3) { 917537aca95SHans Petter Selasky td->error_any = 1; 91842fabcc3SHans Petter Selasky goto complete; 919537aca95SHans Petter Selasky } 920537aca95SHans Petter Selasky } 921b792f659SHans Petter Selasky 9223eabad25SHans Petter Selasky if (hcint & (HCINT_ERRORS | HCINT_RETRY | 9233eabad25SHans Petter Selasky HCINT_ACK | HCINT_NYET)) { 9243eabad25SHans Petter Selasky if (!(hcint & HCINT_ERRORS)) 9253eabad25SHans Petter Selasky td->errcnt = 0; 926b792f659SHans Petter Selasky } 9279cfd0731SHans Petter Selasky 928db4300daSHans Petter Selasky check_state: 9293eabad25SHans Petter Selasky switch (td->state) { 930b792f659SHans Petter Selasky case DWC_CHAN_ST_START: 931b792f659SHans Petter Selasky goto send_pkt; 9323eabad25SHans Petter Selasky 933b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_ANE: 9343eabad25SHans Petter Selasky if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 935ed0ed9b4SHans Petter Selasky td->did_nak = 1; 936bc990482SHans Petter Selasky td->tt_scheduled = 0; 937b792f659SHans Petter Selasky goto send_pkt; 938db4300daSHans Petter Selasky } else if (hcint & (HCINT_ACK | HCINT_NYET)) { 939b792f659SHans Petter Selasky td->offset += td->tx_bytes; 940b792f659SHans Petter Selasky td->remainder -= td->tx_bytes; 941b792f659SHans Petter Selasky td->toggle = 1; 942bc990482SHans Petter Selasky td->tt_scheduled = 0; 94342fabcc3SHans Petter Selasky goto complete; 944b792f659SHans Petter Selasky } 945b792f659SHans Petter Selasky break; 946bc990482SHans Petter Selasky 947b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_S_ANE: 9483eabad25SHans Petter Selasky if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 949ed0ed9b4SHans Petter Selasky td->did_nak = 1; 950bc990482SHans Petter Selasky td->tt_scheduled = 0; 951b792f659SHans Petter Selasky goto send_pkt; 952db4300daSHans Petter Selasky } else if (hcint & (HCINT_ACK | HCINT_NYET)) { 953b792f659SHans Petter Selasky goto send_cpkt; 954b792f659SHans Petter Selasky } 955b792f659SHans Petter Selasky break; 956bc990482SHans Petter Selasky 957b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_C_ANE: 958a3bfcf3eSHans Petter Selasky if (hcint & HCINT_NYET) { 959a3bfcf3eSHans Petter Selasky goto send_cpkt; 960db4300daSHans Petter Selasky } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 961ed0ed9b4SHans Petter Selasky td->did_nak = 1; 962bc990482SHans Petter Selasky td->tt_scheduled = 0; 963a3bfcf3eSHans Petter Selasky goto send_pkt; 964db4300daSHans Petter Selasky } else if (hcint & HCINT_ACK) { 965b792f659SHans Petter Selasky td->offset += td->tx_bytes; 966b792f659SHans Petter Selasky td->remainder -= td->tx_bytes; 967b792f659SHans Petter Selasky td->toggle = 1; 96842fabcc3SHans Petter Selasky goto complete; 969b792f659SHans Petter Selasky } 970b792f659SHans Petter Selasky break; 971bc990482SHans Petter Selasky 972bc990482SHans Petter Selasky case DWC_CHAN_ST_WAIT_C_PKT: 973bc990482SHans Petter Selasky goto send_cpkt; 974bc990482SHans Petter Selasky 975b792f659SHans Petter Selasky default: 976b792f659SHans Petter Selasky break; 977b792f659SHans Petter Selasky } 97842fabcc3SHans Petter Selasky goto busy; 9799cfd0731SHans Petter Selasky 980b792f659SHans Petter Selasky send_pkt: 981db4300daSHans Petter Selasky /* free existing channel, if any */ 98208aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 983db4300daSHans Petter Selasky 9849cfd0731SHans Petter Selasky if (sizeof(req) != td->remainder) { 9859cfd0731SHans Petter Selasky td->error_any = 1; 98642fabcc3SHans Petter Selasky goto complete; 9879cfd0731SHans Petter Selasky } 9889cfd0731SHans Petter Selasky 9893eabad25SHans Petter Selasky if (td->hcsplt != 0) { 990db4300daSHans Petter Selasky delta = td->tt_start_slot - sc->sc_last_frame_num - 1; 991db4300daSHans Petter Selasky if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { 992bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_START; 993db4300daSHans Petter Selasky goto busy; 994db4300daSHans Petter Selasky } 995db4300daSHans Petter Selasky delta = sc->sc_last_frame_num - td->tt_start_slot; 996db4300daSHans Petter Selasky if (delta > 5) { 997db4300daSHans Petter Selasky /* missed it */ 998db4300daSHans Petter Selasky td->tt_scheduled = 0; 999db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_START; 1000db4300daSHans Petter Selasky goto busy; 1001db4300daSHans Petter Selasky } 1002e7162865SHans Petter Selasky } 1003e7162865SHans Petter Selasky 1004db4300daSHans Petter Selasky /* allocate a new channel */ 100508aa4c94SHans Petter Selasky if (dwc_otg_host_channel_alloc(sc, td, 1)) { 1006db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_START; 1007db4300daSHans Petter Selasky goto busy; 1008db4300daSHans Petter Selasky } 1009db4300daSHans Petter Selasky 1010db4300daSHans Petter Selasky if (td->hcsplt != 0) { 10113eabad25SHans Petter Selasky td->hcsplt &= ~HCSPLT_COMPSPLT; 10123eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_S_ANE; 10133eabad25SHans Petter Selasky } else { 10143eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_ANE; 10153eabad25SHans Petter Selasky } 10163eabad25SHans Petter Selasky 1017c2472ff8SHans Petter Selasky /* copy out control request */ 10189cfd0731SHans Petter Selasky usbd_copy_out(td->pc, 0, &req, sizeof(req)); 10199cfd0731SHans Petter Selasky 1020ce842cecSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), 10219cfd0731SHans Petter Selasky (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) | 10229cfd0731SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 10239cfd0731SHans Petter Selasky (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); 10249cfd0731SHans Petter Selasky 1025ce842cecSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); 1026b792f659SHans Petter Selasky 10273eabad25SHans Petter Selasky hcchar = td->hcchar; 1028db4300daSHans Petter Selasky hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK); 1029db4300daSHans Petter Selasky hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; 10309cfd0731SHans Petter Selasky 10319cfd0731SHans Petter Selasky /* must enable channel before writing data to FIFO */ 1032ce842cecSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar); 10339cfd0731SHans Petter Selasky 10349cfd0731SHans Petter Selasky /* transfer data into FIFO */ 10359cfd0731SHans Petter Selasky bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, 1036ce842cecSHans Petter Selasky DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4); 103708aa4c94SHans Petter Selasky 103808aa4c94SHans Petter Selasky /* wait until next slot before trying complete split */ 103908aa4c94SHans Petter Selasky td->tt_complete_slot = sc->sc_last_frame_num + 1; 10409cfd0731SHans Petter Selasky 1041b792f659SHans Petter Selasky /* store number of bytes transmitted */ 1042537aca95SHans Petter Selasky td->tx_bytes = sizeof(req); 104342fabcc3SHans Petter Selasky goto busy; 1044b792f659SHans Petter Selasky 1045b792f659SHans Petter Selasky send_cpkt: 1046db4300daSHans Petter Selasky /* free existing channel, if any */ 104708aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 1048db4300daSHans Petter Selasky 1049db4300daSHans Petter Selasky delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; 1050db4300daSHans Petter Selasky if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { 1051bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 1052db4300daSHans Petter Selasky goto busy; 1053bc990482SHans Petter Selasky } 1054db4300daSHans Petter Selasky delta = sc->sc_last_frame_num - td->tt_start_slot; 1055db4300daSHans Petter Selasky if (delta > DWC_OTG_TT_SLOT_MAX) { 1056db4300daSHans Petter Selasky /* we missed the service interval */ 1057db4300daSHans Petter Selasky if (td->ep_type != UE_ISOCHRONOUS) 1058db4300daSHans Petter Selasky td->error_any = 1; 1059db4300daSHans Petter Selasky goto complete; 1060db4300daSHans Petter Selasky } 1061db4300daSHans Petter Selasky /* allocate a new channel */ 106208aa4c94SHans Petter Selasky if (dwc_otg_host_channel_alloc(sc, td, 0)) { 1063db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 1064db4300daSHans Petter Selasky goto busy; 1065db4300daSHans Petter Selasky } 1066db4300daSHans Petter Selasky 106708aa4c94SHans Petter Selasky /* wait until next slot before trying complete split */ 106808aa4c94SHans Petter Selasky td->tt_complete_slot = sc->sc_last_frame_num + 1; 1069bc990482SHans Petter Selasky 10703eabad25SHans Petter Selasky td->hcsplt |= HCSPLT_COMPSPLT; 10713eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_ANE; 10723eabad25SHans Petter Selasky 1073ce842cecSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), 1074b792f659SHans Petter Selasky (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); 1075b792f659SHans Petter Selasky 1076ce842cecSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); 1077b792f659SHans Petter Selasky 10783eabad25SHans Petter Selasky hcchar = td->hcchar; 1079db4300daSHans Petter Selasky hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK); 1080db4300daSHans Petter Selasky hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; 1081b792f659SHans Petter Selasky 1082b792f659SHans Petter Selasky /* must enable channel before writing data to FIFO */ 1083ce842cecSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar); 1084b792f659SHans Petter Selasky 108542fabcc3SHans Petter Selasky busy: 1086537aca95SHans Petter Selasky return (1); /* busy */ 1087db4300daSHans Petter Selasky 108842fabcc3SHans Petter Selasky complete: 108908aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 109042fabcc3SHans Petter Selasky return (0); /* complete */ 10919cfd0731SHans Petter Selasky } 10929cfd0731SHans Petter Selasky 10939cfd0731SHans Petter Selasky static uint8_t 10942fe7ad87SHans Petter Selasky dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 1095dd03e19cSHans Petter Selasky { 1096dd03e19cSHans Petter Selasky struct usb_device_request req __aligned(4); 1097dd03e19cSHans Petter Selasky uint32_t temp; 1098dd03e19cSHans Petter Selasky uint16_t count; 1099dd03e19cSHans Petter Selasky 1100dd03e19cSHans Petter Selasky /* check endpoint status */ 1101dd03e19cSHans Petter Selasky 1102dd03e19cSHans Petter Selasky if (sc->sc_last_rx_status == 0) 1103dd03e19cSHans Petter Selasky goto not_complete; 1104dd03e19cSHans Petter Selasky 1105710764f7SHans Petter Selasky if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != 0) 1106dd03e19cSHans Petter Selasky goto not_complete; 1107dd03e19cSHans Petter Selasky 1108b4df5b00SHans Petter Selasky if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) != 1109b4df5b00SHans Petter Selasky GRXSTSRD_STP_DATA) { 1110b4df5b00SHans Petter Selasky if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) != 1111b4df5b00SHans Petter Selasky GRXSTSRD_STP_COMPLETE || td->remainder != 0) { 1112dd03e19cSHans Petter Selasky /* release FIFO */ 1113dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1114dd03e19cSHans Petter Selasky goto not_complete; 1115dd03e19cSHans Petter Selasky } 1116b4df5b00SHans Petter Selasky /* release FIFO */ 1117b4df5b00SHans Petter Selasky dwc_otg_common_rx_ack(sc); 1118b4df5b00SHans Petter Selasky return (0); /* complete */ 1119b4df5b00SHans Petter Selasky } 1120dd03e19cSHans Petter Selasky 1121b4df5b00SHans Petter Selasky if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != 1122b4df5b00SHans Petter Selasky GRXSTSRD_DPID_DATA0) { 1123dd03e19cSHans Petter Selasky /* release FIFO */ 1124dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1125dd03e19cSHans Petter Selasky goto not_complete; 1126dd03e19cSHans Petter Selasky } 1127dd03e19cSHans Petter Selasky 1128dd03e19cSHans Petter Selasky DPRINTFN(5, "GRXSTSR=0x%08x\n", sc->sc_last_rx_status); 1129dd03e19cSHans Petter Selasky 1130dd03e19cSHans Petter Selasky /* clear did stall */ 1131dd03e19cSHans Petter Selasky td->did_stall = 0; 1132dd03e19cSHans Petter Selasky 1133dd03e19cSHans Petter Selasky /* get the packet byte count */ 1134710764f7SHans Petter Selasky count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); 1135dd03e19cSHans Petter Selasky 1136dd03e19cSHans Petter Selasky if (count != sizeof(req)) { 1137dd03e19cSHans Petter Selasky DPRINTFN(0, "Unsupported SETUP packet " 1138dd03e19cSHans Petter Selasky "length, %d bytes\n", count); 1139dd03e19cSHans Petter Selasky /* release FIFO */ 1140dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1141dd03e19cSHans Petter Selasky goto not_complete; 1142dd03e19cSHans Petter Selasky } 1143dd03e19cSHans Petter Selasky 1144c2472ff8SHans Petter Selasky /* read FIFO */ 1145c2472ff8SHans Petter Selasky dwc_otg_read_fifo(sc, td->pc, 0, sizeof(req)); 1146dd03e19cSHans Petter Selasky 1147c2472ff8SHans Petter Selasky /* copy out control request */ 1148c2472ff8SHans Petter Selasky usbd_copy_out(td->pc, 0, &req, sizeof(req)); 1149dd03e19cSHans Petter Selasky 1150dd03e19cSHans Petter Selasky td->offset = sizeof(req); 1151dd03e19cSHans Petter Selasky td->remainder = 0; 1152dd03e19cSHans Petter Selasky 1153dd03e19cSHans Petter Selasky /* sneak peek the set address */ 1154dd03e19cSHans Petter Selasky if ((req.bmRequestType == UT_WRITE_DEVICE) && 1155dd03e19cSHans Petter Selasky (req.bRequest == UR_SET_ADDRESS)) { 1156dd03e19cSHans Petter Selasky /* must write address before ZLP */ 1157dd03e19cSHans Petter Selasky dwc_otg_set_address(sc, req.wValue[0] & 0x7F); 1158dd03e19cSHans Petter Selasky } 1159dd03e19cSHans Petter Selasky 1160dd03e19cSHans Petter Selasky /* don't send any data by default */ 1161b4df5b00SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0), DIEPCTL_EPDIS); 1162b4df5b00SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DOEPCTL_EPDIS); 1163dd03e19cSHans Petter Selasky 1164dd03e19cSHans Petter Selasky /* reset IN endpoint buffer */ 1165b4df5b00SHans Petter Selasky dwc_otg_tx_fifo_reset(sc, 1166710764f7SHans Petter Selasky GRSTCTL_TXFIFO(0) | 1167710764f7SHans Petter Selasky GRSTCTL_TXFFLSH); 1168dd03e19cSHans Petter Selasky 1169dd03e19cSHans Petter Selasky /* acknowledge RX status */ 1170dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1171b4df5b00SHans Petter Selasky td->did_stall = 1; 1172dd03e19cSHans Petter Selasky 1173dd03e19cSHans Petter Selasky not_complete: 1174dd03e19cSHans Petter Selasky /* abort any ongoing transfer, before enabling again */ 1175dd03e19cSHans Petter Selasky if (!td->did_stall) { 1176dd03e19cSHans Petter Selasky td->did_stall = 1; 1177dd03e19cSHans Petter Selasky 1178dd03e19cSHans Petter Selasky DPRINTFN(5, "stalling IN and OUT direction\n"); 1179dd03e19cSHans Petter Selasky 1180b4df5b00SHans Petter Selasky temp = sc->sc_out_ctl[0]; 1181b4df5b00SHans Petter Selasky 1182dd03e19cSHans Petter Selasky /* set stall after enabling endpoint */ 1183710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0), 1184710764f7SHans Petter Selasky temp | DOEPCTL_STALL); 1185dd03e19cSHans Petter Selasky 1186dd03e19cSHans Petter Selasky temp = sc->sc_in_ctl[0]; 1187dd03e19cSHans Petter Selasky 1188dd03e19cSHans Petter Selasky /* set stall assuming endpoint is enabled */ 1189710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0), 1190710764f7SHans Petter Selasky temp | DIEPCTL_STALL); 1191dd03e19cSHans Petter Selasky } 1192dd03e19cSHans Petter Selasky return (1); /* not complete */ 1193dd03e19cSHans Petter Selasky } 1194dd03e19cSHans Petter Selasky 1195dd03e19cSHans Petter Selasky static uint8_t 1196bc990482SHans Petter Selasky dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 1197bc990482SHans Petter Selasky { 1198bc990482SHans Petter Selasky uint8_t delta; 1199bc990482SHans Petter Selasky 1200bc990482SHans Petter Selasky delta = sc->sc_tmr_val - td->tmr_val; 1201bc990482SHans Petter Selasky if (delta >= 128) 1202bc990482SHans Petter Selasky return (1); /* busy */ 1203bc990482SHans Petter Selasky 1204bc990482SHans Petter Selasky td->tmr_val = sc->sc_tmr_val + td->tmr_res; 1205bc990482SHans Petter Selasky 1206bc990482SHans Petter Selasky /* set toggle, if any */ 1207bc990482SHans Petter Selasky if (td->set_toggle) { 1208bc990482SHans Petter Selasky td->set_toggle = 0; 1209bc990482SHans Petter Selasky td->toggle = 1; 1210bc990482SHans Petter Selasky } 1211bc990482SHans Petter Selasky return (0); 1212bc990482SHans Petter Selasky } 1213bc990482SHans Petter Selasky 1214bc990482SHans Petter Selasky static uint8_t 12152fe7ad87SHans Petter Selasky dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 12163eabad25SHans Petter Selasky { 1217e4344daeSHans Petter Selasky uint8_t frame_num = (uint8_t)sc->sc_last_frame_num; 1218e4344daeSHans Petter Selasky 1219db4300daSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS) { 1220bc990482SHans Petter Selasky /* non TT isochronous traffic */ 1221ce842cecSHans Petter Selasky if (frame_num & (td->tmr_res - 1)) 12223eabad25SHans Petter Selasky goto busy; 1223ce842cecSHans Petter Selasky if ((frame_num ^ td->tmr_val) & td->tmr_res) 1224ce842cecSHans Petter Selasky goto busy; 1225ce842cecSHans Petter Selasky td->tmr_val = td->tmr_res + sc->sc_last_frame_num; 12263eabad25SHans Petter Selasky td->toggle = 0; 1227e4344daeSHans Petter Selasky return (0); 1228db4300daSHans Petter Selasky } else if (td->ep_type == UE_INTERRUPT) { 122942fabcc3SHans Petter Selasky if (!td->tt_scheduled) 123042fabcc3SHans Petter Selasky goto busy; 123142fabcc3SHans Petter Selasky td->tt_scheduled = 0; 1232e4344daeSHans Petter Selasky return (0); 1233ed0ed9b4SHans Petter Selasky } else if (td->did_nak != 0) { 1234ed0ed9b4SHans Petter Selasky /* check if we should pause sending queries for 125us */ 1235ed0ed9b4SHans Petter Selasky if (td->tmr_res == frame_num) { 1236ed0ed9b4SHans Petter Selasky /* wait a bit */ 1237ed0ed9b4SHans Petter Selasky dwc_otg_enable_sof_irq(sc); 1238bc990482SHans Petter Selasky goto busy; 1239ed0ed9b4SHans Petter Selasky } 12403eabad25SHans Petter Selasky } else if (td->set_toggle) { 12413eabad25SHans Petter Selasky td->set_toggle = 0; 12423eabad25SHans Petter Selasky td->toggle = 1; 12433eabad25SHans Petter Selasky } 1244e4344daeSHans Petter Selasky /* query for data one more time */ 1245e4344daeSHans Petter Selasky td->tmr_res = frame_num; 1246e4344daeSHans Petter Selasky td->did_nak = 0; 12473eabad25SHans Petter Selasky return (0); 12483eabad25SHans Petter Selasky busy: 12493eabad25SHans Petter Selasky return (1); 12503eabad25SHans Petter Selasky } 12513eabad25SHans Petter Selasky 12523eabad25SHans Petter Selasky static uint8_t 1253ce842cecSHans Petter Selasky dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, 1254ce842cecSHans Petter Selasky uint8_t channel) 12559cfd0731SHans Petter Selasky { 12563eabad25SHans Petter Selasky uint32_t count; 12579cfd0731SHans Petter Selasky 12589cfd0731SHans Petter Selasky /* check endpoint status */ 12599cfd0731SHans Petter Selasky if (sc->sc_last_rx_status == 0) 126008aa4c94SHans Petter Selasky goto busy; 126108aa4c94SHans Petter Selasky 126208aa4c94SHans Petter Selasky if (channel >= DWC_OTG_MAX_CHANNELS) 126308aa4c94SHans Petter Selasky goto busy; 12649cfd0731SHans Petter Selasky 1265db4300daSHans Petter Selasky if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel) 126608aa4c94SHans Petter Selasky goto busy; 12679cfd0731SHans Petter Selasky 12689cfd0731SHans Petter Selasky switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) { 12699cfd0731SHans Petter Selasky case GRXSTSRH_IN_DATA: 12709cfd0731SHans Petter Selasky 1271e7162865SHans Petter Selasky DPRINTF("DATA ST=%d STATUS=0x%08x\n", 1272e7162865SHans Petter Selasky (int)td->state, (int)sc->sc_last_rx_status); 1273e7162865SHans Petter Selasky 127408aa4c94SHans Petter Selasky if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) { 1275e7162865SHans Petter Selasky /* 1276e7162865SHans Petter Selasky * When using SPLIT transactions on interrupt 1277e7162865SHans Petter Selasky * endpoints, sometimes data occurs twice. 1278e7162865SHans Petter Selasky */ 1279e7162865SHans Petter Selasky DPRINTF("Data already received\n"); 1280e7162865SHans Petter Selasky break; 1281e7162865SHans Petter Selasky } 12829cfd0731SHans Petter Selasky 12839cfd0731SHans Petter Selasky /* get the packet byte count */ 12849cfd0731SHans Petter Selasky count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); 12859cfd0731SHans Petter Selasky 1286ce842cecSHans Petter Selasky /* check for ISOCHRONOUS endpoint */ 1287ce842cecSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS) { 1288ce842cecSHans Petter Selasky if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != 1289ce842cecSHans Petter Selasky GRXSTSRD_DPID_DATA0) { 1290ce842cecSHans Petter Selasky /* more data to be received */ 1291bc990482SHans Petter Selasky td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE; 1292bc990482SHans Petter Selasky } else { 1293ce842cecSHans Petter Selasky /* all data received */ 1294bc990482SHans Petter Selasky td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; 1295bc990482SHans Petter Selasky /* verify the packet byte count */ 1296ce842cecSHans Petter Selasky if (count != td->remainder) { 1297bc990482SHans Petter Selasky /* we have a short packet */ 1298bc990482SHans Petter Selasky td->short_pkt = 1; 1299bc990482SHans Petter Selasky td->got_short = 1; 1300bc990482SHans Petter Selasky } 1301bc990482SHans Petter Selasky } 1302bc990482SHans Petter Selasky } else { 13039cfd0731SHans Petter Selasky /* verify the packet byte count */ 13049cfd0731SHans Petter Selasky if (count != td->max_packet_size) { 13059cfd0731SHans Petter Selasky if (count < td->max_packet_size) { 13069cfd0731SHans Petter Selasky /* we have a short packet */ 13079cfd0731SHans Petter Selasky td->short_pkt = 1; 1308beefefd4SHans Petter Selasky td->got_short = 1; 13099cfd0731SHans Petter Selasky } else { 13109cfd0731SHans Petter Selasky /* invalid USB packet */ 13119cfd0731SHans Petter Selasky td->error_any = 1; 13129cfd0731SHans Petter Selasky 13139cfd0731SHans Petter Selasky /* release FIFO */ 13149cfd0731SHans Petter Selasky dwc_otg_common_rx_ack(sc); 131542fabcc3SHans Petter Selasky goto complete; 13169cfd0731SHans Petter Selasky } 13179cfd0731SHans Petter Selasky } 1318bc990482SHans Petter Selasky td->toggle ^= 1; 1319bc990482SHans Petter Selasky td->tt_scheduled = 0; 1320bc990482SHans Petter Selasky } 13219cfd0731SHans Petter Selasky 13229cfd0731SHans Petter Selasky /* verify the packet byte count */ 13239cfd0731SHans Petter Selasky if (count > td->remainder) { 13249cfd0731SHans Petter Selasky /* invalid USB packet */ 13259cfd0731SHans Petter Selasky td->error_any = 1; 13269cfd0731SHans Petter Selasky 13279cfd0731SHans Petter Selasky /* release FIFO */ 13289cfd0731SHans Petter Selasky dwc_otg_common_rx_ack(sc); 132942fabcc3SHans Petter Selasky goto complete; 13309cfd0731SHans Petter Selasky } 13319cfd0731SHans Petter Selasky 1332c2472ff8SHans Petter Selasky /* read data from FIFO */ 1333c2472ff8SHans Petter Selasky dwc_otg_read_fifo(sc, td->pc, td->offset, count); 1334beefefd4SHans Petter Selasky 13359cfd0731SHans Petter Selasky td->remainder -= count; 13369cfd0731SHans Petter Selasky td->offset += count; 133708aa4c94SHans Petter Selasky sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY; 1338beefefd4SHans Petter Selasky break; 1339beefefd4SHans Petter Selasky default: 1340beefefd4SHans Petter Selasky break; 1341beefefd4SHans Petter Selasky } 13429cfd0731SHans Petter Selasky /* release FIFO */ 13439cfd0731SHans Petter Selasky dwc_otg_common_rx_ack(sc); 134408aa4c94SHans Petter Selasky busy: 134508aa4c94SHans Petter Selasky return (0); 134608aa4c94SHans Petter Selasky complete: 134708aa4c94SHans Petter Selasky return (1); 134808aa4c94SHans Petter Selasky } 13499cfd0731SHans Petter Selasky 135008aa4c94SHans Petter Selasky static uint8_t 135108aa4c94SHans Petter Selasky dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 135208aa4c94SHans Petter Selasky { 1353ce842cecSHans Petter Selasky uint32_t hcint = 0; 135408aa4c94SHans Petter Selasky uint32_t hcchar; 135508aa4c94SHans Petter Selasky uint8_t delta; 135608aa4c94SHans Petter Selasky uint8_t channel; 1357ce842cecSHans Petter Selasky uint8_t x; 135808aa4c94SHans Petter Selasky 1359ce842cecSHans Petter Selasky for (x = 0; x != td->max_packet_count; x++) { 1360ce842cecSHans Petter Selasky channel = td->channel[x]; 1361ce842cecSHans Petter Selasky if (channel >= DWC_OTG_MAX_CHANNELS) 1362ce842cecSHans Petter Selasky continue; 1363ce842cecSHans Petter Selasky hcint |= sc->sc_chan_state[channel].hcint; 136408aa4c94SHans Petter Selasky 136508aa4c94SHans Petter Selasky DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", 136608aa4c94SHans Petter Selasky channel, td->state, hcint, 136708aa4c94SHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)), 136808aa4c94SHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel))); 136908aa4c94SHans Petter Selasky 137008aa4c94SHans Petter Selasky /* check interrupt bits */ 137108aa4c94SHans Petter Selasky if (hcint & (HCINT_RETRY | 137208aa4c94SHans Petter Selasky HCINT_ACK | HCINT_NYET)) { 137308aa4c94SHans Petter Selasky /* give success bits priority over failure bits */ 137408aa4c94SHans Petter Selasky } else if (hcint & HCINT_STALL) { 137508aa4c94SHans Petter Selasky DPRINTF("CH=%d STALL\n", channel); 137608aa4c94SHans Petter Selasky td->error_stall = 1; 137708aa4c94SHans Petter Selasky td->error_any = 1; 137808aa4c94SHans Petter Selasky goto complete; 137908aa4c94SHans Petter Selasky } else if (hcint & HCINT_ERRORS) { 138008aa4c94SHans Petter Selasky DPRINTF("CH=%d ERROR\n", channel); 138108aa4c94SHans Petter Selasky td->errcnt++; 138208aa4c94SHans Petter Selasky if (td->hcsplt != 0 || td->errcnt >= 3) { 138308aa4c94SHans Petter Selasky if (td->ep_type != UE_ISOCHRONOUS) { 138408aa4c94SHans Petter Selasky td->error_any = 1; 138508aa4c94SHans Petter Selasky goto complete; 138608aa4c94SHans Petter Selasky } 138708aa4c94SHans Petter Selasky } 138808aa4c94SHans Petter Selasky } 138908aa4c94SHans Petter Selasky 139008aa4c94SHans Petter Selasky /* check channels for data, if any */ 1391ce842cecSHans Petter Selasky if (dwc_otg_host_data_rx_sub(sc, td, channel)) 139208aa4c94SHans Petter Selasky goto complete; 139308aa4c94SHans Petter Selasky 139408aa4c94SHans Petter Selasky /* refresh interrupt status */ 1395ce842cecSHans Petter Selasky hcint |= sc->sc_chan_state[channel].hcint; 139608aa4c94SHans Petter Selasky 1397db4300daSHans Petter Selasky if (hcint & (HCINT_ERRORS | HCINT_RETRY | 1398db4300daSHans Petter Selasky HCINT_ACK | HCINT_NYET)) { 1399db4300daSHans Petter Selasky if (!(hcint & HCINT_ERRORS)) 1400db4300daSHans Petter Selasky td->errcnt = 0; 1401db4300daSHans Petter Selasky } 140208aa4c94SHans Petter Selasky } 1403db4300daSHans Petter Selasky 14043eabad25SHans Petter Selasky switch (td->state) { 1405b792f659SHans Petter Selasky case DWC_CHAN_ST_START: 14063eabad25SHans Petter Selasky if (td->hcsplt != 0) 1407b792f659SHans Petter Selasky goto receive_spkt; 14083eabad25SHans Petter Selasky else 14093eabad25SHans Petter Selasky goto receive_pkt; 14103eabad25SHans Petter Selasky 14113eabad25SHans Petter Selasky case DWC_CHAN_ST_WAIT_ANE: 14123eabad25SHans Petter Selasky if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 14132c9288dfSHans Petter Selasky if (td->ep_type == UE_INTERRUPT) { 14142c9288dfSHans Petter Selasky /* 14152c9288dfSHans Petter Selasky * The USB specification does not 14162c9288dfSHans Petter Selasky * mandate a particular data toggle 14172c9288dfSHans Petter Selasky * value for USB INTERRUPT 14182c9288dfSHans Petter Selasky * transfers. Switch the data toggle 14192c9288dfSHans Petter Selasky * value to receive the packet 14202c9288dfSHans Petter Selasky * correctly: 14212c9288dfSHans Petter Selasky */ 14222c9288dfSHans Petter Selasky if (hcint & HCINT_DATATGLERR) { 14232c9288dfSHans Petter Selasky DPRINTF("Retrying packet due to " 14242c9288dfSHans Petter Selasky "data toggle error\n"); 14252c9288dfSHans Petter Selasky td->toggle ^= 1; 14262c9288dfSHans Petter Selasky goto receive_pkt; 14272c9288dfSHans Petter Selasky } 1428ce842cecSHans Petter Selasky } else if (td->ep_type == UE_ISOCHRONOUS) { 142936d2d637SHans Petter Selasky if (td->hcsplt != 0) { 143036d2d637SHans Petter Selasky /* 143136d2d637SHans Petter Selasky * Sometimes the complete 143236d2d637SHans Petter Selasky * split packet may be queued 143336d2d637SHans Petter Selasky * too early and the 143436d2d637SHans Petter Selasky * transaction translator will 143536d2d637SHans Petter Selasky * return a NAK. Ignore 143636d2d637SHans Petter Selasky * this message and retry the 143736d2d637SHans Petter Selasky * complete split instead. 143836d2d637SHans Petter Selasky */ 143936d2d637SHans Petter Selasky DPRINTF("Retrying complete split\n"); 144036d2d637SHans Petter Selasky goto receive_pkt; 144136d2d637SHans Petter Selasky } 1442ce842cecSHans Petter Selasky goto complete; 14432c9288dfSHans Petter Selasky } 1444ed0ed9b4SHans Petter Selasky td->did_nak = 1; 1445bc990482SHans Petter Selasky td->tt_scheduled = 0; 14463eabad25SHans Petter Selasky if (td->hcsplt != 0) 14473eabad25SHans Petter Selasky goto receive_spkt; 14483eabad25SHans Petter Selasky else 1449b792f659SHans Petter Selasky goto receive_pkt; 1450db4300daSHans Petter Selasky } else if (hcint & HCINT_NYET) { 1451db4300daSHans Petter Selasky if (td->hcsplt != 0) { 1452db4300daSHans Petter Selasky /* try again */ 1453db4300daSHans Petter Selasky goto receive_pkt; 1454db4300daSHans Petter Selasky } else { 1455db4300daSHans Petter Selasky /* not a valid token for IN endpoints */ 1456db4300daSHans Petter Selasky td->error_any = 1; 145742fabcc3SHans Petter Selasky goto complete; 1458bc990482SHans Petter Selasky } 1459db4300daSHans Petter Selasky } else if (hcint & HCINT_ACK) { 1460897b7965SHans Petter Selasky /* wait for data - ACK arrived first */ 1461897b7965SHans Petter Selasky if (!(hcint & HCINT_SOFTWARE_ONLY)) 1462897b7965SHans Petter Selasky goto busy; 1463897b7965SHans Petter Selasky 1464db4300daSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS) { 1465bc990482SHans Petter Selasky /* check if we are complete */ 1466ce842cecSHans Petter Selasky if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) { 146742fabcc3SHans Petter Selasky goto complete; 1468a38abbfbSHans Petter Selasky } else if (td->hcsplt != 0) { 1469a38abbfbSHans Petter Selasky goto receive_pkt; 1470ce842cecSHans Petter Selasky } else { 1471ce842cecSHans Petter Selasky /* get more packets */ 1472ce842cecSHans Petter Selasky goto busy; 1473db4300daSHans Petter Selasky } 1474bc990482SHans Petter Selasky } else { 1475b792f659SHans Petter Selasky /* check if we are complete */ 1476b792f659SHans Petter Selasky if ((td->remainder == 0) || (td->got_short != 0)) { 14773eabad25SHans Petter Selasky if (td->short_pkt) 147842fabcc3SHans Petter Selasky goto complete; 14793eabad25SHans Petter Selasky 1480b792f659SHans Petter Selasky /* 1481b792f659SHans Petter Selasky * Else need to receive a zero length 1482b792f659SHans Petter Selasky * packet. 1483b792f659SHans Petter Selasky */ 1484b792f659SHans Petter Selasky } 1485bc990482SHans Petter Selasky td->tt_scheduled = 0; 14864541f273SHans Petter Selasky td->did_nak = 0; 14873eabad25SHans Petter Selasky if (td->hcsplt != 0) 1488b792f659SHans Petter Selasky goto receive_spkt; 14893eabad25SHans Petter Selasky else 1490b792f659SHans Petter Selasky goto receive_pkt; 1491b792f659SHans Petter Selasky } 1492bc990482SHans Petter Selasky } 1493b792f659SHans Petter Selasky break; 14943eabad25SHans Petter Selasky 1495b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_S_ANE: 1496bc990482SHans Petter Selasky /* 1497bc990482SHans Petter Selasky * NOTE: The DWC OTG hardware provides a fake ACK in 1498bc990482SHans Petter Selasky * case of interrupt and isochronous transfers: 1499bc990482SHans Petter Selasky */ 15003eabad25SHans Petter Selasky if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 1501ed0ed9b4SHans Petter Selasky td->did_nak = 1; 1502bc990482SHans Petter Selasky td->tt_scheduled = 0; 1503b792f659SHans Petter Selasky goto receive_spkt; 1504db4300daSHans Petter Selasky } else if (hcint & HCINT_NYET) { 1505db4300daSHans Petter Selasky td->tt_scheduled = 0; 1506db4300daSHans Petter Selasky goto receive_spkt; 15074541f273SHans Petter Selasky } else if (hcint & HCINT_ACK) { 15084541f273SHans Petter Selasky td->did_nak = 0; 1509b792f659SHans Petter Selasky goto receive_pkt; 15104541f273SHans Petter Selasky } 1511b792f659SHans Petter Selasky break; 15123eabad25SHans Petter Selasky 1513bc990482SHans Petter Selasky case DWC_CHAN_ST_WAIT_C_PKT: 15143eabad25SHans Petter Selasky goto receive_pkt; 15153eabad25SHans Petter Selasky 1516b792f659SHans Petter Selasky default: 1517b792f659SHans Petter Selasky break; 1518b792f659SHans Petter Selasky } 15193eabad25SHans Petter Selasky goto busy; 1520b792f659SHans Petter Selasky 1521b792f659SHans Petter Selasky receive_pkt: 1522db4300daSHans Petter Selasky /* free existing channel, if any */ 152308aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 1524db4300daSHans Petter Selasky 1525e7162865SHans Petter Selasky if (td->hcsplt != 0) { 1526db4300daSHans Petter Selasky delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; 1527db4300daSHans Petter Selasky if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { 1528a38abbfbSHans Petter Selasky if (td->ep_type != UE_ISOCHRONOUS) { 1529bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 1530db4300daSHans Petter Selasky goto busy; 1531e7162865SHans Petter Selasky } 1532a38abbfbSHans Petter Selasky } 1533db4300daSHans Petter Selasky delta = sc->sc_last_frame_num - td->tt_start_slot; 1534db4300daSHans Petter Selasky if (delta > DWC_OTG_TT_SLOT_MAX) { 1535dea9afcfSHans Petter Selasky if (td->ep_type != UE_ISOCHRONOUS) { 1536db4300daSHans Petter Selasky /* we missed the service interval */ 1537db4300daSHans Petter Selasky td->error_any = 1; 153808aa4c94SHans Petter Selasky } 1539db4300daSHans Petter Selasky goto complete; 1540db4300daSHans Petter Selasky } 1541db4300daSHans Petter Selasky /* complete split */ 1542db4300daSHans Petter Selasky td->hcsplt |= HCSPLT_COMPSPLT; 1543ce842cecSHans Petter Selasky } else if (dwc_otg_host_rate_check(sc, td)) { 1544db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 1545db4300daSHans Petter Selasky goto busy; 1546db4300daSHans Petter Selasky } 1547db4300daSHans Petter Selasky 1548db4300daSHans Petter Selasky /* allocate a new channel */ 154908aa4c94SHans Petter Selasky if (dwc_otg_host_channel_alloc(sc, td, 0)) { 1550db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 1551db4300daSHans Petter Selasky goto busy; 1552db4300daSHans Petter Selasky } 1553db4300daSHans Petter Selasky 1554bc990482SHans Petter Selasky /* set toggle, if any */ 1555bc990482SHans Petter Selasky if (td->set_toggle) { 1556bc990482SHans Petter Selasky td->set_toggle = 0; 1557bc990482SHans Petter Selasky td->toggle = 1; 1558bc990482SHans Petter Selasky } 1559db4300daSHans Petter Selasky 15603eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_ANE; 15613eabad25SHans Petter Selasky 1562ce842cecSHans Petter Selasky for (x = 0; x != td->max_packet_count; x++) { 1563ce842cecSHans Petter Selasky channel = td->channel[x]; 1564ce842cecSHans Petter Selasky 1565537aca95SHans Petter Selasky /* receive one packet */ 1566db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 1567db4300daSHans Petter Selasky (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) | 1568537aca95SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 15699cfd0731SHans Petter Selasky (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : 15709cfd0731SHans Petter Selasky (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); 15719cfd0731SHans Petter Selasky 1572db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); 1573bc990482SHans Petter Selasky 15743eabad25SHans Petter Selasky hcchar = td->hcchar; 15753eabad25SHans Petter Selasky hcchar |= HCCHAR_EPDIR_IN; 15769cfd0731SHans Petter Selasky 1577a38abbfbSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS) { 1578a38abbfbSHans Petter Selasky if (td->hcsplt != 0) { 1579a38abbfbSHans Petter Selasky /* continously buffer */ 1580a38abbfbSHans Petter Selasky if (sc->sc_last_frame_num & 1) 1581a38abbfbSHans Petter Selasky hcchar &= ~HCCHAR_ODDFRM; 1582a38abbfbSHans Petter Selasky else 1583a38abbfbSHans Petter Selasky hcchar |= HCCHAR_ODDFRM; 1584a38abbfbSHans Petter Selasky } else { 1585a38abbfbSHans Petter Selasky /* multi buffer, if any */ 1586a38abbfbSHans Petter Selasky if (sc->sc_last_frame_num & 1) 1587db4300daSHans Petter Selasky hcchar |= HCCHAR_ODDFRM; 1588db4300daSHans Petter Selasky else 1589db4300daSHans Petter Selasky hcchar &= ~HCCHAR_ODDFRM; 1590a38abbfbSHans Petter Selasky } 1591a38abbfbSHans Petter Selasky } else { 1592a38abbfbSHans Petter Selasky hcchar &= ~HCCHAR_ODDFRM; 1593a38abbfbSHans Petter Selasky } 1594db4300daSHans Petter Selasky 1595db4300daSHans Petter Selasky /* must enable channel before data can be received */ 1596db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); 1597ce842cecSHans Petter Selasky } 159808aa4c94SHans Petter Selasky /* wait until next slot before trying complete split */ 159908aa4c94SHans Petter Selasky td->tt_complete_slot = sc->sc_last_frame_num + 1; 160008aa4c94SHans Petter Selasky 16013eabad25SHans Petter Selasky goto busy; 1602b792f659SHans Petter Selasky 1603b792f659SHans Petter Selasky receive_spkt: 1604db4300daSHans Petter Selasky /* free existing channel(s), if any */ 160508aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 1606db4300daSHans Petter Selasky 1607db4300daSHans Petter Selasky delta = td->tt_start_slot - sc->sc_last_frame_num - 1; 1608db4300daSHans Petter Selasky if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { 1609bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_START; 16103eabad25SHans Petter Selasky goto busy; 1611b792f659SHans Petter Selasky } 1612db4300daSHans Petter Selasky delta = sc->sc_last_frame_num - td->tt_start_slot; 1613db4300daSHans Petter Selasky if (delta > 5) { 1614db4300daSHans Petter Selasky /* missed it */ 1615db4300daSHans Petter Selasky td->tt_scheduled = 0; 1616bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_START; 1617db4300daSHans Petter Selasky goto busy; 1618bc990482SHans Petter Selasky } 1619e7162865SHans Petter Selasky 1620db4300daSHans Petter Selasky /* allocate a new channel */ 162108aa4c94SHans Petter Selasky if (dwc_otg_host_channel_alloc(sc, td, 0)) { 1622db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_START; 1623db4300daSHans Petter Selasky goto busy; 1624db4300daSHans Petter Selasky } 1625db4300daSHans Petter Selasky 1626ce842cecSHans Petter Selasky channel = td->channel[0]; 1627e7162865SHans Petter Selasky 16283eabad25SHans Petter Selasky td->hcsplt &= ~HCSPLT_COMPSPLT; 16293eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_S_ANE; 16303eabad25SHans Petter Selasky 1631db4300daSHans Petter Selasky /* receive one packet */ 1632db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 1633db4300daSHans Petter Selasky (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)); 1634db4300daSHans Petter Selasky 1635db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); 1636db4300daSHans Petter Selasky 1637db4300daSHans Petter Selasky /* send after next SOF event */ 1638a529288dSHans Petter Selasky if ((sc->sc_last_frame_num & 1) == 0 && 1639ce842cecSHans Petter Selasky td->ep_type == UE_ISOCHRONOUS) 1640db4300daSHans Petter Selasky td->hcchar |= HCCHAR_ODDFRM; 1641db4300daSHans Petter Selasky else 1642db4300daSHans Petter Selasky td->hcchar &= ~HCCHAR_ODDFRM; 1643b792f659SHans Petter Selasky 16443eabad25SHans Petter Selasky hcchar = td->hcchar; 16453eabad25SHans Petter Selasky hcchar |= HCCHAR_EPDIR_IN; 1646b792f659SHans Petter Selasky 164708aa4c94SHans Petter Selasky /* wait until next slot before trying complete split */ 164808aa4c94SHans Petter Selasky td->tt_complete_slot = sc->sc_last_frame_num + 1; 164908aa4c94SHans Petter Selasky 1650b792f659SHans Petter Selasky /* must enable channel before data can be received */ 1651db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); 16523eabad25SHans Petter Selasky busy: 1653b792f659SHans Petter Selasky return (1); /* busy */ 1654db4300daSHans Petter Selasky 165542fabcc3SHans Petter Selasky complete: 165608aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 165742fabcc3SHans Petter Selasky return (0); /* complete */ 16589cfd0731SHans Petter Selasky } 16599cfd0731SHans Petter Selasky 16609cfd0731SHans Petter Selasky static uint8_t 16612fe7ad87SHans Petter Selasky dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 1662dd03e19cSHans Petter Selasky { 1663dd03e19cSHans Petter Selasky uint32_t temp; 1664dd03e19cSHans Petter Selasky uint16_t count; 1665dd03e19cSHans Petter Selasky uint8_t got_short; 1666dd03e19cSHans Petter Selasky 1667dd03e19cSHans Petter Selasky got_short = 0; 1668dd03e19cSHans Petter Selasky 1669dd03e19cSHans Petter Selasky /* check endpoint status */ 1670dd03e19cSHans Petter Selasky if (sc->sc_last_rx_status == 0) 1671dd03e19cSHans Petter Selasky goto not_complete; 1672dd03e19cSHans Petter Selasky 1673710764f7SHans Petter Selasky if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->ep_no) 1674dd03e19cSHans Petter Selasky goto not_complete; 1675dd03e19cSHans Petter Selasky 1676dd03e19cSHans Petter Selasky /* check for SETUP packet */ 1677710764f7SHans Petter Selasky if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) == 1678b4df5b00SHans Petter Selasky GRXSTSRD_STP_DATA || 1679b4df5b00SHans Petter Selasky (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) == 1680b4df5b00SHans Petter Selasky GRXSTSRD_STP_COMPLETE) { 1681dd03e19cSHans Petter Selasky if (td->remainder == 0) { 1682dd03e19cSHans Petter Selasky /* 1683dd03e19cSHans Petter Selasky * We are actually complete and have 1684dd03e19cSHans Petter Selasky * received the next SETUP 1685dd03e19cSHans Petter Selasky */ 1686dd03e19cSHans Petter Selasky DPRINTFN(5, "faking complete\n"); 1687dd03e19cSHans Petter Selasky return (0); /* complete */ 1688dd03e19cSHans Petter Selasky } 1689dd03e19cSHans Petter Selasky /* 1690dd03e19cSHans Petter Selasky * USB Host Aborted the transfer. 1691dd03e19cSHans Petter Selasky */ 16929cfd0731SHans Petter Selasky td->error_any = 1; 1693dd03e19cSHans Petter Selasky return (0); /* complete */ 1694dd03e19cSHans Petter Selasky } 1695dd03e19cSHans Petter Selasky 1696710764f7SHans Petter Selasky if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) != 1697710764f7SHans Petter Selasky GRXSTSRD_OUT_DATA) { 1698dd03e19cSHans Petter Selasky /* release FIFO */ 1699dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1700dd03e19cSHans Petter Selasky goto not_complete; 1701dd03e19cSHans Petter Selasky } 1702dd03e19cSHans Petter Selasky 1703dd03e19cSHans Petter Selasky /* get the packet byte count */ 1704710764f7SHans Petter Selasky count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); 1705dd03e19cSHans Petter Selasky 1706dd03e19cSHans Petter Selasky /* verify the packet byte count */ 1707dd03e19cSHans Petter Selasky if (count != td->max_packet_size) { 1708dd03e19cSHans Petter Selasky if (count < td->max_packet_size) { 1709dd03e19cSHans Petter Selasky /* we have a short packet */ 1710dd03e19cSHans Petter Selasky td->short_pkt = 1; 1711dd03e19cSHans Petter Selasky got_short = 1; 1712dd03e19cSHans Petter Selasky } else { 1713dd03e19cSHans Petter Selasky /* invalid USB packet */ 17149cfd0731SHans Petter Selasky td->error_any = 1; 1715dd03e19cSHans Petter Selasky 1716dd03e19cSHans Petter Selasky /* release FIFO */ 1717dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1718dd03e19cSHans Petter Selasky return (0); /* we are complete */ 1719dd03e19cSHans Petter Selasky } 1720dd03e19cSHans Petter Selasky } 1721dd03e19cSHans Petter Selasky /* verify the packet byte count */ 1722dd03e19cSHans Petter Selasky if (count > td->remainder) { 1723dd03e19cSHans Petter Selasky /* invalid USB packet */ 17249cfd0731SHans Petter Selasky td->error_any = 1; 1725dd03e19cSHans Petter Selasky 1726dd03e19cSHans Petter Selasky /* release FIFO */ 1727dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1728dd03e19cSHans Petter Selasky return (0); /* we are complete */ 1729dd03e19cSHans Petter Selasky } 1730dd03e19cSHans Petter Selasky 1731c2472ff8SHans Petter Selasky /* read data from FIFO */ 1732c2472ff8SHans Petter Selasky dwc_otg_read_fifo(sc, td->pc, td->offset, count); 1733c2472ff8SHans Petter Selasky 1734dd03e19cSHans Petter Selasky td->remainder -= count; 1735dd03e19cSHans Petter Selasky td->offset += count; 1736dd03e19cSHans Petter Selasky 1737dd03e19cSHans Petter Selasky /* release FIFO */ 1738dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 1739dd03e19cSHans Petter Selasky 174068691fe0SHans Petter Selasky temp = sc->sc_out_ctl[td->ep_no]; 174168691fe0SHans Petter Selasky 174268691fe0SHans Petter Selasky /* check for isochronous mode */ 174368691fe0SHans Petter Selasky if ((temp & DIEPCTL_EPTYPE_MASK) == 174468691fe0SHans Petter Selasky (DIEPCTL_EPTYPE_ISOC << DIEPCTL_EPTYPE_SHIFT)) { 174568691fe0SHans Petter Selasky /* toggle odd or even frame bit */ 174668691fe0SHans Petter Selasky if (temp & DIEPCTL_SETD1PID) { 174768691fe0SHans Petter Selasky temp &= ~DIEPCTL_SETD1PID; 174868691fe0SHans Petter Selasky temp |= DIEPCTL_SETD0PID; 174968691fe0SHans Petter Selasky } else { 175068691fe0SHans Petter Selasky temp &= ~DIEPCTL_SETD0PID; 175168691fe0SHans Petter Selasky temp |= DIEPCTL_SETD1PID; 175268691fe0SHans Petter Selasky } 175368691fe0SHans Petter Selasky sc->sc_out_ctl[td->ep_no] = temp; 175468691fe0SHans Petter Selasky } 175568691fe0SHans Petter Selasky 1756dd03e19cSHans Petter Selasky /* check if we are complete */ 1757dd03e19cSHans Petter Selasky if ((td->remainder == 0) || got_short) { 1758dd03e19cSHans Petter Selasky if (td->short_pkt) { 1759dd03e19cSHans Petter Selasky /* we are complete */ 1760dd03e19cSHans Petter Selasky return (0); 1761dd03e19cSHans Petter Selasky } 1762dd03e19cSHans Petter Selasky /* else need to receive a zero length packet */ 1763dd03e19cSHans Petter Selasky } 1764dd03e19cSHans Petter Selasky 1765dd03e19cSHans Petter Selasky not_complete: 1766dd03e19cSHans Petter Selasky 1767dd03e19cSHans Petter Selasky /* enable SETUP and transfer complete interrupt */ 1768dd03e19cSHans Petter Selasky if (td->ep_no == 0) { 1769710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), 1770b4df5b00SHans Petter Selasky DXEPTSIZ_SET_MULTI(3) | 1771710764f7SHans Petter Selasky DXEPTSIZ_SET_NPKT(1) | 1772710764f7SHans Petter Selasky DXEPTSIZ_SET_NBYTES(td->max_packet_size)); 1773dd03e19cSHans Petter Selasky } else { 1774dd03e19cSHans Petter Selasky /* allow reception of multiple packets */ 1775710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(td->ep_no), 1776710764f7SHans Petter Selasky DXEPTSIZ_SET_MULTI(1) | 1777710764f7SHans Petter Selasky DXEPTSIZ_SET_NPKT(4) | 1778710764f7SHans Petter Selasky DXEPTSIZ_SET_NBYTES(4 * 1779dd03e19cSHans Petter Selasky ((td->max_packet_size + 3) & ~3))); 1780dd03e19cSHans Petter Selasky } 1781b4df5b00SHans Petter Selasky temp = sc->sc_out_ctl[td->ep_no]; 1782b4df5b00SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp | 1783b4df5b00SHans Petter Selasky DOEPCTL_EPENA | DOEPCTL_CNAK); 1784b4df5b00SHans Petter Selasky 1785dd03e19cSHans Petter Selasky return (1); /* not complete */ 1786dd03e19cSHans Petter Selasky } 1787dd03e19cSHans Petter Selasky 1788dd03e19cSHans Petter Selasky static uint8_t 17892fe7ad87SHans Petter Selasky dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 17909cfd0731SHans Petter Selasky { 17919cfd0731SHans Petter Selasky uint32_t count; 17923eabad25SHans Petter Selasky uint32_t hcint; 17933eabad25SHans Petter Selasky uint32_t hcchar; 1794db4300daSHans Petter Selasky uint8_t delta; 1795db4300daSHans Petter Selasky uint8_t channel; 1796ce842cecSHans Petter Selasky uint8_t x; 17979cfd0731SHans Petter Selasky 179808aa4c94SHans Petter Selasky dwc_otg_host_dump_rx(sc, td); 179908aa4c94SHans Petter Selasky 1800ce842cecSHans Petter Selasky /* check that last channel is complete */ 1801ce842cecSHans Petter Selasky channel = td->channel[td->npkt]; 18029cfd0731SHans Petter Selasky 1803db4300daSHans Petter Selasky if (channel < DWC_OTG_MAX_CHANNELS) { 1804db4300daSHans Petter Selasky hcint = sc->sc_chan_state[channel].hcint; 1805b792f659SHans Petter Selasky 1806b792f659SHans Petter Selasky DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", 1807db4300daSHans Petter Selasky channel, td->state, hcint, 1808db4300daSHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)), 1809db4300daSHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel))); 18109cfd0731SHans Petter Selasky 1811e7162865SHans Petter Selasky if (hcint & (HCINT_RETRY | 1812e7162865SHans Petter Selasky HCINT_ACK | HCINT_NYET)) { 1813e7162865SHans Petter Selasky /* give success bits priority over failure bits */ 1814e7162865SHans Petter Selasky } else if (hcint & HCINT_STALL) { 1815db4300daSHans Petter Selasky DPRINTF("CH=%d STALL\n", channel); 18169cfd0731SHans Petter Selasky td->error_stall = 1; 18179cfd0731SHans Petter Selasky td->error_any = 1; 181842fabcc3SHans Petter Selasky goto complete; 1819e7162865SHans Petter Selasky } else if (hcint & HCINT_ERRORS) { 1820db4300daSHans Petter Selasky DPRINTF("CH=%d ERROR\n", channel); 18213eabad25SHans Petter Selasky td->errcnt++; 18223eabad25SHans Petter Selasky if (td->hcsplt != 0 || td->errcnt >= 3) { 18239cfd0731SHans Petter Selasky td->error_any = 1; 182442fabcc3SHans Petter Selasky goto complete; 18259cfd0731SHans Petter Selasky } 18263eabad25SHans Petter Selasky } 18279cfd0731SHans Petter Selasky 18283eabad25SHans Petter Selasky if (hcint & (HCINT_ERRORS | HCINT_RETRY | 18293eabad25SHans Petter Selasky HCINT_ACK | HCINT_NYET)) { 18303eabad25SHans Petter Selasky if (!(hcint & HCINT_ERRORS)) 18313eabad25SHans Petter Selasky td->errcnt = 0; 1832537aca95SHans Petter Selasky } 183308aa4c94SHans Petter Selasky } else { 183408aa4c94SHans Petter Selasky hcint = 0; 183508aa4c94SHans Petter Selasky } 18369cfd0731SHans Petter Selasky 18373eabad25SHans Petter Selasky switch (td->state) { 1838b792f659SHans Petter Selasky case DWC_CHAN_ST_START: 1839b792f659SHans Petter Selasky goto send_pkt; 18403eabad25SHans Petter Selasky 1841b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_ANE: 18423eabad25SHans Petter Selasky if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 1843ed0ed9b4SHans Petter Selasky td->did_nak = 1; 1844bc990482SHans Petter Selasky td->tt_scheduled = 0; 1845b792f659SHans Petter Selasky goto send_pkt; 1846e2192fdfSHans Petter Selasky } else if (hcint & (HCINT_ACK | HCINT_NYET)) { 1847b792f659SHans Petter Selasky td->offset += td->tx_bytes; 1848b792f659SHans Petter Selasky td->remainder -= td->tx_bytes; 1849b792f659SHans Petter Selasky td->toggle ^= 1; 1850e4344daeSHans Petter Selasky /* check if next response will be a NAK */ 1851e4344daeSHans Petter Selasky if (hcint & HCINT_NYET) 1852e4344daeSHans Petter Selasky td->did_nak = 1; 1853e4344daeSHans Petter Selasky else 18544541f273SHans Petter Selasky td->did_nak = 0; 1855bc990482SHans Petter Selasky td->tt_scheduled = 0; 1856b792f659SHans Petter Selasky 1857b792f659SHans Petter Selasky /* check remainder */ 1858b792f659SHans Petter Selasky if (td->remainder == 0) { 18593eabad25SHans Petter Selasky if (td->short_pkt) 186042fabcc3SHans Petter Selasky goto complete; 1861b792f659SHans Petter Selasky 18623eabad25SHans Petter Selasky /* 18633eabad25SHans Petter Selasky * Else we need to transmit a short 18643eabad25SHans Petter Selasky * packet: 18653eabad25SHans Petter Selasky */ 1866b792f659SHans Petter Selasky } 1867b792f659SHans Petter Selasky goto send_pkt; 1868b792f659SHans Petter Selasky } 1869b792f659SHans Petter Selasky break; 1870bc990482SHans Petter Selasky 1871b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_S_ANE: 18723eabad25SHans Petter Selasky if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 1873ed0ed9b4SHans Petter Selasky td->did_nak = 1; 1874bc990482SHans Petter Selasky td->tt_scheduled = 0; 1875b792f659SHans Petter Selasky goto send_pkt; 18764541f273SHans Petter Selasky } else if (hcint & (HCINT_ACK | HCINT_NYET)) { 18774541f273SHans Petter Selasky td->did_nak = 0; 1878b792f659SHans Petter Selasky goto send_cpkt; 18794541f273SHans Petter Selasky } 1880b792f659SHans Petter Selasky break; 1881bc990482SHans Petter Selasky 1882b792f659SHans Petter Selasky case DWC_CHAN_ST_WAIT_C_ANE: 1883897b7965SHans Petter Selasky if (hcint & HCINT_NYET) { 1884a3bfcf3eSHans Petter Selasky goto send_cpkt; 1885897b7965SHans Petter Selasky } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { 1886ed0ed9b4SHans Petter Selasky td->did_nak = 1; 1887bc990482SHans Petter Selasky td->tt_scheduled = 0; 1888a3bfcf3eSHans Petter Selasky goto send_pkt; 1889897b7965SHans Petter Selasky } else if (hcint & HCINT_ACK) { 1890b792f659SHans Petter Selasky td->offset += td->tx_bytes; 1891b792f659SHans Petter Selasky td->remainder -= td->tx_bytes; 1892b792f659SHans Petter Selasky td->toggle ^= 1; 18934541f273SHans Petter Selasky td->did_nak = 0; 1894bc990482SHans Petter Selasky td->tt_scheduled = 0; 1895b792f659SHans Petter Selasky 1896b792f659SHans Petter Selasky /* check remainder */ 1897b792f659SHans Petter Selasky if (td->remainder == 0) { 18983eabad25SHans Petter Selasky if (td->short_pkt) 189942fabcc3SHans Petter Selasky goto complete; 1900b792f659SHans Petter Selasky 1901b792f659SHans Petter Selasky /* else we need to transmit a short packet */ 1902b792f659SHans Petter Selasky } 1903b792f659SHans Petter Selasky goto send_pkt; 1904b792f659SHans Petter Selasky } 1905b792f659SHans Petter Selasky break; 19063eabad25SHans Petter Selasky 1907bc990482SHans Petter Selasky case DWC_CHAN_ST_WAIT_C_PKT: 19083eabad25SHans Petter Selasky goto send_cpkt; 19093eabad25SHans Petter Selasky 1910bc990482SHans Petter Selasky case DWC_CHAN_ST_TX_WAIT_ISOC: 1911ce842cecSHans Petter Selasky /* Check if ISOCHRONOUS OUT traffic is complete */ 1912db4300daSHans Petter Selasky if ((hcint & HCINT_HCH_DONE_MASK) == 0) 1913bc990482SHans Petter Selasky break; 1914bc990482SHans Petter Selasky 1915bc990482SHans Petter Selasky td->offset += td->tx_bytes; 1916bc990482SHans Petter Selasky td->remainder -= td->tx_bytes; 191742fabcc3SHans Petter Selasky goto complete; 1918b792f659SHans Petter Selasky default: 1919b792f659SHans Petter Selasky break; 1920b792f659SHans Petter Selasky } 19213eabad25SHans Petter Selasky goto busy; 1922b792f659SHans Petter Selasky 1923b792f659SHans Petter Selasky send_pkt: 1924db4300daSHans Petter Selasky /* free existing channel(s), if any */ 192508aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 1926db4300daSHans Petter Selasky 1927bc990482SHans Petter Selasky if (td->hcsplt != 0) { 1928db4300daSHans Petter Selasky delta = td->tt_start_slot - sc->sc_last_frame_num - 1; 1929db4300daSHans Petter Selasky if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { 1930bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_START; 1931bc990482SHans Petter Selasky goto busy; 1932bc990482SHans Petter Selasky } 1933db4300daSHans Petter Selasky delta = sc->sc_last_frame_num - td->tt_start_slot; 1934db4300daSHans Petter Selasky if (delta > 5) { 1935db4300daSHans Petter Selasky /* missed it */ 1936db4300daSHans Petter Selasky td->tt_scheduled = 0; 1937bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_START; 1938db4300daSHans Petter Selasky goto busy; 1939bc990482SHans Petter Selasky } 19402fe7ad87SHans Petter Selasky } else if (dwc_otg_host_rate_check(sc, td)) { 1941bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_START; 1942db4300daSHans Petter Selasky goto busy; 1943bc990482SHans Petter Selasky } 1944bc990482SHans Petter Selasky 1945db4300daSHans Petter Selasky /* allocate a new channel */ 194608aa4c94SHans Petter Selasky if (dwc_otg_host_channel_alloc(sc, td, 1)) { 1947db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_START; 1948db4300daSHans Petter Selasky goto busy; 1949db4300daSHans Petter Selasky } 1950db4300daSHans Petter Selasky 1951bc990482SHans Petter Selasky /* set toggle, if any */ 1952bc990482SHans Petter Selasky if (td->set_toggle) { 1953bc990482SHans Petter Selasky td->set_toggle = 0; 1954bc990482SHans Petter Selasky td->toggle = 1; 1955bc990482SHans Petter Selasky } 19563eabad25SHans Petter Selasky 1957db4300daSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS) { 1958ce842cecSHans Petter Selasky /* ISOCHRONOUS OUT transfers don't have any ACKs */ 1959bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_TX_WAIT_ISOC; 1960bc990482SHans Petter Selasky td->hcsplt &= ~HCSPLT_COMPSPLT; 19613eabad25SHans Petter Selasky if (td->hcsplt != 0) { 1962bc990482SHans Petter Selasky /* get maximum transfer length */ 1963bc990482SHans Petter Selasky count = td->remainder; 1964db4300daSHans Petter Selasky if (count > HCSPLT_XACTLEN_BURST) { 1965db4300daSHans Petter Selasky DPRINTF("TT overflow\n"); 196608aa4c94SHans Petter Selasky td->error_any = 1; 1967db4300daSHans Petter Selasky goto complete; 1968e7162865SHans Petter Selasky } 1969bc990482SHans Petter Selasky /* Update transaction position */ 1970bc990482SHans Petter Selasky td->hcsplt &= ~HCSPLT_XACTPOS_MASK; 1971db4300daSHans Petter Selasky td->hcsplt |= (HCSPLT_XACTPOS_ALL << HCSPLT_XACTPOS_SHIFT); 1972bc990482SHans Petter Selasky } 1973bc990482SHans Petter Selasky } else if (td->hcsplt != 0) { 19743eabad25SHans Petter Selasky td->hcsplt &= ~HCSPLT_COMPSPLT; 1975bc990482SHans Petter Selasky /* Wait for ACK/NAK/ERR from TT */ 19763eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_S_ANE; 1977bc990482SHans Petter Selasky } else { 1978bc990482SHans Petter Selasky /* Wait for ACK/NAK/STALL from device */ 1979bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_ANE; 1980ce842cecSHans Petter Selasky } 1981ce842cecSHans Petter Selasky 1982ce842cecSHans Petter Selasky td->tx_bytes = 0; 1983ce842cecSHans Petter Selasky 1984ce842cecSHans Petter Selasky for (x = 0; x != td->max_packet_count; x++) { 1985ce842cecSHans Petter Selasky uint32_t rem_bytes; 1986ce842cecSHans Petter Selasky 1987ce842cecSHans Petter Selasky channel = td->channel[x]; 1988537aca95SHans Petter Selasky 1989bc990482SHans Petter Selasky /* send one packet at a time */ 1990bc990482SHans Petter Selasky count = td->max_packet_size; 1991ce842cecSHans Petter Selasky rem_bytes = td->remainder - td->tx_bytes; 1992ce842cecSHans Petter Selasky if (rem_bytes < count) { 1993bc990482SHans Petter Selasky /* we have a short packet */ 1994bc990482SHans Petter Selasky td->short_pkt = 1; 1995ce842cecSHans Petter Selasky count = rem_bytes; 1996bc990482SHans Petter Selasky } 1997ce842cecSHans Petter Selasky if (count == rem_bytes) { 1998ce842cecSHans Petter Selasky /* last packet */ 1999ce842cecSHans Petter Selasky switch (x) { 2000ce842cecSHans Petter Selasky case 0: 2001db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 2002bc990482SHans Petter Selasky (count << HCTSIZ_XFERSIZE_SHIFT) | 2003bc990482SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 2004ce842cecSHans Petter Selasky (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : 2005ce842cecSHans Petter Selasky (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); 2006ce842cecSHans Petter Selasky break; 2007ce842cecSHans Petter Selasky case 1: 2008db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 2009bc990482SHans Petter Selasky (count << HCTSIZ_XFERSIZE_SHIFT) | 2010bc990482SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 2011bc990482SHans Petter Selasky (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT)); 2012ce842cecSHans Petter Selasky break; 2013ce842cecSHans Petter Selasky default: 2014db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 2015bc990482SHans Petter Selasky (count << HCTSIZ_XFERSIZE_SHIFT) | 2016bc990482SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 2017bc990482SHans Petter Selasky (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT)); 2018ce842cecSHans Petter Selasky break; 2019bc990482SHans Petter Selasky } 2020ce842cecSHans Petter Selasky } else if (td->ep_type == UE_ISOCHRONOUS && 2021ce842cecSHans Petter Selasky td->max_packet_count > 1) { 2022ce842cecSHans Petter Selasky /* ISOCHRONOUS multi packet */ 2023db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 2024bc990482SHans Petter Selasky (count << HCTSIZ_XFERSIZE_SHIFT) | 2025bc990482SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 2026bc990482SHans Petter Selasky (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT)); 2027bc990482SHans Petter Selasky } else { 2028537aca95SHans Petter Selasky /* TODO: HCTSIZ_DOPNG */ 2029ce842cecSHans Petter Selasky /* standard BULK/INTERRUPT/CONTROL packet */ 2030db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 20319cfd0731SHans Petter Selasky (count << HCTSIZ_XFERSIZE_SHIFT) | 2032537aca95SHans Petter Selasky (1 << HCTSIZ_PKTCNT_SHIFT) | 20339cfd0731SHans Petter Selasky (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : 20349cfd0731SHans Petter Selasky (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); 2035bc990482SHans Petter Selasky } 20369cfd0731SHans Petter Selasky 2037db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); 2038bc990482SHans Petter Selasky 20393eabad25SHans Petter Selasky hcchar = td->hcchar; 20403eabad25SHans Petter Selasky hcchar &= ~HCCHAR_EPDIR_IN; 20419cfd0731SHans Petter Selasky 2042db4300daSHans Petter Selasky /* send after next SOF event */ 2043a529288dSHans Petter Selasky if ((sc->sc_last_frame_num & 1) == 0 && 2044ce842cecSHans Petter Selasky td->ep_type == UE_ISOCHRONOUS) 2045db4300daSHans Petter Selasky hcchar |= HCCHAR_ODDFRM; 2046db4300daSHans Petter Selasky else 2047db4300daSHans Petter Selasky hcchar &= ~HCCHAR_ODDFRM; 2048db4300daSHans Petter Selasky 20499cfd0731SHans Petter Selasky /* must enable before writing data to FIFO */ 2050db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); 20519cfd0731SHans Petter Selasky 20529cfd0731SHans Petter Selasky if (count != 0) { 2053c2472ff8SHans Petter Selasky /* write data into FIFO */ 2054c2472ff8SHans Petter Selasky dwc_otg_write_fifo(sc, td->pc, td->offset + 2055c2472ff8SHans Petter Selasky td->tx_bytes, DOTG_DFIFO(channel), count); 20569cfd0731SHans Petter Selasky } 20579cfd0731SHans Petter Selasky 2058b792f659SHans Petter Selasky /* store number of bytes transmitted */ 2059ce842cecSHans Petter Selasky td->tx_bytes += count; 2060ce842cecSHans Petter Selasky 2061ce842cecSHans Petter Selasky /* store last packet index */ 2062ce842cecSHans Petter Selasky td->npkt = x; 2063ce842cecSHans Petter Selasky 2064ce842cecSHans Petter Selasky /* check for last packet */ 2065ce842cecSHans Petter Selasky if (count == rem_bytes) 2066ce842cecSHans Petter Selasky break; 2067ce842cecSHans Petter Selasky } 20683eabad25SHans Petter Selasky goto busy; 2069b792f659SHans Petter Selasky 2070b792f659SHans Petter Selasky send_cpkt: 2071db4300daSHans Petter Selasky /* free existing channel, if any */ 207208aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 2073db4300daSHans Petter Selasky 2074db4300daSHans Petter Selasky delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; 2075db4300daSHans Petter Selasky if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { 2076bc990482SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 2077db4300daSHans Petter Selasky goto busy; 2078e7162865SHans Petter Selasky } 2079db4300daSHans Petter Selasky delta = sc->sc_last_frame_num - td->tt_start_slot; 2080db4300daSHans Petter Selasky if (delta > DWC_OTG_TT_SLOT_MAX) { 2081db4300daSHans Petter Selasky /* we missed the service interval */ 2082db4300daSHans Petter Selasky if (td->ep_type != UE_ISOCHRONOUS) 2083db4300daSHans Petter Selasky td->error_any = 1; 2084db4300daSHans Petter Selasky goto complete; 2085db4300daSHans Petter Selasky } 2086db4300daSHans Petter Selasky 2087db4300daSHans Petter Selasky /* allocate a new channel */ 208808aa4c94SHans Petter Selasky if (dwc_otg_host_channel_alloc(sc, td, 0)) { 2089db4300daSHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_PKT; 2090db4300daSHans Petter Selasky goto busy; 2091db4300daSHans Petter Selasky } 2092db4300daSHans Petter Selasky 2093ce842cecSHans Petter Selasky channel = td->channel[0]; 2094e7162865SHans Petter Selasky 20953eabad25SHans Petter Selasky td->hcsplt |= HCSPLT_COMPSPLT; 20963eabad25SHans Petter Selasky td->state = DWC_CHAN_ST_WAIT_C_ANE; 20973eabad25SHans Petter Selasky 2098db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), 2099db4300daSHans Petter Selasky (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)); 2100b792f659SHans Petter Selasky 2101db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); 2102b792f659SHans Petter Selasky 21033eabad25SHans Petter Selasky hcchar = td->hcchar; 21043eabad25SHans Petter Selasky hcchar &= ~HCCHAR_EPDIR_IN; 2105b792f659SHans Petter Selasky 210608aa4c94SHans Petter Selasky /* receive complete split ASAP */ 2107a529288dSHans Petter Selasky if ((sc->sc_last_frame_num & 1) != 0 && 2108ce842cecSHans Petter Selasky td->ep_type == UE_ISOCHRONOUS) 2109db4300daSHans Petter Selasky hcchar |= HCCHAR_ODDFRM; 2110db4300daSHans Petter Selasky else 2111db4300daSHans Petter Selasky hcchar &= ~HCCHAR_ODDFRM; 2112b792f659SHans Petter Selasky 2113db4300daSHans Petter Selasky /* must enable channel before data can be received */ 2114db4300daSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); 2115db4300daSHans Petter Selasky 211608aa4c94SHans Petter Selasky /* wait until next slot before trying complete split */ 211708aa4c94SHans Petter Selasky td->tt_complete_slot = sc->sc_last_frame_num + 1; 21183eabad25SHans Petter Selasky busy: 2119537aca95SHans Petter Selasky return (1); /* busy */ 2120db4300daSHans Petter Selasky 212142fabcc3SHans Petter Selasky complete: 212208aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 212342fabcc3SHans Petter Selasky return (0); /* complete */ 21249cfd0731SHans Petter Selasky } 21259cfd0731SHans Petter Selasky 21269cfd0731SHans Petter Selasky static uint8_t 21272fe7ad87SHans Petter Selasky dwc_otg_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 2128dd03e19cSHans Petter Selasky { 2129dd03e19cSHans Petter Selasky uint32_t max_buffer; 2130dd03e19cSHans Petter Selasky uint32_t count; 2131dd03e19cSHans Petter Selasky uint32_t fifo_left; 2132dd03e19cSHans Petter Selasky uint32_t mpkt; 2133dd03e19cSHans Petter Selasky uint32_t temp; 2134dd03e19cSHans Petter Selasky uint8_t to; 2135dd03e19cSHans Petter Selasky 2136dd03e19cSHans Petter Selasky to = 3; /* don't loop forever! */ 2137dd03e19cSHans Petter Selasky 2138dd03e19cSHans Petter Selasky max_buffer = sc->sc_hw_ep_profile[td->ep_no].max_buffer; 2139dd03e19cSHans Petter Selasky 2140dd03e19cSHans Petter Selasky repeat: 21412e09a4acSGordon Bergling /* check for endpoint 0 data */ 2142dd03e19cSHans Petter Selasky 2143dd03e19cSHans Petter Selasky temp = sc->sc_last_rx_status; 2144dd03e19cSHans Petter Selasky 2145dd03e19cSHans Petter Selasky if ((td->ep_no == 0) && (temp != 0) && 2146710764f7SHans Petter Selasky (GRXSTSRD_CHNUM_GET(temp) == 0)) { 2147710764f7SHans Petter Selasky if ((temp & GRXSTSRD_PKTSTS_MASK) != 2148b4df5b00SHans Petter Selasky GRXSTSRD_STP_DATA && 2149b4df5b00SHans Petter Selasky (temp & GRXSTSRD_PKTSTS_MASK) != 2150b4df5b00SHans Petter Selasky GRXSTSRD_STP_COMPLETE) { 2151dd03e19cSHans Petter Selasky /* dump data - wrong direction */ 2152dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 2153dd03e19cSHans Petter Selasky } else { 2154dd03e19cSHans Petter Selasky /* 2155dd03e19cSHans Petter Selasky * The current transfer was cancelled 2156dd03e19cSHans Petter Selasky * by the USB Host: 2157dd03e19cSHans Petter Selasky */ 21589cfd0731SHans Petter Selasky td->error_any = 1; 2159dd03e19cSHans Petter Selasky return (0); /* complete */ 2160dd03e19cSHans Petter Selasky } 2161dd03e19cSHans Petter Selasky } 2162dd03e19cSHans Petter Selasky 2163dd03e19cSHans Petter Selasky /* fill in more TX data, if possible */ 2164dd03e19cSHans Petter Selasky if (td->tx_bytes != 0) { 2165dd03e19cSHans Petter Selasky uint16_t cpkt; 2166dd03e19cSHans Petter Selasky 2167dd03e19cSHans Petter Selasky /* check if packets have been transferred */ 2168710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no)); 2169dd03e19cSHans Petter Selasky 2170dd03e19cSHans Petter Selasky /* get current packet number */ 2171710764f7SHans Petter Selasky cpkt = DXEPTSIZ_GET_NPKT(temp); 2172dd03e19cSHans Petter Selasky 2173dd03e19cSHans Petter Selasky if (cpkt >= td->npkt) { 2174dd03e19cSHans Petter Selasky fifo_left = 0; 2175dd03e19cSHans Petter Selasky } else { 2176dd03e19cSHans Petter Selasky if (max_buffer != 0) { 2177dd03e19cSHans Petter Selasky fifo_left = (td->npkt - cpkt) * 2178dd03e19cSHans Petter Selasky td->max_packet_size; 2179dd03e19cSHans Petter Selasky 2180dd03e19cSHans Petter Selasky if (fifo_left > max_buffer) 2181dd03e19cSHans Petter Selasky fifo_left = max_buffer; 2182dd03e19cSHans Petter Selasky } else { 2183dd03e19cSHans Petter Selasky fifo_left = td->max_packet_size; 2184dd03e19cSHans Petter Selasky } 2185dd03e19cSHans Petter Selasky } 2186dd03e19cSHans Petter Selasky 2187dd03e19cSHans Petter Selasky count = td->tx_bytes; 2188dd03e19cSHans Petter Selasky if (count > fifo_left) 2189dd03e19cSHans Petter Selasky count = fifo_left; 2190dd03e19cSHans Petter Selasky 2191dd03e19cSHans Petter Selasky if (count != 0) { 2192c2472ff8SHans Petter Selasky /* write data into FIFO */ 2193c2472ff8SHans Petter Selasky dwc_otg_write_fifo(sc, td->pc, td->offset, 2194c2472ff8SHans Petter Selasky DOTG_DFIFO(td->ep_no), count); 2195dd03e19cSHans Petter Selasky 2196dd03e19cSHans Petter Selasky td->tx_bytes -= count; 2197dd03e19cSHans Petter Selasky td->remainder -= count; 2198dd03e19cSHans Petter Selasky td->offset += count; 2199dd03e19cSHans Petter Selasky td->npkt = cpkt; 2200dd03e19cSHans Petter Selasky } 2201dd03e19cSHans Petter Selasky if (td->tx_bytes != 0) 2202dd03e19cSHans Petter Selasky goto not_complete; 2203dd03e19cSHans Petter Selasky 2204dd03e19cSHans Petter Selasky /* check remainder */ 2205dd03e19cSHans Petter Selasky if (td->remainder == 0) { 2206dd03e19cSHans Petter Selasky if (td->short_pkt) 2207dd03e19cSHans Petter Selasky return (0); /* complete */ 2208dd03e19cSHans Petter Selasky 2209dd03e19cSHans Petter Selasky /* else we need to transmit a short packet */ 2210dd03e19cSHans Petter Selasky } 2211dd03e19cSHans Petter Selasky } 2212dd03e19cSHans Petter Selasky 22130b8de869SHans Petter Selasky if (!to--) 22140b8de869SHans Petter Selasky goto not_complete; 22150b8de869SHans Petter Selasky 22160b8de869SHans Petter Selasky /* check if not all packets have been transferred */ 2217710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no)); 2218dd03e19cSHans Petter Selasky 2219710764f7SHans Petter Selasky if (DXEPTSIZ_GET_NPKT(temp) != 0) { 2220dd03e19cSHans Petter Selasky DPRINTFN(5, "busy ep=%d npkt=%d DIEPTSIZ=0x%08x " 2221dd03e19cSHans Petter Selasky "DIEPCTL=0x%08x\n", td->ep_no, 2222710764f7SHans Petter Selasky DXEPTSIZ_GET_NPKT(temp), 2223710764f7SHans Petter Selasky temp, DWC_OTG_READ_4(sc, DOTG_DIEPCTL(td->ep_no))); 2224dd03e19cSHans Petter Selasky 2225dd03e19cSHans Petter Selasky goto not_complete; 2226dd03e19cSHans Petter Selasky } 2227dd03e19cSHans Petter Selasky 2228dd03e19cSHans Petter Selasky DPRINTFN(5, "rem=%u ep=%d\n", td->remainder, td->ep_no); 2229dd03e19cSHans Petter Selasky 2230dd03e19cSHans Petter Selasky /* try to optimise by sending more data */ 2231dd03e19cSHans Petter Selasky if ((max_buffer != 0) && ((td->max_packet_size & 3) == 0)) { 2232dd03e19cSHans Petter Selasky /* send multiple packets at the same time */ 2233dd03e19cSHans Petter Selasky mpkt = max_buffer / td->max_packet_size; 2234dd03e19cSHans Petter Selasky 2235dd03e19cSHans Petter Selasky if (mpkt > 0x3FE) 2236dd03e19cSHans Petter Selasky mpkt = 0x3FE; 2237dd03e19cSHans Petter Selasky 2238dd03e19cSHans Petter Selasky count = td->remainder; 2239dd03e19cSHans Petter Selasky if (count > 0x7FFFFF) 2240dd03e19cSHans Petter Selasky count = 0x7FFFFF - (0x7FFFFF % td->max_packet_size); 2241dd03e19cSHans Petter Selasky 2242dd03e19cSHans Petter Selasky td->npkt = count / td->max_packet_size; 2243dd03e19cSHans Petter Selasky 2244dd03e19cSHans Petter Selasky /* 2245dd03e19cSHans Petter Selasky * NOTE: We could use 0x3FE instead of "mpkt" in the 2246dd03e19cSHans Petter Selasky * check below to get more throughput, but then we 2247dd03e19cSHans Petter Selasky * have a dependency towards non-generic chip features 2248dd03e19cSHans Petter Selasky * to disable the TX-FIFO-EMPTY interrupts on a per 2249dd03e19cSHans Petter Selasky * endpoint basis. Increase the maximum buffer size of 2250dd03e19cSHans Petter Selasky * the IN endpoint to increase the performance. 2251dd03e19cSHans Petter Selasky */ 2252dd03e19cSHans Petter Selasky if (td->npkt > mpkt) { 2253dd03e19cSHans Petter Selasky td->npkt = mpkt; 2254dd03e19cSHans Petter Selasky count = td->max_packet_size * mpkt; 2255dd03e19cSHans Petter Selasky } else if ((count == 0) || (count % td->max_packet_size)) { 2256dd03e19cSHans Petter Selasky /* we are transmitting a short packet */ 2257dd03e19cSHans Petter Selasky td->npkt++; 2258dd03e19cSHans Petter Selasky td->short_pkt = 1; 2259dd03e19cSHans Petter Selasky } 2260dd03e19cSHans Petter Selasky } else { 2261dd03e19cSHans Petter Selasky /* send one packet at a time */ 2262dd03e19cSHans Petter Selasky mpkt = 1; 2263dd03e19cSHans Petter Selasky count = td->max_packet_size; 2264dd03e19cSHans Petter Selasky if (td->remainder < count) { 2265dd03e19cSHans Petter Selasky /* we have a short packet */ 2266dd03e19cSHans Petter Selasky td->short_pkt = 1; 2267dd03e19cSHans Petter Selasky count = td->remainder; 2268dd03e19cSHans Petter Selasky } 2269dd03e19cSHans Petter Selasky td->npkt = 1; 2270dd03e19cSHans Petter Selasky } 2271710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(td->ep_no), 2272710764f7SHans Petter Selasky DXEPTSIZ_SET_MULTI(1) | 2273710764f7SHans Petter Selasky DXEPTSIZ_SET_NPKT(td->npkt) | 2274710764f7SHans Petter Selasky DXEPTSIZ_SET_NBYTES(count)); 2275dd03e19cSHans Petter Selasky 2276dd03e19cSHans Petter Selasky /* make room for buffering */ 2277dd03e19cSHans Petter Selasky td->npkt += mpkt; 2278dd03e19cSHans Petter Selasky 2279dd03e19cSHans Petter Selasky temp = sc->sc_in_ctl[td->ep_no]; 2280dd03e19cSHans Petter Selasky 228168691fe0SHans Petter Selasky /* check for isochronous mode */ 228268691fe0SHans Petter Selasky if ((temp & DIEPCTL_EPTYPE_MASK) == 228368691fe0SHans Petter Selasky (DIEPCTL_EPTYPE_ISOC << DIEPCTL_EPTYPE_SHIFT)) { 228468691fe0SHans Petter Selasky /* toggle odd or even frame bit */ 228568691fe0SHans Petter Selasky if (temp & DIEPCTL_SETD1PID) { 228668691fe0SHans Petter Selasky temp &= ~DIEPCTL_SETD1PID; 228768691fe0SHans Petter Selasky temp |= DIEPCTL_SETD0PID; 228868691fe0SHans Petter Selasky } else { 228968691fe0SHans Petter Selasky temp &= ~DIEPCTL_SETD0PID; 229068691fe0SHans Petter Selasky temp |= DIEPCTL_SETD1PID; 229168691fe0SHans Petter Selasky } 229268691fe0SHans Petter Selasky sc->sc_in_ctl[td->ep_no] = temp; 229368691fe0SHans Petter Selasky } 229468691fe0SHans Petter Selasky 2295dd03e19cSHans Petter Selasky /* must enable before writing data to FIFO */ 2296710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(td->ep_no), temp | 229768691fe0SHans Petter Selasky DIEPCTL_EPENA | DIEPCTL_CNAK); 2298dd03e19cSHans Petter Selasky 2299dd03e19cSHans Petter Selasky td->tx_bytes = count; 2300dd03e19cSHans Petter Selasky 2301dd03e19cSHans Petter Selasky /* check remainder */ 2302dd03e19cSHans Petter Selasky if (td->tx_bytes == 0 && 2303dd03e19cSHans Petter Selasky td->remainder == 0) { 2304dd03e19cSHans Petter Selasky if (td->short_pkt) 2305dd03e19cSHans Petter Selasky return (0); /* complete */ 2306dd03e19cSHans Petter Selasky 2307dd03e19cSHans Petter Selasky /* else we need to transmit a short packet */ 2308dd03e19cSHans Petter Selasky } 2309dd03e19cSHans Petter Selasky goto repeat; 2310dd03e19cSHans Petter Selasky 2311dd03e19cSHans Petter Selasky not_complete: 2312dd03e19cSHans Petter Selasky return (1); /* not complete */ 2313dd03e19cSHans Petter Selasky } 2314dd03e19cSHans Petter Selasky 2315dd03e19cSHans Petter Selasky static uint8_t 23162fe7ad87SHans Petter Selasky dwc_otg_data_tx_sync(struct dwc_otg_softc *sc, struct dwc_otg_td *td) 2317dd03e19cSHans Petter Selasky { 2318dd03e19cSHans Petter Selasky uint32_t temp; 2319dd03e19cSHans Petter Selasky 2320dd03e19cSHans Petter Selasky /* 2321dd03e19cSHans Petter Selasky * If all packets are transferred we are complete: 2322dd03e19cSHans Petter Selasky */ 2323710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DIEPTSIZ(td->ep_no)); 2324dd03e19cSHans Petter Selasky 2325dd03e19cSHans Petter Selasky /* check that all packets have been transferred */ 2326710764f7SHans Petter Selasky if (DXEPTSIZ_GET_NPKT(temp) != 0) { 2327dd03e19cSHans Petter Selasky DPRINTFN(5, "busy ep=%d\n", td->ep_no); 2328dd03e19cSHans Petter Selasky goto not_complete; 2329dd03e19cSHans Petter Selasky } 2330dd03e19cSHans Petter Selasky return (0); 2331dd03e19cSHans Petter Selasky 2332dd03e19cSHans Petter Selasky not_complete: 2333dd03e19cSHans Petter Selasky 2334dd03e19cSHans Petter Selasky /* we only want to know if there is a SETUP packet or free IN packet */ 2335dd03e19cSHans Petter Selasky 2336dd03e19cSHans Petter Selasky temp = sc->sc_last_rx_status; 2337dd03e19cSHans Petter Selasky 2338dd03e19cSHans Petter Selasky if ((td->ep_no == 0) && (temp != 0) && 2339710764f7SHans Petter Selasky (GRXSTSRD_CHNUM_GET(temp) == 0)) { 2340710764f7SHans Petter Selasky if ((temp & GRXSTSRD_PKTSTS_MASK) == 2341b4df5b00SHans Petter Selasky GRXSTSRD_STP_DATA || 2342b4df5b00SHans Petter Selasky (temp & GRXSTSRD_PKTSTS_MASK) == 2343b4df5b00SHans Petter Selasky GRXSTSRD_STP_COMPLETE) { 2344dd03e19cSHans Petter Selasky DPRINTFN(5, "faking complete\n"); 2345dd03e19cSHans Petter Selasky /* 2346dd03e19cSHans Petter Selasky * Race condition: We are complete! 2347dd03e19cSHans Petter Selasky */ 2348dd03e19cSHans Petter Selasky return (0); 2349dd03e19cSHans Petter Selasky } else { 2350dd03e19cSHans Petter Selasky /* dump data - wrong direction */ 2351dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 2352dd03e19cSHans Petter Selasky } 2353dd03e19cSHans Petter Selasky } 2354dd03e19cSHans Petter Selasky return (1); /* not complete */ 2355dd03e19cSHans Petter Selasky } 2356dd03e19cSHans Petter Selasky 23572fe7ad87SHans Petter Selasky static void 23582fe7ad87SHans Petter Selasky dwc_otg_xfer_do_fifo(struct dwc_otg_softc *sc, struct usb_xfer *xfer) 2359dd03e19cSHans Petter Selasky { 2360dd03e19cSHans Petter Selasky struct dwc_otg_td *td; 23619cfd0731SHans Petter Selasky uint8_t toggle; 23623eabad25SHans Petter Selasky uint8_t tmr_val; 23633eabad25SHans Petter Selasky uint8_t tmr_res; 2364dd03e19cSHans Petter Selasky 2365dd03e19cSHans Petter Selasky DPRINTFN(9, "\n"); 2366dd03e19cSHans Petter Selasky 2367dd03e19cSHans Petter Selasky td = xfer->td_transfer_cache; 23682fe7ad87SHans Petter Selasky if (td == NULL) 23692fe7ad87SHans Petter Selasky return; 23703eabad25SHans Petter Selasky 2371dd03e19cSHans Petter Selasky while (1) { 23722fe7ad87SHans Petter Selasky if ((td->func) (sc, td)) { 2373dd03e19cSHans Petter Selasky /* operation in progress */ 2374dd03e19cSHans Petter Selasky break; 2375dd03e19cSHans Petter Selasky } 2376dd03e19cSHans Petter Selasky if (((void *)td) == xfer->td_transfer_last) { 2377dd03e19cSHans Petter Selasky goto done; 2378dd03e19cSHans Petter Selasky } 23799cfd0731SHans Petter Selasky if (td->error_any) { 2380dd03e19cSHans Petter Selasky goto done; 2381dd03e19cSHans Petter Selasky } else if (td->remainder > 0) { 2382dd03e19cSHans Petter Selasky /* 2383dd03e19cSHans Petter Selasky * We had a short transfer. If there is no alternate 2384dd03e19cSHans Petter Selasky * next, stop processing ! 2385dd03e19cSHans Petter Selasky */ 2386dd03e19cSHans Petter Selasky if (!td->alt_next) 2387dd03e19cSHans Petter Selasky goto done; 2388dd03e19cSHans Petter Selasky } 2389dd03e19cSHans Petter Selasky 2390dd03e19cSHans Petter Selasky /* 2391dd03e19cSHans Petter Selasky * Fetch the next transfer descriptor and transfer 2392dd03e19cSHans Petter Selasky * some flags to the next transfer descriptor 2393dd03e19cSHans Petter Selasky */ 23943eabad25SHans Petter Selasky tmr_res = td->tmr_res; 23953eabad25SHans Petter Selasky tmr_val = td->tmr_val; 23969cfd0731SHans Petter Selasky toggle = td->toggle; 2397dd03e19cSHans Petter Selasky td = td->obj_next; 2398dd03e19cSHans Petter Selasky xfer->td_transfer_cache = td; 23999cfd0731SHans Petter Selasky td->toggle = toggle; /* transfer toggle */ 24003eabad25SHans Petter Selasky td->tmr_res = tmr_res; 24013eabad25SHans Petter Selasky td->tmr_val = tmr_val; 2402dd03e19cSHans Petter Selasky } 24032fe7ad87SHans Petter Selasky return; 2404dd03e19cSHans Petter Selasky 2405dd03e19cSHans Petter Selasky done: 24062fe7ad87SHans Petter Selasky xfer->td_transfer_cache = NULL; 24072fe7ad87SHans Petter Selasky sc->sc_xfer_complete = 1; 24082fe7ad87SHans Petter Selasky } 2409dd03e19cSHans Petter Selasky 24102fe7ad87SHans Petter Selasky static uint8_t 24112994bac4SHans Petter Selasky dwc_otg_xfer_do_complete_locked(struct dwc_otg_softc *sc, struct usb_xfer *xfer) 24122fe7ad87SHans Petter Selasky { 24132fe7ad87SHans Petter Selasky struct dwc_otg_td *td; 24142fe7ad87SHans Petter Selasky 24152fe7ad87SHans Petter Selasky DPRINTFN(9, "\n"); 24162fe7ad87SHans Petter Selasky 24172fe7ad87SHans Petter Selasky td = xfer->td_transfer_cache; 24182fe7ad87SHans Petter Selasky if (td == NULL) { 24192fe7ad87SHans Petter Selasky /* compute all actual lengths */ 2420dd03e19cSHans Petter Selasky dwc_otg_standard_done(xfer); 24212fe7ad87SHans Petter Selasky return (1); 24222fe7ad87SHans Petter Selasky } 24232fe7ad87SHans Petter Selasky return (0); 2424dd03e19cSHans Petter Selasky } 2425dd03e19cSHans Petter Selasky 2426dd03e19cSHans Petter Selasky static void 24273eabad25SHans Petter Selasky dwc_otg_timer(void *_sc) 24283eabad25SHans Petter Selasky { 24293eabad25SHans Petter Selasky struct dwc_otg_softc *sc = _sc; 24303eabad25SHans Petter Selasky 24313eabad25SHans Petter Selasky USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); 24323eabad25SHans Petter Selasky 24333eabad25SHans Petter Selasky DPRINTF("\n"); 24343eabad25SHans Petter Selasky 24352fe7ad87SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 24362fe7ad87SHans Petter Selasky 24373eabad25SHans Petter Selasky /* increment timer value */ 24383eabad25SHans Petter Selasky sc->sc_tmr_val++; 24393eabad25SHans Petter Selasky 244042fabcc3SHans Petter Selasky /* enable SOF interrupt, which will poll jobs */ 244142fabcc3SHans Petter Selasky dwc_otg_enable_sof_irq(sc); 24423eabad25SHans Petter Selasky 24432fe7ad87SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 24442fe7ad87SHans Petter Selasky 24453eabad25SHans Petter Selasky if (sc->sc_timer_active) { 24463eabad25SHans Petter Selasky /* restart timer */ 24473eabad25SHans Petter Selasky usb_callout_reset(&sc->sc_timer, 24483eabad25SHans Petter Selasky hz / (1000 / DWC_OTG_HOST_TIMER_RATE), 24493eabad25SHans Petter Selasky &dwc_otg_timer, sc); 24503eabad25SHans Petter Selasky } 24513eabad25SHans Petter Selasky } 24523eabad25SHans Petter Selasky 24533eabad25SHans Petter Selasky static void 24543eabad25SHans Petter Selasky dwc_otg_timer_start(struct dwc_otg_softc *sc) 24553eabad25SHans Petter Selasky { 24563eabad25SHans Petter Selasky if (sc->sc_timer_active != 0) 24573eabad25SHans Petter Selasky return; 24583eabad25SHans Petter Selasky 24593eabad25SHans Petter Selasky sc->sc_timer_active = 1; 24603eabad25SHans Petter Selasky 24613eabad25SHans Petter Selasky /* restart timer */ 24623eabad25SHans Petter Selasky usb_callout_reset(&sc->sc_timer, 24633eabad25SHans Petter Selasky hz / (1000 / DWC_OTG_HOST_TIMER_RATE), 24643eabad25SHans Petter Selasky &dwc_otg_timer, sc); 24653eabad25SHans Petter Selasky } 24663eabad25SHans Petter Selasky 24673eabad25SHans Petter Selasky static void 24683eabad25SHans Petter Selasky dwc_otg_timer_stop(struct dwc_otg_softc *sc) 24693eabad25SHans Petter Selasky { 24703eabad25SHans Petter Selasky if (sc->sc_timer_active == 0) 24713eabad25SHans Petter Selasky return; 24723eabad25SHans Petter Selasky 24733eabad25SHans Petter Selasky sc->sc_timer_active = 0; 24743eabad25SHans Petter Selasky 24753eabad25SHans Petter Selasky /* stop timer */ 24763eabad25SHans Petter Selasky usb_callout_stop(&sc->sc_timer); 24773eabad25SHans Petter Selasky } 24783eabad25SHans Petter Selasky 247908aa4c94SHans Petter Selasky static uint16_t 2480dea9afcfSHans Petter Selasky dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo) 248108aa4c94SHans Petter Selasky { 2482dea9afcfSHans Petter Selasky if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX) 248308aa4c94SHans Petter Selasky pinfo->slot_index++; 2484dea9afcfSHans Petter Selasky return (pinfo->slot_index); 248508aa4c94SHans Petter Selasky } 248608aa4c94SHans Petter Selasky 2487db4300daSHans Petter Selasky static uint8_t 24882994bac4SHans Petter Selasky dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc) 2489bc990482SHans Petter Selasky { 2490bc990482SHans Petter Selasky TAILQ_HEAD(, usb_xfer) head; 2491bc990482SHans Petter Selasky struct usb_xfer *xfer; 2492bc990482SHans Petter Selasky struct usb_xfer *xfer_next; 2493bc990482SHans Petter Selasky struct dwc_otg_td *td; 2494bc990482SHans Petter Selasky uint16_t temp; 249508aa4c94SHans Petter Selasky uint16_t slot; 2496bc990482SHans Petter Selasky 2497db4300daSHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK; 2498bc990482SHans Petter Selasky 2499bc990482SHans Petter Selasky if (sc->sc_last_frame_num == temp) 2500db4300daSHans Petter Selasky return (0); 2501bc990482SHans Petter Selasky 2502bc990482SHans Petter Selasky sc->sc_last_frame_num = temp; 2503bc990482SHans Petter Selasky 2504bc990482SHans Petter Selasky TAILQ_INIT(&head); 2505bc990482SHans Petter Selasky 2506bc990482SHans Petter Selasky if ((temp & 7) == 0) { 250708aa4c94SHans Petter Selasky /* reset the schedule */ 250808aa4c94SHans Petter Selasky memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); 250908aa4c94SHans Petter Selasky 2510bc990482SHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 2511bc990482SHans Petter Selasky td = xfer->td_transfer_cache; 2512db4300daSHans Petter Selasky if (td == NULL || td->ep_type != UE_ISOCHRONOUS) 2513bc990482SHans Petter Selasky continue; 2514bc990482SHans Petter Selasky 251508aa4c94SHans Petter Selasky /* check for IN direction */ 251608aa4c94SHans Petter Selasky if ((td->hcchar & HCCHAR_EPDIR_IN) != 0) 251708aa4c94SHans Petter Selasky continue; 251808aa4c94SHans Petter Selasky 2519db4300daSHans Petter Selasky sc->sc_needsof = 1; 2520bc990482SHans Petter Selasky 2521db4300daSHans Petter Selasky if (td->hcsplt == 0 || td->tt_scheduled != 0) 2522bc990482SHans Petter Selasky continue; 2523bc990482SHans Petter Selasky 252408aa4c94SHans Petter Selasky /* compute slot */ 2525dea9afcfSHans Petter Selasky slot = dwc_otg_compute_isoc_rx_tt_slot( 2526dea9afcfSHans Petter Selasky sc->sc_tt_info + td->tt_index); 2527dea9afcfSHans Petter Selasky if (slot > 3) { 2528dea9afcfSHans Petter Selasky /* 2529dea9afcfSHans Petter Selasky * Not enough time to get complete 2530dea9afcfSHans Petter Selasky * split executed. 2531dea9afcfSHans Petter Selasky */ 2532dea9afcfSHans Petter Selasky continue; 2533dea9afcfSHans Petter Selasky } 2534dea9afcfSHans Petter Selasky /* Delayed start */ 2535dea9afcfSHans Petter Selasky td->tt_start_slot = temp + slot; 253608aa4c94SHans Petter Selasky td->tt_scheduled = 1; 253708aa4c94SHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 253808aa4c94SHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 253908aa4c94SHans Petter Selasky } 254008aa4c94SHans Petter Selasky 254108aa4c94SHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 254208aa4c94SHans Petter Selasky td = xfer->td_transfer_cache; 254308aa4c94SHans Petter Selasky if (td == NULL || td->ep_type != UE_ISOCHRONOUS) 254408aa4c94SHans Petter Selasky continue; 254508aa4c94SHans Petter Selasky 254608aa4c94SHans Petter Selasky /* check for OUT direction */ 254708aa4c94SHans Petter Selasky if ((td->hcchar & HCCHAR_EPDIR_IN) == 0) 254808aa4c94SHans Petter Selasky continue; 254908aa4c94SHans Petter Selasky 255008aa4c94SHans Petter Selasky sc->sc_needsof = 1; 255108aa4c94SHans Petter Selasky 255208aa4c94SHans Petter Selasky if (td->hcsplt == 0 || td->tt_scheduled != 0) 255308aa4c94SHans Petter Selasky continue; 255408aa4c94SHans Petter Selasky 255508aa4c94SHans Petter Selasky /* Start ASAP */ 2556dea9afcfSHans Petter Selasky td->tt_start_slot = temp; 2557bc990482SHans Petter Selasky td->tt_scheduled = 1; 2558bc990482SHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 2559bc990482SHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 2560bc990482SHans Petter Selasky } 2561bc990482SHans Petter Selasky 2562bc990482SHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 2563bc990482SHans Petter Selasky td = xfer->td_transfer_cache; 2564db4300daSHans Petter Selasky if (td == NULL || td->ep_type != UE_INTERRUPT) 2565db4300daSHans Petter Selasky continue; 2566db4300daSHans Petter Selasky 2567db4300daSHans Petter Selasky if (td->tt_scheduled != 0) { 2568db4300daSHans Petter Selasky sc->sc_needsof = 1; 2569bc990482SHans Petter Selasky continue; 2570bc990482SHans Petter Selasky } 2571bc990482SHans Petter Selasky 2572bc990482SHans Petter Selasky if (dwc_otg_host_rate_check_interrupt(sc, td)) 2573bc990482SHans Petter Selasky continue; 2574bc990482SHans Petter Selasky 257542fabcc3SHans Petter Selasky if (td->hcsplt == 0) { 2576db4300daSHans Petter Selasky sc->sc_needsof = 1; 257742fabcc3SHans Petter Selasky td->tt_scheduled = 1; 257842fabcc3SHans Petter Selasky continue; 257942fabcc3SHans Petter Selasky } 258042fabcc3SHans Petter Selasky 2581db4300daSHans Petter Selasky /* start ASAP */ 2582dea9afcfSHans Petter Selasky td->tt_start_slot = temp; 2583db4300daSHans Petter Selasky sc->sc_needsof = 1; 2584bc990482SHans Petter Selasky td->tt_scheduled = 1; 2585bc990482SHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 2586bc990482SHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 2587bc990482SHans Petter Selasky } 2588bc990482SHans Petter Selasky 2589db4300daSHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 2590db4300daSHans Petter Selasky td = xfer->td_transfer_cache; 25914541f273SHans Petter Selasky if (td == NULL || 2592ed0ed9b4SHans Petter Selasky td->ep_type != UE_CONTROL) { 2593db4300daSHans Petter Selasky continue; 25944541f273SHans Petter Selasky } 2595db4300daSHans Petter Selasky 2596db4300daSHans Petter Selasky sc->sc_needsof = 1; 2597db4300daSHans Petter Selasky 2598db4300daSHans Petter Selasky if (td->hcsplt == 0 || td->tt_scheduled != 0) 2599db4300daSHans Petter Selasky continue; 2600db4300daSHans Petter Selasky 2601db4300daSHans Petter Selasky /* start ASAP */ 2602dea9afcfSHans Petter Selasky td->tt_start_slot = temp; 2603db4300daSHans Petter Selasky td->tt_scheduled = 1; 2604db4300daSHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 2605db4300daSHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 2606db4300daSHans Petter Selasky } 2607db4300daSHans Petter Selasky } 2608bc990482SHans Petter Selasky if ((temp & 7) < 6) { 2609bc990482SHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 2610bc990482SHans Petter Selasky td = xfer->td_transfer_cache; 26114541f273SHans Petter Selasky if (td == NULL || 2612ed0ed9b4SHans Petter Selasky td->ep_type != UE_BULK) { 261342fabcc3SHans Petter Selasky continue; 26144541f273SHans Petter Selasky } 261542fabcc3SHans Petter Selasky 2616db4300daSHans Petter Selasky sc->sc_needsof = 1; 2617bc990482SHans Petter Selasky 2618db4300daSHans Petter Selasky if (td->hcsplt == 0 || td->tt_scheduled != 0) 2619db4300daSHans Petter Selasky continue; 2620bc990482SHans Petter Selasky 2621db4300daSHans Petter Selasky /* start ASAP */ 2622dea9afcfSHans Petter Selasky td->tt_start_slot = temp; 2623bc990482SHans Petter Selasky td->tt_scheduled = 1; 2624bc990482SHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 2625bc990482SHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 2626bc990482SHans Petter Selasky } 2627bc990482SHans Petter Selasky } 2628bc990482SHans Petter Selasky 2629db4300daSHans Petter Selasky /* Put TT transfers in execution order at the end */ 2630db4300daSHans Petter Selasky TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); 2631db4300daSHans Petter Selasky 2632db4300daSHans Petter Selasky /* move all TT transfers in front, keeping the current order */ 2633db4300daSHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 2634db4300daSHans Petter Selasky td = xfer->td_transfer_cache; 2635db4300daSHans Petter Selasky if (td == NULL || td->hcsplt == 0) 2636db4300daSHans Petter Selasky continue; 2637db4300daSHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 2638db4300daSHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 2639db4300daSHans Petter Selasky } 2640bc990482SHans Petter Selasky TAILQ_CONCAT(&head, &sc->sc_bus.intr_q.head, wait_entry); 2641db4300daSHans Petter Selasky TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); 2642bc990482SHans Petter Selasky 2643ce842cecSHans Petter Selasky /* put non-TT non-ISOCHRONOUS transfers last */ 2644db4300daSHans Petter Selasky TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { 2645db4300daSHans Petter Selasky td = xfer->td_transfer_cache; 2646ce842cecSHans Petter Selasky if (td == NULL || td->hcsplt != 0 || td->ep_type == UE_ISOCHRONOUS) 2647db4300daSHans Petter Selasky continue; 2648db4300daSHans Petter Selasky TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); 2649db4300daSHans Petter Selasky TAILQ_INSERT_TAIL(&head, xfer, wait_entry); 2650db4300daSHans Petter Selasky } 2651bc990482SHans Petter Selasky TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); 2652bc990482SHans Petter Selasky 2653bc990482SHans Petter Selasky if ((temp & 7) == 0) { 2654bc990482SHans Petter Selasky DPRINTFN(12, "SOF interrupt #%d, needsof=%d\n", 2655db4300daSHans Petter Selasky (int)temp, (int)sc->sc_needsof); 2656bc990482SHans Petter Selasky 2657bc990482SHans Petter Selasky /* update SOF IRQ mask */ 2658bc990482SHans Petter Selasky if (sc->sc_irq_mask & GINTMSK_SOFMSK) { 2659db4300daSHans Petter Selasky if (sc->sc_needsof == 0) { 2660bc990482SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_SOFMSK; 2661bc990482SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 2662bc990482SHans Petter Selasky } 2663bc990482SHans Petter Selasky } else { 2664db4300daSHans Petter Selasky if (sc->sc_needsof != 0) { 2665bc990482SHans Petter Selasky sc->sc_irq_mask |= GINTMSK_SOFMSK; 2666bc990482SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 2667bc990482SHans Petter Selasky } 2668bc990482SHans Petter Selasky } 2669db4300daSHans Petter Selasky 2670db4300daSHans Petter Selasky /* clear need SOF flag */ 2671db4300daSHans Petter Selasky sc->sc_needsof = 0; 2672bc990482SHans Petter Selasky } 2673db4300daSHans Petter Selasky return (1); 2674bc990482SHans Petter Selasky } 2675bc990482SHans Petter Selasky 2676bc990482SHans Petter Selasky static void 26772994bac4SHans Petter Selasky dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc) 2678dd03e19cSHans Petter Selasky { 2679dd03e19cSHans Petter Selasky struct usb_xfer *xfer; 26807462a4ddSHans Petter Selasky uint32_t count; 2681dd03e19cSHans Petter Selasky uint32_t temp; 268252d7c638SHans Petter Selasky uint32_t haint; 2683dd03e19cSHans Petter Selasky uint8_t got_rx_status; 26843eabad25SHans Petter Selasky uint8_t x; 2685dd03e19cSHans Petter Selasky 26867462a4ddSHans Petter Selasky if (sc->sc_flags.status_device_mode == 0) { 26877462a4ddSHans Petter Selasky /* 26887462a4ddSHans Petter Selasky * Update host transfer schedule, so that new 26897462a4ddSHans Petter Selasky * transfers can be issued: 26907462a4ddSHans Petter Selasky */ 26917462a4ddSHans Petter Selasky dwc_otg_update_host_transfer_schedule_locked(sc); 26927462a4ddSHans Petter Selasky } 26937462a4ddSHans Petter Selasky count = 0; 2694dd03e19cSHans Petter Selasky repeat: 269556d6361dSHans Petter Selasky if (++count == 16) { 269656d6361dSHans Petter Selasky /* give other interrupts a chance */ 269756d6361dSHans Petter Selasky DPRINTF("Yield\n"); 269856d6361dSHans Petter Selasky return; 269956d6361dSHans Petter Selasky } 270052d7c638SHans Petter Selasky 270156d6361dSHans Petter Selasky /* get all host channel interrupts */ 270252d7c638SHans Petter Selasky haint = DWC_OTG_READ_4(sc, DOTG_HAINT); 270352d7c638SHans Petter Selasky while (1) { 270452d7c638SHans Petter Selasky x = ffs(haint) - 1; 270552d7c638SHans Petter Selasky if (x >= sc->sc_host_ch_max) 270652d7c638SHans Petter Selasky break; 27073eabad25SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); 27083eabad25SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); 270919f9c619SHans Petter Selasky temp &= ~HCINT_SOFTWARE_ONLY; 27103eabad25SHans Petter Selasky sc->sc_chan_state[x].hcint |= temp; 271152d7c638SHans Petter Selasky haint &= ~(1U << x); 27123eabad25SHans Petter Selasky } 27133eabad25SHans Petter Selasky 2714dd03e19cSHans Petter Selasky if (sc->sc_last_rx_status == 0) { 2715710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); 2716710764f7SHans Petter Selasky if (temp & GINTSTS_RXFLVL) { 2717dd03e19cSHans Petter Selasky /* pop current status */ 2718dd03e19cSHans Petter Selasky sc->sc_last_rx_status = 2719710764f7SHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_GRXSTSPD); 2720dd03e19cSHans Petter Selasky } 2721dd03e19cSHans Petter Selasky 2722dd03e19cSHans Petter Selasky if (sc->sc_last_rx_status != 0) { 2723dd03e19cSHans Petter Selasky uint8_t ep_no; 2724dd03e19cSHans Petter Selasky 2725beefefd4SHans Petter Selasky temp = sc->sc_last_rx_status & 2726beefefd4SHans Petter Selasky GRXSTSRD_PKTSTS_MASK; 2727beefefd4SHans Petter Selasky 2728beefefd4SHans Petter Selasky /* non-data messages we simply skip */ 2729beefefd4SHans Petter Selasky if (temp != GRXSTSRD_STP_DATA && 2730b4df5b00SHans Petter Selasky temp != GRXSTSRD_STP_COMPLETE && 2731beefefd4SHans Petter Selasky temp != GRXSTSRD_OUT_DATA) { 2732a529288dSHans Petter Selasky /* check for halted channel */ 2733a529288dSHans Petter Selasky if (temp == GRXSTSRH_HALTED) { 2734a529288dSHans Petter Selasky ep_no = GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status); 2735a529288dSHans Petter Selasky sc->sc_chan_state[ep_no].wait_halted = 0; 2736a529288dSHans Petter Selasky DPRINTFN(5, "channel halt complete ch=%u\n", ep_no); 2737a529288dSHans Petter Selasky } 2738c2472ff8SHans Petter Selasky /* store bytes and FIFO offset */ 2739c2472ff8SHans Petter Selasky sc->sc_current_rx_bytes = 0; 2740c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo = 0; 2741c2472ff8SHans Petter Selasky 2742c2472ff8SHans Petter Selasky /* acknowledge status */ 2743beefefd4SHans Petter Selasky dwc_otg_common_rx_ack(sc); 2744beefefd4SHans Petter Selasky goto repeat; 2745beefefd4SHans Petter Selasky } 2746beefefd4SHans Petter Selasky 2747710764f7SHans Petter Selasky temp = GRXSTSRD_BCNT_GET( 2748dd03e19cSHans Petter Selasky sc->sc_last_rx_status); 2749710764f7SHans Petter Selasky ep_no = GRXSTSRD_CHNUM_GET( 2750dd03e19cSHans Petter Selasky sc->sc_last_rx_status); 2751dd03e19cSHans Petter Selasky 2752c2472ff8SHans Petter Selasky /* store bytes and FIFO offset */ 2753c2472ff8SHans Petter Selasky sc->sc_current_rx_bytes = (temp + 3) & ~3; 2754c2472ff8SHans Petter Selasky sc->sc_current_rx_fifo = DOTG_DFIFO(ep_no); 2755c2472ff8SHans Petter Selasky 2756dd03e19cSHans Petter Selasky DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no); 2757dd03e19cSHans Petter Selasky 2758dd03e19cSHans Petter Selasky /* check if we should dump the data */ 27599cfd0731SHans Petter Selasky if (!(sc->sc_active_rx_ep & (1U << ep_no))) { 2760dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 2761dd03e19cSHans Petter Selasky goto repeat; 2762dd03e19cSHans Petter Selasky } 2763dd03e19cSHans Petter Selasky 2764dd03e19cSHans Petter Selasky got_rx_status = 1; 2765dd03e19cSHans Petter Selasky 2766dd03e19cSHans Petter Selasky DPRINTFN(5, "RX status = 0x%08x: ch=%d pid=%d bytes=%d sts=%d\n", 2767dd03e19cSHans Petter Selasky sc->sc_last_rx_status, ep_no, 2768dd03e19cSHans Petter Selasky (sc->sc_last_rx_status >> 15) & 3, 2769710764f7SHans Petter Selasky GRXSTSRD_BCNT_GET(sc->sc_last_rx_status), 2770dd03e19cSHans Petter Selasky (sc->sc_last_rx_status >> 17) & 15); 2771dd03e19cSHans Petter Selasky } else { 2772dd03e19cSHans Petter Selasky got_rx_status = 0; 2773dd03e19cSHans Petter Selasky } 2774dd03e19cSHans Petter Selasky } else { 2775dd03e19cSHans Petter Selasky uint8_t ep_no; 2776dd03e19cSHans Petter Selasky 2777710764f7SHans Petter Selasky ep_no = GRXSTSRD_CHNUM_GET( 2778dd03e19cSHans Petter Selasky sc->sc_last_rx_status); 2779dd03e19cSHans Petter Selasky 2780dd03e19cSHans Petter Selasky /* check if we should dump the data */ 27819cfd0731SHans Petter Selasky if (!(sc->sc_active_rx_ep & (1U << ep_no))) { 2782dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 2783dd03e19cSHans Petter Selasky goto repeat; 2784dd03e19cSHans Petter Selasky } 2785dd03e19cSHans Petter Selasky 2786dd03e19cSHans Petter Selasky got_rx_status = 1; 2787dd03e19cSHans Petter Selasky } 2788dd03e19cSHans Petter Selasky 27892fe7ad87SHans Petter Selasky /* execute FIFOs */ 27902fe7ad87SHans Petter Selasky TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) 27912fe7ad87SHans Petter Selasky dwc_otg_xfer_do_fifo(sc, xfer); 2792dd03e19cSHans Petter Selasky 2793dd03e19cSHans Petter Selasky if (got_rx_status) { 27949cfd0731SHans Petter Selasky /* check if data was consumed */ 2795dd03e19cSHans Petter Selasky if (sc->sc_last_rx_status == 0) 2796dd03e19cSHans Petter Selasky goto repeat; 2797dd03e19cSHans Petter Selasky 2798dd03e19cSHans Petter Selasky /* disable RX FIFO level interrupt */ 279942fabcc3SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK; 2800710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 2801dd03e19cSHans Petter Selasky } 2802dd03e19cSHans Petter Selasky } 2803dd03e19cSHans Petter Selasky 2804dd03e19cSHans Petter Selasky static void 28052994bac4SHans Petter Selasky dwc_otg_interrupt_complete_locked(struct dwc_otg_softc *sc) 28062fe7ad87SHans Petter Selasky { 28072fe7ad87SHans Petter Selasky struct usb_xfer *xfer; 28082fe7ad87SHans Petter Selasky repeat: 28092fe7ad87SHans Petter Selasky /* scan for completion events */ 28102fe7ad87SHans Petter Selasky TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { 28112994bac4SHans Petter Selasky if (dwc_otg_xfer_do_complete_locked(sc, xfer)) 28122fe7ad87SHans Petter Selasky goto repeat; 28132fe7ad87SHans Petter Selasky } 28142fe7ad87SHans Petter Selasky } 28152fe7ad87SHans Petter Selasky 28162fe7ad87SHans Petter Selasky static void 2817dd03e19cSHans Petter Selasky dwc_otg_vbus_interrupt(struct dwc_otg_softc *sc, uint8_t is_on) 2818dd03e19cSHans Petter Selasky { 2819dd03e19cSHans Petter Selasky DPRINTFN(5, "vbus = %u\n", is_on); 2820dd03e19cSHans Petter Selasky 2821491d5d05SHans Petter Selasky /* 2822491d5d05SHans Petter Selasky * If the USB host mode is forced, then assume VBUS is always 2823491d5d05SHans Petter Selasky * present else rely on the input to this function: 2824491d5d05SHans Petter Selasky */ 2825491d5d05SHans Petter Selasky if ((is_on != 0) || (sc->sc_mode == DWC_MODE_HOST)) { 2826dd03e19cSHans Petter Selasky if (!sc->sc_flags.status_vbus) { 2827dd03e19cSHans Petter Selasky sc->sc_flags.status_vbus = 1; 2828dd03e19cSHans Petter Selasky 2829dd03e19cSHans Petter Selasky /* complete root HUB interrupt endpoint */ 2830dd03e19cSHans Petter Selasky 2831dd03e19cSHans Petter Selasky dwc_otg_root_intr(sc); 2832dd03e19cSHans Petter Selasky } 2833dd03e19cSHans Petter Selasky } else { 2834dd03e19cSHans Petter Selasky if (sc->sc_flags.status_vbus) { 2835dd03e19cSHans Petter Selasky sc->sc_flags.status_vbus = 0; 2836dd03e19cSHans Petter Selasky sc->sc_flags.status_bus_reset = 0; 2837dd03e19cSHans Petter Selasky sc->sc_flags.status_suspend = 0; 2838dd03e19cSHans Petter Selasky sc->sc_flags.change_suspend = 0; 2839dd03e19cSHans Petter Selasky sc->sc_flags.change_connect = 1; 2840dd03e19cSHans Petter Selasky 2841dd03e19cSHans Petter Selasky /* complete root HUB interrupt endpoint */ 2842dd03e19cSHans Petter Selasky 2843dd03e19cSHans Petter Selasky dwc_otg_root_intr(sc); 2844dd03e19cSHans Petter Selasky } 2845dd03e19cSHans Petter Selasky } 2846dd03e19cSHans Petter Selasky } 2847dd03e19cSHans Petter Selasky 28482fe7ad87SHans Petter Selasky int 28492fe7ad87SHans Petter Selasky dwc_otg_filter_interrupt(void *arg) 2850dd03e19cSHans Petter Selasky { 28512fe7ad87SHans Petter Selasky struct dwc_otg_softc *sc = arg; 28522fe7ad87SHans Petter Selasky int retval = FILTER_HANDLED; 2853dd03e19cSHans Petter Selasky uint32_t status; 2854dd03e19cSHans Petter Selasky 28552994bac4SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 28562994bac4SHans Petter Selasky 2857dd03e19cSHans Petter Selasky /* read and clear interrupt status */ 2858710764f7SHans Petter Selasky status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); 28592fe7ad87SHans Petter Selasky 28602fe7ad87SHans Petter Selasky /* clear interrupts we are handling here */ 28612fe7ad87SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & ~DWC_OTG_MSK_GINT_THREAD_IRQ); 28622fe7ad87SHans Petter Selasky 28632fe7ad87SHans Petter Selasky /* check for USB state change interrupts */ 28642fe7ad87SHans Petter Selasky if ((status & DWC_OTG_MSK_GINT_THREAD_IRQ) != 0) 28652fe7ad87SHans Petter Selasky retval = FILTER_SCHEDULE_THREAD; 28662fe7ad87SHans Petter Selasky 2867a529288dSHans Petter Selasky /* clear FIFO empty interrupts */ 2868a529288dSHans Petter Selasky if (status & sc->sc_irq_mask & 2869a529288dSHans Petter Selasky (GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP)) { 2870a529288dSHans Petter Selasky sc->sc_irq_mask &= ~(GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP); 2871a529288dSHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 2872a529288dSHans Petter Selasky } 28732fe7ad87SHans Petter Selasky /* clear all IN endpoint interrupts */ 28742fe7ad87SHans Petter Selasky if (status & GINTSTS_IEPINT) { 28752fe7ad87SHans Petter Selasky uint32_t temp; 28762fe7ad87SHans Petter Selasky uint8_t x; 28772fe7ad87SHans Petter Selasky 28782fe7ad87SHans Petter Selasky for (x = 0; x != sc->sc_dev_in_ep_max; x++) { 28792fe7ad87SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DIEPINT(x)); 2880e4d17320SHans Petter Selasky /* 2881e4d17320SHans Petter Selasky * NOTE: Need to clear all interrupt bits, 2882e4d17320SHans Petter Selasky * because some appears to be unmaskable and 2883e4d17320SHans Petter Selasky * can cause an interrupt loop: 2884e4d17320SHans Petter Selasky */ 2885e4d17320SHans Petter Selasky if (temp != 0) 2886e4d17320SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPINT(x), temp); 28872fe7ad87SHans Petter Selasky } 28882fe7ad87SHans Petter Selasky } 28892fe7ad87SHans Petter Selasky 28902fe7ad87SHans Petter Selasky /* poll FIFOs, if any */ 28912994bac4SHans Petter Selasky dwc_otg_interrupt_poll_locked(sc); 28922fe7ad87SHans Petter Selasky 28932fe7ad87SHans Petter Selasky if (sc->sc_xfer_complete != 0) 28942fe7ad87SHans Petter Selasky retval = FILTER_SCHEDULE_THREAD; 28952fe7ad87SHans Petter Selasky 28962fe7ad87SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 28972fe7ad87SHans Petter Selasky 28982fe7ad87SHans Petter Selasky return (retval); 28992fe7ad87SHans Petter Selasky } 29002fe7ad87SHans Petter Selasky 29012fe7ad87SHans Petter Selasky void 29022fe7ad87SHans Petter Selasky dwc_otg_interrupt(void *arg) 29032fe7ad87SHans Petter Selasky { 29042fe7ad87SHans Petter Selasky struct dwc_otg_softc *sc = arg; 29052fe7ad87SHans Petter Selasky uint32_t status; 29062fe7ad87SHans Petter Selasky 29072fe7ad87SHans Petter Selasky USB_BUS_LOCK(&sc->sc_bus); 29082fe7ad87SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 29092fe7ad87SHans Petter Selasky 29102fe7ad87SHans Petter Selasky /* read and clear interrupt status */ 29112fe7ad87SHans Petter Selasky status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); 29122fe7ad87SHans Petter Selasky 29132fe7ad87SHans Petter Selasky /* clear interrupts we are handling here */ 29142fe7ad87SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & DWC_OTG_MSK_GINT_THREAD_IRQ); 2915dd03e19cSHans Petter Selasky 291692a80a4cSHans Petter Selasky DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n", 2917b792f659SHans Petter Selasky status, DWC_OTG_READ_4(sc, DOTG_HAINT), 2918b792f659SHans Petter Selasky DWC_OTG_READ_4(sc, DOTG_HFNUM)); 291992a80a4cSHans Petter Selasky 2920710764f7SHans Petter Selasky if (status & GINTSTS_USBRST) { 29210b8de869SHans Petter Selasky /* set correct state */ 29229cfd0731SHans Petter Selasky sc->sc_flags.status_device_mode = 1; 29230b8de869SHans Petter Selasky sc->sc_flags.status_bus_reset = 0; 29240b8de869SHans Petter Selasky sc->sc_flags.status_suspend = 0; 29250b8de869SHans Petter Selasky sc->sc_flags.change_suspend = 0; 29260b8de869SHans Petter Selasky sc->sc_flags.change_connect = 1; 29270b8de869SHans Petter Selasky 292842fabcc3SHans Petter Selasky /* Disable SOF interrupt */ 292942fabcc3SHans Petter Selasky sc->sc_irq_mask &= ~GINTMSK_SOFMSK; 293042fabcc3SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 293142fabcc3SHans Petter Selasky 29320b8de869SHans Petter Selasky /* complete root HUB interrupt endpoint */ 29330b8de869SHans Petter Selasky dwc_otg_root_intr(sc); 29340b8de869SHans Petter Selasky } 29350b8de869SHans Petter Selasky 2936dd03e19cSHans Petter Selasky /* check for any bus state change interrupts */ 2937710764f7SHans Petter Selasky if (status & GINTSTS_ENUMDONE) { 2938dd03e19cSHans Petter Selasky uint32_t temp; 2939dd03e19cSHans Petter Selasky 2940dd03e19cSHans Petter Selasky DPRINTFN(5, "end of reset\n"); 2941dd03e19cSHans Petter Selasky 2942dd03e19cSHans Petter Selasky /* set correct state */ 29439cfd0731SHans Petter Selasky sc->sc_flags.status_device_mode = 1; 2944dd03e19cSHans Petter Selasky sc->sc_flags.status_bus_reset = 1; 2945dd03e19cSHans Petter Selasky sc->sc_flags.status_suspend = 0; 2946dd03e19cSHans Petter Selasky sc->sc_flags.change_suspend = 0; 2947dd03e19cSHans Petter Selasky sc->sc_flags.change_connect = 1; 29489cfd0731SHans Petter Selasky sc->sc_flags.status_low_speed = 0; 29499cfd0731SHans Petter Selasky sc->sc_flags.port_enabled = 1; 2950dd03e19cSHans Petter Selasky 2951dd03e19cSHans Petter Selasky /* reset FIFOs */ 2952db4300daSHans Petter Selasky (void) dwc_otg_init_fifo(sc, DWC_MODE_DEVICE); 2953dd03e19cSHans Petter Selasky 2954dd03e19cSHans Petter Selasky /* reset function address */ 2955dd03e19cSHans Petter Selasky dwc_otg_set_address(sc, 0); 2956dd03e19cSHans Petter Selasky 2957dd03e19cSHans Petter Selasky /* figure out enumeration speed */ 2958710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DSTS); 29599cfd0731SHans Petter Selasky if (DSTS_ENUMSPD_GET(temp) == DSTS_ENUMSPD_HI) 2960dd03e19cSHans Petter Selasky sc->sc_flags.status_high_speed = 1; 2961dd03e19cSHans Petter Selasky else 2962dd03e19cSHans Petter Selasky sc->sc_flags.status_high_speed = 0; 2963dd03e19cSHans Petter Selasky 296442fabcc3SHans Petter Selasky /* 296542fabcc3SHans Petter Selasky * Disable resume and SOF interrupt, and enable 296642fabcc3SHans Petter Selasky * suspend and RX frame interrupt: 296742fabcc3SHans Petter Selasky */ 296842fabcc3SHans Petter Selasky sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK); 2969dea9afcfSHans Petter Selasky sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; 2970710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 2971dd03e19cSHans Petter Selasky 2972dd03e19cSHans Petter Selasky /* complete root HUB interrupt endpoint */ 2973dd03e19cSHans Petter Selasky dwc_otg_root_intr(sc); 2974dd03e19cSHans Petter Selasky } 29759cfd0731SHans Petter Selasky 29769cfd0731SHans Petter Selasky if (status & GINTSTS_PRTINT) { 29779cfd0731SHans Petter Selasky uint32_t hprt; 29789cfd0731SHans Petter Selasky 29799cfd0731SHans Petter Selasky hprt = DWC_OTG_READ_4(sc, DOTG_HPRT); 29809cfd0731SHans Petter Selasky 29819cfd0731SHans Petter Selasky /* clear change bits */ 29829cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, (hprt & ( 29839cfd0731SHans Petter Selasky HPRT_PRTPWR | HPRT_PRTENCHNG | 29849cfd0731SHans Petter Selasky HPRT_PRTCONNDET | HPRT_PRTOVRCURRCHNG)) | 29859cfd0731SHans Petter Selasky sc->sc_hprt_val); 29869cfd0731SHans Petter Selasky 29879cfd0731SHans Petter Selasky DPRINTFN(12, "GINTSTS=0x%08x, HPRT=0x%08x\n", status, hprt); 29889cfd0731SHans Petter Selasky 29899cfd0731SHans Petter Selasky sc->sc_flags.status_device_mode = 0; 29909cfd0731SHans Petter Selasky 29919cfd0731SHans Petter Selasky if (hprt & HPRT_PRTCONNSTS) 29929cfd0731SHans Petter Selasky sc->sc_flags.status_bus_reset = 1; 29939cfd0731SHans Petter Selasky else 29949cfd0731SHans Petter Selasky sc->sc_flags.status_bus_reset = 0; 29959cfd0731SHans Petter Selasky 29962d890458SHans Petter Selasky if ((hprt & HPRT_PRTENCHNG) && 29972d890458SHans Petter Selasky (hprt & HPRT_PRTENA) == 0) 29989cfd0731SHans Petter Selasky sc->sc_flags.change_enabled = 1; 29999cfd0731SHans Petter Selasky 30009cfd0731SHans Petter Selasky if (hprt & HPRT_PRTENA) 30019cfd0731SHans Petter Selasky sc->sc_flags.port_enabled = 1; 30029cfd0731SHans Petter Selasky else 30039cfd0731SHans Petter Selasky sc->sc_flags.port_enabled = 0; 30049cfd0731SHans Petter Selasky 30059cfd0731SHans Petter Selasky if (hprt & HPRT_PRTOVRCURRCHNG) 30069cfd0731SHans Petter Selasky sc->sc_flags.change_over_current = 1; 30079cfd0731SHans Petter Selasky 30089cfd0731SHans Petter Selasky if (hprt & HPRT_PRTOVRCURRACT) 30099cfd0731SHans Petter Selasky sc->sc_flags.port_over_current = 1; 30109cfd0731SHans Petter Selasky else 30119cfd0731SHans Petter Selasky sc->sc_flags.port_over_current = 0; 30129cfd0731SHans Petter Selasky 30139cfd0731SHans Petter Selasky if (hprt & HPRT_PRTPWR) 30149cfd0731SHans Petter Selasky sc->sc_flags.port_powered = 1; 30159cfd0731SHans Petter Selasky else 30169cfd0731SHans Petter Selasky sc->sc_flags.port_powered = 0; 30179cfd0731SHans Petter Selasky 30189cfd0731SHans Petter Selasky if (((hprt & HPRT_PRTSPD_MASK) 30199cfd0731SHans Petter Selasky >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_LOW) 30209cfd0731SHans Petter Selasky sc->sc_flags.status_low_speed = 1; 30219cfd0731SHans Petter Selasky else 30229cfd0731SHans Petter Selasky sc->sc_flags.status_low_speed = 0; 30239cfd0731SHans Petter Selasky 30249cfd0731SHans Petter Selasky if (((hprt & HPRT_PRTSPD_MASK) 30259cfd0731SHans Petter Selasky >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_HIGH) 30269cfd0731SHans Petter Selasky sc->sc_flags.status_high_speed = 1; 30279cfd0731SHans Petter Selasky else 30289cfd0731SHans Petter Selasky sc->sc_flags.status_high_speed = 0; 30299cfd0731SHans Petter Selasky 30309cfd0731SHans Petter Selasky if (hprt & HPRT_PRTCONNDET) 30319cfd0731SHans Petter Selasky sc->sc_flags.change_connect = 1; 30329cfd0731SHans Petter Selasky 30339cfd0731SHans Petter Selasky if (hprt & HPRT_PRTSUSP) 30349cfd0731SHans Petter Selasky dwc_otg_suspend_irq(sc); 30359cfd0731SHans Petter Selasky else 30369cfd0731SHans Petter Selasky dwc_otg_resume_irq(sc); 30379cfd0731SHans Petter Selasky 30389cfd0731SHans Petter Selasky /* complete root HUB interrupt endpoint */ 30399cfd0731SHans Petter Selasky dwc_otg_root_intr(sc); 304042fabcc3SHans Petter Selasky 304142fabcc3SHans Petter Selasky /* update host frame interval */ 304242fabcc3SHans Petter Selasky dwc_otg_update_host_frame_interval(sc); 30439cfd0731SHans Petter Selasky } 30449cfd0731SHans Petter Selasky 3045dd03e19cSHans Petter Selasky /* 3046dd03e19cSHans Petter Selasky * If resume and suspend is set at the same time we interpret 3047dd03e19cSHans Petter Selasky * that like RESUME. Resume is set when there is at least 3 3048dd03e19cSHans Petter Selasky * milliseconds of inactivity on the USB BUS. 3049dd03e19cSHans Petter Selasky */ 3050710764f7SHans Petter Selasky if (status & GINTSTS_WKUPINT) { 3051dd03e19cSHans Petter Selasky DPRINTFN(5, "resume interrupt\n"); 3052dd03e19cSHans Petter Selasky 3053dd03e19cSHans Petter Selasky dwc_otg_resume_irq(sc); 3054dd03e19cSHans Petter Selasky 3055710764f7SHans Petter Selasky } else if (status & GINTSTS_USBSUSP) { 3056dd03e19cSHans Petter Selasky DPRINTFN(5, "suspend interrupt\n"); 3057dd03e19cSHans Petter Selasky 30589cfd0731SHans Petter Selasky dwc_otg_suspend_irq(sc); 3059dd03e19cSHans Petter Selasky } 3060dd03e19cSHans Petter Selasky /* check VBUS */ 3061710764f7SHans Petter Selasky if (status & (GINTSTS_USBSUSP | 3062710764f7SHans Petter Selasky GINTSTS_USBRST | 30639cfd0731SHans Petter Selasky GINTMSK_OTGINTMSK | 3064710764f7SHans Petter Selasky GINTSTS_SESSREQINT)) { 3065dd03e19cSHans Petter Selasky uint32_t temp; 3066dd03e19cSHans Petter Selasky 3067710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GOTGCTL); 3068dd03e19cSHans Petter Selasky 3069dd03e19cSHans Petter Selasky DPRINTFN(5, "GOTGCTL=0x%08x\n", temp); 3070dd03e19cSHans Petter Selasky 3071dd03e19cSHans Petter Selasky dwc_otg_vbus_interrupt(sc, 30729cfd0731SHans Petter Selasky (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0); 3073dd03e19cSHans Petter Selasky } 3074dd03e19cSHans Petter Selasky 30752fe7ad87SHans Petter Selasky if (sc->sc_xfer_complete != 0) { 30762fe7ad87SHans Petter Selasky sc->sc_xfer_complete = 0; 3077dd03e19cSHans Petter Selasky 30782fe7ad87SHans Petter Selasky /* complete FIFOs, if any */ 30792994bac4SHans Petter Selasky dwc_otg_interrupt_complete_locked(sc); 30802fe7ad87SHans Petter Selasky } 30812fe7ad87SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 3082dd03e19cSHans Petter Selasky USB_BUS_UNLOCK(&sc->sc_bus); 3083dd03e19cSHans Petter Selasky } 3084dd03e19cSHans Petter Selasky 3085dd03e19cSHans Petter Selasky static void 3086dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp) 3087dd03e19cSHans Petter Selasky { 3088dd03e19cSHans Petter Selasky struct dwc_otg_td *td; 3089dd03e19cSHans Petter Selasky 3090dd03e19cSHans Petter Selasky /* get current Transfer Descriptor */ 3091dd03e19cSHans Petter Selasky td = temp->td_next; 3092dd03e19cSHans Petter Selasky temp->td = td; 3093dd03e19cSHans Petter Selasky 3094dd03e19cSHans Petter Selasky /* prepare for next TD */ 3095dd03e19cSHans Petter Selasky temp->td_next = td->obj_next; 3096dd03e19cSHans Petter Selasky 3097dd03e19cSHans Petter Selasky /* fill out the Transfer Descriptor */ 3098dd03e19cSHans Petter Selasky td->func = temp->func; 3099dd03e19cSHans Petter Selasky td->pc = temp->pc; 3100dd03e19cSHans Petter Selasky td->offset = temp->offset; 3101dd03e19cSHans Petter Selasky td->remainder = temp->len; 3102dd03e19cSHans Petter Selasky td->tx_bytes = 0; 31039cfd0731SHans Petter Selasky td->error_any = 0; 3104e7162865SHans Petter Selasky td->error_stall = 0; 31059cfd0731SHans Petter Selasky td->npkt = 0; 3106dd03e19cSHans Petter Selasky td->did_stall = temp->did_stall; 3107dd03e19cSHans Petter Selasky td->short_pkt = temp->short_pkt; 3108dd03e19cSHans Petter Selasky td->alt_next = temp->setup_alt_next; 31099cfd0731SHans Petter Selasky td->set_toggle = 0; 3110beefefd4SHans Petter Selasky td->got_short = 0; 31113eabad25SHans Petter Selasky td->did_nak = 0; 3112ce842cecSHans Petter Selasky td->channel[0] = DWC_OTG_MAX_CHANNELS; 3113ce842cecSHans Petter Selasky td->channel[1] = DWC_OTG_MAX_CHANNELS; 3114ce842cecSHans Petter Selasky td->channel[2] = DWC_OTG_MAX_CHANNELS; 31153eabad25SHans Petter Selasky td->state = 0; 31163eabad25SHans Petter Selasky td->errcnt = 0; 3117bc990482SHans Petter Selasky td->tt_scheduled = 0; 3118bc990482SHans Petter Selasky td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; 3119dd03e19cSHans Petter Selasky } 3120dd03e19cSHans Petter Selasky 3121dd03e19cSHans Petter Selasky static void 3122dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain(struct usb_xfer *xfer) 3123dd03e19cSHans Petter Selasky { 3124dd03e19cSHans Petter Selasky struct dwc_otg_std_temp temp; 3125dd03e19cSHans Petter Selasky struct dwc_otg_td *td; 3126dd03e19cSHans Petter Selasky uint32_t x; 3127dd03e19cSHans Petter Selasky uint8_t need_sync; 31289cfd0731SHans Petter Selasky uint8_t is_host; 3129dd03e19cSHans Petter Selasky 3130dd03e19cSHans Petter Selasky DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", 3131dd03e19cSHans Petter Selasky xfer->address, UE_GET_ADDR(xfer->endpointno), 3132dd03e19cSHans Petter Selasky xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); 3133dd03e19cSHans Petter Selasky 3134dd03e19cSHans Petter Selasky temp.max_frame_size = xfer->max_frame_size; 3135dd03e19cSHans Petter Selasky 3136dd03e19cSHans Petter Selasky td = xfer->td_start[0]; 3137dd03e19cSHans Petter Selasky xfer->td_transfer_first = td; 3138dd03e19cSHans Petter Selasky xfer->td_transfer_cache = td; 3139dd03e19cSHans Petter Selasky 3140dd03e19cSHans Petter Selasky /* setup temp */ 3141dd03e19cSHans Petter Selasky 3142dd03e19cSHans Petter Selasky temp.pc = NULL; 3143dd03e19cSHans Petter Selasky temp.td = NULL; 3144dd03e19cSHans Petter Selasky temp.td_next = xfer->td_start[0]; 3145dd03e19cSHans Petter Selasky temp.offset = 0; 3146bc990482SHans Petter Selasky temp.setup_alt_next = xfer->flags_int.short_frames_ok || 3147bc990482SHans Petter Selasky xfer->flags_int.isochronous_xfr; 3148dd03e19cSHans Petter Selasky temp.did_stall = !xfer->flags_int.control_stall; 3149dd03e19cSHans Petter Selasky 31509cfd0731SHans Petter Selasky is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST); 31519cfd0731SHans Petter Selasky 3152dd03e19cSHans Petter Selasky /* check if we should prepend a setup message */ 3153dd03e19cSHans Petter Selasky 3154dd03e19cSHans Petter Selasky if (xfer->flags_int.control_xfr) { 3155dd03e19cSHans Petter Selasky if (xfer->flags_int.control_hdr) { 31569cfd0731SHans Petter Selasky if (is_host) 31579cfd0731SHans Petter Selasky temp.func = &dwc_otg_host_setup_tx; 31589cfd0731SHans Petter Selasky else 3159dd03e19cSHans Petter Selasky temp.func = &dwc_otg_setup_rx; 31609cfd0731SHans Petter Selasky 3161dd03e19cSHans Petter Selasky temp.len = xfer->frlengths[0]; 3162dd03e19cSHans Petter Selasky temp.pc = xfer->frbuffers + 0; 3163dd03e19cSHans Petter Selasky temp.short_pkt = temp.len ? 1 : 0; 3164dd03e19cSHans Petter Selasky 3165dd03e19cSHans Petter Selasky /* check for last frame */ 3166dd03e19cSHans Petter Selasky if (xfer->nframes == 1) { 3167dd03e19cSHans Petter Selasky /* no STATUS stage yet, SETUP is last */ 3168dd03e19cSHans Petter Selasky if (xfer->flags_int.control_act) 3169dd03e19cSHans Petter Selasky temp.setup_alt_next = 0; 3170dd03e19cSHans Petter Selasky } 3171dd03e19cSHans Petter Selasky 3172dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(&temp); 3173dd03e19cSHans Petter Selasky } 3174dd03e19cSHans Petter Selasky x = 1; 3175dd03e19cSHans Petter Selasky } else { 3176dd03e19cSHans Petter Selasky x = 0; 3177dd03e19cSHans Petter Selasky } 3178dd03e19cSHans Petter Selasky 3179dd03e19cSHans Petter Selasky if (x != xfer->nframes) { 3180dd03e19cSHans Petter Selasky if (xfer->endpointno & UE_DIR_IN) { 31819cfd0731SHans Petter Selasky if (is_host) { 31829cfd0731SHans Petter Selasky temp.func = &dwc_otg_host_data_rx; 31839cfd0731SHans Petter Selasky need_sync = 0; 31849cfd0731SHans Petter Selasky } else { 3185dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_tx; 3186dd03e19cSHans Petter Selasky need_sync = 1; 31879cfd0731SHans Petter Selasky } 31889cfd0731SHans Petter Selasky } else { 31899cfd0731SHans Petter Selasky if (is_host) { 31909cfd0731SHans Petter Selasky temp.func = &dwc_otg_host_data_tx; 3191537aca95SHans Petter Selasky need_sync = 0; 3192dd03e19cSHans Petter Selasky } else { 3193dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_rx; 3194dd03e19cSHans Petter Selasky need_sync = 0; 3195dd03e19cSHans Petter Selasky } 31969cfd0731SHans Petter Selasky } 3197dd03e19cSHans Petter Selasky 3198dd03e19cSHans Petter Selasky /* setup "pc" pointer */ 3199dd03e19cSHans Petter Selasky temp.pc = xfer->frbuffers + x; 3200dd03e19cSHans Petter Selasky } else { 3201dd03e19cSHans Petter Selasky need_sync = 0; 3202dd03e19cSHans Petter Selasky } 3203dd03e19cSHans Petter Selasky while (x != xfer->nframes) { 3204dd03e19cSHans Petter Selasky /* DATA0 / DATA1 message */ 3205dd03e19cSHans Petter Selasky 3206dd03e19cSHans Petter Selasky temp.len = xfer->frlengths[x]; 3207dd03e19cSHans Petter Selasky 3208dd03e19cSHans Petter Selasky x++; 3209dd03e19cSHans Petter Selasky 3210dd03e19cSHans Petter Selasky if (x == xfer->nframes) { 3211dd03e19cSHans Petter Selasky if (xfer->flags_int.control_xfr) { 3212dd03e19cSHans Petter Selasky if (xfer->flags_int.control_act) { 3213dd03e19cSHans Petter Selasky temp.setup_alt_next = 0; 3214dd03e19cSHans Petter Selasky } 3215dd03e19cSHans Petter Selasky } else { 3216dd03e19cSHans Petter Selasky temp.setup_alt_next = 0; 3217dd03e19cSHans Petter Selasky } 3218dd03e19cSHans Petter Selasky } 3219dd03e19cSHans Petter Selasky if (temp.len == 0) { 3220dd03e19cSHans Petter Selasky /* make sure that we send an USB packet */ 3221dd03e19cSHans Petter Selasky 3222dd03e19cSHans Petter Selasky temp.short_pkt = 0; 3223dd03e19cSHans Petter Selasky 3224dd03e19cSHans Petter Selasky } else { 3225dd03e19cSHans Petter Selasky /* regular data transfer */ 3226dd03e19cSHans Petter Selasky 3227dd03e19cSHans Petter Selasky temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1); 3228dd03e19cSHans Petter Selasky } 3229dd03e19cSHans Petter Selasky 3230dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(&temp); 3231dd03e19cSHans Petter Selasky 3232dd03e19cSHans Petter Selasky if (xfer->flags_int.isochronous_xfr) { 3233dd03e19cSHans Petter Selasky temp.offset += temp.len; 3234dd03e19cSHans Petter Selasky } else { 3235dd03e19cSHans Petter Selasky /* get next Page Cache pointer */ 3236dd03e19cSHans Petter Selasky temp.pc = xfer->frbuffers + x; 3237dd03e19cSHans Petter Selasky } 3238dd03e19cSHans Petter Selasky } 3239dd03e19cSHans Petter Selasky 3240dd03e19cSHans Petter Selasky if (xfer->flags_int.control_xfr) { 3241dd03e19cSHans Petter Selasky /* always setup a valid "pc" pointer for status and sync */ 3242dd03e19cSHans Petter Selasky temp.pc = xfer->frbuffers + 0; 3243dd03e19cSHans Petter Selasky temp.len = 0; 3244dd03e19cSHans Petter Selasky temp.short_pkt = 0; 3245dd03e19cSHans Petter Selasky temp.setup_alt_next = 0; 3246dd03e19cSHans Petter Selasky 3247dd03e19cSHans Petter Selasky /* check if we need to sync */ 3248dd03e19cSHans Petter Selasky if (need_sync) { 3249dd03e19cSHans Petter Selasky /* we need a SYNC point after TX */ 3250dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_tx_sync; 3251dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(&temp); 3252dd03e19cSHans Petter Selasky } 3253dd03e19cSHans Petter Selasky 3254dd03e19cSHans Petter Selasky /* check if we should append a status stage */ 3255dd03e19cSHans Petter Selasky if (!xfer->flags_int.control_act) { 3256dd03e19cSHans Petter Selasky /* 3257dd03e19cSHans Petter Selasky * Send a DATA1 message and invert the current 3258dd03e19cSHans Petter Selasky * endpoint direction. 3259dd03e19cSHans Petter Selasky */ 3260dd03e19cSHans Petter Selasky if (xfer->endpointno & UE_DIR_IN) { 32619cfd0731SHans Petter Selasky if (is_host) { 32629cfd0731SHans Petter Selasky temp.func = &dwc_otg_host_data_tx; 3263537aca95SHans Petter Selasky need_sync = 0; 32649cfd0731SHans Petter Selasky } else { 3265dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_rx; 3266dd03e19cSHans Petter Selasky need_sync = 0; 32679cfd0731SHans Petter Selasky } 32689cfd0731SHans Petter Selasky } else { 32699cfd0731SHans Petter Selasky if (is_host) { 32709cfd0731SHans Petter Selasky temp.func = &dwc_otg_host_data_rx; 32719cfd0731SHans Petter Selasky need_sync = 0; 3272dd03e19cSHans Petter Selasky } else { 3273dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_tx; 3274dd03e19cSHans Petter Selasky need_sync = 1; 3275dd03e19cSHans Petter Selasky } 32769cfd0731SHans Petter Selasky } 3277dd03e19cSHans Petter Selasky 3278dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(&temp); 32799cfd0731SHans Petter Selasky 32809cfd0731SHans Petter Selasky /* data toggle should be DATA1 */ 32819cfd0731SHans Petter Selasky td = temp.td; 32829cfd0731SHans Petter Selasky td->set_toggle = 1; 32839cfd0731SHans Petter Selasky 3284dd03e19cSHans Petter Selasky if (need_sync) { 3285dd03e19cSHans Petter Selasky /* we need a SYNC point after TX */ 3286dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_tx_sync; 3287dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(&temp); 3288dd03e19cSHans Petter Selasky } 3289dd03e19cSHans Petter Selasky } 3290dd03e19cSHans Petter Selasky } else { 3291dd03e19cSHans Petter Selasky /* check if we need to sync */ 3292dd03e19cSHans Petter Selasky if (need_sync) { 3293dd03e19cSHans Petter Selasky temp.pc = xfer->frbuffers + 0; 3294dd03e19cSHans Petter Selasky temp.len = 0; 3295dd03e19cSHans Petter Selasky temp.short_pkt = 0; 3296dd03e19cSHans Petter Selasky temp.setup_alt_next = 0; 3297dd03e19cSHans Petter Selasky 3298dd03e19cSHans Petter Selasky /* we need a SYNC point after TX */ 3299dd03e19cSHans Petter Selasky temp.func = &dwc_otg_data_tx_sync; 3300dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain_sub(&temp); 3301dd03e19cSHans Petter Selasky } 3302dd03e19cSHans Petter Selasky } 3303dd03e19cSHans Petter Selasky 3304dd03e19cSHans Petter Selasky /* must have at least one frame! */ 3305dd03e19cSHans Petter Selasky td = temp.td; 3306dd03e19cSHans Petter Selasky xfer->td_transfer_last = td; 33079cfd0731SHans Petter Selasky 33089cfd0731SHans Petter Selasky if (is_host) { 33099cfd0731SHans Petter Selasky struct dwc_otg_softc *sc; 33103eabad25SHans Petter Selasky uint32_t hcchar; 33113eabad25SHans Petter Selasky uint32_t hcsplt; 33129cfd0731SHans Petter Selasky 33139cfd0731SHans Petter Selasky sc = DWC_OTG_BUS2SC(xfer->xroot->bus); 33149cfd0731SHans Petter Selasky 33159cfd0731SHans Petter Selasky /* get first again */ 33169cfd0731SHans Petter Selasky td = xfer->td_transfer_first; 33179cfd0731SHans Petter Selasky td->toggle = (xfer->endpoint->toggle_next ? 1 : 0); 33189cfd0731SHans Petter Selasky 33193eabad25SHans Petter Selasky hcchar = 33209cfd0731SHans Petter Selasky (xfer->address << HCCHAR_DEVADDR_SHIFT) | 33219cfd0731SHans Petter Selasky ((xfer->endpointno & UE_ADDR) << HCCHAR_EPNUM_SHIFT) | 332258ecbe49SHans Petter Selasky (xfer->max_packet_size << HCCHAR_MPS_SHIFT) | 332358ecbe49SHans Petter Selasky HCCHAR_CHENA; 33249cfd0731SHans Petter Selasky 3325dea9afcfSHans Petter Selasky /* 3326dea9afcfSHans Petter Selasky * We are not always able to meet the timing 3327dea9afcfSHans Petter Selasky * requirements of the USB interrupt endpoint's 3328dea9afcfSHans Petter Selasky * complete split token, when doing transfers going 3329dea9afcfSHans Petter Selasky * via a transaction translator. Use the CONTROL 3330dea9afcfSHans Petter Selasky * transfer type instead of the INTERRUPT transfer 3331dea9afcfSHans Petter Selasky * type in general, as a means to workaround 3332dea9afcfSHans Petter Selasky * that. This trick should work for both FULL and LOW 3333dea9afcfSHans Petter Selasky * speed USB traffic going through a TT. For non-TT 3334dea9afcfSHans Petter Selasky * traffic it works as well. The reason for using 3335dea9afcfSHans Petter Selasky * CONTROL type instead of BULK is that some TTs might 3336dea9afcfSHans Petter Selasky * reject LOW speed BULK traffic. 3337dea9afcfSHans Petter Selasky */ 3338dea9afcfSHans Petter Selasky if (td->ep_type == UE_INTERRUPT) 3339dea9afcfSHans Petter Selasky hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT); 3340dea9afcfSHans Petter Selasky else 3341dea9afcfSHans Petter Selasky hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT); 3342dea9afcfSHans Petter Selasky 33439cfd0731SHans Petter Selasky if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) 33443eabad25SHans Petter Selasky hcchar |= HCCHAR_EPDIR_IN; 33459cfd0731SHans Petter Selasky 33469cfd0731SHans Petter Selasky switch (xfer->xroot->udev->speed) { 33479cfd0731SHans Petter Selasky case USB_SPEED_LOW: 3348076daedaSHans Petter Selasky hcchar |= HCCHAR_LSPDDEV; 3349076daedaSHans Petter Selasky /* FALLTHROUGH */ 3350076daedaSHans Petter Selasky case USB_SPEED_FULL: 33519cfd0731SHans Petter Selasky /* check if root HUB port is running High Speed */ 3352076daedaSHans Petter Selasky if (dwc_otg_uses_split(xfer->xroot->udev)) { 33533eabad25SHans Petter Selasky hcsplt = HCSPLT_SPLTENA | 33549cfd0731SHans Petter Selasky (xfer->xroot->udev->hs_port_no << 33559cfd0731SHans Petter Selasky HCSPLT_PRTADDR_SHIFT) | 33569cfd0731SHans Petter Selasky (xfer->xroot->udev->hs_hub_addr << 33579cfd0731SHans Petter Selasky HCSPLT_HUBADDR_SHIFT); 33589cfd0731SHans Petter Selasky } else { 33593eabad25SHans Petter Selasky hcsplt = 0; 33609cfd0731SHans Petter Selasky } 3361db4300daSHans Petter Selasky if (td->ep_type == UE_INTERRUPT) { 3362d4b6c03eSHans Petter Selasky uint32_t ival; 33633eabad25SHans Petter Selasky ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE; 3364d4b6c03eSHans Petter Selasky if (ival == 0) 3365d4b6c03eSHans Petter Selasky ival = 1; 3366e7162865SHans Petter Selasky else if (ival > 127) 3367e7162865SHans Petter Selasky ival = 127; 33683eabad25SHans Petter Selasky td->tmr_val = sc->sc_tmr_val + ival; 33693eabad25SHans Petter Selasky td->tmr_res = ival; 3370db4300daSHans Petter Selasky } else if (td->ep_type == UE_ISOCHRONOUS) { 3371bc990482SHans Petter Selasky td->tmr_res = 1; 3372ce842cecSHans Petter Selasky td->tmr_val = sc->sc_last_frame_num; 3373ce842cecSHans Petter Selasky if (td->hcchar & HCCHAR_EPDIR_IN) 3374ce842cecSHans Petter Selasky td->tmr_val++; 3375bc990482SHans Petter Selasky } else { 3376bc990482SHans Petter Selasky td->tmr_val = 0; 3377ed0ed9b4SHans Petter Selasky td->tmr_res = (uint8_t)sc->sc_last_frame_num; 3378d4b6c03eSHans Petter Selasky } 33799cfd0731SHans Petter Selasky break; 3380460febc7SHans Petter Selasky case USB_SPEED_HIGH: 33813eabad25SHans Petter Selasky hcsplt = 0; 3382db4300daSHans Petter Selasky if (td->ep_type == UE_INTERRUPT) { 3383d4b6c03eSHans Petter Selasky uint32_t ival; 3384dea9afcfSHans Petter Selasky hcchar |= ((xfer->max_packet_count & 3) 3385dea9afcfSHans Petter Selasky << HCCHAR_MC_SHIFT); 33863eabad25SHans Petter Selasky ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE; 3387d4b6c03eSHans Petter Selasky if (ival == 0) 3388d4b6c03eSHans Petter Selasky ival = 1; 3389e7162865SHans Petter Selasky else if (ival > 127) 3390e7162865SHans Petter Selasky ival = 127; 33913eabad25SHans Petter Selasky td->tmr_val = sc->sc_tmr_val + ival; 33923eabad25SHans Petter Selasky td->tmr_res = ival; 3393db4300daSHans Petter Selasky } else if (td->ep_type == UE_ISOCHRONOUS) { 3394dea9afcfSHans Petter Selasky hcchar |= ((xfer->max_packet_count & 3) 3395dea9afcfSHans Petter Selasky << HCCHAR_MC_SHIFT); 3396bc990482SHans Petter Selasky td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer); 3397ce842cecSHans Petter Selasky td->tmr_val = sc->sc_last_frame_num; 3398ce842cecSHans Petter Selasky if (td->hcchar & HCCHAR_EPDIR_IN) 3399ce842cecSHans Petter Selasky td->tmr_val += td->tmr_res; 3400ce842cecSHans Petter Selasky 3401bc990482SHans Petter Selasky } else { 3402bc990482SHans Petter Selasky td->tmr_val = 0; 3403ed0ed9b4SHans Petter Selasky td->tmr_res = (uint8_t)sc->sc_last_frame_num; 3404d4b6c03eSHans Petter Selasky } 3405460febc7SHans Petter Selasky break; 34069cfd0731SHans Petter Selasky default: 34073eabad25SHans Petter Selasky hcsplt = 0; 34083eabad25SHans Petter Selasky td->tmr_val = 0; 34093eabad25SHans Petter Selasky td->tmr_res = 0; 3410bc990482SHans Petter Selasky break; 34113eabad25SHans Petter Selasky } 34123eabad25SHans Petter Selasky 34133eabad25SHans Petter Selasky /* store configuration in all TD's */ 34143eabad25SHans Petter Selasky while (1) { 34153eabad25SHans Petter Selasky td->hcchar = hcchar; 34163eabad25SHans Petter Selasky td->hcsplt = hcsplt; 34173eabad25SHans Petter Selasky 34183eabad25SHans Petter Selasky if (((void *)td) == xfer->td_transfer_last) 34193eabad25SHans Petter Selasky break; 34203eabad25SHans Petter Selasky 34213eabad25SHans Petter Selasky td = td->obj_next; 3422537aca95SHans Petter Selasky } 34239cfd0731SHans Petter Selasky } 3424dd03e19cSHans Petter Selasky } 3425dd03e19cSHans Petter Selasky 3426dd03e19cSHans Petter Selasky static void 3427dd03e19cSHans Petter Selasky dwc_otg_timeout(void *arg) 3428dd03e19cSHans Petter Selasky { 3429dd03e19cSHans Petter Selasky struct usb_xfer *xfer = arg; 3430dd03e19cSHans Petter Selasky 3431dd03e19cSHans Petter Selasky DPRINTF("xfer=%p\n", xfer); 3432dd03e19cSHans Petter Selasky 3433dd03e19cSHans Petter Selasky USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); 3434dd03e19cSHans Petter Selasky 3435dd03e19cSHans Petter Selasky /* transfer is transferred */ 3436dd03e19cSHans Petter Selasky dwc_otg_device_done(xfer, USB_ERR_TIMEOUT); 3437dd03e19cSHans Petter Selasky } 3438dd03e19cSHans Petter Selasky 3439dd03e19cSHans Petter Selasky static void 3440dd03e19cSHans Petter Selasky dwc_otg_start_standard_chain(struct usb_xfer *xfer) 3441dd03e19cSHans Petter Selasky { 3442bc990482SHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); 3443bc990482SHans Petter Selasky 3444dd03e19cSHans Petter Selasky DPRINTFN(9, "\n"); 3445dd03e19cSHans Petter Selasky 34464541f273SHans Petter Selasky /* 34474541f273SHans Petter Selasky * Poll one time in device mode, which will turn on the 34484541f273SHans Petter Selasky * endpoint interrupts. Else wait for SOF interrupt in host 34494541f273SHans Petter Selasky * mode. 34504541f273SHans Petter Selasky */ 34512994bac4SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 34522994bac4SHans Petter Selasky 34532fe7ad87SHans Petter Selasky if (sc->sc_flags.status_device_mode != 0) { 34542fe7ad87SHans Petter Selasky dwc_otg_xfer_do_fifo(sc, xfer); 34552994bac4SHans Petter Selasky if (dwc_otg_xfer_do_complete_locked(sc, xfer)) 34562994bac4SHans Petter Selasky goto done; 3457ce842cecSHans Petter Selasky } else { 3458ce842cecSHans Petter Selasky struct dwc_otg_td *td = xfer->td_transfer_cache; 3459ce842cecSHans Petter Selasky if (td->ep_type == UE_ISOCHRONOUS && 3460ce842cecSHans Petter Selasky (td->hcchar & HCCHAR_EPDIR_IN) == 0) { 3461ce842cecSHans Petter Selasky /* 3462ce842cecSHans Petter Selasky * Need to start ISOCHRONOUS OUT transfer ASAP 3463ce842cecSHans Petter Selasky * because execution is delayed by one 125us 3464ce842cecSHans Petter Selasky * microframe: 3465ce842cecSHans Petter Selasky */ 3466ce842cecSHans Petter Selasky dwc_otg_xfer_do_fifo(sc, xfer); 3467ce842cecSHans Petter Selasky if (dwc_otg_xfer_do_complete_locked(sc, xfer)) 3468ce842cecSHans Petter Selasky goto done; 3469ce842cecSHans Petter Selasky } 34702fe7ad87SHans Petter Selasky } 3471dd03e19cSHans Petter Selasky 3472dd03e19cSHans Petter Selasky /* put transfer on interrupt queue */ 3473dd03e19cSHans Petter Selasky usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); 3474dd03e19cSHans Petter Selasky 3475dd03e19cSHans Petter Selasky /* start timeout, if any */ 3476dd03e19cSHans Petter Selasky if (xfer->timeout != 0) { 3477dd03e19cSHans Petter Selasky usbd_transfer_timeout_ms(xfer, 3478dd03e19cSHans Petter Selasky &dwc_otg_timeout, xfer->timeout); 3479dd03e19cSHans Petter Selasky } 348042fabcc3SHans Petter Selasky 34814541f273SHans Petter Selasky if (sc->sc_flags.status_device_mode != 0) 34824541f273SHans Petter Selasky goto done; 34834541f273SHans Petter Selasky 348442fabcc3SHans Petter Selasky /* enable SOF interrupt, if any */ 348542fabcc3SHans Petter Selasky dwc_otg_enable_sof_irq(sc); 34862fe7ad87SHans Petter Selasky done: 34872fe7ad87SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 3488dd03e19cSHans Petter Selasky } 3489dd03e19cSHans Petter Selasky 3490dd03e19cSHans Petter Selasky static void 3491dd03e19cSHans Petter Selasky dwc_otg_root_intr(struct dwc_otg_softc *sc) 3492dd03e19cSHans Petter Selasky { 3493dd03e19cSHans Petter Selasky DPRINTFN(9, "\n"); 3494dd03e19cSHans Petter Selasky 3495dd03e19cSHans Petter Selasky USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); 3496dd03e19cSHans Petter Selasky 3497dd03e19cSHans Petter Selasky /* set port bit */ 3498dd03e19cSHans Petter Selasky sc->sc_hub_idata[0] = 0x02; /* we only have one port */ 3499dd03e19cSHans Petter Selasky 3500dd03e19cSHans Petter Selasky uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, 3501dd03e19cSHans Petter Selasky sizeof(sc->sc_hub_idata)); 3502dd03e19cSHans Petter Selasky } 3503dd03e19cSHans Petter Selasky 3504dd03e19cSHans Petter Selasky static usb_error_t 3505dd03e19cSHans Petter Selasky dwc_otg_standard_done_sub(struct usb_xfer *xfer) 3506dd03e19cSHans Petter Selasky { 3507dd03e19cSHans Petter Selasky struct dwc_otg_td *td; 3508dd03e19cSHans Petter Selasky uint32_t len; 35099cfd0731SHans Petter Selasky usb_error_t error; 3510dd03e19cSHans Petter Selasky 3511dd03e19cSHans Petter Selasky DPRINTFN(9, "\n"); 3512dd03e19cSHans Petter Selasky 3513dd03e19cSHans Petter Selasky td = xfer->td_transfer_cache; 3514dd03e19cSHans Petter Selasky 3515dd03e19cSHans Petter Selasky do { 3516dd03e19cSHans Petter Selasky len = td->remainder; 3517dd03e19cSHans Petter Selasky 35189cfd0731SHans Petter Selasky /* store last data toggle */ 35199cfd0731SHans Petter Selasky xfer->endpoint->toggle_next = td->toggle; 35209cfd0731SHans Petter Selasky 3521dd03e19cSHans Petter Selasky if (xfer->aframes != xfer->nframes) { 3522dd03e19cSHans Petter Selasky /* 3523dd03e19cSHans Petter Selasky * Verify the length and subtract 3524dd03e19cSHans Petter Selasky * the remainder from "frlengths[]": 3525dd03e19cSHans Petter Selasky */ 3526dd03e19cSHans Petter Selasky if (len > xfer->frlengths[xfer->aframes]) { 35279cfd0731SHans Petter Selasky td->error_any = 1; 3528dd03e19cSHans Petter Selasky } else { 3529dd03e19cSHans Petter Selasky xfer->frlengths[xfer->aframes] -= len; 3530dd03e19cSHans Petter Selasky } 3531dd03e19cSHans Petter Selasky } 3532dd03e19cSHans Petter Selasky /* Check for transfer error */ 35339cfd0731SHans Petter Selasky if (td->error_any) { 3534dd03e19cSHans Petter Selasky /* the transfer is finished */ 35359cfd0731SHans Petter Selasky error = (td->error_stall ? 35369cfd0731SHans Petter Selasky USB_ERR_STALLED : USB_ERR_IOERROR); 3537dd03e19cSHans Petter Selasky td = NULL; 3538dd03e19cSHans Petter Selasky break; 3539dd03e19cSHans Petter Selasky } 3540dd03e19cSHans Petter Selasky /* Check for short transfer */ 3541dd03e19cSHans Petter Selasky if (len > 0) { 3542bc990482SHans Petter Selasky if (xfer->flags_int.short_frames_ok || 3543bc990482SHans Petter Selasky xfer->flags_int.isochronous_xfr) { 3544dd03e19cSHans Petter Selasky /* follow alt next */ 3545dd03e19cSHans Petter Selasky if (td->alt_next) { 3546dd03e19cSHans Petter Selasky td = td->obj_next; 3547dd03e19cSHans Petter Selasky } else { 3548dd03e19cSHans Petter Selasky td = NULL; 3549dd03e19cSHans Petter Selasky } 3550dd03e19cSHans Petter Selasky } else { 3551dd03e19cSHans Petter Selasky /* the transfer is finished */ 3552dd03e19cSHans Petter Selasky td = NULL; 3553dd03e19cSHans Petter Selasky } 3554dd03e19cSHans Petter Selasky error = 0; 3555dd03e19cSHans Petter Selasky break; 3556dd03e19cSHans Petter Selasky } 3557dd03e19cSHans Petter Selasky td = td->obj_next; 3558dd03e19cSHans Petter Selasky 3559dd03e19cSHans Petter Selasky /* this USB frame is complete */ 3560dd03e19cSHans Petter Selasky error = 0; 3561dd03e19cSHans Petter Selasky break; 3562dd03e19cSHans Petter Selasky 3563dd03e19cSHans Petter Selasky } while (0); 3564dd03e19cSHans Petter Selasky 3565dd03e19cSHans Petter Selasky /* update transfer cache */ 3566dd03e19cSHans Petter Selasky 3567dd03e19cSHans Petter Selasky xfer->td_transfer_cache = td; 3568dd03e19cSHans Petter Selasky 356992a80a4cSHans Petter Selasky return (error); 3570dd03e19cSHans Petter Selasky } 3571dd03e19cSHans Petter Selasky 3572dd03e19cSHans Petter Selasky static void 3573dd03e19cSHans Petter Selasky dwc_otg_standard_done(struct usb_xfer *xfer) 3574dd03e19cSHans Petter Selasky { 3575dd03e19cSHans Petter Selasky usb_error_t err = 0; 3576dd03e19cSHans Petter Selasky 3577dd03e19cSHans Petter Selasky DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", 3578dd03e19cSHans Petter Selasky xfer, xfer->endpoint); 3579dd03e19cSHans Petter Selasky 3580dd03e19cSHans Petter Selasky /* reset scanner */ 3581dd03e19cSHans Petter Selasky 3582dd03e19cSHans Petter Selasky xfer->td_transfer_cache = xfer->td_transfer_first; 3583dd03e19cSHans Petter Selasky 3584dd03e19cSHans Petter Selasky if (xfer->flags_int.control_xfr) { 3585dd03e19cSHans Petter Selasky if (xfer->flags_int.control_hdr) { 3586dd03e19cSHans Petter Selasky err = dwc_otg_standard_done_sub(xfer); 3587dd03e19cSHans Petter Selasky } 3588dd03e19cSHans Petter Selasky xfer->aframes = 1; 3589dd03e19cSHans Petter Selasky 3590dd03e19cSHans Petter Selasky if (xfer->td_transfer_cache == NULL) { 3591dd03e19cSHans Petter Selasky goto done; 3592dd03e19cSHans Petter Selasky } 3593dd03e19cSHans Petter Selasky } 3594dd03e19cSHans Petter Selasky while (xfer->aframes != xfer->nframes) { 3595dd03e19cSHans Petter Selasky err = dwc_otg_standard_done_sub(xfer); 3596dd03e19cSHans Petter Selasky xfer->aframes++; 3597dd03e19cSHans Petter Selasky 3598dd03e19cSHans Petter Selasky if (xfer->td_transfer_cache == NULL) { 3599dd03e19cSHans Petter Selasky goto done; 3600dd03e19cSHans Petter Selasky } 3601dd03e19cSHans Petter Selasky } 3602dd03e19cSHans Petter Selasky 3603dd03e19cSHans Petter Selasky if (xfer->flags_int.control_xfr && 3604dd03e19cSHans Petter Selasky !xfer->flags_int.control_act) { 3605dd03e19cSHans Petter Selasky err = dwc_otg_standard_done_sub(xfer); 3606dd03e19cSHans Petter Selasky } 3607dd03e19cSHans Petter Selasky done: 3608dd03e19cSHans Petter Selasky dwc_otg_device_done(xfer, err); 3609dd03e19cSHans Petter Selasky } 3610dd03e19cSHans Petter Selasky 3611dd03e19cSHans Petter Selasky /*------------------------------------------------------------------------* 3612dd03e19cSHans Petter Selasky * dwc_otg_device_done 3613dd03e19cSHans Petter Selasky * 3614dd03e19cSHans Petter Selasky * NOTE: this function can be called more than one time on the 3615dd03e19cSHans Petter Selasky * same USB transfer! 3616dd03e19cSHans Petter Selasky *------------------------------------------------------------------------*/ 3617dd03e19cSHans Petter Selasky static void 3618dd03e19cSHans Petter Selasky dwc_otg_device_done(struct usb_xfer *xfer, usb_error_t error) 3619dd03e19cSHans Petter Selasky { 36202fe7ad87SHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); 36212fe7ad87SHans Petter Selasky 3622dd03e19cSHans Petter Selasky DPRINTFN(9, "xfer=%p, endpoint=%p, error=%d\n", 3623dd03e19cSHans Petter Selasky xfer, xfer->endpoint, error); 3624dd03e19cSHans Petter Selasky 36252994bac4SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 36262994bac4SHans Petter Selasky 3627dd03e19cSHans Petter Selasky if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { 36282fe7ad87SHans Petter Selasky /* Interrupts are cleared by the interrupt handler */ 36299cfd0731SHans Petter Selasky } else { 36309cfd0731SHans Petter Selasky struct dwc_otg_td *td; 36319cfd0731SHans Petter Selasky 363208aa4c94SHans Petter Selasky td = xfer->td_transfer_cache; 363308aa4c94SHans Petter Selasky if (td != NULL) 363408aa4c94SHans Petter Selasky dwc_otg_host_channel_free(sc, td); 3635dd03e19cSHans Petter Selasky } 3636dd03e19cSHans Petter Selasky /* dequeue transfer and start next transfer */ 3637dd03e19cSHans Petter Selasky usbd_transfer_done(xfer, error); 36382994bac4SHans Petter Selasky 36392994bac4SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 3640dd03e19cSHans Petter Selasky } 3641dd03e19cSHans Petter Selasky 3642dd03e19cSHans Petter Selasky static void 3643a5cf1aaaSHans Petter Selasky dwc_otg_xfer_stall(struct usb_xfer *xfer) 3644a5cf1aaaSHans Petter Selasky { 3645a5cf1aaaSHans Petter Selasky dwc_otg_device_done(xfer, USB_ERR_STALLED); 3646a5cf1aaaSHans Petter Selasky } 3647a5cf1aaaSHans Petter Selasky 3648a5cf1aaaSHans Petter Selasky static void 3649a5cf1aaaSHans Petter Selasky dwc_otg_set_stall(struct usb_device *udev, 3650dd03e19cSHans Petter Selasky struct usb_endpoint *ep, uint8_t *did_stall) 3651dd03e19cSHans Petter Selasky { 3652dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc; 3653dd03e19cSHans Petter Selasky uint32_t temp; 3654dd03e19cSHans Petter Selasky uint32_t reg; 3655dd03e19cSHans Petter Selasky uint8_t ep_no; 3656dd03e19cSHans Petter Selasky 3657dd03e19cSHans Petter Selasky USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); 3658dd03e19cSHans Petter Selasky 36599cfd0731SHans Petter Selasky /* check mode */ 36609cfd0731SHans Petter Selasky if (udev->flags.usb_mode != USB_MODE_DEVICE) { 36619cfd0731SHans Petter Selasky /* not supported */ 36629cfd0731SHans Petter Selasky return; 36639cfd0731SHans Petter Selasky } 36649cfd0731SHans Petter Selasky 3665dd03e19cSHans Petter Selasky sc = DWC_OTG_BUS2SC(udev->bus); 3666dd03e19cSHans Petter Selasky 36672994bac4SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 36682994bac4SHans Petter Selasky 3669dd03e19cSHans Petter Selasky /* get endpoint address */ 3670dd03e19cSHans Petter Selasky ep_no = ep->edesc->bEndpointAddress; 3671dd03e19cSHans Petter Selasky 3672dd03e19cSHans Petter Selasky DPRINTFN(5, "endpoint=0x%x\n", ep_no); 3673dd03e19cSHans Petter Selasky 3674dd03e19cSHans Petter Selasky if (ep_no & UE_DIR_IN) { 3675710764f7SHans Petter Selasky reg = DOTG_DIEPCTL(ep_no & UE_ADDR); 3676dd03e19cSHans Petter Selasky temp = sc->sc_in_ctl[ep_no & UE_ADDR]; 3677dd03e19cSHans Petter Selasky } else { 3678710764f7SHans Petter Selasky reg = DOTG_DOEPCTL(ep_no & UE_ADDR); 3679dd03e19cSHans Petter Selasky temp = sc->sc_out_ctl[ep_no & UE_ADDR]; 3680dd03e19cSHans Petter Selasky } 3681dd03e19cSHans Petter Selasky 3682dd03e19cSHans Petter Selasky /* disable and stall endpoint */ 3683710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_EPDIS); 3684710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_STALL); 3685dd03e19cSHans Petter Selasky 3686dd03e19cSHans Petter Selasky /* clear active OUT ep */ 3687dd03e19cSHans Petter Selasky if (!(ep_no & UE_DIR_IN)) { 36889cfd0731SHans Petter Selasky sc->sc_active_rx_ep &= ~(1U << (ep_no & UE_ADDR)); 3689dd03e19cSHans Petter Selasky 3690dd03e19cSHans Petter Selasky if (sc->sc_last_rx_status != 0 && 3691710764f7SHans Petter Selasky (ep_no & UE_ADDR) == GRXSTSRD_CHNUM_GET( 3692dd03e19cSHans Petter Selasky sc->sc_last_rx_status)) { 3693dd03e19cSHans Petter Selasky /* dump data */ 3694dd03e19cSHans Petter Selasky dwc_otg_common_rx_ack(sc); 3695dd03e19cSHans Petter Selasky /* poll interrupt */ 36962994bac4SHans Petter Selasky dwc_otg_interrupt_poll_locked(sc); 36972994bac4SHans Petter Selasky dwc_otg_interrupt_complete_locked(sc); 3698dd03e19cSHans Petter Selasky } 3699dd03e19cSHans Petter Selasky } 37002994bac4SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 3701dd03e19cSHans Petter Selasky } 3702dd03e19cSHans Petter Selasky 3703dd03e19cSHans Petter Selasky static void 37042994bac4SHans Petter Selasky dwc_otg_clear_stall_sub_locked(struct dwc_otg_softc *sc, uint32_t mps, 3705dd03e19cSHans Petter Selasky uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) 3706dd03e19cSHans Petter Selasky { 3707dd03e19cSHans Petter Selasky uint32_t reg; 3708dd03e19cSHans Petter Selasky uint32_t temp; 3709dd03e19cSHans Petter Selasky 3710dd03e19cSHans Petter Selasky if (ep_type == UE_CONTROL) { 3711dd03e19cSHans Petter Selasky /* clearing stall is not needed */ 3712dd03e19cSHans Petter Selasky return; 3713dd03e19cSHans Petter Selasky } 3714dd03e19cSHans Petter Selasky 3715dd03e19cSHans Petter Selasky if (ep_dir) { 3716710764f7SHans Petter Selasky reg = DOTG_DIEPCTL(ep_no); 3717dd03e19cSHans Petter Selasky } else { 3718710764f7SHans Petter Selasky reg = DOTG_DOEPCTL(ep_no); 37199cfd0731SHans Petter Selasky sc->sc_active_rx_ep |= (1U << ep_no); 3720dd03e19cSHans Petter Selasky } 3721dd03e19cSHans Petter Selasky 3722dd03e19cSHans Petter Selasky /* round up and mask away the multiplier count */ 3723dd03e19cSHans Petter Selasky mps = (mps + 3) & 0x7FC; 3724dd03e19cSHans Petter Selasky 3725dd03e19cSHans Petter Selasky if (ep_type == UE_BULK) { 3726710764f7SHans Petter Selasky temp = DIEPCTL_EPTYPE_SET( 3727710764f7SHans Petter Selasky DIEPCTL_EPTYPE_BULK) | 3728710764f7SHans Petter Selasky DIEPCTL_USBACTEP; 3729dd03e19cSHans Petter Selasky } else if (ep_type == UE_INTERRUPT) { 3730710764f7SHans Petter Selasky temp = DIEPCTL_EPTYPE_SET( 3731710764f7SHans Petter Selasky DIEPCTL_EPTYPE_INTERRUPT) | 3732710764f7SHans Petter Selasky DIEPCTL_USBACTEP; 3733dd03e19cSHans Petter Selasky } else { 3734710764f7SHans Petter Selasky temp = DIEPCTL_EPTYPE_SET( 3735710764f7SHans Petter Selasky DIEPCTL_EPTYPE_ISOC) | 3736710764f7SHans Petter Selasky DIEPCTL_USBACTEP; 3737dd03e19cSHans Petter Selasky } 3738dd03e19cSHans Petter Selasky 3739710764f7SHans Petter Selasky temp |= DIEPCTL_MPS_SET(mps); 3740710764f7SHans Petter Selasky temp |= DIEPCTL_TXFNUM_SET(ep_no); 3741dd03e19cSHans Petter Selasky 3742dd03e19cSHans Petter Selasky if (ep_dir) 3743dd03e19cSHans Petter Selasky sc->sc_in_ctl[ep_no] = temp; 3744dd03e19cSHans Petter Selasky else 3745dd03e19cSHans Petter Selasky sc->sc_out_ctl[ep_no] = temp; 3746dd03e19cSHans Petter Selasky 3747710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_EPDIS); 3748710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, reg, temp | DOEPCTL_SETD0PID); 3749710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, reg, temp | DIEPCTL_SNAK); 3750dd03e19cSHans Petter Selasky 3751dd03e19cSHans Petter Selasky /* we only reset the transmit FIFO */ 3752dd03e19cSHans Petter Selasky if (ep_dir) { 3753b4df5b00SHans Petter Selasky dwc_otg_tx_fifo_reset(sc, 3754710764f7SHans Petter Selasky GRSTCTL_TXFIFO(ep_no) | 3755710764f7SHans Petter Selasky GRSTCTL_TXFFLSH); 3756dd03e19cSHans Petter Selasky 3757dd03e19cSHans Petter Selasky DWC_OTG_WRITE_4(sc, 3758710764f7SHans Petter Selasky DOTG_DIEPTSIZ(ep_no), 0); 3759dd03e19cSHans Petter Selasky } 3760dd03e19cSHans Petter Selasky 3761dd03e19cSHans Petter Selasky /* poll interrupt */ 37622994bac4SHans Petter Selasky dwc_otg_interrupt_poll_locked(sc); 37632994bac4SHans Petter Selasky dwc_otg_interrupt_complete_locked(sc); 3764dd03e19cSHans Petter Selasky } 3765dd03e19cSHans Petter Selasky 3766dd03e19cSHans Petter Selasky static void 3767dd03e19cSHans Petter Selasky dwc_otg_clear_stall(struct usb_device *udev, struct usb_endpoint *ep) 3768dd03e19cSHans Petter Selasky { 3769dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc; 3770dd03e19cSHans Petter Selasky struct usb_endpoint_descriptor *ed; 3771dd03e19cSHans Petter Selasky 3772dd03e19cSHans Petter Selasky DPRINTFN(5, "endpoint=%p\n", ep); 3773dd03e19cSHans Petter Selasky 3774dd03e19cSHans Petter Selasky USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); 3775dd03e19cSHans Petter Selasky 3776dd03e19cSHans Petter Selasky /* check mode */ 3777dd03e19cSHans Petter Selasky if (udev->flags.usb_mode != USB_MODE_DEVICE) { 3778dd03e19cSHans Petter Selasky /* not supported */ 3779dd03e19cSHans Petter Selasky return; 3780dd03e19cSHans Petter Selasky } 3781dd03e19cSHans Petter Selasky /* get softc */ 3782dd03e19cSHans Petter Selasky sc = DWC_OTG_BUS2SC(udev->bus); 3783dd03e19cSHans Petter Selasky 37842994bac4SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 37852994bac4SHans Petter Selasky 3786dd03e19cSHans Petter Selasky /* get endpoint descriptor */ 3787dd03e19cSHans Petter Selasky ed = ep->edesc; 3788dd03e19cSHans Petter Selasky 3789dd03e19cSHans Petter Selasky /* reset endpoint */ 37902994bac4SHans Petter Selasky dwc_otg_clear_stall_sub_locked(sc, 3791dd03e19cSHans Petter Selasky UGETW(ed->wMaxPacketSize), 3792dd03e19cSHans Petter Selasky (ed->bEndpointAddress & UE_ADDR), 3793dd03e19cSHans Petter Selasky (ed->bmAttributes & UE_XFERTYPE), 3794dd03e19cSHans Petter Selasky (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); 37952994bac4SHans Petter Selasky 37962994bac4SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 3797dd03e19cSHans Petter Selasky } 3798dd03e19cSHans Petter Selasky 3799dd03e19cSHans Petter Selasky static void 3800dd03e19cSHans Petter Selasky dwc_otg_device_state_change(struct usb_device *udev) 3801dd03e19cSHans Petter Selasky { 3802dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc; 3803dd03e19cSHans Petter Selasky uint8_t x; 3804dd03e19cSHans Petter Selasky 38059cfd0731SHans Petter Selasky /* check mode */ 38069cfd0731SHans Petter Selasky if (udev->flags.usb_mode != USB_MODE_DEVICE) { 38079cfd0731SHans Petter Selasky /* not supported */ 38089cfd0731SHans Petter Selasky return; 38099cfd0731SHans Petter Selasky } 38109cfd0731SHans Petter Selasky 3811dd03e19cSHans Petter Selasky /* get softc */ 3812dd03e19cSHans Petter Selasky sc = DWC_OTG_BUS2SC(udev->bus); 3813dd03e19cSHans Petter Selasky 3814dd03e19cSHans Petter Selasky /* deactivate all other endpoint but the control endpoint */ 3815dd03e19cSHans Petter Selasky if (udev->state == USB_STATE_CONFIGURED || 3816dd03e19cSHans Petter Selasky udev->state == USB_STATE_ADDRESSED) { 3817dd03e19cSHans Petter Selasky USB_BUS_LOCK(&sc->sc_bus); 3818dd03e19cSHans Petter Selasky 3819dd03e19cSHans Petter Selasky for (x = 1; x != sc->sc_dev_ep_max; x++) { 3820dd03e19cSHans Petter Selasky if (x < sc->sc_dev_in_ep_max) { 3821710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(x), 3822710764f7SHans Petter Selasky DIEPCTL_EPDIS); 3823710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(x), 0); 3824dd03e19cSHans Petter Selasky } 3825dd03e19cSHans Petter Selasky 3826710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(x), 3827710764f7SHans Petter Selasky DOEPCTL_EPDIS); 3828710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(x), 0); 3829dd03e19cSHans Petter Selasky } 3830dd03e19cSHans Petter Selasky USB_BUS_UNLOCK(&sc->sc_bus); 3831dd03e19cSHans Petter Selasky } 3832dd03e19cSHans Petter Selasky } 3833dd03e19cSHans Petter Selasky 3834dd03e19cSHans Petter Selasky int 3835dd03e19cSHans Petter Selasky dwc_otg_init(struct dwc_otg_softc *sc) 3836dd03e19cSHans Petter Selasky { 3837dd03e19cSHans Petter Selasky uint32_t temp; 3838518da7acSAndrew Turner int err; 3839dd03e19cSHans Petter Selasky 3840dd03e19cSHans Petter Selasky DPRINTF("start\n"); 3841dd03e19cSHans Petter Selasky 3842518da7acSAndrew Turner sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 3843518da7acSAndrew Turner sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 3844518da7acSAndrew Turner sc->sc_io_size = rman_get_size(sc->sc_io_res); 3845518da7acSAndrew Turner 3846dd03e19cSHans Petter Selasky /* set up the bus structure */ 3847518da7acSAndrew Turner sc->sc_bus.devices = sc->sc_devices; 3848518da7acSAndrew Turner sc->sc_bus.devices_max = DWC_OTG_MAX_DEVICES; 3849518da7acSAndrew Turner sc->sc_bus.dma_bits = 32; 3850dd03e19cSHans Petter Selasky sc->sc_bus.usbrev = USB_REV_2_0; 3851dd03e19cSHans Petter Selasky sc->sc_bus.methods = &dwc_otg_bus_methods; 3852dd03e19cSHans Petter Selasky 3853518da7acSAndrew Turner /* get all DMA memory */ 3854518da7acSAndrew Turner if (usb_bus_mem_alloc_all(&sc->sc_bus, 3855518da7acSAndrew Turner USB_GET_DMA_TAG(sc->sc_bus.parent), NULL)) { 3856518da7acSAndrew Turner return (ENOMEM); 3857518da7acSAndrew Turner } 3858518da7acSAndrew Turner 3859*5b56413dSWarner Losh sc->sc_bus.bdev = device_add_child(sc->sc_bus.parent, "usbus", DEVICE_UNIT_ANY); 3860518da7acSAndrew Turner if (sc->sc_bus.bdev == NULL) 3861518da7acSAndrew Turner return (ENXIO); 3862518da7acSAndrew Turner 3863518da7acSAndrew Turner device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 3864518da7acSAndrew Turner 3865518da7acSAndrew Turner err = bus_setup_intr(sc->sc_bus.parent, sc->sc_irq_res, 3866518da7acSAndrew Turner INTR_TYPE_TTY | INTR_MPSAFE, &dwc_otg_filter_interrupt, 3867518da7acSAndrew Turner &dwc_otg_interrupt, sc, &sc->sc_intr_hdl); 3868518da7acSAndrew Turner if (err) { 3869518da7acSAndrew Turner sc->sc_intr_hdl = NULL; 3870518da7acSAndrew Turner return (ENXIO); 3871518da7acSAndrew Turner } 3872518da7acSAndrew Turner 38733eabad25SHans Petter Selasky usb_callout_init_mtx(&sc->sc_timer, 38743eabad25SHans Petter Selasky &sc->sc_bus.bus_mtx, 0); 38753eabad25SHans Petter Selasky 3876dd03e19cSHans Petter Selasky USB_BUS_LOCK(&sc->sc_bus); 3877dd03e19cSHans Petter Selasky 3878dd03e19cSHans Petter Selasky /* turn on clocks */ 3879dd03e19cSHans Petter Selasky dwc_otg_clocks_on(sc); 3880dd03e19cSHans Petter Selasky 3881710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GSNPSID); 3882dd03e19cSHans Petter Selasky DPRINTF("Version = 0x%08x\n", temp); 3883dd03e19cSHans Petter Selasky 3884dd03e19cSHans Petter Selasky /* disconnect */ 3885710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, 3886710764f7SHans Petter Selasky DCTL_SFTDISCON); 3887dd03e19cSHans Petter Selasky 3888dd03e19cSHans Petter Selasky /* wait for host to detect disconnect */ 3889dd03e19cSHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 32); 3890dd03e19cSHans Petter Selasky 3891710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, 3892710764f7SHans Petter Selasky GRSTCTL_CSFTRST); 3893dd03e19cSHans Petter Selasky 3894dd03e19cSHans Petter Selasky /* wait a little bit for block to reset */ 3895dd03e19cSHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 128); 3896dd03e19cSHans Petter Selasky 38979cfd0731SHans Petter Selasky switch (sc->sc_mode) { 38989cfd0731SHans Petter Selasky case DWC_MODE_DEVICE: 38999cfd0731SHans Petter Selasky temp = GUSBCFG_FORCEDEVMODE; 39009cfd0731SHans Petter Selasky break; 39019cfd0731SHans Petter Selasky case DWC_MODE_HOST: 39029cfd0731SHans Petter Selasky temp = GUSBCFG_FORCEHOSTMODE; 39039cfd0731SHans Petter Selasky break; 39049cfd0731SHans Petter Selasky default: 39059cfd0731SHans Petter Selasky temp = 0; 39069cfd0731SHans Petter Selasky break; 39079cfd0731SHans Petter Selasky } 39089cfd0731SHans Petter Selasky 3909cec8009cSRuslan Bukin if (sc->sc_phy_type == 0) 3910cec8009cSRuslan Bukin sc->sc_phy_type = dwc_otg_phy_type + 1; 3911cec8009cSRuslan Bukin if (sc->sc_phy_bits == 0) 3912cec8009cSRuslan Bukin sc->sc_phy_bits = 16; 3913cec8009cSRuslan Bukin 3914cec8009cSRuslan Bukin /* select HSIC, ULPI, UTMI+ or internal PHY mode */ 3915cec8009cSRuslan Bukin switch (sc->sc_phy_type) { 3916f5757453SHans Petter Selasky case DWC_OTG_PHY_HSIC: 3917710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, 3918710764f7SHans Petter Selasky GUSBCFG_PHYIF | 39199cfd0731SHans Petter Selasky GUSBCFG_TRD_TIM_SET(5) | temp); 3920710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 3921dd03e19cSHans Petter Selasky 0x000000EC); 3922dd03e19cSHans Petter Selasky 3923710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); 3924710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, 3925710764f7SHans Petter Selasky temp & ~GLPMCFG_HSIC_CONN); 3926710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, 3927710764f7SHans Petter Selasky temp | GLPMCFG_HSIC_CONN); 3928f5757453SHans Petter Selasky break; 3929f5757453SHans Petter Selasky case DWC_OTG_PHY_ULPI: 3930710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, 3931710764f7SHans Petter Selasky GUSBCFG_ULPI_UTMI_SEL | 39329cfd0731SHans Petter Selasky GUSBCFG_TRD_TIM_SET(5) | temp); 3933710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0); 3934dd03e19cSHans Petter Selasky 3935710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); 3936710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, 3937710764f7SHans Petter Selasky temp & ~GLPMCFG_HSIC_CONN); 3938f5757453SHans Petter Selasky break; 3939cec8009cSRuslan Bukin case DWC_OTG_PHY_UTMI: 3940cec8009cSRuslan Bukin DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, 3941cec8009cSRuslan Bukin (sc->sc_phy_bits == 16 ? GUSBCFG_PHYIF : 0) | 3942cec8009cSRuslan Bukin GUSBCFG_TRD_TIM_SET(5) | temp); 3943cec8009cSRuslan Bukin DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0); 3944cec8009cSRuslan Bukin 3945cec8009cSRuslan Bukin temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); 3946cec8009cSRuslan Bukin DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, 3947cec8009cSRuslan Bukin temp & ~GLPMCFG_HSIC_CONN); 3948cec8009cSRuslan Bukin break; 3949f5757453SHans Petter Selasky case DWC_OTG_PHY_INTERNAL: 3950f5757453SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG, 3951f5757453SHans Petter Selasky GUSBCFG_PHYSEL | 3952f5757453SHans Petter Selasky GUSBCFG_TRD_TIM_SET(5) | temp); 3953f5757453SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0); 3954f5757453SHans Petter Selasky 3955f5757453SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG); 3956f5757453SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GLPMCFG, 3957f5757453SHans Petter Selasky temp & ~GLPMCFG_HSIC_CONN); 3958f5757453SHans Petter Selasky 3959f5757453SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GGPIO); 3960f5757453SHans Petter Selasky temp &= ~(DOTG_GGPIO_NOVBUSSENS | DOTG_GGPIO_I2CPADEN); 3961f5757453SHans Petter Selasky temp |= (DOTG_GGPIO_VBUSASEN | DOTG_GGPIO_VBUSBSEN | 3962f5757453SHans Petter Selasky DOTG_GGPIO_PWRDWN); 3963f5757453SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GGPIO, temp); 3964f5757453SHans Petter Selasky break; 3965f5757453SHans Petter Selasky default: 3966f5757453SHans Petter Selasky break; 3967dd03e19cSHans Petter Selasky } 3968dd03e19cSHans Petter Selasky 3969dd03e19cSHans Petter Selasky /* clear global nak */ 3970710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, 3971710764f7SHans Petter Selasky DCTL_CGOUTNAK | 3972710764f7SHans Petter Selasky DCTL_CGNPINNAK); 3973dd03e19cSHans Petter Selasky 39749cfd0731SHans Petter Selasky /* disable USB port */ 39759cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0xFFFFFFFF); 39769cfd0731SHans Petter Selasky 39779cfd0731SHans Petter Selasky /* wait 10ms */ 39789cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); 39799cfd0731SHans Petter Selasky 3980dd03e19cSHans Petter Selasky /* enable USB port */ 3981710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0); 3982dd03e19cSHans Petter Selasky 39839cfd0731SHans Petter Selasky /* wait 10ms */ 39849cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); 39859cfd0731SHans Petter Selasky 3986710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG3); 3987dd03e19cSHans Petter Selasky 3988710764f7SHans Petter Selasky sc->sc_fifo_size = 4 * GHWCFG3_DFIFODEPTH_GET(temp); 3989dd03e19cSHans Petter Selasky 3990710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2); 3991dd03e19cSHans Petter Selasky 3992710764f7SHans Petter Selasky sc->sc_dev_ep_max = GHWCFG2_NUMDEVEPS_GET(temp); 3993dd03e19cSHans Petter Selasky 39949cfd0731SHans Petter Selasky if (sc->sc_dev_ep_max > DWC_OTG_MAX_ENDPOINTS) 39959cfd0731SHans Petter Selasky sc->sc_dev_ep_max = DWC_OTG_MAX_ENDPOINTS; 39969cfd0731SHans Petter Selasky 39979cfd0731SHans Petter Selasky sc->sc_host_ch_max = GHWCFG2_NUMHSTCHNL_GET(temp); 39989cfd0731SHans Petter Selasky 39999cfd0731SHans Petter Selasky if (sc->sc_host_ch_max > DWC_OTG_MAX_CHANNELS) 40009cfd0731SHans Petter Selasky sc->sc_host_ch_max = DWC_OTG_MAX_CHANNELS; 40019cfd0731SHans Petter Selasky 4002710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG4); 4003dd03e19cSHans Petter Selasky 4004710764f7SHans Petter Selasky sc->sc_dev_in_ep_max = GHWCFG4_NUM_IN_EP_GET(temp); 4005dd03e19cSHans Petter Selasky 40069cfd0731SHans Petter Selasky DPRINTF("Total FIFO size = %d bytes, Device EPs = %d/%d Host CHs = %d\n", 40079cfd0731SHans Petter Selasky sc->sc_fifo_size, sc->sc_dev_ep_max, sc->sc_dev_in_ep_max, 40089cfd0731SHans Petter Selasky sc->sc_host_ch_max); 4009dd03e19cSHans Petter Selasky 4010dd03e19cSHans Petter Selasky /* setup FIFO */ 4011db4300daSHans Petter Selasky if (dwc_otg_init_fifo(sc, sc->sc_mode)) { 4012db4300daSHans Petter Selasky USB_BUS_UNLOCK(&sc->sc_bus); 4013dd03e19cSHans Petter Selasky return (EINVAL); 4014db4300daSHans Petter Selasky } 4015dd03e19cSHans Petter Selasky 4016dd03e19cSHans Petter Selasky /* enable interrupts */ 4017e4d17320SHans Petter Selasky sc->sc_irq_mask |= DWC_OTG_MSK_GINT_THREAD_IRQ; 4018710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); 4019dd03e19cSHans Petter Selasky 40209cfd0731SHans Petter Selasky if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_DEVICE) { 4021dd03e19cSHans Petter Selasky /* enable all endpoint interrupts */ 4022710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2); 4023710764f7SHans Petter Selasky if (temp & GHWCFG2_MPI) { 40240b8de869SHans Petter Selasky uint8_t x; 40250b8de869SHans Petter Selasky 40264e36e452SHans Petter Selasky DPRINTF("Disable Multi Process Interrupts\n"); 40270b8de869SHans Petter Selasky 40280b8de869SHans Petter Selasky for (x = 0; x != sc->sc_dev_in_ep_max; x++) { 40294e36e452SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPEACHINTMSK(x), 0); 4030710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPEACHINTMSK(x), 0); 40310b8de869SHans Petter Selasky } 40324e36e452SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DEACHINTMSK, 0); 40334e36e452SHans Petter Selasky } 4034710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DIEPMSK, 4035710764f7SHans Petter Selasky DIEPMSK_XFERCOMPLMSK); 4036710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DOEPMSK, 0); 4037710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF); 40380b8de869SHans Petter Selasky } 4039dd03e19cSHans Petter Selasky 40409cfd0731SHans Petter Selasky if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_HOST) { 40419cfd0731SHans Petter Selasky /* setup clocks */ 40429cfd0731SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_HCFG); 40439cfd0731SHans Petter Selasky temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK); 40449cfd0731SHans Petter Selasky temp |= (1 << HCFG_FSLSPCLKSEL_SHIFT); 40459cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp); 40469cfd0731SHans Petter Selasky } 40479cfd0731SHans Petter Selasky 40489cfd0731SHans Petter Selasky /* only enable global IRQ */ 4049710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, 4050710764f7SHans Petter Selasky GAHBCFG_GLBLINTRMSK); 4051dd03e19cSHans Petter Selasky 4052dd03e19cSHans Petter Selasky /* turn off clocks */ 4053dd03e19cSHans Petter Selasky dwc_otg_clocks_off(sc); 4054dd03e19cSHans Petter Selasky 4055dd03e19cSHans Petter Selasky /* read initial VBUS state */ 4056dd03e19cSHans Petter Selasky 4057710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_GOTGCTL); 4058dd03e19cSHans Petter Selasky 4059dd03e19cSHans Petter Selasky DPRINTFN(5, "GOTCTL=0x%08x\n", temp); 4060dd03e19cSHans Petter Selasky 4061dd03e19cSHans Petter Selasky dwc_otg_vbus_interrupt(sc, 40629cfd0731SHans Petter Selasky (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0); 4063dd03e19cSHans Petter Selasky 4064dd03e19cSHans Petter Selasky USB_BUS_UNLOCK(&sc->sc_bus); 4065dd03e19cSHans Petter Selasky 4066dd03e19cSHans Petter Selasky /* catch any lost interrupts */ 4067dd03e19cSHans Petter Selasky 4068dd03e19cSHans Petter Selasky dwc_otg_do_poll(&sc->sc_bus); 4069dd03e19cSHans Petter Selasky 4070dd03e19cSHans Petter Selasky return (0); /* success */ 4071dd03e19cSHans Petter Selasky } 4072dd03e19cSHans Petter Selasky 4073dd03e19cSHans Petter Selasky void 4074dd03e19cSHans Petter Selasky dwc_otg_uninit(struct dwc_otg_softc *sc) 4075dd03e19cSHans Petter Selasky { 4076dd03e19cSHans Petter Selasky USB_BUS_LOCK(&sc->sc_bus); 4077dd03e19cSHans Petter Selasky 40783eabad25SHans Petter Selasky /* stop host timer */ 40793eabad25SHans Petter Selasky dwc_otg_timer_stop(sc); 40803eabad25SHans Petter Selasky 4081dd03e19cSHans Petter Selasky /* set disconnect */ 4082710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_DCTL, 4083710764f7SHans Petter Selasky DCTL_SFTDISCON); 4084dd03e19cSHans Petter Selasky 4085dd03e19cSHans Petter Selasky /* turn off global IRQ */ 4086710764f7SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, 0); 4087dd03e19cSHans Petter Selasky 40889cfd0731SHans Petter Selasky sc->sc_flags.port_enabled = 0; 4089dd03e19cSHans Petter Selasky sc->sc_flags.port_powered = 0; 4090dd03e19cSHans Petter Selasky sc->sc_flags.status_vbus = 0; 4091dd03e19cSHans Petter Selasky sc->sc_flags.status_bus_reset = 0; 4092dd03e19cSHans Petter Selasky sc->sc_flags.status_suspend = 0; 4093dd03e19cSHans Petter Selasky sc->sc_flags.change_suspend = 0; 4094dd03e19cSHans Petter Selasky sc->sc_flags.change_connect = 1; 4095dd03e19cSHans Petter Selasky 4096dd03e19cSHans Petter Selasky dwc_otg_pull_down(sc); 4097dd03e19cSHans Petter Selasky dwc_otg_clocks_off(sc); 4098dd03e19cSHans Petter Selasky 4099dd03e19cSHans Petter Selasky USB_BUS_UNLOCK(&sc->sc_bus); 41003eabad25SHans Petter Selasky 41013eabad25SHans Petter Selasky usb_callout_drain(&sc->sc_timer); 4102dd03e19cSHans Petter Selasky } 4103dd03e19cSHans Petter Selasky 4104dd03e19cSHans Petter Selasky static void 4105dd03e19cSHans Petter Selasky dwc_otg_suspend(struct dwc_otg_softc *sc) 4106dd03e19cSHans Petter Selasky { 4107dd03e19cSHans Petter Selasky return; 4108dd03e19cSHans Petter Selasky } 4109dd03e19cSHans Petter Selasky 4110dd03e19cSHans Petter Selasky static void 4111dd03e19cSHans Petter Selasky dwc_otg_resume(struct dwc_otg_softc *sc) 4112dd03e19cSHans Petter Selasky { 4113dd03e19cSHans Petter Selasky return; 4114dd03e19cSHans Petter Selasky } 4115dd03e19cSHans Petter Selasky 4116dd03e19cSHans Petter Selasky static void 4117dd03e19cSHans Petter Selasky dwc_otg_do_poll(struct usb_bus *bus) 4118dd03e19cSHans Petter Selasky { 4119dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus); 4120dd03e19cSHans Petter Selasky 4121dd03e19cSHans Petter Selasky USB_BUS_LOCK(&sc->sc_bus); 41222fe7ad87SHans Petter Selasky USB_BUS_SPIN_LOCK(&sc->sc_bus); 41232994bac4SHans Petter Selasky dwc_otg_interrupt_poll_locked(sc); 41242994bac4SHans Petter Selasky dwc_otg_interrupt_complete_locked(sc); 41252fe7ad87SHans Petter Selasky USB_BUS_SPIN_UNLOCK(&sc->sc_bus); 4126dd03e19cSHans Petter Selasky USB_BUS_UNLOCK(&sc->sc_bus); 4127dd03e19cSHans Petter Selasky } 4128dd03e19cSHans Petter Selasky 4129dd03e19cSHans Petter Selasky /*------------------------------------------------------------------------* 41309cfd0731SHans Petter Selasky * DWC OTG bulk support 41319cfd0731SHans Petter Selasky * DWC OTG control support 41329cfd0731SHans Petter Selasky * DWC OTG interrupt support 4133dd03e19cSHans Petter Selasky *------------------------------------------------------------------------*/ 4134dd03e19cSHans Petter Selasky static void 4135dd03e19cSHans Petter Selasky dwc_otg_device_non_isoc_open(struct usb_xfer *xfer) 4136dd03e19cSHans Petter Selasky { 4137dd03e19cSHans Petter Selasky } 4138dd03e19cSHans Petter Selasky 4139dd03e19cSHans Petter Selasky static void 4140dd03e19cSHans Petter Selasky dwc_otg_device_non_isoc_close(struct usb_xfer *xfer) 4141dd03e19cSHans Petter Selasky { 4142dd03e19cSHans Petter Selasky dwc_otg_device_done(xfer, USB_ERR_CANCELLED); 4143dd03e19cSHans Petter Selasky } 4144dd03e19cSHans Petter Selasky 4145dd03e19cSHans Petter Selasky static void 4146dd03e19cSHans Petter Selasky dwc_otg_device_non_isoc_enter(struct usb_xfer *xfer) 4147dd03e19cSHans Petter Selasky { 4148dd03e19cSHans Petter Selasky } 4149dd03e19cSHans Petter Selasky 4150dd03e19cSHans Petter Selasky static void 4151dd03e19cSHans Petter Selasky dwc_otg_device_non_isoc_start(struct usb_xfer *xfer) 4152dd03e19cSHans Petter Selasky { 4153dd03e19cSHans Petter Selasky /* setup TDs */ 4154dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain(xfer); 4155dd03e19cSHans Petter Selasky dwc_otg_start_standard_chain(xfer); 4156dd03e19cSHans Petter Selasky } 4157dd03e19cSHans Petter Selasky 4158e892b3feSHans Petter Selasky static const struct usb_pipe_methods dwc_otg_device_non_isoc_methods = 4159dd03e19cSHans Petter Selasky { 4160dd03e19cSHans Petter Selasky .open = dwc_otg_device_non_isoc_open, 4161dd03e19cSHans Petter Selasky .close = dwc_otg_device_non_isoc_close, 4162dd03e19cSHans Petter Selasky .enter = dwc_otg_device_non_isoc_enter, 4163dd03e19cSHans Petter Selasky .start = dwc_otg_device_non_isoc_start, 4164dd03e19cSHans Petter Selasky }; 4165dd03e19cSHans Petter Selasky 4166dd03e19cSHans Petter Selasky /*------------------------------------------------------------------------* 41679cfd0731SHans Petter Selasky * DWC OTG full speed isochronous support 4168dd03e19cSHans Petter Selasky *------------------------------------------------------------------------*/ 4169dd03e19cSHans Petter Selasky static void 41709cfd0731SHans Petter Selasky dwc_otg_device_isoc_open(struct usb_xfer *xfer) 4171dd03e19cSHans Petter Selasky { 4172dd03e19cSHans Petter Selasky } 4173dd03e19cSHans Petter Selasky 4174dd03e19cSHans Petter Selasky static void 41759cfd0731SHans Petter Selasky dwc_otg_device_isoc_close(struct usb_xfer *xfer) 4176dd03e19cSHans Petter Selasky { 4177dd03e19cSHans Petter Selasky dwc_otg_device_done(xfer, USB_ERR_CANCELLED); 4178dd03e19cSHans Petter Selasky } 4179dd03e19cSHans Petter Selasky 4180dd03e19cSHans Petter Selasky static void 41819cfd0731SHans Petter Selasky dwc_otg_device_isoc_enter(struct usb_xfer *xfer) 4182dd03e19cSHans Petter Selasky { 4183bc990482SHans Petter Selasky } 4184bc990482SHans Petter Selasky 4185bc990482SHans Petter Selasky static void 4186bc990482SHans Petter Selasky dwc_otg_device_isoc_start(struct usb_xfer *xfer) 4187bc990482SHans Petter Selasky { 4188dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); 4189dd03e19cSHans Petter Selasky uint32_t temp; 4190bc990482SHans Petter Selasky uint32_t framenum; 4191dd03e19cSHans Petter Selasky 4192dd03e19cSHans Petter Selasky DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", 4193dd03e19cSHans Petter Selasky xfer, xfer->endpoint->isoc_next, xfer->nframes); 4194dd03e19cSHans Petter Selasky 41959cfd0731SHans Petter Selasky if (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST) { 41963eabad25SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_HFNUM); 41979cfd0731SHans Petter Selasky 41989cfd0731SHans Petter Selasky /* get the current frame index */ 4199bc990482SHans Petter Selasky framenum = (temp & HFNUM_FRNUM_MASK); 42009cfd0731SHans Petter Selasky } else { 4201710764f7SHans Petter Selasky temp = DWC_OTG_READ_4(sc, DOTG_DSTS); 4202dd03e19cSHans Petter Selasky 4203dd03e19cSHans Petter Selasky /* get the current frame index */ 4204bc990482SHans Petter Selasky framenum = DSTS_SOFFN_GET(temp); 42059cfd0731SHans Petter Selasky } 4206dd03e19cSHans Petter Selasky 4207076daedaSHans Petter Selasky /* 4208076daedaSHans Petter Selasky * Check if port is doing 8000 or 1000 frames per second: 4209076daedaSHans Petter Selasky */ 4210076daedaSHans Petter Selasky if (sc->sc_flags.status_high_speed) 4211bc990482SHans Petter Selasky framenum /= 8; 4212dd03e19cSHans Petter Selasky 42138fc2a3c4SHans Petter Selasky if (usbd_xfer_get_isochronous_start_frame( 42148fc2a3c4SHans Petter Selasky xfer, framenum, 0, 1, DWC_OTG_FRAME_MASK, NULL)) 4215dd03e19cSHans Petter Selasky DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); 4216dd03e19cSHans Petter Selasky 4217dd03e19cSHans Petter Selasky /* setup TDs */ 4218dd03e19cSHans Petter Selasky dwc_otg_setup_standard_chain(xfer); 42199cfd0731SHans Petter Selasky 4220dd03e19cSHans Petter Selasky /* start TD chain */ 4221dd03e19cSHans Petter Selasky dwc_otg_start_standard_chain(xfer); 4222dd03e19cSHans Petter Selasky } 4223dd03e19cSHans Petter Selasky 4224e892b3feSHans Petter Selasky static const struct usb_pipe_methods dwc_otg_device_isoc_methods = 4225dd03e19cSHans Petter Selasky { 42269cfd0731SHans Petter Selasky .open = dwc_otg_device_isoc_open, 42279cfd0731SHans Petter Selasky .close = dwc_otg_device_isoc_close, 42289cfd0731SHans Petter Selasky .enter = dwc_otg_device_isoc_enter, 42299cfd0731SHans Petter Selasky .start = dwc_otg_device_isoc_start, 4230dd03e19cSHans Petter Selasky }; 4231dd03e19cSHans Petter Selasky 4232dd03e19cSHans Petter Selasky /*------------------------------------------------------------------------* 42339cfd0731SHans Petter Selasky * DWC OTG root control support 4234dd03e19cSHans Petter Selasky *------------------------------------------------------------------------* 4235dd03e19cSHans Petter Selasky * Simulate a hardware HUB by handling all the necessary requests. 4236dd03e19cSHans Petter Selasky *------------------------------------------------------------------------*/ 4237dd03e19cSHans Petter Selasky 4238dd03e19cSHans Petter Selasky static const struct usb_device_descriptor dwc_otg_devd = { 4239dd03e19cSHans Petter Selasky .bLength = sizeof(struct usb_device_descriptor), 4240dd03e19cSHans Petter Selasky .bDescriptorType = UDESC_DEVICE, 4241dd03e19cSHans Petter Selasky .bcdUSB = {0x00, 0x02}, 4242dd03e19cSHans Petter Selasky .bDeviceClass = UDCLASS_HUB, 4243dd03e19cSHans Petter Selasky .bDeviceSubClass = UDSUBCLASS_HUB, 4244dd03e19cSHans Petter Selasky .bDeviceProtocol = UDPROTO_HSHUBSTT, 4245dd03e19cSHans Petter Selasky .bMaxPacketSize = 64, 4246dd03e19cSHans Petter Selasky .bcdDevice = {0x00, 0x01}, 4247dd03e19cSHans Petter Selasky .iManufacturer = 1, 4248dd03e19cSHans Petter Selasky .iProduct = 2, 4249dd03e19cSHans Petter Selasky .bNumConfigurations = 1, 4250dd03e19cSHans Petter Selasky }; 4251dd03e19cSHans Petter Selasky 4252dd03e19cSHans Petter Selasky static const struct dwc_otg_config_desc dwc_otg_confd = { 4253dd03e19cSHans Petter Selasky .confd = { 4254dd03e19cSHans Petter Selasky .bLength = sizeof(struct usb_config_descriptor), 4255dd03e19cSHans Petter Selasky .bDescriptorType = UDESC_CONFIG, 4256dd03e19cSHans Petter Selasky .wTotalLength[0] = sizeof(dwc_otg_confd), 4257dd03e19cSHans Petter Selasky .bNumInterface = 1, 4258dd03e19cSHans Petter Selasky .bConfigurationValue = 1, 4259dd03e19cSHans Petter Selasky .iConfiguration = 0, 4260dd03e19cSHans Petter Selasky .bmAttributes = UC_SELF_POWERED, 4261dd03e19cSHans Petter Selasky .bMaxPower = 0, 4262dd03e19cSHans Petter Selasky }, 4263dd03e19cSHans Petter Selasky .ifcd = { 4264dd03e19cSHans Petter Selasky .bLength = sizeof(struct usb_interface_descriptor), 4265dd03e19cSHans Petter Selasky .bDescriptorType = UDESC_INTERFACE, 4266dd03e19cSHans Petter Selasky .bNumEndpoints = 1, 4267dd03e19cSHans Petter Selasky .bInterfaceClass = UICLASS_HUB, 4268dd03e19cSHans Petter Selasky .bInterfaceSubClass = UISUBCLASS_HUB, 4269dd03e19cSHans Petter Selasky .bInterfaceProtocol = 0, 4270dd03e19cSHans Petter Selasky }, 4271dd03e19cSHans Petter Selasky .endpd = { 4272dd03e19cSHans Petter Selasky .bLength = sizeof(struct usb_endpoint_descriptor), 4273dd03e19cSHans Petter Selasky .bDescriptorType = UDESC_ENDPOINT, 4274dd03e19cSHans Petter Selasky .bEndpointAddress = (UE_DIR_IN | DWC_OTG_INTR_ENDPT), 4275dd03e19cSHans Petter Selasky .bmAttributes = UE_INTERRUPT, 4276dd03e19cSHans Petter Selasky .wMaxPacketSize[0] = 8, 4277dd03e19cSHans Petter Selasky .bInterval = 255, 4278dd03e19cSHans Petter Selasky }, 4279dd03e19cSHans Petter Selasky }; 42806d917491SHans Petter Selasky #define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) } 42816d917491SHans Petter Selasky 4282dd03e19cSHans Petter Selasky static const struct usb_hub_descriptor_min dwc_otg_hubd = { 4283dd03e19cSHans Petter Selasky .bDescLength = sizeof(dwc_otg_hubd), 4284dd03e19cSHans Petter Selasky .bDescriptorType = UDESC_HUB, 4285dd03e19cSHans Petter Selasky .bNbrPorts = 1, 42866d917491SHans Petter Selasky HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)), 4287dd03e19cSHans Petter Selasky .bPwrOn2PwrGood = 50, 4288dd03e19cSHans Petter Selasky .bHubContrCurrent = 0, 4289dd03e19cSHans Petter Selasky .DeviceRemovable = {0}, /* port is removable */ 4290dd03e19cSHans Petter Selasky }; 4291dd03e19cSHans Petter Selasky 4292dd03e19cSHans Petter Selasky #define STRING_VENDOR \ 4293b51875c9SHans Petter Selasky "D\0W\0C\0O\0T\0G" 4294dd03e19cSHans Petter Selasky 4295dd03e19cSHans Petter Selasky #define STRING_PRODUCT \ 4296b51875c9SHans Petter Selasky "O\0T\0G\0 \0R\0o\0o\0t\0 \0H\0U\0B" 4297dd03e19cSHans Petter Selasky 4298dd03e19cSHans Petter Selasky USB_MAKE_STRING_DESC(STRING_VENDOR, dwc_otg_vendor); 4299dd03e19cSHans Petter Selasky USB_MAKE_STRING_DESC(STRING_PRODUCT, dwc_otg_product); 4300dd03e19cSHans Petter Selasky 4301dd03e19cSHans Petter Selasky static usb_error_t 4302dd03e19cSHans Petter Selasky dwc_otg_roothub_exec(struct usb_device *udev, 4303dd03e19cSHans Petter Selasky struct usb_device_request *req, const void **pptr, uint16_t *plength) 4304dd03e19cSHans Petter Selasky { 4305dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus); 4306dd03e19cSHans Petter Selasky const void *ptr; 4307dd03e19cSHans Petter Selasky uint16_t len; 4308dd03e19cSHans Petter Selasky uint16_t value; 4309dd03e19cSHans Petter Selasky uint16_t index; 4310dd03e19cSHans Petter Selasky usb_error_t err; 4311dd03e19cSHans Petter Selasky 4312dd03e19cSHans Petter Selasky USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); 4313dd03e19cSHans Petter Selasky 4314dd03e19cSHans Petter Selasky /* buffer reset */ 4315dd03e19cSHans Petter Selasky ptr = (const void *)&sc->sc_hub_temp; 4316dd03e19cSHans Petter Selasky len = 0; 4317dd03e19cSHans Petter Selasky err = 0; 4318dd03e19cSHans Petter Selasky 4319dd03e19cSHans Petter Selasky value = UGETW(req->wValue); 4320dd03e19cSHans Petter Selasky index = UGETW(req->wIndex); 4321dd03e19cSHans Petter Selasky 4322dd03e19cSHans Petter Selasky /* demultiplex the control request */ 4323dd03e19cSHans Petter Selasky 4324dd03e19cSHans Petter Selasky switch (req->bmRequestType) { 4325dd03e19cSHans Petter Selasky case UT_READ_DEVICE: 4326dd03e19cSHans Petter Selasky switch (req->bRequest) { 4327dd03e19cSHans Petter Selasky case UR_GET_DESCRIPTOR: 4328dd03e19cSHans Petter Selasky goto tr_handle_get_descriptor; 4329dd03e19cSHans Petter Selasky case UR_GET_CONFIG: 4330dd03e19cSHans Petter Selasky goto tr_handle_get_config; 4331dd03e19cSHans Petter Selasky case UR_GET_STATUS: 4332dd03e19cSHans Petter Selasky goto tr_handle_get_status; 4333dd03e19cSHans Petter Selasky default: 4334dd03e19cSHans Petter Selasky goto tr_stalled; 4335dd03e19cSHans Petter Selasky } 4336dd03e19cSHans Petter Selasky break; 4337dd03e19cSHans Petter Selasky 4338dd03e19cSHans Petter Selasky case UT_WRITE_DEVICE: 4339dd03e19cSHans Petter Selasky switch (req->bRequest) { 4340dd03e19cSHans Petter Selasky case UR_SET_ADDRESS: 4341dd03e19cSHans Petter Selasky goto tr_handle_set_address; 4342dd03e19cSHans Petter Selasky case UR_SET_CONFIG: 4343dd03e19cSHans Petter Selasky goto tr_handle_set_config; 4344dd03e19cSHans Petter Selasky case UR_CLEAR_FEATURE: 4345dd03e19cSHans Petter Selasky goto tr_valid; /* nop */ 4346dd03e19cSHans Petter Selasky case UR_SET_DESCRIPTOR: 4347dd03e19cSHans Petter Selasky goto tr_valid; /* nop */ 4348dd03e19cSHans Petter Selasky case UR_SET_FEATURE: 4349dd03e19cSHans Petter Selasky default: 4350dd03e19cSHans Petter Selasky goto tr_stalled; 4351dd03e19cSHans Petter Selasky } 4352dd03e19cSHans Petter Selasky break; 4353dd03e19cSHans Petter Selasky 4354dd03e19cSHans Petter Selasky case UT_WRITE_ENDPOINT: 4355dd03e19cSHans Petter Selasky switch (req->bRequest) { 4356dd03e19cSHans Petter Selasky case UR_CLEAR_FEATURE: 4357dd03e19cSHans Petter Selasky switch (UGETW(req->wValue)) { 4358dd03e19cSHans Petter Selasky case UF_ENDPOINT_HALT: 4359dd03e19cSHans Petter Selasky goto tr_handle_clear_halt; 4360dd03e19cSHans Petter Selasky case UF_DEVICE_REMOTE_WAKEUP: 4361dd03e19cSHans Petter Selasky goto tr_handle_clear_wakeup; 4362dd03e19cSHans Petter Selasky default: 4363dd03e19cSHans Petter Selasky goto tr_stalled; 4364dd03e19cSHans Petter Selasky } 4365dd03e19cSHans Petter Selasky break; 4366dd03e19cSHans Petter Selasky case UR_SET_FEATURE: 4367dd03e19cSHans Petter Selasky switch (UGETW(req->wValue)) { 4368dd03e19cSHans Petter Selasky case UF_ENDPOINT_HALT: 4369dd03e19cSHans Petter Selasky goto tr_handle_set_halt; 4370dd03e19cSHans Petter Selasky case UF_DEVICE_REMOTE_WAKEUP: 4371dd03e19cSHans Petter Selasky goto tr_handle_set_wakeup; 4372dd03e19cSHans Petter Selasky default: 4373dd03e19cSHans Petter Selasky goto tr_stalled; 4374dd03e19cSHans Petter Selasky } 4375dd03e19cSHans Petter Selasky break; 4376dd03e19cSHans Petter Selasky case UR_SYNCH_FRAME: 4377dd03e19cSHans Petter Selasky goto tr_valid; /* nop */ 4378dd03e19cSHans Petter Selasky default: 4379dd03e19cSHans Petter Selasky goto tr_stalled; 4380dd03e19cSHans Petter Selasky } 4381dd03e19cSHans Petter Selasky break; 4382dd03e19cSHans Petter Selasky 4383dd03e19cSHans Petter Selasky case UT_READ_ENDPOINT: 4384dd03e19cSHans Petter Selasky switch (req->bRequest) { 4385dd03e19cSHans Petter Selasky case UR_GET_STATUS: 4386dd03e19cSHans Petter Selasky goto tr_handle_get_ep_status; 4387dd03e19cSHans Petter Selasky default: 4388dd03e19cSHans Petter Selasky goto tr_stalled; 4389dd03e19cSHans Petter Selasky } 4390dd03e19cSHans Petter Selasky break; 4391dd03e19cSHans Petter Selasky 4392dd03e19cSHans Petter Selasky case UT_WRITE_INTERFACE: 4393dd03e19cSHans Petter Selasky switch (req->bRequest) { 4394dd03e19cSHans Petter Selasky case UR_SET_INTERFACE: 4395dd03e19cSHans Petter Selasky goto tr_handle_set_interface; 4396dd03e19cSHans Petter Selasky case UR_CLEAR_FEATURE: 4397dd03e19cSHans Petter Selasky goto tr_valid; /* nop */ 4398dd03e19cSHans Petter Selasky case UR_SET_FEATURE: 4399dd03e19cSHans Petter Selasky default: 4400dd03e19cSHans Petter Selasky goto tr_stalled; 4401dd03e19cSHans Petter Selasky } 4402dd03e19cSHans Petter Selasky break; 4403dd03e19cSHans Petter Selasky 4404dd03e19cSHans Petter Selasky case UT_READ_INTERFACE: 4405dd03e19cSHans Petter Selasky switch (req->bRequest) { 4406dd03e19cSHans Petter Selasky case UR_GET_INTERFACE: 4407dd03e19cSHans Petter Selasky goto tr_handle_get_interface; 4408dd03e19cSHans Petter Selasky case UR_GET_STATUS: 4409dd03e19cSHans Petter Selasky goto tr_handle_get_iface_status; 4410dd03e19cSHans Petter Selasky default: 4411dd03e19cSHans Petter Selasky goto tr_stalled; 4412dd03e19cSHans Petter Selasky } 4413dd03e19cSHans Petter Selasky break; 4414dd03e19cSHans Petter Selasky 4415dd03e19cSHans Petter Selasky case UT_WRITE_CLASS_INTERFACE: 4416dd03e19cSHans Petter Selasky case UT_WRITE_VENDOR_INTERFACE: 4417dd03e19cSHans Petter Selasky /* XXX forward */ 4418dd03e19cSHans Petter Selasky break; 4419dd03e19cSHans Petter Selasky 4420dd03e19cSHans Petter Selasky case UT_READ_CLASS_INTERFACE: 4421dd03e19cSHans Petter Selasky case UT_READ_VENDOR_INTERFACE: 4422dd03e19cSHans Petter Selasky /* XXX forward */ 4423dd03e19cSHans Petter Selasky break; 4424dd03e19cSHans Petter Selasky 4425dd03e19cSHans Petter Selasky case UT_WRITE_CLASS_DEVICE: 4426dd03e19cSHans Petter Selasky switch (req->bRequest) { 4427dd03e19cSHans Petter Selasky case UR_CLEAR_FEATURE: 4428dd03e19cSHans Petter Selasky goto tr_valid; 4429dd03e19cSHans Petter Selasky case UR_SET_DESCRIPTOR: 4430dd03e19cSHans Petter Selasky case UR_SET_FEATURE: 4431dd03e19cSHans Petter Selasky break; 4432dd03e19cSHans Petter Selasky default: 4433dd03e19cSHans Petter Selasky goto tr_stalled; 4434dd03e19cSHans Petter Selasky } 4435dd03e19cSHans Petter Selasky break; 4436dd03e19cSHans Petter Selasky 4437dd03e19cSHans Petter Selasky case UT_WRITE_CLASS_OTHER: 4438dd03e19cSHans Petter Selasky switch (req->bRequest) { 4439dd03e19cSHans Petter Selasky case UR_CLEAR_FEATURE: 4440dd03e19cSHans Petter Selasky goto tr_handle_clear_port_feature; 4441dd03e19cSHans Petter Selasky case UR_SET_FEATURE: 4442dd03e19cSHans Petter Selasky goto tr_handle_set_port_feature; 4443dd03e19cSHans Petter Selasky case UR_CLEAR_TT_BUFFER: 4444dd03e19cSHans Petter Selasky case UR_RESET_TT: 4445dd03e19cSHans Petter Selasky case UR_STOP_TT: 4446dd03e19cSHans Petter Selasky goto tr_valid; 4447dd03e19cSHans Petter Selasky 4448dd03e19cSHans Petter Selasky default: 4449dd03e19cSHans Petter Selasky goto tr_stalled; 4450dd03e19cSHans Petter Selasky } 4451dd03e19cSHans Petter Selasky break; 4452dd03e19cSHans Petter Selasky 4453dd03e19cSHans Petter Selasky case UT_READ_CLASS_OTHER: 4454dd03e19cSHans Petter Selasky switch (req->bRequest) { 4455dd03e19cSHans Petter Selasky case UR_GET_TT_STATE: 4456dd03e19cSHans Petter Selasky goto tr_handle_get_tt_state; 4457dd03e19cSHans Petter Selasky case UR_GET_STATUS: 4458dd03e19cSHans Petter Selasky goto tr_handle_get_port_status; 4459dd03e19cSHans Petter Selasky default: 4460dd03e19cSHans Petter Selasky goto tr_stalled; 4461dd03e19cSHans Petter Selasky } 4462dd03e19cSHans Petter Selasky break; 4463dd03e19cSHans Petter Selasky 4464dd03e19cSHans Petter Selasky case UT_READ_CLASS_DEVICE: 4465dd03e19cSHans Petter Selasky switch (req->bRequest) { 4466dd03e19cSHans Petter Selasky case UR_GET_DESCRIPTOR: 4467dd03e19cSHans Petter Selasky goto tr_handle_get_class_descriptor; 4468dd03e19cSHans Petter Selasky case UR_GET_STATUS: 4469dd03e19cSHans Petter Selasky goto tr_handle_get_class_status; 4470dd03e19cSHans Petter Selasky 4471dd03e19cSHans Petter Selasky default: 4472dd03e19cSHans Petter Selasky goto tr_stalled; 4473dd03e19cSHans Petter Selasky } 4474dd03e19cSHans Petter Selasky break; 4475dd03e19cSHans Petter Selasky default: 4476dd03e19cSHans Petter Selasky goto tr_stalled; 4477dd03e19cSHans Petter Selasky } 4478dd03e19cSHans Petter Selasky goto tr_valid; 4479dd03e19cSHans Petter Selasky 4480dd03e19cSHans Petter Selasky tr_handle_get_descriptor: 4481dd03e19cSHans Petter Selasky switch (value >> 8) { 4482dd03e19cSHans Petter Selasky case UDESC_DEVICE: 4483dd03e19cSHans Petter Selasky if (value & 0xff) { 4484dd03e19cSHans Petter Selasky goto tr_stalled; 4485dd03e19cSHans Petter Selasky } 4486dd03e19cSHans Petter Selasky len = sizeof(dwc_otg_devd); 4487dd03e19cSHans Petter Selasky ptr = (const void *)&dwc_otg_devd; 4488dd03e19cSHans Petter Selasky goto tr_valid; 4489dd03e19cSHans Petter Selasky case UDESC_CONFIG: 4490dd03e19cSHans Petter Selasky if (value & 0xff) { 4491dd03e19cSHans Petter Selasky goto tr_stalled; 4492dd03e19cSHans Petter Selasky } 4493dd03e19cSHans Petter Selasky len = sizeof(dwc_otg_confd); 4494dd03e19cSHans Petter Selasky ptr = (const void *)&dwc_otg_confd; 4495dd03e19cSHans Petter Selasky goto tr_valid; 4496dd03e19cSHans Petter Selasky case UDESC_STRING: 4497dd03e19cSHans Petter Selasky switch (value & 0xff) { 4498dd03e19cSHans Petter Selasky case 0: /* Language table */ 449923ab0871SHans Petter Selasky len = sizeof(usb_string_lang_en); 450023ab0871SHans Petter Selasky ptr = (const void *)&usb_string_lang_en; 4501dd03e19cSHans Petter Selasky goto tr_valid; 4502dd03e19cSHans Petter Selasky 4503dd03e19cSHans Petter Selasky case 1: /* Vendor */ 4504dd03e19cSHans Petter Selasky len = sizeof(dwc_otg_vendor); 4505dd03e19cSHans Petter Selasky ptr = (const void *)&dwc_otg_vendor; 4506dd03e19cSHans Petter Selasky goto tr_valid; 4507dd03e19cSHans Petter Selasky 4508dd03e19cSHans Petter Selasky case 2: /* Product */ 4509dd03e19cSHans Petter Selasky len = sizeof(dwc_otg_product); 4510dd03e19cSHans Petter Selasky ptr = (const void *)&dwc_otg_product; 4511dd03e19cSHans Petter Selasky goto tr_valid; 4512dd03e19cSHans Petter Selasky default: 4513dd03e19cSHans Petter Selasky break; 4514dd03e19cSHans Petter Selasky } 4515dd03e19cSHans Petter Selasky break; 4516dd03e19cSHans Petter Selasky default: 4517dd03e19cSHans Petter Selasky goto tr_stalled; 4518dd03e19cSHans Petter Selasky } 4519dd03e19cSHans Petter Selasky goto tr_stalled; 4520dd03e19cSHans Petter Selasky 4521dd03e19cSHans Petter Selasky tr_handle_get_config: 4522dd03e19cSHans Petter Selasky len = 1; 4523dd03e19cSHans Petter Selasky sc->sc_hub_temp.wValue[0] = sc->sc_conf; 4524dd03e19cSHans Petter Selasky goto tr_valid; 4525dd03e19cSHans Petter Selasky 4526dd03e19cSHans Petter Selasky tr_handle_get_status: 4527dd03e19cSHans Petter Selasky len = 2; 4528dd03e19cSHans Petter Selasky USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); 4529dd03e19cSHans Petter Selasky goto tr_valid; 4530dd03e19cSHans Petter Selasky 4531dd03e19cSHans Petter Selasky tr_handle_set_address: 4532dd03e19cSHans Petter Selasky if (value & 0xFF00) { 4533dd03e19cSHans Petter Selasky goto tr_stalled; 4534dd03e19cSHans Petter Selasky } 4535dd03e19cSHans Petter Selasky sc->sc_rt_addr = value; 4536dd03e19cSHans Petter Selasky goto tr_valid; 4537dd03e19cSHans Petter Selasky 4538dd03e19cSHans Petter Selasky tr_handle_set_config: 4539dd03e19cSHans Petter Selasky if (value >= 2) { 4540dd03e19cSHans Petter Selasky goto tr_stalled; 4541dd03e19cSHans Petter Selasky } 4542dd03e19cSHans Petter Selasky sc->sc_conf = value; 4543dd03e19cSHans Petter Selasky goto tr_valid; 4544dd03e19cSHans Petter Selasky 4545dd03e19cSHans Petter Selasky tr_handle_get_interface: 4546dd03e19cSHans Petter Selasky len = 1; 4547dd03e19cSHans Petter Selasky sc->sc_hub_temp.wValue[0] = 0; 4548dd03e19cSHans Petter Selasky goto tr_valid; 4549dd03e19cSHans Petter Selasky 4550dd03e19cSHans Petter Selasky tr_handle_get_tt_state: 4551dd03e19cSHans Petter Selasky tr_handle_get_class_status: 4552dd03e19cSHans Petter Selasky tr_handle_get_iface_status: 4553dd03e19cSHans Petter Selasky tr_handle_get_ep_status: 4554dd03e19cSHans Petter Selasky len = 2; 4555dd03e19cSHans Petter Selasky USETW(sc->sc_hub_temp.wValue, 0); 4556dd03e19cSHans Petter Selasky goto tr_valid; 4557dd03e19cSHans Petter Selasky 4558dd03e19cSHans Petter Selasky tr_handle_set_halt: 4559dd03e19cSHans Petter Selasky tr_handle_set_interface: 4560dd03e19cSHans Petter Selasky tr_handle_set_wakeup: 4561dd03e19cSHans Petter Selasky tr_handle_clear_wakeup: 4562dd03e19cSHans Petter Selasky tr_handle_clear_halt: 4563dd03e19cSHans Petter Selasky goto tr_valid; 4564dd03e19cSHans Petter Selasky 4565dd03e19cSHans Petter Selasky tr_handle_clear_port_feature: 45669cfd0731SHans Petter Selasky if (index != 1) 4567dd03e19cSHans Petter Selasky goto tr_stalled; 45689cfd0731SHans Petter Selasky 4569dd03e19cSHans Petter Selasky DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); 4570dd03e19cSHans Petter Selasky 4571dd03e19cSHans Petter Selasky switch (value) { 4572dd03e19cSHans Petter Selasky case UHF_PORT_SUSPEND: 4573dd03e19cSHans Petter Selasky dwc_otg_wakeup_peer(sc); 4574dd03e19cSHans Petter Selasky break; 4575dd03e19cSHans Petter Selasky 4576dd03e19cSHans Petter Selasky case UHF_PORT_ENABLE: 45779cfd0731SHans Petter Selasky if (sc->sc_flags.status_device_mode == 0) { 45789cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, 45799cfd0731SHans Petter Selasky sc->sc_hprt_val | HPRT_PRTENA); 45809cfd0731SHans Petter Selasky } 4581dd03e19cSHans Petter Selasky sc->sc_flags.port_enabled = 0; 4582dd03e19cSHans Petter Selasky break; 4583dd03e19cSHans Petter Selasky 45849cfd0731SHans Petter Selasky case UHF_C_PORT_RESET: 45859cfd0731SHans Petter Selasky sc->sc_flags.change_reset = 0; 45869cfd0731SHans Petter Selasky break; 45879cfd0731SHans Petter Selasky 45889cfd0731SHans Petter Selasky case UHF_C_PORT_ENABLE: 45899cfd0731SHans Petter Selasky sc->sc_flags.change_enabled = 0; 45909cfd0731SHans Petter Selasky break; 45919cfd0731SHans Petter Selasky 45929cfd0731SHans Petter Selasky case UHF_C_PORT_OVER_CURRENT: 45939cfd0731SHans Petter Selasky sc->sc_flags.change_over_current = 0; 45949cfd0731SHans Petter Selasky break; 45959cfd0731SHans Petter Selasky 4596dd03e19cSHans Petter Selasky case UHF_PORT_TEST: 4597dd03e19cSHans Petter Selasky case UHF_PORT_INDICATOR: 4598dd03e19cSHans Petter Selasky /* nops */ 4599dd03e19cSHans Petter Selasky break; 46009cfd0731SHans Petter Selasky 4601dd03e19cSHans Petter Selasky case UHF_PORT_POWER: 4602dd03e19cSHans Petter Selasky sc->sc_flags.port_powered = 0; 46039cfd0731SHans Petter Selasky if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) { 46049cfd0731SHans Petter Selasky sc->sc_hprt_val = 0; 46059cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, HPRT_PRTENA); 46069cfd0731SHans Petter Selasky } 4607dd03e19cSHans Petter Selasky dwc_otg_pull_down(sc); 4608dd03e19cSHans Petter Selasky dwc_otg_clocks_off(sc); 4609dd03e19cSHans Petter Selasky break; 46109cfd0731SHans Petter Selasky 4611dd03e19cSHans Petter Selasky case UHF_C_PORT_CONNECTION: 4612dd03e19cSHans Petter Selasky /* clear connect change flag */ 4613dd03e19cSHans Petter Selasky sc->sc_flags.change_connect = 0; 46149cfd0731SHans Petter Selasky break; 4615dd03e19cSHans Petter Selasky 4616dd03e19cSHans Petter Selasky case UHF_C_PORT_SUSPEND: 4617dd03e19cSHans Petter Selasky sc->sc_flags.change_suspend = 0; 4618dd03e19cSHans Petter Selasky break; 46199cfd0731SHans Petter Selasky 4620dd03e19cSHans Petter Selasky default: 4621dd03e19cSHans Petter Selasky err = USB_ERR_IOERROR; 4622dd03e19cSHans Petter Selasky goto done; 4623dd03e19cSHans Petter Selasky } 4624dd03e19cSHans Petter Selasky goto tr_valid; 4625dd03e19cSHans Petter Selasky 4626dd03e19cSHans Petter Selasky tr_handle_set_port_feature: 4627dd03e19cSHans Petter Selasky if (index != 1) { 4628dd03e19cSHans Petter Selasky goto tr_stalled; 4629dd03e19cSHans Petter Selasky } 4630dd03e19cSHans Petter Selasky DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); 4631dd03e19cSHans Petter Selasky 4632dd03e19cSHans Petter Selasky switch (value) { 4633dd03e19cSHans Petter Selasky case UHF_PORT_ENABLE: 4634dd03e19cSHans Petter Selasky break; 46359cfd0731SHans Petter Selasky 4636dd03e19cSHans Petter Selasky case UHF_PORT_SUSPEND: 46379cfd0731SHans Petter Selasky if (sc->sc_flags.status_device_mode == 0) { 46389cfd0731SHans Petter Selasky /* set suspend BIT */ 46399cfd0731SHans Petter Selasky sc->sc_hprt_val |= HPRT_PRTSUSP; 46409cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); 46419cfd0731SHans Petter Selasky 46429cfd0731SHans Petter Selasky /* generate HUB suspend event */ 46439cfd0731SHans Petter Selasky dwc_otg_suspend_irq(sc); 46449cfd0731SHans Petter Selasky } 46459cfd0731SHans Petter Selasky break; 46469cfd0731SHans Petter Selasky 4647dd03e19cSHans Petter Selasky case UHF_PORT_RESET: 46489cfd0731SHans Petter Selasky if (sc->sc_flags.status_device_mode == 0) { 46499cfd0731SHans Petter Selasky DPRINTF("PORT RESET\n"); 46509cfd0731SHans Petter Selasky 46519cfd0731SHans Petter Selasky /* enable PORT reset */ 46529cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val | HPRT_PRTRST); 46539cfd0731SHans Petter Selasky 46549cfd0731SHans Petter Selasky /* Wait 62.5ms for reset to complete */ 46559cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16); 46569cfd0731SHans Petter Selasky 46579cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); 46589cfd0731SHans Petter Selasky 46599cfd0731SHans Petter Selasky /* Wait 62.5ms for reset to complete */ 46609cfd0731SHans Petter Selasky usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16); 46619cfd0731SHans Petter Selasky 46629cfd0731SHans Petter Selasky /* reset FIFOs */ 4663db4300daSHans Petter Selasky (void) dwc_otg_init_fifo(sc, DWC_MODE_HOST); 46649cfd0731SHans Petter Selasky 46659cfd0731SHans Petter Selasky sc->sc_flags.change_reset = 1; 46669cfd0731SHans Petter Selasky } else { 46679cfd0731SHans Petter Selasky err = USB_ERR_IOERROR; 46689cfd0731SHans Petter Selasky } 46699cfd0731SHans Petter Selasky break; 46709cfd0731SHans Petter Selasky 4671dd03e19cSHans Petter Selasky case UHF_PORT_TEST: 4672dd03e19cSHans Petter Selasky case UHF_PORT_INDICATOR: 4673dd03e19cSHans Petter Selasky /* nops */ 4674dd03e19cSHans Petter Selasky break; 4675dd03e19cSHans Petter Selasky case UHF_PORT_POWER: 4676f5757453SHans Petter Selasky sc->sc_flags.port_powered = 1; 46779cfd0731SHans Petter Selasky if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) { 46789cfd0731SHans Petter Selasky sc->sc_hprt_val |= HPRT_PRTPWR; 46799cfd0731SHans Petter Selasky DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val); 46809cfd0731SHans Petter Selasky } 4681f5757453SHans Petter Selasky if (sc->sc_mode == DWC_MODE_DEVICE || sc->sc_mode == DWC_MODE_OTG) { 4682f5757453SHans Petter Selasky /* pull up D+, if any */ 4683f5757453SHans Petter Selasky dwc_otg_pull_up(sc); 4684f5757453SHans Petter Selasky } 4685dd03e19cSHans Petter Selasky break; 4686dd03e19cSHans Petter Selasky default: 4687dd03e19cSHans Petter Selasky err = USB_ERR_IOERROR; 4688dd03e19cSHans Petter Selasky goto done; 4689dd03e19cSHans Petter Selasky } 4690dd03e19cSHans Petter Selasky goto tr_valid; 4691dd03e19cSHans Petter Selasky 4692dd03e19cSHans Petter Selasky tr_handle_get_port_status: 4693dd03e19cSHans Petter Selasky 4694dd03e19cSHans Petter Selasky DPRINTFN(9, "UR_GET_PORT_STATUS\n"); 4695dd03e19cSHans Petter Selasky 46969cfd0731SHans Petter Selasky if (index != 1) 4697dd03e19cSHans Petter Selasky goto tr_stalled; 46989cfd0731SHans Petter Selasky 46999cfd0731SHans Petter Selasky if (sc->sc_flags.status_vbus) 4700dd03e19cSHans Petter Selasky dwc_otg_clocks_on(sc); 47019cfd0731SHans Petter Selasky else 4702dd03e19cSHans Petter Selasky dwc_otg_clocks_off(sc); 4703dd03e19cSHans Petter Selasky 4704dd03e19cSHans Petter Selasky /* Select Device Side Mode */ 4705dd03e19cSHans Petter Selasky 47063eabad25SHans Petter Selasky if (sc->sc_flags.status_device_mode) { 4707dd03e19cSHans Petter Selasky value = UPS_PORT_MODE_DEVICE; 47083eabad25SHans Petter Selasky dwc_otg_timer_stop(sc); 47093eabad25SHans Petter Selasky } else { 47109cfd0731SHans Petter Selasky value = 0; 47113eabad25SHans Petter Selasky dwc_otg_timer_start(sc); 47123eabad25SHans Petter Selasky } 4713dd03e19cSHans Petter Selasky 47149cfd0731SHans Petter Selasky if (sc->sc_flags.status_high_speed) 4715dd03e19cSHans Petter Selasky value |= UPS_HIGH_SPEED; 47169cfd0731SHans Petter Selasky else if (sc->sc_flags.status_low_speed) 47179cfd0731SHans Petter Selasky value |= UPS_LOW_SPEED; 47189cfd0731SHans Petter Selasky 47199cfd0731SHans Petter Selasky if (sc->sc_flags.port_powered) 4720dd03e19cSHans Petter Selasky value |= UPS_PORT_POWER; 47219cfd0731SHans Petter Selasky 47229cfd0731SHans Petter Selasky if (sc->sc_flags.port_enabled) 4723dd03e19cSHans Petter Selasky value |= UPS_PORT_ENABLED; 47249cfd0731SHans Petter Selasky 47259cfd0731SHans Petter Selasky if (sc->sc_flags.port_over_current) 47269cfd0731SHans Petter Selasky value |= UPS_OVERCURRENT_INDICATOR; 47279cfd0731SHans Petter Selasky 4728dd03e19cSHans Petter Selasky if (sc->sc_flags.status_vbus && 47299cfd0731SHans Petter Selasky sc->sc_flags.status_bus_reset) 4730dd03e19cSHans Petter Selasky value |= UPS_CURRENT_CONNECT_STATUS; 47319cfd0731SHans Petter Selasky 47329cfd0731SHans Petter Selasky if (sc->sc_flags.status_suspend) 4733dd03e19cSHans Petter Selasky value |= UPS_SUSPEND; 47349cfd0731SHans Petter Selasky 4735dd03e19cSHans Petter Selasky USETW(sc->sc_hub_temp.ps.wPortStatus, value); 4736dd03e19cSHans Petter Selasky 4737dd03e19cSHans Petter Selasky value = 0; 4738dd03e19cSHans Petter Selasky 47392d890458SHans Petter Selasky if (sc->sc_flags.change_enabled) 47402d890458SHans Petter Selasky value |= UPS_C_PORT_ENABLED; 47419cfd0731SHans Petter Selasky if (sc->sc_flags.change_connect) 4742dd03e19cSHans Petter Selasky value |= UPS_C_CONNECT_STATUS; 47439cfd0731SHans Petter Selasky if (sc->sc_flags.change_suspend) 4744dd03e19cSHans Petter Selasky value |= UPS_C_SUSPEND; 47459cfd0731SHans Petter Selasky if (sc->sc_flags.change_reset) 47469cfd0731SHans Petter Selasky value |= UPS_C_PORT_RESET; 47479cfd0731SHans Petter Selasky if (sc->sc_flags.change_over_current) 47489cfd0731SHans Petter Selasky value |= UPS_C_OVERCURRENT_INDICATOR; 47499cfd0731SHans Petter Selasky 4750dd03e19cSHans Petter Selasky USETW(sc->sc_hub_temp.ps.wPortChange, value); 4751dd03e19cSHans Petter Selasky len = sizeof(sc->sc_hub_temp.ps); 4752dd03e19cSHans Petter Selasky goto tr_valid; 4753dd03e19cSHans Petter Selasky 4754dd03e19cSHans Petter Selasky tr_handle_get_class_descriptor: 4755dd03e19cSHans Petter Selasky if (value & 0xFF) { 4756dd03e19cSHans Petter Selasky goto tr_stalled; 4757dd03e19cSHans Petter Selasky } 4758dd03e19cSHans Petter Selasky ptr = (const void *)&dwc_otg_hubd; 4759dd03e19cSHans Petter Selasky len = sizeof(dwc_otg_hubd); 4760dd03e19cSHans Petter Selasky goto tr_valid; 4761dd03e19cSHans Petter Selasky 4762dd03e19cSHans Petter Selasky tr_stalled: 4763dd03e19cSHans Petter Selasky err = USB_ERR_STALLED; 4764dd03e19cSHans Petter Selasky tr_valid: 4765dd03e19cSHans Petter Selasky done: 4766dd03e19cSHans Petter Selasky *plength = len; 4767dd03e19cSHans Petter Selasky *pptr = ptr; 4768dd03e19cSHans Petter Selasky return (err); 4769dd03e19cSHans Petter Selasky } 4770dd03e19cSHans Petter Selasky 4771dd03e19cSHans Petter Selasky static void 4772dd03e19cSHans Petter Selasky dwc_otg_xfer_setup(struct usb_setup_params *parm) 4773dd03e19cSHans Petter Selasky { 4774dd03e19cSHans Petter Selasky struct usb_xfer *xfer; 4775dd03e19cSHans Petter Selasky void *last_obj; 4776dd03e19cSHans Petter Selasky uint32_t ntd; 4777dd03e19cSHans Petter Selasky uint32_t n; 4778dd03e19cSHans Petter Selasky uint8_t ep_no; 4779db4300daSHans Petter Selasky uint8_t ep_type; 4780dd03e19cSHans Petter Selasky 4781dd03e19cSHans Petter Selasky xfer = parm->curr_xfer; 4782dd03e19cSHans Petter Selasky 4783dd03e19cSHans Petter Selasky /* 4784dd03e19cSHans Petter Selasky * NOTE: This driver does not use any of the parameters that 4785dd03e19cSHans Petter Selasky * are computed from the following values. Just set some 4786dd03e19cSHans Petter Selasky * reasonable dummies: 4787dd03e19cSHans Petter Selasky */ 4788dd03e19cSHans Petter Selasky parm->hc_max_packet_size = 0x500; 4789bc990482SHans Petter Selasky parm->hc_max_packet_count = 3; 4790bc990482SHans Petter Selasky parm->hc_max_frame_size = 3 * 0x500; 4791dd03e19cSHans Petter Selasky 4792dd03e19cSHans Petter Selasky usbd_transfer_setup_sub(parm); 4793dd03e19cSHans Petter Selasky 4794dd03e19cSHans Petter Selasky /* 4795dd03e19cSHans Petter Selasky * compute maximum number of TDs 4796dd03e19cSHans Petter Selasky */ 4797db4300daSHans Petter Selasky ep_type = (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE); 4798db4300daSHans Petter Selasky 4799db4300daSHans Petter Selasky if (ep_type == UE_CONTROL) { 4800dd03e19cSHans Petter Selasky ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ 48019cfd0731SHans Petter Selasky + 1 /* SYNC 2 */ + 1 /* SYNC 3 */; 4802dd03e19cSHans Petter Selasky } else { 4803dd03e19cSHans Petter Selasky ntd = xfer->nframes + 1 /* SYNC */ ; 4804dd03e19cSHans Petter Selasky } 4805dd03e19cSHans Petter Selasky 4806dd03e19cSHans Petter Selasky /* 4807dd03e19cSHans Petter Selasky * check if "usbd_transfer_setup_sub" set an error 4808dd03e19cSHans Petter Selasky */ 4809dd03e19cSHans Petter Selasky if (parm->err) 4810dd03e19cSHans Petter Selasky return; 4811dd03e19cSHans Petter Selasky 4812dd03e19cSHans Petter Selasky /* 4813dd03e19cSHans Petter Selasky * allocate transfer descriptors 4814dd03e19cSHans Petter Selasky */ 4815dd03e19cSHans Petter Selasky last_obj = NULL; 4816dd03e19cSHans Petter Selasky 4817dd03e19cSHans Petter Selasky ep_no = xfer->endpointno & UE_ADDR; 48188f39f442SHans Petter Selasky 48198f39f442SHans Petter Selasky /* 48208f39f442SHans Petter Selasky * Check for a valid endpoint profile in USB device mode: 48218f39f442SHans Petter Selasky */ 48228f39f442SHans Petter Selasky if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { 48238f39f442SHans Petter Selasky const struct usb_hw_ep_profile *pf; 48248f39f442SHans Petter Selasky 4825dd03e19cSHans Petter Selasky dwc_otg_get_hw_ep_profile(parm->udev, &pf, ep_no); 4826dd03e19cSHans Petter Selasky 4827dd03e19cSHans Petter Selasky if (pf == NULL) { 4828dd03e19cSHans Petter Selasky /* should not happen */ 4829dd03e19cSHans Petter Selasky parm->err = USB_ERR_INVAL; 4830dd03e19cSHans Petter Selasky return; 4831dd03e19cSHans Petter Selasky } 48328f39f442SHans Petter Selasky } 4833dd03e19cSHans Petter Selasky 4834dd03e19cSHans Petter Selasky /* align data */ 4835dd03e19cSHans Petter Selasky parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); 4836dd03e19cSHans Petter Selasky 4837dd03e19cSHans Petter Selasky for (n = 0; n != ntd; n++) { 4838dd03e19cSHans Petter Selasky struct dwc_otg_td *td; 4839dd03e19cSHans Petter Selasky 4840dd03e19cSHans Petter Selasky if (parm->buf) { 4841dd03e19cSHans Petter Selasky td = USB_ADD_BYTES(parm->buf, parm->size[0]); 4842dd03e19cSHans Petter Selasky 484308aa4c94SHans Petter Selasky /* compute shared bandwidth resource index for TT */ 4844076daedaSHans Petter Selasky if (dwc_otg_uses_split(parm->udev)) { 484508aa4c94SHans Petter Selasky if (parm->udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) 484608aa4c94SHans Petter Selasky td->tt_index = parm->udev->device_index; 484708aa4c94SHans Petter Selasky else 484808aa4c94SHans Petter Selasky td->tt_index = parm->udev->parent_hs_hub->device_index; 484908aa4c94SHans Petter Selasky } else { 485008aa4c94SHans Petter Selasky td->tt_index = parm->udev->device_index; 485108aa4c94SHans Petter Selasky } 485208aa4c94SHans Petter Selasky 4853dd03e19cSHans Petter Selasky /* init TD */ 4854dd03e19cSHans Petter Selasky td->max_packet_size = xfer->max_packet_size; 4855bc990482SHans Petter Selasky td->max_packet_count = xfer->max_packet_count; 4856ce842cecSHans Petter Selasky /* range check */ 4857ce842cecSHans Petter Selasky if (td->max_packet_count == 0 || td->max_packet_count > 3) 4858ce842cecSHans Petter Selasky td->max_packet_count = 1; 4859dd03e19cSHans Petter Selasky td->ep_no = ep_no; 4860db4300daSHans Petter Selasky td->ep_type = ep_type; 4861dd03e19cSHans Petter Selasky td->obj_next = last_obj; 4862dd03e19cSHans Petter Selasky 4863dd03e19cSHans Petter Selasky last_obj = td; 4864dd03e19cSHans Petter Selasky } 4865dd03e19cSHans Petter Selasky parm->size[0] += sizeof(*td); 4866dd03e19cSHans Petter Selasky } 4867dd03e19cSHans Petter Selasky 4868dd03e19cSHans Petter Selasky xfer->td_start[0] = last_obj; 4869dd03e19cSHans Petter Selasky } 4870dd03e19cSHans Petter Selasky 4871dd03e19cSHans Petter Selasky static void 4872dd03e19cSHans Petter Selasky dwc_otg_xfer_unsetup(struct usb_xfer *xfer) 4873dd03e19cSHans Petter Selasky { 4874dd03e19cSHans Petter Selasky return; 4875dd03e19cSHans Petter Selasky } 4876dd03e19cSHans Petter Selasky 4877dd03e19cSHans Petter Selasky static void 4878dd03e19cSHans Petter Selasky dwc_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, 4879dd03e19cSHans Petter Selasky struct usb_endpoint *ep) 4880dd03e19cSHans Petter Selasky { 4881dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus); 4882dd03e19cSHans Petter Selasky 4883dd03e19cSHans Petter Selasky DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d,%d)\n", 4884dd03e19cSHans Petter Selasky ep, udev->address, 4885dd03e19cSHans Petter Selasky edesc->bEndpointAddress, udev->flags.usb_mode, 4886dd03e19cSHans Petter Selasky sc->sc_rt_addr, udev->device_index); 4887dd03e19cSHans Petter Selasky 4888dd03e19cSHans Petter Selasky if (udev->device_index != sc->sc_rt_addr) { 48899cfd0731SHans Petter Selasky if (udev->flags.usb_mode == USB_MODE_DEVICE) { 4890dd03e19cSHans Petter Selasky if (udev->speed != USB_SPEED_FULL && 4891dd03e19cSHans Petter Selasky udev->speed != USB_SPEED_HIGH) { 4892dd03e19cSHans Petter Selasky /* not supported */ 4893dd03e19cSHans Petter Selasky return; 4894dd03e19cSHans Petter Selasky } 4895db4300daSHans Petter Selasky } else { 4896ce842cecSHans Petter Selasky if (udev->speed == USB_SPEED_HIGH && 4897ce842cecSHans Petter Selasky (edesc->wMaxPacketSize[1] & 0x18) != 0 && 4898ce842cecSHans Petter Selasky (edesc->bmAttributes & UE_XFERTYPE) != UE_ISOCHRONOUS) { 4899ce842cecSHans Petter Selasky /* not supported */ 4900ce842cecSHans Petter Selasky DPRINTFN(-1, "Non-isochronous high bandwidth " 4901ce842cecSHans Petter Selasky "endpoint not supported\n"); 4902db4300daSHans Petter Selasky return; 4903db4300daSHans Petter Selasky } 4904db4300daSHans Petter Selasky } 4905dd03e19cSHans Petter Selasky if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS) 49069cfd0731SHans Petter Selasky ep->methods = &dwc_otg_device_isoc_methods; 4907dd03e19cSHans Petter Selasky else 4908dd03e19cSHans Petter Selasky ep->methods = &dwc_otg_device_non_isoc_methods; 4909dd03e19cSHans Petter Selasky } 4910dd03e19cSHans Petter Selasky } 4911dd03e19cSHans Petter Selasky 4912dd03e19cSHans Petter Selasky static void 4913dd03e19cSHans Petter Selasky dwc_otg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) 4914dd03e19cSHans Petter Selasky { 4915dd03e19cSHans Petter Selasky struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus); 4916dd03e19cSHans Petter Selasky 4917dd03e19cSHans Petter Selasky switch (state) { 4918dd03e19cSHans Petter Selasky case USB_HW_POWER_SUSPEND: 4919dd03e19cSHans Petter Selasky dwc_otg_suspend(sc); 4920dd03e19cSHans Petter Selasky break; 4921dd03e19cSHans Petter Selasky case USB_HW_POWER_SHUTDOWN: 4922dd03e19cSHans Petter Selasky dwc_otg_uninit(sc); 4923dd03e19cSHans Petter Selasky break; 4924dd03e19cSHans Petter Selasky case USB_HW_POWER_RESUME: 4925dd03e19cSHans Petter Selasky dwc_otg_resume(sc); 4926dd03e19cSHans Petter Selasky break; 4927dd03e19cSHans Petter Selasky default: 4928dd03e19cSHans Petter Selasky break; 4929dd03e19cSHans Petter Selasky } 4930dd03e19cSHans Petter Selasky } 4931dd03e19cSHans Petter Selasky 49329cfd0731SHans Petter Selasky static void 49339cfd0731SHans Petter Selasky dwc_otg_get_dma_delay(struct usb_device *udev, uint32_t *pus) 49349cfd0731SHans Petter Selasky { 49359cfd0731SHans Petter Selasky /* DMA delay - wait until any use of memory is finished */ 49369cfd0731SHans Petter Selasky *pus = (2125); /* microseconds */ 49379cfd0731SHans Petter Selasky } 49389cfd0731SHans Petter Selasky 49399cfd0731SHans Petter Selasky static void 49409cfd0731SHans Petter Selasky dwc_otg_device_resume(struct usb_device *udev) 49419cfd0731SHans Petter Selasky { 49429cfd0731SHans Petter Selasky DPRINTF("\n"); 494358ecbe49SHans Petter Selasky 4944b792f659SHans Petter Selasky /* poll all transfers again to restart resumed ones */ 4945b792f659SHans Petter Selasky dwc_otg_do_poll(udev->bus); 49469cfd0731SHans Petter Selasky } 49479cfd0731SHans Petter Selasky 49489cfd0731SHans Petter Selasky static void 49499cfd0731SHans Petter Selasky dwc_otg_device_suspend(struct usb_device *udev) 49509cfd0731SHans Petter Selasky { 49519cfd0731SHans Petter Selasky DPRINTF("\n"); 49529cfd0731SHans Petter Selasky } 49539cfd0731SHans Petter Selasky 4954e892b3feSHans Petter Selasky static const struct usb_bus_methods dwc_otg_bus_methods = 4955dd03e19cSHans Petter Selasky { 4956dd03e19cSHans Petter Selasky .endpoint_init = &dwc_otg_ep_init, 4957dd03e19cSHans Petter Selasky .xfer_setup = &dwc_otg_xfer_setup, 4958dd03e19cSHans Petter Selasky .xfer_unsetup = &dwc_otg_xfer_unsetup, 4959dd03e19cSHans Petter Selasky .get_hw_ep_profile = &dwc_otg_get_hw_ep_profile, 4960a5cf1aaaSHans Petter Selasky .xfer_stall = &dwc_otg_xfer_stall, 4961dd03e19cSHans Petter Selasky .set_stall = &dwc_otg_set_stall, 4962dd03e19cSHans Petter Selasky .clear_stall = &dwc_otg_clear_stall, 4963dd03e19cSHans Petter Selasky .roothub_exec = &dwc_otg_roothub_exec, 4964dd03e19cSHans Petter Selasky .xfer_poll = &dwc_otg_do_poll, 4965dd03e19cSHans Petter Selasky .device_state_change = &dwc_otg_device_state_change, 4966dd03e19cSHans Petter Selasky .set_hw_power_sleep = &dwc_otg_set_hw_power_sleep, 49679cfd0731SHans Petter Selasky .get_dma_delay = &dwc_otg_get_dma_delay, 49689cfd0731SHans Petter Selasky .device_resume = &dwc_otg_device_resume, 49699cfd0731SHans Petter Selasky .device_suspend = &dwc_otg_device_suspend, 4970dd03e19cSHans Petter Selasky }; 4971