Merge git://www.denx.de/git/u-boot into 2007_05_15-testing
authorMarkus Klotzbuecher <mk@denx.de>
Tue, 29 May 2007 14:37:57 +0000 (16:37 +0200)
committerMarkus Klotzbuecher <mk@pollux.denx.de>
Tue, 29 May 2007 14:37:57 +0000 (16:37 +0200)
39 files changed:
CHANGELOG-before-U-Boot-1.1.5
Makefile
README
board/delta/delta.c
board/trab/auto_update.c
cpu/arm920t/at91rm9200/Makefile
cpu/arm920t/at91rm9200/usb.c [new file with mode: 0644]
cpu/arm920t/s3c24x0/Makefile
cpu/arm920t/s3c24x0/usb.c [new file with mode: 0644]
cpu/mpc5xxx/Makefile
cpu/ppc4xx/Makefile
cpu/pxa/Makefile
cpu/pxa/usb.c [new file with mode: 0644]
doc/README.generic_usb_ohci [new file with mode: 0644]
drivers/Makefile
drivers/isp116x-hcd.c [new file with mode: 0644]
drivers/isp116x.h [new file with mode: 0644]
drivers/usb_ohci.c [moved from cpu/arm920t/at91rm9200/usb_ohci.c with 87% similarity]
drivers/usb_ohci.h [moved from cpu/arm920t/at91rm9200/usb_ohci.h with 94% similarity]
drivers/usbdcore_ep0.c
drivers/usbdcore_mpc8xx.c [new file with mode: 0644]
drivers/usbdcore_omap1510.c
drivers/usbtty.c
drivers/usbtty.h
include/asm-arm/arch-pxa/pxa-regs.h
include/configs/AdderUSB.h [new file with mode: 0644]
include/configs/IceCube.h
include/configs/TQM5200.h
include/configs/delta.h
include/configs/mp2usb.h
include/configs/trab.h
include/configs/yosemite.h
include/da9030.h
include/usb.h
include/usb_cdc_acm.h [new file with mode: 0644]
include/usbdcore.h
include/usbdcore_mpc8xx.h [new file with mode: 0644]
include/usbdcore_omap1510.h
include/usbdescriptors.h

index 727a7b6..2404148 100644 (file)
@@ -438,6 +438,20 @@ Changes for U-Boot 1.1.5:
 
 * Call serial_initialize() before first debug() is used.
 
+* Code cleanup
+
+* Various USB related patches
+  - Add support for mpc8xx USB device.
+  - Add support for Common Device Class - Abstract Control Model USB console.
+  - Add support for flow control in USB slave devices.
+  - Add support for switching between gserial and cdc_acm using environment.
+  - Minor changes to usbdcore_omap1510.c usbdcore_omap1510.h
+  - Update usbcore slightly to ease host enumeration.
+  - Fix non-portable endian problems in usbdcore and usbdcore_ep0.
+  - Add AdderUSB_config as a defconfig to enable usage of the USB console
+    by default with the Adder87x U-Boot port.
+  Patch by Bryan O'Donoghue <bodonoghue@codehermit.ie>, 29 May 2006
+
 * Cleanup trab board for GCC-4.x
 
 * VoiceBlue update: use new MTD flash partitioning methods, use more
index caa5a0e..4417194 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ ifeq ($(HOSTARCH),$(ARCH))
 CROSS_COMPILE =
 else
 ifeq ($(ARCH),ppc)
-CROSS_COMPILE = powerpc-linux-
+CROSS_COMPILE = ppc_8xx-
 endif
 ifeq ($(ARCH),arm)
 CROSS_COMPILE = arm-linux-
@@ -644,6 +644,9 @@ AdderII_config  \
        @echo "#define CONFIG_MPC852T" > $(obj)include/config.h)
        @$(MKCONFIG) -a Adder ppc mpc8xx adder
 
+AdderUSB_config:       unconfig
+       @./mkconfig -a AdderUSB ppc mpc8xx adder
+
 ADS860_config     \
 FADS823_config    \
 FADS850SAR_config \
diff --git a/README b/README
index bb5b46e..f9c1978 100644 (file)
--- a/README
+++ b/README
@@ -249,6 +249,7 @@ The following options need to be configured:
                CONFIG_SA1110
                CONFIG_ARM7
                CONFIG_PXA250
+               CONFIG_PXA27X
                CONFIG_CPU_MONAHANS
 
                MicroBlaze based CPUs:
@@ -901,6 +902,68 @@ The following options need to be configured:
                                for differential drivers: 0x00001000
                                for single ended drivers: 0x00005000
 
+- USB Device:
+               Define the below if you wish to use the USB console.
+               Once firmware is rebuilt from a serial console issue the
+               command "setenv stdin usbtty; setenv stdout usbtty" and
+               attach your usb cable. The Unix command "dmesg" should print
+               it has found a new device. The environment variable usbtty
+               can be set to gserial or cdc_acm to enable your device to
+               appear to a USB host as a Linux gserial device or a
+               Common Device Class Abstract Control Model serial device.
+               If you select usbtty = gserial you should be able to enumerate
+               a Linux host by
+               # modprobe usbserial vendor=0xVendorID product=0xProductID
+               else if using cdc_acm, simply setting the environment
+               variable usbtty to be cdc_acm should suffice. The following
+               might be defined in YourBoardName.h
+
+                       CONFIG_USB_DEVICE
+                       Define this to build a UDC device
+
+                       CONFIG_USB_TTY
+                       Define this to have a tty type of device available to
+                       talk to the UDC device
+
+                       CFG_CONSOLE_IS_IN_ENV
+                       Define this if you want stdin, stdout &/or stderr to
+                       be set to usbtty.
+
+                       mpc8xx:
+                               CFG_USB_EXTC_CLK 0xBLAH
+                               Derive USB clock from external clock "blah"
+                               - CFG_USB_EXTC_CLK 0x02
+
+                               CFG_USB_BRG_CLK 0xBLAH
+                               Derive USB clock from brgclk
+                               - CFG_USB_BRG_CLK 0x04
+
+               If you have a USB-IF assigned VendorID then you may wish to
+               define your own vendor specific values either in BoardName.h
+               or directly in usbd_vendor_info.h. If you don't define
+               CONFIG_USBD_MANUFACTURER, CONFIG_USBD_PRODUCT_NAME,
+               CONFIG_USBD_VENDORID and CONFIG_USBD_PRODUCTID, then U-Boot
+               should pretend to be a Linux device to it's target host.
+
+                       CONFIG_USBD_MANUFACTURER
+                       Define this string as the name of your company for
+                       - CONFIG_USBD_MANUFACTURER "my company"
+
+                       CONFIG_USBD_PRODUCT_NAME
+                       Define this string as the name of your product
+                       - CONFIG_USBD_PRODUCT_NAME "acme usb device"
+
+                       CONFIG_USBD_VENDORID
+                       Define this as your assigned Vendor ID from the USB
+                       Implementors Forum. This *must* be a genuine Vendor ID
+                       to avoid polluting the USB namespace.
+                       - CONFIG_USBD_VENDORID 0xFFFF
+
+                       CONFIG_USBD_PRODUCTID
+                       Define this as the unique Product ID
+                       for your device
+                       - CONFIG_USBD_PRODUCTID 0xFFFF
+
 
 - MMC Support:
                The MMC controller on the Intel PXA is supported. To
index b127ac8..6e22774 100644 (file)
@@ -1,10 +1,6 @@
 /*
- * (C) Copyright 2002
- * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
- *
- * (C) Copyright 2002
- * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- * Marius Groeger <mgroeger@sysgo.de>
+ * (C) Copyright 2006
+ * DENX Software Engineering
  *
  * See file CREDITS for list of people who contributed to this
  * project.
@@ -98,7 +94,6 @@ int board_late_init(void)
        return 0;
 }
 
-
 /*
  * Magic Key Handling, mainly copied from board/lwmon/lwmon.c
  */
@@ -324,6 +319,12 @@ static void init_DA9030()
                return;
        }
 
+       val = 0x80;
+       if(i2c_write(addr, IRQ_MASK_B, 1, &val, 1)) {
+               printf("Error accessing DA9030 via i2c.\n");
+               return;
+       }
+
        i2c_reg_write(addr, REG_CONTROL_1_97, 0xfd); /* disable LDO1, enable LDO6 */
        i2c_reg_write(addr, LDO2_3, 0xd1);      /* LDO2 =1,9V, LDO3=3,1V */
        i2c_reg_write(addr, LDO4_5, 0xcc);      /* LDO2 =1,9V, LDO3=3,1V */
index f4074ae..6f903d2 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifdef CONFIG_AUTO_UPDATE
 
-#ifndef CONFIG_USB_OHCI
+#ifndef CONFIG_USB_OHCI_NEW
 #error "must define CONFIG_USB_OHCI"
 #endif
 
index 8d4e478..eaabad2 100644 (file)
@@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk
 LIB    = $(obj)lib$(SOC).a
 
 COBJS  = bcm5221.o dm9161.o ether.o i2c.o interrupts.o \
-         lxt972.o serial.o usb_ohci.o
+         lxt972.o serial.o usb.o
 SOBJS  = lowlevel_init.o
 
 SRCS   := $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/cpu/arm920t/at91rm9200/usb.c b/cpu/arm920t/at91rm9200/usb.c
new file mode 100644 (file)
index 0000000..366262e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 2006
+ * DENX Software Engineering <mk@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+
+#if defined(CONFIG_USB_OHCI_NEW) && defined(CFG_USB_OHCI_CPU_INIT)
+# ifdef CONFIG_AT91RM9200
+
+#include <asm/arch/hardware.h>
+
+int usb_cpu_init()
+{
+       /* Enable USB host clock. */
+       *AT91C_PMC_SCER = AT91C_PMC_UHP;        /* 48MHz clock enabled for UHP */
+       *AT91C_PMC_PCER = 1 << AT91C_ID_UHP;    /* Peripheral Clock Enable Register */
+       return 0;
+}
+
+int usb_cpu_stop()
+{
+       /* Initialization failed */
+       *AT91C_PMC_PCDR = 1 << AT91C_ID_UHP;    /* Peripheral Clock Disable Register */
+       *AT91C_PMC_SCDR = AT91C_PMC_UHP;        /* 48MHz clock disabled for UHP */
+       return 0;
+}
+
+int usb_cpu_init_fail()
+{
+       usb_cpu_stop();
+}
+
+# endif /* CONFIG_AT91RM9200 */
+#endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */
index 3a7c4b3..0ff36c5 100644 (file)
@@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk
 LIB    = $(obj)lib$(SOC).a
 
 COBJS  = i2c.o interrupts.o serial.o speed.o \
-         usb_ohci.o
+         usb.o
 
 SRCS   := $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS   := $(addprefix $(obj),$(SOBJS) $(COBJS))
diff --git a/cpu/arm920t/s3c24x0/usb.c b/cpu/arm920t/s3c24x0/usb.c
new file mode 100644 (file)
index 0000000..ef5d5bf
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 2006
+ * DENX Software Engineering <mk@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+
+#if defined(CONFIG_USB_OHCI_NEW) && defined(CFG_USB_OHCI_CPU_INIT)
+# if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
+
+#if defined(CONFIG_S3C2400)
+# include <s3c2400.h>
+#elif defined(CONFIG_S3C2410)
+# include <s3c2410.h>
+#endif
+
+int usb_cpu_init (void)
+{
+
+       S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
+       S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
+
+       /*
+        * Set the 48 MHz UPLL clocking. Values are taken from
+        * "PLL value selection guide", 6-23, s3c2400_UM.pdf.
+        */
+       clk_power->UPLLCON = ((40 << 12) + (1 << 4) + 2);
+       gpio->MISCCR |= 0x8; /* 1 = use pads related USB for USB host */
+
+       /*
+        * Enable USB host clock.
+        */
+       clk_power->CLKCON |= (1 << 4);
+
+       return 0;
+}
+
+int usb_cpu_stop (void)
+{
+       S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
+       /* may not want to do this */
+       clk_power->CLKCON &= ~(1 << 4);
+       return 0;
+}
+
+int usb_cpu_init_fail (void)
+{
+       S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
+       clk_power->CLKCON &= ~(1 << 4);
+       return 0;
+}
+
+# endif /* defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) */
+#endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */
index 235adb7..312b0bf 100644 (file)
@@ -28,7 +28,7 @@ LIB   = $(obj)lib$(CPU).a
 START  = start.o
 SOBJS  = io.o firmware_sc_task_bestcomm.impl.o firmware_sc_task.impl.o
 COBJS  = i2c.o traps.o cpu.o cpu_init.o fec.o ide.o interrupts.o \
-         loadtask.o pci_mpc5200.o serial.o speed.o usb_ohci.o
+         loadtask.o pci_mpc5200.o serial.o speed.o usb_ohci.o usb.o
 
 SRCS   := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS   := $(addprefix $(obj),$(SOBJS) $(COBJS))
index 4068b53..66fd02c 100644 (file)
@@ -27,12 +27,12 @@ LIB = $(obj)lib$(CPU).a
 
 START  = start.o resetvec.o kgdb.o
 SOBJS  = dcr.o
-COBJS  = 405gp_pci.o 4xx_enet.o \
+COBJS  = 405gp_pci.o 440spe_pcie.o 4xx_enet.o \
          bedbug_405.o commproc.o \
          cpu.o cpu_init.o gpio.o i2c.o interrupts.o \
          miiphy.o ndfc.o sdram.o serial.o \
          40x_spd_sdram.o 44x_spd_ddr.o 44x_spd_ddr2.o speed.o \
-         tlb.o traps.o usb_ohci.o usbdev.o \
+         tlb.o traps.o usb_ohci.o usbdev.o usb.o \
          440spe_pcie.o
 
 SRCS   := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
index cded7ff..8b4367e 100644 (file)
@@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk
 LIB    = $(obj)lib$(CPU).a
 
 START  = start.o
-COBJS  = serial.o interrupts.o cpu.o i2c.o pxafb.o mmc.o
+COBJS  = serial.o interrupts.o cpu.o i2c.o pxafb.o mmc.o usb.o
 
 SRCS   := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS   := $(addprefix $(obj),$(SOBJS) $(COBJS))
diff --git a/cpu/pxa/usb.c b/cpu/pxa/usb.c
new file mode 100644 (file)
index 0000000..65f457f
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 2006
+ * Markus Klotzbuecher, DENX Software Engineering <mk@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+
+#if defined(CONFIG_USB_OHCI_NEW) && defined(CFG_USB_OHCI_CPU_INIT)
+# if defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_PXA27X)
+
+#include <asm/arch/pxa-regs.h>
+
+int usb_cpu_init()
+{
+#if defined(CONFIG_CPU_MONAHANS)
+       /* Enable USB host clock. */
+       CKENA |= (CKENA_2_USBHOST |  CKENA_20_UDC);
+       udelay(100);
+#endif
+#if defined(CONFIG_PXA27X)
+       /* Enable USB host clock. */
+       CKEN |= CKEN10_USBHOST;
+#endif
+
+#if defined(CONFIG_CPU_MONAHANS)
+       /* Configure Port 2 for Host (USB Client Registers) */
+       UP2OCR = 0x3000c;
+#endif
+
+       UHCHR |= UHCHR_FHR;
+       wait_ms(11);
+       UHCHR &= ~UHCHR_FHR;
+
+       UHCHR |= UHCHR_FSBIR;
+       while (UHCHR & UHCHR_FSBIR)
+               udelay(1);
+
+#if defined(CONFIG_CPU_MONAHANS)
+       UHCHR &= ~UHCHR_SSEP0;
+#endif
+#if defined(CONFIG_PXA27X)
+       UHCHR &= ~UHCHR_SSEP2;
+#endif
+       UHCHR &= ~UHCHR_SSEP1;
+       UHCHR &= ~UHCHR_SSE;
+
+       return 0;
+}
+
+int usb_cpu_stop()
+{
+       return 0;
+}
+
+int usb_cpu_init_fail()
+{
+       return 0;
+}
+
+# endif /* defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_PXA27X) */
+#endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */
diff --git a/doc/README.generic_usb_ohci b/doc/README.generic_usb_ohci
new file mode 100644 (file)
index 0000000..017c69a
--- /dev/null
@@ -0,0 +1,43 @@
+Notes on the the generic USB-OHCI driver
+========================================
+
+This driver (drivers/usb_ohci.[ch]) is the result of the merge of
+various existing OHCI drivers that were basically identical beside
+cpu/board dependant initalization. This initalization has been moved
+into cpu/board directories and are called via the hooks below.
+
+Configuration options
+----------------------
+
+CONFIG_USB_OHCI_NEW: enable the new OHCI driver
+
+CFG_USB_OHCI_BOARD_INIT: call the board dependant hooks:
+
+  - extern int usb_board_init(void);
+  - extern int usb_board_stop(void);
+  - extern int usb_cpu_init_fail(void);
+
+CFG_USB_OHCI_CPU_INIT: call the cpu dependant hooks:
+
+  - extern int usb_cpu_init(void);
+  - extern int usb_cpu_stop(void);
+  - extern int usb_cpu_init_fail(void);
+
+CFG_USB_OHCI_REGS_BASE: defines the base address of the OHCI registers
+
+CFG_USB_OHCI_SLOT_NAME: slot name
+
+CFG_USB_OHCI_MAX_ROOT_PORTS: maximal number of ports of the root hub.
+
+
+Endianness issues
+------------------
+
+The LITTLEENDIAN #define determines if the 'swap_16' and 'swap_32'
+macros do byte swapping or not. But some cpus OHCI-controllers such as
+ppc4xx and mpc5xxx operate in little endian mode, so some extra ifdefs
+were necessary to make this work.
+
+
+
+
index d68cba6..b8dc97b 100644 (file)
@@ -31,8 +31,8 @@ COBJS = 3c589.o 5701rls.o ali512x.o atmel_usart.o \
          bcm570x.o bcm570x_autoneg.o cfb_console.o cfi_flash.o \
          cs8900.o ct69000.o dataflash.o dc2114x.o dm9000x.o \
          e1000.o eepro100.o \
-         i8042.o inca-ip_sw.o keyboard.o \
-         lan91c96.o macb.o \
+         i8042.o inca-ip_sw.o isp116x-hcd.o keyboard.o \
+         lan91c96.o macb.o\
          natsemi.o ne2000.o netarm_eth.o netconsole.o \
          ns16550.o ns8382x.o ns87308.o ns7520_eth.o omap1510_i2c.o \
          omap24xx_i2c.o pci.o pci_auto.o pci_indirect.o \
@@ -47,7 +47,9 @@ COBJS = 3c589.o 5701rls.o ali512x.o atmel_usart.o \
          status_led.o sym53c8xx.o systemace.o ahci.o \
          ti_pci1410a.o tigon3.o tsec.o \
          tsi108_eth.o tsi108_i2c.o tsi108_pci.o \
-         usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbtty.o \
+         usb_ohci.o \
+         usbdcore.o usbdcore_ep0.o usbdcore_mpc8xx.o usbdcore_omap1510.o \
+         usbtty.o \
          videomodes.o w83c553f.o \
          ks8695eth.o \
          pxa_pcmcia.o mpc8xx_pcmcia.o tqm8xx_pcmcia.o  \
diff --git a/drivers/isp116x-hcd.c b/drivers/isp116x-hcd.c
new file mode 100644 (file)
index 0000000..d57b8ec
--- /dev/null
@@ -0,0 +1,1412 @@
+/*
+ * ISP116x HCD (Host Controller Driver) for u-boot.
+ *
+ * Copyright (C) 2006-2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2006-2007 Eurotech S.p.A. <info@eurotech.it>
+ *
+ * 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
+ *
+ *
+ * Derived in part from the SL811 HCD driver "u-boot/drivers/sl811_usb.c"
+ * (original copyright message follows):
+ *
+ *    (C) Copyright 2004
+ *    Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ *    This code is based on linux driver for sl811hs chip, source at
+ *    drivers/usb/host/sl811.c:
+ *
+ *    SL811 Host Controller Interface driver for USB.
+ *
+ *    Copyright (c) 2003/06, Courage Co., Ltd.
+ *
+ *    Based on:
+ *         1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
+ *           Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
+ *           Adam Richter, Gregory P. Smith;
+ *         2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
+ *         3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
+ *
+ *    [[GNU/GPL disclaimer]]
+ *
+ * and in part from AU1x00 OHCI HCD driver "u-boot/cpu/mips/au1x00_usb_ohci.c"
+ * (original copyright message follows):
+ *
+ *    URB OHCI HCD (Host Controller Driver) for USB on the AU1x00.
+ *
+ *    (C) Copyright 2003
+ *    Gary Jennejohn, DENX Software Engineering <gj@denx.de>
+ *
+ *    [[GNU/GPL disclaimer]]
+ *
+ *    Note: Part of this code has been derived from linux
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_USB_ISP116X_HCD
+#include <asm/io.h>
+#include <usb.h>
+#include <malloc.h>
+#include <linux/list.h>
+
+/*
+ * ISP116x chips require certain delays between accesses to its
+ * registers. The following timing options exist.
+ *
+ * 1. Configure your memory controller (the best)
+ * 2. Use ndelay (easiest, poorest). For that, enable the following macro.
+ *
+ * Value is in microseconds.
+ */
+#ifdef ISP116X_HCD_USE_UDELAY
+#define UDELAY         1
+#endif
+
+/*
+ * On some (slowly?) machines an extra delay after data packing into
+ * controller's FIFOs is required, * otherwise you may get the following
+ * error:
+ *
+ *   uboot> usb start
+ *   (Re)start USB...
+ *   USB:   scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT
+ *   isp116x: isp116x_submit_job: ****** FIFO not ready! ******
+ *
+ *         USB device not responding, giving up (status=4)
+ *         isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ *         isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ *         isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ *         3 USB Device(s) found
+ *                scanning bus for storage devices... 0 Storage Device(s) found
+ *
+ * Value is in milliseconds.
+ */
+#ifdef ISP116X_HCD_USE_EXTRA_DELAY
+#define EXTRA_DELAY    2
+#endif
+
+/*
+ * Enable the following defines if you wish enable debugging messages.
+ */
+#undef DEBUG                   /* enable debugging messages */
+#undef TRACE                   /* enable tracing code */
+#undef VERBOSE                 /* verbose debugging messages */
+
+#include "isp116x.h"
+
+#define DRIVER_VERSION "08 Jan 2007"
+static const char hcd_name[] = "isp116x-hcd";
+
+struct isp116x isp116x_dev;
+struct isp116x_platform_data isp116x_board;
+int got_rhsc = 0;              /* root hub status change */
+struct usb_device *devgone;    /* device which was disconnected */
+int rh_devnum = 0;             /* address of Root Hub endpoint */
+
+/* ------------------------------------------------------------------------- */
+
+#define ALIGN(x,a)     (((x)+(a)-1UL)&~((a)-1UL))
+#define min_t(type,x,y)        \
+       ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
+
+/* ------------------------------------------------------------------------- */
+
+static int isp116x_reset(struct isp116x *isp116x);
+
+/* --- Debugging functions ------------------------------------------------- */
+
+#define isp116x_show_reg(d, r) {                               \
+       if ((r) < 0x20) {                                       \
+               DBG("%-12s[%02x]: %08x", #r,                    \
+                       r, isp116x_read_reg32(d, r));           \
+       } else {                                                \
+               DBG("%-12s[%02x]:     %04x", #r,                \
+                       r, isp116x_read_reg16(d, r));           \
+       }                                                       \
+}
+
+#define isp116x_show_regs(d) {                                 \
+       isp116x_show_reg(d, HCREVISION);                        \
+       isp116x_show_reg(d, HCCONTROL);                         \
+       isp116x_show_reg(d, HCCMDSTAT);                         \
+       isp116x_show_reg(d, HCINTSTAT);                         \
+       isp116x_show_reg(d, HCINTENB);                          \
+       isp116x_show_reg(d, HCFMINTVL);                         \
+       isp116x_show_reg(d, HCFMREM);                           \
+       isp116x_show_reg(d, HCFMNUM);                           \
+       isp116x_show_reg(d, HCLSTHRESH);                        \
+       isp116x_show_reg(d, HCRHDESCA);                         \
+       isp116x_show_reg(d, HCRHDESCB);                         \
+       isp116x_show_reg(d, HCRHSTATUS);                        \
+       isp116x_show_reg(d, HCRHPORT1);                         \
+       isp116x_show_reg(d, HCRHPORT2);                         \
+       isp116x_show_reg(d, HCHWCFG);                           \
+       isp116x_show_reg(d, HCDMACFG);                          \
+       isp116x_show_reg(d, HCXFERCTR);                         \
+       isp116x_show_reg(d, HCuPINT);                           \
+       isp116x_show_reg(d, HCuPINTENB);                        \
+       isp116x_show_reg(d, HCCHIPID);                          \
+       isp116x_show_reg(d, HCSCRATCH);                         \
+       isp116x_show_reg(d, HCITLBUFLEN);                       \
+       isp116x_show_reg(d, HCATLBUFLEN);                       \
+       isp116x_show_reg(d, HCBUFSTAT);                         \
+       isp116x_show_reg(d, HCRDITL0LEN);                       \
+       isp116x_show_reg(d, HCRDITL1LEN);                       \
+}
+
+#if defined(TRACE)
+
+static int isp116x_get_current_frame_number(struct usb_device *usb_dev)
+{
+       struct isp116x *isp116x = &isp116x_dev;
+
+       return isp116x_read_reg32(isp116x, HCFMNUM);
+}
+
+static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+                    int len, char *str)
+{
+#if defined(VERBOSE)
+       int i;
+#endif
+
+       DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx",
+           str,
+           isp116x_get_current_frame_number(dev),
+           usb_pipedevice(pipe),
+           usb_pipeendpoint(pipe),
+           usb_pipeout(pipe) ? 'O' : 'I',
+           usb_pipetype(pipe) < 2 ?
+           (usb_pipeint(pipe) ?
+            "INTR" : "ISOC") :
+           (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status);
+#if defined(VERBOSE)
+       if (len > 0 && buffer) {
+               printf(__FILE__ ": data(%d):", len);
+               for (i = 0; i < 16 && i < len; i++)
+                       printf(" %02x", ((__u8 *) buffer)[i]);
+               printf("%s\n", i < len ? "..." : "");
+       }
+#endif
+}
+
+#define PTD_DIR_STR(ptd)  ({char __c;          \
+       switch(PTD_GET_DIR(ptd)){               \
+       case 0:  __c = 's'; break;              \
+       case 1:  __c = 'o'; break;              \
+       default: __c = 'i'; break;              \
+       }; __c;})
+
+/*
+  Dump PTD info. The code documents the format
+  perfectly, right :)
+*/
+static inline void dump_ptd(struct ptd *ptd)
+{
+#if defined(VERBOSE)
+       int k;
+#endif
+
+       DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x",
+           PTD_GET_CC(ptd),
+           PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd),
+           PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+           PTD_GET_TOGGLE(ptd),
+           PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd));
+#if defined(VERBOSE)
+       printf("isp116x: %s: PTD(byte): ", __FUNCTION__);
+       for (k = 0; k < sizeof(struct ptd); ++k)
+               printf("%02x ", ((u8 *) ptd)[k]);
+       printf("\n");
+#endif
+}
+
+static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type)
+{
+#if defined(VERBOSE)
+       int k;
+
+       if (type == 0 /* 0ut data */ ) {
+               printf("isp116x: %s: out data: ", __FUNCTION__);
+               for (k = 0; k < PTD_GET_LEN(ptd); ++k)
+                       printf("%02x ", ((u8 *) buf)[k]);
+               printf("\n");
+       }
+       if (type == 1 /* 1n data */ ) {
+               printf("isp116x: %s: in data: ", __FUNCTION__);
+               for (k = 0; k < PTD_GET_COUNT(ptd); ++k)
+                       printf("%02x ", ((u8 *) buf)[k]);
+               printf("\n");
+       }
+
+       if (PTD_GET_LAST(ptd))
+               DBG("--- last PTD ---");
+#endif
+}
+
+#else
+
+#define dump_msg(dev, pipe, buffer, len, str)                  do { } while (0)
+#define dump_pkt(dev, pipe, buffer, len, setup, str, small)    do {} while (0)
+
+#define dump_ptd(ptd)                  do {} while (0)
+#define dump_ptd_data(ptd, buf, type)  do {} while (0)
+
+#endif
+
+/* --- Virtual Root Hub ---------------------------------------------------- */
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] = {
+       0x12,                   /*  __u8  bLength; */
+       0x01,                   /*  __u8  bDescriptorType; Device */
+       0x10,                   /*  __u16 bcdUSB; v1.1 */
+       0x01,
+       0x09,                   /*  __u8  bDeviceClass; HUB_CLASSCODE */
+       0x00,                   /*  __u8  bDeviceSubClass; */
+       0x00,                   /*  __u8  bDeviceProtocol; */
+       0x08,                   /*  __u8  bMaxPacketSize0; 8 Bytes */
+       0x00,                   /*  __u16 idVendor; */
+       0x00,
+       0x00,                   /*  __u16 idProduct; */
+       0x00,
+       0x00,                   /*  __u16 bcdDevice; */
+       0x00,
+       0x00,                   /*  __u8  iManufacturer; */
+       0x01,                   /*  __u8  iProduct; */
+       0x00,                   /*  __u8  iSerialNumber; */
+       0x01                    /*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] = {
+       0x09,                   /*  __u8  bLength; */
+       0x02,                   /*  __u8  bDescriptorType; Configuration */
+       0x19,                   /*  __u16 wTotalLength; */
+       0x00,
+       0x01,                   /*  __u8  bNumInterfaces; */
+       0x01,                   /*  __u8  bConfigurationValue; */
+       0x00,                   /*  __u8  iConfiguration; */
+       0x40,                   /*  __u8  bmAttributes;
+                                  Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+       0x00,                   /*  __u8  MaxPower; */
+
+       /* interface */
+       0x09,                   /*  __u8  if_bLength; */
+       0x04,                   /*  __u8  if_bDescriptorType; Interface */
+       0x00,                   /*  __u8  if_bInterfaceNumber; */
+       0x00,                   /*  __u8  if_bAlternateSetting; */
+       0x01,                   /*  __u8  if_bNumEndpoints; */
+       0x09,                   /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+       0x00,                   /*  __u8  if_bInterfaceSubClass; */
+       0x00,                   /*  __u8  if_bInterfaceProtocol; */
+       0x00,                   /*  __u8  if_iInterface; */
+
+       /* endpoint */
+       0x07,                   /*  __u8  ep_bLength; */
+       0x05,                   /*  __u8  ep_bDescriptorType; Endpoint */
+       0x81,                   /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,                   /*  __u8  ep_bmAttributes; Interrupt */
+       0x00,                   /*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+       0x02,
+       0xff                    /*  __u8  ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] = {
+       0x04,                   /*  __u8  bLength; */
+       0x03,                   /*  __u8  bDescriptorType; String-descriptor */
+       0x09,                   /*  __u8  lang ID */
+       0x04,                   /*  __u8  lang ID */
+};
+
+static unsigned char root_hub_str_index1[] = {
+       0x22,                   /*  __u8  bLength; */
+       0x03,                   /*  __u8  bDescriptorType; String-descriptor */
+       'I',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'S',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'P',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       '1',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       '1',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       '6',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'x',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       ' ',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'R',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'o',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'o',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       't',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       ' ',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'H',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'u',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+       'b',                    /*  __u8  Unicode */
+       0,                      /*  __u8  Unicode */
+};
+
+/*
+ * Hub class-specific descriptor is constructed dynamically
+ */
+
+/* --- Virtual root hub management functions ------------------------------- */
+
+static int rh_check_port_status(struct isp116x *isp116x)
+{
+       u32 temp, ndp, i;
+       int res;
+
+       res = -1;
+       temp = isp116x_read_reg32(isp116x, HCRHSTATUS);
+       ndp = (temp & RH_A_NDP);
+       for (i = 0; i < ndp; i++) {
+               temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i);
+               /* check for a device disconnect */
+               if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
+                    (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) {
+                       res = i;
+                       break;
+               }
+       }
+       return res;
+}
+
+/* --- HC management functions --------------------------------------------- */
+
+/* Write len bytes to fifo, pad till 32-bit boundary
+ */
+static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+       u8 *dp = (u8 *) buf;
+       u16 *dp2 = (u16 *) buf;
+       u16 w;
+       int quot = len % 4;
+
+       if ((unsigned long)dp2 & 1) {
+               /* not aligned */
+               for (; len > 1; len -= 2) {
+                       w = *dp++;
+                       w |= *dp++ << 8;
+                       isp116x_raw_write_data16(isp116x, w);
+               }
+               if (len)
+                       isp116x_write_data16(isp116x, (u16) * dp);
+       } else {
+               /* aligned */
+               for (; len > 1; len -= 2)
+                       isp116x_raw_write_data16(isp116x, *dp2++);
+               if (len)
+                       isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
+       }
+       if (quot == 1 || quot == 2)
+               isp116x_raw_write_data16(isp116x, 0);
+}
+
+/* Read len bytes from fifo and then read till 32-bit boundary
+ */
+static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+       u8 *dp = (u8 *) buf;
+       u16 *dp2 = (u16 *) buf;
+       u16 w;
+       int quot = len % 4;
+
+       if ((unsigned long)dp2 & 1) {
+               /* not aligned */
+               for (; len > 1; len -= 2) {
+                       w = isp116x_raw_read_data16(isp116x);
+                       *dp++ = w & 0xff;
+                       *dp++ = (w >> 8) & 0xff;
+               }
+               if (len)
+                       *dp = 0xff & isp116x_read_data16(isp116x);
+       } else {
+               /* aligned */
+               for (; len > 1; len -= 2)
+                       *dp2++ = isp116x_raw_read_data16(isp116x);
+               if (len)
+                       *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
+       }
+       if (quot == 1 || quot == 2)
+               isp116x_raw_read_data16(isp116x);
+}
+
+/* Write PTD's and data for scheduled transfers into the fifo ram.
+ * Fifo must be empty and ready */
+static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev,
+                     unsigned long pipe, struct ptd *ptd, int n, void *data,
+                     int len)
+{
+       int buflen = n * sizeof(struct ptd) + len;
+       int i, done;
+
+       DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen);
+
+       isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+       isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+       isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
+
+       done = 0;
+       for (i = 0; i < n; i++) {
+               DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i]));
+
+               dump_ptd(&ptd[i]);
+               isp116x_write_data16(isp116x, ptd[i].count);
+               isp116x_write_data16(isp116x, ptd[i].mps);
+               isp116x_write_data16(isp116x, ptd[i].len);
+               isp116x_write_data16(isp116x, ptd[i].faddr);
+
+               dump_ptd_data(&ptd[i], (__u8 *) data + done, 0);
+               write_ptddata_to_fifo(isp116x,
+                                     (__u8 *) data + done,
+                                     PTD_GET_LEN(&ptd[i]));
+
+               done += PTD_GET_LEN(&ptd[i]);
+       }
+}
+
+/* Read the processed PTD's and data from fifo ram back to URBs' buffers.
+ * Fifo must be full and done */
+static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev,
+                      unsigned long pipe, struct ptd *ptd, int n, void *data,
+                      int len)
+{
+       int buflen = n * sizeof(struct ptd) + len;
+       int i, done, cc, ret;
+
+       isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+       isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+       isp116x_write_addr(isp116x, HCATLPORT);
+
+       ret = TD_CC_NOERROR;
+       done = 0;
+       for (i = 0; i < n; i++) {
+               DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i]));
+
+               ptd[i].count = isp116x_read_data16(isp116x);
+               ptd[i].mps = isp116x_read_data16(isp116x);
+               ptd[i].len = isp116x_read_data16(isp116x);
+               ptd[i].faddr = isp116x_read_data16(isp116x);
+               dump_ptd(&ptd[i]);
+
+               read_ptddata_from_fifo(isp116x,
+                                      (__u8 *) data + done,
+                                      PTD_GET_LEN(&ptd[i]));
+               dump_ptd_data(&ptd[i], (__u8 *) data + done, 1);
+
+               done += PTD_GET_LEN(&ptd[i]);
+
+               cc = PTD_GET_CC(&ptd[i]);
+               if (cc == TD_DATAUNDERRUN) {    /* underrun is no error... */
+                       DBG("allowed data underrun");
+                       cc = TD_CC_NOERROR;
+               }
+               if (cc != TD_CC_NOERROR && ret == TD_CC_NOERROR)
+                       ret = cc;
+       }
+
+       DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen);
+
+       return ret;
+}
+
+/* Interrupt handling
+ */
+static int isp116x_interrupt(struct isp116x *isp116x)
+{
+       u16 irqstat;
+       u32 intstat;
+       int ret = 0;
+
+       isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+       irqstat = isp116x_read_reg16(isp116x, HCuPINT);
+       isp116x_write_reg16(isp116x, HCuPINT, irqstat);
+       DBG(">>>>>> irqstat %x <<<<<<", irqstat);
+
+       if (irqstat & HCuPINT_ATL) {
+               DBG(">>>>>> HCuPINT_ATL <<<<<<");
+               ret = 1;
+       }
+
+       if (irqstat & HCuPINT_OPR) {
+               intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
+               isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
+               DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat);
+
+               if (intstat & HCINT_UE) {
+                       ERR("unrecoverable error, controller disabled");
+
+                       /* FIXME: be optimistic, hope that bug won't repeat
+                        * often. Make some non-interrupt context restart the
+                        * controller. Count and limit the retries though;
+                        * either hardware or software errors can go forever...
+                        */
+                       isp116x_reset(isp116x);
+                       ret = -1;
+                       return -1;
+               }
+
+               if (intstat & HCINT_RHSC) {
+                       got_rhsc = 1;
+                       ret = 1;
+                       /* When root hub or any of its ports is going
+                          to come out of suspend, it may take more
+                          than 10ms for status bits to stabilize. */
+                       wait_ms(20);
+               }
+
+               if (intstat & HCINT_SO) {
+                       ERR("schedule overrun");
+                       ret = -1;
+               }
+
+               irqstat &= ~HCuPINT_OPR;
+       }
+
+       return ret;
+}
+
+#define PTD_NUM                        64      /* it should be enougth... */
+struct ptd ptd[PTD_NUM];
+static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe)
+{
+       return min(PTD_NUM * usb_maxpacket(dev, pipe), PTD_NUM * 16);
+}
+
+/* Do an USB transfer
+ */
+static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe,
+                             int dir, void *buffer, int len)
+{
+       struct isp116x *isp116x = &isp116x_dev;
+       int type = usb_pipetype(pipe);
+       int epnum = usb_pipeendpoint(pipe);
+       int max = usb_maxpacket(dev, pipe);
+       int dir_out = usb_pipeout(pipe);
+       int speed_low = usb_pipeslow(pipe);
+       int i, done, stat, timeout, cc;
+       int retries = 10;
+
+       DBG("------------------------------------------------");
+       dump_msg(dev, pipe, buffer, len, "SUBMIT");
+       DBG("------------------------------------------------");
+
+       if (isp116x->disabled) {
+               ERR("EPIPE");
+               dev->status = USB_ST_CRC_ERR;
+               return -1;
+       }
+
+       /* device pulled? Shortcut the action. */
+       if (devgone == dev) {
+               ERR("ENODEV");
+               dev->status = USB_ST_CRC_ERR;
+               return USB_ST_CRC_ERR;
+       }
+
+       if (!max) {
+               ERR("pipesize for pipe %lx is zero", pipe);
+               dev->status = USB_ST_CRC_ERR;
+               return -1;
+       }
+
+       if (type == PIPE_ISOCHRONOUS) {
+               ERR("isochronous transfers not supported");
+               dev->status = USB_ST_CRC_ERR;
+               return -1;
+       }
+
+       /* FIFO not empty? */
+       if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) {
+               ERR("****** FIFO not empty! ******");
+               dev->status = USB_ST_BUF_ERR;
+               return -1;
+       }
+
+      retry:
+       isp116x_write_reg32(isp116x, HCINTSTAT, 0xff);
+
+       /* Prepare the PTD data */
+       done = 0;
+       i = 0;
+       do {
+               ptd[i].count = PTD_CC_MSK | PTD_ACTIVE_MSK |
+                   PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out));
+               ptd[i].mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum);
+               ptd[i].len = PTD_LEN(max > len - done ? len - done : max) |
+                   PTD_DIR(dir);
+               ptd[i].faddr = PTD_FA(usb_pipedevice(pipe));
+
+               usb_dotoggle(dev, epnum, dir_out);
+               done += PTD_GET_LEN(&ptd[i]);
+               i++;
+               if (i >= PTD_NUM) {
+                       ERR("****** Cannot pack buffer! ******");
+                       dev->status = USB_ST_BUF_ERR;
+                       return -1;
+               }
+       } while (done < len);
+       ptd[i - 1].mps |= PTD_LAST_MSK;
+
+       /* Pack data into FIFO ram */
+       pack_fifo(isp116x, dev, pipe, ptd, i, buffer, len);
+#ifdef EXTRA_DELAY
+       wait_ms(EXTRA_DELAY);
+#endif
+
+       /* Start the data transfer */
+
+       /* Allow more time for a BULK device to react - some are slow */
+       if (usb_pipetype(pipe) == PIPE_BULK)
+               timeout = 5000;
+       else
+               timeout = 100;
+
+       /* Wait for it to complete */
+       for (;;) {
+               /* Check whether the controller is done */
+               stat = isp116x_interrupt(isp116x);
+
+               if (stat < 0) {
+                       dev->status = USB_ST_CRC_ERR;
+                       break;
+               }
+               if (stat > 0)
+                       break;
+
+               /* Check the timeout */
+               if (--timeout)
+                       udelay(1);
+               else {
+                       ERR("CTL:TIMEOUT ");
+                       stat = USB_ST_CRC_ERR;
+                       break;
+               }
+       }
+
+       /* We got an Root Hub Status Change interrupt */
+       if (got_rhsc) {
+               isp116x_show_regs(isp116x);
+
+               got_rhsc = 0;
+
+               /* Abuse timeout */
+               timeout = rh_check_port_status(isp116x);
+               if (timeout >= 0) {
+                       /*
+                        * FIXME! NOTE! AAAARGH!
+                        * This is potentially dangerous because it assumes
+                        * that only one device is ever plugged in!
+                        */
+                       devgone = dev;
+               }
+       }
+
+       /* Ok, now we can read transfer status */
+
+       /* FIFO not ready? */
+       if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) {
+               ERR("****** FIFO not ready! ******");
+               dev->status = USB_ST_BUF_ERR;
+               return -1;
+       }
+
+       /* Unpack data from FIFO ram */
+       cc = unpack_fifo(isp116x, dev, pipe, ptd, i, buffer, len);
+
+       /* Mmm... sometime we get 0x0f as cc which is a non sense!
+        * Just retry the transfer...
+        */
+       if (cc == 0x0f && retries-- > 0) {
+               usb_dotoggle(dev, epnum, dir_out);
+               goto retry;
+       }
+
+       if (cc != TD_CC_NOERROR) {
+               DBG("****** completition code error %x ******", cc);
+               switch (cc) {
+               case TD_CC_BITSTUFFING:
+                       dev->status = USB_ST_BIT_ERR;
+                       break;
+               case TD_CC_STALL:
+                       dev->status = USB_ST_STALLED;
+                       break;
+               case TD_BUFFEROVERRUN:
+               case TD_BUFFERUNDERRUN:
+                       dev->status = USB_ST_BUF_ERR;
+                       break;
+               default:
+                       dev->status = USB_ST_CRC_ERR;
+               }
+               return -cc;
+       }
+
+       dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)");
+
+       dev->status = 0;
+       return done;
+}
+
+/* Adapted from au1x00_usb_ohci.c
+ */
+static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+                                void *buffer, int transfer_len,
+                                struct devrequest *cmd)
+{
+       struct isp116x *isp116x = &isp116x_dev;
+       u32 tmp = 0;
+
+       int leni = transfer_len;
+       int len = 0;
+       int stat = 0;
+       u32 datab[4];
+       u8 *data_buf = (u8 *) datab;
+       u16 bmRType_bReq;
+       u16 wValue;
+       u16 wIndex;
+       u16 wLength;
+
+       if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+               INFO("Root-Hub submit IRQ: NOT implemented");
+               return 0;
+       }
+
+       bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+       wValue = swap_16(cmd->value);
+       wIndex = swap_16(cmd->index);
+       wLength = swap_16(cmd->length);
+
+       DBG("--- HUB ----------------------------------------");
+       DBG("submit rh urb, req=%x val=%#x index=%#x len=%d",
+           bmRType_bReq, wValue, wIndex, wLength);
+       dump_msg(dev, pipe, buffer, transfer_len, "RH");
+       DBG("------------------------------------------------");
+
+       switch (bmRType_bReq) {
+       case RH_GET_STATUS:
+               DBG("RH_GET_STATUS");
+
+               *(__u16 *) data_buf = swap_16(1);
+               len = 2;
+               break;
+
+       case RH_GET_STATUS | RH_INTERFACE:
+               DBG("RH_GET_STATUS | RH_INTERFACE");
+
+               *(__u16 *) data_buf = swap_16(0);
+               len = 2;
+               break;
+
+       case RH_GET_STATUS | RH_ENDPOINT:
+               DBG("RH_GET_STATUS | RH_ENDPOINT");
+
+               *(__u16 *) data_buf = swap_16(0);
+               len = 2;
+               break;
+
+       case RH_GET_STATUS | RH_CLASS:
+               DBG("RH_GET_STATUS | RH_CLASS");
+
+               tmp = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+               *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE));
+               len = 4;
+               break;
+
+       case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+               DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS");
+
+               tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1);
+               *(__u32 *) data_buf = swap_32(tmp);
+               isp116x_show_regs(isp116x);
+               len = 4;
+               break;
+
+       case RH_CLEAR_FEATURE | RH_ENDPOINT:
+               DBG("RH_CLEAR_FEATURE | RH_ENDPOINT");
+
+               switch (wValue) {
+               case RH_ENDPOINT_STALL:
+                       DBG("C_HUB_ENDPOINT_STALL");
+                       len = 0;
+                       break;
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | RH_CLASS:
+               DBG("RH_CLEAR_FEATURE | RH_CLASS");
+
+               switch (wValue) {
+               case RH_C_HUB_LOCAL_POWER:
+                       DBG("C_HUB_LOCAL_POWER");
+                       len = 0;
+                       break;
+
+               case RH_C_HUB_OVER_CURRENT:
+                       DBG("C_HUB_OVER_CURRENT");
+                       isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);
+                       len = 0;
+                       break;
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+               DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS");
+
+               switch (wValue) {
+               case RH_PORT_ENABLE:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_CCS);
+                       len = 0;
+                       break;
+
+               case RH_PORT_SUSPEND:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_POCI);
+                       len = 0;
+                       break;
+
+               case RH_PORT_POWER:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_LSDA);
+                       len = 0;
+                       break;
+
+               case RH_C_PORT_CONNECTION:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_CSC);
+                       len = 0;
+                       break;
+
+               case RH_C_PORT_ENABLE:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PESC);
+                       len = 0;
+                       break;
+
+               case RH_C_PORT_SUSPEND:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PSSC);
+                       len = 0;
+                       break;
+
+               case RH_C_PORT_OVER_CURRENT:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_POCI);
+                       len = 0;
+                       break;
+
+               case RH_C_PORT_RESET:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PRSC);
+                       len = 0;
+                       break;
+
+               default:
+                       ERR("invalid wValue");
+                       stat = USB_ST_STALLED;
+               }
+
+               isp116x_show_regs(isp116x);
+
+               break;
+
+       case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+               DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS");
+
+               switch (wValue) {
+               case RH_PORT_SUSPEND:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PSS);
+                       len = 0;
+                       break;
+
+               case RH_PORT_RESET:
+                       /* Spin until any current reset finishes */
+                       while (1) {
+                               tmp =
+                                   isp116x_read_reg32(isp116x,
+                                                      HCRHPORT1 + wIndex - 1);
+                               if (!(tmp & RH_PS_PRS))
+                                       break;
+                               wait_ms(1);
+                       }
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PRS);
+                       wait_ms(10);
+
+                       len = 0;
+                       break;
+
+               case RH_PORT_POWER:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PPS);
+                       len = 0;
+                       break;
+
+               case RH_PORT_ENABLE:
+                       isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+                                           RH_PS_PES);
+                       len = 0;
+                       break;
+
+               default:
+                       ERR("invalid wValue");
+                       stat = USB_ST_STALLED;
+               }
+
+               isp116x_show_regs(isp116x);
+
+               break;
+
+       case RH_SET_ADDRESS:
+               DBG("RH_SET_ADDRESS");
+
+               rh_devnum = wValue;
+               len = 0;
+               break;
+
+       case RH_GET_DESCRIPTOR:
+               DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength);
+
+               switch (wValue) {
+               case (USB_DT_DEVICE << 8):      /* device descriptor */
+                       len = min_t(unsigned int,
+                                   leni, min_t(unsigned int,
+                                               sizeof(root_hub_dev_des),
+                                               wLength));
+                       data_buf = root_hub_dev_des;
+                       break;
+
+               case (USB_DT_CONFIG << 8):      /* configuration descriptor */
+                       len = min_t(unsigned int,
+                                   leni, min_t(unsigned int,
+                                               sizeof(root_hub_config_des),
+                                               wLength));
+                       data_buf = root_hub_config_des;
+                       break;
+
+               case ((USB_DT_STRING << 8) | 0x00):     /* string 0 descriptors */
+                       len = min_t(unsigned int,
+                                   leni, min_t(unsigned int,
+                                               sizeof(root_hub_str_index0),
+                                               wLength));
+                       data_buf = root_hub_str_index0;
+                       break;
+
+               case ((USB_DT_STRING << 8) | 0x01):     /* string 1 descriptors */
+                       len = min_t(unsigned int,
+                                   leni, min_t(unsigned int,
+                                               sizeof(root_hub_str_index1),
+                                               wLength));
+                       data_buf = root_hub_str_index1;
+                       break;
+
+               default:
+                       ERR("invalid wValue");
+                       stat = USB_ST_STALLED;
+               }
+
+               break;
+
+       case RH_GET_DESCRIPTOR | RH_CLASS:
+               DBG("RH_GET_DESCRIPTOR | RH_CLASS");
+
+               tmp = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+               data_buf[0] = 0x09;     /* min length; */
+               data_buf[1] = 0x29;
+               data_buf[2] = tmp & RH_A_NDP;
+               data_buf[3] = 0;
+               if (tmp & RH_A_PSM)     /* per-port power switching? */
+                       data_buf[3] |= 0x01;
+               if (tmp & RH_A_NOCP)    /* no overcurrent reporting? */
+                       data_buf[3] |= 0x10;
+               else if (tmp & RH_A_OCPM)       /* per-port overcurrent rep? */
+                       data_buf[3] |= 0x08;
+
+               /* Corresponds to data_buf[4-7] */
+               datab[1] = 0;
+               data_buf[5] = (tmp & RH_A_POTPGT) >> 24;
+
+               tmp = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+               data_buf[7] = tmp & RH_B_DR;
+               if (data_buf[2] < 7)
+                       data_buf[8] = 0xff;
+               else {
+                       data_buf[0] += 2;
+                       data_buf[8] = (tmp & RH_B_DR) >> 8;
+                       data_buf[10] = data_buf[9] = 0xff;
+               }
+
+               len = min_t(unsigned int, leni,
+                           min_t(unsigned int, data_buf[0], wLength));
+               break;
+
+       case RH_GET_CONFIGURATION:
+               DBG("RH_GET_CONFIGURATION");
+
+               *(__u8 *) data_buf = 0x01;
+               len = 1;
+               break;
+
+       case RH_SET_CONFIGURATION:
+               DBG("RH_SET_CONFIGURATION");
+
+               isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC);
+               len = 0;
+               break;
+
+       default:
+               ERR("*** *** *** unsupported root hub command *** *** ***");
+               stat = USB_ST_STALLED;
+       }
+
+       len = min_t(int, len, leni);
+       if (buffer != data_buf)
+               memcpy(buffer, data_buf, len);
+
+       dev->act_len = len;
+       dev->status = stat;
+       DBG("dev act_len %d, status %d", dev->act_len, dev->status);
+
+       dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)");
+
+       return stat;
+}
+
+/* --- Transfer functions -------------------------------------------------- */
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+                  int len, int interval)
+{
+       DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d",
+           dev, pipe, buffer, len, interval);
+
+       return -1;
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+                      int len, struct devrequest *setup)
+{
+       int devnum = usb_pipedevice(pipe);
+       int epnum = usb_pipeendpoint(pipe);
+       int max = max_transfer_len(dev, pipe);
+       int dir_in = usb_pipein(pipe);
+       int done, ret;
+
+       /* Control message is for the HUB? */
+       if (devnum == rh_devnum)
+               return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup);
+
+       /* Ok, no HUB message so send the message to the device */
+
+       /* Setup phase */
+       DBG("--- SETUP PHASE --------------------------------");
+       usb_settoggle(dev, epnum, 1, 0);
+       ret = isp116x_submit_job(dev, pipe,
+                                PTD_DIR_SETUP,
+                                setup, sizeof(struct devrequest));
+       if (ret < 0) {
+               DBG("control setup phase error (ret = %d", ret);
+               return -1;
+       }
+
+       /* Data phase */
+       DBG("--- DATA PHASE ---------------------------------");
+       done = 0;
+       usb_settoggle(dev, epnum, !dir_in, 1);
+       while (done < len) {
+               ret = isp116x_submit_job(dev, pipe,
+                                        dir_in ? PTD_DIR_IN : PTD_DIR_OUT,
+                                        (__u8 *) buffer + done,
+                                        max > len - done ? len - done : max);
+               if (ret < 0) {
+                       DBG("control data phase error (ret = %d)", ret);
+                       return -1;
+               }
+               done += ret;
+
+               if (dir_in && ret < max)        /* short packet */
+                       break;
+       }
+
+       /* Status phase */
+       DBG("--- STATUS PHASE -------------------------------");
+       usb_settoggle(dev, epnum, !dir_in, 1);
+       ret = isp116x_submit_job(dev, pipe,
+                                !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0);
+       if (ret < 0) {
+               DBG("control status phase error (ret = %d", ret);
+               return -1;
+       }
+
+       dev->act_len = done;
+
+       dump_msg(dev, pipe, buffer, len, "DEV(ret)");
+
+       return done;
+}
+
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+                   int len)
+{
+       int dir_out = usb_pipeout(pipe);
+       int max = max_transfer_len(dev, pipe);
+       int done, ret;
+
+       DBG("--- BULK ---------------------------------------");
+       DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d",
+           usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
+
+       done = 0;
+       while (done < len) {
+               ret = isp116x_submit_job(dev, pipe,
+                                        !dir_out ? PTD_DIR_IN : PTD_DIR_OUT,
+                                        (__u8 *) buffer + done,
+                                        max > len - done ? len - done : max);
+               if (ret < 0) {
+                       DBG("error on bulk message (ret = %d)", ret);
+                       return -1;
+               }
+
+               done += ret;
+
+               if (!dir_out && ret < max)      /* short packet */
+                       break;
+       }
+
+       dev->act_len = done;
+
+       return 0;
+}
+
+/* --- Basic functions ----------------------------------------------------- */
+
+static int isp116x_sw_reset(struct isp116x *isp116x)
+{
+       int retries = 15;
+       int ret = 0;
+
+       DBG("");
+
+       isp116x->disabled = 1;
+
+       isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);
+       isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR);
+       while (--retries) {
+               /* It usually resets within 1 ms */
+               wait_ms(1);
+               if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR))
+                       break;
+       }
+       if (!retries) {
+               ERR("software reset timeout");
+               ret = -1;
+       }
+       return ret;
+}
+
+static int isp116x_reset(struct isp116x *isp116x)
+{
+       unsigned long t;
+       u16 clkrdy = 0;
+       int ret, timeout = 15 /* ms */ ;
+
+       DBG("");
+
+       ret = isp116x_sw_reset(isp116x);
+       if (ret)
+               return ret;
+
+       for (t = 0; t < timeout; t++) {
+               clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;
+               if (clkrdy)
+                       break;
+               wait_ms(1);
+       }
+       if (!clkrdy) {
+               ERR("clock not ready after %dms", timeout);
+               /* After sw_reset the clock won't report to be ready, if
+                  H_WAKEUP pin is high. */
+               ERR("please make sure that the H_WAKEUP pin is pulled low!");
+               ret = -1;
+       }
+       return ret;
+}
+
+static void isp116x_stop(struct isp116x *isp116x)
+{
+       u32 val;
+
+       DBG("");
+
+       isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+       /* Switch off ports' power, some devices don't come up
+          after next 'start' without this */
+       val = isp116x_read_reg32(isp116x, HCRHDESCA);
+       val &= ~(RH_A_NPS | RH_A_PSM);
+       isp116x_write_reg32(isp116x, HCRHDESCA, val);
+       isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
+
+       isp116x_sw_reset(isp116x);
+}
+
+/*
+ *  Configure the chip. The chip must be successfully reset by now.
+ */
+static int isp116x_start(struct isp116x *isp116x)
+{
+       struct isp116x_platform_data *board = isp116x->board;
+       u32 val;
+
+       DBG("");
+
+       /* Clear interrupt status and disable all interrupt sources */
+       isp116x_write_reg16(isp116x, HCuPINT, 0xff);
+       isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+       isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
+       isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
+
+       /* Hardware configuration */
+       val = HCHWCFG_DBWIDTH(1);
+       if (board->sel15Kres)
+               val |= HCHWCFG_15KRSEL;
+       /* Remote wakeup won't work without working clock */
+       if (board->remote_wakeup_enable)
+               val |= HCHWCFG_CLKNOTSTOP;
+       if (board->oc_enable)
+               val |= HCHWCFG_ANALOG_OC;
+       isp116x_write_reg16(isp116x, HCHWCFG, val);
+
+       /* --- Root hub configuration */
+       val = (25 << 24) & RH_A_POTPGT;
+       /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to
+          be always set. Yet, instead, we request individual port
+          power switching. */
+       val |= RH_A_PSM;
+       /* Report overcurrent per port */
+       val |= RH_A_OCPM;
+       isp116x_write_reg32(isp116x, HCRHDESCA, val);
+       isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+       val = RH_B_PPCM;
+       isp116x_write_reg32(isp116x, HCRHDESCB, val);
+       isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+       val = 0;
+       if (board->remote_wakeup_enable)
+               val |= RH_HS_DRWE;
+       isp116x_write_reg32(isp116x, HCRHSTATUS, val);
+       isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+       isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf);
+
+       /* Go operational */
+       val = HCCONTROL_USB_OPER;
+       if (board->remote_wakeup_enable)
+               val |= HCCONTROL_RWE;
+       isp116x_write_reg32(isp116x, HCCONTROL, val);
+
+       /* Disable ports to avoid race in device enumeration */
+       isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
+       isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
+
+       isp116x_show_regs(isp116x);
+
+       isp116x->disabled = 0;
+
+       return 0;
+}
+
+/* --- Init functions ------------------------------------------------------ */
+
+int isp116x_check_id(struct isp116x *isp116x)
+{
+       int val;
+
+       val = isp116x_read_reg16(isp116x, HCCHIPID);
+       if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+               ERR("invalid chip ID %04x", val);
+               return -1;
+       }
+
+       return 0;
+}
+
+int usb_lowlevel_init(void)
+{
+       struct isp116x *isp116x = &isp116x_dev;
+
+       DBG("");
+
+       /* Init device registers addr */
+       isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR;
+       isp116x->data_reg = (u16 *) ISP116X_HCD_DATA;
+
+       /* Setup specific board settings */
+#ifdef ISP116X_HCD_SEL15kRES
+       isp116x_board.sel15Kres = 1;
+#endif
+#ifdef ISP116X_HCD_OC_ENABLE
+       isp116x_board.oc_enable = 1;
+#endif
+#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE
+       isp116x_board.remote_wakeup_enable = 1;
+#endif
+       isp116x->board = &isp116x_board;
+
+       /* Try to get ISP116x silicon chip ID */
+       if (isp116x_check_id(isp116x) < 0)
+               return -1;
+
+       isp116x->disabled = 1;
+       isp116x->sleeping = 0;
+
+       isp116x_reset(isp116x);
+       isp116x_start(isp116x);
+
+       return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+       struct isp116x *isp116x = &isp116x_dev;
+
+       DBG("");
+
+       if (!isp116x->disabled)
+               isp116x_stop(isp116x);
+
+       return 0;
+}
+
+#endif                         /* CONFIG_USB_ISP116X_HCD */
diff --git a/drivers/isp116x.h b/drivers/isp116x.h
new file mode 100644 (file)
index 0000000..a3ce3b5
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * ISP116x register declarations and HCD data structures
+ *
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ * Portions:
+ * Copyright (C) 2004 Lothar Wassmann
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ *
+ * 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
+ */
+
+#ifdef DEBUG
+#define DBG(fmt, args...)      \
+               printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#else
+#define DBG(fmt, args...)      do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#    define VDBG               DBG
+#else
+#    define VDBG(fmt, args...) do {} while (0)
+#endif
+
+#define ERR(fmt, args...)      \
+               printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#define WARN(fmt, args...)     \
+               printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#define INFO(fmt, args...)     \
+               printf("isp116x: " fmt "\n" , ## args)
+
+/* ------------------------------------------------------------------------- */
+
+/* us of 1ms frame */
+#define  MAX_LOAD_LIMIT                850
+
+/* Full speed: max # of bytes to transfer for a single urb
+   at a time must be < 1024 && must be multiple of 64.
+   832 allows transfering 4kiB within 5 frames. */
+#define MAX_TRANSFER_SIZE_FULLSPEED    832
+
+/* Low speed: there is no reason to schedule in very big
+   chunks; often the requested long transfers are for
+   string descriptors containing short strings. */
+#define MAX_TRANSFER_SIZE_LOWSPEED     64
+
+/* Bytetime (us), a rough indication of how much time it
+   would take to transfer a byte of useful data over USB */
+#define BYTE_TIME_FULLSPEED    1
+#define BYTE_TIME_LOWSPEED     20
+
+/* Buffer sizes */
+#define ISP116x_BUF_SIZE       4096
+#define ISP116x_ITL_BUFSIZE    0
+#define ISP116x_ATL_BUFSIZE    ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
+
+#define ISP116x_WRITE_OFFSET   0x80
+
+/* --- ISP116x registers/bits ---------------------------------------------- */
+
+#define        HCREVISION      0x00
+#define        HCCONTROL       0x01
+#define                HCCONTROL_HCFS  (3 << 6)        /* host controller
+                                                  functional state */
+#define                HCCONTROL_USB_RESET     (0 << 6)
+#define                HCCONTROL_USB_RESUME    (1 << 6)
+#define                HCCONTROL_USB_OPER      (2 << 6)
+#define                HCCONTROL_USB_SUSPEND   (3 << 6)
+#define                HCCONTROL_RWC   (1 << 9)        /* remote wakeup connected */
+#define                HCCONTROL_RWE   (1 << 10)       /* remote wakeup enable */
+#define        HCCMDSTAT       0x02
+#define                HCCMDSTAT_HCR   (1 << 0)        /* host controller reset */
+#define                HCCMDSTAT_SOC   (3 << 16)       /* scheduling overrun count */
+#define        HCINTSTAT       0x03
+#define                HCINT_SO        (1 << 0)        /* scheduling overrun */
+#define                HCINT_WDH       (1 << 1)        /* writeback of done_head */
+#define                HCINT_SF        (1 << 2)        /* start frame */
+#define                HCINT_RD        (1 << 3)        /* resume detect */
+#define                HCINT_UE        (1 << 4)        /* unrecoverable error */
+#define                HCINT_FNO       (1 << 5)        /* frame number overflow */
+#define                HCINT_RHSC      (1 << 6)        /* root hub status change */
+#define                HCINT_OC        (1 << 30)       /* ownership change */
+#define                HCINT_MIE       (1 << 31)       /* master interrupt enable */
+#define        HCINTENB        0x04
+#define        HCINTDIS        0x05
+#define        HCFMINTVL       0x0d
+#define        HCFMREM         0x0e
+#define        HCFMNUM         0x0f
+#define        HCLSTHRESH      0x11
+#define        HCRHDESCA       0x12
+#define                RH_A_NDP        (0x3 << 0)      /* # downstream ports */
+#define                RH_A_PSM        (1 << 8)        /* power switching mode */
+#define                RH_A_NPS        (1 << 9)        /* no power switching */
+#define                RH_A_DT         (1 << 10)       /* device type (mbz) */
+#define                RH_A_OCPM       (1 << 11)       /* overcurrent protection
+                                                  mode */
+#define                RH_A_NOCP       (1 << 12)       /* no overcurrent protection */
+#define                RH_A_POTPGT     (0xff << 24)    /* power on -> power good
+                                                  time */
+#define        HCRHDESCB       0x13
+#define                RH_B_DR         (0xffff << 0)   /* device removable flags */
+#define                RH_B_PPCM       (0xffff << 16)  /* port power control mask */
+#define        HCRHSTATUS      0x14
+#define                RH_HS_LPS       (1 << 0)        /* local power status */
+#define                RH_HS_OCI       (1 << 1)        /* over current indicator */
+#define                RH_HS_DRWE      (1 << 15)       /* device remote wakeup
+                                                  enable */
+#define                RH_HS_LPSC      (1 << 16)       /* local power status change */
+#define                RH_HS_OCIC      (1 << 17)       /* over current indicator
+                                                  change */
+#define                RH_HS_CRWE      (1 << 31)       /* clear remote wakeup
+                                                  enable */
+#define        HCRHPORT1       0x15
+#define                RH_PS_CCS       (1 << 0)        /* current connect status */
+#define                RH_PS_PES       (1 << 1)        /* port enable status */
+#define                RH_PS_PSS       (1 << 2)        /* port suspend status */
+#define                RH_PS_POCI      (1 << 3)        /* port over current
+                                                  indicator */
+#define                RH_PS_PRS       (1 << 4)        /* port reset status */
+#define                RH_PS_PPS       (1 << 8)        /* port power status */
+#define                RH_PS_LSDA      (1 << 9)        /* low speed device attached */
+#define                RH_PS_CSC       (1 << 16)       /* connect status change */
+#define                RH_PS_PESC      (1 << 17)       /* port enable status change */
+#define                RH_PS_PSSC      (1 << 18)       /* port suspend status
+                                                  change */
+#define                RH_PS_OCIC      (1 << 19)       /* over current indicator
+                                                  change */
+#define                RH_PS_PRSC      (1 << 20)       /* port reset status change */
+#define                HCRHPORT_CLRMASK        (0x1f << 16)
+#define        HCRHPORT2       0x16
+#define        HCHWCFG         0x20
+#define                HCHWCFG_15KRSEL         (1 << 12)
+#define                HCHWCFG_CLKNOTSTOP      (1 << 11)
+#define                HCHWCFG_ANALOG_OC       (1 << 10)
+#define                HCHWCFG_DACK_MODE       (1 << 8)
+#define                HCHWCFG_EOT_POL         (1 << 7)
+#define                HCHWCFG_DACK_POL        (1 << 6)
+#define                HCHWCFG_DREQ_POL        (1 << 5)
+#define                HCHWCFG_DBWIDTH_MASK    (0x03 << 3)
+#define                HCHWCFG_DBWIDTH(n)      (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define                HCHWCFG_INT_POL         (1 << 2)
+#define                HCHWCFG_INT_TRIGGER     (1 << 1)
+#define                HCHWCFG_INT_ENABLE      (1 << 0)
+#define        HCDMACFG        0x21
+#define                HCDMACFG_BURST_LEN_MASK (0x03 << 5)
+#define                HCDMACFG_BURST_LEN(n)   (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define                HCDMACFG_BURST_LEN_1    HCDMACFG_BURST_LEN(0)
+#define                HCDMACFG_BURST_LEN_4    HCDMACFG_BURST_LEN(1)
+#define                HCDMACFG_BURST_LEN_8    HCDMACFG_BURST_LEN(2)
+#define                HCDMACFG_DMA_ENABLE     (1 << 4)
+#define                HCDMACFG_BUF_TYPE_MASK  (0x07 << 1)
+#define                HCDMACFG_CTR_SEL        (1 << 2)
+#define                HCDMACFG_ITLATL_SEL     (1 << 1)
+#define                HCDMACFG_DMA_RW_SELECT  (1 << 0)
+#define        HCXFERCTR       0x22
+#define        HCuPINT         0x24
+#define                HCuPINT_SOF             (1 << 0)
+#define                HCuPINT_ATL             (1 << 1)
+#define                HCuPINT_AIIEOT          (1 << 2)
+#define                HCuPINT_OPR             (1 << 4)
+#define                HCuPINT_SUSP            (1 << 5)
+#define                HCuPINT_CLKRDY          (1 << 6)
+#define        HCuPINTENB      0x25
+#define        HCCHIPID        0x27
+#define                HCCHIPID_MASK           0xff00
+#define                HCCHIPID_MAGIC          0x6100
+#define        HCSCRATCH       0x28
+#define        HCSWRES         0x29
+#define                HCSWRES_MAGIC           0x00f6
+#define        HCITLBUFLEN     0x2a
+#define        HCATLBUFLEN     0x2b
+#define        HCBUFSTAT       0x2c
+#define                HCBUFSTAT_ITL0_FULL     (1 << 0)
+#define                HCBUFSTAT_ITL1_FULL     (1 << 1)
+#define                HCBUFSTAT_ATL_FULL      (1 << 2)
+#define                HCBUFSTAT_ITL0_DONE     (1 << 3)
+#define                HCBUFSTAT_ITL1_DONE     (1 << 4)
+#define                HCBUFSTAT_ATL_DONE      (1 << 5)
+#define        HCRDITL0LEN     0x2d
+#define        HCRDITL1LEN     0x2e
+#define        HCITLPORT       0x40
+#define        HCATLPORT       0x41
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p)       (((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v)           (((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p)      (((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v)          (((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p)      (((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v)          (((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p)          (((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v)              (((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p)         (((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v)             (((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p)         (((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v)             (((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p)                (((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v)            (((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p)          (((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v)              (((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p)         (((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v)             (((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p)         (((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v)             (((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_B5_5(p)                (((p)->len & PTD_B5_5_MSK) >> 13)
+#define PTD_B5_5(v)            (((v) << 13) & PTD_B5_5_MSK)
+#define PTD_GET_FA(p)          (((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v)              (((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_FMT(p)         (((p)->faddr & PTD_FMT_MSK) >> 7)
+#define PTD_FMT(v)             (((v) << 7) & PTD_FMT_MSK)
+
+/*  Hardware transfer status codes -- CC from ptd->count */
+#define TD_CC_NOERROR      0x00
+#define TD_CC_CRC          0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL        0x04
+#define TD_DEVNOTRESP      0x05
+#define TD_PIDCHECKFAIL    0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN     0x08
+#define TD_DATAUNDERRUN    0x09
+    /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+    /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED     0x0F
+
+/* ------------------------------------------------------------------------- */
+
+#define        LOG2_PERIODIC_SIZE      5       /* arbitrary; this matches OHCI */
+#define        PERIODIC_SIZE           (1 << LOG2_PERIODIC_SIZE)
+
+/* Philips transfer descriptor */
+struct ptd {
+       u16 count;
+#define        PTD_COUNT_MSK   (0x3ff << 0)
+#define        PTD_TOGGLE_MSK  (1 << 10)
+#define        PTD_ACTIVE_MSK  (1 << 11)
+#define        PTD_CC_MSK      (0xf << 12)
+       u16 mps;
+#define        PTD_MPS_MSK     (0x3ff << 0)
+#define        PTD_SPD_MSK     (1 << 10)
+#define        PTD_LAST_MSK    (1 << 11)
+#define        PTD_EP_MSK      (0xf << 12)
+       u16 len;
+#define        PTD_LEN_MSK     (0x3ff << 0)
+#define        PTD_DIR_MSK     (3 << 10)
+#define        PTD_DIR_SETUP   (0)
+#define        PTD_DIR_OUT     (1)
+#define        PTD_DIR_IN      (2)
+#define        PTD_B5_5_MSK    (1 << 13)
+       u16 faddr;
+#define        PTD_FA_MSK      (0x7f << 0)
+#define        PTD_FMT_MSK     (1 << 7)
+} __attribute__ ((packed, aligned(2)));
+
+struct isp116x_ep {
+       struct usb_device *udev;
+       struct ptd ptd;
+
+       u8 maxpacket;
+       u8 epnum;
+       u8 nextpid;
+
+       u16 length;             /* of current packet */
+       unsigned char *data;    /* to databuf */
+
+       u16 error_count;
+};
+
+/* URB struct */
+#define N_URB_TD               48
+#define URB_DEL                        1
+typedef struct {
+       struct isp116x_ep *ed;
+       void *transfer_buffer;  /* (in) associated data buffer */
+       int actual_length;      /* (return) actual transfer length */
+       unsigned long pipe;     /* (in) pipe information */
+#if 0
+       int state;
+#endif
+} urb_priv_t;
+
+struct isp116x_platform_data {
+       /* Enable internal resistors on downstream ports */
+       unsigned sel15Kres:1;
+       /* On-chip overcurrent detection */
+       unsigned oc_enable:1;
+       /* Enable wakeup by devices on usb bus (e.g. wakeup
+          by attachment/detachment or by device activity
+          such as moving a mouse). When chosen, this option
+          prevents stopping internal clock, increasing
+          thereby power consumption in suspended state. */
+       unsigned remote_wakeup_enable:1;
+};
+
+struct isp116x {
+       u16 *addr_reg;
+       u16 *data_reg;
+
+       struct isp116x_platform_data *board;
+
+       struct dentry *dentry;
+       unsigned long stat1, stat2, stat4, stat8, stat16;
+
+       /* Status flags */
+       unsigned disabled:1;
+       unsigned sleeping:1;
+
+       /* Root hub registers */
+       u32 rhdesca;
+       u32 rhdescb;
+       u32 rhstatus;
+       u32 rhport[2];
+
+       /* Schedule for the current frame */
+       struct isp116x_ep *atl_active;
+       int atl_buflen;
+       int atl_bufshrt;
+       int atl_last_dir;
+       int atl_finishing;
+};
+
+/* ------------------------------------------------- */
+
+/* Inter-io delay (ns). The chip is picky about access timings; it
+ * expects at least:
+ * 150ns delay between consecutive accesses to DATA_REG,
+ * 300ns delay between access to ADDR_REG and DATA_REG
+ * OE, WE MUST NOT be changed during these intervals
+ */
+#if defined(UDELAY)
+#define        isp116x_delay(h,d)      udelay(d)
+#else
+#define        isp116x_delay(h,d)      do {} while (0)
+#endif
+
+static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
+{
+       writew(reg & 0xff, isp116x->addr_reg);
+       isp116x_delay(isp116x, UDELAY);
+}
+
+static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
+{
+       writew(val, isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+}
+
+static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
+{
+       __raw_writew(val, isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+}
+
+static inline u16 isp116x_read_data16(struct isp116x *isp116x)
+{
+       u16 val;
+
+       val = readw(isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+       return val;
+}
+
+static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
+{
+       u16 val;
+
+       val = __raw_readw(isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+       return val;
+}
+
+static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
+{
+       writew(val & 0xffff, isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+       writew(val >> 16, isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+}
+
+static inline u32 isp116x_read_data32(struct isp116x *isp116x)
+{
+       u32 val;
+
+       val = (u32) readw(isp116x->data_reg);
+       isp116x_delay(isp116x, UDELAY);
+       val |= ((u32) readw(isp116x->data_reg)) << 16;
+       isp116x_delay(isp116x, UDELAY);
+       return val;
+}
+
+/* Let's keep register access functions out of line. Hint:
+   we wait at least 150 ns at every access.
+*/
+static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
+{
+       isp116x_write_addr(isp116x, reg);
+       return isp116x_read_data16(isp116x);
+}
+
+static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
+{
+       isp116x_write_addr(isp116x, reg);
+       return isp116x_read_data32(isp116x);
+}
+
+static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
+                               unsigned val)
+{
+       isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+       isp116x_write_data16(isp116x, (u16) (val & 0xffff));
+}
+
+static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
+                               unsigned val)
+{
+       isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+       isp116x_write_data32(isp116x, (u32) val);
+}
+
+/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */
+
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS          0x0500
+#define RH_GET_DESCRIPTOR       0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION    0x0880
+#define RH_SET_CONFIGURATION    0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
similarity index 87%
rename from cpu/arm920t/at91rm9200/usb_ohci.c
rename to drivers/usb_ohci.c
index 5b2c56c..70cb6a3 100644 (file)
@@ -21,7 +21,7 @@
  *
  * 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
+ * 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
 #include <common.h>
 /* #include <pci.h> no PCI on the S3C24X0 */
 
-#ifdef CONFIG_USB_OHCI
-
-#include <asm/arch/hardware.h>
+#ifdef CONFIG_USB_OHCI_NEW
+
+/* mk: are these really required? */
+#if defined(CONFIG_S3C2400)
+# include <s3c2400.h>
+#elif defined(CONFIG_S3C2410)
+# include <s3c2410.h>
+#elif defined(CONFIG_ARM920T)
+# include <asm/arch/hardware.h>
+#elif defined(CONFIG_CPU_MONAHANS)
+# include <asm/arch/pxa-regs.h>
+#elif defined(CONFIG_MPC5200)
+# include <mpc5xxx.h>
+#endif
 
 #include <malloc.h>
 #include <usb.h>
 #include "usb_ohci.h"
 
-#define OHCI_USE_NPS           /* force NoPowerSwitching mode */
+#if defined(CONFIG_ARM920T) || \
+    defined(CONFIG_S3C2400) || \
+    defined(CONFIG_S3C2410) || \
+    defined(CONFIG_440EP) || \
+    defined(CONFIG_MPC5200)
+# define OHCI_USE_NPS          /* force NoPowerSwitching mode */
+#endif
+
 #undef OHCI_VERBOSE_DEBUG      /* not always helpful */
+#undef DEBUG
+#undef SHOW_INFO
+#undef OHCI_FILL_TRACE
 
 /* For initializing controller (mask in an HCFS mode too) */
 #define OHCI_CONTROL_INIT \
 #define info(format, arg...) do {} while(0)
 #endif
 
-#define m16_swap(x) swap_16(x)
-#define m32_swap(x) swap_32(x)
+#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200)
+# define m16_swap(x) (x)
+# define m32_swap(x) (x)
+#else
+# define m16_swap(x) swap_16(x)
+# define m32_swap(x) swap_32(x)
+#endif
 
 /* global ohci_t */
 static ohci_t gohci;
@@ -95,6 +121,10 @@ int got_rhsc;
 /* device which was disconnected */
 struct usb_device *devgone;
 
+/* flag guarding URB transation */
+int urb_finished = 0;
+
+
 /*-------------------------------------------------------------------------*/
 
 /* AMD-756 (D2 rev) reports corrupt register contents in some cases.
@@ -400,6 +430,16 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer,
                return -1;
        }
 
+       /* if we have an unfinished URB from previous transaction let's
+        * fail and scream as quickly as possible so as not to corrupt
+        * further communication */
+       if (!urb_finished) {
+               err("sohci_submit_job: URB NOT FINISHED");
+               return -1;
+       }
+       /* we're about to begin a new transaction here so mark the URB unfinished */
+       urb_finished = 0;
+
        /* every endpoint has a ed, locate and fill it */
        if (!(ed = ep_add_ed (dev, pipe))) {
                err("sohci_submit_job: ENOMEM");
@@ -488,7 +528,7 @@ static int ep_link (ohci_t *ohci, ed_t *edi)
                if (ohci->ed_controltail == NULL) {
                        writel (ed, &ohci->regs->ed_controlhead);
                } else {
-                       ohci->ed_controltail->hwNextED = m32_swap (ed);
+                       ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed);
                }
                ed->ed_prev = ohci->ed_controltail;
                if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
@@ -504,7 +544,7 @@ static int ep_link (ohci_t *ohci, ed_t *edi)
                if (ohci->ed_bulktail == NULL) {
                        writel (ed, &ohci->regs->ed_bulkhead);
                } else {
-                       ohci->ed_bulktail->hwNextED = m32_swap (ed);
+                       ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed);
                }
                ed->ed_prev = ohci->ed_bulktail;
                if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
@@ -525,8 +565,10 @@ static int ep_link (ohci_t *ohci, ed_t *edi)
  * the link from the ed still points to another operational ed or 0
  * so the HC can eventually finish the processing of the unlinked ed */
 
-static int ep_unlink (ohci_t *ohci, ed_t *ed)
+static int ep_unlink (ohci_t *ohci, ed_t *edi)
 {
+       volatile ed_t *ed = edi;
+
        ed->hwINFO |= m32_swap (OHCI_ED_SKIP);
 
        switch (ed->type) {
@@ -571,12 +613,14 @@ static int ep_unlink (ohci_t *ohci, ed_t *ed)
 
 /*-------------------------------------------------------------------------*/
 
-/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
- * but the USB stack is a little bit stateless so we do it at every transaction
- * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
- * in all other cases the state is left unchanged
- * the ed info fields are setted anyway even though most of them should not change */
-
+/* add/reinit an endpoint; this should be done once at the
+ * usb_set_configuration command, but the USB stack is a little bit
+ * stateless so we do it at every transaction if the state of the ed
+ * is ED_NEW then a dummy td is added and the state is changed to
+ * ED_UNLINK in all other cases the state is left unchanged the ed
+ * info fields are setted anyway even though most of them should not
+ * change
+ */
 static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe)
 {
        td_t *td;
@@ -596,7 +640,7 @@ static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe)
                ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */
                /* dummy td; end of td list for ed */
                td = td_alloc (usb_dev);
-               ed->hwTailP = m32_swap (td);
+               ed->hwTailP = m32_swap ((unsigned long)td);
                ed->hwHeadP = ed->hwTailP;
                ed->state = ED_UNLINK;
                ed->type = usb_pipetype (pipe);
@@ -654,13 +698,12 @@ static void td_fill (ohci_t *ohci, unsigned int info,
                data = 0;
 
        td->hwINFO = m32_swap (info);
-       td->hwCBP = m32_swap (data);
+       td->hwCBP = m32_swap ((unsigned long)data);
        if (data)
-               td->hwBE = m32_swap (data + len - 1);
+               td->hwBE = m32_swap ((unsigned long)(data + len - 1));
        else
                td->hwBE = 0;
-       td->hwNextTD = m32_swap (td_pt);
-       td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000);
+       td->hwNextTD = m32_swap ((unsigned long)td_pt);
 
        /* append to queue */
        td->ed->hwTailP = td->hwNextTD;
@@ -789,6 +832,9 @@ static td_t * dl_reverse_done_list (ohci_t *ohci)
                                } else
                                        td_list->ed->hwHeadP &= m32_swap (0xfffffff2);
                        }
+#ifdef CONFIG_MPC5200
+                       td_list->hwNextTD = 0;
+#endif
                }
 
                td_list->next_dl_td = td_rev;
@@ -828,6 +874,21 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list)
                        stat = cc_to_error[cc];
                }
 
+               /* see if this done list makes for all TD's of current URB,
+                * and mark the URB finished if so */
+               if (++(lurb_priv->td_cnt) == lurb_priv->length) {
+#if 1
+                       if ((ed->state & (ED_OPER | ED_UNLINK)) &&
+                           (lurb_priv->state != URB_DEL))
+#else
+                       if ((ed->state & (ED_OPER | ED_UNLINK)))
+                               urb_finished = 1;
+                       else
+                               dbg("dl_done_list: strange.., ED state %x, ed->state\n");
+               } else
+                       dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt,
+                               lurb_priv->length);
+#endif
                if (ed->state != ED_NEW) {
                        edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0;
                        edTailP = m32_swap (ed->hwTailP);
@@ -974,7 +1035,6 @@ int rh_check_port_status(ohci_t *controller)
 #ifdef CONFIG_AT91C_PQFP_UHPBUG
        ndp = (ndp == 2) ? 1:0;
 #endif
-
        for (i = 0; i < ndp; i++) {
                temp = roothub_portstatus (controller, i);
                /* check for a device disconnect */
@@ -1014,9 +1074,15 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
        }
 
        bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
+#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200)
+       wValue        = __swap_16(cmd->value);
+       wIndex        = __swap_16(cmd->index);
+       wLength       = __swap_16(cmd->length);
+#else
        wValue        = m16_swap (cmd->value);
        wIndex        = m16_swap (cmd->index);
        wLength       = m16_swap (cmd->length);
+#endif /* CONFIG_440EP || CONFIG_MPC5200 */
 
        info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x",
                dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
@@ -1030,6 +1096,20 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
           RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
        */
 
+#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200)
+       case RH_GET_STATUS:
+                       *(__u16 *) data_buf = __swap_16(1); OK (2);
+       case RH_GET_STATUS | RH_INTERFACE:
+                       *(__u16 *) data_buf = __swap_16(0); OK (2);
+       case RH_GET_STATUS | RH_ENDPOINT:
+                       *(__u16 *) data_buf = __swap_16(0); OK (2);
+       case RH_GET_STATUS | RH_CLASS:
+                       *(__u32 *) data_buf = __swap_32(
+                               RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+                       OK (4);
+       case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+                       *(__u32 *) data_buf = __swap_32(RD_RH_PORTSTAT); OK (4);
+#else
        case RH_GET_STATUS:
                        *(__u16 *) data_buf = m16_swap (1); OK (2);
        case RH_GET_STATUS | RH_INTERFACE:
@@ -1042,6 +1122,7 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
                        OK (4);
        case RH_GET_STATUS | RH_OTHER | RH_CLASS:
                        *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4);
+#endif /* CONFIG_440EP || CONFIG_MPC5200 */
 
        case RH_CLEAR_FEATURE | RH_ENDPOINT:
                switch (wValue) {
@@ -1088,7 +1169,9 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
                                            WR_RH_PORTSTAT (RH_PS_PRS);
                                        OK (0);
                        case (RH_PORT_POWER):
-                                       WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
+                                       WR_RH_PORTSTAT (RH_PS_PPS );
+                                       wait_ms(100);
+                                       OK (0);
                        case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
                                        if (RD_RH_PORTSTAT & RH_PS_CCS)
                                            WR_RH_PORTSTAT (RH_PS_PES );
@@ -1170,7 +1253,7 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
                }
 
                len = min_t(unsigned int, leni,
-               min_t(unsigned int, data_buf [0], wLength));
+                           min_t(unsigned int, data_buf [0], wLength));
                OK (len);
        }
 
@@ -1240,8 +1323,10 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                return -1;
        }
 
+#if 0
        wait_ms(10);
        /* ohci_dump_status(&gohci); */
+#endif
 
        /* allow more time for a BULK device to react - some are slow */
 #define BULK_TO         5000   /* timeout in milliseconds */
@@ -1258,39 +1343,32 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                        stat = USB_ST_CRC_ERR;
                        break;
                }
-               if (stat >= 0 && stat != 0xff) {
+
+               /* NOTE: since we are not interrupt driven in U-Boot and always
+                * handle only one URB at a time, we cannot assume the
+                * transaction finished on the first successful return from
+                * hc_interrupt().. unless the flag for current URB is set,
+                * meaning that all TD's to/from device got actually
+                * transferred and processed. If the current URB is not
+                * finished we need to re-iterate this loop so as
+                * hc_interrupt() gets called again as there needs to be some
+                * more TD's to process still */
+               if ((stat >= 0) && (stat != 0xff) && (urb_finished)) {
                        /* 0xff is returned for an SF-interrupt */
                        break;
                }
+
                if (--timeout) {
                        wait_ms(1);
                } else {
                        err("CTL:TIMEOUT ");
+                       dbg("submit_common_msg: TO status %x\n", stat);
+                       stat = USB_ST_CRC_ERR;
+                       urb_finished = 1;
                        stat = USB_ST_CRC_ERR;
                        break;
                }
        }
-       /* we got an Root Hub Status Change interrupt */
-       if (got_rhsc) {
-#ifdef DEBUG
-               ohci_dump_roothub (&gohci, 1);
-#endif
-               got_rhsc = 0;
-               /* abuse timeout */
-               timeout = rh_check_port_status(&gohci);
-               if (timeout >= 0) {
-#if 0 /* this does nothing useful, but leave it here in case that changes */
-                       /* the called routine adds 1 to the passed value */
-                       usb_hub_port_connect_change(gohci.rh.dev, timeout - 1);
-#endif
-                       /*
-                        * XXX
-                        * This is potentially dangerous because it assumes
-                        * that only one device is ever plugged in!
-                        */
-                       devgone = dev;
-               }
-       }
 
        dev->status = stat;
        dev->act_len = transfer_len;
@@ -1381,7 +1459,8 @@ static int hc_reset (ohci_t *ohci)
                readl(&ohci->regs->control));
 
        /* Reset USB (needed by some controllers) */
-       writel (0, &ohci->regs->control);
+       ohci->hc_control = 0;
+       writel (ohci->hc_control, &ohci->regs->control);
 
        /* HC Reset requires max 10 us delay */
        writel (OHCI_HCR,  &ohci->regs->cmdstatus);
@@ -1460,24 +1539,30 @@ static int hc_start (ohci_t * ohci)
 
 /* an interrupt happens */
 
-static int
-hc_interrupt (void)
+static int hc_interrupt (void)
 {
        ohci_t *ohci = &gohci;
        struct ohci_regs *regs = ohci->regs;
        int ints;
        int stat = -1;
 
-       if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) {
-               ints =  OHCI_INTR_WDH;
-       } else {
-               ints = readl (&regs->intrstatus);
+       if ((ohci->hcca->done_head != 0) &&
+           !(m32_swap (ohci->hcca->done_head) & 0x01)) {
+               ints =  OHCI_INTR_WDH;
+       } else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
+               ohci->disabled++;
+               err ("%s device removed!", ohci->slot_name);
+               return -1;
+       } else if ((ints &= readl (&regs->intrenable)) == 0) {
+               dbg("hc_interrupt: returning..\n");
+               return 0xff;
        }
 
        /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */
 
        if (ints & OHCI_INTR_RHSC) {
                got_rhsc = 1;
+               stat = 0xff;
        }
 
        if (ints & OHCI_INTR_UE) {
@@ -1549,12 +1634,18 @@ static char ohci_inited = 0;
 
 int usb_lowlevel_init(void)
 {
-       /*
-        * Enable USB host clock.
-        */
-       *AT91C_PMC_SCER = AT91C_PMC_UHP;        /* 48MHz clock enabled for UHP */
-       *AT91C_PMC_PCER = 1 << AT91C_ID_UHP;    /* Peripheral Clock Enable Register */
 
+#ifdef CFG_USB_OHCI_CPU_INIT
+       /* cpu dependant init */
+       if(usb_cpu_init())
+               return -1;
+#endif
+
+#ifdef CFG_USB_OHCI_BOARD_INIT
+       /*  board dependant init */
+       if(usb_board_init())
+               return -1;
+#endif
        memset (&gohci, 0, sizeof (ohci_t));
        memset (&urb_priv, 0, sizeof (urb_priv_t));
 
@@ -1582,29 +1673,42 @@ int usb_lowlevel_init(void)
        gohci.disabled = 1;
        gohci.sleeping = 0;
        gohci.irq = -1;
-       gohci.regs = (struct ohci_regs *)AT91_USB_HOST_BASE;
+       gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE;
 
        gohci.flags = 0;
-       gohci.slot_name = "at91rm9200";
+       gohci.slot_name = CFG_USB_OHCI_SLOT_NAME;
 
        if (hc_reset (&gohci) < 0) {
                hc_release_ohci (&gohci);
-               /* Initialization failed */
-               *AT91C_PMC_PCER = AT91C_ID_UHP;
-               *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP;   /* 48MHz clock disabled for UHP */
+               err ("can't reset usb-%s", gohci.slot_name);
+#ifdef CFG_USB_OHCI_BOARD_INIT
+               /* board dependant cleanup */
+               usb_board_init_fail();
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+               /* cpu dependant cleanup */
+               usb_cpu_init_fail();
+#endif
                return -1;
        }
 
        /* FIXME this is a second HC reset; why?? */
-/*     writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control);
-       wait_ms (10);*/
-
+       /* writel(gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control);
+          wait_ms(10); */
        if (hc_start (&gohci) < 0) {
                err ("can't start usb-%s", gohci.slot_name);
                hc_release_ohci (&gohci);
                /* Initialization failed */
-               *AT91C_PMC_PCER = AT91C_ID_UHP;
-               *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP;   /* 48MHz clock disabled for UHP */
+#ifdef CFG_USB_OHCI_BOARD_INIT
+               /* board dependant cleanup */
+               usb_board_stop();
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+               /* cpu dependant cleanup */
+               usb_cpu_stop();
+#endif
                return -1;
        }
 
@@ -1612,6 +1716,7 @@ int usb_lowlevel_init(void)
        ohci_dump (&gohci, 1);
 #else
        wait_ms(1);
+       urb_finished = 1;
 #endif
        ohci_inited = 1;
        return 0;
@@ -1626,10 +1731,19 @@ int usb_lowlevel_stop(void)
        /* TODO release any interrupts, etc. */
        /* call hc_release_ohci() here ? */
        hc_reset (&gohci);
-       /* may not want to do this */
-       *AT91C_PMC_PCER = 1 << AT91C_ID_UHP;
-       *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP;   /* 48MHz clock disabled for UHP */
+
+#ifdef CFG_USB_OHCI_BOARD_INIT
+       /* board dependant cleanup */
+       if(usb_board_stop())
+               return -1;
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+       /* cpu dependant cleanup */
+       if(usb_cpu_stop())
+               return -1;
+#endif
+
        return 0;
 }
-
-#endif /* CONFIG_USB_OHCI */
+#endif /* CONFIG_USB_OHCI_NEW */
similarity index 94%
rename from cpu/arm920t/at91rm9200/usb_ohci.h
rename to drivers/usb_ohci.h
index ecb4e93..95fbc44 100644 (file)
@@ -7,6 +7,15 @@
  * usb-ohci.h
  */
 
+/* functions for doing board or CPU specific setup/cleanup */
+extern int usb_board_init(void);
+extern int usb_board_stop(void);
+extern int usb_cpu_init_fail(void);
+
+extern int usb_cpu_init(void);
+extern int usb_cpu_stop(void);
+extern int usb_cpu_init_fail(void);
+
 
 static int cc_to_error[16] = {
 
@@ -104,7 +113,9 @@ struct td {
        __u32 hwNextTD;         /* Next TD Pointer */
        __u32 hwBE;             /* Memory Buffer End Pointer */
 
+/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */
        __u16 hwPSW[MAXPSW];
+/* #endif */
        __u8 unused;
        __u8 index;
        struct ed *ed;
@@ -128,8 +139,13 @@ typedef struct td td_t;
 #define NUM_INTS 32    /* part of the OHCI standard */
 struct ohci_hcca {
        __u32   int_table[NUM_INTS];    /* Interrupt ED table */
+#if defined(CONFIG_MPC5200)
+       __u16   pad1;                   /* set to 0 on each frame_no change */
+       __u16   frame_no;               /* current frame number */
+#else
        __u16   frame_no;               /* current frame number */
        __u16   pad1;                   /* set to 0 on each frame_no change */
+#endif
        __u32   done_head;              /* info returned for an interrupt */
        u8              reserved_for_hc[116];
 } __attribute((aligned(256)));
@@ -138,7 +154,9 @@ struct ohci_hcca {
 /*
  * Maximum number of root hub ports.
  */
-#define MAX_ROOT_PORTS 15      /* maximum OHCI root hub ports */
+#ifndef CFG_USB_OHCI_MAX_ROOT_PORTS
+# error "CFG_USB_OHCI_MAX_ROOT_PORTS undefined!"
+#endif
 
 /*
  * This is the structure of the OHCI controller's memory mapped I/O
@@ -172,7 +190,7 @@ struct ohci_regs {
                __u32   a;
                __u32   b;
                __u32   status;
-               __u32   portstatus[MAX_ROOT_PORTS];
+               __u32   portstatus[CFG_USB_OHCI_MAX_ROOT_PORTS];
        } roothub;
 } __attribute((aligned(32)));
 
index 260befe..1e44f32 100644 (file)
@@ -2,6 +2,9 @@
  * (C) Copyright 2003
  * Gerry Hamel, geh@ti.com, Texas Instruments
  *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, deckard@CodeHermit.ie
+ *
  * Based on
  * linux/drivers/usbd/ep0.c
  *
  * function driver. This may need to change.
  *
  * XXX
+ *
+ * As alluded to above, a simple callback cdc_recv_setup has been implemented
+ * in the usb_device data structure to facilicate passing
+ * Common Device Class packets to a function driver.
+ *
+ * XXX
  */
 
 #include <common.h>
 
-#if defined(CONFIG_OMAP1510) && defined(CONFIG_USB_DEVICE)
+#if defined(CONFIG_USB_DEVICE)
 #include "usbdcore.h"
 
 #if 0
@@ -69,7 +78,7 @@ static int ep0_get_status (struct usb_device_instance *device,
        char *cp;
 
        urb->actual_length = 2;
-       cp = urb->buffer;
+       cp = (char*)urb->buffer;
        cp[0] = cp[1] = 0;
 
        switch (requesttype) {
@@ -115,7 +124,7 @@ static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
  *
  * Copy configuration data to urb transfer buffer if there is room for it.
  */
-static void copy_config (struct urb *urb, void *data, int max_length,
+void copy_config (struct urb *urb, void *data, int max_length,
                         int max_buf)
 {
        int available;
@@ -128,10 +137,7 @@ static void copy_config (struct urb *urb, void *data, int max_length,
                dbg_ep0 (1, "data is NULL");
                return;
        }
-       if (!(length = *(unsigned char *) data)) {
-               dbg_ep0 (1, "length is zero");
-               return;
-       }
+       length = max_length;
 
        if (length > max_length) {
                dbg_ep0 (1, "length: %d >= max_length: %d", length,
@@ -192,7 +198,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
 
        /* setup tx urb */
        urb->actual_length = 0;
-       cp = urb->buffer;
+       cp = (char*)urb->buffer;
 
        dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
 
@@ -200,7 +206,6 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
        case USB_DESCRIPTOR_TYPE_DEVICE:
                {
                        struct usb_device_descriptor *device_descriptor;
-
                        if (!
                            (device_descriptor =
                             usbd_device_device_descriptor (device, port))) {
@@ -214,20 +219,16 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
                        /* correct the correct control endpoint 0 max packet size into the descriptor */
                        device_descriptor =
                                (struct usb_device_descriptor *) urb->buffer;
-                       device_descriptor->bMaxPacketSize0 =
-                               urb->device->bus->maxpacketsize;
 
                }
-               /*dbg_ep0(3, "copied device configuration, actual_length: %x", urb->actual_length); */
+               dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
                break;
 
        case USB_DESCRIPTOR_TYPE_CONFIGURATION:
                {
-                       int bNumInterface;
                        struct usb_configuration_descriptor
                                *configuration_descriptor;
                        struct usb_device_descriptor *device_descriptor;
-
                        if (!
                            (device_descriptor =
                             usbd_device_device_descriptor (device, port))) {
@@ -251,130 +252,35 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
                                         index);
                                return -1;
                        }
+                       dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
                        copy_config (urb, configuration_descriptor,
-                                    sizeof (struct
-                                            usb_configuration_descriptor),
-                                    max);
-
-
-                       /* iterate across interfaces for specified configuration */
-                       dbg_ep0 (0, "bNumInterfaces: %d",
-                                configuration_descriptor->bNumInterfaces);
-                       for (bNumInterface = 0;
-                            bNumInterface <
-                            configuration_descriptor->bNumInterfaces;
-                            bNumInterface++) {
 
-                               int bAlternateSetting;
-                               struct usb_interface_instance
-                                       *interface_instance;
-
-                               dbg_ep0 (3, "[%d] bNumInterfaces: %d",
-                                        bNumInterface,
-                                        configuration_descriptor->bNumInterfaces);
-
-                               if (! (interface_instance = usbd_device_interface_instance (device,
-                                                                    port, index, bNumInterface)))
-                               {
-                                       dbg_ep0 (3, "[%d] interface_instance NULL",
-                                                bNumInterface);
-                                       return -1;
-                               }
-                               /* iterate across interface alternates */
-                               for (bAlternateSetting = 0;
-                                    bAlternateSetting < interface_instance->alternates;
-                                    bAlternateSetting++) {
-                                       /*int class; */
-                                       int bNumEndpoint;
-                                       struct usb_interface_descriptor *interface_descriptor;
-
-                                       struct usb_alternate_instance *alternate_instance;
-
-                                       dbg_ep0 (3, "[%d:%d] alternates: %d",
-                                                bNumInterface,
-                                                bAlternateSetting,
-                                                interface_instance->alternates);
-
-                                       if (! (alternate_instance = usbd_device_alternate_instance (device, port, index, bNumInterface, bAlternateSetting))) {
-                                               dbg_ep0 (3, "[%d] alternate_instance NULL",
-                                                        bNumInterface);
-                                               return -1;
-                                       }
-                                       /* copy descriptor for this interface */
-                                       copy_config (urb, alternate_instance->interface_descriptor,
-                                                    sizeof (struct usb_interface_descriptor),
-                                                    max);
-
-                                       /*dbg_ep0(3, "[%d:%d] classes: %d endpoints: %d", bNumInterface, bAlternateSetting, */
-                                       /*        alternate_instance->classes, alternate_instance->endpoints); */
-
-                                       /* iterate across classes for this alternate interface */
-#if 0
-                                       for (class = 0;
-                                            class < alternate_instance->classes;
-                                            class++) {
-                                               struct usb_class_descriptor *class_descriptor;
-                                               /*dbg_ep0(3, "[%d:%d:%d] classes: %d", bNumInterface, bAlternateSetting, */
-                                               /*        class, alternate_instance->classes); */
-                                               if (!(class_descriptor = usbd_device_class_descriptor_index (device, port, index, bNumInterface, bAlternateSetting, class))) {
-                                                       dbg_ep0 (3, "[%d] class NULL",
-                                                                class);
-                                                       return -1;
-                                               }
-                                               /* copy descriptor for this class */
-                                               copy_config (urb, class_descriptor,
-                                                       sizeof (struct usb_class_descriptor),
-                                                       max);
-                                       }
-#endif
-
-                                       /* iterate across endpoints for this alternate interface */
-                                       interface_descriptor = alternate_instance->interface_descriptor;
-                                       for (bNumEndpoint = 0;
-                                            bNumEndpoint < alternate_instance->endpoints;
-                                            bNumEndpoint++) {
-                                               struct usb_endpoint_descriptor *endpoint_descriptor;
-                                               dbg_ep0 (3, "[%d:%d:%d] endpoint: %d",
-                                                        bNumInterface,
-                                                        bAlternateSetting,
-                                                        bNumEndpoint,
-                                                        interface_descriptor->
-                                                        bNumEndpoints);
-                                               if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, index, bNumInterface, bAlternateSetting, bNumEndpoint))) {
-                                                       dbg_ep0 (3, "[%d] endpoint NULL",
-                                                                bNumEndpoint);
-                                                       return -1;
-                                               }
-                                               /* copy descriptor for this endpoint */
-                                               copy_config (urb, endpoint_descriptor,
-                                                            sizeof (struct usb_endpoint_descriptor),
-                                                            max);
-                                       }
-                               }
-                       }
-                       dbg_ep0 (3, "lengths: %d %d",
-                                le16_to_cpu (configuration_descriptor->wTotalLength),
-                                urb->actual_length);
+                                       cpu_to_le16(configuration_descriptor->wTotalLength),
+                                    max);
                }
+
                break;
 
        case USB_DESCRIPTOR_TYPE_STRING:
                {
                        struct usb_string_descriptor *string_descriptor;
-
                        if (!(string_descriptor = usbd_get_string (index))) {
+                               serial_printf("Invalid string index %d\n", index);
                                return -1;
                        }
-                       /*dbg_ep0(3, "string_descriptor: %p", string_descriptor); */
+                       dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
                        copy_config (urb, string_descriptor, string_descriptor->bLength, max);
                }
                break;
        case USB_DESCRIPTOR_TYPE_INTERFACE:
+       serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
                return -1;
        case USB_DESCRIPTOR_TYPE_ENDPOINT:
+               serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
                return -1;
        case USB_DESCRIPTOR_TYPE_HID:
                {
+                       serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
                        return -1;      /* unsupported at this time */
 #if 0
                        int bNumInterface =
@@ -403,6 +309,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
                break;
        case USB_DESCRIPTOR_TYPE_REPORT:
                {
+                       serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
                        return -1;      /* unsupported at this time */
 #if 0
                        int bNumInterface =
@@ -434,12 +341,19 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
 #endif
                }
                break;
+       case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
+               {
+                       /* If a USB device supports both a full speed and low speed operation
+                        * we must send a Device_Qualifier descriptor here
+                        */
+                       return -1;
+               }
        default:
                return -1;
        }
 
 
-       dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d packet size: %2d",
+       dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
                 urb->buffer, urb->buffer_length, urb->actual_length,
                 device->bus->endpoint_array[0].tx_packetSize);
 /*
@@ -495,6 +409,12 @@ int ep0_recv_setup (struct urb *urb)
 
        /* handle USB Standard Request (c.f. USB Spec table 9-2) */
        if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
+               if(device->device_state <= STATE_CONFIGURED){
+                       /*      Attempt to handle a CDC specific request if we are
+                        *      in the configured state.
+                        */
+                       return device->cdc_recv_setup(request,urb);
+               }
                dbg_ep0 (1, "non standard request: %x",
                         request->bmRequestType & USB_REQ_TYPE_MASK);
                return -1;      /* Stall here */
@@ -567,6 +487,7 @@ int ep0_recv_setup (struct urb *urb)
                                                   le16_to_cpu (request->wValue) & 0xff);
 
                case USB_REQ_GET_CONFIGURATION:
+                       serial_printf("get config %d\n", device->configuration);
                        return ep0_get_one (device, urb,
                                            device->configuration);
 
@@ -642,7 +563,6 @@ int ep0_recv_setup (struct urb *urb)
                        /*dbg_ep0(2, "address: %d %d %d", */
                        /*        request->wValue, le16_to_cpu(request->wValue), device->address); */
 
-                       serial_printf ("DEVICE_ADDRESS_ASSIGNED.. event?\n");
                        return 0;
 
                case USB_REQ_SET_DESCRIPTOR:    /* XXX should we support this? */
@@ -653,9 +573,10 @@ int ep0_recv_setup (struct urb *urb)
                        /* c.f. 9.4.7 - the top half of wValue is reserved */
                        /* */
                        if ((device->configuration =
-                            le16_to_cpu (request->wValue) & 0x7f) != 0) {
+                               le16_to_cpu (request->wValue) & 0xFF80) != 0) {
                                /* c.f. 9.4.7 - zero is the default or addressed state, in our case this */
                                /* is the same is configuration zero */
+                               serial_printf("error setting dev->config to zero!\n");
                                device->configuration = 0;      /* TBR - ?????? */
                        }
                        /* reset interface and alternate settings */
diff --git a/drivers/usbdcore_mpc8xx.c b/drivers/usbdcore_mpc8xx.c
new file mode 100644 (file)
index 0000000..e87284b
--- /dev/null
@@ -0,0 +1,1400 @@
+/*
+ * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit
+ * bodonoghue@CodeHermit.ie
+ *
+ * References
+ * DasUBoot/drivers/usbdcore_omap1510.c, for design and implementation ideas.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Notes :
+ * 1.  #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX
+ *             packet to force the USB re-transmit protocol.
+ *
+ * 2.  #define __DEBUG_UDC__ to switch on debug tracing to serial console
+ *     be careful that tracing doesn't create Hiesen-bugs with respect to
+ *     response timeouts to control requests.
+ *
+ * 3.  This driver should be able to support any higher level driver that
+ *     that wants to do either of the two standard UDC implementations
+ *     Control-Bulk-Interrupt or  Bulk-IN/Bulk-Out standards. Hence
+ *     gserial and cdc_acm should work with this code.
+ *
+ * 4.  NAK events never actually get raised at all, the documentation
+ *     is just wrong !
+ *
+ * 5.  For some reason, cbd_datlen is *always* +2 the value it should be.
+ *     this means that having an RX cbd of 16 bytes is not possible, since
+ *     the same size is reported for 14 bytes received as 16 bytes received
+ *     until we can find out why this happens, RX cbds must be limited to 8
+ *     bytes. TODO: check errata for this behaviour.
+ *
+ * 6.  Right now this code doesn't support properly powering up with the USB
+ *     cable attached to the USB host my development board the Adder87x doesn't
+ *     have a pull-up fitted to allow this, so it is necessary to power the
+ *     board and *then* attached the USB cable to the host. However somebody
+ *     with a different design in their board may be able to keep the cable
+ *     constantly connected and simply enable/disable a pull-up  re
+ *     figure 31.1 in MPC885RM.pdf instead of having to power up the board and
+ *     then attach the cable !
+ *
+ */
+#include <common.h>
+#include <config.h>
+
+#if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE)
+#include <commproc.h>
+#include "usbdcore.h"
+#include "usbdcore_mpc8xx.h"
+#include "usbdcore_ep0.h"
+
+#define ERR(fmt, args...)\
+       serial_printf("ERROR : [%s] %s:%d: "fmt,\
+                               __FILE__,__FUNCTION__,__LINE__, ##args)
+#ifdef __DEBUG_UDC__
+#define DBG(fmt,args...)\
+               serial_printf("[%s] %s:%d: "fmt,\
+                               __FILE__,__FUNCTION__,__LINE__, ##args)
+#else
+#define DBG(fmt,args...)
+#endif
+
+/* Static Data */
+#ifdef __SIMULATE_ERROR__
+static char err_poison_test = 0;
+#endif
+static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS];
+static u32 address_base = STATE_NOT_READY;
+static mpc8xx_udc_state_t udc_state = 0;
+static struct usb_device_instance *udc_device = 0;
+static volatile usb_epb_t *endpoints[MAX_ENDPOINTS];
+static volatile cbd_t *tx_cbd[TX_RING_SIZE];
+static volatile cbd_t *rx_cbd[RX_RING_SIZE];
+static volatile immap_t *immr = 0;
+static volatile cpm8xx_t *cp = 0;
+static volatile usb_pram_t *usb_paramp = 0;
+static volatile usb_t *usbp = 0;
+static int rx_ct = 0;
+static int tx_ct = 0;
+
+/* Static Function Declarations */
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+                                           usb_device_state_t final);
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+                                             usb_device_state_t final);
+static void mpc8xx_udc_stall (unsigned int ep);
+static void mpc8xx_udc_flush_tx_fifo (int epid);
+static void mpc8xx_udc_flush_rx_fifo (void);
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+                               struct urb *tx_urb);
+static void mpc8xx_udc_dump_request (struct usb_device_request *request);
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+                                  volatile cpm8xx_t * cp);
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi);
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_cbd_init (void);
+static void mpc8xx_udc_endpoint_init (void);
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size);
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment);
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_set_nak (unsigned int ep);
+static short mpc8xx_udc_handle_txerr (void);
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid);
+
+/******************************************************************************
+                              Global Linkage
+ *****************************************************************************/
+
+/* udc_init
+ *
+ * Do initial bus gluing
+ */
+int udc_init (void)
+{
+       /* Init various pointers */
+       immr = (immap_t *) CFG_IMMR;
+       cp = (cpm8xx_t *) & (immr->im_cpm);
+       usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]);
+       usbp = (usb_t *) & (cp->cp_scc[0]);
+
+       memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS));
+
+       udc_device = 0;
+       udc_state = STATE_NOT_READY;
+
+       usbp->usmod = 0x00;
+       usbp->uscom = 0;
+
+       /* Set USB Frame #0, Respond at Address & Get a clock source  */
+       usbp->usaddr = 0x00;
+       mpc8xx_udc_clock_init (immr, cp);
+
+       /* PA15, PA14 as perhiperal USBRXD and USBOE */
+       immr->im_ioport.iop_padir &= ~0x0003;
+       immr->im_ioport.iop_papar |= 0x0003;
+
+       /* PC11/PC10 as peripheral USBRXP USBRXN */
+       immr->im_ioport.iop_pcso |= 0x0030;
+
+       /* PC7/PC6 as perhiperal USBTXP and USBTXN */
+       immr->im_ioport.iop_pcdir |= 0x0300;
+       immr->im_ioport.iop_pcpar |= 0x0300;
+
+       /* Set the base address */
+       address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE);
+
+       /* Initialise endpoints and circular buffers */
+       mpc8xx_udc_endpoint_init ();
+       mpc8xx_udc_cbd_init ();
+
+       /* Assign allocated Dual Port Endpoint descriptors */
+       usb_paramp->ep0ptr = (u32) endpoints[0];
+       usb_paramp->ep1ptr = (u32) endpoints[1];
+       usb_paramp->ep2ptr = (u32) endpoints[2];
+       usb_paramp->ep3ptr = (u32) endpoints[3];
+       usb_paramp->frame_n = 0;
+
+       DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n",
+            usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr,
+            usb_paramp->ep3ptr);
+
+       return 0;
+}
+
+/* udc_irq
+ *
+ * Poll for whatever events may have occured
+ */
+void udc_irq (void)
+{
+       int epid = 0;
+       volatile cbd_t *rx_cbdp = 0;
+       volatile cbd_t *rx_cbdp_base = 0;
+
+       if (udc_state != STATE_READY) {
+               return;
+       }
+
+       if (usbp->usber & USB_E_BSY) {
+               /* This shouldn't happen. If it does then it's a bug ! */
+               usbp->usber |= USB_E_BSY;
+               mpc8xx_udc_flush_rx_fifo ();
+       }
+
+       /* Scan all RX/Bidirectional Endpoints for RX data. */
+       for (epid = 0; epid < MAX_ENDPOINTS; epid++) {
+               if (!ep_ref[epid].prx) {
+                       continue;
+               }
+               rx_cbdp = rx_cbdp_base = ep_ref[epid].prx;
+
+               do {
+                       if (!(rx_cbdp->cbd_sc & RX_BD_E)) {
+
+                               if (rx_cbdp->cbd_sc & 0x1F) {
+                                       /* Corrupt data discard it.
+                                        * Controller has NAK'd this packet.
+                                        */
+                                       mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+                               } else {
+                                       if (!epid) {
+                                               mpc8xx_udc_ep0_rx (rx_cbdp);
+
+                                       } else {
+                                               /* Process data */
+                                               mpc8xx_udc_set_nak (epid);
+                                               mpc8xx_udc_epn_rx (epid, rx_cbdp);
+                                               mpc8xx_udc_clear_rxbd (rx_cbdp);
+                                       }
+                               }
+
+                               /* Advance RX CBD pointer */
+                               mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+                               ep_ref[epid].prx = rx_cbdp;
+                       } else {
+                               /* Advance RX CBD pointer */
+                               mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+                       }
+
+               } while (rx_cbdp != rx_cbdp_base);
+       }
+
+       /* Handle TX events as appropiate, the correct place to do this is
+        * in a tx routine. Perhaps TX on epn was pre-empted by ep0
+        */
+
+       if (usbp->usber & USB_E_TXB) {
+               usbp->usber |= USB_E_TXB;
+       }
+
+       if (usbp->usber & (USB_TX_ERRMASK)) {
+               mpc8xx_udc_handle_txerr ();
+       }
+
+       /* Switch to the default state, respond at the default address */
+       if (usbp->usber & USB_E_RESET) {
+               usbp->usber |= USB_E_RESET;
+               usbp->usaddr = 0x00;
+               udc_device->device_state = STATE_DEFAULT;
+       }
+
+       /* if(usbp->usber&USB_E_IDLE){
+          We could suspend here !
+          usbp->usber|=USB_E_IDLE;
+          DBG("idle state change\n");
+          }
+          if(usbp->usbs){
+          We could resume here when IDLE is deasserted !
+          Not worth doing, so long as we are self powered though.
+          }
+       */
+
+       return;
+}
+
+/* udc_endpoint_write
+ *
+ * Write some data to an endpoint
+ */
+int udc_endpoint_write (struct usb_endpoint_instance *epi)
+{
+       int ep = 0;
+       short epid = 1, unnak = 0, ret = 0;
+
+       if (udc_state != STATE_READY) {
+               ERR ("invalid udc_state != STATE_READY!\n");
+               return -1;
+       }
+
+       if (!udc_device || !epi) {
+               return -1;
+       }
+
+       if (udc_device->device_state != STATE_CONFIGURED) {
+               return -1;
+       }
+
+       ep = epi->endpoint_address & 0x03;
+       if (ep >= MAX_ENDPOINTS) {
+               return -1;
+       }
+
+       /* Set NAK for all RX endpoints during TX */
+       for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+
+               /* Don't set NAK on DATA IN/CONTROL endpoints */
+               if (ep_ref[epid].sc & USB_DIR_IN) {
+                       continue;
+               }
+
+               if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) {
+                       unnak |= 1 << epid;
+               }
+
+               mpc8xx_udc_set_nak (epid);
+       }
+
+       mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep],
+                           epi->tx_urb);
+       ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]);
+
+       /* Remove temporary NAK */
+       for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+               if (unnak & (1 << epid)) {
+                       udc_unset_nak (epid);
+               }
+       }
+
+       return ret;
+}
+
+/* mpc8xx_udc_assign_urb
+ *
+ * Associate a given urb to an endpoint TX or RX transmit/receive buffers
+ */
+static int mpc8xx_udc_assign_urb (int ep, char direction)
+{
+       struct usb_endpoint_instance *epi = 0;
+
+       if (ep >= MAX_ENDPOINTS) {
+               goto err;
+       }
+       epi = &udc_device->bus->endpoint_array[ep];
+       if (!epi) {
+               goto err;
+       }
+
+       if (!ep_ref[ep].urb) {
+               ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array);
+               if (!ep_ref[ep].urb) {
+                       goto err;
+               }
+       } else {
+               ep_ref[ep].urb->actual_length = 0;
+       }
+
+       switch (direction) {
+       case USB_DIR_IN:
+               epi->tx_urb = ep_ref[ep].urb;
+               break;
+       case USB_DIR_OUT:
+               epi->rcv_urb = ep_ref[ep].urb;
+               break;
+       default:
+               goto err;
+       }
+       return 0;
+
+      err:
+       udc_state = STATE_ERROR;
+       return -1;
+}
+
+/* udc_setup_ep
+ *
+ * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram
+ * Isochronous endpoints aren't yet supported!
+ */
+void udc_setup_ep (struct usb_device_instance *device, unsigned int ep,
+                  struct usb_endpoint_instance *epi)
+{
+       uchar direction = 0;
+       int ep_attrib = 0;
+
+       if (epi && (ep < MAX_ENDPOINTS)) {
+
+               if (ep == 0) {
+                       if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL
+                           || epi->tx_attributes !=
+                           USB_ENDPOINT_XFER_CONTROL) {
+
+                               /* ep0 must be a control endpoint */
+                               udc_state = STATE_ERROR;
+                               return;
+
+                       }
+                       if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+                               mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize,
+                                                      epi->rcv_packetSize);
+                       }
+                       usbp->usep[ep] = 0x0000;
+                       return;
+               }
+
+               if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK)
+                   == USB_DIR_IN) {
+
+                       direction = 1;
+                       ep_attrib = epi->tx_attributes;
+                       epi->rcv_packetSize = 0;
+                       ep_ref[ep].sc |= USB_DIR_IN;
+               } else {
+
+                       direction = 0;
+                       ep_attrib = epi->rcv_attributes;
+                       epi->tx_packetSize = 0;
+                       ep_ref[ep].sc &= ~USB_DIR_IN;
+               }
+
+               if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address
+                                          & USB_ENDPOINT_DIR_MASK)) {
+                       return;
+               }
+
+               switch (ep_attrib) {
+               case USB_ENDPOINT_XFER_CONTROL:
+                       if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+                               mpc8xx_udc_cbd_attach (ep,
+                                                      epi->tx_packetSize,
+                                                      epi->rcv_packetSize);
+                       }
+                       usbp->usep[ep] = ep << 12;
+                       epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb;
+
+                       break;
+               case USB_ENDPOINT_XFER_BULK:
+               case USB_ENDPOINT_XFER_INT:
+                       if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+                               if (direction) {
+                                       mpc8xx_udc_cbd_attach (ep,
+                                                              epi->tx_packetSize,
+                                                              0);
+                               } else {
+                                       mpc8xx_udc_cbd_attach (ep,
+                                                              0,
+                                                              epi->rcv_packetSize);
+                               }
+                       }
+                       usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8);
+
+                       break;
+               case USB_ENDPOINT_XFER_ISOC:
+               default:
+                       serial_printf ("Error endpoint attrib %d>3\n", ep_attrib);
+                       udc_state = STATE_ERROR;
+                       break;
+               }
+       }
+
+}
+
+/* udc_connect
+ *
+ * Move state, switch on the USB
+ */
+void udc_connect (void)
+{
+       /* Enable pull-up resistor on D+
+        * TODO: fit a pull-up resistor to drive SE0 for > 2.5us
+        */
+
+       if (udc_state != STATE_ERROR) {
+               udc_state = STATE_READY;
+               usbp->usmod |= USMOD_EN;
+       }
+}
+
+/* udc_disconnect
+ *
+ * Disconnect is not used but, is included for completeness
+ */
+void udc_disconnect (void)
+{
+       /* Disable pull-up resistor on D-
+        * TODO: fix a pullup resistor to control this
+        */
+
+       if (udc_state != STATE_ERROR) {
+               udc_state = STATE_NOT_READY;
+       }
+       usbp->usmod &= ~USMOD_EN;
+}
+
+/* udc_enable
+ *
+ * Grab an EP0 URB, register interest in a subset of USB events
+ */
+void udc_enable (struct usb_device_instance *device)
+{
+       if (udc_state == STATE_ERROR) {
+               return;
+       }
+
+       udc_device = device;
+
+       if (!ep_ref[0].urb) {
+               ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array);
+       }
+
+       /* Register interest in all events except SOF, enable transceiver */
+       usbp->usber = 0x03FF;
+       usbp->usbmr = 0x02F7;
+
+       return;
+}
+
+/* udc_disable
+ *
+ * disable the currently hooked device
+ */
+void udc_disable (void)
+{
+       int i = 0;
+
+       if (udc_state == STATE_ERROR) {
+               DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n");
+               return;
+       }
+
+       udc_device = 0;
+
+       for (; i < MAX_ENDPOINTS; i++) {
+               if (ep_ref[i].urb) {
+                       usbd_dealloc_urb (ep_ref[i].urb);
+                       ep_ref[i].urb = 0;
+               }
+       }
+
+       usbp->usbmr = 0x00;
+       usbp->usmod = ~USMOD_EN;
+       udc_state = STATE_NOT_READY;
+}
+
+/* udc_startup_events
+ *
+ * Enable the specified device
+ */
+void udc_startup_events (struct usb_device_instance *device)
+{
+       udc_enable (device);
+       if (udc_state == STATE_READY) {
+               usbd_device_event_irq (device, DEVICE_CREATE, 0);
+       }
+}
+
+/* udc_set_nak
+ *
+ * Allow upper layers to signal lower layers should not accept more RX data
+ *
+ */
+void udc_set_nak (int epid)
+{
+       if (epid) {
+               mpc8xx_udc_set_nak (epid);
+       }
+}
+
+/* udc_unset_nak
+ *
+ * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint.
+ * Switch off NAKing on this endpoint to accept more data output from host.
+ *
+ */
+void udc_unset_nak (int epid)
+{
+       if (epid > MAX_ENDPOINTS) {
+               return;
+       }
+
+       if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) {
+               usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK);
+               __asm__ ("eieio");
+       }
+}
+
+/******************************************************************************
+                             Static Linkage
+******************************************************************************/
+
+/* udc_state_transition_up
+ * udc_state_transition_down
+ *
+ * Helper functions to implement device state changes. The device states and
+ * the events that transition between them are:
+ *
+ *                             STATE_ATTACHED
+ *                             ||      /\
+ *                             \/      ||
+ *     DEVICE_HUB_CONFIGURED                   DEVICE_HUB_RESET
+ *                             ||      /\
+ *                             \/      ||
+ *                             STATE_POWERED
+ *                             ||      /\
+ *                             \/      ||
+ *     DEVICE_RESET                            DEVICE_POWER_INTERRUPTION
+ *                             ||      /\
+ *                             \/      ||
+ *                             STATE_DEFAULT
+ *                             ||      /\
+ *                             \/      ||
+ *     DEVICE_ADDRESS_ASSIGNED                 DEVICE_RESET
+ *                             ||      /\
+ *                             \/      ||
+ *                             STATE_ADDRESSED
+ *                             ||      /\
+ *                             \/      ||
+ *     DEVICE_CONFIGURED                       DEVICE_DE_CONFIGURED
+ *                             ||      /\
+ *                             \/      ||
+ *                             STATE_CONFIGURED
+ *
+ * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way.  If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition_down transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ */
+
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+                                           usb_device_state_t final)
+{
+       if (initial < final) {
+               switch (initial) {
+               case STATE_ATTACHED:
+                       usbd_device_event_irq (udc_device,
+                                              DEVICE_HUB_CONFIGURED, 0);
+                       if (final == STATE_POWERED)
+                               break;
+               case STATE_POWERED:
+                       usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+                       if (final == STATE_DEFAULT)
+                               break;
+               case STATE_DEFAULT:
+                       usbd_device_event_irq (udc_device,
+                                              DEVICE_ADDRESS_ASSIGNED, 0);
+                       if (final == STATE_ADDRESSED)
+                               break;
+               case STATE_ADDRESSED:
+                       usbd_device_event_irq (udc_device, DEVICE_CONFIGURED,
+                                              0);
+               case STATE_CONFIGURED:
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+                                             usb_device_state_t final)
+{
+       if (initial > final) {
+               switch (initial) {
+               case STATE_CONFIGURED:
+                       usbd_device_event_irq (udc_device,
+                                              DEVICE_DE_CONFIGURED, 0);
+                       if (final == STATE_ADDRESSED)
+                               break;
+               case STATE_ADDRESSED:
+                       usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+                       if (final == STATE_DEFAULT)
+                               break;
+               case STATE_DEFAULT:
+                       usbd_device_event_irq (udc_device,
+                                              DEVICE_POWER_INTERRUPTION, 0);
+                       if (final == STATE_POWERED)
+                               break;
+               case STATE_POWERED:
+                       usbd_device_event_irq (udc_device, DEVICE_HUB_RESET,
+                                              0);
+               case STATE_ATTACHED:
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+/* mpc8xx_udc_stall
+ *
+ * Force returning of STALL tokens on the given endpoint. Protocol or function
+ * STALL conditions are permissable here
+ */
+static void mpc8xx_udc_stall (unsigned int ep)
+{
+       usbp->usep[ep] |= STALL_BITMASK;
+}
+
+/* mpc8xx_udc_set_nak
+ *
+ * Force returning of NAK responses for the given endpoint as a kind of very
+ * simple flow control
+ */
+static void mpc8xx_udc_set_nak (unsigned int ep)
+{
+       usbp->usep[ep] |= NAK_BITMASK;
+       __asm__ ("eieio");
+}
+
+/* mpc8xx_udc_handle_txerr
+ *
+ * Handle errors relevant to TX. Return a status code to allow calling
+ * indicative of what if anything happened
+ */
+static short mpc8xx_udc_handle_txerr ()
+{
+       short ep = 0, ret = 0;
+
+       for (; ep < TX_RING_SIZE; ep++) {
+               if (usbp->usber & (0x10 << ep)) {
+
+                       /* Timeout or underrun */
+                       if (tx_cbd[ep]->cbd_sc & 0x06) {
+                               ret = 1;
+                               mpc8xx_udc_flush_tx_fifo (ep);
+
+                       } else {
+                               if (usbp->usep[ep] & STALL_BITMASK) {
+                                       if (!ep) {
+                                               usbp->usep[ep] &= ~STALL_BITMASK;
+                                       }
+                               }       /* else NAK */
+                       }
+                       usbp->usber |= (0x10 << ep);
+               }
+       }
+       return ret;
+}
+
+/* mpc8xx_udc_advance_rx
+ *
+ * Advance cbd rx
+ */
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid)
+{
+       if ((*rx_cbdp)->cbd_sc & RX_BD_W) {
+               *rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CFG_IMMR);
+
+       } else {
+               (*rx_cbdp)++;
+       }
+}
+
+
+/* mpc8xx_udc_flush_tx_fifo
+ *
+ * Flush a given TX fifo. Assumes one tx cbd per endpoint
+ */
+static void mpc8xx_udc_flush_tx_fifo (int epid)
+{
+       volatile cbd_t *tx_cbdp = 0;
+
+       if (epid > MAX_ENDPOINTS) {
+               return;
+       }
+
+       /* TX stop */
+       immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01);
+       __asm__ ("eieio");
+       while (immr->im_cpm.cp_cpcr & 0x01);
+
+       usbp->uscom = 0x40 | 0;
+
+       /* reset ring */
+       tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CFG_IMMR);
+       tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W);
+
+
+       endpoints[epid]->tptr = endpoints[epid]->tbase;
+       endpoints[epid]->tstate = 0x00;
+       endpoints[epid]->tbcnt = 0x00;
+
+       /* TX start */
+       immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01);
+       __asm__ ("eieio");
+       while (immr->im_cpm.cp_cpcr & 0x01);
+
+       return;
+}
+
+/* mpc8xx_udc_flush_rx_fifo
+ *
+ * For the sake of completeness of the namespace, it seems like
+ * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo();
+ * If RX_BD_E is true => a driver bug either here or in an upper layer
+ * not polling frequently enough. If RX_BD_E is true we have told the host
+ * we have accepted data but, the CPM found it had no-where to put that data
+ * which needless to say would be a bad thing.
+ */
+static void mpc8xx_udc_flush_rx_fifo ()
+{
+       int i = 0;
+
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+                       ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n",
+                            rx_cbd[i], rx_cbd[i]->cbd_datlen,
+                            rx_cbd[i]->cbd_sc);
+
+               }
+       }
+       ERR ("BUG : Input over-run\n");
+}
+
+/* mpc8xx_udc_clear_rxbd
+ *
+ * Release control of RX CBD to CP.
+ */
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp)
+{
+       rx_cbdp->cbd_datlen = 0x0000;
+       rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I));
+       __asm__ ("eieio");
+}
+
+/* mpc8xx_udc_tx_irq
+ *
+ * Parse for tx timeout, control RX or USB reset/busy conditions
+ * Return -1 on timeout, -2 on fatal error, else return zero
+ */
+static int mpc8xx_udc_tx_irq (int ep)
+{
+       int i = 0;
+
+       if (usbp->usber & (USB_TX_ERRMASK)) {
+               if (mpc8xx_udc_handle_txerr ()) {
+                       /* Timeout, controlling function must retry send */
+                       return -1;
+               }
+       }
+
+       if (usbp->usber & (USB_E_RESET | USB_E_BSY)) {
+               /* Fatal, abandon TX transaction */
+               return -2;
+       }
+
+       if (usbp->usber & USB_E_RXB) {
+               for (i = 0; i < RX_RING_SIZE; i++) {
+                       if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+                               if ((rx_cbd[i] == ep_ref[0].prx) || ep) {
+                                       return -2;
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* mpc8xx_udc_ep_tx
+ *
+ * Transmit in a re-entrant fashion outbound USB packets.
+ * Implement retry/timeout mechanism described in USB specification
+ * Toggle DATA0/DATA1 pids as necessary
+ * Introduces non-standard tx_retry. The USB standard has no scope for slave
+ * devices to give up TX, however tx_retry stops us getting stuck in an endless
+ * TX loop.
+ */
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi)
+{
+       struct urb *urb = epi->tx_urb;
+       volatile cbd_t *tx_cbdp = 0;
+       unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0;
+       int ret = 0;
+
+       if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) {
+               return -1;
+       }
+
+       ep = epi->endpoint_address & 0x03;
+       tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR);
+
+       if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) {
+               mpc8xx_udc_flush_tx_fifo (ep);
+               usbp->usber |= USB_E_TXB;
+       };
+
+       while (tx_retry++ < 100) {
+               ret = mpc8xx_udc_tx_irq (ep);
+               if (ret == -1) {
+                       /* ignore timeout here */
+               } else if (ret == -2) {
+                       /* Abandon TX */
+                       mpc8xx_udc_flush_tx_fifo (ep);
+                       return -1;
+               }
+
+               tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR);
+               while (tx_cbdp->cbd_sc & TX_BD_R) {
+               };
+               tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W);
+
+               pkt_len = urb->actual_length - epi->sent;
+
+               if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) {
+                       pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT);
+               }
+
+               for (x = 0; x < pkt_len; x++) {
+                       *((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) =
+                               urb->buffer[epi->sent + x];
+               }
+               tx_cbdp->cbd_datlen = pkt_len;
+               tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid);
+               __asm__ ("eieio");
+
+#ifdef __SIMULATE_ERROR__
+               if (++err_poison_test == 2) {
+                       err_poison_test = 0;
+                       tx_cbdp->cbd_sc &= ~TX_BD_TC;
+               }
+#endif
+
+               usbp->uscom = (USCOM_STR | ep);
+
+               while (!(usbp->usber & USB_E_TXB)) {
+                       ret = mpc8xx_udc_tx_irq (ep);
+                       if (ret == -1) {
+                               /* TX timeout */
+                               break;
+                       } else if (ret == -2) {
+                               if (usbp->usber & USB_E_TXB) {
+                                       usbp->usber |= USB_E_TXB;
+                               }
+                               mpc8xx_udc_flush_tx_fifo (ep);
+                               return -1;
+                       }
+               };
+
+               if (usbp->usber & USB_E_TXB) {
+                       usbp->usber |= USB_E_TXB;
+               }
+
+               /* ACK must be present <= 18bit times from TX */
+               if (ret == -1) {
+                       continue;
+               }
+
+               /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */
+               epi->sent += pkt_len;
+               epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize);
+               TOGGLE_TX_PID (ep_ref[ep].pid);
+
+               if (epi->sent >= epi->tx_urb->actual_length) {
+
+                       epi->tx_urb->actual_length = 0;
+                       epi->sent = 0;
+
+                       if (ep_ref[ep].sc & EP_SEND_ZLP) {
+                               ep_ref[ep].sc &= ~EP_SEND_ZLP;
+                       } else {
+                               return 0;
+                       }
+               }
+       }
+
+       ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent,
+            epi->tx_urb->actual_length);
+
+       return -1;
+}
+
+/* mpc8xx_udc_dump_request
+ *
+ * Dump a control request to console
+ */
+static void mpc8xx_udc_dump_request (struct usb_device_request *request)
+{
+       DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x "
+            "wIndex:%04x wLength:%04x ?\n",
+            request->bmRequestType,
+            request->bRequest,
+            request->wValue, request->wIndex, request->wLength);
+
+       return;
+}
+
+/* mpc8xx_udc_ep0_rx_setup
+ *
+ * Decode received ep0 SETUP packet. return non-zero on error
+ */
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp)
+{
+       unsigned int x = 0;
+       struct urb *purb = ep_ref[0].urb;
+       struct usb_endpoint_instance *epi =
+               &udc_device->bus->endpoint_array[0];
+
+       for (; x < rx_cbdp->cbd_datlen; x++) {
+               *(((unsigned char *) &ep_ref[0].urb->device_request) + x) =
+                       *((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+       }
+
+       mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+       if (ep0_recv_setup (purb)) {
+               mpc8xx_udc_dump_request (&purb->device_request);
+               return -1;
+       }
+
+       if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+           == USB_REQ_HOST2DEVICE) {
+
+               switch (purb->device_request.bRequest) {
+               case USB_REQ_SET_ADDRESS:
+                       /* Send the Status OUT ZLP */
+                       ep_ref[0].pid = TX_BD_PID_DATA1;
+                       purb->actual_length = 0;
+                       mpc8xx_udc_init_tx (epi, purb);
+                       mpc8xx_udc_ep_tx (epi);
+
+                       /* Move to the addressed state */
+                       usbp->usaddr = udc_device->address;
+                       mpc8xx_udc_state_transition_up (udc_device->device_state,
+                                                       STATE_ADDRESSED);
+                       return 0;
+
+               case USB_REQ_SET_CONFIGURATION:
+                       if (!purb->device_request.wValue) {
+                               /* Respond at default address */
+                               usbp->usaddr = 0x00;
+                               mpc8xx_udc_state_transition_down (udc_device->device_state,
+                                                                 STATE_ADDRESSED);
+                       } else {
+                               /* TODO: Support multiple configurations */
+                               mpc8xx_udc_state_transition_up (udc_device->device_state,
+                                                               STATE_CONFIGURED);
+                               for (x = 1; x < MAX_ENDPOINTS; x++) {
+                                       if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK)
+                                           == USB_DIR_IN) {
+                                               ep_ref[x].pid = TX_BD_PID_DATA0;
+                                       } else {
+                                               ep_ref[x].pid = RX_BD_PID_DATA0;
+                                       }
+                                       /* Set configuration must unstall endpoints */
+                                       usbp->usep[x] &= ~STALL_BITMASK;
+                               }
+                       }
+                       break;
+               default:
+                       /* CDC/Vendor specific */
+                       break;
+               }
+
+               /* Send ZLP as ACK in Status OUT phase */
+               ep_ref[0].pid = TX_BD_PID_DATA1;
+               purb->actual_length = 0;
+               mpc8xx_udc_init_tx (epi, purb);
+               mpc8xx_udc_ep_tx (epi);
+
+       } else {
+
+               if (purb->actual_length) {
+                       ep_ref[0].pid = TX_BD_PID_DATA1;
+                       mpc8xx_udc_init_tx (epi, purb);
+
+                       if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) {
+                               ep_ref[0].sc |= EP_SEND_ZLP;
+                       }
+
+                       if (purb->device_request.wValue ==
+                           USB_DESCRIPTOR_TYPE_DEVICE) {
+                               if (le16_to_cpu (purb->device_request.wLength)
+                                   > purb->actual_length) {
+                                       /* Send EP0_MAX_PACKET_SIZE bytes
+                                        * unless correct size requested.
+                                        */
+                                       if (purb->actual_length > epi->tx_packetSize) {
+                                               purb->actual_length = epi->tx_packetSize;
+                                       }
+                               }
+                       }
+                       mpc8xx_udc_ep_tx (epi);
+
+               } else {
+                       /* Corrupt SETUP packet? */
+                       ERR ("Zero length data or SETUP with DATA-IN phase ?\n");
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* mpc8xx_udc_init_tx
+ *
+ * Setup some basic parameters for a TX transaction
+ */
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+                               struct urb *tx_urb)
+{
+       epi->sent = 0;
+       epi->last = 0;
+       epi->tx_urb = tx_urb;
+}
+
+/* mpc8xx_udc_ep0_rx
+ *
+ * Receive ep0/control USB data. Parse and possibly send a response.
+ */
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp)
+{
+       if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) {
+
+               /* Unconditionally accept SETUP packets */
+               if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) {
+                       mpc8xx_udc_stall (0);
+               }
+
+       } else {
+
+               mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+               if ((rx_cbdp->cbd_datlen - 2)) {
+                       /* SETUP with a DATA phase
+                        * outside of SETUP packet.
+                        * Reply with STALL.
+                        */
+                       mpc8xx_udc_stall (0);
+               }
+       }
+}
+
+/* mpc8xx_udc_epn_rx
+ *
+ * Receive some data from cbd into USB system urb data abstraction
+ * Upper layers should NAK if there is insufficient RX data space
+ */
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp)
+{
+       struct usb_endpoint_instance *epi = 0;
+       struct urb *urb = 0;
+       unsigned int x = 0;
+
+       if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) {
+               return 0;
+       }
+
+       /* USB 2.0 PDF section 8.6.4
+        * Discard data with invalid PID it is a resend.
+        */
+       if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) {
+               return 1;
+       }
+       TOGGLE_RX_PID (ep_ref[epid].pid);
+
+       epi = &udc_device->bus->endpoint_array[epid];
+       urb = epi->rcv_urb;
+
+       for (; x < (rx_cbdp->cbd_datlen - 2); x++) {
+               *((unsigned char *) (urb->buffer + urb->actual_length + x)) =
+                       *((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+       }
+
+       if (x) {
+               usbd_rcv_complete (epi, x, 0);
+               if (ep_ref[epid].urb->status == RECV_ERROR) {
+                       DBG ("RX error unset NAK\n");
+                       udc_unset_nak (epid);
+               }
+       }
+       return x;
+}
+
+/* mpc8xx_udc_clock_init
+ *
+ * Obtain a clock reference for Full Speed Signaling
+ */
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+                                  volatile cpm8xx_t * cp)
+{
+
+#if defined(CFG_USB_EXTC_CLK)
+
+       /* This has been tested with a 48MHz crystal on CLK6 */
+       switch (CFG_USB_EXTC_CLK) {
+       case 1:
+               immr->im_ioport.iop_papar |= 0x0100;
+               immr->im_ioport.iop_padir &= ~0x0100;
+               cp->cp_sicr |= 0x24;
+               break;
+       case 2:
+               immr->im_ioport.iop_papar |= 0x0200;
+               immr->im_ioport.iop_padir &= ~0x0200;
+               cp->cp_sicr |= 0x2D;
+               break;
+       case 3:
+               immr->im_ioport.iop_papar |= 0x0400;
+               immr->im_ioport.iop_padir &= ~0x0400;
+               cp->cp_sicr |= 0x36;
+               break;
+       case 4:
+               immr->im_ioport.iop_papar |= 0x0800;
+               immr->im_ioport.iop_padir &= ~0x0800;
+               cp->cp_sicr |= 0x3F;
+               break;
+       default:
+               udc_state = STATE_ERROR;
+               break;
+       }
+
+#elif defined(CFG_USB_BRGCLK)
+
+       /* This has been tested with brgclk == 50MHz */
+       DECLARE_GLOBAL_DATA_PTR;
+       int divisor = 0;
+
+       if (gd->cpu_clk < 48000000L) {
+               ERR ("brgclk is too slow for full-speed USB!\n");
+               udc_state = STATE_ERROR;
+               return;
+       }
+
+       /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48Mhz)
+        * but, can /probably/ live with close-ish alternative rates.
+        */
+       divisor = (gd->cpu_clk / 48000000L) - 1;
+       cp->cp_sicr &= ~0x0000003F;
+
+       switch (CFG_USB_BRGCLK) {
+       case 1:
+               cp->cp_brgc1 |= (divisor | CPM_BRG_EN);
+               cp->cp_sicr &= ~0x2F;
+               break;
+       case 2:
+               cp->cp_brgc2 |= (divisor | CPM_BRG_EN);
+               cp->cp_sicr |= 0x00000009;
+               break;
+       case 3:
+               cp->cp_brgc3 |= (divisor | CPM_BRG_EN);
+               cp->cp_sicr |= 0x00000012;
+               break;
+       case 4:
+               cp->cp_brgc4 = (divisor | CPM_BRG_EN);
+               cp->cp_sicr |= 0x0000001B;
+               break;
+       default:
+               udc_state = STATE_ERROR;
+               break;
+       }
+
+#else
+#error "CFG_USB_EXTC_CLK or CFG_USB_BRGCLK must be defined"
+#endif
+
+}
+
+/* mpc8xx_udc_cbd_attach
+ *
+ * attach a cbd to and endpoint
+ */
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size)
+{
+
+       if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) {
+               udc_state = STATE_ERROR;
+               return;
+       }
+
+       if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT ||
+           (!tx_size && !rx_size)) {
+               udc_state = STATE_ERROR;
+               return;
+       }
+
+       /* Attach CBD to appropiate Parameter RAM Endpoint data structure */
+       if (rx_size) {
+               endpoints[ep]->rbase = (u32) rx_cbd[rx_ct];
+               endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+               rx_ct++;
+
+               if (!ep) {
+
+                       endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+                       rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+                       rx_ct++;
+
+               } else {
+                       rx_ct += 2;
+                       endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+                       rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+                       rx_ct++;
+               }
+
+               /* Where we expect to RX data on this endpoint */
+               ep_ref[ep].prx = rx_cbd[rx_ct - 1];
+       } else {
+
+               ep_ref[ep].prx = 0;
+               endpoints[ep]->rbase = 0;
+               endpoints[ep]->rbptr = 0;
+       }
+
+       if (tx_size) {
+               endpoints[ep]->tbase = (u32) tx_cbd[tx_ct];
+               endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct];
+               tx_ct++;
+       } else {
+               endpoints[ep]->tbase = 0;
+               endpoints[ep]->tbptr = 0;
+       }
+
+       endpoints[ep]->tstate = 0;
+       endpoints[ep]->tbcnt = 0;
+       endpoints[ep]->mrblr = EP_MAX_PKT;
+       endpoints[ep]->rfcr = 0x18;
+       endpoints[ep]->tfcr = 0x18;
+       ep_ref[ep].sc |= EP_ATTACHED;
+
+       DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n",
+               ep, endpoints[ep]->rbase, endpoints[ep]->rbptr,
+               endpoints[ep]->tbase, endpoints[ep]->tbptr,
+               ep_ref[ep].prx);
+
+       return;
+}
+
+/* mpc8xx_udc_cbd_init
+ *
+ * Allocate space for a cbd and allocate TX/RX data space
+ */
+static void mpc8xx_udc_cbd_init (void)
+{
+       int i = 0;
+
+       for (; i < TX_RING_SIZE; i++) {
+               tx_cbd[i] = (cbd_t *)
+                       mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+       }
+
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               rx_cbd[i] = (cbd_t *)
+                       mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+       }
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               tx_cbd[i]->cbd_bufaddr =
+                       mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+
+               tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W);
+               tx_cbd[i]->cbd_datlen = 0x0000;
+       }
+
+
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               rx_cbd[i]->cbd_bufaddr =
+                       mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+               rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E);
+               rx_cbd[i]->cbd_datlen = 0x0000;
+
+       }
+
+       return;
+}
+
+/* mpc8xx_udc_endpoint_init
+ *
+ * Attach an endpoint to some dpram
+ */
+static void mpc8xx_udc_endpoint_init (void)
+{
+       int i = 0;
+
+       for (; i < MAX_ENDPOINTS; i++) {
+               endpoints[i] = (usb_epb_t *)
+                       mpc8xx_udc_alloc (sizeof (usb_epb_t), 32);
+       }
+}
+
+/* mpc8xx_udc_alloc
+ *
+ * Grab the address of some dpram
+ */
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment)
+{
+       u32 retaddr = address_base;
+
+       while (retaddr % alignment) {
+               retaddr++;
+       }
+       address_base += data_size;
+
+       return retaddr;
+}
+
+#endif /* CONFIG_MPC885_FAMILY && CONFIG_USB_DEVICE) */
index 1d54a63..84bb936 100644 (file)
@@ -1517,4 +1517,31 @@ void udc_startup_events (struct usb_device_instance *device)
        udc_enable (device);
 }
 
+/**
+ * udc_irq - do pseudo interrupts
+ */
+void udc_irq(void)
+{
+       /* Loop while we have interrupts.
+        * If we don't do this, the input chain
+        * polling delay is likely to miss
+        * host requests.
+        */
+       while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) {
+               /* Handle any new IRQs */
+               omap1510_udc_irq ();
+               omap1510_udc_noniso_irq ();
+       }
+}
+
+/* Flow control */
+void udc_set_nak(int epid)
+{
+       /* TODO: implement this functionality in omap1510 */
+}
+
+void udc_unset_nak (int epid)
+{
+       /* TODO: implement this functionality in omap1510 */
+}
 #endif
index ce4a12e..d41a00b 100644 (file)
@@ -2,6 +2,9 @@
  * (C) Copyright 2003
  * Gerry Hamel, geh@ti.com, Texas Instruments
  *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, bodonoghue@codehermit.ie
+ *
  * 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
 #include <circbuf.h>
 #include <devices.h>
 #include "usbtty.h"
+#include "usb_cdc_acm.h"
+#include "usbdescriptors.h"
+#include <config.h>            /* If defined, override Linux identifiers with
+                                * vendor specific ones */
 
 #if 0
-#define TTYDBG(fmt,args...) serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
+#define TTYDBG(fmt,args...)\
+       serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
 #else
 #define TTYDBG(fmt,args...) do{}while(0)
 #endif
 
-#if 0
-#define TTYERR(fmt,args...) serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
+#if 1
+#define TTYERR(fmt,args...)\
+       serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\
+       __LINE__,##args)
 #else
 #define TTYERR(fmt,args...) do{}while(0)
 #endif
 
 /*
+ * Defines
+ */
+#define NUM_CONFIGS    1
+#define MAX_INTERFACES 2
+#define NUM_ENDPOINTS  3
+#define ACM_TX_ENDPOINT 3
+#define ACM_RX_ENDPOINT 2
+#define GSERIAL_TX_ENDPOINT 2
+#define GSERIAL_RX_ENDPOINT 1
+#define NUM_ACM_INTERFACES 2
+#define NUM_GSERIAL_INTERFACES 1
+#define CONFIG_USBD_DATA_INTERFACE_STR "Bulk Data Interface"
+#define CONFIG_USBD_CTRL_INTERFACE_STR "Control Interface"
+
+/*
  * Buffers to hold input and output data
  */
 #define USBTTY_BUFFER_SIZE 256
@@ -50,157 +75,336 @@ static circbuf_t usbtty_output;
  * Instance variables
  */
 static device_t usbttydev;
-static struct usb_device_instance       device_instance[1];
-static struct usb_bus_instance          bus_instance[1];
+static struct usb_device_instance device_instance[1];
+static struct usb_bus_instance bus_instance[1];
 static struct usb_configuration_instance config_instance[NUM_CONFIGS];
-static struct usb_interface_instance    interface_instance[NUM_INTERFACES];
-static struct usb_alternate_instance    alternate_instance[NUM_INTERFACES];
-static struct usb_endpoint_instance     endpoint_instance[NUM_ENDPOINTS+1]; /* one extra for control endpoint */
-
-/*
- * Static allocation of urbs
- */
-#define RECV_ENDPOINT 1
-#define TX_ENDPOINT 2
+static struct usb_interface_instance interface_instance[MAX_INTERFACES];
+static struct usb_alternate_instance alternate_instance[MAX_INTERFACES];
+/* one extra for control endpoint */
+static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1];
 
 /*
  * Global flag
  */
 int usbtty_configured_flag = 0;
 
-
 /*
  * Serial number
  */
 static char serial_number[16];
 
+
 /*
- * Descriptors
+ * Descriptors, Strings, Local variables.
  */
+
+/* defined and used by usbdcore_ep0.c */
+extern struct usb_string_descriptor **usb_strings;
+
+/* Indicies, References */
+static unsigned short rx_endpoint = 0;
+static unsigned short tx_endpoint = 0;
+static unsigned short interface_count = 0;
+static struct usb_string_descriptor *usbtty_string_table[STR_COUNT];
+
+/* USB Descriptor Strings */
 static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
 static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)];
 static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)];
 static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)];
 static u8 wstrConfiguration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)];
-static u8 wstrInterface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)];
-
-static struct usb_string_descriptor *usbtty_string_table[] = {
-  (struct usb_string_descriptor*)wstrLang,
-  (struct usb_string_descriptor*)wstrManufacturer,
-  (struct usb_string_descriptor*)wstrProduct,
-  (struct usb_string_descriptor*)wstrSerial,
-  (struct usb_string_descriptor*)wstrConfiguration,
-  (struct usb_string_descriptor*)wstrInterface
-};
-extern struct usb_string_descriptor **usb_strings; /* defined and used by omap1510_ep0.c */
+static u8 wstrDataInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)];
+static u8 wstrCtrlInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)];
 
+/* Standard USB Data Structures */
+static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES];
+static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];
+static struct usb_configuration_descriptor     *configuration_descriptor = 0;
 static struct usb_device_descriptor device_descriptor = {
-  bLength:           sizeof(struct usb_device_descriptor),
-  bDescriptorType:    USB_DT_DEVICE,
-  bcdUSB:            USB_BCD_VERSION,
-  bDeviceClass:              USBTTY_DEVICE_CLASS,
-  bDeviceSubClass:    USBTTY_DEVICE_SUBCLASS,
-  bDeviceProtocol:    USBTTY_DEVICE_PROTOCOL,
-  bMaxPacketSize0:    EP0_MAX_PACKET_SIZE,
-  idVendor:          CONFIG_USBD_VENDORID,
-  idProduct:         CONFIG_USBD_PRODUCTID,
-  bcdDevice:         USBTTY_BCD_DEVICE,
-  iManufacturer:      STR_MANUFACTURER,
-  iProduct:          STR_PRODUCT,
-  iSerialNumber:      STR_SERIAL,
-  bNumConfigurations: NUM_CONFIGS
-  };
-static struct usb_configuration_descriptor config_descriptors[NUM_CONFIGS] = {
-  {
-    bLength:            sizeof(struct usb_configuration_descriptor),
-    bDescriptorType:    USB_DT_CONFIG,
-    wTotalLength:       (sizeof(struct usb_configuration_descriptor)*NUM_CONFIGS) +
-                        (sizeof(struct usb_interface_descriptor)*NUM_INTERFACES) +
-                        (sizeof(struct usb_endpoint_descriptor)*NUM_ENDPOINTS),
-    bNumInterfaces:     NUM_INTERFACES,
-    bConfigurationValue: 1,
-    iConfiguration:     STR_CONFIG,
-    bmAttributes:       BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED,
-    bMaxPower:          USBTTY_MAXPOWER
-  },
-};
-static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES] = {
-  {
-    bLength:            sizeof(struct usb_interface_descriptor),
-    bDescriptorType:    USB_DT_INTERFACE,
-    bInterfaceNumber:   0,
-    bAlternateSetting:  0,
-    bNumEndpoints:      NUM_ENDPOINTS,
-    bInterfaceClass:    USBTTY_INTERFACE_CLASS,
-    bInterfaceSubClass:         USBTTY_INTERFACE_SUBCLASS,
-    bInterfaceProtocol:         USBTTY_INTERFACE_PROTOCOL,
-    iInterface:                 STR_INTERFACE
-  },
+       .bLength = sizeof(struct usb_device_descriptor),
+       .bDescriptorType =      USB_DT_DEVICE,
+       .bcdUSB =               cpu_to_le16(USB_BCD_VERSION),
+       .bDeviceSubClass =      0x00,
+       .bDeviceProtocol =      0x00,
+       .bMaxPacketSize0 =      EP0_MAX_PACKET_SIZE,
+       .idVendor =             cpu_to_le16(CONFIG_USBD_VENDORID),
+       .bcdDevice =            cpu_to_le16(USBTTY_BCD_DEVICE),
+       .iManufacturer =        STR_MANUFACTURER,
+       .iProduct =             STR_PRODUCT,
+       .iSerialNumber =        STR_SERIAL,
+       .bNumConfigurations =   NUM_CONFIGS
 };
-static struct usb_endpoint_descriptor ep_descriptors[NUM_ENDPOINTS] = {
-  {
-    bLength:            sizeof(struct usb_endpoint_descriptor),
-    bDescriptorType:    USB_DT_ENDPOINT,
-    bEndpointAddress:   CONFIG_USBD_SERIAL_OUT_ENDPOINT | USB_DIR_OUT,
-    bmAttributes:       USB_ENDPOINT_XFER_BULK,
-    wMaxPacketSize:     CONFIG_USBD_SERIAL_OUT_PKTSIZE,
-    bInterval:          0
-  },
-  {
-    bLength:            sizeof(struct usb_endpoint_descriptor),
-    bDescriptorType:    USB_DT_ENDPOINT,
-    bEndpointAddress:   CONFIG_USBD_SERIAL_IN_ENDPOINT | USB_DIR_IN,
-    bmAttributes:       USB_ENDPOINT_XFER_BULK,
-    wMaxPacketSize:     CONFIG_USBD_SERIAL_IN_PKTSIZE,
-    bInterval:          0
-  },
-  {
-    bLength:            sizeof(struct usb_endpoint_descriptor),
-    bDescriptorType:    USB_DT_ENDPOINT,
-    bEndpointAddress:   CONFIG_USBD_SERIAL_INT_ENDPOINT | USB_DIR_IN,
-    bmAttributes:       USB_ENDPOINT_XFER_INT,
-    wMaxPacketSize:     CONFIG_USBD_SERIAL_INT_PKTSIZE,
-    bInterval:          0
-  },
+
+
+/*
+ * Static CDC ACM specific descriptors
+ */
+
+struct acm_config_desc {
+       struct usb_configuration_descriptor configuration_desc;
+
+       /* Master Interface */
+       struct usb_interface_descriptor interface_desc;
+
+       struct usb_class_header_function_descriptor usb_class_header;
+       struct usb_class_call_management_descriptor usb_class_call_mgt;
+       struct usb_class_abstract_control_descriptor usb_class_acm;
+       struct usb_class_union_function_descriptor usb_class_union;
+       struct usb_endpoint_descriptor notification_endpoint;
+
+       /* Slave Interface */
+       struct usb_interface_descriptor data_class_interface;
+       struct usb_endpoint_descriptor
+               data_endpoints[NUM_ENDPOINTS-1] __attribute__((packed));
+} __attribute__((packed));
+
+static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
+       {
+               .configuration_desc ={
+                       .bLength =
+                               sizeof(struct usb_configuration_descriptor),
+                       .bDescriptorType = USB_DT_CONFIG,
+                       .wTotalLength =
+                               cpu_to_le16(sizeof(struct acm_config_desc)),
+                       .bNumInterfaces = NUM_ACM_INTERFACES,
+                       .bConfigurationValue = 1,
+                       .iConfiguration = STR_CONFIG,
+                       .bmAttributes =
+                               BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
+                       .bMaxPower = USBTTY_MAXPOWER
+               },
+               /* Interface 1 */
+               .interface_desc = {
+                       .bLength  = sizeof(struct usb_interface_descriptor),
+                       .bDescriptorType = USB_DT_INTERFACE,
+                       .bInterfaceNumber = 0,
+                       .bAlternateSetting = 0,
+                       .bNumEndpoints = 0x01,
+                       .bInterfaceClass =
+                               COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
+                       .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+                       .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
+                       .iInterface = STR_CTRL_INTERFACE,
+               },
+               .usb_class_header = {
+                       .bFunctionLength        =
+                               sizeof(struct usb_class_header_function_descriptor),
+                       .bDescriptorType        = CS_INTERFACE,
+                       .bDescriptorSubtype     = USB_ST_HEADER,
+                       .bcdCDC = cpu_to_le16(110),
+               },
+               .usb_class_call_mgt = {
+                       .bFunctionLength        =
+                               sizeof(struct usb_class_call_management_descriptor),
+                       .bDescriptorType        = CS_INTERFACE,
+                       .bDescriptorSubtype     = USB_ST_CMF,
+                       .bmCapabilities         = 0x00,
+                       .bDataInterface         = 0x01,
+               },
+               .usb_class_acm = {
+                       .bFunctionLength        =
+                               sizeof(struct usb_class_abstract_control_descriptor),
+                       .bDescriptorType        = CS_INTERFACE,
+                       .bDescriptorSubtype     = USB_ST_ACMF,
+                       .bmCapabilities         = 0x00,
+               },
+               .usb_class_union = {
+                       .bFunctionLength        =
+                               sizeof(struct usb_class_union_function_descriptor),
+                       .bDescriptorType        = CS_INTERFACE,
+                       .bDescriptorSubtype     = USB_ST_UF,
+                       .bMasterInterface       = 0x00,
+                       .bSlaveInterface0       = 0x01,
+               },
+               .notification_endpoint = {
+                       .bLength =
+                               sizeof(struct usb_endpoint_descriptor),
+                       .bDescriptorType        = USB_DT_ENDPOINT,
+                       .bEndpointAddress       = 0x01 | USB_DIR_IN,
+                       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+                       .wMaxPacketSize
+                               = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE),
+                       .bInterval              = 0xFF,
+               },
+
+               /* Interface 2 */
+               .data_class_interface = {
+                       .bLength                =
+                               sizeof(struct usb_interface_descriptor),
+                       .bDescriptorType        = USB_DT_INTERFACE,
+                       .bInterfaceNumber       = 0x01,
+                       .bAlternateSetting      = 0x00,
+                       .bNumEndpoints          = 0x02,
+                       .bInterfaceClass        =
+                               COMMUNICATIONS_INTERFACE_CLASS_DATA,
+                       .bInterfaceSubClass     = DATA_INTERFACE_SUBCLASS_NONE,
+                       .bInterfaceProtocol     = DATA_INTERFACE_PROTOCOL_NONE,
+                       .iInterface             = STR_DATA_INTERFACE,
+               },
+               .data_endpoints = {
+                       {
+                               .bLength                =
+                                       sizeof(struct usb_endpoint_descriptor),
+                               .bDescriptorType        = USB_DT_ENDPOINT,
+                               .bEndpointAddress       = 0x02 | USB_DIR_OUT,
+                               .bmAttributes           =
+                                       USB_ENDPOINT_XFER_BULK,
+                               .wMaxPacketSize         =
+                                       cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+                               .bInterval              = 0xFF,
+                       },
+                       {
+                               .bLength                =
+                                       sizeof(struct usb_endpoint_descriptor),
+                               .bDescriptorType        = USB_DT_ENDPOINT,
+                               .bEndpointAddress       = 0x03 | USB_DIR_IN,
+                               .bmAttributes           =
+                                       USB_ENDPOINT_XFER_BULK,
+                               .wMaxPacketSize         =
+                                       cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+                               .bInterval              = 0xFF,
+                       },
+               },
+       },
 };
-static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS] = {
-  &(ep_descriptors[0]),
-  &(ep_descriptors[1]),
-  &(ep_descriptors[2]),
+
+static struct rs232_emu rs232_desc={
+               .dter           =       115200,
+               .stop_bits      =       0x00,
+               .parity         =       0x00,
+               .data_bits      =       0x08
 };
 
-/* utility function for converting char* to wide string used by USB */
-static void str2wide (char *str, u16 * wide)
-{
-       int i;
 
-       for (i = 0; i < strlen (str) && str[i]; i++)
-               wide[i] = (u16) str[i];
-}
+/*
+ * Static Generic Serial specific data
+ */
+
+
+struct gserial_config_desc {
+
+       struct usb_configuration_descriptor configuration_desc;
+       struct usb_interface_descriptor
+               interface_desc[NUM_GSERIAL_INTERFACES] __attribute__((packed));
+       struct usb_endpoint_descriptor
+               data_endpoints[NUM_ENDPOINTS] __attribute__((packed));
+
+} __attribute__((packed));
+
+static struct gserial_config_desc
+gserial_configuration_descriptors[NUM_CONFIGS] ={
+       {
+               .configuration_desc ={
+                       .bLength = sizeof(struct usb_configuration_descriptor),
+                       .bDescriptorType = USB_DT_CONFIG,
+                       .wTotalLength =
+                               cpu_to_le16(sizeof(struct gserial_config_desc)),
+                       .bNumInterfaces = NUM_GSERIAL_INTERFACES,
+                       .bConfigurationValue = 1,
+                       .iConfiguration = STR_CONFIG,
+                       .bmAttributes =
+                               BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
+                       .bMaxPower = USBTTY_MAXPOWER
+               },
+               .interface_desc = {
+                       {
+                               .bLength  =
+                                       sizeof(struct usb_interface_descriptor),
+                               .bDescriptorType = USB_DT_INTERFACE,
+                               .bInterfaceNumber = 0,
+                               .bAlternateSetting = 0,
+                               .bNumEndpoints = NUM_ENDPOINTS,
+                               .bInterfaceClass =
+                                       COMMUNICATIONS_INTERFACE_CLASS_VENDOR,
+                               .bInterfaceSubClass =
+                                       COMMUNICATIONS_NO_SUBCLASS,
+                               .bInterfaceProtocol =
+                                       COMMUNICATIONS_NO_PROTOCOL,
+                               .iInterface = STR_DATA_INTERFACE
+                       },
+               },
+               .data_endpoints  = {
+                       {
+                               .bLength =
+                                       sizeof(struct usb_endpoint_descriptor),
+                               .bDescriptorType =      USB_DT_ENDPOINT,
+                               .bEndpointAddress =     0x01 | USB_DIR_OUT,
+                               .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+                               .wMaxPacketSize =
+                                       cpu_to_le16(CONFIG_USBD_SERIAL_OUT_PKTSIZE),
+                               .bInterval=             0xFF,
+                       },
+                       {
+                               .bLength =
+                                       sizeof(struct usb_endpoint_descriptor),
+                               .bDescriptorType =      USB_DT_ENDPOINT,
+                               .bEndpointAddress =     0x02 | USB_DIR_IN,
+                               .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+                               .wMaxPacketSize =
+                                       cpu_to_le16(CONFIG_USBD_SERIAL_IN_PKTSIZE),
+                               .bInterval =            0xFF,
+                       },
+                       {
+                               .bLength =
+                                       sizeof(struct usb_endpoint_descriptor),
+                               .bDescriptorType =      USB_DT_ENDPOINT,
+                               .bEndpointAddress =     0x03 | USB_DIR_IN,
+                               .bmAttributes =         USB_ENDPOINT_XFER_INT,
+                               .wMaxPacketSize =
+                                       cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE),
+                               .bInterval =            0xFF,
+                       },
+               },
+       },
+};
 
 /*
- * Prototypes
+ * Static Function Prototypes
  */
+
 static void usbtty_init_strings (void);
 static void usbtty_init_instances (void);
 static void usbtty_init_endpoints (void);
-
+static void usbtty_init_terminal_type(short type);
 static void usbtty_event_handler (struct usb_device_instance *device,
-                                 usb_device_event_t event, int data);
+                               usb_device_event_t event, int data);
+static int usbtty_cdc_setup(struct usb_device_request *request,
+                               struct urb *urb);
 static int usbtty_configured (void);
-
 static int write_buffer (circbuf_t * buf);
 static int fill_buffer (circbuf_t * buf);
 
 void usbtty_poll (void);
-static void pretend_interrupts (void);
 
+/* utility function for converting char* to wide string used by USB */
+static void str2wide (char *str, u16 * wide)
+{
+       int i;
+       for (i = 0; i < strlen (str) && str[i]; i++){
+               #if defined(__LITTLE_ENDIAN__)
+                       wide[i] = (u16) str[i];
+               #elif defined(__BIG_ENDIAN__)
+                       wide[i] = ((u16)(str[i])<<8);
+               #else
+                       #error "__LITTLE_ENDIAN__ or __BIG_ENDIAN__ undefined"
+               #endif
+       }
+}
 
 /*
  * Test whether a character is in the RX buffer
  */
+
 int usbtty_tstc (void)
 {
+       struct usb_endpoint_instance *endpoint =
+               &endpoint_instance[rx_endpoint];
+
+       /* If no input data exists, allow more RX to be accepted */
+       if(usbtty_input.size <= 0){
+               udc_unset_nak(endpoint->endpoint_address&0x03);
+       }
+
        usbtty_poll ();
        return (usbtty_input.size > 0);
 }
@@ -210,15 +414,21 @@ int usbtty_tstc (void)
  * otherwise. When the function is succesfull, the character read is
  * written into its argument c.
  */
+
 int usbtty_getc (void)
 {
        char c;
+       struct usb_endpoint_instance *endpoint =
+               &endpoint_instance[rx_endpoint];
 
        while (usbtty_input.size <= 0) {
+               udc_unset_nak(endpoint->endpoint_address&0x03);
                usbtty_poll ();
        }
 
        buf_pop (&usbtty_input, &c, 1);
+       udc_set_nak(endpoint->endpoint_address&0x03);
+
        return c;
 }
 
@@ -238,7 +448,6 @@ void usbtty_putc (const char c)
        }
 }
 
-
 /* usbtty_puts() helper function for finding the next '\n' in a string */
 static int next_nl_pos (const char *s)
 {
@@ -252,8 +461,9 @@ static int next_nl_pos (const char *s)
 }
 
 /*
- * Output a string to the usb client port.
+ * Output a string to the usb client port - implementing flow control
  */
+
 static void __usbtty_puts (const char *str, int len)
 {
        int maxlen = usbtty_output.totalsize;
@@ -261,22 +471,19 @@ static void __usbtty_puts (const char *str, int len)
 
        /* break str into chunks < buffer size, if needed */
        while (len > 0) {
-               space = maxlen - usbtty_output.size;
+               usbtty_poll ();
 
+               space = maxlen - usbtty_output.size;
                /* Empty buffer here, if needed, to ensure space... */
-               if (space <= 0) {
+               if (space) {
                        write_buffer (&usbtty_output);
-                       space = maxlen - usbtty_output.size;
-                       if (space <= 0) {
-                               space = len;    /* allow old data to be overwritten. */
-                       }
-               }
 
-               n = MIN (space, MIN (len, maxlen));
-               buf_push (&usbtty_output, str, n);
+                       n = MIN (space, MIN (len, maxlen));
+                       buf_push (&usbtty_output, str, n);
 
-               str += n;
-               len -= n;
+                       str += n;
+                       len -= n;
+               }
        }
 }
 
@@ -313,8 +520,10 @@ int drv_usbtty_init (void)
 {
        int rc;
        char * sn;
+       char * tt;
        int snlen;
 
+       /* Ger seiral number */
        if (!(sn = getenv("serial#"))) {
                sn = "000000000000";
        }
@@ -327,6 +536,14 @@ int drv_usbtty_init (void)
        memcpy (serial_number, sn, snlen);
        serial_number[snlen] = '\0';
 
+       /* Decide on which type of UDC device to be.
+        */
+
+       if(!(tt = getenv("usbtty"))) {
+               tt = "generic";
+       }
+       usbtty_init_terminal_type(strcmp(tt,"cdc_acm"));
+
        /* prepare buffers... */
        buf_init (&usbtty_input, USBTTY_BUFFER_SIZE);
        buf_init (&usbtty_output, USBTTY_BUFFER_SIZE);
@@ -337,7 +554,7 @@ int drv_usbtty_init (void)
        usbtty_init_strings ();
        usbtty_init_instances ();
 
-       udc_startup_events (device_instance);   /* Enable our device, initialize udc pointers */
+       udc_startup_events (device_instance);/* Enable dev, init udc pointers */
        udc_connect ();         /* Enable pullup for host detection */
 
        usbtty_init_endpoints ();
@@ -362,30 +579,48 @@ static void usbtty_init_strings (void)
 {
        struct usb_string_descriptor *string;
 
+       usbtty_string_table[STR_LANG] =
+               (struct usb_string_descriptor*)wstrLang;
+
        string = (struct usb_string_descriptor *) wstrManufacturer;
-       string->bLength = sizeof (wstrManufacturer);
+       string->bLength = sizeof(wstrManufacturer);
        string->bDescriptorType = USB_DT_STRING;
        str2wide (CONFIG_USBD_MANUFACTURER, string->wData);
+       usbtty_string_table[STR_MANUFACTURER]=string;
+
 
        string = (struct usb_string_descriptor *) wstrProduct;
-       string->bLength = sizeof (wstrProduct);
+       string->bLength = sizeof(wstrProduct);
        string->bDescriptorType = USB_DT_STRING;
        str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData);
+       usbtty_string_table[STR_PRODUCT]=string;
+
 
        string = (struct usb_string_descriptor *) wstrSerial;
-       string->bLength = 2 + 2*strlen(serial_number);
+       string->bLength = sizeof(serial_number);
        string->bDescriptorType = USB_DT_STRING;
        str2wide (serial_number, string->wData);
+       usbtty_string_table[STR_SERIAL]=string;
+
 
        string = (struct usb_string_descriptor *) wstrConfiguration;
-       string->bLength = sizeof (wstrConfiguration);
+       string->bLength = sizeof(wstrConfiguration);
        string->bDescriptorType = USB_DT_STRING;
        str2wide (CONFIG_USBD_CONFIGURATION_STR, string->wData);
+       usbtty_string_table[STR_CONFIG]=string;
 
-       string = (struct usb_string_descriptor *) wstrInterface;
-       string->bLength = sizeof (wstrInterface);
+
+       string = (struct usb_string_descriptor *) wstrDataInterface;
+       string->bLength = sizeof(wstrDataInterface);
        string->bDescriptorType = USB_DT_STRING;
-       str2wide (CONFIG_USBD_INTERFACE_STR, string->wData);
+       str2wide (CONFIG_USBD_DATA_INTERFACE_STR, string->wData);
+       usbtty_string_table[STR_DATA_INTERFACE]=string;
+
+       string = (struct usb_string_descriptor *) wstrCtrlInterface;
+       string->bLength = sizeof(wstrCtrlInterface);
+       string->bDescriptorType = USB_DT_STRING;
+       str2wide (CONFIG_USBD_CTRL_INTERFACE_STR, string->wData);
+       usbtty_string_table[STR_CTRL_INTERFACE]=string;
 
        /* Now, initialize the string table for ep0 handling */
        usb_strings = usbtty_string_table;
@@ -400,6 +635,7 @@ static void usbtty_init_instances (void)
        device_instance->device_state = STATE_INIT;
        device_instance->device_descriptor = &device_descriptor;
        device_instance->event = usbtty_event_handler;
+       device_instance->cdc_recv_setup = usbtty_cdc_setup;
        device_instance->bus = bus_instance;
        device_instance->configurations = NUM_CONFIGS;
        device_instance->configuration_instance_array = config_instance;
@@ -415,8 +651,8 @@ static void usbtty_init_instances (void)
        /* configuration instance */
        memset (config_instance, 0,
                sizeof (struct usb_configuration_instance));
-       config_instance->interfaces = NUM_INTERFACES;
-       config_instance->configuration_descriptor = config_descriptors;
+       config_instance->interfaces = interface_count;
+       config_instance->configuration_descriptor = configuration_descriptor;
        config_instance->interface_instance_array = interface_instance;
 
        /* interface instance */
@@ -447,17 +683,22 @@ static void usbtty_init_instances (void)
                        sizeof (struct usb_endpoint_instance));
 
                endpoint_instance[i].endpoint_address =
-                       ep_descriptors[i - 1].bEndpointAddress;
+                       ep_descriptor_ptrs[i - 1]->bEndpointAddress;
 
-               endpoint_instance[i].rcv_packetSize =
-                       ep_descriptors[i - 1].wMaxPacketSize;
                endpoint_instance[i].rcv_attributes =
-                       ep_descriptors[i - 1].bmAttributes;
+                       ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+               endpoint_instance[i].rcv_packetSize =
+                       le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
+
+               endpoint_instance[i].tx_attributes =
+                       ep_descriptor_ptrs[i - 1]->bmAttributes;
 
                endpoint_instance[i].tx_packetSize =
-                       ep_descriptors[i - 1].wMaxPacketSize;
+                       le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
+
                endpoint_instance[i].tx_attributes =
-                       ep_descriptors[i - 1].bmAttributes;
+                       ep_descriptor_ptrs[i - 1]->bmAttributes;
 
                urb_link_init (&endpoint_instance[i].rcv);
                urb_link_init (&endpoint_instance[i].rdy);
@@ -480,13 +721,79 @@ static void usbtty_init_endpoints (void)
        int i;
 
        bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
-       for (i = 0; i <= NUM_ENDPOINTS; i++) {
+       for (i = 1; i <= NUM_ENDPOINTS; i++) {
                udc_setup_ep (device_instance, i, &endpoint_instance[i]);
        }
 }
 
+/* usbtty_init_terminal_type
+ *
+ * Do some late binding for our device type.
+ */
+static void usbtty_init_terminal_type(short type)
+{
+       switch(type){
+               /* CDC ACM */
+               case 0:
+                       /* Assign endpoint descriptors */
+                       ep_descriptor_ptrs[0] =
+                               &acm_configuration_descriptors[0].notification_endpoint;
+                       ep_descriptor_ptrs[1] =
+                               &acm_configuration_descriptors[0].data_endpoints[0];
+                       ep_descriptor_ptrs[2] =
+                               &acm_configuration_descriptors[0].data_endpoints[1];
+
+                       /* Enumerate Device Descriptor */
+                       device_descriptor.bDeviceClass =
+                               COMMUNICATIONS_DEVICE_CLASS;
+                       device_descriptor.idProduct =
+                               cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM);
+
+                       /* Assign endpoint indices */
+                       tx_endpoint = ACM_TX_ENDPOINT;
+                       rx_endpoint = ACM_RX_ENDPOINT;
+
+                       /* Configuration Descriptor */
+                       configuration_descriptor =
+                               (struct usb_configuration_descriptor*)
+                               &acm_configuration_descriptors;
+
+                       /* Interface count */
+                       interface_count = NUM_ACM_INTERFACES;
+               break;
+
+               /* BULK IN/OUT & Default */
+               case 1:
+               default:
+                       /* Assign endpoint descriptors */
+                       ep_descriptor_ptrs[0] =
+                               &gserial_configuration_descriptors[0].data_endpoints[0];
+                       ep_descriptor_ptrs[1] =
+                               &gserial_configuration_descriptors[0].data_endpoints[1];
+                       ep_descriptor_ptrs[2] =
+                               &gserial_configuration_descriptors[0].data_endpoints[2];
+
+                       /* Enumerate Device Descriptor */
+                       device_descriptor.bDeviceClass = 0xFF;
+                       device_descriptor.idProduct =
+                               cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL);
+
+                       /* Assign endpoint indices */
+                       tx_endpoint = GSERIAL_TX_ENDPOINT;
+                       rx_endpoint = GSERIAL_RX_ENDPOINT;
+
+                       /* Configuration Descriptor */
+                       configuration_descriptor =
+                               (struct usb_configuration_descriptor*)
+                               &gserial_configuration_descriptors;
+
+                       /* Interface count */
+                       interface_count = NUM_GSERIAL_INTERFACES;
+               break;
+       }
+}
 
-/*********************************************************************************/
+/******************************************************************************/
 
 static struct urb *next_urb (struct usb_device_instance *device,
                             struct usb_endpoint_instance *endpoint)
@@ -526,27 +833,39 @@ static int write_buffer (circbuf_t * buf)
                return 0;
        }
 
-       if (buf->size) {
+       struct usb_endpoint_instance *endpoint =
+                       &endpoint_instance[tx_endpoint];
+       struct urb *current_urb = NULL;
 
-               struct usb_endpoint_instance *endpoint =
-                       &endpoint_instance[TX_ENDPOINT];
-               struct urb *current_urb = NULL;
+       current_urb = next_urb (device_instance, endpoint);
+       /* TX data still exists - send it now
+        */
+       if(endpoint->sent < current_urb->actual_length){
+               if(udc_endpoint_write (endpoint)){
+                       /* Write pre-empted by RX */
+                       return -1;
+               }
+       }
+
+       if (buf->size) {
                char *dest;
 
                int space_avail;
                int popnum, popped;
                int total = 0;
 
-               /* Break buffer into urb sized pieces, and link each to the endpoint */
+               /* Break buffer into urb sized pieces,
+                * and link each to the endpoint
+                */
                while (buf->size > 0) {
-                       current_urb = next_urb (device_instance, endpoint);
+
                        if (!current_urb) {
                                TTYERR ("current_urb is NULL, buf->size %d\n",
                                        buf->size);
                                return total;
                        }
 
-                       dest = current_urb->buffer +
+                       dest = (char*)current_urb->buffer +
                                current_urb->actual_length;
 
                        space_avail =
@@ -562,14 +881,19 @@ static int write_buffer (circbuf_t * buf)
                        current_urb->actual_length += popped;
                        total += popped;
 
-                       /* If endpoint->last == 0, then transfers have not started on this endpoint */
+                       /* If endpoint->last == 0, then transfers have
+                        * not started on this endpoint
+                        */
                        if (endpoint->last == 0) {
-                               udc_endpoint_write (endpoint);
+                               if(udc_endpoint_write (endpoint)){
+                                       /* Write pre-empted by RX */
+                                       return -1;
+                               }
                        }
 
-               }               /* end while */
+               }/* end while */
                return total;
-       }                       /* end if tx_urb */
+       }
 
        return 0;
 }
@@ -577,18 +901,22 @@ static int write_buffer (circbuf_t * buf)
 static int fill_buffer (circbuf_t * buf)
 {
        struct usb_endpoint_instance *endpoint =
-               &endpoint_instance[RECV_ENDPOINT];
+               &endpoint_instance[rx_endpoint];
 
        if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) {
-               unsigned int nb = endpoint->rcv_urb->actual_length;
+               unsigned int nb = 0;
                char *src = (char *) endpoint->rcv_urb->buffer;
+               unsigned int rx_avail = buf->totalsize - buf->size;
+
+               if(rx_avail >= endpoint->rcv_urb->actual_length){
 
-               buf_push (buf, src, nb);
-               endpoint->rcv_urb->actual_length = 0;
+                       nb = endpoint->rcv_urb->actual_length;
+                       buf_push (buf, src, nb);
+                       endpoint->rcv_urb->actual_length = 0;
 
+               }
                return nb;
        }
-
        return 0;
 }
 
@@ -597,7 +925,7 @@ static int usbtty_configured (void)
        return usbtty_configured_flag;
 }
 
-/*********************************************************************************/
+/******************************************************************************/
 
 static void usbtty_event_handler (struct usb_device_instance *device,
                                  usb_device_event_t event, int data)
@@ -619,8 +947,34 @@ static void usbtty_event_handler (struct usb_device_instance *device,
        }
 }
 
-/*********************************************************************************/
+/******************************************************************************/
+
+int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb)
+{
+       switch (request->bRequest){
+
+               case ACM_SET_CONTROL_LINE_STATE:        /* Implies DTE ready */
+                       break;
+               case ACM_SEND_ENCAPSULATED_COMMAND :    /* Required */
+                       break;
+               case ACM_SET_LINE_ENCODING :            /* DTE stop/parity bits
+                                                        * per character */
+                       break;
+               case ACM_GET_ENCAPSULATED_RESPONSE :    /* request response */
+                       break;
+               case ACM_GET_LINE_ENCODING :            /* request DTE rate,
+                                                        * stop/parity bits */
+                       memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc));
+                       urb->actual_length = sizeof(rs232_desc);
+
+                       break;
+               default:
+                       return 1;
+       }
+       return 0;
+}
 
+/******************************************************************************/
 
 /*
  * Since interrupt handling has not yet been implemented, we use this function
@@ -630,36 +984,29 @@ static void usbtty_event_handler (struct usb_device_instance *device,
 void usbtty_poll (void)
 {
        /* New interrupts? */
-       pretend_interrupts ();
+       udc_irq();
 
-       /* Write any output data to host buffer (do this before checking interrupts to avoid missing one) */
+       /* Write any output data to host buffer
+        * (do this before checking interrupts to avoid missing one)
+        */
        if (usbtty_configured ()) {
                write_buffer (&usbtty_output);
        }
 
        /* New interrupts? */
-       pretend_interrupts ();
+       udc_irq();
 
-       /* Check for new data from host.. (do this after checking interrupts to get latest data) */
+       /* Check for new data from host..
+        * (do this after checking interrupts to get latest data)
+        */
        if (usbtty_configured ()) {
                fill_buffer (&usbtty_input);
        }
 
        /* New interrupts? */
-       pretend_interrupts ();
-}
+       udc_irq();
 
-static void pretend_interrupts (void)
-{
-       /* Loop while we have interrupts.
-        * If we don't do this, the input chain
-        * polling delay is likely to miss
-        * host requests.
-        */
-       while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) {
-               /* Handle any new IRQs */
-               omap1510_udc_irq ();
-               omap1510_udc_noniso_irq ();
-       }
 }
+
+
 #endif
index 79c2fe5..8154e30 100644 (file)
@@ -2,6 +2,9 @@
  * (C) Copyright 2003
  * Gerry Hamel, geh@ti.com, Texas Instruments
  *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit
+ *
  * 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
 #ifndef __USB_TTY_H__
 #define __USB_TTY_H__
 
-
 #include "usbdcore.h"
+#if defined(CONFIG_PPC)
+#include "usbdcore_mpc8xx.h"
+#elif defined(CONFIG_ARM)
 #include "usbdcore_omap1510.h"
+#endif
 
+#include <version_autogenerated.h>
 
-#define NUM_CONFIGS    1
-#define NUM_INTERFACES 1
-#define NUM_ENDPOINTS  3
+/* If no VendorID/ProductID is defined in config.h, pretend to be Linux
+ * DO NOT Reuse this Vendor/Product setup with protocol incompatible devices */
 
-#define EP0_MAX_PACKET_SIZE 64
+#define CONFIG_USBD_VENDORID 0x0525    /* Linux/NetChip */
+#define CONFIG_USBD_PRODUCTID_GSERIAL 0xa4a6   /* gserial */
+#define CONFIG_USBD_PRODUCTID_CDCACM  0xa4a7   /* CDC ACM */
+#define CONFIG_USBD_MANUFACTURER "Das U-Boot"
+#define CONFIG_USBD_PRODUCT_NAME U_BOOT_VERSION
 
-#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB"
-#define CONFIG_USBD_INTERFACE_STR     "Simple Serial Data Interface - Bulk Mode"
 
+#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB"
 
-#define CONFIG_USBD_SERIAL_OUT_ENDPOINT 2
-#define CONFIG_USBD_SERIAL_OUT_PKTSIZE 64
-#define CONFIG_USBD_SERIAL_IN_ENDPOINT 1
-#define CONFIG_USBD_SERIAL_IN_PKTSIZE  64
-#define CONFIG_USBD_SERIAL_INT_ENDPOINT 5
-#define CONFIG_USBD_SERIAL_INT_PKTSIZE 16
-
+#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT
+#define CONFIG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT
+#define CONFIG_USBD_SERIAL_IN_PKTSIZE  UDC_IN_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT
+#define CONFIG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_BULK_PKTSIZE        UDC_BULK_PACKET_SIZE
 
 #define USBTTY_DEVICE_CLASS    COMMUNICATIONS_DEVICE_CLASS
-#define USBTTY_DEVICE_SUBCLASS COMMUNICATIONS_NO_SUBCLASS
-#define USBTTY_DEVICE_PROTOCOL COMMUNICATIONS_NO_PROTOCOL
-
-#define USBTTY_INTERFACE_CLASS    0xFF /* Vendor Specific */
-#define USBTTY_INTERFACE_SUBCLASS  0x02
-#define USBTTY_INTERFACE_PROTOCOL  0x01
 
-#define USBTTY_BCD_DEVICE 0x0
-#define USBTTY_MAXPOWER          0x0
+#define USBTTY_BCD_DEVICE      0x00
+#define USBTTY_MAXPOWER                0x00
 
-#define STR_MANUFACTURER 1
-#define STR_PRODUCT     2
-#define STR_SERIAL      3
-#define STR_CONFIG      4
-#define STR_INTERFACE   5
+#define STR_LANG               0x00
+#define STR_MANUFACTURER       0x01
+#define STR_PRODUCT            0x02
+#define STR_SERIAL             0x03
+#define STR_CONFIG             0x04
+#define STR_DATA_INTERFACE     0x05
+#define STR_CTRL_INTERFACE     0x06
+#define STR_COUNT              0x07
 
 #endif
index ebda719..e8cb299 100644 (file)
@@ -592,9 +592,11 @@ typedef void               (*ExcpHndlr) (void) ;
 #define PMC_REG_BASE   __REG(0x40500400)  /* Primary Modem Codec */
 #define SMC_REG_BASE   __REG(0x40500500)  /* Secondary Modem Codec */
 
+
 /*
  * USB Device Controller
  */
+#ifndef CONFIG_CPU_MONAHANS
 #define UDC_RES1       __REG(0x40600004)  /* UDC Undocumented - Reserved1 */
 #define UDC_RES2       __REG(0x40600008)  /* UDC Undocumented - Reserved2 */
 #define UDC_RES3       __REG(0x4060000C)  /* UDC Undocumented - Reserved3 */
@@ -749,11 +751,28 @@ typedef void              (*ExcpHndlr) (void) ;
 #define USIR1_IR13     (1 << 5)        /* Interrup request ep 13 */
 #define USIR1_IR14     (1 << 6)        /* Interrup request ep 14 */
 #define USIR1_IR15     (1 << 7)        /* Interrup request ep 15 */
+#endif /* ! CONFIG_CPU_MONAHANS */
+
+#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS)
+
+/*
+ * USB Client Controller (incomplete)
+ */
+#define UDCCR          __REG(0x40600000)
+#define UDCICR0                __REG(0x40600004)
+#define UDCCIR0                __REG(0x40600008)
+#define UDCISR0                __REG(0x4060000c)
+#define UDCSIR1                __REG(0x40600010)
+#define UDCFNR         __REG(0x40600014)
+#define UDCOTGICR      __REG(0x40600018)
+#define UDCOTGISR      __REG(0x4060001c)
+#define UP2OCR         __REG(0x40600020)
+#define UP3OCR         __REG(0x40600024)
 
-#if defined(CONFIG_PXA27X)
 /*
  * USB Host Controller
  */
+#define OHCI_REGS_BASE 0x4C000000      /* required for ohci driver */
 #define UHCREV         __REG(0x4C000000)
 #define UHCHCON                __REG(0x4C000004)
 #define UHCCOMS                __REG(0x4C000008)
diff --git a/include/configs/AdderUSB.h b/include/configs/AdderUSB.h
new file mode 100644 (file)
index 0000000..a4f7f9a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 CodeHermit.
+ * Bryan O'Donoghue <bodonoghue@codehermit.ie>
+ *
+ * Provides support for USB console on the Analogue & Micro Adder87x
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 __ADDERUSB__
+#define __ADDERUSB__
+
+/* Include the board port */
+#include "Adder.h"
+
+#define CONFIG_USB_DEVICE              /* Include UDC driver */
+#define CONFIG_USB_TTY                 /* Bind the TTY driver to UDC */
+#define CFG_USB_EXTC_CLK 0x02          /* Oscillator on EXTC_CLK 2 */
+#define CFG_USB_BRG_CLK        0x04            /* or use Baud rate generator 0x04 */
+#define CFG_CONSOLE_IS_IN_ENV          /* Console is in env */
+
+/* If you have a USB-IF assigned VendorID then you may wish to define
+ * your own vendor specific values either in BoardName.h or directly in
+ * usbd_vendor_info.h
+ */
+
+/*
+#define CONFIG_USBD_MANUFACTURER       "CodeHermit.ie"
+#define CONFIG_USBD_PRODUCT_NAME       "Das U-Boot"
+#define CONFIG_USBD_VENDORID           0xFFFF
+#define CONFIG_USBD_PRODUCTID_GSERIAL  0xFFFF
+#define CONFIG_USBD_PRODUCTID_CDCACM   0xFFFE
+*/
+
+#endif /* __ADDERUSB_H__ */
index 73be069..4132f4a 100644 (file)
 
 /* USB */
 #if 1
-#define CONFIG_USB_OHCI
+#define CONFIG_USB_OHCI_NEW
 #define ADD_USB_CMD             CFG_CMD_USB | CFG_CMD_FAT
 #define CONFIG_USB_STORAGE
+
+#undef CFG_USB_OHCI_BOARD_INIT
+#define CFG_USB_OHCI_CPU_INIT
+#define CFG_USB_OHCI_REGS_BASE MPC5XXX_USB
+#define CFG_USB_OHCI_SLOT_NAME "mpc5200"
+#define CFG_USB_OHCI_MAX_ROOT_PORTS    15
+
 #else
 #define ADD_USB_CMD             0
 #endif
index 7935593..1854fdc 100644 (file)
 
 /* USB */
 #if defined(CONFIG_STK52XX) || defined(CONFIG_FO300)
-#define CONFIG_USB_OHCI
+#define CONFIG_USB_OHCI_NEW
 #define ADD_USB_CMD            CFG_CMD_USB | CFG_CMD_FAT
 #define CONFIG_USB_STORAGE
+
+#undef CFG_USB_OHCI_BOARD_INIT
+#define CFG_USB_OHCI_CPU_INIT
+#define CFG_USB_OHCI_REGS_BASE MPC5XXX_USB
+#define CFG_USB_OHCI_SLOT_NAME "mpc5200"
+#define CFG_USB_OHCI_MAX_ROOT_PORTS    15
+
 #else
 #define ADD_USB_CMD            0
 #endif
index 1568120..25ba06e 100644 (file)
 # define CONFIG_COMMANDS       ((CONFIG_CMD_DFL \
                                  | CFG_CMD_ENV \
                                  | CFG_CMD_NAND \
-                                 | CFG_CMD_I2C) \
+                                 | CFG_CMD_I2C \
+                                 | CFG_CMD_USB \
+                                 | CFG_CMD_FAT) \
                                 & ~(CFG_CMD_NET \
                                     | CFG_CMD_FLASH \
                                     | CFG_CMD_IMLS))
 #endif
 
+/* USB */
+#define CONFIG_USB_OHCI_NEW    1
+#define CONFIG_USB_STORAGE      1
+#define CONFIG_DOS_PARTITION    1
+
+#undef CFG_USB_OHCI_BOARD_INIT
+#define CFG_USB_OHCI_CPU_INIT  1
+#define CFG_USB_OHCI_REGS_BASE OHCI_REGS_BASE
+#define CFG_USB_OHCI_SLOT_NAME "delta"
+#define CFG_USB_OHCI_MAX_ROOT_PORTS    3
+
+#define LITTLEENDIAN            1       /* used by usb_ohci.c  */
 
 /* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */
 #include <cmd_confdefs.h>
index 04f1f24..2cf78a3 100644 (file)
 
 #undef CONFIG_MODEM_SUPPORT            /* disable modem initialization stuff */
 
-#define CONFIG_USB_OHCI                1
+#define CONFIG_USB_OHCI_NEW    1
 #define CONFIG_USB_KEYBOARD    1
 #define CONFIG_USB_STORAGE     1
 #define CONFIG_DOS_PARTITION   1
 #define CONFIG_AT91C_PQFP_UHPBUG 1
 
+#undef CFG_USB_OHCI_BOARD_INIT
+#define CFG_USB_OHCI_CPU_INIT          1
+#define CFG_USB_OHCI_REGS_BASE         AT91_USB_HOST_BASE
+#define CFG_USB_OHCI_SLOT_NAME         "at91rm9200"
+#define CFG_USB_OHCI_MAX_ROOT_PORTS    15
+
 #undef CONFIG_HARD_I2C
 
 #ifdef CONFIG_HARD_I2C
 #define CONFIG_COMMANDS                \
                       ((CONFIG_CMD_DFL | \
                        CFG_CMD_DATE    | \
-                       CFG_CMD_DHCP    | \
+                       CFG_CMD_DHCP    | \
                        CFG_CMD_EEPROM  | \
                        CFG_CMD_I2C     | \
                        CFG_CMD_NFS     | \
 #else
 #define CONFIG_COMMANDS                \
                       ((CONFIG_CMD_DFL | \
-                       CFG_CMD_DHCP    | \
+                       CFG_CMD_DHCP    | \
                        CFG_CMD_NFS     | \
                        CFG_CMD_SNTP    | \
                        CFG_CMD_USB      | \
 
 #define CONFIG_NR_DRAM_BANKS   1
 #define PHYS_SDRAM             0x20000000
-#define PHYS_SDRAM_SIZE                0x08000000      /* 128 megs */
+#define PHYS_SDRAM_SIZE                0x08000000      /* 128 megs */
 
 #define CFG_MEMTEST_START      PHYS_SDRAM
 #define CFG_MEMTEST_END                CFG_MEMTEST_START + PHYS_SDRAM_SIZE - 262144
index a2dc8e7..acf86d0 100644 (file)
 #define CFG_EEPROM_PAGE_WRITE_DELAY_MS 10
 
 /* USB stuff */
-#define CONFIG_USB_OHCI                1
+#define CONFIG_USB_OHCI_NEW    1
 #define CONFIG_USB_STORAGE     1
 #define CONFIG_DOS_PARTITION   1
 
+#undef CFG_USB_OHCI_BOARD_INIT
+#define CFG_USB_OHCI_CPU_INIT  1
+#define CFG_USB_OHCI_REGS_BASE S3C24X0_USB_HOST_BASE
+#define CFG_USB_OHCI_SLOT_NAME "s3c2400"
+#define CFG_USB_OHCI_MAX_ROOT_PORTS    15
+
 /*
  * Size of malloc() pool
  */
index b68ae54..0a41dff 100644 (file)
 
 #ifdef CONFIG_440EP
 /* USB */
-#define CONFIG_USB_OHCI
+#define CONFIG_USB_OHCI_NEW
 #define CONFIG_USB_STORAGE
 
+#undef CFG_USB_OHCI_BOARD_INIT
+#define CFG_USB_OHCI_CPU_INIT  1
+#define CFG_USB_OHCI_REGS_BASE (CFG_PERIPHERAL_BASE | 0x1000)
+#define CFG_USB_OHCI_SLOT_NAME "ppc440"
+#define CFG_USB_OHCI_MAX_ROOT_PORTS    15
+
 /* Comment this out to enable USB 1.1 device */
 #define USB_2_0_DEVICE
 
index 41108b9..3e3b202 100644 (file)
 #define SYS_CONTROL_A_HWRES_ENABLE             (1<<2)
 #define SYS_CONTROL_A_WDOG_ACTION              (1<<3)
 #define SYS_CONTROL_A_WATCHDOG                 (1<<7)
+
+#define MISC_CONTROLB_USB_INT_RISING           (1<<2)
+#define MISC_CONTROLB_SESSION_VALID_EN         (1<<3)
+
+#define USB_PUMP_USBVE                         (1<<0)
+#define USB_PUMP_USBVEP                                (1<<1)
+#define USB_PUMP_SRP_DETECT                    (1<<2)
+#define USB_PUMP_SESSION_VALID                 (1<<3)
+#define USB_PUMP_VBUS_VALID_4_0                        (1<<4)
+#define USB_PUMP_VBUS_VALID_4_4                        (1<<5)
+#define USB_PUMP_EN_USBVE                      (1<<6)
+#define USB_PUMP_EN_USBVEP                     (1<<7)
index bf71554..504ccc4 100644 (file)
@@ -169,7 +169,10 @@ struct usb_device {
  * this is how the lowlevel part communicate with the outer world
  */
 
-#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined (CONFIG_USB_SL811HS)
+#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \
+       defined(CONFIG_USB_OHCI_NEW) || defined (CONFIG_USB_SL811HS) || \
+       defined(CONFIG_USB_ISP116X_HCD)
+
 int usb_lowlevel_init(void);
 int usb_lowlevel_stop(void);
 int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len);
@@ -230,16 +233,12 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
 
 /* big endian -> little endian conversion */
 /* some CPUs are already little endian e.g. the ARM920T */
-#ifdef LITTLEENDIAN
-#define swap_16(x) ((unsigned short)(x))
-#define swap_32(x) ((unsigned long)(x))
-#else
-#define swap_16(x) \
+#define __swap_16(x) \
        ({ unsigned short x_ = (unsigned short)x; \
         (unsigned short)( \
                ((x_ & 0x00FFU) << 8) | ((x_ & 0xFF00U) >> 8) ); \
        })
-#define swap_32(x) \
+#define __swap_32(x) \
        ({ unsigned long x_ = (unsigned long)x; \
         (unsigned long)( \
                ((x_ & 0x000000FFUL) << 24) | \
@@ -247,6 +246,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
                ((x_ & 0x00FF0000UL) >>  8) | \
                ((x_ & 0xFF000000UL) >> 24) ); \
        })
+
+#ifdef LITTLEENDIAN
+# define swap_16(x) (x)
+# define swap_32(x) (x)
+#else
+# define swap_16(x) __swap_16(x)
+# define swap_32(x) __swap_32(x)
 #endif /* LITTLEENDIAN */
 
 /*
diff --git a/include/usb_cdc_acm.h b/include/usb_cdc_acm.h
new file mode 100644 (file)
index 0000000..87bf50c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, deckard@codehermit.ie, CodeHermit
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* ACM Control Requests */
+#define ACM_SEND_ENCAPSULATED_COMMAND  0x00
+#define ACM_GET_ENCAPSULATED_RESPONSE  0x01
+#define ACM_SET_COMM_FEATURE           0x02
+#define ACM_GET_COMM_FEATRUE           0x03
+#define ACM_CLEAR_COMM_FEATURE         0x04
+#define ACM_SET_LINE_ENCODING          0x20
+#define ACM_GET_LINE_ENCODING          0x21
+#define ACM_SET_CONTROL_LINE_STATE     0x22
+#define ACM_SEND_BREAK                 0x23
+
+/* ACM Notification Codes */
+#define ACM_NETWORK_CONNECTION         0x00
+#define ACM_RESPONSE_AVAILABLE         0x01
+#define ACM_SERIAL_STATE               0x20
+
+/* Format of response expected by a ACM_GET_LINE_ENCODING request */
+struct rs232_emu{
+               unsigned long dter;
+               unsigned char stop_bits;
+               unsigned char parity;
+               unsigned char data_bits;
+}__attribute__((packed));
index 6e92df1..cb2be72 100644 (file)
@@ -576,6 +576,9 @@ struct usb_device_instance {
 
        void (*event) (struct usb_device_instance *device, usb_device_event_t event, int data);
 
+       /* Do cdc device specific control requests */
+       int (*cdc_recv_setup)(struct usb_device_request *request, struct urb *urb);
+
        /* bus interface */
        struct usb_bus_instance *bus;   /* which bus interface driver */
 
diff --git a/include/usbdcore_mpc8xx.h b/include/usbdcore_mpc8xx.h
new file mode 100644 (file)
index 0000000..9df62f4
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2006 Bryan O'Donoghue, CodeHermit
+ * bodonoghue@codehermit.ie
+ *
+ *
+ * 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 <commproc.h>
+
+/* Mode Register */
+#define USMOD_EN       0x01
+#define USMOD_HOST     0x02
+#define USMOD_TEST     0x04
+#define USMOD_SFTE     0x08
+#define USMOD_RESUME   0x40
+#define USMOD_LSS      0x80
+
+/* Endpoint Registers */
+#define USEP_RHS_NORM  0x00
+#define USEP_RHS_IGNORE        0x01
+#define USEP_RHS_NAK   0x02
+#define USEP_RHS_STALL 0x03
+
+#define USEP_THS_NORM  0x00
+#define USEP_THS_IGNORE        0x04
+#define USEP_THS_NAK   0x08
+#define USEP_THS_STALL 0x0C
+
+#define USEP_RTE       0x10
+#define USEP_MF                0x20
+
+#define USEP_TM_CONTROL        0x00
+#define USEP_TM_INT    0x100
+#define USEP_TM_BULK   0x200
+#define USEP_TM_ISO    0x300
+
+/* Command Register */
+#define USCOM_EP0      0x00
+#define USCOM_EP1      0x01
+#define USCOM_EP2      0x02
+#define USCOM_EP3      0x03
+
+#define USCOM_FLUSH    0x40
+#define USCOM_STR      0x80
+
+/* Event Register */
+#define USB_E_RXB      0x0001
+#define USB_E_TXB      0x0002
+#define USB_E_BSY      0x0004
+#define USB_E_SOF      0x0008
+#define USB_E_TXE1     0x0010
+#define USB_E_TXE2     0x0020
+#define USB_E_TXE3     0x0040
+#define USB_E_TXE4     0x0080
+#define USB_TX_ERRMASK (USB_E_TXE1|USB_E_TXE2|USB_E_TXE3|USB_E_TXE4)
+#define USB_E_IDLE     0x0100
+#define USB_E_RESET    0x0200
+
+/* Mask Register */
+#define USBS_IDLE      0x01
+
+/* RX Buffer Descriptor */
+#define RX_BD_OV       0x02
+#define RX_BD_CR       0x04
+#define RX_BD_AB       0x08
+#define RX_BD_NO       0x10
+#define RX_BD_PID_DATA0        0x00
+#define RX_BD_PID_DATA1        0x40
+#define RX_BD_PID_SETUP        0x80
+#define RX_BD_F                0x400
+#define RX_BD_L                0x800
+#define RX_BD_I                0x1000
+#define RX_BD_W                0x2000
+#define RX_BD_E                0x8000
+
+/* Useful masks */
+#define RX_BD_PID_BITMASK (RX_BD_PID_DATA1 | RX_BD_PID_SETUP)
+#define STALL_BITMASK (USEP_THS_STALL | USEP_RHS_STALL)
+#define NAK_BITMASK (USEP_THS_NAK | USEP_RHS_NAK)
+#define CBD_TX_BITMASK (TX_BD_R | TX_BD_L | TX_BD_TC | TX_BD_I | TX_BD_CNF)
+
+/* TX Buffer Descriptor */
+#define TX_BD_UN       0x02
+#define TX_BD_TO       0x04
+#define TX_BD_NO_PID   0x00
+#define TX_BD_PID_DATA0        0x80
+#define TX_BD_PID_DATA1        0xC0
+#define TX_BD_CNF      0x200
+#define TX_BD_TC       0x400
+#define TX_BD_L                0x800
+#define TX_BD_I                0x1000
+#define TX_BD_W                0x2000
+#define TX_BD_R                0x8000
+
+/* Implementation specific defines */
+
+#define EP_MIN_PACKET_SIZE 0x08
+#define MAX_ENDPOINTS  0x04
+#define FIFO_SIZE      0x10
+#define EP_MAX_PKT     FIFO_SIZE
+#define TX_RING_SIZE   0x04
+#define RX_RING_SIZE   0x06
+#define USB_MAX_PKT    0x40
+#define TOGGLE_TX_PID(x) x= ((~x)&0x40)|0x80
+#define TOGGLE_RX_PID(x) x^= 0x40
+#define EP_ATTACHED    0x01    /* Endpoint has a urb attached or not */
+#define EP_SEND_ZLP    0x02    /* Send ZLP y/n ? */
+
+#define PROFF_USB      0x00000000
+#define CPM_USB_BASE   0x00000A00
+
+/* UDC device defines */
+#define EP0_MAX_PACKET_SIZE    EP_MAX_PKT
+#define UDC_OUT_ENDPOINT       0x02
+#define UDC_OUT_PACKET_SIZE    EP_MIN_PACKET_SIZE
+#define UDC_IN_ENDPOINT                0x03
+#define UDC_IN_PACKET_SIZE     EP_MIN_PACKET_SIZE
+#define UDC_INT_ENDPOINT       0x01
+#define UDC_INT_PACKET_SIZE    UDC_IN_PACKET_SIZE
+#define UDC_BULK_PACKET_SIZE   EP_MIN_PACKET_SIZE
+
+struct mpc8xx_ep {
+       struct urb * urb;
+       unsigned char pid;
+       unsigned char sc;
+       volatile cbd_t * prx;
+};
+
+typedef struct mpc8xx_usb{
+       char usmod;     /* Mode Register */
+       char usaddr;    /* Slave Address Register */
+       char uscom;     /* Command Register */
+       char res1;      /* Reserved */
+       ushort usep[4];
+       ulong res2;     /* Reserved */
+       ushort usber;   /* Event Register */
+       ushort res3;    /* Reserved */
+       ushort usbmr;   /* Mask Register */
+       char res4;      /* Reserved */
+       char usbs;      /* Status Register */
+       char res5[8];   /* Reserved */
+}usb_t;
+
+typedef struct mpc8xx_parameter_ram{
+       ushort ep0ptr;  /* Endpoint Pointer Register 0 */
+       ushort ep1ptr;  /* Endpoint Pointer Register 1 */
+       ushort ep2ptr;  /* Endpoint Pointer Register 2 */
+       ushort ep3ptr;  /* Endpoint Pointer Register 3 */
+       uint rstate;    /* Receive state */
+       uint rptr;      /* Receive internal data pointer */
+       ushort frame_n; /* Frame number */
+       ushort rbcnt;   /* Receive byte count */
+       uint rtemp;     /* Receive temp cp use only */
+       uint rxusb;     /* Rx Data Temp */
+       ushort rxuptr;  /* Rx microcode return address temp */
+}usb_pram_t;
+
+typedef struct endpoint_parameter_block_pointer{
+       ushort rbase;   /* RxBD base address */
+       ushort tbase;   /* TxBD base address */
+       char rfcr;      /* Rx Function code */
+       char tfcr;      /* Tx Function code */
+       ushort mrblr;   /* Maximum Receive Buffer Length */
+       ushort rbptr;   /* RxBD pointer Next Buffer Descriptor */
+       ushort tbptr;   /* TxBD pointer Next Buffer Descriptor  */
+       ulong tstate;   /* Transmit internal state */
+       ulong tptr;     /* Transmit internal data pointer */
+       ushort tcrc;    /* Transmit temp CRC */
+       ushort tbcnt;   /* Transmit internal bye count */
+       ulong ttemp;    /* Tx temp */
+       ushort txuptr;  /* Tx microcode return address */
+       ushort res1;    /* Reserved */
+}usb_epb_t;
+
+typedef enum mpc8xx_udc_state{
+       STATE_NOT_READY,
+       STATE_ERROR,
+       STATE_READY,
+}mpc8xx_udc_state_t;
+
+/* Declarations */
+int udc_init(void);
+void udc_irq(void);
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint);
+void udc_setup_ep(struct usb_device_instance *device, unsigned int ep,
+                 struct usb_endpoint_instance *endpoint);
+void udc_connect(void);
+void udc_disconnect(void);
+void udc_enable(struct usb_device_instance *device);
+void udc_disable(void);
+void udc_startup_events(struct usb_device_instance *device);
+
+/* Flow control */
+void udc_set_nak(int epid);
+void udc_unset_nak (int epid);
index 6ea3331..526fcd9 100644 (file)
 #define UDC_VBUS_CTRL      (1 << 19)
 #define UDC_VBUS_MODE      (1 << 18)
 
-
-void omap1510_udc_irq(void);
-void omap1510_udc_noniso_irq(void);
-
+/* OMAP Endpoint parameters */
+#define EP0_MAX_PACKET_SIZE 64
+#define UDC_OUT_ENDPOINT 2
+#define UDC_OUT_PACKET_SIZE 64
+#define UDC_IN_ENDPOINT        1
+#define UDC_IN_PACKET_SIZE 64
+#define UDC_INT_ENDPOINT 5
+#define UDC_INT_PKTSIZE        16
+#define UDC_BULK_PKTSIZE 16
+
+void udc_irq (void);
+/* Flow control */
+void udc_set_nak(int epid);
+void udc_unset_nak (int epid);
 
 /* Higher level functions for abstracting away from specific device */
 void udc_endpoint_write(struct usb_endpoint_instance *endpoint);
index 2d9f739..a752097 100644 (file)
 #define COMMUNICATIONS_DEVICE_CLASS    0x02
 
 /* c.f. CDC 4.2 Table 15 */
-#define COMMUNICATIONS_INTERFACE_CLASS 0x02
+#define COMMUNICATIONS_INTERFACE_CLASS_CONTROL 0x02
+#define COMMUNICATIONS_INTERFACE_CLASS_DATA            0x0A
+#define COMMUNICATIONS_INTERFACE_CLASS_VENDOR  0x0FF
 
 /* c.f. CDC 4.3 Table 16 */
-#define COMMUNICATIONS_NO_SUBCLASS     0x00
+#define COMMUNICATIONS_NO_SUBCLASS             0x00
 #define COMMUNICATIONS_DLCM_SUBCLASS   0x01
-#define COMMUNICATIONS_ACM_SUBCLASS    0x02
-#define COMMUNICATIONS_TCM_SUBCLASS    0x03
+#define COMMUNICATIONS_ACM_SUBCLASS            0x02
+#define COMMUNICATIONS_TCM_SUBCLASS            0x03
 #define COMMUNICATIONS_MCCM_SUBCLASS   0x04
-#define COMMUNICATIONS_CCM_SUBCLASS    0x05
+#define COMMUNICATIONS_CCM_SUBCLASS            0x05
 #define COMMUNICATIONS_ENCM_SUBCLASS   0x06
 #define COMMUNICATIONS_ANCM_SUBCLASS   0x07
 
 /* c.f. WMCD 5.1 */
 #define COMMUNICATIONS_WHCM_SUBCLASS   0x08
-#define COMMUNICATIONS_DMM_SUBCLASS    0x09
+#define COMMUNICATIONS_DMM_SUBCLASS            0x09
 #define COMMUNICATIONS_MDLM_SUBCLASS   0x0a
 #define COMMUNICATIONS_OBEX_SUBCLASS   0x0b
 
-/* c.f. CDC 4.6 Table 18 */
+/* c.f. CDC 4.4 Table 17 */
+#define COMMUNICATIONS_NO_PROTOCOL             0x00
+#define COMMUNICATIONS_V25TER_PROTOCOL 0x01    /*Common AT Hayes compatible*/
+
+/* c.f. CDC 4.5 Table 18 */
 #define DATA_INTERFACE_CLASS           0x0a
 
+/* c.f. CDC 4.6 No Table */
+#define DATA_INTERFACE_SUBCLASS_NONE   0x00    /* No subclass pertinent */
+
 /* c.f. CDC 4.7 Table 19 */
-#define COMMUNICATIONS_NO_PROTOCOL     0x00
+#define DATA_INTERFACE_PROTOCOL_NONE   0x00    /* No class protcol required */
 
 
 /* c.f. CDC 5.2.3 Table 24 */
-#define CS_INTERFACE                   0x24
+#define CS_INTERFACE           0x24
 #define CS_ENDPOINT                    0x25
 
 /*
  * c.f. WMCD 5.3 Table 5.3
  */
 
-#define USB_ST_HEADER                  0x00
+#define USB_ST_HEADER          0x00
 #define USB_ST_CMF                     0x01
 #define USB_ST_ACMF                    0x02
 #define USB_ST_DLMF                    0x03
 #define USB_ST_UF                      0x06
 #define USB_ST_CSF                     0x07
 #define USB_ST_TOMF                    0x08
-#define USB_ST_USBTF                   0x09
+#define USB_ST_USBTF           0x09
 #define USB_ST_NCT                     0x0a
 #define USB_ST_PUF                     0x0b
 #define USB_ST_EUF                     0x0c
 #define USB_ST_MCMF                    0x0d
 #define USB_ST_CCMF                    0x0e
 #define USB_ST_ENF                     0x0f
-#define USB_ST_ATMNF                   0x10
+#define USB_ST_ATMNF           0x10
 
 #define USB_ST_WHCM                    0x11
 #define USB_ST_MDLM                    0x12
-#define USB_ST_MDLMD                   0x13
+#define USB_ST_MDLMD           0x13
 #define USB_ST_DMM                     0x14
 #define USB_ST_OBEX                    0x15
 #define USB_ST_CS                      0x16
@@ -312,7 +321,8 @@ struct usb_class_union_function_descriptor {
        u8 bDescriptorType;
        u8 bDescriptorSubtype;  /* 0x06 */
        u8 bMasterInterface;
-       u8 bSlaveInterface0[0];
+       /* u8 bSlaveInterface0[0]; */
+       u8 bSlaveInterface0;
 } __attribute__ ((packed));
 
 struct usb_class_country_selection_descriptor {