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 at 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 at 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 at 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 via email to