**SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip

jamey_at_crl.dec.com
Date: 08/02/05

  • Next message: Sean Bruno: "Re: Testing RC kernels [KORG]"
    To: linux-kernel@vger.kernel.org
    Date:	Tue,  2 Aug 2005 15:20:47 -0400 (EDT)
    
    

    This patch adds USB gadget support for the USB peripheral controller
    on the MQ11xx graphics chip.

    Signed-off-by: Jamey Hicks <jamey@handhelds.org>
    Signed-off-by: Andrew Zabolotny <anpaza@mail.ru>

    ---
    commit 9b2407962e0786705efeed755ab1d399deba8685
    tree 7c9e5187687bec3112af0f83e9e3831b18c41fa5
    parent d6631d9e21df12578ae5e7a2878b7e7132cfa28c
    author <jamey@lspace.crl.dec.com> Tue, 02 Aug 2005 11:28:38 -0400
    committer <jamey@lspace.crl.dec.com> Tue, 02 Aug 2005 11:28:38 -0400
     drivers/usb/gadget/Kconfig      |   15 
     drivers/usb/gadget/Makefile     |    1 
     drivers/usb/gadget/mq11xx_udc.c | 1710 +++++++++++++++++++++++++++++++++++++++
     drivers/usb/gadget/mq11xx_udc.h |  115 +++
     4 files changed, 1841 insertions(+), 0 deletions(-)
    diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
    --- a/drivers/usb/gadget/Kconfig
    +++ b/drivers/usb/gadget/Kconfig
    @@ -142,6 +142,21 @@ config USB_GOKU
     	select USB_GADGET_SELECTED
     
     
    +config USB_GADGET_MQ11XX
    +	boolean "MediaQ 11xx USB Device Controller"
    +	depends on PLATFORM_MQ11XX
    +	help
    +	   Enable this if you want to use MediaQ chip's integrated
    +	   USB device controller.
    +
    +	   It has three fixed-function endpoints, as well as endpoint
    +	   zero (for control transfers).
    +
    +config USB_MQ11XX
    +	tristate
    +	depends on USB_GADGET_MQ11XX
    +	default USB_GADGET
    +
     config USB_GADGET_LH7A40X
     	boolean "LH7A40X"
     	depends on ARCH_LH7A40X
    diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
    --- a/drivers/usb/gadget/Makefile
    +++ b/drivers/usb/gadget/Makefile
    @@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX)	+= pxa2xx_udc.o
     obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
     obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
     obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
    +obj-$(CONFIG_USB_MQ11XX)	+= mq11xx_udc.o
     
     #
     # USB gadget drivers
    diff --git a/drivers/usb/gadget/mq11xx_udc.c b/drivers/usb/gadget/mq11xx_udc.c
    new file mode 100644
    --- /dev/null
    +++ b/drivers/usb/gadget/mq11xx_udc.c
    @@ -0,0 +1,1710 @@
    +/*
    + * MediaQ 11xx USB Device Controller driver
    + * Goku-S UDC driver used as a template.
    + *
    + * Copyright (C) 2004 Andrew Zabolotny <zap@homelink.ru>
    + *
    + * This file is subject to the terms and conditions of the GNU General Public
    + * License.  See the file COPYING in the main directory of this archive for
    + * more details.
    + */
    +
    +#include <linux/config.h>
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/delay.h>
    +#include <linux/ioport.h>
    +#include <linux/sched.h>
    +#include <linux/slab.h>
    +#include <linux/smp_lock.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/timer.h>
    +#include <linux/list.h>
    +#include <linux/interrupt.h>
    +#include <linux/proc_fs.h>
    +#include <linux/device.h>
    +#include <linux/usb_ch9.h>
    +#include <linux/usb_gadget.h>
    +
    +#include <asm/unaligned.h>
    +
    +#define __MQ11XX_INTERNAL
    +#include "mq11xx_udc.h"
    +
    +/*
    + * This device has ep0 and three semi-configurable interrupt/bulk/isochronous
    + * endpoints.
    + *
    + *  - Endpoints 1 and 2 are IN, 3 is OUT
    + *  - Endpoint 1 is for interrupt transfers; endpoints 2 and 3 can be
    + *    configured to either bulk or isochronous mode.
    + *  - Endpoint 0 and 1 use FIFO, endpoints 2 and 3 use DMA transfers.
    + *
    + * Note that when using debugging output, the delay between the IN token
    + * and the actual time of filling the FIFO can exceed 18 bit times
    + * (as stated in section 7.1.19.1 of the USB specs), and the transfer
    + * times out. But when the host retries the transfer, the data is already
    + * in the FIFO so it gets transferred correctly, except that you can see
    + * "usb_control/bulk_msg: timeout" messages on the host side. Also on bulk
    + * endpoints you can eventually easily get timeouts/misbehaviour from time
    + * to time, especially if you're using small rx/tx buffers.
    + *
    + * NOTE: Isochronous mode is not fully implemented yet.
    + *
    + * NOTE: Due to crappy chip design, there are still some hardware race
    + * conditions and such. I've tried to make these dangerous time periods
    + * as short as possible, but nothing guaranteed. I've marked these places
    + * with [CRAP] marks; if you have a better solution, go ahead and fix it.
    + */
    +
    +/* Enable debug logs in general (The Big Red Switch) */
    +//#define USB_DEBUG
    +/* Enable debugging of packets */
    +//#define USB_DEBUG_TRACE
    +/* Enable debugging of packet contents */
    +//#define USB_DEBUG_DATA
    +/* Log time of every debug event */
    +//#define USB_DEBUG_TIME
    +
    +#ifdef USB_DEBUG_TIME
    +#  ifdef CONFIG_ARCH_PXA
    +#    include <asm/arch/hardware.h>
    +     /* Approximative division by 3.6864 to get microseconds */
    +#    define USB_DEBUG_TIMER ((OSCR*139)/512)
    +#  else
    +#    define USB_DEBUG_TIMER jiffies
    +#  endif
    +#endif
    +
    +#ifdef USB_DEBUG
    +#  ifdef USB_DEBUG_TIMER
    +#    define debug(s, args...) printk (KERN_INFO "(%7u) %s: " s, USB_DEBUG_TIMER, __FUNCTION__, ##args)
    +#  else
    +#    define debug(s, args...) printk (KERN_INFO "%s: " s, __FUNCTION__, ##args)
    +#  endif
    +#else
    +#  define debug(s, args...)
    +#endif
    +#define info(s, args...) printk (KERN_INFO "%s: " s, driver_name, ##args)
    +
    +/* Endpoint 0 and 1 FIFO depth in bytes */
    +#define MQ_EP01_MAXPACKET	(MQ_UDC_FIFO_DEPTH * sizeof (u32))
    +/* Endpoint 2 and 3 packet size */
    +#define MQ_EP23_MAXPACKET	64
    +
    +static const char driver_name [] = "mq11xx_udc";
    +
    +MODULE_DESCRIPTION("MediaQ 11xx USB Device Controller");
    +MODULE_AUTHOR("Andrew Zabolotny <zap@homelink.ru>");
    +MODULE_LICENSE("GPL");
    +
    +/* The size for one buffer in MediaQ internal RAM */
    +static unsigned rxbs = 4096;
    +MODULE_PARM (rxbs, "i");
    +MODULE_PARM_DESC (rxbs, "DMA receive buffer size (two buffers)");
    +
    +static unsigned txbs = 4096;
    +MODULE_PARM (txbs, "i");
    +MODULE_PARM_DESC (txbs, "DMA transmit buffer size (two buffers)");
    +
    +/* Static variables are EVIL :) but for now anyways the UDC architecture
    + * allows only one UDC controller at a time.
    + */
    +struct mq11xx_udc_mach_info *mach_info;
    +
    +static int mq_set_halt(struct usb_ep *, int);
    +static void mq_ep_finish(struct mq_ep *, int status);
    +static void mq_done_request (struct mq_ep *ep, struct mq_request *req,
    +			     int status);
    +
    +#ifdef USB_DEBUG_DATA
    +static void debug_dump (u8 *data, int count, int base)
    +{
    +	char line [80];
    +	u32 i, j, offs = 0;
    +	while (count > 0) {
    +		j = sprintf (line, "%08x | ", base + offs);
    +		for (i = 0; i < 16; i++)
    +			if (i < count)
    +				j += sprintf (line + j, "%02x ", data [offs + i]);
    +			else
    +				j += sprintf (line + j, "   ");
    +		j += sprintf (line + j, "| ");
    +		for (i = 0; i < 16; i++) {
    +			u8 c = data [offs + i];
    +			line [j++] = (i >= count) ? ' ' :
    +				((c < 32 || c > 126) ? '.' : c);
    +		}
    +		line [j] = 0;
    +		printk ("%s\n", line);
    +		offs += 16;
    +		count -= 16;
    +	}
    +}
    +#else
    +#define debug_dump(x,y,z)
    +#endif
    +
    +static int mq_ep_enable (struct usb_ep *_ep,
    +			    const struct usb_endpoint_descriptor *desc)
    +{
    +	struct mqudc *mqdev;
    +	struct mq_ep *ep;
    +	u32 max, dmactl;
    +	unsigned long flags;
    +	volatile struct mediaq11xx_regs *regs;
    +
    +	debug ("%s type:%d addr:%x maxpkt:%d\n", _ep->name,
    +	       desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK,
    +	       desc->bEndpointAddress,
    +	       desc->wMaxPacketSize);
    +
    +	ep = container_of(_ep, struct mq_ep, ep);
    +	if (!_ep || !desc || ep->desc ||
    +	    desc->bDescriptorType != USB_DT_ENDPOINT ||
    +	    (desc->bEndpointAddress & 0x0f) > 3)
    +		return -EINVAL;
    +	mqdev = ep->mqdev;
    +	if (ep == &mqdev->ep[0])
    +		return -EINVAL;
    +	if (!mqdev->driver)
    +		return -ESHUTDOWN;
    +	if (ep->num != (desc->bEndpointAddress & 0x0f))
    +		return -EINVAL;
    +
    +	regs = mqdev->base->regs;
    +
    +	/* Endpoint 1 cannot do anything but interrupt transfers
    +	   while endpoints 2 and 3 can do only bulk/isochronous transfers. */
    +	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
    +	case USB_ENDPOINT_XFER_INT:
    +		if (ep->num != 1)
    +			return -EINVAL;
    +		break;
    +	case USB_ENDPOINT_XFER_BULK:
    +	case USB_ENDPOINT_XFER_ISOC:
    +		if (ep->num > 1) {
    +			max = (ep->num == 2) ? MQ_UDC_EP2_ISOCHRONOUS :
    +				MQ_UDC_EP3_ISOCHRONOUS;
    +			if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
    +				regs->UDC.control &= ~max;
    +				ep->iso = 0;
    +				ep->regs [0] &= ~MQ_UDC_FORCE_DATA0;
    +			} else {
    +				regs->UDC.control |= max;
    +				ep->iso = 1;
    +				ep->regs [0] |= MQ_UDC_FORCE_DATA0;
    +			}
    +			break;
    +		}
    +	default:
    +		return -EINVAL;
    +	}
    +
    +	if (ep->regs [0] & MQ_UDC_EP_ENABLE)
    +		debug ("%s: already enabled\n", ep->ep.name);
    +
    +	/* MediaQ UDC understands only 16 byte packets for EP0 and EP1,
    +         * and 64-byte packets for EP2 & EP3.
    +	 */
    +	max = le16_to_cpu (get_unaligned (&desc->wMaxPacketSize));
    +	if (max > ((ep->num < 2) ? MQ_EP01_MAXPACKET : MQ_EP23_MAXPACKET))
    +		return -EINVAL;
    +
    +	/* Check pipe direction - 1 & 2 are IN, 3 is OUT */
    +	if (desc->bEndpointAddress & USB_DIR_IN) {
    +		if (ep->num > 2)
    +			return -EINVAL;
    +	} else
    +		if (ep->num < 3)
    +			return -EINVAL;
    +
    +	spin_lock_irqsave (&ep->mqdev->lock, flags);
    +
    +	ep->regs [0] |= MQ_UDC_EP_ENABLE;
    +
    +	/* ep2 and ep3 can do double buffering and/or dma */
    +	if (ep->dmaregs) {
    +		ep->active_buffer = 0;
    +		/* DMA burst transfer length - 24 bytes */
    +//		regs->MIU.miu_1 = (regs->MIU.miu_1 & ~MQ_MIU_UDC_BURST_MASK) |
    +//			MQ_MIU_UDC_BURST_COUNT_6;
    +		/* For isochronous mode: burst threshold for RX is 6 free
    +		 * locations in the RX FIFO; for TX is when 6 locations in
    +		 * FIFO are received (6 locations == 24 bytes).
    +		 */
    +//		ep->regs [0] = (ep->regs [0] & ~MQ_UDC_FIFO_THRESHOLD_MASK) |
    +//			MQ_UDC_FIFO_THRESHOLD (6);
    +		/* Switch between 2 DMA buffers on RX/TX */
    +		dmactl = MQ_UDC_DMA_ENABLE | MQ_UDC_DMA_PINGPONG | \
    +			MQ_UDC_DMA_NUMBUFF (2);
    +		if (ep->num == 3)
    +			dmactl |= MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF2_OWNER;
    +		ep->dmaregs [0] = dmactl;
    +	}
    +
    +	mq_set_halt (_ep, 0);
    +	ep->ep.maxpacket = max;
    +	ep->stopped = 1;
    +	ep->desc = desc;
    +	spin_unlock_irqrestore (&ep->mqdev->lock, flags);
    +
    +	return 0;
    +}
    +
    +static void mq_ep_clear_fifo (struct mq_ep *ep)
    +{
    +	if (ep->num < 2) {
    +		ep->quick_buff_bytes = 0;
    +		ep->regs [0] |= MQ_UDC_CLEAR_FIFO;
    +		ep->regs [0] &= ~MQ_UDC_CLEAR_FIFO;
    +		if (ep->num == 0) {
    +			ep->regs [2] |= MQ_UDC_CLEAR_FIFO;
    +			ep->regs [2] &= ~MQ_UDC_CLEAR_FIFO;
    +		}
    +	}
    +}
    +
    +static void mq_ep_reset (struct mq_ep *ep)
    +{
    +	/* Disable the endpoint (if not ep0) or clear the FIFO (if ep0) */
    +	ep->regs [0] = MQ_UDC_CLEAR_FIFO;
    +	ep->regs [0] = 0;
    +	if (ep->num == 0) {
    +		ep->regs [2] = MQ_UDC_CLEAR_FIFO;
    +		ep->regs [2] = 0;
    +	}
    +	if (ep->dmaregs)
    +		ep->dmaregs [0] = 0;
    +
    +	ep->ep.maxpacket = (ep->num < 2) ? MQ_EP01_MAXPACKET : MQ_EP23_MAXPACKET;
    +	ep->desc = 0;
    +	ep->stopped = 1;
    +}
    +
    +/* enable bit 0 - enable primary endpoint interrupt;
    + * bit 1 - enable secondary endpoint interrupt (TX for ep0).
    + */
    +static void mq_ep_ints (struct mq_ep *ep, int enable)
    +{
    +	volatile struct mediaq11xx_regs *regs = ep->mqdev->base->regs;
    +	u32 rxmask = 0, txmask = 0;
    +
    +	debug ("%s: rx:%d tx:%d\n", ep->ep.name, enable & 1, (enable >> 1) & 1);
    +
    +	switch (ep->num) {
    +	case 0:
    +		rxmask = MQ_UDC_IEN_EP0_RX;
    +		txmask = MQ_UDC_IEN_EP0_TX;
    +		break;
    +	case 1:
    +		txmask = MQ_UDC_IEN_EP1_TX;
    +		break;
    +	case 2:
    +		txmask = /*MQ_UDC_IEN_EP2_TX_EOT | */MQ_UDC_IEN_DMA_TX;
    +		break;
    +	case 3:
    +		rxmask = /*MQ_UDC_IEN_EP3_RX_EOT | */MQ_UDC_IEN_DMA_RX | MQ_UDC_IEN_DMA_RX_EOT;
    +		break;
    +	default:
    +		return;
    +	}
    +
    +	if (enable & 1)
    +		regs->UDC.control |= rxmask;
    +	else
    +		regs->UDC.control &= ~rxmask;
    +
    +	if (enable & 2)
    +		regs->UDC.control |= txmask;
    +	else
    +		regs->UDC.control &= ~txmask;
    +}
    +
    +static int mq_ep_disable (struct usb_ep *_ep)
    +{
    +	struct mq_ep *ep;
    +	struct mqudc *mqdev;
    +	unsigned long flags;
    +
    +	ep = container_of(_ep, struct mq_ep, ep);
    +	if (!_ep || !ep->desc)
    +		return -ENODEV;
    +	mqdev = ep->mqdev;
    +	if (mqdev->ep0state == EP0_SUSPEND)
    +		return -EBUSY;
    +
    +	spin_lock_irqsave(&mqdev->lock, flags);
    +
    +	/* Drop all pending requests */
    +	mq_ep_finish(ep, -ESHUTDOWN);
    +
    +	/* Disable endpoint interrupts */
    +	mq_ep_ints (ep, 0);
    +
    +	/* Set the endpoint to RESET state */
    +	mq_ep_reset(ep);
    +
    +	spin_unlock_irqrestore(&mqdev->lock, flags);
    +
    +	return 0;
    +}
    +
    +/*---------------------------------------------------------------------------*/
    +
    +/* Send a packed through endpoint 0 or 1 using FIFO.
    + */
    +static void mq_tx_fifo (struct mqudc *mqdev, struct mq_ep *ep, int rst_status)
    +{
    +	struct mq_request *req;
    +	int bytes_left, packet_len;
    +	u32 *buf;
    +	volatile u32 *fifo;
    +
    +	/* Acknowledge all status bits, we already got the status */
    +	if (rst_status)
    +		ep->regs [1] = 0xff;
    +
    +	req = list_empty (&ep->list) ? NULL :
    +		list_entry (ep->list.next, struct mq_request, queue);
    +
    +	/* If there's a short packet to be sent on endpoint 0, do it. */
    +	if ((ep->quick_buff_bytes) &&
    +	    (!req || req->req.actual >= req->req.length)) {
    +		bytes_left = ep->quick_buff_bytes - 1;
    +		ep->quick_buff_bytes = 0;
    +		buf = &ep->quick_buff;
    +		req = NULL;
    +		goto send_packet;
    +	}
    +
    +	if (unlikely (!req))
    +		return;
    +
    +	/* Since the start of request buffer is always aligned to
    +	 * word boundary, we can move whole words to FIFO until we
    +	 * get to the last (incomplete) word. Also since memory is
    +	 * never allocated in chunks smaller than one word, we can
    +	 * safely read the last incomplete 32-bit word, to avoid
    +	 * copying bytes by one.
    +	 */
    +	bytes_left = req->req.length - req->req.actual;
    +	if (bytes_left > ep->ep.maxpacket)
    +		bytes_left = ep->ep.maxpacket;
    +	buf = (u32 *)(req->req.actual + (u8 *)req->req.buf);
    +	req->req.actual += bytes_left;
    +
    +send_packet:
    +	packet_len = bytes_left;
    +	fifo = (ep->num == 0) ? &ep->regs [5] : &ep->regs [2];
    +	while (bytes_left > 0) {
    +		*fifo = le32_to_cpu (*buf++);
    +		bytes_left -= sizeof (u32);
    +	}
    +
    +	ep->regs [0] = (ep->regs [0] & MQ_UDC_EP_ENABLE) | mqdev->toggle |
    +		MQ_UDC_TX_EDT | MQ_UDC_TX_LAST_ENABLE (4 + bytes_left);
    +
    +	debug ("%s: Pktsize %d ctrl %08x stat %08x\n", ep->ep.name, packet_len,
    +	       ep->regs [0], ep->regs [1]);
    +
    +	/* Toggle DATA0/DATA1 */
    +	mqdev->toggle ^= MQ_UDC_TX_PID_DATA1;
    +
    +	if (!req || (req->req.actual >= req->req.length)) {
    +		/* Emmit a ZLP if the last packet is full size and
    +		 * req->zero is set.
    +		 */
    +		if (unlikely (req && req->req.zero &&
    +			      (packet_len == ep->ep.maxpacket))) {
    +			/* Send ZLP on next interrupt */
    +			ep->quick_buff_bytes = 1;
    +			debug ("%s: ZLP\n", ep->ep.name);
    +		}
    +
    +		/* In any case, the request has been submitted.
    +		 * Note that inside done_request we can get new requests
    +		 * hooked on this endpoint, so be careful.
    +		 */
    +		if (req)
    +			mq_done_request (ep, req, 0);
    +
    +		if (!ep->stopped && list_empty (&ep->list) && !ep->quick_buff_bytes) {
    +			/* If this is the last packet of a request, report
    +			 * successful completion to the gadget driver and
    +			 * disable further TX interrupts (enable them only
    +			 * when needed).
    +			 */
    +			ep->stopped = 1;
    +			/* Disable transmit interrupts */
    +			mq_ep_ints (ep, 1);
    +			if (ep->num == 0)
    +				mqdev->ep0state = EP0_IDLE;
    +			debug ("%s: Stop TX\n", ep->ep.name);
    +		}
    +	}
    +}
    +
    +/* Send a packet through endpoint 2 using DMA.
    + */
    +static void mq_tx_dma (struct mqudc *mqdev, struct mq_ep *ep)
    +{
    +	struct mq_request *req;
    +	int transfer_size;
    +	u32 dmactl, dmadesc, buffno, owner_mask, eot_mask;
    +
    +	debug ("dmactl:%08x status:%08x\n", ep->dmaregs [0], ep->regs [1]);
    +
    +	for (;;) {
    +		dmactl = ep->dmaregs [0];
    +
    +		if (list_empty (&ep->list)) {
    +			/* Disable DMA only after both DMA buffers are transferred */
    +			if (dmactl & (MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF2_OWNER))
    +				return;
    +			mq_ep_ints (ep, 1);
    +			ep->stopped = 1;
    +			debug ("%s: stop TX (ctrl:%08x)\n", ep->ep.name, ep->dmaregs [0]);
    +			return;
    +		}
    +
    +		if ((buffno = ep->active_buffer) == 0) {
    +			owner_mask = MQ_UDC_DMA_BUFF1_OWNER;
    +			eot_mask = MQ_UDC_DMA_BUFF1_EOT;
    +		} else {
    +			owner_mask = MQ_UDC_DMA_BUFF2_OWNER;
    +			eot_mask = MQ_UDC_DMA_BUFF2_EOT;
    +		}
    +
    +		/* Ok, DMA buffers loaded up - return */
    +		if (dmactl & owner_mask)
    +			return;
    +
    +		req = list_entry (ep->list.next, struct mq_request, queue);
    +
    +		transfer_size = req->req.length - req->req.actual;
    +		if (transfer_size > txbs)
    +			transfer_size = txbs;
    +
    +		/* Zero-length transfers not supported (?) */
    +		if (!transfer_size) {
    +			debug ("WARNING: ZERO-LENGTH TRANSFER REQUESTED BUT NOT SUPPORTED BY HARDWARE!\n");
    +			mq_done_request (ep, req, 0);
    +			continue;
    +		}
    +
    +		/* Transfer next portion of data to MediaQ RAM */
    +		memcpy ((u8 *)mqdev->base->ram + ep->dmabuff + (buffno ? txbs : 0),
    +			req->req.actual + (u8 *)req->req.buf, transfer_size);
    +		req->req.actual += transfer_size;
    +		ep->active_buffer ^= 1;
    +
    +		/* Now construct the buffer descriptor */
    +		dmadesc = MQ_UDC_DMA_BUFFER_ADDR (ep->dmabuff + (buffno ? txbs : 0)) |
    +			MQ_UDC_DMA_BUFFER_SIZE (transfer_size);
    +
    +		/* Find if this is last packet and multiple of maxpacket size */
    +		if ((req->req.actual >= req->req.length) &&
    +		    !(transfer_size & 63))
    +			dmadesc |= MQ_UDC_DMA_BUFFER_LAST;
    +		ep->dmaregs [1 + buffno] = dmadesc;
    +
    +		/* Decide if we need a ZLP after last packet */
    +		if (!ep->iso && req->req.zero &&
    +		    (req->req.actual >= req->req.length))
    +			dmactl = ~0;
    +		else {
    +			dmactl = ~eot_mask;
    +			eot_mask = 0;
    +		}
    +
    +		/* Read register value again in the case it has been
    +		 * changed in the meantime (esp. during memcpy()).
    +		 * [CRAP] while we're holding dmaregs[0] in CPU registers,
    +		 * it could change in hardware. Thus this period of time
    +                 * must be as short as possible.
    +		 */
    +		dmactl &= (ep->dmaregs [0] | owner_mask | eot_mask);
    +		ep->dmaregs [0] = dmactl;
    +
    +		debug ("buff:%d size:%d dmactrl:%08x dmadesc:%08x\n",
    +		       buffno, transfer_size, dmactl, ep->dmaregs [1 + buffno]);
    +		debug_dump ((u8 *)req->req.buf + req->req.actual - transfer_size,
    +			    transfer_size, ep->dmabuff + (buffno ? txbs : 0));
    +
    +		if (req->req.actual >= req->req.length)
    +			mq_done_request (ep, req, 0);
    +	}
    +}
    +
    +/* Prepare to receive packets through endpoint 3 using DMA.
    + */
    +static void mq_rx_dma_prepare (struct mq_ep *ep)
    +{
    +	u32 dmactl, addr, buffno;
    +
    +	dmactl = ep->dmaregs [0] | MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF2_OWNER;
    +
    +	for (buffno = 0; buffno <= 1; buffno++) {
    +		if (buffno == 0)
    +			dmactl &= ~(MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF1_EOT);
    +		else
    +			dmactl &= ~(MQ_UDC_DMA_BUFF2_OWNER | MQ_UDC_DMA_BUFF2_EOT);
    +
    +		addr = ep->dmabuff + (buffno ? rxbs : 0);
    +		ep->dmaregs [1 + buffno] = MQ_UDC_DMA_BUFFER_ADDR (addr) |
    +			MQ_UDC_DMA_BUFFER_EADDR (addr + rxbs);
    +		debug ("prepare buff %d, addr %08x\n", buffno, addr);
    +	}
    +
    +	ep->dmaregs [0] = dmactl;
    +}
    +
    +/* Transfer all the data in DMA buffers to pending request.
    + * After this re-submit the buffers to chip DMA controller.
    + */
    +static void mq_rx_dma (struct mqudc *mqdev, struct mq_ep *ep)
    +{
    +	struct mq_request *req = NULL;
    +	int end_transf, transfer_size, space_avail;
    +	u32 dmactl, buffno, owner_mask, eot_mask;
    +	volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
    +
    +	end_transf = 0;
    +try_again:
    +	dmactl = ep->dmaregs [0];
    +	debug ("ctrl:%08x status:%08x\n", dmactl, ep->regs [1]);
    +
    +	for (;;) {
    +		/**
    +		 * [CRAP] the MQ_UDC_INT_DMA_RX_EOT is set for ANY of the
    +		 * two buffers being completely received. Thus we have to
    +		 * do a lot of guesswork in the case when two buffers are
    +                 * marked as "owned by CPU", e.g. contain received data.
    +		 */
    +		if (ep->iso) {
    +			// ??? todo
    +			if (dmactl & MQ_UDC_ISO_TRANSFER_END)
    +				end_transf |= ~ep->dmaregs [0] & (MQ_UDC_DMA_BUFF1_OWNER |
    +								  MQ_UDC_DMA_BUFF2_OWNER);
    +		} else {
    +			if (regs->UDC.intstatus & MQ_UDC_INT_DMA_RX_EOT) {
    +				end_transf |= ~ep->dmaregs [0] & (MQ_UDC_DMA_BUFF1_OWNER |
    +								  MQ_UDC_DMA_BUFF2_OWNER);
    +				regs->UDC.intstatus = MQ_UDC_INT_DMA_RX_EOT;
    +			}
    +		}
    +
    +		if ((buffno = ep->active_buffer) == 0) {
    +			owner_mask = MQ_UDC_DMA_BUFF1_OWNER;
    +			eot_mask = MQ_UDC_DMA_BUFF1_EOT;
    +		} else {
    +			owner_mask = MQ_UDC_DMA_BUFF2_OWNER;
    +			eot_mask = MQ_UDC_DMA_BUFF2_EOT;
    +		}
    +
    +		/* No more received buffers? */
    +		if (!(dmactl & owner_mask)) {
    +			if (dmactl & (owner_mask ^ (MQ_UDC_DMA_BUFF1_EOT | MQ_UDC_DMA_BUFF2_EOT))) {
    +				debug ("Desyncronization! (buff %d, ctrl %08x)\n",
    +				       buffno, dmactl);
    +				ep->active_buffer ^= 1;
    +				continue;
    +			}
    +			if (end_transf) {
    +				debug ("EOT received w/o data\n");
    +				goto try_again;
    +			}
    +			return;
    +		}
    +
    +		ep->active_buffer ^= 1;
    +
    +		if (list_empty (&ep->list)) {
    +			ep->stopped = 1;
    +			debug ("Received data, but no requests in queue!\n");
    +			return;
    +		}
    +
    +		/* Get the next pending receive request */
    +		req = list_entry (ep->list.next, struct mq_request, queue);
    +
    +		/* Find out the size of data pending in DMA buffers */
    +		transfer_size = rxbs;
    +		if (!ep->iso && (dmactl & eot_mask))
    +			transfer_size = ep->dmaregs [3 + buffno];
    +
    +		/* Find out the available space in request' buffer */
    +		space_avail = req->req.length - req->req.actual;
    +		if (transfer_size > space_avail) {
    +			transfer_size = space_avail;
    +			if (!ep->iso)
    +				req->req.status = -EOVERFLOW;
    +		}
    +
    +		/* Copy data from DMA buffers to request buffer */
    +		memcpy (req->req.actual + (u8 *)req->req.buf,
    +			(u8 *)mqdev->base->ram + ep->dmabuff + (buffno ? rxbs : 0),
    +			transfer_size);
    +
    +		/* Mark immediately the DMA buffer as processed and free
    +		 * for more incoming data (maximize throughput).
    +                 *
    +		 * [CRAP] While we hold the old value in CPU registers,
    +		 * the hardware register state can change and we'll write
    +		 * the OLD value back.
    +		 */
    +		dmactl = ~(owner_mask | eot_mask | MQ_UDC_ISO_TRANSFER_END);
    +		dmactl &= ep->dmaregs [0];
    +		ep->dmaregs [0] = dmactl;
    +
    +		debug ("buff:%d size:%d dmactl:%08x end:%x\n",
    +		       buffno, transfer_size, dmactl, end_transf);
    +		debug_dump ((u8 *)req->req.buf + req->req.actual, transfer_size,
    +			    ep->dmabuff + (buffno ? rxbs : 0));
    +
    +		req->req.actual += transfer_size;
    +
    +		/* If we have a short buffer, we obviously have a end
    +		 * of transfer.
    +		 */
    +		if (transfer_size < rxbs)
    +			end_transf = owner_mask;
    +		/* If we have a full buffer, and the other buffer is available,
    +		 * consider the EOP belonging to the next buffer, not this.
    +		 * This often happens when we have an incoming packet with
    +		 * the length equal to rxbs+a little (say, 4 bytes).
    +		 */
    +		else if (end_transf & ~owner_mask)
    +			end_transf &= ~owner_mask;
    +
    +		/* NOTE: we don't handle for now properly the case 
    +                 * when a incoming ISO packet can fill several requests.
    +		 */
    +
    +		if ((ep->iso &&
    +		     (end_transf || (req->req.actual >= req->req.length))) ||
    +		    (!ep->iso &&
    +		     (end_transf & owner_mask))) {
    +			if (ep->iso)
    +				end_transf = 0;
    +			else
    +				end_transf &= ~owner_mask;
    +			mq_done_request (ep, req, 0);
    +		}
    +	}
    +}
    +
    +/*---------------------------------------------------------------------------*/
    +
    +static struct usb_request *mq_alloc_request (struct usb_ep *_ep,
    +						int gfp_flags)
    +{
    +	struct mq_request *req;
    +
    +	req = kmalloc (sizeof (*req), gfp_flags);
    +	if (!req)
    +		return 0;
    +
    +	memset (req, 0, sizeof (*req));
    +	INIT_LIST_HEAD (&req->queue);
    +
    +	return &req->req;
    +}
    +
    +static void mq_free_request (struct usb_ep *_ep, struct usb_request *_req)
    +{
    +	struct mq_request *req;
    +
    +	if (!_ep || !_req)
    +		return;
    +
    +	req = container_of (_req, struct mq_request, req);
    +
    +	if (!list_empty (&req->queue)) {
    +		debug ("%s: request %p still processed, unlinking\n",
    +		       _ep->name, _req);
    +		list_del (&req->queue);
    +		if (likely (req->req.status == -EINPROGRESS))
    +			req->req.status = -ESHUTDOWN;
    +	}
    +	kfree (req);
    +}
    +
    +static void *mq_alloc_buffer (struct usb_ep *_ep, unsigned bytes,
    +				 dma_addr_t *dma, int gfp_flags)
    +{
    +	return kmalloc (bytes, gfp_flags);
    +}
    +
    +static void mq_free_buffer (struct usb_ep *_ep, void *buf, dma_addr_t dma,
    +			    unsigned bytes)
    +{
    +	kfree (buf);
    +}
    +
    +static void mq_done_request (struct mq_ep *ep, struct mq_request *req,
    +			     int status)
    +{
    +	struct mqudc *mqdev;
    +
    +	list_del_init (&req->queue);
    +
    +	if (likely (req->req.status == -EINPROGRESS))
    +		req->req.status = status;
    +	else
    +		status = req->req.status;
    +
    +	mqdev = ep->mqdev;
    +
    +#ifndef USB_DEBUG_TRACE
    +	if (status && status != -ESHUTDOWN)
    +#endif
    +		debug ("complete %s req %p stat %d len %u/%u\n",
    +		       ep->ep.name, &req->req, status,
    +		       req->req.actual, req->req.length);
    +
    +	spin_unlock (&mqdev->lock);
    +	req->req.complete (&ep->ep, &req->req);
    +	spin_lock (&mqdev->lock);
    +}
    +
    +static int mq_start_queue (struct mq_ep *ep)
    +{
    +	unsigned long flags;
    +
    +	debug ("%s\n", ep->ep.name);
    +
    +	spin_lock_irqsave (&ep->mqdev->lock, flags);
    +
    +	/* Enable both RX and TX interrupts from this endpoint */
    +	if (ep->stopped)
    +		mq_ep_ints (ep, 3);
    +
    +	/* For DMA-backed endpoints we must pre-fill the DMA buffers
    +	 * and then transmit buffers ownership to the mq chip; for FIFO
    +	 * endpoints we must fill the FIFO.
    +	 */
    +	if (!ep->dmaregs) {
    +		ep->stopped = 0;
    +		if (ep->is_in)
    +			/* Fill the transmit FIFO for this endpoint */
    +			mq_tx_fifo (ep->mqdev, ep, 1);
    +	} else if (ep->stopped) {
    +		ep->stopped = 0;
    +		if (ep->is_in)
    +			mq_tx_dma (ep->mqdev, ep);
    +		else
    +			mq_rx_dma_prepare (ep);
    +	}
    +
    +	spin_unlock_irqrestore (&ep->mqdev->lock, flags);
    +
    +	return 0;
    +}
    +
    +static int mq_queue (struct usb_ep *_ep, struct usb_request *_req,
    +		     int gfp_flags)
    +{
    +	struct mq_ep *ep;
    +	struct mqudc *mqdev;
    +	unsigned long flags;
    +	int status;
    +	struct mq_request *req = container_of (_req, struct mq_request, req);
    +
    +	if (unlikely(!_req || !req->req.complete
    +		     || !req->req.buf || !list_empty (&req->queue))) {
    +		debug ("%s: Invalid request (%p %p %p %d)\n",
    +		       _ep->name, req, req->req.complete, req->req.buf,
    +		       list_empty (&req->queue));
    +		return -EINVAL;
    +	}
    +
    +	ep = container_of(_ep, struct mq_ep, ep);
    +	if (unlikely (!_ep ||
    +		      ((ep->num != 0) &&
    +		       (!ep->desc || !(ep->regs [0] & MQ_UDC_EP_ENABLE))))) {
    +		debug ("%s: Invalid endpoint\n", _ep->name);
    +		return -EINVAL;
    +	}
    +
    +	mqdev = ep->mqdev;
    +	if (unlikely(!mqdev->driver))
    +		return -ESHUTDOWN;
    +
    +	/* can't touch registers when suspended */
    +	if (mqdev->ep0state == EP0_SUSPEND)
    +		return -EBUSY;
    +
    +#ifdef USB_DEBUG_TRACE
    +	debug("%s queue req %p, len %u buf %p\n",
    +	      _ep->name, _req, req->req.length, req->req.buf);
    +#endif
    +
    +	spin_lock_irqsave (&mqdev->lock, flags);
    +
    +	req->req.status = -EINPROGRESS;
    +	req->req.actual = 0;
    +
    +	/* for ep0 IN without premature status, zlp is required and
    +	 * writing EOP starts the status stage (OUT).
    +	 */
    +	if (unlikely (ep->num == 0 && ep->is_in))
    +		req->req.zero = 1;
    +
    +	list_add_tail (&req->queue, &ep->list);
    +
    +	/* kickstart this i/o queue? */
    +	status = mq_start_queue (ep);
    +
    +	spin_unlock_irqrestore (&mqdev->lock, flags);
    +
    +	return status;
    +}
    +
    +/* dequeue ALL requests */
    +static void mq_ep_finish (struct mq_ep *ep, int status)
    +{
    +	struct mq_request *req;
    +	unsigned long flags;
    +
    +	spin_lock_irqsave (&ep->mqdev->lock, flags);
    +
    +	/* Disable tx interrupts */
    +	mq_ep_ints (ep, 1);
    +	ep->stopped = 1;
    +	/* Clear both FIFOs */
    +	mq_ep_clear_fifo (ep);
    +
    +	while (!list_empty(&ep->list)) {
    +		req = list_entry (ep->list.next, struct mq_request, queue);
    +		mq_done_request (ep, req, status);
    +	}
    +
    +	spin_unlock_irqrestore (&ep->mqdev->lock, flags);
    +}
    +
    +static int mq_dequeue (struct usb_ep *_ep, struct usb_request *_req)
    +{
    +	struct mq_ep *ep;
    +	struct mqudc *mqdev;
    +	unsigned long flags;
    +	struct mq_request *req = container_of (_req, struct mq_request, req);
    +
    +	if (!_req || list_empty (&req->queue))
    +		return -EINVAL;
    +	ep = container_of (_ep, struct mq_ep, ep);
    +	if (!_ep || (!ep->desc && ep->num != 0))
    +		return -EINVAL;
    +	mqdev = ep->mqdev;
    +	if (!mqdev->driver)
    +		return -ESHUTDOWN;
    +
    +	/* we can't touch registers when suspended */
    +	if (mqdev->ep0state == EP0_SUSPEND)
    +		return -EBUSY;
    +
    +	spin_lock_irqsave (&mqdev->lock, flags);
    +	mq_done_request (ep, req, -ECONNRESET);
    +	spin_unlock_irqrestore (&mqdev->lock, flags);
    +
    +	return 0;
    +}
    +
    +/*-------------------------------------------------------------------------*/
    +
    +static int mq_set_halt(struct usb_ep *_ep, int value)
    +{
    +	struct mq_ep *ep;
    +
    +	if (!_ep)
    +		return -ENODEV;
    +
    +	ep = container_of (_ep, struct mq_ep, ep);
    +	debug ("%s %s halt\n", _ep->name, value ? "set" : "clear");
    +
    +	if (ep->num == 0) {
    +		if (value)
    +			ep->mqdev->ep0state = EP0_STALL;
    +		else
    +			return -EINVAL;
    +	}
    +
    +	if (value) {
    +		ep->regs [0] |= MQ_UDC_STALL;
    +		if (ep->num == 0)
    +			ep->regs [2] |= MQ_UDC_STALL;
    +		ep->stopped = 1;
    +	} else {
    +		ep->regs [0] &= ~MQ_UDC_STALL;
    +		if (ep->num == 0)
    +			ep->regs [2] &= ~MQ_UDC_STALL;
    +		ep->stopped = 0;
    +	}
    +
    +	return 0;
    +}
    +
    +static int mq_fifo_status(struct usb_ep *_ep)
    +{
    +	struct mq_ep	*ep;
    +	u32 status;
    +
    +	if (!_ep)
    +		return -ENODEV;
    +	ep = container_of (_ep, struct mq_ep, ep);
    +
    +	if (ep->num > 1)
    +		return -EOPNOTSUPP;
    +
    +	if (ep->num == 0 && ep->is_in)
    +		status = ep->regs [3];
    +	else
    +		status = ep->regs [1];
    +
    +	return (MQ_UDC_FIFO_DEPTH - MQ_UDC_FIFO (status)) * 4;
    +}
    +
    +static void mq_fifo_flush(struct usb_ep *_ep)
    +{
    +	struct mq_ep	*ep;
    +
    +	if (!_ep)
    +		return;
    +	ep = container_of (_ep, struct mq_ep, ep);
    +
    +	ep->regs [0] |= MQ_UDC_CLEAR_FIFO;
    +	ep->regs [0] &= ~MQ_UDC_CLEAR_FIFO;
    +	if (ep->num == 0) {
    +		/* For endpoint 0, clear also RX FIFO */
    +		ep->regs [2] |= MQ_UDC_CLEAR_FIFO;
    +		ep->regs [2] &= ~MQ_UDC_CLEAR_FIFO;
    +	}
    +	return;
    +}
    +
    +static struct usb_ep_ops mq_ep_ops = {
    +	.enable		= mq_ep_enable,
    +	.disable	= mq_ep_disable,
    +
    +	.alloc_request	= mq_alloc_request,
    +	.free_request	= mq_free_request,
    +
    +	.alloc_buffer	= mq_alloc_buffer,
    +	.free_buffer	= mq_free_buffer,
    +
    +	.queue		= mq_queue,
    +	.dequeue	= mq_dequeue,
    +
    +	.set_halt	= mq_set_halt,
    +	.fifo_status	= mq_fifo_status,
    +	.fifo_flush	= mq_fifo_flush,
    +};
    +
    +/*-------------------------------------------------------------------------*/
    +
    +static int mq_get_frame(struct usb_gadget *gadget)
    +{
    +	struct mqudc *mqdev = container_of (gadget, struct mqudc, gadget);
    +	return mqdev->base->regs->UDC.frame_number & MQ_UDC_FRAME_MASK;
    +}
    +
    +int mq_wakeup (struct usb_gadget *gadget)
    +{
    +	struct mqudc *mqdev = container_of (gadget, struct mqudc, gadget);
    +	mqdev->base->regs->UDC.control |= MQ_UDC_WAKEUP_USBHOST;
    +	return 0;
    +}
    +
    +int mq_set_selfpowered (struct usb_gadget *gadget, int value)
    +{
    +	if (value)
    +		return 0;
    +	return -EOPNOTSUPP;
    +}
    +
    +static const struct usb_gadget_ops mq_ops = {
    +	.get_frame		= mq_get_frame,
    +	.wakeup			= mq_wakeup,
    +	.set_selfpowered	= mq_set_selfpowered,
    +};
    +
    +/*-------------------------------------------------------------------------*/
    +
    +static void mq_reinit (struct mqudc *mqdev)
    +{
    +	volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
    +	unsigned i;
    +
    +	mqdev->ep0state = EP0_IDLE;
    +
    +	for (i = 0; i < 4; i++) {
    +		struct mq_ep *ep = &mqdev->ep[i];
    +
    +		INIT_LIST_HEAD (&ep->list);
    +		mq_ep_reset(ep);
    +	}
    +
    +	regs->UDC.control = (regs->UDC.control & 0xffff) |
    +		MQ_UDC_IEN_EP0_RX |
    +		MQ_UDC_IEN_GLOBAL_SUSPEND |
    +		MQ_UDC_IEN_WAKEUP | MQ_UDC_IEN_RESET |
    +		MQ_UDC_SUSPEND_ENABLE |
    +		MQ_UDC_REMOTEHOST_WAKEUP_ENABLE;
    +
    +	/* Set endpoint address to zero after reset */
    +	regs->UDC.address = 0;
    +	/* Drop endpoint status bits, whatever they were */
    +	regs->UDC.ep0txstatus = 0xff;
    +	regs->UDC.ep0rxstatus = 0xff;
    +}
    +
    +static void mq_reset(struct mqudc *mqdev)
    +{
    +	volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
    +
    +	/* Set D+ to Hi-Z (no pullup) */
    +	mach_info->udc_command (MQ11XX_UDC_CMD_DISCONNECT);
    +
    +	/* Initialize device registers */
    +	regs->UDC.control = 0;
    +	regs->UDC.address = 0;
    +	regs->UDC.ep0txcontrol = MQ_UDC_CLEAR_FIFO;
    +	regs->UDC.ep0txcontrol = 0;
    +	regs->UDC.ep0rxcontrol = MQ_UDC_CLEAR_FIFO;
    +	regs->UDC.ep0rxcontrol = 0;
    +	regs->UDC.ep1control = 0;
    +	regs->UDC.ep2control = 0;
    +	regs->UDC.ep3control = 0;
    +	regs->UDC.intstatus = 0xffffffff;
    +	regs->UDC.dmatxcontrol = 0;
    +	regs->UDC.dmarxcontrol = 0;
    +}
    +
    +static void mq_enable(struct mqudc *mqdev)
    +{
    +	mq_reset (mqdev);
    +	mq_reinit (mqdev);
    +
    +	/* Enable 1.5k D+ pullup resistor */
    +	mach_info->udc_command (MQ11XX_UDC_CMD_CONNECT);
    +}
    +
    +/*---------------------------------------------------------------------------*/
    +/*                             USB Gadget exported API                       */
    +/*---------------------------------------------------------------------------*/
    +
    +static struct mqudc *the_controller;
    +
    +int usb_gadget_register_driver(struct usb_gadget_driver *driver)
    +{
    +	struct mqudc *mqdev = the_controller;
    +	int rc;
    +
    +	if (!driver
    +	 || driver->speed != USB_SPEED_FULL
    +	 || !driver->bind
    +	 || !driver->unbind
    +	 || !driver->disconnect
    +	 || !driver->setup)
    +		return -EINVAL;
    +	if (!mqdev)
    +		return -ENODEV;
    +	if (mqdev->driver)
    +		return -EBUSY;
    +
    +	/* hook up the driver */
    +	driver->driver.bus = 0;
    +	mqdev->driver = driver;
    +	mqdev->gadget.dev.driver = &driver->driver;
    +	rc = driver->bind (&mqdev->gadget);
    +	if (rc) {
    +		debug ("error %d while binding to driver %s\n",
    +		       rc, driver->driver.name);
    +		mqdev->driver = 0;
    +		mqdev->gadget.dev.driver = 0;
    +		return rc;
    +	}
    +
    +	/* enable host detection and ep0; and we're ready
    +	 * for set_configuration as well as eventual disconnect.
    +	 */
    +	mq_enable (mqdev);
    +
    +	info ("Registered gadget driver '%s'\n", driver->driver.name);
    +	return 0;
    +}
    +EXPORT_SYMBOL(usb_gadget_register_driver);
    +
    +static void
    +stop_activity(struct mqudc *mqdev, struct usb_gadget_driver *driver)
    +{
    +	unsigned i;
    +
    +	mq_reset (mqdev);
    +	for (i = 0; i < 4; i++)
    +		mq_ep_finish(&mqdev->ep [i], -ESHUTDOWN);
    +	if (driver) {
    +		spin_unlock(&mqdev->lock);
    +		driver->disconnect(&mqdev->gadget);
    +		spin_lock(&mqdev->lock);
    +	}
    +
    +	if (mqdev->driver)
    +		mq_enable(mqdev);
    +}
    +
    +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
    +{
    +	struct mqudc *mqdev = the_controller;
    +	unsigned long flags;
    +
    +	if (!mqdev)
    +		return -ENODEV;
    +	if (!driver || driver != mqdev->driver)
    +		return -EINVAL;
    +
    +	spin_lock_irqsave(&mqdev->lock, flags);
    +	mqdev->driver = 0;
    +	stop_activity(mqdev, driver);
    +	spin_unlock_irqrestore(&mqdev->lock, flags);
    +
    +	driver->unbind(&mqdev->gadget);
    +
    +	debug ("unregistered driver '%s'\n", driver->driver.name);
    +
    +	return 0;
    +}
    +EXPORT_SYMBOL(usb_gadget_unregister_driver);
    +
    +/*---------------------------------------------------------------------------*/
    +/*                                 IRQ handling                              */
    +/*---------------------------------------------------------------------------*/
    +
    +static void ep01_transmit (struct mqudc *mqdev, struct mq_ep *ep)
    +{
    +	u32 txstatus = ep->regs [1];
    +
    +	/* Clear the status of last packet sent */
    +	ep->regs [1] = 0xff;
    +
    +	/* If no ACK for last sent packet, or the End Of Data flag is
    +	 * still set, don't disturb the transmitter - it's not ready yet.
    +	 */
    +	if (!(txstatus & MQ_UDC_ACK) &&
    +	    (ep->regs [0] & MQ_UDC_TX_EDT)) {
    +		debug ("No ACK and EDT is still set\n");
    +		return;
    +	}
    +
    +	if (MQ_UDC_FIFO (txstatus) < MQ_UDC_FIFO_DEPTH) {
    +		debug ("TX FIFO not empty yet\n");
    +		return;
    +	}
    +
    +	if ((ep->num == 0) && (mqdev->ep0state == EP0_STALL)) {
    +		ep->regs [0] |= MQ_UDC_STALL;
    +		return;
    +	}
    +
    +	mq_tx_fifo (mqdev, ep, 0);
    +}
    +
    +static void ep0_receive (struct mqudc *mqdev)
    +{
    +	volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
    +	struct mq_ep *ep = &mqdev->ep[0];
    +	struct usb_ctrlrequest *ctrl;
    +	u32 rxstatus = regs->UDC.ep0rxstatus;
    +	u32 rxbuff [4];
    +	int i, count;
    +
    +	/* Acknowledge all status bits, we already got the status */
    +	regs->UDC.ep0rxstatus = 0xff;
    +
    +	/* If the data is broken, ignore it - the hardware already sent a NAK
    +	 * if the packet is not a SETUP packet, and won't generate an
    +	 * interrupt if the packet is a broken SETUP packet...
    +	 */
    +	if ((rxstatus & (MQ_UDC_ERR | MQ_UDC_TIMEOUT | MQ_UDC_FIFO_OVERRUN | MQ_UDC_ACK)) != MQ_UDC_ACK) {
    +		regs->UDC.ep0rxcontrol = MQ_UDC_CLEAR_FIFO;
    +		regs->UDC.ep0rxcontrol = 0;
    +		return;
    +	}
    +
    +	/* Get the data from RX FIFO */
    +	count = MQ_UDC_FIFO (rxstatus);
    +	for (i = 0; i < count; i++)
    +		/* According to docs, lowest bits are always earlier bytes */
    +		rxbuff [i] = cpu_to_le32 (regs->UDC.ep0rxfifo);
    +	if (count) {
    +		i = MQ_UDC_RX_VALID_BYTES (rxstatus);
    +		if (i == 0)
    +			i = 4;
    +		count = (count - 1) * 4 + i;
    +	}
    +
    +	/* Set output DATA0/DATA1 PID from the PID of incoming packet */
    +	mqdev->toggle = (rxstatus & MQ_UDC_RX_PID_DATA1) ? 0 : MQ_UDC_TX_PID_DATA1;
    +
    +	if (!(rxstatus & MQ_UDC_RX_TOKEN_SETUP)) {
    +		struct mq_request *req;
    +
    +		if (unlikely (ep->is_in))
    +			return;
    +		if (unlikely (list_empty (&ep->list)))
    +			return;
    +
    +		req = list_entry (ep->list.next, struct mq_request, queue);
    +
    +		if (req->req.actual + count > req->req.length) {
    +			debug ("%s: buffer overflow, got %d, max %d\n",
    +			       ep->ep.name, count, req->req.length);
    +			req->req.status = -EOVERFLOW;
    +			count = req->req.length - req->req.actual;
    +		}
    +
    +		memcpy (req->req.buf + req->req.actual, &rxbuff, count);
    +		req->req.actual += count;
    +
    +		/* If packet is short, or the transfer is complete, finish */
    +		if (count < ep->ep.maxpacket ||
    +		    req->req.actual >= req->req.length) {
    +			ep->stopped = 1;
    +			mqdev->ep0state = EP0_IDLE;
    +			mq_done_request (ep, req, 0);
    +		}
    +
    +		return;
    +	}
    +
    +	ctrl = (struct usb_ctrlrequest *)&rxbuff;
    +
    +	le16_to_cpus (&ctrl->wValue);
    +	le16_to_cpus (&ctrl->wIndex);
    +	le16_to_cpus (&ctrl->wLength);
    +
    +	mq_ep_finish (ep, 0);
    +
    +	if (likely (ctrl->bRequestType & USB_DIR_IN)) {
    +		ep->is_in = 1;
    +		mqdev->ep0state = EP0_IN;
    +	} else {
    +		ep->is_in = 0;
    +		mqdev->ep0state = EP0_OUT;
    +	}
    +
    +#ifdef USB_DEBUG_TRACE
    +	debug ("SETUP %02x.%02x v%04x i%04x l%04x\n",
    +	       ctrl->bRequestType, ctrl->bRequest,
    +	       ctrl->wValue, ctrl->wIndex, ctrl->wLength);
    +#endif
    +
    +	/* Handle some SETUP packets ourselves */
    +	switch (ctrl->bRequest) {
    +	case USB_REQ_SET_ADDRESS:
    +		if (ctrl->bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
    +			break;
    +
    +		debug ("SET_ADDRESS (%d)\n", ctrl->wValue);
    +
    +		regs->UDC.address = ctrl->wValue;
    +		/* Reply with a ZLP on next IN token */
    +		ep->quick_buff_bytes = 1;
    +		mq_tx_fifo (mqdev, ep, 1);
    +		return;
    +
    +	case USB_REQ_GET_STATUS: {
    +		struct mq_ep *qep;
    +		int epn = ctrl->wIndex & ~USB_DIR_IN;
    +
    +		/* hw handles device and interface status */
    +		if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_ENDPOINT) ||
    +		    ctrl->wLength > 2 || epn > 3)
    +			break;
    +
    +		debug ("GET_STATUS (%x)\n", ctrl->wIndex);
    +
    +		qep = &mqdev->ep [epn];
    +		if (qep->is_in != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0))
    +			break;
    +
    +		/* Return status on next IN token */
    +		ep->quick_buff_bytes = 2;
    +		ep->quick_buff = cpu_to_le32 (qep->stopped);
    +		mq_tx_fifo (mqdev, ep, 1);
    +		return;
    +	}
    +
    +	case USB_REQ_CLEAR_FEATURE:
    +	case USB_REQ_SET_FEATURE:
    +		if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
    +			struct mq_ep *qep;
    +
    +			debug ("SET/CLR_FEATURE (%d)\n", ctrl->wValue);
    +
    +			/* Support only HALT feature */
    +			if (ctrl->wValue != 0 ||
    +			    ctrl->wLength != 0 ||
    +			    ctrl->wIndex > 3)
    +				goto ep0_stall;
    +
    +			qep = &mqdev->ep [ctrl->wIndex];
    +			if (ctrl->bRequest == USB_REQ_SET_FEATURE) {
    +				qep->regs [0] |= MQ_UDC_STALL;
    +				if (ctrl->wIndex == 0)
    +					qep->regs [2] |= MQ_UDC_STALL;
    +			} else {
    +				qep->regs [0] &= ~MQ_UDC_STALL;
    +				if (ctrl->wIndex == 0)
    +					qep->regs [2] &= ~MQ_UDC_STALL;
    +			}
    +
    +			/* Reply with a ZLP on next IN token */
    +			ep->quick_buff_bytes = 1;
    +			mq_tx_fifo (mqdev, ep, 1);
    +			return;
    +		}
    +		break;
    +	}
    +
    +	if (!mqdev->driver)
    +		return;
    +
    +	/* delegate everything to the gadget driver.
    +	 * it may respond after this irq handler returns.
    +	 */
    +	spin_unlock (&mqdev->lock);
    +	i = mqdev->driver->setup (&mqdev->gadget, ctrl);
    +	spin_lock (&mqdev->lock);
    +
    +	if (unlikely (i < 0)) {
    +ep0_stall:
    +#ifdef USB_DEBUG_TRACE
    +		debug("req %02x.%02x protocol STALL\n",
    +		      ctrl->bRequestType, ctrl->bRequest);
    +#endif
    +		ep->stopped = 1;
    +		mqdev->ep0state = EP0_STALL;
    +		regs->UDC.ep0txcontrol |= MQ_UDC_STALL;
    +	}
    +}
    +
    +static irqreturn_t mq_dev_irq(int irq, void *data, struct pt_regs *r)
    +{
    +	struct mqudc *mqdev = (struct mqudc *)data;
    +	volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
    +	int irq_handled = 0;
    +	u32 mask;
    +
    +	spin_lock(&mqdev->lock);
    +
    +	while ((mask = regs->UDC.intstatus) != 0) {
    +		irq_handled = 1;
    +
    +		/* Sort in decreasing importance order */
    +		if (likely (mask & MQ_UDC_INT_DMA_TX)) {
    +			/* EP2 DMA end-of-transmit buffer reached */
    +			regs->UDC.intstatus = MQ_UDC_INT_DMA_TX;
    +			mq_tx_dma (mqdev, &mqdev->ep [2]);
    +		} else if (likely (mask & (MQ_UDC_INT_DMA_RX | MQ_UDC_INT_DMA_RX_EOT))) {
    +			/* EP3 DMA end-of-receive buffer reached */
    +			regs->UDC.intstatus = mask & MQ_UDC_INT_DMA_RX;
    +			mq_rx_dma (mqdev, &mqdev->ep [3]);
    +		} else if (likely (mask & MQ_UDC_INT_EP0_RX)) {
    +			/* EP0 packet received */
    +			regs->UDC.intstatus = MQ_UDC_INT_EP0_RX;
    +			ep0_receive (mqdev);
    +		} else if (likely (mask & MQ_UDC_INT_EP0_TX)) {
    +			/* EP0 packet transmitted (FIFO empty) */
    +			regs->UDC.intstatus = MQ_UDC_INT_EP0_TX;
    +			ep01_transmit (mqdev, &mqdev->ep [0]);
    +		} else if (likely (mask & MQ_UDC_INT_EP1_TX)) {
    +			/* EP1 packet transmitted (FIFO empty) */
    +			regs->UDC.intstatus = MQ_UDC_INT_EP1_TX;
    +			ep01_transmit (mqdev, &mqdev->ep [1]);
    +		} else if (unlikely (mask & MQ_UDC_INT_RESET)) {
    +			debug ("reset\n");
    +			/* Device reset */
    +			regs->UDC.intstatus = MQ_UDC_INT_RESET;
    +			mq_reinit (mqdev);
    +		} else if (unlikely (mask & MQ_UDC_INT_WAKEUP)) {
    +                        regs->UDC.intstatus = MQ_UDC_INT_WAKEUP;
    +
    +			if (mqdev->ep0state == EP0_SUSPEND) {
    +				debug ("resume\n");
    +				mqdev->ep0state = EP0_IDLE;
    +				if (mqdev->driver && mqdev->driver->resume) {
    +					spin_unlock (&mqdev->lock);
    +					mqdev->driver->resume (&mqdev->gadget);
    +					spin_lock (&mqdev->lock);
    +				}
    +			}
    +		} else if (unlikely (mask & MQ_UDC_INT_GLOBAL_SUSPEND)) {
    +			regs->UDC.intstatus = MQ_UDC_INT_GLOBAL_SUSPEND;
    +
    +			if (mqdev->ep0state != EP0_DISCONNECT &&
    +			    mqdev->ep0state != EP0_SUSPEND) {
    +				debug ("suspend\n");
    +				mqdev->ep0state = EP0_SUSPEND;
    +				if (mqdev->driver && mqdev->driver->suspend) {
    +					spin_unlock (&mqdev->lock);
    +					mqdev->driver->suspend (&mqdev->gadget);
    +					spin_lock (&mqdev->lock);
    +				}
    +			}
    +		} else {
    +			/*
    +			 * MQ_UDC_INT_SOF
    +			 * MQ_UDC_INT_EP2_TX_EOT
    +			 * MQ_UDC_INT_EP3_RX_EOT
    +			 */
    +			debug ("unhandled mask: %08x\n", mask);
    +			regs->UDC.intstatus = mask;
    +		}
    +	}
    +
    +        spin_unlock(&mqdev->lock);
    +
    +	return IRQ_RETVAL (irq_handled);
    +}
    +
    +static irqreturn_t mq_wup_irq(int irq, void *data, struct pt_regs *r)
    +{
    +	debug ("WakeUP IRQ\n");
    +
    +	return IRQ_HANDLED;
    +}
    +
    +/*-------------------------------------------------------------------------*/
    +
    +#ifdef MQ_IRQ_MULTIPLEX
    +static struct mq_irq_desc
    +{
    +	char *desc;
    +	int delta;
    +	irqreturn_t (*handler) (int, void *, struct pt_regs *);
    +} mq_irq_desc [] =
    +{
    +	{ "MediaQ UDC", IRQ_MQ_UDC, mq_dev_irq },
    +	{ "MediaQ UDC wake-up", IRQ_MQ_UDC_WAKE_UP, mq_wup_irq }
    +};
    +#endif
    +
    +static int mq_remove(struct device *dev);
    +
    +static void gadget_release(struct device *dev)
    +{
    +	struct mqudc *mqdev = dev_get_drvdata (dev);
    +	kfree (mqdev);
    +}
    +
    +static int mq_probe (struct device *dev)
    +{
    +	static char *names [] = { "ep0", "ep1in-int", "ep2in", "ep3out" };
    +	struct mqudc *mqdev;
    +	struct mediaq11xx_base *base = (struct mediaq11xx_base *)dev->platform_data;
    +	volatile struct mediaq11xx_regs *regs = base->regs;
    +	int i;
    +
    +	if (!base)
    +		return -ENXIO;
    +#ifdef MQ_IRQ_MULTIPLEX
    +	if (!base->irq_base)
    +		return -ENXIO;
    +#endif
    +
    +	mqdev = kmalloc (sizeof (*mqdev), GFP_KERNEL);
    +	if (!mqdev)
    +		return -ENOMEM;
    +
    +	memset(mqdev, 0, sizeof (*mqdev));
    +	mqdev->base = base;
    +	spin_lock_init(&mqdev->lock);
    +	mqdev->gadget.ops = &mq_ops;
    +
    +	strcpy(mqdev->gadget.dev.bus_id, "gadget");
    +	mqdev->gadget.ep0 = &mqdev->ep [0].ep;
    +	mqdev->gadget.dev.parent = dev;
    +	mqdev->gadget.name = driver_name;
    +	mqdev->gadget.dev.release = gadget_release;
    +	mqdev->gadget.speed = USB_SPEED_FULL;
    +
    +	/* Initialize UDC */
    +	base->set_power (base, MEDIAQ_11XX_UDC_DEVICE_ID, 1);
    +	/* Enable UDC transceiver */
    +	base->regs->DC.config_4 |= MQ_CONFIG_UDC_TRANSCEIVER_ENABLE;
    +	mqdev->enabled = 1;
    +
    +	dev_set_drvdata(dev, mqdev);
    +
    +	/* Initialize endpoint list */
    +	INIT_LIST_HEAD (&mqdev->gadget.ep_list);
    +	for (i = 0; i < 4; i++) {
    +		struct mq_ep *ep = &mqdev->ep[i];
    +
    +		ep->num = i;
    +		ep->ep.name = names[i];
    +		ep->mqdev = mqdev;
    +		ep->ep.ops = &mq_ep_ops;
    +		/* Endpoint 0 is not on the UDC's list of endpoints */
    +		if (i)
    +			list_add_tail (&ep->ep.ep_list, &mqdev->gadget.ep_list);
    +	}
    +
    +	mqdev->ep[0].ep.maxpacket = MQ_EP01_MAXPACKET;
    +
    +	mqdev->ep[0].is_in = 0;
    +	mqdev->ep[1].is_in = 1;
    +	mqdev->ep[2].is_in = 1;
    +	mqdev->ep[3].is_in = 0;
    +
    +	mqdev->ep[0].regs = &regs->UDC.ep0txcontrol;
    +	mqdev->ep[1].regs = &regs->UDC.ep1control;
    +	mqdev->ep[2].regs = &regs->UDC.ep2control;
    +	mqdev->ep[3].regs = &regs->UDC.ep3control;
    +
    +        mqdev->ep[2].dmaregs = &regs->UDC.dmatxcontrol;
    +	mqdev->ep[3].dmaregs = &regs->UDC.dmarxcontrol;
    +
    +	/* Grab all used IRQs */
    +#ifdef MQ_IRQ_MULTIPLEX
    +	for (i = 0; i < ARRAY_SIZE (mq_irq_desc); i++) {
    +		int irqn = base->irq_base + mq_irq_desc [i].delta;
    +		debug ("Requesting IRQ %d for %s\n", irqn, mq_irq_desc [i].desc);
    +		if (request_irq (irqn, mq_irq_desc [i].handler, SA_INTERRUPT,
    +				 mq_irq_desc [i].desc, mqdev)) {
    +			printk (KERN_ERR "%s: failed to request %s IRQ %d\n",
    +				driver_name, mq_irq_desc [i].desc, irqn);
    +			mqdev->got_irq = i - 1;
    +			mq_remove (dev);
    +			return -EBUSY;
    +		}
    +	}
    +	mqdev->got_irq = ARRAY_SIZE (mq_irq_desc);
    +#endif
    +
    +	/* Init the device to known state */
    +	mq_reset (mqdev);
    +	mq_reinit (mqdev);
    +
    +	/* Allocate two 4k buffers (used in turn) in MediaQ RAM for
    +	   every DMA-aware endpoint for MediaQ internal DMA transfers */
    +	for (i = 2; i <= 3; i++) {
    +		int buffsize = 2 * ((i == 2) ? txbs : rxbs);
    +		mqdev->ep[i].dmabuff = base->alloc (base, buffsize, 0);
    +		if (mqdev->ep[i].dmabuff == (u32)-1) {
    +			printk (KERN_ERR "%s: cannot allocate %d bytes in onboard RAM\n",
    +				driver_name, buffsize);
    +			mq_remove (dev);
    +			return -ENOMEM;
    +		}
    +		else
    +			debug ("Allocated %d bytes in MediaQ RAM, addr = 0x%x\n",
    +			       buffsize, mqdev->ep[i].dmabuff);
    +	}
    +
    +	device_register (&mqdev->gadget.dev);
    +	mqdev->registered = 1;
    +
    +	the_controller = mqdev;
    +
    +	info ("MediaQ 11xx UDC driver successfully initialized\n");
    +	return 0;
    +}
    +
    +static int mq_remove(struct device *dev)
    +{
    +	struct mqudc *mqdev = dev_get_drvdata (dev);
    +	int i;
    +
    +	debug ("\n");
    +
    +	if (!mqdev)
    +		return 0;
    +
    +	/* start with the driver above us */
    +	if (mqdev->driver) {
    +		/* should have been done already by driver model core */
    +		debug ("driver '%s' is still registered\n",
    +			    mqdev->driver->driver.name);
    +		usb_gadget_unregister_driver(mqdev->driver);
    +	}
    +
    +	if (mqdev->base)
    +		mq_reset(mqdev);
    +
    +	for (i = 3; i >= 2; i--)
    +		if (mqdev->ep[i].dmabuff) {
    +			int buffsize = 2 * ((i == 2) ? txbs : rxbs);
    +			debug ("Freeing MediaQ RAM at 0x%x, size %d\n",
    +			       mqdev->ep[i].dmabuff, buffsize);
    +			mqdev->base->free (mqdev->base, mqdev->ep[i].dmabuff,
    +					   buffsize);
    +		}
    +
    +#ifdef MQ_IRQ_MULTIPLEX
    +	for (i = ((int)mqdev->got_irq) - 1; i >= 0; i--) {
    +		int irqn = mqdev->base->irq_base + mq_irq_desc [i].delta;
    +		debug ("Freeing IRQ %d\n", irqn);
    +		free_irq(irqn, mqdev);
    +	}
    +#endif
    +
    +	if (mqdev->enabled) {
    +		/* Disable UDC transceiver */
    +		mqdev->base->regs->DC.config_4 &= ~MQ_CONFIG_UDC_TRANSCEIVER_ENABLE;
    +		mqdev->base->set_power (mqdev->base, MEDIAQ_11XX_UDC_DEVICE_ID, 0);
    +	}
    +
    +        if (mqdev->registered)
    +		device_unregister (&mqdev->gadget.dev);
    +
    +	dev_set_drvdata (dev, NULL);
    +	the_controller = NULL;
    +
    +	kfree (mqdev);
    +	return 0;
    +}
    +
    +static void mq_shutdown(struct device *dev)
    +{
    +	debug ("\n");
    +}
    +
    +static int mq_suspend (struct device *dev, u32 state, u32 level)
    +{
    +	debug ("\n");
    +
    +	return 0;
    +}
    +
    +static int mq_resume (struct device *dev, u32 level)
    +{
    +	debug ("\n");
    +
    +	return 0;
    +}
    +
    +struct device_driver mq_platform_driver = {
    +	.name     = (char *) driver_name,
    +	.probe    = mq_probe,
    +	.remove	  = mq_remove,
    +	.shutdown = mq_shutdown,
    +	.suspend  = mq_suspend,
    +	.resume   = mq_resume
    +};
    +
    +static int __init mq_init(void)
    +{
    +	return driver_register(&mq_platform_driver);
    +}
    +
    +static void __exit mq_exit(void)
    +{
    +	driver_unregister (&mq_platform_driver);
    +}
    +
    +module_init (mq_init);
    +module_exit (mq_exit);
    diff --git a/drivers/usb/gadget/mq11xx_udc.h b/drivers/usb/gadget/mq11xx_udc.h
    new file mode 100644
    --- /dev/null
    +++ b/drivers/usb/gadget/mq11xx_udc.h
    @@ -0,0 +1,115 @@
    +/*
    + * MediaQ 11xx USB Device Controller driver
    + * Based on Goku-S UDC driver
    + *
    + * Copyright (C) 2004 Andrew Zabolotny <zap@homelink.ru>
    + *
    + * This file is subject to the terms and conditions of the GNU General Public
    + * License.  See the file COPYING in the main directory of this archive for
    + * more details.
    + */
    +
    +#ifndef _MQ11XX_UDC_H
    +#define _MQ11XX_UDC_H
    +
    +/* Public structure provided by the platform device */
    +struct mq11xx_udc_mach_info {
    +	int  (*udc_is_connected)(void);         /* do we see host? */
    +	void (*udc_command)(int cmd);
    +#define MQ11XX_UDC_CMD_CONNECT          0       /* let host see us */
    +#define MQ11XX_UDC_CMD_DISCONNECT       1       /* so host won't see us */
    +};
    +
    +#ifdef __MQ11XX_INTERNAL
    +
    +#include "../drivers/platform/mq11xx.h"
    +
    +/* MediaQ UDC request structure.
    + */
    +struct mq_request {
    +	/* The public request structure */
    +	struct usb_request req;
    +	/* The chained list to link requests belonging to one endpoint */
    +	struct list_head queue;
    +};
    +
    +/* USB Endpoint structure. 
    + */
    +struct mq_ep {
    +	/* Public endpoint structure */
    +	struct usb_ep ep;
    +	/* A chained list of mq_request structures */
    +	struct list_head list;
    +	/* Endpoint number */
    +	unsigned num:8;
    +	/* 1 if endpoint is INput (e.g. TX from UDC point of view) */
    +	unsigned is_in:1;
    +	/* 1 if endpoint is stopped */
    +	unsigned stopped:1;
    +	/* 1 if MediaQ RAM has been allocated for this endpoint */
    +	unsigned got_mqram:1;
    +	/* 1 if endpoint configured in isochronous mode */
    +	unsigned iso:1;
    +	/* Toggles between active buffers on DMA transfers */
    +	unsigned active_buffer:1;
    +	/* Number of bytes in quick_buff to send plus one */
    +	unsigned quick_buff_bytes:3;
    +	/* A quick buffer for small transfers
    +	 * (typically used during SETUP phase and for ZLP).
    +	 */
    +	u32 quick_buff;
    +	/* A pointer to MediaQ registers associated with this endpoint */
    +	volatile u32 *regs;
    +	/* A pointer to MediaQ registers responsible for DMA transfers */
    +	volatile u32 *dmaregs;
    +	/* The offset in MediaQ internal RAM of two consecutive buffers
    +	 * used for DMA transfers.
    +	 */
    +	volatile u32 dmabuff;
    +	/* Parent device */
    +	struct mqudc *mqdev;
    +	/* Endpoint descriptor */
    +	const struct usb_endpoint_descriptor *desc;
    +};
    +
    +enum ep0state {
    +	EP0_DISCONNECT,		/* no host */
    +	EP0_SUSPEND,		/* usb suspend */
    +	EP0_IDLE,		/* between STATUS ack and SETUP report */
    +	EP0_IN, EP0_OUT, 	/* data stage */
    +	EP0_STALL,		/* something really bad happened */
    +};
    +
    +/* The USB Device Controller structure.
    + */
    +struct mqudc {
    +	/* Public gadget structure */
    +	struct usb_gadget gadget;
    +	/* Spinlock for accessing the UDC */
    +	spinlock_t lock;
    +	/* All of UDC endpoints */
    +	struct mq_ep ep[4];
    +	/* A pointer to bound gadget driver */
    +	struct usb_gadget_driver *driver;
    +	/* DATA0/DATA1 toggle */
    +	u32 toggle;
    +
    +	/* Current endpoint 0 state */
    +	enum ep0state ep0state;
    +
    +	/* MediaQ 11xx base SoC driver public structure pointer */
    +	struct mediaq11xx_base *base;
    +
    +	/* The following is used for device cleanup */
    +
    +	/* Number of IRQs requested so far */
    +	unsigned got_irq:2;
    +	/* 1 if the UDC is enabled */
    +	unsigned enabled:1;
    +        /* 1 if device has been registered with the kernel */
    +	unsigned registered:1;
    +};
    +
    +#endif
    +
    +#endif /* _MQ11XX_UDC_H */
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at  http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at  http://www.tux.org/lkml/
    

  • Next message: Sean Bruno: "Re: Testing RC kernels [KORG]"