[U-Boot] [PATCH v2 1/4] Add Atmel USBA UDC

Marcel Janssen korgull at home.nl
Sun Feb 13 22:52:18 CET 2011


From: Marcel <korgull at home.nl>

Atmel USBA UDC cleanup

Atmel USBA UDC cleanup

more cleanup of Atmel USBA UDC

Some more cleaning of Atmel USBA UDC

further cleaning of Atmel USBA UDC

Signed-off-by: Marcel <korgull at home.nl>
---
 drivers/usb/gadget/Makefile         |    1 +
 drivers/usb/gadget/atmel_usba_udc.c | 1438 +++++++++++++++++++++++++++++++++++
 include/usb/atmel_usba_udc.h        |  398 ++++++++++
 3 files changed, 1837 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/atmel_usba_udc.c
 create mode 100644 include/usb/atmel_usba_udc.h

diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 0846233..024844d 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -29,6 +29,7 @@ LIB	:= $(obj)libusb_gadget.o
 ifdef CONFIG_USB_ETHER
 COBJS-y += ether.o epautoconf.o config.o usbstring.o
 COBJS-$(CONFIG_USB_GADGET_AT91) += at91_udc.o
+COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o
 else
 # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
 ifdef CONFIG_USB_DEVICE
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
new file mode 100644
index 0000000..6d02de6
--- /dev/null
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -0,0 +1,1438 @@
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2011 Marcel Janssen, Admesy B.V.
+ * Copyright (C) 2005-2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <linux/list.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/mtd/compat.h>
+#include <linux/porting-compat.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/at91_pmc.h>
+#include <asm/arch/io.h>
+
+#include <usb/atmel_usba_udc.h>
+
+#define __iomem
+
+#define memcpy_toio(a, b, c)        memcpy((a), (b), (c))
+#define memcpy_fromio(a, b, c)      memcpy((a), (b), (c))
+#define REQ_COUNT 8
+#define MAX_REQ (REQ_COUNT-1)
+
+static struct usba_udc the_udc;
+#define true 1
+#define false 0
+static const char driver_name[] = "atmel_usba_udc";
+static const char ep0name[] = "ep0";
+
+
+static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
+{
+	unsigned int transaction_len;
+
+	transaction_len = req->req.length - req->req.actual;
+	req->last_transaction = 1;
+	if (transaction_len > ep->ep.maxpacket) {
+		transaction_len = ep->ep.maxpacket;
+		req->last_transaction = 0;
+	} else if (transaction_len == ep->ep.maxpacket && req->req.zero)
+		req->last_transaction = 0;
+
+	debug("%s: submit_transaction, req %p (length %d)%s\n",
+		ep->ep.name, req, transaction_len,
+		req->last_transaction ? ", done" : "");
+
+	memcpy_toio(ep->fifo, req->req.buf + req->req.actual, transaction_len);
+	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+	req->req.actual += transaction_len;
+}
+
+static void submit_request(struct usba_ep *ep, struct usba_request *req)
+{
+
+	req->req.actual = 0;
+	req->submitted = 1;
+
+	debug("%s : submit_request: req %p (length %d)\n",
+				ep->ep.name, req, req->req.length);
+	next_fifo_transaction(ep, req);
+	if (req->last_transaction) {
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+	} else {
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+		}
+}
+
+static void submit_next_request(struct usba_ep *ep)
+{
+	struct usba_request *req;
+
+	if (list_empty(&ep->queue)) {
+		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY);
+		return;
+	}
+	req = list_entry(ep->queue.next, struct usba_request, queue);
+	if (!req->submitted)
+		submit_request(ep, req);
+	debug("%s : submit_next_request: req %p (length %d)\n",
+				ep->ep.name, req, req->req.length);
+}
+
+static void send_status(struct usba_udc *udc, struct usba_ep *ep)
+{
+	debug("Send USBA status\n");
+	ep->state = STATUS_STAGE_IN;
+	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+	usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+}
+
+static void receive_data(struct usba_ep *ep)
+{
+	struct usba_udc *udc = ep->udc;
+	struct usba_request *req;
+	unsigned long status;
+	unsigned int bytecount, nr_busy;
+	int is_complete = 0;
+
+	status = usba_ep_readl(ep, STA);
+	nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+	debug("receive data: nr_busy=%u\n", nr_busy);
+
+	while (nr_busy > 0) {
+		if (list_empty(&ep->queue)) {
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+			break;
+		}
+		req = list_entry(ep->queue.next,
+				 struct usba_request, queue);
+
+		bytecount = USBA_BFEXT(BYTE_COUNT, status);
+
+		if (status & (1 << 31))
+			is_complete = 1;
+		if (req->req.actual + bytecount >= req->req.length) {
+			is_complete = 1;
+			bytecount = req->req.length - req->req.actual;
+		}
+
+		memcpy_fromio(req->req.buf + req->req.actual,
+				ep->fifo, bytecount);
+		req->req.actual += bytecount;
+
+		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+
+		if (is_complete) {
+			debug("%s: request done\n", ep->ep.name);
+			req->req.status = 0;
+			list_del_init(&req->queue);
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+			req->req.complete(&ep->ep, &req->req);
+			debug("request->complete done\n");
+		}
+
+		status = usba_ep_readl(ep, STA);
+		nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+		debug("nr_busy = %d\n", nr_busy);
+		if (is_complete && ep_is_control(ep)) {
+			send_status(udc, ep);
+			break;
+		}
+		debug("rcv : bytecount=%d actual=%d\n", req->req.actual);
+	}
+}
+
+static void
+request_complete(struct usba_ep *ep, struct usba_request *req, int status)
+{
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+
+	debug("%s: req %p complete: status %d, actual %u\n",
+		ep->ep.name, req, req->req.status, req->req.actual);
+
+	req->req.complete(&ep->ep, &req->req);
+	debug("request complete finished\n");
+}
+
+static int
+usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct usba_ep *ep;
+	struct usba_udc *udc;
+	unsigned long ept_cfg, maxpacket;
+	unsigned int nr_trans;
+
+	ep = container_of(_ep, struct  usba_ep, ep);
+	udc = ep->udc;
+
+	debug("Endpoint enable ep %d\n", ep->index);
+	maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
+
+	if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
+			|| ep->index == 0
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| maxpacket == 0
+			|| maxpacket > ep->fifo_size) {
+		debug("ep_enable: Invalid argument");
+		return -EINVAL;
+	}
+
+	ep->is_isoc = 0;
+	ep->is_in = 0;
+
+	if (maxpacket <= 8)
+		ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+	else
+		/* LSB is bit 1, not 0 */
+		ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3);
+
+	debug("%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
+			ep->ep.name, ept_cfg, maxpacket);
+
+	if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+		ep->is_in = 1;
+		ept_cfg |= USBA_EPT_DIR_IN;
+		debug("%s: is IN endpoint\n", ep->ep.name);
+	}
+
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
+		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+		debug("%s: is CTRL endpoint\n", ep->ep.name);
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		debug("%s: is ISOC endpoint\n", ep->ep.name);
+		if (!ep->can_isoc)
+			return -EINVAL;
+
+
+		/*
+		 * Bits 11:12 specify number of _additional_
+		 * transactions per microframe.
+		 */
+		nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1;
+		if (nr_trans > 3)
+			return -EINVAL;
+
+		ep->is_isoc = 1;
+		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+
+		/*
+		 * Do triple-buffering on high-bandwidth iso endpoints.
+		 */
+		if (nr_trans > 1 && ep->nr_banks == 3)
+			ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE);
+		else
+			ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+		ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		debug("%s: is BULK endpoint\n", ep->ep.name);
+		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
+		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		debug("%s: is INT endpoint\n", ep->ep.name);
+		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
+		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+		break;
+	}
+
+	if (ep->desc) {
+		debug("ep%d already enabled\n", ep->index);
+		return -EBUSY;
+	}
+
+	ep->desc = desc;
+	ep->ep.maxpacket = maxpacket;
+
+	usba_ep_writel(ep, CFG, ept_cfg);
+	debug("ep%d CFG = 0x%lx\n", ep->index, ept_cfg);
+	usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+
+	debug("ep%d not using DMA\n", ep->index);
+	usba_writel(udc, INT_ENB, (usba_readl(udc, INT_ENB)
+					| USBA_BF(EPT_INT, 1 << ep->index)));
+
+	debug("EPT_CFG%d after init: %#08lx\n", ep->index,
+			(unsigned long)usba_ep_readl(ep, CFG));
+	debug("INT_ENB after init: %#08lx\n",
+			(unsigned long)usba_readl(udc, INT_ENB));
+	debug("INT_STA after init: %#08lx\n",
+			(unsigned long)usba_readl(udc, INT_STA));
+	debug("EPT_CTL_ENB%d after init: %#08lx\n", ep->index,
+			(unsigned long)usba_ep_readl(ep, CTL_ENB));
+	debug("EPT_CTL_DIS%d after init: %#08lx\n", ep->index,
+			(unsigned long)usba_ep_readl(ep, CTL_DIS));
+	debug("EPT_CTL%d after init: %#08lx\n", ep->index,
+			(unsigned long)usba_ep_readl(ep, CTL));
+	debug("EPT_STA%d after init: %#08lx\n", ep->index,
+			(unsigned long)usba_ep_readl(ep, STA));
+	return 0;
+}
+
+static int usba_ep_disable(struct usb_ep *_ep)
+{
+	struct usba_ep *ep;
+	struct usba_udc *udc;
+
+	ep = container_of(_ep, struct usba_ep, ep);
+	udc = ep->udc;
+
+	if (ep == &ep->udc->ep[0])
+		return -EINVAL;
+
+	debug("Endpoint disable ep %d\n", ep->index);
+
+	ep->desc = NULL;
+
+	usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
+	usba_writel(udc, INT_ENB,
+			usba_readl(udc, INT_ENB)
+			& ~USBA_BF(EPT_INT, 1 << ep->index));
+
+	return 0;
+}
+
+#define REQ_COUNT 8
+#define MAX_REQ (REQ_COUNT-1)
+static struct usba_request req_pool[REQ_COUNT];
+
+static void init_requests(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_REQ; i++)
+		req_pool[i].in_use = 0;
+}
+
+static void free_request(struct usba_request *ptr)
+{
+	if (ptr < &req_pool[0] && ptr > &req_pool[REQ_COUNT]) {
+		debug("%s: ptr 0x%p does not specify valid req\n", \
+			__func__, ptr);
+		if (!(ptr->in_use))
+			debug("%s: ptr not marked as \"in_use\"\n", __func__);
+
+		hang();
+	}
+	ptr->in_use = 0;
+}
+
+static struct usba_request *alloc_request(void)
+{
+	int i;
+	struct usba_request *ptr = NULL;
+
+	for (i = 0; i < MAX_REQ; i++) {
+		if (!req_pool[i].in_use) {
+			ptr = &req_pool[i];
+			req_pool[i].in_use = 1;
+			DBG("alloc_req: request[%d]\n", i);
+			break;
+		}
+	}
+	if (!ptr)
+		DBG("panic: no more free req buffers\n");
+	return ptr;
+}
+
+static struct usb_request *
+usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct usba_request *req;
+
+	req = alloc_request();
+
+	if (!req) {
+		debug("ALLOC REQUEST FAILLED : %s\n", _ep->name);
+		return NULL;
+	}
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+static void usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct usba_request *req;
+
+	debug("FREE request for %s\n", _ep->name);
+	req = container_of(_req, struct usba_request, req);
+
+	free_request(req);
+}
+
+static int
+usba_ep_queue(struct usb_ep *_ep,
+	      struct usb_request *_req,
+	      gfp_t gfp_flags)
+{
+	struct usba_request *req = to_usba_req(_req);
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+	int ret;
+
+	debug("%s: queue req %p, len %u\n",
+			ep->ep.name, req, _req->length);
+
+	if (!_req || !_req->complete
+			|| !_req->buf || !list_empty(&req->queue)) {
+
+		debug("invalid request\n");
+		if (!_req)
+			debug("NO REQUEST\n");
+		if (!_req->complete)
+			debug("NO REQUEST COMPLETE\n");
+		if (!_req->buf)
+			debug("NO REQUEST BUF\n");
+		if (!list_empty(&req->queue))
+			debug("NO LIST EMPTY\n");
+		return -EINVAL;
+	}
+
+	if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+		DBG("invalid ep\n");
+		return -EINVAL;
+	}
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || !ep->desc)
+			return -ESHUTDOWN;
+
+	req->submitted = 0;
+	req->last_transaction = 0;
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* May have received a reset since last time we checked */
+	ret = -ESHUTDOWN;
+
+	if (ep->desc) {
+		list_add_tail(&req->queue, &ep->queue);
+
+	  if ((!ep_is_control(ep) && ep->is_in) ||
+		(ep_is_control(ep) && (ep->state == DATA_STAGE_IN ||
+		ep->state == STATUS_STAGE_IN))) {
+
+		usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+		debug("WE TRANSMITTED DATA ep= %s\n", ep->ep.name);
+	  } else {
+		debug("WE RECEIVED OUT DATA ep= %s\n", ep->ep.name);
+		usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+		/*FIXME may be needed, not sure yet */
+		/*usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);*/
+	  }
+	  ret = 0;
+	}
+	return ret;
+}
+
+static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct usba_ep *ep = container_of(_ep, struct usba_ep, ep);
+	struct usba_request *req = container_of(_req, struct usba_request, req);
+
+	debug("ep_dequeue: %s, req %p\n",
+			ep->ep.name, req);
+
+	if (!_ep || ep->ep.name == ep0name) {
+		DBG("invalid dequeue request\n");
+		if (!_ep)
+			debug("NO ENDPOINT\n");
+		if (ep->ep.name == ep0name)
+			debug("Cannot dequeue on ep0\n");
+		return -EINVAL;
+	}
+	/*
+	 * Errors should stop the queue from advancing until the
+	 * completion function returns.
+	 */
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req)
+		return -EINVAL;
+	request_complete(ep, req, -ECONNRESET);
+
+	/* Process the next request if any */
+	submit_next_request(ep);
+
+	return 0;
+}
+
+static int usba_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct usba_ep *ep = container_of(_ep, struct usba_ep, ep);
+	int ret = 0;
+
+	debug("Endpoint halt\n");
+	if (!ep->desc) {
+		debug("Attempted to halt uninitialized ep %s\n",
+				ep->ep.name);
+		return -ENODEV;
+	}
+	if (ep->is_isoc) {
+		debug("Attempted to halt isochronous ep %s\n",
+				ep->ep.name);
+		return -ENOTTY;
+	}
+
+	/*
+	 * We can't halt IN endpoints while there are still data to be
+	 * transferred
+	 */
+	if (!list_empty(&ep->queue)
+			|| ((value && ep->is_in && (usba_ep_readl(ep, STA)
+					& USBA_BF(BUSY_BANKS, -1L))))) {
+		ret = -EAGAIN;
+	} else {
+		if (value)
+			usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+		else
+			usba_ep_writel(ep, CLR_STA,
+					USBA_FORCE_STALL | USBA_TOGGLE_CLR);
+		usba_ep_readl(ep, STA);
+	}
+
+	return ret;
+}
+
+static const struct usb_ep_ops usba_ep_ops = {
+	.enable		= usba_ep_enable,
+	.disable	= usba_ep_disable,
+	.alloc_request	= usba_ep_alloc_request,
+	.free_request	= usba_ep_free_request,
+	.queue		= usba_ep_queue,
+	.dequeue	= usba_ep_dequeue,
+	.set_halt	= usba_ep_set_halt,
+};
+
+static int usba_udc_get_frame(struct usb_gadget *gadget)
+{
+	struct usba_udc *udc = to_usba_udc(gadget);
+
+	return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM));
+}
+
+static int usba_udc_wakeup(struct usb_gadget *gadget)
+{
+	struct usba_udc *udc = to_usba_udc(gadget);
+	u32 ctrl;
+	int ret = -EINVAL;
+	debug("UDC wakeup\n");
+
+	if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+		ctrl = usba_readl(udc, CTRL);
+		usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int
+usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+	struct usba_udc *udc = to_usba_udc(gadget);
+	debug("UDC set selfpowered\n");
+	if (is_selfpowered)
+		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops usba_udc_ops = {
+	.get_frame		= usba_udc_get_frame,
+	.wakeup			= usba_udc_wakeup,
+	.set_selfpowered	= usba_udc_set_selfpowered,
+};
+
+static struct usb_endpoint_descriptor usba_ep0_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 0,
+	.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize = __constant_cpu_to_le16(64),
+	/* FIXME: I have no idea what to put here */
+	.bInterval = 1,
+};
+
+static struct usba_udc the_udc = {
+	.regs = (unsigned *) AT91SAM9G45_BASE_UDPHS,
+	.fifo = (unsigned *) AT91SAM9G45_UDPHS_FIFO,
+	.gadget = {
+		.ops	= &usba_udc_ops,
+		.ep0	= &the_udc.ep[0].ep,
+		.is_dualspeed = 1,
+		.name	= driver_name,
+	},
+
+	.ep[0] = {
+		.ep = {
+			.name	= ep0name,
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 64,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 0,
+		.fifo_size	= 64,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+	.ep[1] = {
+		.ep = {
+			.name	= "ep1",
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 1024,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 1,
+		.fifo_size	= 1024,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+	.ep[2] = {
+		.ep = {
+			.name	= "ep2",
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 1024,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 2,
+		.fifo_size	= 1024,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+	.ep[3] = {
+		.ep = {
+			.name	= "ep3",
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 1024,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 3,
+		.fifo_size	= 1024,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+	.ep[4] = {
+		.ep = {
+			.name	= "ep4",
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 1024,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 4,
+		.fifo_size	= 1024,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+	.ep[5] = {
+		.ep = {
+			.name	= "ep5",
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 1024,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 5,
+		.fifo_size	= 1024,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+	.ep[6] = {
+		.ep = {
+			.name	= "ep6",
+			.ops	= &usba_ep_ops,
+			.maxpacket	= 1024,
+		},
+		.udc		= &the_udc,
+		.is_pingpong	= 0,
+		.int_mask	= 1 << 6,
+		.fifo_size	= 1024,
+		.nr_banks	= 1,
+		.can_dma        = 0,
+		.can_isoc       = 0,
+	},
+};
+
+static void reset_all_endpoints(struct usba_udc *udc)
+{
+
+	struct usba_ep *ep;
+	struct usba_request *req, *tmp_req;
+	debug("USBA : reset all endpoints\n");
+	usba_writel(udc, EPT_RST, ~0UL);
+
+	ep = container_of(udc->gadget.ep0, struct usba_ep, ep);
+	list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) {
+		list_del_init(&req->queue);
+		request_complete(ep, req, -ECONNRESET);
+	}
+
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		if (ep->desc)
+			usba_ep_disable(&ep->ep);
+	}
+}
+
+static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex)
+{
+	struct usba_ep *ep;
+	debug("get EP by Address\n");
+	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+		return container_of(udc->gadget.ep0, struct usba_ep, ep);
+
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		u8 bEndpointAddress;
+
+		if (!ep->desc)
+			continue;
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+		if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+				== (wIndex & USB_ENDPOINT_NUMBER_MASK))
+			return ep;
+	}
+	return NULL;
+}
+
+/* Called with interrupts disabled and udc->lock held */
+static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep)
+{
+	debug("Set protocol stall\n");
+	usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+	ep->state = WAIT_FOR_SETUP;
+}
+
+static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep)
+{
+	debug("IS STALLED\n");
+	if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL)
+		return 1;
+	return 0;
+}
+
+static inline void set_address(struct usba_udc *udc, unsigned int addr)
+{
+	u32 regval;
+	debug("USBA : set address %d\n", addr);
+	regval = usba_readl(udc, CTRL);
+	regval = USBA_BFINS(DEV_ADDR, addr, regval);
+	usba_writel(udc, CTRL, regval);
+}
+
+/* Avoid overly long expressions */
+static inline char feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq)
+{
+	if (crq->wValue == __constant_cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP))
+		return true;
+	return false;
+}
+
+static inline char feature_is_ep_halt(struct usb_ctrlrequest *crq)
+{
+	if (crq->wValue == __constant_cpu_to_le16(USB_ENDPOINT_HALT))
+		return true;
+	return false;
+}
+
+static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
+		struct usb_ctrlrequest *crq)
+{
+	int retval = 0;
+
+	debug("USBA : EP0 setup\n");
+	switch (crq->bRequest) {
+	case USB_REQ_GET_STATUS: {
+		u16 status;
+		debug("USBA : USB_REQ_GET_STATUS\n");
+		if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) {
+			status = cpu_to_le16(udc->devstatus);
+		} else if (crq->bRequestType
+				== (USB_DIR_IN | USB_RECIP_INTERFACE)) {
+			status =  cpu_to_le16(0);
+		} else if (crq->bRequestType
+				== (USB_DIR_IN | USB_RECIP_ENDPOINT)) {
+			struct usba_ep *target;
+
+			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+			if (!target)
+				goto stall;
+
+			status = 0;
+			if (is_stalled(udc, target))
+				status |= cpu_to_le16(1);
+		} else {
+			debug("Delegate : USB_REQ_GET_STATUS\n");
+			goto delegate;
+		}
+		/* Write directly to the FIFO. No queueing is done. */
+		if (crq->wLength != cpu_to_le16(sizeof(status)))
+			goto stall;
+		ep->state = DATA_STAGE_IN;
+		debug("Writing to FIFO\n");
+		__raw_writew(status, ep->fifo);
+		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+		break;
+	}
+
+	case USB_REQ_CLEAR_FEATURE: {
+		debug("USBA : USB_REQ_CLEAR_FEATURE\n");
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			if (feature_is_dev_remote_wakeup(crq))
+				udc->devstatus
+					&= ~(1 << USB_DEVICE_REMOTE_WAKEUP);
+			else
+				/* Can't CLEAR_FEATURE TEST_MODE */
+				goto stall;
+		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+			struct usba_ep *target;
+
+			if (crq->wLength != cpu_to_le16(0)
+					|| !feature_is_ep_halt(crq))
+				goto stall;
+			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+			if (!target)
+				goto stall;
+
+			usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL);
+			if (target->index != 0)
+				usba_ep_writel(target, CLR_STA,
+						USBA_TOGGLE_CLR);
+		} else {
+			goto delegate;
+		}
+
+		send_status(udc, ep);
+		break;
+	}
+
+	case USB_REQ_SET_FEATURE: {
+		debug("USBA :  USB_REQ_SET_FEATURE\n");
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			if (feature_is_dev_remote_wakeup(crq))
+				udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+			else
+				goto stall;
+		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+			struct usba_ep *target;
+
+			if (crq->wLength != cpu_to_le16(0)
+					|| !feature_is_ep_halt(crq))
+				goto stall;
+
+			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+			if (!target)
+				goto stall;
+
+			usba_ep_writel(target, SET_STA, USBA_FORCE_STALL);
+		} else
+			goto delegate;
+
+		send_status(udc, ep);
+		break;
+	}
+
+	case USB_REQ_SET_ADDRESS:
+		debug("USBA :  USB_REQ_SET_ADDRESS\n");
+		if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+			goto delegate;
+
+		set_address(udc, le16_to_cpu(crq->wValue));
+		send_status(udc, ep);
+		ep->state = STATUS_STAGE_ADDR;
+		break;
+
+	default:
+delegate:
+			debug("deligate SETUP Type = %4x %4x %4x\n",
+			crq->bRequest,
+			crq->wValue,
+			crq->wIndex);
+
+		retval = udc->driver->setup(&udc->gadget, crq);
+	}
+
+	return retval;
+
+stall:
+	debug("udc: %s: Invalid setup request: %02x.%02x v%04x i%04x l%d, "
+		"halting endpoint...\n",
+		ep->ep.name, crq->bRequestType, crq->bRequest,
+		le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex),
+		le16_to_cpu(crq->wLength));
+	set_protocol_stall(udc, ep);
+	return -1;
+}
+
+static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+	struct usba_request *req;
+	u32 epstatus;
+	u32 epctrl;
+
+restart:
+	epstatus = usba_ep_readl(ep, STA);
+	epctrl = usba_ep_readl(ep, CTL);
+
+	debug("%s [%d]: s/%08x c/%08x\n",
+			ep->ep.name, ep->state, epstatus, epctrl);
+
+	req = NULL;
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next,
+				 struct usba_request, queue);
+
+	if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+		debug("usba_control_irq : USBA_TX_PK_RDY\n");
+		if (req->submitted)
+			next_fifo_transaction(ep, req);
+		else
+		  submit_request(ep, req);
+
+		if (req->last_transaction) {
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+		}
+		goto restart;
+	}
+	if ((epstatus & epctrl) & USBA_TX_COMPLETE) {
+		debug("usba_control_irq : USBA_TX_COMPLETE\n");
+		usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE);
+
+		switch (ep->state) {
+		case DATA_STAGE_IN:
+			debug("USBA : DATA Stage in\n");
+			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = STATUS_STAGE_OUT;
+			break;
+		case STATUS_STAGE_ADDR:
+			debug("USBA : Status stage addr\n");
+			/* Activate our new address */
+			usba_writel(udc, CTRL, (usba_readl(udc, CTRL)
+						| USBA_FADDR_EN));
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = WAIT_FOR_SETUP;
+			break;
+		case STATUS_STAGE_IN:
+			debug("USBA : Status stage in\n");
+			if (req) {
+				list_del_init(&req->queue);
+				request_complete(ep, req, 0);
+				submit_next_request(ep);
+			}
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = WAIT_FOR_SETUP;
+			break;
+		case STATUS_STAGE_TEST:
+			debug("USBA : test mode not supported\n");
+			ep->state = WAIT_FOR_SETUP;
+			break;
+		default:
+			debug("udc: %s: TXRDY: Invalid endpoint state %d, "
+				"halting endpoint...\n",
+				ep->ep.name, ep->state);
+			set_protocol_stall(udc, ep);
+			break;
+		}
+
+		goto restart;
+	}
+	if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+		debug("USBA : USBA_RX_BK_RDY\n");
+		switch (ep->state) {
+		case STATUS_STAGE_OUT:
+			debug("%s : USBA :STATUS_STAGE_OUT\n", ep->ep.name);
+			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+
+			if (req) {
+				list_del_init(&req->queue);
+				debug("%s :USBA:request complete\n",
+				      ep->ep.name);
+				request_complete(ep, req, 0);
+			}
+			ep->state = WAIT_FOR_SETUP;
+			break;
+
+		case DATA_STAGE_OUT:
+			debug("%s : DATA STAGE OUT\n", ep->ep.name);
+			receive_data(ep);
+			break;
+
+		default:
+			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+			debug("udc: %s: RXRDY: Invalid endpoint state %d, "
+				"halting endpoint...\n",
+				ep->ep.name, ep->state);
+			set_protocol_stall(udc, ep);
+			break;
+		}
+
+		goto restart;
+	}
+	if (epstatus & USBA_RX_SETUP) {
+		debug("USBA : USBA_RX_SETUP\n");
+		union {
+			struct usb_ctrlrequest crq;
+			unsigned long data[2];
+		} crq;
+		unsigned int pkt_len;
+		int ret;
+
+		if (ep->state != WAIT_FOR_SETUP) {
+			/*
+			 * Didn't expect a SETUP packet at this
+			 * point. Clean up any pending requests (which
+			 * may be successful).
+			 */
+			int status = -EPROTO;
+
+			/*
+			 * RXRDY and TXCOMP are dropped when SETUP
+			 * packets arrive.  Just pretend we received
+			 * the status packet.
+			 */
+			if (ep->state == STATUS_STAGE_OUT
+					|| ep->state == STATUS_STAGE_IN) {
+				debug("DROPPING DATA %s\n", ep->ep.name);
+				usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+				status = 0;
+			}
+
+			if (req) {
+				list_del_init(&req->queue);
+				request_complete(ep, req, status);
+			}
+		}
+
+		pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
+		debug("Packet length: %u\n", pkt_len);
+		if (pkt_len != sizeof(crq)) {
+			debug("udc: Invalid packet length %u "
+				"(expected %zu)\n", pkt_len, sizeof(crq));
+			set_protocol_stall(udc, ep);
+			return;
+		}
+
+		debug("Copying ctrl request from 0x%p:\n", ep->fifo);
+		memcpy_fromio(crq.data, ep->fifo, sizeof(crq));
+
+		/* Free up one bank in the FIFO so that we can
+		 * generate or receive a reply right away. */
+		usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP);
+
+		if (crq.crq.bRequestType & USB_DIR_IN) {
+			/*
+			 * The USB 2.0 spec states that "if wLength is
+			 * zero, there is no data transfer phase."
+			 * However, testusb #14 seems to actually
+			 * expect a data phase even if wLength = 0...
+			 */
+			ep->state = DATA_STAGE_IN;
+		} else {
+			if (crq.crq.wLength != cpu_to_le16(0))
+				ep->state = DATA_STAGE_OUT;
+			else
+				ep->state = STATUS_STAGE_IN;
+		}
+
+		ret = -1;
+		if (ep->index == 0)
+			ret = handle_ep0_setup(udc, ep, &crq.crq);
+		else {
+			debug("driver setup %d %d\n",
+				crq.crq.wIndex, crq.crq.wValue);
+			ret = udc->driver->setup(&udc->gadget, &crq.crq);
+		}
+
+		if (ret < 0) {
+			/* Let the host know that we failed */
+			set_protocol_stall(udc, ep);
+		}
+	}
+}
+
+static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+	struct usba_request *req;
+	u32 epstatus;
+	u32 epctrl;
+
+	epstatus = usba_ep_readl(ep, STA);
+	epctrl = usba_ep_readl(ep, CTL);
+
+	while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+		debug("%s: TX PK ready\n", ep->ep.name);
+
+		if (list_empty(&ep->queue)) {
+			debug("ep_irq: queue empty\n");
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+			return;
+		}
+
+		req = list_entry(ep->queue.next, struct usba_request, queue);
+
+		if (req->submitted) {
+			debug("TX next trans %s\n", ep->ep.name);
+			next_fifo_transaction(ep, req);
+		} else {
+			debug("TX submit %s\n", ep->ep.name);
+			submit_request(ep, req);
+		}
+		if (req->last_transaction) {
+			debug("last transaction %s\n", ep->ep.name);
+			list_del_init(&req->queue);
+			submit_next_request(ep);
+			request_complete(ep, req, 0);
+		}
+
+		epstatus = usba_ep_readl(ep, STA);
+		epctrl = usba_ep_readl(ep, CTL);
+	}
+	debug("%s status = %lx\n", ep->ep.name, epstatus);
+	if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+		debug("%s RX data ready\n", ep->ep.name);
+		receive_data(ep);
+		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+	}
+}
+
+int usb_gadget_handle_interrupts(void)
+{
+	struct usba_udc *udc = &the_udc;
+	u32 status;
+	u32 ep_status;
+
+	status = usba_readl(udc, INT_STA);
+	debug("irq, status=%#08x\n", status);
+
+	if (status & USBA_DET_SUSPEND) {
+		debug("USBA : USBA_DET_SUSPEND\n");
+		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN
+				&& udc->driver && udc->driver->suspend) {
+			udc->driver->suspend(&udc->gadget);
+		}
+	}
+
+	if (status & USBA_WAKE_UP) {
+		debug("USBA : USBA_WAKE_UP\n");
+		usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+	}
+
+	if (status & USBA_END_OF_RESUME) {
+		debug("USBA : USBA_END_OF_RESUME\n");
+		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN
+				&& udc->driver && udc->driver->resume) {
+			udc->driver->resume(&udc->gadget);
+		}
+	}
+
+	ep_status = USBA_BFEXT(EPT_INT, status);
+	if (ep_status) {
+		int i;
+
+		for (i = 0; i < USBA_NR_ENDPOINTS; i++)
+			if (ep_status & (1 << i)) {
+				if (ep_is_control(&udc->ep[i])) {
+					debug("EP %d = control\n", i);
+					usba_control_irq(udc, &udc->ep[i]);
+				} else {
+					debug("EP = %d\n", i);
+					usba_ep_irq(udc, &udc->ep[i]);
+				}
+			}
+	}
+
+	if (status & USBA_END_OF_RESET) {
+		struct usba_ep *ep0;
+		debug("USBA : USBA_END_OF_RESET\n");
+		usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+		reset_all_endpoints(udc);
+
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN
+				&& udc->driver->disconnect) {
+			udc->gadget.speed = USB_SPEED_UNKNOWN;
+			udc->driver->disconnect(&udc->gadget);
+		}
+
+		if (status & USBA_HIGH_SPEED) {
+			debug("USBA : High speed\n");
+			udc->gadget.speed = USB_SPEED_HIGH;
+		} else {
+			debug("USBA : Full speed\n");
+			udc->gadget.speed = USB_SPEED_FULL;
+		}
+
+		ep0 = &udc->ep[0];
+		ep0->desc = &usba_ep0_desc;
+		ep0->state = WAIT_FOR_SETUP;
+		usba_ep_writel(ep0, CFG,
+				(USBA_BF(EPT_SIZE, EP0_EPT_SIZE)
+				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL)
+				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
+		usba_ep_writel(ep0, CTL_ENB,
+				USBA_EPT_ENABLE | USBA_RX_SETUP);
+		usba_writel(udc, INT_ENB,
+				(usba_readl(udc, INT_ENB)
+				| USBA_BF(EPT_INT, 1)
+				| USBA_DET_SUSPEND
+				| USBA_END_OF_RESUME));
+
+		/*
+		 * Unclear why we hit this irregularly, e.g. in usbtest,
+		 * but it's clearly harmless...
+		*/
+		if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) {
+			ERR("ODD: EP0 configuration is invalid!\n");
+			debug("ERROR : ep0 config invalid\n");
+		}
+		debug("USBA : USBA_END_OF_RESET - finished\n");
+	}
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct usba_udc *udc = &the_udc;
+
+	if (!driver)
+		return -ENODEV;
+	if (driver != udc->driver || !driver->unbind)
+		return -EINVAL;
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	reset_all_endpoints(udc);
+
+	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+
+	if (udc->driver->disconnect)
+		udc->driver->disconnect(&udc->gadget);
+
+	driver->unbind(&udc->gadget);
+	udc->driver = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct usba_udc *udc = &the_udc;
+	int		retval = -1;
+
+	debug("USBA : registering driver\n");
+
+	if (udc->driver) {
+		debug("UDC already has a gadget driver\n");
+		return -EBUSY;
+	}
+
+	udc->driver = driver;
+
+	if (udc->driver)
+		retval = driver->bind(&udc->gadget);
+
+	if (retval) {
+		debug("driver->bind() returned %d\n", retval);
+		udc->driver = NULL;
+		return retval;
+	}
+
+	debug("Enabling controller\n");
+	usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+	usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+
+	debug("USBA register finished\n");
+	return 0;
+}
+
+/* reinit == restore inital software state */
+static void udc_reinit(struct usba_udc *udc)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+	INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+
+	for (i = 0; i < USBA_NR_ENDPOINTS; i++) {
+		struct usba_ep *ep = &udc->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+		ep->desc = NULL;
+		ep->stopped = 0;
+		ep->fifo_bank = 0;
+		ep->ep.maxpacket = ep->maxpacket;
+		ep->ep_regs = (void __iomem *) udc->regs + USBA_EPT_BASE(i);
+		INIT_LIST_HEAD(&ep->queue);
+	}
+}
+
+int usba_udc_probe(struct platform_data *pdata)
+{
+	struct usba_udc *udc;
+	int  i;
+	/* needed to disable DMA */
+	at91_pmc_t *pmc	= (at91_pmc_t *)AT91_PMC_BASE;
+	u32 regvalue;
+
+	udc = &the_udc;
+	udc->regs = (unsigned *) AT91SAM9G45_BASE_UDPHS;
+	udc->fifo = (unsigned *) AT91SAM9G45_UDPHS_FIFO;
+
+	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+
+	debug("USBA : Probing USB controller\n");
+	writel(readl(&pmc->pcer) | (1 << AT91SAM9G45_ID_UDPHS), &pmc->pcer);
+	writel(readl(&pmc->uckr) |
+		AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr);
+
+	debug("USBA : Enable controller\n");
+	usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+	usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+
+	init_requests();
+	udc_reinit(udc);
+
+	the_udc.gadget.ep0 = &udc->ep[0].ep;
+
+	udc->ep[0].ep_regs = udc->regs + USBA_EPT_BASE(0);
+	udc->ep[0].dma_regs = udc->regs + USBA_DMA_BASE(0);
+	udc->ep[0].fifo = udc->fifo + USBA_FIFO_BASE(0);
+	udc->ep[0].ep.ops = &usba_ep_ops;
+	udc->ep[0].ep.name = the_udc.ep[0].ep.name;
+	udc->ep[0].ep.maxpacket =  64;
+	udc->ep[0].udc = &the_udc;
+	udc->ep[0].fifo_size = 64;
+	udc->ep[0].nr_banks = the_udc.ep[0].nr_banks;
+	udc->ep[0].index = 0;
+	udc->ep[0].is_pingpong = 0;
+	udc->ep[0].can_isoc = 0;
+	udc->ep[0].can_dma = 0;
+
+	for (i = 1; i < USBA_NR_ENDPOINTS; i++) {
+		struct usba_ep *ep = &udc->ep[i];
+
+		ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
+		ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
+		ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
+		ep->ep.ops = &usba_ep_ops;
+		ep->ep.name = the_udc.ep[i].ep.name;
+		ep->ep.maxpacket = 1024;
+		ep->udc = &the_udc;
+		ep->fifo_size = 1024;
+		ep->index = i;
+		if ((i == 1) || (i == 2)) {
+			ep->nr_banks = 1;
+			ep->is_pingpong = 0;
+		} else {
+			ep->nr_banks = 1;
+			ep->is_pingpong = 0;
+		}
+		ep->can_isoc = 0;
+		ep->can_dma = 0;
+	}
+
+	/* This code disables DMA : see g45 spec */
+	regvalue = usba_readl(udc, CTRL);
+	usba_writel(udc, CTRL, (regvalue & ~AT91C_UDPHS_EN_UDPHS));
+	usba_writel(udc, CTRL, (regvalue | AT91C_UDPHS_EN_UDPHS));
+
+	regvalue = usba_readl(udc, CTRL);
+	debug("UDPHS_CTRL after reset 0x%lx\n", regvalue);
+	for (i = 1; i <= (((AT91SAM9G45_BASE_UDPHS + UDPHS_IPFEATURES) &
+			AT91C_UDPHS_DMA_CHANNEL_NBR) >> 4); i++) {
+		debug("UDPHS ep %d disable DMA\n", i);
+		/* DMA stop channel command */
+		__raw_writel(0x00,
+			AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+		/* Disable endpoint */
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+			0x100 + (0x20*i) + UDPHS_EPTCTLDIS);
+		__raw_writel((regvalue | 0XFFFFFFFF),
+			AT91SAM9G45_BASE_UDPHS +
+			0x100 + (0x20*i) + UDPHS_EPTCTLDIS);
+		/* Reset endpoint config */
+		__raw_writel(0x00,
+			AT91SAM9G45_BASE_UDPHS +
+			0x100 + (0x20*i) + UDPHS_EPTCFG);
+		/* Reset DMA channel (Buff count and Control field) */
+		__raw_writel(0x02,
+			AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+		/* DMA stop channel command */
+		__raw_writel(0x00,
+			AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+		/* Clear DMA channel status (read register to clear it) */
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMASTATUS);
+		__raw_writel(regvalue,
+			AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMASTATUS);
+
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMACONTROL);
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+			0x100 + (0x20*i) + UDPHS_EPTCTLDIS);
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+			0x100 + (0x20*i) + UDPHS_EPTCFG);
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS +
+			(UDPHS_DMA + 0x10*i) + UDPHS_DMASTATUS);
+		regvalue = readl(AT91SAM9G45_BASE_UDPHS + 0xF0);
+	}
+	debug("USBA : Probing finished\n");
+	return 0;
+}
+
+MODULE_DESCRIPTION("Atmel usba udc driver");
+MODULE_AUTHOR("Marcel Janssen");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel_usba_udc");
+
diff --git a/include/usb/atmel_usba_udc.h b/include/usb/atmel_usba_udc.h
new file mode 100644
index 0000000..b165003
--- /dev/null
+++ b/include/usb/atmel_usba_udc.h
@@ -0,0 +1,398 @@
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2005-2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
+#define __LINUX_USB_GADGET_USBA_UDC_H__
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/list.h>
+
+#define __iomem
+
+/* USB register offsets */
+#define USBA_CTRL				0x0000
+#define USBA_FNUM				0x0004
+#define USBA_INT_ENB				0x0010
+#define USBA_INT_STA				0x0014
+#define USBA_INT_CLR				0x0018
+#define USBA_EPT_RST				0x001c
+#define USBA_TST				0x00e0
+
+/* USB endpoint register offsets */
+#define USBA_EPT_CFG				0x0000
+#define USBA_EPT_CTL_ENB			0x0004
+#define USBA_EPT_CTL_DIS			0x0008
+#define USBA_EPT_CTL				0x000c
+#define USBA_EPT_SET_STA			0x0014
+#define USBA_EPT_CLR_STA			0x0018
+#define USBA_EPT_STA				0x001c
+
+/* USB DMA register offsets */
+#define USBA_DMA_NXT_DSC			0x0000
+#define USBA_DMA_ADDRESS			0x0004
+#define USBA_DMA_CONTROL			0x0008
+#define USBA_DMA_STATUS				0x000c
+
+/* Bitfields in CTRL */
+#define USBA_DEV_ADDR_OFFSET			0
+#define USBA_DEV_ADDR_SIZE			7
+#define USBA_FADDR_EN				(1 <<  7)
+#define USBA_EN_USBA				(1 <<  8)
+#define USBA_DETACH				(1 <<  9)
+#define USBA_REMOTE_WAKE_UP			(1 << 10)
+#define USBA_PULLD_DIS				(1 << 11)
+
+#if defined(CONFIG_AVR32)
+#define USBA_ENABLE_MASK			USBA_EN_USBA
+#define USBA_DISABLE_MASK			0
+#elif defined(CONFIG_ARCH_AT91)
+#define USBA_ENABLE_MASK			(USBA_EN_USBA | USBA_PULLD_DIS)
+#define USBA_DISABLE_MASK			USBA_DETACH
+#endif /* CONFIG_ARCH_AT91 */
+
+/* Bitfields in FNUM */
+#define USBA_MICRO_FRAME_NUM_OFFSET		0
+#define USBA_MICRO_FRAME_NUM_SIZE		3
+#define USBA_FRAME_NUMBER_OFFSET		3
+#define USBA_FRAME_NUMBER_SIZE			11
+#define USBA_FRAME_NUM_ERROR			(1 << 31)
+
+/* Bitfields in INT_ENB/INT_STA/INT_CLR */
+#define USBA_HIGH_SPEED				(1 <<  0)
+#define USBA_DET_SUSPEND			(1 <<  1)
+#define USBA_MICRO_SOF				(1 <<  2)
+#define USBA_SOF				(1 <<  3)
+#define USBA_END_OF_RESET			(1 <<  4)
+#define USBA_WAKE_UP				(1 <<  5)
+#define USBA_END_OF_RESUME			(1 <<  6)
+#define USBA_UPSTREAM_RESUME			(1 <<  7)
+#define USBA_EPT_INT_OFFSET			8
+#define USBA_EPT_INT_SIZE			16
+#define USBA_DMA_INT_OFFSET			24
+#define USBA_DMA_INT_SIZE			8
+
+/* Bitfields in EPT_RST */
+#define USBA_RST_OFFSET				0
+#define USBA_RST_SIZE				16
+
+/* Bitfields in USBA_TST */
+#define USBA_SPEED_CFG_OFFSET			0
+#define USBA_SPEED_CFG_SIZE			2
+#define USBA_TST_J_MODE				(1 <<  2)
+#define USBA_TST_K_MODE				(1 <<  3)
+#define USBA_TST_PKT_MODE			(1 <<  4)
+#define USBA_OPMODE2				(1 <<  5)
+
+/* Bitfields in EPT_CFG */
+#define USBA_EPT_SIZE_OFFSET			0
+#define USBA_EPT_SIZE_SIZE			3
+#define USBA_EPT_DIR_IN				(1 <<  3)
+#define USBA_EPT_TYPE_OFFSET			4
+#define USBA_EPT_TYPE_SIZE			2
+#define USBA_BK_NUMBER_OFFSET			6
+#define USBA_BK_NUMBER_SIZE			2
+#define USBA_NB_TRANS_OFFSET			8
+#define USBA_NB_TRANS_SIZE			2
+#define USBA_EPT_MAPPED				(1 << 31)
+
+/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */
+#define USBA_EPT_ENABLE				(1 <<  0)
+#define USBA_AUTO_VALID				(1 <<  1)
+#define USBA_INTDIS_DMA				(1 <<  3)
+#define USBA_NYET_DIS				(1 <<  4)
+#define USBA_DATAX_RX				(1 <<  6)
+#define USBA_MDATA_RX				(1 <<  7)
+/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */
+#define USBA_BUSY_BANK_IE			(1 << 18)
+
+/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */
+#define USBA_FORCE_STALL			(1 <<  5)
+#define USBA_TOGGLE_CLR				(1 <<  6)
+#define USBA_TOGGLE_SEQ_OFFSET			6
+#define USBA_TOGGLE_SEQ_SIZE			2
+#define USBA_ERR_OVFLW				(1 <<  8)
+#define USBA_RX_BK_RDY				(1 <<  9)
+#define USBA_KILL_BANK				(1 <<  9)
+#define USBA_TX_COMPLETE			(1 << 10)
+#define USBA_TX_PK_RDY				(1 << 11)
+#define USBA_ISO_ERR_TRANS			(1 << 11)
+#define USBA_RX_SETUP				(1 << 12)
+#define USBA_ISO_ERR_FLOW			(1 << 12)
+#define USBA_STALL_SENT				(1 << 13)
+#define USBA_ISO_ERR_CRC			(1 << 13)
+#define USBA_ISO_ERR_NBTRANS			(1 << 13)
+#define USBA_NAK_IN				(1 << 14)
+#define USBA_ISO_ERR_FLUSH			(1 << 14)
+#define USBA_NAK_OUT				(1 << 15)
+#define USBA_CURRENT_BANK_OFFSET		16
+#define USBA_CURRENT_BANK_SIZE			2
+#define USBA_BUSY_BANKS_OFFSET			18
+#define USBA_BUSY_BANKS_SIZE			2
+#define USBA_BYTE_COUNT_OFFSET			20
+#define USBA_BYTE_COUNT_SIZE			11
+#define USBA_SHORT_PACKET			(1 << 31)
+
+/* Bitfields in DMA_CONTROL */
+#define USBA_DMA_CH_EN				(1 <<  0)
+#define USBA_DMA_LINK				(1 <<  1)
+#define USBA_DMA_END_TR_EN			(1 <<  2)
+#define USBA_DMA_END_BUF_EN			(1 <<  3)
+#define USBA_DMA_END_TR_IE			(1 <<  4)
+#define USBA_DMA_END_BUF_IE			(1 <<  5)
+#define USBA_DMA_DESC_LOAD_IE			(1 <<  6)
+#define USBA_DMA_BURST_LOCK			(1 <<  7)
+#define USBA_DMA_BUF_LEN_OFFSET			16
+#define USBA_DMA_BUF_LEN_SIZE			16
+
+/* Bitfields in DMA_STATUS */
+#define USBA_DMA_CH_ACTIVE			(1 <<  1)
+#define USBA_DMA_END_TR_ST			(1 <<  4)
+#define USBA_DMA_END_BUF_ST			(1 <<  5)
+#define USBA_DMA_DESC_LOAD_ST			(1 <<  6)
+
+/* Constants for SPEED_CFG */
+#define USBA_SPEED_CFG_NORMAL			0
+#define USBA_SPEED_CFG_FORCE_HIGH		2
+#define USBA_SPEED_CFG_FORCE_FULL		3
+
+/* Constants for EPT_SIZE */
+#define USBA_EPT_SIZE_8				0
+#define USBA_EPT_SIZE_16			1
+#define USBA_EPT_SIZE_32			2
+#define USBA_EPT_SIZE_64			3
+#define USBA_EPT_SIZE_128			4
+#define USBA_EPT_SIZE_256			5
+#define USBA_EPT_SIZE_512			6
+#define USBA_EPT_SIZE_1024			7
+
+/* Constants for EPT_TYPE */
+#define USBA_EPT_TYPE_CONTROL			0
+#define USBA_EPT_TYPE_ISO			1
+#define USBA_EPT_TYPE_BULK			2
+#define USBA_EPT_TYPE_INT			3
+
+/* Constants for BK_NUMBER */
+#define USBA_BK_NUMBER_ZERO			0
+#define USBA_BK_NUMBER_ONE			1
+#define USBA_BK_NUMBER_DOUBLE			2
+#define USBA_BK_NUMBER_TRIPLE			3
+
+/* Bit manipulation macros */
+#define USBA_BF(name, value)					\
+	(((value) & ((1 << USBA_##name##_SIZE) - 1))		\
+	 << USBA_##name##_OFFSET)
+#define USBA_BFEXT(name, value)					\
+	(((value) >> USBA_##name##_OFFSET)			\
+	 & ((1 << USBA_##name##_SIZE) - 1))
+#define USBA_BFINS(name, value, old)				\
+	(((old) & ~(((1 << USBA_##name##_SIZE) - 1)		\
+		    << USBA_##name##_OFFSET))			\
+	 | USBA_BF(name, value))
+
+/* Register access macros */
+#define usba_readl(udc, reg)					\
+	__raw_readl((udc)->regs + USBA_##reg)
+#define usba_writel(udc, reg, value)				\
+	__raw_writel((value), (udc)->regs + USBA_##reg)
+#define usba_ep_readl(ep, reg)					\
+	__raw_readl((ep)->ep_regs + USBA_EPT_##reg)
+#define usba_ep_writel(ep, reg, value)				\
+	__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
+#define usba_dma_readl(ep, reg)					\
+	__raw_readl((ep)->dma_regs + USBA_DMA_##reg)
+#define usba_dma_writel(ep, reg, value)				\
+	__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
+
+/* Calculate base address for a given endpoint or DMA controller */
+#define USBA_EPT_BASE(x)	(0x100 + (x) * 0x20)
+#define USBA_DMA_BASE(x)	(0x300 + (x) * 0x10)
+#define USBA_FIFO_BASE(x)	((x) << 16)
+
+/* Synth parameters */
+#define USBA_NR_ENDPOINTS	7
+
+#define EP0_FIFO_SIZE		64
+#define EP0_EPT_SIZE		USBA_EPT_SIZE_64
+#define EP0_NR_BANKS		1
+
+/*
+ * REVISIT: Try to eliminate this value. Can we rely on req->mapped to
+ * provide this information?
+ */
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+#define FIFO_IOMEM_ID	0
+#define CTRL_IOMEM_ID	1
+
+#define DBG_ERR		0x0001	/* report all error returns */
+#define DBG_HW		0x0002	/* debug hardware initialization */
+#define DBG_GADGET	0x0004	/* calls to/from gadget driver */
+#define DBG_INT		0x0008	/* interrupts */
+#define DBG_BUS		0x0010	/* report changes in bus state */
+#define DBG_QUEUE	0x0020  /* debug request queue processing */
+#define DBG_FIFO	0x0040  /* debug FIFO contents */
+#define DBG_DMA		0x0080  /* debug DMA handling */
+#define DBG_REQ		0x0100	/* print out queued request length */
+#define DBG_ALL		0xffff
+#define DBG_NONE	0x0000
+
+#define DEBUG_LEVEL	(DBG_ERR)
+
+enum usba_ctrl_state {
+	WAIT_FOR_SETUP,
+	DATA_STAGE_IN,
+	DATA_STAGE_OUT,
+	STATUS_STAGE_IN,
+	STATUS_STAGE_OUT,
+	STATUS_STAGE_ADDR,
+	STATUS_STAGE_TEST,
+};
+
+struct usba_ep {
+	int				state;
+	struct usb_ep			ep;
+	struct list_head		queue;
+	struct usba_udc			*udc;
+	void __iomem			*fifo;
+	void __iomem			*dma_regs;
+	void __iomem			*ep_regs;
+
+	unsigned			maxpacket:16;
+	u8				int_mask;
+	unsigned			is_pingpong:1;
+	u8				index;
+	u16				fifo_size;
+	u8				nr_banks;
+	unsigned			stopped:1;
+	unsigned int			can_dma:1;
+	unsigned int			can_isoc:1;
+	unsigned			is_in:1;
+	unsigned			is_isoc:1;
+	unsigned			fifo_bank:1;
+
+	const struct usb_endpoint_descriptor	*desc;
+};
+
+struct usba_request {
+	struct usb_request		req;
+	struct list_head		queue;
+	unsigned int			last_transaction:1;
+	unsigned int			submitted:1;
+	unsigned			in_use;
+};
+
+/* USB Device */
+struct usba_udc_data {
+	u8	vbus_pin;		/* high == host powering us */
+	u8	pullup_pin;		/* high == D+ pulled up */
+};
+
+struct platform_data {
+	struct usba_udc_data	board;
+	unsigned		udc_clk;
+};
+
+/*
+ * driver is non-SMP, and just blocks IRQs whenever it needs
+ * access protection for chip registers or driver state
+ */
+
+struct usba_udc {
+	void __iomem *regs;
+	void __iomem *fifo;
+	struct usb_gadget		gadget;
+	struct usba_ep			ep[USBA_NR_ENDPOINTS];
+	struct usb_gadget_driver	*driver;
+	struct usba_udc_data		board;
+	unsigned			vbus:1;
+	int irq;
+	int vbus_pin;
+	struct clk *pclk;
+	struct clk *hclk;
+	u16 devstatus;
+	int vbus_prev;
+};
+
+static inline struct usba_ep *to_usba_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct usba_ep, ep);
+}
+
+static inline struct usba_request *to_usba_req(struct usb_request *req)
+{
+	return container_of(req, struct usba_request, req);
+}
+
+static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct usba_udc, gadget);
+}
+
+int usba_udc_probe(struct platform_data *pdata);
+
+#define ep_is_control(ep)	((ep)->index == 0)
+#define ep_is_idle(ep)		((ep)->state == EP_STATE_IDLE)
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG (stuff...)		printf("udc: " stuff)
+#else
+#define DBG (stuff...)		do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define VDBG(x...)	printf(x)
+#else
+#define VDBG(stuff...)	do {} while (0)
+#endif
+
+#ifdef PACKET_TRACE
+#    define PACKET		VDBG
+#else
+#    define PACKET(stuff...)	do {} while (0)
+#endif
+
+#define ERR(stuff...)		printf("ERR udc: " stuff)
+#define WARN(stuff...)		printf("WARNING udc: " stuff)
+#define INFO(stuff...)		printf("INFO udc: " stuff)
+
+/* following defines come form At91Bootstrap.
+ * They're needed for disabling DMA for the USB device controller
+ */
+#define UDPHS_CTRL			(0)
+#define AT91C_UDPHS_EN_UDPHS		(0x1 <<  8)
+#define UDPHS_IPFEATURES		(248)
+#define AT91C_UDPHS_DMA_CHANNEL_NBR	(0x7 <<  4)
+#define UDPHS_DMACONTROL		(8)
+#define UDPHS_EPT			(256)
+#define AT91C_BASE_UDPHS_EPT_0		(0xFFF78100)
+#define UDPHS_EPTCTLDIS			(8)
+#define UDPHS_EPTCFG			(0)
+#define UDPHS_EPTCTL			(12)
+#define UDPHS_DMA			(768)
+#define AT91C_BASE_UDPHS_DMA_1		(0xFFF78310)
+#define AT91C_BASE_UDPHS_DMA_1		(0xFFF78310)
+#define AT91C_BASE_UDPHS_DMA_2		(0xFFF78320)
+#define AT91C_BASE_UDPHS_DMA_3		(0xFFF78330)
+#define AT91C_BASE_UDPHS_DMA_4		(0xFFF78340)
+#define AT91C_BASE_UDPHS_DMA_5		(0xFFF78350)
+#define AT91C_BASE_UDPHS_DMA_6		(0xFFF78360)
+#define UDPHS_DMASTATUS			(12)
+#define AT91C_BASE_UDPHS_EPTFIFO	(0x00600000)
+#define AT91C_UDPHS_EPTFIFO_READEPT3	(0x00630000)
+#define AT91C_UDPHS_EPTFIFO_READEPT5	(0x00650000)
+#define AT91C_UDPHS_EPTFIFO_READEPT1	(0x00610000)
+#define AT91C_UDPHS_EPTFIFO_READEPT0	(0x00600000)
+#define AT91C_UDPHS_EPTFIFO_READEPT6	(0x00660000)
+#define AT91C_UDPHS_EPTFIFO_READEPT2	(0x00620000)
+#define AT91C_UDPHS_EPTFIFO_READEPT4	(0x00640000)
+
+#endif /* __LINUX_USB_GADGET_USBA_UDC_H */
-- 
1.7.3.4



More information about the U-Boot mailing list