From 2731b9a86685190d26b1883f27afda5ac8e1a313 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 3 Apr 2009 12:46:58 +0200 Subject: drivers/usb: regorganisation move to linux usb driver organisation as following drivers/usb/gadget drivers/usb/host drivers/usb/musb Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Remy Bohmer diff --git a/Makefile b/Makefile index 49b83c0..e91c051 100644 --- a/Makefile +++ b/Makefile @@ -263,7 +263,9 @@ endif LIBS += drivers/rtc/librtc.a LIBS += drivers/serial/libserial.a LIBS += drivers/twserial/libtws.a -LIBS += drivers/usb/libusb.a +LIBS += drivers/usb/gadget/libusb_gadget.a +LIBS += drivers/usb/host/libusb_host.a +LIBS += drivers/usb/musb/libusb_musb.a LIBS += drivers/video/libvideo.a LIBS += drivers/watchdog/libwatchdog.a LIBS += common/libcommon.a diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index d8b1387..f711d0b 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -94,7 +94,7 @@ static char serial_number[16]; * Descriptors, Strings, Local variables. */ -/* defined and used by usbdcore_ep0.c */ +/* defined and used by gadget/ep0.c */ extern struct usb_string_descriptor **usb_strings; /* Indicies, References */ diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index ecefde5..77db972 100644 --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -24,11 +24,11 @@ #ifndef __USB_TTY_H__ #define __USB_TTY_H__ -#include +#include #if defined(CONFIG_PPC) -#include +#include #elif defined(CONFIG_ARM) -#include +#include #endif #include diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile deleted file mode 100644 index 5523cbd..0000000 --- a/drivers/usb/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -# -# (C) Copyright 2000-2007 -# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk - -LIB := $(obj)libusb.a - -# core -COBJS-y += usbdcore.o -COBJS-$(CONFIG_USB_OHCI_NEW) += usb_ohci.o -COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o - -# host -COBJS-$(CONFIG_USB_ATMEL) += atmel_usb.o -COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o -COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o -COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx_usb.o -COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o -COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o -COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o -COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o -COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o -COBJS-$(CONFIG_USB_DAVINCI) += davinci_usb.o -COBJS-$(CONFIG_USB_EHCI_VCT) += usb_ehci_vct.o - -# device -ifdef CONFIG_USB_DEVICE -COBJS-y += usbdcore_ep0.o -COBJS-$(CONFIG_OMAP1510) += usbdcore_omap1510.o -COBJS-$(CONFIG_OMAP1610) += usbdcore_omap1510.o -COBJS-$(CONFIG_MPC885_FAMILY) += usbdcore_mpc8xx.o -endif - -COBJS := $(COBJS-y) -SRCS := $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) - -all: $(LIB) - -$(LIB): $(obj).depend $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - -######################################################################### - -# defines $(obj).depend target -include $(SRCTREE)/rules.mk - -sinclude $(obj).depend - -######################################################################### diff --git a/drivers/usb/atmel_usb.c b/drivers/usb/atmel_usb.c deleted file mode 100644 index 7c44ad0..0000000 --- a/drivers/usb/atmel_usb.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * (C) Copyright 2006 - * DENX Software Engineering - * - * 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 - -#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) - -#include -#include -#include - -int usb_cpu_init(void) -{ - -#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \ - defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) - /* Enable PLLB */ - at91_sys_write(AT91_CKGR_PLLBR, CONFIG_SYS_AT91_PLLB); - while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB) - ; -#endif - - /* Enable USB host clock. */ - at91_sys_write(AT91_PMC_PCER, 1 << AT91_ID_UHP); -#ifdef CONFIG_AT91SAM9261 - at91_sys_write(AT91_PMC_SCER, AT91_PMC_UHP | AT91_PMC_HCK0); -#else - at91_sys_write(AT91_PMC_SCER, AT91_PMC_UHP); -#endif - - return 0; -} - -int usb_cpu_stop(void) -{ - /* Disable USB host clock. */ - at91_sys_write(AT91_PMC_PCDR, 1 << AT91_ID_UHP); -#ifdef CONFIG_AT91SAM9261 - at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP | AT91_PMC_HCK0); -#else - at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP); -#endif - -#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \ - defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) - /* Disable PLLB */ - at91_sys_write(AT91_CKGR_PLLBR, 0); - while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != 0) - ; -#endif - - return 0; -} - -int usb_cpu_init_fail(void) -{ - return usb_cpu_stop(); -} - -#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/drivers/usb/davinci_usb.c b/drivers/usb/davinci_usb.c deleted file mode 100644 index e66f660..0000000 --- a/drivers/usb/davinci_usb.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * TI's Davinci platform specific USB wrapper functions. - * - * Copyright (c) 2008 Texas Instruments - * - * 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 - * - * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments - */ - -#include -#include -#include "davinci_usb.h" - -/* MUSB platform configuration */ -struct musb_config musb_cfg = { - (struct musb_regs *)MENTOR_USB0_BASE, - DAVINCI_USB_TIMEOUT, - 0 -}; - -/* MUSB module register overlay */ -struct davinci_usb_regs *dregs; - -/* - * Enable the USB phy - */ -static u8 phy_on(void) -{ - u32 timeout; - - /* Wait until the USB phy is turned on */ - writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); - timeout = musb_cfg.timeout; - while (timeout--) - if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD) - return 1; - - /* USB phy was not turned on */ - return 0; -} - -/* - * Disable the USB phy - */ -static void phy_off(void) -{ - /* powerdown the on-chip PHY and its oscillator */ - writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR); -} - -/* - * This function performs Davinci platform specific initialization for usb0. - */ -int musb_platform_init(void) -{ - u32 revision; - - /* enable USB VBUS */ - enable_vbus(); - - /* start the on-chip USB phy and its pll */ - if (!phy_on()) - return -1; - - /* reset the controller */ - dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE; - writel(1, &dregs->ctrlr); - udelay(5000); - - /* Returns zero if e.g. not clocked */ - revision = readl(&dregs->version); - if (!revision) - return -1; - - /* Disable all interrupts */ - writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK | - DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr); - return 0; -} - -/* - * This function performs Davinci platform specific deinitialization for usb0. - */ -void musb_platform_deinit(void) -{ - /* Turn of the phy */ - phy_off(); - - /* flush any interrupts */ - writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK | - DAVINCI_USB_RXINT_MASK , &dregs->intclrr); -} diff --git a/drivers/usb/davinci_usb.h b/drivers/usb/davinci_usb.h deleted file mode 100644 index f6751bf..0000000 --- a/drivers/usb/davinci_usb.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * TI's Davinci platform specific USB wrapper functions. - * - * Copyright (c) 2008 Texas Instruments - * - * 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 - * - * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments - */ - -#ifndef __DAVINCI_USB_H__ -#define __DAVINCI_USB_H__ - -#include -#include "musb_core.h" - -/* Base address of DAVINCI usb0 wrapper */ -#define DAVINCI_USB0_BASE 0x01C64000 - -/* Base address of DAVINCI musb core */ -#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400) - -/* - * Davinci platform USB wrapper register overlay. Note: Only the required - * registers are included in this structure. It can be expanded as required. - */ -struct davinci_usb_regs { - u32 version; - u32 ctrlr; - u32 reserved[0x20]; - u32 intclrr; - u32 intmskr; - u32 intmsksetr; -}; - -#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ -#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ -#define DAVINCI_USB_USBINT_SHIFT 16 -#define DAVINCI_USB_TXINT_SHIFT 0 -#define DAVINCI_USB_RXINT_SHIFT 8 -#define DAVINCI_INTR_DRVVBUS 0x0100 - -#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ -#define DAVINCI_USB_TXINT_MASK \ - (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) -#define DAVINCI_USB_RXINT_MASK \ - (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) -#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ - (0x80 + (8*(_bEnd)) + (_bOffset)) - -/* Integrated highspeed/otg PHY */ -#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) -#define USBPHY_PHYCLKGD (1 << 8) -#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ -#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ -#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ -#define USBPHY_CLKO1SEL (1 << 3) -#define USBPHY_OSCPDWN (1 << 2) -#define USBPHY_PHYPDWN (1 << 0) - -/* Timeout for Davinci USB module */ -#define DAVINCI_USB_TIMEOUT 0x3FFFFFF - -/* IO Expander I2C address and VBUS enable mask */ -#define IOEXP_I2C_ADDR 0x3A -#define IOEXP_VBUSEN_MASK 1 - -/* extern functions */ -extern void lpsc_on(unsigned int id); -extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); -extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); -extern void enable_vbus(void); -#endif /* __DAVINCI_USB_H__ */ diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile new file mode 100644 index 0000000..251724f --- /dev/null +++ b/drivers/usb/gadget/Makefile @@ -0,0 +1,52 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB := $(obj)libusb_gadget.a + +ifdef CONFIG_USB_DEVICE +COBJS-y += core.o +COBJS-y += ep0.o +COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o +COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o +COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o +endif + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c new file mode 100644 index 0000000..67b6681 --- /dev/null +++ b/drivers/usb/gadget/core.c @@ -0,0 +1,683 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usbd/usbd.c.c - USB Device Core Layer + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * 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. + * + */ + +#include +#include + +#define MAX_INTERFACES 2 + + +int maxstrings = 20; + +/* Global variables ************************************************************************** */ + +struct usb_string_descriptor **usb_strings; + +int usb_devices; + +extern struct usb_function_driver ep0_driver; + +int registered_functions; +int registered_devices; + +char *usbd_device_events[] = { + "DEVICE_UNKNOWN", + "DEVICE_INIT", + "DEVICE_CREATE", + "DEVICE_HUB_CONFIGURED", + "DEVICE_RESET", + "DEVICE_ADDRESS_ASSIGNED", + "DEVICE_CONFIGURED", + "DEVICE_SET_INTERFACE", + "DEVICE_SET_FEATURE", + "DEVICE_CLEAR_FEATURE", + "DEVICE_DE_CONFIGURED", + "DEVICE_BUS_INACTIVE", + "DEVICE_BUS_ACTIVITY", + "DEVICE_POWER_INTERRUPTION", + "DEVICE_HUB_RESET", + "DEVICE_DESTROY", + "DEVICE_FUNCTION_PRIVATE", +}; + +char *usbd_device_states[] = { + "STATE_INIT", + "STATE_CREATED", + "STATE_ATTACHED", + "STATE_POWERED", + "STATE_DEFAULT", + "STATE_ADDRESSED", + "STATE_CONFIGURED", + "STATE_UNKNOWN", +}; + +char *usbd_device_requests[] = { + "GET STATUS", /* 0 */ + "CLEAR FEATURE", /* 1 */ + "RESERVED", /* 2 */ + "SET FEATURE", /* 3 */ + "RESERVED", /* 4 */ + "SET ADDRESS", /* 5 */ + "GET DESCRIPTOR", /* 6 */ + "SET DESCRIPTOR", /* 7 */ + "GET CONFIGURATION", /* 8 */ + "SET CONFIGURATION", /* 9 */ + "GET INTERFACE", /* 10 */ + "SET INTERFACE", /* 11 */ + "SYNC FRAME", /* 12 */ +}; + +char *usbd_device_descriptors[] = { + "UNKNOWN", /* 0 */ + "DEVICE", /* 1 */ + "CONFIG", /* 2 */ + "STRING", /* 3 */ + "INTERFACE", /* 4 */ + "ENDPOINT", /* 5 */ + "DEVICE QUALIFIER", /* 6 */ + "OTHER SPEED", /* 7 */ + "INTERFACE POWER", /* 8 */ +}; + +char *usbd_device_status[] = { + "USBD_OPENING", + "USBD_OK", + "USBD_SUSPENDED", + "USBD_CLOSING", +}; + + +/* Descriptor support functions ************************************************************** */ + + +/** + * usbd_get_string - find and return a string descriptor + * @index: string index to return + * + * Find an indexed string and return a pointer to a it. + */ +struct usb_string_descriptor *usbd_get_string (__u8 index) +{ + if (index >= maxstrings) { + return NULL; + } + return usb_strings[index]; +} + + +/* Access to device descriptor functions ***************************************************** */ + + +/* * + * usbd_device_configuration_instance - find a configuration instance for this device + * @device: + * @configuration: index to configuration, 0 - N-1 + * + * Get specifed device configuration. Index should be bConfigurationValue-1. + */ +static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, + unsigned int port, unsigned int configuration) +{ + if (configuration >= device->configurations) + return NULL; + + return device->configuration_instance_array + configuration; +} + + +/* * + * usbd_device_interface_instance + * @device: + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * + * Return the specified interface descriptor for the specified device. + */ +struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) +{ + struct usb_configuration_instance *configuration_instance; + + if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { + return NULL; + } + if (interface >= configuration_instance->interfaces) { + return NULL; + } + return configuration_instance->interface_instance_array + interface; +} + +/* * + * usbd_device_alternate_descriptor_list + * @device: + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * + * Return the specified alternate descriptor for the specified device. + */ +struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) +{ + struct usb_interface_instance *interface_instance; + + if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { + return NULL; + } + + if (alternate >= interface_instance->alternates) { + return NULL; + } + + return interface_instance->alternates_instance_array + alternate; +} + + +/* * + * usbd_device_device_descriptor + * @device: which device + * @configuration: index to configuration, 0 - N-1 + * @port: which port + * + * Return the specified configuration descriptor for the specified device. + */ +struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) +{ + return (device->device_descriptor); +} + + +/** + * usbd_device_configuration_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * + * Return the specified configuration descriptor for the specified device. + */ +struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct + usb_device_instance + *device, int port, int configuration) +{ + struct usb_configuration_instance *configuration_instance; + if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { + return NULL; + } + return (configuration_instance->configuration_descriptor); +} + + +/** + * usbd_device_interface_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * + * Return the specified interface descriptor for the specified device. + */ +struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance + *device, int port, int configuration, int interface, int alternate) +{ + struct usb_interface_instance *interface_instance; + if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { + return NULL; + } + if ((alternate < 0) || (alternate >= interface_instance->alternates)) { + return NULL; + } + return (interface_instance->alternates_instance_array[alternate].interface_descriptor); +} + +/** + * usbd_device_endpoint_descriptor_index + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: index setting + * @index: which index + * + * Return the specified endpoint descriptor for the specified device. + */ +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance + *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return NULL; + } + if (index >= alternate_instance->endpoints) { + return NULL; + } + return *(alternate_instance->endpoints_descriptor_array + index); +} + + +/** + * usbd_device_endpoint_transfersize + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @index: which index + * + * Return the specified endpoint transfer size; + */ +int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return 0; + } + if (index >= alternate_instance->endpoints) { + return 0; + } + return *(alternate_instance->endpoint_transfersize_array + index); +} + + +/** + * usbd_device_endpoint_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * @endpoint: which endpoint + * + * Return the specified endpoint descriptor for the specified device. + */ +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) +{ + struct usb_endpoint_descriptor *endpoint_descriptor; + int i; + + for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { + if (endpoint_descriptor->bEndpointAddress == endpoint) { + return endpoint_descriptor; + } + } + return NULL; +} + +/** + * usbd_endpoint_halted + * @device: point to struct usb_device_instance + * @endpoint: endpoint to check + * + * Return non-zero if endpoint is halted. + */ +int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) +{ + return (device->status == USB_STATUS_HALT); +} + + +/** + * usbd_rcv_complete - complete a receive + * @endpoint: + * @len: + * @urb_bad: + * + * Called from rcv interrupt to complete. + */ +void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad) +{ + if (endpoint) { + struct urb *rcv_urb; + + /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */ + + /* if we had an urb then update actual_length, dispatch if neccessary */ + if ((rcv_urb = endpoint->rcv_urb)) { + + /*usbdbg("actual: %d buffer: %d\n", */ + /*rcv_urb->actual_length, rcv_urb->buffer_length); */ + + /* check the urb is ok, are we adding data less than the packetsize */ + if (!urb_bad && (len <= endpoint->rcv_packetSize)) { + /*usbdbg("updating actual_length by %d\n",len); */ + + /* increment the received data size */ + rcv_urb->actual_length += len; + + } else { + usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", + rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); + + rcv_urb->actual_length = 0; + rcv_urb->status = RECV_ERROR; + } + } else { + usberr("no rcv_urb!"); + } + } else { + usberr("no endpoint!"); + } + +} + +/** + * usbd_tx_complete - complete a transmit + * @endpoint: + * @resetart: + * + * Called from tx interrupt to complete. + */ +void usbd_tx_complete (struct usb_endpoint_instance *endpoint) +{ + if (endpoint) { + struct urb *tx_urb; + + /* if we have a tx_urb advance or reset, finish if complete */ + if ((tx_urb = endpoint->tx_urb)) { + int sent = endpoint->last; + endpoint->sent += sent; + endpoint->last -= sent; + + if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) { + tx_urb->actual_length = 0; + endpoint->sent = 0; + endpoint->last = 0; + + /* Remove from active, save for re-use */ + urb_detach(tx_urb); + urb_append(&endpoint->done, tx_urb); + /*usbdbg("done->next %p, tx_urb %p, done %p", */ + /* endpoint->done.next, tx_urb, &endpoint->done); */ + + endpoint->tx_urb = first_urb_detached(&endpoint->tx); + if( endpoint->tx_urb ) { + endpoint->tx_queue--; + usbdbg("got urb from tx list"); + } + if( !endpoint->tx_urb ) { + /*usbdbg("taking urb from done list"); */ + endpoint->tx_urb = first_urb_detached(&endpoint->done); + } + if( !endpoint->tx_urb ) { + usbdbg("allocating new urb for tx_urb"); + endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint); + } + } + } + } +} + +/* URB linked list functions ***************************************************** */ + +/* + * Initialize an urb_link to be a single element list. + * If the urb_link is being used as a distinguished list head + * the list is empty when the head is the only link in the list. + */ +void urb_link_init (urb_link * ul) +{ + if (ul) { + ul->prev = ul->next = ul; + } +} + +/* + * Detach an urb_link from a list, and set it + * up as a single element list, so no dangling + * pointers can be followed, and so it can be + * joined to another list if so desired. + */ +void urb_detach (struct urb *urb) +{ + if (urb) { + urb_link *ul = &urb->link; + ul->next->prev = ul->prev; + ul->prev->next = ul->next; + urb_link_init (ul); + } +} + +/* + * Return the first urb_link in a list with a distinguished + * head "hd", or NULL if the list is empty. This will also + * work as a predicate, returning NULL if empty, and non-NULL + * otherwise. + */ +urb_link *first_urb_link (urb_link * hd) +{ + urb_link *nx; + if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { + /* There is at least one element in the list */ + /* (besides the distinguished head). */ + return (nx); + } + /* The list is empty */ + return (NULL); +} + +/* + * Return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + */ +struct urb *first_urb (urb_link * hd) +{ + urb_link *nx; + if (NULL == (nx = first_urb_link (hd))) { + /* The list is empty */ + return (NULL); + } + return (p2surround (struct urb, link, nx)); +} + +/* + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + */ +struct urb *first_urb_detached (urb_link * hd) +{ + struct urb *urb; + if ((urb = first_urb (hd))) { + urb_detach (urb); + } + return urb; +} + + +/* + * Append an urb_link (or a whole list of + * urb_links) to the tail of another list + * of urb_links. + */ +void urb_append (urb_link * hd, struct urb *urb) +{ + if (hd && urb) { + urb_link *new = &urb->link; + + /* This allows the new urb to be a list of urbs, */ + /* with new pointing at the first, but the link */ + /* must be initialized. */ + /* Order is important here... */ + urb_link *pul = hd->prev; + new->prev->next = hd; + hd->prev = new->prev; + new->prev = pul; + pul->next = new; + } +} + +/* URB create/destroy functions ***************************************************** */ + +/** + * usbd_alloc_urb - allocate an URB appropriate for specified endpoint + * @device: device instance + * @endpoint: endpoint + * + * Allocate an urb structure. The usb device urb structure is used to + * contain all data associated with a transfer, including a setup packet for + * control transfers. + * + * NOTE: endpoint_address MUST contain a direction flag. + */ +struct urb *usbd_alloc_urb (struct usb_device_instance *device, + struct usb_endpoint_instance *endpoint) +{ + struct urb *urb; + + if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) { + usberr (" F A T A L: malloc(%zu) FAILED!!!!", + sizeof (struct urb)); + return NULL; + } + + /* Fill in known fields */ + memset (urb, 0, sizeof (struct urb)); + urb->endpoint = endpoint; + urb->device = device; + urb->buffer = (u8 *) urb->buffer_data; + urb->buffer_length = sizeof (urb->buffer_data); + + urb_link_init (&urb->link); + + return urb; +} + +/** + * usbd_dealloc_urb - deallocate an URB and associated buffer + * @urb: pointer to an urb structure + * + * Deallocate an urb structure and associated data. + */ +void usbd_dealloc_urb (struct urb *urb) +{ + if (urb) { + free (urb); + } +} + +/* Event signaling functions ***************************************************** */ + +/** + * usbd_device_event - called to respond to various usb events + * @device: pointer to struct device + * @event: event to respond to + * + * Used by a Bus driver to indicate an event. + */ +void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + usb_device_state_t state; + + if (!device || !device->bus) { + usberr("(%p,%d) NULL device or device->bus", device, event); + return; + } + + state = device->device_state; + + usbinfo("%s", usbd_device_events[event]); + + switch (event) { + case DEVICE_UNKNOWN: + break; + case DEVICE_INIT: + device->device_state = STATE_INIT; + break; + + case DEVICE_CREATE: + device->device_state = STATE_ATTACHED; + break; + + case DEVICE_HUB_CONFIGURED: + device->device_state = STATE_POWERED; + break; + + case DEVICE_RESET: + device->device_state = STATE_DEFAULT; + device->address = 0; + break; + + case DEVICE_ADDRESS_ASSIGNED: + device->device_state = STATE_ADDRESSED; + break; + + case DEVICE_CONFIGURED: + device->device_state = STATE_CONFIGURED; + break; + + case DEVICE_DE_CONFIGURED: + device->device_state = STATE_ADDRESSED; + break; + + case DEVICE_BUS_INACTIVE: + if (device->status != USBD_CLOSING) { + device->status = USBD_SUSPENDED; + } + break; + case DEVICE_BUS_ACTIVITY: + if (device->status != USBD_CLOSING) { + device->status = USBD_OK; + } + break; + + case DEVICE_SET_INTERFACE: + break; + case DEVICE_SET_FEATURE: + break; + case DEVICE_CLEAR_FEATURE: + break; + + case DEVICE_POWER_INTERRUPTION: + device->device_state = STATE_POWERED; + break; + case DEVICE_HUB_RESET: + device->device_state = STATE_ATTACHED; + break; + case DEVICE_DESTROY: + device->device_state = STATE_UNKNOWN; + break; + + case DEVICE_FUNCTION_PRIVATE: + break; + + default: + usbdbg("event %d - not handled",event); + break; + } + /*usbdbg("%s event: %d oldstate: %d newstate: %d status: %d address: %d", + device->name, event, state, + device->device_state, device->status, device->address); */ + + /* tell the bus interface driver */ + if( device->event ) { + /* usbdbg("calling device->event"); */ + device->event(device, event, data); + } +} diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c new file mode 100644 index 0000000..2b4ec44 --- /dev/null +++ b/drivers/usb/gadget/ep0.c @@ -0,0 +1,597 @@ +/* + * (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 + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * 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. + * + */ + +/* + * This is the builtin ep0 control function. It implements all required functionality + * for responding to control requests (SETUP packets). + * + * XXX + * + * Currently we do not pass any SETUP packets (or other) to the configured + * 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 +#include + +#if 0 +#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args) +#else +#define dbg_ep0(lvl,fmt,args...) +#endif + +/* EP0 Configuration Set ********************************************************************* */ + + +/** + * ep0_get_status - fill in URB data with appropriate status + * @device: + * @urb: + * @index: + * @requesttype: + * + */ +static int ep0_get_status (struct usb_device_instance *device, + struct urb *urb, int index, int requesttype) +{ + char *cp; + + urb->actual_length = 2; + cp = (char*)urb->buffer; + cp[0] = cp[1] = 0; + + switch (requesttype) { + case USB_REQ_RECIPIENT_DEVICE: + cp[0] = USB_STATUS_SELFPOWERED; + break; + case USB_REQ_RECIPIENT_INTERFACE: + break; + case USB_REQ_RECIPIENT_ENDPOINT: + cp[0] = usbd_endpoint_halted (device, index); + break; + case USB_REQ_RECIPIENT_OTHER: + urb->actual_length = 0; + default: + break; + } + dbg_ep0 (2, "%02x %02x", cp[0], cp[1]); + return 0; +} + +/** + * ep0_get_one + * @device: + * @urb: + * @result: + * + * Set a single byte value in the urb send buffer. Return non-zero to signal + * a request error. + */ +static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, + __u8 result) +{ + urb->actual_length = 1; /* XXX 2? */ + ((char *) urb->buffer)[0] = result; + return 0; +} + +/** + * copy_config + * @urb: pointer to urb + * @data: pointer to configuration data + * @length: length of data + * + * Copy configuration data to urb transfer buffer if there is room for it. + */ +void copy_config (struct urb *urb, void *data, int max_length, + int max_buf) +{ + int available; + int length; + + /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */ + /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */ + + if (!data) { + dbg_ep0 (1, "data is NULL"); + return; + } + length = max_length; + + if (length > max_length) { + dbg_ep0 (1, "length: %d >= max_length: %d", length, + max_length); + return; + } + /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */ + /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */ + + if ((available = + /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) { + return; + } + /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ + /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ + + if (length > available) { + length = available; + } + /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ + /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ + + memcpy (urb->buffer + urb->actual_length, data, length); + urb->actual_length += length; + + dbg_ep0 (3, + "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d", + urb->actual_length, urb->buffer_length, max_buf, max_length, + available); +} + +/** + * ep0_get_descriptor + * @device: + * @urb: + * @max: + * @descriptor_type: + * @index: + * + * Called by ep0_rx_process for a get descriptor device command. Determine what + * descriptor is being requested, copy to send buffer. Return zero if ok to send, + * return non-zero to signal a request error. + */ +static int ep0_get_descriptor (struct usb_device_instance *device, + struct urb *urb, int max, int descriptor_type, + int index) +{ + int port = 0; /* XXX compound device */ + char *cp; + + /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */ + + if (!urb || !urb->buffer || !urb->buffer_length + || (urb->buffer_length < 255)) { + dbg_ep0 (2, "invalid urb %p", urb); + return -1L; + } + + /* setup tx urb */ + urb->actual_length = 0; + cp = (char*)urb->buffer; + + dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); + + switch (descriptor_type) { + case USB_DESCRIPTOR_TYPE_DEVICE: + { + struct usb_device_descriptor *device_descriptor; + if (! + (device_descriptor = + usbd_device_device_descriptor (device, port))) { + return -1; + } + /* copy descriptor for this device */ + copy_config (urb, device_descriptor, + sizeof (struct usb_device_descriptor), + max); + + /* correct the correct control endpoint 0 max packet size into the descriptor */ + device_descriptor = + (struct usb_device_descriptor *) urb->buffer; + + } + dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); + break; + + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + { + struct usb_configuration_descriptor + *configuration_descriptor; + struct usb_device_descriptor *device_descriptor; + if (! + (device_descriptor = + usbd_device_device_descriptor (device, port))) { + return -1; + } + /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */ + if (index >= device_descriptor->bNumConfigurations) { + dbg_ep0 (0, "index too large: %d >= %d", index, + device_descriptor-> + bNumConfigurations); + return -1; + } + + if (! + (configuration_descriptor = + usbd_device_configuration_descriptor (device, + port, + index))) { + dbg_ep0 (0, + "usbd_device_configuration_descriptor failed: %d", + 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, + + 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 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 = + le16_to_cpu (urb->device_request.wIndex); + int bAlternateSetting = 0; + int class = 0; + struct usb_class_descriptor *class_descriptor; + + if (!(class_descriptor = + usbd_device_class_descriptor_index (device, + port, 0, + bNumInterface, + bAlternateSetting, + class)) + || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) { + dbg_ep0 (3, "[%d] interface is not HID", + bNumInterface); + return -1; + } + /* copy descriptor for this class */ + copy_config (urb, class_descriptor, + class_descriptor->descriptor.hid.bLength, + max); +#endif + } + 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 = + le16_to_cpu (urb->device_request.wIndex); + int bAlternateSetting = 0; + int class = 0; + struct usb_class_report_descriptor *report_descriptor; + + if (!(report_descriptor = + usbd_device_class_report_descriptor_index + (device, port, 0, bNumInterface, + bAlternateSetting, class)) + || report_descriptor->bDescriptorType != + USB_DT_REPORT) { + dbg_ep0 (3, "[%d] descriptor is not REPORT", + bNumInterface); + return -1; + } + /* copy report descriptor for this class */ + /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */ + if (max - urb->actual_length > 0) { + int length = + MIN (report_descriptor->wLength, + max - urb->actual_length); + memcpy (urb->buffer + urb->actual_length, + &report_descriptor->bData[0], length); + urb->actual_length += length; + } +#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 tx_packetSize: %2d", + urb->buffer, urb->buffer_length, urb->actual_length, + device->bus->endpoint_array[0].tx_packetSize); +/* + if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) { + dbg_ep0(0, "adding null byte"); + urb->buffer[urb->actual_length++] = 0; + dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d", + urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize); + } +*/ + return 0; + +} + +/** + * ep0_recv_setup - called to indicate URB has been received + * @urb: pointer to struct urb + * + * Check if this is a setup packet, process the device request, put results + * back into the urb and return zero or non-zero to indicate success (DATA) + * or failure (STALL). + * + */ +int ep0_recv_setup (struct urb *urb) +{ + /*struct usb_device_request *request = urb->buffer; */ + /*struct usb_device_instance *device = urb->device; */ + + struct usb_device_request *request; + struct usb_device_instance *device; + int address; + + dbg_ep0 (0, "entering ep0_recv_setup()"); + if (!urb || !urb->device) { + dbg_ep0 (3, "invalid URB %p", urb); + return -1; + } + + request = &urb->device_request; + device = urb->device; + + dbg_ep0 (3, "urb: %p device: %p", urb, urb->device); + + + /*dbg_ep0(2, "- - - - - - - - - -"); */ + + dbg_ep0 (2, + "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s", + request->bmRequestType, request->bRequest, + le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex), + le16_to_cpu (request->wLength), + USBD_DEVICE_REQUESTS (request->bRequest)); + + /* 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 */ + } + + switch (device->device_state) { + case STATE_CREATED: + case STATE_ATTACHED: + case STATE_POWERED: + /* It actually is important to allow requests in these states, + * Windows will request descriptors before assigning an + * address to the client. + */ + + /*dbg_ep0 (1, "request %s not allowed in this state: %s", */ + /* USBD_DEVICE_REQUESTS(request->bRequest), */ + /* usbd_device_states[device->device_state]); */ + /*return -1; */ + break; + + case STATE_INIT: + case STATE_DEFAULT: + switch (request->bRequest) { + case USB_REQ_GET_STATUS: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_DESCRIPTOR: + /* case USB_REQ_SET_CONFIGURATION: */ + case USB_REQ_SET_INTERFACE: + dbg_ep0 (1, + "request %s not allowed in DEFAULT state: %s", + USBD_DEVICE_REQUESTS (request->bRequest), + usbd_device_states[device->device_state]); + return -1; + + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_ADDRESS: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + break; + } + case STATE_ADDRESSED: + case STATE_CONFIGURED: + break; + case STATE_UNKNOWN: + dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s", + USBD_DEVICE_REQUESTS (request->bRequest), + usbd_device_states[device->device_state]); + return -1; + } + + /* handle all requests that return data (direction bit set on bm RequestType) */ + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { + + dbg_ep0 (3, "Device-to-Host"); + + switch (request->bRequest) { + + case USB_REQ_GET_STATUS: + return ep0_get_status (device, urb, request->wIndex, + request->bmRequestType & + USB_REQ_RECIPIENT_MASK); + + case USB_REQ_GET_DESCRIPTOR: + return ep0_get_descriptor (device, urb, + le16_to_cpu (request->wLength), + le16_to_cpu (request->wValue) >> 8, + 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); + + case USB_REQ_GET_INTERFACE: + return ep0_get_one (device, urb, device->alternate); + + case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ + return -1; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_INTERFACE: + return -1; + } + } + /* handle the requests that do not return data */ + else { + + + /*dbg_ep0(3, "Host-to-Device"); */ + switch (request->bRequest) { + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dbg_ep0 (0, "Host-to-Device"); + switch (request-> + bmRequestType & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_DEVICE: + /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */ + /* XXX fall through for now as we do not support either */ + case USB_REQ_RECIPIENT_INTERFACE: + case USB_REQ_RECIPIENT_OTHER: + dbg_ep0 (0, "request %s not", + USBD_DEVICE_REQUESTS (request->bRequest)); + default: + return -1; + + case USB_REQ_RECIPIENT_ENDPOINT: + dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue)); + if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) { + /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */ + /* request->bRequest == USB_REQ_SET_FEATURE); */ + /* NEED TO IMPLEMENT THIS!!! */ + return -1; + } else { + dbg_ep0 (1, "request %s bad wValue: %04x", + USBD_DEVICE_REQUESTS + (request->bRequest), + le16_to_cpu (request->wValue)); + return -1; + } + } + + case USB_REQ_SET_ADDRESS: + /* check if this is a re-address, reset first if it is (this shouldn't be possible) */ + if (device->device_state != STATE_DEFAULT) { + dbg_ep0 (1, "set_address: %02x state: %s", + le16_to_cpu (request->wValue), + usbd_device_states[device->device_state]); + return -1; + } + address = le16_to_cpu (request->wValue); + if ((address & 0x7f) != address) { + dbg_ep0 (1, "invalid address %04x %04x", + address, address & 0x7f); + return -1; + } + device->address = address; + + /*dbg_ep0(2, "address: %d %d %d", */ + /* request->wValue, le16_to_cpu(request->wValue), device->address); */ + + return 0; + + case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */ + dbg_ep0 (0, "set descriptor: NOT SUPPORTED"); + return -1; + + case USB_REQ_SET_CONFIGURATION: + /* c.f. 9.4.7 - the top half of wValue is reserved */ + device->configuration = le16_to_cpu(request->wValue) & 0xff; + + /* reset interface and alternate settings */ + device->interface = device->alternate = 0; + + /*dbg_ep0(2, "set configuration: %d", device->configuration); */ + /*serial_printf("DEVICE_CONFIGURED.. event?\n"); */ + return 0; + + case USB_REQ_SET_INTERFACE: + device->interface = le16_to_cpu (request->wIndex); + device->alternate = le16_to_cpu (request->wValue); + /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */ + serial_printf ("DEVICE_SET_INTERFACE.. event?\n"); + return 0; + + case USB_REQ_GET_STATUS: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ + return -1; + } + } + return -1; +} diff --git a/drivers/usb/gadget/ep0.h b/drivers/usb/gadget/ep0.h new file mode 100644 index 0000000..3bec106 --- /dev/null +++ b/drivers/usb/gadget/ep0.h @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usbd/ep0.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * 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. + * + */ + +#ifndef __USBDCORE_EP0_H__ +#define __USBDCORE_EP0_H__ + + +int ep0_recv_setup (struct urb *urb); + + +#endif diff --git a/drivers/usb/gadget/mpc8xx_udc.c b/drivers/usb/gadget/mpc8xx_udc.c new file mode 100644 index 0000000..da3fbba --- /dev/null +++ b/drivers/usb/gadget/mpc8xx_udc.c @@ -0,0 +1,1399 @@ +/* + * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit + * bodonoghue@CodeHermit.ie + * + * References + * DasUBoot/drivers/usb/gadget/omap1510_udc.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 +#include +#include +#include +#include + +#include "ep0.h" + +DECLARE_GLOBAL_DATA_PTR; + +#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 *) CONFIG_SYS_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 + CONFIG_SYS_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 + CONFIG_SYS_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 + CONFIG_SYS_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 + CONFIG_SYS_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(CONFIG_SYS_USB_EXTC_CLK) + + /* This has been tested with a 48MHz crystal on CLK6 */ + switch (CONFIG_SYS_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(CONFIG_SYS_USB_BRGCLK) + + /* This has been tested with brgclk == 50MHz */ + 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 (CONFIG_SYS_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 "CONFIG_SYS_USB_EXTC_CLK or CONFIG_SYS_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; +} diff --git a/drivers/usb/gadget/omap1510_udc.c b/drivers/usb/gadget/omap1510_udc.c new file mode 100644 index 0000000..90f7907 --- /dev/null +++ b/drivers/usb/gadget/omap1510_udc.c @@ -0,0 +1,1567 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usb/device/bi/omap.c + * TI OMAP1510 USB bus interface driver + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * (C) Copyright 2002 + * + * 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 +#include +#ifdef CONFIG_OMAP_SX1 +#include +#endif +#include +#include + +#include "ep0.h" + + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ +#define UDC_MAX_ENDPOINTS 31 /* Number of endpoints on this UDC */ + +/* Some kind of debugging output... */ +#if 1 +#define UDCDBG(str) +#define UDCDBGA(fmt,args...) +#else /* The bugs still exists... */ +#define UDCDBG(str) serial_printf("[%s] %s:%d: " str "\n", __FILE__,__FUNCTION__,__LINE__) +#define UDCDBGA(fmt,args...) serial_printf("[%s] %s:%d: " fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##args) +#endif + +#if 1 +#define UDCREG(name) +#define UDCREGL(name) +#else /* The bugs still exists... */ +#define UDCREG(name) serial_printf("%s():%d: %s[%08x]=%.4x\n",__FUNCTION__,__LINE__, (#name), name, inw(name)) /* For 16-bit regs */ +#define UDCREGL(name) serial_printf("%s():%d: %s[%08x]=%.8x\n",__FUNCTION__,__LINE__, (#name), name, inl(name)) /* For 32-bit regs */ +#endif + + +static struct urb *ep0_urb = NULL; + +static struct usb_device_instance *udc_device; /* Used in interrupt handler */ +static u16 udc_devstat = 0; /* UDC status (DEVSTAT) */ +static u32 udc_interrupts = 0; + +static void udc_stall_ep (unsigned int ep_addr); + + +static struct usb_endpoint_instance *omap1510_find_ep (int ep) +{ + int i; + + for (i = 0; i < udc_device->bus->max_endpoints; i++) { + if (udc_device->bus->endpoint_array[i].endpoint_address == ep) + return &udc_device->bus->endpoint_array[i]; + } + return NULL; +} + +/* ************************************************************************** */ +/* IO + */ + +/* + * omap1510_prepare_endpoint_for_rx + * + * This function implements TRM Figure 14-11. + * + * The endpoint to prepare for transfer is specified as a physical endpoint + * number. For OUT (rx) endpoints 1 through 15, the corresponding endpoint + * configuration register is checked to see if the endpoint is ISO or not. + * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled. + * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30. + */ +static void omap1510_prepare_endpoint_for_rx (int ep_addr) +{ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("omap1510_prepare_endpoint %x", ep_addr); + if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)) { + if ((inw (UDC_EP_RX (ep_num)) & + (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) == + UDC_EPn_RX_Valid) { + /* rx endpoint is valid, non-ISO, so enable its FIFO */ + outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); + outw (UDC_Set_FIFO_En, UDC_CTRL); + outw (0, UDC_EP_NUM); + } + } +} + +/* omap1510_configure_endpoints + * + * This function implements TRM Figure 14-10. + */ +static void omap1510_configure_endpoints (struct usb_device_instance *device) +{ + int ep; + struct usb_bus_instance *bus; + struct usb_endpoint_instance *endpoint; + unsigned short ep_ptr; + unsigned short ep_size; + unsigned short ep_isoc; + unsigned short ep_doublebuffer; + int ep_addr; + int packet_size; + int buffer_size; + int attributes; + + bus = device->bus; + + /* There is a dedicated 2048 byte buffer for USB packets that may be + * arbitrarily partitioned among the endpoints on 8-byte boundaries. + * The first 8 bytes are reserved for receiving setup packets on + * endpoint 0. + */ + ep_ptr = 8; /* reserve the first 8 bytes for the setup fifo */ + + for (ep = 0; ep < bus->max_endpoints; ep++) { + endpoint = bus->endpoint_array + ep; + ep_addr = endpoint->endpoint_address; + if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* IN endpoint */ + packet_size = endpoint->tx_packetSize; + attributes = endpoint->tx_attributes; + } else { + /* OUT endpoint */ + packet_size = endpoint->rcv_packetSize; + attributes = endpoint->rcv_attributes; + } + + switch (packet_size) { + case 0: + ep_size = 0; + break; + case 8: + ep_size = 0; + break; + case 16: + ep_size = 1; + break; + case 32: + ep_size = 2; + break; + case 64: + ep_size = 3; + break; + case 128: + ep_size = 4; + break; + case 256: + ep_size = 5; + break; + case 512: + ep_size = 6; + break; + default: + UDCDBGA ("ep 0x%02x has bad packet size %d", + ep_addr, packet_size); + packet_size = 0; + ep_size = 0; + break; + } + + switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + default: + /* A non-isochronous endpoint may optionally be + * double-buffered. For now we disable + * double-buffering. + */ + ep_doublebuffer = 0; + ep_isoc = 0; + if (packet_size > 64) + packet_size = 0; + if (!ep || !ep_doublebuffer) + buffer_size = packet_size; + else + buffer_size = packet_size * 2; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Isochronous endpoints are always double- + * buffered, but the double-buffering bit + * in the endpoint configuration register + * becomes the msb of the endpoint size so we + * set the double-buffering flag to zero. + */ + ep_doublebuffer = 0; + ep_isoc = 1; + buffer_size = packet_size * 2; + break; + } + + /* check to see if our packet buffer RAM is exhausted */ + if ((ep_ptr + buffer_size) > 2048) { + UDCDBGA ("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size); + buffer_size = packet_size = 0; + } + + /* force a default configuration for endpoint 0 since it is + * always enabled + */ + if (!ep && ((packet_size < 8) || (packet_size > 64))) { + buffer_size = packet_size = 64; + ep_size = 3; + } + + if (!ep) { + /* configure endpoint 0 */ + outw ((ep_size << 12) | (ep_ptr >> 3), UDC_EP0); + /*UDCDBGA("ep 0 buffer offset 0x%03x packet size 0x%03x", */ + /* ep_ptr, packet_size); */ + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* IN endpoint */ + if (packet_size) { + outw ((1 << 15) | (ep_doublebuffer << 14) | + (ep_size << 12) | (ep_isoc << 11) | + (ep_ptr >> 3), + UDC_EP_TX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + UDCDBGA ("IN ep %d buffer offset 0x%03x" + " packet size 0x%03x", + ep_addr & USB_ENDPOINT_NUMBER_MASK, + ep_ptr, packet_size); + } else { + outw (0, + UDC_EP_TX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + } + } else { + /* OUT endpoint */ + if (packet_size) { + outw ((1 << 15) | (ep_doublebuffer << 14) | + (ep_size << 12) | (ep_isoc << 11) | + (ep_ptr >> 3), + UDC_EP_RX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + UDCDBGA ("OUT ep %d buffer offset 0x%03x" + " packet size 0x%03x", + ep_addr & USB_ENDPOINT_NUMBER_MASK, + ep_ptr, packet_size); + } else { + outw (0, + UDC_EP_RX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + } + } + ep_ptr += buffer_size; + } +} + +/* omap1510_deconfigure_device + * + * This function balances omap1510_configure_device. + */ +static void omap1510_deconfigure_device (void) +{ + int epnum; + + UDCDBG ("clear Cfg_Lock"); + outw (inw (UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1); + UDCREG (UDC_SYSCON1); + + /* deconfigure all endpoints */ + for (epnum = 1; epnum <= 15; epnum++) { + outw (0, UDC_EP_RX (epnum)); + outw (0, UDC_EP_TX (epnum)); + } +} + +/* omap1510_configure_device + * + * This function implements TRM Figure 14-9. + */ +static void omap1510_configure_device (struct usb_device_instance *device) +{ + omap1510_configure_endpoints (device); + + + /* Figure 14-9 indicates we should enable interrupts here, but we have + * other routines (udc_all_interrupts, udc_suspended_interrupts) to + * do that. + */ + + UDCDBG ("set Cfg_Lock"); + outw (inw (UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1); + UDCREG (UDC_SYSCON1); +} + +/* omap1510_write_noniso_tx_fifo + * + * This function implements TRM Figure 14-30. + * + * If the endpoint has an active tx_urb, then the next packet of data from the + * URB is written to the tx FIFO. The total amount of data in the urb is given + * by urb->actual_length. The maximum amount of data that can be sent in any + * one packet is given by endpoint->tx_packetSize. The number of data bytes + * from this URB that have already been transmitted is given by endpoint->sent. + * endpoint->last is updated by this routine with the number of data bytes + * transmitted in this packet. + * + * In accordance with Figure 14-30, the EP_NUM register must already have been + * written with the value to select the appropriate tx FIFO before this routine + * is called. + */ +static void omap1510_write_noniso_tx_fifo (struct usb_endpoint_instance + *endpoint) +{ + struct urb *urb = endpoint->tx_urb; + + if (urb) { + unsigned int last, i; + + UDCDBGA ("urb->buffer %p, buffer_length %d, actual_length %d", + urb->buffer, urb->buffer_length, urb->actual_length); + if ((last = + MIN (urb->actual_length - endpoint->sent, + endpoint->tx_packetSize))) { + u8 *cp = urb->buffer + endpoint->sent; + + UDCDBGA ("endpoint->sent %d, tx_packetSize %d, last %d", endpoint->sent, endpoint->tx_packetSize, last); + + if (((u32) cp & 1) == 0) { /* word aligned? */ + outsw (UDC_DATA, cp, last >> 1); + } else { /* byte aligned. */ + for (i = 0; i < (last >> 1); i++) { + u16 w = ((u16) cp[2 * i + 1] << 8) | + (u16) cp[2 * i]; + outw (w, UDC_DATA); + } + } + if (last & 1) { + outb (*(cp + last - 1), UDC_DATA); + } + } + endpoint->last = last; + } +} + +/* omap1510_read_noniso_rx_fifo + * + * This function implements TRM Figure 14-28. + * + * If the endpoint has an active rcv_urb, then the next packet of data is read + * from the rcv FIFO and written to rcv_urb->buffer at offset + * rcv_urb->actual_length to append the packet data to the data from any + * previous packets for this transfer. We assume that there is sufficient room + * left in the buffer to hold an entire packet of data. + * + * The return value is the number of bytes read from the FIFO for this packet. + * + * In accordance with Figure 14-28, the EP_NUM register must already have been + * written with the value to select the appropriate rcv FIFO before this routine + * is called. + */ +static int omap1510_read_noniso_rx_fifo (struct usb_endpoint_instance + *endpoint) +{ + struct urb *urb = endpoint->rcv_urb; + int len = 0; + + if (urb) { + len = inw (UDC_RXFSTAT); + + if (len) { + unsigned char *cp = urb->buffer + urb->actual_length; + + insw (UDC_DATA, cp, len >> 1); + if (len & 1) + *(cp + len - 1) = inb (UDC_DATA); + } + } + return len; +} + +/* omap1510_prepare_for_control_write_status + * + * This function implements TRM Figure 14-17. + * + * We have to deal here with non-autodecoded control writes that haven't already + * been dealt with by ep0_recv_setup. The non-autodecoded standard control + * write requests are: set/clear endpoint feature, set configuration, set + * interface, and set descriptor. ep0_recv_setup handles set/clear requests for + * ENDPOINT_HALT by halting the endpoint for a set request and resetting the + * endpoint for a clear request. ep0_recv_setup returns an error for + * SET_DESCRIPTOR requests which causes them to be terminated with a stall by + * the setup handler. A SET_INTERFACE request is handled by ep0_recv_setup by + * generating a DEVICE_SET_INTERFACE event. This leaves only the + * SET_CONFIGURATION event for us to deal with here. + * + */ +static void omap1510_prepare_for_control_write_status (struct urb *urb) +{ + struct usb_device_request *request = &urb->device_request;; + + /* check for a SET_CONFIGURATION request */ + if (request->bRequest == USB_REQ_SET_CONFIGURATION) { + int configuration = le16_to_cpu (request->wValue) & 0xff; + unsigned short devstat = inw (UDC_DEVSTAT); + + if ((devstat & (UDC_ADD | UDC_CFG)) == UDC_ADD) { + /* device is currently in ADDRESSED state */ + if (configuration) { + /* Assume the specified non-zero configuration + * value is valid and switch to the CONFIGURED + * state. + */ + outw (UDC_Dev_Cfg, UDC_SYSCON2); + } + } else if ((devstat & UDC_CFG) == UDC_CFG) { + /* device is currently in CONFIGURED state */ + if (!configuration) { + /* Switch to ADDRESSED state. */ + outw (UDC_Clr_Cfg, UDC_SYSCON2); + } + } + } + + /* select EP0 tx FIFO */ + outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + /* clear endpoint (no data bytes in status stage) */ + outw (UDC_Clr_EP, UDC_CTRL); + /* enable the EP0 tx FIFO */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the endpoint */ + outw (UDC_EP_Dir, UDC_EP_NUM); +} + +/* 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. + * + * These functions must only be called with interrupts disabled. + */ +static void 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 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; + } + } +} + +/* Handle all device state changes. + * This function implements TRM Figure 14-21. + */ +static void omap1510_udc_state_changed (void) +{ + u16 bits; + u16 devstat = inw (UDC_DEVSTAT); + + UDCDBGA ("state changed, devstat %x, old %x", devstat, udc_devstat); + + bits = devstat ^ udc_devstat; + if (bits) { + if (bits & UDC_ATT) { + if (devstat & UDC_ATT) { + UDCDBG ("device attached and powered"); + udc_state_transition_up (udc_device->device_state, STATE_POWERED); + } else { + UDCDBG ("device detached or unpowered"); + udc_state_transition_down (udc_device->device_state, STATE_ATTACHED); + } + } + if (bits & UDC_USB_Reset) { + if (devstat & UDC_USB_Reset) { + UDCDBG ("device reset in progess"); + udc_state_transition_down (udc_device->device_state, STATE_POWERED); + } else { + UDCDBG ("device reset completed"); + } + } + if (bits & UDC_DEF) { + if (devstat & UDC_DEF) { + UDCDBG ("device entering default state"); + udc_state_transition_up (udc_device->device_state, STATE_DEFAULT); + } else { + UDCDBG ("device leaving default state"); + udc_state_transition_down (udc_device->device_state, STATE_POWERED); + } + } + if (bits & UDC_SUS) { + if (devstat & UDC_SUS) { + UDCDBG ("entering suspended state"); + usbd_device_event_irq (udc_device, DEVICE_BUS_INACTIVE, 0); + } else { + UDCDBG ("leaving suspended state"); + usbd_device_event_irq (udc_device, DEVICE_BUS_ACTIVITY, 0); + } + } + if (bits & UDC_R_WK_OK) { + UDCDBGA ("remote wakeup %s", (devstat & UDC_R_WK_OK) + ? "enabled" : "disabled"); + } + if (bits & UDC_ADD) { + if (devstat & UDC_ADD) { + UDCDBG ("default -> addressed"); + udc_state_transition_up (udc_device->device_state, STATE_ADDRESSED); + } else { + UDCDBG ("addressed -> default"); + udc_state_transition_down (udc_device->device_state, STATE_DEFAULT); + } + } + if (bits & UDC_CFG) { + if (devstat & UDC_CFG) { + UDCDBG ("device configured"); + /* The ep0_recv_setup function generates the + * DEVICE_CONFIGURED event when a + * USB_REQ_SET_CONFIGURATION setup packet is + * received, so we should already be in the + * state STATE_CONFIGURED. + */ + udc_state_transition_up (udc_device->device_state, STATE_CONFIGURED); + } else { + UDCDBG ("device deconfigured"); + udc_state_transition_down (udc_device->device_state, STATE_ADDRESSED); + } + } + } + + /* Clear interrupt source */ + outw (UDC_DS_Chg, UDC_IRQ_SRC); + + /* Save current DEVSTAT */ + udc_devstat = devstat; +} + +/* Handle SETUP USB interrupt. + * This function implements TRM Figure 14-14. + */ +static void omap1510_udc_setup (struct usb_endpoint_instance *endpoint) +{ + UDCDBG ("-> Entering device setup"); + + do { + const int setup_pktsize = 8; + unsigned char *datap = + (unsigned char *) &ep0_urb->device_request; + + /* Gain access to EP 0 setup FIFO */ + outw (UDC_Setup_Sel, UDC_EP_NUM); + + /* Read control request data */ + insb (UDC_DATA, datap, setup_pktsize); + + UDCDBGA ("EP0 setup read [%x %x %x %x %x %x %x %x]", + *(datap + 0), *(datap + 1), *(datap + 2), + *(datap + 3), *(datap + 4), *(datap + 5), + *(datap + 6), *(datap + 7)); + + /* Reset EP0 setup FIFO */ + outw (0, UDC_EP_NUM); + } while (inw (UDC_IRQ_SRC) & UDC_Setup); + + /* Try to process setup packet */ + if (ep0_recv_setup (ep0_urb)) { + /* Not a setup packet, stall next EP0 transaction */ + udc_stall_ep (0); + UDCDBG ("can't parse setup packet, still waiting for setup"); + return; + } + + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + UDCDBG ("control write on EP0"); + if (le16_to_cpu (ep0_urb->device_request.wLength)) { + /* We don't support control write data stages. + * The only standard control write request with a data + * stage is SET_DESCRIPTOR, and ep0_recv_setup doesn't + * support that so we just stall those requests. A + * function driver might support a non-standard + * write request with a data stage, but it isn't + * obvious what we would do with the data if we read it + * so we'll just stall it. It seems like the API isn't + * quite right here. + */ +#if 0 + /* Here is what we would do if we did support control + * write data stages. + */ + ep0_urb->actual_length = 0; + outw (0, UDC_EP_NUM); + /* enable the EP0 rx FIFO */ + outw (UDC_Set_FIFO_En, UDC_CTRL); +#else + /* Stall this request */ + UDCDBG ("Stalling unsupported EP0 control write data " + "stage."); + udc_stall_ep (0); +#endif + } else { + omap1510_prepare_for_control_write_status (ep0_urb); + } + } else { + UDCDBG ("control read on EP0"); + /* The ep0_recv_setup function has already placed our response + * packet data in ep0_urb->buffer and the packet length in + * ep0_urb->actual_length. + */ + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + /* select the EP0 tx FIFO */ + outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + /* Write packet data to the FIFO. omap1510_write_noniso_tx_fifo + * will update endpoint->last with the number of bytes written + * to the FIFO. + */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable the FIFO to start the packet transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the EP0 tx FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } + + UDCDBG ("<- Leaving device setup"); +} + +/* Handle endpoint 0 RX interrupt + * This routine implements TRM Figure 14-16. + */ +static void omap1510_udc_ep0_rx (struct usb_endpoint_instance *endpoint) +{ + unsigned short status; + + UDCDBG ("RX on EP0"); + /* select EP0 rx FIFO */ + outw (UDC_EP_Sel, UDC_EP_NUM); + + status = inw (UDC_STAT_FLG); + + if (status & UDC_ACK) { + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType + & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + /* This rx interrupt must be for a control write data + * stage packet. + * + * We don't support control write data stages. + * We should never end up here. + */ + + /* clear the EP0 rx FIFO */ + outw (UDC_Clr_EP, UDC_CTRL); + + /* deselect the EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + + UDCDBG ("Stalling unexpected EP0 control write " + "data stage packet"); + udc_stall_ep (0); + } else { + /* This rx interrupt must be for a control read status + * stage packet. + */ + UDCDBG ("ACK on EP0 control read status stage packet"); + /* deselect EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + } + } else if (status & UDC_STALL) { + UDCDBG ("EP0 stall during RX"); + /* deselect EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + } else { + /* deselect EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + } +} + +/* Handle endpoint 0 TX interrupt + * This routine implements TRM Figure 14-18. + */ +static void omap1510_udc_ep0_tx (struct usb_endpoint_instance *endpoint) +{ + unsigned short status; + struct usb_device_request *request = &ep0_urb->device_request; + + UDCDBG ("TX on EP0"); + /* select EP0 TX FIFO */ + outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + + status = inw (UDC_STAT_FLG); + if (status & UDC_ACK) { + /* Check direction */ + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE) { + /* This tx interrupt must be for a control write status + * stage packet. + */ + UDCDBG ("ACK on EP0 control write status stage packet"); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } else { + /* This tx interrupt must be for a control read data + * stage packet. + */ + int wLength = le16_to_cpu (request->wLength); + + /* Update our count of bytes sent so far in this + * transfer. + */ + endpoint->sent += endpoint->last; + + /* We are finished with this transfer if we have sent + * all of the bytes in our tx urb (urb->actual_length) + * unless we need a zero-length terminating packet. We + * need a zero-length terminating packet if we returned + * fewer bytes than were requested (wLength) by the host, + * and the number of bytes we returned is an exact + * multiple of the packet size endpoint->tx_packetSize. + */ + if ((endpoint->sent == ep0_urb->actual_length) + && ((ep0_urb->actual_length == wLength) + || (endpoint->last != + endpoint->tx_packetSize))) { + /* Done with control read data stage. */ + UDCDBG ("control read data stage complete"); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + /* select EP0 RX FIFO to prepare for control + * read status stage. + */ + outw (UDC_EP_Sel, UDC_EP_NUM); + /* clear the EP0 RX FIFO */ + outw (UDC_Clr_EP, UDC_CTRL); + /* enable the EP0 RX FIFO */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the EP0 RX FIFO */ + outw (0, UDC_EP_NUM); + } else { + /* We still have another packet of data to send + * in this control read data stage or else we + * need a zero-length terminating packet. + */ + UDCDBG ("ACK control read data stage packet"); + omap1510_write_noniso_tx_fifo (endpoint); + /* enable the EP0 tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } + } + } else if (status & UDC_STALL) { + UDCDBG ("EP0 stall during TX"); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } else { + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } +} + +/* Handle RX transaction on non-ISO endpoint. + * This function implements TRM Figure 14-27. + * The ep argument is a physical endpoint number for a non-ISO OUT endpoint + * in the range 1 to 15. + */ +static void omap1510_udc_epn_rx (int ep) +{ + unsigned short status; + + /* Check endpoint status */ + status = inw (UDC_STAT_FLG); + + if (status & UDC_ACK) { + int nbytes; + struct usb_endpoint_instance *endpoint = + omap1510_find_ep (ep); + + nbytes = omap1510_read_noniso_rx_fifo (endpoint); + usbd_rcv_complete (endpoint, nbytes, 0); + + /* enable rx FIFO to prepare for next packet */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + } else if (status & UDC_STALL) { + UDCDBGA ("STALL on RX endpoint %d", ep); + } else if (status & UDC_NAK) { + UDCDBGA ("NAK on RX ep %d", ep); + } else { + serial_printf ("omap-bi: RX on ep %d with status %x", ep, + status); + } +} + +/* Handle TX transaction on non-ISO endpoint. + * This function implements TRM Figure 14-29. + * The ep argument is a physical endpoint number for a non-ISO IN endpoint + * in the range 16 to 30. + */ +static void omap1510_udc_epn_tx (int ep) +{ + unsigned short status; + + /*serial_printf("omap1510_udc_epn_tx( %x )\n",ep); */ + + /* Check endpoint status */ + status = inw (UDC_STAT_FLG); + + if (status & UDC_ACK) { + struct usb_endpoint_instance *endpoint = + omap1510_find_ep (ep); + + /* We need to transmit a terminating zero-length packet now if + * we have sent all of the data in this URB and the transfer + * size was an exact multiple of the packet size. + */ + if (endpoint->tx_urb + && (endpoint->last == endpoint->tx_packetSize) + && (endpoint->tx_urb->actual_length - endpoint->sent - + endpoint->last == 0)) { + /* Prepare to transmit a zero-length packet. */ + endpoint->sent += endpoint->last; + /* write 0 bytes of data to FIFO */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + } else if (endpoint->tx_urb + && endpoint->tx_urb->actual_length) { + /* retire the data that was just sent */ + usbd_tx_complete (endpoint); + /* Check to see if we have more data ready to transmit + * now. + */ + if (endpoint->tx_urb + && endpoint->tx_urb->actual_length) { + /* write data to FIFO */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + } + } + } else if (status & UDC_STALL) { + UDCDBGA ("STALL on TX endpoint %d", ep); + } else if (status & UDC_NAK) { + UDCDBGA ("NAK on TX endpoint %d", ep); + } else { + /*serial_printf("omap-bi: TX on ep %d with status %x\n", ep, status); */ + } +} + + +/* +------------------------------------------------------------------------------- +*/ + +/* Handle general USB interrupts and dispatch according to type. + * This function implements TRM Figure 14-13. + */ +void omap1510_udc_irq (void) +{ + u16 irq_src = inw (UDC_IRQ_SRC); + int valid_irq = 0; + + if (!(irq_src & ~UDC_SOF_Flg)) /* ignore SOF interrupts ) */ + return; + + UDCDBGA ("< IRQ #%d start >- %x", udc_interrupts, irq_src); + /*serial_printf("< IRQ #%d start >- %x\n", udc_interrupts, irq_src); */ + + if (irq_src & UDC_DS_Chg) { + /* Device status changed */ + omap1510_udc_state_changed (); + valid_irq++; + } + if (irq_src & UDC_EP0_RX) { + /* Endpoint 0 receive */ + outw (UDC_EP0_RX, UDC_IRQ_SRC); /* ack interrupt */ + omap1510_udc_ep0_rx (udc_device->bus->endpoint_array + 0); + valid_irq++; + } + if (irq_src & UDC_EP0_TX) { + /* Endpoint 0 transmit */ + outw (UDC_EP0_TX, UDC_IRQ_SRC); /* ack interrupt */ + omap1510_udc_ep0_tx (udc_device->bus->endpoint_array + 0); + valid_irq++; + } + if (irq_src & UDC_Setup) { + /* Device setup */ + omap1510_udc_setup (udc_device->bus->endpoint_array + 0); + valid_irq++; + } + /*if (!valid_irq) */ + /* serial_printf("unknown interrupt, IRQ_SRC %.4x\n", irq_src); */ + UDCDBGA ("< IRQ #%d end >", udc_interrupts); + udc_interrupts++; +} + +/* This function implements TRM Figure 14-26. */ +void omap1510_udc_noniso_irq (void) +{ + unsigned short epnum; + unsigned short irq_src = inw (UDC_IRQ_SRC); + int valid_irq = 0; + + if (!(irq_src & (UDC_EPn_RX | UDC_EPn_TX))) + return; + + UDCDBGA ("non-ISO IRQ, IRQ_SRC %x", inw (UDC_IRQ_SRC)); + + if (irq_src & UDC_EPn_RX) { /* Endpoint N OUT transaction */ + /* Determine the endpoint number for this interrupt */ + epnum = (inw (UDC_EPN_STAT) & 0x0f00) >> 8; + UDCDBGA ("RX on ep %x", epnum); + + /* acknowledge interrupt */ + outw (UDC_EPn_RX, UDC_IRQ_SRC); + + if (epnum) { + /* select the endpoint FIFO */ + outw (UDC_EP_Sel | epnum, UDC_EP_NUM); + + omap1510_udc_epn_rx (epnum); + + /* deselect the endpoint FIFO */ + outw (epnum, UDC_EP_NUM); + } + valid_irq++; + } + if (irq_src & UDC_EPn_TX) { /* Endpoint N IN transaction */ + /* Determine the endpoint number for this interrupt */ + epnum = (inw (UDC_EPN_STAT) & 0x000f) | USB_DIR_IN; + UDCDBGA ("TX on ep %x", epnum); + + /* acknowledge interrupt */ + outw (UDC_EPn_TX, UDC_IRQ_SRC); + + if (epnum) { + /* select the endpoint FIFO */ + outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); + + omap1510_udc_epn_tx (epnum); + + /* deselect the endpoint FIFO */ + outw (UDC_EP_Dir | epnum, UDC_EP_NUM); + } + valid_irq++; + } + if (!valid_irq) + serial_printf (": unknown non-ISO interrupt, IRQ_SRC %.4x\n", + irq_src); +} + +/* +------------------------------------------------------------------------------- +*/ + + +/* + * Start of public functions. + */ + +/* Called to start packet transmission. */ +int udc_endpoint_write (struct usb_endpoint_instance *endpoint) +{ + unsigned short epnum = + endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("Starting transmit on ep %x", epnum); + + if (endpoint->tx_urb) { + /* select the endpoint FIFO */ + outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); + /* write data to FIFO */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the endpoint FIFO */ + outw (UDC_EP_Dir | epnum, UDC_EP_NUM); + } + + return 0; +} + +/* Start to initialize h/w stuff */ +int udc_init (void) +{ + u16 udc_rev; + uchar value; + ulong gpio; + int i; + + /* Let the device settle down before we start */ + for (i = 0; i < UDC_INIT_MDELAY; i++) udelay(1000); + + udc_device = NULL; + + UDCDBG ("starting"); + + /* Check peripheral reset. Must be 1 to make sure + MPU TIPB peripheral reset is inactive */ + UDCREG (ARM_RSTCT2); + + /* Set and check clock control. + * We might ought to be using the clock control API to do + * this instead of fiddling with the clock registers directly + * here. + */ + outw ((1 << 4) | (1 << 5), CLOCK_CTRL); + UDCREG (CLOCK_CTRL); + +#ifdef CONFIG_OMAP1510 + /* This code was originally implemented for OMAP1510 and + * therefore is only applicable for OMAP1510 boards. For + * OMAP5912 or OMAP16xx the register APLL_CTRL does not + * exist and DPLL_CTRL is already configured. + */ + + /* Set and check APLL */ + outw (0x0008, APLL_CTRL); + UDCREG (APLL_CTRL); + /* Set and check DPLL */ + outw (0x2210, DPLL_CTRL); + UDCREG (DPLL_CTRL); +#endif + /* Set and check SOFT + * The below line of code has been changed to perform a + * read-modify-write instead of a simple write for + * configuring the SOFT_REQ register. This allows the code + * to be compatible with OMAP5912 and OMAP16xx devices + */ + outw ((1 << 4) | (1 << 3) | 1 | (inw(SOFT_REQ)), SOFT_REQ); + + /* Short delay to wait for DPLL */ + udelay (1000); + + /* Print banner with device revision */ + udc_rev = inw (UDC_REV) & 0xff; +#ifdef CONFIG_OMAP1510 + printf ("USB: TI OMAP1510 USB function module rev %d.%d\n", + udc_rev >> 4, udc_rev & 0xf); +#endif + +#ifdef CONFIG_OMAP1610 + printf ("USB: TI OMAP5912 USB function module rev %d.%d\n", + udc_rev >> 4, udc_rev & 0xf); +#endif + +#ifdef CONFIG_OMAP_SX1 + i2c_read (0x32, 0x04, 1, &value, 1); + value |= 0x04; + i2c_write (0x32, 0x04, 1, &value, 1); + + i2c_read (0x32, 0x03, 1, &value, 1); + value |= 0x01; + i2c_write (0x32, 0x03, 1, &value, 1); + + gpio = inl(GPIO_PIN_CONTROL_REG); + gpio |= 0x0002; /* A_IRDA_OFF */ + gpio |= 0x0800; /* A_SWITCH */ + gpio |= 0x8000; /* A_USB_ON */ + outl (gpio, GPIO_PIN_CONTROL_REG); + + gpio = inl(GPIO_DIR_CONTROL_REG); + gpio &= ~0x0002; /* A_IRDA_OFF */ + gpio &= ~0x0800; /* A_SWITCH */ + gpio &= ~0x8000; /* A_USB_ON */ + outl (gpio, GPIO_DIR_CONTROL_REG); + + gpio = inl(GPIO_DATA_OUTPUT_REG); + gpio |= 0x0002; /* A_IRDA_OFF */ + gpio &= ~0x0800; /* A_SWITCH */ + gpio &= ~0x8000; /* A_USB_ON */ + outl (gpio, GPIO_DATA_OUTPUT_REG); +#endif + + /* The VBUS_MODE bit selects whether VBUS detection is done via + * software (1) or hardware (0). When software detection is + * selected, VBUS_CTRL selects whether USB is not connected (0) + * or connected (1). + */ + outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); + outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); + UDCREGL (FUNC_MUX_CTRL_0); + + /* + * At this point, device is ready for configuration... + */ + + UDCDBG ("disable USB interrupts"); + outw (0, UDC_IRQ_EN); + UDCREG (UDC_IRQ_EN); + + UDCDBG ("disable USB DMA"); + outw (0, UDC_DMA_IRQ_EN); + UDCREG (UDC_DMA_IRQ_EN); + + UDCDBG ("initialize SYSCON1"); + outw (UDC_Self_Pwr | UDC_Pullup_En, UDC_SYSCON1); + UDCREG (UDC_SYSCON1); + + return 0; +} + +/* Stall endpoint */ +static void udc_stall_ep (unsigned int ep_addr) +{ + /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("stall ep_addr %d", ep_addr); + + /* REVISIT? + * The OMAP TRM section 14.2.4.2 says we must check that the FIFO + * is empty before halting the endpoint. The current implementation + * doesn't check that the FIFO is empty. + */ + + if (!ep_num) { + outw (UDC_Stall_Cmd, UDC_SYSCON2); + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { + /* we have a valid rx endpoint, so halt it */ + outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); + outw (UDC_Set_Halt, UDC_CTRL); + outw (ep_num, UDC_EP_NUM); + } + } else { + if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { + /* we have a valid tx endpoint, so halt it */ + outw (UDC_EP_Sel | UDC_EP_Dir | ep_num, UDC_EP_NUM); + outw (UDC_Set_Halt, UDC_CTRL); + outw (ep_num, UDC_EP_NUM); + } + } +} + +/* Reset endpoint */ +#if 0 +static void udc_reset_ep (unsigned int ep_addr) +{ + /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("reset ep_addr %d", ep_addr); + + if (!ep_num) { + /* control endpoint 0 can't be reset */ + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + UDCDBGA ("UDC_EP_RX(%d) = 0x%04x", ep_num, + inw (UDC_EP_RX (ep_num))); + if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { + /* we have a valid rx endpoint, so reset it */ + outw (ep_num | UDC_EP_Sel, UDC_EP_NUM); + outw (UDC_Reset_EP, UDC_CTRL); + outw (ep_num, UDC_EP_NUM); + UDCDBGA ("OUT endpoint %d reset", ep_num); + } + } else { + UDCDBGA ("UDC_EP_TX(%d) = 0x%04x", ep_num, + inw (UDC_EP_TX (ep_num))); + /* Resetting of tx endpoints seems to be causing the USB function + * module to fail, which causes problems when the driver is + * uninstalled. We'll skip resetting tx endpoints for now until + * we figure out what the problem is. + */ +#if 0 + if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { + /* we have a valid tx endpoint, so reset it */ + outw (ep_num | UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + outw (UDC_Reset_EP, UDC_CTRL); + outw (ep_num | UDC_EP_Dir, UDC_EP_NUM); + UDCDBGA ("IN endpoint %d reset", ep_num); + } +#endif + } +} +#endif + +/* ************************************************************************** */ + +/** + * udc_check_ep - check logical endpoint + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +#if 0 +int udc_check_ep (int logical_endpoint, int packetsize) +{ + if ((logical_endpoint == 0x80) || + ((logical_endpoint & 0x8f) != logical_endpoint)) { + return 0; + } + + switch (packetsize) { + case 8: + case 16: + case 32: + case 64: + case 128: + case 256: + case 512: + break; + default: + return 0; + } + + return EP_ADDR_TO_PHYS_EP (logical_endpoint); +} +#endif + +/* + * udc_setup_ep - setup endpoint + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, + unsigned int ep, struct usb_endpoint_instance *endpoint) +{ + UDCDBGA ("setting up endpoint addr %x", endpoint->endpoint_address); + + /* This routine gets called by bi_modinit for endpoint 0 and from + * bi_config for all of the other endpoints. bi_config gets called + * during the DEVICE_CREATE, DEVICE_CONFIGURED, and + * DEVICE_SET_INTERFACE events. We need to reconfigure the OMAP packet + * RAM after bi_config scans the selected device configuration and + * initializes the endpoint structures, but before this routine enables + * the OUT endpoint FIFOs. Since bi_config calls this routine in a + * loop for endpoints 1 through UDC_MAX_ENDPOINTS, we reconfigure our + * packet RAM here when ep==1. + * I really hate to do this here, but it seems like the API exported + * by the USB bus interface controller driver to the usbd-bi module + * isn't quite right so there is no good place to do this. + */ + if (ep == 1) { + omap1510_deconfigure_device (); + omap1510_configure_device (device); + } + + if (endpoint && (ep < UDC_MAX_ENDPOINTS)) { + int ep_addr = endpoint->endpoint_address; + + if (!ep_addr) { + /* nothing to do for endpoint 0 */ + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* nothing to do for IN (tx) endpoints */ + } else { /* OUT (rx) endpoint */ + if (endpoint->rcv_packetSize) { + /*struct urb* urb = &(urb_out_array[ep&0xFF]); */ + /*urb->endpoint = endpoint; */ + /*urb->device = device; */ + /*urb->buffer_length = sizeof(urb->buffer); */ + + /*endpoint->rcv_urb = urb; */ + omap1510_prepare_endpoint_for_rx (ep_addr); + } + } + } +} + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +#if 0 +void udc_disable_ep (unsigned int ep_addr) +{ + /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + struct usb_endpoint_instance *endpoint = omap1510_find_ep (ep_addr); /*udc_device->bus->endpoint_array + ep; */ + + UDCDBGA ("disable ep_addr %d", ep_addr); + + if (!ep_num) { + /* nothing to do for endpoint 0 */ ; + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + if (endpoint->tx_packetSize) { + /* we have a valid tx endpoint */ + /*usbd_flush_tx(endpoint); */ + endpoint->tx_urb = NULL; + } + } else { + if (endpoint->rcv_packetSize) { + /* we have a valid rx endpoint */ + /*usbd_flush_rcv(endpoint); */ + endpoint->rcv_urb = NULL; + } + } +} +#endif + +/* ************************************************************************** */ + +/** + * udc_connected - is the USB cable connected + * + * Return non-zero if cable is connected. + */ +#if 0 +int udc_connected (void) +{ + return ((inw (UDC_DEVSTAT) & UDC_ATT) == UDC_ATT); +} +#endif + +/* Turn on the USB connection by enabling the pullup resistor */ +void udc_connect (void) +{ + UDCDBG ("connect, enable Pullup"); + outl (0x00000018, FUNC_MUX_CTRL_D); +} + +/* Turn off the USB connection by disabling the pullup resistor */ +void udc_disconnect (void) +{ + UDCDBG ("disconnect, disable Pullup"); + outl (0x00000000, FUNC_MUX_CTRL_D); +} + +/* ************************************************************************** */ + + +/* + * udc_disable_interrupts - disable interrupts + * switch off interrupts + */ +#if 0 +void udc_disable_interrupts (struct usb_device_instance *device) +{ + UDCDBG ("disabling all interrupts"); + outw (0, UDC_IRQ_EN); +} +#endif + +/* ************************************************************************** */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +#if 0 +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} +#endif + +/* Switch on the UDC */ +void udc_enable (struct usb_device_instance *device) +{ + UDCDBGA ("enable device %p, status %d", device, device->status); + + /* initialize driver state variables */ + udc_devstat = 0; + + /* Save the device structure pointer */ + udc_device = device; + + /* Setup ep0 urb */ + if (!ep0_urb) { + ep0_urb = + usbd_alloc_urb (udc_device, + udc_device->bus->endpoint_array); + } else { + serial_printf ("udc_enable: ep0_urb already allocated %p\n", + ep0_urb); + } + + UDCDBG ("Check clock status"); + UDCREG (STATUS_REQ); + + /* The VBUS_MODE bit selects whether VBUS detection is done via + * software (1) or hardware (0). When software detection is + * selected, VBUS_CTRL selects whether USB is not connected (0) + * or connected (1). + */ + outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_CTRL | UDC_VBUS_MODE, + FUNC_MUX_CTRL_0); + UDCREGL (FUNC_MUX_CTRL_0); + + omap1510_configure_device (device); +} + +/* Switch off the UDC */ +void udc_disable (void) +{ + UDCDBG ("disable UDC"); + + omap1510_deconfigure_device (); + + /* The VBUS_MODE bit selects whether VBUS detection is done via + * software (1) or hardware (0). When software detection is + * selected, VBUS_CTRL selects whether USB is not connected (0) + * or connected (1). + */ + outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); + outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); + UDCREGL (FUNC_MUX_CTRL_0); + + /* Free ep0 URB */ + if (ep0_urb) { + /*usbd_dealloc_urb(ep0_urb); */ + ep0_urb = NULL; + } + + /* Reset device pointer. + * We ought to do this here to balance the initialization of udc_device + * in udc_enable, but some of our other exported functions get called + * by the bus interface driver after udc_disable, so we have to hang on + * to the device pointer to avoid a null pointer dereference. */ + /* udc_device = NULL; */ +} + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq (device, DEVICE_INIT, 0); + + /* The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq (device, DEVICE_CREATE, 0); + + /* Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. + * The OMAP USB client controller has the capability to detect when the + * USB cable is connected to a powered USB bus via the ATT bit in the + * DEVSTAT register, so we will defer the DEVICE_HUB_CONFIGURED and + * DEVICE_RESET events until later. + */ + + 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 */ +} diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile new file mode 100644 index 0000000..ec1d689 --- /dev/null +++ b/drivers/usb/host/Makefile @@ -0,0 +1,59 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB := $(obj)libusb_host.a + +# ohci +COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o +COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o +COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o +COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o +COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx-hcd.o +COBJS-$(CONFIG_USB_SL811HS) += sl811-hcd.o + +# echi +COBJS-$(CONFIG_USB_EHCI) += ehci-hcd.o +COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o +COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o +COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/host/ehci-core.h b/drivers/usb/host/ehci-core.h new file mode 100644 index 0000000..39e5c5e --- /dev/null +++ b/drivers/usb/host/ehci-core.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB + * All rights reserved. + * + * 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 version 2 of + * the License. + * + * 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 USB_EHCI_CORE_H +#define USB_EHCI_CORE_H + +extern int rootdev; +extern struct ehci_hccr *hccr; +extern volatile struct ehci_hcor *hcor; + +#endif diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c new file mode 100644 index 0000000..86ee1d5 --- /dev/null +++ b/drivers/usb/host/ehci-fsl.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * 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 +#include +#include +#include +#include +#include + +#include "ehci.h" +#include "ehci-fsl.h" +#include "ehci-core.h" + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + * + * Excerpts from linux ehci fsl driver. + */ +int ehci_hcd_init(void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + uint32_t addr, temp; + + addr = (uint32_t)&(im->usb[0]); + hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + /* Configure clock */ + clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); + + /* Confgure interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp + | REFSEL_16MHZ | UTMI_PHY_EN); + + /* Wait for clock to stabilize */ + do { + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + udelay(1000); + } while (!(temp & PHY_CLK_VALID)); + + /* Set to Host mode */ + temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST); + + out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB); + out_be32((void *)(addr + FSL_SOC_USB_SNOOP2), + 0x80000000 | SNOOP_SIZE_2GB); + + /* Init phy */ + /* TODO: handle different phys? */ + out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI); + + /* Enable interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c); + out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040); + out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001); + + /* Enable interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h new file mode 100644 index 0000000..c429af1 --- /dev/null +++ b/drivers/usb/host/ehci-fsl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2005 freescale semiconductor + * Copyright (c) 2005 MontaVista Software + * Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB + * + * 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 _EHCI_FSL_H +#define _EHCI_FSL_H + +/* Global offsets */ +#define FSL_SKIP_PCI 0x100 + +/* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_ULPIVP 0x170 +#define FSL_SOC_USB_PORTSC1 0x184 +#define PORT_PTS_MSK (3 << 30) +#define PORT_PTS_UTMI (0 << 30) +#define PORT_PTS_ULPI (2 << 30) +#define PORT_PTS_SERIAL (3 << 30) +#define PORT_PTS_PTW (1 << 28) + +/* USBMODE Register bits */ +#define CM_IDLE (0 << 0) +#define CM_RESERVED (1 << 0) +#define CM_DEVICE (2 << 0) +#define CM_HOST (3 << 0) +#define USBMODE_RESERVED_2 (0 << 2) +#define SLOM (1 << 3) +#define SDIS (1 << 4) + +/* CONTROL Register bits */ +#define ULPI_INT_EN (1 << 0) +#define WU_INT_EN (1 << 1) +#define USB_EN (1 << 2) +#define LSF_EN (1 << 3) +#define KEEP_OTG_ON (1 << 4) +#define OTG_PORT (1 << 5) +#define REFSEL_12MHZ (0 << 6) +#define REFSEL_16MHZ (1 << 6) +#define REFSEL_48MHZ (2 << 6) +#define PLL_RESET (1 << 8) +#define UTMI_PHY_EN (1 << 9) +#define PHY_CLK_SEL_UTMI (0 << 10) +#define PHY_CLK_SEL_ULPI (1 << 10) +#define CLKIN_SEL_USB_CLK (0 << 11) +#define CLKIN_SEL_USB_CLK2 (1 << 11) +#define CLKIN_SEL_SYS_CLK (2 << 11) +#define CLKIN_SEL_SYS_CLK2 (3 << 11) +#define RESERVED_18 (0 << 13) +#define RESERVED_17 (0 << 14) +#define RESERVED_16 (0 << 15) +#define WU_INT (1 << 16) +#define PHY_CLK_VALID (1 << 17) + +#define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ +#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ +#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#define SNOOP_SIZE_2GB 0x1e + +/* System Clock Control Register */ +#define MPC83XX_SCCR_USB_MASK 0x00f00000 +#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 +#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 +#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 + +#endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c new file mode 100644 index 0000000..bbd547b --- /dev/null +++ b/drivers/usb/host/ehci-hcd.c @@ -0,0 +1,883 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB + * Copyright (c) 2008, Michael Trimarchi + * + * All rights reserved. + * + * 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 version 2 of + * the License. + * + * 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 +#include +#include +#include +#include + +#include "ehci.h" + +int rootdev; +struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ +volatile struct ehci_hcor *hcor; + +static uint16_t portreset; +static struct QH qh_list __attribute__((aligned(32))); + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_linux_config_descriptor config; + struct usb_linux_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)) descriptor = { + { + 0x8, /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, /* wHubCharacteristics */ + 0xff, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + {}, /* Device removable */ + {} /* at most 7 ports! XXX */ + }, + { + 0x12, /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + 0x0002, /* bcdUSB: v2.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ + 64, /* bMaxPacketSize: 64 bytes */ + 0x0000, /* idVendor */ + 0x0000, /* idProduct */ + 0x0001, /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + cpu_to_le16(0x19), + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: + * UE_DIR_IN | EHCI_INTR_ENDPT + */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, 0, /* wMaxPacketSize */ + 255 /* bInterval */ + }, +}; + +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + +#if defined(CONFIG_EHCI_DCACHE) +/* + * Routines to handle (flush/invalidate) the dcache for the QH and qTD + * structures and data buffers. This is needed on platforms using this + * EHCI support with dcache enabled. + */ +static void flush_invalidate(u32 addr, int size, int flush) +{ + if (flush) + flush_dcache_range(addr, addr + size); + else + invalidate_dcache_range(addr, addr + size); +} + +static void cache_qtd(struct qTD *qtd, int flush) +{ + u32 *ptr = (u32 *)qtd->qt_buffer[0]; + int len = (qtd->qt_token & 0x7fff0000) >> 16; + + flush_invalidate((u32)qtd, sizeof(struct qTD), flush); + if (ptr && len) + flush_invalidate((u32)ptr, len, flush); +} + + +static inline struct QH *qh_addr(struct QH *qh) +{ + return (struct QH *)((u32)qh & 0xffffffe0); +} + +static void cache_qh(struct QH *qh, int flush) +{ + struct qTD *qtd; + struct qTD *next; + static struct qTD *first_qtd; + + /* + * Walk the QH list and flush/invalidate all entries + */ + while (1) { + flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush); + if ((u32)qh & QH_LINK_TYPE_QH) + break; + qh = qh_addr(qh); + qh = (struct QH *)qh->qh_link; + } + qh = qh_addr(qh); + + /* + * Save first qTD pointer, needed for invalidating pass on this QH + */ + if (flush) + first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay & + 0xffffffe0); + else + qtd = first_qtd; + + /* + * Walk the qTD list and flush/invalidate all entries + */ + while (1) { + if (qtd == NULL) + break; + cache_qtd(qtd, flush); + next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0); + if (next == qtd) + break; + qtd = next; + } +} + +static inline void ehci_flush_dcache(struct QH *qh) +{ + cache_qh(qh, 1); +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ + cache_qh(qh, 0); +} +#else /* CONFIG_EHCI_DCACHE */ +/* + * + */ +static inline void ehci_flush_dcache(struct QH *qh) +{ +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ +} +#endif /* CONFIG_EHCI_DCACHE */ + +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) +{ + uint32_t result; + do { + result = ehci_readl(ptr); + if (result == ~(uint32_t)0) + return -1; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -1; +} + +static void ehci_free(void *p, size_t sz) +{ + +} + +static int ehci_reset(void) +{ + uint32_t cmd; + uint32_t tmp; + uint32_t *reg_ptr; + int ret = 0; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_RESET; + ehci_writel(&hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); + if (ret < 0) { + printf("EHCI fail to reset\n"); + goto out; + } + + if (ehci_is_TDI()) { + reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); + } +out: + return ret; +} + +static void *ehci_alloc(size_t sz, size_t align) +{ + static struct QH qh __attribute__((aligned(32))); + static struct qTD td[3] __attribute__((aligned (32))); + static int ntds; + void *p; + + switch (sz) { + case sizeof(struct QH): + p = &qh; + ntds = 0; + break; + case sizeof(struct qTD): + if (ntds == 3) { + debug("out of TDs\n"); + return NULL; + } + p = &td[ntds]; + ntds++; + break; + default: + debug("unknown allocation size\n"); + return NULL; + } + + memset(p, sz, 0); + return p; +} + +static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) +{ + uint32_t addr, delta, next; + int idx; + + addr = (uint32_t) buf; + idx = 0; + while (idx < 5) { + td->qt_buffer[idx] = cpu_to_hc32(addr); + next = (addr + 4096) & ~4095; + delta = next - addr; + if (delta >= sz) + break; + sz -= delta; + addr = next; + idx++; + } + + if (idx == 5) { + debug("out of buffer pointers (%u bytes left)\n", sz); + return -1; + } + + return 0; +} + +static int +ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + struct QH *qh; + struct qTD *td; + volatile struct qTD *vtd; + unsigned long ts; + uint32_t *tdp; + uint32_t endpt, token, usbsts; + uint32_t c, toggle; + uint32_t cmd; + int ret = 0; + + debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, + buffer, length, req); + if (req != NULL) + debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index)); + + qh = ehci_alloc(sizeof(struct QH), 32); + if (qh == NULL) { + debug("unable to allocate QH\n"); + return -1; + } + qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && + usb_pipeendpoint(pipe) == 0) ? 1 : 0; + endpt = (8 << 28) | + (c << 27) | + (usb_maxpacket(dev, pipe) << 16) | + (0 << 15) | + (1 << 14) | + (usb_pipespeed(pipe) << 12) | + (usb_pipeendpoint(pipe) << 8) | + (0 << 7) | (usb_pipedevice(pipe) << 0); + qh->qh_endpt1 = cpu_to_hc32(endpt); + endpt = (1 << 30) | + (dev->portnr << 23) | + (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); + qh->qh_endpt2 = cpu_to_hc32(endpt); + qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + + td = NULL; + tdp = &qh->qh_overlay.qt_next; + + toggle = + usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + + if (req != NULL) { + td = ehci_alloc(sizeof(struct qTD), 32); + if (td == NULL) { + debug("unable to allocate SETUP td\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (0 << 31) | + (sizeof(*req) << 16) | + (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { + debug("unable construct SETUP td\n"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_hc32((uint32_t) td); + tdp = &td->qt_next; + toggle = 1; + } + + if (length > 0 || req == NULL) { + td = ehci_alloc(sizeof(struct qTD), 32); + if (td == NULL) { + debug("unable to allocate DATA td\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (toggle << 31) | + (length << 16) | + ((req == NULL ? 1 : 0) << 15) | + (0 << 12) | + (3 << 10) | + ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(td, buffer, length) != 0) { + debug("unable construct DATA td\n"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_hc32((uint32_t) td); + tdp = &td->qt_next; + } + + if (req != NULL) { + td = ehci_alloc(sizeof(struct qTD), 32); + if (td == NULL) { + debug("unable to allocate ACK td\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (toggle << 31) | + (0 << 16) | + (1 << 15) | + (0 << 12) | + (3 << 10) | + ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + *tdp = cpu_to_hc32((uint32_t) td); + tdp = &td->qt_next; + } + + qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); + + /* Flush dcache */ + ehci_flush_dcache(&qh_list); + + usbsts = ehci_readl(&hcor->or_usbsts); + ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); + + /* Enable async. schedule. */ + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STD_ASS set\n"); + goto fail; + } + + /* Wait for TDs to be processed. */ + ts = get_timer(0); + vtd = td; + do { + /* Invalidate dcache */ + ehci_invalidate_dcache(&qh_list); + token = hc32_to_cpu(vtd->qt_token); + if (!(token & 0x80)) + break; + } while (get_timer(ts) < CONFIG_SYS_HZ); + + /* Disable async schedule. */ + cmd = ehci_readl(&hcor->or_usbcmd); + cmd &= ~CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STD_ASS reset\n"); + goto fail; + } + + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + + token = hc32_to_cpu(qh->qh_overlay.qt_token); + if (!(token & 0x80)) { + debug("TOKEN=%#x\n", token); + switch (token & 0xfc) { + case 0: + toggle = token >> 31; + usb_settoggle(dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), toggle); + dev->status = 0; + break; + case 0x40: + dev->status = USB_ST_STALLED; + break; + case 0xa0: + case 0x20: + dev->status = USB_ST_BUF_ERR; + break; + case 0x50: + case 0x10: + dev->status = USB_ST_BABBLE_DET; + break; + default: + dev->status = USB_ST_CRC_ERR; + break; + } + dev->act_len = length - ((token >> 16) & 0x7fff); + } else { + dev->act_len = 0; + debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", + dev->devnum, ehci_readl(&hcor->or_usbsts), + ehci_readl(&hcor->or_portsc[0]), + ehci_readl(&hcor->or_portsc[1])); + } + + return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; + +fail: + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); + while (td != (void *)QT_NEXT_TERMINATE) { + qh->qh_overlay.qt_next = td->qt_next; + ehci_free(td, sizeof(*td)); + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); + } + ehci_free(qh, sizeof(*qh)); + return -1; +} + +static inline int min3(int a, int b, int c) +{ + + if (b < a) + a = b; + if (c < a) + a = c; + return a; +} + +int +ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + u16 typeReq; + void *srcptr = NULL; + int len, srclen; + uint32_t reg; + uint32_t *status_reg; + + if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + printf("The request port(%d) is not configured\n", + le16_to_cpu(req->index) - 1); + return -1; + } + status_reg = (uint32_t *)&hcor->or_portsc[ + le16_to_cpu(req->index) - 1]; + srclen = 0; + + debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->index)); + + typeReq = req->request << 8 | req->requesttype; + + switch (le16_to_cpu(typeReq)) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + debug("USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = 0x12; + break; + case USB_DT_CONFIG: + debug("USB_DT_CONFIG config\n"); + srcptr = &descriptor.config; + srclen = 0x19; + break; + case USB_DT_STRING: + debug("USB_DT_STRING config\n"); + switch (le16_to_cpu(req->value) & 0xff) { + case 0: /* Language */ + srcptr = "\4\3\1\0"; + srclen = 4; + break; + case 1: /* Vendor */ + srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product */ + srcptr = "\52\3E\0H\0C\0I\0 " + "\0H\0o\0s\0t\0 " + "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + break; + default: + debug("unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); + goto unknown; + } + break; + default: + debug("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_HUB: + debug("USB_DT_HUB config\n"); + srcptr = &descriptor.hub; + srclen = 0x8; + break; + default: + debug("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + debug("USB_REQ_SET_ADDRESS\n"); + rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + debug("USB_REQ_SET_CONFIGURATION\n"); + /* Nothing to do */ + break; + case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + memset(tmpbuf, 0, 4); + reg = ehci_readl(status_reg); + if (reg & EHCI_PS_CS) + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + if (reg & EHCI_PS_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if (reg & EHCI_PS_SUSP) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if (reg & EHCI_PS_OCA) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & EHCI_PS_PR && + (portreset & (1 << le16_to_cpu(req->index)))) { + int ret; + /* force reset to complete */ + reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); + ehci_writel(status_reg, reg); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); + if (!ret) + tmpbuf[0] |= USB_PORT_STAT_RESET; + else + printf("port(%d) reset error\n", + le16_to_cpu(req->index) - 1); + } + if (reg & EHCI_PS_PP) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + + if (ehci_is_TDI()) { + switch ((reg >> 26) & 3) { + case 0: + break; + case 1: + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + break; + case 2: + default: + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + break; + } + } else { + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + } + + if (reg & EHCI_PS_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & EHCI_PS_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & EHCI_PS_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (portreset & (1 << le16_to_cpu(req->index))) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + + srcptr = tmpbuf; + srclen = 4; + break; + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= EHCI_PS_PE; + ehci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } + break; + case USB_PORT_FEAT_RESET: + if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && + !ehci_is_TDI() && + EHCI_PS_IS_LOWSPEED(reg)) { + /* Low speed device, give up ownership. */ + debug("port %d low speed --> companion\n", + req->index - 1); + reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); + break; + } else { + reg |= EHCI_PS_PR; + reg &= ~EHCI_PS_PE; + ehci_writel(status_reg, reg); + /* + * caller must wait, then call GetPortStatus + * usb 2.0 specification say 50 ms resets on + * root + */ + wait_ms(50); + portreset |= 1 << le16_to_cpu(req->index); + } + break; + default: + debug("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + /* unblock posted writes */ + ehci_readl(&hcor->or_usbcmd); + break; + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~EHCI_PS_PE; + break; + case USB_PORT_FEAT_C_ENABLE: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) + reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); + case USB_PORT_FEAT_C_CONNECTION: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; + break; + case USB_PORT_FEAT_OVER_CURRENT: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; + break; + case USB_PORT_FEAT_C_RESET: + portreset &= ~(1 << le16_to_cpu(req->index)); + break; + default: + debug("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + ehci_writel(status_reg, reg); + /* unblock posted write */ + ehci_readl(&hcor->or_usbcmd); + break; + default: + debug("Unknown request\n"); + goto unknown; + } + + wait_ms(1); + len = min3(srclen, le16_to_cpu(req->length), length); + if (srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + else + debug("Len is 0\n"); + + dev->act_len = len; + dev->status = 0; + return 0; + +unknown: + debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n", + req->requesttype, req->request, le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->length)); + + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return -1; +} + +int usb_lowlevel_stop(void) +{ + return ehci_hcd_stop(); +} + +int usb_lowlevel_init(void) +{ + uint32_t reg; + uint32_t cmd; + + if (ehci_hcd_init() != 0) + return -1; + + /* EHCI spec section 4.1 */ + if (ehci_reset() != 0) + return -1; + +#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) + if (ehci_hcd_init() != 0) + return -1; +#endif + + /* Set head of reclaim list */ + memset(&qh_list, 0, sizeof(qh_list)); + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); + + /* Set async. queue head pointer. */ + ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); + + reg = ehci_readl(&hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); + printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); + /* Port Indicators */ + if (HCS_INDICATOR(reg)) + descriptor.hub.wHubCharacteristics |= 0x80; + /* Port Power Control */ + if (HCS_PPC(reg)) + descriptor.hub.wHubCharacteristics |= 0x01; + + /* Start the host controller. */ + cmd = ehci_readl(&hcor->or_usbcmd); + /* + * Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't + */ + cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + cmd |= CMD_RUN; + ehci_writel(&hcor->or_usbcmd, cmd); + + /* take control over the ports */ + cmd = ehci_readl(&hcor->or_configflag); + cmd |= FLAG_CF; + ehci_writel(&hcor->or_configflag, cmd); + /* unblock posted write */ + cmd = ehci_readl(&hcor->or_usbcmd); + wait_ms(5); + reg = HC_VERSION(ehci_readl(&hccr->cr_capbase)); + printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); + + rootdev = 0; + + return 0; +} + +int +submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length) +{ + + if (usb_pipetype(pipe) != PIPE_BULK) { + debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +int +submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *setup) +{ + + if (usb_pipetype(pipe) != PIPE_CONTROL) { + debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + + if (usb_pipedevice(pipe) == rootdev) { + if (rootdev == 0) + dev->speed = USB_SPEED_HIGH; + return ehci_submit_root(dev, pipe, buffer, length, setup); + } + return ehci_submit_async(dev, pipe, buffer, length, setup); +} + +int +submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, int interval) +{ + + debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", + dev, pipe, buffer, length, interval); + return -1; +} diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c new file mode 100644 index 0000000..b8f15ae --- /dev/null +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -0,0 +1,50 @@ +/* + * (C) Copyright 2008, Michael Trimarchi + * + * Author: Michael Trimarchi + * This code is based on ehci freescale driver + * + * 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 +#include + +#include "ehci.h" +#include "ehci-core.h" +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + hccr = (struct ehci_hccr *)(0xcd000100); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + printf("IXP4XX init hccr %x and hcor %x hc_length %d\n", + (uint32_t)hccr, (uint32_t)hcor, + (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c new file mode 100644 index 0000000..441b1a2 --- /dev/null +++ b/drivers/usb/host/ehci-pci.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * All rights reserved. + * + * 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 version 2 of + * the License. + * + * 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 +#include +#include + +#include "ehci.h" +#include "ehci-core.h" + +#ifdef CONFIG_PCI_EHCI_DEVICE +static struct pci_device_id ehci_pci_ids[] = { + /* Please add supported PCI EHCI controller ids here */ + {0, 0} +}; +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + pci_dev_t pdev; + uint32_t addr; + + pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); + if (pdev == -1) { + printf("EHCI host controller not found\n"); + return -1; + } + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr); + hccr = (struct ehci_hccr *)addr; + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/host/ehci-vct.c b/drivers/usb/host/ehci-vct.c new file mode 100644 index 0000000..3063dd1 --- /dev/null +++ b/drivers/usb/host/ehci-vct.c @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2009 Stefan Roese , DENX Software Engineering + * + * 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 +#include + +#include "ehci.h" +#include "ehci-core.h" + +int vct_ehci_hcd_init(u32 *hccr, u32 *hcor); + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + int ret; + u32 vct_hccr; + u32 vct_hcor; + + /* + * Init VCT specific stuff + */ + ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor); + if (ret) + return ret; + + hccr = (struct ehci_hccr *)vct_hccr; + hcor = (struct ehci_hcor *)vct_hcor; + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h new file mode 100644 index 0000000..b3c1d5d --- /dev/null +++ b/drivers/usb/host/ehci.h @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi + * All rights reserved. + * + * 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 version 2 of + * the License. + * + * 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 USB_EHCI_H +#define USB_EHCI_H + +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 +#endif + +/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ +#define DeviceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) + +#define DeviceOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) + +#define InterfaceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +/* + * Register Space. + */ +struct ehci_hccr { + uint32_t cr_capbase; +#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) +#define HC_VERSION(p) (((p) >> 16) & 0xffff) + uint32_t cr_hcsparams; +#define HCS_PPC(p) ((p) & (1 << 4)) +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) + uint32_t cr_hccparams; + uint8_t cr_hcsp_portrt[8]; +} __attribute__ ((packed)); + +struct ehci_hcor { + uint32_t or_usbcmd; +#define CMD_PARK (1 << 11) /* enable "park" */ +#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ +#define CMD_ASE (1 << 5) /* async schedule enable */ +#define CMD_LRESET (1 << 7) /* partial reset */ +#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_PSE (1 << 4) /* periodic schedule enable */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ + uint32_t or_usbsts; +#define STD_ASS (1 << 15) +#define STS_HALT (1 << 12) + uint32_t or_usbintr; + uint32_t or_frindex; + uint32_t or_ctrldssegment; + uint32_t or_periodiclistbase; + uint32_t or_asynclistaddr; + uint32_t _reserved_[9]; + uint32_t or_configflag; +#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ + uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; + uint32_t or_systune; +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1 << 3) /* Stream disable */ +#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ +#define USBMODE_CM_HC (3 << 0) /* host controller mode */ +#define USBMODE_CM_IDLE (0 << 0) /* idle state */ + +/* Interface descriptor */ +struct usb_linux_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)); + +/* Configuration descriptor information.. */ +struct usb_linux_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; +} __attribute__ ((packed)); + +#if defined CONFIG_EHCI_DESC_BIG_ENDIAN +#define ehci_readl(x) (*((volatile u32 *)(x))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) +#else +#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ + cpu_to_le32(((volatile u32)b))) +#endif + +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#define hc32_to_cpu(x) be32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_be32((x)) +#else +#define hc32_to_cpu(x) le32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_le32((x)) +#endif + +#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ +#define EHCI_PS_PO (1 << 13) /* RW port owner */ +#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ +#define EHCI_PS_LS (3 << 10) /* RO line status */ +#define EHCI_PS_PR (1 << 8) /* RW port reset */ +#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ +#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ +#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ +#define EHCI_PS_OCA (1 << 4) /* RO over current active */ +#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ +#define EHCI_PS_PE (1 << 2) /* RW port enable */ +#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ +#define EHCI_PS_CS (1 << 0) /* RO connect status */ +#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) + +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) + +/* + * Schedule Interface Space. + * + * IMPORTANT: Software must ensure that no interface data structure + * reachable by the EHCI host controller spans a 4K page boundary! + * + * Periodic transfers (i.e. isochronous and interrupt transfers) are + * not supported. + */ + +/* Queue Element Transfer Descriptor (qTD). */ +struct qTD { + uint32_t qt_next; +#define QT_NEXT_TERMINATE 1 + uint32_t qt_altnext; + uint32_t qt_token; + uint32_t qt_buffer[5]; +}; + +/* Queue Head (QH). */ +struct QH { + uint32_t qh_link; +#define QH_LINK_TERMINATE 1 +#define QH_LINK_TYPE_ITD 0 +#define QH_LINK_TYPE_QH 2 +#define QH_LINK_TYPE_SITD 4 +#define QH_LINK_TYPE_FSTN 6 + uint32_t qh_endpt1; + uint32_t qh_endpt2; + uint32_t qh_curtd; + struct qTD qh_overlay; + /* + * Add dummy fill value to make the size of this struct + * aligned to 32 bytes + */ + uint8_t fill[16]; +}; + +/* Low level init functions */ +int ehci_hcd_init(void); +int ehci_hcd_stop(void); + +#endif /* USB_EHCI_H */ diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c new file mode 100644 index 0000000..348e404 --- /dev/null +++ b/drivers/usb/host/isp116x-hcd.c @@ -0,0 +1,1441 @@ +/* + * ISP116x HCD (Host Controller Driver) for u-boot. + * + * Copyright (C) 2006-2007 Rodolfo Giometti + * Copyright (C) 2006-2007 Eurotech S.p.A. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Derived in part from the SL811 HCD driver "u-boot/drivers/usb/sl811_usb.c" + * (original copyright message follows): + * + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + * Adam Richter, Gregory P. Smith; + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua + * + * [[GNU/GPL disclaimer]] + * + * and in part from AU1x00 OHCI HCD driver "u-boot/cpu/mips/au1x00_usb_ohci.c" + * (original copyright message follows): + * + * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * [[GNU/GPL disclaimer]] + * + * Note: Part of this code has been derived from linux + */ + +#include +#include +#include +#include +#include + +/* + * ISP116x chips require certain delays between accesses to its + * registers. The following timing options exist. + * + * 1. Configure your memory controller (the best) + * 2. Use ndelay (easiest, poorest). For that, enable the following macro. + * + * Value is in microseconds. + */ +#ifdef ISP116X_HCD_USE_UDELAY +#define UDELAY 1 +#endif + +/* + * On some (slowly?) machines an extra delay after data packing into + * controller's FIFOs is required, * otherwise you may get the following + * error: + * + * uboot> usb start + * (Re)start USB... + * USB: scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT + * isp116x: isp116x_submit_job: ****** FIFO not ready! ****** + * + * USB device not responding, giving up (status=4) + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * 3 USB Device(s) found + * scanning bus for storage devices... 0 Storage Device(s) found + * + * Value is in milliseconds. + */ +#ifdef ISP116X_HCD_USE_EXTRA_DELAY +#define EXTRA_DELAY 2 +#endif + +/* + * Enable the following defines if you wish enable debugging messages. + */ +#undef DEBUG /* enable debugging messages */ +#undef TRACE /* enable tracing code */ +#undef VERBOSE /* verbose debugging messages */ + +#include "isp116x.h" + +#define DRIVER_VERSION "08 Jan 2007" +static const char hcd_name[] = "isp116x-hcd"; + +struct isp116x isp116x_dev; +struct isp116x_platform_data isp116x_board; +static int got_rhsc; /* root hub status change */ +struct usb_device *devgone; /* device which was disconnected */ +static int rh_devnum; /* address of Root Hub endpoint */ + +/* ------------------------------------------------------------------------- */ + +#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL)) +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +/* ------------------------------------------------------------------------- */ + +static int isp116x_reset(struct isp116x *isp116x); + +/* --- Debugging functions ------------------------------------------------- */ + +#define isp116x_show_reg(d, r) { \ + if ((r) < 0x20) { \ + DBG("%-12s[%02x]: %08x", #r, \ + r, isp116x_read_reg32(d, r)); \ + } else { \ + DBG("%-12s[%02x]: %04x", #r, \ + r, isp116x_read_reg16(d, r)); \ + } \ +} + +#define isp116x_show_regs(d) { \ + isp116x_show_reg(d, HCREVISION); \ + isp116x_show_reg(d, HCCONTROL); \ + isp116x_show_reg(d, HCCMDSTAT); \ + isp116x_show_reg(d, HCINTSTAT); \ + isp116x_show_reg(d, HCINTENB); \ + isp116x_show_reg(d, HCFMINTVL); \ + isp116x_show_reg(d, HCFMREM); \ + isp116x_show_reg(d, HCFMNUM); \ + isp116x_show_reg(d, HCLSTHRESH); \ + isp116x_show_reg(d, HCRHDESCA); \ + isp116x_show_reg(d, HCRHDESCB); \ + isp116x_show_reg(d, HCRHSTATUS); \ + isp116x_show_reg(d, HCRHPORT1); \ + isp116x_show_reg(d, HCRHPORT2); \ + isp116x_show_reg(d, HCHWCFG); \ + isp116x_show_reg(d, HCDMACFG); \ + isp116x_show_reg(d, HCXFERCTR); \ + isp116x_show_reg(d, HCuPINT); \ + isp116x_show_reg(d, HCuPINTENB); \ + isp116x_show_reg(d, HCCHIPID); \ + isp116x_show_reg(d, HCSCRATCH); \ + isp116x_show_reg(d, HCITLBUFLEN); \ + isp116x_show_reg(d, HCATLBUFLEN); \ + isp116x_show_reg(d, HCBUFSTAT); \ + isp116x_show_reg(d, HCRDITL0LEN); \ + isp116x_show_reg(d, HCRDITL1LEN); \ +} + +#if defined(TRACE) + +static int isp116x_get_current_frame_number(struct usb_device *usb_dev) +{ + struct isp116x *isp116x = &isp116x_dev; + + return isp116x_read_reg32(isp116x, HCFMNUM); +} + +static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, char *str) +{ +#if defined(VERBOSE) + int i; +#endif + + DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx", + str, + isp116x_get_current_frame_number(dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe) ? 'O' : 'I', + usb_pipetype(pipe) < 2 ? + (usb_pipeint(pipe) ? + "INTR" : "ISOC") : + (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status); +#if defined(VERBOSE) + if (len > 0 && buffer) { + printf(__FILE__ ": data(%d):", len); + for (i = 0; i < 16 && i < len; i++) + printf(" %02x", ((__u8 *) buffer)[i]); + printf("%s\n", i < len ? "..." : ""); + } +#endif +} + +#define PTD_DIR_STR(ptd) ({char __c; \ + switch(PTD_GET_DIR(ptd)){ \ + case 0: __c = 's'; break; \ + case 1: __c = 'o'; break; \ + default: __c = 'i'; break; \ + }; __c;}) + +/* + Dump PTD info. The code documents the format + perfectly, right :) +*/ +static inline void dump_ptd(struct ptd *ptd) +{ +#if defined(VERBOSE) + int k; +#endif + + DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x", + PTD_GET_CC(ptd), + PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd), + PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), + PTD_GET_TOGGLE(ptd), + PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd)); +#if defined(VERBOSE) + printf("isp116x: %s: PTD(byte): ", __FUNCTION__); + for (k = 0; k < sizeof(struct ptd); ++k) + printf("%02x ", ((u8 *) ptd)[k]); + printf("\n"); +#endif +} + +static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type) +{ +#if defined(VERBOSE) + int k; + + if (type == 0 /* 0ut data */ ) { + printf("isp116x: %s: out data: ", __FUNCTION__); + for (k = 0; k < PTD_GET_LEN(ptd); ++k) + printf("%02x ", ((u8 *) buf)[k]); + printf("\n"); + } + if (type == 1 /* 1n data */ ) { + printf("isp116x: %s: in data: ", __FUNCTION__); + for (k = 0; k < PTD_GET_COUNT(ptd); ++k) + printf("%02x ", ((u8 *) buf)[k]); + printf("\n"); + } + + if (PTD_GET_LAST(ptd)) + DBG("--- last PTD ---"); +#endif +} + +#else + +#define dump_msg(dev, pipe, buffer, len, str) do { } while (0) +#define dump_pkt(dev, pipe, buffer, len, setup, str, small) do {} while (0) + +#define dump_ptd(ptd) do {} while (0) +#define dump_ptd_data(ptd, buf, type) do {} while (0) + +#endif + +/* --- Virtual Root Hub ---------------------------------------------------- */ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = { + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x00, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x02, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = { + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = { + 0x22, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'S', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'P', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '1', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '1', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '6', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'x', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* + * Hub class-specific descriptor is constructed dynamically + */ + +/* --- Virtual root hub management functions ------------------------------- */ + +static int rh_check_port_status(struct isp116x *isp116x) +{ + u32 temp, ndp, i; + int res; + + res = -1; + temp = isp116x_read_reg32(isp116x, HCRHSTATUS); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +/* --- HC management functions --------------------------------------------- */ + +/* Write len bytes to fifo, pad till 32-bit boundary + */ +static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = *dp++; + w |= *dp++ << 8; + isp116x_raw_write_data16(isp116x, w); + } + if (len) + isp116x_write_data16(isp116x, (u16) * dp); + } else { + /* aligned */ + for (; len > 1; len -= 2) + isp116x_raw_write_data16(isp116x, *dp2++); + if (len) + isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); + } + if (quot == 1 || quot == 2) + isp116x_raw_write_data16(isp116x, 0); +} + +/* Read len bytes from fifo and then read till 32-bit boundary + */ +static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = isp116x_raw_read_data16(isp116x); + *dp++ = w & 0xff; + *dp++ = (w >> 8) & 0xff; + } + if (len) + *dp = 0xff & isp116x_read_data16(isp116x); + } else { + /* aligned */ + for (; len > 1; len -= 2) + *dp2++ = isp116x_raw_read_data16(isp116x); + if (len) + *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); + } + if (quot == 1 || quot == 2) + isp116x_raw_read_data16(isp116x); +} + +/* Write PTD's and data for scheduled transfers into the fifo ram. + * Fifo must be empty and ready */ +static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev, + unsigned long pipe, struct ptd *ptd, int n, void *data, + int len) +{ + int buflen = n * sizeof(struct ptd) + len; + int i, done; + + DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); + + done = 0; + for (i = 0; i < n; i++) { + DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + + dump_ptd(&ptd[i]); + isp116x_write_data16(isp116x, ptd[i].count); + isp116x_write_data16(isp116x, ptd[i].mps); + isp116x_write_data16(isp116x, ptd[i].len); + isp116x_write_data16(isp116x, ptd[i].faddr); + + dump_ptd_data(&ptd[i], (__u8 *) data + done, 0); + write_ptddata_to_fifo(isp116x, + (__u8 *) data + done, + PTD_GET_LEN(&ptd[i])); + + done += PTD_GET_LEN(&ptd[i]); + } +} + +/* Read the processed PTD's and data from fifo ram back to URBs' buffers. + * Fifo must be full and done */ +static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev, + unsigned long pipe, struct ptd *ptd, int n, void *data, + int len) +{ + int buflen = n * sizeof(struct ptd) + len; + int i, done, cc, ret; + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT); + + ret = TD_CC_NOERROR; + done = 0; + for (i = 0; i < n; i++) { + DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + + ptd[i].count = isp116x_read_data16(isp116x); + ptd[i].mps = isp116x_read_data16(isp116x); + ptd[i].len = isp116x_read_data16(isp116x); + ptd[i].faddr = isp116x_read_data16(isp116x); + dump_ptd(&ptd[i]); + + read_ptddata_from_fifo(isp116x, + (__u8 *) data + done, + PTD_GET_LEN(&ptd[i])); + dump_ptd_data(&ptd[i], (__u8 *) data + done, 1); + + done += PTD_GET_LEN(&ptd[i]); + + cc = PTD_GET_CC(&ptd[i]); + + /* Data underrun means basically that we had more buffer space than + * the function had data. It is perfectly normal but upper levels have + * to know how much we actually transferred. + */ + if (cc == TD_NOTACCESSED || + (cc != TD_CC_NOERROR && (ret == TD_CC_NOERROR || ret == TD_DATAUNDERRUN))) + ret = cc; + } + + DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + + return ret; +} + +/* Interrupt handling + */ +static int isp116x_interrupt(struct isp116x *isp116x) +{ + u16 irqstat; + u32 intstat; + int ret = 0; + + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + irqstat = isp116x_read_reg16(isp116x, HCuPINT); + isp116x_write_reg16(isp116x, HCuPINT, irqstat); + DBG(">>>>>> irqstat %x <<<<<<", irqstat); + + if (irqstat & HCuPINT_ATL) { + DBG(">>>>>> HCuPINT_ATL <<<<<<"); + udelay(500); + ret = 1; + } + + if (irqstat & HCuPINT_OPR) { + intstat = isp116x_read_reg32(isp116x, HCINTSTAT); + isp116x_write_reg32(isp116x, HCINTSTAT, intstat); + DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat); + + if (intstat & HCINT_UE) { + ERR("unrecoverable error, controller disabled"); + + /* FIXME: be optimistic, hope that bug won't repeat + * often. Make some non-interrupt context restart the + * controller. Count and limit the retries though; + * either hardware or software errors can go forever... + */ + isp116x_reset(isp116x); + ret = -1; + return -1; + } + + if (intstat & HCINT_RHSC) { + got_rhsc = 1; + ret = 1; + /* When root hub or any of its ports is going + to come out of suspend, it may take more + than 10ms for status bits to stabilize. */ + wait_ms(20); + } + + if (intstat & HCINT_SO) { + ERR("schedule overrun"); + ret = -1; + } + + irqstat &= ~HCuPINT_OPR; + } + + return ret; +} + +/* With one PTD we can transfer almost 1K in one go; + * HC does the splitting into endpoint digestible transactions + */ +struct ptd ptd[1]; + +static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe) +{ + unsigned mpck = usb_maxpacket(dev, pipe); + + /* One PTD can transfer 1023 bytes but try to always + * transfer multiples of endpoint buffer size + */ + return 1023 / mpck * mpck; +} + +/* Do an USB transfer + */ +static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe, + int dir, void *buffer, int len) +{ + struct isp116x *isp116x = &isp116x_dev; + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + int max = usb_maxpacket(dev, pipe); + int dir_out = usb_pipeout(pipe); + int speed_low = usb_pipeslow(pipe); + int i, done = 0, stat, timeout, cc; + + /* 500 frames or 0.5s timeout when function is busy and NAKs transactions for a while */ + int retries = 500; + + DBG("------------------------------------------------"); + dump_msg(dev, pipe, buffer, len, "SUBMIT"); + DBG("------------------------------------------------"); + + if (len >= 1024) { + ERR("Too big job"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + if (isp116x->disabled) { + ERR("EPIPE"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + ERR("ENODEV"); + dev->status = USB_ST_CRC_ERR; + return USB_ST_CRC_ERR; + } + + if (!max) { + ERR("pipesize for pipe %lx is zero", pipe); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + if (type == PIPE_ISOCHRONOUS) { + ERR("isochronous transfers not supported"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + /* FIFO not empty? */ + if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) { + ERR("****** FIFO not empty! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + + retry: + isp116x_write_reg32(isp116x, HCINTSTAT, 0xff); + + /* Prepare the PTD data */ + ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | + PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out)); + ptd->mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum) | PTD_LAST_MSK; + ptd->len = PTD_LEN(len) | PTD_DIR(dir); + ptd->faddr = PTD_FA(usb_pipedevice(pipe)); + +retry_same: + /* Pack data into FIFO ram */ + pack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); +#ifdef EXTRA_DELAY + wait_ms(EXTRA_DELAY); +#endif + + /* Start the data transfer */ + + /* Allow more time for a BULK device to react - some are slow */ + if (usb_pipebulk(pipe)) + timeout = 5000; + else + timeout = 100; + + /* Wait for it to complete */ + for (;;) { + /* Check whether the controller is done */ + stat = isp116x_interrupt(isp116x); + + if (stat < 0) { + dev->status = USB_ST_CRC_ERR; + break; + } + if (stat > 0) + break; + + /* Check the timeout */ + if (--timeout) + udelay(1); + else { + ERR("CTL:TIMEOUT "); + stat = USB_ST_CRC_ERR; + break; + } + } + + /* We got an Root Hub Status Change interrupt */ + if (got_rhsc) { + isp116x_show_regs(isp116x); + + got_rhsc = 0; + + /* Abuse timeout */ + timeout = rh_check_port_status(isp116x); + if (timeout >= 0) { + /* + * FIXME! NOTE! AAAARGH! + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } + + /* Ok, now we can read transfer status */ + + /* FIFO not ready? */ + if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) { + ERR("****** FIFO not ready! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + + /* Unpack data from FIFO ram */ + cc = unpack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); + + i = PTD_GET_COUNT(ptd); + done += i; + buffer += i; + len -= i; + + /* There was some kind of real problem; Prepare the PTD again + * and retry from the failed transaction on + */ + if (cc && cc != TD_NOTACCESSED && cc != TD_DATAUNDERRUN) { + if (retries >= 100) { + retries -= 100; + /* The chip will have toggled the toggle bit for the failed + * transaction too. We have to toggle it back. + */ + usb_settoggle(dev, epnum, dir_out, !PTD_GET_TOGGLE(ptd)); + goto retry; + } + } + /* "Normal" errors; TD_NOTACCESSED would mean in effect that the function have NAKed + * the transactions from the first on for the whole frame. It may be busy and we retry + * with the same PTD. PTD_ACTIVE (and not TD_NOTACCESSED) would mean that some of the + * PTD didn't make it because the function was busy or the frame ended before the PTD + * finished. We prepare the rest of the data and try again. + */ + else if (cc == TD_NOTACCESSED || PTD_GET_ACTIVE(ptd) || (cc != TD_DATAUNDERRUN && PTD_GET_COUNT(ptd) < PTD_GET_LEN(ptd))) { + if (retries) { + --retries; + if (cc == TD_NOTACCESSED && PTD_GET_ACTIVE(ptd) && !PTD_GET_COUNT(ptd)) goto retry_same; + usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); + goto retry; + } + } + + if (cc != TD_CC_NOERROR && cc != TD_DATAUNDERRUN) { + DBG("****** completition code error %x ******", cc); + switch (cc) { + case TD_CC_BITSTUFFING: + dev->status = USB_ST_BIT_ERR; + break; + case TD_CC_STALL: + dev->status = USB_ST_STALLED; + break; + case TD_BUFFEROVERRUN: + case TD_BUFFERUNDERRUN: + dev->status = USB_ST_BUF_ERR; + break; + default: + dev->status = USB_ST_CRC_ERR; + } + return -cc; + } + else usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); + + dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)"); + + dev->status = 0; + return done; +} + +/* Adapted from au1x00_usb_ohci.c + */ +static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *cmd) +{ + struct isp116x *isp116x = &isp116x_dev; + u32 tmp = 0; + + int leni = transfer_len; + int len = 0; + int stat = 0; + u32 datab[4]; + u8 *data_buf = (u8 *) datab; + u16 bmRType_bReq; + u16 wValue; + u16 wIndex; + u16 wLength; + + if (usb_pipeint(pipe)) { + INFO("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = swap_16(cmd->value); + wIndex = swap_16(cmd->index); + wLength = swap_16(cmd->length); + + DBG("--- HUB ----------------------------------------"); + DBG("submit rh urb, req=%x val=%#x index=%#x len=%d", + bmRType_bReq, wValue, wIndex, wLength); + dump_msg(dev, pipe, buffer, transfer_len, "RH"); + DBG("------------------------------------------------"); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + DBG("RH_GET_STATUS"); + + *(__u16 *) data_buf = swap_16(1); + len = 2; + break; + + case RH_GET_STATUS | RH_INTERFACE: + DBG("RH_GET_STATUS | RH_INTERFACE"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_ENDPOINT: + DBG("RH_GET_STATUS | RH_ENDPOINT"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_CLASS: + DBG("RH_GET_STATUS | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHSTATUS); + + *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE)); + len = 4; + break; + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1); + *(__u32 *) data_buf = swap_32(tmp); + isp116x_show_regs(isp116x); + len = 4; + break; + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + DBG("RH_CLEAR_FEATURE | RH_ENDPOINT"); + + switch (wValue) { + case RH_ENDPOINT_STALL: + DBG("C_HUB_ENDPOINT_STALL"); + len = 0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + DBG("RH_CLEAR_FEATURE | RH_CLASS"); + + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + DBG("C_HUB_LOCAL_POWER"); + len = 0; + break; + + case RH_C_HUB_OVER_CURRENT: + DBG("C_HUB_OVER_CURRENT"); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); + len = 0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS"); + + switch (wValue) { + case RH_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_CCS); + len = 0; + break; + + case RH_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_POCI); + len = 0; + break; + + case RH_PORT_POWER: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_LSDA); + len = 0; + break; + + case RH_C_PORT_CONNECTION: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_CSC); + len = 0; + break; + + case RH_C_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PESC); + len = 0; + break; + + case RH_C_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PSSC); + len = 0; + break; + + case RH_C_PORT_OVER_CURRENT: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_POCI); + len = 0; + break; + + case RH_C_PORT_RESET: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PRSC); + len = 0; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + isp116x_show_regs(isp116x); + + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS"); + + switch (wValue) { + case RH_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PSS); + len = 0; + break; + + case RH_PORT_RESET: + /* Spin until any current reset finishes */ + while (1) { + tmp = + isp116x_read_reg32(isp116x, + HCRHPORT1 + wIndex - 1); + if (!(tmp & RH_PS_PRS)) + break; + wait_ms(1); + } + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PRS); + wait_ms(10); + + len = 0; + break; + + case RH_PORT_POWER: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PPS); + len = 0; + break; + + case RH_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PES); + len = 0; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + isp116x_show_regs(isp116x); + + break; + + case RH_SET_ADDRESS: + DBG("RH_SET_ADDRESS"); + + rh_devnum = wValue; + len = 0; + break; + + case RH_GET_DESCRIPTOR: + DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength); + + switch (wValue) { + case (USB_DT_DEVICE << 8): /* device descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; + break; + + case (USB_DT_CONFIG << 8): /* configuration descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; + break; + + case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + break; + + case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + DBG("RH_GET_DESCRIPTOR | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHDESCA); + + data_buf[0] = 0x09; /* min length; */ + data_buf[1] = 0x29; + data_buf[2] = tmp & RH_A_NDP; + data_buf[3] = 0; + if (tmp & RH_A_PSM) /* per-port power switching? */ + data_buf[3] |= 0x01; + if (tmp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf[3] |= 0x10; + else if (tmp & RH_A_OCPM) /* per-port overcurrent rep? */ + data_buf[3] |= 0x08; + + /* Corresponds to data_buf[4-7] */ + datab[1] = 0; + data_buf[5] = (tmp & RH_A_POTPGT) >> 24; + + tmp = isp116x_read_reg32(isp116x, HCRHDESCB); + + data_buf[7] = tmp & RH_B_DR; + if (data_buf[2] < 7) + data_buf[8] = 0xff; + else { + data_buf[0] += 2; + data_buf[8] = (tmp & RH_B_DR) >> 8; + data_buf[10] = data_buf[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf[0], wLength)); + break; + + case RH_GET_CONFIGURATION: + DBG("RH_GET_CONFIGURATION"); + + *(__u8 *) data_buf = 0x01; + len = 1; + break; + + case RH_SET_CONFIGURATION: + DBG("RH_SET_CONFIGURATION"); + + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC); + len = 0; + break; + + default: + ERR("*** *** *** unsupported root hub command *** *** ***"); + stat = USB_ST_STALLED; + } + + len = min_t(int, len, leni); + if (buffer != data_buf) + memcpy(buffer, data_buf, len); + + dev->act_len = len; + dev->status = stat; + DBG("dev act_len %d, status %d", dev->act_len, dev->status); + + dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)"); + + return stat; +} + +/* --- Transfer functions -------------------------------------------------- */ + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d", + dev, pipe, buffer, len, interval); + + return -1; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + int epnum = usb_pipeendpoint(pipe); + int max = max_transfer_len(dev, pipe); + int dir_in = usb_pipein(pipe); + int done, ret; + + /* Control message is for the HUB? */ + if (devnum == rh_devnum) + return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup); + + /* Ok, no HUB message so send the message to the device */ + + /* Setup phase */ + DBG("--- SETUP PHASE --------------------------------"); + usb_settoggle(dev, epnum, 1, 0); + ret = isp116x_submit_job(dev, pipe, + PTD_DIR_SETUP, + setup, sizeof(struct devrequest)); + if (ret < 0) { + DBG("control setup phase error (ret = %d", ret); + return -1; + } + + /* Data phase */ + DBG("--- DATA PHASE ---------------------------------"); + done = 0; + usb_settoggle(dev, epnum, !dir_in, 1); + while (done < len) { + ret = isp116x_submit_job(dev, pipe, + dir_in ? PTD_DIR_IN : PTD_DIR_OUT, + (__u8 *) buffer + done, + max > len - done ? len - done : max); + if (ret < 0) { + DBG("control data phase error (ret = %d)", ret); + return -1; + } + done += ret; + + if (dir_in && ret < max) /* short packet */ + break; + } + + /* Status phase */ + DBG("--- STATUS PHASE -------------------------------"); + usb_settoggle(dev, epnum, !dir_in, 1); + ret = isp116x_submit_job(dev, pipe, + !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0); + if (ret < 0) { + DBG("control status phase error (ret = %d", ret); + return -1; + } + + dev->act_len = done; + + dump_msg(dev, pipe, buffer, len, "DEV(ret)"); + + return done; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + int dir_out = usb_pipeout(pipe); + int max = max_transfer_len(dev, pipe); + int done, ret; + + DBG("--- BULK ---------------------------------------"); + DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d", + usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + + done = 0; + while (done < len) { + ret = isp116x_submit_job(dev, pipe, + !dir_out ? PTD_DIR_IN : PTD_DIR_OUT, + (__u8 *) buffer + done, + max > len - done ? len - done : max); + if (ret < 0) { + DBG("error on bulk message (ret = %d)", ret); + return -1; + } + + done += ret; + + if (!dir_out && ret < max) /* short packet */ + break; + } + + dev->act_len = done; + + return 0; +} + +/* --- Basic functions ----------------------------------------------------- */ + +static int isp116x_sw_reset(struct isp116x *isp116x) +{ + int retries = 15; + int ret = 0; + + DBG(""); + + isp116x->disabled = 1; + + isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); + isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); + while (--retries) { + /* It usually resets within 1 ms */ + wait_ms(1); + if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) + break; + } + if (!retries) { + ERR("software reset timeout"); + ret = -1; + } + return ret; +} + +static int isp116x_reset(struct isp116x *isp116x) +{ + unsigned long t; + u16 clkrdy = 0; + int ret, timeout = 15 /* ms */ ; + + DBG(""); + + ret = isp116x_sw_reset(isp116x); + if (ret) + return ret; + + for (t = 0; t < timeout; t++) { + clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; + if (clkrdy) + break; + wait_ms(1); + } + if (!clkrdy) { + ERR("clock not ready after %dms", timeout); + /* After sw_reset the clock won't report to be ready, if + H_WAKEUP pin is high. */ + ERR("please make sure that the H_WAKEUP pin is pulled low!"); + ret = -1; + } + return ret; +} + +static void isp116x_stop(struct isp116x *isp116x) +{ + u32 val; + + DBG(""); + + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + /* Switch off ports' power, some devices don't come up + after next 'start' without this */ + val = isp116x_read_reg32(isp116x, HCRHDESCA); + val &= ~(RH_A_NPS | RH_A_PSM); + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); + + isp116x_sw_reset(isp116x); +} + +/* + * Configure the chip. The chip must be successfully reset by now. + */ +static int isp116x_start(struct isp116x *isp116x) +{ + struct isp116x_platform_data *board = isp116x->board; + u32 val; + + DBG(""); + + /* Clear interrupt status and disable all interrupt sources */ + isp116x_write_reg16(isp116x, HCuPINT, 0xff); + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); + isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); + + /* Hardware configuration */ + val = HCHWCFG_DBWIDTH(1); + if (board->sel15Kres) + val |= HCHWCFG_15KRSEL; + /* Remote wakeup won't work without working clock */ + if (board->remote_wakeup_enable) + val |= HCHWCFG_CLKNOTSTOP; + if (board->oc_enable) + val |= HCHWCFG_ANALOG_OC; + isp116x_write_reg16(isp116x, HCHWCFG, val); + + /* --- Root hub configuration */ + val = (25 << 24) & RH_A_POTPGT; + /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to + be always set. Yet, instead, we request individual port + power switching. */ + val |= RH_A_PSM; + /* Report overcurrent per port */ + val |= RH_A_OCPM; + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); + + val = RH_B_PPCM; + isp116x_write_reg32(isp116x, HCRHDESCB, val); + isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); + + val = 0; + if (board->remote_wakeup_enable) + val |= RH_HS_DRWE; + isp116x_write_reg32(isp116x, HCRHSTATUS, val); + isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); + + isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); + + /* Go operational */ + val = HCCONTROL_USB_OPER; + if (board->remote_wakeup_enable) + val |= HCCONTROL_RWE; + isp116x_write_reg32(isp116x, HCCONTROL, val); + + /* Disable ports to avoid race in device enumeration */ + isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); + isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); + + isp116x_show_regs(isp116x); + + isp116x->disabled = 0; + + return 0; +} + +/* --- Init functions ------------------------------------------------------ */ + +int isp116x_check_id(struct isp116x *isp116x) +{ + int val; + + val = isp116x_read_reg16(isp116x, HCCHIPID); + if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { + ERR("invalid chip ID %04x", val); + return -1; + } + + return 0; +} + +int usb_lowlevel_init(void) +{ + struct isp116x *isp116x = &isp116x_dev; + + DBG(""); + + got_rhsc = rh_devnum = 0; + + /* Init device registers addr */ + isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR; + isp116x->data_reg = (u16 *) ISP116X_HCD_DATA; + + /* Setup specific board settings */ +#ifdef ISP116X_HCD_SEL15kRES + isp116x_board.sel15Kres = 1; +#endif +#ifdef ISP116X_HCD_OC_ENABLE + isp116x_board.oc_enable = 1; +#endif +#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE + isp116x_board.remote_wakeup_enable = 1; +#endif + isp116x->board = &isp116x_board; + + /* Try to get ISP116x silicon chip ID */ + if (isp116x_check_id(isp116x) < 0) + return -1; + + isp116x->disabled = 1; + isp116x->sleeping = 0; + + isp116x_reset(isp116x); + isp116x_start(isp116x); + + return 0; +} + +int usb_lowlevel_stop(void) +{ + struct isp116x *isp116x = &isp116x_dev; + + DBG(""); + + if (!isp116x->disabled) + isp116x_stop(isp116x); + + return 0; +} diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h new file mode 100644 index 0000000..a3ce3b5 --- /dev/null +++ b/drivers/usb/host/isp116x.h @@ -0,0 +1,489 @@ +/* + * ISP116x register declarations and HCD data structures + * + * Copyright (C) 2007 Rodolfo Giometti + * Copyright (C) 2007 Eurotech S.p.A. + * Copyright (C) 2005 Olav Kongas + * Portions: + * Copyright (C) 2004 Lothar Wassmann + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifdef DEBUG +#define DBG(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(fmt, args...) do {} while (0) +#endif + +#define ERR(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define WARN(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define INFO(fmt, args...) \ + printf("isp116x: " fmt "\n" , ## args) + +/* ------------------------------------------------------------------------- */ + +/* us of 1ms frame */ +#define MAX_LOAD_LIMIT 850 + +/* Full speed: max # of bytes to transfer for a single urb + at a time must be < 1024 && must be multiple of 64. + 832 allows transfering 4kiB within 5 frames. */ +#define MAX_TRANSFER_SIZE_FULLSPEED 832 + +/* Low speed: there is no reason to schedule in very big + chunks; often the requested long transfers are for + string descriptors containing short strings. */ +#define MAX_TRANSFER_SIZE_LOWSPEED 64 + +/* Bytetime (us), a rough indication of how much time it + would take to transfer a byte of useful data over USB */ +#define BYTE_TIME_FULLSPEED 1 +#define BYTE_TIME_LOWSPEED 20 + +/* Buffer sizes */ +#define ISP116x_BUF_SIZE 4096 +#define ISP116x_ITL_BUFSIZE 0 +#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE)) + +#define ISP116x_WRITE_OFFSET 0x80 + +/* --- ISP116x registers/bits ---------------------------------------------- */ + +#define HCREVISION 0x00 +#define HCCONTROL 0x01 +#define HCCONTROL_HCFS (3 << 6) /* host controller + functional state */ +#define HCCONTROL_USB_RESET (0 << 6) +#define HCCONTROL_USB_RESUME (1 << 6) +#define HCCONTROL_USB_OPER (2 << 6) +#define HCCONTROL_USB_SUSPEND (3 << 6) +#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */ +#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */ +#define HCCMDSTAT 0x02 +#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */ +#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */ +#define HCINTSTAT 0x03 +#define HCINT_SO (1 << 0) /* scheduling overrun */ +#define HCINT_WDH (1 << 1) /* writeback of done_head */ +#define HCINT_SF (1 << 2) /* start frame */ +#define HCINT_RD (1 << 3) /* resume detect */ +#define HCINT_UE (1 << 4) /* unrecoverable error */ +#define HCINT_FNO (1 << 5) /* frame number overflow */ +#define HCINT_RHSC (1 << 6) /* root hub status change */ +#define HCINT_OC (1 << 30) /* ownership change */ +#define HCINT_MIE (1 << 31) /* master interrupt enable */ +#define HCINTENB 0x04 +#define HCINTDIS 0x05 +#define HCFMINTVL 0x0d +#define HCFMREM 0x0e +#define HCFMNUM 0x0f +#define HCLSTHRESH 0x11 +#define HCRHDESCA 0x12 +#define RH_A_NDP (0x3 << 0) /* # downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* overcurrent protection + mode */ +#define RH_A_NOCP (1 << 12) /* no overcurrent protection */ +#define RH_A_POTPGT (0xff << 24) /* power on -> power good + time */ +#define HCRHDESCB 0x13 +#define RH_B_DR (0xffff << 0) /* device removable flags */ +#define RH_B_PPCM (0xffff << 16) /* port power control mask */ +#define HCRHSTATUS 0x14 +#define RH_HS_LPS (1 << 0) /* local power status */ +#define RH_HS_OCI (1 << 1) /* over current indicator */ +#define RH_HS_DRWE (1 << 15) /* device remote wakeup + enable */ +#define RH_HS_LPSC (1 << 16) /* local power status change */ +#define RH_HS_OCIC (1 << 17) /* over current indicator + change */ +#define RH_HS_CRWE (1 << 31) /* clear remote wakeup + enable */ +#define HCRHPORT1 0x15 +#define RH_PS_CCS (1 << 0) /* current connect status */ +#define RH_PS_PES (1 << 1) /* port enable status */ +#define RH_PS_PSS (1 << 2) /* port suspend status */ +#define RH_PS_POCI (1 << 3) /* port over current + indicator */ +#define RH_PS_PRS (1 << 4) /* port reset status */ +#define RH_PS_PPS (1 << 8) /* port power status */ +#define RH_PS_LSDA (1 << 9) /* low speed device attached */ +#define RH_PS_CSC (1 << 16) /* connect status change */ +#define RH_PS_PESC (1 << 17) /* port enable status change */ +#define RH_PS_PSSC (1 << 18) /* port suspend status + change */ +#define RH_PS_OCIC (1 << 19) /* over current indicator + change */ +#define RH_PS_PRSC (1 << 20) /* port reset status change */ +#define HCRHPORT_CLRMASK (0x1f << 16) +#define HCRHPORT2 0x16 +#define HCHWCFG 0x20 +#define HCHWCFG_15KRSEL (1 << 12) +#define HCHWCFG_CLKNOTSTOP (1 << 11) +#define HCHWCFG_ANALOG_OC (1 << 10) +#define HCHWCFG_DACK_MODE (1 << 8) +#define HCHWCFG_EOT_POL (1 << 7) +#define HCHWCFG_DACK_POL (1 << 6) +#define HCHWCFG_DREQ_POL (1 << 5) +#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) +#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define HCHWCFG_INT_POL (1 << 2) +#define HCHWCFG_INT_TRIGGER (1 << 1) +#define HCHWCFG_INT_ENABLE (1 << 0) +#define HCDMACFG 0x21 +#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) +#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) +#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) +#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) +#define HCDMACFG_DMA_ENABLE (1 << 4) +#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) +#define HCDMACFG_CTR_SEL (1 << 2) +#define HCDMACFG_ITLATL_SEL (1 << 1) +#define HCDMACFG_DMA_RW_SELECT (1 << 0) +#define HCXFERCTR 0x22 +#define HCuPINT 0x24 +#define HCuPINT_SOF (1 << 0) +#define HCuPINT_ATL (1 << 1) +#define HCuPINT_AIIEOT (1 << 2) +#define HCuPINT_OPR (1 << 4) +#define HCuPINT_SUSP (1 << 5) +#define HCuPINT_CLKRDY (1 << 6) +#define HCuPINTENB 0x25 +#define HCCHIPID 0x27 +#define HCCHIPID_MASK 0xff00 +#define HCCHIPID_MAGIC 0x6100 +#define HCSCRATCH 0x28 +#define HCSWRES 0x29 +#define HCSWRES_MAGIC 0x00f6 +#define HCITLBUFLEN 0x2a +#define HCATLBUFLEN 0x2b +#define HCBUFSTAT 0x2c +#define HCBUFSTAT_ITL0_FULL (1 << 0) +#define HCBUFSTAT_ITL1_FULL (1 << 1) +#define HCBUFSTAT_ATL_FULL (1 << 2) +#define HCBUFSTAT_ITL0_DONE (1 << 3) +#define HCBUFSTAT_ITL1_DONE (1 << 4) +#define HCBUFSTAT_ATL_DONE (1 << 5) +#define HCRDITL0LEN 0x2d +#define HCRDITL1LEN 0x2e +#define HCITLPORT 0x40 +#define HCATLPORT 0x41 + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13) +#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK) +#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) +#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7) +#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK) + +/* Hardware transfer status codes -- CC from ptd->count */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define TD_NOTACCESSED 0x0F + +/* ------------------------------------------------------------------------- */ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +/* Philips transfer descriptor */ +struct ptd { + u16 count; +#define PTD_COUNT_MSK (0x3ff << 0) +#define PTD_TOGGLE_MSK (1 << 10) +#define PTD_ACTIVE_MSK (1 << 11) +#define PTD_CC_MSK (0xf << 12) + u16 mps; +#define PTD_MPS_MSK (0x3ff << 0) +#define PTD_SPD_MSK (1 << 10) +#define PTD_LAST_MSK (1 << 11) +#define PTD_EP_MSK (0xf << 12) + u16 len; +#define PTD_LEN_MSK (0x3ff << 0) +#define PTD_DIR_MSK (3 << 10) +#define PTD_DIR_SETUP (0) +#define PTD_DIR_OUT (1) +#define PTD_DIR_IN (2) +#define PTD_B5_5_MSK (1 << 13) + u16 faddr; +#define PTD_FA_MSK (0x7f << 0) +#define PTD_FMT_MSK (1 << 7) +} __attribute__ ((packed, aligned(2))); + +struct isp116x_ep { + struct usb_device *udev; + struct ptd ptd; + + u8 maxpacket; + u8 epnum; + u8 nextpid; + + u16 length; /* of current packet */ + unsigned char *data; /* to databuf */ + + u16 error_count; +}; + +/* URB struct */ +#define N_URB_TD 48 +#define URB_DEL 1 +typedef struct { + struct isp116x_ep *ed; + void *transfer_buffer; /* (in) associated data buffer */ + int actual_length; /* (return) actual transfer length */ + unsigned long pipe; /* (in) pipe information */ +#if 0 + int state; +#endif +} urb_priv_t; + +struct isp116x_platform_data { + /* Enable internal resistors on downstream ports */ + unsigned sel15Kres:1; + /* On-chip overcurrent detection */ + unsigned oc_enable:1; + /* Enable wakeup by devices on usb bus (e.g. wakeup + by attachment/detachment or by device activity + such as moving a mouse). When chosen, this option + prevents stopping internal clock, increasing + thereby power consumption in suspended state. */ + unsigned remote_wakeup_enable:1; +}; + +struct isp116x { + u16 *addr_reg; + u16 *data_reg; + + struct isp116x_platform_data *board; + + struct dentry *dentry; + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* Status flags */ + unsigned disabled:1; + unsigned sleeping:1; + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[2]; + + /* Schedule for the current frame */ + struct isp116x_ep *atl_active; + int atl_buflen; + int atl_bufshrt; + int atl_last_dir; + int atl_finishing; +}; + +/* ------------------------------------------------- */ + +/* Inter-io delay (ns). The chip is picky about access timings; it + * expects at least: + * 150ns delay between consecutive accesses to DATA_REG, + * 300ns delay between access to ADDR_REG and DATA_REG + * OE, WE MUST NOT be changed during these intervals + */ +#if defined(UDELAY) +#define isp116x_delay(h,d) udelay(d) +#else +#define isp116x_delay(h,d) do {} while (0) +#endif + +static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) +{ + writew(reg & 0xff, isp116x->addr_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val) +{ + writew(val, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val) +{ + __raw_writew(val, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline u16 isp116x_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + return val; +} + +static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = __raw_readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + return val; +} + +static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val) +{ + writew(val & 0xffff, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + writew(val >> 16, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline u32 isp116x_read_data32(struct isp116x *isp116x) +{ + u32 val; + + val = (u32) readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + val |= ((u32) readw(isp116x->data_reg)) << 16; + isp116x_delay(isp116x, UDELAY); + return val; +} + +/* Let's keep register access functions out of line. Hint: + we wait at least 150 ns at every access. +*/ +static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data16(isp116x); +} + +static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data32(isp116x); +} + +static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data16(isp116x, (u16) (val & 0xffff)); +} + +static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data32(isp116x, (u32) val); +} + +/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c new file mode 100644 index 0000000..7c44ad0 --- /dev/null +++ b/drivers/usb/host/ohci-at91.c @@ -0,0 +1,80 @@ +/* + * (C) Copyright 2006 + * DENX Software Engineering + * + * 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 + +#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) + +#include +#include +#include + +int usb_cpu_init(void) +{ + +#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \ + defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) + /* Enable PLLB */ + at91_sys_write(AT91_CKGR_PLLBR, CONFIG_SYS_AT91_PLLB); + while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB) + ; +#endif + + /* Enable USB host clock. */ + at91_sys_write(AT91_PMC_PCER, 1 << AT91_ID_UHP); +#ifdef CONFIG_AT91SAM9261 + at91_sys_write(AT91_PMC_SCER, AT91_PMC_UHP | AT91_PMC_HCK0); +#else + at91_sys_write(AT91_PMC_SCER, AT91_PMC_UHP); +#endif + + return 0; +} + +int usb_cpu_stop(void) +{ + /* Disable USB host clock. */ + at91_sys_write(AT91_PMC_PCDR, 1 << AT91_ID_UHP); +#ifdef CONFIG_AT91SAM9261 + at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP | AT91_PMC_HCK0); +#else + at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP); +#endif + +#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \ + defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) + /* Disable PLLB */ + at91_sys_write(AT91_CKGR_PLLBR, 0); + while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != 0) + ; +#endif + + return 0; +} + +int usb_cpu_init_fail(void) +{ + return usb_cpu_stop(); +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c new file mode 100644 index 0000000..e2f289e --- /dev/null +++ b/drivers/usb/host/ohci-hcd.c @@ -0,0 +1,2016 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575 chip and works well with USB keyboard. + * + * (C) Copyright 2007 + * Zhang Wei, Freescale Semiconductor, Inc. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * Modified for the MP2USB by (C) Copyright 2005 Eric Benard + * ebenard@eukrea.com - based on s3c24x0's driver + * + * 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 + * + */ +/* + * IMPORTANT NOTES + * 1 - Read doc/README.generic_usb_ohci + * 2 - this driver is intended for use with USB Mass Storage Devices + * (BBB) and USB keyboard. There is NO support for Isochronous pipes! + * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG + * to activate workaround for bug #41 or this driver will NOT work! + */ + +#include +#include + +#if defined(CONFIG_PCI_OHCI) +# include +#if !defined(CONFIG_PCI_OHCI_DEVNO) +#define CONFIG_PCI_OHCI_DEVNO 0 +#endif +#endif + +#include +#include + +#include "ohci.h" + +#ifdef CONFIG_AT91RM9200 +#include /* needed for AT91_USB_HOST_BASE */ +#endif + +#if defined(CONFIG_ARM920T) || \ + defined(CONFIG_S3C2400) || \ + defined(CONFIG_S3C2410) || \ + defined(CONFIG_S3C6400) || \ + defined(CONFIG_440EP) || \ + defined(CONFIG_PCI_OHCI) || \ + defined(CONFIG_MPC5200) || \ + defined(CONFIG_SYS_OHCI_USE_NPS) +# 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 \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +/* + * e.g. PCI controllers need this + */ +#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS +# define readl(a) __swap_32(*((volatile u32 *)(a))) +# define writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a)) +#else +# define readl(a) (*((volatile u32 *)(a))) +# define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) +#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ + +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef CONFIG_PCI_OHCI +static struct pci_device_id ohci_pci_ids[] = { + {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ + {0x1033, 0x0035}, /* NEC PCI OHCI module ids */ + {0x1131, 0x1561}, /* Philips 1561 PCI OHCI module ids */ + /* Please add supported PCI OHCI controller ids here */ + {0, 0} +}; +#endif + +#ifdef CONFIG_PCI_EHCI_DEVNO +static struct pci_device_id ehci_pci_ids[] = { + {0x1131, 0x1562}, /* Philips 1562 PCI EHCI module ids */ + /* Please add supported PCI EHCI controller ids here */ + {0, 0} +}; +#endif + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while (0) +#endif + +#ifdef CONFIG_SYS_OHCI_BE_CONTROLLER +# define m16_swap(x) cpu_to_be16(x) +# define m32_swap(x) cpu_to_be32(x) +#else +# define m16_swap(x) cpu_to_le16(x) +# define m32_swap(x) cpu_to_le32(x) +#endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* device which was disconnected */ +struct usb_device *devgone; + +static inline u32 roothub_a(struct ohci *hc) + { return readl(&hc->regs->roothub.a); } +static inline u32 roothub_b(struct ohci *hc) + { return readl(&hc->regs->roothub.b); } +static inline u32 roothub_status(struct ohci *hc) + { return readl(&hc->regs->roothub.status); } +static inline u32 roothub_portstatus(struct ohci *hc, int i) + { return readl(&hc->regs->roothub.portstatus[i]); } + +/* forward declaration */ +static int hc_interrupt(void); +static void td_submit_job(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, urb_priv_t *urb, + int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv(urb_priv_t *urb) +{ + int i; + int last; + struct td *td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } + free(urb); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number(struct usb_device *dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print(urb_priv_t *purb, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup, char *str, int small) +{ + dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number(dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe)? 'O': 'I', + usb_pipetype(pipe) < 2 ? \ + (usb_pipeint(pipe)? "INTR": "ISOC"): \ + (usb_pipecontrol(pipe)? "CTRL": "BULK"), + (purb ? purb->actual_length : 0), + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol(pipe)) { + printf(__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf(" %02x", ((__u8 *) setup) [i]); + printf("\n"); + } + if (transfer_len > 0 && buffer) { + printf(__FILE__ ": data(%d/%d):", + (purb ? purb->actual_length : 0), + transfer_len); + len = usb_pipeout(pipe)? transfer_len: + (purb ? purb->actual_length : 0); + for (i = 0; i < 16 && i < len; i++) + printf(" %02x", ((__u8 *) buffer) [i]); + printf("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree + * inclusive iso eds */ +void ep_print_int_eds(ohci_t *ohci, char *str) +{ + int i, j; + __u32 *ed_p; + for (i = 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)m32_swap(ed_p); + printf(" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf("\n"); + } +} + +static void ohci_dump_intr_mask(char *label, __u32 mask) +{ + dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds(char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg("%s %08x", label, value); + dbg("%08x", edp->hwINFO); + dbg("%08x", edp->hwTailP); + dbg("%08x", edp->hwHeadP); + dbg("%08x", edp->hwNextED); + } +} + +static char *hcfs2string(int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status(ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl(®s->revision) & 0xff; + if (temp != 0x10) + dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl(®s->control); + dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string(temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl(®s->cmdstatus); + dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask("intrstatus", readl(®s->intrstatus)); + ohci_dump_intr_mask("intrenable", readl(®s->intrenable)); + + maybe_print_eds("ed_periodcurrent", readl(®s->ed_periodcurrent)); + + maybe_print_eds("ed_controlhead", readl(®s->ed_controlhead)); + maybe_print_eds("ed_controlcurrent", readl(®s->ed_controlcurrent)); + + maybe_print_eds("ed_bulkhead", readl(®s->ed_bulkhead)); + maybe_print_eds("ed_bulkcurrent", readl(®s->ed_bulkcurrent)); + + maybe_print_eds("donehead", readl(®s->donehead)); +} + +static void ohci_dump_roothub(ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a(controller); + ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG + ndp = (ndp == 2) ? 1:0; +#endif + if (verbose) { + dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b(controller); + dbg("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status(controller); + dbg("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus(controller, i); + dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump(ohci_t *controller, int verbose) +{ + dbg("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status(controller); + if (verbose) + ep_print_int_eds(controller, "hcca"); + dbg("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub(controller, 1); +} +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) +{ + ohci_t *ohci; + ed_t *ed; + urb_priv_t *purb_priv = urb; + int i, size = 0; + struct usb_device *dev = urb->dev; + unsigned long pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + int transfer_len = urb->transfer_buffer_length; + int interval = urb->interval; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* we're about to begin a new transaction here so mark the + * URB unfinished */ + urb->finished = 0; + + /* every endpoint has a ed, locate and fill it */ + ed = ep_add_ed(dev, pipe, interval, 1); + if (!ed) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype(pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL:/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + case PIPE_INTERRUPT: /* 1 TD */ + size = 1; + break; + } + + ed->purb = urb; + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc(dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv(purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv(purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link(ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, + setup, purb_priv, interval); + + return 0; +} + +static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) +{ + struct ohci_regs *regs = hc->regs; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_INTERRUPT: + /* implicitly requeued */ + if (urb->dev->irq_handle && + (urb->dev->irq_act_len = urb->actual_length)) { + writel(OHCI_INTR_WDH, ®s->intrenable); + readl(®s->intrenable); /* PCI posting flush */ + urb->dev->irq_handle(urb->dev); + writel(OHCI_INTR_WDH, ®s->intrdisable); + readl(®s->intrdisable); /* PCI posting flush */ + } + urb->actual_length = 0; + td_submit_job( + urb->dev, + urb->pipe, + urb->transfer_buffer, + urb->transfer_buffer_length, + NULL, + urb, + urb->interval); + break; + case PIPE_CONTROL: + case PIPE_BULK: + break; + default: + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number(struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return m16_swap(ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance(ohci_t *ohci, int interval, int load) +{ + int i, branch = 0; + + /* search for the least loaded interrupt endpoint + * branch of all 32 branches + */ + for (i = 0; i < 32; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) + branch = i; + + branch = branch % interval; + for (i = branch; i < 32; i += interval) + ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval(int inter) +{ + int i; + for (i = 0; ((inter >> i) > 1) && (i < 5); i++); + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to + * be mapped the mapping reverses the bits of a word of num_bits length */ +static int ep_rev(int num_bits, int word) +{ + int i, wout = 0; + + for (i = 0; i < num_bits; i++) + wout |= (((word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link(ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + int int_branch; + int i; + int inter; + int interval; + int load; + __u32 *ed_p; + + ed->state = ED_OPER; + ed->int_interval = 0; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) + writel(ed, &ohci->regs->ed_controlhead); + else + ohci->ed_controltail->hwNextED = + m32_swap((unsigned long)ed); + + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) + writel(ed, &ohci->regs->ed_bulkhead); + else + ohci->ed_bulktail->hwNextED = + m32_swap((unsigned long)ed); + + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + + case PIPE_INTERRUPT: + load = ed->int_load; + interval = ep_2_n_interval(ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_ballance(ohci, interval, load); + ed->int_branch = int_branch; + + for (i = 0; i < ep_rev(6, interval); i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca->int_table[\ + ep_rev(5, i) + int_branch]); + (*ed_p != 0) && + (((ed_t *)ed_p)->int_interval >= interval); + ed_p = &(((ed_t *)ed_p)->hwNextED)) + inter = ep_rev(6, + ((ed_t *)ed_p)->int_interval); + ed->hwNextED = *ed_p; + *ed_p = m32_swap((unsigned long)ed); + } + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, + unsigned index, unsigned period) +{ + for (; index < NUM_INTS; index += period) { + __u32 *ed_p = &ohci->hcca->int_table [index]; + + /* ED might have been unlinked through another path */ + while (*ed_p != 0) { + if (((struct ed *) + m32_swap((unsigned long)ed_p)) == ed) { + *ed_p = ed->hwNextED; + break; + } + ed_p = &(((struct ed *) + m32_swap((unsigned long)ed_p))->hwNextED); + } + } +} + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * 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 *edi) +{ + volatile ed_t *ed = edi; + int i; + + ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel(ohci->hc_control, &ohci->regs->control); + } + writel(m32_swap(*((__u32 *)&ed->hwNextED)), + &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)m32_swap( + *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel(ohci->hc_control, &ohci->regs->control); + } + writel(m32_swap(*((__u32 *)&ed->hwNextED)), + &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)m32_swap( + *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_INTERRUPT: + periodic_unlink(ohci, ed, 0, 1); + for (i = ed->int_branch; i < 32; i += ed->int_interval) + ohci->ohci_int_load[i] -= ed->int_load; + break; + } + ed->state = ED_UNLINK; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* 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, + int interval, int load) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | + (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + /* dummy td; end of td list for ed */ + td = td_alloc(usb_dev); + ed->hwTailP = m32_swap((unsigned long)td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype(pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = m32_swap(usb_pipedevice(pipe) + | usb_pipeendpoint(pipe) << 7 + | (usb_pipeisoc(pipe)? 0x8000: 0) + | (usb_pipecontrol(pipe)? 0: \ + (usb_pipeout(pipe)? 0x800: 0x1000)) + | usb_pipeslow(pipe) << 13 + | usb_maxpacket(usb_dev, pipe) << 16); + + if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { + ed->int_period = interval; + ed->int_load = load; + } + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill(ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = + (td_t *)(m32_swap(urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ", i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = m32_swap(info); + td->hwCBP = m32_swap((unsigned long)data); + if (data) + td->hwBE = m32_swap((unsigned long)(data + len - 1)); + else + td->hwBE = 0; + + td->hwNextTD = m32_swap((unsigned long)td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, urb_priv_t *urb, + int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle + * bits for reseting */ + if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype(pipe)) { + case PIPE_BULK: + info = usb_pipeout(pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while (data_len > 4096) { + td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), + data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout(pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), data, + data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) { + /* start bulk list */ + writel(OHCI_BLF, &ohci->regs->cmdstatus); + } + break; + + case PIPE_CONTROL: + /* Setup phase */ + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill(ohci, info, setup, 8, dev, cnt++, urb); + + /* Optional Data phase */ + if (data_len > 0) { + info = usb_pipeout(pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : + TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + } + + /* Status phase */ + info = usb_pipeout(pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: + TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill(ohci, info, data, 0, dev, cnt++, urb); + + if (!ohci->sleeping) { + /* start Control list */ + writel(OHCI_CLF, &ohci->regs->cmdstatus); + } + break; + + case PIPE_INTERRUPT: + info = usb_pipeout(urb->pipe)? + TD_CC | TD_DP_OUT | toggle: + TD_CC | TD_R | TD_DP_IN | toggle; + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t *td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = td->ed->purb; + + tdINFO = m32_swap(td->hwINFO); + tdBE = m32_swap(td->hwBE); + tdCBP = m32_swap(td->hwCBP); + + if (!(usb_pipecontrol(lurb_priv->pipe) && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ +static void check_status(td_t *td_list) +{ + urb_priv_t *lurb_priv = td_list->ed->purb; + int urb_len = lurb_priv->length; + __u32 *phwHeadP = &td_list->ed->hwHeadP; + int cc; + + cc = TD_CC_GET(m32_swap(td_list->hwINFO)); + if (cc) { + err(" USB-error: %s (%x)", cc_to_string[cc], cc); + + if (*phwHeadP & m32_swap(0x1)) { + if (lurb_priv && + ((td_list->index + 1) < urb_len)) { + *phwHeadP = + (lurb_priv->td[urb_len - 1]->hwNextTD &\ + m32_swap(0xfffffff0)) | + (*phwHeadP & m32_swap(0x2)); + + lurb_priv->td_cnt += urb_len - + td_list->index - 1; + } else + *phwHeadP &= m32_swap(0xfffffff2); + } +#ifdef CONFIG_MPC5200 + td_list->hwNextTD = 0; +#endif + } +} + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ +static td_t *dl_reverse_done_list(ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + + td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + check_status(td_list); + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) +{ + if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) + urb->finished = sohci_return_job(ohci, urb); + else + dbg("finish_urb: strange.., ED state %x, \n", status); +} + +/* + * Used to take back a TD from the host controller. This would normally be + * called from within dl_done_list, however it may be called directly if the + * HC no longer sees the TD and it has not appeared on the donelist (after + * two frames). This bug has been observed on ZF Micro systems. + */ +static int takeback_td(ohci_t *ohci, td_t *td_list) +{ + ed_t *ed; + int cc; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + tdINFO = m32_swap(td_list->hwINFO); + + ed = td_list->ed; + lurb_priv = ed->purb; + + dl_transfer_length(td_list); + + lurb_priv->td_cnt++; + + /* error code of transfer */ + cc = TD_CC_GET(tdINFO); + if (cc) { + err("USB-error: %s (%x)", cc_to_string[cc], cc); + stat = cc_to_error[cc]; + } + + /* see if this done list makes for all TD's of current URB, + * and mark the URB finished if so */ + if (lurb_priv->td_cnt == lurb_priv->length) + finish_urb(ohci, lurb_priv, ed->state); + + dbg("dl_done_list: processing TD %x, len %x\n", + lurb_priv->td_cnt, lurb_priv->length); + + if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { + edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; + edTailP = m32_swap(ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink(ohci, ed); + } + return stat; +} + +static int dl_done_list(ohci_t *ohci) +{ + int stat = 0; + td_t *td_list = dl_reverse_done_list(ohci); + + while (td_list) { + td_t *td_next = td_list->next_dl_td; + stat = takeback_td(ohci, td_list); + td_list = td_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x)); writel((x), \ + &gohci.regs->roothub.status); } +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \ + (x)); writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); } +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), \ + &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a(controller); + ndp = (temp & RH_A_NDP); +#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 */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void *data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +pkt_print(NULL, dev, pipe, buffer, transfer_len, + cmd, "SUB(rh)", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if (usb_pipeint(pipe)) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK(4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32(RD_RH_PORTSTAT); + OK(4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK(0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); + OK(0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS); OK(0); + case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0); + case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); + case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC); OK(0); + case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0); + case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); + case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); + case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT(RH_PS_PSS); OK(0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PRS); + OK(0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT(RH_PS_PPS); + wait_ms(100); + OK(0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PES); + OK(0); + } + break; + + case RH_SET_ADDRESS: + gohci.rh.devnum = wValue; + OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if (wValue == 0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if (wValue == 0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a(&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; +#ifdef CONFIG_AT91C_PQFP_UHPBUG + data_buf [2] = (data_buf [2] == 2) ? 1:0; +#endif + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b(&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK(len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK(1); + + case RH_SET_CONFIGURATION: WR_RH_STAT(0x10000); OK(0); + + default: + dbg("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub(&gohci, 1); +#else + wait_ms(1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy(data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + pkt_print(NULL, dev, pipe, buffer, + transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else + wait_ms(1); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + urb_priv_t *urb; + + urb = malloc(sizeof(urb_priv_t)); + memset(urb, 0, sizeof(urb_priv_t)); + + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_buffer_length = transfer_len; + urb->interval = interval; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb->actual_length = 0; + pkt_print(urb, dev, pipe, buffer, transfer_len, + setup, "SUB", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(urb, setup) < 0) { + err("sohci_submit_job failed"); + 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 */ + if (usb_pipebulk(pipe)) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + wait_ms(1); + if (!urb->finished) + dbg("*"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + urb->finished = 1; + stat = USB_ST_CRC_ERR; + break; + } + } + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(urb, dev, pipe, buffer, transfer_len, + setup, "RET(ctlr)", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + + /* free TDs in urb_priv */ + if (!usb_pipeint(pipe)) + urb_free_priv(urb); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + pkt_print(NULL, dev, pipe, buffer, transfer_len, + setup, "SUB", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, + interval); +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset(ohci_t *ohci) +{ +#ifdef CONFIG_PCI_EHCI_DEVNO + pci_dev_t pdev; +#endif + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + dbg("%s\n", __FUNCTION__); + +#ifdef CONFIG_PCI_EHCI_DEVNO + /* + * Some multi-function controllers (e.g. ISP1562) allow root hub + * resetting via EHCI registers only. + */ + pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO); + if (pdev != -1) { + u32 base; + int timeout = 1000; + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); + writel(readl(base + EHCI_USBCMD_OFF) | EHCI_USBCMD_HCRESET, + base + EHCI_USBCMD_OFF); + + while (readl(base + EHCI_USBCMD_OFF) & EHCI_USBCMD_HCRESET) { + if (timeout-- <= 0) { + printf("USB RootHub reset timed out!"); + break; + } + udelay(1); + } + } else + printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO); +#endif + if (readl(&ohci->regs->control) & OHCI_CTRL_IR) { + /* SMM owns the HC */ + writel(OHCI_OCR, &ohci->regs->cmdstatus);/* request ownership */ + info("USB HC TakeOver from SMM"); + while (readl(&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms(10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", + ohci->slot_name, + readl(&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + ohci->hc_control = 0; + writel(ohci->hc_control, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel(OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay(1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start(ohci_t *ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel(0, &ohci->regs->ed_controlhead); + writel(0, &ohci->regs->ed_bulkhead); + + writel((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel(fminterval, &ohci->regs->fminterval); + writel(0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel(ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel(mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel(mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel(mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay((roothub_a(ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Poll USB interrupt. */ +void usb_event_poll(void) +{ + hc_interrupt(); +} + +/* an interrupt happens */ + +static int hc_interrupt(void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && + !(m32_swap(ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else { + ints = readl(®s->intrstatus); + if (ints == ~(u32)0) { + ohci->disabled++; + err("%s device removed!", ohci->slot_name); + return -1; + } else { + ints &= readl(®s->intrenable); + if (ints == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + } + } + + /* dbg("Interrupt: %x frame: %x", ints, + le16_to_cpu(ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) + stat = 0xff; + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump(ohci, 1); +#else + wait_ms(1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset(ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + wait_ms(1); + writel(OHCI_INTR_WDH, ®s->intrdisable); + (void)readl(®s->intrdisable); /* flush */ + stat = dl_done_list(&gohci); + writel(OHCI_INTR_WDH, ®s->intrenable); + (void)readl(®s->intrdisable); /* flush */ + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel(OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; + wait_ms(1); + writel(OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel(OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel(ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci(ohci_t *ohci) +{ + dbg("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset(ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ +#ifdef CONFIG_PCI_OHCI + pci_dev_t pdev; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant init */ + if (usb_cpu_init()) + return -1; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant init */ + if (usb_board_init()) + return -1; +#endif + memset(&gohci, 0, sizeof(ohci_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset(phcca, 0, sizeof(struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; +#ifdef CONFIG_PCI_OHCI + pdev = pci_find_devices(ohci_pci_ids, CONFIG_PCI_OHCI_DEVNO); + + if (pdev != -1) { + u16 vid, did; + u32 base; + pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); + pci_read_config_word(pdev, PCI_DEVICE_ID, &did); + printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", + vid, did, (pdev >> 16) & 0xff, + (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); + printf("OHCI regs address 0x%08x\n", base); + gohci.regs = (struct ohci_regs *)base; + } else + return -1; +#else + gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE; +#endif + + gohci.flags = 0; + gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME; + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + err ("can't reset usb-%s", gohci.slot_name); +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant cleanup */ + usb_board_init_fail(); +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant cleanup */ + usb_cpu_init_fail(); +#endif + return -1; + } + + if (hc_start(&gohci) < 0) { + err("can't start usb-%s", gohci.slot_name); + hc_release_ohci(&gohci); + /* Initialization failed */ +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant cleanup */ + usb_board_stop(); +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant cleanup */ + usb_cpu_stop(); +#endif + return -1; + } + +#ifdef DEBUG + ohci_dump(&gohci, 1); +#else + wait_ms(1); +#endif + ohci_inited = 1; + return 0; +} + +int usb_lowlevel_stop(void) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset(&gohci); + +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant cleanup */ + if (usb_board_stop()) + return -1; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant cleanup */ + if (usb_cpu_stop()) + return -1; +#endif + /* This driver is no longer initialised. It needs a new low-level + * init (board/cpu) before it can be used again. */ + ohci_inited = 0; + return 0; +} diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h new file mode 100644 index 0000000..a547337 --- /dev/null +++ b/drivers/usb/host/ohci.h @@ -0,0 +1,483 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * 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_board_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] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +static const char *cc_to_string[16] = { + "No Error", + "CRC: Last data packet from endpoint contained a CRC error.", + "BITSTUFFING: Last data packet from endpoint contained a bit " \ + "stuffing violation", + "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ + "that did not match the expected value.", + "STALL: TD was moved to the Done Queue because the endpoint returned" \ + " a STALL PID", + "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ + "not provide a handshake (OUT)", + "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ + "(IN) or handshake (OUT)", + "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ + "value is not defined.", + "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ + "either the size of the maximum data packet allowed\n" \ + "from the endpoint (found in MaximumPacketSize field\n" \ + "of ED) or the remaining buffer size.", + "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ + "and that amount was not sufficient to fill the\n" \ + "specified buffer", + "reserved1", + "reserved2", + "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ + "than it could be written to system memory", + "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ + "system memory fast enough to keep up with data USB " \ + "data rate.", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(1)", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(2)", +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + void *purb; + __u32 unused[2]; +} __attribute((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __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; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#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))); + + +/* + * Maximum number of root hub ports. + */ +#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS +# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!" +#endif + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + +/* Some EHCI controls */ +#define EHCI_USBCMD_OFF 0x20 +#define EHCI_USBCMD_HCRESET (1 << 1) + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + struct usb_device *dev; + int state; + unsigned long pipe; + void *transfer_buffer; + int transfer_buffer_length; + int interval; + int actual_length; + int finished; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, + int interval, int load); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ptd[i].usb_dev == NULL) + { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void +ed_free (struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c new file mode 100644 index 0000000..db611b6 --- /dev/null +++ b/drivers/usb/host/r8a66597-hcd.c @@ -0,0 +1,945 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008 Yoshihiro Shimoda + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +#include "r8a66597.h" + +#ifdef R8A66597_DEBUG +#define R8A66597_DPRINT printf +#else +#define R8A66597_DPRINT(...) +#endif + +static const char hcd_name[] = "r8a66597_hcd"; +static unsigned short clock = CONFIG_R8A66597_XTAL; +static unsigned short vif = CONFIG_R8A66597_LDRV; +static unsigned short endian = CONFIG_R8A66597_ENDIAN; +static struct r8a66597 gr8a66597; + +static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport) +{ + int i; + + *hub_devnum = 0; + *hubport = 0; + + /* check a device connected to root_hub */ + if ((dev->parent && dev->parent->devnum == 1) || + (dev->devnum == 1)) + return; + + for (i = 0; i < USB_MAXCHILDREN; i++) { + if (dev->parent->children[i] == dev) { + *hub_devnum = (u8)dev->parent->devnum; + *hubport = i; + return; + } + } + + printf("get_hub_data error.\n"); +} + +static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address, + struct usb_device *dev, int port) +{ + u16 val, usbspd, upphub, hubport; + unsigned long devadd_reg = get_devadd_addr(r8a66597_address); + + get_hub_data(dev, &upphub, &hubport); + usbspd = r8a66597->speed; + val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001); + r8a66597_write(r8a66597, val, devadd_reg); +} + +static int r8a66597_clock_enable(struct r8a66597 *r8a66597) +{ + u16 tmp; + int i = 0; + +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + do { + r8a66597_write(r8a66597, SCKE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & SCKE) != SCKE); + r8a66597_write(r8a66597, 0x04, 0x02); +#else + do { + r8a66597_write(r8a66597, USBE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & USBE) != USBE); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0); + + i = 0; + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + do { + udelay(1000); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 500) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & SCKE) != SCKE); +#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */ + + return 0; +} + +static void r8a66597_clock_disable(struct r8a66597 *r8a66597) +{ + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + udelay(1); +#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + r8a66597_bclr(r8a66597, PLLC, SYSCFG0); + r8a66597_bclr(r8a66597, XCKE, SYSCFG0); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); +#endif +} + +static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port) +{ + u16 val; + + val = port ? DRPD : DCFM | DRPD; + r8a66597_bset(r8a66597, val, get_syscfg_reg(port)); + r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); + + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port)); +} + +static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port) +{ + u16 val, tmp; + + r8a66597_write(r8a66597, 0, get_intenb_reg(port)); + r8a66597_write(r8a66597, 0, get_intsts_reg(port)); + + r8a66597_port_power(r8a66597, port, 0); + + do { + tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS; + udelay(640); + } while (tmp == EDGESTS); + + val = port ? DRPD : DCFM | DRPD; + r8a66597_bclr(r8a66597, val, get_syscfg_reg(port)); + r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); +} + +static int enable_controller(struct r8a66597 *r8a66597) +{ + int ret, port; + + ret = r8a66597_clock_enable(r8a66597); + if (ret < 0) + return ret; + + r8a66597_bset(r8a66597, vif & LDRV, PINCFG); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, INTL, SOFCFG); + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTENB1); + r8a66597_write(r8a66597, 0, INTENB2); + + r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL); + r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL); + r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL); + r8a66597_bset(r8a66597, TRNENSEL, SOFCFG); + + for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) + r8a66597_enable_port(r8a66597, port); + + return 0; +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ + int i; + + if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE)) + return; + + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTSTS0); + + r8a66597_write(r8a66597, 0, D0FIFOSEL); + r8a66597_write(r8a66597, 0, D1FIFOSEL); + r8a66597_write(r8a66597, 0, DCPCFG); + r8a66597_write(r8a66597, 0x40, DCPMAXP); + r8a66597_write(r8a66597, 0, DCPCTR); + + for (i = 0; i <= 10; i++) + r8a66597_write(r8a66597, 0, get_devadd_addr(i)); + for (i = 1; i <= 5; i++) { + r8a66597_write(r8a66597, 0, get_pipetre_addr(i)); + r8a66597_write(r8a66597, 0, get_pipetrn_addr(i)); + } + for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) { + r8a66597_write(r8a66597, 0, get_pipectr_addr(i)); + r8a66597_write(r8a66597, i, PIPESEL); + r8a66597_write(r8a66597, 0, PIPECFG); + r8a66597_write(r8a66597, 0, PIPEBUF); + r8a66597_write(r8a66597, 0, PIPEMAXP); + r8a66597_write(r8a66597, 0, PIPEPERI); + } + + for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) + r8a66597_disable_port(r8a66597, i); + + r8a66597_clock_disable(r8a66597); +} + +static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg, + u16 mask, u16 loop) +{ + u16 tmp; + int i = 0; + + do { + tmp = r8a66597_read(r8a66597, reg); + if (i++ > 1000000) { + printf("register%lx, loop %x is timeout\n", reg, loop); + break; + } + } while ((tmp & mask) != loop); +} + +static void pipe_buffer_setting(struct r8a66597 *r8a66597, + struct usb_device *dev, unsigned long pipe) +{ + u16 val = 0; + u16 pipenum, bufnum, maxpacket; + + if (usb_pipein(pipe)) { + pipenum = BULK_IN_PIPENUM; + bufnum = BULK_IN_BUFNUM; + maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + } else { + pipenum = BULK_OUT_PIPENUM; + bufnum = BULK_OUT_BUFNUM; + maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; + } + + if (r8a66597->pipe_config & (1 << pipenum)) + return; + r8a66597->pipe_config |= (1 << pipenum); + + r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum)); + r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum)); + r8a66597_write(r8a66597, pipenum, PIPESEL); + + /* FIXME: This driver support bulk transfer only. */ + if (!usb_pipein(pipe)) + val |= R8A66597_DIR; + else + val |= R8A66597_SHTNAK; + val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe); + r8a66597_write(r8a66597, val, PIPECFG); + + r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF); + r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) | + maxpacket, PIPEMAXP); + r8a66597_write(r8a66597, 0, PIPEPERI); + r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum)); +} + +static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev, + struct devrequest *setup) +{ + int i; + unsigned short *p = (unsigned short *)setup; + unsigned long setup_addr = USBREQ; + u16 intsts1; + int timeout = 3000; + u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum; + + r8a66597_write(r8a66597, make_devsel(devsel) | + (8 << dev->maxpacketsize), DCPMAXP); + r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1); + + for (i = 0; i < 4; i++) { + r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr); + setup_addr += 2; + } + r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + r8a66597_write(r8a66597, SUREQ, DCPCTR); + + while (1) { + intsts1 = r8a66597_read(r8a66597, INTSTS1); + if (intsts1 & SACK) + break; + if (intsts1 & SIGN) { + printf("setup packet send error\n"); + return -1; + } + if (timeout-- < 0) { + printf("setup packet timeout\n"); + return -1; + } + udelay(500); + } + + return 0; +} + +static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len) +{ + u16 tmp, bufsize; + u16 *buf; + size_t size; + + R8A66597_DPRINT("%s\n", __func__); + + r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM, + MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM); + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set (%x)\n", __func__, tmp); + return -1; + } + + /* prepare parameters */ + bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; + buf = (u16 *)(buffer + dev->act_len); + size = min((int)bufsize, transfer_len - dev->act_len); + + /* write fifo */ + r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + if (buffer) { + r8a66597_write_fifo(r8a66597, CFIFO, buf, size); + r8a66597_write(r8a66597, BVAL, CFIFOCTR); + } + + /* update parameters */ + dev->act_len += size; + + r8a66597_mdfy(r8a66597, PID_BUF, PID, + get_pipectr_addr(BULK_OUT_PIPENUM)); + + while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM))) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + + if (dev->act_len >= transfer_len) + r8a66597_mdfy(r8a66597, PID_NAK, PID, + get_pipectr_addr(BULK_OUT_PIPENUM)); + + return 0; +} + +static int receive_bulk_packet(struct r8a66597 *r8a66597, + struct usb_device *dev, + unsigned long pipe, + void *buffer, int transfer_len) +{ + u16 tmp; + u16 *buf; + const u16 pipenum = BULK_IN_PIPENUM; + int rcv_len; + int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + + R8A66597_DPRINT("%s\n", __func__); + + /* prepare */ + if (dev->act_len == 0) { + r8a66597_mdfy(r8a66597, PID_NAK, PID, + get_pipectr_addr(pipenum)); + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + + r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum)); + r8a66597_write(r8a66597, + (transfer_len + maxpacket - 1) / maxpacket, + get_pipetrn_addr(pipenum)); + r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum)); + + r8a66597_mdfy(r8a66597, PID_BUF, PID, + get_pipectr_addr(pipenum)); + } + + r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum); + + while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum))) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set. (%x)\n", __func__, tmp); + return -1; + } + + buf = (u16 *)(buffer + dev->act_len); + rcv_len = tmp & DTLN; + dev->act_len += rcv_len; + + if (buffer) { + if (rcv_len == 0) + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + else + r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len); + } + + return 0; +} + +static int receive_control_packet(struct r8a66597 *r8a66597, + struct usb_device *dev, + void *buffer, int transfer_len) +{ + u16 tmp; + int rcv_len; + + /* FIXME: limit transfer size : 64byte or less */ + + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_bset(r8a66597, SQSET, DCPCTR); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + + while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001)) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + + r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set. (%x)\n", __func__, tmp); + return -1; + } + + rcv_len = tmp & DTLN; + dev->act_len += rcv_len; + + r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + + if (buffer) { + if (rcv_len == 0) + r8a66597_write(r8a66597, BCLR, DCPCTR); + else + r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len); + } + + return 0; +} + +static int send_status_packet(struct r8a66597 *r8a66597, + unsigned long pipe) +{ + r8a66597_bset(r8a66597, SQSET, DCPCTR); + r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + + if (usb_pipein(pipe)) { + r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR); + } else { + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + } + r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + + while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001)) + if (ctrlc()) + return -1; + + return 0; +} + +static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port) +{ + int count = R8A66597_MAX_SAMPLING; + unsigned short syssts, old_syssts; + + R8A66597_DPRINT("%s\n", __func__); + + old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); + while (count > 0) { + wait_ms(R8A66597_RH_POLL_TIME); + + syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); + if (syssts == old_syssts) { + count--; + } else { + count = R8A66597_MAX_SAMPLING; + old_syssts = syssts; + } + } +} + +static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port) +{ + wait_ms(10); + r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port)); + wait_ms(50); + r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port)); + wait_ms(50); +} + +static int check_usb_device_connecting(struct r8a66597 *r8a66597) +{ + int timeout = 10000; /* 100usec * 10000 = 1sec */ + int i; + + for (i = 0; i < 5; i++) { + /* check a usb cable connect */ + while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) { + if (timeout-- < 0) { + printf("%s timeout.\n", __func__); + return -1; + } + udelay(100); + } + + /* check a data line */ + r8a66597_check_syssts(r8a66597, 0); + + r8a66597_bus_reset(r8a66597, 0); + r8a66597->speed = get_rh_usb_speed(r8a66597, 0); + + if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) { + r8a66597->port_change = USB_PORT_STAT_C_CONNECTION; + r8a66597->port_status = USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE; + return 0; /* success */ + } + + R8A66597_DPRINT("USB device has detached. retry = %d\n", i); + r8a66597_write(r8a66597, ~DTCH, INTSTS1); + } + + return -1; /* fail */ +} + +/* based on usb_ohci.c */ +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; */ + + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 34, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '8', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'A', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '6', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '6', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '5', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '9', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '7', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + unsigned char data[32]; + + R8A66597_DPRINT("%s\n", __func__); + + if (usb_pipeint(pipe)) { + printf("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = cpu_to_le16 (cmd->value); + wIndex = cpu_to_le16 (cmd->index); + wLength = cpu_to_le16 (cmd->length); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + *(__u16 *)buffer = cpu_to_le16(1); + len = 2; + break; + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)buffer = cpu_to_le16(0); + len = 2; + break; + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)buffer = cpu_to_le16(0); + len = 2; + break; + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)buffer = cpu_to_le32(0); + len = 4; + break; + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *)buffer = cpu_to_le32(r8a66597->port_status | + (r8a66597->port_change << 16)); + len = 4; + break; + case RH_CLEAR_FEATURE | RH_ENDPOINT: + case RH_CLEAR_FEATURE | RH_CLASS: + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_C_PORT_CONNECTION: + r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION; + break; + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + break; + case (RH_PORT_RESET): + r8a66597_bus_reset(r8a66597, 0); + break; + case (RH_PORT_POWER): + break; + case (RH_PORT_ENABLE): + break; + } + break; + case RH_SET_ADDRESS: + gr8a66597.rh_devnum = wValue; + break; + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + memcpy(buffer, root_hub_dev_des, len); + break; + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + memcpy(buffer, root_hub_config_des, len); + break; + case (0x03): /* string descriptors */ + if (wValue == 0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + memcpy(buffer, root_hub_str_index0, len); + } + if (wValue == 0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + memcpy(buffer, root_hub_str_index1, len); + } + break; + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = 0x00000001; + + data[0] = 9; /* min length; */ + data[1] = 0x29; + data[2] = temp & RH_A_NDP; + data[3] = 0; + if (temp & RH_A_PSM) + data[3] |= 0x1; + if (temp & RH_A_NOCP) + data[3] |= 0x10; + else if (temp & RH_A_OCPM) + data[3] |= 0x8; + + /* corresponds to data[4-7] */ + data[5] = (temp & RH_A_POTPGT) >> 24; + data[7] = temp & RH_B_DR; + if (data[2] < 7) { + data[8] = 0xff; + } else { + data[0] += 2; + data[8] = (temp & RH_B_DR) >> 8; + data[10] = data[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data[0], wLength)); + memcpy(buffer, data, len); + break; + } + + case RH_GET_CONFIGURATION: + *(__u8 *) buffer = 0x01; + len = 1; + break; + case RH_SET_CONFIGURATION: + break; + default: + R8A66597_DPRINT("unsupported root hub command"); + stat = USB_ST_STALLED; + } + + wait_ms(1); + + len = min_t(int, len, leni); + + dev->act_len = len; + dev->status = stat; + + return stat; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + int ret = 0; + + R8A66597_DPRINT("%s\n", __func__); + R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n", + pipe, buffer, transfer_len, dev->devnum); + + set_devadd(r8a66597, dev->devnum, dev, 0); + + pipe_buffer_setting(r8a66597, dev, pipe); + + dev->act_len = 0; + while (dev->act_len < transfer_len && ret == 0) { + if (ctrlc()) + return -1; + + if (usb_pipein(pipe)) + ret = receive_bulk_packet(r8a66597, dev, pipe, buffer, + transfer_len); + else + ret = send_bulk_packet(r8a66597, dev, pipe, buffer, + transfer_len); + } + + if (ret == 0) + dev->status = 0; + + return ret; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ? + 0 : dev->devnum; + + R8A66597_DPRINT("%s\n", __func__); + if (usb_pipedevice(pipe) == r8a66597->rh_devnum) + return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + + R8A66597_DPRINT("%s: setup\n", __func__); + set_devadd(r8a66597, r8a66597_address, dev, 0); + + if (send_setup_packet(r8a66597, dev, setup) < 0) { + printf("setup packet send error\n"); + return -1; + } + + dev->act_len = 0; + if (usb_pipein(pipe)) + if (receive_control_packet(r8a66597, dev, buffer, + transfer_len) < 0) + return -1; + + if (send_status_packet(r8a66597, pipe) < 0) + return -1; + + dev->status = 0; + + return 0; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + /* no implement */ + R8A66597_DPRINT("%s\n", __func__); + return 0; +} + +void usb_event_poll(void) +{ + /* no implement */ + R8A66597_DPRINT("%s\n", __func__); +} + +int usb_lowlevel_init(void) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + + R8A66597_DPRINT("%s\n", __func__); + + memset(r8a66597, 0, sizeof(r8a66597)); + r8a66597->reg = CONFIG_R8A66597_BASE_ADDR; + + disable_controller(r8a66597); + wait_ms(100); + + enable_controller(r8a66597); + r8a66597_port_power(r8a66597, 0 , 1); + + /* check usb device */ + check_usb_device_connecting(r8a66597); + + wait_ms(50); + + return 0; +} + +int usb_lowlevel_stop(void) +{ + disable_controller(&gr8a66597); + + return 0; +} diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h new file mode 100644 index 0000000..9af6446 --- /dev/null +++ b/drivers/usb/host/r8a66597.h @@ -0,0 +1,659 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008 Yoshihiro Shimoda + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#define SYSCFG0 0x00 +#define SYSCFG1 0x02 +#define SYSSTS0 0x04 +#define SYSSTS1 0x06 +#define DVSTCTR0 0x08 +#define DVSTCTR1 0x0A +#define TESTMODE 0x0C +#define PINCFG 0x0E +#define DMA0CFG 0x10 +#define DMA1CFG 0x12 +#define CFIFO 0x14 +#define D0FIFO 0x18 +#define D1FIFO 0x1C +#define CFIFOSEL 0x20 +#define CFIFOCTR 0x22 +#define CFIFOSIE 0x24 +#define D0FIFOSEL 0x28 +#define D0FIFOCTR 0x2A +#define D1FIFOSEL 0x2C +#define D1FIFOCTR 0x2E +#define INTENB0 0x30 +#define INTENB1 0x32 +#define INTENB2 0x34 +#define BRDYENB 0x36 +#define NRDYENB 0x38 +#define BEMPENB 0x3A +#define SOFCFG 0x3C +#define INTSTS0 0x40 +#define INTSTS1 0x42 +#define INTSTS2 0x44 +#define BRDYSTS 0x46 +#define NRDYSTS 0x48 +#define BEMPSTS 0x4A +#define FRMNUM 0x4C +#define UFRMNUM 0x4E +#define USBADDR 0x50 +#define USBREQ 0x54 +#define USBVAL 0x56 +#define USBINDX 0x58 +#define USBLENG 0x5A +#define DCPCFG 0x5C +#define DCPMAXP 0x5E +#define DCPCTR 0x60 +#define PIPESEL 0x64 +#define PIPECFG 0x68 +#define PIPEBUF 0x6A +#define PIPEMAXP 0x6C +#define PIPEPERI 0x6E +#define PIPE1CTR 0x70 +#define PIPE2CTR 0x72 +#define PIPE3CTR 0x74 +#define PIPE4CTR 0x76 +#define PIPE5CTR 0x78 +#define PIPE6CTR 0x7A +#define PIPE7CTR 0x7C +#define PIPE8CTR 0x7E +#define PIPE9CTR 0x80 +#define PIPE1TRE 0x90 +#define PIPE1TRN 0x92 +#define PIPE2TRE 0x94 +#define PIPE2TRN 0x96 +#define PIPE3TRE 0x98 +#define PIPE3TRN 0x9A +#define PIPE4TRE 0x9C +#define PIPE4TRN 0x9E +#define PIPE5TRE 0xA0 +#define PIPE5TRN 0xA2 +#define DEVADD0 0xD0 +#define DEVADD1 0xD2 +#define DEVADD2 0xD4 +#define DEVADD3 0xD6 +#define DEVADD4 0xD8 +#define DEVADD5 0xDA +#define DEVADD6 0xDC +#define DEVADD7 0xDE +#define DEVADD8 0xE0 +#define DEVADD9 0xE2 +#define DEVADDA 0xE4 + +/* System Configuration Control Register */ +#define XTAL 0xC000 /* b15-14: Crystal selection */ +#define XTAL48 0x8000 /* 48MHz */ +#define XTAL24 0x4000 /* 24MHz */ +#define XTAL12 0x0000 /* 12MHz */ +#define XCKE 0x2000 /* b13: External clock enable */ +#define PLLC 0x0800 /* b11: PLL control */ +#define SCKE 0x0400 /* b10: USB clock enable */ +#define PCSDIS 0x0200 /* b9: not CS wakeup */ +#define LPSME 0x0100 /* b8: Low power sleep mode */ +#define HSE 0x0080 /* b7: Hi-speed enable */ +#define DCFM 0x0040 /* b6: Controller function select */ +#define DRPD 0x0020 /* b5: D+/- pull down control */ +#define DPRPU 0x0010 /* b4: D+ pull up control */ +#define USBE 0x0001 /* b0: USB module operation enable */ + +/* System Configuration Status Register */ +#define OVCBIT 0x8000 /* b15-14: Over-current bit */ +#define OVCMON 0xC000 /* b15-14: Over-current monitor */ +#define SOFEA 0x0020 /* b5: SOF monitor */ +#define IDMON 0x0004 /* b3: ID-pin monitor */ +#define LNST 0x0003 /* b1-0: D+, D- line status */ +#define SE1 0x0003 /* SE1 */ +#define FS_KSTS 0x0002 /* Full-Speed K State */ +#define FS_JSTS 0x0001 /* Full-Speed J State */ +#define LS_JSTS 0x0002 /* Low-Speed J State */ +#define LS_KSTS 0x0001 /* Low-Speed K State */ +#define SE0 0x0000 /* SE0 */ + +/* Device State Control Register */ +#define EXTLP0 0x0400 /* b10: External port */ +#define VBOUT 0x0200 /* b9: VBUS output */ +#define WKUP 0x0100 /* b8: Remote wakeup */ +#define RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define USBRST 0x0040 /* b6: USB reset enable */ +#define RESUME 0x0020 /* b5: Resume enable */ +#define UACT 0x0010 /* b4: USB bus enable */ +#define RHST 0x0007 /* b1-0: Reset handshake status */ +#define HSPROC 0x0004 /* HS handshake is processing */ +#define HSMODE 0x0003 /* Hi-Speed mode */ +#define FSMODE 0x0002 /* Full-Speed mode */ +#define LSMODE 0x0001 /* Low-Speed mode */ +#define UNDECID 0x0000 /* Undecided */ + +/* Test Mode Register */ +#define UTST 0x000F /* b3-0: Test select */ +#define H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define H_TST_K 0x000A /* HOST TEST K */ +#define H_TST_J 0x0009 /* HOST TEST J */ +#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define P_TST_K 0x0002 /* PERI TEST K */ +#define P_TST_J 0x0001 /* PERI TEST J */ +#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +/* Data Pin Configuration Register */ +#define LDRV 0x8000 /* b15: Drive Current Adjust */ +#define VIF1 0x0000 /* VIF = 1.8V */ +#define VIF3 0x8000 /* VIF = 3.3V */ +#define INTA 0x0001 /* b1: USB INT-pin active */ + +/* DMAx Pin Configuration Register */ +#define DREQA 0x4000 /* b14: Dreq active select */ +#define BURST 0x2000 /* b13: Burst mode */ +#define DACKA 0x0400 /* b10: Dack active select */ +#define DFORM 0x0380 /* b9-7: DMA mode select */ +#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define DENDA 0x0040 /* b6: Dend active select */ +#define PKTM 0x0020 /* b5: Packet mode */ +#define DENDE 0x0010 /* b4: Dend enable */ +#define OBUS 0x0004 /* b2: OUTbus mode */ + +/* CFIFO/DxFIFO Port Select Register */ +#define RCNT 0x8000 /* b15: Read count mode */ +#define REW 0x4000 /* b14: Buffer rewind */ +#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define DREQE 0x1000 /* b12: DREQ output enable */ +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define MBW 0x0800 +#else +#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */ +#endif +#define MBW_8 0x0000 /* 8bit */ +#define MBW_16 0x0400 /* 16bit */ +#define BIGEND 0x0100 /* b8: Big endian mode */ +#define BYTE_LITTLE 0x0000 /* little dendian */ +#define BYTE_BIG 0x0100 /* big endifan */ +#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define CURPIPE 0x000F /* b2-0: PIPE select */ + +/* CFIFO/DxFIFO Port Control Register */ +#define BVAL 0x8000 /* b15: Buffer valid flag */ +#define BCLR 0x4000 /* b14: Buffer clear */ +#define FRDY 0x2000 /* b13: FIFO ready */ +#define DTLN 0x0FFF /* b11-0: FIFO received data length */ + +/* Interrupt Enable Register 0 */ +#define VBSE 0x8000 /* b15: VBUS interrupt */ +#define RSME 0x4000 /* b14: Resume interrupt */ +#define SOFE 0x2000 /* b13: Frame update interrupt */ +#define DVSE 0x1000 /* b12: Device state transition interrupt */ +#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ + +/* Interrupt Enable Register 1 */ +#define OVRCRE 0x8000 /* b15: Over-current interrupt */ +#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ +#define EOFERRE 0x0040 /* b6: EOF error interrupt */ +#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ + +/* BRDY Interrupt Enable/Status Register */ +#define BRDY9 0x0200 /* b9: PIPE9 */ +#define BRDY8 0x0100 /* b8: PIPE8 */ +#define BRDY7 0x0080 /* b7: PIPE7 */ +#define BRDY6 0x0040 /* b6: PIPE6 */ +#define BRDY5 0x0020 /* b5: PIPE5 */ +#define BRDY4 0x0010 /* b4: PIPE4 */ +#define BRDY3 0x0008 /* b3: PIPE3 */ +#define BRDY2 0x0004 /* b2: PIPE2 */ +#define BRDY1 0x0002 /* b1: PIPE1 */ +#define BRDY0 0x0001 /* b1: PIPE0 */ + +/* NRDY Interrupt Enable/Status Register */ +#define NRDY9 0x0200 /* b9: PIPE9 */ +#define NRDY8 0x0100 /* b8: PIPE8 */ +#define NRDY7 0x0080 /* b7: PIPE7 */ +#define NRDY6 0x0040 /* b6: PIPE6 */ +#define NRDY5 0x0020 /* b5: PIPE5 */ +#define NRDY4 0x0010 /* b4: PIPE4 */ +#define NRDY3 0x0008 /* b3: PIPE3 */ +#define NRDY2 0x0004 /* b2: PIPE2 */ +#define NRDY1 0x0002 /* b1: PIPE1 */ +#define NRDY0 0x0001 /* b1: PIPE0 */ + +/* BEMP Interrupt Enable/Status Register */ +#define BEMP9 0x0200 /* b9: PIPE9 */ +#define BEMP8 0x0100 /* b8: PIPE8 */ +#define BEMP7 0x0080 /* b7: PIPE7 */ +#define BEMP6 0x0040 /* b6: PIPE6 */ +#define BEMP5 0x0020 /* b5: PIPE5 */ +#define BEMP4 0x0010 /* b4: PIPE4 */ +#define BEMP3 0x0008 /* b3: PIPE3 */ +#define BEMP2 0x0004 /* b2: PIPE2 */ +#define BEMP1 0x0002 /* b1: PIPE1 */ +#define BEMP0 0x0001 /* b0: PIPE0 */ + +/* SOF Pin Configuration Register */ +#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ +#define BRDYM 0x0040 /* b6: BRDY clear timing */ +#define INTL 0x0020 /* b5: Interrupt sense select */ +#define EDGESTS 0x0010 /* b4: */ +#define SOFMODE 0x000C /* b3-2: SOF pin select */ +#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ +#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +/* Interrupt Status Register 0 */ +#define VBINT 0x8000 /* b15: VBUS interrupt */ +#define RESM 0x4000 /* b14: Resume interrupt */ +#define SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define DVST 0x1000 /* b12: Device state transition interrupt */ +#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define VBSTS 0x0080 /* b7: VBUS input port */ +#define DVSQ 0x0070 /* b6-4: Device state */ +#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define DS_SUSP 0x0040 /* Suspend */ +#define DS_CNFG 0x0030 /* Configured */ +#define DS_ADDS 0x0020 /* Address */ +#define DS_DFLT 0x0010 /* Default */ +#define DS_POWR 0x0000 /* Powered */ +#define DVSQS 0x0030 /* b5-4: Device state */ +#define VALID 0x0008 /* b3: Setup packet detected flag */ +#define CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define CS_SQER 0x0006 /* Sequence error */ +#define CS_WRND 0x0005 /* Control write nodata status stage */ +#define CS_WRSS 0x0004 /* Control write status stage */ +#define CS_WRDS 0x0003 /* Control write data stage */ +#define CS_RDSS 0x0002 /* Control read status stage */ +#define CS_RDDS 0x0001 /* Control read data stage */ +#define CS_IDST 0x0000 /* Idle or setup stage */ + +/* Interrupt Status Register 1 */ +#define OVRCR 0x8000 /* b15: Over-current interrupt */ +#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define DTCH 0x1000 /* b12: Detach sense interrupt */ +#define ATTCH 0x0800 /* b11: Attach sense interrupt */ +#define EOFERR 0x0040 /* b6: EOF-error interrupt */ +#define SIGN 0x0020 /* b5: Setup ignore interrupt */ +#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ + +/* Frame Number Register */ +#define OVRN 0x8000 /* b15: Overrun error */ +#define CRCE 0x4000 /* b14: Received data error */ +#define FRNM 0x07FF /* b10-0: Frame number */ + +/* Micro Frame Number Register */ +#define UFRNM 0x0007 /* b2-0: Micro frame number */ + +/* Default Control Pipe Maxpacket Size Register */ +/* Pipe Maxpacket Size Register */ +#define DEVSEL 0xF000 /* b15-14: Device address select */ +#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ + +/* Default Control Pipe Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define SUREQ 0x4000 /* b14: Send USB request */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define SUREQCLR 0x0800 /* b11: stop setup request */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PINGE 0x0010 /* b4: ping enable */ +#define CCPL 0x0004 /* b2: Enable control transfer complete */ +#define PID 0x0003 /* b1-0: Response PID */ +#define PID_STALL11 0x0003 /* STALL */ +#define PID_STALL 0x0002 /* STALL */ +#define PID_BUF 0x0001 /* BUF */ +#define PID_NAK 0x0000 /* NAK */ + +/* Pipe Window Select Register */ +#define PIPENM 0x0007 /* b2-0: Pipe select */ + +/* Pipe Configuration Register */ +#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ +#define R8A66597_ISO 0xC000 /* Isochronous */ +#define R8A66597_INT 0x8000 /* Interrupt */ +#define R8A66597_BULK 0x4000 /* Bulk */ +#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ +#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ +#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ +#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ + +/* Pipe Buffer Configuration Register */ +#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ +#define PIPE0BUF 256 +#define PIPExBUF 64 + +/* Pipe Maxpacket Size Register */ +#define MXPS 0x07FF /* b10-0: Maxpacket size */ + +/* Pipe Cycle Configuration Register */ +#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ +#define IITV 0x0007 /* b2-0: Isochronous interval */ + +/* Pipex Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define ATREPM 0x0400 /* b10: Auto repeat mode */ +#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PID 0x0003 /* b1-0: Response PID */ + +/* PIPExTRE */ +#define TRENB 0x0200 /* b9: Transaction counter enable */ +#define TRCLR 0x0100 /* b8: Transaction counter clear */ + +/* PIPExTRN */ +#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +/* DEVADDx */ +#define UPPHUB 0x7800 +#define HUBPORT 0x0700 +#define USBSPD 0x00C0 +#define RTPORT 0x0001 + +#define R8A66597_MAX_NUM_PIPE 10 +#define R8A66597_BUF_BSIZE 8 +#define R8A66597_MAX_DEVICE 10 +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define R8A66597_MAX_ROOT_HUB 1 +#else +#define R8A66597_MAX_ROOT_HUB 2 +#endif +#define R8A66597_MAX_SAMPLING 5 +#define R8A66597_RH_POLL_TIME 10 + +#define BULK_IN_PIPENUM 3 +#define BULK_IN_BUFNUM 8 + +#define BULK_OUT_PIPENUM 4 +#define BULK_OUT_BUFNUM 40 + +#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5)) +#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9)) +#define make_devsel(addr) (addr << 12) + +struct r8a66597 { + unsigned long reg; + unsigned short pipe_config; /* bit field */ + unsigned short port_status; + unsigned short port_change; + u16 speed; /* HSMODE or FSMODE or LSMODE */ + unsigned char rh_devnum; +}; + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ + return inw(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, + unsigned long offset, void *buf, + int len) +{ + int i; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + unsigned long fifoaddr = r8a66597->reg + offset; + unsigned long count; + unsigned long *p = buf; + + count = len / 4; + for (i = 0; i < count; i++) + inl(p[i], r8a66597->reg + offset); + + if (len & 0x00000003) { + unsigned long tmp = inl(fifoaddr); + memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03); + } +#else + unsigned short *p = buf; + + len = (len + 1) / 2; + for (i = 0; i < len; i++) + p[i] = inw(r8a66597->reg + offset); +#endif +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, + unsigned long offset) +{ + outw(val, r8a66597->reg + offset); +} + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, + unsigned long offset, void *buf, + int len) +{ + int i; + unsigned long fifoaddr = r8a66597->reg + offset; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + unsigned long count; + unsigned char *pb; + unsigned long *p = buf; + + count = len / 4; + for (i = 0; i < count; i++) + outl(p[i], fifoaddr); + + if (len & 0x00000003) { + pb = (unsigned char *)buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) { + if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) + outb(pb[i], fifoaddr + i); + else + outb(pb[i], fifoaddr + 3 - i); + } + } +#else + int odd = len & 0x0001; + unsigned short *p = buf; + + len = len / 2; + for (i = 0; i < len; i++) + outw(p[i], fifoaddr); + + if (odd) { + unsigned char *pb = (unsigned char *)(buf + len); + outb(*pb, fifoaddr); + } +#endif +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + +static inline unsigned long get_syscfg_reg(int port) +{ + return port == 0 ? SYSCFG0 : SYSCFG1; +} + +static inline unsigned long get_syssts_reg(int port) +{ + return port == 0 ? SYSSTS0 : SYSSTS1; +} + +static inline unsigned long get_dvstctr_reg(int port) +{ + return port == 0 ? DVSTCTR0 : DVSTCTR1; +} + +static inline unsigned long get_dmacfg_reg(int port) +{ + return port == 0 ? DMA0CFG : DMA1CFG; +} + +static inline unsigned long get_intenb_reg(int port) +{ + return port == 0 ? INTENB1 : INTENB2; +} + +static inline unsigned long get_intsts_reg(int port) +{ + return port == 0 ? INTSTS1 : INTSTS2; +} + +static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + return r8a66597_read(r8a66597, dvstctr_reg) & RHST; +} + +static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, + int power) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + if (power) + r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); + else + r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); +} + +#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) +#define get_devadd_addr(address) (DEVADD0 + address * 2) + + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#endif /* __R8A66597_H__ */ diff --git a/drivers/usb/host/s3c64xx-hcd.c b/drivers/usb/host/s3c64xx-hcd.c new file mode 100644 index 0000000..274a4ed --- /dev/null +++ b/drivers/usb/host/s3c64xx-hcd.c @@ -0,0 +1,45 @@ +/* + * URB OHCI HCD (Host Controller Driver) initialization for USB on the S3C64XX. + * + * Copyright (C) 2008, + * Guennadi Liakhovetski, DENX Software Engineering + * + * 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 +#include + +int usb_cpu_init(void) +{ + OTHERS_REG |= 0x10000; + return 0; +} + +int usb_cpu_stop(void) +{ + OTHERS_REG &= ~0x10000; + return 0; +} + +void usb_cpu_init_fail(void) +{ + OTHERS_REG &= ~0x10000; +} diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c new file mode 100644 index 0000000..82a8b36 --- /dev/null +++ b/drivers/usb/host/sl811-hcd.c @@ -0,0 +1,734 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + * Adam Richter, Gregory P. Smith; + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua + * + * 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 +#include +#include +#include "sl811.h" + +#include "../../../board/kup/common/kup.h" + +#ifdef __PPC__ +# define EIEIO __asm__ volatile ("eieio") +#else +# define EIEIO /* nothing */ +#endif + +#define SL811_ADR (0x50000000) +#define SL811_DAT (0x50000001) + +#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) + +#ifdef SL811_DEBUG +static int debug = 9; +#endif + +static int root_hub_devnum = 0; +static struct usb_port_status rh_status = { 0 };/* root hub port status */ + +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, + void *data, int buf_len, struct devrequest *cmd); + +static void sl811_write (__u8 index, __u8 data) +{ + *(volatile unsigned char *) (SL811_ADR) = index; + EIEIO; + *(volatile unsigned char *) (SL811_DAT) = data; + EIEIO; +} + +static __u8 sl811_read (__u8 index) +{ + __u8 data; + + *(volatile unsigned char *) (SL811_ADR) = index; + EIEIO; + data = *(volatile unsigned char *) (SL811_DAT); + EIEIO; + return (data); +} + +/* + * Read consecutive bytes of data from the SL811H/SL11H buffer + */ +static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size) +{ + *(volatile unsigned char *) (SL811_ADR) = offset; + EIEIO; + while (size--) { + *buf++ = *(volatile unsigned char *) (SL811_DAT); + EIEIO; + } +} + +/* + * Write consecutive bytes of data to the SL811H/SL11H buffer + */ +static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size) +{ + *(volatile unsigned char *) (SL811_ADR) = offset; + EIEIO; + while (size--) { + *(volatile unsigned char *) (SL811_DAT) = *buf++; + EIEIO; + } +} + +int usb_init_kup4x (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + int i; + unsigned char tmp; + + memctl = &immap->im_memctl; + memctl->memc_or7 = 0xFFFF8726; + memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */ + /* BP 14 low = USB ON */ + immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); + /* PB 14 nomal port */ + immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); + /* output */ + immap->im_cpm.cp_pbdir |= (BP_USB_VCC); + + puts ("USB: "); + + for (i = 0x10; i < 0xff; i++) { + sl811_write(i, i); + tmp = (sl811_read(i)); + if (tmp != i) { + printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); + return (-1); + } + } + printf ("SL811 ready\n"); + return (0); +} + +/* + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Return: 0 = no device attached; 1 = USB device attached + */ +static int sl811_hc_reset(void) +{ + int status ; + + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + + mdelay(20); + + /* Disable hardware SOF generation, clear all irq status. */ + sl811_write(SL811_CTRL1, 0); + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + status = sl811_read(SL811_INTRSTS); + + if (status & SL811_INTR_NOTPRESENT) { + /* Device is not present */ + PDEBUG(0, "Device not present\n"); + rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); + rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(SL811_INTR, SL811_INTR_INSRMV); + return 0; + } + + /* Send SOF to address 0, endpoint 0. */ + sl811_write(SL811_LEN_B, 0); + sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); + sl811_write(SL811_DEV_B, 0x00); + sl811_write(SL811_SOFLOW, SL811_12M_LOW); + + if (status & SL811_INTR_SPEED_FULL) { + /* full speed device connect directly to root hub */ + PDEBUG (0, "Full speed Device attached\n"); + + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); + rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; + rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + } else { + /* slow speed device connect directly to root-hub */ + PDEBUG(0, "Low speed Device attached\n"); + + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); + rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + } + + rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); + + return 1; +} + +int usb_lowlevel_init(void) +{ + root_hub_devnum = 0; + sl811_hc_reset(); + return 0; +} + +int usb_lowlevel_stop(void) +{ + sl811_hc_reset(); + return 0; +} + +static int calc_needed_buswidth(int bytes, int need_preamble) +{ + return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048; +} + +static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len) +{ + __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; + __u16 status = 0; + int err = 0, time_start = get_timer(0); + int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && + usb_pipeslow(pipe); + + if (len > 239) + return -1; + + if (usb_pipeout(pipe)) + ctrl |= SL811_USB_CTRL_DIR_OUT; + if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) + ctrl |= SL811_USB_CTRL_TOGGLE_1; + if (need_preamble) + ctrl |= SL811_USB_CTRL_PREAMBLE; + + sl811_write(SL811_INTRSTS, 0xff); + + while (err < 3) { + sl811_write(SL811_ADDR_A, 0x10); + sl811_write(SL811_LEN_A, len); + if (usb_pipeout(pipe) && len) + sl811_write_buf(0x10, buffer, len); + + if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && + sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) + ctrl |= SL811_USB_CTRL_SOF; + else + ctrl &= ~SL811_USB_CTRL_SOF; + + sl811_write(SL811_CTRL_A, ctrl); + while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { + if (5*CONFIG_SYS_HZ < get_timer(time_start)) { + printf("USB transmit timed out\n"); + return -USB_ST_CRC_ERR; + } + } + + sl811_write(SL811_INTRSTS, 0xff); + status = sl811_read(SL811_STS_A); + + if (status & SL811_USB_STS_ACK) { + int remainder = sl811_read(SL811_CNT_A); + if (remainder) { + PDEBUG(0, "usb transfer remainder = %d\n", remainder); + len -= remainder; + } + if (usb_pipein(pipe) && len) + sl811_read_buf(0x10, buffer, len); + return len; + } + + if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) + continue; + + PDEBUG(0, "usb transfer error %#x\n", (int)status); + err++; + } + + err = 0; + + if (status & SL811_USB_STS_ERROR) + err |= USB_ST_BUF_ERR; + if (status & SL811_USB_STS_TIMEOUT) + err |= USB_ST_CRC_ERR; + if (status & SL811_USB_STS_STALL) + err |= USB_ST_STALLED; + + return -err; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int max = usb_maxpacket(dev, pipe); + int done = 0; + + PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", + usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + + dev->status = 0; + + sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); + sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); + while (done < len) { + int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, + max > len - done ? len - done : max); + if (res < 0) { + dev->status = -res; + return res; + } + + if (!dir_out && res < max) /* short packet */ + break; + + done += res; + usb_dotoggle(dev, ep, dir_out); + } + + dev->act_len = done; + + return 0; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len,struct devrequest *setup) +{ + int done = 0; + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + + dev->status = 0; + + if (devnum == root_hub_devnum) + return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); + + PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n", + devnum, ep, buffer, len, (int)setup->requesttype, + (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64); + + sl811_write(SL811_DEV_A, devnum); + sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep)); + /* setup phase */ + usb_settoggle(dev, ep, 1, 0); + if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep), + (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { + int dir_in = usb_pipein(pipe); + int max = usb_maxpacket(dev, pipe); + + /* data phase */ + sl811_write(SL811_PIDEP_A, + PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep)); + usb_settoggle(dev, ep, usb_pipeout(pipe), 1); + while (done < len) { + int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, + max > len - done ? len - done : max); + if (res < 0) { + PDEBUG(0, "status data failed!\n"); + dev->status = -res; + return 0; + } + done += res; + usb_dotoggle(dev, ep, usb_pipeout(pipe)); + if (dir_in && res < max) /* short packet */ + break; + } + + /* status phase */ + sl811_write(SL811_PIDEP_A, + PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep)); + usb_settoggle(dev, ep, !usb_pipeout(pipe), 1); + if (sl811_send_packet(dev, + !dir_in ? usb_rcvctrlpipe(dev, ep) : + usb_sndctrlpipe(dev, ep), + 0, 0) < 0) { + PDEBUG(0, "status phase failed!\n"); + dev->status = -1; + } + } else { + PDEBUG(0, "setup phase failed!\n"); + dev->status = -1; + } + + dev->act_len = done; + + return done; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe, + buffer, len, interval); + return -1; +} + +/* + * SL811 Virtual Root Hub + */ + +/* Device descriptor */ +static __u8 sl811_rh_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 sl811_rh_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* root hub class descriptor*/ +static __u8 sl811_rh_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x01, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x50, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/* + * helper routine for returning string descriptors in UTF-16LE + * input can actually be ISO-8859-1; ASCII is its 7-bit subset + */ +static int ascii2utf (char *s, u8 *utf, int utfmax) +{ + int retval; + + for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *s++; + *utf++ = 0; + } + return retval; +} + +/* + * root_hub_string is used by each host controller's root hub code, + * so that they're identified consistently throughout the system. + */ +static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) +{ + char buf [30]; + + /* assert (len > (2 * (sizeof (buf) + 1))); + assert (strlen (type) <= 8);*/ + + /* language ids */ + if (id == 0) { + *data++ = 4; *data++ = 3; /* 4 bytes data */ + *data++ = 0; *data++ = 0; /* some language id */ + return 4; + + /* serial number */ + } else if (id == 1) { + sprintf (buf, "%#x", serial); + + /* product description */ + } else if (id == 2) { + sprintf (buf, "USB %s Root Hub", type); + + /* id 3 == vendor description */ + + /* unsupported IDs --> "stall" */ + } else + return 0; + + ascii2utf (buf, data + 2, len - 2); + data [0] = 2 + strlen(buf) * 2; + data [1] = 3; + return data [0]; +} + +/* helper macro */ +#define OK(x) len = (x); break + +/* + * This function handles all USB request to the the virtual root hub + */ +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, + void *data, int buf_len, struct devrequest *cmd) +{ + __u8 data_buf[16]; + __u8 *bufp = data_buf; + int len = 0; + int status = 0; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipeint(pipe)) { + PDEBUG(0, "interrupt transfer unimplemented!\n"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu (cmd->value); + wIndex = le16_to_cpu (cmd->index); + wLength = le16_to_cpu (cmd->length); + + PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n", + bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength); + + /* Request Destination: + without flags: Device, + USB_RECIP_INTERFACE: interface, + USB_RECIP_ENDPOINT: endpoint, + USB_TYPE_CLASS means HUB here, + USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here + */ + switch (bmRType_bReq) { + case RH_GET_STATUS: + *(__u16 *)bufp = cpu_to_le16(1); + OK(2); + + case RH_GET_STATUS | USB_RECIP_INTERFACE: + *(__u16 *)bufp = cpu_to_le16(0); + OK(2); + + case RH_GET_STATUS | USB_RECIP_ENDPOINT: + *(__u16 *)bufp = cpu_to_le16(0); + OK(2); + + case RH_GET_STATUS | USB_TYPE_CLASS: + *(__u32 *)bufp = cpu_to_le32(0); + OK(4); + + case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: + *(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus); + OK(4); + + case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: + switch (wValue) { + case 1: + OK(0); + } + break; + + case RH_CLEAR_FEATURE | USB_TYPE_CLASS: + switch (wValue) { + case C_HUB_LOCAL_POWER: + OK(0); + + case C_HUB_OVER_CURRENT: + OK(0); + } + break; + + case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_SUSPEND: + rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_POWER: + rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_C_CONNECTION: + rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + OK(0); + + case USB_PORT_FEAT_C_ENABLE: + rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; + OK(0); + + case USB_PORT_FEAT_C_SUSPEND: + rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; + OK(0); + + case USB_PORT_FEAT_C_OVER_CURRENT: + rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; + OK(0); + + case USB_PORT_FEAT_C_RESET: + rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; + OK(0); + } + break; + + case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_RESET: + rh_status.wPortStatus |= USB_PORT_STAT_RESET; + rh_status.wPortChange = 0; + rh_status.wPortChange |= USB_PORT_STAT_C_RESET; + rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; + rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_POWER: + rh_status.wPortStatus |= USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_ENABLE: + rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + } + break; + + case RH_SET_ADDRESS: + root_hub_devnum = wValue; + OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case USB_DT_DEVICE: + len = sizeof(sl811_rh_dev_des); + bufp = sl811_rh_dev_des; + OK(len); + + case USB_DT_CONFIG: + len = sizeof(sl811_rh_config_des); + bufp = sl811_rh_config_des; + OK(len); + + case USB_DT_STRING: + len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength); + if (len > 0) { + bufp = data; + OK(len); + } + + default: + status = -32; + } + break; + + case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: + len = sizeof(sl811_rh_hub_des); + bufp = sl811_rh_hub_des; + OK(len); + + case RH_GET_CONFIGURATION: + bufp[0] = 0x01; + OK(1); + + case RH_SET_CONFIGURATION: + OK(0); + + default: + PDEBUG(1, "unsupported root hub command\n"); + status = -32; + } + + len = min(len, buf_len); + if (data != bufp) + memcpy(data, bufp, len); + + PDEBUG(5, "len = %d, status = %d\n", len, status); + + usb_dev->status = status; + usb_dev->act_len = len; + + return status == 0 ? len : status; +} diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h new file mode 100644 index 0000000..c1f9f01 --- /dev/null +++ b/drivers/usb/host/sl811.h @@ -0,0 +1,104 @@ +#ifndef __UBOOT_SL811_H +#define __UBOOT_SL811_H + +#undef SL811_DEBUG + +#ifdef SL811_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (debug >= (level)) printf("[%s:%d] " fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* Sl811 host control register */ +#define SL811_CTRL_A 0x00 +#define SL811_ADDR_A 0x01 +#define SL811_LEN_A 0x02 +#define SL811_STS_A 0x03 /* read */ +#define SL811_PIDEP_A 0x03 /* write */ +#define SL811_CNT_A 0x04 /* read */ +#define SL811_DEV_A 0x04 /* write */ +#define SL811_CTRL1 0x05 +#define SL811_INTR 0x06 +#define SL811_CTRL_B 0x08 +#define SL811_ADDR_B 0x09 +#define SL811_LEN_B 0x0A +#define SL811_STS_B 0x0B /* read */ +#define SL811_PIDEP_B 0x0B /* write */ +#define SL811_CNT_B 0x0C /* read */ +#define SL811_DEV_B 0x0C /* write */ +#define SL811_INTRSTS 0x0D /* write clears bitwise */ +#define SL811_HWREV 0x0E /* read */ +#define SL811_SOFLOW 0x0E /* write */ +#define SL811_SOFCNTDIV 0x0F /* read */ +#define SL811_CTRL2 0x0F /* write */ + +/* USB control register bits (addr 0x00 and addr 0x08) */ +#define SL811_USB_CTRL_ARM 0x01 +#define SL811_USB_CTRL_ENABLE 0x02 +#define SL811_USB_CTRL_DIR_OUT 0x04 +#define SL811_USB_CTRL_ISO 0x10 +#define SL811_USB_CTRL_SOF 0x20 +#define SL811_USB_CTRL_TOGGLE_1 0x40 +#define SL811_USB_CTRL_PREAMBLE 0x80 + +/* USB status register bits (addr 0x03 and addr 0x0B) */ +#define SL811_USB_STS_ACK 0x01 +#define SL811_USB_STS_ERROR 0x02 +#define SL811_USB_STS_TIMEOUT 0x04 +#define SL811_USB_STS_TOGGLE_1 0x08 +#define SL811_USB_STS_SETUP 0x10 +#define SL811_USB_STS_OVERFLOW 0x20 +#define SL811_USB_STS_NAK 0x40 +#define SL811_USB_STS_STALL 0x80 + +/* Control register 1 bits (addr 0x05) */ +#define SL811_CTRL1_SOF 0x01 +#define SL811_CTRL1_RESET 0x08 +#define SL811_CTRL1_JKSTATE 0x10 +#define SL811_CTRL1_SPEED_LOW 0x20 +#define SL811_CTRL1_SUSPEND 0x40 + +/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ +#define SL811_INTR_DONE_A 0x01 +#define SL811_INTR_DONE_B 0x02 +#define SL811_INTR_SOF 0x10 +#define SL811_INTR_INSRMV 0x20 +#define SL811_INTR_DETECT 0x40 +#define SL811_INTR_NOTPRESENT 0x40 +#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0x0E) */ +#define SL811_HWR_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0x0F) */ +#define SL811_CTL2_SOFHI 0x3F +#define SL811_CTL2_DSWAP 0x40 +#define SL811_CTL2_HOST 0x80 + +/* Set up for 1-ms SOF time. */ +#define SL811_12M_LOW 0xE0 +#define SL811_12M_HI 0x2E + +#define SL811_DATA_START 0x10 +#define SL811_DATA_LIMIT 240 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 + + +#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) + +#endif /* __UBOOT_SL811_H */ diff --git a/drivers/usb/isp116x-hcd.c b/drivers/usb/isp116x-hcd.c deleted file mode 100644 index 348e404..0000000 --- a/drivers/usb/isp116x-hcd.c +++ /dev/null @@ -1,1441 +0,0 @@ -/* - * ISP116x HCD (Host Controller Driver) for u-boot. - * - * Copyright (C) 2006-2007 Rodolfo Giometti - * Copyright (C) 2006-2007 Eurotech S.p.A. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * - * - * Derived in part from the SL811 HCD driver "u-boot/drivers/usb/sl811_usb.c" - * (original copyright message follows): - * - * (C) Copyright 2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * This code is based on linux driver for sl811hs chip, source at - * drivers/usb/host/sl811.c: - * - * SL811 Host Controller Interface driver for USB. - * - * Copyright (c) 2003/06, Courage Co., Ltd. - * - * Based on: - * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, - * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, - * Adam Richter, Gregory P. Smith; - * 2.Original SL811 driver (hc_sl811.o) by Pei Liu - * 3.Rewrited as sl811.o by Yin Aihua - * - * [[GNU/GPL disclaimer]] - * - * and in part from AU1x00 OHCI HCD driver "u-boot/cpu/mips/au1x00_usb_ohci.c" - * (original copyright message follows): - * - * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. - * - * (C) Copyright 2003 - * Gary Jennejohn, DENX Software Engineering - * - * [[GNU/GPL disclaimer]] - * - * Note: Part of this code has been derived from linux - */ - -#include -#include -#include -#include -#include - -/* - * ISP116x chips require certain delays between accesses to its - * registers. The following timing options exist. - * - * 1. Configure your memory controller (the best) - * 2. Use ndelay (easiest, poorest). For that, enable the following macro. - * - * Value is in microseconds. - */ -#ifdef ISP116X_HCD_USE_UDELAY -#define UDELAY 1 -#endif - -/* - * On some (slowly?) machines an extra delay after data packing into - * controller's FIFOs is required, * otherwise you may get the following - * error: - * - * uboot> usb start - * (Re)start USB... - * USB: scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT - * isp116x: isp116x_submit_job: ****** FIFO not ready! ****** - * - * USB device not responding, giving up (status=4) - * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** - * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** - * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** - * 3 USB Device(s) found - * scanning bus for storage devices... 0 Storage Device(s) found - * - * Value is in milliseconds. - */ -#ifdef ISP116X_HCD_USE_EXTRA_DELAY -#define EXTRA_DELAY 2 -#endif - -/* - * Enable the following defines if you wish enable debugging messages. - */ -#undef DEBUG /* enable debugging messages */ -#undef TRACE /* enable tracing code */ -#undef VERBOSE /* verbose debugging messages */ - -#include "isp116x.h" - -#define DRIVER_VERSION "08 Jan 2007" -static const char hcd_name[] = "isp116x-hcd"; - -struct isp116x isp116x_dev; -struct isp116x_platform_data isp116x_board; -static int got_rhsc; /* root hub status change */ -struct usb_device *devgone; /* device which was disconnected */ -static int rh_devnum; /* address of Root Hub endpoint */ - -/* ------------------------------------------------------------------------- */ - -#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL)) -#define min_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) - -/* ------------------------------------------------------------------------- */ - -static int isp116x_reset(struct isp116x *isp116x); - -/* --- Debugging functions ------------------------------------------------- */ - -#define isp116x_show_reg(d, r) { \ - if ((r) < 0x20) { \ - DBG("%-12s[%02x]: %08x", #r, \ - r, isp116x_read_reg32(d, r)); \ - } else { \ - DBG("%-12s[%02x]: %04x", #r, \ - r, isp116x_read_reg16(d, r)); \ - } \ -} - -#define isp116x_show_regs(d) { \ - isp116x_show_reg(d, HCREVISION); \ - isp116x_show_reg(d, HCCONTROL); \ - isp116x_show_reg(d, HCCMDSTAT); \ - isp116x_show_reg(d, HCINTSTAT); \ - isp116x_show_reg(d, HCINTENB); \ - isp116x_show_reg(d, HCFMINTVL); \ - isp116x_show_reg(d, HCFMREM); \ - isp116x_show_reg(d, HCFMNUM); \ - isp116x_show_reg(d, HCLSTHRESH); \ - isp116x_show_reg(d, HCRHDESCA); \ - isp116x_show_reg(d, HCRHDESCB); \ - isp116x_show_reg(d, HCRHSTATUS); \ - isp116x_show_reg(d, HCRHPORT1); \ - isp116x_show_reg(d, HCRHPORT2); \ - isp116x_show_reg(d, HCHWCFG); \ - isp116x_show_reg(d, HCDMACFG); \ - isp116x_show_reg(d, HCXFERCTR); \ - isp116x_show_reg(d, HCuPINT); \ - isp116x_show_reg(d, HCuPINTENB); \ - isp116x_show_reg(d, HCCHIPID); \ - isp116x_show_reg(d, HCSCRATCH); \ - isp116x_show_reg(d, HCITLBUFLEN); \ - isp116x_show_reg(d, HCATLBUFLEN); \ - isp116x_show_reg(d, HCBUFSTAT); \ - isp116x_show_reg(d, HCRDITL0LEN); \ - isp116x_show_reg(d, HCRDITL1LEN); \ -} - -#if defined(TRACE) - -static int isp116x_get_current_frame_number(struct usb_device *usb_dev) -{ - struct isp116x *isp116x = &isp116x_dev; - - return isp116x_read_reg32(isp116x, HCFMNUM); -} - -static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, char *str) -{ -#if defined(VERBOSE) - int i; -#endif - - DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx", - str, - isp116x_get_current_frame_number(dev), - usb_pipedevice(pipe), - usb_pipeendpoint(pipe), - usb_pipeout(pipe) ? 'O' : 'I', - usb_pipetype(pipe) < 2 ? - (usb_pipeint(pipe) ? - "INTR" : "ISOC") : - (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status); -#if defined(VERBOSE) - if (len > 0 && buffer) { - printf(__FILE__ ": data(%d):", len); - for (i = 0; i < 16 && i < len; i++) - printf(" %02x", ((__u8 *) buffer)[i]); - printf("%s\n", i < len ? "..." : ""); - } -#endif -} - -#define PTD_DIR_STR(ptd) ({char __c; \ - switch(PTD_GET_DIR(ptd)){ \ - case 0: __c = 's'; break; \ - case 1: __c = 'o'; break; \ - default: __c = 'i'; break; \ - }; __c;}) - -/* - Dump PTD info. The code documents the format - perfectly, right :) -*/ -static inline void dump_ptd(struct ptd *ptd) -{ -#if defined(VERBOSE) - int k; -#endif - - DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x", - PTD_GET_CC(ptd), - PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd), - PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), - PTD_GET_TOGGLE(ptd), - PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd)); -#if defined(VERBOSE) - printf("isp116x: %s: PTD(byte): ", __FUNCTION__); - for (k = 0; k < sizeof(struct ptd); ++k) - printf("%02x ", ((u8 *) ptd)[k]); - printf("\n"); -#endif -} - -static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type) -{ -#if defined(VERBOSE) - int k; - - if (type == 0 /* 0ut data */ ) { - printf("isp116x: %s: out data: ", __FUNCTION__); - for (k = 0; k < PTD_GET_LEN(ptd); ++k) - printf("%02x ", ((u8 *) buf)[k]); - printf("\n"); - } - if (type == 1 /* 1n data */ ) { - printf("isp116x: %s: in data: ", __FUNCTION__); - for (k = 0; k < PTD_GET_COUNT(ptd); ++k) - printf("%02x ", ((u8 *) buf)[k]); - printf("\n"); - } - - if (PTD_GET_LAST(ptd)) - DBG("--- last PTD ---"); -#endif -} - -#else - -#define dump_msg(dev, pipe, buffer, len, str) do { } while (0) -#define dump_pkt(dev, pipe, buffer, len, setup, str, small) do {} while (0) - -#define dump_ptd(ptd) do {} while (0) -#define dump_ptd_data(ptd, buf, type) do {} while (0) - -#endif - -/* --- Virtual Root Hub ---------------------------------------------------- */ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = { - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x01, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = { - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x00, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x02, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static unsigned char root_hub_str_index0[] = { - 0x04, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 0x09, /* __u8 lang ID */ - 0x04, /* __u8 lang ID */ -}; - -static unsigned char root_hub_str_index1[] = { - 0x22, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 'I', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'S', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'P', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '1', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '1', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '6', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'x', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'R', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 't', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'u', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'b', /* __u8 Unicode */ - 0, /* __u8 Unicode */ -}; - -/* - * Hub class-specific descriptor is constructed dynamically - */ - -/* --- Virtual root hub management functions ------------------------------- */ - -static int rh_check_port_status(struct isp116x *isp116x) -{ - u32 temp, ndp, i; - int res; - - res = -1; - temp = isp116x_read_reg32(isp116x, HCRHSTATUS); - ndp = (temp & RH_A_NDP); - for (i = 0; i < ndp; i++) { - temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i); - /* check for a device disconnect */ - if (((temp & (RH_PS_PESC | RH_PS_CSC)) == - (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { - res = i; - break; - } - } - return res; -} - -/* --- HC management functions --------------------------------------------- */ - -/* Write len bytes to fifo, pad till 32-bit boundary - */ -static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) -{ - u8 *dp = (u8 *) buf; - u16 *dp2 = (u16 *) buf; - u16 w; - int quot = len % 4; - - if ((unsigned long)dp2 & 1) { - /* not aligned */ - for (; len > 1; len -= 2) { - w = *dp++; - w |= *dp++ << 8; - isp116x_raw_write_data16(isp116x, w); - } - if (len) - isp116x_write_data16(isp116x, (u16) * dp); - } else { - /* aligned */ - for (; len > 1; len -= 2) - isp116x_raw_write_data16(isp116x, *dp2++); - if (len) - isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); - } - if (quot == 1 || quot == 2) - isp116x_raw_write_data16(isp116x, 0); -} - -/* Read len bytes from fifo and then read till 32-bit boundary - */ -static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) -{ - u8 *dp = (u8 *) buf; - u16 *dp2 = (u16 *) buf; - u16 w; - int quot = len % 4; - - if ((unsigned long)dp2 & 1) { - /* not aligned */ - for (; len > 1; len -= 2) { - w = isp116x_raw_read_data16(isp116x); - *dp++ = w & 0xff; - *dp++ = (w >> 8) & 0xff; - } - if (len) - *dp = 0xff & isp116x_read_data16(isp116x); - } else { - /* aligned */ - for (; len > 1; len -= 2) - *dp2++ = isp116x_raw_read_data16(isp116x); - if (len) - *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); - } - if (quot == 1 || quot == 2) - isp116x_raw_read_data16(isp116x); -} - -/* Write PTD's and data for scheduled transfers into the fifo ram. - * Fifo must be empty and ready */ -static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev, - unsigned long pipe, struct ptd *ptd, int n, void *data, - int len) -{ - int buflen = n * sizeof(struct ptd) + len; - int i, done; - - DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); - - isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); - isp116x_write_reg16(isp116x, HCXFERCTR, buflen); - isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); - - done = 0; - for (i = 0; i < n; i++) { - DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); - - dump_ptd(&ptd[i]); - isp116x_write_data16(isp116x, ptd[i].count); - isp116x_write_data16(isp116x, ptd[i].mps); - isp116x_write_data16(isp116x, ptd[i].len); - isp116x_write_data16(isp116x, ptd[i].faddr); - - dump_ptd_data(&ptd[i], (__u8 *) data + done, 0); - write_ptddata_to_fifo(isp116x, - (__u8 *) data + done, - PTD_GET_LEN(&ptd[i])); - - done += PTD_GET_LEN(&ptd[i]); - } -} - -/* Read the processed PTD's and data from fifo ram back to URBs' buffers. - * Fifo must be full and done */ -static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev, - unsigned long pipe, struct ptd *ptd, int n, void *data, - int len) -{ - int buflen = n * sizeof(struct ptd) + len; - int i, done, cc, ret; - - isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); - isp116x_write_reg16(isp116x, HCXFERCTR, buflen); - isp116x_write_addr(isp116x, HCATLPORT); - - ret = TD_CC_NOERROR; - done = 0; - for (i = 0; i < n; i++) { - DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); - - ptd[i].count = isp116x_read_data16(isp116x); - ptd[i].mps = isp116x_read_data16(isp116x); - ptd[i].len = isp116x_read_data16(isp116x); - ptd[i].faddr = isp116x_read_data16(isp116x); - dump_ptd(&ptd[i]); - - read_ptddata_from_fifo(isp116x, - (__u8 *) data + done, - PTD_GET_LEN(&ptd[i])); - dump_ptd_data(&ptd[i], (__u8 *) data + done, 1); - - done += PTD_GET_LEN(&ptd[i]); - - cc = PTD_GET_CC(&ptd[i]); - - /* Data underrun means basically that we had more buffer space than - * the function had data. It is perfectly normal but upper levels have - * to know how much we actually transferred. - */ - if (cc == TD_NOTACCESSED || - (cc != TD_CC_NOERROR && (ret == TD_CC_NOERROR || ret == TD_DATAUNDERRUN))) - ret = cc; - } - - DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); - - return ret; -} - -/* Interrupt handling - */ -static int isp116x_interrupt(struct isp116x *isp116x) -{ - u16 irqstat; - u32 intstat; - int ret = 0; - - isp116x_write_reg16(isp116x, HCuPINTENB, 0); - irqstat = isp116x_read_reg16(isp116x, HCuPINT); - isp116x_write_reg16(isp116x, HCuPINT, irqstat); - DBG(">>>>>> irqstat %x <<<<<<", irqstat); - - if (irqstat & HCuPINT_ATL) { - DBG(">>>>>> HCuPINT_ATL <<<<<<"); - udelay(500); - ret = 1; - } - - if (irqstat & HCuPINT_OPR) { - intstat = isp116x_read_reg32(isp116x, HCINTSTAT); - isp116x_write_reg32(isp116x, HCINTSTAT, intstat); - DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat); - - if (intstat & HCINT_UE) { - ERR("unrecoverable error, controller disabled"); - - /* FIXME: be optimistic, hope that bug won't repeat - * often. Make some non-interrupt context restart the - * controller. Count and limit the retries though; - * either hardware or software errors can go forever... - */ - isp116x_reset(isp116x); - ret = -1; - return -1; - } - - if (intstat & HCINT_RHSC) { - got_rhsc = 1; - ret = 1; - /* When root hub or any of its ports is going - to come out of suspend, it may take more - than 10ms for status bits to stabilize. */ - wait_ms(20); - } - - if (intstat & HCINT_SO) { - ERR("schedule overrun"); - ret = -1; - } - - irqstat &= ~HCuPINT_OPR; - } - - return ret; -} - -/* With one PTD we can transfer almost 1K in one go; - * HC does the splitting into endpoint digestible transactions - */ -struct ptd ptd[1]; - -static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe) -{ - unsigned mpck = usb_maxpacket(dev, pipe); - - /* One PTD can transfer 1023 bytes but try to always - * transfer multiples of endpoint buffer size - */ - return 1023 / mpck * mpck; -} - -/* Do an USB transfer - */ -static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe, - int dir, void *buffer, int len) -{ - struct isp116x *isp116x = &isp116x_dev; - int type = usb_pipetype(pipe); - int epnum = usb_pipeendpoint(pipe); - int max = usb_maxpacket(dev, pipe); - int dir_out = usb_pipeout(pipe); - int speed_low = usb_pipeslow(pipe); - int i, done = 0, stat, timeout, cc; - - /* 500 frames or 0.5s timeout when function is busy and NAKs transactions for a while */ - int retries = 500; - - DBG("------------------------------------------------"); - dump_msg(dev, pipe, buffer, len, "SUBMIT"); - DBG("------------------------------------------------"); - - if (len >= 1024) { - ERR("Too big job"); - dev->status = USB_ST_CRC_ERR; - return -1; - } - - if (isp116x->disabled) { - ERR("EPIPE"); - dev->status = USB_ST_CRC_ERR; - return -1; - } - - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - ERR("ENODEV"); - dev->status = USB_ST_CRC_ERR; - return USB_ST_CRC_ERR; - } - - if (!max) { - ERR("pipesize for pipe %lx is zero", pipe); - dev->status = USB_ST_CRC_ERR; - return -1; - } - - if (type == PIPE_ISOCHRONOUS) { - ERR("isochronous transfers not supported"); - dev->status = USB_ST_CRC_ERR; - return -1; - } - - /* FIFO not empty? */ - if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) { - ERR("****** FIFO not empty! ******"); - dev->status = USB_ST_BUF_ERR; - return -1; - } - - retry: - isp116x_write_reg32(isp116x, HCINTSTAT, 0xff); - - /* Prepare the PTD data */ - ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | - PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out)); - ptd->mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum) | PTD_LAST_MSK; - ptd->len = PTD_LEN(len) | PTD_DIR(dir); - ptd->faddr = PTD_FA(usb_pipedevice(pipe)); - -retry_same: - /* Pack data into FIFO ram */ - pack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); -#ifdef EXTRA_DELAY - wait_ms(EXTRA_DELAY); -#endif - - /* Start the data transfer */ - - /* Allow more time for a BULK device to react - some are slow */ - if (usb_pipebulk(pipe)) - timeout = 5000; - else - timeout = 100; - - /* Wait for it to complete */ - for (;;) { - /* Check whether the controller is done */ - stat = isp116x_interrupt(isp116x); - - if (stat < 0) { - dev->status = USB_ST_CRC_ERR; - break; - } - if (stat > 0) - break; - - /* Check the timeout */ - if (--timeout) - udelay(1); - else { - ERR("CTL:TIMEOUT "); - stat = USB_ST_CRC_ERR; - break; - } - } - - /* We got an Root Hub Status Change interrupt */ - if (got_rhsc) { - isp116x_show_regs(isp116x); - - got_rhsc = 0; - - /* Abuse timeout */ - timeout = rh_check_port_status(isp116x); - if (timeout >= 0) { - /* - * FIXME! NOTE! AAAARGH! - * This is potentially dangerous because it assumes - * that only one device is ever plugged in! - */ - devgone = dev; - } - } - - /* Ok, now we can read transfer status */ - - /* FIFO not ready? */ - if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) { - ERR("****** FIFO not ready! ******"); - dev->status = USB_ST_BUF_ERR; - return -1; - } - - /* Unpack data from FIFO ram */ - cc = unpack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); - - i = PTD_GET_COUNT(ptd); - done += i; - buffer += i; - len -= i; - - /* There was some kind of real problem; Prepare the PTD again - * and retry from the failed transaction on - */ - if (cc && cc != TD_NOTACCESSED && cc != TD_DATAUNDERRUN) { - if (retries >= 100) { - retries -= 100; - /* The chip will have toggled the toggle bit for the failed - * transaction too. We have to toggle it back. - */ - usb_settoggle(dev, epnum, dir_out, !PTD_GET_TOGGLE(ptd)); - goto retry; - } - } - /* "Normal" errors; TD_NOTACCESSED would mean in effect that the function have NAKed - * the transactions from the first on for the whole frame. It may be busy and we retry - * with the same PTD. PTD_ACTIVE (and not TD_NOTACCESSED) would mean that some of the - * PTD didn't make it because the function was busy or the frame ended before the PTD - * finished. We prepare the rest of the data and try again. - */ - else if (cc == TD_NOTACCESSED || PTD_GET_ACTIVE(ptd) || (cc != TD_DATAUNDERRUN && PTD_GET_COUNT(ptd) < PTD_GET_LEN(ptd))) { - if (retries) { - --retries; - if (cc == TD_NOTACCESSED && PTD_GET_ACTIVE(ptd) && !PTD_GET_COUNT(ptd)) goto retry_same; - usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); - goto retry; - } - } - - if (cc != TD_CC_NOERROR && cc != TD_DATAUNDERRUN) { - DBG("****** completition code error %x ******", cc); - switch (cc) { - case TD_CC_BITSTUFFING: - dev->status = USB_ST_BIT_ERR; - break; - case TD_CC_STALL: - dev->status = USB_ST_STALLED; - break; - case TD_BUFFEROVERRUN: - case TD_BUFFERUNDERRUN: - dev->status = USB_ST_BUF_ERR; - break; - default: - dev->status = USB_ST_CRC_ERR; - } - return -cc; - } - else usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); - - dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)"); - - dev->status = 0; - return done; -} - -/* Adapted from au1x00_usb_ohci.c - */ -static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, - struct devrequest *cmd) -{ - struct isp116x *isp116x = &isp116x_dev; - u32 tmp = 0; - - int leni = transfer_len; - int len = 0; - int stat = 0; - u32 datab[4]; - u8 *data_buf = (u8 *) datab; - u16 bmRType_bReq; - u16 wValue; - u16 wIndex; - u16 wLength; - - if (usb_pipeint(pipe)) { - INFO("Root-Hub submit IRQ: NOT implemented"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = swap_16(cmd->value); - wIndex = swap_16(cmd->index); - wLength = swap_16(cmd->length); - - DBG("--- HUB ----------------------------------------"); - DBG("submit rh urb, req=%x val=%#x index=%#x len=%d", - bmRType_bReq, wValue, wIndex, wLength); - dump_msg(dev, pipe, buffer, transfer_len, "RH"); - DBG("------------------------------------------------"); - - switch (bmRType_bReq) { - case RH_GET_STATUS: - DBG("RH_GET_STATUS"); - - *(__u16 *) data_buf = swap_16(1); - len = 2; - break; - - case RH_GET_STATUS | RH_INTERFACE: - DBG("RH_GET_STATUS | RH_INTERFACE"); - - *(__u16 *) data_buf = swap_16(0); - len = 2; - break; - - case RH_GET_STATUS | RH_ENDPOINT: - DBG("RH_GET_STATUS | RH_ENDPOINT"); - - *(__u16 *) data_buf = swap_16(0); - len = 2; - break; - - case RH_GET_STATUS | RH_CLASS: - DBG("RH_GET_STATUS | RH_CLASS"); - - tmp = isp116x_read_reg32(isp116x, HCRHSTATUS); - - *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE)); - len = 4; - break; - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS"); - - tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1); - *(__u32 *) data_buf = swap_32(tmp); - isp116x_show_regs(isp116x); - len = 4; - break; - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - DBG("RH_CLEAR_FEATURE | RH_ENDPOINT"); - - switch (wValue) { - case RH_ENDPOINT_STALL: - DBG("C_HUB_ENDPOINT_STALL"); - len = 0; - break; - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - DBG("RH_CLEAR_FEATURE | RH_CLASS"); - - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - DBG("C_HUB_LOCAL_POWER"); - len = 0; - break; - - case RH_C_HUB_OVER_CURRENT: - DBG("C_HUB_OVER_CURRENT"); - isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); - len = 0; - break; - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS"); - - switch (wValue) { - case RH_PORT_ENABLE: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_CCS); - len = 0; - break; - - case RH_PORT_SUSPEND: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_POCI); - len = 0; - break; - - case RH_PORT_POWER: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_LSDA); - len = 0; - break; - - case RH_C_PORT_CONNECTION: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_CSC); - len = 0; - break; - - case RH_C_PORT_ENABLE: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PESC); - len = 0; - break; - - case RH_C_PORT_SUSPEND: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PSSC); - len = 0; - break; - - case RH_C_PORT_OVER_CURRENT: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_POCI); - len = 0; - break; - - case RH_C_PORT_RESET: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PRSC); - len = 0; - break; - - default: - ERR("invalid wValue"); - stat = USB_ST_STALLED; - } - - isp116x_show_regs(isp116x); - - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS"); - - switch (wValue) { - case RH_PORT_SUSPEND: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PSS); - len = 0; - break; - - case RH_PORT_RESET: - /* Spin until any current reset finishes */ - while (1) { - tmp = - isp116x_read_reg32(isp116x, - HCRHPORT1 + wIndex - 1); - if (!(tmp & RH_PS_PRS)) - break; - wait_ms(1); - } - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PRS); - wait_ms(10); - - len = 0; - break; - - case RH_PORT_POWER: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PPS); - len = 0; - break; - - case RH_PORT_ENABLE: - isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, - RH_PS_PES); - len = 0; - break; - - default: - ERR("invalid wValue"); - stat = USB_ST_STALLED; - } - - isp116x_show_regs(isp116x); - - break; - - case RH_SET_ADDRESS: - DBG("RH_SET_ADDRESS"); - - rh_devnum = wValue; - len = 0; - break; - - case RH_GET_DESCRIPTOR: - DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength); - - switch (wValue) { - case (USB_DT_DEVICE << 8): /* device descriptor */ - len = min_t(unsigned int, - leni, min_t(unsigned int, - sizeof(root_hub_dev_des), - wLength)); - data_buf = root_hub_dev_des; - break; - - case (USB_DT_CONFIG << 8): /* configuration descriptor */ - len = min_t(unsigned int, - leni, min_t(unsigned int, - sizeof(root_hub_config_des), - wLength)); - data_buf = root_hub_config_des; - break; - - case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */ - len = min_t(unsigned int, - leni, min_t(unsigned int, - sizeof(root_hub_str_index0), - wLength)); - data_buf = root_hub_str_index0; - break; - - case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */ - len = min_t(unsigned int, - leni, min_t(unsigned int, - sizeof(root_hub_str_index1), - wLength)); - data_buf = root_hub_str_index1; - break; - - default: - ERR("invalid wValue"); - stat = USB_ST_STALLED; - } - - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - DBG("RH_GET_DESCRIPTOR | RH_CLASS"); - - tmp = isp116x_read_reg32(isp116x, HCRHDESCA); - - data_buf[0] = 0x09; /* min length; */ - data_buf[1] = 0x29; - data_buf[2] = tmp & RH_A_NDP; - data_buf[3] = 0; - if (tmp & RH_A_PSM) /* per-port power switching? */ - data_buf[3] |= 0x01; - if (tmp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf[3] |= 0x10; - else if (tmp & RH_A_OCPM) /* per-port overcurrent rep? */ - data_buf[3] |= 0x08; - - /* Corresponds to data_buf[4-7] */ - datab[1] = 0; - data_buf[5] = (tmp & RH_A_POTPGT) >> 24; - - tmp = isp116x_read_reg32(isp116x, HCRHDESCB); - - data_buf[7] = tmp & RH_B_DR; - if (data_buf[2] < 7) - data_buf[8] = 0xff; - else { - data_buf[0] += 2; - data_buf[8] = (tmp & RH_B_DR) >> 8; - data_buf[10] = data_buf[9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf[0], wLength)); - break; - - case RH_GET_CONFIGURATION: - DBG("RH_GET_CONFIGURATION"); - - *(__u8 *) data_buf = 0x01; - len = 1; - break; - - case RH_SET_CONFIGURATION: - DBG("RH_SET_CONFIGURATION"); - - isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC); - len = 0; - break; - - default: - ERR("*** *** *** unsupported root hub command *** *** ***"); - stat = USB_ST_STALLED; - } - - len = min_t(int, len, leni); - if (buffer != data_buf) - memcpy(buffer, data_buf, len); - - dev->act_len = len; - dev->status = stat; - DBG("dev act_len %d, status %d", dev->act_len, dev->status); - - dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)"); - - return stat; -} - -/* --- Transfer functions -------------------------------------------------- */ - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, int interval) -{ - DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d", - dev, pipe, buffer, len, interval); - - return -1; -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, struct devrequest *setup) -{ - int devnum = usb_pipedevice(pipe); - int epnum = usb_pipeendpoint(pipe); - int max = max_transfer_len(dev, pipe); - int dir_in = usb_pipein(pipe); - int done, ret; - - /* Control message is for the HUB? */ - if (devnum == rh_devnum) - return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup); - - /* Ok, no HUB message so send the message to the device */ - - /* Setup phase */ - DBG("--- SETUP PHASE --------------------------------"); - usb_settoggle(dev, epnum, 1, 0); - ret = isp116x_submit_job(dev, pipe, - PTD_DIR_SETUP, - setup, sizeof(struct devrequest)); - if (ret < 0) { - DBG("control setup phase error (ret = %d", ret); - return -1; - } - - /* Data phase */ - DBG("--- DATA PHASE ---------------------------------"); - done = 0; - usb_settoggle(dev, epnum, !dir_in, 1); - while (done < len) { - ret = isp116x_submit_job(dev, pipe, - dir_in ? PTD_DIR_IN : PTD_DIR_OUT, - (__u8 *) buffer + done, - max > len - done ? len - done : max); - if (ret < 0) { - DBG("control data phase error (ret = %d)", ret); - return -1; - } - done += ret; - - if (dir_in && ret < max) /* short packet */ - break; - } - - /* Status phase */ - DBG("--- STATUS PHASE -------------------------------"); - usb_settoggle(dev, epnum, !dir_in, 1); - ret = isp116x_submit_job(dev, pipe, - !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0); - if (ret < 0) { - DBG("control status phase error (ret = %d", ret); - return -1; - } - - dev->act_len = done; - - dump_msg(dev, pipe, buffer, len, "DEV(ret)"); - - return done; -} - -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len) -{ - int dir_out = usb_pipeout(pipe); - int max = max_transfer_len(dev, pipe); - int done, ret; - - DBG("--- BULK ---------------------------------------"); - DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d", - usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); - - done = 0; - while (done < len) { - ret = isp116x_submit_job(dev, pipe, - !dir_out ? PTD_DIR_IN : PTD_DIR_OUT, - (__u8 *) buffer + done, - max > len - done ? len - done : max); - if (ret < 0) { - DBG("error on bulk message (ret = %d)", ret); - return -1; - } - - done += ret; - - if (!dir_out && ret < max) /* short packet */ - break; - } - - dev->act_len = done; - - return 0; -} - -/* --- Basic functions ----------------------------------------------------- */ - -static int isp116x_sw_reset(struct isp116x *isp116x) -{ - int retries = 15; - int ret = 0; - - DBG(""); - - isp116x->disabled = 1; - - isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); - isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); - while (--retries) { - /* It usually resets within 1 ms */ - wait_ms(1); - if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) - break; - } - if (!retries) { - ERR("software reset timeout"); - ret = -1; - } - return ret; -} - -static int isp116x_reset(struct isp116x *isp116x) -{ - unsigned long t; - u16 clkrdy = 0; - int ret, timeout = 15 /* ms */ ; - - DBG(""); - - ret = isp116x_sw_reset(isp116x); - if (ret) - return ret; - - for (t = 0; t < timeout; t++) { - clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; - if (clkrdy) - break; - wait_ms(1); - } - if (!clkrdy) { - ERR("clock not ready after %dms", timeout); - /* After sw_reset the clock won't report to be ready, if - H_WAKEUP pin is high. */ - ERR("please make sure that the H_WAKEUP pin is pulled low!"); - ret = -1; - } - return ret; -} - -static void isp116x_stop(struct isp116x *isp116x) -{ - u32 val; - - DBG(""); - - isp116x_write_reg16(isp116x, HCuPINTENB, 0); - - /* Switch off ports' power, some devices don't come up - after next 'start' without this */ - val = isp116x_read_reg32(isp116x, HCRHDESCA); - val &= ~(RH_A_NPS | RH_A_PSM); - isp116x_write_reg32(isp116x, HCRHDESCA, val); - isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); - - isp116x_sw_reset(isp116x); -} - -/* - * Configure the chip. The chip must be successfully reset by now. - */ -static int isp116x_start(struct isp116x *isp116x) -{ - struct isp116x_platform_data *board = isp116x->board; - u32 val; - - DBG(""); - - /* Clear interrupt status and disable all interrupt sources */ - isp116x_write_reg16(isp116x, HCuPINT, 0xff); - isp116x_write_reg16(isp116x, HCuPINTENB, 0); - - isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); - isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); - - /* Hardware configuration */ - val = HCHWCFG_DBWIDTH(1); - if (board->sel15Kres) - val |= HCHWCFG_15KRSEL; - /* Remote wakeup won't work without working clock */ - if (board->remote_wakeup_enable) - val |= HCHWCFG_CLKNOTSTOP; - if (board->oc_enable) - val |= HCHWCFG_ANALOG_OC; - isp116x_write_reg16(isp116x, HCHWCFG, val); - - /* --- Root hub configuration */ - val = (25 << 24) & RH_A_POTPGT; - /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to - be always set. Yet, instead, we request individual port - power switching. */ - val |= RH_A_PSM; - /* Report overcurrent per port */ - val |= RH_A_OCPM; - isp116x_write_reg32(isp116x, HCRHDESCA, val); - isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); - - val = RH_B_PPCM; - isp116x_write_reg32(isp116x, HCRHDESCB, val); - isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); - - val = 0; - if (board->remote_wakeup_enable) - val |= RH_HS_DRWE; - isp116x_write_reg32(isp116x, HCRHSTATUS, val); - isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); - - isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); - - /* Go operational */ - val = HCCONTROL_USB_OPER; - if (board->remote_wakeup_enable) - val |= HCCONTROL_RWE; - isp116x_write_reg32(isp116x, HCCONTROL, val); - - /* Disable ports to avoid race in device enumeration */ - isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); - isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); - - isp116x_show_regs(isp116x); - - isp116x->disabled = 0; - - return 0; -} - -/* --- Init functions ------------------------------------------------------ */ - -int isp116x_check_id(struct isp116x *isp116x) -{ - int val; - - val = isp116x_read_reg16(isp116x, HCCHIPID); - if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { - ERR("invalid chip ID %04x", val); - return -1; - } - - return 0; -} - -int usb_lowlevel_init(void) -{ - struct isp116x *isp116x = &isp116x_dev; - - DBG(""); - - got_rhsc = rh_devnum = 0; - - /* Init device registers addr */ - isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR; - isp116x->data_reg = (u16 *) ISP116X_HCD_DATA; - - /* Setup specific board settings */ -#ifdef ISP116X_HCD_SEL15kRES - isp116x_board.sel15Kres = 1; -#endif -#ifdef ISP116X_HCD_OC_ENABLE - isp116x_board.oc_enable = 1; -#endif -#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE - isp116x_board.remote_wakeup_enable = 1; -#endif - isp116x->board = &isp116x_board; - - /* Try to get ISP116x silicon chip ID */ - if (isp116x_check_id(isp116x) < 0) - return -1; - - isp116x->disabled = 1; - isp116x->sleeping = 0; - - isp116x_reset(isp116x); - isp116x_start(isp116x); - - return 0; -} - -int usb_lowlevel_stop(void) -{ - struct isp116x *isp116x = &isp116x_dev; - - DBG(""); - - if (!isp116x->disabled) - isp116x_stop(isp116x); - - return 0; -} diff --git a/drivers/usb/isp116x.h b/drivers/usb/isp116x.h deleted file mode 100644 index a3ce3b5..0000000 --- a/drivers/usb/isp116x.h +++ /dev/null @@ -1,489 +0,0 @@ -/* - * ISP116x register declarations and HCD data structures - * - * Copyright (C) 2007 Rodolfo Giometti - * Copyright (C) 2007 Eurotech S.p.A. - * Copyright (C) 2005 Olav Kongas - * Portions: - * Copyright (C) 2004 Lothar Wassmann - * Copyright (C) 2004 Psion Teklogix - * Copyright (C) 2004 David Brownell - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#ifdef DEBUG -#define DBG(fmt, args...) \ - printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) -#else -#define DBG(fmt, args...) do {} while (0) -#endif - -#ifdef VERBOSE -# define VDBG DBG -#else -# define VDBG(fmt, args...) do {} while (0) -#endif - -#define ERR(fmt, args...) \ - printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) -#define WARN(fmt, args...) \ - printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) -#define INFO(fmt, args...) \ - printf("isp116x: " fmt "\n" , ## args) - -/* ------------------------------------------------------------------------- */ - -/* us of 1ms frame */ -#define MAX_LOAD_LIMIT 850 - -/* Full speed: max # of bytes to transfer for a single urb - at a time must be < 1024 && must be multiple of 64. - 832 allows transfering 4kiB within 5 frames. */ -#define MAX_TRANSFER_SIZE_FULLSPEED 832 - -/* Low speed: there is no reason to schedule in very big - chunks; often the requested long transfers are for - string descriptors containing short strings. */ -#define MAX_TRANSFER_SIZE_LOWSPEED 64 - -/* Bytetime (us), a rough indication of how much time it - would take to transfer a byte of useful data over USB */ -#define BYTE_TIME_FULLSPEED 1 -#define BYTE_TIME_LOWSPEED 20 - -/* Buffer sizes */ -#define ISP116x_BUF_SIZE 4096 -#define ISP116x_ITL_BUFSIZE 0 -#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE)) - -#define ISP116x_WRITE_OFFSET 0x80 - -/* --- ISP116x registers/bits ---------------------------------------------- */ - -#define HCREVISION 0x00 -#define HCCONTROL 0x01 -#define HCCONTROL_HCFS (3 << 6) /* host controller - functional state */ -#define HCCONTROL_USB_RESET (0 << 6) -#define HCCONTROL_USB_RESUME (1 << 6) -#define HCCONTROL_USB_OPER (2 << 6) -#define HCCONTROL_USB_SUSPEND (3 << 6) -#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */ -#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */ -#define HCCMDSTAT 0x02 -#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */ -#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */ -#define HCINTSTAT 0x03 -#define HCINT_SO (1 << 0) /* scheduling overrun */ -#define HCINT_WDH (1 << 1) /* writeback of done_head */ -#define HCINT_SF (1 << 2) /* start frame */ -#define HCINT_RD (1 << 3) /* resume detect */ -#define HCINT_UE (1 << 4) /* unrecoverable error */ -#define HCINT_FNO (1 << 5) /* frame number overflow */ -#define HCINT_RHSC (1 << 6) /* root hub status change */ -#define HCINT_OC (1 << 30) /* ownership change */ -#define HCINT_MIE (1 << 31) /* master interrupt enable */ -#define HCINTENB 0x04 -#define HCINTDIS 0x05 -#define HCFMINTVL 0x0d -#define HCFMREM 0x0e -#define HCFMNUM 0x0f -#define HCLSTHRESH 0x11 -#define HCRHDESCA 0x12 -#define RH_A_NDP (0x3 << 0) /* # downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* overcurrent protection - mode */ -#define RH_A_NOCP (1 << 12) /* no overcurrent protection */ -#define RH_A_POTPGT (0xff << 24) /* power on -> power good - time */ -#define HCRHDESCB 0x13 -#define RH_B_DR (0xffff << 0) /* device removable flags */ -#define RH_B_PPCM (0xffff << 16) /* port power control mask */ -#define HCRHSTATUS 0x14 -#define RH_HS_LPS (1 << 0) /* local power status */ -#define RH_HS_OCI (1 << 1) /* over current indicator */ -#define RH_HS_DRWE (1 << 15) /* device remote wakeup - enable */ -#define RH_HS_LPSC (1 << 16) /* local power status change */ -#define RH_HS_OCIC (1 << 17) /* over current indicator - change */ -#define RH_HS_CRWE (1 << 31) /* clear remote wakeup - enable */ -#define HCRHPORT1 0x15 -#define RH_PS_CCS (1 << 0) /* current connect status */ -#define RH_PS_PES (1 << 1) /* port enable status */ -#define RH_PS_PSS (1 << 2) /* port suspend status */ -#define RH_PS_POCI (1 << 3) /* port over current - indicator */ -#define RH_PS_PRS (1 << 4) /* port reset status */ -#define RH_PS_PPS (1 << 8) /* port power status */ -#define RH_PS_LSDA (1 << 9) /* low speed device attached */ -#define RH_PS_CSC (1 << 16) /* connect status change */ -#define RH_PS_PESC (1 << 17) /* port enable status change */ -#define RH_PS_PSSC (1 << 18) /* port suspend status - change */ -#define RH_PS_OCIC (1 << 19) /* over current indicator - change */ -#define RH_PS_PRSC (1 << 20) /* port reset status change */ -#define HCRHPORT_CLRMASK (0x1f << 16) -#define HCRHPORT2 0x16 -#define HCHWCFG 0x20 -#define HCHWCFG_15KRSEL (1 << 12) -#define HCHWCFG_CLKNOTSTOP (1 << 11) -#define HCHWCFG_ANALOG_OC (1 << 10) -#define HCHWCFG_DACK_MODE (1 << 8) -#define HCHWCFG_EOT_POL (1 << 7) -#define HCHWCFG_DACK_POL (1 << 6) -#define HCHWCFG_DREQ_POL (1 << 5) -#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) -#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) -#define HCHWCFG_INT_POL (1 << 2) -#define HCHWCFG_INT_TRIGGER (1 << 1) -#define HCHWCFG_INT_ENABLE (1 << 0) -#define HCDMACFG 0x21 -#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) -#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) -#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) -#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) -#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) -#define HCDMACFG_DMA_ENABLE (1 << 4) -#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) -#define HCDMACFG_CTR_SEL (1 << 2) -#define HCDMACFG_ITLATL_SEL (1 << 1) -#define HCDMACFG_DMA_RW_SELECT (1 << 0) -#define HCXFERCTR 0x22 -#define HCuPINT 0x24 -#define HCuPINT_SOF (1 << 0) -#define HCuPINT_ATL (1 << 1) -#define HCuPINT_AIIEOT (1 << 2) -#define HCuPINT_OPR (1 << 4) -#define HCuPINT_SUSP (1 << 5) -#define HCuPINT_CLKRDY (1 << 6) -#define HCuPINTENB 0x25 -#define HCCHIPID 0x27 -#define HCCHIPID_MASK 0xff00 -#define HCCHIPID_MAGIC 0x6100 -#define HCSCRATCH 0x28 -#define HCSWRES 0x29 -#define HCSWRES_MAGIC 0x00f6 -#define HCITLBUFLEN 0x2a -#define HCATLBUFLEN 0x2b -#define HCBUFSTAT 0x2c -#define HCBUFSTAT_ITL0_FULL (1 << 0) -#define HCBUFSTAT_ITL1_FULL (1 << 1) -#define HCBUFSTAT_ATL_FULL (1 << 2) -#define HCBUFSTAT_ITL0_DONE (1 << 3) -#define HCBUFSTAT_ITL1_DONE (1 << 4) -#define HCBUFSTAT_ATL_DONE (1 << 5) -#define HCRDITL0LEN 0x2d -#define HCRDITL1LEN 0x2e -#define HCITLPORT 0x40 -#define HCATLPORT 0x41 - -/* PTD accessor macros. */ -#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) -#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) -#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) -#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) -#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) -#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) -#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) -#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) -#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) -#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) -#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) -#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) -#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) -#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) -#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) -#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) -#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) -#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) -#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) -#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) -#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13) -#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK) -#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) -#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) -#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7) -#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK) - -/* Hardware transfer status codes -- CC from ptd->count */ -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 - /* 0x0A, 0x0B reserved for hardware */ -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D - /* 0x0E, 0x0F reserved for HCD */ -#define TD_NOTACCESSED 0x0F - -/* ------------------------------------------------------------------------- */ - -#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ -#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) - -/* Philips transfer descriptor */ -struct ptd { - u16 count; -#define PTD_COUNT_MSK (0x3ff << 0) -#define PTD_TOGGLE_MSK (1 << 10) -#define PTD_ACTIVE_MSK (1 << 11) -#define PTD_CC_MSK (0xf << 12) - u16 mps; -#define PTD_MPS_MSK (0x3ff << 0) -#define PTD_SPD_MSK (1 << 10) -#define PTD_LAST_MSK (1 << 11) -#define PTD_EP_MSK (0xf << 12) - u16 len; -#define PTD_LEN_MSK (0x3ff << 0) -#define PTD_DIR_MSK (3 << 10) -#define PTD_DIR_SETUP (0) -#define PTD_DIR_OUT (1) -#define PTD_DIR_IN (2) -#define PTD_B5_5_MSK (1 << 13) - u16 faddr; -#define PTD_FA_MSK (0x7f << 0) -#define PTD_FMT_MSK (1 << 7) -} __attribute__ ((packed, aligned(2))); - -struct isp116x_ep { - struct usb_device *udev; - struct ptd ptd; - - u8 maxpacket; - u8 epnum; - u8 nextpid; - - u16 length; /* of current packet */ - unsigned char *data; /* to databuf */ - - u16 error_count; -}; - -/* URB struct */ -#define N_URB_TD 48 -#define URB_DEL 1 -typedef struct { - struct isp116x_ep *ed; - void *transfer_buffer; /* (in) associated data buffer */ - int actual_length; /* (return) actual transfer length */ - unsigned long pipe; /* (in) pipe information */ -#if 0 - int state; -#endif -} urb_priv_t; - -struct isp116x_platform_data { - /* Enable internal resistors on downstream ports */ - unsigned sel15Kres:1; - /* On-chip overcurrent detection */ - unsigned oc_enable:1; - /* Enable wakeup by devices on usb bus (e.g. wakeup - by attachment/detachment or by device activity - such as moving a mouse). When chosen, this option - prevents stopping internal clock, increasing - thereby power consumption in suspended state. */ - unsigned remote_wakeup_enable:1; -}; - -struct isp116x { - u16 *addr_reg; - u16 *data_reg; - - struct isp116x_platform_data *board; - - struct dentry *dentry; - unsigned long stat1, stat2, stat4, stat8, stat16; - - /* Status flags */ - unsigned disabled:1; - unsigned sleeping:1; - - /* Root hub registers */ - u32 rhdesca; - u32 rhdescb; - u32 rhstatus; - u32 rhport[2]; - - /* Schedule for the current frame */ - struct isp116x_ep *atl_active; - int atl_buflen; - int atl_bufshrt; - int atl_last_dir; - int atl_finishing; -}; - -/* ------------------------------------------------- */ - -/* Inter-io delay (ns). The chip is picky about access timings; it - * expects at least: - * 150ns delay between consecutive accesses to DATA_REG, - * 300ns delay between access to ADDR_REG and DATA_REG - * OE, WE MUST NOT be changed during these intervals - */ -#if defined(UDELAY) -#define isp116x_delay(h,d) udelay(d) -#else -#define isp116x_delay(h,d) do {} while (0) -#endif - -static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) -{ - writew(reg & 0xff, isp116x->addr_reg); - isp116x_delay(isp116x, UDELAY); -} - -static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val) -{ - writew(val, isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); -} - -static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val) -{ - __raw_writew(val, isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); -} - -static inline u16 isp116x_read_data16(struct isp116x *isp116x) -{ - u16 val; - - val = readw(isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); - return val; -} - -static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x) -{ - u16 val; - - val = __raw_readw(isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); - return val; -} - -static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val) -{ - writew(val & 0xffff, isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); - writew(val >> 16, isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); -} - -static inline u32 isp116x_read_data32(struct isp116x *isp116x) -{ - u32 val; - - val = (u32) readw(isp116x->data_reg); - isp116x_delay(isp116x, UDELAY); - val |= ((u32) readw(isp116x->data_reg)) << 16; - isp116x_delay(isp116x, UDELAY); - return val; -} - -/* Let's keep register access functions out of line. Hint: - we wait at least 150 ns at every access. -*/ -static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg) -{ - isp116x_write_addr(isp116x, reg); - return isp116x_read_data16(isp116x); -} - -static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg) -{ - isp116x_write_addr(isp116x, reg); - return isp116x_read_data32(isp116x); -} - -static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg, - unsigned val) -{ - isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); - isp116x_write_data16(isp116x, (u16) (val & 0xffff)); -} - -static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, - unsigned val) -{ - isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); - isp116x_write_data32(isp116x, (u32) val); -} - -/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile new file mode 100644 index 0000000..09e0a5f --- /dev/null +++ b/drivers/usb/musb/Makefile @@ -0,0 +1,47 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB := $(obj)libusb_musb.a + +COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o +COBJS-$(CONFIG_USB_DAVINCI) += davinci.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c new file mode 100644 index 0000000..a7648fc --- /dev/null +++ b/drivers/usb/musb/davinci.c @@ -0,0 +1,106 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * 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 + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include +#include +#include "davinci.h" + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + (struct musb_regs *)MENTOR_USB0_BASE, + DAVINCI_USB_TIMEOUT, + 0 +}; + +/* MUSB module register overlay */ +struct davinci_usb_regs *dregs; + +/* + * Enable the USB phy + */ +static u8 phy_on(void) +{ + u32 timeout; + + /* Wait until the USB phy is turned on */ + writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); + timeout = musb_cfg.timeout; + while (timeout--) + if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD) + return 1; + + /* USB phy was not turned on */ + return 0; +} + +/* + * Disable the USB phy + */ +static void phy_off(void) +{ + /* powerdown the on-chip PHY and its oscillator */ + writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR); +} + +/* + * This function performs Davinci platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + + /* enable USB VBUS */ + enable_vbus(); + + /* start the on-chip USB phy and its pll */ + if (!phy_on()) + return -1; + + /* reset the controller */ + dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE; + writel(1, &dregs->ctrlr); + udelay(5000); + + /* Returns zero if e.g. not clocked */ + revision = readl(&dregs->version); + if (!revision) + return -1; + + /* Disable all interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK | + DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr); + return 0; +} + +/* + * This function performs Davinci platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn of the phy */ + phy_off(); + + /* flush any interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK | + DAVINCI_USB_RXINT_MASK , &dregs->intclrr); +} diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h new file mode 100644 index 0000000..f6751bf --- /dev/null +++ b/drivers/usb/musb/davinci.h @@ -0,0 +1,86 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * 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 + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __DAVINCI_USB_H__ +#define __DAVINCI_USB_H__ + +#include +#include "musb_core.h" + +/* Base address of DAVINCI usb0 wrapper */ +#define DAVINCI_USB0_BASE 0x01C64000 + +/* Base address of DAVINCI musb core */ +#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400) + +/* + * Davinci platform USB wrapper register overlay. Note: Only the required + * registers are included in this structure. It can be expanded as required. + */ +struct davinci_usb_regs { + u32 version; + u32 ctrlr; + u32 reserved[0x20]; + u32 intclrr; + u32 intmskr; + u32 intmsksetr; +}; + +#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ +#define DAVINCI_USB_USBINT_SHIFT 16 +#define DAVINCI_USB_TXINT_SHIFT 0 +#define DAVINCI_USB_RXINT_SHIFT 8 +#define DAVINCI_INTR_DRVVBUS 0x0100 + +#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DAVINCI_USB_TXINT_MASK \ + (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) +#define DAVINCI_USB_RXINT_MASK \ + (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +/* Integrated highspeed/otg PHY */ +#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) +#define USBPHY_PHYCLKGD (1 << 8) +#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ +#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ +#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ +#define USBPHY_CLKO1SEL (1 << 3) +#define USBPHY_OSCPDWN (1 << 2) +#define USBPHY_PHYPDWN (1 << 0) + +/* Timeout for Davinci USB module */ +#define DAVINCI_USB_TIMEOUT 0x3FFFFFF + +/* IO Expander I2C address and VBUS enable mask */ +#define IOEXP_I2C_ADDR 0x3A +#define IOEXP_VBUSEN_MASK 1 + +/* extern functions */ +extern void lpsc_on(unsigned int id); +extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern void enable_vbus(void); +#endif /* __DAVINCI_USB_H__ */ diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c new file mode 100644 index 0000000..ec57fc8 --- /dev/null +++ b/drivers/usb/musb/musb_core.c @@ -0,0 +1,141 @@ +/* + * Mentor USB OTG Core functionality common for both Host and Device + * functionality. + * + * Copyright (c) 2008 Texas Instruments + * + * 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 + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include + +#include "musb_core.h" +struct musb_regs *musbr; + +/* + * program the mentor core to start (enable interrupts, dma, etc.) + */ +void musb_start(void) +{ + u8 devctl; + + /* disable all interrupts */ + writew(0, &musbr->intrtxe); + writew(0, &musbr->intrrxe); + writeb(0, &musbr->intrusbe); + writeb(0, &musbr->testmode); + + /* put into basic highspeed mode and start session */ + writeb(MUSB_POWER_HSENAB, &musbr->power); +#if defined(CONFIG_MUSB_HCD) + devctl = readb(&musbr->devctl); + writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl); +#endif +} + +/* + * This function configures the endpoint configuration. The musb hcd or musb + * device implementation can use this function to configure the endpoints + * and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints + * should not be more than the available FIFO size. + * + * epinfo - Pointer to EP configuration table + * cnt - Number of entries in the EP conf table. + */ +void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) +{ + u16 csr; + u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */ + u32 fifosize; + u8 idx; + + while (cnt--) { + /* prepare fifosize to write to register */ + fifosize = epinfo->epsize >> 3; + idx = ffs(fifosize) - 1; + + writeb(epinfo->epnum, &musbr->index); + if (epinfo->epdir) { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->txfifosz); + writew(fifoaddr >> 3, &musbr->txfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_TXCSR_TXPKTRDY) + writew(csr | MUSB_TXCSR_FLUSHFIFO, + &musbr->txcsr); + } else { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->rxfifosz); + writew(fifoaddr >> 3, &musbr->rxfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->rxcsr); + writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_RXCSR_RXPKTRDY) + writew(csr | MUSB_RXCSR_FLUSHFIFO, + &musbr->rxcsr); + } + fifoaddr += epinfo->epsize; + epinfo++; + } +} + +/* + * This function writes data to endpoint fifo + * + * ep - endpoint number + * length - number of bytes to write to FIFO + * fifo_data - Pointer to data buffer that contains the data to write + */ +void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* write the data to the fifo */ + while (length--) + writeb(*data++, &musbr->fifox[ep]); +} + +/* + * This function reads data from endpoint fifo + * + * ep - endpoint number + * length - number of bytes to read from FIFO + * fifo_data - pointer to data buffer into which data is read + */ +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* read the data to the fifo */ + while (length--) + *data++ = readb(&musbr->fifox[ep]); +} diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h new file mode 100644 index 0000000..2597c5f --- /dev/null +++ b/drivers/usb/musb/musb_core.h @@ -0,0 +1,316 @@ +/****************************************************************** + * Copyright 2008 Mentor Graphics Corporation + * Copyright (C) 2008 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION + * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE + * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS + * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. + * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT + * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR + * GRAPHICS SUPPORT CUSTOMER. + ******************************************************************/ + +#ifndef __MUSB_HDRC_DEFS_H__ +#define __MUSB_HDRC_DEFS_H__ + +#include +#include +#include + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* Mentor USB core register overlay structure */ +struct musb_regs { + /* common registers */ + u8 faddr; + u8 power; + u16 intrtx; + u16 intrrx; + u16 intrtxe; + u16 intrrxe; + u8 intrusb; + u8 intrusbe; + u16 frame; + u8 index; + u8 testmode; + /* indexed registers */ + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; + /* fifo */ + u32 fifox[16]; + /* OTG, dynamic FIFO, version & vendor registers */ + u8 devctl; + u8 reserved1; + u8 txfifosz; + u8 rxfifosz; + u16 txfifoadd; + u16 rxfifoadd; + u32 vcontrol; + u16 hwvers; + u16 reserved2[5]; + u8 epinfo; + u8 raminfo; + u8 linkinfo; + u8 vplen; + u8 hseof1; + u8 fseof1; + u8 lseof1; + u8 reserved3; + /* target address registers */ + struct musb_tar_regs { + u8 txfuncaddr; + u8 reserved0; + u8 txhubaddr; + u8 txhubport; + u8 rxfuncaddr; + u8 reserved1; + u8 rxhubaddr; + u8 rxhubport; + } tar[16]; +} __attribute((aligned(32))); + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 +#define MUSB_POWER_HSMODE_SHIFT 4 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_SPEED_HIGH 1 +#define MUSB_TYPE_SPEED_FULL 2 +#define MUSB_TYPE_SPEED_LOW 3 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_BULK 2 +#define MUSB_TYPE_PROTO_INTR 3 + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_MODE 0x2000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 +#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 +#define MUSB_S_RXCSR_H_DATATOGGLE 9 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + +/* Endpoint configuration information. Note: The value of endpoint fifo size + * element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other + * values are not supported + */ +struct musb_epinfo { + u8 epnum; /* endpoint number */ + u8 epdir; /* endpoint direction */ + u16 epsize; /* endpoint FIFO size */ +}; + +/* + * Platform specific MUSB configuration. Any platform using the musb + * functionality should create one instance of this structure in the + * platform specific file. + */ +struct musb_config { + struct musb_regs *regs; + u32 timeout; + u8 musb_speed; +}; + +/* externally defined data */ +extern struct musb_config musb_cfg; +extern struct musb_regs *musbr; + +/* exported functions */ +extern void musb_start(void); +extern void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt); +extern void write_fifo(u8 ep, u32 length, void *fifo_data); +extern void read_fifo(u8 ep, u32 length, void *fifo_data); + +/* extern functions */ +extern inline void musb_writew(u32 offset, u16 value); +extern inline void musb_writeb(u32 offset, u8 value); +extern inline u16 musb_readw(u32 offset); +extern inline u8 musb_readb(u32 offset); + +#endif /* __MUSB_HDRC_DEFS_H__ */ diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c new file mode 100644 index 0000000..352a0d4 --- /dev/null +++ b/drivers/usb/musb/musb_hcd.c @@ -0,0 +1,792 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * 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 + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include +#include "musb_hcd.h" + +/* MSC control transfers */ +#define USB_MSC_BBB_RESET 0xFF +#define USB_MSC_BBB_GET_MAX_LUN 0xFE + +/* Endpoint configuration information */ +static struct musb_epinfo epinfo[3] = { + {MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ + {MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In - 512 Bytes */ + {MUSB_INTR_EP, 0, 64} /* EP2 - Interrupt IN - 64 Bytes */ +}; + +/* + * This function writes the data toggle value. + */ +static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) +{ + u16 toggle = usb_gettoggle(dev, ep, dir_out); + u16 csr; + + if (dir_out) { + if (!toggle) + writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); + else { + csr = readw(&musbr->txcsr); + csr |= MUSB_TXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->txcsr); + csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); + writew(csr, &musbr->txcsr); + } + } else { + if (!toggle) + writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); + else { + csr = readw(&musbr->rxcsr); + csr |= MUSB_RXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->rxcsr); + csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); + writew(csr, &musbr->rxcsr); + } + } +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall + * has occured, the RxStall is cleared and 1 is returned. If RxStall has + * not occured, 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ + u16 csr; + + /* For endpoint 0 */ + if (!ep) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_RXSTALL) { + csr &= ~MUSB_CSR0_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* For non-ep0 */ + if (dir_out) { /* is it tx ep */ + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_RXSTALL) { + csr &= ~MUSB_TXCSR_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* is it rx ep */ + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_RXSTALL) { + csr &= ~MUSB_RXCSR_H_RXSTALL; + writew(csr, &musbr->rxcsr); + return 1; + } + } + } + return 0; +} + +/* + * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout + * error and -2 for stall. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u16 csr; + int result = 1; + + while (result > 0) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_ERROR) { + csr &= ~MUSB_CSR0_H_ERROR; + writew(csr, &musbr->txcsr); + dev->status = USB_ST_CRC_ERR; + result = -1; + break; + } + + switch (bit_mask) { + case MUSB_CSR0_TXPKTRDY: + if (!(csr & MUSB_CSR0_TXPKTRDY)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + + case MUSB_CSR0_RXPKTRDY: + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + if (csr & MUSB_CSR0_RXPKTRDY) + result = 0; + break; + + case MUSB_CSR0_H_REQPKT: + if (!(csr & MUSB_CSR0_H_REQPKT)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + } + } + return result; +} + +/* + * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_txep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 1)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (csr & MUSB_TXCSR_TXPKTRDY); + return 1; +} + +/* + * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_rxep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 0)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (!(csr & MUSB_RXCSR_RXPKTRDY)); + return 1; +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ + int result; + u16 csr; + + /* write the control request to ep0 fifo */ + write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); + + /* enable transfer of setup packet */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); + writew(csr, &musbr->txcsr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + dev->act_len = 0; + return result; +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 rxlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *rxbuff = (u8 *)buffer; + u8 rxedlength; + int result; + + while (rxlen < len) { + /* Determine the next read length */ + nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); + + /* Set the ReqPkt bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); + if (result < 0) + return result; + + /* Actual number of bytes received by usb */ + rxedlength = readb(&musbr->rxcount); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = readw(&musbr->txcsr); + csr &= ~MUSB_CSR0_RXPKTRDY; + writew(csr, &musbr->txcsr); + + /* short packet? */ + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + rxlen += nextlen; + dev->act_len = rxlen; + } + return 0; +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *txbuff = (u8 *)buffer; + int result = 0; + + while (txlen < len) { + /* Determine the next write length */ + nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); + + /* Load the data to send in FIFO */ + write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + + /* Set TXPKTRDY bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, + &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + if (result < 0) + break; + + txlen += nextlen; + dev->act_len = txlen; + } + return result; +} + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | + MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + return result; +} + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit and ReqPkt bit */ + csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; + writew(csr, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = readw(&musbr->txcsr); + csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + return result; +} + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + return (dev->speed & USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : + ((dev->speed & USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : + MUSB_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while (dev->parent->parent != NULL) + if (get_dev_speed(dev->parent) != MUSB_TYPE_SPEED_HIGH) + dev = dev->parent; + else + break; + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + + /* configure the hub address and the port address */ + writeb(hub, &musbr->tar[ep].txhubaddr); + writeb((chid + 1), &musbr->tar[ep].txhubport); + writeb(hub, &musbr->tar[ep].rxhubaddr); + writeb((chid + 1), &musbr->tar[ep].rxhubport); +} + +/* + * do a control transfer + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + u16 csr; + u8 devspeed; + + /* select control endpoint */ + writeb(MUSB_CONTROL_EP, &musbr->index); + csr = readw(&musbr->txcsr); + + /* target addr and (for multipoint) hub addr/port */ + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + config_hub_port(dev, MUSB_CONTROL_EP); + writeb(devspeed << 6, &musbr->txtype); + } else { + writeb(musb_cfg.musb_speed << 6, &musbr->txtype); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return 0; + + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_MSC_BBB_GET_MAX_LUN: + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_CLEAR_FEATURE: + case USB_MSC_BBB_RESET: + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_DESCRIPTOR: + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + default: + /* unhandled control transfer */ + return -1; + } + + dev->status = 0; + dev->act_len = len; + return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select bulk endpoint */ + writeb(MUSB_BULK_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_BULK_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (dir_out) { /* bulk-out transfer */ + /* Program the TxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->txtype); + + /* Write maximum packet size to the TxMaxp register */ + writew(dev->epmaxpacketout[ep], &musbr->txmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? + (len-txlen) : dev->epmaxpacketout[ep]; + + /* Write the data to the FIFO */ + write_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Set the TxPktRdy bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); + + /* Wait until the TxPktRdy bit is cleared */ + if (!wait_until_txep_ready(dev, MUSB_BULK_EP)) { + readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + dev->act_len = txlen; + return 0; + } + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + } else { /* bulk-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_BULK_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* bulk transfer is complete */ + dev->status = 0; + dev->act_len = len; + return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(void) +{ + u8 power; + u32 timeout; + + if (musb_platform_init() == -1) + return -1; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + musb_configure_ep(&epinfo[0], + sizeof(epinfo) / sizeof(struct musb_epinfo)); + musb_start(); + + /* + * Wait until musb is enabled in host mode with a timeout. There + * should be a usb device connected. + */ + timeout = musb_cfg.timeout; + while (timeout--) + if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) + break; + + /* if musb core is not in host mode, then return */ + if (!timeout) + return -1; + + /* start usb bus reset */ + power = readb(&musbr->power); + writeb(power | MUSB_POWER_RESET, &musbr->power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_RESET; + writeb(power, &musbr->power); + + /* Determine if the connected device is a high/full/low speed device */ + musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? + MUSB_TYPE_SPEED_HIGH : + ((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? + MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); + return 0; +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ + /* Reset the USB module */ + musb_platform_deinit(); + writeb(0, &musbr->devctl); + return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt + * transfers are not supported. + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select interrupt endpoint */ + writeb(MUSB_INTR_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_INTR_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (!dir_out) { /* intrrupt-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + writeb(interval, &musbr->rxinterval); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_INTR_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_INTR_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* interrupt transfer is complete */ + dev->irq_status = 0; + dev->irq_act_len = len; + dev->irq_handle(dev); + dev->status = 0; + dev->act_len = len; + return 0; +} + + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ + device_t *dev; + struct usb_device *usb_kbd_dev; + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = device_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], + maxp > 8 ? 8 : maxp, ep->bInterval); +} +#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/musb/musb_hcd.h b/drivers/usb/musb/musb_hcd.h new file mode 100644 index 0000000..bb83311 --- /dev/null +++ b/drivers/usb/musb/musb_hcd.h @@ -0,0 +1,51 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * 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 + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __MUSB_HCD_H__ +#define __MUSB_HCD_H__ + +#include "musb_core.h" +#ifdef CONFIG_USB_KEYBOARD +#include +extern unsigned char new[]; +#endif + +/* This defines the endpoint number used for control transfers */ +#define MUSB_CONTROL_EP 0 + +/* This defines the endpoint number used for bulk transfer */ +#define MUSB_BULK_EP 1 + +/* This defines the endpoint number used for interrupt transfer */ +#define MUSB_INTR_EP 2 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ + >> MUSB_POWER_HSMODE_SHIFT) + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); + +#endif /* __MUSB_HCD_H__ */ diff --git a/drivers/usb/musb_core.c b/drivers/usb/musb_core.c deleted file mode 100644 index ec57fc8..0000000 --- a/drivers/usb/musb_core.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Mentor USB OTG Core functionality common for both Host and Device - * functionality. - * - * Copyright (c) 2008 Texas Instruments - * - * 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 - * - * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments - */ - -#include - -#include "musb_core.h" -struct musb_regs *musbr; - -/* - * program the mentor core to start (enable interrupts, dma, etc.) - */ -void musb_start(void) -{ - u8 devctl; - - /* disable all interrupts */ - writew(0, &musbr->intrtxe); - writew(0, &musbr->intrrxe); - writeb(0, &musbr->intrusbe); - writeb(0, &musbr->testmode); - - /* put into basic highspeed mode and start session */ - writeb(MUSB_POWER_HSENAB, &musbr->power); -#if defined(CONFIG_MUSB_HCD) - devctl = readb(&musbr->devctl); - writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl); -#endif -} - -/* - * This function configures the endpoint configuration. The musb hcd or musb - * device implementation can use this function to configure the endpoints - * and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints - * should not be more than the available FIFO size. - * - * epinfo - Pointer to EP configuration table - * cnt - Number of entries in the EP conf table. - */ -void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) -{ - u16 csr; - u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */ - u32 fifosize; - u8 idx; - - while (cnt--) { - /* prepare fifosize to write to register */ - fifosize = epinfo->epsize >> 3; - idx = ffs(fifosize) - 1; - - writeb(epinfo->epnum, &musbr->index); - if (epinfo->epdir) { - /* Configure fifo size and fifo base address */ - writeb(idx, &musbr->txfifosz); - writew(fifoaddr >> 3, &musbr->txfifoadd); -#if defined(CONFIG_MUSB_HCD) - /* clear the data toggle bit */ - csr = readw(&musbr->txcsr); - writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); -#endif - /* Flush fifo if required */ - if (csr & MUSB_TXCSR_TXPKTRDY) - writew(csr | MUSB_TXCSR_FLUSHFIFO, - &musbr->txcsr); - } else { - /* Configure fifo size and fifo base address */ - writeb(idx, &musbr->rxfifosz); - writew(fifoaddr >> 3, &musbr->rxfifoadd); -#if defined(CONFIG_MUSB_HCD) - /* clear the data toggle bit */ - csr = readw(&musbr->rxcsr); - writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); -#endif - /* Flush fifo if required */ - if (csr & MUSB_RXCSR_RXPKTRDY) - writew(csr | MUSB_RXCSR_FLUSHFIFO, - &musbr->rxcsr); - } - fifoaddr += epinfo->epsize; - epinfo++; - } -} - -/* - * This function writes data to endpoint fifo - * - * ep - endpoint number - * length - number of bytes to write to FIFO - * fifo_data - Pointer to data buffer that contains the data to write - */ -void write_fifo(u8 ep, u32 length, void *fifo_data) -{ - u8 *data = (u8 *)fifo_data; - - /* select the endpoint index */ - writeb(ep, &musbr->index); - - /* write the data to the fifo */ - while (length--) - writeb(*data++, &musbr->fifox[ep]); -} - -/* - * This function reads data from endpoint fifo - * - * ep - endpoint number - * length - number of bytes to read from FIFO - * fifo_data - pointer to data buffer into which data is read - */ -void read_fifo(u8 ep, u32 length, void *fifo_data) -{ - u8 *data = (u8 *)fifo_data; - - /* select the endpoint index */ - writeb(ep, &musbr->index); - - /* read the data to the fifo */ - while (length--) - *data++ = readb(&musbr->fifox[ep]); -} diff --git a/drivers/usb/musb_core.h b/drivers/usb/musb_core.h deleted file mode 100644 index 2597c5f..0000000 --- a/drivers/usb/musb_core.h +++ /dev/null @@ -1,316 +0,0 @@ -/****************************************************************** - * Copyright 2008 Mentor Graphics Corporation - * Copyright (C) 2008 by Texas Instruments - * - * This file is part of the Inventra Controller Driver for Linux. - * - * The Inventra Controller Driver for Linux is free software; you - * can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 as published by the Free Software - * Foundation. - * - * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, - * write to the Free Software Foundation, Inc., 59 Temple Place, - * Suite 330, Boston, MA 02111-1307 USA - * - * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION - * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE - * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS - * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. - * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT - * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR - * GRAPHICS SUPPORT CUSTOMER. - ******************************************************************/ - -#ifndef __MUSB_HDRC_DEFS_H__ -#define __MUSB_HDRC_DEFS_H__ - -#include -#include -#include - -#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ - -/* Mentor USB core register overlay structure */ -struct musb_regs { - /* common registers */ - u8 faddr; - u8 power; - u16 intrtx; - u16 intrrx; - u16 intrtxe; - u16 intrrxe; - u8 intrusb; - u8 intrusbe; - u16 frame; - u8 index; - u8 testmode; - /* indexed registers */ - u16 txmaxp; - u16 txcsr; - u16 rxmaxp; - u16 rxcsr; - u16 rxcount; - u8 txtype; - u8 txinterval; - u8 rxtype; - u8 rxinterval; - u8 reserved0; - u8 fifosize; - /* fifo */ - u32 fifox[16]; - /* OTG, dynamic FIFO, version & vendor registers */ - u8 devctl; - u8 reserved1; - u8 txfifosz; - u8 rxfifosz; - u16 txfifoadd; - u16 rxfifoadd; - u32 vcontrol; - u16 hwvers; - u16 reserved2[5]; - u8 epinfo; - u8 raminfo; - u8 linkinfo; - u8 vplen; - u8 hseof1; - u8 fseof1; - u8 lseof1; - u8 reserved3; - /* target address registers */ - struct musb_tar_regs { - u8 txfuncaddr; - u8 reserved0; - u8 txhubaddr; - u8 txhubport; - u8 rxfuncaddr; - u8 reserved1; - u8 rxhubaddr; - u8 rxhubport; - } tar[16]; -} __attribute((aligned(32))); - -/* - * MUSB Register bits - */ - -/* POWER */ -#define MUSB_POWER_ISOUPDATE 0x80 -#define MUSB_POWER_SOFTCONN 0x40 -#define MUSB_POWER_HSENAB 0x20 -#define MUSB_POWER_HSMODE 0x10 -#define MUSB_POWER_RESET 0x08 -#define MUSB_POWER_RESUME 0x04 -#define MUSB_POWER_SUSPENDM 0x02 -#define MUSB_POWER_ENSUSPEND 0x01 -#define MUSB_POWER_HSMODE_SHIFT 4 - -/* INTRUSB */ -#define MUSB_INTR_SUSPEND 0x01 -#define MUSB_INTR_RESUME 0x02 -#define MUSB_INTR_RESET 0x04 -#define MUSB_INTR_BABBLE 0x04 -#define MUSB_INTR_SOF 0x08 -#define MUSB_INTR_CONNECT 0x10 -#define MUSB_INTR_DISCONNECT 0x20 -#define MUSB_INTR_SESSREQ 0x40 -#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ - -/* DEVCTL */ -#define MUSB_DEVCTL_BDEVICE 0x80 -#define MUSB_DEVCTL_FSDEV 0x40 -#define MUSB_DEVCTL_LSDEV 0x20 -#define MUSB_DEVCTL_VBUS 0x18 -#define MUSB_DEVCTL_VBUS_SHIFT 3 -#define MUSB_DEVCTL_HM 0x04 -#define MUSB_DEVCTL_HR 0x02 -#define MUSB_DEVCTL_SESSION 0x01 - -/* TESTMODE */ -#define MUSB_TEST_FORCE_HOST 0x80 -#define MUSB_TEST_FIFO_ACCESS 0x40 -#define MUSB_TEST_FORCE_FS 0x20 -#define MUSB_TEST_FORCE_HS 0x10 -#define MUSB_TEST_PACKET 0x08 -#define MUSB_TEST_K 0x04 -#define MUSB_TEST_J 0x02 -#define MUSB_TEST_SE0_NAK 0x01 - -/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ -#define MUSB_FIFOSZ_DPB 0x10 -/* Allocation size (8, 16, 32, ... 4096) */ -#define MUSB_FIFOSZ_SIZE 0x0f - -/* CSR0 */ -#define MUSB_CSR0_FLUSHFIFO 0x0100 -#define MUSB_CSR0_TXPKTRDY 0x0002 -#define MUSB_CSR0_RXPKTRDY 0x0001 - -/* CSR0 in Peripheral mode */ -#define MUSB_CSR0_P_SVDSETUPEND 0x0080 -#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 -#define MUSB_CSR0_P_SENDSTALL 0x0020 -#define MUSB_CSR0_P_SETUPEND 0x0010 -#define MUSB_CSR0_P_DATAEND 0x0008 -#define MUSB_CSR0_P_SENTSTALL 0x0004 - -/* CSR0 in Host mode */ -#define MUSB_CSR0_H_DIS_PING 0x0800 -#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ -#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ -#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 -#define MUSB_CSR0_H_STATUSPKT 0x0040 -#define MUSB_CSR0_H_REQPKT 0x0020 -#define MUSB_CSR0_H_ERROR 0x0010 -#define MUSB_CSR0_H_SETUPPKT 0x0008 -#define MUSB_CSR0_H_RXSTALL 0x0004 - -/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ -#define MUSB_CSR0_P_WZC_BITS \ - (MUSB_CSR0_P_SENTSTALL) -#define MUSB_CSR0_H_WZC_BITS \ - (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ - | MUSB_CSR0_RXPKTRDY) - -/* TxType/RxType */ -#define MUSB_TYPE_SPEED 0xc0 -#define MUSB_TYPE_SPEED_SHIFT 6 -#define MUSB_TYPE_SPEED_HIGH 1 -#define MUSB_TYPE_SPEED_FULL 2 -#define MUSB_TYPE_SPEED_LOW 3 -#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ -#define MUSB_TYPE_PROTO_SHIFT 4 -#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ -#define MUSB_TYPE_PROTO_BULK 2 -#define MUSB_TYPE_PROTO_INTR 3 - -/* CONFIGDATA */ -#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ -#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ -#define MUSB_CONFIGDATA_BIGENDIAN 0x20 -#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ -#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ -#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ -#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ -#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ - -/* TXCSR in Peripheral and Host mode */ -#define MUSB_TXCSR_AUTOSET 0x8000 -#define MUSB_TXCSR_MODE 0x2000 -#define MUSB_TXCSR_DMAENAB 0x1000 -#define MUSB_TXCSR_FRCDATATOG 0x0800 -#define MUSB_TXCSR_DMAMODE 0x0400 -#define MUSB_TXCSR_CLRDATATOG 0x0040 -#define MUSB_TXCSR_FLUSHFIFO 0x0008 -#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 -#define MUSB_TXCSR_TXPKTRDY 0x0001 - -/* TXCSR in Peripheral mode */ -#define MUSB_TXCSR_P_ISO 0x4000 -#define MUSB_TXCSR_P_INCOMPTX 0x0080 -#define MUSB_TXCSR_P_SENTSTALL 0x0020 -#define MUSB_TXCSR_P_SENDSTALL 0x0010 -#define MUSB_TXCSR_P_UNDERRUN 0x0004 - -/* TXCSR in Host mode */ -#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 -#define MUSB_TXCSR_H_DATATOGGLE 0x0100 -#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 -#define MUSB_TXCSR_H_RXSTALL 0x0020 -#define MUSB_TXCSR_H_ERROR 0x0004 -#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8 - -/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ -#define MUSB_TXCSR_P_WZC_BITS \ - (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ - | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) -#define MUSB_TXCSR_H_WZC_BITS \ - (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ - | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) - -/* RXCSR in Peripheral and Host mode */ -#define MUSB_RXCSR_AUTOCLEAR 0x8000 -#define MUSB_RXCSR_DMAENAB 0x2000 -#define MUSB_RXCSR_DISNYET 0x1000 -#define MUSB_RXCSR_PID_ERR 0x1000 -#define MUSB_RXCSR_DMAMODE 0x0800 -#define MUSB_RXCSR_INCOMPRX 0x0100 -#define MUSB_RXCSR_CLRDATATOG 0x0080 -#define MUSB_RXCSR_FLUSHFIFO 0x0010 -#define MUSB_RXCSR_DATAERROR 0x0008 -#define MUSB_RXCSR_FIFOFULL 0x0002 -#define MUSB_RXCSR_RXPKTRDY 0x0001 - -/* RXCSR in Peripheral mode */ -#define MUSB_RXCSR_P_ISO 0x4000 -#define MUSB_RXCSR_P_SENTSTALL 0x0040 -#define MUSB_RXCSR_P_SENDSTALL 0x0020 -#define MUSB_RXCSR_P_OVERRUN 0x0004 - -/* RXCSR in Host mode */ -#define MUSB_RXCSR_H_AUTOREQ 0x4000 -#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 -#define MUSB_RXCSR_H_DATATOGGLE 0x0200 -#define MUSB_RXCSR_H_RXSTALL 0x0040 -#define MUSB_RXCSR_H_REQPKT 0x0020 -#define MUSB_RXCSR_H_ERROR 0x0004 -#define MUSB_S_RXCSR_H_DATATOGGLE 9 - -/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ -#define MUSB_RXCSR_P_WZC_BITS \ - (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ - | MUSB_RXCSR_RXPKTRDY) -#define MUSB_RXCSR_H_WZC_BITS \ - (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ - | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) - -/* HUBADDR */ -#define MUSB_HUBADDR_MULTI_TT 0x80 - -/* Endpoint configuration information. Note: The value of endpoint fifo size - * element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other - * values are not supported - */ -struct musb_epinfo { - u8 epnum; /* endpoint number */ - u8 epdir; /* endpoint direction */ - u16 epsize; /* endpoint FIFO size */ -}; - -/* - * Platform specific MUSB configuration. Any platform using the musb - * functionality should create one instance of this structure in the - * platform specific file. - */ -struct musb_config { - struct musb_regs *regs; - u32 timeout; - u8 musb_speed; -}; - -/* externally defined data */ -extern struct musb_config musb_cfg; -extern struct musb_regs *musbr; - -/* exported functions */ -extern void musb_start(void); -extern void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt); -extern void write_fifo(u8 ep, u32 length, void *fifo_data); -extern void read_fifo(u8 ep, u32 length, void *fifo_data); - -/* extern functions */ -extern inline void musb_writew(u32 offset, u16 value); -extern inline void musb_writeb(u32 offset, u8 value); -extern inline u16 musb_readw(u32 offset); -extern inline u8 musb_readb(u32 offset); - -#endif /* __MUSB_HDRC_DEFS_H__ */ diff --git a/drivers/usb/musb_hcd.c b/drivers/usb/musb_hcd.c deleted file mode 100644 index 352a0d4..0000000 --- a/drivers/usb/musb_hcd.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * Mentor USB OTG Core host controller driver. - * - * Copyright (c) 2008 Texas Instruments - * - * 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 - * - * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments - */ - -#include -#include "musb_hcd.h" - -/* MSC control transfers */ -#define USB_MSC_BBB_RESET 0xFF -#define USB_MSC_BBB_GET_MAX_LUN 0xFE - -/* Endpoint configuration information */ -static struct musb_epinfo epinfo[3] = { - {MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ - {MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In - 512 Bytes */ - {MUSB_INTR_EP, 0, 64} /* EP2 - Interrupt IN - 64 Bytes */ -}; - -/* - * This function writes the data toggle value. - */ -static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) -{ - u16 toggle = usb_gettoggle(dev, ep, dir_out); - u16 csr; - - if (dir_out) { - if (!toggle) - writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); - else { - csr = readw(&musbr->txcsr); - csr |= MUSB_TXCSR_H_WR_DATATOGGLE; - writew(csr, &musbr->txcsr); - csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); - writew(csr, &musbr->txcsr); - } - } else { - if (!toggle) - writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); - else { - csr = readw(&musbr->rxcsr); - csr |= MUSB_RXCSR_H_WR_DATATOGGLE; - writew(csr, &musbr->rxcsr); - csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); - writew(csr, &musbr->rxcsr); - } - } -} - -/* - * This function checks if RxStall has occured on the endpoint. If a RxStall - * has occured, the RxStall is cleared and 1 is returned. If RxStall has - * not occured, 0 is returned. - */ -static u8 check_stall(u8 ep, u8 dir_out) -{ - u16 csr; - - /* For endpoint 0 */ - if (!ep) { - csr = readw(&musbr->txcsr); - if (csr & MUSB_CSR0_H_RXSTALL) { - csr &= ~MUSB_CSR0_H_RXSTALL; - writew(csr, &musbr->txcsr); - return 1; - } - } else { /* For non-ep0 */ - if (dir_out) { /* is it tx ep */ - csr = readw(&musbr->txcsr); - if (csr & MUSB_TXCSR_H_RXSTALL) { - csr &= ~MUSB_TXCSR_H_RXSTALL; - writew(csr, &musbr->txcsr); - return 1; - } - } else { /* is it rx ep */ - csr = readw(&musbr->rxcsr); - if (csr & MUSB_RXCSR_H_RXSTALL) { - csr &= ~MUSB_RXCSR_H_RXSTALL; - writew(csr, &musbr->rxcsr); - return 1; - } - } - } - return 0; -} - -/* - * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout - * error and -2 for stall. - */ -static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) -{ - u16 csr; - int result = 1; - - while (result > 0) { - csr = readw(&musbr->txcsr); - if (csr & MUSB_CSR0_H_ERROR) { - csr &= ~MUSB_CSR0_H_ERROR; - writew(csr, &musbr->txcsr); - dev->status = USB_ST_CRC_ERR; - result = -1; - break; - } - - switch (bit_mask) { - case MUSB_CSR0_TXPKTRDY: - if (!(csr & MUSB_CSR0_TXPKTRDY)) { - if (check_stall(MUSB_CONTROL_EP, 0)) { - dev->status = USB_ST_STALLED; - result = -2; - } else - result = 0; - } - break; - - case MUSB_CSR0_RXPKTRDY: - if (check_stall(MUSB_CONTROL_EP, 0)) { - dev->status = USB_ST_STALLED; - result = -2; - } else - if (csr & MUSB_CSR0_RXPKTRDY) - result = 0; - break; - - case MUSB_CSR0_H_REQPKT: - if (!(csr & MUSB_CSR0_H_REQPKT)) { - if (check_stall(MUSB_CONTROL_EP, 0)) { - dev->status = USB_ST_STALLED; - result = -2; - } else - result = 0; - } - break; - } - } - return result; -} - -/* - * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. - */ -static u8 wait_until_txep_ready(struct usb_device *dev, u8 ep) -{ - u16 csr; - - do { - if (check_stall(ep, 1)) { - dev->status = USB_ST_STALLED; - return 0; - } - - csr = readw(&musbr->txcsr); - if (csr & MUSB_TXCSR_H_ERROR) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - } while (csr & MUSB_TXCSR_TXPKTRDY); - return 1; -} - -/* - * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. - */ -static u8 wait_until_rxep_ready(struct usb_device *dev, u8 ep) -{ - u16 csr; - - do { - if (check_stall(ep, 0)) { - dev->status = USB_ST_STALLED; - return 0; - } - - csr = readw(&musbr->rxcsr); - if (csr & MUSB_RXCSR_H_ERROR) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - } while (!(csr & MUSB_RXCSR_RXPKTRDY)); - return 1; -} - -/* - * This function performs the setup phase of the control transfer - */ -static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) -{ - int result; - u16 csr; - - /* write the control request to ep0 fifo */ - write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); - - /* enable transfer of setup packet */ - csr = readw(&musbr->txcsr); - csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); - writew(csr, &musbr->txcsr); - - /* wait until the setup packet is transmitted */ - result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); - dev->act_len = 0; - return result; -} - -/* - * This function handles the control transfer in data phase - */ -static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) -{ - u16 csr; - u32 rxlen = 0; - u32 nextlen = 0; - u8 maxpktsize = (1 << dev->maxpacketsize) * 8; - u8 *rxbuff = (u8 *)buffer; - u8 rxedlength; - int result; - - while (rxlen < len) { - /* Determine the next read length */ - nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); - - /* Set the ReqPkt bit */ - csr = readw(&musbr->txcsr); - writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); - result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); - if (result < 0) - return result; - - /* Actual number of bytes received by usb */ - rxedlength = readb(&musbr->rxcount); - - /* Read the data from the RxFIFO */ - read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); - - /* Clear the RxPktRdy Bit */ - csr = readw(&musbr->txcsr); - csr &= ~MUSB_CSR0_RXPKTRDY; - writew(csr, &musbr->txcsr); - - /* short packet? */ - if (rxedlength != nextlen) { - dev->act_len += rxedlength; - break; - } - rxlen += nextlen; - dev->act_len = rxlen; - } - return 0; -} - -/* - * This function handles the control transfer out data phase - */ -static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) -{ - u16 csr; - u32 txlen = 0; - u32 nextlen = 0; - u8 maxpktsize = (1 << dev->maxpacketsize) * 8; - u8 *txbuff = (u8 *)buffer; - int result = 0; - - while (txlen < len) { - /* Determine the next write length */ - nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); - - /* Load the data to send in FIFO */ - write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); - - /* Set TXPKTRDY bit */ - csr = readw(&musbr->txcsr); - writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, - &musbr->txcsr); - result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); - if (result < 0) - break; - - txlen += nextlen; - dev->act_len = txlen; - } - return result; -} - -/* - * This function handles the control transfer out status phase - */ -static int ctrlreq_out_status_phase(struct usb_device *dev) -{ - u16 csr; - int result; - - /* Set the StatusPkt bit */ - csr = readw(&musbr->txcsr); - csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | - MUSB_CSR0_H_STATUSPKT); - writew(csr, &musbr->txcsr); - - /* Wait until TXPKTRDY bit is cleared */ - result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); - return result; -} - -/* - * This function handles the control transfer in status phase - */ -static int ctrlreq_in_status_phase(struct usb_device *dev) -{ - u16 csr; - int result; - - /* Set the StatusPkt bit and ReqPkt bit */ - csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; - writew(csr, &musbr->txcsr); - result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); - - /* clear StatusPkt bit and RxPktRdy bit */ - csr = readw(&musbr->txcsr); - csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); - writew(csr, &musbr->txcsr); - return result; -} - -/* - * determines the speed of the device (High/Full/Slow) - */ -static u8 get_dev_speed(struct usb_device *dev) -{ - return (dev->speed & USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : - ((dev->speed & USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : - MUSB_TYPE_SPEED_FULL); -} - -/* - * configure the hub address and the port address. - */ -static void config_hub_port(struct usb_device *dev, u8 ep) -{ - u8 chid; - u8 hub; - - /* Find out the nearest parent which is high speed */ - while (dev->parent->parent != NULL) - if (get_dev_speed(dev->parent) != MUSB_TYPE_SPEED_HIGH) - dev = dev->parent; - else - break; - - /* determine the port address at that hub */ - hub = dev->parent->devnum; - for (chid = 0; chid < USB_MAXCHILDREN; chid++) - if (dev->parent->children[chid] == dev) - break; - - /* configure the hub address and the port address */ - writeb(hub, &musbr->tar[ep].txhubaddr); - writeb((chid + 1), &musbr->tar[ep].txhubport); - writeb(hub, &musbr->tar[ep].rxhubaddr); - writeb((chid + 1), &musbr->tar[ep].rxhubport); -} - -/* - * do a control transfer - */ -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, struct devrequest *setup) -{ - int devnum = usb_pipedevice(pipe); - u16 csr; - u8 devspeed; - - /* select control endpoint */ - writeb(MUSB_CONTROL_EP, &musbr->index); - csr = readw(&musbr->txcsr); - - /* target addr and (for multipoint) hub addr/port */ - writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); - writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); - - /* configure the hub address and the port number as required */ - devspeed = get_dev_speed(dev); - if ((musb_ishighspeed()) && (dev->parent != NULL) && - (devspeed != MUSB_TYPE_SPEED_HIGH)) { - config_hub_port(dev, MUSB_CONTROL_EP); - writeb(devspeed << 6, &musbr->txtype); - } else { - writeb(musb_cfg.musb_speed << 6, &musbr->txtype); - writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); - writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); - writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); - writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); - } - - /* Control transfer setup phase */ - if (ctrlreq_setup_phase(dev, setup) < 0) - return 0; - - switch (setup->request) { - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - case USB_REQ_GET_INTERFACE: - case USB_REQ_GET_STATUS: - case USB_MSC_BBB_GET_MAX_LUN: - /* control transfer in-data-phase */ - if (ctrlreq_in_data_phase(dev, len, buffer) < 0) - return 0; - /* control transfer out-status-phase */ - if (ctrlreq_out_status_phase(dev) < 0) - return 0; - break; - - case USB_REQ_SET_ADDRESS: - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_INTERFACE: - case USB_REQ_CLEAR_FEATURE: - case USB_MSC_BBB_RESET: - /* control transfer in status phase */ - if (ctrlreq_in_status_phase(dev) < 0) - return 0; - break; - - case USB_REQ_SET_DESCRIPTOR: - /* control transfer out data phase */ - if (ctrlreq_out_data_phase(dev, len, buffer) < 0) - return 0; - /* control transfer in status phase */ - if (ctrlreq_in_status_phase(dev) < 0) - return 0; - break; - - default: - /* unhandled control transfer */ - return -1; - } - - dev->status = 0; - dev->act_len = len; - return len; -} - -/* - * do a bulk transfer - */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int len) -{ - int dir_out = usb_pipeout(pipe); - int ep = usb_pipeendpoint(pipe); - int devnum = usb_pipedevice(pipe); - u8 type; - u16 csr; - u32 txlen = 0; - u32 nextlen = 0; - u8 devspeed; - - /* select bulk endpoint */ - writeb(MUSB_BULK_EP, &musbr->index); - - /* write the address of the device */ - if (dir_out) - writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); - else - writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); - - /* configure the hub address and the port number as required */ - devspeed = get_dev_speed(dev); - if ((musb_ishighspeed()) && (dev->parent != NULL) && - (devspeed != MUSB_TYPE_SPEED_HIGH)) { - /* - * MUSB is in high speed and the destination device is full - * speed device. So configure the hub address and port - * address registers. - */ - config_hub_port(dev, MUSB_BULK_EP); - } else { - if (dir_out) { - writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); - writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); - } else { - writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); - writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); - } - devspeed = musb_cfg.musb_speed; - } - - /* Write the saved toggle bit value */ - write_toggle(dev, ep, dir_out); - - if (dir_out) { /* bulk-out transfer */ - /* Program the TxType register */ - type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | - (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | - (ep & MUSB_TYPE_REMOTE_END); - writeb(type, &musbr->txtype); - - /* Write maximum packet size to the TxMaxp register */ - writew(dev->epmaxpacketout[ep], &musbr->txmaxp); - while (txlen < len) { - nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? - (len-txlen) : dev->epmaxpacketout[ep]; - - /* Write the data to the FIFO */ - write_fifo(MUSB_BULK_EP, nextlen, - (void *)(((u8 *)buffer) + txlen)); - - /* Set the TxPktRdy bit */ - csr = readw(&musbr->txcsr); - writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); - - /* Wait until the TxPktRdy bit is cleared */ - if (!wait_until_txep_ready(dev, MUSB_BULK_EP)) { - readw(&musbr->txcsr); - usb_settoggle(dev, ep, dir_out, - (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); - dev->act_len = txlen; - return 0; - } - txlen += nextlen; - } - - /* Keep a copy of the data toggle bit */ - csr = readw(&musbr->txcsr); - usb_settoggle(dev, ep, dir_out, - (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); - } else { /* bulk-in transfer */ - /* Write the saved toggle bit value */ - write_toggle(dev, ep, dir_out); - - /* Program the RxType register */ - type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | - (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | - (ep & MUSB_TYPE_REMOTE_END); - writeb(type, &musbr->rxtype); - - /* Write the maximum packet size to the RxMaxp register */ - writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); - while (txlen < len) { - nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? - (len-txlen) : dev->epmaxpacketin[ep]; - - /* Set the ReqPkt bit */ - writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); - - /* Wait until the RxPktRdy bit is set */ - if (!wait_until_rxep_ready(dev, MUSB_BULK_EP)) { - csr = readw(&musbr->rxcsr); - usb_settoggle(dev, ep, dir_out, - (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); - csr &= ~MUSB_RXCSR_RXPKTRDY; - writew(csr, &musbr->rxcsr); - dev->act_len = txlen; - return 0; - } - - /* Read the data from the FIFO */ - read_fifo(MUSB_BULK_EP, nextlen, - (void *)(((u8 *)buffer) + txlen)); - - /* Clear the RxPktRdy bit */ - csr = readw(&musbr->rxcsr); - csr &= ~MUSB_RXCSR_RXPKTRDY; - writew(csr, &musbr->rxcsr); - txlen += nextlen; - } - - /* Keep a copy of the data toggle bit */ - csr = readw(&musbr->rxcsr); - usb_settoggle(dev, ep, dir_out, - (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); - } - - /* bulk transfer is complete */ - dev->status = 0; - dev->act_len = len; - return 0; -} - -/* - * This function initializes the usb controller module. - */ -int usb_lowlevel_init(void) -{ - u8 power; - u32 timeout; - - if (musb_platform_init() == -1) - return -1; - - /* Configure all the endpoint FIFO's and start usb controller */ - musbr = musb_cfg.regs; - musb_configure_ep(&epinfo[0], - sizeof(epinfo) / sizeof(struct musb_epinfo)); - musb_start(); - - /* - * Wait until musb is enabled in host mode with a timeout. There - * should be a usb device connected. - */ - timeout = musb_cfg.timeout; - while (timeout--) - if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) - break; - - /* if musb core is not in host mode, then return */ - if (!timeout) - return -1; - - /* start usb bus reset */ - power = readb(&musbr->power); - writeb(power | MUSB_POWER_RESET, &musbr->power); - - /* After initiating a usb reset, wait for about 20ms to 30ms */ - udelay(30000); - - /* stop usb bus reset */ - power = readb(&musbr->power); - power &= ~MUSB_POWER_RESET; - writeb(power, &musbr->power); - - /* Determine if the connected device is a high/full/low speed device */ - musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? - MUSB_TYPE_SPEED_HIGH : - ((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? - MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); - return 0; -} - -/* - * This function stops the operation of the davinci usb module. - */ -int usb_lowlevel_stop(void) -{ - /* Reset the USB module */ - musb_platform_deinit(); - writeb(0, &musbr->devctl); - return 0; -} - -/* - * This function supports usb interrupt transfers. Currently, usb interrupt - * transfers are not supported. - */ -int submit_int_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int len, int interval) -{ - int dir_out = usb_pipeout(pipe); - int ep = usb_pipeendpoint(pipe); - int devnum = usb_pipedevice(pipe); - u8 type; - u16 csr; - u32 txlen = 0; - u32 nextlen = 0; - u8 devspeed; - - /* select interrupt endpoint */ - writeb(MUSB_INTR_EP, &musbr->index); - - /* write the address of the device */ - if (dir_out) - writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); - else - writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); - - /* configure the hub address and the port number as required */ - devspeed = get_dev_speed(dev); - if ((musb_ishighspeed()) && (dev->parent != NULL) && - (devspeed != MUSB_TYPE_SPEED_HIGH)) { - /* - * MUSB is in high speed and the destination device is full - * speed device. So configure the hub address and port - * address registers. - */ - config_hub_port(dev, MUSB_INTR_EP); - } else { - if (dir_out) { - writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); - writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); - } else { - writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); - writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); - } - devspeed = musb_cfg.musb_speed; - } - - /* Write the saved toggle bit value */ - write_toggle(dev, ep, dir_out); - - if (!dir_out) { /* intrrupt-in transfer */ - /* Write the saved toggle bit value */ - write_toggle(dev, ep, dir_out); - writeb(interval, &musbr->rxinterval); - - /* Program the RxType register */ - type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | - (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | - (ep & MUSB_TYPE_REMOTE_END); - writeb(type, &musbr->rxtype); - - /* Write the maximum packet size to the RxMaxp register */ - writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); - - while (txlen < len) { - nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? - (len-txlen) : dev->epmaxpacketin[ep]; - - /* Set the ReqPkt bit */ - writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); - - /* Wait until the RxPktRdy bit is set */ - if (!wait_until_rxep_ready(dev, MUSB_INTR_EP)) { - csr = readw(&musbr->rxcsr); - usb_settoggle(dev, ep, dir_out, - (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); - csr &= ~MUSB_RXCSR_RXPKTRDY; - writew(csr, &musbr->rxcsr); - dev->act_len = txlen; - return 0; - } - - /* Read the data from the FIFO */ - read_fifo(MUSB_INTR_EP, nextlen, - (void *)(((u8 *)buffer) + txlen)); - - /* Clear the RxPktRdy bit */ - csr = readw(&musbr->rxcsr); - csr &= ~MUSB_RXCSR_RXPKTRDY; - writew(csr, &musbr->rxcsr); - txlen += nextlen; - } - - /* Keep a copy of the data toggle bit */ - csr = readw(&musbr->rxcsr); - usb_settoggle(dev, ep, dir_out, - (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); - } - - /* interrupt transfer is complete */ - dev->irq_status = 0; - dev->irq_act_len = len; - dev->irq_handle(dev); - dev->status = 0; - dev->act_len = len; - return 0; -} - - -#ifdef CONFIG_SYS_USB_EVENT_POLL -/* - * This function polls for USB keyboard data. - */ -void usb_event_poll() -{ - device_t *dev; - struct usb_device *usb_kbd_dev; - struct usb_interface_descriptor *iface; - struct usb_endpoint_descriptor *ep; - int pipe; - int maxp; - - /* Get the pointer to USB Keyboard device pointer */ - dev = device_get_by_name("usbkbd"); - usb_kbd_dev = (struct usb_device *)dev->priv; - iface = &usb_kbd_dev->config.if_desc[0]; - ep = &iface->ep_desc[0]; - pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); - - /* Submit a interrupt transfer request */ - maxp = usb_maxpacket(usb_kbd_dev, pipe); - usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], - maxp > 8 ? 8 : maxp, ep->bInterval); -} -#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/musb_hcd.h b/drivers/usb/musb_hcd.h deleted file mode 100644 index bb83311..0000000 --- a/drivers/usb/musb_hcd.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Mentor USB OTG Core host controller driver. - * - * Copyright (c) 2008 Texas Instruments - * - * 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 - * - * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments - */ - -#ifndef __MUSB_HCD_H__ -#define __MUSB_HCD_H__ - -#include "musb_core.h" -#ifdef CONFIG_USB_KEYBOARD -#include -extern unsigned char new[]; -#endif - -/* This defines the endpoint number used for control transfers */ -#define MUSB_CONTROL_EP 0 - -/* This defines the endpoint number used for bulk transfer */ -#define MUSB_BULK_EP 1 - -/* This defines the endpoint number used for interrupt transfer */ -#define MUSB_INTR_EP 2 - -/* Determine the operating speed of MUSB core */ -#define musb_ishighspeed() \ - ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ - >> MUSB_POWER_HSMODE_SHIFT) - -/* extern functions */ -extern int musb_platform_init(void); -extern void musb_platform_deinit(void); - -#endif /* __MUSB_HCD_H__ */ diff --git a/drivers/usb/r8a66597-hcd.c b/drivers/usb/r8a66597-hcd.c deleted file mode 100644 index db611b6..0000000 --- a/drivers/usb/r8a66597-hcd.c +++ /dev/null @@ -1,945 +0,0 @@ -/* - * R8A66597 HCD (Host Controller Driver) for u-boot - * - * Copyright (C) 2008 Yoshihiro Shimoda - * - * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include - -#include "r8a66597.h" - -#ifdef R8A66597_DEBUG -#define R8A66597_DPRINT printf -#else -#define R8A66597_DPRINT(...) -#endif - -static const char hcd_name[] = "r8a66597_hcd"; -static unsigned short clock = CONFIG_R8A66597_XTAL; -static unsigned short vif = CONFIG_R8A66597_LDRV; -static unsigned short endian = CONFIG_R8A66597_ENDIAN; -static struct r8a66597 gr8a66597; - -static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport) -{ - int i; - - *hub_devnum = 0; - *hubport = 0; - - /* check a device connected to root_hub */ - if ((dev->parent && dev->parent->devnum == 1) || - (dev->devnum == 1)) - return; - - for (i = 0; i < USB_MAXCHILDREN; i++) { - if (dev->parent->children[i] == dev) { - *hub_devnum = (u8)dev->parent->devnum; - *hubport = i; - return; - } - } - - printf("get_hub_data error.\n"); -} - -static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address, - struct usb_device *dev, int port) -{ - u16 val, usbspd, upphub, hubport; - unsigned long devadd_reg = get_devadd_addr(r8a66597_address); - - get_hub_data(dev, &upphub, &hubport); - usbspd = r8a66597->speed; - val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001); - r8a66597_write(r8a66597, val, devadd_reg); -} - -static int r8a66597_clock_enable(struct r8a66597 *r8a66597) -{ - u16 tmp; - int i = 0; - -#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) - do { - r8a66597_write(r8a66597, SCKE, SYSCFG0); - tmp = r8a66597_read(r8a66597, SYSCFG0); - if (i++ > 1000) { - printf("register access fail.\n"); - return -1; - } - } while ((tmp & SCKE) != SCKE); - r8a66597_write(r8a66597, 0x04, 0x02); -#else - do { - r8a66597_write(r8a66597, USBE, SYSCFG0); - tmp = r8a66597_read(r8a66597, SYSCFG0); - if (i++ > 1000) { - printf("register access fail.\n"); - return -1; - } - } while ((tmp & USBE) != USBE); - r8a66597_bclr(r8a66597, USBE, SYSCFG0); - r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0); - - i = 0; - r8a66597_bset(r8a66597, XCKE, SYSCFG0); - do { - udelay(1000); - tmp = r8a66597_read(r8a66597, SYSCFG0); - if (i++ > 500) { - printf("register access fail.\n"); - return -1; - } - } while ((tmp & SCKE) != SCKE); -#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */ - - return 0; -} - -static void r8a66597_clock_disable(struct r8a66597 *r8a66597) -{ - r8a66597_bclr(r8a66597, SCKE, SYSCFG0); - udelay(1); -#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) - r8a66597_bclr(r8a66597, PLLC, SYSCFG0); - r8a66597_bclr(r8a66597, XCKE, SYSCFG0); - r8a66597_bclr(r8a66597, USBE, SYSCFG0); -#endif -} - -static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port) -{ - u16 val; - - val = port ? DRPD : DCFM | DRPD; - r8a66597_bset(r8a66597, val, get_syscfg_reg(port)); - r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); - - r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port)); -} - -static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port) -{ - u16 val, tmp; - - r8a66597_write(r8a66597, 0, get_intenb_reg(port)); - r8a66597_write(r8a66597, 0, get_intsts_reg(port)); - - r8a66597_port_power(r8a66597, port, 0); - - do { - tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS; - udelay(640); - } while (tmp == EDGESTS); - - val = port ? DRPD : DCFM | DRPD; - r8a66597_bclr(r8a66597, val, get_syscfg_reg(port)); - r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); -} - -static int enable_controller(struct r8a66597 *r8a66597) -{ - int ret, port; - - ret = r8a66597_clock_enable(r8a66597); - if (ret < 0) - return ret; - - r8a66597_bset(r8a66597, vif & LDRV, PINCFG); - r8a66597_bset(r8a66597, USBE, SYSCFG0); - - r8a66597_bset(r8a66597, INTL, SOFCFG); - r8a66597_write(r8a66597, 0, INTENB0); - r8a66597_write(r8a66597, 0, INTENB1); - r8a66597_write(r8a66597, 0, INTENB2); - - r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL); - r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL); - r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL); - r8a66597_bset(r8a66597, TRNENSEL, SOFCFG); - - for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) - r8a66597_enable_port(r8a66597, port); - - return 0; -} - -static void disable_controller(struct r8a66597 *r8a66597) -{ - int i; - - if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE)) - return; - - r8a66597_write(r8a66597, 0, INTENB0); - r8a66597_write(r8a66597, 0, INTSTS0); - - r8a66597_write(r8a66597, 0, D0FIFOSEL); - r8a66597_write(r8a66597, 0, D1FIFOSEL); - r8a66597_write(r8a66597, 0, DCPCFG); - r8a66597_write(r8a66597, 0x40, DCPMAXP); - r8a66597_write(r8a66597, 0, DCPCTR); - - for (i = 0; i <= 10; i++) - r8a66597_write(r8a66597, 0, get_devadd_addr(i)); - for (i = 1; i <= 5; i++) { - r8a66597_write(r8a66597, 0, get_pipetre_addr(i)); - r8a66597_write(r8a66597, 0, get_pipetrn_addr(i)); - } - for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) { - r8a66597_write(r8a66597, 0, get_pipectr_addr(i)); - r8a66597_write(r8a66597, i, PIPESEL); - r8a66597_write(r8a66597, 0, PIPECFG); - r8a66597_write(r8a66597, 0, PIPEBUF); - r8a66597_write(r8a66597, 0, PIPEMAXP); - r8a66597_write(r8a66597, 0, PIPEPERI); - } - - for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) - r8a66597_disable_port(r8a66597, i); - - r8a66597_clock_disable(r8a66597); -} - -static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg, - u16 mask, u16 loop) -{ - u16 tmp; - int i = 0; - - do { - tmp = r8a66597_read(r8a66597, reg); - if (i++ > 1000000) { - printf("register%lx, loop %x is timeout\n", reg, loop); - break; - } - } while ((tmp & mask) != loop); -} - -static void pipe_buffer_setting(struct r8a66597 *r8a66597, - struct usb_device *dev, unsigned long pipe) -{ - u16 val = 0; - u16 pipenum, bufnum, maxpacket; - - if (usb_pipein(pipe)) { - pipenum = BULK_IN_PIPENUM; - bufnum = BULK_IN_BUFNUM; - maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; - } else { - pipenum = BULK_OUT_PIPENUM; - bufnum = BULK_OUT_BUFNUM; - maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; - } - - if (r8a66597->pipe_config & (1 << pipenum)) - return; - r8a66597->pipe_config |= (1 << pipenum); - - r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum)); - r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum)); - r8a66597_write(r8a66597, pipenum, PIPESEL); - - /* FIXME: This driver support bulk transfer only. */ - if (!usb_pipein(pipe)) - val |= R8A66597_DIR; - else - val |= R8A66597_SHTNAK; - val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe); - r8a66597_write(r8a66597, val, PIPECFG); - - r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF); - r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) | - maxpacket, PIPEMAXP); - r8a66597_write(r8a66597, 0, PIPEPERI); - r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum)); -} - -static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev, - struct devrequest *setup) -{ - int i; - unsigned short *p = (unsigned short *)setup; - unsigned long setup_addr = USBREQ; - u16 intsts1; - int timeout = 3000; - u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum; - - r8a66597_write(r8a66597, make_devsel(devsel) | - (8 << dev->maxpacketsize), DCPMAXP); - r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1); - - for (i = 0; i < 4; i++) { - r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr); - setup_addr += 2; - } - r8a66597_write(r8a66597, ~0x0001, BRDYSTS); - r8a66597_write(r8a66597, SUREQ, DCPCTR); - - while (1) { - intsts1 = r8a66597_read(r8a66597, INTSTS1); - if (intsts1 & SACK) - break; - if (intsts1 & SIGN) { - printf("setup packet send error\n"); - return -1; - } - if (timeout-- < 0) { - printf("setup packet timeout\n"); - return -1; - } - udelay(500); - } - - return 0; -} - -static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev, - unsigned long pipe, void *buffer, int transfer_len) -{ - u16 tmp, bufsize; - u16 *buf; - size_t size; - - R8A66597_DPRINT("%s\n", __func__); - - r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM, - MBW | CURPIPE, CFIFOSEL); - r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM); - tmp = r8a66597_read(r8a66597, CFIFOCTR); - if ((tmp & FRDY) == 0) { - printf("%s FRDY is not set (%x)\n", __func__, tmp); - return -1; - } - - /* prepare parameters */ - bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; - buf = (u16 *)(buffer + dev->act_len); - size = min((int)bufsize, transfer_len - dev->act_len); - - /* write fifo */ - r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); - if (buffer) { - r8a66597_write_fifo(r8a66597, CFIFO, buf, size); - r8a66597_write(r8a66597, BVAL, CFIFOCTR); - } - - /* update parameters */ - dev->act_len += size; - - r8a66597_mdfy(r8a66597, PID_BUF, PID, - get_pipectr_addr(BULK_OUT_PIPENUM)); - - while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM))) - if (ctrlc()) - return -1; - r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); - - if (dev->act_len >= transfer_len) - r8a66597_mdfy(r8a66597, PID_NAK, PID, - get_pipectr_addr(BULK_OUT_PIPENUM)); - - return 0; -} - -static int receive_bulk_packet(struct r8a66597 *r8a66597, - struct usb_device *dev, - unsigned long pipe, - void *buffer, int transfer_len) -{ - u16 tmp; - u16 *buf; - const u16 pipenum = BULK_IN_PIPENUM; - int rcv_len; - int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; - - R8A66597_DPRINT("%s\n", __func__); - - /* prepare */ - if (dev->act_len == 0) { - r8a66597_mdfy(r8a66597, PID_NAK, PID, - get_pipectr_addr(pipenum)); - r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); - - r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum)); - r8a66597_write(r8a66597, - (transfer_len + maxpacket - 1) / maxpacket, - get_pipetrn_addr(pipenum)); - r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum)); - - r8a66597_mdfy(r8a66597, PID_BUF, PID, - get_pipectr_addr(pipenum)); - } - - r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL); - r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum); - - while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum))) - if (ctrlc()) - return -1; - r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); - - tmp = r8a66597_read(r8a66597, CFIFOCTR); - if ((tmp & FRDY) == 0) { - printf("%s FRDY is not set. (%x)\n", __func__, tmp); - return -1; - } - - buf = (u16 *)(buffer + dev->act_len); - rcv_len = tmp & DTLN; - dev->act_len += rcv_len; - - if (buffer) { - if (rcv_len == 0) - r8a66597_write(r8a66597, BCLR, CFIFOCTR); - else - r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len); - } - - return 0; -} - -static int receive_control_packet(struct r8a66597 *r8a66597, - struct usb_device *dev, - void *buffer, int transfer_len) -{ - u16 tmp; - int rcv_len; - - /* FIXME: limit transfer size : 64byte or less */ - - r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); - r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); - r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); - r8a66597_bset(r8a66597, SQSET, DCPCTR); - r8a66597_write(r8a66597, BCLR, CFIFOCTR); - r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); - - while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001)) - if (ctrlc()) - return -1; - r8a66597_write(r8a66597, ~0x0001, BRDYSTS); - - r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL); - r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); - - tmp = r8a66597_read(r8a66597, CFIFOCTR); - if ((tmp & FRDY) == 0) { - printf("%s FRDY is not set. (%x)\n", __func__, tmp); - return -1; - } - - rcv_len = tmp & DTLN; - dev->act_len += rcv_len; - - r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); - - if (buffer) { - if (rcv_len == 0) - r8a66597_write(r8a66597, BCLR, DCPCTR); - else - r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len); - } - - return 0; -} - -static int send_status_packet(struct r8a66597 *r8a66597, - unsigned long pipe) -{ - r8a66597_bset(r8a66597, SQSET, DCPCTR); - r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); - - if (usb_pipein(pipe)) { - r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); - r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); - r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); - r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); - r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR); - } else { - r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); - r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); - r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); - r8a66597_write(r8a66597, BCLR, CFIFOCTR); - } - r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); - - while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001)) - if (ctrlc()) - return -1; - - return 0; -} - -static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port) -{ - int count = R8A66597_MAX_SAMPLING; - unsigned short syssts, old_syssts; - - R8A66597_DPRINT("%s\n", __func__); - - old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); - while (count > 0) { - wait_ms(R8A66597_RH_POLL_TIME); - - syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); - if (syssts == old_syssts) { - count--; - } else { - count = R8A66597_MAX_SAMPLING; - old_syssts = syssts; - } - } -} - -static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port) -{ - wait_ms(10); - r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port)); - wait_ms(50); - r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port)); - wait_ms(50); -} - -static int check_usb_device_connecting(struct r8a66597 *r8a66597) -{ - int timeout = 10000; /* 100usec * 10000 = 1sec */ - int i; - - for (i = 0; i < 5; i++) { - /* check a usb cable connect */ - while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) { - if (timeout-- < 0) { - printf("%s timeout.\n", __func__); - return -1; - } - udelay(100); - } - - /* check a data line */ - r8a66597_check_syssts(r8a66597, 0); - - r8a66597_bus_reset(r8a66597, 0); - r8a66597->speed = get_rh_usb_speed(r8a66597, 0); - - if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) { - r8a66597->port_change = USB_PORT_STAT_C_CONNECTION; - r8a66597->port_status = USB_PORT_STAT_CONNECTION | - USB_PORT_STAT_ENABLE; - return 0; /* success */ - } - - R8A66597_DPRINT("USB device has detached. retry = %d\n", i); - r8a66597_write(r8a66597, ~DTCH, INTSTS1); - } - - return -1; /* fail */ -} - -/* based on usb_ohci.c */ -#define min_t(type, x, y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x01, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; */ - - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static unsigned char root_hub_str_index0[] = -{ - 0x04, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 0x09, /* __u8 lang ID */ - 0x04, /* __u8 lang ID */ -}; - -static unsigned char root_hub_str_index1[] = -{ - 34, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 'R', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '8', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'A', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '6', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '6', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '5', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '9', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - '7', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'R', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 't', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'u', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'b', /* __u8 Unicode */ - 0, /* __u8 Unicode */ -}; - -static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) -{ - struct r8a66597 *r8a66597 = &gr8a66597; - int leni = transfer_len; - int len = 0; - int stat = 0; - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - unsigned char data[32]; - - R8A66597_DPRINT("%s\n", __func__); - - if (usb_pipeint(pipe)) { - printf("Root-Hub submit IRQ: NOT implemented"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = cpu_to_le16 (cmd->value); - wIndex = cpu_to_le16 (cmd->index); - wLength = cpu_to_le16 (cmd->length); - - switch (bmRType_bReq) { - case RH_GET_STATUS: - *(__u16 *)buffer = cpu_to_le16(1); - len = 2; - break; - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *)buffer = cpu_to_le16(0); - len = 2; - break; - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *)buffer = cpu_to_le16(0); - len = 2; - break; - case RH_GET_STATUS | RH_CLASS: - *(__u32 *)buffer = cpu_to_le32(0); - len = 4; - break; - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *)buffer = cpu_to_le32(r8a66597->port_status | - (r8a66597->port_change << 16)); - len = 4; - break; - case RH_CLEAR_FEATURE | RH_ENDPOINT: - case RH_CLEAR_FEATURE | RH_CLASS: - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case RH_C_PORT_CONNECTION: - r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION; - break; - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - break; - case (RH_PORT_RESET): - r8a66597_bus_reset(r8a66597, 0); - break; - case (RH_PORT_POWER): - break; - case (RH_PORT_ENABLE): - break; - } - break; - case RH_SET_ADDRESS: - gr8a66597.rh_devnum = wValue; - break; - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_dev_des), - wLength)); - memcpy(buffer, root_hub_dev_des, len); - break; - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_config_des), - wLength)); - memcpy(buffer, root_hub_config_des, len); - break; - case (0x03): /* string descriptors */ - if (wValue == 0x0300) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_str_index0), - wLength)); - memcpy(buffer, root_hub_str_index0, len); - } - if (wValue == 0x0301) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_str_index1), - wLength)); - memcpy(buffer, root_hub_str_index1, len); - } - break; - default: - stat = USB_ST_STALLED; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - { - __u32 temp = 0x00000001; - - data[0] = 9; /* min length; */ - data[1] = 0x29; - data[2] = temp & RH_A_NDP; - data[3] = 0; - if (temp & RH_A_PSM) - data[3] |= 0x1; - if (temp & RH_A_NOCP) - data[3] |= 0x10; - else if (temp & RH_A_OCPM) - data[3] |= 0x8; - - /* corresponds to data[4-7] */ - data[5] = (temp & RH_A_POTPGT) >> 24; - data[7] = temp & RH_B_DR; - if (data[2] < 7) { - data[8] = 0xff; - } else { - data[0] += 2; - data[8] = (temp & RH_B_DR) >> 8; - data[10] = data[9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data[0], wLength)); - memcpy(buffer, data, len); - break; - } - - case RH_GET_CONFIGURATION: - *(__u8 *) buffer = 0x01; - len = 1; - break; - case RH_SET_CONFIGURATION: - break; - default: - R8A66597_DPRINT("unsupported root hub command"); - stat = USB_ST_STALLED; - } - - wait_ms(1); - - len = min_t(int, len, leni); - - dev->act_len = len; - dev->status = stat; - - return stat; -} - -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len) -{ - struct r8a66597 *r8a66597 = &gr8a66597; - int ret = 0; - - R8A66597_DPRINT("%s\n", __func__); - R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n", - pipe, buffer, transfer_len, dev->devnum); - - set_devadd(r8a66597, dev->devnum, dev, 0); - - pipe_buffer_setting(r8a66597, dev, pipe); - - dev->act_len = 0; - while (dev->act_len < transfer_len && ret == 0) { - if (ctrlc()) - return -1; - - if (usb_pipein(pipe)) - ret = receive_bulk_packet(r8a66597, dev, pipe, buffer, - transfer_len); - else - ret = send_bulk_packet(r8a66597, dev, pipe, buffer, - transfer_len); - } - - if (ret == 0) - dev->status = 0; - - return ret; -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *setup) -{ - struct r8a66597 *r8a66597 = &gr8a66597; - u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ? - 0 : dev->devnum; - - R8A66597_DPRINT("%s\n", __func__); - if (usb_pipedevice(pipe) == r8a66597->rh_devnum) - return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); - - R8A66597_DPRINT("%s: setup\n", __func__); - set_devadd(r8a66597, r8a66597_address, dev, 0); - - if (send_setup_packet(r8a66597, dev, setup) < 0) { - printf("setup packet send error\n"); - return -1; - } - - dev->act_len = 0; - if (usb_pipein(pipe)) - if (receive_control_packet(r8a66597, dev, buffer, - transfer_len) < 0) - return -1; - - if (send_status_packet(r8a66597, pipe) < 0) - return -1; - - dev->status = 0; - - return 0; -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - /* no implement */ - R8A66597_DPRINT("%s\n", __func__); - return 0; -} - -void usb_event_poll(void) -{ - /* no implement */ - R8A66597_DPRINT("%s\n", __func__); -} - -int usb_lowlevel_init(void) -{ - struct r8a66597 *r8a66597 = &gr8a66597; - - R8A66597_DPRINT("%s\n", __func__); - - memset(r8a66597, 0, sizeof(r8a66597)); - r8a66597->reg = CONFIG_R8A66597_BASE_ADDR; - - disable_controller(r8a66597); - wait_ms(100); - - enable_controller(r8a66597); - r8a66597_port_power(r8a66597, 0 , 1); - - /* check usb device */ - check_usb_device_connecting(r8a66597); - - wait_ms(50); - - return 0; -} - -int usb_lowlevel_stop(void) -{ - disable_controller(&gr8a66597); - - return 0; -} diff --git a/drivers/usb/r8a66597.h b/drivers/usb/r8a66597.h deleted file mode 100644 index 9af6446..0000000 --- a/drivers/usb/r8a66597.h +++ /dev/null @@ -1,659 +0,0 @@ -/* - * R8A66597 HCD (Host Controller Driver) for u-boot - * - * Copyright (C) 2008 Yoshihiro Shimoda - * - * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef __R8A66597_H__ -#define __R8A66597_H__ - -#define SYSCFG0 0x00 -#define SYSCFG1 0x02 -#define SYSSTS0 0x04 -#define SYSSTS1 0x06 -#define DVSTCTR0 0x08 -#define DVSTCTR1 0x0A -#define TESTMODE 0x0C -#define PINCFG 0x0E -#define DMA0CFG 0x10 -#define DMA1CFG 0x12 -#define CFIFO 0x14 -#define D0FIFO 0x18 -#define D1FIFO 0x1C -#define CFIFOSEL 0x20 -#define CFIFOCTR 0x22 -#define CFIFOSIE 0x24 -#define D0FIFOSEL 0x28 -#define D0FIFOCTR 0x2A -#define D1FIFOSEL 0x2C -#define D1FIFOCTR 0x2E -#define INTENB0 0x30 -#define INTENB1 0x32 -#define INTENB2 0x34 -#define BRDYENB 0x36 -#define NRDYENB 0x38 -#define BEMPENB 0x3A -#define SOFCFG 0x3C -#define INTSTS0 0x40 -#define INTSTS1 0x42 -#define INTSTS2 0x44 -#define BRDYSTS 0x46 -#define NRDYSTS 0x48 -#define BEMPSTS 0x4A -#define FRMNUM 0x4C -#define UFRMNUM 0x4E -#define USBADDR 0x50 -#define USBREQ 0x54 -#define USBVAL 0x56 -#define USBINDX 0x58 -#define USBLENG 0x5A -#define DCPCFG 0x5C -#define DCPMAXP 0x5E -#define DCPCTR 0x60 -#define PIPESEL 0x64 -#define PIPECFG 0x68 -#define PIPEBUF 0x6A -#define PIPEMAXP 0x6C -#define PIPEPERI 0x6E -#define PIPE1CTR 0x70 -#define PIPE2CTR 0x72 -#define PIPE3CTR 0x74 -#define PIPE4CTR 0x76 -#define PIPE5CTR 0x78 -#define PIPE6CTR 0x7A -#define PIPE7CTR 0x7C -#define PIPE8CTR 0x7E -#define PIPE9CTR 0x80 -#define PIPE1TRE 0x90 -#define PIPE1TRN 0x92 -#define PIPE2TRE 0x94 -#define PIPE2TRN 0x96 -#define PIPE3TRE 0x98 -#define PIPE3TRN 0x9A -#define PIPE4TRE 0x9C -#define PIPE4TRN 0x9E -#define PIPE5TRE 0xA0 -#define PIPE5TRN 0xA2 -#define DEVADD0 0xD0 -#define DEVADD1 0xD2 -#define DEVADD2 0xD4 -#define DEVADD3 0xD6 -#define DEVADD4 0xD8 -#define DEVADD5 0xDA -#define DEVADD6 0xDC -#define DEVADD7 0xDE -#define DEVADD8 0xE0 -#define DEVADD9 0xE2 -#define DEVADDA 0xE4 - -/* System Configuration Control Register */ -#define XTAL 0xC000 /* b15-14: Crystal selection */ -#define XTAL48 0x8000 /* 48MHz */ -#define XTAL24 0x4000 /* 24MHz */ -#define XTAL12 0x0000 /* 12MHz */ -#define XCKE 0x2000 /* b13: External clock enable */ -#define PLLC 0x0800 /* b11: PLL control */ -#define SCKE 0x0400 /* b10: USB clock enable */ -#define PCSDIS 0x0200 /* b9: not CS wakeup */ -#define LPSME 0x0100 /* b8: Low power sleep mode */ -#define HSE 0x0080 /* b7: Hi-speed enable */ -#define DCFM 0x0040 /* b6: Controller function select */ -#define DRPD 0x0020 /* b5: D+/- pull down control */ -#define DPRPU 0x0010 /* b4: D+ pull up control */ -#define USBE 0x0001 /* b0: USB module operation enable */ - -/* System Configuration Status Register */ -#define OVCBIT 0x8000 /* b15-14: Over-current bit */ -#define OVCMON 0xC000 /* b15-14: Over-current monitor */ -#define SOFEA 0x0020 /* b5: SOF monitor */ -#define IDMON 0x0004 /* b3: ID-pin monitor */ -#define LNST 0x0003 /* b1-0: D+, D- line status */ -#define SE1 0x0003 /* SE1 */ -#define FS_KSTS 0x0002 /* Full-Speed K State */ -#define FS_JSTS 0x0001 /* Full-Speed J State */ -#define LS_JSTS 0x0002 /* Low-Speed J State */ -#define LS_KSTS 0x0001 /* Low-Speed K State */ -#define SE0 0x0000 /* SE0 */ - -/* Device State Control Register */ -#define EXTLP0 0x0400 /* b10: External port */ -#define VBOUT 0x0200 /* b9: VBUS output */ -#define WKUP 0x0100 /* b8: Remote wakeup */ -#define RWUPE 0x0080 /* b7: Remote wakeup sense */ -#define USBRST 0x0040 /* b6: USB reset enable */ -#define RESUME 0x0020 /* b5: Resume enable */ -#define UACT 0x0010 /* b4: USB bus enable */ -#define RHST 0x0007 /* b1-0: Reset handshake status */ -#define HSPROC 0x0004 /* HS handshake is processing */ -#define HSMODE 0x0003 /* Hi-Speed mode */ -#define FSMODE 0x0002 /* Full-Speed mode */ -#define LSMODE 0x0001 /* Low-Speed mode */ -#define UNDECID 0x0000 /* Undecided */ - -/* Test Mode Register */ -#define UTST 0x000F /* b3-0: Test select */ -#define H_TST_PACKET 0x000C /* HOST TEST Packet */ -#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ -#define H_TST_K 0x000A /* HOST TEST K */ -#define H_TST_J 0x0009 /* HOST TEST J */ -#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ -#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ -#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ -#define P_TST_K 0x0002 /* PERI TEST K */ -#define P_TST_J 0x0001 /* PERI TEST J */ -#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ - -/* Data Pin Configuration Register */ -#define LDRV 0x8000 /* b15: Drive Current Adjust */ -#define VIF1 0x0000 /* VIF = 1.8V */ -#define VIF3 0x8000 /* VIF = 3.3V */ -#define INTA 0x0001 /* b1: USB INT-pin active */ - -/* DMAx Pin Configuration Register */ -#define DREQA 0x4000 /* b14: Dreq active select */ -#define BURST 0x2000 /* b13: Burst mode */ -#define DACKA 0x0400 /* b10: Dack active select */ -#define DFORM 0x0380 /* b9-7: DMA mode select */ -#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ -#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ -#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ -#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ -#define DENDA 0x0040 /* b6: Dend active select */ -#define PKTM 0x0020 /* b5: Packet mode */ -#define DENDE 0x0010 /* b4: Dend enable */ -#define OBUS 0x0004 /* b2: OUTbus mode */ - -/* CFIFO/DxFIFO Port Select Register */ -#define RCNT 0x8000 /* b15: Read count mode */ -#define REW 0x4000 /* b14: Buffer rewind */ -#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ -#define DREQE 0x1000 /* b12: DREQ output enable */ -#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) -#define MBW 0x0800 -#else -#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */ -#endif -#define MBW_8 0x0000 /* 8bit */ -#define MBW_16 0x0400 /* 16bit */ -#define BIGEND 0x0100 /* b8: Big endian mode */ -#define BYTE_LITTLE 0x0000 /* little dendian */ -#define BYTE_BIG 0x0100 /* big endifan */ -#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ -#define CURPIPE 0x000F /* b2-0: PIPE select */ - -/* CFIFO/DxFIFO Port Control Register */ -#define BVAL 0x8000 /* b15: Buffer valid flag */ -#define BCLR 0x4000 /* b14: Buffer clear */ -#define FRDY 0x2000 /* b13: FIFO ready */ -#define DTLN 0x0FFF /* b11-0: FIFO received data length */ - -/* Interrupt Enable Register 0 */ -#define VBSE 0x8000 /* b15: VBUS interrupt */ -#define RSME 0x4000 /* b14: Resume interrupt */ -#define SOFE 0x2000 /* b13: Frame update interrupt */ -#define DVSE 0x1000 /* b12: Device state transition interrupt */ -#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ -#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ -#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ -#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ - -/* Interrupt Enable Register 1 */ -#define OVRCRE 0x8000 /* b15: Over-current interrupt */ -#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ -#define DTCHE 0x1000 /* b12: Detach sense interrupt */ -#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ -#define EOFERRE 0x0040 /* b6: EOF error interrupt */ -#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ -#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ - -/* BRDY Interrupt Enable/Status Register */ -#define BRDY9 0x0200 /* b9: PIPE9 */ -#define BRDY8 0x0100 /* b8: PIPE8 */ -#define BRDY7 0x0080 /* b7: PIPE7 */ -#define BRDY6 0x0040 /* b6: PIPE6 */ -#define BRDY5 0x0020 /* b5: PIPE5 */ -#define BRDY4 0x0010 /* b4: PIPE4 */ -#define BRDY3 0x0008 /* b3: PIPE3 */ -#define BRDY2 0x0004 /* b2: PIPE2 */ -#define BRDY1 0x0002 /* b1: PIPE1 */ -#define BRDY0 0x0001 /* b1: PIPE0 */ - -/* NRDY Interrupt Enable/Status Register */ -#define NRDY9 0x0200 /* b9: PIPE9 */ -#define NRDY8 0x0100 /* b8: PIPE8 */ -#define NRDY7 0x0080 /* b7: PIPE7 */ -#define NRDY6 0x0040 /* b6: PIPE6 */ -#define NRDY5 0x0020 /* b5: PIPE5 */ -#define NRDY4 0x0010 /* b4: PIPE4 */ -#define NRDY3 0x0008 /* b3: PIPE3 */ -#define NRDY2 0x0004 /* b2: PIPE2 */ -#define NRDY1 0x0002 /* b1: PIPE1 */ -#define NRDY0 0x0001 /* b1: PIPE0 */ - -/* BEMP Interrupt Enable/Status Register */ -#define BEMP9 0x0200 /* b9: PIPE9 */ -#define BEMP8 0x0100 /* b8: PIPE8 */ -#define BEMP7 0x0080 /* b7: PIPE7 */ -#define BEMP6 0x0040 /* b6: PIPE6 */ -#define BEMP5 0x0020 /* b5: PIPE5 */ -#define BEMP4 0x0010 /* b4: PIPE4 */ -#define BEMP3 0x0008 /* b3: PIPE3 */ -#define BEMP2 0x0004 /* b2: PIPE2 */ -#define BEMP1 0x0002 /* b1: PIPE1 */ -#define BEMP0 0x0001 /* b0: PIPE0 */ - -/* SOF Pin Configuration Register */ -#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ -#define BRDYM 0x0040 /* b6: BRDY clear timing */ -#define INTL 0x0020 /* b5: Interrupt sense select */ -#define EDGESTS 0x0010 /* b4: */ -#define SOFMODE 0x000C /* b3-2: SOF pin select */ -#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ -#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ -#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ - -/* Interrupt Status Register 0 */ -#define VBINT 0x8000 /* b15: VBUS interrupt */ -#define RESM 0x4000 /* b14: Resume interrupt */ -#define SOFR 0x2000 /* b13: SOF frame update interrupt */ -#define DVST 0x1000 /* b12: Device state transition interrupt */ -#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ -#define BEMP 0x0400 /* b10: Buffer empty interrupt */ -#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ -#define BRDY 0x0100 /* b8: Buffer ready interrupt */ -#define VBSTS 0x0080 /* b7: VBUS input port */ -#define DVSQ 0x0070 /* b6-4: Device state */ -#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ -#define DS_SPD_ADDR 0x0060 /* Suspend Address */ -#define DS_SPD_DFLT 0x0050 /* Suspend Default */ -#define DS_SPD_POWR 0x0040 /* Suspend Powered */ -#define DS_SUSP 0x0040 /* Suspend */ -#define DS_CNFG 0x0030 /* Configured */ -#define DS_ADDS 0x0020 /* Address */ -#define DS_DFLT 0x0010 /* Default */ -#define DS_POWR 0x0000 /* Powered */ -#define DVSQS 0x0030 /* b5-4: Device state */ -#define VALID 0x0008 /* b3: Setup packet detected flag */ -#define CTSQ 0x0007 /* b2-0: Control transfer stage */ -#define CS_SQER 0x0006 /* Sequence error */ -#define CS_WRND 0x0005 /* Control write nodata status stage */ -#define CS_WRSS 0x0004 /* Control write status stage */ -#define CS_WRDS 0x0003 /* Control write data stage */ -#define CS_RDSS 0x0002 /* Control read status stage */ -#define CS_RDDS 0x0001 /* Control read data stage */ -#define CS_IDST 0x0000 /* Idle or setup stage */ - -/* Interrupt Status Register 1 */ -#define OVRCR 0x8000 /* b15: Over-current interrupt */ -#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ -#define DTCH 0x1000 /* b12: Detach sense interrupt */ -#define ATTCH 0x0800 /* b11: Attach sense interrupt */ -#define EOFERR 0x0040 /* b6: EOF-error interrupt */ -#define SIGN 0x0020 /* b5: Setup ignore interrupt */ -#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ - -/* Frame Number Register */ -#define OVRN 0x8000 /* b15: Overrun error */ -#define CRCE 0x4000 /* b14: Received data error */ -#define FRNM 0x07FF /* b10-0: Frame number */ - -/* Micro Frame Number Register */ -#define UFRNM 0x0007 /* b2-0: Micro frame number */ - -/* Default Control Pipe Maxpacket Size Register */ -/* Pipe Maxpacket Size Register */ -#define DEVSEL 0xF000 /* b15-14: Device address select */ -#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ - -/* Default Control Pipe Control Register */ -#define BSTS 0x8000 /* b15: Buffer status */ -#define SUREQ 0x4000 /* b14: Send USB request */ -#define CSCLR 0x2000 /* b13: complete-split status clear */ -#define CSSTS 0x1000 /* b12: complete-split status */ -#define SUREQCLR 0x0800 /* b11: stop setup request */ -#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ -#define SQSET 0x0080 /* b7: Sequence toggle bit set */ -#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ -#define PBUSY 0x0020 /* b5: pipe busy */ -#define PINGE 0x0010 /* b4: ping enable */ -#define CCPL 0x0004 /* b2: Enable control transfer complete */ -#define PID 0x0003 /* b1-0: Response PID */ -#define PID_STALL11 0x0003 /* STALL */ -#define PID_STALL 0x0002 /* STALL */ -#define PID_BUF 0x0001 /* BUF */ -#define PID_NAK 0x0000 /* NAK */ - -/* Pipe Window Select Register */ -#define PIPENM 0x0007 /* b2-0: Pipe select */ - -/* Pipe Configuration Register */ -#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ -#define R8A66597_ISO 0xC000 /* Isochronous */ -#define R8A66597_INT 0x8000 /* Interrupt */ -#define R8A66597_BULK 0x4000 /* Bulk */ -#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ -#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ -#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ -#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ -#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ -#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ - -/* Pipe Buffer Configuration Register */ -#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ -#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ -#define PIPE0BUF 256 -#define PIPExBUF 64 - -/* Pipe Maxpacket Size Register */ -#define MXPS 0x07FF /* b10-0: Maxpacket size */ - -/* Pipe Cycle Configuration Register */ -#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ -#define IITV 0x0007 /* b2-0: Isochronous interval */ - -/* Pipex Control Register */ -#define BSTS 0x8000 /* b15: Buffer status */ -#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ -#define CSCLR 0x2000 /* b13: complete-split status clear */ -#define CSSTS 0x1000 /* b12: complete-split status */ -#define ATREPM 0x0400 /* b10: Auto repeat mode */ -#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ -#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ -#define SQSET 0x0080 /* b7: Sequence toggle bit set */ -#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ -#define PBUSY 0x0020 /* b5: pipe busy */ -#define PID 0x0003 /* b1-0: Response PID */ - -/* PIPExTRE */ -#define TRENB 0x0200 /* b9: Transaction counter enable */ -#define TRCLR 0x0100 /* b8: Transaction counter clear */ - -/* PIPExTRN */ -#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ - -/* DEVADDx */ -#define UPPHUB 0x7800 -#define HUBPORT 0x0700 -#define USBSPD 0x00C0 -#define RTPORT 0x0001 - -#define R8A66597_MAX_NUM_PIPE 10 -#define R8A66597_BUF_BSIZE 8 -#define R8A66597_MAX_DEVICE 10 -#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) -#define R8A66597_MAX_ROOT_HUB 1 -#else -#define R8A66597_MAX_ROOT_HUB 2 -#endif -#define R8A66597_MAX_SAMPLING 5 -#define R8A66597_RH_POLL_TIME 10 - -#define BULK_IN_PIPENUM 3 -#define BULK_IN_BUFNUM 8 - -#define BULK_OUT_PIPENUM 4 -#define BULK_OUT_BUFNUM 40 - -#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5)) -#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9)) -#define make_devsel(addr) (addr << 12) - -struct r8a66597 { - unsigned long reg; - unsigned short pipe_config; /* bit field */ - unsigned short port_status; - unsigned short port_change; - u16 speed; /* HSMODE or FSMODE or LSMODE */ - unsigned char rh_devnum; -}; - -static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) -{ - return inw(r8a66597->reg + offset); -} - -static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, - unsigned long offset, void *buf, - int len) -{ - int i; -#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) - unsigned long fifoaddr = r8a66597->reg + offset; - unsigned long count; - unsigned long *p = buf; - - count = len / 4; - for (i = 0; i < count; i++) - inl(p[i], r8a66597->reg + offset); - - if (len & 0x00000003) { - unsigned long tmp = inl(fifoaddr); - memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03); - } -#else - unsigned short *p = buf; - - len = (len + 1) / 2; - for (i = 0; i < len; i++) - p[i] = inw(r8a66597->reg + offset); -#endif -} - -static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, - unsigned long offset) -{ - outw(val, r8a66597->reg + offset); -} - -static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, - unsigned long offset, void *buf, - int len) -{ - int i; - unsigned long fifoaddr = r8a66597->reg + offset; -#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) - unsigned long count; - unsigned char *pb; - unsigned long *p = buf; - - count = len / 4; - for (i = 0; i < count; i++) - outl(p[i], fifoaddr); - - if (len & 0x00000003) { - pb = (unsigned char *)buf + count * 4; - for (i = 0; i < (len & 0x00000003); i++) { - if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) - outb(pb[i], fifoaddr + i); - else - outb(pb[i], fifoaddr + 3 - i); - } - } -#else - int odd = len & 0x0001; - unsigned short *p = buf; - - len = len / 2; - for (i = 0; i < len; i++) - outw(p[i], fifoaddr); - - if (odd) { - unsigned char *pb = (unsigned char *)(buf + len); - outb(*pb, fifoaddr); - } -#endif -} - -static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, - u16 val, u16 pat, unsigned long offset) -{ - u16 tmp; - tmp = r8a66597_read(r8a66597, offset); - tmp = tmp & (~pat); - tmp = tmp | val; - r8a66597_write(r8a66597, tmp, offset); -} - -#define r8a66597_bclr(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, 0, val, offset) -#define r8a66597_bset(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, val, 0, offset) - -static inline unsigned long get_syscfg_reg(int port) -{ - return port == 0 ? SYSCFG0 : SYSCFG1; -} - -static inline unsigned long get_syssts_reg(int port) -{ - return port == 0 ? SYSSTS0 : SYSSTS1; -} - -static inline unsigned long get_dvstctr_reg(int port) -{ - return port == 0 ? DVSTCTR0 : DVSTCTR1; -} - -static inline unsigned long get_dmacfg_reg(int port) -{ - return port == 0 ? DMA0CFG : DMA1CFG; -} - -static inline unsigned long get_intenb_reg(int port) -{ - return port == 0 ? INTENB1 : INTENB2; -} - -static inline unsigned long get_intsts_reg(int port) -{ - return port == 0 ? INTSTS1 : INTSTS2; -} - -static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port) -{ - unsigned long dvstctr_reg = get_dvstctr_reg(port); - - return r8a66597_read(r8a66597, dvstctr_reg) & RHST; -} - -static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, - int power) -{ - unsigned long dvstctr_reg = get_dvstctr_reg(port); - - if (power) - r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); - else - r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); -} - -#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) -#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) -#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) -#define get_devadd_addr(address) (DEVADD0 + address * 2) - - -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -#endif /* __R8A66597_H__ */ diff --git a/drivers/usb/s3c64xx_usb.c b/drivers/usb/s3c64xx_usb.c deleted file mode 100644 index 274a4ed..0000000 --- a/drivers/usb/s3c64xx_usb.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) initialization for USB on the S3C64XX. - * - * Copyright (C) 2008, - * Guennadi Liakhovetski, DENX Software Engineering - * - * 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 -#include - -int usb_cpu_init(void) -{ - OTHERS_REG |= 0x10000; - return 0; -} - -int usb_cpu_stop(void) -{ - OTHERS_REG &= ~0x10000; - return 0; -} - -void usb_cpu_init_fail(void) -{ - OTHERS_REG &= ~0x10000; -} diff --git a/drivers/usb/sl811.h b/drivers/usb/sl811.h deleted file mode 100644 index c1f9f01..0000000 --- a/drivers/usb/sl811.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __UBOOT_SL811_H -#define __UBOOT_SL811_H - -#undef SL811_DEBUG - -#ifdef SL811_DEBUG - #define PDEBUG(level, fmt, args...) \ - if (debug >= (level)) printf("[%s:%d] " fmt, \ - __PRETTY_FUNCTION__, __LINE__ , ## args) -#else - #define PDEBUG(level, fmt, args...) do {} while(0) -#endif - -/* Sl811 host control register */ -#define SL811_CTRL_A 0x00 -#define SL811_ADDR_A 0x01 -#define SL811_LEN_A 0x02 -#define SL811_STS_A 0x03 /* read */ -#define SL811_PIDEP_A 0x03 /* write */ -#define SL811_CNT_A 0x04 /* read */ -#define SL811_DEV_A 0x04 /* write */ -#define SL811_CTRL1 0x05 -#define SL811_INTR 0x06 -#define SL811_CTRL_B 0x08 -#define SL811_ADDR_B 0x09 -#define SL811_LEN_B 0x0A -#define SL811_STS_B 0x0B /* read */ -#define SL811_PIDEP_B 0x0B /* write */ -#define SL811_CNT_B 0x0C /* read */ -#define SL811_DEV_B 0x0C /* write */ -#define SL811_INTRSTS 0x0D /* write clears bitwise */ -#define SL811_HWREV 0x0E /* read */ -#define SL811_SOFLOW 0x0E /* write */ -#define SL811_SOFCNTDIV 0x0F /* read */ -#define SL811_CTRL2 0x0F /* write */ - -/* USB control register bits (addr 0x00 and addr 0x08) */ -#define SL811_USB_CTRL_ARM 0x01 -#define SL811_USB_CTRL_ENABLE 0x02 -#define SL811_USB_CTRL_DIR_OUT 0x04 -#define SL811_USB_CTRL_ISO 0x10 -#define SL811_USB_CTRL_SOF 0x20 -#define SL811_USB_CTRL_TOGGLE_1 0x40 -#define SL811_USB_CTRL_PREAMBLE 0x80 - -/* USB status register bits (addr 0x03 and addr 0x0B) */ -#define SL811_USB_STS_ACK 0x01 -#define SL811_USB_STS_ERROR 0x02 -#define SL811_USB_STS_TIMEOUT 0x04 -#define SL811_USB_STS_TOGGLE_1 0x08 -#define SL811_USB_STS_SETUP 0x10 -#define SL811_USB_STS_OVERFLOW 0x20 -#define SL811_USB_STS_NAK 0x40 -#define SL811_USB_STS_STALL 0x80 - -/* Control register 1 bits (addr 0x05) */ -#define SL811_CTRL1_SOF 0x01 -#define SL811_CTRL1_RESET 0x08 -#define SL811_CTRL1_JKSTATE 0x10 -#define SL811_CTRL1_SPEED_LOW 0x20 -#define SL811_CTRL1_SUSPEND 0x40 - -/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ -#define SL811_INTR_DONE_A 0x01 -#define SL811_INTR_DONE_B 0x02 -#define SL811_INTR_SOF 0x10 -#define SL811_INTR_INSRMV 0x20 -#define SL811_INTR_DETECT 0x40 -#define SL811_INTR_NOTPRESENT 0x40 -#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */ - -/* HW rev and SOF lo register bits (addr 0x0E) */ -#define SL811_HWR_HWREV 0xF0 - -/* SOF counter and control reg 2 (addr 0x0F) */ -#define SL811_CTL2_SOFHI 0x3F -#define SL811_CTL2_DSWAP 0x40 -#define SL811_CTL2_HOST 0x80 - -/* Set up for 1-ms SOF time. */ -#define SL811_12M_LOW 0xE0 -#define SL811_12M_HI 0x2E - -#define SL811_DATA_START 0x10 -#define SL811_DATA_LIMIT 240 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 - - -#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) - -#endif /* __UBOOT_SL811_H */ diff --git a/drivers/usb/sl811_usb.c b/drivers/usb/sl811_usb.c deleted file mode 100644 index a03e469..0000000 --- a/drivers/usb/sl811_usb.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * (C) Copyright 2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * This code is based on linux driver for sl811hs chip, source at - * drivers/usb/host/sl811.c: - * - * SL811 Host Controller Interface driver for USB. - * - * Copyright (c) 2003/06, Courage Co., Ltd. - * - * Based on: - * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, - * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, - * Adam Richter, Gregory P. Smith; - * 2.Original SL811 driver (hc_sl811.o) by Pei Liu - * 3.Rewrited as sl811.o by Yin Aihua - * - * 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 -#include -#include -#include "sl811.h" - -#include "../../board/kup/common/kup.h" - -#ifdef __PPC__ -# define EIEIO __asm__ volatile ("eieio") -#else -# define EIEIO /* nothing */ -#endif - -#define SL811_ADR (0x50000000) -#define SL811_DAT (0x50000001) - -#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) - -#ifdef SL811_DEBUG -static int debug = 9; -#endif - -static int root_hub_devnum = 0; -static struct usb_port_status rh_status = { 0 };/* root hub port status */ - -static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, - void *data, int buf_len, struct devrequest *cmd); - -static void sl811_write (__u8 index, __u8 data) -{ - *(volatile unsigned char *) (SL811_ADR) = index; - EIEIO; - *(volatile unsigned char *) (SL811_DAT) = data; - EIEIO; -} - -static __u8 sl811_read (__u8 index) -{ - __u8 data; - - *(volatile unsigned char *) (SL811_ADR) = index; - EIEIO; - data = *(volatile unsigned char *) (SL811_DAT); - EIEIO; - return (data); -} - -/* - * Read consecutive bytes of data from the SL811H/SL11H buffer - */ -static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size) -{ - *(volatile unsigned char *) (SL811_ADR) = offset; - EIEIO; - while (size--) { - *buf++ = *(volatile unsigned char *) (SL811_DAT); - EIEIO; - } -} - -/* - * Write consecutive bytes of data to the SL811H/SL11H buffer - */ -static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size) -{ - *(volatile unsigned char *) (SL811_ADR) = offset; - EIEIO; - while (size--) { - *(volatile unsigned char *) (SL811_DAT) = *buf++; - EIEIO; - } -} - -int usb_init_kup4x (void) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - int i; - unsigned char tmp; - - memctl = &immap->im_memctl; - memctl->memc_or7 = 0xFFFF8726; - memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */ - /* BP 14 low = USB ON */ - immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); - /* PB 14 nomal port */ - immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); - /* output */ - immap->im_cpm.cp_pbdir |= (BP_USB_VCC); - - puts ("USB: "); - - for (i = 0x10; i < 0xff; i++) { - sl811_write(i, i); - tmp = (sl811_read(i)); - if (tmp != i) { - printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); - return (-1); - } - } - printf ("SL811 ready\n"); - return (0); -} - -/* - * This function resets SL811HS controller and detects the speed of - * the connecting device - * - * Return: 0 = no device attached; 1 = USB device attached - */ -static int sl811_hc_reset(void) -{ - int status ; - - sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); - sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); - - mdelay(20); - - /* Disable hardware SOF generation, clear all irq status. */ - sl811_write(SL811_CTRL1, 0); - mdelay(2); - sl811_write(SL811_INTRSTS, 0xff); - status = sl811_read(SL811_INTRSTS); - - if (status & SL811_INTR_NOTPRESENT) { - /* Device is not present */ - PDEBUG(0, "Device not present\n"); - rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); - rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; - sl811_write(SL811_INTR, SL811_INTR_INSRMV); - return 0; - } - - /* Send SOF to address 0, endpoint 0. */ - sl811_write(SL811_LEN_B, 0); - sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); - sl811_write(SL811_DEV_B, 0x00); - sl811_write(SL811_SOFLOW, SL811_12M_LOW); - - if (status & SL811_INTR_SPEED_FULL) { - /* full speed device connect directly to root hub */ - PDEBUG (0, "Full speed Device attached\n"); - - sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); - mdelay(20); - sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); - sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); - - /* start the SOF or EOP */ - sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); - rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; - rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; - mdelay(2); - sl811_write(SL811_INTRSTS, 0xff); - } else { - /* slow speed device connect directly to root-hub */ - PDEBUG(0, "Low speed Device attached\n"); - - sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); - mdelay(20); - sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); - sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); - - /* start the SOF or EOP */ - sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); - rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; - mdelay(2); - sl811_write(SL811_INTRSTS, 0xff); - } - - rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; - sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); - - return 1; -} - -int usb_lowlevel_init(void) -{ - root_hub_devnum = 0; - sl811_hc_reset(); - return 0; -} - -int usb_lowlevel_stop(void) -{ - sl811_hc_reset(); - return 0; -} - -static int calc_needed_buswidth(int bytes, int need_preamble) -{ - return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048; -} - -static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len) -{ - __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; - __u16 status = 0; - int err = 0, time_start = get_timer(0); - int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && - usb_pipeslow(pipe); - - if (len > 239) - return -1; - - if (usb_pipeout(pipe)) - ctrl |= SL811_USB_CTRL_DIR_OUT; - if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) - ctrl |= SL811_USB_CTRL_TOGGLE_1; - if (need_preamble) - ctrl |= SL811_USB_CTRL_PREAMBLE; - - sl811_write(SL811_INTRSTS, 0xff); - - while (err < 3) { - sl811_write(SL811_ADDR_A, 0x10); - sl811_write(SL811_LEN_A, len); - if (usb_pipeout(pipe) && len) - sl811_write_buf(0x10, buffer, len); - - if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && - sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) - ctrl |= SL811_USB_CTRL_SOF; - else - ctrl &= ~SL811_USB_CTRL_SOF; - - sl811_write(SL811_CTRL_A, ctrl); - while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { - if (5*CONFIG_SYS_HZ < get_timer(time_start)) { - printf("USB transmit timed out\n"); - return -USB_ST_CRC_ERR; - } - } - - sl811_write(SL811_INTRSTS, 0xff); - status = sl811_read(SL811_STS_A); - - if (status & SL811_USB_STS_ACK) { - int remainder = sl811_read(SL811_CNT_A); - if (remainder) { - PDEBUG(0, "usb transfer remainder = %d\n", remainder); - len -= remainder; - } - if (usb_pipein(pipe) && len) - sl811_read_buf(0x10, buffer, len); - return len; - } - - if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) - continue; - - PDEBUG(0, "usb transfer error %#x\n", (int)status); - err++; - } - - err = 0; - - if (status & SL811_USB_STS_ERROR) - err |= USB_ST_BUF_ERR; - if (status & SL811_USB_STS_TIMEOUT) - err |= USB_ST_CRC_ERR; - if (status & SL811_USB_STS_STALL) - err |= USB_ST_STALLED; - - return -err; -} - -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len) -{ - int dir_out = usb_pipeout(pipe); - int ep = usb_pipeendpoint(pipe); - int max = usb_maxpacket(dev, pipe); - int done = 0; - - PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", - usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); - - dev->status = 0; - - sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); - sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); - while (done < len) { - int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, - max > len - done ? len - done : max); - if (res < 0) { - dev->status = -res; - return res; - } - - if (!dir_out && res < max) /* short packet */ - break; - - done += res; - usb_dotoggle(dev, ep, dir_out); - } - - dev->act_len = done; - - return 0; -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len,struct devrequest *setup) -{ - int done = 0; - int devnum = usb_pipedevice(pipe); - int ep = usb_pipeendpoint(pipe); - - dev->status = 0; - - if (devnum == root_hub_devnum) - return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); - - PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n", - devnum, ep, buffer, len, (int)setup->requesttype, - (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64); - - sl811_write(SL811_DEV_A, devnum); - sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep)); - /* setup phase */ - usb_settoggle(dev, ep, 1, 0); - if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep), - (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { - int dir_in = usb_pipein(pipe); - int max = usb_maxpacket(dev, pipe); - - /* data phase */ - sl811_write(SL811_PIDEP_A, - PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep)); - usb_settoggle(dev, ep, usb_pipeout(pipe), 1); - while (done < len) { - int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, - max > len - done ? len - done : max); - if (res < 0) { - PDEBUG(0, "status data failed!\n"); - dev->status = -res; - return 0; - } - done += res; - usb_dotoggle(dev, ep, usb_pipeout(pipe)); - if (dir_in && res < max) /* short packet */ - break; - } - - /* status phase */ - sl811_write(SL811_PIDEP_A, - PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep)); - usb_settoggle(dev, ep, !usb_pipeout(pipe), 1); - if (sl811_send_packet(dev, - !dir_in ? usb_rcvctrlpipe(dev, ep) : - usb_sndctrlpipe(dev, ep), - 0, 0) < 0) { - PDEBUG(0, "status phase failed!\n"); - dev->status = -1; - } - } else { - PDEBUG(0, "setup phase failed!\n"); - dev->status = -1; - } - - dev->act_len = done; - - return done; -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, int interval) -{ - PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe, - buffer, len, interval); - return -1; -} - -/* - * SL811 Virtual Root Hub - */ - -/* Device descriptor */ -static __u8 sl811_rh_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 sl811_rh_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, - 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __u16 ep_wMaxPacketSize; */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* root hub class descriptor*/ -static __u8 sl811_rh_hub_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x29, /* __u8 bDescriptorType; Hub-descriptor */ - 0x01, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, - 0x50, /* __u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* __u8 bHubContrCurrent; 0 mA */ - 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ -}; - -/* - * helper routine for returning string descriptors in UTF-16LE - * input can actually be ISO-8859-1; ASCII is its 7-bit subset - */ -static int ascii2utf (char *s, u8 *utf, int utfmax) -{ - int retval; - - for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *s++; - *utf++ = 0; - } - return retval; -} - -/* - * root_hub_string is used by each host controller's root hub code, - * so that they're identified consistently throughout the system. - */ -static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) -{ - char buf [30]; - - /* assert (len > (2 * (sizeof (buf) + 1))); - assert (strlen (type) <= 8);*/ - - /* language ids */ - if (id == 0) { - *data++ = 4; *data++ = 3; /* 4 bytes data */ - *data++ = 0; *data++ = 0; /* some language id */ - return 4; - - /* serial number */ - } else if (id == 1) { - sprintf (buf, "%#x", serial); - - /* product description */ - } else if (id == 2) { - sprintf (buf, "USB %s Root Hub", type); - - /* id 3 == vendor description */ - - /* unsupported IDs --> "stall" */ - } else - return 0; - - ascii2utf (buf, data + 2, len - 2); - data [0] = 2 + strlen(buf) * 2; - data [1] = 3; - return data [0]; -} - -/* helper macro */ -#define OK(x) len = (x); break - -/* - * This function handles all USB request to the the virtual root hub - */ -static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, - void *data, int buf_len, struct devrequest *cmd) -{ - __u8 data_buf[16]; - __u8 *bufp = data_buf; - int len = 0; - int status = 0; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - if (usb_pipeint(pipe)) { - PDEBUG(0, "interrupt transfer unimplemented!\n"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = le16_to_cpu (cmd->value); - wIndex = le16_to_cpu (cmd->index); - wLength = le16_to_cpu (cmd->length); - - PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n", - bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength); - - /* Request Destination: - without flags: Device, - USB_RECIP_INTERFACE: interface, - USB_RECIP_ENDPOINT: endpoint, - USB_TYPE_CLASS means HUB here, - USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here - */ - switch (bmRType_bReq) { - case RH_GET_STATUS: - *(__u16 *)bufp = cpu_to_le16(1); - OK(2); - - case RH_GET_STATUS | USB_RECIP_INTERFACE: - *(__u16 *)bufp = cpu_to_le16(0); - OK(2); - - case RH_GET_STATUS | USB_RECIP_ENDPOINT: - *(__u16 *)bufp = cpu_to_le16(0); - OK(2); - - case RH_GET_STATUS | USB_TYPE_CLASS: - *(__u32 *)bufp = cpu_to_le32(0); - OK(4); - - case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: - *(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus); - OK(4); - - case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: - switch (wValue) { - case 1: - OK(0); - } - break; - - case RH_CLEAR_FEATURE | USB_TYPE_CLASS: - switch (wValue) { - case C_HUB_LOCAL_POWER: - OK(0); - - case C_HUB_OVER_CURRENT: - OK(0); - } - break; - - case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; - OK(0); - - case USB_PORT_FEAT_SUSPEND: - rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; - OK(0); - - case USB_PORT_FEAT_POWER: - rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; - OK(0); - - case USB_PORT_FEAT_C_CONNECTION: - rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; - OK(0); - - case USB_PORT_FEAT_C_ENABLE: - rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; - OK(0); - - case USB_PORT_FEAT_C_SUSPEND: - rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; - OK(0); - - case USB_PORT_FEAT_C_OVER_CURRENT: - rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; - OK(0); - - case USB_PORT_FEAT_C_RESET: - rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; - OK(0); - } - break; - - case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; - OK(0); - - case USB_PORT_FEAT_RESET: - rh_status.wPortStatus |= USB_PORT_STAT_RESET; - rh_status.wPortChange = 0; - rh_status.wPortChange |= USB_PORT_STAT_C_RESET; - rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; - rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; - OK(0); - - case USB_PORT_FEAT_POWER: - rh_status.wPortStatus |= USB_PORT_STAT_POWER; - OK(0); - - case USB_PORT_FEAT_ENABLE: - rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; - OK(0); - } - break; - - case RH_SET_ADDRESS: - root_hub_devnum = wValue; - OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case USB_DT_DEVICE: - len = sizeof(sl811_rh_dev_des); - bufp = sl811_rh_dev_des; - OK(len); - - case USB_DT_CONFIG: - len = sizeof(sl811_rh_config_des); - bufp = sl811_rh_config_des; - OK(len); - - case USB_DT_STRING: - len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength); - if (len > 0) { - bufp = data; - OK(len); - } - - default: - status = -32; - } - break; - - case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: - len = sizeof(sl811_rh_hub_des); - bufp = sl811_rh_hub_des; - OK(len); - - case RH_GET_CONFIGURATION: - bufp[0] = 0x01; - OK(1); - - case RH_SET_CONFIGURATION: - OK(0); - - default: - PDEBUG(1, "unsupported root hub command\n"); - status = -32; - } - - len = min(len, buf_len); - if (data != bufp) - memcpy(data, bufp, len); - - PDEBUG(5, "len = %d, status = %d\n", len, status); - - usb_dev->status = status; - usb_dev->act_len = len; - - return status == 0 ? len : status; -} diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h deleted file mode 100644 index b3c1d5d..0000000 --- a/drivers/usb/usb_ehci.h +++ /dev/null @@ -1,194 +0,0 @@ -/*- - * Copyright (c) 2007-2008, Juniper Networks, Inc. - * Copyright (c) 2008, Michael Trimarchi - * All rights reserved. - * - * 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 version 2 of - * the License. - * - * 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 USB_EHCI_H -#define USB_EHCI_H - -#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) -#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 -#endif - -/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ -#define DeviceRequest \ - ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) - -#define DeviceOutRequest \ - ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) - -#define InterfaceRequest \ - ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) - -#define EndpointRequest \ - ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) - -#define EndpointOutRequest \ - ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) - -/* - * Register Space. - */ -struct ehci_hccr { - uint32_t cr_capbase; -#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) -#define HC_VERSION(p) (((p) >> 16) & 0xffff) - uint32_t cr_hcsparams; -#define HCS_PPC(p) ((p) & (1 << 4)) -#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ -#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) - uint32_t cr_hccparams; - uint8_t cr_hcsp_portrt[8]; -} __attribute__ ((packed)); - -struct ehci_hcor { - uint32_t or_usbcmd; -#define CMD_PARK (1 << 11) /* enable "park" */ -#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ -#define CMD_ASE (1 << 5) /* async schedule enable */ -#define CMD_LRESET (1 << 7) /* partial reset */ -#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ -#define CMD_PSE (1 << 4) /* periodic schedule enable */ -#define CMD_RESET (1 << 1) /* reset HC not bus */ -#define CMD_RUN (1 << 0) /* start/stop HC */ - uint32_t or_usbsts; -#define STD_ASS (1 << 15) -#define STS_HALT (1 << 12) - uint32_t or_usbintr; - uint32_t or_frindex; - uint32_t or_ctrldssegment; - uint32_t or_periodiclistbase; - uint32_t or_asynclistaddr; - uint32_t _reserved_[9]; - uint32_t or_configflag; -#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ - uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; - uint32_t or_systune; -} __attribute__ ((packed)); - -#define USBMODE 0x68 /* USB Device mode */ -#define USBMODE_SDIS (1 << 3) /* Stream disable */ -#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ -#define USBMODE_CM_HC (3 << 0) /* host controller mode */ -#define USBMODE_CM_IDLE (0 << 0) /* idle state */ - -/* Interface descriptor */ -struct usb_linux_interface_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bInterfaceNumber; - unsigned char bAlternateSetting; - unsigned char bNumEndpoints; - unsigned char bInterfaceClass; - unsigned char bInterfaceSubClass; - unsigned char bInterfaceProtocol; - unsigned char iInterface; -} __attribute__ ((packed)); - -/* Configuration descriptor information.. */ -struct usb_linux_config_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wTotalLength; - unsigned char bNumInterfaces; - unsigned char bConfigurationValue; - unsigned char iConfiguration; - unsigned char bmAttributes; - unsigned char MaxPower; -} __attribute__ ((packed)); - -#if defined CONFIG_EHCI_DESC_BIG_ENDIAN -#define ehci_readl(x) (*((volatile u32 *)(x))) -#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) -#else -#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) -#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ - cpu_to_le32(((volatile u32)b))) -#endif - -#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN -#define hc32_to_cpu(x) be32_to_cpu((x)) -#define cpu_to_hc32(x) cpu_to_be32((x)) -#else -#define hc32_to_cpu(x) le32_to_cpu((x)) -#define cpu_to_hc32(x) cpu_to_le32((x)) -#endif - -#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */ -#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ -#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ -#define EHCI_PS_PO (1 << 13) /* RW port owner */ -#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ -#define EHCI_PS_LS (3 << 10) /* RO line status */ -#define EHCI_PS_PR (1 << 8) /* RW port reset */ -#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ -#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ -#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ -#define EHCI_PS_OCA (1 << 4) /* RO over current active */ -#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ -#define EHCI_PS_PE (1 << 2) /* RW port enable */ -#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ -#define EHCI_PS_CS (1 << 0) /* RO connect status */ -#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) - -#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) - -/* - * Schedule Interface Space. - * - * IMPORTANT: Software must ensure that no interface data structure - * reachable by the EHCI host controller spans a 4K page boundary! - * - * Periodic transfers (i.e. isochronous and interrupt transfers) are - * not supported. - */ - -/* Queue Element Transfer Descriptor (qTD). */ -struct qTD { - uint32_t qt_next; -#define QT_NEXT_TERMINATE 1 - uint32_t qt_altnext; - uint32_t qt_token; - uint32_t qt_buffer[5]; -}; - -/* Queue Head (QH). */ -struct QH { - uint32_t qh_link; -#define QH_LINK_TERMINATE 1 -#define QH_LINK_TYPE_ITD 0 -#define QH_LINK_TYPE_QH 2 -#define QH_LINK_TYPE_SITD 4 -#define QH_LINK_TYPE_FSTN 6 - uint32_t qh_endpt1; - uint32_t qh_endpt2; - uint32_t qh_curtd; - struct qTD qh_overlay; - /* - * Add dummy fill value to make the size of this struct - * aligned to 32 bytes - */ - uint8_t fill[16]; -}; - -/* Low level init functions */ -int ehci_hcd_init(void); -int ehci_hcd_stop(void); - -#endif /* USB_EHCI_H */ diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c deleted file mode 100644 index 4dbfb66..0000000 --- a/drivers/usb/usb_ehci_core.c +++ /dev/null @@ -1,882 +0,0 @@ -/*- - * Copyright (c) 2007-2008, Juniper Networks, Inc. - * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB - * Copyright (c) 2008, Michael Trimarchi - * - * All rights reserved. - * - * 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 version 2 of - * the License. - * - * 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 -#include -#include -#include -#include -#include "usb_ehci.h" - -int rootdev; -struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ -volatile struct ehci_hcor *hcor; - -static uint16_t portreset; -static struct QH qh_list __attribute__((aligned(32))); - -static struct descriptor { - struct usb_hub_descriptor hub; - struct usb_device_descriptor device; - struct usb_linux_config_descriptor config; - struct usb_linux_interface_descriptor interface; - struct usb_endpoint_descriptor endpoint; -} __attribute__ ((packed)) descriptor = { - { - 0x8, /* bDescLength */ - 0x29, /* bDescriptorType: hub descriptor */ - 2, /* bNrPorts -- runtime modified */ - 0, /* wHubCharacteristics */ - 0xff, /* bPwrOn2PwrGood */ - 0, /* bHubCntrCurrent */ - {}, /* Device removable */ - {} /* at most 7 ports! XXX */ - }, - { - 0x12, /* bLength */ - 1, /* bDescriptorType: UDESC_DEVICE */ - 0x0002, /* bcdUSB: v2.0 */ - 9, /* bDeviceClass: UDCLASS_HUB */ - 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ - 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ - 64, /* bMaxPacketSize: 64 bytes */ - 0x0000, /* idVendor */ - 0x0000, /* idProduct */ - 0x0001, /* bcdDevice */ - 1, /* iManufacturer */ - 2, /* iProduct */ - 0, /* iSerialNumber */ - 1 /* bNumConfigurations: 1 */ - }, - { - 0x9, - 2, /* bDescriptorType: UDESC_CONFIG */ - cpu_to_le16(0x19), - 1, /* bNumInterface */ - 1, /* bConfigurationValue */ - 0, /* iConfiguration */ - 0x40, /* bmAttributes: UC_SELF_POWER */ - 0 /* bMaxPower */ - }, - { - 0x9, /* bLength */ - 4, /* bDescriptorType: UDESC_INTERFACE */ - 0, /* bInterfaceNumber */ - 0, /* bAlternateSetting */ - 1, /* bNumEndpoints */ - 9, /* bInterfaceClass: UICLASS_HUB */ - 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ - 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ - 0 /* iInterface */ - }, - { - 0x7, /* bLength */ - 5, /* bDescriptorType: UDESC_ENDPOINT */ - 0x81, /* bEndpointAddress: - * UE_DIR_IN | EHCI_INTR_ENDPT - */ - 3, /* bmAttributes: UE_INTERRUPT */ - 8, 0, /* wMaxPacketSize */ - 255 /* bInterval */ - }, -}; - -#if defined(CONFIG_EHCI_IS_TDI) -#define ehci_is_TDI() (1) -#else -#define ehci_is_TDI() (0) -#endif - -#if defined(CONFIG_EHCI_DCACHE) -/* - * Routines to handle (flush/invalidate) the dcache for the QH and qTD - * structures and data buffers. This is needed on platforms using this - * EHCI support with dcache enabled. - */ -static void flush_invalidate(u32 addr, int size, int flush) -{ - if (flush) - flush_dcache_range(addr, addr + size); - else - invalidate_dcache_range(addr, addr + size); -} - -static void cache_qtd(struct qTD *qtd, int flush) -{ - u32 *ptr = (u32 *)qtd->qt_buffer[0]; - int len = (qtd->qt_token & 0x7fff0000) >> 16; - - flush_invalidate((u32)qtd, sizeof(struct qTD), flush); - if (ptr && len) - flush_invalidate((u32)ptr, len, flush); -} - - -static inline struct QH *qh_addr(struct QH *qh) -{ - return (struct QH *)((u32)qh & 0xffffffe0); -} - -static void cache_qh(struct QH *qh, int flush) -{ - struct qTD *qtd; - struct qTD *next; - static struct qTD *first_qtd; - - /* - * Walk the QH list and flush/invalidate all entries - */ - while (1) { - flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush); - if ((u32)qh & QH_LINK_TYPE_QH) - break; - qh = qh_addr(qh); - qh = (struct QH *)qh->qh_link; - } - qh = qh_addr(qh); - - /* - * Save first qTD pointer, needed for invalidating pass on this QH - */ - if (flush) - first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay & - 0xffffffe0); - else - qtd = first_qtd; - - /* - * Walk the qTD list and flush/invalidate all entries - */ - while (1) { - if (qtd == NULL) - break; - cache_qtd(qtd, flush); - next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0); - if (next == qtd) - break; - qtd = next; - } -} - -static inline void ehci_flush_dcache(struct QH *qh) -{ - cache_qh(qh, 1); -} - -static inline void ehci_invalidate_dcache(struct QH *qh) -{ - cache_qh(qh, 0); -} -#else /* CONFIG_EHCI_DCACHE */ -/* - * - */ -static inline void ehci_flush_dcache(struct QH *qh) -{ -} - -static inline void ehci_invalidate_dcache(struct QH *qh) -{ -} -#endif /* CONFIG_EHCI_DCACHE */ - -static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) -{ - uint32_t result; - do { - result = ehci_readl(ptr); - if (result == ~(uint32_t)0) - return -1; - result &= mask; - if (result == done) - return 0; - udelay(1); - usec--; - } while (usec > 0); - return -1; -} - -static void ehci_free(void *p, size_t sz) -{ - -} - -static int ehci_reset(void) -{ - uint32_t cmd; - uint32_t tmp; - uint32_t *reg_ptr; - int ret = 0; - - cmd = ehci_readl(&hcor->or_usbcmd); - cmd |= CMD_RESET; - ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); - if (ret < 0) { - printf("EHCI fail to reset\n"); - goto out; - } - - if (ehci_is_TDI()) { - reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); - tmp = ehci_readl(reg_ptr); - tmp |= USBMODE_CM_HC; -#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) - tmp |= USBMODE_BE; -#endif - ehci_writel(reg_ptr, tmp); - } -out: - return ret; -} - -static void *ehci_alloc(size_t sz, size_t align) -{ - static struct QH qh __attribute__((aligned(32))); - static struct qTD td[3] __attribute__((aligned (32))); - static int ntds; - void *p; - - switch (sz) { - case sizeof(struct QH): - p = &qh; - ntds = 0; - break; - case sizeof(struct qTD): - if (ntds == 3) { - debug("out of TDs\n"); - return NULL; - } - p = &td[ntds]; - ntds++; - break; - default: - debug("unknown allocation size\n"); - return NULL; - } - - memset(p, sz, 0); - return p; -} - -static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) -{ - uint32_t addr, delta, next; - int idx; - - addr = (uint32_t) buf; - idx = 0; - while (idx < 5) { - td->qt_buffer[idx] = cpu_to_hc32(addr); - next = (addr + 4096) & ~4095; - delta = next - addr; - if (delta >= sz) - break; - sz -= delta; - addr = next; - idx++; - } - - if (idx == 5) { - debug("out of buffer pointers (%u bytes left)\n", sz); - return -1; - } - - return 0; -} - -static int -ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, struct devrequest *req) -{ - struct QH *qh; - struct qTD *td; - volatile struct qTD *vtd; - unsigned long ts; - uint32_t *tdp; - uint32_t endpt, token, usbsts; - uint32_t c, toggle; - uint32_t cmd; - int ret = 0; - - debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, - buffer, length, req); - if (req != NULL) - debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", - req->request, req->request, - req->requesttype, req->requesttype, - le16_to_cpu(req->value), le16_to_cpu(req->value), - le16_to_cpu(req->index)); - - qh = ehci_alloc(sizeof(struct QH), 32); - if (qh == NULL) { - debug("unable to allocate QH\n"); - return -1; - } - qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && - usb_pipeendpoint(pipe) == 0) ? 1 : 0; - endpt = (8 << 28) | - (c << 27) | - (usb_maxpacket(dev, pipe) << 16) | - (0 << 15) | - (1 << 14) | - (usb_pipespeed(pipe) << 12) | - (usb_pipeendpoint(pipe) << 8) | - (0 << 7) | (usb_pipedevice(pipe) << 0); - qh->qh_endpt1 = cpu_to_hc32(endpt); - endpt = (1 << 30) | - (dev->portnr << 23) | - (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); - qh->qh_endpt2 = cpu_to_hc32(endpt); - qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - - td = NULL; - tdp = &qh->qh_overlay.qt_next; - - toggle = - usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); - - if (req != NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); - if (td == NULL) { - debug("unable to allocate SETUP td\n"); - goto fail; - } - td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - token = (0 << 31) | - (sizeof(*req) << 16) | - (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); - td->qt_token = cpu_to_hc32(token); - if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { - debug("unable construct SETUP td\n"); - ehci_free(td, sizeof(*td)); - goto fail; - } - *tdp = cpu_to_hc32((uint32_t) td); - tdp = &td->qt_next; - toggle = 1; - } - - if (length > 0 || req == NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); - if (td == NULL) { - debug("unable to allocate DATA td\n"); - goto fail; - } - td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - token = (toggle << 31) | - (length << 16) | - ((req == NULL ? 1 : 0) << 15) | - (0 << 12) | - (3 << 10) | - ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); - td->qt_token = cpu_to_hc32(token); - if (ehci_td_buffer(td, buffer, length) != 0) { - debug("unable construct DATA td\n"); - ehci_free(td, sizeof(*td)); - goto fail; - } - *tdp = cpu_to_hc32((uint32_t) td); - tdp = &td->qt_next; - } - - if (req != NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); - if (td == NULL) { - debug("unable to allocate ACK td\n"); - goto fail; - } - td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - token = (toggle << 31) | - (0 << 16) | - (1 << 15) | - (0 << 12) | - (3 << 10) | - ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); - td->qt_token = cpu_to_hc32(token); - *tdp = cpu_to_hc32((uint32_t) td); - tdp = &td->qt_next; - } - - qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); - - /* Flush dcache */ - ehci_flush_dcache(&qh_list); - - usbsts = ehci_readl(&hcor->or_usbsts); - ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); - - /* Enable async. schedule. */ - cmd = ehci_readl(&hcor->or_usbcmd); - cmd |= CMD_ASE; - ehci_writel(&hcor->or_usbcmd, cmd); - - ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, - 100 * 1000); - if (ret < 0) { - printf("EHCI fail timeout STD_ASS set\n"); - goto fail; - } - - /* Wait for TDs to be processed. */ - ts = get_timer(0); - vtd = td; - do { - /* Invalidate dcache */ - ehci_invalidate_dcache(&qh_list); - token = hc32_to_cpu(vtd->qt_token); - if (!(token & 0x80)) - break; - } while (get_timer(ts) < CONFIG_SYS_HZ); - - /* Disable async schedule. */ - cmd = ehci_readl(&hcor->or_usbcmd); - cmd &= ~CMD_ASE; - ehci_writel(&hcor->or_usbcmd, cmd); - - ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, - 100 * 1000); - if (ret < 0) { - printf("EHCI fail timeout STD_ASS reset\n"); - goto fail; - } - - qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - - token = hc32_to_cpu(qh->qh_overlay.qt_token); - if (!(token & 0x80)) { - debug("TOKEN=%#x\n", token); - switch (token & 0xfc) { - case 0: - toggle = token >> 31; - usb_settoggle(dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe), toggle); - dev->status = 0; - break; - case 0x40: - dev->status = USB_ST_STALLED; - break; - case 0xa0: - case 0x20: - dev->status = USB_ST_BUF_ERR; - break; - case 0x50: - case 0x10: - dev->status = USB_ST_BABBLE_DET; - break; - default: - dev->status = USB_ST_CRC_ERR; - break; - } - dev->act_len = length - ((token >> 16) & 0x7fff); - } else { - dev->act_len = 0; - debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", - dev->devnum, ehci_readl(&hcor->or_usbsts), - ehci_readl(&hcor->or_portsc[0]), - ehci_readl(&hcor->or_portsc[1])); - } - - return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; - -fail: - td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); - while (td != (void *)QT_NEXT_TERMINATE) { - qh->qh_overlay.qt_next = td->qt_next; - ehci_free(td, sizeof(*td)); - td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); - } - ehci_free(qh, sizeof(*qh)); - return -1; -} - -static inline int min3(int a, int b, int c) -{ - - if (b < a) - a = b; - if (c < a) - a = c; - return a; -} - -int -ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, struct devrequest *req) -{ - uint8_t tmpbuf[4]; - u16 typeReq; - void *srcptr = NULL; - int len, srclen; - uint32_t reg; - uint32_t *status_reg; - - if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { - printf("The request port(%d) is not configured\n", - le16_to_cpu(req->index) - 1); - return -1; - } - status_reg = (uint32_t *)&hcor->or_portsc[ - le16_to_cpu(req->index) - 1]; - srclen = 0; - - debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", - req->request, req->request, - req->requesttype, req->requesttype, - le16_to_cpu(req->value), le16_to_cpu(req->index)); - - typeReq = req->request << 8 | req->requesttype; - - switch (le16_to_cpu(typeReq)) { - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch (le16_to_cpu(req->value) >> 8) { - case USB_DT_DEVICE: - debug("USB_DT_DEVICE request\n"); - srcptr = &descriptor.device; - srclen = 0x12; - break; - case USB_DT_CONFIG: - debug("USB_DT_CONFIG config\n"); - srcptr = &descriptor.config; - srclen = 0x19; - break; - case USB_DT_STRING: - debug("USB_DT_STRING config\n"); - switch (le16_to_cpu(req->value) & 0xff) { - case 0: /* Language */ - srcptr = "\4\3\1\0"; - srclen = 4; - break; - case 1: /* Vendor */ - srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; - srclen = 14; - break; - case 2: /* Product */ - srcptr = "\52\3E\0H\0C\0I\0 " - "\0H\0o\0s\0t\0 " - "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; - srclen = 42; - break; - default: - debug("unknown value DT_STRING %x\n", - le16_to_cpu(req->value)); - goto unknown; - } - break; - default: - debug("unknown value %x\n", le16_to_cpu(req->value)); - goto unknown; - } - break; - case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): - switch (le16_to_cpu(req->value) >> 8) { - case USB_DT_HUB: - debug("USB_DT_HUB config\n"); - srcptr = &descriptor.hub; - srclen = 0x8; - break; - default: - debug("unknown value %x\n", le16_to_cpu(req->value)); - goto unknown; - } - break; - case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): - debug("USB_REQ_SET_ADDRESS\n"); - rootdev = le16_to_cpu(req->value); - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - debug("USB_REQ_SET_CONFIGURATION\n"); - /* Nothing to do */ - break; - case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): - tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ - tmpbuf[1] = 0; - srcptr = tmpbuf; - srclen = 2; - break; - case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): - memset(tmpbuf, 0, 4); - reg = ehci_readl(status_reg); - if (reg & EHCI_PS_CS) - tmpbuf[0] |= USB_PORT_STAT_CONNECTION; - if (reg & EHCI_PS_PE) - tmpbuf[0] |= USB_PORT_STAT_ENABLE; - if (reg & EHCI_PS_SUSP) - tmpbuf[0] |= USB_PORT_STAT_SUSPEND; - if (reg & EHCI_PS_OCA) - tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; - if (reg & EHCI_PS_PR && - (portreset & (1 << le16_to_cpu(req->index)))) { - int ret; - /* force reset to complete */ - reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); - ehci_writel(status_reg, reg); - ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); - if (!ret) - tmpbuf[0] |= USB_PORT_STAT_RESET; - else - printf("port(%d) reset error\n", - le16_to_cpu(req->index) - 1); - } - if (reg & EHCI_PS_PP) - tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; - - if (ehci_is_TDI()) { - switch ((reg >> 26) & 3) { - case 0: - break; - case 1: - tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; - break; - case 2: - default: - tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; - break; - } - } else { - tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; - } - - if (reg & EHCI_PS_CSC) - tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; - if (reg & EHCI_PS_PEC) - tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; - if (reg & EHCI_PS_OCC) - tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; - if (portreset & (1 << le16_to_cpu(req->index))) - tmpbuf[2] |= USB_PORT_STAT_C_RESET; - - srcptr = tmpbuf; - srclen = 4; - break; - case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(status_reg); - reg &= ~EHCI_PS_CLEAR; - switch (le16_to_cpu(req->value)) { - case USB_PORT_FEAT_ENABLE: - reg |= EHCI_PS_PE; - ehci_writel(status_reg, reg); - break; - case USB_PORT_FEAT_POWER: - if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) { - reg |= EHCI_PS_PP; - ehci_writel(status_reg, reg); - } - break; - case USB_PORT_FEAT_RESET: - if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && - !ehci_is_TDI() && - EHCI_PS_IS_LOWSPEED(reg)) { - /* Low speed device, give up ownership. */ - debug("port %d low speed --> companion\n", - req->index - 1); - reg |= EHCI_PS_PO; - ehci_writel(status_reg, reg); - break; - } else { - reg |= EHCI_PS_PR; - reg &= ~EHCI_PS_PE; - ehci_writel(status_reg, reg); - /* - * caller must wait, then call GetPortStatus - * usb 2.0 specification say 50 ms resets on - * root - */ - wait_ms(50); - portreset |= 1 << le16_to_cpu(req->index); - } - break; - default: - debug("unknown feature %x\n", le16_to_cpu(req->value)); - goto unknown; - } - /* unblock posted writes */ - ehci_readl(&hcor->or_usbcmd); - break; - case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(status_reg); - switch (le16_to_cpu(req->value)) { - case USB_PORT_FEAT_ENABLE: - reg &= ~EHCI_PS_PE; - break; - case USB_PORT_FEAT_C_ENABLE: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; - break; - case USB_PORT_FEAT_POWER: - if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) - reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); - case USB_PORT_FEAT_C_CONNECTION: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; - break; - case USB_PORT_FEAT_OVER_CURRENT: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; - break; - case USB_PORT_FEAT_C_RESET: - portreset &= ~(1 << le16_to_cpu(req->index)); - break; - default: - debug("unknown feature %x\n", le16_to_cpu(req->value)); - goto unknown; - } - ehci_writel(status_reg, reg); - /* unblock posted write */ - ehci_readl(&hcor->or_usbcmd); - break; - default: - debug("Unknown request\n"); - goto unknown; - } - - wait_ms(1); - len = min3(srclen, le16_to_cpu(req->length), length); - if (srcptr != NULL && len > 0) - memcpy(buffer, srcptr, len); - else - debug("Len is 0\n"); - - dev->act_len = len; - dev->status = 0; - return 0; - -unknown: - debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n", - req->requesttype, req->request, le16_to_cpu(req->value), - le16_to_cpu(req->index), le16_to_cpu(req->length)); - - dev->act_len = 0; - dev->status = USB_ST_STALLED; - return -1; -} - -int usb_lowlevel_stop(void) -{ - return ehci_hcd_stop(); -} - -int usb_lowlevel_init(void) -{ - uint32_t reg; - uint32_t cmd; - - if (ehci_hcd_init() != 0) - return -1; - - /* EHCI spec section 4.1 */ - if (ehci_reset() != 0) - return -1; - -#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) - if (ehci_hcd_init() != 0) - return -1; -#endif - - /* Set head of reclaim list */ - memset(&qh_list, 0, sizeof(qh_list)); - qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); - qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); - - /* Set async. queue head pointer. */ - ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); - - reg = ehci_readl(&hccr->cr_hcsparams); - descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); - printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); - /* Port Indicators */ - if (HCS_INDICATOR(reg)) - descriptor.hub.wHubCharacteristics |= 0x80; - /* Port Power Control */ - if (HCS_PPC(reg)) - descriptor.hub.wHubCharacteristics |= 0x01; - - /* Start the host controller. */ - cmd = ehci_readl(&hcor->or_usbcmd); - /* - * Philips, Intel, and maybe others need CMD_RUN before the - * root hub will detect new devices (why?); NEC doesn't - */ - cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); - cmd |= CMD_RUN; - ehci_writel(&hcor->or_usbcmd, cmd); - - /* take control over the ports */ - cmd = ehci_readl(&hcor->or_configflag); - cmd |= FLAG_CF; - ehci_writel(&hcor->or_configflag, cmd); - /* unblock posted write */ - cmd = ehci_readl(&hcor->or_usbcmd); - wait_ms(5); - reg = HC_VERSION(ehci_readl(&hccr->cr_capbase)); - printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); - - rootdev = 0; - - return 0; -} - -int -submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int length) -{ - - if (usb_pipetype(pipe) != PIPE_BULK) { - debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); - return -1; - } - return ehci_submit_async(dev, pipe, buffer, length, NULL); -} - -int -submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, struct devrequest *setup) -{ - - if (usb_pipetype(pipe) != PIPE_CONTROL) { - debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); - return -1; - } - - if (usb_pipedevice(pipe) == rootdev) { - if (rootdev == 0) - dev->speed = USB_SPEED_HIGH; - return ehci_submit_root(dev, pipe, buffer, length, setup); - } - return ehci_submit_async(dev, pipe, buffer, length, setup); -} - -int -submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, int interval) -{ - - debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", - dev, pipe, buffer, length, interval); - return -1; -} diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/usb_ehci_core.h deleted file mode 100644 index 39e5c5e..0000000 --- a/drivers/usb/usb_ehci_core.h +++ /dev/null @@ -1,29 +0,0 @@ -/*- - * Copyright (c) 2007-2008, Juniper Networks, Inc. - * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB - * All rights reserved. - * - * 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 version 2 of - * the License. - * - * 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 USB_EHCI_CORE_H -#define USB_EHCI_CORE_H - -extern int rootdev; -extern struct ehci_hccr *hccr; -extern volatile struct ehci_hcor *hcor; - -#endif diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c deleted file mode 100644 index 81d5d21..0000000 --- a/drivers/usb/usb_ehci_fsl.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB - * - * Author: Tor Krill tor@excito.com - * - * 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 -#include -#include -#include -#include -#include - -#include "usb_ehci.h" -#include "usb_ehci_fsl.h" -#include "usb_ehci_core.h" - -/* - * Create the appropriate control structures to manage - * a new EHCI host controller. - * - * Excerpts from linux ehci fsl driver. - */ -int ehci_hcd_init(void) -{ - volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; - uint32_t addr, temp; - - addr = (uint32_t)&(im->usb[0]); - hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); - hcor = (struct ehci_hcor *)((uint32_t) hccr + - HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - - /* Configure clock */ - clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, - MPC83XX_SCCR_USB_DRCM_11); - - /* Confgure interface. */ - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp - | REFSEL_16MHZ | UTMI_PHY_EN); - - /* Wait for clock to stabilize */ - do { - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - udelay(1000); - } while (!(temp & PHY_CLK_VALID)); - - /* Set to Host mode */ - temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); - out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST); - - out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB); - out_be32((void *)(addr + FSL_SOC_USB_SNOOP2), - 0x80000000 | SNOOP_SIZE_2GB); - - /* Init phy */ - /* TODO: handle different phys? */ - out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI); - - /* Enable interface. */ - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); - - out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c); - out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040); - out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001); - - /* Enable interface. */ - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); - - temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); - - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the the EHCI host controller. - */ -int ehci_hcd_stop(void) -{ - return 0; -} diff --git a/drivers/usb/usb_ehci_fsl.h b/drivers/usb/usb_ehci_fsl.h deleted file mode 100644 index c429af1..0000000 --- a/drivers/usb/usb_ehci_fsl.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2005 freescale semiconductor - * Copyright (c) 2005 MontaVista Software - * Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB - * - * 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 _EHCI_FSL_H -#define _EHCI_FSL_H - -/* Global offsets */ -#define FSL_SKIP_PCI 0x100 - -/* offsets for the non-ehci registers in the FSL SOC USB controller */ -#define FSL_SOC_USB_ULPIVP 0x170 -#define FSL_SOC_USB_PORTSC1 0x184 -#define PORT_PTS_MSK (3 << 30) -#define PORT_PTS_UTMI (0 << 30) -#define PORT_PTS_ULPI (2 << 30) -#define PORT_PTS_SERIAL (3 << 30) -#define PORT_PTS_PTW (1 << 28) - -/* USBMODE Register bits */ -#define CM_IDLE (0 << 0) -#define CM_RESERVED (1 << 0) -#define CM_DEVICE (2 << 0) -#define CM_HOST (3 << 0) -#define USBMODE_RESERVED_2 (0 << 2) -#define SLOM (1 << 3) -#define SDIS (1 << 4) - -/* CONTROL Register bits */ -#define ULPI_INT_EN (1 << 0) -#define WU_INT_EN (1 << 1) -#define USB_EN (1 << 2) -#define LSF_EN (1 << 3) -#define KEEP_OTG_ON (1 << 4) -#define OTG_PORT (1 << 5) -#define REFSEL_12MHZ (0 << 6) -#define REFSEL_16MHZ (1 << 6) -#define REFSEL_48MHZ (2 << 6) -#define PLL_RESET (1 << 8) -#define UTMI_PHY_EN (1 << 9) -#define PHY_CLK_SEL_UTMI (0 << 10) -#define PHY_CLK_SEL_ULPI (1 << 10) -#define CLKIN_SEL_USB_CLK (0 << 11) -#define CLKIN_SEL_USB_CLK2 (1 << 11) -#define CLKIN_SEL_SYS_CLK (2 << 11) -#define CLKIN_SEL_SYS_CLK2 (3 << 11) -#define RESERVED_18 (0 << 13) -#define RESERVED_17 (0 << 14) -#define RESERVED_16 (0 << 15) -#define WU_INT (1 << 16) -#define PHY_CLK_VALID (1 << 17) - -#define FSL_SOC_USB_PORTSC2 0x188 -#define FSL_SOC_USB_USBMODE 0x1a8 -#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ -#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ -#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ -#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ -#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ -#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ -#define SNOOP_SIZE_2GB 0x1e - -/* System Clock Control Register */ -#define MPC83XX_SCCR_USB_MASK 0x00f00000 -#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 -#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 -#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 - -#endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/usb_ehci_ixp.c b/drivers/usb/usb_ehci_ixp.c deleted file mode 100644 index 25c18c1..0000000 --- a/drivers/usb/usb_ehci_ixp.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * (C) Copyright 2008, Michael Trimarchi - * - * Author: Michael Trimarchi - * This code is based on ehci freescale driver - * - * 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 -#include -#include "usb_ehci.h" -#include "usb_ehci_core.h" -/* - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(void) -{ - hccr = (struct ehci_hccr *)(0xcd000100); - hcor = (struct ehci_hcor *)((uint32_t) hccr - + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - - printf("IXP4XX init hccr %x and hcor %x hc_length %d\n", - (uint32_t)hccr, (uint32_t)hcor, - (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the the EHCI host controller. - */ -int ehci_hcd_stop(void) -{ - return 0; -} diff --git a/drivers/usb/usb_ehci_pci.c b/drivers/usb/usb_ehci_pci.c deleted file mode 100644 index 3e7143c..0000000 --- a/drivers/usb/usb_ehci_pci.c +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * Copyright (c) 2007-2008, Juniper Networks, Inc. - * All rights reserved. - * - * 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 version 2 of - * the License. - * - * 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 -#include -#include -#include "usb_ehci.h" -#include "usb_ehci_core.h" - -#ifdef CONFIG_PCI_EHCI_DEVICE -static struct pci_device_id ehci_pci_ids[] = { - /* Please add supported PCI EHCI controller ids here */ - {0, 0} -}; -#endif - -/* - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(void) -{ - pci_dev_t pdev; - uint32_t addr; - - pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); - if (pdev == -1) { - printf("EHCI host controller not found\n"); - return -1; - } - - pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr); - hccr = (struct ehci_hccr *)addr; - hcor = (struct ehci_hcor *)((uint32_t) hccr + - HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the the EHCI host controller. - */ -int ehci_hcd_stop(void) -{ - return 0; -} diff --git a/drivers/usb/usb_ehci_vct.c b/drivers/usb/usb_ehci_vct.c deleted file mode 100644 index 89daaaf..0000000 --- a/drivers/usb/usb_ehci_vct.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * (C) Copyright 2009 Stefan Roese , DENX Software Engineering - * - * 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 -#include - -#include "usb_ehci.h" -#include "usb_ehci_core.h" - -int vct_ehci_hcd_init(u32 *hccr, u32 *hcor); - -/* - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(void) -{ - int ret; - u32 vct_hccr; - u32 vct_hcor; - - /* - * Init VCT specific stuff - */ - ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor); - if (ret) - return ret; - - hccr = (struct ehci_hccr *)vct_hccr; - hcor = (struct ehci_hcor *)vct_hcor; - - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the the EHCI host controller. - */ -int ehci_hcd_stop(void) -{ - return 0; -} diff --git a/drivers/usb/usb_ohci.c b/drivers/usb/usb_ohci.c deleted file mode 100644 index 0bbee0f..0000000 --- a/drivers/usb/usb_ohci.c +++ /dev/null @@ -1,2015 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. - * - * Interrupt support is added. Now, it has been tested - * on ULI1575 chip and works well with USB keyboard. - * - * (C) Copyright 2007 - * Zhang Wei, Freescale Semiconductor, Inc. - * - * (C) Copyright 2003 - * Gary Jennejohn, DENX Software Engineering - * - * Note: Much of this code has been derived from Linux 2.4 - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * Modified for the MP2USB by (C) Copyright 2005 Eric Benard - * ebenard@eukrea.com - based on s3c24x0's driver - * - * 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 - * - */ -/* - * IMPORTANT NOTES - * 1 - Read doc/README.generic_usb_ohci - * 2 - this driver is intended for use with USB Mass Storage Devices - * (BBB) and USB keyboard. There is NO support for Isochronous pipes! - * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG - * to activate workaround for bug #41 or this driver will NOT work! - */ - -#include -#include - -#if defined(CONFIG_PCI_OHCI) -# include -#if !defined(CONFIG_PCI_OHCI_DEVNO) -#define CONFIG_PCI_OHCI_DEVNO 0 -#endif -#endif - -#include -#include -#include "usb_ohci.h" - -#ifdef CONFIG_AT91RM9200 -#include /* needed for AT91_USB_HOST_BASE */ -#endif - -#if defined(CONFIG_ARM920T) || \ - defined(CONFIG_S3C2400) || \ - defined(CONFIG_S3C2410) || \ - defined(CONFIG_S3C6400) || \ - defined(CONFIG_440EP) || \ - defined(CONFIG_PCI_OHCI) || \ - defined(CONFIG_MPC5200) || \ - defined(CONFIG_SYS_OHCI_USE_NPS) -# 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 \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - -/* - * e.g. PCI controllers need this - */ -#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS -# define readl(a) __swap_32(*((volatile u32 *)(a))) -# define writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a)) -#else -# define readl(a) (*((volatile u32 *)(a))) -# define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) -#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ - -#define min_t(type, x, y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) - -#ifdef CONFIG_PCI_OHCI -static struct pci_device_id ohci_pci_ids[] = { - {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ - {0x1033, 0x0035}, /* NEC PCI OHCI module ids */ - {0x1131, 0x1561}, /* Philips 1561 PCI OHCI module ids */ - /* Please add supported PCI OHCI controller ids here */ - {0, 0} -}; -#endif - -#ifdef CONFIG_PCI_EHCI_DEVNO -static struct pci_device_id ehci_pci_ids[] = { - {0x1131, 0x1562}, /* Philips 1562 PCI EHCI module ids */ - /* Please add supported PCI EHCI controller ids here */ - {0, 0} -}; -#endif - -#ifdef DEBUG -#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) -#else -#define dbg(format, arg...) do {} while (0) -#endif /* DEBUG */ -#define err(format, arg...) printf("ERROR: " format "\n", ## arg) -#ifdef SHOW_INFO -#define info(format, arg...) printf("INFO: " format "\n", ## arg) -#else -#define info(format, arg...) do {} while (0) -#endif - -#ifdef CONFIG_SYS_OHCI_BE_CONTROLLER -# define m16_swap(x) cpu_to_be16(x) -# define m32_swap(x) cpu_to_be32(x) -#else -# define m16_swap(x) cpu_to_le16(x) -# define m32_swap(x) cpu_to_le32(x) -#endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ - -/* global ohci_t */ -static ohci_t gohci; -/* this must be aligned to a 256 byte boundary */ -struct ohci_hcca ghcca[1]; -/* a pointer to the aligned storage */ -struct ohci_hcca *phcca; -/* this allocates EDs for all possible endpoints */ -struct ohci_device ohci_dev; -/* device which was disconnected */ -struct usb_device *devgone; - -static inline u32 roothub_a(struct ohci *hc) - { return readl(&hc->regs->roothub.a); } -static inline u32 roothub_b(struct ohci *hc) - { return readl(&hc->regs->roothub.b); } -static inline u32 roothub_status(struct ohci *hc) - { return readl(&hc->regs->roothub.status); } -static inline u32 roothub_portstatus(struct ohci *hc, int i) - { return readl(&hc->regs->roothub.portstatus[i]); } - -/* forward declaration */ -static int hc_interrupt(void); -static void td_submit_job(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, - struct devrequest *setup, urb_priv_t *urb, - int interval); - -/*-------------------------------------------------------------------------* - * URB support functions - *-------------------------------------------------------------------------*/ - -/* free HCD-private data associated with this URB */ - -static void urb_free_priv(urb_priv_t *urb) -{ - int i; - int last; - struct td *td; - - last = urb->length - 1; - if (last >= 0) { - for (i = 0; i <= last; i++) { - td = urb->td[i]; - if (td) { - td->usb_dev = NULL; - urb->td[i] = NULL; - } - } - } - free(urb); -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -static int sohci_get_current_frame_number(struct usb_device *dev); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void pkt_print(urb_priv_t *purb, struct usb_device *dev, - unsigned long pipe, void *buffer, int transfer_len, - struct devrequest *setup, char *str, int small) -{ - dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", - str, - sohci_get_current_frame_number(dev), - usb_pipedevice(pipe), - usb_pipeendpoint(pipe), - usb_pipeout(pipe)? 'O': 'I', - usb_pipetype(pipe) < 2 ? \ - (usb_pipeint(pipe)? "INTR": "ISOC"): \ - (usb_pipecontrol(pipe)? "CTRL": "BULK"), - (purb ? purb->actual_length : 0), - transfer_len, dev->status); -#ifdef OHCI_VERBOSE_DEBUG - if (!small) { - int i, len; - - if (usb_pipecontrol(pipe)) { - printf(__FILE__ ": cmd(8):"); - for (i = 0; i < 8 ; i++) - printf(" %02x", ((__u8 *) setup) [i]); - printf("\n"); - } - if (transfer_len > 0 && buffer) { - printf(__FILE__ ": data(%d/%d):", - (purb ? purb->actual_length : 0), - transfer_len); - len = usb_pipeout(pipe)? transfer_len: - (purb ? purb->actual_length : 0); - for (i = 0; i < 16 && i < len; i++) - printf(" %02x", ((__u8 *) buffer) [i]); - printf("%s\n", i < len? "...": ""); - } - } -#endif -} - -/* just for debugging; prints non-empty branches of the int ed tree - * inclusive iso eds */ -void ep_print_int_eds(ohci_t *ohci, char *str) -{ - int i, j; - __u32 *ed_p; - for (i = 0; i < 32; i++) { - j = 5; - ed_p = &(ohci->hcca->int_table [i]); - if (*ed_p == 0) - continue; - printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); - while (*ed_p != 0 && j--) { - ed_t *ed = (ed_t *)m32_swap(ed_p); - printf(" ed: %4x;", ed->hwINFO); - ed_p = &ed->hwNextED; - } - printf("\n"); - } -} - -static void ohci_dump_intr_mask(char *label, __u32 mask) -{ - dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", - label, - mask, - (mask & OHCI_INTR_MIE) ? " MIE" : "", - (mask & OHCI_INTR_OC) ? " OC" : "", - (mask & OHCI_INTR_RHSC) ? " RHSC" : "", - (mask & OHCI_INTR_FNO) ? " FNO" : "", - (mask & OHCI_INTR_UE) ? " UE" : "", - (mask & OHCI_INTR_RD) ? " RD" : "", - (mask & OHCI_INTR_SF) ? " SF" : "", - (mask & OHCI_INTR_WDH) ? " WDH" : "", - (mask & OHCI_INTR_SO) ? " SO" : "" - ); -} - -static void maybe_print_eds(char *label, __u32 value) -{ - ed_t *edp = (ed_t *)value; - - if (value) { - dbg("%s %08x", label, value); - dbg("%08x", edp->hwINFO); - dbg("%08x", edp->hwTailP); - dbg("%08x", edp->hwHeadP); - dbg("%08x", edp->hwNextED); - } -} - -static char *hcfs2string(int state) -{ - switch (state) { - case OHCI_USB_RESET: return "reset"; - case OHCI_USB_RESUME: return "resume"; - case OHCI_USB_OPER: return "operational"; - case OHCI_USB_SUSPEND: return "suspend"; - } - return "?"; -} - -/* dump control and status registers */ -static void ohci_dump_status(ohci_t *controller) -{ - struct ohci_regs *regs = controller->regs; - __u32 temp; - - temp = readl(®s->revision) & 0xff; - if (temp != 0x10) - dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); - - temp = readl(®s->control); - dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, - (temp & OHCI_CTRL_RWE) ? " RWE" : "", - (temp & OHCI_CTRL_RWC) ? " RWC" : "", - (temp & OHCI_CTRL_IR) ? " IR" : "", - hcfs2string(temp & OHCI_CTRL_HCFS), - (temp & OHCI_CTRL_BLE) ? " BLE" : "", - (temp & OHCI_CTRL_CLE) ? " CLE" : "", - (temp & OHCI_CTRL_IE) ? " IE" : "", - (temp & OHCI_CTRL_PLE) ? " PLE" : "", - temp & OHCI_CTRL_CBSR - ); - - temp = readl(®s->cmdstatus); - dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, - (temp & OHCI_SOC) >> 16, - (temp & OHCI_OCR) ? " OCR" : "", - (temp & OHCI_BLF) ? " BLF" : "", - (temp & OHCI_CLF) ? " CLF" : "", - (temp & OHCI_HCR) ? " HCR" : "" - ); - - ohci_dump_intr_mask("intrstatus", readl(®s->intrstatus)); - ohci_dump_intr_mask("intrenable", readl(®s->intrenable)); - - maybe_print_eds("ed_periodcurrent", readl(®s->ed_periodcurrent)); - - maybe_print_eds("ed_controlhead", readl(®s->ed_controlhead)); - maybe_print_eds("ed_controlcurrent", readl(®s->ed_controlcurrent)); - - maybe_print_eds("ed_bulkhead", readl(®s->ed_bulkhead)); - maybe_print_eds("ed_bulkcurrent", readl(®s->ed_bulkcurrent)); - - maybe_print_eds("donehead", readl(®s->donehead)); -} - -static void ohci_dump_roothub(ohci_t *controller, int verbose) -{ - __u32 temp, ndp, i; - - temp = roothub_a(controller); - ndp = (temp & RH_A_NDP); -#ifdef CONFIG_AT91C_PQFP_UHPBUG - ndp = (ndp == 2) ? 1:0; -#endif - if (verbose) { - dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, - ((temp & RH_A_POTPGT) >> 24) & 0xff, - (temp & RH_A_NOCP) ? " NOCP" : "", - (temp & RH_A_OCPM) ? " OCPM" : "", - (temp & RH_A_DT) ? " DT" : "", - (temp & RH_A_NPS) ? " NPS" : "", - (temp & RH_A_PSM) ? " PSM" : "", - ndp - ); - temp = roothub_b(controller); - dbg("roothub.b: %08x PPCM=%04x DR=%04x", - temp, - (temp & RH_B_PPCM) >> 16, - (temp & RH_B_DR) - ); - temp = roothub_status(controller); - dbg("roothub.status: %08x%s%s%s%s%s%s", - temp, - (temp & RH_HS_CRWE) ? " CRWE" : "", - (temp & RH_HS_OCIC) ? " OCIC" : "", - (temp & RH_HS_LPSC) ? " LPSC" : "", - (temp & RH_HS_DRWE) ? " DRWE" : "", - (temp & RH_HS_OCI) ? " OCI" : "", - (temp & RH_HS_LPS) ? " LPS" : "" - ); - } - - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus(controller, i); - dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", - i, - temp, - (temp & RH_PS_PRSC) ? " PRSC" : "", - (temp & RH_PS_OCIC) ? " OCIC" : "", - (temp & RH_PS_PSSC) ? " PSSC" : "", - (temp & RH_PS_PESC) ? " PESC" : "", - (temp & RH_PS_CSC) ? " CSC" : "", - - (temp & RH_PS_LSDA) ? " LSDA" : "", - (temp & RH_PS_PPS) ? " PPS" : "", - (temp & RH_PS_PRS) ? " PRS" : "", - (temp & RH_PS_POCI) ? " POCI" : "", - (temp & RH_PS_PSS) ? " PSS" : "", - - (temp & RH_PS_PES) ? " PES" : "", - (temp & RH_PS_CCS) ? " CCS" : "" - ); - } -} - -static void ohci_dump(ohci_t *controller, int verbose) -{ - dbg("OHCI controller usb-%s state", controller->slot_name); - - /* dumps some of the state we know about */ - ohci_dump_status(controller); - if (verbose) - ep_print_int_eds(controller, "hcca"); - dbg("hcca frame #%04x", controller->hcca->frame_no); - ohci_dump_roothub(controller, 1); -} -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------* - * Interface functions (URB) - *-------------------------------------------------------------------------*/ - -/* get a transfer request */ - -int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) -{ - ohci_t *ohci; - ed_t *ed; - urb_priv_t *purb_priv = urb; - int i, size = 0; - struct usb_device *dev = urb->dev; - unsigned long pipe = urb->pipe; - void *buffer = urb->transfer_buffer; - int transfer_len = urb->transfer_buffer_length; - int interval = urb->interval; - - ohci = &gohci; - - /* when controller's hung, permit only roothub cleanup attempts - * such as powering down ports */ - if (ohci->disabled) { - err("sohci_submit_job: EPIPE"); - return -1; - } - - /* we're about to begin a new transaction here so mark the - * URB unfinished */ - urb->finished = 0; - - /* every endpoint has a ed, locate and fill it */ - ed = ep_add_ed(dev, pipe, interval, 1); - if (!ed) { - err("sohci_submit_job: ENOMEM"); - return -1; - } - - /* for the private part of the URB we need the number of TDs (size) */ - switch (usb_pipetype(pipe)) { - case PIPE_BULK: /* one TD for every 4096 Byte */ - size = (transfer_len - 1) / 4096 + 1; - break; - case PIPE_CONTROL:/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ - size = (transfer_len == 0)? 2: - (transfer_len - 1) / 4096 + 3; - break; - case PIPE_INTERRUPT: /* 1 TD */ - size = 1; - break; - } - - ed->purb = urb; - - if (size >= (N_URB_TD - 1)) { - err("need %d TDs, only have %d", size, N_URB_TD); - return -1; - } - purb_priv->pipe = pipe; - - /* fill the private part of the URB */ - purb_priv->length = size; - purb_priv->ed = ed; - purb_priv->actual_length = 0; - - /* allocate the TDs */ - /* note that td[0] was allocated in ep_add_ed */ - for (i = 0; i < size; i++) { - purb_priv->td[i] = td_alloc(dev); - if (!purb_priv->td[i]) { - purb_priv->length = i; - urb_free_priv(purb_priv); - err("sohci_submit_job: ENOMEM"); - return -1; - } - } - - if (ed->state == ED_NEW || (ed->state & ED_DEL)) { - urb_free_priv(purb_priv); - err("sohci_submit_job: EINVAL"); - return -1; - } - - /* link the ed into a chain if is not already */ - if (ed->state != ED_OPER) - ep_link(ohci, ed); - - /* fill the TDs and link it to the ed */ - td_submit_job(dev, pipe, buffer, transfer_len, - setup, purb_priv, interval); - - return 0; -} - -static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) -{ - struct ohci_regs *regs = hc->regs; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - /* implicitly requeued */ - if (urb->dev->irq_handle && - (urb->dev->irq_act_len = urb->actual_length)) { - writel(OHCI_INTR_WDH, ®s->intrenable); - readl(®s->intrenable); /* PCI posting flush */ - urb->dev->irq_handle(urb->dev); - writel(OHCI_INTR_WDH, ®s->intrdisable); - readl(®s->intrdisable); /* PCI posting flush */ - } - urb->actual_length = 0; - td_submit_job( - urb->dev, - urb->pipe, - urb->transfer_buffer, - urb->transfer_buffer_length, - NULL, - urb, - urb->interval); - break; - case PIPE_CONTROL: - case PIPE_BULK: - break; - default: - return 0; - } - return 1; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -/* tell us the current USB frame number */ - -static int sohci_get_current_frame_number(struct usb_device *usb_dev) -{ - ohci_t *ohci = &gohci; - - return m16_swap(ohci->hcca->frame_no); -} -#endif - -/*-------------------------------------------------------------------------* - * ED handling functions - *-------------------------------------------------------------------------*/ - -/* search for the right branch to insert an interrupt ed into the int tree - * do some load ballancing; - * returns the branch and - * sets the interval to interval = 2^integer (ld (interval)) */ - -static int ep_int_ballance(ohci_t *ohci, int interval, int load) -{ - int i, branch = 0; - - /* search for the least loaded interrupt endpoint - * branch of all 32 branches - */ - for (i = 0; i < 32; i++) - if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) - branch = i; - - branch = branch % interval; - for (i = branch; i < 32; i += interval) - ohci->ohci_int_load [i] += load; - - return branch; -} - -/*-------------------------------------------------------------------------*/ - -/* 2^int( ld (inter)) */ - -static int ep_2_n_interval(int inter) -{ - int i; - for (i = 0; ((inter >> i) > 1) && (i < 5); i++); - return 1 << i; -} - -/*-------------------------------------------------------------------------*/ - -/* the int tree is a binary tree - * in order to process it sequentially the indexes of the branches have to - * be mapped the mapping reverses the bits of a word of num_bits length */ -static int ep_rev(int num_bits, int word) -{ - int i, wout = 0; - - for (i = 0; i < num_bits; i++) - wout |= (((word >> i) & 1) << (num_bits - i - 1)); - return wout; -} - -/*-------------------------------------------------------------------------* - * ED handling functions - *-------------------------------------------------------------------------*/ - -/* link an ed into one of the HC chains */ - -static int ep_link(ohci_t *ohci, ed_t *edi) -{ - volatile ed_t *ed = edi; - int int_branch; - int i; - int inter; - int interval; - int load; - __u32 *ed_p; - - ed->state = ED_OPER; - ed->int_interval = 0; - - switch (ed->type) { - case PIPE_CONTROL: - ed->hwNextED = 0; - if (ohci->ed_controltail == NULL) - writel(ed, &ohci->regs->ed_controlhead); - else - ohci->ed_controltail->hwNextED = - m32_swap((unsigned long)ed); - - ed->ed_prev = ohci->ed_controltail; - if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_CLE; - writel(ohci->hc_control, &ohci->regs->control); - } - ohci->ed_controltail = edi; - break; - - case PIPE_BULK: - ed->hwNextED = 0; - if (ohci->ed_bulktail == NULL) - writel(ed, &ohci->regs->ed_bulkhead); - else - ohci->ed_bulktail->hwNextED = - m32_swap((unsigned long)ed); - - ed->ed_prev = ohci->ed_bulktail; - if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_BLE; - writel(ohci->hc_control, &ohci->regs->control); - } - ohci->ed_bulktail = edi; - break; - - case PIPE_INTERRUPT: - load = ed->int_load; - interval = ep_2_n_interval(ed->int_period); - ed->int_interval = interval; - int_branch = ep_int_ballance(ohci, interval, load); - ed->int_branch = int_branch; - - for (i = 0; i < ep_rev(6, interval); i += inter) { - inter = 1; - for (ed_p = &(ohci->hcca->int_table[\ - ep_rev(5, i) + int_branch]); - (*ed_p != 0) && - (((ed_t *)ed_p)->int_interval >= interval); - ed_p = &(((ed_t *)ed_p)->hwNextED)) - inter = ep_rev(6, - ((ed_t *)ed_p)->int_interval); - ed->hwNextED = *ed_p; - *ed_p = m32_swap((unsigned long)ed); - } - break; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* scan the periodic table to find and unlink this ED */ -static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, - unsigned index, unsigned period) -{ - for (; index < NUM_INTS; index += period) { - __u32 *ed_p = &ohci->hcca->int_table [index]; - - /* ED might have been unlinked through another path */ - while (*ed_p != 0) { - if (((struct ed *) - m32_swap((unsigned long)ed_p)) == ed) { - *ed_p = ed->hwNextED; - break; - } - ed_p = &(((struct ed *) - m32_swap((unsigned long)ed_p))->hwNextED); - } - } -} - -/* unlink an ed from one of the HC chains. - * just the link to the ed is unlinked. - * 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 *edi) -{ - volatile ed_t *ed = edi; - int i; - - ed->hwINFO |= m32_swap(OHCI_ED_SKIP); - - switch (ed->type) { - case PIPE_CONTROL: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_CLE; - writel(ohci->hc_control, &ohci->regs->control); - } - writel(m32_swap(*((__u32 *)&ed->hwNextED)), - &ohci->regs->ed_controlhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } else { - ((ed_t *)m32_swap( - *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_BULK: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_BLE; - writel(ohci->hc_control, &ohci->regs->control); - } - writel(m32_swap(*((__u32 *)&ed->hwNextED)), - &ohci->regs->ed_bulkhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } else { - ((ed_t *)m32_swap( - *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_INTERRUPT: - periodic_unlink(ohci, ed, 0, 1); - for (i = ed->int_branch; i < 32; i += ed->int_interval) - ohci->ohci_int_load[i] -= ed->int_load; - break; - } - ed->state = ED_UNLINK; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* 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, - int interval, int load) -{ - td_t *td; - ed_t *ed_ret; - volatile ed_t *ed; - - ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | - (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; - - if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { - err("ep_add_ed: pending delete"); - /* pending delete request */ - return NULL; - } - - if (ed->state == ED_NEW) { - /* dummy td; end of td list for ed */ - td = td_alloc(usb_dev); - ed->hwTailP = m32_swap((unsigned long)td); - ed->hwHeadP = ed->hwTailP; - ed->state = ED_UNLINK; - ed->type = usb_pipetype(pipe); - ohci_dev.ed_cnt++; - } - - ed->hwINFO = m32_swap(usb_pipedevice(pipe) - | usb_pipeendpoint(pipe) << 7 - | (usb_pipeisoc(pipe)? 0x8000: 0) - | (usb_pipecontrol(pipe)? 0: \ - (usb_pipeout(pipe)? 0x800: 0x1000)) - | usb_pipeslow(pipe) << 13 - | usb_maxpacket(usb_dev, pipe) << 16); - - if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { - ed->int_period = interval; - ed->int_load = load; - } - - return ed_ret; -} - -/*-------------------------------------------------------------------------* - * TD handling functions - *-------------------------------------------------------------------------*/ - -/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ - -static void td_fill(ohci_t *ohci, unsigned int info, - void *data, int len, - struct usb_device *dev, int index, urb_priv_t *urb_priv) -{ - volatile td_t *td, *td_pt; -#ifdef OHCI_FILL_TRACE - int i; -#endif - - if (index > urb_priv->length) { - err("index > length"); - return; - } - /* use this td as the next dummy */ - td_pt = urb_priv->td [index]; - td_pt->hwNextTD = 0; - - /* fill the old dummy TD */ - td = urb_priv->td [index] = - (td_t *)(m32_swap(urb_priv->ed->hwTailP) & ~0xf); - - td->ed = urb_priv->ed; - td->next_dl_td = NULL; - td->index = index; - td->data = (__u32)data; -#ifdef OHCI_FILL_TRACE - if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { - for (i = 0; i < len; i++) - printf("td->data[%d] %#2x ", i, ((unsigned char *)td->data)[i]); - printf("\n"); - } -#endif - if (!len) - data = 0; - - td->hwINFO = m32_swap(info); - td->hwCBP = m32_swap((unsigned long)data); - if (data) - td->hwBE = m32_swap((unsigned long)(data + len - 1)); - else - td->hwBE = 0; - - td->hwNextTD = m32_swap((unsigned long)td_pt); - - /* append to queue */ - td->ed->hwTailP = td->hwNextTD; -} - -/*-------------------------------------------------------------------------*/ - -/* prepare all TDs of a transfer */ - -static void td_submit_job(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, - struct devrequest *setup, urb_priv_t *urb, - int interval) -{ - ohci_t *ohci = &gohci; - int data_len = transfer_len; - void *data; - int cnt = 0; - __u32 info = 0; - unsigned int toggle = 0; - - /* OHCI handles the DATA-toggles itself, we just use the USB-toggle - * bits for reseting */ - if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { - toggle = TD_T_TOGGLE; - } else { - toggle = TD_T_DATA0; - usb_settoggle(dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe), 1); - } - urb->td_cnt = 0; - if (data_len) - data = buffer; - else - data = 0; - - switch (usb_pipetype(pipe)) { - case PIPE_BULK: - info = usb_pipeout(pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; - while (data_len > 4096) { - td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), - data, 4096, dev, cnt, urb); - data += 4096; data_len -= 4096; cnt++; - } - info = usb_pipeout(pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), data, - data_len, dev, cnt, urb); - cnt++; - - if (!ohci->sleeping) { - /* start bulk list */ - writel(OHCI_BLF, &ohci->regs->cmdstatus); - } - break; - - case PIPE_CONTROL: - /* Setup phase */ - info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill(ohci, info, setup, 8, dev, cnt++, urb); - - /* Optional Data phase */ - if (data_len > 0) { - info = usb_pipeout(pipe)? - TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : - TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - /* NOTE: mishandles transfers >8K, some >4K */ - td_fill(ohci, info, data, data_len, dev, cnt++, urb); - } - - /* Status phase */ - info = usb_pipeout(pipe)? - TD_CC | TD_DP_IN | TD_T_DATA1: - TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill(ohci, info, data, 0, dev, cnt++, urb); - - if (!ohci->sleeping) { - /* start Control list */ - writel(OHCI_CLF, &ohci->regs->cmdstatus); - } - break; - - case PIPE_INTERRUPT: - info = usb_pipeout(urb->pipe)? - TD_CC | TD_DP_OUT | toggle: - TD_CC | TD_R | TD_DP_IN | toggle; - td_fill(ohci, info, data, data_len, dev, cnt++, urb); - break; - } - if (urb->length != cnt) - dbg("TD LENGTH %d != CNT %d", urb->length, cnt); -} - -/*-------------------------------------------------------------------------* - * Done List handling functions - *-------------------------------------------------------------------------*/ - -/* calculate the transfer length and update the urb */ - -static void dl_transfer_length(td_t *td) -{ - __u32 tdINFO, tdBE, tdCBP; - urb_priv_t *lurb_priv = td->ed->purb; - - tdINFO = m32_swap(td->hwINFO); - tdBE = m32_swap(td->hwBE); - tdCBP = m32_swap(td->hwCBP); - - if (!(usb_pipecontrol(lurb_priv->pipe) && - ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { - if (tdBE != 0) { - if (td->hwCBP == 0) - lurb_priv->actual_length += tdBE - td->data + 1; - else - lurb_priv->actual_length += tdCBP - td->data; - } - } -} - -/*-------------------------------------------------------------------------*/ -static void check_status(td_t *td_list) -{ - urb_priv_t *lurb_priv = td_list->ed->purb; - int urb_len = lurb_priv->length; - __u32 *phwHeadP = &td_list->ed->hwHeadP; - int cc; - - cc = TD_CC_GET(m32_swap(td_list->hwINFO)); - if (cc) { - err(" USB-error: %s (%x)", cc_to_string[cc], cc); - - if (*phwHeadP & m32_swap(0x1)) { - if (lurb_priv && - ((td_list->index + 1) < urb_len)) { - *phwHeadP = - (lurb_priv->td[urb_len - 1]->hwNextTD &\ - m32_swap(0xfffffff0)) | - (*phwHeadP & m32_swap(0x2)); - - lurb_priv->td_cnt += urb_len - - td_list->index - 1; - } else - *phwHeadP &= m32_swap(0xfffffff2); - } -#ifdef CONFIG_MPC5200 - td_list->hwNextTD = 0; -#endif - } -} - -/* replies to the request have to be on a FIFO basis so - * we reverse the reversed done-list */ -static td_t *dl_reverse_done_list(ohci_t *ohci) -{ - __u32 td_list_hc; - td_t *td_rev = NULL; - td_t *td_list = NULL; - - td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; - ohci->hcca->done_head = 0; - - while (td_list_hc) { - td_list = (td_t *)td_list_hc; - check_status(td_list); - td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; - } - return td_list; -} - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------*/ - -static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) -{ - if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) - urb->finished = sohci_return_job(ohci, urb); - else - dbg("finish_urb: strange.., ED state %x, \n", status); -} - -/* - * Used to take back a TD from the host controller. This would normally be - * called from within dl_done_list, however it may be called directly if the - * HC no longer sees the TD and it has not appeared on the donelist (after - * two frames). This bug has been observed on ZF Micro systems. - */ -static int takeback_td(ohci_t *ohci, td_t *td_list) -{ - ed_t *ed; - int cc; - int stat = 0; - /* urb_t *urb; */ - urb_priv_t *lurb_priv; - __u32 tdINFO, edHeadP, edTailP; - - tdINFO = m32_swap(td_list->hwINFO); - - ed = td_list->ed; - lurb_priv = ed->purb; - - dl_transfer_length(td_list); - - lurb_priv->td_cnt++; - - /* error code of transfer */ - cc = TD_CC_GET(tdINFO); - if (cc) { - err("USB-error: %s (%x)", cc_to_string[cc], cc); - stat = cc_to_error[cc]; - } - - /* see if this done list makes for all TD's of current URB, - * and mark the URB finished if so */ - if (lurb_priv->td_cnt == lurb_priv->length) - finish_urb(ohci, lurb_priv, ed->state); - - dbg("dl_done_list: processing TD %x, len %x\n", - lurb_priv->td_cnt, lurb_priv->length); - - if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { - edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; - edTailP = m32_swap(ed->hwTailP); - - /* unlink eds if they are not busy */ - if ((edHeadP == edTailP) && (ed->state == ED_OPER)) - ep_unlink(ohci, ed); - } - return stat; -} - -static int dl_done_list(ohci_t *ohci) -{ - int stat = 0; - td_t *td_list = dl_reverse_done_list(ohci); - - while (td_list) { - td_t *td_next = td_list->next_dl_td; - stat = takeback_td(ohci, td_list); - td_list = td_next; - } - return stat; -} - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x01, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static unsigned char root_hub_str_index0[] = -{ - 0x04, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 0x09, /* __u8 lang ID */ - 0x04, /* __u8 lang ID */ -}; - -static unsigned char root_hub_str_index1[] = -{ - 28, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 'O', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'C', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'I', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'R', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 't', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'u', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'b', /* __u8 Unicode */ - 0, /* __u8 Unicode */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - -/*-------------------------------------------------------------------------*/ - -#define OK(x) len = (x); break -#ifdef DEBUG -#define WR_RH_STAT(x) {info("WR:status %#8x", (x)); writel((x), \ - &gohci.regs->roothub.status); } -#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \ - (x)); writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); } -#else -#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) -#define WR_RH_PORTSTAT(x) writel((x), \ - &gohci.regs->roothub.portstatus[wIndex-1]) -#endif -#define RD_RH_STAT roothub_status(&gohci) -#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1) - -/* request to virtual root hub */ - -int rh_check_port_status(ohci_t *controller) -{ - __u32 temp, ndp, i; - int res; - - res = -1; - temp = roothub_a(controller); - ndp = (temp & RH_A_NDP); -#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 */ - if (((temp & (RH_PS_PESC | RH_PS_CSC)) == - (RH_PS_PESC | RH_PS_CSC)) && - ((temp & RH_PS_CCS) == 0)) { - res = i; - break; - } - } - return res; -} - -static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) -{ - void *data = buffer; - int leni = transfer_len; - int len = 0; - int stat = 0; - __u32 datab[4]; - __u8 *data_buf = (__u8 *)datab; - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - -#ifdef DEBUG -pkt_print(NULL, dev, pipe, buffer, transfer_len, - cmd, "SUB(rh)", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (usb_pipeint(pipe)) { - info("Root-Hub submit IRQ: NOT implemented"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = le16_to_cpu(cmd->value); - wIndex = le16_to_cpu(cmd->index); - wLength = le16_to_cpu(cmd->length); - - info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", - dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16(1); - OK(2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16(0); - OK(2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16(0); - OK(2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK(4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32(RD_RH_PORTSTAT); - OK(4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK(0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK(0); - case (RH_C_HUB_OVER_CURRENT): - WR_RH_STAT(RH_HS_OCIC); - OK(0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS); OK(0); - case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0); - case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); - case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC); OK(0); - case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0); - case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); - case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); - case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT(RH_PS_PSS); OK(0); - case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT(RH_PS_PRS); - OK(0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT(RH_PS_PPS); - wait_ms(100); - OK(0); - case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT(RH_PS_PES); - OK(0); - } - break; - - case RH_SET_ADDRESS: - gohci.rh.devnum = wValue; - OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_dev_des), - wLength)); - data_buf = root_hub_dev_des; OK(len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_config_des), - wLength)); - data_buf = root_hub_config_des; OK(len); - case (0x03): /* string descriptors */ - if (wValue == 0x0300) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_str_index0), - wLength)); - data_buf = root_hub_str_index0; - OK(len); - } - if (wValue == 0x0301) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof(root_hub_str_index1), - wLength)); - data_buf = root_hub_str_index1; - OK(len); - } - default: - stat = USB_ST_STALLED; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - { - __u32 temp = roothub_a(&gohci); - - data_buf [0] = 9; /* min length; */ - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; -#ifdef CONFIG_AT91C_PQFP_UHPBUG - data_buf [2] = (data_buf [2] == 2) ? 1:0; -#endif - data_buf [3] = 0; - if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; - if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; - else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; - - /* corresponds to data_buf[4-7] */ - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; - temp = roothub_b(&gohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; - } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); - OK(len); - } - - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK(1); - - case RH_SET_CONFIGURATION: WR_RH_STAT(0x10000); OK(0); - - default: - dbg("unsupported root hub command"); - stat = USB_ST_STALLED; - } - -#ifdef DEBUG - ohci_dump_roothub(&gohci, 1); -#else - wait_ms(1); -#endif - - len = min_t(int, len, leni); - if (data != data_buf) - memcpy(data, data_buf, len); - dev->act_len = len; - dev->status = stat; - -#ifdef DEBUG - pkt_print(NULL, dev, pipe, buffer, - transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); -#else - wait_ms(1); -#endif - - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/* common code for handling submit messages - used for all but root hub */ -/* accesses. */ -int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - int stat = 0; - int maxsize = usb_maxpacket(dev, pipe); - int timeout; - urb_priv_t *urb; - - urb = malloc(sizeof(urb_priv_t)); - memset(urb, 0, sizeof(urb_priv_t)); - - urb->dev = dev; - urb->pipe = pipe; - urb->transfer_buffer = buffer; - urb->transfer_buffer_length = transfer_len; - urb->interval = interval; - - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - -#ifdef DEBUG - urb->actual_length = 0; - pkt_print(urb, dev, pipe, buffer, transfer_len, - setup, "SUB", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (!maxsize) { - err("submit_common_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - - if (sohci_submit_job(urb, setup) < 0) { - err("sohci_submit_job failed"); - 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 */ - if (usb_pipebulk(pipe)) - timeout = BULK_TO; - else - timeout = 100; - - /* wait for it to complete */ - for (;;) { - /* check whether the controller is done */ - stat = hc_interrupt(); - if (stat < 0) { - stat = USB_ST_CRC_ERR; - break; - } - - /* NOTE: since we are not interrupt driven in U-Boot and always - * handle only one URB at a time, we cannot assume the - * transaction finished on the first successful return from - * hc_interrupt().. unless the flag for current URB is set, - * meaning that all TD's to/from device got actually - * transferred and processed. If the current URB is not - * finished we need to re-iterate this loop so as - * hc_interrupt() gets called again as there needs to be some - * more TD's to process still */ - if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { - /* 0xff is returned for an SF-interrupt */ - break; - } - - if (--timeout) { - wait_ms(1); - if (!urb->finished) - dbg("*"); - - } else { - err("CTL:TIMEOUT "); - dbg("submit_common_msg: TO status %x\n", stat); - urb->finished = 1; - stat = USB_ST_CRC_ERR; - break; - } - } - - dev->status = stat; - dev->act_len = transfer_len; - -#ifdef DEBUG - pkt_print(urb, dev, pipe, buffer, transfer_len, - setup, "RET(ctlr)", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - - /* free TDs in urb_priv */ - if (!usb_pipeint(pipe)) - urb_free_priv(urb); - return 0; -} - -/* submit routines called from usb.c */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len) -{ - info("submit_bulk_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup) -{ - int maxsize = usb_maxpacket(dev, pipe); - - info("submit_control_msg"); -#ifdef DEBUG - pkt_print(NULL, dev, pipe, buffer, transfer_len, - setup, "SUB", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (!maxsize) { - err("submit_control_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { - gohci.rh.dev = dev; - /* root hub - redirect */ - return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); - } - - return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - info("submit_int_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, - interval); -} - -/*-------------------------------------------------------------------------* - * HC functions - *-------------------------------------------------------------------------*/ - -/* reset the HC and BUS */ - -static int hc_reset(ohci_t *ohci) -{ -#ifdef CONFIG_PCI_EHCI_DEVNO - pci_dev_t pdev; -#endif - int timeout = 30; - int smm_timeout = 50; /* 0,5 sec */ - - dbg("%s\n", __FUNCTION__); - -#ifdef CONFIG_PCI_EHCI_DEVNO - /* - * Some multi-function controllers (e.g. ISP1562) allow root hub - * resetting via EHCI registers only. - */ - pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO); - if (pdev != -1) { - u32 base; - int timeout = 1000; - - pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); - writel(readl(base + EHCI_USBCMD_OFF) | EHCI_USBCMD_HCRESET, - base + EHCI_USBCMD_OFF); - - while (readl(base + EHCI_USBCMD_OFF) & EHCI_USBCMD_HCRESET) { - if (timeout-- <= 0) { - printf("USB RootHub reset timed out!"); - break; - } - udelay(1); - } - } else - printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO); -#endif - if (readl(&ohci->regs->control) & OHCI_CTRL_IR) { - /* SMM owns the HC */ - writel(OHCI_OCR, &ohci->regs->cmdstatus);/* request ownership */ - info("USB HC TakeOver from SMM"); - while (readl(&ohci->regs->control) & OHCI_CTRL_IR) { - wait_ms(10); - if (--smm_timeout == 0) { - err("USB HC TakeOver failed!"); - return -1; - } - } - } - - /* Disable HC interrupts */ - writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); - - dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", - ohci->slot_name, - readl(&ohci->regs->control)); - - /* Reset USB (needed by some controllers) */ - ohci->hc_control = 0; - writel(ohci->hc_control, &ohci->regs->control); - - /* HC Reset requires max 10 us delay */ - writel(OHCI_HCR, &ohci->regs->cmdstatus); - while ((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { - if (--timeout == 0) { - err("USB HC reset timed out!"); - return -1; - } - udelay(1); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub */ - -static int hc_start(ohci_t *ohci) -{ - __u32 mask; - unsigned int fminterval; - - ohci->disabled = 1; - - /* Tell the controller where the control and bulk lists are - * The lists are empty now. */ - - writel(0, &ohci->regs->ed_controlhead); - writel(0, &ohci->regs->ed_bulkhead); - - writel((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ - - fminterval = 0x2edf; - writel((fminterval * 9) / 10, &ohci->regs->periodicstart); - fminterval |= ((((fminterval - 210) * 6) / 7) << 16); - writel(fminterval, &ohci->regs->fminterval); - writel(0x628, &ohci->regs->lsthresh); - - /* start controller operations */ - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - ohci->disabled = 0; - writel(ohci->hc_control, &ohci->regs->control); - - /* disable all interrupts */ - mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | - OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | - OHCI_INTR_OC | OHCI_INTR_MIE); - writel(mask, &ohci->regs->intrdisable); - /* clear all interrupts */ - mask &= ~OHCI_INTR_MIE; - writel(mask, &ohci->regs->intrstatus); - /* Choose the interrupts we care about now - but w/o MIE */ - mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; - writel(mask, &ohci->regs->intrenable); - -#ifdef OHCI_USE_NPS - /* required for AMD-756 and some Mac platforms */ - writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, - &ohci->regs->roothub.a); - writel(RH_HS_LPSC, &ohci->regs->roothub.status); -#endif /* OHCI_USE_NPS */ - -#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) - /* POTPGT delay is bits 24-31, in 2 ms units. */ - mdelay((roothub_a(ohci) >> 23) & 0x1fe); - - /* connect the virtual root hub */ - ohci->rh.devnum = 0; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Poll USB interrupt. */ -void usb_event_poll(void) -{ - hc_interrupt(); -} - -/* an interrupt happens */ - -static int hc_interrupt(void) -{ - ohci_t *ohci = &gohci; - struct ohci_regs *regs = ohci->regs; - int ints; - int stat = -1; - - if ((ohci->hcca->done_head != 0) && - !(m32_swap(ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl(®s->intrstatus); - if (ints == ~(u32)0) { - ohci->disabled++; - err("%s device removed!", ohci->slot_name); - return -1; - } else { - ints &= readl(®s->intrenable); - if (ints == 0) { - dbg("hc_interrupt: returning..\n"); - return 0xff; - } - } - } - - /* dbg("Interrupt: %x frame: %x", ints, - le16_to_cpu(ohci->hcca->frame_no)); */ - - if (ints & OHCI_INTR_RHSC) - stat = 0xff; - - if (ints & OHCI_INTR_UE) { - ohci->disabled++; - err("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->slot_name); - /* e.g. due to PCI Master/Target Abort */ - -#ifdef DEBUG - ohci_dump(ohci, 1); -#else - wait_ms(1); -#endif - /* FIXME: be optimistic, hope that bug won't repeat often. */ - /* Make some non-interrupt context restart the controller. */ - /* Count and limit the retries though; either hardware or */ - /* software errors can go forever... */ - hc_reset(ohci); - return -1; - } - - if (ints & OHCI_INTR_WDH) { - wait_ms(1); - writel(OHCI_INTR_WDH, ®s->intrdisable); - (void)readl(®s->intrdisable); /* flush */ - stat = dl_done_list(&gohci); - writel(OHCI_INTR_WDH, ®s->intrenable); - (void)readl(®s->intrdisable); /* flush */ - } - - if (ints & OHCI_INTR_SO) { - dbg("USB Schedule overrun\n"); - writel(OHCI_INTR_SO, ®s->intrenable); - stat = -1; - } - - /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ - if (ints & OHCI_INTR_SF) { - unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; - wait_ms(1); - writel(OHCI_INTR_SF, ®s->intrdisable); - if (ohci->ed_rm_list[frame] != NULL) - writel(OHCI_INTR_SF, ®s->intrenable); - stat = 0xff; - } - - writel(ints, ®s->intrstatus); - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/*-------------------------------------------------------------------------*/ - -/* De-allocate all resources.. */ - -static void hc_release_ohci(ohci_t *ohci) -{ - dbg("USB HC release ohci usb-%s", ohci->slot_name); - - if (!ohci->disabled) - hc_reset(ohci); -} - -/*-------------------------------------------------------------------------*/ - -/* - * low level initalisation routine, called from usb.c - */ -static char ohci_inited = 0; - -int usb_lowlevel_init(void) -{ -#ifdef CONFIG_PCI_OHCI - pci_dev_t pdev; -#endif - -#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT - /* cpu dependant init */ - if (usb_cpu_init()) - return -1; -#endif - -#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT - /* board dependant init */ - if (usb_board_init()) - return -1; -#endif - memset(&gohci, 0, sizeof(ohci_t)); - - /* align the storage */ - if ((__u32)&ghcca[0] & 0xff) { - err("HCCA not aligned!!"); - return -1; - } - phcca = &ghcca[0]; - info("aligned ghcca %p", phcca); - memset(&ohci_dev, 0, sizeof(struct ohci_device)); - if ((__u32)&ohci_dev.ed[0] & 0x7) { - err("EDs not aligned!!"); - return -1; - } - memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); - if ((__u32)gtd & 0x7) { - err("TDs not aligned!!"); - return -1; - } - ptd = gtd; - gohci.hcca = phcca; - memset(phcca, 0, sizeof(struct ohci_hcca)); - - gohci.disabled = 1; - gohci.sleeping = 0; - gohci.irq = -1; -#ifdef CONFIG_PCI_OHCI - pdev = pci_find_devices(ohci_pci_ids, CONFIG_PCI_OHCI_DEVNO); - - if (pdev != -1) { - u16 vid, did; - u32 base; - pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); - pci_read_config_word(pdev, PCI_DEVICE_ID, &did); - printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", - vid, did, (pdev >> 16) & 0xff, - (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); - pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); - printf("OHCI regs address 0x%08x\n", base); - gohci.regs = (struct ohci_regs *)base; - } else - return -1; -#else - gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE; -#endif - - gohci.flags = 0; - gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME; - - if (hc_reset (&gohci) < 0) { - hc_release_ohci (&gohci); - err ("can't reset usb-%s", gohci.slot_name); -#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT - /* board dependant cleanup */ - usb_board_init_fail(); -#endif - -#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT - /* cpu dependant cleanup */ - usb_cpu_init_fail(); -#endif - return -1; - } - - if (hc_start(&gohci) < 0) { - err("can't start usb-%s", gohci.slot_name); - hc_release_ohci(&gohci); - /* Initialization failed */ -#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT - /* board dependant cleanup */ - usb_board_stop(); -#endif - -#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT - /* cpu dependant cleanup */ - usb_cpu_stop(); -#endif - return -1; - } - -#ifdef DEBUG - ohci_dump(&gohci, 1); -#else - wait_ms(1); -#endif - ohci_inited = 1; - return 0; -} - -int usb_lowlevel_stop(void) -{ - /* this gets called really early - before the controller has */ - /* even been initialized! */ - if (!ohci_inited) - return 0; - /* TODO release any interrupts, etc. */ - /* call hc_release_ohci() here ? */ - hc_reset(&gohci); - -#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT - /* board dependant cleanup */ - if (usb_board_stop()) - return -1; -#endif - -#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT - /* cpu dependant cleanup */ - if (usb_cpu_stop()) - return -1; -#endif - /* This driver is no longer initialised. It needs a new low-level - * init (board/cpu) before it can be used again. */ - ohci_inited = 0; - return 0; -} diff --git a/drivers/usb/usb_ohci.h b/drivers/usb/usb_ohci.h deleted file mode 100644 index a547337..0000000 --- a/drivers/usb/usb_ohci.h +++ /dev/null @@ -1,483 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2001 David Brownell - * - * 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_board_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] = { - -/* mapping of the OHCI CC status to error codes */ - /* No Error */ 0, - /* CRC Error */ USB_ST_CRC_ERR, - /* Bit Stuff */ USB_ST_BIT_ERR, - /* Data Togg */ USB_ST_CRC_ERR, - /* Stall */ USB_ST_STALLED, - /* DevNotResp */ -1, - /* PIDCheck */ USB_ST_BIT_ERR, - /* UnExpPID */ USB_ST_BIT_ERR, - /* DataOver */ USB_ST_BUF_ERR, - /* DataUnder */ USB_ST_BUF_ERR, - /* reservd */ -1, - /* reservd */ -1, - /* BufferOver */ USB_ST_BUF_ERR, - /* BuffUnder */ USB_ST_BUF_ERR, - /* Not Access */ -1, - /* Not Access */ -1 -}; - -static const char *cc_to_string[16] = { - "No Error", - "CRC: Last data packet from endpoint contained a CRC error.", - "BITSTUFFING: Last data packet from endpoint contained a bit " \ - "stuffing violation", - "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ - "that did not match the expected value.", - "STALL: TD was moved to the Done Queue because the endpoint returned" \ - " a STALL PID", - "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ - "not provide a handshake (OUT)", - "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ - "(IN) or handshake (OUT)", - "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ - "value is not defined.", - "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ - "either the size of the maximum data packet allowed\n" \ - "from the endpoint (found in MaximumPacketSize field\n" \ - "of ED) or the remaining buffer size.", - "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ - "and that amount was not sufficient to fill the\n" \ - "specified buffer", - "reserved1", - "reserved2", - "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ - "than it could be written to system memory", - "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ - "system memory fast enough to keep up with data USB " \ - "data rate.", - "NOT ACCESSED: This code is set by software before the TD is placed" \ - "on a list to be processed by the HC.(1)", - "NOT ACCESSED: This code is set by software before the TD is placed" \ - "on a list to be processed by the HC.(2)", -}; - -/* ED States */ - -#define ED_NEW 0x00 -#define ED_UNLINK 0x01 -#define ED_OPER 0x02 -#define ED_DEL 0x04 -#define ED_URB_DEL 0x08 - -/* usb_ohci_ed */ -struct ed { - __u32 hwINFO; - __u32 hwTailP; - __u32 hwHeadP; - __u32 hwNextED; - - struct ed *ed_prev; - __u8 int_period; - __u8 int_branch; - __u8 int_load; - __u8 int_interval; - __u8 state; - __u8 type; - __u16 last_iso; - struct ed *ed_rm_list; - - struct usb_device *usb_dev; - void *purb; - __u32 unused[2]; -} __attribute((aligned(16))); -typedef struct ed ed_t; - - -/* TD info field */ -#define TD_CC 0xf0000000 -#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) -#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) -#define TD_EC 0x0C000000 -#define TD_T 0x03000000 -#define TD_T_DATA0 0x02000000 -#define TD_T_DATA1 0x03000000 -#define TD_T_TOGGLE 0x00000000 -#define TD_R 0x00040000 -#define TD_DI 0x00E00000 -#define TD_DI_SET(X) (((X) & 0x07)<< 21) -#define TD_DP 0x00180000 -#define TD_DP_SETUP 0x00000000 -#define TD_DP_IN 0x00100000 -#define TD_DP_OUT 0x00080000 - -#define TD_ISO 0x00010000 -#define TD_DEL 0x00020000 - -/* CC Codes */ -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -#define MAXPSW 1 - -struct td { - __u32 hwINFO; - __u32 hwCBP; /* Current Buffer Pointer */ - __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; - struct td *next_dl_td; - struct usb_device *usb_dev; - int transfer_len; - __u32 data; - - __u32 unused2[2]; -} __attribute((aligned(32))); -typedef struct td td_t; - -#define OHCI_ED_SKIP (1 << 14) - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ - -#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))); - - -/* - * Maximum number of root hub ports. - */ -#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS -# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!" -#endif - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 donehead; - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS]; - } roothub; -} __attribute((aligned(32))); - -/* Some EHCI controls */ -#define EHCI_USBCMD_OFF 0x20 -#define EHCI_USBCMD_HCRESET (1 << 1) - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ -#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ -#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ -#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ -#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ -#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ -#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -# define OHCI_USB_RESET (0 << 6) -# define OHCI_USB_RESUME (1 << 6) -# define OHCI_USB_OPER (2 << 6) -# define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_CLF (1 << 1) /* control list filled */ -#define OHCI_BLF (1 << 2) /* bulk list filled */ -#define OHCI_OCR (1 << 3) /* ownership change request */ -#define OHCI_SOC (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ -#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_OC (1 << 30) /* ownership change */ -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - - -/* Virtual Root HUB */ -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *dev; /* was urb */ - void *int_addr; - int send; - int interval; -}; - -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -/* urb */ -#define N_URB_TD 48 -typedef struct -{ - ed_t *ed; - __u16 length; /* number of tds associated with this request */ - __u16 td_cnt; /* number of tds already serviced */ - struct usb_device *dev; - int state; - unsigned long pipe; - void *transfer_buffer; - int transfer_buffer_length; - int interval; - int actual_length; - int finished; - td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ -} urb_priv_t; -#define URB_DEL 1 - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ - - -typedef struct ohci { - struct ohci_hcca *hcca; /* hcca */ - /*dma_addr_t hcca_dma;*/ - - int irq; - int disabled; /* e.g. got a UE, we're hung */ - int sleeping; - unsigned long flags; /* for HC bugs */ - - struct ohci_regs *regs; /* OHCI controller's memory */ - - int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ - ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ - ed_t *ed_bulktail; /* last endpoint of bulk list */ - ed_t *ed_controltail; /* last endpoint of control list */ - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - struct usb_device *dev[32]; - struct virt_root_hub rh; - - const char *slot_name; -} ohci_t; - -#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ - -struct ohci_device { - ed_t ed[NUM_EDS]; - int ed_cnt; -}; - -/* hcd */ -/* endpoint */ -static int ep_link(ohci_t * ohci, ed_t * ed); -static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, - int interval, int load); - -/*-------------------------------------------------------------------------*/ - -/* we need more TDs than EDs */ -#define NUM_TD 64 - -/* +1 so we can align the storage */ -td_t gtd[NUM_TD+1]; -/* pointers to aligned storage */ -td_t *ptd; - -/* TDs ... */ -static inline struct td * -td_alloc (struct usb_device *usb_dev) -{ - int i; - struct td *td; - - td = NULL; - for (i = 0; i < NUM_TD; i++) - { - if (ptd[i].usb_dev == NULL) - { - td = &ptd[i]; - td->usb_dev = usb_dev; - break; - } - } - - return td; -} - -static inline void -ed_free (struct ed *ed) -{ - ed->usb_dev = NULL; -} diff --git a/drivers/usb/usbdcore.c b/drivers/usb/usbdcore.c deleted file mode 100644 index 53ed669..0000000 --- a/drivers/usb/usbdcore.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usbd/usbd.c.c - USB Device Core Layer - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne , - * Tom Rushworth , - * Bruce Balden - * - * 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. - * - */ - -#include -#include "usbdcore.h" - -#define MAX_INTERFACES 2 - - -int maxstrings = 20; - -/* Global variables ************************************************************************** */ - -struct usb_string_descriptor **usb_strings; - -int usb_devices; - -extern struct usb_function_driver ep0_driver; - -int registered_functions; -int registered_devices; - -char *usbd_device_events[] = { - "DEVICE_UNKNOWN", - "DEVICE_INIT", - "DEVICE_CREATE", - "DEVICE_HUB_CONFIGURED", - "DEVICE_RESET", - "DEVICE_ADDRESS_ASSIGNED", - "DEVICE_CONFIGURED", - "DEVICE_SET_INTERFACE", - "DEVICE_SET_FEATURE", - "DEVICE_CLEAR_FEATURE", - "DEVICE_DE_CONFIGURED", - "DEVICE_BUS_INACTIVE", - "DEVICE_BUS_ACTIVITY", - "DEVICE_POWER_INTERRUPTION", - "DEVICE_HUB_RESET", - "DEVICE_DESTROY", - "DEVICE_FUNCTION_PRIVATE", -}; - -char *usbd_device_states[] = { - "STATE_INIT", - "STATE_CREATED", - "STATE_ATTACHED", - "STATE_POWERED", - "STATE_DEFAULT", - "STATE_ADDRESSED", - "STATE_CONFIGURED", - "STATE_UNKNOWN", -}; - -char *usbd_device_requests[] = { - "GET STATUS", /* 0 */ - "CLEAR FEATURE", /* 1 */ - "RESERVED", /* 2 */ - "SET FEATURE", /* 3 */ - "RESERVED", /* 4 */ - "SET ADDRESS", /* 5 */ - "GET DESCRIPTOR", /* 6 */ - "SET DESCRIPTOR", /* 7 */ - "GET CONFIGURATION", /* 8 */ - "SET CONFIGURATION", /* 9 */ - "GET INTERFACE", /* 10 */ - "SET INTERFACE", /* 11 */ - "SYNC FRAME", /* 12 */ -}; - -char *usbd_device_descriptors[] = { - "UNKNOWN", /* 0 */ - "DEVICE", /* 1 */ - "CONFIG", /* 2 */ - "STRING", /* 3 */ - "INTERFACE", /* 4 */ - "ENDPOINT", /* 5 */ - "DEVICE QUALIFIER", /* 6 */ - "OTHER SPEED", /* 7 */ - "INTERFACE POWER", /* 8 */ -}; - -char *usbd_device_status[] = { - "USBD_OPENING", - "USBD_OK", - "USBD_SUSPENDED", - "USBD_CLOSING", -}; - - -/* Descriptor support functions ************************************************************** */ - - -/** - * usbd_get_string - find and return a string descriptor - * @index: string index to return - * - * Find an indexed string and return a pointer to a it. - */ -struct usb_string_descriptor *usbd_get_string (__u8 index) -{ - if (index >= maxstrings) { - return NULL; - } - return usb_strings[index]; -} - - -/* Access to device descriptor functions ***************************************************** */ - - -/* * - * usbd_device_configuration_instance - find a configuration instance for this device - * @device: - * @configuration: index to configuration, 0 - N-1 - * - * Get specifed device configuration. Index should be bConfigurationValue-1. - */ -static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, - unsigned int port, unsigned int configuration) -{ - if (configuration >= device->configurations) - return NULL; - - return device->configuration_instance_array + configuration; -} - - -/* * - * usbd_device_interface_instance - * @device: - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * - * Return the specified interface descriptor for the specified device. - */ -struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) -{ - struct usb_configuration_instance *configuration_instance; - - if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { - return NULL; - } - if (interface >= configuration_instance->interfaces) { - return NULL; - } - return configuration_instance->interface_instance_array + interface; -} - -/* * - * usbd_device_alternate_descriptor_list - * @device: - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * - * Return the specified alternate descriptor for the specified device. - */ -struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) -{ - struct usb_interface_instance *interface_instance; - - if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { - return NULL; - } - - if (alternate >= interface_instance->alternates) { - return NULL; - } - - return interface_instance->alternates_instance_array + alternate; -} - - -/* * - * usbd_device_device_descriptor - * @device: which device - * @configuration: index to configuration, 0 - N-1 - * @port: which port - * - * Return the specified configuration descriptor for the specified device. - */ -struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) -{ - return (device->device_descriptor); -} - - -/** - * usbd_device_configuration_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * - * Return the specified configuration descriptor for the specified device. - */ -struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct - usb_device_instance - *device, int port, int configuration) -{ - struct usb_configuration_instance *configuration_instance; - if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { - return NULL; - } - return (configuration_instance->configuration_descriptor); -} - - -/** - * usbd_device_interface_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * - * Return the specified interface descriptor for the specified device. - */ -struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance - *device, int port, int configuration, int interface, int alternate) -{ - struct usb_interface_instance *interface_instance; - if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { - return NULL; - } - if ((alternate < 0) || (alternate >= interface_instance->alternates)) { - return NULL; - } - return (interface_instance->alternates_instance_array[alternate].interface_descriptor); -} - -/** - * usbd_device_endpoint_descriptor_index - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: index setting - * @index: which index - * - * Return the specified endpoint descriptor for the specified device. - */ -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance - *device, int port, int configuration, int interface, int alternate, int index) -{ - struct usb_alternate_instance *alternate_instance; - - if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { - return NULL; - } - if (index >= alternate_instance->endpoints) { - return NULL; - } - return *(alternate_instance->endpoints_descriptor_array + index); -} - - -/** - * usbd_device_endpoint_transfersize - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @index: which index - * - * Return the specified endpoint transfer size; - */ -int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) -{ - struct usb_alternate_instance *alternate_instance; - - if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { - return 0; - } - if (index >= alternate_instance->endpoints) { - return 0; - } - return *(alternate_instance->endpoint_transfersize_array + index); -} - - -/** - * usbd_device_endpoint_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * @endpoint: which endpoint - * - * Return the specified endpoint descriptor for the specified device. - */ -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) -{ - struct usb_endpoint_descriptor *endpoint_descriptor; - int i; - - for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { - if (endpoint_descriptor->bEndpointAddress == endpoint) { - return endpoint_descriptor; - } - } - return NULL; -} - -/** - * usbd_endpoint_halted - * @device: point to struct usb_device_instance - * @endpoint: endpoint to check - * - * Return non-zero if endpoint is halted. - */ -int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) -{ - return (device->status == USB_STATUS_HALT); -} - - -/** - * usbd_rcv_complete - complete a receive - * @endpoint: - * @len: - * @urb_bad: - * - * Called from rcv interrupt to complete. - */ -void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad) -{ - if (endpoint) { - struct urb *rcv_urb; - - /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */ - - /* if we had an urb then update actual_length, dispatch if neccessary */ - if ((rcv_urb = endpoint->rcv_urb)) { - - /*usbdbg("actual: %d buffer: %d\n", */ - /*rcv_urb->actual_length, rcv_urb->buffer_length); */ - - /* check the urb is ok, are we adding data less than the packetsize */ - if (!urb_bad && (len <= endpoint->rcv_packetSize)) { - /*usbdbg("updating actual_length by %d\n",len); */ - - /* increment the received data size */ - rcv_urb->actual_length += len; - - } else { - usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", - rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); - - rcv_urb->actual_length = 0; - rcv_urb->status = RECV_ERROR; - } - } else { - usberr("no rcv_urb!"); - } - } else { - usberr("no endpoint!"); - } - -} - -/** - * usbd_tx_complete - complete a transmit - * @endpoint: - * @resetart: - * - * Called from tx interrupt to complete. - */ -void usbd_tx_complete (struct usb_endpoint_instance *endpoint) -{ - if (endpoint) { - struct urb *tx_urb; - - /* if we have a tx_urb advance or reset, finish if complete */ - if ((tx_urb = endpoint->tx_urb)) { - int sent = endpoint->last; - endpoint->sent += sent; - endpoint->last -= sent; - - if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) { - tx_urb->actual_length = 0; - endpoint->sent = 0; - endpoint->last = 0; - - /* Remove from active, save for re-use */ - urb_detach(tx_urb); - urb_append(&endpoint->done, tx_urb); - /*usbdbg("done->next %p, tx_urb %p, done %p", */ - /* endpoint->done.next, tx_urb, &endpoint->done); */ - - endpoint->tx_urb = first_urb_detached(&endpoint->tx); - if( endpoint->tx_urb ) { - endpoint->tx_queue--; - usbdbg("got urb from tx list"); - } - if( !endpoint->tx_urb ) { - /*usbdbg("taking urb from done list"); */ - endpoint->tx_urb = first_urb_detached(&endpoint->done); - } - if( !endpoint->tx_urb ) { - usbdbg("allocating new urb for tx_urb"); - endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint); - } - } - } - } -} - -/* URB linked list functions ***************************************************** */ - -/* - * Initialize an urb_link to be a single element list. - * If the urb_link is being used as a distinguished list head - * the list is empty when the head is the only link in the list. - */ -void urb_link_init (urb_link * ul) -{ - if (ul) { - ul->prev = ul->next = ul; - } -} - -/* - * Detach an urb_link from a list, and set it - * up as a single element list, so no dangling - * pointers can be followed, and so it can be - * joined to another list if so desired. - */ -void urb_detach (struct urb *urb) -{ - if (urb) { - urb_link *ul = &urb->link; - ul->next->prev = ul->prev; - ul->prev->next = ul->next; - urb_link_init (ul); - } -} - -/* - * Return the first urb_link in a list with a distinguished - * head "hd", or NULL if the list is empty. This will also - * work as a predicate, returning NULL if empty, and non-NULL - * otherwise. - */ -urb_link *first_urb_link (urb_link * hd) -{ - urb_link *nx; - if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { - /* There is at least one element in the list */ - /* (besides the distinguished head). */ - return (nx); - } - /* The list is empty */ - return (NULL); -} - -/* - * Return the first urb in a list with a distinguished - * head "hd", or NULL if the list is empty. - */ -struct urb *first_urb (urb_link * hd) -{ - urb_link *nx; - if (NULL == (nx = first_urb_link (hd))) { - /* The list is empty */ - return (NULL); - } - return (p2surround (struct urb, link, nx)); -} - -/* - * Detach and return the first urb in a list with a distinguished - * head "hd", or NULL if the list is empty. - * - */ -struct urb *first_urb_detached (urb_link * hd) -{ - struct urb *urb; - if ((urb = first_urb (hd))) { - urb_detach (urb); - } - return urb; -} - - -/* - * Append an urb_link (or a whole list of - * urb_links) to the tail of another list - * of urb_links. - */ -void urb_append (urb_link * hd, struct urb *urb) -{ - if (hd && urb) { - urb_link *new = &urb->link; - - /* This allows the new urb to be a list of urbs, */ - /* with new pointing at the first, but the link */ - /* must be initialized. */ - /* Order is important here... */ - urb_link *pul = hd->prev; - new->prev->next = hd; - hd->prev = new->prev; - new->prev = pul; - pul->next = new; - } -} - -/* URB create/destroy functions ***************************************************** */ - -/** - * usbd_alloc_urb - allocate an URB appropriate for specified endpoint - * @device: device instance - * @endpoint: endpoint - * - * Allocate an urb structure. The usb device urb structure is used to - * contain all data associated with a transfer, including a setup packet for - * control transfers. - * - * NOTE: endpoint_address MUST contain a direction flag. - */ -struct urb *usbd_alloc_urb (struct usb_device_instance *device, - struct usb_endpoint_instance *endpoint) -{ - struct urb *urb; - - if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) { - usberr (" F A T A L: malloc(%zu) FAILED!!!!", - sizeof (struct urb)); - return NULL; - } - - /* Fill in known fields */ - memset (urb, 0, sizeof (struct urb)); - urb->endpoint = endpoint; - urb->device = device; - urb->buffer = (u8 *) urb->buffer_data; - urb->buffer_length = sizeof (urb->buffer_data); - - urb_link_init (&urb->link); - - return urb; -} - -/** - * usbd_dealloc_urb - deallocate an URB and associated buffer - * @urb: pointer to an urb structure - * - * Deallocate an urb structure and associated data. - */ -void usbd_dealloc_urb (struct urb *urb) -{ - if (urb) { - free (urb); - } -} - -/* Event signaling functions ***************************************************** */ - -/** - * usbd_device_event - called to respond to various usb events - * @device: pointer to struct device - * @event: event to respond to - * - * Used by a Bus driver to indicate an event. - */ -void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) -{ - usb_device_state_t state; - - if (!device || !device->bus) { - usberr("(%p,%d) NULL device or device->bus", device, event); - return; - } - - state = device->device_state; - - usbinfo("%s", usbd_device_events[event]); - - switch (event) { - case DEVICE_UNKNOWN: - break; - case DEVICE_INIT: - device->device_state = STATE_INIT; - break; - - case DEVICE_CREATE: - device->device_state = STATE_ATTACHED; - break; - - case DEVICE_HUB_CONFIGURED: - device->device_state = STATE_POWERED; - break; - - case DEVICE_RESET: - device->device_state = STATE_DEFAULT; - device->address = 0; - break; - - case DEVICE_ADDRESS_ASSIGNED: - device->device_state = STATE_ADDRESSED; - break; - - case DEVICE_CONFIGURED: - device->device_state = STATE_CONFIGURED; - break; - - case DEVICE_DE_CONFIGURED: - device->device_state = STATE_ADDRESSED; - break; - - case DEVICE_BUS_INACTIVE: - if (device->status != USBD_CLOSING) { - device->status = USBD_SUSPENDED; - } - break; - case DEVICE_BUS_ACTIVITY: - if (device->status != USBD_CLOSING) { - device->status = USBD_OK; - } - break; - - case DEVICE_SET_INTERFACE: - break; - case DEVICE_SET_FEATURE: - break; - case DEVICE_CLEAR_FEATURE: - break; - - case DEVICE_POWER_INTERRUPTION: - device->device_state = STATE_POWERED; - break; - case DEVICE_HUB_RESET: - device->device_state = STATE_ATTACHED; - break; - case DEVICE_DESTROY: - device->device_state = STATE_UNKNOWN; - break; - - case DEVICE_FUNCTION_PRIVATE: - break; - - default: - usbdbg("event %d - not handled",event); - break; - } - /*usbdbg("%s event: %d oldstate: %d newstate: %d status: %d address: %d", - device->name, event, state, - device->device_state, device->status, device->address); */ - - /* tell the bus interface driver */ - if( device->event ) { - /* usbdbg("calling device->event"); */ - device->event(device, event, data); - } -} diff --git a/drivers/usb/usbdcore_ep0.c b/drivers/usb/usbdcore_ep0.c deleted file mode 100644 index f6e017d..0000000 --- a/drivers/usb/usbdcore_ep0.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * (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 - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne , - * Tom Rushworth , - * Bruce Balden - * - * 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. - * - */ - -/* - * This is the builtin ep0 control function. It implements all required functionality - * for responding to control requests (SETUP packets). - * - * XXX - * - * Currently we do not pass any SETUP packets (or other) to the configured - * 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 -#include "usbdcore.h" - -#if 0 -#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args) -#else -#define dbg_ep0(lvl,fmt,args...) -#endif - -/* EP0 Configuration Set ********************************************************************* */ - - -/** - * ep0_get_status - fill in URB data with appropriate status - * @device: - * @urb: - * @index: - * @requesttype: - * - */ -static int ep0_get_status (struct usb_device_instance *device, - struct urb *urb, int index, int requesttype) -{ - char *cp; - - urb->actual_length = 2; - cp = (char*)urb->buffer; - cp[0] = cp[1] = 0; - - switch (requesttype) { - case USB_REQ_RECIPIENT_DEVICE: - cp[0] = USB_STATUS_SELFPOWERED; - break; - case USB_REQ_RECIPIENT_INTERFACE: - break; - case USB_REQ_RECIPIENT_ENDPOINT: - cp[0] = usbd_endpoint_halted (device, index); - break; - case USB_REQ_RECIPIENT_OTHER: - urb->actual_length = 0; - default: - break; - } - dbg_ep0 (2, "%02x %02x", cp[0], cp[1]); - return 0; -} - -/** - * ep0_get_one - * @device: - * @urb: - * @result: - * - * Set a single byte value in the urb send buffer. Return non-zero to signal - * a request error. - */ -static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, - __u8 result) -{ - urb->actual_length = 1; /* XXX 2? */ - ((char *) urb->buffer)[0] = result; - return 0; -} - -/** - * copy_config - * @urb: pointer to urb - * @data: pointer to configuration data - * @length: length of data - * - * Copy configuration data to urb transfer buffer if there is room for it. - */ -void copy_config (struct urb *urb, void *data, int max_length, - int max_buf) -{ - int available; - int length; - - /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */ - /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */ - - if (!data) { - dbg_ep0 (1, "data is NULL"); - return; - } - length = max_length; - - if (length > max_length) { - dbg_ep0 (1, "length: %d >= max_length: %d", length, - max_length); - return; - } - /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */ - - if ((available = - /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) { - return; - } - /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ - - if (length > available) { - length = available; - } - /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ - - memcpy (urb->buffer + urb->actual_length, data, length); - urb->actual_length += length; - - dbg_ep0 (3, - "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d", - urb->actual_length, urb->buffer_length, max_buf, max_length, - available); -} - -/** - * ep0_get_descriptor - * @device: - * @urb: - * @max: - * @descriptor_type: - * @index: - * - * Called by ep0_rx_process for a get descriptor device command. Determine what - * descriptor is being requested, copy to send buffer. Return zero if ok to send, - * return non-zero to signal a request error. - */ -static int ep0_get_descriptor (struct usb_device_instance *device, - struct urb *urb, int max, int descriptor_type, - int index) -{ - int port = 0; /* XXX compound device */ - char *cp; - - /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */ - - if (!urb || !urb->buffer || !urb->buffer_length - || (urb->buffer_length < 255)) { - dbg_ep0 (2, "invalid urb %p", urb); - return -1L; - } - - /* setup tx urb */ - urb->actual_length = 0; - cp = (char*)urb->buffer; - - dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); - - switch (descriptor_type) { - case USB_DESCRIPTOR_TYPE_DEVICE: - { - struct usb_device_descriptor *device_descriptor; - if (! - (device_descriptor = - usbd_device_device_descriptor (device, port))) { - return -1; - } - /* copy descriptor for this device */ - copy_config (urb, device_descriptor, - sizeof (struct usb_device_descriptor), - max); - - /* correct the correct control endpoint 0 max packet size into the descriptor */ - device_descriptor = - (struct usb_device_descriptor *) urb->buffer; - - } - dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); - break; - - case USB_DESCRIPTOR_TYPE_CONFIGURATION: - { - struct usb_configuration_descriptor - *configuration_descriptor; - struct usb_device_descriptor *device_descriptor; - if (! - (device_descriptor = - usbd_device_device_descriptor (device, port))) { - return -1; - } - /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */ - if (index >= device_descriptor->bNumConfigurations) { - dbg_ep0 (0, "index too large: %d >= %d", index, - device_descriptor-> - bNumConfigurations); - return -1; - } - - if (! - (configuration_descriptor = - usbd_device_configuration_descriptor (device, - port, - index))) { - dbg_ep0 (0, - "usbd_device_configuration_descriptor failed: %d", - 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, - - 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 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 = - le16_to_cpu (urb->device_request.wIndex); - int bAlternateSetting = 0; - int class = 0; - struct usb_class_descriptor *class_descriptor; - - if (!(class_descriptor = - usbd_device_class_descriptor_index (device, - port, 0, - bNumInterface, - bAlternateSetting, - class)) - || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) { - dbg_ep0 (3, "[%d] interface is not HID", - bNumInterface); - return -1; - } - /* copy descriptor for this class */ - copy_config (urb, class_descriptor, - class_descriptor->descriptor.hid.bLength, - max); -#endif - } - 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 = - le16_to_cpu (urb->device_request.wIndex); - int bAlternateSetting = 0; - int class = 0; - struct usb_class_report_descriptor *report_descriptor; - - if (!(report_descriptor = - usbd_device_class_report_descriptor_index - (device, port, 0, bNumInterface, - bAlternateSetting, class)) - || report_descriptor->bDescriptorType != - USB_DT_REPORT) { - dbg_ep0 (3, "[%d] descriptor is not REPORT", - bNumInterface); - return -1; - } - /* copy report descriptor for this class */ - /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */ - if (max - urb->actual_length > 0) { - int length = - MIN (report_descriptor->wLength, - max - urb->actual_length); - memcpy (urb->buffer + urb->actual_length, - &report_descriptor->bData[0], length); - urb->actual_length += length; - } -#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 tx_packetSize: %2d", - urb->buffer, urb->buffer_length, urb->actual_length, - device->bus->endpoint_array[0].tx_packetSize); -/* - if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) { - dbg_ep0(0, "adding null byte"); - urb->buffer[urb->actual_length++] = 0; - dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d", - urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize); - } -*/ - return 0; - -} - -/** - * ep0_recv_setup - called to indicate URB has been received - * @urb: pointer to struct urb - * - * Check if this is a setup packet, process the device request, put results - * back into the urb and return zero or non-zero to indicate success (DATA) - * or failure (STALL). - * - */ -int ep0_recv_setup (struct urb *urb) -{ - /*struct usb_device_request *request = urb->buffer; */ - /*struct usb_device_instance *device = urb->device; */ - - struct usb_device_request *request; - struct usb_device_instance *device; - int address; - - dbg_ep0 (0, "entering ep0_recv_setup()"); - if (!urb || !urb->device) { - dbg_ep0 (3, "invalid URB %p", urb); - return -1; - } - - request = &urb->device_request; - device = urb->device; - - dbg_ep0 (3, "urb: %p device: %p", urb, urb->device); - - - /*dbg_ep0(2, "- - - - - - - - - -"); */ - - dbg_ep0 (2, - "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s", - request->bmRequestType, request->bRequest, - le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex), - le16_to_cpu (request->wLength), - USBD_DEVICE_REQUESTS (request->bRequest)); - - /* 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 */ - } - - switch (device->device_state) { - case STATE_CREATED: - case STATE_ATTACHED: - case STATE_POWERED: - /* It actually is important to allow requests in these states, - * Windows will request descriptors before assigning an - * address to the client. - */ - - /*dbg_ep0 (1, "request %s not allowed in this state: %s", */ - /* USBD_DEVICE_REQUESTS(request->bRequest), */ - /* usbd_device_states[device->device_state]); */ - /*return -1; */ - break; - - case STATE_INIT: - case STATE_DEFAULT: - switch (request->bRequest) { - case USB_REQ_GET_STATUS: - case USB_REQ_GET_INTERFACE: - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_DESCRIPTOR: - /* case USB_REQ_SET_CONFIGURATION: */ - case USB_REQ_SET_INTERFACE: - dbg_ep0 (1, - "request %s not allowed in DEFAULT state: %s", - USBD_DEVICE_REQUESTS (request->bRequest), - usbd_device_states[device->device_state]); - return -1; - - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_ADDRESS: - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - break; - } - case STATE_ADDRESSED: - case STATE_CONFIGURED: - break; - case STATE_UNKNOWN: - dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s", - USBD_DEVICE_REQUESTS (request->bRequest), - usbd_device_states[device->device_state]); - return -1; - } - - /* handle all requests that return data (direction bit set on bm RequestType) */ - if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { - - dbg_ep0 (3, "Device-to-Host"); - - switch (request->bRequest) { - - case USB_REQ_GET_STATUS: - return ep0_get_status (device, urb, request->wIndex, - request->bmRequestType & - USB_REQ_RECIPIENT_MASK); - - case USB_REQ_GET_DESCRIPTOR: - return ep0_get_descriptor (device, urb, - le16_to_cpu (request->wLength), - le16_to_cpu (request->wValue) >> 8, - 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); - - case USB_REQ_GET_INTERFACE: - return ep0_get_one (device, urb, device->alternate); - - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - return -1; - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_ADDRESS: - case USB_REQ_SET_DESCRIPTOR: - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_INTERFACE: - return -1; - } - } - /* handle the requests that do not return data */ - else { - - - /*dbg_ep0(3, "Host-to-Device"); */ - switch (request->bRequest) { - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - dbg_ep0 (0, "Host-to-Device"); - switch (request-> - bmRequestType & USB_REQ_RECIPIENT_MASK) { - case USB_REQ_RECIPIENT_DEVICE: - /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */ - /* XXX fall through for now as we do not support either */ - case USB_REQ_RECIPIENT_INTERFACE: - case USB_REQ_RECIPIENT_OTHER: - dbg_ep0 (0, "request %s not", - USBD_DEVICE_REQUESTS (request->bRequest)); - default: - return -1; - - case USB_REQ_RECIPIENT_ENDPOINT: - dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue)); - if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) { - /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */ - /* request->bRequest == USB_REQ_SET_FEATURE); */ - /* NEED TO IMPLEMENT THIS!!! */ - return -1; - } else { - dbg_ep0 (1, "request %s bad wValue: %04x", - USBD_DEVICE_REQUESTS - (request->bRequest), - le16_to_cpu (request->wValue)); - return -1; - } - } - - case USB_REQ_SET_ADDRESS: - /* check if this is a re-address, reset first if it is (this shouldn't be possible) */ - if (device->device_state != STATE_DEFAULT) { - dbg_ep0 (1, "set_address: %02x state: %s", - le16_to_cpu (request->wValue), - usbd_device_states[device->device_state]); - return -1; - } - address = le16_to_cpu (request->wValue); - if ((address & 0x7f) != address) { - dbg_ep0 (1, "invalid address %04x %04x", - address, address & 0x7f); - return -1; - } - device->address = address; - - /*dbg_ep0(2, "address: %d %d %d", */ - /* request->wValue, le16_to_cpu(request->wValue), device->address); */ - - return 0; - - case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */ - dbg_ep0 (0, "set descriptor: NOT SUPPORTED"); - return -1; - - case USB_REQ_SET_CONFIGURATION: - /* c.f. 9.4.7 - the top half of wValue is reserved */ - device->configuration = le16_to_cpu(request->wValue) & 0xff; - - /* reset interface and alternate settings */ - device->interface = device->alternate = 0; - - /*dbg_ep0(2, "set configuration: %d", device->configuration); */ - /*serial_printf("DEVICE_CONFIGURED.. event?\n"); */ - return 0; - - case USB_REQ_SET_INTERFACE: - device->interface = le16_to_cpu (request->wIndex); - device->alternate = le16_to_cpu (request->wValue); - /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */ - serial_printf ("DEVICE_SET_INTERFACE.. event?\n"); - return 0; - - case USB_REQ_GET_STATUS: - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - case USB_REQ_GET_INTERFACE: - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - return -1; - } - } - return -1; -} diff --git a/drivers/usb/usbdcore_mpc8xx.c b/drivers/usb/usbdcore_mpc8xx.c deleted file mode 100644 index 0e311ad..0000000 --- a/drivers/usb/usbdcore_mpc8xx.c +++ /dev/null @@ -1,1398 +0,0 @@ -/* - * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit - * bodonoghue@CodeHermit.ie - * - * References - * DasUBoot/drivers/usb/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 -#include -#include -#include "usbdcore.h" -#include "usbdcore_mpc8xx.h" -#include "usbdcore_ep0.h" - -DECLARE_GLOBAL_DATA_PTR; - -#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 *) CONFIG_SYS_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 + CONFIG_SYS_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 + CONFIG_SYS_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 + CONFIG_SYS_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 + CONFIG_SYS_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(CONFIG_SYS_USB_EXTC_CLK) - - /* This has been tested with a 48MHz crystal on CLK6 */ - switch (CONFIG_SYS_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(CONFIG_SYS_USB_BRGCLK) - - /* This has been tested with brgclk == 50MHz */ - 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 (CONFIG_SYS_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 "CONFIG_SYS_USB_EXTC_CLK or CONFIG_SYS_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; -} diff --git a/drivers/usb/usbdcore_omap1510.c b/drivers/usb/usbdcore_omap1510.c deleted file mode 100644 index 6b7b61b..0000000 --- a/drivers/usb/usbdcore_omap1510.c +++ /dev/null @@ -1,1567 +0,0 @@ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usb/device/bi/omap.c - * TI OMAP1510 USB bus interface driver - * - * Author: MontaVista Software, Inc. - * source@mvista.com - * (C) Copyright 2002 - * - * 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 -#include -#ifdef CONFIG_OMAP_SX1 -#include -#endif - -#include "usbdcore.h" -#include "usbdcore_omap1510.h" -#include "usbdcore_ep0.h" - - -#define UDC_INIT_MDELAY 80 /* Device settle delay */ -#define UDC_MAX_ENDPOINTS 31 /* Number of endpoints on this UDC */ - -/* Some kind of debugging output... */ -#if 1 -#define UDCDBG(str) -#define UDCDBGA(fmt,args...) -#else /* The bugs still exists... */ -#define UDCDBG(str) serial_printf("[%s] %s:%d: " str "\n", __FILE__,__FUNCTION__,__LINE__) -#define UDCDBGA(fmt,args...) serial_printf("[%s] %s:%d: " fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##args) -#endif - -#if 1 -#define UDCREG(name) -#define UDCREGL(name) -#else /* The bugs still exists... */ -#define UDCREG(name) serial_printf("%s():%d: %s[%08x]=%.4x\n",__FUNCTION__,__LINE__, (#name), name, inw(name)) /* For 16-bit regs */ -#define UDCREGL(name) serial_printf("%s():%d: %s[%08x]=%.8x\n",__FUNCTION__,__LINE__, (#name), name, inl(name)) /* For 32-bit regs */ -#endif - - -static struct urb *ep0_urb = NULL; - -static struct usb_device_instance *udc_device; /* Used in interrupt handler */ -static u16 udc_devstat = 0; /* UDC status (DEVSTAT) */ -static u32 udc_interrupts = 0; - -static void udc_stall_ep (unsigned int ep_addr); - - -static struct usb_endpoint_instance *omap1510_find_ep (int ep) -{ - int i; - - for (i = 0; i < udc_device->bus->max_endpoints; i++) { - if (udc_device->bus->endpoint_array[i].endpoint_address == ep) - return &udc_device->bus->endpoint_array[i]; - } - return NULL; -} - -/* ************************************************************************** */ -/* IO - */ - -/* - * omap1510_prepare_endpoint_for_rx - * - * This function implements TRM Figure 14-11. - * - * The endpoint to prepare for transfer is specified as a physical endpoint - * number. For OUT (rx) endpoints 1 through 15, the corresponding endpoint - * configuration register is checked to see if the endpoint is ISO or not. - * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled. - * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30. - */ -static void omap1510_prepare_endpoint_for_rx (int ep_addr) -{ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("omap1510_prepare_endpoint %x", ep_addr); - if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)) { - if ((inw (UDC_EP_RX (ep_num)) & - (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) == - UDC_EPn_RX_Valid) { - /* rx endpoint is valid, non-ISO, so enable its FIFO */ - outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); - outw (UDC_Set_FIFO_En, UDC_CTRL); - outw (0, UDC_EP_NUM); - } - } -} - -/* omap1510_configure_endpoints - * - * This function implements TRM Figure 14-10. - */ -static void omap1510_configure_endpoints (struct usb_device_instance *device) -{ - int ep; - struct usb_bus_instance *bus; - struct usb_endpoint_instance *endpoint; - unsigned short ep_ptr; - unsigned short ep_size; - unsigned short ep_isoc; - unsigned short ep_doublebuffer; - int ep_addr; - int packet_size; - int buffer_size; - int attributes; - - bus = device->bus; - - /* There is a dedicated 2048 byte buffer for USB packets that may be - * arbitrarily partitioned among the endpoints on 8-byte boundaries. - * The first 8 bytes are reserved for receiving setup packets on - * endpoint 0. - */ - ep_ptr = 8; /* reserve the first 8 bytes for the setup fifo */ - - for (ep = 0; ep < bus->max_endpoints; ep++) { - endpoint = bus->endpoint_array + ep; - ep_addr = endpoint->endpoint_address; - if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - /* IN endpoint */ - packet_size = endpoint->tx_packetSize; - attributes = endpoint->tx_attributes; - } else { - /* OUT endpoint */ - packet_size = endpoint->rcv_packetSize; - attributes = endpoint->rcv_attributes; - } - - switch (packet_size) { - case 0: - ep_size = 0; - break; - case 8: - ep_size = 0; - break; - case 16: - ep_size = 1; - break; - case 32: - ep_size = 2; - break; - case 64: - ep_size = 3; - break; - case 128: - ep_size = 4; - break; - case 256: - ep_size = 5; - break; - case 512: - ep_size = 6; - break; - default: - UDCDBGA ("ep 0x%02x has bad packet size %d", - ep_addr, packet_size); - packet_size = 0; - ep_size = 0; - break; - } - - switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_CONTROL: - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_INT: - default: - /* A non-isochronous endpoint may optionally be - * double-buffered. For now we disable - * double-buffering. - */ - ep_doublebuffer = 0; - ep_isoc = 0; - if (packet_size > 64) - packet_size = 0; - if (!ep || !ep_doublebuffer) - buffer_size = packet_size; - else - buffer_size = packet_size * 2; - break; - case USB_ENDPOINT_XFER_ISOC: - /* Isochronous endpoints are always double- - * buffered, but the double-buffering bit - * in the endpoint configuration register - * becomes the msb of the endpoint size so we - * set the double-buffering flag to zero. - */ - ep_doublebuffer = 0; - ep_isoc = 1; - buffer_size = packet_size * 2; - break; - } - - /* check to see if our packet buffer RAM is exhausted */ - if ((ep_ptr + buffer_size) > 2048) { - UDCDBGA ("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size); - buffer_size = packet_size = 0; - } - - /* force a default configuration for endpoint 0 since it is - * always enabled - */ - if (!ep && ((packet_size < 8) || (packet_size > 64))) { - buffer_size = packet_size = 64; - ep_size = 3; - } - - if (!ep) { - /* configure endpoint 0 */ - outw ((ep_size << 12) | (ep_ptr >> 3), UDC_EP0); - /*UDCDBGA("ep 0 buffer offset 0x%03x packet size 0x%03x", */ - /* ep_ptr, packet_size); */ - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - /* IN endpoint */ - if (packet_size) { - outw ((1 << 15) | (ep_doublebuffer << 14) | - (ep_size << 12) | (ep_isoc << 11) | - (ep_ptr >> 3), - UDC_EP_TX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - UDCDBGA ("IN ep %d buffer offset 0x%03x" - " packet size 0x%03x", - ep_addr & USB_ENDPOINT_NUMBER_MASK, - ep_ptr, packet_size); - } else { - outw (0, - UDC_EP_TX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - } - } else { - /* OUT endpoint */ - if (packet_size) { - outw ((1 << 15) | (ep_doublebuffer << 14) | - (ep_size << 12) | (ep_isoc << 11) | - (ep_ptr >> 3), - UDC_EP_RX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - UDCDBGA ("OUT ep %d buffer offset 0x%03x" - " packet size 0x%03x", - ep_addr & USB_ENDPOINT_NUMBER_MASK, - ep_ptr, packet_size); - } else { - outw (0, - UDC_EP_RX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - } - } - ep_ptr += buffer_size; - } -} - -/* omap1510_deconfigure_device - * - * This function balances omap1510_configure_device. - */ -static void omap1510_deconfigure_device (void) -{ - int epnum; - - UDCDBG ("clear Cfg_Lock"); - outw (inw (UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1); - UDCREG (UDC_SYSCON1); - - /* deconfigure all endpoints */ - for (epnum = 1; epnum <= 15; epnum++) { - outw (0, UDC_EP_RX (epnum)); - outw (0, UDC_EP_TX (epnum)); - } -} - -/* omap1510_configure_device - * - * This function implements TRM Figure 14-9. - */ -static void omap1510_configure_device (struct usb_device_instance *device) -{ - omap1510_configure_endpoints (device); - - - /* Figure 14-9 indicates we should enable interrupts here, but we have - * other routines (udc_all_interrupts, udc_suspended_interrupts) to - * do that. - */ - - UDCDBG ("set Cfg_Lock"); - outw (inw (UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1); - UDCREG (UDC_SYSCON1); -} - -/* omap1510_write_noniso_tx_fifo - * - * This function implements TRM Figure 14-30. - * - * If the endpoint has an active tx_urb, then the next packet of data from the - * URB is written to the tx FIFO. The total amount of data in the urb is given - * by urb->actual_length. The maximum amount of data that can be sent in any - * one packet is given by endpoint->tx_packetSize. The number of data bytes - * from this URB that have already been transmitted is given by endpoint->sent. - * endpoint->last is updated by this routine with the number of data bytes - * transmitted in this packet. - * - * In accordance with Figure 14-30, the EP_NUM register must already have been - * written with the value to select the appropriate tx FIFO before this routine - * is called. - */ -static void omap1510_write_noniso_tx_fifo (struct usb_endpoint_instance - *endpoint) -{ - struct urb *urb = endpoint->tx_urb; - - if (urb) { - unsigned int last, i; - - UDCDBGA ("urb->buffer %p, buffer_length %d, actual_length %d", - urb->buffer, urb->buffer_length, urb->actual_length); - if ((last = - MIN (urb->actual_length - endpoint->sent, - endpoint->tx_packetSize))) { - u8 *cp = urb->buffer + endpoint->sent; - - UDCDBGA ("endpoint->sent %d, tx_packetSize %d, last %d", endpoint->sent, endpoint->tx_packetSize, last); - - if (((u32) cp & 1) == 0) { /* word aligned? */ - outsw (UDC_DATA, cp, last >> 1); - } else { /* byte aligned. */ - for (i = 0; i < (last >> 1); i++) { - u16 w = ((u16) cp[2 * i + 1] << 8) | - (u16) cp[2 * i]; - outw (w, UDC_DATA); - } - } - if (last & 1) { - outb (*(cp + last - 1), UDC_DATA); - } - } - endpoint->last = last; - } -} - -/* omap1510_read_noniso_rx_fifo - * - * This function implements TRM Figure 14-28. - * - * If the endpoint has an active rcv_urb, then the next packet of data is read - * from the rcv FIFO and written to rcv_urb->buffer at offset - * rcv_urb->actual_length to append the packet data to the data from any - * previous packets for this transfer. We assume that there is sufficient room - * left in the buffer to hold an entire packet of data. - * - * The return value is the number of bytes read from the FIFO for this packet. - * - * In accordance with Figure 14-28, the EP_NUM register must already have been - * written with the value to select the appropriate rcv FIFO before this routine - * is called. - */ -static int omap1510_read_noniso_rx_fifo (struct usb_endpoint_instance - *endpoint) -{ - struct urb *urb = endpoint->rcv_urb; - int len = 0; - - if (urb) { - len = inw (UDC_RXFSTAT); - - if (len) { - unsigned char *cp = urb->buffer + urb->actual_length; - - insw (UDC_DATA, cp, len >> 1); - if (len & 1) - *(cp + len - 1) = inb (UDC_DATA); - } - } - return len; -} - -/* omap1510_prepare_for_control_write_status - * - * This function implements TRM Figure 14-17. - * - * We have to deal here with non-autodecoded control writes that haven't already - * been dealt with by ep0_recv_setup. The non-autodecoded standard control - * write requests are: set/clear endpoint feature, set configuration, set - * interface, and set descriptor. ep0_recv_setup handles set/clear requests for - * ENDPOINT_HALT by halting the endpoint for a set request and resetting the - * endpoint for a clear request. ep0_recv_setup returns an error for - * SET_DESCRIPTOR requests which causes them to be terminated with a stall by - * the setup handler. A SET_INTERFACE request is handled by ep0_recv_setup by - * generating a DEVICE_SET_INTERFACE event. This leaves only the - * SET_CONFIGURATION event for us to deal with here. - * - */ -static void omap1510_prepare_for_control_write_status (struct urb *urb) -{ - struct usb_device_request *request = &urb->device_request;; - - /* check for a SET_CONFIGURATION request */ - if (request->bRequest == USB_REQ_SET_CONFIGURATION) { - int configuration = le16_to_cpu (request->wValue) & 0xff; - unsigned short devstat = inw (UDC_DEVSTAT); - - if ((devstat & (UDC_ADD | UDC_CFG)) == UDC_ADD) { - /* device is currently in ADDRESSED state */ - if (configuration) { - /* Assume the specified non-zero configuration - * value is valid and switch to the CONFIGURED - * state. - */ - outw (UDC_Dev_Cfg, UDC_SYSCON2); - } - } else if ((devstat & UDC_CFG) == UDC_CFG) { - /* device is currently in CONFIGURED state */ - if (!configuration) { - /* Switch to ADDRESSED state. */ - outw (UDC_Clr_Cfg, UDC_SYSCON2); - } - } - } - - /* select EP0 tx FIFO */ - outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - /* clear endpoint (no data bytes in status stage) */ - outw (UDC_Clr_EP, UDC_CTRL); - /* enable the EP0 tx FIFO */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the endpoint */ - outw (UDC_EP_Dir, UDC_EP_NUM); -} - -/* 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. - * - * These functions must only be called with interrupts disabled. - */ -static void 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 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; - } - } -} - -/* Handle all device state changes. - * This function implements TRM Figure 14-21. - */ -static void omap1510_udc_state_changed (void) -{ - u16 bits; - u16 devstat = inw (UDC_DEVSTAT); - - UDCDBGA ("state changed, devstat %x, old %x", devstat, udc_devstat); - - bits = devstat ^ udc_devstat; - if (bits) { - if (bits & UDC_ATT) { - if (devstat & UDC_ATT) { - UDCDBG ("device attached and powered"); - udc_state_transition_up (udc_device->device_state, STATE_POWERED); - } else { - UDCDBG ("device detached or unpowered"); - udc_state_transition_down (udc_device->device_state, STATE_ATTACHED); - } - } - if (bits & UDC_USB_Reset) { - if (devstat & UDC_USB_Reset) { - UDCDBG ("device reset in progess"); - udc_state_transition_down (udc_device->device_state, STATE_POWERED); - } else { - UDCDBG ("device reset completed"); - } - } - if (bits & UDC_DEF) { - if (devstat & UDC_DEF) { - UDCDBG ("device entering default state"); - udc_state_transition_up (udc_device->device_state, STATE_DEFAULT); - } else { - UDCDBG ("device leaving default state"); - udc_state_transition_down (udc_device->device_state, STATE_POWERED); - } - } - if (bits & UDC_SUS) { - if (devstat & UDC_SUS) { - UDCDBG ("entering suspended state"); - usbd_device_event_irq (udc_device, DEVICE_BUS_INACTIVE, 0); - } else { - UDCDBG ("leaving suspended state"); - usbd_device_event_irq (udc_device, DEVICE_BUS_ACTIVITY, 0); - } - } - if (bits & UDC_R_WK_OK) { - UDCDBGA ("remote wakeup %s", (devstat & UDC_R_WK_OK) - ? "enabled" : "disabled"); - } - if (bits & UDC_ADD) { - if (devstat & UDC_ADD) { - UDCDBG ("default -> addressed"); - udc_state_transition_up (udc_device->device_state, STATE_ADDRESSED); - } else { - UDCDBG ("addressed -> default"); - udc_state_transition_down (udc_device->device_state, STATE_DEFAULT); - } - } - if (bits & UDC_CFG) { - if (devstat & UDC_CFG) { - UDCDBG ("device configured"); - /* The ep0_recv_setup function generates the - * DEVICE_CONFIGURED event when a - * USB_REQ_SET_CONFIGURATION setup packet is - * received, so we should already be in the - * state STATE_CONFIGURED. - */ - udc_state_transition_up (udc_device->device_state, STATE_CONFIGURED); - } else { - UDCDBG ("device deconfigured"); - udc_state_transition_down (udc_device->device_state, STATE_ADDRESSED); - } - } - } - - /* Clear interrupt source */ - outw (UDC_DS_Chg, UDC_IRQ_SRC); - - /* Save current DEVSTAT */ - udc_devstat = devstat; -} - -/* Handle SETUP USB interrupt. - * This function implements TRM Figure 14-14. - */ -static void omap1510_udc_setup (struct usb_endpoint_instance *endpoint) -{ - UDCDBG ("-> Entering device setup"); - - do { - const int setup_pktsize = 8; - unsigned char *datap = - (unsigned char *) &ep0_urb->device_request; - - /* Gain access to EP 0 setup FIFO */ - outw (UDC_Setup_Sel, UDC_EP_NUM); - - /* Read control request data */ - insb (UDC_DATA, datap, setup_pktsize); - - UDCDBGA ("EP0 setup read [%x %x %x %x %x %x %x %x]", - *(datap + 0), *(datap + 1), *(datap + 2), - *(datap + 3), *(datap + 4), *(datap + 5), - *(datap + 6), *(datap + 7)); - - /* Reset EP0 setup FIFO */ - outw (0, UDC_EP_NUM); - } while (inw (UDC_IRQ_SRC) & UDC_Setup); - - /* Try to process setup packet */ - if (ep0_recv_setup (ep0_urb)) { - /* Not a setup packet, stall next EP0 transaction */ - udc_stall_ep (0); - UDCDBG ("can't parse setup packet, still waiting for setup"); - return; - } - - /* Check direction */ - if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) - == USB_REQ_HOST2DEVICE) { - UDCDBG ("control write on EP0"); - if (le16_to_cpu (ep0_urb->device_request.wLength)) { - /* We don't support control write data stages. - * The only standard control write request with a data - * stage is SET_DESCRIPTOR, and ep0_recv_setup doesn't - * support that so we just stall those requests. A - * function driver might support a non-standard - * write request with a data stage, but it isn't - * obvious what we would do with the data if we read it - * so we'll just stall it. It seems like the API isn't - * quite right here. - */ -#if 0 - /* Here is what we would do if we did support control - * write data stages. - */ - ep0_urb->actual_length = 0; - outw (0, UDC_EP_NUM); - /* enable the EP0 rx FIFO */ - outw (UDC_Set_FIFO_En, UDC_CTRL); -#else - /* Stall this request */ - UDCDBG ("Stalling unsupported EP0 control write data " - "stage."); - udc_stall_ep (0); -#endif - } else { - omap1510_prepare_for_control_write_status (ep0_urb); - } - } else { - UDCDBG ("control read on EP0"); - /* The ep0_recv_setup function has already placed our response - * packet data in ep0_urb->buffer and the packet length in - * ep0_urb->actual_length. - */ - endpoint->tx_urb = ep0_urb; - endpoint->sent = 0; - /* select the EP0 tx FIFO */ - outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - /* Write packet data to the FIFO. omap1510_write_noniso_tx_fifo - * will update endpoint->last with the number of bytes written - * to the FIFO. - */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable the FIFO to start the packet transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the EP0 tx FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } - - UDCDBG ("<- Leaving device setup"); -} - -/* Handle endpoint 0 RX interrupt - * This routine implements TRM Figure 14-16. - */ -static void omap1510_udc_ep0_rx (struct usb_endpoint_instance *endpoint) -{ - unsigned short status; - - UDCDBG ("RX on EP0"); - /* select EP0 rx FIFO */ - outw (UDC_EP_Sel, UDC_EP_NUM); - - status = inw (UDC_STAT_FLG); - - if (status & UDC_ACK) { - /* Check direction */ - if ((ep0_urb->device_request.bmRequestType - & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { - /* This rx interrupt must be for a control write data - * stage packet. - * - * We don't support control write data stages. - * We should never end up here. - */ - - /* clear the EP0 rx FIFO */ - outw (UDC_Clr_EP, UDC_CTRL); - - /* deselect the EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - - UDCDBG ("Stalling unexpected EP0 control write " - "data stage packet"); - udc_stall_ep (0); - } else { - /* This rx interrupt must be for a control read status - * stage packet. - */ - UDCDBG ("ACK on EP0 control read status stage packet"); - /* deselect EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - } - } else if (status & UDC_STALL) { - UDCDBG ("EP0 stall during RX"); - /* deselect EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - } else { - /* deselect EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - } -} - -/* Handle endpoint 0 TX interrupt - * This routine implements TRM Figure 14-18. - */ -static void omap1510_udc_ep0_tx (struct usb_endpoint_instance *endpoint) -{ - unsigned short status; - struct usb_device_request *request = &ep0_urb->device_request; - - UDCDBG ("TX on EP0"); - /* select EP0 TX FIFO */ - outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - - status = inw (UDC_STAT_FLG); - if (status & UDC_ACK) { - /* Check direction */ - if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) == - USB_REQ_HOST2DEVICE) { - /* This tx interrupt must be for a control write status - * stage packet. - */ - UDCDBG ("ACK on EP0 control write status stage packet"); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } else { - /* This tx interrupt must be for a control read data - * stage packet. - */ - int wLength = le16_to_cpu (request->wLength); - - /* Update our count of bytes sent so far in this - * transfer. - */ - endpoint->sent += endpoint->last; - - /* We are finished with this transfer if we have sent - * all of the bytes in our tx urb (urb->actual_length) - * unless we need a zero-length terminating packet. We - * need a zero-length terminating packet if we returned - * fewer bytes than were requested (wLength) by the host, - * and the number of bytes we returned is an exact - * multiple of the packet size endpoint->tx_packetSize. - */ - if ((endpoint->sent == ep0_urb->actual_length) - && ((ep0_urb->actual_length == wLength) - || (endpoint->last != - endpoint->tx_packetSize))) { - /* Done with control read data stage. */ - UDCDBG ("control read data stage complete"); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - /* select EP0 RX FIFO to prepare for control - * read status stage. - */ - outw (UDC_EP_Sel, UDC_EP_NUM); - /* clear the EP0 RX FIFO */ - outw (UDC_Clr_EP, UDC_CTRL); - /* enable the EP0 RX FIFO */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the EP0 RX FIFO */ - outw (0, UDC_EP_NUM); - } else { - /* We still have another packet of data to send - * in this control read data stage or else we - * need a zero-length terminating packet. - */ - UDCDBG ("ACK control read data stage packet"); - omap1510_write_noniso_tx_fifo (endpoint); - /* enable the EP0 tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } - } - } else if (status & UDC_STALL) { - UDCDBG ("EP0 stall during TX"); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } else { - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } -} - -/* Handle RX transaction on non-ISO endpoint. - * This function implements TRM Figure 14-27. - * The ep argument is a physical endpoint number for a non-ISO OUT endpoint - * in the range 1 to 15. - */ -static void omap1510_udc_epn_rx (int ep) -{ - unsigned short status; - - /* Check endpoint status */ - status = inw (UDC_STAT_FLG); - - if (status & UDC_ACK) { - int nbytes; - struct usb_endpoint_instance *endpoint = - omap1510_find_ep (ep); - - nbytes = omap1510_read_noniso_rx_fifo (endpoint); - usbd_rcv_complete (endpoint, nbytes, 0); - - /* enable rx FIFO to prepare for next packet */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - } else if (status & UDC_STALL) { - UDCDBGA ("STALL on RX endpoint %d", ep); - } else if (status & UDC_NAK) { - UDCDBGA ("NAK on RX ep %d", ep); - } else { - serial_printf ("omap-bi: RX on ep %d with status %x", ep, - status); - } -} - -/* Handle TX transaction on non-ISO endpoint. - * This function implements TRM Figure 14-29. - * The ep argument is a physical endpoint number for a non-ISO IN endpoint - * in the range 16 to 30. - */ -static void omap1510_udc_epn_tx (int ep) -{ - unsigned short status; - - /*serial_printf("omap1510_udc_epn_tx( %x )\n",ep); */ - - /* Check endpoint status */ - status = inw (UDC_STAT_FLG); - - if (status & UDC_ACK) { - struct usb_endpoint_instance *endpoint = - omap1510_find_ep (ep); - - /* We need to transmit a terminating zero-length packet now if - * we have sent all of the data in this URB and the transfer - * size was an exact multiple of the packet size. - */ - if (endpoint->tx_urb - && (endpoint->last == endpoint->tx_packetSize) - && (endpoint->tx_urb->actual_length - endpoint->sent - - endpoint->last == 0)) { - /* Prepare to transmit a zero-length packet. */ - endpoint->sent += endpoint->last; - /* write 0 bytes of data to FIFO */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - } else if (endpoint->tx_urb - && endpoint->tx_urb->actual_length) { - /* retire the data that was just sent */ - usbd_tx_complete (endpoint); - /* Check to see if we have more data ready to transmit - * now. - */ - if (endpoint->tx_urb - && endpoint->tx_urb->actual_length) { - /* write data to FIFO */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - } - } - } else if (status & UDC_STALL) { - UDCDBGA ("STALL on TX endpoint %d", ep); - } else if (status & UDC_NAK) { - UDCDBGA ("NAK on TX endpoint %d", ep); - } else { - /*serial_printf("omap-bi: TX on ep %d with status %x\n", ep, status); */ - } -} - - -/* -------------------------------------------------------------------------------- -*/ - -/* Handle general USB interrupts and dispatch according to type. - * This function implements TRM Figure 14-13. - */ -void omap1510_udc_irq (void) -{ - u16 irq_src = inw (UDC_IRQ_SRC); - int valid_irq = 0; - - if (!(irq_src & ~UDC_SOF_Flg)) /* ignore SOF interrupts ) */ - return; - - UDCDBGA ("< IRQ #%d start >- %x", udc_interrupts, irq_src); - /*serial_printf("< IRQ #%d start >- %x\n", udc_interrupts, irq_src); */ - - if (irq_src & UDC_DS_Chg) { - /* Device status changed */ - omap1510_udc_state_changed (); - valid_irq++; - } - if (irq_src & UDC_EP0_RX) { - /* Endpoint 0 receive */ - outw (UDC_EP0_RX, UDC_IRQ_SRC); /* ack interrupt */ - omap1510_udc_ep0_rx (udc_device->bus->endpoint_array + 0); - valid_irq++; - } - if (irq_src & UDC_EP0_TX) { - /* Endpoint 0 transmit */ - outw (UDC_EP0_TX, UDC_IRQ_SRC); /* ack interrupt */ - omap1510_udc_ep0_tx (udc_device->bus->endpoint_array + 0); - valid_irq++; - } - if (irq_src & UDC_Setup) { - /* Device setup */ - omap1510_udc_setup (udc_device->bus->endpoint_array + 0); - valid_irq++; - } - /*if (!valid_irq) */ - /* serial_printf("unknown interrupt, IRQ_SRC %.4x\n", irq_src); */ - UDCDBGA ("< IRQ #%d end >", udc_interrupts); - udc_interrupts++; -} - -/* This function implements TRM Figure 14-26. */ -void omap1510_udc_noniso_irq (void) -{ - unsigned short epnum; - unsigned short irq_src = inw (UDC_IRQ_SRC); - int valid_irq = 0; - - if (!(irq_src & (UDC_EPn_RX | UDC_EPn_TX))) - return; - - UDCDBGA ("non-ISO IRQ, IRQ_SRC %x", inw (UDC_IRQ_SRC)); - - if (irq_src & UDC_EPn_RX) { /* Endpoint N OUT transaction */ - /* Determine the endpoint number for this interrupt */ - epnum = (inw (UDC_EPN_STAT) & 0x0f00) >> 8; - UDCDBGA ("RX on ep %x", epnum); - - /* acknowledge interrupt */ - outw (UDC_EPn_RX, UDC_IRQ_SRC); - - if (epnum) { - /* select the endpoint FIFO */ - outw (UDC_EP_Sel | epnum, UDC_EP_NUM); - - omap1510_udc_epn_rx (epnum); - - /* deselect the endpoint FIFO */ - outw (epnum, UDC_EP_NUM); - } - valid_irq++; - } - if (irq_src & UDC_EPn_TX) { /* Endpoint N IN transaction */ - /* Determine the endpoint number for this interrupt */ - epnum = (inw (UDC_EPN_STAT) & 0x000f) | USB_DIR_IN; - UDCDBGA ("TX on ep %x", epnum); - - /* acknowledge interrupt */ - outw (UDC_EPn_TX, UDC_IRQ_SRC); - - if (epnum) { - /* select the endpoint FIFO */ - outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); - - omap1510_udc_epn_tx (epnum); - - /* deselect the endpoint FIFO */ - outw (UDC_EP_Dir | epnum, UDC_EP_NUM); - } - valid_irq++; - } - if (!valid_irq) - serial_printf (": unknown non-ISO interrupt, IRQ_SRC %.4x\n", - irq_src); -} - -/* -------------------------------------------------------------------------------- -*/ - - -/* - * Start of public functions. - */ - -/* Called to start packet transmission. */ -int udc_endpoint_write (struct usb_endpoint_instance *endpoint) -{ - unsigned short epnum = - endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("Starting transmit on ep %x", epnum); - - if (endpoint->tx_urb) { - /* select the endpoint FIFO */ - outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); - /* write data to FIFO */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the endpoint FIFO */ - outw (UDC_EP_Dir | epnum, UDC_EP_NUM); - } - - return 0; -} - -/* Start to initialize h/w stuff */ -int udc_init (void) -{ - u16 udc_rev; - uchar value; - ulong gpio; - int i; - - /* Let the device settle down before we start */ - for (i = 0; i < UDC_INIT_MDELAY; i++) udelay(1000); - - udc_device = NULL; - - UDCDBG ("starting"); - - /* Check peripheral reset. Must be 1 to make sure - MPU TIPB peripheral reset is inactive */ - UDCREG (ARM_RSTCT2); - - /* Set and check clock control. - * We might ought to be using the clock control API to do - * this instead of fiddling with the clock registers directly - * here. - */ - outw ((1 << 4) | (1 << 5), CLOCK_CTRL); - UDCREG (CLOCK_CTRL); - -#ifdef CONFIG_OMAP1510 - /* This code was originally implemented for OMAP1510 and - * therefore is only applicable for OMAP1510 boards. For - * OMAP5912 or OMAP16xx the register APLL_CTRL does not - * exist and DPLL_CTRL is already configured. - */ - - /* Set and check APLL */ - outw (0x0008, APLL_CTRL); - UDCREG (APLL_CTRL); - /* Set and check DPLL */ - outw (0x2210, DPLL_CTRL); - UDCREG (DPLL_CTRL); -#endif - /* Set and check SOFT - * The below line of code has been changed to perform a - * read-modify-write instead of a simple write for - * configuring the SOFT_REQ register. This allows the code - * to be compatible with OMAP5912 and OMAP16xx devices - */ - outw ((1 << 4) | (1 << 3) | 1 | (inw(SOFT_REQ)), SOFT_REQ); - - /* Short delay to wait for DPLL */ - udelay (1000); - - /* Print banner with device revision */ - udc_rev = inw (UDC_REV) & 0xff; -#ifdef CONFIG_OMAP1510 - printf ("USB: TI OMAP1510 USB function module rev %d.%d\n", - udc_rev >> 4, udc_rev & 0xf); -#endif - -#ifdef CONFIG_OMAP1610 - printf ("USB: TI OMAP5912 USB function module rev %d.%d\n", - udc_rev >> 4, udc_rev & 0xf); -#endif - -#ifdef CONFIG_OMAP_SX1 - i2c_read (0x32, 0x04, 1, &value, 1); - value |= 0x04; - i2c_write (0x32, 0x04, 1, &value, 1); - - i2c_read (0x32, 0x03, 1, &value, 1); - value |= 0x01; - i2c_write (0x32, 0x03, 1, &value, 1); - - gpio = inl(GPIO_PIN_CONTROL_REG); - gpio |= 0x0002; /* A_IRDA_OFF */ - gpio |= 0x0800; /* A_SWITCH */ - gpio |= 0x8000; /* A_USB_ON */ - outl (gpio, GPIO_PIN_CONTROL_REG); - - gpio = inl(GPIO_DIR_CONTROL_REG); - gpio &= ~0x0002; /* A_IRDA_OFF */ - gpio &= ~0x0800; /* A_SWITCH */ - gpio &= ~0x8000; /* A_USB_ON */ - outl (gpio, GPIO_DIR_CONTROL_REG); - - gpio = inl(GPIO_DATA_OUTPUT_REG); - gpio |= 0x0002; /* A_IRDA_OFF */ - gpio &= ~0x0800; /* A_SWITCH */ - gpio &= ~0x8000; /* A_USB_ON */ - outl (gpio, GPIO_DATA_OUTPUT_REG); -#endif - - /* The VBUS_MODE bit selects whether VBUS detection is done via - * software (1) or hardware (0). When software detection is - * selected, VBUS_CTRL selects whether USB is not connected (0) - * or connected (1). - */ - outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); - outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); - UDCREGL (FUNC_MUX_CTRL_0); - - /* - * At this point, device is ready for configuration... - */ - - UDCDBG ("disable USB interrupts"); - outw (0, UDC_IRQ_EN); - UDCREG (UDC_IRQ_EN); - - UDCDBG ("disable USB DMA"); - outw (0, UDC_DMA_IRQ_EN); - UDCREG (UDC_DMA_IRQ_EN); - - UDCDBG ("initialize SYSCON1"); - outw (UDC_Self_Pwr | UDC_Pullup_En, UDC_SYSCON1); - UDCREG (UDC_SYSCON1); - - return 0; -} - -/* Stall endpoint */ -static void udc_stall_ep (unsigned int ep_addr) -{ - /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("stall ep_addr %d", ep_addr); - - /* REVISIT? - * The OMAP TRM section 14.2.4.2 says we must check that the FIFO - * is empty before halting the endpoint. The current implementation - * doesn't check that the FIFO is empty. - */ - - if (!ep_num) { - outw (UDC_Stall_Cmd, UDC_SYSCON2); - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { - /* we have a valid rx endpoint, so halt it */ - outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); - outw (UDC_Set_Halt, UDC_CTRL); - outw (ep_num, UDC_EP_NUM); - } - } else { - if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { - /* we have a valid tx endpoint, so halt it */ - outw (UDC_EP_Sel | UDC_EP_Dir | ep_num, UDC_EP_NUM); - outw (UDC_Set_Halt, UDC_CTRL); - outw (ep_num, UDC_EP_NUM); - } - } -} - -/* Reset endpoint */ -#if 0 -static void udc_reset_ep (unsigned int ep_addr) -{ - /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("reset ep_addr %d", ep_addr); - - if (!ep_num) { - /* control endpoint 0 can't be reset */ - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - UDCDBGA ("UDC_EP_RX(%d) = 0x%04x", ep_num, - inw (UDC_EP_RX (ep_num))); - if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { - /* we have a valid rx endpoint, so reset it */ - outw (ep_num | UDC_EP_Sel, UDC_EP_NUM); - outw (UDC_Reset_EP, UDC_CTRL); - outw (ep_num, UDC_EP_NUM); - UDCDBGA ("OUT endpoint %d reset", ep_num); - } - } else { - UDCDBGA ("UDC_EP_TX(%d) = 0x%04x", ep_num, - inw (UDC_EP_TX (ep_num))); - /* Resetting of tx endpoints seems to be causing the USB function - * module to fail, which causes problems when the driver is - * uninstalled. We'll skip resetting tx endpoints for now until - * we figure out what the problem is. - */ -#if 0 - if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { - /* we have a valid tx endpoint, so reset it */ - outw (ep_num | UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - outw (UDC_Reset_EP, UDC_CTRL); - outw (ep_num | UDC_EP_Dir, UDC_EP_NUM); - UDCDBGA ("IN endpoint %d reset", ep_num); - } -#endif - } -} -#endif - -/* ************************************************************************** */ - -/** - * udc_check_ep - check logical endpoint - * - * Return physical endpoint number to use for this logical endpoint or zero if not valid. - */ -#if 0 -int udc_check_ep (int logical_endpoint, int packetsize) -{ - if ((logical_endpoint == 0x80) || - ((logical_endpoint & 0x8f) != logical_endpoint)) { - return 0; - } - - switch (packetsize) { - case 8: - case 16: - case 32: - case 64: - case 128: - case 256: - case 512: - break; - default: - return 0; - } - - return EP_ADDR_TO_PHYS_EP (logical_endpoint); -} -#endif - -/* - * udc_setup_ep - setup endpoint - * - * Associate a physical endpoint with endpoint_instance - */ -void udc_setup_ep (struct usb_device_instance *device, - unsigned int ep, struct usb_endpoint_instance *endpoint) -{ - UDCDBGA ("setting up endpoint addr %x", endpoint->endpoint_address); - - /* This routine gets called by bi_modinit for endpoint 0 and from - * bi_config for all of the other endpoints. bi_config gets called - * during the DEVICE_CREATE, DEVICE_CONFIGURED, and - * DEVICE_SET_INTERFACE events. We need to reconfigure the OMAP packet - * RAM after bi_config scans the selected device configuration and - * initializes the endpoint structures, but before this routine enables - * the OUT endpoint FIFOs. Since bi_config calls this routine in a - * loop for endpoints 1 through UDC_MAX_ENDPOINTS, we reconfigure our - * packet RAM here when ep==1. - * I really hate to do this here, but it seems like the API exported - * by the USB bus interface controller driver to the usbd-bi module - * isn't quite right so there is no good place to do this. - */ - if (ep == 1) { - omap1510_deconfigure_device (); - omap1510_configure_device (device); - } - - if (endpoint && (ep < UDC_MAX_ENDPOINTS)) { - int ep_addr = endpoint->endpoint_address; - - if (!ep_addr) { - /* nothing to do for endpoint 0 */ - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - /* nothing to do for IN (tx) endpoints */ - } else { /* OUT (rx) endpoint */ - if (endpoint->rcv_packetSize) { - /*struct urb* urb = &(urb_out_array[ep&0xFF]); */ - /*urb->endpoint = endpoint; */ - /*urb->device = device; */ - /*urb->buffer_length = sizeof(urb->buffer); */ - - /*endpoint->rcv_urb = urb; */ - omap1510_prepare_endpoint_for_rx (ep_addr); - } - } - } -} - -/** - * udc_disable_ep - disable endpoint - * @ep: - * - * Disable specified endpoint - */ -#if 0 -void udc_disable_ep (unsigned int ep_addr) -{ - /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - struct usb_endpoint_instance *endpoint = omap1510_find_ep (ep_addr); /*udc_device->bus->endpoint_array + ep; */ - - UDCDBGA ("disable ep_addr %d", ep_addr); - - if (!ep_num) { - /* nothing to do for endpoint 0 */ ; - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - if (endpoint->tx_packetSize) { - /* we have a valid tx endpoint */ - /*usbd_flush_tx(endpoint); */ - endpoint->tx_urb = NULL; - } - } else { - if (endpoint->rcv_packetSize) { - /* we have a valid rx endpoint */ - /*usbd_flush_rcv(endpoint); */ - endpoint->rcv_urb = NULL; - } - } -} -#endif - -/* ************************************************************************** */ - -/** - * udc_connected - is the USB cable connected - * - * Return non-zero if cable is connected. - */ -#if 0 -int udc_connected (void) -{ - return ((inw (UDC_DEVSTAT) & UDC_ATT) == UDC_ATT); -} -#endif - -/* Turn on the USB connection by enabling the pullup resistor */ -void udc_connect (void) -{ - UDCDBG ("connect, enable Pullup"); - outl (0x00000018, FUNC_MUX_CTRL_D); -} - -/* Turn off the USB connection by disabling the pullup resistor */ -void udc_disconnect (void) -{ - UDCDBG ("disconnect, disable Pullup"); - outl (0x00000000, FUNC_MUX_CTRL_D); -} - -/* ************************************************************************** */ - - -/* - * udc_disable_interrupts - disable interrupts - * switch off interrupts - */ -#if 0 -void udc_disable_interrupts (struct usb_device_instance *device) -{ - UDCDBG ("disabling all interrupts"); - outw (0, UDC_IRQ_EN); -} -#endif - -/* ************************************************************************** */ - -/** - * udc_ep0_packetsize - return ep0 packetsize - */ -#if 0 -int udc_ep0_packetsize (void) -{ - return EP0_PACKETSIZE; -} -#endif - -/* Switch on the UDC */ -void udc_enable (struct usb_device_instance *device) -{ - UDCDBGA ("enable device %p, status %d", device, device->status); - - /* initialize driver state variables */ - udc_devstat = 0; - - /* Save the device structure pointer */ - udc_device = device; - - /* Setup ep0 urb */ - if (!ep0_urb) { - ep0_urb = - usbd_alloc_urb (udc_device, - udc_device->bus->endpoint_array); - } else { - serial_printf ("udc_enable: ep0_urb already allocated %p\n", - ep0_urb); - } - - UDCDBG ("Check clock status"); - UDCREG (STATUS_REQ); - - /* The VBUS_MODE bit selects whether VBUS detection is done via - * software (1) or hardware (0). When software detection is - * selected, VBUS_CTRL selects whether USB is not connected (0) - * or connected (1). - */ - outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_CTRL | UDC_VBUS_MODE, - FUNC_MUX_CTRL_0); - UDCREGL (FUNC_MUX_CTRL_0); - - omap1510_configure_device (device); -} - -/* Switch off the UDC */ -void udc_disable (void) -{ - UDCDBG ("disable UDC"); - - omap1510_deconfigure_device (); - - /* The VBUS_MODE bit selects whether VBUS detection is done via - * software (1) or hardware (0). When software detection is - * selected, VBUS_CTRL selects whether USB is not connected (0) - * or connected (1). - */ - outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); - outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); - UDCREGL (FUNC_MUX_CTRL_0); - - /* Free ep0 URB */ - if (ep0_urb) { - /*usbd_dealloc_urb(ep0_urb); */ - ep0_urb = NULL; - } - - /* Reset device pointer. - * We ought to do this here to balance the initialization of udc_device - * in udc_enable, but some of our other exported functions get called - * by the bus interface driver after udc_disable, so we have to hang on - * to the device pointer to avoid a null pointer dereference. */ - /* udc_device = NULL; */ -} - -/** - * udc_startup - allow udc code to do any additional startup - */ -void udc_startup_events (struct usb_device_instance *device) -{ - /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ - usbd_device_event_irq (device, DEVICE_INIT, 0); - - /* The DEVICE_CREATE event puts the USB device in the state - * STATE_ATTACHED. - */ - usbd_device_event_irq (device, DEVICE_CREATE, 0); - - /* Some USB controller driver implementations signal - * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. - * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, - * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. - * The OMAP USB client controller has the capability to detect when the - * USB cable is connected to a powered USB bus via the ATT bit in the - * DEVSTAT register, so we will defer the DEVICE_HUB_CONFIGURED and - * DEVICE_RESET events until later. - */ - - 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 */ -} diff --git a/include/usb/mpc8xx_udc.h b/include/usb/mpc8xx_udc.h new file mode 100644 index 0000000..039d245 --- /dev/null +++ b/include/usb/mpc8xx_udc.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 + +/* 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/usb/omap1510_udc.h b/include/usb/omap1510_udc.h new file mode 100644 index 0000000..ece0e95 --- /dev/null +++ b/include/usb/omap1510_udc.h @@ -0,0 +1,193 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usb/device/bi/omap.h + * Register definitions for TI OMAP1510 USB bus interface driver + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __USBDCORE_OMAP1510_H__ +#define __USBDCORE_OMAP1510_H__ + + +/* + * 13.2 MPU Register Map + */ + +/* Table 13-1. USB Function Module Registers (endpoint) */ +#define UDC_BASE 0xFFFB4000 +#define UDC_OFFSET(offset) (UDC_BASE + (offset)) +#define UDC_REV UDC_OFFSET(0x0) /* Revision */ +#define UDC_EP_NUM UDC_OFFSET(0x4) /* Endpoint selection */ +#define UDC_DATA UDC_OFFSET(0x08) /* Data */ +#define UDC_CTRL UDC_OFFSET(0x0C) /* Control */ +#define UDC_STAT_FLG UDC_OFFSET(0x10) /* Status flag */ +#define UDC_RXFSTAT UDC_OFFSET(0x14) /* Receive FIFO status */ +#define UDC_SYSCON1 UDC_OFFSET(0x18) /* System configuration 1 */ +#define UDC_SYSCON2 UDC_OFFSET(0x1C) /* System configuration 2 */ +#define UDC_DEVSTAT UDC_OFFSET(0x20) /* Device status */ +#define UDC_SOF UDC_OFFSET(0x24) /* Start of frame */ +#define UDC_IRQ_EN UDC_OFFSET(0x28) /* Interrupt enable */ +#define UDC_DMA_IRQ_EN UDC_OFFSET(0x2C) /* DMA interrupt enable */ +#define UDC_IRQ_SRC UDC_OFFSET(0x30) /* Interrupt source */ +#define UDC_EPN_STAT UDC_OFFSET(0x34) /* Endpoint interrupt status */ +#define UDC_DMAN_STAT UDC_OFFSET(0x3C) /* DMA endpoint interrupt status */ + +/* IRQ_EN register fields */ +#define UDC_Sof_IE (1 << 7) /* Start-of-frame interrupt enabled */ +#define UDC_EPn_RX_IE (1 << 5) /* Receive endpoint interrupt enabled */ +#define UDC_EPn_TX_IE (1 << 4) /* Transmit endpoint interrupt enabled */ +#define UDC_DS_Chg_IE (1 << 3) /* Device state changed interrupt enabled */ +#define UDC_EP0_IE (1 << 0) /* EP0 transaction interrupt enabled */ + +/* IRQ_SRC register fields */ +#define UDC_TXn_Done (1 << 10) /* Transmit DMA channel n done */ +#define UDC_RXn_Cnt (1 << 9) /* Receive DMA channel n transactions count */ +#define UDC_RXn_EOT (1 << 8) /* Receive DMA channel n end of transfer */ +#define UDC_SOF_Flg (1 << 7) /* Start-of-frame interrupt flag */ +#define UDC_EPn_RX (1 << 5) /* Endpoint n OUT transaction */ +#define UDC_EPn_TX (1 << 4) /* Endpoint n IN transaction */ +#define UDC_DS_Chg (1 << 3) /* Device state changed */ +#define UDC_Setup (1 << 2) /* Setup transaction */ +#define UDC_EP0_RX (1 << 1) /* EP0 OUT transaction */ +#define UDC_EP0_TX (1 << 0) /* EP0 IN transaction */ + +/* DEVSTAT register fields, 14.2.9 */ +#define UDC_R_WK_OK (1 << 6) /* Remote wakeup granted */ +#define UDC_USB_Reset (1 << 5) /* USB reset signalling is active */ +#define UDC_SUS (1 << 4) /* Suspended state */ +#define UDC_CFG (1 << 3) /* Configured state */ +#define UDC_ADD (1 << 2) /* Addressed state */ +#define UDC_DEF (1 << 1) /* Default state */ +#define UDC_ATT (1 << 0) /* Attached state */ + +/* SYSCON1 register fields */ +#define UDC_Cfg_Lock (1 << 8) /* Device configuration locked */ +#define UDC_Nak_En (1 << 4) /* NAK enable */ +#define UDC_Self_Pwr (1 << 2) /* Device is self-powered */ +#define UDC_Soff_Dis (1 << 1) /* Shutoff disabled */ +#define UDC_Pullup_En (1 << 0) /* External pullup enabled */ + +/* SYSCON2 register fields */ +#define UDC_Rmt_Wkp (1 << 6) /* Remote wakeup */ +#define UDC_Stall_Cmd (1 << 5) /* Stall endpoint */ +#define UDC_Dev_Cfg (1 << 3) /* Device configured */ +#define UDC_Clr_Cfg (1 << 2) /* Clear configured */ + +/* + * Select and enable endpoints + */ + +/* Table 13-1. USB Function Module Registers (endpoint configuration) */ +#define UDC_EPBASE UDC_OFFSET(0x80) /* Endpoints base address */ +#define UDC_EP0 UDC_EPBASE /* Control endpoint configuration */ +#define UDC_EP_RX_BASE UDC_OFFSET(0x84) /* Receive endpoints base address */ +#define UDC_EP_RX(endpoint) (UDC_EP_RX_BASE + ((endpoint) - 1) * 4) +#define UDC_EP_TX_BASE UDC_OFFSET(0xC4) /* Transmit endpoints base address */ +#define UDC_EP_TX(endpoint) (UDC_EP_TX_BASE + ((endpoint) - 1) * 4) + +/* EP_NUM register fields */ +#define UDC_Setup_Sel (1 << 6) /* Setup FIFO select */ +#define UDC_EP_Sel (1 << 5) /* TX/RX FIFO select */ +#define UDC_EP_Dir (1 << 4) /* Endpoint direction */ + +/* CTRL register fields */ +#define UDC_Clr_Halt (1 << 7) /* Clear halt endpoint */ +#define UDC_Set_Halt (1 << 6) /* Set halt endpoint */ +#define UDC_Set_FIFO_En (1 << 2) /* Set FIFO enable */ +#define UDC_Clr_EP (1 << 1) /* Clear endpoint */ +#define UDC_Reset_EP (1 << 0) /* Reset endpoint */ + +/* STAT_FLG register fields */ +#define UDC_Miss_In (1 << 14) +#define UDC_Data_Flush (1 << 13) +#define UDC_ISO_Err (1 << 12) +#define UDC_ISO_FIFO_Empty (1 << 9) +#define UDC_ISO_FIFO_Full (1 << 8) +#define UDC_EP_Halted (1 << 6) +#define UDC_STALL (1 << 5) +#define UDC_NAK (1 << 4) +#define UDC_ACK (1 << 3) +#define UDC_FIFO_En (1 << 2) +#define UDC_Non_ISO_FIFO_Empty (1 << 1) +#define UDC_Non_ISO_FIFO_Full (1 << 0) + +/* EPn_RX register fields */ +#define UDC_EPn_RX_Valid (1 << 15) /* valid */ +#define UDC_EPn_RX_Db (1 << 14) /* double-buffer */ +#define UDC_EPn_RX_Iso (1 << 11) /* isochronous */ + +/* EPn_TX register fields */ +#define UDC_EPn_TX_Valid (1 << 15) /* valid */ +#define UDC_EPn_TX_Db (1 << 14) /* double-buffer */ +#define UDC_EPn_TX_Iso (1 << 11) /* isochronous */ + +#define EP0_PACKETSIZE 0x40 + +/* physical to logical endpoint mapping + * Physical endpoints are an index into device->bus->endpoint_array. + * Logical endpoints are endpoints 0 to 15 IN and OUT as defined in + * the USB specification. + * + * physical ep logical ep direction endpoint_address + * 0 0 IN and OUT 0x00 + * 1 to 15 1 to 15 OUT 0x01 to 0x0f + * 16 to 30 1 to 15 IN 0x81 to 0x8f + */ +#define PHYS_EP_TO_EP_ADDR(ep) (((ep) < 16) ? (ep) : (((ep) - 15) | 0x80)) +#define EP_ADDR_TO_PHYS_EP(a) (((a) & 0x80) ? (((a) & ~0x80) + 15) : (a)) + +/* MOD_CONF_CTRL_0 bits (FIXME: move to board hardware.h ?) */ +#define CONF_MOD_USB_W2FC_VBUS_MODE_R (1 << 17) + +/* Other registers (may be) related to USB */ + +#define CLOCK_CTRL (0xFFFE0830) +#define APLL_CTRL (0xFFFE084C) +#define DPLL_CTRL (0xFFFE083C) +#define SOFT_REQ (0xFFFE0834) +#define STATUS_REQ (0xFFFE0840) + +/* FUNC_MUX_CTRL_0 bits related to USB */ +#define UDC_VBUS_CTRL (1 << 19) +#define UDC_VBUS_MODE (1 << 18) + +/* 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_PACKET_SIZE 16 +#define UDC_BULK_PACKET_SIZE 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 */ +int udc_endpoint_write(struct usb_endpoint_instance *endpoint); + +int udc_init (void); + +void udc_enable(struct usb_device_instance *device); +void udc_disable(void); + +void udc_connect(void); +void udc_disconnect(void); + +void udc_startup_events(struct usb_device_instance *device); +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, struct usb_endpoint_instance *endpoint); + +#endif diff --git a/include/usbdcore.h b/include/usbdcore.h deleted file mode 100644 index 206dbbc..0000000 --- a/include/usbdcore.h +++ /dev/null @@ -1,666 +0,0 @@ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on linux/drivers/usbd/usbd.h - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne , - * Tom Rushworth , - * Bruce Balden - * - * 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. - * - */ - -#ifndef __USBDCORE_H__ -#define __USBDCORE_H__ - -#include -#include "usbdescriptors.h" - - -#define MAX_URBS_QUEUED 5 - - -#if 1 -#define usberr(fmt,args...) serial_printf("ERROR: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args) -#else -#define usberr(fmt,args...) do{}while(0) -#endif - -#if 0 -#define usbdbg(fmt,args...) serial_printf("debug: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args) -#else -#define usbdbg(fmt,args...) do{}while(0) -#endif - -#if 0 -#define usbinfo(fmt,args...) serial_printf("info: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args) -#else -#define usbinfo(fmt,args...) do{}while(0) -#endif - -#ifndef le16_to_cpu -#define le16_to_cpu(x) (x) -#endif - -#ifndef inb -#define inb(p) (*(volatile u8*)(p)) -#endif - -#ifndef outb -#define outb(val,p) (*(volatile u8*)(p) = (val)) -#endif - -#ifndef inw -#define inw(p) (*(volatile u16*)(p)) -#endif - -#ifndef outw -#define outw(val,p) (*(volatile u16*)(p) = (val)) -#endif - -#ifndef inl -#define inl(p) (*(volatile u32*)(p)) -#endif - -#ifndef outl -#define outl(val,p) (*(volatile u32*)(p) = (val)) -#endif - -#ifndef insw -#define insw(p,to,len) mmio_insw(p,to,len) -#endif - -#ifndef outsw -#define outsw(p,from,len) mmio_outsw(p,from,len) -#endif - -#ifndef insb -#define insb(p,to,len) mmio_insb(p,to,len) -#endif - -#ifndef mmio_insw -#define mmio_insw(r,b,l) ({ int __i ; \ - u16 *__b2; \ - __b2 = (u16 *) b; \ - for (__i = 0; __i < l; __i++) { \ - *(__b2 + __i) = inw(r); \ - }; \ - }) -#endif - -#ifndef mmio_outsw -#define mmio_outsw(r,b,l) ({ int __i; \ - u16 *__b2; \ - __b2 = (u16 *) b; \ - for (__i = 0; __i < l; __i++) { \ - outw( *(__b2 + __i), r); \ - } \ - }) -#endif - -#ifndef mmio_insb -#define mmio_insb(r,b,l) ({ int __i ; \ - u8 *__b2; \ - __b2 = (u8 *) b; \ - for (__i = 0; __i < l; __i++) { \ - *(__b2 + __i) = inb(r); \ - }; \ - }) -#endif - -/* - * Structure member address manipulation macros. - * These are used by client code (code using the urb_link routines), since - * the urb_link structure is embedded in the client data structures. - * - * Note: a macro offsetof equivalent to member_offset is defined in stddef.h - * but this is kept here for the sake of portability. - * - * p2surround returns a pointer to the surrounding structure given - * type of the surrounding structure, the name memb of the structure - * member pointed at by ptr. For example, if you have: - * - * struct foo { - * int x; - * float y; - * char z; - * } thingy; - * - * char *cp = &thingy.z; - * - * then - * - * &thingy == p2surround(struct foo, z, cp) - * - * Clear? - */ -#define _cv_(ptr) ((char*)(void*)(ptr)) -#define member_offset(type,memb) (_cv_(&(((type*)0)->memb))-(char*)0) -#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb))) - -struct urb; - -struct usb_endpoint_instance; -struct usb_interface_instance; -struct usb_configuration_instance; -struct usb_device_instance; -struct usb_bus_instance; - -/* - * Device and/or Interface Class codes - */ -#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PHYSICAL 5 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_DATA 10 -#define USB_CLASS_APP_SPEC 0xfe -#define USB_CLASS_VENDOR_SPEC 0xff - -/* - * USB types - */ -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -/* - * USB recipients - */ -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* - * USB directions - */ -#define USB_DIR_OUT 0 -#define USB_DIR_IN 0x80 - -/* - * Descriptor types - */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_DT_HID (USB_TYPE_CLASS | 0x01) -#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) -#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) -#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) - -/* - * Descriptor sizes per descriptor type - */ -#define USB_DT_DEVICE_SIZE 18 -#define USB_DT_CONFIG_SIZE 9 -#define USB_DT_INTERFACE_SIZE 9 -#define USB_DT_ENDPOINT_SIZE 7 -#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define USB_DT_HUB_NONVAR_SIZE 7 -#define USB_DT_HID_SIZE 9 - -/* - * Endpoints - */ -#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ -#define USB_ENDPOINT_DIR_MASK 0x80 - -#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ -#define USB_ENDPOINT_XFER_CONTROL 0 -#define USB_ENDPOINT_XFER_ISOC 1 -#define USB_ENDPOINT_XFER_BULK 2 -#define USB_ENDPOINT_XFER_INT 3 - -/* - * USB Packet IDs (PIDs) - */ -#define USB_PID_UNDEF_0 0xf0 -#define USB_PID_OUT 0xe1 -#define USB_PID_ACK 0xd2 -#define USB_PID_DATA0 0xc3 -#define USB_PID_PING 0xb4 /* USB 2.0 */ -#define USB_PID_SOF 0xa5 -#define USB_PID_NYET 0x96 /* USB 2.0 */ -#define USB_PID_DATA2 0x87 /* USB 2.0 */ -#define USB_PID_SPLIT 0x78 /* USB 2.0 */ -#define USB_PID_IN 0x69 -#define USB_PID_NAK 0x5a -#define USB_PID_DATA1 0x4b -#define USB_PID_PREAMBLE 0x3c /* Token mode */ -#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ -#define USB_PID_SETUP 0x2d -#define USB_PID_STALL 0x1e -#define USB_PID_MDATA 0x0f /* USB 2.0 */ - -/* - * Standard requests - */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -#define USB_REQ_SET_FEATURE 0x03 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN") - -/* - * HID requests - */ -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_GET_IDLE 0x02 -#define USB_REQ_GET_PROTOCOL 0x03 -#define USB_REQ_SET_REPORT 0x09 -#define USB_REQ_SET_IDLE 0x0A -#define USB_REQ_SET_PROTOCOL 0x0B - - -/* - * USB Spec Release number - */ - -#define USB_BCD_VERSION 0x0110 - - -/* - * Device Requests (c.f Table 9-2) - */ - -#define USB_REQ_DIRECTION_MASK 0x80 -#define USB_REQ_TYPE_MASK 0x60 -#define USB_REQ_RECIPIENT_MASK 0x1f - -#define USB_REQ_DEVICE2HOST 0x80 -#define USB_REQ_HOST2DEVICE 0x00 - -#define USB_REQ_TYPE_STANDARD 0x00 -#define USB_REQ_TYPE_CLASS 0x20 -#define USB_REQ_TYPE_VENDOR 0x40 - -#define USB_REQ_RECIPIENT_DEVICE 0x00 -#define USB_REQ_RECIPIENT_INTERFACE 0x01 -#define USB_REQ_RECIPIENT_ENDPOINT 0x02 -#define USB_REQ_RECIPIENT_OTHER 0x03 - -/* - * get status bits - */ - -#define USB_STATUS_SELFPOWERED 0x01 -#define USB_STATUS_REMOTEWAKEUP 0x02 - -#define USB_STATUS_HALT 0x01 - -/* - * descriptor types - */ - -#define USB_DESCRIPTOR_TYPE_DEVICE 0x01 -#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02 -#define USB_DESCRIPTOR_TYPE_STRING 0x03 -#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04 -#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 -#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06 -#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 0x07 -#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08 -#define USB_DESCRIPTOR_TYPE_HID 0x21 -#define USB_DESCRIPTOR_TYPE_REPORT 0x22 - -#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \ - usbd_device_descriptors[x] : "UNKNOWN") - -/* - * standard feature selectors - */ -#define USB_ENDPOINT_HALT 0x00 -#define USB_DEVICE_REMOTE_WAKEUP 0x01 -#define USB_TEST_MODE 0x02 - - -/* USB Requests - * - */ - -struct usb_device_request { - u8 bmRequestType; - u8 bRequest; - u16 wValue; - u16 wIndex; - u16 wLength; -} __attribute__ ((packed)); - - -/* USB Status - * - */ -typedef enum urb_send_status { - SEND_IN_PROGRESS, - SEND_FINISHED_OK, - SEND_FINISHED_ERROR, - RECV_READY, - RECV_OK, - RECV_ERROR -} urb_send_status_t; - -/* - * Device State (c.f USB Spec 2.0 Figure 9-1) - * - * What state the usb device is in. - * - * Note the state does not change if the device is suspended, we simply set a - * flag to show that it is suspended. - * - */ -typedef enum usb_device_state { - STATE_INIT, /* just initialized */ - STATE_CREATED, /* just created */ - STATE_ATTACHED, /* we are attached */ - STATE_POWERED, /* we have seen power indication (electrical bus signal) */ - STATE_DEFAULT, /* we been reset */ - STATE_ADDRESSED, /* we have been addressed (in default configuration) */ - STATE_CONFIGURED, /* we have seen a set configuration device command */ - STATE_UNKNOWN, /* destroyed */ -} usb_device_state_t; - -#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN") - -/* - * Device status - * - * Overall state - */ -typedef enum usb_device_status { - USBD_OPENING, /* we are currently opening */ - USBD_OK, /* ok to use */ - USBD_SUSPENDED, /* we are currently suspended */ - USBD_CLOSING, /* we are currently closing */ -} usb_device_status_t; - -#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN") - -/* - * Device Events - * - * These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1). - * - * There are additional events defined to handle some extra actions we need - * to have handled. - * - */ -typedef enum usb_device_event { - - DEVICE_UNKNOWN, /* bi - unknown event */ - DEVICE_INIT, /* bi - initialize */ - DEVICE_CREATE, /* bi - */ - DEVICE_HUB_CONFIGURED, /* bi - bus has been plugged int */ - DEVICE_RESET, /* bi - hub has powered our port */ - - DEVICE_ADDRESS_ASSIGNED, /* ep0 - set address setup received */ - DEVICE_CONFIGURED, /* ep0 - set configure setup received */ - DEVICE_SET_INTERFACE, /* ep0 - set interface setup received */ - - DEVICE_SET_FEATURE, /* ep0 - set feature setup received */ - DEVICE_CLEAR_FEATURE, /* ep0 - clear feature setup received */ - - DEVICE_DE_CONFIGURED, /* ep0 - set configure setup received for ?? */ - - DEVICE_BUS_INACTIVE, /* bi - bus in inactive (no SOF packets) */ - DEVICE_BUS_ACTIVITY, /* bi - bus is active again */ - - DEVICE_POWER_INTERRUPTION, /* bi - hub has depowered our port */ - DEVICE_HUB_RESET, /* bi - bus has been unplugged */ - DEVICE_DESTROY, /* bi - device instance should be destroyed */ - - DEVICE_HOTPLUG, /* bi - a hotplug event has occured */ - - DEVICE_FUNCTION_PRIVATE, /* function - private */ - -} usb_device_event_t; - - -typedef struct urb_link { - struct urb_link *next; - struct urb_link *prev; -} urb_link; - -/* USB Data structure - for passing data around. - * - * This is used for both sending and receiving data. - * - * The callback function is used to let the function driver know when - * transmitted data has been sent. - * - * The callback function is set by the alloc_recv function when an urb is - * allocated for receiving data for an endpoint and used to call the - * function driver to inform it that data has arrived. - */ - -#define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */ -struct urb { - - struct usb_endpoint_instance *endpoint; - struct usb_device_instance *device; - - struct usb_device_request device_request; /* contents of received SETUP packet */ - - struct urb_link link; /* embedded struct for circular doubly linked list of urbs */ - - u8* buffer; - unsigned int buffer_length; - unsigned int actual_length; - - urb_send_status_t status; - int data; - - u16 buffer_data[URB_BUF_SIZE]; /* data received (OUT) or being sent (IN) */ -}; - -/* Endpoint configuration - * - * Per endpoint configuration data. Used to track which function driver owns - * an endpoint. - * - */ -struct usb_endpoint_instance { - int endpoint_address; /* logical endpoint address */ - - /* control */ - int status; /* halted */ - int state; /* available for use by bus interface driver */ - - /* receive side */ - struct urb_link rcv; /* received urbs */ - struct urb_link rdy; /* empty urbs ready to receive */ - struct urb *rcv_urb; /* active urb */ - int rcv_attributes; /* copy of bmAttributes from endpoint descriptor */ - int rcv_packetSize; /* maximum packet size from endpoint descriptor */ - int rcv_transferSize; /* maximum transfer size from function driver */ - int rcv_queue; - - /* transmit side */ - struct urb_link tx; /* urbs ready to transmit */ - struct urb_link done; /* transmitted urbs */ - struct urb *tx_urb; /* active urb */ - int tx_attributes; /* copy of bmAttributes from endpoint descriptor */ - int tx_packetSize; /* maximum packet size from endpoint descriptor */ - int tx_transferSize; /* maximum transfer size from function driver */ - int tx_queue; - - int sent; /* data already sent */ - int last; /* data sent in last packet XXX do we need this */ -}; - -struct usb_alternate_instance { - struct usb_interface_descriptor *interface_descriptor; - - int endpoints; - int *endpoint_transfersize_array; - struct usb_endpoint_descriptor **endpoints_descriptor_array; -}; - -struct usb_interface_instance { - int alternates; - struct usb_alternate_instance *alternates_instance_array; -}; - -struct usb_configuration_instance { - int interfaces; - struct usb_configuration_descriptor *configuration_descriptor; - struct usb_interface_instance *interface_instance_array; -}; - - -/* USB Device Instance - * - * For each physical bus interface we create a logical device structure. This - * tracks all of the required state to track the USB HOST's view of the device. - * - * Keep track of the device configuration for a real physical bus interface, - * this includes the bus interface, multiple function drivers, the current - * configuration and the current state. - * - * This will show: - * the specific bus interface driver - * the default endpoint 0 driver - * the configured function driver - * device state - * device status - * endpoint list - */ - -struct usb_device_instance { - - /* generic */ - char *name; - struct usb_device_descriptor *device_descriptor; /* per device descriptor */ - - 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 */ - - /* configuration descriptors */ - int configurations; - struct usb_configuration_instance *configuration_instance_array; - - /* device state */ - usb_device_state_t device_state; /* current USB Device state */ - usb_device_state_t device_previous_state; /* current USB Device state */ - - u8 address; /* current address (zero is default) */ - u8 configuration; /* current show configuration (zero is default) */ - u8 interface; /* current interface (zero is default) */ - u8 alternate; /* alternate flag */ - - usb_device_status_t status; /* device status */ - - int urbs_queued; /* number of submitted urbs */ - - /* Shouldn't need to make this atomic, all we need is a change indicator */ - unsigned long usbd_rxtx_timestamp; - unsigned long usbd_last_rxtx_timestamp; - -}; - -/* Bus Interface configuration structure - * - * This is allocated for each configured instance of a bus interface driver. - * - * The privdata pointer may be used by the bus interface driver to store private - * per instance state information. - */ -struct usb_bus_instance { - - struct usb_device_instance *device; - struct usb_endpoint_instance *endpoint_array; /* array of available configured endpoints */ - - int max_endpoints; /* maximimum number of rx enpoints */ - unsigned char maxpacketsize; - - unsigned int serial_number; - char *serial_number_str; - void *privdata; /* private data for the bus interface */ - -}; - -extern char *usbd_device_events[]; -extern char *usbd_device_states[]; -extern char *usbd_device_status[]; -extern char *usbd_device_requests[]; -extern char *usbd_device_descriptors[]; - -void urb_link_init (urb_link * ul); -void urb_detach (struct urb *urb); -urb_link *first_urb_link (urb_link * hd); -struct urb *first_urb (urb_link * hd); -struct urb *first_urb_detached (urb_link * hd); -void urb_append (urb_link * hd, struct urb *urb); - -struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint); -void usbd_dealloc_urb (struct urb *urb); - -/* - * usbd_device_event is used by bus interface drivers to tell the higher layers that - * certain events have taken place. - */ -void usbd_device_event_irq (struct usb_device_instance *conf, usb_device_event_t, int); -void usbd_device_event (struct usb_device_instance *conf, usb_device_event_t, int); - -/* descriptors - * - * Various ways of finding descriptors based on the current device and any - * possible configuration / interface / endpoint for it. - */ -struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct usb_device_instance *, int, int); -struct usb_function_instance *usbd_device_function_instance (struct usb_device_instance *, unsigned int); -struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *, int, int, int); -struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *, int, int, int, int); -struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance *, int, int, int, int); -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance *, int, int, int, int, int); -struct usb_class_descriptor *usbd_device_class_descriptor_index (struct usb_device_instance *, int, int, int, int, int); -struct usb_class_report_descriptor *usbd_device_class_report_descriptor_index( struct usb_device_instance *, int , int , int , int , int ); -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *, int, int, int, int, int); -int usbd_device_endpoint_transfersize (struct usb_device_instance *, int, int, int, int, int); -struct usb_string_descriptor *usbd_get_string (u8); -struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *, int); - -int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint); -void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad); -void usbd_tx_complete (struct usb_endpoint_instance *endpoint); - -#endif diff --git a/include/usbdcore_ep0.h b/include/usbdcore_ep0.h deleted file mode 100644 index 3bec106..0000000 --- a/include/usbdcore_ep0.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usbd/ep0.c - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne , - * Tom Rushworth , - * Bruce Balden - * - * 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. - * - */ - -#ifndef __USBDCORE_EP0_H__ -#define __USBDCORE_EP0_H__ - - -int ep0_recv_setup (struct urb *urb); - - -#endif diff --git a/include/usbdcore_mpc8xx.h b/include/usbdcore_mpc8xx.h deleted file mode 100644 index 039d245..0000000 --- a/include/usbdcore_mpc8xx.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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 - -/* 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 deleted file mode 100644 index ece0e95..0000000 --- a/include/usbdcore_omap1510.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usb/device/bi/omap.h - * Register definitions for TI OMAP1510 USB bus interface driver - * - * Author: MontaVista Software, Inc. - * source@mvista.com - * - * 2003 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifndef __USBDCORE_OMAP1510_H__ -#define __USBDCORE_OMAP1510_H__ - - -/* - * 13.2 MPU Register Map - */ - -/* Table 13-1. USB Function Module Registers (endpoint) */ -#define UDC_BASE 0xFFFB4000 -#define UDC_OFFSET(offset) (UDC_BASE + (offset)) -#define UDC_REV UDC_OFFSET(0x0) /* Revision */ -#define UDC_EP_NUM UDC_OFFSET(0x4) /* Endpoint selection */ -#define UDC_DATA UDC_OFFSET(0x08) /* Data */ -#define UDC_CTRL UDC_OFFSET(0x0C) /* Control */ -#define UDC_STAT_FLG UDC_OFFSET(0x10) /* Status flag */ -#define UDC_RXFSTAT UDC_OFFSET(0x14) /* Receive FIFO status */ -#define UDC_SYSCON1 UDC_OFFSET(0x18) /* System configuration 1 */ -#define UDC_SYSCON2 UDC_OFFSET(0x1C) /* System configuration 2 */ -#define UDC_DEVSTAT UDC_OFFSET(0x20) /* Device status */ -#define UDC_SOF UDC_OFFSET(0x24) /* Start of frame */ -#define UDC_IRQ_EN UDC_OFFSET(0x28) /* Interrupt enable */ -#define UDC_DMA_IRQ_EN UDC_OFFSET(0x2C) /* DMA interrupt enable */ -#define UDC_IRQ_SRC UDC_OFFSET(0x30) /* Interrupt source */ -#define UDC_EPN_STAT UDC_OFFSET(0x34) /* Endpoint interrupt status */ -#define UDC_DMAN_STAT UDC_OFFSET(0x3C) /* DMA endpoint interrupt status */ - -/* IRQ_EN register fields */ -#define UDC_Sof_IE (1 << 7) /* Start-of-frame interrupt enabled */ -#define UDC_EPn_RX_IE (1 << 5) /* Receive endpoint interrupt enabled */ -#define UDC_EPn_TX_IE (1 << 4) /* Transmit endpoint interrupt enabled */ -#define UDC_DS_Chg_IE (1 << 3) /* Device state changed interrupt enabled */ -#define UDC_EP0_IE (1 << 0) /* EP0 transaction interrupt enabled */ - -/* IRQ_SRC register fields */ -#define UDC_TXn_Done (1 << 10) /* Transmit DMA channel n done */ -#define UDC_RXn_Cnt (1 << 9) /* Receive DMA channel n transactions count */ -#define UDC_RXn_EOT (1 << 8) /* Receive DMA channel n end of transfer */ -#define UDC_SOF_Flg (1 << 7) /* Start-of-frame interrupt flag */ -#define UDC_EPn_RX (1 << 5) /* Endpoint n OUT transaction */ -#define UDC_EPn_TX (1 << 4) /* Endpoint n IN transaction */ -#define UDC_DS_Chg (1 << 3) /* Device state changed */ -#define UDC_Setup (1 << 2) /* Setup transaction */ -#define UDC_EP0_RX (1 << 1) /* EP0 OUT transaction */ -#define UDC_EP0_TX (1 << 0) /* EP0 IN transaction */ - -/* DEVSTAT register fields, 14.2.9 */ -#define UDC_R_WK_OK (1 << 6) /* Remote wakeup granted */ -#define UDC_USB_Reset (1 << 5) /* USB reset signalling is active */ -#define UDC_SUS (1 << 4) /* Suspended state */ -#define UDC_CFG (1 << 3) /* Configured state */ -#define UDC_ADD (1 << 2) /* Addressed state */ -#define UDC_DEF (1 << 1) /* Default state */ -#define UDC_ATT (1 << 0) /* Attached state */ - -/* SYSCON1 register fields */ -#define UDC_Cfg_Lock (1 << 8) /* Device configuration locked */ -#define UDC_Nak_En (1 << 4) /* NAK enable */ -#define UDC_Self_Pwr (1 << 2) /* Device is self-powered */ -#define UDC_Soff_Dis (1 << 1) /* Shutoff disabled */ -#define UDC_Pullup_En (1 << 0) /* External pullup enabled */ - -/* SYSCON2 register fields */ -#define UDC_Rmt_Wkp (1 << 6) /* Remote wakeup */ -#define UDC_Stall_Cmd (1 << 5) /* Stall endpoint */ -#define UDC_Dev_Cfg (1 << 3) /* Device configured */ -#define UDC_Clr_Cfg (1 << 2) /* Clear configured */ - -/* - * Select and enable endpoints - */ - -/* Table 13-1. USB Function Module Registers (endpoint configuration) */ -#define UDC_EPBASE UDC_OFFSET(0x80) /* Endpoints base address */ -#define UDC_EP0 UDC_EPBASE /* Control endpoint configuration */ -#define UDC_EP_RX_BASE UDC_OFFSET(0x84) /* Receive endpoints base address */ -#define UDC_EP_RX(endpoint) (UDC_EP_RX_BASE + ((endpoint) - 1) * 4) -#define UDC_EP_TX_BASE UDC_OFFSET(0xC4) /* Transmit endpoints base address */ -#define UDC_EP_TX(endpoint) (UDC_EP_TX_BASE + ((endpoint) - 1) * 4) - -/* EP_NUM register fields */ -#define UDC_Setup_Sel (1 << 6) /* Setup FIFO select */ -#define UDC_EP_Sel (1 << 5) /* TX/RX FIFO select */ -#define UDC_EP_Dir (1 << 4) /* Endpoint direction */ - -/* CTRL register fields */ -#define UDC_Clr_Halt (1 << 7) /* Clear halt endpoint */ -#define UDC_Set_Halt (1 << 6) /* Set halt endpoint */ -#define UDC_Set_FIFO_En (1 << 2) /* Set FIFO enable */ -#define UDC_Clr_EP (1 << 1) /* Clear endpoint */ -#define UDC_Reset_EP (1 << 0) /* Reset endpoint */ - -/* STAT_FLG register fields */ -#define UDC_Miss_In (1 << 14) -#define UDC_Data_Flush (1 << 13) -#define UDC_ISO_Err (1 << 12) -#define UDC_ISO_FIFO_Empty (1 << 9) -#define UDC_ISO_FIFO_Full (1 << 8) -#define UDC_EP_Halted (1 << 6) -#define UDC_STALL (1 << 5) -#define UDC_NAK (1 << 4) -#define UDC_ACK (1 << 3) -#define UDC_FIFO_En (1 << 2) -#define UDC_Non_ISO_FIFO_Empty (1 << 1) -#define UDC_Non_ISO_FIFO_Full (1 << 0) - -/* EPn_RX register fields */ -#define UDC_EPn_RX_Valid (1 << 15) /* valid */ -#define UDC_EPn_RX_Db (1 << 14) /* double-buffer */ -#define UDC_EPn_RX_Iso (1 << 11) /* isochronous */ - -/* EPn_TX register fields */ -#define UDC_EPn_TX_Valid (1 << 15) /* valid */ -#define UDC_EPn_TX_Db (1 << 14) /* double-buffer */ -#define UDC_EPn_TX_Iso (1 << 11) /* isochronous */ - -#define EP0_PACKETSIZE 0x40 - -/* physical to logical endpoint mapping - * Physical endpoints are an index into device->bus->endpoint_array. - * Logical endpoints are endpoints 0 to 15 IN and OUT as defined in - * the USB specification. - * - * physical ep logical ep direction endpoint_address - * 0 0 IN and OUT 0x00 - * 1 to 15 1 to 15 OUT 0x01 to 0x0f - * 16 to 30 1 to 15 IN 0x81 to 0x8f - */ -#define PHYS_EP_TO_EP_ADDR(ep) (((ep) < 16) ? (ep) : (((ep) - 15) | 0x80)) -#define EP_ADDR_TO_PHYS_EP(a) (((a) & 0x80) ? (((a) & ~0x80) + 15) : (a)) - -/* MOD_CONF_CTRL_0 bits (FIXME: move to board hardware.h ?) */ -#define CONF_MOD_USB_W2FC_VBUS_MODE_R (1 << 17) - -/* Other registers (may be) related to USB */ - -#define CLOCK_CTRL (0xFFFE0830) -#define APLL_CTRL (0xFFFE084C) -#define DPLL_CTRL (0xFFFE083C) -#define SOFT_REQ (0xFFFE0834) -#define STATUS_REQ (0xFFFE0840) - -/* FUNC_MUX_CTRL_0 bits related to USB */ -#define UDC_VBUS_CTRL (1 << 19) -#define UDC_VBUS_MODE (1 << 18) - -/* 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_PACKET_SIZE 16 -#define UDC_BULK_PACKET_SIZE 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 */ -int udc_endpoint_write(struct usb_endpoint_instance *endpoint); - -int udc_init (void); - -void udc_enable(struct usb_device_instance *device); -void udc_disable(void); - -void udc_connect(void); -void udc_disconnect(void); - -void udc_startup_events(struct usb_device_instance *device); -void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, struct usb_endpoint_instance *endpoint); - -#endif diff --git a/include/usbdevice.h b/include/usbdevice.h new file mode 100644 index 0000000..206dbbc --- /dev/null +++ b/include/usbdevice.h @@ -0,0 +1,666 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on linux/drivers/usbd/usbd.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * 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. + * + */ + +#ifndef __USBDCORE_H__ +#define __USBDCORE_H__ + +#include +#include "usbdescriptors.h" + + +#define MAX_URBS_QUEUED 5 + + +#if 1 +#define usberr(fmt,args...) serial_printf("ERROR: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args) +#else +#define usberr(fmt,args...) do{}while(0) +#endif + +#if 0 +#define usbdbg(fmt,args...) serial_printf("debug: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args) +#else +#define usbdbg(fmt,args...) do{}while(0) +#endif + +#if 0 +#define usbinfo(fmt,args...) serial_printf("info: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args) +#else +#define usbinfo(fmt,args...) do{}while(0) +#endif + +#ifndef le16_to_cpu +#define le16_to_cpu(x) (x) +#endif + +#ifndef inb +#define inb(p) (*(volatile u8*)(p)) +#endif + +#ifndef outb +#define outb(val,p) (*(volatile u8*)(p) = (val)) +#endif + +#ifndef inw +#define inw(p) (*(volatile u16*)(p)) +#endif + +#ifndef outw +#define outw(val,p) (*(volatile u16*)(p) = (val)) +#endif + +#ifndef inl +#define inl(p) (*(volatile u32*)(p)) +#endif + +#ifndef outl +#define outl(val,p) (*(volatile u32*)(p) = (val)) +#endif + +#ifndef insw +#define insw(p,to,len) mmio_insw(p,to,len) +#endif + +#ifndef outsw +#define outsw(p,from,len) mmio_outsw(p,from,len) +#endif + +#ifndef insb +#define insb(p,to,len) mmio_insb(p,to,len) +#endif + +#ifndef mmio_insw +#define mmio_insw(r,b,l) ({ int __i ; \ + u16 *__b2; \ + __b2 = (u16 *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = inw(r); \ + }; \ + }) +#endif + +#ifndef mmio_outsw +#define mmio_outsw(r,b,l) ({ int __i; \ + u16 *__b2; \ + __b2 = (u16 *) b; \ + for (__i = 0; __i < l; __i++) { \ + outw( *(__b2 + __i), r); \ + } \ + }) +#endif + +#ifndef mmio_insb +#define mmio_insb(r,b,l) ({ int __i ; \ + u8 *__b2; \ + __b2 = (u8 *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = inb(r); \ + }; \ + }) +#endif + +/* + * Structure member address manipulation macros. + * These are used by client code (code using the urb_link routines), since + * the urb_link structure is embedded in the client data structures. + * + * Note: a macro offsetof equivalent to member_offset is defined in stddef.h + * but this is kept here for the sake of portability. + * + * p2surround returns a pointer to the surrounding structure given + * type of the surrounding structure, the name memb of the structure + * member pointed at by ptr. For example, if you have: + * + * struct foo { + * int x; + * float y; + * char z; + * } thingy; + * + * char *cp = &thingy.z; + * + * then + * + * &thingy == p2surround(struct foo, z, cp) + * + * Clear? + */ +#define _cv_(ptr) ((char*)(void*)(ptr)) +#define member_offset(type,memb) (_cv_(&(((type*)0)->memb))-(char*)0) +#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb))) + +struct urb; + +struct usb_endpoint_instance; +struct usb_interface_instance; +struct usb_configuration_instance; +struct usb_device_instance; +struct usb_bus_instance; + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * USB types + */ +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients + */ +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * USB directions + */ +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID (USB_TYPE_CLASS | 0x01) +#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) +#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_HID_SIZE 9 + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN") + +/* + * HID requests + */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0A +#define USB_REQ_SET_PROTOCOL 0x0B + + +/* + * USB Spec Release number + */ + +#define USB_BCD_VERSION 0x0110 + + +/* + * Device Requests (c.f Table 9-2) + */ + +#define USB_REQ_DIRECTION_MASK 0x80 +#define USB_REQ_TYPE_MASK 0x60 +#define USB_REQ_RECIPIENT_MASK 0x1f + +#define USB_REQ_DEVICE2HOST 0x80 +#define USB_REQ_HOST2DEVICE 0x00 + +#define USB_REQ_TYPE_STANDARD 0x00 +#define USB_REQ_TYPE_CLASS 0x20 +#define USB_REQ_TYPE_VENDOR 0x40 + +#define USB_REQ_RECIPIENT_DEVICE 0x00 +#define USB_REQ_RECIPIENT_INTERFACE 0x01 +#define USB_REQ_RECIPIENT_ENDPOINT 0x02 +#define USB_REQ_RECIPIENT_OTHER 0x03 + +/* + * get status bits + */ + +#define USB_STATUS_SELFPOWERED 0x01 +#define USB_STATUS_REMOTEWAKEUP 0x02 + +#define USB_STATUS_HALT 0x01 + +/* + * descriptor types + */ + +#define USB_DESCRIPTOR_TYPE_DEVICE 0x01 +#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02 +#define USB_DESCRIPTOR_TYPE_STRING 0x03 +#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04 +#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 +#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06 +#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 0x07 +#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08 +#define USB_DESCRIPTOR_TYPE_HID 0x21 +#define USB_DESCRIPTOR_TYPE_REPORT 0x22 + +#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \ + usbd_device_descriptors[x] : "UNKNOWN") + +/* + * standard feature selectors + */ +#define USB_ENDPOINT_HALT 0x00 +#define USB_DEVICE_REMOTE_WAKEUP 0x01 +#define USB_TEST_MODE 0x02 + + +/* USB Requests + * + */ + +struct usb_device_request { + u8 bmRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} __attribute__ ((packed)); + + +/* USB Status + * + */ +typedef enum urb_send_status { + SEND_IN_PROGRESS, + SEND_FINISHED_OK, + SEND_FINISHED_ERROR, + RECV_READY, + RECV_OK, + RECV_ERROR +} urb_send_status_t; + +/* + * Device State (c.f USB Spec 2.0 Figure 9-1) + * + * What state the usb device is in. + * + * Note the state does not change if the device is suspended, we simply set a + * flag to show that it is suspended. + * + */ +typedef enum usb_device_state { + STATE_INIT, /* just initialized */ + STATE_CREATED, /* just created */ + STATE_ATTACHED, /* we are attached */ + STATE_POWERED, /* we have seen power indication (electrical bus signal) */ + STATE_DEFAULT, /* we been reset */ + STATE_ADDRESSED, /* we have been addressed (in default configuration) */ + STATE_CONFIGURED, /* we have seen a set configuration device command */ + STATE_UNKNOWN, /* destroyed */ +} usb_device_state_t; + +#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN") + +/* + * Device status + * + * Overall state + */ +typedef enum usb_device_status { + USBD_OPENING, /* we are currently opening */ + USBD_OK, /* ok to use */ + USBD_SUSPENDED, /* we are currently suspended */ + USBD_CLOSING, /* we are currently closing */ +} usb_device_status_t; + +#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN") + +/* + * Device Events + * + * These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1). + * + * There are additional events defined to handle some extra actions we need + * to have handled. + * + */ +typedef enum usb_device_event { + + DEVICE_UNKNOWN, /* bi - unknown event */ + DEVICE_INIT, /* bi - initialize */ + DEVICE_CREATE, /* bi - */ + DEVICE_HUB_CONFIGURED, /* bi - bus has been plugged int */ + DEVICE_RESET, /* bi - hub has powered our port */ + + DEVICE_ADDRESS_ASSIGNED, /* ep0 - set address setup received */ + DEVICE_CONFIGURED, /* ep0 - set configure setup received */ + DEVICE_SET_INTERFACE, /* ep0 - set interface setup received */ + + DEVICE_SET_FEATURE, /* ep0 - set feature setup received */ + DEVICE_CLEAR_FEATURE, /* ep0 - clear feature setup received */ + + DEVICE_DE_CONFIGURED, /* ep0 - set configure setup received for ?? */ + + DEVICE_BUS_INACTIVE, /* bi - bus in inactive (no SOF packets) */ + DEVICE_BUS_ACTIVITY, /* bi - bus is active again */ + + DEVICE_POWER_INTERRUPTION, /* bi - hub has depowered our port */ + DEVICE_HUB_RESET, /* bi - bus has been unplugged */ + DEVICE_DESTROY, /* bi - device instance should be destroyed */ + + DEVICE_HOTPLUG, /* bi - a hotplug event has occured */ + + DEVICE_FUNCTION_PRIVATE, /* function - private */ + +} usb_device_event_t; + + +typedef struct urb_link { + struct urb_link *next; + struct urb_link *prev; +} urb_link; + +/* USB Data structure - for passing data around. + * + * This is used for both sending and receiving data. + * + * The callback function is used to let the function driver know when + * transmitted data has been sent. + * + * The callback function is set by the alloc_recv function when an urb is + * allocated for receiving data for an endpoint and used to call the + * function driver to inform it that data has arrived. + */ + +#define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */ +struct urb { + + struct usb_endpoint_instance *endpoint; + struct usb_device_instance *device; + + struct usb_device_request device_request; /* contents of received SETUP packet */ + + struct urb_link link; /* embedded struct for circular doubly linked list of urbs */ + + u8* buffer; + unsigned int buffer_length; + unsigned int actual_length; + + urb_send_status_t status; + int data; + + u16 buffer_data[URB_BUF_SIZE]; /* data received (OUT) or being sent (IN) */ +}; + +/* Endpoint configuration + * + * Per endpoint configuration data. Used to track which function driver owns + * an endpoint. + * + */ +struct usb_endpoint_instance { + int endpoint_address; /* logical endpoint address */ + + /* control */ + int status; /* halted */ + int state; /* available for use by bus interface driver */ + + /* receive side */ + struct urb_link rcv; /* received urbs */ + struct urb_link rdy; /* empty urbs ready to receive */ + struct urb *rcv_urb; /* active urb */ + int rcv_attributes; /* copy of bmAttributes from endpoint descriptor */ + int rcv_packetSize; /* maximum packet size from endpoint descriptor */ + int rcv_transferSize; /* maximum transfer size from function driver */ + int rcv_queue; + + /* transmit side */ + struct urb_link tx; /* urbs ready to transmit */ + struct urb_link done; /* transmitted urbs */ + struct urb *tx_urb; /* active urb */ + int tx_attributes; /* copy of bmAttributes from endpoint descriptor */ + int tx_packetSize; /* maximum packet size from endpoint descriptor */ + int tx_transferSize; /* maximum transfer size from function driver */ + int tx_queue; + + int sent; /* data already sent */ + int last; /* data sent in last packet XXX do we need this */ +}; + +struct usb_alternate_instance { + struct usb_interface_descriptor *interface_descriptor; + + int endpoints; + int *endpoint_transfersize_array; + struct usb_endpoint_descriptor **endpoints_descriptor_array; +}; + +struct usb_interface_instance { + int alternates; + struct usb_alternate_instance *alternates_instance_array; +}; + +struct usb_configuration_instance { + int interfaces; + struct usb_configuration_descriptor *configuration_descriptor; + struct usb_interface_instance *interface_instance_array; +}; + + +/* USB Device Instance + * + * For each physical bus interface we create a logical device structure. This + * tracks all of the required state to track the USB HOST's view of the device. + * + * Keep track of the device configuration for a real physical bus interface, + * this includes the bus interface, multiple function drivers, the current + * configuration and the current state. + * + * This will show: + * the specific bus interface driver + * the default endpoint 0 driver + * the configured function driver + * device state + * device status + * endpoint list + */ + +struct usb_device_instance { + + /* generic */ + char *name; + struct usb_device_descriptor *device_descriptor; /* per device descriptor */ + + 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 */ + + /* configuration descriptors */ + int configurations; + struct usb_configuration_instance *configuration_instance_array; + + /* device state */ + usb_device_state_t device_state; /* current USB Device state */ + usb_device_state_t device_previous_state; /* current USB Device state */ + + u8 address; /* current address (zero is default) */ + u8 configuration; /* current show configuration (zero is default) */ + u8 interface; /* current interface (zero is default) */ + u8 alternate; /* alternate flag */ + + usb_device_status_t status; /* device status */ + + int urbs_queued; /* number of submitted urbs */ + + /* Shouldn't need to make this atomic, all we need is a change indicator */ + unsigned long usbd_rxtx_timestamp; + unsigned long usbd_last_rxtx_timestamp; + +}; + +/* Bus Interface configuration structure + * + * This is allocated for each configured instance of a bus interface driver. + * + * The privdata pointer may be used by the bus interface driver to store private + * per instance state information. + */ +struct usb_bus_instance { + + struct usb_device_instance *device; + struct usb_endpoint_instance *endpoint_array; /* array of available configured endpoints */ + + int max_endpoints; /* maximimum number of rx enpoints */ + unsigned char maxpacketsize; + + unsigned int serial_number; + char *serial_number_str; + void *privdata; /* private data for the bus interface */ + +}; + +extern char *usbd_device_events[]; +extern char *usbd_device_states[]; +extern char *usbd_device_status[]; +extern char *usbd_device_requests[]; +extern char *usbd_device_descriptors[]; + +void urb_link_init (urb_link * ul); +void urb_detach (struct urb *urb); +urb_link *first_urb_link (urb_link * hd); +struct urb *first_urb (urb_link * hd); +struct urb *first_urb_detached (urb_link * hd); +void urb_append (urb_link * hd, struct urb *urb); + +struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint); +void usbd_dealloc_urb (struct urb *urb); + +/* + * usbd_device_event is used by bus interface drivers to tell the higher layers that + * certain events have taken place. + */ +void usbd_device_event_irq (struct usb_device_instance *conf, usb_device_event_t, int); +void usbd_device_event (struct usb_device_instance *conf, usb_device_event_t, int); + +/* descriptors + * + * Various ways of finding descriptors based on the current device and any + * possible configuration / interface / endpoint for it. + */ +struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct usb_device_instance *, int, int); +struct usb_function_instance *usbd_device_function_instance (struct usb_device_instance *, unsigned int); +struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *, int, int, int); +struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *, int, int, int, int); +struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance *, int, int, int, int); +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance *, int, int, int, int, int); +struct usb_class_descriptor *usbd_device_class_descriptor_index (struct usb_device_instance *, int, int, int, int, int); +struct usb_class_report_descriptor *usbd_device_class_report_descriptor_index( struct usb_device_instance *, int , int , int , int , int ); +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *, int, int, int, int, int); +int usbd_device_endpoint_transfersize (struct usb_device_instance *, int, int, int, int, int); +struct usb_string_descriptor *usbd_get_string (u8); +struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *, int); + +int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint); +void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad); +void usbd_tx_complete (struct usb_endpoint_instance *endpoint); + +#endif -- cgit v0.10.2