[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [microblaze-uclinux] USB : Problem in porting C67x00 Driver to petalinux



Hi Childhood,

Please find attached a patch that will should cleanly to a
petalinux-v0.40 release.  It does several things:

 * back-port the C67X00 driver from a more recent kernel
 * Add the necessary platform initialiser glue

I've also uploaded an ML505 USB demo HW app to the petalogix server:

http://developer.petalogix.com/downloads/petalogix-ml505-usb.tar.gz

A couple of tricks to watch for:
 * because the USB interrupt comes in from an external port, directly
to the interrupt controller, you have to hard wire the IQR number in
arch/microblaze/platform/common/c67x00.c
 * you have to use the PERIPH_TYPE_OVERRIDES parameter in your MSS
file, to rename the EPC instance to something that the platform
initialiser can use. See the example HW design for details

Regards,

John

On Wed, Jun 10, 2009 at 1:38 AM, Childhood Hwang
<childhood.hwang@xxxxxxxxx> wrote:
> I am tring to port C67x00 driver to petalinux right now. But I
> can't make it work.
> I saw there was some discussion before, and it seem's that those
> discussions were the right thing  messing me.
>
> I found the discussion in link: http://marc.info/?t=122849175200004&r=1&w=2
>
> But there is no answer how to getover this, just that the problem was
> in different status handling for older kernel.
>
> I am quite newest to Linux.
>
> I insert a kernel module that register the platform device for USB,
> I think the interrupt works well.
> Register code is like that,:
>
> static struct resource c67x00_resources[] = {
>       [0] = {
>               .start  = 0x80800000,
>               .end    = 0x8080ffff,
>               .flags  = IORESOURCE_MEM,
>       },
>       [1] = {
>               .start  = 0,
>               .end    = 0,
>               .flags  = IORESOURCE_IRQ,
>       },
> };
>
> static struct c67x00_platform_data c67x00_data = {
>       .sie_config             = C67X00_SIE1_HOST | C67X00_SIE2_HOST,
>       .hpi_regstep            = 0x02,
> };
>
> static struct platform_device c67x00_device = {
>       .name                   = "c67x00",
>       .id                     = -1,
>       .num_resources          = ARRAY_SIZE(c67x00_resources),
>       .resource               = c67x00_resources,
>       .dev.platform_data      = &c67x00_data,
> };
>
> static int __init c67x00_device_reg_init(void)
> {
>       /* printk("<1>Hello module world.\n");
>       printk("<1>Module parameters were (0x%08x) and \"%s\"\n", myint,
>              mystr); */
>       printk("======== Call : c67x00_device_reg_init() ========\n");
>
>       /* A non 0 return means init_module failed; module can't be loaded. */
>       return platform_device_register(&c67x00_device);
> }
>
>
> static void __exit c67x00_device_reg_exit(void)
> {
>       printk(KERN_ALERT "======== Goodbye c67x00_device_reg_exit().
> ========\n");
>       platform_device_unregister(&c67x00_device);
> }
>
> module_init(c67x00_device_reg_init);
> module_exit(c67x00_device_reg_exit);
>
>
> And the log list below:
>
> # insmod /lib/modules/kernel/drivers/misc/c67x00_device_reg.ko
> ======== Call : c67x00_device_reg_init() ========
> ============ USB driver : Call c67x00_drv_probe() ============
> c67x00 c67x00: Cypress C67X00 Host Controller
> drivers/usb/core/inode.c: creating file 'devices'
> drivers/usb/core/inode.c: creating file '001'
> c67x00 c67x00: new USB bus registered, assigned bus number 1
> usb usb1: default language 0x0409
> usb usb1: new device strings: Mfr=3, Product=2, SerialNumber=1
> usb usb1: Product: Cypress C67X00 Host Controller
> usb usb1: Manufacturer: Linux 2.6.20-uc0 c67x00-hcd
> usb usb1: SerialNumber: c67x00_sie
> usb usb1: usb_probe_device
> usb usb1: configuration #1 chosen from 1 choice
> usb usb1: adding 1-0:1.0 (config #1, interface 0)
> hub 1-0:1.0: usb_probe_interface
> hub 1-0:1.0: usb_probe_interface - got id
> hub 1-0:1.0: USB hub found
> hub 1-0:1.0: 2 ports detected
> hub 1-0:1.0: standalone hub
> hub 1-0:1.0: ganged power switching
> hub 1-0:1.0: global over-current protection
> hub 1-0:1.0: power on to power good time: 100ms
> hub 1-0:1.0: local power source is good
> hub 1-0:1.0: no over-current condition exists
> hub 1-0:1.0: enabling power on all ports
> drivers/usb/core/inode.c: creating file '001'
> hub 1-0:1.0: state 7 ports 2 chg 0000 evt 0000
> c67x00 c67x00: Cypress C67X00 Host Controller
> drivers/usb/core/inode.c: creating file '002'
> c67x00 c67x00: new USB bus registered, assigned bus number 2
> usb usb2: default language 0x0409
> usb usb2: new device strings: Mfr=3, Product=2, SerialNumber=1
> usb usb2: Product: Cypress C67X00 Host Controller
> usb usb2: Manufacturer: Linux 2.6.20-uc0 c67x00-hcd
> usb usb2: SerialNumber: c67x00_sie
> usb usb2: usb_probe_device
> usb usb2: configuration #1 chosen from 1 choice
> usb usb2: adding 2-0:1.0 (config #1, interface 0)
> hub 2-0:1.0: usb_probe_interface
> hub 2-0:1.0: usb_probe_interface - got id
> hub 2-0:1.0: USB hub found
> hub 2-0:1.0: 2 ports detected
> hub 2-0:1.0: standalone hub
> hub 2-0:1.0: ganged power switching
> hub 2-0:1.0: global over-current protection
> hub 2-0:1.0: power on to power good time: 100ms
> hub 2-0:1.0: local power source is good
> hub 2-0:1.0: no over-current condition exists
> hub 2-0:1.0: enabling power on all ports
> drivers/usb/core/inode.c: creating file '001'
> hub 2-0:1.0: state 7 ports 2 chg 0000 evt 0000
>
>
>
> # hub 1-0:1.0: state 7 ports 2 chg 0000 evt 0002
> hub 1-0:1.0: port 1, status 0101, change 0001, 12 Mb/s
> hub 1-0:1.0: debounce: port 1: total 100ms stable 100ms status 0x101
> c67x00 c67x00: ClearPortFeature (0): C_RESET
> usb 1-1: new full speed USB device using c67x00 and address 2
> c67x00 c67x00: ClearPortFeature (0): C_RESET
> c67x00 c67x00: ### TIMEOUT at 0x0500
> c67x00 c67x00: urb:      0x902390cc
> c67x00 c67x00: endpoint:      0
> c67x00 c67x00: pipeout:       1
> c67x00 c67x00: ly_base_addr: 0x0700
> c67x00 c67x00: port_length:  0x0008
> c67x00 c67x00: pid_ep:         0xd0
> c67x00 c67x00: dev_addr:       0x00
> c67x00 c67x00: ctrl_reg:       0x01
> c67x00 c67x00: status:         0x14
> c67x00 c67x00: retry_cnt:      0x00
> c67x00 c67x00: residue:        0x00
> c67x00 c67x00: next_td_addr: 0x0000
> c67x00 c67x00: data:<3>usb 1-1: device not accepting address 2, error -115
> c67x00 c67x00: ClearPortFeature (0): C_RESET
> usb 1-1: new full speed USB device using c67x00 and address 3
> c67x00 c67x00: ClearPortFeature (0): C_RESET
> c67x00 c67x00: ### TIMEOUT at 0x0500
> c67x00 c67x00: urb:      0x902390cc
> c67x00 c67x00: endpoint:      0
> c67x00 c67x00: pipeout:       1
> c67x00 c67x00: ly_base_addr: 0x0700
> c67x00 c67x00: port_length:  0x0008
> c67x00 c67x00: pid_ep:         0xd0
> c67x00 c67x00: dev_addr:       0x00
> c67x00 c67x00: ctrl_reg:       0x01
> c67x00 c67x00: status:         0x14
> c67x00 c67x00: retry_cnt:      0x00
> c67x00 c67x00: residue:        0x00
> c67x00 c67x00: next_td_addr: 0x0000
> c67x00 c67x00: data:<3>usb 1-1: device not accepting address 3, error -115
> c67x00 c67x00: ClearPortFeature (0): C_RESET
> usb 1-1: new full speed USB device using c67x00 and address 4
> c67x00 c67x00: ### TIMEOUT at 0x0500
> c67x00 c67x00: urb:      0x902390cc
> c67x00 c67x00: endpoint:      0
> c67x00 c67x00: pipeout:       1
> c67x00 c67x00: ly_base_addr: 0x0700
> c67x00 c67x00: port_length:  0x0008
> c67x00 c67x00: pid_ep:         0xd0
> c67x00 c67x00: dev_addr:       0x00
> c67x00 c67x00: ctrl_reg:       0x01
> c67x00 c67x00: status:         0x14
> c67x00 c67x00: retry_cnt:      0x00
> c67x00 c67x00: residue:        0x00
> c67x00 c67x00: next_td_addr: 0x0000
> c67x00 c67x00: data:<3>usb 1-1: device not accepting address 4, error -115
> c67x00 c67x00: ClearPortFeature (0): C_RESET
> usb 1-1: new full speed USB device using c67x00 and address 5
> c67x00 c67x00: ### TIMEOUT at 0x0500
> c67x00 c67x00: urb:      0x902390cc
> c67x00 c67x00: endpoint:      0
> c67x00 c67x00: pipeout:       1
> c67x00 c67x00: ly_base_addr: 0x0700
> c67x00 c67x00: port_length:  0x0008
> c67x00 c67x00: pid_ep:         0xd0
> c67x00 c67x00: dev_addr:       0x00
> c67x00 c67x00: ctrl_reg:       0x01
> c67x00 c67x00: status:         0x14
> c67x00 c67x00: retry_cnt:      0x00
> c67x00 c67x00: residue:        0x00
> c67x00 c67x00: next_td_addr: 0x0000
> c67x00 c67x00: data:<3>usb 1-1: device not accepting address 5, error -115
> ___________________________
> microblaze-uclinux mailing list
> microblaze-uclinux@xxxxxxxxxxxxxx
> Project Home Page : http://www.itee.uq.edu.au/~jwilliams/mblaze-uclinux
> Mailing List Archive : http://www.itee.uq.edu.au/~listarch/microblaze-uclinux/
>
>



-- 
John Williams, PhD, B.Eng, B.IT
PetaLogix - Linux Solutions for a Reconfigurable World
w: www.petalogix.com  p: +61-7-30090663  f: +61-7-30090663
Index: software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile
===================================================================
--- software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile	(revision 5469)
+++ software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile	(working copy)
@@ -20,7 +20,9 @@
 platobj-$(CONFIG_INPUT_KEYPADDEV)  += xbtn_decoder.o
 platobj-$(CONFIG_PCI)  += xpci.o
 platobj-$(CONFIG_XILINX_IIC)  += xiic.o
+platobj-$(CONFIG_USB_C67X00_HCD) += c67x00.o
 
+
 obj-y += $(platobj-y) $(platobj-m)
 
 # Make these platform setup sources dependent on .config
@@ -38,3 +40,5 @@
 $(obj)/xbtn_decoder.o:	.config
 $(obj)/xpci.o:		.config
 $(obj)/xiic.o:		.config
+$(obj)/c67x00.o:	.config
+
Index: software/linux-2.6.x-petalogix/drivers/usb/Kconfig
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/Kconfig	(revision 5469)
+++ software/linux-2.6.x-petalogix/drivers/usb/Kconfig	(working copy)
@@ -12,6 +12,7 @@
 	default y if USB_ARCH_HAS_OHCI
 	default y if USB_ARCH_HAS_EHCI
 	default y if ARM				# SL-811
+	default y if MICROBLAZE
 	default PCI
 
 # many non-PCI SOC chips embed OHCI
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-drv.c
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-drv.c	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-drv.c	(revision 0)
@@ -0,0 +1,243 @@
+/*
+ * c67x00-drv.c: Cypress C67X00 USB Common infrastructure
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+/*
+ * This file implements the common infrastructure for using the c67x00.
+ * It is both the link between the platform configuration and subdrivers and
+ * the link between the common hardware parts and the subdrivers (e.g.
+ * interrupt handling).
+ *
+ * The c67x00 has 2 SIE's (serial interface engine) wich can be configured
+ * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG).
+ *
+ * Depending on the platform configuration, the SIE's are created and
+ * the corresponding subdriver is initialized (c67x00_probe_sie).
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/usb/c67x00.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+static void c67x00_probe_sie(struct c67x00_sie *sie,
+			     struct c67x00_device *dev, int sie_num)
+{
+	spin_lock_init(&sie->lock);
+	sie->dev = dev;
+	sie->sie_num = sie_num;
+	sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
+
+	switch (sie->mode) {
+	case C67X00_SIE_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",
+			sie->mode, sie->sie_num);
+		break;
+	}
+}
+
+static void c67x00_remove_sie(struct c67x00_sie *sie)
+{
+	switch (sie->mode) {
+	case C67X00_SIE_HOST:
+		c67x00_hcd_remove(sie);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static irqreturn_t c67x00_irq(int irq, void *__dev)
+{
+	struct c67x00_device *c67x00 = __dev;
+	struct c67x00_sie *sie;
+	u16 msg, int_status;
+	int i, count = 8;
+
+	int_status = c67x00_ll_hpi_status(c67x00);
+	if (!int_status)
+		return IRQ_NONE;
+
+	while (int_status != 0 && (count-- >= 0)) {
+		c67x00_ll_irq(c67x00, int_status);
+		for (i = 0; i < C67X00_SIES; i++) {
+			sie = &c67x00->sie[i];
+			msg = 0;
+			if (int_status & SIEMSG_FLG(i))
+				msg = c67x00_ll_fetch_siemsg(c67x00, i);
+			if (sie->irq)
+				sie->irq(sie, int_status, msg);
+		}
+		int_status = c67x00_ll_hpi_status(c67x00);
+	}
+
+	if (int_status)
+		dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
+			 "status = 0x%04x\n", int_status);
+
+	return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int __devinit c67x00_drv_probe(struct platform_device *pdev)
+{
+	struct c67x00_device *c67x00;
+	struct c67x00_platform_data *pdata;
+	struct resource *res, *res2;
+	int ret, i;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res2)
+		return -ENODEV;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENODEV;
+
+	c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL);
+	if (!c67x00)
+		return -ENOMEM;
+
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+				pdev->name)) {
+		dev_err(&pdev->dev, "Memory region busy\n");
+		ret = -EBUSY;
+		goto request_mem_failed;
+	}
+	c67x00->hpi.base = ioremap(res->start, res->end - res->start + 1);
+	if (!c67x00->hpi.base) {
+		dev_err(&pdev->dev, "Unable to map HPI registers\n");
+		ret = -EIO;
+		goto map_failed;
+	}
+
+	spin_lock_init(&c67x00->hpi.lock);
+	c67x00->hpi.regstep = pdata->hpi_regstep;
+	c67x00->pdata = pdev->dev.platform_data;
+	c67x00->pdev = pdev;
+
+	c67x00_ll_init(c67x00);
+	c67x00_ll_hpi_reg_init(c67x00);
+
+	ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot claim IRQ\n");
+		goto request_irq_failed;
+	}
+
+	ret = c67x00_ll_reset(c67x00);
+	if (ret) {
+		dev_err(&pdev->dev, "Device reset failed\n");
+		goto reset_failed;
+	}
+
+	for (i = 0; i < C67X00_SIES; i++)
+		c67x00_probe_sie(&c67x00->sie[i], c67x00, i);
+
+	platform_set_drvdata(pdev, c67x00);
+
+	return 0;
+
+ reset_failed:
+	free_irq(res2->start, c67x00);
+ request_irq_failed:
+	iounmap(c67x00->hpi.base);
+ map_failed:
+	release_mem_region(res->start, res->end - res->start + 1);
+ request_mem_failed:
+	kfree(c67x00);
+
+	return ret;
+}
+
+static int __devexit c67x00_drv_remove(struct platform_device *pdev)
+{
+	struct c67x00_device *c67x00 = platform_get_drvdata(pdev);
+	struct resource *res;
+	int i;
+
+	for (i = 0; i < C67X00_SIES; i++)
+		c67x00_remove_sie(&c67x00->sie[i]);
+
+	c67x00_ll_release(c67x00);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res)
+		free_irq(res->start, c67x00);
+
+	iounmap(c67x00->hpi.base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, res->end - res->start + 1);
+
+	kfree(c67x00);
+
+	return 0;
+}
+
+static struct platform_driver c67x00_driver = {
+	.probe	= c67x00_drv_probe,
+	.remove	= __devexit_p(c67x00_drv_remove),
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name = "c67x00",
+	},
+};
+MODULE_ALIAS("platform:c67x00");
+
+static int __init c67x00_init(void)
+{
+	return platform_driver_register(&c67x00_driver);
+}
+
+static void __exit c67x00_exit(void)
+{
+	platform_driver_unregister(&c67x00_driver);
+}
+
+module_init(c67x00_init);
+module_exit(c67x00_exit);
+
+MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
+MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
+MODULE_LICENSE("GPL");
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-hcd.c
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-hcd.c	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-hcd.c	(revision 0)
@@ -0,0 +1,412 @@
+/*
+ * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+/* --------------------------------------------------------------------------
+ * Root Hub Support
+ */
+
+static __u8 c67x00_hub_des[] = {
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,			/*   (per-port OC, no power switching) */
+	0x32,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; ** 7 Ports max ** */
+	0xff,			/*  __u8  PortPwrCtrlMask; ** 7 ports max ** */
+};
+
+static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
+{
+	struct c67x00_hcd *c67x00 = sie->private_data;
+	unsigned long flags;
+
+	c67x00_ll_husb_reset(sie, port);
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+	c67x00_ll_husb_reset_port(sie, port);
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	c67x00_ll_set_husb_eot(sie->dev, DEFAULT_EOT);
+}
+
+static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	struct c67x00_sie *sie = c67x00->sie;
+	u16 status;
+	int i;
+
+	*buf = 0;
+	status = c67x00_ll_usb_get_status(sie);
+	for (i = 0; i < C67X00_PORTS; i++)
+		if (status & PORT_CONNECT_CHANGE(i))
+			*buf |= (1 << i);
+
+	/* bit 0 denotes hub change, b1..n port change */
+	*buf <<= 1;
+
+	return !!*buf;
+}
+
+static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+			      u16 wIndex, char *buf, u16 wLength)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	struct c67x00_sie *sie = c67x00->sie;
+	u16 status, usb_status;
+	int len = 0;
+	unsigned int port = wIndex-1;
+	u16 wPortChange, wPortStatus;
+
+	switch (typeReq) {
+
+	case GetHubStatus:
+		*(__le32 *) buf = cpu_to_le32(0);
+		len = 4;		/* hub power */
+		break;
+
+	case GetPortStatus:
+		if (wIndex > C67X00_PORTS)
+			return -EPIPE;
+
+		status = c67x00_ll_usb_get_status(sie);
+		usb_status = c67x00_ll_get_usb_ctl(sie);
+
+		wPortChange = 0;
+		if (status & PORT_CONNECT_CHANGE(port))
+			wPortChange |= USB_PORT_STAT_C_CONNECTION;
+
+		wPortStatus = USB_PORT_STAT_POWER;
+		if (!(status & PORT_SE0_STATUS(port)))
+			wPortStatus |= USB_PORT_STAT_CONNECTION;
+		if (usb_status & LOW_SPEED_PORT(port)) {
+			wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+			c67x00->low_speed_ports |= (1 << port);
+		} else
+			c67x00->low_speed_ports &= ~(1 << port);
+
+		if (usb_status & SOF_EOP_EN(port))
+			wPortStatus |= USB_PORT_STAT_ENABLE;
+
+		*(__le16 *) buf = cpu_to_le16(wPortStatus);
+		*(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
+		len = 4;
+		break;
+
+	case SetHubFeature:	/* We don't implement these */
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+		case C_HUB_LOCAL_POWER:
+			len = 0;
+			break;
+
+		default:
+			return -EPIPE;
+		}
+		break;
+
+	case SetPortFeature:
+		if (wIndex > C67X00_PORTS)
+			return -EPIPE;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"SetPortFeature %d (SUSPEND)\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_RESET:
+			c67x00_hub_reset_host_port(sie, port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_POWER:
+			/* Power always enabled */
+			len = 0;
+			break;
+
+		default:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"%s: SetPortFeature %d (0x%04x) Error!\n",
+				__func__, port, wValue);
+			return -EPIPE;
+		}
+		break;
+
+	case ClearPortFeature:
+		if (wIndex > C67X00_PORTS)
+			return -EPIPE;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			/* Reset the port so that the c67x00 also notices the
+			 * disconnect */
+			c67x00_hub_reset_host_port(sie, port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_ENABLE:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): C_ENABLE\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_SUSPEND:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): SUSPEND\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_SUSPEND:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): C_SUSPEND\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_POWER:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): POWER\n", port);
+			return -EPIPE;
+
+		case USB_PORT_FEAT_C_CONNECTION:
+			c67x00_ll_usb_clear_status(sie,
+						   PORT_CONNECT_CHANGE(port));
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): OVER_CURRENT\n", port);
+			len = 0;
+			break;
+
+		case USB_PORT_FEAT_C_RESET:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"ClearPortFeature (%d): C_RESET\n", port);
+			len = 0;
+			break;
+
+		default:
+			dev_dbg(c67x00_hcd_dev(c67x00),
+				"%s: ClearPortFeature %d (0x%04x) Error!\n",
+				__func__, port, wValue);
+			return -EPIPE;
+		}
+		break;
+
+	case GetHubDescriptor:
+		len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
+		memcpy(buf, c67x00_hub_des, len);
+		break;
+
+	default:
+		dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __func__);
+		return -EPIPE;
+	}
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Main part of host controller driver
+ */
+
+/**
+ * c67x00_hcd_irq
+ *
+ * This function is called from the interrupt handler in c67x00-drv.c
+ */
+static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
+{
+	struct c67x00_hcd *c67x00 = sie->private_data;
+	struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+
+	/* Handle sie message flags */
+	if (msg) {
+		if (msg & HUSB_TDListDone)
+			c67x00_sched_kick(c67x00);
+		else
+			dev_warn(c67x00_hcd_dev(c67x00),
+				 "Unknown SIE msg flag(s): 0x%04x\n", msg);
+	}
+
+	if (unlikely(hcd->state == HC_STATE_HALT))
+		return;
+
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+		return;
+
+	/* Handle Start of frame events */
+	if (int_status & SOFEOP_FLG(sie->sie_num)) {
+		c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
+		c67x00_sched_kick(c67x00);
+		set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+	}
+}
+
+/**
+ * c67x00_hcd_start: Host controller start hook
+ */
+static int c67x00_hcd_start(struct usb_hcd *hcd)
+{
+	hcd->uses_new_polling = 1;
+	hcd->state = HC_STATE_RUNNING;
+	hcd->poll_rh = 1;
+
+	return 0;
+}
+
+/**
+ * c67x00_hcd_stop: Host controller stop hook
+ */
+static void c67x00_hcd_stop(struct usb_hcd *hcd)
+{
+	/* Nothing to do */
+}
+
+static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	u16 temp_val;
+
+	dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __func__);
+	temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
+	temp_val &= HOST_FRAME_MASK;
+	return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
+}
+
+static struct hc_driver c67x00_hc_driver = {
+	.description	= "c67x00-hcd",
+	.product_desc	= "Cypress C67X00 Host Controller",
+	.hcd_priv_size	= sizeof(struct c67x00_hcd),
+	.flags		= HCD_USB11 | HCD_MEMORY,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.start		= c67x00_hcd_start,
+	.stop		= c67x00_hcd_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue	= c67x00_urb_enqueue,
+	.urb_dequeue	= c67x00_urb_dequeue,
+	.endpoint_disable = c67x00_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number = c67x00_hcd_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data = c67x00_hub_status_data,
+	.hub_control	= c67x00_hub_control,
+};
+
+/* ---------------------------------------------------------------------
+ * Setup/Teardown routines
+ */
+
+int c67x00_hcd_probe(struct c67x00_sie *sie)
+{
+	struct c67x00_hcd *c67x00;
+	struct usb_hcd *hcd;
+	unsigned long flags;
+	int retval;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	hcd = usb_create_hcd(&c67x00_hc_driver, sie_dev(sie), "c67x00_sie");
+	if (!hcd) {
+		retval = -ENOMEM;
+		goto err0;
+	}
+	c67x00 = hcd_to_c67x00_hcd(hcd);
+
+	spin_lock_init(&c67x00->lock);
+	c67x00->sie = sie;
+
+	INIT_LIST_HEAD(&c67x00->list[PIPE_ISOCHRONOUS]);
+	INIT_LIST_HEAD(&c67x00->list[PIPE_INTERRUPT]);
+	INIT_LIST_HEAD(&c67x00->list[PIPE_CONTROL]);
+	INIT_LIST_HEAD(&c67x00->list[PIPE_BULK]);
+	c67x00->urb_count = 0;
+	INIT_LIST_HEAD(&c67x00->td_list);
+	c67x00->td_base_addr = CY_HCD_BUF_ADDR + SIE_TD_OFFSET(sie->sie_num);
+	c67x00->buf_base_addr = CY_HCD_BUF_ADDR + SIE_BUF_OFFSET(sie->sie_num);
+	c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+
+	c67x00_ll_husb_init_host_port(sie);
+
+	init_completion(&c67x00->endpoint_disable);
+	retval = c67x00_sched_start_scheduler(c67x00);
+	if (retval)
+		goto err1;
+
+	retval = usb_add_hcd(hcd, 0, 0);
+	if (retval) {
+		dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
+			__func__, retval);
+		goto err2;
+	}
+
+	spin_lock_irqsave(&sie->lock, flags);
+	sie->private_data = c67x00;
+	sie->irq = c67x00_hcd_irq;
+	spin_unlock_irqrestore(&sie->lock, flags);
+
+	return retval;
+
+ err2:
+	c67x00_sched_stop_scheduler(c67x00);
+ err1:
+	usb_put_hcd(hcd);
+ err0:
+	return retval;
+}
+
+/* may be called with controller, bus, and devices active */
+void c67x00_hcd_remove(struct c67x00_sie *sie)
+{
+	struct c67x00_hcd *c67x00 = sie->private_data;
+	struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+
+	c67x00_sched_stop_scheduler(c67x00);
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+}
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-hcd.h
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-hcd.h	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-hcd.h	(revision 0)
@@ -0,0 +1,137 @@
+/*
+ * c67x00-hcd.h: Cypress C67X00 USB HCD
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#ifndef _USB_C67X00_HCD_H
+#define _USB_C67X00_HCD_H
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include "../core/hcd.h"
+#include "c67x00.h"
+
+/*
+ * The following parameters depend on the CPU speed, bus speed, ...
+ * These can be tuned for specific use cases, e.g. if isochronous transfers
+ * are very important, bandwith can be sacrificed to guarantee that the
+ * 1ms deadline will be met.
+ * If bulk transfers are important, the MAX_FRAME_BW can be increased,
+ * but some (or many) isochronous deadlines might not be met.
+ *
+ * The values are specified in bittime.
+ */
+
+/*
+ * The current implementation switches between _STD (default) and _ISO (when
+ * isochronous transfers are scheduled), in order to optimize the throughput
+ * in normal cicrumstances, but also provide good isochronous behaviour.
+ *
+ * Bandwidth is described in bit time so with a 12MHz USB clock and 1ms
+ * frames; there are 12000 bit times per frame.
+ */
+
+#define TOTAL_FRAME_BW		12000
+#define DEFAULT_EOT		2250
+
+#define MAX_FRAME_BW_STD	(TOTAL_FRAME_BW - DEFAULT_EOT)
+#define MAX_FRAME_BW_ISO	2400
+
+/*
+ * Periodic transfers may only use 90% of the full frame, but as
+ * we currently don't even use 90% of the full frame, we may
+ * use the full usable time for periodic transfers.
+ */
+#define MAX_PERIODIC_BW(full_bw)	full_bw
+
+/* -------------------------------------------------------------------------- */
+
+struct c67x00_hcd {
+	spinlock_t lock;
+	struct c67x00_sie *sie;
+	unsigned int low_speed_ports;	/* bitmask of low speed ports */
+	unsigned int urb_count;
+	unsigned int urb_iso_count;
+
+	struct list_head list[4];	/* iso, int, ctrl, bulk */
+#if PIPE_BULK != 3
+#error "Sanity check failed, this code presumes PIPE_... to range from 0 to 3"
+#endif
+
+	/* USB bandwidth allocated to td_list */
+	int bandwidth_allocated;
+	/* USB bandwidth allocated for isoc/int transfer */
+	int periodic_bw_allocated;
+	struct list_head td_list;
+	int max_frame_bw;
+
+	u16 td_base_addr;
+	u16 buf_base_addr;
+	u16 next_td_addr;
+	u16 next_buf_addr;
+
+	struct tasklet_struct tasklet;
+
+	struct completion endpoint_disable;
+
+	u16 current_frame;
+	u16 last_frame;
+};
+
+static inline struct c67x00_hcd *hcd_to_c67x00_hcd(struct usb_hcd *hcd)
+{
+	return (struct c67x00_hcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *c67x00_hcd_to_hcd(struct c67x00_hcd *c67x00)
+{
+	return container_of((void *)c67x00, struct usb_hcd, hcd_priv);
+}
+
+/* ---------------------------------------------------------------------
+ * Functions used by c67x00-drv
+ */
+
+int c67x00_hcd_probe(struct c67x00_sie *sie);
+void c67x00_hcd_remove(struct c67x00_sie *sie);
+
+/* ---------------------------------------------------------------------
+ * Transfer Descriptor scheduling functions
+ */
+//int c67x00_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
+int c67x00_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
+			struct urb *urb, gfp_t mem_flags);
+
+//int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+void c67x00_endpoint_disable(struct usb_hcd *hcd,
+			     struct usb_host_endpoint *ep);
+
+void c67x00_hcd_msg_received(struct c67x00_sie *sie, u16 msg);
+void c67x00_sched_kick(struct c67x00_hcd *c67x00);
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00);
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00);
+
+#define c67x00_hcd_dev(x)	(c67x00_hcd_to_hcd(x)->self.controller)
+
+#endif				/* _USB_C67X00_HCD_H */
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-ll-hpi.c
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-ll-hpi.c	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-ll-hpi.c	(revision 0)
@@ -0,0 +1,481 @@
+/*
+ * c67x00-ll-hpi.c: Cypress C67X00 USB Low level interface using HPI
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/usb/c67x00.h>
+#include "c67x00.h"
+
+#define COMM_REGS 14
+
+struct c67x00_lcp_int_data {
+	u16 regs[COMM_REGS];
+};
+
+/* -------------------------------------------------------------------------- */
+/* Interface definitions */
+
+#define COMM_ACK			0x0FED
+#define COMM_NAK			0xDEAD
+
+#define COMM_RESET			0xFA50
+#define COMM_EXEC_INT			0xCE01
+#define COMM_INT_NUM			0x01C2
+
+/* Registers 0 to COMM_REGS-1 */
+#define COMM_R(x)			(0x01C4 + 2 * (x))
+
+#define HUSB_SIE_pCurrentTDPtr(x)	((x) ? 0x01B2 : 0x01B0)
+#define HUSB_SIE_pTDListDone_Sem(x)	((x) ? 0x01B8 : 0x01B6)
+#define HUSB_pEOT			0x01B4
+
+/* Software interrupts */
+/* 114, 115: */
+#define HUSB_SIE_INIT_INT(x)		((x) ? 0x0073 : 0x0072)
+#define HUSB_RESET_INT			0x0074
+
+#define SUSB_INIT_INT			0x0071
+#define SUSB_INIT_INT_LOC		(SUSB_INIT_INT * 2)
+
+/* -----------------------------------------------------------------------
+ * HPI implementation
+ *
+ * The c67x00 chip also support control via SPI or HSS serial
+ * interfaces.  However, this driver assumes that register access can
+ * be performed from IRQ context.  While this is a safe assuption with
+ * the HPI interface, it is not true for the serial interfaces.
+ */
+
+/* HPI registers */
+#define HPI_DATA	0
+#define HPI_MAILBOX	1
+#define HPI_ADDR	2
+#define HPI_STATUS	3
+
+static inline u16 hpi_read_reg(struct c67x00_device *dev, int reg)
+{
+	return __raw_readw(dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline void hpi_write_reg(struct c67x00_device *dev, int reg, u16 value)
+{
+	__raw_writew(value, dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline u16 hpi_read_word_nolock(struct c67x00_device *dev, u16 reg)
+{
+	hpi_write_reg(dev, HPI_ADDR, reg);
+	return hpi_read_reg(dev, HPI_DATA);
+}
+
+static u16 hpi_read_word(struct c67x00_device *dev, u16 reg)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_word_nolock(dev, reg);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+static void hpi_write_word_nolock(struct c67x00_device *dev, u16 reg, u16 value)
+{
+	hpi_write_reg(dev, HPI_ADDR, reg);
+	hpi_write_reg(dev, HPI_DATA, value);
+}
+
+static void hpi_write_word(struct c67x00_device *dev, u16 reg, u16 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	hpi_write_word_nolock(dev, reg, value);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static void hpi_write_words_le16(struct c67x00_device *dev, u16 addr,
+				 __le16 *data, u16 count)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+
+	hpi_write_reg(dev, HPI_ADDR, addr);
+	for (i = 0; i < count; i++)
+		hpi_write_reg(dev, HPI_DATA, le16_to_cpu(*data++));
+
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static void hpi_read_words_le16(struct c67x00_device *dev, u16 addr,
+				__le16 *data, u16 count)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	hpi_write_reg(dev, HPI_ADDR, addr);
+	for (i = 0; i < count; i++)
+		*data++ = cpu_to_le16(hpi_read_reg(dev, HPI_DATA));
+
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+static void hpi_set_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_word_nolock(dev, reg);
+	hpi_write_word_nolock(dev, reg, value | mask);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+static void hpi_clear_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_word_nolock(dev, reg);
+	hpi_write_word_nolock(dev, reg, value & ~mask);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+}
+
+static u16 hpi_recv_mbox(struct c67x00_device *dev)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_reg(dev, HPI_MAILBOX);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+static u16 hpi_send_mbox(struct c67x00_device *dev, u16 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	hpi_write_reg(dev, HPI_MAILBOX, value);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+u16 c67x00_ll_hpi_status(struct c67x00_device *dev)
+{
+	u16 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hpi.lock, flags);
+	value = hpi_read_reg(dev, HPI_STATUS);
+	spin_unlock_irqrestore(&dev->hpi.lock, flags);
+
+	return value;
+}
+
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev)
+{
+	int i;
+
+	hpi_recv_mbox(dev);
+	c67x00_ll_hpi_status(dev);
+	hpi_write_word(dev, HPI_IRQ_ROUTING_REG, 0);
+
+	for (i = 0; i < C67X00_SIES; i++) {
+		hpi_write_word(dev, SIEMSG_REG(i), 0);
+		hpi_read_word(dev, SIEMSG_REG(i));
+	}
+}
+
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie)
+{
+	hpi_set_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+		     SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie)
+{
+	hpi_clear_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+		       SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+/* Transactions */
+
+static inline u16 ll_recv_msg(struct c67x00_device *dev)
+{
+	u16 res;
+
+	res = wait_for_completion_timeout(&dev->hpi.lcp.msg_received, 5 * HZ);
+	WARN_ON(!res);
+
+	return (res == 0) ? -EIO : 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* General functions */
+
+u16 c67x00_ll_fetch_siemsg(struct c67x00_device *dev, int sie_num)
+{
+	u16 val;
+
+	val = hpi_read_word(dev, SIEMSG_REG(sie_num));
+	/* clear register to allow next message */
+	hpi_write_word(dev, SIEMSG_REG(sie_num), 0);
+
+	return val;
+}
+
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num));
+}
+
+/**
+ * c67x00_ll_usb_clear_status - clear the USB status bits
+ */
+void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits)
+{
+	hpi_write_word(sie->dev, USB_STAT_REG(sie->sie_num), bits);
+}
+
+u16 c67x00_ll_usb_get_status(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, USB_STAT_REG(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int c67x00_comm_exec_int(struct c67x00_device *dev, u16 nr,
+				struct c67x00_lcp_int_data *data)
+{
+	int i, rc;
+
+	mutex_lock(&dev->hpi.lcp.mutex);
+	hpi_write_word(dev, COMM_INT_NUM, nr);
+	for (i = 0; i < COMM_REGS; i++)
+		hpi_write_word(dev, COMM_R(i), data->regs[i]);
+	hpi_send_mbox(dev, COMM_EXEC_INT);
+	rc = ll_recv_msg(dev);
+	mutex_unlock(&dev->hpi.lcp.mutex);
+
+	return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+/* Host specific functions */
+
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value)
+{
+	mutex_lock(&dev->hpi.lcp.mutex);
+	hpi_write_word(dev, HUSB_pEOT, value);
+	mutex_unlock(&dev->hpi.lcp.mutex);
+}
+
+static inline void c67x00_ll_husb_sie_init(struct c67x00_sie *sie)
+{
+	struct c67x00_device *dev = sie->dev;
+	struct c67x00_lcp_int_data data;
+	int rc;
+
+	rc = c67x00_comm_exec_int(dev, HUSB_SIE_INIT_INT(sie->sie_num), &data);
+	BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port)
+{
+	struct c67x00_device *dev = sie->dev;
+	struct c67x00_lcp_int_data data;
+	int rc;
+
+	data.regs[0] = 50;	/* Reset USB port for 50ms */
+	data.regs[1] = port | (sie->sie_num << 1);
+	rc = c67x00_comm_exec_int(dev, HUSB_RESET_INT, &data);
+	BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr)
+{
+	hpi_write_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num), addr);
+}
+
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num));
+}
+
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie *sie)
+{
+	return hpi_read_word(sie->dev, HOST_FRAME_REG(sie->sie_num));
+}
+
+void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie)
+{
+	/* Set port into host mode */
+	hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), HOST_MODE);
+	c67x00_ll_husb_sie_init(sie);
+	/* Clear interrupts */
+	c67x00_ll_usb_clear_status(sie, HOST_STAT_MASK);
+	/* Check */
+	if (!(hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num)) & HOST_MODE))
+		dev_warn(sie_dev(sie),
+			 "SIE %d not set to host mode\n", sie->sie_num);
+}
+
+void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port)
+{
+	/* Clear connect change */
+	c67x00_ll_usb_clear_status(sie, PORT_CONNECT_CHANGE(port));
+
+	/* Enable interrupts */
+	hpi_set_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+		     SOFEOP_TO_CPU_EN(sie->sie_num));
+	hpi_set_bits(sie->dev, HOST_IRQ_EN_REG(sie->sie_num),
+		     SOF_EOP_IRQ_EN | DONE_IRQ_EN);
+
+	/* Enable pull down transistors */
+	hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), PORT_RES_EN(port));
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status)
+{
+	if ((int_status & MBX_OUT_FLG) == 0)
+		return;
+
+	dev->hpi.lcp.last_msg = hpi_recv_mbox(dev);
+	complete(&dev->hpi.lcp.msg_received);
+}
+
+/* -------------------------------------------------------------------------- */
+
+int c67x00_ll_reset(struct c67x00_device *dev)
+{
+	int rc;
+
+	mutex_lock(&dev->hpi.lcp.mutex);
+	hpi_send_mbox(dev, COMM_RESET);
+	rc = ll_recv_msg(dev);
+	mutex_unlock(&dev->hpi.lcp.mutex);
+
+	return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_ll_write_mem_le16 - write into c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_write_mem_le16(struct c67x00_device *dev, u16 addr,
+			      void *data, int len)
+{
+	u8 *buf = data;
+
+	/* Sanity check */
+	if (addr + len > 0xffff) {
+		dev_err(&dev->pdev->dev,
+			"Trying to write beyond writable region!\n");
+		return;
+	}
+
+	if (addr & 0x01) {
+		/* unaligned access */
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr - 1);
+		tmp = (tmp & 0x00ff) | (*buf++ << 8);
+		hpi_write_word(dev, addr - 1, tmp);
+		addr++;
+		len--;
+	}
+
+	hpi_write_words_le16(dev, addr, (__le16 *)buf, len / 2);
+	buf += len & ~0x01;
+	addr += len & ~0x01;
+	len &= 0x01;
+
+	if (len) {
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr);
+		tmp = (tmp & 0xff00) | *buf;
+		hpi_write_word(dev, addr, tmp);
+	}
+}
+
+/**
+ * c67x00_ll_read_mem_le16 - read from c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
+			     void *data, int len)
+{
+	u8 *buf = data;
+
+	if (addr & 0x01) {
+		/* unaligned access */
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr - 1);
+		*buf++ = (tmp >> 8) & 0x00ff;
+		addr++;
+		len--;
+	}
+
+	hpi_read_words_le16(dev, addr, (__le16 *)buf, len / 2);
+	buf += len & ~0x01;
+	addr += len & ~0x01;
+	len &= 0x01;
+
+	if (len) {
+		u16 tmp;
+		tmp = hpi_read_word(dev, addr);
+		*buf = tmp & 0x00ff;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_init(struct c67x00_device *dev)
+{
+	mutex_init(&dev->hpi.lcp.mutex);
+	init_completion(&dev->hpi.lcp.msg_received);
+}
+
+void c67x00_ll_release(struct c67x00_device *dev)
+{
+}
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-sched.c
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-sched.c	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00-sched.c	(revision 0)
@@ -0,0 +1,1211 @@
+/*
+ * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+//#define DEBUG
+
+/*
+ * These are the stages for a control urb, they are kept
+ * in both urb->interval and td->privdata.
+ */
+#define SETUP_STAGE		0
+#define DATA_STAGE		1
+#define STATUS_STAGE		2
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct c67x00_ep_data: Host endpoint data structure
+ */
+struct c67x00_ep_data {
+	struct list_head queue;
+	struct list_head node;
+	struct usb_host_endpoint *hep;
+	struct usb_device *dev;
+	u16 next_frame;		/* For int/isoc transactions */
+};
+
+/**
+ * struct c67x00_td
+ *
+ * Hardware parts are little endiannes, SW in CPU endianess.
+ */
+struct c67x00_td {
+	/* HW specific part */
+	__le16 ly_base_addr;	/* Bytes 0-1 */
+	__le16 port_length;	/* Bytes 2-3 */
+	u8 pid_ep;		/* Byte 4 */
+	u8 dev_addr;		/* Byte 5 */
+	u8 ctrl_reg;		/* Byte 6 */
+	u8 status;		/* Byte 7 */
+	u8 retry_cnt;		/* Byte 8 */
+#define TT_OFFSET		2
+#define TT_CONTROL		0
+#define TT_ISOCHRONOUS		1
+#define TT_BULK			2
+#define TT_INTERRUPT		3
+	u8 residue;		/* Byte 9 */
+	__le16 next_td_addr;	/* Bytes 10-11 */
+	/* SW part */
+	struct list_head td_list;
+	u16 td_addr;
+	void *data;
+	struct urb *urb;
+	unsigned long privdata;
+
+	/* These are needed for handling the toggle bits:
+	 * an urb can be dequeued while a td is in progress
+	 * after checking the td, the toggle bit might need to
+	 * be fixed */
+	struct c67x00_ep_data *ep_data;
+	unsigned int pipe;
+};
+
+struct c67x00_urb_priv {
+	struct list_head hep_node;
+	struct urb *urb;
+	int port;
+	int cnt;		/* packet number for isoc */
+	int status;
+	struct c67x00_ep_data *ep_data;
+};
+
+#define td_udev(td)	((td)->ep_data->dev)
+
+#define CY_TD_SIZE		12
+
+#define TD_PIDEP_OFFSET		0x04
+#define TD_PIDEPMASK_PID	0xF0
+#define TD_PIDEPMASK_EP		0x0F
+#define TD_PORTLENMASK_DL	0x02FF
+#define TD_PORTLENMASK_PN	0xC000
+
+#define TD_STATUS_OFFSET	0x07
+#define TD_STATUSMASK_ACK	0x01
+#define TD_STATUSMASK_ERR	0x02
+#define TD_STATUSMASK_TMOUT	0x04
+#define TD_STATUSMASK_SEQ	0x08
+#define TD_STATUSMASK_SETUP	0x10
+#define TD_STATUSMASK_OVF	0x20
+#define TD_STATUSMASK_NAK	0x40
+#define TD_STATUSMASK_STALL	0x80
+
+#define TD_ERROR_MASK		(TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \
+				 TD_STATUSMASK_STALL)
+
+#define TD_RETRYCNT_OFFSET	0x08
+#define TD_RETRYCNTMASK_ACT_FLG	0x10
+#define TD_RETRYCNTMASK_TX_TYPE	0x0C
+#define TD_RETRYCNTMASK_RTY_CNT	0x03
+
+#define TD_RESIDUE_OVERFLOW	0x80
+
+#define TD_PID_IN		0x90
+
+/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */
+#define td_residue(td)		((__s8)(td->residue))
+#define td_ly_base_addr(td)	(__le16_to_cpu((td)->ly_base_addr))
+#define td_port_length(td)	(__le16_to_cpu((td)->port_length))
+#define td_next_td_addr(td)	(__le16_to_cpu((td)->next_td_addr))
+
+#define td_active(td)		((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG)
+#define td_length(td)		(td_port_length(td) & TD_PORTLENMASK_DL)
+
+#define td_sequence_ok(td)	(!td->status || \
+				 (!(td->status & TD_STATUSMASK_SEQ) ==	\
+				  !(td->ctrl_reg & SEQ_SEL)))
+
+#define td_acked(td)		(!td->status || \
+				 (td->status & TD_STATUSMASK_ACK))
+#define td_actual_bytes(td)	(td_length(td) - td_residue(td))
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef DEBUG
+
+/**
+ * dbg_td - Dump the contents of the TD
+ */
+static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg)
+{
+	struct device *dev = c67x00_hcd_dev(c67x00);
+	int i, len = td_length(td);
+	unsigned char *k;
+	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     ");
+		k = (td->data + i);
+		printk("  0x%02x", *k);
+	}
+	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)
+{
+	return c67x00_ll_husb_get_frame(c67x00->sie) & HOST_FRAME_MASK;
+}
+
+/**
+ * frame_add
+ * Software wraparound for framenumbers.
+ */
+static inline u16 frame_add(u16 a, u16 b)
+{
+	return (a + b) & HOST_FRAME_MASK;
+}
+
+/**
+ * frame_after - is frame a after frame b
+ */
+static inline int frame_after(u16 a, u16 b)
+{
+	return ((HOST_FRAME_MASK + a - b) & HOST_FRAME_MASK) <
+	    (HOST_FRAME_MASK / 2);
+}
+
+/**
+ * frame_after_eq - is frame a after or equal to frame b
+ */
+static inline int frame_after_eq(u16 a, u16 b)
+{
+	return ((HOST_FRAME_MASK + 1 + a - b) & HOST_FRAME_MASK) <
+	    (HOST_FRAME_MASK / 2);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_release_urb - remove link from all tds to this urb
+ * Disconnects the urb from it's tds, so that it can be given back.
+ * pre: urb->hcpriv != NULL
+ */
+static void c67x00_release_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct c67x00_td *td;
+	struct c67x00_urb_priv *urbp;
+
+	BUG_ON(!urb);
+
+	c67x00->urb_count--;
+
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		c67x00->urb_iso_count--;
+		if (c67x00->urb_iso_count == 0)
+			c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+	}
+
+	/* TODO this might be not so efficient when we've got many urbs!
+	 * Alternatives:
+	 *   * only clear when needed
+	 *   * keep a list of tds with each urbp
+	 */
+	list_for_each_entry(td, &c67x00->td_list, td_list)
+		if (urb == td->urb)
+			td->urb = NULL;
+
+	urbp = urb->hcpriv;
+	urb->hcpriv = NULL;
+	list_del(&urbp->hep_node);
+	kfree(urbp);
+}
+
+/* --------------------------------------------------------------------------
+ * This function is to get endpoint from pipe in urb struct. This will be
+ * in higher linux version which includes endpoint in urb struct.
+ * -------------------------------------------------------------------------- */
+static struct usb_host_endpoint *
+c67x00_get_ep(struct urb *urb)
+{
+	unsigned epnum = 0;
+	if (urb == 0)
+		return 0;
+	epnum = usb_pipeendpoint(urb->pipe);
+	if (usb_pipein(urb->pipe))
+		return urb->dev->ep_in[epnum];
+	else
+		return urb->dev->ep_out[epnum];
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct c67x00_ep_data *
+c67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct usb_host_endpoint *hep = c67x00_get_ep(urb);
+	struct c67x00_ep_data *ep_data;
+	int type;
+
+	if (hep == 0)
+		return 0;
+
+	c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+
+	/* Check if endpoint already has a c67x00_ep_data struct allocated */
+	if (hep->hcpriv) {
+		ep_data = hep->hcpriv;
+		if (frame_after(c67x00->current_frame, ep_data->next_frame))
+			ep_data->next_frame =
+			    frame_add(c67x00->current_frame, 1);
+		return hep->hcpriv;
+	}
+
+	/* Allocate and initialize a new c67x00 endpoint data structure */
+	ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC);
+	if (!ep_data)
+		return NULL;
+
+	INIT_LIST_HEAD(&ep_data->queue);
+	INIT_LIST_HEAD(&ep_data->node);
+	ep_data->hep = hep;
+
+	/* hold a reference to udev as long as this endpoint lives,
+	 * this is needed to possibly fix the data toggle */
+	ep_data->dev = usb_get_dev(urb->dev);
+	hep->hcpriv = ep_data;
+
+	/* For ISOC and INT endpoints, start ASAP: */
+	ep_data->next_frame = frame_add(c67x00->current_frame, 1);
+
+	/* Add the endpoint data to one of the pipe lists; must be added
+	   in order of endpoint address */
+	type = usb_pipetype(urb->pipe);
+	if (list_empty(&ep_data->node)) {
+		list_add(&ep_data->node, &c67x00->list[type]);
+	} else {
+		struct c67x00_ep_data *prev;
+
+		list_for_each_entry(prev, &c67x00->list[type], node) {
+			if (prev->hep->desc.bEndpointAddress >
+			    hep->desc.bEndpointAddress) {
+				list_add(&ep_data->node, prev->node.prev);
+				break;
+			}
+		}
+	}
+
+	return ep_data;
+}
+
+static int c67x00_ep_data_free(struct usb_host_endpoint *hep)
+{
+	struct c67x00_ep_data *ep_data = hep->hcpriv;
+
+	if (!ep_data)
+		return 0;
+
+	if (!list_empty(&ep_data->queue))
+		return -EBUSY;
+
+	usb_put_dev(ep_data->dev);
+	list_del(&ep_data->queue);
+	list_del(&ep_data->node);
+
+	kfree(ep_data);
+	hep->hcpriv = NULL;
+
+	return 0;
+}
+
+void c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	unsigned long flags;
+
+	if (!list_empty(&ep->urb_list))
+		dev_warn(c67x00_hcd_dev(c67x00), "error: urb list not empty\n");
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+
+	/* loop waiting for all transfers in the endpoint queue to complete */
+	while (c67x00_ep_data_free(ep)) {
+		/* Drop the lock so we can sleep waiting for the hardware */
+		spin_unlock_irqrestore(&c67x00->lock, flags);
+
+		/* it could happen that we reinitialize this completion, while
+		 * somebody was waiting for that completion.  The timeout and
+		 * while loop handle such cases, but this might be improved */
+		INIT_COMPLETION(c67x00->endpoint_disable);
+		c67x00_sched_kick(c67x00);
+		wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ);
+
+		spin_lock_irqsave(&c67x00->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int get_root_port(struct usb_device *dev)
+{
+	while (dev->parent->parent)
+		dev = dev->parent;
+	return dev->portnum;
+}
+
+int c67x00_urb_enqueue(struct usb_hcd *hcd,
+		       struct usb_host_endpoint *hep,
+		       struct urb *urb, gfp_t mem_flags)
+{
+	int ret;
+	unsigned long flags;
+	struct c67x00_urb_priv *urbp;
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	int port = get_root_port(urb->dev)-1;
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+
+	/* Make sure host controller is running */
+	if (!HC_IS_RUNNING(hcd->state)) {
+		ret = -ENODEV;
+		goto err_not_linked;
+	}
+
+//	ret = usb_hcd_link_urb_to_ep(hcd, urb);
+//	if (ret)
+//		goto err_not_linked;
+
+	/* Allocate and initialize urb private data */
+	urbp = kzalloc(sizeof(*urbp), mem_flags);
+	if (!urbp) {
+		ret = -ENOMEM;
+		goto err_urbp;
+	}
+
+	INIT_LIST_HEAD(&urbp->hep_node);
+	urbp->urb = urb;
+	urbp->port = port;
+
+	urbp->ep_data = c67x00_ep_data_alloc(c67x00, urb);
+
+	if (!urbp->ep_data) {
+		ret = -ENOMEM;
+		goto err_epdata;
+	}
+
+	/* TODO claim bandwidth with usb_claim_bandwidth?
+	 * also release it somewhere! */
+
+	urb->hcpriv = urbp;
+
+	urb->actual_length = 0;	/* Nothing received/transmitted yet */
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		urb->interval = SETUP_STAGE;
+		break;
+	case PIPE_INTERRUPT:
+		break;
+	case PIPE_BULK:
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (c67x00->urb_iso_count == 0)
+			c67x00->max_frame_bw = MAX_FRAME_BW_ISO;
+		c67x00->urb_iso_count++;
+		/* Assume always URB_ISO_ASAP, FIXME */
+		if (list_empty(&urbp->ep_data->queue))
+			urb->start_frame = urbp->ep_data->next_frame;
+		else {
+			/* Go right after the last one */
+			struct urb *last_urb;
+
+			last_urb = list_entry(urbp->ep_data->queue.prev,
+					      struct c67x00_urb_priv,
+					      hep_node)->urb;
+			urb->start_frame =
+			    frame_add(last_urb->start_frame,
+				      last_urb->number_of_packets *
+				      last_urb->interval);
+		}
+		urbp->cnt = 0;
+		break;
+	}
+
+	/* Add the URB to the endpoint queue */
+	list_add_tail(&urbp->hep_node, &urbp->ep_data->queue);
+
+	/* If this is the only URB, kick start the controller */
+	if (!c67x00->urb_count++)
+		c67x00_ll_hpi_enable_sofeop(c67x00->sie);
+
+	c67x00_sched_kick(c67x00);
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	return 0;
+
+err_epdata:
+	kfree(urbp);
+err_urbp:
+//	usb_hcd_unlink_urb_from_ep(hcd, urb);
+	usb_hcd_giveback_urb(hcd, urb);
+err_not_linked:
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	return ret;
+}
+
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+	struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+	struct usb_host_endpoint *hep = c67x00_get_ep(urb);
+	unsigned long flags;
+//	int rc;
+
+	spin_lock_irqsave(&c67x00->lock, flags);
+//	rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+//	if (rc)
+//		goto done;
+
+//	hep = urb->hcpriv;
+	/* URB already unlinked (or never linked)? */
+	if (!hep) {
+		goto done;
+	}
+
+	c67x00_release_urb(c67x00, urb);
+//	usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+	spin_unlock(&c67x00->lock);
+	usb_hcd_giveback_urb(hcd, urb);
+	spin_lock(&c67x00->lock);
+
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+
+	return 0;
+
+ done:
+	spin_unlock_irqrestore(&c67x00->lock, flags);
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * pre: c67x00 locked, urb unlocked
+ */
+static void
+c67x00_giveback_urb(struct c67x00_hcd *c67x00, struct urb *urb, int status)
+{
+	struct c67x00_urb_priv *urbp;
+
+	if (!urb)
+		return;
+
+	urbp = urb->hcpriv;
+	urbp->status = status;
+
+	list_del_init(&urbp->hep_node);
+
+	c67x00_release_urb(c67x00, urb);
+//	usb_hcd_unlink_urb_from_ep(c67x00_hcd_to_hcd(c67x00), urb);
+	spin_unlock(&c67x00->lock);
+	usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb);
+	spin_lock(&c67x00->lock);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int c67x00_claim_frame_bw(struct c67x00_hcd *c67x00, struct urb *urb,
+				 int len, int periodic)
+{
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+	int bit_time;
+
+	/* According to the C67x00 BIOS user manual, page 3-18,19, the
+	 * following calculations provide the full speed bit times for
+	 * a transaction.
+	 *
+	 * FS(in)	= 112.5 +  9.36*BC + HOST_DELAY
+	 * FS(in,iso)	=  90.5 +  9.36*BC + HOST_DELAY
+	 * FS(out)	= 112.5 +  9.36*BC + HOST_DELAY
+	 * FS(out,iso)	=  78.4 +  9.36*BC + HOST_DELAY
+	 * LS(in)	= 802.4 + 75.78*BC + HOST_DELAY
+	 * LS(out)	= 802.6 + 74.67*BC + HOST_DELAY
+	 *
+	 * HOST_DELAY == 106 for the c67200 and c67300.
+	 */
+
+	/* make calculations in 1/100 bit times to maintain resolution */
+	if (urbp->ep_data->dev->speed == USB_SPEED_LOW) {
+		/* Low speed pipe */
+		if (usb_pipein(urb->pipe))
+			bit_time = 80240 + 7578*len;
+		else
+			bit_time = 80260 + 7467*len;
+	} else {
+		/* FS pipes */
+		if (usb_pipeisoc(urb->pipe))
+			bit_time = usb_pipein(urb->pipe) ? 9050 : 7840;
+		else
+			bit_time = 11250;
+		bit_time += 936*len;
+	}
+
+	/* Scale back down to integer bit times.  Use a host delay of 106.
+	 * (this is the only place it is used) */
+	bit_time = ((bit_time+50) / 100) + 106;
+
+	if (unlikely(bit_time + c67x00->bandwidth_allocated >=
+		     c67x00->max_frame_bw))
+		return -EMSGSIZE;
+
+	if (unlikely(c67x00->next_td_addr + CY_TD_SIZE >=
+		     c67x00->td_base_addr + SIE_TD_SIZE))
+		return -EMSGSIZE;
+
+	if (unlikely(c67x00->next_buf_addr + len >=
+		     c67x00->buf_base_addr + SIE_TD_BUF_SIZE))
+		return -EMSGSIZE;
+
+	if (periodic) {
+		if (unlikely(bit_time + c67x00->periodic_bw_allocated >=
+			     MAX_PERIODIC_BW(c67x00->max_frame_bw)))
+			return -EMSGSIZE;
+		c67x00->periodic_bw_allocated += bit_time;
+	}
+
+	c67x00->bandwidth_allocated += bit_time;
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * td_addr and buf_addr must be word aligned
+ */
+static int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb,
+			    void *data, int len, int pid, int toggle,
+			    unsigned long privdata)
+{
+	struct c67x00_td *td;
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+	const __u8 active_flag = 1, retry_cnt = 1;
+	__u8 cmd = 0;
+	int tt = 0;
+
+	if (c67x00_claim_frame_bw(c67x00, urb, len, usb_pipeisoc(urb->pipe)
+				  || usb_pipeint(urb->pipe)))
+		return -EMSGSIZE;	/* Not really an error, but expected */
+
+	td = kzalloc(sizeof(*td), GFP_ATOMIC);
+	if (!td)
+		return -ENOMEM;
+
+	td->pipe = urb->pipe;
+	td->ep_data = urbp->ep_data;
+
+	if ((td_udev(td)->speed == USB_SPEED_LOW) &&
+	    !(c67x00->low_speed_ports & (1 << urbp->port)))
+		cmd |= PREAMBLE_EN;
+
+	switch (usb_pipetype(td->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		tt = TT_ISOCHRONOUS;
+		cmd |= ISO_EN;
+		break;
+	case PIPE_CONTROL:
+		tt = TT_CONTROL;
+		break;
+	case PIPE_BULK:
+		tt = TT_BULK;
+		break;
+	case PIPE_INTERRUPT:
+		tt = TT_INTERRUPT;
+		break;
+	}
+
+	if (toggle)
+		cmd |= SEQ_SEL;
+
+	cmd |= ARM_EN;
+
+	/* SW part */
+	td->td_addr = c67x00->next_td_addr;
+	c67x00->next_td_addr = c67x00->next_td_addr + CY_TD_SIZE;
+
+	/* HW part */
+	td->ly_base_addr = __cpu_to_le16(c67x00->next_buf_addr);
+	td->port_length = __cpu_to_le16((c67x00->sie->sie_num << 15) |
+					(urbp->port << 14) | (len & 0x3FF));
+	td->pid_ep = ((pid & 0xF) << TD_PIDEP_OFFSET) |
+	    (usb_pipeendpoint(td->pipe) & 0xF);
+	td->dev_addr = usb_pipedevice(td->pipe) & 0x7F;
+	td->ctrl_reg = cmd;
+	td->status = 0;
+	td->retry_cnt = (tt << TT_OFFSET) | (active_flag << 4) | retry_cnt;
+	td->residue = 0;
+	td->next_td_addr = __cpu_to_le16(c67x00->next_td_addr);
+
+	/* SW part */
+	td->data = data;
+	td->urb = urb;
+	td->urb->status = td->status;
+
+	td->privdata = privdata;
+
+	c67x00->next_buf_addr += (len + 1) & ~0x01;	/* properly align */
+
+	list_add_tail(&td->td_list, &c67x00->td_list);
+	return 0;
+}
+
+static inline void c67x00_release_td(struct c67x00_td *td)
+{
+	list_del_init(&td->td_list);
+	kfree(td);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int c67x00_add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	int remaining;
+	int toggle;
+	int pid;
+	int ret = 0;
+	int maxps;
+	int need_empty;
+
+	toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			       usb_pipeout(urb->pipe));
+	remaining = urb->transfer_buffer_length - urb->actual_length;
+
+	maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+	    usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+	while (remaining || need_empty) {
+		int len;
+		char *td_buf;
+
+		len = (remaining > maxps) ? maxps : remaining;
+		if (!len)
+			need_empty = 0;
+
+		pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+		td_buf = urb->transfer_buffer + urb->transfer_buffer_length -
+		    remaining;
+		ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, toggle,
+				       DATA_STAGE);
+		if (ret)
+			return ret;	/* td wasn't created */
+
+		toggle ^= 1;
+		remaining -= len;
+		if (usb_pipecontrol(urb->pipe))
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int c67x00_add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	int ret;
+	int pid;
+
+	switch (urb->interval) {
+	default:
+	case SETUP_STAGE:
+		ret = c67x00_create_td(c67x00, urb, urb->setup_packet,
+				       8, USB_PID_SETUP, 0, SETUP_STAGE);
+		if (ret)
+			return ret;
+		urb->interval = SETUP_STAGE;
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), 1);
+		break;
+	case DATA_STAGE:
+		if (urb->transfer_buffer_length) {
+			ret = c67x00_add_data_urb(c67x00, urb);
+			if (ret)
+				return ret;
+			break;
+		}		/* else fallthrough */
+	case STATUS_STAGE:
+		pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+		ret = c67x00_create_td(c67x00, urb, NULL, 0, pid, 1,
+				       STATUS_STAGE);
+		if (ret)
+			return ret;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int c67x00_add_int_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+	if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+		urbp->ep_data->next_frame =
+		    frame_add(urbp->ep_data->next_frame, urb->interval);
+		return c67x00_add_data_urb(c67x00, urb);
+	}
+	return 0;
+}
+
+static int c67x00_add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+	struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+	if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+		char *td_buf;
+		int len, pid, ret;
+
+		BUG_ON(urbp->cnt >= urb->number_of_packets);
+
+		td_buf = urb->transfer_buffer +
+		    urb->iso_frame_desc[urbp->cnt].offset;
+		len = urb->iso_frame_desc[urbp->cnt].length;
+		pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+
+		ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, 0,
+				       urbp->cnt);
+		if (ret) {
+			printk(KERN_DEBUG "create failed: %d\n", ret);
+			urb->iso_frame_desc[urbp->cnt].actual_length = 0;
+			urb->iso_frame_desc[urbp->cnt].status = ret;
+			if (urbp->cnt + 1 == urb->number_of_packets)
+				c67x00_giveback_urb(c67x00, urb, 0);
+		}
+
+		urbp->ep_data->next_frame =
+		    frame_add(urbp->ep_data->next_frame, urb->interval);
+		urbp->cnt++;
+	}
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_fill_from_list(struct c67x00_hcd *c67x00, int type,
+				  int (*add)(struct c67x00_hcd *, struct urb *))
+{
+	struct c67x00_ep_data *ep_data;
+	struct urb *urb;
+
+	/* traverse every endpoint on the list */
+	list_for_each_entry(ep_data, &c67x00->list[type], node) {
+		if (!list_empty(&ep_data->queue)) {
+			/* and add the first urb */
+			/* isochronous transfer rely on this */
+			urb = list_entry(ep_data->queue.next,
+					 struct c67x00_urb_priv,
+					 hep_node)->urb;
+			add(c67x00, urb);
+		}
+	}
+}
+
+static void c67x00_fill_frame(struct c67x00_hcd *c67x00)
+{
+	struct c67x00_td *td, *ttd;
+
+	/* Check if we can proceed */
+	if (!list_empty(&c67x00->td_list)) {
+		dev_warn(c67x00_hcd_dev(c67x00),
+			 "TD list not empty! This should not happen!\n");
+		list_for_each_entry_safe(td, ttd, &c67x00->td_list, td_list) {
+			dbg_td(c67x00, td, "Unprocessed td");
+			c67x00_release_td(td);
+		}
+	}
+
+	/* Reinitialize variables */
+	c67x00->bandwidth_allocated = 0;
+	c67x00->periodic_bw_allocated = 0;
+
+	c67x00->next_td_addr = c67x00->td_base_addr;
+	c67x00->next_buf_addr = c67x00->buf_base_addr;
+
+	/* Fill the list */
+	c67x00_fill_from_list(c67x00, PIPE_ISOCHRONOUS, c67x00_add_iso_urb);
+	c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, c67x00_add_int_urb);
+	c67x00_fill_from_list(c67x00, PIPE_CONTROL, c67x00_add_ctrl_urb);
+	c67x00_fill_from_list(c67x00, PIPE_BULK, c67x00_add_data_urb);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * Get TD from C67X00
+ */
+static inline void
+c67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	c67x00_ll_read_mem_le16(c67x00->sie->dev,
+				td->td_addr, td, CY_TD_SIZE);
+
+	if (usb_pipein(td->pipe) && td_actual_bytes(td))
+		c67x00_ll_read_mem_le16(c67x00->sie->dev, td_ly_base_addr(td),
+					td->data, td_actual_bytes(td));
+}
+
+static int c67x00_td_to_error(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	if (td->status & TD_STATUSMASK_ERR) {
+		dbg_td(c67x00, td, "ERROR_FLAG");
+		return -EILSEQ;
+	}
+	if (td->status & TD_STATUSMASK_STALL) {
+		/* dbg_td(c67x00, td, "STALL"); */
+		return -EPIPE;
+	}
+	if (td->status & TD_STATUSMASK_TMOUT) {
+		dbg_td(c67x00, td, "TIMEOUT");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static inline int c67x00_end_of_data(struct c67x00_td *td)
+{
+	int maxps, need_empty, remaining;
+	struct urb *urb = td->urb;
+	int act_bytes;
+
+	act_bytes = td_actual_bytes(td);
+
+	if (unlikely(!act_bytes))
+		return 1;	/* This was an empty packet */
+
+	maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe));
+
+	if (unlikely(act_bytes < maxps))
+		return 1;	/* Smaller then full packet */
+
+	remaining = urb->transfer_buffer_length - urb->actual_length;
+	need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+	    usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+	if (unlikely(!remaining && !need_empty))
+		return 1;
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Remove all td's from the list which come
+ * after last_td and are meant for the same pipe.
+ * This is used when a short packet has occured */
+static inline void c67x00_clear_pipe(struct c67x00_hcd *c67x00,
+				     struct c67x00_td *last_td)
+{
+	struct c67x00_td *td, *tmp;
+	td = last_td;
+	tmp = last_td;
+	while (td->td_list.next != &c67x00->td_list) {
+		td = list_entry(td->td_list.next, struct c67x00_td, td_list);
+		if (td->pipe == last_td->pipe) {
+			c67x00_release_td(td);
+			td = tmp;
+		}
+		tmp = td;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_handle_successful_td(struct c67x00_hcd *c67x00,
+					struct c67x00_td *td)
+{
+	struct urb *urb = td->urb;
+
+	if (!urb)
+		return;
+
+	urb->actual_length += td_actual_bytes(td);
+
+	switch (usb_pipetype(td->pipe)) {
+		/* isochronous tds are handled separately */
+	case PIPE_CONTROL:
+		switch (td->privdata) {
+		case SETUP_STAGE:
+			urb->interval =
+			    urb->transfer_buffer_length ?
+			    DATA_STAGE : STATUS_STAGE;
+			/* Don't count setup_packet with normal data: */
+			urb->actual_length = 0;
+			break;
+
+		case DATA_STAGE:
+			if (c67x00_end_of_data(td)) {
+				urb->interval = STATUS_STAGE;
+				c67x00_clear_pipe(c67x00, td);
+			}
+			break;
+
+		case STATUS_STAGE:
+			urb->interval = 0;
+			c67x00_giveback_urb(c67x00, urb, 0);
+			break;
+		}
+		break;
+
+	case PIPE_INTERRUPT:
+	case PIPE_BULK:
+		if (unlikely(c67x00_end_of_data(td))) {
+			c67x00_clear_pipe(c67x00, td);
+			c67x00_giveback_urb(c67x00, urb, 0);
+		}
+		break;
+	}
+}
+
+static void c67x00_handle_isoc(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	struct urb *urb = td->urb;
+	struct c67x00_urb_priv *urbp;
+	int cnt;
+
+	if (!urb)
+		return;
+
+	urbp = urb->hcpriv;
+	cnt = td->privdata;
+
+	if (td->status & TD_ERROR_MASK)
+		urb->error_count++;
+
+	urb->iso_frame_desc[cnt].actual_length = td_actual_bytes(td);
+	urb->iso_frame_desc[cnt].status = c67x00_td_to_error(c67x00, td);
+	if (cnt + 1 == urb->number_of_packets)	/* Last packet */
+		c67x00_giveback_urb(c67x00, urb, 0);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_check_td_list - handle tds which have been processed by the c67x00
+ * pre: current_td == 0
+ */
+static inline void c67x00_check_td_list(struct c67x00_hcd *c67x00)
+{
+	struct c67x00_td *td, *tmp;
+	struct urb *urb;
+	int ack_ok;
+	int clear_endpoint;
+
+	list_for_each_entry_safe(td, tmp, &c67x00->td_list, td_list) {
+		/* get the TD */
+		c67x00_parse_td(c67x00, td);
+		urb = td->urb;	/* urb can be NULL! */
+		ack_ok = 0;
+		clear_endpoint = 1;
+
+		/* Handle isochronous transfers separately */
+		if (usb_pipeisoc(td->pipe)) {
+			clear_endpoint = 0;
+			c67x00_handle_isoc(c67x00, td);
+			goto cont;
+		}
+
+		/* When an error occurs, all td's for that pipe go into an
+		 * inactive state. This state matches successful transfers so
+		 * we must make sure not to service them. */
+		if (td->status & TD_ERROR_MASK) {
+			c67x00_giveback_urb(c67x00, urb,
+					    c67x00_td_to_error(c67x00, td));
+			goto cont;
+		}
+
+		if ((td->status & TD_STATUSMASK_NAK) || !td_sequence_ok(td) ||
+		    !td_acked(td))
+			goto cont;
+
+		/* Sequence ok and acked, don't need to fix toggle */
+		ack_ok = 1;
+
+		if (unlikely(td->status & TD_STATUSMASK_OVF)) {
+			if (td_residue(td) & TD_RESIDUE_OVERFLOW) {
+				/* Overflow */
+				c67x00_giveback_urb(c67x00, urb, -EOVERFLOW);
+				goto cont;
+			}
+		}
+
+		clear_endpoint = 0;
+//		dbg_td(c67x00, td, "SUCCESSFUL");
+		c67x00_handle_successful_td(c67x00, td);
+
+cont:
+		if (clear_endpoint)
+			c67x00_clear_pipe(c67x00, td);
+		if (ack_ok)
+			usb_settoggle(td_udev(td), usb_pipeendpoint(td->pipe),
+				      usb_pipeout(td->pipe),
+				      !(td->ctrl_reg & SEQ_SEL));
+		/* next in list could have been removed, due to clear_pipe! */
+		tmp = list_entry(td->td_list.next, typeof(*td), td_list);
+		c67x00_release_td(td);
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int c67x00_all_tds_processed(struct c67x00_hcd *c67x00)
+{
+	/* If all tds are processed, we can check the previous frame (if
+	 * there was any) and start our next frame.
+	 */
+	return !c67x00_ll_husb_get_current_td(c67x00->sie);
+}
+
+/**
+ * Send td to C67X00
+ */
+static void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+	int len = td_length(td);
+
+	if (len && ((td->pid_ep & TD_PIDEPMASK_PID) != TD_PID_IN))
+		c67x00_ll_write_mem_le16(c67x00->sie->dev, td_ly_base_addr(td),
+					 td->data, len);
+
+	c67x00_ll_write_mem_le16(c67x00->sie->dev,
+				 td->td_addr, td, CY_TD_SIZE);
+}
+
+static void c67x00_send_frame(struct c67x00_hcd *c67x00)
+{
+	struct c67x00_td *td;
+
+	if (list_empty(&c67x00->td_list))
+		dev_warn(c67x00_hcd_dev(c67x00),
+			 "%s: td list should not be empty here!\n",
+			 __func__);
+
+	list_for_each_entry(td, &c67x00->td_list, td_list) {
+		if (td->td_list.next == &c67x00->td_list)
+			td->next_td_addr = 0;	/* Last td in list */
+
+		c67x00_send_td(c67x00, td);
+	}
+
+	c67x00_ll_husb_set_current_td(c67x00->sie, c67x00->td_base_addr);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_do_work - Schedulers state machine
+ */
+static void c67x00_do_work(struct c67x00_hcd *c67x00)
+{
+	spin_lock(&c67x00->lock);
+	/* Make sure all tds are processed */
+	if (!c67x00_all_tds_processed(c67x00))
+		goto out;
+
+	c67x00_check_td_list(c67x00);
+
+	/* no td's are being processed (current == 0)
+	 * and all have been "checked" */
+	complete(&c67x00->endpoint_disable);
+
+	if (!list_empty(&c67x00->td_list))
+		goto out;
+
+	c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+	if (c67x00->current_frame == c67x00->last_frame)
+		goto out;	/* Don't send tds in same frame */
+	c67x00->last_frame = c67x00->current_frame;
+
+	/* If no urbs are scheduled, our work is done */
+	if (!c67x00->urb_count) {
+		c67x00_ll_hpi_disable_sofeop(c67x00->sie);
+		goto out;
+	}
+
+	c67x00_fill_frame(c67x00);
+	if (!list_empty(&c67x00->td_list))
+		/* TD's have been added to the frame */
+		c67x00_send_frame(c67x00);
+
+ out:
+	spin_unlock(&c67x00->lock);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_sched_tasklet(unsigned long __c67x00)
+{
+	struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+	c67x00_do_work(c67x00);
+}
+
+void c67x00_sched_kick(struct c67x00_hcd *c67x00)
+{
+	tasklet_hi_schedule(&c67x00->tasklet);
+}
+
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00)
+{
+	tasklet_init(&c67x00->tasklet, c67x00_sched_tasklet,
+		     (unsigned long)c67x00);
+	return 0;
+}
+
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
+{
+	tasklet_kill(&c67x00->tasklet);
+}
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00.h
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00.h	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/c67x00.h	(revision 0)
@@ -0,0 +1,294 @@
+/*
+ * c67x00.h: Cypress C67X00 USB register and field definitions
+ *
+ * Copyright (C) 2006-2008 Barco N.V.
+ *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ *    based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA.
+ */
+
+#ifndef _USB_C67X00_H
+#define _USB_C67X00_H
+
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+
+/* ---------------------------------------------------------------------
+ * Cypress C67x00 register definitions
+ */
+
+/* Hardware Revision Register */
+#define HW_REV_REG		0xC004
+
+/* General USB registers */
+/* ===================== */
+
+/* USB Control Register */
+#define USB_CTL_REG(x)		((x) ? 0xC0AA : 0xC08A)
+
+#define LOW_SPEED_PORT(x)	((x) ? 0x0800 : 0x0400)
+#define HOST_MODE		0x0200
+#define PORT_RES_EN(x)		((x) ? 0x0100 : 0x0080)
+#define SOF_EOP_EN(x)		((x) ? 0x0002 : 0x0001)
+
+/* USB status register - Notice it has different content in hcd/udc mode */
+#define USB_STAT_REG(x)		((x) ? 0xC0B0 : 0xC090)
+
+#define EP0_IRQ_FLG		0x0001
+#define EP1_IRQ_FLG		0x0002
+#define EP2_IRQ_FLG		0x0004
+#define EP3_IRQ_FLG		0x0008
+#define EP4_IRQ_FLG		0x0010
+#define EP5_IRQ_FLG		0x0020
+#define EP6_IRQ_FLG		0x0040
+#define EP7_IRQ_FLG		0x0080
+#define RESET_IRQ_FLG		0x0100
+#define SOF_EOP_IRQ_FLG		0x0200
+#define ID_IRQ_FLG		0x4000
+#define VBUS_IRQ_FLG		0x8000
+
+/* USB Host only registers */
+/* ======================= */
+
+/* Host n Control Register */
+#define HOST_CTL_REG(x)		((x) ? 0xC0A0 : 0xC080)
+
+#define PREAMBLE_EN		0x0080	/* Preamble enable */
+#define SEQ_SEL			0x0040	/* Data Toggle Sequence Bit Select */
+#define ISO_EN			0x0010	/* Isochronous enable  */
+#define ARM_EN			0x0001	/* Arm operation */
+
+/* Host n Interrupt Enable Register */
+#define HOST_IRQ_EN_REG(x)	((x) ? 0xC0AC : 0xC08C)
+
+#define SOF_EOP_IRQ_EN		0x0200	/* SOF/EOP Interrupt Enable  */
+#define SOF_EOP_TMOUT_IRQ_EN	0x0800	/* SOF/EOP Timeout Interrupt Enable  */
+#define ID_IRQ_EN		0x4000	/* ID interrupt enable */
+#define VBUS_IRQ_EN		0x8000	/* VBUS interrupt enable */
+#define DONE_IRQ_EN		0x0001	/* Done Interrupt Enable  */
+
+/* USB status register */
+#define HOST_STAT_MASK		0x02FD
+#define PORT_CONNECT_CHANGE(x)	((x) ? 0x0020 : 0x0010)
+#define PORT_SE0_STATUS(x)	((x) ? 0x0008 : 0x0004)
+
+/* Host Frame Register */
+#define HOST_FRAME_REG(x)	((x) ? 0xC0B6 : 0xC096)
+
+#define HOST_FRAME_MASK		0x07FF
+
+/* USB Peripheral only registers */
+/* ============================= */
+
+/* Device n Port Sel reg */
+#define DEVICE_N_PORT_SEL(x)	((x) ? 0xC0A4 : 0xC084)
+
+/* Device n Interrupt Enable Register */
+#define DEVICE_N_IRQ_EN_REG(x)	((x) ? 0xC0AC : 0xC08C)
+
+#define DEVICE_N_ENDPOINT_N_CTL_REG(dev, ep)	((dev)  		\
+						 ? (0x0280 + (ep << 4)) \
+						 : (0x0200 + (ep << 4)))
+#define DEVICE_N_ENDPOINT_N_STAT_REG(dev, ep)	((dev)			\
+						 ? (0x0286 + (ep << 4)) \
+						 : (0x0206 + (ep << 4)))
+
+#define DEVICE_N_ADDRESS(dev)	((dev) ? (0xC0AE) : (0xC08E))
+
+/* HPI registers */
+/* ============= */
+
+/* HPI Status register */
+#define SOFEOP_FLG(x)		(1 << ((x) ? 12 : 10))
+#define SIEMSG_FLG(x)		(1 << (4 + (x)))
+#define RESET_FLG(x)		((x) ? 0x0200 : 0x0002)
+#define DONE_FLG(x)		(1 << (2 + (x)))
+#define RESUME_FLG(x)		(1 << (6 + (x)))
+#define MBX_OUT_FLG		0x0001	/* Message out available */
+#define MBX_IN_FLG		0x0100
+#define ID_FLG			0x4000
+#define VBUS_FLG		0x8000
+
+/* Interrupt routing register */
+#define HPI_IRQ_ROUTING_REG	0x0142
+
+#define HPI_SWAP_ENABLE(x)	((x) ? 0x0100 : 0x0001)
+#define RESET_TO_HPI_ENABLE(x)	((x) ? 0x0200 : 0x0002)
+#define DONE_TO_HPI_ENABLE(x)	((x) ? 0x0008 : 0x0004)
+#define RESUME_TO_HPI_ENABLE(x)	((x) ? 0x0080 : 0x0040)
+#define SOFEOP_TO_HPI_EN(x)	((x) ? 0x2000 : 0x0800)
+#define SOFEOP_TO_CPU_EN(x)	((x) ? 0x1000 : 0x0400)
+#define ID_TO_HPI_ENABLE	0x4000
+#define VBUS_TO_HPI_ENABLE	0x8000
+
+/* SIE msg registers */
+#define SIEMSG_REG(x)		((x) ? 0x0148 : 0x0144)
+
+#define HUSB_TDListDone		0x1000
+
+#define SUSB_EP0_MSG		0x0001
+#define SUSB_EP1_MSG		0x0002
+#define SUSB_EP2_MSG		0x0004
+#define SUSB_EP3_MSG		0x0008
+#define SUSB_EP4_MSG		0x0010
+#define SUSB_EP5_MSG		0x0020
+#define SUSB_EP6_MSG		0x0040
+#define SUSB_EP7_MSG		0x0080
+#define SUSB_RST_MSG		0x0100
+#define SUSB_SOF_MSG		0x0200
+#define SUSB_CFG_MSG		0x0400
+#define SUSB_SUS_MSG		0x0800
+#define SUSB_ID_MSG	       	0x4000
+#define SUSB_VBUS_MSG		0x8000
+
+/* BIOS interrupt routines */
+
+#define SUSBx_RECEIVE_INT(x)	((x) ? 97 : 81)
+#define SUSBx_SEND_INT(x)	((x) ? 96 : 80)
+
+#define SUSBx_DEV_DESC_VEC(x)	((x) ? 0x00D4 : 0x00B4)
+#define SUSBx_CONF_DESC_VEC(x)	((x) ? 0x00D6 : 0x00B6)
+#define SUSBx_STRING_DESC_VEC(x) ((x) ? 0x00D8 : 0x00B8)
+
+#define CY_HCD_BUF_ADDR		0x500	/* Base address for host */
+#define SIE_TD_SIZE		0x200	/* size of the td list */
+#define SIE_TD_BUF_SIZE		0x400	/* size of the data buffer */
+
+#define SIE_TD_OFFSET(host)	((host) ? (SIE_TD_SIZE+SIE_TD_BUF_SIZE) : 0)
+#define SIE_BUF_OFFSET(host)	(SIE_TD_OFFSET(host) + SIE_TD_SIZE)
+
+/* Base address of HCD + 2 x TD_SIZE + 2 x TD_BUF_SIZE */
+#define CY_UDC_REQ_HEADER_BASE	0x1100
+/* 8- byte request headers for IN/OUT transfers */
+#define CY_UDC_REQ_HEADER_SIZE	8
+
+#define CY_UDC_REQ_HEADER_ADDR(ep_num)	(CY_UDC_REQ_HEADER_BASE + \
+					 ((ep_num) * CY_UDC_REQ_HEADER_SIZE))
+#define CY_UDC_DESC_BASE_ADDRESS	(CY_UDC_REQ_HEADER_ADDR(8))
+
+#define CY_UDC_BIOS_REPLACE_BASE	0x1800
+#define CY_UDC_REQ_BUFFER_BASE		0x2000
+#define CY_UDC_REQ_BUFFER_SIZE		0x0400
+#define CY_UDC_REQ_BUFFER_ADDR(ep_num)	(CY_UDC_REQ_BUFFER_BASE + \
+					 ((ep_num) * CY_UDC_REQ_BUFFER_SIZE))
+
+/* ---------------------------------------------------------------------
+ * Driver data structures
+ */
+
+struct c67x00_device;
+
+/**
+ * struct c67x00_sie - Common data associated with a SIE
+ * @lock: lock to protect this struct and the associated chip registers
+ * @private_data: subdriver dependent data
+ * @irq: subdriver dependent irq handler, set NULL when not used
+ * @dev: link to common driver structure
+ * @sie_num: SIE number on chip, starting from 0
+ * @mode: SIE mode (host/peripheral/otg/not used)
+ */
+struct c67x00_sie {
+	/* Entries to be used by the subdrivers */
+	spinlock_t lock;	/* protect this structure */
+	void *private_data;
+	void (*irq) (struct c67x00_sie *sie, u16 int_status, u16 msg);
+
+	/* Read only: */
+	struct c67x00_device *dev;
+	int sie_num;
+	int mode;
+};
+
+#define sie_dev(s)	(&(s)->dev->pdev->dev)
+
+/**
+ * struct c67x00_lcp
+ */
+struct c67x00_lcp {
+	/* Internal use only */
+	struct mutex mutex;
+	struct completion msg_received;
+	u16 last_msg;
+};
+
+/*
+ * struct c67x00_hpi
+ */
+struct c67x00_hpi {
+	void __iomem *base;
+	int regstep;
+	spinlock_t lock;
+	struct c67x00_lcp lcp;
+};
+
+#define C67X00_SIES	2
+#define C67X00_PORTS	2
+
+/**
+ * struct c67x00_device - Common data associated with a c67x00 instance
+ * @hpi: hpi addresses
+ * @sie: array of sie's on this chip
+ * @pdev: platform device of instance
+ * @pdata: configuration provided by the platform
+ */
+struct c67x00_device {
+	struct c67x00_hpi hpi;
+	struct c67x00_sie sie[C67X00_SIES];
+	struct platform_device *pdev;
+	struct c67x00_platform_data *pdata;
+};
+
+/* ---------------------------------------------------------------------
+ * Low level interface functions
+ */
+
+/* Host Port Interface (HPI) functions */
+u16 c67x00_ll_hpi_status(struct c67x00_device *dev);
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev);
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie);
+void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie);
+
+/* General functions */
+u16 c67x00_ll_fetch_siemsg(struct c67x00_device *dev, int sie_num);
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie);
+void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits);
+u16 c67x00_ll_usb_get_status(struct c67x00_sie *sie);
+void c67x00_ll_write_mem_le16(struct c67x00_device *dev, u16 addr,
+			      void *data, int len);
+void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
+			     void *data, int len);
+
+/* Host specific functions */
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value);
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port);
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr);
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie);
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie *sie);
+void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie);
+void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port);
+
+/* Called by c67x00_irq to handle lcp interrupts */
+void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status);
+
+/* Setup and teardown */
+void c67x00_ll_init(struct c67x00_device *dev);
+void c67x00_ll_release(struct c67x00_device *dev);
+int c67x00_ll_reset(struct c67x00_device *dev);
+
+#endif				/* _USB_C67X00_H */
Index: software/linux-2.6.x-petalogix/drivers/usb/c67x00/Makefile
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/c67x00/Makefile	(revision 0)
+++ software/linux-2.6.x-petalogix/drivers/usb/c67x00/Makefile	(revision 0)
@@ -0,0 +1,9 @@
+#
+# Makefile for Cypress C67X00 USB Controller
+#
+
+ccflags-$(CONFIG_USB_DEBUG)		+= -DDEBUG
+
+obj-$(CONFIG_USB_C67X00_HCD)		+= c67x00.o
+
+c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o
Index: software/linux-2.6.x-petalogix/drivers/usb/host/Kconfig
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/host/Kconfig	(revision 5469)
+++ software/linux-2.6.x-petalogix/drivers/usb/host/Kconfig	(working copy)
@@ -4,6 +4,20 @@
 comment "USB Host Controller Drivers"
 	depends on USB
 
+config USB_C67X00_HCD
+	tristate "Cypress C67x00 HCD support"
+	depends on USB
+	help
+	  The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
+	  host/peripheral/OTG USB controllers.
+
+	  Enable this option to support this chip in host controller mode.
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called c67x00.
+
+
 config USB_EHCI_HCD
 	tristate "EHCI HCD (USB 2.0) support"
 	depends on USB && USB_ARCH_HAS_EHCI
Index: software/linux-2.6.x-petalogix/drivers/usb/Makefile
===================================================================
--- software/linux-2.6.x-petalogix/drivers/usb/Makefile	(revision 5469)
+++ software/linux-2.6.x-petalogix/drivers/usb/Makefile	(working copy)
@@ -18,6 +18,8 @@
 obj-$(CONFIG_ETRAX_USB_HOST)	+= host/
 obj-$(CONFIG_USB_OHCI_AT91)	+= host/
 
+obj-$(CONFIG_USB_C67X00_HCD)    += c67x00/
+
 obj-$(CONFIG_USB_ACM)		+= class/
 obj-$(CONFIG_USB_PRINTER)	+= class/