drivers/usb : move usb drivers to drivers/usb

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
new file mode 100644
index 0000000..f8ea167
--- /dev/null
+++ b/drivers/usb/Makefile
@@ -0,0 +1,52 @@
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB 	:= $(obj)libusb.a
+
+COBJS-y += isp116x-hcd.o
+COBJS-y += sl811_usb.o
+COBJS-y += usb_ohci.o
+COBJS-y += usbdcore.o
+COBJS-y += usbdcore_ep0.o
+COBJS-y += usbdcore_mpc8xx.o
+COBJS-y += usbdcore_omap1510.o
+
+COBJS	:= $(COBJS-y)
+SRCS 	:= $(COBJS:.o=.c)
+OBJS 	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/usb/isp116x-hcd.c b/drivers/usb/isp116x-hcd.c
new file mode 100644
index 0000000..b21af10
--- /dev/null
+++ b/drivers/usb/isp116x-hcd.c
@@ -0,0 +1,1445 @@
+/*
+ * ISP116x HCD (Host Controller Driver) for u-boot.
+ *
+ * Copyright (C) 2006-2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2006-2007 Eurotech S.p.A. <info@eurotech.it>
+ *
+ * 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
+ *
+ *
+ * Derived in part from the SL811 HCD driver "u-boot/drivers/sl811_usb.c"
+ * (original copyright message follows):
+ *
+ *    (C) Copyright 2004
+ *    Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ *    This code is based on linux driver for sl811hs chip, source at
+ *    drivers/usb/host/sl811.c:
+ *
+ *    SL811 Host Controller Interface driver for USB.
+ *
+ *    Copyright (c) 2003/06, Courage Co., Ltd.
+ *
+ *    Based on:
+ *         1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
+ *           Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
+ *           Adam Richter, Gregory P. Smith;
+ *         2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
+ *         3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
+ *
+ *    [[GNU/GPL disclaimer]]
+ *
+ * and in part from AU1x00 OHCI HCD driver "u-boot/cpu/mips/au1x00_usb_ohci.c"
+ * (original copyright message follows):
+ *
+ *    URB OHCI HCD (Host Controller Driver) for USB on the AU1x00.
+ *
+ *    (C) Copyright 2003
+ *    Gary Jennejohn, DENX Software Engineering <gj@denx.de>
+ *
+ *    [[GNU/GPL disclaimer]]
+ *
+ *    Note: Part of this code has been derived from linux
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_USB_ISP116X_HCD
+#include <asm/io.h>
+#include <usb.h>
+#include <malloc.h>
+#include <linux/list.h>
+
+/*
+ * ISP116x chips require certain delays between accesses to its
+ * registers. The following timing options exist.
+ *
+ * 1. Configure your memory controller (the best)
+ * 2. Use ndelay (easiest, poorest). For that, enable the following macro.
+ *
+ * Value is in microseconds.
+ */
+#ifdef ISP116X_HCD_USE_UDELAY
+#define UDELAY		1
+#endif
+
+/*
+ * On some (slowly?) machines an extra delay after data packing into
+ * controller's FIFOs is required, * otherwise you may get the following
+ * error:
+ *
+ *   uboot> usb start
+ *   (Re)start USB...
+ *   USB:   scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT
+ *   isp116x: isp116x_submit_job: ****** FIFO not ready! ******
+ *
+ *         USB device not responding, giving up (status=4)
+ *         isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ *         isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ *         isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ *         3 USB Device(s) found
+ *                scanning bus for storage devices... 0 Storage Device(s) found
+ *
+ * Value is in milliseconds.
+ */
+#ifdef ISP116X_HCD_USE_EXTRA_DELAY
+#define EXTRA_DELAY	2
+#endif
+
+/*
+ * Enable the following defines if you wish enable debugging messages.
+ */
+#undef DEBUG			/* enable debugging messages */
+#undef TRACE			/* enable tracing code */
+#undef VERBOSE			/* verbose debugging messages */
+
+#include "isp116x.h"
+
+#define DRIVER_VERSION	"08 Jan 2007"
+static const char hcd_name[] = "isp116x-hcd";
+
+struct isp116x isp116x_dev;
+struct isp116x_platform_data isp116x_board;
+static int got_rhsc;		/* root hub status change */
+struct usb_device *devgone;	/* device which was disconnected */
+static int rh_devnum;		/* address of Root Hub endpoint */
+
+/* ------------------------------------------------------------------------- */
+
+#define ALIGN(x,a)	(((x)+(a)-1UL)&~((a)-1UL))
+#define min_t(type,x,y)	\
+	({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
+
+/* ------------------------------------------------------------------------- */
+
+static int isp116x_reset(struct isp116x *isp116x);
+
+/* --- Debugging functions ------------------------------------------------- */
+
+#define isp116x_show_reg(d, r) {				\
+	if ((r) < 0x20) {					\
+		DBG("%-12s[%02x]: %08x", #r,			\
+			r, isp116x_read_reg32(d, r));		\
+	} else {						\
+		DBG("%-12s[%02x]:     %04x", #r,		\
+			r, isp116x_read_reg16(d, r));  		\
+	}							\
+}
+
+#define isp116x_show_regs(d) {					\
+	isp116x_show_reg(d, HCREVISION);			\
+	isp116x_show_reg(d, HCCONTROL);				\
+	isp116x_show_reg(d, HCCMDSTAT);				\
+	isp116x_show_reg(d, HCINTSTAT);				\
+	isp116x_show_reg(d, HCINTENB);				\
+	isp116x_show_reg(d, HCFMINTVL);				\
+	isp116x_show_reg(d, HCFMREM);				\
+	isp116x_show_reg(d, HCFMNUM);				\
+	isp116x_show_reg(d, HCLSTHRESH);			\
+	isp116x_show_reg(d, HCRHDESCA);				\
+	isp116x_show_reg(d, HCRHDESCB);				\
+	isp116x_show_reg(d, HCRHSTATUS);			\
+	isp116x_show_reg(d, HCRHPORT1);				\
+	isp116x_show_reg(d, HCRHPORT2);				\
+	isp116x_show_reg(d, HCHWCFG);				\
+	isp116x_show_reg(d, HCDMACFG);				\
+	isp116x_show_reg(d, HCXFERCTR);				\
+	isp116x_show_reg(d, HCuPINT);				\
+	isp116x_show_reg(d, HCuPINTENB);			\
+	isp116x_show_reg(d, HCCHIPID);				\
+	isp116x_show_reg(d, HCSCRATCH);				\
+	isp116x_show_reg(d, HCITLBUFLEN);			\
+	isp116x_show_reg(d, HCATLBUFLEN);			\
+	isp116x_show_reg(d, HCBUFSTAT);				\
+	isp116x_show_reg(d, HCRDITL0LEN);			\
+	isp116x_show_reg(d, HCRDITL1LEN);			\
+}
+
+#if defined(TRACE)
+
+static int isp116x_get_current_frame_number(struct usb_device *usb_dev)
+{
+	struct isp116x *isp116x = &isp116x_dev;
+
+	return isp116x_read_reg32(isp116x, HCFMNUM);
+}
+
+static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		     int len, char *str)
+{
+#if defined(VERBOSE)
+	int i;
+#endif
+
+	DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx",
+	    str,
+	    isp116x_get_current_frame_number(dev),
+	    usb_pipedevice(pipe),
+	    usb_pipeendpoint(pipe),
+	    usb_pipeout(pipe) ? 'O' : 'I',
+	    usb_pipetype(pipe) < 2 ?
+	    (usb_pipeint(pipe) ?
+	     "INTR" : "ISOC") :
+	    (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status);
+#if defined(VERBOSE)
+	if (len > 0 && buffer) {
+		printf(__FILE__ ": data(%d):", len);
+		for (i = 0; i < 16 && i < len; i++)
+			printf(" %02x", ((__u8 *) buffer)[i]);
+		printf("%s\n", i < len ? "..." : "");
+	}
+#endif
+}
+
+#define PTD_DIR_STR(ptd)  ({char __c;		\
+	switch(PTD_GET_DIR(ptd)){		\
+	case 0:  __c = 's'; break;		\
+	case 1:  __c = 'o'; break;		\
+	default: __c = 'i'; break;		\
+	}; __c;})
+
+/*
+  Dump PTD info. The code documents the format
+  perfectly, right :)
+*/
+static inline void dump_ptd(struct ptd *ptd)
+{
+#if defined(VERBOSE)
+	int k;
+#endif
+
+	DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x",
+	    PTD_GET_CC(ptd),
+	    PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd),
+	    PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+	    PTD_GET_TOGGLE(ptd),
+	    PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd));
+#if defined(VERBOSE)
+	printf("isp116x: %s: PTD(byte): ", __FUNCTION__);
+	for (k = 0; k < sizeof(struct ptd); ++k)
+		printf("%02x ", ((u8 *) ptd)[k]);
+	printf("\n");
+#endif
+}
+
+static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type)
+{
+#if defined(VERBOSE)
+	int k;
+
+	if (type == 0 /* 0ut data */ ) {
+		printf("isp116x: %s: out data: ", __FUNCTION__);
+		for (k = 0; k < PTD_GET_LEN(ptd); ++k)
+			printf("%02x ", ((u8 *) buf)[k]);
+		printf("\n");
+	}
+	if (type == 1 /* 1n data */ ) {
+		printf("isp116x: %s: in data: ", __FUNCTION__);
+		for (k = 0; k < PTD_GET_COUNT(ptd); ++k)
+			printf("%02x ", ((u8 *) buf)[k]);
+		printf("\n");
+	}
+
+	if (PTD_GET_LAST(ptd))
+		DBG("--- last PTD ---");
+#endif
+}
+
+#else
+
+#define dump_msg(dev, pipe, buffer, len, str)			do { } while (0)
+#define dump_pkt(dev, pipe, buffer, len, setup, str, small)	do {} while (0)
+
+#define dump_ptd(ptd)			do {} while (0)
+#define dump_ptd_data(ptd, buf, type)	do {} while (0)
+
+#endif
+
+/* --- Virtual Root Hub ---------------------------------------------------- */
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] = {
+	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x10,			/*  __u16 bcdUSB; v1.1 */
+	0x01,
+	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  bDeviceSubClass; */
+	0x00,			/*  __u8  bDeviceProtocol; */
+	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,			/*  __u16 idVendor; */
+	0x00,
+	0x00,			/*  __u16 idProduct; */
+	0x00,
+	0x00,			/*  __u16 bcdDevice; */
+	0x00,
+	0x00,			/*  __u8  iManufacturer; */
+	0x01,			/*  __u8  iProduct; */
+	0x00,			/*  __u8  iSerialNumber; */
+	0x01			/*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] = {
+	0x09,			/*  __u8  bLength; */
+	0x02,			/*  __u8  bDescriptorType; Configuration */
+	0x19,			/*  __u16 wTotalLength; */
+	0x00,
+	0x01,			/*  __u8  bNumInterfaces; */
+	0x01,			/*  __u8  bConfigurationValue; */
+	0x00,			/*  __u8  iConfiguration; */
+	0x40,			/*  __u8  bmAttributes;
+				   Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+	0x00,			/*  __u8  MaxPower; */
+
+	/* interface */
+	0x09,			/*  __u8  if_bLength; */
+	0x04,			/*  __u8  if_bDescriptorType; Interface */
+	0x00,			/*  __u8  if_bInterfaceNumber; */
+	0x00,			/*  __u8  if_bAlternateSetting; */
+	0x01,			/*  __u8  if_bNumEndpoints; */
+	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  if_bInterfaceSubClass; */
+	0x00,			/*  __u8  if_bInterfaceProtocol; */
+	0x00,			/*  __u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,			/*  __u8  ep_bLength; */
+	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,			/*  __u8  ep_bmAttributes; Interrupt */
+	0x00,			/*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+	0x02,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] = {
+	0x04,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	0x09,			/*  __u8  lang ID */
+	0x04,			/*  __u8  lang ID */
+};
+
+static unsigned char root_hub_str_index1[] = {
+	0x22,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	'I',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'S',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'P',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'1',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'1',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'6',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'x',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	' ',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'R',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'o',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'o',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	't',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	' ',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'H',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'u',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+	'b',			/*  __u8  Unicode */
+	0,			/*  __u8  Unicode */
+};
+
+/*
+ * Hub class-specific descriptor is constructed dynamically
+ */
+
+/* --- Virtual root hub management functions ------------------------------- */
+
+static int rh_check_port_status(struct isp116x *isp116x)
+{
+	u32 temp, ndp, i;
+	int res;
+
+	res = -1;
+	temp = isp116x_read_reg32(isp116x, HCRHSTATUS);
+	ndp = (temp & RH_A_NDP);
+	for (i = 0; i < ndp; i++) {
+		temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i);
+		/* check for a device disconnect */
+		if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
+		     (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) {
+			res = i;
+			break;
+		}
+	}
+	return res;
+}
+
+/* --- HC management functions --------------------------------------------- */
+
+/* Write len bytes to fifo, pad till 32-bit boundary
+ */
+static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+	u8 *dp = (u8 *) buf;
+	u16 *dp2 = (u16 *) buf;
+	u16 w;
+	int quot = len % 4;
+
+	if ((unsigned long)dp2 & 1) {
+		/* not aligned */
+		for (; len > 1; len -= 2) {
+			w = *dp++;
+			w |= *dp++ << 8;
+			isp116x_raw_write_data16(isp116x, w);
+		}
+		if (len)
+			isp116x_write_data16(isp116x, (u16) * dp);
+	} else {
+		/* aligned */
+		for (; len > 1; len -= 2)
+			isp116x_raw_write_data16(isp116x, *dp2++);
+		if (len)
+			isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
+	}
+	if (quot == 1 || quot == 2)
+		isp116x_raw_write_data16(isp116x, 0);
+}
+
+/* Read len bytes from fifo and then read till 32-bit boundary
+ */
+static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+	u8 *dp = (u8 *) buf;
+	u16 *dp2 = (u16 *) buf;
+	u16 w;
+	int quot = len % 4;
+
+	if ((unsigned long)dp2 & 1) {
+		/* not aligned */
+		for (; len > 1; len -= 2) {
+			w = isp116x_raw_read_data16(isp116x);
+			*dp++ = w & 0xff;
+			*dp++ = (w >> 8) & 0xff;
+		}
+		if (len)
+			*dp = 0xff & isp116x_read_data16(isp116x);
+	} else {
+		/* aligned */
+		for (; len > 1; len -= 2)
+			*dp2++ = isp116x_raw_read_data16(isp116x);
+		if (len)
+			*(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
+	}
+	if (quot == 1 || quot == 2)
+		isp116x_raw_read_data16(isp116x);
+}
+
+/* Write PTD's and data for scheduled transfers into the fifo ram.
+ * Fifo must be empty and ready */
+static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev,
+		      unsigned long pipe, struct ptd *ptd, int n, void *data,
+		      int len)
+{
+	int buflen = n * sizeof(struct ptd) + len;
+	int i, done;
+
+	DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen);
+
+	isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+	isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+	isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
+
+	done = 0;
+	for (i = 0; i < n; i++) {
+		DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i]));
+
+		dump_ptd(&ptd[i]);
+		isp116x_write_data16(isp116x, ptd[i].count);
+		isp116x_write_data16(isp116x, ptd[i].mps);
+		isp116x_write_data16(isp116x, ptd[i].len);
+		isp116x_write_data16(isp116x, ptd[i].faddr);
+
+		dump_ptd_data(&ptd[i], (__u8 *) data + done, 0);
+		write_ptddata_to_fifo(isp116x,
+				      (__u8 *) data + done,
+				      PTD_GET_LEN(&ptd[i]));
+
+		done += PTD_GET_LEN(&ptd[i]);
+	}
+}
+
+/* Read the processed PTD's and data from fifo ram back to URBs' buffers.
+ * Fifo must be full and done */
+static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev,
+		       unsigned long pipe, struct ptd *ptd, int n, void *data,
+		       int len)
+{
+	int buflen = n * sizeof(struct ptd) + len;
+	int i, done, cc, ret;
+
+	isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+	isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+	isp116x_write_addr(isp116x, HCATLPORT);
+
+	ret = TD_CC_NOERROR;
+	done = 0;
+	for (i = 0; i < n; i++) {
+		DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i]));
+
+		ptd[i].count = isp116x_read_data16(isp116x);
+		ptd[i].mps = isp116x_read_data16(isp116x);
+		ptd[i].len = isp116x_read_data16(isp116x);
+		ptd[i].faddr = isp116x_read_data16(isp116x);
+		dump_ptd(&ptd[i]);
+
+		read_ptddata_from_fifo(isp116x,
+				       (__u8 *) data + done,
+				       PTD_GET_LEN(&ptd[i]));
+		dump_ptd_data(&ptd[i], (__u8 *) data + done, 1);
+
+		done += PTD_GET_LEN(&ptd[i]);
+
+		cc = PTD_GET_CC(&ptd[i]);
+
+		/* Data underrun means basically that we had more buffer space than
+		 * the function had data. It is perfectly normal but upper levels have
+		 * to know how much we actually transferred.
+		 */
+		if (cc == TD_NOTACCESSED ||
+				(cc != TD_CC_NOERROR && (ret == TD_CC_NOERROR || ret == TD_DATAUNDERRUN)))
+			ret = cc;
+	}
+
+	DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen);
+
+	return ret;
+}
+
+/* Interrupt handling
+ */
+static int isp116x_interrupt(struct isp116x *isp116x)
+{
+	u16 irqstat;
+	u32 intstat;
+	int ret = 0;
+
+	isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+	irqstat = isp116x_read_reg16(isp116x, HCuPINT);
+	isp116x_write_reg16(isp116x, HCuPINT, irqstat);
+	DBG(">>>>>> irqstat %x <<<<<<", irqstat);
+
+	if (irqstat & HCuPINT_ATL) {
+		DBG(">>>>>> HCuPINT_ATL <<<<<<");
+		udelay(500);
+		ret = 1;
+	}
+
+	if (irqstat & HCuPINT_OPR) {
+		intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
+		isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
+		DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat);
+
+		if (intstat & HCINT_UE) {
+			ERR("unrecoverable error, controller disabled");
+
+			/* FIXME: be optimistic, hope that bug won't repeat
+			 * often. Make some non-interrupt context restart the
+			 * controller. Count and limit the retries though;
+			 * either hardware or software errors can go forever...
+			 */
+			isp116x_reset(isp116x);
+			ret = -1;
+			return -1;
+		}
+
+		if (intstat & HCINT_RHSC) {
+			got_rhsc = 1;
+			ret = 1;
+			/* When root hub or any of its ports is going
+			   to come out of suspend, it may take more
+			   than 10ms for status bits to stabilize. */
+			wait_ms(20);
+		}
+
+		if (intstat & HCINT_SO) {
+			ERR("schedule overrun");
+			ret = -1;
+		}
+
+		irqstat &= ~HCuPINT_OPR;
+	}
+
+	return ret;
+}
+
+/* With one PTD we can transfer almost 1K in one go;
+ * HC does the splitting into endpoint digestible transactions
+ */
+struct ptd ptd[1];
+
+static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe)
+{
+	unsigned mpck = usb_maxpacket(dev, pipe);
+
+	/* One PTD can transfer 1023 bytes but try to always
+	 * transfer multiples of endpoint buffer size
+	 */
+	return 1023 / mpck * mpck;
+}
+
+/* Do an USB transfer
+ */
+static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe,
+			      int dir, void *buffer, int len)
+{
+	struct isp116x *isp116x = &isp116x_dev;
+	int type = usb_pipetype(pipe);
+	int epnum = usb_pipeendpoint(pipe);
+	int max = usb_maxpacket(dev, pipe);
+	int dir_out = usb_pipeout(pipe);
+	int speed_low = usb_pipeslow(pipe);
+	int i, done = 0, stat, timeout, cc;
+
+	/* 500 frames or 0.5s timeout when function is busy and NAKs transactions for a while */
+	int retries = 500;
+
+	DBG("------------------------------------------------");
+	dump_msg(dev, pipe, buffer, len, "SUBMIT");
+	DBG("------------------------------------------------");
+
+	if (len >= 1024) {
+		ERR("Too big job");
+		dev->status = USB_ST_CRC_ERR;
+		return -1;
+	}
+
+	if (isp116x->disabled) {
+		ERR("EPIPE");
+		dev->status = USB_ST_CRC_ERR;
+		return -1;
+	}
+
+	/* device pulled? Shortcut the action. */
+	if (devgone == dev) {
+		ERR("ENODEV");
+		dev->status = USB_ST_CRC_ERR;
+		return USB_ST_CRC_ERR;
+	}
+
+	if (!max) {
+		ERR("pipesize for pipe %lx is zero", pipe);
+		dev->status = USB_ST_CRC_ERR;
+		return -1;
+	}
+
+	if (type == PIPE_ISOCHRONOUS) {
+		ERR("isochronous transfers not supported");
+		dev->status = USB_ST_CRC_ERR;
+		return -1;
+	}
+
+	/* FIFO not empty? */
+	if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) {
+		ERR("****** FIFO not empty! ******");
+		dev->status = USB_ST_BUF_ERR;
+		return -1;
+	}
+
+      retry:
+	isp116x_write_reg32(isp116x, HCINTSTAT, 0xff);
+
+	/* Prepare the PTD data */
+	ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK |
+		PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out));
+	ptd->mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum) | PTD_LAST_MSK;
+	ptd->len = PTD_LEN(len) | PTD_DIR(dir);
+	ptd->faddr = PTD_FA(usb_pipedevice(pipe));
+
+retry_same:
+	/* Pack data into FIFO ram */
+	pack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len);
+#ifdef EXTRA_DELAY
+	wait_ms(EXTRA_DELAY);
+#endif
+
+	/* Start the data transfer */
+
+	/* Allow more time for a BULK device to react - some are slow */
+	if (usb_pipetype(pipe) == PIPE_BULK)
+		timeout = 5000;
+	else
+		timeout = 100;
+
+	/* Wait for it to complete */
+	for (;;) {
+		/* Check whether the controller is done */
+		stat = isp116x_interrupt(isp116x);
+
+		if (stat < 0) {
+			dev->status = USB_ST_CRC_ERR;
+			break;
+		}
+		if (stat > 0)
+			break;
+
+		/* Check the timeout */
+		if (--timeout)
+			udelay(1);
+		else {
+			ERR("CTL:TIMEOUT ");
+			stat = USB_ST_CRC_ERR;
+			break;
+		}
+	}
+
+	/* We got an Root Hub Status Change interrupt */
+	if (got_rhsc) {
+		isp116x_show_regs(isp116x);
+
+		got_rhsc = 0;
+
+		/* Abuse timeout */
+		timeout = rh_check_port_status(isp116x);
+		if (timeout >= 0) {
+			/*
+			 * FIXME! NOTE! AAAARGH!
+			 * This is potentially dangerous because it assumes
+			 * that only one device is ever plugged in!
+			 */
+			devgone = dev;
+		}
+	}
+
+	/* Ok, now we can read transfer status */
+
+	/* FIFO not ready? */
+	if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) {
+		ERR("****** FIFO not ready! ******");
+		dev->status = USB_ST_BUF_ERR;
+		return -1;
+	}
+
+	/* Unpack data from FIFO ram */
+	cc = unpack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len);
+
+	i = PTD_GET_COUNT(ptd);
+	done += i;
+	buffer += i;
+	len -= i;
+
+	/* There was some kind of real problem; Prepare the PTD again
+	 * and retry from the failed transaction on
+	 */
+	if (cc && cc != TD_NOTACCESSED && cc != TD_DATAUNDERRUN) {
+		if (retries >= 100) {
+			retries -= 100;
+			/* The chip will have toggled the toggle bit for the failed
+			 * transaction too. We have to toggle it back.
+			 */
+			usb_settoggle(dev, epnum, dir_out, !PTD_GET_TOGGLE(ptd));
+			goto retry;
+		}
+	}
+	/* "Normal" errors; TD_NOTACCESSED would mean in effect that the function have NAKed
+	 * the transactions from the first on for the whole frame. It may be busy and we retry
+	 * with the same PTD. PTD_ACTIVE (and not TD_NOTACCESSED) would mean that some of the
+	 * PTD didn't make it because the function was busy or the frame ended before the PTD
+	 * finished. We prepare the rest of the data and try again.
+	 */
+	else if (cc == TD_NOTACCESSED || PTD_GET_ACTIVE(ptd) || (cc != TD_DATAUNDERRUN && PTD_GET_COUNT(ptd) < PTD_GET_LEN(ptd))) {
+		if (retries) {
+			--retries;
+			if (cc == TD_NOTACCESSED && PTD_GET_ACTIVE(ptd) && !PTD_GET_COUNT(ptd)) goto retry_same;
+			usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd));
+			goto retry;
+		}
+	}
+
+	if (cc != TD_CC_NOERROR && cc != TD_DATAUNDERRUN) {
+		DBG("****** completition code error %x ******", cc);
+		switch (cc) {
+		case TD_CC_BITSTUFFING:
+			dev->status = USB_ST_BIT_ERR;
+			break;
+		case TD_CC_STALL:
+			dev->status = USB_ST_STALLED;
+			break;
+		case TD_BUFFEROVERRUN:
+		case TD_BUFFERUNDERRUN:
+			dev->status = USB_ST_BUF_ERR;
+			break;
+		default:
+			dev->status = USB_ST_CRC_ERR;
+		}
+		return -cc;
+	}
+	else usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd));
+
+	dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)");
+
+	dev->status = 0;
+	return done;
+}
+
+/* Adapted from au1x00_usb_ohci.c
+ */
+static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+				 void *buffer, int transfer_len,
+				 struct devrequest *cmd)
+{
+	struct isp116x *isp116x = &isp116x_dev;
+	u32 tmp = 0;
+
+	int leni = transfer_len;
+	int len = 0;
+	int stat = 0;
+	u32 datab[4];
+	u8 *data_buf = (u8 *) datab;
+	u16 bmRType_bReq;
+	u16 wValue;
+	u16 wIndex;
+	u16 wLength;
+
+	if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+		INFO("Root-Hub submit IRQ: NOT implemented");
+		return 0;
+	}
+
+	bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+	wValue = swap_16(cmd->value);
+	wIndex = swap_16(cmd->index);
+	wLength = swap_16(cmd->length);
+
+	DBG("--- HUB ----------------------------------------");
+	DBG("submit rh urb, req=%x val=%#x index=%#x len=%d",
+	    bmRType_bReq, wValue, wIndex, wLength);
+	dump_msg(dev, pipe, buffer, transfer_len, "RH");
+	DBG("------------------------------------------------");
+
+	switch (bmRType_bReq) {
+	case RH_GET_STATUS:
+		DBG("RH_GET_STATUS");
+
+		*(__u16 *) data_buf = swap_16(1);
+		len = 2;
+		break;
+
+	case RH_GET_STATUS | RH_INTERFACE:
+		DBG("RH_GET_STATUS | RH_INTERFACE");
+
+		*(__u16 *) data_buf = swap_16(0);
+		len = 2;
+		break;
+
+	case RH_GET_STATUS | RH_ENDPOINT:
+		DBG("RH_GET_STATUS | RH_ENDPOINT");
+
+		*(__u16 *) data_buf = swap_16(0);
+		len = 2;
+		break;
+
+	case RH_GET_STATUS | RH_CLASS:
+		DBG("RH_GET_STATUS | RH_CLASS");
+
+		tmp = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+		*(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE));
+		len = 4;
+		break;
+
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS");
+
+		tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1);
+		*(__u32 *) data_buf = swap_32(tmp);
+		isp116x_show_regs(isp116x);
+		len = 4;
+		break;
+
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		DBG("RH_CLEAR_FEATURE | RH_ENDPOINT");
+
+		switch (wValue) {
+		case RH_ENDPOINT_STALL:
+			DBG("C_HUB_ENDPOINT_STALL");
+			len = 0;
+			break;
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		DBG("RH_CLEAR_FEATURE | RH_CLASS");
+
+		switch (wValue) {
+		case RH_C_HUB_LOCAL_POWER:
+			DBG("C_HUB_LOCAL_POWER");
+			len = 0;
+			break;
+
+		case RH_C_HUB_OVER_CURRENT:
+			DBG("C_HUB_OVER_CURRENT");
+			isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);
+			len = 0;
+			break;
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS");
+
+		switch (wValue) {
+		case RH_PORT_ENABLE:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_CCS);
+			len = 0;
+			break;
+
+		case RH_PORT_SUSPEND:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_POCI);
+			len = 0;
+			break;
+
+		case RH_PORT_POWER:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_LSDA);
+			len = 0;
+			break;
+
+		case RH_C_PORT_CONNECTION:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_CSC);
+			len = 0;
+			break;
+
+		case RH_C_PORT_ENABLE:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PESC);
+			len = 0;
+			break;
+
+		case RH_C_PORT_SUSPEND:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PSSC);
+			len = 0;
+			break;
+
+		case RH_C_PORT_OVER_CURRENT:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_POCI);
+			len = 0;
+			break;
+
+		case RH_C_PORT_RESET:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PRSC);
+			len = 0;
+			break;
+
+		default:
+			ERR("invalid wValue");
+			stat = USB_ST_STALLED;
+		}
+
+		isp116x_show_regs(isp116x);
+
+		break;
+
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS");
+
+		switch (wValue) {
+		case RH_PORT_SUSPEND:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PSS);
+			len = 0;
+			break;
+
+		case RH_PORT_RESET:
+			/* Spin until any current reset finishes */
+			while (1) {
+				tmp =
+				    isp116x_read_reg32(isp116x,
+						       HCRHPORT1 + wIndex - 1);
+				if (!(tmp & RH_PS_PRS))
+					break;
+				wait_ms(1);
+			}
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PRS);
+			wait_ms(10);
+
+			len = 0;
+			break;
+
+		case RH_PORT_POWER:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PPS);
+			len = 0;
+			break;
+
+		case RH_PORT_ENABLE:
+			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+					    RH_PS_PES);
+			len = 0;
+			break;
+
+		default:
+			ERR("invalid wValue");
+			stat = USB_ST_STALLED;
+		}
+
+		isp116x_show_regs(isp116x);
+
+		break;
+
+	case RH_SET_ADDRESS:
+		DBG("RH_SET_ADDRESS");
+
+		rh_devnum = wValue;
+		len = 0;
+		break;
+
+	case RH_GET_DESCRIPTOR:
+		DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength);
+
+		switch (wValue) {
+		case (USB_DT_DEVICE << 8):	/* device descriptor */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_dev_des),
+						wLength));
+			data_buf = root_hub_dev_des;
+			break;
+
+		case (USB_DT_CONFIG << 8):	/* configuration descriptor */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_config_des),
+						wLength));
+			data_buf = root_hub_config_des;
+			break;
+
+		case ((USB_DT_STRING << 8) | 0x00):	/* string 0 descriptors */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_str_index0),
+						wLength));
+			data_buf = root_hub_str_index0;
+			break;
+
+		case ((USB_DT_STRING << 8) | 0x01):	/* string 1 descriptors */
+			len = min_t(unsigned int,
+				    leni, min_t(unsigned int,
+						sizeof(root_hub_str_index1),
+						wLength));
+			data_buf = root_hub_str_index1;
+			break;
+
+		default:
+			ERR("invalid wValue");
+			stat = USB_ST_STALLED;
+		}
+
+		break;
+
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		DBG("RH_GET_DESCRIPTOR | RH_CLASS");
+
+		tmp = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+		data_buf[0] = 0x09;	/* min length; */
+		data_buf[1] = 0x29;
+		data_buf[2] = tmp & RH_A_NDP;
+		data_buf[3] = 0;
+		if (tmp & RH_A_PSM)	/* per-port power switching? */
+			data_buf[3] |= 0x01;
+		if (tmp & RH_A_NOCP)	/* no overcurrent reporting? */
+			data_buf[3] |= 0x10;
+		else if (tmp & RH_A_OCPM)	/* per-port overcurrent rep? */
+			data_buf[3] |= 0x08;
+
+		/* Corresponds to data_buf[4-7] */
+		datab[1] = 0;
+		data_buf[5] = (tmp & RH_A_POTPGT) >> 24;
+
+		tmp = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+		data_buf[7] = tmp & RH_B_DR;
+		if (data_buf[2] < 7)
+			data_buf[8] = 0xff;
+		else {
+			data_buf[0] += 2;
+			data_buf[8] = (tmp & RH_B_DR) >> 8;
+			data_buf[10] = data_buf[9] = 0xff;
+		}
+
+		len = min_t(unsigned int, leni,
+			    min_t(unsigned int, data_buf[0], wLength));
+		break;
+
+	case RH_GET_CONFIGURATION:
+		DBG("RH_GET_CONFIGURATION");
+
+		*(__u8 *) data_buf = 0x01;
+		len = 1;
+		break;
+
+	case RH_SET_CONFIGURATION:
+		DBG("RH_SET_CONFIGURATION");
+
+		isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC);
+		len = 0;
+		break;
+
+	default:
+		ERR("*** *** *** unsupported root hub command *** *** ***");
+		stat = USB_ST_STALLED;
+	}
+
+	len = min_t(int, len, leni);
+	if (buffer != data_buf)
+		memcpy(buffer, data_buf, len);
+
+	dev->act_len = len;
+	dev->status = stat;
+	DBG("dev act_len %d, status %d", dev->act_len, dev->status);
+
+	dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)");
+
+	return stat;
+}
+
+/* --- Transfer functions -------------------------------------------------- */
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		   int len, int interval)
+{
+	DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d",
+	    dev, pipe, buffer, len, interval);
+
+	return -1;
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		       int len, struct devrequest *setup)
+{
+	int devnum = usb_pipedevice(pipe);
+	int epnum = usb_pipeendpoint(pipe);
+	int max = max_transfer_len(dev, pipe);
+	int dir_in = usb_pipein(pipe);
+	int done, ret;
+
+	/* Control message is for the HUB? */
+	if (devnum == rh_devnum)
+		return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup);
+
+	/* Ok, no HUB message so send the message to the device */
+
+	/* Setup phase */
+	DBG("--- SETUP PHASE --------------------------------");
+	usb_settoggle(dev, epnum, 1, 0);
+	ret = isp116x_submit_job(dev, pipe,
+				 PTD_DIR_SETUP,
+				 setup, sizeof(struct devrequest));
+	if (ret < 0) {
+		DBG("control setup phase error (ret = %d", ret);
+		return -1;
+	}
+
+	/* Data phase */
+	DBG("--- DATA PHASE ---------------------------------");
+	done = 0;
+	usb_settoggle(dev, epnum, !dir_in, 1);
+	while (done < len) {
+		ret = isp116x_submit_job(dev, pipe,
+					 dir_in ? PTD_DIR_IN : PTD_DIR_OUT,
+					 (__u8 *) buffer + done,
+					 max > len - done ? len - done : max);
+		if (ret < 0) {
+			DBG("control data phase error (ret = %d)", ret);
+			return -1;
+		}
+		done += ret;
+
+		if (dir_in && ret < max)	/* short packet */
+			break;
+	}
+
+	/* Status phase */
+	DBG("--- STATUS PHASE -------------------------------");
+	usb_settoggle(dev, epnum, !dir_in, 1);
+	ret = isp116x_submit_job(dev, pipe,
+				 !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0);
+	if (ret < 0) {
+		DBG("control status phase error (ret = %d", ret);
+		return -1;
+	}
+
+	dev->act_len = done;
+
+	dump_msg(dev, pipe, buffer, len, "DEV(ret)");
+
+	return done;
+}
+
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		    int len)
+{
+	int dir_out = usb_pipeout(pipe);
+	int max = max_transfer_len(dev, pipe);
+	int done, ret;
+
+	DBG("--- BULK ---------------------------------------");
+	DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d",
+	    usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
+
+	done = 0;
+	while (done < len) {
+		ret = isp116x_submit_job(dev, pipe,
+					 !dir_out ? PTD_DIR_IN : PTD_DIR_OUT,
+					 (__u8 *) buffer + done,
+					 max > len - done ? len - done : max);
+		if (ret < 0) {
+			DBG("error on bulk message (ret = %d)", ret);
+			return -1;
+		}
+
+		done += ret;
+
+		if (!dir_out && ret < max)	/* short packet */
+			break;
+	}
+
+	dev->act_len = done;
+
+	return 0;
+}
+
+/* --- Basic functions ----------------------------------------------------- */
+
+static int isp116x_sw_reset(struct isp116x *isp116x)
+{
+	int retries = 15;
+	int ret = 0;
+
+	DBG("");
+
+	isp116x->disabled = 1;
+
+	isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);
+	isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR);
+	while (--retries) {
+		/* It usually resets within 1 ms */
+		wait_ms(1);
+		if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR))
+			break;
+	}
+	if (!retries) {
+		ERR("software reset timeout");
+		ret = -1;
+	}
+	return ret;
+}
+
+static int isp116x_reset(struct isp116x *isp116x)
+{
+	unsigned long t;
+	u16 clkrdy = 0;
+	int ret, timeout = 15 /* ms */ ;
+
+	DBG("");
+
+	ret = isp116x_sw_reset(isp116x);
+	if (ret)
+		return ret;
+
+	for (t = 0; t < timeout; t++) {
+		clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;
+		if (clkrdy)
+			break;
+		wait_ms(1);
+	}
+	if (!clkrdy) {
+		ERR("clock not ready after %dms", timeout);
+		/* After sw_reset the clock won't report to be ready, if
+		   H_WAKEUP pin is high. */
+		ERR("please make sure that the H_WAKEUP pin is pulled low!");
+		ret = -1;
+	}
+	return ret;
+}
+
+static void isp116x_stop(struct isp116x *isp116x)
+{
+	u32 val;
+
+	DBG("");
+
+	isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+	/* Switch off ports' power, some devices don't come up
+	   after next 'start' without this */
+	val = isp116x_read_reg32(isp116x, HCRHDESCA);
+	val &= ~(RH_A_NPS | RH_A_PSM);
+	isp116x_write_reg32(isp116x, HCRHDESCA, val);
+	isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
+
+	isp116x_sw_reset(isp116x);
+}
+
+/*
+ *  Configure the chip. The chip must be successfully reset by now.
+ */
+static int isp116x_start(struct isp116x *isp116x)
+{
+	struct isp116x_platform_data *board = isp116x->board;
+	u32 val;
+
+	DBG("");
+
+	/* Clear interrupt status and disable all interrupt sources */
+	isp116x_write_reg16(isp116x, HCuPINT, 0xff);
+	isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+	isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
+	isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
+
+	/* Hardware configuration */
+	val = HCHWCFG_DBWIDTH(1);
+	if (board->sel15Kres)
+		val |= HCHWCFG_15KRSEL;
+	/* Remote wakeup won't work without working clock */
+	if (board->remote_wakeup_enable)
+		val |= HCHWCFG_CLKNOTSTOP;
+	if (board->oc_enable)
+		val |= HCHWCFG_ANALOG_OC;
+	isp116x_write_reg16(isp116x, HCHWCFG, val);
+
+	/* --- Root hub configuration */
+	val = (25 << 24) & RH_A_POTPGT;
+	/* AN10003_1.pdf recommends RH_A_NPS (no power switching) to
+	   be always set. Yet, instead, we request individual port
+	   power switching. */
+	val |= RH_A_PSM;
+	/* Report overcurrent per port */
+	val |= RH_A_OCPM;
+	isp116x_write_reg32(isp116x, HCRHDESCA, val);
+	isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+	val = RH_B_PPCM;
+	isp116x_write_reg32(isp116x, HCRHDESCB, val);
+	isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+	val = 0;
+	if (board->remote_wakeup_enable)
+		val |= RH_HS_DRWE;
+	isp116x_write_reg32(isp116x, HCRHSTATUS, val);
+	isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+	isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf);
+
+	/* Go operational */
+	val = HCCONTROL_USB_OPER;
+	if (board->remote_wakeup_enable)
+		val |= HCCONTROL_RWE;
+	isp116x_write_reg32(isp116x, HCCONTROL, val);
+
+	/* Disable ports to avoid race in device enumeration */
+	isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
+	isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
+
+	isp116x_show_regs(isp116x);
+
+	isp116x->disabled = 0;
+
+	return 0;
+}
+
+/* --- Init functions ------------------------------------------------------ */
+
+int isp116x_check_id(struct isp116x *isp116x)
+{
+	int val;
+
+	val = isp116x_read_reg16(isp116x, HCCHIPID);
+	if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+		ERR("invalid chip ID %04x", val);
+		return -1;
+	}
+
+	return 0;
+}
+
+int usb_lowlevel_init(void)
+{
+	struct isp116x *isp116x = &isp116x_dev;
+
+	DBG("");
+
+	got_rhsc = rh_devnum = 0;
+
+	/* Init device registers addr */
+	isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR;
+	isp116x->data_reg = (u16 *) ISP116X_HCD_DATA;
+
+	/* Setup specific board settings */
+#ifdef ISP116X_HCD_SEL15kRES
+	isp116x_board.sel15Kres = 1;
+#endif
+#ifdef ISP116X_HCD_OC_ENABLE
+	isp116x_board.oc_enable = 1;
+#endif
+#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE
+	isp116x_board.remote_wakeup_enable = 1;
+#endif
+	isp116x->board = &isp116x_board;
+
+	/* Try to get ISP116x silicon chip ID */
+	if (isp116x_check_id(isp116x) < 0)
+		return -1;
+
+	isp116x->disabled = 1;
+	isp116x->sleeping = 0;
+
+	isp116x_reset(isp116x);
+	isp116x_start(isp116x);
+
+	return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+	struct isp116x *isp116x = &isp116x_dev;
+
+	DBG("");
+
+	if (!isp116x->disabled)
+		isp116x_stop(isp116x);
+
+	return 0;
+}
+
+#endif				/* CONFIG_USB_ISP116X_HCD */
diff --git a/drivers/usb/isp116x.h b/drivers/usb/isp116x.h
new file mode 100644
index 0000000..a3ce3b5
--- /dev/null
+++ b/drivers/usb/isp116x.h
@@ -0,0 +1,489 @@
+/*
+ * ISP116x register declarations and HCD data structures
+ *
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ * Portions:
+ * Copyright (C) 2004 Lothar Wassmann
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifdef DEBUG
+#define DBG(fmt, args...)	\
+		printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#else
+#define DBG(fmt, args...)	do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#    define VDBG		DBG
+#else
+#    define VDBG(fmt, args...)	do {} while (0)
+#endif
+
+#define ERR(fmt, args...)	\
+		printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#define WARN(fmt, args...)	\
+		printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#define INFO(fmt, args...)	\
+		printf("isp116x: " fmt "\n" , ## args)
+
+/* ------------------------------------------------------------------------- */
+
+/* us of 1ms frame */
+#define  MAX_LOAD_LIMIT		850
+
+/* Full speed: max # of bytes to transfer for a single urb
+   at a time must be < 1024 && must be multiple of 64.
+   832 allows transfering 4kiB within 5 frames. */
+#define MAX_TRANSFER_SIZE_FULLSPEED	832
+
+/* Low speed: there is no reason to schedule in very big
+   chunks; often the requested long transfers are for
+   string descriptors containing short strings. */
+#define MAX_TRANSFER_SIZE_LOWSPEED	64
+
+/* Bytetime (us), a rough indication of how much time it
+   would take to transfer a byte of useful data over USB */
+#define BYTE_TIME_FULLSPEED	1
+#define BYTE_TIME_LOWSPEED	20
+
+/* Buffer sizes */
+#define ISP116x_BUF_SIZE	4096
+#define ISP116x_ITL_BUFSIZE	0
+#define ISP116x_ATL_BUFSIZE	((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
+
+#define ISP116x_WRITE_OFFSET	0x80
+
+/* --- ISP116x registers/bits ---------------------------------------------- */
+
+#define	HCREVISION	0x00
+#define	HCCONTROL	0x01
+#define		HCCONTROL_HCFS	(3 << 6)	/* host controller
+						   functional state */
+#define		HCCONTROL_USB_RESET	(0 << 6)
+#define		HCCONTROL_USB_RESUME	(1 << 6)
+#define		HCCONTROL_USB_OPER	(2 << 6)
+#define		HCCONTROL_USB_SUSPEND	(3 << 6)
+#define		HCCONTROL_RWC	(1 << 9)	/* remote wakeup connected */
+#define		HCCONTROL_RWE	(1 << 10)	/* remote wakeup enable */
+#define	HCCMDSTAT	0x02
+#define		HCCMDSTAT_HCR	(1 << 0)	/* host controller reset */
+#define		HCCMDSTAT_SOC	(3 << 16)	/* scheduling overrun count */
+#define	HCINTSTAT	0x03
+#define		HCINT_SO	(1 << 0)	/* scheduling overrun */
+#define		HCINT_WDH	(1 << 1)	/* writeback of done_head */
+#define		HCINT_SF	(1 << 2)	/* start frame */
+#define		HCINT_RD	(1 << 3)	/* resume detect */
+#define		HCINT_UE	(1 << 4)	/* unrecoverable error */
+#define		HCINT_FNO	(1 << 5)	/* frame number overflow */
+#define		HCINT_RHSC	(1 << 6)	/* root hub status change */
+#define		HCINT_OC	(1 << 30)	/* ownership change */
+#define		HCINT_MIE	(1 << 31)	/* master interrupt enable */
+#define	HCINTENB	0x04
+#define	HCINTDIS	0x05
+#define	HCFMINTVL	0x0d
+#define	HCFMREM		0x0e
+#define	HCFMNUM		0x0f
+#define	HCLSTHRESH	0x11
+#define	HCRHDESCA	0x12
+#define		RH_A_NDP	(0x3 << 0)	/* # downstream ports */
+#define		RH_A_PSM	(1 << 8)	/* power switching mode */
+#define		RH_A_NPS	(1 << 9)	/* no power switching */
+#define		RH_A_DT		(1 << 10)	/* device type (mbz) */
+#define		RH_A_OCPM	(1 << 11)	/* overcurrent protection
+						   mode */
+#define		RH_A_NOCP	(1 << 12)	/* no overcurrent protection */
+#define		RH_A_POTPGT	(0xff << 24)	/* power on -> power good
+						   time */
+#define	HCRHDESCB	0x13
+#define		RH_B_DR		(0xffff << 0)	/* device removable flags */
+#define		RH_B_PPCM	(0xffff << 16)	/* port power control mask */
+#define	HCRHSTATUS	0x14
+#define		RH_HS_LPS	(1 << 0)	/* local power status */
+#define		RH_HS_OCI	(1 << 1)	/* over current indicator */
+#define		RH_HS_DRWE	(1 << 15)	/* device remote wakeup
+						   enable */
+#define		RH_HS_LPSC	(1 << 16)	/* local power status change */
+#define		RH_HS_OCIC	(1 << 17)	/* over current indicator
+						   change */
+#define		RH_HS_CRWE	(1 << 31)	/* clear remote wakeup
+						   enable */
+#define	HCRHPORT1	0x15
+#define		RH_PS_CCS	(1 << 0)	/* current connect status */
+#define		RH_PS_PES	(1 << 1)	/* port enable status */
+#define		RH_PS_PSS	(1 << 2)	/* port suspend status */
+#define		RH_PS_POCI	(1 << 3)	/* port over current
+						   indicator */
+#define		RH_PS_PRS	(1 << 4)	/* port reset status */
+#define		RH_PS_PPS	(1 << 8)	/* port power status */
+#define		RH_PS_LSDA	(1 << 9)	/* low speed device attached */
+#define		RH_PS_CSC	(1 << 16)	/* connect status change */
+#define		RH_PS_PESC	(1 << 17)	/* port enable status change */
+#define		RH_PS_PSSC	(1 << 18)	/* port suspend status
+						   change */
+#define		RH_PS_OCIC	(1 << 19)	/* over current indicator
+						   change */
+#define		RH_PS_PRSC	(1 << 20)	/* port reset status change */
+#define		HCRHPORT_CLRMASK	(0x1f << 16)
+#define	HCRHPORT2	0x16
+#define	HCHWCFG		0x20
+#define		HCHWCFG_15KRSEL		(1 << 12)
+#define		HCHWCFG_CLKNOTSTOP	(1 << 11)
+#define		HCHWCFG_ANALOG_OC	(1 << 10)
+#define		HCHWCFG_DACK_MODE	(1 << 8)
+#define		HCHWCFG_EOT_POL		(1 << 7)
+#define		HCHWCFG_DACK_POL	(1 << 6)
+#define		HCHWCFG_DREQ_POL	(1 << 5)
+#define		HCHWCFG_DBWIDTH_MASK	(0x03 << 3)
+#define		HCHWCFG_DBWIDTH(n)	(((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define		HCHWCFG_INT_POL		(1 << 2)
+#define		HCHWCFG_INT_TRIGGER	(1 << 1)
+#define		HCHWCFG_INT_ENABLE	(1 << 0)
+#define	HCDMACFG	0x21
+#define		HCDMACFG_BURST_LEN_MASK	(0x03 << 5)
+#define		HCDMACFG_BURST_LEN(n)	(((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define		HCDMACFG_BURST_LEN_1	HCDMACFG_BURST_LEN(0)
+#define		HCDMACFG_BURST_LEN_4	HCDMACFG_BURST_LEN(1)
+#define		HCDMACFG_BURST_LEN_8	HCDMACFG_BURST_LEN(2)
+#define		HCDMACFG_DMA_ENABLE	(1 << 4)
+#define		HCDMACFG_BUF_TYPE_MASK	(0x07 << 1)
+#define		HCDMACFG_CTR_SEL	(1 << 2)
+#define		HCDMACFG_ITLATL_SEL	(1 << 1)
+#define		HCDMACFG_DMA_RW_SELECT	(1 << 0)
+#define	HCXFERCTR	0x22
+#define	HCuPINT		0x24
+#define		HCuPINT_SOF		(1 << 0)
+#define		HCuPINT_ATL		(1 << 1)
+#define		HCuPINT_AIIEOT		(1 << 2)
+#define		HCuPINT_OPR		(1 << 4)
+#define		HCuPINT_SUSP		(1 << 5)
+#define		HCuPINT_CLKRDY		(1 << 6)
+#define	HCuPINTENB	0x25
+#define	HCCHIPID	0x27
+#define		HCCHIPID_MASK		0xff00
+#define		HCCHIPID_MAGIC		0x6100
+#define	HCSCRATCH	0x28
+#define	HCSWRES		0x29
+#define		HCSWRES_MAGIC		0x00f6
+#define	HCITLBUFLEN	0x2a
+#define	HCATLBUFLEN	0x2b
+#define	HCBUFSTAT	0x2c
+#define		HCBUFSTAT_ITL0_FULL	(1 << 0)
+#define		HCBUFSTAT_ITL1_FULL	(1 << 1)
+#define		HCBUFSTAT_ATL_FULL	(1 << 2)
+#define		HCBUFSTAT_ITL0_DONE	(1 << 3)
+#define		HCBUFSTAT_ITL1_DONE	(1 << 4)
+#define		HCBUFSTAT_ATL_DONE	(1 << 5)
+#define	HCRDITL0LEN	0x2d
+#define	HCRDITL1LEN	0x2e
+#define	HCITLPORT	0x40
+#define	HCATLPORT	0x41
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p)	(((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v)		(((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p)	(((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v)		(((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p)	(((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v)		(((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p)		(((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v)		(((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p)		(((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v)		(((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p)		(((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v)		(((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p)		(((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v)		(((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p)		(((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v)		(((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p)		(((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v)		(((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p)		(((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v)		(((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_B5_5(p)		(((p)->len & PTD_B5_5_MSK) >> 13)
+#define PTD_B5_5(v)		(((v) << 13) & PTD_B5_5_MSK)
+#define PTD_GET_FA(p)		(((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v)		(((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_FMT(p)		(((p)->faddr & PTD_FMT_MSK) >> 7)
+#define PTD_FMT(v)		(((v) << 7) & PTD_FMT_MSK)
+
+/*  Hardware transfer status codes -- CC from ptd->count */
+#define TD_CC_NOERROR      0x00
+#define TD_CC_CRC          0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL        0x04
+#define TD_DEVNOTRESP      0x05
+#define TD_PIDCHECKFAIL    0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN     0x08
+#define TD_DATAUNDERRUN    0x09
+    /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+    /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED     0x0F
+
+/* ------------------------------------------------------------------------- */
+
+#define	LOG2_PERIODIC_SIZE	5	/* arbitrary; this matches OHCI */
+#define	PERIODIC_SIZE		(1 << LOG2_PERIODIC_SIZE)
+
+/* Philips transfer descriptor */
+struct ptd {
+	u16 count;
+#define	PTD_COUNT_MSK	(0x3ff << 0)
+#define	PTD_TOGGLE_MSK	(1 << 10)
+#define	PTD_ACTIVE_MSK	(1 << 11)
+#define	PTD_CC_MSK	(0xf << 12)
+	u16 mps;
+#define	PTD_MPS_MSK	(0x3ff << 0)
+#define	PTD_SPD_MSK	(1 << 10)
+#define	PTD_LAST_MSK	(1 << 11)
+#define	PTD_EP_MSK	(0xf << 12)
+	u16 len;
+#define	PTD_LEN_MSK	(0x3ff << 0)
+#define	PTD_DIR_MSK	(3 << 10)
+#define	PTD_DIR_SETUP	(0)
+#define	PTD_DIR_OUT	(1)
+#define	PTD_DIR_IN	(2)
+#define	PTD_B5_5_MSK	(1 << 13)
+	u16 faddr;
+#define	PTD_FA_MSK	(0x7f << 0)
+#define	PTD_FMT_MSK	(1 << 7)
+} __attribute__ ((packed, aligned(2)));
+
+struct isp116x_ep {
+	struct usb_device *udev;
+	struct ptd ptd;
+
+	u8 maxpacket;
+	u8 epnum;
+	u8 nextpid;
+
+	u16 length;		/* of current packet */
+	unsigned char *data;	/* to databuf */
+
+	u16 error_count;
+};
+
+/* URB struct */
+#define N_URB_TD		48
+#define URB_DEL			1
+typedef struct {
+	struct isp116x_ep *ed;
+	void *transfer_buffer;	/* (in) associated data buffer */
+	int actual_length;	/* (return) actual transfer length */
+	unsigned long pipe;	/* (in) pipe information */
+#if 0
+	int state;
+#endif
+} urb_priv_t;
+
+struct isp116x_platform_data {
+	/* Enable internal resistors on downstream ports */
+	unsigned sel15Kres:1;
+	/* On-chip overcurrent detection */
+	unsigned oc_enable:1;
+	/* Enable wakeup by devices on usb bus (e.g. wakeup
+	   by attachment/detachment or by device activity
+	   such as moving a mouse). When chosen, this option
+	   prevents stopping internal clock, increasing
+	   thereby power consumption in suspended state. */
+	unsigned remote_wakeup_enable:1;
+};
+
+struct isp116x {
+	u16 *addr_reg;
+	u16 *data_reg;
+
+	struct isp116x_platform_data *board;
+
+	struct dentry *dentry;
+	unsigned long stat1, stat2, stat4, stat8, stat16;
+
+	/* Status flags */
+	unsigned disabled:1;
+	unsigned sleeping:1;
+
+	/* Root hub registers */
+	u32 rhdesca;
+	u32 rhdescb;
+	u32 rhstatus;
+	u32 rhport[2];
+
+	/* Schedule for the current frame */
+	struct isp116x_ep *atl_active;
+	int atl_buflen;
+	int atl_bufshrt;
+	int atl_last_dir;
+	int atl_finishing;
+};
+
+/* ------------------------------------------------- */
+
+/* Inter-io delay (ns). The chip is picky about access timings; it
+ * expects at least:
+ * 150ns delay between consecutive accesses to DATA_REG,
+ * 300ns delay between access to ADDR_REG and DATA_REG
+ * OE, WE MUST NOT be changed during these intervals
+ */
+#if defined(UDELAY)
+#define	isp116x_delay(h,d)	udelay(d)
+#else
+#define	isp116x_delay(h,d)	do {} while (0)
+#endif
+
+static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
+{
+	writew(reg & 0xff, isp116x->addr_reg);
+	isp116x_delay(isp116x, UDELAY);
+}
+
+static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
+{
+	writew(val, isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+}
+
+static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
+{
+	__raw_writew(val, isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+}
+
+static inline u16 isp116x_read_data16(struct isp116x *isp116x)
+{
+	u16 val;
+
+	val = readw(isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+	return val;
+}
+
+static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
+{
+	u16 val;
+
+	val = __raw_readw(isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+	return val;
+}
+
+static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
+{
+	writew(val & 0xffff, isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+	writew(val >> 16, isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+}
+
+static inline u32 isp116x_read_data32(struct isp116x *isp116x)
+{
+	u32 val;
+
+	val = (u32) readw(isp116x->data_reg);
+	isp116x_delay(isp116x, UDELAY);
+	val |= ((u32) readw(isp116x->data_reg)) << 16;
+	isp116x_delay(isp116x, UDELAY);
+	return val;
+}
+
+/* Let's keep register access functions out of line. Hint:
+   we wait at least 150 ns at every access.
+*/
+static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
+{
+	isp116x_write_addr(isp116x, reg);
+	return isp116x_read_data16(isp116x);
+}
+
+static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
+{
+	isp116x_write_addr(isp116x, reg);
+	return isp116x_read_data32(isp116x);
+}
+
+static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
+				unsigned val)
+{
+	isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+	isp116x_write_data16(isp116x, (u16) (val & 0xffff));
+}
+
+static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
+				unsigned val)
+{
+	isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+	isp116x_write_data32(isp116x, (u32) val);
+}
+
+/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */
+
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS          0x0500
+#define RH_GET_DESCRIPTOR       0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION    0x0880
+#define RH_SET_CONFIGURATION    0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
diff --git a/drivers/usb/sl811.h b/drivers/usb/sl811.h
new file mode 100644
index 0000000..c1f9f01
--- /dev/null
+++ b/drivers/usb/sl811.h
@@ -0,0 +1,104 @@
+#ifndef __UBOOT_SL811_H
+#define __UBOOT_SL811_H
+
+#undef SL811_DEBUG
+
+#ifdef SL811_DEBUG
+	#define PDEBUG(level, fmt, args...) \
+		if (debug >= (level)) printf("[%s:%d] " fmt, \
+		__PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+	#define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+/* Sl811 host control register */
+#define	SL811_CTRL_A		0x00
+#define	SL811_ADDR_A		0x01
+#define	SL811_LEN_A		0x02
+#define	SL811_STS_A		0x03	/* read	*/
+#define	SL811_PIDEP_A		0x03	/* write */
+#define	SL811_CNT_A		0x04	/* read	*/
+#define	SL811_DEV_A		0x04	/* write */
+#define	SL811_CTRL1		0x05
+#define	SL811_INTR		0x06
+#define	SL811_CTRL_B		0x08
+#define	SL811_ADDR_B		0x09
+#define	SL811_LEN_B		0x0A
+#define	SL811_STS_B		0x0B	/* read	*/
+#define	SL811_PIDEP_B		0x0B	/* write */
+#define	SL811_CNT_B		0x0C	/* read	*/
+#define	SL811_DEV_B		0x0C	/* write */
+#define	SL811_INTRSTS		0x0D	/* write clears	bitwise	*/
+#define	SL811_HWREV		0x0E	/* read	*/
+#define	SL811_SOFLOW		0x0E	/* write */
+#define	SL811_SOFCNTDIV		0x0F	/* read	*/
+#define	SL811_CTRL2		0x0F	/* write */
+
+/* USB control register bits (addr 0x00 and addr 0x08) */
+#define	SL811_USB_CTRL_ARM	0x01
+#define	SL811_USB_CTRL_ENABLE	0x02
+#define	SL811_USB_CTRL_DIR_OUT	0x04
+#define	SL811_USB_CTRL_ISO	0x10
+#define	SL811_USB_CTRL_SOF	0x20
+#define	SL811_USB_CTRL_TOGGLE_1	0x40
+#define	SL811_USB_CTRL_PREAMBLE	0x80
+
+/* USB status register bits (addr 0x03 and addr 0x0B) */
+#define	SL811_USB_STS_ACK	0x01
+#define	SL811_USB_STS_ERROR	0x02
+#define	SL811_USB_STS_TIMEOUT	0x04
+#define	SL811_USB_STS_TOGGLE_1	0x08
+#define	SL811_USB_STS_SETUP	0x10
+#define	SL811_USB_STS_OVERFLOW	0x20
+#define	SL811_USB_STS_NAK	0x40
+#define	SL811_USB_STS_STALL	0x80
+
+/* Control register 1 bits (addr 0x05) */
+#define	SL811_CTRL1_SOF		0x01
+#define	SL811_CTRL1_RESET	0x08
+#define	SL811_CTRL1_JKSTATE	0x10
+#define	SL811_CTRL1_SPEED_LOW	0x20
+#define	SL811_CTRL1_SUSPEND	0x40
+
+/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */
+#define	SL811_INTR_DONE_A	0x01
+#define	SL811_INTR_DONE_B	0x02
+#define	SL811_INTR_SOF		0x10
+#define	SL811_INTR_INSRMV	0x20
+#define	SL811_INTR_DETECT	0x40
+#define	SL811_INTR_NOTPRESENT	0x40
+#define	SL811_INTR_SPEED_FULL	0x80    /* only in status reg */
+
+/* HW rev and SOF lo register bits (addr 0x0E) */
+#define	SL811_HWR_HWREV		0xF0
+
+/* SOF counter and control reg 2 (addr 0x0F) */
+#define	SL811_CTL2_SOFHI	0x3F
+#define	SL811_CTL2_DSWAP	0x40
+#define	SL811_CTL2_HOST		0x80
+
+/* Set up for 1-ms SOF time. */
+#define SL811_12M_LOW		0xE0
+#define SL811_12M_HI		0x2E
+
+#define SL811_DATA_START	0x10
+#define SL811_DATA_LIMIT	240
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+
+
+#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep))
+
+#endif	/* __UBOOT_SL811_H */
diff --git a/drivers/usb/sl811_usb.c b/drivers/usb/sl811_usb.c
new file mode 100644
index 0000000..c1f8427
--- /dev/null
+++ b/drivers/usb/sl811_usb.c
@@ -0,0 +1,737 @@
+/*
+ * (C) Copyright 2004
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * This code is based on linux driver for sl811hs chip, source at
+ * drivers/usb/host/sl811.c:
+ *
+ * SL811 Host Controller Interface driver for USB.
+ *
+ * Copyright (c) 2003/06, Courage Co., Ltd.
+ *
+ * Based on:
+ *	1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
+ *	  Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
+ *	  Adam Richter, Gregory P. Smith;
+ *	2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
+ *	3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#ifdef CONFIG_USB_SL811HS
+#include <mpc8xx.h>
+#include <usb.h>
+#include "sl811.h"
+
+#include "../../board/kup/common/kup.h"
+
+#ifdef __PPC__
+# define EIEIO		__asm__ volatile ("eieio")
+#else
+# define EIEIO		/* nothing */
+#endif
+
+#define	 SL811_ADR (0x50000000)
+#define	 SL811_DAT (0x50000001)
+
+#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
+
+#ifdef SL811_DEBUG
+static int debug = 9;
+#endif
+
+static int root_hub_devnum = 0;
+static struct usb_port_status rh_status = { 0 };/* root hub port status */
+
+static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
+			       void *data, int buf_len, struct devrequest *cmd);
+
+static void sl811_write (__u8 index, __u8 data)
+{
+	*(volatile unsigned char *) (SL811_ADR) = index;
+	EIEIO;
+	*(volatile unsigned char *) (SL811_DAT) = data;
+	EIEIO;
+}
+
+static __u8 sl811_read (__u8 index)
+{
+	__u8 data;
+
+	*(volatile unsigned char *) (SL811_ADR) = index;
+	EIEIO;
+	data = *(volatile unsigned char *) (SL811_DAT);
+	EIEIO;
+	return (data);
+}
+
+/*
+ * Read consecutive bytes of data from the SL811H/SL11H buffer
+ */
+static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size)
+{
+	*(volatile unsigned char *) (SL811_ADR) = offset;
+	EIEIO;
+	while (size--) {
+		*buf++ = *(volatile unsigned char *) (SL811_DAT);
+		EIEIO;
+	}
+}
+
+/*
+ * Write consecutive bytes of data to the SL811H/SL11H buffer
+ */
+static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size)
+{
+	*(volatile unsigned char *) (SL811_ADR) = offset;
+	EIEIO;
+	while (size--) {
+		*(volatile unsigned char *) (SL811_DAT) = *buf++;
+		EIEIO;
+	}
+}
+
+int usb_init_kup4x (void)
+{
+	volatile immap_t *immap = (immap_t *) CFG_IMMR;
+	volatile memctl8xx_t *memctl = &immap->im_memctl;
+	int i;
+	unsigned char tmp;
+
+	memctl = &immap->im_memctl;
+	memctl->memc_or7 = 0xFFFF8726;
+	memctl->memc_br7 = 0x50000401;	/* start at 0x50000000 */
+	/* BP 14 low = USB ON */
+	immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC);
+	/* PB 14 nomal port */
+	immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC);
+	/* output */
+	immap->im_cpm.cp_pbdir |= (BP_USB_VCC);
+
+	puts ("USB:   ");
+
+	for (i = 0x10; i < 0xff; i++) {
+		sl811_write(i, i);
+		tmp = (sl811_read(i));
+		if (tmp != i) {
+			printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp);
+			return (-1);
+		}
+	}
+	printf ("SL811 ready\n");
+	return (0);
+}
+
+/*
+ * This function resets SL811HS controller and detects the speed of
+ * the connecting device
+ *
+ * Return: 0 = no device attached; 1 = USB device attached
+ */
+static int sl811_hc_reset(void)
+{
+	int status ;
+
+	sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
+	sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
+
+	mdelay(20);
+
+	/* Disable hardware SOF generation, clear all irq status. */
+	sl811_write(SL811_CTRL1, 0);
+	mdelay(2);
+	sl811_write(SL811_INTRSTS, 0xff);
+	status = sl811_read(SL811_INTRSTS);
+
+	if (status & SL811_INTR_NOTPRESENT) {
+		/* Device is not present */
+		PDEBUG(0, "Device not present\n");
+		rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
+		rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+		sl811_write(SL811_INTR, SL811_INTR_INSRMV);
+		return 0;
+	}
+
+	/* Send SOF to address 0, endpoint 0. */
+	sl811_write(SL811_LEN_B, 0);
+	sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
+	sl811_write(SL811_DEV_B, 0x00);
+	sl811_write(SL811_SOFLOW, SL811_12M_LOW);
+
+	if (status & SL811_INTR_SPEED_FULL) {
+		/* full speed device connect directly to root hub */
+		PDEBUG (0, "Full speed Device attached\n");
+
+		sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
+		mdelay(20);
+		sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
+		sl811_write(SL811_CTRL1, SL811_CTRL1_SOF);
+
+		/* start the SOF or EOP */
+		sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
+		rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
+		rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
+		mdelay(2);
+		sl811_write(SL811_INTRSTS, 0xff);
+	} else {
+		/* slow speed device connect directly to root-hub */
+		PDEBUG(0, "Low speed Device attached\n");
+
+		sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
+		mdelay(20);
+		sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
+		sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
+
+		/* start the SOF or EOP */
+		sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
+		rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
+		mdelay(2);
+		sl811_write(SL811_INTRSTS, 0xff);
+	}
+
+	rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+	sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A);
+
+	return 1;
+}
+
+int usb_lowlevel_init(void)
+{
+	root_hub_devnum = 0;
+	sl811_hc_reset();
+	return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+	sl811_hc_reset();
+	return 0;
+}
+
+static int calc_needed_buswidth(int bytes, int need_preamble)
+{
+	return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048;
+}
+
+static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len)
+{
+	__u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
+	__u16 status = 0;
+	int err = 0, time_start = get_timer(0);
+	int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
+		usb_pipeslow(pipe);
+
+	if (len > 239)
+		return -1;
+
+	if (usb_pipeout(pipe))
+		ctrl |= SL811_USB_CTRL_DIR_OUT;
+	if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)))
+		ctrl |= SL811_USB_CTRL_TOGGLE_1;
+	if (need_preamble)
+		ctrl |= SL811_USB_CTRL_PREAMBLE;
+
+	sl811_write(SL811_INTRSTS, 0xff);
+
+	while (err < 3) {
+		sl811_write(SL811_ADDR_A, 0x10);
+		sl811_write(SL811_LEN_A, len);
+		if (usb_pipeout(pipe) && len)
+			sl811_write_buf(0x10, buffer, len);
+
+		if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
+		    sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble))
+			ctrl |= SL811_USB_CTRL_SOF;
+		else
+			ctrl &= ~SL811_USB_CTRL_SOF;
+
+		sl811_write(SL811_CTRL_A, ctrl);
+ 		while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) {
+			if (5*CFG_HZ < get_timer(time_start)) {
+				printf("USB transmit timed out\n");
+				return -USB_ST_CRC_ERR;
+			}
+		}
+
+		sl811_write(SL811_INTRSTS, 0xff);
+		status = sl811_read(SL811_STS_A);
+
+		if (status & SL811_USB_STS_ACK) {
+			int remainder = sl811_read(SL811_CNT_A);
+			if (remainder) {
+				PDEBUG(0, "usb transfer remainder = %d\n", remainder);
+				len -= remainder;
+			}
+			if (usb_pipein(pipe) && len)
+				sl811_read_buf(0x10, buffer, len);
+			return len;
+		}
+
+		if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK)
+			continue;
+
+		PDEBUG(0, "usb transfer error %#x\n", (int)status);
+		err++;
+	}
+
+	err = 0;
+
+	if (status & SL811_USB_STS_ERROR)
+		err |= USB_ST_BUF_ERR;
+	if (status & SL811_USB_STS_TIMEOUT)
+		err |= USB_ST_CRC_ERR;
+	if (status & SL811_USB_STS_STALL)
+		err |= USB_ST_STALLED;
+
+	return -err;
+}
+
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		    int len)
+{
+	int dir_out = usb_pipeout(pipe);
+	int ep = usb_pipeendpoint(pipe);
+	int max = usb_maxpacket(dev, pipe);
+	int done = 0;
+
+	PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n",
+	       usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
+
+	dev->status = 0;
+
+	sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
+	sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
+	while (done < len) {
+		int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
+					    max > len - done ? len - done : max);
+		if (res < 0) {
+			dev->status = -res;
+			return res;
+		}
+
+		if (!dir_out && res < max) /* short packet */
+			break;
+
+		done += res;
+		usb_dotoggle(dev, ep, dir_out);
+	}
+
+	dev->act_len = done;
+
+	return 0;
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		       int len,struct devrequest *setup)
+{
+	int done = 0;
+	int devnum = usb_pipedevice(pipe);
+	int ep = usb_pipeendpoint(pipe);
+
+	dev->status = 0;
+
+	if (devnum == root_hub_devnum)
+		return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
+
+	PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n",
+	       devnum, ep, buffer, len, (int)setup->requesttype,
+	       (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64);
+
+	sl811_write(SL811_DEV_A, devnum);
+	sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep));
+	/* setup phase */
+	usb_settoggle(dev, ep, 1, 0);
+	if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep),
+			      (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
+		int dir_in = usb_pipein(pipe);
+		int max = usb_maxpacket(dev, pipe);
+
+		/* data phase */
+		sl811_write(SL811_PIDEP_A,
+			    PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
+		usb_settoggle(dev, ep, usb_pipeout(pipe), 1);
+		while (done < len) {
+			int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
+						    max > len - done ? len - done : max);
+			if (res < 0) {
+				PDEBUG(0, "status data failed!\n");
+				dev->status = -res;
+				return 0;
+			}
+			done += res;
+			usb_dotoggle(dev, ep, usb_pipeout(pipe));
+			if (dir_in && res < max) /* short packet */
+				break;
+		}
+
+		/* status phase */
+		sl811_write(SL811_PIDEP_A,
+			    PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep));
+		usb_settoggle(dev, ep, !usb_pipeout(pipe), 1);
+		if (sl811_send_packet(dev,
+				      !dir_in ? usb_rcvctrlpipe(dev, ep) :
+				      usb_sndctrlpipe(dev, ep),
+				      0, 0) < 0) {
+			PDEBUG(0, "status phase failed!\n");
+			dev->status = -1;
+		}
+	} else {
+		PDEBUG(0, "setup phase failed!\n");
+		dev->status = -1;
+	}
+
+	dev->act_len = done;
+
+	return done;
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		   int len, int interval)
+{
+	PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
+	       buffer, len, interval);
+	return -1;
+}
+
+/*
+ * SL811 Virtual Root Hub
+ */
+
+/* Device descriptor */
+static __u8 sl811_rh_dev_des[] =
+{
+	0x12,	    /*	__u8  bLength; */
+	0x01,	    /*	__u8  bDescriptorType; Device */
+	0x10,	    /*	__u16 bcdUSB; v1.1 */
+	0x01,
+	0x09,	    /*	__u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*	__u8  bDeviceSubClass; */
+	0x00,	    /*	__u8  bDeviceProtocol; */
+	0x08,	    /*	__u8  bMaxPacketSize0; 8 Bytes */
+	0x00,	    /*	__u16 idVendor; */
+	0x00,
+	0x00,	    /*	__u16 idProduct; */
+	0x00,
+	0x00,	    /*	__u16 bcdDevice; */
+	0x00,
+	0x00,	    /*	__u8  iManufacturer; */
+	0x02,	    /*	__u8  iProduct; */
+	0x01,	    /*	__u8  iSerialNumber; */
+	0x01	    /*	__u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 sl811_rh_config_des[] =
+{
+	0x09,	    /*	__u8  bLength; */
+	0x02,	    /*	__u8  bDescriptorType; Configuration */
+	0x19,	    /*	__u16 wTotalLength; */
+	0x00,
+	0x01,	    /*	__u8  bNumInterfaces; */
+	0x01,	    /*	__u8  bConfigurationValue; */
+	0x00,	    /*	__u8  iConfiguration; */
+	0x40,	    /*	__u8  bmAttributes;
+		    Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
+		    4..0: resvd */
+	0x00,	    /*	__u8  MaxPower; */
+
+	/* interface */
+	0x09,	    /*	__u8  if_bLength; */
+	0x04,	    /*	__u8  if_bDescriptorType; Interface */
+	0x00,	    /*	__u8  if_bInterfaceNumber; */
+	0x00,	    /*	__u8  if_bAlternateSetting; */
+	0x01,	    /*	__u8  if_bNumEndpoints; */
+	0x09,	    /*	__u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,	    /*	__u8  if_bInterfaceSubClass; */
+	0x00,	    /*	__u8  if_bInterfaceProtocol; */
+	0x00,	    /*	__u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,	    /*	__u8  ep_bLength; */
+	0x05,	    /*	__u8  ep_bDescriptorType; Endpoint */
+	0x81,	    /*	__u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,	    /*	__u8  ep_bmAttributes; Interrupt */
+	0x08,	    /*	__u16 ep_wMaxPacketSize; */
+	0x00,
+	0xff	    /*	__u8  ep_bInterval; 255 ms */
+};
+
+/* root hub class descriptor*/
+static __u8 sl811_rh_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x01,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x50,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0xfc,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/*
+ * helper routine for returning string descriptors in UTF-16LE
+ * input can actually be ISO-8859-1; ASCII is its 7-bit subset
+ */
+static int ascii2utf (char *s, u8 *utf, int utfmax)
+{
+	int retval;
+
+	for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
+		*utf++ = *s++;
+		*utf++ = 0;
+	}
+	return retval;
+}
+
+/*
+ * root_hub_string is used by each host controller's root hub code,
+ * so that they're identified consistently throughout the system.
+ */
+static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+	char buf [30];
+
+	/* assert (len > (2 * (sizeof (buf) + 1)));
+	   assert (strlen (type) <= 8);*/
+
+	/* language ids */
+	if (id == 0) {
+		*data++ = 4; *data++ = 3;	/* 4 bytes data */
+		*data++ = 0; *data++ = 0;	/* some language id */
+		return 4;
+
+	/* serial number */
+	} else if (id == 1) {
+		sprintf (buf, "%#x", serial);
+
+	/* product description */
+	} else if (id == 2) {
+		sprintf (buf, "USB %s Root Hub", type);
+
+	/* id 3 == vendor description */
+
+	/* unsupported IDs --> "stall" */
+	} else
+	    return 0;
+
+	ascii2utf (buf, data + 2, len - 2);
+	data [0] = 2 + strlen(buf) * 2;
+	data [1] = 3;
+	return data [0];
+}
+
+/* helper macro */
+#define OK(x)	len = (x); break
+
+/*
+ * This function handles all USB request to the the virtual root hub
+ */
+static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
+			       void *data, int buf_len, struct devrequest *cmd)
+{
+	__u8 data_buf[16];
+	__u8 *bufp = data_buf;
+	int len = 0;
+	int status = 0;
+
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipeint(pipe)) {
+		PDEBUG(0, "interrupt transfer unimplemented!\n");
+		return 0;
+	}
+
+	bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
+	wValue	      = le16_to_cpu (cmd->value);
+	wIndex	      = le16_to_cpu (cmd->index);
+	wLength	      = le16_to_cpu (cmd->length);
+
+	PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n",
+	       bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength);
+
+	/* Request Destination:
+		   without flags: Device,
+		   USB_RECIP_INTERFACE: interface,
+		   USB_RECIP_ENDPOINT: endpoint,
+		   USB_TYPE_CLASS means HUB here,
+		   USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here
+	*/
+	switch (bmRType_bReq) {
+	case RH_GET_STATUS:
+		*(__u16 *)bufp = cpu_to_le16(1);
+		OK(2);
+
+	case RH_GET_STATUS | USB_RECIP_INTERFACE:
+		*(__u16 *)bufp = cpu_to_le16(0);
+		OK(2);
+
+	case RH_GET_STATUS | USB_RECIP_ENDPOINT:
+		*(__u16 *)bufp = cpu_to_le16(0);
+		OK(2);
+
+	case RH_GET_STATUS | USB_TYPE_CLASS:
+		*(__u32 *)bufp = cpu_to_le32(0);
+		OK(4);
+
+	case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
+		*(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus);
+		OK(4);
+
+	case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
+		switch (wValue) {
+		case 1:
+			OK(0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+			OK(0);
+
+		case C_HUB_OVER_CURRENT:
+			OK(0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
+			OK(0);
+
+		case USB_PORT_FEAT_SUSPEND:
+			rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
+			OK(0);
+
+		case USB_PORT_FEAT_POWER:
+			rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
+			OK(0);
+
+		case USB_PORT_FEAT_C_CONNECTION:
+			rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
+			OK(0);
+
+		case USB_PORT_FEAT_C_ENABLE:
+			rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
+			OK(0);
+
+		case USB_PORT_FEAT_C_SUSPEND:
+			rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
+			OK(0);
+
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
+			OK(0);
+
+		case USB_PORT_FEAT_C_RESET:
+			rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
+			OK(0);
+		}
+		break;
+
+	case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
+			OK(0);
+
+		case USB_PORT_FEAT_RESET:
+			rh_status.wPortStatus |= USB_PORT_STAT_RESET;
+			rh_status.wPortChange = 0;
+			rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
+			rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
+			rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
+			OK(0);
+
+		case USB_PORT_FEAT_POWER:
+			rh_status.wPortStatus |= USB_PORT_STAT_POWER;
+			OK(0);
+
+		case USB_PORT_FEAT_ENABLE:
+			rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
+			OK(0);
+		}
+		break;
+
+	case RH_SET_ADDRESS:
+		root_hub_devnum = wValue;
+		OK(0);
+
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case USB_DT_DEVICE:
+			len = sizeof(sl811_rh_dev_des);
+			bufp = sl811_rh_dev_des;
+			OK(len);
+
+		case USB_DT_CONFIG:
+			len = sizeof(sl811_rh_config_des);
+			bufp = sl811_rh_config_des;
+			OK(len);
+
+		case USB_DT_STRING:
+			len = usb_root_hub_string(wValue & 0xff, (int)(long)0,	"SL811HS", data, wLength);
+			if (len > 0) {
+				bufp = data;
+				OK(len);
+			}
+
+		default:
+			status = -32;
+		}
+		break;
+
+	case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
+		len = sizeof(sl811_rh_hub_des);
+		bufp = sl811_rh_hub_des;
+		OK(len);
+
+	case RH_GET_CONFIGURATION:
+		bufp[0] = 0x01;
+		OK(1);
+
+	case RH_SET_CONFIGURATION:
+		OK(0);
+
+	default:
+		PDEBUG(1, "unsupported root hub command\n");
+		status = -32;
+	}
+
+	len = min(len, buf_len);
+	if (data != bufp)
+		memcpy(data, bufp, len);
+
+	PDEBUG(5, "len = %d, status = %d\n", len, status);
+
+	usb_dev->status = status;
+	usb_dev->act_len = len;
+
+	return status == 0 ? len : status;
+}
+
+#endif	/* CONFIG_USB_SL811HS */
diff --git a/drivers/usb/usb_ohci.c b/drivers/usb/usb_ohci.c
new file mode 100644
index 0000000..cfa384e
--- /dev/null
+++ b/drivers/usb/usb_ohci.c
@@ -0,0 +1,1923 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus.
+ *
+ * Interrupt support is added. Now, it has been tested
+ * on ULI1575 chip and works well with USB keyboard.
+ *
+ * (C) Copyright 2007
+ * Zhang Wei, Freescale Semiconductor, Inc. <wei.zhang@freescale.com>
+ *
+ * (C) Copyright 2003
+ * Gary Jennejohn, DENX Software Engineering <gj@denx.de>
+ *
+ * Note: Much of this code has been derived from Linux 2.4
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell
+ *
+ * Modified for the MP2USB by (C) Copyright 2005 Eric Benard
+ * ebenard@eukrea.com - based on s3c24x0's driver
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ *
+ */
+/*
+ * IMPORTANT NOTES
+ * 1 - Read doc/README.generic_usb_ohci
+ * 2 - this driver is intended for use with USB Mass Storage Devices
+ *     (BBB) and USB keyboard. There is NO support for Isochronous pipes!
+ * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG
+ *     to activate workaround for bug #41 or this driver will NOT work!
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_USB_OHCI_NEW
+
+#include <asm/byteorder.h>
+
+#if defined(CONFIG_PCI_OHCI)
+# include <pci.h>
+#endif
+
+#include <malloc.h>
+#include <usb.h>
+#include "usb_ohci.h"
+
+#ifdef CONFIG_AT91RM9200
+#include <asm/arch/hardware.h>	/* needed for AT91_USB_HOST_BASE */
+#endif
+
+#if defined(CONFIG_ARM920T) || \
+    defined(CONFIG_S3C2400) || \
+    defined(CONFIG_S3C2410) || \
+    defined(CONFIG_440EP) || \
+    defined(CONFIG_PCI_OHCI) || \
+    defined(CONFIG_MPC5200)
+# define OHCI_USE_NPS		/* force NoPowerSwitching mode */
+#endif
+
+#undef OHCI_VERBOSE_DEBUG	/* not always helpful */
+#undef DEBUG
+#undef SHOW_INFO
+#undef OHCI_FILL_TRACE
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT \
+	(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+/*
+ * e.g. PCI controllers need this
+ */
+#ifdef CFG_OHCI_SWAP_REG_ACCESS
+# define readl(a) __swap_32(*((vu_long *)(a)))
+# define writel(a, b) (*((vu_long *)(b)) = __swap_32((vu_long)a))
+#else
+# define readl(a) (*((vu_long *)(a)))
+# define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a))
+#endif /* CFG_OHCI_SWAP_REG_ACCESS */
+
+#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+
+#ifdef CONFIG_PCI_OHCI
+static struct pci_device_id ohci_pci_ids[] = {
+	{0x10b9, 0x5237},	/* ULI1575 PCI OHCI module ids */
+	{0x1033, 0x0035},	/* NEC PCI OHCI module ids */
+	/* Please add supported PCI OHCI controller ids here */
+	{0, 0}
+};
+#endif
+
+#ifdef DEBUG
+#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while(0)
+#endif /* DEBUG */
+#define err(format, arg...) printf("ERROR: " format "\n", ## arg)
+#undef SHOW_INFO
+#ifdef SHOW_INFO
+#define info(format, arg...) printf("INFO: " format "\n", ## arg)
+#else
+#define info(format, arg...) do {} while(0)
+#endif
+
+#ifdef CFG_OHCI_BE_CONTROLLER
+# define m16_swap(x) cpu_to_be16(x)
+# define m32_swap(x) cpu_to_be32(x)
+#else
+# define m16_swap(x) cpu_to_le16(x)
+# define m32_swap(x) cpu_to_le32(x)
+#endif /* CFG_OHCI_BE_CONTROLLER */
+
+/* global ohci_t */
+static ohci_t gohci;
+/* this must be aligned to a 256 byte boundary */
+struct ohci_hcca ghcca[1];
+/* a pointer to the aligned storage */
+struct ohci_hcca *phcca;
+/* this allocates EDs for all possible endpoints */
+struct ohci_device ohci_dev;
+/* RHSC flag */
+int got_rhsc;
+/* device which was disconnected */
+struct usb_device *devgone;
+
+/*-------------------------------------------------------------------------*/
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect.  AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define OHCI_QUIRK_AMD756 0xabcd
+#define read_roothub(hc, register, mask) ({ \
+	u32 temp = readl (&hc->regs->roothub.register); \
+	if (hc->flags & OHCI_QUIRK_AMD756) \
+		while (temp & mask) \
+			temp = readl (&hc->regs->roothub.register); \
+	temp; })
+
+static u32 roothub_a (struct ohci *hc)
+	{ return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci *hc)
+	{ return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci *hc)
+	{ return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci *hc, int i)
+	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+/* forward declaration */
+static int hc_interrupt (void);
+static void
+td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer,
+	int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval);
+
+/*-------------------------------------------------------------------------*
+ * URB support functions
+ *-------------------------------------------------------------------------*/
+
+/* free HCD-private data associated with this URB */
+
+static void urb_free_priv (urb_priv_t * urb)
+{
+	int		i;
+	int		last;
+	struct td	* td;
+
+	last = urb->length - 1;
+	if (last >= 0) {
+		for (i = 0; i <= last; i++) {
+			td = urb->td[i];
+			if (td) {
+				td->usb_dev = NULL;
+				urb->td[i] = NULL;
+			}
+		}
+	}
+	free(urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+static int sohci_get_current_frame_number (struct usb_device * dev);
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header */
+
+static void pkt_print (urb_priv_t *purb, struct usb_device * dev,
+	unsigned long pipe, void * buffer,
+	int transfer_len, struct devrequest * setup, char * str, int small)
+{
+	dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx",
+			str,
+			sohci_get_current_frame_number (dev),
+			usb_pipedevice (pipe),
+			usb_pipeendpoint (pipe),
+			usb_pipeout (pipe)? 'O': 'I',
+			usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
+				(usb_pipecontrol (pipe)? "CTRL": "BULK"),
+			(purb ? purb->actual_length : 0),
+			transfer_len, dev->status);
+#ifdef	OHCI_VERBOSE_DEBUG
+	if (!small) {
+		int i, len;
+
+		if (usb_pipecontrol (pipe)) {
+			printf (__FILE__ ": cmd(8):");
+			for (i = 0; i < 8 ; i++)
+				printf (" %02x", ((__u8 *) setup) [i]);
+			printf ("\n");
+		}
+		if (transfer_len > 0 && buffer) {
+			printf (__FILE__ ": data(%d/%d):",
+				(purb ? purb->actual_length : 0),
+				transfer_len);
+			len = usb_pipeout (pipe)?
+					transfer_len:
+					(purb ? purb->actual_length : 0);
+			for (i = 0; i < 16 && i < len; i++)
+				printf (" %02x", ((__u8 *) buffer) [i]);
+			printf ("%s\n", i < len? "...": "");
+		}
+	}
+#endif
+}
+
+/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
+void ep_print_int_eds (ohci_t *ohci, char * str) {
+	int i, j;
+	 __u32 * ed_p;
+	for (i= 0; i < 32; i++) {
+		j = 5;
+		ed_p = &(ohci->hcca->int_table [i]);
+		if (*ed_p == 0)
+		    continue;
+		printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i);
+		while (*ed_p != 0 && j--) {
+			ed_t *ed = (ed_t *)m32_swap(ed_p);
+			printf (" ed: %4x;", ed->hwINFO);
+			ed_p = &ed->hwNextED;
+		}
+		printf ("\n");
+	}
+}
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+		label,
+		mask,
+		(mask & OHCI_INTR_MIE) ? " MIE" : "",
+		(mask & OHCI_INTR_OC) ? " OC" : "",
+		(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+		(mask & OHCI_INTR_FNO) ? " FNO" : "",
+		(mask & OHCI_INTR_UE) ? " UE" : "",
+		(mask & OHCI_INTR_RD) ? " RD" : "",
+		(mask & OHCI_INTR_SF) ? " SF" : "",
+		(mask & OHCI_INTR_WDH) ? " WDH" : "",
+		(mask & OHCI_INTR_SO) ? " SO" : ""
+		);
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+	ed_t *edp = (ed_t *)value;
+
+	if (value) {
+		dbg ("%s %08x", label, value);
+		dbg ("%08x", edp->hwINFO);
+		dbg ("%08x", edp->hwTailP);
+		dbg ("%08x", edp->hwHeadP);
+		dbg ("%08x", edp->hwNextED);
+	}
+}
+
+static char * hcfs2string (int state)
+{
+	switch (state) {
+		case OHCI_USB_RESET:	return "reset";
+		case OHCI_USB_RESUME:	return "resume";
+		case OHCI_USB_OPER:	return "operational";
+		case OHCI_USB_SUSPEND:	return "suspend";
+	}
+	return "?";
+}
+
+/* dump control and status registers */
+static void ohci_dump_status (ohci_t *controller)
+{
+	struct ohci_regs	*regs = controller->regs;
+	__u32			temp;
+
+	temp = readl (&regs->revision) & 0xff;
+	if (temp != 0x10)
+		dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+	temp = readl (&regs->control);
+	dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+		(temp & OHCI_CTRL_RWE) ? " RWE" : "",
+		(temp & OHCI_CTRL_RWC) ? " RWC" : "",
+		(temp & OHCI_CTRL_IR) ? " IR" : "",
+		hcfs2string (temp & OHCI_CTRL_HCFS),
+		(temp & OHCI_CTRL_BLE) ? " BLE" : "",
+		(temp & OHCI_CTRL_CLE) ? " CLE" : "",
+		(temp & OHCI_CTRL_IE) ? " IE" : "",
+		(temp & OHCI_CTRL_PLE) ? " PLE" : "",
+		temp & OHCI_CTRL_CBSR
+		);
+
+	temp = readl (&regs->cmdstatus);
+	dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+		(temp & OHCI_SOC) >> 16,
+		(temp & OHCI_OCR) ? " OCR" : "",
+		(temp & OHCI_BLF) ? " BLF" : "",
+		(temp & OHCI_CLF) ? " CLF" : "",
+		(temp & OHCI_HCR) ? " HCR" : ""
+		);
+
+	ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
+	ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+
+	maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+	maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+	maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+	maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+	maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+	maybe_print_eds ("donehead", readl (&regs->donehead));
+}
+
+static void ohci_dump_roothub (ohci_t *controller, int verbose)
+{
+	__u32			temp, ndp, i;
+
+	temp = roothub_a (controller);
+	ndp = (temp & RH_A_NDP);
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+	ndp = (ndp == 2) ? 1:0;
+#endif
+	if (verbose) {
+		dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+			((temp & RH_A_POTPGT) >> 24) & 0xff,
+			(temp & RH_A_NOCP) ? " NOCP" : "",
+			(temp & RH_A_OCPM) ? " OCPM" : "",
+			(temp & RH_A_DT) ? " DT" : "",
+			(temp & RH_A_NPS) ? " NPS" : "",
+			(temp & RH_A_PSM) ? " PSM" : "",
+			ndp
+			);
+		temp = roothub_b (controller);
+		dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+			temp,
+			(temp & RH_B_PPCM) >> 16,
+			(temp & RH_B_DR)
+			);
+		temp = roothub_status (controller);
+		dbg ("roothub.status: %08x%s%s%s%s%s%s",
+			temp,
+			(temp & RH_HS_CRWE) ? " CRWE" : "",
+			(temp & RH_HS_OCIC) ? " OCIC" : "",
+			(temp & RH_HS_LPSC) ? " LPSC" : "",
+			(temp & RH_HS_DRWE) ? " DRWE" : "",
+			(temp & RH_HS_OCI) ? " OCI" : "",
+			(temp & RH_HS_LPS) ? " LPS" : ""
+			);
+	}
+
+	for (i = 0; i < ndp; i++) {
+		temp = roothub_portstatus (controller, i);
+		dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
+			i,
+			temp,
+			(temp & RH_PS_PRSC) ? " PRSC" : "",
+			(temp & RH_PS_OCIC) ? " OCIC" : "",
+			(temp & RH_PS_PSSC) ? " PSSC" : "",
+			(temp & RH_PS_PESC) ? " PESC" : "",
+			(temp & RH_PS_CSC) ? " CSC" : "",
+
+			(temp & RH_PS_LSDA) ? " LSDA" : "",
+			(temp & RH_PS_PPS) ? " PPS" : "",
+			(temp & RH_PS_PRS) ? " PRS" : "",
+			(temp & RH_PS_POCI) ? " POCI" : "",
+			(temp & RH_PS_PSS) ? " PSS" : "",
+
+			(temp & RH_PS_PES) ? " PES" : "",
+			(temp & RH_PS_CCS) ? " CCS" : ""
+			);
+	}
+}
+
+static void ohci_dump (ohci_t *controller, int verbose)
+{
+	dbg ("OHCI controller usb-%s state", controller->slot_name);
+
+	/* dumps some of the state we know about */
+	ohci_dump_status (controller);
+	if (verbose)
+		ep_print_int_eds (controller, "hcca");
+	dbg ("hcca frame #%04x", controller->hcca->frame_no);
+	ohci_dump_roothub (controller, 1);
+
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*
+ * Interface functions (URB)
+ *-------------------------------------------------------------------------*/
+
+/* get a transfer request */
+
+int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
+{
+	ohci_t *ohci;
+	ed_t * ed;
+	urb_priv_t *purb_priv = urb;
+	int i, size = 0;
+	struct usb_device *dev = urb->dev;
+	unsigned long pipe = urb->pipe;
+	void *buffer = urb->transfer_buffer;
+	int transfer_len = urb->transfer_buffer_length;
+	int interval = urb->interval;
+
+	ohci = &gohci;
+
+	/* when controller's hung, permit only roothub cleanup attempts
+	 * such as powering down ports */
+	if (ohci->disabled) {
+		err("sohci_submit_job: EPIPE");
+		return -1;
+	}
+
+	/* we're about to begin a new transaction here so mark the URB unfinished */
+	urb->finished = 0;
+
+	/* every endpoint has a ed, locate and fill it */
+	if (!(ed = ep_add_ed (dev, pipe, interval, 1))) {
+		err("sohci_submit_job: ENOMEM");
+		return -1;
+	}
+
+	/* for the private part of the URB we need the number of TDs (size) */
+	switch (usb_pipetype (pipe)) {
+		case PIPE_BULK: /* one TD for every 4096 Byte */
+			size = (transfer_len - 1) / 4096 + 1;
+			break;
+		case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
+			size = (transfer_len == 0)? 2:
+						(transfer_len - 1) / 4096 + 3;
+			break;
+		case PIPE_INTERRUPT: /* 1 TD */
+			size = 1;
+			break;
+	}
+
+	ed->purb = urb;
+
+	if (size >= (N_URB_TD - 1)) {
+		err("need %d TDs, only have %d", size, N_URB_TD);
+		return -1;
+	}
+	purb_priv->pipe = pipe;
+
+	/* fill the private part of the URB */
+	purb_priv->length = size;
+	purb_priv->ed = ed;
+	purb_priv->actual_length = 0;
+
+	/* allocate the TDs */
+	/* note that td[0] was allocated in ep_add_ed */
+	for (i = 0; i < size; i++) {
+		purb_priv->td[i] = td_alloc (dev);
+		if (!purb_priv->td[i]) {
+			purb_priv->length = i;
+			urb_free_priv (purb_priv);
+			err("sohci_submit_job: ENOMEM");
+			return -1;
+		}
+	}
+
+	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+		urb_free_priv (purb_priv);
+		err("sohci_submit_job: EINVAL");
+		return -1;
+	}
+
+	/* link the ed into a chain if is not already */
+	if (ed->state != ED_OPER)
+		ep_link (ohci, ed);
+
+	/* fill the TDs and link it to the ed */
+	td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval);
+
+	return 0;
+}
+
+static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb)
+{
+	struct ohci_regs *regs = hc->regs;
+
+	switch (usb_pipetype (urb->pipe)) {
+	case PIPE_INTERRUPT:
+		/* implicitly requeued */
+		if (urb->dev->irq_handle &&
+				(urb->dev->irq_act_len = urb->actual_length)) {
+			writel (OHCI_INTR_WDH, &regs->intrenable);
+			readl (&regs->intrenable); /* PCI posting flush */
+			urb->dev->irq_handle(urb->dev);
+			writel (OHCI_INTR_WDH, &regs->intrdisable);
+			readl (&regs->intrdisable); /* PCI posting flush */
+		}
+		urb->actual_length = 0;
+		td_submit_job (
+				urb->dev,
+				urb->pipe,
+				urb->transfer_buffer,
+				urb->transfer_buffer_length,
+				NULL,
+				urb,
+				urb->interval);
+		break;
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+/* tell us the current USB frame number */
+
+static int sohci_get_current_frame_number (struct usb_device *usb_dev)
+{
+	ohci_t *ohci = &gohci;
+
+	return m16_swap (ohci->hcca->frame_no);
+}
+#endif
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right branch to insert an interrupt ed into the int tree
+ * do some load ballancing;
+ * returns the branch and
+ * sets the interval to interval = 2^integer (ld (interval)) */
+
+static int ep_int_ballance (ohci_t * ohci, int interval, int load)
+{
+	int i, branch = 0;
+
+	/* search for the least loaded interrupt endpoint
+	 * branch of all 32 branches
+	 */
+	for (i = 0; i < 32; i++)
+		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])
+			branch = i;
+
+	branch = branch % interval;
+	for (i = branch; i < 32; i += interval)
+		ohci->ohci_int_load [i] += load;
+
+	return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*  2^int( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{
+	int i;
+	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
+	return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree
+ * in order to process it sequentially the indexes of the branches have to be mapped
+ * the mapping reverses the bits of a word of num_bits length */
+
+static int ep_rev (int num_bits, int word)
+{
+	int i, wout = 0;
+
+	for (i = 0; i < num_bits; i++)
+		wout |= (((word >> i) & 1) << (num_bits - i - 1));
+	return wout;
+}
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (ohci_t *ohci, ed_t *edi)
+{
+	volatile ed_t *ed = edi;
+	int int_branch;
+	int i;
+	int inter;
+	int interval;
+	int load;
+	__u32 * ed_p;
+
+	ed->state = ED_OPER;
+	ed->int_interval = 0;
+
+	switch (ed->type) {
+	case PIPE_CONTROL:
+		ed->hwNextED = 0;
+		if (ohci->ed_controltail == NULL) {
+			writel (ed, &ohci->regs->ed_controlhead);
+		} else {
+			ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed);
+		}
+		ed->ed_prev = ohci->ed_controltail;
+		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
+			!ohci->ed_rm_list[1] && !ohci->sleeping) {
+			ohci->hc_control |= OHCI_CTRL_CLE;
+			writel (ohci->hc_control, &ohci->regs->control);
+		}
+		ohci->ed_controltail = edi;
+		break;
+
+	case PIPE_BULK:
+		ed->hwNextED = 0;
+		if (ohci->ed_bulktail == NULL) {
+			writel (ed, &ohci->regs->ed_bulkhead);
+		} else {
+			ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed);
+		}
+		ed->ed_prev = ohci->ed_bulktail;
+		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
+			!ohci->ed_rm_list[1] && !ohci->sleeping) {
+			ohci->hc_control |= OHCI_CTRL_BLE;
+			writel (ohci->hc_control, &ohci->regs->control);
+		}
+		ohci->ed_bulktail = edi;
+		break;
+
+	case PIPE_INTERRUPT:
+		load = ed->int_load;
+		interval = ep_2_n_interval (ed->int_period);
+		ed->int_interval = interval;
+		int_branch = ep_int_ballance (ohci, interval, load);
+		ed->int_branch = int_branch;
+
+		for (i = 0; i < ep_rev (6, interval); i += inter) {
+			inter = 1;
+			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);
+				(*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval);
+				ed_p = &(((ed_t *)ed_p)->hwNextED))
+					inter = ep_rev (6, ((ed_t *)ed_p)->int_interval);
+			ed->hwNextED = *ed_p;
+			*ed_p = m32_swap((unsigned long)ed);
+		}
+		break;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed,
+		unsigned index, unsigned period)
+{
+	for (; index < NUM_INTS; index += period) {
+		__u32	*ed_p = &ohci->hcca->int_table [index];
+
+		/* ED might have been unlinked through another path */
+		while (*ed_p != 0) {
+			if (((struct ed *)m32_swap ((unsigned long)ed_p)) == ed) {
+				*ed_p = ed->hwNextED;
+				break;
+			}
+			ed_p = & (((struct ed *)m32_swap ((unsigned long)ed_p))->hwNextED);
+		}
+	}
+}
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed */
+
+static int ep_unlink (ohci_t *ohci, ed_t *edi)
+{
+	volatile ed_t *ed = edi;
+	int i;
+
+	ed->hwINFO |= m32_swap (OHCI_ED_SKIP);
+
+	switch (ed->type) {
+	case PIPE_CONTROL:
+		if (ed->ed_prev == NULL) {
+			if (!ed->hwNextED) {
+				ohci->hc_control &= ~OHCI_CTRL_CLE;
+				writel (ohci->hc_control, &ohci->regs->control);
+			}
+			writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_controltail == ed) {
+			ohci->ed_controltail = ed->ed_prev;
+		} else {
+			((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		}
+		break;
+
+	case PIPE_BULK:
+		if (ed->ed_prev == NULL) {
+			if (!ed->hwNextED) {
+				ohci->hc_control &= ~OHCI_CTRL_BLE;
+				writel (ohci->hc_control, &ohci->regs->control);
+			}
+			writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_bulktail == ed) {
+			ohci->ed_bulktail = ed->ed_prev;
+		} else {
+			((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		}
+		break;
+
+	case PIPE_INTERRUPT:
+		periodic_unlink (ohci, ed, 0, 1);
+		for (i = ed->int_branch; i < 32; i += ed->int_interval)
+		    ohci->ohci_int_load[i] -= ed->int_load;
+		break;
+	}
+	ed->state = ED_UNLINK;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* add/reinit an endpoint; this should be done once at the
+ * usb_set_configuration command, but the USB stack is a little bit
+ * stateless so we do it at every transaction if the state of the ed
+ * is ED_NEW then a dummy td is added and the state is changed to
+ * ED_UNLINK in all other cases the state is left unchanged the ed
+ * info fields are setted anyway even though most of them should not
+ * change
+ */
+static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe,
+		int interval, int load)
+{
+	td_t *td;
+	ed_t *ed_ret;
+	volatile ed_t *ed;
+
+	ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) |
+			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))];
+
+	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
+		err("ep_add_ed: pending delete");
+		/* pending delete request */
+		return NULL;
+	}
+
+	if (ed->state == ED_NEW) {
+		ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */
+		/* dummy td; end of td list for ed */
+		td = td_alloc (usb_dev);
+		ed->hwTailP = m32_swap ((unsigned long)td);
+		ed->hwHeadP = ed->hwTailP;
+		ed->state = ED_UNLINK;
+		ed->type = usb_pipetype (pipe);
+		ohci_dev.ed_cnt++;
+	}
+
+	ed->hwINFO = m32_swap (usb_pipedevice (pipe)
+			| usb_pipeendpoint (pipe) << 7
+			| (usb_pipeisoc (pipe)? 0x8000: 0)
+			| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
+			| usb_pipeslow (pipe) << 13
+			| usb_maxpacket (usb_dev, pipe) << 16);
+
+	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
+		ed->int_period = interval;
+		ed->int_load = load;
+	}
+
+	return ed_ret;
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void td_fill (ohci_t *ohci, unsigned int info,
+	void *data, int len,
+	struct usb_device *dev, int index, urb_priv_t *urb_priv)
+{
+	volatile td_t  *td, *td_pt;
+#ifdef OHCI_FILL_TRACE
+	int i;
+#endif
+
+	if (index > urb_priv->length) {
+		err("index > length");
+		return;
+	}
+	/* use this td as the next dummy */
+	td_pt = urb_priv->td [index];
+	td_pt->hwNextTD = 0;
+
+	/* fill the old dummy TD */
+	td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf);
+
+	td->ed = urb_priv->ed;
+	td->next_dl_td = NULL;
+	td->index = index;
+	td->data = (__u32)data;
+#ifdef OHCI_FILL_TRACE
+	if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) {
+		for (i = 0; i < len; i++)
+		printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
+		printf("\n");
+	}
+#endif
+	if (!len)
+		data = 0;
+
+	td->hwINFO = m32_swap (info);
+	td->hwCBP = m32_swap ((unsigned long)data);
+	if (data)
+		td->hwBE = m32_swap ((unsigned long)(data + len - 1));
+	else
+		td->hwBE = 0;
+	td->hwNextTD = m32_swap ((unsigned long)td_pt);
+
+	/* append to queue */
+	td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare all TDs of a transfer */
+
+static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer,
+	int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval)
+{
+	ohci_t *ohci = &gohci;
+	int data_len = transfer_len;
+	void *data;
+	int cnt = 0;
+	__u32 info = 0;
+	unsigned int toggle = 0;
+
+	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
+	if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
+		toggle = TD_T_TOGGLE;
+	} else {
+		toggle = TD_T_DATA0;
+		usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);
+	}
+	urb->td_cnt = 0;
+	if (data_len)
+		data = buffer;
+	else
+		data = 0;
+
+	switch (usb_pipetype (pipe)) {
+	case PIPE_BULK:
+		info = usb_pipeout (pipe)?
+			TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
+		while(data_len > 4096) {
+			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb);
+			data += 4096; data_len -= 4096; cnt++;
+		}
+		info = usb_pipeout (pipe)?
+			TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+		td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb);
+		cnt++;
+
+		if (!ohci->sleeping)
+			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+		break;
+
+	case PIPE_CONTROL:
+		info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+		td_fill (ohci, info, setup, 8, dev, cnt++, urb);
+		if (data_len > 0) {
+			info = usb_pipeout (pipe)?
+				TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
+			/* NOTE:  mishandles transfers >8K, some >4K */
+			td_fill (ohci, info, data, data_len, dev, cnt++, urb);
+		}
+		info = usb_pipeout (pipe)?
+			TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
+		td_fill (ohci, info, data, 0, dev, cnt++, urb);
+		if (!ohci->sleeping)
+			writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+		break;
+
+	case PIPE_INTERRUPT:
+		info = usb_pipeout (urb->pipe)?
+			TD_CC | TD_DP_OUT | toggle:
+			TD_CC | TD_R | TD_DP_IN | toggle;
+		td_fill (ohci, info, data, data_len, dev, cnt++, urb);
+		break;
+	}
+	if (urb->length != cnt)
+		dbg("TD LENGTH %d != CNT %d", urb->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length(td_t * td)
+{
+	__u32 tdINFO, tdBE, tdCBP;
+	urb_priv_t *lurb_priv = td->ed->purb;
+
+	tdINFO = m32_swap (td->hwINFO);
+	tdBE   = m32_swap (td->hwBE);
+	tdCBP  = m32_swap (td->hwCBP);
+
+	if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL &&
+	    ((td->index == 0) || (td->index == lurb_priv->length - 1)))) {
+		if (tdBE != 0) {
+			if (td->hwCBP == 0)
+				lurb_priv->actual_length += tdBE - td->data + 1;
+			else
+				lurb_priv->actual_length += tdCBP - td->data;
+		}
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+
+static td_t * dl_reverse_done_list (ohci_t *ohci)
+{
+	__u32 td_list_hc;
+	td_t *td_rev = NULL;
+	td_t *td_list = NULL;
+	urb_priv_t *lurb_priv = NULL;
+
+	td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0;
+	ohci->hcca->done_head = 0;
+
+	while (td_list_hc) {
+		td_list = (td_t *)td_list_hc;
+
+		if (TD_CC_GET (m32_swap (td_list->hwINFO))) {
+			lurb_priv = td_list->ed->purb;
+			dbg(" USB-error/status: %x : %p",
+					TD_CC_GET (m32_swap (td_list->hwINFO)), td_list);
+			if (td_list->ed->hwHeadP & m32_swap (0x1)) {
+				if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) {
+					td_list->ed->hwHeadP =
+						(lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) |
+									(td_list->ed->hwHeadP & m32_swap (0x2));
+					lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1;
+				} else
+					td_list->ed->hwHeadP &= m32_swap (0xfffffff2);
+			}
+#ifdef CONFIG_MPC5200
+			td_list->hwNextTD = 0;
+#endif
+		}
+
+		td_list->next_dl_td = td_rev;
+		td_rev = td_list;
+		td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0;
+	}
+	return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+static int dl_done_list (ohci_t *ohci, td_t *td_list)
+{
+	td_t *td_list_next = NULL;
+	ed_t *ed;
+	int cc = 0;
+	int stat = 0;
+	/* urb_t *urb; */
+	urb_priv_t *lurb_priv;
+	__u32 tdINFO, edHeadP, edTailP;
+
+	while (td_list) {
+		td_list_next = td_list->next_dl_td;
+
+		tdINFO = m32_swap (td_list->hwINFO);
+
+		ed = td_list->ed;
+		lurb_priv = ed->purb;
+
+		dl_transfer_length(td_list);
+
+		/* error code of transfer */
+		cc = TD_CC_GET (tdINFO);
+		if (cc != 0) {
+			dbg("ConditionCode %#x", cc);
+			stat = cc_to_error[cc];
+		}
+
+		/* see if this done list makes for all TD's of current URB,
+		 * and mark the URB finished if so */
+		if (++(lurb_priv->td_cnt) == lurb_priv->length) {
+#if 1
+			if ((ed->state & (ED_OPER | ED_UNLINK)) &&
+			    (lurb_priv->state != URB_DEL))
+#else
+			if ((ed->state & (ED_OPER | ED_UNLINK)))
+#endif
+				lurb_priv->finished = sohci_return_job(ohci,
+						lurb_priv);
+			else
+				dbg("dl_done_list: strange.., ED state %x, ed->state\n");
+		} else
+			dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt,
+				lurb_priv->length);
+		if (ed->state != ED_NEW &&
+			  (usb_pipetype (lurb_priv->pipe) != PIPE_INTERRUPT)) {
+			edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0;
+			edTailP = m32_swap (ed->hwTailP);
+
+			/* unlink eds if they are not busy */
+			if ((edHeadP == edTailP) && (ed->state == ED_OPER))
+				ep_unlink (ohci, ed);
+		}
+
+		td_list = td_list_next;
+	}
+	return stat;
+}
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub
+ *-------------------------------------------------------------------------*/
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] =
+{
+	0x12,	    /*	__u8  bLength; */
+	0x01,	    /*	__u8  bDescriptorType; Device */
+	0x10,	    /*	__u16 bcdUSB; v1.1 */
+	0x01,
+	0x09,	    /*	__u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*	__u8  bDeviceSubClass; */
+	0x00,	    /*	__u8  bDeviceProtocol; */
+	0x08,	    /*	__u8  bMaxPacketSize0; 8 Bytes */
+	0x00,	    /*	__u16 idVendor; */
+	0x00,
+	0x00,	    /*	__u16 idProduct; */
+	0x00,
+	0x00,	    /*	__u16 bcdDevice; */
+	0x00,
+	0x00,	    /*	__u8  iManufacturer; */
+	0x01,	    /*	__u8  iProduct; */
+	0x00,	    /*	__u8  iSerialNumber; */
+	0x01	    /*	__u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+	0x09,	    /*	__u8  bLength; */
+	0x02,	    /*	__u8  bDescriptorType; Configuration */
+	0x19,	    /*	__u16 wTotalLength; */
+	0x00,
+	0x01,	    /*	__u8  bNumInterfaces; */
+	0x01,	    /*	__u8  bConfigurationValue; */
+	0x00,	    /*	__u8  iConfiguration; */
+	0x40,	    /*	__u8  bmAttributes;
+		 Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+	0x00,	    /*	__u8  MaxPower; */
+
+	/* interface */
+	0x09,	    /*	__u8  if_bLength; */
+	0x04,	    /*	__u8  if_bDescriptorType; Interface */
+	0x00,	    /*	__u8  if_bInterfaceNumber; */
+	0x00,	    /*	__u8  if_bAlternateSetting; */
+	0x01,	    /*	__u8  if_bNumEndpoints; */
+	0x09,	    /*	__u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,	    /*	__u8  if_bInterfaceSubClass; */
+	0x00,	    /*	__u8  if_bInterfaceProtocol; */
+	0x00,	    /*	__u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,	    /*	__u8  ep_bLength; */
+	0x05,	    /*	__u8  ep_bDescriptorType; Endpoint */
+	0x81,	    /*	__u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,	    /*	__u8  ep_bmAttributes; Interrupt */
+	0x02,	    /*	__u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+	0x00,
+	0xff	    /*	__u8  ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] =
+{
+	0x04,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	0x09,			/*  __u8  lang ID */
+	0x04,			/*  __u8  lang ID */
+};
+
+static unsigned char root_hub_str_index1[] =
+{
+	28,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	'O',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'H',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'C',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'I',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	' ',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'R',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'o',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'o',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	't',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	' ',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'H',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'u',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'b',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+};
+
+/* Hub class-specific descriptor is constructed dynamically */
+
+/*-------------------------------------------------------------------------*/
+
+#define OK(x)			len = (x); break
+#ifdef DEBUG
+#define WR_RH_STAT(x)		{info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);}
+#define WR_RH_PORTSTAT(x)	{info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);}
+#else
+#define WR_RH_STAT(x)		writel((x), &gohci.regs->roothub.status)
+#define WR_RH_PORTSTAT(x)	writel((x), &gohci.regs->roothub.portstatus[wIndex-1])
+#endif
+#define RD_RH_STAT		roothub_status(&gohci)
+#define RD_RH_PORTSTAT		roothub_portstatus(&gohci,wIndex-1)
+
+/* request to virtual root hub */
+
+int rh_check_port_status(ohci_t *controller)
+{
+	__u32 temp, ndp, i;
+	int res;
+
+	res = -1;
+	temp = roothub_a (controller);
+	ndp = (temp & RH_A_NDP);
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+	ndp = (ndp == 2) ? 1:0;
+#endif
+	for (i = 0; i < ndp; i++) {
+		temp = roothub_portstatus (controller, i);
+		/* check for a device disconnect */
+		if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
+			(RH_PS_PESC | RH_PS_CSC)) &&
+			((temp & RH_PS_CCS) == 0)) {
+			res = i;
+			break;
+		}
+	}
+	return res;
+}
+
+static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+		void *buffer, int transfer_len, struct devrequest *cmd)
+{
+	void * data = buffer;
+	int leni = transfer_len;
+	int len = 0;
+	int stat = 0;
+	__u32 datab[4];
+	__u8 *data_buf = (__u8 *)datab;
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+#ifdef DEBUG
+pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
+#else
+	wait_ms(1);
+#endif
+	if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+		info("Root-Hub submit IRQ: NOT implemented");
+		return 0;
+	}
+
+	bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
+	wValue	      = cpu_to_le16 (cmd->value);
+	wIndex	      = cpu_to_le16 (cmd->index);
+	wLength	      = cpu_to_le16 (cmd->length);
+
+	info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x",
+		dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
+
+	switch (bmRType_bReq) {
+	/* Request Destination:
+	   without flags: Device,
+	   RH_INTERFACE: interface,
+	   RH_ENDPOINT: endpoint,
+	   RH_CLASS means HUB here,
+	   RH_OTHER | RH_CLASS	almost ever means HUB_PORT here
+	*/
+
+	case RH_GET_STATUS:
+			*(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
+	case RH_GET_STATUS | RH_INTERFACE:
+			*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+	case RH_GET_STATUS | RH_ENDPOINT:
+			*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+	case RH_GET_STATUS | RH_CLASS:
+			*(__u32 *) data_buf = cpu_to_le32 (
+				RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+			OK (4);
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+			*(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
+
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+			case (RH_ENDPOINT_STALL): OK (0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+			case RH_C_HUB_LOCAL_POWER:
+				OK(0);
+			case (RH_C_HUB_OVER_CURRENT):
+					WR_RH_STAT(RH_HS_OCIC); OK (0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+			case (RH_PORT_ENABLE):
+					WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
+			case (RH_PORT_SUSPEND):
+					WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
+			case (RH_PORT_POWER):
+					WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
+			case (RH_C_PORT_CONNECTION):
+					WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
+			case (RH_C_PORT_ENABLE):
+					WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
+			case (RH_C_PORT_SUSPEND):
+					WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
+			case (RH_C_PORT_OVER_CURRENT):
+					WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
+			case (RH_C_PORT_RESET):
+					WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
+		}
+		break;
+
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+			case (RH_PORT_SUSPEND):
+					WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
+			case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
+					if (RD_RH_PORTSTAT & RH_PS_CCS)
+					    WR_RH_PORTSTAT (RH_PS_PRS);
+					OK (0);
+			case (RH_PORT_POWER):
+					WR_RH_PORTSTAT (RH_PS_PPS );
+					wait_ms(100);
+					OK (0);
+			case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
+					if (RD_RH_PORTSTAT & RH_PS_CCS)
+					    WR_RH_PORTSTAT (RH_PS_PES );
+					OK (0);
+		}
+		break;
+
+	case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0);
+
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+			case (0x01): /* device descriptor */
+				len = min_t(unsigned int,
+					  leni,
+					  min_t(unsigned int,
+					      sizeof (root_hub_dev_des),
+					      wLength));
+				data_buf = root_hub_dev_des; OK(len);
+			case (0x02): /* configuration descriptor */
+				len = min_t(unsigned int,
+					  leni,
+					  min_t(unsigned int,
+					      sizeof (root_hub_config_des),
+					      wLength));
+				data_buf = root_hub_config_des; OK(len);
+			case (0x03): /* string descriptors */
+				if(wValue==0x0300) {
+					len = min_t(unsigned int,
+						  leni,
+						  min_t(unsigned int,
+						      sizeof (root_hub_str_index0),
+						      wLength));
+					data_buf = root_hub_str_index0;
+					OK(len);
+				}
+				if(wValue==0x0301) {
+					len = min_t(unsigned int,
+						  leni,
+						  min_t(unsigned int,
+						      sizeof (root_hub_str_index1),
+						      wLength));
+					data_buf = root_hub_str_index1;
+					OK(len);
+			}
+			default:
+				stat = USB_ST_STALLED;
+		}
+		break;
+
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+	{
+		__u32 temp = roothub_a (&gohci);
+
+		data_buf [0] = 9;		/* min length; */
+		data_buf [1] = 0x29;
+		data_buf [2] = temp & RH_A_NDP;
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+		data_buf [2] = (data_buf [2] == 2) ? 1:0;
+#endif
+		data_buf [3] = 0;
+		if (temp & RH_A_PSM)	/* per-port power switching? */
+			data_buf [3] |= 0x1;
+		if (temp & RH_A_NOCP)	/* no overcurrent reporting? */
+			data_buf [3] |= 0x10;
+		else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */
+			data_buf [3] |= 0x8;
+
+		/* corresponds to data_buf[4-7] */
+		datab [1] = 0;
+		data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+		temp = roothub_b (&gohci);
+		data_buf [7] = temp & RH_B_DR;
+		if (data_buf [2] < 7) {
+			data_buf [8] = 0xff;
+		} else {
+			data_buf [0] += 2;
+			data_buf [8] = (temp & RH_B_DR) >> 8;
+			data_buf [10] = data_buf [9] = 0xff;
+		}
+
+		len = min_t(unsigned int, leni,
+			    min_t(unsigned int, data_buf [0], wLength));
+		OK (len);
+	}
+
+	case RH_GET_CONFIGURATION:	*(__u8 *) data_buf = 0x01; OK (1);
+
+	case RH_SET_CONFIGURATION:	WR_RH_STAT (0x10000); OK (0);
+
+	default:
+		dbg ("unsupported root hub command");
+		stat = USB_ST_STALLED;
+	}
+
+#ifdef	DEBUG
+	ohci_dump_roothub (&gohci, 1);
+#else
+	wait_ms(1);
+#endif
+
+	len = min_t(int, len, leni);
+	if (data != data_buf)
+	    memcpy (data, data_buf, len);
+	dev->act_len = len;
+	dev->status = stat;
+
+#ifdef DEBUG
+	pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/);
+#else
+	wait_ms(1);
+#endif
+
+	return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* common code for handling submit messages - used for all but root hub */
+/* accesses. */
+int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		int transfer_len, struct devrequest *setup, int interval)
+{
+	int stat = 0;
+	int maxsize = usb_maxpacket(dev, pipe);
+	int timeout;
+	urb_priv_t *urb;
+
+	urb = malloc(sizeof(urb_priv_t));
+	memset(urb, 0, sizeof(urb_priv_t));
+
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->transfer_buffer = buffer;
+	urb->transfer_buffer_length = transfer_len;
+	urb->interval = interval;
+
+	/* device pulled? Shortcut the action. */
+	if (devgone == dev) {
+		dev->status = USB_ST_CRC_ERR;
+		return 0;
+	}
+
+#ifdef DEBUG
+	urb->actual_length = 0;
+	pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
+#else
+	wait_ms(1);
+#endif
+	if (!maxsize) {
+		err("submit_common_message: pipesize for pipe %lx is zero",
+			pipe);
+		return -1;
+	}
+
+	if (sohci_submit_job(urb, setup) < 0) {
+		err("sohci_submit_job failed");
+		return -1;
+	}
+
+#if 0
+	wait_ms(10);
+	/* ohci_dump_status(&gohci); */
+#endif
+
+	/* allow more time for a BULK device to react - some are slow */
+#define BULK_TO	 5000	/* timeout in milliseconds */
+	if (usb_pipetype (pipe) == PIPE_BULK)
+		timeout = BULK_TO;
+	else
+		timeout = 100;
+
+	/* wait for it to complete */
+	for (;;) {
+		/* check whether the controller is done */
+		stat = hc_interrupt();
+		if (stat < 0) {
+			stat = USB_ST_CRC_ERR;
+			break;
+		}
+
+		/* NOTE: since we are not interrupt driven in U-Boot and always
+		 * handle only one URB at a time, we cannot assume the
+		 * transaction finished on the first successful return from
+		 * hc_interrupt().. unless the flag for current URB is set,
+		 * meaning that all TD's to/from device got actually
+		 * transferred and processed. If the current URB is not
+		 * finished we need to re-iterate this loop so as
+		 * hc_interrupt() gets called again as there needs to be some
+		 * more TD's to process still */
+		if ((stat >= 0) && (stat != 0xff) && (urb->finished)) {
+			/* 0xff is returned for an SF-interrupt */
+			break;
+		}
+
+		if (--timeout) {
+			wait_ms(1);
+			if (!urb->finished)
+				dbg("\%");
+
+		} else {
+			err("CTL:TIMEOUT ");
+			dbg("submit_common_msg: TO status %x\n", stat);
+			urb->finished = 1;
+			stat = USB_ST_CRC_ERR;
+			break;
+		}
+	}
+
+	dev->status = stat;
+	dev->act_len = transfer_len;
+
+#ifdef DEBUG
+	pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe));
+#else
+	wait_ms(1);
+#endif
+
+	/* free TDs in urb_priv */
+	if (usb_pipetype (pipe) != PIPE_INTERRUPT)
+		urb_free_priv (urb);
+	return 0;
+}
+
+/* submit routines called from usb.c */
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		int transfer_len)
+{
+	info("submit_bulk_msg");
+	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		int transfer_len, struct devrequest *setup)
+{
+	int maxsize = usb_maxpacket(dev, pipe);
+
+	info("submit_control_msg");
+#ifdef DEBUG
+	pkt_print(NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
+#else
+	wait_ms(1);
+#endif
+	if (!maxsize) {
+		err("submit_control_message: pipesize for pipe %lx is zero",
+			pipe);
+		return -1;
+	}
+	if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
+		gohci.rh.dev = dev;
+		/* root hub - redirect */
+		return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
+			setup);
+	}
+
+	return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		int transfer_len, int interval)
+{
+	info("submit_int_msg");
+	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL,
+			interval);
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (ohci_t *ohci)
+{
+	int timeout = 30;
+	int smm_timeout = 50; /* 0,5 sec */
+
+	dbg("%s\n", __FUNCTION__);
+
+	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+		writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
+		info("USB HC TakeOver from SMM");
+		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+			wait_ms (10);
+			if (--smm_timeout == 0) {
+				err("USB HC TakeOver failed!");
+				return -1;
+			}
+		}
+	}
+
+	/* Disable HC interrupts */
+	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+	dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n",
+		ohci->slot_name,
+		readl(&ohci->regs->control));
+
+	/* Reset USB (needed by some controllers) */
+	ohci->hc_control = 0;
+	writel (ohci->hc_control, &ohci->regs->control);
+
+	/* HC Reset requires max 10 us delay */
+	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
+	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+		if (--timeout == 0) {
+			err("USB HC reset timed out!");
+			return -1;
+		}
+		udelay (1);
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub */
+
+static int hc_start (ohci_t * ohci)
+{
+	__u32 mask;
+	unsigned int fminterval;
+
+	ohci->disabled = 1;
+
+	/* Tell the controller where the control and bulk lists are
+	 * The lists are empty now. */
+
+	writel (0, &ohci->regs->ed_controlhead);
+	writel (0, &ohci->regs->ed_bulkhead);
+
+	writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */
+
+	fminterval = 0x2edf;
+	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+	fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
+	writel (fminterval, &ohci->regs->fminterval);
+	writel (0x628, &ohci->regs->lsthresh);
+
+	/* start controller operations */
+	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+	ohci->disabled = 0;
+	writel (ohci->hc_control, &ohci->regs->control);
+
+	/* disable all interrupts */
+	mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD |
+			OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC |
+			OHCI_INTR_OC | OHCI_INTR_MIE);
+	writel (mask, &ohci->regs->intrdisable);
+	/* clear all interrupts */
+	mask &= ~OHCI_INTR_MIE;
+	writel (mask, &ohci->regs->intrstatus);
+	/* Choose the interrupts we care about now  - but w/o MIE */
+	mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+	writel (mask, &ohci->regs->intrenable);
+
+#ifdef	OHCI_USE_NPS
+	/* required for AMD-756 and some Mac platforms */
+	writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
+		&ohci->regs->roothub.a);
+	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif	/* OHCI_USE_NPS */
+
+#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
+	/* POTPGT delay is bits 24-31, in 2 ms units. */
+	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+
+	/* connect the virtual root hub */
+	ohci->rh.devnum = 0;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Poll USB interrupt. */
+void usb_event_poll(void)
+{
+	hc_interrupt();
+}
+
+/* an interrupt happens */
+
+static int hc_interrupt (void)
+{
+	ohci_t *ohci = &gohci;
+	struct ohci_regs *regs = ohci->regs;
+	int ints;
+	int stat = -1;
+
+	if ((ohci->hcca->done_head != 0) &&
+	    !(m32_swap (ohci->hcca->done_head) & 0x01)) {
+		ints =  OHCI_INTR_WDH;
+	} else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
+		ohci->disabled++;
+		err ("%s device removed!", ohci->slot_name);
+		return -1;
+	} else if ((ints &= readl (&regs->intrenable)) == 0) {
+		dbg("hc_interrupt: returning..\n");
+		return 0xff;
+	}
+
+	/* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */
+
+	if (ints & OHCI_INTR_RHSC) {
+		got_rhsc = 1;
+		stat = 0xff;
+	}
+
+	if (ints & OHCI_INTR_UE) {
+		ohci->disabled++;
+		err ("OHCI Unrecoverable Error, controller usb-%s disabled",
+			ohci->slot_name);
+		/* e.g. due to PCI Master/Target Abort */
+
+#ifdef	DEBUG
+		ohci_dump (ohci, 1);
+#else
+	wait_ms(1);
+#endif
+		/* FIXME: be optimistic, hope that bug won't repeat often. */
+		/* Make some non-interrupt context restart the controller. */
+		/* Count and limit the retries though; either hardware or */
+		/* software errors can go forever... */
+		hc_reset (ohci);
+		return -1;
+	}
+
+	if (ints & OHCI_INTR_WDH) {
+		wait_ms(1);
+		writel (OHCI_INTR_WDH, &regs->intrdisable);
+		(void)readl (&regs->intrdisable); /* flush */
+		stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci));
+		writel (OHCI_INTR_WDH, &regs->intrenable);
+		(void)readl (&regs->intrdisable); /* flush */
+	}
+
+	if (ints & OHCI_INTR_SO) {
+		dbg("USB Schedule overrun\n");
+		writel (OHCI_INTR_SO, &regs->intrenable);
+		stat = -1;
+	}
+
+	/* FIXME:  this assumes SOF (1/ms) interrupts don't get lost... */
+	if (ints & OHCI_INTR_SF) {
+		unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1;
+		wait_ms(1);
+		writel (OHCI_INTR_SF, &regs->intrdisable);
+		if (ohci->ed_rm_list[frame] != NULL)
+			writel (OHCI_INTR_SF, &regs->intrenable);
+		stat = 0xff;
+	}
+
+	writel (ints, &regs->intrstatus);
+	return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci (ohci_t *ohci)
+{
+	dbg ("USB HC release ohci usb-%s", ohci->slot_name);
+
+	if (!ohci->disabled)
+		hc_reset (ohci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * low level initalisation routine, called from usb.c
+ */
+static char ohci_inited = 0;
+
+int usb_lowlevel_init(void)
+{
+#ifdef CONFIG_PCI_OHCI
+	pci_dev_t pdev;
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+	/* cpu dependant init */
+	if(usb_cpu_init())
+		return -1;
+#endif
+
+#ifdef CFG_USB_OHCI_BOARD_INIT
+	/*  board dependant init */
+	if(usb_board_init())
+		return -1;
+#endif
+	memset (&gohci, 0, sizeof (ohci_t));
+
+	/* align the storage */
+	if ((__u32)&ghcca[0] & 0xff) {
+		err("HCCA not aligned!!");
+		return -1;
+	}
+	phcca = &ghcca[0];
+	info("aligned ghcca %p", phcca);
+	memset(&ohci_dev, 0, sizeof(struct ohci_device));
+	if ((__u32)&ohci_dev.ed[0] & 0x7) {
+		err("EDs not aligned!!");
+		return -1;
+	}
+	memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
+	if ((__u32)gtd & 0x7) {
+		err("TDs not aligned!!");
+		return -1;
+	}
+	ptd = gtd;
+	gohci.hcca = phcca;
+	memset (phcca, 0, sizeof (struct ohci_hcca));
+
+	gohci.disabled = 1;
+	gohci.sleeping = 0;
+	gohci.irq = -1;
+#ifdef CONFIG_PCI_OHCI
+	pdev = pci_find_devices(ohci_pci_ids, 0);
+
+	if (pdev != -1) {
+		u16 vid, did;
+		u32 base;
+		pci_read_config_word(pdev, PCI_VENDOR_ID, &vid);
+		pci_read_config_word(pdev, PCI_DEVICE_ID, &did);
+		printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n",
+				vid, did, (pdev >> 16) & 0xff,
+				(pdev >> 11) & 0x1f, (pdev >> 8) & 0x7);
+		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base);
+		printf("OHCI regs address 0x%08x\n", base);
+		gohci.regs = (struct ohci_regs *)base;
+	} else
+		return -1;
+#else
+	gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE;
+#endif
+
+	gohci.flags = 0;
+	gohci.slot_name = CFG_USB_OHCI_SLOT_NAME;
+
+	if (hc_reset (&gohci) < 0) {
+		hc_release_ohci (&gohci);
+		err ("can't reset usb-%s", gohci.slot_name);
+#ifdef CFG_USB_OHCI_BOARD_INIT
+		/* board dependant cleanup */
+		usb_board_init_fail();
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+		/* cpu dependant cleanup */
+		usb_cpu_init_fail();
+#endif
+		return -1;
+	}
+
+	/* FIXME this is a second HC reset; why?? */
+	/* writel(gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control);
+	   wait_ms(10); */
+	if (hc_start (&gohci) < 0) {
+		err ("can't start usb-%s", gohci.slot_name);
+		hc_release_ohci (&gohci);
+		/* Initialization failed */
+#ifdef CFG_USB_OHCI_BOARD_INIT
+		/* board dependant cleanup */
+		usb_board_stop();
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+		/* cpu dependant cleanup */
+		usb_cpu_stop();
+#endif
+		return -1;
+	}
+
+#ifdef	DEBUG
+	ohci_dump (&gohci, 1);
+#else
+	wait_ms(1);
+#endif
+	ohci_inited = 1;
+	return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+	/* this gets called really early - before the controller has */
+	/* even been initialized! */
+	if (!ohci_inited)
+		return 0;
+	/* TODO release any interrupts, etc. */
+	/* call hc_release_ohci() here ? */
+	hc_reset (&gohci);
+
+#ifdef CFG_USB_OHCI_BOARD_INIT
+	/* board dependant cleanup */
+	if(usb_board_stop())
+		return -1;
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+	/* cpu dependant cleanup */
+	if(usb_cpu_stop())
+		return -1;
+#endif
+
+	return 0;
+}
+#endif /* CONFIG_USB_OHCI_NEW */
diff --git a/drivers/usb/usb_ohci.h b/drivers/usb/usb_ohci.h
new file mode 100644
index 0000000..380cb4c
--- /dev/null
+++ b/drivers/usb/usb_ohci.h
@@ -0,0 +1,445 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * usb-ohci.h
+ */
+
+/* functions for doing board or CPU specific setup/cleanup */
+extern int usb_board_init(void);
+extern int usb_board_stop(void);
+extern int usb_board_init_fail(void);
+
+extern int usb_cpu_init(void);
+extern int usb_cpu_stop(void);
+extern int usb_cpu_init_fail(void);
+
+
+static int cc_to_error[16] = {
+
+/* mapping of the OHCI CC status to error codes */
+	/* No  Error  */	       0,
+	/* CRC Error  */	       USB_ST_CRC_ERR,
+	/* Bit Stuff  */	       USB_ST_BIT_ERR,
+	/* Data Togg  */	       USB_ST_CRC_ERR,
+	/* Stall      */	       USB_ST_STALLED,
+	/* DevNotResp */	       -1,
+	/* PIDCheck   */	       USB_ST_BIT_ERR,
+	/* UnExpPID   */	       USB_ST_BIT_ERR,
+	/* DataOver   */	       USB_ST_BUF_ERR,
+	/* DataUnder  */	       USB_ST_BUF_ERR,
+	/* reservd    */	       -1,
+	/* reservd    */	       -1,
+	/* BufferOver */	       USB_ST_BUF_ERR,
+	/* BuffUnder  */	       USB_ST_BUF_ERR,
+	/* Not Access */	       -1,
+	/* Not Access */	       -1
+};
+
+/* ED States */
+
+#define ED_NEW		0x00
+#define ED_UNLINK	0x01
+#define ED_OPER		0x02
+#define ED_DEL		0x04
+#define ED_URB_DEL	0x08
+
+/* usb_ohci_ed */
+struct ed {
+	__u32 hwINFO;
+	__u32 hwTailP;
+	__u32 hwHeadP;
+	__u32 hwNextED;
+
+	struct ed *ed_prev;
+	__u8 int_period;
+	__u8 int_branch;
+	__u8 int_load;
+	__u8 int_interval;
+	__u8 state;
+	__u8 type;
+	__u16 last_iso;
+	struct ed *ed_rm_list;
+
+	struct usb_device *usb_dev;
+	void *purb;
+	__u32 unused[2];
+} __attribute((aligned(16)));
+typedef struct ed ed_t;
+
+
+/* TD info field */
+#define TD_CC	    0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC	    0x0C000000
+#define TD_T	    0x03000000
+#define TD_T_DATA0  0x02000000
+#define TD_T_DATA1  0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R	    0x00040000
+#define TD_DI	    0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP	    0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN    0x00100000
+#define TD_DP_OUT   0x00080000
+
+#define TD_ISO	    0x00010000
+#define TD_DEL	    0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR	   0x00
+#define TD_CC_CRC	   0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL	   0x04
+#define TD_DEVNOTRESP	   0x05
+#define TD_PIDCHECKFAIL	   0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN	   0x08
+#define TD_DATAUNDERRUN	   0x09
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+#define TD_NOTACCESSED	   0x0F
+
+
+#define MAXPSW 1
+
+struct td {
+	__u32 hwINFO;
+	__u32 hwCBP;		/* Current Buffer Pointer */
+	__u32 hwNextTD;		/* Next TD Pointer */
+	__u32 hwBE;		/* Memory Buffer End Pointer */
+
+/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */
+	__u16 hwPSW[MAXPSW];
+/* #endif */
+	__u8 unused;
+	__u8 index;
+	struct ed *ed;
+	struct td *next_dl_td;
+	struct usb_device *usb_dev;
+	int transfer_len;
+	__u32 data;
+
+	__u32 unused2[2];
+} __attribute((aligned(32)));
+typedef struct td td_t;
+
+#define OHCI_ED_SKIP	(1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of.  It must be 256-byte aligned.
+ */
+
+#define NUM_INTS 32	/* part of the OHCI standard */
+struct ohci_hcca {
+	__u32	int_table[NUM_INTS];	/* Interrupt ED table */
+#if defined(CONFIG_MPC5200)
+	__u16	pad1;			/* set to 0 on each frame_no change */
+	__u16	frame_no;		/* current frame number */
+#else
+	__u16	frame_no;		/* current frame number */
+	__u16	pad1;			/* set to 0 on each frame_no change */
+#endif
+	__u32	done_head;		/* info returned for an interrupt */
+	u8		reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+
+/*
+ * Maximum number of root hub ports.
+ */
+#ifndef CFG_USB_OHCI_MAX_ROOT_PORTS
+# error "CFG_USB_OHCI_MAX_ROOT_PORTS undefined!"
+#endif
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region.  This is Memory Mapped I/O.	You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+	/* control and status registers */
+	__u32	revision;
+	__u32	control;
+	__u32	cmdstatus;
+	__u32	intrstatus;
+	__u32	intrenable;
+	__u32	intrdisable;
+	/* memory pointers */
+	__u32	hcca;
+	__u32	ed_periodcurrent;
+	__u32	ed_controlhead;
+	__u32	ed_controlcurrent;
+	__u32	ed_bulkhead;
+	__u32	ed_bulkcurrent;
+	__u32	donehead;
+	/* frame counters */
+	__u32	fminterval;
+	__u32	fmremaining;
+	__u32	fmnumber;
+	__u32	periodicstart;
+	__u32	lsthresh;
+	/* Root hub ports */
+	struct	ohci_roothub_regs {
+		__u32	a;
+		__u32	b;
+		__u32	status;
+		__u32	portstatus[CFG_USB_OHCI_MAX_ROOT_PORTS];
+	} roothub;
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */
+#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */
+#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */
+#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */
+#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */
+#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */
+#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */
+#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */
+#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#	define OHCI_USB_RESET	(0 << 6)
+#	define OHCI_USB_RESUME	(1 << 6)
+#	define OHCI_USB_OPER	(2 << 6)
+#	define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR	(1 << 0)	/* host controller reset */
+#define OHCI_CLF	(1 << 1)	/* control list filled */
+#define OHCI_BLF	(1 << 2)	/* bulk list filled */
+#define OHCI_OCR	(1 << 3)	/* ownership change request */
+#define OHCI_SOC	(3 << 16)	/* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */
+#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */
+#define OHCI_INTR_SF	(1 << 2)	/* start frame */
+#define OHCI_INTR_RD	(1 << 3)	/* resume detect */
+#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */
+#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */
+#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */
+#define OHCI_INTR_OC	(1 << 30)	/* ownership change */
+#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */
+
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+	int devnum; /* Address of Root Hub endpoint */
+	void *dev;  /* was urb */
+	void *int_addr;
+	int send;
+	int interval;
+};
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
+
+/* destination of request */
+#define RH_INTERFACE		   0x01
+#define RH_ENDPOINT		   0x02
+#define RH_OTHER		   0x03
+
+#define RH_CLASS		   0x20
+#define RH_VENDOR		   0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS		0x0080
+#define RH_CLEAR_FEATURE	0x0100
+#define RH_SET_FEATURE		0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR	0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE		0x0280
+#define RH_GET_INTERFACE	0x0A80
+#define RH_SET_INTERFACE	0x0B00
+#define RH_SYNC_FRAME		0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP		0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION	   0x00
+#define RH_PORT_ENABLE		   0x01
+#define RH_PORT_SUSPEND		   0x02
+#define RH_PORT_OVER_CURRENT	   0x03
+#define RH_PORT_RESET		   0x04
+#define RH_PORT_POWER		   0x08
+#define RH_PORT_LOW_SPEED	   0x09
+
+#define RH_C_PORT_CONNECTION	   0x10
+#define RH_C_PORT_ENABLE	   0x11
+#define RH_C_PORT_SUSPEND	   0x12
+#define RH_C_PORT_OVER_CURRENT	   0x13
+#define RH_C_PORT_RESET		   0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER	   0x00
+#define RH_C_HUB_OVER_CURRENT	   0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP	   0x00
+#define RH_ENDPOINT_STALL	   0x01
+
+#define RH_ACK			   0x01
+#define RH_REQ_ERR		   -1
+#define RH_NACK			   0x00
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS	     0x00000001		/* current connect status */
+#define RH_PS_PES	     0x00000002		/* port enable status*/
+#define RH_PS_PSS	     0x00000004		/* port suspend status */
+#define RH_PS_POCI	     0x00000008		/* port over current indicator */
+#define RH_PS_PRS	     0x00000010		/* port reset status */
+#define RH_PS_PPS	     0x00000100		/* port power status */
+#define RH_PS_LSDA	     0x00000200		/* low speed device attached */
+#define RH_PS_CSC	     0x00010000		/* connect status change */
+#define RH_PS_PESC	     0x00020000		/* port enable status change */
+#define RH_PS_PSSC	     0x00040000		/* port suspend status change */
+#define RH_PS_OCIC	     0x00080000		/* over current indicator change */
+#define RH_PS_PRSC	     0x00100000		/* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS	     0x00000001		/* local power status */
+#define RH_HS_OCI	     0x00000002		/* over current indicator */
+#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */
+#define RH_HS_LPSC	     0x00010000		/* local power status change */
+#define RH_HS_OCIC	     0x00020000		/* over current indicator change */
+#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR		0x0000ffff		/* device removable flags */
+#define RH_B_PPCM	0xffff0000		/* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP	(0xff << 0)		/* number of downstream ports */
+#define RH_A_PSM	(1 << 8)		/* power switching mode */
+#define RH_A_NPS	(1 << 9)		/* no power switching */
+#define RH_A_DT		(1 << 10)		/* device type (mbz) */
+#define RH_A_OCPM	(1 << 11)		/* over current protection mode */
+#define RH_A_NOCP	(1 << 12)		/* no over current protection */
+#define RH_A_POTPGT	(0xff << 24)		/* power on to power good time */
+
+/* urb */
+#define N_URB_TD 48
+typedef struct
+{
+	ed_t *ed;
+	__u16 length;	/* number of tds associated with this request */
+	__u16 td_cnt;	/* number of tds already serviced */
+	struct usb_device *dev;
+	int   state;
+	unsigned long pipe;
+	void *transfer_buffer;
+	int transfer_buffer_length;
+	int interval;
+	int actual_length;
+	int finished;
+	td_t *td[N_URB_TD];	/* list pointer to all corresponding TDs associated with this request */
+} urb_priv_t;
+#define URB_DEL 1
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+typedef struct ohci {
+	struct ohci_hcca *hcca;		/* hcca */
+	/*dma_addr_t hcca_dma;*/
+
+	int irq;
+	int disabled;			/* e.g. got a UE, we're hung */
+	int sleeping;
+	unsigned long flags;		/* for HC bugs */
+
+	struct ohci_regs *regs; /* OHCI controller's memory */
+
+	int ohci_int_load[32];	 /* load of the 32 Interrupt Chains (for load balancing)*/
+	ed_t *ed_rm_list[2];	 /* lists of all endpoints to be removed */
+	ed_t *ed_bulktail;	 /* last endpoint of bulk list */
+	ed_t *ed_controltail;	 /* last endpoint of control list */
+	int intrstatus;
+	__u32 hc_control;		/* copy of the hc control reg */
+	struct usb_device *dev[32];
+	struct virt_root_hub rh;
+
+	const char	*slot_name;
+} ohci_t;
+
+#define NUM_EDS 8		/* num of preallocated endpoint descriptors */
+
+struct ohci_device {
+	ed_t	ed[NUM_EDS];
+	int ed_cnt;
+};
+
+/* hcd */
+/* endpoint */
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe,
+		int interval, int load);
+
+/*-------------------------------------------------------------------------*/
+
+/* we need more TDs than EDs */
+#define NUM_TD 64
+
+/* +1 so we can align the storage */
+td_t gtd[NUM_TD+1];
+/* pointers to aligned storage */
+td_t *ptd;
+
+/* TDs ... */
+static inline struct td *
+td_alloc (struct usb_device *usb_dev)
+{
+	int i;
+	struct td	*td;
+
+	td = NULL;
+	for (i = 0; i < NUM_TD; i++)
+	{
+		if (ptd[i].usb_dev == NULL)
+		{
+			td = &ptd[i];
+			td->usb_dev = usb_dev;
+			break;
+		}
+	}
+
+	return td;
+}
+
+static inline void
+ed_free (struct ed *ed)
+{
+	ed->usb_dev = NULL;
+}
diff --git a/drivers/usb/usbdcore.c b/drivers/usb/usbdcore.c
new file mode 100644
index 0000000..308c7ce
--- /dev/null
+++ b/drivers/usb/usbdcore.c
@@ -0,0 +1,684 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * Based on
+ * linux/drivers/usbd/usbd.c.c - USB Device Core Layer
+ *
+ * Copyright (c) 2000, 2001, 2002 Lineo
+ * Copyright (c) 2001 Hewlett Packard
+ *
+ * By:
+ *	Stuart Lynne <sl@lineo.com>,
+ *	Tom Rushworth <tbr@lineo.com>,
+ *	Bruce Balden <balden@lineo.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <malloc.h>
+#include "usbdcore.h"
+
+#define MAX_INTERFACES 2
+
+
+int maxstrings = 20;
+
+/* Global variables ************************************************************************** */
+
+struct usb_string_descriptor **usb_strings;
+
+int usb_devices;
+
+extern struct usb_function_driver ep0_driver;
+
+int registered_functions;
+int registered_devices;
+
+char *usbd_device_events[] = {
+	"DEVICE_UNKNOWN",
+	"DEVICE_INIT",
+	"DEVICE_CREATE",
+	"DEVICE_HUB_CONFIGURED",
+	"DEVICE_RESET",
+	"DEVICE_ADDRESS_ASSIGNED",
+	"DEVICE_CONFIGURED",
+	"DEVICE_SET_INTERFACE",
+	"DEVICE_SET_FEATURE",
+	"DEVICE_CLEAR_FEATURE",
+	"DEVICE_DE_CONFIGURED",
+	"DEVICE_BUS_INACTIVE",
+	"DEVICE_BUS_ACTIVITY",
+	"DEVICE_POWER_INTERRUPTION",
+	"DEVICE_HUB_RESET",
+	"DEVICE_DESTROY",
+	"DEVICE_FUNCTION_PRIVATE",
+};
+
+char *usbd_device_states[] = {
+	"STATE_INIT",
+	"STATE_CREATED",
+	"STATE_ATTACHED",
+	"STATE_POWERED",
+	"STATE_DEFAULT",
+	"STATE_ADDRESSED",
+	"STATE_CONFIGURED",
+	"STATE_UNKNOWN",
+};
+
+char *usbd_device_requests[] = {
+	"GET STATUS",		/* 0 */
+	"CLEAR FEATURE",	/* 1 */
+	"RESERVED",		/* 2 */
+	"SET FEATURE",		/* 3 */
+	"RESERVED",		/* 4 */
+	"SET ADDRESS",		/* 5 */
+	"GET DESCRIPTOR",	/* 6 */
+	"SET DESCRIPTOR",	/* 7 */
+	"GET CONFIGURATION",	/* 8 */
+	"SET CONFIGURATION",	/* 9 */
+	"GET INTERFACE",	/* 10 */
+	"SET INTERFACE",	/* 11 */
+	"SYNC FRAME",		/* 12 */
+};
+
+char *usbd_device_descriptors[] = {
+	"UNKNOWN",		/* 0 */
+	"DEVICE",		/* 1 */
+	"CONFIG",		/* 2 */
+	"STRING",		/* 3 */
+	"INTERFACE",		/* 4 */
+	"ENDPOINT",		/* 5 */
+	"DEVICE QUALIFIER",	/* 6 */
+	"OTHER SPEED",		/* 7 */
+	"INTERFACE POWER",	/* 8 */
+};
+
+char *usbd_device_status[] = {
+	"USBD_OPENING",
+	"USBD_OK",
+	"USBD_SUSPENDED",
+	"USBD_CLOSING",
+};
+
+
+/* Descriptor support functions ************************************************************** */
+
+
+/**
+ * usbd_get_string - find and return a string descriptor
+ * @index: string index to return
+ *
+ * Find an indexed string and return a pointer to a it.
+ */
+struct usb_string_descriptor *usbd_get_string (__u8 index)
+{
+	if (index >= maxstrings) {
+		return NULL;
+	}
+	return usb_strings[index];
+}
+
+
+/* Access to device descriptor functions ***************************************************** */
+
+
+/* *
+ * usbd_device_configuration_instance - find a configuration instance for this device
+ * @device:
+ * @configuration: index to configuration, 0 - N-1
+ *
+ * Get specifed device configuration. Index should be bConfigurationValue-1.
+ */
+static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device,
+		unsigned int port, unsigned int configuration)
+{
+	/* XXX */
+	configuration = configuration ? configuration - 1 : 0;
+
+	if (configuration >= device->configurations) {
+		return NULL;
+	}
+	return device->configuration_instance_array + configuration;
+}
+
+
+/* *
+ * usbd_device_interface_instance
+ * @device:
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ *
+ * Return the specified interface descriptor for the specified device.
+ */
+struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface)
+{
+	struct usb_configuration_instance *configuration_instance;
+
+	if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) {
+		return NULL;
+	}
+	if (interface >= configuration_instance->interfaces) {
+		return NULL;
+	}
+	return configuration_instance->interface_instance_array + interface;
+}
+
+/* *
+ * usbd_device_alternate_descriptor_list
+ * @device:
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: alternate setting
+ *
+ * Return the specified alternate descriptor for the specified device.
+ */
+struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate)
+{
+	struct usb_interface_instance *interface_instance;
+
+	if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) {
+		return NULL;
+	}
+
+	if (alternate >= interface_instance->alternates) {
+		return NULL;
+	}
+
+	return interface_instance->alternates_instance_array + alternate;
+}
+
+
+/* *
+ * usbd_device_device_descriptor
+ * @device: which device
+ * @configuration: index to configuration, 0 - N-1
+ * @port: which port
+ *
+ * Return the specified configuration descriptor for the specified device.
+ */
+struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
+{
+	return (device->device_descriptor);
+}
+
+
+/**
+ * usbd_device_configuration_descriptor
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ *
+ * Return the specified configuration descriptor for the specified device.
+ */
+struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct
+									   usb_device_instance
+									   *device, int port, int configuration)
+{
+	struct usb_configuration_instance *configuration_instance;
+	if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
+		return NULL;
+	}
+	return (configuration_instance->configuration_descriptor);
+}
+
+
+/**
+ * usbd_device_interface_descriptor
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: alternate setting
+ *
+ * Return the specified interface descriptor for the specified device.
+ */
+struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance
+								   *device, int port, int configuration, int interface, int alternate)
+{
+	struct usb_interface_instance *interface_instance;
+	if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
+		return NULL;
+	}
+	if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
+		return NULL;
+	}
+	return (interface_instance->alternates_instance_array[alternate].interface_descriptor);
+}
+
+/**
+ * usbd_device_endpoint_descriptor_index
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: index setting
+ * @index: which index
+ *
+ * Return the specified endpoint descriptor for the specified device.
+ */
+struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance
+								       *device, int port, int configuration, int interface, int alternate, int index)
+{
+	struct usb_alternate_instance *alternate_instance;
+
+	if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
+		return NULL;
+	}
+	if (index >= alternate_instance->endpoints) {
+		return NULL;
+	}
+	return *(alternate_instance->endpoints_descriptor_array + index);
+}
+
+
+/**
+ * usbd_device_endpoint_transfersize
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @index: which index
+ *
+ * Return the specified endpoint transfer size;
+ */
+int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index)
+{
+	struct usb_alternate_instance *alternate_instance;
+
+	if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
+		return 0;
+	}
+	if (index >= alternate_instance->endpoints) {
+		return 0;
+	}
+	return *(alternate_instance->endpoint_transfersize_array + index);
+}
+
+
+/**
+ * usbd_device_endpoint_descriptor
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: alternate setting
+ * @endpoint: which endpoint
+ *
+ * Return the specified endpoint descriptor for the specified device.
+ */
+struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint)
+{
+	struct usb_endpoint_descriptor *endpoint_descriptor;
+	int i;
+
+	for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) {
+		if (endpoint_descriptor->bEndpointAddress == endpoint) {
+			return endpoint_descriptor;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * usbd_endpoint_halted
+ * @device: point to struct usb_device_instance
+ * @endpoint: endpoint to check
+ *
+ * Return non-zero if endpoint is halted.
+ */
+int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint)
+{
+	return (device->status == USB_STATUS_HALT);
+}
+
+
+/**
+ * usbd_rcv_complete - complete a receive
+ * @endpoint:
+ * @len:
+ * @urb_bad:
+ *
+ * Called from rcv interrupt to complete.
+ */
+void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad)
+{
+	if (endpoint) {
+		struct urb *rcv_urb;
+
+		/*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */
+
+		/* if we had an urb then update actual_length, dispatch if neccessary */
+		if ((rcv_urb = endpoint->rcv_urb)) {
+
+			/*usbdbg("actual: %d buffer: %d\n", */
+			/*rcv_urb->actual_length, rcv_urb->buffer_length); */
+
+			/* check the urb is ok, are we adding data less than the packetsize */
+			if (!urb_bad && (len <= endpoint->rcv_packetSize)) {
+			  /*usbdbg("updating actual_length by %d\n",len); */
+
+				/* increment the received data size */
+				rcv_urb->actual_length += len;
+
+			} else {
+				usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n",
+				       rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad);
+
+				rcv_urb->actual_length = 0;
+				rcv_urb->status = RECV_ERROR;
+			}
+		} else {
+			usberr("no rcv_urb!");
+		}
+	} else {
+		usberr("no endpoint!");
+	}
+
+}
+
+/**
+ * usbd_tx_complete - complete a transmit
+ * @endpoint:
+ * @resetart:
+ *
+ * Called from tx interrupt to complete.
+ */
+void usbd_tx_complete (struct usb_endpoint_instance *endpoint)
+{
+	if (endpoint) {
+		struct urb *tx_urb;
+
+		/* if we have a tx_urb advance or reset, finish if complete */
+		if ((tx_urb = endpoint->tx_urb)) {
+			int sent = endpoint->last;
+			endpoint->sent += sent;
+			endpoint->last -= sent;
+
+			if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) {
+				tx_urb->actual_length = 0;
+				endpoint->sent = 0;
+				endpoint->last = 0;
+
+				/* Remove from active, save for re-use */
+				urb_detach(tx_urb);
+				urb_append(&endpoint->done, tx_urb);
+				/*usbdbg("done->next %p, tx_urb %p, done %p", */
+				/*	 endpoint->done.next, tx_urb, &endpoint->done); */
+
+				endpoint->tx_urb = first_urb_detached(&endpoint->tx);
+				if( endpoint->tx_urb ) {
+					endpoint->tx_queue--;
+					usbdbg("got urb from tx list");
+				}
+				if( !endpoint->tx_urb ) {
+					/*usbdbg("taking urb from done list"); */
+					endpoint->tx_urb = first_urb_detached(&endpoint->done);
+				}
+				if( !endpoint->tx_urb ) {
+					usbdbg("allocating new urb for tx_urb");
+					endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint);
+				}
+			}
+		}
+	}
+}
+
+/* URB linked list functions ***************************************************** */
+
+/*
+ * Initialize an urb_link to be a single element list.
+ * If the urb_link is being used as a distinguished list head
+ * the list is empty when the head is the only link in the list.
+ */
+void urb_link_init (urb_link * ul)
+{
+	if (ul) {
+		ul->prev = ul->next = ul;
+	}
+}
+
+/*
+ * Detach an urb_link from a list, and set it
+ * up as a single element list, so no dangling
+ * pointers can be followed, and so it can be
+ * joined to another list if so desired.
+ */
+void urb_detach (struct urb *urb)
+{
+	if (urb) {
+		urb_link *ul = &urb->link;
+		ul->next->prev = ul->prev;
+		ul->prev->next = ul->next;
+		urb_link_init (ul);
+	}
+}
+
+/*
+ * Return the first urb_link in a list with a distinguished
+ * head "hd", or NULL if the list is empty.  This will also
+ * work as a predicate, returning NULL if empty, and non-NULL
+ * otherwise.
+ */
+urb_link *first_urb_link (urb_link * hd)
+{
+	urb_link *nx;
+	if (NULL != hd && NULL != (nx = hd->next) && nx != hd) {
+		/* There is at least one element in the list */
+		/* (besides the distinguished head). */
+		return (nx);
+	}
+	/* The list is empty */
+	return (NULL);
+}
+
+/*
+ * Return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ */
+struct urb *first_urb (urb_link * hd)
+{
+	urb_link *nx;
+	if (NULL == (nx = first_urb_link (hd))) {
+		/* The list is empty */
+		return (NULL);
+	}
+	return (p2surround (struct urb, link, nx));
+}
+
+/*
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ */
+struct urb *first_urb_detached (urb_link * hd)
+{
+	struct urb *urb;
+	if ((urb = first_urb (hd))) {
+		urb_detach (urb);
+	}
+	return urb;
+}
+
+
+/*
+ * Append an urb_link (or a whole list of
+ * urb_links) to the tail of another list
+ * of urb_links.
+ */
+void urb_append (urb_link * hd, struct urb *urb)
+{
+	if (hd && urb) {
+		urb_link *new = &urb->link;
+
+		/* This allows the new urb to be a list of urbs, */
+		/* with new pointing at the first, but the link */
+		/* must be initialized. */
+		/* Order is important here... */
+		urb_link *pul = hd->prev;
+		new->prev->next = hd;
+		hd->prev = new->prev;
+		new->prev = pul;
+		pul->next = new;
+	}
+}
+
+/* URB create/destroy functions ***************************************************** */
+
+/**
+ * usbd_alloc_urb - allocate an URB appropriate for specified endpoint
+ * @device: device instance
+ * @endpoint: endpoint
+ *
+ * Allocate an urb structure. The usb device urb structure is used to
+ * contain all data associated with a transfer, including a setup packet for
+ * control transfers.
+ *
+ * NOTE: endpoint_address MUST contain a direction flag.
+ */
+struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint)
+{
+	struct urb *urb;
+
+	if( !(urb = (struct urb*)malloc(sizeof(struct urb))) ) {
+	  usberr(" F A T A L:  malloc(%u) FAILED!!!!", sizeof(struct urb));
+	  return NULL;
+	}
+
+	/* Fill in known fields */
+	memset(urb, 0, sizeof(struct urb));
+	urb->endpoint = endpoint;
+	urb->device = device;
+	urb->buffer = (u8*)urb->buffer_data;
+	urb->buffer_length = sizeof(urb->buffer_data);
+
+	urb_link_init (&urb->link);
+
+	return urb;
+}
+
+/**
+ * usbd_dealloc_urb - deallocate an URB and associated buffer
+ * @urb: pointer to an urb structure
+ *
+ * Deallocate an urb structure and associated data.
+ */
+void usbd_dealloc_urb (struct urb *urb)
+{
+	if (urb) {
+		free (urb);
+	}
+}
+
+/* Event signaling functions ***************************************************** */
+
+/**
+ * usbd_device_event - called to respond to various usb events
+ * @device: pointer to struct device
+ * @event: event to respond to
+ *
+ * Used by a Bus driver to indicate an event.
+ */
+void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data)
+{
+	usb_device_state_t state;
+
+	if (!device || !device->bus) {
+		usberr("(%p,%d) NULL device or device->bus", device, event);
+		return;
+	}
+
+	state = device->device_state;
+
+	usbinfo("%s", usbd_device_events[event]);
+
+	switch (event) {
+	case DEVICE_UNKNOWN:
+		break;
+	case DEVICE_INIT:
+		device->device_state = STATE_INIT;
+		break;
+
+	case DEVICE_CREATE:
+		device->device_state = STATE_ATTACHED;
+		break;
+
+	case DEVICE_HUB_CONFIGURED:
+		device->device_state = STATE_POWERED;
+		break;
+
+	case DEVICE_RESET:
+		device->device_state = STATE_DEFAULT;
+		device->address = 0;
+		break;
+
+	case DEVICE_ADDRESS_ASSIGNED:
+		device->device_state = STATE_ADDRESSED;
+		break;
+
+	case DEVICE_CONFIGURED:
+		device->device_state = STATE_CONFIGURED;
+		break;
+
+	case DEVICE_DE_CONFIGURED:
+		device->device_state = STATE_ADDRESSED;
+		break;
+
+	case DEVICE_BUS_INACTIVE:
+		if (device->status != USBD_CLOSING) {
+			device->status = USBD_SUSPENDED;
+		}
+		break;
+	case DEVICE_BUS_ACTIVITY:
+		if (device->status != USBD_CLOSING) {
+			device->status = USBD_OK;
+		}
+		break;
+
+	case DEVICE_SET_INTERFACE:
+		break;
+	case DEVICE_SET_FEATURE:
+		break;
+	case DEVICE_CLEAR_FEATURE:
+		break;
+
+	case DEVICE_POWER_INTERRUPTION:
+		device->device_state = STATE_POWERED;
+		break;
+	case DEVICE_HUB_RESET:
+		device->device_state = STATE_ATTACHED;
+		break;
+	case DEVICE_DESTROY:
+		device->device_state = STATE_UNKNOWN;
+		break;
+
+	case DEVICE_FUNCTION_PRIVATE:
+		break;
+
+	default:
+		usbdbg("event %d - not handled",event);
+		break;
+	}
+	/*usbdbg("%s event: %d oldstate: %d newstate: %d status: %d address: %d",
+		device->name, event, state,
+		device->device_state, device->status, device->address); */
+
+	/* tell the bus interface driver */
+	if( device->event ) {
+		/* usbdbg("calling device->event"); */
+		device->event(device, event, data);
+	}
+}
diff --git a/drivers/usb/usbdcore_ep0.c b/drivers/usb/usbdcore_ep0.c
new file mode 100644
index 0000000..1e44f32
--- /dev/null
+++ b/drivers/usb/usbdcore_ep0.c
@@ -0,0 +1,607 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, deckard@CodeHermit.ie
+ *
+ * Based on
+ * linux/drivers/usbd/ep0.c
+ *
+ * Copyright (c) 2000, 2001, 2002 Lineo
+ * Copyright (c) 2001 Hewlett Packard
+ *
+ * By:
+ *	Stuart Lynne <sl@lineo.com>,
+ *	Tom Rushworth <tbr@lineo.com>,
+ *	Bruce Balden <balden@lineo.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This is the builtin ep0 control function. It implements all required functionality
+ * for responding to control requests (SETUP packets).
+ *
+ * XXX
+ *
+ * Currently we do not pass any SETUP packets (or other) to the configured
+ * function driver. This may need to change.
+ *
+ * XXX
+ *
+ * As alluded to above, a simple callback cdc_recv_setup has been implemented
+ * in the usb_device data structure to facilicate passing
+ * Common Device Class packets to a function driver.
+ *
+ * XXX
+ */
+
+#include <common.h>
+
+#if defined(CONFIG_USB_DEVICE)
+#include "usbdcore.h"
+
+#if 0
+#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
+#else
+#define dbg_ep0(lvl,fmt,args...)
+#endif
+
+/* EP0 Configuration Set ********************************************************************* */
+
+
+/**
+ * ep0_get_status - fill in URB data with appropriate status
+ * @device:
+ * @urb:
+ * @index:
+ * @requesttype:
+ *
+ */
+static int ep0_get_status (struct usb_device_instance *device,
+			   struct urb *urb, int index, int requesttype)
+{
+	char *cp;
+
+	urb->actual_length = 2;
+	cp = (char*)urb->buffer;
+	cp[0] = cp[1] = 0;
+
+	switch (requesttype) {
+	case USB_REQ_RECIPIENT_DEVICE:
+		cp[0] = USB_STATUS_SELFPOWERED;
+		break;
+	case USB_REQ_RECIPIENT_INTERFACE:
+		break;
+	case USB_REQ_RECIPIENT_ENDPOINT:
+		cp[0] = usbd_endpoint_halted (device, index);
+		break;
+	case USB_REQ_RECIPIENT_OTHER:
+		urb->actual_length = 0;
+	default:
+		break;
+	}
+	dbg_ep0 (2, "%02x %02x", cp[0], cp[1]);
+	return 0;
+}
+
+/**
+ * ep0_get_one
+ * @device:
+ * @urb:
+ * @result:
+ *
+ * Set a single byte value in the urb send buffer. Return non-zero to signal
+ * a request error.
+ */
+static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
+			__u8 result)
+{
+	urb->actual_length = 1;	/* XXX 2? */
+	((char *) urb->buffer)[0] = result;
+	return 0;
+}
+
+/**
+ * copy_config
+ * @urb: pointer to urb
+ * @data: pointer to configuration data
+ * @length: length of data
+ *
+ * Copy configuration data to urb transfer buffer if there is room for it.
+ */
+void copy_config (struct urb *urb, void *data, int max_length,
+			 int max_buf)
+{
+	int available;
+	int length;
+
+	/*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */
+	/*        urb->actual_length, urb->buffer_length, max_buf, max_length, data); */
+
+	if (!data) {
+		dbg_ep0 (1, "data is NULL");
+		return;
+	}
+	length = max_length;
+
+	if (length > max_length) {
+		dbg_ep0 (1, "length: %d >= max_length: %d", length,
+			 max_length);
+		return;
+	}
+	/*dbg_ep0(1, "   actual: %d buf: %d max_buf: %d max_length: %d length: %d", */
+	/*        urb->actual_length, urb->buffer_length, max_buf, max_length, length); */
+
+	if ((available =
+	     /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) {
+		return;
+	}
+	/*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
+	/*        urb->actual_length, urb->buffer_length, max_buf, length, available); */
+
+	if (length > available) {
+		length = available;
+	}
+	/*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
+	/*        urb->actual_length, urb->buffer_length, max_buf, length, available); */
+
+	memcpy (urb->buffer + urb->actual_length, data, length);
+	urb->actual_length += length;
+
+	dbg_ep0 (3,
+		 "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d",
+		 urb->actual_length, urb->buffer_length, max_buf, max_length,
+		 available);
+}
+
+/**
+ * ep0_get_descriptor
+ * @device:
+ * @urb:
+ * @max:
+ * @descriptor_type:
+ * @index:
+ *
+ * Called by ep0_rx_process for a get descriptor device command. Determine what
+ * descriptor is being requested, copy to send buffer. Return zero if ok to send,
+ * return non-zero to signal a request error.
+ */
+static int ep0_get_descriptor (struct usb_device_instance *device,
+			       struct urb *urb, int max, int descriptor_type,
+			       int index)
+{
+	int port = 0;		/* XXX compound device */
+	char *cp;
+
+	/*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */
+
+	if (!urb || !urb->buffer || !urb->buffer_length
+	    || (urb->buffer_length < 255)) {
+		dbg_ep0 (2, "invalid urb %p", urb);
+		return -1L;
+	}
+
+	/* setup tx urb */
+	urb->actual_length = 0;
+	cp = (char*)urb->buffer;
+
+	dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
+
+	switch (descriptor_type) {
+	case USB_DESCRIPTOR_TYPE_DEVICE:
+		{
+			struct usb_device_descriptor *device_descriptor;
+			if (!
+			    (device_descriptor =
+			     usbd_device_device_descriptor (device, port))) {
+				return -1;
+			}
+			/* copy descriptor for this device */
+			copy_config (urb, device_descriptor,
+				     sizeof (struct usb_device_descriptor),
+				     max);
+
+			/* correct the correct control endpoint 0 max packet size into the descriptor */
+			device_descriptor =
+				(struct usb_device_descriptor *) urb->buffer;
+
+		}
+		dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
+		break;
+
+	case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+		{
+			struct usb_configuration_descriptor
+				*configuration_descriptor;
+			struct usb_device_descriptor *device_descriptor;
+			if (!
+			    (device_descriptor =
+			     usbd_device_device_descriptor (device, port))) {
+				return -1;
+			}
+			/*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */
+			if (index > device_descriptor->bNumConfigurations) {
+				dbg_ep0 (0, "index too large: %d > %d", index,
+					 device_descriptor->
+					 bNumConfigurations);
+				return -1;
+			}
+
+			if (!
+			    (configuration_descriptor =
+			     usbd_device_configuration_descriptor (device,
+								   port,
+								   index))) {
+				dbg_ep0 (0,
+					 "usbd_device_configuration_descriptor failed: %d",
+					 index);
+				return -1;
+			}
+			dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
+			copy_config (urb, configuration_descriptor,
+
+					cpu_to_le16(configuration_descriptor->wTotalLength),
+				     max);
+		}
+
+		break;
+
+	case USB_DESCRIPTOR_TYPE_STRING:
+		{
+			struct usb_string_descriptor *string_descriptor;
+			if (!(string_descriptor = usbd_get_string (index))) {
+				serial_printf("Invalid string index %d\n", index);
+				return -1;
+			}
+			dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
+			copy_config (urb, string_descriptor, string_descriptor->bLength, max);
+		}
+		break;
+	case USB_DESCRIPTOR_TYPE_INTERFACE:
+	serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
+		return -1;
+	case USB_DESCRIPTOR_TYPE_ENDPOINT:
+		serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
+		return -1;
+	case USB_DESCRIPTOR_TYPE_HID:
+		{
+			serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
+			return -1;	/* unsupported at this time */
+#if 0
+			int bNumInterface =
+				le16_to_cpu (urb->device_request.wIndex);
+			int bAlternateSetting = 0;
+			int class = 0;
+			struct usb_class_descriptor *class_descriptor;
+
+			if (!(class_descriptor =
+			      usbd_device_class_descriptor_index (device,
+								  port, 0,
+								  bNumInterface,
+								  bAlternateSetting,
+								  class))
+			    || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) {
+				dbg_ep0 (3, "[%d] interface is not HID",
+					 bNumInterface);
+				return -1;
+			}
+			/* copy descriptor for this class */
+			copy_config (urb, class_descriptor,
+				     class_descriptor->descriptor.hid.bLength,
+				     max);
+#endif
+		}
+		break;
+	case USB_DESCRIPTOR_TYPE_REPORT:
+		{
+			serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
+			return -1;	/* unsupported at this time */
+#if 0
+			int bNumInterface =
+				le16_to_cpu (urb->device_request.wIndex);
+			int bAlternateSetting = 0;
+			int class = 0;
+			struct usb_class_report_descriptor *report_descriptor;
+
+			if (!(report_descriptor =
+			      usbd_device_class_report_descriptor_index
+			      (device, port, 0, bNumInterface,
+			       bAlternateSetting, class))
+			    || report_descriptor->bDescriptorType !=
+			    USB_DT_REPORT) {
+				dbg_ep0 (3, "[%d] descriptor is not REPORT",
+					 bNumInterface);
+				return -1;
+			}
+			/* copy report descriptor for this class */
+			/*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */
+			if (max - urb->actual_length > 0) {
+				int length =
+					MIN (report_descriptor->wLength,
+					     max - urb->actual_length);
+				memcpy (urb->buffer + urb->actual_length,
+					&report_descriptor->bData[0], length);
+				urb->actual_length += length;
+			}
+#endif
+		}
+		break;
+	case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
+		{
+			/* If a USB device supports both a full speed and low speed operation
+			 * we must send a Device_Qualifier descriptor here
+			 */
+			return -1;
+		}
+	default:
+		return -1;
+	}
+
+
+	dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
+		 urb->buffer, urb->buffer_length, urb->actual_length,
+		 device->bus->endpoint_array[0].tx_packetSize);
+/*
+    if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) {
+	dbg_ep0(0, "adding null byte");
+	urb->buffer[urb->actual_length++] = 0;
+	dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d",
+		urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize);
+    }
+*/
+	return 0;
+
+}
+
+/**
+ * ep0_recv_setup - called to indicate URB has been received
+ * @urb: pointer to struct urb
+ *
+ * Check if this is a setup packet, process the device request, put results
+ * back into the urb and return zero or non-zero to indicate success (DATA)
+ * or failure (STALL).
+ *
+ */
+int ep0_recv_setup (struct urb *urb)
+{
+	/*struct usb_device_request *request = urb->buffer; */
+	/*struct usb_device_instance *device = urb->device; */
+
+	struct usb_device_request *request;
+	struct usb_device_instance *device;
+	int address;
+
+	dbg_ep0 (0, "entering ep0_recv_setup()");
+	if (!urb || !urb->device) {
+		dbg_ep0 (3, "invalid URB %p", urb);
+		return -1;
+	}
+
+	request = &urb->device_request;
+	device = urb->device;
+
+	dbg_ep0 (3, "urb: %p device: %p", urb, urb->device);
+
+
+	/*dbg_ep0(2, "-       -       -       -       -       -       -       -       -       -"); */
+
+	dbg_ep0 (2,
+		 "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s",
+		 request->bmRequestType, request->bRequest,
+		 le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex),
+		 le16_to_cpu (request->wLength),
+		 USBD_DEVICE_REQUESTS (request->bRequest));
+
+	/* handle USB Standard Request (c.f. USB Spec table 9-2) */
+	if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
+		if(device->device_state <= STATE_CONFIGURED){
+			/*	Attempt to handle a CDC specific request if we are
+			 *	in the configured state.
+			 */
+			return device->cdc_recv_setup(request,urb);
+		}
+		dbg_ep0 (1, "non standard request: %x",
+			 request->bmRequestType & USB_REQ_TYPE_MASK);
+		return -1;	/* Stall here */
+	}
+
+	switch (device->device_state) {
+	case STATE_CREATED:
+	case STATE_ATTACHED:
+	case STATE_POWERED:
+		/* It actually is important to allow requests in these states,
+		 * Windows will request descriptors before assigning an
+		 * address to the client.
+		 */
+
+		/*dbg_ep0 (1, "request %s not allowed in this state: %s", */
+		/*                USBD_DEVICE_REQUESTS(request->bRequest), */
+		/*                usbd_device_states[device->device_state]); */
+		/*return -1; */
+		break;
+
+	case STATE_INIT:
+	case STATE_DEFAULT:
+		switch (request->bRequest) {
+		case USB_REQ_GET_STATUS:
+		case USB_REQ_GET_INTERFACE:
+		case USB_REQ_SYNCH_FRAME:	/* XXX should never see this (?) */
+		case USB_REQ_CLEAR_FEATURE:
+		case USB_REQ_SET_FEATURE:
+		case USB_REQ_SET_DESCRIPTOR:
+			/* case USB_REQ_SET_CONFIGURATION: */
+		case USB_REQ_SET_INTERFACE:
+			dbg_ep0 (1,
+				 "request %s not allowed in DEFAULT state: %s",
+				 USBD_DEVICE_REQUESTS (request->bRequest),
+				 usbd_device_states[device->device_state]);
+			return -1;
+
+		case USB_REQ_SET_CONFIGURATION:
+		case USB_REQ_SET_ADDRESS:
+		case USB_REQ_GET_DESCRIPTOR:
+		case USB_REQ_GET_CONFIGURATION:
+			break;
+		}
+	case STATE_ADDRESSED:
+	case STATE_CONFIGURED:
+		break;
+	case STATE_UNKNOWN:
+		dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s",
+			 USBD_DEVICE_REQUESTS (request->bRequest),
+			 usbd_device_states[device->device_state]);
+		return -1;
+	}
+
+	/* handle all requests that return data (direction bit set on bm RequestType) */
+	if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
+
+		dbg_ep0 (3, "Device-to-Host");
+
+		switch (request->bRequest) {
+
+		case USB_REQ_GET_STATUS:
+			return ep0_get_status (device, urb, request->wIndex,
+					       request->bmRequestType &
+					       USB_REQ_RECIPIENT_MASK);
+
+		case USB_REQ_GET_DESCRIPTOR:
+			return ep0_get_descriptor (device, urb,
+						   le16_to_cpu (request->wLength),
+						   le16_to_cpu (request->wValue) >> 8,
+						   le16_to_cpu (request->wValue) & 0xff);
+
+		case USB_REQ_GET_CONFIGURATION:
+			serial_printf("get config %d\n", device->configuration);
+			return ep0_get_one (device, urb,
+					    device->configuration);
+
+		case USB_REQ_GET_INTERFACE:
+			return ep0_get_one (device, urb, device->alternate);
+
+		case USB_REQ_SYNCH_FRAME:	/* XXX should never see this (?) */
+			return -1;
+
+		case USB_REQ_CLEAR_FEATURE:
+		case USB_REQ_SET_FEATURE:
+		case USB_REQ_SET_ADDRESS:
+		case USB_REQ_SET_DESCRIPTOR:
+		case USB_REQ_SET_CONFIGURATION:
+		case USB_REQ_SET_INTERFACE:
+			return -1;
+		}
+	}
+	/* handle the requests that do not return data */
+	else {
+
+
+		/*dbg_ep0(3, "Host-to-Device"); */
+		switch (request->bRequest) {
+
+		case USB_REQ_CLEAR_FEATURE:
+		case USB_REQ_SET_FEATURE:
+			dbg_ep0 (0, "Host-to-Device");
+			switch (request->
+				bmRequestType & USB_REQ_RECIPIENT_MASK) {
+			case USB_REQ_RECIPIENT_DEVICE:
+				/* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */
+				/* XXX fall through for now as we do not support either */
+			case USB_REQ_RECIPIENT_INTERFACE:
+			case USB_REQ_RECIPIENT_OTHER:
+				dbg_ep0 (0, "request %s not",
+					 USBD_DEVICE_REQUESTS (request->bRequest));
+			default:
+				return -1;
+
+			case USB_REQ_RECIPIENT_ENDPOINT:
+				dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue));
+				if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) {
+					/*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */
+					/*                    request->bRequest == USB_REQ_SET_FEATURE); */
+					/* NEED TO IMPLEMENT THIS!!! */
+					return -1;
+				} else {
+					dbg_ep0 (1, "request %s bad wValue: %04x",
+						 USBD_DEVICE_REQUESTS
+						 (request->bRequest),
+						 le16_to_cpu (request->wValue));
+					return -1;
+				}
+			}
+
+		case USB_REQ_SET_ADDRESS:
+			/* check if this is a re-address, reset first if it is (this shouldn't be possible) */
+			if (device->device_state != STATE_DEFAULT) {
+				dbg_ep0 (1, "set_address: %02x state: %s",
+					 le16_to_cpu (request->wValue),
+					 usbd_device_states[device->device_state]);
+				return -1;
+			}
+			address = le16_to_cpu (request->wValue);
+			if ((address & 0x7f) != address) {
+				dbg_ep0 (1, "invalid address %04x %04x",
+					 address, address & 0x7f);
+				return -1;
+			}
+			device->address = address;
+
+			/*dbg_ep0(2, "address: %d %d %d", */
+			/*        request->wValue, le16_to_cpu(request->wValue), device->address); */
+
+			return 0;
+
+		case USB_REQ_SET_DESCRIPTOR:	/* XXX should we support this? */
+			dbg_ep0 (0, "set descriptor: NOT SUPPORTED");
+			return -1;
+
+		case USB_REQ_SET_CONFIGURATION:
+			/* c.f. 9.4.7 - the top half of wValue is reserved */
+			/* */
+			if ((device->configuration =
+				le16_to_cpu (request->wValue) & 0xFF80) != 0) {
+				/* c.f. 9.4.7 - zero is the default or addressed state, in our case this */
+				/* is the same is configuration zero */
+				serial_printf("error setting dev->config to zero!\n");
+				device->configuration = 0;	/* TBR - ?????? */
+			}
+			/* reset interface and alternate settings */
+			device->interface = device->alternate = 0;
+
+			/*dbg_ep0(2, "set configuration: %d", device->configuration); */
+			/*serial_printf("DEVICE_CONFIGURED.. event?\n"); */
+			return 0;
+
+		case USB_REQ_SET_INTERFACE:
+			device->interface = le16_to_cpu (request->wIndex);
+			device->alternate = le16_to_cpu (request->wValue);
+			/*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */
+			serial_printf ("DEVICE_SET_INTERFACE.. event?\n");
+			return 0;
+
+		case USB_REQ_GET_STATUS:
+		case USB_REQ_GET_DESCRIPTOR:
+		case USB_REQ_GET_CONFIGURATION:
+		case USB_REQ_GET_INTERFACE:
+		case USB_REQ_SYNCH_FRAME:	/* XXX should never see this (?) */
+			return -1;
+		}
+	}
+	return -1;
+}
+
+#endif
diff --git a/drivers/usb/usbdcore_mpc8xx.c b/drivers/usb/usbdcore_mpc8xx.c
new file mode 100644
index 0000000..d4c4096
--- /dev/null
+++ b/drivers/usb/usbdcore_mpc8xx.c
@@ -0,0 +1,1401 @@
+/*
+ * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit
+ * bodonoghue@CodeHermit.ie
+ *
+ * References
+ * DasUBoot/drivers/usbdcore_omap1510.c, for design and implementation ideas.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Notes :
+ * 1.	#define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX
+ *		packet to force the USB re-transmit protocol.
+ *
+ * 2.	#define __DEBUG_UDC__ to switch on debug tracing to serial console
+ *	be careful that tracing doesn't create Hiesen-bugs with respect to
+ *	response timeouts to control requests.
+ *
+ * 3.	This driver should be able to support any higher level driver that
+ *	that wants to do either of the two standard UDC implementations
+ *	Control-Bulk-Interrupt or  Bulk-IN/Bulk-Out standards. Hence
+ *	gserial and cdc_acm should work with this code.
+ *
+ * 4.	NAK events never actually get raised at all, the documentation
+ *	is just wrong !
+ *
+ * 5.	For some reason, cbd_datlen is *always* +2 the value it should be.
+ *	this means that having an RX cbd of 16 bytes is not possible, since
+ *	the same size is reported for 14 bytes received as 16 bytes received
+ *	until we can find out why this happens, RX cbds must be limited to 8
+ *	bytes. TODO: check errata for this behaviour.
+ *
+ * 6.	Right now this code doesn't support properly powering up with the USB
+ *	cable attached to the USB host my development board the Adder87x doesn't
+ *	have a pull-up fitted to allow this, so it is necessary to power the
+ *	board and *then* attached the USB cable to the host. However somebody
+ *	with a different design in their board may be able to keep the cable
+ *	constantly connected and simply enable/disable a pull-up  re
+ *	figure 31.1 in MPC885RM.pdf instead of having to power up the board and
+ *	then attach the cable !
+ *
+ */
+#include <common.h>
+#include <config.h>
+
+#if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE)
+#include <commproc.h>
+#include "usbdcore.h"
+#include "usbdcore_mpc8xx.h"
+#include "usbdcore_ep0.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ERR(fmt, args...)\
+	serial_printf("ERROR : [%s] %s:%d: "fmt,\
+				__FILE__,__FUNCTION__,__LINE__, ##args)
+#ifdef __DEBUG_UDC__
+#define DBG(fmt,args...)\
+		serial_printf("[%s] %s:%d: "fmt,\
+				__FILE__,__FUNCTION__,__LINE__, ##args)
+#else
+#define DBG(fmt,args...)
+#endif
+
+/* Static Data */
+#ifdef __SIMULATE_ERROR__
+static char err_poison_test = 0;
+#endif
+static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS];
+static u32 address_base = STATE_NOT_READY;
+static mpc8xx_udc_state_t udc_state = 0;
+static struct usb_device_instance *udc_device = 0;
+static volatile usb_epb_t *endpoints[MAX_ENDPOINTS];
+static volatile cbd_t *tx_cbd[TX_RING_SIZE];
+static volatile cbd_t *rx_cbd[RX_RING_SIZE];
+static volatile immap_t *immr = 0;
+static volatile cpm8xx_t *cp = 0;
+static volatile usb_pram_t *usb_paramp = 0;
+static volatile usb_t *usbp = 0;
+static int rx_ct = 0;
+static int tx_ct = 0;
+
+/* Static Function Declarations */
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+					    usb_device_state_t final);
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+					      usb_device_state_t final);
+static void mpc8xx_udc_stall (unsigned int ep);
+static void mpc8xx_udc_flush_tx_fifo (int epid);
+static void mpc8xx_udc_flush_rx_fifo (void);
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+				struct urb *tx_urb);
+static void mpc8xx_udc_dump_request (struct usb_device_request *request);
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+				   volatile cpm8xx_t * cp);
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi);
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_cbd_init (void);
+static void mpc8xx_udc_endpoint_init (void);
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size);
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment);
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_set_nak (unsigned int ep);
+static short mpc8xx_udc_handle_txerr (void);
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid);
+
+/******************************************************************************
+			       Global Linkage
+ *****************************************************************************/
+
+/* udc_init
+ *
+ * Do initial bus gluing
+ */
+int udc_init (void)
+{
+	/* Init various pointers */
+	immr = (immap_t *) CFG_IMMR;
+	cp = (cpm8xx_t *) & (immr->im_cpm);
+	usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]);
+	usbp = (usb_t *) & (cp->cp_scc[0]);
+
+	memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS));
+
+	udc_device = 0;
+	udc_state = STATE_NOT_READY;
+
+	usbp->usmod = 0x00;
+	usbp->uscom = 0;
+
+	/* Set USB Frame #0, Respond at Address & Get a clock source  */
+	usbp->usaddr = 0x00;
+	mpc8xx_udc_clock_init (immr, cp);
+
+	/* PA15, PA14 as perhiperal USBRXD and USBOE */
+	immr->im_ioport.iop_padir &= ~0x0003;
+	immr->im_ioport.iop_papar |= 0x0003;
+
+	/* PC11/PC10 as peripheral USBRXP USBRXN */
+	immr->im_ioport.iop_pcso |= 0x0030;
+
+	/* PC7/PC6 as perhiperal USBTXP and USBTXN */
+	immr->im_ioport.iop_pcdir |= 0x0300;
+	immr->im_ioport.iop_pcpar |= 0x0300;
+
+	/* Set the base address */
+	address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE);
+
+	/* Initialise endpoints and circular buffers */
+	mpc8xx_udc_endpoint_init ();
+	mpc8xx_udc_cbd_init ();
+
+	/* Assign allocated Dual Port Endpoint descriptors */
+	usb_paramp->ep0ptr = (u32) endpoints[0];
+	usb_paramp->ep1ptr = (u32) endpoints[1];
+	usb_paramp->ep2ptr = (u32) endpoints[2];
+	usb_paramp->ep3ptr = (u32) endpoints[3];
+	usb_paramp->frame_n = 0;
+
+	DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n",
+	     usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr,
+	     usb_paramp->ep3ptr);
+
+	return 0;
+}
+
+/* udc_irq
+ *
+ * Poll for whatever events may have occured
+ */
+void udc_irq (void)
+{
+	int epid = 0;
+	volatile cbd_t *rx_cbdp = 0;
+	volatile cbd_t *rx_cbdp_base = 0;
+
+	if (udc_state != STATE_READY) {
+		return;
+	}
+
+	if (usbp->usber & USB_E_BSY) {
+		/* This shouldn't happen. If it does then it's a bug ! */
+		usbp->usber |= USB_E_BSY;
+		mpc8xx_udc_flush_rx_fifo ();
+	}
+
+	/* Scan all RX/Bidirectional Endpoints for RX data. */
+	for (epid = 0; epid < MAX_ENDPOINTS; epid++) {
+		if (!ep_ref[epid].prx) {
+			continue;
+		}
+		rx_cbdp = rx_cbdp_base = ep_ref[epid].prx;
+
+		do {
+			if (!(rx_cbdp->cbd_sc & RX_BD_E)) {
+
+				if (rx_cbdp->cbd_sc & 0x1F) {
+					/* Corrupt data discard it.
+					 * Controller has NAK'd this packet.
+					 */
+					mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+				} else {
+					if (!epid) {
+						mpc8xx_udc_ep0_rx (rx_cbdp);
+
+					} else {
+						/* Process data */
+						mpc8xx_udc_set_nak (epid);
+						mpc8xx_udc_epn_rx (epid, rx_cbdp);
+						mpc8xx_udc_clear_rxbd (rx_cbdp);
+					}
+				}
+
+				/* Advance RX CBD pointer */
+				mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+				ep_ref[epid].prx = rx_cbdp;
+			} else {
+				/* Advance RX CBD pointer */
+				mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+			}
+
+		} while (rx_cbdp != rx_cbdp_base);
+	}
+
+	/* Handle TX events as appropiate, the correct place to do this is
+	 * in a tx routine. Perhaps TX on epn was pre-empted by ep0
+	 */
+
+	if (usbp->usber & USB_E_TXB) {
+		usbp->usber |= USB_E_TXB;
+	}
+
+	if (usbp->usber & (USB_TX_ERRMASK)) {
+		mpc8xx_udc_handle_txerr ();
+	}
+
+	/* Switch to the default state, respond at the default address */
+	if (usbp->usber & USB_E_RESET) {
+		usbp->usber |= USB_E_RESET;
+		usbp->usaddr = 0x00;
+		udc_device->device_state = STATE_DEFAULT;
+	}
+
+	/* if(usbp->usber&USB_E_IDLE){
+	   We could suspend here !
+	   usbp->usber|=USB_E_IDLE;
+	   DBG("idle state change\n");
+	   }
+	   if(usbp->usbs){
+	   We could resume here when IDLE is deasserted !
+	   Not worth doing, so long as we are self powered though.
+	   }
+	*/
+
+	return;
+}
+
+/* udc_endpoint_write
+ *
+ * Write some data to an endpoint
+ */
+int udc_endpoint_write (struct usb_endpoint_instance *epi)
+{
+	int ep = 0;
+	short epid = 1, unnak = 0, ret = 0;
+
+	if (udc_state != STATE_READY) {
+		ERR ("invalid udc_state != STATE_READY!\n");
+		return -1;
+	}
+
+	if (!udc_device || !epi) {
+		return -1;
+	}
+
+	if (udc_device->device_state != STATE_CONFIGURED) {
+		return -1;
+	}
+
+	ep = epi->endpoint_address & 0x03;
+	if (ep >= MAX_ENDPOINTS) {
+		return -1;
+	}
+
+	/* Set NAK for all RX endpoints during TX */
+	for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+
+		/* Don't set NAK on DATA IN/CONTROL endpoints */
+		if (ep_ref[epid].sc & USB_DIR_IN) {
+			continue;
+		}
+
+		if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) {
+			unnak |= 1 << epid;
+		}
+
+		mpc8xx_udc_set_nak (epid);
+	}
+
+	mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep],
+			    epi->tx_urb);
+	ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]);
+
+	/* Remove temporary NAK */
+	for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+		if (unnak & (1 << epid)) {
+			udc_unset_nak (epid);
+		}
+	}
+
+	return ret;
+}
+
+/* mpc8xx_udc_assign_urb
+ *
+ * Associate a given urb to an endpoint TX or RX transmit/receive buffers
+ */
+static int mpc8xx_udc_assign_urb (int ep, char direction)
+{
+	struct usb_endpoint_instance *epi = 0;
+
+	if (ep >= MAX_ENDPOINTS) {
+		goto err;
+	}
+	epi = &udc_device->bus->endpoint_array[ep];
+	if (!epi) {
+		goto err;
+	}
+
+	if (!ep_ref[ep].urb) {
+		ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array);
+		if (!ep_ref[ep].urb) {
+			goto err;
+		}
+	} else {
+		ep_ref[ep].urb->actual_length = 0;
+	}
+
+	switch (direction) {
+	case USB_DIR_IN:
+		epi->tx_urb = ep_ref[ep].urb;
+		break;
+	case USB_DIR_OUT:
+		epi->rcv_urb = ep_ref[ep].urb;
+		break;
+	default:
+		goto err;
+	}
+	return 0;
+
+      err:
+	udc_state = STATE_ERROR;
+	return -1;
+}
+
+/* udc_setup_ep
+ *
+ * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram
+ * Isochronous endpoints aren't yet supported!
+ */
+void udc_setup_ep (struct usb_device_instance *device, unsigned int ep,
+		   struct usb_endpoint_instance *epi)
+{
+	uchar direction = 0;
+	int ep_attrib = 0;
+
+	if (epi && (ep < MAX_ENDPOINTS)) {
+
+		if (ep == 0) {
+			if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL
+			    || epi->tx_attributes !=
+			    USB_ENDPOINT_XFER_CONTROL) {
+
+				/* ep0 must be a control endpoint */
+				udc_state = STATE_ERROR;
+				return;
+
+			}
+			if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+				mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize,
+						       epi->rcv_packetSize);
+			}
+			usbp->usep[ep] = 0x0000;
+			return;
+		}
+
+		if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK)
+		    == USB_DIR_IN) {
+
+			direction = 1;
+			ep_attrib = epi->tx_attributes;
+			epi->rcv_packetSize = 0;
+			ep_ref[ep].sc |= USB_DIR_IN;
+		} else {
+
+			direction = 0;
+			ep_attrib = epi->rcv_attributes;
+			epi->tx_packetSize = 0;
+			ep_ref[ep].sc &= ~USB_DIR_IN;
+		}
+
+		if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address
+					   & USB_ENDPOINT_DIR_MASK)) {
+			return;
+		}
+
+		switch (ep_attrib) {
+		case USB_ENDPOINT_XFER_CONTROL:
+			if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+				mpc8xx_udc_cbd_attach (ep,
+						       epi->tx_packetSize,
+						       epi->rcv_packetSize);
+			}
+			usbp->usep[ep] = ep << 12;
+			epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb;
+
+			break;
+		case USB_ENDPOINT_XFER_BULK:
+		case USB_ENDPOINT_XFER_INT:
+			if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+				if (direction) {
+					mpc8xx_udc_cbd_attach (ep,
+							       epi->tx_packetSize,
+							       0);
+				} else {
+					mpc8xx_udc_cbd_attach (ep,
+							       0,
+							       epi->rcv_packetSize);
+				}
+			}
+			usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8);
+
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+		default:
+			serial_printf ("Error endpoint attrib %d>3\n", ep_attrib);
+			udc_state = STATE_ERROR;
+			break;
+		}
+	}
+
+}
+
+/* udc_connect
+ *
+ * Move state, switch on the USB
+ */
+void udc_connect (void)
+{
+	/* Enable pull-up resistor on D+
+	 * TODO: fit a pull-up resistor to drive SE0 for > 2.5us
+	 */
+
+	if (udc_state != STATE_ERROR) {
+		udc_state = STATE_READY;
+		usbp->usmod |= USMOD_EN;
+	}
+}
+
+/* udc_disconnect
+ *
+ * Disconnect is not used but, is included for completeness
+ */
+void udc_disconnect (void)
+{
+	/* Disable pull-up resistor on D-
+	 * TODO: fix a pullup resistor to control this
+	 */
+
+	if (udc_state != STATE_ERROR) {
+		udc_state = STATE_NOT_READY;
+	}
+	usbp->usmod &= ~USMOD_EN;
+}
+
+/* udc_enable
+ *
+ * Grab an EP0 URB, register interest in a subset of USB events
+ */
+void udc_enable (struct usb_device_instance *device)
+{
+	if (udc_state == STATE_ERROR) {
+		return;
+	}
+
+	udc_device = device;
+
+	if (!ep_ref[0].urb) {
+		ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array);
+	}
+
+	/* Register interest in all events except SOF, enable transceiver */
+	usbp->usber = 0x03FF;
+	usbp->usbmr = 0x02F7;
+
+	return;
+}
+
+/* udc_disable
+ *
+ * disable the currently hooked device
+ */
+void udc_disable (void)
+{
+	int i = 0;
+
+	if (udc_state == STATE_ERROR) {
+		DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n");
+		return;
+	}
+
+	udc_device = 0;
+
+	for (; i < MAX_ENDPOINTS; i++) {
+		if (ep_ref[i].urb) {
+			usbd_dealloc_urb (ep_ref[i].urb);
+			ep_ref[i].urb = 0;
+		}
+	}
+
+	usbp->usbmr = 0x00;
+	usbp->usmod = ~USMOD_EN;
+	udc_state = STATE_NOT_READY;
+}
+
+/* udc_startup_events
+ *
+ * Enable the specified device
+ */
+void udc_startup_events (struct usb_device_instance *device)
+{
+	udc_enable (device);
+	if (udc_state == STATE_READY) {
+		usbd_device_event_irq (device, DEVICE_CREATE, 0);
+	}
+}
+
+/* udc_set_nak
+ *
+ * Allow upper layers to signal lower layers should not accept more RX data
+ *
+ */
+void udc_set_nak (int epid)
+{
+	if (epid) {
+		mpc8xx_udc_set_nak (epid);
+	}
+}
+
+/* udc_unset_nak
+ *
+ * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint.
+ * Switch off NAKing on this endpoint to accept more data output from host.
+ *
+ */
+void udc_unset_nak (int epid)
+{
+	if (epid > MAX_ENDPOINTS) {
+		return;
+	}
+
+	if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) {
+		usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK);
+		__asm__ ("eieio");
+	}
+}
+
+/******************************************************************************
+			      Static Linkage
+******************************************************************************/
+
+/* udc_state_transition_up
+ * udc_state_transition_down
+ *
+ * Helper functions to implement device state changes.	The device states and
+ * the events that transition between them are:
+ *
+ *				STATE_ATTACHED
+ *				||	/\
+ *				\/	||
+ *	DEVICE_HUB_CONFIGURED			DEVICE_HUB_RESET
+ *				||	/\
+ *				\/	||
+ *				STATE_POWERED
+ *				||	/\
+ *				\/	||
+ *	DEVICE_RESET				DEVICE_POWER_INTERRUPTION
+ *				||	/\
+ *				\/	||
+ *				STATE_DEFAULT
+ *				||	/\
+ *				\/	||
+ *	DEVICE_ADDRESS_ASSIGNED			DEVICE_RESET
+ *				||	/\
+ *				\/	||
+ *				STATE_ADDRESSED
+ *				||	/\
+ *				\/	||
+ *	DEVICE_CONFIGURED			DEVICE_DE_CONFIGURED
+ *				||	/\
+ *				\/	||
+ *				STATE_CONFIGURED
+ *
+ * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way.  If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition_down transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ */
+
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+					    usb_device_state_t final)
+{
+	if (initial < final) {
+		switch (initial) {
+		case STATE_ATTACHED:
+			usbd_device_event_irq (udc_device,
+					       DEVICE_HUB_CONFIGURED, 0);
+			if (final == STATE_POWERED)
+				break;
+		case STATE_POWERED:
+			usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+			if (final == STATE_DEFAULT)
+				break;
+		case STATE_DEFAULT:
+			usbd_device_event_irq (udc_device,
+					       DEVICE_ADDRESS_ASSIGNED, 0);
+			if (final == STATE_ADDRESSED)
+				break;
+		case STATE_ADDRESSED:
+			usbd_device_event_irq (udc_device, DEVICE_CONFIGURED,
+					       0);
+		case STATE_CONFIGURED:
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+					      usb_device_state_t final)
+{
+	if (initial > final) {
+		switch (initial) {
+		case STATE_CONFIGURED:
+			usbd_device_event_irq (udc_device,
+					       DEVICE_DE_CONFIGURED, 0);
+			if (final == STATE_ADDRESSED)
+				break;
+		case STATE_ADDRESSED:
+			usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+			if (final == STATE_DEFAULT)
+				break;
+		case STATE_DEFAULT:
+			usbd_device_event_irq (udc_device,
+					       DEVICE_POWER_INTERRUPTION, 0);
+			if (final == STATE_POWERED)
+				break;
+		case STATE_POWERED:
+			usbd_device_event_irq (udc_device, DEVICE_HUB_RESET,
+					       0);
+		case STATE_ATTACHED:
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/* mpc8xx_udc_stall
+ *
+ * Force returning of STALL tokens on the given endpoint. Protocol or function
+ * STALL conditions are permissable here
+ */
+static void mpc8xx_udc_stall (unsigned int ep)
+{
+	usbp->usep[ep] |= STALL_BITMASK;
+}
+
+/* mpc8xx_udc_set_nak
+ *
+ * Force returning of NAK responses for the given endpoint as a kind of very
+ * simple flow control
+ */
+static void mpc8xx_udc_set_nak (unsigned int ep)
+{
+	usbp->usep[ep] |= NAK_BITMASK;
+	__asm__ ("eieio");
+}
+
+/* mpc8xx_udc_handle_txerr
+ *
+ * Handle errors relevant to TX. Return a status code to allow calling
+ * indicative of what if anything happened
+ */
+static short mpc8xx_udc_handle_txerr ()
+{
+	short ep = 0, ret = 0;
+
+	for (; ep < TX_RING_SIZE; ep++) {
+		if (usbp->usber & (0x10 << ep)) {
+
+			/* Timeout or underrun */
+			if (tx_cbd[ep]->cbd_sc & 0x06) {
+				ret = 1;
+				mpc8xx_udc_flush_tx_fifo (ep);
+
+			} else {
+				if (usbp->usep[ep] & STALL_BITMASK) {
+					if (!ep) {
+						usbp->usep[ep] &= ~STALL_BITMASK;
+					}
+				}	/* else NAK */
+			}
+			usbp->usber |= (0x10 << ep);
+		}
+	}
+	return ret;
+}
+
+/* mpc8xx_udc_advance_rx
+ *
+ * Advance cbd rx
+ */
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid)
+{
+	if ((*rx_cbdp)->cbd_sc & RX_BD_W) {
+		*rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CFG_IMMR);
+
+	} else {
+		(*rx_cbdp)++;
+	}
+}
+
+
+/* mpc8xx_udc_flush_tx_fifo
+ *
+ * Flush a given TX fifo. Assumes one tx cbd per endpoint
+ */
+static void mpc8xx_udc_flush_tx_fifo (int epid)
+{
+	volatile cbd_t *tx_cbdp = 0;
+
+	if (epid > MAX_ENDPOINTS) {
+		return;
+	}
+
+	/* TX stop */
+	immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01);
+	__asm__ ("eieio");
+	while (immr->im_cpm.cp_cpcr & 0x01);
+
+	usbp->uscom = 0x40 | 0;
+
+	/* reset ring */
+	tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CFG_IMMR);
+	tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W);
+
+
+	endpoints[epid]->tptr = endpoints[epid]->tbase;
+	endpoints[epid]->tstate = 0x00;
+	endpoints[epid]->tbcnt = 0x00;
+
+	/* TX start */
+	immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01);
+	__asm__ ("eieio");
+	while (immr->im_cpm.cp_cpcr & 0x01);
+
+	return;
+}
+
+/* mpc8xx_udc_flush_rx_fifo
+ *
+ * For the sake of completeness of the namespace, it seems like
+ * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo();
+ * If RX_BD_E is true => a driver bug either here or in an upper layer
+ * not polling frequently enough. If RX_BD_E is true we have told the host
+ * we have accepted data but, the CPM found it had no-where to put that data
+ * which needless to say would be a bad thing.
+ */
+static void mpc8xx_udc_flush_rx_fifo ()
+{
+	int i = 0;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+			ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n",
+			     rx_cbd[i], rx_cbd[i]->cbd_datlen,
+			     rx_cbd[i]->cbd_sc);
+
+		}
+	}
+	ERR ("BUG : Input over-run\n");
+}
+
+/* mpc8xx_udc_clear_rxbd
+ *
+ * Release control of RX CBD to CP.
+ */
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp)
+{
+	rx_cbdp->cbd_datlen = 0x0000;
+	rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I));
+	__asm__ ("eieio");
+}
+
+/* mpc8xx_udc_tx_irq
+ *
+ * Parse for tx timeout, control RX or USB reset/busy conditions
+ * Return -1 on timeout, -2 on fatal error, else return zero
+ */
+static int mpc8xx_udc_tx_irq (int ep)
+{
+	int i = 0;
+
+	if (usbp->usber & (USB_TX_ERRMASK)) {
+		if (mpc8xx_udc_handle_txerr ()) {
+			/* Timeout, controlling function must retry send */
+			return -1;
+		}
+	}
+
+	if (usbp->usber & (USB_E_RESET | USB_E_BSY)) {
+		/* Fatal, abandon TX transaction */
+		return -2;
+	}
+
+	if (usbp->usber & USB_E_RXB) {
+		for (i = 0; i < RX_RING_SIZE; i++) {
+			if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+				if ((rx_cbd[i] == ep_ref[0].prx) || ep) {
+					return -2;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* mpc8xx_udc_ep_tx
+ *
+ * Transmit in a re-entrant fashion outbound USB packets.
+ * Implement retry/timeout mechanism described in USB specification
+ * Toggle DATA0/DATA1 pids as necessary
+ * Introduces non-standard tx_retry. The USB standard has no scope for slave
+ * devices to give up TX, however tx_retry stops us getting stuck in an endless
+ * TX loop.
+ */
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi)
+{
+	struct urb *urb = epi->tx_urb;
+	volatile cbd_t *tx_cbdp = 0;
+	unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0;
+	int ret = 0;
+
+	if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) {
+		return -1;
+	}
+
+	ep = epi->endpoint_address & 0x03;
+	tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR);
+
+	if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) {
+		mpc8xx_udc_flush_tx_fifo (ep);
+		usbp->usber |= USB_E_TXB;
+	};
+
+	while (tx_retry++ < 100) {
+		ret = mpc8xx_udc_tx_irq (ep);
+		if (ret == -1) {
+			/* ignore timeout here */
+		} else if (ret == -2) {
+			/* Abandon TX */
+			mpc8xx_udc_flush_tx_fifo (ep);
+			return -1;
+		}
+
+		tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR);
+		while (tx_cbdp->cbd_sc & TX_BD_R) {
+		};
+		tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W);
+
+		pkt_len = urb->actual_length - epi->sent;
+
+		if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) {
+			pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT);
+		}
+
+		for (x = 0; x < pkt_len; x++) {
+			*((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) =
+				urb->buffer[epi->sent + x];
+		}
+		tx_cbdp->cbd_datlen = pkt_len;
+		tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid);
+		__asm__ ("eieio");
+
+#ifdef __SIMULATE_ERROR__
+		if (++err_poison_test == 2) {
+			err_poison_test = 0;
+			tx_cbdp->cbd_sc &= ~TX_BD_TC;
+		}
+#endif
+
+		usbp->uscom = (USCOM_STR | ep);
+
+		while (!(usbp->usber & USB_E_TXB)) {
+			ret = mpc8xx_udc_tx_irq (ep);
+			if (ret == -1) {
+				/* TX timeout */
+				break;
+			} else if (ret == -2) {
+				if (usbp->usber & USB_E_TXB) {
+					usbp->usber |= USB_E_TXB;
+				}
+				mpc8xx_udc_flush_tx_fifo (ep);
+				return -1;
+			}
+		};
+
+		if (usbp->usber & USB_E_TXB) {
+			usbp->usber |= USB_E_TXB;
+		}
+
+		/* ACK must be present <= 18bit times from TX */
+		if (ret == -1) {
+			continue;
+		}
+
+		/* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */
+		epi->sent += pkt_len;
+		epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize);
+		TOGGLE_TX_PID (ep_ref[ep].pid);
+
+		if (epi->sent >= epi->tx_urb->actual_length) {
+
+			epi->tx_urb->actual_length = 0;
+			epi->sent = 0;
+
+			if (ep_ref[ep].sc & EP_SEND_ZLP) {
+				ep_ref[ep].sc &= ~EP_SEND_ZLP;
+			} else {
+				return 0;
+			}
+		}
+	}
+
+	ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent,
+	     epi->tx_urb->actual_length);
+
+	return -1;
+}
+
+/* mpc8xx_udc_dump_request
+ *
+ * Dump a control request to console
+ */
+static void mpc8xx_udc_dump_request (struct usb_device_request *request)
+{
+	DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x "
+	     "wIndex:%04x wLength:%04x ?\n",
+	     request->bmRequestType,
+	     request->bRequest,
+	     request->wValue, request->wIndex, request->wLength);
+
+	return;
+}
+
+/* mpc8xx_udc_ep0_rx_setup
+ *
+ * Decode received ep0 SETUP packet. return non-zero on error
+ */
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp)
+{
+	unsigned int x = 0;
+	struct urb *purb = ep_ref[0].urb;
+	struct usb_endpoint_instance *epi =
+		&udc_device->bus->endpoint_array[0];
+
+	for (; x < rx_cbdp->cbd_datlen; x++) {
+		*(((unsigned char *) &ep_ref[0].urb->device_request) + x) =
+			*((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+	}
+
+	mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+	if (ep0_recv_setup (purb)) {
+		mpc8xx_udc_dump_request (&purb->device_request);
+		return -1;
+	}
+
+	if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+	    == USB_REQ_HOST2DEVICE) {
+
+		switch (purb->device_request.bRequest) {
+		case USB_REQ_SET_ADDRESS:
+			/* Send the Status OUT ZLP */
+			ep_ref[0].pid = TX_BD_PID_DATA1;
+			purb->actual_length = 0;
+			mpc8xx_udc_init_tx (epi, purb);
+			mpc8xx_udc_ep_tx (epi);
+
+			/* Move to the addressed state */
+			usbp->usaddr = udc_device->address;
+			mpc8xx_udc_state_transition_up (udc_device->device_state,
+							STATE_ADDRESSED);
+			return 0;
+
+		case USB_REQ_SET_CONFIGURATION:
+			if (!purb->device_request.wValue) {
+				/* Respond at default address */
+				usbp->usaddr = 0x00;
+				mpc8xx_udc_state_transition_down (udc_device->device_state,
+								  STATE_ADDRESSED);
+			} else {
+				/* TODO: Support multiple configurations */
+				mpc8xx_udc_state_transition_up (udc_device->device_state,
+								STATE_CONFIGURED);
+				for (x = 1; x < MAX_ENDPOINTS; x++) {
+					if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK)
+					    == USB_DIR_IN) {
+						ep_ref[x].pid = TX_BD_PID_DATA0;
+					} else {
+						ep_ref[x].pid = RX_BD_PID_DATA0;
+					}
+					/* Set configuration must unstall endpoints */
+					usbp->usep[x] &= ~STALL_BITMASK;
+				}
+			}
+			break;
+		default:
+			/* CDC/Vendor specific */
+			break;
+		}
+
+		/* Send ZLP as ACK in Status OUT phase */
+		ep_ref[0].pid = TX_BD_PID_DATA1;
+		purb->actual_length = 0;
+		mpc8xx_udc_init_tx (epi, purb);
+		mpc8xx_udc_ep_tx (epi);
+
+	} else {
+
+		if (purb->actual_length) {
+			ep_ref[0].pid = TX_BD_PID_DATA1;
+			mpc8xx_udc_init_tx (epi, purb);
+
+			if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) {
+				ep_ref[0].sc |= EP_SEND_ZLP;
+			}
+
+			if (purb->device_request.wValue ==
+			    USB_DESCRIPTOR_TYPE_DEVICE) {
+				if (le16_to_cpu (purb->device_request.wLength)
+				    > purb->actual_length) {
+					/* Send EP0_MAX_PACKET_SIZE bytes
+					 * unless correct size requested.
+					 */
+					if (purb->actual_length > epi->tx_packetSize) {
+						purb->actual_length = epi->tx_packetSize;
+					}
+				}
+			}
+			mpc8xx_udc_ep_tx (epi);
+
+		} else {
+			/* Corrupt SETUP packet? */
+			ERR ("Zero length data or SETUP with DATA-IN phase ?\n");
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* mpc8xx_udc_init_tx
+ *
+ * Setup some basic parameters for a TX transaction
+ */
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+				struct urb *tx_urb)
+{
+	epi->sent = 0;
+	epi->last = 0;
+	epi->tx_urb = tx_urb;
+}
+
+/* mpc8xx_udc_ep0_rx
+ *
+ * Receive ep0/control USB data. Parse and possibly send a response.
+ */
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp)
+{
+	if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) {
+
+		/* Unconditionally accept SETUP packets */
+		if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) {
+			mpc8xx_udc_stall (0);
+		}
+
+	} else {
+
+		mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+		if ((rx_cbdp->cbd_datlen - 2)) {
+			/* SETUP with a DATA phase
+			 * outside of SETUP packet.
+			 * Reply with STALL.
+			 */
+			mpc8xx_udc_stall (0);
+		}
+	}
+}
+
+/* mpc8xx_udc_epn_rx
+ *
+ * Receive some data from cbd into USB system urb data abstraction
+ * Upper layers should NAK if there is insufficient RX data space
+ */
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp)
+{
+	struct usb_endpoint_instance *epi = 0;
+	struct urb *urb = 0;
+	unsigned int x = 0;
+
+	if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) {
+		return 0;
+	}
+
+	/* USB 2.0 PDF section 8.6.4
+	 * Discard data with invalid PID it is a resend.
+	 */
+	if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) {
+		return 1;
+	}
+	TOGGLE_RX_PID (ep_ref[epid].pid);
+
+	epi = &udc_device->bus->endpoint_array[epid];
+	urb = epi->rcv_urb;
+
+	for (; x < (rx_cbdp->cbd_datlen - 2); x++) {
+		*((unsigned char *) (urb->buffer + urb->actual_length + x)) =
+			*((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+	}
+
+	if (x) {
+		usbd_rcv_complete (epi, x, 0);
+		if (ep_ref[epid].urb->status == RECV_ERROR) {
+			DBG ("RX error unset NAK\n");
+			udc_unset_nak (epid);
+		}
+	}
+	return x;
+}
+
+/* mpc8xx_udc_clock_init
+ *
+ * Obtain a clock reference for Full Speed Signaling
+ */
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+				   volatile cpm8xx_t * cp)
+{
+
+#if defined(CFG_USB_EXTC_CLK)
+
+	/* This has been tested with a 48MHz crystal on CLK6 */
+	switch (CFG_USB_EXTC_CLK) {
+	case 1:
+		immr->im_ioport.iop_papar |= 0x0100;
+		immr->im_ioport.iop_padir &= ~0x0100;
+		cp->cp_sicr |= 0x24;
+		break;
+	case 2:
+		immr->im_ioport.iop_papar |= 0x0200;
+		immr->im_ioport.iop_padir &= ~0x0200;
+		cp->cp_sicr |= 0x2D;
+		break;
+	case 3:
+		immr->im_ioport.iop_papar |= 0x0400;
+		immr->im_ioport.iop_padir &= ~0x0400;
+		cp->cp_sicr |= 0x36;
+		break;
+	case 4:
+		immr->im_ioport.iop_papar |= 0x0800;
+		immr->im_ioport.iop_padir &= ~0x0800;
+		cp->cp_sicr |= 0x3F;
+		break;
+	default:
+		udc_state = STATE_ERROR;
+		break;
+	}
+
+#elif defined(CFG_USB_BRGCLK)
+
+	/* This has been tested with brgclk == 50MHz */
+	int divisor = 0;
+
+	if (gd->cpu_clk < 48000000L) {
+		ERR ("brgclk is too slow for full-speed USB!\n");
+		udc_state = STATE_ERROR;
+		return;
+	}
+
+	/* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48Mhz)
+	 * but, can /probably/ live with close-ish alternative rates.
+	 */
+	divisor = (gd->cpu_clk / 48000000L) - 1;
+	cp->cp_sicr &= ~0x0000003F;
+
+	switch (CFG_USB_BRGCLK) {
+	case 1:
+		cp->cp_brgc1 |= (divisor | CPM_BRG_EN);
+		cp->cp_sicr &= ~0x2F;
+		break;
+	case 2:
+		cp->cp_brgc2 |= (divisor | CPM_BRG_EN);
+		cp->cp_sicr |= 0x00000009;
+		break;
+	case 3:
+		cp->cp_brgc3 |= (divisor | CPM_BRG_EN);
+		cp->cp_sicr |= 0x00000012;
+		break;
+	case 4:
+		cp->cp_brgc4 = (divisor | CPM_BRG_EN);
+		cp->cp_sicr |= 0x0000001B;
+		break;
+	default:
+		udc_state = STATE_ERROR;
+		break;
+	}
+
+#else
+#error "CFG_USB_EXTC_CLK or CFG_USB_BRGCLK must be defined"
+#endif
+
+}
+
+/* mpc8xx_udc_cbd_attach
+ *
+ * attach a cbd to and endpoint
+ */
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size)
+{
+
+	if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) {
+		udc_state = STATE_ERROR;
+		return;
+	}
+
+	if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT ||
+	    (!tx_size && !rx_size)) {
+		udc_state = STATE_ERROR;
+		return;
+	}
+
+	/* Attach CBD to appropiate Parameter RAM Endpoint data structure */
+	if (rx_size) {
+		endpoints[ep]->rbase = (u32) rx_cbd[rx_ct];
+		endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+		rx_ct++;
+
+		if (!ep) {
+
+			endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+			rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+			rx_ct++;
+
+		} else {
+			rx_ct += 2;
+			endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+			rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+			rx_ct++;
+		}
+
+		/* Where we expect to RX data on this endpoint */
+		ep_ref[ep].prx = rx_cbd[rx_ct - 1];
+	} else {
+
+		ep_ref[ep].prx = 0;
+		endpoints[ep]->rbase = 0;
+		endpoints[ep]->rbptr = 0;
+	}
+
+	if (tx_size) {
+		endpoints[ep]->tbase = (u32) tx_cbd[tx_ct];
+		endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct];
+		tx_ct++;
+	} else {
+		endpoints[ep]->tbase = 0;
+		endpoints[ep]->tbptr = 0;
+	}
+
+	endpoints[ep]->tstate = 0;
+	endpoints[ep]->tbcnt = 0;
+	endpoints[ep]->mrblr = EP_MAX_PKT;
+	endpoints[ep]->rfcr = 0x18;
+	endpoints[ep]->tfcr = 0x18;
+	ep_ref[ep].sc |= EP_ATTACHED;
+
+	DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n",
+		ep, endpoints[ep]->rbase, endpoints[ep]->rbptr,
+		endpoints[ep]->tbase, endpoints[ep]->tbptr,
+		ep_ref[ep].prx);
+
+	return;
+}
+
+/* mpc8xx_udc_cbd_init
+ *
+ * Allocate space for a cbd and allocate TX/RX data space
+ */
+static void mpc8xx_udc_cbd_init (void)
+{
+	int i = 0;
+
+	for (; i < TX_RING_SIZE; i++) {
+		tx_cbd[i] = (cbd_t *)
+			mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+	}
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		rx_cbd[i] = (cbd_t *)
+			mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+	}
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		tx_cbd[i]->cbd_bufaddr =
+			mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+
+		tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W);
+		tx_cbd[i]->cbd_datlen = 0x0000;
+	}
+
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		rx_cbd[i]->cbd_bufaddr =
+			mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+		rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E);
+		rx_cbd[i]->cbd_datlen = 0x0000;
+
+	}
+
+	return;
+}
+
+/* mpc8xx_udc_endpoint_init
+ *
+ * Attach an endpoint to some dpram
+ */
+static void mpc8xx_udc_endpoint_init (void)
+{
+	int i = 0;
+
+	for (; i < MAX_ENDPOINTS; i++) {
+		endpoints[i] = (usb_epb_t *)
+			mpc8xx_udc_alloc (sizeof (usb_epb_t), 32);
+	}
+}
+
+/* mpc8xx_udc_alloc
+ *
+ * Grab the address of some dpram
+ */
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment)
+{
+	u32 retaddr = address_base;
+
+	while (retaddr % alignment) {
+		retaddr++;
+	}
+	address_base += data_size;
+
+	return retaddr;
+}
+
+#endif /* CONFIG_MPC885_FAMILY && CONFIG_USB_DEVICE) */
diff --git a/drivers/usb/usbdcore_omap1510.c b/drivers/usb/usbdcore_omap1510.c
new file mode 100644
index 0000000..84bb936
--- /dev/null
+++ b/drivers/usb/usbdcore_omap1510.c
@@ -0,0 +1,1547 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * Based on
+ * linux/drivers/usb/device/bi/omap.c
+ * TI OMAP1510 USB bus interface driver
+ *
+ * Author: MontaVista Software, Inc.
+ *	   source@mvista.com
+ *	   (C) Copyright 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
+ *
+ */
+
+#include <common.h>
+
+#if defined(CONFIG_OMAP1510) && defined(CONFIG_USB_DEVICE)
+
+#include <asm/io.h>
+#ifdef CONFIG_OMAP_SX1
+#include <i2c.h>
+#endif
+
+#include "usbdcore.h"
+#include "usbdcore_omap1510.h"
+#include "usbdcore_ep0.h"
+
+
+#define UDC_INIT_MDELAY		     80 /* Device settle delay */
+#define UDC_MAX_ENDPOINTS	     31 /* Number of endpoints on this UDC */
+
+/* Some kind of debugging output... */
+#if 1
+#define UDCDBG(str)
+#define UDCDBGA(fmt,args...)
+#else  /* The bugs still exists... */
+#define UDCDBG(str) serial_printf("[%s] %s:%d: " str "\n", __FILE__,__FUNCTION__,__LINE__)
+#define UDCDBGA(fmt,args...) serial_printf("[%s] %s:%d: " fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##args)
+#endif
+
+#if 1
+#define UDCREG(name)
+#define UDCREGL(name)
+#else  /* The bugs still exists... */
+#define UDCREG(name)	 serial_printf("%s():%d: %s[%08x]=%.4x\n",__FUNCTION__,__LINE__, (#name), name, inw(name))	/* For 16-bit regs */
+#define UDCREGL(name)	 serial_printf("%s():%d: %s[%08x]=%.8x\n",__FUNCTION__,__LINE__, (#name), name, inl(name))	/* For 32-bit regs */
+#endif
+
+
+static struct urb *ep0_urb = NULL;
+
+static struct usb_device_instance *udc_device;	/* Used in interrupt handler */
+static u16 udc_devstat = 0;	/* UDC status (DEVSTAT) */
+static u32 udc_interrupts = 0;
+
+static void udc_stall_ep (unsigned int ep_addr);
+
+
+static struct usb_endpoint_instance *omap1510_find_ep (int ep)
+{
+	int i;
+
+	for (i = 0; i < udc_device->bus->max_endpoints; i++) {
+		if (udc_device->bus->endpoint_array[i].endpoint_address == ep)
+			return &udc_device->bus->endpoint_array[i];
+	}
+	return NULL;
+}
+
+/* ************************************************************************** */
+/* IO
+ */
+
+/*
+ * omap1510_prepare_endpoint_for_rx
+ *
+ * This function implements TRM Figure 14-11.
+ *
+ * The endpoint to prepare for transfer is specified as a physical endpoint
+ * number.  For OUT (rx) endpoints 1 through 15, the corresponding endpoint
+ * configuration register is checked to see if the endpoint is ISO or not.
+ * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled.
+ * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30.
+ */
+static void omap1510_prepare_endpoint_for_rx (int ep_addr)
+{
+	int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+	UDCDBGA ("omap1510_prepare_endpoint %x", ep_addr);
+	if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)) {
+		if ((inw (UDC_EP_RX (ep_num)) &
+		     (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) ==
+		    UDC_EPn_RX_Valid) {
+			/* rx endpoint is valid, non-ISO, so enable its FIFO */
+			outw (UDC_EP_Sel | ep_num, UDC_EP_NUM);
+			outw (UDC_Set_FIFO_En, UDC_CTRL);
+			outw (0, UDC_EP_NUM);
+		}
+	}
+}
+
+/* omap1510_configure_endpoints
+ *
+ * This function implements TRM Figure 14-10.
+ */
+static void omap1510_configure_endpoints (struct usb_device_instance *device)
+{
+	int ep;
+	struct usb_bus_instance *bus;
+	struct usb_endpoint_instance *endpoint;
+	unsigned short ep_ptr;
+	unsigned short ep_size;
+	unsigned short ep_isoc;
+	unsigned short ep_doublebuffer;
+	int ep_addr;
+	int packet_size;
+	int buffer_size;
+	int attributes;
+
+	bus = device->bus;
+
+	/* There is a dedicated 2048 byte buffer for USB packets that may be
+	 * arbitrarily partitioned among the endpoints on 8-byte boundaries.
+	 * The first 8 bytes are reserved for receiving setup packets on
+	 * endpoint 0.
+	 */
+	ep_ptr = 8;		/* reserve the first 8 bytes for the setup fifo */
+
+	for (ep = 0; ep < bus->max_endpoints; ep++) {
+		endpoint = bus->endpoint_array + ep;
+		ep_addr = endpoint->endpoint_address;
+		if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+			/* IN endpoint */
+			packet_size = endpoint->tx_packetSize;
+			attributes = endpoint->tx_attributes;
+		} else {
+			/* OUT endpoint */
+			packet_size = endpoint->rcv_packetSize;
+			attributes = endpoint->rcv_attributes;
+		}
+
+		switch (packet_size) {
+		case 0:
+			ep_size = 0;
+			break;
+		case 8:
+			ep_size = 0;
+			break;
+		case 16:
+			ep_size = 1;
+			break;
+		case 32:
+			ep_size = 2;
+			break;
+		case 64:
+			ep_size = 3;
+			break;
+		case 128:
+			ep_size = 4;
+			break;
+		case 256:
+			ep_size = 5;
+			break;
+		case 512:
+			ep_size = 6;
+			break;
+		default:
+			UDCDBGA ("ep 0x%02x has bad packet size %d",
+				 ep_addr, packet_size);
+			packet_size = 0;
+			ep_size = 0;
+			break;
+		}
+
+		switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_CONTROL:
+		case USB_ENDPOINT_XFER_BULK:
+		case USB_ENDPOINT_XFER_INT:
+		default:
+			/* A non-isochronous endpoint may optionally be
+			 * double-buffered. For now we disable
+			 * double-buffering.
+			 */
+			ep_doublebuffer = 0;
+			ep_isoc = 0;
+			if (packet_size > 64)
+				packet_size = 0;
+			if (!ep || !ep_doublebuffer)
+				buffer_size = packet_size;
+			else
+				buffer_size = packet_size * 2;
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			/* Isochronous endpoints are always double-
+			 * buffered, but the double-buffering bit
+			 * in the endpoint configuration register
+			 * becomes the msb of the endpoint size so we
+			 * set the double-buffering flag to zero.
+			 */
+			ep_doublebuffer = 0;
+			ep_isoc = 1;
+			buffer_size = packet_size * 2;
+			break;
+		}
+
+		/* check to see if our packet buffer RAM is exhausted */
+		if ((ep_ptr + buffer_size) > 2048) {
+			UDCDBGA ("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size);
+			buffer_size = packet_size = 0;
+		}
+
+		/* force a default configuration for endpoint 0 since it is
+		 * always enabled
+		 */
+		if (!ep && ((packet_size < 8) || (packet_size > 64))) {
+			buffer_size = packet_size = 64;
+			ep_size = 3;
+		}
+
+		if (!ep) {
+			/* configure endpoint 0 */
+			outw ((ep_size << 12) | (ep_ptr >> 3), UDC_EP0);
+			/*UDCDBGA("ep 0 buffer offset 0x%03x packet size 0x%03x", */
+			/*	ep_ptr, packet_size); */
+		} else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+			/* IN endpoint */
+			if (packet_size) {
+				outw ((1 << 15) | (ep_doublebuffer << 14) |
+				      (ep_size << 12) | (ep_isoc << 11) |
+				      (ep_ptr >> 3),
+				      UDC_EP_TX (ep_addr &
+						 USB_ENDPOINT_NUMBER_MASK));
+				UDCDBGA ("IN ep %d buffer offset 0x%03x"
+					 " packet size 0x%03x",
+					 ep_addr & USB_ENDPOINT_NUMBER_MASK,
+					 ep_ptr, packet_size);
+			} else {
+				outw (0,
+				      UDC_EP_TX (ep_addr &
+						 USB_ENDPOINT_NUMBER_MASK));
+			}
+		} else {
+			/* OUT endpoint */
+			if (packet_size) {
+				outw ((1 << 15) | (ep_doublebuffer << 14) |
+				      (ep_size << 12) | (ep_isoc << 11) |
+				      (ep_ptr >> 3),
+				      UDC_EP_RX (ep_addr &
+						 USB_ENDPOINT_NUMBER_MASK));
+				UDCDBGA ("OUT ep %d buffer offset 0x%03x"
+					 " packet size 0x%03x",
+					 ep_addr & USB_ENDPOINT_NUMBER_MASK,
+					 ep_ptr, packet_size);
+			} else {
+				outw (0,
+				      UDC_EP_RX (ep_addr &
+						 USB_ENDPOINT_NUMBER_MASK));
+			}
+		}
+		ep_ptr += buffer_size;
+	}
+}
+
+/* omap1510_deconfigure_device
+ *
+ * This function balances omap1510_configure_device.
+ */
+static void omap1510_deconfigure_device (void)
+{
+	int epnum;
+
+	UDCDBG ("clear Cfg_Lock");
+	outw (inw (UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1);
+	UDCREG (UDC_SYSCON1);
+
+	/* deconfigure all endpoints */
+	for (epnum = 1; epnum <= 15; epnum++) {
+		outw (0, UDC_EP_RX (epnum));
+		outw (0, UDC_EP_TX (epnum));
+	}
+}
+
+/* omap1510_configure_device
+ *
+ * This function implements TRM Figure 14-9.
+ */
+static void omap1510_configure_device (struct usb_device_instance *device)
+{
+	omap1510_configure_endpoints (device);
+
+
+	/* Figure 14-9 indicates we should enable interrupts here, but we have
+	 * other routines (udc_all_interrupts, udc_suspended_interrupts) to
+	 * do that.
+	 */
+
+	UDCDBG ("set Cfg_Lock");
+	outw (inw (UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1);
+	UDCREG (UDC_SYSCON1);
+}
+
+/* omap1510_write_noniso_tx_fifo
+ *
+ * This function implements TRM Figure 14-30.
+ *
+ * If the endpoint has an active tx_urb, then the next packet of data from the
+ * URB is written to the tx FIFO.  The total amount of data in the urb is given
+ * by urb->actual_length.  The maximum amount of data that can be sent in any
+ * one packet is given by endpoint->tx_packetSize.  The number of data bytes
+ * from this URB that have already been transmitted is given by endpoint->sent.
+ * endpoint->last is updated by this routine with the number of data bytes
+ * transmitted in this packet.
+ *
+ * In accordance with Figure 14-30, the EP_NUM register must already have been
+ * written with the value to select the appropriate tx FIFO before this routine
+ * is called.
+ */
+static void omap1510_write_noniso_tx_fifo (struct usb_endpoint_instance
+					   *endpoint)
+{
+	struct urb *urb = endpoint->tx_urb;
+
+	if (urb) {
+		unsigned int last, i;
+
+		UDCDBGA ("urb->buffer %p, buffer_length %d, actual_length %d",
+			 urb->buffer, urb->buffer_length, urb->actual_length);
+		if ((last =
+		     MIN (urb->actual_length - endpoint->sent,
+			  endpoint->tx_packetSize))) {
+			u8 *cp = urb->buffer + endpoint->sent;
+
+			UDCDBGA ("endpoint->sent %d, tx_packetSize %d, last %d", endpoint->sent, endpoint->tx_packetSize, last);
+
+			if (((u32) cp & 1) == 0) {	/* word aligned? */
+				outsw (UDC_DATA, cp, last >> 1);
+			} else {	/* byte aligned. */
+				for (i = 0; i < (last >> 1); i++) {
+					u16 w = ((u16) cp[2 * i + 1] << 8) |
+						(u16) cp[2 * i];
+					outw (w, UDC_DATA);
+				}
+			}
+			if (last & 1) {
+				outb (*(cp + last - 1), UDC_DATA);
+			}
+		}
+		endpoint->last = last;
+	}
+}
+
+/* omap1510_read_noniso_rx_fifo
+ *
+ * This function implements TRM Figure 14-28.
+ *
+ * If the endpoint has an active rcv_urb, then the next packet of data is read
+ * from the rcv FIFO and written to rcv_urb->buffer at offset
+ * rcv_urb->actual_length to append the packet data to the data from any
+ * previous packets for this transfer.	We assume that there is sufficient room
+ * left in the buffer to hold an entire packet of data.
+ *
+ * The return value is the number of bytes read from the FIFO for this packet.
+ *
+ * In accordance with Figure 14-28, the EP_NUM register must already have been
+ * written with the value to select the appropriate rcv FIFO before this routine
+ * is called.
+ */
+static int omap1510_read_noniso_rx_fifo (struct usb_endpoint_instance
+					 *endpoint)
+{
+	struct urb *urb = endpoint->rcv_urb;
+	int len = 0;
+
+	if (urb) {
+		len = inw (UDC_RXFSTAT);
+
+		if (len) {
+			unsigned char *cp = urb->buffer + urb->actual_length;
+
+			insw (UDC_DATA, cp, len >> 1);
+			if (len & 1)
+				*(cp + len - 1) = inb (UDC_DATA);
+		}
+	}
+	return len;
+}
+
+/* omap1510_prepare_for_control_write_status
+ *
+ * This function implements TRM Figure 14-17.
+ *
+ * We have to deal here with non-autodecoded control writes that haven't already
+ * been dealt with by ep0_recv_setup.  The non-autodecoded standard control
+ * write requests are:	set/clear endpoint feature, set configuration, set
+ * interface, and set descriptor.  ep0_recv_setup handles set/clear requests for
+ * ENDPOINT_HALT by halting the endpoint for a set request and resetting the
+ * endpoint for a clear request.  ep0_recv_setup returns an error for
+ * SET_DESCRIPTOR requests which causes them to be terminated with a stall by
+ * the setup handler.  A SET_INTERFACE request is handled by ep0_recv_setup by
+ * generating a DEVICE_SET_INTERFACE event.  This leaves only the
+ * SET_CONFIGURATION event for us to deal with here.
+ *
+ */
+static void omap1510_prepare_for_control_write_status (struct urb *urb)
+{
+	struct usb_device_request *request = &urb->device_request;;
+
+	/* check for a SET_CONFIGURATION request */
+	if (request->bRequest == USB_REQ_SET_CONFIGURATION) {
+		int configuration = le16_to_cpu (request->wValue) & 0xff;
+		unsigned short devstat = inw (UDC_DEVSTAT);
+
+		if ((devstat & (UDC_ADD | UDC_CFG)) == UDC_ADD) {
+			/* device is currently in ADDRESSED state */
+			if (configuration) {
+				/* Assume the specified non-zero configuration
+				 * value is valid and switch to the CONFIGURED
+				 * state.
+				 */
+				outw (UDC_Dev_Cfg, UDC_SYSCON2);
+			}
+		} else if ((devstat & UDC_CFG) == UDC_CFG) {
+			/* device is currently in CONFIGURED state */
+			if (!configuration) {
+				/* Switch to ADDRESSED state. */
+				outw (UDC_Clr_Cfg, UDC_SYSCON2);
+			}
+		}
+	}
+
+	/* select EP0 tx FIFO */
+	outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+	/* clear endpoint (no data bytes in status stage) */
+	outw (UDC_Clr_EP, UDC_CTRL);
+	/* enable the EP0 tx FIFO */
+	outw (UDC_Set_FIFO_En, UDC_CTRL);
+	/* deselect the endpoint */
+	outw (UDC_EP_Dir, UDC_EP_NUM);
+}
+
+/* udc_state_transition_up
+ * udc_state_transition_down
+ *
+ * Helper functions to implement device state changes.	The device states and
+ * the events that transition between them are:
+ *
+ *				STATE_ATTACHED
+ *				||	/\
+ *				\/	||
+ *	DEVICE_HUB_CONFIGURED			DEVICE_HUB_RESET
+ *				||	/\
+ *				\/	||
+ *				STATE_POWERED
+ *				||	/\
+ *				\/	||
+ *	DEVICE_RESET				DEVICE_POWER_INTERRUPTION
+ *				||	/\
+ *				\/	||
+ *				STATE_DEFAULT
+ *				||	/\
+ *				\/	||
+ *	DEVICE_ADDRESS_ASSIGNED			DEVICE_RESET
+ *				||	/\
+ *				\/	||
+ *				STATE_ADDRESSED
+ *				||	/\
+ *				\/	||
+ *	DEVICE_CONFIGURED			DEVICE_DE_CONFIGURED
+ *				||	/\
+ *				\/	||
+ *				STATE_CONFIGURED
+ *
+ * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way.  If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition_down transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ * These functions must only be called with interrupts disabled.
+ */
+static void udc_state_transition_up (usb_device_state_t initial,
+				     usb_device_state_t final)
+{
+	if (initial < final) {
+		switch (initial) {
+		case STATE_ATTACHED:
+			usbd_device_event_irq (udc_device,
+					       DEVICE_HUB_CONFIGURED, 0);
+			if (final == STATE_POWERED)
+				break;
+		case STATE_POWERED:
+			usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+			if (final == STATE_DEFAULT)
+				break;
+		case STATE_DEFAULT:
+			usbd_device_event_irq (udc_device,
+					       DEVICE_ADDRESS_ASSIGNED, 0);
+			if (final == STATE_ADDRESSED)
+				break;
+		case STATE_ADDRESSED:
+			usbd_device_event_irq (udc_device, DEVICE_CONFIGURED,
+					       0);
+		case STATE_CONFIGURED:
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void udc_state_transition_down (usb_device_state_t initial,
+				       usb_device_state_t final)
+{
+	if (initial > final) {
+		switch (initial) {
+		case STATE_CONFIGURED:
+			usbd_device_event_irq (udc_device, DEVICE_DE_CONFIGURED, 0);
+			if (final == STATE_ADDRESSED)
+				break;
+		case STATE_ADDRESSED:
+			usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+			if (final == STATE_DEFAULT)
+				break;
+		case STATE_DEFAULT:
+			usbd_device_event_irq (udc_device, DEVICE_POWER_INTERRUPTION, 0);
+			if (final == STATE_POWERED)
+				break;
+		case STATE_POWERED:
+			usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, 0);
+		case STATE_ATTACHED:
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/* Handle all device state changes.
+ * This function implements TRM Figure 14-21.
+ */
+static void omap1510_udc_state_changed (void)
+{
+	u16 bits;
+	u16 devstat = inw (UDC_DEVSTAT);
+
+	UDCDBGA ("state changed, devstat %x, old %x", devstat, udc_devstat);
+
+	bits = devstat ^ udc_devstat;
+	if (bits) {
+		if (bits & UDC_ATT) {
+			if (devstat & UDC_ATT) {
+				UDCDBG ("device attached and powered");
+				udc_state_transition_up (udc_device->device_state, STATE_POWERED);
+			} else {
+				UDCDBG ("device detached or unpowered");
+				udc_state_transition_down (udc_device->device_state, STATE_ATTACHED);
+			}
+		}
+		if (bits & UDC_USB_Reset) {
+			if (devstat & UDC_USB_Reset) {
+				UDCDBG ("device reset in progess");
+				udc_state_transition_down (udc_device->device_state, STATE_POWERED);
+			} else {
+				UDCDBG ("device reset completed");
+			}
+		}
+		if (bits & UDC_DEF) {
+			if (devstat & UDC_DEF) {
+				UDCDBG ("device entering default state");
+				udc_state_transition_up (udc_device->device_state, STATE_DEFAULT);
+			} else {
+				UDCDBG ("device leaving default state");
+				udc_state_transition_down (udc_device->device_state, STATE_POWERED);
+			}
+		}
+		if (bits & UDC_SUS) {
+			if (devstat & UDC_SUS) {
+				UDCDBG ("entering suspended state");
+				usbd_device_event_irq (udc_device, DEVICE_BUS_INACTIVE, 0);
+			} else {
+				UDCDBG ("leaving suspended state");
+				usbd_device_event_irq (udc_device, DEVICE_BUS_ACTIVITY, 0);
+			}
+		}
+		if (bits & UDC_R_WK_OK) {
+			UDCDBGA ("remote wakeup %s", (devstat & UDC_R_WK_OK)
+				 ? "enabled" : "disabled");
+		}
+		if (bits & UDC_ADD) {
+			if (devstat & UDC_ADD) {
+				UDCDBG ("default -> addressed");
+				udc_state_transition_up (udc_device->device_state, STATE_ADDRESSED);
+			} else {
+				UDCDBG ("addressed -> default");
+				udc_state_transition_down (udc_device->device_state, STATE_DEFAULT);
+			}
+		}
+		if (bits & UDC_CFG) {
+			if (devstat & UDC_CFG) {
+				UDCDBG ("device configured");
+				/* The ep0_recv_setup function generates the
+				 * DEVICE_CONFIGURED event when a
+				 * USB_REQ_SET_CONFIGURATION setup packet is
+				 * received, so we should already be in the
+				 * state STATE_CONFIGURED.
+				 */
+				udc_state_transition_up (udc_device->device_state, STATE_CONFIGURED);
+			} else {
+				UDCDBG ("device deconfigured");
+				udc_state_transition_down (udc_device->device_state, STATE_ADDRESSED);
+			}
+		}
+	}
+
+	/* Clear interrupt source */
+	outw (UDC_DS_Chg, UDC_IRQ_SRC);
+
+	/* Save current DEVSTAT */
+	udc_devstat = devstat;
+}
+
+/* Handle SETUP USB interrupt.
+ * This function implements TRM Figure 14-14.
+ */
+static void omap1510_udc_setup (struct usb_endpoint_instance *endpoint)
+{
+	UDCDBG ("-> Entering device setup");
+
+	do {
+		const int setup_pktsize = 8;
+		unsigned char *datap =
+			(unsigned char *) &ep0_urb->device_request;
+
+		/* Gain access to EP 0 setup FIFO */
+		outw (UDC_Setup_Sel, UDC_EP_NUM);
+
+		/* Read control request data */
+		insb (UDC_DATA, datap, setup_pktsize);
+
+		UDCDBGA ("EP0 setup read [%x %x %x %x %x %x %x %x]",
+			 *(datap + 0), *(datap + 1), *(datap + 2),
+			 *(datap + 3), *(datap + 4), *(datap + 5),
+			 *(datap + 6), *(datap + 7));
+
+		/* Reset EP0 setup FIFO */
+		outw (0, UDC_EP_NUM);
+	} while (inw (UDC_IRQ_SRC) & UDC_Setup);
+
+	/* Try to process setup packet */
+	if (ep0_recv_setup (ep0_urb)) {
+		/* Not a setup packet, stall next EP0 transaction */
+		udc_stall_ep (0);
+		UDCDBG ("can't parse setup packet, still waiting for setup");
+		return;
+	}
+
+	/* Check direction */
+	if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+	    == USB_REQ_HOST2DEVICE) {
+		UDCDBG ("control write on EP0");
+		if (le16_to_cpu (ep0_urb->device_request.wLength)) {
+			/* We don't support control write data stages.
+			 * The only standard control write request with a data
+			 * stage is SET_DESCRIPTOR, and ep0_recv_setup doesn't
+			 * support that so we just stall those requests.  A
+			 * function driver might support a non-standard
+			 * write request with a data stage, but it isn't
+			 * obvious what we would do with the data if we read it
+			 * so we'll just stall it.  It seems like the API isn't
+			 * quite right here.
+			 */
+#if 0
+			/* Here is what we would do if we did support control
+			 * write data stages.
+			 */
+			ep0_urb->actual_length = 0;
+			outw (0, UDC_EP_NUM);
+			/* enable the EP0 rx FIFO */
+			outw (UDC_Set_FIFO_En, UDC_CTRL);
+#else
+			/* Stall this request */
+			UDCDBG ("Stalling unsupported EP0 control write data "
+				"stage.");
+			udc_stall_ep (0);
+#endif
+		} else {
+			omap1510_prepare_for_control_write_status (ep0_urb);
+		}
+	} else {
+		UDCDBG ("control read on EP0");
+		/* The ep0_recv_setup function has already placed our response
+		 * packet data in ep0_urb->buffer and the packet length in
+		 * ep0_urb->actual_length.
+		 */
+		endpoint->tx_urb = ep0_urb;
+		endpoint->sent = 0;
+		/* select the EP0 tx FIFO */
+		outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+		/* Write packet data to the FIFO.  omap1510_write_noniso_tx_fifo
+		 * will update endpoint->last with the number of bytes written
+		 * to the FIFO.
+		 */
+		omap1510_write_noniso_tx_fifo (endpoint);
+		/* enable the FIFO to start the packet transmission */
+		outw (UDC_Set_FIFO_En, UDC_CTRL);
+		/* deselect the EP0 tx FIFO */
+		outw (UDC_EP_Dir, UDC_EP_NUM);
+	}
+
+	UDCDBG ("<- Leaving device setup");
+}
+
+/* Handle endpoint 0 RX interrupt
+ * This routine implements TRM Figure 14-16.
+ */
+static void omap1510_udc_ep0_rx (struct usb_endpoint_instance *endpoint)
+{
+	unsigned short status;
+
+	UDCDBG ("RX on EP0");
+	/* select EP0 rx FIFO */
+	outw (UDC_EP_Sel, UDC_EP_NUM);
+
+	status = inw (UDC_STAT_FLG);
+
+	if (status & UDC_ACK) {
+		/* Check direction */
+		if ((ep0_urb->device_request.bmRequestType
+		     & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {
+			/* This rx interrupt must be for a control write data
+			 * stage packet.
+			 *
+			 * We don't support control write data stages.
+			 * We should never end up here.
+			 */
+
+			/* clear the EP0 rx FIFO */
+			outw (UDC_Clr_EP, UDC_CTRL);
+
+			/* deselect the EP0 rx FIFO */
+			outw (0, UDC_EP_NUM);
+
+			UDCDBG ("Stalling unexpected EP0 control write "
+				"data stage packet");
+			udc_stall_ep (0);
+		} else {
+			/* This rx interrupt must be for a control read status
+			 * stage packet.
+			 */
+			UDCDBG ("ACK on EP0 control read status stage packet");
+			/* deselect EP0 rx FIFO */
+			outw (0, UDC_EP_NUM);
+		}
+	} else if (status & UDC_STALL) {
+		UDCDBG ("EP0 stall during RX");
+		/* deselect EP0 rx FIFO */
+		outw (0, UDC_EP_NUM);
+	} else {
+		/* deselect EP0 rx FIFO */
+		outw (0, UDC_EP_NUM);
+	}
+}
+
+/* Handle endpoint 0 TX interrupt
+ * This routine implements TRM Figure 14-18.
+ */
+static void omap1510_udc_ep0_tx (struct usb_endpoint_instance *endpoint)
+{
+	unsigned short status;
+	struct usb_device_request *request = &ep0_urb->device_request;
+
+	UDCDBG ("TX on EP0");
+	/* select EP0 TX FIFO */
+	outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+
+	status = inw (UDC_STAT_FLG);
+	if (status & UDC_ACK) {
+		/* Check direction */
+		if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) ==
+		    USB_REQ_HOST2DEVICE) {
+			/* This tx interrupt must be for a control write status
+			 * stage packet.
+			 */
+			UDCDBG ("ACK on EP0 control write status stage packet");
+			/* deselect EP0 TX FIFO */
+			outw (UDC_EP_Dir, UDC_EP_NUM);
+		} else {
+			/* This tx interrupt must be for a control read data
+			 * stage packet.
+			 */
+			int wLength = le16_to_cpu (request->wLength);
+
+			/* Update our count of bytes sent so far in this
+			 * transfer.
+			 */
+			endpoint->sent += endpoint->last;
+
+			/* We are finished with this transfer if we have sent
+			 * all of the bytes in our tx urb (urb->actual_length)
+			 * unless we need a zero-length terminating packet.  We
+			 * need a zero-length terminating packet if we returned
+			 * fewer bytes than were requested (wLength) by the host,
+			 * and the number of bytes we returned is an exact
+			 * multiple of the packet size endpoint->tx_packetSize.
+			 */
+			if ((endpoint->sent == ep0_urb->actual_length)
+			    && ((ep0_urb->actual_length == wLength)
+				|| (endpoint->last !=
+				    endpoint->tx_packetSize))) {
+				/* Done with control read data stage. */
+				UDCDBG ("control read data stage complete");
+				/* deselect EP0 TX FIFO */
+				outw (UDC_EP_Dir, UDC_EP_NUM);
+				/* select EP0 RX FIFO to prepare for control
+				 * read status stage.
+				 */
+				outw (UDC_EP_Sel, UDC_EP_NUM);
+				/* clear the EP0 RX FIFO */
+				outw (UDC_Clr_EP, UDC_CTRL);
+				/* enable the EP0 RX FIFO */
+				outw (UDC_Set_FIFO_En, UDC_CTRL);
+				/* deselect the EP0 RX FIFO */
+				outw (0, UDC_EP_NUM);
+			} else {
+				/* We still have another packet of data to send
+				 * in this control read data stage or else we
+				 * need a zero-length terminating packet.
+				 */
+				UDCDBG ("ACK control read data stage packet");
+				omap1510_write_noniso_tx_fifo (endpoint);
+				/* enable the EP0 tx FIFO to start transmission */
+				outw (UDC_Set_FIFO_En, UDC_CTRL);
+				/* deselect EP0 TX FIFO */
+				outw (UDC_EP_Dir, UDC_EP_NUM);
+			}
+		}
+	} else if (status & UDC_STALL) {
+		UDCDBG ("EP0 stall during TX");
+		/* deselect EP0 TX FIFO */
+		outw (UDC_EP_Dir, UDC_EP_NUM);
+	} else {
+		/* deselect EP0 TX FIFO */
+		outw (UDC_EP_Dir, UDC_EP_NUM);
+	}
+}
+
+/* Handle RX transaction on non-ISO endpoint.
+ * This function implements TRM Figure 14-27.
+ * The ep argument is a physical endpoint number for a non-ISO OUT endpoint
+ * in the range 1 to 15.
+ */
+static void omap1510_udc_epn_rx (int ep)
+{
+	unsigned short status;
+
+	/* Check endpoint status */
+	status = inw (UDC_STAT_FLG);
+
+	if (status & UDC_ACK) {
+		int nbytes;
+		struct usb_endpoint_instance *endpoint =
+			omap1510_find_ep (ep);
+
+		nbytes = omap1510_read_noniso_rx_fifo (endpoint);
+		usbd_rcv_complete (endpoint, nbytes, 0);
+
+		/* enable rx FIFO to prepare for next packet */
+		outw (UDC_Set_FIFO_En, UDC_CTRL);
+	} else if (status & UDC_STALL) {
+		UDCDBGA ("STALL on RX endpoint %d", ep);
+	} else if (status & UDC_NAK) {
+		UDCDBGA ("NAK on RX ep %d", ep);
+	} else {
+		serial_printf ("omap-bi: RX on ep %d with status %x", ep,
+			       status);
+	}
+}
+
+/* Handle TX transaction on non-ISO endpoint.
+ * This function implements TRM Figure 14-29.
+ * The ep argument is a physical endpoint number for a non-ISO IN endpoint
+ * in the range 16 to 30.
+ */
+static void omap1510_udc_epn_tx (int ep)
+{
+	unsigned short status;
+
+	/*serial_printf("omap1510_udc_epn_tx( %x )\n",ep); */
+
+	/* Check endpoint status */
+	status = inw (UDC_STAT_FLG);
+
+	if (status & UDC_ACK) {
+		struct usb_endpoint_instance *endpoint =
+			omap1510_find_ep (ep);
+
+		/* We need to transmit a terminating zero-length packet now if
+		 * we have sent all of the data in this URB and the transfer
+		 * size was an exact multiple of the packet size.
+		 */
+		if (endpoint->tx_urb
+		    && (endpoint->last == endpoint->tx_packetSize)
+		    && (endpoint->tx_urb->actual_length - endpoint->sent -
+			endpoint->last == 0)) {
+			/* Prepare to transmit a zero-length packet. */
+			endpoint->sent += endpoint->last;
+			/* write 0 bytes of data to FIFO */
+			omap1510_write_noniso_tx_fifo (endpoint);
+			/* enable tx FIFO to start transmission */
+			outw (UDC_Set_FIFO_En, UDC_CTRL);
+		} else if (endpoint->tx_urb
+			   && endpoint->tx_urb->actual_length) {
+			/* retire the data that was just sent */
+			usbd_tx_complete (endpoint);
+			/* Check to see if we have more data ready to transmit
+			 * now.
+			 */
+			if (endpoint->tx_urb
+			    && endpoint->tx_urb->actual_length) {
+				/* write data to FIFO */
+				omap1510_write_noniso_tx_fifo (endpoint);
+				/* enable tx FIFO to start transmission */
+				outw (UDC_Set_FIFO_En, UDC_CTRL);
+			}
+		}
+	} else if (status & UDC_STALL) {
+		UDCDBGA ("STALL on TX endpoint %d", ep);
+	} else if (status & UDC_NAK) {
+		UDCDBGA ("NAK on TX endpoint %d", ep);
+	} else {
+		/*serial_printf("omap-bi: TX on ep %d with status %x\n", ep, status); */
+	}
+}
+
+
+/*
+-------------------------------------------------------------------------------
+*/
+
+/* Handle general USB interrupts and dispatch according to type.
+ * This function implements TRM Figure 14-13.
+ */
+void omap1510_udc_irq (void)
+{
+	u16 irq_src = inw (UDC_IRQ_SRC);
+	int valid_irq = 0;
+
+	if (!(irq_src & ~UDC_SOF_Flg))	/* ignore SOF interrupts ) */
+		return;
+
+	UDCDBGA ("< IRQ #%d start >- %x", udc_interrupts, irq_src);
+	/*serial_printf("< IRQ #%d start >- %x\n", udc_interrupts, irq_src); */
+
+	if (irq_src & UDC_DS_Chg) {
+		/* Device status changed */
+		omap1510_udc_state_changed ();
+		valid_irq++;
+	}
+	if (irq_src & UDC_EP0_RX) {
+		/* Endpoint 0 receive */
+		outw (UDC_EP0_RX, UDC_IRQ_SRC); /* ack interrupt */
+		omap1510_udc_ep0_rx (udc_device->bus->endpoint_array + 0);
+		valid_irq++;
+	}
+	if (irq_src & UDC_EP0_TX) {
+		/* Endpoint 0 transmit */
+		outw (UDC_EP0_TX, UDC_IRQ_SRC); /* ack interrupt */
+		omap1510_udc_ep0_tx (udc_device->bus->endpoint_array + 0);
+		valid_irq++;
+	}
+	if (irq_src & UDC_Setup) {
+		/* Device setup */
+		omap1510_udc_setup (udc_device->bus->endpoint_array + 0);
+		valid_irq++;
+	}
+	/*if (!valid_irq) */
+	/*	serial_printf("unknown interrupt, IRQ_SRC %.4x\n", irq_src); */
+	UDCDBGA ("< IRQ #%d end >", udc_interrupts);
+	udc_interrupts++;
+}
+
+/* This function implements TRM Figure 14-26. */
+void omap1510_udc_noniso_irq (void)
+{
+	unsigned short epnum;
+	unsigned short irq_src = inw (UDC_IRQ_SRC);
+	int valid_irq = 0;
+
+	if (!(irq_src & (UDC_EPn_RX | UDC_EPn_TX)))
+		return;
+
+	UDCDBGA ("non-ISO IRQ, IRQ_SRC %x", inw (UDC_IRQ_SRC));
+
+	if (irq_src & UDC_EPn_RX) {	/* Endpoint N OUT transaction */
+		/* Determine the endpoint number for this interrupt */
+		epnum = (inw (UDC_EPN_STAT) & 0x0f00) >> 8;
+		UDCDBGA ("RX on ep %x", epnum);
+
+		/* acknowledge interrupt */
+		outw (UDC_EPn_RX, UDC_IRQ_SRC);
+
+		if (epnum) {
+			/* select the endpoint FIFO */
+			outw (UDC_EP_Sel | epnum, UDC_EP_NUM);
+
+			omap1510_udc_epn_rx (epnum);
+
+			/* deselect the endpoint FIFO */
+			outw (epnum, UDC_EP_NUM);
+		}
+		valid_irq++;
+	}
+	if (irq_src & UDC_EPn_TX) {	/* Endpoint N IN transaction */
+		/* Determine the endpoint number for this interrupt */
+		epnum = (inw (UDC_EPN_STAT) & 0x000f) | USB_DIR_IN;
+		UDCDBGA ("TX on ep %x", epnum);
+
+		/* acknowledge interrupt */
+		outw (UDC_EPn_TX, UDC_IRQ_SRC);
+
+		if (epnum) {
+			/* select the endpoint FIFO */
+			outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM);
+
+			omap1510_udc_epn_tx (epnum);
+
+			/* deselect the endpoint FIFO */
+			outw (UDC_EP_Dir | epnum, UDC_EP_NUM);
+		}
+		valid_irq++;
+	}
+	if (!valid_irq)
+		serial_printf (": unknown non-ISO interrupt, IRQ_SRC %.4x\n",
+			       irq_src);
+}
+
+/*
+-------------------------------------------------------------------------------
+*/
+
+
+/*
+ * Start of public functions.
+ */
+
+/* Called to start packet transmission. */
+void udc_endpoint_write (struct usb_endpoint_instance *endpoint)
+{
+	unsigned short epnum =
+		endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
+
+	UDCDBGA ("Starting transmit on ep %x", epnum);
+
+	if (endpoint->tx_urb) {
+		/* select the endpoint FIFO */
+		outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM);
+		/* write data to FIFO */
+		omap1510_write_noniso_tx_fifo (endpoint);
+		/* enable tx FIFO to start transmission */
+		outw (UDC_Set_FIFO_En, UDC_CTRL);
+		/* deselect the endpoint FIFO */
+		outw (UDC_EP_Dir | epnum, UDC_EP_NUM);
+	}
+}
+
+/* Start to initialize h/w stuff */
+int udc_init (void)
+{
+	u16 udc_rev;
+	uchar value;
+	ulong gpio;
+	int i;
+
+	/* Let the device settle down before we start */
+	for (i = 0; i < UDC_INIT_MDELAY; i++) udelay(1000);
+
+	udc_device = NULL;
+
+	UDCDBG ("starting");
+
+	/* Check peripheral reset. Must be 1 to make sure
+	   MPU TIPB peripheral reset is inactive */
+	UDCREG (ARM_RSTCT2);
+
+	/* Set and check clock control.
+	 * We might ought to be using the clock control API to do
+	 * this instead of fiddling with the clock registers directly
+	 * here.
+	 */
+	outw ((1 << 4) | (1 << 5), CLOCK_CTRL);
+	UDCREG (CLOCK_CTRL);
+	/* Set and check APLL */
+	outw (0x0008, APLL_CTRL);
+	UDCREG (APLL_CTRL);
+	/* Set and check DPLL */
+	outw (0x2210, DPLL_CTRL);
+	UDCREG (DPLL_CTRL);
+	/* Set and check SOFT */
+	outw ((1 << 4) | (1 << 3) | 1, SOFT_REQ);
+	/* Short delay to wait for DPLL */
+	udelay (1000);
+
+	/* Print banner with device revision */
+	udc_rev = inw (UDC_REV) & 0xff;
+	printf ("USB:   TI OMAP1510 USB function module rev %d.%d\n",
+		udc_rev >> 4, udc_rev & 0xf);
+
+#ifdef CONFIG_OMAP_SX1
+	i2c_read (0x32, 0x04, 1, &value, 1);
+	value |= 0x04;
+	i2c_write (0x32, 0x04, 1, &value, 1);
+
+	i2c_read (0x32, 0x03, 1, &value, 1);
+	value |= 0x01;
+	i2c_write (0x32, 0x03, 1, &value, 1);
+
+	gpio = inl(GPIO_PIN_CONTROL_REG);
+	gpio |=  0x0002; /* A_IRDA_OFF */
+	gpio |=  0x0800; /* A_SWITCH   */
+	gpio |=  0x8000; /* A_USB_ON   */
+	outl (gpio, GPIO_PIN_CONTROL_REG);
+
+	gpio = inl(GPIO_DIR_CONTROL_REG);
+	gpio &= ~0x0002; /* A_IRDA_OFF */
+	gpio &= ~0x0800; /* A_SWITCH   */
+	gpio &= ~0x8000; /* A_USB_ON   */
+	outl (gpio, GPIO_DIR_CONTROL_REG);
+
+	gpio = inl(GPIO_DATA_OUTPUT_REG);
+	gpio |=  0x0002; /* A_IRDA_OFF */
+	gpio &= ~0x0800; /* A_SWITCH   */
+	gpio &= ~0x8000; /* A_USB_ON   */
+	outl (gpio, GPIO_DATA_OUTPUT_REG);
+#endif
+
+	/* The VBUS_MODE bit selects whether VBUS detection is done via
+	 * software (1) or hardware (0).  When software detection is
+	 * selected, VBUS_CTRL selects whether USB is not connected (0)
+	 * or connected (1).
+	 */
+	outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0);
+	outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0);
+	UDCREGL (FUNC_MUX_CTRL_0);
+
+	/*
+	 * At this point, device is ready for configuration...
+	 */
+
+	UDCDBG ("disable USB interrupts");
+	outw (0, UDC_IRQ_EN);
+	UDCREG (UDC_IRQ_EN);
+
+	UDCDBG ("disable USB DMA");
+	outw (0, UDC_DMA_IRQ_EN);
+	UDCREG (UDC_DMA_IRQ_EN);
+
+	UDCDBG ("initialize SYSCON1");
+	outw (UDC_Self_Pwr | UDC_Pullup_En, UDC_SYSCON1);
+	UDCREG (UDC_SYSCON1);
+
+	return 0;
+}
+
+/* Stall endpoint */
+static void udc_stall_ep (unsigned int ep_addr)
+{
+	/*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */
+	int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+	UDCDBGA ("stall ep_addr %d", ep_addr);
+
+	/* REVISIT?
+	 * The OMAP TRM section 14.2.4.2 says we must check that the FIFO
+	 * is empty before halting the endpoint.  The current implementation
+	 * doesn't check that the FIFO is empty.
+	 */
+
+	if (!ep_num) {
+		outw (UDC_Stall_Cmd, UDC_SYSCON2);
+	} else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+		if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) {
+			/* we have a valid rx endpoint, so halt it */
+			outw (UDC_EP_Sel | ep_num, UDC_EP_NUM);
+			outw (UDC_Set_Halt, UDC_CTRL);
+			outw (ep_num, UDC_EP_NUM);
+		}
+	} else {
+		if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) {
+			/* we have a valid tx endpoint, so halt it */
+			outw (UDC_EP_Sel | UDC_EP_Dir | ep_num, UDC_EP_NUM);
+			outw (UDC_Set_Halt, UDC_CTRL);
+			outw (ep_num, UDC_EP_NUM);
+		}
+	}
+}
+
+/* Reset endpoint */
+#if 0
+static void udc_reset_ep (unsigned int ep_addr)
+{
+	/*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */
+	int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+	UDCDBGA ("reset ep_addr %d", ep_addr);
+
+	if (!ep_num) {
+		/* control endpoint 0 can't be reset */
+	} else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+		UDCDBGA ("UDC_EP_RX(%d) = 0x%04x", ep_num,
+			 inw (UDC_EP_RX (ep_num)));
+		if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) {
+			/* we have a valid rx endpoint, so reset it */
+			outw (ep_num | UDC_EP_Sel, UDC_EP_NUM);
+			outw (UDC_Reset_EP, UDC_CTRL);
+			outw (ep_num, UDC_EP_NUM);
+			UDCDBGA ("OUT endpoint %d reset", ep_num);
+		}
+	} else {
+		UDCDBGA ("UDC_EP_TX(%d) = 0x%04x", ep_num,
+			 inw (UDC_EP_TX (ep_num)));
+		/* Resetting of tx endpoints seems to be causing the USB function
+		 * module to fail, which causes problems when the driver is
+		 * uninstalled.	 We'll skip resetting tx endpoints for now until
+		 * we figure out what the problem is.
+		 */
+#if 0
+		if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) {
+			/* we have a valid tx endpoint, so reset it */
+			outw (ep_num | UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+			outw (UDC_Reset_EP, UDC_CTRL);
+			outw (ep_num | UDC_EP_Dir, UDC_EP_NUM);
+			UDCDBGA ("IN endpoint %d reset", ep_num);
+		}
+#endif
+	}
+}
+#endif
+
+/* ************************************************************************** */
+
+/**
+ * udc_check_ep - check logical endpoint
+  *
+ * Return physical endpoint number to use for this logical endpoint or zero if not valid.
+ */
+#if 0
+int udc_check_ep (int logical_endpoint, int packetsize)
+{
+	if ((logical_endpoint == 0x80) ||
+	    ((logical_endpoint & 0x8f) != logical_endpoint)) {
+		return 0;
+	}
+
+	switch (packetsize) {
+	case 8:
+	case 16:
+	case 32:
+	case 64:
+	case 128:
+	case 256:
+	case 512:
+		break;
+	default:
+		return 0;
+	}
+
+	return EP_ADDR_TO_PHYS_EP (logical_endpoint);
+}
+#endif
+
+/*
+ * udc_setup_ep - setup endpoint
+ *
+ * Associate a physical endpoint with endpoint_instance
+ */
+void udc_setup_ep (struct usb_device_instance *device,
+		   unsigned int ep, struct usb_endpoint_instance *endpoint)
+{
+	UDCDBGA ("setting up endpoint addr %x", endpoint->endpoint_address);
+
+	/* This routine gets called by bi_modinit for endpoint 0 and from
+	 * bi_config for all of the other endpoints.  bi_config gets called
+	 * during the DEVICE_CREATE, DEVICE_CONFIGURED, and
+	 * DEVICE_SET_INTERFACE events.	 We need to reconfigure the OMAP packet
+	 * RAM after bi_config scans the selected device configuration and
+	 * initializes the endpoint structures, but before this routine enables
+	 * the OUT endpoint FIFOs.  Since bi_config calls this routine in a
+	 * loop for endpoints 1 through UDC_MAX_ENDPOINTS, we reconfigure our
+	 * packet RAM here when ep==1.
+	 * I really hate to do this here, but it seems like the API exported
+	 * by the USB bus interface controller driver to the usbd-bi module
+	 * isn't quite right so there is no good place to do this.
+	 */
+	if (ep == 1) {
+		omap1510_deconfigure_device ();
+		omap1510_configure_device (device);
+	}
+
+	if (endpoint && (ep < UDC_MAX_ENDPOINTS)) {
+		int ep_addr = endpoint->endpoint_address;
+
+		if (!ep_addr) {
+			/* nothing to do for endpoint 0 */
+		} else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+			/* nothing to do for IN (tx) endpoints */
+		} else {	/* OUT (rx) endpoint */
+			if (endpoint->rcv_packetSize) {
+				/*struct urb* urb = &(urb_out_array[ep&0xFF]); */
+				/*urb->endpoint = endpoint; */
+				/*urb->device = device; */
+				/*urb->buffer_length = sizeof(urb->buffer); */
+
+				/*endpoint->rcv_urb = urb; */
+				omap1510_prepare_endpoint_for_rx (ep_addr);
+			}
+		}
+	}
+}
+
+/**
+ * udc_disable_ep - disable endpoint
+ * @ep:
+ *
+ * Disable specified endpoint
+ */
+#if 0
+void udc_disable_ep (unsigned int ep_addr)
+{
+	/*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */
+	int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+	struct usb_endpoint_instance *endpoint = omap1510_find_ep (ep_addr);	/*udc_device->bus->endpoint_array + ep; */
+
+	UDCDBGA ("disable ep_addr %d", ep_addr);
+
+	if (!ep_num) {
+		/* nothing to do for endpoint 0 */ ;
+	} else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+		if (endpoint->tx_packetSize) {
+			/* we have a valid tx endpoint */
+			/*usbd_flush_tx(endpoint); */
+			endpoint->tx_urb = NULL;
+		}
+	} else {
+		if (endpoint->rcv_packetSize) {
+			/* we have a valid rx endpoint */
+			/*usbd_flush_rcv(endpoint); */
+			endpoint->rcv_urb = NULL;
+		}
+	}
+}
+#endif
+
+/* ************************************************************************** */
+
+/**
+ * udc_connected - is the USB cable connected
+ *
+ * Return non-zero if cable is connected.
+ */
+#if 0
+int udc_connected (void)
+{
+	return ((inw (UDC_DEVSTAT) & UDC_ATT) == UDC_ATT);
+}
+#endif
+
+/* Turn on the USB connection by enabling the pullup resistor */
+void udc_connect (void)
+{
+	UDCDBG ("connect, enable Pullup");
+	outl (0x00000018, FUNC_MUX_CTRL_D);
+}
+
+/* Turn off the USB connection by disabling the pullup resistor */
+void udc_disconnect (void)
+{
+	UDCDBG ("disconnect, disable Pullup");
+	outl (0x00000000, FUNC_MUX_CTRL_D);
+}
+
+/* ************************************************************************** */
+
+
+/*
+ * udc_disable_interrupts - disable interrupts
+ * switch off interrupts
+ */
+#if 0
+void udc_disable_interrupts (struct usb_device_instance *device)
+{
+	UDCDBG ("disabling all interrupts");
+	outw (0, UDC_IRQ_EN);
+}
+#endif
+
+/* ************************************************************************** */
+
+/**
+ * udc_ep0_packetsize - return ep0 packetsize
+ */
+#if 0
+int udc_ep0_packetsize (void)
+{
+	return EP0_PACKETSIZE;
+}
+#endif
+
+/* Switch on the UDC */
+void udc_enable (struct usb_device_instance *device)
+{
+	UDCDBGA ("enable device %p, status %d", device, device->status);
+
+	/* initialize driver state variables */
+	udc_devstat = 0;
+
+	/* Save the device structure pointer */
+	udc_device = device;
+
+	/* Setup ep0 urb */
+	if (!ep0_urb) {
+		ep0_urb =
+			usbd_alloc_urb (udc_device,
+					udc_device->bus->endpoint_array);
+	} else {
+		serial_printf ("udc_enable: ep0_urb already allocated %p\n",
+			       ep0_urb);
+	}
+
+	UDCDBG ("Check clock status");
+	UDCREG (STATUS_REQ);
+
+	/* The VBUS_MODE bit selects whether VBUS detection is done via
+	 * software (1) or hardware (0).  When software detection is
+	 * selected, VBUS_CTRL selects whether USB is not connected (0)
+	 * or connected (1).
+	 */
+	outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_CTRL | UDC_VBUS_MODE,
+	      FUNC_MUX_CTRL_0);
+	UDCREGL (FUNC_MUX_CTRL_0);
+
+	omap1510_configure_device (device);
+}
+
+/* Switch off the UDC */
+void udc_disable (void)
+{
+	UDCDBG ("disable UDC");
+
+	omap1510_deconfigure_device ();
+
+	/* The VBUS_MODE bit selects whether VBUS detection is done via
+	 * software (1) or hardware (0).  When software detection is
+	 * selected, VBUS_CTRL selects whether USB is not connected (0)
+	 * or connected (1).
+	 */
+	outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0);
+	outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0);
+	UDCREGL (FUNC_MUX_CTRL_0);
+
+	/* Free ep0 URB */
+	if (ep0_urb) {
+		/*usbd_dealloc_urb(ep0_urb); */
+		ep0_urb = NULL;
+	}
+
+	/* Reset device pointer.
+	 * We ought to do this here to balance the initialization of udc_device
+	 * in udc_enable, but some of our other exported functions get called
+	 * by the bus interface driver after udc_disable, so we have to hang on
+	 * to the device pointer to avoid a null pointer dereference. */
+	/* udc_device = NULL; */
+}
+
+/**
+ * udc_startup - allow udc code to do any additional startup
+ */
+void udc_startup_events (struct usb_device_instance *device)
+{
+	/* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
+	usbd_device_event_irq (device, DEVICE_INIT, 0);
+
+	/* The DEVICE_CREATE event puts the USB device in the state
+	 * STATE_ATTACHED.
+	 */
+	usbd_device_event_irq (device, DEVICE_CREATE, 0);
+
+	/* Some USB controller driver implementations signal
+	 * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here.
+	 * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED,
+	 * and DEVICE_RESET causes a transition to the state STATE_DEFAULT.
+	 * The OMAP USB client controller has the capability to detect when the
+	 * USB cable is connected to a powered USB bus via the ATT bit in the
+	 * DEVSTAT register, so we will defer the DEVICE_HUB_CONFIGURED and
+	 * DEVICE_RESET events until later.
+	 */
+
+	udc_enable (device);
+}
+
+/**
+ * udc_irq - do pseudo interrupts
+ */
+void udc_irq(void)
+{
+	/* Loop while we have interrupts.
+	 * If we don't do this, the input chain
+	 * polling delay is likely to miss
+	 * host requests.
+	 */
+	while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) {
+		/* Handle any new IRQs */
+		omap1510_udc_irq ();
+		omap1510_udc_noniso_irq ();
+	}
+}
+
+/* Flow control */
+void udc_set_nak(int epid)
+{
+	/* TODO: implement this functionality in omap1510 */
+}
+
+void udc_unset_nak (int epid)
+{
+	/* TODO: implement this functionality in omap1510 */
+}
+#endif