From owner-svn-src-all@FreeBSD.ORG Sat Jan 21 13:45:23 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 8CA17106564A; Sat, 21 Jan 2012 13:45:23 +0000 (UTC) (envelope-from ray@ddteam.net) Received: from mail-bk0-f54.google.com (mail-bk0-f54.google.com [209.85.214.54]) by mx1.freebsd.org (Postfix) with ESMTP id ADE328FC0C; Sat, 21 Jan 2012 13:45:22 +0000 (UTC) Received: by bkbc12 with SMTP id c12so1682438bkb.13 for ; Sat, 21 Jan 2012 05:45:21 -0800 (PST) Received: by 10.205.26.67 with SMTP id rl3mr650549bkb.45.1327153520000; Sat, 21 Jan 2012 05:45:20 -0800 (PST) Received: from rnote.ddteam.net (58-37-133-95.pool.ukrtel.net. [95.133.37.58]) by mx.google.com with ESMTPS id d2sm13609937bky.11.2012.01.21.05.45.17 (version=SSLv3 cipher=OTHER); Sat, 21 Jan 2012 05:45:19 -0800 (PST) Date: Sat, 21 Jan 2012 15:45:07 +0200 From: Aleksandr Rybalko To: Hans Petter Selasky Message-Id: <20120121154507.0f1cd659.ray@ddteam.net> In-Reply-To: <201201211331.q0LDVc3N093974@svn.freebsd.org> References: <201201211331.q0LDVc3N093974@svn.freebsd.org> X-Mailer: Sylpheed 3.1.2 (GTK+ 2.24.5; amd64-portbld-freebsd9.0) Mime-Version: 1.0 X-Gm-Message-State: ALoCoQlcKWq/hSZTK9fA23qlUwdWRId9ntkGo/jtWXz5w7igQV3CcWf+vYXVYgazOcoeSF3fxlT/ Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Cc: svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org Subject: Re: svn commit: r230424 - head/sys/dev/usb/controller X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 21 Jan 2012 13:45:23 -0000 On Sat, 21 Jan 2012 13:31:38 +0000 (UTC) Hans Petter Selasky wrote: > Author: hselasky > Date: Sat Jan 21 13:31:38 2012 > New Revision: 230424 > URL: http://svn.freebsd.org/changeset/base/230424 > > Log: > Add support for the DesignWare USB 2.0 OTG controller chipset. > Currently the code is not built by any modules. That will > be fixed later. The Atmel ARM bus interface file part of this > commit is just for sake of example. All registers and bits are > declared like macros and not C-structures like in official > Synopsis header files. This driver mostly origins from the > musb_otg.c driver in FreeBSD except that the chip specific > programming has been replaced by the one for DWC 2.0 USB OTG. > Some parts related to system suspend and resume have been left > like empty functions for the future. USB suspend and resume is > fully supported. Wow, it is very cool! This is same controller about which i mailed you year ago Hans. It can be found not only in Atmel ARM, but also in: 1. Cavium Octeon SoC's (some have EHCI, but most DWC OTG) 2. many PowerPC SoC's 3. Ralink RT3050F/RT3052F And I think list much longer. Last (#3) answer your question to me (Subject: Where is controller/dotg.h ?). sorry for long silent about that. But I will rework a bit mips/rt305x and reconnect it with your new driver. Thank you so much! > > Added: > head/sys/dev/usb/controller/dwc_otg.c (contents, props changed) > head/sys/dev/usb/controller/dwc_otg.h (contents, props changed) > head/sys/dev/usb/controller/dwc_otg_atmelarm.c (contents, props > changed) > > Added: head/sys/dev/usb/controller/dwc_otg.c > ============================================================================== > --- /dev/null 00:00:00 1970 (empty, because file is > newly added) +++ head/sys/dev/usb/controller/dwc_otg.c Sat Jan > 21 13:31:38 2012 (r230424) @@ -0,0 +1,2612 @@ > +/*- > + * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above > copyright > + * notice, this list of conditions and the following disclaimer > in the > + * documentation and/or other materials provided with the > distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS > IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, > THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A > PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE > LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR > CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE > GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS > INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN > CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN > ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE > POSSIBILITY OF > + * SUCH DAMAGE. > + */ > + > +/* > + * This file contains the driver for the DesignWare series USB 2.0 > OTG > + * Controller. This driver currently only supports the device mode of > + * the USB hardware. > + */ > + > +/* > + * LIMITATION: Drivers must be bound to all OUT endpoints in the > + * active configuration for this driver to work properly. Blocking > any > + * OUT endpoint will block all OUT endpoints including the control > + * endpoint. Usually this is not a problem. > + */ > + > +/* > + * NOTE: Writing to non-existing registers appears to cause an > + * internal reset. > + */ > + > +#include > +__FBSDID("$FreeBSD$"); > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#define USB_DEBUG_VAR dwc_otg_debug > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#include > + > +#define DWC_OTG_BUS2SC(bus) \ > + ((struct dwc_otg_softc *)(((uint8_t *)(bus)) - \ > + ((uint8_t *)&(((struct dwc_otg_softc *)0)->sc_bus)))) > + > +#define DWC_OTG_PC2SC(pc) \ > + DWC_OTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus) > + > +#define DWC_OTG_MSK_GINT_ENABLED \ > + (DWC_OTG_MSK_GINT_ENUM_DONE | \ > + DWC_OTG_MSK_GINT_USB_SUSPEND | \ > + DWC_OTG_MSK_GINT_INEP | \ > + DWC_OTG_MSK_GINT_RXFLVL | \ > + DWC_OTG_MSK_GINT_SESSREQINT) > + > +#define DWC_OTG_USE_HSIC 0 > + > +#ifdef USB_DEBUG > +static int dwc_otg_debug = 0; > + > +static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW, 0, "USB > DWC OTG"); +SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RW, > + &dwc_otg_debug, 0, "DWC OTG debug level"); > +#endif > + > +#define DWC_OTG_INTR_ENDPT 1 > + > +/* prototypes */ > + > +struct usb_bus_methods dwc_otg_bus_methods; > +struct usb_pipe_methods dwc_otg_device_non_isoc_methods; > +struct usb_pipe_methods dwc_otg_device_isoc_fs_methods; > + > +static dwc_otg_cmd_t dwc_otg_setup_rx; > +static dwc_otg_cmd_t dwc_otg_data_rx; > +static dwc_otg_cmd_t dwc_otg_data_tx; > +static dwc_otg_cmd_t dwc_otg_data_tx_sync; > +static void dwc_otg_device_done(struct usb_xfer *, usb_error_t); > +static void dwc_otg_do_poll(struct usb_bus *); > +static void dwc_otg_standard_done(struct usb_xfer *); > +static void dwc_otg_root_intr(struct dwc_otg_softc *sc); > + > +/* > + * Here is a configuration that the chip supports. > + */ > +static const struct usb_hw_ep_profile dwc_otg_ep_profile[1] = { > + > + [0] = { > + .max_in_frame_size = 64,/* fixed */ > + .max_out_frame_size = 64, /* fixed */ > + .is_simplex = 1, > + .support_control = 1, > + } > +}; > + > +static void > +dwc_otg_get_hw_ep_profile(struct usb_device *udev, > + const struct usb_hw_ep_profile **ppf, uint8_t ep_addr) > +{ > + struct dwc_otg_softc *sc; > + > + sc = DWC_OTG_BUS2SC(udev->bus); > + > + if (ep_addr < sc->sc_dev_ep_max) > + *ppf = &sc->sc_hw_ep_profile[ep_addr].usb; > + else > + *ppf = NULL; > +} > + > +static int > +dwc_otg_init_fifo(struct dwc_otg_softc *sc) > +{ > + struct dwc_otg_profile *pf; > + uint32_t fifo_size; > + uint32_t fifo_regs; > + uint32_t tx_start; > + uint8_t x; > + > + fifo_size = sc->sc_fifo_size; > + > + fifo_regs = 4 * (sc->sc_dev_ep_max + sc->sc_dev_in_ep_max); > + > + if (fifo_size >= fifo_regs) > + fifo_size -= fifo_regs; > + else > + fifo_size = 0; > + > + /* split equally for IN and OUT */ > + fifo_size /= 2; > + > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GRXFSIZ, fifo_size / 4); > + > + /* align to 4-bytes */ > + fifo_size &= ~3; > + > + tx_start = fifo_size; > + > + if (fifo_size < 0x40) { > + DPRINTFN(-1, "Not enough data space for EP0 FIFO. > \n"); > + USB_BUS_UNLOCK(&sc->sc_bus); > + return (EINVAL); > + } > + > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GNPTXFSIZ, (0x10 << 16) | > (tx_start / 4)); > + fifo_size -= 0x40; > + tx_start += 0x40; > + > + /* setup control endpoint profile */ > + sc->sc_hw_ep_profile[0].usb = dwc_otg_ep_profile[0]; > + > + for (x = 1; x != sc->sc_dev_ep_max; x++) { > + > + pf = sc->sc_hw_ep_profile + x; > + > + pf->usb.max_out_frame_size = 1024 * 3; > + pf->usb.is_simplex = 0; /* assume duplex */ > + pf->usb.support_bulk = 1; > + pf->usb.support_interrupt = 1; > + pf->usb.support_isochronous = 1; > + pf->usb.support_out = 1; > + > + if (x < sc->sc_dev_in_ep_max) { > + uint32_t limit; > + > + limit = (x == 1) ? DWC_OTG_MAX_TXN : > + (DWC_OTG_MAX_TXN / 2); > + > + if (fifo_size >= limit) { > + DWC_OTG_WRITE_4(sc, > DWC_OTG_REG_DIEPTXF(x), > + ((limit / 4) << 16) | > + (tx_start / 4)); > + tx_start += limit; > + fifo_size -= limit; > + pf->usb.max_in_frame_size = 0x200; > + pf->usb.support_in = 1; > + pf->max_buffer = limit; > + > + } else if (fifo_size >= 0x80) { > + DWC_OTG_WRITE_4(sc, > DWC_OTG_REG_DIEPTXF(x), > + ((0x80 / 4) << 16) | (tx_start / > 4)); > + tx_start += 0x80; > + fifo_size -= 0x80; > + pf->usb.max_in_frame_size = 0x40; > + pf->usb.support_in = 1; > + > + } else { > + pf->usb.is_simplex = 1; > + DWC_OTG_WRITE_4(sc, > DWC_OTG_REG_DIEPTXF(x), > + (0x0 << 16) | (tx_start / 4)); > + } > + } else { > + pf->usb.is_simplex = 1; > + } > + > + DPRINTF("FIFO%d = IN:%d / OUT:%d\n", x, > + pf->usb.max_in_frame_size, > + pf->usb.max_out_frame_size); > + } > + > + /* reset RX FIFO */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GRSTCTL, > + DWC_OTG_MSK_GRSTCTL_RXFFLUSH); > + > + /* reset all TX FIFOs */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GRSTCTL, > + DWC_OTG_MSK_GRSTCTL_TXFIFO(0x10) | > + DWC_OTG_MSK_GRSTCTL_TXFFLUSH); > + > + return (0); > +} > + > +static void > +dwc_otg_clocks_on(struct dwc_otg_softc *sc) > +{ > + if (sc->sc_flags.clocks_off && > + sc->sc_flags.port_powered) { > + > + DPRINTFN(5, "\n"); > + > + /* TODO - platform specific */ > + > + sc->sc_flags.clocks_off = 0; > + } > +} > + > +static void > +dwc_otg_clocks_off(struct dwc_otg_softc *sc) > +{ > + if (!sc->sc_flags.clocks_off) { > + > + DPRINTFN(5, "\n"); > + > + /* TODO - platform specific */ > + > + sc->sc_flags.clocks_off = 1; > + } > +} > + > +static void > +dwc_otg_pull_up(struct dwc_otg_softc *sc) > +{ > + uint32_t temp; > + > + /* pullup D+, if possible */ > + > + if (!sc->sc_flags.d_pulled_up && > + sc->sc_flags.port_powered) { > + sc->sc_flags.d_pulled_up = 1; > + > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DCTL); > + temp &= ~DWC_OTG_MSK_DCTL_SOFT_DISC; > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DCTL, temp); > + } > +} > + > +static void > +dwc_otg_pull_down(struct dwc_otg_softc *sc) > +{ > + uint32_t temp; > + > + /* pulldown D+, if possible */ > + > + if (sc->sc_flags.d_pulled_up) { > + sc->sc_flags.d_pulled_up = 0; > + > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DCTL); > + temp |= DWC_OTG_MSK_DCTL_SOFT_DISC; > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DCTL, temp); > + } > +} > + > +static void > +dwc_otg_resume_irq(struct dwc_otg_softc *sc) > +{ > + if (sc->sc_flags.status_suspend) { > + /* update status bits */ > + sc->sc_flags.status_suspend = 0; > + sc->sc_flags.change_suspend = 1; > + > + /* > + * Disable resume interrupt and enable suspend > + * interrupt: > + */ > + sc->sc_irq_mask &= ~DWC_OTG_MSK_GINT_WKUPINT; > + sc->sc_irq_mask |= DWC_OTG_MSK_GINT_USB_SUSPEND; > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GINTMSK, > sc->sc_irq_mask); + > + /* complete root HUB interrupt endpoint */ > + dwc_otg_root_intr(sc); > + } > +} > + > +static void > +dwc_otg_wakeup_peer(struct dwc_otg_softc *sc) > +{ > + uint32_t temp; > + > + if (!sc->sc_flags.status_suspend) > + return; > + > + DPRINTFN(5, "Remote wakeup\n"); > + > + /* enable remote wakeup signalling */ > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DCTL); > + temp |= DWC_OTG_MSK_DCTL_REMOTE_WAKEUP; > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DCTL, temp); > + > + /* Wait 8ms for remote wakeup to complete. */ > + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); > + > + temp &= ~DWC_OTG_MSK_DCTL_REMOTE_WAKEUP; > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DCTL, temp); > + > + /* need to fake resume IRQ */ > + dwc_otg_resume_irq(sc); > +} > + > +static void > +dwc_otg_set_address(struct dwc_otg_softc *sc, uint8_t addr) > +{ > + uint32_t temp; > + > + DPRINTFN(5, "addr=%d\n", addr); > + > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DCFG); > + temp &= ~DWC_OTG_MSK_DCFG_SET_DEV_ADDR(0x7F); > + temp |= DWC_OTG_MSK_DCFG_SET_DEV_ADDR(addr); > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DCFG, temp); > +} > + > +static void > +dwc_otg_common_rx_ack(struct dwc_otg_softc *sc) > +{ > + DPRINTFN(5, "RX status clear\n"); > + > + /* enable RX FIFO level interrupt */ > + sc->sc_irq_mask |= DWC_OTG_MSK_GINT_RXFLVL; > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GINTMSK, sc->sc_irq_mask); > + > + /* clear cached status */ > + sc->sc_last_rx_status = 0; > +} > + > +static uint8_t > +dwc_otg_setup_rx(struct dwc_otg_td *td) > +{ > + struct dwc_otg_softc *sc; > + struct usb_device_request req __aligned(4); > + uint32_t temp; > + uint16_t count; > + > + /* get pointer to softc */ > + sc = DWC_OTG_PC2SC(td->pc); > + > + /* check endpoint status */ > + > + if (sc->sc_last_rx_status == 0) > + goto not_complete; > + > + if (DWC_OTG_MSK_GRXSTS_GET_CHANNEL(sc->sc_last_rx_status) != > 0) > + goto not_complete; > + > + if ((sc->sc_last_rx_status & DWC_OTG_MSK_GRXSTS_PID) != > + DWC_OTG_MSK_GRXSTS_PID_DATA0) { > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + goto not_complete; > + } > + > + if ((sc->sc_last_rx_status & DWC_OTG_MSK_GRXSTS_PACKET_STS) ! > = > + DWC_OTG_MSK_GRXSTS_DEV_STP_DATA) { > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + goto not_complete; > + } > + > + DPRINTFN(5, "GRXSTSR=0x%08x\n", sc->sc_last_rx_status); > + > + /* clear did stall */ > + td->did_stall = 0; > + > + /* get the packet byte count */ > + count = DWC_OTG_MSK_GRXSTS_GET_BYTE_CNT > (sc->sc_last_rx_status); + > + /* verify data length */ > + if (count != td->remainder) { > + DPRINTFN(0, "Invalid SETUP packet " > + "length, %d bytes\n", count); > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + goto not_complete; > + } > + if (count != sizeof(req)) { > + DPRINTFN(0, "Unsupported SETUP packet " > + "length, %d bytes\n", count); > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + goto not_complete; > + } > + > + /* copy in control request */ > + memcpy(&req, sc->sc_rx_bounce_buffer, sizeof(req)); > + > + /* copy data into real buffer */ > + usbd_copy_in(td->pc, 0, &req, sizeof(req)); > + > + td->offset = sizeof(req); > + td->remainder = 0; > + > + /* sneak peek the set address */ > + if ((req.bmRequestType == UT_WRITE_DEVICE) && > + (req.bRequest == UR_SET_ADDRESS)) { > + /* must write address before ZLP */ > + dwc_otg_set_address(sc, req.wValue[0] & 0x7F); > + } > + > + /* don't send any data by default */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DIEPTSIZ(0), > + DWC_OTG_MSK_DXEPTSIZ_SET_NPKT(0) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NBYTES(0)); > + > + temp = sc->sc_in_ctl[0]; > + > + /* enable IN endpoint */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DIEPCTL(0), > + temp | DWC_OTG_MSK_DIEPCTL_ENABLE); > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DIEPCTL(0), > + temp | DWC_OTG_MSK_DIEPCTL_SET_NAK); > + > + /* reset IN endpoint buffer */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_GRSTCTL, > + DWC_OTG_MSK_GRSTCTL_TXFIFO(0) | > + DWC_OTG_MSK_GRSTCTL_TXFFLUSH); > + > + /* acknowledge RX status */ > + dwc_otg_common_rx_ack(sc); > + return (0); /* complete */ > + > +not_complete: > + /* abort any ongoing transfer, before enabling again */ > + > + temp = sc->sc_out_ctl[0]; > + > + temp |= DWC_OTG_MSK_DOEPCTL_ENABLE | > + DWC_OTG_MSK_DOEPCTL_SET_NAK; > + > + /* enable OUT endpoint */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DOEPCTL(0), temp); > + > + if (!td->did_stall) { > + td->did_stall = 1; > + > + DPRINTFN(5, "stalling IN and OUT direction\n"); > + > + /* set stall after enabling endpoint */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DOEPCTL(0), > + temp | DWC_OTG_MSK_DOEPCTL_STALL); > + > + temp = sc->sc_in_ctl[0]; > + > + /* set stall assuming endpoint is enabled */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DIEPCTL(0), > + temp | DWC_OTG_MSK_DIEPCTL_STALL); > + } > + > + /* setup number of buffers to receive */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DOEPTSIZ(0), > + DWC_OTG_MSK_DXEPTSIZ_SET_MULTI(3) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NPKT(1) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NBYTES(sizeof(req))); > + > + return (1); /* not complete */ > +} > + > +static uint8_t > +dwc_otg_data_rx(struct dwc_otg_td *td) > +{ > + struct dwc_otg_softc *sc; > + uint32_t temp; > + uint16_t count; > + uint8_t got_short; > + > + got_short = 0; > + > + /* get pointer to softc */ > + sc = DWC_OTG_PC2SC(td->pc); > + > + /* check endpoint status */ > + if (sc->sc_last_rx_status == 0) > + goto not_complete; > + > + if (DWC_OTG_MSK_GRXSTS_GET_CHANNEL(sc->sc_last_rx_status) != > td->ep_no) > + goto not_complete; > + > + /* check for SETUP packet */ > + if ((sc->sc_last_rx_status & DWC_OTG_MSK_GRXSTS_PACKET_STS) > == > + DWC_OTG_MSK_GRXSTS_DEV_STP_DATA) { > + if (td->remainder == 0) { > + /* > + * We are actually complete and have > + * received the next SETUP > + */ > + DPRINTFN(5, "faking complete\n"); > + return (0); /* complete */ > + } > + /* > + * USB Host Aborted the transfer. > + */ > + td->error = 1; > + return (0); /* complete */ > + } > + > + if ((sc->sc_last_rx_status & DWC_OTG_MSK_GRXSTS_PACKET_STS) ! > = > + DWC_OTG_MSK_GRXSTS_DEV_OUT_DATA) { > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + goto not_complete; > + } > + > + /* get the packet byte count */ > + count = DWC_OTG_MSK_GRXSTS_GET_BYTE_CNT > (sc->sc_last_rx_status); + > + /* verify the packet byte count */ > + if (count != td->max_packet_size) { > + if (count < td->max_packet_size) { > + /* we have a short packet */ > + td->short_pkt = 1; > + got_short = 1; > + } else { > + /* invalid USB packet */ > + td->error = 1; > + > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + return (0); /* we are complete */ > + } > + } > + /* verify the packet byte count */ > + if (count > td->remainder) { > + /* invalid USB packet */ > + td->error = 1; > + > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + return (0); /* we are complete */ > + } > + > + usbd_copy_in(td->pc, td->offset, sc->sc_rx_bounce_buffer, > count); > + td->remainder -= count; > + td->offset += count; > + > + /* release FIFO */ > + dwc_otg_common_rx_ack(sc); > + > + /* check if we are complete */ > + if ((td->remainder == 0) || got_short) { > + if (td->short_pkt) { > + /* we are complete */ > + return (0); > + } > + /* else need to receive a zero length packet */ > + } > + > +not_complete: > + > + temp = sc->sc_out_ctl[td->ep_no]; > + > + temp |= DWC_OTG_MSK_DOEPCTL_ENABLE | > + DWC_OTG_MSK_DOEPCTL_CLR_NAK; > + > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DOEPCTL(td->ep_no), temp); > + > + /* enable SETUP and transfer complete interrupt */ > + if (td->ep_no == 0) { > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DOEPTSIZ(0), > + DWC_OTG_MSK_DXEPTSIZ_SET_NPKT(1) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NBYTES > (td->max_packet_size)); > + } else { > + /* allow reception of multiple packets */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DOEPTSIZ(td->ep_no), > + DWC_OTG_MSK_DXEPTSIZ_SET_MULTI(1) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NPKT(4) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NBYTES(4 * > + ((td->max_packet_size + 3) & ~3))); > + } > + return (1); /* not complete */ > +} > + > +static uint8_t > +dwc_otg_data_tx(struct dwc_otg_td *td) > +{ > + struct dwc_otg_softc *sc; > + uint32_t max_buffer; > + uint32_t count; > + uint32_t fifo_left; > + uint32_t mpkt; > + uint32_t temp; > + uint8_t to; > + > + to = 3; /* don't loop > forever! */ + > + /* get pointer to softc */ > + sc = DWC_OTG_PC2SC(td->pc); > + > + max_buffer = sc->sc_hw_ep_profile[td->ep_no].max_buffer; > + > +repeat: > + /* check for for endpoint 0 data */ > + > + temp = sc->sc_last_rx_status; > + > + if ((td->ep_no == 0) && (temp != 0) && > + (DWC_OTG_MSK_GRXSTS_GET_CHANNEL(temp) == 0)) { > + > + if ((temp & DWC_OTG_MSK_GRXSTS_PACKET_STS) != > + DWC_OTG_MSK_GRXSTS_DEV_STP_DATA) { > + > + /* dump data - wrong direction */ > + dwc_otg_common_rx_ack(sc); > + } else { > + /* > + * The current transfer was cancelled > + * by the USB Host: > + */ > + td->error = 1; > + return (0); /* complete */ > + } > + } > + > + /* fill in more TX data, if possible */ > + if (td->tx_bytes != 0) { > + > + uint16_t cpkt; > + > + /* check if packets have been transferred */ > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DIEPTSIZ > (td->ep_no)); + > + /* get current packet number */ > + cpkt = DWC_OTG_MSK_DXEPTSIZ_GET_NPKT(temp); > + > + if (cpkt >= td->npkt) { > + fifo_left = 0; > + } else { > + if (max_buffer != 0) { > + fifo_left = (td->npkt - cpkt) * > + td->max_packet_size; > + > + if (fifo_left > max_buffer) > + fifo_left = max_buffer; > + } else { > + fifo_left = td->max_packet_size; > + } > + } > + > + count = td->tx_bytes; > + if (count > fifo_left) > + count = fifo_left; > + > + if (count != 0) { > + > + /* clear topmost word before copy */ > + sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0; > + > + /* copy out data */ > + usbd_copy_out(td->pc, td->offset, > + sc->sc_tx_bounce_buffer, count); > + > + /* transfer data into FIFO */ > + bus_space_write_region_4(sc->sc_io_tag, > sc->sc_io_hdl, > + DWC_OTG_REG_DFIFO(td->ep_no), > + sc->sc_tx_bounce_buffer, (count + 3) / > 4); + > + td->tx_bytes -= count; > + td->remainder -= count; > + td->offset += count; > + td->npkt = cpkt; > + } > + if (td->tx_bytes != 0) > + goto not_complete; > + > + /* check remainder */ > + if (td->remainder == 0) { > + if (td->short_pkt) > + return (0); /* complete */ > + > + /* else we need to transmit a short packet */ > + } > + } > + > + /* check if no packets have been transferred */ > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DIEPTSIZ(td->ep_no)); > + > + if (DWC_OTG_MSK_DXEPTSIZ_GET_NPKT(temp) != 0) { > + > + DPRINTFN(5, "busy ep=%d npkt=%d DIEPTSIZ=0x%08x " > + "DIEPCTL=0x%08x\n", td->ep_no, > + DWC_OTG_MSK_DXEPTSIZ_GET_NPKT(temp), > + temp, DWC_OTG_READ_4(sc, DWC_OTG_REG_DIEPCTL > (td->ep_no))); + > + goto not_complete; > + } > + > + DPRINTFN(5, "rem=%u ep=%d\n", td->remainder, td->ep_no); > + > + /* try to optimise by sending more data */ > + if ((max_buffer != 0) && ((td->max_packet_size & 3) == 0)) { > + > + /* send multiple packets at the same time */ > + mpkt = max_buffer / td->max_packet_size; > + > + if (mpkt > 0x3FE) > + mpkt = 0x3FE; > + > + count = td->remainder; > + if (count > 0x7FFFFF) > + count = 0x7FFFFF - (0x7FFFFF % > td->max_packet_size); + > + td->npkt = count / td->max_packet_size; > + > + /* > + * NOTE: We could use 0x3FE instead of "mpkt" in the > + * check below to get more throughput, but then we > + * have a dependency towards non-generic chip > features > + * to disable the TX-FIFO-EMPTY interrupts on a per > + * endpoint basis. Increase the maximum buffer size > of > + * the IN endpoint to increase the performance. > + */ > + if (td->npkt > mpkt) { > + td->npkt = mpkt; > + count = td->max_packet_size * mpkt; > + } else if ((count == 0) || (count % > td->max_packet_size)) { > + /* we are transmitting a short packet */ > + td->npkt++; > + td->short_pkt = 1; > + } > + } else { > + /* send one packet at a time */ > + mpkt = 1; > + count = td->max_packet_size; > + if (td->remainder < count) { > + /* we have a short packet */ > + td->short_pkt = 1; > + count = td->remainder; > + } > + td->npkt = 1; > + } > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DIEPTSIZ(td->ep_no), > + DWC_OTG_MSK_DXEPTSIZ_SET_MULTI(1) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NPKT(td->npkt) | > + DWC_OTG_MSK_DXEPTSIZ_SET_NBYTES(count)); > + > + /* make room for buffering */ > + td->npkt += mpkt; > + > + temp = sc->sc_in_ctl[td->ep_no]; > + > + /* must enable before writing data to FIFO */ > + DWC_OTG_WRITE_4(sc, DWC_OTG_REG_DIEPCTL(td->ep_no), temp | > + DWC_OTG_MSK_DIEPCTL_ENABLE | > + DWC_OTG_MSK_DIEPCTL_CLR_NAK); > + > + td->tx_bytes = count; > + > + /* check remainder */ > + if (td->tx_bytes == 0 && > + td->remainder == 0) { > + if (td->short_pkt) > + return (0); /* complete */ > + > + /* else we need to transmit a short packet */ > + } > + > + if (--to) > + goto repeat; > + > +not_complete: > + return (1); /* not complete */ > +} > + > +static uint8_t > +dwc_otg_data_tx_sync(struct dwc_otg_td *td) > +{ > + struct dwc_otg_softc *sc; > + uint32_t temp; > + > + /* get pointer to softc */ > + sc = DWC_OTG_PC2SC(td->pc); > + > + /* > + * If all packets are transferred we are complete: > + */ > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_DIEPTSIZ(td->ep_no)); > + > + /* check that all packets have been transferred */ > + if (DWC_OTG_MSK_DXEPTSIZ_GET_NPKT(temp) != 0) { > + DPRINTFN(5, "busy ep=%d\n", td->ep_no); > + goto not_complete; > + } > + return (0); > + > +not_complete: > + > + /* we only want to know if there is a SETUP packet or free > IN packet */ + > + temp = sc->sc_last_rx_status; > + > + if ((td->ep_no == 0) && (temp != 0) && > + (DWC_OTG_MSK_GRXSTS_GET_CHANNEL(temp) == 0)) { > + > + if ((temp & DWC_OTG_MSK_GRXSTS_PACKET_STS) == > + DWC_OTG_MSK_GRXSTS_DEV_STP_DATA) { > + DPRINTFN(5, "faking complete\n"); > + /* > + * Race condition: We are complete! > + */ > + return (0); > + } else { > + /* dump data - wrong direction */ > + dwc_otg_common_rx_ack(sc); > + } > + } > + return (1); /* not complete */ > +} > + > +static uint8_t > +dwc_otg_xfer_do_fifo(struct usb_xfer *xfer) > +{ > + struct dwc_otg_td *td; > + > + DPRINTFN(9, "\n"); > + > + td = xfer->td_transfer_cache; > + while (1) { > + if ((td->func) (td)) { > + /* operation in progress */ > + break; > + } > + if (((void *)td) == xfer->td_transfer_last) { > + goto done; > + } > + if (td->error) { > + goto done; > + } else if (td->remainder > 0) { > + /* > + * We had a short transfer. If there is no > alternate > + * next, stop processing ! > + */ > + if (!td->alt_next) > + goto done; > + } > + > + /* > + * Fetch the next transfer descriptor and transfer > + * some flags to the next transfer descriptor > + */ > + td = td->obj_next; > + xfer->td_transfer_cache = td; > + } > + return (1); /* not complete */ > + > +done: > + /* compute all actual lengths */ > + > + dwc_otg_standard_done(xfer); > + return (0); /* complete */ > +} > + > +static void > +dwc_otg_interrupt_poll(struct dwc_otg_softc *sc) > +{ > + struct usb_xfer *xfer; > + uint32_t temp; > + uint8_t got_rx_status; > + > +repeat: > + if (sc->sc_last_rx_status == 0) { > + > + temp = DWC_OTG_READ_4(sc, DWC_OTG_REG_GINTSTS); > + if (temp & DWC_OTG_MSK_GINT_RXFLVL) { > + /* pop current status */ > + sc->sc_last_rx_status = > + DWC_OTG_READ_4(sc, DWC_OTG_REG_GRXSTSP); > + } > + > + if (sc->sc_last_rx_status != 0) { > + > + uint32_t temp; > + uint8_t ep_no; > + > + temp = DWC_OTG_MSK_GRXSTS_GET_BYTE_CNT( > + sc->sc_last_rx_status); > + ep_no = DWC_OTG_MSK_GRXSTS_GET_CHANNEL( > + sc->sc_last_rx_status); > + > + /* receive data, if any */ > + if (temp != 0) { > + DPRINTF("Reading %d bytes from ep %d > \n", temp, ep_no); > + bus_space_read_region_4 > (sc->sc_io_tag, sc->sc_io_hdl, > + DWC_OTG_REG_DFIFO(ep_no), > + sc->sc_rx_bounce_buffer, (temp + > 3) / 4); > + } > + > + temp = sc->sc_last_rx_status & > + DWC_OTG_MSK_GRXSTS_PACKET_STS; > + > + /* non-data messages we simply skip */ > + if (temp != DWC_OTG_MSK_GRXSTS_DEV_STP_DATA > && > + temp != DWC_OTG_MSK_GRXSTS_DEV_OUT_DATA) > { > + dwc_otg_common_rx_ack(sc); > + goto repeat; > + } > + > + /* check if we should dump the data */ > + if (!(sc->sc_active_out_ep & (1U << ep_no))) > { > + dwc_otg_common_rx_ack(sc); > + goto repeat; > + } > + > + got_rx_status = 1; > + > + DPRINTFN(5, "RX status = 0x%08x: ch=%d pid=% > d bytes=%d sts=%d\n", > + sc->sc_last_rx_status, ep_no, > + (sc->sc_last_rx_status >> 15) & 3, > + DWC_OTG_MSK_GRXSTS_GET_BYTE_CNT > (sc->sc_last_rx_status), > + (sc->sc_last_rx_status >> 17) & 15); > + } else { > + got_rx_status = 0; > + } > + } else { > + uint8_t ep_no; > + > + ep_no = DWC_OTG_MSK_GRXSTS_GET_CHANNEL( > + sc->sc_last_rx_status); > + > + /* check if we should dump the data */ > + if (!(sc->sc_active_out_ep & (1U << ep_no))) { > + dwc_otg_common_rx_ack(sc); > + goto repeat; > + } > + > + got_rx_status = 1; > + } > + > + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { > + if (!dwc_otg_xfer_do_fifo(xfer)) { > + /* queue has been modified */ > + goto repeat; > + } > + } > + > + if (got_rx_status) { > + if (sc->sc_last_rx_status == 0) > + goto repeat; > + > > *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** -- Aleksandr Rybalko