* ELDK on 64-bit host
From: Kalle Pokki @ 2008-02-20 15:03 UTC (permalink / raw)
To: linuxppc-embedded, wd
Hi,
I tried to copy the ELDK 4.0 installation I had to a new Core 2 Quad
machine running 64-bit Ubuntu 7.10, but the new machine refuses to
execute the cross compiler. Has anyone succeeded in running ELDK in
this kind of environment?
I also tried compiling the ELDK from source RPMs as described in the
documentation, but at least some of the RPMs the scripts try to
download are no longer available on the net, and the build environment
described doesn't seem to quite match the one on the source rpm ISO
image.
^ permalink raw reply
* [BUG] 2.6.25-rc2-git4 - Regression Kernel oops while running kernbench and tbench on powerpc
From: Kamalesh Babulal @ 2008-02-20 15:01 UTC (permalink / raw)
To: kernel list, linux-next, linuxppc-dev, paulus, Andrew Morton,
Andy Whitcroft, Balbir Singh
Hi,
The Kernel oopses is seen while running the kernbench followed by tbench with 2.6.25-rc2-git4
kernel on powerpc, this oops was reported for the 2.6.24-rc8-mm1 kernel (http://lkml.org/lkml/2008/1/18/71)
and is visible with almost all of the main line ,rc(s) and their git(s) release from then.
This oops is visible in the linux-next-20080220 kernel also.The machine is power4+ box with four cpus and
has 30 GB RAM.
oops while running kernbench
-----------------------------
Oops: Kernel access of bad area, sig: 11 [#1]
SMP NR_CPUS=32 NUMA pSeries
Modules linked in:
NIP: 0000000000004570 LR: 000000000ff0135c CTR: 000000000ff012f0
REGS: c000000771a678c0 TRAP: 0300 Not tainted (2.6.25-rc2-autotest-next-20080220)
MSR: 8000000000001000 <ME> CR: 28000282 XER: 00000000
DAR: c000000771a67ce0, DSISR: 000000000a000000
TASK = c00000077b9d2c00[22046] 'cc1' THREAD: c000000771a64000 CPU: 3
GPR00: 0000000000004000 c000000771a67b40 0000000000000052 000000000000d032
GPR04: 0000000000000052 0000000000000400 00000000106823e0 000000000000000e
GPR08: 0000000000000000 0000000044000288 c000000771a67e30 0000998be2321500
GPR12: 8000000000001030 c0000000005d7e00 0000000010030000 0000000010030000
GPR16: 00000000105b0000 00000000105b0000 0000000010440000 00000000105b0000
GPR20: 00000000105b0000 00000000105b0000 00000000105b0000 00000000105b0000
GPR24: 00000000105b41e8 0000000000000020 00000000105b0000 0000000000000400
GPR28: 000000000ffec408 00000000f7ff8000 000000000ffebff4 0000000000000400
NIP [0000000000004570] 0x4570
LR [000000000ff0135c] 0xff0135c
Call Trace:
[c000000771a67b40] [c00000000062d558] 0xc00000000062d558 (unreliable)
[c000000771a67e08] [00000000f7ff8000] 0xf7ff8000
Instruction dump:
48000000 XXXXXXXX XXXXXXXX XXXXXXXX 41820008 XXXXXXXX XXXXXXXX XXXXXXXX
48000010 XXXXXXXX XXXXXXXX XXXXXXXX f92101a0 XXXXXXXX XXXXXXXX XXXXXXXX
---[ end trace 26a7439b76b3cbab ]---
oops while running tbench
--------------------------
Unable to handle kernel paging request for data at address 0xc00000077e2e3ce0
Faulting instruction address: 0x00004570
Oops: Kernel access of bad area, sig: 11 [#2]
SMP NR_CPUS=32 NUMA pSeries
Modules linked in:
NIP: 0000000000004570 LR: c00000000000872c CTR: 0000000000000000
REGS: c00000077e2e38c0 TRAP: 0300 Tainted: G D (2.6.25-rc2-autotest-next-20080220)
MSR: 8000000000001000 <ME> CR: 28022822 XER: 20000000
DAR: c00000077e2e3ce0, DSISR: 000000000a000000
TASK = c00000077cb55800[3900] 'zmd' THREAD: c00000077e2e0000 CPU: 3
GPR00: 0000000000000000 c00000077e2e3b40 c0000000006a29b8 000000000000006e
GPR04: 000000000fe7bac8 0000000054022888 0000000000004000 000000000fe7bae4
GPR08: 000000000000d032 0000000044022824 c00000077e2e3e30 0000998be2321500
GPR12: 8000000000001030 c0000000005d7e00 0000000000000001 0000000000000000
GPR16: 0000000000000000 00000000f7c16708 00000000f7fa0800 00000000100e0040
GPR20: 00000000f7ffe018 0000000000000001 00000000f5397550 00000000f5397548
GPR24: 0000000000000000 0000000000004a19 00000000f7d66474 00000000f53975a8
GPR28: 0000000000000000 0000000000009433 000000000fe94ff4 00000000f7d66490
NIP [0000000000004570] 0x4570
LR [c00000000000872c] syscall_exit+0x0/0x40
Call Trace:
[c00000077e2e3b40] [00000496000729b8] 0x496000729b8 (unreliable)
Instruction dump:
48000000 XXXXXXXX XXXXXXXX XXXXXXXX 41820008 XXXXXXXX XXXXXXXX XXXXXXXX
48000010 XXXXXXXX XXXXXXXX XXXXXXXX f92101a0 XXXXXXXX XXXXXXXX XXXXXXXX
The machine
--
Thanks & Regards,
Kamalesh Babulal,
Linux Technology Center,
IBM, ISTL.
^ permalink raw reply
* initramfs problem - /init file
From: raul.moreno @ 2008-02-20 14:44 UTC (permalink / raw)
To: linuxppc-embedded
Hello everybody,
I created a tiny root filesystem, just with Busybox, that I want to use=
as
rootfs through initramfs (and keep it as the final root filesystem).
First of all, I probed this root filesystem using NFS to be sure it wor=
ked
and in fact, it did. Then I created an init file which is a link to
/sbin/init and I inserted the created cpio file with this filesystem in=
CONFIG_INITRAMFS_SOURCE. When I boot the system up, I get the following=
error:
Failed to execute /init
Kernel panic - not syncing: No init found. Try passing init=3D option =
to
kernel.
If I also tried with an init script instead to a link to /sbin/init, bu=
t
this error happens again.
I think the kernel finds the initramfs right, because if I don't add th=
e
init file, I get a different error:
VFS: Cannot open root device "<NULL>" or unknown-block(0,0)
Please append a correct "root=3D" boot option;
What is the problem with "init"?
Could anyone help me? Thanks in advance,
Ra=FAl Moreno
***********Internet Email Confidentiality Footer*************
This email and any files transmitted with it are confidential and inten=
ded
solely for the use of the organization or individual to whom they are
addressed. It is expressly forbidden to retransmit or copy email and/o=
r
this attached files without our permission . If you are not the
addressee indicated in this message (or responsible for delivery of the=
message to such person), you may not copy or deliver this message
to anyone. In such case, you should destroy this message and kindly
notify the sender by reply email. Please advise immediately if you or
your employer does not consent to Internet email for messages of this
kind. Opinions, conclusions and other information in this message that=
do not relate to the official business of my firm shall be understood a=
s
neither given nor endorsed by it.=
^ permalink raw reply
* Re: mpc8548amc vs mpc8548cds
From: Jon Loeliger @ 2008-02-20 14:41 UTC (permalink / raw)
To: maxime louvel; +Cc: linuxppc-embedded
In-Reply-To: <dda529d0802200601t2db82c67o808c8407215396c@mail.gmail.com>
maxime louvel wrote:
> Hi,
>
> first of all I am sorry if this is not the good place to ask that but I
> don't know where else ...
> My question is:
>
> Does anyone knows the difference between the mpc8548amc and mpc8548cds
> platforms ?
Sorry, no knowledge of the AMC board here.
> I am working on an amc one but the 2.6.24 kernel is supporting only the
> cds one.
> Do you know if I can use the dts file form the mpc8548cds for the
> mpc8548amc ?
They will be similar, but I doubt you can use it directly.
jdl
^ permalink raw reply
* Re: Frustrated question with insmod
From: Wolfgang Grandegger @ 2008-02-20 14:04 UTC (permalink / raw)
To: Bruce_Leonard; +Cc: linuxppc-embedded
In-Reply-To: <OF5B1D6538.27F4BDEE-ON882573F4.0000830F-882573F4.0001FF46@selinc.com>
Bruce_Leonard@selinc.com wrote:
>> Bruce_Leonard@selinc.com wrote:
>>> Sorry if this is the wrong place to post this question. I'm
> developing a
>>> NAND flash driver and I need to do some detailed dubugging using GDB
> with
>>> a BDI2K. According to the Denx web site, to find out the address that
> the
>>> module is loading at you load it using the -m parameter to insmod
> (i.e.,
>>> "insmod -m mymodule"). However, every version of insmod I've tried
>>> doesn't recognize ANY options much less -m. Can anyone please point
> me in
>>> the right direction, or give me another way of knowing what the load
>>> address of my module is?
>> # cat /sys/module/<name>/sections/.text
>>
>> Do not forget to enable CONFIG_KALLSYMS.
>>
>
> Well, okay I guess the address I'm getting is the right one because both
> the above cat and 'cat /proc/modules' gives me the same thing, 0xe1188000.
> I've got CONFIG_KALLSYMS and CONFIG_KALLSYMS_ALL set, though
> CONFIG_KALLSYMS_EXTRA_PASS is not set, don't know if that makes a
> difference.
>
> So it would seem that there's something wrong with my BDI setup that isn't
> allowing address translation in the kernel's dynamically allocated memory
> area. I've got PTBASE set to 0xf0 in the BDI config file so it should be
> finding the virtual address of the page tables just fine. I've also got
> CONFIG_BDI_SWITCH set in .config and I know that works with the BDI,
> because I can set breakpoints at places in the kernel code that are called
> by my module (like nand_scan_ident() ) and everything works just fine.
> It's just when I try to access memory in the dynamic area where my module
> is located that the BDI can't do an address translation. At least I
> assume it's the BDI, because I can load the module and use it with out
> GDB/BDI, so the processor and kernel must be able to handle the addresses
> okay. Can anyone think of where I should go dig? I've had this working
> exactly once in the past, but I don't know what I've changed to cause it
> to stop working.
Try to specify the addresses of the other sections listed by "cat
/sys/module/my-mod/sections/:
add-symbol-file my-mod.o 0xcf030060\
-s .rodata 0xcf030354\
-s .data 0xcf030488\
-s .sdata 0xcf030488\
-s .bss 0xcf030519\
-s .sbss 0xcf03051c
Wolfgang.
^ permalink raw reply
* mpc8548amc vs mpc8548cds
From: maxime louvel @ 2008-02-20 14:01 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 482 bytes --]
Hi,
first of all I am sorry if this is not the good place to ask that but I
don't know where else ...
My question is:
Does anyone knows the difference between the mpc8548amc and mpc8548cds
platforms ?
I am working on an amc one but the 2.6.24 kernel is supporting only the cds
one.
Do you know if I can use the dts file form the mpc8548cds for the mpc8548amc
?
thanks,
Maxime
--
Maxime Louvel
0044 7964 5555 80
43 Allen road
Whitemore reans
WV60AW Wolverhampton
United Kingdom
[-- Attachment #2: Type: text/html, Size: 555 bytes --]
^ permalink raw reply
* Re: 2.6.24 for mpc8458amc
From: Kumar Gala @ 2008-02-20 13:53 UTC (permalink / raw)
To: maxime louvel
Cc: scottwood,
linuxppc-embedded-bounces+raul.moreno=telvent.abengoa.com,
linuxppc-embedded
In-Reply-To: <dda529d0802200500k7cadc8afm4f9e8f04358aea0b@mail.gmail.com>
On Feb 20, 2008, at 7:00 AM, maxime louvel wrote:
> Hi,
>
> yes It has something like 16550 UART.
> the compiler version is gcc-3.4.3 with some specific stuff for the
> platform.
> I also have a gcc-4.1.2 vanilla which has been compiled with the
> previous one. The 4.1.2 works if you tell it to emulate the floating
> point instructions.
>
> thanks Scott,
> without the treeboot-walnut it compiled
>
> I have still my first problem though:
>
> AMC=> bootm 0x1000000
> ## Booting image at 01000000 ...
> Image Name: Linux-2.6.24
> Image Type: PowerPC Linux Kernel Image (gzip compressed)
> Data Size: 1802038 Bytes = 1.7 MB
> Load Address: 00000000
> Entry Point: 00000000
> Verifying Checksum ... OK
> Uncompressing Kernel Image ... OK
>
> and nothing else...
>
> Any idea ?
>
> This may become from the fact that the uboot installed on the cards
> is old and it apparently doesn't support the newer version of the
> kernel image... (I have got that from an another mailing list)
Yes, you'll either need to look at updating u-boot and including
support for the device tree in it or look at the cuImage wrappers in
arch/powerpc/boot as a mechanism to boot a kernel w/an old fw.
- k
^ permalink raw reply
* Re: [PATCH] [POWERPC] 4xx: Fix Haleakala PCIe compatibility problem in dts
From: Stefan Roese @ 2008-02-20 13:16 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20080220070119.27dc250d@zod.rchland.ibm.com>
On Wednesday 20 February 2008, Josh Boyer wrote:
> > Since the 4xx PCIe driver checks for 405ex compatibility, the
> > PCIe interface was not detected as it is currently defined as
> > "405exr" compatible. This patch changes it to "405ex".
> >
> > The 405EX and 405EXr are identical exept that the 2nd PCIe and the
> > 2nd EMAC interfaces are missing.
>
> Does ppc405ex_pciex_core_init need to grow some logic to detect 405ex
> from 405exr and return the correct number of ports then? It
> unconditionally returns 2 today.
Yes, this could be done. Right now it works because the 405EXr board
(Haleakala) only instantiates one PCIe device in the dts.
A simple solution would be to just do a PVR check. Would this be acceptable?
Best regards,
Stefan
^ permalink raw reply
* Re: [PATCH] [POWERPC] 4xx: Fix Haleakala PCIe compatibility problem in dts
From: Josh Boyer @ 2008-02-20 13:01 UTC (permalink / raw)
To: Stefan Roese; +Cc: linuxppc-dev
In-Reply-To: <1203504358-21331-1-git-send-email-sr@denx.de>
On Wed, 20 Feb 2008 11:45:58 +0100
Stefan Roese <sr@denx.de> wrote:
> Since the 4xx PCIe driver checks for 405ex compatibility, the
> PCIe interface was not detected as it is currently defined as
> "405exr" compatible. This patch changes it to "405ex".
>
> The 405EX and 405EXr are identical exept that the 2nd PCIe and the
> 2nd EMAC interfaces are missing.
Does ppc405ex_pciex_core_init need to grow some logic to detect 405ex
from 405exr and return the correct number of ports then? It
unconditionally returns 2 today.
josh
^ permalink raw reply
* Re: 2.6.24 for mpc8458amc
From: maxime louvel @ 2008-02-20 13:00 UTC (permalink / raw)
To: Kumar Gala, scottwood
Cc: linuxppc-embedded-bounces+raul.moreno=telvent.abengoa.com,
linuxppc-embedded
In-Reply-To: <7CDED5EA-D72B-4D9F-A547-AC3B6A59F174@kernel.crashing.org>
[-- Attachment #1: Type: text/plain, Size: 1567 bytes --]
Hi,
yes It has something like 16550 UART.
the compiler version is gcc-3.4.3 with some specific stuff for the platform.
I also have a gcc-4.1.2 vanilla which has been compiled with the previous
one. The 4.1.2 works if you tell it to emulate the floating point
instructions.
thanks Scott,
without the treeboot-walnut it compiled
I have still my first problem though:
AMC=> bootm 0x1000000
## Booting image at 01000000 ...
Image Name: Linux-2.6.24
Image Type: PowerPC Linux Kernel Image (gzip compressed)
Data Size: 1802038 Bytes = 1.7 MB
Load Address: 00000000
Entry Point: 00000000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
and nothing else...
Any idea ?
This may become from the fact that the uboot installed on the cards is old
and it apparently doesn't support the newer version of the kernel image...
(I have got that from an another mailing list)
cheers,
Maxime
On Feb 20, 2008 5:19 AM, Kumar Gala <galak@kernel.crashing.org> wrote:
>
> On Feb 19, 2008, at 6:19 AM, raul.moreno@telvent.abengoa.com wrote:
>
> >
> > Hello Maxime,
> >
> > if your board is still running, although you can't see the messages
> > that
> > means you don't have any console. Try to set one (I think you can
> > use a SCM
> > of the CPM) in the kernel configuration (characters devices) or in
> > the
> > command line (console=).
>
> If you really have a mpc8548 than there is no CPM. It has 16550 style
> UARTs on it.
>
> - k
>
--
Maxime Louvel
0044 7964 5555 80
43 Allen road
Whitemore reans
WV60AW Wolverhampton
United Kingdom
[-- Attachment #2: Type: text/html, Size: 2193 bytes --]
^ permalink raw reply
* Re: [Linux-fbdev-devel] [PATCH 1/2] fb: add support for foreign endianness
From: Anton Vorontsov @ 2008-02-20 12:18 UTC (permalink / raw)
To: Paul Mackerras
Cc: linux-fbdev-devel, adaplas, Krzysztof Helt, linux-kernel,
linuxppc-dev, Geert Uytterhoeven, Andrew Morton
In-Reply-To: <18363.31427.989835.105966@cargo.ozlabs.ibm.com>
On Wed, Feb 20, 2008 at 11:56:35AM +1100, Paul Mackerras wrote:
> Andrew Morton writes:
>
> > Bizarrely, the original author of the patch (Anton) has fallen off the cc.
> > Could whoever did that please thwap himself?
> >
> > Anyway, my head is now officially spinning. Did anyone actually have a
> > reason why we shouldn't proceed with Anton's patch?
>
> I was wondering if it would be sufficient to provide alternative
> versions of fb_readl, fb_writel etc. that do byte-swapping.
This is of course viable alternative. And I was considering this, but
later I abandoned the idea: that way we'll end up doing math in the
native endianness and then converting it to the foreign. This feels
ugly in contrast when we can do the right math in the first place, per
framebuffer.
> That
> would mean that all framebuffers would have to have the same
> endianness,
Yup, another downside of changing the code to fix some narrow
problem. Plus, this means things will break if/when we'll attach
PCI video card into the MPC8360E-RDK.
Thanks,
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: Sample driver
From: Josh Boyer @ 2008-02-20 12:06 UTC (permalink / raw)
To: Silwer star; +Cc: linuxppc-embedded
In-Reply-To: <15582059.post@talk.nabble.com>
On Tue, 19 Feb 2008 20:13:24 -0800 (PST)
Silwer star <fariyaf@gmail.com> wrote:
>
> Does anyone have sample drivers in Linux for the EBC interface for PPC405Ex?
> I would be thankful if someone could put in a sample code here.
Linux typically doesn't touch the EBC itself. That is normally set up
by firmware such as U-Boot and Linux uses it as-is.
josh
^ permalink raw reply
* Question about PPC_NATIVE/hash_native_64.c
From: Gerhard Pircher @ 2008-02-20 11:57 UTC (permalink / raw)
To: linuxppc-dev
Hi,
I'm a little bit confused about the hash_native_64.c file, which is compiled
in the kernel, if PPC_NATIVE is defined. PPC_NATIVE seems to be defined also
for 32bit platforms (CHRP, PREP, etc.), but the name of the hash_native_64.c
file and the Makefile suggest that it is for 64bit platforms only.
What is hash_native_64.c actually good for on 32bit platforms and which cpus
can make use of it?
Thanks!
regards,
Gerhard
--
Psssst! Schon vom neuen GMX MultiMessenger gehört?
Der kann`s mit allen: http://www.gmx.net/de/go/multimessenger
^ permalink raw reply
* [PATCH] [POWERPC] 4xx: Fix Haleakala PCIe compatibility problem in dts
From: Stefan Roese @ 2008-02-20 10:45 UTC (permalink / raw)
To: linuxppc-dev
Since the 4xx PCIe driver checks for 405ex compatibility, the
PCIe interface was not detected as it is currently defined as
"405exr" compatible. This patch changes it to "405ex".
The 405EX and 405EXr are identical exept that the 2nd PCIe and the
2nd EMAC interfaces are missing.
Signed-off-by: Stefan Roese <sr@denx.de>
---
arch/powerpc/boot/dts/haleakala.dts | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/boot/dts/haleakala.dts b/arch/powerpc/boot/dts/haleakala.dts
index 5dd3d15..ae68fef 100644
--- a/arch/powerpc/boot/dts/haleakala.dts
+++ b/arch/powerpc/boot/dts/haleakala.dts
@@ -235,7 +235,7 @@
#interrupt-cells = <1>;
#size-cells = <2>;
#address-cells = <3>;
- compatible = "ibm,plb-pciex-405exr", "ibm,plb-pciex";
+ compatible = "ibm,plb-pciex-405ex", "ibm,plb-pciex";
primary;
port = <0>; /* port number */
reg = <a0000000 20000000 /* Config space access */
--
1.5.4.2
^ permalink raw reply related
* Build fails for adder875 redboot on Linux-2.6.25-rc2
From: Rognlien Dag Kristian @ 2008-02-20 9:50 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <mailman.658.1203499779.17968.linuxppc-dev@ozlabs.org>
After the change to build images for several dts files the redboot image
failes to "wrap".
--
WRAP arch/powerpc/boot/cuImage.adder875-uboot
DTC: dts->dtb on file ".../arch/powerpc/boot/dts/adder875-uboot.dts"
Image Name: Linux-2.6.25-rc2-00359-g101142c
Created: Wed Feb 20 10:38:21 2008
Image Type: PowerPC Linux Kernel Image (gzip compressed)
Data Size: 1053933 Bytes =3D 1029.23 kB =3D 1.01 MB
Load Address: 0x00400000
Entry Point: 0x00400554
WRAP arch/powerpc/boot/zImage-dtb.adder875-redboot
DTC: dts->dtb on file ".../arch/powerpc/boot/dts/adder875-redboot.dts"
ppc_8xx-ld: arch/powerpc/boot/adder875-redboot.o: No such file: No such
file or directory
make[1]: *** [arch/powerpc/boot/zImage-dtb.adder875-redboot] Error 1
make: *** [zImage] Error 2
Regards,
Dag K. W. Rognlien
Research Scientist
SINTEF ICT Communication Systems
^ permalink raw reply
* [patch v8 2/4] USB: add Cypress c67x00 OTG controller core driver
From: Peter Korsgaard @ 2008-02-20 9:07 UTC (permalink / raw)
To: dbrownell, linux-usb, linuxppc-dev, grant.likely, stern, greg
In-Reply-To: <20080220090727.904160000@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 | 229 ++++++++++++++++++++++++++++++++++++++++
include/linux/usb/c67x00.h | 48 ++++++++
3 files changed, 283 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,229 @@
+/*
+ * 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/list.h>
+#include <linux/usb.h>
+#include <linux/usb/c67x00.h>
+#include <asm/io.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",
+ },
+};
+
+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
@@ -3949,6 +3949,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 v8 1/4] USB: add Cypress c67x00 low level interface code
From: Peter Korsgaard @ 2008-02-20 9:07 UTC (permalink / raw)
To: dbrownell, linux-usb, linuxppc-dev, grant.likely, stern, greg
In-Reply-To: <20080220090727.904160000@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/io.h>
+#include <asm/byteorder.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 v8 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Peter Korsgaard @ 2008-02-20 9:07 UTC (permalink / raw)
To: dbrownell, linux-usb, linuxppc-dev, grant.likely, stern, greg
In-Reply-To: <20080220090727.904160000@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 | 11
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 | 12
9 files changed, 1837 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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", __FUNCTION__);
+ 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", __FUNCTION__);
+ 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",
+ __FUNCTION__, 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",
+ __FUNCTION__);
+
+ 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,11 @@
+#
+# Makefile for Cypress C67X00 USB Controller
+#
+
+ifeq ($(CONFIG_USB_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+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 <asm/io.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
@@ -261,3 +261,15 @@
To compile this driver as a module, choose M here: the
module will be called r8a66597-hcd.
+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.
--
Bye, Peter Korsgaard
^ permalink raw reply
* [patch v8 4/4] USB: add Cypress c67x00 OTG controller gadget driver
From: Peter Korsgaard @ 2008-02-20 9:07 UTC (permalink / raw)
To: dbrownell, linux-usb, linuxppc-dev, grant.likely, stern, greg
In-Reply-To: <20080220090727.904160000@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 | 206 ++++++++
drivers/usb/c67x00/c67x00-udc.c | 905 +++++++++++++++++++++++++++++++++++++
drivers/usb/c67x00/c67x00-udc.h | 48 +
drivers/usb/c67x00/c67x00.h | 21
drivers/usb/gadget/Kconfig | 7
drivers/usb/gadget/gadget_chips.h | 8
drivers/usb/host/Kconfig | 12
13 files changed, 1248 insertions(+), 15 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,905 @@
+/*
+ * 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 "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 */
+
+
+
+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;
+};
+
+#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;
+ int config_nr;
+ /* The highest string descriptor entry
+ (used to retrieve descriptors from gadget driver) */
+ int top_str_id;
+ u16 string_desc_addr;
+};
+
+const static unsigned char get_descriptor_device[] = {
+ USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_GET_DESCRIPTOR,
+ 0x00,
+ USB_DT_DEVICE,
+ 0x00,
+ 0x00,
+ 0x12,
+ 0x00
+};
+
+const static unsigned char get_descriptor_config[] = {
+ USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_GET_DESCRIPTOR,
+ 0x00,
+ USB_DT_CONFIG,
+ 0x00,
+ 0x00,
+ 0x40,
+ 0x00
+};
+
+static unsigned char get_descriptor_string[] = {
+ USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_GET_DESCRIPTOR,
+ 0x00,
+ USB_DT_STRING,
+ 0x00,
+ 0x00,
+ 0xFF,
+ 0x00
+};
+
+#define SIEx_DEV_DESC_LOC(x) ((x) ? (CY_UDC_DESC_BASE_ADDRESS + 0x200) \
+ : CY_UDC_DESC_BASE_ADDRESS)
+#define SIEx_CONF_DESC_LOC(x) (SIEx_DEV_DESC_LOC(x) + 32)
+
+/* ------------------------------------------------------------------------- */
+/* 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",
+ },
+ },
+};
+
+
+/*----------------------------------------------------------------------------*/
+
+static void c67x00_udc_set_configuration(struct c67x00_sie *sie, u16 config)
+{
+ struct usb_ctrlrequest request;
+ struct c67x00_udc *udc = sie->private_data;
+
+ dev_dbg(sie_dev(sie), "set configuration %d\n", config);
+
+ request.bRequest = USB_REQ_SET_CONFIGURATION;
+ request.bRequestType = 0;
+ request.wValue = cpu_to_le16(config);
+ udc->driver->setup(&udc->gadget, &request);
+}
+
+static int c67x00_udc_parse_descriptor(struct c67x00_udc *udc,
+ struct c67x00_request *req)
+{
+ int retval = 0;
+ u8 *buf = req->req.buf;
+
+ if (req->req.length < 2)
+ return 0;
+
+ switch (buf[1]) {
+ case USB_DT_DEVICE: {
+ struct usb_device_descriptor *desc = req->req.buf;
+
+ /* Look for the highest stringIndex */
+ if (desc->iManufacturer > udc->top_str_id)
+ udc->top_str_id = desc->iManufacturer;
+ if (desc->iProduct > udc->top_str_id)
+ udc->top_str_id = desc->iProduct;
+ if (desc->iSerialNumber > udc->top_str_id)
+ udc->top_str_id = desc->iSerialNumber;
+
+ /* Write descriptor to C67x00 memory */
+ c67x00_ll_write_mem_le16(udc->sie->dev,
+ SIEx_DEV_DESC_LOC(udc->sie->sie_num),
+ req->req.buf, req->req.length);
+
+ /* Write vector address to c67x00 */
+ c67x00_ll_set_device_descriptor_location(
+ udc->sie, SIEx_DEV_DESC_LOC(udc->sie->sie_num));
+
+ retval = 1;
+ break;
+ }
+
+ case USB_DT_CONFIG: {
+ struct usb_config_descriptor *desc = req->req.buf;
+ int offset;
+ u16 length;
+
+ /* store config number to pass to the gadget driver,
+ once the c67x00 is configured */
+ udc->config_nr = desc->bConfigurationValue;
+
+ length = le16_to_cpu(desc->wTotalLength);
+
+ if (desc->iConfiguration > udc->top_str_id)
+ udc->top_str_id = desc->iConfiguration;
+
+ offset = desc->bLength;
+
+ while (offset < length) {
+ if (buf[offset + 1] == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *if_desc =
+ (struct usb_interface_descriptor *)
+ (buf + offset);
+
+ if (if_desc->iInterface > udc->top_str_id)
+ udc->top_str_id = if_desc->iInterface;
+ }
+
+ offset += buf[offset];
+ }
+
+ if ((length % 8) == 0) {
+ /* BIOS can't handle descriptors with size multiple
+ of xfer size */
+ length += 1;
+ /* desc->wTotalLength = cpu_to_le16(length); */
+ }
+
+ /* BIOS can only handle configuration 1,
+ so make sure the config nr is 1 */
+ desc->bConfigurationValue = 1;
+
+ c67x00_ll_write_mem_le16(udc->sie->dev,
+ SIEx_CONF_DESC_LOC(udc->sie->sie_num),
+ req->req.buf, length);
+
+ /* Write vector address to SW interrupt */
+ c67x00_ll_set_configuration_descriptor_location(
+ udc->sie, SIEx_CONF_DESC_LOC(udc->sie->sie_num));
+
+ /* String descriptors start behind configuration descriptor */
+ udc->string_desc_addr =
+ SIEx_CONF_DESC_LOC(udc->sie->sie_num) + length;
+
+ /* Make sure the address is even */
+ if (udc->string_desc_addr & 0x01)
+ udc->string_desc_addr++;
+
+ /* Write string descriptor vector address */
+ c67x00_ll_set_string_descriptor_location(
+ udc->sie, udc->string_desc_addr);
+
+ retval = 1;
+ break;
+ }
+
+ case USB_DT_STRING:
+ /* Write string descriptor */
+ c67x00_ll_write_mem_le16(udc->sie->dev, udc->string_desc_addr,
+ req->req.buf, req->req.length);
+
+ /* set address to end of this descriptor */
+ udc->string_desc_addr += req->req.length;
+
+ retval = 1;
+ break;
+ }
+
+ return retval;
+}
+
+/*
+ * 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;
+/*
+ if (status && status != -ESHUTDOWN)
+ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+*/
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ 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)
+{
+ 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)
+ c67x00_ll_susb_start_send(sie, ep->ep_num,
+ req->req.buf, req->req.length);
+ else
+ c67x00_ll_susb_start_receive(sie, ep->ep_num, req->req.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);
+}
+
+static void c67x00_udc_done_irq(struct c67x00_udc_ep *ep, int status)
+{
+ struct c67x00_request *req;
+ struct c67x00_sie *sie = ep->udc->sie;
+ int result;
+
+ result = c67x00_ll_susb_get_transfer_status(sie, ep->ep_num);
+ if (result < 0) {
+ dev_err(sie_dev(sie), "udc_done_irq error (%d)\n", result);
+ return;
+ }
+
+ if (unlikely(list_empty(&ep->queue)))
+ return;
+
+ req = list_entry(ep->queue.next, struct c67x00_request, queue);
+
+ req->req.actual = req->req.length - result;
+
+ if (!ep->is_ep_in && ep->ep_num != 0)
+ c67x00_ll_susb_receive(sie, ep->ep_num,
+ req->req.buf, req->req.actual);
+
+ 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", "ep1out-bulk", "ep2in-bulk", "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) {
+ /* The gadget driver returns the descriptors through this way */
+ if (!c67x00_udc_parse_descriptor(dev, req)) {
+ req->req.actual = req->req.length;
+ return -EINVAL;
+ } else {
+ req->req.status = 0;
+ req->req.actual = req->req.length;
+ req->req.complete(&ep->ep, &req->req);
+ return 0;
+ }
+
+ }
+
+ 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,
+};
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_udc_msg_received(struct c67x00_sie *sie, u16 msg)
+{
+ struct c67x00_udc *udc = sie->private_data;
+ u16 EPx_msg_mask = /*SUSB_EP0_MSG | */ SUSB_EP1_MSG
+ | SUSB_EP2_MSG
+ | SUSB_EP3_MSG
+ | SUSB_EP4_MSG
+ | SUSB_EP5_MSG
+ | SUSB_EP6_MSG
+ | SUSB_EP7_MSG;
+
+ if ((msg & EPx_msg_mask) != 0) {
+ int i, mask = 0x01;
+
+ for (i = 0; i < C67X00_MAX_NB_END_POINTS; i++, mask <<= 1)
+ if (msg & mask)
+ c67x00_udc_done_irq(&udc->ep[i], 0);
+ }
+
+ if (msg & SUSB_RST_MSG) {
+ int i;
+ dev_dbg(sie_dev(sie),
+ "udc_msg_rec (0x%04X) : SUSB_RST_MSG\n", msg);
+
+ 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);
+ }
+ }
+ if (udc->driver && udc->driver->disconnect)
+ udc->driver->disconnect(&udc->gadget);
+ }
+
+ if (msg & SUSB_SOF_MSG) {
+ dev_dbg(sie_dev(sie),
+ "udc_msg_rec (0x%04X) : SUSB_SOF_MSG\n", msg);
+ }
+
+ if (msg & SUSB_CFG_MSG) {
+ dev_dbg(sie_dev(sie),
+ "udc_msg_rec (0x%04X) : SUSB_CFG_MSG\n", msg);
+ /* the c67x00 BIOS only supports 1 configuration,
+ so it must be configuration 1 */
+ c67x00_udc_set_configuration(sie, udc->config_nr);
+ }
+
+ if (msg & SUSB_SUS_MSG) {
+ dev_dbg(sie_dev(sie),
+ "udc_msg_rec (0x%04X) : SUSB_SUS_MSG\n", msg);
+ }
+
+ if (msg & SUSB_ID_MSG) {
+ dev_dbg(sie_dev(sie),
+ "udc_msg_rec (0x%04X) : SUSB_ID_MSG\n", msg);
+ }
+
+ if (msg & SUSB_VBUS_MSG) {
+ dev_dbg(sie_dev(sie),
+ "udc_msg_rec (0x%04X) : SUSB_VBUS_MSG\n", msg);
+ }
+}
+
+
+/*
+ * 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;
+
+ if (msg)
+ c67x00_udc_msg_received(sie, msg);
+
+ 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)) {
+ dev_info(sie_dev(sie), "sie%d : reset IRQ\n",
+ sie->sie_num);
+
+ /* Handle reset here */
+ c67x00_ll_usb_clear_status(sie, RESET_IRQ_FLG);
+ }
+
+ if (int_status & DONE_FLG(sie->sie_num))
+ dev_info(sie_dev(sie), "sie%d : done IRQ -> "
+ "device status 0x%04X\n",
+ sie->sie_num, device_status);
+}
+
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct c67x00_udc *udc = &controller;
+ int i, retval;
+
+ if (!driver
+/* || driver->speed < USB_SPEED_FULL*/
+ || !driver->bind || !driver->setup) {
+
+ printk(KERN_ERR
+ "c67x00 : 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;
+ udc->config_nr = 1;
+ udc->top_str_id = 0;
+ 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;
+ }
+
+ /* retrieve descriptors from gadget and program them in device */
+ udc->driver->setup(&udc->gadget,
+ (struct usb_ctrlrequest *)
+ get_descriptor_device);
+ udc->driver->setup(&udc->gadget,
+ (struct usb_ctrlrequest *)
+ get_descriptor_config);
+
+ for (i = 0; i <= udc->top_str_id; i++) {
+ get_descriptor_string[2] = i;
+ udc->driver->setup(&udc->gadget,
+ (struct usb_ctrlrequest *)
+ get_descriptor_string);
+ }
+
+ 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];
+ if (i != 0) {
+ INIT_LIST_HEAD(&udc->ep[i].ep.ep_list);
+ 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->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;
+ 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);
+
+ 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);
+ 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
@@ -296,6 +296,11 @@
return rc;
}
+static u16 c67x00_get_comm_reg(struct c67x00_device *dev, u16 nr)
+{
+ return hpi_read_word(dev, COMM_R(nr));
+}
+
/* -------------------------------------------------------------------------- */
/* Host specific functions */
@@ -372,6 +377,207 @@
}
/* -------------------------------------------------------------------------- */
+void c67x00_ll_susb_init(struct c67x00_sie *sie)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ u16 addr;
+ int rc;
+
+ /* The BIOS SUSB_INIT_INT handler for some reason is hardcoded to only
+ enable peripheral support for port A. Relocate the routine to RAM
+ and patch out that instruction (mov [r10-0xe],[r8]) */
+ addr = hpi_read_word(dev, SUSB_INIT_INT_LOC);
+
+ /* already patched? */
+ if (addr != CY_UDC_BIOS_REPLACE_BASE) {
+ u16 buf[64]; /* should be plenty for the handler */
+ int i;
+
+ c67x00_ll_read_mem_le16(dev, addr, buf, sizeof(buf));
+
+ /* patch it */
+ for (i = 0; i < (ARRAY_SIZE(buf)-1); i++) {
+ if ((buf[i] == cpu_to_le16(0x0432))
+ && (buf[i+1] == cpu_to_le16(0xfff2))) {
+ buf[i] = buf[i+1] = 0; /* nop */
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(buf))
+ dev_warn(sie_dev(sie), "BIOS code not recognized, "
+ "port B may not be available\n");
+
+ c67x00_ll_write_mem_le16(dev, CY_UDC_BIOS_REPLACE_BASE,
+ buf, sizeof(buf));
+ hpi_write_word(dev, SUSB_INIT_INT_LOC,
+ CY_UDC_BIOS_REPLACE_BASE);
+ }
+
+ hpi_clear_bits(dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_HPI_EN(sie->sie_num));
+ hpi_set_bits(dev,
+ HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_CPU_EN(sie->sie_num)
+ | RESUME_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),
+ SOF_EOP_TMOUT_IRQ_EN | ID_IRQ_EN | 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), 0x0000);
+ else
+ hpi_write_word(dev, DEVICE_N_PORT_SEL(sie->sie_num), 0x4000);
+
+ data.regs[1] = 0; /* full speed */
+ data.regs[2] = sie->sie_num + 1;
+ rc = c67x00_comm_exec_int(dev, SUSB_INIT_INT, &data);
+
+ if ((hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num)) & HOST_MODE))
+ dev_warn(sie_dev(sie),
+ "SIE %d not set to peri mode\n", sie->sie_num);
+
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+
+ hpi_set_bits(dev,
+ DEVICE_N_IRQ_EN_REG(sie->sie_num),
+ SOF_EOP_TMOUT_IRQ_EN | ID_IRQ_EN | VBUS_IRQ_EN);
+
+ 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_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);
+}
+
+void c67x00_ll_set_device_descriptor_location(struct c67x00_sie *sie,
+ u16 address)
+{
+ hpi_write_word(sie->dev, SUSBx_DEV_DESC_VEC(sie->sie_num), address);
+}
+
+void c67x00_ll_set_configuration_descriptor_location(struct c67x00_sie *sie,
+ u16 address)
+{
+ hpi_write_word(sie->dev, SUSBx_CONF_DESC_VEC(sie->sie_num), address);
+}
+
+void c67x00_ll_set_string_descriptor_location(struct c67x00_sie *sie,
+ u16 address)
+{
+ hpi_write_word(sie->dev, SUSBx_STRING_DESC_VEC(sie->sie_num), address);
+}
+
+int c67x00_ll_susb_start_send(struct c67x00_sie *sie, int ep,
+ void *data, int len)
+{
+ u16 header[4];
+ struct c67x00_lcp_int_data regs;
+
+ c67x00_ll_write_mem_le16(sie->dev, CY_UDC_REQ_BUFFER_ADDR(ep),
+ data, len);
+
+ header[0] = 0;
+ header[1] = cpu_to_le16(CY_UDC_REQ_BUFFER_ADDR(ep));
+ header[2] = cpu_to_le16(len);
+ header[3] = 0;
+
+ c67x00_ll_write_mem_le16(sie->dev, CY_UDC_REQ_HEADER_ADDR(ep), header,
+ sizeof(header));
+
+ regs.regs[0] = 0;
+ regs.regs[1] = ep;
+ regs.regs[8] = CY_UDC_REQ_HEADER_ADDR(ep);
+
+ return c67x00_comm_exec_int(sie->dev, SUSBx_SEND_INT(sie->sie_num),
+ ®s);
+}
+
+int c67x00_ll_susb_start_receive(struct c67x00_sie *sie, int ep, int len)
+{
+ u16 header[4];
+ struct c67x00_lcp_int_data regs;
+
+ header[0] = 0;
+ header[1] = cpu_to_le16(CY_UDC_REQ_BUFFER_ADDR(ep));
+ header[2] = cpu_to_le16(len);
+ header[3] = 0;
+
+ c67x00_ll_write_mem_le16(sie->dev, CY_UDC_REQ_HEADER_ADDR(ep), header,
+ sizeof(header));
+
+ regs.regs[0] = 0;
+ regs.regs[1] = ep;
+ regs.regs[8] = CY_UDC_REQ_HEADER_ADDR(ep);
+
+ return c67x00_comm_exec_int(sie->dev, SUSBx_RECEIVE_INT(sie->sie_num),
+ ®s);
+}
+
+int c67x00_ll_susb_get_transfer_status(struct c67x00_sie *sie, int ep)
+{
+ u16 header[4];
+ u16 result = c67x00_get_comm_reg(sie->dev, 0);
+
+ if (result)
+ return -result;
+
+ c67x00_ll_read_mem_le16(sie->dev, CY_UDC_REQ_HEADER_ADDR(ep),
+ header, sizeof(header));
+ /* nr of bytes not transferred */
+ return le16_to_cpu(header[2]);
+}
+
+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_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
@@ -283,6 +283,27 @@
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_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);
+void c67x00_ll_set_device_descriptor_location(struct c67x00_sie *sie,
+ u16 address);
+void c67x00_ll_set_configuration_descriptor_location(struct c67x00_sie *sie,
+ u16 address);
+void c67x00_ll_set_string_descriptor_location(struct c67x00_sie *sie,
+ u16 address);
+int c67x00_ll_susb_start_send(struct c67x00_sie *sie, int ep,
+ void *data, int len);
+int c67x00_ll_susb_start_receive(struct c67x00_sie *sie, int ep, int len);
+int c67x00_ll_susb_get_transfer_status(struct c67x00_sie *sie, int ep);
+void c67x00_ll_susb_receive(struct c67x00_sie *sie, int ep,
+ void *data, int len);
+
/* 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
@@ -6,6 +6,9 @@
EXTRA_CFLAGS += -DDEBUG
endif
-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)) && EXPERIMENTAL
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
@@ -261,15 +261,3 @@
To compile this driver as a module, choose M here: the
module will be called r8a66597-hcd.
-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.
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 v8 0/4] Cypress c67x00 (EZ-Host/EZ-OTG) support
From: Peter Korsgaard @ 2008-02-20 9:07 UTC (permalink / raw)
To: dbrownell, linux-usb, linuxppc-dev, grant.likely, stern, 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 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
* Re: [patch v7 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Peter Korsgaard @ 2008-02-20 9:01 UTC (permalink / raw)
To: Greg KH; +Cc: linuxppc-dev, dbrownell, stern, linux-usb
In-Reply-To: <20080219235554.GA13304@kroah.com>
>>>>> "Greg" == Greg KH <greg@kroah.com> writes:
Greg> On Tue, Feb 19, 2008 at 04:09:19PM +0100, Peter Korsgaard wrote:
>> This patch adds HCD support for the Cypress c67x00 family of devices.
>>
>> Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Greg> And it doesn't build:
Greg> CC [M] drivers/usb/c67x00/c67x00-hcd.o
Greg> distcc[2413] ERROR: compile /home/gregkh/.ccache/c67x00-hcd.tmp.mini.2409.i on localhost failed
Greg> drivers/usb/c67x00/c67x00-hcd.c:345: error: redefinition of 'c67x00_hcd_probe'
Greg> drivers/usb/c67x00/c67x00-hcd.h:119: error: previous definition of 'c67x00_hcd_probe' was here
Greg> drivers/usb/c67x00/c67x00-hcd.c:402: error: redefinition of 'c67x00_hcd_remove'
Greg> drivers/usb/c67x00/c67x00-hcd.h:126: error: previous definition of 'c67x00_hcd_remove' was here
Greg> make[2]: *** [drivers/usb/c67x00/c67x00-hcd.o] Error 1
Greg> make[1]: *** [drivers/usb/c67x00] Error 2
Greg> make: *** [_module_drivers/usb] Error 2
Huh, that doesn't make any sense to me. The dumy definitions of
c67x00_hcd_{probe,remove} are protected by an ifndef
CONFIG_USB_C67X00_HCD, and c67x00-hcd.c which contains the real
definitions only gets compiled if CONFIG_USB_C67X00_HCD is
enabled. Could you perhaps provide some more details?
Anyway, the conditional hcd stuff isn't really needed at this point in
the patch series, so I have moved it into patch 4 in the v8 series
I'll post in a minute.
Greg> This is _after_ removing the obviously incorrect usb_disabled()
Greg> function that you included in your .h file.
That was again for conditional hcd/udc support. I have now moved the
check into _hcd_probe() instead.
Greg> I'm going to hold off applying any of these for now, as it
Greg> doesn't look like something is configured properly here.
Could you give the v8 series a try please?
--
Bye, Peter Korsgaard
^ permalink raw reply
* Réf. : Status of I2C on mpc5200
From: Eric DUJARDIN @ 2008-02-20 8:33 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 1933 bytes --]
Got it. The mpc52xx's still don't support interrupts i.e. reads require
polling.
As a consequence reads may time out.
But it doesn't prevent I2c from working.
Sorry for disturbing,
Eric Dujardin
-------------------------------
Sagem DS - DP Combat Terrestre
178 rue de Paris - F-91344 Massy Cedex
(33) 1 69196792 (direct)
(33) 1 69196904 (fax)
-------------------------------
"Eric DUJARDIN" <eric.dujardin@sagem.com>
Envoyé par : linuxppc-dev-bounces+eric.dujardin=sagem.com@ozlabs.org
19/02/2008 19:00
Remis le : 19/02/2008 19:01
Pour :
cc : (ccc : Eric DUJARDIN/DRD/SAGEM)
Objet : Status of I2C on mpc5200
Hello all,
I have a question regarding the stability of I2C on mpc52xx.
According to menuconfig's help it isn't still stable as it says:
"The driver may also work on 52xx family processors, though interrupts are
known not to work"
However there has been a patch from Domen Puncer that apparently fixed the
driver in 2.6.23.
<snip>
If any kind soul could provide a status I'd be glad to provide the patch
for the help file...
Thanks,
Eric Dujardin
" Ce courriel et les documents qui y sont attaches peuvent contenir des informations confidentielles. Si vous n'etes pas le destinataire escompte, merci d'en informer l'expediteur immediatement et de detruire ce courriel ainsi que tous les documents attaches de votre systeme informatique. Toute divulgation, distribution ou copie du present courriel et des documents attaches sans autorisation prealable de son emetteur est interdite."
" This e-mail and any attached documents may contain confidential or proprietary information. If you are not the intended recipient, please advise the sender immediately and delete this e-mail and all attached documents from your computer system. Any unauthorised disclosure, distribution or copying hereof is prohibited."
[-- Attachment #2: Type: text/html, Size: 3242 bytes --]
^ permalink raw reply
* Re: [PATCH 1/3] Fix Unlikely(x) == y
From: Willy Tarreau @ 2008-02-20 7:32 UTC (permalink / raw)
To: Andi Kleen
Cc: Nick Piggin, Roel Kluin, lkml, linuxppc-dev, cbe-oss-dev,
Arjan van de Ven
In-Reply-To: <20080219092846.GB6485@one.firstfloor.org>
On Tue, Feb 19, 2008 at 10:28:46AM +0100, Andi Kleen wrote:
> > Sometimes, for performance critical paths, I would like gcc to be dumb and
> > follow *my* code and not its hard-coded probabilities.
>
> If you really want that, simple: just disable optimization @)
already tried. It fixed some difficulties, but create new expected issues
with data being reloaded often from memory instead of being passed along
a few registers. Don't forget that optimizing for x86 requires a lot of
smartness from the compiler because of the very small number of registers!
> > Maybe one thing we would need would be the ability to assign probabilities
> > to each branch based on what we expect, so that gcc could build a better
> > tree keeping most frequently used code tight.
>
> Just use profile feedback then for user space. I don't think it's a good
> idea for kernel code though because it leads to unreproducible binaries
> which would wreck the development model.
I never found this to be practically usable in fact, because you have to
use it on the *exact* same source. End of game for cross-compilation. It
would be good to be able to use a few pragmas in the code to say "hey, I
want this block optimized like this". This is what I understood the
__builtin_expect() was for, except that it tends to throw unpredicted
branches too far away.
> > Hmm I've just noticed -fno-guess-branch-probability in the man, I never tried
> > it.
>
> Or -fno-reorder-blocks
Thanks for the hint, I will try it.
Willy
^ permalink raw reply
* Re: [PATCH 2/2] i2c-ibm_iic driver
From: Jean Delvare @ 2008-02-20 7:20 UTC (permalink / raw)
To: Sean MacLennan; +Cc: LinuxPPC-dev, i2c
In-Reply-To: <47BB50E9.60508@pikatech.com>
Hi Sean,
On Tue, 19 Feb 2008 16:58:01 -0500, Sean MacLennan wrote:
> Signed-off-by: Sean MacLennan <smaclennan@pikatech.com>
>
> --- for-2.6.25/drivers/i2c/busses/orig-i2c-ibm_iic.c 2008-02-18 16:36:30.000000000 -0500
> +++ for-2.6.25/drivers/i2c/busses/i2c-ibm_iic.c 2008-02-19 16:46:35.000000000 -0500
> @@ -6,6 +6,9 @@
> * Copyright (c) 2003, 2004 Zultys Technologies.
> * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
> *
> + * Copyright (c) 2008 PIKA Technologies
> + * Sean MacLennan <smaclennan@pikatech.com>
> + *
> * Based on original work by
> * Ian DaSilva <idasilva@mvista.com>
> * Armin Kuster <akuster@mvista.com>
> (...)
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -244,7 +244,7 @@ config I2C_PIIX4
>
> config I2C_IBM_IIC
> tristate "IBM PPC 4xx on-chip I2C interface"
> - depends on IBM_OCP
> + depends on 4xx
> help
> Say Y here if you want to use IIC peripheral found on
> embedded IBM PPC 4xx based systems.
I've applied this version. I see that there are discussions going on
about the best "depends on" statement in Kconfig, feel free to submit
an updated (or incremental) patch as soon as a decision will have been
taken.
Thanks,
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH 2/2] i2c-ibm_iic driver
From: Jean Delvare @ 2008-02-20 6:57 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linuxppc-dev, Stefan Roese, i2c, Sean MacLennan
In-Reply-To: <200802192355.17707.arnd@arndb.de>
Hi Arnd,
On Tue, 19 Feb 2008 23:55:16 +0100, Arnd Bergmann wrote:
> On Tuesday 19 February 2008, Stefan Roese wrote:
> > On Tuesday 19 February 2008, Jean Delvare wrote:
> > >
> > > With this Kconfig change, "make menuconfig" lets me select the
> > > i2c-ibm_iic driver on x86_64, but it fails to build horribly. I think
> > > that you want to restrict the build to PPC machines somehow, or at
> > > least make sure that either IBM_OCP or OF support is present.
> >=20
> > How about this:
> >=20
> > -=A0=A0=A0=A0=A0=A0=A0depends on IBM_OCP
> > +=A0=A0=A0=A0=A0=A0=A0depends on 4xx
>=20
> I think we should allow it to be built on other platforms as well,
> as long as they have of_platform_device support.
>=20
> The Axon south bridge used on IBMs QS21 blade probably has an ibm_iic,
> even though it's managed by the firmware and we probably don't want
> to use it at this time, someone could use the same chip in a new
> design and actually do that.
>=20
> In general, I also like to make it possible to enable drivers just
> for the benefit of compile testing, even for stuff that you can't
> find in any existing HW configuration, so as long as it builds on
> a platform, I think we shouldn't forbid it:
Fine with me as long as the default is set appropriately (i.e. default
to not building the driver on archs/platforms where it builds but is
known to be useless.)
>=20
> - depends on IBM_OCP
> + depends on IBM_OCP || PPC_MERGE
--=20
Jean Delvare
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox