LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: David S. Miller @ 2006-05-10 22:25 UTC (permalink / raw)
  To: paulus; +Cc: linux-arch, linuxppc-dev, linux-kernel, rth
In-Reply-To: <17506.21908.857189.645889@cargo.ozlabs.ibm.com>

From: Paul Mackerras <paulus@samba.org>
Date: Thu, 11 May 2006 07:05:24 +1000

> No, Richard has a point, it's not the value that is the concern, it's
> the address, which gcc could assume is still valid after a barrier.
> Drat.

Oh right, and that's currently part of why we obfuscate the
address computation with the RELOC_HIDE() buisness.

Once we expose what's really going on with something like
__thread, gcc can now be "smart" about it.

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Paul Mackerras @ 2006-05-10 23:05 UTC (permalink / raw)
  To: Richard Henderson; +Cc: linux-arch, linuxppc-dev, linux-kernel, t
In-Reply-To: <20060510154702.GA28938@twiddle.net>

Richard Henderson writes:

> How do you plan to address the compiler optimizing
> 
> 	__thread int foo;
> 	{
> 	  use(foo);
> 	  schedule();
> 	  use(foo);
> 	}
> 
> into
> 
> 	{
> 	  int *tmp = &foo;	// tls arithmetic here
> 	  use(*tmp);
> 	  schedule();
> 	  use(*tmp);
> 	}

Hmmm...  Would it be sufficient to use a RELOC_HIDE in __get_cpu_var,
like this?

#define __get_cpu_var(x)	(*(RELOC_HIDE(&per_cpu__##x, 0)))

Paul.

^ permalink raw reply

* Re: Alubook 5,8: No sound with 2.6.17-rc3-g5528e568-dirty
From: Wolfgang Pfeiffer @ 2006-05-10 23:08 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev list
In-Reply-To: <20060510213028.GG3878@localhost>

On Wed, May 10, 2006 at 11:30:28PM +0200, Wolfgang Pfeiffer wrote:
> On Wed, May 10, 2006 at 06:04:13PM +0200, Johannes Berg wrote:
> 
> > Also, try snd-aoa.
> 
> Impossible here, it does not compile here. Neither with gcc 4.0 or 4.1
> 

Not being sure tho' whether the fact my source dir for the running
kernel is cleaned up, IINM, (like "fakeroot make-kpkg clean") is the
culprit for the failed compile ... I'll have a look into that at
next daylight ... :)

Until then

Wolfgang 

-- 
Wolfgang Pfeiffer: /ICQ: 286585973/ + + +  /AIM: crashinglinux/
http://profiles.yahoo.com/wolfgangpfeiffer

Key ID: E3037113
http://keyserver.mine.nu/pks/lookup?search=0xE3037113&fingerprint=on

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Segher Boessenkool @ 2006-05-10 23:17 UTC (permalink / raw)
  To: Paul Mackerras
  Cc: linux-arch, linuxppc-dev, rth, David S. Miller, linux-kernel
In-Reply-To: <17506.21908.857189.645889@cargo.ozlabs.ibm.com>

>>> How do you plan to address the compiler optimizing
>>  ...
>>> Across the schedule, we may have changed cpus, making the cached
>>> address invalid.
>>
>> Per-cpu variables need to be accessed only with preemption
>> disabled.  And the preemption enable/disable operations
>> provide a compiler memory barrier.
>
> No, Richard has a point, it's not the value that is the concern, it's
> the address, which gcc could assume is still valid after a barrier.
> Drat.

Would an asm clobber of GPR13 in the schedule routines (or a wrapper
for them, or whatever) work?


Segher

^ permalink raw reply

* [RFC-PATCH] Prevent tasks from sleeping in die()
From: David Wilder @ 2006-05-11  0:19 UTC (permalink / raw)
  To: linuxppc-dev

I am seeing an issue in die() when voluntary preemption is enabled.
die() 
->>show_regs()->>show_instructions()->>__get_user_nocheck()->>might_sleep()
If multiple CPUs call die() and one should sleep other CPUs may block on 
the die_lock held by the sleeping CPU.  This problem is seen when a 
soft-reset is issued as all CPUs call die() at roughly the same time.

Is this the correct way to fix this problem?

Signed-of-by: <wilder@us.ibm.com>

diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 064a525..dc45bcd 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -101,6 +101,15 @@ int die(const char *str, struct pt_regs
    if (debugger(regs))
        return 1;

+    /* If voluntary preemption is on we can sleep
+     * in show_regs().  This is bad as we hold the
+    * die_lock. Ether the task is exiting or the system
+    * is crashing so there is no need to restore the
+    * NEED_RESCHED flag.
+    */
+    clear_need_resched();
+
+
        console_verbose();
        spin_lock_irq(&die_lock);
        bust_spinlocks(1);

-- 
David Wilder
IBM Linux Technology Center
Beaverton, Oregon, USA 
dwilder@us.ibm.com
(503)578-3789

^ permalink raw reply related

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Paul Mackerras @ 2006-05-10 23:44 UTC (permalink / raw)
  To: Richard Henderson, t, linux-kernel, linux-arch, linuxppc-dev,
	amodra
In-Reply-To: <17506.29128.591758.502430@cargo.ozlabs.ibm.com>

I wrote:

> Hmmm...  Would it be sufficient to use a RELOC_HIDE in __get_cpu_var,
> like this?
> 
> #define __get_cpu_var(x)	(*(RELOC_HIDE(&per_cpu__##x, 0)))

But that won't work because the compiler can still cache &per_cpu__x.
I guess I have to do this:

#define __get_cpu_var(var, cpu)					\
	(*(__typeof__(&per_cpu__##var))({			\
		void *__ptr;					\
		asm("addi %0,13,per_cpu__"#var"@tprel"		\
		    : "=r" (__ptr));				\
		__ptr;						\
	}))

That means we lose the possible optimization of combining the addi
into a following load or store.  Bah.  However, I guess it's still
better than what we do at the moment.

Paul.

^ permalink raw reply

* Re: [RFC-PATCH] Prevent tasks from sleeping in die()
From: Paul Mackerras @ 2006-05-10 23:45 UTC (permalink / raw)
  To: David Wilder; +Cc: linuxppc-dev
In-Reply-To: <44628316.80600@us.ibm.com>

David Wilder writes:

> I am seeing an issue in die() when voluntary preemption is enabled.
> die() 
> ->>show_regs()->>show_instructions()->>__get_user_nocheck()->>might_sleep()

Are you using Linus' current git tree?  It has a patch that fixes this
problem by making __get_user_nocheck do the might_sleep only if the
address you give it is a user address.

Paul.

^ permalink raw reply

* Re: Alubook 5,8: No sound with 2.6.17-rc3-g5528e568-dirty
From: Benjamin Herrenschmidt @ 2006-05-10 23:47 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev list
In-Reply-To: <2829.131.234.104.112.1147277053.squirrel@secure.sipsolutions.net>

On Wed, 2006-05-10 at 18:04 +0200, Johannes Berg wrote:
> > As to the reason why I came here:
> > As I can't be sure whether the issue announced in the subject line is
> > really a kernel issue
> 
> Did it ever work? I don't quite know what machine a 5,8 is since I have a
> 5,6 only. Also, try snd-aoa.

Same as mine, it works fine with snd-aoa

Ben.

^ permalink raw reply

* Re: Alubook 5,8: No sound with 2.6.17-rc3-g5528e568-dirty
From: Benjamin Herrenschmidt @ 2006-05-10 23:48 UTC (permalink / raw)
  To: Wolfgang Pfeiffer; +Cc: linuxppc-dev list, Johannes Berg
In-Reply-To: <20060510172443.GF3878@localhost>

On Wed, 2006-05-10 at 19:24 +0200, Wolfgang Pfeiffer wrote:
> On Wed, May 10, 2006 at 06:04:13PM +0200, Johannes Berg wrote:
> > > As to the reason why I came here:
> > > As I can't be sure whether the issue announced in the subject line is
> > > really a kernel issue
> > 
> > Did it ever work? 
> 
> Not with Debian, but with this Ubuntu live CD I have sound: 
> Ubuntu 6.06 "Dapper Drake" Beta 2 powerpc (20060427)
> 
> The kernel on their CD is a 2.6.15-21-powerpc

Dapper kernel has the hack to consider it as a toonie. No volume control
nor anything.

> > I don't quite know what machine a 5,8 is since I have a
> > 5,6 only. 
> 
> 
> 5.8 is, IINM, the last PPC 15" Powerbook that Apple made
> 
> > Also, try snd-aoa.
> 
> With the git kernel mentioned in my previous mail there's no snd-aoa
> available, IINM. I'll try figure out how to get the source and compile
> another kernel with this driver ...

It's not merged upstream yet.

Ben.

^ permalink raw reply

* Re: Alubook 5,8: No sound with 2.6.17-rc3-g5528e568-dirty
From: Benjamin Herrenschmidt @ 2006-05-10 23:49 UTC (permalink / raw)
  To: Wolfgang Pfeiffer; +Cc: linuxppc-dev list, Johannes Berg
In-Reply-To: <20060510230811.GH3878@localhost>

On Thu, 2006-05-11 at 01:08 +0200, Wolfgang Pfeiffer wrote:
> On Wed, May 10, 2006 at 11:30:28PM +0200, Wolfgang Pfeiffer wrote:
> > On Wed, May 10, 2006 at 06:04:13PM +0200, Johannes Berg wrote:
> > 
> > > Also, try snd-aoa.
> > 
> > Impossible here, it does not compile here. Neither with gcc 4.0 or 4.1
> > 
> 
> Not being sure tho' whether the fact my source dir for the running
> kernel is cleaned up, IINM, (like "fakeroot make-kpkg clean") is the
> culprit for the failed compile ... I'll have a look into that at
> next daylight ... :)

You can't build modules with "cleaned" kernel headers.

Ben.

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: David S. Miller @ 2006-05-11  0:11 UTC (permalink / raw)
  To: paulus; +Cc: linux-arch, linux-kernel, linuxppc-dev, amodra, rth, t
In-Reply-To: <17506.31456.68099.57515@cargo.ozlabs.ibm.com>

From: Paul Mackerras <paulus@samba.org>
Date: Thu, 11 May 2006 09:44:32 +1000

> That means we lose the possible optimization of combining the addi
> into a following load or store.  Bah.  However, I guess it's still
> better than what we do at the moment.

If you have to hide the operation so deeply like this, maybe you can
do something similar to sparc64, by explicitly doing the per-cpu fixed
register and offsets, and still get the single instruction relocs that
powerpc can do for up to 64K by doing something like:

	&per_cpu_blah - &per_cpu_base

to calculate the offset.

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Richard Henderson @ 2006-05-11  0:22 UTC (permalink / raw)
  To: Segher Boessenkool
  Cc: linux-arch, linuxppc-dev, Paul Mackerras, David S. Miller,
	linux-kernel
In-Reply-To: <49BB818F-DF88-43A3-8B6A-7F9F5C7A2C3C@kernel.crashing.org>

On Thu, May 11, 2006 at 01:17:50AM +0200, Segher Boessenkool wrote:
> Would an asm clobber of GPR13 in the schedule routines (or a wrapper
> for them, or whatever) work?

No.  The address is cse'd symbolically long before the r13
reference is exposed.


r~

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Alan Modra @ 2006-05-11  1:04 UTC (permalink / raw)
  To: Paul Mackerras
  Cc: linux-arch, linuxppc-dev, rth, David S. Miller, linux-kernel
In-Reply-To: <17506.21908.857189.645889@cargo.ozlabs.ibm.com>

On Thu, May 11, 2006 at 07:05:24AM +1000, Paul Mackerras wrote:
> No, Richard has a point, it's not the value that is the concern, it's
> the address, which gcc could assume is still valid after a barrier.
> Drat.

That may never happen, at least with a compiler that knows how to
optimise away the addi.  You're using -mtls-size=16 so all your accesses
should look like

	lwz rn,per_cpu_var@tprel(13)

gcc shouldn't think there is any reason to cache the address.

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Paul Mackerras @ 2006-05-11  1:21 UTC (permalink / raw)
  To: Alan Modra; +Cc: linux-arch, linuxppc-dev, rth, David S. Miller, linux-kernel
In-Reply-To: <20060511010438.GE24458@bubble.grove.modra.org>

Alan Modra writes:

> gcc shouldn't think there is any reason to cache the address.

Can I rely on that being true in the future?

Paul.

^ permalink raw reply

* Re: [RFC/PATCH] Make powerpc64 use __thread for per-cpu variables
From: Alan Modra @ 2006-05-11  2:01 UTC (permalink / raw)
  To: Paul Mackerras
  Cc: linux-arch, linuxppc-dev, rth, David S. Miller, linux-kernel
In-Reply-To: <17506.37259.755099.974824@cargo.ozlabs.ibm.com>

On Thu, May 11, 2006 at 11:21:15AM +1000, Paul Mackerras wrote:
> Alan Modra writes:
> 
> > gcc shouldn't think there is any reason to cache the address.
> 
> Can I rely on that being true in the future?

It isn't true in the *present*, except with a compiler on my home
machine.  :-) 

__thread int i1;
void
f3 (void)
{
  int x = i1;
  __asm__ __volatile__ ("#dragons be here.  %0" : "+r" (x));
  i1 = x;
}

current mainline with -O2 -S -mtls-size=16

f3:
        addi 9,2,i1@tprel
        lwz 0,0(9)
#APP
        #dragons be here.  0
#NO_APP
        stw 0,0(9)
        blr

Same thing with my modified compiler.

f3:
        lwz 0,i1@tprel(2)
#APP
        #dragons be here.  0
#NO_APP
        stw 0,i1@tprel(2)
        blr

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply

* mpc8270 : i2c support
From: Heiko Schocher @ 2006-05-11  5:12 UTC (permalink / raw)
  To: linuxppc-embedded

Hello,

on Thu May 4 06:11:17 2006 i wrote:
> Hmmm.... I think there are differences in the memory map between
> MPC826x and MPC827x ... can you try following Hack in
> include/asm-ppc/cpm_8260.h?
> 
> -#define PROFF_I2C       ((16 * 1024) - 64)
> +#define PROFF_I2C       ((8 * 1024) - 64)
> 
> [If it solves the problem, we must do this on a better way ;-)]

I don t know, if this was necessary to solve the problem, but i think
the follwing patch is useful, but I have no Hardware to try it out.

Any comments?

Best regards
Heiko

diff --git a/include/asm-ppc/cpm2.h b/include/asm-ppc/cpm2.h
index 6197986..738259c 100644
--- a/include/asm-ppc/cpm2.h
+++ b/include/asm-ppc/cpm2.h
@@ -181,7 +181,11 @@ #define PROFF_IDMA4_BASE	((uint)0x8afe)
  */
 #define PROFF_SMC1	(0)
 #define PROFF_SMC2	(64)
+#if defined(CONFIG_8272) || defined(CONFIG_MPC8555)
+#define PROFF_I2C	((8 * 1024) - 64)
+#else
 #define PROFF_I2C	((16 * 1024) - 64)
+#endif
 
 /* Define enough so I can at least use the serial port as a UART.
  */

^ permalink raw reply related

* Re: Alubook 5,8: No sound with 2.6.17-rc3-g5528e568-dirty
From: Johannes Berg @ 2006-05-11 11:58 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list
In-Reply-To: <1147304824.32448.82.camel@localhost.localdomain>

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

On Thu, 2006-05-11 at 09:47 +1000, Benjamin Herrenschmidt wrote:

> Same as mine, it works fine with snd-aoa

I should build a database with a machine string -> user* mapping... This
means that even my brother has this one and it works great with snd-aoa
as far as I know :)

Though, since the alsa userspace libs absolutely *SUCK* no program I
know of can properly represent the mixer controls except maybe amixer...
I'd hate to work around this in the kernel, but the alsa libs are such
complex beasts that I have a feeling they won't be fixed.

Anyone up to inventing a new mixer API that uses sysfs? ;)

johannes

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

^ permalink raw reply

* Re: Alubook 5,8: No sound with 2.6.17-rc3-g5528e568-dirty
From: Benjamin Herrenschmidt @ 2006-05-11 12:04 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev list
In-Reply-To: <1147348702.9630.7.camel@johannes>

On Thu, 2006-05-11 at 13:58 +0200, Johannes Berg wrote:
> On Thu, 2006-05-11 at 09:47 +1000, Benjamin Herrenschmidt wrote:
> 
> > Same as mine, it works fine with snd-aoa
> 
> I should build a database with a machine string -> user* mapping... This
> means that even my brother has this one and it works great with snd-aoa
> as far as I know :)
> 
> Though, since the alsa userspace libs absolutely *SUCK* no program I
> know of can properly represent the mixer controls except maybe amixer...
> I'd hate to work around this in the kernel, but the alsa libs are such
> complex beasts that I have a feeling they won't be fixed.

kmix looks approx. ok

> Anyone up to inventing a new mixer API that uses sysfs? ;)
> 
> johannes

^ permalink raw reply

* Help: Linux porting to custom target hw
From: Thiago Galesi @ 2006-05-11 12:02 UTC (permalink / raw)
  To: Jayanta Das; +Cc: linuxppc-embedded
In-Reply-To: <8584FDC94AFF7640B17B8A89B23B19B331C626@sbsserver.AlphionCorp.local>

Ok

What is the specific problem you are having?? Based on what you said
all went well, didn't it?

Basically, I think the main thing you have to configure there is flash
location (in the kernel), to be able to use it via MTD.

Booting from NFS or Flash is pretty much effortless, configurable via cmdli=
ne.

Be more specific, so we can help you more...

Thiago

On 5/10/06, Jayanta Das <JDas@alphion.com> wrote:
>
>
> Hello:
>
> Can someone point me in the right direction for some good documentation o=
n
> the above topic. Following is what I so far have done and what I need to =
do.
>
> 1. Set up host environment based on Fedora Core 4
> 2. Downloaded 'ppc4xx' tool chain, ELDK and kernel
> 3. Built U-Boot and uImage
> 4. Flashed U-Boot on the 2nd flash on the Ocotea board (AMCC440GX PowerPC=
)
> 5. Changed the environment variable for NFS mounted root fs and other MAC
> and IP addresses as needed
> 6. TFTP uImage at 400000
> 7. bootm and kernel boots all right with root fs mounted on the host
>
> I am expecting my hardware based on 440GX end of the month. I told the HW
> engineer to follow the peripheral i/f as much possible close to the ref.
> design. We are putting 32MB of Flash and 256MB of RAM.
>
> I need some guidance so that I can port U-Boot and the kernel (minimal
> changes) so that I can bring up the board initially with root fs NFS mint=
ed
> and later on the RAMDISK.
>
> Appreciate the help.
>
> Regards.
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
>

^ permalink raw reply

* RE: Help Needed: input overrun(s)
From: s.maiti @ 2006-05-11 12:56 UTC (permalink / raw)
  To: Rune Torgersen; +Cc: linuxppc-embedded
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B0189DD9F@ismail.innsys.innovsys.com>

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

Thanks very much for your reply. It's seems you have already developed the 
MCC driver. Are you using channels 32 to 96? Have you made any changes in 
the dp ram allocation for uart or ethernet driver?
Please help me...

Thanks and regards,
Souvik Maiti
Tata Consultancy Services Limited
Mailto: s.maiti@tcs.com
Website: http://www.tcs.com



"Rune Torgersen" <runet@innovsys.com> 
05/10/2006 08:45 PM

To
"Stevan Ignjatovic" <stevan@iritel.com>, <s.maiti@tcs.com>
cc
<linuxppc-embedded@ozlabs.org>
Subject
RE: Help Needed: input overrun(s)






> -----Original Message-----
> From: Stevan Ignjatovic
> Hello,
> 
> > console we are receiving a print "ttyS: 1 input overrun(s)" 
> along with 
> > other prints of the driver and resulting in scrambled output. 
> > Can anyone suggest why this is happening? Is the driver 
> affecting the uart 
> > driver?
> 
> As far as UART driver is concerned, it could be affected if 
> you did not
> carefully allocate some resources. Are you using MCC1 or MCC2? Channel
> specific parameters of MCC1 (channels 0-127) are at the 
> beginning of the
> DPRAM (channel CH_NUM at address 64*CH_NUM). UART driver (as well as
> ethernet driver) allocates its buffer descriptors with
> m8260_cpm_dpalloc. I think that allocating with this function 
> starts at
> the beginning of the DPRAM, so you might have overwritten 
> UART's buffer
> descriptors. So, use MCC2 if you can. If you use some BRGs in your MCC
> driver you should check that as well. 

We have a MCC driver that we see the same happening on. It only occurs
under heavy load (top sows us usig 20-40 % cpu in interrupt).
I am pretty confident that we are not overwriting uart DPRAM. (MCC1 only
uses the middle 64 channels, skipping over the first 32 where the
conflict witht he UARTS are)

I have not seen it corrupt anything, so we have more or less ignored it.
Our theory is that it happens when the CPM gets overloaded.
It would be nice to actually fix it though....

ForwardSourceID:NT00006D1A 
=====-----=====-----=====
Notice: The information contained in this e-mail
message and/or attachments to it may contain 
confidential or privileged information. If you are 
not the intended recipient, any dissemination, use, 
review, distribution, printing or copying of the 
information contained in this e-mail message 
and/or attachments to it are strictly prohibited. If 
you have received this communication in error, 
please notify us by reply e-mail or telephone and 
immediately and permanently delete the message 
and any attachments. Thank you



[-- Attachment #2: Type: text/html, Size: 3722 bytes --]

^ permalink raw reply

* MPC8641(D) software status
From: Sam Song @ 2006-05-11 13:53 UTC (permalink / raw)
  To: linuxppc-embedded

Dear all,

Could anyone kindly disclose any info on
its boot code and kernel support?

Mass production is under way if I am not 
wrong but less info on software. Just out of
interest to know that... Maybe I could have 
the luck to touch it.

Thanks in advance,

Sam


	

	
		
___________________________________________________________ 
雅虎免费邮箱-3.5G容量,20M附件
http://cn.mail.yahoo.com/

^ permalink raw reply

* Re: MPC8641(D) software status
From: Xianghua Xiao @ 2006-05-11 14:08 UTC (permalink / raw)
  To: Sam Song; +Cc: linuxppc-embedded
In-Reply-To: <20060511135328.25523.qmail@web15706.mail.cnb.yahoo.com>

Sam,

We have patches for 8641D against 2.6.15/2.6.16 and it works well in lab
in the last three months along with u-boot code. I think a patch against
2.6.17 or later will be released in the future. However if you need the
2.6.15/2.6.16 patch now, you can contact Freescale directly. In fact,
for all 8641D boards we will ship, we will include the kernel/u-boot
source code on the SATA hard drive directly as well, or you can also
download them from freescale's website.

Hope this helps,
Xianghua


Sam Song wrote:

>Dear all,
>
>Could anyone kindly disclose any info on
>its boot code and kernel support?
>
>Mass production is under way if I am not 
>wrong but less info on software. Just out of
>interest to know that... Maybe I could have 
>the luck to touch it.
>
>Thanks in advance,
>
>Sam
>
>
>	
>
>	
>		
>___________________________________________________________ 
>雅虎免费邮箱-3.5G容量,20M附件
>http://cn.mail.yahoo.com/
>_______________________________________________
>Linuxppc-embedded mailing list
>Linuxppc-embedded@ozlabs.org
>https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>

^ permalink raw reply

* RE: Help Needed: input overrun(s)
From: Rune Torgersen @ 2006-05-11 14:14 UTC (permalink / raw)
  To: s.maiti; +Cc: linuxppc-embedded

> From: s.maiti@tcs.com [mailto:s.maiti@tcs.com]=20
> Thanks very much for your reply. It's seems you have already=20
> developed the MCC driver. Are you using channels 32 to 96?=20
> Have you made any changes in the dp ram allocation for uart=20
> or ethernet driver?=20
> Please help me...=20

We are using every other channel from 32 to 96 on both MCC's as SS7
receivers.
We did not have to do any relocations for UART or ethernet.=20
Ethernet uses upper DPRAM (above 0x8000) and uarts use the first 128
bytes.

We statically allocate DPRAM with cpm_alloc_fixed. MCC1 use 0x0800 to
0x17ff
MCC2 use 0x2800 to 0x37ff. Extra param RAM is allocated with cpm_alloc
and is allocated as 256*8 bytes, and shared between MCC1 and 2.

All BD and interrupt tables are in main memory.

^ permalink raw reply

* Tests to enable Dcache on MPC8270ZUQLDA PPC board restart the system
From: Om Vadlapatla @ 2006-05-11 16:32 UTC (permalink / raw)
  To: linuxppc-embedded@ozlabs.org, U-Boot-Users@lists.sourceforge.net

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

Hello,

Processor:                           MPC8270
Debugger:                            Abatron BDI 2000
Board & processor Initialization by: Uboot 1.1.2

Aim: 
I try to enable d-cache at the Register level with out
having any exceptions. I want to write my own code to
compile into the kernel that is no longer supported by
Montavista (3.0).

Proceedure:
~in window 1 (DIP window) I reset the processor then
the flash mem loads the U-boot version 1.1.2. I do not
load the OS so the system is running at the U-boot
prompt.

=>

~In window 2 (BDI debugger window) I use the Abarton
BDI to force the PPC to enter debug mode by issuing -
"halt" instruction.

MPC8270>halt
 Target CPU        : MPC8280/MGT5200 (Zeppo)
    Target state      : debug mode
    Debug entry cause : COP halt
    Current PC        : 0x0ffe935c
    Current CR        : 0x44004044
    Current MSR       : 0x0000b002
    Current LR        : 0x0ffe13a8

~now by issueing commands from the BDI I try to set
the BATs and the MMU as follows:

I tried two BAT schemes on the abatron that are
attached in BAT register setting table.do &
8280_InitMMU.cmm :

Test 1:- 

For seting DBAT regs by BDI commands ccording to
(BAT_register_setting_table.doc):

// initialize bats  
MPC8270>rm dbat0u 0xffe0003f
MPC8270>rm dbat0l 0xffe00022
MPC8270>rm dbat1u 0x00001fff
MPC8270>rm dbat1l 0x00000002
MPC8270>rm dbat2u 0x300007ff
MPC8270>rm dbat2l 0x30000002
MPC8270>rm dbat3u 0x400003FF
MPC8270>rm dbat3l 0x40000022
MPC8270>rm dbat4u 0xFB0001FF
MPC8270>rm dbat4l 0xFB000022
MPC8270>rm dbat5u 0xFE400003
MPC8270>rm dbat5l 0xFE400022
MPC8270>rm dbat6u 0xF0000003
MPC8270>rm dbat6l 0xF0000022
MPC8270>rm dbat7u 0xFF000003
MPC8270>rm dbat7l 0xFF000022

MPC8270>rm hid0 0x8000c088 // set HID0 to enable 
                           // I & D Cache

MPC8270>go // to let the processor run

I check the PC and it is at 0x200 the Machine check
exception.

Test 2:-
commands I issued throught Abatron BDI window:

// initialization of BATs reffre to (8280_InitMMU.cmm)
// please keep in mind that even though these BAT 
// initialization are for a Stand alone systems I only

// plan to test if I am able to initialize the data 
// cache without the 0x200 (Machine check exception) 
// exception.

MPC8270>rm ibat0u 0x000003fe
MPC8270>rm ibat0l 0x00000002
MPC8270>rm ibat1u 0x04700002
MPC8270>rm ibat1l 0x04700022
MPC8270>rm ibat3u 0xff0000fe
MPC8270>rm ibat3l 0xff000001
MPC8270>rm dbat0u 0x000007fe 
MPC8270>rm dbat0l 0x00000002
MPC8270>rm dbat1u 0x0400007e
MPC8270>rm dbat1l 0x0400002a
MPC8270>rm dbat2u 0x0450007e
MPC8270>rm dbat2l 0x0450002a
MPC8270>rm dbat3u 0xff0000fe
MPC8270>rm dbat3l 0xff000022
// the Bats initialize fine no problem till here 

MPC8270>rm msr 0x9030 // enable MMU (EE + ME + DR +
IR)

// I feel I may be messing it up here (can some one
// correct me?)

MPC8270>go // this is to let the processor run 
           // however ends up restarting the system 

// I dont issue the foll command coz of reset
MPC8270>rm hid0 0x8000c088  // this is to set and
                            // enable the I & D Caches
                            
This is how the DIP window where the boot prompt is
looks after this test 2:-
-------------------------------------------------------
U-Boot 1.1.2 (Jan 27 2006 - 14:27:57) ### Release
1.1.5 ### 

MPC8260 Reset Status: Bus Monitor, External Soft,
External Hard

MPC8260 Clock Configuration
 - Bus-to-Core Mult 4x, VCO Div 2, 60x Bus Freq  25-75
, Core Freq 100-300
 - dfbrg 1, corecnf 0x1a, busdf 5, cpmdf 1, plldf 0,
pllmf 5
 - vco_out  400000002, scc_clk  100000000, brg_clk  
25000000
 - cpu_clk  266666668, cpm_clk  200000001, bus_clk  
66666667

CPU:   MPC8260 (HiP7 Rev 14, Mask 1.0 1K49M) at
266.666 MHz
Board: Fujitsu FW4060
I2C:   ready
DRAM:  256 MB
FLASH:  2 MB
In:    serial
Out:   serial
Err:   serial
Net:   FCC2 ETHERNET
IDE:   Bus 0: OK 
  Device 0: Model: Hitachi XXM2.3.0 Firm: Rev 3.00
Ser#: X0405 20050304185152
            Type: Removable Hard Disk
            Capacity: 61.1 MB = 0.0 GB (125184 x 512)
Hit any key to stop autoboot:  0 
=>Bad trap at PC: fffffffc, SR: 1000, vector=800
NIP: FFFFFFFC XER: 20000000 LR: 00001088 REGS:
0ffa7dc0 TRAP: 0800 DAR: 0FFE55FC
MSR: 00001000 EE: 0 PR: 0 FP: 0 ME: 1 IR/DR: 00

GPR00: 0000A000 0FFA7EB0 00000004 00000000 0FFF0E80
0000000A FFFFFFFD FFFFFFFF 
GPR08: 0FFA7C18 F0000080 00008000 F0000090 00000000
0403FF80 0FFF6000 101C8000 
GPR16: 00000000 00000000 00000000 0100FFE0 00000000
00000001 00000000 00000000 
GPR24: 00000000 FFFFFFFF 00000001 00000003 0FFFEFC8
0FFA7F64 0FFF74AC 0FFF0E80 
Call backtrace: 
Exception in kernel pc fffffffc signal 0

U-Boÿ

U-Boot 1.1.2 (Jan 27 2006 - 14:27:57) ### Release
1.1.5 ### 

MPC8260 Reset Status: External Soft, External Hard

MPC8260 Clock Configuration
 - Bus-to-Core Mult 4x, VCO Div 2, 60x Bus Freq  25-75
, Core Freq 100-300
 - dfbrg 1, corecnf 0x1a, busdf 5, cpmdf 1, plldf 0,
pllmf 5
 - vco_out  400000002, scc_clk  100000000, brg_clk  
25000000
 - cpu_clk  266666668, cpm_clk  200000001, bus_clk  
66666667

CPU:   MPC8260 (HiP7 Rev 14, Mask 1.0 1K49M) at
266.666 MHz
Board: Fujitsu FW4060
I2C:   ready
DRAM:  256 MB
FLASH:  2 MB
In:    serial
Out:   serial
Err:   serial
Net:   FCC2 ETHERNET
IDE:   Bus 0: OK 
  Device 0: Model: Hitachi XXM2.3.0 Firm: Rev 3.00
Ser#: X0405 20050304185152
            Type: Removable Hard Disk
            Capacity: 61.1 MB = 0.0 GB (125184 x 512)
Hit any key to stop autoboot:  0 
=> Bad trap at PC: fffffffc, SR: 1000, vector=800
NIP: FFFFFFFC XER: 00000000 LR: 00001088 REGS:
0ffa7dc0 TRAP: 0800 DAR: 0FFE55FC
MSR: 00001000 EE: 0 PR: 0 FP: 0 ME: 1 IR/DR: 00

GPR00: 0000A000 0FFA7EB0 00000004 00000000 0FFF0E80
0000000A FFFFFFFD 00000000 
GPR08: 00000002 F0000080 00008000 F0000090 00000000
0403FF80 0FFF6000 101C8000 
GPR16: 00000000 00000000 00000000 0100FFE0 00003002
00000001 00000000 0FFCB098 
GPR24: 0FFCE410 00000001 00000001 00000003 0FFFEFC8
0FFA7F64 0FFF74AC 0FFF0E80 
Call backtrace: 
Exception in kernel pc fffffffc signal 0
-------------------------------------------------------

Have I wrongly inilialized the MSR?
Please post comments and suggestions of how I can
initialized MMU for d-cache performance. I am very new
to this.

Thanky you,
Best regards,

Om Vadlapatla

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 

[-- Attachment #2: 790078158-8280_InitMMU.cmm --]
[-- Type: application/octet-stream, Size: 1775 bytes --]

; ********************************
; Initialize BATs
; ********************************

INIT_MMU:

; *** Invalidate TLBs

d.a 0x10000	addi	r2,0,32
d.a 0x10004	mtctr	r2		; Load CTR with 32.
d.a 0x10008	addi	r3,0,0	; Use r3 as the tlb index
d.a 0x1000C	tlbie	r3                ; invalidate the tlb entry
d.a 0x10010	sync
d.a 0x10014	addi	r3,r3,0x1000      ; increment the index
d.a 0x10018	bdnz	0x1000C
d.a 0x1001C	b	0x1001C

r.s IP 0x10000
go
wait 1ms
break


; *** Clear all Upper BATs
d.s SPR:0x218 %l 0	; DBAT0U
d.s SPR:0x21A %l 0	; DBAT1U
d.s SPR:0x21C %l 0	; DBAT2U
d.s SPR:0x21E %l 0	; DBAT3U
d.s SPR:0x238 %l 0	; DBAT4U
d.s SPR:0x23A %l 0	; DBAT5U
d.s SPR:0x23C %l 0	; DBAT6U
d.s SPR:0x23E %l 0	; DBAT7U

d.s SPR:0x210 %l 0	; IBAT0U
d.s SPR:0x212 %l 0	; IBAT1U
d.s SPR:0x214 %l 0	; IBAT2U
d.s SPR:0x216 %l 0	; IBAT3U
d.s SPR:0x230 %l 0	; IBAT4U
d.s SPR:0x232 %l 0	; IBAT5U
d.s SPR:0x234 %l 0	; IBAT6U
d.s SPR:0x236 %l 0	; IBAT7U

; 60-x SDRAM IBAT
d.s SPR:0x210 %l 0x000003FE 	; IBAT0U 32MB
d.s SPR:0x211 %l 0x00000002 	; IBAT0L R/W

; IMMR IBAT
d.s SPR:0x212 %l 0x04700002	; IBAT1U 128KB
d.s SPR:0x213 %l 0x04700022	; IBAT0L I R/W

; Flash IBAT
d.s SPR:0x216 %l 0xFF0000FE	; IBAT3U 8MB
d.s SPR:0x217 %l 0xFF000001	; IBAT3L R/O

; 60-x SDRAM DBAT
d.s SPR:0x218 %l 0x000007FE	; DBAT0U 16MB
d.s SPR:0x219 %l 0x00000002	; DBAT0L R/W

; Local SDRAM DBAT
d.s SPR:0x21A %l 0x0400007E	; DBAT1U
d.s SPR:0x21B %l 0x0400002A	; DBAT1L I,G R/W

; BCSR DBAT
d.s SPR:0x21C %l 0x0450007E	; DBAT2U BCSR + IMMR space
d.s SPR:0x21D %l 0x0450002A	; DBAT2L I,G R/W

; Flash DBAT
d.s SPR:0x21E %l 0xFF0000FE	; DBAT3U 8MB
d.s SPR:0x21F %l 0xFF000022	; DBAT3L I R/W

; Enable MMU
;r.s MSR  0x9030   ; EE + ME + DR + IR

[-- Attachment #3: 1927371312-BAT register setting table.doc --]
[-- Type: application/msword, Size: 28672 bytes --]

^ permalink raw reply

* [PATCH 1/2] Support AMCC Taihu 405EP Eval Board
From: John Otken @ 2006-05-11 18:25 UTC (permalink / raw)
  To: linuxppc-embedded

This patch adds support for the AMCC Taihu 405EP
evaluation board.

I tested it against the latest Denx git tree
(2.6.17-rc3).

The defconfig file follows.

Comments are welcome.

Signed-off-by: John Otken <jotken@softadvances.com>


  arch/ppc/platforms/4xx/Kconfig    |   15
  arch/ppc/platforms/4xx/Makefile   |    1
  arch/ppc/platforms/4xx/taihu.c    |  260 +++++
  arch/ppc/platforms/4xx/taihu.h    |  105 ++
  drivers/mtd/maps/Kconfig          |    8
  drivers/mtd/maps/Makefile         |    1
  drivers/mtd/maps/taihu.c          |  155 +++
  drivers/usb/gadget/Kconfig        |   11
  drivers/usb/gadget/Makefile       |    1
  drivers/usb/gadget/epautoconf.c   |   15
  drivers/usb/gadget/gadget_chips.h |    8
  drivers/usb/gadget/pd12_udc.c     | 1821 +++++++++++++++++++++++++++++++++++++
  drivers/usb/gadget/pd12_udc.h     |  148 +++
  include/asm-ppc/ibm4xx.h          |    4
  14 files changed, 2548 insertions(+), 5 deletions(-)
  create mode 100644 arch/ppc/platforms/4xx/taihu.c
  create mode 100644 arch/ppc/platforms/4xx/taihu.h
  create mode 100644 drivers/mtd/maps/taihu.c
  create mode 100755 drivers/usb/gadget/pd12_udc.c
  create mode 100644 drivers/usb/gadget/pd12_udc.h



diff --git a/arch/ppc/platforms/4xx/Kconfig b/arch/ppc/platforms/4xx/Kconfig
index 51414c4..4efba1e 100644
--- a/arch/ppc/platforms/4xx/Kconfig
+++ b/arch/ppc/platforms/4xx/Kconfig
@@ -66,6 +66,12 @@ config XILINX_ML403
  	bool "Xilinx-ML403"
  	help
  	  This option enables support for the Xilinx ML403 evaluation board.
+
+config TAIHU
+	bool "Taihu"
+	select WANT_EARLY_SERIAL
+	help
+	  This option enables support for the AMCC 405EP evaluation board.
  endchoice

  choice
@@ -120,7 +126,6 @@ config YOSEMITE
         select WANT_EARLY_SERIAL
         help
           This option enables support for the AMCC PPC440EP evaluation board.
-
  endchoice

  config EP405PC
@@ -201,7 +206,7 @@ config BOOKE

  config IBM_OCP
  	bool
-	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || YUCCA || OCOTEA || P3P440 || PPChameleonEVB || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT || YELLOWSTONE || YOSEMITE
+	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || YUCCA || OCOTEA || P3P440 || PPChameleonEVB || REDWOOD_5 || REDWOOD_6 || SYCAMORE || TAIHU || WALNUT || YELLOWSTONE || YOSEMITE
  	default y

  config IBM_EMAC4
@@ -211,7 +216,7 @@ config IBM_EMAC4

  config BIOS_FIXUP
  	bool
-	depends on BUBINGA || EP405 || SYCAMORE || WALNUT
+	depends on BUBINGA || EP405 || SYCAMORE || TAIHU || WALNUT
  	default y

  # OAK doesn't exist but wanted to keep this around for any future 403GCX boards
@@ -222,7 +227,7 @@ config 403GCX

  config 405EP
  	bool
-	depends on BUBINGA || PPChameleonEVB
+	depends on BUBINGA || PPChameleonEVB || TAIHU
  	default y

  config 405GP
@@ -262,7 +267,7 @@ config EMBEDDEDBOOT

  config IBM_OPENBIOS
  	bool
-	depends on ASH || REDWOOD_5 || REDWOOD_6
+	depends on ASH || REDWOOD_5 || REDWOOD_6 || TAIHU
  	default y

  config PPC4xx_DMA
diff --git a/arch/ppc/platforms/4xx/Makefile b/arch/ppc/platforms/4xx/Makefile
index d3a7a16..86374ff 100644
--- a/arch/ppc/platforms/4xx/Makefile
+++ b/arch/ppc/platforms/4xx/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PPChameleonEVB)	+= ppchamel
  obj-$(CONFIG_REDWOOD_5)		+= redwood5.o
  obj-$(CONFIG_REDWOOD_6)		+= redwood6.o
  obj-$(CONFIG_SYCAMORE)		+= sycamore.o
+obj-$(CONFIG_TAIHU)		+= taihu.o
  obj-$(CONFIG_WALNUT)		+= walnut.o
  obj-$(CONFIG_XILINX_ML300)	+= xilinx_ml300.o
  obj-$(CONFIG_XILINX_ML403)	+= xilinx_ml403.o
diff --git a/arch/ppc/platforms/4xx/taihu.c b/arch/ppc/platforms/4xx/taihu.c
new file mode 100644
index 0000000..94bd72d
--- /dev/null
+++ b/arch/ppc/platforms/4xx/taihu.c
@@ -0,0 +1,260 @@
+/*
+ * Support for IBM PPC 405EP evaluation board (Taihu).
+ *
+ * Author: SAW (IBM), derived from walnut.c.
+ *         Maintained by MontaVista Software <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Softare Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/rtc.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include <asm/system.h>
+#include <asm/pci-bridge.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+#include <asm/page.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/todc.h>
+#include <asm/kgdb.h>
+#include <asm/ocp.h>
+#include <asm/ibm_ocp_pci.h>
+
+#include <platforms/4xx/ibm405ep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+extern bd_t __res;
+
+
+/* Some IRQs unique to the board
+ * Used by the generic 405 PCI setup functions in ppc4xx_pci.c
+ */
+int __init
+ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+	static char pci_irq_table[][4] =
+	    /*
+	     *      PCI IDSEL/INTPIN->INTLINE
+	     *      A       B       C       D
+	     */
+	{
+		{25, 26, 27, 28},	/* IDSEL 1 - PCI slot 1 */
+		{26, 27, 28, 25},	/* IDSEL 2 - PCI slot 2 */
+	};
+
+	const long min_idsel = 6, max_idsel = 7, irqs_per_slot = 4;
+	return PCI_IRQ_TABLE_LOOKUP;
+};
+
+/* The serial clock for the chip is an internal clock determined by
+ * different clock speeds/dividers.
+ * Calculate the proper input baud rate and setup the serial driver.
+ */
+static void __init
+taihu_early_serial_map(void)
+{
+	u32 uart_div;
+	int uart_clock;
+	struct uart_port port;
+
+         /* Calculate the serial clock input frequency
+          *
+          * The base baud is the PLL OUTA (provided in the board info
+          * structure) divided by the external UART Divisor, divided
+          * by 16.
+          */
+	uart_div = (mfdcr(DCRN_CPC0_UCR_BASE) & DCRN_CPC0_UCR_U0DIV);
+	uart_clock = __res.bi_pllouta_freq / uart_div;
+
+	/* Setup serial port access */
+	memset(&port, 0, sizeof(port));
+	port.membase = (void*)ACTING_UART0_IO_BASE;
+	port.irq = ACTING_UART0_INT;
+	port.uartclk = uart_clock;
+	port.regshift = 0;
+	port.iotype = SERIAL_IO_MEM;
+	port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
+	port.line = 0;
+
+	if (early_serial_setup(&port) != 0) {
+		printk("Early serial init of port 0 failed\n");
+	}
+
+	port.membase = (void*)ACTING_UART1_IO_BASE;
+	port.irq = ACTING_UART1_INT;
+	port.line = 1;
+
+	if (early_serial_setup(&port) != 0) {
+		printk("Early serial init of port 1 failed\n");
+	}
+}
+
+void __init
+bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip)
+{
+
+	unsigned int bar_response, bar;
+	/*
+	 * Expected PCI mapping:
+	 *
+	 *  PLB addr             PCI memory addr
+	 *  ---------------------       ---------------------
+	 *  0000'0000 - 7fff'ffff <---  0000'0000 - 7fff'ffff
+	 *  8000'0000 - Bfff'ffff --->  8000'0000 - Bfff'ffff
+	 *
+	 *  PLB addr             PCI io addr
+	 *  ---------------------       ---------------------
+	 *  e800'0000 - e800'ffff --->  0000'0000 - 0001'0000
+	 *
+	 * The following code is simplified by assuming that the bootrom
+	 * has been well behaved in following this mapping.
+	 */
+
+#ifdef DEBUG
+	int i;
+
+	printk("ioremap PCLIO_BASE = 0x%x\n", pcip);
+	printk("PCI bridge regs before fixup \n");
+	for (i = 0; i <= 3; i++) {
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha)));
+	}
+	printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms)));
+	printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la)));
+	printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms)));
+	printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la)));
+
+#endif
+
+	/* added for IBM boot rom version 1.15 bios bar changes  -AK */
+
+	/* Disable region first */
+	out_le32((void *) &(pcip->pmm[0].ma), 0x00000000);
+	/* PLB starting addr, PCI: 0x80000000 */
+	out_le32((void *) &(pcip->pmm[0].la), 0x80000000);
+	/* PCI start addr, 0x80000000 */
+	out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE);
+	/* 512MB range of PLB to PCI */
+	out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000);
+	/* Enable no pre-fetch, enable region */
+	out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff -
+						(PPC405_PCI_UPPER_MEM -
+						 PPC405_PCI_MEM_BASE)) | 0x01));
+
+	/* Disable region one */
+	out_le32((void *) &(pcip->pmm[1].ma), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].la), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].ma), 0x00000000);
+	out_le32((void *) &(pcip->ptm1ms), 0x00000001);
+
+	/* Disable region two */
+	out_le32((void *) &(pcip->pmm[2].ma), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].la), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].ma), 0x00000000);
+	out_le32((void *) &(pcip->ptm2ms), 0x00000000);
+	out_le32((void *) &(pcip->ptm2la), 0x00000000);
+
+	/* Zero config bars */
+	for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) {
+		early_write_config_dword(hose, hose->first_busno,
+					 PCI_FUNC(hose->first_busno), bar,
+					 0x00000000);
+		early_read_config_dword(hose, hose->first_busno,
+					PCI_FUNC(hose->first_busno), bar,
+					&bar_response);
+		DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n",
+		    hose->first_busno, PCI_SLOT(hose->first_busno),
+		    PCI_FUNC(hose->first_busno), bar, bar_response);
+	}
+	/* end work arround */
+
+#ifdef DEBUG
+	printk("PCI bridge regs after fixup \n");
+	for (i = 0; i <= 3; i++) {
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha)));
+	}
+	printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms)));
+	printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la)));
+	printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms)));
+	printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la)));
+
+#endif
+}
+
+static void __init
+taihu_set_emacdata(void)
+{
+	struct ocp_def *def;
+	struct ocp_func_emac_data *emacdata;
+
+	def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 0);
+	emacdata = def->additions;
+	emacdata->phy_map = 0x000fffff;	/* skip 0x00 .. 0x13 */
+}
+
+void __init
+taihu_setup_arch(void)
+{
+	taihu_set_emacdata();
+
+	ppc4xx_setup_arch();
+
+	ibm_ocp_set_emac(0, 1);
+
+        taihu_early_serial_map();
+
+        /* Identify the system */
+        printk("AMCC PowerPC 405EP Taihu Platform\n");
+}
+
+void __init
+taihu_map_io(void)
+{
+	ppc4xx_map_io();
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+	      unsigned long r6, unsigned long r7)
+{
+	ppc4xx_init(r3, r4, r5, r6, r7);
+
+	ppc_md.setup_arch = taihu_setup_arch;
+	ppc_md.setup_io_mappings = taihu_map_io;
+
+#ifdef CONFIG_KGDB
+	ppc_md.early_serial_map = taihu_early_serial_map;
+#endif
+}
+
diff --git a/arch/ppc/platforms/4xx/taihu.h b/arch/ppc/platforms/4xx/taihu.h
new file mode 100644
index 0000000..eb2aa7a
--- /dev/null
+++ b/arch/ppc/platforms/4xx/taihu.h
@@ -0,0 +1,105 @@
+/*
+ * Support for IBM PPC 405EP evaluation board (Taihu).
+ *
+ * Author: SAW (IBM), derived from walnut.h.
+ *         Maintained by MontaVista Software <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Softare Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifdef __KERNEL__
+#ifndef __TAIHU_H__
+#define __TAIHU_H__
+
+/* 405EP */
+#include <platforms/4xx/ibm405ep.h>
+
+#ifndef __ASSEMBLY__
+/*
+ * Data structure defining board information maintained by the boot
+ * ROM on IBM's evaluation board. An effort has been made to
+ * keep the field names consistent with the 8xx 'bd_t' board info
+ * structures.
+ */
+#define CONFIG_HAS_ETH1 1
+typedef struct board_info {
+	unsigned long	bi_memstart;	/* start of DRAM memory */
+	unsigned long	bi_memsize;	/* size	 of DRAM memory in bytes */
+	unsigned long	bi_flashstart;	/* start of FLASH memory */
+	unsigned long	bi_flashsize;	/* size	 of FLASH memory */
+	unsigned long	bi_flashoffset; /* reserved area for startup monitor */
+	unsigned long	bi_sramstart;	/* start of SRAM memory */
+	unsigned long	bi_sramsize;	/* size	 of SRAM memory */
+	unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS) */
+	unsigned long	bi_ip_addr;	/* IP Address */
+	unsigned char	bi_enetaddr[6];	/* Ethernet adress */
+	unsigned short	bi_ethspeed;	/* Ethernet speed in Mbps */
+	unsigned long	bi_intfreq;	/* Internal Freq, in MHz */
+	unsigned long	bi_busfreq;	/* Bus Freq, in MHz */
+	unsigned long	bi_baudrate;	/* Console Baudrate */
+#if defined(CONFIG_405)   || \
+    defined(CONFIG_405GP) || \
+    defined(CONFIG_405CR) || \
+    defined(CONFIG_405EP) || \
+    defined(CONFIG_440)
+	unsigned char	bi_s_version[4];	/* Version of this structure */
+	unsigned char	bi_r_version[32];	/* Version of the ROM (IBM) */
+	unsigned int	bi_pllouta_freq;	/* CPU (Internal) Freq, in Hz */
+	unsigned int	bi_plb_busfreq;	/* PLB Bus speed, in Hz */
+	unsigned int	bi_pci_busfreq;	/* PCI Bus speed, in Hz */
+	unsigned char	bi_pci_enetaddr[6];	/* PCI Ethernet MAC address */
+#endif
+
+#ifdef CONFIG_HAS_ETH1
+	/* second onboard ethernet port */
+	unsigned char   bi_enet1addr[6];
+#endif
+#ifdef CONFIG_HAS_ETH2
+	/* third onboard ethernet port */
+	unsigned char	bi_enet2addr[6];
+#endif
+#ifdef CONFIG_HAS_ETH3
+	unsigned char   bi_enet3addr[6];
+#endif
+
+#if defined(CONFIG_405GP) || defined(CONFIG_405EP) || defined (CONFIG_440GX) || \
+    defined(CONFIG_440EP) || defined(CONFIG_440GR)
+	unsigned int	bi_opbfreq;		/* OPB clock in Hz */
+	int		bi_iic_fast[2];		/* Use fast i2c mode */
+#endif
+#if defined(CONFIG_4xx)
+#if defined(CONFIG_440GX)
+	int 		bi_phynum[4];           /* Determines phy mapping */
+	int 		bi_phymode[4];          /* Determines phy mode */
+#elif defined(CONFIG_405EP) || defined(CONFIG_440)
+	int 		bi_phynum[2];           /* Determines phy mapping */
+	int 		bi_phymode[2];          /* Determines phy mode */
+#else
+	int 		bi_phynum[1];           /* Determines phy mapping */
+	int 		bi_phymode[1];          /* Determines phy mode */
+#endif
+#endif /* defined(CONFIG_4xx) */
+} bd_t;
+
+/* Some 4xx parts use a different timebase frequency from the internal clock.
+*/
+#define bi_tbfreq bi_intfreq
+
+
+/* The UART clock is based off an internal clock -
+ * define BASE_BAUD based on the internal clock and divider(s).
+ * Since BASE_BAUD must be a constant, we will initialize it
+ * using clock/divider values which OpenBIOS initializes
+ * for typical configurations at various CPU speeds.
+ * The base baud is calculated as (FWDA / EXT UART DIV / 16)
+ */
+#define BASE_BAUD      691200
+
+#define PPC4xx_MACHINE_NAME     "AMCC Taihu"
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __TAIHU_H__ */
+#endif /* __KERNEL__ */
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 9848471..41941c7 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -321,6 +321,14 @@ config MTD_ARCTIC
  	  Arctic board. If you have one of these boards and would like to
  	  use the flash chips on it, say 'Y'.

+config MTD_TAIHU
+	tristate "Flash device mapped on AMCC 405EP Taihu"
+	depends on MTD_CFI && PPC32 && 40x && TAIHU
+	help
+	  This enables access routines for the flash chips on the AMCC 405EP
+	  Taihu board. If you have one of these boards and would like to
+	  use the flash chips on it, say 'Y'.
+
  config MTD_WALNUT
  	tristate "Flash device mapped on AMCC 405GP/r/EP Walnut/Sycamore/Bubinga"
  	depends on MTD_JEDECPROBE && (WALNUT || SYCAMORE || BUBINGA)
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index c2cf73a..b2210de 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_OCOTEA)	+= ocotea.o
  obj-$(CONFIG_MTD_BAMBOO)	+= bamboo.o
  obj-$(CONFIG_MTD_BEECH)		+= beech-mtd.o
  obj-$(CONFIG_MTD_ARCTIC)	+= arctic-mtd.o
+obj-$(CONFIG_MTD_TAIHU)         += taihu.o
  obj-$(CONFIG_MTD_WALNUT)        += walnut.o
  obj-$(CONFIG_MTD_YOSEMITE)      += yosemite.o
  obj-$(CONFIG_MTD_H720X)		+= h720x-flash.o
diff --git a/drivers/mtd/maps/taihu.c b/drivers/mtd/maps/taihu.c
new file mode 100644
index 0000000..432e07d
--- /dev/null
+++ b/drivers/mtd/maps/taihu.c
@@ -0,0 +1,155 @@
+/*
+ *
+ * drivers/mtd/maps/taihu.c
+ *
+ * FLASH map for the AMCC Taihu boards.
+ *
+ * 2005 UDTech, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+#define BOOTWINDOW_ADDR 0xffe00000
+#define BOOTWINDOW_SIZE 0x00200000
+
+#define APPWINDOW_ADDR 	0xfc000000
+#define APPWINDOW_SIZE 	0x02000000
+
+
+static struct mtd_partition taihu_bootflash_partitions[] = {
+        {
+		.name = "kozio diags",
+		.offset = 0,
+		.size = 0x001a0000,
+		.mask_flags = MTD_WRITEABLE      /* force read-only */
+	},
+	{
+		.name = "u-boot env",
+		.offset = 0x001a0000,
+		.size = 0x00020000
+	},
+	{
+		.name = "u-boot",
+		.offset = 0x001c0000,
+		.size = 0x00040000,
+		.mask_flags = MTD_WRITEABLE     /* force read-only */
+	}
+};
+
+struct map_info taihu_bootflash_map = {
+	.name = "AMCC Taihu Boot Flash",
+	.size = BOOTWINDOW_SIZE,
+	.bankwidth = 2,
+	.phys = BOOTWINDOW_ADDR,
+};
+
+static struct mtd_partition taihu_appflash_partitions[] = {
+	{
+		.name = "kernel",
+		.offset = 0,
+		.size = 0x00300000,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	},
+	{
+		.name = "initrd",
+		.offset = 0x00300000,
+		.size = 0x01a00000,
+		.mask_flags = MTD_WRITEABLE    /* force read-only */
+	},
+	{
+		.name = "jffs2",
+		.offset = 0x01D00000,
+		.size = 0x00300000
+	}
+};
+
+
+struct map_info taihu_appflash_map = {
+	.name = "AMCC Taihu Application Flash",
+	.size = APPWINDOW_SIZE,
+	.bankwidth = 2,
+	.phys = APPWINDOW_ADDR,
+};
+
+
+#define NUM_TAIHU_FLASH_PARTITIONS(parts) \
+	(sizeof(parts)/sizeof(parts[0]))
+
+static struct mtd_info *taihu_mtd;
+
+int __init init_taihu_flash(void)
+{
+
+	printk(KERN_NOTICE "taihu: bootflash mapping: %x at %x\n",
+			BOOTWINDOW_SIZE, BOOTWINDOW_ADDR);
+	taihu_bootflash_map.virt = ioremap(BOOTWINDOW_ADDR, BOOTWINDOW_SIZE);
+	if (!taihu_bootflash_map.virt) {
+		printk("init_taihu_flash: failed to ioremap for bootflash\n");
+		return -EIO;
+	}
+	simple_map_init(&taihu_bootflash_map);
+	taihu_mtd = do_map_probe("cfi_probe", &taihu_bootflash_map);
+	if (taihu_mtd) {
+		taihu_mtd->owner = THIS_MODULE;
+		add_mtd_partitions(taihu_mtd,
+				taihu_bootflash_partitions,
+				ARRAY_SIZE(taihu_bootflash_partitions));
+	} else {
+		printk("map probe failed (bootflash)\n");
+		return -ENXIO;
+	}
+
+	printk(KERN_NOTICE "taihu: appflash mapping: %x at %x\n",
+			APPWINDOW_SIZE, APPWINDOW_ADDR);
+	taihu_appflash_map.virt = ioremap(APPWINDOW_ADDR, APPWINDOW_SIZE);
+	if (!taihu_appflash_map.virt) {
+		printk("init_taihu_flash: failed to ioremap for appflash\n");
+		return -EIO;
+	}
+	simple_map_init(&taihu_appflash_map);
+	taihu_mtd = do_map_probe("cfi_probe", &taihu_appflash_map);
+	if (taihu_mtd) {
+		taihu_mtd->owner = THIS_MODULE;
+		add_mtd_partitions(taihu_mtd,
+				taihu_appflash_partitions,
+				ARRAY_SIZE(taihu_appflash_partitions));
+	} else {
+		printk("map probe failed (appflash)\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_taihu_flash(void)
+{
+	if (taihu_mtd) {
+		del_mtd_partitions(taihu_mtd);
+		/* moved iounmap after map_destroy - armin */
+		map_destroy(taihu_mtd);
+	}
+
+	if (taihu_bootflash_map.virt)
+		iounmap((void *)taihu_bootflash_map.virt);
+	if (taihu_appflash_map.virt)
+		iounmap((void *)taihu_appflash_map.virt);
+}
+
+module_init(init_taihu_flash);
+module_exit(cleanup_taihu_flash);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MTD map driver for the AMCC Taihu board");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 363b2ad..c541e12 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -154,6 +154,17 @@ config USB_LH7A40X
  	default USB_GADGET
  	select USB_GADGET_SELECTED

+config USB_GADGET_PD12
+	boolean "PD12 UDC"
+	depends on TAIHU
+	help
+    This driver provides USB Device Controller driver for PD12 UDC
+
+config USB_PD12
+	tristate
+	depends on USB_GADGET_PD12
+	default USB_GADGET
+	select USB_GADGET_SELECTED

  config USB_GADGET_OMAP
  	boolean "OMAP USB Device Controller"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5a28e61..4422f49 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
  obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
  obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
  obj-$(CONFIG_USB_AT91)		+= at91_udc.o
+obj-$(CONFIG_USB_PD12)	+= pd12_udc.o

  #
  # USB gadget drivers
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index f7c6d75..9c32fa8 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -274,6 +274,21 @@ struct usb_ep * __init usb_ep_autoconfig
  		ep = find_ep (gadget, "ep1-bulk");
  		if (ep && ep_matches (gadget, ep, desc))
  			return ep;
+
+	} else if (gadget_is_pd12 (gadget)) {
+		if (USB_ENDPOINT_XFER_BULK == type
+				&& (USB_DIR_IN & desc->bEndpointAddress)) {
+			/* single buffering is enough */
+			ep = find_ep (gadget, "ep2in-bulk");
+			if (ep && ep_matches (gadget, ep, desc))
+				return ep;
+		} else if (USB_ENDPOINT_XFER_BULK == type
+				&& (USB_DIR_OUT & desc->bEndpointAddress)) {
+			/* DMA may be available */
+			ep = find_ep (gadget, "ep1out-bulk");
+			if (ep && ep_matches (gadget, ep, desc))
+				return ep;
+		}
  	}

  	/* Second, look at endpoints until an unclaimed one looks usable */
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index aa80f09..af3767c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -87,6 +87,12 @@
  #define gadget_is_at91(g)	0
  #endif

+#ifdef CONFIG_USB_GADGET_PD12
+#define gadget_is_pd12(g)	!strcmp("pd12_udc", (g)->name)
+#else
+#define gadget_is_pd12(g)	0
+#endif
+
  #ifdef CONFIG_USB_GADGET_IMX
  #define gadget_is_imx(g)	!strcmp("imx_udc", (g)->name)
  #else
@@ -169,5 +175,7 @@ static inline int usb_gadget_controller_
  		return 0x16;
  	else if (gadget_is_mpc8272(gadget))
  		return 0x17;
+	else if (gadget_is_pd12(gadget))
+		return 0x18;
  	return -ENOENT;
  }
diff --git a/drivers/usb/gadget/pd12_udc.c b/drivers/usb/gadget/pd12_udc.c
new file mode 100755
index 0000000..6c52353
--- /dev/null
+++ b/drivers/usb/gadget/pd12_udc.c
@@ -0,0 +1,1821 @@
+/*
+ * linux/drivers/usb/gadget/pd12_udc.c
+ * Taihu pd12-udc full speed USB device controllers
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "pd12_udc.h"
+
+/*#define DEBUG_PD12 printk*/
+/*#define DEBUG_PD12_EP0 printk*/
+/*#define DEBUG_PD12_SETUP printk*/
+
+#ifndef DEBUG_PD12_EP0
+# define DEBUG_PD12_EP0(fmt,args...)
+#endif
+#ifndef DEBUG_PD12_SETUP
+# define DEBUG_PD12_SETUP(fmt,args...)
+#endif
+#ifndef DEBUG_PD12
+# define NO_STATES
+# define DEBUG_PD12(fmt,args...)
+#endif
+
+#define	DRIVER_DESC			"PD12 USB Device Controller"
+#define	DRIVER_VERSION		__DATE__
+
+
+static const char driver_name[] = "pd12_udc";
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+static void __iomem *th_pd12_virt;
+static void __iomem *th_cpld_virt;
+static u8 first_tran = 1;
+
+#define DEV_CMD_ADDR  ((ulong)th_pd12_virt+1)
+#define DEV_DATA_ADDR (th_pd12_virt)
+
+#define CPLD_REG0_ADDR (th_cpld_virt)
+#define CPLD_REG1_ADDR ((ulong)th_cpld_virt+1)
+/*
+  Local definintions.
+*/
+
+#ifndef NO_STATES
+static char *state_names[] = {
+	"WAIT_FOR_SETUP",
+	"DATA_STATE_XMIT",
+	"DATA_STATE_NEED_ZLP",
+	"WAIT_FOR_OUT_STATUS",
+	"DATA_STATE_RECV"
+};
+#endif
+
+/*
+  Local declarations.
+*/
+static int pd12_ep_enable(struct usb_ep *ep,
+			     const struct usb_endpoint_descriptor *);
+static int pd12_ep_disable(struct usb_ep *ep);
+static struct usb_request *pd12_alloc_request(struct usb_ep *ep, unsigned);
+static void pd12_free_request(struct usb_ep *ep, struct usb_request *);
+static void *pd12_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *,
+				  unsigned);
+static void pd12_free_buffer(struct usb_ep *ep, void *, dma_addr_t,
+				unsigned);
+static int pd12_queue(struct usb_ep *ep, struct usb_request *, unsigned);
+static int pd12_dequeue(struct usb_ep *ep, struct usb_request *);
+static int pd12_set_halt(struct usb_ep *ep, int);
+static void pd12_ep0_kick(struct pd12_udc *dev, struct pd12_ep *ep);
+static void pd12_handle_ep0(struct pd12_udc *dev);
+
+static void done(struct pd12_ep *ep, struct pd12_request *req,
+		 int status);
+static void stop_activity(struct pd12_udc *dev,
+			  struct usb_gadget_driver *driver);
+static void flush(struct pd12_ep *ep);
+static void udc_enable(struct pd12_udc *dev);
+static void udc_set_address(struct pd12_udc *dev, unsigned char address);
+
+static struct usb_ep_ops pd12_ep_ops = {
+	.enable = pd12_ep_enable,
+	.disable = pd12_ep_disable,
+
+	.alloc_request = pd12_alloc_request,
+	.free_request = pd12_free_request,
+
+	.alloc_buffer = pd12_alloc_buffer,
+	.free_buffer = pd12_free_buffer,
+
+	.queue = pd12_queue,
+	.dequeue = pd12_dequeue,
+
+	.set_halt = pd12_set_halt,
+};
+
+
+static __inline__ void read_data(volatile u8 *val)
+{
+	*val = *(volatile u8 *)DEV_DATA_ADDR;
+	udelay(5);
+}
+
+static __inline__ void write_data(u8 val)
+{
+	*(volatile u8 *)DEV_DATA_ADDR = val;
+	udelay(5);
+}
+
+static __inline__ void write_cmd(u8 val)
+{
+	*(volatile u8 *)DEV_CMD_ADDR = val;
+	udelay(5);
+}
+
+static __inline__ void usb_set_index(u8 ep)
+{
+	if(ep != 0)
+		ep += 1;
+	write_cmd(ep);
+}
+
+static void pd12_set_ack(u8 index)
+{
+	
+	write_cmd(index);
+	write_cmd(PD12_ACK_SETUP);
+	if(index  == 0)
+		write_cmd(PD12_CLEAR_BUF);
+		
+}
+
+static int write_fifo(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 *buf;
+	unsigned count;
+	unsigned length;
+	int is_last;
+	u8 ep_sts;
+	
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+	
+	count = ep->ep.maxpacket;	
+	length = req->req.length - req->req.actual;
+	length = min(length, count);
+	req->req.actual += length;
+
+	DEBUG_PD12("Write %d (max %d), fifo %p\n", length, count, buf);
+	write_cmd(1);
+	read_data(&ep_sts);
+/*	write_cmd(PD12_ACK_SETUP); */
+	write_cmd(PD12_WRITE_BUF);
+	write_data(0x0);
+	write_data(length);
+	if(length == 0)
+		write_data(0x0);
+	while(length--)
+		write_data(*buf++);
+	write_cmd(PD12_VALIDATE_BUF);
+	if(length != ep->ep.maxpacket)
+		is_last = 1;
+	else if(req->req.length == req->req.actual
+			&& !req->req.zero)
+		is_last = 1;
+	else
+		is_last = 0;
+
+	if(is_last)
+		done(ep,req,0);
+	return is_last;
+
+}
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_PD12_FILES
+
+static const char proc_node_name[] = "driver/udc";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+	      int *eof, void *_dev)
+{
+	char *buf = page;
+	struct pd12_udc *dev = _dev;
+	char *next = buf;
+	unsigned size = count;
+	unsigned long flags;
+	int t;
+	u8 ep_status[6];
+
+	if (off != 0)
+		return 0;
+
+	local_irq_save(flags);
+
+	/* basic device status */
+	t = scnprintf(next, size,
+		      DRIVER_DESC "\n"
+		      "%s version: %s\n"
+		      "Gadget driver: %s\n"
+		      "Host: %s\n\n",
+		      driver_name, DRIVER_VERSION,
+		      dev->driver ? dev->driver->driver.name : "(none)");
+	size -= t;
+	next += t;
+
+	for(i=0;i<PD12_MAX_ENDPOINTS;i++)
+	{
+		write_cmd(PD12_READ_LAST_STATUS+i);
+		read_data(&ep_status[i]);
+	}
+	t = scnprintf(next, size,
+		      "Endpoints last status:\n"
+			  " ep0: 0x%x, ep1: 0x%x, ep2: 0x%x\n"
+			  " ep3: 0x%x, ep4: 0x%x, ep5: 0x%x\n\n",
+		      ep_status[0], ep_status[1], ep_status[2],
+		      ep_status[3], ep_status[4], ep_status[5]
+	    );
+	size -= t;
+	next += t;
+
+	local_irq_restore(flags);
+	*eof = 1;
+	return count - size;
+}
+
+#define create_proc_files() 	create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() 	remove_proc_entry(proc_node_name, NULL)
+
+#else	/* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+static void pd12_set_mode(u8 val)
+{
+	write_cmd(PD12_SET_MODE);
+	write_data(val);
+	write_data(0x3);/*maybe 0x43*/
+}
+static void pd12_read_int(u16* val)
+{
+	write_cmd(PD12_READ_INT);
+	read_data((u8*)val);
+}
+
+static void pd12_read_lstatus(u8 index, u8* val)
+{
+	write_cmd(PD12_READ_LAST_STATUS + index);
+	read_data(val);
+}
+/*
+ * 	udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pd12_udc *dev)
+{
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, dev);
+
+	udc_set_address(dev, 0);
+	pd12_set_mode(0x06); /*disconnect soft connect pullup resior */
+	
+	dev->ep0state = WAIT_FOR_SETUP;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->usb_address = 0;
+}
+
+
+/*
+ * 	udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pd12_udc *dev)
+{
+	u8 i;
+	u16 tmp;
+	
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, dev);
+
+	udc_set_address(dev, 0);
+	pd12_set_mode(0x06); /*disconnect soft connect pullup resior */
+	mdelay(1500);
+	pd12_set_mode(0x16); /*soft connnect*/
+	pd12_read_int(&tmp);
+	for(i= 0; i< 5; i++)
+		pd12_read_lstatus(i,(u8 *)(&tmp));
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+	dev->ep0state = WAIT_FOR_SETUP;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->usb_address = 0;
+
+	/* basic endpoint records init */
+	for (i = 0; i < PD12_MAX_ENDPOINTS; i++) {
+		struct pd12_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->desc = 0;
+		ep->stopped = 0;
+		INIT_LIST_HEAD(&ep->queue);
+	}
+
+	/* the rest was statically initialized, and is read-only */
+}
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static void udc_enable(struct pd12_udc *dev)
+{
+
+	u8 i;
+	u16 tmp;
+	
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, dev);
+
+	pd12_set_mode(0x06); /*disconnect soft connect pullup resior */
+	mdelay(1500);
+	pd12_set_mode(0x16); /*soft connnect*/
+	pd12_read_int(&tmp);
+	for(i= 0; i< 5; i++)
+		pd12_read_lstatus(i,(u8 *)(&tmp));
+	dev->gadget.speed = USB_SPEED_FULL;
+
+}
+
+/*
+  Register entry point for the peripheral controller driver.
+*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct pd12_udc *dev = the_controller;
+	int retval;
+
+	DEBUG_PD12("%s: %s\n", __FUNCTION__, driver->driver.name);
+	if (!driver
+	    || driver->speed != USB_SPEED_FULL
+	    || !driver->bind
+	    || !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+	/* first hook up the driver ... */
+	dev->driver = driver;
+	dev->gadget.dev.driver = &driver->driver;
+
+	device_add(&dev->gadget.dev);
+	retval = driver->bind(&dev->gadget);
+	if (retval) {
+		printk("%s: bind to driver %s --> error %d\n", dev->gadget.name,
+		       driver->driver.name, retval);
+		device_del(&dev->gadget.dev);
+
+		dev->driver = 0;
+		dev->gadget.dev.driver = 0;
+		return retval;
+	}
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 * NOTE:  this shouldn't power up until later.
+	 */
+	printk("%s: registered gadget driver '%s'\n", dev->gadget.name,
+	       driver->driver.name);
+
+	udc_enable(dev);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/*
+  Unregister entry point for the peripheral controller driver.
+*/
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct pd12_udc *dev = the_controller;
+	unsigned long flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->driver = 0;
+	stop_activity(dev, driver);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	driver->unbind(&dev->gadget);
+	device_del(&dev->gadget.dev);
+
+	udc_disable(dev);
+
+	DEBUG_PD12("unregistered gadget driver '%s'\n", driver->driver.name);
+	return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------*/
+
+/** Read to request from FIFO (max read == bytes in fifo)
+ *  Return:  0 = still running, 1 = completed, negative = errno
+ *  NOTE: INDEX register must be set for EP
+ */
+static int read_fifo(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 count;
+	u8 ep_sts;
+	u8 *buf;
+	unsigned bufferspace, is_short;
+
+	/* make sure there's a packet in the FIFO. */
+	usb_set_index(ep_index(ep));
+	read_data(&ep_sts);
+	if ((ep_sts & UDC_FIFO_UNREADABLE) == UDC_FIFO_UNREADABLE) {
+		DEBUG_PD12("%s: Packet NOT ready!\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+	bufferspace = req->req.length - req->req.actual;
+
+	/* read all bytes from this packet */
+	write_cmd(PD12_READ_BUF);
+	read_data(&count);
+	read_data(&count);
+	req->req.actual += min((unsigned)count, bufferspace);
+
+	is_short = (count < ep->ep.maxpacket);
+	DEBUG_PD12("read %s %02x, %d bytes%s req %p %d/%d\n",
+	      ep->ep.name, ep_sts, count,
+	      is_short ? "/S" : "", req, req->req.actual, req->req.length);
+
+	while (count-- != 0) {
+
+		if (bufferspace == 0) {
+			/* this happens when the driver's buffer
+			 * is smaller than what the host sent.
+			 * discard the extra data.
+			 */
+			if (req->req.status != -EOVERFLOW)
+				printk("%s overflow %d\n", ep->ep.name, count);
+			req->req.status = -EOVERFLOW;
+		} else {
+			read_data(buf++);
+			bufferspace--;
+		}
+	}
+
+	write_cmd(PD12_CLEAR_BUF);
+
+	/* completion */
+	if (is_short || req->req.actual == req->req.length) {
+		done(ep, req, 0);
+
+		return 1;
+	}
+
+	/* finished that packet.  the next one may be waiting... */
+	return 0;
+}
+
+
+/*
+ *	done - retire a request; caller blocked irqs
+ *  INDEX register is preserved to keep same
+ */
+static void done(struct pd12_ep *ep, struct pd12_request *req, int status)
+{
+	unsigned int stopped = ep->stopped;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+	list_del_init(&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		DEBUG_PD12("complete %s req %p stat %d len %u/%u\n",
+		      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->dev->lock);
+	req->req.complete(&ep->ep, &req->req);
+	spin_lock(&ep->dev->lock);
+	ep->stopped = stopped;
+}
+
+
+/*
+ * 	nuke - dequeue ALL requests
+ */
+void nuke(struct pd12_ep *ep, int status)
+{
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+
+	/* Flush FIFO */
+	flush(ep);
+
+	/* called with irqs blocked */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+		done(ep, req, status);
+	}
+
+}
+
+/** Flush EP
+ * NOTE: INDEX register must be set before this call
+ */
+static void flush(struct pd12_ep *ep)
+{
+}
+
+/**
+ *  handle IN interrupt
+ */
+static void pd12_in_epn(struct pd12_udc *dev, u8 ep_idx)
+{
+	u8 ep_sts;
+	struct pd12_ep *ep = &dev->ep[ep_idx];
+	struct pd12_request *req;
+
+	usb_set_index(ep_idx);
+	read_data(&ep_sts);
+	DEBUG_PD12("%s: %d, status %x\n", __FUNCTION__, ep_idx, ep_sts);
+
+	if (ep_sts & UDC_EP_STALL) {
+		DEBUG_PD12("USB_EP_STALL\n");
+		return;
+	}
+
+	if (!ep->desc) {
+		DEBUG_PD12("%s: NO EP DESC\n", __FUNCTION__);
+		return;
+	}
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	DEBUG_PD12("req: %p\n", req);
+
+	if (!req)
+		return;
+
+	write_fifo(ep, req);
+}
+
+/*
+* handle OUT interrupt(recv)
+ */
+
+static void pd12_out_epn(struct pd12_udc *dev, u8 ep_idx)
+{
+	u8 ep_sts;
+	struct pd12_ep *ep = &dev->ep[ep_idx];
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s: %d\n", __FUNCTION__, ep_idx);
+
+	usb_set_index(ep_idx);
+	read_data(&ep_sts);
+	DEBUG_PD12("%s: %d, status %x\n", __FUNCTION__, ep_idx, ep_sts);
+
+	if (ep_sts & UDC_EP_STALL) {
+		DEBUG_PD12("USB_EP_STALL\n");
+		flush(ep);
+		return;
+	}
+
+	if (ep->desc) {
+
+		if (list_empty(&ep->queue))
+			req = 0;
+		else
+			req = list_entry(ep->queue.next,
+					   struct pd12_request,
+					   queue);
+
+		if (!req) {
+			printk("%s: NULL REQ %d\n",
+				   __FUNCTION__, ep_idx);
+			flush(ep);
+		} else {
+			read_fifo(ep, req);
+		}
+
+	} else {
+		/* Throw packet away.. */
+		printk("%s: No descriptor?!?\n", __FUNCTION__);
+		flush(ep);
+	}
+}
+
+static void stop_activity(struct pd12_udc *dev,
+			  struct usb_gadget_driver *driver)
+{
+	int i;
+
+	/* don't disconnect drivers more than once */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = 0;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < PD12_MAX_ENDPOINTS - 1; i++) {
+		struct pd12_ep *ep = &dev->ep[i];
+		ep->stopped = 1;
+
+		usb_set_index(i);
+		write_cmd(PD12_SET_STATUS);
+		write_data(0x1);
+		nuke(ep, -ESHUTDOWN);
+	}
+		
+	write_cmd(1);
+	write_cmd(PD12_SET_STATUS);
+	write_data(0x1);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&dev->lock);
+		driver->disconnect(&dev->gadget);
+		spin_lock(&dev->lock);
+	}
+
+	/* re-init driver-visible data structures */
+	udc_reinit(dev);
+}
+
+/** Handle USB RESET interrupt
+ */
+static void pd12_reset_intr(struct pd12_udc *dev)
+{
+
+	struct pd12_request *req;
+	struct pd12_ep *ep = &dev->ep[0];
+
+	DEBUG_PD12_EP0("%s: \n", __FUNCTION__);
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	if (req){
+		done(ep,req,0);
+	} else {
+		DEBUG_PD12_EP0("%s: NULL REQ\n", __FUNCTION__);
+	}
+
+	udc_set_address(dev, 0);
+	pd12_set_ack(0);
+	pd12_set_ack(1);
+	dev->ep0state = WAIT_FOR_SETUP;
+	first_tran = 1;
+}
+
+/*
+ *	pd12 usb client interrupt handler.
+ */
+static irqreturn_t pd12_udc_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct pd12_udc *dev = _dev;
+	volatile u8 int_status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock,flags);
+
+	DEBUG_PD12("%s (on state %s)\n", __FUNCTION__,
+		  state_names[dev->ep0state]);
+	write_cmd(PD12_READ_INT);
+	read_data(&int_status);
+	if (int_status & (PD12_CNTL_IN | PD12_CNTL_OUT))
+	{
+		if((int_status & PD12_CNTL_OUT) == PD12_CNTL_OUT)
+			dev->ep0state = WAIT_FOR_SETUP;
+		int_status &= ~(PD12_CNTL_IN | PD12_CNTL_OUT);
+		DEBUG_PD12("PD12_EP0 (control)\n");
+		pd12_handle_ep0(dev);
+		
+	}
+	if (int_status & PD12_SUSPEND_CHG)
+	{
+		u8 tmp;
+		tmp = *(volatile char *)CPLD_REG0_ADDR;
+		int_status &= ~PD12_SUSPEND_CHG;
+		if(tmp & USB_SUSPEND)
+		{
+		/*	write_cmd(PD12_SND_RESUME); */
+			if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->resume)
+				dev->driver->resume(&dev->gadget);
+		}
+		else
+		{	
+			if (dev->gadget.speed !=
+				   USB_SPEED_FULL && dev->driver
+				   && dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+		}
+	}
+	if (int_status & PD12_BUS_RST)
+	{
+		int_status &= ~PD12_BUS_RST;
+		pd12_reset_intr(dev);
+	}
+	if (int_status & PD12_EP1_IN)
+	{
+		int_status &= ~PD12_EP1_IN;
+		DEBUG_PD12("PD12_EP1_IN\n");
+		pd12_in_epn(dev, 2);
+	}
+	if (int_status & PD12_MAIN_IN)
+	{
+		int_status &= ~PD12_MAIN_IN;
+		DEBUG_PD12("PD12_MAIN_IN\n");
+		pd12_in_epn(dev, 4);
+	}
+	if (int_status & PD12_EP1_OUT)
+	{
+		int_status &= ~PD12_EP1_OUT;
+		DEBUG_PD12("PD12_EP1_OUT\n");
+		pd12_out_epn(dev, 1);
+	}
+	if (int_status & PD12_MAIN_OUT)
+	{
+		int_status &= ~PD12_MAIN_OUT;
+		DEBUG_PD12("PD12_MAIN_OUT\n");
+		pd12_out_epn(dev, 3);
+	}
+			
+	spin_unlock_irqrestore(&dev->lock,flags);
+	return IRQ_HANDLED;
+}
+
+static int pd12_ep_enable(struct usb_ep *_ep,
+			     const struct usb_endpoint_descriptor *desc)
+{
+	struct pd12_ep *ep;
+	struct pd12_udc *dev;
+	unsigned long flags;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _ep);
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+	    || desc->bDescriptorType != USB_DT_ENDPOINT
+	    || ep->bEndpointAddress != desc->bEndpointAddress
+	    || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
+		DEBUG_PD12("%s, bad ep or descriptor\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	/* xfer types must match, except that interrupt ~= bulk */
+	if (ep->bmAttributes != desc->bmAttributes ) {
+		DEBUG_PD12("%s, %s type mismatch\n", __FUNCTION__, _ep->name);
+		return -EINVAL;
+	}
+
+	/* hardware _could_ do smaller, but driver doesn't */
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+	     && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))
+	    || !desc->wMaxPacketSize) {
+		DEBUG_PD12("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
+		return -ERANGE;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		DEBUG_PD12("%s, bogus device state\n", __FUNCTION__);
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	ep->stopped = 0;
+	ep->desc = desc;
+	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* Reset halt state (does flush) */
+	pd12_set_halt(_ep, 0);
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG_PD12("%s: enabled %s\n", __FUNCTION__, _ep->name);
+	return 0;
+}
+
+/** Disable EP
+ *  NOTE: Sets INDEX register
+ */
+static int pd12_ep_disable(struct usb_ep *_ep)
+{
+	struct pd12_ep *ep;
+	unsigned long flags;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _ep);
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || !ep->desc) {
+		DEBUG_PD12("%s, %s not enabled\n", __FUNCTION__,
+		      _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	usb_set_index(ep_index(ep));
+
+	/* Nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+
+	/* Disable ep  */
+	write_cmd(PD12_SET_EP_EN);
+	write_data(0x0);
+
+	ep->desc = 0;
+	ep->stopped = 1;
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG_PD12("%s: disabled %s\n", __FUNCTION__, _ep->name);
+	return 0;
+}
+
+static struct usb_request *pd12_alloc_request(struct usb_ep *ep,
+						 unsigned gfp_flags)
+{
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+
+	req = kmalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return 0;
+
+	memset(req, 0, sizeof *req);
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void pd12_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+
+	req = container_of(_req, struct pd12_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static void *pd12_alloc_buffer(struct usb_ep *ep, unsigned bytes,
+				  dma_addr_t * dma, unsigned gfp_flags)
+{
+	char *retval;
+
+	DEBUG_PD12("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags);
+
+	retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
+	if (retval)
+		*dma = virt_to_bus(retval);
+	return retval;
+}
+
+static void pd12_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma,
+				unsigned bytes)
+{
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+	kfree(buf);
+}
+
+/** Queue one request
+ *  Kickstart transfer if needed
+ *  NOTE: Sets INDEX register
+ */
+static int pd12_queue(struct usb_ep *_ep, struct usb_request *_req,
+			 unsigned gfp_flags)
+{
+	struct pd12_request *req;
+	struct pd12_ep *ep;
+	struct pd12_udc *dev;
+	unsigned long flags;
+	u8 ep_status;
+
+	DEBUG_PD12("\n\n\n%s, %p\n", __FUNCTION__, _ep);
+
+	req = container_of(_req, struct pd12_request, req);
+	if (!_req || !_req->complete || !_req->buf
+	     || !list_empty(&req->queue)) {
+		DEBUG_PD12("%s, bad params\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || (!ep->desc && (ep->ep.name != ep0name))) {
+		DEBUG_PD12("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		DEBUG_PD12("%s, bogus device state %p\n", __FUNCTION__, dev->driver);
+		return -ESHUTDOWN;
+	}
+
+	DEBUG_PD12("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length,
+	      _req->buf);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	DEBUG_PD12("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue),
+	      ep->stopped);
+	if (list_empty(&ep->queue) && !ep->stopped) {
+
+		if (ep_index(ep) == 0) {
+			/* EP0 */
+			list_add_tail(&req->queue, &ep->queue);
+			pd12_ep0_kick(dev, ep);
+			req = 0;
+		} else if (ep_is_in(ep)) {
+			/* EP2 & EP4 */
+			usb_set_index(ep_index(ep));
+			read_data(&ep_status);
+			if ((ep_status & 0x0) == 0x0) {
+				if (write_fifo(ep, req) == 1)
+					req = 0;
+			}
+		} else {
+			/* EP1  & EP3 */
+			usb_set_index(ep_index(ep));
+			read_data(&ep_status);
+			if ((ep_status & 0x01) == 0x01) {
+				if (read_fifo(ep, req) == 1)
+					req = 0;
+			}
+		}
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (req != 0)
+		list_add_tail(&req->queue, &ep->queue);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/* dequeue JUST ONE request */
+static int pd12_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pd12_ep *ep;
+	struct pd12_request *req;
+	unsigned long flags;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _ep);
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || ep->ep.name == ep0name )
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->dev->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->dev->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+/** Halt specific EP
+ *  Return 0 if success
+ *  NOTE: Sets INDEX register to EP !
+ */
+static int pd12_set_halt(struct usb_ep *_ep, int value)
+{
+	struct pd12_ep *ep;
+	unsigned long flags;
+	u8 ep_status;
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+		DEBUG_PD12("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	usb_set_index(ep_index(ep));
+
+	DEBUG_PD12("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value);
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	if (ep_index(ep) == 0) {
+		/* EP0 */
+		write_cmd(PD12_SET_STATUS | ep_index(ep));
+		write_data(0x01);
+	} else if (ep_is_in(ep)) {
+		write_cmd(PD12_READ_EP_STATUS);
+		read_data(&ep_status);
+		if (value && ((ep_status & 0x60) /*buffer 0 or 1 full*/
+			      || !list_empty(&ep->queue))) {
+			/*
+			 * Attempts to halt IN endpoints will fail (returning -EAGAIN)
+			 * if any transfer requests are still queued, or if the controller
+			 * FIFO still holds bytes that the host hasn't collected.
+			 */
+			spin_unlock_irqrestore(&ep->dev->lock, flags);
+			DEBUG_PD12
+			    ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n",
+			     (ep_status & 0x60),
+			     !list_empty(&ep->queue));
+			return -EAGAIN;
+		}
+		flush(ep);
+		if (value)
+		{
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x01);
+		} else {
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x00);
+		}
+
+	} else {
+
+		flush(ep);
+		if (value)
+		{
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x01);
+		} else {
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x00);
+		}
+	}
+
+	if (value) {
+		ep->stopped = 1;
+	} else {
+		ep->stopped = 0;
+	}
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG_PD12("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS");
+
+	return 0;
+}
+
+
+/****************************************************************/
+/* End Point 0 related functions                                */
+/****************************************************************/
+
+/* return:  0 = still running, 1 = completed, negative = errno */
+static int write_fifo_ep0(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 max;
+	unsigned count;
+	int is_last;
+	unsigned length;
+	u8* buf;
+	u8 ep_sts;
+
+	write_cmd(1); /* index 1*/
+	read_data(&ep_sts);
+/*	pd12_set_ack(1); */
+	max = ep_maxpacket(ep);
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	DEBUG_PD12_EP0("%s\n", __FUNCTION__);
+
+	length = req->req.length - req->req.actual;
+	length = min(length, (unsigned)max);
+	req->req.actual += length;
+
+	DEBUG_PD12("Write %d (max %d), fifo %p\n", length, max, buf);
+
+	count = length;
+	write_cmd(PD12_WRITE_BUF);
+	write_data(0x0);
+	write_data(count);
+	while (length--) {
+		write_data(*buf++);
+	}
+	
+	write_cmd(PD12_VALIDATE_BUF);
+	/* last packet is usually short (or a zlp) */
+	if (unlikely(count != max))
+		is_last = 1;
+	else {
+		if (likely(req->req.length != req->req.actual) || req->req.zero)
+			is_last = 0;
+		else
+			is_last = 1;
+	}
+
+	DEBUG_PD12_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__,
+		  ep->ep.name, count,
+		  is_last ? "/L" : "", req->req.length - req->req.actual, req);
+
+	/* requests complete when all IN data is in the FIFO */
+	if (is_last) {
+		done(ep, req, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+static __inline__ void pd12_fifo_read(struct pd12_ep *ep,
+					unsigned char *cp, u8 max)
+{
+	u8 count;
+
+	usb_set_index(0);
+	write_cmd(PD12_READ_BUF);
+	read_data(&count);
+	read_data(&count);
+	if (count > max)
+		count = max;
+	while (count--){
+		read_data(cp++);
+	}
+	write_cmd(PD12_CLEAR_BUF);
+}
+
+static __inline__ void pd12_fifo_write(struct pd12_ep *ep,
+					  unsigned char *cp, u8 count)
+{
+	write_cmd(1); /* index 1 */
+	write_cmd(PD12_WRITE_BUF);
+	write_data(0x0);
+	write_data(count);
+	DEBUG_PD12_EP0("fifo_write: %d %d\n", ep_index(ep), count);
+	while (count--)
+		write_data(*cp++);
+	write_cmd(PD12_VALIDATE_BUF);
+}
+static int read_fifo_ep0(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 ep_status;
+	u8 len;
+	u8 *buf;
+	unsigned bufferspace, count, is_short;
+
+	DEBUG_PD12_EP0("%s\n", __FUNCTION__);
+
+	usb_set_index(0); /* index 0*/
+	read_data(&ep_status);
+	if ((ep_status & UDC_FIFO_UNREADABLE) == UDC_FIFO_UNREADABLE)
+		return 0;
+
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+	bufferspace = req->req.length - req->req.actual;
+
+	write_cmd(PD12_READ_BUF);
+	read_data(&len);
+	read_data(&len);
+	count = (unsigned)len;
+
+	is_short = (count < ep->ep.maxpacket);
+	DEBUG_PD12_EP0("read %s, %d bytes%s req %p %d/%d\n",
+		  ep->ep.name, count,
+		  is_short ? "/S" : "", req, req->req.actual, req->req.length);
+
+	while (count--) {
+		u8 byte;
+		read_data(&byte);
+		if (unlikely(bufferspace == 0)) {
+			/* this happens when the driver's buffer
+			 * is smaller than what the host sent.
+			 * discard the extra data.
+			 */
+			if (req->req.status != -EOVERFLOW)
+				DEBUG_PD12_EP0("%s overflow %d\n", ep->ep.name,
+					  count);
+			req->req.status = -EOVERFLOW;
+		} else {
+			*buf++ = byte;
+			bufferspace--;
+		}
+	}
+
+	/* completion */
+	if (is_short || req->req.actual == req->req.length) {
+		done(ep, req, 0);
+		return 1;
+	}
+
+	/* finished that packet.  the next one may be waiting... */
+	return 0;
+}
+
+/**
+ * udc_set_address - set the USB address for this device
+ * @address:
+ *
+ * Called from control endpoint function after it decodes a set address setup packet.
+ */
+static void udc_set_address(struct pd12_udc *dev, unsigned char address)
+{
+	DEBUG_PD12_EP0("%s: %d\n", __FUNCTION__, address);
+
+	dev->usb_address = address;
+	write_cmd(PD12_SET_ADDR_EN);
+	write_data(0x80 | address);
+}
+
+/*
+ * DATA_STATE_RECV (OUT_PKT_RDY)
+ *      - if error
+ *              set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
+ *      - else
+ *              set EP0_CLR_OUT bit
+ 				if last set EP0_DATA_END bit
+ */
+static void pd12_ep0_out(struct pd12_udc *dev)
+{
+	struct pd12_request *req;
+	struct pd12_ep *ep = &dev->ep[0];
+	int ret;
+
+	DEBUG_PD12_EP0("%s: \n", __FUNCTION__);
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	if (req) {
+
+		if (req->req.length == 0) {
+			DEBUG_PD12_EP0("ZERO LENGTH OUT!\n");
+			dev->ep0state = WAIT_FOR_SETUP;
+			return;
+		}
+		ret = read_fifo_ep0(ep, req);
+		if (ret) {
+			/* Done! */
+			DEBUG_PD12_EP0("%s: finished, waiting for status\n",
+				  __FUNCTION__);
+			/* read last status here ?*/
+			dev->ep0state = WAIT_FOR_SETUP;
+		} else {
+			/* Not done yet.. */
+			DEBUG_PD12_EP0("%s: not finished\n", __FUNCTION__);
+			/* usb_set(EP0_CLR_OUT, USB_EP0_CSR); */
+		}
+	} else {
+		DEBUG_PD12_EP0("NO REQ??!\n");
+	}
+}
+
+/*
+ * DATA_STATE_XMIT
+ */
+static int pd12_ep0_in(struct pd12_udc *dev)
+{
+	struct pd12_request *req;
+	struct pd12_ep *ep = &dev->ep[0];
+	int ret, need_zlp = 0;
+	u8 val;
+	
+	DEBUG_PD12_EP0("%s: \n", __FUNCTION__);
+
+
+	pd12_read_lstatus(1,&val);
+	pd12_set_ack(1);
+	if(((val & 0x1) != 0x1) && !first_tran){
+	/*	printk("return from ep0_in\n"); */
+		return 0;
+	}
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	if (!req) {
+		dev->ep0state = WAIT_FOR_SETUP;
+		DEBUG_PD12_EP0("%s: NULL REQ\n", __FUNCTION__);
+		return 0;
+	}
+
+	if (req->req.length == 0) {
+		dev->ep0state = WAIT_FOR_SETUP;
+		return 1;
+	}
+
+	ret = write_fifo_ep0(ep, req);
+	first_tran = 0;
+	if (ret == 1 && !need_zlp) {
+		/* Last packet */
+		DEBUG_PD12_EP0("%s: finished, waiting for status\n", __FUNCTION__);
+		dev->ep0state = WAIT_FOR_SETUP;
+	} else {
+		DEBUG_PD12_EP0("%s: not finished\n", __FUNCTION__);
+	}
+
+	return 1;
+}
+
+static int pd12_handle_get_status(struct pd12_udc *dev,
+				     struct usb_ctrlrequest *ctrl)
+{
+	struct pd12_ep *ep0 = &dev->ep[0];
+	struct pd12_ep *qep;
+	int reqtype = (ctrl->bRequestType & USB_RECIP_MASK);
+	u16 val = 0;
+	u8 ep_sts;
+
+	if (reqtype == USB_RECIP_INTERFACE) {
+		/* This is not supported.
+		 * And according to the USB spec, this one does nothing..
+		 * Just return 0
+		 */
+		DEBUG_PD12_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n");
+	} else if (reqtype == USB_RECIP_DEVICE) {
+		DEBUG_PD12_SETUP("GET_STATUS: USB_RECIP_DEVICE\n");
+		val |= (1 << 0);	/* Self powered */
+		/*val |= (1<<1); */	/* Remote wakeup */
+	} else if (reqtype == USB_RECIP_ENDPOINT) {
+		int ep_num = (ctrl->wIndex & ~USB_DIR_IN);
+
+		DEBUG_PD12_SETUP
+		    ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n",
+		     ep_num, ctrl->wLength);
+
+		if (ctrl->wLength > 2 || ep_num > 3) /* ep_num cannt abouve maxenpoint?*/
+			return -EOPNOTSUPP;
+
+		qep = &dev->ep[ep_num];
+		if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0)
+		    && ep_index(qep) != 0) {
+			return -EOPNOTSUPP;
+		}
+
+		usb_set_index(ep_index(qep));
+		read_data(&ep_sts);
+		val = (u16)ep_sts;
+
+		/* Back to EP0 index */
+		usb_set_index(0);
+
+		DEBUG_PD12_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num,
+			    ctrl->wIndex, val);
+	} else {
+		DEBUG_PD12_SETUP("Unknown REQ TYPE: %d\n", reqtype);
+		return -EOPNOTSUPP;
+	}
+
+	/* Put status to FIFO */
+	pd12_fifo_write(ep0, (u8 *) & val, sizeof(val));
+
+	return 0;
+}
+
+/*
+ * WAIT_FOR_SETUP
+ *      - read data packet from EP0 FIFO
+ *      - decode command
+ *      - if error
+ *              set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
+ *      - else
+ *              set EP0_CLR_OUT | EP0_DATA_END bits
+ */
+static void pd12_ep0_setup(struct pd12_udc *dev)
+{
+	struct pd12_ep *ep = &dev->ep[0];
+	struct usb_ctrlrequest ctrl;
+	int i;
+	u8 stat;
+	u8 inbuf[16];
+	
+	DEBUG_PD12_SETUP("%s: \n", __FUNCTION__);
+
+	/* Nuke all previous transfers */
+	nuke(ep, -EPROTO);
+	pd12_read_lstatus(0,&stat);
+	pd12_set_ack(0);
+	pd12_set_ack(1);
+	if((stat & 0x01) != 0x01){
+		return ;
+	}
+	pd12_fifo_read(ep, (u8 *)&ctrl, 8);
+
+	DEBUG_PD12_SETUP("CTRL.bRequestType = 0x%x (is_in 0x%x)\n", ctrl.bRequestType,
+		    ctrl.bRequestType == USB_DIR_IN);
+	DEBUG_PD12_SETUP("CTRL.bRequest = 0x%x\n", ctrl.bRequest);
+	DEBUG_PD12_SETUP("CTRL.wLength = 0x%x\n", ctrl.wLength);
+	DEBUG_PD12_SETUP("CTRL.wValue = 0x%x (%d)\n", ctrl.wValue, ctrl.wValue >> 8);
+	DEBUG_PD12_SETUP("CTRL.wIndex = 0x%x\n", ctrl.wIndex);
+
+	/* Set direction of EP0 */
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		ep->bEndpointAddress |= USB_DIR_IN;
+	} else {
+		ep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+
+	/* Handle some SETUP packets ourselves */
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+
+		DEBUG_PD12_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue);
+		udc_set_address(dev, le16_to_cpu(ctrl.wValue));
+		pd12_fifo_write(ep,inbuf,0);
+		return;
+
+	case USB_REQ_GET_STATUS:{
+			if (pd12_handle_get_status(dev, &ctrl) == 0)
+				return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+			if (ctrl.bRequestType == USB_RECIP_ENDPOINT) {
+				struct pd12_ep *qep;
+				int ep_num = (ctrl.wIndex & 0x0f);
+
+				/* Support only HALT feature */
+				if (ctrl.wValue != 0 || ctrl.wLength != 0
+				    || ep_num > 4 || ep_num < 1)
+					break;
+
+				qep = &dev->ep[ep_num];
+				if (ctrl.bRequest == USB_REQ_SET_FEATURE) {
+					DEBUG_PD12_SETUP("SET_FEATURE (%d)\n",
+						    ep_num);
+					pd12_set_halt(&qep->ep, 1);
+				} else {
+					DEBUG_PD12_SETUP("CLR_FEATURE (%d)\n",
+						    ep_num);
+					pd12_set_halt(&qep->ep, 0);
+				}
+				usb_set_index(0);
+
+				return;
+			}
+			break;
+		}
+
+	default:
+		break;
+	}
+
+	if (dev->driver) {
+		/* device-2-host (IN) or no data setup command, process immediately */
+		spin_unlock(&dev->lock);
+		i = dev->driver->setup(&dev->gadget, &ctrl);
+		spin_lock(&dev->lock);
+
+		if (i < 0) {
+			pd12_ep0_in(dev);
+			/* setup processing failed, force stall */
+			DEBUG_PD12_SETUP
+			    ("  --> ERROR: gadget setup FAILED (stalling), setup returned %d\n",
+			     i);
+		/*	usb_set_index(0); */
+			write_cmd(PD12_SET_STATUS);
+			write_data(0x1);
+			write_cmd(PD12_SET_STATUS + 0x1);
+			write_data(0x1);
+			/* ep->stopped = 1; */
+			dev->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+}
+
+/*
+ * handle ep0 in interrupt
+ */
+static void pd12_handle_ep0(struct pd12_udc *dev)
+{
+	struct pd12_ep *ep = &dev->ep[0];
+	u8 ep0in_sts,ep0out_sts/*,int_sts*/;
+	
+	/* Set index 0 */
+	write_cmd(0x0);
+	read_data(&ep0out_sts);
+	
+	write_cmd(0x1);
+	read_data(&ep0in_sts);
+	
+
+	/*
+	 * if STALL is set, clear STALL bit
+	 */
+	if (ep0out_sts & UDC_EP_STALL ) {
+/*		DEBUG_PD12_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, status); */
+		write_cmd(PD12_SET_STATUS);
+		write_data(0x0);
+		nuke(ep, -ECONNABORTED);
+		dev->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (ep0in_sts & UDC_EP_STALL ) {
+/*		DEBUG_PD12_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, status); */
+		write_cmd(PD12_SET_STATUS + 0x1);
+		write_data(0x0);
+		nuke(ep, -ECONNABORTED);
+		dev->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	switch (dev->ep0state) {
+		case WAIT_FOR_SETUP:
+			DEBUG_PD12_EP0("WAIT_FOR_SETUP\n");
+			pd12_ep0_setup(dev);
+			break;
+		case DATA_STATE_RECV:
+			DEBUG_PD12_EP0("DATA_STATE_RECV\n");
+			pd12_ep0_out(dev);
+			break;
+		case DATA_STATE_XMIT:
+			DEBUG_PD12_EP0("continue with DATA_STATE_XMIT\n");
+			pd12_ep0_in(dev);
+			return;
+		default:
+			/* Stall? */
+			DEBUG_PD12_EP0("Odd state!! state = %s\n",
+				  state_names[dev->ep0state]);
+			dev->ep0state = WAIT_FOR_SETUP;
+			/* nuke(ep, 0); */
+			/* usb_set(EP0_SEND_STALL, ep->csr1); */
+			break;
+	}
+
+}
+
+static void pd12_ep0_kick(struct pd12_udc *dev, struct pd12_ep *ep)
+{
+
+	if (ep_is_in(ep)) {
+		dev->ep0state = DATA_STATE_XMIT;
+		pd12_ep0_in(dev);
+	} else {
+		dev->ep0state = DATA_STATE_RECV;
+		pd12_ep0_out(dev);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * 	device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static int pd12_udc_get_frame(struct usb_gadget *_gadget)
+{
+	u16 frame1,frame2;
+	write_cmd(PD12_RD_CUR_FRAME_NUM);
+	read_data((u8 *)&frame1);/* Least significant 8 bits */
+	read_data((u8 *)&frame2);/* Most significant 3 bits */
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _gadget);
+	return ((frame2 & 0x07) << 8) | (frame1 & 0xff);
+}
+
+static int pd12_udc_wakeup(struct usb_gadget *_gadget)
+{
+	/* host may not have enabled remote wakeup */
+	/*if ((UDCCS0 & UDCCS0_DRWF) == 0)
+	   return -EHOSTUNREACH;
+	   udc_set_mask_UDCCR(UDCCR_RSM); */
+	return -ENOTSUPP;
+}
+
+static const struct usb_gadget_ops pd12_udc_ops = {
+	.get_frame = pd12_udc_get_frame,
+	.wakeup = pd12_udc_wakeup,
+	/* current versions must always be self-powered */
+};
+
+static void nop_release(struct device *dev)
+{
+	DEBUG_PD12("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+static struct pd12_udc usb_memory = {
+	.usb_address = 0,
+
+	.gadget = {
+		   .ops = &pd12_udc_ops,
+		   .ep0 = &usb_memory.ep[0].ep,
+		   .name = driver_name,
+		   .dev = {
+			   .bus_id = "gadget",
+			   .release = nop_release,
+			   },
+		   },
+
+	/* control endpoint */
+	.ep[0] = {
+		  .ep = {
+			 .name = ep0name,
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 16,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 0,
+		  .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+		  },
+
+	/* first group of endpoints */
+	.ep[1] = {
+		  .ep = {
+			 .name = "ep1out-bulk",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 16,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 1,
+		  .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+		  },
+
+	.ep[2] = {
+		  .ep = {
+			 .name = "ep2in-bulk",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 16,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 2 | USB_DIR_IN,
+		  .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+		  },
+	.ep[3] = {
+		  .ep = {
+			 .name = "ep3out-int",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 64,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 3,
+		  .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+		  },
+	.ep[4] = {
+		  .ep = {
+			 .name = "ep3in-int",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 64,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 4 | USB_DIR_IN,
+		  .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+		  },
+};
+
+/*
+ * 	probe - binds to the platform device
+ */
+static int __devinit pd12_udc_probe(struct device *_dev)
+{
+	struct pd12_udc *dev = &usb_memory;
+	int retval;
+
+	DEBUG_PD12("%s: %p\n", __FUNCTION__, _dev);
+	spin_lock_init(&dev->lock);
+	dev->dev = _dev;
+	
+	device_initialize(&dev->gadget.dev);
+	dev->gadget.dev.parent = _dev;
+
+	the_controller = dev;
+	dev_set_drvdata(_dev, dev);
+
+	udc_reinit(dev);
+
+	dev->gadget.speed = USB_SPEED_FULL;
+	/* irq setup after old hardware state is cleaned up */
+	retval =
+	    request_irq(IRQ_USBINTR, pd12_udc_irq, /*SA_INTERRUPT*/SA_SAMPLE_RANDOM, driver_name,
+			dev);
+	if (retval != 0) {
+		DEBUG_PD12(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
+		      IRQ_USBINTR, retval);
+		return -EBUSY;
+	}
+
+	create_proc_files();
+
+	return retval;
+}
+
+static int __devexit pd12_udc_remove(struct device *_dev)
+{
+	struct pd12_udc *dev = _dev->driver_data;
+
+	DEBUG_PD12("%s: %p\n", __FUNCTION__, dev);
+
+	udc_disable(dev);
+	remove_proc_files();
+	usb_gadget_unregister_driver(dev->driver);
+
+	free_irq(IRQ_USBINTR, dev);
+
+	dev_set_drvdata(_dev, 0);
+
+	the_controller = 0;
+
+	return 0;
+}
+static struct platform_device	pd12_pdev = {
+	.name	= (char *) driver_name,
+	.id		= -1,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct device_driver udc_driver = {
+	.name = (char *)driver_name,
+	.bus = &platform_bus_type,
+	.probe = pd12_udc_probe,
+	.remove = pd12_udc_remove
+	    /* FIXME power management support */
+	    /* .suspend = ... disable UDC */
+	    /* .resume = ... re-enable UDC */
+};
+
+static int __init udc_init(void)
+{
+	int retval;
+	
+	DEBUG_PD12("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION);
+	th_pd12_virt = ioremap((ulong)TH_PD12_ADDR,0x10);
+	if(!th_pd12_virt){
+		printk("%s: ioremap fail\n",__FUNCTION__);
+		return -EIO;
+	}
+	th_cpld_virt = ioremap((ulong)TH_CPLD_ADDR,0x10);
+	if(!th_cpld_virt){
+		printk("%s: ioremap fail\n",__FUNCTION__);
+		return -EIO;
+	}
+	retval = platform_device_register (&pd12_pdev);
+	if (retval < 0){
+		platform_device_unregister (&pd12_pdev);
+		return retval;
+	}
+	return driver_register(&udc_driver);
+}
+
+static void __exit udc_exit(void)
+{
+	if(th_pd12_virt){
+		iounmap((void*)th_pd12_virt);
+		th_pd12_virt = NULL;
+	}
+	if(th_cpld_virt){
+		iounmap((void*)th_cpld_virt);
+		th_cpld_virt = NULL;
+	}
+	driver_unregister(&udc_driver);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/pd12_udc.h b/drivers/usb/gadget/pd12_udc.h
new file mode 100644
index 0000000..3ffd07e
--- /dev/null
+++ b/drivers/usb/gadget/pd12_udc.h
@@ -0,0 +1,148 @@
+/*
+ * linux/drivers/usb/gadget/pd12_udc.h
+ * Taihu pd12 full speed USB device controllers
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __PD12_UDC_H_
+#define __PD12_UDC_H_
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/ibm4xx.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#define TH_PD12_ADDR  0x50000000
+#define TH_CPLD_ADDR  0x50100000
+
+#define IRQ_USBINTR 29
+#define USB_SUSPEND 0x20
+
+#define UDC_FIFO_EMPTY 0x0
+#define UDC_FIFO_FULL 0x01
+#define UDC_EP_STALL 0x02
+#define UDC_FIFO_UNWRITABLE (UDC_EP_STALL | UDC_FIFO_FULL)
+#define UDC_FIFO_UNREADABLE (UDC_FIFO_EMPTY | UDC_EP_STALL)
+
+/* pd12 udc command */
+#define PD12_SET_ADDR_EN		0xd0
+#define PD12_SET_EP_EN			0xd8
+#define PD12_SET_MODE 			0xf3
+#define PD12_SET_DMA 			0xfb
+#define PD12_READ_INT			0xf4
+#define PD12_READ_LAST_STATUS 	0x40
+#define PD12_SET_STATUS			0x40
+#define PD12_READ_EP_STATUS 	0x80
+#define PD12_READ_BUF			0xf0
+#define PD12_WRITE_BUF			0xf0
+#define PD12_SET_EP_STATUS		0x40
+#define PD12_ACK_SETUP			0xf1
+#define PD12_CLEAR_BUF			0xf2
+#define PD12_VALIDATE_BUF		0xfa
+#define PD12_SND_RESUME			0xf6
+#define PD12_RD_CUR_FRAME_NUM	0xf5
+
+
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define WAIT_FOR_OUT_STATUS     2
+#define DATA_STATE_RECV         3
+
+#define PD12_SUSPEND_CHG 		0x80
+#define PD12_BUS_RST 			0x40
+#define PD12_MAIN_IN 			0x20
+#define PD12_MAIN_OUT 			0x10
+#define PD12_EP1_IN 			0x08
+#define PD12_EP1_OUT 			0x04
+#define PD12_CNTL_IN 			0x02
+#define PD12_CNTL_OUT 			0x01
+
+
+#define PD12_MAX_ENDPOINTS       6
+
+/* ********************************************************************************************* */
+/* IO
+ */
+
+struct pd12_ep {
+	struct usb_ep ep;
+	struct pd12_udc *dev;
+
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	unsigned long pio_irqs;
+	unsigned long dma_irqs;
+	short		  dma;
+
+	u8 stopped;
+	u8 bEndpointAddress;
+	u8 bmAttributes;
+
+};
+
+struct pd12_request {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+struct pd12_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	spinlock_t lock;
+
+	int ep0state;
+	struct pd12_ep ep[PD12_MAX_ENDPOINTS];
+
+	unsigned char usb_address;
+
+	unsigned req_pending:1, req_std:1, req_config:1;
+};
+
+static struct pd12_udc *the_controller;
+
+#define ep_is_in(EP) 		(((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN)
+#define ep_index(EP) 		((EP)->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) 	((EP)->ep.maxpacket)
+
+#endif
diff --git a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h
index b67db19..da0de26 100644
--- a/include/asm-ppc/ibm4xx.h
+++ b/include/asm-ppc/ibm4xx.h
@@ -47,6 +47,10 @@
  #include <platforms/4xx/sycamore.h>
  #endif

+#if defined(CONFIG_TAIHU)
+#include <platforms/4xx/taihu.h>
+#endif
+
  #if defined(CONFIG_WALNUT)
  #include <platforms/4xx/walnut.h>
  #endif

^ permalink raw reply related


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