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