* Re: [PATCH 5/8] [POWERPC] qe_lib: export qe_get_brg_clk()
From: Kumar Gala @ 2008-03-11 18:36 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: linuxppc-dev
In-Reply-To: <20080311172424.GE7727@localhost.localdomain>
On Mar 11, 2008, at 12:24 PM, Anton Vorontsov wrote:
> qe_get_brg_clk() will be used by the fsl_gtm routines.
What is the GTM code going to be used for?
- k
^ permalink raw reply
* [PATCH 0/4] GPIO LIB API support for OF and PowerPC
From: Anton Vorontsov @ 2008-03-11 18:40 UTC (permalink / raw)
To: linuxppc-dev; +Cc: David Miller
Hi all,
Here are few patches needed to support GPIO LIB API on PowerPC.
It would be great to see these applied for the 2.6.26...
For now we use static gpio base allocation, later we'll switch to
the dynamic bases. The patch below shows the idea of what needs to be
done when dynamic gpio base allocation patch[1] will hit the mainline.
[1] http://lkml.org/lkml/2008/3/11/261
- - - -
Subject: of/gpio: use dynamic base allocation
Signed-off-by: not yet
---
depends on http://lkml.org/lkml/2008/3/11/261
drivers/of/gpio.c | 38 +-------------------------------------
1 files changed, 1 insertions(+), 37 deletions(-)
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index ed1c161..a6775ff 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -118,38 +118,6 @@ static int of_gpio_simple_xlate(struct of_gpio_chip *of_gc,
return *gpio;
}
-/* Should be sufficient for now, later we'll use dynamic bases. */
-#if defined(CONFIG_PPC32) || defined(CONFIG_SPARC32)
-#define GPIOS_PER_CHIP 32
-#else
-#define GPIOS_PER_CHIP 64
-#endif
-
-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 += GPIOS_PER_CHIP;
- continue;
- }
-
- of_node_put(gc);
-
- if (gpiochip_base >= ARCH_NR_GPIOS)
- return -ENOSPC;
-
- return gpiochip_base;
- }
-
- return -ENOENT;
-}
-
int of_mm_gpiochip_add(struct device_node *np,
struct of_mm_gpio_chip *mm_gc)
{
@@ -165,11 +133,7 @@ int of_mm_gpiochip_add(struct device_node *np,
if (!mm_gc->regs)
goto err1;
- gc->base = of_get_gpiochip_base(np);
- if (gc->base < 0) {
- ret = gc->base;
- goto err1;
- }
+ gc->base = -1;
if (!of_gc->xlate)
of_gc->xlate = of_gpio_simple_xlate;
--
1.5.2.2
^ permalink raw reply related
* [PATCH 1/4] OF helpers for the GPIO API
From: Anton Vorontsov @ 2008-03-11 18:42 UTC (permalink / raw)
To: linuxppc-dev; +Cc: David Miller
In-Reply-To: <20080311184012.GA28466@localhost.localdomain>
This patch implements various helpers to support OF bindings for
the GPIO LIB API.
Previously this was PowerPC specific, but it seems this code
isn't arch-dependent anyhow, so let's place it into of/.
SPARC will not see this addition yet, real hardware seem to not use
GPIOs at all. But this might change:
http://www.leox.org/docs/faq_MLleon.html
"16-bit I/O port" sounds promising. :-)
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/of/Kconfig | 6 ++
drivers/of/Makefile | 1 +
drivers/of/gpio.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/of_gpio.h | 96 ++++++++++++++++++++++
4 files changed, 305 insertions(+), 0 deletions(-)
create mode 100644 drivers/of/gpio.c
create mode 100644 include/linux/of_gpio.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index c03072b..3354ad7 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -1,3 +1,9 @@
config OF_DEVICE
def_bool y
depends on OF && (SPARC || PPC_OF)
+
+config OF_GPIO
+ def_bool y
+ depends on OF && PPC_OF && HAVE_GPIO_LIB
+ help
+ OpenFirmware GPIO accessors
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ab9be5d..5a61f70 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,2 +1,3 @@
obj-y = base.o
obj-$(CONFIG_OF_DEVICE) += device.o platform.o
+obj-$(CONFIG_OF_GPIO) += gpio.o
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
new file mode 100644
index 0000000..ed1c161
--- /dev/null
+++ b/drivers/of/gpio.c
@@ -0,0 +1,202 @@
+/*
+ * OF helpers for the GPIO API
+ *
+ * Copyright (c) 2007-2008 MontaVista Software, Inc.
+ *
+ * Author: 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 <linux/of_gpio.h>
+#include <asm/prom.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; gpio_index++) {
+ 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++;
+ 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);
+ }
+
+ 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;
+}
+
+/* Should be sufficient for now, later we'll use dynamic bases. */
+#if defined(CONFIG_PPC32) || defined(CONFIG_SPARC32)
+#define GPIOS_PER_CHIP 32
+#else
+#define GPIOS_PER_CHIP 64
+#endif
+
+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 += GPIOS_PER_CHIP;
+ continue;
+ }
+
+ of_node_put(gc);
+
+ if (gpiochip_base >= ARCH_NR_GPIOS)
+ 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;
+ struct gpio_chip *gc = &of_gc->gc;
+
+ gc->label = kstrdup(np->full_name, GFP_KERNEL);
+ if (!gc->label)
+ goto err0;
+
+ mm_gc->regs = of_iomap(np, 0);
+ if (!mm_gc->regs)
+ goto err1;
+
+ gc->base = of_get_gpiochip_base(np);
+ if (gc->base < 0) {
+ ret = gc->base;
+ goto err1;
+ }
+
+ if (!of_gc->xlate)
+ of_gc->xlate = of_gpio_simple_xlate;
+
+ if (mm_gc->save_regs)
+ mm_gc->save_regs(mm_gc);
+
+ np->data = of_gc;
+
+ ret = gpiochip_add(gc);
+ if (ret)
+ goto err2;
+
+ /* 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, gc->base);
+ return 0;
+err2:
+ np->data = NULL;
+ iounmap(mm_gc->regs);
+err1:
+ kfree(gc->label);
+err0:
+ 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/linux/of_gpio.h b/include/linux/of_gpio.h
new file mode 100644
index 0000000..ca313f0
--- /dev/null
+++ b/include/linux/of_gpio.h
@@ -0,0 +1,96 @@
+/*
+ * Generic GPIO API implementation for PowerPC.
+ *
+ * Copyright (c) 2007-2008 MontaVista Software, Inc.
+ *
+ * Author: 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 __LINUX_OF_GPIO_H
+#define __LINUX_OF_GPIO_H
+
+#include <asm/gpio.h>
+
+#ifdef CONFIG_OF_GPIO
+
+/*
+ * 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);
+};
+
+static inline struct of_gpio_chip *to_of_gpio_chip(struct gpio_chip *gc)
+{
+ return container_of(gc, 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;
+};
+
+static inline struct of_mm_gpio_chip *to_of_mm_gpio_chip(struct gpio_chip *gc)
+{
+ struct of_gpio_chip *of_gc = to_of_gpio_chip(gc);
+
+ return container_of(of_gc, 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:
+ * - all the callbacks
+ *
+ * 2) In the of_gpio_chip structure:
+ * - gpio_cells
+ * - xlate callback (optional)
+ *
+ * 3) In the of_mm_gpio_chip structure:
+ * - 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);
+
+#else
+
+/* Drivers may not strictly depend on the GPIO support, so let them link. */
+static inline int of_get_gpio(struct device_node *np, int index)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_OF_GPIO */
+
+#endif /* __LINUX_OF_GPIO_H */
--
1.5.2.2
^ permalink raw reply related
* [PATCH 2/4] [POWERPC] Implement support for the GPIO LIB API
From: Anton Vorontsov @ 2008-03-11 18:42 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20080311184012.GA28466@localhost.localdomain>
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 | 52 ++++++++++++++++++++++++
arch/powerpc/Kconfig | 5 ++
include/asm-powerpc/gpio.h | 55 ++++++++++++++++++++++++++
3 files changed, 112 insertions(+), 0 deletions(-)
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 b506245..e9e0c2f 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -66,6 +66,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
@@ -2925,6 +2929,54 @@ 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
+ 0 /* holes are permitted, means no GPIO 3 */
+ &gpio-controller4-phandle gpio4-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:
+
+ node {
+ gpios = <&qe_pio_e 18 0>;
+ };
+
+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-a", "fsl,qe-pario-bank";
+ reg = <0x1400 0x18>;
+ gpio-controller;
+ };
+
+ qe_pio_e: gpio-controller@1460 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "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 0b27cbd..f328509 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/include/asm-powerpc/gpio.h b/include/asm-powerpc/gpio.h
new file mode 100644
index 0000000..057840d
--- /dev/null
+++ b/include/asm-powerpc/gpio.h
@@ -0,0 +1,55 @@
+/*
+ * Generic GPIO API implementation for PowerPC.
+ *
+ * Copyright (c) 2007-2008 MontaVista Software, Inc.
+ *
+ * Author: 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
+
+/*
+ * 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);
+}
+
+static inline int gpio_cansleep(unsigned int gpio)
+{
+ return __gpio_cansleep(gpio);
+}
+
+/*
+ * Not implemented, yet.
+ */
+static inline int gpio_to_irq(unsigned int gpio)
+{
+ return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_HAVE_GPIO_LIB */
+
+#endif /* __ASM_POWERPC_GPIO_H */
--
1.5.2.2
^ permalink raw reply related
* [PATCH 3/4] [POWERPC] QE: split par_io_config_pin()
From: Anton Vorontsov @ 2008-03-11 18:42 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20080311184012.GA28466@localhost.localdomain>
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 93916a4..a3f9c3f 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -38,6 +38,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
};
@@ -64,28 +68,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 -
@@ -97,36 +102,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
* [PATCH 4/4] [POWERPC] QE: implement support for the GPIO LIB API
From: Anton Vorontsov @ 2008-03-11 18:42 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20080311184012.GA28466@localhost.localdomain>
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 | 137 +++++++++++++++++++++++++-
3 files changed, 158 insertions(+), 13 deletions(-)
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index e9e0c2f..113884b 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1703,24 +1703,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-<bank>", "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-a", "fsl,qe-pario-bank";
+ reg = <0x1400 0x18>;
+ gpio-controller;
+ };
+ qe_pio_e: gpio-controller@1460 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "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 0afd225..2d91ed9 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 a3f9c3f..a4a195a 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -20,7 +20,8 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/ioport.h>
-
+#include <linux/spinlock.h>
+#include <linux/of_gpio.h>
#include <asm/io.h>
#include <asm/qe.h>
#include <asm/prom.h>
@@ -214,6 +215,140 @@ 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;
+};
+
+static inline struct qe_gpio_chip *
+to_qe_gpio_chip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, 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(®s->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(®s->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(®s->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;
+
+ 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
* Re: [PATCH 2/2] Add local bus device nodes to MPC837xMDS device trees.
From: Segher Boessenkool @ 2008-03-11 18:43 UTC (permalink / raw)
To: David Gibson; +Cc: linuxppc-dev, Li Yang, paulus
In-Reply-To: <20080310235429.GC11559@localhost.localdomain>
>>> This isn't a problem with this device tree, but it's probably time we
>>> started establishing some conventional generic names for nand flash
>>> and board-control devices.
>>>
>>> So, to start the ball rolling, I've seen several names for nand flash
>>> nodes, I'd suggest we standardise on "nand-flash".
>>
>> What's wrong with the already well-established generic name "flash"?
>
> I was concerned that using "flash" for both NOR flash (which it
> already is) and NAND flash might be unwise. I am quite open to being
> convinced otherwise, though.
You already said you're convinced, but I'll add another argument
anyway...
For NAND flash, there will usually be a parent node named
"nand-controller"
or similar, while NOR flash will typically be direct-mapped.
There is always this tension between making the names as generic as
possible, and not losing too much information. In my experience, you
can always make leaf nodes have very very generic names, it's only the
bus nodes where this can be harder. And then there are exceptions like
"board-control" where there just _is_ no really good name ;-)
Segher
^ permalink raw reply
* Re: [PATCH 5/8] [POWERPC] qe_lib: export qe_get_brg_clk()
From: Anton Vorontsov @ 2008-03-11 18:44 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <7360820C-8025-4797-9E05-B68BD2FC6CE3@kernel.crashing.org>
On Tue, Mar 11, 2008 at 01:36:16PM -0500, Kumar Gala wrote:
>
> On Mar 11, 2008, at 12:24 PM, Anton Vorontsov wrote:
>
> >qe_get_brg_clk() will be used by the fsl_gtm routines.
>
> What is the GTM code going to be used for?
Freescale QE USB Host Controller. I posted that driver already,
but will resend the updated one soon.
--
Anton Vorontsov
email: cboumailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: dtc: Add some documentation for the dts formta
From: Segher Boessenkool @ 2008-03-11 18:50 UTC (permalink / raw)
To: David Gibson; +Cc: linuxppc-dev, Yoder Stuart
In-Reply-To: <20080310234714.GB11559@localhost.localdomain>
> This patch adds a dts-format.txt in the Documentation directory, with
> an introduction to the dtc source format.
Looks good, some minor nits...
> +* A 64-bit value is represented with two 32-bit cells.
> +
> + e.g. clock-frequency = <0x00000001 0x00000000>;
Please add that this is big-endian order always.
> +* Values may have several comma-separated components, which are
> + concatenated together.
> + e.g. compatible = "ns16550", "ns8250";
You might want to clarify that the terminating 0 of ns16550 becomes
part of the property, i.e. this is not like strcat() in C.
Segher
^ permalink raw reply
* Re: [RFC/PATCH] [POWER] mpc85xx_ds add DMA engine to the DT and parse it.
From: Kumar Gala @ 2008-03-11 18:53 UTC (permalink / raw)
To: Sebastian Siewior; +Cc: linuxppc-embedded
In-Reply-To: <20080311103906.GD9656@www.tglx.de>
On Mar 11, 2008, at 5:39 AM, Sebastian Siewior wrote:
> The DT entry is copy / paste from the documentation.
>
> Signed-off-by: Sebastian Siewior <bigeasy@linutronix.de>
> ---
> arch/powerpc/boot/dts/mpc8544ds.dts | 41 +++++++++++++++++++++
> +++++++++
> arch/powerpc/platforms/85xx/mpc85xx_ds.c | 13 +++++++++
> 2 files changed, 54 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/
> dts/mpc8544ds.dts
> index 688af9d..fdaf793 100644
> --- a/arch/powerpc/boot/dts/mpc8544ds.dts
> +++ b/arch/powerpc/boot/dts/mpc8544ds.dts
> @@ -116,6 +116,47 @@
> };
> };
>
> + dma@21300 {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "fsl,mpc8540-dma", "fsl,eloplus-dma";
> + reg = <21300 4>;
> + ranges = <0 21100 200>;
> + cell-index = <0>;
> + dma-channel@0 {
> + compatible = "fsl,mpc8540-dma-channel",
> + "fsl,eloplus-dma-channel";
this should be mpc8544-dma everywhere.
- k
^ permalink raw reply
* [PATCH] Freescale QUICC Engine USB Host Controller
From: Anton Vorontsov @ 2008-03-11 19:17 UTC (permalink / raw)
To: linux-usb, linuxppc-dev
This is patch adds support for the FHCI USB controller, as found
in the Freescale MPC836x and MPC832x processors. It can support
Full or Low speed modes.
Quite a lot hardware is doing by itself (SOF generation, CRC generation
and checking), though scheduling and retransmission is on the software
shoulders.
This controller does not integrate the root hub, so this driver also
fakes an one-port hub. External hub is required to support more than
one device.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/usb/Makefile | 1 +
drivers/usb/host/Kconfig | 18 +
drivers/usb/host/Makefile | 2 +-
drivers/usb/host/fhci-cq.c | 105 +++++
drivers/usb/host/fhci-dbg.c | 144 +++++++
drivers/usb/host/fhci-hcd.c | 789 +++++++++++++++++++++++++++++++++++++
drivers/usb/host/fhci-hub.c | 336 ++++++++++++++++
drivers/usb/host/fhci-mem.c | 105 +++++
drivers/usb/host/fhci-q.c | 242 ++++++++++++
drivers/usb/host/fhci-sched.c | 865 +++++++++++++++++++++++++++++++++++++++++
drivers/usb/host/fhci-tds.c | 631 ++++++++++++++++++++++++++++++
drivers/usb/host/fhci.h | 535 +++++++++++++++++++++++++
12 files changed, 3772 insertions(+), 1 deletions(-)
create mode 100644 drivers/usb/host/fhci-cq.c
create mode 100644 drivers/usb/host/fhci-dbg.c
create mode 100644 drivers/usb/host/fhci-hcd.c
create mode 100644 drivers/usb/host/fhci-hub.c
create mode 100644 drivers/usb/host/fhci-mem.c
create mode 100644 drivers/usb/host/fhci-q.c
create mode 100644 drivers/usb/host/fhci-sched.c
create mode 100644 drivers/usb/host/fhci-tds.c
create mode 100644 drivers/usb/host/fhci.h
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 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 bf8be2a..95e1c70 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -260,3 +260,21 @@ 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 && OF_GPIO && QUICC_ENGINE
+ select FSL_GTM
+ help
+ Some Freescale PowerPC processors (such as MPC8360E and
+ MPC8323) have a Full/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.
+
+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.
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..23716fa
--- /dev/null
+++ b/drivers/usb/host/fhci-cq.c
@@ -0,0 +1,105 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/* circular queue structure */
+struct cir_q {
+ int max; /* size of queue */
+ int max_in; /* max items in queue */
+ int first; /* index of first in queue */
+ int last; /* index after last in queue */
+ int read; /* current reading position */
+ void *table[1]; /* fake size */
+};
+
+/* circular queue handle */
+static int cq_howmany(struct cir_q *cq)
+{
+ int l = cq->last;
+ int f = cq->first;
+
+ return l >= f ? l - f : l + cq->max - f;
+}
+
+static struct cir_q *cq_new(int size)
+{
+ struct cir_q *cq;
+
+ cq = kzalloc((sizeof(*cq) + size * sizeof(void *)), GFP_KERNEL);
+ if (cq) {
+ cq->max = size;
+ cq->first = 0;
+ cq->last = 0;
+ cq->read = 0;
+ cq->max_in = 0;
+ }
+
+ return cq;
+}
+
+static void cq_delete(struct cir_q *cq)
+{
+ kfree(cq);
+}
+
+static int cq_put(struct cir_q *cq, void *p)
+{
+ int n;
+ int k;
+
+ /* see if we can freely advance the last pointer */
+ n = cq->last;
+ k = cq_howmany(cq);
+ if ((k + 1) >= cq->max)
+ return -1;
+
+ if (++n >= cq->max)
+ n = 0;
+
+ /* add element to queue */
+ cq->table[cq->last] = p;
+ cq->last = n;
+ if ((k + 1) > cq->max_in)
+ cq->max_in = k + 1;
+
+ return k;
+}
+
+static void *cq_get(struct cir_q *cq)
+{
+ int n;
+ int k;
+ void *p;
+
+ n = cq->first;
+ /* see if the queue is not empty */
+ if (n == cq->last)
+ return NULL;
+
+ p = cq->table[n];
+ if (++n >= cq->max)
+ n = 0;
+ if (cq->read == cq->first)
+ cq->read = n;
+ cq->first = n;
+
+ /* see if we've passed our previous maximum */
+ k = cq_howmany(cq);
+ if (k > cq->max_in)
+ cq->max_in = k;
+
+ return p;
+}
diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c
new file mode 100644
index 0000000..8127935
--- /dev/null
+++ b/drivers/usb/host/fhci-dbg.c
@@ -0,0 +1,144 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifdef CONFIG_FHCI_DEBUG
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er)
+{
+ int i;
+
+ if (usb_er == -1) {
+ fhci->usb_irq_stat[12]++;
+ return;
+ }
+
+ for (i = 0; i < 12; ++i) {
+ if (usb_er & (1 << i))
+ fhci->usb_irq_stat[i]++;
+ }
+}
+
+static int fhci_dfs_regs_show(struct seq_file *s, void *v)
+{
+ struct fhci_hcd *fhci = s->private;
+ struct fhci_regs __iomem *regs = fhci->regs;
+
+ seq_printf(s,
+ "mode: 0x%x\n" "addr: 0x%x\n"
+ "command: 0x%x\n" "ep0: 0x%x\n"
+ "event: 0x%x\n" "mask: 0x%x\n"
+ "status: 0x%x\n" "SOF timer: %d\n"
+ "frame number: %d\n"
+ "lines status: 0x%x\n",
+ in_8(®s->usb_mod), in_8(®s->usb_addr),
+ in_8(®s->usb_comm), in_be16(®s->usb_ep[0]),
+ in_be16(®s->usb_event), in_be16(®s->usb_mask),
+ in_8(®s->usb_status), in_be16(®s->usb_sof_tmr),
+ in_be16(®s->usb_frame_num),
+ fhci_ioports_check_bus_state(fhci));
+
+ return 0;
+}
+
+static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v)
+{
+ struct fhci_hcd *fhci = s->private;
+ int *usb_irq_stat = fhci->usb_irq_stat;
+
+ seq_printf(s,
+ "RXB: %d\n" "TXB: %d\n" "BSY: %d\n"
+ "SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n"
+ "TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n"
+ "RESET: %d\n" "SFT: %d\n" "MSF: %d\n"
+ "IDLE_ONLY: %d\n",
+ usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2],
+ usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5],
+ usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8],
+ usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11],
+ usb_irq_stat[12]);
+
+ return 0;
+}
+
+static int fhci_dfs_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fhci_dfs_regs_show, inode->i_private);
+}
+
+static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fhci_dfs_irq_stat_show, inode->i_private);
+}
+
+static const struct file_operations fhci_dfs_regs_fops = {
+ .open = fhci_dfs_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations fhci_dfs_irq_stat_fops = {
+ .open = fhci_dfs_irq_stat_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void fhci_dfs_create(struct fhci_hcd *fhci)
+{
+ struct device *dev = fhci_to_hcd(fhci)->self.controller;
+
+ fhci->dfs_root = debugfs_create_dir(dev->bus_id, NULL);
+ if (!fhci->dfs_root) {
+ WARN_ON(1);
+ return;
+ }
+
+ fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO,
+ fhci->dfs_root, fhci, &fhci_dfs_regs_fops);
+
+ fhci->dfs_irq_stat = debugfs_create_file("irq_stat",
+ S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
+ &fhci_dfs_irq_stat_fops);
+
+ WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat);
+}
+
+static void fhci_dfs_destroy(struct fhci_hcd *fhci)
+{
+ if (!fhci->dfs_root)
+ return;
+
+ if (fhci->dfs_irq_stat)
+ debugfs_remove(fhci->dfs_irq_stat);
+
+ if (fhci->dfs_regs)
+ debugfs_remove(fhci->dfs_regs);
+
+ debugfs_remove(fhci->dfs_root);
+}
+
+#else
+
+static void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) {}
+static void fhci_dfs_destroy(struct fhci_hcd *fhci) {}
+static void fhci_dfs_create(struct fhci_hcd *fhci) {}
+
+#endif /* CONFIG_FHCI_DEBUG */
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
new file mode 100644
index 0000000..8e684cc
--- /dev/null
+++ b/drivers/usb/host/fhci-hcd.c
@@ -0,0 +1,789 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#if defined(CONFIG_FHCI_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <asm/qe.h>
+#include <asm/fsl_gtm.h>
+#include "../core/hcd.h"
+#include "fhci.h"
+#include "fhci-hub.c"
+#include "fhci-q.c"
+#include "fhci-dbg.c"
+#include "fhci-mem.c"
+#include "fhci-cq.c"
+#include "fhci-tds.c"
+#include "fhci-sched.c"
+
+static void fhci_start_sof_timer(struct fhci_hcd *fhci)
+{
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ /* clear frame_n */
+ out_be16(&fhci->pram->frame_num, 0);
+
+ out_be16(&fhci->regs->usb_sof_tmr, 0);
+ setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static void fhci_stop_sof_timer(struct fhci_hcd *fhci)
+{
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE);
+ gtm_stop_timer_16(fhci->timer);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static u16 get_sof_timer_count(struct fhci_usb *usb)
+{
+ return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12);
+}
+
+/* initialize the endpoint zero */
+static u32 endpoint_zero_init(struct fhci_usb *usb,
+ enum fhci_mem_alloc data_mem,
+ u32 ring_len)
+{
+ u32 rc;
+
+ rc = create_endpoint(usb, data_mem, ring_len);
+ if (rc)
+ return rc;
+
+ /* inilialize endpoint registers */
+ init_endpoint_registers(usb, usb->ep0, data_mem);
+
+ return 0;
+}
+
+/* enable the USB interrupts */
+static void fhci_usb_enable_interrupt(struct fhci_usb *usb)
+{
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (usb->intr_nesting_cnt == 1) {
+ /* initialize the USB interrupt */
+ enable_irq(fhci_to_hcd(fhci)->irq);
+
+ /* initialize the event register and mask register */
+ out_be16(&usb->fhci->regs->usb_event, 0xffff);
+ out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+
+ /* enable the timer interrupts */
+ enable_irq(fhci->timer->irq);
+ } else if (usb->intr_nesting_cnt > 1)
+ fhci_info(fhci, "unbalanced USB interrupts nesting\n");
+ usb->intr_nesting_cnt--;
+}
+
+/* diable the usb interrupt */
+static void fhci_usb_disable_interrupt(struct fhci_usb *usb)
+{
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (usb->intr_nesting_cnt == 0) {
+ /* diable the timer interrupt */
+ disable_irq(fhci->timer->irq);
+
+ /* disable the usb interrupt */
+ disable_irq(fhci_to_hcd(fhci)->irq);
+ out_be16(&usb->fhci->regs->usb_mask, 0);
+ }
+ usb->intr_nesting_cnt++;
+}
+
+/* enable the USB controller */
+static u32 fhci_usb_enable(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ out_be16(&usb->fhci->regs->usb_event, 0xffff);
+ out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+ setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+
+ mdelay(100);
+
+ return 0;
+}
+
+/* disable the USB controller */
+static u32 fhci_usb_disable(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ fhci_usb_disable_interrupt(usb);
+ usb_port_disable(fhci);
+
+ /* disable the usb controller */
+ if (usb->port_status == FHCI_PORT_FULL ||
+ usb->port_status == FHCI_PORT_LOW)
+ device_disconnected_interrupt(fhci);
+
+ clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+
+ return 0;
+}
+
+/* check the bus state by polling the QE bit on the IO ports */
+static int fhci_ioports_check_bus_state(struct fhci_hcd *fhci)
+{
+ u8 bits = 0;
+
+ /* check USBOE,if transmitting,exit */
+ if (!gpio_get_value(fhci->gpios[GPIO_USBOE]))
+ return -1;
+
+ /* check USBRP */
+ if (gpio_get_value(fhci->gpios[GPIO_USBRP]))
+ bits |= 0x2;
+
+ /* check USBRN */
+ if (gpio_get_value(fhci->gpios[GPIO_USBRN]))
+ bits |= 0x1;
+
+ return bits;
+}
+
+static void fhci_mem_free(struct fhci_hcd *fhci)
+{
+ struct td *td;
+ struct ed *ed;
+
+ while (!list_empty(&fhci->empty_eds)) {
+ ed = list_entry(fhci->empty_eds.next, struct ed, node);
+ list_del(fhci->empty_eds.next);
+ }
+
+ while (!list_empty(&fhci->empty_tds)) {
+ td = list_entry(fhci->empty_tds.next, struct td, node);
+ list_del(fhci->empty_tds.next);
+ }
+
+ kfree(fhci->vroot_hub);
+ kfree(fhci->hc_list);
+}
+
+static int fhci_mem_init(struct fhci_hcd *fhci)
+{
+ int i, error = 0;
+
+ fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL);
+ if (!fhci->hc_list)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&fhci->hc_list->ctrl_list);
+ INIT_LIST_HEAD(&fhci->hc_list->bulk_list);
+ INIT_LIST_HEAD(&fhci->hc_list->iso_list);
+ INIT_LIST_HEAD(&fhci->hc_list->intr_list);
+ INIT_LIST_HEAD(&fhci->hc_list->done_list);
+
+ fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL);
+ if (!fhci->vroot_hub)
+ return -ENOMEM;
+
+
+ INIT_LIST_HEAD(&fhci->empty_eds);
+ INIT_LIST_HEAD(&fhci->empty_tds);
+
+ /* initialize work queue to handle done list */
+ fhci_tasklet.data = (unsigned long)fhci;
+ fhci->process_done_task = &fhci_tasklet;
+
+ for (i = 0; i < MAX_TDS; i++) {
+ struct td *td = kmalloc(sizeof(*td), GFP_KERNEL);
+
+ if (!td) {
+ error = 1;
+ break;
+ }
+ recycle_empty_td(fhci, td);
+ }
+ for (i = 0; i < MAX_EDS; i++) {
+ struct ed *ed = kmalloc(sizeof(*ed), GFP_KERNEL);
+
+ if (!ed) {
+ error = 1;
+ break;
+ }
+ recycle_empty_ed(fhci, ed);
+ }
+
+ if (error) {
+ fhci_mem_free(fhci);
+ return -ENOMEM;
+ }
+
+ fhci->active_urbs = 0;
+
+ return error;
+}
+
+/* destroy the fhci_usb structure */
+static void fhci_usb_free(void *lld)
+{
+ struct fhci_usb *usb = lld;
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (usb) {
+ config_transceiver(fhci, FHCI_OP_POWER_OFF);
+ config_transceiver(fhci, FHCI_OP_DISCONNECT);
+
+ endpoint_zero_free(usb);
+ kfree(usb->actual_frame);
+ kfree(usb);
+ }
+}
+
+/* initialize the USB */
+static u32 fhci_usb_init(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE);
+
+ usb->port_status = FHCI_PORT_DISABLED;
+ usb->max_frame_usage = FRAME_TIME_USAGE;
+ usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION;
+
+ usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL);
+ if (!usb->actual_frame) {
+ fhci_usb_free(usb);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&usb->actual_frame->tds_list);
+
+ /* initializing registers on chip, clear frame number */
+ out_be16(&fhci->pram->frame_num, 0);
+
+ /* clear rx state */
+ out_be32(&fhci->pram->rx_state, 0);
+
+ /* set mask register */
+ usb->saved_msk = (USB_E_TXB_MASK |
+ USB_E_TXE1_MASK |
+ USB_E_IDLE_MASK |
+ USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK);
+
+ out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN);
+
+ /* clearing the mask register */
+ out_be16(&usb->fhci->regs->usb_mask, 0);
+
+ /* initialing the event register */
+ out_be16(&usb->fhci->regs->usb_event, 0xffff);
+
+ if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) {
+ fhci_usb_free(usb);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* initialize the fhci_usb struct and the corresponding data staruct */
+static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb;
+
+ /* allocate memory for SCC data structure */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb) {
+ fhci_err(fhci, "no memory for SCC data struct\n");
+ return NULL;
+ }
+
+ usb->fhci = fhci;
+ usb->hc_list = fhci->hc_list;
+ usb->vroot_hub = fhci->vroot_hub;
+
+ usb->transfer_confirm = transfer_confirm_callback;
+
+ return usb;
+}
+
+static int fhci_start(struct usb_hcd *hcd)
+{
+ int ret;
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+ ret = fhci_mem_init(fhci);
+
+ fhci->usb_lld = fhci_create_lld(fhci);
+ if (!fhci->usb_lld) {
+ fhci_err(fhci, "low level driver config failed\n");
+ fhci_mem_free(fhci);
+ return -ENODEV;
+ }
+ if (fhci_usb_init(fhci)) {
+ fhci_err(fhci, "low level driver initialize failed\n");
+ fhci_mem_free(fhci);
+ return -ENODEV;
+ }
+ spin_lock_init(&fhci->lock);
+
+ /* connect the virtual root hub */
+ fhci->vroot_hub->dev_num = 1; /* this field may be needed to fix */
+ fhci->vroot_hub->hub.wHubStatus = 0;
+ fhci->vroot_hub->hub.wHubChange = 0;
+ fhci->vroot_hub->port.wPortStatus = 0;
+ fhci->vroot_hub->port.wPortChange = 0;
+
+ hcd->state = HC_STATE_RUNNING;
+
+ /*
+ * From here on, khubd concurrently accesses the root
+ * hub; drivers will be talking to enumerated devices.
+ * (On restart paths, khubd already knows about the root
+ * hub and could find work as soon as we wrote FLAG_CF.)
+ *
+ * Before this point the HC was idle/ready. After, khubd
+ * and device drivers may start it running.
+ */
+ fhci_usb_enable(fhci);
+
+ return 0;
+}
+
+static void fhci_stop(struct usb_hcd *hcd)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+ fhci_usb_disable_interrupt(fhci->usb_lld);
+ fhci_usb_disable(fhci);
+
+ fhci_usb_free(fhci->usb_lld);
+ fhci->usb_lld = NULL;
+ fhci_mem_free(fhci);
+}
+
+static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ u32 pipe = urb->pipe;
+ int i, size = 0;
+ struct urb_priv *urb_priv;
+ unsigned long flags;
+
+ switch (usb_pipetype(pipe)) {
+ case PIPE_CONTROL:
+ /* 1 td fro setup,1 for ack */
+ size = 2;
+ case PIPE_BULK:
+ /* one td for every 4096 bytes(can be upto 8k) */
+ size += urb->transfer_buffer_length / 4096;
+ /* ...add for any remaining bytes... */
+ if ((urb->transfer_buffer_length % 4096) != 0)
+ size++;
+ /* ..and maybe a zero length packet to wrap it up */
+ if (size == 0)
+ size++;
+ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
+ && (urb->transfer_buffer_length
+ % usb_maxpacket(urb->dev, pipe,
+ usb_pipeout(pipe))) != 0)
+ size++;
+ break;
+ case PIPE_ISOCHRONOUS:
+ size = urb->number_of_packets;
+ if (size <= 0)
+ return -EINVAL;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = (u32) (-EXDEV);
+ }
+ break;
+ case PIPE_INTERRUPT:
+ size = 1;
+ }
+
+ /* allocate the private part of the URB */
+ urb_priv = kzalloc(sizeof(*urb_priv), mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ /* allocate the private part of the URB */
+ urb_priv->tds = kzalloc(size * sizeof(struct td), mem_flags);
+ if (!urb_priv->tds) {
+ kfree(urb_priv);
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&fhci->lock, flags);
+ /* fill the private part of the URB */
+ urb_priv->num_of_tds = size;
+
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->error_count = 0;
+ urb->hcpriv = urb_priv;
+
+ queue_urb(fhci, urb);
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+ return 0;
+}
+
+/* dequeue FHCI URB */
+static int fhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ struct fhci_usb *usb = fhci->usb_lld;
+ unsigned long flags;
+
+ if (!urb || !urb->dev || !urb->dev->bus)
+ goto out;
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ if (usb->port_status != FHCI_PORT_DISABLED) {
+ struct urb_priv *urb_priv;
+
+ /*
+ * flag the urb's data for deletion in some upcoming
+ * SF interrupt's delete list processing
+ */
+ urb_priv = urb->hcpriv;
+
+ if (!urb_priv || (urb_priv->state == URB_DEL))
+ goto out2;
+
+ urb_priv->state = URB_DEL;
+
+ /* already pending? */
+ urb_priv->ed->state = FHCI_ED_URB_DEL;
+ } else
+ urb_complete_free(fhci, urb);
+
+out2:
+ spin_unlock_irqrestore(&fhci->lock, flags);
+out:
+ return 0;
+}
+
+static void fhci_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct fhci_hcd *fhci;
+ struct ed *ed;
+ unsigned long flags;
+
+ fhci = hcd_to_fhci(hcd);
+ spin_lock_irqsave(&fhci->lock, flags);
+ ed = ep->hcpriv;
+ if (ed) {
+ while (ed->td_head != NULL) {
+ struct td *td = remove_td_from_ed(ed);
+ urb_complete_free(fhci, td->urb);
+ }
+ recycle_empty_ed(fhci, ed);
+ ep->hcpriv = NULL;
+ }
+ spin_unlock_irqrestore(&fhci->lock, flags);
+}
+
+static int fhci_get_frame_number(struct usb_hcd *hcd)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+
+ return get_frame_num(fhci);
+}
+
+static const struct hc_driver fhci_driver = {
+ .description = "fsl,usb-fhci",
+ .product_desc = "FHCI HOST Controller",
+ .hcd_priv_size = sizeof(struct fhci_hcd),
+
+ /* generic hardware linkage */
+ .irq = fhci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /* basic lifecycle operation */
+ .start = fhci_start,
+ .stop = fhci_stop,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = fhci_urb_enqueue,
+ .urb_dequeue = fhci_urb_dequeue,
+ .endpoint_disable = fhci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = fhci_get_frame_number,
+
+ /* root hub support */
+ .hub_status_data = fhci_hub_status_data,
+ .hub_control = fhci_hub_control,
+};
+
+struct fhci_probe_info {
+ struct resource regs;
+ unsigned long pram_addr;
+ struct resource usb_irq;
+ int gpios[NUM_GPIOS];
+ enum qe_clock fullspeed_clk;
+ enum qe_clock lowspeed_clk;
+ unsigned int power_budget;
+};
+
+static int __devinit fhci_probe(struct device *dev, struct fhci_probe_info *pi)
+{
+ unsigned long ret;
+ int i;
+ struct usb_hcd *hcd = NULL;
+ struct fhci_hcd *fhci;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ hcd = usb_create_hcd(&fhci_driver, dev, dev->bus_id);
+ if (!hcd) {
+ dev_dbg(dev, "could not create hcd\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(dev, hcd);
+ fhci = hcd_to_fhci(hcd);
+
+ hcd->self.controller = dev;
+ hcd->power_budget = pi->power_budget;
+ hcd->regs = ioremap(pi->regs.start, pi->regs.end - pi->regs.start + 1);
+ fhci->regs = hcd->regs;
+ memcpy(fhci->gpios, pi->gpios, sizeof(fhci->gpios));
+
+ ret = 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;
+ }
+ }
+
+ fhci->timer = gtm_get_timer(16);
+ if (IS_ERR(fhci->timer)) {
+ ret = PTR_ERR(fhci->timer);
+ dev_err(dev, "failed to request qe timer: %li", ret);
+ goto err_get_timer;
+ }
+
+ fhci->fullspeed_clk = pi->fullspeed_clk;
+ fhci->lowspeed_clk = pi->lowspeed_clk;
+
+ ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq,
+ IRQF_DISABLED | IRQF_TIMER, "qe timer (usb)", hcd);
+ if (ret) {
+ dev_err(dev, "failed to request timer irq");
+ goto err_timer_irq;
+ }
+
+ dev_info(dev, "at 0x%p,irq %d\n", hcd->regs, pi->usb_irq.start);
+
+ /* 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:
+ gtm_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);
+ gtm_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 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..6c656bb
--- /dev/null
+++ b/drivers/usb/host/fhci-hub.c
@@ -0,0 +1,336 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/* virtual root hub specific descriptor */
+static u8 root_hub_des[] = {
+ 0x09, /* blength */
+ 0x29, /* bDescriptorType;hub-descriptor */
+ 0x01, /* bNbrPorts */
+ 0x00, /* wHubCharacteristics */
+ 0x00,
+ 0x01, /* bPwrOn2pwrGood;2ms */
+ 0x00, /* bHubContrCurrent;0mA */
+ 0x00, /* DeviceRemoveable */
+ 0xff /* PortPwrCtrlMask */
+};
+
+static void config_transceiver(struct fhci_hcd *fhci, enum fhci_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)
+ udelay(1000);
+ 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);
+ udelay(1000);
+ }
+ 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;
+ }
+
+ fhci_dbg(fhci, "<- %s: %d\n", __func__, mode);
+}
+
+/* disable the USB port by clearing the EN bit in the USBMOD register */
+static void usb_port_disable(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
+ enum fhci_port_status port_status;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_stop_sof_timer(fhci);
+
+ flush_all_transmissions(usb);
+
+ 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;
+ out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+
+ /* check if during the disconnection process attached new device */
+ if (port_status == FHCI_PORT_WAITING)
+ device_connected_interrupt(fhci);
+ usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
+ usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
+ fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* enable the USB port by setting the EN bit in the USBMOD register */
+static void usb_port_enable(void *lld)
+{
+ struct fhci_usb *usb = (struct fhci_usb *)lld;
+ struct fhci_hcd *fhci = usb->fhci;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ if ((usb->port_status != FHCI_PORT_FULL) &&
+ (usb->port_status != FHCI_PORT_LOW))
+ fhci_start_sof_timer(fhci);
+
+ usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
+ usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static void io_port_generate_reset(struct fhci_hcd *fhci)
+{
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
+ gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
+ gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
+
+ udelay(5000);
+
+ qe_gpio_set_dedicated(fhci->gpios[GPIO_USBOE], 0);
+ qe_gpio_set_dedicated(fhci->gpios[GPIO_USBTP], 0);
+ qe_gpio_set_dedicated(fhci->gpios[GPIO_USBTN], 0);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* generate the RESET condition on the bus */
+static void usb_port_reset(void *lld)
+{
+ struct fhci_usb *usb = (struct fhci_usb *)lld;
+ struct fhci_hcd *fhci = usb->fhci;
+ u8 mode;
+ u16 mask;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_stop_sof_timer(fhci);
+ /* disable the USB controller */
+ mode = in_8(&fhci->regs->usb_mod);
+ out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN));
+
+ /* disable idle interrupts */
+ mask = in_be16(&fhci->regs->usb_mask);
+ out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK));
+
+ io_port_generate_reset(fhci);
+
+ /* enable interrupt on this endpoint */
+ out_be16(&fhci->regs->usb_mask, mask);
+
+ /* enable the USB controller */
+ mode = in_8(&fhci->regs->usb_mod);
+ out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN);
+ fhci_start_sof_timer(fhci);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ int ret = 0;
+ unsigned long flags;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ udelay(1000);
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
+ USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
+ *buf = 1 << 1;
+ ret = 1;
+ fhci_dbg(fhci, "-- %s\n", __func__);
+ }
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+
+ return ret;
+}
+
+static int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ int retval = 0;
+ int len = 0;
+ struct usb_hub_status *hub_status;
+ struct usb_port_status *port_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ fhci->vroot_hub->feature &= (1 << wValue);
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_ENABLE;
+ usb_port_disable(fhci);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_ENABLE;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_SUSPEND;
+ fhci_stop_sof_timer(fhci);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ fhci->vroot_hub->port.wPortChange &=
+ ~USB_PORT_STAT_C_SUSPEND;
+ break;
+ case USB_PORT_FEAT_POWER:
+ fhci->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_POWER;
+ config_transceiver(fhci, FHCI_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;
+ }
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+ local_irq_restore(flags);
+
+ return retval;
+}
diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c
new file mode 100644
index 0000000..d9e098e
--- /dev/null
+++ b/drivers/usb/host/fhci-mem.c
@@ -0,0 +1,105 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+static void init_td(struct td *td)
+{
+ memset(td, 0, sizeof(*td));
+ INIT_LIST_HEAD(&td->node);
+ INIT_LIST_HEAD(&td->frame_lh);
+}
+
+static void init_ed(struct ed *ed)
+{
+ memset(ed, 0, sizeof(*ed));
+ INIT_LIST_HEAD(&ed->td_list);
+ INIT_LIST_HEAD(&ed->node);
+}
+
+static struct td *get_empty_td(struct fhci_hcd *fhci)
+{
+ struct td *td;
+
+ if (!list_empty(&fhci->empty_tds)) {
+ td = list_entry(fhci->empty_tds.next, struct td, node);
+ list_del(fhci->empty_tds.next);
+ } else {
+ td = kmalloc(sizeof(*td), GFP_ATOMIC);
+ if (!td)
+ fhci_err(fhci, "No memory to allocate to TD\n");
+ else
+ init_td(td);
+ }
+
+ return td;
+}
+
+static void recycle_empty_td(struct fhci_hcd *fhci, struct td *td)
+{
+ init_td(td);
+ list_add(&td->node, &fhci->empty_tds);
+}
+
+static struct ed *get_empty_ed(struct fhci_hcd *fhci)
+{
+ struct ed *ed;
+
+ if (!list_empty(&fhci->empty_eds)) {
+ ed = list_entry(fhci->empty_eds.next, struct ed, node);
+ list_del(fhci->empty_eds.next);
+ } else {
+ ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
+ if (!ed)
+ fhci_err(fhci, "No memory to allocate to ED\n");
+ else
+ init_ed(ed);
+ }
+
+ return ed;
+}
+
+static void recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed)
+{
+ init_ed(ed);
+ list_add(&ed->node, &fhci->empty_eds);
+}
+
+static struct td *td_fill(struct fhci_hcd *fhci, struct urb *urb,
+ struct urb_priv *urb_priv, struct ed *ed, u16 index,
+ enum fhci_ta_type type, int toggle, u8 *data, u32 len,
+ u16 interval, u16 start_frame, bool ioc)
+{
+ struct td *td = get_empty_td(fhci);
+
+ if (!td)
+ return NULL;
+
+ td->urb = urb;
+ td->ed = ed;
+ td->type = type;
+ td->toggle = toggle;
+ td->data = data;
+ td->len = len;
+ td->iso_index = index;
+ td->interval = interval;
+ td->start_frame = start_frame;
+ td->ioc = ioc;
+ td->status = USB_TD_OK;
+
+ urb_priv->tds[index] = td;
+
+ return td;
+}
diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c
new file mode 100644
index 0000000..493c7a5
--- /dev/null
+++ b/drivers/usb/host/fhci-q.c
@@ -0,0 +1,242 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+static void add_td_to_frame(struct fhci_time_frame *frame, struct td *td)
+{
+ list_add_tail(&td->frame_lh, &frame->tds_list);
+}
+
+static void add_tds_to_ed(struct ed *ed, struct td **td_list, int number)
+{
+ int i;
+
+ for (i = 0; i < number; i++) {
+ struct td *td = td_list[i];
+ list_add_tail(&td->node, &ed->td_list);
+ }
+ if (ed->td_head == NULL)
+ ed->td_head = td_list[0];
+}
+
+static struct td *peek_td_from_ed(struct ed *ed)
+{
+ struct td *td;
+
+ if (!list_empty(&ed->td_list))
+ td = list_entry(ed->td_list.next, struct td, node);
+ else
+ td = NULL;
+
+ return td;
+}
+
+static struct td *remove_td_from_frame(struct fhci_time_frame *frame)
+{
+ struct td *td;
+
+ if (!list_empty(&frame->tds_list)) {
+ td = list_entry(frame->tds_list.next, struct td, frame_lh);
+ list_del_init(frame->tds_list.next);
+ } else
+ td = NULL;
+
+ return td;
+}
+
+static struct td *peek_td_from_frame(struct fhci_time_frame *frame)
+{
+ struct td *td;
+
+ if (!list_empty(&frame->tds_list))
+ td = list_entry(frame->tds_list.next, struct td, frame_lh);
+ else
+ td = NULL;
+
+ return td;
+}
+static struct td *remove_td_from_ed(struct ed *ed)
+{
+ struct td *td;
+
+ if (!list_empty(&ed->td_list)) {
+ td = list_entry(ed->td_list.next, struct td, node);
+ list_del_init(ed->td_list.next);
+
+ /* if this TD was the ED's head, find next TD */
+ if (!list_empty(&ed->td_list))
+ ed->td_head = list_entry(ed->td_list.next, struct td,
+ node);
+ else
+ ed->td_head = NULL;
+ } else
+ td = NULL;
+
+ return td;
+}
+
+static struct td *remove_td_from_done_list(struct fhci_controller_list *p_list)
+{
+ struct td *td;
+
+ if (!list_empty(&p_list->done_list)) {
+ td = list_entry(p_list->done_list.next, struct td, node);
+ list_del_init(p_list->done_list.next);
+ } else
+ td = NULL;
+
+ return td;
+}
+
+static void move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed)
+{
+ struct td *td;
+
+ td = ed->td_head;
+ list_del_init(&td->node);
+
+ /* If this TD was the ED's head,find next TD */
+ if (!list_empty(&ed->td_list))
+ ed->td_head = list_entry(ed->td_list.next, struct td, node);
+ else {
+ ed->td_head = NULL;
+ ed->state = FHCI_ED_SKIP;
+ }
+ ed->toggle_carry = td->toggle;
+ list_add_tail(&td->node, &usb->hc_list->done_list);
+ if (td->ioc)
+ usb->transfer_confirm(usb->fhci);
+}
+
+/* free done FHCI URB resource such as ED and TD */
+static void free_urb_priv(struct fhci_hcd *fhci, struct urb *urb)
+{
+ int i;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct ed *ed = urb_priv->ed;
+
+ for (i = 0; i < urb_priv->num_of_tds; i++) {
+ list_del_init(&urb_priv->tds[i]->node);
+ recycle_empty_td(fhci, urb_priv->tds[i]);
+ }
+
+ /* if this TD was the ED's head,find the next TD */
+ if (!list_empty(&ed->td_list))
+ ed->td_head = list_entry(ed->td_list.next, struct td, node);
+ else
+ ed->td_head = NULL;
+
+ kfree(urb_priv->tds);
+ kfree(urb_priv);
+ urb->hcpriv = NULL;
+
+ /* if this TD was the ED's head,find next TD */
+ if (ed->td_head == NULL)
+ list_del_init(&ed->node);
+ fhci->active_urbs--;
+}
+
+/* this routine called to complete and free done URB */
+static void urb_complete_free(struct fhci_hcd *fhci, struct urb *urb)
+{
+ free_urb_priv(fhci, urb);
+
+ if (urb->status == -EINPROGRESS) {
+ if (urb->actual_length != urb->transfer_buffer_length &&
+ urb->transfer_flags & URB_SHORT_NOT_OK)
+ urb->status = -EREMOTEIO;
+ else
+ urb->status = 0;
+ }
+ spin_unlock(&fhci->lock);
+
+ usb_hcd_giveback_urb(fhci_to_hcd(fhci), urb, urb->status);
+
+ spin_lock(&fhci->lock);
+}
+
+/*
+ * caculate transfer length/stats and update the urb
+ * Precondition: irqsafe(only for urb-?status locking)
+ */
+static void done_td(struct urb *urb, struct td *td)
+{
+ struct ed *ed = td->ed;
+ u32 cc = td->status;
+
+ /* ISO...drivers see per-TD length/status */
+ if (ed->mode == FHCI_TF_ISO) {
+ u32 len;
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK &&
+ cc == USB_TD_RX_DATA_UNDERUN))
+ cc = USB_TD_OK;
+
+ if (usb_pipeout(urb->pipe))
+ len = urb->iso_frame_desc[td->iso_index].length;
+ else
+ len = td->actual_len;
+
+ urb->actual_length += len;
+ urb->iso_frame_desc[td->iso_index].actual_length = len;
+ urb->iso_frame_desc[td->iso_index].status =
+ status_to_error(cc);
+ }
+
+ /* BULK,INT,CONTROL... drivers see aggregate length/status,
+ * except that "setup" bytes aren't counted and "short" transfers
+ * might not be reported as errors.
+ */
+ else {
+ if (td->error_cnt >= 3)
+ urb->error_count = 3;
+
+ /* control endpoint only have soft stalls */
+
+ /* update packet status if needed(short may be ok) */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK) &&
+ cc == USB_TD_RX_DATA_UNDERUN) {
+ ed->state = FHCI_ED_OPER;
+ cc = USB_TD_OK;
+ }
+ if (cc != USB_TD_OK) {
+ if (urb->status == -EINPROGRESS)
+ urb->status = status_to_error(cc);
+ }
+
+ /* count all non-empty packets except control SETUP packet */
+ if (td->type != FHCI_TA_SETUP || td->iso_index != 0)
+ urb->actual_length += td->actual_len;
+ }
+}
+
+/* there are some pedning request to unlink */
+static void del_ed_list(struct fhci_hcd *fhci, struct ed *ed)
+{
+ struct td *td = peek_td_from_ed(ed);
+ struct urb *urb = td->urb;
+ struct urb_priv *urb_priv = urb->hcpriv;
+
+ if (urb_priv->state == URB_DEL) {
+ td = remove_td_from_ed(ed);
+ /* HC may have partly processed this TD */
+ if (td->status != USB_TD_INPROGRESS)
+ done_td(urb, td);
+
+ /* URB is done;clean up */
+ if (++(urb_priv->tds_cnt) == urb_priv->num_of_tds)
+ urb_complete_free(fhci, urb);
+ }
+}
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
new file mode 100644
index 0000000..32996a0
--- /dev/null
+++ b/drivers/usb/host/fhci-sched.c
@@ -0,0 +1,865 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+static void recycle_frame(struct fhci_usb *usb, struct packet *pkt)
+{
+ pkt->data = NULL;
+ pkt->len = 0;
+ pkt->status = USB_TD_OK;
+ pkt->info = 0;
+ pkt->priv_data = NULL;
+
+ cq_put(usb->ep0->empty_frame_Q, pkt);
+}
+
+/* confirm submitted packet */
+static void transaction_confirm(struct fhci_usb *usb, struct packet *pkt)
+{
+ struct td *td;
+ struct packet *td_pkt;
+ struct ed *ed;
+ u32 trans_len;
+ bool td_done = false;
+
+ td = remove_td_from_frame(usb->actual_frame);
+ td_pkt = td->pkt;
+ trans_len = pkt->len;
+ td->status = pkt->status;
+ if (td->type == FHCI_TA_IN && td_pkt->info & PKT_DUMMY_PACKET) {
+ if ((td->data + td->actual_len) && trans_len)
+ memcpy(td->data + td->actual_len, pkt->data,
+ trans_len);
+ cq_put(usb->ep0->dummy_packets_Q, pkt->data);
+ }
+
+ recycle_frame(usb, pkt);
+
+ ed = td->ed;
+ if (ed->mode == FHCI_TF_ISO) {
+ if (ed->td_list.next->next != &ed->td_list) {
+ struct td *td_next =
+ list_entry(ed->td_list.next->next, struct td,
+ node);
+
+ td_next->start_frame = usb->actual_frame->frame_num;
+ }
+ td->actual_len = trans_len;
+ td_done = true;
+ } else if ((td->status & USB_TD_ERROR) &&
+ !(td->status & USB_TD_TX_ER_NAK)) {
+ /*
+ * There was an error on the transaction (but not NAK).
+ * If it is fatal error (data underrun, stall, bad pid or 3
+ * errors exceeded), mark this TD as done.
+ */
+ if ((td->status & USB_TD_RX_DATA_UNDERUN) ||
+ (td->status & USB_TD_TX_ER_STALL) ||
+ (td->status & USB_TD_RX_ER_PID) ||
+ (++td->error_cnt >= 3)) {
+ ed->state = FHCI_ED_HALTED;
+ td_done = true;
+
+ if (td->status & USB_TD_RX_DATA_UNDERUN) {
+ fhci_dbg(usb->fhci, "td err fu\n");
+ td->toggle = !td->toggle;
+ td->actual_len += trans_len;
+ } else {
+ fhci_dbg(usb->fhci, "td err f!u\n");
+ }
+ } else {
+ fhci_dbg(usb->fhci, "td err !f\n");
+ /* it is not a fatal error -retry this transaction */
+ td->nak_cnt = 0;
+ td->error_cnt++;
+ td->status = USB_TD_OK;
+ }
+ } else if (td->status & USB_TD_TX_ER_NAK) {
+ /* there was a NAK response */
+ fhci_vdbg(usb->fhci, "td nack\n");
+ td->nak_cnt++;
+ td->error_cnt = 0;
+ td->status = USB_TD_OK;
+ } else {
+ /* there was no error on transaction */
+ td->error_cnt = 0;
+ td->nak_cnt = 0;
+ td->toggle = !td->toggle;
+ td->actual_len += trans_len;
+
+ if (td->len == td->actual_len)
+ td_done = true;
+ }
+
+ if (td_done)
+ move_td_from_ed_to_done_list(usb, ed);
+}
+
+/*
+ * Flush all transmitted packets from BDs
+ * This routine is called when disabling the USB port to flush all
+ * transmissions that are allready scheduled in the BDs
+ */
+static void flush_all_transmissions(struct fhci_usb *usb)
+{
+ u8 mode;
+ struct td *td;
+
+ mode = in_8(&usb->fhci->regs->usb_mod);
+ clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN);
+
+ flush_bds(usb);
+
+ while ((td = peek_td_from_frame(usb->actual_frame)) != NULL) {
+ struct packet *pkt = td->pkt;
+
+ pkt->status = USB_TD_TX_ER_TIMEOUT;
+ transaction_confirm(usb, pkt);
+ }
+
+ usb->actual_frame->frame_status = FRAME_END_TRANSMISSION;
+
+ /* reset the event register */
+ out_be16(&usb->fhci->regs->usb_event, 0xffff);
+ /* enable the USB controller */
+ out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
+}
+
+/*
+ * This function forms the packet and transmit the packet. This function
+ * will handle all endpoint type:ISO,interrupt,control and bulk
+ */
+static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td)
+{
+ u32 fw_transaction_time, len = 0;
+ struct packet *pkt;
+ u8 *data = NULL;
+
+ /* calcalate data address,len and toggle and then add the transaction */
+ if (td->toggle == USB_TD_TOGGLE_CARRY)
+ td->toggle = ed->toggle_carry;
+
+ switch (ed->mode) {
+ case FHCI_TF_ISO:
+ len = td->len;
+ if (td->type != FHCI_TA_IN)
+ data = td->data;
+ break;
+ case FHCI_TF_CTRL:
+ case FHCI_TF_BULK:
+ len = min(td->len - td->actual_len, ed->max_pkt_size);
+ if (!((td->type == FHCI_TA_IN) &&
+ ((len + td->actual_len) == td->len)))
+ data = td->data + td->actual_len;
+ break;
+ case FHCI_TF_INTR:
+ len = min(td->len, ed->max_pkt_size);
+ if (!((td->type == FHCI_TA_IN) &&
+ ((td->len + CRC_SIZE) >= ed->max_pkt_size)))
+ data = td->data;
+ break;
+ default:
+ break;
+ }
+
+ if (usb->port_status == FHCI_PORT_FULL)
+ fw_transaction_time = (((len + PROTOCOL_OVERHEAD) * 11) >> 4);
+ else
+ fw_transaction_time = ((len + PROTOCOL_OVERHEAD) * 6);
+
+ /* check if there's enough space in this frame to submit this TD */
+ if (usb->actual_frame->total_bytes + len + PROTOCOL_OVERHEAD >=
+ usb->max_bytes_per_frame) {
+ fhci_vdbg(usb->fhci, "not enough space in this frame: "
+ "%d %d %d\n", usb->actual_frame->total_bytes, len,
+ usb->max_bytes_per_frame);
+ return -1;
+ }
+
+ /* check if there's enough time in this frame to submit this TD */
+ if (usb->actual_frame->frame_status != FRAME_IS_PREPARED &&
+ (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION ||
+ (fw_transaction_time + usb->sw_transaction_time >=
+ 1000 - get_sof_timer_count(usb)))) {
+ fhci_dbg(usb->fhci, "not enough time in this frame\n");
+ return -1;
+ }
+
+ /* update frame object fields before transmitting */
+ pkt = cq_get(usb->ep0->empty_frame_Q);
+ if (!pkt) {
+ fhci_dbg(usb->fhci, "there is no empty frame\n");
+ return -1;
+ }
+ td->pkt = pkt;
+
+ pkt->info = 0;
+ if (data == NULL) {
+ data = cq_get(usb->ep0->dummy_packets_Q);
+ BUG_ON(!data);
+ pkt->info = PKT_DUMMY_PACKET;
+ }
+ pkt->data = data;
+ pkt->len = len;
+ pkt->status = USB_TD_OK;
+ /* update TD status field before transmitting */
+ td->status = USB_TD_INPROGRESS;
+ /* update actual frame time object with the actual transmission */
+ usb->actual_frame->total_bytes += (len + PROTOCOL_OVERHEAD);
+ add_td_to_frame(usb->actual_frame, td);
+
+ if (usb->port_status != FHCI_PORT_FULL &&
+ usb->port_status != FHCI_PORT_LOW) {
+ pkt->status = USB_TD_TX_ER_TIMEOUT;
+ pkt->len = 0;
+ transaction_confirm(usb, pkt);
+ } else if (host_transaction(usb, pkt, td->type, ed->dev_addr,
+ ed->ep_addr, ed->mode, ed->speed, td->toggle)) {
+ /* remove TD from actual frame */
+ list_del_init(&td->frame_lh);
+ td->status = USB_TD_OK;
+ if (pkt->info & PKT_DUMMY_PACKET)
+ cq_put(usb->ep0->dummy_packets_Q, pkt->data);
+ recycle_frame(usb, pkt);
+ usb->actual_frame->total_bytes -= (len + PROTOCOL_OVERHEAD);
+ fhci_err(usb->fhci, "host transaction failed\n");
+ return -1;
+ }
+
+ return len;
+}
+
+/*
+ * This function goes through the endpoint list and schedules the
+ * transactions within this list
+ */
+static int scan_ed_list(struct fhci_usb *usb,
+ struct list_head *list, enum fhci_tf_mode list_type)
+{
+ static const int frame_part[4] = {
+ [FHCI_TF_CTRL] = MAX_BYTES_PER_FRAME,
+ [FHCI_TF_ISO] = (MAX_BYTES_PER_FRAME *
+ MAX_PERIODIC_FRAME_USAGE) / 100,
+ [FHCI_TF_BULK] = MAX_BYTES_PER_FRAME,
+ [FHCI_TF_INTR] = (MAX_BYTES_PER_FRAME *
+ MAX_PERIODIC_FRAME_USAGE) / 100
+ };
+ struct list_head *ed_lh = NULL;
+ struct ed *ed;
+ struct td *td;
+ int ans = 1;
+ u32 save_transaction_time = usb->sw_transaction_time;
+
+ list_for_each(ed_lh, list) {
+ ed = list_entry(ed_lh, struct ed, node);
+ td = ed->td_head;
+
+ if (!td || (td && td->status == USB_TD_INPROGRESS))
+ continue;
+
+ if (ed->state != FHCI_ED_OPER) {
+ if (ed->state == FHCI_ED_URB_DEL) {
+ td->status = USB_TD_OK;
+ move_td_from_ed_to_done_list(usb, ed);
+ ed->state = FHCI_ED_SKIP;
+ }
+ continue;
+ }
+
+ /*
+ * if it isn't interrupt pipe or it is not iso pipe and the
+ * interval time passed
+ */
+ if ((list_type == FHCI_TF_INTR || list_type == FHCI_TF_ISO) &&
+ (((usb->actual_frame->frame_num -
+ td->start_frame) & 0x7ff) < td->interval))
+ continue;
+
+ if (add_packet(usb, ed, td) < 0)
+ continue;
+
+ /* update time stamps in the TD */
+ td->start_frame = usb->actual_frame->frame_num;
+ usb->sw_transaction_time += save_transaction_time;
+
+ if (usb->actual_frame->total_bytes >=
+ usb->max_bytes_per_frame) {
+ usb->actual_frame->frame_status =
+ FRAME_DATA_END_TRANSMISSION;
+ push_dummy_bd(usb->ep0);
+ ans = 0;
+ break;
+ }
+
+ if (usb->actual_frame->total_bytes >= frame_part[list_type])
+ break;
+ }
+
+ /* be fair to each ED(move list head around) */
+ move_head_to_tail(list);
+ usb->sw_transaction_time = save_transaction_time;
+
+ return ans;
+}
+
+static u32 rotate_frames(struct fhci_usb *usb)
+{
+ struct fhci_hcd *fhci = usb->fhci;
+
+ if (!list_empty(&usb->actual_frame->tds_list)) {
+ if ((((in_be16(&fhci->pram->frame_num) & 0x07ff) -
+ usb->actual_frame->frame_num) & 0x7ff) > 5)
+ flush_actual_frame(usb);
+ else
+ return -EINVAL;
+ }
+
+ usb->actual_frame->frame_status = FRAME_IS_PREPARED;
+ usb->actual_frame->frame_num = in_be16(&fhci->pram->frame_num) & 0x7ff;
+ usb->actual_frame->total_bytes = 0;
+
+ return 0;
+}
+
+/*
+ * This function schedule the USB transaction and will process the
+ * endpoint in the following order: iso, interrupt, control and bulk.
+ */
+static void schedule_transactions(struct fhci_usb *usb)
+{
+ int left = 1;
+
+ if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
+ if (rotate_frames(usb) != 0)
+ return;
+
+ if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)
+ return;
+
+ if (usb->actual_frame->total_bytes == 0) {
+ /*
+ * schedule the next available ISO transfer
+ *or next stage of the ISO transfer
+ */
+ scan_ed_list(usb, &usb->hc_list->iso_list, FHCI_TF_ISO);
+
+ /*
+ * schedule the next available interrupt transfer or
+ * the next stage of the interrupt transfer
+ */
+ scan_ed_list(usb, &usb->hc_list->intr_list, FHCI_TF_INTR);
+
+ /*
+ * schedule the next available control transfer
+ * or the next stage of the control transfer
+ */
+ left = scan_ed_list(usb, &usb->hc_list->ctrl_list,
+ FHCI_TF_CTRL);
+ }
+
+ /*
+ * schedule the next available bulk transfer or the next stage of the
+ * bulk transfer
+ */
+ if (left > 0)
+ scan_ed_list(usb, &usb->hc_list->bulk_list, FHCI_TF_BULK);
+}
+
+/* Handles SOF interrupt */
+static void sof_interrupt(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ if ((usb->port_status == FHCI_PORT_DISABLED) &&
+ (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_CONNECTION) &&
+ !(usb->vroot_hub->port.wPortChange & USB_PORT_STAT_C_CONNECTION)) {
+ if (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_LOW_SPEED)
+ usb->port_status = FHCI_PORT_LOW;
+ else
+ usb->port_status = FHCI_PORT_FULL;
+ /* Disable IDLE */
+ usb->saved_msk &= ~USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+ }
+
+ gtm_reset_ref_timer_16(fhci->timer, 2000000, usb->max_frame_usage,
+ false);
+
+ host_transmit_actual_frame(usb);
+ usb->actual_frame->frame_status = FRAME_IS_TRANSMITTED;
+
+ schedule_transactions(usb);
+}
+
+/* Handles device disconnected interrupt on port */
+static void device_disconnected_interrupt(struct fhci_hcd *fhci)
+{
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_usb_disable_interrupt(usb);
+ clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+ usb->port_status = FHCI_PORT_DISABLED;
+
+ /* Enable IDLE since we want to know if something comes along */
+ usb->saved_msk |= USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk);
+
+ usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION;
+ usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ usb->max_bytes_per_frame = 0;
+ fhci_usb_enable_interrupt(usb);
+
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+/* detect a new device connected on the USB port */
+static void device_connected_interrupt(struct fhci_hcd *fhci)
+{
+
+ struct fhci_usb *usb = fhci->usb_lld;
+ int state;
+ int ret;
+
+ fhci_dbg(fhci, "-> %s\n", __func__);
+
+ fhci_usb_disable_interrupt(usb);
+ state = fhci_ioports_check_bus_state(fhci);
+
+ /* low-speed device was connected to the USB port */
+ if (state == 1) {
+ ret = qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3);
+ if (ret) {
+ fhci_warn(fhci, "Low-Speed device is not supported, "
+ "try use BRGx\n");
+ goto out;
+ }
+
+ usb->port_status = FHCI_PORT_LOW;
+ setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+ usb->vroot_hub->port.wPortStatus |=
+ (USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_CONNECTION);
+ usb->vroot_hub->port.wPortChange |=
+ USB_PORT_STAT_C_CONNECTION;
+ usb->max_bytes_per_frame =
+ (MAX_BYTES_PER_FRAME >> 3) - 7;
+ usb_port_enable(usb);
+ } else if (state == 2) {
+ ret = qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK);
+ if (ret) {
+ fhci_warn(fhci, "Full-Speed device is not supported, "
+ "try use CLKx\n");
+ goto out;
+ }
+
+ usb->port_status = FHCI_PORT_FULL;
+ clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS);
+ usb->vroot_hub->port.wPortStatus &=
+ ~USB_PORT_STAT_LOW_SPEED;
+ usb->vroot_hub->port.wPortStatus |=
+ USB_PORT_STAT_CONNECTION;
+ usb->vroot_hub->port.wPortChange |=
+ USB_PORT_STAT_C_CONNECTION;
+ usb->max_bytes_per_frame = (MAX_BYTES_PER_FRAME - 15);
+ usb_port_enable(usb);
+ }
+out:
+ fhci_usb_enable_interrupt(usb);
+ fhci_dbg(fhci, "<- %s\n", __func__);
+}
+
+static irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd)
+{
+ struct usb_hcd *hcd = _hcd;
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ struct fhci_usb *usb = fhci->usb_lld;
+
+ spin_lock(&fhci->lock);
+
+ gtm_reset_ref_timer_16(fhci->timer, 2000000, 1000, false);
+
+ if (usb->actual_frame->frame_status == FRAME_IS_TRANSMITTED) {
+ usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+ push_dummy_bd(usb->ep0);
+ }
+
+ schedule_transactions(usb);
+
+ spin_unlock(&fhci->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* Cancel transmission on the USB endpoint */
+static void abort_transmission(struct fhci_usb *usb)
+{
+ fhci_dbg(usb->fhci, "-> %s\n", __func__);
+ /* issue stop Tx command */
+ qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
+ /* flush Tx FIFOs */
+ out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO);
+ udelay(1000);
+ /* reset Tx BDs */
+ flush_bds(usb);
+ /* issue restart Tx command */
+ qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0);
+ fhci_dbg(usb->fhci, "<- %s\n", __func__);
+}
+
+static irqreturn_t fhci_irq(struct usb_hcd *hcd)
+{
+ struct fhci_hcd *fhci = hcd_to_fhci(hcd);
+ struct fhci_usb *usb;
+ u16 usb_er = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fhci->lock, flags);
+
+ usb = fhci->usb_lld;
+
+ usb_er |= in_be16(&usb->fhci->regs->usb_event) &
+ in_be16(&usb->fhci->regs->usb_mask);
+
+ /* clear event bits for next time */
+ out_be16(&usb->fhci->regs->usb_event, usb_er);
+
+ fhci_dbg_isr(fhci, usb_er);
+
+ if (usb_er & USB_E_RESET_MASK) {
+ if ((usb->port_status == FHCI_PORT_FULL) ||
+ (usb->port_status == FHCI_PORT_LOW)) {
+ device_disconnected_interrupt(fhci);
+ usb_er &= ~USB_E_IDLE_MASK;
+ } else if (usb->port_status == FHCI_PORT_WAITING) {
+ usb->port_status = FHCI_PORT_DISCONNECTING;
+
+ /* Turn on IDLE since we want to disconnect */
+ usb->saved_msk |= USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_event,
+ usb->saved_msk);
+ } else if (usb->port_status == FHCI_PORT_DISABLED) {
+ if (fhci_ioports_check_bus_state(fhci) == 1 &&
+ usb->port_status != FHCI_PORT_LOW &&
+ usb->port_status != FHCI_PORT_FULL)
+ device_connected_interrupt(fhci);
+ }
+ usb_er &= ~USB_E_RESET_MASK;
+ }
+
+ if (usb_er & USB_E_MSF_MASK) {
+ abort_transmission(fhci->usb_lld);
+ usb_er &= ~USB_E_MSF_MASK;
+ }
+
+ if (usb_er & (USB_E_SOF_MASK | USB_E_SFT_MASK)) {
+ sof_interrupt(fhci);
+ usb_er &= ~(USB_E_SOF_MASK | USB_E_SFT_MASK);
+ }
+
+ if (usb_er & USB_E_TXB_MASK) {
+ tx_conf_interrupt(fhci->usb_lld);
+ usb_er &= ~USB_E_TXB_MASK;
+ }
+
+ if (usb_er & USB_E_TXE1_MASK) {
+ tx_conf_interrupt(fhci->usb_lld);
+ usb_er &= ~USB_E_TXE1_MASK;
+ }
+
+ if (usb_er & USB_E_IDLE_MASK) {
+ if (usb->port_status == FHCI_PORT_DISABLED &&
+ usb->port_status != FHCI_PORT_LOW &&
+ usb->port_status != FHCI_PORT_FULL) {
+ usb_er &= ~USB_E_RESET_MASK;
+ device_connected_interrupt(fhci);
+ } else if (usb->port_status ==
+ FHCI_PORT_DISCONNECTING) {
+ /* XXX usb->port_status = FHCI_PORT_WAITING; */
+ /* Disable IDLE */
+ usb->saved_msk &= ~USB_E_IDLE_MASK;
+ out_be16(&usb->fhci->regs->usb_mask,
+ usb->saved_msk);
+ } else {
+ fhci_dbg_isr(fhci, -1);
+ }
+
+ usb_er &= ~USB_E_IDLE_MASK;
+ }
+
+ spin_unlock_irqrestore(&fhci->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * Process normal completions(error or sucess) and clean the schedule.
+ *
+ * This is the main path for handing urbs back to drivers. The only other patth
+ * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning
+ * the (re-reversed) done list as this does.
+ */
+static void process_done_list(unsigned long data)
+{
+ struct urb *urb;
+ struct ed *ed;
+ struct td *td;
+ struct urb_priv *urb_priv;
+ struct fhci_hcd *fhci = (struct fhci_hcd *)data;
+
+ disable_irq(fhci->timer->irq);
+ disable_irq(fhci_to_hcd(fhci)->irq);
+ spin_lock(&fhci->lock);
+
+ td = remove_td_from_done_list(fhci->hc_list);
+ while (td != NULL) {
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+ ed = td->ed;
+
+ /* update URB's length and status from TD */
+ done_td(urb, td);
+ urb_priv->tds_cnt++;
+
+ /*
+ * if all this urb's TDs are done, call complete()
+ * Interrupt transfers are the onley special case:
+ * they are reissued,until "deleted" by usb_unlink_urb
+ * (real work done in a SOF intr, by process_del_list)
+ */
+ if (urb_priv->tds_cnt == urb_priv->num_of_tds) {
+ urb_complete_free(fhci, urb);
+ } else if (urb_priv->state == URB_DEL &&
+ ed->state == FHCI_ED_SKIP) {
+ del_ed_list(fhci, ed);
+ ed->state = FHCI_ED_OPER;
+ } else if (ed->state == FHCI_ED_HALTED) {
+ urb_priv->state = URB_DEL;
+ ed->state = FHCI_ED_URB_DEL;
+ del_ed_list(fhci, ed);
+ ed->state = FHCI_ED_OPER;
+ }
+
+ td = remove_td_from_done_list(fhci->hc_list);
+ }
+
+ spin_unlock(&fhci->lock);
+ enable_irq(fhci->timer->irq);
+ enable_irq(fhci_to_hcd(fhci)->irq);
+}
+
+static DECLARE_TASKLET(fhci_tasklet, process_done_list, 0);
+
+/* transfer complted callback */
+static u32 transfer_confirm_callback(struct fhci_hcd *fhci)
+{
+ if (!fhci->process_done_task->state)
+ tasklet_schedule(fhci->process_done_task);
+ return 0;
+}
+
+/*
+ * adds urb to the endpoint descriptor list
+ * arguments:
+ * fhci data structure for the Low level host controller
+ * ep USB Host endpoint data structure
+ * urb USB request block data structure
+ */
+static void queue_urb(struct fhci_hcd *fhci, struct urb *urb)
+{
+ struct ed *ed = urb->ep->hcpriv;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ u32 data_len = urb->transfer_buffer_length;
+ int urb_state = 0;
+ int toggle = 0;
+ struct td *td;
+ u8 *data;
+ u16 cnt = 0;
+
+ if (ed == NULL) {
+ ed = get_empty_ed(fhci);
+ ed->dev_addr = usb_pipedevice(urb->pipe);
+ ed->ep_addr = usb_pipeendpoint(urb->pipe);
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ ed->mode = FHCI_TF_CTRL;
+ break;
+ case PIPE_BULK:
+ ed->mode = FHCI_TF_BULK;
+ break;
+ case PIPE_INTERRUPT:
+ ed->mode = FHCI_TF_INTR;
+ break;
+ case PIPE_ISOCHRONOUS:
+ ed->mode = FHCI_TF_ISO;
+ break;
+ default:
+ break;
+ }
+ ed->speed = (urb->dev->speed == USB_SPEED_LOW) ?
+ FHCI_LOW_SPEED : FHCI_FULL_SPEED;
+ ed->max_pkt_size = usb_maxpacket(urb->dev,
+ urb->pipe, usb_pipeout(urb->pipe));
+ urb->ep->hcpriv = ed;
+ fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n",
+ ed->speed, ed->max_pkt_size);
+ }
+
+ /* for ISO transfer calculate start frame index */
+ if (ed->mode == FHCI_TF_ISO && urb->transfer_flags & URB_ISO_ASAP)
+ urb->start_frame = ed->td_head ? ed->last_iso + 1 :
+ get_frame_num(fhci);
+
+ /*
+ * OHCI handles the DATA toggle itself,we just use the USB
+ * toggle bits
+ */
+ if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)))
+ toggle = USB_TD_TOGGLE_CARRY;
+ else {
+ toggle = USB_TD_TOGGLE_DATA0;
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 1);
+ }
+
+ urb_priv->tds_cnt = 0;
+ urb_priv->ed = ed;
+ if (data_len > 0)
+ data = urb->transfer_buffer;
+ else
+ data = NULL;
+
+ switch (ed->mode) {
+ case FHCI_TF_BULK:
+ if (urb->transfer_flags & URB_ZERO_PACKET &&
+ urb->transfer_buffer_length > 0 &&
+ ((urb->transfer_buffer_length %
+ usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe))) == 0))
+ urb_state = US_BULK0;
+ while (data_len > 4096) {
+ td = td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ cnt ? USB_TD_TOGGLE_CARRY :
+ toggle,
+ data, 4096, 0, 0, true);
+ data += 4096;
+ data_len -= 4096;
+ cnt++;
+ }
+
+ td = td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
+ cnt ? USB_TD_TOGGLE_CARRY : toggle,
+ data, data_len, 0, 0, true);
+ cnt++;
+
+ if (urb->transfer_flags & URB_ZERO_PACKET &&
+ cnt < urb_priv->num_of_tds) {
+ td = td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true);
+ cnt++;
+ }
+ break;
+ case FHCI_TF_INTR:
+ urb->start_frame = get_frame_num(fhci) + 1;
+ td = td_fill(fhci, urb, urb_priv, ed, cnt++,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA0, data, data_len,
+ urb->interval, urb->start_frame, true);
+ break;
+ case FHCI_TF_CTRL:
+ ed->dev_addr = usb_pipedevice(urb->pipe);
+ ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ td = td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP,
+ USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true);
+
+ if (data_len > 0) {
+ td = td_fill(fhci, urb, urb_priv, ed, cnt++,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA1, data, data_len, 0, 0,
+ true);
+ }
+ td = td_fill(fhci, urb, urb_priv, ed, cnt++,
+ usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT,
+ USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+ urb_state = US_CTRL_SETUP;
+ break;
+ case FHCI_TF_ISO:
+ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+ u16 frame = urb->start_frame;
+
+ /*
+ * FIXME scheduling should handle frame counter
+ * roll-around ... exotic case (and OHCI has
+ * a 2^16 iso range, vs other HCs max of 2^10)
+ */
+ frame += cnt * urb->interval;
+ frame &= 0x07ff;
+ td = td_fill(fhci, urb, urb_priv, ed, cnt,
+ usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA0,
+ data + urb->iso_frame_desc[cnt].offset,
+ urb->iso_frame_desc[cnt].length,
+ urb->interval, frame, true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * set the state of URB
+ * control pipe:3 states -- setup,data,status
+ * interrupt and bulk pipe:1 state -- data
+ */
+ urb->pipe &= ~0x1f;
+ urb->pipe |= urb_state & 0x1f;
+
+ urb_priv->state = URB_INPROGRESS;
+
+ if (!ed->td_head) {
+ ed->state = FHCI_ED_OPER;
+ switch (ed->mode) {
+ case FHCI_TF_CTRL:
+ list_add(&ed->node, &fhci->hc_list->ctrl_list);
+ break;
+ case FHCI_TF_BULK:
+ list_add(&ed->node, &fhci->hc_list->bulk_list);
+ break;
+ case FHCI_TF_INTR:
+ list_add(&ed->node, &fhci->hc_list->intr_list);
+ break;
+ case FHCI_TF_ISO:
+ list_add(&ed->node, &fhci->hc_list->iso_list);
+ break;
+ default:
+ break;
+ }
+ }
+
+ add_tds_to_ed(ed, urb_priv->tds, urb_priv->num_of_tds);
+ fhci->active_urbs++;
+}
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
new file mode 100644
index 0000000..c6f37f3
--- /dev/null
+++ b/drivers/usb/host/fhci-tds.c
@@ -0,0 +1,631 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define DUMMY_BD_BUFFER 0xdeadbeef
+#define DUMMY2_BD_BUFFER 0xbaadf00d
+
+/* Transaction Descriptors bits */
+#define TD_R 0x8000 /* ready bit */
+#define TD_W 0x2000 /* wrap bit */
+#define TD_I 0x1000 /* interrupt on completion */
+#define TD_L 0x0800 /* last */
+#define TD_TC 0x0400 /* transmit CRC */
+#define TD_CNF 0x0200 /* CNF - Must be always 1 */
+#define TD_LSP 0x0100 /* Low-speed transaction */
+#define TD_PID 0x00c0 /* packet id */
+#define TD_RXER 0x0020 /* Rx error or not */
+
+#define TD_NAK 0x0010 /* No ack. */
+#define TD_STAL 0x0008 /* Stall recieved */
+#define TD_TO 0x0004 /* time out */
+#define TD_UN 0x0002 /* underrun */
+#define TD_NO 0x0010 /* Rx Non Octet Aligned Packet */
+#define TD_AB 0x0008 /* Frame Aborted */
+#define TD_CR 0x0004 /* CRC Error */
+#define TD_OV 0x0002 /* Overrun */
+#define TD_BOV 0x0001 /* Buffer Overrun */
+
+#define TD_ERRORS (TD_NAK | TD_STAL | TD_TO | TD_UN | \
+ TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV)
+
+#define TD_PID_DATA0 0x0080 /* Data 0 toggle */
+#define TD_PID_DATA1 0x00c0 /* Data 1 toggle */
+#define TD_PID_TOGGLE 0x00c0 /* Data 0/1 toggle mask */
+
+#define TD_TOK_SETUP 0x0000
+#define TD_TOK_OUT 0x4000
+#define TD_TOK_IN 0x8000
+#define TD_ISO 0x1000
+#define TD_ENDP 0x0780
+#define TD_ADDR 0x007f
+
+#define TD_ENDP_SHIFT 7
+
+struct usb_td {
+ __be16 status;
+ __be16 length;
+ __be32 buf_ptr;
+ __be16 extra;
+ __be16 reserved;
+};
+
+struct endpoint {
+ /* Pointer to ep parameter RAM */
+ struct fhci_ep_pram __iomem *ep_pram_ptr;
+
+ /* Host transactions */
+ struct usb_td __iomem *td_base; /* first TD in the ring */
+ struct usb_td __iomem *conf_td; /* next TD for confirm after transac */
+ struct usb_td __iomem *empty_td; /* next TD for new transaction request */
+ void *empty_frame_Q; /* Empty frames list to use */
+ void *conf_frame_Q; /* frames passed to TDs,waiting for tx */
+ void *dummy_packets_Q; /* dummy packets for the CRC overun */
+
+ bool already_pushed_dummy_bd;
+};
+
+static struct usb_td __iomem *next_bd(struct usb_td __iomem *base,
+ struct usb_td __iomem *td,
+ u16 status)
+{
+ if (status & TD_W)
+ return base;
+ else
+ return ++td;
+}
+
+static void push_dummy_bd(struct endpoint *ep)
+{
+ if (ep->already_pushed_dummy_bd == false) {
+ u16 td_status = in_be16(&ep->empty_td->status);
+
+ out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER);
+ /* get the next TD in the ring */
+ ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
+ ep->already_pushed_dummy_bd = true;
+ }
+}
+
+/* destroy an USB endpoint */
+static void endpoint_zero_free(struct fhci_usb *usb)
+{
+ struct endpoint *ep;
+ int size;
+
+ ep = usb->ep0;
+ if (ep) {
+ if (ep->td_base)
+ 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
+ */
+static u32 create_endpoint(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
+ u32 ring_len)
+{
+ struct endpoint *ep;
+ struct usb_td __iomem *td;
+ unsigned long ep_offset;
+ char *err_for = "enpoint PRAM";
+ int ep_mem_size;
+ u32 i;
+
+ /* we need at least 3 TDs in the ring */
+ if (!(ring_len > 2)) {
+ fhci_err(usb->fhci, "illegal TD ring length parameters\n");
+ return -EINVAL;
+ }
+
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ goto err;
+
+ ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram);
+ ep_offset = 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(ring_len + 2);
+ ep->empty_frame_Q = cq_new(ring_len + 2);
+ ep->dummy_packets_Q = cq_new(ring_len + 2);
+ if (!ep->conf_frame_Q || !ep->empty_frame_Q || !ep->dummy_packets_Q) {
+ err_for = "frame_queues";
+ goto err;
+ }
+
+ for (i = 0; i < (ring_len + 1); i++) {
+ struct packet *pkt;
+ u8 *buff;
+
+ pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt) {
+ err_for = "frame";
+ goto err;
+ }
+
+ buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL);
+ if (!buff) {
+ kfree(pkt);
+ err_for = "buffer";
+ goto err;
+ }
+ cq_put(ep->empty_frame_Q, pkt);
+ cq_put(ep->dummy_packets_Q, buff);
+ }
+
+ /* we put the endpoint parameter RAM right behind the TD ring */
+ ep->ep_pram_ptr = (void __iomem *)ep->td_base + sizeof(*td) * ring_len;
+
+ ep->conf_td = ep->td_base;
+ ep->empty_td = ep->td_base;
+
+ ep->already_pushed_dummy_bd = false;
+
+ /* initialize tds */
+ td = ep->td_base;
+ for (i = 0; i < ring_len; i++) {
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->status, 0);
+ out_be16(&td->length, 0);
+ out_be16(&td->extra, 0);
+ td++;
+ }
+ td--;
+ out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
+ out_be16(&td->length, 0);
+
+ /* endpoint structure has been created */
+ usb->ep0 = ep;
+
+ return 0;
+err:
+ kfree(ep);
+ endpoint_zero_free(usb);
+ fhci_err(usb->fhci, "no memory for the %s\n", err_for);
+ return -ENOMEM;
+}
+
+/*
+ * initialize the endpoint register according to the given parameters
+ *
+ * artuments:
+ * usb A pointer to the data strucutre of the USB
+ * ep A pointer to the endpoint structre
+ * data_mem The data memory partition(BUS)
+ */
+static void init_endpoint_registers(struct fhci_usb *usb,
+ struct endpoint *ep,
+ enum fhci_mem_alloc data_mem)
+{
+ u8 rt;
+
+ /* set the endpoint registers according to the endpoint */
+ out_be16(&usb->fhci->regs->usb_ep[0],
+ USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
+ out_be16(&usb->fhci->pram->ep_ptr[0],
+ 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
+ out_8(&ep->ep_pram_ptr->rx_func_code, rt);
+ out_8(&ep->ep_pram_ptr->tx_func_code, rt);
+ out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028);
+ out_be16(&ep->ep_pram_ptr->rx_base, 0);
+ out_be16(&ep->ep_pram_ptr->tx_base, qe_muram_offset(ep->td_base));
+ out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0);
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr, qe_muram_offset(ep->td_base));
+ out_be32(&ep->ep_pram_ptr->tx_state, 0);
+}
+
+/*
+ * Collect the submitted frames and inform the application about them
+ * It is also prepearing the TDs for new frames. If the Tx interrupts
+ * are diabled, the application should call that routine to get
+ * confirmation about the submitted frames. Otherwise, the routine is
+ * called frome the interrupt service routine during the Tx interrupt.
+ * In that case the application is informed by calling the application
+ * specific 'transaction_confirm' routine
+ */
+static void td_transaction_confirm(struct fhci_usb *usb)
+{
+ struct endpoint *ep = usb->ep0;
+ struct packet *pkt;
+ struct usb_td __iomem *td;
+ u16 extra_data;
+ u16 td_status;
+ u16 td_length;
+ u32 buf;
+
+ /*
+ * collect transmitted BDs from the chip. The routine clears all BDs
+ * with R bit = 0 and the pointer to data buffer is not NULL, that is
+ * BDs which point to the transmitted data buffer
+ */
+ while (1) {
+ td = ep->conf_td;
+ td_status = in_be16(&td->status);
+ td_length = in_be16(&td->length);
+ buf = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+
+ /* check if the TD is empty */
+ if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf)))
+ break;
+ /* check if it is a dummy buffer */
+ else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W))
+ break;
+
+ /* mark TD as empty */
+ clrbits16(&td->status, ~TD_W);
+ out_be16(&td->length, 0);
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->extra, 0);
+ /* advance the TD pointer */
+ ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status);
+
+ /* check if it is a dummy buffer(type2) */
+ if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W))
+ continue;
+
+ pkt = cq_get(ep->conf_frame_Q);
+ if (!pkt)
+ fhci_err(usb->fhci, "no frame to confirm\n");
+
+ if (td_status & TD_ERRORS) {
+ if (td_status & TD_RXER) {
+ if (td_status & TD_CR)
+ pkt->status = USB_TD_RX_ER_CRC;
+ else if (td_status & TD_AB)
+ pkt->status = USB_TD_RX_ER_BITSTUFF;
+ else if (td_status & TD_OV)
+ pkt->status = USB_TD_RX_ER_OVERUN;
+ else if (td_status & TD_BOV)
+ pkt->status = USB_TD_RX_DATA_OVERUN;
+ else if (td_status & TD_NO)
+ pkt->status = USB_TD_RX_ER_NONOCT;
+ else
+ fhci_err(usb->fhci, "illegal error "
+ "occured\n");
+ } else if (td_status & TD_NAK)
+ pkt->status = USB_TD_TX_ER_NAK;
+ else if (td_status & TD_TO)
+ pkt->status = USB_TD_TX_ER_TIMEOUT;
+ else if (td_status & TD_UN)
+ pkt->status = USB_TD_TX_ER_UNDERUN;
+ else if (td_status & TD_STAL)
+ pkt->status = USB_TD_TX_ER_STALL;
+ else
+ fhci_err(usb->fhci, "illegal error occured\n");
+ } else if ((extra_data & TD_TOK_IN) &&
+ pkt->len > td_length - CRC_SIZE) {
+ pkt->status = USB_TD_RX_DATA_UNDERUN;
+ }
+
+ if (extra_data & TD_TOK_IN)
+ pkt->len = td_length - CRC_SIZE;
+ else if (pkt->info & PKT_ZLP)
+ pkt->len = 0;
+ else
+ pkt->len = td_length;
+
+ transaction_confirm(usb, pkt);
+ }
+}
+
+/*
+ * Submitting a data frame to a specified endpoint of a USB device
+ * The frame is put in the driver's transmit queue for this endpoint
+ *
+ * Arguments:
+ * usb A pointer to the USB structure
+ * pkt A pointer to the user frame structure
+ * trans_type Transaction tyep - IN,OUT or SETUP
+ * dest_addr Device address - 0~127
+ * dest_ep Endpoint number of the device - 0~16
+ * trans_mode Pipe type - ISO,Interrupt,bulk or control
+ * dest_speed USB speed - Low speed or FULL speed
+ * data_toggle Data sequence toggle - 0 or 1
+ */
+static u32 host_transaction(struct fhci_usb *usb,
+ struct packet *pkt,
+ enum fhci_ta_type trans_type,
+ u8 dest_addr,
+ u8 dest_ep,
+ enum fhci_tf_mode trans_mode,
+ enum fhci_speed dest_speed, u8 data_toggle)
+{
+ struct endpoint *ep = usb->ep0;
+ struct usb_td __iomem *td;
+ u16 extra_data;
+ u16 td_status;
+
+ fhci_usb_disable_interrupt(usb);
+ /* start from the next BD that should be filled */
+ td = ep->empty_td;
+ td_status = in_be16(&td->status);
+
+ if (td_status & TD_R && in_be16(&td->length)) {
+ /* if the TD is not free */
+ fhci_usb_enable_interrupt(usb);
+ return -1;
+ }
+
+ /* get the next TD in the ring */
+ ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
+ fhci_usb_enable_interrupt(usb);
+ pkt->priv_data = td;
+ out_be32(&td->buf_ptr, virt_to_phys(pkt->data));
+ /* sets up transaction parameters - addr,endp,dir,and type */
+ extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr;
+ switch (trans_type) {
+ case FHCI_TA_IN:
+ extra_data |= TD_TOK_IN;
+ break;
+ case FHCI_TA_OUT:
+ extra_data |= TD_TOK_OUT;
+ break;
+ case FHCI_TA_SETUP:
+ extra_data |= TD_TOK_SETUP;
+ break;
+ }
+ if (trans_mode == FHCI_TF_ISO)
+ extra_data |= TD_ISO;
+ out_be16(&td->extra, extra_data);
+
+ /* sets up the buffer descriptor */
+ td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF);
+ if (!(pkt->info & PKT_NO_CRC))
+ td_status |= TD_TC;
+
+ switch (trans_type) {
+ case FHCI_TA_IN:
+ if (data_toggle)
+ pkt->info |= PKT_PID_DATA1;
+ else
+ pkt->info |= PKT_PID_DATA0;
+ break;
+ default:
+ if (data_toggle) {
+ td_status |= TD_PID_DATA1;
+ pkt->info |= PKT_PID_DATA1;
+ } else {
+ td_status |= TD_PID_DATA0;
+ pkt->info |= PKT_PID_DATA0;
+ }
+ break;
+ }
+
+ if ((dest_speed == FHCI_LOW_SPEED) &&
+ (usb->port_status == FHCI_PORT_FULL))
+ td_status |= TD_LSP;
+
+ out_be16(&td->status, td_status);
+
+ /* set up buffer length */
+ if (trans_type == FHCI_TA_IN)
+ out_be16(&td->length, pkt->len + CRC_SIZE);
+ else
+ out_be16(&td->length, pkt->len);
+
+ /* put the frame to the confirmation queue */
+ cq_put(ep->conf_frame_Q, pkt);
+
+ if (cq_howmany(ep->conf_frame_Q) == 1)
+ out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
+
+ return 0;
+}
+
+/* Reset the Tx BD ring */
+static void flush_bds(struct fhci_usb *usb)
+{
+ u16 extra_data;
+ u16 td_status;
+ u32 buf;
+ struct usb_td __iomem *td;
+ struct endpoint *ep = usb->ep0;
+
+ td = ep->td_base;
+ while (1) {
+ td_status = in_be16(&td->status);
+ buf = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+
+ /* if the TD is not empty - we'll confirm it as Timeout */
+ if (td_status & TD_R)
+ out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
+ /* if this TD is dummy - let's skip this TD */
+ else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER)
+ out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER);
+ /* if this is the last TD - break */
+ if (td_status & TD_W)
+ break;
+
+ td++;
+ }
+
+ td_transaction_confirm(usb);
+
+ td = ep->td_base;
+ do {
+ out_be16(&td->status, 0);
+ out_be16(&td->length, 0);
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->extra, 0);
+ td++;
+ } while (!(in_be16(&td->status) & TD_W));
+ out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
+ out_be16(&td->length, 0);
+ out_be32(&td->buf_ptr, 0);
+ out_be16(&td->extra, 0);
+
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
+ in_be16(&ep->ep_pram_ptr->tx_base));
+ out_be32(&ep->ep_pram_ptr->tx_state, 0);
+ out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
+ ep->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
+ */
+static void flush_actual_frame(struct fhci_usb *usb)
+{
+ u8 mode;
+ u16 tb_ptr;
+ u16 extra_data;
+ u16 td_status;
+ u32 buf_ptr;
+ struct usb_td __iomem *td;
+ struct endpoint *ep = usb->ep0;
+
+ /* disable the USB controller */
+ mode = in_8(&usb->fhci->regs->usb_mod);
+ out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN);
+
+ tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
+ td = qe_muram_addr(tb_ptr);
+ td_status = in_be16(&td->status);
+ buf_ptr = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+ do {
+ if (td_status & TD_R) {
+ out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
+ } else {
+ out_be32(&td->buf_ptr, 0);
+ ep->already_pushed_dummy_bd = false;
+ break;
+ }
+
+ /* advance the TD pointer */
+ td = next_bd(ep->td_base, td, td_status);
+ td_status = in_be16(&td->status);
+ buf_ptr = in_be32(&td->buf_ptr);
+ extra_data = in_be16(&td->extra);
+ } while ((td_status & TD_R) || buf_ptr);
+
+ td_transaction_confirm(usb);
+
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
+ in_be16(&ep->ep_pram_ptr->tx_base));
+ out_be32(&ep->ep_pram_ptr->tx_state, 0);
+ out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
+ ep->conf_td = ep->empty_td = ep->td_base;
+
+ usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
+
+ /* reset the event register */
+ out_be16(&usb->fhci->regs->usb_event, 0xffff);
+ /* enable the USB controller */
+ out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN);
+}
+
+/* handles Tx confirm and Tx error interrupt */
+static void tx_conf_interrupt(struct fhci_usb *usb)
+{
+ td_transaction_confirm(usb);
+
+ /*
+ * Schedule another transaction to this frame only if we have
+ * already confirmed all transaction in the frame.
+ */
+ if (((get_sof_timer_count(usb) < usb->max_frame_usage) ||
+ (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) &&
+ (list_empty(&usb->actual_frame->tds_list)))
+ schedule_transactions(usb);
+}
+
+static void host_transmit_actual_frame(struct fhci_usb *usb)
+{
+ u16 tb_ptr;
+ u16 td_status;
+ struct usb_td __iomem *td;
+ struct endpoint *ep = usb->ep0;
+
+ tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
+ td = qe_muram_addr(tb_ptr);
+
+ if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) {
+ struct usb_td __iomem *old_td = td;
+
+ ep->already_pushed_dummy_bd = false;
+ td_status = in_be16(&td->status);
+ /* gets the next TD in the ring */
+ td = next_bd(ep->td_base, td, td_status);
+ tb_ptr = qe_muram_offset(td);
+ out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr);
+
+ /* start transmit only if we have something in the TDs */
+ if (in_be16(&td->status) & TD_R)
+ out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO);
+
+ if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) {
+ out_be32(&old_td->buf_ptr, 0);
+ ep->conf_td = next_bd(ep->td_base, ep->conf_td,
+ td_status);
+ } else {
+ out_be32(&old_td->buf_ptr, DUMMY2_BD_BUFFER);
+ }
+ }
+}
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
new file mode 100644
index 0000000..fa6940b
--- /dev/null
+++ b/drivers/usb/host/fhci.h
@@ -0,0 +1,535 @@
+/*
+ * Freescale QUICC Engine USB Host Controller Driver
+ *
+ * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) Logic Product Development, Inc. 2007
+ * Peter Barada <peterb@logicpd.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __FHCI_H
+#define __FHCI_H
+
+#define USB_CLOCK 48000000
+
+#define FHCI_PRAM_SIZE 0x100
+
+#define MAX_EDS 32
+#define MAX_TDS 32
+
+
+/* CRC16 field size */
+#define CRC_SIZE 2
+
+/* USB protocol overhead for each frame transmitted from the host */
+#define PROTOCOL_OVERHEAD 7
+
+/* Packet structure, info field */
+#define PKT_PID_DATA0 0x80000000 /* PID - Data toggle zero */
+#define PKT_PID_DATA1 0x40000000 /* PID - Data toggle one */
+#define PKT_PID_SETUP 0x20000000 /* PID - Setup bit */
+#define PKT_SETUP_STATUS 0x10000000 /* Setup status bit */
+#define PKT_SETADDR_STATUS 0x08000000 /* Set address status bit */
+#define PKT_SET_HOST_LAST 0x04000000 /* Last data packet */
+#define PKT_HOST_DATA 0x02000000 /* Data packet */
+#define PKT_FIRST_IN_FRAME 0x01000000 /* First packet in the frame */
+#define PKT_TOKEN_FRAME 0x00800000 /* Token packet */
+#define PKT_ZLP 0x00400000 /* Zero length packet */
+#define PKT_IN_TOKEN_FRAME 0x00200000 /* IN token packet */
+#define PKT_OUT_TOKEN_FRAME 0x00100000 /* OUT token packet */
+#define PKT_SETUP_TOKEN_FRAME 0x00080000 /* SETUP token packet */
+#define PKT_STALL_FRAME 0x00040000 /* STALL packet */
+#define PKT_NACK_FRAME 0x00020000 /* NACK packet */
+#define PKT_NO_PID 0x00010000 /* No PID */
+#define PKT_NO_CRC 0x00008000 /* don't append CRC */
+#define PKT_HOST_COMMAND 0x00004000 /* Host command packet */
+#define PKT_DUMMY_PACKET 0x00002000 /* Dummy packet, used for mmm */
+#define PKT_LOW_SPEED_PACKET 0x00001000 /* Low-Speed packet */
+
+#define TRANS_OK (0)
+#define TRANS_INPROGRESS (-1)
+#define TRANS_DISCARD (-2)
+#define TRANS_FAIL (-3)
+
+#define PS_INT 0
+#define PS_DISCONNECTED 1
+#define PS_CONNECTED 2
+#define PS_READY 3
+#define PS_MISSING 4
+
+/* Transfer Descriptor status field */
+#define USB_TD_OK 0x00000000 /* TD transmited or received ok */
+#define USB_TD_INPROGRESS 0x80000000 /* TD is being transmitted */
+#define USB_TD_RX_ER_NONOCT 0x40000000 /* Tx Non Octet Aligned Packet */
+#define USB_TD_RX_ER_BITSTUFF 0x20000000 /* Frame Aborted-Received pkt */
+#define USB_TD_RX_ER_CRC 0x10000000 /* CRC error */
+#define USB_TD_RX_ER_OVERUN 0x08000000 /* Over - run occured */
+#define USB_TD_RX_ER_PID 0x04000000 /* wrong PID received */
+#define USB_TD_RX_DATA_UNDERUN 0x02000000 /* shorter than expected */
+#define USB_TD_RX_DATA_OVERUN 0x01000000 /* longer than expected */
+#define USB_TD_TX_ER_NAK 0x00800000 /* NAK handshake */
+#define USB_TD_TX_ER_STALL 0x00400000 /* STALL handshake */
+#define USB_TD_TX_ER_TIMEOUT 0x00200000 /* transmit time out */
+#define USB_TD_TX_ER_UNDERUN 0x00100000 /* transmit underrun */
+
+#define USB_TD_ERROR (USB_TD_RX_ER_NONOCT | USB_TD_RX_ER_BITSTUFF | \
+ USB_TD_RX_ER_CRC | USB_TD_RX_ER_OVERUN | USB_TD_RX_ER_PID | \
+ USB_TD_RX_DATA_UNDERUN | USB_TD_RX_DATA_OVERUN | \
+ USB_TD_TX_ER_NAK | USB_TD_TX_ER_STALL | \
+ USB_TD_TX_ER_TIMEOUT | USB_TD_TX_ER_UNDERUN)
+
+/* Transfer Descriptor toggle field */
+#define USB_TD_TOGGLE_DATA0 0
+#define USB_TD_TOGGLE_DATA1 1
+#define USB_TD_TOGGLE_CARRY 2
+
+/* #define MULTI_DATA_BUS */
+
+/* Bus mode register RBMR/TBMR */
+#define BUS_MODE_GBL 0x20 /* Global snooping */
+#define BUS_MODE_BO 0x18 /* Byte ordering */
+#define BUS_MODE_BO_BE 0x10 /* Byte ordering - Big-endian */
+#define BUS_MODE_DTB 0x02 /* Data bus */
+
+/* FHCI QE USB Register Description */
+
+/* USB Mode Register bit define */
+#define USB_MODE_EN 0x01
+#define USB_MODE_HOST 0x02
+#define USB_MODE_TEST 0x04
+#define USB_MODE_SFTE 0x08
+#define USB_MODE_RESUME 0x40
+#define USB_MODE_LSS 0x80
+
+/* USB Slave Address Register Mask */
+#define USB_SLVADDR_MASK 0x7F
+
+/* USB Endpoint register define */
+#define USB_EPNUM_MASK 0xF000
+#define USB_EPNUM_SHIFT 12
+
+#define USB_TRANS_MODE_SHIFT 8
+#define USB_TRANS_CTR 0x0000
+#define USB_TRANS_INT 0x0100
+#define USB_TRANS_BULK 0x0200
+#define USB_TRANS_ISO 0x0300
+
+#define USB_EP_MF 0x0020
+#define USB_EP_RTE 0x0010
+
+#define USB_THS_SHIFT 2
+#define USB_THS_MASK 0x000c
+#define USB_THS_NORMAL 0x0
+#define USB_THS_IGNORE_IN 0x0004
+#define USB_THS_NACK 0x0008
+#define USB_THS_STALL 0x000c
+
+#define USB_RHS_SHIFT 0
+#define USB_RHS_MASK 0x0003
+#define USB_RHS_NORMAL 0x0
+#define USB_RHS_IGNORE_OUT 0x0001
+#define USB_RHS_NACK 0x0002
+#define USB_RHS_STALL 0x0003
+
+#define USB_RTHS_MASK 0x000f
+
+/* USB Command Register define */
+#define USB_CMD_STR_FIFO 0x80
+#define USB_CMD_FLUSH_FIFO 0x40
+#define USB_CMD_ISFT 0x20
+#define USB_CMD_DSFT 0x10
+#define USB_CMD_EP_MASK 0x03
+
+/* USB Event and Mask Register define */
+#define USB_E_MSF_MASK 0x0800
+#define USB_E_SFT_MASK 0x0400
+#define USB_E_RESET_MASK 0x0200
+#define USB_E_IDLE_MASK 0x0100
+#define USB_E_TXE4_MASK 0x0080
+#define USB_E_TXE3_MASK 0x0040
+#define USB_E_TXE2_MASK 0x0020
+#define USB_E_TXE1_MASK 0x0010
+#define USB_E_SOF_MASK 0x0008
+#define USB_E_BSY_MASK 0x0004
+#define USB_E_TXB_MASK 0x0002
+#define USB_E_RXB_MASK 0x0001
+
+/* 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];
+ __be16 usb_ep[4]; /* endpoint register */
+ u8 reserved2[4];
+ __be16 usb_event; /* event register */
+ u8 reserved3[2];
+ __be16 usb_mask; /* mask register */
+ u8 reserved4[1];
+ u8 usb_status; /* status register */
+ __be16 usb_sof_tmr; /* Start Of Frame timer */
+ u8 reserved5[2];
+ __be16 usb_frame_num; /* frame number register */
+ u8 reserved6[1];
+};
+
+/* Freescale USB HOST */
+struct fhci_pram {
+ __be16 ep_ptr[4]; /* Endpoint porter reg */
+ __be32 rx_state; /* Rx internal state */
+ __be32 rx_ptr; /* Rx internal data pointer */
+ __be16 frame_num; /* Frame number */
+ __be16 rx_cnt; /* Rx byte count */
+ __be32 rx_temp; /* Rx temp */
+ __be32 rx_data_temp; /* Rx data temp */
+ __be16 rx_u_ptr; /* Rx microcode return address temp */
+ u8 reserved1[2]; /* reserved area */
+ __be32 sof_tbl; /* SOF lookup table pointer */
+ u8 sof_u_crc_temp; /* SOF micorcode CRC5 temp reg */
+ u8 reserved2[0xdb];
+};
+
+/* Freescale USB Endpoint*/
+struct fhci_ep_pram {
+ __be16 rx_base; /* Rx BD base address */
+ __be16 tx_base; /* Tx BD base address */
+ u8 rx_func_code; /* Rx function code */
+ u8 tx_func_code; /* Tx function code */
+ __be16 rx_buff_len; /* Rx buffer length */
+ __be16 rx_bd_ptr; /* Rx BD pointer */
+ __be16 tx_bd_ptr; /* Tx BD pointer */
+ __be32 tx_state; /* Tx internal state */
+ __be32 tx_ptr; /* Tx internal data pointer */
+ __be16 tx_crc; /* temp transmit CRC */
+ __be16 tx_cnt; /* Tx byte count */
+ __be32 tx_temp; /* Tx temp */
+ __be16 tx_u_ptr; /* Tx microcode return address temp */
+ __be16 reserved;
+};
+
+struct fhci_controller_list {
+ struct list_head ctrl_list; /* control endpoints */
+ struct list_head bulk_list; /* bulk endpoints */
+ struct list_head iso_list; /* isochronous endpoints */
+ struct list_head intr_list; /* interruput endpoints */
+ struct list_head done_list; /* done transfers */
+};
+
+struct virtual_root_hub {
+ int dev_num; /* USB address of the root hub */
+ u32 feature; /* indicates what feature has been set */
+ struct usb_hub_status hub;
+ struct usb_port_status port;
+};
+
+enum fhci_gpios {
+ GPIO_USBOE = 0,
+ GPIO_USBTP,
+ GPIO_USBTN,
+ GPIO_USBRP,
+ GPIO_USBRN,
+ /* these are optional */
+ GPIO_SPEED,
+ GPIO_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 */
+ struct gtm_timer *timer;
+ int gpios[NUM_GPIOS];
+ enum qe_clock fullspeed_clk;
+ enum qe_clock lowspeed_clk;
+
+ spinlock_t lock;
+ struct fhci_usb *usb_lld; /* Low-level driver */
+ struct virtual_root_hub *vroot_hub; /* the virtual root hub */
+ int active_urbs;
+ struct fhci_controller_list *hc_list;
+ struct tasklet_struct *process_done_task; /* tasklet for done list */
+
+ struct list_head empty_eds;
+ struct list_head empty_tds;
+
+#ifdef CONFIG_FHCI_DEBUG
+ int usb_irq_stat[13];
+ struct dentry *dfs_root;
+ struct dentry *dfs_regs;
+ struct dentry *dfs_irq_stat;
+#endif
+};
+
+#define USB_FRAME_USAGE 90
+#define FRAME_TIME_USAGE (USB_FRAME_USAGE*10) /* frame time usage */
+#define SW_FIX_TIME_BETWEEN_TRANSACTION 150 /* SW */
+#define MAX_BYTES_PER_FRAME (USB_FRAME_USAGE*15)
+#define MAX_PERIODIC_FRAME_USAGE 90
+
+/* transaction type */
+enum fhci_ta_type {
+ FHCI_TA_IN = 0, /* input transaction */
+ FHCI_TA_OUT, /* output transaction */
+ FHCI_TA_SETUP, /* setup transaction */
+};
+
+/* transfer mode */
+enum fhci_tf_mode {
+ FHCI_TF_CTRL = 0,
+ FHCI_TF_ISO,
+ FHCI_TF_BULK,
+ FHCI_TF_INTR,
+};
+
+enum fhci_speed{
+ FHCI_FULL_SPEED,
+ FHCI_LOW_SPEED,
+};
+
+/* endpoint state */
+enum fhci_ed_state {
+ FHCI_ED_NEW = 0, /* pipe is new */
+ FHCI_ED_OPER, /* pipe is operating */
+ FHCI_ED_URB_DEL, /* pipe is in hold because urb is being deleted */
+ FHCI_ED_SKIP, /* skip this pipe */
+ FHCI_ED_HALTED, /* pipe is halted */
+};
+
+enum fhci_port_status {
+ FHCI_PORT_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 __iomem *priv_data; /* private data of the driver (TDs or BDs) */
+};
+
+/* struct for each URB */
+#define URB_INPROGRESS 0
+#define URB_DEL 1
+
+/* URB states (state field) */
+#define US_BULK 0
+#define US_BULK0 1
+
+/* three setup states */
+#define US_CTRL_SETUP 2
+#define US_CTRL_DATA 1
+#define US_CTRL_ACK 0
+
+#define EP_ZERO 0
+
+struct urb_priv {
+ int num_of_tds;
+ int tds_cnt;
+ int state;
+
+ struct td **tds;
+ struct ed *ed;
+ struct timer_list time_out;
+};
+
+/* struct for each 1mSec frame time */
+#define FRAME_IS_TRANSMITTED 0x00
+#define FRAME_TIMER_END_TRANSMISSION 0x01
+#define FRAME_DATA_END_TRANSMISSION 0x02
+#define FRAME_END_TRANSMISSION 0x03
+#define FRAME_IS_PREPARED 0x04
+
+struct fhci_time_frame {
+ u16 frame_num; /* frame number */
+ u16 total_bytes; /* total bytes submitted within this frame */
+ u8 frame_status; /* flag that indicates to stop fill this frame */
+ struct list_head tds_list; /* all tds of this frame */
+};
+
+/* internal driver structure*/
+struct fhci_usb {
+ u16 saved_msk; /* saving of the USB mask register */
+ struct endpoint *ep0; /* pointer for endpoint0 structure */
+ int intr_nesting_cnt; /* interrupt nesting counter */
+ u16 max_frame_usage; /* max frame time usage,in micro-sec */
+ u16 max_bytes_per_frame; /* max byte can be tx in one time frame */
+ u32 sw_transaction_time; /* sw complete trans time,in micro-sec */
+ struct fhci_time_frame *actual_frame;
+ struct fhci_controller_list *hc_list; /* main structure for hc */
+ struct virtual_root_hub *vroot_hub;
+ enum fhci_port_status port_status; /* v_rh port status */
+
+ u32 (*transfer_confirm)(struct fhci_hcd *fhci);
+
+ struct fhci_hcd *fhci;
+};
+
+/*
+ * Various helpers and prototypes below.
+ */
+
+static void move_head_to_tail(struct list_head *list)
+{
+ struct list_head *node = list->next;
+
+ if (!list_empty(list)) {
+ list_del(node);
+ list_add_tail(node, list);
+ }
+}
+
+/* maps the hardware error code to the USB error code */
+static int status_to_error(u32 status)
+{
+ if (status == USB_TD_OK)
+ return 0;
+ else if (status & USB_TD_RX_ER_CRC)
+ return -EILSEQ;
+ else if (status & USB_TD_RX_ER_NONOCT)
+ return -EPROTO;
+ else if (status & USB_TD_RX_ER_OVERUN)
+ return -ECOMM;
+ else if (status & USB_TD_RX_ER_BITSTUFF)
+ return -EPROTO;
+ else if (status & USB_TD_RX_ER_PID)
+ return -EILSEQ;
+ else if (status & (USB_TD_TX_ER_NAK | USB_TD_TX_ER_TIMEOUT))
+ return -ETIMEDOUT;
+ else if (status & USB_TD_TX_ER_STALL)
+ return -EPIPE;
+ else if (status & USB_TD_TX_ER_UNDERUN)
+ return -ENOSR;
+ else if (status & USB_TD_RX_DATA_UNDERUN)
+ return -EREMOTEIO;
+ else if (status & USB_TD_RX_DATA_OVERUN)
+ return -EOVERFLOW;
+ else
+ return -EINVAL;
+}
+
+static u16 get_frame_num(struct fhci_hcd *fhci)
+{
+ return in_be16(&fhci->pram->frame_num) & 0x07ff;
+}
+
+#define fhci_dbg(fhci, fmt, args...) \
+ dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_vdbg(fhci, fmt, args...) \
+ dev_vdbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_err(fhci, fmt, args...) \
+ dev_err(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_info(fhci, fmt, args...) \
+ dev_info(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+#define fhci_warn(fhci, fmt, args...) \
+ dev_warn(fhci_to_hcd(fhci)->self.controller, fmt, ##args)
+
+static struct fhci_hcd *hcd_to_fhci(struct usb_hcd *hcd)
+{
+ return (struct fhci_hcd *)hcd->hcd_priv;
+}
+
+static struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci)
+{
+ return container_of((void *)fhci, struct usb_hcd, hcd_priv);
+}
+
+/* fhci-hcd.c */
+static void fhci_start_sof_timer(struct fhci_hcd *fhci);
+static void fhci_stop_sof_timer(struct fhci_hcd *fhci);
+static u16 get_sof_timer_count(struct fhci_usb *usb);
+static void fhci_usb_enable_interrupt(struct fhci_usb *usb);
+static void fhci_usb_disable_interrupt(struct fhci_usb *usb);
+static int fhci_ioports_check_bus_state(struct fhci_hcd *fhci);
+
+/* fhci-mem.c */
+static void recycle_empty_td(struct fhci_hcd *fhci, struct td *td);
+
+/* fhci-{tds,bds}.c */
+static void flush_bds(struct fhci_usb *usb);
+static void flush_actual_frame(struct fhci_usb *usb);
+static u32 host_transaction(struct fhci_usb *usb,
+ struct packet *pkt, enum fhci_ta_type trans_type,
+ u8 dest_addr, u8 dest_ep, enum fhci_tf_mode trans_mode,
+ enum fhci_speed dest_speed, u8 data_toggle);
+static void push_dummy_bd(struct endpoint *ep);
+
+/* fhci-sched.c */
+static void transaction_confirm(struct fhci_usb *usb, struct packet *pkt);
+static void flush_all_transmissions(struct fhci_usb *usb);
+static void schedule_transactions(struct fhci_usb *usb);
+static void device_connected_interrupt(struct fhci_hcd *fhci);
+
+#endif /* __FHCI_H */
--
1.5.2.2
^ permalink raw reply related
* [PATCH] [NAND] FSL UPM NAND driver
From: Anton Vorontsov @ 2008-03-11 19:33 UTC (permalink / raw)
To: linux-mtd, linuxppc-dev
This is very simple driver, NAND is connected through localbus,
and User-Programmable Machine is doing various adjustments to
speak NAND. No special efforts needed to do read and write cycles,
though to control ALE and CLE phases, we ask UPM to generate exact
pre-programmed signals on the localbus lines.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/mtd/nand/Kconfig | 8 ++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/fsl_upm.c | 291 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 300 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/nand/fsl_upm.c
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 4a3c675..288b871 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -330,4 +330,12 @@ config MTD_NAND_FSL_ELBC
Enabling this option will enable you to use this to control
external NAND devices.
+config MTD_NAND_FSL_UPM
+ tristate "Support for NAND on Freescale UPM"
+ depends on MTD_NAND && OF_GPIO && (PPC_83xx || PPC_85xx)
+ select FSL_LBC
+ help
+ Enables support for NAND Flash chips wired onto Freescale PowerPC
+ processor localbus with User-Programmable Machine support.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 80d575e..44eddb1 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -32,5 +32,6 @@ obj-$(CONFIG_MTD_ALAUDA) += alauda.o
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
+obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
new file mode 100644
index 0000000..f1f8cf8
--- /dev/null
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -0,0 +1,291 @@
+/*
+ * Freescale UPM NAND driver.
+ *
+ * Copyright (c) 2007-2008 MontaVista Software, Inc.
+ *
+ * Author: 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/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/mtd.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <asm/fsl_lbc.h>
+
+struct fsl_upm_nand {
+ struct device *dev;
+ struct mtd_info mtd;
+ struct nand_chip chip;
+ int last_ctrl;
+#ifdef CONFIG_MTD_PARTITIONS
+ struct mtd_partition *parts;
+#endif
+
+ struct fsl_upm upm;
+ u8 upm_addr_offset;
+ u8 upm_cmd_offset;
+ void __iomem *io_base;
+ int rnb_gpio;
+ const u32 *wait_pattern;
+ const u32 *wait_write;
+ int chip_delay;
+};
+
+#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
+
+static int fun_chip_ready(struct mtd_info *mtd)
+{
+ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+
+ if (gpio_get_value(fun->rnb_gpio))
+ return 1;
+
+ dev_vdbg(fun->dev, "busy\n");
+ return 0;
+}
+
+static void fun_wait_rnb(struct fsl_upm_nand *fun)
+{
+ int cnt = 1000000;
+
+ if (fun->rnb_gpio >= 0) {
+ while (--cnt && !fun_chip_ready(&fun->mtd))
+ cpu_relax();
+ }
+
+ if (!cnt)
+ dev_err(fun->dev, "tired waiting for RNB\n");
+}
+
+static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+
+ if (!(ctrl & fun->last_ctrl)) {
+ fsl_upm_end_pattern(&fun->upm);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
+ }
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (ctrl & NAND_ALE)
+ fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
+ else if (ctrl & NAND_CLE)
+ fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
+ }
+
+ fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd);
+
+ if (fun->wait_pattern)
+ fun_wait_rnb(fun);
+}
+
+static uint8_t fun_read_byte(struct mtd_info *mtd)
+{
+ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+
+ return in_8(fun->chip.IO_ADDR_R);
+}
+
+static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = in_8(fun->chip.IO_ADDR_R);
+}
+
+static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+ int i;
+
+ for (i = 0; i < len; i++) {
+ out_8(fun->chip.IO_ADDR_W, buf[i]);
+ if (fun->wait_write)
+ fun_wait_rnb(fun);
+ }
+}
+
+static int __devinit fun_chip_init(struct fsl_upm_nand *fun)
+{
+ int ret;
+#ifdef CONFIG_MTD_PARTITIONS
+ static const char *part_types[] = { "cmdlinepart", NULL, };
+#endif
+
+ fun->chip.IO_ADDR_R = fun->io_base;
+ fun->chip.IO_ADDR_W = fun->io_base;
+ fun->chip.cmd_ctrl = fun_cmd_ctrl;
+ fun->chip.chip_delay = fun->chip_delay;
+ fun->chip.read_byte = fun_read_byte;
+ fun->chip.read_buf = fun_read_buf;
+ fun->chip.write_buf = fun_write_buf;
+ fun->chip.ecc.mode = NAND_ECC_SOFT;
+
+ if (fun->rnb_gpio >= 0)
+ fun->chip.dev_ready = fun_chip_ready;
+
+ fun->mtd.priv = &fun->chip;
+ fun->mtd.owner = THIS_MODULE;
+
+ ret = nand_scan(&fun->mtd, 1);
+ if (ret)
+ return ret;
+
+ fun->mtd.name = fun->dev->bus_id;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0);
+ if (ret > 0)
+ return add_mtd_partitions(&fun->mtd, fun->parts, ret);
+#endif
+ return add_mtd_device(&fun->mtd);
+}
+
+static int __devinit fun_probe(struct of_device *ofdev,
+ const struct of_device_id *ofid)
+{
+ struct fsl_upm_nand *fun;
+ struct resource io_res;
+ const u32 *prop;
+ int ret;
+ int size;
+
+ fun = kzalloc(sizeof(*fun), GFP_KERNEL);
+ if (!fun)
+ return -ENOMEM;
+
+ ret = of_address_to_resource(ofdev->node, 0, &io_res);
+ if (ret) {
+ dev_err(&ofdev->dev, "can't get IO base\n");
+ goto err1;
+ }
+
+ ret = fsl_upm_find(io_res.start, &fun->upm);
+ if (ret) {
+ dev_err(&ofdev->dev, "can't find UPM\n");
+ goto err1;
+ }
+
+ prop = of_get_property(ofdev->node, "fsl,upm-addr-offset", &size);
+ if (!prop || size != sizeof(u32)) {
+ dev_err(&ofdev->dev, "can't get UPM address offset\n");
+ ret = -EINVAL;
+ goto err2;
+ }
+ fun->upm_addr_offset = *prop;
+
+ prop = of_get_property(ofdev->node, "fsl,upm-cmd-offset", &size);
+ if (!prop || size != sizeof(u32)) {
+ dev_err(&ofdev->dev, "can't get UPM command offset\n");
+ ret = -EINVAL;
+ goto err2;
+ }
+ fun->upm_cmd_offset = *prop;
+
+ fun->rnb_gpio = of_get_gpio(ofdev->node, 0);
+ if (fun->rnb_gpio >= 0) {
+ ret = gpio_request(fun->rnb_gpio, ofdev->dev.bus_id);
+ if (ret) {
+ dev_err(&ofdev->dev, "can't request RNB gpio\n");
+ goto err2;
+ }
+ gpio_direction_input(fun->rnb_gpio);
+ } else if (fun->rnb_gpio == -EINVAL) {
+ dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
+ goto err2;
+ }
+
+ fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
+ io_res.end - io_res.start + 1);
+ if (!fun->io_base) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ fun->dev = &ofdev->dev;
+ fun->last_ctrl = NAND_CLE;
+ fun->wait_pattern = of_get_property(ofdev->node, "fsl,wait-pattern",
+ NULL);
+ fun->wait_write = of_get_property(ofdev->node, "fsl,wait-write", NULL);
+
+ prop = of_get_property(ofdev->node, "chip-delay", NULL);
+ if (prop)
+ fun->chip_delay = *prop;
+ else
+ fun->chip_delay = 50;
+
+ ret = fun_chip_init(fun);
+ if (ret)
+ goto err2;
+
+ dev_set_drvdata(&ofdev->dev, fun);
+
+ return 0;
+err2:
+ if (fun->rnb_gpio >= 0)
+ gpio_free(fun->rnb_gpio);
+err1:
+ kfree(fun);
+
+ return ret;
+}
+
+static int __devexit fun_remove(struct of_device *ofdev)
+{
+ struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
+
+ nand_release(&fun->mtd);
+
+ if (fun->rnb_gpio >= 0)
+ gpio_free(fun->rnb_gpio);
+
+ kfree(fun);
+
+ return 0;
+}
+
+static struct of_device_id of_fun_match[] = {
+ { .compatible = "fsl,upm-nand" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_fun_match);
+
+static struct of_platform_driver of_fun_driver = {
+ .name = "fsl,upm-nand",
+ .match_table = of_fun_match,
+ .probe = fun_probe,
+ .remove = __devexit_p(fun_remove),
+};
+
+static int __init fun_module_init(void)
+{
+ return of_register_platform_driver(&of_fun_driver);
+}
+module_init(fun_module_init);
+
+static void __exit fun_module_exit(void)
+{
+ of_unregister_platform_driver(&of_fun_driver);
+}
+module_exit(fun_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
+ "LocalBus User-Programmable Machine");
--
1.5.2.2
^ permalink raw reply related
* Re: [PATCH] pasemi_dma: Driver for PA Semi PWRficient on-chip DMA engine
From: Dan Williams @ 2008-03-11 20:37 UTC (permalink / raw)
To: Kumar Gala
Cc: hskinnemoen, Nelson, Shannon, linux-kernel, linuxppc-dev,
pasemi-linux, Olof Johansson, Andrew Morton
In-Reply-To: <F4B05C0E-99BB-4F60-BDD8-A79F07A81692@kernel.crashing.org>
On Tue, 2008-03-11 at 11:29 -0700, Kumar Gala wrote:
> > My fault for not pushing out this cleanup to the old url while the
> > git-md-accel changeover was pending.
>
> Any reason not to push Zhang's fixes for 2.6.25?
I plan to push these fixes for 2.6.25. Olof had some valid comments
about "Fix fsldma.c warning messages..." [1].
> I also have a patch I want to push for 2.6.25 to deal with the powerpc
> device tree. I was going to handle this via the powerpc tree's since
> that seems to be the model we have used for netdev and other drivers
> that play with arch/powerpc. I hope that's not an issue (I'll CC you
> on the patch).
Not an issue at all. Just give me a heads up on what you want me to
pick up and what you want me to just ack.
[1] http://marc.info/?l=linux-kernel&m=120524510630957&w=2
^ permalink raw reply
* RE: [PATCH] Freescale QUICC Engine USB Host Controller
From: Joakim Tjernlund @ 2008-03-11 21:32 UTC (permalink / raw)
To: 'Anton Vorontsov', linux-usb, linuxppc-dev
In-Reply-To: <20080311191744.GA10518@localhost.localdomain>
> -----Original Message-----
> From: linuxppc-dev-bounces+joakim.tjernlund=transmode.se@ozlabs.org [mailto:linuxppc-dev-
> bounces+joakim.tjernlund=transmode.se@ozlabs.org] On Behalf Of Anton Vorontsov
> Sent: den 11 mars 2008 20:18
> To: linux-usb@vger.kernel.org; linuxppc-dev@ozlabs.org
> Subject: [PATCH] Freescale QUICC Engine USB Host Controller
>
> This is patch adds support for the FHCI USB controller, as found
> in the Freescale MPC836x and MPC832x processors. It can support
> Full or Low speed modes.
>
> Quite a lot hardware is doing by itself (SOF generation, CRC generation
> and checking), though scheduling and retransmission is on the software
> shoulders.
>
> This controller does not integrate the root hub, so this driver also
> fakes an one-port hub. External hub is required to support more than
> one device.
Cool, can one turn this into a gadget driver too?
Jocke
^ permalink raw reply
* Re: of_serial vs legacy serial support with powerpc arch on 405
From: Josh Boyer @ 2008-03-11 22:02 UTC (permalink / raw)
To: John.Linn; +Cc: linuxppc-dev
In-Reply-To: <20080311150430.153AD3D8066@mail134-sin.bigfish.com>
On Tue, 11 Mar 2008 09:04:27 -0600
"John Linn" <John.Linn@xilinx.com> wrote:
> I've searched and found some threads, but it's still not clear to me.
> I'm working on 405 with powerpc and trying to get UART550 working.
Walnut works. So do Halakeau and Kilauea. Is your serial port
significantly different from those?
> I know that I need speed, freq, and reg-shift properties in the device
> tree and I have found patches for of_serial.c.
Patches for of_serial.c for what?
> I still don't understand when to use of_serial.c (SERIAL_OF_PLATFORM) vs
> the legacy support for serial ports. Is there any docs that I'm not
> finding that I should read?
There are no docs. At one time, of_serial was declared as the one true
serial driver, but these days either will do. The benefit of
legacy_serial is that output is enabled earlier in the boot.
> I don't see a way to configure in the legacy support, but I'm using
> SERIAL_OF and having problems. I don't see the console getting enabled
> even though it found the serial port driver.
You need to have CONFIG_PPC_UDBG_16550 set to bring in legacy_serial.
josh
^ permalink raw reply
* RE: of_serial vs legacy serial support with powerpc arch on 405
From: Stephen Neuendorffer @ 2008-03-11 22:14 UTC (permalink / raw)
To: Josh Boyer, John Linn; +Cc: linuxppc-dev
In-Reply-To: <20080311170255.4866e51a@zod.rchland.ibm.com>
Josh,
Specifically, we're trying to get console on of_serial, but there
doesn't seem to be any code to register a console, except in
legacy_serial? But legacy_serial doesn't implement the offsets
necessary for the Xilinx uart16550 cores. Does it seem reasonable to
add code to of_serial.c to register consoles, or is there something else
we're missing here?
Steve
> -----Original Message-----
> From: =
linuxppc-dev-bounces+stephen.neuendorffer=3Dxilinx.com@ozlabs.org
[mailto:linuxppc-dev-
> bounces+stephen.neuendorffer=3Dxilinx.com@ozlabs.org] On Behalf Of =
Josh
Boyer
> Sent: Tuesday, March 11, 2008 3:03 PM
> To: John Linn
> Cc: linuxppc-dev@ozlabs.org
> Subject: Re: of_serial vs legacy serial support with powerpc arch on
405
>=20
> On Tue, 11 Mar 2008 09:04:27 -0600
> "John Linn" <John.Linn@xilinx.com> wrote:
>=20
> > I've searched and found some threads, but it's still not clear to
me.
> > I'm working on 405 with powerpc and trying to get UART550 working.
>=20
> Walnut works. So do Halakeau and Kilauea. Is your serial port
> significantly different from those?
>=20
> > I know that I need speed, freq, and reg-shift properties in the
device
> > tree and I have found patches for of_serial.c.
>=20
> Patches for of_serial.c for what?
>=20
> > I still don't understand when to use of_serial.c
(SERIAL_OF_PLATFORM) vs
> > the legacy support for serial ports. Is there any docs that I'm not
> > finding that I should read?
>=20
> There are no docs. At one time, of_serial was declared as the one
true
> serial driver, but these days either will do. The benefit of
> legacy_serial is that output is enabled earlier in the boot.
>=20
> > I don't see a way to configure in the legacy support, but I'm using
> > SERIAL_OF and having problems. I don't see the console getting
enabled
> > even though it found the serial port driver.
>=20
> You need to have CONFIG_PPC_UDBG_16550 set to bring in legacy_serial.
>=20
> josh
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* RE: of_serial vs legacy serial support with powerpc arch on 405
From: John Linn @ 2008-03-11 22:23 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20080311170255.4866e51a@zod.rchland.ibm.com>
Thanks for the help Josh. I made some comments in-line.
-----Original Message-----
From: Josh Boyer [mailto:jwboyer@linux.vnet.ibm.com]=20
Sent: Tuesday, March 11, 2008 4:03 PM
To: John Linn
Cc: linuxppc-dev@ozlabs.org
Subject: Re: of_serial vs legacy serial support with powerpc arch on 405
On Tue, 11 Mar 2008 09:04:27 -0600
"John Linn" <John.Linn@xilinx.com> wrote:
> I've searched and found some threads, but it's still not clear to me.
> I'm working on 405 with powerpc and trying to get UART550 working.
Walnut works. So do Halakeau and Kilauea. Is your serial port
significantly different from those?
>> Requires a shift as the regs are farther apart.
> I know that I need speed, freq, and reg-shift properties in the device
> tree and I have found patches for of_serial.c.
Patches for of_serial.c for what?
>> There was a patch to correct where the spd was assumed to be in the=20
>> device tree and it would bomb out if it wasn't. I was thinking there
was
>> a patch for the reg-shift value also.
=20
> I still don't understand when to use of_serial.c (SERIAL_OF_PLATFORM)
vs
> the legacy support for serial ports. Is there any docs that I'm not
> finding that I should read?
There are no docs. At one time, of_serial was declared as the one true
serial driver, but these days either will do. The benefit of
legacy_serial is that output is enabled earlier in the boot.
> I don't see a way to configure in the legacy support, but I'm using
> SERIAL_OF and having problems. I don't see the console getting enabled
> even though it found the serial port driver.
You need to have CONFIG_PPC_UDBG_16550 set to bring in legacy_serial.
>> Thanks got it working with some mods.
josh
^ permalink raw reply
* Re: OF compatible MTD platform RAM driver ?
From: David Gibson @ 2008-03-11 22:40 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: ben, linuxppc-dev, linux-mtd
In-Reply-To: <200803111139.12667.laurentp@cse-semaphore.com>
[-- Attachment #1: Type: text/plain, Size: 1384 bytes --]
On Tue, Mar 11, 2008 at 11:39:08AM +0100, Laurent Pinchart wrote:
> On Tuesday 11 March 2008 01:45, David Gibson wrote:
> > On Mon, Mar 10, 2008 at 12:00:22PM -0500, Rune Torgersen wrote:
> > > linuxppc-dev-bounces+runet=innovsys.com@ozlabs.org wrote:
[snip]
> > > We ran ito the same issue.
> > > We did option 3, as it was efinetly the easiest,
> >
> > I think this is the best option in principle.
>
> I'll implement that and post a patch after completing the ppc-to-powerpc
> migration.
>
> > > here is the sram entry in our dts:
> >
> > Except that your implementation of it is not good.
> >
> > You're relying on the old obsolete flash binding with the "probe-type"
> > field. The solution should be adapted to the new approach which uses
> > values in the "compatible" field to indicate various sorts of flash
> > device.
>
> What "compatible" values should I use for ROM and RAM mappings ?
That I'm not so sure of. We'll need to find some consensus.
There may be existing IEEE1275 bindings for roms, which we should
investigate. Arguably RAM should be represented by a memory node, but
that's going to get messy for this sort of application.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: OF compatible MTD platform RAM driver ?
From: David Gibson @ 2008-03-11 22:41 UTC (permalink / raw)
To: Rune Torgersen; +Cc: ben, linuxppc-dev, linux-mtd
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B041854B3@ismail.innsys.innovsys.com>
On Tue, Mar 11, 2008 at 10:00:10AM -0500, Rune Torgersen wrote:
> David Gibson wrote:
> > On Mon, Mar 10, 2008 at 12:00:22PM -0500, Rune Torgersen wrote:
> >> We ran ito the same issue.
> >> We did option 3, as it was efinetly the easiest,
> >
> > I think this is the best option in principle.
> >
> >> here is the sram entry in our dts:
> >
> > Except that your implementation of it is not good.
> >
> > You're relying on the old obsolete flash binding with the "probe-type"
> > field. The solution should be adapted to the new approach which uses
> > values in the "compatible" field to indicate various sorts of flash
> > device.
>
> Yea, I know. But it was the easiest way of doing it at the time we did
> our port....
> In a timecrunch, easier is sometimes better than correct. :)
Well sure, but don't expect others to be swayed by your time pressure.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: ARCH=ppc -> ARCH=powerpc : help needed for dts file
From: David Gibson @ 2008-03-11 22:42 UTC (permalink / raw)
To: Philippe De Muyter; +Cc: Scott Wood, linuxppc-dev
In-Reply-To: <20080311114601.GA19189@netgate.macqel>
On Tue, Mar 11, 2008 at 12:46:02PM +0100, Philippe De Muyter wrote:
> Hi David,
>
> On Tue, Mar 11, 2008 at 11:32:49AM +1100, David Gibson wrote:
> > On Sun, Mar 09, 2008 at 11:31:09PM +0100, Philippe De Muyter wrote:
> > > Hi Ben,
> > >
> > > I now have a working linux on my mpc8540 based board, with support for
> > > the compactflash disk and the i2c rtc.
> > >
> > > The network tough, does not work yet. altough the the integrated ethernet
> > > controller (FEC) seems to be recognized. Could it be a problem with the phy ?
> > > I notice that I do not have an entry for gfar_interrupt in /proc/interrupts
> > > on my ethernet-missing linux, while I have one ont the working arch/ppc linux ?
> > > Do I need to give the phy type in the dts file, and how ?
> > >
> > > I would also like to know if it is possible to still get in linux the mac
> > > address known by uboot when using a dts file, and how ?
> >
> > This chiefly depends on whether you're using an old u-boot that
> > doesn't know about the device tree, or a new u-boot which itself
> > supplies a device tree to the kernel.
> >
> > If the old u-boot, you'll need to write a bootwrapper for your
> > platform which reads the bd_t and pokes the right mac addresses into
> > the device tree.
> >
> > If the new u-boot, u-boot itself should put the address into the
> > device tree. If it's not, why it's not is a u-boot question rather
> > than a device tree question.
>
> OK :(
>
> Now back to the first an bigger problem :
> currently, I have an "old" U-boot and I have written myself a dts file.
>
> Problem is : ethernet does not work, but that's not a mac-address problem,
> but something else that I do not understand yet. The symptom is I get
>
> ip route add default via 192.168.85.33 dev eth0
> RTNETLINK answers: Network is unreachable
>
> I surmise this is because my eth0 does not become up, and I surmise
> again this is because there is no driver selected to drive the phy.
>
> In my arch/ppc setup this was automagically handled by fixed@100:1 IIRC
>
> Is there something I can put in my dts file to activate a driver for
> my phy ?
Probably, but how phy selection works is dependent on the ethernet
driver, so I can't help you. You'll need someone familiar with the
driver in question (or read the code and figure it out).
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: Help needed to describe a custom bus in the device tree
From: David Gibson @ 2008-03-11 22:54 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: linuxppc-dev
In-Reply-To: <200803111527.32836.laurentp@cse-semaphore.com>
[-- Attachment #1: Type: text/plain, Size: 6235 bytes --]
On Tue, Mar 11, 2008 at 03:27:26PM +0100, Laurent Pinchart wrote:
> Hi everybody,
>
> the migration process from ARCH=ppc to ARCH=powerpc is easier than I
> thought in some parts, but a few devices are still giving me
> headaches. This should hopefully be one of my last major requests
> for help (I'm sure most of you will be happy to see traffic on this
> list going down when I'll be done :-))
>
> I'm having trouble describing a custom bus named MS bus (completely
> unrelated to a well-known software company) in the device tree. The
> hardware is MPC8248-based and has the following hardware topology.
>
> MPC8248 <-- localbus --> FPGA <-- ms bus --> Custom peripherals
>
> The bus interrupt controller, serial access (SPI) controller and
> status registers are accessed through memory-mapped registers in the
> FPGA. Parallel access to the MS bus is handled transparently by the
> FPGA which handles address mapping.
>
> The FPGA is mapped on the locabus at address 0xf4000000. Bus control
> registers are at 0xf4002000 - 0xf4003000. The parallel bus memory
> window on the localbus is located at 0xf5000000.
>
> My current dts draft describes that topology as follows (unrelated
> devices on the local bus such as flash memory are removed for
> clarity).
>
> localbus@f0010100 {
> compatible = "fsl,pq2-localbus";
> #address-cells = <2>;
> #size-cells = <1>;
> reg = <f0010100 40>;
>
> ranges = <0 0 40000000 01000000
> 2 0 f2000000 00100000
> 3 0 f3000000 00100000
> 4 0 f4000000 00100000
> 5 0 f5000000 00100000>;
>
> fpga@4,0 {
> #address-cells = <1>;
> #size-cells = <1>;
> ranges = <4 0 0 00010000>;
>
> msbus-arbitrer@2000 {
> compatible = "tbox,cp11-msbus-arbitrer";
> reg = <2000 4>;
> };
>
> msbus_pic: interrupt-controller@2100 {
> compatible = "tbox,cp11-msbus-pic";
> reg = <2100 8>;
> interrupts = <17 2>;
> interrupt-parent = <&cpm_pic>;
> #interrupt-cells = <1>;
> interrupt-controller;
> };
>
> msbus-spi@2200 {
> compatible = "tbox,cp11-msbus-spi";
> reg = <2200 100>;
> interrupts = <18 8>;
> interrupt-parent = <&cpm_pic>;
> };
>
> sdhc@5000 {
> compatible = "tbox,sdhci";
> reg = <5000 1000>;
> interrupts = <16 8>;
> interrupt-parent = <&cpm_pic>;
> };
> };
>
> msbus@5,0 {
> compatible = "tbox,cp11-msbus";
> #address-cells = <1>;
> #size-cells = <1>;
> #interrupt-cells = <1>;
> reg = <5 0 0 00000400>;
> interrupt-parent = <&msbus_pic>;
> };
> };
>
> The device tree reflects the physical topology but makes driver
> access to the bus quite complex. An OF platform device driver
> matching on compatible = "tbox,cp11-msbus" will not have the bus
> FPGA registers described in its device node.
>
> Having a look at the various device trees included in the kernel
> sources, it seems platforms with a PCI bus experience a similar
> problem. To solve it the PCI bus node address and registers describe
> the configuration registers, and the memory window to access PCI
> devices is described by the ranges property. Applying that to my
> custom bus would lead to the following tree.
>
> localbus@f0010100 {
> compatible = "fsl,pq2-localbus";
> #address-cells = <2>;
> #size-cells = <1>;
> reg = <f0010100 40>;
>
> ranges = <0 0 40000000 01000000
> 2 0 f2000000 00100000
> 3 0 f3000000 00100000
> 4 0 f4000000 00100000
> 4 1 f4002000 00000100
> 5 0 f5000000 00100000>;
>
> fpga@4,0 {
> #address-cells = <1>;
> #size-cells = <1>;
> ranges = <4 0 0 00010000>;
>
> msbus_pic: interrupt-controller@2100 {
> compatible = "tbox,cp11-msbus-pic";
> reg = <2100 8>;
> interrupts = <17 2>;
> interrupt-parent = <&cpm_pic>;
> #interrupt-cells = <1>;
> interrupt-controller;
> };
>
> msbus-spi@2200 {
> compatible = "tbox,cp11-msbus-spi";
> reg = <2200 100>;
> interrupts = <18 8>;
> interrupt-parent = <&cpm_pic>;
> };
>
> sdhc@5000 {
> compatible = "tbox,sdhci";
> reg = <5000 1000>;
> interrupts = <16 8>;
> interrupt-parent = <&cpm_pic>;
> };
> };
>
> msbus@4,1 {
> compatible = "tbox,cp11-msbus";
> #address-cells = <1>;
> #size-cells = <1>;
> #interrupt-cells = <1>;
> reg = <4 1 4>;
> interrupt-parent = <&msbus_pic>;
> ranges = <5 0 0 00000400>;
> };
> };
>
> Is this correct ? Is that the best way to describe my custom bus in
> the device tree ?
Your second example looks closer to right. Certainly you should use
'reg' only for bus control registers, and 'ranges' for windows into
the bus address space itself.
The device tree describes hardware from a functional point of view, so
I don't know that it's relevant that all the bus control functions are
implemented in an FPGA. Each of the subnodes are more-or-less
independent devices, so they could just have separate nodes.
Or, if this seems more sensible, you could decide that they're
sufficiently closely related to put them all as one node, with
multiple register blocks listed in the 'reg' property. That would
probably get messy for your PIC at the very least though.
> How would the relationships between the bus and
> its PIC and SPI controller be handled in the drivers ?
If the msbus driver needs to work with the associated PIC and SPI
controllers, then you should put properties in the msbus node giving
their phandles.
> I also don't
> understand how interrupt mappings are supposed to be handled. PCI
> busses have two CPM interrupt lines, one for the PCI PIC and one for
> the PCI bus, with the PCI bus having the CPM PIC as its interrupt
> controller. My bus PIC uses a single interrupt line. Is there some
> documentation explaining how PICs and interrupt mappings should be
> described ?
Are interrupts from devices on the msbus routed over the msbus, or are
they routed independently to the mspic or the cpm PIC?
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH 3/4] Fix drivers/macintosh/mediabay.c when !CONFIG_ADB_PMU.
From: Tony Breeds @ 2008-03-11 23:48 UTC (permalink / raw)
To: Paul Mackerras, linuxppc-dev
In-Reply-To: <66e63844f95778cdc331d245889ff0ebfe17bbb4.1205279151.git.tony@bakeyournoodle.com>
When building drivers/macintosh/mediabay.c if CONFIG_ADB_PMU isn't defined we
get:
drivers/built-in.o: In function `media_bay_step':
mediabay.c:(.text+0x92b84): undefined reference to `pmu_suspend'
mediabay.c:(.text+0x92c08): undefined reference to `pmu_resume'
Create empty place holders in that scenario.
Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
---
include/linux/pmu.h | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/include/linux/pmu.h b/include/linux/pmu.h
index b02b57c..cafe98d 100644
--- a/include/linux/pmu.h
+++ b/include/linux/pmu.h
@@ -147,8 +147,15 @@ extern void pmu_wait_complete(struct adb_request *req);
/* For use before switching interrupts off for a long time;
* warning: not stackable
*/
+#if defined(CONFIG_ADB_PMU)
extern void pmu_suspend(void);
extern void pmu_resume(void);
+#else
+static inline void pmu_suspend(void)
+{}
+static inline void pmu_resume(void)
+{}
+#endif
extern void pmu_enable_irled(int on);
--
1.5.4.3
^ permalink raw reply related
* [PATCH 2/4] Fix build of modular drivers/macintosh/apm_emu.c
From: Tony Breeds @ 2008-03-11 23:48 UTC (permalink / raw)
To: Paul Mackerras, linuxppc-dev
In-Reply-To: <66e63844f95778cdc331d245889ff0ebfe17bbb4.1205279151.git.tony@bakeyournoodle.com>
apm_emu.c needs access to pmu_batteries[] and friends. If CONFIG_SUSPEND isn't
defined these symbols aren't exported and the build fails with:
Building modules, stage 2.
ERROR: "pmu_batteries" [drivers/macintosh/apm_emu.ko] undefined!
ERROR: "pmu_battery_count" [drivers/macintosh/apm_emu.ko] undefined!
ERROR: "pmu_power_flags" [drivers/macintosh/apm_emu.ko] undefined!
Fix that.
Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
---
drivers/macintosh/via-pmu.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index ebec663..0170f55 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -2528,10 +2528,10 @@ EXPORT_SYMBOL(pmu_wait_complete);
EXPORT_SYMBOL(pmu_suspend);
EXPORT_SYMBOL(pmu_resume);
EXPORT_SYMBOL(pmu_unlock);
-#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
+#if defined(CONFIG_PPC32)
EXPORT_SYMBOL(pmu_enable_irled);
EXPORT_SYMBOL(pmu_battery_count);
EXPORT_SYMBOL(pmu_batteries);
EXPORT_SYMBOL(pmu_power_flags);
-#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
+#endif /* CONFIG_PPC32 */
--
1.5.4.3
^ permalink raw reply related
* [PATCH 1/4] Ensure that pmu_sys_suspended exists in appropriate configs.
From: Tony Breeds @ 2008-03-11 23:48 UTC (permalink / raw)
To: Paul Mackerras, linuxppc-dev; +Cc: Guido Guenther
pmu_sys_suspended is declared extern when:
defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
but only defined when:
defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
which is wrong. Lets fix that.
Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
---
include/linux/pmu.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/linux/pmu.h b/include/linux/pmu.h
index 4c5f653..b02b57c 100644
--- a/include/linux/pmu.h
+++ b/include/linux/pmu.h
@@ -192,7 +192,7 @@ extern unsigned int pmu_power_flags;
extern void pmu_backlight_init(void);
/* some code needs to know if the PMU was suspended for hibernation */
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
extern int pmu_sys_suspended;
#else
/* if power management is not configured it can't be suspended */
--
1.5.4.3
^ permalink raw reply related
* [PATCH 4/4] Fix arch/powerpc/platforms/powermac/pic.c when !CONFIG_ADB_PMU.
From: Tony Breeds @ 2008-03-11 23:48 UTC (permalink / raw)
To: Paul Mackerras, linuxppc-dev
In-Reply-To: <66e63844f95778cdc331d245889ff0ebfe17bbb4.1205279151.git.tony@bakeyournoodle.com>
When building arch/powerpc/platforms/powermac/pic.c when !CONFIG_ADB_PMU
we get the following warnings:
arch/powerpc/platforms/powermac/pic.c: In function 'pmacpic_find_viaint':
arch/powerpc/platforms/powermac/pic.c:623: warning: label 'not_found' defined but not used
Fix that.
Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
---
arch/powerpc/platforms/powermac/pic.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c
index 4073640..829b8b0 100644
--- a/arch/powerpc/platforms/powermac/pic.c
+++ b/arch/powerpc/platforms/powermac/pic.c
@@ -618,9 +618,9 @@ static int pmacpic_find_viaint(void)
if (np == NULL)
goto not_found;
viaint = irq_of_parse_and_map(np, 0);;
-#endif /* CONFIG_ADB_PMU */
not_found:
+#endif /* CONFIG_ADB_PMU */
return viaint;
}
--
1.5.4.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox