diff mbox

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

Message ID 1297633938-15622-1-git-send-email-korgull@home.nl
State Changes Requested
Delegated to: Remy Bohmer
Headers show

Commit Message

Marcel Janssen Feb. 13, 2011, 9:52 p.m. UTC
From: Marcel <korgull@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@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

Comments

Remy Bohmer Feb. 15, 2011, 8:04 p.m. UTC | #1
Hi,

Continuing producing some remarks:

2011/2/13 Marcel Janssen <korgull@home.nl>:
> From: Marcel <korgull@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

Strange header.

> Signed-off-by: Marcel <korgull@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.
> + */

Is this GPLv2 only from its origin? I thought only GPLv2+ code was acceptable.
See http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign

> +               if (!(ptr->in_use))
> +                       debug("%s: ptr not marked as \"in_use\"\n", __func__);
> +
> +               hang();

Is hang required? Is there no recovery to the terminal possible?

> +       if (!ptr)
> +               DBG("panic: no more free req buffers\n");

Cool. A panic in a debug printf that is removed by the preprocessor.
Is it really panic?

> +       udc->regs = (unsigned *) AT91SAM9G45_BASE_UDPHS;
> +       udc->fifo = (unsigned *) AT91SAM9G45_UDPHS_FIFO;

Is at91sam9g45 the only SoC that has this UDP-USBA controller?
Make it a generic define. Globally.

Please fix the double empty lines globally as well.

This patch looks relatively clean compared to the reset of the series.
Please rework comments.

Kind regards,

Remy
Marcel Janssen Feb. 15, 2011, 8:20 p.m. UTC | #2
Hi Remy,

> Continuing producing some remarks:
> 
> 2011/2/13 Marcel Janssen <korgull@home.nl>:
> > From: Marcel <korgull@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
> 
> Strange header.
> 
> > Signed-off-by: Marcel <korgull@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.
> > + */
> 
> Is this GPLv2 only from its origin? I thought only GPLv2+ code was
> acceptable. See
> http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Si
> gn

Wow, there's something I completely missed.
I would need to check on that. The original code comes from kernel 2.6.33 and 
I didn't change that part.

> > +               if (!(ptr->in_use))
> > +                       debug("%s: ptr not marked as \"in_use\"\n",
> > __func__); +
> > +               hang();
> 
> Is hang required? Is there no recovery to the terminal possible?

I guess not.

> > +       if (!ptr)
> > +               DBG("panic: no more free req buffers\n");
> 
> Cool. A panic in a debug printf that is removed by the preprocessor.
> Is it really panic?

Actually I think this should never be hit unless someone calls it wrong.

> > +       udc->regs = (unsigned *) AT91SAM9G45_BASE_UDPHS;
> > +       udc->fifo = (unsigned *) AT91SAM9G45_UDPHS_FIFO;
> 
> Is at91sam9g45 the only SoC that has this UDP-USBA controller?
> Make it a generic define. Globally.

So far I haven't seen others than the at91sam9g45 and at91sam9g45m10

> Please fix the double empty lines globally as well.
>
> This patch looks relatively clean compared to the reset of the series.
> Please rework comments.

I see what I can do.

Best regards,
Marcel
diff mbox

Patch

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 */