drivers/usb: regorganisation

move to linux usb driver organisation

as following

drivers/usb/gadget
drivers/usb/host
drivers/usb/musb

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Remy Bohmer <linux@bohmer.net>
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
new file mode 100644
index 0000000..ec1d689
--- /dev/null
+++ b/drivers/usb/host/Makefile
@@ -0,0 +1,59 @@
+#
+# (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_host.a
+
+# ohci
+COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o
+COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o
+COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
+COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
+COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx-hcd.o
+COBJS-$(CONFIG_USB_SL811HS) += sl811-hcd.o
+
+# echi
+COBJS-$(CONFIG_USB_EHCI) += ehci-hcd.o
+COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
+COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o
+COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
+COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.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/host/ehci-core.h b/drivers/usb/host/ehci-core.h
new file mode 100644
index 0000000..39e5c5e
--- /dev/null
+++ b/drivers/usb/host/ehci-core.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2007-2008, Juniper Networks, Inc.
+ * Copyright (c) 2008, Excito Elektronik i Skåne AB
+ * All rights reserved.
+ *
+ * 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 version 2 of
+ * the License.
+ *
+ * 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
+ */
+
+#ifndef USB_EHCI_CORE_H
+#define USB_EHCI_CORE_H
+
+extern int rootdev;
+extern struct ehci_hccr *hccr;
+extern volatile struct ehci_hcor *hcor;
+
+#endif
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
new file mode 100644
index 0000000..86ee1d5
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.c
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
+ *
+ * Author: Tor Krill tor@excito.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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <pci.h>
+#include <usb.h>
+#include <mpc83xx.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "ehci.h"
+#include "ehci-fsl.h"
+#include "ehci-core.h"
+
+/*
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ *
+ * Excerpts from linux ehci fsl driver.
+ */
+int ehci_hcd_init(void)
+{
+	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+	uint32_t addr, temp;
+
+	addr = (uint32_t)&(im->usb[0]);
+	hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI);
+	hcor = (struct ehci_hcor *)((uint32_t) hccr +
+			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+	/* Configure clock */
+	clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK,
+			MPC83XX_SCCR_USB_DRCM_11);
+
+	/* Confgure interface. */
+	temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
+	out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp
+		 | REFSEL_16MHZ | UTMI_PHY_EN);
+
+	/* Wait for clock to stabilize */
+	do {
+		temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
+		udelay(1000);
+	} while (!(temp & PHY_CLK_VALID));
+
+	/* Set to Host mode */
+	temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE));
+	out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST);
+
+	out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB);
+	out_be32((void *)(addr + FSL_SOC_USB_SNOOP2),
+		 0x80000000 | SNOOP_SIZE_2GB);
+
+	/* Init phy */
+	/* TODO: handle different phys? */
+	out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI);
+
+	/* Enable interface. */
+	temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
+	out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN);
+
+	out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c);
+	out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040);
+	out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001);
+
+	/* Enable interface. */
+	temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
+	out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN);
+
+	temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE));
+
+	return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the the EHCI host controller.
+ */
+int ehci_hcd_stop(void)
+{
+	return 0;
+}
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
new file mode 100644
index 0000000..c429af1
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2005 freescale semiconductor
+ * Copyright (c) 2005 MontaVista Software
+ * Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB
+ *
+ * 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
+ */
+
+#ifndef _EHCI_FSL_H
+#define _EHCI_FSL_H
+
+/* Global offsets */
+#define FSL_SKIP_PCI		0x100
+
+/* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_ULPIVP	0x170
+#define FSL_SOC_USB_PORTSC1	0x184
+#define PORT_PTS_MSK		(3 << 30)
+#define PORT_PTS_UTMI		(0 << 30)
+#define PORT_PTS_ULPI		(2 << 30)
+#define PORT_PTS_SERIAL		(3 << 30)
+#define PORT_PTS_PTW		(1 << 28)
+
+/* USBMODE Register bits */
+#define CM_IDLE			(0 << 0)
+#define CM_RESERVED		(1 << 0)
+#define CM_DEVICE		(2 << 0)
+#define CM_HOST			(3 << 0)
+#define USBMODE_RESERVED_2	(0 << 2)
+#define SLOM			(1 << 3)
+#define SDIS			(1 << 4)
+
+/* CONTROL Register bits */
+#define ULPI_INT_EN		(1 << 0)
+#define WU_INT_EN		(1 << 1)
+#define USB_EN			(1 << 2)
+#define LSF_EN			(1 << 3)
+#define KEEP_OTG_ON		(1 << 4)
+#define OTG_PORT		(1 << 5)
+#define REFSEL_12MHZ		(0 << 6)
+#define REFSEL_16MHZ		(1 << 6)
+#define REFSEL_48MHZ		(2 << 6)
+#define PLL_RESET		(1 << 8)
+#define UTMI_PHY_EN		(1 << 9)
+#define PHY_CLK_SEL_UTMI	(0 << 10)
+#define PHY_CLK_SEL_ULPI	(1 << 10)
+#define CLKIN_SEL_USB_CLK	(0 << 11)
+#define CLKIN_SEL_USB_CLK2	(1 << 11)
+#define CLKIN_SEL_SYS_CLK	(2 << 11)
+#define CLKIN_SEL_SYS_CLK2	(3 << 11)
+#define RESERVED_18		(0 << 13)
+#define RESERVED_17		(0 << 14)
+#define RESERVED_16		(0 << 15)
+#define WU_INT			(1 << 16)
+#define PHY_CLK_VALID		(1 << 17)
+
+#define FSL_SOC_USB_PORTSC2	0x188
+#define FSL_SOC_USB_USBMODE	0x1a8
+#define FSL_SOC_USB_SNOOP1	0x400	/* NOTE: big-endian */
+#define FSL_SOC_USB_SNOOP2	0x404	/* NOTE: big-endian */
+#define FSL_SOC_USB_AGECNTTHRSH	0x408	/* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL	0x40c	/* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL	0x410	/* NOTE: big-endian */
+#define FSL_SOC_USB_CTRL	0x500	/* NOTE: big-endian */
+#define SNOOP_SIZE_2GB		0x1e
+
+/* System Clock Control Register */
+#define MPC83XX_SCCR_USB_MASK		0x00f00000
+#define MPC83XX_SCCR_USB_DRCM_11	0x00300000
+#define MPC83XX_SCCR_USB_DRCM_01	0x00100000
+#define MPC83XX_SCCR_USB_DRCM_10	0x00200000
+
+#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
new file mode 100644
index 0000000..bbd547b
--- /dev/null
+++ b/drivers/usb/host/ehci-hcd.c
@@ -0,0 +1,883 @@
+/*-
+ * Copyright (c) 2007-2008, Juniper Networks, Inc.
+ * Copyright (c) 2008, Excito Elektronik i Skåne AB
+ * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
+ *
+ * All rights reserved.
+ *
+ * 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 version 2 of
+ * the License.
+ *
+ * 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>
+#include <asm/byteorder.h>
+#include <usb.h>
+#include <asm/io.h>
+#include <malloc.h>
+
+#include "ehci.h"
+
+int rootdev;
+struct ehci_hccr *hccr;	/* R/O registers, not need for volatile */
+volatile struct ehci_hcor *hcor;
+
+static uint16_t portreset;
+static struct QH qh_list __attribute__((aligned(32)));
+
+static struct descriptor {
+	struct usb_hub_descriptor hub;
+	struct usb_device_descriptor device;
+	struct usb_linux_config_descriptor config;
+	struct usb_linux_interface_descriptor interface;
+	struct usb_endpoint_descriptor endpoint;
+}  __attribute__ ((packed)) descriptor = {
+	{
+		0x8,		/* bDescLength */
+		0x29,		/* bDescriptorType: hub descriptor */
+		2,		/* bNrPorts -- runtime modified */
+		0,		/* wHubCharacteristics */
+		0xff,		/* bPwrOn2PwrGood */
+		0,		/* bHubCntrCurrent */
+		{},		/* Device removable */
+		{}		/* at most 7 ports! XXX */
+	},
+	{
+		0x12,		/* bLength */
+		1,		/* bDescriptorType: UDESC_DEVICE */
+		0x0002,		/* bcdUSB: v2.0 */
+		9,		/* bDeviceClass: UDCLASS_HUB */
+		0,		/* bDeviceSubClass: UDSUBCLASS_HUB */
+		1,		/* bDeviceProtocol: UDPROTO_HSHUBSTT */
+		64,		/* bMaxPacketSize: 64 bytes */
+		0x0000,		/* idVendor */
+		0x0000,		/* idProduct */
+		0x0001,		/* bcdDevice */
+		1,		/* iManufacturer */
+		2,		/* iProduct */
+		0,		/* iSerialNumber */
+		1		/* bNumConfigurations: 1 */
+	},
+	{
+		0x9,
+		2,		/* bDescriptorType: UDESC_CONFIG */
+		cpu_to_le16(0x19),
+		1,		/* bNumInterface */
+		1,		/* bConfigurationValue */
+		0,		/* iConfiguration */
+		0x40,		/* bmAttributes: UC_SELF_POWER */
+		0		/* bMaxPower */
+	},
+	{
+		0x9,		/* bLength */
+		4,		/* bDescriptorType: UDESC_INTERFACE */
+		0,		/* bInterfaceNumber */
+		0,		/* bAlternateSetting */
+		1,		/* bNumEndpoints */
+		9,		/* bInterfaceClass: UICLASS_HUB */
+		0,		/* bInterfaceSubClass: UISUBCLASS_HUB */
+		0,		/* bInterfaceProtocol: UIPROTO_HSHUBSTT */
+		0		/* iInterface */
+	},
+	{
+		0x7,		/* bLength */
+		5,		/* bDescriptorType: UDESC_ENDPOINT */
+		0x81,		/* bEndpointAddress:
+				 * UE_DIR_IN | EHCI_INTR_ENDPT
+				 */
+		3,		/* bmAttributes: UE_INTERRUPT */
+		8, 0,		/* wMaxPacketSize */
+		255		/* bInterval */
+	},
+};
+
+#if defined(CONFIG_EHCI_IS_TDI)
+#define ehci_is_TDI()	(1)
+#else
+#define ehci_is_TDI()	(0)
+#endif
+
+#if defined(CONFIG_EHCI_DCACHE)
+/*
+ * Routines to handle (flush/invalidate) the dcache for the QH and qTD
+ * structures and data buffers. This is needed on platforms using this
+ * EHCI support with dcache enabled.
+ */
+static void flush_invalidate(u32 addr, int size, int flush)
+{
+	if (flush)
+		flush_dcache_range(addr, addr + size);
+	else
+		invalidate_dcache_range(addr, addr + size);
+}
+
+static void cache_qtd(struct qTD *qtd, int flush)
+{
+	u32 *ptr = (u32 *)qtd->qt_buffer[0];
+	int len = (qtd->qt_token & 0x7fff0000) >> 16;
+
+	flush_invalidate((u32)qtd, sizeof(struct qTD), flush);
+	if (ptr && len)
+		flush_invalidate((u32)ptr, len, flush);
+}
+
+
+static inline struct QH *qh_addr(struct QH *qh)
+{
+	return (struct QH *)((u32)qh & 0xffffffe0);
+}
+
+static void cache_qh(struct QH *qh, int flush)
+{
+	struct qTD *qtd;
+	struct qTD *next;
+	static struct qTD *first_qtd;
+
+	/*
+	 * Walk the QH list and flush/invalidate all entries
+	 */
+	while (1) {
+		flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush);
+		if ((u32)qh & QH_LINK_TYPE_QH)
+			break;
+		qh = qh_addr(qh);
+		qh = (struct QH *)qh->qh_link;
+	}
+	qh = qh_addr(qh);
+
+	/*
+	 * Save first qTD pointer, needed for invalidating pass on this QH
+	 */
+	if (flush)
+		first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay &
+						 0xffffffe0);
+	else
+		qtd = first_qtd;
+
+	/*
+	 * Walk the qTD list and flush/invalidate all entries
+	 */
+	while (1) {
+		if (qtd == NULL)
+			break;
+		cache_qtd(qtd, flush);
+		next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0);
+		if (next == qtd)
+			break;
+		qtd = next;
+	}
+}
+
+static inline void ehci_flush_dcache(struct QH *qh)
+{
+	cache_qh(qh, 1);
+}
+
+static inline void ehci_invalidate_dcache(struct QH *qh)
+{
+	cache_qh(qh, 0);
+}
+#else /* CONFIG_EHCI_DCACHE */
+/*
+ *
+ */
+static inline void ehci_flush_dcache(struct QH *qh)
+{
+}
+
+static inline void ehci_invalidate_dcache(struct QH *qh)
+{
+}
+#endif /* CONFIG_EHCI_DCACHE */
+
+static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
+{
+	uint32_t result;
+	do {
+		result = ehci_readl(ptr);
+		if (result == ~(uint32_t)0)
+			return -1;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay(1);
+		usec--;
+	} while (usec > 0);
+	return -1;
+}
+
+static void ehci_free(void *p, size_t sz)
+{
+
+}
+
+static int ehci_reset(void)
+{
+	uint32_t cmd;
+	uint32_t tmp;
+	uint32_t *reg_ptr;
+	int ret = 0;
+
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	cmd |= CMD_RESET;
+	ehci_writel(&hcor->or_usbcmd, cmd);
+	ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000);
+	if (ret < 0) {
+		printf("EHCI fail to reset\n");
+		goto out;
+	}
+
+	if (ehci_is_TDI()) {
+		reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE);
+		tmp = ehci_readl(reg_ptr);
+		tmp |= USBMODE_CM_HC;
+#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
+		tmp |= USBMODE_BE;
+#endif
+		ehci_writel(reg_ptr, tmp);
+	}
+out:
+	return ret;
+}
+
+static void *ehci_alloc(size_t sz, size_t align)
+{
+	static struct QH qh __attribute__((aligned(32)));
+	static struct qTD td[3] __attribute__((aligned (32)));
+	static int ntds;
+	void *p;
+
+	switch (sz) {
+	case sizeof(struct QH):
+		p = &qh;
+		ntds = 0;
+		break;
+	case sizeof(struct qTD):
+		if (ntds == 3) {
+			debug("out of TDs\n");
+			return NULL;
+		}
+		p = &td[ntds];
+		ntds++;
+		break;
+	default:
+		debug("unknown allocation size\n");
+		return NULL;
+	}
+
+	memset(p, sz, 0);
+	return p;
+}
+
+static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
+{
+	uint32_t addr, delta, next;
+	int idx;
+
+	addr = (uint32_t) buf;
+	idx = 0;
+	while (idx < 5) {
+		td->qt_buffer[idx] = cpu_to_hc32(addr);
+		next = (addr + 4096) & ~4095;
+		delta = next - addr;
+		if (delta >= sz)
+			break;
+		sz -= delta;
+		addr = next;
+		idx++;
+	}
+
+	if (idx == 5) {
+		debug("out of buffer pointers (%u bytes left)\n", sz);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
+		   int length, struct devrequest *req)
+{
+	struct QH *qh;
+	struct qTD *td;
+	volatile struct qTD *vtd;
+	unsigned long ts;
+	uint32_t *tdp;
+	uint32_t endpt, token, usbsts;
+	uint32_t c, toggle;
+	uint32_t cmd;
+	int ret = 0;
+
+	debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
+	      buffer, length, req);
+	if (req != NULL)
+		debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
+		      req->request, req->request,
+		      req->requesttype, req->requesttype,
+		      le16_to_cpu(req->value), le16_to_cpu(req->value),
+		      le16_to_cpu(req->index));
+
+	qh = ehci_alloc(sizeof(struct QH), 32);
+	if (qh == NULL) {
+		debug("unable to allocate QH\n");
+		return -1;
+	}
+	qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
+	c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
+	     usb_pipeendpoint(pipe) == 0) ? 1 : 0;
+	endpt = (8 << 28) |
+	    (c << 27) |
+	    (usb_maxpacket(dev, pipe) << 16) |
+	    (0 << 15) |
+	    (1 << 14) |
+	    (usb_pipespeed(pipe) << 12) |
+	    (usb_pipeendpoint(pipe) << 8) |
+	    (0 << 7) | (usb_pipedevice(pipe) << 0);
+	qh->qh_endpt1 = cpu_to_hc32(endpt);
+	endpt = (1 << 30) |
+	    (dev->portnr << 23) |
+	    (dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
+	qh->qh_endpt2 = cpu_to_hc32(endpt);
+	qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+	qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+
+	td = NULL;
+	tdp = &qh->qh_overlay.qt_next;
+
+	toggle =
+	    usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
+	if (req != NULL) {
+		td = ehci_alloc(sizeof(struct qTD), 32);
+		if (td == NULL) {
+			debug("unable to allocate SETUP td\n");
+			goto fail;
+		}
+		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+		token = (0 << 31) |
+		    (sizeof(*req) << 16) |
+		    (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
+		td->qt_token = cpu_to_hc32(token);
+		if (ehci_td_buffer(td, req, sizeof(*req)) != 0) {
+			debug("unable construct SETUP td\n");
+			ehci_free(td, sizeof(*td));
+			goto fail;
+		}
+		*tdp = cpu_to_hc32((uint32_t) td);
+		tdp = &td->qt_next;
+		toggle = 1;
+	}
+
+	if (length > 0 || req == NULL) {
+		td = ehci_alloc(sizeof(struct qTD), 32);
+		if (td == NULL) {
+			debug("unable to allocate DATA td\n");
+			goto fail;
+		}
+		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+		token = (toggle << 31) |
+		    (length << 16) |
+		    ((req == NULL ? 1 : 0) << 15) |
+		    (0 << 12) |
+		    (3 << 10) |
+		    ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
+		td->qt_token = cpu_to_hc32(token);
+		if (ehci_td_buffer(td, buffer, length) != 0) {
+			debug("unable construct DATA td\n");
+			ehci_free(td, sizeof(*td));
+			goto fail;
+		}
+		*tdp = cpu_to_hc32((uint32_t) td);
+		tdp = &td->qt_next;
+	}
+
+	if (req != NULL) {
+		td = ehci_alloc(sizeof(struct qTD), 32);
+		if (td == NULL) {
+			debug("unable to allocate ACK td\n");
+			goto fail;
+		}
+		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+		token = (toggle << 31) |
+		    (0 << 16) |
+		    (1 << 15) |
+		    (0 << 12) |
+		    (3 << 10) |
+		    ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
+		td->qt_token = cpu_to_hc32(token);
+		*tdp = cpu_to_hc32((uint32_t) td);
+		tdp = &td->qt_next;
+	}
+
+	qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);
+
+	/* Flush dcache */
+	ehci_flush_dcache(&qh_list);
+
+	usbsts = ehci_readl(&hcor->or_usbsts);
+	ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));
+
+	/* Enable async. schedule. */
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	cmd |= CMD_ASE;
+	ehci_writel(&hcor->or_usbcmd, cmd);
+
+	ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
+			100 * 1000);
+	if (ret < 0) {
+		printf("EHCI fail timeout STD_ASS set\n");
+		goto fail;
+	}
+
+	/* Wait for TDs to be processed. */
+	ts = get_timer(0);
+	vtd = td;
+	do {
+		/* Invalidate dcache */
+		ehci_invalidate_dcache(&qh_list);
+		token = hc32_to_cpu(vtd->qt_token);
+		if (!(token & 0x80))
+			break;
+	} while (get_timer(ts) < CONFIG_SYS_HZ);
+
+	/* Disable async schedule. */
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	cmd &= ~CMD_ASE;
+	ehci_writel(&hcor->or_usbcmd, cmd);
+
+	ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
+			100 * 1000);
+	if (ret < 0) {
+		printf("EHCI fail timeout STD_ASS reset\n");
+		goto fail;
+	}
+
+	qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
+
+	token = hc32_to_cpu(qh->qh_overlay.qt_token);
+	if (!(token & 0x80)) {
+		debug("TOKEN=%#x\n", token);
+		switch (token & 0xfc) {
+		case 0:
+			toggle = token >> 31;
+			usb_settoggle(dev, usb_pipeendpoint(pipe),
+				       usb_pipeout(pipe), toggle);
+			dev->status = 0;
+			break;
+		case 0x40:
+			dev->status = USB_ST_STALLED;
+			break;
+		case 0xa0:
+		case 0x20:
+			dev->status = USB_ST_BUF_ERR;
+			break;
+		case 0x50:
+		case 0x10:
+			dev->status = USB_ST_BABBLE_DET;
+			break;
+		default:
+			dev->status = USB_ST_CRC_ERR;
+			break;
+		}
+		dev->act_len = length - ((token >> 16) & 0x7fff);
+	} else {
+		dev->act_len = 0;
+		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
+		      dev->devnum, ehci_readl(&hcor->or_usbsts),
+		      ehci_readl(&hcor->or_portsc[0]),
+		      ehci_readl(&hcor->or_portsc[1]));
+	}
+
+	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
+
+fail:
+	td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
+	while (td != (void *)QT_NEXT_TERMINATE) {
+		qh->qh_overlay.qt_next = td->qt_next;
+		ehci_free(td, sizeof(*td));
+		td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
+	}
+	ehci_free(qh, sizeof(*qh));
+	return -1;
+}
+
+static inline int min3(int a, int b, int c)
+{
+
+	if (b < a)
+		a = b;
+	if (c < a)
+		a = c;
+	return a;
+}
+
+int
+ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
+		 int length, struct devrequest *req)
+{
+	uint8_t tmpbuf[4];
+	u16 typeReq;
+	void *srcptr = NULL;
+	int len, srclen;
+	uint32_t reg;
+	uint32_t *status_reg;
+
+	if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
+		printf("The request port(%d) is not configured\n",
+			le16_to_cpu(req->index) - 1);
+		return -1;
+	}
+	status_reg = (uint32_t *)&hcor->or_portsc[
+						le16_to_cpu(req->index) - 1];
+	srclen = 0;
+
+	debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
+	      req->request, req->request,
+	      req->requesttype, req->requesttype,
+	      le16_to_cpu(req->value), le16_to_cpu(req->index));
+
+	typeReq = req->request << 8 | req->requesttype;
+
+	switch (le16_to_cpu(typeReq)) {
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		switch (le16_to_cpu(req->value) >> 8) {
+		case USB_DT_DEVICE:
+			debug("USB_DT_DEVICE request\n");
+			srcptr = &descriptor.device;
+			srclen = 0x12;
+			break;
+		case USB_DT_CONFIG:
+			debug("USB_DT_CONFIG config\n");
+			srcptr = &descriptor.config;
+			srclen = 0x19;
+			break;
+		case USB_DT_STRING:
+			debug("USB_DT_STRING config\n");
+			switch (le16_to_cpu(req->value) & 0xff) {
+			case 0:	/* Language */
+				srcptr = "\4\3\1\0";
+				srclen = 4;
+				break;
+			case 1:	/* Vendor */
+				srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
+				srclen = 14;
+				break;
+			case 2:	/* Product */
+				srcptr = "\52\3E\0H\0C\0I\0 "
+					 "\0H\0o\0s\0t\0 "
+					 "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0";
+				srclen = 42;
+				break;
+			default:
+				debug("unknown value DT_STRING %x\n",
+					le16_to_cpu(req->value));
+				goto unknown;
+			}
+			break;
+		default:
+			debug("unknown value %x\n", le16_to_cpu(req->value));
+			goto unknown;
+		}
+		break;
+	case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8):
+		switch (le16_to_cpu(req->value) >> 8) {
+		case USB_DT_HUB:
+			debug("USB_DT_HUB config\n");
+			srcptr = &descriptor.hub;
+			srclen = 0x8;
+			break;
+		default:
+			debug("unknown value %x\n", le16_to_cpu(req->value));
+			goto unknown;
+		}
+		break;
+	case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8):
+		debug("USB_REQ_SET_ADDRESS\n");
+		rootdev = le16_to_cpu(req->value);
+		break;
+	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+		debug("USB_REQ_SET_CONFIGURATION\n");
+		/* Nothing to do */
+		break;
+	case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8):
+		tmpbuf[0] = 1;	/* USB_STATUS_SELFPOWERED */
+		tmpbuf[1] = 0;
+		srcptr = tmpbuf;
+		srclen = 2;
+		break;
+	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
+		memset(tmpbuf, 0, 4);
+		reg = ehci_readl(status_reg);
+		if (reg & EHCI_PS_CS)
+			tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
+		if (reg & EHCI_PS_PE)
+			tmpbuf[0] |= USB_PORT_STAT_ENABLE;
+		if (reg & EHCI_PS_SUSP)
+			tmpbuf[0] |= USB_PORT_STAT_SUSPEND;
+		if (reg & EHCI_PS_OCA)
+			tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT;
+		if (reg & EHCI_PS_PR &&
+		    (portreset & (1 << le16_to_cpu(req->index)))) {
+			int ret;
+			/* force reset to complete */
+			reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR);
+			ehci_writel(status_reg, reg);
+			ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000);
+			if (!ret)
+				tmpbuf[0] |= USB_PORT_STAT_RESET;
+			else
+				printf("port(%d) reset error\n",
+					le16_to_cpu(req->index) - 1);
+		}
+		if (reg & EHCI_PS_PP)
+			tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
+
+		if (ehci_is_TDI()) {
+			switch ((reg >> 26) & 3) {
+			case 0:
+				break;
+			case 1:
+				tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
+				break;
+			case 2:
+			default:
+				tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
+				break;
+			}
+		} else {
+			tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
+		}
+
+		if (reg & EHCI_PS_CSC)
+			tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
+		if (reg & EHCI_PS_PEC)
+			tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
+		if (reg & EHCI_PS_OCC)
+			tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
+		if (portreset & (1 << le16_to_cpu(req->index)))
+			tmpbuf[2] |= USB_PORT_STAT_C_RESET;
+
+		srcptr = tmpbuf;
+		srclen = 4;
+		break;
+	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
+		reg = ehci_readl(status_reg);
+		reg &= ~EHCI_PS_CLEAR;
+		switch (le16_to_cpu(req->value)) {
+		case USB_PORT_FEAT_ENABLE:
+			reg |= EHCI_PS_PE;
+			ehci_writel(status_reg, reg);
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) {
+				reg |= EHCI_PS_PP;
+				ehci_writel(status_reg, reg);
+			}
+			break;
+		case USB_PORT_FEAT_RESET:
+			if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS &&
+			    !ehci_is_TDI() &&
+			    EHCI_PS_IS_LOWSPEED(reg)) {
+				/* Low speed device, give up ownership. */
+				debug("port %d low speed --> companion\n",
+				      req->index - 1);
+				reg |= EHCI_PS_PO;
+				ehci_writel(status_reg, reg);
+				break;
+			} else {
+				reg |= EHCI_PS_PR;
+				reg &= ~EHCI_PS_PE;
+				ehci_writel(status_reg, reg);
+				/*
+				 * caller must wait, then call GetPortStatus
+				 * usb 2.0 specification say 50 ms resets on
+				 * root
+				 */
+				wait_ms(50);
+				portreset |= 1 << le16_to_cpu(req->index);
+			}
+			break;
+		default:
+			debug("unknown feature %x\n", le16_to_cpu(req->value));
+			goto unknown;
+		}
+		/* unblock posted writes */
+		ehci_readl(&hcor->or_usbcmd);
+		break;
+	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
+		reg = ehci_readl(status_reg);
+		switch (le16_to_cpu(req->value)) {
+		case USB_PORT_FEAT_ENABLE:
+			reg &= ~EHCI_PS_PE;
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE;
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams)))
+				reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP);
+		case USB_PORT_FEAT_C_CONNECTION:
+			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC;
+			break;
+		case USB_PORT_FEAT_OVER_CURRENT:
+			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
+			break;
+		case USB_PORT_FEAT_C_RESET:
+			portreset &= ~(1 << le16_to_cpu(req->index));
+			break;
+		default:
+			debug("unknown feature %x\n", le16_to_cpu(req->value));
+			goto unknown;
+		}
+		ehci_writel(status_reg, reg);
+		/* unblock posted write */
+		ehci_readl(&hcor->or_usbcmd);
+		break;
+	default:
+		debug("Unknown request\n");
+		goto unknown;
+	}
+
+	wait_ms(1);
+	len = min3(srclen, le16_to_cpu(req->length), length);
+	if (srcptr != NULL && len > 0)
+		memcpy(buffer, srcptr, len);
+	else
+		debug("Len is 0\n");
+
+	dev->act_len = len;
+	dev->status = 0;
+	return 0;
+
+unknown:
+	debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n",
+	      req->requesttype, req->request, le16_to_cpu(req->value),
+	      le16_to_cpu(req->index), le16_to_cpu(req->length));
+
+	dev->act_len = 0;
+	dev->status = USB_ST_STALLED;
+	return -1;
+}
+
+int usb_lowlevel_stop(void)
+{
+	return ehci_hcd_stop();
+}
+
+int usb_lowlevel_init(void)
+{
+	uint32_t reg;
+	uint32_t cmd;
+
+	if (ehci_hcd_init() != 0)
+		return -1;
+
+	/* EHCI spec section 4.1 */
+	if (ehci_reset() != 0)
+		return -1;
+
+#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
+	if (ehci_hcd_init() != 0)
+		return -1;
+#endif
+
+	/* Set head of reclaim list */
+	memset(&qh_list, 0, sizeof(qh_list));
+	qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
+	qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12));
+	qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
+	qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+	qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+	qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40);
+
+	/* Set async. queue head pointer. */
+	ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list);
+
+	reg = ehci_readl(&hccr->cr_hcsparams);
+	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
+	printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
+	/* Port Indicators */
+	if (HCS_INDICATOR(reg))
+		descriptor.hub.wHubCharacteristics |= 0x80;
+	/* Port Power Control */
+	if (HCS_PPC(reg))
+		descriptor.hub.wHubCharacteristics |= 0x01;
+
+	/* Start the host controller. */
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	/*
+	 * Philips, Intel, and maybe others need CMD_RUN before the
+	 * root hub will detect new devices (why?); NEC doesn't
+	 */
+	cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+	cmd |= CMD_RUN;
+	ehci_writel(&hcor->or_usbcmd, cmd);
+
+	/* take control over the ports */
+	cmd = ehci_readl(&hcor->or_configflag);
+	cmd |= FLAG_CF;
+	ehci_writel(&hcor->or_configflag, cmd);
+	/* unblock posted write */
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	wait_ms(5);
+	reg = HC_VERSION(ehci_readl(&hccr->cr_capbase));
+	printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
+
+	rootdev = 0;
+
+	return 0;
+}
+
+int
+submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		int length)
+{
+
+	if (usb_pipetype(pipe) != PIPE_BULK) {
+		debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
+		return -1;
+	}
+	return ehci_submit_async(dev, pipe, buffer, length, NULL);
+}
+
+int
+submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		   int length, struct devrequest *setup)
+{
+
+	if (usb_pipetype(pipe) != PIPE_CONTROL) {
+		debug("non-control pipe (type=%lu)", usb_pipetype(pipe));
+		return -1;
+	}
+
+	if (usb_pipedevice(pipe) == rootdev) {
+		if (rootdev == 0)
+			dev->speed = USB_SPEED_HIGH;
+		return ehci_submit_root(dev, pipe, buffer, length, setup);
+	}
+	return ehci_submit_async(dev, pipe, buffer, length, setup);
+}
+
+int
+submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+	       int length, int interval)
+{
+
+	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
+	      dev, pipe, buffer, length, interval);
+	return -1;
+}
diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c
new file mode 100644
index 0000000..b8f15ae
--- /dev/null
+++ b/drivers/usb/host/ehci-ixp4xx.c
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
+ *
+ * Author: Michael Trimarchi <trimarchimichael@yahoo.it>
+ * This code is based on ehci freescale driver
+ *
+ * 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>
+#include <usb.h>
+
+#include "ehci.h"
+#include "ehci-core.h"
+/*
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ */
+int ehci_hcd_init(void)
+{
+	hccr = (struct ehci_hccr *)(0xcd000100);
+	hcor = (struct ehci_hcor *)((uint32_t) hccr
+			+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+	printf("IXP4XX init hccr %x and hcor %x hc_length %d\n",
+		(uint32_t)hccr, (uint32_t)hcor,
+		(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+	return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the the EHCI host controller.
+ */
+int ehci_hcd_stop(void)
+{
+	return 0;
+}
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
new file mode 100644
index 0000000..441b1a2
--- /dev/null
+++ b/drivers/usb/host/ehci-pci.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2007-2008, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * 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 version 2 of
+ * the License.
+ *
+ * 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>
+#include <pci.h>
+#include <usb.h>
+
+#include "ehci.h"
+#include "ehci-core.h"
+
+#ifdef CONFIG_PCI_EHCI_DEVICE
+static struct pci_device_id ehci_pci_ids[] = {
+	/* Please add supported PCI EHCI controller ids here */
+	{0, 0}
+};
+#endif
+
+/*
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ */
+int ehci_hcd_init(void)
+{
+	pci_dev_t pdev;
+	uint32_t addr;
+
+	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
+	if (pdev == -1) {
+		printf("EHCI host controller not found\n");
+		return -1;
+	}
+
+	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr);
+	hccr = (struct ehci_hccr *)addr;
+	hcor = (struct ehci_hcor *)((uint32_t) hccr +
+			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+	return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the the EHCI host controller.
+ */
+int ehci_hcd_stop(void)
+{
+	return 0;
+}
diff --git a/drivers/usb/host/ehci-vct.c b/drivers/usb/host/ehci-vct.c
new file mode 100644
index 0000000..3063dd1
--- /dev/null
+++ b/drivers/usb/host/ehci-vct.c
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 2009 Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * 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>
+#include <usb.h>
+
+#include "ehci.h"
+#include "ehci-core.h"
+
+int vct_ehci_hcd_init(u32 *hccr, u32 *hcor);
+
+/*
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ */
+int ehci_hcd_init(void)
+{
+	int ret;
+	u32 vct_hccr;
+	u32 vct_hcor;
+
+	/*
+	 * Init VCT specific stuff
+	 */
+	ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor);
+	if (ret)
+		return ret;
+
+	hccr = (struct ehci_hccr *)vct_hccr;
+	hcor = (struct ehci_hcor *)vct_hcor;
+
+	return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the the EHCI host controller.
+ */
+int ehci_hcd_stop(void)
+{
+	return 0;
+}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
new file mode 100644
index 0000000..b3c1d5d
--- /dev/null
+++ b/drivers/usb/host/ehci.h
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 2007-2008, Juniper Networks, Inc.
+ * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
+ * All rights reserved.
+ *
+ * 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 version 2 of
+ * the License.
+ *
+ * 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
+ */
+
+#ifndef USB_EHCI_H
+#define USB_EHCI_H
+
+#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS)
+#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS	2
+#endif
+
+/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
+#define DeviceRequest \
+	((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
+
+#define DeviceOutRequest \
+	((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
+
+#define InterfaceRequest \
+	((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
+
+#define EndpointRequest \
+	((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
+
+#define EndpointOutRequest \
+	((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
+
+/*
+ * Register Space.
+ */
+struct ehci_hccr {
+	uint32_t cr_capbase;
+#define HC_LENGTH(p)		(((p) >> 0) & 0x00ff)
+#define HC_VERSION(p)		(((p) >> 16) & 0xffff)
+	uint32_t cr_hcsparams;
+#define HCS_PPC(p)		((p) & (1 << 4))
+#define HCS_INDICATOR(p)	((p) & (1 << 16)) /* Port indicators */
+#define HCS_N_PORTS(p)		(((p) >> 0) & 0xf)
+	uint32_t cr_hccparams;
+	uint8_t cr_hcsp_portrt[8];
+} __attribute__ ((packed));
+
+struct ehci_hcor {
+	uint32_t or_usbcmd;
+#define CMD_PARK	(1 << 11)		/* enable "park" */
+#define CMD_PARK_CNT(c)	(((c) >> 8) & 3)	/* how many transfers to park */
+#define CMD_ASE		(1 << 5)		/* async schedule enable */
+#define CMD_LRESET	(1 << 7)		/* partial reset */
+#define CMD_IAAD	(1 << 5)		/* "doorbell" interrupt */
+#define CMD_PSE		(1 << 4)		/* periodic schedule enable */
+#define CMD_RESET	(1 << 1)		/* reset HC not bus */
+#define CMD_RUN		(1 << 0)		/* start/stop HC */
+	uint32_t or_usbsts;
+#define	STD_ASS		(1 << 15)
+#define STS_HALT	(1 << 12)
+	uint32_t or_usbintr;
+	uint32_t or_frindex;
+	uint32_t or_ctrldssegment;
+	uint32_t or_periodiclistbase;
+	uint32_t or_asynclistaddr;
+	uint32_t _reserved_[9];
+	uint32_t or_configflag;
+#define FLAG_CF		(1 << 0)	/* true:  we'll support "high speed" */
+	uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
+	uint32_t or_systune;
+} __attribute__ ((packed));
+
+#define USBMODE		0x68		/* USB Device mode */
+#define USBMODE_SDIS	(1 << 3)	/* Stream disable */
+#define USBMODE_BE	(1 << 2)	/* BE/LE endiannes select */
+#define USBMODE_CM_HC	(3 << 0)	/* host controller mode */
+#define USBMODE_CM_IDLE	(0 << 0)	/* idle state */
+
+/* Interface descriptor */
+struct usb_linux_interface_descriptor {
+	unsigned char	bLength;
+	unsigned char	bDescriptorType;
+	unsigned char	bInterfaceNumber;
+	unsigned char	bAlternateSetting;
+	unsigned char	bNumEndpoints;
+	unsigned char	bInterfaceClass;
+	unsigned char	bInterfaceSubClass;
+	unsigned char	bInterfaceProtocol;
+	unsigned char	iInterface;
+} __attribute__ ((packed));
+
+/* Configuration descriptor information.. */
+struct usb_linux_config_descriptor {
+	unsigned char	bLength;
+	unsigned char	bDescriptorType;
+	unsigned short	wTotalLength;
+	unsigned char	bNumInterfaces;
+	unsigned char	bConfigurationValue;
+	unsigned char	iConfiguration;
+	unsigned char	bmAttributes;
+	unsigned char	MaxPower;
+} __attribute__ ((packed));
+
+#if defined CONFIG_EHCI_DESC_BIG_ENDIAN
+#define	ehci_readl(x)		(*((volatile u32 *)(x)))
+#define ehci_writel(a, b)	(*((volatile u32 *)(a)) = ((volatile u32)b))
+#else
+#define ehci_readl(x)		cpu_to_le32((*((volatile u32 *)(x))))
+#define ehci_writel(a, b)	(*((volatile u32 *)(a)) = \
+					cpu_to_le32(((volatile u32)b)))
+#endif
+
+#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN
+#define hc32_to_cpu(x)		be32_to_cpu((x))
+#define cpu_to_hc32(x)		cpu_to_be32((x))
+#else
+#define hc32_to_cpu(x)		le32_to_cpu((x))
+#define cpu_to_hc32(x)		cpu_to_le32((x))
+#endif
+
+#define EHCI_PS_WKOC_E		(1 << 22)	/* RW wake on over current */
+#define EHCI_PS_WKDSCNNT_E	(1 << 21)	/* RW wake on disconnect */
+#define EHCI_PS_WKCNNT_E	(1 << 20)	/* RW wake on connect */
+#define EHCI_PS_PO		(1 << 13)	/* RW port owner */
+#define EHCI_PS_PP		(1 << 12)	/* RW,RO port power */
+#define EHCI_PS_LS		(3 << 10)	/* RO line status */
+#define EHCI_PS_PR		(1 << 8)	/* RW port reset */
+#define EHCI_PS_SUSP		(1 << 7)	/* RW suspend */
+#define EHCI_PS_FPR		(1 << 6)	/* RW force port resume */
+#define EHCI_PS_OCC		(1 << 5)	/* RWC over current change */
+#define EHCI_PS_OCA		(1 << 4)	/* RO over current active */
+#define EHCI_PS_PEC		(1 << 3)	/* RWC port enable change */
+#define EHCI_PS_PE		(1 << 2)	/* RW port enable */
+#define EHCI_PS_CSC		(1 << 1)	/* RWC connect status change */
+#define EHCI_PS_CS		(1 << 0)	/* RO connect status */
+#define EHCI_PS_CLEAR		(EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC)
+
+#define EHCI_PS_IS_LOWSPEED(x)	(((x) & EHCI_PS_LS) == (1 << 10))
+
+/*
+ * Schedule Interface Space.
+ *
+ * IMPORTANT: Software must ensure that no interface data structure
+ * reachable by the EHCI host controller spans a 4K page boundary!
+ *
+ * Periodic transfers (i.e. isochronous and interrupt transfers) are
+ * not supported.
+ */
+
+/* Queue Element Transfer Descriptor (qTD). */
+struct qTD {
+	uint32_t qt_next;
+#define	QT_NEXT_TERMINATE	1
+	uint32_t qt_altnext;
+	uint32_t qt_token;
+	uint32_t qt_buffer[5];
+};
+
+/* Queue Head (QH). */
+struct QH {
+	uint32_t qh_link;
+#define	QH_LINK_TERMINATE	1
+#define	QH_LINK_TYPE_ITD	0
+#define	QH_LINK_TYPE_QH		2
+#define	QH_LINK_TYPE_SITD	4
+#define	QH_LINK_TYPE_FSTN	6
+	uint32_t qh_endpt1;
+	uint32_t qh_endpt2;
+	uint32_t qh_curtd;
+	struct qTD qh_overlay;
+	/*
+	 * Add dummy fill value to make the size of this struct
+	 * aligned to 32 bytes
+	 */
+	uint8_t fill[16];
+};
+
+/* Low level init functions */
+int ehci_hcd_init(void);
+int ehci_hcd_stop(void);
+
+#endif /* USB_EHCI_H */
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
new file mode 100644
index 0000000..348e404
--- /dev/null
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -0,0 +1,1441 @@
+/*
+ * 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/usb/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>
+#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_pipebulk(pipe))
+		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 (usb_pipeint(pipe)) {
+		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;
+}
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
new file mode 100644
index 0000000..a3ce3b5
--- /dev/null
+++ b/drivers/usb/host/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/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
new file mode 100644
index 0000000..7c44ad0
--- /dev/null
+++ b/drivers/usb/host/ohci-at91.c
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 2006
+ * DENX Software Engineering <mk@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 <common.h>
+
+#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT)
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/io.h>
+#include <asm/arch/at91_pmc.h>
+
+int usb_cpu_init(void)
+{
+
+#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
+    defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20)
+	/* Enable PLLB */
+	at91_sys_write(AT91_CKGR_PLLBR, CONFIG_SYS_AT91_PLLB);
+	while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB)
+		;
+#endif
+
+	/* Enable USB host clock. */
+	at91_sys_write(AT91_PMC_PCER, 1 << AT91_ID_UHP);
+#ifdef CONFIG_AT91SAM9261
+	at91_sys_write(AT91_PMC_SCER, AT91_PMC_UHP | AT91_PMC_HCK0);
+#else
+	at91_sys_write(AT91_PMC_SCER, AT91_PMC_UHP);
+#endif
+
+	return 0;
+}
+
+int usb_cpu_stop(void)
+{
+	/* Disable USB host clock. */
+	at91_sys_write(AT91_PMC_PCDR, 1 << AT91_ID_UHP);
+#ifdef CONFIG_AT91SAM9261
+	at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP | AT91_PMC_HCK0);
+#else
+	at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP);
+#endif
+
+#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
+    defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20)
+	/* Disable PLLB */
+	at91_sys_write(AT91_CKGR_PLLBR, 0);
+	while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != 0)
+		;
+#endif
+
+	return 0;
+}
+
+int usb_cpu_init_fail(void)
+{
+	return usb_cpu_stop();
+}
+
+#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
new file mode 100644
index 0000000..e2f289e
--- /dev/null
+++ b/drivers/usb/host/ohci-hcd.c
@@ -0,0 +1,2016 @@
+/*
+ * 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>
+#include <asm/byteorder.h>
+
+#if defined(CONFIG_PCI_OHCI)
+# include <pci.h>
+#if !defined(CONFIG_PCI_OHCI_DEVNO)
+#define CONFIG_PCI_OHCI_DEVNO	0
+#endif
+#endif
+
+#include <malloc.h>
+#include <usb.h>
+
+#include "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_S3C6400) || \
+    defined(CONFIG_440EP) || \
+    defined(CONFIG_PCI_OHCI) || \
+    defined(CONFIG_MPC5200) || \
+    defined(CONFIG_SYS_OHCI_USE_NPS)
+# 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 CONFIG_SYS_OHCI_SWAP_REG_ACCESS
+# define readl(a) __swap_32(*((volatile u32 *)(a)))
+# define writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a))
+#else
+# define readl(a) (*((volatile u32 *)(a)))
+# define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a))
+#endif /* CONFIG_SYS_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 */
+	{0x1131, 0x1561},	/* Philips 1561 PCI OHCI module ids */
+	/* Please add supported PCI OHCI controller ids here */
+	{0, 0}
+};
+#endif
+
+#ifdef CONFIG_PCI_EHCI_DEVNO
+static struct pci_device_id ehci_pci_ids[] = {
+	{0x1131, 0x1562},	/* Philips 1562 PCI EHCI module ids */
+	/* Please add supported PCI EHCI 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)
+#ifdef SHOW_INFO
+#define info(format, arg...) printf("INFO: " format "\n", ## arg)
+#else
+#define info(format, arg...) do {} while (0)
+#endif
+
+#ifdef CONFIG_SYS_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 /* CONFIG_SYS_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;
+/* device which was disconnected */
+struct usb_device *devgone;
+
+static inline u32 roothub_a(struct ohci *hc)
+	{ return readl(&hc->regs->roothub.a); }
+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 inline u32 roothub_portstatus(struct ohci *hc, int i)
+	{ return readl(&hc->regs->roothub.portstatus[i]); }
+
+/* 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:%2lu,ep:%2lu-%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 */
+	ed = ep_add_ed(dev, pipe, interval, 1);
+	if (!ed) {
+		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) {
+		/* 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_pipebulk(urb_priv->pipe) && 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) {
+			/* start bulk list */
+			writel(OHCI_BLF, &ohci->regs->cmdstatus);
+		}
+		break;
+
+	case PIPE_CONTROL:
+		/* Setup phase */
+		info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+		td_fill(ohci, info, setup, 8, dev, cnt++, urb);
+
+		/* Optional Data phase */
+		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);
+		}
+
+		/* Status phase */
+		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) {
+			/* start Control list */
+			writel(OHCI_CLF, &ohci->regs->cmdstatus);
+		}
+		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_pipecontrol(lurb_priv->pipe) &&
+	    ((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;
+		}
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+static void check_status(td_t *td_list)
+{
+	urb_priv_t *lurb_priv = td_list->ed->purb;
+	int	   urb_len    = lurb_priv->length;
+	__u32      *phwHeadP  = &td_list->ed->hwHeadP;
+	int	   cc;
+
+	cc = TD_CC_GET(m32_swap(td_list->hwINFO));
+	if (cc) {
+		err(" USB-error: %s (%x)", cc_to_string[cc], cc);
+
+		if (*phwHeadP & m32_swap(0x1)) {
+			if (lurb_priv &&
+			    ((td_list->index + 1) < urb_len)) {
+				*phwHeadP =
+					(lurb_priv->td[urb_len - 1]->hwNextTD &\
+							m32_swap(0xfffffff0)) |
+						   (*phwHeadP & m32_swap(0x2));
+
+				lurb_priv->td_cnt += urb_len -
+						     td_list->index - 1;
+			} else
+				*phwHeadP &= m32_swap(0xfffffff2);
+		}
+#ifdef CONFIG_MPC5200
+		td_list->hwNextTD = 0;
+#endif
+	}
+}
+
+/* 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;
+
+	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;
+		check_status(td_list);
+		td_list->next_dl_td = td_rev;
+		td_rev = td_list;
+		td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0;
+	}
+	return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+
+static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status)
+{
+	if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL))
+		urb->finished = sohci_return_job(ohci, urb);
+	else
+		dbg("finish_urb: strange.., ED state %x, \n", status);
+}
+
+/*
+ * Used to take back a TD from the host controller. This would normally be
+ * called from within dl_done_list, however it may be called directly if the
+ * HC no longer sees the TD and it has not appeared on the donelist (after
+ * two frames).  This bug has been observed on ZF Micro systems.
+ */
+static int takeback_td(ohci_t *ohci, td_t *td_list)
+{
+	ed_t *ed;
+	int cc;
+	int stat = 0;
+	/* urb_t *urb; */
+	urb_priv_t *lurb_priv;
+	__u32 tdINFO, edHeadP, edTailP;
+
+	tdINFO = m32_swap(td_list->hwINFO);
+
+	ed = td_list->ed;
+	lurb_priv = ed->purb;
+
+	dl_transfer_length(td_list);
+
+	lurb_priv->td_cnt++;
+
+	/* error code of transfer */
+	cc = TD_CC_GET(tdINFO);
+	if (cc) {
+		err("USB-error: %s (%x)", cc_to_string[cc], 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)
+		finish_urb(ohci, lurb_priv, ed->state);
+
+	dbg("dl_done_list: processing TD %x, len %x\n",
+		lurb_priv->td_cnt, lurb_priv->length);
+
+	if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) {
+		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);
+	}
+	return stat;
+}
+
+static int dl_done_list(ohci_t *ohci)
+{
+	int stat = 0;
+	td_t	*td_list = dl_reverse_done_list(ohci);
+
+	while (td_list) {
+		td_t	*td_next = td_list->next_dl_td;
+		stat = takeback_td(ohci, td_list);
+		td_list = td_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 (usb_pipeint(pipe)) {
+		info("Root-Hub submit IRQ: NOT implemented");
+		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);
+
+	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_pipebulk(pipe))
+		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_pipeint(pipe))
+		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)
+{
+#ifdef CONFIG_PCI_EHCI_DEVNO
+	pci_dev_t pdev;
+#endif
+	int timeout = 30;
+	int smm_timeout = 50; /* 0,5 sec */
+
+	dbg("%s\n", __FUNCTION__);
+
+#ifdef CONFIG_PCI_EHCI_DEVNO
+	/*
+	 *  Some multi-function controllers (e.g. ISP1562) allow root hub
+	 * resetting via EHCI registers only.
+	 */
+	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO);
+	if (pdev != -1) {
+		u32 base;
+		int timeout = 1000;
+
+		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base);
+		writel(readl(base + EHCI_USBCMD_OFF) | EHCI_USBCMD_HCRESET,
+			base + EHCI_USBCMD_OFF);
+
+		while (readl(base + EHCI_USBCMD_OFF) & EHCI_USBCMD_HCRESET) {
+			if (timeout-- <= 0) {
+				printf("USB RootHub reset timed out!");
+				break;
+			}
+			udelay(1);
+		}
+	} else
+		printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO);
+#endif
+	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 {
+		ints = readl(&regs->intrstatus);
+		if (ints == ~(u32)0) {
+			ohci->disabled++;
+			err("%s device removed!", ohci->slot_name);
+			return -1;
+		} else {
+			ints &= readl(&regs->intrenable);
+			if (ints == 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)
+		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);
+		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 CONFIG_SYS_USB_OHCI_CPU_INIT
+	/* cpu dependant init */
+	if (usb_cpu_init())
+		return -1;
+#endif
+
+#ifdef CONFIG_SYS_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, CONFIG_PCI_OHCI_DEVNO);
+
+	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 *)CONFIG_SYS_USB_OHCI_REGS_BASE;
+#endif
+
+	gohci.flags = 0;
+	gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME;
+
+	if (hc_reset (&gohci) < 0) {
+		hc_release_ohci (&gohci);
+		err ("can't reset usb-%s", gohci.slot_name);
+#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
+		/* board dependant cleanup */
+		usb_board_init_fail();
+#endif
+
+#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
+		/* cpu dependant cleanup */
+		usb_cpu_init_fail();
+#endif
+		return -1;
+	}
+
+	if (hc_start(&gohci) < 0) {
+		err("can't start usb-%s", gohci.slot_name);
+		hc_release_ohci(&gohci);
+		/* Initialization failed */
+#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
+		/* board dependant cleanup */
+		usb_board_stop();
+#endif
+
+#ifdef CONFIG_SYS_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 CONFIG_SYS_USB_OHCI_BOARD_INIT
+	/* board dependant cleanup */
+	if (usb_board_stop())
+		return -1;
+#endif
+
+#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
+	/* cpu dependant cleanup */
+	if (usb_cpu_stop())
+		return -1;
+#endif
+	/* This driver is no longer initialised. It needs a new low-level
+	 * init (board/cpu) before it can be used again. */
+	ohci_inited = 0;
+	return 0;
+}
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
new file mode 100644
index 0000000..a547337
--- /dev/null
+++ b/drivers/usb/host/ohci.h
@@ -0,0 +1,483 @@
+/*
+ * 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
+};
+
+static const char *cc_to_string[16] = {
+	"No Error",
+	"CRC: Last data packet from endpoint contained a CRC error.",
+	"BITSTUFFING: Last data packet from endpoint contained a bit " \
+		     "stuffing violation",
+	"DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \
+		     "that did not match the expected value.",
+	"STALL: TD was moved to the Done Queue because the endpoint returned" \
+		     " a STALL PID",
+	"DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \
+		     "not provide a handshake (OUT)",
+	"PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\
+		     "(IN) or handshake (OUT)",
+	"UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \
+		     "value is not defined.",
+	"DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \
+		     "either the size of the maximum data packet allowed\n" \
+		     "from the endpoint (found in MaximumPacketSize field\n" \
+		     "of ED) or the remaining buffer size.",
+	"DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \
+		     "and that amount was not sufficient to fill the\n" \
+		     "specified buffer",
+	"reserved1",
+	"reserved2",
+	"BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \
+		     "than it could be written to system memory",
+	"BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \
+		     "system memory fast enough to keep up with data USB " \
+		     "data rate.",
+	"NOT ACCESSED: This code is set by software before the TD is placed" \
+		     "on a list to be processed by the HC.(1)",
+	"NOT ACCESSED: This code is set by software before the TD is placed" \
+		     "on a list to be processed by the HC.(2)",
+};
+
+/* 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 CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS
+# error "CONFIG_SYS_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[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS];
+	} roothub;
+} __attribute((aligned(32)));
+
+/* Some EHCI controls */
+#define EHCI_USBCMD_OFF		0x20
+#define EHCI_USBCMD_HCRESET	(1 << 1)
+
+/* 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/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
new file mode 100644
index 0000000..db611b6
--- /dev/null
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -0,0 +1,945 @@
+/*
+ * R8A66597 HCD (Host Controller Driver) for u-boot
+ *
+ * Copyright (C) 2008  Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <common.h>
+#include <usb.h>
+#include <asm/io.h>
+
+#include "r8a66597.h"
+
+#ifdef R8A66597_DEBUG
+#define R8A66597_DPRINT		printf
+#else
+#define R8A66597_DPRINT(...)
+#endif
+
+static const char hcd_name[] = "r8a66597_hcd";
+static unsigned short clock = CONFIG_R8A66597_XTAL;
+static unsigned short vif = CONFIG_R8A66597_LDRV;
+static unsigned short endian = CONFIG_R8A66597_ENDIAN;
+static struct r8a66597 gr8a66597;
+
+static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport)
+{
+	int i;
+
+	*hub_devnum = 0;
+	*hubport = 0;
+
+	/* check a device connected to root_hub */
+	if ((dev->parent && dev->parent->devnum == 1) ||
+	    (dev->devnum == 1))
+		return;
+
+	for (i = 0; i < USB_MAXCHILDREN; i++) {
+		if (dev->parent->children[i] == dev) {
+			*hub_devnum = (u8)dev->parent->devnum;
+			*hubport = i;
+			return;
+		}
+	}
+
+	printf("get_hub_data error.\n");
+}
+
+static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address,
+			struct usb_device *dev, int port)
+{
+	u16 val, usbspd, upphub, hubport;
+	unsigned long devadd_reg = get_devadd_addr(r8a66597_address);
+
+	get_hub_data(dev, &upphub, &hubport);
+	usbspd = r8a66597->speed;
+	val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001);
+	r8a66597_write(r8a66597, val, devadd_reg);
+}
+
+static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
+{
+	u16 tmp;
+	int i = 0;
+
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+	do {
+		r8a66597_write(r8a66597, SCKE, SYSCFG0);
+		tmp = r8a66597_read(r8a66597, SYSCFG0);
+		if (i++ > 1000) {
+			printf("register access fail.\n");
+			return -1;
+		}
+	} while ((tmp & SCKE) != SCKE);
+	r8a66597_write(r8a66597, 0x04, 0x02);
+#else
+	do {
+		r8a66597_write(r8a66597, USBE, SYSCFG0);
+		tmp = r8a66597_read(r8a66597, SYSCFG0);
+		if (i++ > 1000) {
+			printf("register access fail.\n");
+			return -1;
+		}
+	} while ((tmp & USBE) != USBE);
+	r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+	r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0);
+
+	i = 0;
+	r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+	do {
+		udelay(1000);
+		tmp = r8a66597_read(r8a66597, SYSCFG0);
+		if (i++ > 500) {
+			printf("register access fail.\n");
+			return -1;
+		}
+	} while ((tmp & SCKE) != SCKE);
+#endif	/* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
+
+	return 0;
+}
+
+static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
+{
+	r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+	udelay(1);
+#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+	r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+	r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+	r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+#endif
+}
+
+static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port)
+{
+	u16 val;
+
+	val = port ? DRPD : DCFM | DRPD;
+	r8a66597_bset(r8a66597, val, get_syscfg_reg(port));
+	r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
+
+	r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port));
+}
+
+static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
+{
+	u16 val, tmp;
+
+	r8a66597_write(r8a66597, 0, get_intenb_reg(port));
+	r8a66597_write(r8a66597, 0, get_intsts_reg(port));
+
+	r8a66597_port_power(r8a66597, port, 0);
+
+	do {
+		tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
+		udelay(640);
+	} while (tmp == EDGESTS);
+
+	val = port ? DRPD : DCFM | DRPD;
+	r8a66597_bclr(r8a66597, val, get_syscfg_reg(port));
+	r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port));
+}
+
+static int enable_controller(struct r8a66597 *r8a66597)
+{
+	int ret, port;
+
+	ret = r8a66597_clock_enable(r8a66597);
+	if (ret < 0)
+		return ret;
+
+	r8a66597_bset(r8a66597, vif & LDRV, PINCFG);
+	r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+	r8a66597_bset(r8a66597, INTL, SOFCFG);
+	r8a66597_write(r8a66597, 0, INTENB0);
+	r8a66597_write(r8a66597, 0, INTENB1);
+	r8a66597_write(r8a66597, 0, INTENB2);
+
+	r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL);
+	r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL);
+	r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL);
+	r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
+
+	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
+		r8a66597_enable_port(r8a66597, port);
+
+	return 0;
+}
+
+static void disable_controller(struct r8a66597 *r8a66597)
+{
+	int i;
+
+	if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE))
+		return;
+
+	r8a66597_write(r8a66597, 0, INTENB0);
+	r8a66597_write(r8a66597, 0, INTSTS0);
+
+	r8a66597_write(r8a66597, 0, D0FIFOSEL);
+	r8a66597_write(r8a66597, 0, D1FIFOSEL);
+	r8a66597_write(r8a66597, 0, DCPCFG);
+	r8a66597_write(r8a66597, 0x40, DCPMAXP);
+	r8a66597_write(r8a66597, 0, DCPCTR);
+
+	for (i = 0; i <= 10; i++)
+		r8a66597_write(r8a66597, 0, get_devadd_addr(i));
+	for (i = 1; i <= 5; i++) {
+		r8a66597_write(r8a66597, 0, get_pipetre_addr(i));
+		r8a66597_write(r8a66597, 0, get_pipetrn_addr(i));
+	}
+	for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) {
+		r8a66597_write(r8a66597, 0, get_pipectr_addr(i));
+		r8a66597_write(r8a66597, i, PIPESEL);
+		r8a66597_write(r8a66597, 0, PIPECFG);
+		r8a66597_write(r8a66597, 0, PIPEBUF);
+		r8a66597_write(r8a66597, 0, PIPEMAXP);
+		r8a66597_write(r8a66597, 0, PIPEPERI);
+	}
+
+	for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++)
+		r8a66597_disable_port(r8a66597, i);
+
+	r8a66597_clock_disable(r8a66597);
+}
+
+static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,
+			      u16 mask, u16 loop)
+{
+	u16 tmp;
+	int i = 0;
+
+	do {
+		tmp = r8a66597_read(r8a66597, reg);
+		if (i++ > 1000000) {
+			printf("register%lx, loop %x is timeout\n", reg, loop);
+			break;
+		}
+	} while ((tmp & mask) != loop);
+}
+
+static void pipe_buffer_setting(struct r8a66597 *r8a66597,
+				struct usb_device *dev, unsigned long pipe)
+{
+	u16 val = 0;
+	u16 pipenum, bufnum, maxpacket;
+
+	if (usb_pipein(pipe)) {
+		pipenum = BULK_IN_PIPENUM;
+		bufnum = BULK_IN_BUFNUM;
+		maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)];
+	} else {
+		pipenum = BULK_OUT_PIPENUM;
+		bufnum = BULK_OUT_BUFNUM;
+		maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)];
+	}
+
+	if (r8a66597->pipe_config & (1 << pipenum))
+		return;
+	r8a66597->pipe_config |= (1 << pipenum);
+
+	r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum));
+	r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum));
+	r8a66597_write(r8a66597, pipenum, PIPESEL);
+
+	/* FIXME: This driver support bulk transfer only. */
+	if (!usb_pipein(pipe))
+		val |= R8A66597_DIR;
+	else
+		val |= R8A66597_SHTNAK;
+	val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe);
+	r8a66597_write(r8a66597, val, PIPECFG);
+
+	r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF);
+	r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) |
+				 maxpacket, PIPEMAXP);
+	r8a66597_write(r8a66597, 0, PIPEPERI);
+	r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum));
+}
+
+static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev,
+			     struct devrequest *setup)
+{
+	int i;
+	unsigned short *p = (unsigned short *)setup;
+	unsigned long setup_addr = USBREQ;
+	u16 intsts1;
+	int timeout = 3000;
+	u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum;
+
+	r8a66597_write(r8a66597, make_devsel(devsel) |
+				 (8 << dev->maxpacketsize), DCPMAXP);
+	r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1);
+
+	for (i = 0; i < 4; i++) {
+		r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr);
+		setup_addr += 2;
+	}
+	r8a66597_write(r8a66597, ~0x0001, BRDYSTS);
+	r8a66597_write(r8a66597, SUREQ, DCPCTR);
+
+	while (1) {
+		intsts1 = r8a66597_read(r8a66597, INTSTS1);
+		if (intsts1 & SACK)
+			break;
+		if (intsts1 & SIGN) {
+			printf("setup packet send error\n");
+			return -1;
+		}
+		if (timeout-- < 0) {
+			printf("setup packet timeout\n");
+			return -1;
+		}
+		udelay(500);
+	}
+
+	return 0;
+}
+
+static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev,
+			    unsigned long pipe, void *buffer, int transfer_len)
+{
+	u16 tmp, bufsize;
+	u16 *buf;
+	size_t size;
+
+	R8A66597_DPRINT("%s\n", __func__);
+
+	r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM,
+			MBW | CURPIPE, CFIFOSEL);
+	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM);
+	tmp = r8a66597_read(r8a66597, CFIFOCTR);
+	if ((tmp & FRDY) == 0) {
+		printf("%s FRDY is not set (%x)\n", __func__, tmp);
+		return -1;
+	}
+
+	/* prepare parameters */
+	bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)];
+	buf = (u16 *)(buffer + dev->act_len);
+	size = min((int)bufsize, transfer_len - dev->act_len);
+
+	/* write fifo */
+	r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS);
+	if (buffer) {
+		r8a66597_write_fifo(r8a66597, CFIFO, buf, size);
+		r8a66597_write(r8a66597, BVAL, CFIFOCTR);
+	}
+
+	/* update parameters */
+	dev->act_len += size;
+
+	r8a66597_mdfy(r8a66597, PID_BUF, PID,
+			get_pipectr_addr(BULK_OUT_PIPENUM));
+
+	while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM)))
+		if (ctrlc())
+			return -1;
+	r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS);
+
+	if (dev->act_len >= transfer_len)
+		r8a66597_mdfy(r8a66597, PID_NAK, PID,
+				get_pipectr_addr(BULK_OUT_PIPENUM));
+
+	return 0;
+}
+
+static int receive_bulk_packet(struct r8a66597 *r8a66597,
+			       struct usb_device *dev,
+			       unsigned long pipe,
+			       void *buffer, int transfer_len)
+{
+	u16 tmp;
+	u16 *buf;
+	const u16 pipenum = BULK_IN_PIPENUM;
+	int rcv_len;
+	int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)];
+
+	R8A66597_DPRINT("%s\n", __func__);
+
+	/* prepare */
+	if (dev->act_len == 0) {
+		r8a66597_mdfy(r8a66597, PID_NAK, PID,
+				get_pipectr_addr(pipenum));
+		r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
+
+		r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum));
+		r8a66597_write(r8a66597,
+				(transfer_len + maxpacket - 1) / maxpacket,
+				get_pipetrn_addr(pipenum));
+		r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum));
+
+		r8a66597_mdfy(r8a66597, PID_BUF, PID,
+				get_pipectr_addr(pipenum));
+	}
+
+	r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL);
+	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
+
+	while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum)))
+		if (ctrlc())
+			return -1;
+	r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
+
+	tmp = r8a66597_read(r8a66597, CFIFOCTR);
+	if ((tmp & FRDY) == 0) {
+		printf("%s FRDY is not set. (%x)\n", __func__, tmp);
+		return -1;
+	}
+
+	buf = (u16 *)(buffer + dev->act_len);
+	rcv_len = tmp & DTLN;
+	dev->act_len += rcv_len;
+
+	if (buffer) {
+		if (rcv_len == 0)
+			r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+		else
+			r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len);
+	}
+
+	return 0;
+}
+
+static int receive_control_packet(struct r8a66597 *r8a66597,
+				  struct usb_device *dev,
+				  void *buffer, int transfer_len)
+{
+	u16 tmp;
+	int rcv_len;
+
+	/* FIXME: limit transfer size : 64byte or less */
+
+	r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+	r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+	r8a66597_bset(r8a66597, SQSET, DCPCTR);
+	r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+	r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR);
+
+	while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001))
+		if (ctrlc())
+			return -1;
+	r8a66597_write(r8a66597, ~0x0001, BRDYSTS);
+
+	r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL);
+	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+
+	tmp = r8a66597_read(r8a66597, CFIFOCTR);
+	if ((tmp & FRDY) == 0) {
+		printf("%s FRDY is not set. (%x)\n", __func__, tmp);
+		return -1;
+	}
+
+	rcv_len = tmp & DTLN;
+	dev->act_len += rcv_len;
+
+	r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR);
+
+	if (buffer) {
+		if (rcv_len == 0)
+			r8a66597_write(r8a66597, BCLR, DCPCTR);
+		else
+			r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len);
+	}
+
+	return 0;
+}
+
+static int send_status_packet(struct r8a66597 *r8a66597,
+			       unsigned long pipe)
+{
+	r8a66597_bset(r8a66597, SQSET, DCPCTR);
+	r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR);
+
+	if (usb_pipein(pipe)) {
+		r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+		r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+		r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
+		r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR);
+	} else {
+		r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+		r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+		r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+	}
+	r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR);
+
+	while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001))
+		if (ctrlc())
+			return -1;
+
+	return 0;
+}
+
+static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port)
+{
+	int count = R8A66597_MAX_SAMPLING;
+	unsigned short syssts, old_syssts;
+
+	R8A66597_DPRINT("%s\n", __func__);
+
+	old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST);
+	while (count > 0) {
+		wait_ms(R8A66597_RH_POLL_TIME);
+
+		syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST);
+		if (syssts == old_syssts) {
+			count--;
+		} else {
+			count = R8A66597_MAX_SAMPLING;
+			old_syssts = syssts;
+		}
+	}
+}
+
+static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port)
+{
+	wait_ms(10);
+	r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port));
+	wait_ms(50);
+	r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port));
+	wait_ms(50);
+}
+
+static int check_usb_device_connecting(struct r8a66597 *r8a66597)
+{
+	int timeout = 10000;	/* 100usec * 10000 = 1sec */
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		/* check a usb cable connect */
+		while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) {
+			if (timeout-- < 0) {
+				printf("%s timeout.\n", __func__);
+				return -1;
+			}
+			udelay(100);
+		}
+
+		/* check a data line */
+		r8a66597_check_syssts(r8a66597, 0);
+
+		r8a66597_bus_reset(r8a66597, 0);
+		r8a66597->speed = get_rh_usb_speed(r8a66597, 0);
+
+		if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) {
+			r8a66597->port_change = USB_PORT_STAT_C_CONNECTION;
+			r8a66597->port_status = USB_PORT_STAT_CONNECTION |
+						USB_PORT_STAT_ENABLE;
+			return 0;	/* success */
+		}
+
+		R8A66597_DPRINT("USB device has detached. retry = %d\n", i);
+		r8a66597_write(r8a66597, ~DTCH, INTSTS1);
+	}
+
+	return -1;	/* fail */
+}
+
+/* based on usb_ohci.c */
+#define min_t(type, x, y) \
+		({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
+/*-------------------------------------------------------------------------*
+ * 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; */
+
+	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[] =
+{
+	34,			/*  __u8  bLength; */
+	0x03,			/*  __u8  bDescriptorType; String-descriptor */
+	'R',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'8',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'A',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'6',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'6',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'5',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'9',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'7',			/*  __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 */
+	'H',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'u',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+	'b',			/*  __u8  Unicode */
+	0,				/*  __u8  Unicode */
+};
+
+static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+			void *buffer, int transfer_len, struct devrequest *cmd)
+{
+	struct r8a66597 *r8a66597 = &gr8a66597;
+	int leni = transfer_len;
+	int len = 0;
+	int stat = 0;
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+	unsigned char data[32];
+
+	R8A66597_DPRINT("%s\n", __func__);
+
+	if (usb_pipeint(pipe)) {
+		printf("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);
+
+	switch (bmRType_bReq) {
+	case RH_GET_STATUS:
+		*(__u16 *)buffer = cpu_to_le16(1);
+		len = 2;
+		break;
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *)buffer = cpu_to_le16(0);
+		len = 2;
+		break;
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *)buffer = cpu_to_le16(0);
+		len = 2;
+		break;
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *)buffer = cpu_to_le32(0);
+		len = 4;
+		break;
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		*(__u32 *)buffer = cpu_to_le32(r8a66597->port_status |
+						(r8a66597->port_change << 16));
+		len = 4;
+		break;
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_C_PORT_CONNECTION:
+			r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION;
+			break;
+		}
+		break;
+
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_SUSPEND):
+			break;
+		case (RH_PORT_RESET):
+			r8a66597_bus_reset(r8a66597, 0);
+			break;
+		case (RH_PORT_POWER):
+			break;
+		case (RH_PORT_ENABLE):
+			break;
+		}
+		break;
+	case RH_SET_ADDRESS:
+		gr8a66597.rh_devnum = wValue;
+		break;
+	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));
+			memcpy(buffer, root_hub_dev_des, len);
+			break;
+		case (0x02): /* configuration descriptor */
+			len = min_t(unsigned int,
+				  leni,
+				  min_t(unsigned int,
+				      sizeof(root_hub_config_des),
+				      wLength));
+			memcpy(buffer, root_hub_config_des, len);
+			break;
+		case (0x03): /* string descriptors */
+			if (wValue == 0x0300) {
+				len = min_t(unsigned int,
+					  leni,
+					  min_t(unsigned int,
+					      sizeof(root_hub_str_index0),
+					      wLength));
+				memcpy(buffer, root_hub_str_index0, len);
+			}
+			if (wValue == 0x0301) {
+				len = min_t(unsigned int,
+					  leni,
+					  min_t(unsigned int,
+					      sizeof(root_hub_str_index1),
+					      wLength));
+				memcpy(buffer, root_hub_str_index1, len);
+			}
+			break;
+		default:
+			stat = USB_ST_STALLED;
+		}
+		break;
+
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+	{
+		__u32 temp = 0x00000001;
+
+		data[0] = 9;		/* min length; */
+		data[1] = 0x29;
+		data[2] = temp & RH_A_NDP;
+		data[3] = 0;
+		if (temp & RH_A_PSM)
+			data[3] |= 0x1;
+		if (temp & RH_A_NOCP)
+			data[3] |= 0x10;
+		else if (temp & RH_A_OCPM)
+			data[3] |= 0x8;
+
+		/* corresponds to data[4-7] */
+		data[5] = (temp & RH_A_POTPGT) >> 24;
+		data[7] = temp & RH_B_DR;
+		if (data[2] < 7) {
+			data[8] = 0xff;
+		} else {
+			data[0] += 2;
+			data[8] = (temp & RH_B_DR) >> 8;
+			data[10] = data[9] = 0xff;
+		}
+
+		len = min_t(unsigned int, leni,
+			    min_t(unsigned int, data[0], wLength));
+		memcpy(buffer, data, len);
+		break;
+	}
+
+	case RH_GET_CONFIGURATION:
+		*(__u8 *) buffer = 0x01;
+		len = 1;
+		break;
+	case RH_SET_CONFIGURATION:
+		break;
+	default:
+		R8A66597_DPRINT("unsupported root hub command");
+		stat = USB_ST_STALLED;
+	}
+
+	wait_ms(1);
+
+	len = min_t(int, len, leni);
+
+	dev->act_len = len;
+	dev->status = stat;
+
+	return stat;
+}
+
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+		    int transfer_len)
+{
+	struct r8a66597 *r8a66597 = &gr8a66597;
+	int ret = 0;
+
+	R8A66597_DPRINT("%s\n", __func__);
+	R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n",
+			pipe, buffer, transfer_len, dev->devnum);
+
+	set_devadd(r8a66597, dev->devnum, dev, 0);
+
+	pipe_buffer_setting(r8a66597, dev, pipe);
+
+	dev->act_len = 0;
+	while (dev->act_len < transfer_len && ret == 0) {
+		if (ctrlc())
+			return -1;
+
+		if (usb_pipein(pipe))
+			ret = receive_bulk_packet(r8a66597, dev, pipe, buffer,
+							transfer_len);
+		else
+			ret = send_bulk_packet(r8a66597, dev, pipe, buffer,
+							transfer_len);
+	}
+
+	if (ret == 0)
+		dev->status = 0;
+
+	return ret;
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe,
+		       void *buffer, int transfer_len, struct devrequest *setup)
+{
+	struct r8a66597 *r8a66597 = &gr8a66597;
+	u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ?
+					0 : dev->devnum;
+
+	R8A66597_DPRINT("%s\n", __func__);
+	if (usb_pipedevice(pipe) == r8a66597->rh_devnum)
+		return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len,
+						setup);
+
+	R8A66597_DPRINT("%s: setup\n", __func__);
+	set_devadd(r8a66597, r8a66597_address, dev, 0);
+
+	if (send_setup_packet(r8a66597, dev, setup) < 0) {
+		printf("setup packet send error\n");
+		return -1;
+	}
+
+	dev->act_len = 0;
+	if (usb_pipein(pipe))
+		if (receive_control_packet(r8a66597, dev, buffer,
+						transfer_len) < 0)
+			return -1;
+
+	if (send_status_packet(r8a66597, pipe) < 0)
+		return -1;
+
+	dev->status = 0;
+
+	return 0;
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+			int transfer_len, int interval)
+{
+	/* no implement */
+	R8A66597_DPRINT("%s\n", __func__);
+	return 0;
+}
+
+void usb_event_poll(void)
+{
+	/* no implement */
+	R8A66597_DPRINT("%s\n", __func__);
+}
+
+int usb_lowlevel_init(void)
+{
+	struct r8a66597 *r8a66597 = &gr8a66597;
+
+	R8A66597_DPRINT("%s\n", __func__);
+
+	memset(r8a66597, 0, sizeof(r8a66597));
+	r8a66597->reg = CONFIG_R8A66597_BASE_ADDR;
+
+	disable_controller(r8a66597);
+	wait_ms(100);
+
+	enable_controller(r8a66597);
+	r8a66597_port_power(r8a66597, 0 , 1);
+
+	/* check usb device */
+	check_usb_device_connecting(r8a66597);
+
+	wait_ms(50);
+
+	return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+	disable_controller(&gr8a66597);
+
+	return 0;
+}
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
new file mode 100644
index 0000000..9af6446
--- /dev/null
+++ b/drivers/usb/host/r8a66597.h
@@ -0,0 +1,659 @@
+/*
+ * R8A66597 HCD (Host Controller Driver) for u-boot
+ *
+ * Copyright (C) 2008  Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __R8A66597_H__
+#define __R8A66597_H__
+
+#define SYSCFG0		0x00
+#define SYSCFG1		0x02
+#define SYSSTS0		0x04
+#define SYSSTS1		0x06
+#define DVSTCTR0	0x08
+#define DVSTCTR1	0x0A
+#define TESTMODE	0x0C
+#define PINCFG		0x0E
+#define DMA0CFG		0x10
+#define DMA1CFG		0x12
+#define CFIFO		0x14
+#define D0FIFO		0x18
+#define D1FIFO		0x1C
+#define CFIFOSEL	0x20
+#define CFIFOCTR	0x22
+#define CFIFOSIE	0x24
+#define D0FIFOSEL	0x28
+#define D0FIFOCTR	0x2A
+#define D1FIFOSEL	0x2C
+#define D1FIFOCTR	0x2E
+#define INTENB0		0x30
+#define INTENB1		0x32
+#define INTENB2		0x34
+#define BRDYENB		0x36
+#define NRDYENB		0x38
+#define BEMPENB		0x3A
+#define SOFCFG		0x3C
+#define INTSTS0		0x40
+#define INTSTS1		0x42
+#define INTSTS2		0x44
+#define BRDYSTS		0x46
+#define NRDYSTS		0x48
+#define BEMPSTS		0x4A
+#define FRMNUM		0x4C
+#define UFRMNUM		0x4E
+#define USBADDR		0x50
+#define USBREQ		0x54
+#define USBVAL		0x56
+#define USBINDX		0x58
+#define USBLENG		0x5A
+#define DCPCFG		0x5C
+#define DCPMAXP		0x5E
+#define DCPCTR		0x60
+#define PIPESEL		0x64
+#define PIPECFG		0x68
+#define PIPEBUF		0x6A
+#define PIPEMAXP	0x6C
+#define PIPEPERI	0x6E
+#define PIPE1CTR	0x70
+#define PIPE2CTR	0x72
+#define PIPE3CTR	0x74
+#define PIPE4CTR	0x76
+#define PIPE5CTR	0x78
+#define PIPE6CTR	0x7A
+#define PIPE7CTR	0x7C
+#define PIPE8CTR	0x7E
+#define PIPE9CTR	0x80
+#define PIPE1TRE	0x90
+#define PIPE1TRN	0x92
+#define PIPE2TRE	0x94
+#define PIPE2TRN	0x96
+#define PIPE3TRE	0x98
+#define PIPE3TRN	0x9A
+#define PIPE4TRE	0x9C
+#define	PIPE4TRN	0x9E
+#define	PIPE5TRE	0xA0
+#define	PIPE5TRN	0xA2
+#define DEVADD0		0xD0
+#define DEVADD1		0xD2
+#define DEVADD2		0xD4
+#define DEVADD3		0xD6
+#define DEVADD4		0xD8
+#define DEVADD5		0xDA
+#define DEVADD6		0xDC
+#define DEVADD7		0xDE
+#define DEVADD8		0xE0
+#define DEVADD9		0xE2
+#define DEVADDA		0xE4
+
+/* System Configuration Control Register */
+#define	XTAL		0xC000	/* b15-14: Crystal selection */
+#define	  XTAL48	 0x8000	  /* 48MHz */
+#define	  XTAL24	 0x4000	  /* 24MHz */
+#define	  XTAL12	 0x0000	  /* 12MHz */
+#define	XCKE		0x2000	/* b13: External clock enable */
+#define	PLLC		0x0800	/* b11: PLL control */
+#define	SCKE		0x0400	/* b10: USB clock enable */
+#define	PCSDIS		0x0200	/* b9: not CS wakeup */
+#define	LPSME		0x0100	/* b8: Low power sleep mode */
+#define	HSE		0x0080	/* b7: Hi-speed enable */
+#define	DCFM		0x0040	/* b6: Controller function select  */
+#define	DRPD		0x0020	/* b5: D+/- pull down control */
+#define	DPRPU		0x0010	/* b4: D+ pull up control */
+#define	USBE		0x0001	/* b0: USB module operation enable */
+
+/* System Configuration Status Register */
+#define	OVCBIT		0x8000	/* b15-14: Over-current bit */
+#define	OVCMON		0xC000	/* b15-14: Over-current monitor */
+#define	SOFEA		0x0020	/* b5: SOF monitor */
+#define	IDMON		0x0004	/* b3: ID-pin monitor */
+#define	LNST		0x0003	/* b1-0: D+, D- line status */
+#define	  SE1		 0x0003	  /* SE1 */
+#define	  FS_KSTS	 0x0002	  /* Full-Speed K State */
+#define	  FS_JSTS	 0x0001	  /* Full-Speed J State */
+#define	  LS_JSTS	 0x0002	  /* Low-Speed J State */
+#define	  LS_KSTS	 0x0001	  /* Low-Speed K State */
+#define	  SE0		 0x0000	  /* SE0 */
+
+/* Device State Control Register */
+#define	EXTLP0		0x0400	/* b10: External port */
+#define	VBOUT		0x0200	/* b9: VBUS output */
+#define	WKUP		0x0100	/* b8: Remote wakeup */
+#define	RWUPE		0x0080	/* b7: Remote wakeup sense */
+#define	USBRST		0x0040	/* b6: USB reset enable */
+#define	RESUME		0x0020	/* b5: Resume enable */
+#define	UACT		0x0010	/* b4: USB bus enable */
+#define	RHST		0x0007	/* b1-0: Reset handshake status */
+#define	  HSPROC	 0x0004	  /* HS handshake is processing */
+#define	  HSMODE	 0x0003	  /* Hi-Speed mode */
+#define	  FSMODE	 0x0002	  /* Full-Speed mode */
+#define	  LSMODE	 0x0001	  /* Low-Speed mode */
+#define	  UNDECID	 0x0000	  /* Undecided */
+
+/* Test Mode Register */
+#define	UTST			0x000F	/* b3-0: Test select */
+#define	  H_TST_PACKET		 0x000C	  /* HOST TEST Packet */
+#define	  H_TST_SE0_NAK		 0x000B	  /* HOST TEST SE0 NAK */
+#define	  H_TST_K		 0x000A	  /* HOST TEST K */
+#define	  H_TST_J		 0x0009	  /* HOST TEST J */
+#define	  H_TST_NORMAL		 0x0000	  /* HOST Normal Mode */
+#define	  P_TST_PACKET		 0x0004	  /* PERI TEST Packet */
+#define	  P_TST_SE0_NAK		 0x0003	  /* PERI TEST SE0 NAK */
+#define	  P_TST_K		 0x0002	  /* PERI TEST K */
+#define	  P_TST_J		 0x0001	  /* PERI TEST J */
+#define	  P_TST_NORMAL		 0x0000	  /* PERI Normal Mode */
+
+/* Data Pin Configuration Register */
+#define	LDRV			0x8000	/* b15: Drive Current Adjust */
+#define	  VIF1			  0x0000		/* VIF = 1.8V */
+#define	  VIF3			  0x8000		/* VIF = 3.3V */
+#define	INTA			0x0001	/* b1: USB INT-pin active */
+
+/* DMAx Pin Configuration Register */
+#define	DREQA			0x4000	/* b14: Dreq active select */
+#define	BURST			0x2000	/* b13: Burst mode */
+#define	DACKA			0x0400	/* b10: Dack active select */
+#define	DFORM			0x0380	/* b9-7: DMA mode select */
+#define	  CPU_ADR_RD_WR		 0x0000	  /* Address + RD/WR mode (CPU bus) */
+#define	  CPU_DACK_RD_WR	 0x0100	  /* DACK + RD/WR mode (CPU bus) */
+#define	  CPU_DACK_ONLY		 0x0180	  /* DACK only mode (CPU bus) */
+#define	  SPLIT_DACK_ONLY	 0x0200	  /* DACK only mode (SPLIT bus) */
+#define	DENDA			0x0040	/* b6: Dend active select */
+#define	PKTM			0x0020	/* b5: Packet mode */
+#define	DENDE			0x0010	/* b4: Dend enable */
+#define	OBUS			0x0004	/* b2: OUTbus mode */
+
+/* CFIFO/DxFIFO Port Select Register */
+#define	RCNT		0x8000	/* b15: Read count mode */
+#define	REW		0x4000	/* b14: Buffer rewind */
+#define	DCLRM		0x2000	/* b13: DMA buffer clear mode */
+#define	DREQE		0x1000	/* b12: DREQ output enable */
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#define	MBW		0x0800
+#else
+#define	MBW		0x0400	/* b10: Maximum bit width for FIFO access */
+#endif
+#define	  MBW_8		 0x0000	  /*  8bit */
+#define	  MBW_16	 0x0400	  /* 16bit */
+#define	BIGEND		0x0100	/* b8: Big endian mode */
+#define	  BYTE_LITTLE	 0x0000		/* little dendian */
+#define	  BYTE_BIG	 0x0100		/* big endifan */
+#define	ISEL		0x0020	/* b5: DCP FIFO port direction select */
+#define	CURPIPE		0x000F	/* b2-0: PIPE select */
+
+/* CFIFO/DxFIFO Port Control Register */
+#define	BVAL		0x8000	/* b15: Buffer valid flag */
+#define	BCLR		0x4000	/* b14: Buffer clear */
+#define	FRDY		0x2000	/* b13: FIFO ready */
+#define	DTLN		0x0FFF	/* b11-0: FIFO received data length */
+
+/* Interrupt Enable Register 0 */
+#define	VBSE	0x8000	/* b15: VBUS interrupt */
+#define	RSME	0x4000	/* b14: Resume interrupt */
+#define	SOFE	0x2000	/* b13: Frame update interrupt */
+#define	DVSE	0x1000	/* b12: Device state transition interrupt */
+#define	CTRE	0x0800	/* b11: Control transfer stage transition interrupt */
+#define	BEMPE	0x0400	/* b10: Buffer empty interrupt */
+#define	NRDYE	0x0200	/* b9: Buffer not ready interrupt */
+#define	BRDYE	0x0100	/* b8: Buffer ready interrupt */
+
+/* Interrupt Enable Register 1 */
+#define	OVRCRE		0x8000	/* b15: Over-current interrupt */
+#define	BCHGE		0x4000	/* b14: USB us chenge interrupt */
+#define	DTCHE		0x1000	/* b12: Detach sense interrupt */
+#define	ATTCHE		0x0800	/* b11: Attach sense interrupt */
+#define	EOFERRE		0x0040	/* b6: EOF error interrupt */
+#define	SIGNE		0x0020	/* b5: SETUP IGNORE interrupt */
+#define	SACKE		0x0010	/* b4: SETUP ACK interrupt */
+
+/* BRDY Interrupt Enable/Status Register */
+#define	BRDY9		0x0200	/* b9: PIPE9 */
+#define	BRDY8		0x0100	/* b8: PIPE8 */
+#define	BRDY7		0x0080	/* b7: PIPE7 */
+#define	BRDY6		0x0040	/* b6: PIPE6 */
+#define	BRDY5		0x0020	/* b5: PIPE5 */
+#define	BRDY4		0x0010	/* b4: PIPE4 */
+#define	BRDY3		0x0008	/* b3: PIPE3 */
+#define	BRDY2		0x0004	/* b2: PIPE2 */
+#define	BRDY1		0x0002	/* b1: PIPE1 */
+#define	BRDY0		0x0001	/* b1: PIPE0 */
+
+/* NRDY Interrupt Enable/Status Register */
+#define	NRDY9		0x0200	/* b9: PIPE9 */
+#define	NRDY8		0x0100	/* b8: PIPE8 */
+#define	NRDY7		0x0080	/* b7: PIPE7 */
+#define	NRDY6		0x0040	/* b6: PIPE6 */
+#define	NRDY5		0x0020	/* b5: PIPE5 */
+#define	NRDY4		0x0010	/* b4: PIPE4 */
+#define	NRDY3		0x0008	/* b3: PIPE3 */
+#define	NRDY2		0x0004	/* b2: PIPE2 */
+#define	NRDY1		0x0002	/* b1: PIPE1 */
+#define	NRDY0		0x0001	/* b1: PIPE0 */
+
+/* BEMP Interrupt Enable/Status Register */
+#define	BEMP9		0x0200	/* b9: PIPE9 */
+#define	BEMP8		0x0100	/* b8: PIPE8 */
+#define	BEMP7		0x0080	/* b7: PIPE7 */
+#define	BEMP6		0x0040	/* b6: PIPE6 */
+#define	BEMP5		0x0020	/* b5: PIPE5 */
+#define	BEMP4		0x0010	/* b4: PIPE4 */
+#define	BEMP3		0x0008	/* b3: PIPE3 */
+#define	BEMP2		0x0004	/* b2: PIPE2 */
+#define	BEMP1		0x0002	/* b1: PIPE1 */
+#define	BEMP0		0x0001	/* b0: PIPE0 */
+
+/* SOF Pin Configuration Register */
+#define	TRNENSEL	0x0100	/* b8: Select transaction enable period */
+#define	BRDYM		0x0040	/* b6: BRDY clear timing */
+#define	INTL		0x0020	/* b5: Interrupt sense select */
+#define	EDGESTS		0x0010	/* b4:  */
+#define	SOFMODE		0x000C	/* b3-2: SOF pin select */
+#define	  SOF_125US	 0x0008	  /* SOF OUT 125us Frame Signal */
+#define	  SOF_1MS	 0x0004	  /* SOF OUT 1ms Frame Signal */
+#define	  SOF_DISABLE	 0x0000	  /* SOF OUT Disable */
+
+/* Interrupt Status Register 0 */
+#define	VBINT	0x8000	/* b15: VBUS interrupt */
+#define	RESM	0x4000	/* b14: Resume interrupt */
+#define	SOFR	0x2000	/* b13: SOF frame update interrupt */
+#define	DVST	0x1000	/* b12: Device state transition interrupt */
+#define	CTRT	0x0800	/* b11: Control transfer stage transition interrupt */
+#define	BEMP	0x0400	/* b10: Buffer empty interrupt */
+#define	NRDY	0x0200	/* b9: Buffer not ready interrupt */
+#define	BRDY	0x0100	/* b8: Buffer ready interrupt */
+#define	VBSTS	0x0080	/* b7: VBUS input port */
+#define	DVSQ	0x0070	/* b6-4: Device state */
+#define	  DS_SPD_CNFG	 0x0070	  /* Suspend Configured */
+#define	  DS_SPD_ADDR	 0x0060	  /* Suspend Address */
+#define	  DS_SPD_DFLT	 0x0050	  /* Suspend Default */
+#define	  DS_SPD_POWR	 0x0040	  /* Suspend Powered */
+#define	  DS_SUSP	 0x0040	  /* Suspend */
+#define	  DS_CNFG	 0x0030	  /* Configured */
+#define	  DS_ADDS	 0x0020	  /* Address */
+#define	  DS_DFLT	 0x0010	  /* Default */
+#define	  DS_POWR	 0x0000	  /* Powered */
+#define	DVSQS		0x0030	/* b5-4: Device state */
+#define	VALID		0x0008	/* b3: Setup packet detected flag */
+#define	CTSQ		0x0007	/* b2-0: Control transfer stage */
+#define	  CS_SQER	 0x0006	  /* Sequence error */
+#define	  CS_WRND	 0x0005	  /* Control write nodata status stage */
+#define	  CS_WRSS	 0x0004	  /* Control write status stage */
+#define	  CS_WRDS	 0x0003	  /* Control write data stage */
+#define	  CS_RDSS	 0x0002	  /* Control read status stage */
+#define	  CS_RDDS	 0x0001	  /* Control read data stage */
+#define	  CS_IDST	 0x0000	  /* Idle or setup stage */
+
+/* Interrupt Status Register 1 */
+#define	OVRCR		0x8000	/* b15: Over-current interrupt */
+#define	BCHG		0x4000	/* b14: USB bus chenge interrupt */
+#define	DTCH		0x1000	/* b12: Detach sense interrupt */
+#define	ATTCH		0x0800	/* b11: Attach sense interrupt */
+#define	EOFERR		0x0040	/* b6: EOF-error interrupt */
+#define	SIGN		0x0020	/* b5: Setup ignore interrupt */
+#define	SACK		0x0010	/* b4: Setup acknowledge interrupt */
+
+/* Frame Number Register */
+#define	OVRN		0x8000	/* b15: Overrun error */
+#define	CRCE		0x4000	/* b14: Received data error */
+#define	FRNM		0x07FF	/* b10-0: Frame number */
+
+/* Micro Frame Number Register */
+#define	UFRNM		0x0007	/* b2-0: Micro frame number */
+
+/* Default Control Pipe Maxpacket Size Register */
+/* Pipe Maxpacket Size Register */
+#define	DEVSEL	0xF000	/* b15-14: Device address select */
+#define	MAXP	0x007F	/* b6-0: Maxpacket size of default control pipe */
+
+/* Default Control Pipe Control Register */
+#define	BSTS		0x8000	/* b15: Buffer status */
+#define	SUREQ		0x4000	/* b14: Send USB request  */
+#define	CSCLR		0x2000	/* b13: complete-split status clear */
+#define	CSSTS		0x1000	/* b12: complete-split status */
+#define	SUREQCLR	0x0800	/* b11: stop setup request */
+#define	SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	PBUSY		0x0020	/* b5: pipe busy */
+#define	PINGE		0x0010	/* b4: ping enable */
+#define	CCPL		0x0004	/* b2: Enable control transfer complete */
+#define	PID		0x0003	/* b1-0: Response PID */
+#define	  PID_STALL11	 0x0003	  /* STALL */
+#define	  PID_STALL	 0x0002	  /* STALL */
+#define	  PID_BUF	 0x0001	  /* BUF */
+#define	  PID_NAK	 0x0000	  /* NAK */
+
+/* Pipe Window Select Register */
+#define	PIPENM		0x0007	/* b2-0: Pipe select */
+
+/* Pipe Configuration Register */
+#define	R8A66597_TYP	0xC000	/* b15-14: Transfer type */
+#define	  R8A66597_ISO	 0xC000		  /* Isochronous */
+#define	  R8A66597_INT	 0x8000		  /* Interrupt */
+#define	  R8A66597_BULK	 0x4000		  /* Bulk */
+#define	R8A66597_BFRE	0x0400	/* b10: Buffer ready interrupt mode select */
+#define	R8A66597_DBLB	0x0200	/* b9: Double buffer mode select */
+#define	R8A66597_CNTMD	0x0100	/* b8: Continuous transfer mode select */
+#define	R8A66597_SHTNAK	0x0080	/* b7: Transfer end NAK */
+#define	R8A66597_DIR	0x0010	/* b4: Transfer direction select */
+#define	R8A66597_EPNUM	0x000F	/* b3-0: Eendpoint number select */
+
+/* Pipe Buffer Configuration Register */
+#define	BUFSIZE		0x7C00	/* b14-10: Pipe buffer size */
+#define	BUFNMB		0x007F	/* b6-0: Pipe buffer number */
+#define	PIPE0BUF	256
+#define	PIPExBUF	64
+
+/* Pipe Maxpacket Size Register */
+#define	MXPS		0x07FF	/* b10-0: Maxpacket size */
+
+/* Pipe Cycle Configuration Register */
+#define	IFIS	0x1000	/* b12: Isochronous in-buffer flush mode select */
+#define	IITV	0x0007	/* b2-0: Isochronous interval */
+
+/* Pipex Control Register */
+#define	BSTS	0x8000	/* b15: Buffer status */
+#define	INBUFM	0x4000	/* b14: IN buffer monitor (Only for PIPE1 to 5) */
+#define	CSCLR	0x2000	/* b13: complete-split status clear */
+#define	CSSTS	0x1000	/* b12: complete-split status */
+#define	ATREPM	0x0400	/* b10: Auto repeat mode */
+#define	ACLRM	0x0200	/* b9: Out buffer auto clear mode */
+#define	SQCLR	0x0100	/* b8: Sequence toggle bit clear */
+#define	SQSET	0x0080	/* b7: Sequence toggle bit set */
+#define	SQMON	0x0040	/* b6: Sequence toggle bit monitor */
+#define	PBUSY	0x0020	/* b5: pipe busy */
+#define	PID	0x0003	/* b1-0: Response PID */
+
+/* PIPExTRE */
+#define	TRENB		0x0200	/* b9: Transaction counter enable */
+#define	TRCLR		0x0100	/* b8: Transaction counter clear */
+
+/* PIPExTRN */
+#define	TRNCNT		0xFFFF	/* b15-0: Transaction counter */
+
+/* DEVADDx */
+#define	UPPHUB		0x7800
+#define	HUBPORT		0x0700
+#define	USBSPD		0x00C0
+#define	RTPORT		0x0001
+
+#define R8A66597_MAX_NUM_PIPE		10
+#define R8A66597_BUF_BSIZE		8
+#define R8A66597_MAX_DEVICE		10
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#define R8A66597_MAX_ROOT_HUB		1
+#else
+#define R8A66597_MAX_ROOT_HUB		2
+#endif
+#define R8A66597_MAX_SAMPLING		5
+#define R8A66597_RH_POLL_TIME		10
+
+#define BULK_IN_PIPENUM		3
+#define BULK_IN_BUFNUM		8
+
+#define BULK_OUT_PIPENUM	4
+#define BULK_OUT_BUFNUM		40
+
+#define check_bulk_or_isoc(pipenum)	((pipenum >= 1 && pipenum <= 5))
+#define check_interrupt(pipenum)	((pipenum >= 6 && pipenum <= 9))
+#define make_devsel(addr)		(addr << 12)
+
+struct r8a66597 {
+	unsigned long reg;
+	unsigned short pipe_config;	/* bit field */
+	unsigned short port_status;
+	unsigned short port_change;
+	u16 speed;	/* HSMODE or FSMODE or LSMODE */
+	unsigned char rh_devnum;
+};
+
+static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
+{
+	return inw(r8a66597->reg + offset);
+}
+
+static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
+				      unsigned long offset, void *buf,
+				      int len)
+{
+	int i;
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+	unsigned long fifoaddr = r8a66597->reg + offset;
+	unsigned long count;
+	unsigned long *p = buf;
+
+	count = len / 4;
+	for (i = 0; i < count; i++)
+		inl(p[i], r8a66597->reg + offset);
+
+	if (len & 0x00000003) {
+		unsigned long tmp = inl(fifoaddr);
+		memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03);
+	}
+#else
+	unsigned short *p = buf;
+
+	len = (len + 1) / 2;
+	for (i = 0; i < len; i++)
+		p[i] = inw(r8a66597->reg + offset);
+#endif
+}
+
+static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
+				  unsigned long offset)
+{
+	outw(val, r8a66597->reg + offset);
+}
+
+static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
+				       unsigned long offset, void *buf,
+				       int len)
+{
+	int i;
+	unsigned long fifoaddr = r8a66597->reg + offset;
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+	unsigned long count;
+	unsigned char *pb;
+	unsigned long *p = buf;
+
+	count = len / 4;
+	for (i = 0; i < count; i++)
+		outl(p[i], fifoaddr);
+
+	if (len & 0x00000003) {
+		pb = (unsigned char *)buf + count * 4;
+		for (i = 0; i < (len & 0x00000003); i++) {
+			if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)
+				outb(pb[i], fifoaddr + i);
+			else
+				outb(pb[i], fifoaddr + 3 - i);
+		}
+	}
+#else
+	int odd = len & 0x0001;
+	unsigned short *p = buf;
+
+	len = len / 2;
+	for (i = 0; i < len; i++)
+		outw(p[i], fifoaddr);
+
+	if (odd) {
+		unsigned char *pb = (unsigned char *)(buf + len);
+		outb(*pb, fifoaddr);
+	}
+#endif
+}
+
+static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
+				 u16 val, u16 pat, unsigned long offset)
+{
+	u16 tmp;
+	tmp = r8a66597_read(r8a66597, offset);
+	tmp = tmp & (~pat);
+	tmp = tmp | val;
+	r8a66597_write(r8a66597, tmp, offset);
+}
+
+#define r8a66597_bclr(r8a66597, val, offset)	\
+			r8a66597_mdfy(r8a66597, 0, val, offset)
+#define r8a66597_bset(r8a66597, val, offset)	\
+			r8a66597_mdfy(r8a66597, val, 0, offset)
+
+static inline unsigned long get_syscfg_reg(int port)
+{
+	return port == 0 ? SYSCFG0 : SYSCFG1;
+}
+
+static inline unsigned long get_syssts_reg(int port)
+{
+	return port == 0 ? SYSSTS0 : SYSSTS1;
+}
+
+static inline unsigned long get_dvstctr_reg(int port)
+{
+	return port == 0 ? DVSTCTR0 : DVSTCTR1;
+}
+
+static inline unsigned long get_dmacfg_reg(int port)
+{
+	return port == 0 ? DMA0CFG : DMA1CFG;
+}
+
+static inline unsigned long get_intenb_reg(int port)
+{
+	return port == 0 ? INTENB1 : INTENB2;
+}
+
+static inline unsigned long get_intsts_reg(int port)
+{
+	return port == 0 ? INTSTS1 : INTSTS2;
+}
+
+static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
+{
+	unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+	return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
+}
+
+static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
+				       int power)
+{
+	unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+	if (power)
+		r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
+	else
+		r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
+}
+
+#define get_pipectr_addr(pipenum)	(PIPE1CTR + (pipenum - 1) * 2)
+#define get_pipetre_addr(pipenum)	(PIPE1TRE + (pipenum - 1) * 4)
+#define get_pipetrn_addr(pipenum)	(PIPE1TRN + (pipenum - 1) * 4)
+#define get_devadd_addr(address)	(DEVADD0 + address * 2)
+
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.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 */
+
+#endif	/* __R8A66597_H__ */
diff --git a/drivers/usb/host/s3c64xx-hcd.c b/drivers/usb/host/s3c64xx-hcd.c
new file mode 100644
index 0000000..274a4ed
--- /dev/null
+++ b/drivers/usb/host/s3c64xx-hcd.c
@@ -0,0 +1,45 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) initialization for USB on the S3C64XX.
+ *
+ * Copyright (C) 2008,
+ * Guennadi Liakhovetski, DENX Software Engineering <lg@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 <common.h>
+#include <s3c6400.h>
+
+int usb_cpu_init(void)
+{
+	OTHERS_REG |= 0x10000;
+	return 0;
+}
+
+int usb_cpu_stop(void)
+{
+	OTHERS_REG &= ~0x10000;
+	return 0;
+}
+
+void usb_cpu_init_fail(void)
+{
+	OTHERS_REG &= ~0x10000;
+}
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
new file mode 100644
index 0000000..82a8b36
--- /dev/null
+++ b/drivers/usb/host/sl811-hcd.c
@@ -0,0 +1,734 @@
+/*
+ * (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>
+#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 *) CONFIG_SYS_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*CONFIG_SYS_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;
+}
diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
new file mode 100644
index 0000000..c1f9f01
--- /dev/null
+++ b/drivers/usb/host/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 */