All of lore.kernel.org
 help / color / mirror / Atom feed
From: John Otken <jotken@softadvances.com>
To: linuxppc-embedded@ozlabs.org
Subject: [PATCH 1/2] Support AMCC Taihu 405EP Eval Board
Date: Thu, 11 May 2006 13:25:27 -0500	[thread overview]
Message-ID: <44638197.1030207@softadvances.com> (raw)

This patch adds support for the AMCC Taihu 405EP
evaluation board.

I tested it against the latest Denx git tree
(2.6.17-rc3).

The defconfig file follows.

Comments are welcome.

Signed-off-by: John Otken <jotken@softadvances.com>


  arch/ppc/platforms/4xx/Kconfig    |   15
  arch/ppc/platforms/4xx/Makefile   |    1
  arch/ppc/platforms/4xx/taihu.c    |  260 +++++
  arch/ppc/platforms/4xx/taihu.h    |  105 ++
  drivers/mtd/maps/Kconfig          |    8
  drivers/mtd/maps/Makefile         |    1
  drivers/mtd/maps/taihu.c          |  155 +++
  drivers/usb/gadget/Kconfig        |   11
  drivers/usb/gadget/Makefile       |    1
  drivers/usb/gadget/epautoconf.c   |   15
  drivers/usb/gadget/gadget_chips.h |    8
  drivers/usb/gadget/pd12_udc.c     | 1821 +++++++++++++++++++++++++++++++++++++
  drivers/usb/gadget/pd12_udc.h     |  148 +++
  include/asm-ppc/ibm4xx.h          |    4
  14 files changed, 2548 insertions(+), 5 deletions(-)
  create mode 100644 arch/ppc/platforms/4xx/taihu.c
  create mode 100644 arch/ppc/platforms/4xx/taihu.h
  create mode 100644 drivers/mtd/maps/taihu.c
  create mode 100755 drivers/usb/gadget/pd12_udc.c
  create mode 100644 drivers/usb/gadget/pd12_udc.h



diff --git a/arch/ppc/platforms/4xx/Kconfig b/arch/ppc/platforms/4xx/Kconfig
index 51414c4..4efba1e 100644
--- a/arch/ppc/platforms/4xx/Kconfig
+++ b/arch/ppc/platforms/4xx/Kconfig
@@ -66,6 +66,12 @@ config XILINX_ML403
  	bool "Xilinx-ML403"
  	help
  	  This option enables support for the Xilinx ML403 evaluation board.
+
+config TAIHU
+	bool "Taihu"
+	select WANT_EARLY_SERIAL
+	help
+	  This option enables support for the AMCC 405EP evaluation board.
  endchoice

  choice
@@ -120,7 +126,6 @@ config YOSEMITE
         select WANT_EARLY_SERIAL
         help
           This option enables support for the AMCC PPC440EP evaluation board.
-
  endchoice

  config EP405PC
@@ -201,7 +206,7 @@ config BOOKE

  config IBM_OCP
  	bool
-	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || YUCCA || OCOTEA || P3P440 || PPChameleonEVB || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT || YELLOWSTONE || YOSEMITE
+	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || YUCCA || OCOTEA || P3P440 || PPChameleonEVB || REDWOOD_5 || REDWOOD_6 || SYCAMORE || TAIHU || WALNUT || YELLOWSTONE || YOSEMITE
  	default y

  config IBM_EMAC4
@@ -211,7 +216,7 @@ config IBM_EMAC4

  config BIOS_FIXUP
  	bool
-	depends on BUBINGA || EP405 || SYCAMORE || WALNUT
+	depends on BUBINGA || EP405 || SYCAMORE || TAIHU || WALNUT
  	default y

  # OAK doesn't exist but wanted to keep this around for any future 403GCX boards
@@ -222,7 +227,7 @@ config 403GCX

  config 405EP
  	bool
-	depends on BUBINGA || PPChameleonEVB
+	depends on BUBINGA || PPChameleonEVB || TAIHU
  	default y

  config 405GP
@@ -262,7 +267,7 @@ config EMBEDDEDBOOT

  config IBM_OPENBIOS
  	bool
-	depends on ASH || REDWOOD_5 || REDWOOD_6
+	depends on ASH || REDWOOD_5 || REDWOOD_6 || TAIHU
  	default y

  config PPC4xx_DMA
diff --git a/arch/ppc/platforms/4xx/Makefile b/arch/ppc/platforms/4xx/Makefile
index d3a7a16..86374ff 100644
--- a/arch/ppc/platforms/4xx/Makefile
+++ b/arch/ppc/platforms/4xx/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PPChameleonEVB)	+= ppchamel
  obj-$(CONFIG_REDWOOD_5)		+= redwood5.o
  obj-$(CONFIG_REDWOOD_6)		+= redwood6.o
  obj-$(CONFIG_SYCAMORE)		+= sycamore.o
+obj-$(CONFIG_TAIHU)		+= taihu.o
  obj-$(CONFIG_WALNUT)		+= walnut.o
  obj-$(CONFIG_XILINX_ML300)	+= xilinx_ml300.o
  obj-$(CONFIG_XILINX_ML403)	+= xilinx_ml403.o
diff --git a/arch/ppc/platforms/4xx/taihu.c b/arch/ppc/platforms/4xx/taihu.c
new file mode 100644
index 0000000..94bd72d
--- /dev/null
+++ b/arch/ppc/platforms/4xx/taihu.c
@@ -0,0 +1,260 @@
+/*
+ * Support for IBM PPC 405EP evaluation board (Taihu).
+ *
+ * Author: SAW (IBM), derived from walnut.c.
+ *         Maintained by MontaVista Software <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Softare Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/rtc.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include <asm/system.h>
+#include <asm/pci-bridge.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+#include <asm/page.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/todc.h>
+#include <asm/kgdb.h>
+#include <asm/ocp.h>
+#include <asm/ibm_ocp_pci.h>
+
+#include <platforms/4xx/ibm405ep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+extern bd_t __res;
+
+
+/* Some IRQs unique to the board
+ * Used by the generic 405 PCI setup functions in ppc4xx_pci.c
+ */
+int __init
+ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+	static char pci_irq_table[][4] =
+	    /*
+	     *      PCI IDSEL/INTPIN->INTLINE
+	     *      A       B       C       D
+	     */
+	{
+		{25, 26, 27, 28},	/* IDSEL 1 - PCI slot 1 */
+		{26, 27, 28, 25},	/* IDSEL 2 - PCI slot 2 */
+	};
+
+	const long min_idsel = 6, max_idsel = 7, irqs_per_slot = 4;
+	return PCI_IRQ_TABLE_LOOKUP;
+};
+
+/* The serial clock for the chip is an internal clock determined by
+ * different clock speeds/dividers.
+ * Calculate the proper input baud rate and setup the serial driver.
+ */
+static void __init
+taihu_early_serial_map(void)
+{
+	u32 uart_div;
+	int uart_clock;
+	struct uart_port port;
+
+         /* Calculate the serial clock input frequency
+          *
+          * The base baud is the PLL OUTA (provided in the board info
+          * structure) divided by the external UART Divisor, divided
+          * by 16.
+          */
+	uart_div = (mfdcr(DCRN_CPC0_UCR_BASE) & DCRN_CPC0_UCR_U0DIV);
+	uart_clock = __res.bi_pllouta_freq / uart_div;
+
+	/* Setup serial port access */
+	memset(&port, 0, sizeof(port));
+	port.membase = (void*)ACTING_UART0_IO_BASE;
+	port.irq = ACTING_UART0_INT;
+	port.uartclk = uart_clock;
+	port.regshift = 0;
+	port.iotype = SERIAL_IO_MEM;
+	port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
+	port.line = 0;
+
+	if (early_serial_setup(&port) != 0) {
+		printk("Early serial init of port 0 failed\n");
+	}
+
+	port.membase = (void*)ACTING_UART1_IO_BASE;
+	port.irq = ACTING_UART1_INT;
+	port.line = 1;
+
+	if (early_serial_setup(&port) != 0) {
+		printk("Early serial init of port 1 failed\n");
+	}
+}
+
+void __init
+bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip)
+{
+
+	unsigned int bar_response, bar;
+	/*
+	 * Expected PCI mapping:
+	 *
+	 *  PLB addr             PCI memory addr
+	 *  ---------------------       ---------------------
+	 *  0000'0000 - 7fff'ffff <---  0000'0000 - 7fff'ffff
+	 *  8000'0000 - Bfff'ffff --->  8000'0000 - Bfff'ffff
+	 *
+	 *  PLB addr             PCI io addr
+	 *  ---------------------       ---------------------
+	 *  e800'0000 - e800'ffff --->  0000'0000 - 0001'0000
+	 *
+	 * The following code is simplified by assuming that the bootrom
+	 * has been well behaved in following this mapping.
+	 */
+
+#ifdef DEBUG
+	int i;
+
+	printk("ioremap PCLIO_BASE = 0x%x\n", pcip);
+	printk("PCI bridge regs before fixup \n");
+	for (i = 0; i <= 3; i++) {
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha)));
+	}
+	printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms)));
+	printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la)));
+	printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms)));
+	printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la)));
+
+#endif
+
+	/* added for IBM boot rom version 1.15 bios bar changes  -AK */
+
+	/* Disable region first */
+	out_le32((void *) &(pcip->pmm[0].ma), 0x00000000);
+	/* PLB starting addr, PCI: 0x80000000 */
+	out_le32((void *) &(pcip->pmm[0].la), 0x80000000);
+	/* PCI start addr, 0x80000000 */
+	out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE);
+	/* 512MB range of PLB to PCI */
+	out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000);
+	/* Enable no pre-fetch, enable region */
+	out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff -
+						(PPC405_PCI_UPPER_MEM -
+						 PPC405_PCI_MEM_BASE)) | 0x01));
+
+	/* Disable region one */
+	out_le32((void *) &(pcip->pmm[1].ma), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].la), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000);
+	out_le32((void *) &(pcip->pmm[1].ma), 0x00000000);
+	out_le32((void *) &(pcip->ptm1ms), 0x00000001);
+
+	/* Disable region two */
+	out_le32((void *) &(pcip->pmm[2].ma), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].la), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000);
+	out_le32((void *) &(pcip->pmm[2].ma), 0x00000000);
+	out_le32((void *) &(pcip->ptm2ms), 0x00000000);
+	out_le32((void *) &(pcip->ptm2la), 0x00000000);
+
+	/* Zero config bars */
+	for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) {
+		early_write_config_dword(hose, hose->first_busno,
+					 PCI_FUNC(hose->first_busno), bar,
+					 0x00000000);
+		early_read_config_dword(hose, hose->first_busno,
+					PCI_FUNC(hose->first_busno), bar,
+					&bar_response);
+		DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n",
+		    hose->first_busno, PCI_SLOT(hose->first_busno),
+		    PCI_FUNC(hose->first_busno), bar, bar_response);
+	}
+	/* end work arround */
+
+#ifdef DEBUG
+	printk("PCI bridge regs after fixup \n");
+	for (i = 0; i <= 3; i++) {
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila)));
+		printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha)));
+	}
+	printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms)));
+	printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la)));
+	printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms)));
+	printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la)));
+
+#endif
+}
+
+static void __init
+taihu_set_emacdata(void)
+{
+	struct ocp_def *def;
+	struct ocp_func_emac_data *emacdata;
+
+	def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 0);
+	emacdata = def->additions;
+	emacdata->phy_map = 0x000fffff;	/* skip 0x00 .. 0x13 */
+}
+
+void __init
+taihu_setup_arch(void)
+{
+	taihu_set_emacdata();
+
+	ppc4xx_setup_arch();
+
+	ibm_ocp_set_emac(0, 1);
+
+        taihu_early_serial_map();
+
+        /* Identify the system */
+        printk("AMCC PowerPC 405EP Taihu Platform\n");
+}
+
+void __init
+taihu_map_io(void)
+{
+	ppc4xx_map_io();
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+	      unsigned long r6, unsigned long r7)
+{
+	ppc4xx_init(r3, r4, r5, r6, r7);
+
+	ppc_md.setup_arch = taihu_setup_arch;
+	ppc_md.setup_io_mappings = taihu_map_io;
+
+#ifdef CONFIG_KGDB
+	ppc_md.early_serial_map = taihu_early_serial_map;
+#endif
+}
+
diff --git a/arch/ppc/platforms/4xx/taihu.h b/arch/ppc/platforms/4xx/taihu.h
new file mode 100644
index 0000000..eb2aa7a
--- /dev/null
+++ b/arch/ppc/platforms/4xx/taihu.h
@@ -0,0 +1,105 @@
+/*
+ * Support for IBM PPC 405EP evaluation board (Taihu).
+ *
+ * Author: SAW (IBM), derived from walnut.h.
+ *         Maintained by MontaVista Software <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Softare Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifdef __KERNEL__
+#ifndef __TAIHU_H__
+#define __TAIHU_H__
+
+/* 405EP */
+#include <platforms/4xx/ibm405ep.h>
+
+#ifndef __ASSEMBLY__
+/*
+ * Data structure defining board information maintained by the boot
+ * ROM on IBM's evaluation board. An effort has been made to
+ * keep the field names consistent with the 8xx 'bd_t' board info
+ * structures.
+ */
+#define CONFIG_HAS_ETH1 1
+typedef struct board_info {
+	unsigned long	bi_memstart;	/* start of DRAM memory */
+	unsigned long	bi_memsize;	/* size	 of DRAM memory in bytes */
+	unsigned long	bi_flashstart;	/* start of FLASH memory */
+	unsigned long	bi_flashsize;	/* size	 of FLASH memory */
+	unsigned long	bi_flashoffset; /* reserved area for startup monitor */
+	unsigned long	bi_sramstart;	/* start of SRAM memory */
+	unsigned long	bi_sramsize;	/* size	 of SRAM memory */
+	unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS) */
+	unsigned long	bi_ip_addr;	/* IP Address */
+	unsigned char	bi_enetaddr[6];	/* Ethernet adress */
+	unsigned short	bi_ethspeed;	/* Ethernet speed in Mbps */
+	unsigned long	bi_intfreq;	/* Internal Freq, in MHz */
+	unsigned long	bi_busfreq;	/* Bus Freq, in MHz */
+	unsigned long	bi_baudrate;	/* Console Baudrate */
+#if defined(CONFIG_405)   || \
+    defined(CONFIG_405GP) || \
+    defined(CONFIG_405CR) || \
+    defined(CONFIG_405EP) || \
+    defined(CONFIG_440)
+	unsigned char	bi_s_version[4];	/* Version of this structure */
+	unsigned char	bi_r_version[32];	/* Version of the ROM (IBM) */
+	unsigned int	bi_pllouta_freq;	/* CPU (Internal) Freq, in Hz */
+	unsigned int	bi_plb_busfreq;	/* PLB Bus speed, in Hz */
+	unsigned int	bi_pci_busfreq;	/* PCI Bus speed, in Hz */
+	unsigned char	bi_pci_enetaddr[6];	/* PCI Ethernet MAC address */
+#endif
+
+#ifdef CONFIG_HAS_ETH1
+	/* second onboard ethernet port */
+	unsigned char   bi_enet1addr[6];
+#endif
+#ifdef CONFIG_HAS_ETH2
+	/* third onboard ethernet port */
+	unsigned char	bi_enet2addr[6];
+#endif
+#ifdef CONFIG_HAS_ETH3
+	unsigned char   bi_enet3addr[6];
+#endif
+
+#if defined(CONFIG_405GP) || defined(CONFIG_405EP) || defined (CONFIG_440GX) || \
+    defined(CONFIG_440EP) || defined(CONFIG_440GR)
+	unsigned int	bi_opbfreq;		/* OPB clock in Hz */
+	int		bi_iic_fast[2];		/* Use fast i2c mode */
+#endif
+#if defined(CONFIG_4xx)
+#if defined(CONFIG_440GX)
+	int 		bi_phynum[4];           /* Determines phy mapping */
+	int 		bi_phymode[4];          /* Determines phy mode */
+#elif defined(CONFIG_405EP) || defined(CONFIG_440)
+	int 		bi_phynum[2];           /* Determines phy mapping */
+	int 		bi_phymode[2];          /* Determines phy mode */
+#else
+	int 		bi_phynum[1];           /* Determines phy mapping */
+	int 		bi_phymode[1];          /* Determines phy mode */
+#endif
+#endif /* defined(CONFIG_4xx) */
+} bd_t;
+
+/* Some 4xx parts use a different timebase frequency from the internal clock.
+*/
+#define bi_tbfreq bi_intfreq
+
+
+/* The UART clock is based off an internal clock -
+ * define BASE_BAUD based on the internal clock and divider(s).
+ * Since BASE_BAUD must be a constant, we will initialize it
+ * using clock/divider values which OpenBIOS initializes
+ * for typical configurations at various CPU speeds.
+ * The base baud is calculated as (FWDA / EXT UART DIV / 16)
+ */
+#define BASE_BAUD      691200
+
+#define PPC4xx_MACHINE_NAME     "AMCC Taihu"
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __TAIHU_H__ */
+#endif /* __KERNEL__ */
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 9848471..41941c7 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -321,6 +321,14 @@ config MTD_ARCTIC
  	  Arctic board. If you have one of these boards and would like to
  	  use the flash chips on it, say 'Y'.

+config MTD_TAIHU
+	tristate "Flash device mapped on AMCC 405EP Taihu"
+	depends on MTD_CFI && PPC32 && 40x && TAIHU
+	help
+	  This enables access routines for the flash chips on the AMCC 405EP
+	  Taihu board. If you have one of these boards and would like to
+	  use the flash chips on it, say 'Y'.
+
  config MTD_WALNUT
  	tristate "Flash device mapped on AMCC 405GP/r/EP Walnut/Sycamore/Bubinga"
  	depends on MTD_JEDECPROBE && (WALNUT || SYCAMORE || BUBINGA)
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index c2cf73a..b2210de 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_OCOTEA)	+= ocotea.o
  obj-$(CONFIG_MTD_BAMBOO)	+= bamboo.o
  obj-$(CONFIG_MTD_BEECH)		+= beech-mtd.o
  obj-$(CONFIG_MTD_ARCTIC)	+= arctic-mtd.o
+obj-$(CONFIG_MTD_TAIHU)         += taihu.o
  obj-$(CONFIG_MTD_WALNUT)        += walnut.o
  obj-$(CONFIG_MTD_YOSEMITE)      += yosemite.o
  obj-$(CONFIG_MTD_H720X)		+= h720x-flash.o
diff --git a/drivers/mtd/maps/taihu.c b/drivers/mtd/maps/taihu.c
new file mode 100644
index 0000000..432e07d
--- /dev/null
+++ b/drivers/mtd/maps/taihu.c
@@ -0,0 +1,155 @@
+/*
+ *
+ * drivers/mtd/maps/taihu.c
+ *
+ * FLASH map for the AMCC Taihu boards.
+ *
+ * 2005 UDTech, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+#define BOOTWINDOW_ADDR 0xffe00000
+#define BOOTWINDOW_SIZE 0x00200000
+
+#define APPWINDOW_ADDR 	0xfc000000
+#define APPWINDOW_SIZE 	0x02000000
+
+
+static struct mtd_partition taihu_bootflash_partitions[] = {
+        {
+		.name = "kozio diags",
+		.offset = 0,
+		.size = 0x001a0000,
+		.mask_flags = MTD_WRITEABLE      /* force read-only */
+	},
+	{
+		.name = "u-boot env",
+		.offset = 0x001a0000,
+		.size = 0x00020000
+	},
+	{
+		.name = "u-boot",
+		.offset = 0x001c0000,
+		.size = 0x00040000,
+		.mask_flags = MTD_WRITEABLE     /* force read-only */
+	}
+};
+
+struct map_info taihu_bootflash_map = {
+	.name = "AMCC Taihu Boot Flash",
+	.size = BOOTWINDOW_SIZE,
+	.bankwidth = 2,
+	.phys = BOOTWINDOW_ADDR,
+};
+
+static struct mtd_partition taihu_appflash_partitions[] = {
+	{
+		.name = "kernel",
+		.offset = 0,
+		.size = 0x00300000,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	},
+	{
+		.name = "initrd",
+		.offset = 0x00300000,
+		.size = 0x01a00000,
+		.mask_flags = MTD_WRITEABLE    /* force read-only */
+	},
+	{
+		.name = "jffs2",
+		.offset = 0x01D00000,
+		.size = 0x00300000
+	}
+};
+
+
+struct map_info taihu_appflash_map = {
+	.name = "AMCC Taihu Application Flash",
+	.size = APPWINDOW_SIZE,
+	.bankwidth = 2,
+	.phys = APPWINDOW_ADDR,
+};
+
+
+#define NUM_TAIHU_FLASH_PARTITIONS(parts) \
+	(sizeof(parts)/sizeof(parts[0]))
+
+static struct mtd_info *taihu_mtd;
+
+int __init init_taihu_flash(void)
+{
+
+	printk(KERN_NOTICE "taihu: bootflash mapping: %x at %x\n",
+			BOOTWINDOW_SIZE, BOOTWINDOW_ADDR);
+	taihu_bootflash_map.virt = ioremap(BOOTWINDOW_ADDR, BOOTWINDOW_SIZE);
+	if (!taihu_bootflash_map.virt) {
+		printk("init_taihu_flash: failed to ioremap for bootflash\n");
+		return -EIO;
+	}
+	simple_map_init(&taihu_bootflash_map);
+	taihu_mtd = do_map_probe("cfi_probe", &taihu_bootflash_map);
+	if (taihu_mtd) {
+		taihu_mtd->owner = THIS_MODULE;
+		add_mtd_partitions(taihu_mtd,
+				taihu_bootflash_partitions,
+				ARRAY_SIZE(taihu_bootflash_partitions));
+	} else {
+		printk("map probe failed (bootflash)\n");
+		return -ENXIO;
+	}
+
+	printk(KERN_NOTICE "taihu: appflash mapping: %x at %x\n",
+			APPWINDOW_SIZE, APPWINDOW_ADDR);
+	taihu_appflash_map.virt = ioremap(APPWINDOW_ADDR, APPWINDOW_SIZE);
+	if (!taihu_appflash_map.virt) {
+		printk("init_taihu_flash: failed to ioremap for appflash\n");
+		return -EIO;
+	}
+	simple_map_init(&taihu_appflash_map);
+	taihu_mtd = do_map_probe("cfi_probe", &taihu_appflash_map);
+	if (taihu_mtd) {
+		taihu_mtd->owner = THIS_MODULE;
+		add_mtd_partitions(taihu_mtd,
+				taihu_appflash_partitions,
+				ARRAY_SIZE(taihu_appflash_partitions));
+	} else {
+		printk("map probe failed (appflash)\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_taihu_flash(void)
+{
+	if (taihu_mtd) {
+		del_mtd_partitions(taihu_mtd);
+		/* moved iounmap after map_destroy - armin */
+		map_destroy(taihu_mtd);
+	}
+
+	if (taihu_bootflash_map.virt)
+		iounmap((void *)taihu_bootflash_map.virt);
+	if (taihu_appflash_map.virt)
+		iounmap((void *)taihu_appflash_map.virt);
+}
+
+module_init(init_taihu_flash);
+module_exit(cleanup_taihu_flash);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MTD map driver for the AMCC Taihu board");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 363b2ad..c541e12 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -154,6 +154,17 @@ config USB_LH7A40X
  	default USB_GADGET
  	select USB_GADGET_SELECTED

+config USB_GADGET_PD12
+	boolean "PD12 UDC"
+	depends on TAIHU
+	help
+    This driver provides USB Device Controller driver for PD12 UDC
+
+config USB_PD12
+	tristate
+	depends on USB_GADGET_PD12
+	default USB_GADGET
+	select USB_GADGET_SELECTED

  config USB_GADGET_OMAP
  	boolean "OMAP USB Device Controller"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5a28e61..4422f49 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
  obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
  obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
  obj-$(CONFIG_USB_AT91)		+= at91_udc.o
+obj-$(CONFIG_USB_PD12)	+= pd12_udc.o

  #
  # USB gadget drivers
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index f7c6d75..9c32fa8 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -274,6 +274,21 @@ struct usb_ep * __init usb_ep_autoconfig
  		ep = find_ep (gadget, "ep1-bulk");
  		if (ep && ep_matches (gadget, ep, desc))
  			return ep;
+
+	} else if (gadget_is_pd12 (gadget)) {
+		if (USB_ENDPOINT_XFER_BULK == type
+				&& (USB_DIR_IN & desc->bEndpointAddress)) {
+			/* single buffering is enough */
+			ep = find_ep (gadget, "ep2in-bulk");
+			if (ep && ep_matches (gadget, ep, desc))
+				return ep;
+		} else if (USB_ENDPOINT_XFER_BULK == type
+				&& (USB_DIR_OUT & desc->bEndpointAddress)) {
+			/* DMA may be available */
+			ep = find_ep (gadget, "ep1out-bulk");
+			if (ep && ep_matches (gadget, ep, desc))
+				return ep;
+		}
  	}

  	/* Second, look at endpoints until an unclaimed one looks usable */
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index aa80f09..af3767c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -87,6 +87,12 @@
  #define gadget_is_at91(g)	0
  #endif

+#ifdef CONFIG_USB_GADGET_PD12
+#define gadget_is_pd12(g)	!strcmp("pd12_udc", (g)->name)
+#else
+#define gadget_is_pd12(g)	0
+#endif
+
  #ifdef CONFIG_USB_GADGET_IMX
  #define gadget_is_imx(g)	!strcmp("imx_udc", (g)->name)
  #else
@@ -169,5 +175,7 @@ static inline int usb_gadget_controller_
  		return 0x16;
  	else if (gadget_is_mpc8272(gadget))
  		return 0x17;
+	else if (gadget_is_pd12(gadget))
+		return 0x18;
  	return -ENOENT;
  }
diff --git a/drivers/usb/gadget/pd12_udc.c b/drivers/usb/gadget/pd12_udc.c
new file mode 100755
index 0000000..6c52353
--- /dev/null
+++ b/drivers/usb/gadget/pd12_udc.c
@@ -0,0 +1,1821 @@
+/*
+ * linux/drivers/usb/gadget/pd12_udc.c
+ * Taihu pd12-udc full speed USB device controllers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "pd12_udc.h"
+
+/*#define DEBUG_PD12 printk*/
+/*#define DEBUG_PD12_EP0 printk*/
+/*#define DEBUG_PD12_SETUP printk*/
+
+#ifndef DEBUG_PD12_EP0
+# define DEBUG_PD12_EP0(fmt,args...)
+#endif
+#ifndef DEBUG_PD12_SETUP
+# define DEBUG_PD12_SETUP(fmt,args...)
+#endif
+#ifndef DEBUG_PD12
+# define NO_STATES
+# define DEBUG_PD12(fmt,args...)
+#endif
+
+#define	DRIVER_DESC			"PD12 USB Device Controller"
+#define	DRIVER_VERSION		__DATE__
+
+
+static const char driver_name[] = "pd12_udc";
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+static void __iomem *th_pd12_virt;
+static void __iomem *th_cpld_virt;
+static u8 first_tran = 1;
+
+#define DEV_CMD_ADDR  ((ulong)th_pd12_virt+1)
+#define DEV_DATA_ADDR (th_pd12_virt)
+
+#define CPLD_REG0_ADDR (th_cpld_virt)
+#define CPLD_REG1_ADDR ((ulong)th_cpld_virt+1)
+/*
+  Local definintions.
+*/
+
+#ifndef NO_STATES
+static char *state_names[] = {
+	"WAIT_FOR_SETUP",
+	"DATA_STATE_XMIT",
+	"DATA_STATE_NEED_ZLP",
+	"WAIT_FOR_OUT_STATUS",
+	"DATA_STATE_RECV"
+};
+#endif
+
+/*
+  Local declarations.
+*/
+static int pd12_ep_enable(struct usb_ep *ep,
+			     const struct usb_endpoint_descriptor *);
+static int pd12_ep_disable(struct usb_ep *ep);
+static struct usb_request *pd12_alloc_request(struct usb_ep *ep, unsigned);
+static void pd12_free_request(struct usb_ep *ep, struct usb_request *);
+static void *pd12_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *,
+				  unsigned);
+static void pd12_free_buffer(struct usb_ep *ep, void *, dma_addr_t,
+				unsigned);
+static int pd12_queue(struct usb_ep *ep, struct usb_request *, unsigned);
+static int pd12_dequeue(struct usb_ep *ep, struct usb_request *);
+static int pd12_set_halt(struct usb_ep *ep, int);
+static void pd12_ep0_kick(struct pd12_udc *dev, struct pd12_ep *ep);
+static void pd12_handle_ep0(struct pd12_udc *dev);
+
+static void done(struct pd12_ep *ep, struct pd12_request *req,
+		 int status);
+static void stop_activity(struct pd12_udc *dev,
+			  struct usb_gadget_driver *driver);
+static void flush(struct pd12_ep *ep);
+static void udc_enable(struct pd12_udc *dev);
+static void udc_set_address(struct pd12_udc *dev, unsigned char address);
+
+static struct usb_ep_ops pd12_ep_ops = {
+	.enable = pd12_ep_enable,
+	.disable = pd12_ep_disable,
+
+	.alloc_request = pd12_alloc_request,
+	.free_request = pd12_free_request,
+
+	.alloc_buffer = pd12_alloc_buffer,
+	.free_buffer = pd12_free_buffer,
+
+	.queue = pd12_queue,
+	.dequeue = pd12_dequeue,
+
+	.set_halt = pd12_set_halt,
+};
+
+
+static __inline__ void read_data(volatile u8 *val)
+{
+	*val = *(volatile u8 *)DEV_DATA_ADDR;
+	udelay(5);
+}
+
+static __inline__ void write_data(u8 val)
+{
+	*(volatile u8 *)DEV_DATA_ADDR = val;
+	udelay(5);
+}
+
+static __inline__ void write_cmd(u8 val)
+{
+	*(volatile u8 *)DEV_CMD_ADDR = val;
+	udelay(5);
+}
+
+static __inline__ void usb_set_index(u8 ep)
+{
+	if(ep != 0)
+		ep += 1;
+	write_cmd(ep);
+}
+
+static void pd12_set_ack(u8 index)
+{
+	
+	write_cmd(index);
+	write_cmd(PD12_ACK_SETUP);
+	if(index  == 0)
+		write_cmd(PD12_CLEAR_BUF);
+		
+}
+
+static int write_fifo(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 *buf;
+	unsigned count;
+	unsigned length;
+	int is_last;
+	u8 ep_sts;
+	
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+	
+	count = ep->ep.maxpacket;	
+	length = req->req.length - req->req.actual;
+	length = min(length, count);
+	req->req.actual += length;
+
+	DEBUG_PD12("Write %d (max %d), fifo %p\n", length, count, buf);
+	write_cmd(1);
+	read_data(&ep_sts);
+/*	write_cmd(PD12_ACK_SETUP); */
+	write_cmd(PD12_WRITE_BUF);
+	write_data(0x0);
+	write_data(length);
+	if(length == 0)
+		write_data(0x0);
+	while(length--)
+		write_data(*buf++);
+	write_cmd(PD12_VALIDATE_BUF);
+	if(length != ep->ep.maxpacket)
+		is_last = 1;
+	else if(req->req.length == req->req.actual
+			&& !req->req.zero)
+		is_last = 1;
+	else
+		is_last = 0;
+
+	if(is_last)
+		done(ep,req,0);
+	return is_last;
+
+}
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_PD12_FILES
+
+static const char proc_node_name[] = "driver/udc";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+	      int *eof, void *_dev)
+{
+	char *buf = page;
+	struct pd12_udc *dev = _dev;
+	char *next = buf;
+	unsigned size = count;
+	unsigned long flags;
+	int t;
+	u8 ep_status[6];
+
+	if (off != 0)
+		return 0;
+
+	local_irq_save(flags);
+
+	/* basic device status */
+	t = scnprintf(next, size,
+		      DRIVER_DESC "\n"
+		      "%s version: %s\n"
+		      "Gadget driver: %s\n"
+		      "Host: %s\n\n",
+		      driver_name, DRIVER_VERSION,
+		      dev->driver ? dev->driver->driver.name : "(none)");
+	size -= t;
+	next += t;
+
+	for(i=0;i<PD12_MAX_ENDPOINTS;i++)
+	{
+		write_cmd(PD12_READ_LAST_STATUS+i);
+		read_data(&ep_status[i]);
+	}
+	t = scnprintf(next, size,
+		      "Endpoints last status:\n"
+			  " ep0: 0x%x, ep1: 0x%x, ep2: 0x%x\n"
+			  " ep3: 0x%x, ep4: 0x%x, ep5: 0x%x\n\n",
+		      ep_status[0], ep_status[1], ep_status[2],
+		      ep_status[3], ep_status[4], ep_status[5]
+	    );
+	size -= t;
+	next += t;
+
+	local_irq_restore(flags);
+	*eof = 1;
+	return count - size;
+}
+
+#define create_proc_files() 	create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() 	remove_proc_entry(proc_node_name, NULL)
+
+#else	/* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+static void pd12_set_mode(u8 val)
+{
+	write_cmd(PD12_SET_MODE);
+	write_data(val);
+	write_data(0x3);/*maybe 0x43*/
+}
+static void pd12_read_int(u16* val)
+{
+	write_cmd(PD12_READ_INT);
+	read_data((u8*)val);
+}
+
+static void pd12_read_lstatus(u8 index, u8* val)
+{
+	write_cmd(PD12_READ_LAST_STATUS + index);
+	read_data(val);
+}
+/*
+ * 	udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pd12_udc *dev)
+{
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, dev);
+
+	udc_set_address(dev, 0);
+	pd12_set_mode(0x06); /*disconnect soft connect pullup resior */
+	
+	dev->ep0state = WAIT_FOR_SETUP;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->usb_address = 0;
+}
+
+
+/*
+ * 	udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pd12_udc *dev)
+{
+	u8 i;
+	u16 tmp;
+	
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, dev);
+
+	udc_set_address(dev, 0);
+	pd12_set_mode(0x06); /*disconnect soft connect pullup resior */
+	mdelay(1500);
+	pd12_set_mode(0x16); /*soft connnect*/
+	pd12_read_int(&tmp);
+	for(i= 0; i< 5; i++)
+		pd12_read_lstatus(i,(u8 *)(&tmp));
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+	dev->ep0state = WAIT_FOR_SETUP;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->usb_address = 0;
+
+	/* basic endpoint records init */
+	for (i = 0; i < PD12_MAX_ENDPOINTS; i++) {
+		struct pd12_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->desc = 0;
+		ep->stopped = 0;
+		INIT_LIST_HEAD(&ep->queue);
+	}
+
+	/* the rest was statically initialized, and is read-only */
+}
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static void udc_enable(struct pd12_udc *dev)
+{
+
+	u8 i;
+	u16 tmp;
+	
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, dev);
+
+	pd12_set_mode(0x06); /*disconnect soft connect pullup resior */
+	mdelay(1500);
+	pd12_set_mode(0x16); /*soft connnect*/
+	pd12_read_int(&tmp);
+	for(i= 0; i< 5; i++)
+		pd12_read_lstatus(i,(u8 *)(&tmp));
+	dev->gadget.speed = USB_SPEED_FULL;
+
+}
+
+/*
+  Register entry point for the peripheral controller driver.
+*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct pd12_udc *dev = the_controller;
+	int retval;
+
+	DEBUG_PD12("%s: %s\n", __FUNCTION__, driver->driver.name);
+	if (!driver
+	    || driver->speed != USB_SPEED_FULL
+	    || !driver->bind
+	    || !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+	/* first hook up the driver ... */
+	dev->driver = driver;
+	dev->gadget.dev.driver = &driver->driver;
+
+	device_add(&dev->gadget.dev);
+	retval = driver->bind(&dev->gadget);
+	if (retval) {
+		printk("%s: bind to driver %s --> error %d\n", dev->gadget.name,
+		       driver->driver.name, retval);
+		device_del(&dev->gadget.dev);
+
+		dev->driver = 0;
+		dev->gadget.dev.driver = 0;
+		return retval;
+	}
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 * NOTE:  this shouldn't power up until later.
+	 */
+	printk("%s: registered gadget driver '%s'\n", dev->gadget.name,
+	       driver->driver.name);
+
+	udc_enable(dev);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/*
+  Unregister entry point for the peripheral controller driver.
+*/
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct pd12_udc *dev = the_controller;
+	unsigned long flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->driver = 0;
+	stop_activity(dev, driver);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	driver->unbind(&dev->gadget);
+	device_del(&dev->gadget.dev);
+
+	udc_disable(dev);
+
+	DEBUG_PD12("unregistered gadget driver '%s'\n", driver->driver.name);
+	return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------*/
+
+/** Read to request from FIFO (max read == bytes in fifo)
+ *  Return:  0 = still running, 1 = completed, negative = errno
+ *  NOTE: INDEX register must be set for EP
+ */
+static int read_fifo(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 count;
+	u8 ep_sts;
+	u8 *buf;
+	unsigned bufferspace, is_short;
+
+	/* make sure there's a packet in the FIFO. */
+	usb_set_index(ep_index(ep));
+	read_data(&ep_sts);
+	if ((ep_sts & UDC_FIFO_UNREADABLE) == UDC_FIFO_UNREADABLE) {
+		DEBUG_PD12("%s: Packet NOT ready!\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+	bufferspace = req->req.length - req->req.actual;
+
+	/* read all bytes from this packet */
+	write_cmd(PD12_READ_BUF);
+	read_data(&count);
+	read_data(&count);
+	req->req.actual += min((unsigned)count, bufferspace);
+
+	is_short = (count < ep->ep.maxpacket);
+	DEBUG_PD12("read %s %02x, %d bytes%s req %p %d/%d\n",
+	      ep->ep.name, ep_sts, count,
+	      is_short ? "/S" : "", req, req->req.actual, req->req.length);
+
+	while (count-- != 0) {
+
+		if (bufferspace == 0) {
+			/* this happens when the driver's buffer
+			 * is smaller than what the host sent.
+			 * discard the extra data.
+			 */
+			if (req->req.status != -EOVERFLOW)
+				printk("%s overflow %d\n", ep->ep.name, count);
+			req->req.status = -EOVERFLOW;
+		} else {
+			read_data(buf++);
+			bufferspace--;
+		}
+	}
+
+	write_cmd(PD12_CLEAR_BUF);
+
+	/* completion */
+	if (is_short || req->req.actual == req->req.length) {
+		done(ep, req, 0);
+
+		return 1;
+	}
+
+	/* finished that packet.  the next one may be waiting... */
+	return 0;
+}
+
+
+/*
+ *	done - retire a request; caller blocked irqs
+ *  INDEX register is preserved to keep same
+ */
+static void done(struct pd12_ep *ep, struct pd12_request *req, int status)
+{
+	unsigned int stopped = ep->stopped;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+	list_del_init(&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		DEBUG_PD12("complete %s req %p stat %d len %u/%u\n",
+		      ep->ep.name, &req->req, status,
+		      req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+
+	spin_unlock(&ep->dev->lock);
+	req->req.complete(&ep->ep, &req->req);
+	spin_lock(&ep->dev->lock);
+	ep->stopped = stopped;
+}
+
+
+/*
+ * 	nuke - dequeue ALL requests
+ */
+void nuke(struct pd12_ep *ep, int status)
+{
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+
+	/* Flush FIFO */
+	flush(ep);
+
+	/* called with irqs blocked */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+		done(ep, req, status);
+	}
+
+}
+
+/** Flush EP
+ * NOTE: INDEX register must be set before this call
+ */
+static void flush(struct pd12_ep *ep)
+{
+}
+
+/**
+ *  handle IN interrupt
+ */
+static void pd12_in_epn(struct pd12_udc *dev, u8 ep_idx)
+{
+	u8 ep_sts;
+	struct pd12_ep *ep = &dev->ep[ep_idx];
+	struct pd12_request *req;
+
+	usb_set_index(ep_idx);
+	read_data(&ep_sts);
+	DEBUG_PD12("%s: %d, status %x\n", __FUNCTION__, ep_idx, ep_sts);
+
+	if (ep_sts & UDC_EP_STALL) {
+		DEBUG_PD12("USB_EP_STALL\n");
+		return;
+	}
+
+	if (!ep->desc) {
+		DEBUG_PD12("%s: NO EP DESC\n", __FUNCTION__);
+		return;
+	}
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	DEBUG_PD12("req: %p\n", req);
+
+	if (!req)
+		return;
+
+	write_fifo(ep, req);
+}
+
+/*
+* handle OUT interrupt(recv)
+ */
+
+static void pd12_out_epn(struct pd12_udc *dev, u8 ep_idx)
+{
+	u8 ep_sts;
+	struct pd12_ep *ep = &dev->ep[ep_idx];
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s: %d\n", __FUNCTION__, ep_idx);
+
+	usb_set_index(ep_idx);
+	read_data(&ep_sts);
+	DEBUG_PD12("%s: %d, status %x\n", __FUNCTION__, ep_idx, ep_sts);
+
+	if (ep_sts & UDC_EP_STALL) {
+		DEBUG_PD12("USB_EP_STALL\n");
+		flush(ep);
+		return;
+	}
+
+	if (ep->desc) {
+
+		if (list_empty(&ep->queue))
+			req = 0;
+		else
+			req = list_entry(ep->queue.next,
+					   struct pd12_request,
+					   queue);
+
+		if (!req) {
+			printk("%s: NULL REQ %d\n",
+				   __FUNCTION__, ep_idx);
+			flush(ep);
+		} else {
+			read_fifo(ep, req);
+		}
+
+	} else {
+		/* Throw packet away.. */
+		printk("%s: No descriptor?!?\n", __FUNCTION__);
+		flush(ep);
+	}
+}
+
+static void stop_activity(struct pd12_udc *dev,
+			  struct usb_gadget_driver *driver)
+{
+	int i;
+
+	/* don't disconnect drivers more than once */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = 0;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < PD12_MAX_ENDPOINTS - 1; i++) {
+		struct pd12_ep *ep = &dev->ep[i];
+		ep->stopped = 1;
+
+		usb_set_index(i);
+		write_cmd(PD12_SET_STATUS);
+		write_data(0x1);
+		nuke(ep, -ESHUTDOWN);
+	}
+		
+	write_cmd(1);
+	write_cmd(PD12_SET_STATUS);
+	write_data(0x1);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&dev->lock);
+		driver->disconnect(&dev->gadget);
+		spin_lock(&dev->lock);
+	}
+
+	/* re-init driver-visible data structures */
+	udc_reinit(dev);
+}
+
+/** Handle USB RESET interrupt
+ */
+static void pd12_reset_intr(struct pd12_udc *dev)
+{
+
+	struct pd12_request *req;
+	struct pd12_ep *ep = &dev->ep[0];
+
+	DEBUG_PD12_EP0("%s: \n", __FUNCTION__);
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	if (req){
+		done(ep,req,0);
+	} else {
+		DEBUG_PD12_EP0("%s: NULL REQ\n", __FUNCTION__);
+	}
+
+	udc_set_address(dev, 0);
+	pd12_set_ack(0);
+	pd12_set_ack(1);
+	dev->ep0state = WAIT_FOR_SETUP;
+	first_tran = 1;
+}
+
+/*
+ *	pd12 usb client interrupt handler.
+ */
+static irqreturn_t pd12_udc_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct pd12_udc *dev = _dev;
+	volatile u8 int_status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock,flags);
+
+	DEBUG_PD12("%s (on state %s)\n", __FUNCTION__,
+		  state_names[dev->ep0state]);
+	write_cmd(PD12_READ_INT);
+	read_data(&int_status);
+	if (int_status & (PD12_CNTL_IN | PD12_CNTL_OUT))
+	{
+		if((int_status & PD12_CNTL_OUT) == PD12_CNTL_OUT)
+			dev->ep0state = WAIT_FOR_SETUP;
+		int_status &= ~(PD12_CNTL_IN | PD12_CNTL_OUT);
+		DEBUG_PD12("PD12_EP0 (control)\n");
+		pd12_handle_ep0(dev);
+		
+	}
+	if (int_status & PD12_SUSPEND_CHG)
+	{
+		u8 tmp;
+		tmp = *(volatile char *)CPLD_REG0_ADDR;
+		int_status &= ~PD12_SUSPEND_CHG;
+		if(tmp & USB_SUSPEND)
+		{
+		/*	write_cmd(PD12_SND_RESUME); */
+			if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->resume)
+				dev->driver->resume(&dev->gadget);
+		}
+		else
+		{	
+			if (dev->gadget.speed !=
+				   USB_SPEED_FULL && dev->driver
+				   && dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+		}
+	}
+	if (int_status & PD12_BUS_RST)
+	{
+		int_status &= ~PD12_BUS_RST;
+		pd12_reset_intr(dev);
+	}
+	if (int_status & PD12_EP1_IN)
+	{
+		int_status &= ~PD12_EP1_IN;
+		DEBUG_PD12("PD12_EP1_IN\n");
+		pd12_in_epn(dev, 2);
+	}
+	if (int_status & PD12_MAIN_IN)
+	{
+		int_status &= ~PD12_MAIN_IN;
+		DEBUG_PD12("PD12_MAIN_IN\n");
+		pd12_in_epn(dev, 4);
+	}
+	if (int_status & PD12_EP1_OUT)
+	{
+		int_status &= ~PD12_EP1_OUT;
+		DEBUG_PD12("PD12_EP1_OUT\n");
+		pd12_out_epn(dev, 1);
+	}
+	if (int_status & PD12_MAIN_OUT)
+	{
+		int_status &= ~PD12_MAIN_OUT;
+		DEBUG_PD12("PD12_MAIN_OUT\n");
+		pd12_out_epn(dev, 3);
+	}
+			
+	spin_unlock_irqrestore(&dev->lock,flags);
+	return IRQ_HANDLED;
+}
+
+static int pd12_ep_enable(struct usb_ep *_ep,
+			     const struct usb_endpoint_descriptor *desc)
+{
+	struct pd12_ep *ep;
+	struct pd12_udc *dev;
+	unsigned long flags;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _ep);
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+	    || desc->bDescriptorType != USB_DT_ENDPOINT
+	    || ep->bEndpointAddress != desc->bEndpointAddress
+	    || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
+		DEBUG_PD12("%s, bad ep or descriptor\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	/* xfer types must match, except that interrupt ~= bulk */
+	if (ep->bmAttributes != desc->bmAttributes ) {
+		DEBUG_PD12("%s, %s type mismatch\n", __FUNCTION__, _ep->name);
+		return -EINVAL;
+	}
+
+	/* hardware _could_ do smaller, but driver doesn't */
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+	     && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))
+	    || !desc->wMaxPacketSize) {
+		DEBUG_PD12("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
+		return -ERANGE;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		DEBUG_PD12("%s, bogus device state\n", __FUNCTION__);
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	ep->stopped = 0;
+	ep->desc = desc;
+	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* Reset halt state (does flush) */
+	pd12_set_halt(_ep, 0);
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG_PD12("%s: enabled %s\n", __FUNCTION__, _ep->name);
+	return 0;
+}
+
+/** Disable EP
+ *  NOTE: Sets INDEX register
+ */
+static int pd12_ep_disable(struct usb_ep *_ep)
+{
+	struct pd12_ep *ep;
+	unsigned long flags;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _ep);
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || !ep->desc) {
+		DEBUG_PD12("%s, %s not enabled\n", __FUNCTION__,
+		      _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	usb_set_index(ep_index(ep));
+
+	/* Nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+
+	/* Disable ep  */
+	write_cmd(PD12_SET_EP_EN);
+	write_data(0x0);
+
+	ep->desc = 0;
+	ep->stopped = 1;
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG_PD12("%s: disabled %s\n", __FUNCTION__, _ep->name);
+	return 0;
+}
+
+static struct usb_request *pd12_alloc_request(struct usb_ep *ep,
+						 unsigned gfp_flags)
+{
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+
+	req = kmalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return 0;
+
+	memset(req, 0, sizeof *req);
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void pd12_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct pd12_request *req;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+
+	req = container_of(_req, struct pd12_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static void *pd12_alloc_buffer(struct usb_ep *ep, unsigned bytes,
+				  dma_addr_t * dma, unsigned gfp_flags)
+{
+	char *retval;
+
+	DEBUG_PD12("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags);
+
+	retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
+	if (retval)
+		*dma = virt_to_bus(retval);
+	return retval;
+}
+
+static void pd12_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma,
+				unsigned bytes)
+{
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, ep);
+	kfree(buf);
+}
+
+/** Queue one request
+ *  Kickstart transfer if needed
+ *  NOTE: Sets INDEX register
+ */
+static int pd12_queue(struct usb_ep *_ep, struct usb_request *_req,
+			 unsigned gfp_flags)
+{
+	struct pd12_request *req;
+	struct pd12_ep *ep;
+	struct pd12_udc *dev;
+	unsigned long flags;
+	u8 ep_status;
+
+	DEBUG_PD12("\n\n\n%s, %p\n", __FUNCTION__, _ep);
+
+	req = container_of(_req, struct pd12_request, req);
+	if (!_req || !_req->complete || !_req->buf
+	     || !list_empty(&req->queue)) {
+		DEBUG_PD12("%s, bad params\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || (!ep->desc && (ep->ep.name != ep0name))) {
+		DEBUG_PD12("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		DEBUG_PD12("%s, bogus device state %p\n", __FUNCTION__, dev->driver);
+		return -ESHUTDOWN;
+	}
+
+	DEBUG_PD12("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length,
+	      _req->buf);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	DEBUG_PD12("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue),
+	      ep->stopped);
+	if (list_empty(&ep->queue) && !ep->stopped) {
+
+		if (ep_index(ep) == 0) {
+			/* EP0 */
+			list_add_tail(&req->queue, &ep->queue);
+			pd12_ep0_kick(dev, ep);
+			req = 0;
+		} else if (ep_is_in(ep)) {
+			/* EP2 & EP4 */
+			usb_set_index(ep_index(ep));
+			read_data(&ep_status);
+			if ((ep_status & 0x0) == 0x0) {
+				if (write_fifo(ep, req) == 1)
+					req = 0;
+			}
+		} else {
+			/* EP1  & EP3 */
+			usb_set_index(ep_index(ep));
+			read_data(&ep_status);
+			if ((ep_status & 0x01) == 0x01) {
+				if (read_fifo(ep, req) == 1)
+					req = 0;
+			}
+		}
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (req != 0)
+		list_add_tail(&req->queue, &ep->queue);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/* dequeue JUST ONE request */
+static int pd12_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pd12_ep *ep;
+	struct pd12_request *req;
+	unsigned long flags;
+
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _ep);
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || ep->ep.name == ep0name )
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->dev->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+/** Halt specific EP
+ *  Return 0 if success
+ *  NOTE: Sets INDEX register to EP !
+ */
+static int pd12_set_halt(struct usb_ep *_ep, int value)
+{
+	struct pd12_ep *ep;
+	unsigned long flags;
+	u8 ep_status;
+
+	ep = container_of(_ep, struct pd12_ep, ep);
+	if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+		DEBUG_PD12("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	usb_set_index(ep_index(ep));
+
+	DEBUG_PD12("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value);
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	if (ep_index(ep) == 0) {
+		/* EP0 */
+		write_cmd(PD12_SET_STATUS | ep_index(ep));
+		write_data(0x01);
+	} else if (ep_is_in(ep)) {
+		write_cmd(PD12_READ_EP_STATUS);
+		read_data(&ep_status);
+		if (value && ((ep_status & 0x60) /*buffer 0 or 1 full*/
+			      || !list_empty(&ep->queue))) {
+			/*
+			 * Attempts to halt IN endpoints will fail (returning -EAGAIN)
+			 * if any transfer requests are still queued, or if the controller
+			 * FIFO still holds bytes that the host hasn't collected.
+			 */
+			spin_unlock_irqrestore(&ep->dev->lock, flags);
+			DEBUG_PD12
+			    ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n",
+			     (ep_status & 0x60),
+			     !list_empty(&ep->queue));
+			return -EAGAIN;
+		}
+		flush(ep);
+		if (value)
+		{
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x01);
+		} else {
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x00);
+		}
+
+	} else {
+
+		flush(ep);
+		if (value)
+		{
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x01);
+		} else {
+			write_cmd(PD12_SET_STATUS | ep_index(ep));
+			write_data(0x00);
+		}
+	}
+
+	if (value) {
+		ep->stopped = 1;
+	} else {
+		ep->stopped = 0;
+	}
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG_PD12("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS");
+
+	return 0;
+}
+
+
+/****************************************************************/
+/* End Point 0 related functions                                */
+/****************************************************************/
+
+/* return:  0 = still running, 1 = completed, negative = errno */
+static int write_fifo_ep0(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 max;
+	unsigned count;
+	int is_last;
+	unsigned length;
+	u8* buf;
+	u8 ep_sts;
+
+	write_cmd(1); /* index 1*/
+	read_data(&ep_sts);
+/*	pd12_set_ack(1); */
+	max = ep_maxpacket(ep);
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	DEBUG_PD12_EP0("%s\n", __FUNCTION__);
+
+	length = req->req.length - req->req.actual;
+	length = min(length, (unsigned)max);
+	req->req.actual += length;
+
+	DEBUG_PD12("Write %d (max %d), fifo %p\n", length, max, buf);
+
+	count = length;
+	write_cmd(PD12_WRITE_BUF);
+	write_data(0x0);
+	write_data(count);
+	while (length--) {
+		write_data(*buf++);
+	}
+	
+	write_cmd(PD12_VALIDATE_BUF);
+	/* last packet is usually short (or a zlp) */
+	if (unlikely(count != max))
+		is_last = 1;
+	else {
+		if (likely(req->req.length != req->req.actual) || req->req.zero)
+			is_last = 0;
+		else
+			is_last = 1;
+	}
+
+	DEBUG_PD12_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__,
+		  ep->ep.name, count,
+		  is_last ? "/L" : "", req->req.length - req->req.actual, req);
+
+	/* requests complete when all IN data is in the FIFO */
+	if (is_last) {
+		done(ep, req, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+static __inline__ void pd12_fifo_read(struct pd12_ep *ep,
+					unsigned char *cp, u8 max)
+{
+	u8 count;
+
+	usb_set_index(0);
+	write_cmd(PD12_READ_BUF);
+	read_data(&count);
+	read_data(&count);
+	if (count > max)
+		count = max;
+	while (count--){
+		read_data(cp++);
+	}
+	write_cmd(PD12_CLEAR_BUF);
+}
+
+static __inline__ void pd12_fifo_write(struct pd12_ep *ep,
+					  unsigned char *cp, u8 count)
+{
+	write_cmd(1); /* index 1 */
+	write_cmd(PD12_WRITE_BUF);
+	write_data(0x0);
+	write_data(count);
+	DEBUG_PD12_EP0("fifo_write: %d %d\n", ep_index(ep), count);
+	while (count--)
+		write_data(*cp++);
+	write_cmd(PD12_VALIDATE_BUF);
+}
+static int read_fifo_ep0(struct pd12_ep *ep, struct pd12_request *req)
+{
+	u8 ep_status;
+	u8 len;
+	u8 *buf;
+	unsigned bufferspace, count, is_short;
+
+	DEBUG_PD12_EP0("%s\n", __FUNCTION__);
+
+	usb_set_index(0); /* index 0*/
+	read_data(&ep_status);
+	if ((ep_status & UDC_FIFO_UNREADABLE) == UDC_FIFO_UNREADABLE)
+		return 0;
+
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+	bufferspace = req->req.length - req->req.actual;
+
+	write_cmd(PD12_READ_BUF);
+	read_data(&len);
+	read_data(&len);
+	count = (unsigned)len;
+
+	is_short = (count < ep->ep.maxpacket);
+	DEBUG_PD12_EP0("read %s, %d bytes%s req %p %d/%d\n",
+		  ep->ep.name, count,
+		  is_short ? "/S" : "", req, req->req.actual, req->req.length);
+
+	while (count--) {
+		u8 byte;
+		read_data(&byte);
+		if (unlikely(bufferspace == 0)) {
+			/* this happens when the driver's buffer
+			 * is smaller than what the host sent.
+			 * discard the extra data.
+			 */
+			if (req->req.status != -EOVERFLOW)
+				DEBUG_PD12_EP0("%s overflow %d\n", ep->ep.name,
+					  count);
+			req->req.status = -EOVERFLOW;
+		} else {
+			*buf++ = byte;
+			bufferspace--;
+		}
+	}
+
+	/* completion */
+	if (is_short || req->req.actual == req->req.length) {
+		done(ep, req, 0);
+		return 1;
+	}
+
+	/* finished that packet.  the next one may be waiting... */
+	return 0;
+}
+
+/**
+ * udc_set_address - set the USB address for this device
+ * @address:
+ *
+ * Called from control endpoint function after it decodes a set address setup packet.
+ */
+static void udc_set_address(struct pd12_udc *dev, unsigned char address)
+{
+	DEBUG_PD12_EP0("%s: %d\n", __FUNCTION__, address);
+
+	dev->usb_address = address;
+	write_cmd(PD12_SET_ADDR_EN);
+	write_data(0x80 | address);
+}
+
+/*
+ * DATA_STATE_RECV (OUT_PKT_RDY)
+ *      - if error
+ *              set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
+ *      - else
+ *              set EP0_CLR_OUT bit
+ 				if last set EP0_DATA_END bit
+ */
+static void pd12_ep0_out(struct pd12_udc *dev)
+{
+	struct pd12_request *req;
+	struct pd12_ep *ep = &dev->ep[0];
+	int ret;
+
+	DEBUG_PD12_EP0("%s: \n", __FUNCTION__);
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	if (req) {
+
+		if (req->req.length == 0) {
+			DEBUG_PD12_EP0("ZERO LENGTH OUT!\n");
+			dev->ep0state = WAIT_FOR_SETUP;
+			return;
+		}
+		ret = read_fifo_ep0(ep, req);
+		if (ret) {
+			/* Done! */
+			DEBUG_PD12_EP0("%s: finished, waiting for status\n",
+				  __FUNCTION__);
+			/* read last status here ?*/
+			dev->ep0state = WAIT_FOR_SETUP;
+		} else {
+			/* Not done yet.. */
+			DEBUG_PD12_EP0("%s: not finished\n", __FUNCTION__);
+			/* usb_set(EP0_CLR_OUT, USB_EP0_CSR); */
+		}
+	} else {
+		DEBUG_PD12_EP0("NO REQ??!\n");
+	}
+}
+
+/*
+ * DATA_STATE_XMIT
+ */
+static int pd12_ep0_in(struct pd12_udc *dev)
+{
+	struct pd12_request *req;
+	struct pd12_ep *ep = &dev->ep[0];
+	int ret, need_zlp = 0;
+	u8 val;
+	
+	DEBUG_PD12_EP0("%s: \n", __FUNCTION__);
+
+
+	pd12_read_lstatus(1,&val);
+	pd12_set_ack(1);
+	if(((val & 0x1) != 0x1) && !first_tran){
+	/*	printk("return from ep0_in\n"); */
+		return 0;
+	}
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pd12_request, queue);
+
+	if (!req) {
+		dev->ep0state = WAIT_FOR_SETUP;
+		DEBUG_PD12_EP0("%s: NULL REQ\n", __FUNCTION__);
+		return 0;
+	}
+
+	if (req->req.length == 0) {
+		dev->ep0state = WAIT_FOR_SETUP;
+		return 1;
+	}
+
+	ret = write_fifo_ep0(ep, req);
+	first_tran = 0;
+	if (ret == 1 && !need_zlp) {
+		/* Last packet */
+		DEBUG_PD12_EP0("%s: finished, waiting for status\n", __FUNCTION__);
+		dev->ep0state = WAIT_FOR_SETUP;
+	} else {
+		DEBUG_PD12_EP0("%s: not finished\n", __FUNCTION__);
+	}
+
+	return 1;
+}
+
+static int pd12_handle_get_status(struct pd12_udc *dev,
+				     struct usb_ctrlrequest *ctrl)
+{
+	struct pd12_ep *ep0 = &dev->ep[0];
+	struct pd12_ep *qep;
+	int reqtype = (ctrl->bRequestType & USB_RECIP_MASK);
+	u16 val = 0;
+	u8 ep_sts;
+
+	if (reqtype == USB_RECIP_INTERFACE) {
+		/* This is not supported.
+		 * And according to the USB spec, this one does nothing..
+		 * Just return 0
+		 */
+		DEBUG_PD12_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n");
+	} else if (reqtype == USB_RECIP_DEVICE) {
+		DEBUG_PD12_SETUP("GET_STATUS: USB_RECIP_DEVICE\n");
+		val |= (1 << 0);	/* Self powered */
+		/*val |= (1<<1); */	/* Remote wakeup */
+	} else if (reqtype == USB_RECIP_ENDPOINT) {
+		int ep_num = (ctrl->wIndex & ~USB_DIR_IN);
+
+		DEBUG_PD12_SETUP
+		    ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n",
+		     ep_num, ctrl->wLength);
+
+		if (ctrl->wLength > 2 || ep_num > 3) /* ep_num cannt abouve maxenpoint?*/
+			return -EOPNOTSUPP;
+
+		qep = &dev->ep[ep_num];
+		if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0)
+		    && ep_index(qep) != 0) {
+			return -EOPNOTSUPP;
+		}
+
+		usb_set_index(ep_index(qep));
+		read_data(&ep_sts);
+		val = (u16)ep_sts;
+
+		/* Back to EP0 index */
+		usb_set_index(0);
+
+		DEBUG_PD12_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num,
+			    ctrl->wIndex, val);
+	} else {
+		DEBUG_PD12_SETUP("Unknown REQ TYPE: %d\n", reqtype);
+		return -EOPNOTSUPP;
+	}
+
+	/* Put status to FIFO */
+	pd12_fifo_write(ep0, (u8 *) & val, sizeof(val));
+
+	return 0;
+}
+
+/*
+ * WAIT_FOR_SETUP
+ *      - read data packet from EP0 FIFO
+ *      - decode command
+ *      - if error
+ *              set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
+ *      - else
+ *              set EP0_CLR_OUT | EP0_DATA_END bits
+ */
+static void pd12_ep0_setup(struct pd12_udc *dev)
+{
+	struct pd12_ep *ep = &dev->ep[0];
+	struct usb_ctrlrequest ctrl;
+	int i;
+	u8 stat;
+	u8 inbuf[16];
+	
+	DEBUG_PD12_SETUP("%s: \n", __FUNCTION__);
+
+	/* Nuke all previous transfers */
+	nuke(ep, -EPROTO);
+	pd12_read_lstatus(0,&stat);
+	pd12_set_ack(0);
+	pd12_set_ack(1);
+	if((stat & 0x01) != 0x01){
+		return ;
+	}
+	pd12_fifo_read(ep, (u8 *)&ctrl, 8);
+
+	DEBUG_PD12_SETUP("CTRL.bRequestType = 0x%x (is_in 0x%x)\n", ctrl.bRequestType,
+		    ctrl.bRequestType == USB_DIR_IN);
+	DEBUG_PD12_SETUP("CTRL.bRequest = 0x%x\n", ctrl.bRequest);
+	DEBUG_PD12_SETUP("CTRL.wLength = 0x%x\n", ctrl.wLength);
+	DEBUG_PD12_SETUP("CTRL.wValue = 0x%x (%d)\n", ctrl.wValue, ctrl.wValue >> 8);
+	DEBUG_PD12_SETUP("CTRL.wIndex = 0x%x\n", ctrl.wIndex);
+
+	/* Set direction of EP0 */
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		ep->bEndpointAddress |= USB_DIR_IN;
+	} else {
+		ep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+
+	/* Handle some SETUP packets ourselves */
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+
+		DEBUG_PD12_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue);
+		udc_set_address(dev, le16_to_cpu(ctrl.wValue));
+		pd12_fifo_write(ep,inbuf,0);
+		return;
+
+	case USB_REQ_GET_STATUS:{
+			if (pd12_handle_get_status(dev, &ctrl) == 0)
+				return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+			if (ctrl.bRequestType == USB_RECIP_ENDPOINT) {
+				struct pd12_ep *qep;
+				int ep_num = (ctrl.wIndex & 0x0f);
+
+				/* Support only HALT feature */
+				if (ctrl.wValue != 0 || ctrl.wLength != 0
+				    || ep_num > 4 || ep_num < 1)
+					break;
+
+				qep = &dev->ep[ep_num];
+				if (ctrl.bRequest == USB_REQ_SET_FEATURE) {
+					DEBUG_PD12_SETUP("SET_FEATURE (%d)\n",
+						    ep_num);
+					pd12_set_halt(&qep->ep, 1);
+				} else {
+					DEBUG_PD12_SETUP("CLR_FEATURE (%d)\n",
+						    ep_num);
+					pd12_set_halt(&qep->ep, 0);
+				}
+				usb_set_index(0);
+
+				return;
+			}
+			break;
+		}
+
+	default:
+		break;
+	}
+
+	if (dev->driver) {
+		/* device-2-host (IN) or no data setup command, process immediately */
+		spin_unlock(&dev->lock);
+		i = dev->driver->setup(&dev->gadget, &ctrl);
+		spin_lock(&dev->lock);
+
+		if (i < 0) {
+			pd12_ep0_in(dev);
+			/* setup processing failed, force stall */
+			DEBUG_PD12_SETUP
+			    ("  --> ERROR: gadget setup FAILED (stalling), setup returned %d\n",
+			     i);
+		/*	usb_set_index(0); */
+			write_cmd(PD12_SET_STATUS);
+			write_data(0x1);
+			write_cmd(PD12_SET_STATUS + 0x1);
+			write_data(0x1);
+			/* ep->stopped = 1; */
+			dev->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+}
+
+/*
+ * handle ep0 in interrupt
+ */
+static void pd12_handle_ep0(struct pd12_udc *dev)
+{
+	struct pd12_ep *ep = &dev->ep[0];
+	u8 ep0in_sts,ep0out_sts/*,int_sts*/;
+	
+	/* Set index 0 */
+	write_cmd(0x0);
+	read_data(&ep0out_sts);
+	
+	write_cmd(0x1);
+	read_data(&ep0in_sts);
+	
+
+	/*
+	 * if STALL is set, clear STALL bit
+	 */
+	if (ep0out_sts & UDC_EP_STALL ) {
+/*		DEBUG_PD12_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, status); */
+		write_cmd(PD12_SET_STATUS);
+		write_data(0x0);
+		nuke(ep, -ECONNABORTED);
+		dev->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (ep0in_sts & UDC_EP_STALL ) {
+/*		DEBUG_PD12_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, status); */
+		write_cmd(PD12_SET_STATUS + 0x1);
+		write_data(0x0);
+		nuke(ep, -ECONNABORTED);
+		dev->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	switch (dev->ep0state) {
+		case WAIT_FOR_SETUP:
+			DEBUG_PD12_EP0("WAIT_FOR_SETUP\n");
+			pd12_ep0_setup(dev);
+			break;
+		case DATA_STATE_RECV:
+			DEBUG_PD12_EP0("DATA_STATE_RECV\n");
+			pd12_ep0_out(dev);
+			break;
+		case DATA_STATE_XMIT:
+			DEBUG_PD12_EP0("continue with DATA_STATE_XMIT\n");
+			pd12_ep0_in(dev);
+			return;
+		default:
+			/* Stall? */
+			DEBUG_PD12_EP0("Odd state!! state = %s\n",
+				  state_names[dev->ep0state]);
+			dev->ep0state = WAIT_FOR_SETUP;
+			/* nuke(ep, 0); */
+			/* usb_set(EP0_SEND_STALL, ep->csr1); */
+			break;
+	}
+
+}
+
+static void pd12_ep0_kick(struct pd12_udc *dev, struct pd12_ep *ep)
+{
+
+	if (ep_is_in(ep)) {
+		dev->ep0state = DATA_STATE_XMIT;
+		pd12_ep0_in(dev);
+	} else {
+		dev->ep0state = DATA_STATE_RECV;
+		pd12_ep0_out(dev);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * 	device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static int pd12_udc_get_frame(struct usb_gadget *_gadget)
+{
+	u16 frame1,frame2;
+	write_cmd(PD12_RD_CUR_FRAME_NUM);
+	read_data((u8 *)&frame1);/* Least significant 8 bits */
+	read_data((u8 *)&frame2);/* Most significant 3 bits */
+	DEBUG_PD12("%s, %p\n", __FUNCTION__, _gadget);
+	return ((frame2 & 0x07) << 8) | (frame1 & 0xff);
+}
+
+static int pd12_udc_wakeup(struct usb_gadget *_gadget)
+{
+	/* host may not have enabled remote wakeup */
+	/*if ((UDCCS0 & UDCCS0_DRWF) == 0)
+	   return -EHOSTUNREACH;
+	   udc_set_mask_UDCCR(UDCCR_RSM); */
+	return -ENOTSUPP;
+}
+
+static const struct usb_gadget_ops pd12_udc_ops = {
+	.get_frame = pd12_udc_get_frame,
+	.wakeup = pd12_udc_wakeup,
+	/* current versions must always be self-powered */
+};
+
+static void nop_release(struct device *dev)
+{
+	DEBUG_PD12("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+static struct pd12_udc usb_memory = {
+	.usb_address = 0,
+
+	.gadget = {
+		   .ops = &pd12_udc_ops,
+		   .ep0 = &usb_memory.ep[0].ep,
+		   .name = driver_name,
+		   .dev = {
+			   .bus_id = "gadget",
+			   .release = nop_release,
+			   },
+		   },
+
+	/* control endpoint */
+	.ep[0] = {
+		  .ep = {
+			 .name = ep0name,
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 16,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 0,
+		  .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+		  },
+
+	/* first group of endpoints */
+	.ep[1] = {
+		  .ep = {
+			 .name = "ep1out-bulk",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 16,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 1,
+		  .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+		  },
+
+	.ep[2] = {
+		  .ep = {
+			 .name = "ep2in-bulk",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 16,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 2 | USB_DIR_IN,
+		  .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+		  },
+	.ep[3] = {
+		  .ep = {
+			 .name = "ep3out-int",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 64,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 3,
+		  .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+		  },
+	.ep[4] = {
+		  .ep = {
+			 .name = "ep3in-int",
+			 .ops = &pd12_ep_ops,
+			 .maxpacket = 64,
+			 },
+		  .dev = &usb_memory,
+
+		  .bEndpointAddress = 4 | USB_DIR_IN,
+		  .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+		  },
+};
+
+/*
+ * 	probe - binds to the platform device
+ */
+static int __devinit pd12_udc_probe(struct device *_dev)
+{
+	struct pd12_udc *dev = &usb_memory;
+	int retval;
+
+	DEBUG_PD12("%s: %p\n", __FUNCTION__, _dev);
+	spin_lock_init(&dev->lock);
+	dev->dev = _dev;
+	
+	device_initialize(&dev->gadget.dev);
+	dev->gadget.dev.parent = _dev;
+
+	the_controller = dev;
+	dev_set_drvdata(_dev, dev);
+
+	udc_reinit(dev);
+
+	dev->gadget.speed = USB_SPEED_FULL;
+	/* irq setup after old hardware state is cleaned up */
+	retval =
+	    request_irq(IRQ_USBINTR, pd12_udc_irq, /*SA_INTERRUPT*/SA_SAMPLE_RANDOM, driver_name,
+			dev);
+	if (retval != 0) {
+		DEBUG_PD12(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
+		      IRQ_USBINTR, retval);
+		return -EBUSY;
+	}
+
+	create_proc_files();
+
+	return retval;
+}
+
+static int __devexit pd12_udc_remove(struct device *_dev)
+{
+	struct pd12_udc *dev = _dev->driver_data;
+
+	DEBUG_PD12("%s: %p\n", __FUNCTION__, dev);
+
+	udc_disable(dev);
+	remove_proc_files();
+	usb_gadget_unregister_driver(dev->driver);
+
+	free_irq(IRQ_USBINTR, dev);
+
+	dev_set_drvdata(_dev, 0);
+
+	the_controller = 0;
+
+	return 0;
+}
+static struct platform_device	pd12_pdev = {
+	.name	= (char *) driver_name,
+	.id		= -1,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct device_driver udc_driver = {
+	.name = (char *)driver_name,
+	.bus = &platform_bus_type,
+	.probe = pd12_udc_probe,
+	.remove = pd12_udc_remove
+	    /* FIXME power management support */
+	    /* .suspend = ... disable UDC */
+	    /* .resume = ... re-enable UDC */
+};
+
+static int __init udc_init(void)
+{
+	int retval;
+	
+	DEBUG_PD12("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION);
+	th_pd12_virt = ioremap((ulong)TH_PD12_ADDR,0x10);
+	if(!th_pd12_virt){
+		printk("%s: ioremap fail\n",__FUNCTION__);
+		return -EIO;
+	}
+	th_cpld_virt = ioremap((ulong)TH_CPLD_ADDR,0x10);
+	if(!th_cpld_virt){
+		printk("%s: ioremap fail\n",__FUNCTION__);
+		return -EIO;
+	}
+	retval = platform_device_register (&pd12_pdev);
+	if (retval < 0){
+		platform_device_unregister (&pd12_pdev);
+		return retval;
+	}
+	return driver_register(&udc_driver);
+}
+
+static void __exit udc_exit(void)
+{
+	if(th_pd12_virt){
+		iounmap((void*)th_pd12_virt);
+		th_pd12_virt = NULL;
+	}
+	if(th_cpld_virt){
+		iounmap((void*)th_cpld_virt);
+		th_cpld_virt = NULL;
+	}
+	driver_unregister(&udc_driver);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/pd12_udc.h b/drivers/usb/gadget/pd12_udc.h
new file mode 100644
index 0000000..3ffd07e
--- /dev/null
+++ b/drivers/usb/gadget/pd12_udc.h
@@ -0,0 +1,148 @@
+/*
+ * linux/drivers/usb/gadget/pd12_udc.h
+ * Taihu pd12 full speed USB device controllers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __PD12_UDC_H_
+#define __PD12_UDC_H_
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/ibm4xx.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#define TH_PD12_ADDR  0x50000000
+#define TH_CPLD_ADDR  0x50100000
+
+#define IRQ_USBINTR 29
+#define USB_SUSPEND 0x20
+
+#define UDC_FIFO_EMPTY 0x0
+#define UDC_FIFO_FULL 0x01
+#define UDC_EP_STALL 0x02
+#define UDC_FIFO_UNWRITABLE (UDC_EP_STALL | UDC_FIFO_FULL)
+#define UDC_FIFO_UNREADABLE (UDC_FIFO_EMPTY | UDC_EP_STALL)
+
+/* pd12 udc command */
+#define PD12_SET_ADDR_EN		0xd0
+#define PD12_SET_EP_EN			0xd8
+#define PD12_SET_MODE 			0xf3
+#define PD12_SET_DMA 			0xfb
+#define PD12_READ_INT			0xf4
+#define PD12_READ_LAST_STATUS 	0x40
+#define PD12_SET_STATUS			0x40
+#define PD12_READ_EP_STATUS 	0x80
+#define PD12_READ_BUF			0xf0
+#define PD12_WRITE_BUF			0xf0
+#define PD12_SET_EP_STATUS		0x40
+#define PD12_ACK_SETUP			0xf1
+#define PD12_CLEAR_BUF			0xf2
+#define PD12_VALIDATE_BUF		0xfa
+#define PD12_SND_RESUME			0xf6
+#define PD12_RD_CUR_FRAME_NUM	0xf5
+
+
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define WAIT_FOR_OUT_STATUS     2
+#define DATA_STATE_RECV         3
+
+#define PD12_SUSPEND_CHG 		0x80
+#define PD12_BUS_RST 			0x40
+#define PD12_MAIN_IN 			0x20
+#define PD12_MAIN_OUT 			0x10
+#define PD12_EP1_IN 			0x08
+#define PD12_EP1_OUT 			0x04
+#define PD12_CNTL_IN 			0x02
+#define PD12_CNTL_OUT 			0x01
+
+
+#define PD12_MAX_ENDPOINTS       6
+
+/* ********************************************************************************************* */
+/* IO
+ */
+
+struct pd12_ep {
+	struct usb_ep ep;
+	struct pd12_udc *dev;
+
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	unsigned long pio_irqs;
+	unsigned long dma_irqs;
+	short		  dma;
+
+	u8 stopped;
+	u8 bEndpointAddress;
+	u8 bmAttributes;
+
+};
+
+struct pd12_request {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+struct pd12_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	spinlock_t lock;
+
+	int ep0state;
+	struct pd12_ep ep[PD12_MAX_ENDPOINTS];
+
+	unsigned char usb_address;
+
+	unsigned req_pending:1, req_std:1, req_config:1;
+};
+
+static struct pd12_udc *the_controller;
+
+#define ep_is_in(EP) 		(((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN)
+#define ep_index(EP) 		((EP)->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) 	((EP)->ep.maxpacket)
+
+#endif
diff --git a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h
index b67db19..da0de26 100644
--- a/include/asm-ppc/ibm4xx.h
+++ b/include/asm-ppc/ibm4xx.h
@@ -47,6 +47,10 @@
  #include <platforms/4xx/sycamore.h>
  #endif

+#if defined(CONFIG_TAIHU)
+#include <platforms/4xx/taihu.h>
+#endif
+
  #if defined(CONFIG_WALNUT)
  #include <platforms/4xx/walnut.h>
  #endif

                 reply	other threads:[~2006-05-11 18:25 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=44638197.1030207@softadvances.com \
    --to=jotken@softadvances.com \
    --cc=linuxppc-embedded@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.