LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] powerpc: EOI spurious irqs during boot so they can be reenabled later
From: miltonm @ 2008-08-06  2:55 UTC (permalink / raw)
  To: Michael Ellerman, Paul Mackerras; +Cc: linuxppc-dev, Milton Miller

----- Original Message Follows -----
From: Michael Ellerman <michael@ellerman.id.au>
To: Paul Mackerras <paulus@samba.org>
Cc: <linuxppc-dev@ozlabs.org>, Milton Miller
<miltonm@bga.com>, Segher Boessenkool
<segher@kernel.crashing.org>
Subject: [PATCH] powerpc: EOI spurious irqs during boot so
they can be reenabled later
Date: Wed,  6 Aug 2008 12:03:37 +1000 (EST)
 
> In the xics code, if we receive an irq during boot that
> hasn't been setup yet - ie. we have no reverse mapping for
> it - we mask the irq so we don't hear from it again.
> 
> Later on if someone request_irq()'s that irq, we'll unmask
> it but it will still never fire. This is because we never
> EOI'ed the irq when we originally received it - when it
> was spurious.
> 
> This can be reproduced trivially by banging the keyboard
> while kexec'ing on a P5 LPAR, even though the hvc_console
> driver request's the console irq, the console is
> non-functional because we're receiving no console
> interrupts.
> 
 
which means some driver didn't disable interrupts on its
shutdown, but I digress.
 
> So when we receive a spurious irq mask it and then EOI it.
> 
> Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
> ---
>  arch/powerpc/platforms/pseries/xics.c |   29
> ++++++++++++++++++++---------
>  1 files changed, 20 insertions(+), 9 deletions(-)
> 
> 
> Updated to mask then EOI, thanks to Segher for whacking me
> with the clue stick.
>
 
Actually, just sending the EOI would likely result in the
exact same interrupt comming back, as the mask will set
a bit near the source but the interrupt would have already
been missed.  (most irqs in xics systems are level).
 
Thanks to segher for noticing the order.   However...
 
 
 
> diff --git a/arch/powerpc/platforms/pseries/xics.c
> b/arch/powerpc/platforms/pseries/xics.c index
> 0fc830f..4c692b2 100644 ---
> a/arch/powerpc/platforms/pseries/xics.c +++
> b/arch/powerpc/platforms/pseries/xics.c @@ -321,21 +321,26
> @@ static unsigned int xics_startup(unsigned int virq)
>      return 0;
>  }
>  
> +static void xics_eoi_hwirq_direct(unsigned int hwirq)
> +{
> +    iosync();
> +    direct_xirr_info_set((0xff << 24) | hwirq);
> +}
> +
>  static void xics_eoi_direct(unsigned int virq)
>  {
> -    unsigned int irq = (unsigned int)irq_map[virq].hwirq;
> +    xics_eoi_hwirq_direct((unsigned
> int)irq_map[virq].hwirq); +}
>  
> +static void xics_eoi_hwirq_lpar(unsigned int hwirq)
> +{
>      iosync();
> -    direct_xirr_info_set((0xff << 24) | irq);
> +    lpar_xirr_info_set((0xff << 24) | hwirq);
>  }
>  
> -
>  static void xics_eoi_lpar(unsigned int virq)
>  {
> -    unsigned int irq = (unsigned int)irq_map[virq].hwirq;
> -
> -    iosync();
> -    lpar_xirr_info_set((0xff << 24) | irq);
> +    xics_eoi_hwirq_lpar((unsigned
> int)irq_map[virq].hwirq);
>  }
>  
>  static inline unsigned int xics_remap_irq(unsigned int
> vec) @@ -350,9 +355,15 @@ static inline unsigned int
> xics_remap_irq(unsigned int vec)
>      if (likely(irq != NO_IRQ))
>          return irq;
>  
> -    printk(KERN_ERR "Interrupt %u (real) is invalid,"
> -           " disabling it.\n", vec);
> +    pr_err("%s: no mapping for hwirq %u, disabling it.\n"
> , __func__, vec); +
>      xics_mask_real_irq(vec);
> +
> +    if (firmware_has_feature(FW_FEATURE_LPAR))
> +        xics_eoi_hwirq_lpar(vec);
> +    else
> +        xics_eoi_hwirq_direct(vec);
> +
>      return NO_IRQ;
>  }
 
 
I really dislike this great big if in the middle of a
function.
we called this function from two different call sites where
the
choice of which to call was based on their environment.
 
Please move the call to eoi the hwirq to the callers, who
know
which path to take.
 
I guess it might make sense to put the printk in a 
mask_invalid_real_irq wrapper, and then perhaps just
duplicate 
the small remaining code?   Or split the return of spurious
from NO_IRQ (ie should spurious be NO_IRQ_IGNORE?)
 
>  
> -- 
> 1.5.5
> 

^ permalink raw reply

* Re: [PATCH add immr alias 1/4] powerpc: Teach get_immrbase to use immr alias if it exists.
From: Stephen Rothwell @ 2008-08-06  3:41 UTC (permalink / raw)
  To: John Rigby; +Cc: linuxppc-dev
In-Reply-To: <1217967220-30557-1-git-send-email-jrigby@freescale.com>

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

Hi John,

[From further in the discussion, this may no longer be relevant ...]

On Tue,  5 Aug 2008 14:13:37 -0600 John Rigby <jrigby@freescale.com> wrote:
>
> -	soc = of_find_node_by_type(NULL, "soc");
> +	/*
> +	 * First look for an immr alias
> +	 */
> +	np = of_find_node_by_name(NULL, "/aliases");
> +	if (np) {
> +		path = of_get_property(np, "immr", NULL);
> +		if (path)
> +			soc = of_find_node_by_name(NULL, path);

		of_node_put(np);

> +	}
-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* libfdt: Implement fdt_get_property_namelen() and fdt_getprop_namelen()
From: David Gibson @ 2008-08-06  4:50 UTC (permalink / raw)
  To: Jon Loeliger; +Cc: linuxppc-dev

As well as fdt_subnode_offset(), libfdt includes an
fdt_subnode_offset_namelen() function that takes the subnode name to
look up not as a NUL-terminated string, but as a string with an
explicit length.  This can be useful when the caller has the name as
part of a longer string, such as a full path.

However, we don't have corresponding 'namelen' versions for
fdt_get_property() and fdt_getprop().  There are less obvious use
cases for these variants on property names, but there are
circumstances where they can be useful e.g. looking up property names
which need to be parsed from a longer string buffer such as user input
or a configuration file, or looking up an alias in a path with
IEEE1275 style aliases.

So, since it's very easy to implement such variants, this patch does
so.  The original NUL-terminated variants are, of course, implemented
in terms of the namelen versions.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>

Index: dtc/libfdt/fdt_ro.c
===================================================================
--- dtc.orig/libfdt/fdt_ro.c	2008-08-05 14:12:17.000000000 +1000
+++ dtc/libfdt/fdt_ro.c	2008-08-05 14:39:19.000000000 +1000
@@ -80,6 +80,14 @@ const char *fdt_string(const void *fdt, 
 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
 }
 
+static int _fdt_string_eq(const void *fdt, int stroffset,
+			  const char *s, int len)
+{
+	const char *p = fdt_string(fdt, stroffset);
+
+	return (strlen(p) == len) && (memcmp(p, s, len) == 0);
+}
+
 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
 {
 	FDT_CHECK_HEADER(fdt);
@@ -175,9 +183,10 @@ const char *fdt_get_name(const void *fdt
 	return NULL;
 }
 
-const struct fdt_property *fdt_get_property(const void *fdt,
-					    int nodeoffset,
-					    const char *name, int *lenp)
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+						    int nodeoffset,
+						    const char *name,
+						    int namelen, int *lenp)
 {
 	uint32_t tag;
 	const struct fdt_property *prop;
@@ -210,7 +219,7 @@ const struct fdt_property *fdt_get_prope
 			if (! prop)
 				goto fail;
 			namestroff = fdt32_to_cpu(prop->nameoff);
-			if (strcmp(fdt_string(fdt, namestroff), name) == 0) {
+			if (_fdt_string_eq(fdt, namestroff, name, namelen)) {
 				/* Found it! */
 				int len = fdt32_to_cpu(prop->len);
 				prop = fdt_offset_ptr(fdt, offset,
@@ -238,18 +247,32 @@ const struct fdt_property *fdt_get_prope
 	return NULL;
 }
 
-const void *fdt_getprop(const void *fdt, int nodeoffset,
-		  const char *name, int *lenp)
+const struct fdt_property *fdt_get_property(const void *fdt,
+					    int nodeoffset,
+					    const char *name, int *lenp)
+{
+	return fdt_get_property_namelen(fdt, nodeoffset, name,
+					strlen(name), lenp);
+}
+
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+				const char *name, int namelen, int *lenp)
 {
 	const struct fdt_property *prop;
 
-	prop = fdt_get_property(fdt, nodeoffset, name, lenp);
+	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
 	if (! prop)
 		return NULL;
 
 	return prop->data;
 }
 
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+			const char *name, int *lenp)
+{
+	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
+}
+
 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
 {
 	const uint32_t *php;
Index: dtc/libfdt/libfdt.h
===================================================================
--- dtc.orig/libfdt/libfdt.h	2008-08-05 14:29:15.000000000 +1000
+++ dtc/libfdt/libfdt.h	2008-08-05 14:40:58.000000000 +1000
@@ -343,6 +343,22 @@ int fdt_path_offset(const void *fdt, con
 const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
 
 /**
+ * fdt_get_property_namelen - find a property based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_get_property_namelen(), but only examine the first
+ * namelen characters of name for matching the property name.
+ */
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+						    int nodeoffset,
+						    const char *name,
+						    int namelen, int *lenp);
+
+/**
  * fdt_get_property - find a given property in a given node
  * @fdt: pointer to the device tree blob
  * @nodeoffset: offset of the node whose property to find
@@ -380,6 +396,20 @@ static inline struct fdt_property *fdt_g
 }
 
 /**
+ * fdt_getprop_namelen - get property value based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_getprop(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+				const char *name, int namelen, int *lenp);
+
+/**
  * fdt_getprop - retrieve the value of a given property
  * @fdt: pointer to the device tree blob
  * @nodeoffset: offset of the node whose property to find

-- 
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: [PATCH] powerpc: EOI spurious irqs during boot so they can be reenabled later
From: Michael Ellerman @ 2008-08-06  4:53 UTC (permalink / raw)
  To: miltonm; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <489912b6.cc.1809.1931903668@bga.com>

On Tue, 2008-08-05 at 20:55 -0600, miltonm wrote:
> ----- Original Message Follows -----
> From: Michael Ellerman <michael@ellerman.id.au>
> To: Paul Mackerras <paulus@samba.org>
> Cc: <linuxppc-dev@ozlabs.org>, Milton Miller
> <miltonm@bga.com>, Segher Boessenkool
> <segher@kernel.crashing.org>
> Subject: [PATCH] powerpc: EOI spurious irqs during boot so
> they can be reenabled later
> Date: Wed,  6 Aug 2008 12:03:37 +1000 (EST)
>  
> > In the xics code, if we receive an irq during boot that
> > hasn't been setup yet - ie. we have no reverse mapping for
> > it - we mask the irq so we don't hear from it again.
> > 
> > Later on if someone request_irq()'s that irq, we'll unmask
> > it but it will still never fire. This is because we never
> > EOI'ed the irq when we originally received it - when it
> > was spurious.
> > 
> > This can be reproduced trivially by banging the keyboard
> > while kexec'ing on a P5 LPAR, even though the hvc_console
> > driver request's the console irq, the console is
> > non-functional because we're receiving no console
> > interrupts.
> > 
>  
> which means some driver didn't disable interrupts on its
> shutdown, but I digress.

But in the case of kdump the driver never gets a chance to shutdown its
irq, but I digress too :)

> > diff --git a/arch/powerpc/platforms/pseries/xics.c
> > b/arch/powerpc/platforms/pseries/xics.c index
> > 0fc830f..4c692b2 100644 ---
> > a/arch/powerpc/platforms/pseries/xics.c +++
> > b/arch/powerpc/platforms/pseries/xics.c @@ -321,21 +321,26
> > @@ static unsigned int xics_startup(unsigned int virq)
> >      return 0;
> >  }
> >  
> > +static void xics_eoi_hwirq_direct(unsigned int hwirq)
> > +{
> > +    iosync();
> > +    direct_xirr_info_set((0xff << 24) | hwirq);
> > +}
> > +
> >  static void xics_eoi_direct(unsigned int virq)
> >  {
> > -    unsigned int irq = (unsigned int)irq_map[virq].hwirq;
> > +    xics_eoi_hwirq_direct((unsigned
> > int)irq_map[virq].hwirq); +}
> >  
> > +static void xics_eoi_hwirq_lpar(unsigned int hwirq)
> > +{
> >      iosync();
> > -    direct_xirr_info_set((0xff << 24) | irq);
> > +    lpar_xirr_info_set((0xff << 24) | hwirq);
> >  }
> >  
> > -
> >  static void xics_eoi_lpar(unsigned int virq)
> >  {
> > -    unsigned int irq = (unsigned int)irq_map[virq].hwirq;
> > -
> > -    iosync();
> > -    lpar_xirr_info_set((0xff << 24) | irq);
> > +    xics_eoi_hwirq_lpar((unsigned
> > int)irq_map[virq].hwirq);
> >  }
> >  
> >  static inline unsigned int xics_remap_irq(unsigned int
> > vec) @@ -350,9 +355,15 @@ static inline unsigned int
> > xics_remap_irq(unsigned int vec)
> >      if (likely(irq != NO_IRQ))
> >          return irq;
> >  
> > -    printk(KERN_ERR "Interrupt %u (real) is invalid,"
> > -           " disabling it.\n", vec);
> > +    pr_err("%s: no mapping for hwirq %u, disabling it.\n"
> > , __func__, vec); +
> >      xics_mask_real_irq(vec);
> > +
> > +    if (firmware_has_feature(FW_FEATURE_LPAR))
> > +        xics_eoi_hwirq_lpar(vec);
> > +    else
> > +        xics_eoi_hwirq_direct(vec);
> > +
> >      return NO_IRQ;
> >  }
>  
> 
> I really dislike this great big if in the middle of a
> function.
> we called this function from two different call sites where
> the
> choice of which to call was based on their environment.
>  
> Please move the call to eoi the hwirq to the callers, who
> know which path to take.

It's not pretty, but the alternative is worse I think:

>From 0a908825c8de6cd4c26288aba8c5b7fe3ed0a69f Mon Sep 17 00:00:00 2001
From: Michael Ellerman <michael@ellerman.id.au>
Date: Tue, 5 Aug 2008 22:34:48 +1000
Subject: [PATCH] powerpc: EOI spurious irqs during boot so they can be reenabled later

In the xics code, if we receive an irq during boot that hasn't been setup
yet - ie. we have no reverse mapping for it - we mask the irq so we don't
hear from it again.

Later on if someone request_irq()'s that irq, we'll unmask it but it will
still never fire. This is because we never EOI'ed the irq when we originally
received it - when it was spurious.

This can be reproduced trivially by banging the keyboard while kexec'ing on
a P5 LPAR, even though the hvc_console driver request's the console irq, the
console is non-functional because we're receiving no console interrupts.

So when we receive a spurious irq mask it and then EOI it.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
 arch/powerpc/platforms/pseries/xics.c |   74 ++++++++++++++++++++++-----------
 1 files changed, 50 insertions(+), 24 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c
index 0fc830f..754706c 100644
--- a/arch/powerpc/platforms/pseries/xics.c
+++ b/arch/powerpc/platforms/pseries/xics.c
@@ -90,7 +90,7 @@ static inline unsigned int direct_xirr_info_get(void)
 {
 	int cpu = smp_processor_id();
 
-	return in_be32(&xics_per_cpu[cpu]->xirr.word);
+	return in_be32(&xics_per_cpu[cpu]->xirr.word) & 0x00ffffff;
 }
 
 static inline void direct_xirr_info_set(int value)
@@ -124,7 +124,8 @@ static inline unsigned int lpar_xirr_info_get(void)
 	lpar_rc = plpar_xirr(&return_value);
 	if (lpar_rc != H_SUCCESS)
 		panic(" bad return code xirr - rc = %lx \n", lpar_rc);
-	return (unsigned int)return_value;
+
+	return (unsigned int)return_value & 0x00ffffff;
 }
 
 static inline void lpar_xirr_info_set(int value)
@@ -321,49 +322,74 @@ static unsigned int xics_startup(unsigned int virq)
 	return 0;
 }
 
+static void xics_eoi_hwirq_direct(unsigned int hwirq)
+{
+	iosync();
+	direct_xirr_info_set((0xff << 24) | hwirq);
+}
+
 static void xics_eoi_direct(unsigned int virq)
 {
-	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
+	xics_eoi_hwirq_direct((unsigned int)irq_map[virq].hwirq);
+}
 
+static void xics_eoi_hwirq_lpar(unsigned int hwirq)
+{
 	iosync();
-	direct_xirr_info_set((0xff << 24) | irq);
+	lpar_xirr_info_set((0xff << 24) | hwirq);
 }
 
-
 static void xics_eoi_lpar(unsigned int virq)
 {
-	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
-
-	iosync();
-	lpar_xirr_info_set((0xff << 24) | irq);
+	xics_eoi_hwirq_lpar((unsigned int)irq_map[virq].hwirq);
 }
 
 static inline unsigned int xics_remap_irq(unsigned int vec)
 {
-	unsigned int irq;
-
-	vec &= 0x00ffffff;
-
-	if (vec == XICS_IRQ_SPURIOUS)
-		return NO_IRQ;
-	irq = irq_radix_revmap(xics_host, vec);
-	if (likely(irq != NO_IRQ))
-		return irq;
+	return irq_radix_revmap(xics_host, vec);
+}
 
-	printk(KERN_ERR "Interrupt %u (real) is invalid,"
-	       " disabling it.\n", vec);
-	xics_mask_real_irq(vec);
-	return NO_IRQ;
+static void xics_report_bad_irq(unsigned int hwirq)
+{
+	pr_err("%s: no mapping for hwirq %u, disabling it.\n", __func__, hwirq);
 }
 
 static unsigned int xics_get_irq_direct(void)
 {
-	return xics_remap_irq(direct_xirr_info_get());
+	unsigned int virq, hwirq;
+
+	hwirq = direct_xirr_info_get();
+	if (hwirq == XICS_IRQ_SPURIOUS)
+		return NO_IRQ;
+
+	virq = xics_remap_irq(hwirq);
+
+	if (unlikely(virq == NO_IRQ)) {
+		xics_report_bad_irq(hwirq);
+		xics_mask_real_irq(hwirq);
+		xics_eoi_hwirq_direct(hwirq);
+	}
+
+	return virq;
 }
 
 static unsigned int xics_get_irq_lpar(void)
 {
-	return xics_remap_irq(lpar_xirr_info_get());
+	unsigned int virq, hwirq;
+
+	hwirq = lpar_xirr_info_get();
+	if (hwirq == XICS_IRQ_SPURIOUS)
+		return NO_IRQ;
+
+	virq = xics_remap_irq(hwirq);
+
+	if (unlikely(virq == NO_IRQ)) {
+		xics_report_bad_irq(hwirq);
+		xics_mask_real_irq(hwirq);
+		xics_eoi_hwirq_lpar(hwirq);
+	}
+
+	return virq;
 }
 
 #ifdef CONFIG_SMP
-- 
1.5.5

^ permalink raw reply related

* [RFC PATCH] Link the bootwrapper as a position-independent executable
From: Paul Mackerras @ 2008-08-06  5:40 UTC (permalink / raw)
  To: linuxppc-dev

This changes the way we make the boot wrapper position-independent.
Before we just added the offset (the difference between runtime
address and link address) to each entry in the .got2 section.  That
doesn't relocate pointer values in initialized variables and arrays.

Instead we now link the bootwrapper with -pie to get a position-
independent executable, and process the relocations in the dynamic
relocation section that the linker puts into the executable.  This
means that initialized variables and arrays get relocated properly.

Currently we only process R_PPC_RELATIVE relocations, which is all we
get provided C files are compiled with -fPIC (which they are already)
and assembly code is written to be position-independent.  Some of the
changes below are to make parts of crt0.S be PIC code (i.e. generate
no relocations other than R_PPC_RELATIVE).

The relocation code below does nothing if there is no dynamic section,
which means that we can link without -pie and it will work provided
that the bootwrapper executable is loaded at its linked-at address.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/arch/powerpc/boot/crt0.S b/arch/powerpc/boot/crt0.S
index f1c4dfc..a6901c3 100644
--- a/arch/powerpc/boot/crt0.S
+++ b/arch/powerpc/boot/crt0.S
@@ -6,16 +6,28 @@
  * as published by the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  *
- * NOTE: this code runs in 32 bit mode and is packaged as ELF32.
+ * NOTE: this code runs in 32 bit mode, is position-independent,
+ * and is packaged as ELF32.
  */
 
 #include "ppc_asm.h"
 
 	.text
-	/* a procedure descriptor used when booting this as a COFF file */
+	/* A procedure descriptor used when booting this as a COFF file.
+	 * When making COFF, this comes first in the link and we're
+	 * linked at 0x500000.
+	 */
 	.globl	_zimage_start_opd
 _zimage_start_opd:
-	.long	_zimage_start, 0, 0, 0
+	.long	0x500000, 0, 0, 0
+
+p_start:	.long	_start
+p_etext:	.long	_etext
+p_bss_start:	.long	__bss_start
+p_end:		.long	_end
+
+	.weak	_platform_stack_top
+p_pstack:	.long	_platform_stack_top
 
 	.weak	_zimage_start
 	.globl	_zimage_start
@@ -24,37 +36,60 @@ _zimage_start:
 _zimage_start_lib:
 	/* Work out the offset between the address we were linked at
 	   and the address where we're running. */
-	bl	1f
-1:	mflr	r0
-	lis	r9,1b@ha
-	addi	r9,r9,1b@l
-	subf.	r0,r9,r0
-	beq	3f		/* if running at same address as linked */
+	bl	.+4
+p_base:	mflr	r10		/* r10 now points to runtime addr of p_base */
+	/* grab the link address of the dynamic section in r11 */
+	addis	r11,r10,(_GLOBAL_OFFSET_TABLE_-p_base)@ha
+	lwz	r11,(_GLOBAL_OFFSET_TABLE_-p_base)@l(r11)
+	cmpwi	r11,0
+	beq	3f		/* if not linked -pie */
+	/* get the runtime address of the dynamic section in r12 */
+	.weak	__dynamic_start
+	addis	r12,r10,(__dynamic_start-p_base)@ha
+	addi	r12,r12,(__dynamic_start-p_base)@l
+	subf	r11,r11,r12	/* runtime - linktime offset */
 
-	/* The .got2 section contains a list of addresses, so add
-	   the address offset onto each entry. */
-	lis	r9,__got2_start@ha
-	addi	r9,r9,__got2_start@l
-	lis	r8,__got2_end@ha
-	addi	r8,r8,__got2_end@l
-	subf.	r8,r9,r8
+	/* The dynamic section contains a series of tagged entries.
+	 * We need the RELA and RELACOUNT entries. */
+RELA = 7
+RELACOUNT = 0x6ffffff9
+	li	r9,0
+	li	r0,0
+9:	lwz	r8,0(r12)	/* get tag */
+	cmpwi	r8,0
+	beq	10f		/* end of list */
+	cmpwi	r8,RELA
+	bne	11f
+	lwz	r9,4(r12)	/* get RELA pointer in r9 */
+	b	12f
+11:	addis	r8,r8,(-RELACOUNT)@ha
+	cmpwi	r8,RELACOUNT@l
+	bne	12f
+	lwz	r0,4(r12)	/* get RELACOUNT value in r0 */
+12:	addi	r12,r12,8
+	b	9b
+
+	/* The relocation section contains a list of relocations.
+	 * We now do the R_PPC_RELATIVE ones, which point to words
+	 * which need to be initialized with addend + offset.
+	 * The R_PPC_RELATIVE ones come first and there are RELACOUNT
+	 * of them. */
+10:	or.	r8,r0,r9	/* skip relocation if we don't have both */
 	beq	3f
-	srwi.	r8,r8,2
-	mtctr	r8
-	add	r9,r0,r9
-2:	lwz	r8,0(r9)
-	add	r8,r8,r0
-	stw	r8,0(r9)
-	addi	r9,r9,4
+	mtctr	r0
+2:	lbz	r0,4+3(r9)	/* ELF32_R_INFO(reloc->r_info) */
+	cmpwi	r0,22		/* R_PPC_RELATIVE */
+	bne	3f
+	lwz	r12,0(r9)	/* reloc->r_offset */
+	lwz	r0,8(r9)	/* reloc->r_addend */
+	add	r0,r0,r11
+	stwx	r0,r11,r12
+	addi	r9,r9,12
 	bdnz	2b
 
 	/* Do a cache flush for our text, in case the loader didn't */
-3:	lis	r9,_start@ha
-	addi	r9,r9,_start@l
-	add	r9,r0,r9
-	lis	r8,_etext@ha
-	addi	r8,r8,_etext@l
-	add	r8,r0,r8
+3:	lwz	r9,p_start-p_base(r10)	/* note: these are relocated now */
+	lwz	r8,p_etext-p_base(r10)
 4:	dcbf	r0,r9
 	icbi	r0,r9
 	addi	r9,r9,0x20
@@ -64,27 +99,19 @@ _zimage_start_lib:
 	isync
 
 	/* Clear the BSS */
-	lis	r9,__bss_start@ha
-	addi	r9,r9,__bss_start@l
-	add	r9,r0,r9
-	lis	r8,_end@ha
-	addi	r8,r8,_end@l
-	add	r8,r0,r8
-	li	r10,0
-5:	stw	r10,0(r9)
+	lwz	r9,p_bss_start-p_base(r10)
+	lwz	r8,p_end-p_base(r10)
+	li	r0,0
+5:	stw	r0,0(r9)
 	addi	r9,r9,4
 	cmplw	cr0,r9,r8
 	blt	5b
 
 	/* Possibly set up a custom stack */
-.weak	_platform_stack_top
-	lis	r8,_platform_stack_top@ha
-	addi	r8,r8,_platform_stack_top@l
+	lwz	r8,p_pstack-p_base(r10)
 	cmpwi	r8,0
 	beq	6f
-	add	r8,r0,r8
 	lwz	r1,0(r8)
-	add	r1,r0,r1
 	li	r0,0
 	stwu	r0,-16(r1)	/* establish a stack frame */
 6:
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index 644bf9d..64653b3 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -39,6 +39,7 @@ dts=
 cacheit=
 binary=
 gzip=.gz
+pie=-pie
 
 # cross-compilation prefix
 CROSS=
@@ -149,9 +150,10 @@ pmac|chrp)
     platformo=$object/of.o
     ;;
 coff)
-    platformo=$object/of.o
+    platformo=$object/crt0.o $object/of.o
     lds=$object/zImage.coff.lds
     link_address='0x500000'
+    pie=
     ;;
 miboot|uboot)
     # miboot and U-boot want just the bare bits, not an ELF binary
@@ -197,6 +199,7 @@ ps3)
     ksection=.kernel:vmlinux.bin
     isection=.kernel:initrd
     link_address=''
+    pie=
     ;;
 ep88xc|ep405|ep8248e)
     platformo="$object/fixed-head.o $object/$platform.o"
@@ -288,9 +291,9 @@ fi
 
 if [ "$platform" != "miboot" ]; then
     if [ -n "$link_address" ] ; then
-        text_start="-Ttext $link_address --defsym _start=$link_address"
+        text_start="-Ttext $link_address"
     fi
-    ${CROSS}ld -m elf32ppc -T $lds $text_start -o "$ofile" \
+    ${CROSS}ld -m elf32ppc -T $lds $text_start $pie -o "$ofile" \
 	$platformo $tmp $object/wrapper.a
     rm $tmp
 fi
diff --git a/arch/powerpc/boot/zImage.coff.lds.S b/arch/powerpc/boot/zImage.coff.lds.S
index 856dc78..de4c9e3 100644
--- a/arch/powerpc/boot/zImage.coff.lds.S
+++ b/arch/powerpc/boot/zImage.coff.lds.S
@@ -3,13 +3,13 @@ ENTRY(_zimage_start_opd)
 EXTERN(_zimage_start_opd)
 SECTIONS
 {
-  _start = .;
   .text      :
   {
+    _start = .;
     *(.text)
     *(.fixup)
+    _etext = .;
   }
-  _etext = .;
   . = ALIGN(4096);
   .data    :
   {
@@ -17,9 +17,7 @@ SECTIONS
     *(.data*)
     *(__builtin_*)
     *(.sdata*)
-    __got2_start = .;
     *(.got2)
-    __got2_end = .;
 
     _dtb_start = .;
     *(.kernel:dtb)
diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S
index 0962d62..05af879 100644
--- a/arch/powerpc/boot/zImage.lds.S
+++ b/arch/powerpc/boot/zImage.lds.S
@@ -3,49 +3,63 @@ ENTRY(_zimage_start)
 EXTERN(_zimage_start)
 SECTIONS
 {
-  _start = .;
   .text      :
   {
+    _start = .;
     *(.text)
     *(.fixup)
+    _etext = .;
   }
-  _etext = .;
   . = ALIGN(4096);
   .data    :
   {
     *(.rodata*)
     *(.data*)
     *(.sdata*)
-    __got2_start = .;
     *(.got2)
-    __got2_end = .;
   }
+  .dynsym : { *(.dynsym) }
+  .dynstr : { *(.dynstr) }
+  .dynamic :
+  {
+    __dynamic_start = .;
+    *(.dynamic)
+  }
+  .hash : { *(.hash) }
+  .interp : { *(.interp) }
+  .rela.dyn : { *(.rela*) }
 
   . = ALIGN(8);
-  _dtb_start = .;
-  .kernel:dtb : { *(.kernel:dtb) }
-  _dtb_end = .;
-
-  . = ALIGN(4096);
-  _vmlinux_start =  .;
-  .kernel:vmlinux.strip : { *(.kernel:vmlinux.strip) }
-  _vmlinux_end =  .;
+  .kernel:dtb :
+  {
+    _dtb_start = .;
+    *(.kernel:dtb)
+    _dtb_end = .;
+  }
 
   . = ALIGN(4096);
-  _initrd_start =  .;
-  .kernel:initrd : { *(.kernel:initrd) }
-  _initrd_end =  .;
+  .kernel:vmlinux.strip :
+  {
+    _vmlinux_start =  .;
+    *(.kernel:vmlinux.strip)
+    _vmlinux_end =  .;
+  }
 
   . = ALIGN(4096);
-  _edata  =  .;
+  .kernel:initrd :
+  {
+    _initrd_start =  .;
+    *(.kernel:initrd)
+    _initrd_end =  .;
+  }
 
   . = ALIGN(4096);
-  __bss_start = .;
   .bss       :
   {
-   *(.sbss)
-   *(.bss)
+    _edata  =  .;
+    __bss_start = .;
+    *(.sbss)
+    *(.bss)
+    _end = . ;
   }
-  . = ALIGN(4096);
-  _end = . ;
 }

^ permalink raw reply related

* [RFC/PATCH 0/3] Attempt at making 32bit BAT assignment more intelligent
From: Grant Likely @ 2008-08-06  6:02 UTC (permalink / raw)
  To: linuxppc-dev, benh, paulus, galak, jwboyer; +Cc: miltonm

Here is my attempt to make BAT allocations dynamic instead of hard coded.
The first patch changes setbat() to dynamically assign BATs instead of requiring
the caller to select a BAT on its own.

A primary user of setbat is mmu_mapin_ram() which used to hard code BATs
2 and 3 for mapping as much of RAM as can fit in 2 BATs.  The first patch
changes the routine to try to use as many BATs as it needs to map all of RAM.
(Note: I've still got BAT0 reserved for mapping RAM, so even if lots of other
users of setbat() appear, RAM is guaranteed to be allocated at least 1 BAT).

The first patch also adds an ioremap_bat() function which works like
ioremap(), but uses BATs instead of page tables and can be called really
early (before MMU_init()).  ioremap_bat() mappings persist after MMU_init
is called too so it can be used to map all of an SoC's IMMR region, or
any other IO device for that matter.  I've seen about a 2.5% performance
improvement by using this on a simple network workload with SoC registers
BAT mapped.

The second patch is just a utility function required by the third patch.

The third patch is a new user of ioremap_bat() to implement early debug
support for the mpc5200 platform.

The first patch is the one I really want feedback on.  It shouldn't break
any 32 bit platforms, but I need testing to make sure.  Also, this is my
first attempt at messing with MMU code, so please let me know if I'm doing
anything foolish or dangerous.

Kumar, Josh; I'd appreciate testing to make sure patch 1 doesn't break stuff.

Thanks,
g.

--
Grant Likely, B.Sc. P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* [RFC/PATCH 1/3] powerpc: add ioremap_bat() function for setting up BAT translated IO regions.
From: Grant Likely @ 2008-08-06  6:02 UTC (permalink / raw)
  To: linuxppc-dev, benh, paulus, galak, jwboyer; +Cc: miltonm
In-Reply-To: <20080806055214.30717.86092.stgit@trillian.secretlab.ca>

From: Grant Likely <grant.likely@secretlab.ca>

ioremap_bat() is useful for things like mapping SoC internally memory mapped
register and early text because it allows mappings to devices to be setup
early in the boot process where they are needed, and the mappings persist
after the MMU is configured.

Without ioremap_bat(), setting up the MMU would cause the early text
mappings to get lost and mostly likely result in a kernel panic on the next
attempt at output.

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---

 arch/powerpc/kernel/setup_32.c   |    9 ++
 arch/powerpc/mm/init_32.c        |    7 --
 arch/powerpc/mm/mmu_decl.h       |    4 +
 arch/powerpc/mm/pgtable_32.c     |    2 -
 arch/powerpc/mm/ppc_mmu_32.c     |  148 ++++++++++++++++++++++++++++++++------
 arch/powerpc/sysdev/cpm_common.c |    2 -
 include/asm-powerpc/pgalloc-32.h |    2 +
 7 files changed, 140 insertions(+), 34 deletions(-)

diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 066e65c..7b25b57 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -113,6 +113,15 @@ notrace unsigned long __init early_init(unsigned long dt_ptr)
  */
 notrace void __init machine_init(unsigned long dt_ptr, unsigned long phys)
 {
+	/* Do the bare minimum to start allocting from the IO region so
+	 * that ioremap_bat() works */
+#ifdef CONFIG_HIGHMEM
+	ioremap_base = PKMAP_BASE;
+#else
+	ioremap_base = 0xfe000000UL;	/* for now, could be 0xfffff000 */
+#endif /* CONFIG_HIGHMEM */
+	ioremap_bot = ioremap_base;
+
 	/* Enable early debugging if any specified (see udbg.h) */
 	udbg_early_init();
 
diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c
index 388ceda..a3d9b4e 100644
--- a/arch/powerpc/mm/init_32.c
+++ b/arch/powerpc/mm/init_32.c
@@ -169,13 +169,6 @@ void __init MMU_init(void)
 		ppc_md.progress("MMU:mapin", 0x301);
 	mapin_ram();
 
-#ifdef CONFIG_HIGHMEM
-	ioremap_base = PKMAP_BASE;
-#else
-	ioremap_base = 0xfe000000UL;	/* for now, could be 0xfffff000 */
-#endif /* CONFIG_HIGHMEM */
-	ioremap_bot = ioremap_base;
-
 	/* Map in I/O resources */
 	if (ppc_md.progress)
 		ppc_md.progress("MMU:setio", 0x302);
diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h
index fab3cfa..5027736 100644
--- a/arch/powerpc/mm/mmu_decl.h
+++ b/arch/powerpc/mm/mmu_decl.h
@@ -29,8 +29,8 @@ extern void hash_preload(struct mm_struct *mm, unsigned long ea,
 #ifdef CONFIG_PPC32
 extern void mapin_ram(void);
 extern int map_page(unsigned long va, phys_addr_t pa, int flags);
-extern void setbat(int index, unsigned long virt, phys_addr_t phys,
-		   unsigned int size, int flags);
+extern int setbat(unsigned long virt, phys_addr_t phys,
+		  unsigned int size, int flags);
 extern void settlbcam(int index, unsigned long virt, phys_addr_t phys,
 		      unsigned int size, int flags, unsigned int pid);
 extern void invalidate_tlbcam_entry(int index);
diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
index 2001abd..e96f745 100644
--- a/arch/powerpc/mm/pgtable_32.c
+++ b/arch/powerpc/mm/pgtable_32.c
@@ -55,8 +55,6 @@ extern void hash_page_sync(void);
 #ifdef HAVE_BATS
 extern phys_addr_t v_mapped_by_bats(unsigned long va);
 extern unsigned long p_mapped_by_bats(phys_addr_t pa);
-void setbat(int index, unsigned long virt, phys_addr_t phys,
-	    unsigned int size, int flags);
 
 #else /* !HAVE_BATS */
 #define v_mapped_by_bats(x)	(0UL)
diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index c53145f..62c4603 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -72,41 +72,44 @@ unsigned long p_mapped_by_bats(phys_addr_t pa)
 	return 0;
 }
 
+/**
+ * mmu_mapin_ram - Map as much of RAM as possible into kernel space using BATs
+ */
 unsigned long __init mmu_mapin_ram(void)
 {
 #ifdef CONFIG_POWER4
 	return 0;
 #else
 	unsigned long tot, bl, done;
-	unsigned long max_size = (256<<20);
+	int rc;
 
 	if (__map_without_bats) {
 		printk(KERN_DEBUG "RAM mapped without BATs\n");
 		return 0;
 	}
 
-	/* Set up BAT2 and if necessary BAT3 to cover RAM. */
-
-	/* Make sure we don't map a block larger than the
-	   smallest alignment of the physical address. */
+	/* Set up BATs to cover RAM. */
 	tot = total_lowmem;
-	for (bl = 128<<10; bl < max_size; bl <<= 1) {
-		if (bl * 2 > tot)
+	done = 0;
+	while (done < tot) {
+		/* determine the smallest block size need to map the region.
+		 * Don't use a BAT mapping if the remaining region is less
+		 * that 128k */
+		if (tot - done <= 128<<10)
 			break;
-	}
-
-	setbat(2, KERNELBASE, 0, bl, _PAGE_RAM);
-	done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1;
-	if ((done < tot) && !bat_addrs[3].limit) {
-		/* use BAT3 to cover a bit more */
-		tot -= done;
-		for (bl = 128<<10; bl < max_size; bl <<= 1)
-			if (bl * 2 > tot)
+		for (bl = 128<<10; bl < (256<<20); bl <<= 1)
+			if ((bl * 2) > (tot - done))
 				break;
-		setbat(3, KERNELBASE+done, done, bl, _PAGE_RAM);
-		done = (unsigned long)bat_addrs[3].limit - KERNELBASE + 1;
+
+		/* Allocate the BAT and recalculate amount of RAM mapped */
+		rc = setbat(KERNELBASE+done, done, bl, _PAGE_RAM);
+		if (rc < 0)
+			break;
+		done = (unsigned long)bat_addrs[rc].limit - KERNELBASE + 1;
 	}
 
+	if (done == 0)
+		printk(KERN_CRIT "Weird; No BATs available for RAM.\n");
 	return done;
 #endif
 }
@@ -116,12 +119,29 @@ unsigned long __init mmu_mapin_ram(void)
  * The parameters are not checked; in particular size must be a power
  * of 2 between 128k and 256M.
  */
-void __init setbat(int index, unsigned long virt, phys_addr_t phys,
-		   unsigned int size, int flags)
+int __init setbat(unsigned long virt, phys_addr_t phys,
+		  unsigned int size, int flags)
 {
 	unsigned int bl;
-	int wimgxpp;
-	struct ppc_bat *bat = BATS[index];
+	int wimgxpp, index, nr_bats;
+	struct ppc_bat *bat;
+
+	/* Find a free BAT
+	 *
+	 * Special case; Keep the first entry in reserve for mapping RAM.
+	 * Otherwise the too many other users can prevent RAM from getting
+	 * mapped at all with a BAT.
+	 */
+	index = (flags == _PAGE_RAM) ? 0 : 1;
+	nr_bats = cpu_has_feature(CPU_FTR_HAS_HIGH_BATS) ? 8 : 4;
+	for (; index < nr_bats; index++) {
+		if ((BATS[index][0].batu == 0) && (BATS[index][1].batu == 0))
+			break;
+	}
+	if (index == nr_bats)
+		return -1;
+
+	bat = BATS[index];
 
 	if (((flags & _PAGE_NO_CACHE) == 0) &&
 	    cpu_has_feature(CPU_FTR_NEED_COHERENT))
@@ -162,6 +182,90 @@ void __init setbat(int index, unsigned long virt, phys_addr_t phys,
 	bat_addrs[index].start = virt;
 	bat_addrs[index].limit = virt + ((bl + 1) << 17) - 1;
 	bat_addrs[index].phys = phys;
+	return index;
+}
+
+/**
+ * ioremap_bat - Allow IO regions to be mapped using BAT registers
+ * @addr: physical address of region
+ * @size: size of region
+ *
+ * This routine uses setbat() to set up IO ranges before the MMU is
+ * fully configured.  Regions allocated with this function will
+ * automatically be converted into page table entries once the MMU is able
+ * to accept them.
+ *
+ * This routine can be called really early, before MMU_init() is called.  It
+ * is useful for setting up early debug output consoles and frequently
+ * accessed IO regions, like the internally memory mapped registers (IMMR)
+ * in an SoC.
+ *
+ * Just like in setbat, size must be a power of 2 between 128k and 256M.
+ * It is also assumed that callers are somewhat sane and will not be trying
+ * to call this multiple times on the same region.
+ */
+void __iomem * __init
+ioremap_bat(phys_addr_t addr, unsigned long size)
+{
+	struct ppc_bat *bat;
+	unsigned long v;
+	int i;
+
+	/* BAT mappings must be 128k aligned */
+	if (addr != _ALIGN_DOWN(addr, 128 << 10))
+		return NULL;
+
+	/* Carve out a chunk of the ioremap virtual address region
+	 * Also must be 128k aligned */
+	v = ioremap_bot = _ALIGN_DOWN(ioremap_bot - size, 128 << 10);
+
+	/* Allocate a BAT for this IO region */
+	i = setbat(v, addr, size, _PAGE_IO);
+	if (i < 0)
+		return NULL;
+	bat = BATS[i];
+
+	/*
+	 * IO BAT setting can be loaded immediately.
+	 * This only sets the DBATs.  IBATs are irrelevant for IO ranges
+	 *
+	 * Note: Don't disturb BAT 0; it is dedicated for mapping RAM,
+	 * especially in early boot.  Kernel will break if it gets changed
+	 * here.  (actually, setbat should never return index 0 for IO BAT
+	 * mappings).
+	 */
+	switch(i) {
+	case 1:
+		mtspr(SPRN_DBAT1U, bat[1].batu);
+		mtspr(SPRN_DBAT1L, bat[1].batl);
+		break;
+	case 2:
+		mtspr(SPRN_DBAT2U, bat[1].batu);
+		mtspr(SPRN_DBAT2L, bat[1].batl);
+		break;
+	case 3:
+		mtspr(SPRN_DBAT3U, bat[1].batu);
+		mtspr(SPRN_DBAT3L, bat[1].batl);
+		break;
+	case 4:
+		mtspr(SPRN_DBAT4U, bat[1].batu);
+		mtspr(SPRN_DBAT4L, bat[1].batl);
+		break;
+	case 5:
+		mtspr(SPRN_DBAT5U, bat[1].batu);
+		mtspr(SPRN_DBAT5L, bat[1].batl);
+		break;
+	case 6:
+		mtspr(SPRN_DBAT6U, bat[1].batu);
+		mtspr(SPRN_DBAT6L, bat[1].batl);
+		break;
+	case 7:
+		mtspr(SPRN_DBAT7U, bat[1].batu);
+		mtspr(SPRN_DBAT7L, bat[1].batl);
+		break;
+	}
+
+	return (void __iomem *)v;
 }
 
 /*
diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c
index 53da8a0..b3b4f8c 100644
--- a/arch/powerpc/sysdev/cpm_common.c
+++ b/arch/powerpc/sysdev/cpm_common.c
@@ -56,7 +56,7 @@ void __init udbg_init_cpm(void)
 {
 	if (cpm_udbg_txdesc) {
 #ifdef CONFIG_CPM2
-		setbat(1, 0xf0000000, 0xf0000000, 1024*1024, _PAGE_IO);
+		setbat(0xf0000000, 0xf0000000, 1024*1024, _PAGE_IO);
 #endif
 		udbg_putc = udbg_putc_cpm;
 	}
diff --git a/include/asm-powerpc/pgalloc-32.h b/include/asm-powerpc/pgalloc-32.h
index 58c0714..ea8b23d 100644
--- a/include/asm-powerpc/pgalloc-32.h
+++ b/include/asm-powerpc/pgalloc-32.h
@@ -40,4 +40,6 @@ extern void pte_free(struct mm_struct *mm, pgtable_t pte);
 
 #define check_pgt_cache()	do { } while (0)
 
+extern void __iomem *ioremap_bat(phys_addr_t addr, unsigned long size);
+
 #endif /* _ASM_POWERPC_PGALLOC_32_H */

^ permalink raw reply related

* [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: Grant Likely @ 2008-08-06  6:02 UTC (permalink / raw)
  To: linuxppc-dev, benh, paulus, galak, jwboyer; +Cc: miltonm
In-Reply-To: <20080806055214.30717.86092.stgit@trillian.secretlab.ca>

From: Grant Likely <grant.likely@secretlab.ca>

of_lookup_stdout() is useful for figuring out what device to use as output
for early boot progress messages.  It returns the node pointed to by the
linux,stdout-path property in the chosen node.

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---

 drivers/of/base.c  |   24 ++++++++++++++++++++++++
 include/linux/of.h |    1 +
 2 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index ad8ac1a..10c6a46 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -473,3 +473,27 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
 }
 EXPORT_SYMBOL_GPL(of_modalias_node);
 
+/**
+ * of_lookup_stdout - find node pointed to by chosen linux,stdout-path property
+ *
+ * This routine returns a pointer to the stdout node or NULL on failure
+ */
+struct device_node __init *of_lookup_stdout(void)
+{
+	struct device_node *chosen, *stdout_node;
+	const char *stdout_path;
+
+	chosen = of_find_node_by_path("/chosen");
+	if (!chosen)
+		return NULL;
+
+	stdout_path = of_get_property(chosen, "linux,stdout-path", NULL);
+	if (!stdout_path) {
+		of_node_put(chosen);
+		return NULL;
+	}
+
+	stdout_node = of_find_node_by_path(stdout_path);
+	of_node_put(chosen);
+	return stdout_node;
+}
diff --git a/include/linux/of.h b/include/linux/of.h
index 79886ad..e8b215b 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -71,5 +71,6 @@ extern int of_n_size_cells(struct device_node *np);
 extern const struct of_device_id *of_match_node(
 	const struct of_device_id *matches, const struct device_node *node);
 extern int of_modalias_node(struct device_node *node, char *modalias, int len);
+struct device_node __init *of_lookup_stdout(void);
 
 #endif /* _LINUX_OF_H */

^ permalink raw reply related

* [RFC/PATCH 3/3] powerpc/52xx: add udbg and early debug support for PSC serial console
From: Grant Likely @ 2008-08-06  6:02 UTC (permalink / raw)
  To: linuxppc-dev, benh, paulus, galak, jwboyer; +Cc: miltonm
In-Reply-To: <20080806055214.30717.86092.stgit@trillian.secretlab.ca>

From: Grant Likely <grant.likely@secretlab.ca>

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---

 arch/powerpc/Kconfig.debug                   |   11 +++
 arch/powerpc/kernel/udbg.c                   |    2 
 arch/powerpc/platforms/52xx/lite5200.c       |    6 +
 arch/powerpc/platforms/52xx/mpc5200_simple.c |    3 +
 arch/powerpc/platforms/52xx/mpc52xx_common.c |  109 ++++++++++++++++++++++++++
 include/asm-powerpc/mpc52xx.h                |    1 
 include/asm-powerpc/udbg.h                   |    1 
 7 files changed, 131 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 8c8aadb..26e12d6 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -210,6 +210,12 @@ config PPC_EARLY_DEBUG_40x
 	  inbuilt serial port. This works on chips with a 16550 compatible
 	  UART. Xilinx chips with uartlite cannot use this option.
 
+config PPC_EARLY_DEBUG_5200
+	bool "MPC5200 PSC serial port"
+	depends on PPC_MPC52xx
+	help
+	  Select this to enable early debugging for the Freescale MPC5200 SoC.
+
 config PPC_EARLY_DEBUG_CPM
 	bool "Early serial debugging for Freescale CPM-based serial ports"
 	depends on SERIAL_CPM
@@ -239,6 +245,11 @@ config PPC_EARLY_DEBUG_40x_PHYSADDR
 	depends on PPC_EARLY_DEBUG_40x
 	default "0xef600300"
 
+config PPC_EARLY_DEBUG_5200_PHYSADDR
+	hex "Early debug PSC UART physical address"
+	depends on PPC_EARLY_DEBUG_5200
+	default "0xf0002000"
+
 config PPC_EARLY_DEBUG_CPM_ADDR
 	hex "CPM UART early debug transmit descriptor address"
 	depends on PPC_EARLY_DEBUG_CPM
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index 7d6c9bb..a3a0c13 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -57,6 +57,8 @@ void __init udbg_early_init(void)
 #elif defined(CONFIG_PPC_EARLY_DEBUG_40x)
 	/* PPC40x debug */
 	udbg_init_40x_realmode();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_5200)
+	udbg_init_mpc5200();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_CPM)
 	udbg_init_cpm();
 #endif
diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c
index 6d584f4..0685c2c 100644
--- a/arch/powerpc/platforms/52xx/lite5200.c
+++ b/arch/powerpc/platforms/52xx/lite5200.c
@@ -25,6 +25,7 @@
 #include <asm/machdep.h>
 #include <asm/prom.h>
 #include <asm/mpc52xx.h>
+#include <asm/udbg.h>
 
 /* ************************************************************************
  *
@@ -182,7 +183,8 @@ static int __init lite5200_probe(void)
 	if (!of_flat_dt_is_compatible(node, "fsl,lite5200") &&
 	    !of_flat_dt_is_compatible(node, "fsl,lite5200b"))
 		return 0;
-	pr_debug("%s board found\n", model ? model : "unknown");
+
+	udbg_printf("%s board found\n", model ? model : "unknown");
 
 	return 1;
 }
@@ -191,9 +193,11 @@ define_machine(lite5200) {
 	.name 		= "lite5200",
 	.probe 		= lite5200_probe,
 	.setup_arch 	= lite5200_setup_arch,
+	.init_early	= mpc52xx_udbg_init,
 	.init		= mpc52xx_declare_of_platform_devices,
 	.init_IRQ 	= mpc52xx_init_irq,
 	.get_irq 	= mpc52xx_get_irq,
 	.restart	= mpc52xx_restart,
 	.calibrate_decr	= generic_calibrate_decr,
+	.progress	= udbg_progress,
 };
diff --git a/arch/powerpc/platforms/52xx/mpc5200_simple.c b/arch/powerpc/platforms/52xx/mpc5200_simple.c
index a3bda0b..16daf9d 100644
--- a/arch/powerpc/platforms/52xx/mpc5200_simple.c
+++ b/arch/powerpc/platforms/52xx/mpc5200_simple.c
@@ -30,6 +30,7 @@
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/mpc52xx.h>
+#include <asm/udbg.h>
 
 /*
  * Setup the architecture
@@ -78,9 +79,11 @@ define_machine(mpc5200_simple_platform) {
 	.name		= "mpc5200-simple-platform",
 	.probe		= mpc5200_simple_probe,
 	.setup_arch	= mpc5200_simple_setup_arch,
+	.init_early	= mpc52xx_udbg_init,
 	.init		= mpc52xx_declare_of_platform_devices,
 	.init_IRQ	= mpc52xx_init_irq,
 	.get_irq	= mpc52xx_get_irq,
 	.restart	= mpc52xx_restart,
 	.calibrate_decr	= generic_calibrate_decr,
+	.progress	= udbg_progress,
 };
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
index 4d5fd1d..dcbeb06 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -10,7 +10,7 @@
  *
  */
 
-#undef DEBUG
+#define DEBUG
 
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
@@ -18,6 +18,18 @@
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/mpc52xx.h>
+#include <asm/udbg.h>
+#include <asm/pgalloc.h>
+#include <mm/mmu_decl.h>
+
+/* Programmable Serial Controller (PSC) status register bits */
+#define MPC52xx_PSC_SR		0x04
+#define MPC52xx_PSC_SR_RXRDY		0x0100
+#define MPC52xx_PSC_SR_RXFULL		0x0200
+#define MPC52xx_PSC_SR_TXRDY		0x0400
+#define MPC52xx_PSC_SR_TXEMP		0x0800
+
+#define MPC52xx_PSC_BUFFER	0x0C
 
 /* MPC5200 device tree match tables */
 static struct of_device_id mpc52xx_xlb_ids[] __initdata = {
@@ -36,6 +48,101 @@ static struct of_device_id mpc52xx_bus_ids[] __initdata = {
 	{}
 };
 
+static struct of_device_id mpc52xx_psc_uart_ids[] __initdata = {
+	{ .compatible = "fsl,mpc5200-psc-uart", },
+};
+
+static void __iomem *psc_console;
+
+static void mpc52xx_udbg_putc(char c)
+{
+	while (!(in_be16(psc_console + MPC52xx_PSC_SR) & MPC52xx_PSC_SR_TXRDY))
+		; /* wait for idle */
+	out_8(psc_console + MPC52xx_PSC_BUFFER, c);
+	if (c == '\n')
+		mpc52xx_udbg_putc('\r');
+}
+
+static int mpc52xx_udbg_getc(void)
+{
+	while (!(in_be16(psc_console + MPC52xx_PSC_SR) & MPC52xx_PSC_SR_RXRDY))
+		; /* wait for char */
+	return in_8(psc_console + MPC52xx_PSC_BUFFER);
+}
+
+static int mpc52xx_udbg_getc_poll(void)
+{
+	if (!(in_be16(psc_console + MPC52xx_PSC_SR) & MPC52xx_PSC_SR_RXRDY))
+		return -1;
+
+	return in_8(psc_console + MPC52xx_PSC_BUFFER);
+}
+
+
+#if defined(CONFIG_PPC_EARLY_DEBUG_5200)
+/**
+ * mpc52xx_udbg_init_early - Set up UDBG for early debug
+ *
+ * Called by udbg code if early debug is configured for the MPC5200
+ */
+void __init udbg_init_mpc5200(void)
+{
+	/* At this point, the bootwrapper or u-boot has already set up the
+	 * serial console correctly.  Assume that it is still configured
+	 * for the correct baud rate */
+
+	/* Map the entire IMMR range  (minimum mapping of 128k) */
+	psc_console = ioremap_bat(0xf0000000, 128<<10);
+	if (!psc_console)
+		return;
+
+	/* Adjust upwards for the base address of the PSC. */
+	psc_console += CONFIG_PPC_EARLY_DEBUG_5200_PHYSADDR - 0xf0000000;
+
+	/* See if it works */
+	mpc52xx_udbg_putc('=');
+
+	udbg_putc = mpc52xx_udbg_putc;
+	udbg_getc = mpc52xx_udbg_getc;
+	udbg_getc_poll = mpc52xx_udbg_getc_poll;
+	udbg_printf("psc_console=%p\n", psc_console);
+}
+#endif
+
+/**
+ * Initialize UDBG based on data in the device tree
+ */
+void __init mpc52xx_udbg_init(void)
+{
+	/* Lookup the console node */
+	struct device_node *stdout_node = of_lookup_stdout();
+	if (!stdout_node)
+		return;
+
+	/* Is this a PSC? */
+	if (!of_match_node(mpc52xx_psc_uart_ids, stdout_node)) {
+		of_node_put(stdout_node);
+		return;
+	}
+
+	/* Map the PSC registers */
+	psc_console = of_iomap(stdout_node, 0);
+	of_node_put(stdout_node);
+	if (!psc_console)
+		return;
+
+	/* See if it works */
+	mpc52xx_udbg_putc('+');
+
+	/* At this point, the bootwrapper or u-boot has already set up the
+	 * serial console correctly.  Assume that it is still configured
+	 * for the correct baud rate */
+	udbg_putc = mpc52xx_udbg_putc;
+	udbg_getc = mpc52xx_udbg_getc;
+	udbg_getc_poll = mpc52xx_udbg_getc_poll;
+	udbg_printf("psc_console=%p\n", psc_console);
+}
+
 /*
  * This variable is mapped in mpc52xx_map_wdt() and used in mpc52xx_restart().
  * Permanent mapping is required because mpc52xx_restart() can be called
diff --git a/include/asm-powerpc/mpc52xx.h b/include/asm-powerpc/mpc52xx.h
index 81ef10b..94e7f22 100644
--- a/include/asm-powerpc/mpc52xx.h
+++ b/include/asm-powerpc/mpc52xx.h
@@ -255,6 +255,7 @@ extern void mpc52xx_declare_of_platform_devices(void);
 extern void mpc52xx_map_common_devices(void);
 extern int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv);
 extern void mpc52xx_restart(char *cmd);
+extern void __init mpc52xx_udbg_init(void);
 
 /* mpc52xx_pic.c */
 extern void mpc52xx_init_irq(void);
diff --git a/include/asm-powerpc/udbg.h b/include/asm-powerpc/udbg.h
index 6418cee..ea6dd41 100644
--- a/include/asm-powerpc/udbg.h
+++ b/include/asm-powerpc/udbg.h
@@ -49,6 +49,7 @@ extern void __init udbg_init_debug_beat(void);
 extern void __init udbg_init_btext(void);
 extern void __init udbg_init_44x_as1(void);
 extern void __init udbg_init_40x_realmode(void);
+extern void __init udbg_init_mpc5200(void);
 extern void __init udbg_init_cpm(void);
 
 #endif /* __KERNEL__ */

^ permalink raw reply related

* Re: [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: Michael Ellerman @ 2008-08-06  6:14 UTC (permalink / raw)
  To: Grant Likely; +Cc: miltonm, linuxppc-dev, paulus
In-Reply-To: <20080806060239.30717.79273.stgit@trillian.secretlab.ca>

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

On Wed, 2008-08-06 at 00:02 -0600, Grant Likely wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
> 
> of_lookup_stdout() is useful for figuring out what device to use as output
> for early boot progress messages.  It returns the node pointed to by the
> linux,stdout-path property in the chosen node.

Nice. You don't feel like converting all the places that currently do it
by hand to use this do you :)

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

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

^ permalink raw reply

* Re: [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: David Miller @ 2008-08-06  6:32 UTC (permalink / raw)
  To: grant.likely; +Cc: miltonm, linuxppc-dev, paulus
In-Reply-To: <20080806060239.30717.79273.stgit@trillian.secretlab.ca>

From: Grant Likely <grant.likely@secretlab.ca>
Date: Wed, 06 Aug 2008 00:02:44 -0600

> of_lookup_stdout() is useful for figuring out what device to use as output
> for early boot progress messages.  It returns the node pointed to by the
> linux,stdout-path property in the chosen node.
> 
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

On sparc platforms this is obtained differently.  We obtain the 32-bit
instance value of "/chosen/stdout" and convert that into a prom device
node path using "instance-to-path".

If this of_lookup_stdout() is going into generic OF code, it should be
done in a way that works on all platforms.  All of these "linux,*"
properties and node names are powerpc platform specific.

^ permalink raw reply

* Re: [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: Grant Likely @ 2008-08-06  6:34 UTC (permalink / raw)
  To: michael; +Cc: miltonm, linuxppc-dev, paulus
In-Reply-To: <1218003249.7893.19.camel@localhost>

On Wed, Aug 6, 2008 at 12:14 AM, Michael Ellerman
<michael@ellerman.id.au> wrote:
> On Wed, 2008-08-06 at 00:02 -0600, Grant Likely wrote:
>> From: Grant Likely <grant.likely@secretlab.ca>
>>
>> of_lookup_stdout() is useful for figuring out what device to use as output
>> for early boot progress messages.  It returns the node pointed to by the
>> linux,stdout-path property in the chosen node.
>
> Nice. You don't feel like converting all the places that currently do it
> by hand to use this do you :)

Yep, I'll do this.  :-)

g.

^ permalink raw reply

* Re: [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: Grant Likely @ 2008-08-06  6:35 UTC (permalink / raw)
  To: David Miller; +Cc: miltonm, linuxppc-dev, paulus
In-Reply-To: <20080805.233205.201125898.davem@davemloft.net>

On Wed, Aug 6, 2008 at 12:32 AM, David Miller <davem@davemloft.net> wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
> Date: Wed, 06 Aug 2008 00:02:44 -0600
>
>> of_lookup_stdout() is useful for figuring out what device to use as output
>> for early boot progress messages.  It returns the node pointed to by the
>> linux,stdout-path property in the chosen node.
>>
>> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
>
> On sparc platforms this is obtained differently.  We obtain the 32-bit
> instance value of "/chosen/stdout" and convert that into a prom device
> node path using "instance-to-path".

How about if I modify it to try both methods?

g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* Re: nfsd, v4: oops in find_acceptable_alias, ppc32 Linux, post-2.6.27-rc1
From: Benjamin Herrenschmidt @ 2008-08-06  6:29 UTC (permalink / raw)
  To: Paul Collins
  Cc: J. Bruce Fields, Neil Brown, nfsv4, linux-kernel, linuxppc-dev
In-Reply-To: <1217920618.24157.161.camel@pasglop>

On Tue, 2008-08-05 at 17:16 +1000, Benjamin Herrenschmidt wrote:
> On Tue, 2008-08-05 at 16:47 +1200, Paul Collins wrote:
> > It's about four years old.  It was in storage for about six months and I
> > got it repaired a few weeks ago (display cable and inverter).  The sort
> > of crazy crap I've been reporting certainly smacks of memory corruption.
> > But on the other hand, 2.6.25 (Debian's) and 2.6.26 (my own) have been
> > trouble-free.
> 
> Any chance you can bisect the problem ?

Ok, so I can reproduce on a few 32 bits configs with ftrace enabled.

Looks like some non volatile GPRs get corrupted. I don't know yet if
ftrace is the culprit though, I couldn't find anything obviously wrong
with the mcount implementation we have.

It looks like the corrupted GPR has been saved/restored on the stack
and that the corruption is due to the stack itself being written
to. It's not clear by whome though and in what circumstances.

We'll have to dig more.

Cheers,
Ben.

^ permalink raw reply

* Re: patches for 2.6.27... (Kumar Gala)
From: Heiko Schocher @ 2008-08-06  6:58 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <486B512B.9000004@denx.de>

Hello Kumar,

On Wed Jul 2 19:58:03 EST 2008 Heiko Schocher wrote:
>On Wednesday 02 July 2008, Kumar Gala wrote:
>> Please point out any patches that have been posted but havent made it
>> into a git tree related to Freescale chips.
>
> Here are 2 patches that I'd like to see in 2.6.27. They haven't made into
> any git tree as far as I know.
>
> http://ozlabs.org/pipermail/linuxppc-dev/2008-June/058118.html
>
> http://ozlabs.org/pipermail/linuxppc-dev/2008-June/057972.html

Can you tell me, if these patches go into the Tree?
I had generally acks for this boards, but didnt see them in any tree yet?

Are there some more issues?

TIA
Heiko
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

^ permalink raw reply

* [PATCH 1/6] powerpc: update flash size and partition in mpc8272ads dts
From: Li Yang @ 2008-08-06  7:04 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Li Yang

Make the flash size 8M to be consistent with the module comes with board.
Add predefined partitions for the flash.  Add ethernet port aliases.

Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/boot/dts/mpc8272ads.dts |   38 +++++++++++++++++++++++++++++----
 1 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts
index 2a1929a..b2ce4c0 100644
--- a/arch/powerpc/boot/dts/mpc8272ads.dts
+++ b/arch/powerpc/boot/dts/mpc8272ads.dts
@@ -17,6 +17,11 @@
 	#address-cells = <1>;
 	#size-cells = <1>;
 
+	aliases {
+		ethernet0 = &enet0;
+		ethernet1 = &enet1;
+	};
+
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -46,15 +51,38 @@
 		#size-cells = <1>;
 		reg = <0xf0010100 0x40>;
 
-		ranges = <0x0 0x0 0xfe000000 0x2000000
+		ranges = <0x0 0x0 0xff800000 0x800000
 		          0x1 0x0 0xf4500000 0x8000
 		          0x3 0x0 0xf8200000 0x8000>;
 
 		flash@0,0 {
-			compatible = "jedec-flash";
-			reg = <0x0 0x0 0x2000000>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "cfi-flash";
+			probe-type = "CFI";
+			reg = <0x0 0x0 0x800000>;
 			bank-width = <4>;
 			device-width = <1>;
+			hrcw@0 {
+				label = "hrcw";
+				reg = <0x0 0x40000>;
+			};
+			fs@40000 {
+				label = "fs";
+				reg = <0x40000 0x540000>;
+			};
+			kernel@580000 {
+				label = "kernel";
+				reg = <0x580000 0x180000>;
+			};
+			u-boot@700000 {
+				label = "u-boot";
+				reg = <0x700000 0x40000>;
+			};
+			u-boot-env@740000 {
+				label = "u-boot-env";
+				reg = <0x740000 0x40000>;
+			};
 		};
 
 		board-control@1,0 {
@@ -192,7 +220,7 @@
 				};
 			};
 
-			ethernet@11300 {
+			enet0: ethernet@11300 {
 				device_type = "network";
 				compatible = "fsl,mpc8272-fcc-enet",
 				             "fsl,cpm2-fcc-enet";
@@ -205,7 +233,7 @@
 				fsl,cpm-command = <0x12000300>;
 			};
 
-			ethernet@11320 {
+			enet1: ethernet@11320 {
 				device_type = "network";
 				compatible = "fsl,mpc8272-fcc-enet",
 				             "fsl,cpm2-fcc-enet";
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH 2/6] powerpc: export cpm2_immr symbol for CPM2 drivers to compile as module
From: Li Yang @ 2008-08-06  7:04 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Li Yang
In-Reply-To: <1218006285-27138-1-git-send-email-leoli@freescale.com>

Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/sysdev/cpm2.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/sysdev/cpm2.c b/arch/powerpc/sysdev/cpm2.c
index f1c3395..021480e 100644
--- a/arch/powerpc/sysdev/cpm2.c
+++ b/arch/powerpc/sysdev/cpm2.c
@@ -52,6 +52,7 @@ cpm_cpm2_t __iomem *cpmp; /* Pointer to comm processor space */
  * the communication processor devices.
  */
 cpm2_map_t __iomem *cpm2_immr;
+EXPORT_SYMBOL(cpm2_immr);
 
 #define CPM_MAP_SIZE	(0x40000)	/* 256k - the PQ3 reserve this amount
 					   of space for CPM as it is larger
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH 3/6] powerpc: update QE/CPM2 headers for USB support
From: Li Yang @ 2008-08-06  7:04 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Li Yang
In-Reply-To: <1218006285-27138-2-git-send-email-leoli@freescale.com>

Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/include/asm/immap_cpm2.h |    9 +++------
 arch/powerpc/include/asm/immap_qe.h   |    9 +++------
 2 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/arch/powerpc/include/asm/immap_cpm2.h b/arch/powerpc/include/asm/immap_cpm2.h
index 4080bab..d4f069b 100644
--- a/arch/powerpc/include/asm/immap_cpm2.h
+++ b/arch/powerpc/include/asm/immap_cpm2.h
@@ -554,14 +554,11 @@ typedef struct usb_ctlr {
 	u8	usb_usadr;
 	u8	usb_uscom;
 	u8	res1[1];
-	u16	usb_usep1;
-	u16	usb_usep2;
-	u16	usb_usep3;
-	u16	usb_usep4;
+	__be16  usb_usep[4];
 	u8	res2[4];
-	u16	usb_usber;
+	__be16  usb_usber;
 	u8	res3[2];
-	u16	usb_usbmr;
+	__be16  usb_usbmr;
 	u8	usb_usbs;
 	u8	res4[7];
 } usb_cpm2_t;
diff --git a/arch/powerpc/include/asm/immap_qe.h b/arch/powerpc/include/asm/immap_qe.h
index 3c2fced..08d616a 100644
--- a/arch/powerpc/include/asm/immap_qe.h
+++ b/arch/powerpc/include/asm/immap_qe.h
@@ -215,10 +215,7 @@ struct usb_ctlr {
 	u8	usb_usadr;
 	u8	usb_uscom;
 	u8	res1[1];
-	__be16	usb_usep1;
-	__be16	usb_usep2;
-	__be16	usb_usep3;
-	__be16	usb_usep4;
+	__be16  usb_usep[4];
 	u8	res2[4];
 	__be16	usb_usber;
 	u8	res3[2];
@@ -472,8 +469,8 @@ extern phys_addr_t get_qe_base(void);
 
 static inline unsigned long immrbar_virt_to_phys(void *address)
 {
-	if ( ((u32)address >= (u32)qe_immr) &&
-			((u32)address < ((u32)qe_immr + QE_IMMAP_SIZE)) )
+	if (((u32)address >= (u32)qe_immr) &&
+			((u32)address < ((u32)qe_immr + QE_IMMAP_SIZE)))
 		return (unsigned long)(address - (u32)qe_immr +
 				(u32)get_qe_base());
 	return (unsigned long)virt_to_phys(address);
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH 4/6] powerpc: add USB peripheral support to MPC8272ADS
From: Li Yang @ 2008-08-06  7:04 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Li Yang
In-Reply-To: <1218006285-27138-3-git-send-email-leoli@freescale.com>

Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/boot/dts/mpc8272ads.dts      |    8 ++++++++
 arch/powerpc/platforms/82xx/mpc8272_ads.c |   25 +++++++++++++++++++++++++
 arch/powerpc/platforms/82xx/pq2ads.h      |    3 +++
 3 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts
index b2ce4c0..75cc94c 100644
--- a/arch/powerpc/boot/dts/mpc8272ads.dts
+++ b/arch/powerpc/boot/dts/mpc8272ads.dts
@@ -256,6 +256,14 @@
 				#address-cells = <1>;
 				#size-cells = <0>;
 			};
+
+			usb@11b60 {
+				compatible = "fsl,qe_udc";
+				reg = <0x11b60 0x40 0x8b00 0x100>;
+				interrupts = <11 8>;
+				interrupt-parent = <&PIC>;
+				mode = "slave";
+			};
 		};
 
 		PIC: interrupt-controller@10c00 {
diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c
index 8054c68..69781e6 100644
--- a/arch/powerpc/platforms/82xx/mpc8272_ads.c
+++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c
@@ -23,6 +23,7 @@
 #include <asm/udbg.h>
 #include <asm/machdep.h>
 #include <asm/time.h>
+#include <asm/fs_pd.h>
 
 #include <platforms/82xx/pq2.h>
 
@@ -100,11 +101,22 @@ static struct cpm_pin mpc8272_ads_pins[] = {
 	/* I2C */
 	{3, 14, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_OPENDRAIN},
 	{3, 15, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_OPENDRAIN},
+
+	/* USB */
+	{2, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* output enable */
+	{2, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* RP */
+	{2, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* RN */
+	{3, 25, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* Rxd */
+	{3, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* TN */
+	{3, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* TP */
+	{2, 24, CPM_PIN_INPUT | CPM_PIN_PRIMARY},  /* Usb_clk */
 };
 
 static void __init init_ioports(void)
 {
 	int i;
+	cpmux_t __iomem *im_cpmux;
+	u32 reg;
 
 	for (i = 0; i < ARRAY_SIZE(mpc8272_ads_pins); i++) {
 		struct cpm_pin *pin = &mpc8272_ads_pins[i];
@@ -119,6 +131,13 @@ static void __init init_ioports(void)
 	cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK10, CPM_CLK_TX);
 	cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK15, CPM_CLK_RX);
 	cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK16, CPM_CLK_TX);
+
+	/* USB use clock of SCC3 */
+	cpm2_clk_setup(CPM_CLK_SCC3, CPM_CLK8, CPM_CLK_TX);
+	/* SCC3 cannot be used in NMSI mode */
+	im_cpmux = cpm2_map(im_cpmux);
+	reg = in_be32(&im_cpmux->cmx_scr);
+	out_be32(&im_cpmux->cmx_scr, reg | CMXSCR_SC3);
 }
 
 static void __init mpc8272_ads_setup_arch(void)
@@ -150,6 +169,12 @@ static void __init mpc8272_ads_setup_arch(void)
 	clrbits32(&bcsr[3], BCSR3_FETHIEN2);
 	setbits32(&bcsr[3], BCSR3_FETH2_RST);
 
+	/* Enabling USB support in BCSR */
+	np = of_find_compatible_node(NULL, NULL, "fsl,qe_udc");
+	if (np != NULL) {
+		clrbits32(&bcsr[3], BCSR3_USB_EN);
+		clrbits32(&bcsr[3], BCSR3_USB_HI_SPEED);
+	}
 	iounmap(bcsr);
 
 	init_ioports();
diff --git a/arch/powerpc/platforms/82xx/pq2ads.h b/arch/powerpc/platforms/82xx/pq2ads.h
index 984db42..d0e82e2 100644
--- a/arch/powerpc/platforms/82xx/pq2ads.h
+++ b/arch/powerpc/platforms/82xx/pq2ads.h
@@ -43,6 +43,9 @@
 #define BCSR1_RS232_EN2		((uint)0x01000000)      /* 0 ==enable */
 #define BCSR3_FETHIEN2		((uint)0x10000000)      /* 0 == enable*/
 #define BCSR3_FETH2_RST		((uint)0x80000000)      /* 0 == reset */
+#define BCSR3_USB_EN      ((uint)0x80000000)        /* 0 == enable*/
+#define BCSR3_USB_HI_SPEED     ((uint)0x40000000)   /* 0 == highspeed */
+#define BCSR3_USBVCC     ((uint)0x20000000)         /* 0 == disable */
 
 /* cpm serial driver works with constants below */
 
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH 5/6] powerpc: add USB peripheral support to MPC836xMDS
From: Li Yang @ 2008-08-06  7:04 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Li Yang
In-Reply-To: <1218006285-27138-4-git-send-email-leoli@freescale.com>

Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/boot/dts/mpc836x_mds.dts     |   15 ++++++-
 arch/powerpc/platforms/83xx/mpc836x_mds.c |   19 ++++++++-
 arch/powerpc/platforms/83xx/mpc83xx.h     |    1 +
 arch/powerpc/platforms/83xx/usb.c         |   67 +++++++++++++++++++++++++++++
 4 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts
index a3b76a7..596377b 100644
--- a/arch/powerpc/boot/dts/mpc836x_mds.dts
+++ b/arch/powerpc/boot/dts/mpc836x_mds.dts
@@ -235,6 +235,17 @@
 					0  2  1  0  1  0>; /* MDC */
 			};
 
+			pio_usb: usb_pin@01 {
+				pio-map = <
+			/* port  pin  dir  open_drain  assignment  has_irq */
+					1  2  1  0  3  0        /* USBOE  */
+					1  3  1  0  3  0        /* USBTP  */
+					1  8  1  0  1  0        /* USBTN  */
+					1 10  2  0  3  0        /* USBRXD */
+					1  9  2  1  3  0        /* USBRP  */
+					1 11  2  1  3  0>;      /* USBRN  */
+			};
+
 		};
 	};
 
@@ -280,11 +291,13 @@
 		};
 
 		usb@6c0 {
-			compatible = "qe_udc";
+			compatible = "fsl,qe_udc";
 			reg = <0x6c0 0x40 0x8b00 0x100>;
 			interrupts = <11>;
 			interrupt-parent = <&qeic>;
 			mode = "slave";
+			usb-clock = <21>;
+			pio-handle = <&pio_usb>;
 		};
 
 		enet0: ucc@2000 {
diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c
index 9d46e5b..92afd40 100644
--- a/arch/powerpc/platforms/83xx/mpc836x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c
@@ -93,6 +93,12 @@ static void __init mpc836x_mds_setup_arch(void)
 
 		for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;)
 			par_io_of_config(np);
+
+		np = of_find_compatible_node(NULL, NULL, "fsl,qe_udc");
+		if (np) {
+			par_io_of_config(np);
+			qe_usb_clock_set(np);
+		}
 	}
 
 	if ((np = of_find_compatible_node(NULL, "network", "ucc_geth"))
@@ -127,9 +133,20 @@ static void __init mpc836x_mds_setup_arch(void)
 			iounmap(immap);
 		}
 
-		iounmap(bcsr_regs);
 		of_node_put(np);
 	}
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,qe_udc");
+	if (np != NULL) {
+		/* Set the TESCs run on RGMII mode */
+		bcsr_regs[8] &= ~0xf0;
+		/* Enable the USB Device PHY */
+		bcsr_regs[13] &= ~0x0f;
+		udelay(1000);
+		bcsr_regs[13] |= 0x05;
+		of_node_put(np);
+	}
+	iounmap(bcsr_regs);
 #endif				/* CONFIG_QUICC_ENGINE */
 }
 
diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h
index 2a7cbab..d025b47 100644
--- a/arch/powerpc/platforms/83xx/mpc83xx.h
+++ b/arch/powerpc/platforms/83xx/mpc83xx.h
@@ -63,5 +63,6 @@ extern void mpc83xx_restart(char *cmd);
 extern long mpc83xx_time_init(void);
 extern int mpc834x_usb_cfg(void);
 extern int mpc831x_usb_cfg(void);
+extern int qe_usb_clock_set(struct device_node *np);
 
 #endif				/* __MPC83XX_H__ */
diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c
index cc99c28..3d04ab5 100644
--- a/arch/powerpc/platforms/83xx/usb.c
+++ b/arch/powerpc/platforms/83xx/usb.c
@@ -18,6 +18,7 @@
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <sysdev/fsl_soc.h>
+#include <asm/qe.h>
 
 #include "mpc83xx.h"
 
@@ -240,3 +241,69 @@ int mpc837x_usb_cfg(void)
 	return ret;
 }
 #endif /* CONFIG_PPC_MPC837x */
+
+#ifdef CONFIG_QUICC_ENGINE
+/* QE USB_CLOCK configure functions */
+int qe_usb_clock_set(struct device_node *np)
+{
+	u32 tmpreg = 0;
+	struct qe_mux *qemux = NULL;
+	const int *clock;
+
+	qemux = &qe_immr->qmx;
+
+	clock = of_get_property(np, "usb-clock", NULL);
+	if (!clock)
+		return -EINVAL;
+
+	/* CLK21 -> USBCLK on MPC8360-PB*/
+	tmpreg = in_be32(&qemux->cmxgcr) & ~QE_CMXGCR_USBCS;
+	switch (*clock) {
+	case 21:
+		tmpreg |= 0x8;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 20, 2, 0, 1, 0);  /* PC20 for CLK21 */
+		break;
+	case 19:
+		tmpreg |= 0x7;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 18, 2, 0, 1, 0);  /* PC18 for CLK19 */
+		break;
+	case 17:
+		tmpreg |= 0x6;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 16, 2, 0, 1, 0);  /* PC16 for CLK17 */
+		break;
+	case 13:
+		tmpreg |= 0x5;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 12, 2, 0, 1, 0);  /* PC12 for CLK13 */
+		break;
+	case 9:
+		tmpreg |= 0x4;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 8, 2, 0, 1, 0);  /* PC8 for CLK9 */
+		break;
+	case 7:
+		tmpreg |= 0x3;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 6, 2, 0, 1, 0);  /* PC6 for CLK7 */
+		break;
+	case 5:
+		tmpreg |= 0x2;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 4, 2, 0, 1, 0);  /* PC4 for CLK5 */
+		break;
+	case 3:
+		tmpreg |= 0x1;
+		out_be32(&qemux->cmxgcr, tmpreg);
+		par_io_config_pin(2, 2, 2, 0, 1, 0);  /* PC2 for CLK3 */
+		break;
+	default:
+		printk(KERN_ERR "Unsupport usb-clock input pin\n");
+	}
+
+	return 0;
+}
+#endif
+
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH 6/6] powerpc: add 82xx platform level support to SEC engine
From: Li Yang @ 2008-08-06  7:04 UTC (permalink / raw)
  To: linuxppc-dev, galak; +Cc: Li Yang
In-Reply-To: <1218006285-27138-5-git-send-email-leoli@freescale.com>

Initialize base and size of SEC memory region and bus priority for SEC.

Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/include/asm/immap_cpm2.h     |    7 +++++--
 arch/powerpc/platforms/82xx/mpc8272_ads.c |    1 +
 arch/powerpc/platforms/82xx/pq2.c         |   19 +++++++++++++++++++
 arch/powerpc/platforms/82xx/pq2.h         |    1 +
 4 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/include/asm/immap_cpm2.h b/arch/powerpc/include/asm/immap_cpm2.h
index d4f069b..71de9d0 100644
--- a/arch/powerpc/include/asm/immap_cpm2.h
+++ b/arch/powerpc/include/asm/immap_cpm2.h
@@ -115,10 +115,13 @@ typedef struct	mem_ctlr {
 	u32	memc_immr;
 	u32	memc_pcibr0;
 	u32	memc_pcibr1;
-	u8	res10[16];
+	u32	secbr;
+	u8	res10[4];
+	u32	secmr;
+	u8	res11[4];
 	u32	memc_pcimsk0;
 	u32	memc_pcimsk1;
-	u8	res11[52];
+	u8	res12[52];
 } memctl_cpm2_t;
 
 /* System Integration Timers.
diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c
index 69781e6..a98d97c 100644
--- a/arch/powerpc/platforms/82xx/mpc8272_ads.c
+++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c
@@ -179,6 +179,7 @@ static void __init mpc8272_ads_setup_arch(void)
 
 	init_ioports();
 	pq2_init_pci();
+	pq2_init_sec();
 
 	if (ppc_md.progress)
 		ppc_md.progress("mpc8272_ads_setup_arch(), finish", 0);
diff --git a/arch/powerpc/platforms/82xx/pq2.c b/arch/powerpc/platforms/82xx/pq2.c
index 1b75902..bacb136 100644
--- a/arch/powerpc/platforms/82xx/pq2.c
+++ b/arch/powerpc/platforms/82xx/pq2.c
@@ -22,6 +22,8 @@
 #include <platforms/82xx/pq2.h>
 
 #define RMR_CSRE 0x00000001
+#define PQ2_SECMR_128K 0xfffe0000
+#define PQ2_ALRH_SEC 0x30126745
 
 void pq2_restart(char *cmd)
 {
@@ -35,6 +37,23 @@ void pq2_restart(char *cmd)
 	panic("Restart failed\n");
 }
 
+void __init pq2_init_sec(void)
+{
+	struct device_node *np = NULL;
+	struct resource res;
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,talitos");
+	if (!np)
+		return;
+
+	of_address_to_resource(np, 0, &res);
+	printk(KERN_INFO "Setting SECBR and SECMR\n");
+	out_be32(&cpm2_immr->im_memctl.secbr, (u32)res.start);
+	out_be32(&cpm2_immr->im_memctl.secmr, PQ2_SECMR_128K);
+	out_be32(&cpm2_immr->im_siu_conf.siu_82xx.sc_ppc_alrh, PQ2_ALRH_SEC);
+	of_node_put(np);
+}
+
 #ifdef CONFIG_PCI
 static int pq2_pci_exclude_device(struct pci_controller *hose,
                                   u_char bus, u8 devfn)
diff --git a/arch/powerpc/platforms/82xx/pq2.h b/arch/powerpc/platforms/82xx/pq2.h
index a41f84a..98d3c3c 100644
--- a/arch/powerpc/platforms/82xx/pq2.h
+++ b/arch/powerpc/platforms/82xx/pq2.h
@@ -2,6 +2,7 @@
 #define _PQ2_H
 
 void pq2_restart(char *cmd);
+void pq2_init_sec(void);
 
 #ifdef CONFIG_PCI
 int pq2ads_pci_init_irq(void);
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH] net/fs_enet: remove redundant messages for performance
From: Li Yang @ 2008-08-06  7:08 UTC (permalink / raw)
  To: jeff, netdev; +Cc: linuxppc-dev, Li Yang

Currently when we do a packet flood to the Ethernet port, the console
reports error every time when a packet is dropped.  This is too
redundant and cost performance.  Remove message for this type of event.

Signed-off-by: Li Yang <leoli@freescale.com>
---
 drivers/net/fs_enet/mac-fcc.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c
index 0a97fc2..1c7ef81 100644
--- a/drivers/net/fs_enet/mac-fcc.c
+++ b/drivers/net/fs_enet/mac-fcc.c
@@ -126,7 +126,7 @@ out:
 #define FCC_NAPI_RX_EVENT_MSK	(FCC_ENET_RXF | FCC_ENET_RXB)
 #define FCC_RX_EVENT		(FCC_ENET_RXF)
 #define FCC_TX_EVENT		(FCC_ENET_TXB)
-#define FCC_ERR_EVENT_MSK	(FCC_ENET_TXE | FCC_ENET_BSY)
+#define FCC_ERR_EVENT_MSK	(FCC_ENET_TXE)
 
 static int setup_data(struct net_device *dev)
 {
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
From: Li Yang @ 2008-08-06  7:16 UTC (permalink / raw)
  To: gregkh, david-b, linux-usb; +Cc: linuxppc-dev, Li Yang, Xie Xiaobo

Some of Freescale SoC chips have a QE or CPM co-processor which
supports full speed USB.  The driver adds device mode support
of both QE and CPM USB controller to Linux USB gadget.  The
driver is tested with MPC8360 and MPC8272, and should work with
other models having QE/CPM given minor tweaks.

Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
 drivers/usb/gadget/Kconfig        |   19 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/fsl_qe_udc.c   | 2729 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/fsl_qe_udc.h   |  458 +++++++
 drivers/usb/gadget/gadget_chips.h |    9 +
 5 files changed, 3216 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/fsl_qe_udc.c
 create mode 100644 drivers/usb/gadget/fsl_qe_udc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c6a8c6b..fba8305 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -150,6 +150,25 @@ config USB_FSL_USB2
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_FSL_QE
+    boolean "Freescale QE/CPM USB Device Controller"
+    help
+       Some of Freescale PowerPC processors have a Full Speed
+       QE/CPM2 USB controller, which support device mode with 4
+       programmable endpoints. This driver supports the
+       controller in the MPC8360 and MPC8272, and should work with
+       controllers having QE or CPM2, given minor tweaks.
+
+       Say "y" to link the driver statically, or "m" to build a
+       dynamically linked module called "fsl_qe_udc" and force all
+       gadget drivers to also be dynamically linked.
+
+config USB_FSL_QE
+    tristate
+    depends on USB_GADGET_FSL_QE
+    default USB_GADGET
+    select USB_GADGET_SELECTED
+
 config USB_GADGET_NET2280
 	boolean "NetChip 228x"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index fcb5cb9..4871554 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 obj-$(CONFIG_USB_ATMEL_USBA)	+= atmel_usba_udc.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
+obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
new file mode 100644
index 0000000..52790f7
--- /dev/null
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -0,0 +1,2729 @@
+/*
+ * driver/usb/gadget/fsl_qe_udc.c
+ *
+ * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Xie Xiaobo <X.Xie@freescale.com>
+ * Li Yang <leoli@freescale.com>
+ * Based on bareboard code from Shlomi Gridish.
+ *
+ * Description:
+ * Freescle QE/CPM USB Pheripheral Controller Driver
+ * The controller can be found on MPC8360, MPC8272, and etc.
+ * MPC8360 Rev 1.1 may need QE mircocode update
+ *
+ * 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.
+ */
+
+#undef USB_TRACE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/moduleparam.h>
+
+#include <linux/of_platform.h>
+
+#include <asm/qe.h>
+#include <asm/dma.h>
+#include <asm/reg.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include "fsl_qe_udc.h"
+
+#ifdef CONFIG_CPM2
+extern int cpm_command(u32 command, u8 opcode);
+extern unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
+extern int cpm_muram_free(unsigned long offset);
+
+#define qe_muram_alloc cpm_muram_alloc
+#define qe_muram_free cpm_muram_free
+#endif
+
+#define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
+#define DRIVER_AUTHOR   "Xie XiaoBo"
+#define DRIVER_VERSION  "1.0"
+
+#define DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl_qe_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/*ep name is important in gadget, it should obey the convention of ep_match()*/
+static const char *const ep_name[] = {
+	"ep0-control", /* everyone has ep0 */
+	/* 3 configurable endpoints */
+	"ep1",
+	"ep2",
+	"ep3",
+};
+
+static struct usb_endpoint_descriptor
+qe_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
+};
+
+/* it is initialized in probe()  */
+static struct qe_udc *udc_controller;
+
+/********************************************************************
+ *      Internal Used Function Start
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ *--------------------------------------------------------------*/
+static void done(struct qe_ep *ep, struct qe_req *req, int status)
+{
+	struct qe_udc *udc = NULL;
+	unsigned char stopped = ep->stopped;
+	udc = (struct qe_udc *) ep->udc;
+
+	/* the req->queue pointer is used by ep_queue() func, in which
+	* the request will be added into a udc_ep->queue 'd tail
+	* so here the req will be dropped from the ep->queue
+	*/
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (req->mapped) {
+		dma_unmap_single(ep->udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else
+		dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+
+	if (status && (status != -ESHUTDOWN))
+		VDBG("complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&ep->udc->lock);
+
+	/* this complete() should a func implemented by gadget layer,
+	* eg fsg->bulk_in_complete() */
+	if (req->req.complete)
+		req->req.complete(&ep->ep, &req->req);
+
+	spin_lock(&ep->udc->lock);
+
+	ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ *--------------------------------------------------------------*/
+static void nuke(struct qe_ep *ep, int status)
+{
+	/* Whether this eq has request linked */
+	while (!list_empty(&ep->queue)) {
+		struct qe_req *req = NULL;
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		done(ep, req, status);
+	}
+}
+
+/*---------------------------------------------------------------------------*
+ * USB and Endpoint manipulate process, include parameter and register       *
+ *---------------------------------------------------------------------------*/
+/* @value: 1--set stall 0--clean stall */
+static int qe_eprx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_RHS_MASK;
+	if (value == 1)
+		tem_usep |= USB_RHS_STALL;
+	else if (ep->dir == USB_DIR_IN)
+		tem_usep |= USB_RHS_IGNORE_OUT;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+	return 0;
+}
+
+static int qe_eptx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_THS_MASK;
+	if (value == 1)
+		tem_usep |= USB_THS_STALL;
+	else if (ep->dir == USB_DIR_OUT)
+		tem_usep |= USB_THS_IGNORE_IN;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+
+	return 0;
+}
+
+static int qe_ep0_stall(struct qe_udc *udc)
+{
+	qe_eptx_stall_change(&udc->eps[0], 1);
+	qe_eprx_stall_change(&udc->eps[0], 1);
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+	return 0;
+}
+
+static int qe_eprx_nack(struct qe_ep *ep)
+{
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_IDLE) {
+		/* Set the ep's nack */
+		clrsetbits_be16(&udc->usb_regs->usb_usep[epnum],
+				USB_RHS_MASK, USB_RHS_NACK);
+
+		/* Mask Rx and Busy interrupts */
+		clrbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_NACK;
+	}
+	return 0;
+}
+
+static int qe_eprx_normal(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_NACK) {
+		clrsetbits_be16(&udc->usb_regs->usb_usep[ep->epnum],
+				USB_RTHS_MASK, USB_THS_IGNORE_IN);
+
+		/* Unmask RX interrupts */
+		out_be16(&udc->usb_regs->usb_usber,
+				USB_E_BSY_MASK|USB_E_RXB_MASK);
+		setbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_IDLE;
+		ep->has_data = 0;
+	}
+
+	return 0;
+}
+
+static int qe_ep_cmd_stoptx(struct qe_ep *ep)
+{
+	u8 ep_num;
+#ifdef CONFIG_CPM2
+	u32 command;
+	u8 opcode;
+
+	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
+	command = CPM_USB_STOP_TX | (u32)ep_num;
+	opcode = CPM_USB_STOP_TX_OPCODE;
+	cpm_command(command, opcode);
+#else
+	ep_num = ep->epnum;
+	qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
+#endif
+	return 0;
+}
+
+static int qe_ep_cmd_restarttx(struct qe_ep *ep)
+{
+	u8 ep_num;
+#ifdef CONFIG_CPM2
+	u32 command;
+	u8 opcode;
+
+	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
+	command = CPM_USB_RESTART_TX | (u32)ep_num;
+	opcode = CPM_USB_RESTART_TX_OPCODE;
+	cpm_command(command, opcode);
+#else
+	ep_num = ep->epnum;
+	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
+#endif
+	return 0;
+}
+
+static int qe_ep_flushtxfifo(struct qe_ep *ep)
+{
+	struct qe_ep *tmp_ep;
+	struct qe_udc *udc = ep->udc;
+	int i;
+
+	tmp_ep = ep;
+	i = (int)tmp_ep->epnum;
+
+	qe_ep_cmd_stoptx(tmp_ep);
+	out_8(&udc->usb_regs->usb_uscom,
+		USB_CMD_FLUSH_FIFO | (USB_CMD_EP_MASK & (tmp_ep->epnum)));
+	udc->ep_param[i]->tbptr = udc->ep_param[i]->tbase;
+	udc->ep_param[i]->tstate = 0;
+	udc->ep_param[i]->tbcnt = 0;
+
+	tmp_ep->c_txbd = tmp_ep->txbase;
+	tmp_ep->n_txbd = tmp_ep->txbase;
+	qe_ep_cmd_restarttx(tmp_ep);
+	return 0;
+}
+
+static int qe_ep_filltxfifo(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	out_8(&udc->usb_regs->usb_uscom,
+			USB_CMD_STR_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
+	return 0;
+}
+
+static int qe_epbds_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u32 bdring_len;
+	u8 *bd;
+	int i;
+
+	ep = &(udc->eps[pipe_num]);
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	bd = (u8 *)ep->rxbase;
+	for (i = 0; i < bdring_len; i++) {
+		BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I));
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I|R_W));
+
+	bd = (u8 *)(ep->txbase);
+	for (i = 0; i < USB_BDRING_LEN_TX; i++) {
+		BD_BUFFER_CLEAR(bd);
+		BD_STATUS_AND_LENGTH_SET(bd, 0);
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u16 tmpusep;
+
+	ep = &(udc->eps[pipe_num]);
+	tmpusep = in_be16(&udc->usb_regs->usb_usep[pipe_num]);
+	tmpusep &= ~USB_RTHS_MASK;
+
+	switch (ep->dir) {
+	case USB_DIR_BOTH:
+		qe_ep_flushtxfifo(ep);
+		break;
+	case USB_DIR_OUT:
+		tmpusep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		qe_ep_flushtxfifo(ep);
+		tmpusep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], tmpusep);
+
+	qe_epbds_reset(udc, pipe_num);
+
+	return 0;
+}
+
+static int qe_ep_toggledata01(struct qe_ep *ep)
+{
+	struct qe_ep *tmpep = ep;
+
+	tmpep->data01 ^= 0x1;
+	return 0;
+}
+
+static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+	unsigned long tmp_addr = 0;
+	struct usb_ep_para __iomem *epparam;
+	int i;
+	u8 *bd;
+	int bdring_len;
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	epparam = udc->ep_param[pipe_num];
+	/* alloc multi-ram for BD rings and set the ep parameters */
+	tmp_addr = qe_muram_alloc(QE_SIZEOF_BD * (bdring_len +
+				USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
+	out_be16(&epparam->rbase, (u16)tmp_addr);
+	out_be16(&epparam->tbase, (u16)(tmp_addr +
+				(QE_SIZEOF_BD * bdring_len)));
+
+	out_be16(&epparam->rbptr, in_be16(&epparam->rbase));
+	out_be16(&epparam->tbptr, in_be16(&epparam->tbase));
+
+	ep->rxbase = (struct qe_bd *)muram_addr(tmp_addr);
+	ep->txbase = (struct qe_bd *)muram_addr(tmp_addr +
+						(QE_SIZEOF_BD * bdring_len));
+	ep->n_rxbd = ep->rxbase;
+	ep->e_rxbd = ep->rxbase;
+	ep->n_txbd = ep->txbase;
+	ep->c_txbd = ep->txbase;
+	ep->data01 = 0; /* data0 */
+
+	/* Init TX and RX bds */
+	bd = (u8 *)(ep->rxbase);
+	for (i = 0; i < bdring_len; i++) {
+		BD_BUFFER_CLEAR(bd);
+		BD_STATUS_AND_LENGTH_SET(bd, 0);
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, R_W);
+
+	bd = (u8 *)(ep->txbase);
+	for (i = 0; i < USB_BDRING_LEN_TX; i++) {
+		BD_BUFFER_CLEAR(bd);
+		BD_STATUS_AND_LENGTH_SET(bd, 0);
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_rxbd_update(struct qe_ep *ep)
+{
+	unsigned int size;
+	int i;
+	unsigned int tmp;
+	struct qe_bd *bd;
+	unsigned int bdring_len;
+
+	if (ep->rxbase == NULL)
+		return -EINVAL;
+
+	bd = ep->rxbase;
+
+	ep->rxframe = kmalloc(sizeof(struct qe_frame), GFP_KERNEL);
+	if (ep->rxframe == NULL) {
+		dev_err(ep->udc->dev, "malloc rxframe failed\n");
+		return -ENOMEM;
+	}
+
+	qe_frame_init(ep->rxframe);
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
+	ep->rxbuffer = kzalloc(size, GFP_KERNEL);
+	if (ep->rxbuffer == NULL) {
+		dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
+				size);
+		return -ENOMEM;
+	}
+
+	ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer);
+	if (ep->rxbuf_d == DMA_ADDR_INVALID) {
+		ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent,
+					ep->rxbuffer,
+					size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 1;
+	} else {
+		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 0;
+	}
+
+	size = ep->ep.maxpacket + USB_CRC_SIZE + 2;
+	tmp = ep->rxbuf_d;
+	tmp = (u32)(((tmp>>2)<<2) + 4);
+
+	for (i = 0; i < bdring_len; i++) {
+		BD_BUFFER_SET(bd, tmp);
+		BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I));
+		tmp = tmp + size;
+		bd++;
+	}
+	bd--;
+	BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I|R_W));
+
+	return 0;
+}
+
+static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+	struct usb_ep_para __iomem *epparam;
+	u16 usep, logepnum;
+	u16 tmp;
+	u8 rtfcr = 0;
+
+	epparam = udc->ep_param[pipe_num];
+
+	usep = 0;
+	logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	usep |= (logepnum << USB_EPNUM_SHIFT);
+
+	switch (ep->desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_BULK:
+		usep |= USB_TRANS_BULK;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		usep |=  USB_TRANS_ISO;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		usep |= USB_TRANS_INT;
+		break;
+	default:
+		usep |= USB_TRANS_CTR;
+		break;
+	}
+
+	switch (ep->dir) {
+	case USB_DIR_OUT:
+		usep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		usep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], usep);
+
+	rtfcr = 0x30;
+	out_8(&epparam->rbmr, rtfcr);
+	out_8(&epparam->tbmr, rtfcr);
+
+	tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE);
+	/* MRBLR must be divisble by 4 */
+	tmp = (u16)(((tmp >> 2) << 2) + 4);
+	out_be16(&epparam->mrblr, tmp);
+
+	return 0;
+}
+
+static int qe_ep_init(struct qe_udc *udc,
+		      unsigned char pipe_num,
+		      const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+	unsigned long flags = 0;
+	int reval = 0;
+	u16 max = 0;
+
+	max = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* check the max package size validate for this endpoint */
+	/* Refer to USB2.0 spec table 9-13,
+	*/
+	if (pipe_num != 0) {
+		switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_BULK:
+			if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int"))
+			goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			if ((max == 128) || (max == 256) || (max == 512))
+				break;
+			default:
+				switch (max) {
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+				case USB_SPEED_LOW:
+					goto en_done;
+				}
+			}
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			if (strstr(ep->ep.name, "-iso"))	/* bulk is ok */
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 64)
+					break;
+			default:
+				if (max <= 8)
+					break;
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			if (strstr(ep->ep.name, "-bulk")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 1023)
+					break;
+			default:
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_CONTROL:
+			if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			case USB_SPEED_FULL:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+					goto en_done;
+				}
+			case USB_SPEED_LOW:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					break;
+				default:
+					goto en_done;
+				}
+			default:
+				goto en_done;
+			}
+			break;
+
+		default:
+			goto en_done;
+		}
+	} /* if ep0*/
+
+	/* here initialize variable of ep */
+	ep->ep.maxpacket = max;
+	ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	ep->desc = desc;
+	ep->stopped = 0;
+	ep->init = 1;
+
+	if (pipe_num == 0) {
+		ep->dir = USB_DIR_BOTH;
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_SETUP;
+	} else	{
+		switch ((desc->bEndpointAddress)&USB_ENDPOINT_DIR_MASK) {
+		case USB_DIR_OUT:
+			ep->dir = USB_DIR_OUT;
+			break;
+		case USB_DIR_IN:
+			ep->dir = USB_DIR_IN;
+		default:
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* hardware special operation */
+	qe_ep_bd_init((void *)udc, pipe_num);
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_OUT)) {
+		reval = qe_ep_rxbd_update(ep);
+		if (reval)
+			goto en_done;
+	}
+
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
+		ep->txframe = kmalloc(sizeof(struct qe_frame), GFP_KERNEL);
+		if (ep->txframe == NULL) {
+			dev_err(udc->dev, "malloc txframe failed\n");
+			goto en_done;
+		}
+		qe_frame_init(ep->txframe);
+	}
+	qe_ep_register_init((void *)udc, pipe_num);
+
+	/* Now HW will be NAKing transfers to that EP,
+	 * until a buffer is queued to it. */
+
+	/* should have stop the lock */
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+
+en_done:
+	VDBG("init %s failed!", ep->ep.name);
+	return -ENODEV;
+}
+
+static int qe_usb_enable(void)
+{
+	setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
+
+	return 0;
+}
+
+static int qe_usb_disable(void)
+{
+	clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*
+ *		USB and EP basic manipulate function end		      *
+ *----------------------------------------------------------------------------*/
+
+
+/******************************************************************************
+		UDC transmit and receive process
+ ******************************************************************************/
+static void recycle_one_rxbd(struct qe_ep *ep)
+{
+	u32 bdstatus;
+
+	bdstatus = (u32)BD_STATUS_AND_LENGTH(ep->e_rxbd);
+	bdstatus = R_I | R_E | (bdstatus & R_W);
+	BD_STATUS_AND_LENGTH_SET(ep->e_rxbd, bdstatus);
+
+	if (bdstatus & R_W)
+		ep->e_rxbd = ep->rxbase;
+	else
+		ep->e_rxbd++;
+}
+
+static void recycle_rxbds(struct qe_ep *ep, unsigned char stopatnext)
+{
+	u32 bdstatus;
+	struct qe_bd *bd, *nextbd;
+	unsigned char stop = 0;
+
+	nextbd = ep->n_rxbd;
+	bd = ep->e_rxbd;
+	bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+
+	while (!(bdstatus & R_E) && !(bdstatus&BD_LENGTH_MASK) && !stop) {
+		bdstatus = R_E | R_I | (bdstatus & R_W);
+		BD_STATUS_AND_LENGTH_SET(bd, bdstatus);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+		if (stopatnext && (bd == nextbd))
+			stop = 1;
+	}
+
+	ep->e_rxbd = (struct qe_bd *)bd;
+}
+
+static void ep_recycle_rxbds(struct qe_ep *ep)
+{
+	struct qe_bd *bd = NULL;
+	u32 bdstatus;
+	u8 epnum;
+	struct qe_udc *udc = NULL;
+
+	udc = ep->udc;
+	epnum = ep->epnum;
+
+	bd = ep->n_rxbd;
+	bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+	if (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK)) {
+		bd = (struct qe_bd *)(ep->rxbase +
+				((in_be16(&udc->ep_param[epnum]->rbptr) -
+				  in_be16(&udc->ep_param[epnum]->rbase))
+				 >> 3));
+		bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		ep->e_rxbd = (struct qe_bd *)bd;
+		VDBG("The QE completed full bd cycle");
+		recycle_rxbds(ep, 0);
+		ep->e_rxbd = ep->n_rxbd;
+	} else
+		recycle_rxbds(ep, 1);
+
+	if (in_be16(&udc->usb_regs->usb_usber) & USB_E_BSY_MASK)
+		out_be16(&udc->usb_regs->usb_usber, USB_E_BSY_MASK);
+
+	if (ep->has_data <= 0 && (!list_empty(&ep->queue)))
+		qe_eprx_normal(ep);
+
+	ep->localnack = 0;
+}
+
+static void setup_received_handle(struct qe_udc *udc,
+					struct usb_ctrlrequest *setup);
+static int qe_ep_rxframe_handle(struct qe_ep *ep);
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);
+/* when BD PID is setup, handle the packet */
+static int ep0_setup_handle(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &(udc->eps[0]);
+	struct qe_frame *pframe = NULL;
+	unsigned int fsize;
+	u8 *cp;
+
+	pframe = (struct qe_frame *)ep->rxframe;
+	if ((frame_get_info(pframe) & PID_SETUP)
+		&& (udc->ep0_state == WAIT_FOR_SETUP)) {
+		fsize = (int)frame_get_length(pframe);
+		if (unlikely(fsize != 8))
+			return -EINVAL;
+		cp = (u8 *)&(udc->local_setup_buff);
+		memcpy(cp, pframe->data, fsize);
+		ep->data01 = 1;
+
+		/* handle the usb command base on the usb_ctrlrequest */
+		setup_received_handle(udc, &udc->local_setup_buff);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int qe_ep0_rx(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &(udc->eps[0]);
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u32 bdstatus, length;
+	u32 vaddr;
+
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		VDBG("error:This is a transmit ep!");
+		return -EINVAL;
+	}
+
+	bd = (struct qe_bd *)(ep->n_rxbd);
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus&R_E) && length) {
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+			&& !(bdstatus & R_ERROR)) {
+			if (length == USB_CRC_SIZE) {
+				udc->ep0_state = WAIT_FOR_SETUP;
+				VDBG("receive a ZLP in status phase");
+			} else {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+
+				if ((bdstatus & R_PID) == R_PID_SETUP)
+					ep0_setup_handle(udc);
+				else
+					qe_ep_rxframe_handle(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address */
+		recycle_one_rxbd(ep);
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+	}
+
+	ep->n_rxbd = (struct qe_bd *)bd;
+
+	return 0;
+}
+
+static int qe_ep_rxframe_handle(struct qe_ep *ep)
+{
+	struct qe_frame *pframe = NULL;
+	u8 framepid = 0;
+	unsigned int fsize;
+	u8 *cp = NULL;
+	struct qe_req *req = NULL;
+
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	if (frame_get_info(pframe) & PID_DATA1)
+		framepid = 0x1;
+
+	if (framepid != ep->data01) {
+		dev_err(ep->udc->dev, "the data01 error!\n");
+		return -EIO;
+	}
+
+	fsize = frame_get_length(pframe);
+	if (list_empty(&ep->queue)) {
+		dev_err(ep->udc->dev, "the %s have no requeue!\n", ep->name);
+	} else {
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		cp = (u8 *)(req->req.buf) + req->req.actual;
+		if (cp) {
+			memcpy(cp, pframe->data, fsize);
+			req->req.actual += fsize;
+			if ((fsize < ep->ep.maxpacket) ||
+					(req->req.actual >= req->req.length)) {
+				if (ep->epnum == 0)
+					ep0_req_complete(ep->udc, req);
+				else
+					done(ep, req, 0);
+				if (list_empty(&ep->queue) && ep->epnum != 0)
+					qe_eprx_nack(ep);
+			}
+		}
+	}
+
+	qe_ep_toggledata01(ep);
+
+	return 0;
+}
+
+static void ep_rx_tasklet(unsigned long data)
+{
+	struct qe_udc *udc = (struct qe_udc *)data;
+	struct qe_ep *ep = NULL;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u32 bdstatus, length;
+	u32 vaddr, i;
+
+	for (i = 1; i < USB_MAX_ENDPOINTS; i++) {
+		ep = (struct qe_ep *)&(udc->eps[i]);
+
+		if (ep->dir == USB_DIR_IN || ep->enable_tasklet == 0) {
+			VDBG("This is a transmit ep or disable tasklet!");
+			continue;
+		}
+
+		pframe = (struct qe_frame *)ep->rxframe;
+		bd = (struct qe_bd *)(ep->n_rxbd);
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+		while (!(bdstatus&R_E) && length) {
+			if (list_empty(&ep->queue)) {
+				qe_eprx_nack(ep);
+				VDBG("The rxep have noreq %d", ep->has_data);
+				break;
+			}
+
+			if ((bdstatus & R_F) && (bdstatus & R_L)
+				&& !(bdstatus & R_ERROR)) {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+				/* handle the rx frame */
+				qe_ep_rxframe_handle(ep);
+			} else {
+				dev_err(udc->dev,
+					"error in received frame\n");
+			}
+			/* note: don't clear the rxbd's buffer address */
+			/*clear the length */
+			BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&BD_STATUS_MASK));
+			ep->has_data--;
+			if (!(ep->localnack))
+				recycle_one_rxbd(ep);
+
+			/* Get next BD */
+			if (bdstatus & R_W)
+				bd = ep->rxbase;
+			else
+				bd++;
+
+			bdstatus = BD_STATUS_AND_LENGTH(bd);
+			length = bdstatus & BD_LENGTH_MASK;
+		}
+
+		ep->n_rxbd = (struct qe_bd *)bd;
+
+		if (ep->localnack)
+			ep_recycle_rxbds(ep);
+
+		ep->enable_tasklet = 0;
+	} /* for i=1 */
+}
+
+static int qe_ep_rx(struct qe_ep *ep)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u16 swoffs, ucoffs, emptybds;
+	u32 bdstatus, length;
+
+	udc = (struct qe_udc *)ep->udc;
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		VDBG("error:This is a transmit ep!");
+		return -EINVAL;
+	}
+
+	bd = (struct qe_bd *)(ep->n_rxbd);
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	swoffs = (u16)(((u8 *)bd - (u8 *)ep->rxbase) >> 3);
+	ucoffs = (u16)((in_be16(&udc->ep_param[ep->epnum]->rbptr) -
+			in_be16(&udc->ep_param[ep->epnum]->rbase)) >> 3);
+	if (swoffs < ucoffs)
+		emptybds = USB_BDRING_LEN_RX - ucoffs + swoffs;
+	else
+		emptybds = swoffs - ucoffs;
+
+	if (emptybds < MIN_EMPTY_BDS) {
+		qe_eprx_nack(ep);
+		ep->localnack = 1;
+		VDBG("The rx ep have a nack!%d", emptybds);
+	}
+	ep->has_data = USB_BDRING_LEN_RX - emptybds;
+
+	if (list_empty(&ep->queue)) {
+		qe_eprx_nack(ep);
+		VDBG("The rxep have noreq %d", ep->has_data);
+		return 0;
+	}
+
+	tasklet_schedule(&udc->rx_tasklet);
+	ep->enable_tasklet = 1;
+
+	return 0;
+}
+
+/* send data from a frame, no matter what tx_req */
+static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_udc *udc = NULL;
+
+	struct qe_bd *bd = NULL;
+	u16 saveusbmr;
+	u32 bdstatus, pidmask;
+	u32 paddr;
+
+	udc = (struct qe_udc *)ep->udc;
+
+	if (ep->dir == USB_DIR_OUT) {
+		VDBG("error:this is a receive ep!");
+		return -EINVAL;
+	}
+
+	/* Disable the Tx interrupt */
+	saveusbmr = in_be16(&udc->usb_regs->usb_usbmr);
+	out_be16(&udc->usb_regs->usb_usbmr,
+			saveusbmr & ~(USB_E_TXB_MASK | USB_E_TXE_MASK));
+
+	bd = ep->n_txbd;
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+
+	if (!(bdstatus & (T_R|BD_LENGTH_MASK))) {
+		if (frame_get_length(frame) == 0) {
+			frame_set_data(frame, udc->nullbuf);
+			frame_set_length(frame, 2);
+			frame->info |= (ZLP|NO_CRC);
+			VDBG("the frame size = 0");
+		}
+		paddr = virt_to_phys((void *)frame->data);
+		BD_BUFFER_SET(bd, paddr);
+		bdstatus = (bdstatus&T_W);
+		if (!(frame_get_info(frame) & NO_CRC))
+			bdstatus |= T_R | T_I | T_L | T_TC
+					| frame_get_length(frame);
+		else
+			bdstatus |= T_R | T_I | T_L | frame_get_length(frame);
+
+		/* if the packet is a ZLP in status phase */
+		if ((ep->epnum == 0) && (udc->ep0_state == DATA_STATE_NEED_ZLP))
+			ep->data01 = 0x1;
+
+		if (ep->data01) {
+			pidmask = T_PID_DATA1;
+			frame->info |= PID_DATA1;
+		} else {
+			pidmask = T_PID_DATA0;
+			frame->info |= PID_DATA0;
+		}
+		bdstatus |= T_CNF;
+		bdstatus |= pidmask;
+		BD_STATUS_AND_LENGTH_SET(bd, bdstatus);
+		qe_ep_filltxfifo(ep);
+
+		/* enable the TX interrupt */
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+
+		qe_ep_toggledata01(ep);
+		if (bdstatus & T_W)
+			ep->n_txbd = ep->txbase;
+		else
+			ep->n_txbd++;
+
+		return 0;
+	} else {
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+		VDBG("The tx bd is not ready!");
+		return -EBUSY;
+	}
+}
+
+/* when an bd was transmitted, the function can *
+ * handle the tx_req, not include ep0           */
+static int txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	struct qe_req *tx_req = NULL;
+
+	tx_req = ep->tx_req;
+	if (tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			done(ep, ep->tx_req, 0);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	/* we should gain a new tx_req fot this endpoint */
+	if (ep->tx_req == NULL) {
+		if (!list_empty(&ep->queue)) {
+			ep->tx_req = list_entry(ep->queue.next,	struct qe_req,
+							queue);
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	return 0;
+}
+
+/* give a frame and a tx_req,send some data */
+static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame)
+{
+	unsigned int size;
+	u8 *buf;
+
+	qe_frame_clean(frame);
+	size = min_t(u32, (ep->tx_req->req.length - ep->sent),
+				ep->ep.maxpacket);
+	buf = (u8 *)ep->tx_req->req.buf + ep->sent;
+	if (buf && size) {
+		ep->last = size;
+		frame_set_data(frame, buf);
+		frame_set_length(frame, size);
+		frame_set_status(frame, FRAME_OK);
+		frame_set_info(frame, 0);
+		return qe_ep_tx(ep, frame);
+	}
+	return -EIO;
+}
+
+/* give a frame struct,send a ZLP */
+static int sendnulldata(struct qe_ep *ep, struct qe_frame *frame, uint infor)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (frame == NULL)
+		return -ENODEV;
+
+	qe_frame_clean(frame);
+	frame_set_data(frame, (u8 *)udc->nullbuf);
+	frame_set_length(frame, 2);
+	frame_set_status(frame, FRAME_OK);
+	frame_set_info(frame, (ZLP|NO_CRC|infor));
+
+	return qe_ep_tx(ep, frame);
+}
+
+static int frame_create_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_req *req;
+	int reval;
+	if (ep->tx_req == NULL)
+		return -ENODEV;
+
+	req = ep->tx_req;
+
+	if ((req->req.length - ep->sent) > 0)
+		reval = qe_usb_senddata(ep, frame);
+	else
+		reval = sendnulldata(ep, frame, 0);
+
+	return reval;
+}
+
+/* if direction is DIR_IN, the status is Device->Host
+ * if direction is DIR_OUT, the status transaction is Device<-Host
+ * in status phase, udc create a request and gain status */
+static int ep0_prime_status(struct qe_udc *udc, int direction)
+{
+
+	struct qe_ep *ep = &udc->eps[0];
+
+	if (direction == USB_DIR_IN) {
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+		udc->ep0_dir = USB_DIR_IN;
+		sendnulldata(ep, ep->txframe, SETUP_STATUS|NO_REQ);
+	} else {
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+	}
+
+	return 0;
+}
+
+/* a request complete in ep0, whether gadget request or udc request */
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req)
+{
+	struct qe_ep *ep = &(udc->eps[0]);
+	/* because usb and ep's status already been set in ch9setaddress() */
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		done(ep, req, 0);
+		/* receive status phase */
+		if (ep0_prime_status(udc, USB_DIR_OUT))
+			qe_ep0_stall(udc);
+		break;
+
+	case DATA_STATE_NEED_ZLP:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case DATA_STATE_RECV:
+		done(ep, req, 0);
+		/* send status phase */
+		if (ep0_prime_status(udc, USB_DIR_IN))
+			qe_ep0_stall(udc);
+		break;
+
+	case WAIT_FOR_OUT_STATUS:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case WAIT_FOR_SETUP:
+		VDBG("Unexpected interrupt");
+		break;
+
+	default:
+		qe_ep0_stall(udc);
+		break;
+	}
+}
+
+static int ep0_txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	struct qe_req *tx_req = NULL;
+	struct qe_frame *frame = ep->txframe;
+
+	if ((frame_get_info(frame)&(ZLP|NO_REQ)) == (ZLP|NO_REQ)) {
+		if (!restart)
+			ep->udc->ep0_state = WAIT_FOR_SETUP;
+		else
+			sendnulldata(ep, ep->txframe, SETUP_STATUS|NO_REQ);
+		return 0;
+	}
+
+	tx_req = ep->tx_req;
+	if (tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			ep0_req_complete(ep->udc, ep->tx_req);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	} else {
+		VDBG("the ep0_controller have no req");
+	}
+
+	return 0;
+}
+
+static int ep0_txframe_handle(struct qe_ep *ep)
+{
+	/* if have error, transmit again */
+	if (frame_get_status(ep->txframe)&FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		VDBG("The EP0 transmit data have error!");
+		if (frame_get_info(ep->txframe)&PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		ep0_txcomplete(ep, 1);
+	} else
+		ep0_txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe);
+	return 0;
+}
+
+static int qe_ep0_txconf(struct qe_ep *ep)
+{
+	struct qe_bd *bd = NULL;
+	struct qe_frame *pframe = NULL;
+	u32 bdstatus;
+
+	bd = ep->c_txbd;
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+
+		/* clear and recycle the BD */
+		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&T_W));
+		BD_BUFFER_CLEAR(bd);
+		if (bdstatus&T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		if (ep->c_txbd == ep->n_txbd) {
+			if (bdstatus & DEVICE_T_ERROR) {
+				frame_set_status(pframe, FRAME_ERROR);
+				if (bdstatus & T_TO)
+					pframe->status |= TX_ER_TIMEOUT;
+				if (bdstatus & T_UN)
+					pframe->status |= TX_ER_UNDERUN;
+			}
+			ep0_txframe_handle(ep);
+		}
+
+		bd = ep->c_txbd;
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+	}
+
+	return 0;
+}
+
+static int ep_txframe_handle(struct qe_ep *ep)
+{
+	if (frame_get_status(ep->txframe)&FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		VDBG("The EP0 transmit data have error!");
+		if (frame_get_info(ep->txframe)&PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		txcomplete(ep, 1);
+	} else
+		txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe); /* send the data */
+	return 0;
+}
+
+/* confirm the already trainsmited bd */
+static int qe_ep_txconf(struct qe_ep *ep)
+{
+	struct qe_bd *bd = NULL;
+	struct qe_frame *pframe = NULL;
+	u32 bdstatus;
+	unsigned char breakonrxinterrupt = 0;
+
+	bd = ep->c_txbd;
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+		if (bdstatus & DEVICE_T_ERROR) {
+			frame_set_status(pframe, FRAME_ERROR);
+			if (bdstatus & T_TO)
+				pframe->status |= TX_ER_TIMEOUT;
+			if (bdstatus & T_UN)
+				pframe->status |= TX_ER_UNDERUN;
+		}
+
+		/* clear and recycle the BD */
+		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&T_W));
+		BD_BUFFER_CLEAR(bd);
+		if (bdstatus&T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		/* handle the tx frame */
+		ep_txframe_handle(ep);
+		bd = ep->c_txbd;
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+	}
+	if (breakonrxinterrupt)
+		return -EIO;
+	else
+		return 0;
+}
+
+/* Add a request in queue, and try to transmit a packet */
+static int ep_req_send(struct qe_ep *ep, struct qe_req *req)
+{
+	int reval = 0;
+
+	if (ep->tx_req == NULL) {
+		ep->sent = 0;
+		ep->last = 0;
+		txcomplete(ep, 0); /* can gain a new tx_req */
+		reval = frame_create_tx(ep, ep->txframe);
+	}
+	return reval;
+}
+
+/* Maybe this is a good ideal */
+static int ep_req_rx(struct qe_ep *ep, struct qe_req *req)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u32 bdstatus, length;
+	u32 vaddr, fsize;
+	u8 *cp;
+	u8 finish_req = 0;
+	u8 framepid;
+
+	if (list_empty(&ep->queue)) {
+		VDBG("the req already finish!");
+		return 0;
+	}
+	udc = (struct qe_udc *)ep->udc;
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	bd = (struct qe_bd *)(ep->n_rxbd);
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus&R_E) && length) {
+		if (finish_req)
+			break;
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+					&& !(bdstatus & R_ERROR)) {
+			qe_frame_clean(pframe);
+			vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
+			frame_set_data(pframe, (u8 *)vaddr);
+			frame_set_length(pframe, (length - USB_CRC_SIZE));
+			frame_set_status(pframe, FRAME_OK);
+			switch (bdstatus & R_PID) {
+			case R_PID_DATA1:
+				frame_set_info(pframe, PID_DATA1); break;
+			default:
+				frame_set_info(pframe, PID_DATA0); break;
+			}
+			/* handle the rx frame */
+
+			if (frame_get_info(pframe)&PID_DATA1)
+				framepid = 0x1;
+			else
+				framepid = 0;
+
+			if (framepid != ep->data01) {
+				VDBG("the data01 error!");
+			} else {
+				fsize = frame_get_length(pframe);
+
+				cp = (u8 *)(req->req.buf) + req->req.actual;
+				if (cp) {
+					memcpy(cp, pframe->data, fsize);
+					req->req.actual += fsize;
+					if ((fsize < ep->ep.maxpacket)
+						|| (req->req.actual >=
+							req->req.length)) {
+						finish_req = 1;
+						done(ep, req, 0);
+						if (list_empty(&ep->queue))
+							qe_eprx_nack(ep);
+					}
+				}
+				qe_ep_toggledata01(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address *
+		 * only Clear the length */
+		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus & BD_STATUS_MASK));
+		ep->has_data--;
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+		length = bdstatus & BD_LENGTH_MASK;
+	}
+
+	ep->n_rxbd = (struct qe_bd *)bd;
+	ep_recycle_rxbds(ep);
+
+	return 0;
+}
+
+/* only add the request in queue */
+static int ep_req_receive(struct qe_ep *ep, struct qe_req *req)
+{
+	if (ep->state == EP_STATE_NACK) {
+		if (ep->has_data <= 0) {
+			/* Enable rx and unmask rx interrupt */
+			qe_eprx_normal(ep);
+		} else {
+			/* Copy the exist BD data */
+			ep_req_rx(ep, req);
+		}
+	}
+
+	return 0;
+}
+
+/********************************************************************
+	Internal Used Function End
+********************************************************************/
+
+/*-----------------------------------------------------------------------
+	Endpoint Management Functions For Gadget
+ -----------------------------------------------------------------------*/
+static int qe_ep_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_ep *ep = NULL;
+	int retval = 0;
+	unsigned char epnum;
+	unsigned long flags = 0;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] ||
+		(desc->bDescriptorType != USB_DT_ENDPOINT))
+		/* FIXME: add judge for ep->bEndpointAddress */
+		return -EINVAL;
+
+	udc = ep->udc;
+	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	epnum = (u8)desc->bEndpointAddress&0xF;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	retval = qe_ep_init((void *)udc, epnum, desc);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	if (retval != 0) {
+		qe_muram_free(muram_offset(ep->rxbase));
+		VDBG("enable ep%d fail", ep->epnum);
+		return -EINVAL;
+	}
+	VDBG("enable ep%d ok", ep->epnum);
+	return 0;
+}
+
+static int qe_ep_disable(struct usb_ep *_ep)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_ep *ep = NULL;
+	unsigned long flags = 0;
+	unsigned int size;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	if (!_ep || !ep->desc) {
+		VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	udc = (struct qe_udc *) ep->udc;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* Nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+	ep->desc = NULL;
+	ep->stopped = 1;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	qe_muram_free(muram_offset(ep->rxbase));
+
+	if (ep->dir == USB_DIR_OUT)
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN_RX + 1);
+	else
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN + 1);
+
+	if (ep->dir != USB_DIR_IN) {
+		kfree(ep->rxframe);
+		if (ep->rxbufmap) {
+			dma_unmap_single(udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+				ep->rxbuf_d = DMA_ADDR_INVALID;
+		} else
+			dma_sync_single_for_cpu(
+					udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		kfree(ep->rxbuffer);
+	}
+
+	if (ep->dir != USB_DIR_OUT)
+		kfree(ep->txframe);
+
+	VDBG("disabled %s OK", _ep->name);
+	return 0;
+}
+
+static struct usb_request *qe_alloc_request(struct usb_ep *_ep,	gfp_t gfp_flags)
+{
+	struct qe_req *req = NULL;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->req.dma = DMA_ADDR_INVALID;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req = NULL;
+
+	req = container_of(_req, struct qe_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+				gfp_t gfp_flags)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+	struct qe_udc *udc;
+	unsigned long flags;
+	int reval;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+	|| !list_empty(&req->queue)) {
+		VDBG("bad params");
+		return -EINVAL;
+	}
+	if (!_ep || (!ep->desc && ep_index(ep))) {
+		VDBG("bad ep");
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	/* map virtual address to hardware */
+	if (req->req.dma == DMA_ADDR_INVALID) {
+		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+					req->req.buf,
+					req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 1;
+	} else {
+		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+					req->req.dma, req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 0;
+	}
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+
+	list_add_tail(&req->queue, &ep->queue);
+	VDBG("gadget have request in %s! %d", ep->name, req->req.length);
+	spin_lock_irqsave(&udc->lock, flags);
+	/* push the request to device */
+	if (ep_is_in(ep))
+		reval = ep_req_send(ep, req);
+
+	/* EP0 */
+	if (ep_index(ep) == 0 && req->req.length > 0) {
+		if (ep_is_in(ep))
+			udc->ep0_state = DATA_STATE_XMIT;
+		else
+			udc->ep0_state = DATA_STATE_RECV;
+	}
+
+	if (ep->dir == USB_DIR_OUT)
+		reval = ep_req_receive(ep, req);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req;
+	unsigned long flags;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt  0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int qe_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct qe_ep *ep = NULL;
+	unsigned long flags = 0;
+	int status = -EOPNOTSUPP;
+	struct qe_udc *udc = NULL;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+	udc = ep->udc;
+	if (!_ep || !ep->desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (ep->epnum != 0) {
+		status = 0;
+		goto out;
+	}
+
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	status = 0;
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	qe_eptx_stall_change(ep, value);
+	qe_eprx_stall_change(ep, value);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep->epnum == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = 0;
+	}
+out:
+	VDBG(" %s %s halt stat %d", ep->ep.name,
+			value ?  "set" : "clear", status);
+
+	return status;
+}
+
+static struct usb_ep_ops qe_ep_ops = {
+	.enable = qe_ep_enable,
+	.disable = qe_ep_disable,
+
+	.alloc_request = qe_alloc_request,
+	.free_request = qe_free_request,
+
+	.queue = qe_ep_queue,
+	.dequeue = qe_ep_dequeue,
+
+	.set_halt = qe_ep_set_halt,
+};
+
+/*------------------------------------------------------------------------
+	Gadget Driver Layer Operations
+ ------------------------------------------------------------------------*/
+
+/* Get the current frame number */
+static int qe_get_frame(struct usb_gadget *gadget)
+{
+	u16 tmp;
+
+	tmp = in_be16(&udc_controller->usb_param->frame_n);
+	if (tmp & 0x8000)
+		tmp = tmp & 0x07ff;
+	else
+		tmp = -EINVAL;
+
+	return (int)tmp;
+}
+
+/* Tries to wake up the host connected to this gadget
+ *
+ * Return : 0-success
+ * Negative-this feature not enabled by host or not supported by device hw
+ */
+static int qe_wakeup(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+   detects VBUS sessions */
+static int qe_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	return -ENOTSUPP;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int qe_pullup(struct usb_gadget *gadget, int is_on)
+{
+	return -ENOTSUPP;
+}
+
+/* defined in usb_gadget.h */
+static struct usb_gadget_ops qe_gadget_ops = {
+	.get_frame = qe_get_frame,
+	.wakeup = qe_wakeup,
+/*	.set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */
+	.vbus_session = qe_vbus_session,
+	.vbus_draw = qe_vbus_draw,
+	.pullup = qe_pullup,
+};
+
+/*-------------------------------------------------------------------------
+	USB ep0 Setup process in BUS Enumeration
+ -------------------------------------------------------------------------*/
+static int udc_reset_ep_queue(struct qe_udc *udc, u8 pipe)
+{
+	struct qe_ep *ep = &udc->eps[pipe];
+
+	nuke(ep, -ECONNRESET);
+	ep->tx_req = NULL;
+	return 0;
+}
+
+static int reset_queues(struct qe_udc *udc)
+{
+	u8 pipe;
+
+	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++)
+		udc_reset_ep_queue(udc, pipe);
+
+	/* report disconnect; the driver is already quiesced */
+	udc->driver->disconnect(&udc->gadget);
+
+	return 0;
+}
+
+static void ch9setaddress(struct qe_udc *udc, u16 value, u16 index,
+			u16 length)
+{
+	/* Save the new address to device struct */
+	udc->device_address = (u8) value;
+	/* Update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+
+	/* Status phase , send a ZLP */
+	if (ep0_prime_status(udc, USB_DIR_IN))
+		qe_ep0_stall(udc);
+}
+
+static void ownercomplete(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+
+	req->req.buf = 0;
+	kfree(req);
+}
+
+static void ch9getstatus(struct qe_udc *udc, u16 value, u16 index,
+			 u16 length)
+{
+	u16 usb_status = 0;	/* fix me to give correct status */
+
+	struct qe_req *req;
+	struct qe_ep *ep;
+	int status = 0;
+
+	ep = &udc->eps[0];
+
+	req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL),
+					struct qe_req, req);
+	req->req.length = 2;
+	req->req.buf = udc->nullbuf;
+	memcpy(req->req.buf, (u8 *)&usb_status, 2);
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = ownercomplete;
+
+	udc->ep0_dir = USB_DIR_IN;
+
+	/* data phase */
+	status = qe_ep_queue(&(ep->ep), &(req->req), GFP_ATOMIC);
+
+	if (status) {
+		dev_err(udc->dev, "Can't respond to getstatus request \n");
+		qe_ep0_stall(udc);
+	}
+}
+
+/* only handle the setup request, suppose the device in normal status */
+static void setup_received_handle(struct qe_udc *udc,
+				struct usb_ctrlrequest *setup)
+{
+	/* Fix Endian (udc->local_setup_buff is cpu Endian now)*/
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	/* clear the previous request in the ep0 */
+	udc_reset_ep_queue(udc, 0);
+
+	if (setup->bRequestType & USB_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	switch (setup->bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase form udc */
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
+						USB_RECIP_DEVICE))
+			break;
+		ch9setaddress(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Requests with no data phase, status phase from udc */
+		if ((setup->bRequestType &  USB_TYPE_MASK)
+					!= USB_TYPE_STANDARD)
+			break;
+
+		if ((setup->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_ENDPOINT) {
+			int pipe = wIndex & USB_ENDPOINT_NUMBER_MASK;
+			struct qe_ep *ep;
+
+			if (wValue != 0 || wLength != 0
+				|| pipe > USB_MAX_ENDPOINTS)
+				break;
+			ep = &udc->eps[pipe];
+
+			spin_unlock(&udc->lock);
+			qe_ep_set_halt(&ep->ep,
+					(setup->bRequest == USB_REQ_SET_FEATURE)
+						? 1 : 0);
+			spin_lock(&udc->lock);
+		}
+
+		ep0_prime_status(udc, USB_DIR_IN);
+
+		return;
+
+	default:
+		break;
+	}
+
+	if (wLength) {
+		/* Data phase from gadget, status phase from udc */
+		if (setup->bRequestType & USB_DIR_IN) {
+			udc->ep0_state = DATA_STATE_XMIT;
+			udc->ep0_dir = USB_DIR_IN;
+		} else{
+			udc->ep0_state = DATA_STATE_RECV;
+			udc->ep0_dir = USB_DIR_OUT;
+		}
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+	} else {
+		/* No data phase, IN status from gadget */
+		udc->ep0_dir = USB_DIR_IN;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+	}
+}
+
+/*-------------------------------------------------------------------------
+	USB Interrupt handlers
+ -------------------------------------------------------------------------*/
+static void suspend_irq(struct qe_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	/* report suspend to the driver ,serial.c not support this*/
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void resume_irq(struct qe_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver , serial.c not support this*/
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+static void idle_irq(struct qe_udc *udc)
+{
+	u8 usbs;
+
+	usbs = in_8(&udc->usb_regs->usb_usbs);
+	if (usbs & USB_IDLE_STATUS_MASK) {
+		if ((udc->usb_state) != USB_STATE_SUSPENDED)
+			suspend_irq(udc);
+	} else {
+		if (udc->usb_state == USB_STATE_SUSPENDED)
+			resume_irq(udc);
+	}
+}
+
+static int reset_irq(struct qe_udc *udc)
+{
+	unsigned char i;
+
+	qe_usb_disable();
+	out_8(&udc->usb_regs->usb_usadr, 0);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		if (udc->eps[i].init)
+			qe_ep_reset(udc, i);
+	}
+
+	reset_queues(udc);
+	udc->usb_state = USB_STATE_DEFAULT;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = USB_DIR_OUT;
+	qe_usb_enable();
+	return 0;
+}
+
+static int bsy_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+static int txe_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+/* ep0 tx interrupt also in here */
+static int tx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	int i, res = 0;
+	struct qe_bd *bd;
+
+	if ((udc->usb_state == USB_STATE_ADDRESS)
+		&& (in_8(&udc->usb_regs->usb_usadr) == 0))
+		out_8(&udc->usb_regs->usb_usadr, udc->device_address);
+
+	for (i = (USB_MAX_ENDPOINTS-1); ((i >= 0) && (res == 0)); i--) {
+		ep = &(udc->eps[i]);
+		if (ep && ep->init && (ep->dir != USB_DIR_OUT)) {
+			bd = ep->c_txbd;
+			if (!(BD_STATUS_AND_LENGTH(bd)&T_R)
+						&& (BD_BUFFER(bd))) {
+				/* Disable the TX Interrupt */
+				/*confirm the transmitted bd*/
+				if (ep->epnum == 0)
+					res = qe_ep0_txconf(ep);
+				else
+					res = qe_ep_txconf(ep);
+				/* Enable the TX Interrupt */
+			}
+		}
+	}
+	return res;
+}
+
+
+/* setup packect's rx is handle in the function too */
+static void rx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	int i;
+	struct qe_bd *bd;
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		ep = &(udc->eps[i]);
+		if (ep && ep->init && (ep->dir != USB_DIR_IN)) {
+			bd = ep->n_rxbd;
+			if (!(BD_STATUS_AND_LENGTH(bd)&R_E)
+						&& (BD_BUFFER(bd))) {
+				if (ep->epnum == 0) {
+					qe_ep0_rx(udc);
+				} else {
+					/*non-setup package receive*/
+					qe_ep_rx(ep);
+				}
+			}
+		}
+	}
+}
+
+static irqreturn_t qe_udc_irq(int irq, void *_udc)
+{
+	struct qe_udc *udc = (struct qe_udc *)_udc;
+	u16 irq_src;
+	irqreturn_t status = IRQ_NONE;
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	irq_src = in_be16(&udc->usb_regs->usb_usber) &
+		in_be16(&udc->usb_regs->usb_usbmr);
+	/* Clear notification bits */
+	out_be16(&udc->usb_regs->usb_usber, irq_src);
+	/* USB Interrupt */
+	if (irq_src & USB_E_IDLE_MASK) {
+		idle_irq(udc);
+		irq_src &= ~USB_E_IDLE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXB_MASK) {
+		tx_irq(udc);
+		irq_src &= ~USB_E_TXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RXB_MASK) {
+		rx_irq(udc);
+		irq_src &= ~USB_E_RXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RESET_MASK) {
+		reset_irq(udc);
+		irq_src &= ~USB_E_RESET_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_BSY_MASK) {
+		bsy_irq(udc);
+		irq_src &= ~USB_E_BSY_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXE_MASK) {
+		txe_irq(udc);
+		irq_src &= ~USB_E_TXE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return status;
+}
+
+/*-------------------------------------------------------------------------
+	Gadget driver register and unregister.
+ --------------------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	int retval;
+	unsigned long flags = 0;
+
+	/* standard operations */
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || (driver->speed != USB_SPEED_FULL
+			&& driver->speed != USB_SPEED_HIGH)
+			|| !driver->bind || !driver->unbind ||
+			!driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (udc_controller->driver)
+		return -EBUSY;
+
+	/* lock is needed but whether should use this lock or another */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+
+	driver->driver.bus = 0;
+	/* hook up the driver */
+	udc_controller->driver = driver;
+	udc_controller->gadget.dev.driver = &driver->driver;
+	udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	retval = driver->bind(&udc_controller->gadget);
+	if (retval) {
+		dev_err(udc_controller->dev, "bind to %s --> %d",
+				driver->driver.name, retval);
+		udc_controller->gadget.dev.driver = 0;
+		udc_controller->driver = 0;
+		return retval;
+	}
+
+	/* Enable IRQ reg and Set usbcmd reg EN bit */
+	qe_usb_enable();
+
+	out_be16(&udc_controller->usb_regs->usb_usber, 0xffff);
+	out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = USB_DIR_OUT;
+	dev_info(udc_controller->dev, "%s bind to driver %s \n",
+		udc_controller->gadget.name, driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct qe_ep *loop_ep;
+	unsigned long flags;
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || driver != udc_controller->driver)
+		return -EINVAL;
+
+	/* stop usb controller, disable intr */
+	qe_usb_disable();
+
+	/* in fact, no needed */
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+
+	/* stand operation */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc_controller->eps[0], -ESHUTDOWN);
+	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+				ep.ep_list)
+		nuke(loop_ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	/* unbind gadget and unhook driver. */
+	driver->unbind(&udc_controller->gadget);
+	udc_controller->gadget.dev.driver = NULL;
+	udc_controller->driver = NULL;
+
+	dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/* udc structure's alloc and setup, include ep-param alloc */
+static void *qe_udc_config(struct of_device *ofdev)
+{
+	struct qe_udc *udc = NULL;
+	struct device_node *np = ofdev->node;
+	unsigned int tmp_addr = 0;
+	struct usb_device_para __iomem *usbpram = NULL;
+	unsigned int i;
+	u64 size;
+	u32 offset;
+	unsigned int nsize;
+	const unsigned int *prop;
+
+	udc = kzalloc(sizeof(struct qe_udc), GFP_KERNEL);
+	if (udc == NULL) {
+		dev_err(&ofdev->dev, "malloc udc failed\n");
+		goto cleanup;
+	}
+
+	udc->dev = &ofdev->dev;
+
+	/* use the default address for the usb parameter */
+	prop = of_get_property(np, "reg", &nsize);
+	offset = of_read_number(prop + 2, 1);
+	size = of_read_number(prop + 3, 1);
+
+	udc->usb_param = muram_addr(offset);
+
+	memset_io(udc->usb_param, 0, size);
+
+	usbpram = udc->usb_param;
+
+	out_be16(&usbpram->frame_n, 0);
+	out_be32(&usbpram->rstate, 0);
+
+	tmp_addr = qe_muram_alloc((USB_MAX_ENDPOINTS *
+					sizeof(struct usb_ep_para)),
+					   USB_EP_PARA_ALIGNMENT);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		usbpram->epptr[i] = (u16)tmp_addr;
+		udc->ep_param[i] = (struct usb_ep_para *)muram_addr(tmp_addr);
+		tmp_addr += 32;
+	}
+
+	memset_io(udc->ep_param[0], 0,
+			USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para));
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = 0;
+
+	/* initliaze the qe_udc lock */
+	spin_lock_init(&udc->lock);
+	return udc;
+
+cleanup:
+	kfree(udc);
+	return NULL;
+}
+
+/* USB Controller register init */
+static int qe_udc_reg_init(struct qe_udc *udc)
+{
+	struct usb_ctlr __iomem *qe_usbregs;
+	qe_usbregs = udc->usb_regs;
+
+	/* Init the usb register */
+	out_8(&qe_usbregs->usb_usmod, 0x01); /* FIXME if need enable in here */
+	out_be16(&qe_usbregs->usb_usbmr, 0);
+	out_8(&qe_usbregs->usb_uscom, 0);
+	out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR);
+
+	return 0;
+}
+
+static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+
+	ep->udc = udc;
+	strcpy(ep->name, ep_name[pipe_num]);
+	ep->ep.name = ep_name[pipe_num];
+
+	ep->ep.ops = &qe_ep_ops;
+	ep->stopped = 1;
+	ep->ep.maxpacket = (unsigned short) ~0;
+	ep->desc = NULL;
+	ep->dir = 0xff;
+	ep->epnum = (u8)pipe_num;
+	ep->sent = 0;
+	ep->last = 0;
+	ep->init = 0;
+	ep->rxframe = NULL;
+	ep->txframe = NULL;
+	ep->tx_req = NULL;
+	ep->state = EP_STATE_IDLE;
+	ep->has_data = 0;
+
+	/* the queue lists any req for this ep */
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* gagdet.ep_list used for ep_autoconfig so no ep0*/
+	if (pipe_num != 0)
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+	ep->gadget = &udc->gadget;
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ *	UDC device Driver operation functions				*
+ *----------------------------------------------------------------------*/
+static void qe_udc_release(struct device *dev)
+{
+	int i = 0;
+
+	complete(udc_controller->done);
+	qe_muram_free(muram_offset(&udc_controller->ep_param[0]));
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+		udc_controller->ep_param[i] = NULL;
+
+	kfree(udc_controller);
+	udc_controller = NULL;
+}
+
+/* Driver probe functions */
+static int qe_udc_probe(struct of_device *ofdev,
+			const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	unsigned int tmp_status = -ENODEV;
+	unsigned int i;
+
+	/* Initialize the udc structure including QH member and other member */
+	udc_controller = (struct qe_udc *)qe_udc_config(ofdev);
+	if (!udc_controller) {
+		VDBG("udc_controll is NULL");
+		return -ENOMEM;
+	}
+
+	udc_controller->usb_regs = of_iomap(np, 0);
+
+	/* initialize usb hw reg except for regs for EP,
+	 * leave usbintr reg untouched*/
+	qe_udc_reg_init(udc_controller);
+
+	/* here comes the stand operations for probe
+	 * set the qe_udc->gadget.xxx */
+	udc_controller->gadget.ops = &qe_gadget_ops;
+
+	/* gadget.ep0 is a pointer */
+	udc_controller->gadget.ep0 = &(udc_controller->eps[0].ep);
+
+	INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+
+	/* modify in register gadget process */
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* name: Identifies the controller hardware type. */
+	udc_controller->gadget.name = driver_name;
+
+	device_initialize(&udc_controller->gadget.dev);
+
+	strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+
+	udc_controller->gadget.dev.release = qe_udc_release;
+	udc_controller->gadget.dev.parent = &ofdev->dev;
+
+
+	/* EP:intialization qe_ep struct */
+	for (i = 0; i < USB_MAX_ENDPOINTS ; i++) {
+		/*because the ep type isn't decide here so
+		 * qe_ep_init() should be called in ep_enable() */
+
+		/* setup the qe_ep struct and link ep.ep.list
+		 * into gadget.ep_list */
+		qe_ep_config(udc_controller, (unsigned char)i);
+	}
+
+	/* ep0 initialization in here */
+	qe_ep_init(udc_controller, 0, &qe_ep0_desc);
+
+	/* create a buf for ZLP send */
+	udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
+	if (udc_controller->nullbuf == NULL) {
+		VDBG("cannot alloc nullbuf");
+		return -ENOMEM;
+	}
+
+	udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf);
+	if (udc_controller->nullp == DMA_ADDR_INVALID) {
+		udc_controller->nullp = dma_map_single(
+					udc_controller->gadget.dev.parent,
+					udc_controller->nullbuf,
+					256,
+					DMA_TO_DEVICE);
+		udc_controller->nullmap = 1;
+	} else {
+		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
+					udc_controller->nullp, 256,
+					DMA_TO_DEVICE);
+		udc_controller->nullmap = 1;
+	}
+
+	tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet,
+			(unsigned long)udc_controller);
+	/* request irq and disable DR  */
+	udc_controller->usb_irq = irq_of_parse_and_map(np, 0);
+
+	tmp_status = request_irq(udc_controller->usb_irq, qe_udc_irq, 0,
+				driver_name, udc_controller);
+	if (tmp_status != 0) {
+		dev_err(udc_controller->dev, "cannot request irq %d err %d \n",
+			udc_controller->usb_irq, tmp_status);
+		return tmp_status;
+	}
+
+	tmp_status = device_add(&udc_controller->gadget.dev);
+	if (tmp_status != 0)
+		return tmp_status;
+
+	dev_info(udc_controller->dev,
+			"QE/CPM USB controller initialized as device\n");
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int qe_udc_suspend(struct of_device *dev, pm_message_t state)
+{
+	return -ENOTSUPP;
+}
+
+static int qe_udc_resume(struct of_device *dev)
+{
+	return -ENOTSUPP;
+}
+#endif
+
+static int qe_udc_remove(struct of_device *ofdev)
+{
+	struct qe_ep *ep = NULL;
+	unsigned int size;
+
+	DECLARE_COMPLETION(done);
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	udc_controller->done = &done;
+	tasklet_disable(&udc_controller->rx_tasklet);
+
+	if (udc_controller->nullmap) {
+		dma_unmap_single(udc_controller->gadget.dev.parent,
+			udc_controller->nullp, 256,
+				DMA_TO_DEVICE);
+			udc_controller->nullp = DMA_ADDR_INVALID;
+	} else
+		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
+			udc_controller->nullp, 256,
+				DMA_TO_DEVICE);
+	kfree(udc_controller->nullbuf);
+
+	ep = &(udc_controller->eps[0]);
+	qe_muram_free(muram_offset(ep->rxbase));
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2)*(USB_BDRING_LEN + 1);
+
+	kfree(ep->rxframe);
+	if (ep->rxbufmap) {
+		dma_unmap_single(udc_controller->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+		ep->rxbuf_d = DMA_ADDR_INVALID;
+	} else
+		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+
+	kfree(ep->rxbuffer);
+	kfree(ep->txframe);
+
+	/* free irq */
+	free_irq(udc_controller->usb_irq, udc_controller);
+
+	tasklet_kill(&udc_controller->rx_tasklet);
+
+	device_unregister(&udc_controller->gadget.dev);
+	if (udc_controller != NULL)
+		kfree(udc_controller);
+
+	/* free udc --wait for the release() finished */
+	wait_for_completion(&done);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct of_device_id qe_udc_match[] = {
+	{
+		.compatible = "fsl,qe_udc",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, qe_udc_match);
+
+static struct of_platform_driver udc_driver = {
+	.name           = (char *) driver_name,
+	.match_table    = qe_udc_match,
+	.probe          = qe_udc_probe,
+	.remove         = qe_udc_remove,
+#ifdef CONFIG_PM
+	.suspend        = qe_udc_suspend,
+	.resume         = qe_udc_resume,
+#endif
+};
+
+static int __init qe_udc_init(void)
+{
+	printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc,
+			DRIVER_VERSION);
+	return of_register_platform_driver(&udc_driver);
+}
+
+static void __exit qe_udc_exit(void)
+{
+	of_unregister_platform_driver(&udc_driver);
+}
+
+module_init(qe_udc_init);
+module_exit(qe_udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
new file mode 100644
index 0000000..8b333f9
--- /dev/null
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -0,0 +1,458 @@
+/*
+ * drivers/usb/gadget/qe_udc.h
+ *
+ * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Xiaobo Xie <X.Xie@freescale.com>
+ *
+ * Description:
+ * Freescale USB device/endpoint management registers
+ *
+ * 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 __FSL_QE_UDC_H
+#define __FSL_QE_UDC_H
+
+#ifdef CONFIG_CPM2
+extern unsigned long cpm2_immr;
+
+static inline void __iomem *muram_addr(unsigned long offset)
+{
+	return (void __iomem *)cpm2_immr + offset;
+}
+
+static inline unsigned long muram_offset(void __iomem *addr)
+{
+	return addr - (void __iomem *)cpm2_immr;
+}
+#else
+#define muram_addr	qe_muram_addr
+#define muram_offset	qe_muram_offset
+#endif
+
+#define USB_MAX_ENDPOINTS               4
+#define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
+#define USB_EP0_MAX_SIZE		64
+#define USB_MAX_CTRL_PAYLOAD            0x4000
+#define USB_BDRING_LEN			16
+#define USB_BDRING_LEN_RX		256
+#define USB_BDRING_LEN_TX		16
+#define MIN_EMPTY_BDS			128
+#define MAX_DATA_BDS			8
+#define USB_CRC_SIZE			2
+#define USB_DIR_BOTH			0x88
+#define R_BUF_MAXSIZE			0x800
+#define USB_EP_PARA_ALIGNMENT		32
+
+/* 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
+#define USBER_ALL_CLEAR 	0x0fff
+
+#define USB_E_DEFAULT_DEVICE   (USB_E_RESET_MASK | USB_E_TXE4_MASK | \
+				USB_E_TXE3_MASK | USB_E_TXE2_MASK | \
+				USB_E_TXE1_MASK | USB_E_BSY_MASK | \
+				USB_E_TXB_MASK | USB_E_RXB_MASK)
+
+#define USB_E_TXE_MASK         (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\
+				 USB_E_TXE2_MASK | USB_E_TXE1_MASK)
+/* USB Status Register define */
+#define USB_IDLE_STATUS_MASK	0x01
+
+/* USB Start of Frame Timer */
+#define USB_USSFT_MASK		0x3FFF
+
+/* USB Frame Number Register */
+#define USB_USFRN_MASK		0xFFFF
+
+struct usb_device_para{
+	u16	epptr[4];
+	u32	rstate;
+	u32	rptr;
+	u16	frame_n;
+	u16	rbcnt;
+	u32	rtemp;
+	u32	rxusb_data;
+	u16	rxuptr;
+	u8	reso[2];
+	u32	softbl;
+	u8	sofucrctemp;
+};
+
+struct usb_ep_para{
+	u16	rbase;
+	u16	tbase;
+	u8	rbmr;
+	u8	tbmr;
+	u16	mrblr;
+	u16	rbptr;
+	u16	tbptr;
+	u32	tstate;
+	u32	tptr;
+	u16	tcrc;
+	u16	tbcnt;
+	u32	ttemp;
+	u16	txusbu_ptr;
+	u8	reserve[2];
+};
+
+#define USB_BUSMODE_GBL		0x20
+#define USB_BUSMODE_BO_MASK	0x18
+#define USB_BUSMODE_BO_SHIFT	0x3
+#define USB_BUSMODE_BE		0x2
+#define USB_BUSMODE_CETM	0x04
+#define USB_BUSMODE_DTB		0x02
+
+/* Endpoint basic handle */
+#define ep_index(EP)		((EP)->desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
+#define ep_is_in(EP)	((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+			USB_DIR_IN):((EP)->desc->bEndpointAddress \
+			& USB_DIR_IN) == USB_DIR_IN)
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+/* ep tramsfer mode */
+#define USBP_TM_CTL	0
+#define USBP_TM_ISO	1
+#define USBP_TM_BULK	2
+#define USBP_TM_INT	3
+
+/*-----------------------------------------------------------------------------
+	USB RX And TX DATA Frame
+ -----------------------------------------------------------------------------*/
+struct qe_frame{
+	u8 *data;
+	u32 len;
+	u32 status;
+	u32 info;
+
+	void *privdata;
+	struct list_head node;
+};
+
+/* Frame structure, info field. */
+#define PID_DATA0              0x80000000 /* Data toggle zero */
+#define PID_DATA1              0x40000000 /* Data toggle one  */
+#define PID_SETUP              0x20000000 /* setup bit */
+#define SETUP_STATUS           0x10000000 /* setup status bit */
+#define SETADDR_STATUS         0x08000000 /* setupup address status bit */
+#define NO_REQ                 0x04000000 /* Frame without request */
+#define HOST_DATA              0x02000000 /* Host data frame */
+#define FIRST_PACKET_IN_FRAME  0x01000000 /* first packet in the frame */
+#define TOKEN_FRAME            0x00800000 /* Host token frame */
+#define ZLP                    0x00400000 /* Zero length packet */
+#define IN_TOKEN_FRAME         0x00200000 /* In token package */
+#define OUT_TOKEN_FRAME        0x00100000 /* Out token package */
+#define SETUP_TOKEN_FRAME      0x00080000 /* Setup token package */
+#define STALL_FRAME            0x00040000 /* Stall handshake */
+#define NACK_FRAME             0x00020000 /* Nack handshake */
+#define NO_PID                 0x00010000 /* No send PID */
+#define NO_CRC                 0x00008000 /* No send CRC */
+#define HOST_COMMAND           0x00004000 /* Host command frame   */
+
+/* Frame status field */
+/* Receive side */
+#define FRAME_OK               0x00000000 /* Frame tranmitted or received OK */
+#define FRAME_ERROR            0x80000000 /* Error occured on frame */
+#define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */
+#define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */
+#define RX_ER_NONOCT           0x10000000 /* Rx Non Octet Aligned Packet */
+#define RX_ER_BITSTUFF         0x08000000 /* Frame Aborted --Received packet
+						with bit stuff error */
+#define RX_ER_CRC              0x04000000 /* Received packet with CRC error */
+#define RX_ER_OVERUN           0x02000000 /* Over-run occured on reception */
+#define RX_ER_PID              0x01000000 /* Wrong PID received */
+/* Tranmit side */
+#define TX_ER_NAK              0x00800000 /* Received NAK handshake */
+#define TX_ER_STALL            0x00400000 /* Received STALL handshake */
+#define TX_ER_TIMEOUT          0x00200000 /* Transmit time out */
+#define TX_ER_UNDERUN          0x00100000 /* Transmit underrun */
+#define FRAME_INPROGRESS       0x00080000 /* Frame is being transmitted */
+#define ER_DATA_UNDERUN        0x00040000 /* Frame is shorter then expected */
+#define ER_DATA_OVERUN         0x00020000 /* Frame is longer then expected */
+
+/* QE USB frame operation functions */
+#define frame_get_length(frm) (((struct qe_frame *)frm)->len)
+#define frame_set_length(frm, leng) (((struct qe_frame *)frm)->len = leng)
+#define frame_get_data(frm) (((struct qe_frame *)frm)->data)
+#define frame_set_data(frm, dat) (((struct qe_frame *)frm)->data = dat)
+#define frame_get_info(frm) (((struct qe_frame *)frm)->info)
+#define frame_set_info(frm, inf) (((struct qe_frame *)frm)->info = inf)
+#define frame_get_status(frm) (((struct qe_frame *)frm)->status)
+#define frame_set_status(frm, stat) (((struct qe_frame *)frm)->status = stat)
+#define frame_get_privdata(frm) (((struct qe_frame *)frm)->privdata)
+#define frame_set_privdata(frm, dat) (((struct qe_frame *)frm)->privdata = dat)
+
+#define qe_frame_clean(frm)                     \
+	do {                                  \
+		frame_set_data(frm, NULL);         \
+		frame_set_length(frm, 0);          \
+		frame_set_status(frm, FRAME_OK);   \
+		frame_set_info(frm, 0);            \
+		frame_set_privdata(frm, 0);        \
+	} while (0)
+
+#define qe_frame_init(frm)                                   \
+	do {                                               \
+		qe_frame_clean(frm);                         \
+		INIT_LIST_HEAD(&(((struct qe_frame *)frm)->node)); \
+	} while (0)
+
+struct qe_req {
+	struct usb_request req;
+	struct list_head queue;
+	/* ep_queue() func will add
+	 a request->queue into a udc_ep->queue 'd tail */
+	struct qe_ep *ep;
+	unsigned mapped:1;
+};
+
+struct qe_ep {
+	struct usb_ep ep;
+	struct list_head queue;
+	struct qe_udc *udc;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_gadget *gadget;
+
+	u8 state;
+
+	struct qe_bd *rxbase;
+	struct qe_bd *n_rxbd;
+	struct qe_bd *e_rxbd;
+
+	struct qe_bd *txbase;
+	struct qe_bd *n_txbd;
+	struct qe_bd *c_txbd;
+
+	struct qe_frame *rxframe;
+	u8 *rxbuffer;
+	dma_addr_t rxbuf_d;
+	u8 rxbufmap;
+	unsigned char localnack;
+	int has_data;
+
+	struct qe_frame *txframe;
+	struct qe_req *tx_req;
+	int sent;  /*data already sent */
+	int last;  /*data sent in the last time*/
+
+	u8 dir;
+	u8 epnum;
+	u8 tm; /* transfer mode */
+	u8 data01;
+	u8 init;
+
+	u8 already_seen;
+	u8 enable_tasklet;
+	u8 setup_stage;
+	u32 last_io;            /* timestamp */
+
+	char name[14];
+
+	unsigned double_buf:1;
+	unsigned stopped:1;
+	unsigned fnf:1;
+	unsigned has_dma:1;
+
+	u8 ackwait;
+	u8 dma_channel;
+	u16 dma_counter;
+	int lch;
+
+	struct timer_list timer;
+};
+
+struct qe_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct qe_ep eps[USB_MAX_ENDPOINTS];
+	struct usb_ctrlrequest local_setup_buff;
+	spinlock_t lock;	/* lock for set/config qe_udc */
+
+	struct qe_req *status_req;     /* ep0 status request */
+
+	/* USB and EP Parameter Block pointer */
+	struct usb_device_para __iomem *usb_param;
+	struct usb_ep_para __iomem *ep_param[4];
+
+	u32 max_pipes;          /* Device max pipes */
+	u32 max_use_endpts;     /* Max endpointes to be used */
+	u32 bus_reset;          /* Device is bus reseting */
+	u32 resume_state;       /* USB state to resume*/
+	u32 usb_state;          /* USB current state */
+	u32 usb_next_state;     /* USB next state */
+	u32 ep0_state;          /* Enpoint zero state */
+	u32 ep0_dir;            /* Enpoint zero direction: can be
+				USB_DIR_IN or USB_DIR_OUT*/
+	u32 usb_sof_count;      /* SOF count */
+	u32 errors;             /* USB ERRORs count */
+
+	u8 *tmpbuf;
+	u32 c_start;
+	u32 c_end;
+
+	u8 *nullbuf;
+	dma_addr_t nullp;
+	u8 nullmap;
+	u8 device_address;	/* Device USB address */
+
+	unsigned int usb_clock;
+	unsigned int usb_irq;
+	struct usb_ctlr __iomem *usb_regs;
+
+	struct tasklet_struct rx_tasklet;
+
+	struct completion *done;	/* to make sure release() is done */
+};
+
+#define EP_STATE_IDLE	0
+#define EP_STATE_NACK	1
+#define EP_STATE_STALL	2
+
+/*---------------------------------------------------------------------
+ * 		Mask definitions for usb BD                           *
+ *--------------------------------------------------------------------*/
+#define QE_SIZEOF_BD       sizeof(struct qe_bd)
+
+#define BD_BUFFER_ARG(bd)                   (((struct qe_bd *)bd)->buf)
+#define BD_BUFFER_CLEAR(bd)                 out_be32(&(BD_BUFFER_ARG(bd)), 0);
+#define BD_BUFFER(bd)                       in_be32(&(BD_BUFFER_ARG(bd)))
+#define BD_STATUS_AND_LENGTH_SET(bd, val)   out_be32((u32 *)bd, val)
+#define BD_STATUS_AND_LENGTH(bd)            in_be32((u32 *)bd)
+#define BD_BUFFER_SET(bd, buffer)           out_be32(&(BD_BUFFER_ARG(bd)), \
+							(u32)(buffer))
+
+/*
+ * transmit BD's status
+ */
+#define T_R           0x80000000         /* ready bit */
+#define T_W           0x20000000         /* wrap bit */
+#define T_I           0x10000000         /* interrupt on completion */
+#define T_L           0x08000000         /* last */
+#define T_TC          0x04000000         /* transmit CRC */
+#define T_CNF         0x02000000         /* wait for  transmit confirm */
+#define T_LSP         0x01000000         /* Low-speed transaction */
+#define T_PID         0x00c00000         /* packet id */
+#define T_NAK         0x00100000         /* No ack. */
+#define T_STAL        0x00080000         /* Stall recieved */
+#define T_TO          0x00040000         /* time out */
+#define T_UN          0x00020000         /* underrun */
+
+#define DEVICE_T_ERROR    (T_UN | T_TO)
+#define HOST_T_ERROR      (T_UN | T_TO | T_NAK | T_STAL)
+#define DEVICE_T_BD_MASK  DEVICE_T_ERROR
+#define HOST_T_BD_MASK    HOST_T_ERROR
+
+#define T_PID_SHIFT   6
+#define T_PID_DATA0   0x00800000         /* Data 0 toggle */
+#define T_PID_DATA1   0x00c00000         /* Data 1 toggle */
+
+/*
+ * receive BD's status
+ */
+#define R_E           0x80000000         /* buffer empty */
+#define R_W           0x20000000         /* wrap bit */
+#define R_I           0x10000000         /* interrupt on reception */
+#define R_L           0x08000000         /* last */
+#define R_F           0x04000000         /* first */
+#define R_PID         0x00c00000         /* packet id */
+#define R_NO          0x00100000         /* Rx Non Octet Aligned Packet */
+#define R_AB          0x00080000         /* Frame Aborted */
+#define R_CR          0x00040000         /* CRC Error */
+#define R_OV          0x00020000         /* Overrun */
+
+#define R_ERROR       (R_NO | R_AB | R_CR | R_OV)
+#define R_BD_MASK     R_ERROR
+
+#define R_PID_DATA0   0x00000000
+#define R_PID_DATA1   0x00400000
+#define R_PID_SETUP   0x00800000
+
+/* Bulk only class request */
+#define USB_BULK_RESET_REQUEST          0xff
+
+extern int par_io_config_pin(u8  port, u8  pin, int dir, int open_drain,
+				int assignment, int has_irq);
+
+#ifdef DEBUG
+#define VDBG(fmt, args...)	printk(KERN_DEBUG "[%s]  " fmt "\n", \
+				__func__, ## args)
+#else
+#define VDBG(fmt, args...)       do {} while (0)
+#endif
+
+#define CPM_USB_STOP_TX 0x2e600000
+#define CPM_USB_RESTART_TX 0x2e600000
+#define CPM_USB_STOP_TX_OPCODE 0x0a
+#define CPM_USB_RESTART_TX_OPCODE 0x0b
+#define CPM_USB_EP_SHIFT 5
+
+#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 5246e8f..7e5be52 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -147,6 +147,13 @@
 #define	gadget_is_m66592(g)	0
 #endif
 
+/* Freescale CPM/QE UDC SUPPORT */
+#ifdef CONFIG_USB_GADGET_FSL_QE
+#define gadget_is_fsl_qe(g)	!strcmp("fsl_qe_udc", (g)->name)
+#else
+#define gadget_is_fsl_qe(g)	0
+#endif
+
 
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
@@ -212,6 +219,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x20;
 	else if (gadget_is_m66592(gadget))
 		return 0x21;
+	else if (gadget_is_fsl_qe(gadget))
+		return 0x22;
 	return -ENOENT;
 }
 
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related

* Re: [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: David Miller @ 2008-08-06  7:19 UTC (permalink / raw)
  To: grant.likely; +Cc: miltonm, linuxppc-dev, paulus
In-Reply-To: <fa686aa40808052335i7a3bbc64g5bcdaca21c3550af@mail.gmail.com>

From: "Grant Likely" <grant.likely@secretlab.ca>
Date: Wed, 6 Aug 2008 00:35:24 -0600

> On Wed, Aug 6, 2008 at 12:32 AM, David Miller <davem@davemloft.net> wrote:
> > From: Grant Likely <grant.likely@secretlab.ca>
> > Date: Wed, 06 Aug 2008 00:02:44 -0600
> >
> >> of_lookup_stdout() is useful for figuring out what device to use as output
> >> for early boot progress messages.  It returns the node pointed to by the
> >> linux,stdout-path property in the chosen node.
> >>
> >> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> >
> > On sparc platforms this is obtained differently.  We obtain the 32-bit
> > instance value of "/chosen/stdout" and convert that into a prom device
> > node path using "instance-to-path".
> 
> How about if I modify it to try both methods?

Sure, that would be nice.

^ permalink raw reply

* Re: [RFC/PATCH 2/3] of: add of_lookup_stdout() utility function
From: Stephen Rothwell @ 2008-08-06  7:42 UTC (permalink / raw)
  To: Grant Likely; +Cc: linuxppc-dev, paulus, miltonm
In-Reply-To: <fa686aa40808052334m2946298aw453afce8b47e6bf7@mail.gmail.com>

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

Hi Grant,

On Wed, 6 Aug 2008 00:34:04 -0600 "Grant Likely" <grant.likely@secretlab.ca> wrote:
>
> On Wed, Aug 6, 2008 at 12:14 AM, Michael Ellerman
> <michael@ellerman.id.au> wrote:
> > On Wed, 2008-08-06 at 00:02 -0600, Grant Likely wrote:
> >> From: Grant Likely <grant.likely@secretlab.ca>
> >>
> >> of_lookup_stdout() is useful for figuring out what device to use as output
> >> for early boot progress messages.  It returns the node pointed to by the
> >> linux,stdout-path property in the chosen node.
> >
> > Nice. You don't feel like converting all the places that currently do it
> > by hand to use this do you :)
> 
> Yep, I'll do this.  :-)

Could you also send an email to Dave Miller to see if he has any objections
to this function being generic (since the Sparc guys share this code).

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox