svn commit: r230424 - head/sys/dev/usb/controller
Aleksandr Rybalko
ray at ddteam.net
Sat Jan 21 13:45:23 UTC 2012
On Sat, 21 Jan 2012 13:31:38 +0000 (UTC)
Hans Petter Selasky <hselasky at FreeBSD.org> 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 <sys/cdefs.h>
> +__FBSDID("$FreeBSD$");
> +
> +#include <sys/stdint.h>
> +#include <sys/stddef.h>
> +#include <sys/param.h>
> +#include <sys/queue.h>
> +#include <sys/types.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/bus.h>
> +#include <sys/module.h>
> +#include <sys/lock.h>
> +#include <sys/mutex.h>
> +#include <sys/condvar.h>
> +#include <sys/sysctl.h>
> +#include <sys/sx.h>
> +#include <sys/unistd.h>
> +#include <sys/callout.h>
> +#include <sys/malloc.h>
> +#include <sys/priv.h>
> +
> +#include <dev/usb/usb.h>
> +#include <dev/usb/usbdi.h>
> +
> +#define USB_DEBUG_VAR dwc_otg_debug
> +
> +#include <dev/usb/usb_core.h>
> +#include <dev/usb/usb_debug.h>
> +#include <dev/usb/usb_busdma.h>
> +#include <dev/usb/usb_process.h>
> +#include <dev/usb/usb_transfer.h>
> +#include <dev/usb/usb_device.h>
> +#include <dev/usb/usb_hub.h>
> +#include <dev/usb/usb_util.h>
> +
> +#include <dev/usb/usb_controller.h>
> +#include <dev/usb/usb_bus.h>
> +
> +#include <dev/usb/controller/dwc_otg.h>
> +
> +#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 <ray at ddteam.net>
More information about the svn-src-head
mailing list