[RFC] [PATCH] USB: ISP1763 host and device driver

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

 



This patch adds support for STE-ISP1763 USB host and device controller
The host-side is based largely on isp1760-hcd and STE pehci driver
The device-side and platform parsing code is written mostly from scratch.

Co-authored-by: Florian VÃgel <fvoegel@xxxxxxxxxxxx>
---
 drivers/usb/Kconfig                |    1 +
 drivers/usb/Makefile               |    1 +
 drivers/usb/isp1763/Kconfig        |   59 +
 drivers/usb/isp1763/Makefile       |    3 +
 drivers/usb/isp1763/isp1763.h      |  301 +++++
 drivers/usb/isp1763/isp1763_base.c |  976 +++++++++++++++
 drivers/usb/isp1763/isp1763_hcd.c  | 2395 ++++++++++++++++++++++++++++++++++++
 drivers/usb/isp1763/isp1763_hcd.h  |  322 +++++
 drivers/usb/isp1763/isp1763_udc.c  | 1673 +++++++++++++++++++++++++
 drivers/usb/isp1763/isp1763_udc.h  |  113 ++
 10 files changed, 5844 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/isp1763/Kconfig
 create mode 100644 drivers/usb/isp1763/Makefile
 create mode 100644 drivers/usb/isp1763/isp1763.h
 create mode 100644 drivers/usb/isp1763/isp1763_base.c
 create mode 100644 drivers/usb/isp1763/isp1763_hcd.c
 create mode 100644 drivers/usb/isp1763/isp1763_hcd.h
 create mode 100644 drivers/usb/isp1763/isp1763_udc.c
 create mode 100644 drivers/usb/isp1763/isp1763_udc.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 2407508..7d9b9cc 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -157,4 +157,5 @@ source "drivers/usb/gadget/Kconfig"
 
 source "drivers/usb/otg/Kconfig"
 
+source "drivers/usb/isp1763/Kconfig"
 endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index be3c9b8..e9f3e56 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
 obj-$(CONFIG_USB_HWA_HCD)	+= host/
 obj-$(CONFIG_USB_ISP1760_HCD)	+= host/
+obj-$(CONFIG_USB_ISP1763)	+= isp1763/
 
 obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/
 
diff --git a/drivers/usb/isp1763/Kconfig b/drivers/usb/isp1763/Kconfig
new file mode 100644
index 0000000..ca1bec2
--- /dev/null
+++ b/drivers/usb/isp1763/Kconfig
@@ -0,0 +1,59 @@
+#
+#
+
+comment "Enable Host or Gadget support to see ISP1763 options"
+	depends on !USB && USB_GADGET=n
+
+menuconfig USB_ISP1763
+	tristate "ISP 1763A highspeed dual role controller support"
+      	depends on (USB || USB_GADGET)
+	help
+  	  Support for ST Ericsson ISP1763A USB OTG controller
+
+
+if USB_ISP1763
+
+config USB_ISP1763_UDC
+        tristate
+
+config USB_ISP1763_HCD
+        tristate
+
+choice
+	prompt "ISP1763 controller drivers"
+	depends on USB_ISP1763
+	help
+	  Blah
+
+config USB_ISP1763_OTG
+	depends on USB_ISP1763
+	tristate 'ISP1763 OTG (host+device auto-select) support'
+	select USB_ISP1763_UDC
+	select USB_ISP1763_HCD
+	help
+	  Limited OTG support for ISP1763; enables detection of ID signal and
+	  automatic switching between host and device controller mode. Does
+	  not include HRP and SRP support.
+
+	  This option will build the drivers for both host and peripheral
+	  controller.
+
+config USB_ISP1763_UDC_SELECT
+         depends on USB_ISP1763
+	 depends on USB_GADGET
+	 select USB_ISP1763_FUNCTION_SELECTED
+	 select USB_ISP1763_UDC
+	 tristate 'ISP1763 UDC peripheral-only support'
+	 help
+	   Support for device controller functionality of the ISP1763
+
+config USB_ISP1763_HCD_SELECT
+         depends on USB_ISP1763
+	 select USB_ISP1763_HCD
+	 tristate 'ISP1763 HCD host-only support'
+	 help
+	   Support for host controller functionality of the ISP1763
+
+endchoice
+
+endif
diff --git a/drivers/usb/isp1763/Makefile b/drivers/usb/isp1763/Makefile
new file mode 100644
index 0000000..f2cfbd7
--- /dev/null
+++ b/drivers/usb/isp1763/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_USB_ISP1763) += isp1763_base.o
+obj-$(CONFIG_USB_ISP1763_UDC) += isp1763_udc.o
+obj-$(CONFIG_USB_ISP1763_HCD) += isp1763_hcd.o
diff --git a/drivers/usb/isp1763/isp1763.h b/drivers/usb/isp1763/isp1763.h
new file mode 100644
index 0000000..7cabf50
--- /dev/null
+++ b/drivers/usb/isp1763/isp1763.h
@@ -0,0 +1,301 @@
+#ifndef __ISP1763_H__
+#define __ISP1763_H__
+
+/*
+* Device flags that can vary from board to board.  All of these
+* indicate the most "atypical" case, so that a devflags of 0 is
+* a sane default configuration.
+*/
+#define ISP1763_FLAG_BUS_WIDTH_8	0x00000002	/* 8-bit data bus width */
+#define ISP1763_FLAG_PORT1_ROLE_HOST	0x00000004	/* Static host */
+#define ISP1763_FLAG_PORT1_ROLE_GADGET	0x00000008	/* Static gadget */
+#define ISP1763_FLAG_PORT1_ROLE_OTG	0x0000000C	/* Port 1 supports OTG */
+#define ISP1763_FLAG_DACK_POL_HIGH	0x00000010	/* DACK active high */
+#define ISP1763_FLAG_DREQ_POL_HIGH	0x00000020	/* DREQ active high */
+#define ISP1763_FLAG_INTR_POL_HIGH	0x00000080	/* Interrupt polarity active high */
+#define ISP1763_FLAG_INTR_EDGE_TRIG	0x00000100	/* Interrupt edge triggered */
+#define ISP1763_FLAG_DMA_ENABLED	0x00001000
+#define ISP1763_FLAG_DMA_EXTERNAL	0x00002000	/* on: external DMA, off: isp-internal */
+
+/* memory map of ISP1763A USB dual role/OTG controller */
+struct isp1763_regs {
+
+	/* DC registers (mostly) */
+	u16 address;		/* 0x00 device address */
+	u16 reserved001;
+	u16 ep_maxpktsize;	/* 0x04 EP maxpacketsize */
+	u16 reserved002;
+	u16 ep_type;		/* 0x08 EP type */
+	u16 reserved003;
+	u16 mode;		/* 0x0C mode register */
+	u16 reserved004;
+	u16 icr;		/* 0x10 Interrupt control register */
+	u16 debug;		/* 0x12 debug register - useless */
+	u32 dc_int_enable;	/* 0x14 DC interrupt enable */
+	u32 dc_interrupt;	/* 0x18 DC interrupt */
+	u16 buflen;		/* 0x1C EP buffer length */
+	u16 dcbufstatus;	/* 0x1E EP buffer status */
+	u16 data_port;		/* 0x20 DC data port */
+	u16 reserved006[3];
+	u16 ctrlfn;		/* 0x28 DC control function */
+	u16 reserved007;
+	u16 ep_index;		/* 0x2C PIO EP index */
+	u16 reserved008;
+	u16 dma_cmd;		/* 0x30 DMA command */
+	u16 reserved009;
+	u32 dma_xfer_cnt;	/* 0x34 DMA transfer count */
+	u16 dcdmaconf;		/* 0x38 DC DMA config */
+	u16 reserved010;
+	u16 dmahw;		/* 0x3C DMA hardware parameters */
+	u8 reserved011[18];
+	u16 dma_ireason;	/* 0x50 DMA interrupt reason */
+	u16 reserved012;
+	u16 dma_ienable;	/* 0x54 DMA interrupt enable */
+	u16 reserved013;
+	u16 dma_ep;		/* 0x58 DMA EP index */
+	u8 reserved014[6];
+	u16 dma_data;		/* 0x60 DMA data register */
+	u16 reserved015;
+	u16 dma_burst_cnt;	/* 0x64 DMA burst count */
+	u16 reserved016[4];
+	u32 chip_id;		/* 0x70 chip ID */
+	u16 frameno;		/* 0x74 current frame number */
+	u16 reserved017;
+	u16 scratch;		/* 0x78 scratch register */
+	u16 reserved018;
+	u16 unlock;		/* 0x7C unlock register */
+	u16 reserved019;
+	u16 int_pulse_width;	/* 0x80 interrupt pulse width */
+	u16 reserved020;
+	u16 testmode;		/* 0x84 testmode */
+	u16 reserved021[3];
+
+	/* HC registers */
+	u32 usbcmd;		/* 0x8C USB command register */
+	u32 usbsts;		/* 0x90 USB status register */
+	u32 reserved022;
+	u32 frindex;		/* 0x98 frame index */
+	u32 configflag;		/* 0x9C config valid flag */
+	u32 portsc1;		/* 0xA0 Port 1 status register */
+	u16 iso_pt_done_map;	/* 0xA4 ISO transfer done map */
+	u16 iso_pt_skip_map;	/* 0xA6 ISO transfer skip map */
+	u16 iso_pt_last_ptd;	/* 0xA8 ISO transfer last  */
+	u16 int_pt_done_map;	/* 0xAA INT transfer done map */
+	u16 int_pt_skip_map;	/* 0xAC INT transfer skip map */
+	u16 int_pt_last_ptd;	/* 0xAE INT transfer last */
+	u16 atl_pt_done_map;	/* 0xB0 ATL transfer done map */
+	u16 atl_pt_skip_map;	/* 0xB2 ATL transfer skip map */
+	u16 atl_pt_last_ptd;	/* 0xB4 ATL transfer last map */
+	u16 hwmodectrl;		/* 0xB6 Hardware mode control */
+	u16 swreset;		/* 0xB8 software reset */
+	u16 hcbufferstatus;	/* 0xBA Host controller buffer status */
+	u32 hcdmaconfig;	/* 0xBC Host controller DMA config */
+	u32 atl_done_timeout;	/* 0xC0 ATL done timeout */
+	u16 memory;		/* 0xC2 HC memory pointer register */
+	u16 data;		/* 0xC6 HC data register */
+	u32 edge_int_count;	/* 0xC8 Edge interrupt configuration */
+	u16 dma_start_addr;	/* 0xCC DMA start address */
+	u16 reserved023;
+	u32 power_down_ctrl;	/* 0xD0 power down control */
+	u16 hc_interrupt;	/* 0xD4 HC interrupt status */
+	u16 hc_interrupt_enable;	/* 0xD6 HC interrupt enable */
+	u16 iso_irq_mask_or;	/* 0xD8 ISO IRQ mask OR */
+	u16 int_irq_mask_or;	/* 0xDA INT IRQ mask OR */
+	u16 atl_irq_mask_or;	/* 0xDC ATL IRQ mask OR */
+	u16 reserved024;
+
+	/* OTG registers */
+	u32 reserved025;
+	u32 otg_ctrl;		/* 0xE4 OTG control set & clear */
+	u16 otg_status;		/* 0xE8 OTG status set & clear */
+	u16 reserved026;
+	u32 otg_int_latch;	/* 0xEC OTG interrupt latch set & clear */
+	u32 otg_int_enable_fall;	/* 0xF0 OTG interrupt enable falling set & clear */
+	u32 otg_int_enable_rise;	/* 0xF4 OTG interrupt enable rising set & clear */
+	u32 otg_timer_lw;	/* 0xF8 OTG timer configuration low word */
+	u32 otg_timer_hw;	/* 0xFC OTG timer configuration high word */
+} __attribute__ ((aligned(1)));
+
+/** struct isp1763_controller - common structure containing interface and private data
+*/
+struct isp1763_controller {
+	int active;
+	struct device *device;
+	struct isp1763_regs *regs;
+	int (*do_irq) (struct isp1763_controller * ctrl, u32 flags);
+	int (*suspend) (struct isp1763_controller * ctrl);
+	int (*resume) (struct isp1763_controller * ctrl);
+	int (*probe) (struct isp1763_controller * ctrl);
+	int (*remove) (struct isp1763_controller * ctrl);
+
+	void *priv;
+};
+
+typedef void (*otgtimer_callback_t) (void *data);
+
+/**
+* struct isp1763_dev - main driver structure
+* @irq: interrupt number
+* @current_role: currently active role
+* @ctrl: array of controllers, role is index
+* @regs: pointer to memory mapped chip registers
+* @otgtimer_cb: registered OTG timer callback
+* @otgtimer_data: registered OTG timer callback context data
+* @devflags: saved hardware configuration flags
+* @dev: pointer to parent device structure
+*/
+struct isp1763_dev {
+	int irq;
+	int current_role;
+	struct isp1763_controller *ctrl[2];
+	struct isp1763_regs *regs;
+	otgtimer_callback_t otgtimer_cb;
+	void *otgtimer_data;
+	unsigned long devflags;
+	struct device *dev;
+};
+
+#define ISP_CHIP_ID	0x00176320l
+
+#define MASK_OTGINTEN_DP2_SRP		(1 << 11)
+#define MASK_OTGINTEN_P2_A_SESS_VALID	(1 << 10)
+#define MASK_OTGINTEN_TMR_TIMEOUT	(1 <<  9)
+#define MASK_OTGINTEN_B_SE0_SRP		(1 <<  8)
+#define MASK_OTGINTEN_B_SESS_END	(1 <<  7)
+#define MASK_OTGINTEN_BDIS_ACON		(1 <<  6)
+#define MASK_OTGINTEN_RMT_CONN		(1 <<  4)
+#define MASK_OTGINTEN_ID		(1 <<  3)
+#define MASK_OTGINTEN_DP_SRP		(1 <<  2)
+#define MASK_OTGINTEN_SESS_VALID	(1 <<  1)
+#define MASK_OTGINTEN_VBUS_VALID	(1 <<  0)
+
+#define MASK_HCINT_OTG		(1 << 10)
+#define MASK_HCINT_ISO		(1 <<  9)
+#define MASK_HCINT_ATL		(1 <<  8)
+#define MASK_HCINT_INT		(1 <<  7)
+#define MASK_HCINT_CLK_READY	(1 <<  6)
+#define MASK_HCINT_HCSUSP	(1 <<  5)
+#define MASK_HCINT_OPR_REG	(1 <<  4)
+#define MASK_HCINT_DMAEOT_INT	(1 <<  3)
+#define MASK_HCINT_SOFINT	(1 <<  1)
+#define MASK_HCINT_MSOFINT	(1 <<  0)
+
+#define MASK_OTGSTATUS_ID	0x0008
+
+#define MASK_INTCONFIG_INTLVL	0x02
+#define MASK_INTCONFIG_INTPOL	0x01
+
+#define USE_LEVEL	1
+#define USE_PULSE	0
+#define TRIG_HIGH	1
+#define TRIG_LOW	0
+
+#define MASK_HWMODECTRL_ID_PU_DISABLE		(1 << 12)
+#define MASK_HWMODECTRL_DEV_DMA			(1 << 11)
+#define MASK_HWMODECTRL_COMN_INT		(1 << 10)
+#define MASK_HWMODECTRL_COMN_DMA		(1 <<  9)
+#define MASK_HWMODECTRL_DACK_POL		(1 <<  6)
+#define MASK_HWMODECTRL_DREQ_POL		(1 <<  5)
+#define MASK_HWMODECTRL_DATA_BUS_WIDTH		(1 <<  4)
+#define MASK_HWMODECTRL_INTF_LOCK		(1 <<  3)
+#define MASK_HWMODECTRL_INTR_POLARITY		(1 <<  2)
+#define MASK_HWMODECTRL_INTR_EDGE		(1 <<  1)
+#define MASK_HWMODECTRL_GLOBAL_INT_ENABLE	(1 <<  0)
+
+#define MASK_OTGCTRL_HC2DIS		(1 << 15)
+#define MASK_OTGCTRL_TMR_SEL		(1 << 13)
+#define MASK_OTGCTRL_DISABLE		(1 << 10)
+#define MASK_OTGCTRL_SE0_EN		(1 <<  9)
+#define MASK_OTGCTRL_BDIS_ACON_EN	(1 <<  8)
+#define MASK_OTGCTRL_SW_SEL_HC_DC	(1 <<  7)
+#define MASK_OTGCTRL_VBUS_CHRG		(1 <<  6)
+#define MASK_OTGCTRL_VBUS_DISCHRG	(1 <<  5)
+#define MASK_OTGCTRL_VBUS_DRV		(1 <<  4)
+#define MASK_OTGCTRL_DM_PULLDOWN	(1 <<  2)
+#define MASK_OTGCTRL_DP_PULLDOWN	(1 <<  1)
+#define MASK_OTGCTRL_DP_PULLUP		(1 <<  0)
+
+#define MASK_MODE_DMACLOCK_ON	(1 << 9)
+#define MASK_MODE_VBUSSTAT	(1 << 8)
+#define MASK_MODE_CLKAON	(1 << 7)
+#define MASK_MODE_SNDRSU	(1 << 6)
+#define MASK_MODE_GOSUSP	(1 << 5)
+#define MASK_MODE_SFRESET	(1 << 4)
+#define MASK_MODE_GLINTENA	(1 << 3)
+#define MASK_MODE_WKUPCS	(1 << 2)
+
+#define MASK_DCINT_BRESET	(1 <<  0)
+#define MASK_DCINT_SOF		(1 <<  1)
+#define MASK_DCINT_PSOF		(1 <<  2)
+#define MASK_DCINT_SUSP		(1 <<  3)
+#define MASK_DCINT_RESUME	(1 <<  4)
+#define MASK_DCINT_HS_STAT	(1 <<  5)
+#define MASK_DCINT_DMA		(1 <<  6)
+#define MASK_DCINT_VBUS		(1 <<  7)
+#define MASK_DCINT_EP0SETUP	(1 <<  8)
+#define MASK_DCINT_EP0RX	(1 << 10)
+#define MASK_DCINT_EP0TX	(1 << 11)
+#define MASK_DCINT_EP1RX	(1 << 12)
+#define MASK_DCINT_EP1TX	(1 << 13)
+#define MASK_DCINT_EP2RX	(1 << 14)
+#define MASK_DCINT_EP2TX	(1 << 15)
+#define MASK_DCINT_EP3RX	(1 << 16)
+#define MASK_DCINT_EP3TX	(1 << 17)
+#define MASK_DCINT_EP4RX	(1 << 18)
+#define MASK_DCINT_EP4TX	(1 << 19)
+#define MASK_DCINT_EP5RX	(1 << 20)
+#define MASK_DCINT_EP5TX	(1 << 21)
+#define MASK_DCINT_EP6RX	(1 << 22)
+#define MASK_DCINT_EP6TX	(1 << 23)
+#define MASK_DCINT_EP7RX	(1 << 24)
+#define MASK_DCINT_EP7TX	(1 << 25)
+
+#define MASK_DCINT_EP_EVENT		(0x03FFF000)
+#define MASK_DCINTENABLE_RELEVANT	0x00000DF9
+#define UNLOCK_CODE			0xAA37
+
+#define MASK_CTRL_STALL		(1 << 0)
+#define MASK_CTRL_STATUS	(1 << 1)
+#define MASK_CTRL_DSEN		(1 << 2)
+#define MASK_CTRL_VENDP		(1 << 3)
+#define MASK_CTRL_CLBUF		(1 << 4)
+
+#define TOTAL_FIFO_SIZE	4096
+
+#define IDX_EP0_SETUP	(1 << 5)
+#define DIR_TX	1
+#define DIR_RX	0
+
+#define isp1763_readl(val)		readl(val)
+#define isp1763_writel(val, regs)	writel(val, regs)
+#define isp1763_readw(regs)		readw(regs)
+#define isp1763_writew(val,regs)	writew(val, regs)
+
+#define get_id_pin(dev)	(!!(isp1763_readw(&dev->regs->otg_status) & MASK_OTGSTATUS_ID))
+
+enum id_status {
+	ID_PIN_HOST = 0,
+	ID_PIN_DEVICE,
+};
+
+enum role {
+	ROLE_HOST = 0,
+	ROLE_DEVICE,
+};
+
+#define queue_printk(args...)	//printk(args)
+#define info_printk(args...)	//printk(args)
+#define warn_printk(args...)	//printk(args)
+#define error_printk(args...)	//printk(args)
+#define debug_printk(args...)	//printk(args)
+
+int isp1763_otg_timer_start(otgtimer_callback_t cb, unsigned long timeout,
+			    void *data);
+void isp1763_otg_timer_cancel(void);
+int isp1763_register_ctrl(struct isp1763_controller *ctrl, int role);
+void isp1763_unregister_ctrl(int role);
+
+#define static_role(x) ((x & ISP1763_FLAG_PORT1_ROLE_OTG) != ISP1763_FLAG_PORT1_ROLE_OTG)
+#define static_role_host(x) ((x & ISP1763_FLAG_PORT1_ROLE_OTG) == ISP1763_FLAG_PORT1_ROLE_HOST)
+
+#endif // ! __ISP1763_H__
diff --git a/drivers/usb/isp1763/isp1763_base.c b/drivers/usb/isp1763/isp1763_base.c
new file mode 100644
index 0000000..ac8ed71
--- /dev/null
+++ b/drivers/usb/isp1763/isp1763_base.c
@@ -0,0 +1,976 @@
+/*
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * Description:
+ *
+ * ISP1763 BASE driver - common initialization and OTG support
+ *
+ * Originally being written for a simple bus powered device, this
+ * driver does not support or implement HRP or SRP.
+ *
+ * (c) 2010 F.A. Voegel, Carangul.Tech 
+ * (c) 2010 I+ME ACTIA GmbH
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/workqueue.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+
+#ifdef CONFIG_PPC_OF
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/prom.h>
+#endif
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
+#include <linux/usb/isp1763.h>
+#include <linux/platform_device.h>
+
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/gpio.h>
+#include <asm/system.h>
+
+#include "isp1763.h"
+#include "isp1763_udc.h"
+#include "isp1763_hcd.h"
+
+static struct isp1763_dev *ispdev = NULL;
+
+
+#define DRIVER_NAME "isp1763"
+static const char driver_name[] = DRIVER_NAME;
+static const char driver_desc[] = "ISP 1763A device controller driver";
+static struct task_struct *otgtask = NULL;
+static char *role[] = { "HOST", "PERIPHERAL" };
+
+/** 
+ * otg_set_role - set/switch role between host and device
+ * @dev: isp1763_dev structure
+ * @role: role to switch to
+*/
+static void otg_set_role(struct isp1763_dev *dev, int role)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	if (role == ROLE_HOST) {
+		printk(KERN_ERR "%s() switching to HOST role\n", __FUNCTION__);
+		isp1763_writel(0xFFFF << 16, &dev->regs->otg_ctrl);
+		isp1763_writel(MASK_OTGCTRL_VBUS_DRV |
+			       MASK_OTGCTRL_VBUS_CHRG |
+			       MASK_OTGCTRL_DM_PULLDOWN |
+			       MASK_OTGCTRL_DP_PULLDOWN,
+			       &dev->regs->otg_ctrl);
+	}
+
+	else {
+		printk(KERN_ERR "%s() switching to PERIPHERAL role\n",
+		       __FUNCTION__);
+		isp1763_writel(0xFFFF << 16, &dev->regs->otg_ctrl);
+		isp1763_writel(MASK_OTGCTRL_SW_SEL_HC_DC |
+			       MASK_OTGCTRL_DP_PULLUP,
+			       &dev->regs->otg_ctrl);
+	}
+	dev->current_role = role;
+	local_irq_restore(flags);
+}
+
+static int isp1763_init_device(struct isp1763_dev *dev);
+
+/**
+* otg_role_switcher - role switch handling task
+* @data: pointer to struct isp1763_dev
+*/
+int otg_role_switcher(void *data)
+{
+	struct isp1763_dev *dev = data;
+	int prev_role = dev->current_role;
+	int id_pin;
+
+	set_current_state(TASK_RUNNING);
+	printk(KERN_ERR "%s starting up...\n", __FUNCTION__);
+
+	do {
+
+		if (kthread_should_stop()) {
+			set_current_state(TASK_RUNNING);
+			break;
+		}
+
+		schedule_timeout_interruptible(1);
+
+		if((id_pin = get_id_pin(dev)) == dev->current_role)
+			continue;
+
+		printk(KERN_ERR "%s: init role change %s => %s \n", __FUNCTION__, 
+		       role[dev->current_role], role[id_pin]);
+
+
+		if (dev->ctrl[dev->current_role]) {
+			if (dev->ctrl[dev->current_role]->suspend)
+				printk(KERN_ERR "=> SUSPEND %s\n", role[dev->current_role]);
+				dev->ctrl[dev->current_role]->suspend(dev->ctrl
+							      [dev->current_role]);
+		}
+
+		isp1763_init_device(dev);
+		otg_set_role(dev, id_pin);
+		
+		if (dev->ctrl[dev->current_role]) {
+			if (dev->ctrl[dev->current_role]->resume) {
+				printk(KERN_ERR "=> RESUME %s\n", role[dev->current_role]);
+				dev->ctrl[dev->current_role]->resume(dev->ctrl
+							    [dev->current_role]);
+				dev->ctrl[dev->current_role]->do_irq(dev->ctrl[dev->current_role], MASK_DCINT_VBUS);
+			}
+		}
+		prev_role = dev->current_role;
+
+	} while (1);
+	return 0;
+}
+
+/**
+ * otg_do_interrupt - Handle OTG interrupt
+ * @dev: pointer to isp1763_dev struct
+*/
+static void otg_do_interrupt(struct isp1763_dev *dev, u32 latch)
+{
+
+	if ((latch & MASK_OTGSTATUS_ID)
+	    && !static_role(dev->devflags)) {
+		wake_up_process(otgtask);
+	} else if (latch & MASK_OTGSTATUS_ID) {
+		printk(KERN_EMERG "Ignoring OTG event\n");
+	}
+	if (latch & MASK_OTGINTEN_TMR_TIMEOUT) {
+		if (dev->otgtimer_cb) {
+			dev->otgtimer_cb(dev->otgtimer_data);
+		}
+	}
+}
+
+void *mbar = NULL;
+
+
+/**
+ * isp1763 - main IRQ handler
+ * @data: pointer to struct isp1763_dev
+ */
+static irqreturn_t isp1763_irq(int irq, void *data)
+{
+	struct isp1763_dev *dev = (struct isp1763_dev *) data;
+	u32 flags_hc;
+	u32 flags_dc;
+	u32 flags_otg;
+	
+	isp1763_writew(UNLOCK_CODE, &dev->regs->unlock);
+	
+	flags_dc  = isp1763_readl(&dev->regs->dc_interrupt);
+	isp1763_writel(flags_dc, &dev->regs->dc_interrupt);
+	flags_hc  = isp1763_readw(&dev->regs->hc_interrupt);
+	isp1763_writew(flags_hc, &dev->regs->hc_interrupt);
+	flags_otg = isp1763_readl(&dev->regs->otg_int_latch);
+	isp1763_writel(0xFFFF0000, &dev->regs->otg_int_latch);
+	
+	//printk(KERN_DEBUG "%.8lx %.4x %.8lx\n", flags_dc, flags_hc, flags_otg);
+	
+	if(flags_otg)
+		otg_do_interrupt(dev, flags_otg);
+	if (dev->current_role == ROLE_HOST) {
+		if(flags_hc && dev->ctrl[ROLE_HOST] 
+				/*&& dev->ctrl[ROLE_HOST]->do_irq*/)
+			dev->ctrl[ROLE_HOST]->do_irq(dev->ctrl[ROLE_HOST], flags_hc);
+	} else {
+		if (flags_dc && dev->ctrl[ROLE_DEVICE]
+				/*&& dev->ctrl[ROLE_DEVICE]->do_irq*/)
+			dev->ctrl[ROLE_DEVICE]->do_irq(dev->ctrl[ROLE_DEVICE], flags_dc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static char *bustypes[] = {
+	"NAND", "Generic", "NOR", "SRAM"
+};
+
+/**
+ * isp1763_init_device - reset & initialize controller hardware
+ * @dev: pointer to struct isp1763_dev
+*/
+static int isp1763_init_device(struct isp1763_dev *dev)
+{
+	int i;
+	int ret = 0;
+	volatile u16 hwmode =
+	    MASK_HWMODECTRL_COMN_INT | MASK_HWMODECTRL_GLOBAL_INT_ENABLE;
+
+	mdelay(10);
+	/* Dummy reads to "stabilize host controller access" */
+	isp1763_readw(&dev->regs->chip_id);
+	isp1763_readw(&dev->regs->chip_id);
+	isp1763_readw(&dev->regs->chip_id);
+	mdelay(20);
+
+	if (dev->devflags & ISP1763_FLAG_BUS_WIDTH_8) {
+		hwmode |= MASK_HWMODECTRL_DATA_BUS_WIDTH;
+		printk(KERN_NOTICE "hwmode 8bit\n");
+	}
+	if (dev->devflags & ISP1763_FLAG_DACK_POL_HIGH) {
+		hwmode |= MASK_HWMODECTRL_DACK_POL;
+		printk(KERN_NOTICE "hwmode dack polarity high\n");
+	}
+	if (dev->devflags & ISP1763_FLAG_DREQ_POL_HIGH) {
+		hwmode |= MASK_HWMODECTRL_DREQ_POL;
+		printk(KERN_NOTICE "hwmode dreq polarity hight\n");
+	}
+	if (dev->devflags & ISP1763_FLAG_INTR_POL_HIGH) {
+		hwmode |= MASK_HWMODECTRL_INTR_POLARITY;
+		printk(KERN_NOTICE "hwmode int polarity high\n");
+	}
+	if (dev->devflags & ISP1763_FLAG_INTR_EDGE_TRIG) {
+		hwmode |= MASK_HWMODECTRL_INTR_EDGE;
+		printk(KERN_NOTICE "hwmode int edge triggered\n");
+	}
+	if (static_role(dev->devflags)) {
+		hwmode |= MASK_HWMODECTRL_ID_PU_DISABLE;
+		printk(KERN_NOTICE "hwmode pullup disable\n");
+	}
+	// DEBUG
+	hwmode &= ~MASK_HWMODECTRL_INTR_POLARITY;
+	hwmode &= ~MASK_HWMODECTRL_INTR_EDGE;
+	isp1763_writew(hwmode, &dev->regs->hwmodectrl);
+	isp1763_writew(hwmode, &dev->regs->hwmodectrl);
+
+	if (isp1763_readl(&dev->regs->chip_id) != ISP_CHIP_ID) {
+		printk(KERN_ERR
+		       "Error: ISP1763 chip ID wrong! (%.8lx, expected %.8lx)\n",
+		       (unsigned long) isp1763_readl(&dev->regs->chip_id),
+		       ISP_CHIP_ID);
+		ret = -EIO;
+		goto out;
+	}
+
+	printk(KERN_NOTICE "%s: %s mode, %i bit bus width\n", DRIVER_NAME,
+	       bustypes[(isp1763_readw(&dev->regs->swreset) >> 6) & 0x3],
+	       isp1763_readw(&dev->regs->hwmodectrl) & 
+				MASK_HWMODECTRL_DATA_BUS_WIDTH ? 8 : 16);
+
+	isp1763_writew(UNLOCK_CODE, &dev->regs->unlock);
+
+	printk(KERN_NOTICE "Resetting ISP1763...\n");
+	isp1763_writew(isp1763_readw(&dev->regs->swreset) | 1 | 8, 
+					       &dev->regs->swreset);
+	isp1763_writew(isp1763_readw(&dev->regs->mode) | MASK_MODE_SFRESET,
+		       &dev->regs->mode);
+	udelay(50);
+	isp1763_writew(isp1763_readw(&dev->regs->mode) &
+		       ~MASK_MODE_SFRESET, &dev->regs->mode);
+	udelay(1000);
+
+	isp1763_writew(UNLOCK_CODE, &dev->regs->unlock);
+
+	if (isp1763_readl(&dev->regs->chip_id) != ISP_CHIP_ID) {
+		printk(KERN_ERR
+		       "Error: ISP1763 chip ID wrong after reset! (%.8lx, expected %.8lx)\n",
+		       (unsigned long) isp1763_readl(&dev->regs->chip_id),
+		       ISP_CHIP_ID);
+		ret = -EIO;
+		goto out;
+	}
+	printk(KERN_NOTICE "Reset done\n");
+
+	isp1763_writew(hwmode, &dev->regs->hwmodectrl);
+	isp1763_writew(MASK_HCINT_OTG, &dev->regs->hc_interrupt_enable);
+
+	/*
+	  You'd think there was one single way to configure the interrupt for
+	  this chip, but no. You have to independently configure both
+	  peripheral and host controller IRQs despite the settings obviously
+	  having to be the same...
+	*/
+	isp1763_writew(0xFC |
+		       ((ispdev->devflags & ISP1763_FLAG_INTR_EDGE_TRIG) >> 1)
+		       | (ispdev->devflags & ISP1763_FLAG_INTR_POL_HIGH),
+		       &dev->regs->icr);
+
+	/* data line test */
+	for (i = 0; i < 0x10000; i++) {
+		u16 readback = 0;
+		isp1763_writew(i, &dev->regs->scratch);
+		readback = isp1763_readw(&dev->regs->scratch);
+		if (readback != i) {
+			printk(KERN_ERR
+			       "ERROR: Scratch register write test failed: scratch=%.4x != i=%.4x!\n",
+			       readback, i);
+			ret = -EIO;
+			/* 
+			* Don't break here, let us see the other errors too,
+			* helps with diagnosis
+			*/
+		}
+	}
+
+	isp1763_writew(1, &dev->regs->int_pulse_width);
+	
+out:
+	return ret;
+}
+
+
+/** otg_setup - initialize ISP1763 OTG component
+  * @dev: isp1763_dev struture
+*/
+static int otg_setup(struct isp1763_dev *dev)
+{
+	int role = ROLE_DEVICE;
+
+	if (static_role_host(dev->devflags))
+		role = ROLE_HOST;
+	else
+		role = get_id_pin(dev);
+
+	printk(KERN_NOTICE "ISP1783 using %s mode\n",
+				static_role(dev->devflags) ? 
+					(static_role_host(dev->devflags) ?
+						"host" : "device") : "otg");
+
+	otg_set_role(dev, role);
+
+	enable_glint(dev);
+	isp1763_writel(0xFFFF0000, &dev->regs->otg_int_enable_fall);
+	isp1763_writel(0xFFFF0000, &dev->regs->otg_int_enable_rise);
+	isp1763_writel(0xFFFFFFFF, &dev->regs->otg_int_latch);
+
+	isp1763_writel(MASK_OTGINTEN_ID | MASK_OTGINTEN_SESS_VALID |
+		       MASK_OTGINTEN_VBUS_VALID |
+		       MASK_OTGINTEN_TMR_TIMEOUT,
+		       &dev->regs->otg_int_enable_fall);
+
+	isp1763_writel(MASK_OTGINTEN_ID | MASK_OTGINTEN_SESS_VALID |
+		       MASK_OTGINTEN_VBUS_VALID |
+		       MASK_OTGINTEN_TMR_TIMEOUT,
+		       &dev->regs->otg_int_enable_rise);
+
+	ispdev->current_role = role;
+
+	return role;
+}
+
+/**
+* isp1763_otg_timer_start - start OTG timer in ISP1763
+* @cb: callback function
+* @timeout: timeout in multiples of 10 microseconds
+* @data: callback context data
+*/
+int isp1763_otg_timer_start(otgtimer_callback_t cb, 
+				unsigned long timeout,
+				void *data)
+{
+	if (isp1763_readl(&ispdev->regs->otg_timer_hw) & 0x8000)
+		return -EBUSY;
+
+	ispdev->otgtimer_cb = cb;
+	ispdev->otgtimer_data = data;
+
+	isp1763_writel(0xFFFF0000, &ispdev->regs->otg_timer_lw);
+	isp1763_writel(0xFFFF0000, &ispdev->regs->otg_timer_hw);
+
+	isp1763_writel(timeout & 0xFFFF, &ispdev->regs->otg_timer_lw);
+	isp1763_writel(((timeout >> 16) & 0x7FFF) | 0x8000,
+			       &ispdev->regs->otg_timer_hw);
+	return 0;
+}
+EXPORT_SYMBOL(isp1763_otg_timer_start);
+
+/**
+* isp1763_otg_timer_cancel - cancel running OTG timer
+*
+* No harm done if timer isn't actually running
+*/
+void isp1763_otg_timer_cancel(void)
+{
+	isp1763_writel(isp1763_readl(&ispdev->regs->otg_timer_hw) & 0x7FFF,
+		       &ispdev->regs->otg_timer_hw);
+	ispdev->otgtimer_cb = NULL;
+	ispdev->otgtimer_data = NULL;
+}
+EXPORT_SYMBOL(isp1763_otg_timer_cancel);
+
+/**
+* isp1763_register_ctrl - register a host or device controller driver
+* @ctrl: controller structure to register
+* @role: role for which to register driver
+*
+* Register a driver for a host or peripheral controller driver. After 
+* registering, the driver's probe function will be called, and if OTG is
+* enabled and the current role matches the driver, it's resume function
+* will be called as well.
+*/
+int isp1763_register_ctrl(struct isp1763_controller *ctrl, int role)
+{
+	if (!ispdev)
+		return -ENODEV;
+
+	if (ispdev->ctrl[role]) {
+		printk(KERN_ERR
+		       "Trying to register driver for role %s which already has driver registered\n",
+		       role == ROLE_HOST ? "host" : "device");
+		return -EBUSY;
+	}
+	ispdev->ctrl[role] = ctrl;
+
+	ctrl->device = ispdev->dev;
+	ctrl->regs = ispdev->regs;
+
+	if (!ctrl->probe(ctrl) < 0)
+		return -ENODEV;
+
+	/* driver for the current role? resume right now. */
+	if (role == ispdev->current_role) {
+		printk(KERN_ERR "Registered %s as active driver\n",
+		       role == ROLE_HOST ? "host" : "device");
+		ctrl->resume(ctrl);
+	} else
+		printk(KERN_ERR "Registered %s as suspended driver\n",
+		       role == ROLE_HOST ? "host" : "device");
+
+	return 0;
+}
+EXPORT_SYMBOL(isp1763_register_ctrl);
+
+/**
+ * isp1763_unregister_ctrl - unregister a previously registered controller
+ * @role: role the controller filled
+ */
+void isp1763_unregister_ctrl(int _role)
+{
+	if(!ispdev->ctrl[_role]) {
+		printk(KERN_ERR "Failed to unregister driver for '%s' controller\n", role[_role]);
+		return;
+	}
+	
+	ispdev->ctrl[_role]->suspend(ispdev->ctrl[_role]);
+	if(ispdev->ctrl[_role]->remove)
+		ispdev->ctrl[_role]->remove(ispdev->ctrl[_role]);
+	ispdev->ctrl[_role] = NULL;
+}
+EXPORT_SYMBOL(isp1763_unregister_ctrl);
+
+/*******************************************/
+/* module init / exit / probe related code */
+/*******************************************/
+
+
+/**
+* isp1763_common_init - common module init
+* @dev: struct device pointer passed from probe function
+* @devflags: hardware flags as determined by calling probe function
+* @irq: IRQ number to use
+* @res: ISP1763 register memory resource pointer
+* @res_len: length of resources
+*
+* Called by all probe functions to initialize the hardware and setup data
+* structures.
+*/
+static int isp1763_common_init(struct device *dev,
+				unsigned long devflags,
+				int irq,
+				struct resource *res, 
+				resource_size_t res_len)
+{
+	int ret = 0;
+
+	ispdev = kmalloc(sizeof(struct isp1763_dev), GFP_ATOMIC);
+	if (!ispdev) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	memset(ispdev, 0, sizeof(struct isp1763_dev));
+
+	printk(KERN_ERR "devflags passed by probe: 0x%.8lx\n", devflags);
+
+	ispdev->irq = irq;
+	ispdev->devflags = devflags;
+	ispdev->regs = ioremap(res->start, res->end - res->start);
+
+	if (request_irq(ispdev->irq, isp1763_irq, IRQF_SHARED, "ISP1763 IRQ",
+							(void *) ispdev)) {
+		ret = -EIO;
+		goto error_free;
+	}
+
+	isp1763_init_device(ispdev);
+	otg_setup(ispdev);
+
+	/* Only start role switch thread if we're doing OTG */
+	if (!static_role(ispdev->devflags)) {
+		otgtask = kthread_create(otg_role_switcher, ispdev,
+							"isp1763otg");
+		if(otgtask)
+			wake_up_process(otgtask);
+	}
+	
+	ispdev->dev = dev;
+
+	printk(KERN_INFO "%s initialization successfully\n", DRIVER_NAME);
+
+	return 0;
+      error_free:
+	kfree(ispdev);
+      error:
+	return ret;
+}
+
+#ifdef CONFIG_PPC_OF
+static int __devinit isp1763_of_probe(struct of_device *dev,
+					const struct of_device_id *match)
+{
+	struct device_node *np = dev->node;
+	const u32 *prop;
+	int ret = 0;
+	unsigned long devflags = 0;
+	struct resource *res;
+	struct resource memory;
+	int irq;
+	resource_size_t res_len;
+	const char *port1_role = NULL;
+
+	prop = of_get_property(np, "bus-width", NULL);
+	if (prop && *prop == 8)
+		devflags |= ISP1763_FLAG_BUS_WIDTH_8;
+
+	port1_role = of_get_property(np, "port1-role", NULL);
+	if (port1_role) {
+		printk(KERN_ERR "PORT1 OTG setting: %s\n", port1_role);
+		if (strcmp(port1_role, "otg") == 0)
+			devflags |= ISP1763_FLAG_PORT1_ROLE_OTG;
+		else if (strcmp(port1_role, "host") == 0)
+			devflags |= ISP1763_FLAG_PORT1_ROLE_HOST;
+		else
+			devflags |= ISP1763_FLAG_PORT1_ROLE_GADGET;
+	} else
+		devflags |= ISP1763_FLAG_PORT1_ROLE_GADGET;
+
+	if (of_get_property(np, "dack-polarity-high", NULL) != NULL)
+		devflags |= ISP1763_FLAG_DACK_POL_HIGH;
+
+	if (of_get_property(np, "dreq-polarity-high", NULL) != NULL)
+		devflags |= ISP1763_FLAG_DREQ_POL_HIGH;
+
+	if (of_get_property(np, "intr-polarity-high", NULL) != NULL)
+		devflags |= ISP1763_FLAG_INTR_POL_HIGH;
+
+	if (of_get_property(np, "intr-edge-trig", NULL) != NULL)
+		devflags |= ISP1763_FLAG_INTR_EDGE_TRIG;
+
+	irq = irq_of_parse_and_map(np, 0);
+
+	if (irq == NO_IRQ) {
+		pr_warning("of_isp1763: no interrupt!\n");
+		ret = -EIO;
+		goto error_out;
+	}
+
+	ret = of_address_to_resource(np, 0, &memory);
+	if (ret) {
+		pr_warning("of_isp1763: Memory resource not available\n");
+		goto error_out;
+	}
+
+	res_len = resource_size(&memory);
+
+	res =
+	    request_mem_region(memory.start, res_len, dev_name(&dev->dev));
+	if (!res) {
+		pr_warning
+		    ("of_isp1763: Cannot reserve the memory resource\n");
+		return -EBUSY;
+	}
+	/* FIXME: what's the idea behind this? */
+	res_len = memory.end - memory.start + 1;
+
+	return isp1763_common_init(&dev->dev, devflags, irq, res, res_len);
+
+      error_out:
+	return ret;
+}
+
+static int __devexit isp1763_of_remove(struct of_device *ofdev)
+{
+	if (ispdev->irq)
+		free_irq(ispdev->irq, (void *) ispdev);
+
+	kfree(ispdev);
+	return 0;
+}
+
+
+static struct of_device_id __devinitdata isp1763_match[] = {
+	{
+	 .compatible = "st,usb-isp1763",
+	 },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, isp1763_match);
+
+static struct of_platform_driver isp1763_driver = {
+	.name = (char *) driver_name,
+	.match_table = isp1763_match,
+	.probe = isp1763_of_probe,
+	.remove = __devexit_p(isp1763_of_remove),
+};
+
+
+#endif
+
+
+/******************************************************************/
+/* PCI PROBE                                                      */
+/******************************************************************/
+
+#ifdef CONFIG_PCI
+static int __devinit isp1763_pci_probe(struct pci_dev *dev,
+				       const struct pci_device_id *id)
+{
+	u8 latency, limit;
+	__u32 reg_data;
+	int retry_count;
+	unsigned int devflags = 0;
+	int ret_status = 0;
+
+	resource_size_t pci_mem_phy0;
+	resource_size_t memlength;
+
+	u8 __iomem *chip_addr;
+	u8 __iomem *iobase;
+	resource_size_t nxp_pci_io_base;
+	resource_size_t iolength;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (pci_enable_device(dev) < 0)
+		return -ENODEV;
+
+	if (!dev->irq)
+		return -ENODEV;
+
+	/* Grab the PLX PCI mem maped port start address we need  */
+	nxp_pci_io_base = pci_resource_start(dev, 0);
+	iolength = pci_resource_len(dev, 0);
+
+	if (!request_mem_region
+	    (nxp_pci_io_base, iolength, "ISP1763 IO MEM")) {
+		printk(KERN_ERR "request region #1\n");
+		return -EBUSY;
+	}
+
+	iobase = ioremap_nocache(nxp_pci_io_base, iolength);
+	if (!iobase) {
+		printk(KERN_ERR "ioremap #1\n");
+		ret_status = -ENOMEM;
+		goto cleanup1;
+	}
+	/* Grab the PLX PCI shared memory of the ISP 1763 we need  */
+	pci_mem_phy0 = pci_resource_start(dev, 3);
+	memlength = pci_resource_len(dev, 3);
+	if (memlength < 0xffff) {
+		printk(KERN_ERR
+		       "memory length for this resource is wrong\n");
+		ret_status = -ENOMEM;
+		goto cleanup2;
+	}
+
+	if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
+		printk(KERN_ERR "host controller already in use\n");
+		ret_status = -EBUSY;
+		goto cleanup2;
+	}
+
+	/* map available memory */
+	chip_addr = ioremap_nocache(pci_mem_phy0, memlength);
+	if (!chip_addr) {
+		printk(KERN_ERR "Error ioremap failed\n");
+		ret_status = -ENOMEM;
+		goto cleanup3;
+	}
+
+	/* bad pci latencies can contribute to overruns */
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+	if (latency) {
+		pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
+		if (limit && limit < latency)
+			pci_write_config_byte(dev, PCI_LATENCY_TIMER,
+					      limit);
+	}
+
+	/* Try to check whether we can access Scratch Register of
+	 * Host Controller or not. The initial PCI access is retried until
+	 * local init for the PCI bridge is completed
+	 */
+	retry_count = 20;
+	reg_data = 0;
+	while ((reg_data != 0xFACE) && retry_count) {
+		/*by default host is in 16bit mode, so
+		 * io operations at this stage must be 16 bit
+		 * */
+		writel(0xface, chip_addr + HC_SCRATCH_REG);
+		udelay(100);
+		reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
+		retry_count--;
+	}
+
+	iounmap(chip_addr);
+
+	/* Host Controller presence is detected by writing to scratch register
+	 * and reading back and checking the contents are same or not
+	 */
+	if (reg_data != 0xFACE) {
+		dev_err(&dev->dev, "scratch register mismatch %x\n",
+			reg_data);
+		ret_status = -ENOMEM;
+		goto cleanup3;
+	}
+
+	pci_set_master(dev);
+
+	/* configure PLX PCI chip to pass interrupts */
+#define PLX_INT_CSR_REG 0x68
+	reg_data = readl(iobase + PLX_INT_CSR_REG);
+	reg_data |= 0x900;
+	writel(reg_data, iobase + PLX_INT_CSR_REG);
+
+	dev->dev.dma_mask = NULL;
+	/*  hcd = isp1763_register(pci_mem_phy0, memlength, dev->irq,
+	   IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
+	   devflags); */
+	if (isp1763_common_init
+	    (&dev->dev, devflags, dev->irq, (void *) pci_mem_phy0,
+	     memlength) < 0)
+		goto cleanup3;
+
+	/* done with PLX IO access */
+	iounmap(iobase);
+	release_mem_region(nxp_pci_io_base, iolength);
+
+	return 0;
+
+      cleanup3:
+	release_mem_region(pci_mem_phy0, memlength);
+      cleanup2:
+	iounmap(iobase);
+      cleanup1:
+	release_mem_region(nxp_pci_io_base, iolength);
+	return ret_status;
+}
+
+static void isp1763_pci_remove(struct pci_dev *dev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = pci_get_drvdata(dev);
+	pci_disable_device(dev);
+}
+
+static void isp1763_pci_shutdown(struct pci_dev *dev)
+{
+	printk(KERN_ERR "ips1763_pci_shutdown\n");
+}
+
+static const struct pci_device_id isp1763_plx[] = {
+	{
+	 .class = PCI_CLASS_BRIDGE_OTHER << 8,
+	 .class_mask = ~0,
+	 .vendor = PCI_VENDOR_ID_PLX,
+	 .device = 0x5406,
+	 .subvendor = PCI_VENDOR_ID_PLX,
+	 .subdevice = 0x9054,
+	 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, isp1763_plx);
+
+static struct pci_driver isp1763_pci_driver = {
+	.name = "isp1763",
+	.id_table = isp1763_plx,
+	.probe = isp1763_pci_probe,
+	.remove = isp1763_pci_remove,
+	.shutdown = isp1763_pci_shutdown,
+};
+#endif
+
+
+/******************************************************************/
+/* PLATFORM DEVICE PROBE                                          */
+/******************************************************************/
+
+static int __devexit isp1763_plat_remove(struct platform_device *pdev)
+{
+	struct resource *mem_res;
+	resource_size_t mem_size;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mem_size = resource_size(mem_res);
+	release_mem_region(mem_res->start, mem_size);
+
+	return 0;
+}
+
+static int __devinit isp1763_plat_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *mem_res;
+	struct resource *irq_res;
+	resource_size_t mem_size;
+	struct isp1763_platform_data *priv = pdev->dev.platform_data;
+	unsigned int devflags = 0;
+	unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		pr_warning("isp1763: Memory resource not available\n");
+		ret = -ENODEV;
+		goto out;
+	}
+	mem_size = resource_size(mem_res);
+	if (!request_mem_region(mem_res->start, mem_size, "isp1763")) {
+		pr_warning
+		    ("isp1763: Cannot reserve the memory resource\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		pr_warning("isp1763: IRQ resource not available\n");
+		return -ENODEV;
+	}
+	irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;
+
+	if (priv) {
+		if (priv->bus_width_8)
+			devflags |= ISP1763_FLAG_BUS_WIDTH_8;
+		if (priv->port1_otg)
+			devflags |= ISP1763_FLAG_PORT1_ROLE_OTG;
+		else
+			/* TODO: add pure PERIPHERAL support */
+			devflags |= ISP1763_FLAG_PORT1_ROLE_HOST;	
+		if (priv->dack_polarity_high)
+			devflags |= ISP1763_FLAG_DACK_POL_HIGH;
+		if (priv->dreq_polarity_high)
+			devflags |= ISP1763_FLAG_DREQ_POL_HIGH;
+		if (priv->intr_polarity_high)
+			devflags |= ISP1763_FLAG_INTR_POL_HIGH;
+		if (priv->intr_edge_trigger)
+			devflags |= ISP1763_FLAG_INTR_EDGE_TRIG;
+	}
+
+	if (isp1763_common_init(&pdev->dev, devflags, irq_res->start, 
+						mem_res, mem_size) < 0)
+		goto cleanup;
+
+	pr_info("ISP1763 USB device initialised\n");
+	return ret;
+
+      cleanup:
+	release_mem_region(mem_res->start, mem_size);
+      out:
+	return ret;
+}
+
+static struct platform_driver isp1763_plat_driver = {
+	.probe = isp1763_plat_probe,
+	.remove = __devexit_p(isp1763_plat_remove),
+	.driver = {
+		.name = "isp1763",
+	},
+};
+
+static int __init isp1763_init(void)
+{
+	int ret = 0;
+	int any_ret = -ENODEV;
+
+	mbar = ioremap(0x80000000, 0x10000);
+	
+	ret = platform_driver_register(&isp1763_plat_driver);
+	if (!ret)
+		any_ret = 0;
+	
+#ifdef CONFIG_PPC_OF
+	ret = of_register_platform_driver(&isp1763_driver);
+	if(!ret)
+		any_ret = 0;
+#endif
+
+#ifdef CONFIG_PCI
+	ret = pci_register_driver(&isp1763_pci_driver);
+	if (!ret)
+		any_ret = 0;
+#endif
+
+	return any_ret;
+}
+
+static void __exit isp1763_exit(void)
+{
+#ifdef CONFIG_PPC_OF
+	of_unregister_platform_driver(&isp1763_driver);
+#endif
+
+#ifdef CONFIG_PCI
+	pci_unregister_driver(&isp1763_pci_driver);
+#endif
+
+	platform_driver_unregister(&isp1763_plat_driver);
+}
+
+module_init(isp1763_init);
+module_exit(isp1763_exit);
+
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_AUTHOR("F.A. Voegel, Carangul.Tech");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/isp1763/isp1763_hcd.c b/drivers/usb/isp1763/isp1763_hcd.c
new file mode 100644
index 0000000..e4f9384
--- /dev/null
+++ b/drivers/usb/isp1763/isp1763_hcd.c
@@ -0,0 +1,2395 @@
+/*
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * Description:
+ *
+ * isp1763 Host Controller Driver
+ *
+ * Based on isp1760 driver by Sebastian Siewior <bigeasy@xxxxxxxxxxxxx> and
+ * pehci hcd from ST-Ericsson wired support <wired.support@xxxxxxxxxxxxxx>
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <asm/unaligned.h>
+#include <asm/cacheflush.h>
+
+#include "../core/hcd.h"
+#include "../core/hub.h"
+#include "isp1763.h"
+#include "isp1763_hcd.h"
+
+
+static struct kmem_cache *qtd_cachep;
+static struct kmem_cache *qh_cachep;
+
+static inline struct isp1763_hcd *hcd_to_priv(struct usb_hcd *hcd)
+{
+	return (struct isp1763_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *priv_to_hcd(struct isp1763_hcd *priv)
+{
+	return container_of((void *) priv, struct usb_hcd, hcd_priv);
+}
+
+/**
+ * isp1763_read_mem - read HC descriptor+payload memory
+ * @hcd: the usb_hcd handle (needed to know the device base address)
+ * @srcaddr: the source (start) address where the data reside on the hcd device
+ * @dstpt: the destination where we want to store the data
+ * @len: the length of the data
+ */
+static void isp1763_read_mem(struct usb_hcd *hcd,
+			     u16 srcaddr, u16 * dstptr, u32 len)
+{
+	if (!hcd || !dstptr || len <= 0) {
+		printk(KERN_ERR "ERROR: hcd: %p dstptr: %p len: %d\n",
+		       hcd, dstptr, len);
+		return;
+	}
+
+	/* Write the starting device address to the hcd memory register */
+	isp1763_writew(srcaddr, hcd->regs + HC_MEMORY_REG);
+
+	/* As long there are at least 16-bit to read ... */
+	while (len >= 2) {
+		*dstptr = __raw_readw(hcd->regs + HC_DATA_REG);
+		len -= 2;	/* Adjust len */
+		dstptr++;	/* Set dest data pointer to its next address */
+	}
+
+	/* If there are no more bytes to read, return */
+	if (len <= 0)
+		return;
+
+	*((u8 *) dstptr) = (u8) (readw(hcd->regs + HC_DATA_REG) & 0xFF);
+}
+
+/**
+ * isp1763_write_mem - write HC descriptor+payload memory
+ * @hcd: the usb_hcd handle (needed to know the device base address)
+ * @srcptr: the source of the data we want to write
+ * @dstaddr: the desitnation (start) address where data is to be written
+ * @len: the length of the data
+ */
+static void isp1763_write_mem(struct usb_hcd *hcd,
+			      u16 * dataptr, u16 dstaddr, u32 len)
+{
+	if (!hcd || !dataptr || len <= 0) {
+		printk(KERN_ERR "ERROR: hcd: %p dataptr: %p len: %d\n",
+		       hcd, dataptr, len);
+		return;
+	}
+
+	isp1763_writew(dstaddr, hcd->regs + HC_MEMORY_REG);
+	while (len >= 2) {
+		__raw_writew(*dataptr, hcd->regs + HC_DATA_REG);
+		dataptr++;
+		len -= 2;
+	}
+
+	if (len <= 0) {
+		return;
+	}
+
+	writew(*((u8 *) dataptr), hcd->regs + HC_DATA_REG);
+}
+
+/**
+ * init_memory - initialize HC memory management
+ * @priv: HC private data
+ *
+ * memory management of the 20kb payload on the chip from 0x1000 to 0x5fff
+ */
+static void init_memory(struct isp1763_hcd *priv)
+{
+	int i;
+	u32 payload;
+
+	payload = 0x1000;
+	for (i = 0; i < BLOCK_1_NUM; i++) {
+		priv->memory_pool[i].start = payload;
+		priv->memory_pool[i].size = BLOCK_1_SIZE;
+		priv->memory_pool[i].free = 1;
+		payload += priv->memory_pool[i].size;
+	}
+
+
+	for (i = BLOCK_1_NUM; i < BLOCK_1_NUM + BLOCK_2_NUM; i++) {
+		priv->memory_pool[i].start = payload;
+		priv->memory_pool[i].size = BLOCK_2_SIZE;
+		priv->memory_pool[i].free = 1;
+		payload += priv->memory_pool[i].size;
+	}
+
+
+	for (i = BLOCK_1_NUM + BLOCK_2_NUM; i < BLOCKS; i++) {
+		priv->memory_pool[i].start = payload;
+		priv->memory_pool[i].size = BLOCK_3_SIZE;
+		priv->memory_pool[i].free = 1;
+		payload += priv->memory_pool[i].size;
+	}
+
+	BUG_ON(payload - priv->memory_pool[i - 1].size > PAYLOAD_SIZE);
+}
+
+/**
+ * alloc_mem - allocate HC-internal memory for payload data
+ * @priv: HC private data
+ * @size: size of memory chunk to allocate
+ *
+ * memory management of the 20kb payload on the chip from 0x1000 to 0x5fff
+ */
+static u32 alloc_mem(struct isp1763_hcd *priv, u32 size)
+{
+	int i;
+
+	if (!size)
+		return ISP1763_NULL_POINTER;
+
+	for (i = 0; i < BLOCKS; i++) {
+		if (priv->memory_pool[i].size >= size &&
+		    priv->memory_pool[i].free) {
+
+			priv->memory_pool[i].free = 0;
+			return priv->memory_pool[i].start;
+		}
+	}
+
+	printk(KERN_ERR
+	       "ISP1763 MEM: can not allocate %d bytes of memory\n",
+	       size);
+	printk(KERN_ERR "Current memory map:\n");
+	for (i = 0; i < BLOCKS; i++) {
+		printk(KERN_ERR "Pool %2d size %4d status: %d\n",
+		       i,
+		       priv->memory_pool[i].size,
+		       priv->memory_pool[i].free);
+	}
+	/* XXX maybe -ENOMEM could be possible */
+	BUG();
+	return 0;
+}
+
+/**
+ * free_mem - free previously allocated HC-internal memory
+ * @priv: HC private data
+ * @mem: offset of memory block to free
+ */
+static void free_mem(struct isp1763_hcd *priv, u32 mem)
+{
+	int i;
+
+	if (mem == ISP1763_NULL_POINTER)
+		return;
+
+	for (i = 0; i < BLOCKS; i++) {
+		if (priv->memory_pool[i].start == mem) {
+
+			BUG_ON(priv->memory_pool[i].free);
+
+			priv->memory_pool[i].free = 1;
+			return;
+		}
+	}
+
+	printk(KERN_ERR "Trying to free not-here-allocated memory :%08x\n",
+	       mem);
+	BUG();
+}
+
+/**
+ * isp1763_init_regs - initialize registers
+ * @hcd: the usb_hcd handle
+ */
+static void isp1763_init_regs(struct usb_hcd *hcd)
+{
+	isp1763_writew(0, hcd->regs + HC_BUFFER_STATUS_REG);
+
+	isp1763_writew(NO_TRANSFER_ACTIVE,
+		       hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+	isp1763_writew(NO_TRANSFER_ACTIVE,
+		       hcd->regs + HC_INT_PTD_SKIPMAP_REG);
+	isp1763_writew(NO_TRANSFER_ACTIVE,
+		       hcd->regs + HC_ISO_PTD_SKIPMAP_REG);
+
+	isp1763_writew((u16) (~NO_TRANSFER_ACTIVE),
+		       hcd->regs + HC_ATL_PTD_DONEMAP_REG);
+	isp1763_writew((u16) (~NO_TRANSFER_ACTIVE),
+		       hcd->regs + HC_INT_PTD_DONEMAP_REG);
+	isp1763_writew((u16) (~NO_TRANSFER_ACTIVE),
+		       hcd->regs + HC_ISO_PTD_DONEMAP_REG);
+}
+
+/**
+ * handshake - wait for bitmask to be set in register
+ * @priv: hc private data
+ * @ptr: pointer to memory-mapped register to read
+ * @mask: mask to apply to register (bitwise AND)
+ * @done: expected result of masked register contents
+ * @usec: timeout in microseconds
+ *
+ * WARNING!
+ * This handshake() function should only be used for 32-bit registers
+ * All the accesses so far seems okay for isp1763, since they only access the
+ * standard EHCI regs, which are all 32-bit.
+ */
+static int handshake(struct isp1763_hcd *priv, void __iomem * ptr,
+		     u32 mask, u32 done, int usec)
+{
+	u32 result;
+
+	do {
+		result = isp1763_readl(ptr);
+		if (result == ~0)
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay(1);
+		usec--;
+	} while (usec > 0);
+
+	return -ETIMEDOUT;
+}
+
+/**
+ * ehci_reset - reset EHCI registers
+ * @priv: HC private data
+ *
+ * reset a non-running (STS_HALT == 1) controller
+*/
+static int ehci_reset(struct isp1763_hcd *priv)
+{
+	int retval;
+	struct usb_hcd *hcd = priv_to_hcd(priv);
+	u32 command = isp1763_readl(hcd->regs + HC_USBCMD);
+
+	command |= CMD_RESET;
+	isp1763_writel(command, hcd->regs + HC_USBCMD);
+	hcd->state = HC_STATE_HALT;
+	priv->next_statechange = jiffies;
+	retval = handshake(priv, hcd->regs + HC_USBCMD,
+			   CMD_RESET, 0, 250 * 1000);
+	return retval;
+}
+
+static void qh_destroy(struct isp1763_qh *qh)
+{
+	BUG_ON(!list_empty(&qh->qtd_list));
+	kmem_cache_free(qh_cachep, qh);
+}
+
+static struct isp1763_qh *isp1763_qh_alloc(struct isp1763_hcd *priv,
+							gfp_t flags)
+{
+	struct isp1763_qh *qh;
+
+	qh = kmem_cache_zalloc(qh_cachep, flags);
+	if (!qh)
+		return qh;
+
+	INIT_LIST_HEAD(&qh->qtd_list);
+	qh->priv = priv;
+	return qh;
+}
+
+/**
+ * priv_init - one-time init, only for memory state
+ * @hcd: HC structure
+ */
+static int priv_init(struct usb_hcd *hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	u32 hcc_params = HCC_HARDCODE;	/* Use hardcoded value */
+
+	spin_lock_init(&priv->lock);
+
+	/*
+	 * hw default: 1K periodic list heads, one per frame.
+	 * periodic_size can shrink by USBCMD update if hcc_params allows.
+	 */
+	priv->periodic_size = DEFAULT_I_TDPS;
+
+	/* full frame cache */
+	if (HCC_ISOC_CACHE(hcc_params))
+		priv->i_thresh = 8;
+	else			/* N microframes cached */
+		priv->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
+
+	return 0;
+}
+
+/**
+ * isp1763_hc_setup - setup host controller
+ * @hcd: host controller structure
+ *
+ * Fill registers with initial values and reset EHCI controller, enable IRQs
+ */
+static int isp1763_hc_setup(struct usb_hcd *hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	int result;
+
+	isp1763_init_regs(hcd);
+
+	isp1763_writew(isp1763_readw(hcd->regs + HC_RESET_REG) |
+						SW_RESET_RESET_HC,
+		       hcd->regs + HC_RESET_REG);
+	result = ehci_reset(priv);
+	if (result)
+		return result;
+
+	isp1763_info(priv, "bus width: %d\n",
+		     (priv->devflags & ISP1763_FLAG_BUS_WIDTH_8) ? 8 : 16);
+
+	isp1763_dbg(priv, "hwmode: 0x%04X\n",
+		    isp1763_readw(hcd->regs + HC_HW_MODE_CTRL));
+
+	isp1763_writew(INTERRUPT_ENABLE_MASK,
+		       hcd->regs + HC_INTERRUPT_REG);
+	isp1763_writew(INTERRUPT_ENABLE_MASK,
+		       hcd->regs + HC_INTERRUPT_ENABLE);
+
+	/* This chip does not have HCSPARAMS register, use a hardcoded value. */
+	priv->hcs_params = HCS_HARDCODE;
+
+	return priv_init(hcd);
+}
+
+static void isp1763_init_maps(struct usb_hcd *hcd)
+{
+	/* Set last maps, for iso its only 1, else 16 tds bitmap */
+	isp1763_writew(0x8000, hcd->regs + HC_ATL_PTD_LASTPTD_REG);
+	isp1763_writew(0x8000, hcd->regs + HC_INT_PTD_LASTPTD_REG);
+	isp1763_writew(0x0001, hcd->regs + HC_ISO_PTD_LASTPTD_REG);
+}
+
+static void isp1763_enable_interrupts(struct usb_hcd *hcd)
+{
+	isp1763_writew(0x0000, hcd->regs + HC_ATL_IRQ_MASK_AND_REG);
+	isp1763_writew(0x0000, hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+	isp1763_writew(0x0000, hcd->regs + HC_INT_IRQ_MASK_AND_REG);
+	isp1763_writew(0x0000, hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+	isp1763_writew(0x0000, hcd->regs + HC_ISO_IRQ_MASK_AND_REG);
+	isp1763_writew(0xffff, hcd->regs + HC_ISO_IRQ_MASK_OR_REG);
+}
+
+static int isp1763_run(struct usb_hcd *hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	int retval;
+	u16 temp;
+	u32 command;
+	u32 chipid;
+
+
+	hcd->uses_new_polling = 1;
+	hcd->poll_rh = 0;
+	hcd->state = HC_STATE_RUNNING;
+
+	isp1763_enable_interrupts(hcd);
+	temp = isp1763_readw(hcd->regs + HC_HW_MODE_CTRL);
+	isp1763_writew(temp | HW_GLOBAL_INTR_EN,
+		       hcd->regs + HC_HW_MODE_CTRL);
+
+	command = isp1763_readl(hcd->regs + HC_USBCMD);
+	command &= ~(CMD_LRESET | CMD_RESET);
+	command |= CMD_RUN;
+	isp1763_writel(command, hcd->regs + HC_USBCMD);
+
+	retval = handshake(priv, hcd->regs + HC_USBCMD, CMD_RUN, CMD_RUN,
+			   250 * 1000);
+	if (retval)
+		return retval;
+
+	down_write(&ehci_cf_port_reset_rwsem);
+	isp1763_writel(FLAG_CF, hcd->regs + HC_CONFIGFLAG);
+
+	retval = handshake(priv, hcd->regs + HC_CONFIGFLAG, FLAG_CF, FLAG_CF,
+								250 * 1000);
+	up_write(&ehci_cf_port_reset_rwsem);
+	if (retval)
+		return retval;
+
+	chipid = isp1763_readl(hcd->regs + HC_CHIP_ID_REG);
+	isp1763_info(priv, "USB ISP %04x HW rev. %d started\n",
+		     ((chipid & 0x00ffff00) >> 8), (chipid & 0x000000ff));
+
+	/* PTD Register Init Part 2, Step 28 */
+	/* enable INTs */
+	isp1763_init_maps(hcd);
+
+	return 0;
+}
+
+static inline u32 base_to_chip(u32 base)
+{
+	return ((base - 0x400) >> 3);
+}
+
+static void transform_into_atl(struct isp1763_hcd *priv,
+			       struct isp1763_qh *qh,
+			       struct isp1763_qtd *qtd, struct urb *urb,
+			       u32 payload, struct ptd *ptd)
+{
+	u32 dw0;
+	u32 dw1;
+	u32 dw2;
+	u32 dw3;
+	u32 maxpacket;
+	u32 multi;
+	u32 pid_code;
+	u32 rl = RL_COUNTER;
+	u32 nak = NAK_COUNTER;
+
+	/* according to 3.6.2, max packet len can not be > 0x400 */
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	multi = 1 + ((maxpacket >> 11) & 0x3);
+	maxpacket &= 0x7ff;
+
+	dw0 = PTD_VALID;
+	dw0 |= PTD_LENGTH(qtd->length);
+	dw0 |= PTD_MAXPACKET(maxpacket);
+	dw0 |= PTD_ENDPOINT(usb_pipeendpoint(urb->pipe));
+	dw1 = usb_pipeendpoint(urb->pipe) >> 1;
+
+	dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(urb->pipe));
+
+	pid_code = qtd->packet_type;
+	dw1 |= PTD_PID_TOKEN(pid_code);
+
+	if (usb_pipebulk(urb->pipe))
+		dw1 |= PTD_TRANS_BULK;
+	else if (usb_pipeint(urb->pipe))
+		dw1 |= PTD_TRANS_INT;
+
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/* split transaction */
+
+		dw1 |= PTD_TRANS_SPLIT;
+		if (urb->dev->speed == USB_SPEED_LOW)
+			dw1 |= PTD_SE_USB_LOSPEED;
+
+		dw1 |= PTD_PORT_NUM(urb->dev->ttport);
+		dw1 |= PTD_HUB_NUM(urb->dev->tt->hub->devnum);
+
+		/* SE bit for Split INT transfers */
+		if (usb_pipeint(urb->pipe) &&
+		    (urb->dev->speed == USB_SPEED_LOW))
+			dw1 |= 2 << 16;
+
+		dw3 = 0;
+		rl = 0;
+		nak = 0;
+	} else {
+		dw0 |= PTD_MULTI(multi);
+		if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe))
+			dw3 = qh->ping;
+		else
+			dw3 = 0;
+	}
+
+	dw2 = 0;
+	dw2 |= PTD_DATA_START_ADDR(base_to_chip(payload));
+	dw2 |= PTD_RL_CNT(rl);
+	dw3 |= PTD_NAC_CNT(nak);
+
+	if (usb_pipecontrol(urb->pipe))
+		dw3 |= PTD_DATA_TOGGLE(qtd->toggle);
+	else
+		dw3 |= qh->toggle;
+
+
+	dw3 |= PTD_ACTIVE;
+	dw3 |= PTD_CERR(ERR_COUNTER);
+
+	memset((void*)ptd + 4*sizeof(u32), 0, 4*sizeof(u32));
+
+	ptd->dw0 = cpu_to_le32(dw0);
+	ptd->dw1 = cpu_to_le32(dw1);
+	ptd->dw2 = cpu_to_le32(dw2);
+	ptd->dw3 = cpu_to_le32(dw3);
+}
+
+static void transform_add_int(struct isp1763_hcd *priv,
+			      struct isp1763_qh *qh,
+			      struct isp1763_qtd *qtd, struct urb *urb,
+			      u32 payload, struct ptd *ptd)
+{
+	u32 maxpacket;
+	u32 multi;
+	u32 numberofusofs;
+	u32 i;
+	u32 usofmask, usof;
+	u32 period;
+
+	maxpacket =
+	    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	multi = 1 + ((maxpacket >> 11) & 0x3);
+	maxpacket &= 0x7ff;
+	/* length of the data per uframe */
+	maxpacket = multi * maxpacket;
+
+	numberofusofs = urb->transfer_buffer_length / maxpacket;
+	if (urb->transfer_buffer_length % maxpacket)
+		numberofusofs += 1;
+
+	usofmask = 1;
+	usof = 0;
+	for (i = 0; i < numberofusofs; i++) {
+		usof |= usofmask;
+		usofmask <<= 1;
+	}
+
+	/* FIXME: Vendor code checks for:
+	 * if (urb->dev->speed != USB_SPEED_HIGH && usb_pipeint(urb->pipe))
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/* split */
+		ptd->dw5 = cpu_to_le32(0x1c);
+
+		if (qh->period >= 32)
+			period = qh->period / 2;
+		else
+			period = qh->period;
+
+	} else {
+
+		if (qh->period >= 8)
+			period = qh->period / 8;
+		else
+			period = qh->period;
+
+		if (period >= 32)
+			period = 16;
+
+		if (qh->period >= 8) {
+			/* millisecond period */
+			period = (period << 3);
+		} else {
+			/* usof based transfers */
+			/* minimum 4 usofs */
+			usof = 0x11;
+		}
+	}
+
+	ptd->dw2 |= cpu_to_le32(period);
+	ptd->dw4 = cpu_to_le32(usof);
+}
+
+static void transform_into_int(struct isp1763_hcd *priv,
+			       struct isp1763_qh *qh,
+			       struct isp1763_qtd *qtd, struct urb *urb,
+			       u32 payload, struct ptd *ptd)
+{
+	transform_into_atl(priv, qh, qtd, urb, payload, ptd);
+	transform_add_int(priv, qh, qtd, urb, payload, ptd);
+}
+
+static int qtd_fill(struct isp1763_qtd *qtd, void *databuffer, size_t len,
+		    u32 token)
+{
+	int count;
+
+	qtd->data_buffer = databuffer;
+	qtd->packet_type = GET_QTD_TOKEN_TYPE(token);
+	qtd->toggle = GET_DATA_TOGGLE(token);
+
+	if (len > HC_ATL_PL_SIZE)
+		count = HC_ATL_PL_SIZE;
+	else
+		count = len;
+
+	qtd->length = count;
+	return count;
+}
+
+static int check_error(struct ptd *ptd)
+{
+	int error = 0;
+	u32 dw3;
+
+	dw3 = le32_to_cpu(ptd->dw3);
+	if (dw3 & DW3_HALT_BIT) {
+		error = -EPIPE;
+
+		if (dw3 & DW3_ERROR_BIT)
+			pr_err("error bit is set in DW3\n");
+	}
+
+	if (dw3 & DW3_QTD_ACTIVE) {
+		printk(KERN_ERR "transfer active bit is set DW3\n");
+		printk(KERN_ERR "nak counter: %d, rl: %d\n",
+		       (dw3 >> 19) & 0xf,
+		       (le32_to_cpu(ptd->dw2) >> 25) & 0xf);
+	}
+
+	return error;
+}
+
+static void check_int_err_status(u32 dw4)
+{
+	u32 i;
+
+	dw4 >>= 8;
+
+	for (i = 0; i < 8; i++) {
+		switch (dw4 & 0x7) {
+		case INT_UNDERRUN:
+			printk(KERN_ERR "ERROR: under run , %d\n", i);
+			break;
+
+		case INT_EXACT:
+			printk(KERN_ERR "ERROR: transaction error, %d\n",
+			       i);
+			break;
+
+		case INT_BABBLE:
+			printk(KERN_ERR "ERROR: babble error, %d\n", i);
+			break;
+		}
+		dw4 >>= 3;
+	}
+}
+
+static void enqueue_one_qtd(struct isp1763_qtd *qtd,
+			    struct isp1763_hcd *priv, u32 payload)
+{
+	u32 token;
+	struct usb_hcd *hcd = priv_to_hcd(priv);
+
+	token = qtd->packet_type;
+
+	if (qtd->length && (qtd->length <= HC_ATL_PL_SIZE)) {
+		switch (token) {
+		case IN_PID:
+			break;
+		case OUT_PID:
+		case SETUP_PID:
+			isp1763_write_mem(hcd, (u16 *) qtd->data_buffer,
+					  (u16) payload, qtd->length);
+		}
+	}
+}
+
+static void enqueue_one_atl_qtd(u32 atl_regs, u32 payload,
+				struct isp1763_hcd *priv,
+				struct isp1763_qh *qh, struct urb *urb,
+				u32 slot, struct isp1763_qtd *qtd)
+{
+	struct ptd ptd;
+	struct usb_hcd *hcd = priv_to_hcd(priv);
+
+
+	transform_into_atl(priv, qh, qtd, urb, payload, &ptd);
+	isp1763_write_mem(hcd, (u16 *) & ptd, atl_regs, sizeof(ptd));
+	enqueue_one_qtd(qtd, priv, payload);
+
+	priv->atl_ints[slot].urb = urb;
+	priv->atl_ints[slot].qh = qh;
+	priv->atl_ints[slot].qtd = qtd;
+	priv->atl_ints[slot].data_buffer = qtd->data_buffer;
+	priv->atl_ints[slot].payload = payload;
+	qtd->status |= URB_ENQUEUED | URB_TYPE_ATL;
+	qtd->status |= slot << 16;
+}
+
+static void enqueue_one_int_qtd(u32 int_regs, u32 payload,
+				struct isp1763_hcd *priv,
+				struct isp1763_qh *qh, struct urb *urb,
+				u32 slot, struct isp1763_qtd *qtd)
+{
+	struct ptd ptd;
+	struct usb_hcd *hcd = priv_to_hcd(priv);
+
+
+	transform_into_int(priv, qh, qtd, urb, payload, &ptd);
+	isp1763_write_mem(hcd, (u16 *) & ptd, int_regs, sizeof(ptd));
+	enqueue_one_qtd(qtd, priv, payload);
+
+	priv->int_ints[slot].urb = urb;
+	priv->int_ints[slot].qh = qh;
+	priv->int_ints[slot].qtd = qtd;
+	priv->int_ints[slot].data_buffer = qtd->data_buffer;
+	priv->int_ints[slot].payload = payload;
+	qtd->status |= URB_ENQUEUED | URB_TYPE_INT;
+	qtd->status |= slot << 16;
+}
+
+static void enqueue_an_ATL_packet(struct usb_hcd *hcd,
+				  struct isp1763_qh *qh,
+				  struct isp1763_qtd *qtd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	u16 skip_map, or_map;
+	u16 queue_entry;
+	u32 slot;
+	u32 atl_regs, payload;
+	u16 buffstatus;
+
+	/*
+	 * When this function is called from the interrupt handler to enqueue
+	 * a follow-up packet, the SKIP register gets written and read back
+	 * almost immediately. With ISP1763, this register requires a delay of
+	 * 100ns between a write and subsequent read (see section 15.1.2).
+	 */
+	ndelay(100);
+
+	skip_map = isp1763_readw(hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+	BUG_ON(!skip_map);
+
+	/* 32-bit to 16-bit adjustment */
+	slot = __ffs((u32) skip_map);
+	BUG_ON(slot > 16);	/* skip_map is only 16 bit */
+
+	queue_entry = 1 << slot;
+
+	atl_regs = ATL_REGS_OFFSET + slot * sizeof(struct ptd);
+
+	payload = alloc_mem(priv, qtd->length);
+
+	enqueue_one_atl_qtd(atl_regs, payload, priv, qh, qtd->urb, slot,
+			    qtd);
+
+	or_map = isp1763_readw(hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+	or_map |= queue_entry;
+	isp1763_writew(or_map, hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+
+	skip_map &= ~queue_entry;
+	isp1763_writew(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+
+	buffstatus = isp1763_readw(hcd->regs + HC_BUFFER_STATUS_REG);
+	buffstatus |= ATL_BUFFER;
+	isp1763_writew(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
+}
+
+static void enqueue_an_INT_packet(struct usb_hcd *hcd,
+				  struct isp1763_qh *qh,
+				  struct isp1763_qtd *qtd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	u16 skip_map, or_map;
+	u16 queue_entry;
+	u32 slot;
+	u32 int_regs, payload;
+	u16 buffstatus;
+
+	/*
+	 * When this function is called from the interrupt handler to enqueue
+	 * a follow-up packet, the SKIP register gets written and read back
+	 * almost immediately. With ISP1763, this register requires a delay of
+	 * 100ns between a write and subsequent read (see section 15.1.2).
+	 */
+	ndelay(100);
+
+	skip_map = isp1763_readw(hcd->regs + HC_INT_PTD_SKIPMAP_REG);
+	BUG_ON(!skip_map);
+
+	/* 32-bit to 16-bit adjustment */
+	slot = __ffs((u32) skip_map);
+	BUG_ON(slot > 16);	/* skip_map is only 16 bit */
+
+	queue_entry = 1 << slot;
+
+	int_regs = INT_REGS_OFFSET + slot * sizeof(struct ptd);
+
+	payload = alloc_mem(priv, qtd->length);
+
+	enqueue_one_int_qtd(int_regs, payload, priv, qh, qtd->urb, slot,
+			    qtd);
+
+	or_map = isp1763_readw(hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+	or_map |= queue_entry;
+	isp1763_writew(or_map, hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+
+	skip_map &= ~queue_entry;
+	isp1763_writew(skip_map, hcd->regs + HC_INT_PTD_SKIPMAP_REG);
+
+	buffstatus = isp1763_readw(hcd->regs + HC_BUFFER_STATUS_REG);
+	buffstatus |= INT_BUFFER;
+	isp1763_writew(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
+}
+
+static void isp1763_urb_done(struct isp1763_hcd *priv, struct urb *urb,
+			     int status)
+__releases(priv->lock)
+__acquires(priv->lock)
+{
+	if (!urb->unlinked) {
+		if (status == -EINPROGRESS)
+			status = 0;
+	}
+
+	if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_CONTROL) {
+		void *ptr;
+		for (ptr = urb->transfer_buffer;
+		     ptr < urb->transfer_buffer + urb->transfer_buffer_length;
+		     ptr += PAGE_SIZE)
+			flush_dcache_page(virt_to_page(ptr));
+	}
+
+	/* complete() can reenter this HCD */
+	usb_hcd_unlink_urb_from_ep(priv_to_hcd(priv), urb);
+	spin_unlock(&priv->lock);
+	usb_hcd_giveback_urb(priv_to_hcd(priv), urb, status);
+	spin_lock(&priv->lock);
+}
+
+static void isp1763_qtd_free(struct isp1763_qtd *qtd)
+{
+	kmem_cache_free(qtd_cachep, qtd);
+}
+
+static struct isp1763_qtd *clean_this_qtd(struct isp1763_qtd *qtd)
+{
+	struct isp1763_qtd *tmp_qtd;
+
+	tmp_qtd = qtd->hw_next;
+	list_del(&qtd->qtd_list);
+	isp1763_qtd_free(qtd);
+	return tmp_qtd;
+}
+
+/*
+ * Remove this QTD from the QH list and free its memory. If this QTD
+ * isn't the last one than remove also his successor(s).
+ * Returns the QTD which is part of an new URB and should be enqueued.
+ */
+static struct isp1763_qtd *clean_up_qtdlist(struct isp1763_qtd *qtd)
+{
+	struct isp1763_qtd *tmp_qtd;
+	int last_one;
+
+	do {
+		tmp_qtd = qtd->hw_next;
+		last_one = qtd->status & URB_COMPLETE_NOTIFY;
+		list_del(&qtd->qtd_list);
+		isp1763_qtd_free(qtd);
+		qtd = tmp_qtd;
+	} while (!last_one && qtd);
+
+	return qtd;
+}
+
+/* NOTE:
+ * It seems that setting up two memory banks in the isp1760's code
+ * is merely for convenience's sake; right now trying with:
+ *
+ * 1. Setup up the memory register for the PTD
+ * 2. Copy PTD from device to the driver's PTD copy
+ * 3. Process the PTD data
+ * (later on)
+ * 4. Setup the memory register for the payload
+ * 5. Copy payload from device to the driver's payload space
+ *
+ * There may be a gotcha here if the isp1760 is taking advantage of the ISP_BANK
+ * ability to automatically increment the address per bank on consecutive access
+ * to the same bank; It does not seem so because atl_regs & payload are
+ * reassigned at every while(done_map) iteration.
+ */
+static void do_atl_int(struct usb_hcd *usb_hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(usb_hcd);
+	u16 done_map, skip_map;
+	struct ptd ptd;
+	struct urb *urb = NULL;
+	u32 atl_regs_base;
+	u32 atl_regs;
+	u32 queue_entry;
+	u32 payload;
+	u32 length;
+	u16 or_map;
+	u32 status = -EINVAL;
+	int error;
+	struct isp1763_qtd *qtd;
+	struct isp1763_qh *qh;
+	u32 rl;
+	u32 nakcount;
+	u16 buffstatus;
+
+	done_map = isp1763_readw(usb_hcd->regs + HC_ATL_PTD_DONEMAP_REG);
+
+	/* NOTE: Move inside while (done_map) at the beginning and remove the
+	 * skip_map read at the end of the loop?
+	 */
+	skip_map = isp1763_readw(usb_hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+
+	or_map = isp1763_readw(usb_hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+	or_map &= ~done_map;
+	isp1763_writew(or_map, usb_hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+
+	atl_regs_base = ATL_REGS_OFFSET;
+
+	while (done_map) {
+		u32 dw1;
+		u32 dw2;
+		u32 dw3;
+
+		status = 0;
+
+		/* 32-bit to 16-bit done_map adjustment */
+		queue_entry = __ffs((u32) done_map);
+		BUG_ON(queue_entry > 16);
+
+		done_map &= ~(1 << queue_entry);
+		skip_map |= 1 << queue_entry;
+
+		atl_regs =
+		    atl_regs_base + (queue_entry * sizeof(struct ptd));
+
+		urb = priv->atl_ints[queue_entry].urb;
+		qtd = priv->atl_ints[queue_entry].qtd;
+		qh = priv->atl_ints[queue_entry].qh;
+		payload = priv->atl_ints[queue_entry].payload;
+
+		if (!qh) {
+			printk(KERN_ERR "qh is 0\n");
+			continue;
+		}
+
+		// memset(&ptd, 0, sizeof(ptd));
+		isp1763_read_mem(usb_hcd, (u16) atl_regs, (u16 *) & ptd,
+				 sizeof(u32)*4/*sizeof(ptd)*/);
+
+		dw1 = le32_to_cpu(ptd.dw1);
+		dw2 = le32_to_cpu(ptd.dw2);
+		dw3 = le32_to_cpu(ptd.dw3);
+		rl = (dw2 >> 25) & 0x0f;
+		nakcount = (dw3 >> 19) & 0xf;
+
+		/* Transfer Error, *but* active and no HALT -> reload */
+		if (unlikely((dw3 & DW3_ERROR_BIT) && (dw3 & DW3_QTD_ACTIVE) &&
+		    !(dw3 & DW3_HALT_BIT))) {
+
+			/* according to priv code, we have to
+			 * reload this one if trasfered bytes != requested bytes
+			 * else act like everything went smooth..
+			 * XXX This just doesn't feel right and hasn't
+			 * triggered so far.
+			 */
+
+			length = PTD_XFERRED_LENGTH(dw3);
+			printk(KERN_ERR
+			       "Should reload now.... transfered %d "
+			       "of %zu\n", length, qtd->length);
+			BUG();
+		}
+
+		if (unlikely(!nakcount && (dw3 & DW3_QTD_ACTIVE))) {
+			printk(KERN_NOTICE
+			       "Reloading ptd %p/%p... qh %p read: "
+			       "%d of %zu done: %08x cur: %08x\n", qtd,
+			       urb, qh, PTD_XFERRED_LENGTH(dw3),
+			       qtd->length, done_map, (1 << queue_entry));
+
+			/* RL counter = ERR counter */
+			dw3 &= ~(0xf << 19);
+			dw3 |= rl << 19;
+			dw3 &= ~(3 << (55 - 32));
+			dw3 |= ERR_COUNTER << (55 - 32);
+
+			/*
+			 * It is not needed to write skip map back because it
+			 * is unchanged. Just make sure that this entry is
+			 * unskipped once it gets written to the HW.
+			 */
+			skip_map &= ~(1 << queue_entry);
+			or_map = isp1763_readw(usb_hcd->regs +
+					       HC_ATL_IRQ_MASK_OR_REG);
+			or_map |= 1 << queue_entry;
+			isp1763_writew(or_map, usb_hcd->regs +
+				       HC_ATL_IRQ_MASK_OR_REG);
+
+			/* FIXME: Why isp1760 need 2 write_mem back-to-back? */
+			ptd.dw3 = cpu_to_le32(dw3);
+			isp1763_write_mem(usb_hcd, (u16 *) & ptd, atl_regs,
+					  sizeof(ptd));
+
+			ptd.dw0 |= cpu_to_le32(PTD_VALID);
+			isp1763_write_mem(usb_hcd, (u16 *) & ptd, atl_regs,
+					  sizeof(u32)/*sizeof(ptd)*/);
+
+			buffstatus = isp1763_readw(usb_hcd->regs +
+						   HC_BUFFER_STATUS_REG);
+			buffstatus |= ATL_BUFFER;
+			isp1763_writew(buffstatus, usb_hcd->regs +
+				       HC_BUFFER_STATUS_REG);
+			continue;
+		}
+
+		error = check_error(&ptd);
+		if (unlikely(error)) {
+			status = error;
+			priv->atl_ints[queue_entry].qh->toggle = 0;
+			priv->atl_ints[queue_entry].qh->ping = 0;
+			urb->status = -EPIPE;
+		} else {
+			if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+				priv->atl_ints[queue_entry].qh->toggle =
+				    dw3 & (1 << 25);
+				priv->atl_ints[queue_entry].qh->ping =
+				    dw3 & (1 << 26);
+			}
+		}
+
+		length = PTD_XFERRED_LENGTH(dw3);
+		if (length) {
+			switch (DW1_GET_PID(dw1)) {
+			case IN_PID:
+				isp1763_read_mem(usb_hcd, (u16) payload,
+						 priv->
+						 atl_ints[queue_entry].
+						 data_buffer, length);
+			case OUT_PID:
+				urb->actual_length += length;
+			case SETUP_PID:
+				break;
+			}
+		}
+
+		priv->atl_ints[queue_entry].data_buffer = NULL;
+		priv->atl_ints[queue_entry].urb = NULL;
+		priv->atl_ints[queue_entry].qtd = NULL;
+		priv->atl_ints[queue_entry].qh = NULL;
+
+		free_mem(priv, payload);
+
+		isp1763_writew(skip_map, usb_hcd->regs +
+			       HC_ATL_PTD_SKIPMAP_REG);
+
+		if (unlikely(urb->status == -EPIPE)) {
+			/* HALT was received */
+
+			qtd = clean_up_qtdlist(qtd);
+			isp1763_urb_done(priv, urb, urb->status);
+		} else if (usb_pipebulk(urb->pipe)
+			   && (length < qtd->length)) {
+			/* short BULK received */
+
+			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+				urb->status = -EREMOTEIO;
+				isp1763_dbg(priv,
+					    "short bulk, %d instead %zu "
+					    "with URB_SHORT_NOT_OK flag.\n",
+					    length, qtd->length);
+			}
+
+			if (urb->status == -EINPROGRESS)
+				urb->status = 0;
+			qtd = clean_up_qtdlist(qtd);
+			isp1763_urb_done(priv, urb, urb->status);
+
+		} else if (qtd->status & URB_COMPLETE_NOTIFY) {
+			/* that was the last qtd of that URB */
+
+			if (urb->status == -EINPROGRESS)
+				urb->status = 0;
+
+			qtd = clean_this_qtd(qtd);
+			isp1763_urb_done(priv, urb, urb->status);
+
+		} else {
+			/* next QTD of this URB */
+
+			qtd = clean_this_qtd(qtd);
+			BUG_ON(!qtd);
+		}
+
+		if (qtd)
+			enqueue_an_ATL_packet(usb_hcd, qh, qtd);
+
+		/* NOTE: Move to the beginning of while (done_map) loop? */
+		skip_map = isp1763_readw(usb_hcd->regs +
+					 HC_ATL_PTD_SKIPMAP_REG);
+	}
+}
+
+
+/* NOTE:
+ * It seems that setting up two memory banks in the isp1760's code
+ * is merely for convenience's sake; right now trying with:
+ *
+ * 1. Setup up the memory register for the PTD
+ * 2. Copy PTD from device to the driver's PTD copy
+ * 3. Process the PTD data
+ * (later on)
+ * 4. Setup the memory register for the payload
+ * 5. Copy payload from device to the driver's payload space
+ *
+ * There may be a gotcha here if the isp1760 is taking advantage of the ISP_BANK
+ * ability to automatically increment the address per bank on consecutive access
+ * to the same bank; It does not seem so because atl_regs & payload are
+ * reassigned at every while(done_map) iteration.
+ */
+static void do_intl_int(struct usb_hcd *usb_hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(usb_hcd);
+	u16 done_map, skip_map;
+	struct ptd ptd;
+	struct urb *urb = NULL;
+	u32 int_regs;
+	u32 int_regs_base;
+	u32 payload;
+	u32 length;
+	u16 or_map;
+	int error;
+	u32 queue_entry;
+	struct isp1763_qtd *qtd;
+	struct isp1763_qh *qh;
+
+	done_map = isp1763_readw(usb_hcd->regs + HC_INT_PTD_DONEMAP_REG);
+
+	/* NOTE: Move inside while (done_map) at the beginning and remove the
+	 * skip_map read at the end of the loop?
+	 */
+	skip_map = isp1763_readw(usb_hcd->regs + HC_INT_PTD_SKIPMAP_REG);
+
+	or_map = isp1763_readw(usb_hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+	or_map &= ~done_map;
+	isp1763_writew(or_map, usb_hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+
+	int_regs_base = INT_REGS_OFFSET;
+
+	while (done_map) {
+		u32 dw1;
+		u32 dw3;
+
+		/* 32-bit to 16-bit done_map adjustment */
+		queue_entry = __ffs((u32) done_map);
+		BUG_ON(queue_entry > 16);
+
+		done_map &= ~(1 << queue_entry);
+		skip_map |= 1 << queue_entry;
+
+		int_regs =
+		    int_regs_base + (queue_entry * sizeof(struct ptd));
+		urb = priv->int_ints[queue_entry].urb;
+		qtd = priv->int_ints[queue_entry].qtd;
+		qh = priv->int_ints[queue_entry].qh;
+		payload = priv->int_ints[queue_entry].payload;
+
+		if (!qh) {
+			printk(KERN_ERR "(INT) qh is 0\n");
+			continue;
+		}
+		isp1763_read_mem(usb_hcd, (u16) int_regs, (u16 *) & ptd,
+				 sizeof(ptd));
+
+		dw1 = le32_to_cpu(ptd.dw1);
+		dw3 = le32_to_cpu(ptd.dw3);
+		check_int_err_status(le32_to_cpu(ptd.dw4));
+
+		error = check_error(&ptd);
+		if (error) {
+#if 0
+			printk(KERN_ERR "Error in %s().\n", __func__);
+			printk(KERN_ERR "IN dw0: %08x dw1: %08x dw2: %08x "
+			       "dw3: %08x dw4: %08x dw5: %08x dw6: "
+			       "%08x dw7: %08x\n",
+			       ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3,
+			       ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7);
+#endif
+			urb->status = -EPIPE;
+			priv->int_ints[queue_entry].qh->toggle = 0;
+			priv->int_ints[queue_entry].qh->ping = 0;
+
+		} else {
+			priv->int_ints[queue_entry].qh->toggle =
+			    dw3 & (1 << 25);
+			priv->int_ints[queue_entry].qh->ping =
+			    dw3 & (1 << 26);
+		}
+
+		if (urb->dev->speed != USB_SPEED_HIGH)
+			length = PTD_XFERRED_LENGTH_LO(dw3);
+		else
+			length = PTD_XFERRED_LENGTH(dw3);
+
+		if (length) {
+			switch (DW1_GET_PID(dw1)) {
+			case IN_PID:
+
+				isp1763_read_mem(usb_hcd, (u16) payload,
+						 priv->
+						 int_ints[queue_entry].
+						 data_buffer, length);
+			case OUT_PID:
+
+				urb->actual_length += length;
+
+			case SETUP_PID:
+				break;
+			}
+		}
+
+		priv->int_ints[queue_entry].data_buffer = NULL;
+		priv->int_ints[queue_entry].urb = NULL;
+		priv->int_ints[queue_entry].qtd = NULL;
+		priv->int_ints[queue_entry].qh = NULL;
+
+		isp1763_writew(skip_map, usb_hcd->regs +
+			       HC_INT_PTD_SKIPMAP_REG);
+		free_mem(priv, payload);
+
+		if (urb->status == -EPIPE) {
+			/* HALT received */
+
+			qtd = clean_up_qtdlist(qtd);
+			isp1763_urb_done(priv, urb, urb->status);
+
+		} else if (qtd->status & URB_COMPLETE_NOTIFY) {
+
+			if (urb->status == -EINPROGRESS)
+				urb->status = 0;
+
+			qtd = clean_this_qtd(qtd);
+			isp1763_urb_done(priv, urb, urb->status);
+
+		} else {
+			/* next QTD of this URB */
+
+			qtd = clean_this_qtd(qtd);
+			BUG_ON(!qtd);
+		}
+
+		if (qtd)
+			enqueue_an_INT_packet(usb_hcd, qh, qtd);
+
+		/* NOTE: Move to the beginning of while (done_map) loop? */
+		skip_map = isp1763_readw(usb_hcd->regs +
+					 HC_INT_PTD_SKIPMAP_REG);
+	}
+}
+
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * GIT COMMIT: 1b9a38bfa6e664ff02511314f5586d711c83cc91 adapted
+ * "USB: EHCI: fix handling of unusual interrupt intervals"
+ */
+static struct isp1763_qh *qh_make(struct isp1763_hcd *priv,
+				  struct urb *urb, gfp_t flags)
+{
+	struct isp1763_qh *qh;
+	int is_input, type;
+
+	qh = isp1763_qh_alloc(priv, flags);
+	if (!qh)
+		return qh;
+
+	/*
+	 * init endpoint/device data for this QH
+	 */
+	is_input = usb_pipein(urb->pipe);
+	type = usb_pipetype(urb->pipe);
+
+	if (type == PIPE_INTERRUPT) {
+
+		if (urb->dev->speed == USB_SPEED_HIGH) {
+
+			qh->period = urb->interval >> 3;
+			if (qh->period == 0 && urb->interval != 1) {
+				/* NOTE interval 2 or 4 uframes could work.
+				 * But interval 1 scheduling is simpler, and
+				 * includes high bandwidth.
+				 */
+				urb->interval = 1;
+			} else if (qh->period > priv->periodic_size) {
+				qh->period = priv->periodic_size;
+				urb->interval = qh->period << 3;
+			}
+		} else {
+			qh->period = urb->interval;
+			if (qh->period > priv->periodic_size) {
+				qh->period = priv->periodic_size;
+				urb->interval = qh->period;
+			}
+		}
+	}
+
+	/* support for tt scheduling, and access to toggles */
+	qh->dev = urb->dev;
+
+	if (!usb_pipecontrol(urb->pipe))
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      !is_input, 1);
+	return qh;
+}
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct isp1763_qh *qh_append_tds(struct isp1763_hcd *priv,
+					struct urb *urb,
+					struct list_head *qtd_list,
+					int epnum, void **ptr)
+{
+	struct isp1763_qh *qh;
+	struct isp1763_qtd *qtd;
+	struct isp1763_qtd *prev_qtd;
+
+	qh = (struct isp1763_qh *) *ptr;
+	if (!qh) {
+		/* can't sleep here, we have priv->lock... */
+		qh = qh_make(priv, urb, GFP_ATOMIC);
+		if (!qh)
+			return qh;
+		*ptr = qh;
+	}
+
+	qtd = list_entry(qtd_list->next, struct isp1763_qtd, qtd_list);
+	if (!list_empty(&qh->qtd_list))
+		prev_qtd = list_entry(qh->qtd_list.prev,
+				      struct isp1763_qtd, qtd_list);
+	else
+		prev_qtd = NULL;
+
+	list_splice(qtd_list, qh->qtd_list.prev);
+	if (prev_qtd) {
+		BUG_ON(prev_qtd->hw_next);
+		prev_qtd->hw_next = qtd;
+	}
+
+	urb->hcpriv = qh;
+	return qh;
+}
+
+static void qtd_list_free(struct isp1763_hcd *priv, struct urb *urb,
+			  struct list_head *qtd_list)
+{
+	struct list_head *entry, *temp;
+
+	list_for_each_safe(entry, temp, qtd_list) {
+		struct isp1763_qtd *qtd;
+
+		qtd = list_entry(entry, struct isp1763_qtd, qtd_list);
+		list_del(&qtd->qtd_list);
+		isp1763_qtd_free(qtd);
+	}
+}
+
+static int isp1763_prepare_enqueue(struct isp1763_hcd *priv,
+				   struct urb *urb,
+				   struct list_head *qtd_list,
+				   gfp_t mem_flags, packet_enqueue * p)
+{
+	struct isp1763_qtd *qtd;
+	int epnum;
+	unsigned long flags;
+	struct isp1763_qh *qh = NULL;
+	int rc;
+	int qh_busy;
+
+	qtd = list_entry(qtd_list->next, struct isp1763_qtd, qtd_list);
+	epnum = urb->ep->desc.bEndpointAddress;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &priv_to_hcd(priv)->flags)) {
+		rc = -ESHUTDOWN;
+		goto done;
+	}
+	rc = usb_hcd_link_urb_to_ep(priv_to_hcd(priv), urb);
+	if (rc)
+		goto done;
+
+	qh = urb->ep->hcpriv;
+	if (qh)
+		qh_busy = !list_empty(&qh->qtd_list);
+	else
+		qh_busy = 0;
+
+	qh = qh_append_tds(priv, urb, qtd_list, epnum, &urb->ep->hcpriv);
+	if (!qh) {
+		usb_hcd_unlink_urb_from_ep(priv_to_hcd(priv), urb);
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	if (!qh_busy)
+		p(priv_to_hcd(priv), qh, qtd);
+
+      done:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	if (!qh)
+		qtd_list_free(priv, urb, qtd_list);
+	return rc;
+}
+
+static struct isp1763_qtd *isp1763_qtd_alloc(struct isp1763_hcd *priv,
+					     gfp_t flags)
+{
+	struct isp1763_qtd *qtd;
+
+	qtd = kmem_cache_zalloc(qtd_cachep, flags);
+	if (qtd)
+		INIT_LIST_HEAD(&qtd->qtd_list);
+
+	return qtd;
+}
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *qh_urb_transaction(struct isp1763_hcd *priv,
+					    struct urb *urb,
+					    struct list_head *head,
+					    gfp_t flags)
+{
+	struct isp1763_qtd *qtd, *qtd_prev;
+	void *buf;
+	int len, maxpacket;
+	int is_input;
+	u32 token;
+
+	/*
+	 * URBs map to sequences of QTDs:  one logical transaction
+	 */
+	qtd = isp1763_qtd_alloc(priv, flags);
+	if (!qtd)
+		return NULL;
+
+	list_add_tail(&qtd->qtd_list, head);
+	qtd->urb = urb;
+	urb->status = -EINPROGRESS;
+
+	token = 0;
+	/* for split transactions, SplitXState initialized to zero */
+
+	len = urb->transfer_buffer_length;
+	is_input = usb_pipein(urb->pipe);
+	if (usb_pipecontrol(urb->pipe)) {
+		/* SETUP pid */
+		qtd_fill(qtd, urb->setup_packet,
+			 sizeof(struct usb_ctrlrequest),
+			 token | SETUP_PID);
+
+		/* ... and always at least one more pid */
+		token ^= DATA_TOGGLE;
+		qtd_prev = qtd;
+		qtd = isp1763_qtd_alloc(priv, flags);
+		if (!qtd)
+			goto cleanup;
+		qtd->urb = urb;
+		qtd_prev->hw_next = qtd;
+		list_add_tail(&qtd->qtd_list, head);
+
+		/* for zero length DATA stages, STATUS is always IN */
+		if (len == 0)
+			token |= IN_PID;
+	}
+
+	/*
+	 * data transfer stage:  buffer setup
+	 */
+	buf = urb->transfer_buffer;
+
+	if (is_input)
+		token |= IN_PID;
+	else
+		token |= OUT_PID;
+
+	maxpacket =
+	    max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+	/*
+	 * buffer gets wrapped in one or more qtds;
+	 * last one may be "short" (including zero len)
+	 * and may serve as a control status ack
+	 */
+	for (;;) {
+		int this_qtd_len;
+
+		if (!buf && len) {
+			/* XXX This looks like usb storage / SCSI bug */
+			printk(KERN_ERR
+			       "buf is null, dma is %08lx len is %d\n",
+			       (long unsigned) urb->transfer_dma, len);
+			WARN_ON(1);
+		}
+
+		this_qtd_len = qtd_fill(qtd, buf, len, token);
+		len -= this_qtd_len;
+		buf += this_qtd_len;
+
+		/* qh makes control packets use qtd toggle; maybe switch it */
+		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+			token ^= DATA_TOGGLE;
+
+		if (len <= 0)
+			break;
+
+		qtd_prev = qtd;
+		qtd = isp1763_qtd_alloc(priv, flags);
+		if (!qtd)
+			goto cleanup;
+		qtd->urb = urb;
+		qtd_prev->hw_next = qtd;
+		list_add_tail(&qtd->qtd_list, head);
+	}
+
+	/*
+	 * control requests may need a terminating data "status" ack;
+	 * bulk ones may need a terminating short packet (zero length).
+	 */
+	if (urb->transfer_buffer_length != 0) {
+		int one_more = 0;
+
+		if (usb_pipecontrol(urb->pipe)) {
+			one_more = 1;
+			/* "in" <--> "out"  */
+			token ^= IN_PID;
+			/* force DATA1 */
+			token |= DATA_TOGGLE;
+		} else if (usb_pipebulk(urb->pipe)
+			   && (urb->transfer_flags & URB_ZERO_PACKET)
+			   && !(urb->transfer_buffer_length % maxpacket)) {
+			one_more = 1;
+		}
+		if (one_more) {
+			qtd_prev = qtd;
+			qtd = isp1763_qtd_alloc(priv, flags);
+			if (!qtd)
+				goto cleanup;
+			qtd->urb = urb;
+			qtd_prev->hw_next = qtd;
+			list_add_tail(&qtd->qtd_list, head);
+
+			/* never any data in such packets */
+			qtd_fill(qtd, NULL, 0, token);
+		}
+	}
+
+	qtd->status = URB_COMPLETE_NOTIFY;
+	return head;
+
+      cleanup:
+	qtd_list_free(priv, urb, head);
+	return NULL;
+}
+
+static int isp1763_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+			       gfp_t mem_flags)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	struct list_head qtd_list;
+	packet_enqueue *pe;
+
+	INIT_LIST_HEAD(&qtd_list);
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+
+		if (!qh_urb_transaction(priv, urb, &qtd_list, mem_flags))
+			return -ENOMEM;
+		pe = enqueue_an_ATL_packet;
+		break;
+
+	case PIPE_INTERRUPT:
+		if (!qh_urb_transaction(priv, urb, &qtd_list, mem_flags))
+			return -ENOMEM;
+		pe = enqueue_an_INT_packet;
+		break;
+
+	case PIPE_ISOCHRONOUS:
+		printk(KERN_ERR "PIPE_ISOCHRONOUS ain't supported\n");
+	default:
+		return -EPIPE;
+	}
+
+	return isp1763_prepare_enqueue(priv, urb, &qtd_list, mem_flags,
+				       pe);
+}
+
+static int isp1763_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+			       int status)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	struct inter_packet_info *ints;
+	u32 i;
+	u32 reg_base, or_reg, skip_reg;
+	unsigned long flags;
+	struct ptd ptd;
+	packet_enqueue *pe;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		return -EPIPE;
+		break;
+
+	case PIPE_INTERRUPT:
+		ints = priv->int_ints;
+		reg_base = INT_REGS_OFFSET;
+		or_reg = HC_INT_IRQ_MASK_OR_REG;
+		skip_reg = HC_INT_PTD_SKIPMAP_REG;
+		pe = enqueue_an_INT_packet;
+		break;
+
+	default:
+		ints = priv->atl_ints;
+		reg_base = ATL_REGS_OFFSET;
+		or_reg = HC_ATL_IRQ_MASK_OR_REG;
+		skip_reg = HC_ATL_PTD_SKIPMAP_REG;
+		pe = enqueue_an_ATL_packet;
+		break;
+	}
+
+	memset(&ptd, 0, sizeof(ptd));
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* NOTE: isp1763 only have 16 PTDs */
+	for (i = 0; i < NUM_OF_PTD; i++) {
+		if (ints->urb == urb) {
+			u16 skip_map;
+			u16 or_map;
+			struct isp1763_qtd *qtd;
+			struct isp1763_qh *qh = ints->qh;
+
+			skip_map = isp1763_readw(hcd->regs + skip_reg);
+			skip_map |= 1 << (u16) i;
+			isp1763_writew(skip_map, hcd->regs + skip_reg);
+
+			or_map = isp1763_readw(hcd->regs + or_reg);
+			or_map &= ~(1 << (u16) i);
+			isp1763_writew(or_map, hcd->regs + or_reg);
+
+			isp1763_write_mem(hcd, (u16 *) & ptd,
+					  reg_base + i * sizeof(ptd),
+					  sizeof(ptd));
+
+			qtd = ints->qtd;
+			qtd = clean_up_qtdlist(qtd);
+
+			free_mem(priv, ints->payload);
+
+			ints->urb = NULL;
+			ints->qh = NULL;
+			ints->qtd = NULL;
+			ints->data_buffer = NULL;
+			ints->payload = 0;
+
+			isp1763_urb_done(priv, urb, status);
+			if (qtd)
+				pe(hcd, qh, qtd);
+			break;
+
+		} else if (ints->qtd) {
+			struct isp1763_qtd *qtd, *prev_qtd = ints->qtd;
+
+			for (qtd = ints->qtd->hw_next; qtd;
+			     qtd = qtd->hw_next) {
+				if (qtd->urb == urb) {
+					prev_qtd->hw_next =
+					    clean_up_qtdlist(qtd);
+					isp1763_urb_done(priv, urb,
+							 status);
+					break;
+				}
+				prev_qtd = qtd;
+			}
+			/* we found the urb before the end of the list */
+			if (qtd)
+				break;
+		}
+		ints++;
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return 0;
+}
+
+/**
+ * isp1763_irq - host controller interrupt routine
+ * @ctrl: controller structure
+ *
+ * Called by main IRQ handler in isp1763_base.c and not registered via
+ * usb_hcd structure.
+ */
+static int isp1763_irq(struct isp1763_controller *ctrl, u32 flags)
+{
+	struct usb_hcd *usb_hcd = ctrl->priv;
+	struct isp1763_hcd *priv = hcd_to_priv(usb_hcd);
+	u16 imask;
+	irqreturn_t irqret = IRQ_NONE;
+
+	spin_lock(&priv->lock);
+
+	if (!(usb_hcd->state & HC_STATE_RUNNING))
+		goto leave;
+
+	imask = flags;
+//	imask = isp1763_readw(usb_hcd->regs + HC_INTERRUPT_REG);
+	if (unlikely(!imask))
+		goto leave;
+
+//	isp1763_writew(imask, usb_hcd->regs + HC_INTERRUPT_REG);
+	if (imask & HC_ATL_INT)
+		do_atl_int(usb_hcd);
+
+	if (imask & HC_INTL_INT)
+		do_intl_int(usb_hcd);
+
+	irqret = IRQ_HANDLED;
+      leave:
+	spin_unlock(&priv->lock);
+	return irqret;
+}
+
+static int isp1763_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	u32 temp, status = 0;
+	u32 mask;
+	int retval = 1;
+	unsigned long flags;
+
+	/* if !USB_SUSPEND, root hub timers won't get shut down ... */
+	if (!HC_IS_RUNNING(hcd->state))
+		return 0;
+
+	/* init status to no-changes */
+	buf[0] = 0;
+	mask = PORT_CSC;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	temp = isp1763_readl(hcd->regs + HC_PORTSC1);
+
+	if (temp & PORT_OWNER) {
+		if (temp & PORT_CSC) {
+			temp &= ~PORT_CSC;
+			isp1763_writel(temp, hcd->regs + HC_PORTSC1);
+			goto done;
+		}
+	}
+
+	/*
+	 * Return status information even for ports with OWNER set.
+	 * Otherwise khubd wouldn't see the disconnect event when a
+	 * high-speed device is switched over to the companion
+	 * controller by the user.
+	 */
+
+	if ((temp & mask) != 0
+	    || ((temp & PORT_RESUME) != 0
+		&& time_after_eq(jiffies, priv->reset_done))) {
+		buf[0] |= 1 << (0 + 1);
+		status = STS_PCD;
+	}
+	/* FIXME autosuspend idle root hubs */
+      done:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return status ? retval : 0;
+}
+
+/* ref: isp1763: pehci.c:pehci_hub_descriptor() */
+static void isp1763_hub_descriptor(struct isp1763_hcd *priv,
+				   struct usb_hub_descriptor *desc)
+{
+	int ports = HCS_N_PORTS(priv->hcs_params);
+	u16 temp;
+
+	desc->bDescriptorType = 0x29;
+	/* priv 1.0, 2.3.9 says 20ms max */
+	desc->bPwrOn2PwrGood = 10;
+	desc->bHubContrCurrent = 0;
+
+	desc->bNbrPorts = ports;
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 + 2 * temp;
+
+	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+	memset(&desc->bitmap[0], 0, temp);
+	memset(&desc->bitmap[temp], 0xff, temp);
+
+	/* per-port overcurrent reporting; no power switching */
+	temp = 0x0008;
+
+	if (HCS_PPC(priv->hcs_params)) {
+		temp |= 0x0001;	/* per-port power control */
+	} else {
+		temp |= 0x0002;	/* no power switching */
+	}
+
+	desc->wHubCharacteristics = cpu_to_le16(temp);
+}
+
+#define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+
+static int check_reset_complete(struct isp1763_hcd *priv, int index,
+				u32 __iomem * status_reg, int port_status)
+{
+	if (!(port_status & PORT_CONNECT))
+		return port_status;
+
+	/* if reset finished and it's still not enabled -- handoff */
+	if (!(port_status & PORT_PE)) {
+
+		printk(KERN_ERR "port %d full speed --> companion\n",
+		       index + 1);
+
+		port_status |= PORT_OWNER;
+		port_status &= ~PORT_RWC_BITS;
+		isp1763_writel(port_status, status_reg);
+
+	} else
+		printk(KERN_ERR "port %d high speed\n", index + 1);
+
+	return port_status;
+}
+
+static int isp1763_hub_control(struct usb_hcd *hcd, u16 typeReq,
+			       u16 wValue, u16 wIndex, char *buf,
+			       u16 wLength)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	int ports = HCS_N_PORTS(priv->hcs_params);
+	u32 __iomem *status_reg = hcd->regs + HC_PORTSC1;
+	u32 temp, status;
+	unsigned long flags;
+	int retval = 0;
+	unsigned selector;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+
+	case ClearPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		temp = isp1763_readl(status_reg);
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			isp1763_writel(temp & ~PORT_PE, status_reg);
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			/* XXX error? */
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			if (temp & PORT_RESET)
+				goto error;
+
+			if (temp & PORT_SUSPEND) {
+				if ((temp & PORT_PE) == 0)
+					goto error;
+				/* resume signaling for 20 msec */
+				temp &= ~(PORT_RWC_BITS);
+				isp1763_writel(temp | PORT_RESUME,
+					       status_reg);
+				priv->reset_done = jiffies +
+				    msecs_to_jiffies(20);
+			}
+			break;
+		case USB_PORT_FEAT_C_SUSPEND:
+			/* we auto-clear this feature */
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (HCS_PPC(priv->hcs_params))
+				isp1763_writel(temp & ~PORT_POWER,
+					       status_reg);
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			isp1763_writel(temp | PORT_CSC, status_reg);
+			break;
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			/* XXX error ? */
+			break;
+		case USB_PORT_FEAT_C_RESET:
+			/* GetPortStatus clears reset */
+			break;
+		default:
+			goto error;
+		}
+		isp1763_readl(hcd->regs + HC_USBCMD);
+		break;
+
+	case GetHubDescriptor:
+		isp1763_hub_descriptor(priv, (struct usb_hub_descriptor *)
+				       buf);
+		break;
+
+	case GetHubStatus:
+		/* no hub-wide feature/status flags */
+		memset(buf, 0, 4);
+		break;
+
+	case GetPortStatus:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		status = 0;
+		temp = isp1763_readl(status_reg);
+
+		/* wPortChange bits */
+		if (temp & PORT_CSC) {
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+		}
+
+		/* whoever resumes must GetPortStatus to complete it!! */
+		if (temp & PORT_RESUME) {
+			printk(KERN_ERR
+			       "Port resume should be skipped.\n");
+
+			/* Remote Wakeup received? */
+			if (!priv->reset_done) {
+				/* resume signaling for 20 msec */
+				priv->reset_done = jiffies
+				    + msecs_to_jiffies(20);
+				/* check the port again */
+				mod_timer(&priv_to_hcd(priv)->rh_timer,
+					  priv->reset_done);
+			}
+
+			/* resume completed? */
+			else if (time_after_eq(jiffies, priv->reset_done)) {
+				status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+				priv->reset_done = 0;
+
+				/* stop resume signaling */
+				temp = isp1763_readl(status_reg);
+				isp1763_writel(temp &
+					       ~(PORT_RWC_BITS |
+						 PORT_RESUME), status_reg);
+				retval =
+				    handshake(priv, status_reg,
+					      PORT_RESUME, 0,
+					      2000 /* 2msec */ );
+				if (retval != 0) {
+					isp1763_err(priv,
+						    "port %d resume error %d\n",
+						    wIndex + 1, retval);
+					goto error;
+				}
+				temp &=
+				    ~(PORT_SUSPEND | PORT_RESUME |
+				      (3 << 10));
+			}
+		}
+
+		/* whoever resets must GetPortStatus to complete it!! */
+		if ((temp & PORT_RESET)
+		    && time_after_eq(jiffies, priv->reset_done)) {
+			status |= 1 << USB_PORT_FEAT_C_RESET;
+			priv->reset_done = 0;
+
+			/* force reset to complete */
+			isp1763_writel(temp & ~PORT_RESET, status_reg);
+			/* REVISIT:  some hardware needs 550+ usec to clear
+			 * this bit; seems too long to spin routinely...
+			 */
+			retval = handshake(priv, status_reg,
+					   PORT_RESET, 0, 750);
+			if (retval != 0) {
+				isp1763_err(priv,
+					    "port %d reset error %d\n",
+					    wIndex + 1, retval);
+				goto error;
+			}
+
+			/* see what we found out */
+			temp =
+			    check_reset_complete(priv, wIndex, status_reg,
+						 isp1763_readl
+						 (status_reg));
+		}
+		/*
+		 * Even if OWNER is set, there's no harm letting khubd
+		 * see the wPortStatus values (they should all be 0 except
+		 * for PORT_POWER anyway).
+		 */
+
+		if (temp & PORT_OWNER)
+			printk(KERN_ERR "Warning: PORT_OWNER is set\n");
+
+		if (temp & PORT_CONNECT) {
+			status |= 1 << USB_PORT_FEAT_CONNECTION;
+			/* status may be from integrated TT */
+			status |= ehci_port_speed(priv, temp);
+		}
+		if (temp & PORT_PE)
+			status |= 1 << USB_PORT_FEAT_ENABLE;
+		if (temp & (PORT_SUSPEND | PORT_RESUME))
+			status |= 1 << USB_PORT_FEAT_SUSPEND;
+		if (temp & PORT_RESET)
+			status |= 1 << USB_PORT_FEAT_RESET;
+		if (temp & PORT_POWER)
+			status |= 1 << USB_PORT_FEAT_POWER;
+
+		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
+		break;
+
+	case SetHubFeature:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+
+	case SetPortFeature:
+		selector = wIndex >> 8;
+		wIndex &= 0xff;
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		temp = isp1763_readl(status_reg);
+		if (temp & PORT_OWNER)
+			break;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			isp1763_writel(temp | PORT_PE, status_reg);
+			break;
+
+		case USB_PORT_FEAT_SUSPEND:
+			if ((temp & PORT_PE) == 0
+			    || (temp & PORT_RESET) != 0)
+				goto error;
+
+			isp1763_writel(temp | PORT_SUSPEND, status_reg);
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (HCS_PPC(priv->hcs_params))
+				isp1763_writel(temp | PORT_POWER,
+					       status_reg);
+			break;
+		case USB_PORT_FEAT_RESET:
+			if (temp & PORT_RESUME)
+				goto error;
+
+			if ((temp & (PORT_PE | PORT_CONNECT)) ==
+			    PORT_CONNECT && PORT_USB11(temp)) {
+				temp |= PORT_OWNER;
+			} else {
+				if ((temp & PORT_CONNECT) == 0) {
+					printk(KERN_ERR
+					       "ERROR: Port not connected\n");
+					goto error;
+				}
+				if ((temp & PORT_POWER) == 0) {
+					printk(KERN_ERR
+					       "ERROR: Port power not enabled!\n");
+					goto error;
+				}
+
+				temp |= PORT_RESET;
+				temp &= ~PORT_PE;
+
+				isp1763_writel(temp, status_reg);
+				/*
+				 * caller must wait, then call GetPortStatus
+				 * usb 2.0 spec says 50 ms resets on root
+				 */
+				priv->reset_done = jiffies +
+				    msecs_to_jiffies(50);
+				    udelay(50000);
+
+				temp &= ~PORT_RESET;
+				isp1763_writel(temp, status_reg);
+				while (isp1763_readl(status_reg) &
+				       PORT_RESET);
+				temp |= PORT_PE;
+				isp1763_writel(temp, status_reg);
+			}
+			break;
+		default:
+			goto error;
+		}
+		isp1763_readl(hcd->regs + HC_USBCMD);
+		break;
+
+	default:
+	      error:
+		/* "stall" on error */
+		retval = -EPIPE;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return retval;
+}
+
+static void isp1763_endpoint_disable(struct usb_hcd *usb_hcd,
+				     struct usb_host_endpoint *ep)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(usb_hcd);
+	struct isp1763_qh *qh;
+	struct isp1763_qtd *qtd;
+	unsigned long flags;
+	struct urb *urb;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	qh = ep->hcpriv;
+	if (!qh)
+		goto out;
+
+	ep->hcpriv = NULL;
+	do {
+		/* more than entry might get removed */
+		if (list_empty(&qh->qtd_list))
+			break;
+
+		qtd = list_first_entry(&qh->qtd_list, struct isp1763_qtd,
+				       qtd_list);
+
+		if (qtd->status & URB_ENQUEUED) {
+
+			spin_unlock_irqrestore(&priv->lock, flags);
+			isp1763_urb_dequeue(usb_hcd, qtd->urb,
+					    -ECONNRESET);
+			spin_lock_irqsave(&priv->lock, flags);
+		} else {
+			urb = qtd->urb;
+			clean_up_qtdlist(qtd);
+			isp1763_urb_done(priv, urb, -ECONNRESET);
+		}
+	} while (1);
+
+	qh_destroy(qh);
+	/* remove requests and leak them.
+	 * ATL are pretty fast done, INT could take a while...
+	 * The latter shoule be removed
+	 */
+      out:
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int isp1763_get_frame(struct usb_hcd *hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	u32 fr;
+
+	fr = isp1763_readl(hcd->regs + HC_FRINDEX);
+	return (fr >> 3) % priv->periodic_size;
+}
+
+static void isp1763_stop(struct usb_hcd *hcd)
+{
+	struct isp1763_hcd *priv = hcd_to_priv(hcd);
+	u32 command;
+
+	isp1763_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
+			    NULL, 0);
+	mdelay(20);
+
+	spin_lock_irq(&priv->lock);
+	/* FIXME: is this really needed? */
+	ehci_reset(priv);
+
+	/* Stop host controller */
+	command = isp1763_readl(hcd->regs + HC_USBCMD);
+	command &= ~CMD_RUN;
+	isp1763_writel(command, hcd->regs + HC_USBCMD);
+
+	spin_unlock_irq(&priv->lock);
+
+	isp1763_writel(0, hcd->regs + HC_CONFIGFLAG);
+}
+
+static void isp1763_shutdown(struct usb_hcd *hcd)
+{
+	u16 temp;
+	u32 command;
+
+	isp1763_stop(hcd);
+	temp = isp1763_readw(hcd->regs + HC_HW_MODE_CTRL);
+	isp1763_writew(temp &=
+		       ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
+
+	command = isp1763_readl(hcd->regs + HC_USBCMD);
+	command &= ~CMD_RUN;
+	isp1763_writel(command, hcd->regs + HC_USBCMD);
+}
+
+static const struct hc_driver isp1763_hc_driver = {
+	.description = "isp1763-hcd",
+	.product_desc = "ST-Ericsson ISP1763 USB Host Controller",
+	.hcd_priv_size = sizeof(struct isp1763_hcd),
+	.flags = HCD_MEMORY | HCD_USB2,
+	.reset = isp1763_hc_setup,
+	.start = isp1763_run,
+	.stop = isp1763_stop,
+	.shutdown = isp1763_shutdown,
+	.urb_enqueue = isp1763_urb_enqueue,
+	.urb_dequeue = isp1763_urb_dequeue,
+	.endpoint_disable = isp1763_endpoint_disable,
+	.get_frame_number = isp1763_get_frame,
+	.hub_status_data = isp1763_hub_status_data,
+	.hub_control = isp1763_hub_control,
+};
+
+int __init init_kmem_once(void)
+{
+	qtd_cachep = kmem_cache_create("isp1763_qtd",
+				       sizeof(struct isp1763_qtd), 0,
+				       SLAB_TEMPORARY | SLAB_MEM_SPREAD,
+				       NULL);
+
+	if (!qtd_cachep)
+		return -ENOMEM;
+
+	qh_cachep =
+	    kmem_cache_create("isp1763_qh", sizeof(struct isp1763_qh), 0,
+			      SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL);
+
+	if (!qh_cachep) {
+		kmem_cache_destroy(qtd_cachep);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+void deinit_kmem_cache(void)
+{
+	kmem_cache_destroy(qtd_cachep);
+	kmem_cache_destroy(qh_cachep);
+}
+
+extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg);
+extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg);
+
+#define BUFFER_BSIZE 1024
+#define BUFFER_BLOCKS 24
+#define BUFFER_SIZE (BUFFER_BSIZE * BUFFER_BLOCKS)
+#define NUM_LOOPS 1024
+
+/**
+ * isp1763_hcd_resume - resume host
+ * @ctrl: controller
+ *
+ * Register and start up host controller, called by OTG code either during
+ * init or after a role change
+ */
+static int isp1763_hcd_resume(struct isp1763_controller *ctrl)
+{
+	struct usb_hcd *hcd = ctrl->priv;
+
+
+	usb_add_hcd(hcd, 0, 0);
+
+	ctrl->active = 1;
+	return 1;
+}
+
+/**
+ * isp1763_hcd_suspend - suspend host
+ * @ctrl: controller
+ *
+ * Stop and deregister host controller, called by OTG code after a role
+ * change.
+ */
+static int isp1763_hcd_suspend(struct isp1763_controller *ctrl)
+{
+	struct usb_hcd *hcd = ctrl->priv;
+
+
+	if (hcd->state == HC_STATE_RUNNING)
+		usb_remove_hcd(hcd);
+
+	ctrl->active = 0;
+	return 0;
+}
+
+/**
+ * isp1763_hcd_probe - initialize USB host controller structures
+ * @ctrl: controller
+ */
+static int isp1763_hcd_probe(struct isp1763_controller *ctrl)
+{
+	struct usb_hcd *hcd;
+	struct isp1763_hcd *priv;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (!ctrl->device)
+		return -ENODEV;
+
+	hcd = usb_create_hcd(&isp1763_hc_driver, ctrl->device,
+					dev_name(ctrl->device));
+	if (!hcd)
+		return -ENOMEM;
+
+	priv = hcd_to_priv(hcd);
+	priv->devflags = 0;
+	init_memory(priv);
+
+	ctrl->active = 0;
+	ctrl->priv = hcd;
+
+	hcd->regs = ctrl->regs;
+	if (hcd->regs)
+		return 0;
+
+	usb_put_hcd(hcd);
+	return -EIO;
+}
+
+static struct isp1763_controller isp1763_hc_controller = {
+	.active = 0,
+	.do_irq = isp1763_irq,
+	.suspend = isp1763_hcd_suspend,
+	.resume = isp1763_hcd_resume,
+	.probe = isp1763_hcd_probe,
+};
+
+static int __init isp1763_hc_init(void)
+{
+	isp1763_register_ctrl(&isp1763_hc_controller, ROLE_HOST);
+	init_kmem_once();
+
+	printk(KERN_ERR "isp1763 host controller driver loaded\n");
+	return 0;
+}
+
+static void __exit isp1763_hc_exit(void)
+{
+	isp1763_unregister_ctrl(ROLE_HOST);
+	if (isp1763_hc_controller.priv)
+		usb_put_hcd(isp1763_hc_controller.priv);
+	return;
+}
+
+module_init(isp1763_hc_init);
+module_exit(isp1763_hc_exit);
+
+
+MODULE_DESCRIPTION("Driver for the ISP1763 USB-controller from ST-Ericsson");
+MODULE_AUTHOR("Richard Retanubun <richardretanubun@xxxxxxxxxxxxx>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/isp1763/isp1763_hcd.h b/drivers/usb/isp1763/isp1763_hcd.h
new file mode 100644
index 0000000..3e4492c
--- /dev/null
+++ b/drivers/usb/isp1763/isp1763_hcd.h
@@ -0,0 +1,322 @@
+#ifndef _ISP1763_HCD_H_
+#define _ISP1763_HCD_H_
+
+/* exports for isp1763-if */
+struct usb_hcd *isp1763_register(phys_addr_t res_start,
+				 resource_size_t res_len,
+				 int irq,
+				 unsigned long irqflags,
+				 struct device *dev,
+				 const char *busname, unsigned int devflags);
+int init_kmem_once(void);
+void deinit_kmem_cache(void);
+
+/* EHCI capability registers;
+ * Vendor claims that HCCPARAMS and HCSPARAMS does not exist for isp1763,
+ * Any access to these registers need to return some hardcoded value.
+ */
+#define HC_CAPLENGTH		0x00
+#define HC_HCSPARAMS		0x04
+#define HC_HCCPARAMS		0x08
+
+/* EHCI operational registers */
+#define HC_USBCMD		0x8C
+#define HC_USBSTS		0x90
+#define HC_FRINDEX		0x98
+#define HC_CONFIGFLAG		0x9C
+#define HC_PORTSC1		0xA0
+#define HC_ISO_PTD_DONEMAP_REG	0xA4
+#define HC_ISO_PTD_SKIPMAP_REG	0xA6
+#define HC_ISO_PTD_LASTPTD_REG	0xA8
+#define HC_INT_PTD_DONEMAP_REG	0xAA
+#define HC_INT_PTD_SKIPMAP_REG	0xAC
+#define HC_INT_PTD_LASTPTD_REG	0xAE
+#define HC_ATL_PTD_DONEMAP_REG	0xB0
+#define HC_ATL_PTD_SKIPMAP_REG	0xB2
+#define HC_ATL_PTD_LASTPTD_REG	0xB4
+
+/* Configuration Register */
+#define HC_HW_MODE_CTRL		0xB6
+#define HW_ID_PULLUP		(1 << 12)
+#define HW_DEV_DMA_EN		(1 << 11)
+#define HW_COMN_INT_EN		(1 << 10)
+#define HW_COMN_DMA_EN		(1 << 9)
+#define HW_DACK_POL_HIGH	(1 << 6)
+#define HW_DREQ_POL_HIGH	(1 << 5)
+#define HW_DATA_BUS_8BIT	(1 << 4)
+#define HW_INTF_LOCK		(1 << 3)
+#define HW_INTR_HIGH_ACT	(1 << 2)
+#define HW_INTR_EDGE_TRIG	(1 << 1)
+#define HW_GLOBAL_INTR_EN	(1 << 0)
+
+#define HC_CHIP_ID_REG		0x70
+#define HC_SCRATCH_REG		0x78
+#define HC_RESET_REG		0xB8
+#define HC_PORT1_SC		0xA0
+
+/* INTF_MODE[1:0] is SW_RESET[7:6] */
+#define SW_INTF_MODE_MASK	0xC0
+/* The various operation mode the chip supports */
+#define SW_INTF_MODE_NAND	0x00
+#define SW_INTF_MODE_GNRC	0x40
+#define SW_INTF_MODE_NOR	0x80
+#define SW_INTF_MODE_SRAM	0xC0
+
+#define SW_RESET_RESET_ATX	(1 << 3)
+#define SW_RESET_RESET_HC	(1 << 1)
+#define SW_RESET_RESET_ALL	(1 << 0)
+
+#define HC_BUFFER_STATUS_REG	0xBA
+#define ATL_BUFFER		0x1
+#define INT_BUFFER		0x2
+#define ISO_BUFFER		0x4
+#define BUFFER_MAP		0x7
+
+#define HC_MEMORY_REG		0xC4
+
+#define HC_DATA_REG		0xC6
+
+#define HW_OTG_CTRL_SET		0xE4
+#define HW_OTG_CTRL_CLR		0xE6
+#define HW_OTG_CTRL_HC_2_DIS		(1 << 15)
+#define HW_OTG_CTRL_OTG_DIS		(1 << 10)
+#define HW_OTG_CTRL_SW_SEL_HC_DC	(1 << 7)
+
+/* Interrupt Register */
+#define HC_INTERRUPT_REG	0xD4
+
+#define HC_INTERRUPT_ENABLE	0xD6
+#define INTERRUPT_ENABLE_MASK	(HC_INTL_INT | HC_ATL_INT | HC_EOT_INT \
+					| HC_OPR_REG_INT | HC_OTG_INT)
+#define HC_OTG_INT		(1 << 10)
+#define HC_ISO_INT		(1 << 9)
+#define HC_ATL_INT		(1 << 8)
+#define HC_INTL_INT		(1 << 7)
+#define HC_CLK_READY_INT	(1 << 6)
+#define HC_HCSUSP_INT		(1 << 5)
+#define HC_OPR_REG_INT		(1 << 4)
+#define HC_EOT_INT		(1 << 3)
+#define HC_SOF_INT		(1 << 1) /* renamed from HC_SOT_INT */
+#define HC_USOF_INT		(1 << 0) /* micro SOF interrupt */
+
+#define HC_ISO_IRQ_MASK_OR_REG	0xD8
+#define HC_INT_IRQ_MASK_OR_REG	0xDA
+#define HC_ATL_IRQ_MASK_OR_REG	0xDC
+#define HC_ISO_IRQ_MASK_AND_REG	0xDE
+#define HC_INT_IRQ_MASK_AND_REG	0xE0
+#define HC_ATL_IRQ_MASK_AND_REG	0xE2
+
+/* Register sets */
+#define HC_BEGIN_OF_ATL		0x0c00
+#define HC_BEGIN_OF_INT		0x0800
+#define HC_BEGIN_OF_ISO		0x0400
+#define HC_BEGIN_OF_PAYLOAD	0x1000 /* size 20KB */
+
+/* urb state*/
+#define DELETE_URB		(0x0008)
+#define NO_TRANSFER_ACTIVE	(0xffff)
+
+#define ATL_REGS_OFFSET		(0x0c00)
+#define INT_REGS_OFFSET		(0x0800)
+
+/* Philips/Proprietary Transfer Descriptor (PTD) */
+struct ptd {
+	__le32 dw0;
+	__le32 dw1;
+	__le32 dw2;
+	__le32 dw3;
+	__le32 dw4;
+	__le32 dw5;
+	__le32 dw6;
+	__le32 dw7;
+};
+
+struct inter_packet_info {
+	void *data_buffer;
+	u32 payload;
+#define PTD_FIRE_NEXT           (1 << 0)
+#define PTD_URB_FINISHED        (1 << 1)
+	struct urb *urb;
+	struct isp1763_qh *qh;
+	struct isp1763_qtd *qtd;
+};
+
+typedef void (packet_enqueue) (struct usb_hcd * hcd,
+			       struct isp1763_qh * qh,
+			       struct isp1763_qtd * qtd);
+
+#define isp1763_dbg(priv, fmt, args...) \
+        dev_dbg(priv_to_hcd(priv)->self.controller, fmt, ##args)
+
+#define isp1763_info(priv, fmt, args...) \
+        dev_info(priv_to_hcd(priv)->self.controller, fmt, ##args)
+
+#define isp1763_err(priv, fmt, args...) \
+        dev_err(priv_to_hcd(priv)->self.controller, fmt, ##args)
+
+/* chip memory management */
+struct memory_chunk {
+	unsigned int start;
+	unsigned int size;
+	unsigned int free;
+};
+
+/*
+ * 20kb divided in: (per pehci.h from ST-Ericsson pehci driver)
+ * -  8 blocks @ 256  bytes =  2KB (10%)
+ * -  6 blocks @ 1024 bytes =  6KB (30%)
+ * -  3 blocks @ 4096 bytes = 12KB (60%)
+ */
+#define BLOCK_1_NUM 8
+#define BLOCK_2_NUM 6
+#define BLOCK_3_NUM 3
+
+#define BLOCK_1_SIZE 256
+#define BLOCK_2_SIZE 1024
+#define BLOCK_3_SIZE 4096
+#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
+#define PAYLOAD_SIZE 0x5000	/* 20 KB */
+
+/* I saw if some reloads if the pointer was negative */
+#define ISP1763_NULL_POINTER	(0x400)
+
+/* ATL */
+/* DW0 */
+#define PTD_VALID			1
+#define PTD_LENGTH(x)			(((u32) x) << 3)
+#define PTD_MAXPACKET(x)		(((u32) x) << 18)
+#define PTD_MULTI(x)			(((u32) x) << 29)
+#define PTD_ENDPOINT(x)			(((u32) x) << 31)
+/* DW1 */
+#define PTD_DEVICE_ADDR(x)		(((u32) x) << 3)
+#define PTD_PID_TOKEN(x)		(((u32) x) << 10)
+#define PTD_TRANS_BULK			((u32) 2 << 12)
+#define PTD_TRANS_INT			((u32) 3 << 12)
+#define PTD_TRANS_SPLIT			((u32) 1 << 14)
+#define PTD_SE_USB_LOSPEED		((u32) 2 << 16)
+#define PTD_PORT_NUM(x)			(((u32) x) << 18)
+#define PTD_HUB_NUM(x)			(((u32) x) << 25)
+#define PTD_PING(x)			(((u32) x) << 26)
+/* DW2 */
+#define PTD_RL_CNT(x)			(((u32) x) << 25)
+#define PTD_DATA_START_ADDR(x)		(((u32) x) << 8)
+#define BASE_ADDR			0x1000
+/* DW3 */
+#define PTD_CERR(x)			(((u32) x) << 23)
+#define PTD_NAC_CNT(x)			(((u32) x) << 19)
+#define PTD_ACTIVE			((u32) 1 << 31)
+#define PTD_DATA_TOGGLE(x)		(((u32) x) << 25)
+
+#define DW3_HALT_BIT			(1 << 30)
+#define DW3_ERROR_BIT			(1 << 28)
+#define DW3_QTD_ACTIVE			(1 << 31)
+
+#define INT_UNDERRUN			(1 << 2)
+#define INT_BABBLE			(1 << 1)
+#define INT_EXACT			(1 << 0)
+
+#define DW1_GET_PID(x)			(((x) >> 10) & 0x3)
+#define PTD_XFERRED_LENGTH(x)		((x) & 0x7fff)
+#define PTD_XFERRED_LENGTH_LO(x)	((x) & 0x7ff)
+
+#define SETUP_PID	(2)
+#define IN_PID		(1)
+#define OUT_PID		(0)
+#define GET_QTD_TOKEN_TYPE(x)	((x) & 0x3)
+
+#define DATA_TOGGLE		(1 << 31)
+#define GET_DATA_TOGGLE(x)	((x) >> 31)
+
+/* FIXME!: Does this apply to isp1763? ST-Ericsson believes no */
+/* Set these to HW defaults of ZERO? */
+/* Errata 1 */
+#define RL_COUNTER	(0)// isp1760 (0)
+#define NAK_COUNTER	(0)// isp1760 (0)
+#define ERR_COUNTER	(0)// isp1760 (2)
+
+#define HC_ATL_PL_SIZE        (4096)	/* isp1760 sets this to (8192) */
+
+/* Section 2.2 Host Controller Capability Registers */
+#define HC_LENGTH(p)		(((p)>>00)&0x00ff)	/* bits 7:0 */
+#define HC_VERSION(p)		(((p)>>16)&0xffff)	/* bits 31:16 */
+#define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */
+#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */
+#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
+#define HCC_ISOC_CACHE(p)       ((p)&(1 << 7))	/* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)	/* bits 6:4, uframes cached */
+
+/* Section 2.3 Host Controller Operational Registers */
+#define CMD_LRESET	(1<<7)	/* partial reset (no ports, etc) */
+#define CMD_RESET	(1<<1)	/* reset HC not bus */
+#define CMD_RUN		(1<<0)	/* start/stop HC */
+#define STS_PCD		(1<<2)	/* port change detect */
+#define FLAG_CF		(1<<0)	/* true: we'll support "high speed" */
+
+#define PORT_OWNER	(1<<13)	/* true: companion hc owns this port */
+#define PORT_POWER	(1<<12)	/* true: has power (see PPC) */
+#define PORT_USB11(x)	(((x) & (3 << 10)) == (1 << 10))	/* USB 1.1 device */
+#define PORT_RESET	(1<<8)	/* reset port */
+#define PORT_SUSPEND	(1<<7)	/* suspend port */
+#define PORT_RESUME	(1<<6)	/* resume it */
+#define PORT_PE		(1<<2)	/* port enable */
+#define PORT_CSC	(1<<1)	/* connect status change */
+#define PORT_CONNECT	(1<<0)	/* device connected */
+#define PORT_RWC_BITS   (PORT_CSC)
+
+/* isp1763 does not have HCCPARAMS and HCSPARAMS, use hardcoded values here */
+#define HCS_HARDCODE	(1 | (1 << 4))	/* Port Power Control */
+#define HCC_HARDCODE	(1 << 1)	/* Programmable Frame List */
+
+#define NUM_OF_PTD	16	/* isp1760 have 32 max PTDs; isp 1763 have 16 */
+
+struct isp1763_hcd {
+	u32 hcs_params;
+	spinlock_t lock;
+	struct inter_packet_info atl_ints[NUM_OF_PTD];
+	struct inter_packet_info int_ints[NUM_OF_PTD];
+	struct memory_chunk memory_pool[BLOCKS];
+
+	/* periodic schedule support */
+#define	DEFAULT_I_TDPS		1024
+	unsigned periodic_size;
+	unsigned i_thresh;
+	unsigned long reset_done;
+	unsigned long next_statechange;
+	unsigned int devflags;
+};
+
+struct isp1763_qtd {
+	struct isp1763_qtd *hw_next;
+	u8 packet_type;
+	u8 toggle;
+
+	void *data_buffer;
+	/* the rest is HCD-private */
+	struct list_head qtd_list;
+	struct urb *urb;
+	size_t length;
+
+	/* isp special */
+	u32 status;
+#define URB_COMPLETE_NOTIFY	(1 << 0)
+#define URB_ENQUEUED		(1 << 1)
+#define URB_TYPE_ATL		(1 << 2)
+#define URB_TYPE_INT		(1 << 3)
+};
+
+struct isp1763_qh {
+	/* first part defined by EHCI spec */
+	struct list_head qtd_list;
+	struct isp1763_hcd *priv;
+
+	/* periodic schedule info */
+	unsigned short period;	/* polling interval */
+	struct usb_device *dev;
+
+	u32 toggle;
+	u32 ping;
+};
+
+#define ehci_port_speed(priv, portsc) (1 << USB_PORT_FEAT_HIGHSPEED)
+
+#endif
diff --git a/drivers/usb/isp1763/isp1763_udc.c b/drivers/usb/isp1763/isp1763_udc.c
new file mode 100644
index 0000000..74f1272
--- /dev/null
+++ b/drivers/usb/isp1763/isp1763_udc.c
@@ -0,0 +1,1673 @@
+/*
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * Description:
+ *
+ * ISP1763A UDC driver
+ *
+ * TODO:
+ * - check locking for completeness
+ * - use double buffering
+ *
+ * (c) 2010 F. Voegel, Carangul.Tech
+ * (c) 2010 I+ME ACTIA Informatik und Mikroelektronik GmbH
+ *
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/gpio.h>
+#include <asm/system.h>
+#include <linux/torino_gpio.h>
+#include <linux/usb/cdc.h>
+
+#include "isp1763.h"
+#include "isp1763_udc.h"
+
+#define DRIVER_NAME "isp1763_udc"
+static const char driver_name[] = DRIVER_NAME;
+static const char driver_desc[] = "ISP 1763A udc";
+
+static struct isp1763_udc udc_dev;
+static struct isp_ep isp_eps[USB_MAX_ENDPOINTS];
+static int isp_debug = CONFIG_USB_ISP1763_DEBUG;
+
+static int isp1763_udc_do_irq(struct isp1763_controller *ctrl, u32 flags);
+#define USE_DOUBLE_BUFFERING
+
+static u32 irq_bits = MASK_DCINTENABLE_RELEVANT;
+
+/* Ladies and gentlemen, I give you... THE WHEEL */
+
+/**
+* queue_add - add item to queue
+* @head: queue head
+* @elem: element for which to create entry
+*/
+static void queue_add(struct queue **head, void *elem)
+{
+	struct queue *p = NULL;
+	struct queue *new = kmalloc(sizeof(struct queue), GFP_ATOMIC);
+
+	new->next = NULL;
+	new->elem = elem;
+
+	queue_printk(KERN_ERR "Adding queue entry %p with elem %p\n", new,
+		     new->elem);
+
+	if (*head) {
+		for (p = *head; p->next; p = p->next);
+		p->next = new;
+	} else
+		*head = new;
+}
+
+/**
+* queue_get - get item from top of queue
+* @head: queue head
+*/
+static void *queue_get(struct queue **head)
+{
+	if (head) {
+		if (*head) {
+			queue_printk(KERN_ERR
+				     "Retrieving queue entry %p with elem %p\n",
+				     *head, (*head)->elem);
+			return (*head)->elem;
+		}
+	}
+	return NULL;
+}
+
+/**
+* queue_topkill - remove first entry from queue
+* @head: queue head
+*/
+static void queue_topkill(struct queue **head)
+{
+	struct queue *old = *head;
+
+	queue_printk(KERN_ERR "Killing queue entry %p with elem %p\n",
+		     *head, (*head)->elem);
+
+	if (*head == NULL)
+		return;
+
+	if ((*head)->next)
+		*head = (*head)->next;
+	else
+		*head = NULL;
+
+	kfree(old);
+}
+
+/**
+* queue_empty - is queue empty?
+* @head: queue head
+*/
+static int queue_empty(struct queue **head)
+{
+	return *head == NULL;
+}
+
+/**
+* queue_count - return number of entries in queue
+* @head: queue head
+*/
+static int queue_count(struct queue **head)
+{
+	int cnt = 0;
+	struct queue *p = *head;
+
+	for (; p != NULL; p = p->next) {
+		struct usb_request *r = p->elem;
+		if (!r)
+			continue;
+		if (r->buf == NULL)
+			queue_printk(KERN_ERR
+				     "queued request %i (%p) has NULL buffer and %i size (actual=%i)\n",
+				     cnt, r, r->length, r->actual);
+		cnt++;
+	}
+
+	return cnt;
+}
+
+static void queue_flush(struct queue **head)
+{
+	while(!queue_empty(head))
+		queue_topkill(head);
+}
+
+/**
+* dump_ctrlrequest - debug - dump control request
+* @req: pointer to request structure
+*/
+static void dump_ctrlrequest(struct usb_ctrlrequest *req)
+{
+	char reqname[64] = "???";
+	char typename[64] = "???";
+	switch (req->bRequestType & USB_TYPE_MASK) {
+	case USB_TYPE_STANDARD:
+		sprintf(typename, "STD");
+		switch (req->bRequest) {
+		case USB_REQ_GET_STATUS:
+			sprintf(reqname, "USB_REQ_GET_STATUS");
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			sprintf(reqname, "USB_REQ_CLEAR_FEATURE");
+			break;
+		case USB_REQ_SET_FEATURE:
+			sprintf(reqname, "USB_REQ_SET_FEATURE");
+			break;
+		case USB_REQ_SET_ADDRESS:
+			sprintf(reqname, "USB_REQ_SET_ADDRESS(%i)",
+				le16_to_cpu(req->wValue));
+			break;
+		case USB_REQ_GET_DESCRIPTOR:
+			sprintf(reqname, "USB_REQ_GET_DESCRIPTOR");
+			break;
+		case USB_REQ_SET_DESCRIPTOR:
+			sprintf(reqname, "USB_REQ_SET_DESCRIPTOR");
+			break;
+		case USB_REQ_GET_CONFIGURATION:
+			sprintf(reqname, "USB_REQ_GET_CONFIGURATION");
+			break;
+		case USB_REQ_SET_CONFIGURATION:
+			sprintf(reqname, "USB_REQ_SET_CONFIGURATION");
+			break;
+		case USB_REQ_GET_INTERFACE:
+			sprintf(reqname, "USB_REQ_GET_INTERFACE");
+			break;
+		case USB_REQ_SET_INTERFACE:
+			sprintf(reqname, "USB_REQ_SET_INTERFACE");
+			break;
+		case USB_REQ_SYNCH_FRAME:
+			sprintf(reqname, "USB_REQ_SYNCH_FRAME");
+			break;
+		}
+		break;
+	case USB_TYPE_CLASS:
+		sprintf(typename, "CLASS");
+		switch (req->bRequest) {
+		case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+			sprintf(reqname,
+				"USB_CDC_SEND_ENCAPSULATED_COMMAND");
+			break;
+		case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+			sprintf(reqname,
+				"USB_CDC_GET_ENCAPSULATED_RESPONSE");
+			break;
+		case USB_CDC_REQ_SET_LINE_CODING:
+			sprintf(reqname, "USB_CDC_REQ_SET_LINE_CODING");
+			break;
+		case USB_CDC_REQ_GET_LINE_CODING:
+			sprintf(reqname, "USB_CDC_REQ_GET_LINE_CODING");
+			break;
+		case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+			sprintf(reqname,
+				"USB_CDC_REQ_SET_CONTROL_LINE_STATE");
+			break;
+		case USB_CDC_REQ_SEND_BREAK:
+			sprintf(reqname, "USB_CDC_REQ_SEND_BREAK");
+			break;
+		case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+			sprintf(reqname,
+				"USB_CDC_SET_ETHERNET_MULTICAST_FILTERS");
+			break;
+		case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+			sprintf(reqname,
+				"USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER");
+			break;
+		case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+			sprintf(reqname,
+				"USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER");
+			break;
+		case USB_CDC_SET_ETHERNET_PACKET_FILTER:
+			sprintf(reqname,
+				"USB_CDC_SET_ETHERNET_PACKET_FILTER");
+			break;
+		case USB_CDC_GET_ETHERNET_STATISTIC:
+			sprintf(reqname, "USB_CDC_GET_ETHERNET_STATISTIC");
+			break;
+		}
+
+		break;
+	case USB_TYPE_VENDOR:
+		sprintf(typename, "VENDOR");
+		sprintf(reqname, "%.2x", req->bRequest);
+		break;
+	}
+
+	printk(KERN_INFO "bRequestType: 0x%.2x (%s)\n",
+		    req->bRequestType, typename);
+	printk(KERN_INFO "bRequest:     0x%.2x (%s)\n", req->bRequest,
+		    reqname);
+	printk(KERN_INFO "wValue:       0x%.4x\n", req->wValue);
+	printk(KERN_INFO "wIndex:       0x%.4x\n", req->wIndex);
+	printk(KERN_INFO "wLength:      0x%.4x\n", req->wLength);
+}
+
+/**
+* dump_usb_request - debug - dump USB request
+* @ep: pointer to EP struct
+* @req: pointer to request
+*/
+static void dump_usb_request(struct usb_ep *ep, struct usb_request *req)
+{
+	int i;
+
+#define MAXDUMPLEN 64
+	info_printk("\n%s on %s: ", __FUNCTION__, ep->name);
+	for (i = 0;
+	     i < (req->length > MAXDUMPLEN ? MAXDUMPLEN : req->length);
+	     i++) {
+		info_printk("%.2x ", ((char *) req->buf)[i]);
+	}
+	info_printk("\n");
+}
+
+/**
+* get_ep_index - get endpoint index
+* @ep: endpoint pointer
+*/
+static int get_ep_index(struct usb_ep *ep)
+{
+	int i;
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		if (&isp_eps[i].ep == ep)
+			return i;
+	}
+	return -1;
+}
+
+/**
+* ep_set_index - set PIO endpoint index
+* @dev: UDC device pointer
+* @index: new EP index
+*/
+static void ep_set_index(struct isp1763_udc *dev, unsigned char index)
+{
+	isp1763_writew(index, &dev->regs->ep_index);
+	isp1763_writew(~index, &dev->regs->dma_ep);
+	ndelay(200);
+}
+
+/**
+* ep_stall - stall endpoint
+* @dev: UDC device pointer
+* @idx: index of ep to stall
+* @do_stall: 1=stall, 0=unstall
+*/
+static void ep_stall(struct isp1763_udc *dev, int idx, int do_stall)
+{
+	warn_printk(KERN_ERR "%sSTALLING EP INDEX %i\n",
+		    do_stall ? "" : "UN", idx);
+	ep_set_index(dev, idx);
+
+	if (do_stall)
+		set_stall_flag(dev);
+	else
+		clr_stall_flag(dev);
+}
+
+#define REAL_EP_NUM(x) (((x & 0x0F) << 1) | x >> 7)
+
+/**
+* ep_feature - handle EP feature request
+* @dev: UDC device pointer
+* @req: pointer to request
+*/
+static void
+ep_feature(struct isp1763_udc *dev, struct usb_ctrlrequest *req)
+{
+	if (le16_to_cpu(req->wValue) == USB_ENDPOINT_HALT) {
+		ep_stall(dev, REAL_EP_NUM(le16_to_cpu(req->wIndex)),
+			 req->bRequest == USB_REQ_SET_FEATURE ? 1 : 0);
+	} else {
+		set_status_flag(dev);
+	}
+}
+
+/**
+* write_ep - write data to endpoint
+* @dev: UDC device pointer
+* @buf: buffer to send
+* @buflen: buffer length
+*/
+static int
+write_ep(struct isp1763_udc *dev, unsigned short *buf, int buflen)
+{
+	int i = 0;
+
+	if (!buf && buflen > 0) {
+		warn_printk(KERN_ERR
+			    "%s: NULL buffer submitted with len %i!\n",
+			    __FUNCTION__, buflen);
+		return 0;
+	}
+
+	isp1763_writew(buflen, &dev->regs->buflen);
+	disable_glint__(dev);
+
+	for (i = 0; i < (buflen / 2); i++) {
+		__raw_writew(buf[i], &dev->regs->data_port);
+	}
+	if (buflen % 2) {
+		char *bufc = (char *) buf;
+		__raw_writew((unsigned short) (bufc[buflen - 1] |
+					       (bufc[buflen - 1] << 8)),
+			     &dev->regs->data_port);
+	}
+
+	enable_glint(dev);
+
+	return i * 2 - (buflen % 2);
+}
+
+/**
+* read_ep0 - read request from endpoint 0
+* @dev: UDC device pointer
+* @req: pointer to request to fill
+*/
+int read_ep0(struct isp_ep *ep, struct usb_request *req)
+{
+	struct isp1763_udc *dev = ep->udc;
+	unsigned short *buf;
+	int i;
+	int reqreadlen;
+	int buflen = 0;
+
+	if (!req)
+		return -EINVAL;
+
+	reqreadlen = req->length - req->actual;
+
+	disable_glint(dev);
+
+	buf = (unsigned short *) ((char *) req->buf + req->actual);
+	buflen = reqreadlen > 64 ? 64 : reqreadlen;
+
+	for (i = 0; i < (buflen / 2); i++) {
+		buf[i] = __raw_readw(&dev->regs->data_port);
+	}
+	if (buflen % 2) {
+		char *bufc = (char *) buf;
+		bufc[buflen - 1] =
+		    (char) (__raw_readw(&dev->regs->data_port) & 0xff);
+	}
+
+	req->actual += buflen;
+
+	/*
+	 * Complete if buf filled to capacity or buflen < wMaxPacketSize
+	 * (short packet)
+	*/
+	if (req->actual == req->length || buflen < ep->maxpacketsize) {
+		req->status = 0;
+		set_status_flag(dev);
+		enable_glint(dev);
+		return 1;
+	}
+
+	enable_glint(dev);
+	return 0;
+}
+
+/**
+* read_ep - read data from endpoint
+* @ep: endpoint 
+* @req: request
+*/
+int read_ep(struct isp_ep *ep, struct usb_request *req)
+{
+	struct isp1763_udc *dev = ep->udc;
+	int buflen = -1;
+	unsigned short *buf;
+	int i;
+	int reqreadlen = req->length - req->actual;
+
+	/* 
+	   At first I believed this to be a source of errors, but actually a RX interrupt
+	   without any data is caused by a icr setting where an int is generated on the first
+	   NAK after previously having an ACK. This is it. Nothing to it, so just leave and
+	   do naught.
+	 */
+	if (isp1763_readw(&dev->regs->dcbufstatus) == 0)
+		return -1;
+
+	if (req->buf == NULL) {
+		error_printk(KERN_ERR
+			     "%s(): ERROR! req->buf == NULL! (len=%i) %p\n",
+			     __FUNCTION__, req->length, req);
+		req->actual = 0;
+		req->status = -EINVAL;
+		if (!req->no_interrupt)
+			if (req->complete) {
+				debug_printk(KERN_ERR
+					     "Completing request %p\n",
+					     req);
+				req->complete(&ep->ep, req);
+			}
+		return -1;
+	}
+
+	buflen = isp1763_readw(&dev->regs->buflen);
+
+	/* 
+	 * Seems inevitable, but what would happen to the rest of the buffer? 
+	 * Does this even happen? 
+	*/
+	if (reqreadlen < buflen)
+		buflen = reqreadlen;
+
+	buf = (unsigned short *) ((char *) req->buf + req->actual);
+
+	disable_glint(dev);
+
+	for (i = 0; i < (buflen / 2); i++) {
+		buf[i] = __raw_readw(&dev->regs->data_port);
+	}
+	if (buflen % 2) {
+		char *bufc = (char *) buf;
+		bufc[buflen - 1] =
+		    (char) (readw(&dev->regs->data_port) & 0xFF);
+	}
+
+	req->actual += buflen;
+
+	enable_glint(dev);
+
+	/* 
+	 * Complete if buf filled to capacity or buflen < wMaxPacketSize
+	 * (short packet) 
+	*/
+	if (req->actual == req->length || buflen < ep->maxpacketsize) {
+		req->status = 0;
+		isp1763_writew(0, &dev->regs->dcbufstatus);
+		set_clbuf_flag(dev);
+		if (!req->no_interrupt)
+			if (req->complete) {
+				debug_printk(KERN_ERR
+					     "Completing OUT request %p (%i bytes)\n",
+					     req, req->actual);
+				req->complete(&ep->ep, req);
+			}
+		return 1;
+	}
+
+	return 0;
+}
+
+/* EP0 FUNCTIONS */
+
+/**
+* configure_ep0 - configure control endpoint
+* @dev: UDC device pointer
+*/
+static int configure_ep0(struct isp1763_udc *dev)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		ep_set_index(dev, EP_INDEX(0, DIR_RX));
+		isp1763_writew(64, &dev->regs->ep_maxpktsize);
+		isp1763_writew(EP_TYPE_UNUSED | (i << 3),
+			       &dev->regs->ep_type);
+
+		ep_set_index(dev, EP_INDEX(0, DIR_TX));
+		isp1763_writew(64, &dev->regs->ep_maxpktsize);
+		isp1763_writew(EP_TYPE_UNUSED | (i << 3),
+			       &dev->regs->ep_type);
+
+		ep_set_index(dev, IDX_EP0_SETUP);
+		isp1763_writew(64, &dev->regs->ep_maxpktsize);
+		isp1763_writew(EP_TYPE_UNUSED | (i << 3),
+			       &dev->regs->ep_type);
+
+		udelay(5);
+	}
+
+	return 0;
+}
+
+/**
+* read_ep0_setup - read control endpoint setup request
+* @dev: UDC device pointer
+* @buf: buffer
+* @buflen: length of buffer
+*/
+static int
+read_ep0_setup(struct isp1763_udc *dev, unsigned short *buf, int buflen)
+{
+	int readlen = 0;
+	int i;
+
+	ep_set_index(dev, IDX_EP0_SETUP);
+
+	readlen = isp1763_readw(&dev->regs->buflen);
+
+	if (buflen < readlen)
+		return -ENOMEM;
+
+	disable_glint(dev);
+
+	for (i = 0; i < ((readlen / 2) + (readlen % 2)); i++) {
+		buf[i] = __raw_readw(&dev->regs->data_port);
+	}
+	if (buflen % 2) {
+		char *bufc = (char *) buf;
+		bufc[buflen - 1] =
+		    (char) (__raw_readw(&dev->regs->data_port) & 0xff);
+	}
+
+	enable_glint(dev);
+
+	return readlen;
+}
+
+/**
+* handle_ep0_setup - handle control endpoint setup IRQ
+*/
+static void handle_ep0_setup(struct isp1763_udc *dev)
+{
+	char buffer[8];
+	struct usb_ctrlrequest *setup_request;
+	int readlen = 0;
+	int ret = 0;
+
+	memset(buffer, 0, sizeof(buffer));
+	readlen = read_ep0_setup(dev, (unsigned short *) buffer, 
+						sizeof(buffer));
+
+	if (readlen < sizeof(struct usb_ctrlrequest)) {
+		warn_printk(KERN_ERR "short setup packet!\n");
+		return;
+	}
+	setup_request = (struct usb_ctrlrequest *) buffer;
+
+	if (isp_debug > 0)
+		dump_ctrlrequest(setup_request);
+
+
+	if ((setup_request->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+		goto non_standard;
+
+	/* STANDARD REQUEST */
+	dev->ep0->state = DIR_TX;
+
+	switch (setup_request->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		isp1763_writew(le16_to_cpu(setup_request->wValue) |
+					0x80, &dev->regs->address);
+		set_status_flag(dev);
+		break;
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		ep_feature(dev, setup_request);
+		break;
+	default:
+		/* 
+		 * TODO: Check if setup() left any data for us to process, 
+		 * if not, ZiLP EP
+		*/
+		
+		if (!dev->driver)
+			return;
+		
+		if ((ret = dev->driver->setup(dev->gadget,
+					setup_request)) < 0) {
+			error_printk(KERN_ERR
+					"###### ERROR : ->setup() returned with %i\n",
+					ret);
+			ep_stall(dev, 0, 1);
+		}
+		if (setup_request->bRequest & USB_DIR_IN) {
+			if (queue_empty(&dev->ep0->queue) && 
+					(setup_request->bRequest & 0x80)) {
+				ep_set_index(dev, EP_INDEX(0,DIR_TX));
+				set_dsen_flag(dev);
+				write_ep(dev, NULL, 0);
+				set_vendp_flag(dev);
+			}
+		} else if (setup_request->bRequest == USB_REQ_SET_CONFIGURATION) {
+			ep_set_index(dev, EP_INDEX(0, DIR_TX));
+			write_ep(dev, NULL, 0);
+			set_status_flag(dev);
+		}
+	}
+	return;
+	
+	/* CLASS / VENDOR REQUEST */
+non_standard:
+	debug_printk(KERN_ERR
+			"Processing EP0 class/vendor request\n");
+
+	if (setup_request->bRequestType & USB_DIR_IN) {
+		info_printk(KERN_ERR "CLASS IN REQUEST\n");
+		dev->ep0->state = DIR_TX;
+	} else {
+		int timeout = 0;
+		ep_set_index(&udc_dev, EP_INDEX(0, DIR_RX));
+		clr_dsen_flag(&udc_dev);
+		set_dsen_flag(&udc_dev);
+#if 1
+		while(!isp1763_readw(&dev->regs->buflen)) {
+			udelay(10);
+			if(++timeout > 1000) {
+				printk(KERN_EMERG "TIMEOUT!\n");
+				break;
+			ep_set_index(&udc_dev, EP_INDEX(0, DIR_RX));
+
+			}
+		}
+#endif
+		info_printk(KERN_ERR "CLASS OUT REQUEST\n");
+		if (0) {
+			int i;
+			
+			printk("DATA IN BUFFER FOR EP %i RIGHT NOW: ", isp1763_readw(&dev->regs->ep_index));
+			for(i = 0; i < 16; i++)
+				printk("%.4x ", isp1763_readw(&dev->regs->data_port));
+			printk(KERN_ERR "\n");
+		}
+		
+		debug_printk(KERN_ERR "dcBufferStatus: %.4x buflen: %i\n",
+				isp1763_readw(&dev->regs->dcbufstatus),
+				isp1763_readw(&dev->regs->buflen));
+		dev->ep0->state = DIR_RX;
+		// experimental
+	}
+
+	/* Probably a class request */
+	if ((ret = dev->driver->setup(dev->gadget, setup_request)) < 0) {
+		printk(KERN_ERR "dev->driver->setup() returned %i! stalling ep0...\n", ret);
+		ep_stall(dev, 0, 1);
+		return;
+	}
+
+	if (setup_request->bRequestType & USB_DIR_IN) {
+		/* ??? */
+	} else {
+		ep_set_index(dev, EP_INDEX(0, DIR_RX));
+		set_dsen_flag(dev);
+//		printk(KERN_ERR "%s:%i %s()\n", __FILE__,__LINE__,__FUNCTION__);
+		set_status_flag(dev);
+	}
+}
+
+/* DEVICE ENABLE / DISABLE */
+
+/**
+* isp1763_udc_disable - disable controller activity
+* @dev: UDC device pointer
+*/
+static void isp1763_udc_disable(struct isp1763_udc *dev)
+{
+	isp1763_writew(0, &dev->regs->address);
+}
+
+/**
+* isp1763_udc_enable - enable controller activity
+* @dev: UDC device pointer
+*/
+static void isp1763_udc_enable(struct isp1763_udc *dev)
+{
+	isp1763_writew(MASK_MODE_CLKAON | MASK_MODE_GLINTENA |
+		       MASK_MODE_WKUPCS, &dev->regs->mode);
+	isp1763_writel(0xFFFFFFFF, &dev->regs->dc_interrupt);
+	isp1763_writel(irq_bits,
+		       &dev->regs->dc_int_enable);
+	isp1763_writew(0x80, &dev->regs->address);
+}
+
+/* ENDPOINT OPS */
+
+static int ep_fifo_space_available = TOTAL_FIFO_SIZE - (3 * 64);
+
+/**
+* isp1763_udc_ep_enable - enable EP (EP API)
+* @ep: USB endpoint to enable
+* @desc: configuration descriptor
+*/
+static int isp1763_udc_ep_enable(struct usb_ep *ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct isp1763_udc *dev = &udc_dev;
+	struct isp_ep *ispep = ep->driver_data;
+	int ep_index = 0;
+	int fifo_size = 0;
+	unsigned long flags = 0;
+	
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+
+	fifo_size = le16_to_cpu(desc->wMaxPacketSize) & 0x7FF;
+	if (ep_fifo_space_available < fifo_size) {
+		error_printk(KERN_ERR
+			     "%s: no FIFO space available for %s, requested %i bytes, have %i\n",
+			     __FUNCTION__, ep->name, fifo_size,
+			     ep_fifo_space_available);
+		return -ENOMEM;
+	}
+
+	local_irq_save(flags);
+
+	ep_index = get_ep_index(ep);
+	if (ep_index < 0) {
+		error_printk(KERN_ERR
+			     "%s:%i: %s INVALID ENDPOINT INDEX %i\n",
+			     __FILE__, __LINE__, __FUNCTION__, ep_index);
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	ispep = &isp_eps[ep_index];
+	dev = ispep->udc;
+
+	ep_set_index(dev, ispep->index);
+
+	info_printk(KERN_ERR "%s:%i: %s(%s=type %i;maxsize=%i)\n",
+		    __FILE__, __LINE__, __FUNCTION__, ep->name,
+		    desc->bmAttributes & 0x03,
+		    le16_to_cpu(desc->wMaxPacketSize));
+
+	isp1763_writew(desc->bmAttributes & 0x03, &dev->regs->ep_type);
+	isp1763_writew(fifo_size, &dev->regs->ep_maxpktsize);
+
+	isp1763_writew((desc->
+			bmAttributes & 0x03) | EP_TYPE_ENABLE |
+		       EP_TYPE_DBLBUF, &dev->regs->ep_type);
+
+	/* clear buffers, twice for double buffering */
+	set_clbuf_flag(dev);
+	clr_clbuf_flag(dev);
+	set_clbuf_flag(dev);
+	clr_clbuf_flag(dev);
+
+	ispep->maxpacketsize = fifo_size;
+	ispep->ep_type = desc->bmAttributes & 0x03;
+
+	ep_fifo_space_available -= fifo_size * 2;
+
+	memcpy(&ispep->desc, desc, sizeof(struct usb_endpoint_descriptor));
+
+	irq_bits |= 1 << (ep_index + 12);
+	isp1763_writew(irq_bits, &dev->regs->dc_int_enable);
+//	printk(KERN_ERR "Enabling bit %.8lx in irq_bits\n", 1 << (ep_index + 10));
+	local_irq_restore(flags);
+	return 0;
+}
+
+/**
+* isp1763_udc_ep_disable - disable EP (EP API)
+* @ep: USB endpoint to disable
+*/
+static int isp1763_udc_ep_disable(struct usb_ep *ep)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+
+	struct isp1763_udc *dev = &udc_dev;
+	struct isp_ep *ispep;
+	int ep_index = 0;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	ep_index = get_ep_index(ep);
+	if (ep_index < 0) {
+		error_printk(KERN_ERR
+			     "%s:%i: %s INVALID ENDPOINT INDEX %i\n",
+			     __FILE__, __LINE__, __FUNCTION__, ep_index);
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+
+	irq_bits &= ~(1 << (ep_index + 12));
+	isp1763_writew(irq_bits, &dev->regs->dc_int_enable);
+//	printk(KERN_ERR "Disabling bit %.8lx in irq_bits\n", 1 << (ep_index + 10));
+
+
+	ispep = &isp_eps[ep_index];
+	dev = ispep->udc;
+
+	ep_fifo_space_available += ispep->maxpacketsize;
+
+	ispep->maxpacketsize = 0;
+
+	ep_set_index(dev, ispep->index);
+	isp1763_writew(0, &dev->regs->ep_type);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+/**
+* isp1763_udc_ep_alloc_request - allocate usb_request structure (EP API)
+* @ep: USB endpoint
+* @gfp_flags: unused
+*/
+static struct usb_request *isp1763_udc_ep_alloc_request(struct usb_ep *ep,
+							gfp_t gfp_flags)
+{
+	struct usb_request *req =
+	    kmalloc(sizeof(struct usb_request), GFP_ATOMIC);
+	info_printk(KERN_ERR "%s:%i: %s(%s) = %p\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name, req);
+
+	memset(req, 0, sizeof(*req));
+	return req;
+}
+
+/**
+* isp1763_udc_ep_free_request - free USB request (EP API)
+* @ep: USB endpoint
+* @req: request to free
+*/
+static void
+isp1763_udc_ep_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+	kfree(req);
+}
+
+/**
+* isp1763_udc_ep_queue - queue request on endpoint queue (EP API)
+* @ep: USB endpoint
+* @req: request to queue
+* @gfp_flags: unused
+*/
+static int
+isp1763_udc_ep_queue(struct usb_ep *ep, struct usb_request *req,
+		     gfp_t gfp_flags)
+{
+
+	unsigned long flags = 0;
+	struct isp1763_udc *dev = &udc_dev;
+	local_irq_save(flags);
+
+	disable_glint(dev);
+
+	debug_printk(KERN_ERR "%s:%i: %s(%s) %i bytes %p\n", __FILE__,
+		     __LINE__, __FUNCTION__, ep->name, req->length, req);
+
+	if (req->buf == NULL && req->length > 0) {
+		error_printk(KERN_ERR "%s: INVALID REQUEST\n",
+			     __FUNCTION__);
+		req->status = -EINVAL;
+		local_irq_restore(flags);
+		enable_glint(dev);
+		return req->status;
+	}
+
+	req->status = -EINPROGRESS;
+	req->actual = 0;
+
+	if (ep == udc_dev.gadget->ep0) {
+		queue_add(&udc_dev.ep0->queue, req);
+
+		if (udc_dev.ep0->state == DIR_TX) {
+			debug_printk(KERN_ERR "QUEUE EP0 TX PACKET\n");
+
+			ep_set_index(&udc_dev, EP_INDEX(0, DIR_TX));
+			set_dsen_flag(&udc_dev);
+
+			req->actual = 
+				write_ep(&udc_dev, 
+					(unsigned short *) req->buf,
+					req->length <= 64 ? req->length : 64);
+			if (req->actual < 64)
+				set_vendp_flag(&udc_dev);
+		}
+
+		else {
+			int i;
+			int ret = 0;
+
+			udelay(1000);
+			
+			memset((char *) req->buf, 0, req->length);
+			#if 0
+			ep_set_index(&udc_dev, EP_INDEX(0, DIR_RX));
+			set_dsen_flag(&udc_dev);
+			#endif
+
+		      more:
+			for (i = 0; i < 150; i++) {
+				ep_set_index(&udc_dev, EP_INDEX(0, DIR_RX));
+				if (isp1763_readw(&dev->regs->buflen))
+					break;
+			}
+			if (!isp1763_readw(&dev->regs->buflen)) {
+				printk(KERN_ERR
+				       "%s ERROR: buflen == 0, bufstatus=%.4x\n",
+				       __FUNCTION__,
+				       isp1763_readw(&dev->regs->dcbufstatus));
+				local_irq_restore(flags);
+				enable_glint(&udc_dev);
+				return -EAGAIN;
+			}
+
+			if ((ret = read_ep0(udc_dev.ep0, req)) == 1) {
+				if (!req->no_interrupt)
+					if (req->complete) {
+						debug_printk(KERN_ERR
+							     "Completing OUT request %p (%i bytes) p=%p ep=%p\n",
+							     req,
+							     req->actual,
+							     req->complete,
+							     ep);
+						req->complete(ep, req);
+					}
+			} else if (ret == 0)
+				goto more;
+
+		}
+	}
+	/* normal data transfer */
+	else {
+		struct isp_ep *ispep;
+		int ep_index = get_ep_index(ep);
+
+		if (ep_index < 0) {
+			error_printk(KERN_ERR
+				     "ERROR: INVALID EP POINTER %p\n", ep);
+			local_irq_restore(flags);
+			enable_glint(dev);
+			return -EINVAL;
+		}
+
+		ispep = &isp_eps[ep_index];
+		queue_add(&ispep->queue, req);
+
+		/* IN transfer */
+		if (GET_EP_DIR(ep_index) ==
+		    DIR_TX /* && ispep->ep_type != EP_TYPE_INTERRUPT */ ) {
+			if (isp1763_readw(&dev->regs->buflen) == 0
+				    && queue_count(&ispep->queue) == 1) {
+				ep_set_index(&udc_dev, ispep->index);
+				req->actual +=
+				    write_ep(&udc_dev,
+					     (unsigned short *) req->buf,
+					     req->length <=
+					     ispep->maxpacketsize ? 
+					     req->length : ispep->maxpacketsize);
+				if (req->actual == req->length)
+					set_vendp_flag(&udc_dev);
+			}
+		}
+	}
+
+	if (isp_debug > 0)
+		dump_usb_request(ep, req);
+
+	local_irq_restore(flags);
+	enable_glint(dev);
+	return 0;
+}
+
+/**
+* isp1763_udc_ep_dequeue - dequeue endpoint request
+* @ep: endpoint
+* @req: request
+*
+* NOTE: currently unimplemented
+*/
+static int
+isp1763_udc_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+	return 0;
+}
+
+static int isp1763_udc_ep_set_halt(struct usb_ep *ep, int value)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+	return 0;
+}
+
+static int isp1763_udc_ep_set_wedge(struct usb_ep *ep)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+	return 0;
+}
+
+static int isp1763_udc_ep_fifo_status(struct usb_ep *ep)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+	return 0;
+}
+
+static void isp1763_udc_ep_fifo_flush(struct usb_ep *ep)
+{
+	info_printk(KERN_ERR "%s:%i: %s(%s)\n", __FILE__, __LINE__,
+		    __FUNCTION__, ep->name);
+}
+
+static struct usb_ep_ops isp1763_ep_ops = {
+	.enable = isp1763_udc_ep_enable,
+	.disable = isp1763_udc_ep_disable,
+	.alloc_request = isp1763_udc_ep_alloc_request,
+	.free_request = isp1763_udc_ep_free_request,
+	.queue = isp1763_udc_ep_queue,
+	.dequeue = isp1763_udc_ep_dequeue,
+	.set_halt = isp1763_udc_ep_set_halt,
+	.set_wedge = isp1763_udc_ep_set_wedge,
+	.fifo_status = isp1763_udc_ep_fifo_status,
+	.fifo_flush = isp1763_udc_ep_fifo_flush,
+};
+
+/* GADGET OPS */
+
+/**
+* isp_get_frame - return frame number
+* @gadget: gadget pointer
+*/
+static int isp_get_frame(struct usb_gadget *gadget)
+{
+	struct isp1763_dev *dev = gadget->dev.driver_data;
+	unsigned long flags;
+	int ret = 0;
+
+	local_irq_save(flags);
+	ret = isp1763_readw(&dev->regs->frameno);
+	local_irq_restore(flags);
+
+	return ret;
+}
+
+/**
+* isp_pullup - enable or disable pullup
+* @gadget: gadget pointer
+* @is_on: on/off
+*
+* Enable or disable pullup on D+
+* I'm not actually sure it's a good idea to implement this, nor do
+* I know a gadget driver that uses it...
+*/
+static int isp_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct isp1763_dev *dev = gadget->dev.driver_data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (is_on)
+		isp1763_writel(MASK_OTGCTRL_DP_PULLUP,
+			       &dev->regs->otg_ctrl);
+	else
+		isp1763_writel(MASK_OTGCTRL_DP_PULLUP << 16,
+			       &dev->regs->otg_ctrl);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static int isp_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	return -ENOTSUPP;
+}
+
+static int isp_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	return -ENOTSUPP;
+}
+
+static int isp_wakeup(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+static struct usb_gadget_ops isp1763_gadget_ops = {
+	.get_frame = isp_get_frame,
+	.wakeup = isp_wakeup,
+	.vbus_session = isp_vbus_session,
+	.vbus_draw = isp_vbus_draw,
+	.pullup = isp_pullup,
+
+};
+
+/* GADGET DEVICE FUNCTIONS */
+
+/**
+* usb_gadget_register_driver - register gadget driver
+* @driver: gadget driver to register
+*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	if (!driver ||
+	    !driver->bind ||
+	    !driver->unbind ||
+	    !driver->setup || driver->speed != USB_SPEED_HIGH) {
+		return -EINVAL;
+	}
+
+	info_printk(KERN_ERR "%s: registering gadget function '%s'\n",
+		    __FILE__, driver->function);
+
+	udc_dev.driver = driver;
+
+	udc_dev.driver->bind(udc_dev.gadget);
+
+	if (udc_dev.ctrl.active) {
+		configure_ep0(&udc_dev);
+		isp1763_udc_enable(&udc_dev);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/**
+* usb_gadget_unregister_driver - unregister gadget driver
+* @driver: gadget driver to unregister
+*/
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	if (udc_dev.ctrl.active) {
+		isp1763_udc_disable(&udc_dev);
+	}
+	
+	/* FIXME: Need to do some more here? */
+	udc_dev.driver->unbind(udc_dev.gadget);
+
+	udc_dev.driver = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static struct usb_ep ep0 = {
+	.name = "ep0",
+	.ops = &isp1763_ep_ops,
+	.maxpacket = 64,
+
+};
+
+static struct usb_gadget isp1763_gadget = {
+	.ops = &isp1763_gadget_ops,
+	.ep0 = &ep0,
+	.name = "isp1763_udc",
+	.speed = USB_SPEED_HIGH,
+	.is_otg = 1,
+	.b_hnp_enable = 0,
+	.a_hnp_support = 0,
+	.a_alt_hnp_support = 0,
+};
+
+/**
+* setup_ep_struct - setup endpoint structures
+* @dev: UDC device
+*/
+static void setup_ep_struct(struct isp1763_udc *dev)
+{
+	int i = 0;
+
+	dev->ep0 = kmalloc(sizeof(struct isp_ep), GFP_ATOMIC);
+	memset(dev->ep0, 0, sizeof(struct isp_ep));
+
+	INIT_LIST_HEAD(&dev->gadget->ep_list);
+	dev->ep0->queue = NULL;
+	dev->ep0->udc = &udc_dev;
+	dev->ep0->gadget = dev->gadget;
+	dev->ep0->state = 0;
+	dev->ep0->ep_type = EP_TYPE_UNUSED;
+	snprintf(dev->ep0->name, 8, "ep0");
+	dev->ep0->index = 0;
+
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		isp_eps[i].queue = NULL;
+		isp_eps[i].ep.driver_data = NULL;
+		isp_eps[i].ep.ops = &isp1763_ep_ops;
+		isp_eps[i].ep.ep_list = dev->gadget->ep_list;
+		isp_eps[i].ep.maxpacket = 4096;
+		isp_eps[i].udc = dev;
+		isp_eps[i].gadget = dev->gadget;
+		isp_eps[i].state = 0;
+		isp_eps[i].ep_type = EP_TYPE_UNUSED;
+		snprintf(isp_eps[i].name, 8, "ep%i%s",
+			 GET_EP_NUM_CANONICAL(i) + 1,
+			 GET_EP_DIR(i) == DIR_TX ? "in" : "out");
+		isp_eps[i].ep.name = kstrdup(isp_eps[i].name, GFP_KERNEL);
+		isp_eps[i].index = i + 2;
+		list_add_tail(&isp_eps[i].ep.ep_list,
+			      &dev->gadget->ep_list);
+
+	}
+}
+
+static void destroy_ep_struct(struct isp1763_udc *dev)
+{
+	int i = 0;
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		queue_flush(&isp_eps[i].queue);
+		kfree(isp_eps[i].ep.name);
+	}
+
+	queue_flush(&dev->ep0->queue);
+	kfree(dev->ep0);
+}
+
+/* IF FUNCTIONS */
+/**
+* handle_ep_irq - handle endpoint interrupts
+* @dev: UDC device
+* @bit: interrupt bit
+*/
+static void handle_ep_irq(struct isp1763_udc *dev, int bit)
+{
+	struct usb_request *req;
+	int ep_index = bit + 2;
+	struct isp_ep *ispep = NULL;
+
+	ispep = &dev->eps[bit];
+
+	debug_printk(KERN_ERR
+			"Interrupt for EP %s (Bit %i, epindex %i)\n", ispep->name, bit, ep_index);
+
+	if (queue_empty(&ispep->queue)) {
+		queue_printk(KERN_ERR "%s/%s queue empty!\n",
+				ispep->name,
+				ispep->ep.name);
+		goto done;
+	}
+
+	ep_set_index(dev, ep_index);
+	req = queue_get(&ispep->queue);
+	
+	if (GET_EP_DIR(ep_index) == DIR_RX) {
+		if (read_ep(ispep, req) > 0)
+			queue_topkill(&ispep->queue);
+	} else {
+		int already_sent_data = 0;
+		/*
+			* check if there's more data to write for 
+			* previous packet, otherwise send completion 
+		*/
+		if (req->actual == req->length) {
+			if (req->actual > 0
+				&& req->actual % 512 == 0) {
+				/* 
+				* insert ZLP to terminate transaction where 
+				* length is a multiple of 512
+				*/
+				write_ep(&udc_dev, NULL, 0);
+				set_vendp_flag(&udc_dev);
+			}
+			req->status = 0;
+			
+			if (req->complete) {
+				debug_printk
+					(KERN_ERR
+					"Completing IN request %p (%i bytes)\n",
+					req, req->actual);
+				req->complete(&ispep->ep, req);
+			}
+			queue_topkill(&ispep->queue);
+		} else {
+			int written;
+
+			already_sent_data = 1;
+			written = write_ep(&udc_dev,
+					(unsigned short	*) ((char *)
+						req->buf + req->actual),
+					(req->length - req->actual) <= 
+						ispep->maxpacketsize
+						? (req->length - req->actual) :
+						ispep->maxpacketsize);
+			if (written < ispep->maxpacketsize)
+				set_vendp_flag(&udc_dev);
+
+			req->actual += written;
+		}
+
+		/* 
+		 * if no data was submitted from previous paket, get next from 
+		 * list and submit that
+		 */
+		if (!already_sent_data) {
+			if (!queue_empty
+				(&ispep->queue)) {
+
+				req =queue_get(&ispep->queue);
+				req->actual = write_ep(&udc_dev, 
+					(unsigned short *)req->buf,
+					req->length <= ispep->maxpacketsize
+					? req->length :	ispep->maxpacketsize);
+				if (req->actual < ispep->maxpacketsize)
+					set_vendp_flag(&udc_dev);
+			}
+		}
+	}
+done:
+	return;
+}
+
+/**
+* isp1763_udc_do_irq - UDC interrupt handler
+* @ctrl: controller pointer
+*/
+static int isp1763_udc_do_irq(struct isp1763_controller *ctrl, u32 flags)
+{
+	struct isp1763_udc *dev = ctrl->priv;
+	u32 interrupts = 0;
+	int bit = 0;
+	int i;
+	
+	isp1763_writew(UNLOCK_CODE, &dev->regs->unlock);
+	disable_glint(dev);
+
+	isp1763_writel(irq_bits,
+		       &dev->regs->dc_int_enable);
+	interrupts = flags & irq_bits;
+
+	isp1763_otg_timer_cancel();
+	isp1763_otg_timer_start((otgtimer_callback_t) isp1763_udc_do_irq,
+				100, ctrl);
+	for (i = 1; i < USB_MAX_ENDPOINTS; i += 2) {
+		struct isp_ep *ispep = &udc_dev.eps[i];
+		if (queue_count(&ispep->queue) > 0) {
+			ep_set_index(dev, ispep->index);
+			if (isp1763_readw(&dev->regs->dcbufstatus)
+				== 0) {
+				interrupts |= 1 << (i + 12);
+			}
+		}
+	}
+				
+	if(!interrupts)
+		return 0;
+
+	#if 0
+	printk(KERN_ERR "dev->regs->dc_int_enable: %.8lx\n", isp1763_readl(&dev->regs->dc_int_enable));
+	printk(KERN_ERR "%s() flags = %.8lx irqbits = %.8lx\n", __FUNCTION__,flags, irq_bits);
+	#endif
+	/* VBUS event */
+	if (interrupts & MASK_DCINT_VBUS) {
+		u16 mode = isp1763_readw(&dev->regs->mode);
+
+		debug_printk(KERN_ERR "VBUS interrupt (Vbus = %i)\n",
+			     ! !(mode & MASK_MODE_VBUSSTAT));
+		if (mode & MASK_MODE_VBUSSTAT) {
+			isp1763_writel(MASK_OTGCTRL_DP_PULLUP |
+				       MASK_OTGCTRL_SW_SEL_HC_DC,
+				       &dev->regs->otg_ctrl);
+		} else
+			isp1763_writel(MASK_OTGCTRL_SW_SEL_HC_DC,
+				       &dev->regs->otg_ctrl);
+	}
+
+	/* Control endpoint setup event */
+	if (interrupts & MASK_DCINT_EP0SETUP) {
+		debug_printk(KERN_ERR "EP0 SETUP event\n");
+		handle_ep0_setup(dev);
+	}
+
+	/* EP0 TX */
+	if (interrupts & MASK_DCINT_EP0TX) {
+		struct usb_request *req;
+		int written = 0;
+
+		debug_printk(KERN_ERR "EP0 TX\n");
+
+		if (dev->ep0->state == DIR_RX)
+			goto done;
+
+		if (queue_empty(&dev->ep0->queue)) {
+			warn_printk(KERN_ERR "EP0TX queue empty!\n");
+			goto done;
+		}
+
+		req = queue_get(&dev->ep0->queue);
+
+		queue_printk(KERN_ERR "EP0 request len=%i, written=%i\n",
+			     req->length, req->actual);
+		ep_set_index(dev, EP_INDEX(0, DIR_TX));
+
+		if (req->actual < req->length) {
+			queue_printk(KERN_ERR
+				     "EP0 request need to write more data\n");
+			written += write_ep(dev,
+				     (unsigned short *) ((char *) req->buf + 
+				     req->actual),
+				     (req->length - req->actual) <=
+				     64 ? (req->length - req->actual) : 64);
+			if (written < 64)
+				set_vendp_flag(dev);
+
+			req->actual += written;
+		}
+		/* complete? */
+		if (req->actual == req->length) {
+			debug_printk(KERN_ERR "EP0 TX request complete\n");
+			ep_set_index(dev, EP_INDEX(0, DIR_RX));
+//			printk(KERN_ERR "%s:%i %s()\n", __FILE__,__LINE__,__FUNCTION__);
+			set_status_flag(dev);
+			req->status = 0;
+			if (!req->no_interrupt)
+				if (req->complete)
+					req->complete(dev->gadget->ep0,
+						      req);
+			queue_topkill(&dev->ep0->queue);
+		}
+
+		debug_printk(KERN_ERR "EP0 TX event\n");
+	}
+	
+	/* EP0 RX */
+	if (interrupts & MASK_DCINT_EP0RX) {
+		struct usb_request *req;
+
+		ep_set_index(dev, EP_INDEX(0, DIR_RX));
+		if (isp1763_readw(&dev->regs->buflen) == 0) {
+			set_status_flag(dev);
+			goto done;
+		}
+
+		if (queue_empty(&dev->ep0->queue)) {
+			debug_printk(KERN_ERR "EP0RX queue empty!\n");
+			goto done;
+		}
+
+		req = queue_get(&dev->ep0->queue);
+
+		queue_printk(KERN_ERR "EP0 request len=%i, written=%i\n",
+			     req->length, req->actual);
+
+		read_ep(dev->ep0, req);
+		set_status_flag(dev);
+		queue_topkill(&dev->ep0->queue);
+
+		debug_printk(KERN_ERR "EP0 RX event\n");
+	}
+
+	/* BUS reset */
+	if (interrupts & MASK_DCINT_BRESET) {
+		int i;
+#if 1
+		debug_printk(KERN_ERR "BUS reset detected\n");
+		dev->gadget->speed = USB_SPEED_FULL;
+		for (i = 0; i < 14; i++) {
+			isp1763_udc_ep_disable(&dev->eps[i].ep);
+		}
+#endif
+	}
+
+	/* Highspeed status change */
+	if (interrupts & MASK_DCINT_HS_STAT) {
+		debug_printk(KERN_ERR "Speed change detected\n");
+		dev->gadget->speed = USB_SPEED_HIGH;
+	}
+
+	/* EP interrupts */
+	if (interrupts & MASK_DCINT_EP_EVENT) {
+		for (bit = 0; bit < 14; bit++) {
+			if ((interrupts & (1 << (12 + bit))) == 0)
+				continue;
+			handle_ep_irq(dev, bit);
+		}
+	}
+
+done:
+	enable_glint(dev);
+	return 0;
+}
+
+/**
+* isp1763_udc_suspend - suspend gadget device
+* @ctrl: controller pointer
+*
+* Suspend device controller, usually in order to then activate the
+* host controller
+*/
+static int isp1763_udc_suspend(struct isp1763_controller *ctrl)
+{
+	struct isp1763_udc *dev = ctrl->priv;
+	info_printk(KERN_ERR "udc suspend called\n");
+
+	ctrl->active = 0;
+	isp1763_udc_disable(dev);
+
+	return 0;
+}
+
+/**
+* isp1763_udc_resume - resume gadget device
+* @ctrl: controller pointer
+*
+* Resume/Init device controller
+*/
+static int isp1763_udc_resume(struct isp1763_controller *ctrl)
+{
+	struct isp1763_udc *dev = ctrl->priv;
+	info_printk(KERN_ERR "udc resume called\n");
+
+	ctrl->active = 1;
+
+	configure_ep0(dev);
+	isp1763_udc_enable(dev);
+
+	return 0;
+}
+
+static void isp1763_dc_release(struct device *dev);
+
+/**
+* isp1763_udc_probe - device controller 'probe' function
+* @ctrl: controller pointer
+*
+* Initialize driver, but don't start controller yet
+*/
+static int isp1763_udc_probe(struct isp1763_controller *ctrl)
+{
+	struct isp1763_udc *dev = ctrl->priv;
+
+	printk(KERN_EMERG "%s:%i %s()\n", __FILE__, __LINE__,
+	       __FUNCTION__);
+	dev->regs = ctrl->regs;
+	if (!dev->regs) {
+		error_printk(KERN_ERR
+			     "%s: error retrieving memory mapping from base driver!\n",
+			     __FILE__);
+		return -ENODEV;
+	}
+	ep0.driver_data = dev;
+
+	dev->gadget = &isp1763_gadget;
+	dev->gadget->dev.driver_data = (void *) &udc_dev;
+	setup_ep_struct(dev);
+
+	dev_set_name(&udc_dev.gadget->dev, "gadget");
+	udc_dev.gadget->dev.release = isp1763_dc_release;
+	device_register(&udc_dev.gadget->dev);
+
+	udc_dev.eps = isp_eps;
+
+	int i;
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		warn_printk(KERN_ERR "EP %i (%s/%s)\n", i,
+			    udc_dev.eps[i].name, udc_dev.eps[i].ep.name);
+	}
+	return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void isp1763_dc_release(struct device *dev)
+{
+	printk(KERN_EMERG "%s:%i %s()\n", __FILE__, __LINE__,
+	       __FUNCTION__);
+}
+
+static int __init isp1763_dc_init(void)
+{
+	printk(KERN_EMERG "%s:%i %s()\n", __FILE__, __LINE__,
+	       __FUNCTION__);
+	udc_dev.ctrl.active = 0;
+	udc_dev.ctrl.probe = isp1763_udc_probe;
+	udc_dev.ctrl.do_irq = isp1763_udc_do_irq;
+	udc_dev.ctrl.suspend = isp1763_udc_suspend;
+	udc_dev.ctrl.resume = isp1763_udc_resume;
+	udc_dev.ctrl.priv = &udc_dev;
+	isp1763_register_ctrl(&udc_dev.ctrl, ROLE_DEVICE);
+
+	return 0;
+}
+
+static void __exit isp1763_dc_exit(void)
+{
+	isp1763_unregister_ctrl(ROLE_DEVICE);	destroy_ep_struct(&udc_dev);
+
+	return;
+}
+
+module_init(isp1763_dc_init);
+module_exit(isp1763_dc_exit);
+
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_AUTHOR("F. Voegel, Carangul.Tech");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/isp1763/isp1763_udc.h b/drivers/usb/isp1763/isp1763_udc.h
new file mode 100644
index 0000000..09b93d8
--- /dev/null
+++ b/drivers/usb/isp1763/isp1763_udc.h
@@ -0,0 +1,113 @@
+#ifndef __ISP1763_UDC_H__
+#define __ISP1763_UDC_H__
+
+enum ep_types {
+	EP_TYPE_UNUSED = 0,
+	EP_TYPE_ISOCHRONOUS,
+	EP_TYPE_BULK,
+	EP_TYPE_INTERRUPT,
+};
+
+#define EP_TYPE_ENABLE  (1 << 3)
+#define EP_TYPE_DBLBUF  (1 << 2)
+#define EP_TYPE_NOEMPKT (1 << 4)
+#define EP_MAXPKTSIZE_NTRANS_SHIFT 11
+
+#define USB_MAX_ENDPOINTS 14
+
+/**
+* struct queue - queue structure
+* @next: next pointer
+* @elem: data pointer
+*/
+struct queue {
+	struct queue *next;
+	void *elem;
+};
+
+/**
+* struct isp1763_udc - UDC device
+* @isp_ep: endpoints
+* @ep0: endpoint 0
+* @gadget: gadget interface
+* @gadget_driver: pointer to gadget driver
+* @isp1763_regs: pointer to memory mapped registers
+* @isp1763_controller: pointer to controller structure
+*/
+struct isp1763_udc {
+	struct isp_ep *eps;
+	struct isp_ep *ep0;
+	struct usb_gadget *gadget;
+	struct usb_gadget_driver *driver;
+	struct isp1763_regs *regs;
+
+	struct isp1763_controller ctrl;
+};
+
+/**
+* struct isp_ep - endpoint structure
+* @ep: embedded usb_ep structure
+* @queue: pointer to EP queue
+* @udc: pointer to parent UDC device struct
+* @gadget: pointer to gadget interface
+* @state: current state
+* @index: EP index
+* @name: EP name (eg ep0-in)
+* @maxpacketsize: maximum packet size
+* @ep_type: type of endpoint
+* @desc: copy of EP descriptor
+*/
+struct isp_ep {
+	struct usb_ep ep;
+	struct queue *queue;
+	struct isp1763_udc *udc;
+	struct usb_gadget *gadget;
+	u8 state;
+	int index;
+	char name[8];
+	int maxpacketsize;
+	int ep_type;
+	struct usb_endpoint_descriptor desc;
+};
+
+#define GET_EP_DIR(idx) ((idx) & 1 ? DIR_TX : DIR_RX)
+#define GET_EP_NUM_CANONICAL(epn) ((epn) >> 1)
+
+#define EP_INDEX(epnum, is_out) (((epnum) << 1) | !!is_out)
+#define set_clbuf_flag(dev)  isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				| MASK_CTRL_CLBUF, &(dev)->regs->ctrlfn)
+#define set_dsen_flag(dev)   isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				| MASK_CTRL_DSEN, &(dev)->regs->ctrlfn)
+#define set_status_flag(dev) isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				| MASK_CTRL_STATUS, &(dev)->regs->ctrlfn)
+#define set_stall_flag(dev)  isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				| MASK_CTRL_STALL, &(dev)->regs->ctrlfn)
+#define set_vendp_flag(dev)  isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				| MASK_CTRL_VENDP, &(dev)->regs->ctrlfn)
+
+#define clr_clbuf_flag(dev)  isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				& ~MASK_CTRL_CLBUF, &(dev)->regs->ctrlfn)
+#define clr_dsen_flag(dev)   isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				& ~MASK_CTRL_DSEN, &(dev)->regs->ctrlfn)
+#define clr_status_flag(dev) isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				& ~MASK_CTRL_STATUS, &(dev)->regs->ctrlfn)
+#define clr_stall_flag(dev)  isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				& ~MASK_CTRL_STALL, &(dev)->regs->ctrlfn)
+#define clr_vendp_flag(dev)  isp1763_writew(isp1763_readw(&(dev)->regs->ctrlfn) \
+				& ~MASK_CTRL_VENDP, &(dev)->regs->ctrlfn)
+
+#if 0
+#define disable_glint(dev)   isp1763_writew(isp1763_readw(&(dev)->regs->mode) \
+				& ~MASK_MODE_GLINTENA, &(dev)->regs->mode);
+#else
+#define disable_glint(dev) 
+#endif
+#define enable_glint(dev)    isp1763_writew(isp1763_readw(&(dev)->regs->mode) \
+				| MASK_MODE_GLINTENA,  &(dev)->regs->mode);
+#define ep_idx_to_addr(x) (x+2)
+
+
+#define disable_glint__(dev)   isp1763_writew(isp1763_readw(&(dev)->regs->mode) \
+				& ~MASK_MODE_GLINTENA, &(dev)->regs->mode);
+
+#endif
-- 
1.7.2.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux