diff options
author | Markus Klotzbuecher <mk@denx.de> | 2007-03-23 09:52:17 +0100 |
---|---|---|
committer | Markus Klotzbuecher <mk@pollux.denx.de> | 2007-03-23 09:52:17 +0100 |
commit | f2b07ebd32e42a5b1126c98efc768ddb8908de62 (patch) | |
tree | e2a374a15d0abf6872fbbdeecf8e34855e56d3c9 | |
parent | 44ba464b99001f8bd1c456a1e9d59726252f707a (diff) | |
parent | 83dc830b1693252d996bda920cd5f3161d7c64a9 (diff) |
Merge with git://www.denx.de/git/u-boot.git#testing-USB
37 files changed, 3044 insertions, 424 deletions
diff --git a/CHANGELOG b/CHANGELOG index 284ba76d898..3bc119fb7d3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2443,9 +2443,27 @@ Date: Sat Oct 28 02:29:14 2006 +0200 Enable commandline editing and hush shell on all TQM boards. +<<<<<<< master +* 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 +======= commit 8078f1a5f63a739b8533478f6c2b62fb1e2f79d7 Author: Wolfgang Denk <wd@pollux.denx.de> Date: Sat Oct 28 02:28:02 2006 +0200 +>>>>>>> trunk README says CFG_AUTO_COMPLETE, but ocde uses CONFIG_AUTO_COMPLETE @@ -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- @@ -636,6 +636,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 \ @@ -883,6 +883,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 diff --git a/board/delta/delta.c b/board/delta/delta.c index b127ac8cab7..6e227748b01 100644 --- a/board/delta/delta.c +++ b/board/delta/delta.c @@ -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 */ diff --git a/board/trab/auto_update.c b/board/trab/auto_update.c index f4074aecb11..6f903d2b037 100644 --- a/board/trab/auto_update.c +++ b/board/trab/auto_update.c @@ -34,7 +34,7 @@ #ifdef CONFIG_AUTO_UPDATE -#ifndef CONFIG_USB_OHCI +#ifndef CONFIG_USB_OHCI_NEW #error "must define CONFIG_USB_OHCI" #endif diff --git a/cpu/arm920t/at91rm9200/Makefile b/cpu/arm920t/at91rm9200/Makefile index 8d4e478fb5e..eaabad26a0c 100644 --- a/cpu/arm920t/at91rm9200/Makefile +++ b/cpu/arm920t/at91rm9200/Makefile @@ -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 index 00000000000..366262e4ccd --- /dev/null +++ b/cpu/arm920t/at91rm9200/usb.c @@ -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) */ diff --git a/cpu/arm920t/s3c24x0/Makefile b/cpu/arm920t/s3c24x0/Makefile index 3a7c4b35fd8..0ff36c596a3 100644 --- a/cpu/arm920t/s3c24x0/Makefile +++ b/cpu/arm920t/s3c24x0/Makefile @@ -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 index 00000000000..ef5d5bf71b1 --- /dev/null +++ b/cpu/arm920t/s3c24x0/usb.c @@ -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) */ diff --git a/cpu/mpc5xxx/Makefile b/cpu/mpc5xxx/Makefile index 235adb7c04e..312b0bfc6cc 100644 --- a/cpu/mpc5xxx/Makefile +++ b/cpu/mpc5xxx/Makefile @@ -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)) diff --git a/cpu/ppc4xx/Makefile b/cpu/ppc4xx/Makefile index 96f0f62eb60..1347e0f9f09 100644 --- a/cpu/ppc4xx/Makefile +++ b/cpu/ppc4xx/Makefile @@ -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 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) diff --git a/cpu/pxa/Makefile b/cpu/pxa/Makefile index cded7ffd35d..8b4367e2051 100644 --- a/cpu/pxa/Makefile +++ b/cpu/pxa/Makefile @@ -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 index 00000000000..5d273cb3518 --- /dev/null +++ b/cpu/pxa/usb.c @@ -0,0 +1,78 @@ +/* + * (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) +# ifdef CONFIG_CPU_MONAHANS + +#include <asm/arch/pxa-regs.h> + +int usb_cpu_init() +{ + /* Enable USB host clock. */ + CKENA |= (CKENA_2_USBHOST | CKENA_20_UDC); + udelay(100); + + /* Configure Port 2 for Host (USB Client Registers) */ + UP2OCR = 0x3000c; + +#if 0 + GPIO2_2 = 0x801; /* USBHPEN - Alt. Fkt. 1 */ + GPIO3_2 = 0x801; /* USBHPWR - Alt. Fkt. 1 */ +#endif + + UHCHR |= UHCHR_FHR; + wait_ms(11); + UHCHR &= ~UHCHR_FHR; + + UHCHR |= UHCHR_FSBIR; + while (UHCHR & UHCHR_FSBIR) + udelay(1); + +#if 0 + UHCHR |= UHCHR_PCPL; /* USBHPEN is active low */ + UHCHR |= UHCHR_PSPL; /* USBHPWR is active low */ +#endif + + UHCHR &= ~UHCHR_SSEP0; + UHCHR &= ~UHCHR_SSEP1; + UHCHR &= ~UHCHR_SSE; + + return 0; +} + +int usb_cpu_stop() +{ + /* may not want to do this */ + /* CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); */ + return 0; +} + +int usb_cpu_init_fail() +{ + return 0; +} + +# endif /* CONFIG_CPU_MONAHANS */ +#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 index 00000000000..017c69ab079 --- /dev/null +++ b/doc/README.generic_usb_ohci @@ -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. + + + + diff --git a/drivers/Makefile b/drivers/Makefile index fffc22a5e1d..0ca400c6821 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -46,7 +46,9 @@ COBJS = 3c589.o 5701rls.o ali512x.o atmel_usart.o \ sl811_usb.o sm501.o smc91111.o smiLynxEM.o \ status_led.o sym53c8xx.o systemace.o ahci.o \ ti_pci1410a.o tigon3.o tsec.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/cpu/arm920t/at91rm9200/usb_ohci.c b/drivers/usb_ohci.c index 5b2c56cffc4..c5e4c38efdf 100644 --- a/cpu/arm920t/at91rm9200/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -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 @@ -43,16 +43,39 @@ #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 */ +#define S3C24X0_merge + +#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 \ @@ -77,8 +100,13 @@ #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 +123,12 @@ int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; +#ifdef S3C24X0_merge +/* flag guarding URB transation */ +int urb_finished = 0; +#endif + + /*-------------------------------------------------------------------------*/ /* AMD-756 (D2 rev) reports corrupt register contents in some cases. @@ -399,6 +433,17 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, err("sohci_submit_job: EPIPE"); return -1; } +#ifdef S3C24X0_merge + /* 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; +#endif /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (dev, pipe))) { @@ -488,7 +533,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 +549,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 +570,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 +618,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 +645,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 +703,15 @@ 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->hwNextTD = m32_swap ((unsigned long)td_pt); +#ifndef S3C24X0_merge td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); +#endif /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -789,6 +840,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; @@ -827,7 +881,23 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) dbg("ConditionCode %#x", cc); stat = cc_to_error[cc]; } - +#ifdef S3C24X0_merge + /* 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))) +#endif + 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 +1044,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 +1083,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 +1105,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 +1131,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) { @@ -1170,7 +1260,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 +1330,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,18 +1350,39 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, stat = USB_ST_CRC_ERR; break; } + +#ifdef S3C24X0_merge + /* 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)) { +#else if (stat >= 0 && stat != 0xff) { +#endif /* 0xff is returned for an SF-interrupt */ break; } + if (--timeout) { wait_ms(1); } else { err("CTL:TIMEOUT "); +#ifdef S3C24X0_merge + dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; + urb_finished = 1; +#endif stat = USB_ST_CRC_ERR; break; } } +#ifndef S3C24X0_merge /* we got an Root Hub Status Change interrupt */ if (got_rhsc) { #ifdef DEBUG @@ -1291,6 +1404,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, devgone = dev; } } +#endif /* S3C24X0_merge */ dev->status = stat; dev->act_len = transfer_len; @@ -1381,7 +1495,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 +1575,40 @@ 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; +#ifdef S3C24X0_merge + + if ((ohci->hcca->done_head != 0) && + !(m32_swap (ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } +#else if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; } else { ints = readl (®s->intrstatus); } - +#endif /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ if (ints & OHCI_INTR_RHSC) { got_rhsc = 1; +#ifdef S3C24X0_merge + stat = 0xff; +#endif } if (ints & OHCI_INTR_UE) { @@ -1549,12 +1680,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 +1719,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 +1762,9 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #else wait_ms(1); +# ifdef S3C24X0_merge + urb_finished = 1; +# endif #endif ohci_inited = 1; return 0; @@ -1626,10 +1779,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 */ diff --git a/cpu/arm920t/at91rm9200/usb_ohci.h b/drivers/usb_ohci.h index ecb4e937b18..95fbc446549 100644 --- a/cpu/arm920t/at91rm9200/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -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))); diff --git a/drivers/usbdcore_ep0.c b/drivers/usbdcore_ep0.c index 260befe9786..1e44f322a79 100644 --- a/drivers/usbdcore_ep0.c +++ b/drivers/usbdcore_ep0.c @@ -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 * @@ -39,11 +42,17 @@ * 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 index 00000000000..e87284b1787 --- /dev/null +++ b/drivers/usbdcore_mpc8xx.c @@ -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) */ diff --git a/drivers/usbdcore_omap1510.c b/drivers/usbdcore_omap1510.c index 1d54a635755..84bb936d869 100644 --- a/drivers/usbdcore_omap1510.c +++ b/drivers/usbdcore_omap1510.c @@ -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 diff --git a/drivers/usbtty.c b/drivers/usbtty.c index ce4a12e16e0..d41a00b8cf4 100644 --- a/drivers/usbtty.c +++ b/drivers/usbtty.c @@ -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 @@ -25,20 +28,42 @@ #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 diff --git a/drivers/usbtty.h b/drivers/usbtty.h index 79c2fe57d7a..8154e3072ef 100644 --- a/drivers/usbtty.h +++ b/drivers/usbtty.h @@ -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 @@ -21,44 +24,47 @@ #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 diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h index ebda7192ed0..e8cb29903be 100644 --- a/include/asm-arm/arch-pxa/pxa-regs.h +++ b/include/asm-arm/arch-pxa/pxa-regs.h @@ -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 index 00000000000..a4f7f9a28fb --- /dev/null +++ b/include/configs/AdderUSB.h @@ -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__ */ diff --git a/include/configs/IceCube.h b/include/configs/IceCube.h index 0d3825413ea..c380c545922 100644 --- a/include/configs/IceCube.h +++ b/include/configs/IceCube.h @@ -94,9 +94,16 @@ /* 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 diff --git a/include/configs/TQM5200.h b/include/configs/TQM5200.h index 7069b35ad60..5b011e82a36 100644 --- a/include/configs/TQM5200.h +++ b/include/configs/TQM5200.h @@ -146,9 +146,16 @@ /* 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 diff --git a/include/configs/delta.h b/include/configs/delta.h index 91284fdace8..4038f219602 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -94,12 +94,26 @@ # 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> diff --git a/include/configs/mp2usb.h b/include/configs/mp2usb.h index 04f1f240808..2cf78a3b150 100644 --- a/include/configs/mp2usb.h +++ b/include/configs/mp2usb.h @@ -101,12 +101,18 @@ #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 @@ -127,7 +133,7 @@ #define CONFIG_COMMANDS \ ((CONFIG_CMD_DFL | \ CFG_CMD_DATE | \ - CFG_CMD_DHCP | \ + CFG_CMD_DHCP | \ CFG_CMD_EEPROM | \ CFG_CMD_I2C | \ CFG_CMD_NFS | \ @@ -136,7 +142,7 @@ #else #define CONFIG_COMMANDS \ ((CONFIG_CMD_DFL | \ - CFG_CMD_DHCP | \ + CFG_CMD_DHCP | \ CFG_CMD_NFS | \ CFG_CMD_SNTP | \ CFG_CMD_USB | \ @@ -156,7 +162,7 @@ #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 diff --git a/include/configs/trab.h b/include/configs/trab.h index a2dc8e7bff8..acf86d08ad2 100644 --- a/include/configs/trab.h +++ b/include/configs/trab.h @@ -80,10 +80,16 @@ #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 */ diff --git a/include/configs/yosemite.h b/include/configs/yosemite.h index 818462eedee..b59f75b9d0f 100644 --- a/include/configs/yosemite.h +++ b/include/configs/yosemite.h @@ -233,9 +233,15 @@ #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 diff --git a/include/da9030.h b/include/da9030.h index 41108b9b36f..3e3b2024156 100644 --- a/include/da9030.h +++ b/include/da9030.h @@ -104,3 +104,15 @@ #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) diff --git a/include/usb.h b/include/usb.h index bf715540417..419a7e364a5 100644 --- a/include/usb.h +++ b/include/usb.h @@ -169,7 +169,7 @@ 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_SL811HS) || defined(CONFIG_USB_OHCI_NEW) 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 +230,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 +243,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 index 00000000000..87bf50c52ba --- /dev/null +++ b/include/usb_cdc_acm.h @@ -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)); diff --git a/include/usbdcore.h b/include/usbdcore.h index 6e92df13bd0..cb2be72804a 100644 --- a/include/usbdcore.h +++ b/include/usbdcore.h @@ -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 index 00000000000..9df62f4ca51 --- /dev/null +++ b/include/usbdcore_mpc8xx.h @@ -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); diff --git a/include/usbdcore_omap1510.h b/include/usbdcore_omap1510.h index 6ea333122fd..526fcd920db 100644 --- a/include/usbdcore_omap1510.h +++ b/include/usbdcore_omap1510.h @@ -161,10 +161,20 @@ #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); diff --git a/include/usbdescriptors.h b/include/usbdescriptors.h index 2d9f739343e..a752097e5bc 100644 --- a/include/usbdescriptors.h +++ b/include/usbdescriptors.h @@ -92,33 +92,42 @@ #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 /* @@ -128,7 +137,7 @@ * 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 @@ -137,18 +146,18 @@ #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 { |