Driver for PLX NET2272 USB device controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Vendor driver and vendor hasn't provided s-o-b tag.  I'm posting this
because we've been keeping it up-to-date and other people might find it
useful.
---
 drivers/usb/gadget/Kconfig        |   19 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/net2272.c      | 2647 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/net2272.h      |  548 ++++++++
 5 files changed, 3223 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/net2272.c
 create mode 100644 drivers/usb/gadget/net2272.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 7f8e83a..e58a7c0 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -432,6 +432,25 @@ config USB_CI13XXX
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_NET2272
+	boolean "PLX NET2272"
+	select USB_GADGET_DUALSPEED
+	help
+	  PLX NET2272 is a USB peripheral controller which supports
+	  both full and high speed USB 2.0 data transfers.
+
+	  It has three configurable endpoints, as well as endpoint zero
+	  (for control transfer).
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "net2272" and force all
+	  gadget drivers to also be dynamically linked.
+
+config USB_NET2272
+	tristate
+	depends on USB_GADGET_NET2272
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_NET2280
 	boolean "NetChip 228x"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index e6017e6..7317300 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -6,6 +6,7 @@ ifeq ($(CONFIG_USB_GADGET_DEBUG),y)
 endif
 
 obj-$(CONFIG_USB_DUMMY_HCD)	+= dummy_hcd.o
+obj-$(CONFIG_USB_NET2272)	+= net2272.o
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
 obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
 obj-$(CONFIG_USB_PXA25X)	+= pxa25x_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 8e0e9a0..68b5148 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -15,6 +15,12 @@
 #ifndef __GADGET_CHIPS_H
 #define __GADGET_CHIPS_H
 
+#ifdef CONFIG_USB_GADGET_NET2272
+#define gadget_is_net2272(g)	!strcmp("net2272", (g)->name)
+#else
+#define gadget_is_net2272(g)	0
+#endif
+
 #ifdef CONFIG_USB_GADGET_NET2280
 #define	gadget_is_net2280(g)	!strcmp("net2280", (g)->name)
 #else
@@ -239,6 +245,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x23;
 	else if (gadget_is_langwell(gadget))
 		return 0x24;
+	else if (gadget_is_net2272(gadget))
+		return 0x25;
 	return -ENOENT;
 }
 
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c
new file mode 100644
index 0000000..f903b98
--- /dev/null
+++ b/drivers/usb/gadget/net2272.c
@@ -0,0 +1,2647 @@
+/*
+ * Driver for PLX NET2272 USB device controller
+ *
+ * Copyright (C) 2005 PLX Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/platform_device.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/gpio.h>
+
+#undef	DEBUG			/* messages on error and most fault paths */
+#undef	VERBOSE			/* extra debug messages (success too) */
+
+#undef PLX_PCI_RDK             /* define to use the PLX pci rdk board for development testing */
+
+#if defined(PLX_PCI_RDK)
+#define DRIVER_DESC		"PLX NET2272 PCI-RDK USB Peripheral Controller"
+#endif
+
+#if !defined(PLX_PCI_RDK)
+#define DRIVER_DESC		"PLX NET2272 USB Peripheral Controller"
+#endif
+
+#define DRIVER_VERSION		"2005 Sept 8"
+
+#define DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+static const char driver_name [] = "net2272";
+static const char driver_desc [] = DRIVER_DESC;
+
+static const char ep0name [] = "ep0";
+static const char *ep_name [] = {
+	ep0name,
+	"ep-a", "ep-b", "ep-c",
+};
+
+/*---------------------------------------------------------------------------*/
+#if !defined(PLX_PCI_RDK)
+/* base - net2272 base address */
+static unsigned long base = 0;
+module_param (base,  ulong, 0644);
+
+/* irq - net2272 irq */
+static ushort irq = 0;
+module_param (irq,  ushort, 0644);
+#endif
+
+/* If present, the NET2272 can use an external DMA controller.
+ * Note that since there is no generic DMA api, some functions, notably
+ * request_dma, start_dma, and cancel_dma will need to be modified for your
+ * platform's particular dma controller.
+ *
+ * If use_dma is disabled, pio will be used instead.
+ */
+static int use_dma = 0;
+module_param (use_dma, bool, 0644);
+
+/* dma_ep: selects the endpoint for use with dma (1=ep-a, 2=ep-b)
+ * The NET2272 can only use dma for a single endpoint at a time.  At some point
+ * this could be modified to allow either endpoint to take control of dma as it
+ * becomes available.
+ *
+ * Note that DMA should not be used on OUT endpoints unless it can be
+ * guaranteed that no short packets will arrive on an IN endpoint while the
+ * DMA operation is pending.  Otherwise the OUT DMA will terminate
+ * prematurely (See NET2272 Errata 630-0213-0101)
+ */
+static ushort dma_ep = 1;
+module_param (dma_ep, ushort, 0644);
+
+/* dma_mode - net2272 dma mode setting (see LOCCTL1 definiton):
+ *	mode 0 == Slow DREQ mode
+ *	mode 1 == Fast DREQ mode
+ *	mode 2 == Burst mode
+ */
+static ushort dma_mode = 2;
+module_param (dma_mode, ushort, 0644);
+
+/* fifo_mode - net2272 buffer configuration:
+ *      mode 0 == ep-{a,b,c} 512db each
+ *      mode 1 == ep-a 1k, ep-{b,c} 512db
+ *      mode 2 == ep-a 1k, ep-b 1k, ep-c 512db
+ *      mode 3 == ep-a 1k, ep-b disabled, ep-c 512db
+ */
+static ushort fifo_mode = 0;
+
+/* "modprobe net2272 fifo_mode=1" etc */
+module_param (fifo_mode, ushort, 0644);
+
+#include "net2272.h"
+
+/*---------------------------------------------------------------------------*/
+
+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined(DEBUG)
+static char *type_string (u8 bmAttributes)
+{
+	switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:    return "bulk";
+	case USB_ENDPOINT_XFER_ISOC:    return "iso";
+	case USB_ENDPOINT_XFER_INT:     return "intr";
+	};
+	return "control";
+}
+
+static char *buf_state_string (unsigned state)
+{
+	switch (state) {
+	case BUFF_FREE:		return "free";
+	case BUFF_VALID:	return "valid";
+	case BUFF_LCL:		return "local";
+	case BUFF_USB:		return "usb";
+	};
+	return "unknown";
+}
+#endif
+
+#ifdef PLX_PCI_RDK
+static char *dma_mode_string (void)
+{
+	switch (dma_mode) {
+	case 0:
+		return "SLOW DREQ";
+	case 1:
+		return "FAST DREQ";
+	case 2:
+		return "BURST";
+	default:
+		return "invalid";
+	}
+}
+#endif
+/*---------------------------------------------------------------------------*/
+
+static int
+net2272_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct net2272		*dev;
+	struct net2272_ep	*ep;
+	u32			max;
+	u8			tmp;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	_ep->maxpacket = max & 0x7fff;
+	ep->desc = desc;
+
+	/* ep_reset() has already been called */
+	ep->stopped = 0;
+
+	/* set speed-dependent max packet */
+	net2272_ep_write (ep, EP_MAXPKT0, max & 0xff);
+	net2272_ep_write (ep, EP_MAXPKT1, (max & 0xff00) >> 8);
+
+	/* set type, direction, address; reset fifo counters */
+	net2272_ep_write (ep, EP_STAT1, 1 << BUFFER_FLUSH);
+	tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	if (tmp == USB_ENDPOINT_XFER_BULK) {
+		/* catch some particularly blatant driver bugs */
+		if ((dev->gadget.speed == USB_SPEED_HIGH
+					&& max != 512)
+				|| (dev->gadget.speed == USB_SPEED_FULL
+					&& max > 64)) {
+			spin_unlock_irqrestore (&dev->lock, flags);
+			return -ERANGE;
+		}
+	}
+	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
+	tmp <<= ENDPOINT_TYPE;
+	tmp |= ((desc->bEndpointAddress & 0x0f) << ENDPOINT_NUMBER);
+	tmp |= (((desc->bEndpointAddress & USB_DIR_IN) ?
+				1 : 0) << ENDPOINT_DIRECTION);
+	tmp |= (1 << ENDPOINT_ENABLE);
+
+	/* for OUT transfers, block the rx fifo until a read is posted */
+	ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+	if (!ep->is_in)
+		net2272_ep_write (ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
+
+	net2272_ep_write (ep, EP_CFG, tmp);
+
+	/* enable irqs */
+	tmp = (1 << ep->num) | net2272_read (dev, IRQENB0);
+	net2272_write (dev, IRQENB0, tmp);
+
+	tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+		| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+		| net2272_ep_read (ep, EP_IRQENB);
+	net2272_ep_write (ep, EP_IRQENB, tmp);
+
+	tmp = desc->bEndpointAddress;
+	DEBUG (dev, "enabled %s (ep%d%s-%s) max %04x cfg %02x\n",
+			_ep->name, tmp & 0x0f, DIR_STRING (tmp),
+			type_string (desc->bmAttributes), max,
+			net2272_ep_read (ep, EP_CFG));
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops net2272_ep_ops;
+
+static void ep_reset (struct net2272_ep *ep)
+{
+	u8			tmp;
+
+	ep->desc = NULL;
+	INIT_LIST_HEAD (&ep->queue);
+
+	ep->ep.maxpacket = ~0;
+	ep->ep.ops = &net2272_ep_ops;
+
+	/* disable irqs, endpoint */
+	net2272_ep_write (ep, EP_IRQENB, 0);
+
+	/* init to our chosen defaults, notably so that we NAK OUT
+	 * packets until the driver queues a read.
+	 */
+	tmp = (1 << NAK_OUT_PACKETS_MODE) | (1 << ALT_NAK_OUT_PACKETS);
+	net2272_ep_write (ep, EP_RSPSET, tmp);
+
+	tmp = (1 << INTERRUPT_MODE) | (1 << HIDE_STATUS_PHASE);
+	if (ep->num != 0)
+		tmp |= (1 << ENDPOINT_TOGGLE) | (1 << ENDPOINT_HALT);
+
+	net2272_ep_write (ep, EP_RSPCLR, tmp);
+
+	/* scrub most status bits, and flush any fifo state */
+	net2272_ep_write (ep, EP_STAT0,
+			  (1 << DATA_IN_TOKEN_INTERRUPT)
+			| (1 << DATA_OUT_TOKEN_INTERRUPT)
+			| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+			| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+			| (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT));
+
+	net2272_ep_write (ep, EP_STAT1,
+			    (1 << TIMEOUT)
+			  | (1 << USB_OUT_ACK_SENT)
+			  | (1 << USB_OUT_NAK_SENT)
+			  | (1 << USB_IN_ACK_RCVD)
+			  | (1 << USB_IN_NAK_SENT)
+			  | (1 << USB_STALL_SENT)
+			  | (1 << LOCAL_OUT_ZLP)
+			  | (1 << BUFFER_FLUSH));
+
+	/* fifo size is handled seperately */
+}
+
+static void nuke (struct net2272_ep *);
+
+static int net2272_disable (struct usb_ep *_ep)
+{
+	struct net2272_ep	*ep;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || !ep->desc || _ep->name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	nuke (ep);
+	ep_reset (ep);
+
+	VDEBUG (ep->dev, "disabled %s\n", _ep->name);
+
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static struct usb_request *
+net2272_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct net2272_ep	*ep;
+	struct net2272_request	*req;
+
+	if (!_ep)
+		return NULL;
+	ep = container_of (_ep, struct net2272_ep, ep);
+
+	req = kmalloc (sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	memset (req, 0, sizeof *req);
+	req->req.dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD (&req->queue);
+
+	return &req->req;
+}
+
+static void
+net2272_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2272_ep	*ep;
+	struct net2272_request	*req;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || !_req)
+		return;
+
+	req = container_of (_req, struct net2272_request, req);
+	WARN_ON (!list_empty (&req->queue));
+	kfree (req);
+}
+
+static void
+done (struct net2272_ep *ep, struct net2272_request *req, int status)
+{
+	struct net2272		*dev;
+	unsigned		stopped = ep->stopped;
+
+	if (ep->num == 0) {
+		if (ep->dev->protocol_stall) {
+			ep->stopped = 1;
+			set_halt (ep);
+		}
+		allow_status (ep);
+	}
+
+	list_del_init (&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	if (req->mapped) {
+		pci_unmap_single ((struct pci_dev*)dev->pdev, req->req.dma,
+			req->req.length, ep->is_in ? PCI_DMA_TODEVICE : \
+			PCI_DMA_FROMDEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	}
+
+	if (status && status != -ESHUTDOWN)
+		VDEBUG (dev, "complete %s req %p stat %d len %u/%u buf %p\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length, req->req.buf);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock (&dev->lock);
+	req->req.complete (&ep->ep, &req->req);
+	spin_lock (&dev->lock);
+	ep->stopped = stopped;
+}
+
+static int
+write_packet (struct net2272_ep *ep,
+		u8 *buf, struct net2272_request *req, unsigned max)
+{
+#ifdef CONFIG_BLACKFIN
+	u16		__iomem *ep_data =(u16*) (ep->dev->base_addr + EP_DATA);
+#else
+	u16		__iomem *ep_data = ep->dev->base_addr + EP_DATA;
+#endif
+	unsigned	length, count;
+	u16		*bufp;
+	u8		tmp;
+
+	length = min (req->req.length - req->req.actual, max);
+	req->req.actual += length;
+
+#if 0
+	VDEBUG (ep->dev, "write packet %s req %p max %u len %u avail %u\n",
+			ep->ep.name, req, max, length,
+			(net2272_ep_read (ep, EP_AVAIL1) << 8)
+				| net2272_ep_read (ep, EP_AVAIL0));
+#endif
+	count = length;
+	bufp = (u16 *)buf;
+
+	while (likely (count >= 2)) {
+		/* no byte-swap required; chip endian set during init */
+		writew (*bufp++, ep_data);
+		count -= 2;
+	}
+	buf = (u8 *)bufp;
+
+	/* write final byte by placing the NET2272 into 8-bit mode */
+	if (unlikely (count)) {
+		tmp = net2272_read (ep->dev, LOCCTL);
+		net2272_write (ep->dev, LOCCTL, tmp & ~(1 << DATA_WIDTH));
+		writeb (*buf, ep_data);
+		net2272_write (ep->dev, LOCCTL, tmp);
+	}
+	return length;
+}
+
+static int kick_dma (struct net2272_ep *ep, struct net2272_request *req);
+
+/* returns: 0: still running, 1: completed, negative: errno */
+static int write_fifo (struct net2272_ep *ep, struct net2272_request *req)
+{
+	u8		*buf;
+	unsigned	count, max;
+	int		is_last;
+	int		status;
+
+#if 0
+	VDEBUG (ep->dev, "write_fifo %s actual %d len %d\n",
+			ep->ep.name, req->req.actual, req->req.length);
+#endif
+
+top:
+	while (!(net2272_ep_read (ep, EP_STAT0) & (1 << BUFFER_FULL))) {
+		buf = req->req.buf + req->req.actual;
+		prefetch (buf);
+
+		/* force pagesel */
+		net2272_ep_read (ep, EP_STAT0);
+
+		max = (net2272_ep_read (ep, EP_AVAIL1) << 8) |
+			(net2272_ep_read (ep, EP_AVAIL0));
+
+		if (max < ep->ep.maxpacket)
+			max = (net2272_ep_read (ep, EP_AVAIL1) << 8)
+				| (net2272_ep_read (ep, EP_AVAIL0));
+
+		count = write_packet (ep, buf, req, max);
+
+		/* validate short packet */
+		if (unlikely (count < ep->ep.maxpacket)) {
+			set_fifo_bytecount (ep, 0);
+			is_last = 1;
+		} else {
+			if (likely (req->req.length != req->req.actual)
+					|| req->req.zero)
+				is_last = 0;
+			else
+				is_last = 1;
+		}
+
+		if (is_last) {
+			done (ep, req, 0);
+
+			if (!list_empty (&ep->queue)) {
+				req = list_entry (ep->queue.next,
+						struct net2272_request,
+						queue);
+				status = kick_dma (ep, req);
+
+				if (status < 0)
+					if ((net2272_ep_read (ep, EP_STAT0)
+							& (1 << BUFFER_EMPTY)))
+						goto top;
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void out_flush (struct net2272_ep *ep)
+{
+	ASSERT_OUT_NAKING (ep);
+
+	net2272_ep_write (ep, EP_STAT0, (1 << DATA_OUT_TOKEN_INTERRUPT)
+			| (1 << DATA_PACKET_RECEIVED_INTERRUPT));
+	net2272_ep_write (ep, EP_STAT1, 1 << BUFFER_FLUSH);
+}
+
+static int
+read_packet (struct net2272_ep *ep,
+		u8 *buf, struct net2272_request *req, unsigned avail)
+{
+#ifdef CONFIG_BLACKFIN
+	u16			__iomem *ep_data = (u16*)(ep->dev->base_addr + EP_DATA);
+#else
+	u16			__iomem *ep_data = ep->dev->base_addr + EP_DATA;
+#endif
+	unsigned		is_short;
+	u16			*bufp;
+
+	req->req.actual += avail;
+#if 0
+	VDEBUG (ep->dev, "read packet %s req %p len %u avail %u\n",
+			ep->ep.name, req, avail,
+			(net2272_ep_read (ep, EP_AVAIL1) << 8)
+				| net2272_ep_read (ep, EP_AVAIL0));
+#endif
+	is_short = (avail < ep->ep.maxpacket);
+
+	if (unlikely (avail == 0)) {
+		/* remove any zlp from the buffer */
+		(void)readw (ep_data);
+		return is_short;
+	}
+
+	/* Ensure we get the final byte */
+	if (unlikely (avail % 2))
+		avail++;
+	bufp = (u16 *)buf;
+
+	do {
+		*bufp++ = readw (ep_data);
+		avail -= 2;
+	} while (avail);
+
+	// To avoid false endpoint available race condition must read ep stat0 twice in the case
+	// of a short transfer
+	if (net2272_ep_read (ep, EP_STAT0) & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT))
+	{
+		net2272_ep_read (ep, EP_STAT0);
+	}
+
+	return is_short;
+}
+
+static int read_fifo (struct net2272_ep *ep, struct net2272_request *req)
+{
+	u8		*buf;
+	unsigned	is_short;
+	int		count;
+	int		tmp;
+	int		cleanup = 0;
+	int		status = -1;
+
+#if 0
+	VDEBUG (ep->dev, "read_fifo %s actual %d len %d\n",
+			ep->ep.name, req->req.actual, req->req.length);
+#endif
+top:
+	do {
+		buf = req->req.buf + req->req.actual;
+		prefetchw (buf);
+
+		count = (net2272_ep_read (ep, EP_AVAIL1) << 8)
+			| net2272_ep_read (ep, EP_AVAIL0);
+
+		net2272_ep_write (ep, EP_STAT0,
+				(1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
+			      | (1 << DATA_PACKET_RECEIVED_INTERRUPT));
+
+		tmp = req->req.length - req->req.actual;
+
+		if (count > tmp) {
+			if ((tmp % ep->ep.maxpacket) != 0) {
+				ERROR (ep->dev,
+					"%s out fifo %d bytes, expected %d\n",
+						ep->ep.name, count, tmp);
+				cleanup = 1;
+			}
+			count = (tmp > 0) ? tmp : 0;
+		}
+
+		is_short = read_packet (ep, buf, req, count);
+
+		/* completion */
+		if (unlikely (cleanup || is_short ||
+				((req->req.actual == req->req.length)
+				 && !req->req.zero))) {
+
+			if (cleanup) {
+				out_flush (ep);
+				done (ep, req, -EOVERFLOW);
+			} else
+				done (ep, req, 0);
+
+			/* re-initialize endpoint transfer registers
+			 * otherwise they may result in erroneous pre-validation
+			 * for subsequent control reads
+			 */
+			if (unlikely (ep->num == 0)) {
+				net2272_ep_write (ep, EP_TRANSFER2, 0);
+				net2272_ep_write (ep, EP_TRANSFER1, 0);
+				net2272_ep_write (ep, EP_TRANSFER0, 0);
+			}
+
+			if (!list_empty (&ep->queue)) {
+				req = list_entry (ep->queue.next,
+						struct net2272_request, queue);
+				status = kick_dma (ep, req);
+				if ((status < 0) && !net2272_ep_read (ep,
+							EP_STAT0) &
+						(1 << BUFFER_EMPTY))
+					goto top;
+			}
+			return 1;
+		}
+	} while (!(net2272_ep_read (ep, EP_STAT0) & (1 << BUFFER_EMPTY)));
+	return 0;
+}
+
+static inline void
+pio_advance (struct net2272_ep *ep)
+{
+	struct net2272_request	*req;
+
+	if (unlikely (list_empty (&ep->queue)))
+		return;
+	req = list_entry (ep->queue.next, struct net2272_request, queue);
+	(ep->is_in ? write_fifo : read_fifo)(ep, req);
+}
+
+/* returns 0 on success, else negative errno */
+static inline int
+request_dma (struct net2272 *dev, unsigned ep, u32 buf, unsigned len,
+		unsigned dir)
+{
+	VDEBUG (dev, "request_dma ep %d buf %08x len %d dir %d\n",
+			ep, buf, len, dir);
+
+	/* The NET2272 only supports a single dma channel */
+	if (dev->dma_busy)
+		return -EBUSY;
+	/* EP_TRANSFER (used to determine the number of bytes received
+	 * in an OUT transfer) is 24 bits wide; don't ask for more than that.
+	 */
+	if ((dir == 1) && (len > 0x1000000))
+		return -EINVAL;
+
+	dev->dma_busy = 1;
+
+	/* initialize platform's dma */
+	/* NET2272 addr, buffer addr, length, etc. */
+#if defined (PLX_PCI_RDK)
+	/* Setup PLX 9054 DMA mode */
+	writel (  (1 << LOCAL_BUS_WIDTH)
+		| (1 << TA_READY_INPUT_ENABLE)
+		| (0 << LOCAL_BURST_ENABLE)
+		| (1 << DONE_INTERRUPT_ENABLE)
+		| (1 << LOCAL_ADDRESSING_MODE)
+		| (1 << DEMAND_MODE)
+		| (1 << DMA_EOT_ENABLE)
+		| (1 << FAST_SLOW_TERMINATE_MODE_SELECT)
+		| (1 << DMA_CHANNEL_INTERRUPT_SELECT),
+		dev->plx9054_base_addr + DMAMODE0);
+
+	writel (0x100000, dev->plx9054_base_addr + DMALADR0);
+	writel (buf, dev->plx9054_base_addr + DMAPADR0);
+	writel (len, dev->plx9054_base_addr + DMASIZ0);
+	writel ((dir << DIRECTION_OF_TRANSFER)
+			| (1 << INTERRUPT_AFTER_TERMINAL_COUNT),
+			dev->plx9054_base_addr + DMADPR0);
+	writel ((1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE) |
+			readl (dev->plx9054_base_addr + INTCSR),
+			dev->plx9054_base_addr + INTCSR);
+#endif
+	net2272_write (dev, DMAREQ,
+		  (0 << DMA_BUFFER_VALID)
+		| (1 << DMA_REQUEST_ENABLE)
+		| (1 << DMA_CONTROL_DACK)
+		| (dev->dma_eot_polarity << EOT_POLARITY)
+		| (dev->dma_dack_polarity << DACK_POLARITY)
+		| (dev->dma_dreq_polarity << DREQ_POLARITY)
+		| ((ep >> 1) << DMA_ENDPOINT_SELECT));
+
+	(void) net2272_read (dev, SCRATCH);
+
+	return 0;
+}
+
+static inline void
+start_dma (struct net2272 *dev)
+{
+	/* start platform's dma controller */
+#if defined(PLX_PCI_RDK)
+	writeb ((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START),
+			dev->plx9054_base_addr + DMACSR0);
+#endif
+}
+
+/* returns 0 on success, else negative errno */
+static int kick_dma (struct net2272_ep *ep, struct net2272_request *req)
+{
+	unsigned	size;
+	u8		tmp;
+
+	if (!use_dma || (ep->num < 1) || (ep->num > 2) || !ep->dma)
+		return -EINVAL;
+
+	/* don't use dma for odd-length transfers
+	 * otherwise, we'd need to deal with the last byte with pio
+	 */
+	if (req->req.length & 1)
+		return -EINVAL;
+
+	VDEBUG (ep->dev, "kick_dma %s req %p dma %08x\n",
+			ep->ep.name, req, req->req.dma);
+
+	net2272_ep_write (ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
+
+	/* The NET2272 can only use DMA on one endpoint at a time */
+	if (ep->dev->dma_busy)
+		return -EBUSY;
+
+	/* Make sure we only DMA an even number of bytes (we'll use
+	 * pio to complete the transfer)
+	 */
+	size = req->req.length;
+	size &= ~1;
+
+	/* device-to-host transfer */
+	if (ep->is_in) {
+		/* initialize platform's dma controller */
+		if ((request_dma (ep->dev, ep->num, req->req.dma, size, 0))
+				< 0) {
+			/* unable to obtain DMA channel; return error and use
+			 * pio mode.
+			 */
+			return -EBUSY;
+		}
+		req->req.actual += size;
+
+	/* host-to-device transfer */
+	} else {
+		tmp = net2272_ep_read (ep, EP_STAT0);
+
+		/* initialize platform's dma controller */
+		if ((request_dma (ep->dev, ep->num, req->req.dma, size, 1))
+				< 0) {
+			/* unable to obtain DMA channel; return error and use
+			 * pio mode.
+			 */
+			return -EBUSY;
+		}
+
+		if (!(tmp & (1 << BUFFER_EMPTY)))
+			ep->not_empty = 1;
+		else
+			ep->not_empty = 0;
+
+
+		/* allow the endpoint's buffer to fill */
+		net2272_ep_write (ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
+
+		/* this transfer completed and data's already in the fifo
+		 * return error so pio gets used.
+		 */
+		if (tmp & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+
+			/* deassert dreq */
+			net2272_write (ep->dev, DMAREQ,
+				  (0 << DMA_BUFFER_VALID)
+				| (0 << DMA_REQUEST_ENABLE)
+				| (1 << DMA_CONTROL_DACK)
+				| (ep->dev->dma_eot_polarity << EOT_POLARITY)
+				| (ep->dev->dma_dack_polarity << DACK_POLARITY)
+				| (ep->dev->dma_dreq_polarity << DREQ_POLARITY)
+				| ((ep->num >> 1) << DMA_ENDPOINT_SELECT));
+
+			return -EBUSY;
+		}
+	}
+
+	/* Don't use per-packet interrupts: use dma interrupts only */
+	net2272_ep_write (ep, EP_IRQENB, 0);
+
+	start_dma (ep->dev);
+
+	return 0;
+}
+
+static inline void cancel_dma (struct net2272 *dev)
+{
+#if defined(PLX_PCI_RDK)
+	writeb (0, dev->plx9054_base_addr + DMACSR0);
+	writeb (1 << CHANNEL_ABORT, dev->plx9054_base_addr + DMACSR0);
+	while (!(readb (dev->plx9054_base_addr + DMACSR0)
+				& (1 << CHANNEL_DONE)))
+		;	/* wait for dma to stabalize */
+
+	/* dma abort generates an interrupt */
+	writeb (1 << CHANNEL_CLEAR_INTERRUPT, dev->plx9054_base_addr + DMACSR0);
+#endif
+	dev->dma_busy = 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int
+net2272_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct net2272_request	*req;
+	struct net2272_ep	*ep;
+	struct net2272		*dev;
+	unsigned long		flags;
+	int			status = -1;
+	u8			s;
+
+	req = container_of (_req, struct net2272_request, req);
+	if (!_req || !_req->complete || !_req->buf
+			|| !list_empty (&req->queue))
+		return -EINVAL;
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* set up dma mapping in case the caller didn't */
+	if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
+		_req->dma = pci_map_single ((struct pci_dev*)dev->pdev,
+			_req->buf, _req->length, ep->is_in ? PCI_DMA_TODEVICE \
+			: PCI_DMA_FROMDEVICE);
+		req->mapped = 1;
+	}
+
+#if 0
+	VDEBUG (dev, "%s queue req %p, len %d buf %p dma %08x %s\n",
+			_ep->name, _req, _req->length, _req->buf,
+			_req->dma, _req->zero ? "zero" : "!zero");
+#endif
+
+	spin_lock_irqsave (&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	if (list_empty (&ep->queue) && !ep->stopped) {
+		/* maybe there's no control data, just status ack */
+		if (ep->num == 0 && _req->length == 0) {
+			done (ep, req, 0);
+			VDEBUG (dev, "%s status ack\n", ep->ep.name);
+			goto done;
+		}
+
+		// Return zlp, don't let it block subsequent packets
+		s = net2272_ep_read (ep, EP_STAT0);
+		if (s & (1 << BUFFER_EMPTY))
+		{
+			// Buffer is empty check for a blocking zlp, handle it
+			if ((s & (1 << NAK_OUT_PACKETS)) && net2272_ep_read (ep, EP_STAT1) & (1 << LOCAL_OUT_ZLP))
+			{
+				DEBUG(dev, "WARNING: returning ZLP short packet termination!\n");
+				// Request is going to terminate with a short packet
+				// hope client is ready for it!
+				status = read_fifo (ep, req);
+				// clear short packet naking
+				net2272_ep_write (ep, EP_STAT0, (1 << NAK_OUT_PACKETS));
+				goto done;
+			}
+		}
+
+		/* try dma first */
+		status = kick_dma (ep, req);
+
+		if (status < 0) {
+			/* dma failed (most likely in use by another endpoint)
+			 * fallback to pio
+			 */
+			status = 0;
+
+			if (ep->is_in)
+				status = write_fifo (ep, req);
+			else {
+				s = net2272_ep_read (ep, EP_STAT0);
+				if ((s & (1 << BUFFER_EMPTY)) == 0)
+					status = read_fifo (ep, req);
+			}
+
+			if (unlikely (status != 0)) {
+				if (status > 0)
+					status = 0;
+				req = NULL;
+			}
+		}
+	}
+	if (likely (req != 0))
+		list_add_tail (&req->queue, &ep->queue);
+
+	if (likely (!list_empty (&ep->queue)))
+		net2272_ep_write (ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
+done:
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return 0;
+}
+
+/* dequeue ALL requests */
+static void nuke (struct net2272_ep *ep)
+{
+	struct net2272_request	*req;
+
+	/* called with spinlock held */
+	ep->stopped = 1;
+
+	while (!list_empty (&ep->queue)) {
+		req = list_entry (ep->queue.next,
+				struct net2272_request,
+				queue);
+		done (ep, req, -ESHUTDOWN);
+	}
+}
+
+/* dequeue JUST ONE request */
+static int net2272_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2272_ep		*ep;
+	struct net2272_request		*req;
+	unsigned long			flags;
+	int				stopped;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	stopped = ep->stopped;
+	ep->stopped = 1;
+
+	/* make sure it's still queued on this endpoint */
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		spin_unlock_irqrestore (&ep->dev->lock, flags);
+		return -EINVAL;
+	}
+
+	/* queue head may be partially complete */
+	if (ep->queue.next == &req->queue) {
+		DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
+		done (ep, req, -ECONNRESET);
+	}
+	req = NULL;
+	ep->stopped = stopped;
+
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int net2272_fifo_status (struct usb_ep *_ep);
+
+static int
+net2272_set_halt (struct usb_ep *_ep, int value)
+{
+	struct net2272_ep	*ep;
+	unsigned long		flags;
+	int			retval = 0;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+	if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
+			== USB_ENDPOINT_XFER_ISOC)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	if (!list_empty (&ep->queue))
+		retval = -EAGAIN;
+	else if (ep->is_in && value && net2272_fifo_status (_ep) != 0)
+		retval = -EAGAIN;
+	else {
+		VDEBUG (ep->dev, "%s %s halt\n", _ep->name,
+				value ? "set" : "clear");
+		/* set/clear */
+		if (value) {
+			if (ep->num == 0)
+				ep->dev->protocol_stall = 1;
+			else
+				set_halt (ep);
+		}
+		else
+			clear_halt (ep);
+	}
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+
+	return retval;
+}
+
+static int
+net2272_fifo_status (struct usb_ep *_ep)
+{
+	struct net2272_ep	*ep;
+	u16			avail;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -ENODEV;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	avail = net2272_ep_read (ep, EP_AVAIL1) << 8;
+	avail |= net2272_ep_read (ep, EP_AVAIL0);
+	if (avail > ep->fifo_size)
+		return -EOVERFLOW;
+	if (ep->is_in)
+		avail = ep->fifo_size - avail;
+	return avail;
+}
+
+static void
+net2272_fifo_flush (struct usb_ep *_ep)
+{
+	struct net2272_ep	*ep;
+
+	ep = container_of (_ep, struct net2272_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return;
+
+	net2272_ep_write (ep, EP_STAT1, 1 << BUFFER_FLUSH);
+}
+
+static struct usb_ep_ops net2272_ep_ops = {
+	.enable		= net2272_enable,
+	.disable	= net2272_disable,
+
+	.alloc_request	= net2272_alloc_request,
+	.free_request	= net2272_free_request,
+
+	.queue		= net2272_queue,
+	.dequeue	= net2272_dequeue,
+
+	.set_halt	= net2272_set_halt,
+	.fifo_status	= net2272_fifo_status,
+	.fifo_flush	= net2272_fifo_flush,
+};
+
+/*---------------------------------------------------------------------------*/
+
+static int net2272_get_frame (struct usb_gadget *_gadget)
+{
+	struct net2272		*dev;
+	unsigned long		flags;
+	u16			retval;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of (_gadget, struct net2272, gadget);
+	spin_lock_irqsave (&dev->lock, flags);
+
+	retval = net2272_read (dev, FRAME1) << 8;
+	retval |= net2272_read (dev, FRAME0);
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return retval;
+}
+
+static int net2272_wakeup (struct usb_gadget *_gadget)
+{
+	struct net2272		*dev;
+	u8			tmp;
+	unsigned long		flags;
+
+	if (!_gadget)
+		return 0;
+	dev = container_of (_gadget, struct net2272, gadget);
+
+	spin_lock_irqsave (&dev->lock, flags);
+	tmp = net2272_read (dev, USBCTL0);
+	if (tmp & (1 << IO_WAKEUP_ENABLE))
+		net2272_write (dev, USBCTL1, (1 << GENERATE_RESUME));
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return 0;
+}
+
+static int net2272_set_selfpowered (struct usb_gadget *_gadget, int value)
+{
+	struct net2272		*dev;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of (_gadget, struct net2272, gadget);
+
+	dev->is_selfpowered = value;
+
+	return 0;
+}
+
+static int net2272_pullup (struct usb_gadget *_gadget, int is_on)
+{
+	struct net2272		*dev;
+	u8			tmp;
+	unsigned long		flags;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of (_gadget, struct net2272, gadget);
+
+	spin_lock_irqsave (&dev->lock, flags);
+	tmp = net2272_read (dev, USBCTL0);
+	dev->softconnect = (is_on != 0);
+	if (is_on)
+		tmp |= (1 << USB_DETECT_ENABLE);
+	else
+		tmp &= ~(1 << USB_DETECT_ENABLE);
+	net2272_write (dev, USBCTL0, tmp);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops net2272_ops = {
+	.get_frame		= net2272_get_frame,
+	.wakeup			= net2272_wakeup,
+	.set_selfpowered	= net2272_set_selfpowered,
+	.pullup			= net2272_pullup
+};
+
+/*---------------------------------------------------------------------------*/
+
+static ssize_t
+show_registers (struct device *_dev, struct device_attribute *attr, char *buf)
+{
+	struct net2272		*dev;
+	char			*next;
+	unsigned		size, t;
+	unsigned long		flags;
+	u8			t1, t2;
+	int			i;
+	const char		*s;
+
+	dev = dev_get_drvdata (_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave (&dev->lock, flags);
+
+	if (dev->driver)
+		s = dev->driver->driver.name;
+	else
+		s = "(none)";
+
+	/* Main Control Registers */
+	t = scnprintf (next, size, "%s version " DRIVER_VERSION
+			", chiprev %02x, locctl %02x\n"
+			"irqenb0 %02x irqenb1 %02x "
+			"irqstat0 %02x irqstat1 %02x\n",
+			driver_name, dev->chiprev,
+			net2272_read (dev, LOCCTL),
+			net2272_read (dev, IRQENB0),
+			net2272_read (dev, IRQENB1),
+			net2272_read (dev, IRQSTAT0),
+			net2272_read (dev, IRQSTAT1));
+	size -= t;
+	next += t;
+
+	/* DMA */
+	t1 = net2272_read (dev, DMAREQ);
+	t = scnprintf (next, size, "\ndmareq %02x: %s %s%s%s%s\n",
+			t1, ep_name [(t1 & 0x01) + 1],
+			t1 & (1 << DMA_CONTROL_DACK) ? "dack " : "",
+			t1 & (1 << DMA_REQUEST_ENABLE) ? "reqenb " : "",
+			t1 & (1 << DMA_REQUEST) ? "req " : "",
+			t1 & (1 << DMA_BUFFER_VALID) ? "valid " : "");
+	size -= t;
+	next += t;
+
+	/* USB Control Registers */
+	t1 = net2272_read (dev, USBCTL1);
+	if (t1 & (1 << VBUS_PIN)) {
+		if (t1 & (1 << USB_HIGH_SPEED))
+			s = "high speed";
+		else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+			s = "powered";
+		else
+			s = "full speed";
+	} else
+		s = "not attached";
+	t = scnprintf (next, size,
+			"usbctl0 %02x usbctl1 %02x addr 0x%02x (%s)\n",
+			net2272_read (dev, USBCTL0), t1,
+			net2272_read (dev, OURADDR), s);
+	size -= t;
+	next += t;
+
+	/* Endpoint Registers */
+	for (i = 0; i < 4; i ++) {
+		struct net2272_ep	*ep;
+
+		ep = &dev->ep [i];
+		if (i && !ep->desc)
+			continue;
+
+		t1 = net2272_ep_read (ep, EP_CFG);
+		t2 = net2272_ep_read (ep, EP_RSPSET);
+		t = scnprintf (next, size,
+				"\n%s\tcfg %02x rsp (%02x) %s%s%s%s%s%s%s%s"
+				"irqenb %02x\n",
+				ep->ep.name, t1, t2,
+				(t2 & (1 << ALT_NAK_OUT_PACKETS))
+					? "NAK " : "",
+				(t2 & (1 << HIDE_STATUS_PHASE))
+					? "hide " : "",
+				(t2 & (1 << AUTOVALIDATE))
+					? "auto " : "",
+				(t2 & (1 << INTERRUPT_MODE))
+					? "interrupt " : "",
+				(t2 & (1 << CONTROL_STATUS_PHASE_HANDSHAKE))
+					? "status " : "",
+				(t2 & (1 << NAK_OUT_PACKETS_MODE))
+					? "NAKmode " : "",
+				(t2 & (1 << ENDPOINT_TOGGLE))
+					? "DATA1 " : "DATA0 ",
+				(t2 & (1 << ENDPOINT_HALT))
+					? "HALT " : "",
+				net2272_ep_read (ep, EP_IRQENB));
+		size -= t;
+		next += t;
+
+		t = scnprintf (next, size,
+			"\tstat0 %02x stat1 %02x avail %04x "
+			"(ep%d%s-%s)%s\n",
+			net2272_ep_read (ep, EP_STAT0),
+			net2272_ep_read (ep, EP_STAT1),
+			(net2272_ep_read (ep, EP_AVAIL1) << 8) |
+				net2272_ep_read (ep, EP_AVAIL0),
+			t1 & 0x0f,
+			ep->is_in ? "in" : "out",
+			type_string (t1 >> 5),
+			ep->stopped ? "*" : "");
+		size -= t;
+		next += t;
+
+		t = scnprintf (next, size,
+			"\tep_transfer %06x\n",
+			((net2272_ep_read (ep, EP_TRANSFER2) & 0xff) << 16) |
+			((net2272_ep_read (ep, EP_TRANSFER1) & 0xff) << 8) |
+			((net2272_ep_read (ep, EP_TRANSFER0) & 0xff)));
+		size -= t;
+		next += t;
+
+
+
+		t1 = net2272_ep_read (ep, EP_BUFF_STATES) & 0x03;
+		t2 = (net2272_ep_read (ep, EP_BUFF_STATES) >> 2) & 0x03;
+		t = scnprintf (next, size,
+				"\tbuf-a %s buf-b %s\n",
+				buf_state_string (t1),
+				buf_state_string (t2));
+		size -= t;
+		next += t;
+	}
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+/*---------------------------------------------------------------------------*/
+
+static void set_fifo_mode (struct net2272 *dev, int mode)
+{
+	u8	tmp;
+
+	tmp = net2272_read (dev, LOCCTL) & 0x3f;
+	tmp |= (mode << 6);
+	net2272_write (dev, LOCCTL, tmp);
+
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+
+	/* always ep-a, ep-c ... maybe not ep-b */
+	list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list);
+
+	switch (mode) {
+	case 0:
+		list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = dev->ep [2].fifo_size = 512;
+		break;
+	case 1:
+		list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = 1024;
+		dev->ep [2].fifo_size = 512;
+		break;
+	case 2:
+		list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024;
+		break;
+	case 3:
+		dev->ep [1].fifo_size = 1024;
+		break;
+	}
+
+	/* ep-c is always 2 512 byte buffers */
+	list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
+	dev->ep [3].fifo_size = 512;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static struct net2272	*the_controller;
+
+static void usb_reset (struct net2272 *dev)
+{
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	cancel_dma (dev);
+
+	net2272_write (dev, IRQENB0, 0);
+	net2272_write (dev, IRQENB1, 0);
+
+	/* clear irq state */
+	net2272_write (dev, IRQSTAT0, 0xff);
+	net2272_write (dev, IRQSTAT1, ~(1 << SUSPEND_REQUEST_INTERRUPT));
+
+	net2272_write (dev, DMAREQ,
+		  (0 << DMA_BUFFER_VALID)
+		| (0 << DMA_REQUEST_ENABLE)
+		| (1 << DMA_CONTROL_DACK)
+		| (dev->dma_eot_polarity << EOT_POLARITY)
+		| (dev->dma_dack_polarity << DACK_POLARITY)
+		| (dev->dma_dreq_polarity << DREQ_POLARITY)
+		| ((dma_ep >> 1) << DMA_ENDPOINT_SELECT));
+
+#if defined(PLX_PCI_RDK)
+	/* disable split dma bus mode */
+	*((u8 *)dev->base_addr + EPLD_DMA_CONTROL_REGISTER) =
+		(dma_mode << EPLD_DMA_MODE);
+#endif
+	set_fifo_mode (dev, (fifo_mode <= 3) ? fifo_mode : 0);
+
+	/* Set the NET2272 ep fifo data width to 16-bit mode and for correct byte swapping
+	 * note that the higher level gadget drivers are expected to convert data to little endian.
+	 * Enable byte swap for your local bus/cpu if needed by setting BYTE_SWAP in LOCCTL here
+	 */
+	net2272_write (dev, LOCCTL, net2272_read (dev, LOCCTL) | (1 << DATA_WIDTH));
+	net2272_write (dev, LOCCTL1, (dma_mode << DMA_MODE));
+}
+
+static void usb_reinit (struct net2272 *dev)
+{
+	int	i;
+
+	/* basic endpoint init */
+	for (i = 0; i < 4; i++) {
+		struct net2272_ep	*ep = &dev->ep [i];
+
+		ep->ep.name = ep_name [i];
+		ep->dev = dev;
+		ep->num = i;
+		ep->not_empty = 0;
+
+		if (use_dma && ep->num == dma_ep)
+			ep->dma = 1;
+
+		if (i > 0 && i <= 3)
+			ep->fifo_size = 512;
+		else
+			ep->fifo_size = 64;
+		ep_reset (ep);
+	}
+	dev->ep [0].ep.maxpacket = 64;
+
+	dev->gadget.ep0 = &dev->ep [0].ep;
+	dev->ep [0].stopped = 0;
+	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+}
+
+static void ep0_start (struct net2272 *dev)
+{
+	struct net2272_ep	*ep0 = &dev->ep [0];
+
+	net2272_ep_write (ep0, EP_RSPSET,
+			  (1 << NAK_OUT_PACKETS_MODE)
+			| (1 << ALT_NAK_OUT_PACKETS));
+	net2272_ep_write (ep0, EP_RSPCLR,
+			  (1 << HIDE_STATUS_PHASE)
+			| (1 << CONTROL_STATUS_PHASE_HANDSHAKE));
+	net2272_write (dev, USBCTL0,
+			  (dev->softconnect << USB_DETECT_ENABLE)
+			| (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
+			| (1 << IO_WAKEUP_ENABLE));
+	net2272_write (dev, IRQENB0,
+			  (1 << SETUP_PACKET_INTERRUPT_ENABLE)
+			| (1 << ENDPOINT_0_INTERRUPT_ENABLE)
+			| (1 << DMA_DONE_INTERRUPT_ENABLE));
+	net2272_write (dev, IRQENB1,
+			  (1 << VBUS_INTERRUPT_ENABLE)
+			| (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE)
+			| (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE));
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+	struct net2272		*dev = the_controller;
+	int			retval;
+	unsigned		i;
+
+	if (!driver
+			|| driver->speed != USB_SPEED_HIGH
+			|| !driver->bind
+			|| !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+
+	for (i = 0; i < 4; i++)
+		dev->ep [i].irqs = 0;
+	/* hook up the driver ... */
+	dev->softconnect = 1;
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+	dev->gadget.dev.driver = &driver->driver;
+	retval = driver->bind (&dev->gadget);
+	if (retval) {
+		DEBUG (dev, "bind to driver %s --> %d\n",
+				driver->driver.name, retval);
+		dev->driver = NULL;
+		dev->gadget.dev.driver = NULL;
+		return retval;
+	}
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	ep0_start (dev);
+
+	DEBUG (dev, "%s ready\n", driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+static void
+stop_activity (struct net2272 *dev, struct usb_gadget_driver *driver)
+{
+	int		i;
+
+	/* don't disconnect if it's not connected */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+
+	/* stop hardware; prevent new request submissions;
+	 * and kill any outstanding requests.
+	 */
+	usb_reset (dev);
+	for (i = 0; i < 4; i++)
+		nuke (&dev->ep [i]);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock (&dev->lock);
+		driver->disconnect (&dev->gadget);
+		spin_lock (&dev->lock);
+
+	}
+	usb_reinit (dev);
+}
+
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	struct net2272	*dev = the_controller;
+	unsigned long	flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	stop_activity (dev, driver);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	net2272_pullup (&dev->gadget, 0);
+
+	driver->unbind (&dev->gadget);
+	dev->gadget.dev.driver = NULL;
+	dev->driver = NULL;
+
+	DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+/*---------------------------------------------------------------------------*/
+/* handle ep-a/ep-b dma completions */
+static void handle_dma (struct net2272_ep *ep)
+{
+	struct net2272_request	*req;
+	unsigned		len;
+	int			status;
+
+	if (!list_empty (&ep->queue))
+		req = list_entry (ep->queue.next,
+				struct net2272_request, queue);
+	else
+		req = NULL;
+
+	VDEBUG (ep->dev, "handle_dma %s req %p\n", ep->ep.name, req);
+
+	/* Ensure DREQ is de-asserted */
+	net2272_write (ep->dev, DMAREQ,
+		(0 << DMA_BUFFER_VALID)
+	      | (0 << DMA_REQUEST_ENABLE)
+	      | (1 << DMA_CONTROL_DACK)
+	      | (ep->dev->dma_eot_polarity << EOT_POLARITY)
+	      | (ep->dev->dma_dack_polarity << DACK_POLARITY)
+	      | (ep->dev->dma_dreq_polarity << DREQ_POLARITY)
+	      | ((ep->dma >> 1) << DMA_ENDPOINT_SELECT));
+
+	ep->dev->dma_busy = 0;
+
+	net2272_ep_write (ep, EP_IRQENB,
+		  (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+		| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+		| net2272_ep_read (ep, EP_IRQENB));
+
+	/* device-to-host transfer completed */
+	if (ep->is_in) {
+		/* validate a short packet or zlp if necessary */
+		if ((req->req.length % ep->ep.maxpacket != 0) ||
+				req->req.zero)
+			set_fifo_bytecount (ep, 0);
+
+		done (ep, req, 0);
+		if (!list_empty (&ep->queue)) {
+			req = list_entry (ep->queue.next,
+					struct net2272_request, queue);
+			status = kick_dma (ep, req);
+			if (status < 0)
+				pio_advance (ep);
+		}
+
+	/* host-to-device transfer completed */
+	} else {
+		/* terminated with a short packet? */
+		if (net2272_read (ep->dev, IRQSTAT0) &
+				(1 << DMA_DONE_INTERRUPT)) {
+			/* abort system dma */
+			cancel_dma (ep->dev);
+		}
+
+		/* EP_TRANSFER will contain the number of bytes
+		 * actually received.
+		 * NOTE: There is no overflow detection on EP_TRANSFER:
+		 * We can't deal with transfers larger than 2^24 bytes!
+		 */
+		len = (net2272_ep_read (ep, EP_TRANSFER2) << 16)
+			| (net2272_ep_read (ep, EP_TRANSFER1) << 8)
+			| (net2272_ep_read (ep, EP_TRANSFER0));
+
+		if (ep->not_empty)
+			len += 4;
+
+		req->req.actual += len;
+
+		/* get any remaining data */
+		pio_advance (ep);
+	}
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void handle_ep (struct net2272_ep *ep)
+{
+	struct net2272_request	*req;
+	u8			stat0, stat1;
+
+	if (!list_empty (&ep->queue))
+		req = list_entry (ep->queue.next,
+			struct net2272_request, queue);
+	else
+		req = NULL;
+
+	/* ack all, and handle what we care about */
+	stat0 = net2272_ep_read (ep, EP_STAT0);
+	stat1 = net2272_ep_read (ep, EP_STAT1);
+	ep->irqs++;
+#if 0
+	VDEBUG (ep->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n",
+			ep->ep.name, stat0, stat1, req ? &req->req : 0);
+#endif
+	net2272_ep_write (ep, EP_STAT0, stat0 &
+			~((1 << NAK_OUT_PACKETS)
+			| (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)));
+	net2272_ep_write (ep, EP_STAT1, stat1);
+
+
+	/* data packet(s) received (in the fifo, OUT)
+	 * direction must be validated, otherwise control read status phase
+	 * could be interpreted as a valid packet
+	 */
+	if (!ep->is_in && (stat0 & (1 << DATA_PACKET_RECEIVED_INTERRUPT)))
+		pio_advance (ep);
+	/* data packet(s) transmitted (IN) */
+	else if (stat0 & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
+		pio_advance (ep);
+}
+
+static struct net2272_ep *
+get_ep_by_addr (struct net2272 *dev, u16 wIndex)
+{
+	struct net2272_ep	*ep;
+
+	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+		return &dev->ep [0];
+
+	list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) {
+		u8	bEndpointAddress;
+
+		if (!ep->desc)
+			continue;
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+		if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+			return ep;
+	}
+	return NULL;
+}
+
+/* USB Test Packet:
+ * JKJKJKJK * 9
+ * JJKKJJKK * 8
+ * JJJJKKKK * 8
+ * JJJJJJJKKKKKKK * 8
+ * JJJJJJJK * 8
+ * {JKKKKKKK * 10}, JK
+ */
+u8 test_packet [] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+	0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+	0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,
+	0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFD, 0x7E
+};
+
+static void set_test_mode (struct net2272 *dev, int mode)
+{
+	int		i;
+
+	/* Disable all net2272 interrupts:
+	 * Nothing but a power cycle should stop the test.
+	 */
+	net2272_write (dev, IRQENB0, 0x00);
+	net2272_write (dev, IRQENB1, 0x00);
+
+	/* Force tranceiver to high-speed */
+	net2272_write (dev, XCVRDIAG, 1 << FORCE_HIGH_SPEED);
+
+	net2272_write (dev, PAGESEL, 0);
+	net2272_write (dev, EP_STAT0, 1 << DATA_PACKET_TRANSMITTED_INTERRUPT);
+	net2272_write (dev, EP_RSPCLR,
+			  (1 << CONTROL_STATUS_PHASE_HANDSHAKE)
+			| (1 << HIDE_STATUS_PHASE));
+	net2272_write (dev, EP_CFG, 1 << ENDPOINT_DIRECTION);
+	net2272_write (dev, EP_STAT1, 1 << BUFFER_FLUSH);
+
+	/* wait for status phase to complete */
+	while (!(net2272_read (dev, EP_STAT0) &
+				(1 << DATA_PACKET_TRANSMITTED_INTERRUPT)))
+		;
+
+	/* Enable test mode */
+	net2272_write (dev, USBTEST, mode);
+
+	/* load test packet */
+	if (mode == TEST_PACKET) {
+		/* switch to 8 bit mode */
+		net2272_write (dev, LOCCTL, net2272_read (dev, LOCCTL) &
+				~(1 << DATA_WIDTH));
+
+		for (i = 0; i < sizeof test_packet; i++)
+			net2272_write (dev, EP_DATA, test_packet [i]);
+
+		/* Validate test packet */
+		net2272_write (dev, EP_TRANSFER0, 0);
+	}
+}
+
+static void handle_stat0_irqs (struct net2272 *dev, u8 stat)
+{
+	struct net2272_ep	*ep;
+	u8			num, scratch;
+
+	/* starting a control request? */
+	if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) {
+		union {
+			u8			raw [8];
+			struct usb_ctrlrequest	r;
+		} u;
+		int				tmp = 0;
+		struct net2272_request		*req;
+
+		if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+			if (net2272_read (dev, USBCTL1) & (1 << USB_HIGH_SPEED))
+				dev->gadget.speed = USB_SPEED_HIGH;
+			else
+				dev->gadget.speed = USB_SPEED_FULL;
+			DEBUG (dev, "%s speed\n",
+					(dev->gadget.speed == USB_SPEED_HIGH)
+					? "high" : "full");
+		}
+
+		ep = &dev->ep [0];
+		ep->irqs++;
+
+		/* make sure any leftover interrupt state is cleared */
+		stat &= ~(1 << ENDPOINT_0_INTERRUPT);
+		while (!list_empty (&ep->queue)) {
+			req = list_entry (ep->queue.next,
+					struct net2272_request, queue);
+			done (ep, req, (req->req.actual == req->req.length)
+					? 0 : -EPROTO);
+		}
+		ep->stopped = 0;
+		dev->protocol_stall = 0;
+		net2272_ep_write (ep, EP_STAT0,
+			    (1 << DATA_IN_TOKEN_INTERRUPT)
+			  | (1 << DATA_OUT_TOKEN_INTERRUPT)
+			  | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+			  | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+			  | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT));
+		net2272_ep_write (ep, EP_STAT1,
+			    (1 << TIMEOUT)
+			  | (1 << USB_OUT_ACK_SENT)
+			  | (1 << USB_OUT_NAK_SENT)
+			  | (1 << USB_IN_ACK_RCVD)
+			  | (1 << USB_IN_NAK_SENT)
+			  | (1 << USB_STALL_SENT)
+			  | (1 << LOCAL_OUT_ZLP));
+
+		// Ensure Control Read pre-validation setting is beyond maximum size
+		//  - Control Writes can leave non-zero values in EP_TRANSFER. If
+		//    an EP0 transfer following the Control Write is a Control Read,
+		//    the NET2272 sees the non-zero EP_TRANSFER as an unexpected
+		//    pre-validation count.
+		//  - Setting EP_TRANSFER beyond the maximum EP0 transfer size ensures
+		//    the pre-validation count cannot cause an unexpected validatation
+		net2272_write (dev, PAGESEL, 0);
+		net2272_write (dev, EP_TRANSFER2, 0xff);
+		net2272_write (dev, EP_TRANSFER1, 0xff);
+		net2272_write (dev, EP_TRANSFER0, 0xff);
+
+		u.raw [0] = net2272_read (dev, SETUP0);
+		u.raw [1] = net2272_read (dev, SETUP1);
+		u.raw [2] = net2272_read (dev, SETUP2);
+		u.raw [3] = net2272_read (dev, SETUP3);
+		u.raw [4] = net2272_read (dev, SETUP4);
+		u.raw [5] = net2272_read (dev, SETUP5);
+		u.raw [6] = net2272_read (dev, SETUP6);
+		u.raw [7] = net2272_read (dev, SETUP7);
+
+		le16_to_cpus (&u.r.wValue);
+		le16_to_cpus (&u.r.wIndex);
+		le16_to_cpus (&u.r.wLength);
+
+		/* ack the irq */
+		net2272_write (dev, IRQSTAT0, 1 << SETUP_PACKET_INTERRUPT);
+		stat ^= (1 << SETUP_PACKET_INTERRUPT);
+
+		/* watch control traffic at the token level, and force
+		 * synchronization before letting the status phase happen.
+		 */
+		ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+		if (ep->is_in) {
+			scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+				| (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE)
+				| (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE);
+			stop_out_naking (ep);
+		} else
+			scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+				| (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE)
+				| (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE);
+		net2272_ep_write (ep, EP_IRQENB, scratch);
+
+		if ((u.r.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			goto delegate;
+		switch (u.r.bRequest) {
+		case USB_REQ_GET_STATUS: {
+			struct net2272_ep	*e;
+			u16			status = 0;
+
+			if ((u.r.bRequestType & USB_RECIP_MASK)
+					== USB_RECIP_ENDPOINT) {
+				if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0
+						|| u.r.wLength > 2)
+					goto do_stall;
+				if (net2272_ep_read (e, EP_RSPSET)
+						& (1 << ENDPOINT_HALT))
+					status = __constant_cpu_to_le16 (1);
+				else
+					status = __constant_cpu_to_le16 (0);
+
+				/* don't bother with a request object! */
+				net2272_ep_write (&dev->ep [0], EP_IRQENB, 0);
+				writew (status, dev->base_addr + EP_DATA);
+				set_fifo_bytecount (&dev->ep [0], 0);
+				allow_status (ep);
+				VDEBUG (dev, "%s stat %02x\n", ep->ep.name,
+						status);
+				goto next_endpoints;
+			} else if ((u.r.bRequestType & USB_RECIP_MASK)
+					== USB_RECIP_DEVICE) {
+				if (u.r.wLength > 2)
+					goto do_stall;
+				if (dev->is_selfpowered)
+					status = (1 << USB_DEVICE_SELF_POWERED);
+
+				/* don't bother with a request object! */
+				net2272_ep_write (&dev->ep [0], EP_IRQENB, 0);
+				writew (status, dev->base_addr + EP_DATA);
+				set_fifo_bytecount (&dev->ep [0], 0);
+				allow_status (ep);
+				VDEBUG (dev, "device stat %02x\n", status);
+				goto next_endpoints;
+			} else if ((u.r.bRequestType & USB_RECIP_MASK)
+					== USB_RECIP_INTERFACE) {
+				if (u.r.wLength > 2)
+					goto do_stall;
+
+				/* don't bother with a request object! */
+				net2272_ep_write (&dev->ep [0], EP_IRQENB, 0);
+				writew (status, dev->base_addr + EP_DATA);
+				set_fifo_bytecount (&dev->ep [0], 0);
+				allow_status (ep);
+				VDEBUG (dev, "interface status %02x\n", status);
+				goto next_endpoints;
+			}
+		}
+		case USB_REQ_CLEAR_FEATURE: {
+			struct net2272_ep	*e;
+
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (u.r.wValue != USB_ENDPOINT_HALT
+					|| u.r.wLength != 0)
+				goto do_stall;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+				goto do_stall;
+			clear_halt (e);
+			allow_status (ep);
+			VDEBUG (dev, "%s clear halt\n", ep->ep.name);
+			goto next_endpoints;
+			}
+		case USB_REQ_SET_FEATURE: {
+			struct net2272_ep	*e;
+
+			if (u.r.bRequestType == USB_RECIP_DEVICE) {
+				if (u.r.wIndex != NORMAL_OPERATION)
+					set_test_mode (dev, u.r.wIndex);
+				allow_status (ep);
+				VDEBUG (dev, "test mode: %d\n", u.r.wIndex);
+				goto next_endpoints;
+			} else if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (u.r.wValue != USB_ENDPOINT_HALT
+					|| u.r.wLength != 0)
+				goto do_stall;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+				goto do_stall;
+			set_halt (e);
+			allow_status (ep);
+			VDEBUG (dev, "%s set halt\n", ep->ep.name);
+			goto next_endpoints;
+			}
+		case USB_REQ_SET_ADDRESS: {
+			net2272_write (dev, OURADDR, u.r.wValue & 0xff);
+			allow_status (ep);
+			break;
+			}
+		default:
+delegate:
+			VDEBUG (dev, "setup %02x.%02x v%04x i%04x "
+				"ep_cfg %08x\n",
+				u.r.bRequestType, u.r.bRequest,
+				u.r.wValue, u.r.wIndex,
+				net2272_ep_read (ep, EP_CFG));
+			spin_unlock (&dev->lock);
+			tmp = dev->driver->setup (&dev->gadget, &u.r);
+			spin_lock (&dev->lock);
+		}
+
+		/* stall ep0 on error */
+		if (tmp < 0) {
+do_stall:
+			VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n",
+					u.r.bRequestType, u.r.bRequest, tmp);
+			dev->protocol_stall = 1;
+		}
+	/* endpoint dma irq? */
+	} else if (stat & (1 << DMA_DONE_INTERRUPT)) {
+		cancel_dma (dev);
+		net2272_write (dev, IRQSTAT0, 1 << DMA_DONE_INTERRUPT);
+		stat &= ~(1 << DMA_DONE_INTERRUPT);
+		num = (net2272_read (dev, DMAREQ) & (1 << DMA_ENDPOINT_SELECT))
+			? 2 : 1;
+
+		ep = &dev->ep [num];
+		handle_dma (ep);
+	}
+
+next_endpoints:
+	/* endpoint data irq? */
+	scratch = stat & 0x0f;
+	stat &= ~0x0f;
+	for (num = 0; scratch; num++) {
+		u8	t;
+
+		/* does this endpoint's FIFO and queue need tending? */
+		t = 1 << num;
+		if ((scratch & t) == 0)
+			continue;
+		scratch ^= t;
+
+		ep = &dev->ep [num];
+		handle_ep (ep);
+	}
+
+	/* some interrupts we can just ignore */
+	stat &= ~(1 << SOF_INTERRUPT);
+
+	if (stat)
+		DEBUG (dev, "unhandled irqstat0 %02x\n", stat);
+}
+
+static void handle_stat1_irqs (struct net2272 *dev, u8 stat)
+{
+	u8			tmp, mask;
+
+	/* after disconnect there's nothing else to do! */
+	tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
+	mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED);
+
+	if (stat & tmp) {
+		net2272_write (dev, IRQSTAT1, tmp);
+		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
+				(( net2272_read (dev, USBCTL1) & mask) == 0))
+			|| ((net2272_read (dev, USBCTL1) & (1 << VBUS_PIN))
+				== 0))
+				&& (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
+			DEBUG (dev, "disconnect %s\n",
+				dev->driver->driver.name);
+			stop_activity (dev, dev->driver);
+			ep0_start (dev);
+			return;
+		}
+		stat &= ~tmp;
+
+		if (!stat)
+			return;
+	}
+
+	tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
+	if (stat & tmp) {
+		net2272_write (dev, IRQSTAT1, tmp);
+		if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
+			if (dev->driver->suspend)
+				dev->driver->suspend (&dev->gadget);
+		} else {
+			if (dev->driver->resume)
+				dev->driver->resume (&dev->gadget);
+		}
+		stat &= ~tmp;
+	}
+
+	/* clear any other status/irqs */
+	if (stat)
+		net2272_write (dev, IRQSTAT1, stat);
+
+	/* some status we can just ignore */
+	stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
+			| (1 << SUSPEND_REQUEST_INTERRUPT)
+			| (1 << RESUME_INTERRUPT));
+	if (!stat)
+		return;
+	else
+		DEBUG (dev, "unhandled irqstat1 %02x\n", stat);
+}
+
+static irqreturn_t net2272_irq (int irq, void *_dev)
+{
+	struct net2272		*dev = _dev;
+#if defined(PLX_PCI_RDK)
+	u32			intcsr;
+	u8			dmareq;
+#endif
+	spin_lock (&dev->lock);
+#if defined(PLX_PCI_RDK)
+	intcsr = readl (dev->plx9054_base_addr + INTCSR);
+
+	if ((intcsr & LOCAL_INTERRUPT_TEST) == LOCAL_INTERRUPT_TEST) {
+		writel (intcsr & ~(1 << PCI_INTERRUPT_ENABLE),
+				dev->plx9054_base_addr + INTCSR);
+		handle_stat1_irqs (dev, net2272_read (dev, IRQSTAT1));
+		handle_stat0_irqs (dev, net2272_read (dev, IRQSTAT0));
+		intcsr = readl (dev->plx9054_base_addr + INTCSR);
+		writel (intcsr | (1 << PCI_INTERRUPT_ENABLE),
+			dev->plx9054_base_addr + INTCSR);
+	}
+	if ((intcsr & DMA_CHANNEL_0_TEST) == DMA_CHANNEL_0_TEST) {
+		writeb ((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)),
+				dev->plx9054_base_addr + DMACSR0);
+
+		dmareq = net2272_read (dev, DMAREQ);
+		if (dmareq & 0x01)
+			handle_dma (&dev->ep [2]);
+		else
+			handle_dma (&dev->ep [1]);
+	}
+#endif
+#if !defined(PLX_PCI_RDK)
+	handle_stat1_irqs (dev, net2272_read (dev, IRQSTAT1));
+	handle_stat0_irqs (dev, net2272_read (dev, IRQSTAT0));
+#endif
+	spin_unlock (&dev->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int net2272_present(struct net2272 *dev)
+{   // Quick test to see if CPU can communicate properly with the NET2272
+	//  - Verifies connection using writes and reads to write/read and read-only registers
+
+	// This routine is strongly recommended especially during early bring-up of new
+	// hardware, however for designs that do not apply Power On System Tests (POST)
+	// it may discarded (or perhaps minimized).
+	unsigned int ii;
+	u8 Val, RefVal;
+
+	// Verify NET2272 write/read SCRATCH register can write and read
+	RefVal = (u8)net2272_read(dev, SCRATCH);
+	for (ii = 0; ii < 0x100; ii += 7)
+	{
+		net2272_write(dev, SCRATCH, ii);
+		if ((Val = net2272_read(dev, SCRATCH)) != ii)
+		{
+			DEBUG(dev, "AreYouThere(): write/read SCRATCH register test failed: wrote:0x%2.2x, read:0x%2.2x\n",
+			      ii, Val
+			);
+			return -EINVAL;
+		}
+	}
+	// To be nice, we write the original SCRATCH value back:
+	net2272_write(dev, SCRATCH, RefVal);
+
+	// Verify NET2272 CHIPREV register is read-only:
+	RefVal = net2272_read(dev, CHIPREV_2272);
+	for (ii = 0; ii < 0x100; ii += 7)
+	{
+		net2272_write(dev, CHIPREV_2272, ii);
+		if ((Val = net2272_read(dev, CHIPREV_2272)) != RefVal)
+		{
+			DEBUG(dev, "AreYouThere(): write/read CHIPREV register test failed: wrote 0x%2.2x, read:0x%2.2x expected:0x%2.2x\n",
+			      ii, Val, RefVal);
+			return -EINVAL;
+		}
+	}
+
+	// Verify NET2272's "NET2270 legacy revision" register
+	//  - NET2272 has two revision registers. The NET2270 legacy revision register should
+	//    read the same value, regardless of the NET2272 silicon revision. (The legacy
+	//    register applies to NET2270 firmware being applied to the NET2272)
+	Val = net2272_read(dev, CHIPREV_LEGACY);
+	if (Val != NET2270_LEGACY_REV)
+	{   // Unexpected legacy revision value
+		//  - Perhaps the chip is a NET2270?
+		DEBUG(dev,
+		      "\nNcDev_AreYouThere(): WARNING: UNEXPECTED NET2272 LEGACY REGISTER VALUE:\n"
+		      " - CHIPREV_LEGACY: expected 0x%2.2x, got:0x%2.2x. (Not NET2272?)\n\n",
+		      NET2270_LEGACY_REV,
+		      Val
+		);
+		// Return Success, even though the chip does not appear to be a NET2272
+		return -EINVAL;
+	}
+
+	// Verify NET2272 silicon revision
+	//  - This revision register is appropriate for the silicon version of the NET2272
+	Val = net2272_read(dev, CHIPREV_2272);
+
+	switch (Val)
+	{   // This NET2272 firmware is designed for these versions of NET2272 silicon:
+		case CHIPREV_NET2272_R1:
+			// NET2272 Rev 1 has DMA related errata:
+			//  - Newer silicon (Rev 1A or better) required
+			DEBUG(dev,
+			      "NcDev_AreYouThere(): NET2272 Rev 1 detected:\n"
+			      " - Newer silicon recommended for DMA support.\n");
+			break;
+		case CHIPREV_NET2272_R1A:
+			break;
+		default:
+			// NET2272 silicon version *may* not work with this firmware
+			//  - Show warning:
+			DEBUG(dev,
+			      "NcDev_AreYouThere(): Unexpected NET2272 silicon revision register value:\n"
+			      " - CHIPREV_2272: 0x%2.2x\n",
+			      Val
+			);
+			// Return Success, even though the chip rev is not an expected value
+			//  - Older, pre-built firmware can attempt to operate on newer silicon
+			//  - Often, new silicon is perfectly compatible
+	}
+
+	// Success: NET2272 checks out OK
+	return 0;
+}
+
+static void gadget_release (struct device *_dev)
+{
+	struct net2272	*dev = dev_get_drvdata (_dev);
+
+	kfree (dev);
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+/*---------------------------------------------------------------------------*/
+#if defined(PLX_PCI_RDK)
+
+static void net2272_rdk_remove (struct pci_dev *pdev)
+{
+	struct net2272		*dev = pci_get_drvdata (pdev);
+	int			i;
+
+	/* start with the driver above us */
+	if (dev->driver) {
+		/* should have been done already by driver model core */
+		WARNING(dev, "pci remove, driver '%s' is still registered\n",
+		      dev->driver->driver.name);
+		usb_gadget_unregister_driver (dev->driver);
+	}
+
+	/* disable PLX 9054 interrupts */
+	writel (readl (dev->plx9054_base_addr + INTCSR) &
+		~(1 << PCI_INTERRUPT_ENABLE),
+		dev->plx9054_base_addr + INTCSR);
+
+	/* clean up resources allocated during probe() */
+	if (dev->got_irq)
+		free_irq (pdev->irq, dev);
+	if (dev->plx9054_base_addr)
+		iounmap (dev->plx9054_base_addr);
+	if (dev->epld_base_addr)
+		iounmap (dev->epld_base_addr);
+	if (dev->base_addr)
+		iounmap (dev->base_addr);
+	for (i = 0; i < 4; i++) {
+		if (i == 1)
+			continue;	/* BAR1 unused */
+		release_mem_region (pci_resource_start (pdev, i),
+				    pci_resource_len (pdev, i));
+	}
+	if (dev->enabled)
+		pci_disable_device (pdev);
+	device_unregister (&dev->gadget.dev);
+	device_remove_file (&pdev->dev, &dev_attr_registers);
+
+
+
+	INFO (dev, "unbind\n");
+
+	the_controller = NULL;
+}
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds tous
+ */
+static int
+net2272_rdk_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net2272		*dev;
+	unsigned long		resource, len;
+	void			__iomem *mem_mapped_addr[4];
+	int			retval, i;
+	char			buf [8], *bufp;
+	unsigned int		tmp;
+
+	if (the_controller) {
+		dev_warn (&pdev->dev, "ignoring\n");
+		return -EBUSY;
+	}
+
+	/* alloc, and start init */
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (dev == NULL) {
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	dev->pdev = pdev;
+	dev->gadget.ops = &net2272_ops;
+	dev->gadget.is_dualspeed = 1;
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	strcpy (dev->gadget.dev.bus_id, "gadget");
+	dev->gadget.dev.parent = &pdev->dev;
+	dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	dev->gadget.dev.release = gadget_release;
+	dev->gadget.name = driver_name;
+
+	if (pci_enable_device (pdev) < 0) {
+		retval = -ENODEV;
+		goto done;
+	}
+	dev->enabled = 1;
+
+	pci_set_master (pdev);
+
+	/* BAR 0 holds PLX 9054 config registers
+	 * BAR 1 is i/o memory; unused here
+	 * BAR 2 holds EPLD config registers
+	 * BAR 3 holds NET2272 registers
+	 */
+
+	/* Find and map all address spaces */
+	for (i = 0; i < 4; i++) {
+		if (i == 1)
+			continue;	/* BAR1 unused */
+
+		resource = pci_resource_start (pdev, i);
+		len = pci_resource_len (pdev, i);
+
+		if (!request_mem_region (resource, len, driver_name)) {
+			DEBUG(dev, "controller already in use\n");
+			retval = -EBUSY;
+			goto done;
+		}
+
+		mem_mapped_addr [i] = ioremap_nocache (resource, len);
+		if (mem_mapped_addr [i] == NULL) {
+			DEBUG (dev, "can't map memory\n");
+			retval = -EFAULT;
+			goto done;
+		}
+	}
+
+	dev->plx9054_base_addr = mem_mapped_addr [0];
+	dev->epld_base_addr = mem_mapped_addr [2];
+	dev->base_addr = mem_mapped_addr [3];
+
+	dev->indexed_threshold = 1 << 5;
+
+	dev->dma_eot_polarity  = 0;
+	dev->dma_dack_polarity = 0;
+	dev->dma_dreq_polarity = 0;
+	dev->dma_busy = 0;
+
+	/* Set PLX 9054 bus width (16 bits) */
+	tmp = readl (dev->plx9054_base_addr + LBRD1);
+	writel ((tmp & ~(3 << MEMORY_SPACE_LOCAL_BUS_WIDTH)) | W16_BIT,
+			dev->plx9054_base_addr + LBRD1);
+
+	/* Enable PLX 9054 Interrupts */
+	writel (readl (dev->plx9054_base_addr + INTCSR) |
+			(1 << PCI_INTERRUPT_ENABLE) |
+			(1 << LOCAL_INTERRUPT_INPUT_ENABLE),
+			dev->plx9054_base_addr + INTCSR);
+
+	writeb ((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)),
+			dev->plx9054_base_addr + DMACSR0);
+
+	/* reset */
+	*((u8 *)dev->base_addr + EPLD_IO_CONTROL_REGISTER) =
+		  (1 << EPLD_DMA_ENABLE)
+		| (1 << DMA_CTL_DACK)
+		| (1 << DMA_TIMEOUT_ENABLE)
+		| (1 << USER)
+		| (0 << MPX_MODE)
+		| (1 << BUSWIDTH)
+		| (1 << NET2272_RESET);
+
+	mb ();
+	*((u8 *)dev->base_addr + EPLD_IO_CONTROL_REGISTER) &=
+			~(1 << NET2272_RESET);
+	udelay(200);
+	// See if there...
+	if (net2272_present(dev))
+	{
+		WARNING(dev, "2272 not found!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+
+	usb_reset (dev);
+	usb_reinit (dev);
+
+	if (!pdev->irq) {
+		DEBUG (dev, "No IRQ!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+#ifndef __sparc__
+	snprintf (buf, sizeof buf, "%d", pdev->irq);
+	bufp = buf;
+#else
+	bufp = __irq_itoa (pdev->irq);
+#endif
+	if (request_irq (pdev->irq, net2272_irq, SA_SHIRQ, driver_name, dev) != 0)
+	{
+		ERROR(dev, "request interrupt %s failed\n", bufp);
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->got_irq = 1;
+
+	dev->chiprev = net2272_read (dev, CHIPREV_2272);
+
+	/* done */
+	pci_set_drvdata (pdev, dev);
+	INFO (dev, "%s\n", driver_desc);
+        char *dmode = dma_mode_string();
+	INFO (dev, "irq %s, pci mem %p, chip rev %04x, dma %s\n",
+			bufp, dev->base_addr, dev->chiprev,
+			dmode);
+	INFO (dev, "version: %s\n", DRIVER_VERSION);
+
+	the_controller = dev;
+
+	device_register (&dev->gadget.dev);
+	device_create_file (&pdev->dev, &dev_attr_registers);
+
+	return 0;
+
+done:
+	if (dev)
+		net2272_rdk_remove (pdev);
+	return retval;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* Table of matching PCI IDs */
+static struct pci_device_id __devinitdata pci_ids [] = { {
+	.class			= ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe),
+	.class_mask		= 0,
+	.vendor			= 0x10b5,
+	.device			= 0x9054,
+	.subvendor		= PCI_ANY_ID,
+	.subdevice		= PCI_ANY_ID,
+}, { 0 /* end: all zeros */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+static struct pci_driver net2272_driver = {
+	.name			= (char *)driver_name,
+	.id_table		= pci_ids,
+
+	.probe			= net2272_rdk_probe,
+	.remove			= net2272_rdk_remove,
+};
+
+#endif
+
+/*---------------------------------------------------------------------------*/
+
+#if !defined(PLX_PCI_RDK)
+
+// Platform remove
+static int net2272_remove (struct device *_dev)
+{
+	struct net2272		*dev;
+	struct platform_device	*pdev;
+
+	pdev = to_platform_device (_dev);
+	dev = dev_get_drvdata (&pdev->dev);
+
+	/* start with the driver above us */
+	if (dev->driver) {
+		/* should have been done already by driver model core */
+		WARNING(dev, "remove, driver '%s' is still registered\n",
+				dev->driver->driver.name);
+		usb_gadget_unregister_driver (dev->driver);
+	}
+
+	/* clean up resources allocated during probe() */
+	if (dev->got_irq)
+		free_irq (pdev->resource [1].start, dev);
+
+	if (dev->base_addr)
+		iounmap (dev->base_addr);
+
+	device_remove_file (&pdev->dev, &dev_attr_registers);
+
+	INFO (dev, "unbind\n");
+
+	dev_set_drvdata (_dev, 0);
+	the_controller = NULL;
+
+#ifdef CONFIG_BFIN533_STAMP
+	gpio_free(GPIO_0);
+	gpio_free(GPIO_1);
+#endif
+
+#ifdef CONFIG_BFIN537_BLUETECHNIX_CM
+	gpio_free(GPIO_47);
+#endif
+
+	return 0;
+}
+
+static int net2272_probe (struct device *_dev)
+{
+	struct net2272		*dev;
+	struct platform_device	*pdev;
+	int			retval;
+
+	pdev = to_platform_device (_dev);
+
+	if (the_controller) {
+		dev_warn (&pdev->dev, "ignoring\n");
+		return -EBUSY;
+	}
+#ifdef CONFIG_BLACKFIN
+	{
+		struct resource *iomem;
+
+		irq = platform_get_irq(pdev, 0);
+		iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (iomem)
+			base = iomem->start;
+#ifdef CONFIG_BFIN533_STAMP
+		/* Set PF0 to 0, PF1 to 1 make /AMS3 work properly */
+
+	if (gpio_request(GPIO_0, driver_name)) {
+		printk(KERN_ERR "net2272: Failed to request GPIO_%d\n", GPIO_0);
+		return -EBUSY;
+	}
+	if (gpio_request(GPIO_1, driver_name)) {
+		printk(KERN_ERR "net2272: Failed to request GPIO_%d\n", GPIO_1);
+		gpio_free(GPIO_0);
+		return -EBUSY;
+	}
+	gpio_direction_output(GPIO_0, 0);
+	gpio_direction_output(GPIO_1, 1);
+#endif
+
+#if defined(CONFIG_BFIN533_STAMP) || defined(CONFIG_BFIN561_EZKIT)
+	if (gpio_request(GPIO_11, driver_name)) {
+		printk(KERN_ERR "net2272: Failed to request GPIO_%d\n", GPIO_11);
+		gpio_free(GPIO_0);
+		gpio_free(GPIO_1);
+		return -EBUSY;
+	}
+	gpio_direction_output(GPIO_11, 0);
+	/* Reset USB Chip */
+	/* Hold it for 2ms */
+	mdelay(2);
+	gpio_set_value(GPIO_11, 1);
+#endif
+
+#ifdef CONFIG_BFIN537_STAMP
+	if (gpio_request(GPIO_6, driver_name)) {
+		printk(KERN_ERR "net2272: Failed to request GPIO_%d\n", GPIO_6);
+		return -EBUSY;
+	}
+
+	gpio_direction_output(GPIO_6, 0);
+	/* Reset USB Chip */
+	/* Hold it for 2ms */
+	mdelay(2);
+	gpio_set_value(GPIO_6, 1);
+#endif
+
+#ifdef CONFIG_BFIN537_BLUETECHNIX_CM /* Set PH15 Low make /AMS2 work properly */
+	if (gpio_request(GPIO_47, driver_name)) {
+		printk(KERN_ERR "net2272: Failed ro request GPIO_%d\n", GPIO_47);
+		return -EBUSY;
+	}
+	gpio_direction_output(GPIO_47, 0);
+#endif
+	}
+#endif
+
+	if (!base || !irq) {
+		printk ("must provide base/irq as parameters! %lu %d\n", base, irq);
+		return -EINVAL;
+	}
+
+	/* alloc, and start init */
+	dev = kmalloc (sizeof *dev, GFP_KERNEL);
+	if (dev == NULL) {
+		printk ("can't allocate memory!\n");
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	dev->pdev = pdev;
+	dev->gadget.ops = &net2272_ops;
+	dev->gadget.is_dualspeed = 1;
+
+	/* the "gadget" abstracts/virtualizes the controller */
+
+	dev_set_name(&dev->gadget.dev, "gadget");
+	dev->gadget.dev.parent = &pdev->dev;
+	dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	dev->gadget.dev.release = gadget_release;
+	dev->gadget.name = driver_name;
+
+	dev->enabled = 1;
+
+	dev->base_addr = ioremap_nocache (base, 256);
+	if (!dev->base_addr) {
+		DEBUG (dev, "can't map memory\n");
+		retval = -EFAULT;
+		goto done;
+	}
+
+	dev->indexed_threshold = 1 << 5;
+	dev->dma_eot_polarity = 0;
+	dev->dma_dack_polarity = 0;
+	dev->dma_dreq_polarity = 0;
+	dev->dma_busy = 0;
+
+	usb_reset (dev);
+	usb_reinit (dev);
+
+	if (request_irq (irq, net2272_irq, IRQF_TRIGGER_LOW , driver_name, dev) != 0) {
+		ERROR(dev, "request interrupt %d failed\n", irq);
+		retval = -EBUSY;
+		goto done;
+	}
+
+	dev->got_irq = 1;
+	dev->irq = irq;
+
+	dev->chiprev = net2272_read (dev, CHIPREV_2272);
+
+	/* done */
+	dev_set_drvdata (&pdev->dev, dev);
+	INFO (dev, "%s\n", driver_desc);
+	INFO (dev, "irq %d, mapped mem %p, chip rev %04x\n",
+			dev->irq, dev->base_addr, dev->chiprev);
+	INFO (dev, "running in 16-bit, %s local bus mode\n",
+			(net2272_read (dev, LOCCTL) & (1 << BYTE_SWAP)) ?
+				"byte swap" : "no byte swap");
+	INFO (dev, "version: %s\n", DRIVER_VERSION);
+
+	the_controller = dev;
+
+	retval = device_register (&dev->gadget.dev);
+	if (retval) goto done;
+	retval = device_create_file (&pdev->dev, &dev_attr_registers);
+	if (retval) goto done;
+
+	// See if there..., can remove this test for production code
+	if (net2272_present(dev))
+	{
+		WARNING(dev, "2272 not found!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+
+	return 0;
+
+done:
+	if (dev)
+		net2272_remove (_dev);
+
+	return retval;
+}
+
+static struct device_driver net2272_driver = {
+	.name			= (char *)driver_name,
+	.bus			= &platform_bus_type,
+
+	.probe			= net2272_probe,
+	.remove			= net2272_remove,
+
+	/* FIXME .suspend, .resume */
+};
+/*---------------------------------------------------------------------------*/
+#endif
+
+static int __init init (void)
+{
+#if defined(PLX_PCI_RDK)
+	return pci_register_driver (&net2272_driver);
+#else
+	return driver_register (&net2272_driver);
+#endif
+}
+
+static void __exit cleanup (void)
+{
+#if defined(PLX_PCI_RDK)
+	pci_unregister_driver (&net2272_driver);
+#else
+	driver_unregister (&net2272_driver);
+#endif
+}
+module_init (init);
+module_exit (cleanup);
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("PLX Technology, Inc.");
+MODULE_LICENSE ("GPL");
+
diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/net2272.h
new file mode 100644
index 0000000..d598aaf
--- /dev/null
+++ b/drivers/usb/gadget/net2272.h
@@ -0,0 +1,548 @@
+/* PLX NET2272 high/full speed USB device controller
+ *
+ * Copyright (C) 2005 PLX Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef __KERNEL__
+
+/* Main Registers */
+#define REGADDRPTR			0x00
+#define REGDATA				0x01
+#define IRQSTAT0			0x02
+#define		ENDPOINT_0_INTERRUPT			0
+#define		ENDPOINT_A_INTERRUPT			1
+#define		ENDPOINT_B_INTERRUPT			2
+#define		ENDPOINT_C_INTERRUPT			3
+#define		VIRTUALIZED_ENDPOINT_INTERRUPT		4
+#define		SETUP_PACKET_INTERRUPT			5
+#define		DMA_DONE_INTERRUPT			6
+#define		SOF_INTERRUPT				7
+#define IRQSTAT1			0x03
+#define		CONTROL_STATUS_INTERRUPT		1
+#define		VBUS_INTERRUPT				2
+#define		SUSPEND_REQUEST_INTERRUPT		3
+#define		SUSPEND_REQUEST_CHANGE_INTERRUPT	4
+#define		RESUME_INTERRUPT			5
+#define		ROOT_PORT_RESET_INTERRUPT		6
+#define		RESET_STATUS				7
+#define PAGESEL				0x04
+#define DMAREQ				0x1c
+#define		DMA_ENDPOINT_SELECT			0
+#define		DREQ_POLARITY				1
+#define		DACK_POLARITY				2
+#define		EOT_POLARITY				3
+#define		DMA_CONTROL_DACK			4
+#define		DMA_REQUEST_ENABLE			5
+#define		DMA_REQUEST				6
+#define		DMA_BUFFER_VALID			7
+#define SCRATCH				0x1d
+#define IRQENB0				0x20
+#define		ENDPOINT_0_INTERRUPT_ENABLE		0
+#define		ENDPOINT_A_INTERRUPT_ENABLE		1
+#define		ENDPOINT_B_INTERRUPT_ENABLE		2
+#define		ENDPOINT_C_INTERRUPT_ENABLE		3
+#define		VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE	4
+#define		SETUP_PACKET_INTERRUPT_ENABLE		5
+#define		DMA_DONE_INTERRUPT_ENABLE		6
+#define		SOF_INTERRUPT_ENABLE			7
+#define IRQENB1				0x21
+#define		VBUS_INTERRUPT_ENABLE			2
+#define		SUSPEND_REQUEST_INTERRUPT_ENABLE	3
+#define		SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE	4
+#define		RESUME_INTERRUPT_ENABLE			5
+#define		ROOT_PORT_RESET_INTERRUPT_ENABLE	6
+#define LOCCTL				0x22
+#define		DATA_WIDTH				0
+#define		LOCAL_CLOCK_OUTPUT			1
+#define			LOCAL_CLOCK_OUTPUT_OFF			0
+#define			LOCAL_CLOCK_OUTPUT_3_75MHZ		1
+#define			LOCAL_CLOCK_OUTPUT_7_5MHZ		2
+#define			LOCAL_CLOCK_OUTPUT_15MHZ		3
+#define			LOCAL_CLOCK_OUTPUT_30MHZ		4
+#define			LOCAL_CLOCK_OUTPUT_60MHZ		5
+#define		DMA_SPLIT_BUS_MODE			4
+#define		BYTE_SWAP				5
+#define		BUFFER_CONFIGURATION			6
+#define			BUFFER_CONFIGURATION_EPA512_EPB512	0
+#define			BUFFER_CONFIGURATION_EPA1024_EPB512	1
+#define			BUFFER_CONFIGURATION_EPA1024_EPB1024	2
+#define			BUFFER_CONFIGURATION_EPA1024DB		3
+#define CHIPREV_LEGACY			0x23
+#define			NET2270_LEGACY_REV			0x40
+#define LOCCTL1				0x24
+#define		DMA_MODE				0
+#define			SLOW_DREQ				0
+#define			FAST_DREQ				1
+#define			BURST_MODE				2
+#define		DMA_DACK_ENABLE				2
+#define CHIPREV_2272			0x25
+#define			CHIPREV_NET2272_R1			0x10
+#define			CHIPREV_NET2272_R1A			0x11
+/* USB Registers */
+#define USBCTL0				0x18
+#define		IO_WAKEUP_ENABLE			1
+#define		USB_DETECT_ENABLE			3
+#define		USB_ROOT_PORT_WAKEUP_ENABLE		5
+#define USBCTL1				0x19
+#define		VBUS_PIN				0
+#define			USB_FULL_SPEED				1
+#define			USB_HIGH_SPEED				2
+#define		GENERATE_RESUME				3
+#define		VIRTUAL_ENDPOINT_ENABLE			4
+#define FRAME0				0x1a
+#define FRAME1				0x1b
+#define OURADDR				0x30
+#define		FORCE_IMMEDIATE				7
+#define USBDIAG				0x31
+#define		FORCE_TRANSMIT_CRC_ERROR		0
+#define		PREVENT_TRANSMIT_BIT_STUFF		1
+#define		FORCE_RECEIVE_ERROR			2
+#define		FAST_TIMES				4
+#define USBTEST				0x32
+#define		TEST_MODE_SELECT			0
+#define			NORMAL_OPERATION			0
+#define			TEST_J					1
+#define			TEST_K					2
+#define			TEST_SE0_NAK				3
+#define			TEST_PACKET				4
+#define			TEST_FORCE_ENABLE			5
+#define XCVRDIAG			0x33
+#define		FORCE_FULL_SPEED			2
+#define		FORCE_HIGH_SPEED			3
+#define		OPMODE					4
+#define			NORMAL_OPERATION			0
+#define			NON_DRIVING				1
+#define			DISABLE_BITSTUFF_AND_NRZI_ENCODE	2
+#define		LINESTATE				6
+#define			SE0_STATE				0
+#define			J_STATE					1
+#define			K_STATE					2
+#define			SE1_STATE				3
+#define VIRTOUT0			0x34
+#define VIRTOUT1			0x35
+#define VIRTIN0				0x36
+#define VIRTIN1				0x37
+#define SETUP0				0x40
+#define SETUP1				0x41
+#define SETUP2				0x42
+#define SETUP3				0x43
+#define SETUP4				0x44
+#define SETUP5				0x45
+#define SETUP6				0x46
+#define SETUP7				0x47
+/* Endpoint Registers (Paged via PAGESEL) */
+#define EP_DATA				0x05
+#define EP_STAT0			0x06
+#define		DATA_IN_TOKEN_INTERRUPT			0
+#define		DATA_OUT_TOKEN_INTERRUPT		1
+#define		DATA_PACKET_TRANSMITTED_INTERRUPT	2
+#define		DATA_PACKET_RECEIVED_INTERRUPT		3
+#define		SHORT_PACKET_TRANSFERRED_INTERRUPT	4
+#define		NAK_OUT_PACKETS				5
+#define		BUFFER_EMPTY				6
+#define		BUFFER_FULL				7
+#define EP_STAT1			0x07
+#define		TIMEOUT					0
+#define		USB_OUT_ACK_SENT			1
+#define		USB_OUT_NAK_SENT			2
+#define		USB_IN_ACK_RCVD				3
+#define		USB_IN_NAK_SENT				4
+#define		USB_STALL_SENT				5
+#define		LOCAL_OUT_ZLP				6
+#define		BUFFER_FLUSH				7
+#define EP_TRANSFER0			0x08
+#define EP_TRANSFER1			0x09
+#define EP_TRANSFER2			0x0a
+#define EP_IRQENB			0x0b
+#define		DATA_IN_TOKEN_INTERRUPT_ENABLE		0
+#define		DATA_OUT_TOKEN_INTERRUPT_ENABLE		1
+#define		DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE	2
+#define		DATA_PACKET_RECEIVED_INTERRUPT_ENABLE	3
+#define		SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE	4
+#define EP_AVAIL0			0x0c
+#define EP_AVAIL1			0x0d
+#define EP_RSPCLR			0x0e
+#define EP_RSPSET			0x0f
+#define		ENDPOINT_HALT				0
+#define		ENDPOINT_TOGGLE				1
+#define		NAK_OUT_PACKETS_MODE			2
+#define		CONTROL_STATUS_PHASE_HANDSHAKE		3
+#define		INTERRUPT_MODE				4
+#define		AUTOVALIDATE				5
+#define		HIDE_STATUS_PHASE			6
+#define		ALT_NAK_OUT_PACKETS			7
+#define EP_MAXPKT0			0x28
+#define EP_MAXPKT1			0x29
+#define		ADDITIONAL_TRANSACTION_OPPORTUNITIES	3
+#define			NONE_ADDITIONAL_TRANSACTION		0
+#define			ONE_ADDITIONAL_TRANSACTION		1
+#define			TWO_ADDITIONAL_TRANSACTION		2
+#define EP_CFG				0x2a
+#define		ENDPOINT_NUMBER				0
+#define		ENDPOINT_DIRECTION			4
+#define		ENDPOINT_TYPE				5
+#define		ENDPOINT_ENABLE				7
+#define EP_HBW				0x2b
+#define		HIGH_BANDWIDTH_OUT_TRANSACTION_PID	0
+#define			DATA0_PID				0
+#define			DATA1_PID				1
+#define			DATA2_PID				2
+#define			MDATA_PID				3
+#define EP_BUFF_STATES			0x2c
+#define		BUFFER_A_STATE				0
+#define		BUFFER_B_STATE				2
+#define			BUFF_FREE				0
+#define			BUFF_VALID				1
+#define			BUFF_LCL				2
+#define			BUFF_USB				3
+
+#if defined(PLX_PCI_RDK)
+
+/*---------------------------------------------------------------------------*/
+/* PCI-RDK registers (PLX 9054, EPLD) */
+
+/* PCI-RDK EPLD Registers */
+#define RDK_EPLD_IO_REGISTER1				0x00000000
+#define		RDK_EPLD_USB_RESET				0
+#define		RDK_EPLD_USB_POWERDOWN				1
+#define		RDK_EPLD_USB_WAKEUP				2
+#define		RDK_EPLD_USB_EOT				3
+#define		RDK_EPLD_DPPULL					4
+#define RDK_EPLD_IO_REGISTER2				0x00000004
+#define		RDK_EPLD_BUSWIDTH				0
+#define		RDK_EPLD_USER					2
+#define		RDK_EPLD_RESET_INTERRUPT_ENABLE			3
+#define		RDK_EPLD_DMA_TIMEOUT_ENABLE			4
+#define RDK_EPLD_STATUS_REGISTER			0x00000008
+#define		RDK_EPLD_USB_LRESET				0
+#define RDK_EPLD_REVISION_REGISTER			0x0000000c
+
+/* PCI-RDK PLX 9054 Registers */
+#define INTCSR						0x68
+#define		PCI_INTERRUPT_ENABLE				8
+#define		LOCAL_INTERRUPT_INPUT_ENABLE			11
+#define		LOCAL_INPUT_INTERRUPT_ACTIVE			15
+#define		LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE		18
+#define		LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE		19
+#define		DMA_CHANNEL_0_INTERRUPT_ACTIVE			21
+#define		DMA_CHANNEL_1_INTERRUPT_ACTIVE			22
+#define CNTRL						0x6C
+#define		RELOAD_CONFIGURATION_REGISTERS			29
+#define		PCI_ADAPTER_SOFTWARE_RESET			30
+#define DMAMODE0					0x80
+#define		LOCAL_BUS_WIDTH					0
+#define		INTERNAL_WAIT_STATES				2
+#define		TA_READY_INPUT_ENABLE				6
+#define		LOCAL_BURST_ENABLE				8
+#define		SCATTER_GATHER_MODE				9
+#define		DONE_INTERRUPT_ENABLE				10
+#define		LOCAL_ADDRESSING_MODE				11
+#define		DEMAND_MODE					12
+#define		DMA_EOT_ENABLE					14
+#define		FAST_SLOW_TERMINATE_MODE_SELECT			15
+#define		DMA_CHANNEL_INTERRUPT_SELECT			17
+#define DMAPADR0					0x84
+#define DMALADR0					0x88
+#define DMASIZ0						0x8c
+#define DMADPR0						0x90
+#define		DESCRIPTOR_LOCATION				0
+#define		END_OF_CHAIN					1
+#define		INTERRUPT_AFTER_TERMINAL_COUNT			2
+#define		DIRECTION_OF_TRANSFER				3
+#define DMACSR0						0xa8
+#define		CHANNEL_ENABLE					0
+#define		CHANNEL_START					1
+#define		CHANNEL_ABORT					2
+#define		CHANNEL_CLEAR_INTERRUPT				3
+#define		CHANNEL_DONE					4
+#define DMATHR						0xb0
+#define LBRD1						0xf8
+#define		MEMORY_SPACE_LOCAL_BUS_WIDTH			0
+#define		W8_BIT							0
+#define		W16_BIT							1
+
+/* Special OR'ing of INTCSR bits */
+#define LOCAL_INTERRUPT_TEST \
+((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \
+ (1 << LOCAL_INTERRUPT_INPUT_ENABLE))
+
+#define DMA_CHANNEL_0_TEST \
+((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \
+ (1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE))
+
+#define DMA_CHANNEL_1_TEST \
+((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \
+ (1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE))
+
+/* EPLD Registers */
+#define RDK_EPLD_IO_REGISTER1			0x00000000
+#define 	RDK_EPLD_USB_RESET			0
+#define 	RDK_EPLD_USB_POWERDOWN			1
+#define 	RDK_EPLD_USB_WAKEUP			2
+#define 	RDK_EPLD_USB_EOT			3
+#define 	RDK_EPLD_DPPULL				4
+#define RDK_EPLD_IO_REGISTER2			0x00000004
+#define 	RDK_EPLD_BUSWIDTH			0
+#define 	RDK_EPLD_USER				2
+#define 	RDK_EPLD_RESET_INTERRUPT_ENABLE		3
+#define 	RDK_EPLD_DMA_TIMEOUT_ENABLE		4
+#define RDK_EPLD_STATUS_REGISTER		0x00000008
+#define RDK_EPLD_USB_LRESET				0
+#define RDK_EPLD_REVISION_REGISTER		0x0000000c
+
+#define EPLD_IO_CONTROL_REGISTER		0x400
+#define 	NET2272_RESET				0
+#define 	BUSWIDTH				1
+#define 	MPX_MODE				3
+#define 	USER					4
+#define 	DMA_TIMEOUT_ENABLE			5
+#define 	DMA_CTL_DACK				6
+#define 	EPLD_DMA_ENABLE				7
+#define EPLD_DMA_CONTROL_REGISTER		0x800
+#define 	SPLIT_DMA_MODE				0
+#define 	SPLIT_DMA_DIRECTION			1
+#define 	SPLIT_DMA_ENABLE			2
+#define 	SPLIT_DMA_INTERRUPT_ENABLE		3
+#define 	SPLIT_DMA_INTERRUPT			4
+#define 	EPLD_DMA_MODE				5
+#define		EPLD_DMA_CONTROLLER_ENABLE		7
+#define SPLIT_DMA_ADDRESS_LOW			0xc00
+#define SPLIT_DMA_ADDRESS_HIGH			0x1000
+#define SPLIT_DMA_BYTE_COUNT_LOW		0x1400
+#define SPLIT_DMA_BYTE_COUNT_HIGH		0x1800
+#define EPLD_REVISION_REGISTER			0x1c00
+#define SPLIT_DMA_RAM				0x4000
+#define DMA_RAM_SIZE				0x1000
+
+#endif
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+struct net2272_ep {
+	struct usb_ep			ep;
+	struct net2272			*dev;
+	unsigned long			irqs;
+
+	/* analogous to a host-side qh */
+	struct list_head		queue;
+	const struct			usb_endpoint_descriptor *desc;
+	unsigned			num:8,
+					fifo_size:12,
+					stopped:1,
+					is_in:1,
+					is_iso:1,
+					dma:1,
+					not_empty:1;
+};
+
+struct net2272 {
+        /* each device provides one gadget, several endpoints */
+        struct usb_gadget               gadget;
+        spinlock_t                      lock;
+        struct net2272_ep               ep[4];
+        struct usb_gadget_driver        *driver;
+        unsigned                        enabled:1,
+                                        protocol_stall:1,
+                                        softconnect:1,
+                                        is_selfpowered:1,
+                                        wakeup:1,
+                                        got_irq:1,
+                                        dma_eot_polarity:1,
+                                        dma_dack_polarity:1,
+                                        dma_dreq_polarity:1,
+                                        dma_busy:1;
+        u16                             chiprev;
+
+        unsigned int                    indexed_threshold;
+        u8                              pagesel;
+        unsigned int                    irq;
+#if defined(PLX_PCI_RDK)
+        struct pci_dev                  *pdev;
+        void                            __iomem *plx9054_base_addr;
+        void                            __iomem *epld_base_addr;
+#else
+        struct platform_device          *pdev;
+#endif
+#if defined(CONFIG_BLACKFIN) && !defined(CONFIG_BFIN537_BLUETECHNIX_CM)
+	u32				__iomem *base_addr;
+#else
+        u16                             __iomem *base_addr;
+#endif
+};
+
+
+static inline void
+net2272_write (struct net2272 *dev, unsigned int reg, u8 value)
+{
+	// u8		tmp;
+
+	if (reg >= dev->indexed_threshold) {
+		// Indexed register; use REGADDRPTR/REGDATA
+        //  - Save and restore REGADDRPTR. This prevents REGADDRPTR from
+        //    changes between other code sections, but it is time consuming.
+        //  - Performance tips: either do not save and restore REGADDRPTR (if it
+        //    is safe) or do save/restore operations only in critical sections.
+		// tmp = readb (dev->base_addr + REGADDRPTR);
+		writeb ((u8)reg, dev->base_addr + REGADDRPTR);
+		writeb (value, dev->base_addr + REGDATA);
+		// writeb (tmp, dev->base_addr + REGADDRPTR);
+	} else
+		writeb (value, dev->base_addr + reg);
+}
+
+static inline u8
+net2272_read (struct net2272 *dev, unsigned int reg)
+{
+	u8		retval;
+
+	if (reg >= dev->indexed_threshold) {
+        //  - Save and restore REGADDRPTR. This prevents REGADDRPTR from
+        //    changes between other code sections, but it is time consuming.
+        //  - Performance tips: either do not save and restore REGADDRPTR (if it
+        //    is safe) or do save/restore operations only in critical sections.
+		// tmp = readb (dev->base_addr + REGADDRPTR);
+		// Indexed register; use REGADDRPTR/REGDATA
+		writeb ((u8)reg, dev->base_addr + REGADDRPTR);
+		retval = readb (dev->base_addr + REGDATA);
+		// writeb (tmp, dev->base_addr + REGADDRPTR);
+	} else
+		retval = readb (dev->base_addr + reg);
+
+	return retval;
+}
+
+static inline void
+net2272_ep_write (struct net2272_ep *ep, unsigned int reg, u8 value)
+{
+	struct net2272			*dev = ep->dev;
+
+	if (dev->pagesel != ep->num) {
+		net2272_write (dev, PAGESEL, ep->num);
+		dev->pagesel = ep->num;
+	}
+	net2272_write (dev, reg, value);
+}
+
+static inline u8
+net2272_ep_read (struct net2272_ep *ep, unsigned int reg)
+{
+	struct net2272			*dev = ep->dev;
+
+	if (dev->pagesel != ep->num) {
+		net2272_write (dev, PAGESEL, ep->num);
+		dev->pagesel = ep->num;
+	}
+	return net2272_read (dev, reg);
+}
+
+static inline void allow_status (struct net2272_ep *ep)
+{
+	/* ep0 only */
+	net2272_ep_write (ep, EP_RSPCLR, (1 << CONTROL_STATUS_PHASE_HANDSHAKE)
+					| (1 << ALT_NAK_OUT_PACKETS)
+					| (1 << NAK_OUT_PACKETS_MODE));
+	ep->stopped = 1;
+}
+
+static inline void set_halt (struct net2272_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	net2272_ep_write (ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE);
+	net2272_ep_write (ep, EP_RSPSET, 1 << ENDPOINT_HALT);
+}
+
+static inline void clear_halt (struct net2272_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	net2272_ep_write (ep, EP_RSPCLR, (1 << ENDPOINT_HALT)
+					| (1 << ENDPOINT_TOGGLE));
+}
+
+/* count (<= 4) bytes in the next fifo write will be valid */
+static inline void set_fifo_bytecount (struct net2272_ep *ep, unsigned count)
+{
+	u8		tmp;
+
+	tmp = count & 0x0f0000 >> 16;
+	net2272_ep_write (ep, EP_TRANSFER2, tmp);
+	tmp = count & 0x00ff00 >> 8;
+	net2272_ep_write (ep, EP_TRANSFER1, tmp);
+	tmp = count & 0x0000ff;
+	net2272_ep_write (ep, EP_TRANSFER0, tmp);
+}
+
+
+struct net2272_request {
+	struct usb_request		req;
+	struct list_head		queue;
+	unsigned			mapped:1,
+					valid:1;
+};
+
+/*---------------------------------------------------------------------------*/
+#define xprintk(dev,level,fmt,args...) \
+	printk(level "%s: " fmt , driver_name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev, KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif			/* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif			/* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev, KERN_ERR , fmt , ## args)
+#define WARNING(dev, fmt, args...) \
+	xprintk(dev, KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev, KERN_INFO , fmt , ## args)
+
+#ifdef DEBUG
+static inline void assert_out_naking (struct net2272_ep *ep, const char *where)
+{
+	u8			tmp = net2272_ep_read (ep, EP_STAT0);
+
+	if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
+		DEBUG (ep->dev, "%s %s %02x !NAK\n",
+					ep->ep.name, where, tmp);
+		net2272_ep_write (ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
+	}
+}
+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__FUNCTION__)
+#else
+#define ASSERT_OUT_NAKING(ep) do {} while (0)
+#endif
+
+static inline void stop_out_naking (struct net2272_ep *ep)
+{
+	u8		tmp;
+
+	tmp = net2272_ep_read (ep, EP_STAT0);
+	if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
+		net2272_ep_write (ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
+}
+
+#endif	/* __KERNEL__ */
+
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux