mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/5] Enable a console over a serial USB gadget
@ 2011-11-24  3:01 Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 1/5] console: add console unregistering Robert Jarzmik
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Robert Jarzmik @ 2011-11-24  3:01 UTC (permalink / raw)
  To: barebox

This patchset aims at providing a working console over a USB port.
It includes:
 - a driver for PXA27x SoC UDC device
 - various fixes in the serial gadgets
 - a small evolution in the generic console to sustain a console unregistering
  (as when a USB cable is severed)

This serie was obviously tested on a XScale PX27x chip.

Cheers.

--
Robert

Robert Jarzmik (5):
  console: add console unregistering
  usb/gadget: add pxa27x_udc driver
  usb/gadget: use generic usb_gadget_poll() in u_serial
  usb/gadget: add USB serial connect and disconnect
  usb/gadget: make serial gadget resistant to cable unplug

 arch/arm/mach-pxa/include/mach/udc_pxa2xx.h |   26 +
 common/console.c                            |   24 +-
 common/console_simple.c                     |    4 +
 drivers/usb/gadget/Kconfig                  |    7 +
 drivers/usb/gadget/Makefile                 |    1 +
 drivers/usb/gadget/f_serial.c               |    5 +-
 drivers/usb/gadget/pxa27x_udc.c             | 1524 +++++++++++++++++++++++++++
 drivers/usb/gadget/pxa27x_udc.h             |  393 +++++++
 drivers/usb/gadget/u_serial.c               |   27 +-
 include/console.h                           |    1 +
 10 files changed, 1997 insertions(+), 15 deletions(-)
 create mode 100644 arch/arm/mach-pxa/include/mach/udc_pxa2xx.h
 create mode 100644 drivers/usb/gadget/pxa27x_udc.c
 create mode 100644 drivers/usb/gadget/pxa27x_udc.h

-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] console: add console unregistering
  2011-11-24  3:01 [PATCH 0/5] Enable a console over a serial USB gadget Robert Jarzmik
@ 2011-11-24  3:01 ` Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 2/5] usb/gadget: add pxa27x_udc driver Robert Jarzmik
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Jarzmik @ 2011-11-24  3:01 UTC (permalink / raw)
  To: barebox; +Cc: Robert Jarzmik

From: Robert Jarzmik <robert.jarzmik@atosorigin.com>

Some console are transient, like the USB connected serial
console which should be removed when the USB connection is
severed.

Enable console removal for such devices.

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
 common/console.c        |   24 ++++++++++++++++++------
 common/console_simple.c |    4 ++++
 include/console.h       |    1 +
 3 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/common/console.c b/common/console.c
index 06e9c29..706d9b2 100644
--- a/common/console.c
+++ b/common/console.c
@@ -154,13 +154,9 @@ int console_register(struct console_device *newcdev)
 
 	list_add_tail(&newcdev->list, &console_list);
 
-	if (console_output_buffer) {
-		while (kfifo_getc(console_output_buffer, &ch) == 0)
-			console_putc(CONSOLE_STDOUT, ch);
-		kfifo_free(console_output_buffer);
-		console_output_buffer = NULL;
-	}
 
+	while (kfifo_getc(console_output_buffer, &ch) == 0)
+		console_putc(CONSOLE_STDOUT, ch);
 	if (first)
 		barebox_banner();
 
@@ -168,6 +164,22 @@ int console_register(struct console_device *newcdev)
 }
 EXPORT_SYMBOL(console_register);
 
+int console_unregister(struct console_device *cdev)
+{
+	struct device_d *dev = &cdev->class_dev;
+	int status;
+
+	list_del(&cdev->list);
+	if (list_empty(&console_list))
+		initialized = CONSOLE_UNINITIALIZED;
+
+	status = unregister_device(dev);
+	if (!status)
+		memset(cdev, 0, sizeof(*cdev));
+	return status;
+}
+EXPORT_SYMBOL(console_unregister);
+
 static int getc_raw(void)
 {
 	struct console_device *cdev;
diff --git a/common/console_simple.c b/common/console_simple.c
index 7304d8e..7e0619d 100644
--- a/common/console_simple.c
+++ b/common/console_simple.c
@@ -157,3 +157,7 @@ int console_register(struct console_device *newcdev)
 	}
 	return 0;
 }
+
+int console_unregister(struct console_device *cdev)
+{
+}
diff --git a/include/console.h b/include/console.h
index 3bcc5db..c0817f6 100644
--- a/include/console.h
+++ b/include/console.h
@@ -49,6 +49,7 @@ struct console_device {
 };
 
 int console_register(struct console_device *cdev);
+int console_unregister(struct console_device *cdev);
 
 extern struct list_head console_list;
 #define for_each_console(console) list_for_each_entry(console, &console_list, list)
-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/5] usb/gadget: add pxa27x_udc driver
  2011-11-24  3:01 [PATCH 0/5] Enable a console over a serial USB gadget Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 1/5] console: add console unregistering Robert Jarzmik
@ 2011-11-24  3:01 ` Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 3/5] usb/gadget: use generic usb_gadget_poll() in u_serial Robert Jarzmik
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Jarzmik @ 2011-11-24  3:01 UTC (permalink / raw)
  To: barebox; +Cc: Robert Jarzmik

From: Robert Jarzmik <robert.jarzmik@atosorigin.com>

Adapt mainline kernel pxa27x_udc driver to barebox :
 - remove function header comments as they are in mainline
 - test it with serial gadget

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
 arch/arm/mach-pxa/include/mach/udc_pxa2xx.h |   26 +
 drivers/usb/gadget/Kconfig                  |    7 +
 drivers/usb/gadget/Makefile                 |    1 +
 drivers/usb/gadget/pxa27x_udc.c             | 1524 +++++++++++++++++++++++++++
 drivers/usb/gadget/pxa27x_udc.h             |  393 +++++++
 5 files changed, 1951 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-pxa/include/mach/udc_pxa2xx.h
 create mode 100644 drivers/usb/gadget/pxa27x_udc.c
 create mode 100644 drivers/usb/gadget/pxa27x_udc.h

diff --git a/arch/arm/mach-pxa/include/mach/udc_pxa2xx.h b/arch/arm/mach-pxa/include/mach/udc_pxa2xx.h
new file mode 100644
index 0000000..dcdd36a
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/udc_pxa2xx.h
@@ -0,0 +1,26 @@
+/*
+ * arch/arm/mach-pxa/include/mach/udc_pxa2xx.h
+ *
+ * This supports machine-specific differences in how the PXA2xx
+ * USB Device Controller (UDC) is wired.
+ *
+ * It is set in linux/arch/arm/mach-pxa/<machine>.c or in
+ * linux/arch/mach-ixp4xx/<machine>.c and used in
+ * the probe routine of linux/drivers/usb/gadget/pxa2xx_udc.c
+ */
+
+struct pxa2xx_udc_mach_info {
+	int  (*udc_is_connected)(void);		/* do we see host? */
+	void (*udc_command)(int cmd);
+#define	PXA2XX_UDC_CMD_CONNECT		0	/* let host see us */
+#define	PXA2XX_UDC_CMD_DISCONNECT	1	/* so host won't see us */
+
+	/* Boards following the design guidelines in the developer's manual,
+	 * with on-chip GPIOs not Lubbock's weird hardware, can have a sane
+	 * VBUS IRQ and omit the methods above.  Store the GPIO number
+	 * here.  Note that sometimes the signals go through inverters...
+	 */
+	bool	gpio_pullup_inverted;
+	int	gpio_pullup;			/* high == pullup activated */
+};
+
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index b8375c6..472c591 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -14,6 +14,13 @@ config USB_GADGET_DRIVER_ARC
 	prompt "Arc OTG device core"
 	depends on ARCH_IMX
 	select USB_GADGET_DUALSPEED
+
+config USB_GADGET_DRIVER_PXA27X
+	bool
+	prompt "PXA27x gadget driver"
+	depends on ARCH_PXA
+	select USB_GADGET_DUALSPEED
+	select POLLER
 endchoice
 
 choice
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 804bb91..e034786 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o
 obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o
 obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
 obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o
+obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 0000000..d0dbee9
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,1524 @@
+/*
+ * Handles the Intel 27x USB Device Controller (UDC)
+ *
+ * Inspired by original driver by Frank Becker, David Brownell, and others.
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Taken from linux-2.6 kernel and adapted to barebox.
+ */
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <io.h>
+#include <gpio.h>
+#include <init.h>
+#include <poller.h>
+
+#include <usb/ch9.h>
+#include <usb/gadget.h>
+
+#include "pxa27x_udc.h"
+#include <mach/udc_pxa2xx.h>
+#include <mach/pxa-regs.h>
+
+#define	DRIVER_VERSION	"2008-04-18"
+#define	DRIVER_DESC	"PXA 27x USB Device Controller driver"
+
+static const char driver_name[] = "pxa27x_udc";
+static struct pxa_udc *the_controller;
+
+static void handle_ep(struct pxa_ep *ep);
+
+static int is_match_usb_pxa(struct udc_usb_ep *udc_usb_ep, struct pxa_ep *ep,
+		int config, int interface, int altsetting)
+{
+	if (usb_endpoint_num(&udc_usb_ep->desc) != ep->addr)
+		return 0;
+	if (usb_endpoint_dir_in(&udc_usb_ep->desc) != ep->dir_in)
+		return 0;
+	if (usb_endpoint_type(&udc_usb_ep->desc) != ep->type)
+		return 0;
+	if ((ep->config != config) || (ep->interface != interface)
+			|| (ep->alternate != altsetting))
+		return 0;
+	return 1;
+}
+
+static struct pxa_ep *find_pxa_ep(struct pxa_udc *udc,
+		struct udc_usb_ep *udc_usb_ep)
+{
+	int i;
+	struct pxa_ep *ep;
+	int cfg = udc->config;
+	int iface = udc->last_interface;
+	int alt = udc->last_alternate;
+
+	if (udc_usb_ep == &udc->udc_usb_ep[0])
+		return &udc->pxa_ep[0];
+
+	for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
+		ep = &udc->pxa_ep[i];
+		if (is_match_usb_pxa(udc_usb_ep, ep, cfg, iface, alt))
+			return ep;
+	}
+	return NULL;
+}
+
+static void update_pxa_ep_matches(struct pxa_udc *udc)
+{
+	int i;
+	struct udc_usb_ep *udc_usb_ep;
+
+	for (i = 1; i < NR_USB_ENDPOINTS; i++) {
+		udc_usb_ep = &udc->udc_usb_ep[i];
+		if (udc_usb_ep->pxa_ep)
+			udc_usb_ep->pxa_ep = find_pxa_ep(udc, udc_usb_ep);
+	}
+}
+
+static void pio_irq_enable(struct pxa_ep *ep)
+{
+	struct pxa_udc *udc = ep->dev;
+	int index = EPIDX(ep);
+	u32 udcicr0 = udc_readl(udc, UDCICR0);
+	u32 udcicr1 = udc_readl(udc, UDCICR1);
+
+	if (index < 16)
+		udc_writel(udc, UDCICR0, udcicr0 | (3 << (index * 2)));
+	else
+		udc_writel(udc, UDCICR1, udcicr1 | (3 << ((index - 16) * 2)));
+}
+
+static void pio_irq_disable(struct pxa_ep *ep)
+{
+	struct pxa_udc *udc = ep->dev;
+	int index = EPIDX(ep);
+	u32 udcicr0 = udc_readl(udc, UDCICR0);
+	u32 udcicr1 = udc_readl(udc, UDCICR1);
+
+	if (index < 16)
+		udc_writel(udc, UDCICR0, udcicr0 & ~(3 << (index * 2)));
+	else
+		udc_writel(udc, UDCICR1, udcicr1 & ~(3 << ((index - 16) * 2)));
+}
+
+static inline void udc_set_mask_UDCCR(struct pxa_udc *udc, int mask)
+{
+	u32 udccr = udc_readl(udc, UDCCR);
+	udc_writel(udc, UDCCR,
+			(udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS));
+}
+
+static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask)
+{
+	u32 udccr = udc_readl(udc, UDCCR);
+	udc_writel(udc, UDCCR,
+			(udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS));
+}
+
+static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask)
+{
+	if (is_ep0(ep))
+		mask |= UDCCSR0_ACM;
+	udc_ep_writel(ep, UDCCSR, mask);
+}
+
+static int ep_count_bytes_remain(struct pxa_ep *ep)
+{
+	if (ep->dir_in)
+		return -EOPNOTSUPP;
+	return udc_ep_readl(ep, UDCBCR) & 0x3ff;
+}
+
+static int ep_is_empty(struct pxa_ep *ep)
+{
+	int ret;
+
+	if (!is_ep0(ep) && ep->dir_in)
+		return -EOPNOTSUPP;
+	if (is_ep0(ep))
+		ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR0_RNE);
+	else
+		ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNE);
+	return ret;
+}
+
+static int ep_is_full(struct pxa_ep *ep)
+{
+	if (is_ep0(ep))
+		return udc_ep_readl(ep, UDCCSR) & UDCCSR0_IPR;
+	if (!ep->dir_in)
+		return -EOPNOTSUPP;
+	return !(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNF);
+}
+
+static int epout_has_pkt(struct pxa_ep *ep)
+{
+	if (!is_ep0(ep) && ep->dir_in)
+		return -EOPNOTSUPP;
+	if (is_ep0(ep))
+		return udc_ep_readl(ep, UDCCSR) & UDCCSR0_OPC;
+	return udc_ep_readl(ep, UDCCSR) & UDCCSR_PC;
+}
+
+static void set_ep0state(struct pxa_udc *udc, int state)
+{
+	struct pxa_ep *ep = &udc->pxa_ep[0];
+	char *old_stname = EP0_STNAME(udc);
+
+	udc->ep0state = state;
+	ep_dbg(ep, "state=%s->%s, udccsr0=0x%03x, udcbcr=%d\n", old_stname,
+		EP0_STNAME(udc), udc_ep_readl(ep, UDCCSR),
+		udc_ep_readl(ep, UDCBCR));
+}
+
+static void ep0_idle(struct pxa_udc *dev)
+{
+	set_ep0state(dev, WAIT_FOR_SETUP);
+}
+
+static __init void pxa_ep_setup(struct pxa_ep *ep)
+{
+	u32 new_udccr;
+
+	new_udccr = ((ep->config << UDCCONR_CN_S) & UDCCONR_CN)
+		| ((ep->interface << UDCCONR_IN_S) & UDCCONR_IN)
+		| ((ep->alternate << UDCCONR_AISN_S) & UDCCONR_AISN)
+		| ((EPADDR(ep) << UDCCONR_EN_S) & UDCCONR_EN)
+		| ((EPXFERTYPE(ep) << UDCCONR_ET_S) & UDCCONR_ET)
+		| ((ep->dir_in) ? UDCCONR_ED : 0)
+		| ((ep->fifo_size << UDCCONR_MPS_S) & UDCCONR_MPS)
+		| UDCCONR_EE;
+
+	udc_ep_writel(ep, UDCCR, new_udccr);
+}
+
+static __init void pxa_eps_setup(struct pxa_udc *dev)
+{
+	unsigned int i;
+
+	dev_dbg(dev->dev, "%s: dev=%p\n", __func__, dev);
+
+	for (i = 1; i < NR_PXA_ENDPOINTS; i++)
+		pxa_ep_setup(&dev->pxa_ep[i]);
+}
+
+static struct usb_request *pxa_ep_alloc_request(struct usb_ep *_ep)
+{
+	struct pxa27x_request *req;
+
+	req = xzalloc(sizeof *req);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	req->in_use = 0;
+	req->udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+
+	return &req->req;
+}
+
+static void pxa_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa27x_request *req;
+
+	req = container_of(_req, struct pxa27x_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static void ep_add_request(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	if (unlikely(!req))
+		return;
+	ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req,
+		req->req.length, udc_ep_readl(ep, UDCCSR));
+
+	req->in_use = 1;
+	list_add_tail(&req->queue, &ep->queue);
+	pio_irq_enable(ep);
+}
+
+static void ep_del_request(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	if (unlikely(!req))
+		return;
+	ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req,
+		req->req.length, udc_ep_readl(ep, UDCCSR));
+
+	list_del_init(&req->queue);
+	req->in_use = 0;
+	if (!is_ep0(ep) && list_empty(&ep->queue))
+		pio_irq_disable(ep);
+}
+
+static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status)
+{
+	ep_del_request(ep, req);
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		ep_dbg(ep, "complete req %p stat %d len %u/%u\n",
+			&req->req, status,
+			req->req.actual, req->req.length);
+
+	req->req.complete(&req->udc_usb_ep->usb_ep, &req->req);
+}
+
+static void ep_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	req_done(ep, req, 0);
+}
+
+static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	set_ep0state(ep->dev, OUT_STATUS_STAGE);
+	ep_end_out_req(ep, req);
+	ep0_idle(ep->dev);
+}
+
+static void ep_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	req_done(ep, req, 0);
+}
+
+static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	set_ep0state(ep->dev, IN_STATUS_STAGE);
+	ep_end_in_req(ep, req);
+}
+
+static void nuke(struct pxa_ep *ep, int status)
+{
+	struct pxa27x_request	*req;
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+		req_done(ep, req, status);
+	}
+}
+
+static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	u32 *buf;
+	int bytes_ep, bufferspace, count, i;
+
+	bytes_ep = ep_count_bytes_remain(ep);
+	bufferspace = req->req.length - req->req.actual;
+
+	buf = (u32 *)(req->req.buf + req->req.actual);
+
+	if (likely(!ep_is_empty(ep)))
+		count = min(bytes_ep, bufferspace);
+	else /* zlp */
+		count = 0;
+
+	for (i = count; i > 0; i -= 4)
+		*buf++ = udc_ep_readl(ep, UDCDR);
+	req->req.actual += count;
+
+	ep_write_UDCCSR(ep, UDCCSR_PC);
+
+	return count;
+}
+
+static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req,
+			unsigned int max)
+{
+	int length, count, remain, i;
+	u32 *buf;
+	u8 *buf_8;
+
+	buf = (u32 *)(req->req.buf + req->req.actual);
+	prefetch(buf);
+
+	length = min(req->req.length - req->req.actual, max);
+	req->req.actual += length;
+
+	remain = length & 0x3;
+	count = length & ~(0x3);
+	for (i = count; i > 0 ; i -= 4)
+		udc_ep_writel(ep, UDCDR, *buf++);
+
+	buf_8 = (u8 *)buf;
+	for (i = remain; i > 0; i--)
+		udc_ep_writeb(ep, UDCDR, *buf_8++);
+
+	ep_vdbg(ep, "length=%d+%d, udccsr=0x%03x\n", count, remain,
+		udc_ep_readl(ep, UDCCSR));
+
+	return length;
+}
+
+static int read_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	int count, is_short, completed = 0;
+
+	while (epout_has_pkt(ep)) {
+		count = read_packet(ep, req);
+
+		is_short = (count < ep->fifo_size);
+		ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n",
+			udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "",
+			&req->req, req->req.actual, req->req.length);
+
+		/* completion */
+		if (is_short || req->req.actual == req->req.length) {
+			completed = 1;
+			break;
+		}
+		/* finished that packet.  the next one may be waiting... */
+	}
+	return completed;
+}
+
+static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	unsigned max;
+	int count, is_short, is_last = 0, completed = 0, totcount = 0;
+	u32 udccsr;
+
+	max = ep->fifo_size;
+	do {
+		is_short = 0;
+
+		udccsr = udc_ep_readl(ep, UDCCSR);
+		if (udccsr & UDCCSR_PC) {
+			ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
+				udccsr);
+			ep_write_UDCCSR(ep, UDCCSR_PC);
+		}
+		if (udccsr & UDCCSR_TRN) {
+			ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n",
+				udccsr);
+			ep_write_UDCCSR(ep, UDCCSR_TRN);
+		}
+
+		count = write_packet(ep, req, max);
+		totcount += count;
+
+		/* last packet is usually short (or a zlp) */
+		if (unlikely(count < max)) {
+			is_last = 1;
+			is_short = 1;
+		} else {
+			if (likely(req->req.length > req->req.actual)
+					|| req->req.zero)
+				is_last = 0;
+			else
+				is_last = 1;
+			/* interrupt/iso maxpacket may not fill the fifo */
+			is_short = unlikely(max < ep->fifo_size);
+		}
+
+		if (is_short)
+			ep_write_UDCCSR(ep, UDCCSR_SP);
+
+		/* requests complete when all IN data is in the FIFO */
+		if (is_last) {
+			completed = 1;
+			break;
+		}
+	} while (!ep_is_full(ep));
+
+	ep_dbg(ep, "wrote count:%d bytes%s%s, left:%d req=%p\n",
+			totcount, is_last ? "/L" : "", is_short ? "/S" : "",
+			req->req.length - req->req.actual, &req->req);
+
+	return completed;
+}
+
+static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	int count, is_short, completed = 0;
+
+	while (epout_has_pkt(ep)) {
+		count = read_packet(ep, req);
+		ep_write_UDCCSR(ep, UDCCSR0_OPC);
+
+		is_short = (count < ep->fifo_size);
+		ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n",
+			udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "",
+			&req->req, req->req.actual, req->req.length);
+
+		if (is_short || req->req.actual >= req->req.length) {
+			completed = 1;
+			break;
+		}
+	}
+
+	return completed;
+}
+
+static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	unsigned	count;
+	int		is_last, is_short;
+
+	count = write_packet(ep, req, EP0_FIFO_SIZE);
+
+	is_short = (count < EP0_FIFO_SIZE);
+	is_last = ((count == 0) || (count < EP0_FIFO_SIZE));
+
+	/* Sends either a short packet or a 0 length packet */
+	if (unlikely(is_short))
+		ep_write_UDCCSR(ep, UDCCSR0_IPR);
+
+	ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n",
+		count, is_short ? "/S" : "", is_last ? "/L" : "",
+		req->req.length - req->req.actual,
+		&req->req, udc_ep_readl(ep, UDCCSR));
+
+	return is_last;
+}
+
+static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct udc_usb_ep	*udc_usb_ep;
+	struct pxa_ep		*ep;
+	struct pxa27x_request	*req;
+	struct pxa_udc		*dev;
+	int			rc = 0;
+	int			is_first_req;
+	unsigned		length;
+
+	req = container_of(_req, struct pxa27x_request, req);
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+
+	if (unlikely(!_req || !_req->complete || !_req->buf))
+		return -EINVAL;
+
+	if (unlikely(!_ep))
+		return -EINVAL;
+
+	dev = udc_usb_ep->dev;
+	ep = udc_usb_ep->pxa_ep;
+	if (unlikely(!ep))
+		return -EINVAL;
+
+	dev = ep->dev;
+	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		ep_dbg(ep, "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	/* iso is always one packet per request, that's the only way
+	 * we can report per-packet status.  that also helps with dma.
+	 */
+	if (unlikely(EPXFERTYPE_is_ISO(ep)
+			&& req->req.length > ep->fifo_size))
+		return -EMSGSIZE;
+
+	is_first_req = list_empty(&ep->queue);
+	ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n",
+			_req, is_first_req ? "yes" : "no",
+			_req->length, _req->buf);
+
+	if (!ep->enabled) {
+		_req->status = -ESHUTDOWN;
+		rc = -ESHUTDOWN;
+		goto out_locked;
+	}
+
+	if (req->in_use) {
+		ep_err(ep, "refusing to queue req %p (already queued)\n", req);
+		goto out_locked;
+	}
+
+	length = _req->length;
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	ep_add_request(ep, req);
+
+	if (is_ep0(ep)) {
+		switch (dev->ep0state) {
+		case WAIT_ACK_SET_CONF_INTERF:
+			if (length == 0) {
+				ep_end_in_req(ep, req);
+			} else {
+				ep_err(ep, "got a request of %d bytes while"
+					"in state WAIT_ACK_SET_CONF_INTERF\n",
+					length);
+				ep_del_request(ep, req);
+				rc = -EL2HLT;
+			}
+			ep0_idle(ep->dev);
+			break;
+		case IN_DATA_STAGE:
+			if (!ep_is_full(ep))
+				if (write_ep0_fifo(ep, req))
+					ep0_end_in_req(ep, req);
+			break;
+		case OUT_DATA_STAGE:
+			if ((length == 0) || !epout_has_pkt(ep))
+				if (read_ep0_fifo(ep, req))
+					ep0_end_out_req(ep, req);
+			break;
+		default:
+			ep_err(ep, "odd state %s to send me a request\n",
+				EP0_STNAME(ep->dev));
+			ep_del_request(ep, req);
+			rc = -EL2HLT;
+			break;
+		}
+	} else {
+		handle_ep(ep);
+	}
+
+out:
+	return rc;
+out_locked:
+	goto out;
+}
+
+static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	struct pxa27x_request	*req;
+	int			rc = -EINVAL;
+
+	if (!_ep)
+		return rc;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return rc;
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			rc = 0;
+			break;
+		}
+	}
+
+	if (!rc)
+		req_done(ep, req, -ECONNRESET);
+	return rc;
+}
+
+static int pxa_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	int rc;
+
+
+	if (!_ep)
+		return -EINVAL;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return -EINVAL;
+
+	if (value == 0) {
+		/*
+		 * This path (reset toggle+halt) is needed to implement
+		 * SET_INTERFACE on normal hardware.  but it can't be
+		 * done from software on the PXA UDC, and the hardware
+		 * forgets to do it as part of SET_INTERFACE automagic.
+		 */
+		ep_dbg(ep, "only host can clear halt\n");
+		return -EROFS;
+	}
+
+	rc = -EAGAIN;
+	if (ep->dir_in	&& (ep_is_full(ep) || !list_empty(&ep->queue)))
+		goto out;
+
+	/* FST, FEF bits are the same for control and non control endpoints */
+	rc = 0;
+	ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF);
+	if (is_ep0(ep))
+		set_ep0state(ep->dev, STALL);
+
+out:
+	return rc;
+}
+
+static int pxa_ep_fifo_status(struct usb_ep *_ep)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+
+	if (!_ep)
+		return -ENODEV;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return -ENODEV;
+
+	if (ep->dir_in)
+		return -EOPNOTSUPP;
+	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN || ep_is_empty(ep))
+		return 0;
+	else
+		return ep_count_bytes_remain(ep) + 1;
+}
+
+static void pxa_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+
+	if (!_ep)
+		return;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return;
+
+	if (unlikely(!list_empty(&ep->queue)))
+		ep_dbg(ep, "called while queue list not empty\n");
+	ep_dbg(ep, "called\n");
+
+	/* for OUT, just read and discard the FIFO contents. */
+	if (!ep->dir_in) {
+		while (!ep_is_empty(ep))
+			udc_ep_readl(ep, UDCDR);
+	} else {
+		/* most IN status is the same, but ISO can't stall */
+		ep_write_UDCCSR(ep,
+				UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN
+				| (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST));
+	}
+}
+
+static int pxa_ep_enable(struct usb_ep *_ep,
+	const struct usb_endpoint_descriptor *desc)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	struct pxa_udc		*udc;
+
+	if (!_ep || !desc)
+		return -EINVAL;
+
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	if (udc_usb_ep->pxa_ep) {
+		ep = udc_usb_ep->pxa_ep;
+		ep_warn(ep, "usb_ep %s already enabled, doing nothing\n",
+			_ep->name);
+	} else {
+		ep = find_pxa_ep(udc_usb_ep->dev, udc_usb_ep);
+	}
+
+	if (!ep || is_ep0(ep)) {
+		dev_err(udc_usb_ep->dev->dev,
+			"unable to match pxa_ep for ep %s\n",
+			_ep->name);
+		return -EINVAL;
+	}
+
+	if ((desc->bDescriptorType != USB_DT_ENDPOINT)
+			|| (ep->type != usb_endpoint_type(desc))) {
+		ep_err(ep, "type mismatch\n");
+		return -EINVAL;
+	}
+
+	if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+		ep_err(ep, "bad maxpacket\n");
+		return -ERANGE;
+	}
+
+	udc_usb_ep->pxa_ep = ep;
+	udc = ep->dev;
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		ep_err(ep, "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	ep->enabled = 1;
+
+	/* flush fifo (mostly for OUT buffers) */
+	pxa_ep_fifo_flush(_ep);
+
+	ep_dbg(ep, "enabled\n");
+	return 0;
+}
+
+static int pxa_ep_disable(struct usb_ep *_ep)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+
+	if (!_ep)
+		return -EINVAL;
+
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep) || !list_empty(&ep->queue))
+		return -EINVAL;
+
+	ep->enabled = 0;
+	nuke(ep, -ESHUTDOWN);
+
+	pxa_ep_fifo_flush(_ep);
+	udc_usb_ep->pxa_ep = NULL;
+
+	ep_dbg(ep, "disabled\n");
+	return 0;
+}
+
+static struct usb_ep_ops pxa_ep_ops = {
+	.enable		= pxa_ep_enable,
+	.disable	= pxa_ep_disable,
+
+	.alloc_request	= pxa_ep_alloc_request,
+	.free_request	= pxa_ep_free_request,
+
+	.queue		= pxa_ep_queue,
+	.dequeue	= pxa_ep_dequeue,
+
+	.set_halt	= pxa_ep_set_halt,
+	.fifo_status	= pxa_ep_fifo_status,
+	.fifo_flush	= pxa_ep_fifo_flush,
+};
+
+static void dplus_pullup(struct pxa_udc *udc, int on)
+{
+	if (on) {
+		if (udc->mach->gpio_pullup > 0)
+			gpio_set_value(udc->mach->gpio_pullup,
+				       !udc->mach->gpio_pullup_inverted);
+		if (udc->mach->udc_command)
+			udc->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+	} else {
+		if (udc->mach->gpio_pullup > 0)
+			gpio_set_value(udc->mach->gpio_pullup,
+				       udc->mach->gpio_pullup_inverted);
+		if (udc->mach->udc_command)
+			udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+	}
+	udc->pullup_on = on;
+}
+
+/**
+ * pxa_udc_get_frame - Returns usb frame number
+ * @_gadget: usb gadget
+ */
+static int pxa_udc_get_frame(struct usb_gadget *_gadget)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	return udc_readl(udc, UDCFNR) & 0x7ff;
+}
+
+static int pxa_udc_wakeup(struct usb_gadget *_gadget)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	/* host may not have enabled remote wakeup */
+	if ((udc_readl(udc, UDCCR) & UDCCR_DWRE) == 0)
+		return -EHOSTUNREACH;
+	udc_set_mask_UDCCR(udc, UDCCR_UDR);
+	return 0;
+}
+
+static void udc_enable(struct pxa_udc *udc);
+static void udc_disable(struct pxa_udc *udc);
+
+static int should_enable_udc(struct pxa_udc *udc)
+{
+	int put_on;
+
+	put_on = ((udc->pullup_on) && (udc->driver));
+	put_on &= udc->vbus_sensed;
+	return put_on;
+}
+
+static int should_disable_udc(struct pxa_udc *udc)
+{
+	int put_off;
+
+	put_off = ((!udc->pullup_on) || (!udc->driver));
+	put_off |= !udc->vbus_sensed;
+	return put_off;
+}
+
+static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	if ((udc->mach->gpio_pullup < 0) && !udc->mach->udc_command)
+		return -EOPNOTSUPP;
+
+	dplus_pullup(udc, is_active);
+
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	if (should_disable_udc(udc))
+		udc_disable(udc);
+	return 0;
+}
+
+static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	udc->vbus_sensed = is_active;
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	if (should_disable_udc(udc))
+		udc_disable(udc);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops pxa_udc_ops = {
+	.get_frame	= pxa_udc_get_frame,
+	.wakeup		= pxa_udc_wakeup,
+	.pullup		= pxa_udc_pullup,
+	.vbus_session	= pxa_udc_vbus_session,
+};
+
+static void clk_enable(void)
+{
+	CKEN |= CKEN_USB;
+}
+
+static void clk_disable(void)
+{
+	CKEN &= ~CKEN_USB;
+}
+
+static void udc_disable(struct pxa_udc *udc)
+{
+	if (!udc->enabled)
+		return;
+
+	udc_writel(udc, UDCICR0, 0);
+	udc_writel(udc, UDCICR1, 0);
+
+	udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+	clk_disable();
+
+	ep0_idle(udc);
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	udc->enabled = 0;
+}
+
+static __init void udc_init_data(struct pxa_udc *dev)
+{
+	int i;
+	struct pxa_ep *ep;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+	dev->udc_usb_ep[0].pxa_ep = &dev->pxa_ep[0];
+	ep0_idle(dev);
+
+	/* PXA endpoints init */
+	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
+		ep = &dev->pxa_ep[i];
+
+		ep->enabled = is_ep0(ep);
+		INIT_LIST_HEAD(&ep->queue);
+	}
+
+	/* USB endpoints init */
+	for (i = 1; i < NR_USB_ENDPOINTS; i++)
+		list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list,
+				&dev->gadget.ep_list);
+}
+
+static void udc_enable(struct pxa_udc *udc)
+{
+	if (udc->enabled)
+		return;
+
+	udc_writel(udc, UDCICR0, 0);
+	udc_writel(udc, UDCICR1, 0);
+	udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+
+	clk_enable();
+
+	ep0_idle(udc);
+	udc->gadget.speed = USB_SPEED_FULL;
+	memset(&udc->stats, 0, sizeof(udc->stats));
+
+	udc_set_mask_UDCCR(udc, UDCCR_UDE);
+	ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
+	udelay(2);
+	if (udc_readl(udc, UDCCR) & UDCCR_EMCE)
+		dev_err(udc->dev, "Configuration errors, udc disabled\n");
+
+	/*
+	 * Caller must be able to sleep in order to cope with startup transients
+	 */
+	mdelay(100);
+
+	/* enable suspend/resume and reset irqs */
+	udc_writel(udc, UDCICR1,
+			UDCICR1_IECC | UDCICR1_IERU
+			| UDCICR1_IESU | UDCICR1_IERS);
+
+	pio_irq_enable(&udc->pxa_ep[0]);
+	udc->enabled = 1;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct pxa_udc *udc = the_controller;
+	int retval;
+
+	if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind
+			|| !driver->disconnect || !driver->setup)
+		return -EINVAL;
+	if (!udc)
+		return -ENODEV;
+	if (udc->driver)
+		return -EBUSY;
+
+	/* first hook up the driver ... */
+	udc->driver = driver;
+	dplus_pullup(udc, 1);
+
+	retval = driver->bind(&udc->gadget);
+	if (retval) {
+		dev_err(udc->dev, "bind to function %s --> error %d\n",
+			driver->function, retval);
+		goto bind_fail;
+	}
+	dev_dbg(udc->dev, "registered gadget function '%s'\n",
+		driver->function);
+
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	return 0;
+
+bind_fail:
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
+{
+	int i;
+
+	/* don't disconnect drivers more than once */
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (i = 0; i < NR_USB_ENDPOINTS; i++)
+		pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep);
+
+	if (driver)
+		driver->disconnect(&udc->gadget);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct pxa_udc *udc = the_controller;
+
+	if (!udc)
+		return -ENODEV;
+	if (!driver || driver != udc->driver || !driver->unbind)
+		return -EINVAL;
+
+	stop_activity(udc, driver);
+	udc_disable(udc);
+	dplus_pullup(udc, 0);
+
+	driver->disconnect(&udc->gadget);
+	driver->unbind(&udc->gadget);
+	udc->driver = NULL;
+
+	/*
+	dev_info(udc->dev, "unregistered gadget driver '%s'\n",
+		 driver->driver.name);
+	*/
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static void handle_ep0_ctrl_req(struct pxa_udc *udc,
+				struct pxa27x_request *req)
+{
+	struct pxa_ep *ep = &udc->pxa_ep[0];
+	union {
+		struct usb_ctrlrequest	r;
+		u32			word[2];
+	} u;
+	int i;
+	int have_extrabytes = 0;
+
+	nuke(ep, -EPROTO);
+
+	/*
+	 * In the PXA320 manual, in the section about Back-to-Back setup
+	 * packets, it describes this situation.  The solution is to set OPC to
+	 * get rid of the status packet, and then continue with the setup
+	 * packet. Generalize to pxa27x CPUs.
+	 */
+	if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0))
+		ep_write_UDCCSR(ep, UDCCSR0_OPC);
+
+	/* read SETUP packet */
+	for (i = 0; i < 2; i++) {
+		if (unlikely(ep_is_empty(ep)))
+			goto stall;
+		u.word[i] = udc_ep_readl(ep, UDCDR);
+	}
+
+	have_extrabytes = !ep_is_empty(ep);
+	while (!ep_is_empty(ep)) {
+		i = udc_ep_readl(ep, UDCDR);
+		ep_err(ep, "wrong to have extra bytes for setup : 0x%08x\n", i);
+	}
+
+	ep_dbg(ep, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+		u.r.bRequestType, u.r.bRequest,
+		le16_to_cpu(u.r.wValue), le16_to_cpu(u.r.wIndex),
+		le16_to_cpu(u.r.wLength));
+	if (unlikely(have_extrabytes))
+		goto stall;
+
+	if (u.r.bRequestType & USB_DIR_IN)
+		set_ep0state(udc, IN_DATA_STAGE);
+	else
+		set_ep0state(udc, OUT_DATA_STAGE);
+
+	/* Tell UDC to enter Data Stage */
+	ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC);
+
+	i = udc->driver->setup(&udc->gadget, &u.r);
+	if (i < 0)
+		goto stall;
+out:
+	return;
+stall:
+	ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n",
+		udc_ep_readl(ep, UDCCSR), i);
+	ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF);
+	set_ep0state(udc, STALL);
+	goto out;
+}
+
+static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
+{
+	u32			udccsr0;
+	struct pxa_ep		*ep = &udc->pxa_ep[0];
+	struct pxa27x_request	*req = NULL;
+	int			completed = 0;
+
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+	udccsr0 = udc_ep_readl(ep, UDCCSR);
+	ep_dbg(ep, "state=%s, req=%p, udccsr0=0x%03x, udcbcr=%d, irq_msk=%x\n",
+		EP0_STNAME(udc), req, udccsr0, udc_ep_readl(ep, UDCBCR),
+		(fifo_irq << 1 | opc_irq));
+
+	if (udccsr0 & UDCCSR0_SST) {
+		ep_dbg(ep, "clearing stall status\n");
+		nuke(ep, -EPIPE);
+		ep_write_UDCCSR(ep, UDCCSR0_SST);
+		ep0_idle(udc);
+	}
+
+	if (udccsr0 & UDCCSR0_SA) {
+		nuke(ep, 0);
+		set_ep0state(udc, SETUP_STAGE);
+	}
+
+	switch (udc->ep0state) {
+	case WAIT_FOR_SETUP:
+		/*
+		 * Hardware bug : beware, we cannot clear OPC, since we would
+		 * miss a potential OPC irq for a setup packet.
+		 * So, we only do ... nothing, and hope for a next irq with
+		 * UDCCSR0_SA set.
+		 */
+		break;
+	case SETUP_STAGE:
+		udccsr0 &= UDCCSR0_CTRL_REQ_MASK;
+		if (likely(udccsr0 == UDCCSR0_CTRL_REQ_MASK))
+			handle_ep0_ctrl_req(udc, req);
+		break;
+	case IN_DATA_STAGE:			/* GET_DESCRIPTOR */
+		if (epout_has_pkt(ep))
+			ep_write_UDCCSR(ep, UDCCSR0_OPC);
+		if (req && !ep_is_full(ep))
+			completed = write_ep0_fifo(ep, req);
+		if (completed)
+			ep0_end_in_req(ep, req);
+		break;
+	case OUT_DATA_STAGE:			/* SET_DESCRIPTOR */
+		if (epout_has_pkt(ep) && req)
+			completed = read_ep0_fifo(ep, req);
+		if (completed)
+			ep0_end_out_req(ep, req);
+		break;
+	case STALL:
+		ep_write_UDCCSR(ep, UDCCSR0_FST);
+		break;
+	case IN_STATUS_STAGE:
+		/*
+		 * Hardware bug : beware, we cannot clear OPC, since we would
+		 * miss a potential PC irq for a setup packet.
+		 * So, we only put the ep0 into WAIT_FOR_SETUP state.
+		 */
+		if (opc_irq)
+			ep0_idle(udc);
+		break;
+	case OUT_STATUS_STAGE:
+	case WAIT_ACK_SET_CONF_INTERF:
+		ep_warn(ep, "should never get in %s state here!!!\n",
+				EP0_STNAME(ep->dev));
+		ep0_idle(udc);
+		break;
+	}
+}
+
+static void handle_ep(struct pxa_ep *ep)
+{
+	struct pxa27x_request	*req;
+	int completed;
+	u32 udccsr;
+	int is_in = ep->dir_in;
+	int loop = 0;
+
+	do {
+		completed = 0;
+		udccsr = udc_ep_readl(ep, UDCCSR);
+
+		if (likely(!list_empty(&ep->queue)))
+			req = list_entry(ep->queue.next,
+					struct pxa27x_request, queue);
+		else
+			req = NULL;
+
+		ep_dbg(ep, "req:%p, udccsr 0x%03x loop=%d\n",
+				req, udccsr, loop++);
+
+		if (unlikely(udccsr & (UDCCSR_SST | UDCCSR_TRN)))
+			udc_ep_writel(ep, UDCCSR,
+					udccsr & (UDCCSR_SST | UDCCSR_TRN));
+		if (!req)
+			break;
+
+		if (unlikely(is_in)) {
+			if (likely(!ep_is_full(ep)))
+				completed = write_fifo(ep, req);
+		} else {
+			if (likely(epout_has_pkt(ep)))
+				completed = read_fifo(ep, req);
+		}
+
+		if (completed) {
+			if (is_in)
+				ep_end_in_req(ep, req);
+			else
+				ep_end_out_req(ep, req);
+		}
+	} while (completed);
+
+	return;
+}
+
+static void pxa27x_change_configuration(struct pxa_udc *udc, int config)
+{
+	struct usb_ctrlrequest req ;
+
+	dev_dbg(udc->dev, "config=%d\n", config);
+
+	udc->config = config;
+	udc->last_interface = 0;
+	udc->last_alternate = 0;
+
+	req.bRequestType = 0;
+	req.bRequest = USB_REQ_SET_CONFIGURATION;
+	req.wValue = config;
+	req.wIndex = 0;
+	req.wLength = 0;
+
+	set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
+	udc->driver->setup(&udc->gadget, &req);
+}
+
+static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt)
+{
+	struct usb_ctrlrequest  req;
+
+	dev_dbg(udc->dev, "interface=%d, alternate setting=%d\n", iface, alt);
+
+	udc->last_interface = iface;
+	udc->last_alternate = alt;
+
+	req.bRequestType = USB_RECIP_INTERFACE;
+	req.bRequest = USB_REQ_SET_INTERFACE;
+	req.wValue = alt;
+	req.wIndex = iface;
+	req.wLength = 0;
+
+	set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
+	ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
+	udc->driver->setup(&udc->gadget, &req);
+}
+
+static void irq_handle_data(struct pxa_udc *udc)
+{
+	int i;
+	struct pxa_ep *ep;
+	u32 udcisr0 = udc_readl(udc, UDCISR0) & UDCCISR0_EP_MASK;
+	u32 udcisr1 = udc_readl(udc, UDCISR1) & UDCCISR1_EP_MASK;
+
+	if (udcisr0 & UDCISR_INT_MASK) {
+		udc_writel(udc, UDCISR0, UDCISR_INT(0, UDCISR_INT_MASK));
+		handle_ep0(udc, !!(udcisr0 & UDCICR_FIFOERR),
+				!!(udcisr0 & UDCICR_PKTCOMPL));
+	}
+
+	udcisr0 >>= 2;
+	for (i = 1; udcisr0 != 0 && i < 16; udcisr0 >>= 2, i++) {
+		if (!(udcisr0 & UDCISR_INT_MASK))
+			continue;
+
+		udc_writel(udc, UDCISR0, UDCISR_INT(i, UDCISR_INT_MASK));
+
+		WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
+		if (i < ARRAY_SIZE(udc->pxa_ep)) {
+			ep = &udc->pxa_ep[i];
+			if (ep->enabled)
+				handle_ep(ep);
+		}
+	}
+
+	for (i = 16; udcisr1 != 0 && i < 24; udcisr1 >>= 2, i++) {
+		udc_writel(udc, UDCISR1, UDCISR_INT(i - 16, UDCISR_INT_MASK));
+		if (!(udcisr1 & UDCISR_INT_MASK))
+			continue;
+
+		WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
+		if (i < ARRAY_SIZE(udc->pxa_ep)) {
+			ep = &udc->pxa_ep[i];
+			if (ep->enabled)
+				handle_ep(ep);
+		}
+	}
+
+}
+
+static void irq_udc_suspend(struct pxa_udc *udc)
+{
+	udc_writel(udc, UDCISR1, UDCISR1_IRSU);
+
+	if (udc->gadget.speed != USB_SPEED_UNKNOWN
+			&& udc->driver && udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+	ep0_idle(udc);
+}
+
+static void irq_udc_resume(struct pxa_udc *udc)
+{
+	udc_writel(udc, UDCISR1, UDCISR1_IRRU);
+
+	if (udc->gadget.speed != USB_SPEED_UNKNOWN
+			&& udc->driver && udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+static void irq_udc_reconfig(struct pxa_udc *udc)
+{
+	unsigned config, interface, alternate, config_change;
+	u32 udccr = udc_readl(udc, UDCCR);
+
+	udc_writel(udc, UDCISR1, UDCISR1_IRCC);
+
+	config = (udccr & UDCCR_ACN) >> UDCCR_ACN_S;
+	config_change = (config != udc->config);
+	if (config_change)
+		update_pxa_ep_matches(udc);
+	udc_set_mask_UDCCR(udc, UDCCR_SMAC);
+	ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
+
+	pxa27x_change_configuration(udc, config);
+	interface = (udccr & UDCCR_AIN) >> UDCCR_AIN_S;
+	alternate = (udccr & UDCCR_AAISN) >> UDCCR_AAISN_S;
+
+	/*
+	 * barebox specific: do not call change interface, as change_config has
+	 * already setup the gadget.
+	 * pxa27x_change_interface(udc, interface, alternate);
+	 */
+}
+
+static void irq_udc_reset(struct pxa_udc *udc)
+{
+	u32 udccr = udc_readl(udc, UDCCR);
+	struct pxa_ep *ep = &udc->pxa_ep[0];
+
+	dev_info(udc->dev, "USB reset\n");
+	udc_writel(udc, UDCISR1, UDCISR1_IRRS);
+
+	if ((udccr & UDCCR_UDA) == 0) {
+		dev_dbg(udc->dev, "USB reset start\n");
+		stop_activity(udc, udc->driver);
+	}
+	udc->gadget.speed = USB_SPEED_FULL;
+
+	nuke(ep, -EPROTO);
+	ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC);
+	ep0_idle(udc);
+}
+
+int usb_gadget_poll(void)
+{
+	struct pxa_udc *udc = the_controller;
+	u32 udcisr0 = udc_readl(udc, UDCISR0);
+	u32 udcisr1 = udc_readl(udc, UDCISR1);
+	u32 udccr = udc_readl(udc, UDCCR);
+	u32 udcisr1_spec;
+	int ret = 0;
+
+	udc->vbus_sensed = udc->mach->udc_is_connected();
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	if (should_disable_udc(udc)) {
+		stop_activity(udc, udc->driver);
+		udc_disable(udc);
+	}
+
+	if (!udc->enabled)
+		return -EIO;
+
+	dev_dbg(udc->dev, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, "
+		"UDCCR:0x%08x\n", udcisr0, udcisr1, udccr);
+
+	udcisr1_spec = udcisr1 & 0xf8000000;
+	if (unlikely(udcisr1_spec & UDCISR1_IRSU))
+		irq_udc_suspend(udc);
+	if (unlikely(udcisr1_spec & UDCISR1_IRRU))
+		irq_udc_resume(udc);
+	if (unlikely(udcisr1_spec & UDCISR1_IRCC))
+		irq_udc_reconfig(udc);
+	if (unlikely(udcisr1_spec & UDCISR1_IRRS))
+		irq_udc_reset(udc);
+	if (udcisr1_spec)
+		ret = 1;
+
+	if ((udcisr0 & UDCCISR0_EP_MASK) | (udcisr1 & UDCCISR1_EP_MASK)) {
+		irq_handle_data(udc);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static struct pxa_udc memory = {
+	.gadget = {
+		.ops		= &pxa_udc_ops,
+		.ep0		= &memory.udc_usb_ep[0].usb_ep,
+		.name		= driver_name,
+	},
+
+	.udc_usb_ep = {
+		USB_EP_CTRL,
+		USB_EP_OUT_BULK(1),
+		USB_EP_IN_BULK(2),
+		USB_EP_IN_ISO(3),
+		USB_EP_OUT_ISO(4),
+		USB_EP_IN_INT(5),
+	},
+
+	.pxa_ep = {
+		PXA_EP_CTRL,
+		/* Endpoints for gadget zero */
+		PXA_EP_OUT_BULK(1, 1, 3, 0, 0),
+		PXA_EP_IN_BULK(2,  2, 3, 0, 0),
+		/* Endpoints for ether gadget, file storage gadget */
+		PXA_EP_OUT_BULK(3, 1, 1, 0, 0),
+		PXA_EP_IN_BULK(4,  2, 1, 0, 0),
+		PXA_EP_IN_ISO(5,   3, 1, 0, 0),
+		PXA_EP_OUT_ISO(6,  4, 1, 0, 0),
+		PXA_EP_IN_INT(7,   5, 1, 0, 0),
+		/* Endpoints for RNDIS, serial */
+		PXA_EP_OUT_BULK(8, 1, 2, 0, 0),
+		PXA_EP_IN_BULK(9,  2, 2, 0, 0),
+		PXA_EP_IN_INT(10,  5, 2, 0, 0),
+		/*
+		 * All the following endpoints are only for completion.  They
+		 * won't never work, as multiple interfaces are really broken on
+		 * the pxa.
+		*/
+		PXA_EP_OUT_BULK(11, 1, 2, 1, 0),
+		PXA_EP_IN_BULK(12,  2, 2, 1, 0),
+		/* Endpoint for CDC Ether */
+		PXA_EP_OUT_BULK(13, 1, 1, 1, 1),
+		PXA_EP_IN_BULK(14,  2, 1, 1, 1),
+	}
+};
+
+static int __init pxa_udc_probe(struct device_d *dev)
+{
+	struct pxa_udc *udc = &memory;
+	int gpio;
+
+	udc->regs = dev_request_mem_region(dev, 0);
+	if (!udc->regs)
+		return -ENXIO;
+
+	udc->dev = dev;
+	udc->mach = dev->platform_data;
+
+	gpio = udc->mach->gpio_pullup;
+	if (gpio >= 0) {
+		gpio_direction_output(gpio,
+				      udc->mach->gpio_pullup_inverted);
+	}
+
+	udc->vbus_sensed = 0;
+
+	the_controller = udc;
+	udc_init_data(udc);
+	pxa_eps_setup(udc);
+	return 0;
+}
+
+#define pxa27x_clear_otgph()   do {} while (0)
+
+static struct driver_d udc_driver = {
+	.name		= "pxa27x-udc",
+	.probe		= pxa_udc_probe,
+};
+
+static int pxa27x_udc_poller(struct poller_struct *poller)
+{
+	return usb_gadget_poll();
+}
+static struct poller_struct poller = {
+	.func		= pxa27x_udc_poller
+};
+
+static int __init pxa27x_udc_init(void)
+{
+	register_driver(&udc_driver);
+	poller_register(&poller);
+	return 0;
+}
+
+device_initcall(pxa27x_udc_init);
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
new file mode 100644
index 0000000..eace2e4
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -0,0 +1,393 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.h
+ * Intel PXA27x on-chip full speed USB device controller
+ *
+ * Inspired by original driver by Frank Becker, David Brownell, and others.
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
+ *
+ * Taken from linux-2.6 kernel and adapted to barebox.
+ */
+
+#ifndef __LINUX_USB_GADGET_PXA27X_H
+#define __LINUX_USB_GADGET_PXA27X_H
+
+/*
+ * Register definitions
+ */
+/* Offsets */
+#define UDCCR		0x0000		/* UDC Control Register */
+#define UDCICR0		0x0004		/* UDC Interrupt Control Register0 */
+#define UDCICR1		0x0008		/* UDC Interrupt Control Register1 */
+#define UDCISR0		0x000C		/* UDC Interrupt Status Register 0 */
+#define UDCISR1		0x0010		/* UDC Interrupt Status Register 1 */
+#define UDCFNR		0x0014		/* UDC Frame Number Register */
+#define UDCOTGICR	0x0018		/* UDC On-The-Go interrupt control */
+#define UP2OCR		0x0020		/* USB Port 2 Output Control register */
+#define UP3OCR		0x0024		/* USB Port 3 Output Control register */
+#define UDCCSRn(x)	(0x0100 + ((x)<<2)) /* UDC Control/Status register */
+#define UDCBCRn(x)	(0x0200 + ((x)<<2)) /* UDC Byte Count Register */
+#define UDCDRn(x)	(0x0300 + ((x)<<2)) /* UDC Data Register  */
+#define UDCCRn(x)	(0x0400 + ((x)<<2)) /* UDC Control Register */
+
+#define UDCCR_OEN	(1 << 31)	/* On-the-Go Enable */
+#define UDCCR_AALTHNP	(1 << 30)	/* A-device Alternate Host Negotiation
+					   Protocol Port Support */
+#define UDCCR_AHNP	(1 << 29)	/* A-device Host Negotiation Protocol
+					   Support */
+#define UDCCR_BHNP	(1 << 28)	/* B-device Host Negotiation Protocol
+					   Enable */
+#define UDCCR_DWRE	(1 << 16)	/* Device Remote Wake-up Enable */
+#define UDCCR_ACN	(0x03 << 11)	/* Active UDC configuration Number */
+#define UDCCR_ACN_S	11
+#define UDCCR_AIN	(0x07 << 8)	/* Active UDC interface Number */
+#define UDCCR_AIN_S	8
+#define UDCCR_AAISN	(0x07 << 5)	/* Active UDC Alternate Interface
+					   Setting Number */
+#define UDCCR_AAISN_S	5
+#define UDCCR_SMAC	(1 << 4)	/* Switch Endpoint Memory to Active
+					   Configuration */
+#define UDCCR_EMCE	(1 << 3)	/* Endpoint Memory Configuration
+					   Error */
+#define UDCCR_UDR	(1 << 2)	/* UDC Resume */
+#define UDCCR_UDA	(1 << 1)	/* UDC Active */
+#define UDCCR_UDE	(1 << 0)	/* UDC Enable */
+
+#define UDCICR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
+#define UDCICR1_IECC	(1 << 31)	/* IntEn - Configuration Change */
+#define UDCICR1_IESOF	(1 << 30)	/* IntEn - Start of Frame */
+#define UDCICR1_IERU	(1 << 29)	/* IntEn - Resume */
+#define UDCICR1_IESU	(1 << 28)	/* IntEn - Suspend */
+#define UDCICR1_IERS	(1 << 27)	/* IntEn - Reset */
+#define UDCICR_FIFOERR	(1 << 1)	/* FIFO Error interrupt for EP */
+#define UDCICR_PKTCOMPL	(1 << 0)	/* Packet Complete interrupt for EP */
+#define UDCICR_INT_MASK	(UDCICR_FIFOERR | UDCICR_PKTCOMPL)
+
+#define UDCISR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
+#define UDCISR1_IRCC	(1 << 31)	/* IntReq - Configuration Change */
+#define UDCISR1_IRSOF	(1 << 30)	/* IntReq - Start of Frame */
+#define UDCISR1_IRRU	(1 << 29)	/* IntReq - Resume */
+#define UDCISR1_IRSU	(1 << 28)	/* IntReq - Suspend */
+#define UDCISR1_IRRS	(1 << 27)	/* IntReq - Reset */
+#define UDCISR_INT_MASK	(UDCICR_FIFOERR | UDCICR_PKTCOMPL)
+
+#define UDCOTGICR_IESF	(1 << 24)	/* OTG SET_FEATURE command recvd */
+#define UDCOTGICR_IEXR	(1 << 17)	/* Extra Transceiver Interrupt
+					   Rising Edge Interrupt Enable */
+#define UDCOTGICR_IEXF	(1 << 16)	/* Extra Transceiver Interrupt
+					   Falling Edge Interrupt Enable */
+#define UDCOTGICR_IEVV40R (1 << 9)	/* OTG Vbus Valid 4.0V Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEVV40F (1 << 8)	/* OTG Vbus Valid 4.0V Falling Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEVV44R (1 << 7)	/* OTG Vbus Valid 4.4V Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEVV44F (1 << 6)	/* OTG Vbus Valid 4.4V Falling Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IESVR	(1 << 5)	/* OTG Session Valid Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IESVF	(1 << 4)	/* OTG Session Valid Falling Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IESDR	(1 << 3)	/* OTG A-Device SRP Detect Rising
+					   Edge Interrupt Enable */
+#define UDCOTGICR_IESDF	(1 << 2)	/* OTG A-Device SRP Detect Falling
+					   Edge Interrupt Enable */
+#define UDCOTGICR_IEIDR	(1 << 1)	/* OTG ID Change Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEIDF	(1 << 0)	/* OTG ID Change Falling Edge
+					   Interrupt Enable */
+
+/* Host Port 2 field bits */
+#define UP2OCR_CPVEN	(1 << 0)	/* Charge Pump Vbus Enable */
+#define UP2OCR_CPVPE	(1 << 1)	/* Charge Pump Vbus Pulse Enable */
+					/* Transceiver enablers */
+#define UP2OCR_DPPDE	(1 << 2)	/*   D+ Pull Down Enable */
+#define UP2OCR_DMPDE	(1 << 3)	/*   D- Pull Down Enable */
+#define UP2OCR_DPPUE	(1 << 4)	/*   D+ Pull Up Enable */
+#define UP2OCR_DMPUE	(1 << 5)	/*   D- Pull Up Enable */
+#define UP2OCR_DPPUBE	(1 << 6)	/*   D+ Pull Up Bypass Enable */
+#define UP2OCR_DMPUBE	(1 << 7)	/*   D- Pull Up Bypass Enable */
+#define UP2OCR_EXSP	(1 << 8)	/* External Transceiver Speed Control */
+#define UP2OCR_EXSUS	(1 << 9)	/* External Transceiver Speed Enable */
+#define UP2OCR_IDON	(1 << 10)	/* OTG ID Read Enable */
+#define UP2OCR_HXS	(1 << 16)	/* Transceiver Output Select */
+#define UP2OCR_HXOE	(1 << 17)	/* Transceiver Output Enable */
+#define UP2OCR_SEOS	(1 << 24)	/* Single-Ended Output Select */
+
+#define UDCCSR0_ACM	(1 << 9)	/* Ack Control Mode */
+#define UDCCSR0_AREN	(1 << 8)	/* Ack Response Enable */
+#define UDCCSR0_SA	(1 << 7)	/* Setup Active */
+#define UDCCSR0_RNE	(1 << 6)	/* Receive FIFO Not Empty */
+#define UDCCSR0_FST	(1 << 5)	/* Force Stall */
+#define UDCCSR0_SST	(1 << 4)	/* Sent Stall */
+#define UDCCSR0_DME	(1 << 3)	/* DMA Enable */
+#define UDCCSR0_FTF	(1 << 2)	/* Flush Transmit FIFO */
+#define UDCCSR0_IPR	(1 << 1)	/* IN Packet Ready */
+#define UDCCSR0_OPC	(1 << 0)	/* OUT Packet Complete */
+
+#define UDCCSR_DPE	(1 << 9)	/* Data Packet Error */
+#define UDCCSR_FEF	(1 << 8)	/* Flush Endpoint FIFO */
+#define UDCCSR_SP	(1 << 7)	/* Short Packet Control/Status */
+#define UDCCSR_BNE	(1 << 6)	/* Buffer Not Empty (IN endpoints) */
+#define UDCCSR_BNF	(1 << 6)	/* Buffer Not Full (OUT endpoints) */
+#define UDCCSR_FST	(1 << 5)	/* Force STALL */
+#define UDCCSR_SST	(1 << 4)	/* Sent STALL */
+#define UDCCSR_DME	(1 << 3)	/* DMA Enable */
+#define UDCCSR_TRN	(1 << 2)	/* Tx/Rx NAK */
+#define UDCCSR_PC	(1 << 1)	/* Packet Complete */
+#define UDCCSR_FS	(1 << 0)	/* FIFO needs service */
+
+#define UDCCONR_CN	(0x03 << 25)	/* Configuration Number */
+#define UDCCONR_CN_S	25
+#define UDCCONR_IN	(0x07 << 22)	/* Interface Number */
+#define UDCCONR_IN_S	22
+#define UDCCONR_AISN	(0x07 << 19)	/* Alternate Interface Number */
+#define UDCCONR_AISN_S	19
+#define UDCCONR_EN	(0x0f << 15)	/* Endpoint Number */
+#define UDCCONR_EN_S	15
+#define UDCCONR_ET	(0x03 << 13)	/* Endpoint Type: */
+#define UDCCONR_ET_S	13
+#define UDCCONR_ET_INT	(0x03 << 13)	/*   Interrupt */
+#define UDCCONR_ET_BULK	(0x02 << 13)	/*   Bulk */
+#define UDCCONR_ET_ISO	(0x01 << 13)	/*   Isochronous */
+#define UDCCONR_ET_NU	(0x00 << 13)	/*   Not used */
+#define UDCCONR_ED	(1 << 12)	/* Endpoint Direction */
+#define UDCCONR_MPS	(0x3ff << 2)	/* Maximum Packet Size */
+#define UDCCONR_MPS_S	2
+#define UDCCONR_DE	(1 << 1)	/* Double Buffering Enable */
+#define UDCCONR_EE	(1 << 0)	/* Endpoint Enable */
+
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_SMAC | UDCCR_UDR | UDCCR_UDE)
+#define UDCCSR_WR_MASK	(UDCCSR_DME | UDCCSR_FST)
+#define UDC_FNR_MASK	(0x7ff)
+#define UDC_BCR_MASK	(0x3ff)
+
+/*
+ * UDCCR = UDC Endpoint Configuration Registers
+ * UDCCSR = UDC Control/Status Register for this EP
+ * UDCBCR = UDC Byte Count Remaining (contents of OUT fifo)
+ * UDCDR = UDC Endpoint Data Register (the fifo)
+ */
+#define ofs_UDCCR(ep)	(UDCCRn(ep->idx))
+#define ofs_UDCCSR(ep)	(UDCCSRn(ep->idx))
+#define ofs_UDCBCR(ep)	(UDCBCRn(ep->idx))
+#define ofs_UDCDR(ep)	(UDCDRn(ep->idx))
+
+/* Register access macros */
+#define udc_ep_readl(ep, reg)	\
+	readl((ep)->dev->regs + ofs_##reg(ep))
+#define udc_ep_writel(ep, reg, value)	\
+	writel((value), ep->dev->regs + ofs_##reg(ep))
+#define udc_ep_readb(ep, reg)	\
+	readb((ep)->dev->regs + ofs_##reg(ep))
+#define udc_ep_writeb(ep, reg, value)	\
+	writeb((value), ep->dev->regs + ofs_##reg(ep))
+#define udc_readl(dev, reg)	\
+	readl((dev)->regs + (reg))
+#define udc_writel(udc, reg, value)	\
+	writel((value), (udc)->regs + (reg))
+
+#define UDCCSR_MASK		(UDCCSR_FST | UDCCSR_DME)
+#define UDCCISR0_EP_MASK	(~0)
+#define UDCCISR1_EP_MASK	0xffff
+#define UDCCSR0_CTRL_REQ_MASK	(UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)
+
+#define EPIDX(ep)	(ep->idx)
+#define EPADDR(ep)	(ep->addr)
+#define EPXFERTYPE(ep)	(ep->type)
+#define EPNAME(ep)	(ep->name)
+#define is_ep0(ep)	(!ep->idx)
+#define EPXFERTYPE_is_ISO(ep) (EPXFERTYPE(ep) == USB_ENDPOINT_XFER_ISOC)
+
+/*
+ * Endpoint definition helpers
+ */
+#define USB_EP_DEF(addr, bname, dir, type, maxpkt) \
+{ .usb_ep = { .name = bname, .ops = &pxa_ep_ops, .maxpacket = maxpkt, }, \
+  .desc = {	.bEndpointAddress = addr | (dir ? USB_DIR_IN : 0), \
+		.bmAttributes = type, \
+		.wMaxPacketSize = maxpkt, }, \
+  .dev = &memory \
+}
+#define USB_EP_BULK(addr, bname, dir) \
+	USB_EP_DEF(addr, bname, dir, USB_ENDPOINT_XFER_BULK, BULK_FIFO_SIZE)
+#define USB_EP_ISO(addr, bname, dir) \
+	USB_EP_DEF(addr, bname, dir, USB_ENDPOINT_XFER_ISOC, ISO_FIFO_SIZE)
+#define USB_EP_INT(addr, bname, dir) \
+	USB_EP_DEF(addr, bname, dir, USB_ENDPOINT_XFER_INT, INT_FIFO_SIZE)
+#define USB_EP_IN_BULK(n)	USB_EP_BULK(n, "ep" #n "in-bulk", 1)
+#define USB_EP_OUT_BULK(n)	USB_EP_BULK(n, "ep" #n "out-bulk", 0)
+#define USB_EP_IN_ISO(n)	USB_EP_ISO(n,  "ep" #n "in-iso", 1)
+#define USB_EP_OUT_ISO(n)	USB_EP_ISO(n,  "ep" #n "out-iso", 0)
+#define USB_EP_IN_INT(n)	USB_EP_INT(n,  "ep" #n "in-int", 1)
+#define USB_EP_CTRL		USB_EP_DEF(0,  "ep0", 0, 0, EP0_FIFO_SIZE)
+
+#define PXA_EP_DEF(_idx, _addr, dir, _type, maxpkt, _config, iface, altset) \
+{ \
+	.dev = &memory, \
+	.name = "ep" #_idx, \
+	.idx = _idx, .enabled = 0, \
+	.dir_in = dir, .addr = _addr, \
+	.config = _config, .interface = iface, .alternate = altset, \
+	.type = _type, .fifo_size = maxpkt, \
+}
+#define PXA_EP_BULK(_idx, addr, dir, config, iface, alt) \
+	PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_BULK, BULK_FIFO_SIZE, \
+		config, iface, alt)
+#define PXA_EP_ISO(_idx, addr, dir, config, iface, alt) \
+	PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_ISOC, ISO_FIFO_SIZE, \
+		config, iface, alt)
+#define PXA_EP_INT(_idx, addr, dir, config, iface, alt) \
+	PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_INT, INT_FIFO_SIZE, \
+		config, iface, alt)
+#define PXA_EP_IN_BULK(i, adr, c, f, a)		PXA_EP_BULK(i, adr, 1, c, f, a)
+#define PXA_EP_OUT_BULK(i, adr, c, f, a)	PXA_EP_BULK(i, adr, 0, c, f, a)
+#define PXA_EP_IN_ISO(i, adr, c, f, a)		PXA_EP_ISO(i, adr, 1, c, f, a)
+#define PXA_EP_OUT_ISO(i, adr, c, f, a)		PXA_EP_ISO(i, adr, 0, c, f, a)
+#define PXA_EP_IN_INT(i, adr, c, f, a)		PXA_EP_INT(i, adr, 1, c, f, a)
+#define PXA_EP_CTRL	PXA_EP_DEF(0, 0, 0, 0, EP0_FIFO_SIZE, 0, 0, 0)
+
+struct pxa27x_udc;
+
+struct stats {
+	unsigned long in_ops;
+	unsigned long out_ops;
+	unsigned long in_bytes;
+	unsigned long out_bytes;
+	unsigned long irqs;
+};
+
+/**
+ * struct udc_usb_ep - container of each usb_ep structure
+ * @usb_ep: usb endpoint
+ * @desc: usb descriptor, especially type and address
+ * @dev: udc managing this endpoint
+ * @pxa_ep: matching pxa_ep (cache of find_pxa_ep() call)
+ */
+struct udc_usb_ep {
+	struct usb_ep usb_ep;
+	struct usb_endpoint_descriptor desc;
+	struct pxa_udc *dev;
+	struct pxa_ep *pxa_ep;
+};
+
+struct pxa_ep {
+	struct pxa_udc		*dev;
+
+	struct list_head	queue;
+	unsigned		enabled:1;
+
+	unsigned		idx:5;
+	char			*name;
+
+	/*
+	 * Specific pxa endpoint data, needed for hardware initialization
+	 */
+	unsigned		dir_in:1;
+	unsigned		addr:4;
+	unsigned		config:2;
+	unsigned		interface:3;
+	unsigned		alternate:3;
+	unsigned		fifo_size;
+	unsigned		type;
+};
+
+struct pxa27x_request {
+	struct usb_request			req;
+	struct udc_usb_ep			*udc_usb_ep;
+	unsigned				in_use:1;
+	struct list_head			queue;
+};
+
+enum ep0_state {
+	WAIT_FOR_SETUP,
+	SETUP_STAGE,
+	IN_DATA_STAGE,
+	OUT_DATA_STAGE,
+	IN_STATUS_STAGE,
+	OUT_STATUS_STAGE,
+	STALL,
+	WAIT_ACK_SET_CONF_INTERF
+};
+
+static char *ep0_state_name[] = {
+	"WAIT_FOR_SETUP", "SETUP_STAGE", "IN_DATA_STAGE", "OUT_DATA_STAGE",
+	"IN_STATUS_STAGE", "OUT_STATUS_STAGE", "STALL",
+	"WAIT_ACK_SET_CONF_INTERF"
+};
+#define EP0_STNAME(udc) ep0_state_name[(udc)->ep0state]
+
+#define EP0_FIFO_SIZE	16U
+#define BULK_FIFO_SIZE	64U
+#define ISO_FIFO_SIZE	256U
+#define INT_FIFO_SIZE	16U
+
+struct udc_stats {
+	unsigned long	irqs_reset;
+	unsigned long	irqs_suspend;
+	unsigned long	irqs_resume;
+	unsigned long	irqs_reconfig;
+};
+
+#define NR_USB_ENDPOINTS (1 + 5)	/* ep0 + ep1in-bulk + .. + ep3in-iso */
+#define NR_PXA_ENDPOINTS (1 + 14)	/* ep0 + epA + epB + .. + epX */
+
+struct pxa_udc {
+	void __iomem				*regs;
+	int					irq;
+	struct clk				*clk;
+	struct device_d				*dev;
+
+	struct usb_gadget			gadget;
+	struct usb_gadget_driver		*driver;
+	struct pxa2xx_udc_mach_info		*mach;
+
+	enum ep0_state				ep0state;
+	struct udc_stats			stats;
+
+	struct udc_usb_ep			udc_usb_ep[NR_USB_ENDPOINTS];
+	struct pxa_ep				pxa_ep[NR_PXA_ENDPOINTS];
+
+	unsigned				enabled:1;
+	unsigned				pullup_on:1;
+	unsigned				pullup_resume:1;
+	unsigned				vbus_sensed:1;
+	unsigned				config:2;
+	unsigned				last_interface:3;
+	unsigned				last_alternate:3;
+
+};
+
+static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct pxa_udc, gadget);
+}
+
+/*
+ * Debugging/message support
+ */
+#define ep_dbg(ep, fmt, arg...) \
+	dev_dbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_vdbg(ep, fmt, arg...) \
+	dev_dbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_err(ep, fmt, arg...) \
+	dev_err(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_info(ep, fmt, arg...) \
+	dev_info(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_warn(ep, fmt, arg...) \
+	dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg)
+
+#endif /* __LINUX_USB_GADGET_PXA27X_H */
-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/5] usb/gadget: use generic usb_gadget_poll() in u_serial
  2011-11-24  3:01 [PATCH 0/5] Enable a console over a serial USB gadget Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 1/5] console: add console unregistering Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 2/5] usb/gadget: add pxa27x_udc driver Robert Jarzmik
@ 2011-11-24  3:01 ` Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 4/5] usb/gadget: add USB serial connect and disconnect Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 5/5] usb/gadget: make serial gadget resistant to cable unplug Robert Jarzmik
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Jarzmik @ 2011-11-24  3:01 UTC (permalink / raw)
  To: barebox; +Cc: Robert Jarzmik

From: Robert Jarzmik <robert.jarzmik@atosorigin.com>

Use the generic gadget polling function instead of the
specific fsl function. This is a fix from a leftover in
gadget development.

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
 drivers/usb/gadget/u_serial.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index d3baed7..8c2c8fa 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -382,7 +382,7 @@ static void serial_putc(struct console_device *cdev, char c)
 	status = usb_ep_queue(in, req);
 
 	while (list_empty(pool))
-		fsl_udc_irq();
+		usb_gadget_poll();
 }
 
 static int serial_tstc(struct console_device *cdev)
@@ -475,7 +475,7 @@ static int do_mycdev(struct command *cmdtp, int argc, char *argv[])
 		printf("%c", i);
 		mdelay(500);
 		for (j = 0; j < 100; j++)
-			fsl_udc_irq();
+			usb_gadget_poll();
 	}
 	return 0;
 }
@@ -522,4 +522,3 @@ printf("%s\n", __func__);
 	gs_free_requests(gser->out, &port->read_pool);
 	gs_free_requests(gser->in, &port->write_pool);
 }
-
-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/5] usb/gadget: add USB serial connect and disconnect
  2011-11-24  3:01 [PATCH 0/5] Enable a console over a serial USB gadget Robert Jarzmik
                   ` (2 preceding siblings ...)
  2011-11-24  3:01 ` [PATCH 3/5] usb/gadget: use generic usb_gadget_poll() in u_serial Robert Jarzmik
@ 2011-11-24  3:01 ` Robert Jarzmik
  2011-11-24  3:01 ` [PATCH 5/5] usb/gadget: make serial gadget resistant to cable unplug Robert Jarzmik
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Jarzmik @ 2011-11-24  3:01 UTC (permalink / raw)
  To: barebox; +Cc: Robert Jarzmik

From: Robert Jarzmik <robert.jarzmik@atosorigin.com>

The serial gadget can only work if the serial function is
properly connected on USB trigger (ie. on SET_CONFIGURATION
or SET_INTERFACE USB message triggering set_alt() in
composite.c).

Make this connection and handle also the disconnection in
f_serial.

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
 drivers/usb/gadget/f_serial.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 5b7eb2c..b933105 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -16,7 +16,6 @@
 #include "gadget_chips.h"
 #include "u_serial.h"
 
-
 /*
  * This function packages a simple "generic serial" port with no real
  * control mechanisms, just raw data transfer over two bulk endpoints.
@@ -140,6 +139,7 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 				gser->hs.in, gser->fs.in);
 		gser->port.out_desc = ep_choose(cdev->gadget,
 				gser->hs.out, gser->fs.out);
+		gserial_connect(&gser->port, gser->port_num);
 	}
 
 	return 0;
@@ -147,7 +147,10 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 
 static void gser_disable(struct usb_function *f)
 {
+	struct f_gser		*gser = func_to_gser(f);
+
 	DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
+	gserial_disconnect(&gser->port);
 }
 
 /*-------------------------------------------------------------------------*/
-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 5/5] usb/gadget: make serial gadget resistant to cable unplug
  2011-11-24  3:01 [PATCH 0/5] Enable a console over a serial USB gadget Robert Jarzmik
                   ` (3 preceding siblings ...)
  2011-11-24  3:01 ` [PATCH 4/5] usb/gadget: add USB serial connect and disconnect Robert Jarzmik
@ 2011-11-24  3:01 ` Robert Jarzmik
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Jarzmik @ 2011-11-24  3:01 UTC (permalink / raw)
  To: barebox; +Cc: Robert Jarzmik

From: Robert Jarzmik <robert.jarzmik@atosorigin.com>

As USB can have its cable plugged out anytime, make the
serial gadget reliable in case of unplugging :
 - unregister the gadget_serial provided console
 - don't stay forever in the read loop if connection was cut

This behaviour relies on correct implementation of
usb_gadget_poll(), which should return an error if the USB
cable was removed.

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
 drivers/usb/gadget/u_serial.c |   24 ++++++++++++++++++------
 1 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 8c2c8fa..49aedc2 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -367,12 +367,13 @@ static void serial_putc(struct console_device *cdev, char c)
 	struct gs_port	*port = container_of(cdev,
 					struct gs_port, cdev);
 	struct list_head	*pool = &port->write_pool;
-	struct usb_ep		*in = port->port_usb->in;
+	struct usb_ep		*in;
 	struct usb_request	*req;
 	int status;
 
 	if (list_empty(pool))
 		return;
+	in = port->port_usb->in;
 	req = list_entry(pool->next, struct usb_request, list);
 
 	req->length = 1;
@@ -381,8 +382,8 @@ static void serial_putc(struct console_device *cdev, char c)
 	*(unsigned char *)req->buf = c;
 	status = usb_ep_queue(in, req);
 
-	while (list_empty(pool))
-		usb_gadget_poll();
+	while (status >= 0 && list_empty(pool))
+		status = usb_gadget_poll();
 }
 
 static int serial_tstc(struct console_device *cdev)
@@ -399,7 +400,10 @@ static int serial_getc(struct console_device *cdev)
 					struct gs_port, cdev);
 	unsigned char ch;
 
-	while (kfifo_getc(port->recv_fifo, &ch));
+	if (!port->port_usb)
+		return -EIO;
+	while (kfifo_getc(port->recv_fifo, &ch))
+		usb_gadget_poll();
 
 	return ch;
 }
@@ -421,6 +425,10 @@ int gserial_connect(struct gserial *gser, u8 port_num)
 	/* we "know" gserial_cleanup() hasn't been called */
 	port = ports[port_num].port;
 
+	/* In case of multiple activation (ie. multiple SET_INTERFACE) */
+	if (port->port_usb)
+		return 0;
+
 	/* activate the endpoints */
 	status = usb_ep_enable(gser->in, gser->in_desc);
 	if (status < 0)
@@ -498,7 +506,9 @@ BAREBOX_CMD_END
 void gserial_disconnect(struct gserial *gser)
 {
 	struct gs_port	*port = gser->ioport;
-printf("%s\n", __func__);
+	struct console_device *cdev;
+
+	printf("%s\n", __func__);
 	if (!port)
 		return;
 
@@ -518,7 +528,9 @@ printf("%s\n", __func__);
 	gser->in->driver_data = NULL;
 
 	/* finally, free any unused/unusable I/O buffers */
-
 	gs_free_requests(gser->out, &port->read_pool);
 	gs_free_requests(gser->in, &port->write_pool);
+
+	cdev = &port->cdev;
+	console_unregister(cdev);
 }
-- 
1.7.5.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2011-11-24  3:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-11-24  3:01 [PATCH 0/5] Enable a console over a serial USB gadget Robert Jarzmik
2011-11-24  3:01 ` [PATCH 1/5] console: add console unregistering Robert Jarzmik
2011-11-24  3:01 ` [PATCH 2/5] usb/gadget: add pxa27x_udc driver Robert Jarzmik
2011-11-24  3:01 ` [PATCH 3/5] usb/gadget: use generic usb_gadget_poll() in u_serial Robert Jarzmik
2011-11-24  3:01 ` [PATCH 4/5] usb/gadget: add USB serial connect and disconnect Robert Jarzmik
2011-11-24  3:01 ` [PATCH 5/5] usb/gadget: make serial gadget resistant to cable unplug Robert Jarzmik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox