diff -Nur linux-2.6.36.orig/arch/cris/Kconfig linux-2.6.36/arch/cris/Kconfig --- linux-2.6.36.orig/arch/cris/Kconfig 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/Kconfig 2010-11-15 17:57:17.000000000 +0100 @@ -177,6 +177,12 @@ help Size of DRAM (decimal in MB) typically 2, 8 or 16. +config ETRAX_MTD_SIZE + hex "MTD size (hex)" + default "0x00800000" + help + Size of MTD device typically 4 or 8 MB. + config ETRAX_VMEM_SIZE int "Video memory size (dec, in MB)" depends on ETRAX_ARCH_V32 && !ETRAXFS @@ -282,7 +288,7 @@ select MTD_CFI_AMDSTD select MTD_JEDECPROBE if ETRAX_ARCH_V32 select MTD_CHAR - select MTD_BLOCK + select MTD_BLOCK_RO select MTD_PARTITIONS select MTD_CONCAT select MTD_COMPLEX_MAPPINGS @@ -671,6 +677,11 @@ source "drivers/ide/Kconfig" +#mysteriously part of this standard linux driver was removed from cris build! - info@crisos.org +source "drivers/scsi/Kconfig" + +source "drivers/media/Kconfig" + source "drivers/net/Kconfig" source "drivers/i2c/Kconfig" diff -Nur linux-2.6.36.orig/arch/cris/Makefile linux-2.6.36/arch/cris/Makefile --- linux-2.6.36.orig/arch/cris/Makefile 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/Makefile 2010-11-15 17:57:17.000000000 +0100 @@ -40,10 +40,10 @@ LD = $(CROSS_COMPILE)ld -mcrislinux -OBJCOPYFLAGS := -O binary -R .note -R .comment -S +OBJCOPYFLAGS := -O binary -R .bss -R .note -R .note.gnu.build-id -R .comment -S KBUILD_AFLAGS += -mlinux -march=$(arch-y) $(inc) -KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe $(inc) +KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe -fno-peephole2 $(inc) KBUILD_CPPFLAGS += $(inc) ifdef CONFIG_FRAME_POINTER diff -Nur linux-2.6.36.orig/arch/cris/arch-v10/drivers/axisflashmap.c linux-2.6.36/arch/cris/arch-v10/drivers/axisflashmap.c --- linux-2.6.36.orig/arch/cris/arch-v10/drivers/axisflashmap.c 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/arch-v10/drivers/axisflashmap.c 2010-11-15 17:57:17.000000000 +0100 @@ -113,7 +113,7 @@ /* If no partition-table was found, we use this default-set. */ #define MAX_PARTITIONS 7 -#define NUM_DEFAULT_PARTITIONS 3 +#define NUM_DEFAULT_PARTITIONS 4 /* * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the @@ -122,19 +122,24 @@ */ static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { { - .name = "boot firmware", - .size = CONFIG_ETRAX_PTABLE_SECTOR, + .name = "kernel", + .size = 0x00, .offset = 0 }, { - .name = "kernel", - .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), - .offset = CONFIG_ETRAX_PTABLE_SECTOR + .name = "rootfs", + .size = 0x200000 , + .offset = 0x200000 }, { - .name = "filesystem", - .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) + .name = "cfgfs", + .size = 0x20000 , + .offset = CONFIG_ETRAX_MTD_SIZE - 0x20000 + }, + { + .name = "linux", + .size = CONFIG_ETRAX_MTD_SIZE - 0x20000, + .offset = 0 } }; @@ -281,6 +286,11 @@ struct partitiontable_entry *ptable; int use_default_ptable = 1; /* Until proven otherwise. */ const char pmsg[] = " /dev/flash%d at 0x%08x, size 0x%08x\n"; + unsigned int kernel_part_size = 0; + unsigned char *flash_mem = (unsigned char*)(FLASH_CACHED_ADDR); + unsigned int flash_scan_count = 0; + const char *part_magic = "ACME_PART_MAGIC"; + unsigned int magic_len = strlen(part_magic); if (!(mymtd = flash_probe())) { /* There's no reason to use this module if no flash chip can @@ -292,6 +302,31 @@ mymtd->name, mymtd->size); axisflash_mtd = mymtd; } + /* scan flash to findout where out partition starts */ + + printk(KERN_INFO "Scanning flash for end of kernel magic\n"); + for(flash_scan_count = 0; flash_scan_count < 100000; flash_scan_count++){ + if(strncmp(&flash_mem[flash_scan_count], part_magic, magic_len - 1) == 0) + { + kernel_part_size = flash_mem[flash_scan_count + magic_len ]; + kernel_part_size <<= 8; + kernel_part_size += flash_mem[flash_scan_count + magic_len + 2]; + kernel_part_size <<= 8; + kernel_part_size += flash_mem[flash_scan_count + magic_len + 1]; + kernel_part_size <<= 8; + kernel_part_size += flash_mem[flash_scan_count + magic_len + 3]; + printk(KERN_INFO "Kernel ends at 0x%.08X\n", kernel_part_size); + flash_scan_count = 1100000; + } + } + + + if(kernel_part_size){ + kernel_part_size = (kernel_part_size & 0xffff0000); + axis_default_partitions[0].size = kernel_part_size; + axis_default_partitions[1].size = mymtd->size - axis_default_partitions[0].size - axis_default_partitions[2].size; + axis_default_partitions[1].offset = axis_default_partitions[0].size; + } if (mymtd) { mymtd->owner = THIS_MODULE; @@ -360,21 +395,6 @@ use_default_ptable = !ptable_ok; } - if (romfs_in_flash) { - /* Add an overlapping device for the root partition (romfs). */ - - axis_partitions[pidx].name = "romfs"; - axis_partitions[pidx].size = romfs_length; - axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; - axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; - - printk(KERN_INFO - " Adding readonly flash partition for romfs image:\n"); - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); - pidx++; - } - #ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE if (mymtd) { main_partition.size = mymtd->size; @@ -397,36 +417,6 @@ if (err) panic("axisflashmap could not add MTD partitions!\n"); } - - if (!romfs_in_flash) { - /* Create an RAM device for the root partition (romfs). */ - -#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) - /* No use trying to boot this kernel from RAM. Panic! */ - printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " - "device due to kernel (mis)configuration!\n"); - panic("This kernel cannot boot from RAM!\n"); -#else - struct mtd_info *mtd_ram; - - mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!mtd_ram) - panic("axisflashmap couldn't allocate memory for " - "mtd_info!\n"); - - printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, (unsigned)romfs_start, - (unsigned)romfs_length); - - err = mtdram_init_device(mtd_ram, - (void *)romfs_start, - romfs_length, - "romfs"); - if (err) - panic("axisflashmap could not initialize MTD RAM " - "device!\n"); -#endif - } return err; } diff -Nur linux-2.6.36.orig/arch/cris/arch-v10/drivers/ds1302.c linux-2.6.36/arch/cris/arch-v10/drivers/ds1302.c --- linux-2.6.36.orig/arch/cris/arch-v10/drivers/ds1302.c 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/arch-v10/drivers/ds1302.c 2010-11-15 17:57:17.000000000 +0100 @@ -22,6 +22,7 @@ #include <linux/smp_lock.h> #include <linux/bcd.h> #include <linux/capability.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -499,6 +500,10 @@ return 0; } +#ifdef CONFIG_SYSFS +static struct class *rtc_class; +#endif + static int __init ds1302_register(void) { ds1302_init(); @@ -507,6 +512,12 @@ ds1302_name, RTC_MAJOR_NR); return -1; } + #ifdef CONFIG_SYSFS + rtc_class = class_create(THIS_MODULE, "rtc"); + class_device_create(rtc_class, NULL, MKDEV(RTC_MAJOR_NR, 0), + NULL, "rtc"); + #endif + return 0; } diff -Nur linux-2.6.36.orig/arch/cris/arch-v10/drivers/gpio.c linux-2.6.36/arch/cris/arch-v10/drivers/gpio.c --- linux-2.6.36.orig/arch/cris/arch-v10/drivers/gpio.c 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/arch-v10/drivers/gpio.c 2010-11-15 17:57:17.000000000 +0100 @@ -20,6 +20,7 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/device.h> #include <asm/etraxgpio.h> #include <arch/svinto.h> @@ -797,6 +798,10 @@ /* main driver initialization routine, called from mem.c */ +#ifdef CONFIG_SYSFS +static struct class *gpio_class; +#endif + static int __init gpio_init(void) { int res; @@ -810,6 +815,13 @@ return res; } +#ifdef CONFIG_SYSFS + gpio_class = class_create(THIS_MODULE, "gpio"); + device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, 0), NULL, "gpioa"); + device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, 1), NULL, "gpiob"); + device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, 2), NULL, "leds"); + device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, 3), NULL, "gpiog"); +#endif /* Clear all leds */ #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) CRIS_LED_NETWORK_SET(0); diff -Nur linux-2.6.36.orig/arch/cris/arch-v10/lib/hw_settings.S linux-2.6.36/arch/cris/arch-v10/lib/hw_settings.S --- linux-2.6.36.orig/arch/cris/arch-v10/lib/hw_settings.S 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/arch-v10/lib/hw_settings.S 2010-11-15 17:57:17.000000000 +0100 @@ -58,3 +58,5 @@ .dword R_PORT_PB_SET .dword PB_SET_VALUE .dword 0 ; No more register values + .ascii "ACME_PART_MAGIC" + .dword 0xdeadc0de diff -Nur linux-2.6.36.orig/arch/cris/arch-v10/mm/init.c linux-2.6.36/arch/cris/arch-v10/mm/init.c --- linux-2.6.36.orig/arch/cris/arch-v10/mm/init.c 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/arch-v10/mm/init.c 2010-11-15 17:57:17.000000000 +0100 @@ -184,6 +184,9 @@ free_area_init_node(0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); } +void free_initrd_mem(unsigned long start, unsigned long end) +{ +} /* Initialize remaps of some I/O-ports. It is important that this * is called before any driver is initialized. diff -Nur linux-2.6.36.orig/arch/cris/boot/Makefile linux-2.6.36/arch/cris/boot/Makefile --- linux-2.6.36.orig/arch/cris/boot/Makefile 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/boot/Makefile 2010-11-15 17:57:17.000000000 +0100 @@ -5,7 +5,7 @@ objcopyflags-$(CONFIG_ETRAX_ARCH_V10) += -R .note -R .comment objcopyflags-$(CONFIG_ETRAX_ARCH_V32) += --remove-section=.bss --remove-section=.note.gnu.build-id -OBJCOPYFLAGS = -O binary $(objcopyflags-y) +#OBJCOPYFLAGS = -O binary $(objcopyflags-y) subdir- := compressed rescue @@ -17,7 +17,6 @@ $(obj)/compressed/vmlinux: $(obj)/Image FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed $@ - $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin $(obj)/zImage: $(obj)/compressed/vmlinux @cp $< $@ diff -Nur linux-2.6.36.orig/arch/cris/boot/compressed/Makefile linux-2.6.36/arch/cris/boot/compressed/Makefile --- linux-2.6.36.orig/arch/cris/boot/compressed/Makefile 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/arch/cris/boot/compressed/Makefile 2010-11-15 17:57:17.000000000 +0100 @@ -18,7 +18,7 @@ OBJECTS-$(CONFIG_ETRAX_ARCH_V32) = $(obj)/head_v32.o OBJECTS-$(CONFIG_ETRAX_ARCH_V10) = $(obj)/head_v10.o OBJECTS= $(OBJECTS-y) $(obj)/misc.o -OBJCOPYFLAGS = -O binary --remove-section=.bss +#OBJCOPYFLAGS = -O binary --remove-section=.bss quiet_cmd_image = BUILD $@ cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ diff -Nur linux-2.6.36.orig/drivers/net/cris/eth_v10.c linux-2.6.36/drivers/net/cris/eth_v10.c --- linux-2.6.36.orig/drivers/net/cris/eth_v10.c 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/drivers/net/cris/eth_v10.c 2010-11-15 17:57:17.000000000 +0100 @@ -1718,7 +1718,7 @@ static void e100_netpoll(struct net_device* netdev) { - e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev, NULL); + e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev); } #endif diff -Nur linux-2.6.36.orig/drivers/serial/crisv10.c linux-2.6.36/drivers/serial/crisv10.c --- linux-2.6.36.orig/drivers/serial/crisv10.c 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/drivers/serial/crisv10.c 2010-11-15 17:57:17.000000000 +0100 @@ -13,6 +13,7 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/tty.h> @@ -27,6 +28,7 @@ #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/bitops.h> +#include <linux/device.h> #include <linux/seq_file.h> #include <linux/delay.h> #include <linux/module.h> @@ -4426,6 +4428,7 @@ #endif }; +static struct class *rs_class; static int __init rs_init(void) { int i; @@ -4559,6 +4562,24 @@ #endif #endif /* CONFIG_SVINTO_SIM */ + rs_class = class_create(THIS_MODULE, "rs_tty"); +#ifdef CONFIG_ETRAX_SERIAL_PORT0 + device_create(rs_class, NULL, + MKDEV(TTY_MAJOR, 64), NULL, "ttyS0"); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1 + device_create(rs_class, NULL, + MKDEV(TTY_MAJOR, 65), NULL, "ttyS1"); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + device_create(rs_class, NULL, + MKDEV(TTY_MAJOR, 66), NULL, "ttyS2"); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3 + device_create(rs_class, NULL, + MKDEV(TTY_MAJOR, 67), NULL, "ttyS3"); +#endif + return 0; } diff -Nur linux-2.6.36.orig/drivers/usb/Makefile linux-2.6.36/drivers/usb/Makefile --- linux-2.6.36.orig/drivers/usb/Makefile 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/drivers/usb/Makefile 2010-11-15 17:57:18.000000000 +0100 @@ -21,6 +21,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_ISP1760_HCD) += host/ +obj-$(CONFIG_ETRAX_USB_HOST) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ diff -Nur linux-2.6.36.orig/drivers/usb/host/Makefile linux-2.6.36/drivers/usb/host/Makefile --- linux-2.6.36.orig/drivers/usb/host/Makefile 2010-10-20 22:30:22.000000000 +0200 +++ linux-2.6.36/drivers/usb/host/Makefile 2010-11-15 17:57:18.000000000 +0100 @@ -32,5 +32,6 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o +obj-$(CONFIG_ETRAX_USB_HOST) += hc-crisv10.o obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o diff -Nur linux-2.6.36.orig/drivers/usb/host/hc-cris-dbg.h linux-2.6.36/drivers/usb/host/hc-cris-dbg.h --- linux-2.6.36.orig/drivers/usb/host/hc-cris-dbg.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.36/drivers/usb/host/hc-cris-dbg.h 2010-11-15 17:57:17.000000000 +0100 @@ -0,0 +1,146 @@ + +/* macros for debug output */ + +#define warn(fmt, args...) \ + printk(KERN_INFO "crisv10 warn: ");printk(fmt, ## args) + +#define hcd_dbg(hcd, fmt, args...) \ + dev_info(hcd->self.controller, fmt, ## args) +#define hcd_err(hcd, fmt, args...) \ + dev_err(hcd->self.controller, fmt, ## args) +#define hcd_info(hcd, fmt, args...) \ + dev_info(hcd->self.controller, fmt, ## args) +#define hcd_warn(hcd, fmt, args...) \ + dev_warn(hcd->self.controller, fmt, ## args) + +/* +#define devdrv_dbg(fmt, args...) \ + printk(KERN_INFO "usb_devdrv dbg: ");printk(fmt, ## args) +*/ +#define devdrv_dbg(fmt, args...) {} + +#define devdrv_err(fmt, args...) \ + printk(KERN_ERR "usb_devdrv error: ");printk(fmt, ## args) +#define devdrv_info(fmt, args...) \ + printk(KERN_INFO "usb_devdrv: ");printk(fmt, ## args) + +#define irq_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_irq dbg: ");printk(fmt, ## args) +#define irq_err(fmt, args...) \ + printk(KERN_ERR "crisv10_irq error: ");printk(fmt, ## args) +#define irq_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_irq warn: ");printk(fmt, ## args) +#define irq_info(fmt, args...) \ + printk(KERN_INFO "crisv10_hcd: ");printk(fmt, ## args) + +/* +#define rh_dbg(fmt, args...) \ + printk(KERN_DEBUG "crisv10_rh dbg: ");printk(fmt, ## args) +*/ +#define rh_dbg(fmt, args...) {} + +#define rh_err(fmt, args...) \ + printk(KERN_ERR "crisv10_rh error: ");printk(fmt, ## args) +#define rh_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_rh warning: ");printk(fmt, ## args) +#define rh_info(fmt, args...) \ + printk(KERN_INFO "crisv10_rh: ");printk(fmt, ## args) + +/* +#define tc_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_tc dbg: ");printk(fmt, ## args) +*/ +#define tc_dbg(fmt, args...) {while(0){}} + +#define tc_err(fmt, args...) \ + printk(KERN_ERR "crisv10_tc error: ");printk(fmt, ## args) +/* +#define tc_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_tc warning: ");printk(fmt, ## args) +*/ +#define tc_warn(fmt, args...) {while(0){}} + +#define tc_info(fmt, args...) \ + printk(KERN_INFO "crisv10_tc: ");printk(fmt, ## args) + + +/* Debug print-outs for various traffic types */ + +#define intr_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_intr warning: ");printk(fmt, ## args) + +#define intr_dbg(fmt, args...) \ + printk(KERN_DEBUG "crisv10_intr dbg: ");printk(fmt, ## args) +/* +#define intr_dbg(fmt, args...) {while(0){}} +*/ + + +#define isoc_err(fmt, args...) \ + printk(KERN_ERR "crisv10_isoc error: ");printk(fmt, ## args) +/* +#define isoc_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_isoc warning: ");printk(fmt, ## args) +*/ +#define isoc_warn(fmt, args...) {while(0){}} + +/* +#define isoc_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_isoc dbg: ");printk(fmt, ## args) +*/ +#define isoc_dbg(fmt, args...) {while(0){}} + +/* +#define timer_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_timer warning: ");printk(fmt, ## args) +*/ +#define timer_warn(fmt, args...) {while(0){}} + +/* +#define timer_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_timer dbg: ");printk(fmt, ## args) +*/ +#define timer_dbg(fmt, args...) {while(0){}} + + +/* Debug printouts for events related to late finishing of URBs */ + +#define late_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_late dbg: ");printk(fmt, ## args) +/* +#define late_dbg(fmt, args...) {while(0){}} +*/ + +#define late_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_late warning: ");printk(fmt, ## args) +/* +#define errno_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_errno dbg: ");printk(fmt, ## args) +*/ +#define errno_dbg(fmt, args...) {while(0){}} + + +#define dma_dbg(fmt, args...) \ + printk(KERN_INFO "crisv10_dma dbg: ");printk(fmt, ## args) +#define dma_err(fmt, args...) \ + printk(KERN_ERR "crisv10_dma error: ");printk(fmt, ## args) +#define dma_warn(fmt, args...) \ + printk(KERN_INFO "crisv10_dma warning: ");printk(fmt, ## args) +#define dma_info(fmt, args...) \ + printk(KERN_INFO "crisv10_dma: ");printk(fmt, ## args) + + + +#define str_dir(pipe) \ + (usb_pipeout(pipe) ? "out" : "in") +#define str_type(pipe) \ + ({ \ + char *s = "?"; \ + switch (usb_pipetype(pipe)) { \ + case PIPE_ISOCHRONOUS: s = "iso"; break; \ + case PIPE_INTERRUPT: s = "intr"; break; \ + case PIPE_CONTROL: s = "ctrl"; break; \ + case PIPE_BULK: s = "bulk"; break; \ + }; \ + s; \ + }) diff -Nur linux-2.6.36.orig/drivers/usb/host/hc-crisv10.c linux-2.6.36/drivers/usb/host/hc-crisv10.c --- linux-2.6.36.orig/drivers/usb/host/hc-crisv10.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.36/drivers/usb/host/hc-crisv10.c 2010-11-15 17:57:18.000000000 +0100 @@ -0,0 +1,4801 @@ +/* + * + * ETRAX 100LX USB Host Controller Driver + * + * Copyright (C) 2005, 2006 Axis Communications AB + * + * Author: Konrad Eriksson <konrad.eriksson@axis.se> + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/usb.h> +#include <linux/platform_device.h> +#include <linux/usb/hcd.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <arch/dma.h> +#include <arch/io_interface_mux.h> + +#include "hc-crisv10.h" +#include "hc-cris-dbg.h" + + +/***************************************************************************/ +/***************************************************************************/ +/* Host Controller settings */ +/***************************************************************************/ +/***************************************************************************/ + +#define VERSION "1.00 hinko.4" +#define COPYRIGHT "(c) 2005, 2006 Axis Communications AB" +#define DESCRIPTION "ETRAX 100LX USB Host Controller" + +#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR +#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR +#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR + +/* Number of physical ports in Etrax 100LX */ +#define USB_ROOT_HUB_PORTS 2 + +const char hc_name[] = "hc-crisv10"; +const char product_desc[] = DESCRIPTION; + +/* The number of epids is, among other things, used for pre-allocating + ctrl, bulk and isoc EP descriptors (one for each epid). + Assumed to be > 1 when initiating the DMA lists. */ +#define NBR_OF_EPIDS 32 + +/* Support interrupt traffic intervals up to 128 ms. */ +#define MAX_INTR_INTERVAL 128 + +/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP + table must be "invalid". By this we mean that we shouldn't care about epid + attentions for this epid, or at least handle them differently from epid + attentions for "valid" epids. This define determines which one to use + (don't change it). */ +#define INVALID_EPID 31 +/* A special epid for the bulk dummys. */ +#define DUMMY_EPID 30 + +/* Module settings */ + +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Konrad Eriksson <konrad.eriksson@axis.se>"); + + +/* Module parameters */ + +/* 0 = No ports enabled + 1 = Only port 1 enabled (on board ethernet on devboard) + 2 = Only port 2 enabled (external connector on devboard) + 3 = Both ports enabled +*/ +static unsigned int ports = 3; +module_param(ports, uint, S_IRUGO); +MODULE_PARM_DESC(ports, "Bitmask indicating USB ports to use"); + + +/***************************************************************************/ +/***************************************************************************/ +/* Shared global variables for this module */ +/***************************************************************************/ +/***************************************************************************/ + +/* EP descriptor lists for non period transfers. Must be 32-bit aligned. */ +static volatile struct USB_EP_Desc TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); + +static volatile struct USB_EP_Desc TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); + +/* EP descriptor lists for period transfers. Must be 32-bit aligned. */ +static volatile struct USB_EP_Desc TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4))); +static volatile struct USB_SB_Desc TxIntrSB_zout __attribute__ ((aligned (4))); + +static volatile struct USB_EP_Desc TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); +static volatile struct USB_SB_Desc TxIsocSB_zout __attribute__ ((aligned (4))); + +//static volatile struct USB_SB_Desc TxIsocSBList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); + +/* After each enabled bulk EP IN we put two disabled EP descriptors with the eol flag set, + causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which + gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the + EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors + in each frame. */ +static volatile struct USB_EP_Desc TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4))); + +/* List of URB pointers, where each points to the active URB for a epid. + For Bulk, Ctrl and Intr this means which URB that currently is added to + DMA lists (Isoc URBs are all directly added to DMA lists). As soon as + URB has completed is the queue examined and the first URB in queue is + removed and moved to the activeUrbList while its state change to STARTED and + its transfer(s) gets added to DMA list (exception Isoc where URBs enter + state STARTED directly and added transfers added to DMA lists). */ +static struct urb *activeUrbList[NBR_OF_EPIDS]; + +/* Additional software state info for each epid */ +static struct etrax_epid epid_state[NBR_OF_EPIDS]; + +/* Timer handles for bulk traffic timer used to avoid DMA bug where DMA stops + even if there is new data waiting to be processed */ +static struct timer_list bulk_start_timer = TIMER_INITIALIZER(NULL, 0, 0); +static struct timer_list bulk_eot_timer = TIMER_INITIALIZER(NULL, 0, 0); + +/* We want the start timer to expire before the eot timer, because the former + might start traffic, thus making it unnecessary for the latter to time + out. */ +#define BULK_START_TIMER_INTERVAL (HZ/50) /* 20 ms */ +#define BULK_EOT_TIMER_INTERVAL (HZ/16) /* 60 ms */ + +/* Delay before a URB completion happen when it's scheduled to be delayed */ +#define LATER_TIMER_DELAY (HZ/50) /* 20 ms */ + +/* Simplifying macros for checking software state info of a epid */ +/* ----------------------------------------------------------------------- */ +#define epid_inuse(epid) epid_state[epid].inuse +#define epid_out_traffic(epid) epid_state[epid].out_traffic +#define epid_isoc(epid) (epid_state[epid].type == PIPE_ISOCHRONOUS ? 1 : 0) +#define epid_intr(epid) (epid_state[epid].type == PIPE_INTERRUPT ? 1 : 0) + + +/***************************************************************************/ +/***************************************************************************/ +/* DEBUG FUNCTIONS */ +/***************************************************************************/ +/***************************************************************************/ +/* Note that these functions are always available in their "__" variants, + for use in error situations. The "__" missing variants are controlled by + the USB_DEBUG_DESC/USB_DEBUG_URB macros. */ +static void __dump_urb(struct urb* purb) +{ + struct crisv10_urb_priv *urb_priv = purb->hcpriv; + int urb_num = -1; + if(urb_priv) { + urb_num = urb_priv->urb_num; + } + printk("\nURB:0x%x[%d]\n", (unsigned int)purb, urb_num); + printk("dev :0x%08lx\n", (unsigned long)purb->dev); + printk("pipe :0x%08x\n", purb->pipe); + printk("status :%d\n", purb->status); + printk("transfer_flags :0x%08x\n", purb->transfer_flags); + printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer); + printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length); + printk("actual_length :%d\n", purb->actual_length); + printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet); + printk("start_frame :%d\n", purb->start_frame); + printk("number_of_packets :%d\n", purb->number_of_packets); + printk("interval :%d\n", purb->interval); + printk("error_count :%d\n", purb->error_count); + printk("context :0x%08lx\n", (unsigned long)purb->context); + printk("complete :0x%08lx\n\n", (unsigned long)purb->complete); +} + +static void __dump_in_desc(volatile struct USB_IN_Desc *in) +{ + printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in); + printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len); + printk(" command : 0x%04x\n", in->command); + printk(" next : 0x%08lx\n", in->next); + printk(" buf : 0x%08lx\n", in->buf); + printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len); + printk(" status : 0x%04x\n\n", in->status); +} + +static void __dump_sb_desc(volatile struct USB_SB_Desc *sb) +{ + char tt = (sb->command & 0x30) >> 4; + char *tt_string; + + switch (tt) { + case 0: + tt_string = "zout"; + break; + case 1: + tt_string = "in"; + break; + case 2: + tt_string = "out"; + break; + case 3: + tt_string = "setup"; + break; + default: + tt_string = "unknown (weird)"; + } + + printk(" USB_SB_Desc at 0x%08lx ", (unsigned long)sb); + printk(" command:0x%04x (", sb->command); + printk("rem:%d ", (sb->command & 0x3f00) >> 8); + printk("full:%d ", (sb->command & 0x40) >> 6); + printk("tt:%d(%s) ", tt, tt_string); + printk("intr:%d ", (sb->command & 0x8) >> 3); + printk("eot:%d ", (sb->command & 0x2) >> 1); + printk("eol:%d)", sb->command & 0x1); + printk(" sw_len:0x%04x(%d)", sb->sw_len, sb->sw_len); + printk(" next:0x%08lx", sb->next); + printk(" buf:0x%08lx\n", sb->buf); +} + + +static void __dump_ep_desc(volatile struct USB_EP_Desc *ep) +{ + printk("USB_EP_Desc at 0x%08lx ", (unsigned long)ep); + printk(" command:0x%04x (", ep->command); + printk("ep_id:%d ", (ep->command & 0x1f00) >> 8); + printk("enable:%d ", (ep->command & 0x10) >> 4); + printk("intr:%d ", (ep->command & 0x8) >> 3); + printk("eof:%d ", (ep->command & 0x2) >> 1); + printk("eol:%d)", ep->command & 0x1); + printk(" hw_len:0x%04x(%d)", ep->hw_len, ep->hw_len); + printk(" next:0x%08lx", ep->next); + printk(" sub:0x%08lx\n", ep->sub); +} + +static inline void __dump_ep_list(int pipe_type) +{ + volatile struct USB_EP_Desc *ep; + volatile struct USB_EP_Desc *first_ep; + volatile struct USB_SB_Desc *sb; + + switch (pipe_type) + { + case PIPE_BULK: + first_ep = &TxBulkEPList[0]; + break; + case PIPE_CONTROL: + first_ep = &TxCtrlEPList[0]; + break; + case PIPE_INTERRUPT: + first_ep = &TxIntrEPList[0]; + break; + case PIPE_ISOCHRONOUS: + first_ep = &TxIsocEPList[0]; + break; + default: + warn("Cannot dump unknown traffic type"); + return; + } + ep = first_ep; + + printk("\n\nDumping EP list...\n\n"); + + do { + __dump_ep_desc(ep); + /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */ + sb = ep->sub ? phys_to_virt(ep->sub) : 0; + while (sb) { + __dump_sb_desc(sb); + sb = sb->next ? phys_to_virt(sb->next) : 0; + } + ep = (volatile struct USB_EP_Desc *)(phys_to_virt(ep->next)); + + } while (ep != first_ep); +} + +static inline void __dump_ept_data(int epid) +{ + unsigned long flags; + __u32 r_usb_ept_data; + + if (epid < 0 || epid > 31) { + printk("Cannot dump ept data for invalid epid %d\n", epid); + return; + } + + local_irq_save(flags); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + r_usb_ept_data = *R_USB_EPT_DATA; + local_irq_restore(flags); + + printk(" R_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid); + if (r_usb_ept_data == 0) { + /* No need for more detailed printing. */ + return; + } + printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31); + printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30); + printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28); + printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27); + printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26); + printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24); + printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22); + printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21); + printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19); + printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11); + printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7); + printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f)); +} + +static inline void __dump_ept_data_iso(int epid) +{ + unsigned long flags; + __u32 ept_data; + + if (epid < 0 || epid > 31) { + printk("Cannot dump ept data for invalid epid %d\n", epid); + return; + } + + local_irq_save(flags); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + ept_data = *R_USB_EPT_DATA_ISO; + local_irq_restore(flags); + + printk(" R_USB_EPT_DATA = 0x%x for epid %d :\n", ept_data, epid); + if (ept_data == 0) { + /* No need for more detailed printing. */ + return; + } + printk(" valid : %d\n", IO_EXTRACT(R_USB_EPT_DATA_ISO, valid, + ept_data)); + printk(" port : %d\n", IO_EXTRACT(R_USB_EPT_DATA_ISO, port, + ept_data)); + printk(" error_code : %d\n", IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, + ept_data)); + printk(" max_len : %d\n", IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, + ept_data)); + printk(" ep : %d\n", IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, + ept_data)); + printk(" dev : %d\n", IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, + ept_data)); +} + +static inline void __dump_ept_data_list(void) +{ + int i; + + printk("Dumping the whole R_USB_EPT_DATA list\n"); + + for (i = 0; i < 32; i++) { + __dump_ept_data(i); + } +} + +static void debug_epid(int epid) { + int i; + + if(epid_isoc(epid)) { + __dump_ept_data_iso(epid); + } else { + __dump_ept_data(epid); + } + + printk("Bulk:\n"); + for(i = 0; i < 32; i++) { + if(IO_EXTRACT(USB_EP_command, epid, TxBulkEPList[i].command) == + epid) { + printk("%d: ", i); __dump_ep_desc(&(TxBulkEPList[i])); + } + } + + printk("Ctrl:\n"); + for(i = 0; i < 32; i++) { + if(IO_EXTRACT(USB_EP_command, epid, TxCtrlEPList[i].command) == + epid) { + printk("%d: ", i); __dump_ep_desc(&(TxCtrlEPList[i])); + } + } + + printk("Intr:\n"); + for(i = 0; i < MAX_INTR_INTERVAL; i++) { + if(IO_EXTRACT(USB_EP_command, epid, TxIntrEPList[i].command) == + epid) { + printk("%d: ", i); __dump_ep_desc(&(TxIntrEPList[i])); + } + } + + printk("Isoc:\n"); + for(i = 0; i < 32; i++) { + if(IO_EXTRACT(USB_EP_command, epid, TxIsocEPList[i].command) == + epid) { + printk("%d: ", i); __dump_ep_desc(&(TxIsocEPList[i])); + } + } + + __dump_ept_data_list(); + __dump_ep_list(PIPE_INTERRUPT); + printk("\n\n"); +} + + + +char* hcd_status_to_str(__u8 bUsbStatus) { + static char hcd_status_str[128]; + hcd_status_str[0] = '\0'; + if(bUsbStatus & IO_STATE(R_USB_STATUS, ourun, yes)) { + strcat(hcd_status_str, "ourun "); + } + if(bUsbStatus & IO_STATE(R_USB_STATUS, perror, yes)) { + strcat(hcd_status_str, "perror "); + } + if(bUsbStatus & IO_STATE(R_USB_STATUS, device_mode, yes)) { + strcat(hcd_status_str, "device_mode "); + } + if(bUsbStatus & IO_STATE(R_USB_STATUS, host_mode, yes)) { + strcat(hcd_status_str, "host_mode "); + } + if(bUsbStatus & IO_STATE(R_USB_STATUS, started, yes)) { + strcat(hcd_status_str, "started "); + } + if(bUsbStatus & IO_STATE(R_USB_STATUS, running, yes)) { + strcat(hcd_status_str, "running "); + } + return hcd_status_str; +} + + +char* sblist_to_str(struct USB_SB_Desc* sb_desc) { + static char sblist_to_str_buff[128]; + char tmp[32], tmp2[32]; + sblist_to_str_buff[0] = '\0'; + while(sb_desc != NULL) { + switch(IO_EXTRACT(USB_SB_command, tt, sb_desc->command)) { + case 0: sprintf(tmp, "zout"); break; + case 1: sprintf(tmp, "in"); break; + case 2: sprintf(tmp, "out"); break; + case 3: sprintf(tmp, "setup"); break; + } + sprintf(tmp2, "(%s %d)", tmp, sb_desc->sw_len); + strcat(sblist_to_str_buff, tmp2); + if(sb_desc->next != 0) { + sb_desc = phys_to_virt(sb_desc->next); + } else { + sb_desc = NULL; + } + } + return sblist_to_str_buff; +} + +char* port_status_to_str(__u16 wPortStatus) { + static char port_status_str[128]; + port_status_str[0] = '\0'; + if(wPortStatus & IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) { + strcat(port_status_str, "connected "); + } + if(wPortStatus & IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes)) { + strcat(port_status_str, "enabled "); + } + if(wPortStatus & IO_STATE(R_USB_RH_PORT_STATUS_1, suspended, yes)) { + strcat(port_status_str, "suspended "); + } + if(wPortStatus & IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes)) { + strcat(port_status_str, "reset "); + } + if(wPortStatus & IO_STATE(R_USB_RH_PORT_STATUS_1, speed, full)) { + strcat(port_status_str, "full-speed "); + } else { + strcat(port_status_str, "low-speed "); + } + return port_status_str; +} + + +char* endpoint_to_str(struct usb_endpoint_descriptor *ed) { + static char endpoint_to_str_buff[128]; + char tmp[32]; + int epnum = ed->bEndpointAddress & 0x0F; + int dir = ed->bEndpointAddress & 0x80; + int type = ed->bmAttributes & 0x03; + endpoint_to_str_buff[0] = '\0'; + sprintf(endpoint_to_str_buff, "ep:%d ", epnum); + switch(type) { + case 0: + sprintf(tmp, " ctrl"); + break; + case 1: + sprintf(tmp, " isoc"); + break; + case 2: + sprintf(tmp, " bulk"); + break; + case 3: + sprintf(tmp, " intr"); + break; + } + strcat(endpoint_to_str_buff, tmp); + if(dir) { + sprintf(tmp, " in"); + } else { + sprintf(tmp, " out"); + } + strcat(endpoint_to_str_buff, tmp); + + return endpoint_to_str_buff; +} + +/* Debug helper functions for Transfer Controller */ +char* pipe_to_str(unsigned int pipe) { + static char pipe_to_str_buff[128]; + char tmp[64]; + sprintf(pipe_to_str_buff, "dir:%s", str_dir(pipe)); + sprintf(tmp, " type:%s", str_type(pipe)); + strcat(pipe_to_str_buff, tmp); + + sprintf(tmp, " dev:%d", usb_pipedevice(pipe)); + strcat(pipe_to_str_buff, tmp); + sprintf(tmp, " ep:%d", usb_pipeendpoint(pipe)); + strcat(pipe_to_str_buff, tmp); + return pipe_to_str_buff; +} + + +#define USB_DEBUG_DESC 1 + +#ifdef USB_DEBUG_DESC +#define dump_in_desc(x) __dump_in_desc(x) +#define dump_sb_desc(...) __dump_sb_desc(...) +#define dump_ep_desc(x) __dump_ep_desc(x) +#define dump_ept_data(x) __dump_ept_data(x) +#else +#define dump_in_desc(...) do {} while (0) +#define dump_sb_desc(...) do {} while (0) +#define dump_ep_desc(...) do {} while (0) +#endif + + +/* Uncomment this to enable massive function call trace + #define USB_DEBUG_TRACE */ +//#define USB_DEBUG_TRACE 1 + +#ifdef USB_DEBUG_TRACE +#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__)) +#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__)) +#else +#define DBFENTER do {} while (0) +#define DBFEXIT do {} while (0) +#endif + +#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \ +{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);} + +/* Most helpful debugging aid */ +#define ASSERT(expr) ((void) ((expr) ? 0 : (err("assert failed at: %s %d",__FUNCTION__, __LINE__)))) + + +/***************************************************************************/ +/***************************************************************************/ +/* Forward declarations */ +/***************************************************************************/ +/***************************************************************************/ +void crisv10_hcd_epid_attn_irq(struct crisv10_irq_reg *reg); +void crisv10_hcd_port_status_irq(struct crisv10_irq_reg *reg); +void crisv10_hcd_ctl_status_irq(struct crisv10_irq_reg *reg); +void crisv10_hcd_isoc_eof_irq(struct crisv10_irq_reg *reg); + +void rh_port_status_change(__u16[]); +int rh_clear_port_feature(__u8, __u16); +int rh_set_port_feature(__u8, __u16); +static void rh_disable_port(unsigned int port); + +static void check_finished_bulk_tx_epids(struct usb_hcd *hcd, + int timer); + +//static int tc_setup_epid(struct usb_host_endpoint *ep, struct urb *urb, +// int mem_flags); +static int tc_setup_epid(struct urb *urb, int mem_flags); +static void tc_free_epid(struct usb_host_endpoint *ep); +static int tc_allocate_epid(void); +static void tc_finish_urb(struct usb_hcd *hcd, struct urb *urb, int status); +static void tc_finish_urb_later(struct usb_hcd *hcd, struct urb *urb, + int status); + +static int urb_priv_create(struct usb_hcd *hcd, struct urb *urb, int epid, + int mem_flags); +static void urb_priv_free(struct usb_hcd *hcd, struct urb *urb); + +static inline struct urb *urb_list_first(int epid); +static inline void urb_list_add(struct urb *urb, int epid, + int mem_flags); +static inline urb_entry_t *urb_list_entry(struct urb *urb, int epid); +static inline void urb_list_del(struct urb *urb, int epid); +static inline void urb_list_move_last(struct urb *urb, int epid); +static inline struct urb *urb_list_next(struct urb *urb, int epid); + +int create_sb_for_urb(struct urb *urb, int mem_flags); +int init_intr_urb(struct urb *urb, int mem_flags); + +static inline void etrax_epid_set(__u8 index, __u32 data); +static inline void etrax_epid_clear_error(__u8 index); +static inline void etrax_epid_set_toggle(__u8 index, __u8 dirout, + __u8 toggle); +static inline __u8 etrax_epid_get_toggle(__u8 index, __u8 dirout); +static inline __u32 etrax_epid_get(__u8 index); + +/* We're accessing the same register position in Etrax so + when we do full access the internal difference doesn't matter */ +#define etrax_epid_iso_set(index, data) etrax_epid_set(index, data) +#define etrax_epid_iso_get(index) etrax_epid_get(index) + + +//static void tc_dma_process_isoc_urb(struct urb *urb); +static void tc_dma_process_queue(int epid); +static void tc_dma_unlink_intr_urb(struct urb *urb); +static irqreturn_t tc_dma_tx_interrupt(int irq, void *vhc); +static irqreturn_t tc_dma_rx_interrupt(int irq, void *vhc); + +static void tc_bulk_start_timer_func(unsigned long dummy); +static void tc_bulk_eot_timer_func(unsigned long dummy); + + +/*************************************************************/ +/*************************************************************/ +/* Host Controler Driver block */ +/*************************************************************/ +/*************************************************************/ + +/* HCD operations */ +static irqreturn_t crisv10_hcd_top_irq(int irq, void*); +static int crisv10_hcd_reset(struct usb_hcd *); +static int crisv10_hcd_start(struct usb_hcd *); +static void crisv10_hcd_stop(struct usb_hcd *); +#ifdef CONFIG_PM +static int crisv10_hcd_suspend(struct device *, u32, u32); +static int crisv10_hcd_resume(struct device *, u32); +#endif /* CONFIG_PM */ +static int crisv10_hcd_get_frame(struct usb_hcd *); + +//static int tc_urb_enqueue(struct usb_hcd *, struct usb_host_endpoint *ep, struct urb *, gfp_t mem_flags); +static int tc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); +//static int tc_urb_dequeue(struct usb_hcd *, struct urb *); +static int tc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); +static void tc_endpoint_disable(struct usb_hcd *, struct usb_host_endpoint *ep); + +static int rh_status_data_request(struct usb_hcd *, char *); +static int rh_control_request(struct usb_hcd *, u16, u16, u16, char*, u16); + +#ifdef CONFIG_PM +static int crisv10_hcd_hub_suspend(struct usb_hcd *); +static int crisv10_hcd_hub_resume(struct usb_hcd *); +#endif /* CONFIG_PM */ +#ifdef CONFIG_USB_OTG +static int crisv10_hcd_start_port_reset(struct usb_hcd *, unsigned); +#endif /* CONFIG_USB_OTG */ + +/* host controller driver interface */ +static const struct hc_driver crisv10_hc_driver = + { + .description = hc_name, + .product_desc = product_desc, + .hcd_priv_size = sizeof(struct crisv10_hcd), + + /* Attaching IRQ handler manualy in probe() */ + /* .irq = crisv10_hcd_irq, */ + + .flags = HCD_USB11, + + /* called to init HCD and root hub */ + .reset = crisv10_hcd_reset, + .start = crisv10_hcd_start, + + /* cleanly make HCD stop writing memory and doing I/O */ + .stop = crisv10_hcd_stop, + + /* return current frame number */ + .get_frame_number = crisv10_hcd_get_frame, + + + /* Manage i/o requests via the Transfer Controller */ + .urb_enqueue = tc_urb_enqueue, + .urb_dequeue = tc_urb_dequeue, + + /* hw synch, freeing endpoint resources that urb_dequeue can't */ + .endpoint_disable = tc_endpoint_disable, + + + /* Root Hub support */ + .hub_status_data = rh_status_data_request, + .hub_control = rh_control_request, +#ifdef CONFIG_PM + .hub_suspend = rh_suspend_request, + .hub_resume = rh_resume_request, +#endif /* CONFIG_PM */ +#ifdef CONFIG_USB_OTG + .start_port_reset = crisv10_hcd_start_port_reset, +#endif /* CONFIG_USB_OTG */ + }; + + +/* + * conversion between pointers to a hcd and the corresponding + * crisv10_hcd + */ + +static inline struct crisv10_hcd *hcd_to_crisv10_hcd(struct usb_hcd *hcd) +{ + return (struct crisv10_hcd *) hcd->hcd_priv; +} + +static inline struct usb_hcd *crisv10_hcd_to_hcd(struct crisv10_hcd *hcd) +{ + return container_of((void *) hcd, struct usb_hcd, hcd_priv); +} + +/* check if specified port is in use */ +static inline int port_in_use(unsigned int port) +{ + return ports & (1 << port); +} + +/* number of ports in use */ +static inline unsigned int num_ports(void) +{ + unsigned int i, num = 0; + for (i = 0; i < USB_ROOT_HUB_PORTS; i++) + if (port_in_use(i)) + num++; + return num; +} + +/* map hub port number to the port number used internally by the HC */ +static inline unsigned int map_port(unsigned int port) +{ + unsigned int i, num = 0; + for (i = 0; i < USB_ROOT_HUB_PORTS; i++) + if (port_in_use(i)) + if (++num == port) + return i; + return -1; +} + +/* size of descriptors in slab cache */ +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + + +/******************************************************************/ +/* Hardware Interrupt functions */ +/******************************************************************/ + +/* Fast interrupt handler for HC */ +static irqreturn_t crisv10_hcd_top_irq(int irq, void *vcd) +{ + struct usb_hcd *hcd = vcd; + struct crisv10_irq_reg reg; + __u32 irq_mask; + unsigned long flags; + + DBFENTER; + + ASSERT(hcd != NULL); + reg.hcd = hcd; + + /* Turn of other interrupts while handling these sensitive cases */ + local_irq_save(flags); + + /* Read out which interrupts that are flaged */ + irq_mask = *R_USB_IRQ_MASK_READ; + reg.r_usb_irq_mask_read = irq_mask; + + /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that + R_USB_STATUS must be read before R_USB_EPID_ATTN since reading the latter + clears the ourun and perror fields of R_USB_STATUS. */ + reg.r_usb_status = *R_USB_STATUS; + + /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn + interrupts. */ + reg.r_usb_epid_attn = *R_USB_EPID_ATTN; + + /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the + port_status interrupt. */ + reg.r_usb_rh_port_status_1 = *R_USB_RH_PORT_STATUS_1; + reg.r_usb_rh_port_status_2 = *R_USB_RH_PORT_STATUS_2; + + /* Reading R_USB_FM_NUMBER clears the sof interrupt. */ + /* Note: the lower 11 bits contain the actual frame number, sent with each + sof. */ + reg.r_usb_fm_number = *R_USB_FM_NUMBER; + + /* Interrupts are handled in order of priority. */ + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) { + crisv10_hcd_port_status_irq(®); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) { + crisv10_hcd_epid_attn_irq(®); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) { + crisv10_hcd_ctl_status_irq(®); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) { + crisv10_hcd_isoc_eof_irq(®); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) { + /* Update/restart the bulk start timer since obviously the channel is + running. */ + mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); + /* Update/restart the bulk eot timer since we just received an bulk eot + interrupt. */ + mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); + + /* Check for finished bulk transfers on epids */ + check_finished_bulk_tx_epids(hcd, 0); + } + local_irq_restore(flags); + + DBFEXIT; + return IRQ_HANDLED; +} + + +void crisv10_hcd_epid_attn_irq(struct crisv10_irq_reg *reg) { + struct usb_hcd *hcd = reg->hcd; + struct crisv10_urb_priv *urb_priv; + int epid; + DBFENTER; + + for (epid = 0; epid < NBR_OF_EPIDS; epid++) { + if (test_bit(epid, (void *)®->r_usb_epid_attn)) { + struct urb *urb; + __u32 ept_data; + int error_code; + + if (epid == DUMMY_EPID || epid == INVALID_EPID) { + /* We definitely don't care about these ones. Besides, they are + always disabled, so any possible disabling caused by the + epid attention interrupt is irrelevant. */ + warn("Got epid_attn for INVALID_EPID or DUMMY_EPID (%d).", epid); + continue; + } + + if(!epid_inuse(epid)) { + irq_err("Epid attention on epid:%d that isn't in use\n", epid); + printk("R_USB_STATUS: 0x%x\n", reg->r_usb_status); + debug_epid(epid); + continue; + } + + /* Note that although there are separate R_USB_EPT_DATA and + R_USB_EPT_DATA_ISO registers, they are located at the same address and + are of the same size. In other words, this read should be ok for isoc + also. */ + ept_data = etrax_epid_get(epid); + error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, ept_data); + + /* Get the active URB for this epid. We blatantly assume + that only this URB could have caused the epid attention. */ + urb = activeUrbList[epid]; + if (urb == NULL) { + irq_err("Attention on epid:%d error:%d with no active URB.\n", + epid, error_code); + printk("R_USB_STATUS: 0x%x\n", reg->r_usb_status); + debug_epid(epid); + continue; + } + + urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; + ASSERT(urb_priv); + + /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */ + if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { + + /* Isoc traffic doesn't have error_count_in/error_count_out. */ + if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) && + (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, ept_data) == 3 || + IO_EXTRACT(R_USB_EPT_DATA, error_count_out, ept_data) == 3)) { + /* Check if URB allready is marked for late-finish, we can get + several 3rd error for Intr traffic when a device is unplugged */ + if(urb_priv->later_data == NULL) { + /* 3rd error. */ + irq_warn("3rd error for epid:%d (%s %s) URB:0x%x[%d]\n", epid, + str_dir(urb->pipe), str_type(urb->pipe), + (unsigned int)urb, urb_priv->urb_num); + + tc_finish_urb_later(hcd, urb, -EPROTO); + } + + } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { + irq_warn("Perror for epid:%d\n", epid); + printk("FM_NUMBER: %d\n", reg->r_usb_fm_number & 0x7ff); + printk("R_USB_STATUS: 0x%x\n", reg->r_usb_status); + __dump_urb(urb); + debug_epid(epid); + + if (!(ept_data & IO_MASK(R_USB_EPT_DATA, valid))) { + /* invalid ep_id */ + panic("Perror because of invalid epid." + " Deconfigured too early?"); + } else { + /* past eof1, near eof, zout transfer, setup transfer */ + /* Dump the urb and the relevant EP descriptor. */ + panic("Something wrong with DMA descriptor contents." + " Too much traffic inserted?"); + } + } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { + /* buffer ourun */ + printk("FM_NUMBER: %d\n", reg->r_usb_fm_number & 0x7ff); + printk("R_USB_STATUS: 0x%x\n", reg->r_usb_status); + __dump_urb(urb); + debug_epid(epid); + + panic("Buffer overrun/underrun for epid:%d. DMA too busy?", epid); + } else { + irq_warn("Attention on epid:%d (%s %s) with no error code\n", epid, + str_dir(urb->pipe), str_type(urb->pipe)); + printk("R_USB_STATUS: 0x%x\n", reg->r_usb_status); + __dump_urb(urb); + debug_epid(epid); + } + + } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, + stall)) { + /* Not really a protocol error, just says that the endpoint gave + a stall response. Note that error_code cannot be stall for isoc. */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + panic("Isoc traffic cannot stall"); + } + + tc_dbg("Stall for epid:%d (%s %s) URB:0x%x\n", epid, + str_dir(urb->pipe), str_type(urb->pipe), (unsigned int)urb); + tc_finish_urb(hcd, urb, -EPIPE); + + } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, + bus_error)) { + /* Two devices responded to a transaction request. Must be resolved + by software. FIXME: Reset ports? */ + panic("Bus error for epid %d." + " Two devices responded to transaction request\n", + epid); + + } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, + buffer_error)) { + /* DMA overrun or underrun. */ + irq_warn("Buffer overrun/underrun for epid:%d (%s %s)\n", epid, + str_dir(urb->pipe), str_type(urb->pipe)); + + /* It seems that error_code = buffer_error in + R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS + are the same error. */ + tc_finish_urb(hcd, urb, -EPROTO); + } else { + irq_warn("Unknown attention on epid:%d (%s %s)\n", epid, + str_dir(urb->pipe), str_type(urb->pipe)); + dump_ept_data(epid); + } + } + } + DBFEXIT; +} + +void crisv10_hcd_port_status_irq(struct crisv10_irq_reg *reg) +{ + __u16 port_reg[USB_ROOT_HUB_PORTS]; + DBFENTER; + port_reg[0] = reg->r_usb_rh_port_status_1; + port_reg[1] = reg->r_usb_rh_port_status_2; + rh_port_status_change(port_reg); + DBFEXIT; +} + +void crisv10_hcd_isoc_eof_irq(struct crisv10_irq_reg *reg) +{ + int epid; + struct urb *urb; + struct crisv10_urb_priv *urb_priv; + + DBFENTER; + + for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { + + /* Only check epids that are in use, is valid and has SB list */ + if (!epid_inuse(epid) || epid == INVALID_EPID || + TxIsocEPList[epid].sub == 0 || epid == DUMMY_EPID) { + /* Nothing here to see. */ + continue; + } + ASSERT(epid_isoc(epid)); + + /* Get the active URB for this epid (if any). */ + urb = activeUrbList[epid]; + if (urb == 0) { + isoc_warn("Ignoring NULL urb for epid:%d\n", epid); + continue; + } + if(!epid_out_traffic(epid)) { + /* Sanity check. */ + ASSERT(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); + + urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; + ASSERT(urb_priv); + + if (urb_priv->urb_state == NOT_STARTED) { + /* If ASAP is not set and urb->start_frame is the current frame, + start the transfer. */ + if (!(urb->transfer_flags & URB_ISO_ASAP) && + (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) { + /* EP should not be enabled if we're waiting for start_frame */ + ASSERT((TxIsocEPList[epid].command & + IO_STATE(USB_EP_command, enable, yes)) == 0); + + isoc_warn("Enabling isoc IN EP descr for epid %d\n", epid); + TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); + + /* This urb is now active. */ + urb_priv->urb_state = STARTED; + continue; + } + } + } + } + + DBFEXIT; +} + +void crisv10_hcd_ctl_status_irq(struct crisv10_irq_reg *reg) +{ + struct crisv10_hcd* crisv10_hcd = hcd_to_crisv10_hcd(reg->hcd); + + DBFENTER; + ASSERT(crisv10_hcd); + + irq_dbg("ctr_status_irq, controller status: %s\n", + hcd_status_to_str(reg->r_usb_status)); + + /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB + list for the corresponding epid? */ + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { + panic("USB controller got ourun."); + } + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { + + /* Before, etrax_usb_do_intr_recover was called on this epid if it was + an interrupt pipe. I don't see how re-enabling all EP descriptors + will help if there was a programming error. */ + panic("USB controller got perror."); + } + + /* Keep track of USB Controller, if it's running or not */ + if(reg->r_usb_status & IO_STATE(R_USB_STATUS, running, yes)) { + crisv10_hcd->running = 1; + } else { + crisv10_hcd->running = 0; + } + + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) { + /* We should never operate in device mode. */ + panic("USB controller in device mode."); + } + + /* Set the flag to avoid getting "Unlink after no-IRQ? Controller is probably + using the wrong IRQ" from hcd_unlink_urb() in drivers/usb/core/hcd.c */ + set_bit(HCD_FLAG_SAW_IRQ, ®->hcd->flags); + + DBFEXIT; +} + + +/******************************************************************/ +/* Host Controller interface functions */ +/******************************************************************/ + +static inline void crisv10_ready_wait(void) { + volatile int timeout = 10000; + /* Check the busy bit of USB controller in Etrax */ + while((*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)) && + (timeout-- > 0)); + if(timeout == 0) { + warn("Timeout while waiting for USB controller to be idle\n"); + } +} + +/* reset host controller */ +static int crisv10_hcd_reset(struct usb_hcd *hcd) +{ + DBFENTER; + hcd_dbg(hcd, "reset\n"); + + + /* Reset the USB interface. */ + /* + *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | + IO_STATE(R_USB_COMMAND, ctrl_cmd, reset); + nop(); + */ + DBFEXIT; + return 0; +} + +/* start host controller */ +static int crisv10_hcd_start(struct usb_hcd *hcd) +{ + DBFENTER; + hcd_dbg(hcd, "start\n"); + + crisv10_ready_wait(); + + /* Start processing of USB traffic. */ + *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | + IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); + + nop(); + + hcd->state = HC_STATE_RUNNING; + + DBFEXIT; + return 0; +} + +/* stop host controller */ +static void crisv10_hcd_stop(struct usb_hcd *hcd) +{ + DBFENTER; + hcd_dbg(hcd, "stop\n"); + crisv10_hcd_reset(hcd); + DBFEXIT; +} + +/* return the current frame number */ +static int crisv10_hcd_get_frame(struct usb_hcd *hcd) +{ + DBFENTER; + DBFEXIT; + return (*R_USB_FM_NUMBER & 0x7ff); +} + +#ifdef CONFIG_USB_OTG + +static int crisv10_hcd_start_port_reset(struct usb_hcd *hcd, unsigned port) +{ + return 0; /* no-op for now */ +} + +#endif /* CONFIG_USB_OTG */ + + +/******************************************************************/ +/* Root Hub functions */ +/******************************************************************/ + +/* root hub status */ +static const struct usb_hub_status rh_hub_status = + { + .wHubStatus = 0, + .wHubChange = 0, + }; + +/* root hub descriptor */ +static const u8 rh_hub_descr[] = + { + 0x09, /* bDescLength */ + 0x29, /* bDescriptorType */ + USB_ROOT_HUB_PORTS, /* bNbrPorts */ + 0x00, /* wHubCharacteristics */ + 0x00, + 0x01, /* bPwrOn2pwrGood */ + 0x00, /* bHubContrCurrent */ + 0x00, /* DeviceRemovable */ + 0xff /* PortPwrCtrlMask */ + }; + +/* Actual holder of root hub status*/ +struct crisv10_rh rh; + +/* Initialize root hub data structures (called from dvdrv_hcd_probe()) */ +int rh_init(void) { + int i; + /* Reset port status flags */ + for (i = 0; i < USB_ROOT_HUB_PORTS; i++) { + rh.wPortChange[i] = 0; + rh.wPortStatusPrev[i] = 0; + } + return 0; +} + +#define RH_FEAT_MASK ((1<<USB_PORT_FEAT_CONNECTION)|\ + (1<<USB_PORT_FEAT_ENABLE)|\ + (1<<USB_PORT_FEAT_SUSPEND)|\ + (1<<USB_PORT_FEAT_RESET)) + +/* Handle port status change interrupt (called from bottom part interrupt) */ +void rh_port_status_change(__u16 port_reg[]) { + int i; + __u16 wChange; + + for(i = 0; i < USB_ROOT_HUB_PORTS; i++) { + /* Xor out changes since last read, masked for important flags */ + wChange = (port_reg[i] & RH_FEAT_MASK) ^ rh.wPortStatusPrev[i]; + /* Or changes together with (if any) saved changes */ + rh.wPortChange[i] |= wChange; + /* Save new status */ + rh.wPortStatusPrev[i] = port_reg[i]; + + if(wChange) { + rh_dbg("Interrupt port_status change port%d: %s Current-status:%s\n", i+1, + port_status_to_str(wChange), + port_status_to_str(port_reg[i])); + } + } +} + +/* Construct port status change bitmap for the root hub */ +static int rh_status_data_request(struct usb_hcd *hcd, char *buf) +{ + struct crisv10_hcd* crisv10_hcd = hcd_to_crisv10_hcd(hcd); + unsigned int i; + +// DBFENTER; + + /* + * corresponds to hub status change EP (USB 2.0 spec section 11.13.4) + * return bitmap indicating ports with status change + */ + *buf = 0; + spin_lock(&crisv10_hcd->lock); + for (i = 1; i <= crisv10_hcd->num_ports; i++) { + if (rh.wPortChange[map_port(i)]) { + *buf |= (1 << i); + rh_dbg("rh_status_data_request, change on port %d: %s Current Status: %s\n", i, + port_status_to_str(rh.wPortChange[map_port(i)]), + port_status_to_str(rh.wPortStatusPrev[map_port(i)])); + } + } + spin_unlock(&crisv10_hcd->lock); + +// DBFEXIT; + + return *buf == 0 ? 0 : 1; +} + +/* Handle a control request for the root hub (called from hcd_driver) */ +static int rh_control_request(struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength) { + + struct crisv10_hcd *crisv10_hcd = hcd_to_crisv10_hcd(hcd); + int retval = 0; + int len; + DBFENTER; + + switch (typeReq) { + case GetHubDescriptor: + rh_dbg("GetHubDescriptor\n"); + len = min_t(unsigned int, sizeof rh_hub_descr, wLength); + memcpy(buf, rh_hub_descr, len); + buf[2] = crisv10_hcd->num_ports; + break; + case GetHubStatus: + rh_dbg("GetHubStatus\n"); + len = min_t(unsigned int, sizeof rh_hub_status, wLength); + memcpy(buf, &rh_hub_status, len); + break; + case GetPortStatus: + if (!wIndex || wIndex > crisv10_hcd->num_ports) + goto error; + rh_dbg("GetportStatus, port:%d change:%s status:%s\n", wIndex, + port_status_to_str(rh.wPortChange[map_port(wIndex)]), + port_status_to_str(rh.wPortStatusPrev[map_port(wIndex)])); + *(u16 *) buf = cpu_to_le16(rh.wPortStatusPrev[map_port(wIndex)]); + *(u16 *) (buf + 2) = cpu_to_le16(rh.wPortChange[map_port(wIndex)]); + break; + case SetHubFeature: + rh_dbg("SetHubFeature\n"); + case ClearHubFeature: + rh_dbg("ClearHubFeature\n"); + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + rh_warn("Not implemented hub request:%d \n", typeReq); + /* not implemented */ + break; + default: + goto error; + } + break; + case SetPortFeature: + if (!wIndex || wIndex > crisv10_hcd->num_ports) + goto error; + if(rh_set_port_feature(map_port(wIndex), wValue)) + goto error; + break; + case ClearPortFeature: + if (!wIndex || wIndex > crisv10_hcd->num_ports) + goto error; + if(rh_clear_port_feature(map_port(wIndex), wValue)) + goto error; + break; + default: + rh_warn("Unknown hub request: %d\n", typeReq); + error: + retval = -EPIPE; + } + DBFEXIT; + return retval; +} + +int rh_set_port_feature(__u8 bPort, __u16 wFeature) { + __u8 bUsbCommand = 0; + switch(wFeature) { + case USB_PORT_FEAT_RESET: + rh_dbg("SetPortFeature: reset\n"); + bUsbCommand |= IO_STATE(R_USB_COMMAND, port_cmd, reset); + goto set; + break; + case USB_PORT_FEAT_SUSPEND: + rh_dbg("SetPortFeature: suspend\n"); + bUsbCommand |= IO_STATE(R_USB_COMMAND, port_cmd, suspend); + goto set; + break; + case USB_PORT_FEAT_POWER: + rh_dbg("SetPortFeature: power\n"); + break; + case USB_PORT_FEAT_C_CONNECTION: + rh_dbg("SetPortFeature: c_connection\n"); + break; + case USB_PORT_FEAT_C_RESET: + rh_dbg("SetPortFeature: c_reset\n"); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + rh_dbg("SetPortFeature: c_over_current\n"); + break; + + set: + /* Select which port via the port_sel field */ + bUsbCommand |= IO_FIELD(R_USB_COMMAND, port_sel, bPort+1); + + /* Make sure the controller isn't busy. */ + crisv10_ready_wait(); + /* Send out the actual command to the USB controller */ + *R_USB_COMMAND = bUsbCommand; + + /* If port reset then also bring USB controller into running state */ + if(wFeature == USB_PORT_FEAT_RESET) { + /* Wait a while for controller to first become started after port reset */ + udelay(12000); /* 12ms blocking wait */ + + /* Make sure the controller isn't busy. */ + crisv10_ready_wait(); + + /* If all enabled ports were disabled the host controller goes down into + started mode, so we need to bring it back into the running state. + (This is safe even if it's already in the running state.) */ + *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | + IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); + } + + break; + default: + rh_dbg("SetPortFeature: unknown feature\n"); + return -1; + } + return 0; +} + +int rh_clear_port_feature(__u8 bPort, __u16 wFeature) { + switch(wFeature) { + case USB_PORT_FEAT_ENABLE: + rh_dbg("ClearPortFeature: enable\n"); + rh_disable_port(bPort); + break; + case USB_PORT_FEAT_SUSPEND: + rh_dbg("Cle