linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Patches to support QE USB Host Controller
@ 2008-08-08 16:17 Anton Vorontsov
  2008-08-08 16:18 ` [PATCH 1/3] gpiolib: make gpio_to_chip() public Anton Vorontsov
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-08 16:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman, linux-usb
  Cc: David Brownell, Li Yang, Timur Tabi, linuxppc-dev, linux-kernel

Hi all,

Most patches that were needed to support QE USB Host were merged during
2.6.27 merge window, and only three more patches left over. Here they
are.

David, could you bear with gpio_to_chip() exported function, just as
a stopgap for a proper api?

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-08 16:17 [PATCH 0/3] Patches to support QE USB Host Controller Anton Vorontsov
@ 2008-08-08 16:18 ` Anton Vorontsov
  2008-08-14 14:04   ` Laurent Pinchart
  2008-08-08 16:18 ` [PATCH 2/3] powerpc/qe: new call to revert a gpio to a dedicated function Anton Vorontsov
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-08 16:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman, linux-usb
  Cc: David Brownell, Li Yang, Timur Tabi, linuxppc-dev, linux-kernel

We'll need this function to write platform-specific hooks to deal
with pin's dedicated functions. Quite obviously this will work only
for the platforms with 1-to-1 GPIO to PIN mapping.

This is stopgap solution till we think out and implement a proper
api (pinlib?).

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/gpio/gpiolib.c     |    3 ++-
 include/asm-generic/gpio.h |    1 +
 2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 8d29405..9536fa8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -81,10 +81,11 @@ static void gpio_ensure_requested(struct gpio_desc *desc)
 }
 
 /* caller holds gpio_lock *OR* gpio is marked as requested */
-static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
+struct gpio_chip *gpio_to_chip(unsigned gpio)
 {
 	return gpio_desc[gpio].chip;
 }
+EXPORT_SYMBOL_GPL(gpio_to_chip);
 
 /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
 static int gpiochip_find_base(int ngpio)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 0f99ad3..d70ee45 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -81,6 +81,7 @@ struct gpio_chip {
 	unsigned		exported:1;
 };
 
+extern struct gpio_chip *gpio_to_chip(unsigned gpio);
 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
 			unsigned offset);
 extern int __must_check gpiochip_reserve(int start, int ngpio);
-- 
1.5.5.4


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

* [PATCH 2/3] powerpc/qe: new call to revert a gpio to a dedicated function
  2008-08-08 16:17 [PATCH 0/3] Patches to support QE USB Host Controller Anton Vorontsov
  2008-08-08 16:18 ` [PATCH 1/3] gpiolib: make gpio_to_chip() public Anton Vorontsov
@ 2008-08-08 16:18 ` Anton Vorontsov
  2008-08-08 16:18 ` [PATCH 3/3] USB: driver for Freescale QUICC Engine USB Host Controller Anton Vorontsov
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-08 16:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman, linux-usb
  Cc: David Brownell, Li Yang, Timur Tabi, linuxppc-dev, linux-kernel

qe_gpio_set_dedicated() is a platform specific function, which is used
to revert a pin to a dedicated function. Caller should have already
obtained the gpio via gpio_request().

This is needed to support Freescale USB Host Controller.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/include/asm/qe.h     |    1 +
 arch/powerpc/sysdev/qe_lib/gpio.c |   46 +++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h
index edee15d..c5c92d9 100644
--- a/arch/powerpc/include/asm/qe.h
+++ b/arch/powerpc/include/asm/qe.h
@@ -111,6 +111,7 @@ extern void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin,
 extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
 			     int assignment, int has_irq);
 extern int par_io_data_set(u8 port, u8 pin, u8 val);
+int qe_gpio_set_dedicated(unsigned int gpio);
 
 /* QE internal API */
 int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input);
diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
index 8e5a0bc..bd7278f 100644
--- a/arch/powerpc/sysdev/qe_lib/gpio.c
+++ b/arch/powerpc/sysdev/qe_lib/gpio.c
@@ -26,6 +26,9 @@ struct qe_gpio_chip {
 
 	/* shadowed data register to clear/set bits safely */
 	u32 cpdata;
+
+	/* saved_regs used to restore dedicated functions */
+	struct qe_pio_regs saved_regs;
 };
 
 static inline struct qe_gpio_chip *
@@ -40,6 +43,12 @@ static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
 	struct qe_pio_regs __iomem *regs = mm_gc->regs;
 
 	qe_gc->cpdata = in_be32(&regs->cpdata);
+	qe_gc->saved_regs.cpdata = qe_gc->cpdata;
+	qe_gc->saved_regs.cpdir1 = in_be32(&regs->cpdir1);
+	qe_gc->saved_regs.cpdir2 = in_be32(&regs->cpdir2);
+	qe_gc->saved_regs.cppar1 = in_be32(&regs->cppar1);
+	qe_gc->saved_regs.cppar2 = in_be32(&regs->cppar2);
+	qe_gc->saved_regs.cpodr = in_be32(&regs->cpodr);
 }
 
 static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio)
@@ -103,6 +112,43 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
 	return 0;
 }
 
+int qe_gpio_set_dedicated(unsigned int gpio)
+{
+	struct gpio_chip *gc = gpio_to_chip(gpio);
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+	struct qe_pio_regs __iomem *regs = mm_gc->regs;
+	struct qe_pio_regs *sregs = &qe_gc->saved_regs;
+	u8 pin = gpio - gc->base;
+	u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1));
+	u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2);
+	bool second_reg = pin > (QE_PIO_PINS / 2) - 1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qe_gc->lock, flags);
+
+	if (second_reg) {
+		clrsetbits_be32(&regs->cpdir2, mask2, sregs->cpdir2 & mask2);
+		clrsetbits_be32(&regs->cppar2, mask2, sregs->cppar2 & mask2);
+	} else {
+		clrsetbits_be32(&regs->cpdir1, mask2, sregs->cpdir1 & mask2);
+		clrsetbits_be32(&regs->cppar1, mask2, sregs->cppar1 & mask2);
+	}
+
+	if (sregs->cpdata & mask1)
+		qe_gc->cpdata |= mask1;
+	else
+		qe_gc->cpdata &= ~mask1;
+
+	out_be32(&regs->cpdata, qe_gc->cpdata);
+	clrsetbits_be32(&regs->cpodr, mask1, sregs->cpodr & mask1);
+
+	spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(qe_gpio_set_dedicated);
+
 static int __init qe_add_gpiochips(void)
 {
 	struct device_node *np;
-- 
1.5.5.4


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

* [PATCH 3/3] USB: driver for Freescale QUICC Engine USB Host Controller
  2008-08-08 16:17 [PATCH 0/3] Patches to support QE USB Host Controller Anton Vorontsov
  2008-08-08 16:18 ` [PATCH 1/3] gpiolib: make gpio_to_chip() public Anton Vorontsov
  2008-08-08 16:18 ` [PATCH 2/3] powerpc/qe: new call to revert a gpio to a dedicated function Anton Vorontsov
@ 2008-08-08 16:18 ` Anton Vorontsov
  2008-08-08 23:26 ` [PATCH 0/3] Patches to support QE " Greg KH
  2008-08-14 12:15 ` Laurent Pinchart
  4 siblings, 0 replies; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-08 16:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman, linux-usb
  Cc: David Brownell, Li Yang, Timur Tabi, linuxppc-dev, linux-kernel

This is patch adds support for the FHCI USB controller, as found
in the Freescale MPC836x and MPC832x processors. It can support
Full or Low speed modes.

Quite a lot the hardware is doing by itself (SOF generation, CRC
generation and checking), though scheduling and retransmission is on
software's shoulders.

This controller does not integrate the root hub, so this driver also
fakes an one-port hub. External hub is required to support more than
one device.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/usb/Makefile          |    1 +
 drivers/usb/host/Kconfig      |   17 +
 drivers/usb/host/Makefile     |    1 +
 drivers/usb/host/fhci-cq.c    |  105 +++++
 drivers/usb/host/fhci-dbg.c   |  144 +++++++
 drivers/usb/host/fhci-hcd.c   |  783 +++++++++++++++++++++++++++++++++++++
 drivers/usb/host/fhci-hub.c   |  332 ++++++++++++++++
 drivers/usb/host/fhci-mem.c   |  105 +++++
 drivers/usb/host/fhci-q.c     |  242 ++++++++++++
 drivers/usb/host/fhci-sched.c |  864 +++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/fhci-tds.c   |  633 ++++++++++++++++++++++++++++++
 drivers/usb/host/fhci.h       |  526 +++++++++++++++++++++++++
 12 files changed, 3753 insertions(+), 0 deletions(-)
 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-sched.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 a419c42..7b14564 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_C67X00_HCD)	+= c67x00/
 
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 228797e..594c263 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -305,3 +305,20 @@ config SUPERH_ON_CHIP_R8A66597
 	help
 	   This driver enables support for the on-chip R8A66597 in the
 	   SH7366 and SH7723 processors.
+
+config USB_FHCI_HCD
+	tristate "Freescale QE USB Host Controller support"
+	depends on USB && OF_GPIO && QUICC_ENGINE
+	select FSL_GTM
+	select QE_USB
+	help
+	  This driver enables support for Freescale QE USB Host Controller
+	  (as found on MPC8360 and MPC8323 processors), the driver supports
+	  Full and Low Speed USB.
+
+config FHCI_DEBUG
+	bool "Freescale QE USB Host Controller debug support"
+	depends on USB_FHCI_HCD && DEBUG_FS
+	help
+	  Say "y" to see some FHCI debug information and statistics
+	  throught debugfs.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index f1edda2..b71dfed 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -19,3 +19,4 @@ 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_ISP1760_HCD)	+= isp1760.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..23716fa
--- /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@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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 NULL;
+
+	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..8127935
--- /dev/null
+++ b/drivers/usb/host/fhci-dbg.c
@@ -0,0 +1,144 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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.
+ */
+
+#ifdef CONFIG_FHCI_DEBUG
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+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) {
+		WARN_ON(1);
+		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);
+
+	WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat);
+}
+
+static void fhci_dfs_destroy(struct fhci_hcd *fhci)
+{
+	if (!fhci->dfs_root)
+		return;
+
+	if (fhci->dfs_irq_stat)
+		debugfs_remove(fhci->dfs_irq_stat);
+
+	if (fhci->dfs_regs)
+		debugfs_remove(fhci->dfs_regs);
+
+	debugfs_remove(fhci->dfs_root);
+}
+
+#else
+
+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) {}
+
+#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..8cc2aaa
--- /dev/null
+++ b/drivers/usb/host/fhci-hcd.c
@@ -0,0 +1,783 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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.
+ */
+
+#if defined(CONFIG_FHCI_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+#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 <linux/of_gpio.h>
+#include <asm/qe.h>
+#include <asm/fsl_gtm.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"
+#include "fhci-tds.c"
+#include "fhci-sched.c"
+
+static void fhci_start_sof_timer(struct fhci_hcd *fhci)
+{
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	/* clear frame_n */
+	out_be16(&fhci->pram->frame_num, 0);
+
+	out_be16(&fhci->regs->usb_sof_tmr, 0);
+	setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static void fhci_stop_sof_timer(struct fhci_hcd *fhci)
+{
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
+	gtm_stop_timer16(fhci->timer);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static u16 get_sof_timer_count(struct fhci_usb *usb)
+{
+	return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12);
+}
+
+/* initialize the endpoint zero */
+static 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 */
+static 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 */
+		out_be16(&usb->fhci->regs->usb_event, 0xffff);
+		out_be16(&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 */
+static 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_nosync(fhci->timer->irq);
+
+		/* disable the usb interrupt */
+		disable_irq_nosync(fhci_to_hcd(fhci)->irq);
+		out_be16(&usb->fhci->regs->usb_mask, 0);
+	}
+	usb->intr_nesting_cnt++;
+}
+
+/* enable the USB controller */
+static u32 fhci_usb_enable(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	out_be16(&usb->fhci->regs->usb_event, 0xffff);
+	out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+	setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+
+	mdelay(100);
+
+	return 0;
+}
+
+/* disable the USB controller */
+static u32 fhci_usb_disable(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	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);
+
+	clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+
+	return 0;
+}
+
+/* check the bus state by polling the QE bit on the IO ports */
+static 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;
+}
+
+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);
+}
+
+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;
+}
+
+/* destroy the fhci_usb structure */
+static void fhci_usb_free(void *lld)
+{
+	struct fhci_usb *usb = lld;
+	struct fhci_hcd *fhci = usb->fhci;
+
+	if (usb) {
+		config_transceiver(fhci, FHCI_PORT_POWER_OFF);
+		endpoint_zero_free(usb);
+		kfree(usb->actual_frame);
+		kfree(usb);
+	}
+}
+
+/* initialize the USB */
+static u32 fhci_usb_init(struct fhci_hcd *fhci)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE);
+
+	usb->port_status = FHCI_PORT_DISABLED;
+	usb->max_frame_usage = FRAME_TIME_USAGE;
+	usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION;
+
+	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 */
+	out_be16(&fhci->pram->frame_num, 0);
+
+	/* clear rx state */
+	out_be32(&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);
+
+	out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN);
+
+	/* clearing the mask register */
+	out_be16(&usb->fhci->regs->usb_mask, 0);
+
+	/* initialing the event register */
+	out_be16(&usb->fhci->regs->usb_event, 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 */
+static 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;
+	unsigned long flags;
+
+	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;
+	}
+
+	spin_lock_irqsave(&fhci->lock, flags);
+	/* 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_irqrestore(&fhci->lock, flags);
+	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;
+	unsigned long flags;
+
+	if (!urb || !urb->dev || !urb->dev->bus)
+		goto out;
+
+	spin_lock_irqsave(&fhci->lock, flags);
+
+	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 out2;
+
+		urb_priv->state = URB_DEL;
+
+		/* already pending? */
+		urb_priv->ed->state = FHCI_ED_URB_DEL;
+	} else
+		urb_complete_free(fhci, urb);
+
+out2:
+	spin_unlock_irqrestore(&fhci->lock, flags);
+out:
+	return 0;
+}
+
+static void fhci_endpoint_disable(struct usb_hcd *hcd,
+				  struct usb_host_endpoint *ep)
+{
+	struct fhci_hcd *fhci;
+	struct ed *ed;
+	unsigned long flags;
+
+	fhci = hcd_to_fhci(hcd);
+	spin_lock_irqsave(&fhci->lock, flags);
+	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_irqrestore(&fhci->lock, flags);
+}
+
+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 | HCD_MEMORY,
+
+	/* 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 = NULL;
+	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 = cpm_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 = cpm_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" : "power");
+			}
+		}
+
+		ret = gpio_request(gpio, dev->bus_id);
+		if (ret) {
+			dev_err(dev, "failed to request gpio %d", i);
+			goto err_gpios;
+		}
+	}
+
+	fhci->timer = gtm_get_timer16();
+	if (IS_ERR(fhci->timer)) {
+		ret = PTR_ERR(fhci->timer);
+		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 | IRQF_TIMER, "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);
+
+	config_transceiver(fhci, FHCI_PORT_POWER_OFF);
+
+	/* start with low-speed, if possible */
+	if (fhci->lowspeed_clk != QE_CLK_NONE) {
+		config_transceiver(fhci, FHCI_PORT_LOW);
+		qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
+	} else {
+		config_transceiver(fhci, FHCI_PORT_FULL);
+		qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
+	}
+
+	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:
+	gtm_put_timer16(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);
+	gtm_put_timer16(fhci->timer);
+	cpm_muram_free(cpm_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 u32 *reg;
+	const char *clk;
+	const u32 *power_budget;
+	int i;
+
+	reg = of_get_property(ofdev->node, "reg", &size);
+	if (!reg || size < sizeof(*reg) * 4) {
+		dev_err(dev, "can't get pram offset\n");
+		return -EINVAL;;
+	}
+	pi.pram_addr = reg[2];
+
+	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, "fsl,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, "fsl,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, "linux,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 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@freescale.com>, "
+	      "Jerry Huang <Chang-Ming.Huang@freescale.com>, "
+	      "Anton Vorontsov <avorontsov@ru.mvista.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..e736d0b
--- /dev/null
+++ b/drivers/usb/host/fhci-hub.c
@@ -0,0 +1,332 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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 config_transceiver(struct fhci_hcd *fhci,
+			       enum fhci_port_status status)
+{
+	fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
+
+	switch (status) {
+	case FHCI_PORT_POWER_OFF:
+		if (fhci->gpios[GPIO_POWER] >= 0) {
+			gpio_set_value(fhci->gpios[GPIO_POWER], 0);
+			mdelay(5);
+		}
+		break;
+	case FHCI_PORT_DISABLED:
+	case FHCI_PORT_WAITING:
+		if (fhci->gpios[GPIO_POWER] >= 0) {
+			gpio_set_value(fhci->gpios[GPIO_POWER], 1);
+			mdelay(5);
+		}
+		break;
+	case FHCI_PORT_LOW:
+		if (fhci->gpios[GPIO_SPEED] >= 0) {
+			gpio_set_value(fhci->gpios[GPIO_SPEED], 0);
+			mdelay(5);
+		}
+		break;
+	case FHCI_PORT_FULL:
+		if (fhci->gpios[GPIO_SPEED] >= 0) {
+			gpio_set_value(fhci->gpios[GPIO_SPEED], 1);
+			mdelay(5);
+		}
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
+}
+
+/* disable the USB port by clearing the EN bit in the USBMOD register */
+static 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_dbg(fhci, "-> %s\n", __func__);
+
+	fhci_stop_sof_timer(fhci);
+
+	flush_all_transmissions(usb);
+
+	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;
+	out_be16(&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;
+	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
+	fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* enable the USB port by setting the EN bit in the USBMOD register */
+static void usb_port_enable(void *lld)
+{
+	struct fhci_usb *usb = (struct fhci_usb *)lld;
+	struct fhci_hcd *fhci = usb->fhci;
+
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	config_transceiver(fhci, usb->port_status);
+
+	if ((usb->port_status != FHCI_PORT_FULL) &&
+			(usb->port_status != FHCI_PORT_LOW))
+		fhci_start_sof_timer(fhci);
+
+	usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
+	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static void io_port_generate_reset(struct fhci_hcd *fhci)
+{
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
+	gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
+	gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
+
+	mdelay(5);
+
+	qe_gpio_set_dedicated(fhci->gpios[GPIO_USBOE]);
+	qe_gpio_set_dedicated(fhci->gpios[GPIO_USBTP]);
+	qe_gpio_set_dedicated(fhci->gpios[GPIO_USBTN]);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* generate the RESET condition on the bus */
+static 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_dbg(fhci, "-> %s\n", __func__);
+
+	fhci_stop_sof_timer(fhci);
+	/* disable the USB controller */
+	mode = in_8(&fhci->regs->usb_mod);
+	out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN));
+
+	/* disable idle interrupts */
+	mask = in_be16(&fhci->regs->usb_mask);
+	out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK));
+
+	io_port_generate_reset(fhci);
+
+	/* enable interrupt on this endpoint */
+	out_be16(&fhci->regs->usb_mask, mask);
+
+	/* enable the USB controller */
+	mode = in_8(&fhci->regs->usb_mod);
+	out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN);
+	fhci_start_sof_timer(fhci);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	int ret = 0;
+	unsigned long flags;
+
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	spin_lock_irqsave(&fhci->lock, flags);
+
+	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)) {
+		*buf = 1 << 1;
+		ret = 1;
+		fhci_dbg(fhci, "-- %s\n", __func__);
+	}
+
+	spin_unlock_irqrestore(&fhci->lock, flags);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+
+	return ret;
+}
+
+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;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fhci->lock, flags);
+
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	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_PORT_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_PORT_WAITING);
+			break;
+		default:
+			goto error;
+		}
+		break;
+	default:
+error:
+		retval = -EPIPE;
+	}
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+
+	spin_unlock_irqrestore(&fhci->lock, flags);
+
+	return retval;
+}
diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c
new file mode 100644
index 0000000..d9e098e
--- /dev/null
+++ b/drivers/usb/host/fhci-mem.c
@@ -0,0 +1,105 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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_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->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;
+
+	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..493c7a5
--- /dev/null
+++ b/drivers/usb/host/fhci-q.c
@@ -0,0 +1,242 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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 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);
+	}
+}
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
new file mode 100644
index 0000000..ce8bc89
--- /dev/null
+++ b/drivers/usb/host/fhci-sched.c
@@ -0,0 +1,864 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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 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 = NULL;
+
+	cq_put(usb->ep0->empty_frame_Q, pkt);
+}
+
+/* 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 ((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) {
+				fhci_dbg(usb->fhci, "td err fu\n");
+				td->toggle = !td->toggle;
+				td->actual_len += trans_len;
+			} else {
+				fhci_dbg(usb->fhci, "td err f!u\n");
+			}
+		} else {
+			fhci_dbg(usb->fhci, "td err !f\n");
+			/* 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 */
+		fhci_vdbg(usb->fhci, "td nack\n");
+		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);
+}
+
+/*
+ * 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
+ */
+static void flush_all_transmissions(struct fhci_usb *usb)
+{
+	u8 mode;
+	struct td *td;
+
+	mode = in_8(&usb->fhci->regs->usb_mod);
+	clrbits8(&usb->fhci->regs->usb_mod, 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 */
+	out_be16(&usb->fhci->regs->usb_event, 0xffff);
+	/* enable the USB controller */
+	out_8(&usb->fhci->regs->usb_mod, 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;
+		break;
+	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) {
+		fhci_vdbg(usb->fhci, "not enough space in this frame: "
+			  "%d %d %d\n", usb->actual_frame->total_bytes, len,
+			  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)))) {
+		fhci_dbg(usb->fhci, "not enough time in this frame\n");
+		return -1;
+	}
+
+	/* update frame object fields before transmitting */
+	pkt = cq_get(usb->ep0->empty_frame_Q);
+	if (!pkt) {
+		fhci_dbg(usb->fhci, "there is no empty frame\n");
+		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);
+		fhci_err(usb->fhci, "host transaction failed\n");
+		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 || (td && td->status == USB_TD_INPROGRESS))
+			continue;
+
+		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;
+			}
+			continue;
+		}
+
+		/*
+		 * if it isn't interrupt pipe or it is not iso pipe and the
+		 * interval time passed
+		 */
+		if ((list_type == FHCI_TF_INTR || list_type == FHCI_TF_ISO) &&
+				(((usb->actual_frame->frame_num -
+				   td->start_frame) & 0x7ff) < td->interval))
+			continue;
+
+		if (add_packet(usb, ed, td) < 0)
+			continue;
+
+		/* 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 ((((in_be16(&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 = in_be16(&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);
+}
+
+/* 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;
+		out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+	}
+
+	gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false);
+
+	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)
+{
+	struct fhci_usb *usb = fhci->usb_lld;
+
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	fhci_usb_disable_interrupt(usb);
+	clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+	usb->port_status = FHCI_PORT_DISABLED;
+
+	/* Enable IDLE since we want to know if something comes along */
+	usb->saved_msk |= USB_E_IDLE_MASK;
+	out_be16(&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);
+
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* 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;
+
+	fhci_dbg(fhci, "-> %s\n", __func__);
+
+	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_warn(fhci, "Low-Speed device is not supported, "
+				  "try use BRGx\n");
+			goto out;
+		}
+
+		usb->port_status = FHCI_PORT_LOW;
+		setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+		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_warn(fhci, "Full-Speed device is not supported, "
+				  "try use CLKx\n");
+			goto out;
+		}
+
+		usb->port_status = FHCI_PORT_FULL;
+		clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+		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);
+	}
+out:
+	fhci_usb_enable_interrupt(usb);
+	fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static 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);
+
+	gtm_set_exact_timer16(fhci->timer, 1000, false);
+
+	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;
+}
+
+/* Cancel transmission on the USB endpoint */
+static void abort_transmission(struct fhci_usb *usb)
+{
+	fhci_dbg(usb->fhci, "-> %s\n", __func__);
+	/* issue stop Tx command */
+	qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
+	/* flush Tx FIFOs */
+	out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO);
+	udelay(1000);
+	/* reset Tx BDs */
+	flush_bds(usb);
+	/* issue restart Tx command */
+	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
+	fhci_dbg(usb->fhci, "<- %s\n", __func__);
+}
+
+static irqreturn_t fhci_irq(struct usb_hcd *hcd)
+{
+	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+	struct fhci_usb *usb;
+	u16 usb_er = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fhci->lock, flags);
+
+	usb = fhci->usb_lld;
+
+	usb_er |= in_be16(&usb->fhci->regs->usb_event) &
+		  in_be16(&usb->fhci->regs->usb_mask);
+
+	/* clear event bits for next time */
+	out_be16(&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;
+			out_be16(&usb->fhci->regs->usb_event,
+				 usb->saved_msk);
+		} else if (usb->port_status == FHCI_PORT_DISABLED) {
+			if (fhci_ioports_check_bus_state(fhci) == 1 &&
+					usb->port_status != FHCI_PORT_LOW &&
+					usb->port_status != FHCI_PORT_FULL)
+				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);
+	}
+
+	if (usb_er & USB_E_TXB_MASK) {
+		tx_conf_interrupt(fhci->usb_lld);
+		usb_er &= ~USB_E_TXB_MASK;
+	}
+
+	if (usb_er & USB_E_TXE1_MASK) {
+		tx_conf_interrupt(fhci->usb_lld);
+		usb_er &= ~USB_E_TXE1_MASK;
+	}
+
+	if (usb_er & USB_E_IDLE_MASK) {
+		if (usb->port_status == FHCI_PORT_DISABLED &&
+				usb->port_status != FHCI_PORT_LOW &&
+				usb->port_status != FHCI_PORT_FULL) {
+			usb_er &= ~USB_E_RESET_MASK;
+			device_connected_interrupt(fhci);
+		} else if (usb->port_status ==
+				FHCI_PORT_DISCONNECTING) {
+			/* XXX usb->port_status = FHCI_PORT_WAITING; */
+			/* Disable IDLE */
+			usb->saved_msk &= ~USB_E_IDLE_MASK;
+			out_be16(&usb->fhci->regs->usb_mask,
+				 usb->saved_msk);
+		} else {
+			fhci_dbg_isr(fhci, -1);
+		}
+
+		usb_er &= ~USB_E_IDLE_MASK;
+	}
+
+	spin_unlock_irqrestore(&fhci->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * 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;
+
+	disable_irq(fhci->timer->irq);
+	disable_irq(fhci_to_hcd(fhci)->irq);
+	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);
+	enable_irq(fhci->timer->irq);
+	enable_irq(fhci_to_hcd(fhci)->irq);
+}
+
+static DECLARE_TASKLET(fhci_tasklet, process_done_list, 0);
+
+/* 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;
+}
+
+/*
+ * 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 = usb_pipedevice(urb->pipe);
+		ed->ep_addr = 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;
+		fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n",
+			 ed->speed, ed->max_pkt_size);
+	}
+
+	/* 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 = NULL;
+
+	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_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_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_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_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_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_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_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 = 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_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);
+		}
+		break;
+	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);
+			break;
+		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..49df9c8
--- /dev/null
+++ b/drivers/usb/host/fhci-tds.c
@@ -0,0 +1,633 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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 {
+	__be16 status;
+	__be16 length;
+	__be32 buf_ptr;
+	__be16 extra;
+	__be16 reserved;
+};
+
+struct endpoint {
+	/* Pointer to ep parameter RAM */
+	struct fhci_ep_pram __iomem *ep_pram_ptr;
+
+	/* Host transactions */
+	struct usb_td __iomem *td_base; /* first TD in the ring */
+	struct usb_td __iomem *conf_td; /* next TD for confirm after transac */
+	struct usb_td __iomem *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;
+};
+
+static struct usb_td __iomem *next_bd(struct usb_td __iomem *base,
+				      struct usb_td __iomem *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 = in_be16(&ep->empty_td->status);
+
+		out_be32(&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 */
+static void endpoint_zero_free(struct fhci_usb *usb)
+{
+	struct endpoint *ep;
+	int size;
+
+	ep = usb->ep0;
+	if (ep) {
+		if (ep->td_base)
+			cpm_muram_free(cpm_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
+ */
+static u32 create_endpoint(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
+			   u32 ring_len)
+{
+	struct endpoint *ep;
+	struct usb_td __iomem *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 = cpm_muram_alloc(ep_mem_size, 32);
+	if (IS_ERR_VALUE(ep_offset))
+		goto err;
+	ep->td_base = cpm_muram_addr(ep_offset);
+
+	/* zero all queue pointers */
+	ep->conf_frame_Q = cq_new(ring_len + 2);
+	ep->empty_frame_Q = cq_new(ring_len + 2);
+	ep->dummy_packets_Q = cq_new(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 __iomem *)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++) {
+		out_be32(&td->buf_ptr, 0);
+		out_be16(&td->status, 0);
+		out_be16(&td->length, 0);
+		out_be16(&td->extra, 0);
+		td++;
+	}
+	td--;
+	out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
+	out_be16(&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)
+ */
+static 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 */
+	out_be16(&usb->fhci->regs->usb_ep[0],
+		 USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
+	out_be16(&usb->fhci->pram->ep_ptr[0],
+		 cpm_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
+	out_8(&ep->ep_pram_ptr->rx_func_code, rt);
+	out_8(&ep->ep_pram_ptr->tx_func_code, rt);
+	out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028);
+	out_be16(&ep->ep_pram_ptr->rx_base, 0);
+	out_be16(&ep->ep_pram_ptr->tx_base, cpm_muram_offset(ep->td_base));
+	out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0);
+	out_be16(&ep->ep_pram_ptr->tx_bd_ptr, cpm_muram_offset(ep->td_base));
+	out_be32(&ep->ep_pram_ptr->tx_state, 0);
+}
+
+/*
+ * 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 __iomem *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
+	 */
+	while (1) {
+		td = ep->conf_td;
+		td_status = in_be16(&td->status);
+		td_length = in_be16(&td->length);
+		buf = in_be32(&td->buf_ptr);
+		extra_data = in_be16(&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 */
+		clrbits16(&td->status, ~TD_W);
+		out_be16(&td->length, 0);
+		out_be32(&td->buf_ptr, 0);
+		out_be16(&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);
+	}
+}
+
+/*
+ * 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
+ */
+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)
+{
+	struct endpoint *ep = usb->ep0;
+	struct usb_td __iomem *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 = in_be16(&td->status);
+
+	if (td_status & TD_R && in_be16(&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;
+	out_be32(&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;
+	out_be16(&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;
+
+	out_be16(&td->status, td_status);
+
+	/* set up buffer length */
+	if (trans_type == FHCI_TA_IN)
+		out_be16(&td->length, pkt->len + CRC_SIZE);
+	else
+		out_be16(&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)
+		out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
+
+	return 0;
+}
+
+/* Reset the Tx BD ring */
+static void flush_bds(struct fhci_usb *usb)
+{
+	u16 extra_data;
+	u16 td_status;
+	u32 buf;
+	struct usb_td __iomem *td;
+	struct endpoint *ep = usb->ep0;
+
+	td = ep->td_base;
+	while (1) {
+		td_status = in_be16(&td->status);
+		buf = in_be32(&td->buf_ptr);
+		extra_data = in_be16(&td->extra);
+
+		/* if the TD is not empty - we'll confirm it as Timeout */
+		if (td_status & TD_R)
+			out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
+		/* if this TD is dummy - let's skip this TD */
+		else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER)
+			out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER);
+		/* if this is the last TD - break */
+		if (td_status & TD_W)
+			break;
+
+		td++;
+	}
+
+	td_transaction_confirm(usb);
+
+	td = ep->td_base;
+	do {
+		out_be16(&td->status, 0);
+		out_be16(&td->length, 0);
+		out_be32(&td->buf_ptr, 0);
+		out_be16(&td->extra, 0);
+		td++;
+	} while (!(in_be16(&td->status) & TD_W));
+	out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
+	out_be16(&td->length, 0);
+	out_be32(&td->buf_ptr, 0);
+	out_be16(&td->extra, 0);
+
+	out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
+		 in_be16(&ep->ep_pram_ptr->tx_base));
+	out_be32(&ep->ep_pram_ptr->tx_state, 0);
+	out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
+	ep->empty_td = ep->td_base;
+	ep->conf_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
+ */
+static 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 __iomem *td;
+	struct endpoint *ep = usb->ep0;
+
+	/* disable the USB controller */
+	mode = in_8(&usb->fhci->regs->usb_mod);
+	out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN);
+
+	tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
+	td = cpm_muram_addr(tb_ptr);
+	td_status = in_be16(&td->status);
+	buf_ptr = in_be32(&td->buf_ptr);
+	extra_data = in_be16(&td->extra);
+	do {
+		if (td_status & TD_R) {
+			out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
+		} else {
+			out_be32(&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 = in_be16(&td->status);
+		buf_ptr = in_be32(&td->buf_ptr);
+		extra_data = in_be16(&td->extra);
+	} while ((td_status & TD_R) || buf_ptr);
+
+	td_transaction_confirm(usb);
+
+	out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
+		 in_be16(&ep->ep_pram_ptr->tx_base));
+	out_be32(&ep->ep_pram_ptr->tx_state, 0);
+	out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
+	ep->empty_td = ep->td_base;
+	ep->conf_td = ep->td_base;
+
+	usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+
+	/* reset the event register */
+	out_be16(&usb->fhci->regs->usb_event, 0xffff);
+	/* enable the USB controller */
+	out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
+}
+
+/* handles Tx confirm and Tx error interrupt */
+static 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);
+}
+
+static void host_transmit_actual_frame(struct fhci_usb *usb)
+{
+	u16 tb_ptr;
+	u16 td_status;
+	struct usb_td __iomem *td;
+	struct endpoint *ep = usb->ep0;
+
+	tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
+	td = cpm_muram_addr(tb_ptr);
+
+	if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) {
+		struct usb_td __iomem *old_td = td;
+
+		ep->already_pushed_dummy_bd = false;
+		td_status = in_be16(&td->status);
+		/* gets the next TD in the ring */
+		td = next_bd(ep->td_base, td, td_status);
+		tb_ptr = cpm_muram_offset(td);
+		out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr);
+
+		/* start transmit only if we have something in the TDs */
+		if (in_be16(&td->status) & TD_R)
+			out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
+
+		if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) {
+			out_be32(&old_td->buf_ptr, 0);
+			ep->conf_td = next_bd(ep->td_base, ep->conf_td,
+					      td_status);
+		} else {
+			out_be32(&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..3df6b21
--- /dev/null
+++ b/drivers/usb/host/fhci.h
@@ -0,0 +1,526 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ *               Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *               Anton Vorontsov <avorontsov@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
+
+#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
+
+/* 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];
+	__be16 usb_ep[4];	/* endpoint register */
+	u8 reserved2[4];
+	__be16 usb_event;	/* event register */
+	u8 reserved3[2];
+	__be16 usb_mask;	/* mask register */
+	u8 reserved4[1];
+	u8 usb_status;		/* status register */
+	__be16 usb_sof_tmr;	/* Start Of Frame timer */
+	u8 reserved5[2];
+	__be16 usb_frame_num;	/* frame number register */
+	u8 reserved6[1];
+};
+
+/* Freescale USB HOST */
+struct fhci_pram {
+	__be16 ep_ptr[4];	/* Endpoint porter reg */
+	__be32 rx_state;	/* Rx internal state */
+	__be32 rx_ptr;		/* Rx internal data pointer */
+	__be16 frame_num;	/* Frame number */
+	__be16 rx_cnt;		/* Rx byte count */
+	__be32 rx_temp;		/* Rx temp */
+	__be32 rx_data_temp;	/* Rx data temp */
+	__be16 rx_u_ptr;	/* Rx microcode return address temp */
+	u8 reserved1[2];	/* reserved area */
+	__be32 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 {
+	__be16 rx_base;		/* Rx BD base address */
+	__be16 tx_base;		/* Tx BD base address */
+	u8 rx_func_code;	/* Rx function code */
+	u8 tx_func_code;	/* Tx function code */
+	__be16 rx_buff_len;	/* Rx buffer length */
+	__be16 rx_bd_ptr;	/* Rx BD pointer */
+	__be16 tx_bd_ptr;	/* Tx BD pointer */
+	__be32 tx_state;	/* Tx internal state */
+	__be32 tx_ptr;		/* Tx internal data pointer */
+	__be16 tx_crc;		/* temp transmit CRC */
+	__be16 tx_cnt;		/* Tx byte count */
+	__be32 tx_temp;		/* Tx temp */
+	__be16 tx_u_ptr;	/* Tx microcode return address temp */
+	__be16 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_POWER,
+};
+
+#define NUM_GPIOS (GPIO_POWER + 1)
+
+struct fhci_hcd {
+	struct fhci_regs __iomem *regs;	/* I/O memory used to communicate */
+	struct fhci_pram __iomem *pram;	/* Parameter RAM */
+	struct gtm_timer *timer;
+	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_POWER_OFF = 0,
+	FHCI_PORT_DISABLED,
+	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 __iomem *priv_data; /* private data of the driver (TDs or BDs) */
+};
+
+/* 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 */
+	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 in_be16(&fhci->pram->frame_num) & 0x07ff;
+}
+
+#define fhci_dbg(fhci, fmt, args...) \
+		dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_vdbg(fhci, fmt, args...) \
+		dev_vdbg(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);
+}
+
+/* fhci-hcd.c */
+static void fhci_start_sof_timer(struct fhci_hcd *fhci);
+static void fhci_stop_sof_timer(struct fhci_hcd *fhci);
+static u16 get_sof_timer_count(struct fhci_usb *usb);
+static void fhci_usb_enable_interrupt(struct fhci_usb *usb);
+static void fhci_usb_disable_interrupt(struct fhci_usb *usb);
+static int fhci_ioports_check_bus_state(struct fhci_hcd *fhci);
+
+/* fhci-mem.c */
+static void recycle_empty_td(struct fhci_hcd *fhci, struct td *td);
+
+/* fhci-{tds,bds}.c */
+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);
+
+/* fhci-sched.c */
+static void transaction_confirm(struct fhci_usb *usb, struct packet *pkt);
+static void flush_all_transmissions(struct fhci_usb *usb);
+static void schedule_transactions(struct fhci_usb *usb);
+static void device_connected_interrupt(struct fhci_hcd *fhci);
+
+#endif /* __FHCI_H */
-- 
1.5.5.4

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

* Re: [PATCH 0/3] Patches to support QE USB Host Controller
  2008-08-08 16:17 [PATCH 0/3] Patches to support QE USB Host Controller Anton Vorontsov
                   ` (2 preceding siblings ...)
  2008-08-08 16:18 ` [PATCH 3/3] USB: driver for Freescale QUICC Engine USB Host Controller Anton Vorontsov
@ 2008-08-08 23:26 ` Greg KH
  2008-08-26 15:49   ` Anton Vorontsov
  2008-08-14 12:15 ` Laurent Pinchart
  4 siblings, 1 reply; 19+ messages in thread
From: Greg KH @ 2008-08-08 23:26 UTC (permalink / raw)
  To: Anton Vorontsov
  Cc: linux-usb, David Brownell, Li Yang, Timur Tabi, linuxppc-dev,
	linux-kernel

On Fri, Aug 08, 2008 at 08:17:17PM +0400, Anton Vorontsov wrote:
> Hi all,
> 
> Most patches that were needed to support QE USB Host were merged during
> 2.6.27 merge window, and only three more patches left over. Here they
> are.
> 
> David, could you bear with gpio_to_chip() exported function, just as
> a stopgap for a proper api?

I'll hold off on the 3rd patch until david takes the first two.

thanks,

greg k-h

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

* Re: [PATCH 0/3] Patches to support QE USB Host Controller
  2008-08-08 16:17 [PATCH 0/3] Patches to support QE USB Host Controller Anton Vorontsov
                   ` (3 preceding siblings ...)
  2008-08-08 23:26 ` [PATCH 0/3] Patches to support QE " Greg KH
@ 2008-08-14 12:15 ` Laurent Pinchart
  2008-09-24 20:04   ` David Brownell
  4 siblings, 1 reply; 19+ messages in thread
From: Laurent Pinchart @ 2008-08-14 12:15 UTC (permalink / raw)
  To: linuxppc-dev, avorontsov
  Cc: Greg Kroah-Hartman, linux-usb, David Brownell, Li Yang,
	linux-kernel, Timur Tabi

[-- Attachment #1: Type: text/plain, Size: 613 bytes --]

On Friday 08 August 2008, Anton Vorontsov wrote:
> Hi all,
> 
> Most patches that were needed to support QE USB Host were merged during
> 2.6.27 merge window, and only three more patches left over. Here they
> are.
> 
> David, could you bear with gpio_to_chip() exported function, just as
> a stopgap for a proper api?

I need gpio_to_chip() (or another 'proper API') as well for RTS/CTS based flow control in the CPM/CPM2 UART driver.

Best regards,

-- 
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-08 16:18 ` [PATCH 1/3] gpiolib: make gpio_to_chip() public Anton Vorontsov
@ 2008-08-14 14:04   ` Laurent Pinchart
  2008-08-14 14:14     ` Anton Vorontsov
  0 siblings, 1 reply; 19+ messages in thread
From: Laurent Pinchart @ 2008-08-14 14:04 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Anton Vorontsov, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

[-- Attachment #1: Type: text/plain, Size: 1867 bytes --]

On Friday 08 August 2008, Anton Vorontsov wrote:
> We'll need this function to write platform-specific hooks to deal
> with pin's dedicated functions. Quite obviously this will work only
> for the platforms with 1-to-1 GPIO to PIN mapping.
> 
> This is stopgap solution till we think out and implement a proper
> api (pinlib?).

How do you support reverting the GPIO mode to non-dedicated ?

> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
>  drivers/gpio/gpiolib.c     |    3 ++-
>  include/asm-generic/gpio.h |    1 +
>  2 files changed, 3 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index 8d29405..9536fa8 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -81,10 +81,11 @@ static void gpio_ensure_requested(struct gpio_desc *desc)
>  }
>  
>  /* caller holds gpio_lock *OR* gpio is marked as requested */
> -static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
> +struct gpio_chip *gpio_to_chip(unsigned gpio)
>  {
>  	return gpio_desc[gpio].chip;
>  }
> +EXPORT_SYMBOL_GPL(gpio_to_chip);
>  
>  /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
>  static int gpiochip_find_base(int ngpio)
> diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
> index 0f99ad3..d70ee45 100644
> --- a/include/asm-generic/gpio.h
> +++ b/include/asm-generic/gpio.h
> @@ -81,6 +81,7 @@ struct gpio_chip {
>  	unsigned		exported:1;
>  };
>  
> +extern struct gpio_chip *gpio_to_chip(unsigned gpio);
>  extern const char *gpiochip_is_requested(struct gpio_chip *chip,
>  			unsigned offset);
>  extern int __must_check gpiochip_reserve(int start, int ngpio);

-- 
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-14 14:04   ` Laurent Pinchart
@ 2008-08-14 14:14     ` Anton Vorontsov
  2008-08-14 14:45       ` Laurent Pinchart
  0 siblings, 1 reply; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-14 14:14 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

On Thu, Aug 14, 2008 at 04:04:18PM +0200, Laurent Pinchart wrote:
> On Friday 08 August 2008, Anton Vorontsov wrote:
> > We'll need this function to write platform-specific hooks to deal
> > with pin's dedicated functions. Quite obviously this will work only
> > for the platforms with 1-to-1 GPIO to PIN mapping.
> > 
> > This is stopgap solution till we think out and implement a proper
> > api (pinlib?).
> 
> How do you support reverting the GPIO mode to non-dedicated ?

As we always do with the GPIO API: gpio_direction_*() calls.

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-14 14:14     ` Anton Vorontsov
@ 2008-08-14 14:45       ` Laurent Pinchart
  2008-08-14 15:10         ` Anton Vorontsov
  0 siblings, 1 reply; 19+ messages in thread
From: Laurent Pinchart @ 2008-08-14 14:45 UTC (permalink / raw)
  To: avorontsov
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

[-- Attachment #1: Type: text/plain, Size: 980 bytes --]

On Thursday 14 August 2008, Anton Vorontsov wrote:
> On Thu, Aug 14, 2008 at 04:04:18PM +0200, Laurent Pinchart wrote:
> > On Friday 08 August 2008, Anton Vorontsov wrote:
> > > We'll need this function to write platform-specific hooks to deal
> > > with pin's dedicated functions. Quite obviously this will work only
> > > for the platforms with 1-to-1 GPIO to PIN mapping.
> > > 
> > > This is stopgap solution till we think out and implement a proper
> > > api (pinlib?).
> > 
> > How do you support reverting the GPIO mode to non-dedicated ?
> 
> As we always do with the GPIO API: gpio_direction_*() calls.

So the proper sequence to configure a pin in dedicated mode is to set the direction first (which will unset the dedicated mode bit) and then set dedicated mode (which will not touch the direction bit) ?

-- 
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-14 14:45       ` Laurent Pinchart
@ 2008-08-14 15:10         ` Anton Vorontsov
  2008-08-18 13:58           ` Laurent Pinchart
  0 siblings, 1 reply; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-14 15:10 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

On Thu, Aug 14, 2008 at 04:45:52PM +0200, Laurent Pinchart wrote:
> On Thursday 14 August 2008, Anton Vorontsov wrote:
> > On Thu, Aug 14, 2008 at 04:04:18PM +0200, Laurent Pinchart wrote:
> > > On Friday 08 August 2008, Anton Vorontsov wrote:
> > > > We'll need this function to write platform-specific hooks to deal
> > > > with pin's dedicated functions. Quite obviously this will work only
> > > > for the platforms with 1-to-1 GPIO to PIN mapping.
> > > > 
> > > > This is stopgap solution till we think out and implement a proper
> > > > api (pinlib?).
> > > 
> > > How do you support reverting the GPIO mode to non-dedicated ?
> > 
> > As we always do with the GPIO API: gpio_direction_*() calls.
> 
> So the proper sequence to configure a pin in dedicated mode is to set
> the direction first (which will unset the dedicated mode bit) and
> then set dedicated mode (which will not touch the direction bit) ?

Not exactly. But you can do this way, if you need to preserve
a direction. What I did is a bit different though.

qe_gpio_set_dedicated() actually just restores a mode that
firmware had set up, including direction (since direction could
be a part of dedicated configuration).

That is, upon GPIO controller registration, we save all registers,
then driver can set up a pin to a GPIO mode via standard API, and
then it can _revert_ a pin to a dedicated function via
qe_gpio_set_dedicated() call. Dedicated function is specified by
the firmware (or board file), we're just restoring it.

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-14 15:10         ` Anton Vorontsov
@ 2008-08-18 13:58           ` Laurent Pinchart
  2008-08-18 14:33             ` Anton Vorontsov
  0 siblings, 1 reply; 19+ messages in thread
From: Laurent Pinchart @ 2008-08-18 13:58 UTC (permalink / raw)
  To: avorontsov
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

[-- Attachment #1: Type: text/plain, Size: 2461 bytes --]

On Thursday 14 August 2008, Anton Vorontsov wrote:
> On Thu, Aug 14, 2008 at 04:45:52PM +0200, Laurent Pinchart wrote:
> > On Thursday 14 August 2008, Anton Vorontsov wrote:
> > > On Thu, Aug 14, 2008 at 04:04:18PM +0200, Laurent Pinchart wrote:
> > > > On Friday 08 August 2008, Anton Vorontsov wrote:
> > > > > We'll need this function to write platform-specific hooks to deal
> > > > > with pin's dedicated functions. Quite obviously this will work only
> > > > > for the platforms with 1-to-1 GPIO to PIN mapping.
> > > > > 
> > > > > This is stopgap solution till we think out and implement a proper
> > > > > api (pinlib?).
> > > > 
> > > > How do you support reverting the GPIO mode to non-dedicated ?
> > > 
> > > As we always do with the GPIO API: gpio_direction_*() calls.
> > 
> > So the proper sequence to configure a pin in dedicated mode is to set
> > the direction first (which will unset the dedicated mode bit) and
> > then set dedicated mode (which will not touch the direction bit) ?
> 
> Not exactly. But you can do this way, if you need to preserve
> a direction. What I did is a bit different though.
> 
> qe_gpio_set_dedicated() actually just restores a mode that
> firmware had set up, including direction (since direction could
> be a part of dedicated configuration).
> 
> That is, upon GPIO controller registration, we save all registers,
> then driver can set up a pin to a GPIO mode via standard API, and
> then it can _revert_ a pin to a dedicated function via
> qe_gpio_set_dedicated() call. Dedicated function is specified by
> the firmware (or board file), we're just restoring it.

The semantic of the set_dedicated() operation needs to be clearly defined then. I can live with this behaviour, but it might not be acceptable for everybody.

Your patch requires the firmware to set a pin in dedicated mode at bootup in order to be used later in dedicated mode. If, for some reason (driver not loaded, ...), no GPIO user shows up for that pin, it will stay configured in dedicated mode.

It might be better to set the PAR bit unconditionally in qe_gpio_set_dedicated() instead of restoring its value. That way the board initialization code could just set the DIR, DAT and ODR bits for dedicated mode but still configure the pin in GPIO mode.

-- 
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-18 13:58           ` Laurent Pinchart
@ 2008-08-18 14:33             ` Anton Vorontsov
  2008-08-18 14:44               ` Laurent Pinchart
  0 siblings, 1 reply; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-18 14:33 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

On Mon, Aug 18, 2008 at 03:58:46PM +0200, Laurent Pinchart wrote:
[...]
> > Not exactly. But you can do this way, if you need to preserve
> > a direction. What I did is a bit different though.
> > 
> > qe_gpio_set_dedicated() actually just restores a mode that
> > firmware had set up, including direction (since direction could
> > be a part of dedicated configuration).
> > 
> > That is, upon GPIO controller registration, we save all registers,
> > then driver can set up a pin to a GPIO mode via standard API, and
> > then it can _revert_ a pin to a dedicated function via
> > qe_gpio_set_dedicated() call. Dedicated function is specified by
> > the firmware (or board file), we're just restoring it.
> 
> The semantic of the set_dedicated() operation needs to be clearly
> defined then.

It is. We set up a dedicated function that firmware (or board file)
has configured.

> I can live with this behaviour, but it might not be
> acceptable for everybody.

For example?

> Your patch requires the firmware to set a pin in dedicated mode at
> bootup in order to be used later in dedicated mode.

Yes. On a PowerPC this is always true: firmware should set up PIO
config. Linux' board file could fixup the firmware though.

Another option would be to add some argument to the set_dedicated
call, thus the software could specify arbitrary dedicated
function (thus no need to save/restore anything). But this would
be SOC-model specific, thus no driver can use this argument anyway.

> If, for some
> reason (driver not loaded, ...), no GPIO user shows up for that
> pin, it will stay configured in dedicated mode.

Yes.

> It might be better to set the PAR bit unconditionally in

Why it might be better? That way you may set up wrong _GPIO_
mode, because you didn't set PAR bit (when PAR bit set
DIR/ODR/DAT bits are losing their meanings).

> qe_gpio_set_dedicated() instead of restoring its value. That way
> the board initialization code could just set the DIR, DAT and ODR
> bits for dedicated mode but still configure the pin in GPIO mode.

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-18 14:33             ` Anton Vorontsov
@ 2008-08-18 14:44               ` Laurent Pinchart
  2008-08-18 15:33                 ` Anton Vorontsov
  0 siblings, 1 reply; 19+ messages in thread
From: Laurent Pinchart @ 2008-08-18 14:44 UTC (permalink / raw)
  To: avorontsov
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

[-- Attachment #1: Type: text/plain, Size: 3210 bytes --]

On Monday 18 August 2008, Anton Vorontsov wrote:
> On Mon, Aug 18, 2008 at 03:58:46PM +0200, Laurent Pinchart wrote:
> [...]
> > > Not exactly. But you can do this way, if you need to preserve
> > > a direction. What I did is a bit different though.
> > > 
> > > qe_gpio_set_dedicated() actually just restores a mode that
> > > firmware had set up, including direction (since direction could
> > > be a part of dedicated configuration).
> > > 
> > > That is, upon GPIO controller registration, we save all registers,
> > > then driver can set up a pin to a GPIO mode via standard API, and
> > > then it can _revert_ a pin to a dedicated function via
> > > qe_gpio_set_dedicated() call. Dedicated function is specified by
> > > the firmware (or board file), we're just restoring it.
> > 
> > The semantic of the set_dedicated() operation needs to be clearly
> > defined then.
> 
> It is. We set up a dedicated function that firmware (or board file)
> has configured.

A comment in the source would help.

> > I can live with this behaviour, but it might not be
> > acceptable for everybody.
> 
> For example?
> 
> > Your patch requires the firmware to set a pin in dedicated mode at
> > bootup in order to be used later in dedicated mode.
> 
> Yes. On a PowerPC this is always true: firmware should set up PIO
> config. Linux' board file could fixup the firmware though.

That's not what I meant. What if the hardware requires to pin to be configured in GPIO mode with a fixed value until the SOC-specific driver that will drive the GPIO is loaded ? That's not possible with your API.

Until a SOC peripheral is initialized by its associated Linux driver, the behaviour of a GPIO pin in dedicated mode will be undefined. The firmware/board code will probably want to set the pin as a GPIO output with a fixed value until the driver initializes the hardware.

> Another option would be to add some argument to the set_dedicated
> call, thus the software could specify arbitrary dedicated
> function (thus no need to save/restore anything). But this would
> be SOC-model specific, thus no driver can use this argument anyway.

Drivers that require dedicated mode are SOC-specific anyway.

> > If, for some
> > reason (driver not loaded, ...), no GPIO user shows up for that
> > pin, it will stay configured in dedicated mode.
> 
> Yes.
> 
> > It might be better to set the PAR bit unconditionally in
> 
> Why it might be better?

Because, as explained a few lines down, the board initialization code will be able to configure a pin in a known state (PAR not set) at boot time until a driver requests the pin to be switched to dedicated mode.

> That way you may set up wrong _GPIO_ 
> mode, because you didn't set PAR bit (when PAR bit set
> DIR/ODR/DAT bits are losing their meanings).
> 
> > qe_gpio_set_dedicated() instead of restoring its value. That way
> > the board initialization code could just set the DIR, DAT and ODR
> > bits for dedicated mode but still configure the pin in GPIO mode.

-- 
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-18 14:44               ` Laurent Pinchart
@ 2008-08-18 15:33                 ` Anton Vorontsov
  2008-08-19  9:26                   ` Laurent Pinchart
  0 siblings, 1 reply; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-18 15:33 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

On Mon, Aug 18, 2008 at 04:44:36PM +0200, Laurent Pinchart wrote:
> On Monday 18 August 2008, Anton Vorontsov wrote:
> > On Mon, Aug 18, 2008 at 03:58:46PM +0200, Laurent Pinchart wrote:
> > [...]
> > > > Not exactly. But you can do this way, if you need to preserve
> > > > a direction. What I did is a bit different though.
> > > > 
> > > > qe_gpio_set_dedicated() actually just restores a mode that
> > > > firmware had set up, including direction (since direction could
> > > > be a part of dedicated configuration).
> > > > 
> > > > That is, upon GPIO controller registration, we save all registers,
> > > > then driver can set up a pin to a GPIO mode via standard API, and
> > > > then it can _revert_ a pin to a dedicated function via
> > > > qe_gpio_set_dedicated() call. Dedicated function is specified by
> > > > the firmware (or board file), we're just restoring it.
> > > 
> > > The semantic of the set_dedicated() operation needs to be clearly
> > > defined then.
> > 
> > It is. We set up a dedicated function that firmware (or board file)
> > has configured.
> 
> A comment in the source would help.
> 
> > > I can live with this behaviour, but it might not be
> > > acceptable for everybody.
> > 
> > For example?
> > 
> > > Your patch requires the firmware to set a pin in dedicated mode at
> > > bootup in order to be used later in dedicated mode.
> > 
> > Yes. On a PowerPC this is always true: firmware should set up PIO
> > config. Linux' board file could fixup the firmware though.
> 
> That's not what I meant. What if the hardware requires to pin to be
> configured in GPIO mode with a fixed value until the SOC-specific
> driver that will drive the GPIO is loaded ? That's not possible
> with your API.

Yes, this isn't possible with this API. Because you can do this
with standard GPIO API! ;-)

Just call gpio_direction_*() in the board file, before probing the
hardware.

> Until a SOC peripheral is initialized by its associated Linux driver,
> the behaviour of a GPIO pin in dedicated mode will be undefined.

Huh?! Then all current software is simply broken: we're setting pinmux
config _prior_ to controller initialization.

> The firmware/board code will probably want to set the pin as a GPIO
> output with a fixed value until the driver initializes the hardware.

Probably? Do you have any such hardware?

> > Another option would be to add some argument to the set_dedicated
> > call, thus the software could specify arbitrary dedicated
> > function (thus no need to save/restore anything). But this would
> > be SOC-model specific, thus no driver can use this argument anyway.
> 
> Drivers that require dedicated mode are SOC-specific anyway.

I didn't say "SOC-specific". I said "SOC-model specific", which
means that the driver would be not portable even across QE chips
(i.e. MPC8323 vs. MPC8360, you can assume that the "CLK12" function
is having same PAR/ODR/DAT/DIR bits).

> > > If, for some
> > > reason (driver not loaded, ...), no GPIO user shows up for that
> > > pin, it will stay configured in dedicated mode.
> > 
> > Yes.
> > 
> > > It might be better to set the PAR bit unconditionally in
> > 
> > Why it might be better?
> 
> Because, as explained a few lines down, the board initialization code
> will be able to configure a pin in a known state (PAR not set) at boot
> time until a driver requests the pin to be switched to dedicated mode.

You can do this as I described above. But prior to this, yes, you have
to configure the pins and let Linux save these values. There is no other
way to pass this information, unfortunately.

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-18 15:33                 ` Anton Vorontsov
@ 2008-08-19  9:26                   ` Laurent Pinchart
  2008-08-19 14:50                     ` Anton Vorontsov
  0 siblings, 1 reply; 19+ messages in thread
From: Laurent Pinchart @ 2008-08-19  9:26 UTC (permalink / raw)
  To: avorontsov
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

[-- Attachment #1: Type: text/plain, Size: 5545 bytes --]

On Monday 18 August 2008, Anton Vorontsov wrote:
> On Mon, Aug 18, 2008 at 04:44:36PM +0200, Laurent Pinchart wrote:
> > On Monday 18 August 2008, Anton Vorontsov wrote:
> > > On Mon, Aug 18, 2008 at 03:58:46PM +0200, Laurent Pinchart wrote:
> > > [...]
> > > > > Not exactly. But you can do this way, if you need to preserve
> > > > > a direction. What I did is a bit different though.
> > > > > 
> > > > > qe_gpio_set_dedicated() actually just restores a mode that
> > > > > firmware had set up, including direction (since direction could
> > > > > be a part of dedicated configuration).
> > > > > 
> > > > > That is, upon GPIO controller registration, we save all registers,
> > > > > then driver can set up a pin to a GPIO mode via standard API, and
> > > > > then it can _revert_ a pin to a dedicated function via
> > > > > qe_gpio_set_dedicated() call. Dedicated function is specified by
> > > > > the firmware (or board file), we're just restoring it.
> > > > 
> > > > The semantic of the set_dedicated() operation needs to be clearly
> > > > defined then.
> > > 
> > > It is. We set up a dedicated function that firmware (or board file)
> > > has configured.
> > 
> > A comment in the source would help.
> > 
> > > > I can live with this behaviour, but it might not be
> > > > acceptable for everybody.
> > > 
> > > For example?
> > > 
> > > > Your patch requires the firmware to set a pin in dedicated mode at
> > > > bootup in order to be used later in dedicated mode.
> > > 
> > > Yes. On a PowerPC this is always true: firmware should set up PIO
> > > config. Linux' board file could fixup the firmware though.
> > 
> > That's not what I meant. What if the hardware requires to pin to be
> > configured in GPIO mode with a fixed value until the SOC-specific
> > driver that will drive the GPIO is loaded ? That's not possible
> > with your API.
> 
> Yes, this isn't possible with this API. Because you can do this
> with standard GPIO API! ;-)
> 
> Just call gpio_direction_*() in the board file, before probing the
> hardware.

You'd have to do that after the GPIO drivers saved the pin registers.

> > Until a SOC peripheral is initialized by its associated Linux driver,
> > the behaviour of a GPIO pin in dedicated mode will be undefined.
> 
> Huh?! Then all current software is simply broken: we're setting pinmux
> config _prior_ to controller initialization.

Undefined behaviour doesn't mean broken behaviour :-) Signals coming from SOC peripherals are mostly undefined until the peripheral is initialized. For most hardware that doesn't matter much, but it might in some cases. For instance the state of the RTS signal on a serial port probably doesn't matter before we start serial communication, but some boards might require that RTS is deasserted before the controller is initialized. We can just ignore the issue for now and wait until it bites us.

> > The firmware/board code will probably want to set the pin as a GPIO
> > output with a fixed value until the driver initializes the hardware.
> 
> Probably? Do you have any such hardware?

Nope. I was referring to the hardware such as in the above example.

> > > Another option would be to add some argument to the set_dedicated
> > > call, thus the software could specify arbitrary dedicated
> > > function (thus no need to save/restore anything). But this would
> > > be SOC-model specific, thus no driver can use this argument anyway.
> > 
> > Drivers that require dedicated mode are SOC-specific anyway.
> 
> I didn't say "SOC-specific". I said "SOC-model specific", which
> means that the driver would be not portable even across QE chips
> (i.e. MPC8323 vs. MPC8360, you can assume that the "CLK12" function
> is having same PAR/ODR/DAT/DIR bits).

If I'm not mistaken, the PAR bit is used to select between GPIO and dedicated mode by definition. It should be safe to assume that setting a GPIO in dedicated mode requires the PAR bit to be set. But as I stated above, we can ignore that for now until a hardware use case comes up.

> > > > If, for some
> > > > reason (driver not loaded, ...), no GPIO user shows up for that
> > > > pin, it will stay configured in dedicated mode.
> > > 
> > > Yes.
> > > 
> > > > It might be better to set the PAR bit unconditionally in
> > > 
> > > Why it might be better?
> > 
> > Because, as explained a few lines down, the board initialization code
> > will be able to configure a pin in a known state (PAR not set) at boot
> > time until a driver requests the pin to be switched to dedicated mode.
> 
> You can do this as I described above. But prior to this, yes, you have
> to configure the pins and let Linux save these values. There is no other
> way to pass this information, unfortunately.

Ok.

I started implementing CPM2 dedicated mode support to be used in the CPM UART driver for RTS/CTS flow control, and soon realized there was a show stopper. The CPM UART driver is mostly CPM1/CPM2 agnostic. I can't use a function such as cpm2_gpio32_set_dedicated, as that wouldn't work on a CPM1 (at least not on all its ports). I could call the right function depending on which port the GPIO is on, but that's really hackish and would defeat the purpose of using GPIOs. What we really need there is a set_dedicated/set_option/set_whatever function in the GPIO API :-/

-- 
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

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

* Re: [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-08-19  9:26                   ` Laurent Pinchart
@ 2008-08-19 14:50                     ` Anton Vorontsov
  0 siblings, 0 replies; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-19 14:50 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linuxppc-dev, Greg Kroah-Hartman, linux-usb, David Brownell,
	Li Yang, linux-kernel, Timur Tabi

On Tue, Aug 19, 2008 at 11:26:28AM +0200, Laurent Pinchart wrote:
[...]
> > I didn't say "SOC-specific". I said "SOC-model specific", which
> > means that the driver would be not portable even across QE chips
> > (i.e. MPC8323 vs. MPC8360, you can assume that the "CLK12" function
> > is having same PAR/ODR/DAT/DIR bits).
> 
> If I'm not mistaken, the PAR bit is used to select between GPIO and
> dedicated mode by definition. It should be safe to assume that setting
> a GPIO in dedicated mode requires the PAR bit to be set. But as I
> stated above, we can ignore that for now until a hardware use case
> comes up.

One more thing: you're assuming that there is one par bit, but
there are two par bits for QE chips. Which one would you set in
set_dedicated()? ;-)

> > > > > If, for some
> > > > > reason (driver not loaded, ...), no GPIO user shows up for that
> > > > > pin, it will stay configured in dedicated mode.
> > > > 
> > > > Yes.
> > > > 
> > > > > It might be better to set the PAR bit unconditionally in
> > > > 
> > > > Why it might be better?
> > > 
> > > Because, as explained a few lines down, the board initialization code
> > > will be able to configure a pin in a known state (PAR not set) at boot
> > > time until a driver requests the pin to be switched to dedicated mode.
> > 
> > You can do this as I described above. But prior to this, yes, you have
> > to configure the pins and let Linux save these values. There is no other
> > way to pass this information, unfortunately.
> 
> Ok.
> 
> I started implementing CPM2 dedicated mode support to be used in the
> CPM UART driver for RTS/CTS flow control, and soon realized there was
> a show stopper. The CPM UART driver is mostly CPM1/CPM2 agnostic.
> I can't use a function such as cpm2_gpio32_set_dedicated, as that
> wouldn't work on a CPM1 (at least not on all its ports). I could call
> the right function depending on which port the GPIO is on, but that's
> really hackish and would defeat the purpose of using GPIOs.

You can easily refactor cpm gpio code so that you'll have
common cpm structure with platform-specific "GPIO API extension"
callbacks. Something like this:

/*
 * generic structure, does not know anything about cpm1/2/qe, or
 * ports width.
 */
struct cpm_gpio_chip {
	struct of_mm_gpio_chip mm_gc;
	spinlock_t lock;
	void (*set_dedicated)(unsigned int gpio);
};

struct cpm1_gpio16_chip {
	struct cpm_gpio_chip cpgc;

	/* shadowed data register to clear/set bits safely */
	u16 cpdata;
};

void cpm_gpio_set_dedicated(unsigned int gpio)
{
	struct gpio_chip *gc = gpio_to_chip(gpio);
	struct of_gpio_chip *ofgc = to_of_gpio_chip(gc);
	struct cpm_gpio_chip *cpgc = to_cpm_gpio_chip(ofgc);

	if (cpgc->set_dedicated)
		cpgc->set_dedicated(gc->base - gpio);
}

> What we
> really need there is a set_dedicated/set_option/set_whatever function
> in the GPIO API :-/

Won't happen. ;-)

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH 0/3] Patches to support QE USB Host Controller
  2008-08-08 23:26 ` [PATCH 0/3] Patches to support QE " Greg KH
@ 2008-08-26 15:49   ` Anton Vorontsov
  0 siblings, 0 replies; 19+ messages in thread
From: Anton Vorontsov @ 2008-08-26 15:49 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-usb, David Brownell, Li Yang, Timur Tabi, linuxppc-dev,
	linux-kernel

On Fri, Aug 08, 2008 at 04:26:35PM -0700, Greg KH wrote:
> On Fri, Aug 08, 2008 at 08:17:17PM +0400, Anton Vorontsov wrote:
> > Hi all,
> > 
> > Most patches that were needed to support QE USB Host were merged during
> > 2.6.27 merge window, and only three more patches left over. Here they
> > are.
> > 
> > David, could you bear with gpio_to_chip() exported function, just as
> > a stopgap for a proper api?
> 
> I'll hold off on the 3rd patch until david takes the first two.

David, are there any issues with the first patch in the series?

It would be great if you'll ack the first patch, so that I'll
respin the whole patchset with some minor fixes in the 3rd patch.

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH 0/3] Patches to support QE USB Host Controller
  2008-08-14 12:15 ` Laurent Pinchart
@ 2008-09-24 20:04   ` David Brownell
  0 siblings, 0 replies; 19+ messages in thread
From: David Brownell @ 2008-09-24 20:04 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linuxppc-dev, avorontsov, Greg Kroah-Hartman, linux-usb, Li Yang,
	linux-kernel, Timur Tabi

On Thursday 14 August 2008, Laurent Pinchart wrote:
> 
> > David, could you bear with gpio_to_chip() exported function, just as
> > a stopgap for a proper api?
> 
> I need gpio_to_chip() (or another 'proper API') as well for RTS/CTS
> based flow control in the CPM/CPM2 UART driver. 

I'l still say "proper".  This should be straightforward; along
the lines of

	struct qe_pin {
		struct ... *qe_ports;	/* includes gpio_chip */
		unsigned offset;
	};

And instead of having the driver look up a "gpio" for such
non-GPIO usage, have it call something that sets up a qe_pin.
All that infrastructure exists already...

Then drivers can use calls which mux the pin into its "normal"
mode (QE function of some kind), or into its "gpio" mode.

The gpio number would be gpio_chip->base + offset, and the
gpio_chip is visible -- in a fully typesafe manner! -- from
the qe_ports structure.

No type-unsafe interfaces.  No confusion between roles of a
given pin.  No hidden assumption there's only one kind of GPIO
(backed by QE ports).  And ... no need to change any core
structural assumptions of the GPIO framework.

- Dave


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

* [PATCH 1/3] gpiolib: make gpio_to_chip() public
  2008-09-18 15:16 Anton Vorontsov
@ 2008-09-18 15:17 ` Anton Vorontsov
  0 siblings, 0 replies; 19+ messages in thread
From: Anton Vorontsov @ 2008-09-18 15:17 UTC (permalink / raw)
  To: Andrew Morton
  Cc: David Brownell, Greg Kroah-Hartman, Kumar Gala, Timur Tabi,
	Li Yang, Laurent Pinchart, linuxppc-dev, linux-usb, linux-kernel

We'll need this function to write platform-specific hooks to deal
with pin's dedicated functions. Quite obviously this will work only
for the platforms with 1-to-1 GPIO to PIN mapping.

This is stopgap solution till we think out and implement a proper
api (pinlib?).

p.s. This patch actually exports gpio_desc and places gpio_to_chip
into the asm-generic/gpio.h as `static inline'. This is needed
to not cause function calls for this trivial translation.

Also, the patch does not export FLAG_*s... the names are too
generic, and nobody is using them outside of gpiolib.c.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/gpio/gpiolib.c     |   16 ++--------------
 include/asm-generic/gpio.h |   17 +++++++++++++++++
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 8d29405..fb36a90 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -40,9 +40,6 @@
  */
 static DEFINE_SPINLOCK(gpio_lock);
 
-struct gpio_desc {
-	struct gpio_chip	*chip;
-	unsigned long		flags;
 /* flag symbols are bit numbers */
 #define FLAG_REQUESTED	0
 #define FLAG_IS_OUT	1
@@ -50,11 +47,8 @@ struct gpio_desc {
 #define FLAG_EXPORT	3	/* protected by sysfs_lock */
 #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
 
-#ifdef CONFIG_DEBUG_FS
-	const char		*label;
-#endif
-};
-static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
+struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
+EXPORT_SYMBOL_GPL(gpio_desc);
 
 static inline void desc_set_label(struct gpio_desc *d, const char *label)
 {
@@ -80,12 +74,6 @@ static void gpio_ensure_requested(struct gpio_desc *desc)
 	}
 }
 
-/* caller holds gpio_lock *OR* gpio is marked as requested */
-static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
-{
-	return gpio_desc[gpio].chip;
-}
-
 /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
 static int gpiochip_find_base(int ngpio)
 {
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 0f99ad3..88962f0 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -81,6 +81,23 @@ struct gpio_chip {
 	unsigned		exported:1;
 };
 
+struct gpio_desc {
+	struct gpio_chip	*chip;
+	unsigned long		flags;
+
+#ifdef CONFIG_DEBUG_FS
+	const char		*label;
+#endif
+};
+
+extern struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
+
+/* caller holds gpio_lock *OR* gpio is marked as requested */
+static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
+{
+	return gpio_desc[gpio].chip;
+}
+
 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
 			unsigned offset);
 extern int __must_check gpiochip_reserve(int start, int ngpio);
-- 
1.5.6.3


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

end of thread, other threads:[~2008-09-24 20:48 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-08 16:17 [PATCH 0/3] Patches to support QE USB Host Controller Anton Vorontsov
2008-08-08 16:18 ` [PATCH 1/3] gpiolib: make gpio_to_chip() public Anton Vorontsov
2008-08-14 14:04   ` Laurent Pinchart
2008-08-14 14:14     ` Anton Vorontsov
2008-08-14 14:45       ` Laurent Pinchart
2008-08-14 15:10         ` Anton Vorontsov
2008-08-18 13:58           ` Laurent Pinchart
2008-08-18 14:33             ` Anton Vorontsov
2008-08-18 14:44               ` Laurent Pinchart
2008-08-18 15:33                 ` Anton Vorontsov
2008-08-19  9:26                   ` Laurent Pinchart
2008-08-19 14:50                     ` Anton Vorontsov
2008-08-08 16:18 ` [PATCH 2/3] powerpc/qe: new call to revert a gpio to a dedicated function Anton Vorontsov
2008-08-08 16:18 ` [PATCH 3/3] USB: driver for Freescale QUICC Engine USB Host Controller Anton Vorontsov
2008-08-08 23:26 ` [PATCH 0/3] Patches to support QE " Greg KH
2008-08-26 15:49   ` Anton Vorontsov
2008-08-14 12:15 ` Laurent Pinchart
2008-09-24 20:04   ` David Brownell
2008-09-18 15:16 Anton Vorontsov
2008-09-18 15:17 ` [PATCH 1/3] gpiolib: make gpio_to_chip() public Anton Vorontsov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).