* Re: How to do IO mapped Implimentation ???
From: Misbah khan @ 2007-12-31 6:46 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <14522349.post@talk.nabble.com>
yes with respect to ioremap() its right ...i just missed the secound argument
....
in_8() and out_8() will work fine or i need to use in_be8() and out_be8() on
PPC architecture ????
---Misbah<><
Misbah khan wrote:
>
> Hi all...
>
> I am writing a driver in which device port is mapped to CPLD and 8 bit
> data bus is directly connected from processor to CPLD. Read write on CPLD
> memory mapped (buffer/register) is required to control the device. This is
> now IO mapped to processor.
>
> I need to know whether i am right if i impliment like this :-
> addr=ioremap(base_addr); // Remap to Mem mapped address
> outb(addr) and inb(addr);
>
> Please suggest me if i am wrong or there could be better solution to this
> .
>
> -----Misbah <><
>
>
--
View this message in context: http://www.nabble.com/How-to-do-IO-mapped-Implimentation-----tp14522349p14554843.html
Sent from the linuxppc-embedded mailing list archive at Nabble.com.
^ permalink raw reply
* [PATCH] [POWERPC] 4xx: PCIe: Increase max busses per port to 64
From: Stefan Roese @ 2007-12-31 5:41 UTC (permalink / raw)
To: linuxppc-dev
Because of how big mapping the config space is (1M per bus), we limit how
many busses we support for now. In the long run, we could replace that
with something akin to kmap_atomic instead.
This patch changes the limit from currently 16 to 64.
Signed-off-by: Stefan Roese <sr@denx.de>
---
arch/powerpc/sysdev/ppc4xx_pci.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 3c2c14c..5abfcd1 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -533,7 +533,7 @@ static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
*
*/
-#define MAX_PCIE_BUS_MAPPED 0x10
+#define MAX_PCIE_BUS_MAPPED 0x40
struct ppc4xx_pciex_port
{
--
1.5.4.rc2
^ permalink raw reply related
* Re: Please pull from 'for-2.6.25' branch of pasemi tree
From: Paul Mackerras @ 2007-12-31 4:20 UTC (permalink / raw)
To: Olof Johansson; +Cc: linuxppc-dev
In-Reply-To: <20071230235345.GA23903@lixom.net>
Olof Johansson writes:
> Paul,
>
> Please pull from:
>
> master.kernel.org:/pub/scm/linux/kernel/git/olof/pasemi.git for-2.6.25
Pulled & pushed out.
Paul.
^ permalink raw reply
* Re: Please pull from 'for-2.6.25' branch of 4xx tree
From: Paul Mackerras @ 2007-12-31 4:20 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20071229121642.405d2764@zod.rchland.ibm.com>
Josh Boyer writes:
> Please pull from:
>
> master.kernel.org:/pub/scm/linux/kernel/git/jwboyer/powerpc-4xx.git for-2.6.25
Pulled & pushed out.
Paul.
^ permalink raw reply
* Re: + iommu-sg-add-iommu-helper-functions-for-the-free-area-management.patch added to -mm tree
From: FUJITA Tomonori @ 2007-12-31 3:26 UTC (permalink / raw)
To: akpm
Cc: James.Bottomley, mm-commits, jeff, fujita.tomonori, linuxppc-dev,
jens.axboe, tomof, balbir
In-Reply-To: <200712072212.lB7MCKmS006195@imap1.linux-foundation.org>
On Fri, 07 Dec 2007 14:12:20 -0800
akpm@linux-foundation.org wrote:
>
> The patch titled
> iommu sg: add IOMMU helper functions for the free area management
> has been added to the -mm tree. Its filename is
> iommu-sg-add-iommu-helper-functions-for-the-free-area-management.patch
>
> *** Remember to use Documentation/SubmitChecklist when testing your code ***
>
> See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find
> out what to do about this
>
> ------------------------------------------------------
> Subject: iommu sg: add IOMMU helper functions for the free area management
> From: FUJITA Tomonori <tomof@acm.org>
>
> This adds IOMMU helper functions for the free area management. These
> functions take care of LLD's segment boundary limit for IOMMUs. They would be
> useful for IOMMUs that use bitmap for the free area management.
>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
> Cc: Jeff Garzik <jeff@garzik.org>
> Cc: James Bottomley <James.Bottomley@steeleye.com>
> Cc: Jens Axboe <jens.axboe@oracle.com>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
>
> include/linux/iommu-helper.h | 7 +++
> lib/Makefile | 1
> lib/iommu-helper.c | 76 +++++++++++++++++++++++++++++++++
> 3 files changed, 84 insertions(+)
Andrew, can you replace this patch with the attached patch?
There was a clear bug in the align allocation path though I still wait
for Balbir to test this:
http://marc.info/?l=linux-scsi&m=119881993112939&w=2
Thanks,
=
Subject: [PATCH] add IOMMU helper functions for the free area management
This adds IOMMU helper functions for the free area management. These
functions take care of LLD's segment boundary limit for IOMMUs. They would be
useful for IOMMUs that use bitmap for the free area management.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
---
include/linux/iommu-helper.h | 7 ++++
lib/Makefile | 1 +
lib/iommu-helper.c | 80 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+), 0 deletions(-)
create mode 100644 include/linux/iommu-helper.h
create mode 100644 lib/iommu-helper.c
diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h
new file mode 100644
index 0000000..4dd4c04
--- /dev/null
+++ b/include/linux/iommu-helper.h
@@ -0,0 +1,7 @@
+extern unsigned long iommu_area_alloc(unsigned long *map, unsigned long size,
+ unsigned long start, unsigned int nr,
+ unsigned long shift,
+ unsigned long boundary_size,
+ unsigned long align_mask);
+extern void iommu_area_free(unsigned long *map, unsigned long start,
+ unsigned int nr);
diff --git a/lib/Makefile b/lib/Makefile
index b6793ed..0e7383f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_SMP) += percpu_counter.o
obj-$(CONFIG_AUDIT_GENERIC) += audit.o
obj-$(CONFIG_SWIOTLB) += swiotlb.o
+obj-$(CONFIG_IOMMU_HELPER) += iommu-helper.o
obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o
lib-$(CONFIG_GENERIC_BUG) += bug.o
diff --git a/lib/iommu-helper.c b/lib/iommu-helper.c
new file mode 100644
index 0000000..495575a
--- /dev/null
+++ b/lib/iommu-helper.c
@@ -0,0 +1,80 @@
+/*
+ * IOMMU helper functions for the free area management
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+static unsigned long find_next_zero_area(unsigned long *map,
+ unsigned long size,
+ unsigned long start,
+ unsigned int nr,
+ unsigned long align_mask)
+{
+ unsigned long index, end, i;
+again:
+ index = find_next_zero_bit(map, size, start);
+
+ /* Align allocation */
+ index = (index + align_mask) & ~align_mask;
+
+ end = index + nr;
+ if (end >= size)
+ return -1;
+ for (i = index; i < end; i++) {
+ if (test_bit(i, map)) {
+ start = i+1;
+ goto again;
+ }
+ }
+ return index;
+}
+
+static inline void set_bit_area(unsigned long *map, unsigned long i,
+ int len)
+{
+ unsigned long end = i + len;
+ while (i < end) {
+ __set_bit(i, map);
+ i++;
+ }
+}
+
+static inline int is_span_boundary(unsigned int index, unsigned int nr,
+ unsigned long shift,
+ unsigned long boundary_size)
+{
+ shift = (shift + index) & (boundary_size - 1);
+ return shift + nr > boundary_size;
+}
+
+unsigned long iommu_area_alloc(unsigned long *map, unsigned long size,
+ unsigned long start, unsigned int nr,
+ unsigned long shift, unsigned long boundary_size,
+ unsigned long align_mask)
+{
+ unsigned long index;
+again:
+ index = find_next_zero_area(map, size, start, nr, align_mask);
+ if (index != -1) {
+ if (is_span_boundary(index, nr, shift, boundary_size)) {
+ /* we could do more effectively */
+ start = index + 1;
+ goto again;
+ }
+ set_bit_area(map, index, nr);
+ }
+ return index;
+}
+EXPORT_SYMBOL(iommu_area_alloc);
+
+void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr)
+{
+ unsigned long end = start + nr;
+
+ while (start < end) {
+ __clear_bit(start, map);
+ start++;
+ }
+}
+EXPORT_SYMBOL(iommu_area_free);
--
1.5.3.4
^ permalink raw reply related
* Re: Please pull powerpc.git merge branch
From: Paul Mackerras @ 2007-12-31 1:40 UTC (permalink / raw)
To: Geoff Levand; +Cc: linuxppc-dev, rrnelson, jk, arnd
In-Reply-To: <47757040.4020508@am.sony.com>
Geoff Levand writes:
> This broke Cell builds.
>
> arch/powerpc/platforms/cell/spufs/sched.c:200: multiple definition of `.notify_spus_active'
> arch/powerpc/platforms/cell/spu_syscalls.c:149: first defined here
>
>
> commit aed3a8c9bb1a8623a618232087c5ff62718e3b9a
> Author: Bob Nelson <rrnelson@linux.vnet.ibm.com>
> Date: Sat Dec 15 01:27:30 2007 +1100
>
> [POWERPC] Oprofile: Remove dependency on spufs module
>
>
> Looking at the code, it seems just when CONFIG_SPU_FS=y, not when =m.
Indeed. Clearly, several people have failed to review this patch
properly. :(
It looks to me that the definition of notify_spus_active under #ifndef
MODULE in spufs/sched.c is bogus and should just be removed
completely. Also, I don't see any need for do_notify_spus_active to
be exported.
I propose the patch below. Arnd, Jeremy, Bob, any opinions?
Paul.
---
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 6e2a45e..9ad53e6 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -192,15 +192,6 @@ void do_notify_spus_active(void)
mutex_unlock(&cbe_spu_info[node].list_mutex);
}
}
-EXPORT_SYMBOL_GPL(do_notify_spus_active);
-
-#ifndef MODULE
-void notify_spus_active(void)
-{
- do_notify_spus_active();
-}
-EXPORT_SYMBOL_GPL(notify_spus_active);
-#endif
/**
* spu_bind_context - bind spu context to physical spu
^ permalink raw reply related
* 83xx, ELDK 2.6.23, IP-Config: No network devices
From: Russell McGuire @ 2007-12-31 0:51 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <mailman.1.1198890002.24584.linuxppc-embedded@ozlabs.org>
All,
Attempting to get MPC8360 board booted into Linux using a NFS root fs.
I can see the kernel boots up, and registers both the MDIO driver and the
ucc_geth: driver, and with additional debug they encounter no errors.
However, for some reason the probe function never gets called for the MDIO
or the UCC driver. So as a result <assuming> the IP layer never gets a
network device. I am using a National DP83865 PHY device connected directly
to the 83xx QE. I don't see any direct definition for this PHY device, but
it doesn't even look like it queries the ID.
So two questions:
1) Is there some basic kernel feature I am missing? I have enabled the GIGE
UEC GETH driver in the kernel. Perhaps a PHY LIB? Isn't generic MII
supported by default?
2) Is there something in the startup board files, that I need to add to
register my PHY like an of_put_node()? Again I have pretty much copied the
MPC8360E MDS board and it is starting, and defining the par_io port already,
except that my PHY ID <on the iC2 bus, is using dev ID 0x01>. However, I
don't see the probe function being called, so I don't think this is a
concern yet.
Anyway, how do I get the UEC_driver to see my phy device so I can get the
NFS to boot?
-Russ
^ permalink raw reply
* Please pull from 'for-2.6.25' branch of pasemi tree
From: Olof Johansson @ 2007-12-30 23:53 UTC (permalink / raw)
To: paulus; +Cc: linuxppc-dev
Paul,
Please pull from:
master.kernel.org:/pub/scm/linux/kernel/git/olof/pasemi.git for-2.6.25
For the following pasemi patches:
Olof Johansson (3):
[POWERPC] pasemi: Implement NMI support
[POWERPC] pasemi: Distribute interrupts evenly across cpus
[POWERPC] Enable CONFIG_PCI_MSI and CONFIG_MD in pasemi_defconfig
arch/powerpc/configs/pasemi_defconfig | 30 ++++++++++++++++++++++++++----
arch/powerpc/platforms/pasemi/setup.c | 30 +++++++++++++++++++++++++++---
arch/powerpc/sysdev/mpic.c | 31 ++++++++++++++++++++++++++++---
arch/powerpc/sysdev/mpic.h | 3 ---
include/asm-powerpc/mpic.h | 20 +++++++++++++++++++-
5 files changed, 100 insertions(+), 14 deletions(-)
Thanks,
-Olof
^ permalink raw reply
* Re: Device node - How does kernel know about it
From: Brad Boyer @ 2007-12-30 23:43 UTC (permalink / raw)
To: Siva Prasad; +Cc: linuxppc-dev, Nicholas Mc Guire, linuxppc-embedded
In-Reply-To: <D83235F0F3C86D4D889D8B9A0DA8C6D7012AFA2D@corpexc01.corp.networkrobots.com>
On Thu, Dec 27, 2007 at 07:27:17PM -0800, Siva Prasad wrote:
> What is the kernel routine that is first called when there is, for
> example a read() function call from user program?
> I would like to start debugging from there and see if any thing at all
> happens when there is a call. Appreciate your help with this question.
I don't generally recommend starting debugging at that level, but I'll
try to give you some pointers. Every system call coming into the kernel
from a user space program initially runs a little piece of assembly
language code that looks up a handler (by number) in the system call
table and sets up the proper environment to call the appropriate function
that implements that call (which is written in C). Normally, those
functions are named with a prefix of sys_ and the name of the system
call. For example, the implementation of read(2) is called sys_read. You
should be able to find it in fs/read_write.c. Other system call
implementations are scattered around to be with code related to that
call. Most of the file related ones can be found someplace under the
fs directory.
Brad Boyer
flar@allandria.com
^ permalink raw reply
* Re: How can I support 2GB of memory in Linux kernel on 440SPe
From: Vincitore @ 2007-12-30 15:36 UTC (permalink / raw)
To: Michele Pallaro; +Cc: linuxppc-embedded
In-Reply-To: <47751456.7010600@alcatel-lucent.it>
Hi Michele,
I also tried this and it did not work. It also panics while unpacking rootfs
Onced your system is up do you see all of the 2GB of Memory.
# cat /proc/meminfo
Thank You,
Vincitore
On 12/28/07, Michele Pallaro <michele.pallaro@alcatel-lucent.it> wrote:
> CONFIG_ADVANCED_OPTIONS=y
> # CONFIG_HIGHMEM_START_BOOL is not set
> CONFIG_HIGHMEM_START=0xfe000000
> # CONFIG_LOWMEM_SIZE_BOOL is not set
> CONFIG_LOWMEM_SIZE=0x30000000
> CONFIG_KERNEL_START_BOOL=y
> CONFIG_KERNEL_START=0x80000000
> # CONFIG_TASK_SIZE_BOOL is not set
> CONFIG_TASK_SIZE=0x80000000
>
> I use ppc 8555E with 2G
>
> Michele
>
>
> Vincitore wrote:
> > Hi,
> >
> > I need to support 2GB of memory on a custom 440SPe board.
> >
> > I see to support 1GB of memory on the 440SPe yucca board the following
> > configuration changes were done.
> >
> > #
> > # Advanced setup
> > #
> >
> > CONFIG_ADVANCED_OPTIONS=y
> > CONFIG_HIGHMEM_START=0xfe000000
> > CONFIG_LOWMEM_SIZE_BOOL=y
> > CONFIG_LOWMEM_SIZE=0x40000000
> > CONFIG_KERNEL_START_BOOL=y
> > CONFIG_KERNEL_START=0xa0000000
> > # CONFIG_TASK_SIZE_BOOL is not set
> > CONFIG_TASK_SIZE=0x80000000
> > # CONFIG_CONSISTENT_START_BOOL is not set CONFIG_CONSISTENT_START=0xff100000
> > # CONFIG_CONSISTENT_SIZE_BOOL is not set
> > CONFIG_CONSISTENT_SIZE=0x00200000 # CONFIG_BOOT_LOAD_BOOL is not set
> > CONFIG_BOOT_LOAD=0x01000000
> >
> > It looks like the only thing I would need to do is modify the
> > following for 2GB support.
> >
> > CONFIG_LOWMEM_SIZE=0x80000000
> > and
> > CONFIG_KERNEL_START=0x60000000
> >
> > But this does not work. It almost does, but right after I get to the
> > Linux prompt, this system panics. If I scale back to one 1GB
> > everything works great.
> >
> > Any ideas?
> >
> > Thank You,
> >
> > Vincitore
> > _______________________________________________
> > Linuxppc-embedded mailing list
> > Linuxppc-embedded@ozlabs.org
> > https://ozlabs.org/mailman/listinfo/linuxppc-embedded
> >
>
>
^ permalink raw reply
* Re: How can I support 2GB of memory in Linux kernel on 440SPe
From: Vincitore @ 2007-12-30 15:31 UTC (permalink / raw)
To: Rune Torgersen; +Cc: linuxppc-embedded
In-Reply-To: <DCEAAC0833DD314AB0B58112AD99B93B03C7D08F@ismail.innsys.innovsys.com>
Hi Rune,
I tried what you suggested. With no luck.
I enabled CONFIG_HIGHMEM=y
Now I panic when when unpacking_rootfs.
...
Memory: 2076148k available (2160k kernel code, 764k data, 144k init,
1048572k highmem)
Mount-cache hash table entries: 512
checking if image is initramfs...Oops: kernel access of bad area, sig: 11 [#1]
NIP: A02A9618 LR: A02A9598 CTR: A002EAE8
REGS: dfc01e60 TRAP: 0300 Not tainted (2.6.19.2)
MSR: 00029000 <EE,ME> CR: 24000084 XER: 00000000
DAR: 1FDEA000, DSISR: 00000000
TASK = a13e4ba0[1] 'swapper' THREAD: dfc00000
GPR00: 00000000 DFC01F10 A13E4BA0 DFC10000 000000D0 00000001 2D89B700 A02C0000
GPR08: DFC01D18 A02C0000 00000000 A02C3F78 24000084 70000000 A02D0000 00000000
GPR16: 00000001 00800000 7FFFF0FC 00000000 A02C0000 A02C0000 7FFEE61C 00000001
GPR24: 00000000 007FFE70 00000D80 00184F9D 1FDEA000 A02D0000 A00012F8 A02D0000
NIP [A02A9618] unpack_to_rootfs+0xdc/0xa54
LR [A02A9598] unpack_to_rootfs+0x5c/0xa54
Call Trace:
[DFC01F10] [A02A9598] unpack_to_rootfs+0x5c/0xa54 (unreliable)
[DFC01F60] [A02AA050] populate_rootfs+0x7c/0x104
[DFC01F80] [A0001328] init+0x30/0x2a4
[DFC01FF0] [A00047E0] kernel_thread+0x44/0x60
Instruction dump:
912b0000 3ce0a02c 3d20a02c 914b0004 90074038 90093f8c 3ce0a02c 80074038
2f800000 409e0054 2f9b0000 419e004c <881c0000> 39753f78 2f800030 82cb0000
This is the same if I have CONFIG_HIGHMEM disabled. The only
difference is highmem now shows up as 0k.
Memory: 2076148k available (2160k kernel code, 764k data, 144k init, 0k highmem)
Mount-cache hash table entries: 512
checking if image is initramfs...Oops: kernel access of bad area, sig: 11 [#1]
...
Vincitore
On 12/28/07, Rune Torgersen <runet@innovsys.com> wrote:
> > From: linuxppc-embedded-bounces+runet=innovsys.com@ozlabs.org
> > [mailto:linuxppc-embedded-bounces+runet=innovsys.com@ozlabs.or
> > g] On Behalf Of Vincitore
> > Sent: Friday, December 28, 2007 8:00 AM
> > To: linuxppc-embedded@ozlabs.org
> > Subject: How can I support 2GB of memory in Linux kernel on 440SPe
> >
> > Hi,
> >
> > I need to support 2GB of memory on a custom 440SPe board.
> >
> > I see to support 1GB of memory on the 440SPe yucca board the following
> > configuration changes were done.
>
> Just leave the configuration for 1 GB, and enable CONFIG_HIGHMEM
> That gives you 2 (or more) GB of RAM, with 1G as lowmwm.
>
^ permalink raw reply
* Re: [PATCH v5] qe: add ability to upload QE firmware
From: Anton Vorontsov @ 2007-12-30 0:20 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev, Andy Fleming
In-Reply-To: <4776C518.6060002@freescale.com>
On Sat, Dec 29, 2007 at 04:07:20PM -0600, Timur Tabi wrote:
> Anton Vorontsov wrote:
>
> >> + firmware {
> >> + id = "Soft-UART";
> >> + extended_modes = <0 0>;
> >> + virtual_traps = <0 0 0 0 0 0 0 0>;
> >
> > I believe using underscores for the property name is discouraged.
>
> Ugh, this one change would require me to repost all my patches. Oh
> well. I wish someone else had noticed it before I got to version 5.
Yeah, sorry about that.
But you know it, reviewing is tedious, and only few of us are doing
reviews for the code they don't use.
I've started to use this code just now, so I reviewed it on the way.
> I'll post a new version on January 7, when I get back to the office.
>
> >> +A Python program that creates firmware binaries from the header files normally
> >> +distributed by Freescale can be found on http://opensource.freescale.com.
> >
> > Hm... I didn't find it there. Could you provide more specific pointer?
>
> I'll add those files only after my patches have been applied. The
> layout may change between now and then, and I don't want to have to
> repost the firmware.
>
> >> +
> >> + /*
> >> + * If we haven't checked yet, and a driver hasn't uploaded a firmware
> >> + * yet, then check the device tree for information.
> >> + */
> >> + do {
> > ^^^^
> > This is very unusual method of error handling. You could stick
> > to gotos and lower the indentation level.
>
> Ok, I'll change it. I wasn't crazy about it after I wrote it, anyway.
>
> >> + qe = of_find_node_by_type(NULL, "qe");
> >
> > Please, add compatible "fsl,qe" matching, so this code could
> > work with new device trees.
>
> Ok, but that new design was added after I posted this patch.
>
> >> + /* Find the 'firmware' child node */
> >> + while ((fw = of_get_next_child(qe, fw)))
> >> + if (strcmp(fw->name, "firmware") == 0)
> >> + break;
> >
> > Hmm. Maybe of_find_node_by_name? Or better by compatible.
>
> What's wrong with looking for the firmware node inside the QE node?
> That's the only place it can be?
Ah, I see it now. Revoking my comment.
> >> + iprop = of_get_property(fw, "extended_modes", NULL);
> >
> > Checking for size?
>
> Ok.
Thanks,
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH v2] ucc_uart: add support for Freescale QUICCEngine UART
From: Timur Tabi @ 2007-12-29 22:10 UTC (permalink / raw)
To: avorontsov; +Cc: linuxppc-dev
In-Reply-To: <20071226165816.GB11449@localhost.localdomain>
Anton Vorontsov wrote:
>> + ucc@2400 {
>> + device_type = "serial";
>> + compatible = "ucc_uart";
>> + model = "UCC";
>
> model isn't used, is it needed at all?
I have no idea, but all the other UCC nodes have it, so I'm going to
keep it. Maybe one day we'll merge QE and CPM drivers, so this would be
useful.
>> + if (soft_uart) {
>> + struct qe_firmware_info *qe_fw_info;
>> +
>> + qe_fw_info = qe_get_firmware_info();
>> +
>> + /* Check if the firmware has been uploaded. */
>> + if (strstr(qe_fw_info->id, "Soft-UART")) {
>
> qe_fw_info used w/o NULL checking.
Ok.
> [...]
>> +
>> + qe_port->port.irq = irq_of_parse_and_map(np, 0);
>> + if (qe_port->port.irq == NO_IRQ) {
>> + dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n",
>> + qe_port->ucc_num + 1);
>> + kfree(qe_port);
>> + return -EINVAL;
>> + }
>> +
>> + np = of_find_node_by_type(NULL, "qe");
>
> Please, add "fsl,qe" compatible matching.
Ok.
>> +static struct of_platform_driver ucc_uart_of_driver = {
>> + .owner = THIS_MODULE,
>> + .name = "ucc_uart",
>
> Maybe better fsl,ucc_uart?
The CPM serial driver uses "cpm_uart" and the QE ethernet driver uses
"ucc_geth", so ucc_uart matches the pattern.
^ permalink raw reply
* Re: [PATCH v5] qe: add ability to upload QE firmware
From: Timur Tabi @ 2007-12-29 22:07 UTC (permalink / raw)
To: avorontsov; +Cc: linuxppc-dev, Andy Fleming
In-Reply-To: <20071226165004.GA11449@localhost.localdomain>
Anton Vorontsov wrote:
>> + firmware {
>> + id = "Soft-UART";
>> + extended_modes = <0 0>;
>> + virtual_traps = <0 0 0 0 0 0 0 0>;
>
> I believe using underscores for the property name is discouraged.
Ugh, this one change would require me to repost all my patches. Oh
well. I wish someone else had noticed it before I got to version 5.
I'll post a new version on January 7, when I get back to the office.
>> +A Python program that creates firmware binaries from the header files normally
>> +distributed by Freescale can be found on http://opensource.freescale.com.
>
> Hm... I didn't find it there. Could you provide more specific pointer?
I'll add those files only after my patches have been applied. The
layout may change between now and then, and I don't want to have to
repost the firmware.
>> +
>> + /*
>> + * If we haven't checked yet, and a driver hasn't uploaded a firmware
>> + * yet, then check the device tree for information.
>> + */
>> + do {
> ^^^^
> This is very unusual method of error handling. You could stick
> to gotos and lower the indentation level.
Ok, I'll change it. I wasn't crazy about it after I wrote it, anyway.
>> + qe = of_find_node_by_type(NULL, "qe");
>
> Please, add compatible "fsl,qe" matching, so this code could
> work with new device trees.
Ok, but that new design was added after I posted this patch.
>> + /* Find the 'firmware' child node */
>> + while ((fw = of_get_next_child(qe, fw)))
>> + if (strcmp(fw->name, "firmware") == 0)
>> + break;
>
> Hmm. Maybe of_find_node_by_name? Or better by compatible.
What's wrong with looking for the firmware node inside the QE node?
That's the only place it can be?
>> + iprop = of_get_property(fw, "extended_modes", NULL);
>
> Checking for size?
Ok.
^ permalink raw reply
* Re: [linux-usb-devel] [PATCH v2 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Alan Stern @ 2007-12-29 21:44 UTC (permalink / raw)
To: Grant Likely; +Cc: akpm, dbrownell, linux-usb-devel, gregkh, linuxppc-dev
In-Reply-To: <20071228235241.16003.2229.stgit@trillian.secretlab.ca>
On Fri, 28 Dec 2007, Grant Likely wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
>
> This patch adds HDC support for the Cypress c67x00 family of devices.
One minor correction:
> +static void c67x00_sched_done(unsigned long __c67x00)
> +{
> + struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
> + struct c67x00_urb_priv *urbp, *tmp;
> + struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
> + struct urb *urb;
> + int status;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&c67x00->lock, flags);
> +
> + /* Loop over the done list and give back all the urbs */
> + list_for_each_entry_safe(urbp, tmp, &c67x00->done_list, hep_node) {
> + urb = urbp->urb;
> + status = urbp->status;
> +
> + c67x00_release_urb(c67x00, urb);
> +
> + usb_hcd_unlink_urb_from_ep(hcd, urb);
> +
> + spin_unlock_irqrestore(&c67x00->lock, flags);
> + usb_hcd_giveback_urb(hcd, urb, status);
> + spin_lock_irqsave(&c67x00->lock, flags);
The giveback routine is supposed to be called with interrupts disabled.
Consequently you should use spin_unlock() and spin_lock() here, not the
_irqsave/_irqrestore variants.
> + }
> + spin_unlock_irqrestore(&c67x00->lock, flags);
> +}
Alan Stern
^ permalink raw reply
* Please pull from 'for-2.6.25' branch of 4xx tree
From: Josh Boyer @ 2007-12-29 18:16 UTC (permalink / raw)
To: paulus; +Cc: linuxppc-dev
Hi Paul,
Please pull from:
master.kernel.org:/pub/scm/linux/kernel/git/jwboyer/powerpc-4xx.git for-2.6.25
to pick up the current 4xx patches for 2.6.25. There is a large PCI
port from Ben, some new board ports from Stefan, Valentine and Hugh, and
a handful of various fixes. The total diff stat is large, but a lot of
it comes from new DTS and defconfig files.
josh
Benjamin Herrenschmidt (19):
[POWERPC] Reworking machine check handling and Fix 440/440A
[POWERPC] 4xx: Improve support for 4xx indirect DCRs
[POWERPC] 4xx: PLB to PCI-X support
[POWERPC] 4xx: PLB to PCI 2.x support
[POWERPC] 4xx: PLB to PCI Express support
[POWERPC] 4xx: PCI support for Ebony board
[POWERPC] 4xx: Add early udbg support for 40x processors
[POWERPC] 4xx: EP405 boards support for arch/powerpc
[POWERPC] 4xx: Add PCI to Walnut platform
[POWERPC] 4xx: Wire up PCI on Bamboo board
[POWERPC] 4xx: Wire up 440EP USB controller support to Bamboo board
[POWERPC] 4xx: Adds decoding of 440SPE memory size to boot wrapper library
[POWERPC] 4xx: Add mfspr/mtspr inline macros to 4xx bootwrapper
[POWERPC] 4xx: Add CPR0 accessors to boot wrapper
[POWERPC] 4xx: Rework clock probing in boot wrapper
[POWERPC] 4xx: Base support for 440SPe "Katmai" eval board
[POWERPC] 4xx: remove bogus "ranges" property in Bamboo EBC node
[POWERPC] 4xx: PCI-E Link setup improvements
[POWERPC] pci32: 4xx embedded platforms want to reassign all PCI resources
Hugh Blemings (1):
[POWERPC] 4xx: Base support for 440GX Taishan eval board
Josh Boyer (11):
[POWERPC] 4xx: Fix 440grx setup function to call 440A fixup
[POWERPC] 4xx: Include missing header
[POWERPC] 4xx: libfdt and pci fixes for Rainier
[POWERPC] 4xx: Rename CPU nodes to avoid dtc incompatibility
[POWERPC] 4xx: Update Kilauea, Rainier, and Walnut defconfigs
[POWERPC] 4xx: Mark of_bus structures as __initdata
[POWERPC] Conditionally compile e200 and e500 platforms in cputable
[POWERPC] Remove unneeded variable declarations from mpc837x_mds
[POWERPC] 4xx: Use machine_device_initcall for bus probe
[POWERPC] 4xx: Minor coding style cleanups for 4xx bootwrapper
[POWERPC] 4xx: Update defconfigs
Stefan Roese (11):
[POWERPC] 4xx: Add 440SPe revA runtime detection to PCIe
[POWERPC] 4xx: Fix TLB 0 problem with CONFIG_SERIAL_TEXT_DEBUG
[POWERPC] 4xx: Add 405EX CPU type needed for EMAC support on Kilauea
[POWERPC] 4xx: Change Kilauea dts to support new EMAC device tree properti
[POWERPC] 4xx: Add Kilauea PCIe support to dts and Kconfig
[POWERPC] 4xx: Set ibpre for 405EX in 4xx PCIe driver
[POWERPC] 4xx: Add aliases node to 4xx dts files
[POWERPC] 4xx: Change Kilauea PCIe bus ranges in dts file
[POWERPC] 4xx: Add AMCC Makalu board support to platforms/40x
[POWERPC] 4xx: Makalu dts
[POWERPC] 4xx: Makalu defconfig
Valentine Barshak (12):
[POWERPC] 4xx: 440EPx Sequoia USB OHCI DTS entry
[POWERPC] 4xx: 440GRx Rainier bootwrapper.
[POWERPC] 4xx: 440GRx Rainier DTS.
[POWERPC] 4xx: 440GRx Rainier board support.
[POWERPC] 4xx: 440GRx Rainier default config
[POWERPC] 4xx: make UIC use generic level irq handler
[POWERPC] 4xx: rework UIC cascade irq handling
[POWERPC] 4xx: Correct 440GRx machine_check callback
[POWERPC] 4xx: Add PCI entry to 440EPx Sequoia DTS.
[POWERPC] 44x: Sequoia and Rainier updates for 2.6.25
[POWERPC] 4xx: update 440EP(x)/440GR(x) identical PVR issue workaround
[POWERPC] 4xx: Add PCI entry to 440GRx Rainier DTS.
arch/powerpc/Kconfig | 1 +
arch/powerpc/Kconfig.debug | 19 +-
arch/powerpc/boot/4xx.c | 405 ++++++--
arch/powerpc/boot/4xx.h | 13 +-
arch/powerpc/boot/Makefile | 10 +-
arch/powerpc/boot/bamboo.c | 4 +-
arch/powerpc/boot/cuboot-katmai.c | 56 ++
arch/powerpc/boot/cuboot-rainier.c | 56 ++
arch/powerpc/boot/cuboot-sequoia.c | 2 +-
arch/powerpc/boot/cuboot-taishan.c | 54 +
arch/powerpc/boot/dcr.h | 54 +-
arch/powerpc/boot/dts/bamboo.dts | 62 ++-
arch/powerpc/boot/dts/ebony.dts | 53 +-
arch/powerpc/boot/dts/ep405.dts | 228 +++++
arch/powerpc/boot/dts/katmai.dts | 400 ++++++++
arch/powerpc/boot/dts/kilauea.dts | 99 ++-
arch/powerpc/boot/dts/makalu.dts | 347 +++++++
arch/powerpc/boot/dts/rainier.dts | 353 +++++++
arch/powerpc/boot/dts/sequoia.dts | 49 +-
arch/powerpc/boot/dts/taishan.dts | 383 ++++++++
arch/powerpc/boot/dts/walnut.dts | 50 +-
arch/powerpc/boot/ebony.c | 62 +--
arch/powerpc/boot/ep405.c | 74 ++
arch/powerpc/boot/reg.h | 8 +
arch/powerpc/boot/treeboot-walnut.c | 51 +-
arch/powerpc/boot/wrapper | 2 +-
arch/powerpc/configs/bamboo_defconfig | 22 +-
arch/powerpc/configs/ebony_defconfig | 8 +-
arch/powerpc/configs/ep405_defconfig | 952 ++++++++++++++++++
arch/powerpc/configs/katmai_defconfig | 790 +++++++++++++++
arch/powerpc/configs/kilauea_defconfig | 53 +-
arch/powerpc/configs/makalu_defconfig | 812 +++++++++++++++
arch/powerpc/configs/rainier_defconfig | 873 ++++++++++++++++
arch/powerpc/configs/sequoia_defconfig | 10 +-
arch/powerpc/configs/taishan_defconfig | 790 +++++++++++++++
arch/powerpc/configs/walnut_defconfig | 94 ++-
arch/powerpc/kernel/cpu_setup_44x.S | 15 +-
arch/powerpc/kernel/cputable.c | 108 ++
arch/powerpc/kernel/head_44x.S | 14 +-
arch/powerpc/kernel/head_booke.h | 2 +-
arch/powerpc/kernel/misc_32.S | 39 +
arch/powerpc/kernel/prom.c | 37 +-
arch/powerpc/kernel/traps.c | 62 +-
arch/powerpc/kernel/udbg.c | 3 +
arch/powerpc/kernel/udbg_16550.c | 33 +
arch/powerpc/platforms/40x/Kconfig | 40 +-
arch/powerpc/platforms/40x/Makefile | 2 +
arch/powerpc/platforms/40x/ep405.c | 123 +++
arch/powerpc/platforms/40x/kilauea.c | 10 +-
arch/powerpc/platforms/40x/makalu.c | 58 ++
arch/powerpc/platforms/40x/virtex.c | 5 +-
arch/powerpc/platforms/40x/walnut.c | 11 +-
arch/powerpc/platforms/44x/Kconfig | 46 +-
arch/powerpc/platforms/44x/Makefile | 5 +-
arch/powerpc/platforms/44x/bamboo.c | 11 +-
arch/powerpc/platforms/44x/ebony.c | 10 +-
arch/powerpc/platforms/44x/katmai.c | 63 ++
arch/powerpc/platforms/44x/rainier.c | 61 ++
arch/powerpc/platforms/44x/sequoia.c | 11 +-
arch/powerpc/platforms/44x/taishan.c | 73 ++
arch/powerpc/platforms/83xx/mpc837x_mds.c | 5 -
arch/powerpc/platforms/Kconfig.cputype | 1 +
arch/powerpc/sysdev/Kconfig | 8 +
arch/powerpc/sysdev/Makefile | 3 +
arch/powerpc/sysdev/ppc4xx_pci.c | 1528 +++++++++++++++++++++++++++++
arch/powerpc/sysdev/ppc4xx_pci.h | 369 +++++++
arch/powerpc/sysdev/uic.c | 117 +--
arch/ppc/kernel/head_44x.S | 2 +-
arch/ppc/kernel/traps.c | 98 ++-
arch/ppc/mm/44x_mmu.c | 51 +-
include/asm-powerpc/cputable.h | 13 +
include/asm-powerpc/dcr-native.h | 30 +-
include/asm-powerpc/dcr-regs.h | 71 ++
include/asm-powerpc/ptrace.h | 3 +-
include/asm-powerpc/reg_booke.h | 3 +-
include/asm-powerpc/udbg.h | 1 +
include/asm-ppc/mmu.h | 6 +
include/asm-ppc/reg_booke.h | 2 +-
78 files changed, 9952 insertions(+), 500 deletions(-)
^ permalink raw reply
* RE: [PATCH 1/3] [NET] phy/fixed.c: rework to not duplicate PHY layerfunctionality
From: Joakim Tjernlund @ 2007-12-29 17:13 UTC (permalink / raw)
To: 'Vitaly Bordug', 'Paul Mackerras'
Cc: netdev, 'linuxppc-dev'
In-Reply-To: <20071206225121.31080.86606.stgit@localhost.localdomain>
> -----Original Message-----
> From:
> linuxppc-dev-bounces+joakim.tjernlund=transmode.se@ozlabs.org
> [mailto:linuxppc-dev-bounces+joakim.tjernlund=transmode.se@ozl
> abs.org] On Behalf Of Vitaly Bordug
> Sent: den 6 december 2007 23:51
> To: Paul Mackerras
> Cc: netdev@vger.kernel.org; linuxppc-dev
> Subject: [PATCH 1/3] [NET] phy/fixed.c: rework to not
> duplicate PHY layerfunctionality
>
>
> With that patch fixed.c now fully emulates MDIO bus, thus no need
> to duplicate PHY layer functionality. That, in turn, drastically
> simplifies the code, and drops down line count.
>
> As an additional bonus, now there is no need to register MDIO bus
> for each PHY, all emulated PHYs placed on the platform fixed MDIO bus.
> There is also no more need to pre-allocate PHYs via .config option,
> this is all now handled dynamically.
>
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org>
> Acked-by: Jeff Garzik <jeff@garzik.org>
What happened to this patch set? Is it sitting in a tree somewhere waiting
for 2.4.25 or does it need more work?
Jocke
^ permalink raw reply
* [2.6.24 patch] Fix Cell OProfile support
From: Mathieu Desnoyers @ 2007-12-29 16:09 UTC (permalink / raw)
To: Adrian Bunk
Cc: Randy Dunlap, phil.el, linux-kernel, linuxppc-dev, paulus,
oprofile-list
In-Reply-To: <20071228185649.GC4738@stusta.de>
This patch restores the Cell OProfile support that was killed by
commit 09cadedbdc01f1a4bea1f427d4fb4642eaa19da9.
It puts it in arch/powerpc/Kconfig. Since I don't see any good reason to leave
this as a supplementary user-selectable option, it is now automatically enabled
whenever SPU_FS and OPROFILE are enabled.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
CC: Adrian Bunk <adrian.bunk@movial.fi>
CC: Randy Dunlap <randy.dunlap@oracle.com>
CC: phil.el@wanadoo.fr
CC: paulus@samba.org
CC: linuxppc-dev@ozlabs.org
CC: oprofile-list@lists.sourceforge.net
---
arch/powerpc/Kconfig | 4 ++++
1 file changed, 4 insertions(+)
Index: linux-2.6-lttng/arch/powerpc/Kconfig
===================================================================
--- linux-2.6-lttng.orig/arch/powerpc/Kconfig 2007-12-28 17:00:24.000000000 -0500
+++ linux-2.6-lttng/arch/powerpc/Kconfig 2007-12-28 17:00:39.000000000 -0500
@@ -163,6 +163,10 @@ config PPC_OF_PLATFORM_PCI
depends on PPC64 # not supported on 32 bits yet
default n
+config OPROFILE_CELL
+ def_bool y
+ depends on (SPU_FS = y && OPROFILE = m) || (SPU_FS = y && OPROFILE = y)
+
source "init/Kconfig"
source "arch/powerpc/platforms/Kconfig"
--
Mathieu Desnoyers
Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply
* Re: [PATCH v2 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Stephen Rothwell @ 2007-12-29 6:43 UTC (permalink / raw)
To: Grant Likely
Cc: akpm, dbrownell, linux-usb-devel, gregkh, linuxppc-dev, stern
In-Reply-To: <20071228235241.16003.2229.stgit@trillian.secretlab.ca>
[-- Attachment #1: Type: text/plain, Size: 1002 bytes --]
Hi Grant,
Just did a superficial look over this.
On Fri, 28 Dec 2007 16:52:41 -0700 Grant Likely <grant.likely@secretlab.ca> wrote:
>
> +++ b/drivers/usb/c67x00/c67x00-hcd.c
> +
> +#define OK(x) len = (x); break
I was going to say "URK!" but it looks like it is not used, so just
remove it.
> +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_husb_get_status(sie);
> + for (i=0; i<C67X00_PORTS; i++)
Spacing.
> +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__);
We normally put a blank line between declarations and code.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/4] USB: add Cypress c67x00 OTG controller core driver
From: Stephen Rothwell @ 2007-12-29 6:24 UTC (permalink / raw)
To: Grant Likely
Cc: akpm, dbrownell, linux-usb-devel, gregkh, linuxppc-dev, stern
In-Reply-To: <20071228235236.16003.44475.stgit@trillian.secretlab.ca>
[-- Attachment #1: Type: text/plain, Size: 740 bytes --]
Hi Grant,
Just one nit ...
On Fri, 28 Dec 2007 16:52:36 -0700 Grant Likely <grant.likely@secretlab.ca> wrote:
>
> +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 = (struct c67x00_platform_data*)pdev->dev.platform_data;
This cast is unnecessary.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH/RFC] Add support for PowerQUICC watchdog
From: Stephen Rothwell @ 2007-12-29 4:03 UTC (permalink / raw)
To: Jochen Friedrich; +Cc: Scott Wood, linuxppc-dev, wim, linux-kernel
In-Reply-To: <47751287.6000801@scram.de>
[-- Attachment #1: Type: text/plain, Size: 1180 bytes --]
Hi Jochen,
Just a couple of suggestions.
On Fri, 28 Dec 2007 16:13:11 +0100 Jochen Friedrich <jochen@scram.de> wrote:
>
> +int __init pq_wdt_early_init(void)
> +{
>
> + data = of_get_property(soc, "bus-frequency", NULL);
> + if (!data) {
> + of_node_put(soc);
> + printk(KERN_ERR "Could not find bus-frequency in soc node\n");
> + ret = -ENODEV;
> + goto out;
> + }
> + of_node_put(soc);
If you move the "of_node_put(soc)" just above the "if (!data)" then you
won't need to repeat it.
> +static struct of_platform_driver pq_wdt_driver = {
> + .owner = THIS_MODULE,
> + .name = "pq-wdt",
> + .match_table = pq_wdt_match,
> + .probe = pq_wdt_probe,
> + .remove = pq_wdt_remove,
> +};
We are removing the owner and name fields from struct of_platform_driver, so the preferred initialization looks like this:
static struct of_platform_driver pq_wdt_driver = {
.match_table = pq_wdt_match,
.probe = pq_wdt_probe,
.remove = pq_wdt_remove,
.driver = {
.name = "pq-wdt",
.owner = THIS_MODULE,
}
};
or similar.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH v2 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Grant Likely @ 2007-12-28 23:52 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev, jacmet,
stern
In-Reply-To: <20071228234537.16003.56035.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
This patch adds HDC support for the Cypress c67x00 family of devices.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-hcd.c | 392 ++++++++++++
drivers/usb/c67x00/c67x00-hcd.h | 137 ++++
drivers/usb/c67x00/c67x00-sched.c | 1205 +++++++++++++++++++++++++++++++++++++
3 files changed, 1734 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c
new file mode 100644
index 0000000..5fabd3c
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-hcd.c
@@ -0,0 +1,392 @@
+/*
+ * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
+ *
+ * Copyright (C) 2006-2007 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 ** */
+};
+
+#define OK(x) len = (x); break
+
+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_husb_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_husb_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_husb_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 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 msg)
+{
+ struct c67x00_device *c67x00 = sie->dev;
+ struct c67x00_hcd *c67x00_hcd = sie->private_data;
+ struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00_hcd);
+
+ /* Handle sie message flags */
+ if (msg) {
+ if (msg & HUSB_TDListDone)
+ c67x00_sched_kick(c67x00_hcd);
+ else
+ dev_warn(c67x00_hcd_dev(c67x00_hcd),
+ "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 (sie->dev->int_status & SOFEOP_FLG(sie->sie_num)) {
+ c67x00_ll_husb_clear_status(sie, SOF_EOP_IRQ_FLG);
+ c67x00_sched_kick(c67x00_hcd);
+ 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_start: Host Controller stop hook
+ */
+static void c67x00_hcd_stop(struct usb_hcd *hcd)
+{
+ /* Nothing todo */
+}
+
+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)
+{
+ int retval;
+ struct usb_hcd *hcd = NULL;
+ struct c67x00_hcd *c67x00;
+
+ 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);
+ INIT_LIST_HEAD(&c67x00->done_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;
+
+ spin_lock(&sie->lock);
+ sie->private_data = c67x00;
+ sie->irq = c67x00_hcd_irq;
+ spin_unlock(&sie->lock);
+
+ 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;
+ }
+
+ 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 usb_hcd *hcd;
+ struct c67x00_hcd *c67x00;
+
+ c67x00 = sie->private_data;
+ hcd = c67x00_hcd_to_hcd(c67x00);
+ c67x00_sched_stop_scheduler(c67x00);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h
new file mode 100644
index 0000000..5b35f01
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-hcd.h
@@ -0,0 +1,137 @@
+/*
+ * c67x00-hcd.h: Cypress C67X00 USB HCD
+ *
+ * Copyright (C) 2006-2007 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;
+ /* Requirement:
+ * All enabled bits in ports must have a position <= MAX_NB_HCD_PORTS
+ * (position starts counting from 1!)
+ * ( ports & ~((1<<MAX_NB_HCD_PORTS)-1) ) == 0
+ *
+ * This might be relaxed if needed by using an other indexing scheme
+ * for port[] (e.g. use wIndex instead of real port number)
+ * */
+#define MAX_NB_HCD_PORTS 2
+ 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;
+ struct list_head done_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 tasklet_struct done_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);
+}
+
+/* ---------------------------------------------------------------------
+ * 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 */
diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c
new file mode 100644
index 0000000..f60c5eb
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-sched.c
@@ -0,0 +1,1205 @@
+/*
+ * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
+ *
+ * Copyright (C) 2006-2007 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;
+ char *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
+/*
+ * These patterns are written into the c67x00 internal memory and the
+ * urb->transfer_buffer respectively in order to simplify debugging.
+ */
+#define NON_RECEIVED_PATTERN 0xac
+#define UNREAD_PATTERN 0x0c
+/* #define DEBUG_PATTERN */
+
+/**
+ * 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);
+ int i, len = td_length(td);
+ 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:");
+ for (i = 0; i < len; i++) {
+ if (!(i % 8))
+ printk("\n ");
+ printk(" 0x%02x", td->data[i]);
+ }
+ printk("\n");
+}
+#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)
+{
+ u16 temp_val;
+ temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
+ temp_val &= HOST_FRAME_MASK;
+ return temp_val;
+}
+
+/**
+ * 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 earch urbp
+ */
+ list_for_each_entry(td, &c67x00->td_list, td_list) {
+ if (urb == td->urb)
+ td->urb = NULL;
+ }
+
+ /* Discard the urb private data */
+ 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_device *udev = urb->dev;
+ struct usb_host_endpoint *hep = urb->ep;
+ struct c67x00_ep_data *ep_data;
+ struct c67x00_ep_data *prev;
+ 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 {
+ 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 = NULL;
+ 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;
+
+ /* Something went wrong; unwind the allocations */
+ 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);
+ //gcl//usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb, status);
+
+done:
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * pre: urb != NULL and c67x00 locked, urb unlocked
+ */
+static inline 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);
+ list_add_tail(&urbp->hep_node, &c67x00->done_list);
+ tasklet_schedule(&c67x00->done_tasklet);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int 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 create_td(struct c67x00_hcd *c67x00,
+ struct urb *urb,
+ char *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 (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 release_td(struct c67x00_td *td)
+{
+ list_del_init(&td->td_list);
+ kfree(td);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int 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 = create_td(c67x00, urb, td_buf, len, pid, toggle,
+ DATA_STAGE);
+ if (ret)
+ goto out; /* td wasn't created */
+ toggle ^= 1;
+ remaining -= len;
+ if (usb_pipecontrol(urb->pipe))
+ break;
+ }
+ out:
+ return ret;
+}
+
+/**
+ *
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ int ret;
+ int pid;
+
+ switch (urb->interval) {
+ default:
+ case SETUP_STAGE:
+ ret = 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 = 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 = 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 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 add_data_urb(c67x00, urb);
+ }
+ return 0;
+}
+
+static int 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 = 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");
+ 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, add_iso_urb);
+ c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, add_int_urb);
+ c67x00_fill_from_list(c67x00, PIPE_CONTROL, add_ctrl_urb);
+ c67x00_fill_from_list(c67x00, PIPE_BULK, add_data_urb);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * Get TD from C67X00
+ */
+static inline void
+c67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ c67x00_ll_hpi_read_mem_le16(c67x00->sie->dev, td->td_addr, CY_TD_SIZE,
+ (char *)td);
+
+ if (usb_pipein(td->pipe) && td_actual_bytes(td))
+ c67x00_ll_hpi_read_mem_le16(c67x00->sie->dev,
+ td_ly_base_addr(td),
+ td_actual_bytes(td), td->data);
+}
+
+/* -------------------------------------------------------------------------- */
+
+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) {
+ 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);
+ 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_hpi_write_mem_le16(c67x00->sie->dev,
+ td_ly_base_addr(td),
+ len, td->data);
+
+ c67x00_ll_hpi_write_mem_le16(c67x00->sie->dev, td->td_addr,
+ CY_TD_SIZE, (char *)td);
+}
+
+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))
+ /* URBs aren't for this frame */
+ goto out;
+ else {
+ /* TD's have been added to the frame */
+ c67x00_send_frame(c67x00);
+ goto out;
+ }
+ 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);
+}
+
+static void c67x00_sched_done(unsigned long __c67x00)
+{
+ struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+ struct c67x00_urb_priv *urbp, *tmp;
+ struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+ struct urb *urb;
+ int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+
+ /* Loop over the done list and give back all the urbs */
+ list_for_each_entry_safe(urbp, tmp, &c67x00->done_list, hep_node) {
+ urb = urbp->urb;
+ status = urbp->status;
+
+ c67x00_release_urb(c67x00, urb);
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ spin_lock_irqsave(&c67x00->lock, flags);
+ }
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+}
+
+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);
+ tasklet_init(&c67x00->done_tasklet, c67x00_sched_done,
+ (unsigned long)c67x00);
+ return 0;
+}
+
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
+{
+ tasklet_kill(&c67x00->tasklet);
+ tasklet_kill(&c67x00->done_tasklet);
+}
^ permalink raw reply related
* [PATCH v2 2/4] USB: add Cypress c67x00 OTG controller core driver
From: Grant Likely @ 2007-12-28 23:52 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev, jacmet,
stern
In-Reply-To: <20071228234537.16003.56035.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
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: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-drv.c | 278 +++++++++++++++++++++++++++++++++++++++
include/linux/usb/c67x00.h | 45 ++++++
2 files changed, 323 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c
new file mode 100644
index 0000000..be90002
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-drv.c
@@ -0,0 +1,278 @@
+/*
+ * c67x00-drv.c: Cypress C67X00 USB Common infrastructure
+ *
+ * Copyright (C) 2006-2007 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 (setup_sie)
+ * 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 struct platform_driver c67x00_driver;
+
+static void
+c67x00_setup_sie(struct c67x00_sie *sie, struct c67x00_device *dev, int sie_num)
+{
+ static unsigned int id = 0;
+
+ /* Fill in needed attributes */
+ sie->pdev = platform_device_register_simple("c67x00_sie",
+ id++, NULL, 0);
+ /* driver used in hub.c: hub_port_init */
+ sie->pdev->dev.driver = &c67x00_driver.driver;
+ spin_lock_init(&sie->lock);
+ sie->dev = dev;
+ sie->sie_num = sie_num;
+ sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
+}
+
+static void c67x00_teardown_sie(struct c67x00_sie *sie)
+{
+ sie->pdev->dev.driver = NULL;
+ platform_device_unregister(sie->pdev);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void c67x00_probe_sie(struct c67x00_sie *sie)
+{
+ switch (c67x00_sie_config(sie->dev->pdata->sie_config, sie->sie_num)) {
+ 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);
+ break;
+
+ default:
+ dev_err(sie_dev(sie),
+ "Unsupported configuration: 0x%x for SIE %d\n",
+ c67x00_sie_config(sie->dev->pdata->sie_config,
+ sie->sie_num), sie->sie_num);
+ break;
+ }
+}
+
+static void c67x00_remove_sie(struct c67x00_sie *sie)
+{
+ switch (c67x00_sie_config(sie->dev->pdata->sie_config, sie->sie_num)) {
+ case C67X00_SIE_HOST:
+ c67x00_hcd_remove(sie);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static irqreturn_t c67x00_irq(int irq, void *__dev)
+{
+ struct c67x00_device *c67x00 = __dev;
+ struct c67x00_sie *sie;
+ u16 msg;
+ int i, count = 8;
+
+ c67x00->int_status = c67x00_ll_hpi_status(c67x00);
+ if (!c67x00->int_status)
+ return IRQ_NONE;
+
+ while (c67x00->int_status != 0 && (count-- >= 0)) {
+ c67x00_ll_irq(c67x00);
+ for (i = 0; i < C67X00_SIES; i++) {
+ sie = &c67x00->sie[i];
+ msg = 0;
+ spin_lock(&sie->lock);
+ if (c67x00->int_status & SIEMSG_FLAG(sie->sie_num))
+ msg = c67x00_ll_get_siemsg(c67x00,sie->sie_num);
+ if (sie->irq)
+ sie->irq(sie, msg);
+ spin_unlock(&sie->lock);
+ }
+ c67x00->int_status = c67x00_ll_hpi_status(c67x00);
+ }
+
+ if (c67x00->int_status)
+ dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
+ "status = 0x%04x\n", c67x00->int_status);
+
+ return IRQ_HANDLED;
+}
+
+/* ---------------------------------------------------------------------
+ * Platform bus binding
+ */
+
+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 = (struct c67x00_platform_data*)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->hw_lock);
+ c67x00->hpi.regstep = pdata->hpi_regstep;
+ c67x00->pdata = pdev->dev.platform_data;
+ c67x00->pdev = pdev;
+
+ for (i = 0; i < C67X00_SIES; i++)
+ c67x00_setup_sie(&c67x00->sie[i], c67x00, i);
+
+ c67x00_ll_init(c67x00);
+ c67x00_ll_hpi_reg_init(c67x00);
+
+ dev_info(&pdev->dev, "USB OTG controller, p:0x%x, v:0x%p, irq:%i\n",
+ res->start, c67x00->hpi.base, res2->start);
+
+ 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]);
+
+ 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_teardown_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)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return platform_driver_register(&c67x00_driver);
+}
+module_init(c67x00_init);
+
+static void __exit c67x00_exit(void)
+{
+ platform_driver_unregister(&c67x00_driver);
+}
+module_exit(c67x00_exit);
+
+MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
+MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/usb/c67x00.h b/include/linux/usb/c67x00.h
new file mode 100644
index 0000000..80b1a87
--- /dev/null
+++ b/include/linux/usb/c67x00.h
@@ -0,0 +1,45 @@
+/*
+ * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip
+ *
+ * Copyright (C) 2006-2007 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 2
+
+#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 (C67X00_SIE_PERIPHERAL << 0)
+
+#define C67X00_SIE2_UNUSED (C67X00_SIE_UNUSED << 4)
+#define C67X00_SIE2_HOST (C67X00_SIE_HOST << 4)
+#define C67X00_SIE2_PERIPHERAL (C67X00_SIE_PERIPHERAL << 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 */
^ permalink raw reply related
* [PATCH v2 4/4] USB: add Cypress c67x00 OTG controller driver to Kconfig and Makefiles
From: Grant Likely @ 2007-12-28 23:52 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev, jacmet,
stern
In-Reply-To: <20071228234537.16003.56035.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
add c67x00 driver to build
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/Makefile | 2 ++
drivers/usb/c67x00/Makefile | 11 +++++++++++
drivers/usb/host/Kconfig | 12 ++++++++++++
3 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 516a640..a419c42 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -17,6 +17,8 @@ obj-$(CONFIG_USB_SL811_HCD) += host/
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/
diff --git a/drivers/usb/c67x00/Makefile b/drivers/usb/c67x00/Makefile
new file mode 100644
index 0000000..7e6eb0b
--- /dev/null
+++ b/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
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 49a91c5..49521d1 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -261,3 +261,15 @@ config USB_R8A66597_HCD
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.
^ permalink raw reply related
* [PATCH v2 0/4] Add C67x00 USB HCD driver.
From: Grant Likely @ 2007-12-28 23:52 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev, jacmet,
stern
This patch series is based on the c67x00 work done by Peter Korsgaard and
posted back in April this year. I posted my first rework to the series
on Nov 23. This version addresses the comments that I received then.
The Cypress c67x00 is an OTG controller so it can behave as either a host
or gadget controller. This series implements the HCD behaviour.
The c67x00 is found on a number of the Xilinx MLxxx series of boards.
I've been performing my testing on a board derived from the ML403.
I'd like to get this driver queued up for inclusion in 2.6.25. I think it
is ready. This series can also be pulled from my git tree here:
git://git.secretlab.ca/git/linux-2.6-virtex.git virtex-c67x00
For those who are interested in the progression from Peter's original series
to this one, the full history of my changes can be found in here:
git://git.secretlab.ca/git/linux-2.6-virtex.git virtex-c67x00-historical
Thanks,
g.
Grant Likely (4):
USB: add Cypress c67x00 low level interface code
USB: add Cypress c67x00 OTG controller core driver
USB: add Cypress c67x00 OTG controller HCD driver
USB: add Cypress c67x00 OTG controller driver to Kconfig and Makefiles
drivers/usb/Makefile | 2 +
drivers/usb/c67x00/Makefile | 11 +
drivers/usb/c67x00/c67x00-drv.c | 278 +++++++++
drivers/usb/c67x00/c67x00-hcd.c | 392 ++++++++++++
drivers/usb/c67x00/c67x00-hcd.h | 137 ++++
drivers/usb/c67x00/c67x00-ll-hpi.c | 516 +++++++++++++++
drivers/usb/c67x00/c67x00-sched.c | 1205 ++++++++++++++++++++++++++++++++++++
drivers/usb/c67x00/c67x00.h | 242 ++++++++
drivers/usb/host/Kconfig | 12 +
include/linux/usb/c67x00.h | 45 ++
10 files changed, 2840 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/c67x00/Makefile
create mode 100644 drivers/usb/c67x00/c67x00-drv.c
create mode 100644 drivers/usb/c67x00/c67x00-hcd.c
create mode 100644 drivers/usb/c67x00/c67x00-hcd.h
create mode 100644 drivers/usb/c67x00/c67x00-ll-hpi.c
create mode 100644 drivers/usb/c67x00/c67x00-sched.c
create mode 100644 drivers/usb/c67x00/c67x00.h
create mode 100644 include/linux/usb/c67x00.h
--
Grant Likely, B.Sc. P.Eng.
Secret Lab Technologies Ltd.
^ 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