[PATCH 1/2] Support AMCC Taihu 405EP Eval Board
John Otken
jotken at softadvances.com
Fri May 12 04:25:27 EST 2006
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
More information about the Linuxppc-embedded
mailing list