LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] ibm_newemac: Increase MDIO timeouts
From: Benjamin Herrenschmidt @ 2008-04-26  3:35 UTC (permalink / raw)
  To: Josh Boyer; +Cc: super.firetwister, netdev, Bill Fink, jgarzik, linuxppc-dev
In-Reply-To: <20080425185842.7ab463e7@zod.rchland.ibm.com>


On Fri, 2008-04-25 at 18:58 -0500, Josh Boyer wrote:
> > > I guess them being "two short" is why they needed to be
> doubled.  :-)
> > 
> > Well, in that case, it makes sense, it's still pretty short and
> better
> > safe than sorry. On day I may look at actually measuring PHYs and
> see if
> > it's worth trying to do sleeping waits in there.
> 
> I think you missed Bill's joke.  Read your original email and spot the
> typo, then read Bill's pun.
> 
> And stop emailing before you eat breakfast ;)

Hrm ... yeah allright I missed my initial typo and I missed Bill
joke :-) looks like another case of replying while still half asleep ...

Ben.

^ permalink raw reply

* Re: Enabling 64 bit data type for dma_addr_t on non PPC64 architecture
From: Benjamin Herrenschmidt @ 2008-04-26  3:38 UTC (permalink / raw)
  To: Tushar Tyagi; +Cc: linuxppc-dev
In-Reply-To: <BA76C315ECE5B149AE7194FE094819E205E59014@SDCEXCHANGE01.ad.amcc.com>


On Fri, 2008-04-25 at 19:52 -0700, Tushar Tyagi wrote:
> Hello,
> I'm working a new DMA hardware for PPC processors.
> The processor is a 32 bit architecture having 40 bit physical address
> space. 
> So we need the 32 bit processor code base but we want the type
> dma_addr_t to represent 64 bit data, without enabling the CONFIG_PPC64
> flag.

That shouldn't be a big issue, we already support 64 bits resources,
extending dma_addr_t shouldn't too hard.

> We want to use the Linux generic DMA layer to offload DMA operations to
> the hw and the Linux code path goes through function
> dma_async_memcpy_buf_to_buf and then dma_map_single.
>  
> Currently the dma_map_single function has 2 versions based upon
> CONFIG_PPC64 defined or not.
>  
> Is it possible to reuse the CONFIG_PPC64 based code only pertaining to
> DMA by doing the following:

It's possible, it might be a good idea even as it would allow to have
different implementations for different busses, which is in fact needed
for some things like 4xx if we start changing the mapping between
different PCI bridges vs. SoCs. In fact, I think there's been some
patches from Becky Bruce posted in februrary for doing just that, which
I totally forgot to review...
 
> Add a new flag called CONFIG_DMA64 along with CONFIG_PPC64 and
> __powerpc64__ flags, enabling our code to use the generic DMA layer with
> 64 bit data type for dma_addr_t.
>  
> With the above modification, the function dma_map_single starts
> returning 64 bit data type instead of 32.
>  
> Do you have any comments or suggestions ?

I'd suggest working with Becky on her initial patch and using that as
the basis for your stuff. I'll try to give it a good review asap.

Cheers,
Ben.

^ permalink raw reply

* Re: sysfs cpu entry
From: Kevin Diggs @ 2008-04-26  7:16 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <8564AA3F-C745-4A5A-952F-477B6A68681C@kernel.crashing.org>

Kumar Gala wrote:
> 
> What 32-bit chip are you looking to enable this for?
> 
> - k
> 

I am working on some stuff for the 750GX

kevin

^ permalink raw reply

* get_cycles()
From: Kevin Diggs @ 2008-04-26  7:54 UTC (permalink / raw)
  To: linuxppc-dev

H,

	Anyone know how to turn get_cycles() into an actual time in a module? 
ppc_tb_freq does not seem to be exported?

kevin

^ permalink raw reply

* Re: get_cycles()
From: David Miller @ 2008-04-26  7:11 UTC (permalink / raw)
  To: kevdig; +Cc: linuxppc-dev
In-Reply-To: <4812DFA3.6090106@hypersurf.com>

From: Kevin Diggs <kevdig@hypersurf.com>
Date: Sat, 26 Apr 2008 00:54:11 -0700

> 	Anyone know how to turn get_cycles() into an actual time in a module? 
> ppc_tb_freq does not seem to be exported?

You should really be using ktime_t and associated interfaces.

^ permalink raw reply

* [BUG] 2.6.25-git8 Kernel Bug while bootup on ppc and x86
From: Kamalesh Babulal @ 2008-04-26 11:21 UTC (permalink / raw)
  To: kernel list; +Cc: linuxppc-dev, Ingo Molnar, Balbir Singh

While booting the 2.6.25-git8 kernel on the ppc and x86_64 machine, kernel
bug is hit. This was reported in the next-20080423 kernel 
http://lkml.org/lkml/2008/4/23/206.

Call trace of x86_64 machine

BUG: unable to handle kernel paging request at 00000000ffffffff
IP: [<ffffffff802359e5>] put_files_struct+0x25/0x110
PGD 0 
Oops: 0002 [333] SMP 
CPU 3 
Modules linked in:
Pid: 1391, comm: khelper Tainted: G      D  2.6.25-git8-autotest #1
RIP: 0010:[<ffffffff802359e5>]  [<ffffffff802359e5>] put_files_struct+0x25/0x110
RSP: 0000:ffff81090d06bdb0  EFLAGS: 00010282
RAX: 0000000000000101 RBX: fffffffffffffffe RCX: ffff8100010260e0
RDX: 0000000000000007 RSI: 0000000000000001 RDI: 00000000ffffffff
RBP: 00000000fffffffe R08: 2222222222222267 R09: 2222222222222222
R10: 0000000000000010 R11: 2222222222222222 R12: 00000000ffffffff
R13: ffff81032d2e9000 R14: ffff81090e74b000 R15: ffff81061e4cbce0
FS:  0000000000000000(0000) GS:ffff81032e4ae940(0000) knlGS:0000000000000000
CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 00000000ffffffff CR3: 0000000000201000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process khelper (pid: 1391, threadinfo ffff81090d06a000, task ffff81090d8113a0)
Stack:  ffff81090d8113a0 fffffffffffffffe 00000000fffffffe ffff81032e556e00
 ffff81032d2e9000 ffff81090e74b000 ffff81061e4cbce0 ffffffff80294278
 ffff81090d06be70 ffffffff80636bc0 ffff81090d3646c0 ffff81032d2e9000
Call Trace:
 [<ffffffff80294278>] do_execve+0x108/0x230
 [<ffffffff802098e9>] sys_execve+0x49/0x80
 [<ffffffff8020c417>] kernel_execve+0x67/0xd0
 [<ffffffff8021d060>] physflat_send_IPI_mask+0x0/0xb0
 [<ffffffff802443e9>] ____call_usermodehelper+0x119/0x130
 [<ffffffff8022e577>] schedule_tail+0x27/0x60
 [<ffffffff8020c3a8>] child_rip+0xa/0x12
 [<ffffffff8021d060>] physflat_send_IPI_mask+0x0/0xb0
 [<ffffffff802442d0>] ____call_usermodehelper+0x0/0x130
 [<ffffffff8020c39e>] child_rip+0x0/0x12


Code: 48 83 c4 18 c3 90 48 83 ec 38 4c 89 64 24 18 48 89 5c 24 08 49 89 fc 48 89 6c 24 10 4c 89 6c 24 20 4c 89 74 24 28 4c 89 7c 24 30 <f0> ff 0f 0f 94 c0 84 c0 0f 84 b8 00 00 00 4c 8b 6f 08 31 ed 41 
RIP  [<ffffffff802359e5>] put_files_struct+0x25/0x110
 RSP <ffff81090d06bdb0>
CR2: 00000000ffffffff
---[ end trace ca143223eefdc828 ]---
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
Freeing unused kernel memory: 404k freed
input: AT Translated Set 2 keyboard as /class/input/input0
BUG: unable to handle kernel paging request at 000000010000001c
IP: [<ffffffff804dcfc5>] _spin_lock+0x5/0x20
PGD 0 
Oops: 0002 [334] SMP 
CPU 11 
Modules linked in:
Pid: 1, comm: swapper Tainted: G      D  2.6.25-git8-autotest #1
RIP: 0010:[<ffffffff804dcfc5>]  [<ffffffff804dcfc5>] _spin_lock+0x5/0x20
RSP: 0000:ffff81061e4cba08  EFLAGS: 00010282
RAX: 0000000000000100 RBX: ffffffff805bceb7 RCX: 0000000000000000
RDX: ffff81061e4cbfd8 RSI: 0000000000000001 RDI: 000000010000001c
RBP: ffff81061e740b00 R08: 0000000000000000 R09: 000000000000279c
R10: 0000000000000004 R11: 0000000000479490 R12: 00000000ffffff9c
R13: 00000000000001f8 R14: ffff81061e4cbe10 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffff81061e57ef40(0000) knlGS:0000000000000000
CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 000000010000001c CR3: 0000000000201000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process swapper (pid: 1, threadinfo ffff81061e4ca000, task ffff81032e4b9710)
Stack:  ffffffff8028c22e ffff81061e740b00 ffffffff805bceb7 ffff81061e740b00
 ffff81032dd2aa00 00000000000001f8 ffff81061e4cbe10 00000000000001f8
 ffffffff802c6de2 0000000000000000 ffffffff8026232e 0000000000000282
Call Trace:
 [<ffffffff8028c22e>] get_unused_fd_flags+0x2e/0x120
 [<ffffffff802c6de2>] load_elf_binary+0x1b2/0x1d40
 [<ffffffff8026232e>] generic_file_aio_read+0x4de/0x560
 [<ffffffff8028e129>] do_sync_read+0xd9/0x120
 [<ffffffff80248110>] autoremove_wake_function+0x0/0x30
 [<ffffffff8028c5f7>] __dentry_open+0x197/0x2b0
 [<ffffffff80297985>] deny_write_access+0x15/0x40
 [<ffffffff80292944>] open_exec+0xa4/0xc0
 [<ffffffff8029258d>] search_binary_handler+0x8d/0x210
 [<ffffffff802c55e6>] load_script+0x266/0x270
 [<ffffffff80292166>] get_arg_page+0x46/0xb0
 [<ffffffff802924a1>] copy_strings+0x1c1/0x1e0
 [<ffffffff8029258d>] search_binary_handler+0x8d/0x210
 [<ffffffff80294352>] do_execve+0x1e2/0x230
 [<ffffffff802098e9>] sys_execve+0x49/0x80
 [<ffffffff8020c417>] kernel_execve+0x67/0xd0
 [<ffffffff80209078>] init_post+0x58/0xf0
 [<ffffffff8020c3a8>] child_rip+0xa/0x12
 [<ffffffff8020c39e>] child_rip+0x0/0x12


Code: 66 66 90 66 66 90 f0 81 2f 00 00 00 01 0f 94 c0 84 c0 ba 01 00 00 00 75 09 f0 81 07 00 00 00 01 30 d2 89 d0 c3 90 b8 00 01 00 00 <f0> 66 0f c1 07 38 e0 74 06 f3 90 8a 07 eb f6 c3 66 66 66 90 66 
RIP  [<ffffffff804dcfc5>] _spin_lock+0x5/0x20
 RSP <ffff81061e4cba08>
CR2: 000000010000001c
---[ end trace ca143223eefdc828 ]---


call trace on ppc boxes - machine 1


Unable to handle kernel paging request for data at address 0x00500081
Faulting instruction address: 0xc0000000003bef00
Oops: Kernel access of bad area, sig: 11 [#1]
SMP NR_CPUS=128 NUMA pSeries
Modules linked in:
NIP: c0000000003bef00 LR: c0000000000e9974 CTR: 0000000000000000
REGS: c00000007e0731e0 TRAP: 0300   Not tainted  (2.6.25-git8-autotest)
MSR: 8000000000009032 <EE,ME,IR,DR>  CR: 24000082  XER: 2000000a
DAR: 0000000000500081, DSISR: 0000000040000000
TASK = c000000009fd3320[1] 'swapper' THREAD: c00000007e070000 CPU: 1
GPR00: 0000000080000001 c00000007e073460 c00000000067b448 0000000000500081 
GPR04: 0000000000000001 0000000000000000 0000000000000000 0000000000000000 
GPR08: c00000007e0730b0 c000000009fd3320 00000000000000a8 c000000009021d80 
GPR12: 0000000600000004 c0000000006af500 0000000000000000 0000000000000000 
GPR16: c00000000980be00 0000000000000000 0000000000000000 0000000000000000 
GPR20: 0000000000000000 0000000000000000 c000000009e80400 4000000001c00000 
GPR24: c00000007e073b20 c00000000980be80 0000000000000000 0000000000500001 
GPR28: ffffffffffffffe8 0000000000000080 c00000000062eca0 0000000000500081 
NIP [c0000000003bef00] __lock_text_start+0x20/0x88
LR [c0000000000e9974] .get_unused_fd_flags+0x40/0x184
Call Trace:
[c00000007e0734e0] [c0000000000e9974] .get_unused_fd_flags+0x40/0x184
[c00000007e073580] [c000000000138ecc] .load_elf_binary+0x1c8/0x1878
[c00000007e0736c0] [c0000000000f1a0c] .search_binary_handler+0x104/0x300
[c00000007e073770] [c0000000001356a0] .load_script+0x2c0/0x2ec
[c00000007e0738a0] [c0000000000f1a0c] .search_binary_handler+0x104/0x300
[c00000007e073950] [c0000000000f1dcc] .do_execve+0x1c4/0x2b8
[c00000007e073a10] [c000000000011aa8] .sys_execve+0x78/0xb4
[c00000007e073ab0] [c00000000000872c] syscall_exit+0x0/0x40
--- Exception: c01 at .kernel_execve+0x8/0x14
    LR = .run_init_process+0x28/0x40
[c00000007e073da0] [c0000000000fb108] .sys_dup+0x2c/0x44 (unreliable)
[c00000007e073e20] [c000000000009300] .init_post+0x90/0xf0
[c00000007e073ea0] [c00000000054edfc] .kernel_init+0x38c/0x3c0
[c00000007e073f90] [c000000000028a98] .kernel_thread+0x4c/0x68
Instruction dump:
e8010010 eba1ffe8 7c0803a6 4e800020 7c0802a6 fbe1fff0 7c7f1b78 f8010010 
38000000 f821ff81 980d01dc 800d0008 <7d20f828> 2c090000 40820010 7c00f92d 
---[ end trace 2de3013543283acc ]---


machine 2


Faulting instruction address: 0xc0000000000586f8
Oops: Kernel access of bad area, sig: 11 [#22]
SMP NR_CPUS=128 NUMA pSeries
Modules linked in:
NIP: c0000000000586f8 LR: c0000000000e7668 CTR: c0000000001068e8
REGS: c0000000fe237790 TRAP: 0300   Tainted: G      D   (2.6.25-git8-autotest)
MSR: 8000000000009032 <EE,ME,IR,DR>  CR: 28000042  XER: 20000006
DAR: 000000007fe3fb78, DSISR: 0000000040000000
TASK = c0000000fe1a6910[120] 'khelper' THREAD: c0000000fe234000 CPU: 2
GPR00: c0000000000e7668 c0000000fe237a10 c0000000008387b8 000000007fe3fb78 
GPR04: cf00000003794408 c0000000fe137000 0000000000000004 0000000000000000 
GPR08: c000000000998eb8 0000000000000000 0000000000000001 c0000000009995a0 
GPR12: 8000000000009032 c000000000874700 0000000000000000 c0000000005fd188 
GPR16: 4000000001c00000 c0000000005fba10 0000000000000000 00000000002b1000 
GPR20: 00000000022d9e68 c0000000006d9e68 00000000022da0d8 c0000000006da0d8 
GPR24: c0000000fe237c80 c0000000fe077bc0 c0000000fe02a000 c0000000fe229000 
GPR28: 000000007fe3fb78 fffffffffffffffe c0000000007b2248 fffffffffffffffe 
NIP [c0000000000586f8] .put_files_struct+0x30/0x13c
LR [c0000000000e7668] .do_execve+0x220/0x258
Call Trace:
[c0000000fe237a10] [c0000000fe237ab0] 0xc0000000fe237ab0 (unreliable)
[c0000000fe237ab0] [c0000000000e7668] .do_execve+0x220/0x258
[c0000000fe237b70] [c00000000001079c] .sys_execve+0x70/0xac
[c0000000fe237c10] [c00000000000872c] syscall_exit+0x0/0x40
--- Exception: c01 at .kernel_execve+0x8/0x14
    LR = .____call_usermodehelper+0x158/0x16c
[c0000000fe237f00] [c00000000006b29c] .____call_usermodehelper+0x144/0x16c (unreliable)
[c0000000fe237f90] [c000000000025218] .kernel_thread+0x4c/0x68
Instruction dump:
7c0802a6 fb81ffe0 fbc1fff0 fb41ffd0 fb61ffd8 fba1ffe8 fbe1fff8 f8010010 
ebc2ad30 f821ff61 7c7c1b78 7c2004ac <7c001828> 3000ffff 7c00192d 40a2fff4 
---[ end trace 8640abe69a316dee ]---
NET: Registered protocol family 2
Switched to high resolution mode on CPU 0
Switched to high resolution mode on CPU 3
Switched to high resolution mode on CPU 2
Switched to high resolution mode on CPU 1
Unable to handle kernel paging request for data at address 0xbffffffffe02b000
Faulting instruction address: 0xc0000000000d9a64
Oops: Kernel access of bad area, sig: 11 [#23]
SMP NR_CPUS=128 NUMA pSeries
Modules linked in:
NIP: c0000000000d9a64 LR: c0000000000d99f4 CTR: 0000000000000000
REGS: c0000000fe0777b0 TRAP: 0300   Tainted: G      D   (2.6.25-git8-autotest)
MSR: 8000000000009032 <EE,ME,IR,DR>  CR: 24000042  XER: 20000003
DAR: bffffffffe02b000, DSISR: 0000000040000000
TASK = c0000000fe070000[1] 'swapper' THREAD: c0000000fe074000 CPU: 0
GPR00: 0000000000000000 c0000000fe077a30 c0000000008387b8 c000000000862620 
GPR04: 00000000000080d0 0000000000000000 0000000000000001 0000000000001000 
GPR08: 0000000000000000 c000000000862620 00000000000080d0 c000000000862620 
GPR12: 00000000a6b962e2 c000000000874300 0000000000000000 c0000000005fd188 
GPR16: 4000000001c00000 c0000000005fba10 0000000000000000 00000000002b1000 
GPR20: 00000000022d9e68 c0000000006d9e68 00000000022da0d8 c0000000006da0d8 
GPR24: c0000000005fcbc8 00000000000080d0 c0000000000dda80 0000000000000001 
GPR28: 0000000000000000 c000000000989420 c0000000007b6600 bffffffffe02b000 
NIP [c0000000000d9a64] .__kmalloc_node+0xd4/0x144
LR [c0000000000d99f4] .__kmalloc_node+0x64/0x144
Call Trace:
[c0000000fe077a30] [c0000000fe077ac0] 0xc0000000fe077ac0 (unreliable)
[c0000000fe077ae0] [c0000000000dda80] .percpu_populate+0x90/0xd8
[c0000000fe077b70] [c0000000000ddb18] .__percpu_populate_mask+0x50/0xf4
[c0000000fe077c20] [c0000000000ddc30] .__percpu_alloc_mask+0x74/0x128
[c0000000fe077cc0] [c0000000006d135c] .ip_rt_init+0x60/0x2bc
[c0000000fe077d70] [c0000000006d1830] .ip_init+0x10/0x34
[c0000000fe077df0] [c0000000006d2610] .inet_init+0x164/0x3e4
[c0000000fe077ea0] [c0000000006a5c38] .kernel_init+0x1fc/0x3c0
[c0000000fe077f90] [c000000000025218] .kernel_thread+0x4c/0x68
Instruction dump:
7f80e000 41be0024 7d635b78 7f85e378 7f46d378 7f24cb78 7fa7eb78 4bffe289 
7c7f1b78 48000014 801d0014 78001f24 <7c1f002a> f81d0000 2fbb0000 38600000 
---[ end trace 8640abe69a316dee ]---
-- 
Thanks & Regards,
Kamalesh Babulal,
Linux Technology Center,
IBM, ISTL.

^ permalink raw reply

* Re: [BUG] 2.6.25-git8 Kernel Bug while bootup on ppc and x86
From: Adrian Bunk @ 2008-04-26 11:30 UTC (permalink / raw)
  To: Kamalesh Babulal
  Cc: kernel list, linuxppc-dev, Al Viro, Ingo Molnar, Balbir Singh
In-Reply-To: <48131052.2080102@linux.vnet.ibm.com>

On Sat, Apr 26, 2008 at 04:51:54PM +0530, Kamalesh Babulal wrote:
> While booting the 2.6.25-git8 kernel on the ppc and x86_64 machine, kernel
> bug is hit. This was reported in the next-20080423 kernel 
> http://lkml.org/lkml/2008/4/23/206.
> 
> Call trace of x86_64 machine
> 
> BUG: unable to handle kernel paging request at 00000000ffffffff
> IP: [<ffffffff802359e5>] put_files_struct+0x25/0x110
> PGD 0 
> Oops: 0002 [333] SMP 
> CPU 3 
> Modules linked in:
> Pid: 1391, comm: khelper Tainted: G      D  2.6.25-git8-autotest #1
> RIP: 0010:[<ffffffff802359e5>]  [<ffffffff802359e5>] put_files_struct+0x25/0x110
> RSP: 0000:ffff81090d06bdb0  EFLAGS: 00010282
> RAX: 0000000000000101 RBX: fffffffffffffffe RCX: ffff8100010260e0
> RDX: 0000000000000007 RSI: 0000000000000001 RDI: 00000000ffffffff
> RBP: 00000000fffffffe R08: 2222222222222267 R09: 2222222222222222
> R10: 0000000000000010 R11: 2222222222222222 R12: 00000000ffffffff
> R13: ffff81032d2e9000 R14: ffff81090e74b000 R15: ffff81061e4cbce0
> FS:  0000000000000000(0000) GS:ffff81032e4ae940(0000) knlGS:0000000000000000
> CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
> CR2: 00000000ffffffff CR3: 0000000000201000 CR4: 00000000000006e0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process khelper (pid: 1391, threadinfo ffff81090d06a000, task ffff81090d8113a0)
> Stack:  ffff81090d8113a0 fffffffffffffffe 00000000fffffffe ffff81032e556e00
>  ffff81032d2e9000 ffff81090e74b000 ffff81061e4cbce0 ffffffff80294278
>  ffff81090d06be70 ffffffff80636bc0 ffff81090d3646c0 ffff81032d2e9000
> Call Trace:
>  [<ffffffff80294278>] do_execve+0x108/0x230
>...

Most likely fixed by
  http://lkml.org/lkml/2008/4/26/1

> Thanks & Regards,
> Kamalesh Babulal,

cu
Adrian

-- 

       "Is there not promise of rain?" Ling Tan asked suddenly out
        of the darkness. There had been need of rain for many days.
       "Only a promise," Lao Er said.
                                       Pearl S. Buck - Dragon Seed

^ permalink raw reply

* Re: [BUG] 2.6.25-git8 Kernel Bug while bootup on ppc and x86
From: Kamalesh Babulal @ 2008-04-26 12:30 UTC (permalink / raw)
  To: Adrian Bunk; +Cc: kernel list, linuxppc-dev, Al Viro, Ingo Molnar, Balbir Singh
In-Reply-To: <20080426113040.GC2252@cs181133002.pp.htv.fi>

Adrian Bunk wrote:
> On Sat, Apr 26, 2008 at 04:51:54PM +0530, Kamalesh Babulal wrote:
>> While booting the 2.6.25-git8 kernel on the ppc and x86_64 machine, kernel
>> bug is hit. This was reported in the next-20080423 kernel 
>> http://lkml.org/lkml/2008/4/23/206.
>>
>> Call trace of x86_64 machine
>>
>> BUG: unable to handle kernel paging request at 00000000ffffffff
>> IP: [<ffffffff802359e5>] put_files_struct+0x25/0x110
>> PGD 0 
>> Oops: 0002 [333] SMP 
>> CPU 3 
>> Modules linked in:
>> Pid: 1391, comm: khelper Tainted: G      D  2.6.25-git8-autotest #1
>> RIP: 0010:[<ffffffff802359e5>]  [<ffffffff802359e5>] put_files_struct+0x25/0x110
>> RSP: 0000:ffff81090d06bdb0  EFLAGS: 00010282
>> RAX: 0000000000000101 RBX: fffffffffffffffe RCX: ffff8100010260e0
>> RDX: 0000000000000007 RSI: 0000000000000001 RDI: 00000000ffffffff
>> RBP: 00000000fffffffe R08: 2222222222222267 R09: 2222222222222222
>> R10: 0000000000000010 R11: 2222222222222222 R12: 00000000ffffffff
>> R13: ffff81032d2e9000 R14: ffff81090e74b000 R15: ffff81061e4cbce0
>> FS:  0000000000000000(0000) GS:ffff81032e4ae940(0000) knlGS:0000000000000000
>> CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
>> CR2: 00000000ffffffff CR3: 0000000000201000 CR4: 00000000000006e0
>> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
>> Process khelper (pid: 1391, threadinfo ffff81090d06a000, task ffff81090d8113a0)
>> Stack:  ffff81090d8113a0 fffffffffffffffe 00000000fffffffe ffff81032e556e00
>>  ffff81032d2e9000 ffff81090e74b000 ffff81061e4cbce0 ffffffff80294278
>>  ffff81090d06be70 ffffffff80636bc0 ffff81090d3646c0 ffff81032d2e9000
>> Call Trace:
>>  [<ffffffff80294278>] do_execve+0x108/0x230
>> ...
> 
> Most likely fixed by
>   http://lkml.org/lkml/2008/4/26/1

Hi Adrian,

Thanks, the patch fixes the boot up bug.

-- 
Thanks & Regards,
Kamalesh Babulal,
Linux Technology Center,
IBM, ISTL.

^ permalink raw reply

* Re: [PATCH1/7] i2c: Add support for device alias names
From: Jean Delvare @ 2008-04-26 20:38 UTC (permalink / raw)
  To: Jochen Friedrich; +Cc: Kernel, Linux, linuxppc-dev list, Scott Wood, Linux I2C
In-Reply-To: <47FF70A7.5070002@scram.de>

Hi Jochen,

On Fri, 11 Apr 2008 16:07:35 +0200, Jochen Friedrich wrote:
> Based on earlier work by Jon Smirl and Jean Delvare.
> 
> This patch allows new-style i2c chip drivers to have alias names using
> the official kernel aliasing system and MODULE_DEVICE_TABLE(). At this
> point, the old i2c driver binding scheme (driver_name/type) is still
> supported.
> 
> Signed-off-by: Jochen Friedrich <jochen@scram.de>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Jon Smirl <jonsmirl@gmail.com>
> ---
>  drivers/hwmon/f75375s.c                    |   21 ++++++++----
>  drivers/i2c/chips/ds1682.c                 |    3 +-
>  drivers/i2c/chips/menelaus.c               |    3 +-
>  drivers/i2c/chips/tps65010.c               |    3 +-
>  drivers/i2c/chips/tsl2550.c                |    3 +-
>  drivers/i2c/i2c-core.c                     |   51 +++++++++++++++++++++++-----
>  drivers/media/video/cs5345.c               |    3 +-
>  drivers/media/video/cs53l32a.c             |    3 +-
>  drivers/media/video/cx25840/cx25840-core.c |    3 +-
>  drivers/media/video/ivtv/ivtv-i2c.c        |    2 +-
>  drivers/media/video/m52790.c               |    3 +-
>  drivers/media/video/msp3400-driver.c       |    3 +-
>  drivers/media/video/saa7115.c              |    3 +-
>  drivers/media/video/saa7127.c              |    3 +-
>  drivers/media/video/tlv320aic23b.c         |    3 +-
>  drivers/media/video/tuner-core.c           |    3 +-
>  drivers/media/video/tvaudio.c              |    3 +-
>  drivers/media/video/upd64031a.c            |    3 +-
>  drivers/media/video/upd64083.c             |    3 +-
>  drivers/media/video/v4l2-common.c          |    5 ++-
>  drivers/media/video/vp27smpx.c             |    3 +-
>  drivers/media/video/wm8739.c               |    3 +-
>  drivers/media/video/wm8775.c               |    3 +-
>  drivers/rtc/rtc-ds1307.c                   |    3 +-
>  drivers/rtc/rtc-ds1374.c                   |    3 +-
>  drivers/rtc/rtc-m41t80.c                   |    3 +-
>  drivers/rtc/rtc-rs5c372.c                  |    3 +-
>  include/linux/i2c.h                        |    5 +--
>  include/linux/mod_devicetable.h            |   13 +++++++
>  include/media/v4l2-common.h                |    4 ++-
>  include/media/v4l2-i2c-drv-legacy.h        |    2 +-
>  include/media/v4l2-i2c-drv.h               |    2 +-
>  scripts/mod/file2alias.c                   |   13 +++++++
>  33 files changed, 139 insertions(+), 48 deletions(-)
> 
> diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
> index 1464338..3ec9123 100644
> --- a/drivers/hwmon/f75375s.c
> +++ b/drivers/hwmon/f75375s.c
> @@ -117,7 +117,8 @@ struct f75375_data {
>  static int f75375_attach_adapter(struct i2c_adapter *adapter);
>  static int f75375_detect(struct i2c_adapter *adapter, int address, int kind);
>  static int f75375_detach_client(struct i2c_client *client);
> -static int f75375_probe(struct i2c_client *client);
> +static int f75375_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id);
>  static int f75375_remove(struct i2c_client *client);
>  
>  static struct i2c_driver f75375_legacy_driver = {
> @@ -628,7 +629,8 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data,
>  
>  }
>  
> -static int f75375_probe(struct i2c_client *client)
> +static int f75375_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
>  {
>  	struct f75375_data *data = i2c_get_clientdata(client);
>  	struct f75375s_platform_data *f75375s_pdata = client->dev.platform_data;
> @@ -637,7 +639,8 @@ static int f75375_probe(struct i2c_client *client)
>  	if (!i2c_check_functionality(client->adapter,
>  				I2C_FUNC_SMBUS_BYTE_DATA))
>  		return -EIO;
> -	if (!(data = kzalloc(sizeof(struct f75375_data), GFP_KERNEL)))
> +	data = kzalloc(sizeof(struct f75375_data), GFP_KERNEL);
> +	if (!data)
>  		return -ENOMEM;
>  
>  	i2c_set_clientdata(client, data);
> @@ -653,7 +656,8 @@ static int f75375_probe(struct i2c_client *client)
>  		return -ENODEV;
>  	}
>  
> -	if ((err = sysfs_create_group(&client->dev.kobj, &f75375_group)))
> +	err = sysfs_create_group(&client->dev.kobj, &f75375_group);
> +	if (err)
>  		goto exit_free;
>  
>  	if (data->kind == f75375) {
> @@ -713,7 +717,8 @@ static int f75375_detect(struct i2c_adapter *adapter, int address, int kind)
>  	int err = 0;
>  	const char *name = "";
>  
> -	if (!(client = kzalloc(sizeof(*client), GFP_KERNEL))) {
> +	client = kzalloc(sizeof(*client), GFP_KERNEL);
> +	if (!client) {
>  		err = -ENOMEM;
>  		goto exit;
>  	}
> @@ -745,10 +750,12 @@ static int f75375_detect(struct i2c_adapter *adapter, int address, int kind)
>  	dev_info(&adapter->dev, "found %s version: %02X\n", name, version);
>  	strlcpy(client->name, name, I2C_NAME_SIZE);
>  
> -	if ((err = i2c_attach_client(client)))
> +	err = i2c_attach_client(client);
> +	if (err)
>  		goto exit_free;
>  
> -	if ((err = f75375_probe(client)) < 0)
> +	err = f75375_probe(client, NULL);
> +	if (err < 0)
>  		goto exit_detach;
>  
>  	return 0;

These coding style cleanups don't belong to this patch (if they belong
anywhere - I don't much see the point)

> (...)
> diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
> index fa5ab1e..37b0fd6 100644
> --- a/drivers/media/video/ivtv/ivtv-i2c.c
> +++ b/drivers/media/video/ivtv/ivtv-i2c.c
> @@ -167,7 +167,7 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
>  		return -1;
>  	id = hw_driverids[idx];
>  	memset(&info, 0, sizeof(info));
> -	strcpy(info.driver_name, hw_drivernames[idx]);
> +	strcpy(info.type, hw_drivernames[idx]);
>  	info.addr = hw_addrs[idx];
>  	for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
>  

This change should not be included in this patch. At this point, the old
binding model is still available, and I believe that the change above
will break ivtv, because all the device drivers it needs are still
using the old binding model (they don't define i2c device IDs yet.)

For the rest, there are a number of drivers which are missing:

drivers/gpio/pca953x.c
drivers/gpio/pcf857x.c
drivers/media/video/mt9m001.c
drivers/media/video/mt9v022.c
drivers/media/video/saa717x.c
drivers/media/video/tcm825x.c
drivers/rtc/rtc-s35390a.c

Either you missed them, or they were added after you updated the patch.
Either way, they need to be added. I hope I didn't miss any, it's hard
to make sure as some of them won't build on my development system
(x86-64).

Oh, and Documentation/i2c/writing-clients needs to be updated, too.

I'll add the missing chunks tomorrow, then I'll post an updated patch,
after I'm done with checking the 2nd patch of the set. I might leave
ivtv alone for the time being, as it's a complex one and I don't want
to break it for rc1.

-- 
Jean Delvare

^ permalink raw reply

* Re: sysfs cpu entry
From: Benjamin Herrenschmidt @ 2008-04-26 23:10 UTC (permalink / raw)
  To: Kevin Diggs; +Cc: linuxppc-dev
In-Reply-To: <4811593B.8080508@hypersurf.com>


On Thu, 2008-04-24 at 21:08 -0700, Kevin Diggs wrote:
> P.S.:  On an unrelated note, anyone know where to start looking for
> problems in pmac_zilog. My 8600 modem which worked fine in 2.4 is now
> essentially useless. Some problem with handshaking, I think.

It's possible that handshaking is broken, I remember some nasty tricks
with it on some Apple HW and I may have gotten that wrong... 

Ben.

^ permalink raw reply

* syslogd lockup or hang at startup
From: Dave Littell @ 2008-04-26 23:59 UTC (permalink / raw)
  To: linuxppc-embedded

Hi all,

I occasionally see syslogd hang at system startup.  The startup process
will issue a "failed (1:)" message after about 5 minutes and the system
will continue to boot, but syslogd is nowhere to be found.

Has anyone else seen this and (more importantly) know how to fix it?
One suggestion was to use "-m 0", but that didn't resolve this problem.


Thanks,
Dave

^ permalink raw reply

* Re: get_cycles()
From: Kevin Diggs @ 2008-04-27  2:39 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20080426.001102.65527296.davem@davemloft.net>

David Miller wrote:
> From: Kevin Diggs <kevdig@hypersurf.com>
> Date: Sat, 26 Apr 2008 00:54:11 -0700
> 
> 
>>	Anyone know how to turn get_cycles() into an actual time in a module? 
>>ppc_tb_freq does not seem to be exported?
> 
> 
> You should really be using ktime_t and associated interfaces.
> 
This looks pretty cool ... but I don't want to create a dependency on
hrtimer.

So ... How expensive would it be to export ppc_tb_freq? Or add a
get_cycles_tb() function?

kevin

^ permalink raw reply

* Re: mpc5200 arch=powerpc kernel=2.6.24: how do I request external IRQ0-3?
From: mtwallet @ 2008-04-27  1:58 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <161B3BAD77161449A144FF054231C3D60206D6DC@uss-am-xch-01.am.trimblecorp.net>


I got something working. I created a "device" child in the DTS file and used
th platform driver subsystem to register my module and get the virtual IRQ
as derived from the interrupt triplet-style spec in the dts file.

I'm still not real content as my child is nested with the SOC peripherals,
but this is more of a style/readability hang-up for me, for now. At least
now I can get the virtual irq by registering a platform driver, requesting
the irq number, and successfully requesting the irq.

I welcome any advice if I have used the dts file in an unintended fashion,
but it feels like the right place to specify a "device", even if all the
device embodies is an IRQ. Newbie conceptual difficulty overcome.



mtwallet wrote:
> 
> I am confused on a very simple subject: requesting an external IRQ from
> a custom driver I'm writing  for a lite5200b-based board running kernel
> 2.6.24, arch=powerpc. 
> 
>  
> 
> With the old arch=ppc I saw reference to MPC52xx_IRQ3 and such. I don't
> see reference to specific IRQs under the powerpc arch. I am starting to
> better understand the dts scheme for peripheral interrupts, but I am
> hung-up on the seemingly simple task of requesting an external IRQ and
> registering a callback from my driver:
> 
>  
> 
> request_irq(  virtual_irq_number_for_IRQ3_that_I_don't_know_right_now,
> &my_callback, flags, name, dev)
> 
>  
> 
> For this to work do I need to create a child node in the dts that
> specifies interrupts = <1 3 2> for IRQ3?  Must the IRQ I'm requesting be
> associated with the dev argument to request_irq via the dts?
> 
>  
> 
> I just think I'm missing something very obvious as regards using
> IRQ[0-3] on the mpc52xx. The DTS makes sens to me for peripheral
> interrupts getting associated with their respective peripherals, but
> when I want any old driver to use IRQ0-3 How do I request it?
> 
>  
> 
> Thanks.
> 
> 
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
> 

-- 
View this message in context: http://www.nabble.com/mpc5200-arch%3Dpowerpc-kernel%3D2.6.24%3A-how-do-I-request-external-IRQ0-3--tp16900805p16918127.html
Sent from the linuxppc-embedded mailing list archive at Nabble.com.

^ permalink raw reply

* Re: get_cycles()
From: David Miller @ 2008-04-27  3:21 UTC (permalink / raw)
  To: kevdig; +Cc: linuxppc-dev
In-Reply-To: <4813E74B.1020506@hypersurf.com>

From: Kevin Diggs <kevdig@hypersurf.com>
Date: Sat, 26 Apr 2008 19:39:07 -0700

> This looks pretty cool ... but I don't want to create a dependency on
> hrtimer.

It doesn't create such a dependency.

We use it unconditionally in the generic networking.

Please don't use platform specific interfaces if you don't have to.
You're be insulated from so many things.

^ permalink raw reply

* Re: mpc5200 arch=powerpc kernel=2.6.24: how do I request external IRQ0-3?
From: Grant Likely @ 2008-04-27  5:01 UTC (permalink / raw)
  To: mtwallet; +Cc: linuxppc-embedded
In-Reply-To: <16918127.post@talk.nabble.com>

On Sat, Apr 26, 2008 at 7:58 PM, mtwallet <mike_timmons@trimble.com> wrote:
>
>  I got something working. I created a "device" child in the DTS file and used
>  th platform driver subsystem to register my module and get the virtual IRQ
>  as derived from the interrupt triplet-style spec in the dts file.

Sorry I didn't get back to you right away.  Yes, the best/right thing
to do is put a node in your device tree to capture the interrupt and
other interfaces to your custom device.

You should consider using the of_platform infrastructure for
registering your device.  Your driver code can extract its register
locations and interrupts directly from a device tree node.  For an
example, look at the ulite_of_probe and ulite_of_driver in
driver/serial/uartlite.c.

>  I'm still not real content as my child is nested with the SOC peripherals,
>  but this is more of a style/readability hang-up for me, for now. At least
>  now I can get the virtual irq by registering a platform driver, requesting
>  the irq number, and successfully requesting the irq.
>
>  I welcome any advice if I have used the dts file in an unintended fashion,
>  but it feels like the right place to specify a "device", even if all the
>  device embodies is an IRQ. Newbie conceptual difficulty overcome.

Your part of the way there.  Your node should be a child of the device
that it is connected to.  Is it attached to the local bus?  or i2c?
It should have a "compatible" property so that your device driver can
find it (in the form 'compatible = "<manufacturer>,<device>";').  It
should have both an 'interrupt-parent' and an 'interrupts' property
for specifying the interrupt.  If the device is addressable, it should
have a 'reg' property.

You're right that it shouldn't be mixed in with the SoC nodes because
that doesn't accurately describe your platform.  But, it is perfectly
fine for it to be a child of one of the SoC nodes (for example if it
is attached to one of the i2c busses).

Cheers,
g.

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

^ permalink raw reply

* [patch v11 0/4] Cypress c67x00 (EZ-Host/EZ-OTG) support
From: Peter Korsgaard @ 2008-04-27  6:59 UTC (permalink / raw)
  To: dbrownell, linux-usb, linuxppc-dev, greg

The Cypress c67x00 (EZ-Host/EZ-OTG) controllers are multi-role low/fullspeed
USB controllers. This patch series implements a HCD driver and shows the
work-in-progress status of a gadget driver.

I believe patch 1..3 are ready, and I would like to see them queued up for
mainline.

Changes since v10:
 - Rebased against mainline, platform driver MODULE_ALIAS, checkpatch fixes
 - Gadget driver accesses hw directly instead of using BIOS and working around
   it's limitations. Still not merge ready though.

Changes since v9:
 - Put Kconfig in alphabetical order, use ccflags-$(CONFIG_USB_DEBUG)
   (feedback from David)

Changes since v8:
 - checkpatch.pl fixes (__FUNCTION__ -> __func__, indent)

Changes since v7:
 - Moved dummy hcd probe/remove functions for conditional hcd support into
   udc patch.
 - Moved usb_disabled() check into c67x00_hcd_probe().
 - Moved c67x00_get_comm_reg() to udc patch as it is only needed for gadget
   support.

Changes since v6:
 - Addressed David and Alan's comments (removed done list + tasklet)

Changes since v5:
 - Merged c67x00_ll_{get,set}_siemsg() into c67x00_ll_fetch_siemsg().
 - Fix for interrupt race condition at probe time (reported by Grant)

Changes since v4:
 - Addressed Grant's comments (c67x00_dev->c67x00_hcd_dev, label indent)
 - Moved c67x00_ll_set_ep_{ctrl,packet_size}_reg() to patch 4 as they are
   only needed for gadget support.

Changes since v3:
- Lots of cleanups: checkpatch, interrupt handling, c67x00_ prefixes, ..
- The dummy platform_device's created per serial engine are gone.
- Gadget driver (WIP)
-- 
Bye, Peter Korsgaard

^ permalink raw reply

* [patch v11 2/4] USB: add Cypress c67x00 OTG controller core driver
From: Peter Korsgaard @ 2008-04-27  6:59 UTC (permalink / raw)
  To: dbrownell, linux-usb, linuxppc-dev, greg
In-Reply-To: <20080427065942.514492739@sunsite.dk>

This patch add the core driver for the c67x00 USB OTG controller.  The core
driver is responsible for the platform bus binding and creating either
USB HCD or USB Gadget instances for each of the serial interface engines
on the chip.

This driver does not directly implement the HCD or gadget behaviours; it
just controls access to the chip.

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
---
 MAINTAINERS                     |    6 +
 drivers/usb/c67x00/c67x00-drv.c |  230 ++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/c67x00.h      |   48 ++++++++
 3 files changed, 284 insertions(+)

Index: linux-2.6/drivers/usb/c67x00/c67x00-drv.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-drv.c
@@ -0,0 +1,230 @@
+/*
+ * c67x00-drv.c: Cypress C67X00 USB Common infrastructure
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+/*
+ * This file implements the common infrastructure for using the c67x00.
+ * It is both the link between the platform configuration and subdrivers and
+ * the link between the common hardware parts and the subdrivers (e.g.
+ * interrupt handling).
+ *
+ * The c67x00 has 2 SIE's (serial interface engine) wich can be configured
+ * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG).
+ *
+ * Depending on the platform configuration, the SIE's are created and
+ * the corresponding subdriver is initialized (c67x00_probe_sie).
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/usb/c67x00.h>
+
+#include "c67x00.h"
+
+static void c67x00_probe_sie(struct c67x00_sie *sie,
+			     struct c67x00_device *dev, int sie_num)
+{
+	spin_lock_init(&sie->lock);
+	sie->dev = dev;
+	sie->sie_num = sie_num;
+	sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
+
+	switch (sie->mode) {
+	case C67X00_SIE_UNUSED:
+		dev_info(sie_dev(sie),
+			 "Not using SIE %d as requested\n", sie->sie_num);
+		break;
+
+	default:
+		dev_err(sie_dev(sie),
+			"Unsupported configuration: 0x%x for SIE %d\n",
+			sie->mode, sie->sie_num);
+		break;
+	}
+}
+
+static void c67x00_remove_sie(struct c67x00_sie *sie)
+{
+}
+
+static irqreturn_t c67x00_irq(int irq, void *__dev)
+{
+	struct c67x00_device *c67x00 = __dev;
+	struct c67x00_sie *sie;
+	u16 msg, int_status;
+	int i, count = 8;
+
+	int_status = c67x00_ll_hpi_status(c67x00);
+	if (!int_status)
+		return IRQ_NONE;
+
+	while (int_status != 0 && (count-- >= 0)) {
+		c67x00_ll_irq(c67x00, int_status);
+		for (i = 0; i < C67X00_SIES; i++) {
+			sie = &c67x00->sie[i];
+			msg = 0;
+			if (int_status & SIEMSG_FLG(i))
+				msg = c67x00_ll_fetch_siemsg(c67x00, i);
+			if (sie->irq)
+				sie->irq(sie, int_status, msg);
+		}
+		int_status = c67x00_ll_hpi_status(c67x00);
+	}
+
+	if (int_status)
+		dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
+			 "status = 0x%04x\n", int_status);
+
+	return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int __devinit c67x00_drv_probe(struct platform_device *pdev)
+{
+	struct c67x00_device *c67x00;
+	struct c67x00_platform_data *pdata;
+	struct resource *res, *res2;
+	int ret, i;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res2)
+		return -ENODEV;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENODEV;
+
+	c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL);
+	if (!c67x00)
+		return -ENOMEM;
+
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+				pdev->name)) {
+		dev_err(&pdev->dev, "Memory region busy\n");
+		ret = -EBUSY;
+		goto request_mem_failed;
+	}
+	c67x00->hpi.base = ioremap(res->start, res->end - res->start + 1);
+	if (!c67x00->hpi.base) {
+		dev_err(&pdev->dev, "Unable to map HPI registers\n");
+		ret = -EIO;
+		goto map_failed;
+	}
+
+	spin_lock_init(&c67x00->hpi.lock);
+	c67x00->hpi.regstep = pdata->hpi_regstep;
+	c67x00->pdata = pdev->dev.platform_data;
+	c67x00->pdev = pdev;
+
+	c67x00_ll_init(c67x00);
+	c67x00_ll_hpi_reg_init(c67x00);
+
+	ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot claim IRQ\n");
+		goto request_irq_failed;
+	}
+
+	ret = c67x00_ll_reset(c67x00);
+	if (ret) {
+		dev_err(&pdev->dev, "Device reset failed\n");
+		goto reset_failed;
+	}
+
+	for (i = 0; i < C67X00_SIES; i++)
+		c67x00_probe_sie(&c67x00->sie[i], c67x00, i);
+
+	platform_set_drvdata(pdev, c67x00);
+
+	return 0;
+
+ reset_failed:
+	free_irq(res2->start, c67x00);
+ request_irq_failed:
+	iounmap(c67x00->hpi.base);
+ map_failed:
+	release_mem_region(res->start, res->end - res->start + 1);
+ request_mem_failed:
+	kfree(c67x00);
+
+	return ret;
+}
+
+static int __devexit c67x00_drv_remove(struct platform_device *pdev)
+{
+	struct c67x00_device *c67x00 = platform_get_drvdata(pdev);
+	struct resource *res;
+	int i;
+
+	for (i = 0; i < C67X00_SIES; i++)
+		c67x00_remove_sie(&c67x00->sie[i]);
+
+	c67x00_ll_release(c67x00);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res)
+		free_irq(res->start, c67x00);
+
+	iounmap(c67x00->hpi.base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, res->end - res->start + 1);
+
+	kfree(c67x00);
+
+	return 0;
+}
+
+static struct platform_driver c67x00_driver = {
+	.probe	= c67x00_drv_probe,
+	.remove	= __devexit_p(c67x00_drv_remove),
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name = "c67x00",
+	},
+};
+MODULE_ALIAS("platform:c67x00");
+
+static int __init c67x00_init(void)
+{
+	return platform_driver_register(&c67x00_driver);
+}
+
+static void __exit c67x00_exit(void)
+{
+	platform_driver_unregister(&c67x00_driver);
+}
+
+module_init(c67x00_init);
+module_exit(c67x00_exit);
+
+MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
+MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6/include/linux/usb/c67x00.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/usb/c67x00.h
@@ -0,0 +1,48 @@
+/*
+ * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#ifndef _LINUX_USB_C67X00_H
+#define _LINUX_USB_C67X00_H
+
+/* SIE configuration */
+#define C67X00_SIE_UNUSED	0
+#define C67X00_SIE_HOST		1
+#define C67X00_SIE_PERIPHERAL_A	2	/* peripheral on A port */
+#define C67X00_SIE_PERIPHERAL_B	3	/* peripheral on B port */
+
+#define c67x00_sie_config(config, n)  (((config)>>(4*(n)))&0x3)
+
+#define C67X00_SIE1_UNUSED	        (C67X00_SIE_UNUSED		<< 0)
+#define C67X00_SIE1_HOST	        (C67X00_SIE_HOST		<< 0)
+#define C67X00_SIE1_PERIPHERAL_A	(C67X00_SIE_PERIPHERAL_A	<< 0)
+#define C67X00_SIE1_PERIPHERAL_B	(C67X00_SIE_PERIPHERAL_B	<< 0)
+
+#define C67X00_SIE2_UNUSED		(C67X00_SIE_UNUSED		<< 4)
+#define C67X00_SIE2_HOST		(C67X00_SIE_HOST		<< 4)
+#define C67X00_SIE2_PERIPHERAL_A	(C67X00_SIE_PERIPHERAL_A	<< 4)
+#define C67X00_SIE2_PERIPHERAL_B	(C67X00_SIE_PERIPHERAL_B	<< 4)
+
+struct c67x00_platform_data {
+	int sie_config;			/* SIEs config (C67X00_SIEx_*) */
+	unsigned long hpi_regstep;	/* Step between HPI registers  */
+};
+
+#endif /* _LINUX_USB_C67X00_H */
Index: linux-2.6/MAINTAINERS
===================================================================
--- linux-2.6.orig/MAINTAINERS
+++ linux-2.6/MAINTAINERS
@@ -4010,6 +4010,12 @@
 S:	Maintained
 W:	http://www.kroah.com/linux-usb/
 
+USB CYPRESS C67X00 DRIVER
+P:	Peter Korsgaard
+M:	jacmet@sunsite.dk
+L:	linux-usb@vger.kernel.org
+S:	Maintained
+
 USB DAVICOM DM9601 DRIVER
 P:	Peter Korsgaard
 M:	jacmet@sunsite.dk

-- 
Bye, Peter Korsgaard

^ permalink raw reply

* [patch v11 1/4] USB: add Cypress c67x00 low level interface code
From: Peter Korsgaard @ 2008-04-27  6:59 UTC (permalink / raw)
  To: dbrownell, linux-usb, linuxppc-dev, greg
In-Reply-To: <20080427065942.514492739@sunsite.dk>

This patch adds the low level support code for the Cypress c67x00 family of
OTG controllers.  The low level code is responsible for register access and
implements the software protocol for communicating with the 16bit
microcontroller inside the c67x00 device.

Communication is done over the HPI interface (16bit SRAM-like parallel bus).

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/usb/c67x00/c67x00-ll-hpi.c |  405 +++++++++++++++++++++++++++++++++++++
 drivers/usb/c67x00/c67x00.h        |  285 ++++++++++++++++++++++++++
 2 files changed, 690 insertions(+)

Index: linux-2.6/drivers/usb/c67x00/c67x00.h
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00.h
@@ -0,0 +1,285 @@
+/*
+ * c67x00.h: Cypress C67X00 USB register and field definitions
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#ifndef _USB_C67X00_H
+#define _USB_C67X00_H
+
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+
+/* ---------------------------------------------------------------------
+ * Cypress C67x00 register definitions
+ */
+
+/* Hardware Revision Register */
+#define HW_REV_REG		0xC004
+
+/* General USB registers */
+/* ===================== */
+
+/* USB Control Register */
+#define USB_CTL_REG(x)		((x) ? 0xC0AA : 0xC08A)
+
+#define LOW_SPEED_PORT(x)	((x) ? 0x0800 : 0x0400)
+#define HOST_MODE		0x0200
+#define PORT_RES_EN(x)		((x) ? 0x0100 : 0x0080)
+#define SOF_EOP_EN(x)		((x) ? 0x0002 : 0x0001)
+
+/* USB status register - Notice it has different content in hcd/udc mode */
+#define USB_STAT_REG(x)		((x) ? 0xC0B0 : 0xC090)
+
+#define EP0_IRQ_FLG		0x0001
+#define EP1_IRQ_FLG		0x0002
+#define EP2_IRQ_FLG		0x0004
+#define EP3_IRQ_FLG		0x0008
+#define EP4_IRQ_FLG		0x0010
+#define EP5_IRQ_FLG		0x0020
+#define EP6_IRQ_FLG		0x0040
+#define EP7_IRQ_FLG		0x0080
+#define RESET_IRQ_FLG		0x0100
+#define SOF_EOP_IRQ_FLG		0x0200
+#define ID_IRQ_FLG		0x4000
+#define VBUS_IRQ_FLG		0x8000
+
+/* USB Host only registers */
+/* ======================= */
+
+/* Host n Control Register */
+#define HOST_CTL_REG(x)		((x) ? 0xC0A0 : 0xC080)
+
+#define PREAMBLE_EN		0x0080	/* Preamble enable */
+#define SEQ_SEL			0x0040	/* Data Toggle Sequence Bit Select */
+#define ISO_EN			0x0010	/* Isochronous enable  */
+#define ARM_EN			0x0001	/* Arm operation */
+
+/* Host n Interrupt Enable Register */
+#define HOST_IRQ_EN_REG(x)	((x) ? 0xC0AC : 0xC08C)
+
+#define SOF_EOP_IRQ_EN		0x0200	/* SOF/EOP Interrupt Enable  */
+#define SOF_EOP_TMOUT_IRQ_EN	0x0800	/* SOF/EOP Timeout Interrupt Enable  */
+#define ID_IRQ_EN		0x4000	/* ID interrupt enable */
+#define VBUS_IRQ_EN		0x8000	/* VBUS interrupt enable */
+#define DONE_IRQ_EN		0x0001	/* Done Interrupt Enable  */
+
+/* USB status register */
+#define HOST_STAT_MASK		0x02FD
+#define PORT_CONNECT_CHANGE(x)	((x) ? 0x0020 : 0x0010)
+#define PORT_SE0_STATUS(x)	((x) ? 0x0008 : 0x0004)
+
+/* Host Frame Register */
+#define HOST_FRAME_REG(x)	((x) ? 0xC0B6 : 0xC096)
+
+#define HOST_FRAME_MASK		0x07FF
+
+/* USB Peripheral only registers */
+/* ============================= */
+
+/* Device n Port Sel reg */
+#define DEVICE_N_PORT_SEL(x)	((x) ? 0xC0A4 : 0xC084)
+
+/* Device n Interrupt Enable Register */
+#define DEVICE_N_IRQ_EN_REG(x)	((x) ? 0xC0AC : 0xC08C)
+
+#define DEVICE_N_ENDPOINT_N_CTL_REG(dev, ep)	((dev)  		\
+						 ? (0x0280 + (ep << 4)) \
+						 : (0x0200 + (ep << 4)))
+#define DEVICE_N_ENDPOINT_N_STAT_REG(dev, ep)	((dev)			\
+						 ? (0x0286 + (ep << 4)) \
+						 : (0x0206 + (ep << 4)))
+
+#define DEVICE_N_ADDRESS(dev)	((dev) ? (0xC0AE) : (0xC08E))
+
+/* HPI registers */
+/* ============= */
+
+/* HPI Status register */
+#define SOFEOP_FLG(x)		(1 << ((x) ? 12 : 10))
+#define SIEMSG_FLG(x)		(1 << (4 + (x)))
+#define RESET_FLG(x)		((x) ? 0x0200 : 0x0002)
+#define DONE_FLG(x)		(1 << (2 + (x)))
+#define RESUME_FLG(x)		(1 << (6 + (x)))
+#define MBX_OUT_FLG		0x0001	/* Message out available */
+#define MBX_IN_FLG		0x0100
+#define ID_FLG			0x4000
+#define VBUS_FLG		0x8000
+
+/* Interrupt routing register */
+#define HPI_IRQ_ROUTING_REG	0x0142
+
+#define HPI_SWAP_ENABLE(x)	((x) ? 0x0100 : 0x0001)
+#define RESET_TO_HPI_ENABLE(x)	((x) ? 0x0200 : 0x0002)
+#define DONE_TO_HPI_ENABLE(x)	((x) ? 0x0008 : 0x0004)
+#define RESUME_TO_HPI_ENABLE(x)	((x) ? 0x0080 : 0x0040)
+#define SOFEOP_TO_HPI_EN(x)	((x) ? 0x2000 : 0x0800)
+#define SOFEOP_TO_CPU_EN(x)	((x) ? 0x1000 : 0x0400)
+#define ID_TO_HPI_ENABLE	0x4000
+#define VBUS_TO_HPI_ENABLE	0x8000
+
+/* SIE msg registers */
+#define SIEMSG_REG(x)		((x) ? 0x0148 : 0x0144)
+
+#define HUSB_TDListDone		0x1000
+
+#define SUSB_EP0_MSG		0x0001
+#define SUSB_EP1_MSG		0x0002
+#define SUSB_EP2_MSG		0x0004
+#define SUSB_EP3_MSG		0x0008
+#define SUSB_EP4_MSG		0x0010
+#define SUSB_EP5_MSG		0x0020
+#define SUSB_EP6_MSG		0x0040
+#define SUSB_EP7_MSG		0x0080
+#define SUSB_RST_MSG		0x0100
+#define SUSB_SOF_MSG		0x0200
+#define SUSB_CFG_MSG		0x0400
+#define SUSB_SUS_MSG		0x0800
+#define SUSB_ID_MSG	       	0x4000
+#define SUSB_VBUS_MSG		0x8000
+
+/* BIOS interrupt routines */
+
+#define SUSBx_RECEIVE_INT(x)	((x) ? 97 : 81)
+#define SUSBx_SEND_INT(x)	((x) ? 96 : 80)
+
+#define SUSBx_DEV_DESC_VEC(x)	((x) ? 0x00D4 : 0x00B4)
+#define SUSBx_CONF_DESC_VEC(x)	((x) ? 0x00D6 : 0x00B6)
+#define SUSBx_STRING_DESC_VEC(x) ((x) ? 0x00D8 : 0x00B8)
+
+#define CY_HCD_BUF_ADDR		0x500	/* Base address for host */
+#define SIE_TD_SIZE		0x200	/* size of the td list */
+#define SIE_TD_BUF_SIZE		0x400	/* size of the data buffer */
+
+#define SIE_TD_OFFSET(host)	((host) ? (SIE_TD_SIZE+SIE_TD_BUF_SIZE) : 0)
+#define SIE_BUF_OFFSET(host)	(SIE_TD_OFFSET(host) + SIE_TD_SIZE)
+
+/* Base address of HCD + 2 x TD_SIZE + 2 x TD_BUF_SIZE */
+#define CY_UDC_REQ_HEADER_BASE	0x1100
+/* 8- byte request headers for IN/OUT transfers */
+#define CY_UDC_REQ_HEADER_SIZE	8
+
+#define CY_UDC_REQ_HEADER_ADDR(ep_num)	(CY_UDC_REQ_HEADER_BASE + \
+					 ((ep_num) * CY_UDC_REQ_HEADER_SIZE))
+#define CY_UDC_DESC_BASE_ADDRESS	(CY_UDC_REQ_HEADER_ADDR(8))
+
+#define CY_UDC_BIOS_REPLACE_BASE	0x1800
+#define CY_UDC_REQ_BUFFER_BASE		0x2000
+#define CY_UDC_REQ_BUFFER_SIZE		0x0400
+#define CY_UDC_REQ_BUFFER_ADDR(ep_num)	(CY_UDC_REQ_BUFFER_BASE + \
+					 ((ep_num) * CY_UDC_REQ_BUFFER_SIZE))
+
+/* ---------------------------------------------------------------------
+ * Driver data structures
+ */
+
+struct c67x00_device;
+
+/**
+ * struct c67x00_sie - Common data associated with a SIE
+ * @lock: lock to protect this struct and the associated chip registers
+ * @private_data: subdriver dependent data
+ * @irq: subdriver dependent irq handler, set NULL when not used
+ * @dev: link to common driver structure
+ * @sie_num: SIE number on chip, starting from 0
+ * @mode: SIE mode (host/peripheral/otg/not used)
+ */
+struct c67x00_sie {
+	/* Entries to be used by the subdrivers */
+	spinlock_t lock;	/* protect this structure */
+	void *private_data;
+	void (*irq) (struct c67x00_sie *sie, u16 int_status, u16 msg);
+
+	/* Read only: */
+	struct c67x00_device *dev;
+	int sie_num;
+	int mode;
+};
+
+#define sie_dev(s)	(&(s)->dev->pdev->dev)
+
+/**
+ * struct c67x00_lcp
+ */
+struct c67x00_lcp {
+	/* Internal use only */
+	struct mutex mutex;
+	struct completion msg_received;
+	u16 last_msg;
+};
+
+/*
+ * struct c67x00_hpi
+ */
+struct c67x00_hpi {
+	void __iomem *base;
+	int regstep;
+	spinlock_t lock;
+	struct c67x00_lcp lcp;
+};
+
+#define C67X00_SIES	2
+#define C67X00_PORTS	2
+
+/**
+ * struct c67x00_device - Common data associated with a c67x00 instance
+ * @hpi: hpi addresses
+ * @sie: array of sie's on this chip
+ * @pdev: platform device of instance
+ * @pdata: configuration provided by the platform
+ */
+struct c67x00_device {
+	struct c67x00_hpi hpi;
+	struct c67x00_sie sie[C67X00_SIES];
+	struct platform_device *pdev;
+	struct c67x00_platform_data *pdata;
+};
+
+/* ---------------------------------------------------------------------
+ * Low level interface functions
+ */
+
+/* Host Port Interface (HPI) functions */
+u16 c67x00_ll_hpi_status(struct c67x00_device *dev);
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev);
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie);
+void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie);
+
+/* General functions */
+u16 c67x00_ll_fetch_siemsg(struct c67x00_device *dev, int sie_num);
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie);
+void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits);
+u16 c67x00_ll_usb_get_status(struct c67x00_sie *sie);
+void c67x00_ll_write_mem_le16(struct c67x00_device *dev, u16 addr,
+			      void *data, int len);
+void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
+			     void *data, int len);
+
+/* Called by c67x00_irq to handle lcp interrupts */
+void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status);
+
+/* Setup and teardown */
+void c67x00_ll_init(struct c67x00_device *dev);
+void c67x00_ll_release(struct c67x00_device *dev);
+int c67x00_ll_reset(struct c67x00_device *dev);
+
+#endif				/* _USB_C67X00_H */
Index: linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
@@ -0,0 +1,405 @@
+/*
+ * c67x00-ll-hpi.c: Cypress C67X00 USB Low level interface using HPI
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/usb/c67x00.h>
+#include "c67x00.h"
+
+#define COMM_REGS 14
+
+struct c67x00_lcp_int_data {
+	u16 regs[COMM_REGS];
+};
+
+/* -------------------------------------------------------------------------- */
+/* Interface definitions */
+
+#define COMM_ACK			0x0FED
+#define COMM_NAK			0xDEAD
+
+#define COMM_RESET			0xFA50
+#define COMM_EXEC_INT			0xCE01
+#define COMM_INT_NUM			0x01C2
+
+/* Registers 0 to COMM_REGS-1 */
+#define COMM_R(x)			(0x01C4 + 2 * (x))
+
+#define HUSB_SIE_pCurrentTDPtr(x)	((x) ? 0x01B2 : 0x01B0)
+#define HUSB_SIE_pTDListDone_Sem(x)	((x) ? 0x01B8 : 0x01B6)
+#define HUSB_pEOT			0x01B4
+
+/* Software interrupts */
+/* 114, 115: */
+#define HUSB_SIE_INIT_INT(x)		((x) ? 0x0073 : 0x0072)
+#define HUSB_RESET_INT			0x0074
+
+#define SUSB_INIT_INT			0x0071
+#define SUSB_INIT_INT_LOC		(SUSB_INIT_INT * 2)
+
+/* -----------------------------------------------------------------------
+ * HPI implementation
+ *
+ * The c67x00 chip also support control via SPI or HSS serial
+ * interfaces.  However, this driver assumes that register access can
+ * be performed from IRQ context.  While this is a safe assuption with
+ * the HPI interface, it is not true for the serial interfaces.
+ */
+
+/* HPI registers */
+#define HPI_DATA	0
+#define HPI_MAILBOX	1
+#define HPI_ADDR	2
+#define HPI_STATUS	3
+
+static inline u16 hpi_read_reg(struct c67x00_device *dev, int reg)
+{
+	return __raw_readw(dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline void hpi_write_reg(struct c67x00_device *dev, int reg, u16 value)
+{
+	__raw_writew(value, dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline u16 hpi_read_word_nolock(struct c67x00_device *dev, u16 reg)
+{
+	hpi_write_reg(dev, HPI_ADDR, reg);
+	return hpi_read_reg(dev, HPI_DATA);
+}
+
+static u16 hpi_read_word(struct c67x00_device *dev, u16 reg)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_word_nolock(dev, reg);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+static void hpi_write_word_nolock(struct c67x00_device *dev, u16 reg, u16 value)
+{
+	hpi_write_reg(dev, HPI_ADDR, reg);
+	hpi_write_reg(dev, HPI_DATA, value);
+}
+
+static void hpi_write_word(struct c67x00_device *dev, u16 reg, u16 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	hpi_write_word_nolock(dev, reg, value);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static void hpi_write_words_le16(struct c67x00_device *dev, u16 addr,
+				 u16 *data, u16 count)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+
+	hpi_write_reg(dev, HPI_ADDR, addr);
+	for (i = 0; i < count; i++)
+		hpi_write_reg(dev, HPI_DATA, cpu_to_le16(*data++));
+
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static void hpi_read_words_le16(struct c67x00_device *dev, u16 addr,
+				u16 *data, u16 count)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	hpi_write_reg(dev, HPI_ADDR, addr);
+	for (i = 0; i < count; i++)
+		*data++ = le16_to_cpu(hpi_read_reg(dev, HPI_DATA));
+
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+static void hpi_set_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_word_nolock(dev, reg);
+	hpi_write_word_nolock(dev, reg, value | mask);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+static void hpi_clear_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_word_nolock(dev, reg);
+	hpi_write_word_nolock(dev, reg, value & ~mask);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+static u16 hpi_recv_mbox(struct c67x00_device *dev)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_reg(dev, HPI_MAILBOX);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+static u16 hpi_send_mbox(struct c67x00_device *dev, u16 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	hpi_write_reg(dev, HPI_MAILBOX, value);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+u16 c67x00_ll_hpi_status(struct c67x00_device *dev)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_reg(dev, HPI_STATUS);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev)
+{
+	int i;
+
+	hpi_recv_mbox(dev);
+	c67x00_ll_hpi_status(dev);
+	hpi_write_word(dev, HPI_IRQ_ROUTING_REG, 0);
+
+	for (i = 0; i < C67X00_SIES; i++) {
+		hpi_write_word(dev, SIEMSG_REG(i), 0);
+		hpi_read_word(dev, SIEMSG_REG(i));
+	}
+}
+
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie)
+{
+	hpi_set_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+		     SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie)
+{
+	hpi_clear_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+		       SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+/* Transactions */
+
+static inline u16 ll_recv_msg(struct c67x00_device *dev)
+{
+	u16 res;
+
+	res = wait_for_completion_timeout(&dev->hpi.lcp.msg_received, 5 * HZ);
+	WARN_ON(!res);
+
+	return (res == 0) ? -EIO : 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* General functions */
+
+u16 c67x00_ll_fetch_siemsg(struct c67x00_device *dev, int sie_num)
+{
+	u16 val;
+
+	val = hpi_read_word(dev, SIEMSG_REG(sie_num));
+	/* clear register to allow next message */
+	hpi_write_word(dev, SIEMSG_REG(sie_num), 0);
+
+	return val;
+}
+
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num));
+}
+
+/**
+ * c67x00_ll_usb_clear_status - clear the USB status bits
+ */
+void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits)
+{
+	hpi_write_word(sie->dev, USB_STAT_REG(sie->sie_num), bits);
+}
+
+u16 c67x00_ll_usb_get_status(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, USB_STAT_REG(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int c67x00_comm_exec_int(struct c67x00_device *dev, u16 nr,
+				struct c67x00_lcp_int_data *data)
+{
+	int i, rc;
+
+	mutex_lock(&dev->hpi.lcp.mutex);
+	hpi_write_word(dev, COMM_INT_NUM, nr);
+	for (i = 0; i < COMM_REGS; i++)
+		hpi_write_word(dev, COMM_R(i), data->regs[i]);
+	hpi_send_mbox(dev, COMM_EXEC_INT);
+	rc = ll_recv_msg(dev);
+	mutex_unlock(&dev->hpi.lcp.mutex);
+
+	return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status)
+{
+	if ((int_status & MBX_OUT_FLG) == 0)
+		return;
+
+	dev->hpi.lcp.last_msg = hpi_recv_mbox(dev);
+	complete(&dev->hpi.lcp.msg_received);
+}
+
+/* -------------------------------------------------------------------------- */
+
+int c67x00_ll_reset(struct c67x00_device *dev)
+{
+	int rc;
+
+	mutex_lock(&dev->hpi.lcp.mutex);
+	hpi_send_mbox(dev, COMM_RESET);
+	rc = ll_recv_msg(dev);
+	mutex_unlock(&dev->hpi.lcp.mutex);
+
+	return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_ll_write_mem_le16 - write into c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_write_mem_le16(struct c67x00_device *dev, u16 addr,
+			      void *data, int len)
+{
+	u8 *buf = data;
+
+	/* Sanity check */
+	if (addr + len > 0xffff) {
+		dev_err(&dev->pdev->dev,
+			"Trying to write beyond writable region!\n");
+		return;
+	}
+
+	if (addr & 0x01) {
+		/* unaligned access */
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr - 1);
+		tmp = (tmp & 0x00ff) | (*buf++ << 8);
+		hpi_write_word(dev, addr - 1, tmp);
+		addr++;
+		len--;
+	}
+
+	hpi_write_words_le16(dev, addr, (u16 *)buf, len / 2);
+	buf += len & ~0x01;
+	addr += len & ~0x01;
+	len &= 0x01;
+
+	if (len) {
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr);
+		tmp = (tmp & 0xff00) | *buf;
+		hpi_write_word(dev, addr, tmp);
+	}
+}
+
+/**
+ * c67x00_ll_read_mem_le16 - read from c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
+			     void *data, int len)
+{
+	u8 *buf = data;
+
+	if (addr & 0x01) {
+		/* unaligned access */
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr - 1);
+		*buf++ = (tmp >> 8) & 0x00ff;
+		addr++;
+		len--;
+	}
+
+	hpi_read_words_le16(dev, addr, (u16 *)buf, len / 2);
+	buf += len & ~0x01;
+	addr += len & ~0x01;
+	len &= 0x01;
+
+	if (len) {
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr);
+		*buf = tmp & 0x00ff;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_init(struct c67x00_device *dev)
+{
+	mutex_init(&dev->hpi.lcp.mutex);
+	init_completion(&dev->hpi.lcp.msg_received);
+}
+
+void c67x00_ll_release(struct c67x00_device *dev)
+{
+}

-- 
Bye, Peter Korsgaard

^ permalink raw reply

* [patch v11 4/4] USB: add Cypress c67x00 OTG controller gadget driver
From: Peter Korsgaard @ 2008-04-27  6:59 UTC (permalink / raw)
  To: dbrownell, linux-usb, linuxppc-dev, greg
In-Reply-To: <20080427065942.514492739@sunsite.dk>

This patch adds USB gadget support for the Cypress c67x00 family of devices.

This is work in progress and not ready to be committed yet. I'm posting this
to show how it fits with the rest of the driver and to collect feedback.

The driver works good enought to use g_serial, but there are still issues
to be solved. The biggest issue is that endpoint 0 is currently handled by
the BIOS inside the c67x00, so the gadget stack never sees the data.
The BIOS also has other deficiencies, E.G. see the patching done in
c67x00_ll_susb_init().
---
 drivers/usb/Kconfig                |    2 
 drivers/usb/Makefile               |    2 
 drivers/usb/c67x00/Kconfig         |   21 
 drivers/usb/c67x00/Makefile        |    7 
 drivers/usb/c67x00/c67x00-drv.c    |   11 
 drivers/usb/c67x00/c67x00-hcd.h    |   13 
 drivers/usb/c67x00/c67x00-ll-hpi.c |  204 ++++++
 drivers/usb/c67x00/c67x00-udc.c    | 1167 +++++++++++++++++++++++++++++++++++++
 drivers/usb/c67x00/c67x00-udc.h    |   48 +
 drivers/usb/c67x00/c67x00.h        |  107 ++-
 drivers/usb/gadget/Kconfig         |    7 
 drivers/usb/gadget/gadget_chips.h  |    8 
 drivers/usb/host/Kconfig           |   13 
 13 files changed, 1561 insertions(+), 49 deletions(-)

Index: linux-2.6/drivers/usb/c67x00/c67x00-udc.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-udc.c
@@ -0,0 +1,1167 @@
+/*
+ * c67x00-udc.c: Cypress C67X00 USB device controller
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple device controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <linux/device.h>
+#include <linux/usb.h>
+#include <linux/usb/c67x00.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/proc_fs.h>
+
+#include "c67x00.h"
+#include "c67x00-udc.h"
+
+/* Defined in DEVICE n ENDPOINT STATUS REGISTERS */
+#define OVERFLOW_FLG		0x0800	/* Receive overflow */
+#define UNDERFLOW_FLG		0x0400	/* Receive underflow */
+#define OUT_EXCEPTION_FLG	0x0200	/* OUT received when armed for IN */
+#define IN_EXCEPTION_FLG	0x0100	/* IN received when armed for OUT */
+#define STALL_FLG		0x0080	/* Stall sent */
+#define NAK_FLG			0x0040	/* NAK sent */
+#define LENGTH_EXCEPT_FLG	0x0020	/* Overflow or Underflow occured */
+#define SETUP_FLG		0x0010	/* SETUP packet received */
+#define SEQ_STAT		0x0008	/* Last Data Toggle Sequence bit sent
+						or received */
+#define TIMEOUT_FLG		0x0004	/* Last transmission timed out */
+#define ERROR_FLG		0x0002	/* CRC Err detected in last
+						reception*/
+#define ACK_FLG			0x0001	/* Last transaction ACK'D (sent
+						or received) */
+
+/* Defined in DEVICE n ENDPOINT CONTROL REGISTERS */
+#define DIR_SEL_IN	0x0004	/* Last transmission timed out */
+#define EP_ENABLE	0x0002	/* Enable Endpoint */
+
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+
+struct c67x00_request {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+
+
+struct c67x00_udc_ep {
+	struct usb_ep ep;
+	struct c67x00_udc *udc;
+
+	struct list_head queue;
+	int ep_num;
+	int is_ep_in;
+	int enable;
+	int stopped;
+	int start_io;
+	int datatoggle;
+	int xfer_size;
+	/* could add descriptors here */
+};
+
+#define C67X00_MAX_NB_END_POINTS 8
+
+struct c67x00_udc {
+	spinlock_t lock;
+	struct c67x00_sie *sie;
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct c67x00_udc_ep ep[C67X00_MAX_NB_END_POINTS];
+	struct work_struct io_work;
+	/* The highest string descriptor entry
+	   (used to retrieve descriptors from gadget driver) */
+	int connected;
+	int device_address;
+	int set_address;
+	int ep0_state;
+	int ep0_dir;
+	struct usb_ctrlrequest local_setup_buff;
+	struct c67x00_request *status_req;
+};
+
+
+/* ------------------------------------------------------------------------- */
+/* gadget ops */
+
+static int c67x00_get_frame(struct usb_gadget *_gadget)
+{
+	printk(KERN_DEBUG "c67x00-udc: c67x00_get_frame\n");
+	return -ENODEV;
+}
+
+static int c67x00_wakeup(struct usb_gadget *_gadget)
+{
+	printk(KERN_DEBUG "c67x00-udc: c67x00_wakeup\n");
+	return -ENODEV;
+}
+
+static int c67x00_selfpowered(struct usb_gadget *_gadget,
+			      int is_selfpowered)
+{
+	printk(KERN_DEBUG "c67x00-udc: c67x00_selfpowered\n");
+	return -ENODEV;
+}
+
+static int c67x00_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	printk(KERN_DEBUG "c67x00-udc: c67x00_vbus_session\n");
+	return -ENODEV;
+}
+
+static int c67x00_pullup(struct usb_gadget *_gadget, int is_on)
+{
+	printk(KERN_DEBUG "c67x00-udc: c67x00_pullup\n");
+	return -ENODEV;
+}
+
+static const struct usb_gadget_ops c67x00_udc_ops = {
+	.get_frame = c67x00_get_frame,
+	.wakeup = c67x00_wakeup,
+	.set_selfpowered = c67x00_selfpowered,
+	.vbus_session = c67x00_vbus_session,
+	.pullup = c67x00_pullup
+};
+
+/*
+ * the sie and gadget_driver get probed/registered from 2 totally independant
+ * places, this datastructure binds them together
+ */
+static struct c67x00_udc controller = {
+	.gadget = {
+		.ops = &c67x00_udc_ops,
+		.name = "c67x00_udc",
+		.speed = USB_SPEED_FULL,
+		.is_dualspeed = 1,
+		.is_a_peripheral = 1,
+		.dev = {
+			.bus_id = "gadget",
+		},
+	},
+};
+
+
+/*-------------------------------------------------------------------------
+		PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/c67x00_udc";
+
+static int c67x00_proc_read(char *page, char **start, off_t off, int count,
+		int *eof, void *_dev)
+{
+	char *buf = page;
+	char *next = buf;
+	unsigned size = count;
+	unsigned long flags;
+	int t;
+	struct c67x00_udc *udc = &controller;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* ------basic driver infomation ---- */
+	t = scnprintf(next, size, udc->connected ? "active\n" : "Inactive\n"));
+	size -= t;
+	next += t;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	*eof = 1;
+	return count - size;
+}
+
+#define create_proc_file()	create_proc_read_entry(proc_filename, \
+				0, NULL, c67x00_proc_read, NULL)
+
+#define remove_proc_file()	remove_proc_entry(proc_filename, NULL)
+
+#else				/* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_file()	do {} while (0)
+#define remove_proc_file()	do {} while (0)
+
+#endif				/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
+/*----------------------------------------------------------------------------*/
+
+static void c67x00_udc_set_address(struct c67x00_udc *udc, u16 address)
+{
+	udc->device_address = address;
+	c67x00_ll_susb_set_addres(udc->sie, address);
+}
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void c67x00_udc_done(struct c67x00_udc_ep *ep,
+			    struct c67x00_request *req, int status)
+{
+	int stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	if (req->req.complete)
+		req->req.complete(&ep->ep, &req->req);
+	ep->stopped = stopped;
+}
+
+/*----------- UDC send/receive functions -------------------------------------*/
+
+static void c67x00_udc_start_io_irq(struct c67x00_udc_ep *ep)
+{
+	int length;
+	struct c67x00_sie *sie = ep->udc->sie;
+	struct c67x00_request *req =
+		list_entry(ep->queue.next, struct c67x00_request, queue);
+
+	if ((ep->is_ep_in && ep->ep_num != 0)
+	    || (ep->ep_num == 0 && ep->udc->ep0_dir == USB_DIR_IN)) {
+
+		if (req->req.length - req->req.actual > ep->ep.maxpacket)
+			length = ep->ep.maxpacket;
+		else
+			length = req->req.length - req->req.actual;
+
+		c67x00_ll_susb_start_send(sie, ep->ep_num,
+			req->req.buf + req->req.actual, length, ep->datatoggle);
+		ep->datatoggle = ((ep->datatoggle == 0)?1:0);
+		ep->xfer_size = length;
+
+		req->req.actual += length; /* TODO move to udc_done_irq */
+	} else {
+		if (req->req.length - req->req.actual > ep->ep.maxpacket)
+			length = ep->ep.maxpacket;
+		else
+			length = req->req.length - req->req.actual;
+
+		c67x00_ll_susb_start_receive(sie, ep->ep_num, length);
+		ep->xfer_size = length;
+	}
+}
+
+static void c67x00_udc_io_work(struct work_struct *_udc)
+{
+	int i = 0;
+	struct c67x00_udc *udc =
+	    container_of(_udc, struct c67x00_udc, io_work);
+
+	for (i = 0; i < C67X00_MAX_NB_END_POINTS; i++) {
+		struct c67x00_udc_ep *ep = &udc->ep[i];
+		if (ep->start_io) {
+			ep->start_io = 0;
+			c67x00_udc_start_io_irq(ep);
+		}
+	}
+}
+
+static void c67x00_udc_schedule_io_irq(struct c67x00_udc_ep *ep)
+{
+	ep->start_io = 1;
+
+	/* start work queue */
+	schedule_work(&ep->udc->io_work);
+}
+
+/* Set protocol stall on ep0, protocol stall will automatically be cleared
+   on new transaction */
+static void ep0stall(struct c67x00_udc *udc)
+{
+	c67x00_ll_set_ep_stall(udc->sie, 0);
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = USB_DIR_OUT;
+}
+
+/* Prime a status phase for ep0 */
+static int ep0_prime_status(struct c67x00_udc *udc, int direction)
+{
+	struct c67x00_request *req = udc->status_req;
+	struct c67x00_udc_ep *ep;
+	int status = 0;
+	int request = 0;
+
+	if (direction == USB_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	ep = &udc->ep[0];
+	udc->ep0_state = WAIT_FOR_OUT_STATUS;
+
+	if (direction == USB_DIR_IN)
+		ep->is_ep_in = 1;
+	else
+		ep->is_ep_in = 0;
+
+	req->req.length = 0;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = NULL;
+
+	/* Start I/O queue if the list was empty */
+	if (list_empty(&ep->queue) /*&& !ep->stopped*/)
+		request = 1;
+
+	/* Add the request to the queue of the endpoint */
+	list_add_tail(&req->queue, &ep->queue);
+
+	if (request) {
+		dev_dbg(sie_dev(ep->udc->sie), "prime - schedule work\n");
+		c67x00_udc_schedule_io_irq(ep);
+	}
+
+	return status;
+}
+
+static void ep0_req_complete(struct c67x00_udc *udc,
+			     struct c67x00_udc_ep *ep0,
+			     struct c67x00_request *req)
+{
+	if (udc->set_address) {
+		/* Set the new address */
+		c67x00_udc_set_address(udc, udc->device_address);
+		udc->set_address = 0;
+	}
+
+	c67x00_udc_done(ep0, req, 0);
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		/* receive status phase */
+		if (ep0_prime_status(udc, USB_DIR_OUT))
+			ep0stall(udc);
+		break;
+	case DATA_STATE_RECV:
+		/* send status phase */
+		if (ep0_prime_status(udc, USB_DIR_IN))
+			ep0stall(udc);
+		break;
+	case WAIT_FOR_OUT_STATUS:
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+	case WAIT_FOR_SETUP:
+		break;
+	default:
+		ep0stall(udc);
+		break;
+	}
+}
+
+static void c67x00_udc_done_irq(struct c67x00_udc_ep *ep, int status)
+{
+	struct c67x00_request *req;
+	struct c67x00_sie *sie = ep->udc->sie;
+	struct c67x00_udc *udc = ep->udc;
+
+	if (unlikely(list_empty(&ep->queue)))
+		return;
+
+	req = list_entry(ep->queue.next, struct c67x00_request, queue);
+
+	if (status == 0) {
+		if ((!ep->is_ep_in && ep->ep_num != 0)
+		    || (ep->ep_num == 0 && udc->ep0_dir == USB_DIR_OUT)) {
+			int cnt_result = c67x00_ll_get_device_ep_count_result(
+				sie, ep->ep_num);
+			int length = 0;
+			length = (ep->xfer_size - cnt_result);
+
+			if (length > 0) {
+				c67x00_ll_susb_receive(sie, ep->ep_num,
+					req->req.buf + req->req.actual, length);
+				req->req.actual += length;
+			}
+
+			if (ep->ep_num == 0
+			    && req->req.actual < req->req.length) {
+				c67x00_udc_schedule_io_irq(ep);
+				return;
+			}
+		} else {
+			if (req->req.actual < req->req.length) {
+				c67x00_udc_schedule_io_irq(ep);
+				return;
+			}
+		}
+	}
+
+	if (ep->ep_num == 0)
+		ep0_req_complete(udc, ep, req);
+	else {
+		c67x00_udc_done(ep, req, 0);
+		if (!list_empty(&ep->queue) && !ep->stopped)
+			/* restart io req */
+			c67x00_udc_schedule_io_irq(ep);
+	}
+
+}
+
+/* -------------------------------------------------------------------------- */
+/* endpoints */
+
+static const char *c67x00_ep_name[C67X00_MAX_NB_END_POINTS] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"
+};
+
+
+/* -------------------------------------------------------------------------- */
+/* ep opts */
+
+/*
+ * empties entire endpoint queue
+ */
+static void c67x00_nuke_ep(struct c67x00_udc_ep *ep, int status)
+{
+	ep->stopped = 1;
+
+	while (!list_empty(&ep->queue)) {
+		struct c67x00_request *req =
+			list_entry(ep->queue.next, struct c67x00_request,
+				   queue);
+		c67x00_udc_done(ep, req, status);
+	}
+}
+
+static int c67x00_ep_enable(struct usb_ep *_ep,
+			    const struct usb_endpoint_descriptor *desc)
+{
+	struct c67x00_udc_ep *ep;
+	struct c67x00_sie *sie;
+	u16 maxpacket;
+
+	ep = container_of(_ep, struct c67x00_udc_ep, ep);
+	sie = ep->udc->sie;
+
+	maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	c67x00_ll_set_device_ep_status(sie, ep->ep_num, 0x0000);
+
+	if (desc->bEndpointAddress & USB_DIR_IN) {
+		ep->is_ep_in = 1;
+		c67x00_ll_set_ep_ctrl_reg(sie, ep->ep_num,
+					  EP_ENABLE | DIR_SEL_IN);
+		c67x00_ll_set_ep_packet_size_reg(sie, ep->ep_num, maxpacket);
+	} else {
+		ep->is_ep_in = 0;
+		c67x00_ll_set_ep_ctrl_reg(sie, ep->ep_num, EP_ENABLE);
+		c67x00_ll_set_ep_packet_size_reg(sie, ep->ep_num, maxpacket);
+	}
+
+	ep->enable = 1;
+	ep->stopped = 0;
+	ep->ep.maxpacket = maxpacket;
+
+	return 0;
+}
+
+static int c67x00_ep_disable(struct usb_ep *_ep)
+{
+	unsigned long flags;
+	struct c67x00_udc_ep *ep;
+
+	ep = container_of(_ep, struct c67x00_udc_ep, ep);
+
+	dev_dbg(sie_dev(ep->udc->sie), "ep_disable %s\n", _ep->name);
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	ep->enable = 0;
+	ep->stopped = 1;
+
+	c67x00_nuke_ep(ep, -ESHUTDOWN);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	return 0;
+}
+
+struct usb_request *c67x00_ep_alloc_request(struct usb_ep *_ep,
+					    gfp_t gfp_flags)
+{
+	struct c67x00_request *req;
+
+	req = kzalloc(sizeof(struct c67x00_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+static void c67x00_ep_free_request(struct usb_ep *_ep,
+				   struct usb_request *_req)
+{
+	struct c67x00_request *req = NULL;
+	struct c67x00_udc_ep *ep;
+
+	ep = container_of(_ep, struct c67x00_udc_ep, ep);
+	dev_dbg(sie_dev(ep->udc->sie), "free_request %s\n", _ep->name);
+
+	req = container_of(_req, struct c67x00_request, req);
+
+	if (_req)
+		kfree(req);
+}
+
+static int c67x00_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+			   gfp_t gfp_flags)
+{
+	struct c67x00_udc_ep *ep;
+	struct c67x00_request *req;
+	unsigned long flags;
+	int request = 0;
+	struct c67x00_udc *dev;
+
+	req = container_of(_req, struct c67x00_request, req);
+	if (unlikely
+	    (!_req || !_req->complete || !_req->buf
+	     || !list_empty(&req->queue))) {
+		printk(KERN_WARNING "bad params\n");
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct c67x00_udc_ep, ep);
+	if (unlikely(!_ep)) {
+		dev_warn(sie_dev(ep->udc->sie), "bad ep\n");
+		return -EINVAL;
+	}
+
+	dev = ep->udc;
+	if (unlikely
+	    (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		dev_warn(sie_dev(ep->udc->sie), "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	if (ep->ep_num == 0)
+		ep->udc->ep0_state = DATA_STATE_XMIT;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* Start I/O queue if the list was empty */
+	if (list_empty(&ep->queue) /*&& !ep->stopped*/)
+		request = 1;
+
+	/* Add the request to the queue of the endpoint */
+	list_add_tail(&req->queue, &ep->queue);
+
+	if (request)
+		c67x00_udc_schedule_io_irq(ep);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static int c67x00_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct c67x00_udc_ep *ep;
+	struct c67x00_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct c67x00_udc_ep, ep);
+	if (!_ep || ep->ep_num == 0)
+		return -EINVAL;
+
+	dev_dbg(sie_dev(ep->udc->sie), "dequeue %s\n", _ep->name);
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	c67x00_udc_done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return 0;
+}
+
+static int c67x00_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	printk(KERN_WARNING "c67x00-udc: ep set_halt %s\n", _ep->name);
+	return -ENODEV;
+}
+
+static void c67x00_ep_fifo_flush(struct usb_ep *_ep)
+{
+	printk(KERN_WARNING "c67x00-udc: ep fifo_flush %s\n", _ep->name);
+}
+
+static const struct usb_ep_ops c67x00_ep_ops = {
+	.enable = c67x00_ep_enable,
+	.disable = c67x00_ep_disable,
+
+	.alloc_request = c67x00_ep_alloc_request,
+	.free_request = c67x00_ep_free_request,
+
+	.queue = c67x00_ep_queue,
+	.dequeue = c67x00_ep_dequeue,
+
+	.set_halt = c67x00_ep_set_halt,
+	.fifo_flush = c67x00_ep_fifo_flush,
+};
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * ch9 Get status
+ */
+static void ch9getstatus(struct c67x00_udc *udc, u8 request_type, u16 value,
+			 u16 index, u16 length)
+{
+	u16 tmp = 0;		/* Status, cpu endian */
+
+	struct c67x00_request *req;
+	struct c67x00_udc_ep *ep;
+	int request = 0;
+
+	ep = &udc->ep[0];
+
+	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+		/* Get device status */
+		tmp = 1 << USB_DEVICE_SELF_POWERED;
+		dev_dbg(sie_dev(udc->sie),
+				 "get status device\n");
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+		/* Get interface status */
+		/* We don't have interface information in udc driver */
+		tmp = 0;
+		dev_dbg(sie_dev(udc->sie),
+				 "get status interface\n");
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+		/* Get endpoint status */
+		tmp = 0;
+		dev_dbg(sie_dev(udc->sie),
+				 "get status ep\n");
+
+	}
+
+	udc->ep0_dir = USB_DIR_IN;
+	/* Borrow the per device status_req */
+	req = udc->status_req;
+	/* Fill in the reqest structure */
+	*((u16 *) req->req.buf) = cpu_to_le16(tmp);
+	req->req.length = 2;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = NULL;
+
+	/* prime the data phase */
+	/* Start I/O queue if the list was empty */
+	if (list_empty(&ep->queue) /*&& !ep->stopped*/)
+		request = 1;
+
+	/* Add the request to the queue of the endpoint */
+	list_add_tail(&req->queue, &ep->queue);
+
+	if (request) {
+		dev_dbg(sie_dev(ep->udc->sie), "prime - schedule work\n");
+		c67x00_udc_schedule_io_irq(ep);
+	}
+	udc->ep0_state = DATA_STATE_XMIT;
+	return;
+}
+
+static void setup_received_irq(struct c67x00_udc *udc)
+{
+	struct usb_ctrlrequest *setup = &udc->local_setup_buff;
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	c67x00_nuke_ep(&udc->ep[0], -ESHUTDOWN);
+
+	udc->ep[0].datatoggle = 1;
+
+	/* We process some stardard setup requests here */
+	switch (setup->bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase from udc */
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
+		return;
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+						| USB_RECIP_DEVICE))
+			break;
+		udc->device_address = wValue;
+		udc->set_address = 1;
+
+		/* Status phase */
+		if (ep0_prime_status(udc, USB_DIR_IN))
+			ep0stall(udc);
+
+		return;
+#if 0 /* TODO add support */
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Status phase from udc */
+	{
+		int rc = -EOPNOTSUPP;
+
+		if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
+				== (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
+
+			if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
+				break;
+		} else if ((setup->bRequestType & (USB_RECIP_MASK
+				| USB_TYPE_MASK)) == (USB_RECIP_DEVICE
+				| USB_TYPE_STANDARD)) {
+			/* Note: The driver has not include OTG support yet.
+			 * This will be set when OTG support is added */
+			if (!gadget_is_otg(&udc->gadget))
+				break;
+			else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
+				udc->gadget.b_hnp_enable = 1;
+			else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
+				udc->gadget.a_hnp_support = 1;
+			else if (setup->bRequest ==
+					USB_DEVICE_A_ALT_HNP_SUPPORT)
+				udc->gadget.a_alt_hnp_support = 1;
+			else
+				break;
+			rc = 0;
+		} else
+			break;
+
+		if (rc == 0) {
+			if (ep0_prime_status(udc, USB_DIR_IN))
+				ep0stall(udc);
+		}
+		return;
+	}
+#endif
+	default:
+		break;
+	}
+
+	/* Requests handled by gadget */
+	if (wLength) {
+		/* Data phase from gadget, status phase from udc */
+		udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+				?  USB_DIR_IN : USB_DIR_OUT;
+		spin_unlock(&udc->lock);
+		if (udc->driver && udc->driver->setup
+		    && udc->driver->setup(&udc->gadget,
+					  &udc->local_setup_buff) < 0)
+			ep0stall(udc);
+
+		spin_lock(&udc->lock);
+		udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+				?  DATA_STATE_XMIT : DATA_STATE_RECV;
+	} else {
+		/* No data phase, IN status from gadget */
+		udc->ep0_dir = USB_DIR_IN;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+				       &udc->local_setup_buff) < 0) {
+			ep0stall(udc);
+		}
+		spin_lock(&udc->lock);
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+	}
+}
+
+static void c67x00_udc_ep_irq(struct c67x00_sie *sie, int ep)
+{
+	struct c67x00_udc *udc = &controller;
+	u16 ep_status = c67x00_ll_get_device_ep_status(sie, ep);
+	int status = 0;
+
+	if (ep_status & EP_IRQ_FLG_SETUP) {
+		dev_dbg(sie_dev(sie),
+				 "EP%d FLG_SETUP\n", ep);
+
+		c67x00_ll_susb_get_setup_data(sie, &udc->local_setup_buff);
+
+#if 0
+/* Only for outputting debug info -> setup is handled in setup_received_irq */
+		switch (udc->local_setup_buff.bRequestType & USB_TYPE_MASK) {
+		case USB_TYPE_STANDARD:
+			switch (udc->local_setup_buff.bRequest) {
+			case USB_REQ_GET_STATUS :
+				dev_dbg(sie_dev(sie),
+						 "GET_STATUS\n");
+
+				break;
+
+			case USB_REQ_CLEAR_FEATURE :
+				dev_dbg(sie_dev(sie),
+						 "CLEAR_FEATURE\n");
+
+				break;
+
+			case USB_REQ_SET_FEATURE :
+				dev_dbg(sie_dev(sie),
+						 "SET_FEATURE\n");
+
+				break;
+
+			case USB_REQ_SET_ADDRESS :
+				dev_dbg(sie_dev(sie),
+						 "SET_ADDRESS\n");
+
+				break;
+
+			case USB_REQ_GET_DESCRIPTOR :
+				dev_dbg(sie_dev(sie),
+						 "GET_DESCRIPTOR\n");
+
+				break;
+
+			case USB_REQ_SET_DESCRIPTOR :
+				dev_dbg(sie_dev(sie),
+						 "SET_DESCRIPTOR\n");
+
+				break;
+
+			case USB_REQ_GET_CONFIGURATION :
+				dev_dbg(sie_dev(sie),
+						 "GET_CONFIGURATION\n");
+
+				break;
+
+			case USB_REQ_SET_CONFIGURATION :
+				dev_dbg(sie_dev(sie),
+						 "SET_CONFIGURATION\n");
+
+				break;
+
+			case USB_REQ_GET_INTERFACE :
+				dev_dbg(sie_dev(sie),
+						 "GET_INTERFACE\n");
+
+				break;
+
+			case USB_REQ_SET_INTERFACE :
+				dev_dbg(sie_dev(sie),
+						 "SET_INTERFACE\n");
+
+				break;
+
+			case USB_REQ_SYNCH_FRAME :
+				dev_dbg(sie_dev(sie),
+						 "SYNCH_FRAME\n");
+
+				break;
+			default:
+				dev_dbg(sie_dev(sie),
+						 "UNKNOWN request\n");
+				break;
+
+			}
+			break;
+
+		case USB_TYPE_CLASS:
+			dev_dbg(sie_dev(sie),
+					 "EP%d CLASS request\n", ep);
+			break;
+
+		case USB_TYPE_VENDOR:
+			dev_dbg(sie_dev(sie),
+					 "EP%d VENDOR request\n", ep);
+			break;
+
+		default:
+			dev_dbg(sie_dev(sie),
+					 "EP%d UNKNOWN request\n", ep);
+			break;
+
+		}
+#endif
+		setup_received_irq(udc);
+
+	} else 	if (ep_status & EP_IRQ_FLG_ERROR) {
+		dev_dbg(sie_dev(sie), "EP%d FLG_ERROR\n", ep);
+		/* TODO resend instead */
+	} else {
+#if 0
+		if (ep_status & EP_IRQ_FLG_ACK)
+			dev_dbg(sie_dev(sie), "EP%d FLG_ACK\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_TIMEOUT)
+			dev_dbg(sie_dev(sie), "EP%d FLG_TIMEOUT\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_LEN_EXCEP)
+			dev_dbg(sie_dev(sie), "EP%d FLG_LEN_EXCEP\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_NACK)
+			dev_dbg(sie_dev(sie), "EP%d FLG_NACK\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_STALL)
+			dev_dbg(sie_dev(sie), "EP%d FLG_STALL\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_IN_EXCEP)
+			dev_dbg(sie_dev(sie), "EP%d FLG_IN_EXCEP\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_OUT_EXCEP)
+			dev_dbg(sie_dev(sie), "EP%d FLG_OUT_EXCEP\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_UNDERFLOW)
+			dev_dbg(sie_dev(sie), "EP%d FLG_UNDERFLOW\n", ep);
+
+		if (ep_status & EP_IRQ_FLG_OVERFLOW)
+			dev_dbg(sie_dev(sie), "EP%d FLG_OVERFLOW\n", ep);
+#endif
+
+		c67x00_udc_done_irq(&udc->ep[ep], status);
+	}
+}
+
+/*
+ * This function is called from the interrupt handler in c67x00-drv.c
+ */
+static void c67x00_udc_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
+{
+	u16 device_status;
+	unsigned long flags = 0;
+	struct c67x00_udc *udc = sie->private_data;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	device_status = c67x00_ll_usb_get_status(sie);
+
+	if (int_status & SOFEOP_FLG(sie->sie_num))
+		c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
+
+	if (int_status & RESET_FLG(sie->sie_num)) {
+		int i;
+
+		dev_dbg(sie_dev(sie), "sie%d: reset IRQ\n",
+			 sie->sie_num);
+
+		/* Handle reset here */
+		c67x00_ll_usb_clear_status(sie, RESET_IRQ_FLG);
+
+		for (i = 0; i < C67X00_MAX_NB_END_POINTS; i++) {
+			struct c67x00_udc_ep *ep = &udc->ep[i];
+			if (i != 0 && ep->enable) {
+				ep->stopped = 1;
+				c67x00_nuke_ep(ep, -ESHUTDOWN);
+			}
+			c67x00_ll_set_ep_ctrl_reg(sie, i,
+						  ((i == 0)?EP_ENABLE:0x00));
+		}
+
+		c67x00_udc_set_address(udc, 0);
+
+		if (udc->driver && udc->driver->disconnect)
+			udc->driver->disconnect(&udc->gadget);
+	}
+
+	if (int_status & DONE_FLG(sie->sie_num)) {
+		int i, mask = 0x01;
+		dev_dbg(sie_dev(sie), "sie%d: done IRQ -> "
+			"device status 0x%04X\n",
+			 sie->sie_num, device_status);
+
+		for (i = 0; i < C67X00_MAX_NB_END_POINTS; i++, mask <<= 1)
+			if (device_status & mask)
+				c67x00_udc_ep_irq(sie, i);
+
+		c67x00_ll_usb_clear_status(sie, device_status);
+
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct c67x00_udc *udc = &controller;
+	int retval;
+
+	if (!driver || !driver->bind || !driver->setup) {
+		printk(KERN_ERR
+		       "c67x00-udc: invalid gadget driver provided\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&udc->lock);
+	if (!udc->sie || udc->driver) {
+		spin_unlock(&udc->lock);
+		return -EBUSY;
+	}
+
+	udc->driver = driver;
+	udc->gadget.dev.driver = &driver->driver;
+	device_add(&udc->gadget.dev);
+
+	driver->driver.bus = NULL;
+
+	dev_dbg(sie_dev(udc->sie), "Binding %s to SIE%d\n",
+		 driver->function, udc->sie->sie_num);
+
+	retval = driver->bind(&udc->gadget);
+	if (retval) {
+		dev_warn(sie_dev(udc->sie), "Driver bind failed\n");
+		goto error;
+	}
+
+	spin_unlock(&udc->lock);
+
+	/* enable device */
+	c67x00_ll_susb_init(udc->sie);
+
+	return 0;
+
+ error:
+	udc->driver = NULL;
+	udc->gadget.dev.driver = NULL;
+	device_del(&udc->gadget.dev);
+
+	spin_unlock(&udc->lock);
+
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct c67x00_udc *udc = &controller;
+
+	printk(KERN_WARNING "c67x00-udc: usb_gadget_unregister_driver\n");
+
+	spin_lock(&udc->lock);
+	if (udc->driver != driver) {
+		spin_unlock(&udc->lock);
+		return -EINVAL;
+	}
+
+	udc->driver = NULL;
+
+	driver->unbind(&udc->gadget);
+	device_del(&udc->gadget.dev);
+
+	c67x00_ll_susb_disable(udc->sie);
+
+	spin_unlock(&udc->lock);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/* -------------------------------------------------------------------------- */
+
+int c67x00_udc_probe(struct c67x00_sie *sie)
+{
+	struct c67x00_udc *udc = &controller;
+	unsigned long flags;
+	int i;
+
+	if (udc->sie) {
+		dev_err(sie_dev(sie),
+			"Only 1 peripheral port supported, check sie_config\n");
+		return -EBUSY;
+	}
+
+	spin_lock_init(&udc->lock);
+	INIT_WORK(&udc->io_work, c67x00_udc_io_work);
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+	udc->gadget.ep0 = &udc->ep[0].ep;
+	INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+	for (i = 0; i < C67X00_MAX_NB_END_POINTS; i++) {
+		struct c67x00_udc_ep *ep = &udc->ep[i];
+		INIT_LIST_HEAD(&udc->ep[i].ep.ep_list);
+		if (i != 0) {
+			list_add_tail(&udc->ep[i].ep.ep_list,
+				      &udc->gadget.ep_list);
+		}
+		ep->ep.name = c67x00_ep_name[i];
+		ep->ep.ops = &c67x00_ep_ops;
+		ep->enable = 0;
+		ep->stopped = 0;
+		ep->start_io = 0;
+		if (i == 0)
+			ep->ep.maxpacket = 8;
+		else
+			/* Size is set when endpoint is enabled */
+			ep->ep.maxpacket = 512;
+		ep->ep_num = i;
+		ep->udc = udc;
+		INIT_LIST_HEAD(&ep->queue);
+	}
+
+	udc->sie = sie;
+	udc->gadget.dev.parent = &sie->dev->pdev->dev;
+	udc->connected = 0;
+	udc->set_address = 0;
+	udc->status_req = container_of(
+		c67x00_ep_alloc_request(NULL, GFP_KERNEL),
+		struct c67x00_request, req);
+
+	device_initialize(&udc->gadget.dev);
+
+	spin_lock_irqsave(&sie->lock, flags);
+	sie->private_data = udc;
+	sie->irq = c67x00_udc_irq;
+	spin_unlock_irqrestore(&sie->lock, flags);
+
+	create_proc_file();
+
+	return 0;
+}
+
+void c67x00_udc_remove(struct c67x00_sie *sie)
+{
+	struct c67x00_udc *udc = sie->private_data;
+
+	if (!udc) {
+		dev_err(sie_dev(sie), "No udc found!\n");
+		return;
+	}
+
+	/* gadget driver must not be registered */
+	BUG_ON(udc->driver != NULL);
+
+	spin_lock(&udc->lock);
+	/* TODO free status_request member */
+	sie->private_data = NULL;
+	udc->sie = NULL;
+	spin_unlock(&udc->lock);
+}
Index: linux-2.6/drivers/usb/c67x00/c67x00-udc.h
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-udc.h
@@ -0,0 +1,48 @@
+/*
+ * c67x00-udc.h: Cypress C67X00 USB device controller
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple device controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#ifndef _USB_C67X00_UDC_H
+#define _USB_C67X00_UDC_H
+
+#include <linux/kernel.h>
+
+#include "c67x00.h"
+
+#ifdef CONFIG_USB_GADGET_C67X00
+/* Functions used by drv */
+int c67x00_udc_probe(struct c67x00_sie *sie);
+void c67x00_udc_remove(struct c67x00_sie *sie);
+#else
+static inline int c67x00_udc_probe(struct c67x00_sie *sie)
+{
+	printk(KERN_ERR "udc requested but CONFIG_USB_GADGET_C67X00 "
+	       "not enabled!\n");
+	return -ENODEV;
+}
+
+static inline void c67x00_udc_remove(struct c67x00_sie *sie)
+{
+}
+#endif				/* CONFIG_USB_GADGET_C67X00 */
+
+#endif				/* _USB_C67X00_UDC_H */
Index: linux-2.6/drivers/usb/c67x00/c67x00-drv.c
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00-drv.c
+++ linux-2.6/drivers/usb/c67x00/c67x00-drv.c
@@ -42,6 +42,7 @@
 
 #include "c67x00.h"
 #include "c67x00-hcd.h"
+#include "c67x00-udc.h"
 
 static void c67x00_probe_sie(struct c67x00_sie *sie,
 			     struct c67x00_device *dev, int sie_num)
@@ -56,6 +57,11 @@
 		c67x00_hcd_probe(sie);
 		break;
 
+	case C67X00_SIE_PERIPHERAL_A:
+	case C67X00_SIE_PERIPHERAL_B:
+		c67x00_udc_probe(sie);
+		break;
+
 	case C67X00_SIE_UNUSED:
 		dev_info(sie_dev(sie),
 			 "Not using SIE %d as requested\n", sie->sie_num);
@@ -76,6 +82,11 @@
 		c67x00_hcd_remove(sie);
 		break;
 
+	case C67X00_SIE_PERIPHERAL_A:
+	case C67X00_SIE_PERIPHERAL_B:
+		c67x00_udc_remove(sie);
+		break;
+
 	default:
 		break;
 	}
Index: linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00-ll-hpi.c
+++ linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
@@ -54,9 +54,6 @@
 #define HUSB_SIE_INIT_INT(x)		((x) ? 0x0073 : 0x0072)
 #define HUSB_RESET_INT			0x0074
 
-#define SUSB_INIT_INT			0x0071
-#define SUSB_INIT_INT_LOC		(SUSB_INIT_INT * 2)
-
 /* -----------------------------------------------------------------------
  * HPI implementation
  *
@@ -296,6 +293,7 @@
 	return rc;
 }
 
+
 /* -------------------------------------------------------------------------- */
 /* Host specific functions */
 
@@ -372,6 +370,206 @@
 }
 
 /* -------------------------------------------------------------------------- */
+void c67x00_ll_susb_init(struct c67x00_sie *sie)
+{
+	struct c67x00_device *dev = sie->dev;
+
+	hpi_clear_bits(dev, HPI_IRQ_ROUTING_REG,
+		       SOFEOP_TO_CPU_EN(sie->sie_num));
+	hpi_set_bits(dev, HPI_IRQ_ROUTING_REG,
+		     SOFEOP_TO_HPI_EN(sie->sie_num)
+		     | RESUME_TO_HPI_ENABLE(sie->sie_num)
+		     | RESET_TO_HPI_ENABLE(sie->sie_num)
+		     | DONE_TO_HPI_ENABLE(sie->sie_num)
+		     | ID_TO_HPI_ENABLE | VBUS_TO_HPI_ENABLE);
+
+	hpi_set_bits(dev,
+		     DEVICE_N_IRQ_EN_REG(sie->sie_num),
+		     DEV_EP0_IRQ_EN
+		     | DEV_EP1_IRQ_EN
+		     | DEV_EP2_IRQ_EN
+		     | DEV_EP3_IRQ_EN
+		     | DEV_EP4_IRQ_EN
+		     | DEV_EP5_IRQ_EN
+		     | DEV_EP6_IRQ_EN
+		     | DEV_EP7_IRQ_EN
+		     | DEV_RESET_IRQ_EN
+		     | DEV_SOF_EOP_IRQ_EN
+		     | DEV_SOF_TMOUT_IRQ_EN
+		     | DEV_ID_IRQ_EN
+		     | DEV_VBUS_IRQ_EN);
+
+	hpi_clear_bits(dev,
+		       USB_CTL_REG(sie->sie_num),
+		       SOF_EOP_EN(0) | SOF_EOP_EN(1));
+
+	if (sie->mode == C67X00_SIE_PERIPHERAL_A)
+		hpi_write_word(dev, DEVICE_N_PORT_SEL(sie->sie_num),
+			       PERIPHERAL_PORT_A);
+	else
+		hpi_write_word(dev, DEVICE_N_PORT_SEL(sie->sie_num),
+			       PERIPHERAL_PORT_B);
+
+	/* Clear endpoint0 status register register */
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_STAT_REG(sie->sie_num, 0), 0);
+	/* Clear address register */
+	hpi_write_word(sie->dev, DEVICE_N_ADDRESS(sie->sie_num), 0);
+	/* Clear the interrupts */
+	hpi_write_word(sie->dev, USB_STAT_REG(sie->sie_num), 0xFFFF);
+	/* reset SOF/EOP time-out counter */
+	hpi_write_word(sie->dev,
+		       DEVICE_N_SOF_EOP_COUNT_REG(sie->sie_num), 0x3138);
+
+	hpi_set_bits(dev,
+		     USB_CTL_REG(sie->sie_num),
+		     SOF_EOP_EN(0) | SOF_EOP_EN(1)
+		     | PORT_RES_EN(0) | PORT_RES_EN(1));
+
+	hpi_set_bits(sie->dev, HW_IRQ_EN_REG,
+		     HOST_DEVICE_x_IRQ_EN(sie->sie_num));
+
+	dev_info(sie_dev(sie),
+		 "Peripheral USB device setup on SIE%d\n",
+		 sie->sie_num);
+}
+
+void c67x00_ll_susb_disable(struct c67x00_sie *sie)
+{
+	hpi_write_word(sie->dev, DEVICE_N_IRQ_EN_REG(sie->sie_num), 0);
+	hpi_write_word(sie->dev, USB_CTL_REG(sie->sie_num),  0x0000);
+}
+
+void c67x00_ll_set_ep_ctrl_reg(struct c67x00_sie *sie, int ep_num, u16 val)
+{
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep_num), val);
+}
+
+void c67x00_ll_set_ep_stall(struct c67x00_sie *sie, int ep_num)
+{
+	hpi_set_bits(sie->dev,
+		     DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep_num),
+		     EP_CTRL_STALL_ENABLE);
+}
+
+void c67x00_ll_set_ep_packet_size_reg(struct c67x00_sie *sie, int ep_num,
+				      u16 val)
+{
+	/* This undocumented register needs to be set to the packet size
+	   Normally the BIOS sets this correctly when it is able to parse
+	   the configuration descriptor correctly */
+	hpi_write_word(sie->dev,
+		       (DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep_num)
+			+ 0x0A), val);
+}
+
+u16 c67x00_ll_get_device_ep_status(struct c67x00_sie *sie, int ep)
+{
+	return hpi_read_word(sie->dev,
+			     DEVICE_N_ENDPOINT_N_STAT_REG(sie->sie_num, ep));
+}
+
+void c67x00_ll_set_device_ep_status(struct c67x00_sie *sie,
+				    int ep, u16 value)
+{
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_STAT_REG(sie->sie_num, ep),
+		       value);
+}
+
+u16 c67x00_ll_get_device_ep_count_result(struct c67x00_sie *sie, int ep)
+{
+	return hpi_read_word(sie->dev,
+			     DEVICE_N_ENDPOINT_N_COUNT_RESULT_REG(
+				     sie->sie_num, ep));
+}
+
+int c67x00_ll_susb_start_send(struct c67x00_sie *sie, int ep,
+			      void *data, int len, int data_toggle)
+{
+	dev_dbg(sie_dev(sie),
+		 "Send data on sie%d ep%d (len %d)\n", sie->sie_num, ep, len);
+
+	c67x00_ll_write_mem_le16(sie->dev, CY_UDC_REQ_BUFFER_ADDR(ep),
+				 data, len);
+
+	if (data_toggle)
+		hpi_set_bits(sie->dev,
+			     DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+			     EP_CTRL_SEQ_SEL);
+	else
+		hpi_clear_bits(sie->dev,
+			       DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+			       EP_CTRL_SEQ_SEL);
+
+	hpi_clear_bits(sie->dev, DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+		       EP_CTRL_STALL_ENABLE);
+	hpi_set_bits(sie->dev, DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+		     EP_CTRL_DIR_SEL);
+
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_ADDRESS_REG(sie->sie_num, ep),
+		       CY_UDC_REQ_BUFFER_ADDR(ep));
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_COUNT_REG(sie->sie_num, ep), len);
+	hpi_set_bits(sie->dev,
+		     DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+		     EP_CTRL_ARM_EN);
+
+	return 0;
+}
+
+int c67x00_ll_susb_start_receive(struct c67x00_sie *sie, int ep, int len)
+{
+	dev_dbg(sie_dev(sie),
+		"Start receive data on sie%d ep%d (len %d)\n",
+		sie->sie_num, ep, len);
+
+	if (ep == 0)
+		hpi_set_bits(sie->dev,
+			     DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+			     EP_CTRL_SEQ_SEL | EP_CTRL_ENABLE);
+
+	hpi_clear_bits(sie->dev,
+		       DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+		       EP_CTRL_STALL_ENABLE);
+	hpi_clear_bits(sie->dev,
+		       DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+		       EP_CTRL_DIR_SEL);
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_ADDRESS_REG(sie->sie_num, ep),
+		       CY_UDC_REQ_BUFFER_ADDR(ep));
+	hpi_write_word(sie->dev,
+		       DEVICE_N_ENDPOINT_N_COUNT_REG(sie->sie_num, ep), len);
+	hpi_set_bits(sie->dev,
+		     DEVICE_N_ENDPOINT_N_CTL_REG(sie->sie_num, ep),
+		     EP_CTRL_ARM_EN);
+	return 0;
+}
+
+void c67x00_ll_susb_receive(struct c67x00_sie *sie, int ep,
+			    void *data, int len)
+{
+	c67x00_ll_read_mem_le16(sie->dev, CY_UDC_REQ_BUFFER_ADDR(ep),
+				data, len);
+}
+
+
+void c67x00_ll_susb_get_setup_data(struct c67x00_sie *sie, void *data)
+{
+	c67x00_ll_read_mem_le16(sie->dev,
+				DEVICE_SETUP_PACKET_ADDRESS(sie->sie_num),
+				data, 8);
+}
+
+void c67x00_ll_susb_set_addres(struct c67x00_sie *sie, u16 address)
+{
+	hpi_write_word(sie->dev, DEVICE_N_ADDRESS(sie->sie_num),
+				address);
+}
+
+/* -------------------------------------------------------------------------- */
 
 void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status)
 {
Index: linux-2.6/drivers/usb/c67x00/c67x00.h
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00.h
+++ linux-2.6/drivers/usb/c67x00/c67x00.h
@@ -35,6 +35,9 @@
 
 /* Hardware Revision Register */
 #define HW_REV_REG		0xC004
+#define HW_IRQ_EN_REG		0xC00E
+
+#define HOST_DEVICE_x_IRQ_EN(x) ((x) ? 0x0200 : 0x0100)
 
 /* General USB registers */
 /* ===================== */
@@ -50,18 +53,8 @@
 /* USB status register - Notice it has different content in hcd/udc mode */
 #define USB_STAT_REG(x)		((x) ? 0xC0B0 : 0xC090)
 
-#define EP0_IRQ_FLG		0x0001
-#define EP1_IRQ_FLG		0x0002
-#define EP2_IRQ_FLG		0x0004
-#define EP3_IRQ_FLG		0x0008
-#define EP4_IRQ_FLG		0x0010
-#define EP5_IRQ_FLG		0x0020
-#define EP6_IRQ_FLG		0x0040
-#define EP7_IRQ_FLG		0x0080
 #define RESET_IRQ_FLG		0x0100
 #define SOF_EOP_IRQ_FLG		0x0200
-#define ID_IRQ_FLG		0x4000
-#define VBUS_IRQ_FLG		0x8000
 
 /* USB Host only registers */
 /* ======================= */
@@ -78,9 +71,6 @@
 #define HOST_IRQ_EN_REG(x)	((x) ? 0xC0AC : 0xC08C)
 
 #define SOF_EOP_IRQ_EN		0x0200	/* SOF/EOP Interrupt Enable  */
-#define SOF_EOP_TMOUT_IRQ_EN	0x0800	/* SOF/EOP Timeout Interrupt Enable  */
-#define ID_IRQ_EN		0x4000	/* ID interrupt enable */
-#define VBUS_IRQ_EN		0x8000	/* VBUS interrupt enable */
 #define DONE_IRQ_EN		0x0001	/* Done Interrupt Enable  */
 
 /* USB status register */
@@ -99,18 +89,74 @@
 /* Device n Port Sel reg */
 #define DEVICE_N_PORT_SEL(x)	((x) ? 0xC0A4 : 0xC084)
 
+#define PERIPHERAL_PORT_A	0x0000
+#define PERIPHERAL_PORT_B	0x4000
+
 /* Device n Interrupt Enable Register */
 #define DEVICE_N_IRQ_EN_REG(x)	((x) ? 0xC0AC : 0xC08C)
 
+#define DEV_EP0_IRQ_EN		0x0001
+#define DEV_EP1_IRQ_EN		0x0002
+#define DEV_EP2_IRQ_EN		0x0004
+#define DEV_EP3_IRQ_EN		0x0008
+#define DEV_EP4_IRQ_EN		0x0010
+#define DEV_EP5_IRQ_EN		0x0020
+#define DEV_EP6_IRQ_EN		0x0040
+#define DEV_EP7_IRQ_EN		0x0080
+#define DEV_RESET_IRQ_EN	0x0100
+#define DEV_SOF_EOP_IRQ_EN	0x0200
+#define DEV_SOF_TMOUT_IRQ_EN	0x0800	/* SOF/EOP Timeout Interrupt Enable  */
+#define DEV_ID_IRQ_EN		0x4000
+#define DEV_VBUS_IRQ_EN		0x8000
+
+/* Endpoint control register */
 #define DEVICE_N_ENDPOINT_N_CTL_REG(dev, ep)	((dev)  		\
 						 ? (0x0280 + (ep << 4)) \
 						 : (0x0200 + (ep << 4)))
+#define EP_CTRL_ARM_EN		0x0001
+#define EP_CTRL_ENABLE		0X0002
+#define EP_CTRL_DIR_SEL		0x0004
+#define EP_CTRL_NACK_IRQ_EN	0x0008
+#define EP_CTRL_ISO_ENABLE	0x0010
+#define EP_CTRL_STALL_ENABLE	0x0020
+#define EP_CTRL_SEQ_SEL		0x0040
+#define EP_CTRL_IO_IGNORE	0x0080
+
+#define DEVICE_N_ENDPOINT_N_ADDRESS_REG(dev, ep) ((dev)  		\
+						 ? (0x0282 + (ep << 4)) \
+						 : (0x0202 + (ep << 4)))
+#define DEVICE_N_ENDPOINT_N_COUNT_REG(dev, ep)	((dev)  		\
+						 ? (0x0284 + (ep << 4)) \
+						 : (0x0204 + (ep << 4)))
 #define DEVICE_N_ENDPOINT_N_STAT_REG(dev, ep)	((dev)			\
 						 ? (0x0286 + (ep << 4)) \
 						 : (0x0206 + (ep << 4)))
+#define DEVICE_N_ENDPOINT_N_COUNT_RESULT_REG(dev, ep) ((dev)  		\
+						 ? (0x0288 + (ep << 4)) \
+						 : (0x0208 + (ep << 4)))
 
 #define DEVICE_N_ADDRESS(dev)	((dev) ? (0xC0AE) : (0xC08E))
 
+#define DEVICE_N_SOF_EOP_COUNT_REG(dev)	((dev) ? (0xC0B4) : (0xC094))
+
+#define DEVICE_SETUP_PACKET_ADDRESS(dev)	((dev) ? (0x0308) : (0x0300))
+
+
+#define EP_IRQ_FLG_ACK			0x0001
+#define EP_IRQ_FLG_ERROR		0x0002
+#define EP_IRQ_FLG_TIMEOUT		0x0004
+#define EP_IRQ_FLG_SEQ			0x0008
+#define EP_IRQ_FLG_SETUP		0x0010
+#define EP_IRQ_FLG_LEN_EXCEP	0x0020
+#define EP_IRQ_FLG_NACK			0x0040
+#define EP_IRQ_FLG_STALL		0x0080
+#define EP_IRQ_FLG_IN_EXCEP		0x0100
+#define EP_IRQ_FLG_OUT_EXCEP	0x0200
+#define EP_IRQ_FLG_UNDERFLOW	0x0400
+#define EP_IRQ_FLG_OVERFLOW		0x0800
+
+
+
 /* HPI registers */
 /* ============= */
 
@@ -159,13 +205,6 @@
 
 /* BIOS interrupt routines */
 
-#define SUSBx_RECEIVE_INT(x)	((x) ? 97 : 81)
-#define SUSBx_SEND_INT(x)	((x) ? 96 : 80)
-
-#define SUSBx_DEV_DESC_VEC(x)	((x) ? 0x00D4 : 0x00B4)
-#define SUSBx_CONF_DESC_VEC(x)	((x) ? 0x00D6 : 0x00B6)
-#define SUSBx_STRING_DESC_VEC(x) ((x) ? 0x00D8 : 0x00B8)
-
 #define CY_HCD_BUF_ADDR		0x500	/* Base address for host */
 #define SIE_TD_SIZE		0x200	/* size of the td list */
 #define SIE_TD_BUF_SIZE		0x400	/* size of the data buffer */
@@ -173,16 +212,6 @@
 #define SIE_TD_OFFSET(host)	((host) ? (SIE_TD_SIZE+SIE_TD_BUF_SIZE) : 0)
 #define SIE_BUF_OFFSET(host)	(SIE_TD_OFFSET(host) + SIE_TD_SIZE)
 
-/* Base address of HCD + 2 x TD_SIZE + 2 x TD_BUF_SIZE */
-#define CY_UDC_REQ_HEADER_BASE	0x1100
-/* 8- byte request headers for IN/OUT transfers */
-#define CY_UDC_REQ_HEADER_SIZE	8
-
-#define CY_UDC_REQ_HEADER_ADDR(ep_num)	(CY_UDC_REQ_HEADER_BASE + \
-					 ((ep_num) * CY_UDC_REQ_HEADER_SIZE))
-#define CY_UDC_DESC_BASE_ADDRESS	(CY_UDC_REQ_HEADER_ADDR(8))
-
-#define CY_UDC_BIOS_REPLACE_BASE	0x1800
 #define CY_UDC_REQ_BUFFER_BASE		0x2000
 #define CY_UDC_REQ_BUFFER_SIZE		0x0400
 #define CY_UDC_REQ_BUFFER_ADDR(ep_num)	(CY_UDC_REQ_BUFFER_BASE + \
@@ -283,6 +312,24 @@
 void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie);
 void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port);
 
+/* Slave specific functions */
+void c67x00_ll_susb_init(struct c67x00_sie *sie);
+void c67x00_ll_susb_disable(struct c67x00_sie *sie);
+void c67x00_ll_set_ep_ctrl_reg(struct c67x00_sie *sie, int ep_num, u16 val);
+void c67x00_ll_set_ep_stall(struct c67x00_sie *sie, int ep_num);
+void c67x00_ll_set_ep_packet_size_reg(struct c67x00_sie *sie, int ep_num,
+				      u16 val);
+u16 c67x00_ll_get_device_ep_status(struct c67x00_sie *sie, int ep);
+void c67x00_ll_set_device_ep_status(struct c67x00_sie *sie, int ep, u16 value);
+u16 c67x00_ll_get_device_ep_count_result(struct c67x00_sie *sie, int ep);
+int c67x00_ll_susb_start_send(struct c67x00_sie *sie, int ep,
+			      void *data, int len, int data_toggle);
+int c67x00_ll_susb_start_receive(struct c67x00_sie *sie, int ep, int len);
+void c67x00_ll_susb_receive(struct c67x00_sie *sie, int ep,
+			    void *data, int len);
+void c67x00_ll_susb_get_setup_data(struct c67x00_sie *sie, void *data);
+void c67x00_ll_susb_set_addres(struct c67x00_sie *sie, u16 address);
+
 /* Called by c67x00_irq to handle lcp interrupts */
 void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status);
 
Index: linux-2.6/drivers/usb/c67x00/Makefile
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/Makefile
+++ linux-2.6/drivers/usb/c67x00/Makefile
@@ -4,6 +4,9 @@
 
 ccflags-$(CONFIG_USB_DEBUG)		+= -DDEBUG
 
-obj-$(CONFIG_USB_C67X00_HCD)		+= c67x00.o
+obj-$(CONFIG_USB_C67X00_DRV)		+= c67x00.o
 
-c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o
+c67x00-y				+= c67x00-drv.o c67x00-ll-hpi.o
+
+c67x00-$(CONFIG_USB_C67X00_HCD)		+= c67x00-hcd.o c67x00-sched.o
+c67x00-$(CONFIG_USB_GADGET_C67X00)	+= c67x00-udc.o
Index: linux-2.6/drivers/usb/gadget/Kconfig
===================================================================
--- linux-2.6.orig/drivers/usb/gadget/Kconfig
+++ linux-2.6/drivers/usb/gadget/Kconfig
@@ -335,6 +335,13 @@
 	depends on USB_GADGET_AT91
 	default USB_GADGET
 
+config USB_GADGET_C67X00
+	boolean "Cypress C67X00 Gadget support"
+	depends on USB_C67X00_DRV
+	select USB_GADGET_SELECTED
+	help
+	  This enables the gadget functionality of the Cypress C67X00.
+
 config USB_GADGET_DUMMY_HCD
 	boolean "Dummy HCD (DEVELOPMENT)"
 	depends on USB=y || (USB=m && USB_GADGET=m)
Index: linux-2.6/drivers/usb/gadget/gadget_chips.h
===================================================================
--- linux-2.6.orig/drivers/usb/gadget/gadget_chips.h
+++ linux-2.6/drivers/usb/gadget/gadget_chips.h
@@ -147,6 +147,12 @@
 #define	gadget_is_m66592(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_C67X00
+#define	gadget_is_c67x00(g)	!strcmp("c67x00_udc", (g)->name)
+#else
+#define	gadget_is_c67x00(g)	0
+#endif
+
 
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
@@ -212,5 +218,7 @@
 		return 0x20;
 	else if (gadget_is_m66592(gadget))
 		return 0x21;
+	else if (gadget_is_c67x00(gadget))
+		return 0x22;
 	return -ENOENT;
 }
Index: linux-2.6/drivers/usb/host/Kconfig
===================================================================
--- linux-2.6.orig/drivers/usb/host/Kconfig
+++ linux-2.6/drivers/usb/host/Kconfig
@@ -4,19 +4,6 @@
 comment "USB Host Controller Drivers"
 	depends on USB
 
-config USB_C67X00_HCD
-	tristate "Cypress C67x00 HCD support"
-	depends on USB
-	help
-	  The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
-	  host/peripheral/OTG USB controllers.
-
-	  Enable this option to support this chip in host controller mode.
-	  If unsure, say N.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called c67x00.
-
 config USB_EHCI_HCD
 	tristate "EHCI HCD (USB 2.0) support"
 	depends on USB && USB_ARCH_HAS_EHCI
Index: linux-2.6/drivers/usb/Makefile
===================================================================
--- linux-2.6.orig/drivers/usb/Makefile
+++ linux-2.6/drivers/usb/Makefile
@@ -17,7 +17,7 @@
 obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
 
-obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/
+obj-$(CONFIG_USB_C67X00_DRV)	+= c67x00/
 
 obj-$(CONFIG_USB_ACM)		+= class/
 obj-$(CONFIG_USB_PRINTER)	+= class/
Index: linux-2.6/drivers/usb/Kconfig
===================================================================
--- linux-2.6.orig/drivers/usb/Kconfig
+++ linux-2.6/drivers/usb/Kconfig
@@ -97,6 +97,8 @@
 
 source "drivers/usb/host/Kconfig"
 
+source "drivers/usb/c67x00/Kconfig"
+
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
Index: linux-2.6/drivers/usb/c67x00/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/Kconfig
@@ -0,0 +1,21 @@
+#
+# Cypress C67x00 USB controller
+#
+config USB_C67X00_DRV
+	tristate "Cypress C67x00 support"
+	# only allowed to be =y if both USB!=m and USB_GADGET!=m
+	depends on (!USB && USB_GADGET) || (!USB_GADGET && USB) || (USB && USB_GADGET)
+	help
+	  The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
+	  host/peripheral USB controllers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called c67x00.
+
+config USB_C67X00_HCD
+	bool "Cypress C67X00 HCD support"
+	depends on USB && USB_C67X00_DRV
+	default y
+	help
+	  Enable this option to support the Cypress C67x00 in host
+	  controller mode.
Index: linux-2.6/drivers/usb/c67x00/c67x00-hcd.h
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00-hcd.h
+++ linux-2.6/drivers/usb/c67x00/c67x00-hcd.h
@@ -112,8 +112,21 @@
  * Functions used by c67x00-drv
  */
 
+#ifdef CONFIG_USB_C67X00_HCD
 int c67x00_hcd_probe(struct c67x00_sie *sie);
 void c67x00_hcd_remove(struct c67x00_sie *sie);
+#else
+static inline int c67x00_hcd_probe(struct c67x00_sie *sie)
+{
+	printk(KERN_ERR "hcd requested but CONFIG_USB_C67X00_HCD "
+	       "not enabled!\n");
+	return -ENODEV;
+}
+
+static inline void c67x00_hcd_remove(struct c67x00_sie *sie)
+{
+}
+#endif				/* CONFIG_USB_C67X00_HCD */
 
 /* ---------------------------------------------------------------------
  * Transfer Descriptor scheduling functions

-- 
Bye, Peter Korsgaard

^ permalink raw reply

* [patch v11 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Peter Korsgaard @ 2008-04-27  6:59 UTC (permalink / raw)
  To: dbrownell, linux-usb, linuxppc-dev, greg
In-Reply-To: <20080427065942.514492739@sunsite.dk>

This patch adds HCD support for the Cypress c67x00 family of devices.

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
---
 drivers/usb/Makefile               |    2 
 drivers/usb/c67x00/Makefile        |    9 
 drivers/usb/c67x00/c67x00-drv.c    |   13 
 drivers/usb/c67x00/c67x00-hcd.c    |  412 +++++++++++++
 drivers/usb/c67x00/c67x00-hcd.h    |  133 ++++
 drivers/usb/c67x00/c67x00-ll-hpi.c |   75 ++
 drivers/usb/c67x00/c67x00-sched.c  | 1170 +++++++++++++++++++++++++++++++++++++
 drivers/usb/c67x00/c67x00.h        |    9 
 drivers/usb/host/Kconfig           |   13 
 9 files changed, 1836 insertions(+)

Index: linux-2.6/drivers/usb/c67x00/c67x00-hcd.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-hcd.c
@@ -0,0 +1,412 @@
+/*
+ * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+/* --------------------------------------------------------------------------
+ * Root Hub Support
+ */
+
+static __u8 c67x00_hub_des[] = {
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,			/*   (per-port OC, no power switching) */
+	0x32,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; ** 7 Ports max ** */
+	0xff,			/*  __u8  PortPwrCtrlMask; ** 7 ports max ** */
+};
+
+static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
+{
+	struct c67x00_hcd *c67x00 = sie->private_data;
+	unsigned long flags;
+
+	c67x00_ll_husb_reset(sie, port);
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+	c67x00_ll_husb_reset_port(sie, port);
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	c67x00_ll_set_husb_eot(sie->dev, DEFAULT_EOT);
+}
+
+static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	struct c67x00_sie *sie = c67x00->sie;
+	u16 status;
+	int i;
+
+	*buf = 0;
+	status = c67x00_ll_usb_get_status(sie);
+	for (i = 0; i < C67X00_PORTS; i++)
+		if (status & PORT_CONNECT_CHANGE(i))
+			*buf |= (1 << i);
+
+	/* bit 0 denotes hub change, b1..n port change */
+	*buf <<= 1;
+
+	return !!*buf;
+}
+
+static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+			      u16 wIndex, char *buf, u16 wLength)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	struct c67x00_sie *sie = c67x00->sie;
+	u16 status, usb_status;
+	int len = 0;
+	unsigned int port = wIndex-1;
+	u16 wPortChange, wPortStatus;
+
+	switch (typeReq) {
+
+	case GetHubStatus:
+		*(__le32 *) buf = cpu_to_le32(0);
+		len = 4;		/* hub power */
+		break;
+
+	case GetPortStatus:
+		if (wIndex > C67X00_PORTS)
+			return -EPIPE;
+
+		status = c67x00_ll_usb_get_status(sie);
+		usb_status = c67x00_ll_get_usb_ctl(sie);
+
+		wPortChange = 0;
+		if (status & PORT_CONNECT_CHANGE(port))
+			wPortChange |= USB_PORT_STAT_C_CONNECTION;
+
+		wPortStatus = USB_PORT_STAT_POWER;
+		if (!(status & PORT_SE0_STATUS(port)))
+			wPortStatus |= USB_PORT_STAT_CONNECTION;
+		if (usb_status & LOW_SPEED_PORT(port)) {
+			wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+			c67x00->low_speed_ports |= (1 << port);
+		} else
+			c67x00->low_speed_ports &= ~(1 << port);
+
+		if (usb_status & SOF_EOP_EN(port))
+			wPortStatus |= USB_PORT_STAT_ENABLE;
+
+		*(__le16 *) buf = cpu_to_le16(wPortStatus);
+		*(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
+		len = 4;
+		break;
+
+	case SetHubFeature:	/* We don't implement these */
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+		case C_HUB_LOCAL_POWER:
+			len = 0;
+			break;
+
+		default:
+			return -EPIPE;
+		}
+		break;
+
+	case SetPortFeature:
+		if (wIndex > C67X00_PORTS)
+			return -EPIPE;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"SetPortFeature %d (SUSPEND)\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_RESET:
+			c67x00_hub_reset_host_port(sie, port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_POWER:
+			/* Power always enabled */
+			len = 0;
+			break;
+
+		default:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"%s: SetPortFeature %d (0x%04x) Error!\n",
+				__func__, port, wValue);
+			return -EPIPE;
+		}
+		break;
+
+	case ClearPortFeature:
+		if (wIndex > C67X00_PORTS)
+			return -EPIPE;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			/* Reset the port so that the c67x00 also notices the
+			 * disconnect */
+			c67x00_hub_reset_host_port(sie, port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_ENABLE:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): C_ENABLE\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_SUSPEND:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): SUSPEND\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_SUSPEND:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): C_SUSPEND\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_POWER:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): POWER\n", port);
+			return -EPIPE;
+
+		case USB_PORT_FEAT_C_CONNECTION:
+			c67x00_ll_usb_clear_status(sie,
+						   PORT_CONNECT_CHANGE(port));
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): OVER_CURRENT\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_RESET:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): C_RESET\n", port);
+			len = 0;
+			break;
+
+		default:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"%s: ClearPortFeature %d (0x%04x) Error!\n",
+				__func__, port, wValue);
+			return -EPIPE;
+		}
+		break;
+
+	case GetHubDescriptor:
+		len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
+		memcpy(buf, c67x00_hub_des, len);
+		break;
+
+	default:
+		dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __func__);
+		return -EPIPE;
+	}
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Main part of host controller driver
+ */
+
+/**
+ * c67x00_hcd_irq
+ *
+ * This function is called from the interrupt handler in c67x00-drv.c
+ */
+static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
+{
+	struct c67x00_hcd *c67x00 = sie->private_data;
+	struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+
+	/* Handle sie message flags */
+	if (msg) {
+		if (msg & HUSB_TDListDone)
+			c67x00_sched_kick(c67x00);
+		else
+			dev_warn(c67x00_hcd_dev(c67x00),
+				 "Unknown SIE msg flag(s): 0x%04x\n", msg);
+	}
+
+	if (unlikely(hcd->state == HC_STATE_HALT))
+		return;
+
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+		return;
+
+	/* Handle Start of frame events */
+	if (int_status & SOFEOP_FLG(sie->sie_num)) {
+		c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
+		c67x00_sched_kick(c67x00);
+		set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+	}
+}
+
+/**
+ * c67x00_hcd_start: Host controller start hook
+ */
+static int c67x00_hcd_start(struct usb_hcd *hcd)
+{
+	hcd->uses_new_polling = 1;
+	hcd->state = HC_STATE_RUNNING;
+	hcd->poll_rh = 1;
+
+	return 0;
+}
+
+/**
+ * c67x00_hcd_stop: Host controller stop hook
+ */
+static void c67x00_hcd_stop(struct usb_hcd *hcd)
+{
+	/* Nothing to do */
+}
+
+static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	u16 temp_val;
+
+	dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __func__);
+	temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
+	temp_val &= HOST_FRAME_MASK;
+	return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
+}
+
+static struct hc_driver c67x00_hc_driver = {
+	.description	= "c67x00-hcd",
+	.product_desc	= "Cypress C67X00 Host Controller",
+	.hcd_priv_size	= sizeof(struct c67x00_hcd),
+	.flags		= HCD_USB11 | HCD_MEMORY,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.start		= c67x00_hcd_start,
+	.stop		= c67x00_hcd_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue	= c67x00_urb_enqueue,
+	.urb_dequeue	= c67x00_urb_dequeue,
+	.endpoint_disable = c67x00_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number = c67x00_hcd_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data = c67x00_hub_status_data,
+	.hub_control	= c67x00_hub_control,
+};
+
+/* ---------------------------------------------------------------------
+ * Setup/Teardown routines
+ */
+
+int c67x00_hcd_probe(struct c67x00_sie *sie)
+{
+	struct c67x00_hcd *c67x00;
+	struct usb_hcd *hcd;
+	unsigned long flags;
+	int retval;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	hcd = usb_create_hcd(&c67x00_hc_driver, sie_dev(sie), "c67x00_sie");
+	if (!hcd) {
+		retval = -ENOMEM;
+		goto err0;
+	}
+	c67x00 = hcd_to_c67x00_hcd(hcd);
+
+	spin_lock_init(&c67x00->lock);
+	c67x00->sie = sie;
+
+	INIT_LIST_HEAD(&c67x00->list[PIPE_ISOCHRONOUS]);
+	INIT_LIST_HEAD(&c67x00->list[PIPE_INTERRUPT]);
+	INIT_LIST_HEAD(&c67x00->list[PIPE_CONTROL]);
+	INIT_LIST_HEAD(&c67x00->list[PIPE_BULK]);
+	c67x00->urb_count = 0;
+	INIT_LIST_HEAD(&c67x00->td_list);
+	c67x00->td_base_addr = CY_HCD_BUF_ADDR + SIE_TD_OFFSET(sie->sie_num);
+	c67x00->buf_base_addr = CY_HCD_BUF_ADDR + SIE_BUF_OFFSET(sie->sie_num);
+	c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+
+	c67x00_ll_husb_init_host_port(sie);
+
+	init_completion(&c67x00->endpoint_disable);
+	retval = c67x00_sched_start_scheduler(c67x00);
+	if (retval)
+		goto err1;
+
+	retval = usb_add_hcd(hcd, 0, 0);
+	if (retval) {
+		dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
+			__func__, retval);
+		goto err2;
+	}
+
+	spin_lock_irqsave(&sie->lock, flags);
+	sie->private_data = c67x00;
+	sie->irq = c67x00_hcd_irq;
+	spin_unlock_irqrestore(&sie->lock, flags);
+
+	return retval;
+
+ err2:
+	c67x00_sched_stop_scheduler(c67x00);
+ err1:
+	usb_put_hcd(hcd);
+ err0:
+	return retval;
+}
+
+/* may be called with controller, bus, and devices active */
+void c67x00_hcd_remove(struct c67x00_sie *sie)
+{
+	struct c67x00_hcd *c67x00 = sie->private_data;
+	struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+
+	c67x00_sched_stop_scheduler(c67x00);
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+}
Index: linux-2.6/drivers/usb/c67x00/c67x00-hcd.h
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-hcd.h
@@ -0,0 +1,133 @@
+/*
+ * c67x00-hcd.h: Cypress C67X00 USB HCD
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#ifndef _USB_C67X00_HCD_H
+#define _USB_C67X00_HCD_H
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include "../core/hcd.h"
+#include "c67x00.h"
+
+/*
+ * The following parameters depend on the CPU speed, bus speed, ...
+ * These can be tuned for specific use cases, e.g. if isochronous transfers
+ * are very important, bandwith can be sacrificed to guarantee that the
+ * 1ms deadline will be met.
+ * If bulk transfers are important, the MAX_FRAME_BW can be increased,
+ * but some (or many) isochronous deadlines might not be met.
+ *
+ * The values are specified in bittime.
+ */
+
+/*
+ * The current implementation switches between _STD (default) and _ISO (when
+ * isochronous transfers are scheduled), in order to optimize the throughput
+ * in normal cicrumstances, but also provide good isochronous behaviour.
+ *
+ * Bandwidth is described in bit time so with a 12MHz USB clock and 1ms
+ * frames; there are 12000 bit times per frame.
+ */
+
+#define TOTAL_FRAME_BW		12000
+#define DEFAULT_EOT		2250
+
+#define MAX_FRAME_BW_STD	(TOTAL_FRAME_BW - DEFAULT_EOT)
+#define MAX_FRAME_BW_ISO	2400
+
+/*
+ * Periodic transfers may only use 90% of the full frame, but as
+ * we currently don't even use 90% of the full frame, we may
+ * use the full usable time for periodic transfers.
+ */
+#define MAX_PERIODIC_BW(full_bw)	full_bw
+
+/* -------------------------------------------------------------------------- */
+
+struct c67x00_hcd {
+	spinlock_t lock;
+	struct c67x00_sie *sie;
+	unsigned int low_speed_ports;	/* bitmask of low speed ports */
+	unsigned int urb_count;
+	unsigned int urb_iso_count;
+
+	struct list_head list[4];	/* iso, int, ctrl, bulk */
+#if PIPE_BULK != 3
+#error "Sanity check failed, this code presumes PIPE_... to range from 0 to 3"
+#endif
+
+	/* USB bandwidth allocated to td_list */
+	int bandwidth_allocated;
+	/* USB bandwidth allocated for isoc/int transfer */
+	int periodic_bw_allocated;
+	struct list_head td_list;
+	int max_frame_bw;
+
+	u16 td_base_addr;
+	u16 buf_base_addr;
+	u16 next_td_addr;
+	u16 next_buf_addr;
+
+	struct tasklet_struct tasklet;
+
+	struct completion endpoint_disable;
+
+	u16 current_frame;
+	u16 last_frame;
+};
+
+static inline struct c67x00_hcd *hcd_to_c67x00_hcd(struct usb_hcd *hcd)
+{
+	return (struct c67x00_hcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *c67x00_hcd_to_hcd(struct c67x00_hcd *c67x00)
+{
+	return container_of((void *)c67x00, struct usb_hcd, hcd_priv);
+}
+
+/* ---------------------------------------------------------------------
+ * Functions used by c67x00-drv
+ */
+
+int c67x00_hcd_probe(struct c67x00_sie *sie);
+void c67x00_hcd_remove(struct c67x00_sie *sie);
+
+/* ---------------------------------------------------------------------
+ * Transfer Descriptor scheduling functions
+ */
+int c67x00_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+void c67x00_endpoint_disable(struct usb_hcd *hcd,
+			     struct usb_host_endpoint *ep);
+
+void c67x00_hcd_msg_received(struct c67x00_sie *sie, u16 msg);
+void c67x00_sched_kick(struct c67x00_hcd *c67x00);
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00);
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00);
+
+#define c67x00_hcd_dev(x)	(c67x00_hcd_to_hcd(x)->self.controller)
+
+#endif				/* _USB_C67X00_HCD_H */
Index: linux-2.6/drivers/usb/c67x00/c67x00-sched.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/c67x00-sched.c
@@ -0,0 +1,1170 @@
+/*
+ * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <linux/kthread.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+/*
+ * These are the stages for a control urb, they are kept
+ * in both urb->interval and td->privdata.
+ */
+#define SETUP_STAGE		0
+#define DATA_STAGE		1
+#define STATUS_STAGE		2
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct c67x00_ep_data: Host endpoint data structure
+ */
+struct c67x00_ep_data {
+	struct list_head queue;
+	struct list_head node;
+	struct usb_host_endpoint *hep;
+	struct usb_device *dev;
+	u16 next_frame;		/* For int/isoc transactions */
+};
+
+/**
+ * struct c67x00_td
+ *
+ * Hardware parts are little endiannes, SW in CPU endianess.
+ */
+struct c67x00_td {
+	/* HW specific part */
+	__le16 ly_base_addr;	/* Bytes 0-1 */
+	__le16 port_length;	/* Bytes 2-3 */
+	u8 pid_ep;		/* Byte 4 */
+	u8 dev_addr;		/* Byte 5 */
+	u8 ctrl_reg;		/* Byte 6 */
+	u8 status;		/* Byte 7 */
+	u8 retry_cnt;		/* Byte 8 */
+#define TT_OFFSET		2
+#define TT_CONTROL		0
+#define TT_ISOCHRONOUS		1
+#define TT_BULK			2
+#define TT_INTERRUPT		3
+	u8 residue;		/* Byte 9 */
+	__le16 next_td_addr;	/* Bytes 10-11 */
+	/* SW part */
+	struct list_head td_list;
+	u16 td_addr;
+	void *data;
+	struct urb *urb;
+	unsigned long privdata;
+
+	/* These are needed for handling the toggle bits:
+	 * an urb can be dequeued while a td is in progress
+	 * after checking the td, the toggle bit might need to
+	 * be fixed */
+	struct c67x00_ep_data *ep_data;
+	unsigned int pipe;
+};
+
+struct c67x00_urb_priv {
+	struct list_head hep_node;
+	struct urb *urb;
+	int port;
+	int cnt;		/* packet number for isoc */
+	int status;
+	struct c67x00_ep_data *ep_data;
+};
+
+#define td_udev(td)	((td)->ep_data->dev)
+
+#define CY_TD_SIZE		12
+
+#define TD_PIDEP_OFFSET		0x04
+#define TD_PIDEPMASK_PID	0xF0
+#define TD_PIDEPMASK_EP		0x0F
+#define TD_PORTLENMASK_DL	0x02FF
+#define TD_PORTLENMASK_PN	0xC000
+
+#define TD_STATUS_OFFSET	0x07
+#define TD_STATUSMASK_ACK	0x01
+#define TD_STATUSMASK_ERR	0x02
+#define TD_STATUSMASK_TMOUT	0x04
+#define TD_STATUSMASK_SEQ	0x08
+#define TD_STATUSMASK_SETUP	0x10
+#define TD_STATUSMASK_OVF	0x20
+#define TD_STATUSMASK_NAK	0x40
+#define TD_STATUSMASK_STALL	0x80
+
+#define TD_ERROR_MASK		(TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \
+				 TD_STATUSMASK_STALL)
+
+#define TD_RETRYCNT_OFFSET	0x08
+#define TD_RETRYCNTMASK_ACT_FLG	0x10
+#define TD_RETRYCNTMASK_TX_TYPE	0x0C
+#define TD_RETRYCNTMASK_RTY_CNT	0x03
+
+#define TD_RESIDUE_OVERFLOW	0x80
+
+#define TD_PID_IN		0x90
+
+/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */
+#define td_residue(td)		((__s8)(td->residue))
+#define td_ly_base_addr(td)	(__le16_to_cpu((td)->ly_base_addr))
+#define td_port_length(td)	(__le16_to_cpu((td)->port_length))
+#define td_next_td_addr(td)	(__le16_to_cpu((td)->next_td_addr))
+
+#define td_active(td)		((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG)
+#define td_length(td)		(td_port_length(td) & TD_PORTLENMASK_DL)
+
+#define td_sequence_ok(td)	(!td->status || \
+				 (!(td->status & TD_STATUSMASK_SEQ) ==	\
+				  !(td->ctrl_reg & SEQ_SEL)))
+
+#define td_acked(td)		(!td->status || \
+				 (td->status & TD_STATUSMASK_ACK))
+#define td_actual_bytes(td)	(td_length(td) - td_residue(td))
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef DEBUG
+
+/**
+ * dbg_td - Dump the contents of the TD
+ */
+static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg)
+{
+	struct device *dev = c67x00_hcd_dev(c67x00);
+
+	dev_dbg(dev, "### %s at 0x%04x\n", msg, td->td_addr);
+	dev_dbg(dev, "urb:      0x%p\n", td->urb);
+	dev_dbg(dev, "endpoint:   %4d\n", usb_pipeendpoint(td->pipe));
+	dev_dbg(dev, "pipeout:    %4d\n", usb_pipeout(td->pipe));
+	dev_dbg(dev, "ly_base_addr: 0x%04x\n", td_ly_base_addr(td));
+	dev_dbg(dev, "port_length:  0x%04x\n", td_port_length(td));
+	dev_dbg(dev, "pid_ep:         0x%02x\n", td->pid_ep);
+	dev_dbg(dev, "dev_addr:       0x%02x\n", td->dev_addr);
+	dev_dbg(dev, "ctrl_reg:       0x%02x\n", td->ctrl_reg);
+	dev_dbg(dev, "status:         0x%02x\n", td->status);
+	dev_dbg(dev, "retry_cnt:      0x%02x\n", td->retry_cnt);
+	dev_dbg(dev, "residue:        0x%02x\n", td->residue);
+	dev_dbg(dev, "next_td_addr: 0x%04x\n", td_next_td_addr(td));
+	dev_dbg(dev, "data:");
+	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1,
+		       td->data, td_length(td), 1);
+}
+#else				/* DEBUG */
+
+static inline void
+dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) { }
+
+#endif				/* DEBUG */
+
+/* -------------------------------------------------------------------------- */
+/* Helper functions */
+
+static inline u16 c67x00_get_current_frame_number(struct c67x00_hcd *c67x00)
+{
+	return c67x00_ll_husb_get_frame(c67x00->sie) & HOST_FRAME_MASK;
+}
+
+/**
+ * frame_add
+ * Software wraparound for framenumbers.
+ */
+static inline u16 frame_add(u16 a, u16 b)
+{
+	return (a + b) & HOST_FRAME_MASK;
+}
+
+/**
+ * frame_after - is frame a after frame b
+ */
+static inline int frame_after(u16 a, u16 b)
+{
+	return ((HOST_FRAME_MASK + a - b) & HOST_FRAME_MASK) <
+	    (HOST_FRAME_MASK / 2);
+}
+
+/**
+ * frame_after_eq - is frame a after or equal to frame b
+ */
+static inline int frame_after_eq(u16 a, u16 b)
+{
+	return ((HOST_FRAME_MASK + 1 + a - b) & HOST_FRAME_MASK) <
+	    (HOST_FRAME_MASK / 2);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_release_urb - remove link from all tds to this urb
+ * Disconnects the urb from it's tds, so that it can be given back.
+ * pre: urb->hcpriv != NULL
+ */
+static void c67x00_release_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct c67x00_td *td;
+	struct c67x00_urb_priv *urbp;
+
+	BUG_ON(!urb);
+
+	c67x00->urb_count--;
+
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		c67x00->urb_iso_count--;
+		if (c67x00->urb_iso_count == 0)
+			c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+	}
+
+	/* TODO this might be not so efficient when we've got many urbs!
+	 * Alternatives:
+	 *   * only clear when needed
+	 *   * keep a list of tds with each urbp
+	 */
+	list_for_each_entry(td, &c67x00->td_list, td_list)
+		if (urb == td->urb)
+			td->urb = NULL;
+
+	urbp = urb->hcpriv;
+	urb->hcpriv = NULL;
+	list_del(&urbp->hep_node);
+	kfree(urbp);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct c67x00_ep_data *
+c67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct usb_host_endpoint *hep = urb->ep;
+	struct c67x00_ep_data *ep_data;
+	int type;
+
+	c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+
+	/* Check if endpoint already has a c67x00_ep_data struct allocated */
+	if (hep->hcpriv) {
+		ep_data = hep->hcpriv;
+		if (frame_after(c67x00->current_frame, ep_data->next_frame))
+			ep_data->next_frame =
+			    frame_add(c67x00->current_frame, 1);
+		return hep->hcpriv;
+	}
+
+	/* Allocate and initialize a new c67x00 endpoint data structure */
+	ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC);
+	if (!ep_data)
+		return NULL;
+
+	INIT_LIST_HEAD(&ep_data->queue);
+	INIT_LIST_HEAD(&ep_data->node);
+	ep_data->hep = hep;
+
+	/* hold a reference to udev as long as this endpoint lives,
+	 * this is needed to possibly fix the data toggle */
+	ep_data->dev = usb_get_dev(urb->dev);
+	hep->hcpriv = ep_data;
+
+	/* For ISOC and INT endpoints, start ASAP: */
+	ep_data->next_frame = frame_add(c67x00->current_frame, 1);
+
+	/* Add the endpoint data to one of the pipe lists; must be added
+	   in order of endpoint address */
+	type = usb_pipetype(urb->pipe);
+	if (list_empty(&ep_data->node)) {
+		list_add(&ep_data->node, &c67x00->list[type]);
+	} else {
+		struct c67x00_ep_data *prev;
+
+		list_for_each_entry(prev, &c67x00->list[type], node) {
+			if (prev->hep->desc.bEndpointAddress >
+			    hep->desc.bEndpointAddress) {
+				list_add(&ep_data->node, prev->node.prev);
+				break;
+			}
+		}
+	}
+
+	return ep_data;
+}
+
+static int c67x00_ep_data_free(struct usb_host_endpoint *hep)
+{
+	struct c67x00_ep_data *ep_data = hep->hcpriv;
+
+	if (!ep_data)
+		return 0;
+
+	if (!list_empty(&ep_data->queue))
+		return -EBUSY;
+
+	usb_put_dev(ep_data->dev);
+	list_del(&ep_data->queue);
+	list_del(&ep_data->node);
+
+	kfree(ep_data);
+	hep->hcpriv = NULL;
+
+	return 0;
+}
+
+void c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	unsigned long flags;
+
+	if (!list_empty(&ep->urb_list))
+		dev_warn(c67x00_hcd_dev(c67x00), "error: urb list not empty\n");
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+
+	/* loop waiting for all transfers in the endpoint queue to complete */
+	while (c67x00_ep_data_free(ep)) {
+		/* Drop the lock so we can sleep waiting for the hardware */
+		spin_unlock_irqrestore(&c67x00->lock, flags);
+
+		/* it could happen that we reinitialize this completion, while
+		 * somebody was waiting for that completion.  The timeout and
+		 * while loop handle such cases, but this might be improved */
+		INIT_COMPLETION(c67x00->endpoint_disable);
+		c67x00_sched_kick(c67x00);
+		wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ);
+
+		spin_lock_irqsave(&c67x00->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int get_root_port(struct usb_device *dev)
+{
+	while (dev->parent->parent)
+		dev = dev->parent;
+	return dev->portnum;
+}
+
+int c67x00_urb_enqueue(struct usb_hcd *hcd,
+		       struct urb *urb, gfp_t mem_flags)
+{
+	int ret;
+	unsigned long flags;
+	struct c67x00_urb_priv *urbp;
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	int port = get_root_port(urb->dev)-1;
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+
+	/* Make sure host controller is running */
+	if (!HC_IS_RUNNING(hcd->state)) {
+		ret = -ENODEV;
+		goto err_not_linked;
+	}
+
+	ret = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (ret)
+		goto err_not_linked;
+
+	/* Allocate and initialize urb private data */
+	urbp = kzalloc(sizeof(*urbp), mem_flags);
+	if (!urbp) {
+		ret = -ENOMEM;
+		goto err_urbp;
+	}
+
+	INIT_LIST_HEAD(&urbp->hep_node);
+	urbp->urb = urb;
+	urbp->port = port;
+
+	urbp->ep_data = c67x00_ep_data_alloc(c67x00, urb);
+
+	if (!urbp->ep_data) {
+		ret = -ENOMEM;
+		goto err_epdata;
+	}
+
+	/* TODO claim bandwidth with usb_claim_bandwidth?
+	 * also release it somewhere! */
+
+	urb->hcpriv = urbp;
+
+	urb->actual_length = 0;	/* Nothing received/transmitted yet */
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		urb->interval = SETUP_STAGE;
+		break;
+	case PIPE_INTERRUPT:
+		break;
+	case PIPE_BULK:
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (c67x00->urb_iso_count == 0)
+			c67x00->max_frame_bw = MAX_FRAME_BW_ISO;
+		c67x00->urb_iso_count++;
+		/* Assume always URB_ISO_ASAP, FIXME */
+		if (list_empty(&urbp->ep_data->queue))
+			urb->start_frame = urbp->ep_data->next_frame;
+		else {
+			/* Go right after the last one */
+			struct urb *last_urb;
+
+			last_urb = list_entry(urbp->ep_data->queue.prev,
+					      struct c67x00_urb_priv,
+					      hep_node)->urb;
+			urb->start_frame =
+			    frame_add(last_urb->start_frame,
+				      last_urb->number_of_packets *
+				      last_urb->interval);
+		}
+		urbp->cnt = 0;
+		break;
+	}
+
+	/* Add the URB to the endpoint queue */
+	list_add_tail(&urbp->hep_node, &urbp->ep_data->queue);
+
+	/* If this is the only URB, kick start the controller */
+	if (!c67x00->urb_count++)
+		c67x00_ll_hpi_enable_sofeop(c67x00->sie);
+
+	c67x00_sched_kick(c67x00);
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	return 0;
+
+err_epdata:
+	kfree(urbp);
+err_urbp:
+	usb_hcd_unlink_urb_from_ep(hcd, urb);
+err_not_linked:
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	return ret;
+}
+
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+	rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+	if (rc)
+		goto done;
+
+	c67x00_release_urb(c67x00, urb);
+	usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+	spin_unlock(&c67x00->lock);
+	usb_hcd_giveback_urb(hcd, urb, status);
+	spin_lock(&c67x00->lock);
+
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	return 0;
+
+ done:
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+	return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * pre: c67x00 locked, urb unlocked
+ */
+static void
+c67x00_giveback_urb(struct c67x00_hcd *c67x00, struct urb *urb, int status)
+{
+	struct c67x00_urb_priv *urbp;
+
+	if (!urb)
+		return;
+
+	urbp = urb->hcpriv;
+	urbp->status = status;
+
+	list_del_init(&urbp->hep_node);
+
+	c67x00_release_urb(c67x00, urb);
+	usb_hcd_unlink_urb_from_ep(c67x00_hcd_to_hcd(c67x00), urb);
+	spin_unlock(&c67x00->lock);
+	usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb, urbp->status);
+	spin_lock(&c67x00->lock);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int c67x00_claim_frame_bw(struct c67x00_hcd *c67x00, struct urb *urb,
+				 int len, int periodic)
+{
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+	int bit_time;
+
+	/* According to the C67x00 BIOS user manual, page 3-18,19, the
+	 * following calculations provide the full speed bit times for
+	 * a transaction.
+	 *
+	 * FS(in)	= 112.5 +  9.36*BC + HOST_DELAY
+	 * FS(in,iso)	=  90.5 +  9.36*BC + HOST_DELAY
+	 * FS(out)	= 112.5 +  9.36*BC + HOST_DELAY
+	 * FS(out,iso)	=  78.4 +  9.36*BC + HOST_DELAY
+	 * LS(in)	= 802.4 + 75.78*BC + HOST_DELAY
+	 * LS(out)	= 802.6 + 74.67*BC + HOST_DELAY
+	 *
+	 * HOST_DELAY == 106 for the c67200 and c67300.
+	 */
+
+	/* make calculations in 1/100 bit times to maintain resolution */
+	if (urbp->ep_data->dev->speed == USB_SPEED_LOW) {
+		/* Low speed pipe */
+		if (usb_pipein(urb->pipe))
+			bit_time = 80240 + 7578*len;
+		else
+			bit_time = 80260 + 7467*len;
+	} else {
+		/* FS pipes */
+		if (usb_pipeisoc(urb->pipe))
+			bit_time = usb_pipein(urb->pipe) ? 9050 : 7840;
+		else
+			bit_time = 11250;
+		bit_time += 936*len;
+	}
+
+	/* Scale back down to integer bit times.  Use a host delay of 106.
+	 * (this is the only place it is used) */
+	bit_time = ((bit_time+50) / 100) + 106;
+
+	if (unlikely(bit_time + c67x00->bandwidth_allocated >=
+		     c67x00->max_frame_bw))
+		return -EMSGSIZE;
+
+	if (unlikely(c67x00->next_td_addr + CY_TD_SIZE >=
+		     c67x00->td_base_addr + SIE_TD_SIZE))
+		return -EMSGSIZE;
+
+	if (unlikely(c67x00->next_buf_addr + len >=
+		     c67x00->buf_base_addr + SIE_TD_BUF_SIZE))
+		return -EMSGSIZE;
+
+	if (periodic) {
+		if (unlikely(bit_time + c67x00->periodic_bw_allocated >=
+			     MAX_PERIODIC_BW(c67x00->max_frame_bw)))
+			return -EMSGSIZE;
+		c67x00->periodic_bw_allocated += bit_time;
+	}
+
+	c67x00->bandwidth_allocated += bit_time;
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * td_addr and buf_addr must be word aligned
+ */
+static int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb,
+			    void *data, int len, int pid, int toggle,
+			    unsigned long privdata)
+{
+	struct c67x00_td *td;
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+	const __u8 active_flag = 1, retry_cnt = 1;
+	__u8 cmd = 0;
+	int tt = 0;
+
+	if (c67x00_claim_frame_bw(c67x00, urb, len, usb_pipeisoc(urb->pipe)
+				  || usb_pipeint(urb->pipe)))
+		return -EMSGSIZE;	/* Not really an error, but expected */
+
+	td = kzalloc(sizeof(*td), GFP_ATOMIC);
+	if (!td)
+		return -ENOMEM;
+
+	td->pipe = urb->pipe;
+	td->ep_data = urbp->ep_data;
+
+	if ((td_udev(td)->speed == USB_SPEED_LOW) &&
+	    !(c67x00->low_speed_ports & (1 << urbp->port)))
+		cmd |= PREAMBLE_EN;
+
+	switch (usb_pipetype(td->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		tt = TT_ISOCHRONOUS;
+		cmd |= ISO_EN;
+		break;
+	case PIPE_CONTROL:
+		tt = TT_CONTROL;
+		break;
+	case PIPE_BULK:
+		tt = TT_BULK;
+		break;
+	case PIPE_INTERRUPT:
+		tt = TT_INTERRUPT;
+		break;
+	}
+
+	if (toggle)
+		cmd |= SEQ_SEL;
+
+	cmd |= ARM_EN;
+
+	/* SW part */
+	td->td_addr = c67x00->next_td_addr;
+	c67x00->next_td_addr = c67x00->next_td_addr + CY_TD_SIZE;
+
+	/* HW part */
+	td->ly_base_addr = __cpu_to_le16(c67x00->next_buf_addr);
+	td->port_length = __cpu_to_le16((c67x00->sie->sie_num << 15) |
+					(urbp->port << 14) | (len & 0x3FF));
+	td->pid_ep = ((pid & 0xF) << TD_PIDEP_OFFSET) |
+	    (usb_pipeendpoint(td->pipe) & 0xF);
+	td->dev_addr = usb_pipedevice(td->pipe) & 0x7F;
+	td->ctrl_reg = cmd;
+	td->status = 0;
+	td->retry_cnt = (tt << TT_OFFSET) | (active_flag << 4) | retry_cnt;
+	td->residue = 0;
+	td->next_td_addr = __cpu_to_le16(c67x00->next_td_addr);
+
+	/* SW part */
+	td->data = data;
+	td->urb = urb;
+	td->privdata = privdata;
+
+	c67x00->next_buf_addr += (len + 1) & ~0x01;	/* properly align */
+
+	list_add_tail(&td->td_list, &c67x00->td_list);
+	return 0;
+}
+
+static inline void c67x00_release_td(struct c67x00_td *td)
+{
+	list_del_init(&td->td_list);
+	kfree(td);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int c67x00_add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	int remaining;
+	int toggle;
+	int pid;
+	int ret = 0;
+	int maxps;
+	int need_empty;
+
+	toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			       usb_pipeout(urb->pipe));
+	remaining = urb->transfer_buffer_length - urb->actual_length;
+
+	maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+	    usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+	while (remaining || need_empty) {
+		int len;
+		char *td_buf;
+
+		len = (remaining > maxps) ? maxps : remaining;
+		if (!len)
+			need_empty = 0;
+
+		pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+		td_buf = urb->transfer_buffer + urb->transfer_buffer_length -
+		    remaining;
+		ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, toggle,
+				       DATA_STAGE);
+		if (ret)
+			return ret;	/* td wasn't created */
+
+		toggle ^= 1;
+		remaining -= len;
+		if (usb_pipecontrol(urb->pipe))
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int c67x00_add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	int ret;
+	int pid;
+
+	switch (urb->interval) {
+	default:
+	case SETUP_STAGE:
+		ret = c67x00_create_td(c67x00, urb, urb->setup_packet,
+				       8, USB_PID_SETUP, 0, SETUP_STAGE);
+		if (ret)
+			return ret;
+		urb->interval = SETUP_STAGE;
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), 1);
+		break;
+	case DATA_STAGE:
+		if (urb->transfer_buffer_length) {
+			ret = c67x00_add_data_urb(c67x00, urb);
+			if (ret)
+				return ret;
+			break;
+		}		/* else fallthrough */
+	case STATUS_STAGE:
+		pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+		ret = c67x00_create_td(c67x00, urb, NULL, 0, pid, 1,
+				       STATUS_STAGE);
+		if (ret)
+			return ret;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int c67x00_add_int_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+	if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+		urbp->ep_data->next_frame =
+		    frame_add(urbp->ep_data->next_frame, urb->interval);
+		return c67x00_add_data_urb(c67x00, urb);
+	}
+	return 0;
+}
+
+static int c67x00_add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+	if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+		char *td_buf;
+		int len, pid, ret;
+
+		BUG_ON(urbp->cnt >= urb->number_of_packets);
+
+		td_buf = urb->transfer_buffer +
+		    urb->iso_frame_desc[urbp->cnt].offset;
+		len = urb->iso_frame_desc[urbp->cnt].length;
+		pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+
+		ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, 0,
+				       urbp->cnt);
+		if (ret) {
+			printk(KERN_DEBUG "create failed: %d\n", ret);
+			urb->iso_frame_desc[urbp->cnt].actual_length = 0;
+			urb->iso_frame_desc[urbp->cnt].status = ret;
+			if (urbp->cnt + 1 == urb->number_of_packets)
+				c67x00_giveback_urb(c67x00, urb, 0);
+		}
+
+		urbp->ep_data->next_frame =
+		    frame_add(urbp->ep_data->next_frame, urb->interval);
+		urbp->cnt++;
+	}
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_fill_from_list(struct c67x00_hcd *c67x00, int type,
+				  int (*add)(struct c67x00_hcd *, struct urb *))
+{
+	struct c67x00_ep_data *ep_data;
+	struct urb *urb;
+
+	/* traverse every endpoint on the list */
+	list_for_each_entry(ep_data, &c67x00->list[type], node) {
+		if (!list_empty(&ep_data->queue)) {
+			/* and add the first urb */
+			/* isochronous transfer rely on this */
+			urb = list_entry(ep_data->queue.next,
+					 struct c67x00_urb_priv,
+					 hep_node)->urb;
+			add(c67x00, urb);
+		}
+	}
+}
+
+static void c67x00_fill_frame(struct c67x00_hcd *c67x00)
+{
+	struct c67x00_td *td, *ttd;
+
+	/* Check if we can proceed */
+	if (!list_empty(&c67x00->td_list)) {
+		dev_warn(c67x00_hcd_dev(c67x00),
+			 "TD list not empty! This should not happen!\n");
+		list_for_each_entry_safe(td, ttd, &c67x00->td_list, td_list) {
+			dbg_td(c67x00, td, "Unprocessed td");
+			c67x00_release_td(td);
+		}
+	}
+
+	/* Reinitialize variables */
+	c67x00->bandwidth_allocated = 0;
+	c67x00->periodic_bw_allocated = 0;
+
+	c67x00->next_td_addr = c67x00->td_base_addr;
+	c67x00->next_buf_addr = c67x00->buf_base_addr;
+
+	/* Fill the list */
+	c67x00_fill_from_list(c67x00, PIPE_ISOCHRONOUS, c67x00_add_iso_urb);
+	c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, c67x00_add_int_urb);
+	c67x00_fill_from_list(c67x00, PIPE_CONTROL, c67x00_add_ctrl_urb);
+	c67x00_fill_from_list(c67x00, PIPE_BULK, c67x00_add_data_urb);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * Get TD from C67X00
+ */
+static inline void
+c67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	c67x00_ll_read_mem_le16(c67x00->sie->dev,
+				td->td_addr, td, CY_TD_SIZE);
+
+	if (usb_pipein(td->pipe) && td_actual_bytes(td))
+		c67x00_ll_read_mem_le16(c67x00->sie->dev, td_ly_base_addr(td),
+					td->data, td_actual_bytes(td));
+}
+
+static int c67x00_td_to_error(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	if (td->status & TD_STATUSMASK_ERR) {
+		dbg_td(c67x00, td, "ERROR_FLAG");
+		return -EILSEQ;
+	}
+	if (td->status & TD_STATUSMASK_STALL) {
+		/* dbg_td(c67x00, td, "STALL"); */
+		return -EPIPE;
+	}
+	if (td->status & TD_STATUSMASK_TMOUT) {
+		dbg_td(c67x00, td, "TIMEOUT");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static inline int c67x00_end_of_data(struct c67x00_td *td)
+{
+	int maxps, need_empty, remaining;
+	struct urb *urb = td->urb;
+	int act_bytes;
+
+	act_bytes = td_actual_bytes(td);
+
+	if (unlikely(!act_bytes))
+		return 1;	/* This was an empty packet */
+
+	maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe));
+
+	if (unlikely(act_bytes < maxps))
+		return 1;	/* Smaller then full packet */
+
+	remaining = urb->transfer_buffer_length - urb->actual_length;
+	need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+	    usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+	if (unlikely(!remaining && !need_empty))
+		return 1;
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Remove all td's from the list which come
+ * after last_td and are meant for the same pipe.
+ * This is used when a short packet has occured */
+static inline void c67x00_clear_pipe(struct c67x00_hcd *c67x00,
+				     struct c67x00_td *last_td)
+{
+	struct c67x00_td *td, *tmp;
+	td = last_td;
+	tmp = last_td;
+	while (td->td_list.next != &c67x00->td_list) {
+		td = list_entry(td->td_list.next, struct c67x00_td, td_list);
+		if (td->pipe == last_td->pipe) {
+			c67x00_release_td(td);
+			td = tmp;
+		}
+		tmp = td;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_handle_successful_td(struct c67x00_hcd *c67x00,
+					struct c67x00_td *td)
+{
+	struct urb *urb = td->urb;
+
+	if (!urb)
+		return;
+
+	urb->actual_length += td_actual_bytes(td);
+
+	switch (usb_pipetype(td->pipe)) {
+		/* isochronous tds are handled separately */
+	case PIPE_CONTROL:
+		switch (td->privdata) {
+		case SETUP_STAGE:
+			urb->interval =
+			    urb->transfer_buffer_length ?
+			    DATA_STAGE : STATUS_STAGE;
+			/* Don't count setup_packet with normal data: */
+			urb->actual_length = 0;
+			break;
+
+		case DATA_STAGE:
+			if (c67x00_end_of_data(td)) {
+				urb->interval = STATUS_STAGE;
+				c67x00_clear_pipe(c67x00, td);
+			}
+			break;
+
+		case STATUS_STAGE:
+			urb->interval = 0;
+			c67x00_giveback_urb(c67x00, urb, 0);
+			break;
+		}
+		break;
+
+	case PIPE_INTERRUPT:
+	case PIPE_BULK:
+		if (unlikely(c67x00_end_of_data(td))) {
+			c67x00_clear_pipe(c67x00, td);
+			c67x00_giveback_urb(c67x00, urb, 0);
+		}
+		break;
+	}
+}
+
+static void c67x00_handle_isoc(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	struct urb *urb = td->urb;
+	struct c67x00_urb_priv *urbp;
+	int cnt;
+
+	if (!urb)
+		return;
+
+	urbp = urb->hcpriv;
+	cnt = td->privdata;
+
+	if (td->status & TD_ERROR_MASK)
+		urb->error_count++;
+
+	urb->iso_frame_desc[cnt].actual_length = td_actual_bytes(td);
+	urb->iso_frame_desc[cnt].status = c67x00_td_to_error(c67x00, td);
+	if (cnt + 1 == urb->number_of_packets)	/* Last packet */
+		c67x00_giveback_urb(c67x00, urb, 0);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_check_td_list - handle tds which have been processed by the c67x00
+ * pre: current_td == 0
+ */
+static inline void c67x00_check_td_list(struct c67x00_hcd *c67x00)
+{
+	struct c67x00_td *td, *tmp;
+	struct urb *urb;
+	int ack_ok;
+	int clear_endpoint;
+
+	list_for_each_entry_safe(td, tmp, &c67x00->td_list, td_list) {
+		/* get the TD */
+		c67x00_parse_td(c67x00, td);
+		urb = td->urb;	/* urb can be NULL! */
+		ack_ok = 0;
+		clear_endpoint = 1;
+
+		/* Handle isochronous transfers separately */
+		if (usb_pipeisoc(td->pipe)) {
+			clear_endpoint = 0;
+			c67x00_handle_isoc(c67x00, td);
+			goto cont;
+		}
+
+		/* When an error occurs, all td's for that pipe go into an
+		 * inactive state. This state matches successful transfers so
+		 * we must make sure not to service them. */
+		if (td->status & TD_ERROR_MASK) {
+			c67x00_giveback_urb(c67x00, urb,
+					    c67x00_td_to_error(c67x00, td));
+			goto cont;
+		}
+
+		if ((td->status & TD_STATUSMASK_NAK) || !td_sequence_ok(td) ||
+		    !td_acked(td))
+			goto cont;
+
+		/* Sequence ok and acked, don't need to fix toggle */
+		ack_ok = 1;
+
+		if (unlikely(td->status & TD_STATUSMASK_OVF)) {
+			if (td_residue(td) & TD_RESIDUE_OVERFLOW) {
+				/* Overflow */
+				c67x00_giveback_urb(c67x00, urb, -EOVERFLOW);
+				goto cont;
+			}
+		}
+
+		clear_endpoint = 0;
+		c67x00_handle_successful_td(c67x00, td);
+
+cont:
+		if (clear_endpoint)
+			c67x00_clear_pipe(c67x00, td);
+		if (ack_ok)
+			usb_settoggle(td_udev(td), usb_pipeendpoint(td->pipe),
+				      usb_pipeout(td->pipe),
+				      !(td->ctrl_reg & SEQ_SEL));
+		/* next in list could have been removed, due to clear_pipe! */
+		tmp = list_entry(td->td_list.next, typeof(*td), td_list);
+		c67x00_release_td(td);
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int c67x00_all_tds_processed(struct c67x00_hcd *c67x00)
+{
+	/* If all tds are processed, we can check the previous frame (if
+	 * there was any) and start our next frame.
+	 */
+	return !c67x00_ll_husb_get_current_td(c67x00->sie);
+}
+
+/**
+ * Send td to C67X00
+ */
+static void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	int len = td_length(td);
+
+	if (len && ((td->pid_ep & TD_PIDEPMASK_PID) != TD_PID_IN))
+		c67x00_ll_write_mem_le16(c67x00->sie->dev, td_ly_base_addr(td),
+					 td->data, len);
+
+	c67x00_ll_write_mem_le16(c67x00->sie->dev,
+				 td->td_addr, td, CY_TD_SIZE);
+}
+
+static void c67x00_send_frame(struct c67x00_hcd *c67x00)
+{
+	struct c67x00_td *td;
+
+	if (list_empty(&c67x00->td_list))
+		dev_warn(c67x00_hcd_dev(c67x00),
+			 "%s: td list should not be empty here!\n",
+			 __func__);
+
+	list_for_each_entry(td, &c67x00->td_list, td_list) {
+		if (td->td_list.next == &c67x00->td_list)
+			td->next_td_addr = 0;	/* Last td in list */
+
+		c67x00_send_td(c67x00, td);
+	}
+
+	c67x00_ll_husb_set_current_td(c67x00->sie, c67x00->td_base_addr);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_do_work - Schedulers state machine
+ */
+static void c67x00_do_work(struct c67x00_hcd *c67x00)
+{
+	spin_lock(&c67x00->lock);
+	/* Make sure all tds are processed */
+	if (!c67x00_all_tds_processed(c67x00))
+		goto out;
+
+	c67x00_check_td_list(c67x00);
+
+	/* no td's are being processed (current == 0)
+	 * and all have been "checked" */
+	complete(&c67x00->endpoint_disable);
+
+	if (!list_empty(&c67x00->td_list))
+		goto out;
+
+	c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+	if (c67x00->current_frame == c67x00->last_frame)
+		goto out;	/* Don't send tds in same frame */
+	c67x00->last_frame = c67x00->current_frame;
+
+	/* If no urbs are scheduled, our work is done */
+	if (!c67x00->urb_count) {
+		c67x00_ll_hpi_disable_sofeop(c67x00->sie);
+		goto out;
+	}
+
+	c67x00_fill_frame(c67x00);
+	if (!list_empty(&c67x00->td_list))
+		/* TD's have been added to the frame */
+		c67x00_send_frame(c67x00);
+
+ out:
+	spin_unlock(&c67x00->lock);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_sched_tasklet(unsigned long __c67x00)
+{
+	struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+	c67x00_do_work(c67x00);
+}
+
+void c67x00_sched_kick(struct c67x00_hcd *c67x00)
+{
+	tasklet_hi_schedule(&c67x00->tasklet);
+}
+
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00)
+{
+	tasklet_init(&c67x00->tasklet, c67x00_sched_tasklet,
+		     (unsigned long)c67x00);
+	return 0;
+}
+
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
+{
+	tasklet_kill(&c67x00->tasklet);
+}
Index: linux-2.6/drivers/usb/c67x00/Makefile
===================================================================
--- /dev/null
+++ linux-2.6/drivers/usb/c67x00/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for Cypress C67X00 USB Controller
+#
+
+ccflags-$(CONFIG_USB_DEBUG)		+= -DDEBUG
+
+obj-$(CONFIG_USB_C67X00_HCD)		+= c67x00.o
+
+c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o
Index: linux-2.6/drivers/usb/Makefile
===================================================================
--- linux-2.6.orig/drivers/usb/Makefile
+++ linux-2.6/drivers/usb/Makefile
@@ -17,6 +17,8 @@
 obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
 
+obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/
+
 obj-$(CONFIG_USB_ACM)		+= class/
 obj-$(CONFIG_USB_PRINTER)	+= class/
 
Index: linux-2.6/drivers/usb/c67x00/c67x00-drv.c
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00-drv.c
+++ linux-2.6/drivers/usb/c67x00/c67x00-drv.c
@@ -41,6 +41,7 @@
 #include <linux/usb/c67x00.h>
 
 #include "c67x00.h"
+#include "c67x00-hcd.h"
 
 static void c67x00_probe_sie(struct c67x00_sie *sie,
 			     struct c67x00_device *dev, int sie_num)
@@ -51,6 +52,10 @@
 	sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
 
 	switch (sie->mode) {
+	case C67X00_SIE_HOST:
+		c67x00_hcd_probe(sie);
+		break;
+
 	case C67X00_SIE_UNUSED:
 		dev_info(sie_dev(sie),
 			 "Not using SIE %d as requested\n", sie->sie_num);
@@ -66,6 +71,14 @@
 
 static void c67x00_remove_sie(struct c67x00_sie *sie)
 {
+	switch (sie->mode) {
+	case C67X00_SIE_HOST:
+		c67x00_hcd_remove(sie);
+		break;
+
+	default:
+		break;
+	}
 }
 
 static irqreturn_t c67x00_irq(int irq, void *__dev)
Index: linux-2.6/drivers/usb/c67x00/c67x00.h
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00.h
+++ linux-2.6/drivers/usb/c67x00/c67x00.h
@@ -274,6 +274,15 @@
 void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
 			     void *data, int len);
 
+/* Host specific functions */
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value);
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port);
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr);
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie);
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie *sie);
+void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie);
+void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port);
+
 /* Called by c67x00_irq to handle lcp interrupts */
 void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status);
 
Index: linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
===================================================================
--- linux-2.6.orig/drivers/usb/c67x00/c67x00-ll-hpi.c
+++ linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
@@ -297,6 +297,81 @@
 }
 
 /* -------------------------------------------------------------------------- */
+/* Host specific functions */
+
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value)
+{
+	mutex_lock(&dev->hpi.lcp.mutex);
+	hpi_write_word(dev, HUSB_pEOT, value);
+	mutex_unlock(&dev->hpi.lcp.mutex);
+}
+
+static inline void c67x00_ll_husb_sie_init(struct c67x00_sie *sie)
+{
+	struct c67x00_device *dev = sie->dev;
+	struct c67x00_lcp_int_data data;
+	int rc;
+
+	rc = c67x00_comm_exec_int(dev, HUSB_SIE_INIT_INT(sie->sie_num), &data);
+	BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port)
+{
+	struct c67x00_device *dev = sie->dev;
+	struct c67x00_lcp_int_data data;
+	int rc;
+
+	data.regs[0] = 50;	/* Reset USB port for 50ms */
+	data.regs[1] = port | (sie->sie_num << 1);
+	rc = c67x00_comm_exec_int(dev, HUSB_RESET_INT, &data);
+	BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr)
+{
+	hpi_write_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num), addr);
+}
+
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num));
+}
+
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, HOST_FRAME_REG(sie->sie_num));
+}
+
+void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie)
+{
+	/* Set port into host mode */
+	hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), HOST_MODE);
+	c67x00_ll_husb_sie_init(sie);
+	/* Clear interrupts */
+	c67x00_ll_usb_clear_status(sie, HOST_STAT_MASK);
+	/* Check */
+	if (!(hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num)) & HOST_MODE))
+		dev_warn(sie_dev(sie),
+			 "SIE %d not set to host mode\n", sie->sie_num);
+}
+
+void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port)
+{
+	/* Clear connect change */
+	c67x00_ll_usb_clear_status(sie, PORT_CONNECT_CHANGE(port));
+
+	/* Enable interrupts */
+	hpi_set_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+		     SOFEOP_TO_CPU_EN(sie->sie_num));
+	hpi_set_bits(sie->dev, HOST_IRQ_EN_REG(sie->sie_num),
+		     SOF_EOP_IRQ_EN | DONE_IRQ_EN);
+
+	/* Enable pull down transistors */
+	hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), PORT_RES_EN(port));
+}
+
+/* -------------------------------------------------------------------------- */
 
 void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status)
 {
Index: linux-2.6/drivers/usb/host/Kconfig
===================================================================
--- linux-2.6.orig/drivers/usb/host/Kconfig
+++ linux-2.6/drivers/usb/host/Kconfig
@@ -4,6 +4,19 @@
 comment "USB Host Controller Drivers"
 	depends on USB
 
+config USB_C67X00_HCD
+	tristate "Cypress C67x00 HCD support"
+	depends on USB
+	help
+	  The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
+	  host/peripheral/OTG USB controllers.
+
+	  Enable this option to support this chip in host controller mode.
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called c67x00.
+
 config USB_EHCI_HCD
 	tristate "EHCI HCD (USB 2.0) support"
 	depends on USB && USB_ARCH_HAS_EHCI

-- 
Bye, Peter Korsgaard

^ permalink raw reply

* Re: get_cycles()
From: Kevin Diggs @ 2008-04-27 10:35 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20080426.202135.145326755.davem@davemloft.net>

David Miller wrote:
> From: Kevin Diggs <kevdig@hypersurf.com>
> Date: Sat, 26 Apr 2008 19:39:07 -0700
> 
> 
>>This looks pretty cool ... but I don't want to create a dependency on
>>hrtimer.
> 
> 
> It doesn't create such a dependency.
> 
> We use it unconditionally in the generic networking.
> 
> Please don't use platform specific interfaces if you don't have to.
> You're be insulated from so many things.
> 

I'm working on a cpufreq driver for the 750GX so I don't think I have to 
worry about being to platform specific.

Would a compile time configuration be a good idea (hrtimer or 
get_cycles() assisted timing)?

In the 2.4 code I just used a timer 2 ticks in the future to be certain 
I did not go under the 100 us PLL lock delay. I was trying to see if I 
could cut the latency down.

What about using OF? Isn't there a timebase property for the cpus?

Thoughts?

kevin

^ permalink raw reply

* Re: [PATCH1/7] i2c: Add support for device alias names
From: Jochen Friedrich @ 2008-04-27 11:07 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Kernel, Linux, linuxppc-dev list, Scott Wood, Linux I2C
In-Reply-To: <20080426223815.16e38a85@hyperion.delvare>

Hi Jean,

>> -	if ((err = f75375_probe(client)) < 0)
>> +	err = f75375_probe(client, NULL);
>> +	if (err < 0)
>>  		goto exit_detach;
>>  
>>  	return 0;
> 
> These coding style cleanups don't belong to this patch (if they belong
> anywhere - I don't much see the point)

I just wanted to make sure, scripts/checkpatch.pl doesn't complain.
But you're right. The coding style cleanup should have been another patch.

Thanks a lot for your efforts of getting the alias stuff in!

Jochen

^ permalink raw reply

* Re: [PATCH] rtc-pcf8563: Add device ids table
From: Jean Delvare @ 2008-04-27 12:18 UTC (permalink / raw)
  To: Jochen Friedrich
  Cc: Alessandro Zummo, rtc-linux, linuxppc-dev list, i2c,
	Andrew Morton
In-Reply-To: <480F390A.90707@scram.de>

On Wed, 23 Apr 2008 15:26:34 +0200, Jochen Friedrich wrote:
> Hi Jean,
> 
> > Jochen, I'm a bit confused by the dependencies that exist - or not -
> > between these 7 patches you sent at once. I thought they had to be
> > applied in sequence but it seems not? And some of them should
> > apparently go through me i2c tree but others (e.g. [7/7]) not?
> 
> [1/7] and [2/7] are forward ports of patches from you. I'm currently
> just running a make allmodconfig compile to check if it really caught
> all affected i2c drivers.
> 
> These are the patches we are talking about.
> 
> [3/7] is the OF helper stuff which translates between OF names and
> i2c types. It does NOT translate OF names to module names, but relies
> on [1/7] to do so. Without [1/7], [3/7] still applies, but module
> auto loading won't work.
> 
> This has been applied to 2.6.26.
> 
> > I would appreciate if you could summarize quickly which patches depend
> > on others in which way. If we can make smaller subsets of patches, that
> > will be easier for me to review and push upstream on my limited time.
> 
> [4/7] is the patch from Jon Smirl to convert i2c-mpc to OF. This relies
> on [3/7] instead of Jons initial OF-autoloading patches you didn't like.
> As there were no comments at all, I would however postpone this to 2.6.27.
> 
> [5/7] is the cleanup patch (originating from you) which completely removes
> old driver matching scheme. This should probably wait until 2.6.27, as well.
> This relies on [1/7] and [2/7].
> 
> [6/7] and [7/7] are a new driver and depend on [3/7]. This can wait for 2.6.27,
> as well (i would rather like to see those in -mm to get more testing, though).

Thanks for the summary. Right now I'm working on [1/7] and [2/7] to get
something ready to go upstream in the next few days. Probably only a
part of [2/7] can go there quickly, non-trivial cases (e.g. ivtv) will
have to wait until rc2 or later. I also agree with you that [5/7]
should be delayed for now, we need to make sure that no driver is left
using the old device/driver matching scheme, and that can't possibly be
done before rc2 at best.

I will post the 2 updated patches for review and testing as soon as I
am done.

-- 
Jean Delvare

^ permalink raw reply

* Re: [PATCH 2/3] i2c: Convert all new-style drivers to use module aliasing
From: Jean Delvare @ 2008-04-27 16:56 UTC (permalink / raw)
  To: David Brownell; +Cc: linuxppc-dev, Linux I2C
In-Reply-To: <200801271040.20248.david-b@pacbell.net>

On Sun, 27 Jan 2008 10:40:19 -0800, David Brownell wrote:
> General comment:  if you're going to index arrays by enum
> values, it's best to initialize them that way too.  Else
> you're expecting a particular optional policy for how the
> enums get grown...
> 
> - Dave
> 
> 
> On Monday 21 January 2008, Jean Delvare wrote:
> > --- linux-2.6.24-rc8.orig/drivers/rtc/rtc-ds1307.c	2008-01-20 17:26:58.000000000 +0100
> > +++ linux-2.6.24-rc8/drivers/rtc/rtc-ds1307.c	2008-01-20 19:03:48.000000000 +0100
> > @@ -102,42 +102,36 @@ struct chip_desc {
> >  	char			name[9];
> >  	unsigned		nvram56:1;
> >  	unsigned		alarm:1;
> > -	enum ds_type		type;
> >  };
> >  
> >  static const struct chip_desc chips[] = { {
> >  	.name		= "ds1307",
> > -	.type		= ds_1307,
> >  	.nvram56	= 1,
> >  }, {
> 
> So tables like this would become
> 
> 	[ds1307] = { ... },
> 	[ds1337] = { ... },
> 
> >  	.name		= "ds1337",
> > -	.type		= ds_1337,
> >  	.alarm		= 1,
> >  }, {
> >  	.name		= "ds1338",
> > -	.type		= ds_1338,
> >  	.nvram56	= 1,
> >  }, {
> >  	.name		= "ds1339",
> > -	.type		= ds_1339,
> >  	.alarm		= 1,
> >  }, {
> >  	.name		= "ds1340",
> > -	.type		= ds_1340,
> >  }, {
> >  	.name		= "m41t00",
> > -	.type		= m41t00,
> >  }, };
> >  
> > -static inline const struct chip_desc *find_chip(const char *s)
> > -{
> > -	unsigned i;
> > -
> > -	for (i = 0; i < ARRAY_SIZE(chips); i++)
> > -		if (strnicmp(s, chips[i].name, sizeof chips[i].name) == 0)
> > -			return &chips[i];
> > -	return NULL;
> > -}
> > +static const struct i2c_device_id ds1307_id[] = {
> > +	{ "ds1307", ds_1307 },
> > +	{ "ds1337", ds_1337 },
> > +	{ "ds1338", ds_1338 },
> > +	{ "ds1339", ds_1339 },
> > +	{ "ds1340", ds_1340 },
> > +	{ "m41t00", m41t00 },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(i2c, ds1307_id);
> >  
> >  static int ds1307_get_time(struct device *dev, struct rtc_time *t)
> >  {
> > @@ -335,12 +329,7 @@ static int __devinit ds1307_probe(struct
> >  	const struct chip_desc	*chip;
> >  	struct i2c_adapter	*adapter = to_i2c_adapter(client->dev.parent);
> >  
> > -	chip = find_chip(client->name);
> > -	if (!chip) {
> > -		dev_err(&client->dev, "unknown chip type '%s'\n",
> > -				client->name);
> > -		return -ENODEV;
> > -	}
> > +	chip = &chips[id->driver_data];
> 
> ... and that would *ensure* such lines always work right,
> no matter how the enum values grow.
> 
> >  
> >  	if (!i2c_check_functionality(adapter,
> >  			I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))

Done, thanks for the suggestion and sorry for the delay.

-- 
Jean Delvare

^ permalink raw reply

* 2.6.25: pmac_newworld undefined
From: Christian Kujau @ 2008-04-27 18:03 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: LKML

Hi,

the build failure reported[0] by Kamalesh back in 01/2008 is still 
present in today's 2.6.25-git with CONFIG_NVRAM=m (instead of =y):

   Building modules, stage 2.
   MODPOST 72 modules
ERROR: "pmac_newworld" [arch/powerpc/platforms/powermac/nvram.ko] undefined!
ERROR: "__alloc_bootmem" [arch/powerpc/platforms/powermac/nvram.ko] undefined!
make[1]: *** [__modpost] Error 1

Full .config and make.log: http://nerdbynature.de/bits/2.6.25/nvram/

I could not find an #ifdef or sth. around pmac_newworld....

Thanks,
Christian.

[0] http://lkml.org/lkml/2008/1/17/55
-- 
BOFH excuse #187:

Reformatting Page. Wait...

^ permalink raw reply


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