From owner-svn-src-stable@FreeBSD.ORG Thu Oct 24 06:22:43 2013 Return-Path: Delivered-To: svn-src-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id EA1317B8; Thu, 24 Oct 2013 06:22:43 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id C96962C94; Thu, 24 Oct 2013 06:22:43 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9O6MhZa038740; Thu, 24 Oct 2013 06:22:43 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9O6Mh7s038738; Thu, 24 Oct 2013 06:22:43 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201310240622.r9O6Mh7s038738@svn.freebsd.org> From: Hans Petter Selasky Date: Thu, 24 Oct 2013 06:22:43 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r257041 - stable/9/sys/dev/usb/controller X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 24 Oct 2013 06:22:44 -0000 Author: hselasky Date: Thu Oct 24 06:22:43 2013 New Revision: 257041 URL: http://svnweb.freebsd.org/changeset/base/257041 Log: MFC r252912, r254828 and r256548: Add host mode support to the Mentor Graphics USB OTG controller driver. PR: usb/181987 Modified: stable/9/sys/dev/usb/controller/musb_otg.c stable/9/sys/dev/usb/controller/musb_otg.h stable/9/sys/dev/usb/controller/musb_otg_atmelarm.c Directory Properties: stable/9/sys/ (props changed) stable/9/sys/dev/ (props changed) Modified: stable/9/sys/dev/usb/controller/musb_otg.c ============================================================================== --- stable/9/sys/dev/usb/controller/musb_otg.c Thu Oct 24 06:06:17 2013 (r257040) +++ stable/9/sys/dev/usb/controller/musb_otg.c Thu Oct 24 06:22:43 2013 (r257041) @@ -90,6 +90,8 @@ SYSCTL_INT(_hw_usb_musbotg, OID_AUTO, de &musbotgdebug, 0, "Debug level"); #endif +#define MAX_NAK_TO 16 + /* prototypes */ struct usb_bus_methods musbotg_bus_methods; @@ -98,17 +100,35 @@ struct usb_pipe_methods musbotg_device_c struct usb_pipe_methods musbotg_device_intr_methods; struct usb_pipe_methods musbotg_device_isoc_methods; -static musbotg_cmd_t musbotg_setup_rx; -static musbotg_cmd_t musbotg_setup_data_rx; -static musbotg_cmd_t musbotg_setup_data_tx; -static musbotg_cmd_t musbotg_setup_status; -static musbotg_cmd_t musbotg_data_rx; -static musbotg_cmd_t musbotg_data_tx; +/* Control transfers: Device mode */ +static musbotg_cmd_t musbotg_dev_ctrl_setup_rx; +static musbotg_cmd_t musbotg_dev_ctrl_data_rx; +static musbotg_cmd_t musbotg_dev_ctrl_data_tx; +static musbotg_cmd_t musbotg_dev_ctrl_status; + +/* Control transfers: Host mode */ +static musbotg_cmd_t musbotg_host_ctrl_setup_tx; +static musbotg_cmd_t musbotg_host_ctrl_data_rx; +static musbotg_cmd_t musbotg_host_ctrl_data_tx; +static musbotg_cmd_t musbotg_host_ctrl_status_rx; +static musbotg_cmd_t musbotg_host_ctrl_status_tx; + +/* Bulk, Interrupt, Isochronous: Device mode */ +static musbotg_cmd_t musbotg_dev_data_rx; +static musbotg_cmd_t musbotg_dev_data_tx; + +/* Bulk, Interrupt, Isochronous: Host mode */ +static musbotg_cmd_t musbotg_host_data_rx; +static musbotg_cmd_t musbotg_host_data_tx; + static void musbotg_device_done(struct usb_xfer *, usb_error_t); static void musbotg_do_poll(struct usb_bus *); static void musbotg_standard_done(struct usb_xfer *); static void musbotg_interrupt_poll(struct musbotg_softc *); static void musbotg_root_intr(struct musbotg_softc *); +static int musbotg_channel_alloc(struct musbotg_softc *, struct musbotg_td *td); +static void musbotg_channel_free(struct musbotg_softc *, struct musbotg_td *td); +static void musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on); /* * Here is a configuration that the chip supports. @@ -123,6 +143,64 @@ static const struct usb_hw_ep_profile mu } }; +static int +musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td) +{ + int ch; + int ep; + + ep = td->ep_no; + + /* In device mode each EP got its own channel */ + if (sc->sc_mode == MUSB2_DEVICE_MODE) { + musbotg_ep_int_set(sc, ep, 1); + return (ep); + } + + /* + * All control transactions go through EP0 + */ + if (ep == 0) { + if (sc->sc_channel_mask & (1 << 0)) + return (-1); + sc->sc_channel_mask |= (1 << 0); + musbotg_ep_int_set(sc, ep, 1); + return (0); + } + + for (ch = 1; ch < MUSB2_EP_MAX; ch++) { + if (!(sc->sc_channel_mask & (1 << ch))) { + sc->sc_channel_mask |= (1 << ch); + musbotg_ep_int_set(sc, ch, 1); + return (ch); + } + } + + DPRINTFN(-1, "No available channels. Mask: %04x\n", sc->sc_channel_mask); + + return (-1); +} + +static void +musbotg_channel_free(struct musbotg_softc *sc, struct musbotg_td *td) +{ + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + if (sc->sc_mode == MUSB2_DEVICE_MODE) + return; + + if (td == NULL) + return; + if (td->channel == -1) + return; + + musbotg_ep_int_set(sc, td->channel, 0); + sc->sc_channel_mask &= ~(1 << td->channel); + + td->channel = -1; +} + static void musbotg_get_hw_ep_profile(struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr) @@ -213,6 +291,46 @@ musbotg_pull_down(struct musbotg_softc * } static void +musbotg_suspend_host(struct musbotg_softc *sc) +{ + uint8_t temp; + + if (sc->sc_flags.status_suspend) { + return; + } + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp |= MUSB2_MASK_SUSPMODE; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + sc->sc_flags.status_suspend = 1; +} + +static void +musbotg_wakeup_host(struct musbotg_softc *sc) +{ + uint8_t temp; + + if (!(sc->sc_flags.status_suspend)) { + return; + } + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp &= ~MUSB2_MASK_SUSPMODE; + temp |= MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + + /* wait 20 milliseconds */ + /* Wait for reset to complete. */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp &= ~MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + + sc->sc_flags.status_suspend = 0; +} + +static void musbotg_wakeup_peer(struct musbotg_softc *sc) { uint8_t temp; @@ -243,7 +361,7 @@ musbotg_set_address(struct musbotg_softc } static uint8_t -musbotg_setup_rx(struct musbotg_td *td) +musbotg_dev_ctrl_setup_rx(struct musbotg_td *td) { struct musbotg_softc *sc; struct usb_device_request req; @@ -253,6 +371,15 @@ musbotg_setup_rx(struct musbotg_td *td) /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); @@ -269,8 +396,10 @@ musbotg_setup_rx(struct musbotg_td *td) /* do not stall at this point */ td->did_stall = 1; /* wait for interrupt */ + DPRINTFN(0, "CSR0 DATAEND\n"); goto not_complete; } + if (csr & MUSB2_MASK_CSR0L_SENTSTALL) { /* clear SENTSTALL */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); @@ -289,6 +418,7 @@ musbotg_setup_rx(struct musbotg_td *td) sc->sc_ep0_busy = 0; } if (sc->sc_ep0_busy) { + DPRINTFN(0, "EP0 BUSY\n"); goto not_complete; } if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) { @@ -337,6 +467,8 @@ musbotg_setup_rx(struct musbotg_td *td) } else { sc->sc_dv_addr = 0xFF; } + + musbotg_channel_free(sc, td); return (0); /* complete */ not_complete: @@ -350,10 +482,117 @@ not_complete: return (1); /* not complete */ } +static uint8_t +musbotg_host_ctrl_setup_tx(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + struct usb_device_request req; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* Not ready yet yet */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO) { + DPRINTFN(1, "NAK timeout\n"); + + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); + } + + /* Fifo is not empty and there is no NAK timeout */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + /* check if we are complete */ + if (td->remainder == 0) { + /* we are complete */ + musbotg_channel_free(sc, td); + return (0); + } + + /* copy data into real buffer */ + usbd_copy_out(td->pc, 0, &req, sizeof(req)); + + /* send data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req)); + + /* update offset and remainder */ + td->offset += sizeof(req); + td->remainder -= sizeof(req); + + + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_TXPKTRDY | + MUSB2_MASK_CSR0L_SETUPPKT); + + /* Just to be consistent, not used above */ + td->transaction_started = 1; + + return (1); /* in progress */ +} + /* Control endpoint only data handling functions (RX/TX/SYNC) */ static uint8_t -musbotg_setup_data_rx(struct musbotg_td *td) +musbotg_dev_ctrl_data_rx(struct musbotg_td *td) { struct usb_page_search buf_res; struct musbotg_softc *sc; @@ -496,7 +735,7 @@ musbotg_setup_data_rx(struct musbotg_td } static uint8_t -musbotg_setup_data_tx(struct musbotg_td *td) +musbotg_dev_ctrl_data_tx(struct musbotg_td *td) { struct usb_page_search buf_res; struct musbotg_softc *sc; @@ -614,77 +853,943 @@ musbotg_setup_data_tx(struct musbotg_td } static uint8_t -musbotg_setup_status(struct musbotg_td *td) +musbotg_host_ctrl_data_rx(struct musbotg_td *td) { + struct usb_page_search buf_res; struct musbotg_softc *sc; + uint16_t count; uint8_t csr; + uint8_t got_short; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); - if (sc->sc_ep0_busy) { - sc->sc_ep0_busy = 0; - sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND; - MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); - sc->sc_ep0_cmd = 0; - } /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); - if (csr & MUSB2_MASK_CSR0L_DATAEND) { - /* wait for interrupt */ - return (1); /* not complete */ + got_short = 0; + if (!td->transaction_started) { + td->transaction_started = 1; + + MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO); + + MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0), + td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type); + + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_REQPKT); + + return (1); + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO) { + csr &= ~MUSB2_MASK_CSR0L_REQPKT; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + } + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + + if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) + return (1); /* not yet */ + + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + + /* verify the packet byte count */ + if (count != td->max_frame_size) { + if (count < td->max_frame_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + temp = count & ~3; + + if (temp) { + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), + (void *)(&sc->sc_bounce_buf[count / 4]), temp); + } + usbd_copy_in(td->pc, td->offset, + sc->sc_bounce_buf, count); + + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* receive data */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + csr &= ~MUSB2_MASK_CSR0L_RXPKTRDY; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + + musbotg_channel_free(sc, td); + return (0); + } + /* else need to receive a zero length packet */ + } + + td->transaction_started = 1; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_REQPKT); + + return (1); /* not complete */ +} + +static uint8_t +musbotg_host_ctrl_data_tx(struct musbotg_td *td) +{ + struct usb_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* No free EPs */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) { + /* clear status bits */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + td->error = 1; + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO ) { + + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + } + + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + /* + * Wait while FIFO is empty. + * Do not flush it because it will cause transactions + * with size more then packet size. It might upset + * some devices + */ + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) + return (1); + + /* Packet still being processed */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + if (td->transaction_started) { + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + + /* We're not complete - more transactions required */ + td->transaction_started = 0; + } + + /* check for short packet */ + count = td->max_frame_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + usbd_copy_out(td->pc, td->offset, + sc->sc_bounce_buf, count); + + temp = count & ~3; + + if (temp) { + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), + sc->sc_bounce_buf, temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* transmit data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* Function address */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* TX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_TXPKTRDY); + + td->transaction_started = 1; + + return (1); /* not complete */ +} + +static uint8_t +musbotg_dev_ctrl_status(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + if (sc->sc_ep0_busy) { + sc->sc_ep0_busy = 0; + sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); + sc->sc_ep0_cmd = 0; + } + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & MUSB2_MASK_CSR0L_DATAEND) { + /* wait for interrupt */ + return (1); /* not complete */ + } + if (sc->sc_dv_addr != 0xFF) { + /* write function address */ + musbotg_set_address(sc, sc->sc_dv_addr); + } + + musbotg_channel_free(sc, td); + return (0); /* complete */ +} + +static uint8_t +musbotg_host_ctrl_status_rx(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + if (!td->transaction_started) { + MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0), + td->dev_addr); + + MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type); + + /* RX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO); + + td->transaction_started = 1; + + /* Disable PING */ + csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH); + csrh |= MUSB2_MASK_CSR0H_PING_DIS; + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, csrh); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_STATUSPKT | + MUSB2_MASK_CSR0L_REQPKT); + + return (1); /* Just started */ + + } + + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "IN STATUS csr=0x%02x\n", csr); + + if (csr & MUSB2_MASK_CSR0L_RXPKTRDY) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_RXPKTRDY_CLR); + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO) { + csr &= ~ (MUSB2_MASK_CSR0L_STATUSPKT | + MUSB2_MASK_CSR0L_REQPKT); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + td->error = 1; + } + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); + } + + return (1); /* Not ready yet */ +} + +static uint8_t +musbotg_host_ctrl_status_tx(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d/%d [%d@%d.%d/%02x]\n", td->channel, td->transaction_started, + td->dev_addr,td->haddr,td->hport, td->transfer_type); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* Not yet */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + if (td->transaction_started) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, MUSB2_MASK_CSR0H_PING_DIS); + + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* TX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + + td->transaction_started = 1; + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_STATUSPKT | + MUSB2_MASK_CSR0L_TXPKTRDY); + + return (1); /* wait for interrupt */ +} + +static uint8_t +musbotg_dev_data_rx(struct musbotg_td *td) +{ + struct usb_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr; + uint8_t to; + uint8_t got_short; + + to = 8; /* don't loop forever! */ + got_short = 0; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel); + +repeat: + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* clear overrun */ + if (csr & MUSB2_MASK_CSRL_RXOVERRUN) { + /* make sure we don't clear "RXPKTRDY" */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXPKTRDY); + } + + /* check status */ + if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) + return (1); /* not complete */ + + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + + DPRINTFN(4, "count=0x%04x\n", count); + + /* + * Check for short or invalid packet: + */ + if (count != td->max_frame_size) { + if (count < td->max_frame_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + temp = count & ~3; + + if (temp) { + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_read_multi_1(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***