[PATCH 11/11] [RFC USB POWERPC] Freescale QUICC Engine USB Host Controller

Anton Vorontsov avorontsov at ru.mvista.com
Mon Feb 4 04:11:21 EST 2008


Signed-off-by: Anton Vorontsov <avorontsov at ru.mvista.com>
---

Hi all,

This is RFC for the "FHCI" USB host controller driver.

My TODO list:
1. Locking completely braindead, needs fixing. I just inserted
   plain spin_lock()s to outline what I'm going to do. _irq and
   _irqsave variants will be inserted appropriately.
2. Try to get rid of -cq part of the driver and use kfifo instead;
3. Probably fix Packet-Level interface support. It was and is
   broken. I didn't include fhci-bds.c file in this RFC, today it's
   not worth looking, and weights about 60Kb additionally;
4. Make sparse happy.


Will appreciate any comments,

Thanks,

 drivers/usb/Makefile        |    1 +
 drivers/usb/host/Kconfig    |   36 +
 drivers/usb/host/Makefile   |    2 +-
 drivers/usb/host/fhci-bds.c | 1696 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/fhci-cq.c  |  105 +++
 drivers/usb/host/fhci-dbg.c |  133 ++++
 drivers/usb/host/fhci-hcd.c | 1391 +++++++++++++++++++++++++++++++++++
 drivers/usb/host/fhci-hub.c |  343 +++++++++
 drivers/usb/host/fhci-mem.c |   95 +++
 drivers/usb/host/fhci-q.c   |  495 +++++++++++++
 drivers/usb/host/fhci-tds.c |  628 ++++++++++++++++
 drivers/usb/host/fhci.h     |  543 ++++++++++++++
 12 files changed, 5467 insertions(+), 1 deletions(-)
 create mode 100644 drivers/usb/host/fhci-bds.c
 create mode 100644 drivers/usb/host/fhci-cq.c
 create mode 100644 drivers/usb/host/fhci-dbg.c
 create mode 100644 drivers/usb/host/fhci-hcd.c
 create mode 100644 drivers/usb/host/fhci-hub.c
 create mode 100644 drivers/usb/host/fhci-mem.c
 create mode 100644 drivers/usb/host/fhci-q.c
 create mode 100644 drivers/usb/host/fhci-tds.c
 create mode 100644 drivers/usb/host/fhci.h

diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 516a640..e0f71af 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_USB_UHCI_HCD)	+= host/
 obj-$(CONFIG_USB_SL811_HCD)	+= host/
 obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
+obj-$(CONFIG_USB_FHCI_HCD)	+= host/
 
 obj-$(CONFIG_USB_ACM)		+= class/
 obj-$(CONFIG_USB_PRINTER)	+= class/
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 49a91c5..6b1d408 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -261,3 +261,39 @@ config USB_R8A66597_HCD
 	  To compile this driver as a module, choose M here: the
 	  module will be called r8a66597-hcd.
 
+config USB_FHCI_HCD
+	tristate "Freescale QE USB Host Controller support"
+	depends on USB && GENERIC_GPIO
+	select QE_GTM
+	help
+	  Some Freescale PowerPC processors (such as MPC8360E and
+	  MPC8323) have a Full Speed or Low Speed QE USB Host controller.
+
+	  Say "y" to enable support for such controllers, or "m" to compile
+	  it as a module: the module will be called fhci-hcd.ko
+
+config FHCI_DEBUG
+	bool "Freescale QE USB Host Controller debug support"
+	depends on USB_FHCI_HCD
+	select DEBUG_FS
+	help
+	  Say "y" to see some FHCI debug information and statistics
+	  throught debugfs.
+
+choice
+	prompt "FHCI interface type"
+	depends on USB_FHCI_HCD
+	default FHCI_WITH_TDS
+
+config FHCI_WITH_BDS
+	bool "Packet-Level interface (BDs)"
+	depends on BROKEN
+	help
+	  Controller uses a packet-level interface to communicate.
+
+config FHCI_WITH_TDS
+	bool "Transaction-Level interface (TDs)"
+	help
+	  Controller uses a transaction-level interface to communicate.
+
+endchoice
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index bb8e9d4..4282205 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -16,4 +16,4 @@ obj-$(CONFIG_USB_SL811_HCD)	+= sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
 obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o
 obj-$(CONFIG_USB_R8A66597_HCD)	+= r8a66597-hcd.o
-
+obj-$(CONFIG_USB_FHCI_HCD)	+= fhci-hcd.o
diff --git a/drivers/usb/host/fhci-cq.c b/drivers/usb/host/fhci-cq.c
new file mode 100644
index 0000000..299704a
--- /dev/null
+++ b/drivers/usb/host/fhci-cq.c
@@ -0,0 +1,105 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+/* circular queue structure */
+struct cir_q {
+       int max;		/* size of queue */
+       int max_in;	/* max items in queue */
+       int first;	/* index of first in queue */
+       int last;	/* index after last in queue */
+       int read;	/* current reading position */
+       void *table[1];	/* fake size */
+};
+
+/* circular queue handle */
+static int cq_howmany(struct cir_q *cq)
+{
+	int l = cq->last;
+	int f = cq->first;
+
+	return l >= f ? l - f : l + cq->max - f;
+}
+
+static struct cir_q *cq_new(int size)
+{
+	struct cir_q *cq;
+
+	cq = kzalloc((sizeof(*cq) + size * sizeof(void *)), GFP_KERNEL);
+	if (cq) {
+		cq->max = size;
+		cq->first = 0;
+		cq->last = 0;
+		cq->read = 0;
+		cq->max_in = 0;
+	}
+
+	return cq;
+}
+
+static void cq_delete(struct cir_q *cq)
+{
+	kfree(cq);
+}
+
+static int cq_put(struct cir_q *cq, void *p)
+{
+	int n;
+	int k;
+
+	/* see if we can freely advance the last pointer */
+	n = cq->last;
+	k = cq_howmany(cq);
+	if ((k + 1) >= cq->max)
+		return -1;
+
+	if (++n >= cq->max)
+		n = 0;
+
+	/* add element to queue */
+	cq->table[cq->last] = p;
+	cq->last = n;
+	if ((k + 1) > cq->max_in)
+		cq->max_in = k + 1;
+
+	return k;
+}
+
+static void *cq_get(struct cir_q *cq)
+{
+	int n;
+	int k;
+	void *p;
+
+	n = cq->first;
+	/* see if the queue is not empty */
+	if (n == cq->last)
+		return 0;
+
+	p = cq->table[n];
+	if (++n >= cq->max)
+		n = 0;
+	if (cq->read == cq->first)
+		cq->read = n;
+	cq->first = n;
+
+	/* see if we've passed our previous maximum */
+	k = cq_howmany(cq);
+	if (k > cq->max_in)
+		cq->max_in = k;
+
+	return p;
+}
diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c
new file mode 100644
index 0000000..daa79fc
--- /dev/null
+++ b/drivers/usb/host/fhci-dbg.c
@@ -0,0 +1,133 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#ifndef CONFIG_FHCI_DEBUG
+
+static void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) {}
+static void fhci_dfs_destroy(struct fhci_hcd *fhci) {}
+static void fhci_dfs_create(struct fhci_hcd *fhci) {}
+
+#else
+
+static void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er)
+{
+	int i;
+
+	if (usb_er == -1) {
+		fhci->usb_irq_stat[12]++;
+		return;
+	}
+
+	for (i = 0; i < 12; ++i) {
+		if (usb_er & (1 << i))
+			fhci->usb_irq_stat[i]++;
+	}
+}
+
+static int fhci_dfs_regs_show(struct seq_file *s, void *v)
+{
+	struct fhci_hcd *fhci = s->private;
+	struct fhci_regs __iomem *regs = fhci->regs;
+
+	seq_printf(s,
+		"mode: 0x%x\n" "addr: 0x%x\n"
+		"command: 0x%x\n" "ep0: 0x%x\n"
+		"event: 0x%x\n" "mask: 0x%x\n"
+		"status: 0x%x\n" "SOF timer: %d\n"
+		"frame number: %d\n"
+		"lines status: 0x%x\n",
+		in_8(&regs->usb_mod), in_8(&regs->usb_addr),
+		in_8(&regs->usb_comm), in_be16(&regs->usb_ep[0]),
+		in_be16(&regs->usb_event), in_be16(&regs->usb_mask),
+		in_8(&regs->usb_status), in_be16(&regs->usb_sof_tmr),
+		in_be16(&regs->usb_frame_num),
+		fhci_ioports_check_bus_state(fhci));
+
+	return 0;
+}
+
+static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v)
+{
+	struct fhci_hcd *fhci = s->private;
+	int *usb_irq_stat = fhci->usb_irq_stat;
+
+	seq_printf(s,
+		"RXB: %d\n" "TXB: %d\n" "BSY: %d\n"
+		"SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n"
+		"TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n"
+		"RESET: %d\n" "SFT: %d\n" "MSF: %d\n"
+		"IDLE_ONLY: %d\n",
+		usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2],
+		usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5],
+		usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8],
+		usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11],
+		usb_irq_stat[12]);
+
+	return 0;
+}
+
+static int fhci_dfs_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, fhci_dfs_regs_show, inode->i_private);
+}
+
+static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, fhci_dfs_irq_stat_show, inode->i_private);
+}
+
+static const struct file_operations fhci_dfs_regs_fops = {
+	.open = fhci_dfs_regs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static const struct file_operations fhci_dfs_irq_stat_fops = {
+	.open = fhci_dfs_irq_stat_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static void fhci_dfs_create(struct fhci_hcd *fhci)
+{
+	struct device *dev = fhci_to_hcd(fhci)->self.controller;
+
+	fhci->dfs_root = debugfs_create_dir(dev->bus_id, NULL);
+	if (!fhci->dfs_root || IS_ERR(fhci->dfs_root))
+		return;
+
+	fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO,
+		fhci->dfs_root, fhci, &fhci_dfs_regs_fops);
+
+	fhci->dfs_irq_stat = debugfs_create_file("irq_stat",
+		S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
+		&fhci_dfs_irq_stat_fops);
+}
+
+static void fhci_dfs_destroy(struct fhci_hcd *fhci)
+{
+	debugfs_remove(fhci->dfs_irq_stat);
+	debugfs_remove(fhci->dfs_regs);
+	debugfs_remove(fhci->dfs_root);
+}
+
+#endif /* CONFIG_FHCI_DEBUG */
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
new file mode 100644
index 0000000..cefbce3
--- /dev/null
+++ b/drivers/usb/host/fhci-hcd.c
@@ -0,0 +1,1391 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/of_platform.h>
+#include <asm/qe.h>
+#include <asm/gpio.h>
+#include "../core/hcd.h"
+
+#include "fhci.h"
+#include "fhci-hub.c"
+#include "fhci-q.c"
+#include "fhci-dbg.c"
+#include "fhci-mem.c"
+#include "fhci-cq.c"
+#if defined(CONFIG_FHCI_WITH_BDS)
+#include "fhci-bds.c"
+#elif defined(CONFIG_FHCI_WITH_TDS)
+#include "fhci-tds.c"
+#endif
+
+static void recycle_frame(struct fhci_usb *usb, struct packet *pkt)
+{
+	pkt->data = NULL;
+	pkt->len = 0;
+	pkt->status = USB_TD_OK;
+	pkt->info = 0;
+	pkt->priv_data = 0;
+
+	cq_put(usb->ep0->empty_frame_Q, pkt);
+}
+
+static void *get_empty_frame(struct fhci_usb *usb)
+{
+	return cq_get(usb->ep0->empty_frame_Q);
+}
+
+/* confirm submitted packet */
+static void transaction_confirm(struct fhci_usb *usb, struct packet *pkt)
+{
+	struct td *td;
+	struct packet *td_pkt;
+	struct ed *ed;
+	u32 trans_len;
+	bool td_done = false;
+
+	td = remove_td_from_frame(usb->actual_frame);
+	td_pkt = td->pkt;
+	trans_len = pkt->len;
+	td->status = pkt->status;
+	if (td->type == FHCI_TA_IN && td_pkt->info & PKT_DUMMY_PACKET) {
+		if (((u32) td->data + td->actual_len) && trans_len)
+			memcpy(td->data + td->actual_len, pkt->data,
+			       trans_len);
+		cq_put(usb->ep0->dummy_packets_Q, pkt->data);
+	}
+
+	recycle_frame(usb, pkt);
+
+	ed = td->ed;
+	if (ed->mode == FHCI_TF_ISO) {
+		if (ed->td_list.next->next != &ed->td_list) {
+			struct td *td_next =
+			    list_entry(ed->td_list.next->next, struct td,
+				       node);
+
+			td_next->start_frame = usb->actual_frame->frame_num;
+		}
+		td->actual_len = trans_len;
+		td_done = true;
+	} else if ((td->status & USB_TD_ERROR) &&
+			!(td->status & USB_TD_TX_ER_NAK)) {
+		/*
+		 * There was an error on the transaction (but not NAK).
+		 * If it is fatal error (data underrun, stall, bad pid or 3
+		 * errors exceeded), mark this TD as done.
+		 */
+		if ((td->status & USB_TD_RX_DATA_UNDERUN) ||
+				(td->status & USB_TD_TX_ER_STALL) ||
+				(td->status & USB_TD_RX_ER_PID) ||
+				(++td->error_cnt >= 3)) {
+			ed->state = FHCI_ED_HALTED;
+			td_done = true;
+
+			if (td->status & USB_TD_RX_DATA_UNDERUN) {
+				td->toggle = !td->toggle;
+				td->actual_len += trans_len;
+			}
+		} else {
+			/* it is not a fatal error -retry this transaction */
+			td->nak_cnt = 0;
+			td->error_cnt++;
+			td->status = USB_TD_OK;
+		}
+	} else if (td->status & USB_TD_TX_ER_NAK) {
+		/* there was a NAK response */
+		td->nak_cnt++;
+		td->error_cnt = 0;
+		td->status = USB_TD_OK;
+	} else {
+		/* there was no error on transaction */
+		td->error_cnt = 0;
+		td->nak_cnt = 0;
+		td->toggle = !td->toggle;
+		td->actual_len += trans_len;
+
+		if (td->len == td->actual_len)
+			td_done = true;
+	}
+
+	if (td_done)
+		move_td_from_ed_to_done_list(usb, ed);
+}
+
+void qe_usb_stop_tx(u8 ep)
+{
+	qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, ep, 0);
+}
+
+static void qe_usb_restart_tx(u8 ep)
+{
+	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep, 0);
+}
+
+/* Cancel transmission on the USB endpoint*/
+static void abort_transmission(struct fhci_usb *usb)
+{
+	/* issue stop Tx command */
+	qe_usb_stop_tx(EP_ZERO);
+	udelay(1000);
+	/* flush Tx FIFOs */
+	usb->fhci->regs->usb_comm = (u8) (USB_CMD_FLUSH_FIFO | EP_ZERO);
+	udelay(1000);
+	/* reset Tx BDs */
+	flush_bds(usb);
+	/* issue restart Tx command */
+	qe_usb_restart_tx(EP_ZERO);
+}
+
+/*
+ * Flush all transmitted packets from BDs
+ * This routine is called when disabling the USB port to flush all
+ * transmissions that are allready scheduled in the BDs
+ */
+void flush_all_transmissions(struct fhci_usb *usb)
+{
+	u8 mode;
+	struct td *td;
+
+	mode = usb->fhci->regs->usb_mod;
+	usb->fhci->regs->usb_mod = (u8) (mode & ~USB_MODE_EN);
+
+	flush_bds(usb);
+
+	while ((td = peek_td_from_frame(usb->actual_frame)) != NULL) {
+		struct packet *pkt = td->pkt;
+
+		pkt->status = USB_TD_TX_ER_TIMEOUT;
+		transaction_confirm(usb, pkt);
+	}
+
+	usb->actual_frame->frame_status = FRAME_END_TRANSMISSION;
+
+	/* reset the event register */
+	usb->fhci->regs->usb_event = 0xffff;
+	/* enable the USB controller */
+	usb->fhci->regs->usb_mod = (u8) (mode | USB_MODE_EN);
+}
+
+/*
+ * This function forms the packet and transmit the packet. This function
+ * will handle all endpoint type:ISO,interrupt,control and bulk
+ */
+static int add_packet(struct fhci_usb *usb, struct ed *ed,
+			    struct td *td)
+{
+	u32 fw_transaction_time, len = 0;
+	struct packet *pkt;
+	u8 *data = NULL;
+
+	/* calcalate data address,len and toggle and then add the transaction */
+	if (td->toggle == USB_TD_TOGGLE_CARRY)
+		td->toggle = ed->toggle_carry;
+
+	switch (ed->mode) {
+	case FHCI_TF_ISO:
+		len = td->len;
+		if (td->type != FHCI_TA_IN)
+			data = td->data;
+		break;
+	case FHCI_TF_CTRL:
+	case FHCI_TF_BULK:
+		len = min(td->len - td->actual_len, ed->max_pkt_size);
+		if (!((td->type == FHCI_TA_IN) &&
+		      ((len + td->actual_len) == td->len)))
+			data = td->data + td->actual_len;
+		break;
+	case FHCI_TF_INTR:
+		len = min(td->len, ed->max_pkt_size);
+		if (!((td->type == FHCI_TA_IN) &&
+		      ((td->len + CRC_SIZE) >= ed->max_pkt_size)))
+			data = td->data;
+	default:
+		break;
+	}
+
+	if (usb->port_status == FHCI_PORT_FULL)
+		fw_transaction_time = (((len + PROTOCOL_OVERHEAD) * 11) >> 4);
+	else
+		fw_transaction_time = ((len + PROTOCOL_OVERHEAD) * 6);
+
+	/* check if there's enough space in this frame to submit this TD */
+	if (usb->actual_frame->total_bytes + len + PROTOCOL_OVERHEAD >=
+			usb->max_bytes_per_frame)
+		return -1;
+
+	/* check if there's enough time in this frame to submit this TD */
+	if (usb->actual_frame->frame_status != FRAME_IS_PREPARED &&
+	    (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION ||
+	     (fw_transaction_time + usb->sw_transaction_time >=
+	      1000 - get_sof_timer_count(usb))))
+		return -1;
+
+	/* update frame object fields before transmitting */
+	pkt = get_empty_frame(usb);
+	if (!pkt)
+		return -1;
+	td->pkt = pkt;
+
+	pkt->info = 0;
+	if (data == NULL) {
+		data = cq_get(usb->ep0->dummy_packets_Q);
+		BUG_ON(!data);
+		pkt->info = PKT_DUMMY_PACKET;
+	}
+	pkt->data = data;
+	pkt->len = len;
+	pkt->status = USB_TD_OK;
+	/* update TD status field before transmitting */
+	td->status = USB_TD_INPROGRESS;
+	/* update actual frame time object with the actual transmission */
+	usb->actual_frame->total_bytes += (len + PROTOCOL_OVERHEAD);
+	add_td_to_frame(usb->actual_frame, td);
+
+	if (usb->port_status != FHCI_PORT_FULL &&
+			usb->port_status != FHCI_PORT_LOW) {
+		pkt->status = USB_TD_TX_ER_TIMEOUT;
+		pkt->len = 0;
+		transaction_confirm(usb, pkt);
+	} else if (host_transaction(usb, pkt, td->type, ed->dev_addr,
+			ed->ep_addr, ed->mode, ed->speed, td->toggle)) {
+		/* remove TD from actual frame */
+		list_del_init(&td->frame_lh);
+		td->status = USB_TD_OK;
+		if (pkt->info & PKT_DUMMY_PACKET)
+			cq_put(usb->ep0->dummy_packets_Q, pkt->data);
+		recycle_frame(usb, pkt);
+		usb->actual_frame->total_bytes -= (len + PROTOCOL_OVERHEAD);
+		return -1;
+	}
+
+	return len;
+}
+
+/*
+ * This function goes through the endpoint list and schedules the
+ * transactions within this list
+ */
+static int scan_ed_list(struct fhci_usb *usb,
+			struct list_head *list, enum fhci_tf_mode list_type)
+{
+	static const int frame_part[4] = {
+		[FHCI_TF_CTRL] = MAX_BYTES_PER_FRAME,
+		[FHCI_TF_ISO] = (MAX_BYTES_PER_FRAME *
+				 MAX_PERIODIC_FRAME_USAGE) / 100,
+		[FHCI_TF_BULK] = MAX_BYTES_PER_FRAME,
+		[FHCI_TF_INTR] = (MAX_BYTES_PER_FRAME *
+				  MAX_PERIODIC_FRAME_USAGE) / 100
+	};
+	struct list_head *ed_lh = NULL;
+	struct ed *ed;
+	struct td *td;
+	int ans = 1;
+	u32 save_transaction_time = usb->sw_transaction_time;
+
+	list_for_each(ed_lh, list) {
+		ed = list_entry(ed_lh, struct ed, node);
+		td = ed->td_head;
+
+		if (td != NULL && td->status != USB_TD_INPROGRESS) {
+			if (ed->state != FHCI_ED_OPER) {
+				if (ed->state == FHCI_ED_URB_DEL) {
+					td->status = USB_TD_OK;
+					move_td_from_ed_to_done_list(usb, ed);
+					ed->state = FHCI_ED_SKIP;
+				}
+			}
+			/*
+			 * if it isn't interrupt pipe or it is not iso pipe
+			 * and the interval time passed
+			 */
+			else if (!(list_type == FHCI_TF_INTR ||
+				   list_type == FHCI_TF_ISO) ||
+				 (((usb->actual_frame->frame_num -
+				    td->start_frame) & 0x7ff) >=
+				  td->interval)) {
+				if (add_packet(usb, ed, td) >= 0) {
+					/* update time stamps in the TD */
+					td->start_frame =
+					    usb->actual_frame->frame_num;
+					usb->sw_transaction_time +=
+					    save_transaction_time;
+
+					if (usb->actual_frame->total_bytes >=
+					    usb->max_bytes_per_frame) {
+						usb->actual_frame->
+						    frame_status =
+						    FRAME_DATA_END_TRANSMISSION;
+						push_dummy_bd(usb->ep0);
+						ans = 0;
+						break;
+					}
+					if (usb->actual_frame->total_bytes >=
+							frame_part[list_type])
+						break;
+				}
+			}
+		}
+	}
+
+	/* be fair to each ED(move list head around) */
+	move_head_to_tail(list);
+	usb->sw_transaction_time = save_transaction_time;
+
+	return ans;
+}
+
+static u32 rotate_frames(struct fhci_usb *usb)
+{
+	struct fhci_hcd *fhci = usb->fhci;
+
+	if (!list_empty(&usb->actual_frame->tds_list)) {
+		if ((((u16)(fhci->pram->frame_num & 0x07ff) -
+		      usb->actual_frame->frame_num) & 0x7ff) > 5)
+			flush_actual_frame(usb);
+		else
+			return -EINVAL;
+	}
+
+	usb->actual_frame->frame_status = FRAME_IS_PREPARED;
+	usb->actual_frame->frame_num = (u16) (fhci->pram->frame_num & 0x7ff);
+	usb->actual_frame->total_bytes = 0;
+
+	return 0;
+}
+
+/*
+ * This function schedule the USB transaction and will process the
+ * endpoint in the following order: iso, interrupt, control and bulk.
+ */
+static void schedule_transactions(struct fhci_usb *usb)
+{
+	int left = 1;
+
+	if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
+		if (rotate_frames(usb) != 0)
+			return;
+
+	if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
+		return;
+
+	if (usb->actual_frame->total_bytes == 0) {
+		/* schedule the next available ISO transfer
+		 *or next stage of the ISO transfer*/
+		scan_ed_list(usb, &usb->hc_list->iso_list, FHCI_TF_ISO);
+
+		/*
+		 * schedule the next available interrupt transfer or
+		 * the next stage of the interrupt transfer
+		 */
+		scan_ed_list(usb, &usb->hc_list->intr_list, FHCI_TF_INTR);
+
+		/*
+		 * schedule the next available control transfer
+		 * or the next stage of the control transfer
+		 */
+		left =
+		    scan_ed_list(usb, &usb->hc_list->ctrl_list, FHCI_TF_CTRL);
+	}
+
+	/*
+	 * schedule the next available bulk transfer or the next stage of the
+	 * bulk transfer
+	 */
+	if (left > 0)
+		scan_ed_list(usb, &usb->hc_list->bulk_list, FHCI_TF_BULK);
+}
+
+/* initialize the endpoint zero */
+u32 endpoint_zero_init(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
+		       u32 ring_len)
+{
+	u32 rc;
+
+	rc = create_endpoint(usb, data_mem, ring_len);
+	if (rc)
+		return rc;
+
+	/* inilialize endpoint registers */
+	init_endpoint_registers(usb, usb->ep0, data_mem);
+
+	return 0;
+}
+
+/* enable the USB interrupts */
+void fhci_usb_enable_interrupt(struct fhci_usb *usb)
+{
+	struct fhci_hcd *fhci = usb->fhci;
+
+	if (usb->intr_nesting_cnt == 1) {
+		/* initialize the USB interrupt */
+		enable_irq(fhci_to_hcd(fhci)->irq);
+
+		/* initialize the event register and mask register */
+		usb->fhci->regs->usb_event = 0xffff;
+		usb->fhci->regs->usb_mask = usb->saved_msk;
+
+		/* enable the timer interrupts */
+		enable_irq(fhci->timer_irq);
+	} else if (usb->intr_nesting_cnt > 1)
+		fhci_info(fhci, "unbalanced USB interrupts nesting\n");
+	usb->intr_nesting_cnt--;
+}
+
+/* diable the usb interrupt */
+void fhci_usb_disable_interrupt(struct fhci_usb *usb)
+{
+	struct fhci_hcd *fhci = usb->fhci;
+
+	if (usb->intr_nesting_cnt == 0) {
+		/* diable the timer interrupt */
+		disable_irq(fhci->timer_irq);
+
+		/* disable the usb interrupt */
+		disable_irq(fhci_to_hcd(fhci)->irq);
+		usb->fhci->regs->usb_mask = 0;
+	}
+	usb->intr_nesting_cnt++;
+}
+
+/* enable the USB controller */
+u32 fhci_usb_enable(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+	u8 mode;
+
+	/* configure IO ports for USB--in Linux kernel initialize */
+
+	usb->fhci->regs->usb_event = 0xffff;
+	usb->fhci->regs->usb_mask = usb->saved_msk;
+
+	mode = usb->fhci->regs->usb_mod;
+	mode |= USB_MODE_EN;
+	usb->fhci->regs->usb_mod = mode;
+
+	msleep(100);
+
+	return 0;
+}
+
+/* disable the USB controller */
+u32 fhci_usb_disable(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+	u8 mode;
+
+	fhci_usb_disable_interrupt(usb);
+	usb_port_disable(fhci);
+
+	/* disable the usb controller */
+	if (usb->port_status == FHCI_PORT_FULL ||
+			usb->port_status == FHCI_PORT_LOW)
+		device_disconnected_interrupt(fhci);
+
+	mode = usb->fhci->regs->usb_mod;
+	mode &= ~USB_MODE_EN;
+	usb->fhci->regs->usb_mod = mode;
+
+	return 0;
+}
+
+/* check the bus state by polling the QE bit on the IO ports */
+int fhci_ioports_check_bus_state(struct fhci_hcd *fhci)
+{
+	u8 bits = 0;
+
+	/* check USBOE,if transmitting,exit */
+	if (!gpio_get_value(fhci->gpios[GPIO_USBOE]))
+		return -1;
+
+	/* check USBRP */
+	if (gpio_get_value(fhci->gpios[GPIO_USBRP]))
+		bits |= 0x2;
+
+	/* check USBRN */
+	if (gpio_get_value(fhci->gpios[GPIO_USBRN]))
+		bits |= 0x1;
+
+	return bits;
+}
+
+/* Handles SOF interrupt */
+static void sof_interrupt(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	if ((usb->port_status == FHCI_PORT_DISABLED) &&
+	    (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_CONNECTION) &&
+	    !(usb->vroot_hub->port.wPortChange & USB_PORT_STAT_C_CONNECTION)) {
+		if (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_LOW_SPEED)
+			usb->port_status = FHCI_PORT_LOW;
+		else
+			usb->port_status = FHCI_PORT_FULL;
+		/* Disable IDLE */
+		usb->saved_msk &= ~USB_E_IDLE_MASK;
+		usb->fhci->regs->usb_mask = usb->saved_msk;
+	}
+
+	qe_reset_ref_timer_16(fhci->timer, 1000000, usb->max_frame_usage);
+
+	host_transmit_actual_frame(usb);
+	usb->actual_frame->frame_status = FRAME_IS_TRANSMITTED;
+
+	schedule_transactions(usb);
+}
+
+/* Handles device disconnected interrupt on port */
+static void device_disconnected_interrupt(struct fhci_hcd *fhci)
+{
+	u8 mode;
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	fhci_usb_disable_interrupt(usb);
+	mode = usb->fhci->regs->usb_mod;
+	mode &= ~USB_MODE_LSS;
+	usb->fhci->regs->usb_mod = mode;
+	usb->port_status = FHCI_PORT_DISABLED;
+
+	/* Enable IDLE since we want to know if something comes along */
+	usb->saved_msk |= USB_E_IDLE_MASK;
+	usb->fhci->regs->usb_mask = usb->saved_msk;
+
+	usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION;
+	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+	usb->max_bytes_per_frame = 0;
+	fhci_usb_enable_interrupt(usb);
+}
+
+/* detect a new device connected on the USB port*/
+static void device_connected_interrupt(struct fhci_hcd *fhci)
+{
+
+	struct fhci_usb *usb = fhci->usb_lld;
+	int state;
+	int ret;
+	u8 mode;
+
+	fhci_usb_disable_interrupt(usb);
+	state = fhci_ioports_check_bus_state(fhci);
+
+	/* low-speed device was connected to the USB port */
+	if (state == 1) {
+		ret = qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
+		if (ret) {
+			fhci_info(fhci, "Low-Speed device is not supported, "
+				  "try use BRGx\n");
+			return;
+		}
+
+		mode = usb->fhci->regs->usb_mod;
+		mode |= USB_MODE_LSS;
+		usb->fhci->regs->usb_mod = mode;
+		usb->vroot_hub->port.wPortStatus |=
+		    (USB_PORT_STAT_LOW_SPEED |
+		     USB_PORT_STAT_CONNECTION);
+		usb->vroot_hub->port.wPortChange |=
+		    USB_PORT_STAT_C_CONNECTION;
+		usb->max_bytes_per_frame =
+		    (MAX_BYTES_PER_FRAME >> 3) - 7;
+		usb_port_enable(usb);
+	} else if (state == 2) {
+		ret = qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
+		if (ret) {
+			fhci_info(fhci, "Full-Speed device is not supported, "
+				  "try use CLKx\n");
+			return;
+		}
+
+		mode = usb->fhci->regs->usb_mod;
+		mode &= ~USB_MODE_LSS;
+		usb->fhci->regs->usb_mod = mode;
+		usb->vroot_hub->port.wPortStatus &=
+		    ~USB_PORT_STAT_LOW_SPEED;
+		usb->vroot_hub->port.wPortStatus |=
+		    USB_PORT_STAT_CONNECTION;
+		usb->vroot_hub->port.wPortChange |=
+		    USB_PORT_STAT_C_CONNECTION;
+		usb->max_bytes_per_frame = (MAX_BYTES_PER_FRAME - 15);
+		usb_port_enable(usb);
+	}
+	fhci_usb_enable_interrupt(usb);
+}
+
+irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd)
+{
+	struct usb_hcd *hcd = _hcd;
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	spin_lock(&fhci->lock);
+
+	qe_reset_ref_timer_16(fhci->timer, 1000000, 1000);
+
+	if (usb->actual_frame->frame_status == FRAME_IS_TRANSMITTED) {
+		usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+		push_dummy_bd(usb->ep0);
+	}
+
+	schedule_transactions(usb);
+
+	spin_unlock(&fhci->lock);
+
+	return IRQ_HANDLED;
+}
+
+
+
+static irqreturn_t fhci_irq(struct usb_hcd *hcd)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	struct fhci_usb *usb;
+	register u16 usb_er = 0;
+
+	spin_lock(&fhci->lock);
+
+	usb = fhci->usb_lld;
+
+	usb->intr_counter++;
+	do {
+		usb_er |= (u16) (usb->fhci->regs->usb_event &
+				 usb->fhci->regs->usb_mask);
+
+		/* clear event bits for next time */
+		usb->fhci->regs->usb_event = usb_er;
+
+		fhci_dbg_isr(fhci, usb_er);
+
+		if (usb_er & USB_E_RESET_MASK) {
+			if ((usb->port_status == FHCI_PORT_FULL) ||
+			    (usb->port_status == FHCI_PORT_LOW)) {
+				device_disconnected_interrupt(fhci);
+				usb_er &= ~USB_E_IDLE_MASK;
+			} else if (usb->port_status ==
+				   FHCI_PORT_WAITING) {
+				usb->port_status = FHCI_PORT_DISCONNECTING;
+
+				/* Turn on IDLE since we want to disconnect */
+				usb->saved_msk |= USB_E_IDLE_MASK;
+				usb->fhci->regs->usb_event = usb->saved_msk;
+			} else if (usb->port_status == FHCI_PORT_DISABLED) {
+				if (fhci_ioports_check_bus_state(fhci) == 1)
+					device_connected_interrupt(fhci);
+			}
+			usb_er &= ~USB_E_RESET_MASK;
+		}
+
+		if (usb_er & USB_E_MSF_MASK) {
+			abort_transmission(fhci->usb_lld);
+			usb_er &= ~USB_E_MSF_MASK;
+		}
+
+		if (usb_er & (USB_E_SOF_MASK | USB_E_SFT_MASK)) {
+			sof_interrupt(fhci);
+			usb_er &= ~(USB_E_SOF_MASK | USB_E_SFT_MASK);
+		}
+#ifdef CONFIG_FHCI_WITH_BDS
+		if (usb_er & USB_E_RXB_MASK) {
+			if (receive_packet_interrupt(fhci) == 0)
+				usb_er &= ~USB_E_RXB_MASK;
+		}
+#endif /* CONFIG_FHCI_WITH_BDS */
+
+		if (usb_er & USB_E_TXB_MASK) {
+			tx_conf_interrupt(fhci->usb_lld);
+			usb_er &= ~USB_E_TXB_MASK;
+#ifdef CONFIG_FHCI_WITH_BDS
+			continue;
+#endif /* CONFIG_FHCI_WITH_BDS */
+		}
+
+		if (usb_er & USB_E_TXE1_MASK) {
+			tx_conf_interrupt(fhci->usb_lld);
+			usb_er &= ~USB_E_TXE1_MASK;
+#ifdef CONFIG_FHCI_WITH_BDS
+			continue;
+#endif /* CONFIG_FHCI_WITH_BDS */
+		}
+
+		if (usb_er & USB_E_IDLE_MASK) {
+			if (usb->port_status == FHCI_PORT_DISABLED) {
+				usb_er &= ~USB_E_RESET_MASK;
+				device_connected_interrupt(fhci);
+			} else if (usb->port_status ==
+					FHCI_PORT_DISCONNECTING) {
+				usb->port_status = FHCI_PORT_WAITING;
+
+				/* Disable IDLE */
+				usb->saved_msk &= ~USB_E_IDLE_MASK;
+				usb->fhci->regs->usb_mask = usb->saved_msk;
+			} else
+				fhci_dbg_isr(fhci, -1);
+
+			usb_er &= ~USB_E_IDLE_MASK;
+		}
+	} while (usb_er);
+
+	spin_unlock(&fhci->lock);
+
+	return IRQ_HANDLED;
+}
+
+static void fhci_mem_free(struct fhci_hcd *fhci)
+{
+	struct td *td;
+	struct ed *ed;
+
+	while (!list_empty(&fhci->empty_eds)) {
+		ed = list_entry(fhci->empty_eds.next, struct ed, node);
+		list_del(fhci->empty_eds.next);
+	}
+
+	while (!list_empty(&fhci->empty_tds)) {
+		td = list_entry(fhci->empty_tds.next, struct td, node);
+		list_del(fhci->empty_tds.next);
+	}
+
+	kfree(fhci->vroot_hub);
+	kfree(fhci->hc_list);
+}
+
+DECLARE_TASKLET(fhci_tasklet, process_done_list, 0);
+
+static int fhci_mem_init(struct fhci_hcd *fhci)
+{
+	int i, error = 0;
+
+	fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL);
+	if (!fhci->hc_list)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&fhci->hc_list->ctrl_list);
+	INIT_LIST_HEAD(&fhci->hc_list->bulk_list);
+	INIT_LIST_HEAD(&fhci->hc_list->iso_list);
+	INIT_LIST_HEAD(&fhci->hc_list->intr_list);
+	INIT_LIST_HEAD(&fhci->hc_list->done_list);
+
+	fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL);
+	if (!fhci->vroot_hub)
+		return -ENOMEM;
+
+
+	INIT_LIST_HEAD(&fhci->empty_eds);
+	INIT_LIST_HEAD(&fhci->empty_tds);
+
+	/* initialize work queue to handle done list */
+	fhci_tasklet.data = (unsigned long)fhci;
+	fhci->process_done_task = &fhci_tasklet;
+
+	for (i = 0; i < MAX_TDS; i++) {
+		struct td *td = kmalloc(sizeof(*td), GFP_KERNEL);
+
+		if (!td) {
+			error = 1;
+			break;
+		}
+		recycle_empty_td(fhci, td);
+	}
+	for (i = 0; i < MAX_EDS; i++) {
+		struct ed *ed = kmalloc(sizeof(*ed), GFP_KERNEL);
+
+		if (!ed) {
+			error = 1;
+			break;
+		}
+		recycle_empty_ed(fhci, ed);
+	}
+
+	if (error) {
+		fhci_mem_free(fhci);
+		return -ENOMEM;
+	}
+
+	fhci->active_urbs = 0;
+
+	return error;
+}
+
+/* transfer complted callback*/
+static u32 transfer_confirm_callback(struct fhci_hcd *fhci)
+{
+	if (!fhci->process_done_task->state)
+		tasklet_schedule(fhci->process_done_task);
+	return 0;
+}
+
+/* destroy the fhci_usb structure */
+void fhci_usb_free(void *lld)
+{
+	struct fhci_usb *usb = lld;
+	struct fhci_hcd *fhci = usb->fhci;
+
+	if (usb) {
+		config_transceiver(fhci, FHCI_OP_POWER_OFF);
+		config_transceiver(fhci, FHCI_OP_DISCONNECT);
+
+		endpoint_zero_free(usb);
+#if defined(CONFIG_FHCI_HAS_NO_RT_SOF)
+		kfree(usb->sof_pkts);
+#elif defined(CONFIG_FHCI_HAS_UC_RT_SOF)
+		u8 *tmp_crc5_pkts = phys_to_virt(usb->pram->sof_tbl);
+		kfree(tmp_crc5_pkts);
+#endif
+		kfree(usb->actual_frame);
+		kfree(usb);
+	}
+}
+
+/* initialize the USB*/
+u32 fhci_usb_init(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+	u8 mode = 0;
+
+	memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE);
+
+	usb->port_status = FHCI_PORT_DISABLED;
+	usb->max_frame_usage = FRAME_TIME_USAGE;
+#ifdef CONFIG_FHCI_WITH_BDS
+	/* the predefined time limitation are for the driver with
+	 * transcation level interface, so we have to take about 2 times
+	 */
+	usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION * 2;
+#else
+	usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION;
+#endif
+
+	usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL);
+	if (!usb->actual_frame) {
+		fhci_usb_free(usb);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&usb->actual_frame->tds_list);
+
+	/* initializing registers on chip clear frame number */
+	fhci->pram->frame_num = 0;
+
+	/* clear rx state */
+	fhci->pram->rx_state = 0;
+
+	/* set mask register */
+	usb->saved_msk = (USB_E_TXB_MASK |
+			  USB_E_TXE1_MASK |
+			  USB_E_IDLE_MASK |
+			  USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK);
+
+#ifdef CONFIG_FHCI_WITH_BDS
+	usb->saved_msk |= USB_E_RXB_MASK;
+#endif
+
+	/* config the usb mode register */
+	mode = USB_MODE_HOST | USB_MODE_EN;
+	usb->fhci->regs->usb_mod = mode;
+
+	/* clearing the mask register */
+	usb->fhci->regs->usb_mask = (u16) 0x0;
+
+	/* initialing the event register */
+	usb->fhci->regs->usb_event = (u16) 0xffff;
+
+	if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) {
+		fhci_usb_free(usb);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* initialize the fhci_usb struct and the corresponding data staruct*/
+struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb;
+
+	/* allocate memory for SCC data structure */
+	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+	if (!usb) {
+		fhci_err(fhci, "no memory for SCC data struct\n");
+		return NULL;
+	}
+
+	usb->fhci = fhci;
+	usb->hc_list = fhci->hc_list;
+	usb->vroot_hub = fhci->vroot_hub;
+
+	usb->transfer_confirm = transfer_confirm_callback;
+
+	return usb;
+}
+
+static int fhci_start(struct usb_hcd *hcd)
+{
+	int ret;
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+	ret = fhci_mem_init(fhci);
+
+	fhci->usb_lld = fhci_create_lld(fhci);
+	if (!fhci->usb_lld) {
+		fhci_err(fhci, "low level driver config failed\n");
+		fhci_mem_free(fhci);
+		return -ENODEV;
+	}
+	if (fhci_usb_init(fhci)) {
+		fhci_err(fhci, "low level driver initialize failed\n");
+		fhci_mem_free(fhci);
+		return -ENODEV;
+	}
+	spin_lock_init(&fhci->lock);
+
+	/* connect the virtual root hub */
+	fhci->vroot_hub->dev_num = 1;	/* this field may be needed to fix */
+	fhci->vroot_hub->hub.wHubStatus = 0;
+	fhci->vroot_hub->hub.wHubChange = 0;
+	fhci->vroot_hub->port.wPortStatus = 0;
+	fhci->vroot_hub->port.wPortChange = 0;
+
+	hcd->state = HC_STATE_RUNNING;
+
+	/*
+	 * From here on, khubd concurrently accesses the root
+	 * hub; drivers will be talking to enumerated devices.
+	 * (On restart paths, khubd already knows about the root
+	 * hub and could find work as soon as we wrote FLAG_CF.)
+	 *
+	 * Before this point the HC was idle/ready.  After, khubd
+	 * and device drivers may start it running.
+	 */
+	fhci_usb_enable(fhci);
+
+	return 0;
+}
+
+static void fhci_stop(struct usb_hcd *hcd)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+	fhci_usb_disable_interrupt(fhci->usb_lld);
+	fhci_usb_disable(fhci);
+
+	fhci_usb_free(fhci->usb_lld);
+	fhci->usb_lld = NULL;
+	fhci_mem_free(fhci);
+}
+
+static int fhci_urb_enqueue(struct usb_hcd *hcd,
+			    struct urb *urb, gfp_t mem_flags)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	u32 pipe = urb->pipe;
+	int i, size = 0;
+	struct urb_priv *urb_priv;
+
+	spin_lock(&fhci->lock);
+
+	switch (usb_pipetype(pipe)) {
+	case PIPE_CONTROL:
+		/* 1 td fro setup,1 for ack */
+		size = 2;
+	case PIPE_BULK:
+		/* one td for every 4096 bytes(can be upto 8k) */
+		size += urb->transfer_buffer_length / 4096;
+		/* ...add for any remaining bytes... */
+		if ((urb->transfer_buffer_length % 4096) != 0)
+			size++;
+		/* ..and maybe a zero length packet to wrap it up */
+		if (size == 0)
+			size++;
+		else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
+			 && (urb->transfer_buffer_length
+			     % usb_maxpacket(urb->dev, pipe,
+					     usb_pipeout(pipe))) != 0)
+			size++;
+		break;
+	case PIPE_ISOCHRONOUS:
+		size = urb->number_of_packets;
+		if (size <= 0)
+			return -EINVAL;
+		for (i = 0; i < urb->number_of_packets; i++) {
+			urb->iso_frame_desc[i].actual_length = 0;
+			urb->iso_frame_desc[i].status = (u32) (-EXDEV);
+		}
+		break;
+	case PIPE_INTERRUPT:
+		size = 1;
+	}
+
+	/* allocate the private part of the URB */
+	urb_priv = kzalloc(sizeof(*urb_priv), mem_flags);
+	if (!urb_priv)
+		return -ENOMEM;
+
+	/* allocate the private part of the URB */
+	urb_priv->tds = kzalloc(size * sizeof(struct td), mem_flags);
+	if (!urb_priv->tds) {
+		kfree(urb_priv);
+		return -ENOMEM;
+	}
+
+	/* fill the private part of the URB */
+	urb_priv->num_of_tds = size;
+
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urb->error_count = 0;
+	urb->hcpriv = urb_priv;
+
+	queue_urb(fhci, urb);
+
+	spin_unlock(&fhci->lock);
+	return 0;
+}
+
+/* dequeue FHCI URB */
+static int fhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	spin_lock(&fhci->lock);
+
+	if (!urb || !urb->dev || !urb->dev->bus)
+		goto out;
+
+	if (usb->port_status != FHCI_PORT_DISABLED) {
+		struct urb_priv *urb_priv;
+
+		/*
+		 * flag the urb's data for deletion in some upcoming
+		 * SF interrupt's delete list processing
+		 */
+		urb_priv = urb->hcpriv;
+
+		if (!urb_priv || (urb_priv->state == URB_DEL))
+			goto out;
+
+		urb_priv->state = URB_DEL;
+
+		/* already pending? */
+		urb_priv->ed->state = FHCI_ED_URB_DEL;
+	} else
+		urb_complete_free(fhci, urb);
+out:
+	spin_unlock(&fhci->lock);
+
+	return 0;
+}
+
+static void fhci_endpoint_disable(struct usb_hcd *hcd,
+				  struct usb_host_endpoint *ep)
+{
+	struct fhci_hcd *fhci;
+	struct ed *ed;
+
+	fhci = hcd_to_fhci(hcd);
+	spin_lock(&fhci->lock);
+	ed = ep->hcpriv;
+	if (ed) {
+		while (ed->td_head != NULL) {
+			struct td *td = remove_td_from_ed(ed);
+			urb_complete_free(fhci, td->urb);
+		}
+		recycle_empty_ed(fhci, ed);
+		ep->hcpriv = NULL;
+	}
+	spin_unlock(&fhci->lock);
+}
+
+static int fhci_get_frame_number(struct usb_hcd *hcd)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+	return get_frame_num(fhci);
+}
+
+static const struct hc_driver fhci_driver = {
+	.description = "fsl,usb-fhci",
+	.product_desc = "FHCI HOST Controller",
+	.hcd_priv_size = sizeof(struct fhci_hcd),
+
+	/* generic hardware linkage */
+	.irq = fhci_irq,
+	.flags = HCD_USB11,
+
+	/* basic lifecycle operation */
+	.start = fhci_start,
+	.stop = fhci_stop,
+
+	/* managing i/o requests and associated device resources */
+	.urb_enqueue = fhci_urb_enqueue,
+	.urb_dequeue = fhci_urb_dequeue,
+	.endpoint_disable = fhci_endpoint_disable,
+
+	/* scheduling support */
+	.get_frame_number = fhci_get_frame_number,
+
+	/* root hub support */
+	.hub_status_data = fhci_hub_status_data,
+	.hub_control = fhci_hub_control,
+};
+
+struct fhci_probe_info {
+	struct resource regs;
+	unsigned long pram_addr;
+	struct resource usb_irq;
+	int gpios[NUM_GPIOS];
+	enum qe_clock fullspeed_clk;
+	enum qe_clock lowspeed_clk;
+	unsigned int power_budget;
+};
+
+static int __devinit fhci_probe(struct device *dev, struct fhci_probe_info *pi)
+{
+	unsigned long ret;
+	int i;
+	struct usb_hcd *hcd = 0;
+	struct fhci_hcd *fhci;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	hcd = usb_create_hcd(&fhci_driver, dev, dev->bus_id);
+	if (!hcd) {
+		dev_dbg(dev, "could not create hcd\n");
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(dev, hcd);
+	fhci = hcd_to_fhci(hcd);
+
+	hcd->self.controller = dev;
+	hcd->power_budget = pi->power_budget;
+	hcd->regs = ioremap(pi->regs.start, pi->regs.end - pi->regs.start + 1);
+	fhci->regs = hcd->regs;
+	memcpy(fhci->gpios, pi->gpios, sizeof(fhci->gpios));
+
+	ret = qe_muram_alloc_fixed(pi->pram_addr, FHCI_PRAM_SIZE);
+	if (IS_ERR_VALUE(ret) || ret != pi->pram_addr) {
+		dev_err(dev, "failed to allocate usb pram\n");
+		goto err_pram_alloc;
+	}
+	fhci->pram = qe_muram_addr(pi->pram_addr);
+
+	for (i = 0; i < NUM_GPIOS; i++) {
+		int gpio = fhci->gpios[i];
+
+		if (gpio < 0) {
+			if (gpio < GPIO_SPEED) {
+				dev_err(dev, "incorrect GPIO%d: %d\n",
+					i, gpio);
+				goto err_gpios;
+			} else {
+				dev_info(dev, "assuming board doesn't have "
+					"%s gpio\n", gpio == GPIO_SPEED ?
+					"speed" : "suspn");
+			}
+		}
+
+		ret = gpio_request(gpio, dev->bus_id);
+		if (ret) {
+			dev_err(dev, "failed to request gpio %d", i);
+			goto err_gpios;
+		}
+	}
+
+	ret = fhci->timer = qe_get_timer(16, &fhci->timer_irq);
+	if (ret < 0) {
+		dev_err(dev, "failed to request qe timer: %li", ret);
+		goto err_get_timer;
+	}
+
+	fhci->fullspeed_clk = pi->fullspeed_clk;
+	fhci->lowspeed_clk = pi->lowspeed_clk;
+
+	ret = request_irq(fhci->timer_irq, fhci_frame_limit_timer_irq,
+			  IRQF_DISABLED, "qe timer (usb)", hcd);
+	if (ret) {
+		dev_err(dev, "failed to request timer irq");
+		goto err_timer_irq;
+	}
+
+	dev_info(dev, "at 0x%p,irq %d\n", hcd->regs, pi->usb_irq.start);
+
+	/* start with low-speed, if possible */
+	if (fhci->lowspeed_clk != QE_CLK_NONE)
+		qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
+	else
+		qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
+
+	config_transceiver(fhci, FHCI_OP_HOST);
+
+	ret = usb_add_hcd(hcd, pi->usb_irq.start, IRQF_DISABLED);
+	if (ret < 0)
+		goto err_add_hcd;
+
+	fhci_dfs_create(fhci);
+
+	return 0;
+
+err_add_hcd:
+	free_irq(fhci->timer_irq, hcd);
+err_timer_irq:
+	qe_put_timer(fhci->timer);
+err_get_timer:
+err_gpios:
+	while (--i >= 0) {
+		if (fhci->gpios[i] >= 0)
+			gpio_free(fhci->gpios[i]);
+	}
+err_pram_alloc:
+	usb_put_hcd(hcd);
+	return ret;
+}
+
+static int __devexit fhci_remove(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+	fhci_dfs_destroy(fhci);
+	usb_remove_hcd(hcd);
+	free_irq(fhci->timer_irq, hcd);
+	qe_put_timer(fhci->timer);
+	qe_muram_free(qe_muram_offset(fhci->pram));
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static int __devinit of_fhci_probe(struct of_device *ofdev,
+				   const struct of_device_id *ofid)
+{
+	int ret;
+	struct device *dev = &ofdev->dev;
+	struct fhci_probe_info pi = {
+		.lowspeed_clk = QE_CLK_NONE,
+		.fullspeed_clk = QE_CLK_NONE,
+	};
+	int size;
+	const unsigned long *pram_addr;
+	const char *clk;
+	const u32 *power_budget;
+	struct device_node *np;
+	int i;
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,qe-muram-usb-pram");
+	if (!np) {
+		dev_err(dev, "can't find usb-pram node\n");
+		return -ENOENT;
+	}
+
+	pram_addr = of_get_property(np, "reg", &size);
+	if (!pram_addr || size < sizeof(*pram_addr)) {
+		dev_err(dev, "can't get pram offset\n");
+		of_node_put(np);
+		return -EINVAL;;
+	}
+	pi.pram_addr = *pram_addr;
+	of_node_put(np);
+
+	ret = of_address_to_resource(ofdev->node, 0, &pi.regs);
+	if (ret) {
+		dev_err(dev, "can't get regs\n");
+		return -EINVAL;
+	}
+
+	clk = of_get_property(ofdev->node, "fullspeed-clock", NULL);
+	if (clk) {
+		pi.fullspeed_clk = qe_clock_source(clk);
+		if (pi.fullspeed_clk == QE_CLK_DUMMY) {
+			dev_err(dev, "wrong fullspeed-clock\n");
+			return -EINVAL;
+		}
+	}
+
+	clk = of_get_property(ofdev->node, "lowspeed-clock", NULL);
+	if (clk) {
+		pi.lowspeed_clk = qe_clock_source(clk);
+		if (pi.lowspeed_clk == QE_CLK_DUMMY) {
+			dev_err(dev, "wrong lowspeed-clock\n");
+			return -EINVAL;
+		}
+	}
+
+	if (pi.fullspeed_clk == QE_CLK_NONE &&
+			pi.lowspeed_clk == QE_CLK_NONE) {
+		dev_err(dev, "no clocks specified\n");
+		return -EINVAL;
+	}
+
+	power_budget = of_get_property(ofdev->node, "hub-power-budget", &size);
+	if (power_budget && size == sizeof(*power_budget))
+		pi.power_budget = *power_budget;
+
+	ret = of_irq_to_resource(ofdev->node, 0, &pi.usb_irq);
+	if (ret == NO_IRQ) {
+		dev_err(dev, "can't get usb irq\n");
+		return ret;
+	}
+
+	/* gpios error and sanity checks are in the fhci_probe() */
+	for (i = 0; i < NUM_GPIOS; i++)
+		pi.gpios[i] = of_get_gpio(ofdev->node, i);
+
+	return fhci_probe(dev, &pi);
+}
+
+static int __devexit of_fhci_remove(struct of_device *ofdev)
+{
+	return fhci_remove(&ofdev->dev);
+}
+
+static const struct of_device_id of_fhci_match[] = {
+	{ .compatible = "fsl,usb-fhci", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_fhci_match);
+
+static struct of_platform_driver of_fhci_driver = {
+	.name		= "fsl,usb-fhci",
+	.match_table	= of_fhci_match,
+	.probe		= of_fhci_probe,
+	.remove		= __devexit_p(of_fhci_remove),
+};
+
+static int __init fhci_module_init(void)
+{
+	return of_register_platform_driver(&of_fhci_driver);
+}
+module_init(fhci_module_init);
+
+static void __exit fhci_module_exit(void)
+{
+	of_unregister_platform_driver(&of_fhci_driver);
+}
+module_exit(fhci_module_exit);
+
+MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver");
+MODULE_AUTHOR("Shlomi Gridish <gridish at freescale.com>, "
+	      "Jerry Huang <Chang-Ming.Huang at freescale.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c
new file mode 100644
index 0000000..38b5cd3
--- /dev/null
+++ b/drivers/usb/host/fhci-hub.c
@@ -0,0 +1,343 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+/* Virtual root hub specific descriptor */
+static u8 root_hub_des[] = {
+	0x09,			/* blength */
+	0x29,			/* bDescriptorType;hub-descriptor */
+	0x01,			/* bNbrPorts */
+	0x00,			/* wHubCharacteristics */
+	0x00,
+	0x01,			/* bPwrOn2pwrGood;2ms */
+	0x00,			/* bHubContrCurrent;0mA */
+	0x00,			/* DeviceRemoveable */
+	0xff			/* PortPwrCtrlMask */
+};
+
+static void fhci_start_sof_timer(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+	u8 mode;
+
+	/* clear frame_n */
+	fhci->pram->frame_num = 0;
+
+#ifdef CONFIG_FHCI_HAS_EOP_MISSING_BUG
+	usb->eop_missing_bug_indicator = 0;
+#endif
+
+	fhci->regs->usb_sof_tmr = 0;
+	mode = fhci->regs->usb_mod;
+	mode |= USB_MODE_SFTE;
+	fhci->regs->usb_mod = mode;
+
+	qe_reset_ref_timer_16(fhci->timer, 1000000, usb->max_frame_usage);
+}
+
+static void fhci_stop_sof_timer(struct fhci_hcd *fhci)
+{
+	u8 mode;
+
+	mode = fhci->regs->usb_mod;
+	mode &= ~USB_MODE_SFTE;
+	fhci->regs->usb_mod = mode;
+
+	qe_stop_timer(fhci->timer);
+}
+
+static u16 get_sof_timer_count(struct fhci_usb *usb)
+{
+	return (u16) (usb->fhci->regs->usb_sof_tmr / 12);
+}
+
+void config_transceiver(struct fhci_hcd *fhci, enum fhci_op_mode mode)
+{
+	fhci_dbg(fhci, "%s: %d\n", __func__, mode);
+
+	switch (mode) {
+	case FHCI_OP_HOST:
+		if (fhci->gpios[GPIO_SPEED] >= 0)
+			gpio_set_value(fhci->gpios[GPIO_SPEED], 1);
+		if (fhci->gpios[GPIO_SUSPN] >= 0)
+			gpio_set_value(fhci->gpios[GPIO_SUSPN], 1);
+		if (fhci->gpios[GPIO_SPEED] >= 0 ||
+				fhci->gpios[GPIO_SUSPN] >= 0)
+			msleep(100);
+		break;
+	case FHCI_OP_DISCONNECT:
+		if (fhci->gpios[GPIO_SPEED] >= 0)
+			gpio_set_value(fhci->gpios[GPIO_SPEED], 1);
+		if (fhci->gpios[GPIO_SUSPN] >= 0)
+			gpio_set_value(fhci->gpios[GPIO_SUSPN], 1);
+		break;
+	case FHCI_OP_POWER_ON:
+		/* vcc on */
+		if (fhci->gpios[GPIO_SUSPN] >= 0) {
+			gpio_set_value(fhci->gpios[GPIO_SUSPN], 0);
+			msleep(100);
+		}
+		break;
+	case FHCI_OP_POWER_OFF:
+		/* vcc off */
+		if (fhci->gpios[GPIO_SUSPN] >= 0)
+			gpio_set_value(fhci->gpios[GPIO_SUSPN], 1);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+}
+
+/* disable the USB port by clearing the EN bit in the USBMOD register */
+void usb_port_disable(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
+	enum fhci_port_status port_status;
+
+	fhci_stop_sof_timer(fhci);
+
+	flush_all_transmissions(usb);
+
+	config_transceiver(fhci, FHCI_OP_POWER_OFF);
+
+	fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
+	port_status = usb->port_status;
+	usb->port_status = FHCI_PORT_DISABLED;
+
+	/* Enable IDLE since we want to know if something comes along */
+	usb->saved_msk |= USB_E_IDLE_MASK;
+	usb->fhci->regs->usb_mask = usb->saved_msk;
+
+	/* check if during the disconnection process attached new device */
+	if (port_status == FHCI_PORT_WAITING)
+		device_connected_interrupt(fhci);
+	usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
+	fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
+}
+
+/* enable the USB port by setting the EN bit in the USBMOD register */
+void usb_port_enable(void *lld)
+{
+	struct fhci_usb *usb = (struct fhci_usb *)lld;
+	struct fhci_hcd *fhci = usb->fhci;
+
+	if ((usb->port_status != FHCI_PORT_FULL) &&
+	    (usb->port_status != FHCI_PORT_LOW))
+		fhci_start_sof_timer(fhci);
+	udelay(5000);
+	usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
+}
+
+static void io_port_generate_reset(struct fhci_hcd *fhci)
+{
+	gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
+	gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
+	gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
+
+	udelay(5000);
+
+	gpio_set_dedicated(fhci->gpios[GPIO_USBOE], 0);
+	gpio_set_dedicated(fhci->gpios[GPIO_USBTP], 0);
+	gpio_set_dedicated(fhci->gpios[GPIO_USBTN], 0);
+}
+
+/* generate the RESET condition on the bus */
+void usb_port_reset(void *lld)
+{
+	struct fhci_usb *usb = (struct fhci_usb *)lld;
+	struct fhci_hcd *fhci = usb->fhci;
+	u8 mode;
+	u16 mask;
+
+	fhci_stop_sof_timer(fhci);
+	/* disable the USB controller */
+	mode = fhci->regs->usb_mod;
+	fhci->regs->usb_mod = mode & (~USB_MODE_EN);
+
+	/* disable idle interrupts */
+	mask = fhci->regs->usb_mask;
+	fhci->regs->usb_mask = mask & (~USB_E_IDLE_MASK);
+
+	io_port_generate_reset(fhci);
+
+	/* enable interrupt on this endpoint */
+	fhci->regs->usb_mask = mask;
+
+	/* enable the USB controller */
+	mode = fhci->regs->usb_mod;
+	fhci->regs->usb_mod = (mode | USB_MODE_EN);
+	fhci_start_sof_timer(fhci);
+}
+
+static int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	int ret = 0;
+	char data;
+
+	data = (fhci->vroot_hub->hub.wHubChange &
+		HUB_CHANGE_LOCAL_POWER) ? (char)1 : (char)0;
+	ret = data;
+
+	if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
+			USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
+			USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
+		data |= 1 << 1;
+		ret += data;
+	}
+
+	if (ret > 0) {
+		*(char *)buf = data;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int fhci_hub_control(struct usb_hcd *hcd,
+			    u16 typeReq,
+			    u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	int retval = 0;
+	int len = 0;
+	struct usb_hub_status *hub_status;
+	struct usb_port_status *port_status;
+
+	spin_lock(&fhci->lock);
+	switch (typeReq) {
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case ClearPortFeature:
+		fhci->vroot_hub->feature &= (1 << wValue);
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			fhci->vroot_hub->port.wPortStatus &=
+			    ~USB_PORT_STAT_ENABLE;
+			usb_port_disable(fhci);
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			fhci->vroot_hub->port.wPortChange &=
+			    ~USB_PORT_STAT_C_ENABLE;
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			fhci->vroot_hub->port.wPortStatus &=
+			    ~USB_PORT_STAT_SUSPEND;
+			fhci_stop_sof_timer(fhci);
+			break;
+		case USB_PORT_FEAT_C_SUSPEND:
+			fhci->vroot_hub->port.wPortChange &=
+			    ~USB_PORT_STAT_C_SUSPEND;
+			break;
+		case USB_PORT_FEAT_POWER:
+			fhci->vroot_hub->port.wPortStatus &=
+			    ~USB_PORT_STAT_POWER;
+			config_transceiver(fhci, FHCI_OP_POWER_OFF);
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			fhci->vroot_hub->port.wPortChange &=
+			    ~USB_PORT_STAT_C_CONNECTION;
+			break;
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			fhci->vroot_hub->port.wPortChange &=
+			    ~USB_PORT_STAT_C_OVERCURRENT;
+			break;
+		case USB_PORT_FEAT_C_RESET:
+			fhci->vroot_hub->port.wPortChange &=
+			    ~USB_PORT_STAT_C_RESET;
+		default:
+			goto error;
+		}
+		break;
+	case GetHubDescriptor:
+		memcpy(buf, root_hub_des, sizeof(root_hub_des));
+		buf[3] = 0x11; /* per-port power, no ovrcrnt */
+		len = (buf[0] < wLength) ? buf[0] : wLength;
+		break;
+	case GetHubStatus:
+		hub_status = (struct usb_hub_status *)buf;
+		hub_status->wHubStatus =
+		    cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
+		hub_status->wHubChange =
+		    cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
+		len = 4;
+		break;
+	case GetPortStatus:
+		port_status = (struct usb_port_status *)buf;
+		port_status->wPortStatus =
+		    cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
+		port_status->wPortChange =
+		    cpu_to_le16(fhci->vroot_hub->port.wPortChange);
+		len = 4;
+		break;
+	case SetHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+		case C_HUB_LOCAL_POWER:
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case SetPortFeature:
+		fhci->vroot_hub->feature |= (1 << wValue);
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			fhci->vroot_hub->port.wPortStatus |=
+			    USB_PORT_STAT_ENABLE;
+			usb_port_enable(fhci->usb_lld);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			fhci->vroot_hub->port.wPortStatus |=
+			    USB_PORT_STAT_SUSPEND;
+			fhci_stop_sof_timer(fhci);
+			break;
+		case USB_PORT_FEAT_RESET:
+			fhci->vroot_hub->port.wPortStatus |=
+			    USB_PORT_STAT_RESET;
+			usb_port_reset(fhci->usb_lld);
+			fhci->vroot_hub->port.wPortStatus |=
+			    USB_PORT_STAT_ENABLE;
+			fhci->vroot_hub->port.wPortStatus &=
+			    ~USB_PORT_STAT_RESET;
+			break;
+		case USB_PORT_FEAT_POWER:
+			fhci->vroot_hub->port.wPortStatus |=
+			    USB_PORT_STAT_POWER;
+			config_transceiver(fhci, FHCI_OP_POWER_ON);
+			break;
+		default:
+			goto error;
+		}
+		break;
+	default:
+error:
+		retval = -EPIPE;
+	}
+
+	spin_unlock(&fhci->lock);
+	return retval;
+}
diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c
new file mode 100644
index 0000000..b9e51bc
--- /dev/null
+++ b/drivers/usb/host/fhci-mem.c
@@ -0,0 +1,95 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+static void init_td(struct td *td)
+{
+	memset(td, 0, sizeof(*td));
+	INIT_LIST_HEAD(&td->node);
+	INIT_LIST_HEAD(&td->frame_lh);
+}
+
+static void init_ed(struct ed *ed)
+{
+	memset(ed, 0, sizeof(*ed));
+	INIT_LIST_HEAD(&ed->td_list);
+	INIT_LIST_HEAD(&ed->node);
+}
+
+static struct td *get_empty_td(struct fhci_hcd *fhci)
+{
+	struct td *td;
+
+	if (!list_empty(&fhci->empty_tds)) {
+		td = list_entry(fhci->empty_tds.next, struct td, node);
+		list_del(fhci->empty_tds.next);
+	} else {
+		td = kmalloc(sizeof(*td), GFP_ATOMIC);
+		if (!td)
+			fhci_err(fhci, "No memory to allocate to TD\n");
+		else
+			init_td(td);
+	}
+
+	return td;
+}
+
+static void recycle_empty_td(struct fhci_hcd *fhci, struct td *td)
+{
+	init_td(td);
+	list_add(&td->node, &fhci->empty_tds);
+}
+
+static struct ed *get_empty_ed(struct fhci_hcd *fhci)
+{
+	struct ed *ed;
+
+	if (!list_empty(&fhci->empty_eds)) {
+		ed = list_entry(fhci->empty_eds.next, struct ed, node);
+		list_del(fhci->empty_eds.next);
+	} else {
+		ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
+		if (!ed)
+			fhci_err(fhci, "No memory to allocate to ED\n");
+		else
+			init_ed(ed);
+	}
+
+	return ed;
+}
+
+static void recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed)
+{
+	init_ed(ed);
+	list_add(&ed->node, &fhci->empty_eds);
+}
+
+static struct td *td_alloc_fill(struct fhci_hcd *fhci, struct urb *urb,
+		struct urb_priv *urb_priv, struct ed *ed, u16 index,
+		enum fhci_ta_type type, int toggle, u8 *data, u32 len,
+		u16 interval, u16 start_frame, bool ioc)
+{
+	struct td *td = get_empty_td(fhci);
+
+	if (!td)
+		return NULL;
+
+	td_fill(urb, ed, td, index, type, toggle, data, len, interval,
+		start_frame, ioc);
+	urb_priv->tds[index] = td;
+
+	return td;
+}
diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c
new file mode 100644
index 0000000..459c5e2
--- /dev/null
+++ b/drivers/usb/host/fhci-q.c
@@ -0,0 +1,495 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+static void td_fill(struct urb *urb, struct ed *ed, struct td *td,
+		    u16 index, enum fhci_ta_type type, int toggle, u8 *data,
+		    u32 len, u16 interval, u16 start_frame, bool ioc)
+{
+	td->urb = urb;
+	td->ed = ed;
+	td->type = type;
+	td->toggle = toggle;
+	td->data = data;
+	td->len = len;
+	td->iso_index = index;
+	td->interval = interval;
+	td->start_frame = start_frame;
+	td->ioc = ioc;
+	td->status = USB_TD_OK;
+}
+
+static void add_td_to_frame(struct fhci_time_frame *frame, struct td *td)
+{
+	list_add_tail(&td->frame_lh, &frame->tds_list);
+}
+
+static void add_tds_to_ed(struct ed *ed, struct td **td_list, int number)
+{
+	int i;
+
+	for (i = 0; i < number; i++) {
+		struct td *td = td_list[i];
+		list_add_tail(&td->node, &ed->td_list);
+	}
+	if (ed->td_head == NULL)
+		ed->td_head = td_list[0];
+}
+
+static struct td *peek_td_from_ed(struct ed *ed)
+{
+	struct td *td;
+
+	if (!list_empty(&ed->td_list))
+		td = list_entry(ed->td_list.next, struct td, node);
+	else
+		td = NULL;
+
+	return td;
+}
+
+static struct td *remove_td_from_frame(struct fhci_time_frame *frame)
+{
+	struct td *td;
+
+	if (!list_empty(&frame->tds_list)) {
+		td = list_entry(frame->tds_list.next, struct td, frame_lh);
+		list_del_init(frame->tds_list.next);
+	} else
+		td = NULL;
+
+	return td;
+}
+
+static struct td *peek_td_from_frame(struct fhci_time_frame *frame)
+{
+	struct td *td;
+
+	if (!list_empty(&frame->tds_list))
+		td = list_entry(frame->tds_list.next, struct td, frame_lh);
+	else
+		td = NULL;
+
+	return td;
+}
+static struct td *remove_td_from_ed(struct ed *ed)
+{
+	struct td *td;
+
+	if (!list_empty(&ed->td_list)) {
+		td = list_entry(ed->td_list.next, struct td, node);
+		list_del_init(ed->td_list.next);
+
+		/* if this TD was the ED's head, find next TD */
+		if (!list_empty(&ed->td_list))
+			ed->td_head = list_entry(ed->td_list.next, struct td,
+						 node);
+		else
+			ed->td_head = NULL;
+	} else
+		td = NULL;
+
+	return td;
+}
+
+static struct td *remove_td_from_done_list(struct fhci_controller_list *p_list)
+{
+	struct td *td;
+
+	if (!list_empty(&p_list->done_list)) {
+		td = list_entry(p_list->done_list.next, struct td, node);
+		list_del_init(p_list->done_list.next);
+	} else
+		td = NULL;
+
+	return td;
+}
+
+static void move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed)
+{
+	struct td *td;
+
+	td = ed->td_head;
+	list_del_init(&td->node);
+
+	/* If this TD was the ED's head,find next TD */
+	if (!list_empty(&ed->td_list))
+		ed->td_head = list_entry(ed->td_list.next, struct td, node);
+	else {
+		ed->td_head = NULL;
+		ed->state = FHCI_ED_SKIP;
+	}
+	ed->toggle_carry = td->toggle;
+	list_add_tail(&td->node, &usb->hc_list->done_list);
+	if (td->ioc)
+		usb->transfer_confirm(usb->fhci);
+}
+
+/* free done FHCI URB resource such as ED and TD*/
+static void free_urb_priv(struct fhci_hcd *fhci, struct urb *urb)
+{
+	int i;
+	struct urb_priv *urb_priv = urb->hcpriv;
+	struct ed *ed = urb_priv->ed;
+
+	for (i = 0; i < urb_priv->num_of_tds; i++) {
+		list_del_init(&urb_priv->tds[i]->node);
+		recycle_empty_td(fhci, urb_priv->tds[i]);
+	}
+
+	/* if this TD was the ED's head,find the next TD */
+	if (!list_empty(&ed->td_list))
+		ed->td_head = list_entry(ed->td_list.next, struct td, node);
+	else
+		ed->td_head = NULL;
+
+	kfree(urb_priv->tds);
+	kfree(urb_priv);
+	urb->hcpriv = NULL;
+
+	/* if this TD was the ED's head,find next TD */
+	if (ed->td_head == NULL)
+		list_del_init(&ed->node);
+	fhci->active_urbs--;
+}
+
+/* this routine called to complete and free done URB */
+static void urb_complete_free(struct fhci_hcd *fhci, struct urb *urb)
+{
+	free_urb_priv(fhci, urb);
+
+	if (urb->status == -EINPROGRESS) {
+		if (urb->actual_length != urb->transfer_buffer_length &&
+				urb->transfer_flags & URB_SHORT_NOT_OK)
+			urb->status = -EREMOTEIO;
+		else
+			urb->status = 0;
+	}
+	spin_unlock(&fhci->lock);
+	usb_hcd_giveback_urb(fhci_to_hcd(fhci), urb, urb->status);
+	spin_lock(&fhci->lock);
+}
+
+/*
+ * caculate transfer length/stats and update the urb
+ * Precondition: irqsafe(only for urb-?status locking)
+ */
+static void done_td(struct urb *urb, struct td *td)
+{
+	struct ed *ed = td->ed;
+	u32 cc = td->status;
+
+	/* ISO...drivers see per-TD length/status */
+	if (ed->mode == FHCI_TF_ISO) {
+		u32 len;
+		if (!(urb->transfer_flags & URB_SHORT_NOT_OK &&
+				cc == USB_TD_RX_DATA_UNDERUN))
+			cc = USB_TD_OK;
+
+		if (usb_pipeout(urb->pipe))
+			len = urb->iso_frame_desc[td->iso_index].length;
+		else
+			len = td->actual_len;
+
+		urb->actual_length += len;
+		urb->iso_frame_desc[td->iso_index].actual_length = len;
+		urb->iso_frame_desc[td->iso_index].status =
+			status_to_error(cc);
+	}
+
+	/* BULK,INT,CONTROL... drivers see aggregate length/status,
+	 * except that "setup" bytes aren't counted and "short" transfers
+	 * might not be reported as errors.
+	 */
+	else {
+		if (td->error_cnt >= 3)
+			urb->error_count = 3;
+
+		/* control endpoint only have soft stalls */
+
+		/* update packet status if needed(short may be ok) */
+		if (!(urb->transfer_flags & URB_SHORT_NOT_OK) &&
+				cc == USB_TD_RX_DATA_UNDERUN) {
+			ed->state = FHCI_ED_OPER;
+			cc = USB_TD_OK;
+		}
+		if (cc != USB_TD_OK) {
+			if (urb->status == -EINPROGRESS)
+				urb->status = status_to_error(cc);
+		}
+
+		/* count all non-empty packets except control SETUP packet */
+		if (td->type != FHCI_TA_SETUP || td->iso_index != 0)
+			urb->actual_length += td->actual_len;
+	}
+}
+
+/* there are some pedning request to unlink */
+static void del_ed_list(struct fhci_hcd *fhci, struct ed *ed)
+{
+	struct td *td = peek_td_from_ed(ed);
+	struct urb *urb = td->urb;
+	struct urb_priv *urb_priv = urb->hcpriv;
+
+	if (urb_priv->state == URB_DEL) {
+		td = remove_td_from_ed(ed);
+		/* HC may have partly processed this TD */
+		if (td->status != USB_TD_INPROGRESS)
+			done_td(urb, td);
+
+		/* URB is done;clean up */
+		if (++(urb_priv->tds_cnt) == urb_priv->num_of_tds)
+			urb_complete_free(fhci, urb);
+	}
+}
+
+/*
+ * Process normal completions(error or sucess) and clean the schedule.
+ *
+ * This is the main path for handing urbs back to drivers. The only other patth
+ * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning
+ * the (re-reversed) done list as this does.
+ */
+static void process_done_list(unsigned long data)
+{
+	struct urb *urb;
+	struct ed *ed;
+	struct td *td;
+	struct urb_priv *urb_priv;
+	struct fhci_hcd *fhci = (struct fhci_hcd *)data;
+
+	spin_lock(&fhci->lock);
+	td = remove_td_from_done_list(fhci->hc_list);
+	while (td != NULL) {
+		urb = td->urb;
+		urb_priv = urb->hcpriv;
+		ed = td->ed;
+
+		/* update URB's length and status from TD */
+		done_td(urb, td);
+		urb_priv->tds_cnt++;
+
+		/*
+		 * if all this urb's TDs are done, call complete()
+		 * Interrupt transfers are the onley special case:
+		 * they are reissued,until "deleted" by usb_unlink_urb
+		 * (real work done in a SOF intr, by process_del_list)
+		 */
+		if (urb_priv->tds_cnt == urb_priv->num_of_tds) {
+			urb_complete_free(fhci, urb);
+		} else if (urb_priv->state == URB_DEL &&
+				ed->state == FHCI_ED_SKIP) {
+			del_ed_list(fhci, ed);
+			ed->state = FHCI_ED_OPER;
+		} else if (ed->state == FHCI_ED_HALTED) {
+			urb_priv->state = URB_DEL;
+			ed->state = FHCI_ED_URB_DEL;
+			del_ed_list(fhci, ed);
+			ed->state = FHCI_ED_OPER;
+		}
+
+		td = remove_td_from_done_list(fhci->hc_list);
+	}
+	spin_unlock(&fhci->lock);
+}
+
+/*
+ * adds urb to the endpoint descriptor list
+ * arguments:
+ * fhci		data structure for the Low level host controller
+ * ep		USB Host endpoint data structure
+ * urb		USB request block data structure
+ */
+static void queue_urb(struct fhci_hcd *fhci, struct urb *urb)
+{
+	struct ed *ed = urb->ep->hcpriv;
+	struct urb_priv *urb_priv = urb->hcpriv;
+	u32 data_len = urb->transfer_buffer_length;
+	int urb_state = 0;
+	int toggle = 0;
+	struct td *td;
+	u8 *data;
+	u16 cnt = 0;
+
+	if (ed == NULL) {
+		ed = get_empty_ed(fhci);
+		ed->dev_addr = (u8) usb_pipedevice(urb->pipe);
+		ed->ep_addr = (u8) usb_pipeendpoint(urb->pipe);
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_CONTROL:
+			ed->mode = FHCI_TF_CTRL;
+			break;
+		case PIPE_BULK:
+			ed->mode = FHCI_TF_BULK;
+			break;
+		case PIPE_INTERRUPT:
+			ed->mode = FHCI_TF_INTR;
+			break;
+		case PIPE_ISOCHRONOUS:
+			ed->mode = FHCI_TF_ISO;
+			break;
+		default:
+			break;
+		}
+		ed->speed = (urb->dev->speed == USB_SPEED_LOW) ?
+			FHCI_LOW_SPEED : FHCI_FULL_SPEED;
+		ed->max_pkt_size = usb_maxpacket(urb->dev,
+			urb->pipe, usb_pipeout(urb->pipe));
+		urb->ep->hcpriv = ed;
+	}
+
+	/* for ISO transfer calculate start frame index */
+	if (ed->mode == FHCI_TF_ISO && urb->transfer_flags & URB_ISO_ASAP)
+		urb->start_frame = ed->td_head ? ed->last_iso + 1 :
+						 get_frame_num(fhci);
+
+	/*
+	 * OHCI handles the DATA toggle itself,we just use the USB
+	 * toggle bits
+	 */
+	if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			  usb_pipeout(urb->pipe)))
+		toggle = USB_TD_TOGGLE_CARRY;
+	else {
+		toggle = USB_TD_TOGGLE_DATA0;
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), 1);
+	}
+
+	urb_priv->tds_cnt = 0;
+	urb_priv->ed = ed;
+	if (data_len > 0)
+		data = urb->transfer_buffer;
+	else
+		data = 0;
+
+	switch (ed->mode) {
+	case FHCI_TF_BULK:
+		if (urb->transfer_flags & URB_ZERO_PACKET &&
+				urb->transfer_buffer_length > 0 &&
+				((urb->transfer_buffer_length %
+				usb_maxpacket(urb->dev, urb->pipe,
+				usb_pipeout(urb->pipe))) == 0))
+			urb_state = US_BULK0;
+		while (data_len > 4096) {
+			td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt,
+				usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+							 FHCI_TA_IN,
+				cnt ? USB_TD_TOGGLE_CARRY :
+				      toggle,
+				data, 4096, 0, 0, true);
+			data += 4096;
+			data_len -= 4096;
+			cnt++;
+		}
+
+		td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt,
+			usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
+			cnt ? USB_TD_TOGGLE_CARRY : toggle,
+			data, data_len, 0, 0, true);
+		cnt++;
+
+		if (urb->transfer_flags & URB_ZERO_PACKET &&
+				cnt < urb_priv->num_of_tds) {
+			td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt,
+				usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+							 FHCI_TA_IN,
+				USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true);
+			cnt++;
+		}
+		break;
+	case FHCI_TF_INTR:
+		urb->start_frame = get_frame_num(fhci) + 1;
+		td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt++,
+			usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
+			USB_TD_TOGGLE_DATA0, data, data_len,
+			urb->interval, urb->start_frame, true);
+		break;
+	case FHCI_TF_CTRL:
+		ed->dev_addr = usb_pipedevice(urb->pipe);
+		ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe,
+			usb_pipeout(urb->pipe));
+		td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt++,
+			FHCI_TA_SETUP, USB_TD_TOGGLE_DATA0, urb->setup_packet,
+			8, 0, 0, true);
+
+		if (data_len > 0) {
+			td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt++,
+				usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+							 FHCI_TA_IN,
+				USB_TD_TOGGLE_DATA1, data, data_len, 0, 0,
+				true);
+		}
+		td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt++,
+			usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT,
+			USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+		urb_state = US_CTRL_SETUP;
+		break;
+	case FHCI_TF_ISO:
+		for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+			u16 frame = (u16) urb->start_frame;
+
+			/*
+			 * FIXME scheduling should handle frame counter
+			 * roll-around ... exotic case (and OHCI has
+			 * a 2^16 iso range, vs other HCs max of 2^10)
+			 */
+			frame += cnt * urb->interval;
+			frame &= 0x07ff;
+			td = td_alloc_fill(fhci, urb, urb_priv, ed, cnt,
+				usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+							 FHCI_TA_IN,
+				USB_TD_TOGGLE_DATA0,
+				data + urb->iso_frame_desc[cnt].offset,
+				urb->iso_frame_desc[cnt].length,
+				urb->interval, frame, true);
+		}
+	default:
+		break;
+	}
+
+	/*
+	 * set the state of URB
+	 * control pipe:3 states -- setup,data,status
+	 * interrupt and bulk pipe:1 state -- data
+	 */
+	urb->pipe &= ~0x1f;
+	urb->pipe |= urb_state & 0x1f;
+
+	urb_priv->state = URB_INPROGRESS;
+
+	if (!ed->td_head) {
+		ed->state = FHCI_ED_OPER;
+		switch (ed->mode) {
+		case FHCI_TF_CTRL:
+			list_add(&ed->node, &fhci->hc_list->ctrl_list);
+			break;
+		case FHCI_TF_BULK:
+			list_add(&ed->node, &fhci->hc_list->bulk_list);
+			break;
+		case FHCI_TF_INTR:
+			list_add(&ed->node, &fhci->hc_list->intr_list);
+			break;
+		case FHCI_TF_ISO:
+			list_add(&ed->node, &fhci->hc_list->iso_list);
+		default:
+			break;
+		}
+	}
+
+	add_tds_to_ed(ed, urb_priv->tds, urb_priv->num_of_tds);
+	fhci->active_urbs++;
+}
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
new file mode 100644
index 0000000..88922f8
--- /dev/null
+++ b/drivers/usb/host/fhci-tds.c
@@ -0,0 +1,628 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+#define DUMMY_BD_BUFFER  0xdeadbeef
+#define DUMMY2_BD_BUFFER 0xbaadf00d
+
+/* Transaction Descriptors bits */
+#define TD_R		0x8000 /* ready bit */
+#define TD_W		0x2000 /* wrap bit */
+#define TD_I		0x1000 /* interrupt on completion */
+#define TD_L		0x0800 /* last */
+#define TD_TC		0x0400 /* transmit CRC */
+#define TD_CNF		0x0200 /* CNF - Must be always 1 */
+#define TD_LSP		0x0100 /* Low-speed transaction */
+#define TD_PID		0x00c0 /* packet id */
+#define TD_RXER		0x0020 /* Rx error or not */
+
+#define TD_NAK		0x0010 /* No ack. */
+#define TD_STAL		0x0008 /* Stall recieved */
+#define TD_TO		0x0004 /* time out */
+#define TD_UN		0x0002 /* underrun */
+#define TD_NO		0x0010 /* Rx Non Octet Aligned Packet */
+#define TD_AB		0x0008 /* Frame Aborted */
+#define TD_CR		0x0004 /* CRC Error */
+#define TD_OV		0x0002 /* Overrun */
+#define TD_BOV		0x0001 /* Buffer Overrun */
+
+#define TD_ERRORS	(TD_NAK | TD_STAL | TD_TO | TD_UN | \
+			 TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV)
+
+#define TD_PID_DATA0	0x0080 /* Data 0 toggle */
+#define TD_PID_DATA1	0x00c0 /* Data 1 toggle */
+#define TD_PID_TOGGLE	0x00c0 /* Data 0/1 toggle mask */
+
+#define TD_TOK_SETUP	0x0000
+#define TD_TOK_OUT	0x4000
+#define TD_TOK_IN	0x8000
+#define TD_ISO		0x1000
+#define TD_ENDP		0x0780
+#define TD_ADDR		0x007f
+
+#define TD_ENDP_SHIFT 7
+
+struct usb_td {
+	u16 status;
+	u16 length;
+	u32 buf_ptr;
+	u16 extra;
+	u16 reserved;
+};
+
+struct endpoint {
+	/* Pointer to ep parameter RAM */
+	struct fhci_ep_pram *ep_pram_ptr;
+
+	/* Host transactions */
+	struct usb_td *td_base;	/* first TD in the ring */
+	struct usb_td *conf_td;	/* next TD for confirm after transac */
+	struct usb_td *empty_td;/* next TD for new transaction request */
+	void *empty_frame_Q;	/* Empty frames list to use */
+	void *conf_frame_Q;	/* frames passed to TDs,waiting for tx */
+	void *dummy_packets_Q;	/* dummy packets for the CRC overun */
+
+	bool already_pushed_dummy_bd;
+};
+
+struct usb_td *next_bd(struct usb_td *base, struct usb_td *td, u16 status)
+{
+	if (status & TD_W)
+		return base;
+	else
+		return ++td;
+}
+
+static void push_dummy_bd(struct endpoint *ep)
+{
+	if (ep->already_pushed_dummy_bd == false) {
+		u16 td_status = ep->empty_td->status;
+
+		ep->empty_td->buf_ptr = DUMMY_BD_BUFFER;
+		/* get the next TD in the ring */
+		ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
+		ep->already_pushed_dummy_bd = true;
+	}
+}
+
+/* destroy an USB endpoint */
+void endpoint_zero_free(struct fhci_usb *usb)
+{
+	struct endpoint *ep;
+	int size;
+
+	ep = usb->ep0;
+	if (ep) {
+		if (ep->td_base)
+			qe_muram_free(qe_muram_offset(ep->td_base));
+
+		if (ep->conf_frame_Q) {
+			size = cq_howmany(ep->conf_frame_Q);
+			for (; size; size--) {
+				struct packet *pkt = cq_get(ep->conf_frame_Q);
+
+				kfree(pkt);
+			}
+			cq_delete(ep->conf_frame_Q);
+		}
+
+		if (ep->empty_frame_Q) {
+			size = cq_howmany(ep->empty_frame_Q);
+			for (; size; size--) {
+				struct packet *pkt = cq_get(ep->empty_frame_Q);
+
+				kfree(pkt);
+			}
+			cq_delete(ep->empty_frame_Q);
+		}
+
+		if (ep->dummy_packets_Q) {
+			size = cq_howmany(ep->dummy_packets_Q);
+			for (; size; size--) {
+				u8 *buff = cq_get(ep->dummy_packets_Q);
+
+				kfree(buff);
+			}
+			cq_delete(ep->dummy_packets_Q);
+		}
+
+		kfree(ep);
+		usb->ep0 = NULL;
+	}
+}
+
+/*
+ * create the endpoint structure
+ *
+ * arguments:
+ * usb		A pointer to the data structure of the USB
+ * data_mem	The data memory partition(BUS)
+ * ring_len	TD ring length
+ */
+u32 create_endpoint(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
+		    u32 ring_len)
+{
+	struct endpoint *ep;
+	struct usb_td *td;
+	unsigned long ep_offset;
+	char *err_for = "enpoint PRAM";
+	int ep_mem_size;
+	u32 i;
+
+	/* we need at least 3 TDs in the ring */
+	if (!(ring_len > 2)) {
+		fhci_err(usb->fhci, "illegal TD ring length parameters\n");
+		return -EINVAL;
+	}
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		goto err;
+
+	ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram);
+	ep_offset = qe_muram_alloc(ep_mem_size, 32);
+	if (IS_ERR_VALUE(ep_offset))
+		goto err;
+	ep->td_base = qe_muram_addr(ep_offset);
+
+	/* zero all queue pointers */
+	ep->conf_frame_Q = cq_new((u8) ring_len + 2);
+	ep->empty_frame_Q = cq_new((u8) ring_len + 2);
+	ep->dummy_packets_Q = cq_new((u8) ring_len + 2);
+	if (!ep->conf_frame_Q || !ep->empty_frame_Q || !ep->dummy_packets_Q) {
+		err_for = "frame_queues";
+		goto err;
+	}
+
+	for (i = 0; i < (ring_len + 1); i++) {
+		struct packet *pkt;
+		u8 *buff;
+
+		pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+		if (!pkt) {
+			err_for = "frame";
+			goto err;
+		}
+
+		buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL);
+		if (!buff) {
+			kfree(pkt);
+			err_for = "buffer";
+			goto err;
+		}
+		cq_put(ep->empty_frame_Q, pkt);
+		cq_put(ep->dummy_packets_Q, buff);
+	}
+
+	/* we put the endpoint parameter RAM right behind the TD ring */
+	ep->ep_pram_ptr = (void *)ep->td_base + sizeof(*td) * ring_len;
+
+	ep->conf_td = ep->td_base;
+	ep->empty_td = ep->td_base;
+
+	ep->already_pushed_dummy_bd = false;
+
+	/* initialize tds */
+	td = ep->td_base;
+	for (i = 0; i < ring_len; i++) {
+		td->buf_ptr = 0;
+		td->status = 0;
+		td->length = 0;
+		td->extra = 0;
+		td++;
+	}
+	td--;
+	td->status = TD_W; /* for last TD set Wrap bit */
+	td->length = 0;
+
+	/* endpoint structure has been created */
+	usb->ep0 = ep;
+
+	return 0;
+err:
+	kfree(ep);
+	endpoint_zero_free(usb);
+	fhci_err(usb->fhci, "no memory for the %s\n", err_for);
+	return -ENOMEM;
+}
+
+/*
+ * initialize the endpoint register according to the given parameters
+ *
+ * artuments:
+ * usb		A pointer to the data strucutre of the USB
+ * ep		A pointer to the endpoint structre
+ * data_mem	The data memory partition(BUS)
+ */
+void init_endpoint_registers(struct fhci_usb *usb,
+			     struct endpoint *ep, enum fhci_mem_alloc data_mem)
+{
+	u8 rt;
+
+	/* set the endpoint registers according to the endpoint */
+	usb->fhci->regs->usb_ep[0] = (USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
+	usb->fhci->pram->ep_ptr[0] = qe_muram_offset(ep->ep_pram_ptr);
+
+	rt = (BUS_MODE_BO_BE | BUS_MODE_GBL);
+#ifdef MULTI_DATA_BUS
+	if (data_mem == MEM_SECONDARY)
+		rt |= BUS_MODE_DTB;
+#endif
+	ep->ep_pram_ptr->rx_func_code = rt;
+	ep->ep_pram_ptr->tx_func_code = rt;
+	ep->ep_pram_ptr->rx_buff_len = 1028;
+	ep->ep_pram_ptr->rx_base = 0;
+	ep->ep_pram_ptr->tx_base = qe_muram_offset(ep->td_base);
+	ep->ep_pram_ptr->rx_bd_ptr = 0;
+	ep->ep_pram_ptr->tx_bd_ptr = qe_muram_offset(ep->td_base);
+	ep->ep_pram_ptr->tx_state = 0;
+	/*
+	 * FIXME: what the is this? I presume it is needed for u-code
+	 * 	  patched FHCIs, but this needs checking.
+	 */
+	ep->ep_pram_ptr->reserved = (u16)((u32)qe_muram_addr(0) >> 16);
+}
+
+/* Collect the submitted frames and inform the application about them
+ * It is also prepearing the TDs for new frames. If the Tx interrupts
+ * are diabled, the application should call that routine to get
+ * confirmation about the submitted frames. Otherwise, the routine is
+ * called frome the interrupt service routine during the Tx interrupt.
+ * In that case the application is informed by calling the application
+ * specific 'transaction_confirm' routine*/
+static void td_transaction_confirm(struct fhci_usb *usb)
+{
+	struct endpoint *ep = usb->ep0;
+	struct packet *pkt;
+	struct usb_td *td;
+	u16 extra_data;
+	u16 td_status;
+	u16 td_length;
+	u32 buf;
+
+	/*
+	 * collect transmitted BDs from the chip. The routine clears all BDs
+	 * with R bit = 0 and the pointer to data buffer is not NULL, that is
+	 * BDs which point to the transmitted data buffer
+	 */
+	do {
+		td = ep->conf_td;
+		td_status = td->status;
+		td_length = td->length;
+		buf = td->buf_ptr;
+		extra_data = td->extra;
+
+		/* check if the TD is empty */
+		if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf)))
+			break;
+		/* check if it is a dummy buffer */
+		else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W))
+			break;
+
+		/* mark TD as empty */
+		td->status &= TD_W;
+		td->length = 0;
+		td->buf_ptr = 0;
+		td->extra = 0;
+		/* advance the TD pointer */
+		ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status);
+
+		/* check if it is a dummy buffer(type2) */
+		if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W))
+			continue;
+
+		pkt = cq_get(ep->conf_frame_Q);
+		if (!pkt)
+			fhci_err(usb->fhci, "no frame to confirm\n");
+
+		if (td_status & TD_ERRORS) {
+			if (td_status & TD_RXER) {
+				if (td_status & TD_CR)
+					pkt->status = USB_TD_RX_ER_CRC;
+				else if (td_status & TD_AB)
+					pkt->status = USB_TD_RX_ER_BITSTUFF;
+				else if (td_status & TD_OV)
+					pkt->status = USB_TD_RX_ER_OVERUN;
+				else if (td_status & TD_BOV)
+					pkt->status = USB_TD_RX_DATA_OVERUN;
+				else if (td_status & TD_NO)
+					pkt->status = USB_TD_RX_ER_NONOCT;
+				else
+					fhci_err(usb->fhci, "illegal error "
+						 "occured\n");
+			} else if (td_status & TD_NAK)
+				pkt->status = USB_TD_TX_ER_NAK;
+			else if (td_status & TD_TO)
+				pkt->status = USB_TD_TX_ER_TIMEOUT;
+			else if (td_status & TD_UN)
+				pkt->status = USB_TD_TX_ER_UNDERUN;
+			else if (td_status & TD_STAL)
+				pkt->status = USB_TD_TX_ER_STALL;
+			else
+				fhci_err(usb->fhci, "illegal error occured\n");
+		} else if ((extra_data & TD_TOK_IN) &&
+				pkt->len > td_length - CRC_SIZE) {
+			pkt->status = USB_TD_RX_DATA_UNDERUN;
+		}
+
+		if (extra_data & TD_TOK_IN)
+			pkt->len = td_length - CRC_SIZE;
+		else if (pkt->info & PKT_ZLP)
+			pkt->len = 0;
+		else
+			pkt->len = td_length;
+
+		transaction_confirm(usb, pkt);
+	} while (1);
+}
+
+/*
+ * Submitting a data frame to a specified endpoint of a USB device
+ * The frame is put in the driver's transmit queue for this endpoint
+ *
+ * Arguments:
+ * usb          A pointer to the USB structure
+ * pkt          A pointer to the user frame structure
+ * trans_type   Transaction tyep - IN,OUT or SETUP
+ * dest_addr    Device address - 0~127
+ * dest_ep      Endpoint number of the device - 0~16
+ * trans_mode   Pipe type - ISO,Interrupt,bulk or control
+ * dest_speed   USB speed - Low speed or FULL speed
+ * data_toggle  Data sequence toggle - 0 or 1
+ */
+u32 host_transaction(struct fhci_usb *usb,
+		     struct packet *pkt,
+		     enum fhci_ta_type trans_type,
+		     u8 dest_addr,
+		     u8 dest_ep,
+		     enum fhci_tf_mode trans_mode,
+		     enum fhci_speed dest_speed, u8 data_toggle)
+{
+	struct endpoint *ep = usb->ep0;
+	struct usb_td *td;
+	u16 extra_data;
+	u16 td_status;
+
+	fhci_usb_disable_interrupt(usb);
+	/* start from the next BD that should be filled */
+	td = ep->empty_td;
+	td_status = td->status;
+
+	if (td_status & TD_R && td->length) {
+		/* if the TD is not free */
+		fhci_usb_enable_interrupt(usb);
+		return -1;
+	}
+
+	/* get the next TD in the ring */
+	ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
+	fhci_usb_enable_interrupt(usb);
+	pkt->priv_data = td;
+	td->buf_ptr = virt_to_phys(pkt->data);
+	/* sets up transaction parameters - addr,endp,dir,and type */
+	extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr;
+	switch (trans_type) {
+	case FHCI_TA_IN:
+		extra_data |= TD_TOK_IN;
+		break;
+	case FHCI_TA_OUT:
+		extra_data |= TD_TOK_OUT;
+		break;
+	case FHCI_TA_SETUP:
+		extra_data |= TD_TOK_SETUP;
+		break;
+	}
+	if (trans_mode == FHCI_TF_ISO)
+		extra_data |= TD_ISO;
+	td->extra = extra_data;
+
+	/* sets up the buffer descriptor */
+	td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF);
+	if (!(pkt->info & PKT_NO_CRC))
+		td_status |= TD_TC;
+
+	switch (trans_type) {
+	case FHCI_TA_IN:
+		if (data_toggle) {
+			pkt->info |= PKT_PID_DATA1;
+		} else {
+			pkt->info |= PKT_PID_DATA0;
+		}
+		break;
+	default:
+		if (data_toggle) {
+			td_status |= TD_PID_DATA1;
+			pkt->info |= PKT_PID_DATA1;
+		} else {
+			td_status |= TD_PID_DATA0;
+			pkt->info |= PKT_PID_DATA0;
+		}
+		break;
+	}
+
+	if ((dest_speed == FHCI_LOW_SPEED) &&
+	    (usb->port_status == FHCI_PORT_FULL))
+		td_status |= TD_LSP;
+
+	td->status = td_status;
+
+	/* set up buffer length */
+	if (trans_type == FHCI_TA_IN)
+		td->length = pkt->len + CRC_SIZE;
+	else
+		td->length = pkt->len;
+
+	/* put the frame to the confirmation queue */
+	cq_put(ep->conf_frame_Q, pkt);
+
+	if (cq_howmany(ep->conf_frame_Q) == 1)
+		usb->fhci->regs->usb_comm = USB_CMD_STR_FIFO;
+
+	return 0;
+}
+
+/* Reset the Tx BD ring*/
+void flush_bds(struct fhci_usb *usb)
+{
+	u16 extra_data;
+	u16 td_status;
+	u32 buf;
+	struct usb_td *td;
+	struct endpoint *ep = usb->ep0;
+
+	td = ep->td_base;
+	do {
+		td_status = td->status;
+		buf = td->buf_ptr;
+		extra_data = td->extra;
+
+		/* if the TD is not empty - we'll confirm it as Timeout */
+		if (td_status & TD_R)
+			td->status = (td_status & ~TD_R) | TD_TO;
+		/* if this TD is dummy - let's skip this TD */
+		else if (td->buf_ptr == DUMMY_BD_BUFFER)
+			td->buf_ptr = DUMMY2_BD_BUFFER;
+		/* if this is the last TD - break */
+		if (td_status & TD_W)
+			break;
+
+		td++;
+	} while (1);
+
+	td_transaction_confirm(usb);
+
+	td = ep->td_base;
+	do {
+		td->status = 0;
+		td->length = 0;
+		td->buf_ptr = 0;
+		td->extra = 0;
+		td++;
+	} while (!(td->status & TD_W));
+	td->status = TD_W; /* for last TD set Wrap bit */
+	td->length = 0;
+	td->buf_ptr = 0;
+	td->extra = 0;
+
+	ep->ep_pram_ptr->tx_bd_ptr = ep->ep_pram_ptr->tx_base;
+	ep->ep_pram_ptr->tx_state = 0;
+	ep->ep_pram_ptr->tx_cnt = 0;
+	ep->conf_td = ep->empty_td = ep->td_base;
+}
+
+/*
+ * Flush all transmitted packets from TDs in the actual frame.
+ * This routine is called when something wrong with the controller and
+ * we want to get rid of the actual frame and start again next frame
+ */
+void flush_actual_frame(struct fhci_usb *usb)
+{
+	u8 mode;
+	u16 tb_ptr;
+	u16 extra_data;
+	u16 td_status;
+	u32 buf_ptr;
+	struct usb_td *td;
+	struct endpoint *ep = usb->ep0;
+
+	/* disable the USB controller */
+	mode = usb->fhci->regs->usb_mod;
+	usb->fhci->regs->usb_mod = (u8) (mode & ~USB_MODE_EN);
+
+	tb_ptr = ep->ep_pram_ptr->tx_bd_ptr;
+	td = qe_muram_addr(tb_ptr);
+	td_status = td->status;
+	buf_ptr = td->buf_ptr;
+	extra_data = td->extra;
+	do {
+		if (td_status & TD_R) {
+			td->status = (td_status & ~TD_R) | TD_TO;
+		} else {
+			td->buf_ptr = 0;
+			ep->already_pushed_dummy_bd = false;
+			break;
+		}
+
+		/* advance the TD pointer */
+		td = next_bd(ep->td_base, td, td_status);
+		td_status = td->status;
+		buf_ptr = td->buf_ptr;
+		extra_data = td->extra;
+	} while ((td_status & TD_R) || buf_ptr);
+
+	td_transaction_confirm(usb);
+
+	ep->ep_pram_ptr->tx_bd_ptr = ep->ep_pram_ptr->tx_base;
+	ep->ep_pram_ptr->tx_state = 0;
+	ep->ep_pram_ptr->tx_cnt = 0;
+	ep->conf_td = ep->empty_td = ep->td_base;
+
+	usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+
+	/* reset the event register */
+	usb->fhci->regs->usb_event = 0xffff;
+	/* enable the USB controller */
+	usb->fhci->regs->usb_mod = (u8) (mode | USB_MODE_EN);
+}
+
+/* handles Tx confirm and Tx error interrupt */
+void tx_conf_interrupt(struct fhci_usb *usb)
+{
+	td_transaction_confirm(usb);
+
+	/*
+	 * Schedule another transaction to this frame only if we have
+	 * already confirmed all transaction in the frame.
+	 */
+	if (((get_sof_timer_count(usb) < usb->max_frame_usage) ||
+	     (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) &&
+	    (list_empty(&usb->actual_frame->tds_list)))
+		schedule_transactions(usb);
+}
+
+void host_transmit_actual_frame(struct fhci_usb *usb)
+{
+	u16 tb_ptr;
+	u16 td_status;
+	struct usb_td *td;
+	struct endpoint *ep = usb->ep0;
+
+	tb_ptr = ep->ep_pram_ptr->tx_bd_ptr;
+	td = qe_muram_addr(tb_ptr);
+
+	if (td->buf_ptr == DUMMY_BD_BUFFER) {
+		struct usb_td *old_td = td;
+
+		ep->already_pushed_dummy_bd = false;
+		td_status = td->status;
+		/* gets the next TD in the ring */
+		td = next_bd(ep->td_base, td, td_status);
+		tb_ptr = qe_muram_offset(td);
+		ep->ep_pram_ptr->tx_bd_ptr = tb_ptr;
+
+		/* start transmit only if we have something in the TDs */
+		if (td->status & TD_R)
+			usb->fhci->regs->usb_comm = USB_CMD_STR_FIFO;
+
+		if (ep->conf_td->buf_ptr == DUMMY_BD_BUFFER) {
+			old_td->buf_ptr = 0;
+			ep->conf_td = next_bd(ep->td_base, ep->conf_td,
+					      td_status);
+		} else {
+			old_td->buf_ptr = DUMMY2_BD_BUFFER;
+		}
+	}
+}
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
new file mode 100644
index 0000000..9eb454a
--- /dev/null
+++ b/drivers/usb/host/fhci.h
@@ -0,0 +1,543 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish at freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang at freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb at logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov at ru.mvista.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.
+ */
+
+#ifndef __FHCI_H
+#define __FHCI_H
+
+#if defined(CONFIG_FHCI_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+#define USB_CLOCK	48000000
+
+#define FHCI_PRAM_SIZE 0x100
+
+#define MAX_EDS		32
+#define MAX_TDS		32
+
+
+/* CRC16 field size */
+#define CRC_SIZE 2
+
+/* USB protocol overhead for each frame transmitted from the host */
+#define PROTOCOL_OVERHEAD 7
+
+/* Packet structure, info field */
+#define PKT_PID_DATA0		0x80000000 /* PID - Data toggle zero */
+#define PKT_PID_DATA1		0x40000000 /* PID - Data toggle one  */
+#define PKT_PID_SETUP		0x20000000 /* PID - Setup bit */
+#define PKT_SETUP_STATUS	0x10000000 /* Setup status bit */
+#define PKT_SETADDR_STATUS	0x08000000 /* Set address status bit */
+#define PKT_SET_HOST_LAST	0x04000000 /* Last data packet */
+#define PKT_HOST_DATA		0x02000000 /* Data packet */
+#define PKT_FIRST_IN_FRAME	0x01000000 /* First packet in the frame */
+#define PKT_TOKEN_FRAME		0x00800000 /* Token packet */
+#define PKT_ZLP			0x00400000 /* Zero length packet */
+#define PKT_IN_TOKEN_FRAME	0x00200000 /* IN token packet */
+#define PKT_OUT_TOKEN_FRAME	0x00100000 /* OUT token packet */
+#define PKT_SETUP_TOKEN_FRAME	0x00080000 /* SETUP token packet */
+#define PKT_STALL_FRAME		0x00040000 /* STALL packet */
+#define PKT_NACK_FRAME		0x00020000 /* NACK packet */
+#define PKT_NO_PID		0x00010000 /* No PID */
+#define PKT_NO_CRC		0x00008000 /* don't append CRC */
+#define PKT_HOST_COMMAND	0x00004000 /* Host command packet */
+#define PKT_DUMMY_PACKET	0x00002000 /* Dummy packet, used for mmm */
+#define PKT_LOW_SPEED_PACKET	0x00001000 /* Low-Speed packet */
+
+#define TRANS_OK		0
+#define TRANS_INPROGRESS	-1
+#define TRANS_DISCARD		-2
+#define TRANS_FAIL		-3
+
+#define PS_INT		0
+#define PS_DISCONNECTED	1
+#define PS_CONNECTED	2
+#define PS_READY	3
+#define PS_MISSING	4
+
+/* Transfer Descriptor status field */
+#define USB_TD_OK		0x00000000 /* TD transmited or received ok */
+#define USB_TD_INPROGRESS	0x80000000 /* TD is being transmitted */
+#define USB_TD_RX_ER_NONOCT	0x40000000 /* Tx Non Octet Aligned Packet */
+#define USB_TD_RX_ER_BITSTUFF	0x20000000 /* Frame Aborted-Received pkt */
+#define USB_TD_RX_ER_CRC	0x10000000 /* CRC error */
+#define USB_TD_RX_ER_OVERUN	0x08000000 /* Over - run occured */
+#define USB_TD_RX_ER_PID	0x04000000 /* wrong PID received */
+#define USB_TD_RX_DATA_UNDERUN	0x02000000 /* shorter than expected */
+#define USB_TD_RX_DATA_OVERUN	0x01000000 /* longer than expected */
+#define USB_TD_TX_ER_NAK	0x00800000 /* NAK handshake */
+#define USB_TD_TX_ER_STALL	0x00400000 /* STALL handshake */
+#define USB_TD_TX_ER_TIMEOUT	0x00200000 /* transmit time out */
+#define USB_TD_TX_ER_UNDERUN	0x00100000 /* transmit underrun */
+
+#define USB_TD_ERROR (USB_TD_RX_ER_NONOCT | USB_TD_RX_ER_BITSTUFF | \
+		USB_TD_RX_ER_CRC | USB_TD_RX_ER_OVERUN | USB_TD_RX_ER_PID | \
+		USB_TD_RX_DATA_UNDERUN | USB_TD_RX_DATA_OVERUN | \
+		USB_TD_TX_ER_NAK | USB_TD_TX_ER_STALL | \
+		USB_TD_TX_ER_TIMEOUT | USB_TD_TX_ER_UNDERUN)
+
+/* Transfer Descriptor toggle field */
+#define USB_TD_TOGGLE_DATA0	0
+#define USB_TD_TOGGLE_DATA1	1
+#define USB_TD_TOGGLE_CARRY	2
+
+/* #define MULTI_DATA_BUS */
+
+/* Bus mode register RBMR/TBMR */
+#define BUS_MODE_GBL	0x20	/*Global snooping */
+#define BUS_MODE_BO	0x18	/*Byte ordering */
+#define BUS_MODE_BO_BE	0x10	/*Byte ordering - Big-endian */
+#define BUS_MODE_DTB	0x02	/*Data bus */
+
+/* FHCI QE USB Register Description */
+
+/* USB Mode Register bit define */
+#define USB_MODE_EN		0x01
+#define USB_MODE_HOST		0x02
+#define USB_MODE_TEST		0x04
+#define USB_MODE_SFTE		0x08
+#define USB_MODE_RESUME		0x40
+#define USB_MODE_LSS		0x80
+
+/* USB Slave Address Register Mask */
+#define USB_SLVADDR_MASK	0x7F
+
+/* USB Endpoint register define */
+#define USB_EPNUM_MASK		0xF000
+#define USB_EPNUM_SHIFT		12
+
+#define USB_TRANS_MODE_SHIFT	8
+#define USB_TRANS_CTR		0x0000
+#define USB_TRANS_INT		0x0100
+#define USB_TRANS_BULK		0x0200
+#define USB_TRANS_ISO		0x0300
+
+#define USB_EP_MF		0x0020
+#define USB_EP_RTE		0x0010
+
+#define USB_THS_SHIFT		2
+#define USB_THS_MASK		0x000c
+#define USB_THS_NORMAL		0x0
+#define USB_THS_IGNORE_IN	0x0004
+#define USB_THS_NACK		0x0008
+#define USB_THS_STALL		0x000c
+
+#define USB_RHS_SHIFT   	0
+#define USB_RHS_MASK		0x0003
+#define USB_RHS_NORMAL  	0x0
+#define USB_RHS_IGNORE_OUT	0x0001
+#define USB_RHS_NACK		0x0002
+#define USB_RHS_STALL		0x0003
+
+#define USB_RTHS_MASK		0x000f
+
+/* USB Command Register define */
+#define USB_CMD_STR_FIFO	0x80
+#define USB_CMD_FLUSH_FIFO	0x40
+#define USB_CMD_ISFT		0x20
+#define USB_CMD_DSFT		0x10
+#define USB_CMD_EP_MASK		0x03
+
+/* USB Event and Mask Register define */
+#define USB_E_MSF_MASK		0x0800
+#define USB_E_SFT_MASK		0x0400
+#define USB_E_RESET_MASK	0x0200
+#define USB_E_IDLE_MASK		0x0100
+#define USB_E_TXE4_MASK		0x0080
+#define USB_E_TXE3_MASK		0x0040
+#define USB_E_TXE2_MASK		0x0020
+#define USB_E_TXE1_MASK		0x0010
+#define USB_E_SOF_MASK		0x0008
+#define USB_E_BSY_MASK		0x0004
+#define USB_E_TXB_MASK		0x0002
+#define USB_E_RXB_MASK		0x0001
+
+/* operation mode */
+enum fhci_op_mode {
+	FHCI_OP_FS_DEVICE = 0,
+	FHCI_OP_LS_DEVICE,
+	FHCI_OP_HOST,
+	FHCI_OP_DISCONNECT,
+	FHCI_OP_POWER_ON,
+	FHCI_OP_POWER_OFF
+};
+
+/* Freescale USB Host controller registers */
+struct fhci_regs {
+	u8 usb_mod;		/* mode register */
+	u8 usb_addr;		/* address register */
+	u8 usb_comm;		/* command register */
+	u8 reserved1[1];
+	u16 usb_ep[4];		/* endpoint register */
+	u8 reserved2[4];
+	u16 usb_event;		/* event register */
+	u8 reserved3[2];
+	u16 usb_mask;		/* mask register */
+	u8 reserved4[1];
+	u8 usb_status;		/* status register */
+	u16 usb_sof_tmr;	/* Start Of Frame timer */
+	u8 reserved5[2];
+	u16 usb_frame_num;	/* frame number register */
+	u8 reserved6[1];
+};
+
+/* Freescale USB HOST */
+struct fhci_pram {
+	u16 ep_ptr[4];		/* Endpoint porter reg */
+	u32 rx_state;		/* Rx internal state */
+	u32 rx_ptr;		/* Rx internal data pointer */
+	u16 frame_num;		/* Frame number */
+	u16 rx_cnt;		/* Rx byte count */
+	u32 rx_temp;		/* Rx temp */
+	u32 rx_data_temp;	/* Rx data temp */
+	u16 rx_u_ptr;		/* Rx microcode return address temp */
+	u8 reserved1[2];	/* reserved area */
+	u32 sof_tbl;		/* SOF lookup table pointer */
+	u8 sof_u_crc_temp;	/* SOF micorcode CRC5 temp reg */
+	u8 reserved2[0xdb];
+};
+
+/* Freescale USB Endpoint*/
+struct fhci_ep_pram {
+	u16 rx_base;		/* Rx BD base address */
+	u16 tx_base;		/* Tx BD base address */
+	u8 rx_func_code;	/* Rx function code */
+	u8 tx_func_code;	/* Tx function code */
+	u16 rx_buff_len;	/* Rx buffer length */
+	u16 rx_bd_ptr;		/* Rx BD pointer */
+	u16 tx_bd_ptr;		/* Tx BD pointer */
+	u32 tx_state;		/* Tx internal state */
+	u32 tx_ptr;		/* Tx internal data pointer */
+	u16 tx_crc;		/* temp transmit CRC */
+	u16 tx_cnt;		/* Tx byte count */
+	u32 tx_temp;		/* Tx temp */
+	u16 tx_u_ptr;		/* Tx microcode return address temp */
+	u16 reserved;
+};
+
+struct fhci_controller_list {
+	struct list_head ctrl_list;	/* control endpoints */
+	struct list_head bulk_list;	/* bulk endpoints */
+	struct list_head iso_list;	/* isochronous endpoints */
+	struct list_head intr_list;	/* interruput endpoints */
+	struct list_head done_list;	/* done transfers */
+};
+
+struct virtual_root_hub {
+	int dev_num;	/* USB address of the root hub */
+	u32 feature;	/* indicates what feature has been set */
+	struct usb_hub_status hub;
+	struct usb_port_status port;
+};
+
+enum fhci_gpios {
+	GPIO_USBOE = 0,
+	GPIO_USBTP,
+	GPIO_USBTN,
+	GPIO_USBRP,
+	GPIO_USBRN,
+	/* these are optional */
+	GPIO_SPEED,
+	GPIO_SUSPN,
+};
+
+#define NUM_GPIOS (GPIO_SUSPN + 1)
+
+struct fhci_hcd {
+	struct fhci_regs __iomem *regs;	/* I/O memory used to communicate */
+	struct fhci_pram __iomem *pram;	/* Parameter RAM */
+	int timer;
+	unsigned int timer_irq;
+	int gpios[NUM_GPIOS];
+	enum qe_clock fullspeed_clk;
+	enum qe_clock lowspeed_clk;
+
+	spinlock_t lock;
+	struct fhci_usb *usb_lld; /* Low-level driver */
+	struct virtual_root_hub *vroot_hub; /* the virtual root hub */
+	int active_urbs;
+	struct fhci_controller_list *hc_list;
+	struct tasklet_struct *process_done_task; /* tasklet for done list */
+
+	struct list_head empty_eds;
+	struct list_head empty_tds;
+
+#ifdef CONFIG_FHCI_DEBUG
+	int usb_irq_stat[13];
+	struct dentry *dfs_root;
+	struct dentry *dfs_regs;
+	struct dentry *dfs_irq_stat;
+#endif
+};
+
+#define USB_FRAME_USAGE 90
+#define FRAME_TIME_USAGE (USB_FRAME_USAGE*10)	/* frame time usage */
+#define SW_FIX_TIME_BETWEEN_TRANSACTION 150	/* SW */
+#define MAX_BYTES_PER_FRAME (USB_FRAME_USAGE*15)
+#define MAX_PERIODIC_FRAME_USAGE 90
+
+/* transaction type */
+enum fhci_ta_type {
+	FHCI_TA_IN = 0,	/* input transaction */
+	FHCI_TA_OUT,	/* output transaction */
+	FHCI_TA_SETUP,	/* setup transaction */
+};
+
+/* transfer mode */
+enum fhci_tf_mode {
+	FHCI_TF_CTRL = 0,
+	FHCI_TF_ISO,
+	FHCI_TF_BULK,
+	FHCI_TF_INTR,
+};
+
+enum fhci_speed{
+	FHCI_FULL_SPEED,
+	FHCI_LOW_SPEED,
+};
+
+/* endpoint state */
+enum fhci_ed_state {
+	FHCI_ED_NEW = 0,/* pipe is new */
+	FHCI_ED_OPER,	/* pipe is operating */
+	FHCI_ED_URB_DEL,/* pipe is in hold because urb is being deleted */
+	FHCI_ED_SKIP,	/* skip this pipe */
+	FHCI_ED_HALTED,	/* pipe is halted */
+};
+
+enum fhci_port_status {
+	FHCI_PORT_DISABLED = 0,
+	FHCI_PORT_DISCONNECTING,
+	FHCI_PORT_WAITING, /* waiting for connection */
+	FHCI_PORT_FULL, /* full speed connected */
+	FHCI_PORT_LOW, /* low speed connected */
+};
+
+enum fhci_mem_alloc {
+	MEM_CACHABLE_SYS = 0x00000001,	/* primary DDR,cachable */
+	MEM_NOCACHE_SYS = 0x00000004,	/* primary DDR,non-cachable */
+	MEM_SECONDARY = 0x00000002,	/* either secondary DDR or SDRAM */
+	MEM_PRAM = 0x00000008,	/* multi-user RAM identifier */
+};
+
+/* USB default parameters*/
+#define DEFAULT_RING_LEN	8
+#define DEFAULT_DATA_MEM	MEM_CACHABLE_SYS
+
+struct ed {
+	u8 dev_addr;		/*device address */
+	u8 ep_addr;		/*endpoint address */
+	enum fhci_tf_mode mode;	/*USB transfer mode */
+	enum fhci_speed speed;
+	unsigned int max_pkt_size;
+	enum fhci_ed_state state;
+	struct list_head td_list; /*a list of all queued TD to this pipe */
+	struct list_head node;
+
+	/*read only parameters, should be cleared upon initialization */
+	u8 toggle_carry;	/*toggle carry from the last TD submitted */
+	u32 last_iso;		/*time stamp of last queued ISO transfer */
+	struct td *td_head;	/*a pointer to the current TD handled */
+};
+
+struct td {
+	void *data;		/* a pointer to the data buffer */
+	unsigned int len;	/* length of the data to be submitted */
+	unsigned int actual_len;/* actual bytes transfered on this td */
+	enum fhci_ta_type type;	/* transaction type */
+	u8 toggle;		/* toggle for the next transwithin this TD */
+	u16 iso_index;		/* ISO transaction index */
+	u16 start_frame;	/* start frame time stamp */
+	u16 interval;		/* interval between transaction (for ISO/Intr) */
+	u32 status;		/* status of the TD */
+	struct ed *ed;		/* a handle to the corresponding ED */
+	struct urb *urb;	/* a handle to the corresponding URB */
+	bool ioc;		/* Inform On Completion */
+	struct list_head node;
+
+	/*read only parameters should be cleared upon initialization */
+	struct packet *pkt;
+	int nak_cnt;
+	int error_cnt;
+	struct list_head frame_lh;
+};
+
+struct packet {
+	u8 *data;	/* packet data */
+	u32 len;	/* packet length */
+	u32 status;	/* status of the packet - equivalent to the status
+			 * field for the corresponding structure td */
+	u32 info;	/* packet information */
+	void *priv_data;/* private data of the driver */
+};
+
+/* struct for each URB */
+#define URB_INPROGRESS	0
+#define URB_DEL		1
+
+/* URB states (state field) */
+#define US_BULK		0
+#define US_BULK0	1
+
+/* three setup states */
+#define US_CTRL_SETUP	2
+#define US_CTRL_DATA	1
+#define US_CTRL_ACK	0
+
+#define EP_ZERO	0
+
+struct urb_priv {
+	int num_of_tds;
+	int tds_cnt;
+	int state;
+
+	struct td **tds;
+	struct ed *ed;
+	struct timer_list time_out;
+};
+
+/* struct for each 1mSec frame time */
+#define FRAME_IS_TRANSMITTED		0x00
+#define FRAME_TIMER_END_TRANSMISSION	0x01
+#define FRAME_DATA_END_TRANSMISSION	0x02
+#define FRAME_END_TRANSMISSION		0x03
+#define FRAME_IS_PREPARED		0x04
+
+struct fhci_time_frame {
+	u16 frame_num;		/*frame number */
+	u16 total_bytes;	/* total bytes submitted within this frame */
+	u8 frame_status;	/*flag that indicates to stop fill this frame */
+	struct list_head tds_list;	/*all tds of this frame */
+};
+
+/* internal driver structure*/
+struct fhci_usb {
+	u16 saved_msk;		/*saving of the USB mask register */
+	struct endpoint *ep0;	/*pointer for endpoint0 structure */
+	u32 intr_counter;	/*counter of the incoming USB Intr */
+	int intr_nesting_cnt;	/*interrupt nesting counter */
+	u16 max_frame_usage;	/*max frame time usage,in micro-sec */
+	u16 max_bytes_per_frame;/*max byte can be tx in one time frame */
+	u32 sw_transaction_time;/*sw complete trans time,in micro-sec */
+	struct fhci_time_frame *actual_frame;
+	struct fhci_controller_list *hc_list;	/* main structure for hc */
+	struct virtual_root_hub *vroot_hub;
+	enum fhci_port_status port_status;	/* v_rh port status */
+
+	u32 (*transfer_confirm)(struct fhci_hcd *fhci);
+
+	struct fhci_hcd *fhci;
+};
+
+/*
+ * Various helpers and prototypes below.
+ */
+
+static void move_head_to_tail(struct list_head *list)
+{
+	struct list_head *node = list->next;
+
+	if (!list_empty(list)) {
+		list_del(node);
+		list_add_tail(node, list);
+	}
+}
+
+/* maps the hardware error code to the USB error code */
+static int status_to_error(u32 status)
+{
+	if (status == USB_TD_OK)
+		return 0;
+	else if (status & USB_TD_RX_ER_CRC)
+		return -EILSEQ;
+	else if (status & USB_TD_RX_ER_NONOCT)
+		return -EPROTO;
+	else if (status & USB_TD_RX_ER_OVERUN)
+		return -ECOMM;
+	else if (status & USB_TD_RX_ER_BITSTUFF)
+		return -EPROTO;
+	else if (status & USB_TD_RX_ER_PID)
+		return -EILSEQ;
+	else if (status & (USB_TD_TX_ER_NAK | USB_TD_TX_ER_TIMEOUT))
+		return -ETIMEDOUT;
+	else if (status & USB_TD_TX_ER_STALL)
+		return -EPIPE;
+	else if (status & USB_TD_TX_ER_UNDERUN)
+		return -ENOSR;
+	else if (status & USB_TD_RX_DATA_UNDERUN)
+		return -EREMOTEIO;
+	else if (status & USB_TD_RX_DATA_OVERUN)
+		return -EOVERFLOW;
+	else
+		return -EINVAL;
+}
+
+static u16 get_frame_num(struct fhci_hcd *fhci)
+{
+	return fhci->pram->frame_num & 0x07ff;
+}
+
+
+#define fhci_dbg(fhci, fmt, args...) \
+		dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_err(fhci, fmt, args...) \
+		dev_err(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_info(fhci, fmt, args...) \
+		dev_info(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_warn(fhci, fmt, args...) \
+		dev_warn(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+
+static struct fhci_hcd *hcd_to_fhci(struct usb_hcd *hcd)
+{
+	return (struct fhci_hcd *)hcd->hcd_priv;
+}
+
+static struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci)
+{
+	return container_of((void *)fhci, struct usb_hcd, hcd_priv);
+}
+
+static void fhci_usb_enable_interrupt(struct fhci_usb *usb);
+static void fhci_usb_disable_interrupt(struct fhci_usb *usb);
+static void flush_all_transmissions(struct fhci_usb *usb);
+static void device_connected_interrupt(struct fhci_hcd *fhci);
+static void recycle_empty_td(struct fhci_hcd *fhci, struct td *td);
+static struct ed *get_empty_ed(struct fhci_hcd *fhci);
+
+static struct td *td_alloc_fill(struct fhci_hcd *fhci,
+	struct urb *urb, struct urb_priv *urb_priv,
+	struct ed *ed, u16 index, enum fhci_ta_type type,
+	int toggle, u8 *data, u32 len, u16 interval,
+	u16 start_frame, bool ioc);
+static void flush_bds(struct fhci_usb *usb);
+static void flush_actual_frame(struct fhci_usb *usb);
+static u32 host_transaction(struct fhci_usb *usb,
+	struct packet *pkt, enum fhci_ta_type trans_type,
+	u8 dest_addr, u8 dest_ep, enum fhci_tf_mode trans_mode,
+	enum fhci_speed dest_speed, u8 data_toggle);
+static void push_dummy_bd(struct endpoint *ep);
+static void device_disconnected_interrupt(struct fhci_hcd *fhci);
+static int fhci_ioports_check_bus_state(struct fhci_hcd *fhci);
+static void process_done_list(unsigned long data);
+
+
+static void recycle_frame(struct fhci_usb *usb, struct packet *pkt);
+static void transaction_confirm(struct fhci_usb *usb, struct packet *pkt);
+static void qe_usb_restart_tx(u8 ep);
+static void schedule_transactions(struct fhci_usb *usb);
+
+#endif /* __FHCI_H */
-- 
1.5.2.2



More information about the Linuxppc-dev mailing list