linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/11] Patches needed to support QE USB Host Controller
@ 2008-02-03 17:08 Anton Vorontsov
  2008-02-03 17:09 ` [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API Anton Vorontsov
                   ` (10 more replies)
  0 siblings, 11 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:08 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell, linux-usb

Hi all,

These patches are needed to support "FHCI" host controller.

This includes:

- GPIO LIB support for PowerPC;
- GPIO LIB support for QUICC Engine;
- gpio_set_dedicated() addition to GPIO LIB;
- QE LIB additions: usb, gtm;
- FHCI driver;
- Support for MPC8360E-RDK as an example.

I have already posted GPIO LIB support for PowerPC/QE, but since
patches changed a bit, I thought it's good idea to take a chance
and repost them for the next RFC round.

Thanks,

p.s.

David, I'm Cc:ing you to this patchset (except qe_gtm, and qe_usb
patches -- nothing insteresting), to show the complete picture of
why we need additions to GPIO LIB.

Also will appreciate any comments regarding GPIO LIB support for
PowerPC/QE.

linux-usb Cc'ed only for FHCI driver patch.

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

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

* [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
@ 2008-02-03 17:09 ` Anton Vorontsov
  2008-02-03 21:17   ` David Brownell
  2008-02-03 17:09 ` [PATCH 02/11] [POWERPC] QE: split par_io_config_pin() Anton Vorontsov
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:09 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell

This patch implements support for the GPIO LIB API. Two calls
unimplemented though: irq_to_gpio and gpio_to_irq.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 Documentation/powerpc/booting-without-of.txt |   58 ++++++++
 arch/powerpc/Kconfig                         |    5 +
 arch/powerpc/kernel/Makefile                 |    1 +
 arch/powerpc/kernel/gpio.c                   |  200 ++++++++++++++++++++++++++
 include/asm-powerpc/gpio.h                   |  118 +++++++++++++++
 5 files changed, 382 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/kernel/gpio.c
 create mode 100644 include/asm-powerpc/gpio.h

diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index 7b30798..ce77b47 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -64,6 +64,10 @@ Table of Contents
     3) OpenPIC Interrupt Controllers
     4) ISA Interrupt Controllers
 
+  VIII - Specifying GPIO information for devices
+    1) gpios property
+    2) gpio-controller nodes
+
   Appendix A - Sample SOC node for MPC8540
 
 
@@ -2858,6 +2862,60 @@ encodings listed below:
 	2 =  high to low edge sensitive type enabled
 	3 =  low to high edge sensitive type enabled
 
+VIII - Specifying GPIO information for devices
+==============================================
+
+1) gpios property
+-----------------
+
+Nodes that makes use of GPIOs should define them using `gpios' property,
+format of which is: <&gpio-controller1-phandle gpio1-specifier
+		     &gpio-controller2-phandle gpio2-specifier
+		     ...>;
+
+Note that gpio-specifier length is controller dependent.
+
+gpio-specifier may encode: bank, pin position inside the bank,
+whether pin is open-drain and whether pin is logically inverted.
+
+Example of the node using GPIOs:
+
+	nand-flash@1,0 {
+		compatible = "stmicro,NAND512W3A2BN6E", "fsl,upm-nand";
+		reg = <1 0 1>;
+		width = <1>;
+		upm = "A";
+		upm-addr-offset = <16>;
+		upm-cmd-offset = <8>;
+		gpios = <&qe_pio_e 18 0>;
+		wait-pattern;
+		wait-write;
+	};
+
+In this example gpio-specifier is "18 0" and encodes GPIO pin number,
+and empty GPIO flags as accepted by the "qe_pio_e" gpio-controller.
+
+2) gpio-controller nodes
+------------------------
+
+Every GPIO controller node must have #gpio-cells property defined,
+this information will be used to translate gpio-specifiers.
+
+Example of two SOC GPIO banks defined as gpio-controller nodes:
+
+	qe_pio_a: gpio-controller@1400 {
+		#gpio-cells = <2>;
+		compatible = "fsl,qe-pario-bank";
+		reg = <0x1400 0x18>;
+		gpio-controller;
+	};
+
+	qe_pio_e: gpio-controller@1460 {
+		#gpio-cells = <2>;
+		compatible = "fsl,qe-pario-bank";
+		reg = <0x1460 0x18>;
+		gpio-controller;
+	};
 
 Appendix A - Sample SOC node for MPC8540
 ========================================
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 9c44af3..f9ed22b 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -81,6 +81,11 @@ config GENERIC_FIND_NEXT_BIT
 	bool
 	default y
 
+config GENERIC_GPIO
+	bool
+	help
+	  Generic GPIO API support
+
 config ARCH_NO_VIRT_TO_BUS
 	def_bool PPC64
 
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 58dbfef..349a52d 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_BOOTX_TEXT)	+= btext.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o
 obj-$(CONFIG_PPC_UDBG_16550)	+= legacy_serial.o udbg_16550.o
+obj-$(CONFIG_HAVE_GPIO_LIB)	+= gpio.o
 
 pci64-$(CONFIG_PPC64)		+= pci_dn.o isa-bridge.o
 obj-$(CONFIG_PCI)		+= pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \
diff --git a/arch/powerpc/kernel/gpio.c b/arch/powerpc/kernel/gpio.c
new file mode 100644
index 0000000..64c567d
--- /dev/null
+++ b/arch/powerpc/kernel/gpio.c
@@ -0,0 +1,200 @@
+/*
+ * OF helpers for the GPIO API
+ *
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * Copyright (c) 2007  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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <asm/prom.h>
+#include <asm/gpio.h>
+
+int of_get_gpio(struct device_node *np, int index)
+{
+	int ret = -EINVAL;
+	struct device_node *gc;
+	struct of_gpio_chip *of_gc = NULL;
+	int size;
+	const u32 *gpios;
+	u32 nr_cells;
+	int i;
+	const void *gpio_spec;
+	const u32 *gpio_cells;
+	int gpio_index = 0;
+
+	gpios = of_get_property(np, "gpios", &size);
+	if (!gpios) {
+		ret = -ENOENT;
+		goto err0;
+	}
+	nr_cells = size / sizeof(u32);
+
+	for (i = 0; i < nr_cells;) {
+		const phandle *gpio_phandle;
+
+		gpio_phandle = gpios + i;
+		gpio_spec = gpio_phandle + 1;
+
+		/* one cell hole in the gpios = <>; */
+		if (!*gpio_phandle) {
+			if (gpio_index == index)
+				return -ENOENT;
+			i++;
+			gpio_index++;
+			continue;
+		}
+
+		gc = of_find_node_by_phandle(*gpio_phandle);
+		if (!gc) {
+			pr_debug("%s: could not find phandle for gpios\n",
+				 np->full_name);
+			goto err0;
+		}
+
+		of_gc = gc->data;
+		if (!of_gc) {
+			pr_debug("%s: gpio controller %s isn't registered\n",
+				 np->full_name, gc->full_name);
+			goto err1;
+		}
+
+		gpio_cells = of_get_property(gc, "#gpio-cells", &size);
+		if (!gpio_cells || size != sizeof(*gpio_cells) ||
+				*gpio_cells != of_gc->gpio_cells) {
+			pr_debug("%s: wrong #gpio-cells for %s\n",
+				 np->full_name, gc->full_name);
+			goto err1;
+		}
+
+		/* Next phandle is at phandle cells + #gpio-cells */
+		i += sizeof(*gpio_phandle) / sizeof(u32) + *gpio_cells;
+		if (i >= nr_cells + 1) {
+			pr_debug("%s: insufficient gpio-spec length\n",
+				 np->full_name);
+			goto err1;
+		}
+
+		if (gpio_index == index)
+			break;
+
+		of_gc = NULL;
+		of_node_put(gc);
+		gpio_index++;
+	}
+
+	if (!of_gc) {
+		ret = -ENOENT;
+		goto err0;
+	}
+
+	ret = of_gc->xlate(of_gc, np, gpio_spec);
+	if (ret < 0)
+		goto err1;
+
+	ret += of_gc->gc.base;
+err1:
+	of_node_put(gc);
+err0:
+	pr_debug("%s exited with status %d\n", __func__, ret);
+	return ret;
+}
+EXPORT_SYMBOL(of_get_gpio);
+
+static int of_gpio_simple_xlate(struct of_gpio_chip *of_gc,
+				struct device_node *np,
+				const void *gpio_spec)
+{
+	const u32 *gpio = gpio_spec;
+
+	if (*gpio > of_gc->gc.ngpio)
+		return -EINVAL;
+
+	return *gpio;
+}
+
+static int of_get_gpiochip_base(struct device_node *np)
+{
+	struct device_node *gc = NULL;
+	int gpiochip_base = 0;
+
+	while ((gc = of_find_all_nodes(gc))) {
+		if (!of_get_property(gc, "gpio-controller", NULL))
+			continue;
+
+		if (gc != np) {
+			gpiochip_base += ARCH_OF_GPIOS_PER_CHIP;
+			continue;
+		}
+
+		of_node_put(gc);
+
+		if (gpiochip_base >= ARCH_OF_GPIOS_END)
+			return -ENOSPC;
+
+		return gpiochip_base;
+	}
+
+	return -ENOENT;
+}
+
+int of_mm_gpiochip_add(struct device_node *np,
+		       struct of_mm_gpio_chip *mm_gc)
+{
+	int ret = -ENOMEM;
+	struct of_gpio_chip *of_gc = &mm_gc->of_gc;
+
+	if (of_gc->gc.ngpio > ARCH_OF_GPIOS_PER_CHIP) {
+		ret = -ENOSPC;
+		goto err;
+	}
+
+	mm_gc->of_gc.gc.label = kstrdup(np->full_name, GFP_KERNEL);
+	if (!mm_gc->of_gc.gc.label)
+		goto err;
+
+	mm_gc->regs = of_iomap(np, 0);
+	if (!mm_gc->regs)
+		goto err1;
+
+	ret = of_get_gpiochip_base(np);
+	if (ret < 0)
+		goto err2;
+	mm_gc->of_gc.gc.base = ret;
+
+	if (!of_gc->xlate)
+		of_gc->xlate = of_gpio_simple_xlate;
+
+	if (mm_gc->save_regs)
+		mm_gc->save_regs(mm_gc);
+
+	np->data = &mm_gc->of_gc;
+
+	ret = gpiochip_add(&mm_gc->of_gc.gc);
+	if (ret)
+		goto err3;
+
+	/* We don't want to lose the node and its ->data */
+	of_node_get(np);
+
+	pr_debug("%s: registered as generic GPIO chip, base is %d\n",
+		 np->full_name, mm_gc->of_gc.gc.base);
+	return 0;
+err3:
+	np->data = NULL;
+err2:
+	iounmap(mm_gc->regs);
+err1:
+	kfree(mm_gc->of_gc.gc.label);
+err:
+	pr_err("%s: GPIO chip registration failed with status %d\n",
+	       np->full_name, ret);
+	return ret;
+}
+EXPORT_SYMBOL(of_mm_gpiochip_add);
diff --git a/include/asm-powerpc/gpio.h b/include/asm-powerpc/gpio.h
new file mode 100644
index 0000000..a97a93c
--- /dev/null
+++ b/include/asm-powerpc/gpio.h
@@ -0,0 +1,118 @@
+/*
+ * Generic GPIO API implementation for PowerPC.
+ *
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * Copyright (c) 2007  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 __ASM_POWERPC_GPIO_H
+#define __ASM_POWERPC_GPIO_H
+
+#include <asm-generic/gpio.h>
+
+#ifdef CONFIG_HAVE_GPIO_LIB
+
+#define ARCH_OF_GPIOS_PER_CHIP	32
+#define ARCH_OF_GPIOS_BASE	0
+#define ARCH_OF_GPIOS_END	(ARCH_OF_GPIOS_PER_CHIP * 7)
+#define ARCH_NON_OF_GPIOS_BASE	ARCH_OF_GPIOS_END
+#define ARCH_NON_OF_GPIOS_END	ARCH_NR_GPIOS
+
+#if ARCH_NON_OF_GPIOS_BASE >= ARCH_NON_OF_GPIOS_END
+#error "Default ARCH_NR_GPIOS isn't sufficient, define yours."
+#endif
+
+/*
+ * We don't (yet) implement inlined/rapid versions for on-chip gpios.
+ * Just call gpiolib.
+ */
+static inline int gpio_get_value(unsigned int gpio)
+{
+	return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+	__gpio_set_value(gpio, value);
+}
+
+/*
+ * Not implemented, yet.
+ */
+static inline int gpio_to_irq(unsigned int gpio)
+{
+	return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+	return -ENOSYS;
+}
+
+/*
+ * Generic OF GPIO chip
+ */
+struct of_gpio_chip {
+	struct gpio_chip gc;
+	int gpio_cells;
+	int (*xlate)(struct of_gpio_chip *of_gc, struct device_node *np,
+		     const void *gpio_spec);
+};
+
+#define to_of_gpio_chip(x) container_of(x, struct of_gpio_chip, gc)
+
+/*
+ * OF GPIO chip for memory mapped banks
+ */
+struct of_mm_gpio_chip {
+	struct of_gpio_chip of_gc;
+	void (*save_regs)(struct of_mm_gpio_chip *mm_gc);
+	void __iomem *regs;
+};
+
+#define to_of_mm_gpio_chip(x) container_of(to_of_gpio_chip(x), \
+					   struct of_mm_gpio_chip, of_gc)
+
+/**
+ * of_get_gpio - Get a GPIO number from the device tree to use with GPIO API
+ * @np:		device node to get GPIO from
+ * @index:	index of the GPIO
+ *
+ * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
+ * value on the error condition.
+ */
+extern int of_get_gpio(struct device_node *np, int index);
+
+/**
+ * of_mm_gpiochip_add - Add memory mapped GPIO chip (bank)
+ * @np:		device node of the GPIO chip
+ * @mm_gc:	pointer to the of_mm_gpio_chip allocated structure
+ *
+ * To use this function you should allocate and fill mm_gc with:
+ *
+ * 1) In the gpio_chip structure:
+ *    a) all the callbacks
+ *    b) ngpios (GPIOs per bank)
+ *
+ * 2) In the of_gpio_chip structure:
+ *    a) gpio_cells
+ *    b) xlate callback (optional)
+ *
+ * 3) In the of_mm_gpio_chip structure:
+ *    a) save_regs callback (optional)
+ *
+ * If succeeded, this function will map bank's memory and will
+ * do all necessary work for you. Then you'll able to use .regs
+ * to manage GPIOs from the callbacks.
+ */
+extern int of_mm_gpiochip_add(struct device_node *np,
+			      struct of_mm_gpio_chip *mm_gc);
+
+#endif /* CONFIG_HAVE_GPIO_LIB */
+
+#endif /* __ASM_POWERPC_GPIO_H */
-- 
1.5.2.2

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

* [PATCH 02/11] [POWERPC] QE: split par_io_config_pin()
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
  2008-02-03 17:09 ` [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API Anton Vorontsov
@ 2008-02-03 17:09 ` Anton Vorontsov
  2008-02-03 17:10 ` [PATCH 03/11] [POWERPC] QE: implement GPIO LIB API Anton Vorontsov
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:09 UTC (permalink / raw)
  To: linuxppc-dev

This patch splits par_io_config_pin so we can use it with GPIO LIB API.

Also add a comment regarding #ifdef CONFIG_PPC_85xx being legacy.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/sysdev/qe_lib/qe_io.c |   60 +++++++++++++++++++++++------------
 1 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index e53ea4d..aef893b 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -37,6 +37,10 @@ struct port_regs {
 	__be32	cppar1;		/* Pin assignment register */
 	__be32	cppar2;		/* Pin assignment register */
 #ifdef CONFIG_PPC_85xx
+	/*
+	 * This is needed for legacy support only, should go away,
+	 * because we started using per-bank gpio chips.
+	 */
 	u8	pad[8];
 #endif
 };
@@ -63,28 +67,29 @@ int par_io_init(struct device_node *np)
 	return 0;
 }
 
-int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
-		      int assignment, int has_irq)
+static void __par_io_config_pin(struct port_regs __iomem *par_io,
+				u8 pin, int dir, int open_drain,
+				int assignment, int has_irq)
 {
-	u32 pin_mask1bit, pin_mask2bits, new_mask2bits, tmp_val;
-
-	if (!par_io)
-		return -1;
+	u32 pin_mask1bit;
+	u32 pin_mask2bits;
+	u32 new_mask2bits;
+	u32 tmp_val;
 
 	/* calculate pin location for single and 2 bits information */
 	pin_mask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1)));
 
 	/* Set open drain, if required */
-	tmp_val = in_be32(&par_io[port].cpodr);
+	tmp_val = in_be32(&par_io->cpodr);
 	if (open_drain)
-		out_be32(&par_io[port].cpodr, pin_mask1bit | tmp_val);
+		out_be32(&par_io->cpodr, pin_mask1bit | tmp_val);
 	else
-		out_be32(&par_io[port].cpodr, ~pin_mask1bit & tmp_val);
+		out_be32(&par_io->cpodr, ~pin_mask1bit & tmp_val);
 
 	/* define direction */
 	tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
-		in_be32(&par_io[port].cpdir2) :
-		in_be32(&par_io[port].cpdir1);
+		in_be32(&par_io->cpdir2) :
+		in_be32(&par_io->cpdir1);
 
 	/* get all bits mask for 2 bit per port */
 	pin_mask2bits = (u32) (0x3 << (NUM_OF_PINS -
@@ -96,36 +101,49 @@ int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
 
 	/* clear and set 2 bits mask */
 	if (pin > (NUM_OF_PINS / 2) - 1) {
-		out_be32(&par_io[port].cpdir2,
+		out_be32(&par_io->cpdir2,
 			 ~pin_mask2bits & tmp_val);
 		tmp_val &= ~pin_mask2bits;
-		out_be32(&par_io[port].cpdir2, new_mask2bits | tmp_val);
+		out_be32(&par_io->cpdir2, new_mask2bits | tmp_val);
 	} else {
-		out_be32(&par_io[port].cpdir1,
+		out_be32(&par_io->cpdir1,
 			 ~pin_mask2bits & tmp_val);
 		tmp_val &= ~pin_mask2bits;
-		out_be32(&par_io[port].cpdir1, new_mask2bits | tmp_val);
+		out_be32(&par_io->cpdir1, new_mask2bits | tmp_val);
 	}
 	/* define pin assignment */
 	tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
-		in_be32(&par_io[port].cppar2) :
-		in_be32(&par_io[port].cppar1);
+		in_be32(&par_io->cppar2) :
+		in_be32(&par_io->cppar1);
 
 	new_mask2bits = (u32) (assignment << (NUM_OF_PINS -
 			(pin % (NUM_OF_PINS / 2) + 1) * 2));
 	/* clear and set 2 bits mask */
 	if (pin > (NUM_OF_PINS / 2) - 1) {
-		out_be32(&par_io[port].cppar2,
+		out_be32(&par_io->cppar2,
 			 ~pin_mask2bits & tmp_val);
 		tmp_val &= ~pin_mask2bits;
-		out_be32(&par_io[port].cppar2, new_mask2bits | tmp_val);
+		out_be32(&par_io->cppar2, new_mask2bits | tmp_val);
 	} else {
-		out_be32(&par_io[port].cppar1,
+		out_be32(&par_io->cppar1,
 			 ~pin_mask2bits & tmp_val);
 		tmp_val &= ~pin_mask2bits;
-		out_be32(&par_io[port].cppar1, new_mask2bits | tmp_val);
+		out_be32(&par_io->cppar1, new_mask2bits | tmp_val);
 	}
+}
+
+/*
+ * This is "legacy" function that takes port number as an argument
+ * instead of pointer to the appropriate bank.
+ */
+int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
+		      int assignment, int has_irq)
+{
+	if (!par_io || port >= num_par_io_ports)
+		return -EINVAL;
 
+	__par_io_config_pin(&par_io[port], pin, dir, open_drain, assignment,
+			    has_irq);
 	return 0;
 }
 EXPORT_SYMBOL(par_io_config_pin);
-- 
1.5.2.2

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

* [PATCH 03/11] [POWERPC] QE: implement GPIO LIB API
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
  2008-02-03 17:09 ` [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API Anton Vorontsov
  2008-02-03 17:09 ` [PATCH 02/11] [POWERPC] QE: split par_io_config_pin() Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-03 17:10 ` [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine Anton Vorontsov
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 Documentation/powerpc/booting-without-of.txt |   32 ++++---
 arch/powerpc/platforms/Kconfig               |    2 +
 arch/powerpc/sysdev/qe_lib/qe_io.c           |  133 ++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 12 deletions(-)

diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index ce77b47..c5b6004 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1701,24 +1701,32 @@ platforms are moved over to use the flattened-device-tree model.
    information.
 
    Required properties:
-   - device_type : should be "par_io".
+   - #gpio-cells : should be "2".
+   - compatible : should be "fsl,qe-pario-bank"
    - reg : offset to the register set and its length.
-   - num-ports : number of Parallel I/O ports
+   - gpio-controller : node to identify gpio controllers.
 
-   Example:
-	par_io@1400 {
-		reg = <1400 100>;
-		#address-cells = <1>;
-		#size-cells = <0>;
-		device_type = "par_io";
-		num-ports = <7>;
-		ucc_pin@01 {
-			......
-		};
+   For example, two QE Par I/O banks:
+	qe_pio_a: gpio-controller@1400 {
+		#gpio-cells = <2>;
+		compatible = "fsl,qe-pario-bank";
+		reg = <0x1400 0x18>;
+		gpio-controller;
+	};
 
+	qe_pio_e: gpio-controller@1460 {
+		#gpio-cells = <2>;
+		compatible = "fsl,qe-pario-bank";
+		reg = <0x1460 0x18>;
+		gpio-controller;
+	};
 
    vi) Pin configuration nodes
 
+   NOTE: pin configuration nodes are obsolete. Usually, their existance
+         is an evidence of the firmware shortcomings. Such fixups are
+         better handled by the Linux board file, not the device tree.
+
    Required properties:
    - linux,phandle : phandle of this node; likely referenced by a QE
      device.
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index fdce10c..50199cf 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -271,6 +271,8 @@ config QUICC_ENGINE
 	bool
 	select PPC_LIB_RHEAP
 	select CRC32
+	select GENERIC_GPIO
+	select HAVE_GPIO_LIB
 	help
 	  The QUICC Engine (QE) is a new generation of communications
 	  coprocessors on Freescale embedded CPUs (akin to CPM in older chips).
diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index aef893b..dffb44a 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -20,9 +20,11 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/ioport.h>
+#include <linux/spinlock.h>
 
 #include <asm/io.h>
 #include <asm/prom.h>
+#include <asm/gpio.h>
 #include <sysdev/fsl_soc.h>
 
 #undef DEBUG
@@ -213,6 +215,137 @@ int par_io_of_config(struct device_node *np)
 }
 EXPORT_SYMBOL(par_io_of_config);
 
+/*
+ * GPIO LIB API implementation
+ */
+
+struct qe_gpio_chip {
+	struct of_mm_gpio_chip mm_gc;
+	spinlock_t lock;
+
+	/* shadowed data register to clear/set bits safely */
+	u32 cpdata;
+};
+
+#define to_qe_gpio_chip(x) container_of(x, struct qe_gpio_chip, mm_gc)
+
+static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+	struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+	struct port_regs __iomem *regs = mm_gc->regs;
+
+	qe_gc->cpdata = in_be32(&regs->cpdata);
+}
+
+static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct port_regs __iomem *regs = mm_gc->regs;
+	u32 pin_mask;
+
+	/* calculate pin location */
+	pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - gpio));
+
+	return !!(in_be32(&regs->cpdata) & pin_mask);
+}
+
+static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	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 port_regs __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+	u32 pin_mask = 1 << (NUM_OF_PINS - 1 - gpio);
+
+	spin_lock_irqsave(&qe_gc->lock, flags);
+
+	if (val)
+		qe_gc->cpdata |= pin_mask;
+	else
+		qe_gc->cpdata &= ~pin_mask;
+
+	out_be32(&regs->cpdata, qe_gc->cpdata);
+
+	spin_unlock_irqrestore(&qe_gc->lock, flags);
+}
+
+static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int 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);
+	unsigned long flags;
+
+	spin_lock_irqsave(&qe_gc->lock, flags);
+
+	__par_io_config_pin(mm_gc->regs, gpio, 2, 0, 0, 0);
+
+	spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+	return 0;
+}
+
+static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	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);
+	unsigned long flags;
+
+	spin_lock_irqsave(&qe_gc->lock, flags);
+
+	__par_io_config_pin(mm_gc->regs, gpio, 1, 0, 0, 0);
+
+	spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+	qe_gpio_set(gc, gpio, val);
+
+	return 0;
+}
+
+static int __init qe_add_gpiochips(void)
+{
+	int ret;
+	struct device_node *np;
+
+	for_each_compatible_node(np, NULL, "fsl,qe-pario-bank") {
+		struct qe_gpio_chip *qe_gc;
+		struct of_mm_gpio_chip *mm_gc;
+		struct of_gpio_chip *of_gc;
+		struct gpio_chip *gc;
+
+		qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL);
+		if (!qe_gc) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		spin_lock_init(&qe_gc->lock);
+
+		mm_gc = &qe_gc->mm_gc;
+		of_gc = &mm_gc->of_gc;
+		gc = &of_gc->gc;
+
+		mm_gc->save_regs = qe_gpio_save_regs;
+		of_gc->gpio_cells = 2;
+		gc->ngpio = NUM_OF_PINS;
+		gc->direction_input = qe_gpio_dir_in;
+		gc->direction_output = qe_gpio_dir_out;
+		gc->get = qe_gpio_get;
+		gc->set = qe_gpio_set;
+		gc->set_dedicated = qe_gpio_set_dedicated;
+
+		ret = of_mm_gpiochip_add(np, mm_gc);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+err:
+	pr_err("%s: registration failed with status %d\n", np->full_name, ret);
+	of_node_put(np);
+	return ret;
+}
+arch_initcall(qe_add_gpiochips);
+
 #ifdef DEBUG
 static void dump_par_io(void)
 {
-- 
1.5.2.2

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

* [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (2 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 03/11] [POWERPC] QE: implement GPIO LIB API Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-03 21:22   ` David Brownell
  2008-02-03 17:10 ` [PATCH 05/11] [POWERPC] qe_lib: support for gpio_set_dedicated Anton Vorontsov
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell

This routine sets dedicated functions of the GPIO pin.

---

Hello David,

Yes, I did read Documentation/gpio.txt's last chapter. :-)

...that says:

  One of the biggest things these conventions omit is pin multiplexing,
  since this is highly chip-specific and nonportable.

Let me counter: "chip-specific" is a weak argument, IMO. Imagine some
GPIO controller that can't do inputs, or outputs. First one will be
still suitable for gpio_leds, second one will be suitable for gpio_keys.

Or... gpio_to_irq/irq_to_gpio. More than chip-specific, isn't it?
Some GPIO controllers might provide interrupt sources, some might
not.

Or let's put it completely different way: IRQs, they are
chip specific too, some of them can't do EDGE_FALLING or
EDGE_RISING. But these flags still exists for the IRQs,
and drivers use them.

The same for GPIO pin multiplexing: some drivers aren't aware of
GPIO multiplexing, but some are.

With the device tree/OpenFirmware environment it's quite easy
to pass "dedicated functions" flags to the drivers. Platform device
drivers also may accept functions via platform data (or better yet
via IORESOURCE_GPIO and its flags -- yet to be implemented, of course).

Today, there is a driver for the Freescale USB Host Controller, that
needs switching some pins to GPIOs for short period, and then back to
the dedicated functions...

So, down below is the proposal patch: gpio_set_dedicated() routine.

There are other options, tough. But I don't like them:

- Don't use GPIO API for that driver. Bad idea, this driver
  completely fits in the current GPIO use cases, except
  set_dedicated()...

- Export "gpio_chips", thus we can implement arch-specific functions.
  Bad idea, we'll smear "GPIO LIB" across the whole kernel.

- Implement gpio_chip->free() and gpio_chip->request() callbacks,
  so controllers could restore pin's functions in the ->free().

  Then drivers could do:
  gpio_request();
  gpio_direction_...();
  ...do some GPIO work...
  gpio_free(); /* and controller will restore dedicated function */

  Well, this is viable option. But expensive and it isn't good
  idea to release gpios when driver already probed (think someone
  might request it meantime). Still viable for my case, though.

  Oh, there is no way to pass "func" argument also.

- Another option?

Thanks!

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

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d8db2f8..de6e765 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -440,6 +440,19 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
 
+int gpio_set_dedicated(unsigned gpio, int func)
+{
+	struct gpio_chip	*chip;
+
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(gpio);
+	if (chip->set_dedicated)
+		return chip->set_dedicated(chip, gpio - chip->base, func);
+
+	return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(gpio_set_dedicated);
+
 
 #ifdef CONFIG_DEBUG_FS
 
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 806b86c..cfbeea8 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -61,6 +61,8 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	int			(*set_dedicated)(struct gpio_chip *chip,
+						unsigned offset, int func);
 	void			(*dbg_show)(struct seq_file *s,
 						struct gpio_chip *chip);
 	int			base;
@@ -88,6 +90,7 @@ extern int gpio_direction_output(unsigned gpio, int value);
 extern int gpio_get_value_cansleep(unsigned gpio);
 extern void gpio_set_value_cansleep(unsigned gpio, int value);
 
+extern int gpio_set_dedicated(unsigned gpio, int func);
 
 /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
  * the GPIO is constant and refers to some always-present controller,
-- 
1.5.2.2

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

* [PATCH 05/11] [POWERPC] qe_lib: support for gpio_set_dedicated
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (3 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-04 17:38   ` Timur Tabi
  2008-02-03 17:10 ` [PATCH 06/11] [POWERPC] qe_lib: implement qe_muram_offset Anton Vorontsov
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell

So far we just restore pre-set dedicated function of the pin.
No need for anything else, so far.

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

diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index dffb44a..abe02e0 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -225,6 +225,9 @@ struct qe_gpio_chip {
 
 	/* shadowed data register to clear/set bits safely */
 	u32 cpdata;
+
+	/* saved_regs used to restore dedicated functions */
+	struct port_regs saved_regs;
 };
 
 #define to_qe_gpio_chip(x) container_of(x, struct qe_gpio_chip, mm_gc)
@@ -235,6 +238,12 @@ static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
 	struct port_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)
@@ -301,6 +310,43 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
 	return 0;
 }
 
+static int qe_gpio_set_dedicated(struct gpio_chip *gc, unsigned int gpio,
+				 int func)
+{
+	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 port_regs __iomem *regs = mm_gc->regs;
+	struct port_regs *sregs = &qe_gc->saved_regs;
+	unsigned long flags;
+	u32 mask1 = 1 << (NUM_OF_PINS - (gpio + 1));
+	u32 mask2 = 0x3 << (NUM_OF_PINS - (gpio % (NUM_OF_PINS / 2) + 1) * 2);
+	bool second_reg = gpio > (NUM_OF_PINS / 2) - 1;
+
+	spin_lock_irqsave(&qe_gc->lock, flags);
+
+	if (second_reg)
+		clrsetbits_be32(&regs->cpdir2, mask2, sregs->cpdir2 & mask2);
+	else
+		clrsetbits_be32(&regs->cpdir1, mask2, sregs->cpdir1 & mask2);
+
+	if (second_reg)
+		clrsetbits_be32(&regs->cppar2, mask2, sregs->cppar2 & mask2);
+	else
+		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;
+}
+
 static int __init qe_add_gpiochips(void)
 {
 	int ret;
-- 
1.5.2.2

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

* [PATCH 06/11] [POWERPC] qe_lib: implement qe_muram_offset
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (4 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 05/11] [POWERPC] qe_lib: support for gpio_set_dedicated Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-03 17:10 ` [PATCH 07/11] [POWERPC] qe_lib: export qe_get_brg_clk Anton Vorontsov
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev

qe_muram_offset is the reverse of the qe_muram_addr.

Also, move qe_muram_addr to the qe.h header.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/sysdev/qe_lib/qe.c |    6 ------
 include/asm-powerpc/qe.h        |   11 ++++++++++-
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 5ef844d..0757def 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -415,12 +415,6 @@ void qe_muram_dump(void)
 }
 EXPORT_SYMBOL(qe_muram_dump);
 
-void *qe_muram_addr(unsigned long offset)
-{
-	return (void *)&qe_immr->muram[offset];
-}
-EXPORT_SYMBOL(qe_muram_addr);
-
 /* The maximum number of RISCs we support */
 #define MAX_QE_RISC     2
 
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index 430dc77..df20f73 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -92,7 +92,16 @@ unsigned long qe_muram_alloc(int size, int align);
 int qe_muram_free(unsigned long offset);
 unsigned long qe_muram_alloc_fixed(unsigned long offset, int size);
 void qe_muram_dump(void);
-void *qe_muram_addr(unsigned long offset);
+
+static inline void *qe_muram_addr(unsigned long offset)
+{
+	return (void *)&qe_immr->muram[offset];
+}
+
+static inline unsigned long qe_muram_offset(void *addr)
+{
+	return addr - (void *)qe_immr->muram;
+}
 
 /* Structure that defines QE firmware binary files.
  *
-- 
1.5.2.2

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

* [PATCH 07/11] [POWERPC] qe_lib: export qe_get_brg_clk
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (5 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 06/11] [POWERPC] qe_lib: implement qe_muram_offset Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-03 17:10 ` [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support Anton Vorontsov
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev

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

diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 0757def..a0d0ec0 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -155,7 +155,7 @@ EXPORT_SYMBOL(qe_issue_cmd);
  */
 static unsigned int brg_clk = 0;
 
-unsigned int get_brg_clk(void)
+unsigned int qe_get_brg_clk(void)
 {
 	struct device_node *qe;
 	unsigned int size;
@@ -180,6 +180,7 @@ unsigned int get_brg_clk(void)
 
 	return brg_clk;
 }
+EXPORT_SYMBOL(qe_get_brg_clk);
 
 /* Program the BRG to the given sampling rate and multiplier
  *
@@ -197,7 +198,7 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
 	if ((brg < QE_BRG1) || (brg > QE_BRG16))
 		return -EINVAL;
 
-	divisor = get_brg_clk() / (rate * multiplier);
+	divisor = qe_get_brg_clk() / (rate * multiplier);
 
 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
 		div16 = QE_BRGC_DIV16;
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index df20f73..3487f50 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -85,6 +85,7 @@ extern int par_io_data_set(u8 port, u8 pin, u8 val);
 /* QE internal API */
 int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input);
 enum qe_clock qe_clock_source(const char *source);
+unsigned int qe_get_brg_clk(void);
 int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier);
 int qe_get_snum(void);
 void qe_put_snum(u8 snum);
-- 
1.5.2.2

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

* [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (6 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 07/11] [POWERPC] qe_lib: export qe_get_brg_clk Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-04 20:30   ` Scott Wood
  2008-02-03 17:10 ` [PATCH 09/11] [POWERPC] qe_lib: add support for QE USB Anton Vorontsov
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev

GTM stands for General-purpose Timers Module and able to generate
timer{1,2,3,4} interrupts.

There are several limitations in this support:
1. Cascaded (32 bit) timers unimplemented (1-2, 3-4).
   This is straightforward to implement when needed, two timers should
   be marked as "requested" and configured as appropriate.
2. Super-cascaded (64 bit) timers unimplemented (1-2-3-4).
   This is also straightforward to implement when needed, all timers
   should be marked as "requested" and configured as appropriate.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/sysdev/qe_lib/Kconfig  |    4 +
 arch/powerpc/sysdev/qe_lib/Makefile |    1 +
 arch/powerpc/sysdev/qe_lib/gtm.c    |  204 +++++++++++++++++++++++++++++++++++
 include/asm-powerpc/immap_qe.h      |    7 +-
 include/asm-powerpc/qe.h            |   22 ++++
 5 files changed, 236 insertions(+), 2 deletions(-)
 create mode 100644 arch/powerpc/sysdev/qe_lib/gtm.c

diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig
index adc6621..3966151 100644
--- a/arch/powerpc/sysdev/qe_lib/Kconfig
+++ b/arch/powerpc/sysdev/qe_lib/Kconfig
@@ -20,3 +20,7 @@ config UCC
 	bool
 	default y if UCC_FAST || UCC_SLOW
 
+config QE_GTM
+	bool
+	help
+	  QE General-Purpose Timers Module support
diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/sysdev/qe_lib/Makefile
index 874fe1a..3297a52 100644
--- a/arch/powerpc/sysdev/qe_lib/Makefile
+++ b/arch/powerpc/sysdev/qe_lib/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o
 obj-$(CONFIG_UCC)	+= ucc.o
 obj-$(CONFIG_UCC_SLOW)	+= ucc_slow.o
 obj-$(CONFIG_UCC_FAST)	+= ucc_fast.o
+obj-$(CONFIG_QE_GTM)	+= gtm.o
diff --git a/arch/powerpc/sysdev/qe_lib/gtm.c b/arch/powerpc/sysdev/qe_lib/gtm.c
new file mode 100644
index 0000000..8f5b422
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/gtm.c
@@ -0,0 +1,204 @@
+/*
+ * QE General-Purpose Timers Module
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <asm/immap_qe.h>
+#include <asm/qe.h>
+
+struct gtm_timer {
+	unsigned int irq;
+	bool requested;
+
+	u8 __iomem *gtcfr;
+	u16 __iomem *gtmdr;
+	u16 __iomem *gtpsr;
+	u16 __iomem *gtcnr;
+	u16 __iomem *gtrfr;
+	u16 __iomem *gtevr;
+};
+
+static struct gtm_timer timers[4];
+static struct qe_timers __iomem *qet;
+static spinlock_t gtm_lock = __SPIN_LOCK_UNLOCKED(gtm_lock);
+
+static int __init qe_init_gtm(void)
+{
+	struct device_node *gtm;
+	int i;
+
+	gtm = of_find_compatible_node(NULL, NULL, "fsl,qe-gtm");
+	if (!gtm)
+		return -ENODEV;
+
+	for (i = 0; i < 3; i++) {
+		int ret;
+		struct resource irq;
+
+		ret = of_irq_to_resource(gtm, i, &irq);
+		if (ret == NO_IRQ) {
+			pr_err("%s: not enough interrupts specified\n",
+			       gtm->full_name);
+			of_node_put(gtm);
+			return -EINVAL;
+		}
+		timers[i].irq = irq.start;
+	}
+
+	qet = of_iomap(gtm, 0);
+	of_node_put(gtm);
+	if (!qet) {
+		pr_err("%s: unable to iomap registers\n", gtm->full_name);
+		return -EINVAL;
+	}
+
+	/*
+	 * Yeah, I don't like this either, but timers' registers a bit messed,
+	 * so we have to provide shortcuts to write timer independent code.
+	 */
+	timers[0].gtcfr = &qet->gtcfr1;
+	timers[0].gtmdr = &qet->gtmdr1;
+	timers[0].gtpsr = &qet->gtpsr1;
+	timers[0].gtcnr = &qet->gtcnr1;
+	timers[0].gtrfr = &qet->gtrfr1;
+	timers[0].gtevr = &qet->gtevr1;
+
+	timers[1].gtcfr = &qet->gtcfr1;
+	timers[1].gtmdr = &qet->gtmdr2;
+	timers[1].gtpsr = &qet->gtpsr2;
+	timers[1].gtcnr = &qet->gtcnr2;
+	timers[1].gtrfr = &qet->gtrfr2;
+	timers[1].gtevr = &qet->gtevr2;
+
+	timers[2].gtcfr = &qet->gtcfr2;
+	timers[2].gtmdr = &qet->gtmdr3;
+	timers[2].gtpsr = &qet->gtpsr3;
+	timers[2].gtcnr = &qet->gtcnr3;
+	timers[2].gtrfr = &qet->gtrfr3;
+	timers[2].gtevr = &qet->gtevr3;
+
+	timers[3].gtcfr = &qet->gtcfr2;
+	timers[3].gtmdr = &qet->gtmdr4;
+	timers[3].gtpsr = &qet->gtpsr4;
+	timers[3].gtcnr = &qet->gtcnr4;
+	timers[3].gtrfr = &qet->gtrfr4;
+	timers[3].gtevr = &qet->gtevr4;
+
+	return 0;
+}
+arch_initcall(qe_init_gtm);
+
+int qe_get_timer(int width, unsigned int *irq)
+{
+	int i;
+
+	BUG_ON(!irq);
+	if (!qet)
+		return -ENODEV;
+	if (width != 16)
+		return -ENOSYS;
+
+	spin_lock_irq(&gtm_lock);
+
+	for (i = 0; i < 3; i++) {
+		if (!timers[i].requested) {
+			timers[i].requested = true;
+			*irq = timers[i].irq;
+			return i;
+		}
+	}
+
+	spin_unlock_irq(&gtm_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(qe_get_timer);
+
+void qe_put_timer(int num)
+{
+	spin_lock_irq(&gtm_lock);
+
+	timers[num].requested = false;
+
+	spin_unlock_irq(&gtm_lock);
+}
+EXPORT_SYMBOL(qe_put_timer);
+
+int qe_reset_ref_timer_16(int num, unsigned int hz, u16 ref)
+{
+	struct gtm_timer *tmr = &timers[num];
+	unsigned long flags;
+	unsigned int prescaler;
+	u8 psr;
+	u8 sps;
+
+	prescaler = qe_get_brg_clk() / hz;
+
+	/*
+	 * We have two 8 bit prescalers -- primary and secondary (psr, sps),
+	 * so total prescale value is (psr + 1) * (sps + 1).
+	 */
+	if (prescaler > 256 * 256) {
+		return -EINVAL;
+	} else if (prescaler > 256) {
+		psr = 256 - 1;
+		sps = prescaler / 256 - 1;
+	} else {
+		psr = prescaler - 1;
+		sps = 1 - 1;
+	}
+
+	spin_lock_irqsave(&gtm_lock, flags);
+
+	/*
+	 * Properly reset timers: stop, reset, set up prescalers, reference
+	 * value and clear event register.
+	 */
+	clrsetbits_8(tmr->gtcfr, ~(QE_GTCFR_STP(num) | QE_GTCFR_RST(num)),
+				 QE_GTCFR_STP(num) | QE_GTCFR_RST(num));
+
+	setbits8(tmr->gtcfr, QE_GTCFR_STP(num));
+
+	out_be16(tmr->gtpsr, psr);
+	setbits16(tmr->gtmdr, QE_GTMDR_SPS(sps) | QE_GTMDR_ICLK_QERF |
+			      QE_GTMDR_ORI);
+	out_be16(tmr->gtcnr, 0);
+	out_be16(tmr->gtrfr, ref);
+	out_be16(tmr->gtevr, 0xFFFF);
+
+	/* Let it be. */
+	clrbits8(tmr->gtcfr, QE_GTCFR_STP(num));
+
+	spin_unlock_irqrestore(&gtm_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(qe_reset_ref_timer_16);
+
+void qe_stop_timer(int num)
+{
+	struct gtm_timer *tmr = &timers[num];
+	unsigned long flags;
+
+	spin_lock_irqsave(&gtm_lock, flags);
+
+	setbits8(tmr->gtcfr, QE_GTCFR_STP(num));
+
+	spin_unlock_irqrestore(&gtm_lock, flags);
+}
+EXPORT_SYMBOL(qe_stop_timer);
diff --git a/include/asm-powerpc/immap_qe.h b/include/asm-powerpc/immap_qe.h
index 82a4526..cfa0d86 100644
--- a/include/asm-powerpc/immap_qe.h
+++ b/include/asm-powerpc/immap_qe.h
@@ -128,8 +128,11 @@ struct qe_timers {
 	__be16	gtevr2;		/* Timer 2 event register */
 	__be16	gtevr3;		/* Timer 3 event register */
 	__be16	gtevr4;		/* Timer 4 event register */
-	__be16	gtps;		/* Timer 1 prescale register */
-	u8 res2[0x46];
+	__be16	gtpsr1;		/* Timer 1 prescale register */
+	__be16	gtpsr2;		/* Timer 2 prescale register */
+	__be16	gtpsr3;		/* Timer 3 prescale register */
+	__be16	gtpsr4;		/* Timer 4 prescale register */
+	u8 res2[0x40];
 } __attribute__ ((packed));
 
 /* BRG */
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index 3487f50..3664aaa 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -153,6 +153,12 @@ struct qe_firmware_info {
 /* Upload a firmware to the QE */
 int qe_upload_firmware(const struct qe_firmware *firmware);
 
+/* QE GTM */
+extern int qe_get_timer(int width, unsigned int *irq);
+extern void qe_put_timer(int num);
+extern int qe_reset_ref_timer_16(int num, unsigned int hz, u16 ref);
+extern void qe_stop_timer(int num);
+
 /* Obtain information on the uploaded firmware */
 struct qe_firmware_info *qe_get_firmware_info(void);
 
@@ -255,6 +261,11 @@ enum comm_dir {
 #define QE_CMXGCR_MII_ENET_MNG_SHIFT	12
 #define QE_CMXGCR_USBCS			0x0000000f
 
+#define QE_CMXGCR_TIMERCS	0x00300000
+#define QE_CMXGCR_TIMERCS_CLK11	0x00000000
+#define QE_CMXGCR_TIMERCS_CLK12	0x00100000
+#define QE_CMXGCR_TIMERCS_BRG11	0x00300000
+
 /* QE CECR Commands.
 */
 #define QE_CR_FLG			0x00010000
@@ -367,6 +378,17 @@ enum comm_dir {
 #define QE_GTCFR1_STP1	0x02
 #define QE_GTCFR1_RST1	0x01
 
+#define QE_GTCFR_STP(x)	((x) & 1 ? 1 << 5 : 1 << 1)
+#define QE_GTCFR_RST(x)	((x) & 1 ? 1 << 4 : 1 << 0)
+
+#define QE_GTMDR_ICLK_MASK	(3 << 1)
+#define QE_GTMDR_ICLK_ICAS	(0 << 1)
+#define QE_GTMDR_ICLK_QERF	(1 << 1)
+#define QE_GTMDR_ICLK_SLGO	(2 << 1)
+#define QE_GTMDR_ORI		(1 << 4)
+#define QE_GTMDR_SPS_MASK	(0xFF << 8)
+#define QE_GTMDR_SPS(x)		((x) << 8)
+
 /* SDMA registers */
 #define QE_SDSR_BER1	0x02000000
 #define QE_SDSR_BER2	0x01000000
-- 
1.5.2.2

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

* [PATCH 09/11] [POWERPC] qe_lib: add support for QE USB
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (7 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-03 17:10 ` [PATCH 10/11] [POWERPC] mpc8360erdk: add FHCI USB support Anton Vorontsov
  2008-02-03 17:11 ` [PATCH 11/11] [RFC USB POWERPC] Freescale QUICC Engine USB Host Controller Anton Vorontsov
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev

I believe QE USB clocks routing is qe_lib authority, so usb.c
created. Also, now cmxgcr needs its own lock.

This patch also fixes QE_USB_RESTART_TX command definition.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/sysdev/qe_lib/Kconfig  |    6 ++++
 arch/powerpc/sysdev/qe_lib/Makefile |    1 +
 arch/powerpc/sysdev/qe_lib/ucc.c    |    7 ++--
 arch/powerpc/sysdev/qe_lib/usb.c    |   57 +++++++++++++++++++++++++++++++++++
 include/asm-powerpc/qe.h            |   18 ++++++++++-
 5 files changed, 85 insertions(+), 4 deletions(-)
 create mode 100644 arch/powerpc/sysdev/qe_lib/usb.c

diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig
index 3966151..5c400e1 100644
--- a/arch/powerpc/sysdev/qe_lib/Kconfig
+++ b/arch/powerpc/sysdev/qe_lib/Kconfig
@@ -24,3 +24,9 @@ config QE_GTM
 	bool
 	help
 	  QE General-Purpose Timers Module support
+
+config QE_USB
+	bool
+	default y if USB_FHCI_HCD
+	help
+	  QE USB Host Controller support
diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/sysdev/qe_lib/Makefile
index 3297a52..c666a59 100644
--- a/arch/powerpc/sysdev/qe_lib/Makefile
+++ b/arch/powerpc/sysdev/qe_lib/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_UCC)	+= ucc.o
 obj-$(CONFIG_UCC_SLOW)	+= ucc_slow.o
 obj-$(CONFIG_UCC_FAST)	+= ucc_fast.o
 obj-$(CONFIG_QE_GTM)	+= gtm.o
+obj-$(CONFIG_QE_USB)	+= usb.o
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 0e348d9..d3c7f5a 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -26,7 +26,8 @@
 #include <asm/qe.h>
 #include <asm/ucc.h>
 
-static DEFINE_SPINLOCK(ucc_lock);
+DEFINE_SPINLOCK(cmxgcr_lock);
+EXPORT_SYMBOL(cmxgcr_lock);
 
 int ucc_set_qe_mux_mii_mng(unsigned int ucc_num)
 {
@@ -35,10 +36,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num)
 	if (ucc_num > UCC_MAX_NUM - 1)
 		return -EINVAL;
 
-	spin_lock_irqsave(&ucc_lock, flags);
+	spin_lock_irqsave(&cmxgcr_lock, flags);
 	clrsetbits_be32(&qe_immr->qmx.cmxgcr, QE_CMXGCR_MII_ENET_MNG,
 		ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT);
-	spin_unlock_irqrestore(&ucc_lock, flags);
+	spin_unlock_irqrestore(&cmxgcr_lock, flags);
 
 	return 0;
 }
diff --git a/arch/powerpc/sysdev/qe_lib/usb.c b/arch/powerpc/sysdev/qe_lib/usb.c
new file mode 100644
index 0000000..60ce676
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/usb.c
@@ -0,0 +1,57 @@
+/*
+ * QE USB routines
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ *               Shlomi Gridish <gridish@freescale.com>
+ *               Jerry Huang <Chang-Ming.Huang@freescale.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <asm/immap_qe.h>
+#include <asm/qe.h>
+
+int qe_usb_clock_set(enum qe_clock clk, int rate)
+{
+	struct qe_mux __iomem *mux = &qe_immr->qmx;
+	unsigned long flags;
+	const bool is_brg = clk < QE_CLK1;
+	u32 val;
+
+	switch (clk) {
+	case QE_CLK3:  val = QE_CMXGCR_USBCS_CLK3;  break;
+	case QE_CLK5:  val = QE_CMXGCR_USBCS_CLK5;  break;
+	case QE_CLK7:  val = QE_CMXGCR_USBCS_CLK7;  break;
+	case QE_CLK9:  val = QE_CMXGCR_USBCS_CLK9;  break;
+	case QE_CLK13: val = QE_CMXGCR_USBCS_CLK13; break;
+	case QE_CLK17: val = QE_CMXGCR_USBCS_CLK17; break;
+	case QE_CLK19: val = QE_CMXGCR_USBCS_CLK19; break;
+	case QE_CLK21: val = QE_CMXGCR_USBCS_CLK21; break;
+	case QE_BRG9:  val = QE_CMXGCR_USBCS_BRG9;  break;
+	case QE_BRG10: val = QE_CMXGCR_USBCS_BRG10; break;
+	default:
+		pr_err("%s: requested unknown clock %d\n", __func__, clk);
+		return -EINVAL;
+	}
+
+	if (is_brg)
+		qe_setbrg(clk, rate, 1);
+
+	spin_lock_irqsave(&cmxgcr_lock, flags);
+
+	clrsetbits_be32(&mux->cmxgcr, QE_CMXGCR_USBCS, val);
+
+	spin_unlock_irqrestore(&cmxgcr_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(qe_usb_clock_set);
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index 3664aaa..dad2a8b 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -16,6 +16,7 @@
 #define _ASM_POWERPC_QE_H
 #ifdef __KERNEL__
 
+#include <linux/spinlock.h>
 #include <asm/immap_qe.h>
 
 #define QE_NUM_OF_SNUM	28
@@ -74,6 +75,8 @@ enum qe_clock {
 	QE_CLK_DUMMY
 };
 
+extern spinlock_t cmxgcr_lock;
+
 /* Export QE common operations */
 extern void qe_reset(void);
 extern int par_io_init(struct device_node *np);
@@ -159,6 +162,9 @@ extern void qe_put_timer(int num);
 extern int qe_reset_ref_timer_16(int num, unsigned int hz, u16 ref);
 extern void qe_stop_timer(int num);
 
+/* QE USB */
+int qe_usb_clock_set(enum qe_clock clk, int rate);
+
 /* Obtain information on the uploaded firmware */
 struct qe_firmware_info *qe_get_firmware_info(void);
 
@@ -260,6 +266,16 @@ enum comm_dir {
 #define QE_CMXGCR_MII_ENET_MNG		0x00007000
 #define QE_CMXGCR_MII_ENET_MNG_SHIFT	12
 #define QE_CMXGCR_USBCS			0x0000000f
+#define QE_CMXGCR_USBCS_CLK3		0x1
+#define QE_CMXGCR_USBCS_CLK5		0x2
+#define QE_CMXGCR_USBCS_CLK7		0x3
+#define QE_CMXGCR_USBCS_CLK9		0x4
+#define QE_CMXGCR_USBCS_CLK13		0x5
+#define QE_CMXGCR_USBCS_CLK17		0x6
+#define QE_CMXGCR_USBCS_CLK19		0x7
+#define QE_CMXGCR_USBCS_CLK21		0x8
+#define QE_CMXGCR_USBCS_BRG9		0x9
+#define QE_CMXGCR_USBCS_BRG10		0xa
 
 #define QE_CMXGCR_TIMERCS	0x00300000
 #define QE_CMXGCR_TIMERCS_CLK11	0x00000000
@@ -294,7 +310,7 @@ enum comm_dir {
 #define QE_HPAC_START_TX		0x0000060b
 #define QE_HPAC_START_RX		0x0000070b
 #define QE_USB_STOP_TX			0x0000000a
-#define QE_USB_RESTART_TX		0x0000000b
+#define QE_USB_RESTART_TX		0x0000000c
 #define QE_QMC_STOP_TX			0x0000000c
 #define QE_QMC_STOP_RX			0x0000000d
 #define QE_SS7_SU_FIL_RESET		0x0000000e
-- 
1.5.2.2

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

* [PATCH 10/11] [POWERPC] mpc8360erdk: add FHCI USB support
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (8 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 09/11] [POWERPC] qe_lib: add support for QE USB Anton Vorontsov
@ 2008-02-03 17:10 ` Anton Vorontsov
  2008-02-03 17:11 ` [PATCH 11/11] [RFC USB POWERPC] Freescale QUICC Engine USB Host Controller Anton Vorontsov
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:10 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell

This consists of: usb node, gtm node and gpio-controllers of the
B and E banks.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/boot/dts/mpc836x_rdk.dts |   42 +++++++++++++++++++++++++++++++++
 1 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts
index cc56338..f770ac1 100644
--- a/arch/powerpc/boot/dts/mpc836x_rdk.dts
+++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts
@@ -133,6 +133,20 @@
 			reg = <0x700 0x100>;
 		};
 
+		qe_pio_b: gpio-controller@1418 {
+			#gpio-cells = <2>;
+			compatible = "fsl,qe-pario-bank";
+			reg = <0x1418 0x18>;
+			gpio-controller;
+		};
+
+		qe_pio_e: gpio-controller@1460 {
+			#gpio-cells = <2>;
+			compatible = "fsl,qe-pario-bank";
+			reg = <0x1460 0x18>;
+			gpio-controller;
+		};
+
 		qe@100000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
@@ -156,6 +170,34 @@
 						     "fsl,cpm-muram-data";
 					reg = <0 0xc000>;
 				};
+
+				usb-pram@8b00 {
+					compatible = "fsl,qe-muram-usb-pram";
+					reg = <0x8b00 0x100>;
+				};
+			};
+
+			gtm@440 {
+				compatible = "fsl,qe-gtm";
+				reg = <0x440 0x40>;
+				interrupts = <12 13 14 15>;
+				interrupt-parent = <&qeic>;
+			};
+
+			usb@6c0 {
+				compatible = "fsl,usb-fhci";
+				reg = <0x6c0 0x40>;
+				interrupts = <11>;
+				interrupt-parent = <&qeic>;
+				fullspeed-clock = "clk21";
+				gpios = <&qe_pio_b  2 0 /* USBOE */
+					 &qe_pio_b  3 0 /* USBTP */
+					 &qe_pio_b  8 0 /* USBTN */
+					 &qe_pio_b  9 0 /* USBRP */
+					 &qe_pio_b 11 0 /* USBRN */
+					 &qe_pio_e 20 0 /* SPEED */
+					 &qe_pio_e 21 0 /* SUSPN */>;
+				mode = "host";
 			};
 
 			spi@4c0 {
-- 
1.5.2.2

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

* [PATCH 11/11] [RFC USB POWERPC] Freescale QUICC Engine USB Host Controller
  2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
                   ` (9 preceding siblings ...)
  2008-02-03 17:10 ` [PATCH 10/11] [POWERPC] mpc8360erdk: add FHCI USB support Anton Vorontsov
@ 2008-02-03 17:11 ` Anton Vorontsov
  10 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 17:11 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: David Brownell, linux-usb

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

Hi all,

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

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


Will appreciate any comments,

Thanks,

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

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

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

* Re: [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API
  2008-02-03 17:09 ` [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API Anton Vorontsov
@ 2008-02-03 21:17   ` David Brownell
  2008-02-04 13:19     ` Anton Vorontsov
  0 siblings, 1 reply; 22+ messages in thread
From: David Brownell @ 2008-02-03 21:17 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linuxppc-dev

On Sunday 03 February 2008, Anton Vorontsov wrote:
> This patch implements support for the GPIO LIB API. Two calls
> unimplemented though: irq_to_gpio and gpio_to_irq.

Also gpio_cansleep().


> +Example of two SOC GPIO banks defined as gpio-controller nodes:
> +
> +	qe_pio_a: gpio-controller@1400 {
> +		#gpio-cells = <2>;
> +		compatible = "fsl,qe-pario-bank";
> +		reg = <0x1400 0x18>;
> +		gpio-controller;
> +	};
> +
> +	qe_pio_e: gpio-controller@1460 {
> +		#gpio-cells = <2>;
> +		compatible = "fsl,qe-pario-bank";
> +		reg = <0x1460 0x18>;
> +		gpio-controller;
> +	};

Let me suggest another example to provide:   an I2C GPIO expander
such as a pcf8574 or pca9354 (both eight bit expanders).  The SOC
case is probably the easiest to cover.


> +#define ARCH_OF_GPIOS_PER_CHIP 32

Well, ARCH_OF_* is clearly not the now-removed ARCH_GPIOS_PER_CHIP,
but I still suggest moving away from that concept.  


> +static inline int gpio_get_value(unsigned int gpio)
> +{
> +	return __gpio_get_value(gpio);
> +}
> +
> +static inline void gpio_set_value(unsigned int gpio, int value)
> +{
> +	__gpio_set_value(gpio, value);
> +}

static inline int gpio_cansleep(unsigned int gpio)
{
	return __gpio_cansleep(gpio);
}


> +static inline int irq_to_gpio(unsigned int irq)
> +{
> +	return -ENOSYS;

Minor nit:  "-EINVAL" would be better here, since the argument
is constrained to have been returned from gpio_to_irq(), and
as you wrote that call there can be no such values.

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

* Re: [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine
  2008-02-03 17:10 ` [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine Anton Vorontsov
@ 2008-02-03 21:22   ` David Brownell
  2008-02-03 23:32     ` Anton Vorontsov
  0 siblings, 1 reply; 22+ messages in thread
From: David Brownell @ 2008-02-03 21:22 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linuxppc-dev

On Sunday 03 February 2008, Anton Vorontsov wrote:
> This routine sets dedicated functions of the GPIO pin.
> 
> ---
> 
> Hello David,
> 
> Yes, I did read Documentation/gpio.txt's last chapter. :-)
> 
> ...that says:
> 
>   One of the biggest things these conventions omit is pin multiplexing,
>   since this is highly chip-specific and nonportable.
> 
> Let me counter: "chip-specific" is a weak argument, IMO.

Then focus on "nonportable concepts" instead.  :)


Note that "pin" != "GPIO".  There are platforms that let one GPIO
be routed to any of several pins/balls; and let a given pin/ball
be multiplexed to support any of several GPIOs.  (Annoying because
it's so error prone, but true.  I'll call that the "OMAP1" pinmux
issue, since that's where I first ran into it.)

Likewise, not all pins with multiplexed functionality include GPIO
as one of those functions.

So when you assume that a GPIO number can uniquely specify a pin for
use in function multiplexing ... you're stressing a "nonportable"
aspect of this issue.

Ditto when you assume that the multiplexing is on a per-pin basis,
rather than affecting a defined group of pins.  (More common, and
less annoying, than the OMAP1 issue.)

(And that doesn't even touch issues like configurable drive strength,
pullups, pulldowns, and so on.)


> Imagine some 
> GPIO controller that can't do inputs, or outputs. First one will be
> still suitable for gpio_leds, second one will be suitable for gpio_keys.

The interface easily handles input-only and output-only GPIOs.
Make the relevant gpio_direction_*() methods fail those requests.


> Or... gpio_to_irq/irq_to_gpio. More than chip-specific, isn't it?
> Some GPIO controllers might provide interrupt sources, some might
> not.

Right now there isn't a generic hookup between GPIOs and IRQs;
that's all very platform-specific.  For example, maybe it doesn't
use the genirq framework ... and even if it does, there's a huge
hole where "removing irq_chip and its irqs" fits.

It's easy enough for most platforms to arrange that a particular
range of GPIO numbers maps to a particular set of IRQ numbers
through the IRQ chaining mechanism.


> Or let's put it completely different way: IRQs, they are
> chip specific too, some of them can't do EDGE_FALLING or
> EDGE_RISING. But these flags still exists for the IRQs,
> and drivers use them.

Sure; though as a rule, any driver that specifies trigger modes
is platform-specific when it does so.  Plus, very few would know
what to do when they learn that the EDGE_FALLING mode they asked
for is not supported by the underlying hardware.


> The same for GPIO pin multiplexing: some drivers aren't aware of
> GPIO multiplexing, but some are.

And if they are aware, that's platform-specific code.  So there can
be no issue in requiring use of platform-specific calls for that.

Example, when a given function can come out on either of two pins
(like MMC0.CMD on some chips), and those pins vary between models
of that SOC family ... the driver will either know highly nonportable
details about each chip, or will punt to external code that needs to
accommodate both board-specific wiring choices and chip-specific
differences in ballout.  (In fact, that particular situation is
mostly handled by board-specific setup code, not a driver.)


 > So, down below is the proposal patch: gpio_set_dedicated() routine.
> 
> There are other options, tough. But I don't like them:
> 
> ...
> 
> - Another option?

The way it's usually done:  platform-specific code to handle those
platform-specific pin configuration issues.


> +int gpio_set_dedicated(unsigned gpio, int func)

It's not required that a pin/ball identifier have a one-to-one mapping
to "gpio" numbers, or that all pins/balls have "gpio" as one of the
possible functions.  So if there were a cross-platform call like this,
I'd want to see such it reference not a "gpio" but a "pin".

And for that matter, "dedicated" is inaccurate.  It's not uncommon
that even after setting a pin function among the N options available
for that pin on that platform, it still be usable as a GPIO input.

Of course, the "function" codes are extremely chip-specific ... and
some platforms would want to include things like pullups, pulldowns,
and so forth as part of pin configuration.

If you want to pursue this problem, don't couple it to GPIOs.

- Dave


> +{
> +	struct gpio_chip	*chip;
> +
> +	might_sleep_if(extra_checks);
> +	chip = gpio_to_chip(gpio);
> +	if (chip->set_dedicated)
> +		return chip->set_dedicated(chip, gpio - chip->base, func);
> +
> +	return -ENOSYS;
> +}
> +EXPORT_SYMBOL_GPL(gpio_set_dedicated);

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

* Re: [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine
  2008-02-03 21:22   ` David Brownell
@ 2008-02-03 23:32     ` Anton Vorontsov
  2008-02-15  4:36       ` David Brownell
  0 siblings, 1 reply; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-03 23:32 UTC (permalink / raw)
  To: David Brownell; +Cc: linuxppc-dev

On Sun, Feb 03, 2008 at 01:22:08PM -0800, David Brownell wrote:
[...]
> >   One of the biggest things these conventions omit is pin multiplexing,
> >   since this is highly chip-specific and nonportable.
> > 
> > Let me counter: "chip-specific" is a weak argument, IMO.
> 
> Then focus on "nonportable concepts" instead.  :)
> 
> 
> Note that "pin" != "GPIO".  There are platforms that let one GPIO
> be routed to any of several pins/balls; and let a given pin/ball
> be multiplexed to support any of several GPIOs.  (Annoying because
> it's so error prone, but true.  I'll call that the "OMAP1" pinmux
> issue, since that's where I first ran into it.)
> 
> Likewise, not all pins with multiplexed functionality include GPIO
> as one of those functions.
> 
> So when you assume that a GPIO number can uniquely specify a pin for
> use in function multiplexing ... you're stressing a "nonportable"
> aspect of this issue.
> 
> Ditto when you assume that the multiplexing is on a per-pin basis,
> rather than affecting a defined group of pins.  (More common, and
> less annoying, than the OMAP1 issue.)
> 
> (And that doesn't even touch issues like configurable drive strength,
> pullups, pulldowns, and so on.)

This is all true, of course.

> 
> > Imagine some 
> > GPIO controller that can't do inputs, or outputs. First one will be
> > still suitable for gpio_leds, second one will be suitable for gpio_keys.
> 
> The interface easily handles input-only and output-only GPIOs.
> Make the relevant gpio_direction_*() methods fail those requests.

The point was: GPIOs could be "input only" but you still have
"output" callback, and that doesn't troubles anybody. Not sure
why set_dedicated() such a big issue then, it could fail too! :-)

We're talking about General Purpose IOs, right? They're general
enough to support not only input/output stuff. And we want some
API for these General Purpose IOs. GPIO LIB is the best candidate
and we can implement such API at almost no cost, few lines of code.

> > Or... gpio_to_irq/irq_to_gpio. More than chip-specific, isn't it?
> > Some GPIO controllers might provide interrupt sources, some might
> > not.
> 
> Right now there isn't a generic hookup between GPIOs and IRQs;
> that's all very platform-specific.  For example, maybe it doesn't
> use the genirq framework ... and even if it does, there's a huge
> hole where "removing irq_chip and its irqs" fits.
> 
> It's easy enough for most platforms to arrange that a particular
> range of GPIO numbers maps to a particular set of IRQ numbers
> through the IRQ chaining mechanism.

He-he. Actually, I have a patch that adds "to_irq" callback
to GPIO LIB. :-) But I just didn't find time yet to cleanup
the "user" of that addition (ARM-based "samcop" companion chip).

Briefly: gpio<->irq mapping there isn't "flat". It is messed
all around. GPIO 1 is IRQ 12, GPIO 2 if IRQ 45 and so on... no
common pattern. So, to support gpio_to_irq() we have to either:

1. change the mappings of the IRQs, to match GPIOs.
or
2. implement to_irq() callback (way easier).

> > Or let's put it completely different way: IRQs, they are
> > chip specific too, some of them can't do EDGE_FALLING or
> > EDGE_RISING. But these flags still exists for the IRQs,
> > and drivers use them.
> 
> Sure; though as a rule, any driver that specifies trigger modes
> is platform-specific when it does so.  Plus, very few would know
> what to do when they learn that the EDGE_FALLING mode they asked
> for is not supported by the underlying hardware.

Exactly. The question is how much "platforms" that driver could
support. When driver that using EDGE_* works for >= 2 platforms,
then this flag is worthwhile already.

> > The same for GPIO pin multiplexing: some drivers aren't aware of
> > GPIO multiplexing, but some are.
> 
> And if they are aware, that's platform-specific code.  So there can
> be no issue in requiring use of platform-specific calls for that.
> 
> Example, when a given function can come out on either of two pins
> (like MMC0.CMD on some chips), and those pins vary between models
> of that SOC family ... the driver will either know highly nonportable
> details about each chip,

No. Driver don't have to know chip details. _Platform code_ is passing
these details to the driver (via platform data or OF device tree
properties). Then driver is using these details absolutely blindly,
without knowing their meaning.

> or will punt to external code that needs to
> accommodate both board-specific wiring choices and chip-specific
> differences in ballout.  (In fact, that particular situation is
> mostly handled by board-specific setup code, not a driver.)

The point is that driver needs non-static GPIO configuration.
In some cases you can't just configure gpio's dedicated functions
at the start-up and use it for the whole driver's lifecycle. Driver
wants to switch between "pin as GPIO" and "pin as dedicated function".

I think GPIO LIB should provide this ability to do so. See below
though.

>  > So, down below is the proposal patch: gpio_set_dedicated() routine.
> > 
> > There are other options, tough. But I don't like them:
> > 
> > ...
> > 
> > - Another option?
> 
> The way it's usually done:  platform-specific code to handle those
> platform-specific pin configuration issues.

There is a problem. Driver could be cross-platform. For example,
we have platforms with "CPM1", "CPM2" and "QE" on-chip gpio
controllers.

One kernel could run on all these platforms (well, not now, but
there are some plans). Mostly, they share drivers (well, actually no,
but there are some plans :-), i.e. differences between CPM and QE
peripherals usually minor enough to write a driver which is able to
work on both.

At the same time, difference between CPM and QE gpio controllers
are drastic, so we can't use "qe_set_dedicated" for both (assuming
that we don't want "if (is_qe) ... else ..." scheme).

So, imagine driver X which is doing:

qe_set_dedicated(pin);

It will be tied to the "QE" platform. But if we'll do

gpio_set_dedicated(pin);

Then underlaying gpio controller would handle that call,
be it CPM or QE. See? GPIO LIB is a simple dispatcher.

And despite special _set_dedicated() function, this driver
actually does _use_ pins as GPIOs. And as dedicated functions.
And as GPIOs. The same pins, but at the different times.


Okay. FHCI is probably not the case of the cross-platform driver,
so for now we can forget about cross-platform usage. Ok, let's
imagine I'll not use GPIO LIB for these "special" pins. But there
are other pins, usual GPIOs. So, to use gpio api and some special
qe_set_dedicated() we want them to play nicely with each other,
locking e.t.c. Will you agree to export "chips" so we can write
GPIO LIB "platform extensions", like

qe_set_dedicated(unsigned int value_that_we_got_from_gpio_request)
{
	qe_chip = container_of(gpio_to_chip(...), struct qe_chip, chip);
	spin_lock_irqsave(&qe_chip->lock, flags);
	...do qe-specific work...
}

?

> > +int gpio_set_dedicated(unsigned gpio, int func)
> 
> It's not required that a pin/ball identifier have a one-to-one mapping
> to "gpio" numbers, or that all pins/balls have "gpio" as one of the
> possible functions.  So if there were a cross-platform call like this,
> I'd want to see such it reference not a "gpio" but a "pin".
> 
> And for that matter, "dedicated" is inaccurate.  It's not uncommon
> that even after setting a pin function among the N options available
> for that pin on that platform, it still be usable as a GPIO input.

Yes. But again, there are lots of cases when GPIOs are special
just as IRQs being special. And we still want to use them through
single interface: genirq, gpiolib.

> Of course, the "function" codes are extremely chip-specific ... and
> some platforms would want to include things like pullups, pulldowns,
> and so forth as part of pin configuration.
> 
> If you want to pursue this problem, don't couple it to GPIOs.

Um... couple it to what then?..


Thanks,

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

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

* Re: [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API
  2008-02-03 21:17   ` David Brownell
@ 2008-02-04 13:19     ` Anton Vorontsov
  0 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-04 13:19 UTC (permalink / raw)
  To: David Brownell; +Cc: linuxppc-dev

On Sun, Feb 03, 2008 at 01:17:43PM -0800, David Brownell wrote:
> On Sunday 03 February 2008, Anton Vorontsov wrote:
> > This patch implements support for the GPIO LIB API. Two calls
> > unimplemented though: irq_to_gpio and gpio_to_irq.
> 
> Also gpio_cansleep().
> 
> 
> > +Example of two SOC GPIO banks defined as gpio-controller nodes:
> > +
> > +	qe_pio_a: gpio-controller@1400 {
> > +		#gpio-cells = <2>;
> > +		compatible = "fsl,qe-pario-bank";
> > +		reg = <0x1400 0x18>;
> > +		gpio-controller;
> > +	};
> > +
> > +	qe_pio_e: gpio-controller@1460 {
> > +		#gpio-cells = <2>;
> > +		compatible = "fsl,qe-pario-bank";
> > +		reg = <0x1460 0x18>;
> > +		gpio-controller;
> > +	};
> 
> Let me suggest another example to provide:   an I2C GPIO expander
> such as a pcf8574 or pca9354 (both eight bit expanders).  The SOC
> case is probably the easiest to cover.

I2C expander will be easy to support via device tree too, something
like:

i2c@3000 {
	#address-cells = <1>;
	#size-cells = <0>;
	cell-index = <0>;
	compatible = "fsl-i2c";
	reg = <0x3000 0x100>;
	interrupts = <14 8>;
	interrupt-parent = <&ipic>;
	dfsrr;

	pcf_pio: gpio-controller@27 {
		#gpio-cells = <2>;
		compatible = "ti,pcf8574";
		reg = <0x27>;
		gpio-controller;
	};
};

Of course, to support this, pcf857x driver needs OF bindings.

> > +#define ARCH_OF_GPIOS_PER_CHIP 32
> 
> Well, ARCH_OF_* is clearly not the now-removed ARCH_GPIOS_PER_CHIP,
> but I still suggest moving away from that concept.  

Yes, I noticed that ARCH_GPIOS_PER_CHIP is removed from the
gpiolib. But I solely use it to assign gpio_bases to the chips:

static int of_get_gpiochip_base(struct device_node *np)
{
	struct device_node *gc = NULL;
	int gpiochip_base = 0;

	while ((gc = of_find_all_nodes(gc))) {
		if (!of_get_property(gc, "gpio-controller", NULL))
			continue;

		if (gc != np) {
			gpiochip_base += ARCH_OF_GPIOS_PER_CHIP;
			continue;
		}

		of_node_put(gc);

		if (gpiochip_base >= ARCH_OF_GPIOS_END)
			return -ENOSPC;

		return gpiochip_base;
	}

	return -ENOENT;
}

This function walks the device tree and assigns gpio_base 0 to
the first encountered gpio-controller, ARCH_OF_GPIOS_PER_CHIP * N
to the Nth controller...

Technically, I can replace

		if (gc != np) {
			gpiochip_base += ARCH_OF_GPIOS_PER_CHIP;
			continue;
		}

With

		if (gc != np) {
			struct gpio_chip *chip = gc->data;

			if (chip)
				gpiochip_base += chip->ngpio;
			continue;
		}

Here we're getting next available GPIO base. But then this code can't
handle gpio chip removal. So, we need smart "dynamic GPIO base
allocator" as described asm-generic/gpio.h, gpio_find_base(ngpios)
that will find free gpio base suitable for ngpios to place. Until
that allocator implemented, we use simple allocator with
OF_GPIOS_PER_CHIP...

> > +static inline int gpio_get_value(unsigned int gpio)
> > +{
> > +	return __gpio_get_value(gpio);
> > +}
> > +
> > +static inline void gpio_set_value(unsigned int gpio, int value)
> > +{
> > +	__gpio_set_value(gpio, value);
> > +}
> 
> static inline int gpio_cansleep(unsigned int gpio)
> {
> 	return __gpio_cansleep(gpio);
> }

Will fix.

> > +static inline int irq_to_gpio(unsigned int irq)
> > +{
> > +	return -ENOSYS;
> 
> Minor nit:  "-EINVAL" would be better here, since the argument
> is constrained to have been returned from gpio_to_irq(), and
> as you wrote that call there can be no such values.

Will change.


Thanks!

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

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

* Re: [PATCH 05/11] [POWERPC] qe_lib: support for gpio_set_dedicated
  2008-02-03 17:10 ` [PATCH 05/11] [POWERPC] qe_lib: support for gpio_set_dedicated Anton Vorontsov
@ 2008-02-04 17:38   ` Timur Tabi
  0 siblings, 0 replies; 22+ messages in thread
From: Timur Tabi @ 2008-02-04 17:38 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: David Brownell, linuxppc-dev

Anton Vorontsov wrote:

> +static int qe_gpio_set_dedicated(struct gpio_chip *gc, unsigned int gpio,
> +				 int func)
> +{
> +	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 port_regs __iomem *regs = mm_gc->regs;
> +	struct port_regs *sregs = &qe_gc->saved_regs;
> +	unsigned long flags;
> +	u32 mask1 = 1 << (NUM_OF_PINS - (gpio + 1));
> +	u32 mask2 = 0x3 << (NUM_OF_PINS - (gpio % (NUM_OF_PINS / 2) + 1) * 2);
> +	bool second_reg = gpio > (NUM_OF_PINS / 2) - 1;
> +
> +	spin_lock_irqsave(&qe_gc->lock, flags);
> +
> +	if (second_reg)
> +		clrsetbits_be32(&regs->cpdir2, mask2, sregs->cpdir2 & mask2);
> +	else
> +		clrsetbits_be32(&regs->cpdir1, mask2, sregs->cpdir1 & mask2);
> +
> +	if (second_reg)
> +		clrsetbits_be32(&regs->cppar2, mask2, sregs->cppar2 & mask2);
> +	else
> +		clrsetbits_be32(&regs->cppar1, mask2, sregs->cppar1 & mask2);

You could combine these into one if-statement.

I took a quick look at all of your QE lib patches.  They generally look okay, 
but I haven't examined them thoroughly enough to formally ACK them.

-- 
Timur Tabi
Linux kernel developer at Freescale

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

* Re: [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support
  2008-02-03 17:10 ` [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support Anton Vorontsov
@ 2008-02-04 20:30   ` Scott Wood
  2008-02-04 20:56     ` Anton Vorontsov
  0 siblings, 1 reply; 22+ messages in thread
From: Scott Wood @ 2008-02-04 20:30 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linuxppc-dev

On Sun, Feb 03, 2008 at 08:10:19PM +0300, Anton Vorontsov wrote:
> GTM stands for General-purpose Timers Module and able to generate
> timer{1,2,3,4} interrupts.
> 
> There are several limitations in this support:
> 1. Cascaded (32 bit) timers unimplemented (1-2, 3-4).
>    This is straightforward to implement when needed, two timers should
>    be marked as "requested" and configured as appropriate.
> 2. Super-cascaded (64 bit) timers unimplemented (1-2-3-4).
>    This is also straightforward to implement when needed, all timers
>    should be marked as "requested" and configured as appropriate.

This isn't QE-specific; the same (as far as I see) timer hardware exists on,
for example, mpc8313 and mpc8349.

> +config QE_GTM
> +	bool
> +	help
> +	  QE General-Purpose Timers Module support

No bool text?

> +struct gtm_timer {
> +	unsigned int irq;
> +	bool requested;
> +
> +	u8 __iomem *gtcfr;
> +	u16 __iomem *gtmdr;
> +	u16 __iomem *gtpsr;
> +	u16 __iomem *gtcnr;
> +	u16 __iomem *gtrfr;
> +	u16 __iomem *gtevr;
> +};

__be16

> +static struct gtm_timer timers[4];
> +static struct qe_timers __iomem *qet;
> +static spinlock_t gtm_lock = __SPIN_LOCK_UNLOCKED(gtm_lock);

static DEFINE_SPINLOCK(gtm_lock);

Put these in a struct so multiple timer blocks can be supported (the non-QE
chips tend to have two blocks).

> +int qe_reset_ref_timer_16(int num, unsigned int hz, u16 ref)

What does this function do?  What goes in "hz" and "ref"?  Is it periodic or
one-shot?

-Scott

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

* Re: [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support
  2008-02-04 20:30   ` Scott Wood
@ 2008-02-04 20:56     ` Anton Vorontsov
  0 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-04 20:56 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev

On Mon, Feb 04, 2008 at 02:30:04PM -0600, Scott Wood wrote:
> On Sun, Feb 03, 2008 at 08:10:19PM +0300, Anton Vorontsov wrote:
> > GTM stands for General-purpose Timers Module and able to generate
> > timer{1,2,3,4} interrupts.
> > 
> > There are several limitations in this support:
> > 1. Cascaded (32 bit) timers unimplemented (1-2, 3-4).
> >    This is straightforward to implement when needed, two timers should
> >    be marked as "requested" and configured as appropriate.
> > 2. Super-cascaded (64 bit) timers unimplemented (1-2-3-4).
> >    This is also straightforward to implement when needed, all timers
> >    should be marked as "requested" and configured as appropriate.
> 
> This isn't QE-specific; the same (as far as I see) timer hardware exists on,
> for example, mpc8313 and mpc8349.

In my endless todo list. I'll try to evolve it to support fsl gtms in
general. Though the problem is that, for example, for QE USB we prefer
QE timers. So, we probably want to pass timers via device tree to
distinguish them.

> > +config QE_GTM
> > +	bool
> > +	help
> > +	  QE General-Purpose Timers Module support
> 
> No bool text?

No. This is silent config option.

> > +struct gtm_timer {
> > +	unsigned int irq;
> > +	bool requested;
> > +
> > +	u8 __iomem *gtcfr;
> > +	u16 __iomem *gtmdr;
> > +	u16 __iomem *gtpsr;
> > +	u16 __iomem *gtcnr;
> > +	u16 __iomem *gtrfr;
> > +	u16 __iomem *gtevr;
> > +};
> 
> __be16

Will do.

> > +static struct gtm_timer timers[4];
> > +static struct qe_timers __iomem *qet;
> > +static spinlock_t gtm_lock = __SPIN_LOCK_UNLOCKED(gtm_lock);
> 
> static DEFINE_SPINLOCK(gtm_lock);

I'm trying to open-code as much as I could (well, not as much, but
just using common sense :-), so I prefer the first variant, if you
don't mind. DEFINE_SPINLOCK() and above are equivalents.

This is not something I'm going to argue about, though. Just ask
one more time and I'll simply change it to DEFINE_SPINLOCK().

> Put these in a struct so multiple timer blocks can be supported (the non-QE
> chips tend to have two blocks).

Yes, this is in my todo list. Btw, QE chips have three gtms, QE + 2 IMMR.

> > +int qe_reset_ref_timer_16(int num, unsigned int hz, u16 ref)
> 
> What does this function do?  What goes in "hz" and "ref"?  Is it periodic or
> one-shot?

"ref" means that it counts to some reference value and fires the
interrupt. Hz is count rate. This particular function implements
"reset reference mode", timer will reset itself when reference
value is reached. We probably want to add frr flag (free run).
Will kernel-doc this function.

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

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

* Re: [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine
  2008-02-03 23:32     ` Anton Vorontsov
@ 2008-02-15  4:36       ` David Brownell
  2008-02-15 11:40         ` Anton Vorontsov
  0 siblings, 1 reply; 22+ messages in thread
From: David Brownell @ 2008-02-15  4:36 UTC (permalink / raw)
  To: cbouatmailru; +Cc: linuxppc-dev

On Sunday 03 February 2008, Anton Vorontsov wrote:
> On Sun, Feb 03, 2008 at 01:22:08PM -0800, David Brownell wrote:
> [...]
> 
> > So when you assume that a GPIO number can uniquely specify a pin for
> > use in function multiplexing ... you're stressing a "nonportable"
> > aspect of this issue.
> > 
> > Ditto when you assume that the multiplexing is on a per-pin basis,
> > rather than affecting a defined group of pins.  (More common, and
> > less annoying, than the OMAP1 issue.)
> > 
> > (And that doesn't even touch issues like configurable drive strength,
> > pullups, pulldowns, and so on.)
> 
> This is all true, of course.

And a related truism:  with at best rare exceptions, those things
get set up once at board setup and don't change later.  So the
audience for reconfiguring pins is quite limited ... basically
it'd all be for code which is already hardware-specific.

That's much less true of GPIOs.  It really *is* practical to have
generic GPIO based drivers for LEDs, keypad buttons, bitbanged
I2C, 1-wire, SPI ... etc.


> > > Imagine some 
> > > GPIO controller that can't do inputs, or outputs. First one will be
> > > still suitable for gpio_leds, second one will be suitable for gpio_keys.
> > 
> > The interface easily handles input-only and output-only GPIOs.
> > Make the relevant gpio_direction_*() methods fail those requests.
> 
> The point was: GPIOs could be "input only" but you still have
> "output" callback, and that doesn't troubles anybody. Not sure
> why set_dedicated() such a big issue then, it could fail too! :-)

See above:  you're equating pin and gpio ... and while GPIOs
have (by definition) standardized boolean input and output
functionality, the rest of what can be done with a pin isn't.


> We're talking about General Purpose IOs, right? They're general
> enough to support not only input/output stuff. And we want some
> API for these General Purpose IOs. GPIO LIB is the best candidate
> and we can implement such API at almost no cost, few lines of code.

GPIOs are not intended to be a kitchen sink though; and that's
the sense in which you seem to mean "best candidate":  it's a
clean lightweight interface, not gummed up with all kinds of
unrelated stuff.


Take your ideas and use them to create a pin configuration
interface.  On some platforms, configurable pins will map
onto GPIOs, one-to-one.  On others, there will be configurable
pins that aren't GPIOs ... GPIOs that can be mapped to any of
several pins ... pins that can be used with several GPIOs ...
pins that can only be used with GPIOs ... and so on.


> He-he. Actually, I have a patch that adds "to_irq" callback
> to GPIO LIB. :-) But I just didn't find time yet to cleanup
> the "user" of that addition (ARM-based "samcop" companion chip).
> 
> Briefly: gpio<->irq mapping there isn't "flat". It is messed
> all around. GPIO 1 is IRQ 12, GPIO 2 if IRQ 45 and so on... no
> common pattern. So, to support gpio_to_irq() we have to either:
> 
> 1. change the mappings of the IRQs, to match GPIOs.
> or
> 2. implement to_irq() callback (way easier).

That seems like maybe a reasonable thing to add to gpiolib.
It's fully within the current scope of the GPIO interface.
Show it when you've got a cleaned up version...

And although the original notion was that those mappings
cost on the order of an addition, I think that's not a big
deal ... except maybe sometimes for irq_to_gpio().


> >  > So, down below is the proposal patch: gpio_set_dedicated() routine.
> > > 
> > > There are other options, tough. But I don't like them:
> > > 
> > > ...
> > > 
> > > - Another option?
> > 
> > The way it's usually done:  platform-specific code to handle those
> > platform-specific pin configuration issues.
> 
> There is a problem. Driver could be cross-platform. For example,
> we have platforms with "CPM1", "CPM2" and "QE" on-chip gpio
> controllers.

So "platform" isn't exactly the right word.  "Hardware-specific"
is more to the point.  You know you're using <Brand-X-SOC-27> and
that means you must reconfigure <this-way> because <reason>; or
likewise, <Vendor-Y-ASIC-2> must be reconfigured <that-way> etc.


> And despite special _set_dedicated() function, this driver
> actually does _use_ pins as GPIOs. And as dedicated functions.
> And as GPIOs. The same pins, but at the different times.

But you're assuming that GPIO identifiers can be used as pin/ball
identifiers ... and as I pointed out, that's chip-specific.



> 	Will you agree to export "chips" so we can write
> GPIO LIB "platform extensions", like
> 
> qe_set_dedicated(unsigned int value_that_we_got_from_gpio_request)

gpio_request() returns zero-or-negative-errno.  Never unsigned int.  ;)


> {
> 	qe_chip = container_of(gpio_to_chip(...), struct qe_chip, chip);
> 	...

You know, you can write all this yourself, without needing any
support from the GPIO framework whatsoever.  The QE stuff that
registered a gpio_chip interface to its hardware probably keeps
some records itself, sufficient for such (infrequent) mappings.


> > > +int gpio_set_dedicated(unsigned gpio, int func)
> > 
> > It's not required that a pin/ball identifier have a one-to-one mapping
> > to "gpio" numbers, or that all pins/balls have "gpio" as one of the
> > possible functions.  So if there were a cross-platform call like this,
> > I'd want to see such it reference not a "gpio" but a "pin".
> > 
> > And for that matter, "dedicated" is inaccurate.
>
>	... deletia ...
> 
> > Of course, the "function" codes are extremely chip-specific ... and
> > some platforms would want to include things like pullups, pulldowns,
> > and so forth as part of pin configuration.
> > 
> > If you want to pursue this problem, don't couple it to GPIOs.
> 
> Um... couple it to what then?..

An abstraction of "pin", where pins don't correspond to GPIOs in a
one-to-one manner.

- dave

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

* Re: [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine
  2008-02-15  4:36       ` David Brownell
@ 2008-02-15 11:40         ` Anton Vorontsov
  2008-02-29 20:55           ` Anton Vorontsov
  0 siblings, 1 reply; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-15 11:40 UTC (permalink / raw)
  To: David Brownell; +Cc: linuxppc-dev

On Thu, Feb 14, 2008 at 08:36:40PM -0800, David Brownell wrote:
[...]
> > The point was: GPIOs could be "input only" but you still have
> > "output" callback, and that doesn't troubles anybody. Not sure
> > why set_dedicated() such a big issue then, it could fail too! :-)
> 
> See above:  you're equating pin and gpio ... and while GPIOs
> have (by definition) standardized boolean input and output
> functionality, the rest of what can be done with a pin isn't.

There is nothing much left to do with a "pin", IMO. Pin can be GPIO
or abstract "dedicated function". Am I missing anything else here?

> > We're talking about General Purpose IOs, right? They're general
> > enough to support not only input/output stuff. And we want some
> > API for these General Purpose IOs. GPIO LIB is the best candidate
> > and we can implement such API at almost no cost, few lines of code.
> 
> GPIOs are not intended to be a kitchen sink though; and that's
> the sense in which you seem to mean "best candidate":  it's a
> clean lightweight interface, not gummed up with all kinds of
> unrelated stuff.

Just one more callback won't bloat lightweight interface...  Later
I'll ask to add another one, and someone will ask for another. Yes, I
see the pattern. Surely, if you want to keep it clean and lightweight,
you should resist to additions -- I fully understand that. :-)

> Take your ideas and use them to create a pin configuration
> interface.  On some platforms, configurable pins will map
> onto GPIOs, one-to-one.  On others, there will be configurable
> pins that aren't GPIOs ... GPIOs that can be mapped to any of
> several pins ... pins that can be used with several GPIOs ...
> pins that can only be used with GPIOs ... and so on.

Sure, that's doable. But IMO this idea is about overcomplicating
the trivial task. Let's call it pinlib, with:

1. pin_request
2. pin_configure(pin, integer-representing-configuration)
3. pin_free

Hm... 2 auxiliary functions with the full-fledged subsystem just to
configure some pins? Plus count here that drivers would have to deal
with the two separate subsystems. Both GPIO/Pin-providers and
drivers that would use these GPIOs/Pins.

> > > The way it's usually done:  platform-specific code to handle those
> > > platform-specific pin configuration issues.
> > 
> > There is a problem. Driver could be cross-platform. For example,
> > we have platforms with "CPM1", "CPM2" and "QE" on-chip gpio
> > controllers.
> 
> So "platform" isn't exactly the right word.  "Hardware-specific"
> is more to the point.  You know you're using <Brand-X-SOC-27> and
> that means you must reconfigure <this-way> because <reason>; or
> likewise, <Vendor-Y-ASIC-2> must be reconfigured <that-way> etc.

<this-way> or <that-way> is passed through platform data,
platform code knowns "hardware-specific" details.

> > And despite special _set_dedicated() function, this driver
> > actually does _use_ pins as GPIOs. And as dedicated functions.
> > And as GPIOs. The same pins, but at the different times.
> 
> But you're assuming that GPIO identifiers can be used as pin/ball
> identifiers ... and as I pointed out, that's chip-specific.

Heh. I did re-read Documentation/gpio.txt once again, and still don't
get it, where are the differences between GPIOs and Pins? Yes, some
pins can't be GPIOs, but any GPIO must have associated Pin (internal
or external doesn't matter, it's "logical Pin").

For pins that can not be "GPIOs", we can just return errors from
output/input callbacks. Is it violation of the current GPIO API
conventions? Yes, but this is extension that will not break anything,
neither we contradict the "GPIO" term here, btw.

> > {
> > 	qe_chip = container_of(gpio_to_chip(...), struct qe_chip, chip);
> > 	...
> 
> You know, you can write all this yourself, without needing any
> support from the GPIO framework whatsoever.  The QE stuff that
> registered a gpio_chip interface to its hardware probably keeps
> some records itself, sufficient for such (infrequent) mappings.

Yes, surely. And as I've said already, I do know how to workaround
that, without GPIOLIB support. The thing is that I don't like the
way I'm going to do it.

[...]
> > > Of course, the "function" codes are extremely chip-specific ... and
> > > some platforms would want to include things like pullups, pulldowns,
> > > and so forth as part of pin configuration.
> > > 
> > > If you want to pursue this problem, don't couple it to GPIOs.
> > 
> > Um... couple it to what then?..
> 
> An abstraction of "pin", where pins don't correspond to GPIOs in a
> one-to-one manner.

Keeping in mind that GPIOLIB was brewing a year (?), I think another
GPIOLIB-alike subsystem not worth powder and shot, pins and GPIOs
will be obsolete at that time. :-)


Thanks,

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

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

* Re: [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine
  2008-02-15 11:40         ` Anton Vorontsov
@ 2008-02-29 20:55           ` Anton Vorontsov
  0 siblings, 0 replies; 22+ messages in thread
From: Anton Vorontsov @ 2008-02-29 20:55 UTC (permalink / raw)
  To: David Brownell; +Cc: linuxppc-dev

On Fri, Feb 15, 2008 at 02:40:29PM +0300, Anton Vorontsov wrote:
[...]
> > > {
> > > 	qe_chip = container_of(gpio_to_chip(...), struct qe_chip, chip);
> > > 	...
> > 
> > You know, you can write all this yourself, without needing any
> > support from the GPIO framework whatsoever.  The QE stuff that
> > registered a gpio_chip interface to its hardware probably keeps
> > some records itself, sufficient for such (infrequent) mappings.
> 
> Yes, surely. And as I've said already, I do know how to workaround
> that, without GPIOLIB support. The thing is that I don't like the
> way I'm going to do it.

...please, don't force me to do these dirty hacks inside the arch code,
nobody will like them, I know it beforehand. ;-)

...if you so reluctant supporting dedicated functions inside the gpiolib,
then nothing could be easier than exporting gpio_to_chip(), so arch code
can write extensions without dirty hacks. Please?


p.s. By "dirty hacks" I mean:

a) drivers will need to fetch and store device_node in their private
   driver data, in _addition_ to gpio numbers;

b) which means that I'll have to introduce of_get_gpio_controller()
   to solely workaround gpio_to_chip() absence;

c) and all that will result in 9 * N additional lines of code to
   handle by the drivers, where N is the number of GPIOs we want to use:

   struct drvdata {
   (1) struct device_node *gpioX_controller;

   drv_probe()
   (2) drvdata->gpioX_controller = of_get_gpio_controller(...);
   (3) if (!drvdata->gpioX_controller) {
   (4)        ret = -EINVAL;
   (5)        goto gpioX_contr_failed;
   (6) }
   (7) gpioX_contr_failed:
   (8) of_put_gpio_controller(drvdata->gpioX_controller)

   drv_remove()
   {
   (9) of_put_gpio_controller(drvdata->gpioX_controller)

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

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

end of thread, other threads:[~2008-02-29 20:56 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-03 17:08 [RFC PATCH 0/11] Patches needed to support QE USB Host Controller Anton Vorontsov
2008-02-03 17:09 ` [PATCH 01/11] [POWERPC] Implement support for the GPIO LIB API Anton Vorontsov
2008-02-03 21:17   ` David Brownell
2008-02-04 13:19     ` Anton Vorontsov
2008-02-03 17:09 ` [PATCH 02/11] [POWERPC] QE: split par_io_config_pin() Anton Vorontsov
2008-02-03 17:10 ` [PATCH 03/11] [POWERPC] QE: implement GPIO LIB API Anton Vorontsov
2008-02-03 17:10 ` [PATCH 04/11] [RFC][GPIOLIB] add gpio_set_dedicated() routine Anton Vorontsov
2008-02-03 21:22   ` David Brownell
2008-02-03 23:32     ` Anton Vorontsov
2008-02-15  4:36       ` David Brownell
2008-02-15 11:40         ` Anton Vorontsov
2008-02-29 20:55           ` Anton Vorontsov
2008-02-03 17:10 ` [PATCH 05/11] [POWERPC] qe_lib: support for gpio_set_dedicated Anton Vorontsov
2008-02-04 17:38   ` Timur Tabi
2008-02-03 17:10 ` [PATCH 06/11] [POWERPC] qe_lib: implement qe_muram_offset Anton Vorontsov
2008-02-03 17:10 ` [PATCH 07/11] [POWERPC] qe_lib: export qe_get_brg_clk Anton Vorontsov
2008-02-03 17:10 ` [PATCH 08/11] [POWERPC] qe_lib: implement QE GTM support Anton Vorontsov
2008-02-04 20:30   ` Scott Wood
2008-02-04 20:56     ` Anton Vorontsov
2008-02-03 17:10 ` [PATCH 09/11] [POWERPC] qe_lib: add support for QE USB Anton Vorontsov
2008-02-03 17:10 ` [PATCH 10/11] [POWERPC] mpc8360erdk: add FHCI USB support Anton Vorontsov
2008-02-03 17:11 ` [PATCH 11/11] [RFC USB POWERPC] Freescale QUICC Engine USB Host Controller 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).